diff --git a/.env b/.env new file mode 100644 index 000000000..af8151e15 --- /dev/null +++ b/.env @@ -0,0 +1,38 @@ + +#PORT NUMBERS: If any of these ports are already in use by a different app, please pick a new number between 1 and 50000. +WEB_SERVER_PORT=9999 +WEB_SOCKETS_PORT=8080 + + +# TASK MANAGER VARIABLES: + +TASK_SERVER_PATH=./TaskServer + +# TASK SERVER VARIABLES: + +INTER_PROCESS_FILES_PATH=./Inter-Process-Files +STORAGE_PATH=./Data-Storage +PLOTTERS_PATH=./Legacy-Code + +USE_KEYVAULT=false +WRITE_LOGS_TO_FILES=true +BOTS_PATH=./Legacy-Code +LOG_PATH=./Log-Files + +# WEB SERVER VARIABLES: + +#Log options +CONSOLE_LOG=false +CONSOLE_ERROR_LOG=true +LOG_FILE_CONTENT=false + +# Server commands +REINITIALIZE_COMMAND=sdnacGYGdw756dnxasxbGGGjsdbd5576576gfdsxgvbnFFF + +PATH_TO_CANVAS_APP=./CanvasApp +PATH_TO_FILES_COMPONENT=./Files-Component/src +PATH_TO_WEB_SERVER=./WebServer/ + +GATEWAY_ENDPOINT=https://demo-api-dev.superalgos.org/graphql + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ca941931..082d116e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,21 +2,21 @@ We are thrilled you are interested in contributing to this project. Your help may be essential to the project's success and we want you in! -Superalgos is a collective project, currently at a very early stage. We not only welcome contributions but may even choose to incentivize relevant contributions via the project's native crypto token. +Superalgos is a collective project, currently at a very early stage. We not only welcome contributions but may even choose to incentivize relevant contributions via the project's native crypto token, to be deployed in the near future. Please read the [Join the Dev Team]( https://superalgos.org/developers-join-dev-team.shtml) page in our website to learn more about how you may become a relevant member of the Superalgos Project and even make a living of contributing code. -Contributions are deemed _voluntary work_, irrespective of the incentives that may be in place, and are subject to the conditions described in the software [LICENSE](https://github.com/Superalgos/DesktopApp/blob/master/LICENSE). +Contributions are deemed _voluntary work_, irrespective of the incentives that may be in place, and are subject to the conditions described in the software [LICENSE](LICENSE). ## Getting Started -First step is to download, install and start using the app. The release includes all source code which constitutes your development environment right out of the package. The app runs uncompilled on the actual code. +First step is to fork this repository and start using the app. The repository includes all source code which constitutes your development environment. The app runs uncompilled on the actual code. -* The priority at this moment is the stabilization of existing functionality. Your first contribution will be using and testing the app, and reporting any bugs you may find. +* The priority at this moment is the stabilization of existing functionality. Your first contribution will be using, learning, and testing the app, and reporting any bugs you may find. -* You may debug and remove bugs yourself, and submit pull requests to the corresponding repositories. The name of the folder containing the code maps each of the repositories involved in the release. +* You may debug and remove bugs yourself, and submit pull requests. -* Additionally, you may contribute helping improve the README and building the documentation in each of the repositories involved. +* Another top priority is building a robust base of indicators and technical studies. This is done from the UI, using the functionality managed by data mines. As you engage with other developers and the Core Team, we will get to know you and develop a relationship, so that you may later participate in core development tasks. diff --git a/CanvasApp/Animation.js b/CanvasApp/Animation.js new file mode 100644 index 000000000..41ec4c976 --- /dev/null +++ b/CanvasApp/Animation.js @@ -0,0 +1,172 @@ + +function newAnimation () { + const MODULE_NAME = 'Animation' + const ERROR_LOG = true + const logger = newWebDebugLog() + 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, + finalize: finalize + } + + let totalConsumption = 0 + let totalCounter = 0 + let pointerIcon + let videoRecorder + return thisObject + + function finalize () { + thisObject.stop() + pointerIcon = undefined + videoRecorder = undefined + } + + function initialize () { + videoRecorder = newVideoRecorder() + } + + function start () { + animationLoop() // Inside this function the animation process is started, and at the same time it creates a loop. + } + + function stop () { + window.cancelAnimationFrame(animationLoopHandle) + } + + function addCallBackFunction (key, callBack) { + callBackFunctions.set(key, callBack) + } + + function removeCallBackFunction (key) { + callBackFunctions.delete(key) + } + + function animationLoop () { + try { + if (window.canvasApp.visible === true) { + /* We set the canvas to its normal width and height */ + browserCanvas.width = window.innerWidth + browserCanvas.height = window.innerHeight - CURRENT_TOP_MARGIN + + /* First thing is to clear the actual canvas */ + // clearBrowserCanvas() + + /* We loop through the callback functions collections and execute them all. */ + let performanceMap = new Map() + let totalTimeConsumed = 0 + let totalElements = 0 + let chanceToExecute + let randomNumber + let mustExecute + + // console.clear() + let row = 0 + for (const [key, callBackFunction] of callBackFunctions.entries()) { + row++ + switch (key) { + case 'Floating Space Physics': + chanceToExecute = 50 + randomNumber = Math.random() * 100 * chanceToExecute + if (randomNumber > 100 - chanceToExecute) { mustExecute = true } else { mustExecute = false } + break + default: { + mustExecute = true + } + + } + let timeConsumed = 0 + if (mustExecute === true) { + let t0 = performance.now() + callBackFunction() + let t1 = performance.now() + timeConsumed = t1 - t0 + if (key === 'Charting Space Draw') { + if (Math.random() * 100 > 99) { + totalConsumption = 0 + totalCounter = 0 + } + + totalConsumption = totalConsumption + timeConsumed + totalCounter = totalCounter + 1 + } + performanceMap.set(key, timeConsumed) + totalTimeConsumed = totalTimeConsumed + timeConsumed + totalElements++ + } + } + + /* Performance Check */ + if (SHOW_ANIMATION_PERFORMACE === true) { + row = 0 + for (const [key, timeConsumed] of performanceMap.entries()) { + row++ + labelToPrint = key + ' ' + timeConsumed.toFixed(4) + printLabel(labelToPrint, 10, 100 + row * 30, 1, 20, UI_COLOR.RED) + let percentage = timeConsumed * 100 / totalTimeConsumed + labelToPrint = key + ' ' + percentage.toFixed(1) + '%' + printLabel(labelToPrint, 300, 100 + row * 30, 1, 20, UI_COLOR.RED) + } + + /* Other Variables */ + row++ + printLabel(DEBUG.variable1, 300, 100 + row * 30, 1, 20, UI_COLOR.RED) + row++ + printLabel(DEBUG.variable2, 300, 100 + row * 30, 1, 20, UI_COLOR.RED) + row++ + printLabel(DEBUG.variable3, 300, 100 + row * 30, 1, 20, UI_COLOR.RED) + row++ + printLabel(DEBUG.variable4, 300, 100 + row * 30, 1, 20, UI_COLOR.RED) + } + } else { + browserCanvas.width = 1 + browserCanvas.height = 1 + } + + /* Media Recording */ + if (areWeRecording === true) { + videoRecorder.recordCanvas() + } + + /* We request the next frame to be drawn, and stablishing a loop */ + + /* Use this code for full animation speed. */ + animationLoopHandle = window.requestAnimationFrame(animationLoop) + + /* Use this code for max 10 frames per second animation speed, if the app is consumming too much of your CPU. */ + /* + setTimeout(nextLoop, 100) + function nextLoop () { + animationLoopHandle = window.requestAnimationFrame(animationLoop) + } + */ + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] animationLoop -> err = ' + err.stack) } + } + } + + function clearBrowserCanvas () { + browserCanvasContext.beginPath() + + browserCanvasContext.rect(0, 0, browserCanvas.width, browserCanvas.height) + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + + browserCanvasContext.closePath() + browserCanvasContext.fill() + } +} diff --git a/CanvasApp/Canvas.js b/CanvasApp/Canvas.js new file mode 100644 index 000000000..50e4a896e --- /dev/null +++ b/CanvasApp/Canvas.js @@ -0,0 +1,990 @@ + +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 ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + /* Mouse event related variables. */ + + let containerDragStarted = false + let floatingObjectDragStarted = false + let floatingObjectBeingDragged + let containerBeingDragged + let viewPortBeingDragged = false + let ignoreNextClick = false + + let dragVector = { + downX: 0, + downY: 0, + upX: 0, + upY: 0 + } + + /* canvas object */ + + let thisObject = { + eventHandler: undefined, + topSpace: undefined, + chartingSpace: undefined, + floatingSpace: undefined, + panelsSpace: undefined, + cockpitSpace: undefined, + bottomSpace: undefined, + designSpace: undefined, + animation: undefined, + mouse: undefined, + initialize: initialize, + finalize: finalize + } + + thisObject.eventHandler = newEventHandler() + + let splashScreen + let lastContainerMouseOver + let lastShortcutKeyRejection + + thisObject.mouse = { + position: { + x: 0, + y: 0 + }, + action: '' + } + return thisObject + + function finalize () { + try { + thisObject.chartingSpace.finalize() + thisObject.floatingSpace.finalize() + + browserCanvas.removeEventListener('mousedown', onMouseDown, false) + browserCanvas.removeEventListener('mouseup', onMouseUp, false) + browserCanvas.removeEventListener('mousemove', onMouseMove, false) + browserCanvas.removeEventListener('click', onMouseClick, false) + browserCanvas.removeEventListener('mouseout', onMouseOut, false) + + if (browserCanvas.removeEventListener) { + browserCanvas.removeEventListener('mousewheel', onMouseWheel, false)// IE9, Chrome, Safari, Opera + browserCanvas.removeEventListener('DOMMouseScroll', onMouseWheel, false) // Firefox + } else browserCanvas.detachEvent('onmousewheel', onMouseWheel) // IE 6/7/8 + + browserCanvas.removeEventListener('dragenter', onDragEnter, false) + browserCanvas.removeEventListener('dragleave', onDragLeave, false) + browserCanvas.removeEventListener('dragover', onDragOver, false) + browserCanvas.removeEventListener('drop', onDragDrop, false) + + browserCanvas.removeEventListener('keydown', onKeyDown, false) + browserCanvas.removeEventListener('keyup', onKeyUp, false) + + splashScreen = undefined + lastContainerMouseOver = undefined + + hisObject.mouse = undefined + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } + } + } + + function initialize () { + try { + initializeBrowserCanvas() + + addCanvasEvents() + + /* Instantiate all the children spaces of Canvas object */ + + thisObject.floatingSpace = newFloatingSpace() + thisObject.floatingSpace.initialize() + + thisObject.topSpace = newTopSpace() + thisObject.topSpace.initialize() + + thisObject.designSpace = newDesignSpace() + thisObject.designSpace.initialize() + + thisObject.cockpitSpace = newCockpitSpace() + thisObject.cockpitSpace.initialize() + + thisObject.panelsSpace = newPanelsSpace() + thisObject.panelsSpace.initialize() + + thisObject.chartingSpace = newChartingSpace() + thisObject.chartingSpace.initialize() + + thisObject.bottomSpace = thisObject.cockpitSpace + + splashScreen = newSplashScreen() + splashScreen.initialize() + + let animation = newAnimation() + animation.initialize() + + thisObject.animation = animation + /* Low Level Infraestructure First */ + animation.addCallBackFunction('System Event Handler Physics', systemEventHandler.physics) + + /* Spcaces Physics */ + animation.addCallBackFunction('CockpitSpace Physics', thisObject.cockpitSpace.physics) + animation.addCallBackFunction('Floating Space Physics', thisObject.floatingSpace.physics) + animation.addCallBackFunction('Charting Space Physics', thisObject.chartingSpace.physics) + animation.addCallBackFunction('Design Space Physics', thisObject.designSpace.physics) + animation.addCallBackFunction('Panels Space Physics', thisObject.panelsSpace.physics) + + /* Spcaces Drawing */ + animation.addCallBackFunction('Floating Space Draw', thisObject.floatingSpace.draw) + animation.addCallBackFunction('Charting Space Draw', thisObject.chartingSpace.draw) + animation.addCallBackFunction('Panels Space', thisObject.panelsSpace.draw) + animation.addCallBackFunction('CockpitSpace Draw', thisObject.cockpitSpace.draw) + animation.addCallBackFunction('Design Space Draw', thisObject.designSpace.draw) + animation.addCallBackFunction('Top Space Draw', thisObject.topSpace.draw) + animation.addCallBackFunction('Splash Screen Draw', splashScreen.draw) + animation.start() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + } + } + + function initializeBrowserCanvas () { + try { + browserCanvasContext = browserCanvas.getContext('2d') + browserCanvasContext.font = 'Saira Condensed' + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initializeBrowserCanvas -> err = ' + err.stack) } + } + } + + function addCanvasEvents () { + try { + canvas.eventHandler.listenToEvent('Browser Resized', browserResized) + + /* Keyboard events */ + window.addEventListener('keydown', onKeyDown, true) + window.addEventListener('keyup', onKeyUp, true) + + /* Mouse Events */ + browserCanvas.addEventListener('mousedown', onMouseDown, false) + browserCanvas.addEventListener('mouseup', onMouseUp, false) + browserCanvas.addEventListener('mousemove', onMouseMove, false) + browserCanvas.addEventListener('click', onMouseClick, false) + browserCanvas.addEventListener('mouseout', onMouseOut, false) + + if (browserCanvas.addEventListener) { + browserCanvas.addEventListener('mousewheel', onMouseWheel, false) // IE9, Chrome, Safari, Opera + browserCanvas.addEventListener('DOMMouseScroll', onMouseWheel, false) // Firefox + } else browserCanvas.attachEvent('onmousewheel', onMouseWheel)// IE 6/7/8 + + /* Dragging Files Over the Canvas */ + browserCanvas.addEventListener('dragenter', onDragEnter, false) + browserCanvas.addEventListener('dragleave', onDragLeave, false) + browserCanvas.addEventListener('dragover', onDragOver, false) + browserCanvas.addEventListener('drop', onDragDrop, false) + + // Disables the context menu when you right mouse click the canvas. + browserCanvas.oncontextmenu = function (e) { + e.preventDefault() + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] addCanvasEvents -> err = ' + err.stack) } + } + } + + function browserResized () { + try { + browserCanvas = document.getElementById('canvas') + + browserCanvas.width = window.innerWidth + browserCanvas.height = window.innerHeight - CURRENT_TOP_MARGIN + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] browserResized -> err = ' + err.stack) } + } + } + + function checkMediaRecording (event) { + if ((event.ctrlKey === true || event.metaKey === true)) { + let constructorParams + switch (event.keyCode) { + case 119: { // F8 + downloadCanvas('Superalgos.image.capture') + return + break + } + case 120: { // F9 + constructorParams = { format: 'gif', workersPath: 'externalScripts/', framerate: 8, name: 'Superalgos.video.capture'} + break + } + case 121: { // F10 + constructorParams = { format: 'webm', framerate: 8, name: 'Superalgos.video.capture' } + break + } + default: return + } + + if (areWeRecording === false) { + console.log('RECORDING', constructorParams.format) + mediaRecorder = new CCapture(constructorParams) + mediaRecorder.start() + areWeRecording = true + } else { + console.log('SAVING', constructorParams.format) + areWeRecording = false + mediaRecorder.stop() + mediaRecorder.save() + } + } + } + + function onKeyUp (event) { + thisObject.mouse.event = event + thisObject.mouse.action = 'key up' + } + + function onKeyDown (event) { + thisObject.mouse.event = event + thisObject.mouse.action = 'key down' + + checkMediaRecording(event) + + let nodeOnFocus = canvas.designSpace.workspace.getNodeThatIsOnFocus() + if (nodeOnFocus !== undefined) { + if (nodeOnFocus.payload.uiObject.codeEditor !== undefined) { + if (nodeOnFocus.payload.uiObject.codeEditor.visible === true) { + return + } + } + if (nodeOnFocus.payload.uiObject.configEditor !== undefined) { + if (nodeOnFocus.payload.uiObject.configEditor.visible === true) { + return + } + } + if (nodeOnFocus.payload.uiObject.formulaEditor !== undefined) { + if (nodeOnFocus.payload.uiObject.formulaEditor.visible === true) { + return + } + } + if (nodeOnFocus.payload.uiObject.conditionEditor !== undefined) { + if (nodeOnFocus.payload.uiObject.conditionEditor.visible === true) { + return + } + } + if (nodeOnFocus.payload.uiObject.uiObjectTitle !== undefined) { + if (nodeOnFocus.payload.uiObject.uiObjectTitle.editMode === true) { + return + } + } + } + + if ((event.ctrlKey === true || event.metaKey === true) && event.altKey === true && event.shiftKey === true && event.keyCode === 123) { // Dev Tool when used with F12 + if (nodeOnFocus !== undefined) { + console.log(nodeOnFocus) + return + } + } + + if ((event.ctrlKey === true || event.metaKey === true) && event.altKey === true && event.shiftKey === true && event.keyCode === 122) { // Dev Tool when used with F11 + if (SHOW_ANIMATION_PERFORMACE === false) { + SHOW_ANIMATION_PERFORMACE = true + } else { + SHOW_ANIMATION_PERFORMACE = false + } + return + } + + if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowUp') { + thisObject.cockpitSpace.toTop() + return + } + + if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowDown') { + thisObject.cockpitSpace.toBottom() + return + } + + if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowLeft') { + thisObject.cockpitSpace.moveUp() + return + } + + if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowRight') { + thisObject.cockpitSpace.moveDown() + return + } + + if (event.shiftKey === true && event.ctrlKey === false && event.code === 'ArrowLeft') { + canvas.chartingSpace.oneScreenLeft() + return + } + + if (event.shiftKey === true && event.ctrlKey === false && event.code === 'ArrowRight') { + canvas.chartingSpace.oneScreenRight() + return + } + + if (event.shiftKey === true && event.code === 'ArrowUp') { + canvas.chartingSpace.oneScreenUp() + return + } + + if (event.shiftKey === true && event.code === 'ArrowDown') { + canvas.chartingSpace.oneScreenDown() + return + } + + if ((event.ctrlKey === true || event.metaKey === true) && event.shiftKey === false && event.code === 'ArrowLeft') { + let displaceVector = canvas.floatingSpace.oneScreenLeft() + dragVector.downX = dragVector.downX + displaceVector.x + dragVector.downY = dragVector.downY + displaceVector.y + checkDrag() + return + } + + if ((event.ctrlKey === true || event.metaKey === true) && event.shiftKey === false && event.code === 'ArrowRight') { + let displaceVector = canvas.floatingSpace.oneScreenRight() + dragVector.downX = dragVector.downX + displaceVector.x + dragVector.downY = dragVector.downY + displaceVector.y + checkDrag() + return + } + + if ((event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowUp') { + let displaceVector = canvas.floatingSpace.oneScreenUp() + dragVector.downX = dragVector.downX + displaceVector.x + dragVector.downY = dragVector.downY + displaceVector.y + checkDrag() + return + } + + if ((event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowDown') { + let displaceVector = canvas.floatingSpace.oneScreenDown() + dragVector.downX = dragVector.downX + displaceVector.x + dragVector.downY = dragVector.downY + displaceVector.y + checkDrag() + return + } + + if (event.code === 'Period') { + if (nodeOnFocus !== undefined) { + nodeOnFocus.payload.uiObject.setValue('Id: ' + nodeOnFocus.id) + return + } + } + + if ((event.ctrlKey === true || event.metaKey === true) && event.altKey === true) { + if ((event.keyCode >= 48 && event.keyCode <= 57) || (event.keyCode >= 65 && event.keyCode <= 90)) { + /* From here we prevent the default behaviour */ + event.preventDefault() + + let nodeUsingThisKey = canvas.designSpace.workspace.getNodeByShortcutKey(event.key) + + if (nodeOnFocus === undefined && nodeUsingThisKey !== undefined) { + /* Then we displace the whole workspace to center it at the node using this key */ + nodeUsingThisKey = canvas.floatingSpace.positionAtNode(nodeUsingThisKey) + return + } + + /* If there is a node in focus, we try to assign the key to it. */ + if (nodeUsingThisKey !== undefined && nodeOnFocus !== undefined) { + if (nodeUsingThisKey.id === nodeOnFocus.id) { + nodeOnFocus.payload.uiObject.shortcutKey = '' + nodeOnFocus.payload.uiObject.setValue('Shortcut Key Removed ') + return + } else { + if (lastShortcutKeyRejection !== event.key + nodeUsingThisKey.type + ' ' + nodeUsingThisKey.name) { + /* The first time we show a warning that this key is in use. */ + nodeOnFocus.payload.uiObject.setErrorMessage('Key already in use by ' + nodeUsingThisKey.type + ' ' + nodeUsingThisKey.name) + lastShortcutKeyRejection = event.key + nodeUsingThisKey.type + ' ' + nodeUsingThisKey.name + return + } else { + /* After the warning, we allow the key to be re-assigned */ + nodeUsingThisKey.payload.uiObject.shortcutKey = '' + nodeUsingThisKey === undefined + } + } + } + /* If there is not node using this key and a node in focus, we assign this key to this node */ + if (nodeUsingThisKey === undefined && nodeOnFocus !== undefined) { + nodeOnFocus.payload.uiObject.shortcutKey = event.key + nodeOnFocus.payload.uiObject.setValue('Shortcut Key: Ctrl + Alt + ' + event.key) + } + return + } + } + + if (event.ctrlKey === true && event.altKey === true && nodeOnFocus !== undefined) { + if (nodeOnFocus.payload.uiObject.shortcutKey !== undefined && nodeOnFocus.payload.uiObject.shortcutKey !== '') { + nodeOnFocus.payload.uiObject.setValue('Shortcut Key: Ctrl + Alt + ' + nodeOnFocus.payload.uiObject.shortcutKey) + } + } + } + + function onDragEnter (event) { + try { + event.preventDefault() + event.stopPropagation() + thisObject.cockpitSpace.toTop() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragEnter -> err = ' + err.stack) } + } + } + + function onDragLeave (event) { + try { + event.preventDefault() + event.stopPropagation() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragLeave -> err = ' + err.stack) } + } + } + + function onDragOver (event) { + try { + event.preventDefault() + event.stopPropagation() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragOver -> err = ' + err.stack) } + } + } + + function onDragDrop (event) { + try { + event.preventDefault() + event.stopPropagation() + + let files = event.dataTransfer.files + + handleFiles(files) + function handleFiles (files) { + ([...files]).forEach(loadFileData) + } + + function loadFileData (file) { + let reader = new FileReader() + reader.readAsText(file) + reader.onloadend = function () { + let mousePosition = { + x: event.x, + y: event.y + } + thisObject.designSpace.workspace.spawn(reader.result, mousePosition) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragDrop -> err = ' + err.stack) } + } + } + + function onMouseDown (event) { + try { + thisObject.mouse.event = event + thisObject.mouse.position.x = event.pageX + thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN + switch (event.buttons) { + case 1: { + thisObject.mouse.action = 'clicking with left button' + break + } + case 2: { + thisObject.mouse.action = 'clicking with right button' + break + } + } + + let point = event + point.x = event.pageX + point.y = event.pageY - CURRENT_TOP_MARGIN + + dragVector.downX = point.x + dragVector.downY = point.y + + let container + + /* We check if the mouse is over an element of the Design Space / */ + + container = thisObject.designSpace.getContainer(point) + + if (container !== undefined && container.isDraggeable === true) { + containerBeingDragged = container + containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) + return + } + + if (container !== undefined && container.isDraggeable === false) { + return + } + + /* We check if the mouse is over an element of the Top Space / */ + + container = thisObject.topSpace.getContainer(point) + + if (container !== undefined && container.isDraggeable === true) { + containerBeingDragged = container + containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) + return + } + + /* We check if the mouse is over an element of the CockpitSpace / */ + + container = thisObject.cockpitSpace.getContainer(point) + + if (container !== undefined && container.isDraggeable === true) { + containerBeingDragged = container + containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) + return + } + + if (container !== undefined && container.isClickeable === true) { + /* We dont want to mess up with the click */ + return + } + + /* We check if the mouse is over a panel/ */ + + container = thisObject.panelsSpace.getContainer(point) + + if (container !== undefined && container.isDraggeable === true && event.shiftKey === false) { + containerBeingDragged = container + containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) + return + } + + if (container !== undefined && container.isClickeable === true) { + /* We dont want to mess up with the click */ + return + } + + /* we check if it is over any of the existing containers at the Charting Space. */ + + container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.DRAGGING) + + if (container !== undefined) { + if (container.isDraggeable === true) { + containerBeingDragged = container + containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) + return + } else { + if (container.isClickeable === false) { + viewPortBeingDragged = true + } + return + } + } + + container = thisObject.floatingSpace.getContainer(point) + + if (container !== undefined && container.isDraggeable === true) { + containerBeingDragged = container + containerDragStarted = true + floatingObjectDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) + return + } + + if (container !== undefined && container.isClickeable === true) { + /* We dont want to mess up with the click */ + return + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onMouseDown -> err = ' + err.stack) } + } + } + + function onMouseClick (event) { + try { + if (ignoreNextClick === true) { + ignoreNextClick = false + return + } + + let point = event + point.x = event.pageX + point.y = event.pageY - CURRENT_TOP_MARGIN + + let container + + /* We check if the mouse is over an element of the Design Space / */ + + container = thisObject.designSpace.getContainer(point) + + if (container !== undefined && container.isClickeable === true) { + container.eventHandler.raiseEvent('onMouseClick', point) + return + } + + /* We check if the mouse is over an element of the Top Space / */ + + container = thisObject.topSpace.getContainer(point) + + if (container !== undefined && container.isClickeable === true) { + container.eventHandler.raiseEvent('onMouseClick', point) + return + } + + /* We check if the mouse is over an element of the CockpitSpace / */ + + container = thisObject.cockpitSpace.getContainer(point) + + if (container !== undefined && container.isClickeable === true) { + container.eventHandler.raiseEvent('onMouseClick', point) + return + } + + /* We check if the mouse is over a panel/ */ + + container = thisObject.panelsSpace.getContainer(point) + + if (container !== undefined && container.isClickeable === true) { + container.eventHandler.raiseEvent('onMouseClick', point) + return + } + + /* If it is not, then we check if it is over any of the existing containers at the Charting Space. */ + + container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_CLICK) + + if (container !== undefined && container.isClickeable === true) { + container.eventHandler.raiseEvent('onMouseClick', point) + return + } + + /* We check if the mouse is over a floatingObject/ */ + container = thisObject.floatingSpace.getContainer(point) + + if (container !== undefined && container.isClickeable === true) { + container.eventHandler.raiseEvent('onMouseClick', point) + return + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onMouseClick -> err = ' + err.stack) } + } + } + + function onMouseOut (event) { + deactivateDragging(event) + + /* When the mouse leaves the canvas, our elements needs to react to the fact that the mouse is over a far away place */ + let thisEvent = { + pageX: VERY_LARGE_NUMBER, + pageY: VERY_LARGE_NUMBER + } + onMouseOver(thisEvent) + } + + function onMouseUp (event) { + deactivateDragging(event) + } + + function onMouseMove (event) { + try { + thisObject.mouse.event = event + thisObject.mouse.position.x = event.pageX + thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN + thisObject.mouse.action = 'moving' + + /* Processing the event */ + let point = event + point.x = event.pageX + point.y = event.pageY - CURRENT_TOP_MARGIN + + if (canvas.chartingSpace.viewport !== undefined) { + canvas.chartingSpace.viewport.mousePosition.x = point.x + canvas.chartingSpace.viewport.mousePosition.y = point.y + } + + if (containerDragStarted === true || floatingObjectDragStarted === true || viewPortBeingDragged === true) { + if (floatingObjectDragStarted === true) { + let targetContainer = thisObject.floatingSpace.getContainer(point) + if (targetContainer !== undefined) { + if (targetContainer.id !== containerBeingDragged.id) { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', point) + containerBeingDragged = undefined + containerDragStarted = false + floatingObjectDragStarted = false + browserCanvas.style.cursor = 'auto' + ignoreNextClick = true + return + } + } else { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', point) + containerDragStarted = false + containerBeingDragged = undefined + floatingObjectDragStarted = false + browserCanvas.style.cursor = 'auto' + ignoreNextClick = true + return + } + } + + dragVector.upX = point.x + dragVector.upY = point.y + + checkDrag(event) + } + onMouseOver(event) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onMouseMove -> err = ' + err.stack) } + } + } + + function onMouseOver (event) { + try { + let point = event + point.x = event.pageX + point.y = event.pageY - CURRENT_TOP_MARGIN + + if (containerDragStarted === true) { + containerBeingDragged.eventHandler.raiseEvent('onMouseOver', point) + return + } + + /* Then we check who is the current object underneeth the mounse. */ + + let container + + /* We check if the mouse is over an element of the Strategy Space / */ + if (thisObject.designSpace !== undefined) { + container = thisObject.designSpace.getContainer(point) + + if (container !== undefined && container.detectMouseOver === true) { + containerFound() + return + } + } + + /* We check if the mouse is over an element of the Top Space / */ + if (thisObject.topSpace !== undefined) { + container = thisObject.topSpace.getContainer(point) + + if (container !== undefined && container.detectMouseOver === true) { + containerFound() + return + } + } + + /* We check if the mouse is over an element of the CockpitSpace / */ + if (thisObject.cockpitSpace !== undefined) { + container = thisObject.cockpitSpace.getContainer(point) + + if (container !== undefined && container.detectMouseOver === true) { + containerFound() + return + } + } + + /* We check if the mouse is over a panel/ */ + if (thisObject.panelsSpace !== undefined) { + container = thisObject.panelsSpace.getContainer(point) + + if (container !== undefined && container.detectMouseOver === true) { + containerFound() + return + } + } + + /* If it is not, then we check if it is over any of the existing containers at the Charting Space. */ + if (thisObject.chartingSpace !== undefined) { + container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_OVER) + + if (container !== undefined && container.detectMouseOver === true) { + containerFound() + return + } + } + + /* We check if the mouse is over a floatingObject/ */ + if (thisObject.floatingSpace !== undefined) { + container = thisObject.floatingSpace.getContainer(point) + + if (container !== undefined && container.detectMouseOver === true) { + containerFound() + return + } + } + + function containerFound () { + if (lastContainerMouseOver !== undefined) { + if (container.id !== lastContainerMouseOver.id) { + lastContainerMouseOver.eventHandler.raiseEvent('onMouseNotOver', point) + } + } + container.eventHandler.raiseEvent('onMouseOver', point) + lastContainerMouseOver = container + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onMouseOver -> err = ' + err.stack) } + } + } + + function onMouseWheel (event) { + try { + thisObject.mouse.event = event + thisObject.mouse.position.x = event.pageX + thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN + thisObject.mouse.action = 'wheel' + + // cross-browser wheel delta + var event = window.event || event // old IE support + event.delta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail)) + + /* We try first with panels. */ + + let point = { + x: event.pageX, + y: event.pageY - CURRENT_TOP_MARGIN + } + + event.mousePosition = point + let container + container = canvas.panelsSpace.getContainer({ x: point.x, y: point.y }) + + if (container !== undefined && container.isWheelable === true) { + container.eventHandler.raiseEvent('onMouseWheel', event) + return false // This instructs the browser not to take the event and scroll the page. + } + + /* We try the CockpitSpace. */ + + container = canvas.cockpitSpace.getContainer({ x: point.x, y: point.y }) + + if (container !== undefined && container.isWheelable === true) { + container.eventHandler.raiseEvent('onMouseWheel', event) + return false // This instructs the browser not to take the event and scroll the page. + } + + /* Charting Space. */ + + container = canvas.chartingSpace.getContainer({ x: point.x, y: point.y }, GET_CONTAINER_PURPOSE.MOUSE_WHEEL) + if (container !== undefined && container.isWheelable === true) { + container.eventHandler.raiseEvent('onMouseWheel', event) + return false // This instructs the browser not to take the event and scroll the page. + } + + if (container !== undefined) { + canvas.chartingSpace.viewport.onMouseWheel(event) + return false + } + + /* We try second with floating objects. */ + + container = canvas.floatingSpace.getContainer({ x: point.x, y: point.y }) + + if (container !== undefined && container.isWheelable === true) { + container.eventHandler.raiseEvent('onMouseWheel', event) + return false // This instructs the browser not to take the event and scroll the page. + } + + return false + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onMouseWheel -> err = ' + err.stack) } + } + } + + function deactivateDragging (event) { + try { + if (containerDragStarted || viewPortBeingDragged || floatingObjectDragStarted) { + thisObject.eventHandler.raiseEvent('Drag Finished', undefined) + } + + if ( + containerDragStarted || + floatingObjectDragStarted || + viewPortBeingDragged + ) { + ignoreNextClick = true + } + /* Turn off all the possible things that can be dragged. */ + + containerDragStarted = false + floatingObjectDragStarted = false + viewPortBeingDragged = false + + if (containerBeingDragged !== undefined) { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', event) + containerBeingDragged = undefined + } + + browserCanvas.style.cursor = 'auto' + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] deactivateDragging -> err = ' + err.stack) } + } + } + + function checkDrag (event) { + try { + if (containerDragStarted === true || floatingObjectDragStarted === true || viewPortBeingDragged === true) { + thisObject.mouse.event = event + thisObject.mouse.position.x = event.pageX + thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN + thisObject.mouse.action = 'dragging' + + browserCanvas.style.cursor = 'grabbing' + thisObject.eventHandler.raiseEvent('Dragging', undefined) + + if (viewPortBeingDragged) { + let displaceVector = { + x: dragVector.upX - dragVector.downX, + y: dragVector.upY - dragVector.downY + } + + canvas.chartingSpace.viewport.displace(displaceVector) + } + if (containerDragStarted) { + if (containerBeingDragged !== undefined) { + if (containerBeingDragged.space === 'Charting Space') { + let downCopy = { + x: dragVector.downX, + y: dragVector.downY + } + + let downNoZoom + downNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(downCopy) + + let upCopy = { + x: dragVector.upX, + y: dragVector.upY + } + + let upNoZoom + upNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(upCopy) + + displaceVector = { + x: upNoZoom.x - downNoZoom.x, + y: upNoZoom.y - downNoZoom.y + } + + let moveSucceed = containerBeingDragged.displace(displaceVector) + if (moveSucceed === false) { + deactivateDragging(event) + } + } else { + let displaceVector = { + x: dragVector.upX - dragVector.downX, + y: dragVector.upY - dragVector.downY + } + let moveSucceed = containerBeingDragged.displace(displaceVector) + if (moveSucceed === false) { + deactivateDragging(event) + } + } + } + } + + /* Finally we set the starting point of the new dragVector at this current point. */ + dragVector.downX = dragVector.upX + dragVector.downY = dragVector.upY + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] checkDrag -> err = ' + err.stack) } + } + } +} diff --git a/CanvasApp/ChartingSpace/ChartUtilities.js b/CanvasApp/ChartingSpace/ChartUtilities.js new file mode 100644 index 000000000..cdc205ec2 --- /dev/null +++ b/CanvasApp/ChartingSpace/ChartUtilities.js @@ -0,0 +1,139 @@ + +function getSideDays (timeFrame) { + let daysOnSides + + if (timeFrame < _1_HOUR_IN_MILISECONDS) { + daysOnSides = 1 + } + + if (timeFrame === _1_HOUR_IN_MILISECONDS) { + daysOnSides = 9 + } + + if (timeFrame === _2_HOURS_IN_MILISECONDS) { + daysOnSides = 10 + } + + if (timeFrame === _3_HOURS_IN_MILISECONDS) { + daysOnSides = 12 + } + + if (timeFrame === _4_HOURS_IN_MILISECONDS) { + daysOnSides = 20 + } + + if (timeFrame === _6_HOURS_IN_MILISECONDS) { + daysOnSides = 35 + } + + if (timeFrame === _8_HOURS_IN_MILISECONDS) { + daysOnSides = 44 + } + + if (timeFrame === _12_HOURS_IN_MILISECONDS) { + daysOnSides = 56 + } + + if (timeFrame === ONE_DAY_IN_MILISECONDS) { + daysOnSides = 154 + } + + return daysOnSides +} + +function getCondenseFactor (timeFrame) { + switch (timeFrame) { + 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 (timeFrame) { + switch (timeFrame) { + 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('NEW ZOOM LEVEL', zoomLevel) + if (zoomLevel > 400) { + return _1_MINUTE_IN_MILISECONDS + } + + if (zoomLevel > 360) { + return _2_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 330) { + return _3_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 300) { + return _4_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 270) { + return _5_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 250) { + return _10_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 230) { + return _15_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 210) { + return _20_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 190) { + return _30_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 170) { + return _40_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 150) { + return _45_MINUTES_IN_MILISECONDS + } + + if (zoomLevel > 130) { + return _1_HOUR_IN_MILISECONDS + } + + if (zoomLevel > 110) { + return _2_HOURS_IN_MILISECONDS + } + + if (zoomLevel > 80) { + return _3_HOURS_IN_MILISECONDS + } + + if (zoomLevel > 50) { + return _4_HOURS_IN_MILISECONDS + } + + if (zoomLevel > 35) { + return _6_HOURS_IN_MILISECONDS + } + + if (zoomLevel > 20) { + return _8_HOURS_IN_MILISECONDS + } + + if (zoomLevel > 10) { + return _12_HOURS_IN_MILISECONDS + } + + return ONE_DAY_IN_MILISECONDS +} diff --git a/CanvasApp/ChartingSpace/CoordinateSystem.js b/CanvasApp/ChartingSpace/CoordinateSystem.js new file mode 100644 index 000000000..4fbfc24bd --- /dev/null +++ b/CanvasApp/ChartingSpace/CoordinateSystem.js @@ -0,0 +1,262 @@ + /* + +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 newCoordinateSystem () { + let thisObject = { + min: undefined, + max: undefined, + maxHeight: undefined, + maxWidth: undefined, + scale: undefined, + eventHandler: undefined, + autoMinXScale: true, + autoMaxXScale: true, + autoMinYScale: true, + autoMaxYScale: true, + physics: physics, + reportXValue: reportXValue, + reportYValue: reportYValue, + zoomX: zoomX, + zoomY: zoomY, + recalculateScale: recalculateScale, + transformThisPoint: transformThisPoint, + transformThisPoint2: transformThisPoint2, + unInverseTransform: unInverseTransform, + inverseTransformUncappedY: inverseTransformUncappedY, + initializeX: initializeX, + initializeY: initializeY, + initialize: initialize, + finalize: finalize + } + + thisObject.min = { + x: 0, + y: 0 + } + thisObject.max = { + x: 0, + y: 0 + } + thisObject.scale = { + x: 0, + y: 0 + } + + thisObject.eventHandler = newEventHandler() + thisObject.eventHandler.name = 'Coordinate System' + + let newXMax = -VERY_LARGE_NUMBER + let newXMin = VERY_LARGE_NUMBER + let noXValueReported = true + + let newYMax = -VERY_LARGE_NUMBER + let newYMin = VERY_LARGE_NUMBER + let noYValueReported = true + + return thisObject + + function finalize () { + thisObject.eventHandler.finalize() + thisObject.eventHandler = undefined + } + + function initialize (minValue, maxValue, pMaxWidth, 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 + thisObject.max.x = maxValue.x + + thisObject.min.y = minValue.y + thisObject.max.y = maxValue.y + + thisObject.maxWidth = pMaxWidth + thisObject.maxHeight = pMaxHeight + + recalculateScale() + } + + function physics () { + let mustRecalculate = false + + if ((thisObject.autoMinXScale === true || thisObject.autoMaxXScale === true) && noXValueReported === false) { + if (thisObject.autoMaxXScale === true && thisObject.max.x !== newXMax) { + thisObject.max.x = newXMax + mustRecalculate = true + } + + if (thisObject.autoMaxXScale === true) { + newXMax = -VERY_LARGE_NUMBER + } + + if (thisObject.autoMinXScale === true && thisObject.min.x !== newXMin) { + thisObject.min.x = newXMin + newXMin = VERY_LARGE_NUMBER + mustRecalculate = true + } + + if (thisObject.autoMinXScale === true) { + newXMin = VERY_LARGE_NUMBER + } + + /* Reseting this to start over at each cycle. */ + noXValueReported = true + } + + if ((thisObject.autoMinYScale === true || thisObject.autoMaxYScale === true) && noYValueReported === false) { + if (thisObject.autoMaxYScale === true && thisObject.max.y !== newYMax) { + thisObject.max.y = newYMax + mustRecalculate = true + } + + if (thisObject.autoMaxYScale === true) { + newYMax = -VERY_LARGE_NUMBER + } + + if (thisObject.autoMinYScale === true && thisObject.min.y !== newYMin) { + thisObject.min.y = newYMin + newYMin = VERY_LARGE_NUMBER + mustRecalculate = true + } + + if (thisObject.autoMinYScale === true) { + newYMin = VERY_LARGE_NUMBER + } + + /* Reseting this to start over at each cycle. */ + noYValueReported = true + } + + if (mustRecalculate === true) { + recalculateScale() + } + } + + function reportXValue (value) { + if (thisObject.autoMinXScale === true) { + if (value < newXMin) { newXMin = value } + noXValueReported = false + } + if (thisObject.autoMaxXScale === true) { + if (value > newXMax) { newXMax = value } + noXValueReported = false + } + } + + function reportYValue (value) { + if (thisObject.autoMinYScale === true) { + if (value < newYMin) { newYMin = value } + noYValueReported = false + } + if (thisObject.autoMaxYScale === true) { + if (value > newYMax) { newYMax = value } + noYValueReported = false + } + } + + function zoomX (factor, mousePosition, container) { + let mouseAtCointainer = unTransformThisPoint(mousePosition, container) + let leftDistance = mouseAtCointainer.x + let rightDistance = container.frame.width - mouseAtCointainer.x + let diff = thisObject.max.x - thisObject.min.x + let min = thisObject.min.x + diff * factor * leftDistance / (container.frame.width / 2) + let max = thisObject.max.x - diff * factor * rightDistance / (container.frame.width / 2) + + if (min < max) { + thisObject.min.x = min + thisObject.max.x = max + thisObject.recalculateScale() + } + } + + function zoomY (factor, mousePosition, container) { + let mouseAtCointainer = unTransformThisPoint(mousePosition, container) + let topDistance = mouseAtCointainer.y + let bottomDistance = container.frame.height - mouseAtCointainer.y + let diff = thisObject.max.y - thisObject.min.y + let min = thisObject.min.y + diff * factor * bottomDistance / (container.frame.height / 2) + let max = thisObject.max.y - diff * factor * topDistance / (container.frame.height / 2) + + if (min < max) { + thisObject.min.y = min + thisObject.max.y = max + thisObject.recalculateScale() + } + } + + function recalculateScale (event) { + thisObject.scale.x = thisObject.maxWidth / (thisObject.max.x - thisObject.min.x) + thisObject.scale.y = thisObject.maxHeight / (thisObject.max.y - thisObject.min.y) + + thisObject.eventHandler.raiseEvent('Scale Changed', event) + } + + 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 = thisObject.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) + + thisObject.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 - thisObject.min.x) * thisObject.scale.x, + y: thisObject.maxHeight - (point.y - thisObject.min.y) * thisObject.scale.y + } + + return point + } + + function transformThisPoint2 (point) { + point = { + x: (point.x - thisObject.min.x) * thisObject.scale.x, + y: (thisObject.maxHeight - point.y - thisObject.min.y) * thisObject.scale.y + } + + return point + } + + function unInverseTransform (point, inverseY) { + point = { + x: (point.x / thisObject.scale.x) + thisObject.min.x, + y: (inverseY - point.y) / thisObject.scale.y + thisObject.min.y + } + + return point + } + + function inverseTransformUncappedY (point, inverseY) { + point = { + x: (point.x - thisObject.min.x) * thisObject.scale.x, + y: (inverseY - point.y) * thisObject.scale.y + } + + return point + } +} diff --git a/CanvasApp/ChartingSpace/EdgeEditor.js b/CanvasApp/ChartingSpace/EdgeEditor.js new file mode 100644 index 000000000..f40dffd2f --- /dev/null +++ b/CanvasApp/ChartingSpace/EdgeEditor.js @@ -0,0 +1,475 @@ +function newEdgeEditor () { + const MODULE_NAME = 'Edge Editor' + + let thisObject = { + container: undefined, + fitFunction: undefined, + isVisible: true, + physics: physics, + drawForeground: drawForeground, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + const EDGE_SIZE = 6 + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = true + thisObject.container.isClickeable = false + thisObject.container.isWheelable = false + thisObject.container.detectMouseOver = true + + let buttonUsedForDragging + let isMouseOver + let whereIsMouseOver = 'outside' + + let coordinateSystem + + let onMouseOverEventSubscriptionId + let onMouseNotOverEventSubscriptionId + let onDragStartedEventSubscriptionId + + let mouse = { + position: { + x: 0, + y: 0 + } + } + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onDragStartedEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + mouse = undefined + } + + function initialize (pCoordinateSystem) { + coordinateSystem = pCoordinateSystem + + onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + onDragStartedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragStarted', onDragStarted) + } + + function onMouseOver (event) { + isMouseOver = true + mouse = { + position: { + x: event.x, + y: event.y + } + } + } + + function onMouseNotOver () { + isMouseOver = false + } + + function onDragStarted (event) { + switch (event.buttons) { + case 1: { + buttonUsedForDragging = 'left mouse button' + break + } + case 2: { + buttonUsedForDragging = 'right mouse button' + break + } + } + } + + function getContainer (event, purpose) { + if (thisObject.container.frame.isThisPointHere(event, undefined, undefined, -EDGE_SIZE) === true && event.shiftKey !== true) { + let point = { + x: event.x, + y: event.y + } + point = unTransformThisPoint(point, thisObject.container) + + if (point.x <= 0) { + whereIsMouseOver = 'left' + return thisObject.container + } + + if (point.x >= thisObject.container.frame.width) { + whereIsMouseOver = 'right' + return thisObject.container + } + + if (point.y <= 0) { + whereIsMouseOver = 'top' + return thisObject.container + } + + if (point.y >= thisObject.container.frame.height) { + whereIsMouseOver = 'bottom' + return thisObject.container + } + + whereIsMouseOver = 'center' + switch (purpose) { + case GET_CONTAINER_PURPOSE.MOUSE_OVER: + return // We will let the Time Machine to handle this situation. + break + case GET_CONTAINER_PURPOSE.DRAGGING: + return thisObject.container + break + } + } + } + + function physics () { + if (thisObject.container.frame.position.x === 0 && thisObject.container.frame.position.y === 0) { return } + + let dragVector = { + x: thisObject.container.frame.position.x, + y: thisObject.container.frame.position.y + } + + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + const MIN_WIDTH = 100 + const MIN_HEIGHT = 50 + + switch (whereIsMouseOver) { + case 'center': { + switch (buttonUsedForDragging) { + case 'right mouse button': { + /* This is equivalent to drag the whole Time Machine, so we will apply the translation received onto the Time Machine container. */ + thisObject.container.parentContainer.frame.position.x = thisObject.container.parentContainer.frame.position.x + dragVector.x + thisObject.container.parentContainer.frame.position.y = thisObject.container.parentContainer.frame.position.y + dragVector.y + thisObject.container.parentContainer.eventHandler.raiseEvent('onDisplace') + break + } + case 'left mouse button': { + let point = { + x: -dragVector.x, + y: -dragVector.y + } + let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + let newMaxRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + let xDifferenceMaxMin = coordinateSystem.max.x - coordinateSystem.min.x + let yDifferenceMaxMin = coordinateSystem.max.y - coordinateSystem.min.y + coordinateSystem.min.x = newMinDate.valueOf() + coordinateSystem.max.x = newMinDate.valueOf() + xDifferenceMaxMin + coordinateSystem.min.y = newMaxRate - yDifferenceMaxMin + coordinateSystem.max.y = newMaxRate + let event = { + type: 'center dragged', + dragVector: dragVector + } + coordinateSystem.recalculateScale(event) + } + } + thisObject.container.parentContainer.eventHandler.raiseEvent('onMouseOver', mouse.position) + break + } + case 'top' : { + if (thisObject.container.parentContainer.frame.height - dragVector.y < MIN_HEIGHT && dragVector.y > 0) { + return + } + + let point = { + x: dragVector.x, + y: dragVector.y + } + let newMaxRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + + thisObject.container.parentContainer.frame.position.y = thisObject.container.parentContainer.frame.position.y + dragVector.y + thisObject.container.parentContainer.frame.height = thisObject.container.parentContainer.frame.height - dragVector.y + + switch (buttonUsedForDragging) { + case 'left mouse button': { + coordinateSystem.max.y = newMaxRate + coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height + let event = { + type: 'top dragged', + dragVector: dragVector + } + coordinateSystem.recalculateScale(event) + break + } + case 'right mouse button': { + coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height + coordinateSystem.recalculateScale() + break + } + } + thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event) + break + } + case 'bottom' : { + if (thisObject.container.parentContainer.frame.height + dragVector.y < MIN_HEIGHT && dragVector.y < 0) { + return + } + + let point = { + x: dragVector.x, + y: dragVector.y + thisObject.container.frame.height + } + let newMinRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + + thisObject.container.parentContainer.frame.height = thisObject.container.parentContainer.frame.height + dragVector.y + + switch (buttonUsedForDragging) { + case 'left mouse button': { + coordinateSystem.min.y = newMinRate + coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height + let event = { + type: 'bottom dragged', + dragVector: dragVector + } + coordinateSystem.recalculateScale(event) + break + } + case 'right mouse button': { + coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height + coordinateSystem.recalculateScale() + break + } + } + thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event) + break + } + case 'left' : { + if (thisObject.container.parentContainer.frame.width - dragVector.x < MIN_WIDTH && dragVector.x > 0) { + return + } + let point = { + x: dragVector.x, + y: dragVector.y + } + let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + + thisObject.container.parentContainer.frame.position.x = thisObject.container.parentContainer.frame.position.x + dragVector.x + thisObject.container.parentContainer.frame.width = thisObject.container.parentContainer.frame.width - dragVector.x + + switch (buttonUsedForDragging) { + case 'left mouse button': { + coordinateSystem.min.x = newMinDate.valueOf() + coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width + coordinateSystem.recalculateScale() + break + } + case 'right mouse button': { + coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width + coordinateSystem.recalculateScale() + break + } + } + thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event) + break + } + case 'right' : { + if (thisObject.container.parentContainer.frame.width + dragVector.x < MIN_WIDTH && dragVector.x < 0) { + return + } + + let point = { + x: dragVector.x + thisObject.container.frame.width, + y: dragVector.y + } + let newMaxDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem) + + thisObject.container.parentContainer.frame.width = thisObject.container.parentContainer.frame.width + dragVector.x + + switch (buttonUsedForDragging) { + case 'left mouse button': { + coordinateSystem.max.x = newMaxDate.valueOf() + coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width + coordinateSystem.recalculateScale() + break + } + case 'right mouse button': { + coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width + coordinateSystem.recalculateScale() + break + } + } + thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event) + break + } + } + } + + function drawForeground () { + let MARGIN = 1 + let edgeSize + let lineWidth + + if (whereIsMouseOver === 'outside' || isMouseOver === false) { + edgeSize = EDGE_SIZE / 2 + lineWidth = 0.1 + } else { + edgeSize = EDGE_SIZE + lineWidth = 0.5 + } + + const OPACITY = 1 + + pointA1 = { + x: 0, + y: 0 + } + + pointA2 = { + x: thisObject.container.frame.width, + y: 0 + } + + pointA3 = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + } + + pointA4 = { + x: 0, + y: thisObject.container.frame.height + } + + pointB1 = { + x: 0, + y: 0 + } + + pointB2 = { + x: thisObject.container.frame.width, + y: 0 + } + + pointB3 = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + } + + pointB4 = { + x: 0, + y: thisObject.container.frame.height + } + + pointA1 = transformThisPoint(pointA1, thisObject.container) + pointA2 = transformThisPoint(pointA2, thisObject.container) + pointA3 = transformThisPoint(pointA3, thisObject.container) + pointA4 = transformThisPoint(pointA4, thisObject.container) + + pointB1 = transformThisPoint(pointB1, thisObject.container) + pointB2 = transformThisPoint(pointB2, thisObject.container) + pointB3 = transformThisPoint(pointB3, thisObject.container) + pointB4 = transformThisPoint(pointB4, thisObject.container) + + pointA1 = { + x: pointA1.x - MARGIN, + y: pointA1.y - MARGIN + } + + pointA2 = { + x: pointA2.x + MARGIN, + y: pointA2.y - MARGIN + } + + pointA3 = { + x: pointA3.x + MARGIN, + y: pointA3.y + MARGIN + } + + pointA4 = { + x: pointA4.x - MARGIN, + y: pointA4.y + MARGIN + } + + pointB1 = { + x: pointB1.x - edgeSize, + y: pointB1.y - edgeSize + } + + pointB2 = { + x: pointB2.x + edgeSize, + y: pointB2.y - edgeSize + } + + pointB3 = { + x: pointB3.x + edgeSize, + y: pointB3.y + edgeSize + } + + pointB4 = { + x: pointB4.x - edgeSize, + y: pointB4.y + edgeSize + } + + pointA1 = canvas.chartingSpace.viewport.fitIntoViewport(pointA1) + pointA2 = canvas.chartingSpace.viewport.fitIntoViewport(pointA2) + pointA3 = canvas.chartingSpace.viewport.fitIntoViewport(pointA3) + pointA4 = canvas.chartingSpace.viewport.fitIntoViewport(pointA4) + + pointB1 = canvas.chartingSpace.viewport.fitIntoViewport(pointB1) + pointB2 = canvas.chartingSpace.viewport.fitIntoViewport(pointB2) + pointB3 = canvas.chartingSpace.viewport.fitIntoViewport(pointB3) + pointB4 = canvas.chartingSpace.viewport.fitIntoViewport(pointB4) + + browserCanvasContext.setLineDash([0, 0]) + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(pointA1.x, pointA1.y) + browserCanvasContext.lineTo(pointA2.x, pointA2.y) + browserCanvasContext.lineTo(pointB2.x, pointB2.y) + browserCanvasContext.lineTo(pointB1.x, pointB1.y) + browserCanvasContext.lineTo(pointA1.x, pointA1.y) + browserCanvasContext.closePath() + + drawEdge('top') + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(pointA2.x, pointA2.y) + browserCanvasContext.lineTo(pointA3.x, pointA3.y) + browserCanvasContext.lineTo(pointB3.x, pointB3.y) + browserCanvasContext.lineTo(pointB2.x, pointB2.y) + browserCanvasContext.lineTo(pointA2.x, pointA2.y) + browserCanvasContext.closePath() + + drawEdge('right') + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(pointA3.x, pointA3.y) + browserCanvasContext.lineTo(pointA4.x, pointA4.y) + browserCanvasContext.lineTo(pointB4.x, pointB4.y) + browserCanvasContext.lineTo(pointB3.x, pointB3.y) + browserCanvasContext.lineTo(pointA3.x, pointA3.y) + browserCanvasContext.closePath() + + drawEdge('bottom') + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(pointA4.x, pointA4.y) + browserCanvasContext.lineTo(pointA1.x, pointA1.y) + browserCanvasContext.lineTo(pointB1.x, pointB1.y) + browserCanvasContext.lineTo(pointB4.x, pointB4.y) + browserCanvasContext.lineTo(pointA4.x, pointA4.y) + browserCanvasContext.closePath() + + drawEdge('left') + + function drawEdge (edgeType) { + if (whereIsMouseOver === edgeType && isMouseOver === true) { + browserCanvasContext.lineWidth = lineWidth + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')' + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + } else { + browserCanvasContext.lineWidth = lineWidth + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')' + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')' + } + browserCanvasContext.fill() + browserCanvasContext.stroke() + } + } +} diff --git a/CanvasApp/ChartingSpace/PlottersManager.js b/CanvasApp/ChartingSpace/PlottersManager.js new file mode 100644 index 000000000..3b245cf99 --- /dev/null +++ b/CanvasApp/ChartingSpace/PlottersManager.js @@ -0,0 +1,309 @@ +function newPlottersManager () { + const MODULE_NAME = 'Plotters Manager' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let timeFrame = INITIAL_TIME_PERIOD + let datetime = NEW_SESSION_INITIAL_DATE + + let thisObject = { + fitFunction: undefined, + container: undefined, + payload: undefined, + connectors: [], + setDatetime: setDatetime, + setTimeFrame: setTimeFrame, + setCoordinateSystem: setCoordinateSystem, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.connectors = [] + + let initializationReady = false + let layersPanel + let onLayerStatusChangedEventSuscriptionId + let coordinateSystem + + setupContainer() + return thisObject + + function setupContainer () { + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + } + + function finalize () { + layersPanel.container.eventHandler.stopListening(onLayerStatusChangedEventSuscriptionId) + layersPanel = undefined + + for (let i = 0; i < thisObject.connectors.length; i++) { + let connector = thisObject.connectors[i] + /* Then the panels. */ + for (let j = 0; j < connector.panels.length; j++) { + canvas.panelsSpace.destroyPanel(connector.panels[j]) + connector.panels[j] = undefined + } + connector.panels = undefined + + /* Finalize the plotters */ + if (connector.plotter.finalize !== undefined) { + connector.plotter.container.eventHandler.stopListening(connector.plotter.onRecordChangeEventsSubscriptionId) + connector.plotter.finalize() + } + connector.plotter = undefined + + /* Finally the Storage Objects */ + finalizeStorage(connector.storage) + connector.storage = undefined + + connector = undefined + } + thisObject.connectors = [] + coordinateSystem = undefined + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.payload = undefined + } + + function finalizeStorage (storage) { + storage.eventHandler.stopListening(storage.onMarketFileLoadedLayerEventsSubscriptionId) + storage.eventHandler.stopListening(storage.onDailyFileLoadedLayerEventsSubscriptionId) + storage.eventHandler.stopListening(storage.onSingleFileLoadedLayerEventsSubscriptionId) + storage.eventHandler.stopListening(storage.onFileSequenceLoadedLayerEventsSubscriptionId) + storage.eventHandler.stopListening(storage.onDailyFileLoadedPlotterEventsSubscriptionId) + storage.finalize() + } + + function initialize (pLayersPanel) { + /* Remember the Layers Panel */ + layersPanel = pLayersPanel + + /* Listen to the event of change of status */ + onLayerStatusChangedEventSuscriptionId = layersPanel.container.eventHandler.listenToEvent('Layer Status Changed', onLayerStatusChanged) + } + + function initializePlotter (layer) { + try { + /* Before Initializing a Plotter, we need the Storage it will use, loaded with the files it will initially need. */ + let storage = newProductStorage(layer.payload.node.id) + + /* + Before Initializing the Storage, we will put the Layer 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 < layer.definition.referenceParent.referenceParent.datasets.length; i++) { + let dataset = layer.definition.referenceParent.referenceParent.datasets[i] + + switch (dataset.code.type) { + case 'Market Files': { + storage.onMarketFileLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('Market File Loaded', layer.onMarketFileLoaded) + } + break + case 'Daily Files': { + storage.onDailyFileLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('Daily File Loaded', layer.onDailyFileLoaded) + } + break + case 'Single File': { + storage.onSingleFileLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('Single File Loaded', layer.onSingleFileLoaded) + } + break + case 'File Sequence': { + storage.onFileSequenceLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('File Sequence Loaded', layer.onFileSequenceLoaded) + } + break + default: { + if (ERROR_LOG === true) { + logger.write('[ERROR] initializePlotter -> Dataset Type Incorrect or Undefined -> dataset.type = ' + dataset.type) + } + return + } + } + } + + let baseAsset = layer.definition.referenceParent.parentNode.referenceParent.baseAsset.referenceParent.code.codeName + let quotedAsset = layer.definition.referenceParent.parentNode.referenceParent.quotedAsset.referenceParent.code.codeName + let market = { + baseAsset: baseAsset, + quotedAsset: quotedAsset + } + let product = layer.definition.referenceParent.referenceParent + let bot = layer.definition.referenceParent.referenceParent.parentNode + let dataMine = layer.definition.referenceParent.referenceParent.parentNode.parentNode + let exchange = layer.definition.referenceParent.parentNode.referenceParent.parentNode.parentNode + let plotterModule = layer.definition.referenceParent.referenceParent.referenceParent + let session + + if (layer.definition.referenceParent.parentNode.parentNode !== undefined) { + if (layer.definition.referenceParent.parentNode.parentNode.type === 'Session Reference') { + if (layer.definition.referenceParent.parentNode.parentNode.referenceParent !== undefined) { + session = layer.definition.referenceParent.parentNode.parentNode.referenceParent + } + } + } + + storage.initialize( + dataMine, + bot, + session, + product, + exchange, + market, + datetime, + timeFrame, + onProductStorageInitialized + ) + + function onProductStorageInitialized (err) { + try { + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + /* Now we have all the initial data loaded and ready to be delivered to the new instance of the plotter. */ + let plotter + + if (plotterModule.code.isLegacy !== true) { + plotter = newPlotter() + } else { + plotter = getNewPlotter(dataMine.code.codeName, plotterModule.parentNode.code.codeName, plotterModule.code.codeName) + } + + plotter.container.connectToParent(thisObject.container, true, true, false, true, true, true, false, false, true) + 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.fitFunction = thisObject.fitFunction + plotter.initialize(storage, datetime, timeFrame, coordinateSystem, onPlotterInizialized, product) + + function onPlotterInizialized () { + try { + let connector = { + layer: layer, + plotter: plotter, + storage: storage + } + /* Let the Plotter listen to the event of Cursor Files loaded, so that it can react recalculating if needed. */ + plotter.onDailyFileLoadedPlotterEventsSubscriptionId = storage.eventHandler.listenToEvent('Daily File Loaded', plotter.onDailyFileLoaded) + /* Lets load now this plotter panels. */ + connector.panels = [] + + if (product !== undefined) { + for (let i = 0; i < plotterModule.panels.length; i++) { + let panel = plotterModule.panels[i] + + let parameters = { + dataMine: dataMine.code.codeName, + plotterCodeName: plotterModule.parentNode.code.codeName, + moduleCodeName: plotterModule.code.codeName, + panelNode: panel + } + + let owner = thisObject.payload.node.payload.parentNode.id // Panels are owned by the time machine. + let plotterPanelHandle = canvas.panelsSpace.createNewPanel('Plotter Panel', parameters, owner, layer.session) + let plotterPanel = canvas.panelsSpace.getPanel(plotterPanelHandle) + + /* Connect Panel to the Plotter via an Event. */ + connector.plotter.onRecordChangeEventsSubscriptionId = connector.plotter.container.eventHandler.listenToEvent('Current Record Changed', plotterPanel.onRecordChange) + connector.panels.push(plotterPanelHandle) + } + + connector.layer.panels = connector.panels + } + + thisObject.connectors.push(connector) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initializePlotter -> onProductStorageInitialized -> onPlotterInizialized -> err = ' + err.stack) } + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initializePlotter -> onProductStorageInitialized -> err = ' + err.stack) } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initializePlotter -> err = ' + err.stack) } + } + } + + function onLayerStatusChanged (layer) { + if (layer.status === LAYER_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 < thisObject.connectors.length; i++) { + let connector = thisObject.connectors[i] + if (connector.layer.payload.node.id === layer.payload.node.id) { + found = true + } + } + if (found === false) { + initializePlotter(layer, onProductPlotterInitialized) + function onProductPlotterInitialized (err) { + /* There is no policy yet of what to do if this fails. */ + } + } + } + if (layer.status === LAYER_STATUS.OFF) { + /* If the plotter of this card is not on our Active Plotters list, then we remove it. */ + for (let i = 0; i < thisObject.connectors.length; i++) { + let connector = thisObject.connectors[i] + if (connector.layer.payload.node.id === layer.payload.node.id) { + /* Then the panels. */ + for (let j = 0; j < connector.panels.length; j++) { + canvas.panelsSpace.destroyPanel(connector.panels[j]) + } + /* Finally the Storage Objects */ + + if (connector.plotter.finalize !== undefined) { + connector.plotter.container.eventHandler.stopListening(connector.plotter.onRecordChangeEventsSubscriptionId) + connector.plotter.container.finalize() + connector.plotter.finalize() + } + finalizeStorage(connector.storage) + thisObject.connectors.splice(i, 1) // Delete item from array. + return // We already found the product woth changes and processed it. + } + } + } + } + + function setTimeFrame (pTimeFrame) { + timeFrame = pTimeFrame + for (let i = 0; i < thisObject.connectors.length; i++) { + let connector = thisObject.connectors[i] + connector.layer.setTimeFrame(timeFrame) + connector.storage.setTimeFrame(timeFrame) + connector.plotter.setTimeFrame(timeFrame) + } + } + + function setDatetime (pDatetime) { + datetime = pDatetime + for (let i = 0; i < thisObject.connectors.length; i++) { + let connector = thisObject.connectors[i] + connector.layer.setDatetime(pDatetime) + connector.storage.setDatetime(pDatetime) + connector.plotter.setDatetime(pDatetime) + } + } + + function setCoordinateSystem (pCoordinateSystem) { + coordinateSystem = pCoordinateSystem + for (let i = 0; i < thisObject.connectors.length; i++) { + let connector = thisObject.connectors[i] + connector.plotter.setCoordinateSystem(coordinateSystem) + } + } + + function getContainer (point) { + } + + function draw () { + if (thisObject.connectors === undefined) { return } // We need to wait + /* First the Product Plotters. */ + for (let i = 0; i < thisObject.connectors.length; i++) { + let connector = thisObject.connectors[thisObject.connectors.length - i - 1] + connector.plotter.draw() + } + } +} diff --git a/CanvasApp/ChartingSpace/TimeMachine.js b/CanvasApp/ChartingSpace/TimeMachine.js new file mode 100644 index 000000000..948938cd1 --- /dev/null +++ b/CanvasApp/ChartingSpace/TimeMachine.js @@ -0,0 +1,629 @@ +/* + +Markets are a function of time. When watching them, end users must be positioned at one particular point in time. The system currently allows users +to position themselves at any time they like. + +In the future, it will be usefull to explore markets and compare them at different times simultaneously. Anticipating that future this module exists. +All the timelineCharts that depand on a datetime are children of this object Time Machine. In the future we will allow users to have more than one Time Machine, +each one with it own timelineCharts, and each one positioned at an especific point in titme. + +*/ + +function newTimeMachine () { + const MODULE_NAME = 'Time Machine' + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let timeFrame = INITIAL_TIME_PERIOD + + let thisObject = { + container: undefined, + timeScale: undefined, + rateScale: undefined, + payload: undefined, + edgeEditor: undefined, + timelineCharts: [], + fitFunction: fitFunction, + physics: physics, + draw: draw, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, // returns the inner most container that holds the point received by parameter. + initialize: initialize, + finalize: finalize + } + + let timeMachineCoordinateSystem = newCoordinateSystem() + + let mouse = { + position: { + x: 0, + y: 0 + } + } + + let drawScales = false + let syncWithDesignerLoop = 0 + let timelineChartsMap = new Map() + + let onViewportPositionChangedEventSuscriptionId + let onViewportZoomChangedEventSuscriptionId + let onMouseOverEventSuscriptionId + let onMouseNotOverEventSuscriptionId + let timeScaleMouseOverEventSuscriptionId + let rateScaleMouseOverEventSuscriptionId + let timeFrameScaleEventSuscriptionId + let timeFrameScaleMouseOverEventSuscriptionId + let onScaleChangedEventSubscriptionId + + setupContainer() + return thisObject + + function setupContainer () { + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.fitFunction = thisObject.fitFunction + thisObject.container.isDraggeable = false + thisObject.container.insideViewport = true + thisObject.container.detectMouseOver = true + + thisObject.container.frame.width = TIME_MACHINE_WIDTH + thisObject.container.frame.height = TIME_MACHINE_HEIGHT + } + + function finalize () { + canvas.chartingSpace.viewport.eventHandler.stopListening(onViewportPositionChangedEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(onViewportZoomChangedEventSuscriptionId) + timeMachineCoordinateSystem.eventHandler.stopListening(onScaleChangedEventSubscriptionId) + + if (thisObject.timeScale !== undefined) { + finalizeTimeScale() + } + + if (thisObject.rateScale !== undefined) { + finalizeRateScale() + } + + if (thisObject.timeFrameScale !== undefined) { + finalizeTimeFrameScale() + } + + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + thisObject.container.eventHandler.stopListening(onMouseNotOverEventSuscriptionId) + + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[i] + timelineChart.container.eventHandler.stopListening(timelineChart.onChildrenMouseOverEventSuscriptionId) + timelineChart.finalize() + } + + thisObject.timelineCharts = undefined + thisObject.fitFunction = undefined + thisObject.container.finalize() + thisObject.container = undefined + thisObject.payload = undefined + thisObject.edgeEditor = undefined + + mouse = undefined + timelineChartsMap = undefined + + timeMachineCoordinateSystem.finalize() + timeMachineCoordinateSystem = undefined + } + + function finalizeTimeScale () { + if (thisObject.timeScale === undefined) { return } + + thisObject.timeScale.container.eventHandler.stopListening(timeScaleMouseOverEventSuscriptionId) + thisObject.timeScale.finalize() + thisObject.timeScale = undefined + } + + function finalizeRateScale () { + if (thisObject.rateScale === undefined) { return } + + thisObject.rateScale.container.eventHandler.stopListening(rateScaleMouseOverEventSuscriptionId) + thisObject.rateScale.finalize() + thisObject.rateScale = undefined + } + + function finalizeTimeFrameScale () { + if (thisObject.timeFrameScale === undefined) { return } + + thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleEventSuscriptionId) + thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleMouseOverEventSuscriptionId) + thisObject.timeFrameScale.finalize() + thisObject.timeFrameScale = undefined + } + + function initialize (callBackFunction) { + timeFrame = INITIAL_TIME_PERIOD + loadFrame(thisObject.payload, thisObject.container.frame) + + // thisObject.container.frame.position.x = 0 + // thisObject.container.frame.position.y = 0 + + recalculateCoordinateSystem() + recalculateCurrentDatetime() + + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + onMouseNotOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + onViewportPositionChangedEventSuscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Position Changed', onViewportPositionChanged) + onViewportZoomChangedEventSuscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Zoom Changed', onViewportZoomChanged) + onScaleChangedEventSubscriptionId = timeMachineCoordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + + thisObject.edgeEditor = newEdgeEditor() + thisObject.edgeEditor.initialize(timeMachineCoordinateSystem) + thisObject.edgeEditor.container.connectToParent(thisObject.container, true, true, false, true, true, true) + + callBackFunction() + } + + function onMouseOver (event) { + drawScales = true + + mouse.position.x = event.x + mouse.position.y = event.y + + if (thisObject.timeScale !== undefined) { + thisObject.timeScale.onMouseOverSomeTimeMachineContainer(event) + } + if (thisObject.rateScale !== undefined) { + thisObject.rateScale.onMouseOverSomeTimeMachineContainer(event) + } + if (thisObject.timeFrameScale !== undefined) { + thisObject.timeFrameScale.onMouseOverSomeTimeMachineContainer(event) + } + } + + function onMouseNotOver (event) { + drawScales = false + + if (thisObject.timeScale !== undefined) { + thisObject.timeScale.visible = false + } + if (thisObject.rateScale !== undefined) { + thisObject.rateScale.visible = false + } + if (thisObject.timeFrameScale !== undefined) { + thisObject.timeFrameScale.visible = false + } + } + + function initializeTimeScale () { + thisObject.timeScale = newTimeScale() + thisObject.timeScale.fitFunction = thisObject.fitFunction + thisObject.timeScale.payload = thisObject.payload.node.timeScale.payload + + timeScaleMouseOverEventSuscriptionId = thisObject.timeScale.container.eventHandler.listenToEvent('onMouseOverScale', timeScaleMouseOver) + thisObject.timeScale.initialize(timeMachineCoordinateSystem, thisObject.container) + + function timeScaleMouseOver (event) { + thisObject.container.eventHandler.raiseEvent('onMouseOver', event) + } + } + + function initializeRateScale () { + thisObject.rateScale = newRateScale() + thisObject.rateScale.fitFunction = thisObject.fitFunction + thisObject.rateScale.payload = thisObject.payload.node.rateScale.payload + + rateScaleMouseOverEventSuscriptionId = thisObject.rateScale.container.eventHandler.listenToEvent('onMouseOverScale', rateScaleMouseOver) + thisObject.rateScale.initialize(timeMachineCoordinateSystem, thisObject.container, thisObject.container) + + function rateScaleMouseOver (event) { + thisObject.container.eventHandler.raiseEvent('onMouseOver', event) + } + } + + function initializeTimeFrameScale () { + thisObject.timeFrameScale = newTimeFrameScale() + thisObject.timeFrameScale.fitFunction = thisObject.fitFunction + thisObject.timeFrameScale.payload = thisObject.payload.node.timeFrameScale.payload + + timeFrameScaleEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('Time Frame Value Changed', timeFrameScaleValueChanged) + timeFrameScaleMouseOverEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('onMouseOverScale', timeFrameScaleMouseOver) + thisObject.timeFrameScale.initialize(thisObject.container) + + function timeFrameScaleValueChanged (event) { + let currentTimeFrame = timeFrame + timeFrame = event.timeFrame + if (timeFrame !== currentTimeFrame) { + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[i] + timelineChart.upstreamTimeFrame = timeFrame + if (timelineChart.timeFrameScale === undefined) { + timelineChart.setTimeFrame(timeFrame) + } + } + } + } + function timeFrameScaleMouseOver (event) { + thisObject.container.eventHandler.raiseEvent('onMouseOver', event) + } + } + + function getContainer (point, purpose) { + let container + + if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { + container = thisObject.rateScale.getContainer(point, purpose) + if (container !== undefined) { + if (container.isForThisPurpose(purpose)) { + return container + } else { + if (purpose === GET_CONTAINER_PURPOSE.DRAGGING && container.isClickeable === true) { + return container + } + } + } + } + + if (thisObject.timeScale !== undefined && thisObject.timeScale.isVisible === true) { + container = thisObject.timeScale.getContainer(point, purpose) + if (container !== undefined) { + if (container.isForThisPurpose(purpose)) { + return container + } else { + if (purpose === GET_CONTAINER_PURPOSE.DRAGGING && container.isClickeable === true) { + return container + } + } + } + } + + if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { + container = thisObject.timeFrameScale.getContainer(point, purpose) + if (container !== undefined) { + if (container.isForThisPurpose(purpose)) { + return container + } + } + } + + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + container = thisObject.timelineCharts[i].getContainer(point, purpose) + if (container !== undefined) { + if (container.isForThisPurpose(purpose)) { + if (thisObject.container.frame.isThisPointHere(point) === true) { + return container + } + } else { + if (purpose === GET_CONTAINER_PURPOSE.DRAGGING && container.isClickeable === true) { + if (thisObject.container.frame.isThisPointHere(point) === true) { + return container + } + } + } + } + } + + container = thisObject.edgeEditor.getContainer(point, purpose) + if (container !== undefined) { + return container + } else { + if (thisObject.container.isForThisPurpose(purpose)) { + if (thisObject.container.frame.isThisPointHere(point) === true) { + return thisObject.container + } + } + } + } + + function onViewportZoomChanged (event) { + if (thisObject.container.frame.isInViewPort()) { + recalculateCurrentDatetime() + } + } + + function onViewportPositionChanged () { + if (thisObject.container.frame.isInViewPort()) { + recalculateCurrentDatetime() + } + } + + function onScaleChanged () { + recalculateCurrentDatetime() + } + + function recalculateCurrentDatetime () { + /* + The view port was moved or the view port zoom level was changed and the center of the screen points to a different datetime that we + must calculate. + */ + let center = { + x: (canvas.chartingSpace.viewport.visibleArea.bottomRight.x - canvas.chartingSpace.viewport.visibleArea.bottomLeft.x) / 2, + y: (canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topRight.y) / 2 + } + + center = unTransformThisPoint(center, thisObject.container) + center = timeMachineCoordinateSystem.unInverseTransform(center, thisObject.container.frame.height) + + datetime = new Date(center.x) + + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[i] + timelineChart.setDatetime(datetime) + } + } + + function fitFunction (point, fullVisible, margin, topMargin, bottomMargin) { + /* We prevent a point to be out of the container AND out of the Charting Space in general */ + + let returnPoint = { + x: point.x, + y: point.y + } + + let upCorner = { + x: 0, + y: 0 + } + + let bottonCorner = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + } + + upCorner = transformThisPoint(upCorner, thisObject.container) + bottonCorner = transformThisPoint(bottonCorner, thisObject.container) + + /* Checking against the container limits. */ + if (margin === undefined) { margin = 0 } + if (topMargin === undefined) { topMargin = 0 } + if (bottomMargin === undefined) { bottomMargin = 0 } + + if (returnPoint.x - margin < upCorner.x) { returnPoint.x = upCorner.x + margin } + if (returnPoint.x + margin > bottonCorner.x) { returnPoint.x = bottonCorner.x - margin } + if (returnPoint.y - margin - topMargin < upCorner.y) { returnPoint.y = upCorner.y + margin + topMargin } + if (returnPoint.y + margin + bottomMargin > bottonCorner.y) { returnPoint.y = bottonCorner.y - margin - bottomMargin } + + returnPoint = canvas.chartingSpace.fitFunction(returnPoint, fullVisible) + + return returnPoint + } + + function physics () { + thisObject.edgeEditor.physics() + timeMachineCoordinateSystem.physics() + + saveFrame(thisObject.payload, thisObject.container.frame) + if (thisObject.container.frame.isInViewPort()) { + childrenPhysics() + syncWithDesigner() + } + panelPhysics() + } + + function panelPhysics () { + if (thisObject.container.frame.isInViewPort() && canvas.chartingSpace.viewport.zoomTargetLevel > ZOOM_OUT_THRESHOLD_FOR_HIDDING_PANELS) { + canvas.panelsSpace.unHide(thisObject.payload.node.id, 'Layers Panel') + canvas.panelsSpace.unHide(thisObject.payload.node.id, 'Plotter Panel') + } else { + canvas.panelsSpace.hide(thisObject.payload.node.id, 'Layers Panel') + canvas.panelsSpace.hide(thisObject.payload.node.id, 'Plotter Panel') + } + } + + function syncWithDesigner () { + syncWithDesignerTimelineCharts() + syncWithDesignerScales() + } + + function syncWithDesignerScales () { + if (thisObject.payload.node === undefined) { + finalizeTimeScale() + finalizeRateScale() + finalizeTimeFrameScale() + return + } + if (thisObject.payload.node.timeScale === undefined && thisObject.timeScale !== undefined) { + finalizeTimeScale() + } + if (thisObject.payload.node.timeScale !== undefined && thisObject.timeScale === undefined) { + initializeTimeScale() + } + if (thisObject.payload.node.rateScale === undefined && thisObject.rateScale !== undefined) { + finalizeRateScale() + } + if (thisObject.payload.node.rateScale !== undefined && thisObject.rateScale === undefined) { + initializeRateScale() + } + if (thisObject.payload.node.timeFrameScale === undefined && thisObject.timeFrameScale !== undefined) { + finalizeTimeFrameScale() + } + if (thisObject.payload.node.timeFrameScale !== undefined && thisObject.timeFrameScale === undefined) { + initializeTimeFrameScale() + } + } + + function syncWithDesignerTimelineCharts () { + syncWithDesignerLoop = syncWithDesignerLoop + 0.00000000001 + + for (let j = 0; j < thisObject.payload.node.timelineCharts.length; j++) { + let node = thisObject.payload.node.timelineCharts[j] + let timelineChart = timelineChartsMap.get(node.id) + if (timelineChart === undefined) { + /* The timeline timelineChart node is new, thus we need to initialize a new timelineChart */ + initializeTimelineChart(node, syncWithDesignerLoop) + } else { + /* The time machine already exists, we tag it as existing at the current loop. */ + timelineChart.syncWithDesignerLoop = syncWithDesignerLoop + } + } + + /* We check all the timeMachines we have to see if we need to remove any of them */ + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[i] + if (timelineChart.syncWithDesignerLoop < syncWithDesignerLoop) { + /* Must be removed */ + timelineChart.container.eventHandler.stopListening(timelineChart.onChildrenMouseOverEventSuscriptionId) + timelineChart.finalize() + timelineChartsMap.delete(timelineChart.nodeId) + thisObject.timelineCharts.splice(i, 1) + /* We remove one at the time */ + return + } + } + + function initializeTimelineChart (node, syncWithDesignerLoop) { + let timelineChart = newTimelineChart() + timelineChart.syncWithDesignerLoop = syncWithDesignerLoop + timelineChart.payload = node.payload + timelineChart.nodeId = node.id + timelineChartsMap.set(node.id, timelineChart) + timelineChart.payload.uiObject.setValue('Loading...') + + /* Setting up the new timeline timelineChart. */ + timelineChart.container.connectToParent(thisObject.container, true, true, false, true, true, true, false, false, true) + timelineChart.container.fitFunction = thisObject.container.fitFunction + timelineChart.fitFunction = thisObject.fitFunction + timelineChart.container.frame.width = thisObject.container.frame.width + timelineChart.container.frame.height = thisObject.container.frame.height + timelineChart.container.frame.position.x = 0 + timelineChart.container.frame.position.y = 0 + timelineChart.initialize(timeMachineCoordinateSystem, timeFrame) + + /* we will store the event suscription id as a property of the timelineChart, to avoid keeping it an a separate array */ + timelineChart.onChildrenMouseOverEventSuscriptionId = timelineChart.container.eventHandler.listenToEvent('onChildrenMouseOver', onChildrenMouseOver) + + thisObject.timelineCharts.push(timelineChart) + timelineChart.payload.uiObject.setValue('') + + function onChildrenMouseOver (event) { + thisObject.container.eventHandler.raiseEvent('onMouseOver', event) + } + } + } + + function childrenPhysics () { + if (thisObject.timeScale !== undefined) { + thisObject.timeScale.physics() + } + if (thisObject.rateScale !== undefined) { + thisObject.rateScale.physics() + } + if (thisObject.timeFrameScale !== undefined) { + thisObject.timeFrameScale.physics() + } + + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[i] + timelineChart.physics() + } + } + + function drawBackground () { + drawChartsBackground() + if (thisObject.container.frame.isInViewPort()) { + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[thisObject.timelineCharts.length - i - 1] + timelineChart.drawBackground() + } + } + } + + function drawChartsBackground () { + drawContainerBackground(thisObject.container, UI_COLOR.WHITE, 0.5, thisObject.fitFunction) + } + + function draw () { + if (thisObject.payload === undefined) { return } + if (thisObject.payload.node === undefined) { return } + if (thisObject.container.frame.isInViewPort()) { + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[thisObject.timelineCharts.length - i - 1] + timelineChart.draw() + } + + if (drawScales === true) { + if (thisObject.timeScale !== undefined && thisObject.timeScale.isVisible === true) { thisObject.timeScale.draw() } + if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.draw() } + if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.draw() } + } + } else { + let icon = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type) + if (icon !== undefined) { + if (icon.canDrawIcon === true) { + let imageSize = 40 + let imagePosition = { + x: thisObject.container.frame.width / 2, + y: thisObject.container.frame.height / 2 + } + + imagePosition = transformThisPoint(imagePosition, thisObject.container) + imagePosition = thisObject.fitFunction(imagePosition, true) + + browserCanvasContext.drawImage( + icon, + imagePosition.x - imageSize / 2, + imagePosition.y - imageSize / 2, + imageSize, + imageSize) + } + } + } + } + + function drawForeground () { + if (thisObject.container.frame.isInViewPort()) { + for (let i = 0; i < thisObject.timelineCharts.length; i++) { + let timelineChart = thisObject.timelineCharts[thisObject.timelineCharts.length - i - 1] + timelineChart.drawForeground() + } + + if (drawScales === true) { + if (thisObject.timeScale !== undefined && thisObject.timeScale.isVisible === true) { thisObject.timeScale.drawForeground() } + if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.drawForeground() } + if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.drawForeground() } + } + + drawLabel() + thisObject.edgeEditor.drawForeground() + } + } + + function drawLabel () { + /* Draw Title Above the Container */ + let position = { + x: 0, + y: 0 + } + let imageSize = 25 + let fontSize = 25 + let opacity = 1 + let icon = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type) + + position = transformThisPoint(position, thisObject.container) + printLabel(thisObject.payload.node.name, position.x + 30, position.y - 10, opacity, fontSize) + + if (icon !== undefined) { + if (icon.canDrawIcon === true) { + browserCanvasContext.drawImage( + icon, position.x - 0, + position.y - 30, + imageSize, + imageSize) + } + } + } + + function recalculateCoordinateSystem () { + let minValue = { + x: MIN_PLOTABLE_DATE.valueOf(), + y: 0 + } + + let maxValue = { + x: MAX_PLOTABLE_DATE.valueOf(), + y: nextPorwerOf10(MAX_DEFAULT_RATE_SCALE_VALUE) / 4 + } + + timeMachineCoordinateSystem.initialize( + minValue, + maxValue, + thisObject.container.frame.width, + thisObject.container.frame.height + ) + } +} diff --git a/CanvasApp/ChartingSpace/TimelineChart.js b/CanvasApp/ChartingSpace/TimelineChart.js new file mode 100644 index 000000000..ce39cf53d --- /dev/null +++ b/CanvasApp/ChartingSpace/TimelineChart.js @@ -0,0 +1,407 @@ +function newTimelineChart () { + const MODULE_NAME = 'Timeline Chart' + const INFO_LOG = false + const INTENSIVE_LOG = false + const ERROR_LOG = true + let logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let timeFrame = INITIAL_TIME_PERIOD + let datetime = NEW_SESSION_INITIAL_DATE + + let thisObject = { + container: undefined, + fitFunction: undefined, + rateScale: undefined, + timeFrameScale: undefined, + payload: undefined, + layersManager: undefined, + plotterManager: undefined, + upstreamTimeFrame: undefined, + setDatetime: setDatetime, + setTimeFrame: setTimeFrame, + physics: physics, + draw: draw, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + let timeMachineCoordinateSystem + let timelineChartCoordinateSystem = newCoordinateSystem() + let coordinateSystem + + let layersPanel + let layersPanelHandle + + let onMouseOverEventSuscriptionId + let onMouseNotOverEventSuscriptionId + let rateScaleUpstreamEventSuscriptionId + let rateScaleMouseOverEventSuscriptionId + let timeFrameScaleEventSuscriptionId + let timeFrameScaleMouseOverEventSuscriptionId + let scaleChangedEventSubscriptionId + + let drawScales = false + let mouse = { + position: { + x: 0, + y: 0 + } + } + + setupContainer() + return thisObject + + function setupContainer () { + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.detectMouseOver = true + } + + function finalize () { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + + if (thisObject.layersManager !== undefined) { + finalizeLayersManager() + } + + if (thisObject.rateScale !== undefined) { + finalizeRateScale() + } + + if (thisObject.timeFrameScale !== undefined) { + finalizeTimeFrameScale() + } + + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + thisObject.container.eventHandler.stopListening(onMouseNotOverEventSuscriptionId) + thisObject.container.finalize() + thisObject.container = undefined + thisObject.payload = undefined + + mouse = undefined + logger = undefined + + timeMachineCoordinateSystem = undefined + timelineChartCoordinateSystem.finalize() + timelineChartCoordinateSystem = undefined + coordinateSystem = undefined + layersPanel = undefined + layersPanelHandle = undefined + } + + function finalizeLayersManager () { + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.finalize() + } + thisObject.plotterManager = undefined + thisObject.layersManager = undefined + + canvas.panelsSpace.destroyPanel(layersPanelHandle) + } + + function finalizeTimeFrameScale () { + if (thisObject.timeFrameScale === undefined) { return } + + thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleEventSuscriptionId) + thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleMouseOverEventSuscriptionId) + thisObject.timeFrameScale.finalize() + thisObject.timeFrameScale = undefined + if (thisObject.upstreamTimeFrame !== undefined) { + timeFrame = thisObject.upstreamTimeFrame + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.setTimeFrame(timeFrame) + } + } + } + + function finalizeRateScale () { + if (thisObject.rateScale === undefined) { return } + + thisObject.rateScale.container.eventHandler.stopListening(rateScaleMouseOverEventSuscriptionId) + thisObject.container.parentContainer.eventHandler.stopListening(rateScaleUpstreamEventSuscriptionId) + thisObject.rateScale.finalize() + thisObject.rateScale = undefined + + /* Resets the local container with the dimessions of its parent, the Time Machine */ + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + thisObject.container.frame.height = thisObject.container.parentContainer.frame.height + thisObject.container.frame.width = thisObject.container.parentContainer.frame.width + } + + function initialize (pTimeMachineCoordinateSystem, pTimeFrame) { + /* We load the logow we will need for the background. */ + + timeMachineCoordinateSystem = pTimeMachineCoordinateSystem + coordinateSystem = timeMachineCoordinateSystem + initializeCoordinateSystem() + + timeFrame = pTimeFrame + + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + onMouseNotOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + scaleChangedEventSubscriptionId = timeMachineCoordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function initializeCoordinateSystem () { + let minValue = { + x: timeMachineCoordinateSystem.min.x, + y: 0 + } + + let maxValue = { + x: timeMachineCoordinateSystem.max.x, + y: nextPorwerOf10(MAX_DEFAULT_RATE_SCALE_VALUE) / 4 + } + + if (thisObject.rateScale !== undefined) { + minValue.y = thisObject.rateScale.minValue + maxValue.y = thisObject.rateScale.maxValue + } + + timelineChartCoordinateSystem.initialize( + minValue, + maxValue, + thisObject.container.frame.width, + thisObject.container.frame.height + ) + } + + function initializeLayersManager () { + let owner = thisObject.payload.node.payload.parentNode.id // The real owner is the Time Machine + layersPanelHandle = canvas.panelsSpace.createNewPanel('Layers Panel', undefined, owner) + thisObject.layersManager = canvas.panelsSpace.getPanel(layersPanelHandle) + thisObject.layersManager.payload = thisObject.payload.node.layersManager.payload + thisObject.layersManager.initialize() + + /* Initialize the Plotter Manager */ + thisObject.plotterManager = newPlottersManager() + + thisObject.plotterManager.container.connectToParent(thisObject.container, true, true, false, true, true, true, false, false, true) + + thisObject.plotterManager.container.frame.position.x = 0 + thisObject.plotterManager.container.frame.position.y = 0 + + thisObject.plotterManager.fitFunction = thisObject.fitFunction + thisObject.plotterManager.payload = thisObject.payload + thisObject.plotterManager.initialize(thisObject.layersManager) + thisObject.plotterManager.setTimeFrame(timeFrame) + thisObject.plotterManager.setDatetime(datetime) + thisObject.plotterManager.setCoordinateSystem(coordinateSystem) + } + + function initializeRateScale () { + thisObject.rateScale = newRateScale() + thisObject.rateScale.fitFunction = thisObject.fitFunction + thisObject.rateScale.payload = thisObject.payload.node.rateScale.payload + + rateScaleMouseOverEventSuscriptionId = thisObject.rateScale.container.eventHandler.listenToEvent('onMouseOverScale', rateScaleMouseOver) + thisObject.rateScale.initialize(timelineChartCoordinateSystem, thisObject.container.parentContainer, thisObject.container) + + function rateScaleMouseOver (event) { + thisObject.container.eventHandler.raiseEvent('onChildrenMouseOver', event) + } + } + + function initializeTimeFrameScale () { + thisObject.timeFrameScale = newTimeFrameScale() + thisObject.timeFrameScale.fitFunction = thisObject.fitFunction + thisObject.timeFrameScale.payload = thisObject.payload.node.timeFrameScale.payload + + timeFrameScaleEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('Time Frame Value Changed', timeFrameScaleValueChanged) + timeFrameScaleMouseOverEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('onMouseOverScale', timeFrameScaleMouseOver) + thisObject.timeFrameScale.initialize(thisObject.container.parentContainer) + + function timeFrameScaleValueChanged (event) { + let currentTimeFrame = timeFrame + timeFrame = event.timeFrame + if (timeFrame !== currentTimeFrame) { + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.setTimeFrame(timeFrame) + } + } + } + + function timeFrameScaleMouseOver (event) { + thisObject.container.eventHandler.raiseEvent('onChildrenMouseOver', event) + } + } + + function onScaleChanged (event) { + if (thisObject.rateScale !== undefined) { + thisObject.rateScale.onUpstreamScaleChanged(event) + } + timelineChartCoordinateSystem.min.x = timeMachineCoordinateSystem.min.x + timelineChartCoordinateSystem.max.x = timeMachineCoordinateSystem.max.x + timelineChartCoordinateSystem.maxHeight = timeMachineCoordinateSystem.maxHeight + timelineChartCoordinateSystem.maxWidth = timeMachineCoordinateSystem.maxWidth + timelineChartCoordinateSystem.autoMinXScale = timeMachineCoordinateSystem.autoMinXScale + timelineChartCoordinateSystem.autoMaxXScale = timeMachineCoordinateSystem.autoMaxXScale + timelineChartCoordinateSystem.recalculateScale() + } + + function setTimeFrame (pTimeFrame) { + timeFrame = pTimeFrame + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.setTimeFrame(timeFrame) + } + } + + function setDatetime (pDatetime) { + datetime = pDatetime + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.setDatetime(datetime) + } + } + + function onMouseOver (event) { + /* This event gets to the timelinechart container because it inherits it from the time machine container, which is the one raising Mouse Over and Mouse not Over Events to its children. */ + drawScales = true + + if (thisObject.rateScale !== undefined) { + thisObject.rateScale.onMouseOverSomeTimeMachineContainer(event) + } + + if (thisObject.timeFrameScale !== undefined) { + thisObject.timeFrameScale.onMouseOverSomeTimeMachineContainer(event) + } + + mouse.position.x = event.x + mouse.position.y = event.y + } + + function onMouseNotOver (event) { + /* This event is inherited from the Time Machine */ + drawScales = false + + if (thisObject.rateScale !== undefined) { + thisObject.rateScale.visible = false + } + if (thisObject.timeFrameScale !== undefined) { + thisObject.timeFrameScale.visible = false + } + } + + function getContainer (point, purpose) { + let container + + if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { + container = thisObject.rateScale.getContainer(point, purpose) + if (container !== undefined) { + if (container.isForThisPurpose(purpose)) { + return container + } + } + } + + if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { + container = thisObject.timeFrameScale.getContainer(point, purpose) + if (container !== undefined) { + if (container.isForThisPurpose(purpose)) { + return container + } + } + } + + return container + } + + function physics () { + thisObjectPhysics() + childrenPhysics() + syncWithDesignerScales() + syncWithDesignerLayersManager() + } + + function thisObjectPhysics () { + timelineChartCoordinateSystem.physics() + } + + function syncWithDesignerLayersManager () { + if (thisObject.payload.node === undefined) { + finalizeLayersManager() + return + } + + if (thisObject.payload.node.layersManager === undefined && thisObject.layersManager !== undefined) { + finalizeLayersManager() + } + if (thisObject.payload.node.layersManager !== undefined && thisObject.layersManager === undefined) { + initializeLayersManager() + } + } + + function syncWithDesignerScales () { + if (thisObject.payload.node === undefined) { + finalizeRateScale() + finalizeTimeFrameScale() + return + } + if (thisObject.payload.node.rateScale === undefined && thisObject.rateScale !== undefined) { + finalizeRateScale() + coordinateSystem = timeMachineCoordinateSystem + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.setCoordinateSystem(coordinateSystem) + } + } + if (thisObject.payload.node.rateScale !== undefined && thisObject.rateScale === undefined) { + initializeRateScale() + coordinateSystem = timelineChartCoordinateSystem + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.setCoordinateSystem(coordinateSystem) + } + } + if (thisObject.payload.node.timeFrameScale === undefined && thisObject.timeFrameScale !== undefined) { + finalizeTimeFrameScale() + } + if (thisObject.payload.node.timeFrameScale !== undefined && thisObject.timeFrameScale === undefined) { + initializeTimeFrameScale() + } + } + + function childrenPhysics () { + if (thisObject.rateScale !== undefined) { + thisObject.rateScale.layersOn = thisObject.plotterManager.connectors.length + thisObject.rateScale.physics() + } + if (thisObject.timeFrameScale !== undefined) { + thisObject.timeFrameScale.layersOn = thisObject.plotterManager.connectors.length + thisObject.timeFrameScale.physics() + } + } + + function drawBackground () { + if (thisObject.container.frame.isInViewPort()) { + drawChartsBackground() + if (thisObject.plotterManager !== undefined) { + thisObject.plotterManager.draw() + } + } + } + + function draw () { + if (thisObject.container.frame.isInViewPort()) { + if (drawScales === true) { + if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.draw() } + if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.draw() } + } + } + } + + function drawForeground () { + if (thisObject.container.frame.isInViewPort()) { + if (drawScales === true) { + if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.drawForeground() } + if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.drawForeground() } + } + } + } + + function drawChartsBackground () { + drawContainerBackground(thisObject.container, UI_COLOR.WHITE, 0, thisObject.fitFunction) + } +} diff --git a/CanvasApp/ChartingSpace/Viewport.js b/CanvasApp/ChartingSpace/Viewport.js new file mode 100644 index 000000000..2a054191c --- /dev/null +++ b/CanvasApp/ChartingSpace/Viewport.js @@ -0,0 +1,400 @@ + +function newViewport () { + const MODULE_NAME = 'Viewport' + + const MIN_ZOOM_LEVEL = 0 + const MAX_ZOOM_LEVEL = 500 + + let ANIMATION_INCREMENT = 0.25 + let ANIMATION_STEPS = 5 + + let TOP_MARGIN = 40 + TOP_SPACE_HEIGHT + let BOTTOM_MARGIN = 42 + COCKPIT_SPACE_HEIGHT + let LEFT_MARGIN = 0 + let RIGHT_MARGIN = 0 + let MARGINS = { + TOP: TOP_MARGIN, + BOTTOM: BOTTOM_MARGIN, + LEFT: LEFT_MARGIN, + RIGHT: RIGHT_MARGIN + } + + let thisObject = { + visibleArea: undefined, + width: undefined, + height: undefined, + eventHandler: undefined, + zoomTargetLevel: undefined, + zoomLevel: undefined, + mousePosition: undefined, + margins: MARGINS, + payload: undefined, + changeZoom: changeZoom, + onMouseWheel: onMouseWheel, + transformThisPoint: transformThisPoint, + unTransformThisPoint: unTransformThisPoint, + isThisPointVisible: isThisPointVisible, + isThisPointInViewport: isThisPointInViewport, + fitIntoVisibleArea: fitIntoVisibleArea, + fitIntoViewport: fitIntoViewport, + displace: displace, + physics: physics, + raiseEvents: raiseEvents, + resize: resize, + initialize: initialize, + finalize: finalize + } + let increment = 0.035 + let position = { + x: 0, + y: 0 + } + let targetPosition = { + x: 0, + y: 0 + } + let positionIncrement = { + x: 0, + y: 0 + } + thisObject.mousePosition = { + x: 0, + y: 0 + } + thisObject.eventHandler = newEventHandler() + +/* Initial default value */ + thisObject.zoomLevel = MIN_ZOOM_LEVEL + thisObject.zoomTargetLevel = MIN_ZOOM_LEVEL + INITIAL_TIME_PERIOD = recalculatePeriod(thisObject.zoomLevel) + + return thisObject + + function finalize () { + thisObject.eventHandler.finalize() + thisObject.payload = undefined + thisObject = undefined + } + + function initialize () { + if (thisObject.payload !== undefined) { + /* Read the position from the frame structure */ + + let frame = { + position: { + x: 0, + y: 0 + } + } + loadFrame(thisObject.payload, frame) + if (!isNaN(frame.position.x)) { + position.x = frame.position.x + } + if (!isNaN(frame.position.y)) { + position.y = frame.position.y + } + } + + resize() + readObjectState() + } + + function resize () { + TOP_MARGIN = 40 + TOP_SPACE_HEIGHT + BOTTOM_MARGIN = 42 + browserCanvas.height - COCKPIT_SPACE_POSITION + LEFT_MARGIN = 0 + RIGHT_MARGIN = 0 + MARGINS = { + TOP: TOP_MARGIN, + BOTTOM: BOTTOM_MARGIN, + LEFT: LEFT_MARGIN, + RIGHT: RIGHT_MARGIN + } + + thisObject.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} + } + + thisObject.width = thisObject.visibleArea.topRight.x - thisObject.visibleArea.topLeft.x + thisObject.height = thisObject.visibleArea.bottomRight.y - thisObject.visibleArea.topLeft.y + + thisObject.center = { + x: LEFT_MARGIN + thisObject.width / 2, + y: TOP_MARGIN + thisObject.height / 2 + } + } + + function raiseEvents () { + let event = { + newPosition: position + } + + thisObject.eventHandler.raiseEvent('Position Changed', event) + } + + function physics () { + animationPhysics() + positioningPhysics() + readObjectState() + } + + function positioningPhysics () { + if (thisObject.payload === undefined) { return } + /* Save the position at the frame lavel */ + let frame = {} + frame.position = position + saveFrame(thisObject.payload, frame) + } + + function animationPhysics () { + if (thisObject.zoomLevel < thisObject.zoomTargetLevel) { + if (thisObject.zoomTargetLevel - thisObject.zoomLevel < ANIMATION_INCREMENT) { + ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) + } + thisObject.zoomLevel = thisObject.zoomLevel + ANIMATION_INCREMENT + changeZoom(thisObject.zoomLevel - ANIMATION_INCREMENT, thisObject.zoomLevel) + } + + if (thisObject.zoomLevel > thisObject.zoomTargetLevel) { + if (thisObject.zoomLevel - thisObject.zoomTargetLevel < ANIMATION_INCREMENT) { + ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) + } + thisObject.zoomLevel = thisObject.zoomLevel - ANIMATION_INCREMENT + changeZoom(thisObject.zoomLevel + ANIMATION_INCREMENT, thisObject.zoomLevel) + } + + if (positionIncrement.y !== 0) { + if (Math.trunc(Math.abs(targetPosition.y - position.y) * 1000) >= Math.trunc(Math.abs(positionIncrement.y) * 1000)) { + position.y = position.y + positionIncrement.y + } else { + positionIncrement.y = 0 + } + } + } + + function onMouseWheel (event) { + if ((event.ctrlKey === true || event.metaKey === true)) { return } + let morePower = 1 + let amount = event.delta + if (event.buttons === 4) { morePower = 2 } // Mouse wheel pressed. + /* We adjust the sensitivity for Mac Users */ + let isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0 + if (isMac) { amount = amount / 5 } + + if (thisObject.zoomTargetLevel > 10) { + amount = amount * 2 + } + if (thisObject.zoomTargetLevel > 25) { + amount = amount * 3 + } + if (thisObject.zoomTargetLevel > 50) { + amount = amount * 3.5 + } + + if (thisObject.zoomTargetLevel + amount * morePower > MAX_ZOOM_LEVEL) { + return false + } + if (thisObject.zoomTargetLevel + amount * morePower < MIN_ZOOM_LEVEL) { + return false + } + thisObject.zoomTargetLevel = thisObject.zoomTargetLevel + amount * morePower + + ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) / ANIMATION_STEPS + + let newEvent = { + newLevel: thisObject.zoomTargetLevel, + newPosition: position, + type: undefined, + shiftKey: event.shiftKey + } + + if (amount > 0) { + newEvent.type = 'Zoom In' + } else { + newEvent.type = 'Zoom Out' + } + + thisObject.eventHandler.raiseEvent('Zoom Changed', newEvent) + return true + } + + function fitIntoVisibleArea (point) { + let pointCopy = { + x: point.x, + y: point.y + } + + /* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */ + if (pointCopy.x > thisObject.visibleArea.bottomRight.x + 1) { + pointCopy.x = thisObject.visibleArea.bottomRight.x + 1 + } + + if (pointCopy.x < thisObject.visibleArea.topLeft.x - 1) { + pointCopy.x = thisObject.visibleArea.topLeft.x - 1 + } + + if (pointCopy.y > thisObject.visibleArea.bottomRight.y + 1) { + pointCopy.y = thisObject.visibleArea.bottomRight.y + 1 + } + + if (pointCopy.y < thisObject.visibleArea.topLeft.y - 1) { + pointCopy.y = thisObject.visibleArea.topLeft.y - 1 + } + + return pointCopy + } + + function fitIntoViewport (point) { + let pointCopy = { + x: point.x, + y: point.y + } + if (pointCopy.y > COCKPIT_SPACE_POSITION) { + pointCopy.y = COCKPIT_SPACE_POSITION + } + return pointCopy + } + + function displace (displaceVector, recalculate) { + position.x = position.x + displaceVector.x + position.y = position.y + displaceVector.y + + saveObjectState() + + let event = { + newPosition: position, + recalculate: recalculate + } + + thisObject.eventHandler.raiseEvent('Position Changed', event) + } + + function changeZoom (oldLevel, newLevel) { + let oldMouse = unTransformThisPoint(thisObject.mousePosition, oldLevel) + let newMouse = transformThisPoint(oldMouse, newLevel) + + position.x = position.x - newMouse.x + thisObject.mousePosition.x + position.y = position.y - newMouse.y + thisObject.mousePosition.y + + let testPoint = unTransformThisPoint(thisObject.mousePosition, newLevel) + + saveObjectState() + + targetPosition.x = position.x + targetPosition.y = position.y + + positionIncrement = { + x: 0, + y: 0 + } + + thisObject.eventHandler.raiseEvent('Zoom Changed') + } + + function transformThisPoint (point, level) { + let transformedPoint = { + x: 0, + y: 0 + } + + if (level === undefined) { + transformedPoint.x = point.x * (1 + thisObject.zoomLevel) + position.x + transformedPoint.y = point.y * (1 + thisObject.zoomLevel) + position.y + } else { + transformedPoint.x = point.x * (1 + level) + position.x + transformedPoint.y = point.y * (1 + level) + position.y + } + + return transformedPoint + } + + function unTransformThisPoint (point, level) { + let pointWithoutZoom = { + x: 0, + y: 0 + } + if (level === undefined) { + pointWithoutZoom.x = (point.x - position.x) / (1 + thisObject.zoomLevel) + pointWithoutZoom.y = (point.y - position.y) / (1 + thisObject.zoomLevel) + } else { + pointWithoutZoom.x = (point.x - position.x) / (1 + level) + pointWithoutZoom.y = (point.y - position.y) / (1 + level) + } + + return pointWithoutZoom + } + + function isThisPointVisible (point) { + if (point.x < thisObject.visibleArea.topLeft.x || point.x > thisObject.visibleArea.bottomRight.x || point.y < thisObject.visibleArea.topLeft.y || point.y > thisObject.visibleArea.bottomRight.y) { + return false + } else { + return true + } + } + + function isThisPointInViewport (point) { + if (point.y > COCKPIT_SPACE_POSITION) { + return false + } else { + return true + } + } + + function saveObjectState () { + if (thisObject.payload === undefined) { return } + + /* Save the zoom at the node config, so that the user can change it if he wishes to. */ + try { + let code = JSON.parse(thisObject.payload.node.code) + code.zoom = (thisObject.zoomTargetLevel - MIN_ZOOM_LEVEL) / (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL) * 100 + code.zoom = code.zoom.toFixed(2) + thisObject.payload.node.code = JSON.stringify(code) + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + } + + function readObjectState () { + if (thisObject.payload === undefined) { return } + + /* Read the zoom level from the node config */ + try { + let code = JSON.parse(thisObject.payload.node.code) + + if (isNaN(code.zoom) || code.zoom === null || code.zoom === undefined) { + saveObjectState() + return + } + code.zoom = code.zoom / 100 * (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL) + MIN_ZOOM_LEVEL + if (code.zoom < MIN_ZOOM_LEVEL) { code.zoom = MIN_ZOOM_LEVEL } + if (code.zoom > MAX_ZOOM_LEVEL) { code.zoom = MAX_ZOOM_LEVEL } + + if (code.zoom.toFixed(2) !== thisObject.zoomTargetLevel.toFixed(2)) { + newZoomLevel(code.zoom) + } else { + saveObjectState() + } + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + + function newZoomLevel (level) { + thisObject.zoomLevel = level + thisObject.zoomTargetLevel = level + INITIAL_TIME_PERIOD = recalculatePeriod(level) + saveObjectState() + ANIMATION_INCREMENT = 0 + + let event = { + newLevel: thisObject.zoomTargetLevel, + newPosition: position, + type: undefined + } + thisObject.eventHandler.raiseEvent('Zoom Changed', event) + return true + } + } +} diff --git a/CanvasApp/CockpitSpace/AssetBalances.js b/CanvasApp/CockpitSpace/AssetBalances.js new file mode 100644 index 000000000..6cb267ef4 --- /dev/null +++ b/CanvasApp/CockpitSpace/AssetBalances.js @@ -0,0 +1,120 @@ + +function newQuotedAssetalances () { + const MODULE_NAME = 'Asset Balances' + + let thisObject = { + visible: true, + container: undefined, + paramsArray: undefined, + setParamsArray: setParamsArray, + physics: physics, + draw: draw, + speedometers: [], + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isWheelable = false + + return thisObject + + function finalize () { + for (let i = 0; i < thisObject.speedometers.length; i++) { + let speedometer = thisObject.speedometers[i] + speedometer.finalize() + } + thisObject.container.finalize() + thisObject.container = undefined + thisObject.paramsArray = undefined + } + + function initialize () { + for (let i = 0; i < 2; i++) { + let speedometer = newSpeedometer() + + speedometer.initialize() + thisObject.speedometers.push(speedometer) + } + } + + function getContainer (point, purpose) { + if (thisObject.visible !== true) { return } + + let container + for (let i = 0; i < thisObject.speedometers.length; i++) { + container = thisObject.speedometers[i].getContainer(point, purpose) + if (container !== undefined) { + if (container.isForThisPurpose(purpose)) { + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return container + } + } + } + } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function setParamsArray (paramsArray) { + thisObject.paramsArray = paramsArray + for (let i = 0; i < thisObject.speedometers.length; i++) { + let speedometer = thisObject.speedometers[i] + speedometer.params = paramsArray[i] + } + } + + function physics () { + + } + + function draw () { + if (thisObject.visible !== true) { return } + + for (let i = 0; i < thisObject.speedometers.length; i++) { + let speedometer = thisObject.speedometers[i] + speedometer.draw() + } + + drawQuotedAssetalances() + } + + function drawQuotedAssetalances () { + if (thisObject.paramsArray === undefined) { return } + + let fontSize + let label + let xOffset + let yOffset + + const OPACITY = 1 + + /* We put the params.VALUE in the middle */ + + fontSize = 15 + + browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY + + label = 'ASSET BALANCES' + + xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + yOffset = -45 + 9 + + labelPoint = { + x: 0 - xOffset + (thisObject.paramsArray[1].LEFT_OFFSET - thisObject.paramsArray[0].LEFT_OFFSET) / 2 + thisObject.paramsArray[0].LEFT_OFFSET - 20, + y: COCKPIT_SPACE_POSITION - COCKPIT_SPACE_HEIGHT / 2 - yOffset + } + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } +} + diff --git a/CanvasApp/CockpitSpace/FullScreen.js b/CanvasApp/CockpitSpace/FullScreen.js new file mode 100644 index 000000000..12951403f --- /dev/null +++ b/CanvasApp/CockpitSpace/FullScreen.js @@ -0,0 +1,169 @@ + +function newFullScreen () { + const MODULE_NAME = 'Fullscreen' + + let thisObject = { + visible: true, + container: undefined, + status: undefined, + physics: physics, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isWheelable = false + thisObject.container.isClickeable = true + thisObject.container.detectMouseOver = true + + let selfMouseOverEventSubscriptionId + let selfMouseClickEventSubscriptionId + let selfMouseNotOverEventSubscriptionId + + let isMouseOver = false + let counterTillNextState = 0 + + let layersToTurnOn = [] + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(selfMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseNotOverEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + } + + function initialize () { + thisObject.container.frame.width = 50 + thisObject.container.frame.height = COCKPIT_SPACE_HEIGHT - 12 + + selfMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + selfMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + + thisObject.status = 'Ready' + } + + function getContainer (point, purpose) { + if (thisObject.visible !== true) { return } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function onMouseOver (point) { + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + isMouseOver = true + } else { + isMouseOver = false + } + } + + function onMouseNotOver (point) { + isMouseOver = false + } + + function onMouseClick (event) { + if (thisObject.status !== 'On') { + thisObject.status = 'On' + goFullscreen() + CURRENT_TOP_MARGIN = 0 + AT_FULL_SCREEN_MODE = true + } else { + thisObject.status = 'Off' + goOutFullscreen() + CURRENT_TOP_MARGIN = window.canvasApp.topMargin + AT_FULL_SCREEN_MODE = false + } + } + + function goFullscreen () { + if (browserCanvas.requestFullscreen) { + browserCanvas.requestFullscreen() + } else if (browserCanvas.mozRequestFullScreen) { + browserCanvas.mozRequestFullScreen() + } else if (browserCanvas.webkitRequestFullscreen) { + browserCanvas.webkitRequestFullscreen() + } else if (browserCanvas.msRequestFullscreen) { browserCanvas.msRequestFullscreen() } + } + + function goOutFullscreen () { + if (document.exitFullscreen) { + document.exitFullscreen() + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen() + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen() + } else if (document.msExitFullscreen) { document.msExitFullscreen() } + } + + function physics () { + positionPhysics() + } + + function positionPhysics () { + thisObject.container.frame.position.x = thisObject.container.parentContainer.frame.width - thisObject.container.frame.width + thisObject.container.frame.position.y = 6 + } + + function draw () { + if (thisObject.visible !== true) { return } + drawBackground() + drawText() + } + + function drawBackground () { + let params = { + cornerRadius: 3, + lineWidth: 0.01, + container: thisObject.container, + borderColor: UI_COLOR.DARK, + castShadow: false, + opacity: 1 + } + + if (isMouseOver === true) { + params.backgroundColor = UI_COLOR.TURQUOISE + } else { + params.backgroundColor = UI_COLOR.DARK_TURQUOISE + } + + roundedCornersBackground(params) + } + + function drawText () { + let fontSize + let label + let xOffset + let yOffset + + const OPACITY = 1 + + /* We put the params.VALUE in the middle */ + + fontSize = 15 + + browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY + label = 'FS' + + let labelPoint = { + x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO, + y: thisObject.container.frame.height - 9 + } + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } +} + diff --git a/CanvasApp/CockpitSpace/Speedometer.js b/CanvasApp/CockpitSpace/Speedometer.js new file mode 100644 index 000000000..7a2f0a857 --- /dev/null +++ b/CanvasApp/CockpitSpace/Speedometer.js @@ -0,0 +1,259 @@ + +function newSpeedometer () { + const MODULE_NAME = 'Speedometer' + + let thisObject = { + visible: true, + container: undefined, + params: undefined, + physics: physics, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isWheelable = false + + return thisObject + + function finalize () { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.params = undefined + } + + function initialize () { + + } + + function getContainer (point, purpose) { + if (thisObject.visible !== true) { return } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function physics () { + + } + + function draw () { + if (thisObject.params === undefined) { return } + drawAssetCircle() + } + + function drawAssetCircle () { + const RED_LINE_HIGHT = 2 + const RADIUS = 50 + const OPACITY = 1 + + let centerPoint = { + x: thisObject.params.LEFT_OFFSET, + y: COCKPIT_SPACE_POSITION - COCKPIT_SPACE_HEIGHT / 2 - 9 + } + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, RADIUS + RED_LINE_HIGHT, 0.85 * Math.PI, 2.15 * Math.PI) + browserCanvasContext.closePath() + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')' + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, RADIUS, 0.0 * Math.PI, 2.0 * Math.PI) + browserCanvasContext.closePath() + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')' + browserCanvasContext.fill() + + /* We draw the circle bar */ + + const DEFAULT_THICKNESS = 2 + const VALUE_THICKNESS = 8 + const VALUE_BG_THICKNESS = 1 + const BAR_RADIUS = RADIUS * 0.85 + + let BAR_START_ANGLE = 0.85 * Math.PI + let BAR_END_ANGLE = 2.15 * Math.PI + let CURRENT_VALUE_ANGLE = thisObject.params.VALUE * (BAR_END_ANGLE - BAR_START_ANGLE) / thisObject.params.MAX_VALUE + BAR_START_ANGLE + let MIN_VALUE_ANGLE = thisObject.params.MIN_VALUE * (BAR_END_ANGLE - BAR_START_ANGLE) / thisObject.params.MAX_VALUE + BAR_START_ANGLE + let INIT_VALUE_ANGLE = thisObject.params.INIT_VALUE * (BAR_END_ANGLE - BAR_START_ANGLE) / thisObject.params.MAX_VALUE + BAR_START_ANGLE + let PROFIT_VALUE_ANGLE = CURRENT_VALUE_ANGLE + + if (CURRENT_VALUE_ANGLE > INIT_VALUE_ANGLE) { CURRENT_VALUE_ANGLE = INIT_VALUE_ANGLE } + if (CURRENT_VALUE_ANGLE < MIN_VALUE_ANGLE) { CURRENT_VALUE_ANGLE = MIN_VALUE_ANGLE } + if (PROFIT_VALUE_ANGLE > BAR_END_ANGLE) { PROFIT_VALUE_ANGLE = BAR_END_ANGLE } + + if ( + thisObject.params.VALUE > 0 && + thisObject.params.ASSET_NAME === thisObject.params.BASE_ASSET && + thisObject.params.POSITION_TAKEN === false + ) { + browserCanvasContext.setLineDash([0, 0]) + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, BAR_START_ANGLE, MIN_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_BG_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, MIN_VALUE_ANGLE, CURRENT_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_BG_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, CURRENT_VALUE_ANGLE, INIT_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_BG_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + + if (PROFIT_VALUE_ANGLE > INIT_VALUE_ANGLE) { + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, INIT_VALUE_ANGLE, PROFIT_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_BG_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + } + + browserCanvasContext.setLineDash([2, 3]) + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, BAR_START_ANGLE, MIN_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, MIN_VALUE_ANGLE, CURRENT_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, CURRENT_VALUE_ANGLE, INIT_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + + if (PROFIT_VALUE_ANGLE > INIT_VALUE_ANGLE) { + browserCanvasContext.beginPath() + browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, INIT_VALUE_ANGLE, PROFIT_VALUE_ANGLE) + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = VALUE_THICKNESS + browserCanvasContext.stroke() + browserCanvasContext.closePath() + } + } + + /* Common Variables */ + + let fontSize + let label + let xOffset + let yOffset + + /* We put the thisObject.params.VALUE in the middle */ + + fontSize = 22 + if (thisObject.params.VALUE > 999) { + fontSize = 18 + } + if (thisObject.params.VALUE > 9999) { + fontSize = 14 + } + if (thisObject.params.VALUE > 99999) { + fontSize = 10 + } + browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY + + label = thisObject.params.VALUE + + if (isNaN(label) === false) { + label = Number(label) + + label = Math.trunc(label * Math.pow(10, thisObject.params.DECIMALS)) / Math.pow(10, thisObject.params.DECIMALS) + if (label === 0) { label = label.toFixed(2) } else { label = label.toLocaleString() } + } + + // label = label.substring(0, 6) + + xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + 7 + + labelPoint = { + x: centerPoint.x - xOffset, + y: centerPoint.y + } + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + + /* We put the top label */ + + fontSize = 12 + + browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY + + label = thisObject.params.ASSET_LABEL + + xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + 3 + yOffset = 22 + + labelPoint = { + x: centerPoint.x - xOffset, + y: centerPoint.y - yOffset + } + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + + /* We put the bottom label */ + + fontSize = 12 + + browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY + + label = thisObject.params.ASSET_NAME + ' ' + + xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + 3 + yOffset = -15 + + labelPoint = { + x: centerPoint.x - xOffset, + y: centerPoint.y - yOffset + } + + if (thisObject.params.ASSET_NAME === thisObject.params.BASE_ASSET) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + } else { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + } + + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } +} + diff --git a/CanvasApp/Container.js b/CanvasApp/Container.js new file mode 100644 index 000000000..a4a0b5901 --- /dev/null +++ b/CanvasApp/Container.js @@ -0,0 +1,262 @@ + +function newContainer () { + let thisObject = { + id: Math.trunc(Math.random() * 10000000), + type: 'Rectangle', + frame: undefined, + eventHandler: undefined, + parentContainer: undefined, + isDraggeable: true, + notDraggingOnX: false, + notDraggingOnY: false, + isClickeable: false, + isWheelable: false, + detectMouseOver: false, + name: undefined, + fitFunction: undefined, + isVisibleFunction: undefined, + displace: displace, + initialize: initialize, + finalize: finalize, + connectToParent: connectToParent, + isForThisPurpose: isForThisPurpose + } + + let connectedToParentWidth = false + let connectedToParentHeight = false + let connectedToParentRadius = false + + let onDimmensionsChangedParentEvent = false + let onMouseOverParentEvent = false + let onMouseNotOverParentEvent = false + let onFocusParentEvent = false + let onNotFocusParentEvent = false + let onDisplaceParentEvent = false + let onDragStartedParentEvent = false + let onDragFinishedParentEvent = false + + let isConnectedToParent = false + + let dimensionsChangedEventSubscriptionId + let onMouseOverEventSubscriptionId + let onMouseNotOverEventSubscriptionId + let onFocusEventSubscriptionId + let onNotFocusEventSubscriptionId + let onDisplaceEventSubscriptionId + let onDragStartedEventSubscriptionId + let onDragFinishedEventSubscriptionId + + return thisObject + + function finalize () { + if (isConnectedToParent === true) { + thisObject.parentContainer.eventHandler.stopListening(dimensionsChangedEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onMouseOverEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onMouseNotOverEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onFocusEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onNotFocusEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onDisplaceEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onDragStartedEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onDragFinishedEventSubscriptionId) + + thisObject.parentContainer = undefined + thisObject.eventHandler.finalize() + + thisObject.frame.finalize() + } + } + + function initialize (pName, pType) { + thisObject.name = pName + thisObject.type = pType + + thisObject.frame = newFrame() + thisObject.frame.initialize(pType) + if (pName !== undefined) { + thisObject.frame.containerName = pName + } + thisObject.frame.container = thisObject + + thisObject.eventHandler = newEventHandler() + thisObject.eventHandler.name = pName + } + + function connectToParent ( + parentContainer, + onWidth, + onHeight, + onRadius, + onDimmensionsChangedEvent, + onMouseOverEvent, + onMouseNotOverEvent, + onFocusEvent, + onNotFocusEvent, + onDisplaceEvent, + onDragStartedEvent, + onDragFinishedEvent + ) { + connectedToParentWidth = onWidth + connectedToParentHeight = onHeight + connectedToParentRadius = onRadius + + onDimmensionsChangedParentEvent = onDimmensionsChangedEvent + onMouseOverParentEvent = onMouseOverEvent + onMouseNotOverParentEvent = onMouseNotOverEvent + onFocusParentEvent = onFocusEvent + onNotFocusParentEvent = onNotFocusEvent + onDisplaceParentEvent = onDisplaceEvent + onDragStartedParentEvent = onDragStartedEvent + onDragFinishedParentEvent = onDragFinishedEvent + + isConnectedToParent = true + + thisObject.frame.parentFrame = parentContainer.frame + thisObject.parentContainer = parentContainer + + if (onDimmensionsChangedParentEvent === true) { + dimensionsChangedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('Dimmensions Changed', onParentDimmensionsChanged) + } + if (onMouseOverParentEvent === true) { + onMouseOverEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onMouseOver', onMouseOver) + } + if (onMouseNotOverParentEvent === true) { + onMouseNotOverEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + } + if (onFocusParentEvent === true) { + onFocusEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onFocus', onFocus) + } + if (onNotFocusParentEvent === true) { + onNotFocusEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onNotFocus', onNotFocus) + } + if (onDisplaceParentEvent === true) { + onDisplaceEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDisplace', onDisplace) + } + if (onDragStartedParentEvent === true) { + onDragStartedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDragStarted', onDragStarted) + } + if (onDragFinishedParentEvent === true) { + onDragFinishedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDragFinished', onDragFinished) + } + + if (connectedToParentWidth) { + thisObject.frame.width = thisObject.parentContainer.frame.width + } + if (connectedToParentHeight) { + thisObject.frame.height = thisObject.parentContainer.frame.height + } + if (connectedToParentRadius) { + thisObject.frame.radius = thisObject.parentContainer.frame.radius + } + } + + function onParentDimmensionsChanged (event) { + let dimmensionsChanged = false + if (connectedToParentWidth) { + thisObject.frame.width = thisObject.parentContainer.frame.width + dimmensionsChanged = true + } + if (connectedToParentHeight) { + thisObject.frame.height = thisObject.parentContainer.frame.height + dimmensionsChanged = true + } + if (connectedToParentRadius) { + thisObject.frame.radius = thisObject.parentContainer.frame.radius + dimmensionsChanged = true + } + + if (dimmensionsChanged) { + thisObject.eventHandler.raiseEvent('Dimmensions Changed', event) + } + } + + function onMouseOver (event) { + thisObject.eventHandler.raiseEvent('onMouseOver', event) + } + + function onMouseNotOver (event) { + thisObject.eventHandler.raiseEvent('onMouseNotOver', event) + } + + function onFocus (event) { + thisObject.eventHandler.raiseEvent('onFocus', event) + } + + function onNotFocus (event) { + thisObject.eventHandler.raiseEvent('onNotFocus', event) + } + + function onDisplace (event) { + thisObject.eventHandler.raiseEvent('onDisplace', event) + } + + function onDragStarted (event) { + thisObject.eventHandler.raiseEvent('onDragStarted', event) + } + + function onDragFinished (event) { + thisObject.eventHandler.raiseEvent('onDragFinished', event) + } + + function isForThisPurpose (purpose) { + if (purpose !== undefined) { + switch (purpose) { + case GET_CONTAINER_PURPOSE.MOUSE_OVER: + if (thisObject.detectMouseOver === true) { + return true + } + break + case GET_CONTAINER_PURPOSE.MOUSE_WHEEL: + if (thisObject.isWheelable === true) { + return true + } + break + case GET_CONTAINER_PURPOSE.MOUSE_CLICK: + if (thisObject.isClickeable === true) { + return true + } + break + case GET_CONTAINER_PURPOSE.DRAGGING: + if (thisObject.isDraggeable === true) { + return true + } + break + default: { return false } + + } + } + return false + } + + function displace (point) { + /* This function will move the container according to the vector received. If it has a visible function will use it + to see if the container can be moved there or not. It wont if the center will not be visible. + + Returns true if move was possible, false if not. */ + + let checkPoint = {} + + if (thisObject.isVisibleFunction === undefined) { + drag() + return true + } else { + checkPoint.x = thisObject.frame.position.x + point.x + thisObject.frame.width / 2 + checkPoint.y = thisObject.frame.position.y + point.y + thisObject.frame.height / 2 + let isVisible = thisObject.isVisibleFunction(checkPoint) + if (isVisible === true) { + drag() + return true + } else { + return false + } + } + function drag () { + if (thisObject.notDraggingOnX === false) { + thisObject.frame.position.x = thisObject.frame.position.x + point.x + } + if (thisObject.notDraggingOnY === false) { + thisObject.frame.position.y = thisObject.frame.position.y + point.y + } + thisObject.eventHandler.raiseEvent('onDisplace', event) + } + } +} diff --git a/CanvasApp/ControlsToolBox/SidePanel.js b/CanvasApp/ControlsToolBox/SidePanel.js new file mode 100644 index 000000000..8c133e6cc --- /dev/null +++ b/CanvasApp/ControlsToolBox/SidePanel.js @@ -0,0 +1,131 @@ +function newSidePanel () { + const MODULE_NAME = 'Side Panel' + let thisObject = { + areas: [], + sidePanelTab: undefined, + container: undefined, + draw: draw, + getContainer: getContainer, + initialize: initialize + } + + let isInitialized = false + + thisObject.container = newContainer() + thisObject.container.name = MODULE_NAME + thisObject.container.initialize() + thisObject.container.isClickeable = true + thisObject.container.isDraggeable = false + thisObject.container.status = 'hidden' + + resize() + return thisObject + + function initialize () { + thisObject.sidePanelTab = newSidePanelTab() + thisObject.sidePanelTab.container.connectToParent(thisObject.container, false, false) + thisObject.sidePanelTab.initialize() + + canvas.eventHandler.listenToEvent('Browser Resized', resize) + isInitialized = true + } + + function resize () { + thisObject.container.frame.width = SIDE_PANEL_WIDTH + thisObject.container.frame.height = browserCanvas.height // - TOP_SPACE_HEIGHT + thisObject.container.frame.position.x = -SIDE_PANEL_WIDTH + thisObject.container.frame.position.y = 0 // TOP_SPACE_HEIGHT + } + + function getContainer (point) { + let container + + container = thisObject.sidePanelTab.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function draw () { + if (isInitialized === false) { return } + borders() + thisObject.sidePanelTab.draw() + + for (let i = 0; i < thisObject.areas.length; i++) { + let area = thisObject.areas[i] + area.draw() + } + } + + function borders () { + let point1 + let point2 + let point3 + let point4 + + point1 = { + x: 0, + y: 0 + } + + point2 = { + x: thisObject.container.frame.width, + y: 0 + } + + point3 = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + } + + point4 = { + x: 0, + y: thisObject.container.frame.height + } + + 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.setLineDash([0, 0]) + 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() + + let opacity = 1 + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.LIGHT_GREY + ', ' + opacity + '' + browserCanvasContext.fill() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + opacity + '' + browserCanvasContext.lineWidth = 0.3 + browserCanvasContext.stroke() + + /* Shadow */ + + if (thisObject.container.status !== 'hidden') { + for (let i = 0; i <= 30; i++) { + opacity = 1 - (i / 300) - 0.95 + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(point2.x + i, point2.y) + browserCanvasContext.lineTo(point3.x + i, point3.y) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + opacity + '' + browserCanvasContext.lineWidth = 1 + browserCanvasContext.stroke() + } + } + } +} diff --git a/CanvasApp/ControlsToolBox/SidePanelTab.js b/CanvasApp/ControlsToolBox/SidePanelTab.js new file mode 100644 index 000000000..8bd0bd253 --- /dev/null +++ b/CanvasApp/ControlsToolBox/SidePanelTab.js @@ -0,0 +1,216 @@ +function newSidePanelTab () { + const MODULE_NAME = 'Side Panel Tab' + let thisObject = { + pointerDirection: 'left', + container: undefined, + draw: draw, + getContainer: getContainer, + initialize: initialize + } + + const TAB_WIDTH = 25 + const TAB_HEIGHT = 60 + + let isInitialized = false + let animation = 'none' + let ORIGINAL_PARENT_POSITON + let xOffset = 0 + + thisObject.container = newContainer() + thisObject.container.name = MODULE_NAME + thisObject.container.initialize() + thisObject.container.isClickeable = true + thisObject.container.isDraggeable = false + + return thisObject + + function initialize () { + thisObject.container.frame.width = TAB_WIDTH + thisObject.container.frame.height = TAB_HEIGHT + thisObject.container.frame.position.x = 0 + thisObject.container.parentContainer.frame.width + thisObject.container.frame.position.y = 20 + + ORIGINAL_PARENT_POSITON = thisObject.container.parentContainer.frame.position.x + + thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + isInitialized = true + } + + function onMouseClick (event) { + if (thisObject.pointerDirection === 'left') { + animation = 'opening' + } else { + animation = 'closing' + } + } + + function getContainer (point) { + let container + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function draw () { + if (isInitialized === false) { return } + animate() + borders() + arrow() + } + + function animate () { + const STEP = 50 + + if (animation === 'opening') { + xOffset = xOffset + STEP + thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset + if (xOffset > thisObject.container.parentContainer.frame.width) { + animation = 'none' + xOffset = thisObject.container.parentContainer.frame.width + thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset + thisObject.container.parentContainer.status = 'visible' + thisObject.pointerDirection = 'right' + } + } + + if (animation === 'closing') { + xOffset = xOffset - STEP + thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset + if (xOffset < 0) { + animation = 'none' + xOffset = 0 + thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + thisObject.container.parentContainer.status = 'hidden' + thisObject.pointerDirection = 'left' + } + } + } + + function arrow () { + let point1 + let point2 + let point3 + + if (thisObject.pointerDirection === 'left') { + point1 = { + x: TAB_WIDTH * 3.5 / 10, + y: TAB_HEIGHT * 12.5 / 30 + } + + point2 = { + x: TAB_WIDTH * 6.5 / 10, + y: TAB_HEIGHT * 15 / 30 + } + + point3 = { + x: TAB_WIDTH * 3.5 / 10, + y: TAB_HEIGHT * 17.5 / 30 + } + } else { + point1 = { + x: TAB_WIDTH * 6.5 / 10, + y: TAB_HEIGHT * 12.5 / 30 + } + + point2 = { + x: TAB_WIDTH * 3.5 / 10, + y: TAB_HEIGHT * 15 / 30 + } + + point3 = { + x: TAB_WIDTH * 6.5 / 10, + y: TAB_HEIGHT * 17.5 / 30 + } + } + + point1 = thisObject.container.frame.frameThisPoint(point1) + point2 = thisObject.container.frame.frameThisPoint(point2) + point3 = thisObject.container.frame.frameThisPoint(point3) + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(point1.x, point1.y) + browserCanvasContext.lineTo(point2.x, point2.y) + browserCanvasContext.lineTo(point3.x, point3.y) + browserCanvasContext.closePath() + + let opacity = 1 + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREY + ', ' + opacity + '' + browserCanvasContext.fill() + } + + function borders () { + let point1 + let point2 + let point3 + let point4 + + point1 = { + x: 0, + y: 0 + } + + point2 = { + x: TAB_WIDTH, + y: 0 + } + + point3 = { + x: TAB_WIDTH, + y: TAB_HEIGHT + } + + point4 = { + x: 0, + y: TAB_HEIGHT + } + + point1 = thisObject.container.frame.frameThisPoint(point1) + point2 = thisObject.container.frame.frameThisPoint(point2) + point3 = thisObject.container.frame.frameThisPoint(point3) + point4 = thisObject.container.frame.frameThisPoint(point4) + + let opacity + + /* Shadow */ + + for (let i = 0; i <= 5; i++) { + opacity = 1 - (i / 100) - 0.93 + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(point1.x + i, point1.y + i) + browserCanvasContext.lineTo(point2.x + i, point2.y + i) + browserCanvasContext.lineTo(point3.x + i, point3.y + i) + browserCanvasContext.lineTo(point4.x + i, point4.y + i) + browserCanvasContext.lineTo(point1.x + i, point1.y + i) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + opacity + '' + browserCanvasContext.lineWidth = 1 + browserCanvasContext.stroke() + } + + /* Background and Border */ + + browserCanvasContext.setLineDash([0, 0]) + 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() + + opacity = 1 + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + '' + browserCanvasContext.fill() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + opacity + '' + browserCanvasContext.lineWidth = 0.3 + browserCanvasContext.stroke() + } +} + diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ChainAttachDetach.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ChainAttachDetach.js new file mode 100644 index 000000000..586a01dbd --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ChainAttachDetach.js @@ -0,0 +1,220 @@ +function newChainAttachDetach () { + thisObject = { + chainDetachNode: chainDetachNode, + chainAttachNode: chainAttachNode + } + + return thisObject + + function chainDetachNode (node, rootNodes) { + switch (node.type) { + case 'Phase': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.phases.length; i++) { + let phase = payload.parentNode.phases[i] + if (phase.id === node.id) { + if (i < payload.parentNode.phases.length - 1) { + let nextPhase = payload.parentNode.phases[i + 1] + if (i > 0) { + let previousPhase = payload.parentNode.phases[i - 1] + nextPhase.payload.chainParent = previousPhase + } else { + nextPhase.payload.chainParent = payload.parentNode + } + } + payload.parentNode.phases.splice(i, 1) + completeDetachment(node, rootNodes) + return + } + } + return + } + default: { + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + /* Detach from parent */ + if (node.payload.parentNode !== undefined) { + let parentNodeDefinition = APP_SCHEMA_MAP.get(node.payload.parentNode.type) + if (parentNodeDefinition !== undefined) { + if (parentNodeDefinition.properties !== undefined) { + for (let i = 0; i < parentNodeDefinition.properties.length; i++) { + let property = parentNodeDefinition.properties[i] + if (nodeDefinition.propertyNameAtParent === property.name) { + switch (property.type) { + case 'node': { + node.payload.parentNode[property.name] = undefined + } + break + case 'array': { + let nodePropertyArray = node.payload.parentNode[property.name] + if (nodePropertyArray !== undefined) { + for (let j = 0; j < nodePropertyArray.length; j++) { + let arrayItem = nodePropertyArray[j] + if (arrayItem.id === node.id) { + nodePropertyArray.splice(j, 1) + } + } + } + } + break + } + } + } + } + } + } + completeDetachment(node, rootNodes) + } + } + } + } + + function chainAttachNode (node, attachToNode, rootNodes) { + switch (node.type) { + + case 'Exchange Account Key': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + if (node.payload.parentNode.keys !== undefined) { + node.payload.parentNode.keys.push(node) + } else { + node.payload.parentNode.key = node + } + completeAttachment(node, rootNodes) + } + break + + case 'Phase': { + switch (attachToNode.type) { + case 'Stop': { + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + completeAttachment(node, rootNodes) + } + break + case 'Take Profit': { + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + completeAttachment(node, rootNodes) + } + break + case 'Initial Stop': { + if (attachToNode.phases.length >= 1) { + return + } + + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + completeAttachment(node, rootNodes) + } + break + case 'Initial Take Profit': { + if (attachToNode.phases.length >= 1) { + return + } + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + completeAttachment(node, rootNodes) + } + break + case 'Phase': { + node.payload.parentNode = attachToNode.payload.parentNode + for (let i = 0; i < node.payload.parentNode.phases.length; i++) { + let phase = node.payload.parentNode.phases[i] + if (attachToNode.id === phase.id) { + if (i === node.payload.parentNode.phases.length - 1) { + node.payload.chainParent = attachToNode + node.payload.parentNode.phases.push(node) + completeAttachment(node, rootNodes) + } else { + node.payload.chainParent = attachToNode + let nextPhase = node.payload.parentNode.phases[i + 1] + nextPhase.payload.chainParent = node + node.payload.parentNode.phases.splice(i + 1, 0, node) + completeAttachment(node, rootNodes) + return + } + } + } + } + } + } + break + default: { + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + /* Attach to new parent */ + if (node.payload.parentNode !== undefined) { + let parentNodeDefinition = APP_SCHEMA_MAP.get(node.payload.parentNode.type) + if (parentNodeDefinition !== undefined) { + if (parentNodeDefinition.properties !== undefined) { + for (let i = 0; i < parentNodeDefinition.properties.length; i++) { + let property = parentNodeDefinition.properties[i] + if (nodeDefinition.propertyNameAtParent === property.name) { + switch (property.type) { + case 'node': { + node.payload.parentNode[property.name] = node + } + break + case 'array': { + if (node.payload.parentNode[property.name] === undefined) { + node.payload.parentNode[property.name] = [] + } + let nodePropertyArray = node.payload.parentNode[property.name] + nodePropertyArray.push(node) + } + break + } + } + } + } + } + } + completeAttachment(node, rootNodes) + } + } + } + } + + function completeDetachment (node, rootNodes) { + node.payload.parentNode = undefined + node.payload.chainParent = undefined + rootNodes.push(node) + } + + function completeAttachment (node, rootNodes) { + for (let i = 0; i < rootNodes.length; i++) { + let rootNode = rootNodes[i] + if (rootNode.id === node.id) { + rootNodes.splice(i, 1) + return + } + } + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeChildren.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeChildren.js new file mode 100644 index 000000000..894dd0623 --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeChildren.js @@ -0,0 +1,67 @@ +function newNodeChildren () { + thisObject = { + childrenCount: childrenCount + } + + return thisObject + + function childrenCount (parentNode, childNode) { + return countChildrenUiObject(parentNode, childNode) + } + + function countChildrenUiObject (parentNode, childNode) { + let response = { + childrenCount: 0, + childIndex: undefined + } + let parentNodeDefinition = APP_SCHEMA_MAP.get(parentNode.type) + if (parentNodeDefinition !== undefined) { + if (parentNodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + + for (let i = 0; i < parentNodeDefinition.properties.length; i++) { + let property = parentNodeDefinition.properties[i] + if (parentNode[property.name] !== undefined) { + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + response.childrenCount++ + if (parentNode[property.name].id === childNode.id) { + response.childIndex = response.childrenCount + } + previousPropertyName = property.name + } + } + break + case 'array': { + let nodeDefinition = APP_SCHEMA_MAP.get(childNode.type) + if (nodeDefinition !== undefined) { + let nodePropertyArray = parentNode[property.name] + for (let j = 0; j < nodePropertyArray.length; j++) { + let child = nodePropertyArray[j] + if (nodeDefinition.chainedToSameType === true) { + if (j < 1) { + response.childrenCount++ + if (child.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + } else { + response.childrenCount++ + if (child.id === childNode.id) { + response.childIndex = response.childrenCount + } + } + } + } + } + break + } + } + } + } + } + + return response + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeCloning.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeCloning.js new file mode 100644 index 000000000..72d458dcf --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeCloning.js @@ -0,0 +1,173 @@ +function newNodeCloning () { + const MODULE_NAME = 'Node Cloning' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + thisObject = { + getNodeClone: getNodeClone + } + return thisObject + + function getNodeClone (node) { + let idConversionMap = new Map() + + let inmatureClone = cloneNode(node) + let matureClone = redirectReferenceParents(inmatureClone) + + return matureClone + + function cloneNode (node) { + if (node === undefined) { return } + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + let object = { + type: node.type, + name: node.name, + code: node.code + } + + /* Children Nodes */ + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + object[property.name] = cloneNode(node[property.name]) + } + previousPropertyName = property.name + } + break + } + case 'array': { + if (node[property.name] !== undefined) { + let nodePropertyArray = node[property.name] + object[property.name] = [] + for (let m = 0; m < nodePropertyArray.length; m++) { + let protocolNode = cloneNode(nodePropertyArray[m]) + if (protocolNode !== undefined) { + object[property.name].push(protocolNode) + } + } + } + break + } + } + } + } + + object.id = newUniqueId() + idConversionMap.set(node.id, object.id) + + object.savedPayload = getSavedPayload(node) + + return object + } + } + + function redirectReferenceParents (node) { + if (node === undefined) { return } + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + let object = { + type: node.type, + name: node.name, + code: node.code + } + + /* Children Nodes */ + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + object[property.name] = redirectReferenceParents(node[property.name]) + } + previousPropertyName = property.name + } + break + } + case 'array': { + if (node[property.name] !== undefined) { + let nodePropertyArray = node[property.name] + object[property.name] = [] + for (let m = 0; m < nodePropertyArray.length; m++) { + let protocolNode = redirectReferenceParents(nodePropertyArray[m]) + if (protocolNode !== undefined) { + object[property.name].push(protocolNode) + } + } + } + break + } + } + } + } + + object.id = node.id + + object.savedPayload = node.savedPayload + + if (object.savedPayload !== undefined) { + if (object.savedPayload.referenceParent !== undefined) { + let newId = idConversionMap.get(object.savedPayload.referenceParent.id) + if (newId !== undefined) { + object.savedPayload.referenceParent.id = newId + } + } + } + + return object + } + } + } + + function getSavedPayload (node) { + if (node.payload === undefined) { return } + let savedPayload = { + position: { + x: node.payload.position.x, + y: node.payload.position.y + }, + targetPosition: { + x: node.payload.targetPosition.x, + y: node.payload.targetPosition.y + }, + floatingObject: { + isPinned: node.payload.floatingObject.isPinned, + isFrozen: (node.payload.floatingObject.isFrozen && node.payload.floatingObject.frozenManually), + isCollapsed: (node.payload.floatingObject.isCollapsed && node.payload.floatingObject.collapsedManually), + angleToParent: (node.payload.floatingObject.angleToParent), + distanceToParent: (node.payload.floatingObject.distanceToParent) + }, + uiObject: { + isRunning: node.payload.uiObject.isRunning, + shortcutKey: node.payload.uiObject.shortcutKey + } + } + + /* Next for the ones that have a reference parent, we include it */ + if (node.payload.referenceParent !== undefined) { + savedPayload.referenceParent = { + type: node.payload.referenceParent.type, + name: node.payload.referenceParent.name, + id: node.payload.referenceParent.id + } + } else { + /* The referenceParent property can be inherited from the previous saved payload. */ + if (node.savedPayload !== undefined) { + savedPayload.referenceParent = node.savedPayload.referenceParent + } + } + + return savedPayload + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeDeleter.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeDeleter.js new file mode 100644 index 000000000..46ad39fcb --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/NodeDeleter.js @@ -0,0 +1,128 @@ +function newNodeDeleter () { + thisObject = { + deleteWorkspace: deleteWorkspace, + deleteUIObject: deleteUIObject + } + + return thisObject + + function destroyUiObject (node) { + canvas.floatingSpace.uiObjectConstructor.destroyUiObject(node.payload) + } + + function deleteWorkspace (node, rootNodes) { + if (node.rootNodes !== undefined) { + while (node.rootNodes.length > 0) { + let rootNode = node.rootNodes[0] + deleteUIObject(rootNode, rootNodes) + } + } + + completeDeletion(node, rootNodes) + destroyUiObject(node) + cleanNode(node) + } + + function deleteUIObject (node, rootNodes) { + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + /* Remove all of its own children nodes. */ + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + deleteUIObject(node[property.name], rootNodes) + } + previousPropertyName = property.name + } + } + break + case 'array': { + let nodePropertyArray = node[property.name] + if (nodePropertyArray !== undefined) { + while (nodePropertyArray.length > 0) { + deleteUIObject(nodePropertyArray[0], rootNodes) + } + } + } + break + } + } + } + + /* Remove node from parent */ + if (node.payload.parentNode !== undefined) { + let parentNodeDefinition = APP_SCHEMA_MAP.get(node.payload.parentNode.type) + if (parentNodeDefinition !== undefined) { + if (parentNodeDefinition.properties !== undefined) { + for (let i = 0; i < parentNodeDefinition.properties.length; i++) { + let property = parentNodeDefinition.properties[i] + if (nodeDefinition.propertyNameAtParent === property.name) { + switch (property.type) { + case 'node': { + node.payload.parentNode[property.name] = undefined + } + break + case 'array': { + let nodePropertyArray = node.payload.parentNode[property.name] + if (nodePropertyArray !== undefined) { + for (let j = 0; j < nodePropertyArray.length; j++) { + let arrayItem = nodePropertyArray[j] + if (arrayItem.id === node.id) { + /* If this object is chained to the ones of the same type we need to give the next in the chain the reference to the current chain parent */ + if (nodeDefinition.chainedToSameType === true) { + if (j < nodePropertyArray.length - 1) { + let nextArrayItem = nodePropertyArray[j + 1] + nextArrayItem.payload.chainParent = node.payload.chainParent + } + } + + nodePropertyArray.splice(j, 1) + } + } + } + } + break + } + } + } + } + } + } + + completeDeletion(node, rootNodes) + destroyUiObject(node) + cleanNode(node) + } + } + + function completeDeletion (node, rootNodes) { + for (let i = 0; i < rootNodes.length; i++) { + let rootNode = rootNodes[i] + if (rootNode.id === node.id) { + rootNodes.splice(i, 1) + return + } + } + } + + function cleanNode (node) { + node.payload.targetPosition.x = undefined + node.payload.targetPosition.y = undefined + node.payload.visible = undefined + node.payload.subTitle = undefined + node.payload.title = undefined + node.payload.node = undefined + node.payload.parentNode = undefined + node.payload.chainParent = undefined + node.payload.onMenuItemClick = undefined + node.handle = undefined + node.payload = undefined + node.cleaned = true + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/OnFocus.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/OnFocus.js new file mode 100644 index 000000000..a45c9f76c --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/OnFocus.js @@ -0,0 +1,56 @@ +function newOnFocus () { + thisObject = { + getNodeThatIsOnFocus: getNodeThatIsOnFocus + } + return thisObject + + function getNodeThatIsOnFocus (node) { + if (node === undefined) { return } + if (node.payload === undefined) { return } + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + /* First we ask the question to ourself */ + if (node.payload.uiObject.isOnFocus === true) { + return node + } + + /* Then we check all of its own children nodes. */ + if (node.payload.floatingObject.isCollapsed !== true) { + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + let child + child = getNodeThatIsOnFocus(node[property.name]) + if (child !== undefined) { + return child + } + } + previousPropertyName = property.name + } + } + break + case 'array': { + let nodePropertyArray = node[property.name] + if (nodePropertyArray !== undefined) { + for (let m = 0; m < nodePropertyArray.length; m++) { + child = getNodeThatIsOnFocus(nodePropertyArray[m]) + if (child !== undefined) { + return child + } + } + } + } + break + } + } + } + } + } + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js new file mode 100644 index 000000000..4eaf8ec8c --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js @@ -0,0 +1,189 @@ +function newProtocolNode () { + const MODULE_NAME = 'Protocol Node' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + thisObject = { + getProtocolNode: getProtocolNode + } + return thisObject + + function getProtocolNode (node, removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, lightingPath) { + if (node === undefined) { return } + if (node.payload === undefined) { return } + + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + if (removePersonalData === true && nodeDefinition.isPersonalData === true) { return } + let object = { + type: node.type, + name: node.name, + code: node.code + } + + /* Children Nodes */ + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (node[property.name] !== undefined) { + if (property.name !== previousPropertyName) { + if (lightingPath === undefined) { + object[property.name] = getProtocolNode(node[property.name], removePersonalData, parseCode, includeIds, includeReferencesInSavedPayload, includePayload) + } else { + let newLightingPath = getNewLightingPath(lightingPath, node.type, property.childType) + if (newLightingPath !== undefined) { + object[property.name] = getProtocolNode(node[property.name], removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath) + } + } + previousPropertyName = property.name + } + } + break + } + case 'array': { + if (node[property.name] !== undefined) { + if (lightingPath === undefined) { + let nodePropertyArray = node[property.name] + object[property.name] = [] + for (let m = 0; m < nodePropertyArray.length; m++) { + let protocolNode = getProtocolNode(nodePropertyArray[m], removePersonalData, parseCode, includeIds, includeReferencesInSavedPayload, includePayload) + if (protocolNode !== undefined) { + object[property.name].push(protocolNode) + } + } + } else { + let newLightingPath = getNewLightingPath(lightingPath, node.type, property.childType) + if (newLightingPath !== undefined) { + let nodePropertyArray = node[property.name] + object[property.name] = [] + for (let m = 0; m < nodePropertyArray.length; m++) { + let protocolNode = getProtocolNode(nodePropertyArray[m], removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath) + if (protocolNode !== undefined) { + object[property.name].push(protocolNode) + } + } + } + } + } + break + } + } + } + } + + /* Parent Nodes */ + if (node.payload.parentNode !== undefined) { + let newLightingPath = getNewLightingPath(lightingPath, node.type, node.payload.parentNode.type) + if (newLightingPath !== undefined) { + object.parentNode = getProtocolNode(node.payload.parentNode, removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath) + } + } + if (node.payload.referenceParent !== undefined) { + let newLightingPath = getNewLightingPath(lightingPath, node.type, node.payload.referenceParent.type) + if (newLightingPath !== undefined) { + object.referenceParent = getProtocolNode(node.payload.referenceParent, removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath) + } + } + + if (parseCode && object.code !== undefined && nodeDefinition.editors !== undefined) { + if (nodeDefinition.editors.config === true) { + try { + object.code = JSON.parse(node.code) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> err = ' + err.stack) } + if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.type = ' + node.type) } + if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.name = ' + node.name) } + if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.code = ' + node.code) } + if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.id = ' + node.id) } + } + } + } + + if (includeIds) { + object.id = node.id + } + if (includePayload) { + object.savedPayload = getSavedPayload(node, includeReferencesInSavedPayload) + } + + return object + } + } + + function getNewLightingPath (lightingPath, currentNodeType, nextNodeType) { + if (lightingPath === undefined) { return } + + if (currentNodeType === undefined) { return } + if (nextNodeType === undefined) { return } + + let currentNodePosition = lightingPath.indexOf('->' + currentNodeType + '->') + let nextNodePosition = lightingPath.indexOf('->' + nextNodeType + '->') + + let newLightingPath = lightingPath.substring(nextNodePosition, lightingPath.length + 1) + + if (nextNodePosition > currentNodePosition) { + return newLightingPath + } else { + return + } + } + + function getSavedPayload (node, includeReferencesInSavedPayload) { + if (node.payload === undefined) { return } + let savedPayload = { + position: { + x: node.payload.position.x, + y: node.payload.position.y + }, + targetPosition: { + x: node.payload.targetPosition.x, + y: node.payload.targetPosition.y + }, + floatingObject: { + isPinned: node.payload.floatingObject.isPinned, + isFrozen: (node.payload.floatingObject.isFrozen && node.payload.floatingObject.frozenManually), + isCollapsed: (node.payload.floatingObject.isCollapsed && node.payload.floatingObject.collapsedManually), + angleToParent: (node.payload.floatingObject.angleToParent), + distanceToParent: (node.payload.floatingObject.distanceToParent) + }, + uiObject: { + isRunning: node.payload.uiObject.isRunning, + shortcutKey: node.payload.uiObject.shortcutKey + } + } + + if (node.payload.frame !== undefined) { + savedPayload.frame = { + position: { + x: node.payload.frame.position.x, + y: node.payload.frame.position.y + }, + width: node.payload.frame.width, + height: node.payload.frame.height, + radius: node.payload.frame.radius + } + } + + if (includeReferencesInSavedPayload) { + /* Next for the ones that have a reference parent, we include it */ + if (node.payload.referenceParent !== undefined) { + savedPayload.referenceParent = { + type: node.payload.referenceParent.type, + name: node.payload.referenceParent.name, + id: node.payload.referenceParent.id + } + } else { + /* The referenceParent property can be inherited from the previous saved payload. */ + if (node.savedPayload !== undefined) { + savedPayload.referenceParent = node.savedPayload.referenceParent + } + } + } + return savedPayload + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ReferenceAttachDetach.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ReferenceAttachDetach.js new file mode 100644 index 000000000..cdf5385dc --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ReferenceAttachDetach.js @@ -0,0 +1,24 @@ +function newReferenceAttachDetach () { + thisObject = { + referenceDetachNode: referenceDetachNode, + referenceAttachNode: referenceAttachNode + } + + return thisObject + + function referenceDetachNode (node) { + completeDetachment(node) + } + + function referenceAttachNode (node, attachToNode, rootNodes) { + completeAttachment(node, attachToNode) + } + + function completeAttachment (node, attachToNode) { + node.payload.referenceParent = attachToNode + } + + function completeDetachment (node) { + node.payload.referenceParent = undefined + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js new file mode 100644 index 000000000..5b967b29d --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js @@ -0,0 +1,81 @@ +function newSessionFunctions () { + thisObject = { + runSession: runSession, + stopSession: stopSession + } + + return thisObject + + function runSession (node, functionLibraryProtocolNode, callBackFunction) { + /* We can not run a sessionif its parent process is not running. Less if it does not have a parent. */ + if (node.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be able to run.') + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + if (node.payload.parentNode.payload.uiObject.isRunning !== true) { + node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be running to be able to run.') + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + if (node.payload.referenceParent === undefined) { + node.payload.uiObject.setErrorMessage('Parent Process Instance need to reference a Process Definition.') + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + node.payload.uiObject.run(callBackFunction) + + let key = node.name + '-' + node.type + '-' + node.id + + let lightingPath = '' + + 'Trading System->' + + 'Parameters->' + + 'Base Asset->Time Range->Time Frame->Slippage->Fee Structure->' + + 'Exchange Account Asset->Asset->' + + 'Strategy->' + + 'Trigger Stage->Trigger On Event->Trigger Off Event->Take Position Event->' + + 'Announcement->Telegram Bot->' + + 'Open Stage->Initial Definition->Open Execution->' + + 'Position Size->Position Rate->Formula->' + + 'Initial Stop->Initial Take Profit->' + + 'Manage Stage->' + + 'Stop->Take Profit->' + + 'Phase->Formula->Next Phase Event->' + + 'Situation->Condition->Javascript Code->' + + 'Announcement->Telegram Bot->' + + 'Close Stage->Close Execution->' + + let tradingSystem = functionLibraryProtocolNode.getProtocolNode(node.payload.referenceParent, false, true, true, false, false, lightingPath) + + lightingPath = '' + + 'Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->' + + 'Parameters->' + + 'Base Asset->Time Range->Time Frame->Slippage->Fee Structure->' + + 'Exchange Account Asset->Asset->' + + let session = functionLibraryProtocolNode.getProtocolNode(node, false, true, true, false, false, lightingPath) + + /* Raise event to run the session */ + let event = { + session: JSON.stringify(session), + tradingSystem: JSON.stringify(tradingSystem) + } + + systemEventHandler.raiseEvent(key, 'Run Session', event) + + if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + } + + function stopSession (node, functionLibraryProtocolNode, callBackFunction) { + let key = node.name + '-' + node.type + '-' + node.id + systemEventHandler.raiseEvent(key, 'Stop Session') + + node.payload.uiObject.stop(callBackFunction) + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ShortcutKeys.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ShortcutKeys.js new file mode 100644 index 000000000..28e6afc2a --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ShortcutKeys.js @@ -0,0 +1,55 @@ +function newShortcutKeys () { + thisObject = { + getNodeByShortcutKey: getNodeByShortcutKey + } + return thisObject + + function getNodeByShortcutKey (node, searchingKey) { + if (node === undefined) { return } + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + /* Check Self first */ + if (node.payload.uiObject.shortcutKey === searchingKey) { + return node + } + + /* Check all of its own children nodes. */ + if (node.payload.floatingObject.isCollapsed !== true) { + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + let child + child = getNodeByShortcutKey(node[property.name], searchingKey) + if (child !== undefined) { + return child + } + } + previousPropertyName = property.name + } + } + break + case 'array': { + let nodePropertyArray = node[property.name] + if (nodePropertyArray !== undefined) { + for (let m = 0; m < nodePropertyArray.length; m++) { + child = getNodeByShortcutKey(nodePropertyArray[m], searchingKey) + if (child !== undefined) { + return child + } + } + } + } + break + } + } + } + } + } + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js new file mode 100644 index 000000000..ff624cb88 --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js @@ -0,0 +1,110 @@ +function newTaskFunctions () { + thisObject = { + runTask: runTask, + stopTask: stopTask, + runAllTasks: runAllTasks, + stopAllTasks: stopAllTasks + } + + return thisObject + + function runTask (node, functionLibraryProtocolNode, callBackFunction) { + /* Check if it is possible to Run or not */ + if (node.bot === undefined) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + if (node.bot.processes.length === 0) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + for (let i = 0; i < node.bot.processes.length; i++) { + let process = node.bot.processes[i] + process.payload.uiObject.run() + } + + let lightingPath = '->Task->' + + 'Sensor Bot Instance->' + + 'Indicator Bot Instance->' + + 'Trading Bot Instance->' + + 'Sensor Process Instance->Indicator Process Instance->Trading Process Instance->' + + 'Market Reference->' + + 'Key Instance->Exchange Account Key->' + + 'Market->Exchange Markets->Crypto Exchange->' + + 'Market Base Asset->Market Quoted Asset->Asset->' + + 'Backtesting Session->Live Trading Session->Paper Trading Session->Fordward Testing Session->' + + 'Process Definition->' + + 'Process Output->Output Dataset->Dataset Definition->Product Definition->' + + 'Process Dependencies->' + + 'Status Dependency->Status Report->Process Definition->' + + 'Data Dependency->Dataset Definition->Product Definition->' + + 'Record Definition->Record Property->Formula->' + + 'Data Building Procedure->' + + 'Procedure Initialization->Javascript Code->' + + 'Procedure Loop->Javascript Code->' + + 'Calculations Procedure->' + + 'Procedure Initialization->Javascript Code->' + + 'Procedure Loop->Javascript Code->' + + 'Status Report->' + + 'Execution Finished Event->' + + 'Execution Started Event->Execution Finished Event->Process Definition->' + + 'Sensor Bot->' + + 'Indicator Bot->' + + 'Trading Bot->' + + 'Data Mine->' + + let definition = functionLibraryProtocolNode.getProtocolNode(node, false, true, true, false, false, lightingPath) + + let event = { + taskId: node.id, + taskName: node.name, + definition: JSON.stringify(definition) // <- We need to do this workaround in order no to send unescaped charactars to the taskManager. + } + + if (node.payload.parentNode === undefined) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + systemEventHandler.raiseEvent('Task Server', 'Debug Task Started', event) + return + } + + node.payload.uiObject.run(callBackFunction) + systemEventHandler.raiseEvent('Task Manager', 'Run Task', event) + } + + function stopTask (node, functionLibraryProtocolNode, callBackFunction) { + let event = { + taskId: node.id, + taskName: node.name + } + systemEventHandler.raiseEvent('Task Manager', 'Stop Task', event) + + node.payload.uiObject.stop(callBackFunction) + + if (node.bot === undefined) { return } + if (node.bot.processes.length === 0) { return } + + for (let i = 0; i < node.bot.processes.length; i++) { + let process = node.bot.processes[i] + process.payload.uiObject.stop() + } + } + + function runAllTasks (taskManager, functionLibraryProtocolNode) { + for (let i = 0; i < taskManager.tasks.length; i++) { + let node = taskManager.tasks[i] + let menu = node.payload.uiObject.menu + + menu.internalClick('Run Task') + } + } + + function stopAllTasks (taskManager, functionLibraryProtocolNode) { + for (let i = 0; i < taskManager.tasks.length; i++) { + let node = taskManager.tasks[i] + let menu = node.payload.uiObject.menu + + menu.internalClick('Stop Task') + } + } +} diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js new file mode 100644 index 000000000..96f7bfef3 --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js @@ -0,0 +1,413 @@ +function newUiObjectsFromNodes () { + thisObject = { + recreateWorkspace: recreateWorkspace, + tryToConnectChildrenWithReferenceParents: tryToConnectChildrenWithReferenceParents, + createUiObjectFromNode: createUiObjectFromNode, + addUIObject: addUIObject, + addMissingChildren: addMissingChildren + } + + let mapOfReferenceChildren = new Map() + let mapOfNodes + let tasksToRun + let sessionsToRun + + return thisObject + + function recreateWorkspace (node, replacingCurrentWorkspace) { + mapOfNodes = new Map() + tasksToRun = [] + sessionsToRun = [] + + /* Create the workspace UI OBject and then continue with the root nodes. */ + createUiObject(false, 'Workspace', node.name, node, undefined, undefined, 'Workspace') + if (node.rootNodes !== undefined) { + for (let i = 0; i < node.rootNodes.length; i++) { + let rootNode = node.rootNodes[i] + createUiObjectFromNode(rootNode, undefined, undefined) + } + } + + tryToConnectChildrenWithReferenceParents() + + if (replacingCurrentWorkspace === true) { + // We need to wait all tasks that were potentially running to stop + setTimeout(runTasks, 70000) + // We give a few seconds for the tasks to start + setTimeout(runSessions, 80000) + } else { + runTasks() + // We give a few seconds for the tasks to start + setTimeout(runSessions, 10000) + } + } + + function runTasks () { + for (let i = 0; i < tasksToRun.length; i++) { + let node = tasksToRun[i] + node.payload.uiObject.menu.internalClick('Run Task') + } + tasksToRun = undefined + } + + function runSessions () { + for (let i = 0; i < sessionsToRun.length; i++) { + let node = sessionsToRun[i] + node.payload.uiObject.menu.internalClick('Run Session') + } + sessionsToRun = undefined + } + + function tryToConnectChildrenWithReferenceParents () { + /* We reconstruct here the reference relationships. */ + for (const [key, node] of mapOfNodes) { + if (node.payload === undefined) { continue } + if (node.payload.referenceParent !== undefined) { + if (node.payload.referenceParent.cleaned === true) { + node.payload.referenceParent = mapOfNodes.get(node.payload.referenceParent.id) + continue // We were referencing a deleted node, so we replace it potentially with a newly created one. + } else { + continue // In this case the reference is already good. + } + } + if (node.savedPayload !== undefined) { + if (node.savedPayload.referenceParent !== undefined) { // these are children recreated + node.payload.referenceParent = mapOfNodes.get(node.savedPayload.referenceParent.id) + } + } + } + } + + function createUiObjectFromNode (node, parentNode, chainParent, positionOffset) { + /* Get node definition */ + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition !== undefined) { + /* Resolve Initial Values */ + if (nodeDefinition.initialValues !== undefined) { + if (nodeDefinition.initialValues.code !== undefined) { + if (node.code === undefined) { + node.code = nodeDefinition.initialValues.code + } + } + } + + /* For the cases where an node is not chained to its parent but to the one at the parent before it at its collection */ + if (nodeDefinition.chainedToSameType === true) { + if (parentNode !== undefined) { + let parentNodeDefinition = APP_SCHEMA_MAP.get(parentNode.type) + if (parentNodeDefinition !== undefined) { + if (parentNodeDefinition.properties !== undefined) { + for (let i = 0; i < parentNodeDefinition.properties.length; i++) { + let property = parentNodeDefinition.properties[i] + if (property.childType === node.type) { + if (property.type === 'array') { + if (parentNode[property.name] !== undefined) { + if (parentNode[property.name].length > 1) { + let nodeChildren = parentNode[property.name] + for (let j = 0; j < nodeChildren.length; j++) { + if (node.id === nodeChildren[j].id) { + if (j > 0) { + chainParent = nodeChildren[j - 1] + } + } + } + } + } + } + } + } + } + } + } + } + /* Create Self */ + createUiObject(false, node.type, node.name, node, parentNode, chainParent, node.type, positionOffset) + + /* Create Children */ + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + if (node[property.name] !== undefined) { + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + createUiObjectFromNode(node[property.name], node, node, positionOffset) + previousPropertyName = property.name + } + } + break + case 'array': { + let nodePropertyArray = node[property.name] + for (let m = 0; m < nodePropertyArray.length; m++) { + let arrayItem = nodePropertyArray[m] + createUiObjectFromNode(arrayItem, node, node, positionOffset) + } + } + break + } + } + } + } + } + } + + function addUIObject (parentNode, type) { + let object = { + name: 'New ' + type, + type: type + } + + let parentNodeDefinition = APP_SCHEMA_MAP.get(parentNode.type) + if (parentNodeDefinition !== undefined) { + /* Resolve Initial Values */ + let nodeDefinition = APP_SCHEMA_MAP.get(object.type) + + if (nodeDefinition.isHierarchyHead === true) { + parentNode.rootNodes.push(object) + createUiObject(true, object.type, object.name, object, parentNode, undefined) + return object + } + + /* For the cases where an node is not chained to its parent but to the one at the parent before it at its collection */ + let chainParent = parentNode + if (nodeDefinition.chainedToSameType === true) { + if (parentNodeDefinition.properties !== undefined) { + for (let i = 0; i < parentNodeDefinition.properties.length; i++) { + let property = parentNodeDefinition.properties[i] + if (property.childType === type) { + if (property.type === 'array') { + if (parentNode[property.name] !== undefined) { + if (parentNode[property.name].length > 0) { + let nodeChildren = parentNode[property.name] + chainParent = nodeChildren[nodeChildren.length - 1] + } + } + } + } + } + } + } + + if (nodeDefinition !== undefined) { + if (nodeDefinition.initialValues !== undefined) { + if (nodeDefinition.initialValues.code !== undefined) { + object.code = nodeDefinition.initialValues.code + } + } + } + + /* Connect to Parent */ + if (parentNodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < parentNodeDefinition.properties.length; i++) { + let property = parentNodeDefinition.properties[i] + if (property.childType === type) { + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + parentNode[property.name] = object + previousPropertyName = property.name + } + } + break + case 'array': { + if (parentNode[property.name] === undefined) { + parentNode[property.name] = [] + } + if (property.maxItems !== undefined) { + if (parentNode[property.name].length < property.maxItems) { + parentNode[property.name].push(object) + } else { + return // Object can not be created. + } + } else { + parentNode[property.name].push(object) + } + } + break + } + } + } + } + + createUiObject(true, object.type, object.name, object, parentNode, chainParent) + + /* Auto Add more Children */ + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (property.autoAdd === true) { + addUIObject(object, property.childType) + previousPropertyName = property.name + } + } + } + break + case 'array': { + if (property.autoAdd === true) { + if (object[property.name] === undefined) { + object[property.name] = [] + } + addUIObject(object, property.childType) + } + } + break + } + } + } + } + return object + } + + function addMissingChildren (node) { + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + + /* Connect to Parent */ + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + if (property.type === 'node') { + if (property.name !== previousPropertyName) { + if (node[property.name] === undefined) { + addUIObject(node, property.childType) + previousPropertyName = property.name + } + } + } + } + } + } + + function createUiObject (userAddingNew, uiObjectType, name, node, parentNode, chainParent, title, positionOffset) { + let payload = {} + + /* Mechanism related to keeping references when cloning */ + if (node.id === undefined) { + node.id = newUniqueId() + } else { + let testNode = mapOfNodes.get(node.id) + if (testNode !== undefined && testNode.cleaned !== true) { + node.id = newUniqueId() + } + } + + /* Default Naming */ + if (name === '' || name === undefined) { name = 'My ' + uiObjectType } + + /* If we are creating this object as the result of restoring a backup, share or clone, we will bring some of the saved properties to the new running instance. */ + if (userAddingNew === false && uiObjectType !== 'Workspace') { + /* This following info can not be missing. */ + if (node.savedPayload === undefined) { + console.log('Node ' + node.type + ' without savedPayload.', node) + return + } + if (node.savedPayload.position === undefined) { + console.log('Node ' + node.type + ' without savedPayload.position.', node) + return + } + if (node.savedPayload.targetPosition === undefined) { + console.log('Node ' + node.type + ' without savedPayload.targetPosition.', node) + return + } + + /* If there is not a position offset, which happens when we are dropping a node into the designer, we create a cero vector then. */ + if (positionOffset === undefined) { + positionOffset = { + x: 0, + y: 0 + } + } + + /* Adding the offset to the saved positions. */ + node.savedPayload.targetPosition.x = node.savedPayload.targetPosition.x + positionOffset.x + node.savedPayload.targetPosition.y = node.savedPayload.targetPosition.y + positionOffset.y + node.savedPayload.position.x = node.savedPayload.position.x + positionOffset.x + node.savedPayload.position.y = node.savedPayload.position.y + positionOffset.y + + /* Transferring the saved payload properties into the running instance being created. */ + payload.targetPosition = { + x: node.savedPayload.targetPosition.x, + y: node.savedPayload.targetPosition.y + } + node.savedPayload.targetPosition = undefined + + /* Reference children connection to parents */ + if (node.savedPayload.referenceParent !== undefined) { + payload.referenceParent = mapOfNodes.get(node.savedPayload.referenceParent.id) + + if (payload.referenceParent !== undefined) { + mapOfReferenceChildren.set(node.id, node) + } + } + } + + /* If we are adding a new object, then we set the initial values for position and targetPosition */ + if (userAddingNew === true || uiObjectType === 'Workspace') { + /* Workspace allways to the spawn position */ + if (uiObjectType === 'Workspace') { + payload.position = { + x: spawnPosition.x, + y: spawnPosition.y + } + } + + /* Chain parent pointing to the position of the chain parent if defined. */ + if (chainParent === undefined) { + payload.targetPosition = { + x: spawnPosition.x, + y: spawnPosition.y + } + } else { + payload.targetPosition = { + x: chainParent.payload.position.x, + y: chainParent.payload.position.y + } + } + } + + /* Setting the title */ + if (title !== undefined) { + payload.subTitle = title + } else { + payload.subTitle = uiObjectType + } + + payload.visible = true + payload.title = name + payload.node = node + payload.parentNode = parentNode + payload.chainParent = chainParent + payload.onMenuItemClick = canvas.designSpace.workspace.onMenuItemClick + + node.payload = payload + node.type = uiObjectType + + /* Now we mount the floating object where the UIOBject will be laying on top of */ + canvas.floatingSpace.uiObjectConstructor.createUiObject(userAddingNew, payload) + + mapOfNodes.set(node.id, node) + + /* Check if there are tasks to run */ + if (userAddingNew === false && uiObjectType === 'Task' && node.savedPayload !== undefined) { + if (node.savedPayload.uiObject.isRunning === true) { + tasksToRun.push(node) + } + } + + /* Check if there are sessions to run */ + if (userAddingNew === false && node.savedPayload !== undefined) { + if (uiObjectType === 'Live Trading Session' || uiObjectType === 'Fordward Testing Session' || uiObjectType === 'Backtesting Session' || uiObjectType === 'Paper Trading Session') { + if (node.savedPayload.uiObject.isRunning === true) { + sessionsToRun.push(node) + } + } + } + } +} diff --git a/CanvasApp/DesignSpace/Workspace/Workspace.js b/CanvasApp/DesignSpace/Workspace/Workspace.js new file mode 100644 index 000000000..e9075d8a7 --- /dev/null +++ b/CanvasApp/DesignSpace/Workspace/Workspace.js @@ -0,0 +1,371 @@ + +function newWorkspace () { + const MODULE_NAME = 'Workspace' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + workspaceNode: undefined, + tradingSystem: undefined, + container: undefined, + enabled: false, + nodeChildren: undefined, + getNodeThatIsOnFocus: getNodeThatIsOnFocus, + getNodeByShortcutKey: getNodeByShortcutKey, + stopAllRunningTasks: stopAllRunningTasks, + onMenuItemClick: onMenuItemClick, + physics: physics, + draw: draw, + spawn: spawn, + chainDetachNode: chainDetachNode, + chainAttachNode: chainAttachNode, + referenceDetachNode: referenceDetachNode, + referenceAttachNode: referenceAttachNode, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.isWheelable = false + thisObject.container.detectMouseOver = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + thisObject.container.frame.width = 0 + thisObject.container.frame.height = 0 + + spawnPosition = { + x: canvas.floatingSpace.container.frame.width / 2, + y: canvas.floatingSpace.container.frame.height / 2 + } + + thisObject.workspaceNode = {} + thisObject.workspaceNode.rootNodes = [] + + let functionLibraryReferenceAttachDetach = newReferenceAttachDetach() + let functionLibraryChainAttachDetach = newChainAttachDetach() + let functionLibraryNodeDeleter = newNodeDeleter() + let functionLibraryUiObjectsFromNodes = newUiObjectsFromNodes() + let functionLibraryProtocolNode = newProtocolNode() + let functionLibraryNodeCloning = newNodeCloning() + let functionLibraryTaskFunctions = newTaskFunctions() + let functionLibrarySessionFunctions = newSessionFunctions() + let functionLibraryShortcutKeys = newShortcutKeys() + let functionLibraryOnFocus = newOnFocus() + + thisObject.nodeChildren = newNodeChildren() + + let workingAtTask = 0 + let circularProgressBar = newBusyProgressBar() + circularProgressBar.fitFunction = canvas.floatingSpace.fitIntoVisibleArea + let droppedNode + + return thisObject + + function finalize () { + thisObject.definition = undefined + thisObject.container.finalize() + thisObject.container = undefined + thisObject.workspaceNode = undefined + circularProgressBar.finalize() + circularProgressBar = undefined + } + + function initialize () { + try { + let savedWorkspace = window.localStorage.getItem(CANVAS_APP_NAME + '.' + 'Workspace') + + if (savedWorkspace === null) { + thisObject.workspaceNode.type = 'Workspace' + thisObject.workspaceNode.name = 'My Workspace' + } else { + thisObject.workspaceNode = JSON.parse(savedWorkspace) + } + functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode) + thisObject.enabled = true + + setInterval(saveWorkspace, 5000) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + } + } + + function chainDetachNode (node) { + functionLibraryChainAttachDetach.chainDetachNode(node, thisObject.workspaceNode.rootNodes) + } + + function chainAttachNode (node, attachToNode) { + functionLibraryChainAttachDetach.chainAttachNode(node, attachToNode, thisObject.workspaceNode.rootNodes) + } + + function referenceDetachNode (node) { + functionLibraryReferenceAttachDetach.referenceDetachNode(node) + } + + function referenceAttachNode (node, attachToNode) { + functionLibraryReferenceAttachDetach.referenceAttachNode(node, attachToNode, thisObject.workspaceNode.rootNodes) + } + + function saveWorkspace () { + let textToSave = stringifyWorkspace() + window.localStorage.setItem(CANVAS_APP_NAME + '.' + 'Workspace', textToSave) + } + + function physics () { + if (thisObject.enabled !== true) { return } + + if (workingAtTask > 0) { + circularProgressBar.physics() + + switch (workingAtTask) { + case 1: + stopAllRunningTasks() + workingAtTask++ + break + case 2: + functionLibraryNodeDeleter.deleteWorkspace(thisObject.workspaceNode, thisObject.workspaceNode.rootNodes) + workingAtTask++ + break + case 3: + thisObject.workspaceNode = droppedNode + droppedNode = undefined + workingAtTask++ + break + case 4: + functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode, true) + workingAtTask++ + break + case 5: + canvas.chartingSpace.reset() + workingAtTask++ + break + case 6: + workingAtTask = 0 + circularProgressBar.visible = false + break + } + } + } + + function draw () { + if (circularProgressBar !== undefined) { + circularProgressBar.draw() + } + } + + function stringifyWorkspace (removePersonalData) { + let stringifyReadyNodes = [] + for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) { + let rootNode = thisObject.workspaceNode.rootNodes[i] + let node = functionLibraryProtocolNode.getProtocolNode(rootNode, removePersonalData, false, true, true, true) + if (node) { + stringifyReadyNodes.push(node) + } + } + let workspace = { + type: 'Workspace', + name: thisObject.workspaceNode.name, + rootNodes: stringifyReadyNodes + } + + return JSON.stringify(workspace) + } + + function stopAllRunningTasks () { + for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) { + let rootNode = thisObject.workspaceNode.rootNodes[i] + if (rootNode.type === 'Network') { + if (rootNode.networkNodes !== undefined) { + for (let j = 0; j < rootNode.networkNodes.length; j++) { + let networkNode = rootNode.networkNodes[j] + if (networkNode.dataMining !== undefined) { + for (let i = 0; i < networkNode.dataMining.taskManagers.length; i++) { + let taskManager = networkNode.dataMining.taskManagers[i] + taskManager.payload.uiObject.menu.internalClick('Stop All Tasks') + } + } + if (networkNode.testingEnvironment !== undefined) { + for (let i = 0; i < networkNode.testingEnvironment.taskManagers.length; i++) { + let taskManager = networkNode.testingEnvironment.taskManagers[i] + taskManager.payload.uiObject.menu.internalClick('Stop All Tasks') + } + } + if (networkNode.productionEnvironment !== undefined) { + for (let i = 0; i < networkNode.productionEnvironment.taskManagers.length; i++) { + let taskManager = networkNode.productionEnvironment.taskManagers[i] + taskManager.payload.uiObject.menu.internalClick('Stop All Tasks') + } + } + } + } + } + } + } + + function getNodeByShortcutKey (searchingKey) { + for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) { + let rootNode = thisObject.workspaceNode.rootNodes[i] + let node = functionLibraryShortcutKeys.getNodeByShortcutKey(rootNode, searchingKey) + if (node !== undefined) { return node } + } + } + + function getNodeThatIsOnFocus () { + for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) { + let rootNode = thisObject.workspaceNode.rootNodes[i] + let node = functionLibraryOnFocus.getNodeThatIsOnFocus(rootNode) + if (node !== undefined) { return node } + } + } + + function spawn (nodeText, mousePointer) { + try { + let point = { + x: mousePointer.x, + y: mousePointer.y + } + point = canvas.floatingSpace.container.frame.unframeThisPoint(point) + spawnPosition.x = point.x + spawnPosition.y = point.y + + droppedNode = JSON.parse(nodeText) + + if (droppedNode.type === 'Workspace') { + circularProgressBar.initialize(mousePointer) + circularProgressBar.visible = true + workingAtTask = 1 + return + } + + /* It does not exist, so we recreeate it respecting the inner state of each object. */ + let positionOffset = { + x: spawnPosition.x - droppedNode.savedPayload.position.x, + y: spawnPosition.y - droppedNode.savedPayload.position.y + } + + thisObject.workspaceNode.rootNodes.push(droppedNode) + functionLibraryUiObjectsFromNodes.createUiObjectFromNode(droppedNode, undefined, undefined, positionOffset) + functionLibraryUiObjectsFromNodes.tryToConnectChildrenWithReferenceParents() + + droppedNode = undefined + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] spawn -> err = ' + err.stack) } + } + } + + async function onMenuItemClick (payload, action, relatedUiObject, callBackFunction) { + switch (action) { + case 'Add UI Object': + { + functionLibraryUiObjectsFromNodes.addUIObject(payload.node, relatedUiObject) + } + break + case 'Add Missing Children': + { + functionLibraryUiObjectsFromNodes.addMissingChildren(payload.node) + } + break + case 'Delete UI Object': + { + functionLibraryNodeDeleter.deleteUIObject(payload.node, thisObject.workspaceNode.rootNodes) + } + break + case 'Share Workspace': + { + let text = stringifyWorkspace(true) + let fileName = 'Share - ' + payload.node.type + ' - ' + payload.node.name + '.json' + downloadText(fileName, text) + } + break + case 'Backup Workspace': + { + let text = stringifyWorkspace(false) + let fileName = 'Backup - ' + payload.node.type + ' - ' + payload.node.name + '.json' + downloadText(fileName, text) + } + break + case 'Edit Code': + + break + case 'Share': + { + let text = JSON.stringify(functionLibraryProtocolNode.getProtocolNode(payload.node, true, false, true, true, true)) + + let nodeName = payload.node.name + if (nodeName === undefined) { + nodeName = '' + } else { + nodeName = '.' + nodeName + } + let fileName = 'Share - ' + payload.node.type + ' - ' + nodeName + '.json' + downloadText(fileName, text) + } + + break + case 'Backup': + { + let text = JSON.stringify(functionLibraryProtocolNode.getProtocolNode(payload.node, false, false, true, true, true)) + + let nodeName = payload.node.name + if (nodeName === undefined) { + nodeName = '' + } else { + nodeName = ' ' + nodeName + } + let fileName = 'Backup - ' + payload.node.type + ' - ' + nodeName + '.json' + downloadText(fileName, text) + } + + break + case 'Clone': + { + let text = JSON.stringify(functionLibraryNodeCloning.getNodeClone(payload.node)) + + let nodeName = payload.node.name + if (nodeName === undefined) { + nodeName = '' + } else { + nodeName = ' ' + nodeName + } + let fileName = 'Clone - ' + payload.node.type + ' - ' + nodeName + '.json' + downloadText(fileName, text) + } + + break + case 'Run Task': + { + functionLibraryTaskFunctions.runTask(payload.node, functionLibraryProtocolNode, callBackFunction) + } + break + case 'Stop Task': + { + functionLibraryTaskFunctions.stopTask(payload.node, functionLibraryProtocolNode, callBackFunction) + } + break + case 'Run All Tasks': + { + functionLibraryTaskFunctions.runAllTasks(payload.node, functionLibraryProtocolNode) + } + break + case 'Stop All Tasks': + { + functionLibraryTaskFunctions.stopAllTasks(payload.node, functionLibraryProtocolNode) + } + break + case 'Run Session': + { + functionLibrarySessionFunctions.runSession(payload.node, functionLibraryProtocolNode, callBackFunction) + } + break + case 'Stop Session': + { + functionLibrarySessionFunctions.stopSession(payload.node, functionLibraryProtocolNode, callBackFunction) + } + break + } + } +} diff --git a/CanvasApp/EventHandler.js b/CanvasApp/EventHandler.js new file mode 100644 index 000000000..da29745a9 --- /dev/null +++ b/CanvasApp/EventHandler.js @@ -0,0 +1,46 @@ +  +function newEventHandler () { + let thisObject = { + name: undefined, // This is for debugging purposes only. + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent, + finalize: finalize + } + + let eventHandlers = [] // Here we store all the functions we will call when an event is raiseed. + + return thisObject + + function finalize () { + eventHandlers = [] + } + + function listenToEvent (eventType, handler, extraData) { + let eventSubscriptionId = Math.trunc(Math.random() * 1000000) + eventHandlers.push([eventType, handler, extraData, eventSubscriptionId]) + return eventSubscriptionId + } + + function stopListening (pEventSubscriptionId) { + for (let i = 0; i < eventHandlers.length; i++) { + if (pEventSubscriptionId === eventHandlers[i][3]) { + eventHandlers.splice(i, 1) + return + } + } + } + + function raiseEvent (eventType, event) { + for (var i = 0; i < eventHandlers.length; i++) { + /* We will execute all the functions listening to this event type. */ + + if (eventHandlers[i][0] === eventType) { + let handler = eventHandlers[i][1] + let extraData = eventHandlers[i][2] + + handler(event, extraData) + } + } + } +} diff --git a/CanvasApp/FloatingSpace/BusyProgressBar.js b/CanvasApp/FloatingSpace/BusyProgressBar.js new file mode 100644 index 000000000..a3c846b38 --- /dev/null +++ b/CanvasApp/FloatingSpace/BusyProgressBar.js @@ -0,0 +1,72 @@ + +function newBusyProgressBar () { + const MODULE_NAME = 'Circular Progress Bar' + + let thisObject = { + container: undefined, + isDeployed: undefined, + visible: undefined, + physics: physics, + draw: draw, + finalize: finalize, + initialize: initialize + } + + let position + let addRadius + + let eventSubscriptionHeartbeat + + return thisObject + + function finalize () { + thisObject.payload = undefined + thisObject.fitFunction = undefined + } + + function initialize (pPosition) { + position = pPosition + + addRadius = 0 + } + + function physics () { + addRadius = addRadius + 20 + } + + function draw () { + if (thisObject.visible !== true) { return } + + let visiblePosition = { + x: position.x, + y: position.y + } + + visiblePosition = thisObject.fitFunction(visiblePosition) + + const RADIUS = 10 + addRadius + let OPACITY = 1 + + let initialAngle = 0 + let finalAngle = Math.PI * 2 + + browserCanvasContext.beginPath() + + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, RADIUS, initialAngle, finalAngle, false) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)' + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY * 2 + ')' + browserCanvasContext.lineWidth = 15 + browserCanvasContext.setLineDash([3, 4]) + + browserCanvasContext.stroke() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([3, 4]) + browserCanvasContext.stroke() + } +} + diff --git a/CanvasApp/FloatingSpace/CircularMenu.js b/CanvasApp/FloatingSpace/CircularMenu.js new file mode 100644 index 000000000..0e81863c4 --- /dev/null +++ b/CanvasApp/FloatingSpace/CircularMenu.js @@ -0,0 +1,213 @@ + +function newCircularMenu () { + const MODULE_NAME = 'Circular Menu' + + let thisObject = { + container: undefined, + isDeployed: undefined, + internalClick: internalClick, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME, 'Circle') + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let menuItems = [] + + let selfFocusEventSubscriptionId + let selfNotFocuskEventSubscriptionId + + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfNotFocuskEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + menuItem.finalize() + } + + menuItems = undefined + } + + function initialize (menuItemsInitialValues, payload) { +/* Create the array of Menu Items */ + + let iconAndTextArray = [] + let iconOnlyArray + let ringsArray = [[], [], []] + + for (let i = 0; i < menuItemsInitialValues.length; i++) { + let menuItem = newCircularMenuItem() + let menuItemInitialValue = menuItemsInitialValues[i] + + menuItem.action = menuItemInitialValue.action + menuItem.actionFunction = menuItemInitialValue.actionFunction + menuItem.actionStatus = menuItemInitialValue.actionStatus + menuItem.label = menuItemInitialValue.label + menuItem.workingLabel = menuItemInitialValue.workingLabel + menuItem.workDoneLabel = menuItemInitialValue.workDoneLabel + menuItem.workFailedLabel = menuItemInitialValue.workFailedLabel + menuItem.secondaryAction = menuItemInitialValue.secondaryAction + menuItem.secondaryLabel = menuItemInitialValue.secondaryLabel + menuItem.secondaryWorkingLabel = menuItemInitialValue.secondaryWorkingLabel + menuItem.secondaryWorkDoneLabel = menuItemInitialValue.secondaryWorkDoneLabel + menuItem.secondaryWorkFailedLabel = menuItemInitialValue.secondaryWorkFailedLabel + menuItem.secondaryIcon = menuItemInitialValue.secondaryIcon + menuItem.visible = menuItemInitialValue.visible + menuItem.iconPathOn = menuItemInitialValue.iconPathOn + menuItem.iconPathOff = menuItemInitialValue.iconPathOff + menuItem.rawRadius = menuItemInitialValue.rawRadius + menuItem.targetRadius = menuItemInitialValue.label + menuItem.currentRadius = menuItemInitialValue.currentRadius + menuItem.angle = menuItemInitialValue.angle + menuItem.currentStatus = menuItemInitialValue.currentStatus + menuItem.relatedUiObject = menuItemInitialValue.relatedUiObject + menuItem.dontShowAtFullscreen = menuItemInitialValue.dontShowAtFullscreen + menuItem.askConfirmation = menuItemInitialValue.askConfirmation + menuItem.confirmationLabel = menuItemInitialValue.confirmationLabel + menuItem.disableIfPropertyIsDefined = menuItemInitialValue.disableIfPropertyIsDefined + menuItem.propertyToCheckFor = menuItemInitialValue.propertyToCheckFor + menuItem.ring = menuItemInitialValue.ring + menuItem.icons = menuItemInitialValue.icons + + if (menuItem.label === undefined) { + menuItem.type = 'Icon Only' + + if (menuItem.ring === undefined) { + menutItem.ring = 1 + } + iconOnlyArray = ringsArray[menuItem.ring - 1] + iconOnlyArray.push(menuItem) + } else { + menuItem.type = 'Icon & Text' + iconAndTextArray.push(menuItem) + } + + menuItem.initialize(payload) + menuItem.container.connectToParent(thisObject.container, false, false, true, true, false, false, true, true) + menuItems.push(menuItem) + } + + /* There are 3 possible rings of icons, we will go through each of them here. */ + let amplitudeArray = [80, 50, 40] + let initialAngleArray = [220, 205, 200] + for (let j = 1; j < 4; j++) { + let iconOnlyArray = ringsArray[j - 1] + /* Here we calculate the angles for each menu item, and then apply it if it was not previously defined. */ + let amplitude = amplitudeArray[j - 1] + let initialAngle = initialAngleArray[j - 1] + let step = amplitude / (iconOnlyArray.length - 1) + + for (let i = 0; i < iconOnlyArray.length; i++) { + let menuItem = iconOnlyArray[i] + let angle = initialAngle - step * i + if (menuItem.angle === undefined) { + menuItem.angle = angle + } + } + } + /* Text and Icon */ + let amplitude = 140 + let initialAngle = -70 + step = amplitude / 7 + let currentItem = (7 - iconAndTextArray.length) / 2 + 1 + for (let i = 0; i < iconAndTextArray.length; i++) { + let menuItem = iconAndTextArray[i] + let angle = initialAngle + step * (currentItem - 0.5) + currentItem++ + if (menuItem.angle === undefined) { + menuItem.angle = angle + } + } + + iconOnlyArray = undefined + iconAndTextArray = undefined + + /* Listen to events */ + selfFocusEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onFocus', onFocus) + selfNotFocuskEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onNotFocus', onNotFocus) + } + + function getContainer (point) { + let container + + if (thisObject.isDeployed === true) { + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + if (menuItem.visible === true) { + container = menuItem.getContainer(point) + } + if (container !== undefined) { return container } + } + } + } + + function internalClick (action) { + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + if (menuItem.visible === true) { + if (menuItem.nextAction === action) { + menuItem.internalClick() + } + } + } + } + + function physics () { + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + menuItem.physics() + } + } + + function onFocus () { + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + menuItem.targetRadius = menuItem.rawRadius * 1.5 + menuItem.isDeployed = true + } + thisObject.isDeployed = true + } + + function onNotFocus () { + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + menuItem.targetRadius = menuItem.rawRadius * 0 - i * 4 + menuItem.isDeployed = false + } + thisObject.isDeployed = false + } + + function drawBackground (pFloatingObject) { + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + if (menuItem.visible === true) { + menuItem.drawBackground() + } + } + } + + function drawForeground (pFloatingObject) { + for (let i = 0; i < menuItems.length; i++) { + let menuItem = menuItems[i] + if (menuItem.visible === true) { + menuItem.drawForeground() + } + } + } +} diff --git a/CanvasApp/FloatingSpace/CircularMenuItem.js b/CanvasApp/FloatingSpace/CircularMenuItem.js new file mode 100644 index 000000000..b54779f50 --- /dev/null +++ b/CanvasApp/FloatingSpace/CircularMenuItem.js @@ -0,0 +1,437 @@ + +function newCircularMenuItem () { + const MODULE_NAME = 'Circular Menu Iem' + + let thisObject = { + type: undefined, + isDeployed: undefined, + askConfirmation: undefined, + confirmationLabel: undefined, + iconOn: undefined, + iconOff: undefined, + icons: undefined, + currentIcon: undefined, + disableIfPropertyIsDefined: undefined, + propertyToCheckFor: undefined, + action: undefined, + actionFunction: undefined, + actionStatus: undefined, + label: undefined, + workingLabel: undefined, + workDoneLabel: undefined, + workFailedLabel: undefined, + secondaryAction: undefined, + secondaryLabel: undefined, + secondaryWorkingLabel: undefined, + secondaryWorkDoneLabel: undefined, + secondaryWorkFailedLabel: undefined, + secondaryIcon: undefined, + nextAction: undefined, + visible: false, + iconPathOn: undefined, + iconPathOff: undefined, + rawRadius: undefined, + targetRadius: undefined, + currentRadius: undefined, + angle: undefined, + container: undefined, + payload: undefined, + relatedUiObject: undefined, + dontShowAtFullscreen: undefined, + isEnabled: true, + internalClick: internalClick, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = true + thisObject.container.isDraggeable = false + thisObject.container.detectMouseOver = true + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + thisObject.container.frame.width = 0 + thisObject.container.frame.height = 30 + + let isMouseOver = false + + let selfMouseOverEventSubscriptionId + let selfMouseClickEventSubscriptionId + let selfMouseNotOverEventSubscriptionId + + let labelToPrint = '' + let defaultBackgroudColor = UI_COLOR.RED + let backgroundColorToUse = UI_COLOR.RED + let temporaryStatus = 0 + let temporaryStatusCounter = 0 + + const EXTRA_MOUSE_OVER_ICON_SIZE = 6 + + const STATUS_NO_ACTION_TAKEN_YET = 0 + const STATUS_PRIMARY_ACTION_WORKING = -1 + const STATUS_SECONDARY_ACTION_WORKING = -2 + const STATUS_PRIMARY_WORK_DONE = -3 + const STATUS_PRIMARY_WORK_FAILED = -4 + const STATUS_WAITING_CONFIRMATION = -5 + const STATUS_SECONDARY_WORK_DONE = -6 + const STATUS_SECONDARY_WORK_FAILED = -7 + + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(selfMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseNotOverEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.iconOn = undefined + thisObject.iconOff = undefined + thisObject.icons = undefined + thisObject.currentIcon = undefined + thisObject.payload = undefined + thisObject.actionFunction = undefined + thisObject.actionStatus = undefined + thisObject.disableIfPropertyIsDefined = undefined + thisObject.propertyToCheckFor = undefined + } + + function initialize (pPayload) { + thisObject.payload = pPayload + + iconPhysics() + + selfMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + selfMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + + if (thisObject.type === 'Icon & Text') { + thisObject.container.frame.width = 150 + } else { + thisObject.container.frame.width = 50 + } + + thisObject.nextAction = thisObject.action + } + + function getContainer (point) { + if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return } + + let container + if (thisObject.isDeployed === true) { + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + return thisObject.container + } else { + return undefined + } + } + } + + function physics () { + if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return } + + const INCREASE_STEP = 2 + + if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= INCREASE_STEP) { + if (thisObject.currentRadius < thisObject.targetRadius) { + thisObject.currentRadius = thisObject.currentRadius + INCREASE_STEP + } else { + thisObject.currentRadius = thisObject.currentRadius - INCREASE_STEP + } + } + + let radiusGrowthFactor + if (thisObject.type === 'Icon Only') { + switch (thisObject.ring) { + case 1: { + radiusGrowthFactor = 5 + break + } + case 2: { + radiusGrowthFactor = 3.5 + break + } + case 3: { + radiusGrowthFactor = 2 + break + } + } + } else { + radiusGrowthFactor = 4 + } + + thisObject.container.frame.position.x = thisObject.container.frame.radius * radiusGrowthFactor / 7 * Math.cos(toRadians(thisObject.angle)) - thisObject.currentRadius * 1.5 + thisObject.container.frame.position.y = thisObject.container.frame.radius * radiusGrowthFactor / 7 * Math.sin(toRadians(thisObject.angle)) - thisObject.container.frame.height / 2 + + /* Temporary Status impacts on the label to use and the background of that label */ + if (temporaryStatusCounter > 0) { + temporaryStatusCounter-- + } + + if (temporaryStatusCounter === 0) { + temporaryStatus = STATUS_NO_ACTION_TAKEN_YET + labelToPrint = thisObject.label + backgroundColorToUse = defaultBackgroudColor + thisObject.nextAction = thisObject.action + } + + /* Here we will check if we need to monitor a property that influences the status of the Menu Item. */ + if (thisObject.disableIfPropertyIsDefined === true) { + if (thisObject.payload.node[thisObject.propertyToCheckFor] === undefined) { + /* This menu item is enabled. */ + thisObject.isEnabled = true + } else { + /* This menu item is disabled. */ + thisObject.isEnabled = false + } + } + iconPhysics() + } + + function iconPhysics () { + if ( + ( + temporaryStatus === STATUS_PRIMARY_WORK_DONE || + temporaryStatus === STATUS_SECONDARY_ACTION_WORKING || + temporaryStatus === STATUS_SECONDARY_WORK_DONE || + temporaryStatus === STATUS_SECONDARY_WORK_FAILED + ) && thisObject.secondaryAction !== undefined + ) { + thisObject.iconOn = canvas.designSpace.iconCollection.get(thisObject.secondaryIcon) + thisObject.iconOff = canvas.designSpace.iconCollection.get(thisObject.secondaryIcon) + } else { + if (thisObject.relatedUiObject !== undefined) { + thisObject.iconOn = canvas.designSpace.iconByUiObjectType.get(thisObject.relatedUiObject) + thisObject.iconOff = canvas.designSpace.iconByUiObjectType.get(thisObject.relatedUiObject) + } else { + if (thisObject.iconPathOn !== undefined && thisObject.iconPathOff !== undefined) { + thisObject.iconOn = canvas.designSpace.iconCollection.get(thisObject.iconPathOn) + thisObject.iconOff = canvas.designSpace.iconCollection.get(thisObject.iconPathOff) + } else { + thisObject.iconOn = canvas.designSpace.iconCollection.get(thisObject.icons[thisObject.actionStatus()]) + thisObject.iconOff = canvas.designSpace.iconCollection.get(thisObject.icons[thisObject.actionStatus()]) + } + } + } + + /* Current Status might be linked to some other object status */ + + if (thisObject.actionStatus !== undefined) { + thisObject.currentStatus = thisObject.actionStatus() + } + + /* Current Status sets the icon to be used */ + + if (thisObject.currentStatus === true) { + thisObject.icon = thisObject.iconOn + } else { + thisObject.icon = thisObject.iconOff + } + } + + function onMouseOver (point) { + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + isMouseOver = true + } else { + isMouseOver = false + } + } + + function onMouseNotOver (point) { + isMouseOver = false + } + + function internalClick () { + onMouseClick() + } + + function onMouseClick () { + if (thisObject.isEnabled === false) { return } + + if (thisObject.label === undefined) { + /* This is what we have in the case of menu items that are only Icons. In this situation there is no complex logic, just execute the specified action. */ + thisObject.actionFunction(thisObject.payload, thisObject.action, thisObject.relatedUiObject) + return + } + + if (thisObject.askConfirmation !== true) { /* No confirmation is needed */ + if (temporaryStatus === STATUS_NO_ACTION_TAKEN_YET || temporaryStatus === STATUS_PRIMARY_WORK_DONE) { + executeAction() + } // Any click out of those states is ignored + } else { + /* Confirmation is needed */ + + /* The first click ask for confirmation. */ + if (temporaryStatus === STATUS_NO_ACTION_TAKEN_YET) { + setStatus(thisObject.confirmationLabel, UI_COLOR.GOLDEN_ORANGE, 250, STATUS_WAITING_CONFIRMATION) + return + } + /* A Click during confirmation executes the pre-defined action. */ + if (temporaryStatus === STATUS_WAITING_CONFIRMATION || temporaryStatus === STATUS_PRIMARY_WORK_DONE) { + executeAction() + return + } + } + + function executeAction () { + if (temporaryStatus === STATUS_NO_ACTION_TAKEN_YET || temporaryStatus === STATUS_WAITING_CONFIRMATION) { + /* We need to execute the main Action */ + /* If there is a working label defined, we use it here. */ + if (thisObject.workingLabel !== undefined) { + setStatus(thisObject.workingLabel, UI_COLOR.GREY, undefined, STATUS_PRIMARY_ACTION_WORKING) // Status will not expire, will only change with a callback. Mouse Clicks will be ignored. + } + + /* Execute the action and wait for callbacks to update our statuus. */ + thisObject.actionFunction(thisObject.payload, thisObject.action, thisObject.relatedUiObject, onPrimaryCallBack) + return + } + if (temporaryStatus === STATUS_PRIMARY_WORK_DONE && thisObject.secondaryAction !== undefined) { + /* We need to execute the secondary action. */ + if (thisObject.secondaryWorkingLabel !== undefined) { + setStatus(thisObject.secondaryWorkingLabel, UI_COLOR.GREY, undefined, STATUS_SECONDARY_ACTION_WORKING) // Status will not expire, will only change with a callback. Mouse Clicks will be ignored. + } + + /* Execute the action and wait for callbacks to update our statuus. */ + thisObject.actionFunction(thisObject.payload, thisObject.secondaryAction, thisObject.relatedUiObject, onSecondaryCallBack) + return + } + + function onPrimaryCallBack (err, event) { + /* While the primary action is being executed some event might have happen. Following we process the ones we can */ + + if (event !== undefined) { + if (event.type === 'Secondary Action Already Executed') { + setStatus(thisObject.secondaryWorkDoneLabel, UI_COLOR.PATINATED_TURQUOISE, 100, STATUS_SECONDARY_WORK_DONE) + return + } + } + + /* If there is a secondary action we will act different that if there is not */ + if (thisObject.secondaryAction === undefined) { // This means there are no more possible actions. + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + if (thisObject.workDoneLabel !== undefined) { + setStatus(thisObject.workDoneLabel, UI_COLOR.PATINATED_TURQUOISE, 100, STATUS_PRIMARY_WORK_DONE) + } + } else { + if (thisObject.workFailedLabel != undefined) { + setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 100, STATUS_PRIMARY_WORK_FAILED) + } + } + } else { + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + if (thisObject.workDoneLabel !== undefined) { + thisObject.nextAction = thisObject.secondaryAction + setStatus(thisObject.secondaryLabel, defaultBackgroudColor, undefined, STATUS_PRIMARY_WORK_DONE) + } + } else { + if (thisObject.workFailedLabel != undefined) { + setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 100, STATUS_PRIMARY_WORK_FAILED) + } + } + } + } + function onSecondaryCallBack (err) { + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + if (thisObject.secondaryWorkDoneLabel !== undefined) { + setStatus(thisObject.secondaryWorkDoneLabel, UI_COLOR.PATINATED_TURQUOISE, 100, STATUS_SECONDARY_WORK_DONE) + } + } else { + if (thisObject.secondaryWorkFailedLabel != undefined) { + setStatus(thisObject.secondaryWorkFailedLabel, UI_COLOR.TITANIUM_YELLOW, 100, STATUS_SECONDARY_WORK_FAILED) + } + } + } + } + } + + function setStatus (text, backgroundColor, waitingCycles, newStatus) { + labelToPrint = text + backgroundColorToUse = backgroundColor + temporaryStatus = newStatus + temporaryStatusCounter = newStatus // This will often put this into negatives numbers, which will disable the counting back and automatic reseting. + if (waitingCycles !== undefined) { // This will override the often negative value with a positive one that will tend to zero onto the default state. + temporaryStatusCounter = waitingCycles + } + } + + function drawBackground () { + if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return } + + if (thisObject.container.frame.position.x > 0 && thisObject.isDeployed === true && thisObject.currentRadius >= thisObject.targetRadius) { + if (thisObject.type === 'Icon & Text') { + let backgroundColor = backgroundColorToUse + if (thisObject.isEnabled === false) { + backgroundColor = UI_COLOR.GREY + } + let params = { + cornerRadius: 3, + lineWidth: 0.1, + container: thisObject.container, + borderColor: UI_COLOR.DARK, + backgroundColor: backgroundColor, + castShadow: false, + xOffset: 40 + } + + if (isMouseOver === true) { + params.opacity = 1 + } else { + params.opacity = 0.8 + } + + roundedCornersBackground(params) + } + } + } + + function drawForeground () { + if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return } + + let menuPosition = { + x: thisObject.currentRadius * 1.5, + y: thisObject.container.frame.height / 2 + } + + menuPosition = thisObject.container.frame.frameThisPoint(menuPosition) + + /* Menu Item */ + + let iconSize + if (isMouseOver === true) { + iconSize = thisObject.currentRadius + EXTRA_MOUSE_OVER_ICON_SIZE + } else { + iconSize = thisObject.currentRadius + } + + if (thisObject.icon === undefined) { return } + if (thisObject.icon.canDrawIcon === true && thisObject.currentRadius > 1 && thisObject.isDeployed === true) { + browserCanvasContext.drawImage(thisObject.icon, menuPosition.x - iconSize, menuPosition.y - iconSize, iconSize * 2, iconSize * 2) + + /* Menu Label */ + + if (thisObject.type === 'Icon & Text') { + let labelPoint + let fontSize = 10 + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + + if (thisObject.currentRadius >= thisObject.targetRadius) { + labelPoint = { + x: menuPosition.x + thisObject.currentRadius + 20, + y: menuPosition.y + fontSize * FONT_ASPECT_RATIO + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fillText(labelToPrint, labelPoint.x, labelPoint.y) + } + } + } + } +} diff --git a/CanvasApp/FloatingSpace/CircularProgressBar.js b/CanvasApp/FloatingSpace/CircularProgressBar.js new file mode 100644 index 000000000..0e59f3cf5 --- /dev/null +++ b/CanvasApp/FloatingSpace/CircularProgressBar.js @@ -0,0 +1,119 @@ + +function newCircularProgressBar () { + const MODULE_NAME = 'Circular Progress Bar' + + let thisObject = { + container: undefined, + isDeployed: undefined, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME, 'Circle') + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let opacityCounters = [] + + let eventSubscriptionIdHeartbeat + + return thisObject + + function finalize () { + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + systemEventHandler.stopListening(key, eventSubscriptionIdHeartbeat) + + thisObject.container = undefined + thisObject.payload = undefined + thisObject.fitFunction = undefined + } + + function initialize (payload) { + thisObject.payload = payload + + for (let i = 0; i < 60; i++) { + opacityCounters.push(0) + } + + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + systemEventHandler.listenToEvent(key, 'Heartbeat', undefined, key, onResponse, onHeartBeat) + + function onResponse (message) { + eventSubscriptionIdHeartbeat = message.eventSubscriptionId + } + } + + function onHeartBeat (message) { + if (thisObject.payload === undefined) { return } + if (thisObject.payload.uiObject === undefined) { return } + opacityCounters[message.event.seconds] = 2000 + thisObject.payload.uiObject.setValue(message.event.processingDate) + } + + function getContainer (point) { + let container + + for (let i = 0; i < menuItems.length; i++) { + let menutItem = menuItems[i] + container = menutItem.getContainer(point) + if (container !== undefined) { return container } + } + } + + function physics () { + for (let i = 0; i < 60; i++) { + opacityCounters[i] = opacityCounters[i] - 2 + + if (opacityCounters[i] < 0) { + opacityCounters[i] = 0 + } + } + } + + function drawForeground (pFloatingObject) { + + } + + function drawBackground (pFloatingObject) { + const VISIBLE_RADIUS = thisObject.container.frame.radius * 2 + + let visiblePosition = { + x: thisObject.container.frame.position.x, + y: thisObject.container.frame.position.y + } + + visiblePosition = thisObject.container.frame.frameThisPoint(visiblePosition) + visiblePosition = thisObject.fitFunction(visiblePosition) + + for (let i = 0; i < 60; i++) { + let OPACITY = opacityCounters[i] / 1000 + + if (OPACITY === 0) { continue } + + let initialAngle = Math.PI * 2 / 60 * i - Math.PI / 2 + let finalAngle = Math.PI * 2 / 60 * (i + 1) - Math.PI / 2 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, initialAngle, finalAngle, false) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY * 2 + ')' + browserCanvasContext.lineWidth = 6 + opacityCounters[i] / 100 / 2 + browserCanvasContext.setLineDash([3, 4]) + browserCanvasContext.stroke() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 0 + opacityCounters[i] / 100 / 2 + browserCanvasContext.setLineDash([3, 4]) + browserCanvasContext.stroke() + } + } +} diff --git a/CanvasApp/FloatingSpace/CodeEditor.js b/CanvasApp/FloatingSpace/CodeEditor.js new file mode 100644 index 000000000..cffba932a --- /dev/null +++ b/CanvasApp/FloatingSpace/CodeEditor.js @@ -0,0 +1,215 @@ + +function newCodeEditor () { + const MODULE_NAME = 'Code Editor' + + let thisObject = { + isVisibleFunction: undefined, + iconOK: undefined, + iconNOT_OK: undefined, + visible: false, + imagePathOK: undefined, + imagePathNOT_OK: undefined, + rawRadius: undefined, + targetRadius: undefined, + currentRadius: undefined, + container: undefined, + payload: undefined, + deactivate: deactivate, + activate: activate, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.detectMouseOver = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let isMouseOver = false + + return thisObject + + function finalize () { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.iconOK = undefined + thisObject.iconNOT_OK = undefined + thisObject.payload = undefined + thisObject.isVisibleFunction = undefined + } + + function initialize () { + /* Load Images */ + + thisObject.iconOK = new Image() + thisObject.iconOK.onload = onImageLoad + + function onImageLoad () { + thisObject.iconNOT_OK = new Image() + thisObject.iconNOT_OK.onload = onImageLoad + + function onImageLoad () { + thisObject.canDrawIcon = true + } + thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK + } + // thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK + // thisObject.icon = thisObject.iconOK // The default value is ON. + } + + function deactivate () { + if (thisObject.visible === true) { + thisObject.visible = false + let textArea = document.getElementById('textArea') + textArea.style.display = 'none' + thisObject.payload.node.code = textArea.value + } + } + + function activate (payload) { + thisObject.visible = true + thisObject.payload = payload + thisObject.rawRadius = 8 + thisObject.targetRadius = thisObject.container.frame.radius + thisObject.currentRadius = 0 + thisObject.payload.uiObject.setErrorMessage('', 0) + + let textArea = document.getElementById('textArea') + textArea.value = payload.node.code + textArea.style = 'resize: none;' + + ' border: none;' + + ' outline: none;' + + 'box-shadow: none;' + + 'overflow:hidden;' + + 'font-family: ' + UI_FONT.PRIMARY + ';' + + 'font-size: 12px;' + + 'background-color: rgb(' + UI_COLOR.RUSTED_RED + ');' + + 'color:rgb(255, 255, 255);' + + 'width: ' + thisObject.container.frame.width + 'px;' + + 'height: ' + thisObject.container.frame.height + 'px' + textArea.style.display = 'block' + textArea.focus() + } + + function getContainer (point) { + let container + if (thisObject.visible === true) { + if (point.x === VERY_LARGE_NUMBER) { + /* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that + anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is + triggered. We compensate recognizing this coordinate and returning our container. */ + return thisObject.container + } + + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + return thisObject.container + } else { + return undefined + } + } + } + + function physics () { + if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) { + if (thisObject.currentRadius < thisObject.targetRadius) { + thisObject.currentRadius = thisObject.currentRadius + 0.5 + } else { + thisObject.currentRadius = thisObject.currentRadius - 0.5 + } + } + + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * 2 + thisObject.container.frame.height = thisObject.container.frame.radius * 1 * 2 + + let textAreaPosition = { + x: 0 - thisObject.container.frame.width / 2, + y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN + } + + textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition) + if (thisObject.visible === true) { + let checkPosition = { + x: textAreaPosition.x, + y: textAreaPosition.y - CURRENT_TOP_MARGIN + } + if (thisObject.isVisibleFunction(checkPosition) === false) { + deactivate() + } + } + if (thisObject.visible === true) { + let textAreaDiv = document.getElementById('textAreaDiv') + textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; ' + } + } + + function drawBackground () { + if (thisObject.visible === true) { + let position = { + x: 0, + y: 0 + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius * 2 + + if (radius > 0.5) { + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')' + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + 1 + ')' + browserCanvasContext.fill() + } + } + } + function drawForeground () { + let iconPosition = { + x: 0, + y: thisObject.currentRadius * 1.5 + } + + iconPosition = thisObject.container.frame.frameThisPoint(iconPosition) + + /* Menu Item */ + + if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) { + browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2) + + /* Menu Label */ + + let label = 'Code looks good!' + let labelPoint + let fontSize = 10 + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + + if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) { + labelPoint = { + x: iconPosition.x + thisObject.currentRadius + 10, + y: iconPosition.y + fontSize * FONT_ASPECT_RATIO + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } +} diff --git a/CanvasApp/FloatingSpace/ConditionEditor.js b/CanvasApp/FloatingSpace/ConditionEditor.js new file mode 100644 index 000000000..373c5def7 --- /dev/null +++ b/CanvasApp/FloatingSpace/ConditionEditor.js @@ -0,0 +1,215 @@ + +function newConditionEditor () { + const MODULE_NAME = 'Condition Editor' + + let thisObject = { + isVisibleFunction: undefined, + iconOK: undefined, + iconNOT_OK: undefined, + visible: false, + imagePathOK: undefined, + imagePathNOT_OK: undefined, + rawRadius: undefined, + targetRadius: undefined, + currentRadius: undefined, + container: undefined, + payload: undefined, + deactivate: deactivate, + activate: activate, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.detectMouseOver = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let isMouseOver = false + + return thisObject + + function finalize () { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.iconOK = undefined + thisObject.iconNOT_OK = undefined + thisObject.payload = undefined + thisObject.isVisibleFunction = undefined + } + + function initialize () { + /* Load Images */ + + thisObject.iconOK = new Image() + thisObject.iconOK.onload = onImageLoad + + function onImageLoad () { + thisObject.iconNOT_OK = new Image() + thisObject.iconNOT_OK.onload = onImageLoad + + function onImageLoad () { + thisObject.canDrawIcon = true + } + thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK + } + // thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK + // thisObject.icon = thisObject.iconOK // The default value is ON. + } + + function deactivate () { + if (thisObject.visible === true) { + thisObject.visible = false + let textArea = document.getElementById('textArea') + textArea.style.display = 'none' + thisObject.payload.node.code = textArea.value + } + } + + function activate (payload) { + thisObject.visible = true + thisObject.payload = payload + thisObject.rawRadius = 8 + thisObject.targetRadius = thisObject.container.frame.radius + thisObject.currentRadius = 0 + thisObject.payload.uiObject.setErrorMessage('', 0) + + let textArea = document.getElementById('textArea') + textArea.value = payload.node.code + textArea.style = 'resize: none;' + + ' border: none;' + + ' outline: none;' + + 'box-shadow: none;' + + 'overflow:hidden;' + + 'font-family: ' + UI_FONT.PRIMARY + ';' + + 'font-size: 12px;' + + 'background-color: rgb(' + UI_COLOR.GREEN + ');' + + 'color:rgb(255, 255, 255);' + + 'width: ' + thisObject.container.frame.width + 'px;' + + 'height: ' + thisObject.container.frame.height + 'px' + textArea.style.display = 'block' + textArea.focus() + } + + function getContainer (point) { + let container + if (thisObject.visible === true) { + if (point.x === VERY_LARGE_NUMBER) { + /* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that + anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is + triggered. We compensate recognizing this coordinate and returning our container. */ + return thisObject.container + } + + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + return thisObject.container + } else { + return undefined + } + } + } + + function physics () { + if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) { + if (thisObject.currentRadius < thisObject.targetRadius) { + thisObject.currentRadius = thisObject.currentRadius + 0.5 + } else { + thisObject.currentRadius = thisObject.currentRadius - 0.5 + } + } + + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * 2 + thisObject.container.frame.height = thisObject.container.frame.radius * 1 * 2 + + let textAreaPosition = { + x: 0 - thisObject.container.frame.width / 2, + y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN + } + + textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition) + if (thisObject.visible === true) { + let checkPosition = { + x: textAreaPosition.x, + y: textAreaPosition.y - CURRENT_TOP_MARGIN + } + if (thisObject.isVisibleFunction(checkPosition) === false) { + deactivate() + } + } + if (thisObject.visible === true) { + let textAreaDiv = document.getElementById('textAreaDiv') + textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; ' + } + } + + function drawBackground () { + if (thisObject.visible === true) { + let position = { + x: 0, + y: 0 + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius * 2 + + if (radius > 0.5) { + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')' + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + 1 + ')' + browserCanvasContext.fill() + } + } + } + function drawForeground () { + let iconPosition = { + x: 0, + y: thisObject.currentRadius * 1.5 + } + + iconPosition = thisObject.container.frame.frameThisPoint(iconPosition) + + /* Menu Item */ + + if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) { + browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2) + + /* Menu Label */ + + let label = 'Code looks good!' + let labelPoint + let fontSize = 10 + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + + if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) { + labelPoint = { + x: iconPosition.x + thisObject.currentRadius + 10, + y: iconPosition.y + fontSize * FONT_ASPECT_RATIO + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } +} diff --git a/CanvasApp/FloatingSpace/ConfigEditor.js b/CanvasApp/FloatingSpace/ConfigEditor.js new file mode 100644 index 000000000..83048c84a --- /dev/null +++ b/CanvasApp/FloatingSpace/ConfigEditor.js @@ -0,0 +1,222 @@ + +function newConfigEditor () { + const MODULE_NAME = 'Config Editor' + + let thisObject = { + isVisibleFunction: undefined, + iconOK: undefined, + iconNOT_OK: undefined, + visible: false, + imagePathOK: undefined, + imagePathNOT_OK: undefined, + rawRadius: undefined, + targetRadius: undefined, + currentRadius: undefined, + container: undefined, + payload: undefined, + deactivate: deactivate, + activate: activate, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.detectMouseOver = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let isMouseOver = false + let SIZE_FACTOR = 0.6 + + return thisObject + + function finalize () { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.iconOK = undefined + thisObject.iconNOT_OK = undefined + thisObject.payload = undefined + thisObject.isVisibleFunction = undefined + } + + function initialize () { + /* Load Images */ + + thisObject.iconOK = new Image() + thisObject.iconOK.onload = onImageLoad + + function onImageLoad () { + thisObject.iconNOT_OK = new Image() + thisObject.iconNOT_OK.onload = onImageLoad + + function onImageLoad () { + thisObject.canDrawIcon = true + } + thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK + } + // thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK + // thisObject.icon = thisObject.iconOK // The default value is ON. + } + + function deactivate () { + if (thisObject.visible === true) { + thisObject.visible = false + let textArea = document.getElementById('textArea') + textArea.style.display = 'none' + thisObject.payload.node.code = textArea.value + + try { + let code = JSON.parse(thisObject.payload.node.code) + } catch (err) { + thisObject.payload.uiObject.setErrorMessage(err.message, 10) + } + } + } + + function activate (payload) { + thisObject.visible = true + thisObject.payload = payload + thisObject.rawRadius = 8 + thisObject.targetRadius = thisObject.container.frame.radius + thisObject.currentRadius = 0 + thisObject.payload.uiObject.setErrorMessage('', 0) + + let textArea = document.getElementById('textArea') + textArea.value = payload.node.code + textArea.style = 'resize: none;' + + ' border: none;' + + ' outline: none;' + + 'box-shadow: none;' + + 'overflow:hidden;' + + 'font-family: ' + UI_FONT.PRIMARY + ';' + + 'font-size: 12px;' + + 'background-color: rgb(' + UI_COLOR.DARK_TURQUOISE + ');' + + 'color:rgb(255, 255, 255);' + + 'width: ' + thisObject.container.frame.width + 'px;' + + 'height: ' + thisObject.container.frame.height + 'px' + textArea.style.display = 'block' + textArea.focus() + } + + function getContainer (point) { + let container + if (thisObject.visible === true) { + if (point.x === VERY_LARGE_NUMBER) { + /* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that + anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is + triggered. We compensate recognizing this coordinate and returning our container. */ + return thisObject.container + } + + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + return thisObject.container + } else { + return undefined + } + } + } + + function physics () { + if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) { + if (thisObject.currentRadius < thisObject.targetRadius) { + thisObject.currentRadius = thisObject.currentRadius + 0.5 + } else { + thisObject.currentRadius = thisObject.currentRadius - 0.5 + } + } + + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * SIZE_FACTOR + thisObject.container.frame.height = thisObject.container.frame.radius * 1 * SIZE_FACTOR + + let textAreaPosition = { + x: 0 - thisObject.container.frame.width / 2, + y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN + } + + textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition) + if (thisObject.visible === true) { + let checkPosition = { + x: textAreaPosition.x, + y: textAreaPosition.y - CURRENT_TOP_MARGIN + } + if (thisObject.isVisibleFunction(checkPosition) === false) { + deactivate() + } + } + if (thisObject.visible === true) { + let textAreaDiv = document.getElementById('textAreaDiv') + textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; ' + } + } + + function drawBackground () { + if (thisObject.visible === true) { + let position = { + x: 0, + y: 0 + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius * SIZE_FACTOR + + if (radius > 0.5) { + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')' + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + 1 + ')' + browserCanvasContext.fill() + } + } + } + function drawForeground () { + let iconPosition = { + x: 0, + y: thisObject.currentRadius * 1.5 + } + + iconPosition = thisObject.container.frame.frameThisPoint(iconPosition) + + /* Menu Item */ + + if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) { + browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2) + + /* Menu Label */ + + let label = 'Code looks good!' + let labelPoint + let fontSize = 10 + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + + if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) { + labelPoint = { + x: iconPosition.x + thisObject.currentRadius + 10, + y: iconPosition.y + fontSize * FONT_ASPECT_RATIO + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } +} diff --git a/CanvasApp/FloatingSpace/FloatingLayer.js b/CanvasApp/FloatingSpace/FloatingLayer.js new file mode 100644 index 000000000..621404fbb --- /dev/null +++ b/CanvasApp/FloatingSpace/FloatingLayer.js @@ -0,0 +1,733 @@ +/* + This module represent one layer of many possible layers that could be at the Floating Space. Objects in the same layer are subject to + the same physics, and by being at the same level, they interact with each other. For example, they can bounce when one hit the other. + This module posesses its own physics engine. There are different types of floating objects, but this layer takes care of each of them. + + Floating Objects Types: + + 1. Profile Balls: These are small balls with a profile picture, and two levels, one above and one below the ball. They are usually used + to specify the location of a bot instance. + + 2. Notes: Notes are a rectangular area where some text is posted. It has a subject and a body. + + 3. UI Objects: These are small balls that represent parts of an strategy. + */ + +function newFloatingLayer () { + const MODULE_NAME = 'Floating Layer' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + addFloatingObject: addFloatingObject, + removeFloatingObject: removeFloatingObject, + getFloatingObject: getFloatingObject, + physics: physics, + changeTargetRepulsion: changeTargetRepulsion, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + /* + When objects are added to this layer they are added to the invisibleFloatingObjects array and they stay there because they are invisible. + Visibility of objects is not decided at this layer, but outside. The ones who decides which object is visible or not are plotters, because + they know the position of objects and the coordinate system which these positions belong to. + + To save resources, the physics engine only apply its calculations to visible floating objects. + */ + + let invisibleFloatingObjects = [] + + /* + Floating objects are accesible to plotters because they carry a "payload" object that was creted by them. This object includes the position + and visible property. When visible turns to true, this layer module will move the reference of the floating object from the invisible array + to the visibleFloatingObjects array. If the plotter decides that the object is to far from the what the user is seeing, it can eventually + turn the visible property off again, forcing the layer to remove the object from the visibleFloatingObjects and place it again at the invisible + array. + */ + + let visibleFloatingObjects = [] + + let maxTargetRepulsionForce = 0.001 + let currentHandle = 0 + + return thisObject + + function finalize () { + invisibleFloatingObjects = [] + visibleFloatingObjects = [] + } + + function initialize () { + + /* We dont need to initialize anything right now. */ + } + + function getContainer (point) { + let container + + for (let i = 0; i < visibleFloatingObjects.length; i++) { + let floatingObject = visibleFloatingObjects[i] + container = floatingObject.getContainer(point) + if (container !== undefined) { return container } + } + + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[i] + container = floatingObject.getContainer(point) + if (container !== undefined) { return container } + } + + return container + } + + function addFloatingObject (pFloatingObject) { + currentHandle++ + pFloatingObject.handle = currentHandle // Math.floor((Math.random() * 10000000) + 1); + visibleFloatingObjects.push(pFloatingObject) + } + + function removeFloatingObject (pFloatingObjectHandle) { + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[i] + + if (floatingObject.handle === pFloatingObjectHandle) { + invisibleFloatingObjects.splice(i, 1) // Delete item from array. + 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. + return + } + } + } + + function getFloatingObject (pFloatingObjectHandle, pFloatingObjectIndex) { + /* + There are two ways to look for a floating object: by handle or by index. The search by handle is done on the visible and invisible array. + The search by index is done only on the visible ones. The search by index is usually needed when mouse events occurs. In those cases + only objects visible to the end usser matters. + */ + + try { + 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 + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFloatingObject -> err= ' + err.stack) } + } + } + + function draw () { + drawInvisibleObjects() + drawVisibleObjects() + } + + function drawInvisibleObjects () { + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[i] + floatingObject.drawBackground() + } + + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[i] + floatingObject.drawMiddleground() + } + + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[invisibleFloatingObjects.length - i - 1] + floatingObject.drawForeground() + } + } + + function drawVisibleObjects () { + 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[i] + floatingObject.drawMiddleground() + } + + for (let i = 0; i < visibleFloatingObjects.length; i++) { + let floatingObject = visibleFloatingObjects[visibleFloatingObjects.length - i - 1] + floatingObject.drawForeground() + } + + /* Invisible objects on focus (freezed) should still have some priority */ + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[i] + floatingObject.drawOnFocus() + } + + for (let i = 0; i < visibleFloatingObjects.length; i++) { + let floatingObject = visibleFloatingObjects[i] + floatingObject.drawOnFocus() + } + } + + function makeVisible () { + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[i] + + if (floatingObject.isFrozen === false && floatingObject.isParentCollapsed === false) { + visibleFloatingObjects.push(floatingObject) + invisibleFloatingObjects.splice(i, 1) // Delete item from array. + makeVisible() + return // Only one at the time. + } + } + } + + function makeInvisible () { + for (let i = 0; i < visibleFloatingObjects.length; i++) { + let floatingObject = visibleFloatingObjects[i] + + if (floatingObject.isFrozen === true || floatingObject.isParentCollapsed === true) { + invisibleFloatingObjects.push(floatingObject) + visibleFloatingObjects.splice(i, 1) // Delete item from array. + makeInvisible() + return // Only one at the time. + } + } + } + + /******************************************/ + /* */ + /* Physics Engine Follows */ + /* */ + /******************************************/ + + function physics () { + /* + The Physics engine is hooked at the animation loop, so it executes very very often. According to this Physics engine, each + floating object has a position and a speed. In fact, each floating object has two positions at the same time: + + 1. The current position: is the position of the ball, floating around. + 2. The anchor position: is the position of where the ball is achored on the plot. This one is inside the payload object controlled by the + plotter. + + The anchor position influences the current position, since the Physics engine will apply a gravity force to the ball's position in order + to make it gravitate towards the anchor position. At the same time there will be a repulsion force that will prevent the ball reaching + the anchor position, so that the anchor point keeps visible to the user. + + */ + + try { + makeVisible() + makeInvisible() + applyPhysics() + + function applyPhysics () { + /* This function makes all the calculations to apply phisycs on all visible floatingObjects in this layer. */ + + try { + for (let i = 0; i < invisibleFloatingObjects.length; i++) { + let floatingObject = invisibleFloatingObjects[i] + floatingObject.physics() + } + + for (let i = 0; i < visibleFloatingObjects.length; i++) { + let floatingObject = visibleFloatingObjects[i] + floatingObject.physics() + + /* From here on, only if they are not too far. */ + if (canvas.floatingSpace.isItFar(floatingObject.payload)) { continue } + + if (floatingObject.positionLocked === false) { + floatingObject.container.frame.position.x = floatingObject.container.frame.position.x + floatingObject.currentSpeed.x + floatingObject.container.frame.position.y = floatingObject.container.frame.position.y + floatingObject.currentSpeed.y + } + + if (floatingObject.friction < floatingObject.targetFriction) { + floatingObject.friction = floatingObject.friction + 0.00001 + } else { + floatingObject.friction = floatingObject.friction - 0.00001 + } + + floatingObject.currentSpeed.x = floatingObject.currentSpeed.x * floatingObject.friction // Desaceleration factor. + floatingObject.currentSpeed.y = floatingObject.currentSpeed.y * floatingObject.friction // Desaceleration factor. + + let payload = { + position: undefined, + visible: false + } + + switch (floatingObject.type) { + case 'UI Object': { + payload.targetPosition = floatingObject.payload.targetPosition + payload.visible = floatingObject.payload.visible + break + } + default: { + break + } + } + + if (floatingObject.container.frame.position.x < payload.targetPosition.x) { + floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + 0.005 + } else { + floatingObject.currentSpeed.x = floatingObject.currentSpeed.x - 0.005 + } + + if (floatingObject.container.frame.position.y < payload.targetPosition.y) { + floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + 0.005 + } else { + floatingObject.currentSpeed.y = floatingObject.currentSpeed.y - 0.005 + } + + const MAX_SPEED = 10 + + 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 + } + + // We let the Floating Object animate the physics loops by itself. + checkBoundaries(floatingObject) + + /* 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 */ + + repulsionForceBetweenFloatingObjects(i) + + targetRepulsionForce(i) + + gravityForce(floatingObject, payload) + + proximityBetweenFloatingObjects(i) + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] physics -> applyPhysics -> err= ' + err.stack) } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] physics -> err= ' + err.stack) } + } + } + + function checkBoundaries (floatingObject) { + if (floatingObject.container.frame.position.x - floatingObject.container.frame.radius < 0) { + floatingObject.container.frame.position.x = floatingObject.container.frame.radius + floatingObject.currentSpeed.x = -floatingObject.currentSpeed.x + } + + if (floatingObject.container.frame.position.x + floatingObject.container.frame.radius > canvas.floatingSpace.container.frame.width) { + floatingObject.container.frame.position.x = canvas.floatingSpace.container.frame.width - floatingObject.container.frame.radius + floatingObject.currentSpeed.x = -floatingObject.currentSpeed.x + } + + if (floatingObject.container.frame.position.y - floatingObject.container.frame.radius < 0) { + floatingObject.container.frame.position.y = floatingObject.container.frame.radius + floatingObject.currentSpeed.y = -floatingObject.currentSpeed.y + } + + if (floatingObject.container.frame.position.y + floatingObject.container.frame.radius > canvas.floatingSpace.container.frame.height) { + floatingObject.container.frame.position.y = canvas.floatingSpace.container.frame.height - floatingObject.container.frame.radius + floatingObject.currentSpeed.y = -floatingObject.currentSpeed.y + } + } + + function gravityForce (floatingObject, payload) { + try { + /* 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 = 0.00001 + const minForce = 0.01 + + let d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject.container.frame.position.y, 2)) // ... we calculate the distance ... + + let 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 + } + + let pos1 = { + x: floatingObject.container.frame.position.x, + y: floatingObject.container.frame.position.y + } + + let pos2 = { + x: payload.targetPosition.x, + y: payload.targetPosition.y + } + + let posDiff = { // Next we need the vector resulting from the 2 positions. + x: pos2.x - pos1.x, + y: pos2.y - pos1.y + } + + if (d !== 0) { + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + x: posDiff.x / d, + y: posDiff.y / d + } + + let forceVector = { + x: unitVector.x * force, + y: unitVector.y * force + } + + /* We add the force vector to the speed vector */ + if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) { + floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + forceVector.x + floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + forceVector.y + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] gravityForce -> err= ' + err.stack) } + } + } + + function repulsionForceBetweenFloatingObjects (currentFloatingObject) { + try { + /* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */ + + const coulomb = 2 + + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] + + for (let i = 0; i < visibleFloatingObjects.length; i++) { + // The force to be applied is considering all other floatingObjects... + + if (i !== currentFloatingObject) { + // ... except for the current one. + + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + + let 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 + } + + let pos1 = { + x: floatingObject1.container.frame.position.x, + y: floatingObject1.container.frame.position.y + } + + let pos2 = { + x: floatingObject2.container.frame.position.x, + y: floatingObject2.container.frame.position.y + } + + let posDiff = { // Next we need the vector resulting from the 2 positions. + x: pos2.x - pos1.x, + y: pos2.y - pos1.y + } + + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + x: posDiff.x / d, + y: posDiff.y / d + } + + let forceVector = { + x: unitVector.x * force, + y: unitVector.y * force + } + + /* We substract the force vector to the speed vector of the current floatingObject */ + if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) { + floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x + floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] repulsionForceBetweenFloatingObjects -> err= ' + err.stack) } + } + } + + function proximityBetweenFloatingObjects (currentFloatingObject) { + try { + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] + floatingObject1.nearbyFloatingObjects = [] + for (let i = 0; i < visibleFloatingObjects.length; i++) { + if (i !== currentFloatingObject) { + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + + if (floatingObject1.nearbyFloatingObjects.length === 0) { + floatingObject1.nearbyFloatingObjects.push([d, floatingObject2]) + } else { + let recordAdded = false + for (let j = 0; j < floatingObject1.nearbyFloatingObjects.length; j++) { + let recordedDistance = floatingObject1.nearbyFloatingObjects[j][0] + if (d < recordedDistance) { + floatingObject1.nearbyFloatingObjects.splice(j, 0, [d, floatingObject2]) + recordAdded = true + break + } + } + if (recordAdded === false) { + floatingObject1.nearbyFloatingObjects.push([d, floatingObject2]) + } + } + floatingObject1.nearbyFloatingObjects.splice(10, floatingObject1.nearbyFloatingObjects.length) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] proximityBetweenFloatingObjects -> err= ' + err.stack) } + } + } + + function targetRepulsionForce (currentFloatingObject) { + try { + /* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */ + + const coulomb = 2 + + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] + + for (let i = 0; i < visibleFloatingObjects.length; i++) { + // The force to be applied is considering all other floatingObjects... + + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + + let payload = { + position: undefined + } + + switch (floatingObject2.type) { + case 'UI Object': { + payload.targetPosition = floatingObject2.payload.targetPosition + break + } + default: { + break + } + } + + let d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject1.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + + let 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 + } + + let pos1 = { + x: floatingObject1.container.frame.position.x, + y: floatingObject1.container.frame.position.y + } + + let pos2 = { + x: payload.targetPosition.x, + y: payload.targetPosition.y + } + + let posDiff = { // Next we need the vector resulting from the 2 positions. + x: pos2.x - pos1.x, + y: pos2.y - pos1.y + } + + if (d !== 0) { + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + x: posDiff.x / d, + y: posDiff.y / d + } + + let forceVector = { + x: unitVector.x * force, + y: unitVector.y * force + } + + /* We substract the force vector to the speed vector of the current floatingObject */ + + if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) { + floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x + floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] targetRepulsionForce -> err= ' + err.stack) } + } + } + + function changeTargetRepulsion (pDelta) { + try { + if (pDelta > 0) { + pDelta = 1 + } else { + pDelta = -1 + } + + maxTargetRepulsionForce = maxTargetRepulsionForce + pDelta / 1000 + + if (maxTargetRepulsionForce < 0.0000000001) { + maxTargetRepulsionForce = 0.0000000001 + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] changeTargetRepulsion -> err= ' + err.stack) } + } + } + + function colliding (floatingObject1, floatingObject2) { + try { + /* This function detects weather 2 floatingObjects collide with each other. */ + + let r1 = floatingObject1.container.frame.radius + let r2 = floatingObject2.container.frame.radius + + let distance = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.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 + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] colliding -> err= ' + err.stack) } + } + } + + function distance (x1, y1, x2, y2) { + return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) + } + + function resolveCollision (floatingObject1, floatingObject2) { + try { + /* This function changes speed and position of floatingObjects that are in collision */ + + let collisionision_angle = Math.atan2((floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y), (floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x)) + + let speed1 = Math.sqrt(floatingObject1.currentSpeed.x * floatingObject1.currentSpeed.x + floatingObject1.currentSpeed.y * floatingObject1.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 1 + let speed2 = Math.sqrt(floatingObject2.currentSpeed.x * floatingObject2.currentSpeed.x + floatingObject2.currentSpeed.y * floatingObject2.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 2 + + let direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x) + let direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x) + + let new_xspeed_1 = speed1 * Math.cos(direction_1 - collisionision_angle) + let new_yspeed_1 = speed1 * Math.sin(direction_1 - collisionision_angle) + let new_xspeed_2 = speed2 * Math.cos(direction_2 - collisionision_angle) + let new_yspeed_2 = speed2 * Math.sin(direction_2 - collisionision_angle) + + let final_xspeed_1 = ((floatingObject1.currentMass - floatingObject2.currentMass) * new_xspeed_1 + (floatingObject2.currentMass + floatingObject2.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) + let final_xspeed_2 = ((floatingObject1.currentMass + floatingObject1.currentMass) * new_xspeed_1 + (floatingObject2.currentMass - floatingObject1.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) + let final_yspeed_1 = new_yspeed_1 + let final_yspeed_2 = new_yspeed_2 + + let cosAngle = Math.cos(collisionision_angle) + let 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 + + let pos1 = { + x: floatingObject1.container.frame.position.x, + y: floatingObject1.container.frame.position.y + } + + let pos2 = { + x: floatingObject2.container.frame.position.x, + y: floatingObject2.container.frame.position.y + } + + // get the mtd + let posDiff = { + x: pos1.x - pos2.x, + y: pos1.y - pos2.y + } + + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) + + // minimum translation distance to push floatingObjects apart after intersecting + let scalar = (((floatingObject1.container.frame.radius + floatingObject2.container.frame.radius) - d) / d) + + let minTD = { + x: posDiff.x * scalar, + y: posDiff.y * scalar + } + + // resolve intersection -- + // computing inverse mass quantities + let im1 = 1 / floatingObject1.currentMass + let 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)) + + if (floatingObject1.positionLocked === false && isNaN(pos1.x) === false && isNaN(pos1.y) === false) { + floatingObject1.container.frame.position = pos1 + } + if (floatingObject2.positionLocked === false && isNaN(pos2.x) === false && isNaN(pos2.y) === false) { + floatingObject2.container.frame.position = pos2 + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] resolveCollision -> err= ' + err.stack) } + } + } +} diff --git a/CanvasApp/FloatingSpace/FloatingObject.js b/CanvasApp/FloatingSpace/FloatingObject.js new file mode 100644 index 000000000..6e9ce5833 --- /dev/null +++ b/CanvasApp/FloatingSpace/FloatingObject.js @@ -0,0 +1,566 @@ + +function newFloatingObject () { + const MODULE_NAME = 'Floating Object' + const ERROR_LOG = true + + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + fitFunction: undefined, + container: undefined, + positionLocked: false, + isOnFocus: false, + 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. + currentSpeed: 0, // This is the current speed of the floating object. + 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. + targetFriction: 0, + 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. + isPinned: false, + isFrozen: false, + angleToParent: ANGLE_TO_PARENT.RANGE_90, + distanceToParent: DISTANCE_TO_PARENT.PARENT_100X, + isCollapsed: false, + isParentCollapsed: false, + frozenManually: false, + collapsedManually: false, + getPinStatus: getPinStatus, + getFreezeStatus: getFreezeStatus, + getCollapseStatus: getCollapseStatus, + getAngleToParent: getAngleToParent, + getDistanceToParent: getDistanceToParent, + nearbyFloatingObjects: [], + setPosition: setPosition, + pinToggle: pinToggle, + freezeToggle: freezeToggle, + collapseToggle: collapseToggle, + angleToParentToggle: angleToParentToggle, + distanceToParentToggle: distanceToParentToggle, + physics: physics, + initializeMass: initializeMass, + initializeRadius: initializeRadius, + initializeImageSize: initializeImageSize, + initializeFontSize: initializeFontSize, + initializeCurrentPosition: initializeCurrentPosition, + radomizeCurrentSpeed: radomizeCurrentSpeed, + drawBackground: drawBackground, + drawMiddleground: drawMiddleground, + drawForeground: drawForeground, + drawOnFocus: drawOnFocus, + updateMass: updateMass, // Function to update the mass when the zoom level changed. + updateRadius: updateRadius, // Function to update the radius when the zoom level changed. + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME, 'Circle') + thisObject.container.isClickeable = true + thisObject.container.isDraggeable = true + thisObject.container.detectMouseOver = true + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let selfMouseOverEventSubscriptionId + let selfMouseClickEventSubscriptionId + let spaceMouseOverEventSubscriptionId + let spaceFocusAquiredEventSubscriptionId + let lastParentAngle + + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(selfMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId) + canvas.floatingSpace.container.eventHandler.stopListening(spaceMouseOverEventSubscriptionId) + canvas.floatingSpace.container.eventHandler.stopListening(spaceFocusAquiredEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.payload = undefined + thisObject.fitFunction = undefined + thisObject.nearbyFloatingObjects = undefined + } + + function initialize (type, payload) { + thisObject.payload = payload + thisObject.type = type + + selfMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + + /* To consider that this object lost the focus, we monitor the space for 2 key events */ + spaceMouseOverEventSubscriptionId = canvas.floatingSpace.container.eventHandler.listenToEvent('onMouseOver', mouseOverFlotingSpace) + spaceFocusAquiredEventSubscriptionId = canvas.floatingSpace.container.eventHandler.listenToEvent('onFocusAquired', someoneAquiredFocus) + + /* Assign a position and speed */ + + thisObject.initializeCurrentPosition(thisObject.payload.targetPosition) + thisObject.radomizeCurrentSpeed() + } + + function getContainer (point) { + if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return } + let container + + container = thisObject.payload.uiObject.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisScreenPointHere(point) === true) { + return thisObject.container + } else { + return undefined + } + } + + function getPinStatus () { + return thisObject.isPinned + } + + function pinToggle () { + if (thisObject.isPinned !== true) { + thisObject.isPinned = true + thisObject.positionLocked = true + } else { + thisObject.isPinned = false + } + return thisObject.isPinned + } + + function getFreezeStatus () { + return thisObject.isFrozen + } + + function getCollapseStatus () { + return thisObject.isCollapsed + } + + function getAngleToParent () { + return thisObject.angleToParent + } + + function getDistanceToParent () { + return thisObject.distanceToParent + } + + function freezeToggle () { + if (thisObject.isFrozen !== true) { + thisObject.isFrozen = true + thisObject.frozenManually = true + } else { + thisObject.isFrozen = false + thisObject.frozenManually = false + } + return thisObject.isFrozen + } + + function collapseToggle () { + if (thisObject.isCollapsed !== true) { + thisObject.isCollapsed = true + thisObject.collapsedManually = true + } else { + thisObject.isCollapsed = false + thisObject.collapsedManually = false + } + return thisObject.isCollapsed + } + + function angleToParentToggle () { + switch (thisObject.angleToParent) { + case ANGLE_TO_PARENT.NOT_FIXED: + thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_360 + break + case ANGLE_TO_PARENT.RANGE_360: + thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_180 + break + case ANGLE_TO_PARENT.RANGE_180: + thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_90 + break + case ANGLE_TO_PARENT.RANGE_90: + thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_45 + break + case ANGLE_TO_PARENT.RANGE_45: + thisObject.angleToParent = ANGLE_TO_PARENT.NOT_FIXED + break + } + + return thisObject.angleToParent + } + + function distanceToParentToggle () { + switch (thisObject.distanceToParent) { + case DISTANCE_TO_PARENT.NOT_FIXED: + thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_025X + break + case DISTANCE_TO_PARENT.PARENT_025X: + thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_050X + break + case DISTANCE_TO_PARENT.PARENT_050X: + thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_100X + break + case DISTANCE_TO_PARENT.PARENT_100X: + thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_150X + break + case DISTANCE_TO_PARENT.PARENT_150X: + thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_200X + break + case DISTANCE_TO_PARENT.PARENT_200X: + thisObject.distanceToParent = DISTANCE_TO_PARENT.NOT_FIXED + break + } + + return thisObject.distanceToParent + } + + function physics () { + collapsePhysics() + frozenPhysics() + /* From here on, only if they are not too far. */ + if (canvas.floatingSpace.isItFar(thisObject.payload)) { return } + thisObjectPhysics() + thisObject.payload.uiObject.physics() + positionContraintsPhysics() + } + + function frozenPhysics () { + if (thisObject.frozenManually !== false) { return } + if (thisObject.payload === undefined) { return } + let parent = thisObject.payload.chainParent + if (parent !== undefined) { + if (parent.payload !== undefined) { + if (parent.payload.floatingObject !== undefined) { + thisObject.isFrozen = parent.payload.floatingObject.isFrozen + } + } + } + } + + function collapsePhysics () { + let parent = thisObject.payload.chainParent + if (parent === undefined) { return } + if (parent.payload === undefined) { return } + if (parent.payload.floatingObject === undefined) { return } + + thisObject.isParentCollapsed = parent.payload.floatingObject.isCollapsed + if (thisObject.collapsedManually === false) { + thisObject.isCollapsed = parent.payload.floatingObject.isCollapsed + } + } + + function positionContraintsPhysics () { + if (thisObject.angleToParent !== ANGLE_TO_PARENT.NOT_FIXED && thisObject.isOnFocus !== true) { + let parent = thisObject.payload.chainParent + if (parent === undefined) { return } + if (parent.payload === undefined) { return } + if (parent.payload.position === undefined) { return } + + let distanceToParent = Math.sqrt(Math.pow(parent.payload.position.x - thisObject.container.frame.position.x, 2) + Math.pow(parent.payload.position.y - thisObject.container.frame.position.y, 2)) // ... we calculate the distance ... + let parentChildren = canvas.designSpace.workspace.nodeChildren.childrenCount(parent, thisObject.payload.node) + let axisCount = parentChildren.childrenCount + let axisIndex = parentChildren.childIndex + let baseAngle = 0 + let angleToParentAngle + + if (parent.payload.distance !== undefined) { + switch (thisObject.distanceToParent) { + case DISTANCE_TO_PARENT.PARENT_025X: + distanceToParent = parent.payload.distance / 4 + break + case DISTANCE_TO_PARENT.PARENT_050X: + distanceToParent = parent.payload.distance / 2 + break + case DISTANCE_TO_PARENT.PARENT_100X: + distanceToParent = parent.payload.distance + break + case DISTANCE_TO_PARENT.PARENT_150X: + distanceToParent = parent.payload.distance * 1.5 + break + case DISTANCE_TO_PARENT.PARENT_200X: + distanceToParent = parent.payload.distance * 2 + break + } + } + + switch (thisObject.angleToParent) { + case ANGLE_TO_PARENT.RANGE_360: + angleToParentAngle = 360 + break + case ANGLE_TO_PARENT.RANGE_180: + angleToParentAngle = 180 + break + case ANGLE_TO_PARENT.RANGE_90: + angleToParentAngle = 90 + break + case ANGLE_TO_PARENT.RANGE_45: + angleToParentAngle = 45 + break + } + + if (axisIndex === undefined) { + axisCount = 1 + axisIndex = axisCount + } + + if (parent.payload.chainParent !== undefined && parent.payload.angle !== undefined) { + axisCount++ + axisIndex++ + baseAngle = parent.payload.angle + 180 + lastParentAngle = parent.payload.angle + } else { + if (lastParentAngle !== undefined) { + axisCount++ + axisIndex++ + baseAngle = lastParentAngle + 180 + } + } + + let separatorAngle = (360 - angleToParentAngle) / 2 + let angleStep = angleToParentAngle / axisCount + + thisObject.payload.angle = baseAngle + separatorAngle + (axisIndex - 1) * angleStep + if (thisObject.payload.angle >= 360) { + thisObject.payload.angle = thisObject.payload.angle - 360 + } + + thisObject.payload.distance = distanceToParent + + if (distanceToParent > 2000 || thisObject.isPinned === true) { return } // this is introduced to avoid edges cases when importing workspaces. + + newPosition = { + x: parent.payload.position.x + distanceToParent * Math.cos(toRadians(thisObject.payload.angle)), + y: parent.payload.position.y + distanceToParent * Math.sin(toRadians(thisObject.payload.angle)) + } + if (isNaN(newPosition.x) === false) { + thisObject.container.frame.position.x = newPosition.x + } + if (isNaN(newPosition.y) === false) { + thisObject.container.frame.position.y = newPosition.y + } + } + } + + function thisObjectPhysics () { + // The radius also have a target. + + if (Math.abs(thisObject.container.frame.radius - thisObject.targetRadius) >= 15) { + if (thisObject.container.frame.radius < thisObject.targetRadius) { + thisObject.container.frame.radius = thisObject.container.frame.radius + 15 + } else { + thisObject.container.frame.radius = thisObject.container.frame.radius - 15 + } + thisObject.container.eventHandler.raiseEvent('Dimmensions Changed', event) + } + + // The imageSize also have a target. + + if (Math.abs(thisObject.currentImageSize - thisObject.targetImageSize) >= 10) { + if (thisObject.currentImageSize < thisObject.targetImageSize) { + thisObject.currentImageSize = thisObject.currentImageSize + 10 + } else { + thisObject.currentImageSize = thisObject.currentImageSize - 10 + } + } + + // The fontSize also have a target. + + if (Math.abs(thisObject.currentFontSize - thisObject.targetFontSize) >= 1) { + if (thisObject.currentFontSize < thisObject.targetFontSize) { + thisObject.currentFontSize = thisObject.currentFontSize + 1 + } else { + thisObject.currentFontSize = thisObject.currentFontSize - 1 + } + } + + /* Floating object position in screen coordinates */ + + thisObject.payload.position.x = thisObject.container.frame.position.x + thisObject.payload.position.y = thisObject.container.frame.position.y + } + + function onMouseOver (point) { + if (thisObject.isOnFocus === false) { + thisObject.targetRadius = thisObject.rawRadius * 6.0 + thisObject.targetImageSize = thisObject.rawImageSize * 2.0 + thisObject.targetFontSize = thisObject.rawFontSize * 2.0 + + thisObject.payload.uiObject.container.eventHandler.raiseEvent('onFocus', point) + + thisObject.positionLocked = true + + canvas.floatingSpace.container.eventHandler.raiseEvent('onFocusAquired', thisObject.container) + thisObject.isOnFocus = true + } + } + + function mouseOverFlotingSpace (point) { + removeFocus() + } + + function someoneAquiredFocus (container) { + if (container === undefined) { + return + } + if (thisObject.container === undefined) { + return + } + if (container.id !== thisObject.container.id) { + removeFocus() + } + } + + function removeFocus () { + if (thisObject.payload === undefined) { return } + if (thisObject.isOnFocus === true) { + thisObject.targetRadius = thisObject.rawRadius * 1 + thisObject.targetImageSize = thisObject.rawImageSize * 1 + thisObject.targetFontSize = thisObject.rawFontSize * 1 + + thisObject.payload.uiObject.container.eventHandler.raiseEvent('onNotFocus') + + if (thisObject.isPinned !== true) { + thisObject.positionLocked = false + } + thisObject.isOnFocus = false + } + } + + function onMouseClick (pPoint) { + let event = { + point: pPoint, + parent: thisObject + } + thisObject.payload.uiObject.container.eventHandler.raiseEvent('onMouseClick', event) + } + + function drawBackground () { + if (canvas.floatingSpace.isItFar(thisObject.payload)) { return } + if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return } + thisObject.payload.uiObject.drawBackground() + } + + function drawMiddleground () { + if (canvas.floatingSpace.isItFar(thisObject.payload)) { return } + if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return } + thisObject.payload.uiObject.drawMiddleground() + } + + function drawForeground () { + if (canvas.floatingSpace.isItFar(thisObject.payload)) { return } + if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return } + thisObject.payload.uiObject.drawForeground() + } + + function drawOnFocus () { + thisObject.payload.uiObject.drawOnFocus() + } + + function initializeMass (suggestedValue) { + let mass = suggestedValue + if (mass < 0.1) { + mass = 0.1 + } + + thisObject.rawMass = mass + thisObject.currentMass = mass + } + + function initializeRadius (suggestedValue) { + let radius = suggestedValue + if (radius < 2) { + radius = 2 + } + + thisObject.rawRadius = radius + thisObject.targetRadius = radius + thisObject.container.frame.radius = radius / 3 + + thisObject.container.eventHandler.raiseEvent('Dimmensions Changed', event) + } + + function initializeImageSize (suggestedValue) { + var size = suggestedValue + if (size < 2) { + size = 2 + } + + thisObject.rawImageSize = size + thisObject.targetImageSize = size + thisObject.currentImageSize = size / 3 + } + + function initializeFontSize (suggestedValue) { + var size = suggestedValue + if (size < 3) { + size = 3 + } + + thisObject.rawFontSize = size + thisObject.targetFontSize = size + thisObject.currentFontSize = size / 3 + } + + function initializeCurrentPosition (arroundPoint) { + let position = {} + + if (thisObject.payload.position === undefined) { + position = { + x: Math.floor((Math.random() * (200) - 100)) + arroundPoint.x, + y: Math.floor((Math.random() * (200) - 100)) + arroundPoint.y + } + } else { + position = { + x: thisObject.payload.position.x, + y: thisObject.payload.position.y + } + } + + setPosition(position) + } + + function setPosition (position) { + thisObject.container.frame.position.x = position.x + thisObject.container.frame.position.y = position.y + + thisObject.payload.position = {} + thisObject.payload.position.x = position.x + thisObject.payload.position.y = position.y + } + + 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 () { + + } + + function updateRadius () { + } +} diff --git a/CanvasApp/FloatingSpace/FormulaEditor.js b/CanvasApp/FloatingSpace/FormulaEditor.js new file mode 100644 index 000000000..f2e805485 --- /dev/null +++ b/CanvasApp/FloatingSpace/FormulaEditor.js @@ -0,0 +1,215 @@ + +function newFormulaEditor () { + const MODULE_NAME = 'Formula Editor' + + let thisObject = { + isVisibleFunction: undefined, + iconOK: undefined, + iconNOT_OK: undefined, + visible: false, + imagePathOK: undefined, + imagePathNOT_OK: undefined, + rawRadius: undefined, + targetRadius: undefined, + currentRadius: undefined, + container: undefined, + payload: undefined, + deactivate: deactivate, + activate: activate, + physics: physics, + drawBackground: drawBackground, + drawForeground: drawForeground, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.detectMouseOver = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let isMouseOver = false + + return thisObject + + function finalize () { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.iconOK = undefined + thisObject.iconNOT_OK = undefined + thisObject.payload = undefined + thisObject.isVisibleFunction = undefined + } + + function initialize () { + /* Load Images */ + + thisObject.iconOK = new Image() + thisObject.iconOK.onload = onImageLoad + + function onImageLoad () { + thisObject.iconNOT_OK = new Image() + thisObject.iconNOT_OK.onload = onImageLoad + + function onImageLoad () { + thisObject.canDrawIcon = true + } + thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK + } + // thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK + // thisObject.icon = thisObject.iconOK // The default value is ON. + } + + function deactivate () { + if (thisObject.visible === true) { + thisObject.visible = false + let textArea = document.getElementById('textArea') + textArea.style.display = 'none' + thisObject.payload.node.code = textArea.value + } + } + + function activate (payload) { + thisObject.visible = true + thisObject.payload = payload + thisObject.rawRadius = 8 + thisObject.targetRadius = thisObject.container.frame.radius + thisObject.currentRadius = 0 + thisObject.payload.uiObject.setErrorMessage('', 0) + + let textArea = document.getElementById('textArea') + textArea.value = payload.node.code + textArea.style = 'resize: none;' + + ' border: none;' + + ' outline: none;' + + 'box-shadow: none;' + + 'overflow:hidden;' + + 'font-family: ' + UI_FONT.PRIMARY + ';' + + 'font-size: 12px;' + + 'background-color: rgb(' + UI_COLOR.MANGANESE_PURPLE + ');' + + 'color:rgb(255, 255, 255);' + + 'width: ' + thisObject.container.frame.width + 'px;' + + 'height: ' + thisObject.container.frame.height + 'px' + textArea.style.display = 'block' + textArea.focus() + } + + function getContainer (point) { + let container + if (thisObject.visible === true) { + if (point.x === VERY_LARGE_NUMBER) { + /* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that + anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is + triggered. We compensate recognizing this coordinate and returning our container. */ + return thisObject.container + } + + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + return thisObject.container + } else { + return undefined + } + } + } + + function physics () { + if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) { + if (thisObject.currentRadius < thisObject.targetRadius) { + thisObject.currentRadius = thisObject.currentRadius + 0.5 + } else { + thisObject.currentRadius = thisObject.currentRadius - 0.5 + } + } + + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * 2 + thisObject.container.frame.height = thisObject.container.frame.radius * 1 * 2 + + let textAreaPosition = { + x: 0 - thisObject.container.frame.width / 2, + y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN + } + + textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition) + if (thisObject.visible === true) { + let checkPosition = { + x: textAreaPosition.x, + y: textAreaPosition.y - CURRENT_TOP_MARGIN + } + if (thisObject.isVisibleFunction(checkPosition) === false) { + deactivate() + } + } + if (thisObject.visible === true) { + let textAreaDiv = document.getElementById('textAreaDiv') + textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; ' + } + } + + function drawBackground () { + if (thisObject.visible === true) { + let position = { + x: 0, + y: 0 + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius * 2 + + if (radius > 0.5) { + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')' + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', ' + 1 + ')' + browserCanvasContext.fill() + } + } + } + function drawForeground () { + let iconPosition = { + x: 0, + y: thisObject.currentRadius * 1.5 + } + + iconPosition = thisObject.container.frame.frameThisPoint(iconPosition) + + /* Menu Item */ + + if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) { + browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2) + + /* Menu Label */ + + let label = 'Code looks good!' + let labelPoint + let fontSize = 10 + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + + if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) { + labelPoint = { + x: iconPosition.x + thisObject.currentRadius + 10, + y: iconPosition.y + fontSize * FONT_ASPECT_RATIO + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } +} diff --git a/CanvasApp/FloatingSpace/UiObject.js b/CanvasApp/FloatingSpace/UiObject.js new file mode 100644 index 000000000..ee4510d2c --- /dev/null +++ b/CanvasApp/FloatingSpace/UiObject.js @@ -0,0 +1,1391 @@ + +function newUiObject () { + const MODULE_NAME = 'UI Object' + const ERROR_LOG = true + + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + fitFunction: undefined, + isVisibleFunction: undefined, + type: undefined, + menu: undefined, + isOnFocus: false, + container: undefined, + payload: undefined, + codeEditor: undefined, + configEditor: undefined, + conditionEditor: undefined, + formulaEditor: undefined, + uiObjectTitle: undefined, + circularProgressBar: undefined, + isExecuting: undefined, + isRunning: undefined, + shortcutKey: undefined, + run: run, + stop: stop, + getReadyToChainAttach: getReadyToChainAttach, + showAvailabilityToChainAttach: showAvailabilityToChainAttach, + getReadyToReferenceAttach: getReadyToReferenceAttach, + showAvailabilityToReferenceAttach: showAvailabilityToReferenceAttach, + highlight: highlight, + setErrorMessage: setErrorMessage, + setValue: setValue, + physics: physics, + drawBackground: drawBackground, + drawMiddleground: drawMiddleground, + drawForeground: drawForeground, + drawOnFocus: drawOnFocus, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME, 'Circle') + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + let icon + let executingIcon + + let selfFocusEventSubscriptionId + let selfNotFocuskEventSubscriptionId + let selfDisplaceEventSubscriptionId + let selfDragStartedEventSubscriptionId + let selfDragFinishedEventSubscriptionId + + let isHighlighted + let highlightCounter = 0 + + let hasError + let errorMessageCounter = 0 + + let hasValue + let valueCounter = 0 + + let previousDistanceToChainParent + let readyToChainAttachDisplayCounter = 5 + let readyToChainAttachDisplayIncrement = 0.1 + let readyToChainAttachCounter = 0 + let isReadyToChainAttach + let availableToChainAttachCounter = 0 + let isAvailableToChainAttach + let isChainAttaching = false + let chainAttachToNode + + let previousDistanceToReferenceParent + let readyToReferenceAttachDisplayCounter = 5 + let readyToReferenceAttachDisplayIncrement = 0.1 + let readyToReferenceAttachCounter = 0 + let isReadyToReferenceAttach + let availableToReferenceAttachCounter = 0 + let isAvailableToReferenceAttach + let isReferenceAttaching = false + let referenceAttachToNode + + let isDragging = false + + let errorMessage = '' + let currentValue = 0 + let rightDragging = false + + let eventSubscriptionIdHeartbeat + let eventSubscriptionIdOnStopped + + return thisObject + + function finalize () { + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + systemEventHandler.stopListening(key, eventSubscriptionIdHeartbeat, 'UiObject') + systemEventHandler.stopListening(key, eventSubscriptionIdOnStopped, 'UiObject') + + thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfNotFocuskEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfDisplaceEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfDragStartedEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfDragFinishedEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.payload = undefined + thisObject.menu.finalize() + thisObject.menu = undefined + thisObject.uiObjectTitle.finalize() + thisObject.uiObjectTitle = undefined + thisObject.fitFunction = undefined + thisObject.isVisibleFunction = undefined + + if (thisObject.codeEditor !== undefined) { + thisObject.codeEditor.finalize() + thisObject.codeEditor = undefined + } + + if (thisObject.configEditor !== undefined) { + thisObject.configEditor.finalize() + thisObject.configEditor = undefined + } + + if (thisObject.conditionEditor !== undefined) { + thisObject.conditionEditor.finalize() + thisObject.conditionEditor = undefined + } + + if (thisObject.formulaEditor !== undefined) { + thisObject.formulaEditor.finalize() + thisObject.formulaEditor = undefined + } + + icon = undefined + chainAttachToNode = undefined + referenceAttachToNode = undefined + } + + function initialize (payload, menuItemsInitialValues) { + thisObject.payload = payload + +/* Initialize the Menu */ + + thisObject.menu = newCircularMenu() + thisObject.menu.initialize(menuItemsInitialValues, thisObject.payload) + thisObject.menu.container.connectToParent(thisObject.container, false, false, true, true, false, false, true, true) + +/* Initialize UI Object Title */ + + thisObject.uiObjectTitle = newUiObjectTitle() + thisObject.uiObjectTitle.isVisibleFunction = thisObject.isVisibleFunction + thisObject.uiObjectTitle.container.connectToParent(thisObject.container, false, false, true, true, false, false, true, true) + thisObject.uiObjectTitle.initialize(thisObject.payload) + +/* Load UI Object Image */ + + iconPhysics() + + selfFocusEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onFocus', onFocus) + selfNotFocuskEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onNotFocus', onNotFocus) + selfDisplaceEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDisplace', onDisplace) + selfDragStartedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragStarted', onDragStarted) + selfDragFinishedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragFinished', onDragFinished) + } + + function getContainer (point) { + let container + + if (isDragging === false && thisObject.isOnFocus === true) { + if (thisObject.codeEditor !== undefined) { + container = thisObject.codeEditor.getContainer(point) + if (container !== undefined) { return container } + } + + if (thisObject.configEditor !== undefined) { + container = thisObject.configEditor.getContainer(point) + if (container !== undefined) { return container } + } + + if (thisObject.conditionEditor !== undefined) { + container = thisObject.conditionEditor.getContainer(point) + if (container !== undefined) { return container } + } + + if (thisObject.formulaEditor !== undefined) { + container = thisObject.formulaEditor.getContainer(point) + if (container !== undefined) { return container } + } + + let getitle = true + + if (thisObject.codeEditor !== undefined) { + if (thisObject.codeEditor.visible === true) { + getitle = false + } + } + if (thisObject.configEditor !== undefined) { + if (thisObject.configEditor.visible === true) { + getitle = false + } + } + if (thisObject.conditionEditor !== undefined) { + if (thisObject.conditionEditor.visible === true) { + getitle = false + } + } + if (thisObject.formulaEditor !== undefined) { + if (thisObject.formulaEditor.visible === true) { + getitle = false + } + } + + if (getitle === true) { + container = thisObject.uiObjectTitle.getContainer(point) + if (container !== undefined) { return container } + } + + container = thisObject.menu.getContainer(point) + if (container !== undefined) { return container } + } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function physics () { + thisObject.menu.physics() + thisObject.uiObjectTitle.physics() + + if (thisObject.codeEditor !== undefined) { + thisObject.codeEditor.physics() + } + + if (thisObject.configEditor !== undefined) { + thisObject.configEditor.physics() + } + + if (thisObject.conditionEditor !== undefined) { + thisObject.conditionEditor.physics() + } + + if (thisObject.formulaEditor !== undefined) { + thisObject.formulaEditor.physics() + } + + if (thisObject.payload.chainParent === undefined || thisObject.payload.chainParent.payload === undefined) { + thisObject.payload.targetPosition.x = thisObject.payload.position.x, + thisObject.payload.targetPosition.y = thisObject.payload.position.y + } else { + if (thisObject.payload.chainParent.payload.position !== undefined) { + thisObject.payload.targetPosition.x = thisObject.payload.chainParent.payload.position.x + thisObject.payload.targetPosition.y = thisObject.payload.chainParent.payload.position.y + } + } + + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.physics() + } + + iconPhysics() + highlightPhisycs() + errorMessagePhisycs() + valuePhisycs() + chainDetachingPhysics() + chainAttachingPhysics() + referenceDetachingPhysics() + referenceAttachingPhysics() + } + + function chainAttachingPhysics () { + chainAttacchingCounters() + + if (thisObject.isOnFocus !== true) { return } + if (isDragging !== true) { return } + if (rightDragging !== true) { return } + if (thisObject.payload.chainParent !== undefined) { return } + + let nearbyFloatingObjects = thisObject.payload.floatingObject.nearbyFloatingObjects + let compatibleTypes + + let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition !== undefined) { + if (nodeDefinition.chainAttachesTo !== undefined) { + compatibleTypes = nodeDefinition.chainAttachesTo.compatibleTypes + } else { + return + } + } else { + return + } + + let foundCompatible = false + chainAttachToNode = undefined + isChainAttaching = false + + for (let i = 0; i < nearbyFloatingObjects.length; i++) { + let nearby = nearbyFloatingObjects[i] + let distance = nearby[0] + let floatingObject = nearby[1] + let nearbyNode = floatingObject.payload.node + if (compatibleTypes.indexOf('->' + nearbyNode.type + '->') >= 0) { + /* Discard App Schema defined objects with busy coonection ports */ + nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition !== undefined) { + let mustContinue = false + let parentNodeDefinition = APP_SCHEMA_MAP.get(nearbyNode.type) + if (parentNodeDefinition !== undefined) { + if (parentNodeDefinition.properties !== undefined) { + for (let j = 0; j < parentNodeDefinition.properties.length; j++) { + let property = parentNodeDefinition.properties[j] + if (nodeDefinition.propertyNameAtParent === property.name) { + switch (property.type) { + case 'node': { + if (nearbyNode[property.name] !== undefined) { + mustContinue = true + } + } + break + case 'array': { + /* Nothing to worry about since an array can take more than one element. */ + } + break + } + } + } + } + } + if (mustContinue === true) { continue } + } + + /* Discard Phases without partent */ + if (thisObject.payload.node.type === 'Phase' && nearbyNode.type === 'Phase' && nearbyNode.payload.parentNode === undefined) { continue } + /* Control maxPhases */ + if (thisObject.payload.node.type === 'Phase') { + if (nearbyNode.maxPhases !== undefined) { + if (nearbyNode.phases.length >= nearbyNode.maxPhases) { + continue + } + } + } + if (thisObject.payload.node.type === 'Phase' && nearbyNode.type === 'Phase') { + if (nearbyNode.payload.parentNode.maxPhases !== undefined) { + if (nearbyNode.payload.parentNode.phases.length >= nearbyNode.payload.parentNode.maxPhases) { + continue + } + } + } + if (foundCompatible === false) { + if (distance < thisObject.container.frame.radius * 1.5 + floatingObject.container.frame.radius * 1.5) { + nearbyNode.payload.uiObject.getReadyToChainAttach() + isChainAttaching = true + chainAttachToNode = nearbyNode + foundCompatible = true + } + } + nearbyNode.payload.uiObject.showAvailabilityToChainAttach() + } + } + } + + function chainAttacchingCounters () { + if (readyToChainAttachDisplayCounter > 15) { + readyToChainAttachDisplayIncrement = -0.25 + } + + if (readyToChainAttachDisplayCounter < 5) { + readyToChainAttachDisplayIncrement = 0.25 + } + + readyToChainAttachDisplayCounter = readyToChainAttachDisplayCounter + readyToChainAttachDisplayIncrement + + readyToChainAttachCounter-- + if (readyToChainAttachCounter <= 0) { + readyToChainAttachCounter = 0 + isReadyToChainAttach = false + } else { + isReadyToChainAttach = true + } + + availableToChainAttachCounter-- + if (availableToChainAttachCounter <= 0) { + availableToChainAttachCounter = 0 + isAvailableToChainAttach = false + } else { + isAvailableToChainAttach = true + } + } + + function getReadyToChainAttach () { + readyToChainAttachCounter = 10 + } + + function showAvailabilityToChainAttach () { + availableToChainAttachCounter = 10 + } + + function chainDetachingPhysics () { + if (isDragging !== true) { return } + if (thisObject.payload.floatingObject.isFrozen === true) { return } + if (rightDragging === false) { return } + + let distanceToChainParent = Math.sqrt(Math.pow(thisObject.payload.position.x - thisObject.payload.targetPosition.x, 2) + Math.pow(thisObject.payload.position.y - thisObject.payload.targetPosition.y, 2)) + let ratio = distanceToChainParent / previousDistanceToChainParent + if (previousDistanceToChainParent === 0) { + previousDistanceToChainParent = distanceToChainParent + return + } + previousDistanceToChainParent = distanceToChainParent + + if (thisObject.isOnFocus !== true) { return } + if (thisObject.payload.chainParent === undefined) { return } + if (thisObject.payload.parentNode === undefined) { return } + + let THRESHOLD = 1.15 + + if (previousDistanceToChainParent !== undefined) { + if (ratio > THRESHOLD) { + canvas.designSpace.workspace.chainDetachNode(thisObject.payload.node) + } + } + } + + function referenceAttachingPhysics () { + referenceAttacchingCounters() + + if (thisObject.isOnFocus !== true) { return } + if (isDragging !== true) { return } + if (rightDragging !== true) { return } + if (thisObject.payload.referenceParent !== undefined) { return } + + let nearbyFloatingObjects = thisObject.payload.floatingObject.nearbyFloatingObjects + let compatibleTypes + + let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition !== undefined) { + if (nodeDefinition.referenceAttachesTo !== undefined) { + compatibleTypes = nodeDefinition.referenceAttachesTo.compatibleTypes + } else { + return + } + } else { + return + } + + let foundCompatible = false + referenceAttachToNode = undefined + isReferenceAttaching = false + + for (let i = 0; i < nearbyFloatingObjects.length; i++) { + let nearby = nearbyFloatingObjects[i] + let distance = nearby[0] + let floatingObject = nearby[1] + let nearbyNode = floatingObject.payload.node + if (compatibleTypes.indexOf('->' + nearbyNode.type + '->') >= 0) { + if (foundCompatible === false) { + if (distance < thisObject.container.frame.radius * 1.5 + floatingObject.container.frame.radius * 1.5) { + nearbyNode.payload.uiObject.getReadyToReferenceAttach() + isReferenceAttaching = true + referenceAttachToNode = nearbyNode + foundCompatible = true + } + } + nearbyNode.payload.uiObject.showAvailabilityToReferenceAttach() + } + } + } + + function referenceAttacchingCounters () { + if (readyToReferenceAttachDisplayCounter > 15) { + readyToReferenceAttachDisplayIncrement = -0.25 + } + + if (readyToReferenceAttachDisplayCounter < 5) { + readyToReferenceAttachDisplayIncrement = 0.25 + } + + readyToReferenceAttachDisplayCounter = readyToReferenceAttachDisplayCounter + readyToReferenceAttachDisplayIncrement + + readyToReferenceAttachCounter-- + if (readyToReferenceAttachCounter <= 0) { + readyToReferenceAttachCounter = 0 + isReadyToReferenceAttach = false + } else { + isReadyToReferenceAttach = true + } + + availableToReferenceAttachCounter-- + if (availableToReferenceAttachCounter <= 0) { + availableToReferenceAttachCounter = 0 + isAvailableToReferenceAttach = false + } else { + isAvailableToReferenceAttach = true + } + } + + function getReadyToReferenceAttach () { + readyToReferenceAttachCounter = 10 + } + + function showAvailabilityToReferenceAttach () { + availableToReferenceAttachCounter = 10 + } + + function referenceDetachingPhysics () { + if (isDragging !== true) { return } + if (thisObject.payload.floatingObject.isFrozen === true) { return } + if (rightDragging === false) { return } + if (thisObject.payload.referenceParent === undefined) { return } + + let distanceToReferenceParent = Math.sqrt(Math.pow(thisObject.payload.position.x - thisObject.payload.referenceParent.payload.position.x, 2) + Math.pow(thisObject.payload.position.y - thisObject.payload.referenceParent.payload.position.y, 2)) + let ratio = distanceToReferenceParent / previousDistanceToReferenceParent + if (previousDistanceToReferenceParent === 0) { + previousDistanceToReferenceParent = distanceToReferenceParent + return + } + previousDistanceToReferenceParent = distanceToReferenceParent + + if (thisObject.isOnFocus !== true) { return } + if (thisObject.payload.referenceParent === undefined) { return } + + let THRESHOLD = 1.15 + + if (previousDistanceToReferenceParent !== undefined) { + if (ratio > THRESHOLD) { + canvas.designSpace.workspace.referenceDetachNode(thisObject.payload.node) + } + } + } + + function highlightPhisycs () { + highlightCounter-- + if (highlightCounter < 0) { + highlightCounter = 0 + isHighlighted = false + } + } + + function errorMessagePhisycs () { + errorMessageCounter-- + if (errorMessageCounter < 0) { + errorMessageCounter = 0 + hasError = false + } + } + + function valuePhisycs () { + valueCounter-- + if (valueCounter < 0) { + valueCounter = 0 + hasValue = false + } + } + + function highlight () { + isHighlighted = true + highlightCounter = 30 + } + + function setErrorMessage (message, duration) { + if (message !== undefined) { + errorMessage = message + hasError = true + if (duration === undefined) { duration = 1 } + errorMessageCounter = 100 * duration + } + } + + function setValue (value) { + if (value !== undefined) { + currentValue = value + hasValue = true + valueCounter = 500 + } + } + + function run (callBackFunction) { + /* We setup the circular progress bar. */ + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.finalize() + } + + thisObject.circularProgressBar = newCircularProgressBar() + thisObject.circularProgressBar.initialize(thisObject.payload) + thisObject.circularProgressBar.fitFunction = thisObject.fitFunction + thisObject.circularProgressBar.container = thisObject.container + + /* We will wait to hear the first onHeartBeat in order to confirm the execution was really started */ + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + systemEventHandler.listenToEvent(key, 'Heartbeat', undefined, 'UiObject', onResponse, onHeartBeat) + + function onResponse (message) { + eventSubscriptionIdHeartbeat = message.eventSubscriptionId + } + + function onHeartBeat () { + if (thisObject.payload === undefined) { return } + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + systemEventHandler.stopListening(key, eventSubscriptionIdHeartbeat, 'UiObject') + + if (callBackFunction !== undefined) { + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) + } + } + + /* + While it is running, it can happen that it naturally stops or is stopped not from the UI but from other means. + In those cases, the stop function would never be called (from the UI). So what we will do is to call it from + here with and event, and passing our own callBackFunction. In case there is an external source stopping this, + this will produce an execution of the callback with our event, which will produce that the menu item is restored + to its default stage. + + If on the other side, it is executed from the UI, then we will be processing the Stopped event twice, which in + both cases will reset the menu item to its default state. + */ + + let event = { + type: 'Secondary Action Already Executed' + } + stop(callBackFunction, event) + + thisObject.isRunning = true + } + + function stop (callBackFunction, event) { + /* We will wait to the event that the execution was terminated in order to call back the menu item */ + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + systemEventHandler.listenToEvent(key, 'Stopped', undefined, 'UiObject', onResponse, onStopped) + + function onResponse (message) { + eventSubscriptionIdOnStopped = message.eventSubscriptionId + } + + function onStopped () { + if (thisObject.payload === undefined) { return } + if (callBackFunction !== undefined) { + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, event) + } + + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.finalize() + thisObject.circularProgressBar = undefined + } + thisObject.isRunning = false + } + } + + function iconPhysics () { + icon = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type) + let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition.alternativeIcons !== undefined) { + let nodeToUse = thisObject.payload.node + if (nodeDefinition.alternativeIcons === 'Use Reference Parent') { + if (thisObject.payload.node.payload.referenceParent !== undefined) { + nodeToUse = thisObject.payload.node.payload.referenceParent + } + } + if (nodeDefinition.alternativeIcons === 'Use Reference Grandparent') { + if (thisObject.payload.node.payload.referenceParent !== undefined) { + if (thisObject.payload.node.payload.referenceParent.payload.referenceParent !== undefined) { + nodeToUse = thisObject.payload.node.payload.referenceParent.payload.referenceParent + } + } + } + nodeDefinition = APP_SCHEMA_MAP.get(nodeToUse.type) + let code = nodeToUse.code + try { + code = JSON.parse(code) + let alternativeIcon + let iconName + for (let i = 0; i < nodeDefinition.alternativeIcons.length; i++) { + alternativeIcon = nodeDefinition.alternativeIcons[i] + if (alternativeIcon.codeName === code.codeName) { + iconName = alternativeIcon.iconName + } + } + let newIcon = canvas.designSpace.iconCollection.get(iconName) + if (newIcon !== undefined) { + icon = newIcon + } + } catch (err) { + // Nothing to do if JSON is baddly formated. + } + } + + executingIcon = canvas.designSpace.iconCollection.get('attractive') + } + + function onFocus () { + thisObject.isOnFocus = true + } + + function onNotFocus () { + thisObject.isOnFocus = false + if (thisObject.codeEditor !== undefined) { + thisObject.codeEditor.deactivate() + } + if (thisObject.configEditor !== undefined) { + thisObject.configEditor.deactivate() + } + if (thisObject.conditionEditor !== undefined) { + thisObject.conditionEditor.deactivate() + } + if (thisObject.formulaEditor !== undefined) { + thisObject.formulaEditor.deactivate() + } + } + + function onDisplace (event) { + thisObject.uiObjectTitle.exitEditMode() + } + + function onDragStarted (event) { + thisObject.uiObjectTitle.exitEditMode() + if (thisObject.codeEditor !== undefined) { + thisObject.codeEditor.deactivate() + } + if (thisObject.configEditor !== undefined) { + thisObject.configEditor.deactivate() + } + if (thisObject.conditionEditor !== undefined) { + thisObject.conditionEditor.deactivate() + } + if (thisObject.formulaEditor !== undefined) { + thisObject.formulaEditor.deactivate() + } + isDragging = true + if (event.button === 2) { + rightDragging = true + } else { + rightDragging = false + } + + previousDistanceToReferenceParent = undefined + previousDistanceToChainParent = undefined + } + + function onDragFinished (event) { + if (isChainAttaching === true) { + canvas.designSpace.workspace.chainAttachNode(thisObject.payload.node, chainAttachToNode) + chainAttachToNode = undefined + isChainAttaching = false + } + if (isReferenceAttaching === true) { + canvas.designSpace.workspace.referenceAttachNode(thisObject.payload.node, referenceAttachToNode) + referenceAttachToNode = undefined + isReferenceAttaching = false + } + isDragging = false + } + + function drawBackground () { + if (thisObject.isOnFocus === false) { + drawReferenceLine() + drawChainLine() + + if (isDragging === false && thisObject.isOnFocus === true) { + thisObject.menu.drawBackground() + } + } + + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.drawBackground() + } + } + + function drawMiddleground () { + if (thisObject.isOnFocus === false) { + drawValue() + drawText() + thisObject.uiObjectTitle.draw() + } + } + + function drawForeground () { + if (thisObject.isOnFocus === false) { + drawBodyAndPicture() + if (isDragging === false) { + thisObject.menu.drawForeground() + } + } + + if (thisObject.circularProgressBar !== undefined) { + thisObject.circularProgressBar.drawForeground() + } + } + + function drawOnFocus () { + if (thisObject.isOnFocus === true) { + drawReferenceLine() + drawChainLine() + + if (thisObject.codeEditor !== undefined) { + thisObject.codeEditor.drawBackground() + thisObject.codeEditor.drawForeground() + } + if (thisObject.configEditor !== undefined) { + thisObject.configEditor.drawBackground() + thisObject.configEditor.drawForeground() + } + if (thisObject.conditionEditor !== undefined) { + thisObject.conditionEditor.drawBackground() + thisObject.conditionEditor.drawForeground() + } + if (thisObject.formulaEditor !== undefined) { + thisObject.formulaEditor.drawBackground() + thisObject.formulaEditor.drawForeground() + } + + let drawMenu = true + let drawTitle = true + + if (thisObject.codeEditor !== undefined) { + if (thisObject.codeEditor.visible === true) { + drawMenu = false + drawTitle = false + } + } + if (thisObject.configEditor !== undefined) { + if (thisObject.configEditor.visible === true) { + drawMenu = false + drawTitle = false + } + } + if (thisObject.conditionEditor !== undefined) { + if (thisObject.conditionEditor.visible === true) { + drawMenu = false + drawTitle = false + } + } + if (thisObject.formulaEditor !== undefined) { + if (thisObject.formulaEditor.visible === true) { + drawMenu = false + drawTitle = false + } + } + + if (drawMenu === true) { + drawBodyAndPicture() + if (isDragging === false) { + thisObject.menu.drawBackground() + thisObject.menu.drawForeground() + } + } + + drawErrorMessage() + drawValue() + drawText() + + if (drawTitle === true) { + thisObject.uiObjectTitle.draw() + } + } + } + + function drawChainLine () { + if (thisObject.payload.chainParent === undefined) { return } + + let targetPoint = { + x: thisObject.payload.chainParent.payload.floatingObject.container.frame.position.x, + y: thisObject.payload.chainParent.payload.floatingObject.container.frame.position.y + } + + let position = { + x: 0, + y: 0 + } + + targetPoint = canvas.floatingSpace.container.frame.frameThisPoint(targetPoint) + position = thisObject.container.frame.frameThisPoint(position) + + if (thisObject.container.frame.radius > 1) { + let LINE_STYLE = UI_COLOR.TITANIUM_YELLOW + if (thisObject.payload.floatingObject.angleToParent !== ANGLE_TO_PARENT.NOT_FIXED) { + LINE_STYLE = UI_COLOR.GOLDEN_ORANGE + } + if (thisObject.payload.floatingObject.isFrozen === true) { + LINE_STYLE = UI_COLOR.TURQUOISE + } + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(position.x, position.y) + browserCanvasContext.lineTo(targetPoint.x, targetPoint.y) + browserCanvasContext.strokeStyle = 'rgba(' + LINE_STYLE + ', 1)' + browserCanvasContext.setLineDash([3, 47]) + browserCanvasContext.lineWidth = 3 + browserCanvasContext.stroke() + browserCanvasContext.setLineDash([0, 0]) + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(position.x, position.y) + browserCanvasContext.lineTo(targetPoint.x, targetPoint.y) + browserCanvasContext.strokeStyle = 'rgba(' + LINE_STYLE + ', 1)' + browserCanvasContext.setLineDash([1, 9]) + browserCanvasContext.lineWidth = 1 + browserCanvasContext.stroke() + browserCanvasContext.setLineDash([0, 0]) + } + + if (thisObject.container.frame.radius > 0.5) { + let radius = 1 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(targetPoint.x, targetPoint.y, radius, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fill() + } + } + + function drawReferenceLine () { + if (thisObject.payload.referenceParent === undefined) { return } + if (thisObject.payload.referenceParent.payload === undefined) { return } + if (thisObject.payload.referenceParent.payload.floatingObject === undefined) { return } + if (thisObject.payload.referenceParent.payload.floatingObject.isParentCollapsed === true) { return } + + let targetPoint = { + x: thisObject.payload.referenceParent.payload.position.x, + y: thisObject.payload.referenceParent.payload.position.y + } + + let position = { + x: 0, + y: 0 + } + + targetPoint = canvas.floatingSpace.container.frame.frameThisPoint(targetPoint) + position = thisObject.container.frame.frameThisPoint(position) + + let LINE_STYLE = UI_COLOR.GREY + + if (thisObject.container.frame.radius > 1) { + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(position.x, position.y) + browserCanvasContext.lineTo(targetPoint.x, targetPoint.y) + browserCanvasContext.strokeStyle = 'rgba(' + LINE_STYLE + ', 1)' + browserCanvasContext.setLineDash([1, 9]) + browserCanvasContext.lineWidth = 0.75 + browserCanvasContext.stroke() + browserCanvasContext.setLineDash([0, 0]) + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(position.x, position.y) + browserCanvasContext.lineTo(targetPoint.x, targetPoint.y) + browserCanvasContext.strokeStyle = 'rgba(' + LINE_STYLE + ', 1)' + browserCanvasContext.setLineDash([1, 9, 2, 8, 3, 7, 4, 6, 0, 500]) + browserCanvasContext.lineWidth = 4 + browserCanvasContext.stroke() + browserCanvasContext.setLineDash([0, 0]) + } + + if (thisObject.container.frame.radius > 0.5) { + let radius = 1 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(targetPoint.x, targetPoint.y, radius, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fill() + } + } + + function drawText () { +/* Text Follows */ + let position = { + x: 0, + y: 0 + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius + /* Label Text */ + let labelPoint + let fontSize = thisObject.payload.floatingObject.currentFontSize + let label + + if (radius > 6) { + const MAX_LABEL_LENGTH = 30 + + label = thisObject.payload.subTitle + label = addIndexNumber(label) + + if (label !== undefined) { + if (label.length > MAX_LABEL_LENGTH) { + label = label.substring(0, MAX_LABEL_LENGTH) + '...' + } + + labelPoint = { + x: position.x - label.length / 2 * fontSize * FONT_ASPECT_RATIO - 5, + y: position.y + radius * 4 / 5 + fontSize * FONT_ASPECT_RATIO + 15 + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = thisObject.payload.floatingObject.labelStrokeStyle + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } + + function drawErrorMessage () { + if (hasError === false) { return } + +/* Text Follows */ + let position = { + x: 0, + y: 0 + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius * 3.5 + /* Label Text */ + let labelPoint + let fontSize = thisObject.payload.floatingObject.currentFontSize * 3 / 4 + let label + + if (radius > 6) { + const MAX_LABEL_LENGTH = 80 + + label = errorMessage + + if (label !== undefined) { + if (label.length > MAX_LABEL_LENGTH) { + label = label.substring(0, MAX_LABEL_LENGTH) + '...' + } + + labelPoint = { + x: position.x - label.length / 2 * fontSize * FONT_ASPECT_RATIO - 5, + y: position.y + radius * 2 / 5 + fontSize * FONT_ASPECT_RATIO + 15 + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } + + function drawValue () { + if (hasValue === false) { return } + +/* Text Follows */ + let position = { + x: 0, + y: 0 + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius * 1.5 + /* Label Text */ + let labelPoint + let fontSize = thisObject.payload.floatingObject.currentFontSize * 6 / 4 + let label + + if (radius > 6) { + const MAX_LABEL_LENGTH = 65 + + label = currentValue + if (!isNaN(label)) { + if (currentValue.toFixed !== undefined) { + label = currentValue.toFixed(2) + } + } + + if (label !== undefined) { + if (label.length > MAX_LABEL_LENGTH) { + label = label.substring(0, MAX_LABEL_LENGTH) + '...' + } + + labelPoint = { + x: position.x - label.length / 2 * fontSize * FONT_ASPECT_RATIO - 5, + y: position.y + radius * 7 / 5 + fontSize * FONT_ASPECT_RATIO + 15 + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TURQUOISE + ', 1)' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } + + function addIndexNumber (label) { + switch (thisObject.payload.node.type) { + case 'Phase': { + let parent = thisObject.payload.parentNode + if (parent !== undefined) { + let granParent = parent.payload.parentNode + if (granParent !== undefined) { + if (granParent.type === 'Initial Definition') { + return label + ' #' + 0 + } + } + for (let i = 0; i < parent.phases.length; i++) { + let phase = parent.phases[i] + if (phase.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } + } + } else { + return label + } + + break + } + case 'Situation': { + let parent = thisObject.payload.parentNode + if (parent !== undefined) { + for (let i = 0; i < parent.situations.length; i++) { + let situation = parent.situations[i] + if (situation.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } + } + } else { + return label + } + + break + } + case 'Condition': { + let parent = thisObject.payload.parentNode + if (parent !== undefined) { + for (let i = 0; i < parent.conditions.length; i++) { + let condition = parent.conditions[i] + if (condition.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } + } + } else { + return label + } + + break + } + default: { + return label + } + } + } + + function drawBodyAndPicture () { + let position = { + x: thisObject.container.frame.position.x, + y: thisObject.container.frame.position.y + } + + position = thisObject.container.frame.frameThisPoint(position) + + let radius = thisObject.container.frame.radius + + if (radius > 0.5) { + let VISIBLE_RADIUS = 5 + + let visiblePosition = { + x: thisObject.container.frame.position.x, + y: thisObject.container.frame.position.y + } + + visiblePosition = thisObject.container.frame.frameThisPoint(visiblePosition) + visiblePosition = thisObject.fitFunction(visiblePosition) + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.fillStyle = thisObject.payload.floatingObject.fillStyle + + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS - 2, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)' + + browserCanvasContext.fill() + + let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition !== undefined) { + if (nodeDefinition.isHierarchyHead === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 2 + let OPACITY = 1 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.LIGHT_GREY + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([4, 16]) + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.LIGHT_GREY + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 1 + browserCanvasContext.setLineDash([20, 20]) + browserCanvasContext.stroke() + } + } + + if (thisObject.isOnFocus === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS - 2, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.BLACK + ', 0.25)' + + browserCanvasContext.fill() + } + + if (hasError === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 1 + let OPACITY = errorMessageCounter / 30 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')' + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([19, 20]) + browserCanvasContext.stroke() + } + + if (isHighlighted === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 1 + let OPACITY = highlightCounter / 30 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([4, 20]) + browserCanvasContext.stroke() + } + + if (isReadyToChainAttach === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 2.5 + readyToChainAttachDisplayCounter - readyToChainAttachDisplayCounter / 2 + let OPACITY = readyToChainAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([10, 90]) + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 5 + browserCanvasContext.setLineDash([5, 45]) + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 1 + browserCanvasContext.setLineDash([2, 8]) + browserCanvasContext.stroke() + } + + if (isAvailableToChainAttach === true && isReadyToChainAttach === false) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 2.5 + let OPACITY = availableToChainAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TURQUOISE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([8, 32]) + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TURQUOISE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 2 + browserCanvasContext.setLineDash([3, 5]) + browserCanvasContext.stroke() + } + + if (isReadyToReferenceAttach === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 2.5 + readyToReferenceAttachDisplayCounter - readyToReferenceAttachDisplayCounter / 2 + let OPACITY = readyToReferenceAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([10, 90]) + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 5 + browserCanvasContext.setLineDash([5, 45]) + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 1 + browserCanvasContext.setLineDash([2, 8]) + browserCanvasContext.stroke() + } + + if (isAvailableToReferenceAttach === true && isReadyToReferenceAttach === false) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 2.5 + let OPACITY = availableToReferenceAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([8, 32]) + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + OPACITY + ')' + browserCanvasContext.lineWidth = 2 + browserCanvasContext.setLineDash([3, 5]) + browserCanvasContext.stroke() + } + } + + /* Image */ + + if (icon !== undefined) { + if (icon.canDrawIcon === true) { + let additionalImageSize = 0 + if (thisObject.isExecuting === true || isReadyToReferenceAttach === true || isReadyToChainAttach === true) { additionalImageSize = 20 } + let totalImageSize = additionalImageSize + thisObject.payload.floatingObject.currentImageSize + + browserCanvasContext.drawImage( + icon, position.x - totalImageSize / 2, + position.y - totalImageSize / 2, + totalImageSize, + totalImageSize) + } + } + + if (executingIcon !== undefined) { + if (executingIcon.canDrawIcon === true) { + if (thisObject.isExecuting === true) { + const DISTANCE_FROM_CENTER = thisObject.container.frame.radius / 3 + 50 + const EXECUTING_ICON_SIZE = 20 + thisObject.container.frame.radius / 6 + + browserCanvasContext.drawImage( + executingIcon, position.x - EXECUTING_ICON_SIZE / 2, + position.y - EXECUTING_ICON_SIZE / 2 - DISTANCE_FROM_CENTER, + EXECUTING_ICON_SIZE, + EXECUTING_ICON_SIZE) + } + } + } + } +} diff --git a/CanvasApp/FloatingSpace/UiObjectConstructor.js b/CanvasApp/FloatingSpace/UiObjectConstructor.js new file mode 100644 index 000000000..ac5ee4dd3 --- /dev/null +++ b/CanvasApp/FloatingSpace/UiObjectConstructor.js @@ -0,0 +1,483 @@ + +function newUiObjectConstructor () { + const MODULE_NAME = 'UI Object Constructor' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + createUiObject: createUiObject, + destroyUiObject: destroyUiObject, + initialize: initialize, + finalize: finalize + } + + let floatingLayer + + return thisObject + + function finalize () { + floatingLayer = undefined + } + + function initialize (pFloatingLayer) { + floatingLayer = pFloatingLayer + } + + function createUiObject (userAddingNew, payload) { + let floatingObject = newFloatingObject() + floatingObject.fitFunction = canvas.floatingSpace.fitIntoVisibleArea + floatingObject.container.connectToParent(canvas.floatingSpace.container, false, false, false, false, false, false, false, false) + floatingObject.initialize('UI Object', payload) + payload.floatingObject = floatingObject + + /* + When this object is created based on a backup, share or clone, we will have a savedPayload that we will use to set the initial properties. + If it is a new object being created out of the user interface, we jusst continue with the construction process. + */ + if (userAddingNew === false && payload.node.type !== 'Workspace') { + let position = { + x: 0, + y: 0 + } + + position = { + x: payload.node.savedPayload.position.x, + y: payload.node.savedPayload.position.y + } + + floatingObject.setPosition(position) + payload.node.savedPayload.position = undefined + if (payload.node.savedPayload.floatingObject.isPinned === true) { + floatingObject.pinToggle() + } + if (payload.node.savedPayload.floatingObject.isFrozen === true) { + floatingObject.freezeToggle() + } + if (payload.node.savedPayload.floatingObject.isCollapsed === true) { + floatingObject.collapseToggle() + } + if (payload.node.savedPayload.floatingObject.angleToParent !== undefined) { + floatingObject.angleToParent = payload.node.savedPayload.floatingObject.angleToParent + } + if (payload.node.savedPayload.floatingObject.distanceToParent !== undefined) { + floatingObject.distanceToParent = payload.node.savedPayload.floatingObject.distanceToParent + } + } + + /* For brand new objects being created directly by the user, we will make them inherit some properties from their parents. */ + if (userAddingNew === true) { + floatingObject.angleToParent = payload.parentNode.payload.floatingObject.angleToParent + floatingObject.distanceToParent = payload.parentNode.payload.floatingObject.distanceToParent + } + + let uiObject = newUiObject() + payload.uiObject = uiObject + uiObject.fitFunction = canvas.floatingSpace.fitIntoVisibleArea + uiObject.isVisibleFunction = canvas.floatingSpace.isThisPointVisible + let menuItemsInitialValues = getMenuItemsInitialValues(uiObject, floatingObject, payload) + + for (let i = 0; i < APP_SCHEMA_ARRAY.length; i++) { + let schemaNode = APP_SCHEMA_ARRAY[i] + if (schemaNode.type === payload.node.type) { + let menuItems = schemaNode.menuItems + for (let j = 0; j < menuItems.length; j++) { + let item = menuItems[j] + item.angle = undefined + if (item.action.indexOf('Add ') >= 0 && item.action.indexOf('Missing') >= 0) { + item.action = 'Add Missing Children' + } + if (item.action.indexOf('Delete ') >= 0) { + // item.action = 'Delete UI Object' + } + if (item.action.indexOf('Configure ') >= 0) { + if (schemaNode.editors.config === true) { + // item.actionFunction = 'uiObject.configEditor.activate' + } + } + if (item.action.indexOf('Edit ') >= 0) { + if (schemaNode.editors.code === true) { + // item.actionFunction = 'uiObject.codeEditor.activate' + } + } + if (item.actionFunction === undefined) { + // item.actionFunction = 'payload.onMenuItemClick' + } + } + } + } + + uiObject.initialize(payload, menuItemsInitialValues) + uiObject.container.connectToParent(floatingObject.container, false, false, true, true, false, false, true, true, true, true, true) + + setFloatingObjectBasicProperties(floatingObject, payload) + + if (payload.node.savedPayload !== undefined) { + if (payload.node.savedPayload.uiObject !== undefined) { + payload.uiObject.shortcutKey = payload.node.savedPayload.uiObject.shortcutKey + } + } + + floatingLayer.addFloatingObject(floatingObject) + + return + } + + function addLeftIcons (menuItemsInitialValues, floatingObject, isPersonalData) { + menuItemsInitialValues.push( + { + action: 'Pin / Unpin', + actionFunction: floatingObject.pinToggle, + actionStatus: floatingObject.getPinStatus, + currentStatus: false, + label: undefined, + visible: true, + iconPathOn: 'menu-fix-pinned', + iconPathOff: 'menu-fix-unpinned', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 1 + } + ) + menuItemsInitialValues.push( + { + action: 'Change Tension Level', + actionFunction: floatingObject.angleToParentToggle, + actionStatus: floatingObject.getAngleToParent, + currentStatus: true, + label: undefined, + visible: true, + icons: ['angle-to-parent-000', 'angle-to-parent-360', 'angle-to-parent-180', 'angle-to-parent-090', 'angle-to-parent-045'], + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 1 + } + ) + menuItemsInitialValues.push( + { + action: 'change Distance to Paarent', + actionFunction: floatingObject.distanceToParentToggle, + actionStatus: floatingObject.getDistanceToParent, + currentStatus: true, + label: undefined, + visible: true, + icons: ['distance-to-parent-000', 'distance-to-parent-025', 'distance-to-parent-050', 'distance-to-parent-100', 'distance-to-parent-150', 'distance-to-parent-200'], + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 1 + } + ) + menuItemsInitialValues.push( + { + action: 'Freeze / Unfreeze', + actionFunction: floatingObject.freezeToggle, + actionStatus: floatingObject.getFreezeStatus, + currentStatus: true, + label: undefined, + visible: true, + iconPathOn: 'menu-mobility-unfreeze', + iconPathOff: 'menu-mobility-freeze', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 1 + } + ) + menuItemsInitialValues.push( + { + action: 'Collapse / Uncollapse', + actionFunction: floatingObject.collapseToggle, + actionStatus: floatingObject.getCollapseStatus, + currentStatus: false, + label: undefined, + visible: true, + iconPathOn: 'menu-tree-plus', + iconPathOff: 'menu-tree-minus', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 1 + } + ) + menuItemsInitialValues.push( + { + action: 'Backup', + actionFunction: floatingObject.payload.onMenuItemClick, + label: undefined, + visible: true, + iconPathOn: 'menu-backup', + iconPathOff: 'menu-backup', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 2 + } + ) + menuItemsInitialValues.push( + { + action: 'Clone', + actionFunction: floatingObject.payload.onMenuItemClick, + label: undefined, + visible: true, + iconPathOn: 'clone', + iconPathOff: 'clone', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 2 + } + ) + if (isPersonalData !== true) { + menuItemsInitialValues.push( + { + action: 'Share', + actionFunction: floatingObject.payload.onMenuItemClick, + label: undefined, + visible: true, + iconPathOn: 'menu-share', + iconPathOff: 'menu-share', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + ring: 2 + } + ) + } + } + + function getMenuItemsInitialValues (uiObject, floatingObject, payload) { + let menuItemsInitialValues = [] + + let nodeDefinition = APP_SCHEMA_MAP.get(payload.node.type) + if (nodeDefinition !== undefined) { + if (nodeDefinition.editors !== undefined) { + if (nodeDefinition.editors.config === true) { + uiObject.configEditor = newConfigEditor() + uiObject.configEditor.isVisibleFunction = uiObject.isVisibleFunction + uiObject.configEditor.initialize() + uiObject.configEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false) + } + if (nodeDefinition.editors.code === true) { + uiObject.codeEditor = newCodeEditor() + uiObject.codeEditor.isVisibleFunction = uiObject.isVisibleFunction + uiObject.codeEditor.initialize() + uiObject.codeEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false) + } + if (nodeDefinition.editors.formula === true) { + uiObject.formulaEditor = newFormulaEditor() + uiObject.formulaEditor.isVisibleFunction = uiObject.isVisibleFunction + uiObject.formulaEditor.initialize() + uiObject.formulaEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false) + } + if (nodeDefinition.editors.condition === true) { + uiObject.conditionEditor = newConditionEditor() + uiObject.conditionEditor.isVisibleFunction = uiObject.isVisibleFunction + uiObject.conditionEditor.initialize() + uiObject.conditionEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false) + } + } + if (nodeDefinition.addLeftIcons === true) { + addLeftIcons(menuItemsInitialValues, floatingObject, nodeDefinition.isPersonalData) + } + if (nodeDefinition.isPinned === true) { + floatingObject.isPinned = true + } + if (nodeDefinition.positionLocked === true) { + floatingObject.positionLocked = true + } + + for (let i = 0; i < nodeDefinition.menuItems.length; i++) { + let menutItemDefinition = nodeDefinition.menuItems[i] + let newMenuItem = JSON.parse(JSON.stringify(menutItemDefinition)) + + /* We need to reference the real function based on its name */ + if (menutItemDefinition.actionFunction !== undefined) { + newMenuItem.actionFunction = eval(menutItemDefinition.actionFunction) + } + + /* Adding default values */ + if (newMenuItem.visible === undefined) { + newMenuItem.visible = true + } + + if (newMenuItem.rawRadius === undefined) { + newMenuItem.rawRadius = 8 + } + + if (newMenuItem.targetRadius === undefined) { + newMenuItem.targetRadius = 0 + } + + if (newMenuItem.currentRadius === undefined) { + newMenuItem.currentRadius = 0 + } + + menuItemsInitialValues.push(newMenuItem) + } + } else { + if (ERROR_LOG === true) { logger.write('[ERROR] getMenuItemsInitialValues -> UI Object Type not Recognized -> type = ' + payload.node.type) } + } + + return menuItemsInitialValues + } + + function setFloatingObjectBasicProperties (floatingObject, payload) { + const FRICTION = 0.95 + const INITIAL_FRICTION = 0.97 + const INITIAL_FONT_SIZE = 12 + + switch (payload.node.type) { + case 'Workspace': { + level_0() + floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_360 + break + } + + default: { + let nodeDefinition = APP_SCHEMA_MAP.get(payload.node.type) + if (nodeDefinition !== undefined) { + switch (nodeDefinition.level) { + case 0: { + level_0() + break + } + case 1: { + level_1() + break + } + case 2: { + level_2() + break + } + case 3: { + level_3() + break + } + case 4: { + level_4() + break + } + case 5: { + level_5() + break + } + } + } + } + } + + function level_0 () { + floatingObject.targetFriction = 0.93 + floatingObject.friction = INITIAL_FRICTION + + floatingObject.initializeMass(500) + floatingObject.initializeRadius(45) + floatingObject.initializeImageSize(80) + floatingObject.initializeFontSize(INITIAL_FONT_SIZE) + + floatingObject.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + } + function level_1 () { + floatingObject.targetFriction = 0.94 + floatingObject.friction = INITIAL_FRICTION + + floatingObject.initializeMass(600) + floatingObject.initializeRadius(45) + floatingObject.initializeImageSize(80) + floatingObject.initializeFontSize(INITIAL_FONT_SIZE) + + floatingObject.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + + if (payload.node.savedPayload === undefined) { + // floatingObject.angleToParentToggle() + } + } + function level_2 () { + floatingObject.targetFriction = FRICTION + floatingObject.friction = INITIAL_FRICTION + + floatingObject.initializeMass(300) + floatingObject.initializeRadius(40) + floatingObject.initializeImageSize(70) + floatingObject.initializeFontSize(INITIAL_FONT_SIZE) + + floatingObject.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', 1)' + + if (payload.node.savedPayload === undefined) { + // floatingObject.angleToParentToggle() + } + } + function level_3 () { + floatingObject.targetFriction = FRICTION + floatingObject.friction = INITIAL_FRICTION + + floatingObject.initializeMass(150) + floatingObject.initializeRadius(35) + floatingObject.initializeImageSize(60) + floatingObject.initializeFontSize(INITIAL_FONT_SIZE) + + floatingObject.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)' + + if (payload.node.savedPayload === undefined) { + // floatingObject.angleToParentToggle() + } + } + function level_4 () { + floatingObject.targetFriction = FRICTION + floatingObject.friction = INITIAL_FRICTION + + floatingObject.initializeMass(75) + floatingObject.initializeRadius(30) + floatingObject.initializeImageSize(50) + floatingObject.initializeFontSize(INITIAL_FONT_SIZE) + + floatingObject.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)' + + if (payload.node.savedPayload === undefined) { + // floatingObject.angleToParentToggle() + } + } + function level_5 () { + floatingObject.targetFriction = FRICTION + floatingObject.friction = INITIAL_FRICTION + + floatingObject.initializeMass(50) + floatingObject.initializeRadius(25) + floatingObject.initializeImageSize(40) + floatingObject.initializeFontSize(INITIAL_FONT_SIZE) + + floatingObject.fillStyle = 'rgba(' + UI_COLOR.RED + ', 1)' + } + function level_6 () { + floatingObject.targetFriction = FRICTION + floatingObject.friction = INITIAL_FRICTION + + floatingObject.initializeMass(25) + floatingObject.initializeRadius(20) + floatingObject.initializeImageSize(30) + floatingObject.initializeFontSize(INITIAL_FONT_SIZE) + + floatingObject.fillStyle = 'rgba(' + UI_COLOR.RED + ', 1)' + } + + floatingObject.labelStrokeStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + } + + function destroyUiObject (payload) { + floatingLayer.removeFloatingObject(payload.floatingObject.handle) + + payload.floatingObject.finalize() + payload.floatingObject = undefined + + payload.uiObject.finalize() + payload.uiObject = undefined + + payload.referenceParent = undefined + payload.parent = undefined + payload.chainParent = undefined + payload.node.savedPayload = undefined + } +} diff --git a/CanvasApp/FloatingSpace/UiObjectTitle.js b/CanvasApp/FloatingSpace/UiObjectTitle.js new file mode 100644 index 000000000..efb8a1da2 --- /dev/null +++ b/CanvasApp/FloatingSpace/UiObjectTitle.js @@ -0,0 +1,298 @@ + +function newUiObjectTitle () { + const MODULE_NAME = 'UI Object Title' + const ERROR_LOG = true + + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + isVisibleFunction: undefined, + allwaysVisible: undefined, + editMode: undefined, + container: undefined, + payload: undefined, + enterEditMode: enterEditMode, + exitEditMode: exitEditMode, + physics: physics, + draw: draw, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = true + thisObject.container.isDraggeable = false + thisObject.container.frame.radius = 0 + + let selfFocusEventSubscriptionId + let selfNotFocuskEventSubscriptionId + let selfMouseClickEventSubscriptionId + + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfNotFocuskEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.payload = undefined + thisObject.isVisibleFunction = undefined + } + + function initialize (payload) { + thisObject.payload = payload + + thisObject.allwaysVisible = false // Default value + let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition !== undefined) { + if (nodeDefinition.isTitleAllwaysVisible === true) { + thisObject.allwaysVisible = true + } + } + + selfFocusEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onFocus', onFocus) + selfNotFocuskEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onNotFocus', onNotFocus) + selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + } + + function getContainer (point) { + if (AT_FULL_SCREEN_MODE === true) { return } // Fullscreen mode does not allow INPUT elements + + let container + + if (thisObject.editMode === true) { + if (point.x === VERY_LARGE_NUMBER) { + /* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that + anyone can react. In our case, this object has an html input element that is not part of the canvas, so the event is + triggered. We compensate recognizing this coordinate and returning our container. */ + return thisObject.container + } + } + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function physics () { + if (thisObject.payload.title === undefined) { return } + + /* It is possible to override the default title by setting the APP SCHEMA property 'title' */ + let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition.title !== undefined) { + thisObject.payload.title = '' + thisObject.payload.node.name = '' + let separator = '' + for (let i = 0; i < nodeDefinition.title.length; i++) { + let titleReference = nodeDefinition.title[i] + switch (titleReference) { + case 'Use Reference Parent': { + let nodeToUse = thisObject.payload.node.payload.referenceParent + if (nodeToUse !== undefined && nodeToUse.payload !== undefined) { + thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title + thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name + } + break + } + case 'Use Reference Grandparent': { + let nodeToUse = thisObject.payload.node.payload.referenceParent + if (nodeToUse !== undefined && nodeToUse.payload !== undefined) { + nodeToUse = thisObject.payload.node.payload.referenceParent.payload.referenceParent + if (nodeToUse !== undefined) { + thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title + thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name + } + } + break + } + case 'Use Reference Parent Parent': { + let nodeToUse = thisObject.payload.node.payload.referenceParent + if (nodeToUse !== undefined && nodeToUse.payload !== undefined) { + nodeToUse = thisObject.payload.node.payload.referenceParent.payload.parentNode + if (nodeToUse !== undefined && nodeToUse.payload !== undefined) { + thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title + thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name + } + } + break + } + case 'Use Reference Parent Parent Parent': { + let nodeToUse = thisObject.payload.node.payload.referenceParent + if (nodeToUse !== undefined && nodeToUse.payload !== undefined) { + nodeToUse = thisObject.payload.node.payload.referenceParent.payload.parentNode + if (nodeToUse !== undefined && nodeToUse.payload !== undefined) { + nodeToUse = thisObject.payload.node.payload.referenceParent.payload.parentNode.payload.parentNode + if (nodeToUse !== undefined && nodeToUse.payload !== undefined) { + thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title + thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name + } + } + } + break + } + } + separator = ' ' + } + } + + let title = trimTitle(thisObject.payload.title) + + /* Here we set the dimensions and position of this object */ + const FRAME_HEIGHT = 25 + const Y_OFFSET = -20 + const FRAME_WIDTH = (title.length + 2) / 2 * thisObject.payload.floatingObject.currentFontSize * FONT_ASPECT_RATIO * 1.2 * 2 + thisObject.container.frame.position.x = 0 - FRAME_WIDTH / 2 + thisObject.container.frame.position.y = 0 - thisObject.container.frame.radius * 1 / 2 - thisObject.payload.floatingObject.currentFontSize * FONT_ASPECT_RATIO + Y_OFFSET - FRAME_HEIGHT + + thisObject.container.frame.width = FRAME_WIDTH + thisObject.container.frame.height = FRAME_HEIGHT + + if (thisObject.editMode === true) { + let inputPosition = { + x: 0, + y: 0 + CURRENT_TOP_MARGIN + Y_OFFSET + } + + inputPosition = thisObject.container.frame.frameThisPoint(inputPosition) + + if (inputPosition.y < CURRENT_TOP_MARGIN) { exitEditMode() } + + let inputDiv = document.getElementById('inputDiv') + inputDiv.style = 'position:absolute; top:' + inputPosition.y + 'px; left:' + inputPosition.x + 'px; z-index:1; ' + } + } + + function onFocus () { + thisObject.isOnFocus = true + } + + function onNotFocus () { + thisObject.isOnFocus = false + exitEditMode() + } + + function onMouseClick (event) { + let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type) + if (nodeDefinition.title !== undefined) { return } + + let checkPoint = { + x: 0, + y: 0 + } + checkPoint = thisObject.container.frame.frameThisPoint(checkPoint) + + if (thisObject.isVisibleFunction(checkPoint) === true) { + enterEditMode() + } + } + + function exitEditMode () { + if (thisObject.editMode === true) { + thisObject.editMode = false + let input = document.getElementById('input') + input.style.display = 'none' + if (input.value.length > 2) { + thisObject.payload.title = input.value + thisObject.payload.node.name = input.value + } + } + } + + function enterEditMode () { + const WIDTH = thisObject.container.frame.width + const HEIGHT = thisObject.container.frame.height + let fontSize = thisObject.payload.floatingObject.currentFontSize + + thisObject.editMode = true + let input = document.getElementById('input') + input.value = thisObject.payload.title + + let backgroundColor = '0, 0, 0' + if (thisObject.payload.uiObject.codeEditor !== undefined) { + if (thisObject.payload.uiObject.codeEditor.visible === true) { + backgroundColor = '204, 88, 53' + } + } + + input.style = 'resize: none; border: none; outline: none; box-shadow: none; overflow:hidden; font-family: Saira Condensed; font-size: ' + fontSize + 'px; background-color: rgb(' + backgroundColor + ');color:rgb(255, 255, 255); width: ' + WIDTH + 'px; height: ' + HEIGHT + 'px' + input.style.display = 'block' + input.focus() + } + + function draw () { + if (thisObject.isOnFocus === true || thisObject.allwaysVisible === true) { + if (thisObject.payload.uiObject.codeEditor !== undefined) { + if (thisObject.payload.uiObject.codeEditor.visible !== true) { + drawTitleBackground() + } + } else { + drawTitleBackground() + } + + drawText() + } + } + + function drawTitleBackground () { + if (thisObject.editMode !== true) { + let params = { + cornerRadius: 3, + lineWidth: 0.1, + container: thisObject.container, + borderColor: UI_COLOR.BLACK, + backgroundColor: UI_COLOR.BLACK, + castShadow: false, + opacity: 0.25 + } + + roundedCornersBackground(params) + } + } + + function drawText () { + if (thisObject.editMode !== true) { + let radius = thisObject.container.frame.radius + let labelPoint + let fontSize = thisObject.payload.floatingObject.currentFontSize + let label + + if (radius > 6 && (thisObject.isOnFocus === true || thisObject.allwaysVisible === true)) { + browserCanvasContext.strokeStyle = thisObject.payload.floatingObject.labelStrokeStyle + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + + label = thisObject.payload.title + + if (label !== undefined) { + label = trimTitle(label) + + labelPoint = { + x: 5, + y: thisObject.container.frame.height * 0.8 + } + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint) + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = thisObject.payload.floatingObject.labelStrokeStyle + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + } + } + } + + function trimTitle (title) { + if (title === undefined) { return } + const MAX_LABEL_LENGTH = 50 + if (title.length > MAX_LABEL_LENGTH) { + title = title.substring(0, MAX_LABEL_LENGTH) + '...' + } + return title + } +} diff --git a/CanvasApp/Frame.js b/CanvasApp/Frame.js new file mode 100644 index 000000000..3f8bb1590 --- /dev/null +++ b/CanvasApp/Frame.js @@ -0,0 +1,511 @@ + +function newFrame () { + const PANEL_CORNERS_RADIUS = 5 + const TITLE_BAR_HEIGHT = 15 // this must be grater than radius + + let thisObject = { + type: 'Rectangle', + containerName: '', // This is for debugging purposes only. + parentFrame: undefined, // Here we store the parent cointainer zoom object. + radius: 0, + offset: undefined, + position: undefined, + container: undefined, + width: browserCanvas.width, + height: browserCanvas.height, + getBodyHeight: getBodyHeight, + draw: draw, + fitIntoFrame: fitIntoFrame, + frameThisPoint: frameThisPoint, // This function changes the actual frame coordinate system to the screen coordinate system. + unframeThisPoint: unframeThisPoint, + isThisPointHere: isThisPointHere, // This function return true is the point received as parameter lives within this frame. + isThisScreenPointHere: isThisScreenPointHere, + isInViewPort: isInViewPort, + initialize: initialize, + finalize: finalize + } + + return thisObject + + function finalize () { + thisObject.parentFrame = undefined + thisObject.position = undefined + thisObject.offset = undefined + } + + function initialize (pType) { + if (pType !== undefined) { thisObject.type = pType } + + thisObject.position = { + x: 0, + y: 0 + } + + thisObject.offset = { + x: 0, + y: 0 + } + } + + function getBodyHeight () { + return thisObject.height - TITLE_BAR_HEIGHT + } + + function isInViewPort () { + /* This function is usefull to know if the object who has this frame is currently appearing at least in part at the canvas.chartingSpace.viewport */ + + point1 = { + x: 0, + y: 0 + } + + point3 = { + x: thisObject.width, + y: thisObject.height + } + + /* Now the transformations. */ + + point1 = transformThisPoint(point1, thisObject.container) + point3 = transformThisPoint(point3, thisObject.container) + + if (point1.x < canvas.chartingSpace.viewport.visibleArea.topRight.x && point1.y < canvas.chartingSpace.viewport.visibleArea.bottomRight.y && point3.x > canvas.chartingSpace.viewport.visibleArea.bottomLeft.x && point3.y > canvas.chartingSpace.viewport.visibleArea.topLeft.y) { + return true + } else { + return false + } + } + + function frameThisPoint (point) { + /* We need not to modify the point received, so me make a copy of it. */ + + let checkPoint = { + x: point.x, + y: point.y + } + + let parentPoint = { + x: 0, + y: 0 + } + + let returnPoint = { + x: 0, + y: 0 + } + + if (thisObject.parentFrame !== undefined) { + parentPoint = thisObject.parentFrame.frameThisPoint(checkPoint) + + returnPoint.x = parentPoint.x + thisObject.position.x + thisObject.offset.x + returnPoint.y = parentPoint.y + thisObject.position.y + thisObject.offset.y + } else { + returnPoint.x = checkPoint.x + thisObject.position.x + thisObject.offset.x + returnPoint.y = checkPoint.y + thisObject.position.y + thisObject.offset.y + } + + return returnPoint + } + + function unframeThisPoint (point) { + /* We need not to modify the point received, so me make a copy of it. */ + + let checkPoint = { + x: point.x, + y: point.y + } + + let parentPoint = { + x: 0, + y: 0 + } + + let returnPoint = { + x: 0, + y: 0 + } + + if (thisObject.parentFrame !== undefined) { + parentPoint = thisObject.parentFrame.unframeThisPoint(checkPoint) + + returnPoint.x = parentPoint.x - thisObject.position.x + returnPoint.y = parentPoint.y - thisObject.position.y + } else { + returnPoint.x = checkPoint.x - thisObject.position.x + returnPoint.y = checkPoint.y - thisObject.position.y + } + + return returnPoint + } + + function fitIntoFrame (point) { + /* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */ + + if (point.x > thisObject.width) { + point.x = thisObject.width + } + + if (point.x < 0) { + point.x = 0 + } + + if (point.y > thisObject.height) { + point.y = thisObject.height + } + + if (point.y < 0) { + point.y = 0 + } + + return point + } + + function isThisPointHere (point, outsideViewPort, dontTransform, padding) { + // The second parameter is usefull when you want to check a point that you already know is outside the viewport. + // The padding is a distance to the borders of the container. Can be either negative (outside, to increase the cointainer size) or positive (inside to decrease the cointainer size) + + if (padding === undefined) { padding = 0 } + + /* We need not to modify the point received, so me make a copy of it. */ + let 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 (dontTransform === false || dontTransform === undefined) { + if (outsideViewPort === true) { + checkPoint = thisObject.container.frame.unframeThisPoint(checkPoint) + } else { + checkPoint = unTransformThisPoint(checkPoint, thisObject.container) + } + } + + /* Now we check if the resulting point is whin the current Frame. */ + + if (thisObject.type === 'Circle') { + let distance = Math.sqrt(Math.pow(thisObject.position.x - point.x, 2) + Math.pow(thisObject.position.y - point.y, 2)) + + if (distance < thisObject.radius) { + return true + } else { + return false + } + } + + if (thisObject.type === 'Rectangle') { + if (checkPoint.x < 0 + padding || checkPoint.y < 0 + padding || checkPoint.x > thisObject.width - padding || checkPoint.y > thisObject.height - padding) { + return false + } else { + return true + } + } + } + + function isThisScreenPointHere (point) { + let checkPoint = { + x: point.x, + y: point.y + } + + let thisPoint = { + x: thisObject.position.x, + y: thisObject.position.y + } + + if (thisObject.parentFrame !== undefined) { + thisPoint = thisObject.parentFrame.frameThisPoint(thisPoint) + } + + /* Now we check if the resulting point is whin the current Frame. */ + + if (thisObject.type === 'Circle') { + let distance = Math.sqrt(Math.pow(thisPoint.x - checkPoint.x, 2) + Math.pow(thisPoint.y - checkPoint.y, 2)) + + if (distance < thisObject.radius) { + return true + } else { + return false + } + } + + if (thisObject.type === 'Rectangle') { + if (checkPoint.x < 0 || checkPoint.y < 0 || checkPoint.x > thisObject.width || checkPoint.y > thisObject.height) { + return false + } else { + return true + } + } + } + + function draw (drawGrid, drawBorders, drawBackground, fitFunction, style) { + if (drawBorders === true) { + borders(fitFunction, style) + } + + if (drawGrid === true) { + grid(fitFunction) + } + + if (drawBackground === true) { + background(fitFunction) + } + } + + function background (fitFunction) { + let params = { + cornerRadius: 5, + lineWidth: 0.1, + opacity: 0.75, + container: thisObject.container, + borderColor: UI_COLOR.DARK, + backgroundColor: UI_COLOR.WHITE, + fitFunction: fitFunction + } + + roundedCornersBackground(params) + + titleBarPoint1 = { + x: 0, + y: TITLE_BAR_HEIGHT + } + + titleBarPoint2 = { + x: thisObject.width, + y: TITLE_BAR_HEIGHT + } + + /* Now the transformations. */ + + titleBarPoint1 = frameThisPoint(titleBarPoint1) + titleBarPoint2 = frameThisPoint(titleBarPoint2) + + if (fitFunction !== undefined) { + titleBarPoint1 = fitFunction(titleBarPoint1) + titleBarPoint2 = fitFunction(titleBarPoint2) + } + + /* We paint the title bar now */ + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)' + browserCanvasContext.beginPath() + + browserCanvasContext.moveTo(titleBarPoint1.x, titleBarPoint1.y) + browserCanvasContext.lineTo(borderPoint1.x - PANEL_CORNERS_RADIUS, borderPoint1.y) + browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, PANEL_CORNERS_RADIUS, 1.0 * Math.PI, 1.5 * Math.PI) + browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - PANEL_CORNERS_RADIUS) + browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, PANEL_CORNERS_RADIUS, 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(' + UI_COLOR.MANGANESE_PURPLE + ', 0.75)' + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke() + + /* print the title */ + + let labelPoint + let fontSize = 10 + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + + let label = thisObject.containerName + + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + let yOffset = (TITLE_BAR_HEIGHT - fontSize) / 2 + 2 + + labelPoint = { + x: thisObject.width / 2 - xOffset, + y: TITLE_BAR_HEIGHT - yOffset + } + + labelPoint = thisObject.frameThisPoint(labelPoint) + + if (fitFunction !== undefined) { + labelPoint = fitFunction(labelPoint) + } + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + + function borders (fitFunction, style) { + /* Lest get the important points of the drawing so as to apply the needed transformations. */ + + let point1 + let point2 + let point3 + let point4 + + point1 = { + x: 0, + y: 0 + } + + point2 = { + x: thisObject.width, + y: 0 + } + + point3 = { + x: thisObject.width, + y: thisObject.height + } + + point4 = { + x: 0, + y: thisObject.height + } + + /* Now the transformations. */ + point1 = transformThisPoint(point1, thisObject.container) + point2 = transformThisPoint(point2, thisObject.container) + point3 = transformThisPoint(point3, thisObject.container) + point4 = transformThisPoint(point4, thisObject.container) + + if (fitFunction !== undefined) { + point1 = fitFunction(point1) + point2 = fitFunction(point2) + point3 = fitFunction(point3) + point4 = fitFunction(point4) + } + + /* 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(' + style.color + ', ' + style.opacity + ')' + browserCanvasContext.lineWidth = style.lineWidth + browserCanvasContext.setLineDash(style.lineDash) + browserCanvasContext.stroke() + + browserCanvasContext.closePath() + } + + function grid (fitFunction, smallLines) { + if (smallLines === true) { + /* Small Lines */ + + let step = thisObject.width / 100 + + browserCanvasContext.beginPath() + + for (let i = 0; i < thisObject.width; i = i + step) { + for (let j = 0; j < thisObject.height; j = j + step) { + let point1 = { + x: 0, + y: j + } + + let point2 = { + x: thisObject.width, + y: j + } + + point1 = transformThisPoint(point1, thisObject.container) + point2 = transformThisPoint(point2, thisObject.container) + + if (fitFunction !== undefined) { + point1 = fitFunction(point1) + point2 = fitFunction(point2) + } + + browserCanvasContext.moveTo(point1.x, point1.y) + browserCanvasContext.lineTo(point2.x, point2.y) + } + + let point3 = { + x: thisObject.width, + y: thisObject.height + } + + let point4 = { + x: 0, + y: thisObject.height + } + + point3 = transformThisPoint(point3, thisObject.container) + point4 = transformThisPoint(point4, thisObject.container) + + if (fitFunction !== undefined) { + point3 = fitFunction(point3) + point4 = fitFunction(point4) + } + + 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.setLineDash([0, 0]) + browserCanvasContext.stroke() + } + + /* Main Lines */ + + let step = thisObject.width / 20 + + browserCanvasContext.beginPath() + + for (let i = 0; i < thisObject.width; i = i + step) { + for (let j = 0; j < thisObject.height; j = j + step) { + let point1 = { + x: 0, + y: j + } + + let point2 = { + x: thisObject.width, + y: j + } + + point1 = transformThisPoint(point1, thisObject.container) + point2 = transformThisPoint(point2, thisObject.container) + + if (fitFunction !== undefined) { + point1 = fitFunction(point1) + point2 = fitFunction(point2) + } + + browserCanvasContext.moveTo(point1.x, point1.y) + browserCanvasContext.lineTo(point2.x, point2.y) + } + + let point3 = { + x: i, + y: 0 + } + + let point4 = { + x: i, + y: thisObject.height + } + + point3 = transformThisPoint(point3, thisObject.container) + point4 = transformThisPoint(point4, thisObject.container) + + if (fitFunction !== undefined) { + point3 = fitFunction(point3) + point4 = fitFunction(point4) + } + + 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.setLineDash([0, 0]) + browserCanvasContext.stroke() + } +} diff --git a/CanvasApp/Globals.js b/CanvasApp/Globals.js new file mode 100644 index 000000000..24454d260 --- /dev/null +++ b/CanvasApp/Globals.js @@ -0,0 +1,221 @@ + + /* User Interface Colors */ + +const CANVAS_APP_NAME = 'Canvas App' + +const SUPPORTED_EXCHANGES = ["Poloniex"]; +const SUPPORTED_MARKETS = [ + { + baseAsset: "USDT", + quotedAsset: "BTC", + } +]; +const DEFAULT_EXCHANGE = "Poloniex"; +const DEFAULT_MARKET = { + baseAsset: "USDT", + quotedAsset: "BTC", +}; + +UI_COLOR = { + DARK: "48, 48, 54", + LIGHT: "234, 226, 222", + GREY: "150, 150, 150", + LIGHT_GREY: "247, 247, 247", + WHITE: "255, 255, 255", + BLACK: "0, 0, 0", + GOLDEN_ORANGE: "240, 162, 2", + RUSTED_RED: "204, 88, 53", + GREEN: "188, 214, 67", + RED: "223, 70, 60", + PATINATED_TURQUOISE: "27,153,139", + TITANIUM_YELLOW: "244,228,9", + MANGANESE_PURPLE: "91,80,122", + TURQUOISE: "74,207,217", + DARK_TURQUOISE: "2,149,170" +}; + +/* User Interface Fonts */ + +UI_FONT = { + PRIMARY: "Saira Condensed", + SECONDARY: "Source Code Pro" +}; + +const DEBUG = {} +const ZOOM_OUT_THRESHOLD_FOR_HIDDING_PANELS = 5 // This regulates some behavious changes that happens when the user zoom out below this level. +const ZOOM_OUT_THRESHOLD_FOR_CHANGING_TIME_FRAME = 5 // This help regulate when to change the Time Frame base on the level of zoom out. +const ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT = 5 + +let FONT_ASPECT_RATIO = 0.32; + + +const GET_CONTAINER_PURPOSE = { + MOUSE_OVER: 1, + MOUSE_WHEEL: 2, + MOUSE_CLICK: 3, + DRAGGING: 4 +} + +const ANGLE_TO_PARENT = { + NOT_FIXED: 0, + RANGE_360: 1, + RANGE_180: 2, + RANGE_90: 3, + RANGE_45: 4 +} + +const DISTANCE_TO_PARENT = { + NOT_FIXED: 0, + PARENT_025X: 1, + PARENT_050X: 2, + PARENT_100X: 3, + PARENT_150X: 4, + PARENT_200X: 5 +} + +/* User Interface Panels */ + +UI_PANEL = { + WIDTH: { + SMALL: 100, + NORMAL: 150, + MEDIUM: 200, + LARGE: 250, + X_LARGE: 350 + }, + HEIGHT: { + SMALL: 65, + MEDIUM: 90, + NORMAL: 300, + LARGE: 450 + } +}; + + +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); + +SHOW_ANIMATION_PERFORMACE = false; + +let testUser = window.localStorage.getItem("test_user") +let LOGGED_IN_USER_LOCAL_STORAGE_KEY +let LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY + +if (testUser !== null) { + LOGGED_IN_USER_LOCAL_STORAGE_KEY = "test_user" + LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY = "test_access_token" +} else { + LOGGED_IN_USER_LOCAL_STORAGE_KEY = "user" + LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY = "access_token" +} + +const DEFAULT_CONFIG_TEXT = '// Write your config here' +const DEFAULT_FORMULA_TEXT = '// Write your formula here' +const DEFAULT_CODE_TEXT = '// Write your code here' + +const MAX_DEFAULT_RATE_SCALE_VALUE = 35000; // 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; + +let NEW_SESSION_INITIAL_DATE = new Date(); // This value will be overwritten at the canvas.chartingSpace.viewport.initialize if the user had a prevous session with this same browser. +let INITIAL_ZOOM_LEVEL = -28.25 // This is the zoom level at the view port in which the APP starts. +let INITIAL_TIME_PERIOD = ONE_DAY_IN_MILISECONDS // This value will be overwritten at the canvas.chartingSpace.viewport.initialize if the user had a prevous session with this same browser. +let VERY_LARGE_NUMBER = 100000000000000 + +let CURRENT_TOP_MARGIN = window.canvasApp.topMargin +let AT_FULL_SCREEN_MODE = false + +let maxDate = new Date(); +maxDate.setMilliseconds(0); +maxDate.setDate(maxDate.getDate() + 365 * 1); // We might have charts that projects data into the future. + +const MIN_PLOTABLE_DATE = new Date(2015, 0, 1, 0, 0, 0); +const MAX_PLOTABLE_DATE = maxDate; + +const TOP_SPACE_HEIGHT = 40 +const COCKPIT_SPACE_HEIGHT = 30; +const BREAKPOINT_HEIGHT = 15; +const SIDE_PANEL_WIDTH = 450 +let COCKPIT_SPACE_POSITION = browserCanvas.height - COCKPIT_SPACE_HEIGHT + +const LAYER_STATUS = { + ON: 'on', + LOADING: 'loading', + OFF: 'off' +}; + +let spawnPosition // this is used in several places. + +/* 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"; + +/* +We define here the size of the chartingSpace. It has to bee enough big in order to accomodate all the charts we expect to display in this space. +*/ + +const TIME_MACHINE_WIDTH = 320; +const TIME_MACHINE_HEIGHT = 120; + +let mediaRecorder // to downloadText canvas animation as a mediaRecorder +let areWeRecording = false + +function toRadians(angle) { + return angle * (Math.PI / 180); +} diff --git a/CanvasApp/Panels/Layer.js b/CanvasApp/Panels/Layer.js new file mode 100644 index 000000000..f17574b6c --- /dev/null +++ b/CanvasApp/Panels/Layer.js @@ -0,0 +1,531 @@ +function newLayer () { + const MODULE_NAME = 'Layer' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + container: undefined, + status: LAYER_STATUS.OFF, + fitFunction: undefined, + payload: undefined, + definition: undefined, + panels: undefined, + physics: physics, + draw: draw, + turnOff: turnOff, + turnOn: turnOn, + setDatetime: setDatetime, + setTimeFrame: setTimeFrame, + onMarketFileLoaded: onMarketFileLoaded, + onDailyFileLoaded: onDailyFileLoaded, + onSingleFileLoaded: onSingleFileLoaded, + onFileSequenceLoaded: onFileSequenceLoaded, + getContainer: getContainer, // returns the inner most container that holds the point received by parameter. + initialize: initialize, + finalize: finalize + } + + 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(45, 232, 28, @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 timeFrame = INITIAL_TIME_PERIOD + let datetime = NEW_SESSION_INITIAL_DATE + + /* Add an Event Handler */ + + thisObject.eventHandler = newEventHandler() + let imagesLoaded = 0 + let onMouseClickEventSuscriptionId + let panelsVisibleButton + + return thisObject + + function finalize () { + thisObject.container.finalize() + thisObject.eventHandler.finalize() + thisObject.container = undefined + thisObject.eventHandler = undefined + thisObject.status = undefined + thisObject.fitFunction = undefined + thisObject.payload = undefined + thisObject.definition = undefined + thisObject.panels = undefined + + timeFrame = undefined + datetime = undefined + + LOADING_FILL_STYLE = undefined + LOADED_FILL_STYLE = undefined + UNLOADED_FILL_STYLE = undefined + + LOADING_STROKE_STYLE = undefined + LOADED_STROKE_STYLE = undefined + UNLOADED_STROKE_STYLE = undefined + + marketFileProgressBar = undefined + dailyFileProgressBar = undefined + singleFileProgressBar = undefined + fileSequenceProgressBar = undefined + + panelsVisibleButton = undefined + + thisObject.container.eventHandler.stopListening(onMouseClickEventSuscriptionId) + } + + function initialize (callBackFunction) { + /* Create this objects continer */ + try { + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME + ' ' + thisObject.payload.node.id) + thisObject.container.isDraggeable = false + thisObject.container.isClickeable = true + + /* Lets set the basic dimensions of this thisObject. */ + let position = { + x: 0, + y: 0 + } + + thisObject.container.frame.position = position + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL + thisObject.container.frame.height = 70 + + let functionLibraryProtocolNode = newProtocolNode() + let lightingPath = + '->Layer->' + + 'Data Product->Single Market Data->' + + 'Session Independent Data->Session Based Data->' + + 'Session Reference->Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->' + + 'Market->Market Base Asset->Asset->' + + 'Market Quoted Asset->Asset->' + + 'Exchange Markets->Crypto Exchange->' + + 'Product Definition->' + + 'Sensor Bot->Indicator Bot->Trading Bot->' + + 'Data Mine->' + + 'Dataset Definition->' + + 'Record Definition->Record Property->Formula->' + + 'Data Building Procedure->Procedure Loop->Javascript Code->Procedure Initialization->Javascript Code->' + + 'Calculations Procedure->Procedure Loop->Javascript Code->Procedure Initialization->Javascript Code->' + + 'Plotter Module->Plotter->' + + 'Shapes->' + + 'Chart Points->Point->Point Formula->' + + 'Polygon->Polygon Body->Style->Style Condition->Style->' + + 'Polygon Border->Style->Style Condition->Style->' + + 'Polygon Vertex->Point->' + + 'Plotter Panel->Javascript Code->Panel Data->Data Formula->' + thisObject.definition = functionLibraryProtocolNode.getProtocolNode(thisObject.payload.node, false, true, true, false, false, lightingPath) + + /* Here we validate that we have all the needed information */ + if (thisObject.definition.referenceParent === undefined) { return } + if (thisObject.definition.referenceParent.parentNode === undefined) { return } + if (thisObject.definition.referenceParent.parentNode.referenceParent === undefined) { return } + if (thisObject.definition.referenceParent.parentNode.referenceParent.parentNode === undefined) { return } + if (thisObject.definition.referenceParent.parentNode.referenceParent.parentNode.parentNode === undefined) { return } + if (thisObject.definition.referenceParent.parentNode.referenceParent.baseAsset === undefined) { return } + if (thisObject.definition.referenceParent.parentNode.referenceParent.baseAsset.referenceParent === undefined) { return } + if (thisObject.definition.referenceParent.parentNode.referenceParent.quotedAsset === undefined) { return } + if (thisObject.definition.referenceParent.parentNode.referenceParent.quotedAsset.referenceParent === undefined) { return } + if (thisObject.definition.referenceParent.referenceParent === undefined) { return } + if (thisObject.definition.referenceParent.referenceParent.parentNode === undefined) { return } + if (thisObject.definition.referenceParent.referenceParent.parentNode.parentNode === undefined) { return } + + /* Lets listen to our own events to react when we have a Mouse Click */ + onMouseClickEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + } + } + + function getContainer (point) { + if (panelsVisibleButton !== undefined) { + let container = panelsVisibleButton.getContainer(point) + if (container !== undefined) { + return container + } + } + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } + } + + 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 (timeFrame <= _1_HOUR_IN_MILISECONDS) { + dailyFileProgressBar.animatedValue = 0 + } + } + } + + function setTimeFrame (pTimeFrame) { + /* + 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 (timeFrame !== pTimeFrame) { + timeFrame = pTimeFrame + + if (timeFrame <= _1_HOUR_IN_MILISECONDS) { + dailyFileProgressBar.animatedValue = 0 + } + } + } + + function onMarketFileLoaded (event) { + marketFileProgressBar.value = Math.trunc(event.totalValue * 100 / event.totalValue) + marketFileProgressBar.fillStyle = LOADING_FILL_STYLE + marketFileProgressBar.strokeStyle = LOADING_STROKE_STYLE + + if (marketFileProgressBar.value > 100) { marketFileProgressBar.value = 100 } + } + + 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 } + } + + 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 } + } + + 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 } + } + + function turnOff () { + if (thisObject.status !== LAYER_STATUS.OFF) { + resetProgressBars() + changeStatusTo(LAYER_STATUS.OFF) + } + } + + function turnOn () { + if (thisObject.status === LAYER_STATUS.OFF) { + resetProgressBars() + changeStatusTo(LAYER_STATUS.LOADING) + } + } + + function onMouseClick (event) { + switch (thisObject.status) { + case LAYER_STATUS.ON: + resetProgressBars() + changeStatusTo(LAYER_STATUS.OFF) + break + case LAYER_STATUS.OFF: + resetProgressBars() + changeStatusTo(LAYER_STATUS.LOADING) + break + case LAYER_STATUS.LOADING: + changeStatusTo(LAYER_STATUS.OFF) + break + } + } + + function resetProgressBars () { + 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 + } + + function physics () { + statusPhysics() + childrenPhysics() + } + + function childrenPhysics () { + /* Panels Visible Button Setup */ + if (thisObject.status === LAYER_STATUS.ON) { + if (panelsVisibleButton === undefined) { + if (thisObject.panels !== undefined) { + if (thisObject.panels.length > 0) { + panelsVisibleButton = newPanelsVisibleButton() + + let storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'showPanels') + if (storedValue !== undefined) { + if (storedValue === true || storedValue === false) { + panelsVisibleButton.showPanels = storedValue + } + } + + panelsVisibleButton.initialize(thisObject.panels) + panelsVisibleButton.container.connectToParent(thisObject.container) + } + } + } else { + savePropertyAtNodeConfig(thisObject.payload, 'showPanels', panelsVisibleButton.showPanels) + } + } + } + + function statusPhysics () { + /* We retrieve the stored status at the config. */ + try { + let storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'status') + + if (storedValue !== undefined) { + if (storedValue !== thisObject.status) { + if (storedValue === LAYER_STATUS.ON) { + resetProgressBars() + changeStatusTo(LAYER_STATUS.LOADING) + } + if (storedValue === LAYER_STATUS.OFF) { + resetProgressBars() + changeStatusTo(LAYER_STATUS.OFF) + } + } + } else { + resetProgressBars() + changeStatusTo(LAYER_STATUS.OFF) + } + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + + /* Check when the loading finishes */ + if ( + thisObject.status === LAYER_STATUS.LOADING && + marketFileProgressBar.value === 100 && + dailyFileProgressBar.value === 100 + ) { + changeStatusTo(LAYER_STATUS.ON) + } + } + + function changeStatusTo (newStatus) { + if (thisObject.status !== newStatus) { + thisObject.status = newStatus + + if (thisObject.status === LAYER_STATUS.OFF) { + if (panelsVisibleButton !== undefined) { + panelsVisibleButton.finalize() + panelsVisibleButton = undefined + } + } + + savePropertyAtNodeConfig(thisObject.payload, 'status', thisObject.status) + let eventData = thisObject + thisObject.container.eventHandler.raiseEvent('Status Changed', eventData) + } + } + + function draw () { + drawLayer() + if (panelsVisibleButton !== undefined) { + panelsVisibleButton.draw() + } + } + + function drawLayer () { + drawLayerDisplay() + } + + function drawProgressBar (progressBar, lineWidth, offsetY) { + const ANIMATED_INCREMENT = 5 + const OPACITY_INCREMENT = 0.01 + const OPACITY_MIN = 1 + + let point1 + let point2 + let horizontalMargin = 12 + + if (progressBar.value === 0) { + return + } + + /* Animate */ + if (progressBar.animatedValue < progressBar.value) { + progressBar.animatedValue = progressBar.animatedValue + ANIMATED_INCREMENT + progressBar.opacity = progressBar.opacity + OPACITY_INCREMENT + } + + if (progressBar.animatedValue >= 100) { + progressBar.animatedValue = 100 + + progressBar.opacity = progressBar.opacity - OPACITY_INCREMENT + if (progressBar.opacity < OPACITY_MIN) { progressBar.opacity = OPACITY_MIN } + + progressBar.strokeStyle = LOADED_STROKE_STYLE.replace('@Opacity', progressBar.opacity.toString()) + } + + point1 = { + x: 0 + horizontalMargin, + y: thisObject.container.frame.height + offsetY + } + + point2 = { + x: (thisObject.container.frame.width - horizontalMargin * 2) * progressBar.animatedValue / 100 + horizontalMargin, + y: thisObject.container.frame.height + offsetY + } + + /* Now the transformations. */ + point1 = thisObject.container.frame.frameThisPoint(point1) + point2 = thisObject.container.frame.frameThisPoint(point2) + + point1 = thisObject.fitFunction(point1) + point2 = thisObject.fitFunction(point2) + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(point1.x, point1.y) + browserCanvasContext.lineTo(point2.x, point2.y) + + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = progressBar.strokeStyle.replace('@Opacity', progressBar.opacity.toString()) + + browserCanvasContext.setLineDash([1, 4]) + browserCanvasContext.lineWidth = lineWidth + browserCanvasContext.stroke() + } + + function drawLayerDisplay () { + let baseAsset = thisObject.definition.referenceParent.parentNode.referenceParent.baseAsset.referenceParent + let quotedAsset = thisObject.definition.referenceParent.parentNode.referenceParent.quotedAsset.referenceParent + let market = baseAsset.code.codeName + '/' + quotedAsset.code.codeName + let exchange = thisObject.definition.referenceParent.parentNode.referenceParent.parentNode.parentNode + let plotterModule = thisObject.definition.referenceParent.referenceParent.referenceParent + + let label1 = thisObject.payload.node.name + let label2 = exchange.name.substring(0, 15) + ' - ' + market + let label3 = thisObject.status.toUpperCase() + + if (label1 !== undefined) { + label1 = label1.substring(0, 22) + } + + let icon1 = getIcon(exchange) + + let icon2 + if (plotterModule.code.icon !== undefined) { + icon2 = canvas.designSpace.iconCollection.get(plotterModule.code.icon) + } + + let icon3 = getIcon(baseAsset) + let icon4 = getIcon(quotedAsset) + + function getIcon (node) { + let nodeDefinition = APP_SCHEMA_MAP.get(node.type) + let iconName + if (nodeDefinition.alternativeIcons !== undefined) { + for (let i = 0; i < nodeDefinition.alternativeIcons.length; i++) { + alternativeIcon = nodeDefinition.alternativeIcons[i] + if (alternativeIcon.codeName === node.code.codeName) { + iconName = alternativeIcon.iconName + } + } + } + if (iconName !== undefined) { + icon = canvas.designSpace.iconCollection.get(iconName) + } else { + icon = canvas.designSpace.iconCollection.get(nodeDefinition.icon) + } + return icon + } + + let backgroundColor = UI_COLOR.BLACK + + const RED_LINE_HIGHT = 4 + const OPACITY = 0.75 + + let params = { + cornerRadius: 0, + lineWidth: 1, + container: thisObject.container, + borderColor: UI_COLOR.RUSTED_RED, + castShadow: false, + backgroundColor: backgroundColor, + opacity: OPACITY + } + + roundedCornersBackground(params) + + drawLabel(label1, 1 / 2, 6 / 10, -5, 0, 17, thisObject.container) + drawLabel(label2, 1 / 2, 8.2 / 10, -5, 0, 9, thisObject.container) + drawLabel(label3, 1 / 2, 9.5 / 10, -5, 0, 9, thisObject.container) + + drawProgressBar(marketFileProgressBar, 1, -45) + drawProgressBar(dailyFileProgressBar, 1, -46) + drawProgressBar(singleFileProgressBar, 1, -47) + drawProgressBar(fileSequenceProgressBar, 1, -48) + + drawIcon(icon1, 1 / 8, 2 / 10, 0, 0, 14, thisObject.container) + drawIcon(icon2, 7 / 8, 2 / 10, 0, 0, 14, thisObject.container) + drawIcon(icon3, 3.4 / 8, 2 / 10, 0, 0, 14, thisObject.container) + drawIcon(icon4, 4.6 / 8, 2 / 10, 0, 0, 14, thisObject.container) + } +} diff --git a/CanvasApp/Panels/LayersPanel.js b/CanvasApp/Panels/LayersPanel.js new file mode 100644 index 000000000..9381a844b --- /dev/null +++ b/CanvasApp/Panels/LayersPanel.js @@ -0,0 +1,458 @@ +function newLayersPanel () { + let thisObject = { + name: 'Layers Panel', + fitFunction: undefined, + container: undefined, + layers: [], + payload: undefined, + isVisible: true, + panelTabButton: undefined, + physics: physics, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize() + thisObject.container.isDraggeable = true + thisObject.container.isWheelable = true + + let isInitialized = false + + let layersMap = new Map() + let visibleLayers = [] + let firstVisibleLayer = 1 + + const LAYER_SEPARATION = 0 + + let headerHeight = 40 + let footerHeight = 10 + let layerHeight = 70 + let desiredVisibleLayers = 5 + let posibleVisibleLayers = 5 + let desiredPanelHeight = (layerHeight + LAYER_SEPARATION) * desiredVisibleLayers + headerHeight + footerHeight + let posiblePanelHeight = (layerHeight + LAYER_SEPARATION) * posibleVisibleLayers + headerHeight + footerHeight + + let onMouseWheelEventSuscriptionId + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseWheelEventSuscriptionId) + + thisObject.panelTabButton.finalize() + thisObject.panelTabButton = undefined + layersMap = undefined + visibleLayers = undefined + + thisObject.container.finalize() + thisObject.container = undefined + + thisObject.payload = undefined + thisObject = undefined + } + + function initialize () { + thisObject.container.name = thisObject.payload.node.name + thisObject.container.frame.containerName = thisObject.container.name + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL + thisObject.container.frame.height = headerHeight + + let position = { // Default position + x: canvas.chartingSpace.viewport.visibleArea.topLeft.x, + y: canvas.chartingSpace.viewport.visibleArea.topLeft.y + } + + thisObject.container.frame.position = position + loadFrame(thisObject.payload, thisObject.container.frame) + + thisObject.panelTabButton = newPanelTabButton() + thisObject.panelTabButton.parentContainer = thisObject.container + thisObject.panelTabButton.container.frame.parentFrame = thisObject.container.frame + thisObject.panelTabButton.fitFunction = thisObject.fitFunction + thisObject.panelTabButton.initialize() + + onMouseWheelEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel) + + readObjectState() + + isInitialized = true + } + + function saveObjectStateVisibleLayers () { + savePropertyAtNodeConfig(thisObject.payload, 'visibleLayers', desiredVisibleLayers) + } + + function saveObjectStatePanelLocation () { + savePropertyAtNodeConfig(thisObject.payload, 'panelLocation', thisObject.panelTabButton.status) + } + + function readObjectState () { + let storedValue + + storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'visibleLayers') + + if (isNaN(storedValue) || storedValue === null || storedValue === undefined) { + // not using this value + saveObjectStateVisibleLayers() // this overrides any invalid value at the config. + return + } else { + if (storedValue < 0) { storedValue = 0 } + if (storedValue > thisObject.layers.length) { + storedValue = thisObject.layers.length + } + + if (storedValue !== desiredVisibleLayers) { + desiredVisibleLayers = storedValue + desiredPanelHeight = (layerHeight + LAYER_SEPARATION) * desiredVisibleLayers + headerHeight + footerHeight + calculateVisbleLayers() + } + } + + storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'panelLocation') + + if (storedValue === null || storedValue === undefined) { + // not using this value + saveObjectStatePanelLocation() // this overrides any invalid value at the config. + return + } else { + if (storedValue !== 'up' && storedValue !== 'down') { + saveObjectStatePanelLocation() // this overrides any invalid value at the config. + return + } + thisObject.panelTabButton.setStatus(storedValue) + saveObjectStatePanelLocation() + } + } + + function removeLayer (id) { + let layer = layersMap.get(id) + layersMap.delete(id) + layer.turnOff() + layer.container.eventHandler.stopListening(layer.onLayerStatusChangedEventSuscriptionId) + layer.container.eventHandler.stopListening(layer.onMouseWheelEventSuscriptionId) + layer.finalize() + } + + function addLayer (layerNode) { + /* Now we create Product objects */ + let layer = newLayer() + layer.payload = layerNode.payload + layer.nodeId = layerNode.payload.node.id + layer.fitFunction = thisObject.fitFunction + + /* Initialize it */ + layer.initialize(onInitialized) + + function onInitialized (err) { + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + layersMap.set(layerNode.id, layer) + + /* Container Stuff */ + layer.container.frame.parentFrame = thisObject.container.frame + layer.container.parentContainer = thisObject.container + layer.container.isWheelable = true + + /* Add to the Product Array */ + thisObject.layers.push(layer) + + /* Listen to Status Changes Events */ + layer.onLayerStatusChangedEventSuscriptionId = layer.container.eventHandler.listenToEvent('Status Changed', onLayerStatusChanged) + layer.onMouseWheelEventSuscriptionId = layer.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel) + + return layer + } + } + } + + function onMouseWheel (event) { + delta = event.wheelDelta + if (delta > 0) { + delta = -1 + } else { + delta = 1 + } + + if (event.y - thisObject.container.frame.position.y - CURRENT_TOP_MARGIN < headerHeight) { // Mouse wheel over the header, not a layer + desiredVisibleLayers = desiredVisibleLayers + delta + if (desiredVisibleLayers < 0) { desiredVisibleLayers = 0 } + if (desiredVisibleLayers > thisObject.layers.length) { desiredVisibleLayers = thisObject.layers.length } + } else { + firstVisibleLayer = firstVisibleLayer + delta + } + desiredPanelHeight = (layerHeight + LAYER_SEPARATION) * desiredVisibleLayers + headerHeight + footerHeight + calculateVisbleLayers() + saveObjectStateVisibleLayers() + } + + function panelSizePhysics () { + if (isInitialized === false || thisObject.visible === false || thisObject.isHidden === true) { return } + + let viewPortHeight = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y + + if (viewPortHeight < headerHeight) { + thisObject.visible = false + } else { + thisObject.visible = true + } + + if (desiredPanelHeight > viewPortHeight) { + posibleVisibleLayers = Math.trunc((viewPortHeight - headerHeight - footerHeight) / (layerHeight + LAYER_SEPARATION)) + } else { + posibleVisibleLayers = desiredVisibleLayers + } + if (thisObject.layers.length < posibleVisibleLayers) { posibleVisibleLayers = thisObject.layers.length } + + if (posibleVisibleLayers === 0) { + posiblePanelHeight = (layerHeight + LAYER_SEPARATION) * posibleVisibleLayers + headerHeight + } else { + posiblePanelHeight = (layerHeight + LAYER_SEPARATION) * posibleVisibleLayers + headerHeight + footerHeight + } + + thisObject.container.frame.height = posiblePanelHeight + } + + function calculateVisbleLayers () { + panelSizePhysics() + let availableSlots = posibleVisibleLayers + + if (firstVisibleLayer < 1) { firstVisibleLayer = 1 } + if (firstVisibleLayer > (thisObject.layers.length - availableSlots + 1)) { firstVisibleLayer = thisObject.layers.length - availableSlots + 1 } + + visibleLayers = [] + + for (let i = 0; i < thisObject.layers.length; i++) { + if (i + 1 >= firstVisibleLayer && i + 1 < firstVisibleLayer + availableSlots) { + let layer = thisObject.layers[i] + + layer.container.frame.position.x = 0 + layer.container.frame.position.y = (layerHeight + LAYER_SEPARATION) * visibleLayers.length + headerHeight + + /* Add to Visible Product Array */ + visibleLayers.push(layer) + } + } + } + + function onLayerStatusChanged (layer) { + thisObject.container.eventHandler.raiseEvent('Layer Status Changed', layer) + } + + function getContainer (point) { + if (isInitialized === false || thisObject.visible === false || thisObject.isHidden === true) { return } + let container + + container = thisObject.panelTabButton.getContainer(point) + if (container !== undefined) { return 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 (let i = 0; i < visibleLayers.length; i++) { + container = visibleLayers[i].getContainer(point) + + if (container !== undefined) { + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return container + } + } + } + + /* The point does not belong to any inner container, so we return the current container. */ + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container + } + } + } + + function childrenPhysics () { + for (let i = 0; i < thisObject.layers.length; i++) { + let layer = thisObject.layers[i] + layer.physics() + } + } + + function syncWithConfigPhysics () { + readObjectState() + } + + function physics () { + if (isInitialized === false) { return } + thisObject.panelTabButton.physics() + saveFrame(thisObject.payload, thisObject.container.frame) + syncWithConfigPhysics() + + /* + The overall idea here is that we need to keep syncronized the panel with the layers that are + defined at the Designer. Users can connect or disconnect any objext resulting in changes in which + are valid layers and which not at any point in time. So what we do here is trying to keep the panel + only with the layers that are connected to the Charting Space hiriarcy. + + To achieve this, first we are going to move all session related cards to a local array. Then we are + going to check for layers at the designer, and will move back the cards which still have layers well + defined to where they were. + + If we find new layers we will add them at that point. Finally, the cards that still remain at the + local array after all the layers at the designer have been processed, are turned off and discarded. + */ + childrenPhysics() + + let localLayers = [] + moveToLocalLayers() + syncWithDesignerLayers() + + /* At this poins all the cards still at the local array need to be removed from the panel. */ + turnOffUnusedLayers() + calculateVisbleLayers() + + function syncWithDesignerLayers () { + let layerManager = thisObject.payload.node + for (let p = 0; p < layerManager.layers.length; p++) { + let layerNode = layerManager.layers[p] + + let found = removeFromLocalLayers(layerNode.id) + if (found !== true) { + layer = addLayer(layerNode) + if (layer !== undefined) { + onLayerStatusChanged(layer) + } + } + } + } + + function moveToLocalLayers () { + removeNext() + + function removeNext () { + for (let i = 0; i < thisObject.layers.length; i++) { + let layer = thisObject.layers[i] + + thisObject.layers.splice(i, 1) + localLayers.push(layer) + removeNext() + } + } + } + + function removeFromLocalLayers (id) { + for (let i = 0; i < localLayers.length; i++) { + let layer = localLayers[i] + if (layer.payload.node.id === id) { + thisObject.layers.push(layer) + localLayers.splice(i, 1) + return true + } + } + } + + function turnOffUnusedLayers () { + for (let i = 0; i < localLayers.length; i++) { + let layer = localLayers[i] + removeLayer(layer.nodeId) + } + } + } + + function draw () { + if (isInitialized === false || thisObject.visible === false || thisObject.isHidden === true) { return } + + // thisObject.container.frame.draw(false, false, false, thisObject.fitFunction) + + drawHeader() + for (let i = 0; i < visibleLayers.length; i++) { + visibleLayers[i].draw() + } + drawScrollBar() + thisObject.panelTabButton.draw() + } + + function drawHeader () { + let label1 = thisObject.payload.node.payload.parentNode.payload.parentNode.name.substring(0, 18) + let label2 = thisObject.payload.node.payload.parentNode.name.substring(0, 18) + let label3 = '' + + let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.payload.parentNode.type) + let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type) + + let backgroundColor = UI_COLOR.BLACK + + let params = { + cornerRadius: 5, + lineWidth: 1, + container: thisObject.container, + borderColor: UI_COLOR.RUSTED_RED, + castShadow: false, + backgroundColor: backgroundColor, + opacity: 1 + } + + roundedCornersBackground(params) + + drawLabel(label1, 1 / 2, 0, 0, 15, 9, thisObject.container) + drawLabel(label2, 1 / 2, 0, 0, 30, 9, thisObject.container) + + drawIcon(icon1, 1 / 8, 0, 0, 20, 28, thisObject.container) + drawIcon(icon2, 7 / 8, 0, 0, 20, 28, thisObject.container) + } + + function drawScrollBar () { + if (thisObject.layers.length > posibleVisibleLayers && posibleVisibleLayers > 0) { + let xOffset = 4 + let barTopPoint = { + x: thisObject.container.frame.width - xOffset, + y: headerHeight + } + let barBottomPoint = { + x: thisObject.container.frame.width - xOffset, + y: thisObject.container.frame.height - footerHeight + } + let ratio = posibleVisibleLayers / thisObject.layers.length + let handleHeight = (posibleVisibleLayers * (layerHeight + LAYER_SEPARATION)) * ratio + let handleTopPoint = { + x: thisObject.container.frame.width - xOffset, + y: headerHeight + (layerHeight + LAYER_SEPARATION) * ratio * (firstVisibleLayer - 1) + } + let handleBottomPoint = { + x: thisObject.container.frame.width - xOffset, + y: handleTopPoint.y + handleHeight + } + barTopPoint = thisObject.container.frame.frameThisPoint(barTopPoint) + barBottomPoint = thisObject.container.frame.frameThisPoint(barBottomPoint) + handleTopPoint = thisObject.container.frame.frameThisPoint(handleTopPoint) + handleBottomPoint = thisObject.container.frame.frameThisPoint(handleBottomPoint) + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(barTopPoint.x, barTopPoint.y) + browserCanvasContext.lineTo(barBottomPoint.x, barBottomPoint.y) + browserCanvasContext.closePath() + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 1 + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + 1 + ')' + browserCanvasContext.stroke() + + browserCanvasContext.beginPath() + browserCanvasContext.moveTo(handleTopPoint.x, handleTopPoint.y) + browserCanvasContext.lineTo(handleBottomPoint.x, handleBottomPoint.y) + browserCanvasContext.closePath() + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 4 + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + 1 + ')' + browserCanvasContext.stroke() + } + } +} diff --git a/CanvasApp/Panels/PanelTabButton.js b/CanvasApp/Panels/PanelTabButton.js new file mode 100644 index 000000000..2d4b37565 --- /dev/null +++ b/CanvasApp/Panels/PanelTabButton.js @@ -0,0 +1,253 @@ +function newPanelTabButton () { + let thisObject = { + container: undefined, + fitFunction: undefined, + parentContainer: undefined, + status: undefined, + setStatus: setStatus, + physics: physics, + draw: draw, + getContainer: getContainer, // returns the inner most container that holds the point received by parameter. + initialize: initialize, + finalize: finalize + } + + let isInitialized = false + /* Cointainer stuff */ + + thisObject.container = newContainer() + thisObject.container.name = 'Panel Tab Button' + thisObject.container.initialize() + thisObject.container.isClickeable = true + thisObject.container.frame.containerName = 'Panel Tab Button' + + /* Animation to hide the pannel */ + thisObject.status = 'up' + let upPosition = {} + let downPosition = {} + let transitionPosition = {} + + let onMouseClickEventSuscriptionId + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseClickEventSuscriptionId) + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.parentContainer = undefined + + thisObject.status = undefined + upPosition = undefined + downPosition = undefined + transitionPosition = undefined + } + + function initialize () { + thisObject.container.frame.width = 10 + thisObject.container.frame.height = 10 + + var position = { + x: thisObject.parentContainer.frame.width - 14, + y: 1 + } + + thisObject.container.frame.position = position + + upPosition.x = thisObject.parentContainer.frame.position.x + upPosition.y = thisObject.parentContainer.frame.position.y + + downPosition.x = thisObject.parentContainer.frame.position.x + downPosition.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y + + transitionPosition.x = upPosition.x + transitionPosition.y = upPosition.y + + /* Lets listen to our own events to react when we have a Mouse Click */ + onMouseClickEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + + isInitialized = true + } + + function setStatus (value) { + if (value === 'up' && thisObject.status === 'down') { + thisObject.status = 'going up' + } + if (value === 'down' && thisObject.status === 'up') { + thisObject.status = 'going down' + } + } + + function onMouseClick (event) { + if (thisObject.status === 'up') { + transitionPosition.x = upPosition.x + transitionPosition.y = upPosition.y + + thisObject.status = 'going down' + } else { + transitionPosition.x = downPosition.x + transitionPosition.y = downPosition.y + + thisObject.status = 'going up' + } + } + + function physics () { + if (isInitialized === false) { return } + + upPosition.x = thisObject.parentContainer.frame.position.x + upPosition.y = canvas.chartingSpace.viewport.visibleArea.topRight.y + + downPosition.x = thisObject.parentContainer.frame.position.x + downPosition.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y + + if (thisObject.status === 'going down') { + if (transitionPosition.y < downPosition.y) { + transitionPosition.y = transitionPosition.y + 50 + thisObject.parentContainer.frame.position.y = transitionPosition.y + } else { + thisObject.parentContainer.frame.position.y = downPosition.y + thisObject.status = 'down' + } + } + if (thisObject.status === 'going up') { + if (transitionPosition.y > upPosition.y) { + transitionPosition.y = transitionPosition.y - 50 + thisObject.parentContainer.frame.position.y = transitionPosition.y + } else { + thisObject.parentContainer.frame.position.y = upPosition.y + thisObject.status = 'up' + } + } + } + + function draw () { + if (isInitialized === false) { return } + + drawButton() + } + + function getContainer (point) { + let 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 */ + + return thisObject.container + } else { + /* This point does not belong to thisObject space. */ + + return undefined + } + } + + function drawButton () { + let point1 + let point2 + let point3 + + switch (thisObject.status) { + case 'up': { + point1 = { + x: 3, + y: 3 + } + + point2 = { + x: thisObject.container.frame.width - 3, + y: 3 + } + + point3 = { + x: thisObject.container.frame.width / 2, + y: thisObject.container.frame.height - 3 + } + break + } + + case 'going down': { + point1 = { + x: 4, + y: 4 + } + + point2 = { + x: thisObject.container.frame.width - 4, + y: 4 + } + + point3 = { + x: thisObject.container.frame.width / 2, + y: thisObject.container.frame.height - 4 + } + break + } + + case 'going up': { + point1 = { + x: 4, + y: thisObject.container.frame.height - 4 + } + + point2 = { + x: thisObject.container.frame.width - 4, + y: thisObject.container.frame.height - 4 + } + + point3 = { + x: thisObject.container.frame.width / 2, + y: 4 + } + break + } + + case 'down': { + point1 = { + x: 3, + y: thisObject.container.frame.height - 3 + } + + point2 = { + x: thisObject.container.frame.width - 3, + y: thisObject.container.frame.height - 3 + } + + point3 = { + x: thisObject.container.frame.width / 2, + y: 3 + } + break + } + } + + /* Now the transformations. */ + + point1 = thisObject.container.frame.frameThisPoint(point1) + point2 = thisObject.container.frame.frameThisPoint(point2) + point3 = thisObject.container.frame.frameThisPoint(point3) + + if (thisObject.fitFunction !== undefined) { + point1 = thisObject.fitFunction(point1) + point2 = thisObject.fitFunction(point2) + point3 = thisObject.fitFunction(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() + + browserCanvasContext.strokeStyle = 'rgba(250, 250, 250, 1)' + browserCanvasContext.lineWidth = 1 + browserCanvasContext.stroke() + + browserCanvasContext.fillStyle = 'rgba(250, 250, 250, 1)' + browserCanvasContext.fill() + } +} diff --git a/CanvasApp/Panels/PanelsVisibleButton.js b/CanvasApp/Panels/PanelsVisibleButton.js new file mode 100644 index 000000000..b2266845e --- /dev/null +++ b/CanvasApp/Panels/PanelsVisibleButton.js @@ -0,0 +1,84 @@ +function newPanelsVisibleButton () { + let thisObject = { + container: undefined, + fitFunction: undefined, + parentContainer: undefined, + panels: undefined, + showPanels: true, + draw: draw, + getContainer: getContainer, // returns the inner most container that holds the point received by parameter. + initialize: initialize, + finalize: finalize + } + + /* Cointainer stuff */ + thisObject.container = newContainer() + thisObject.container.name = 'Panels Visible Button' + thisObject.container.initialize() + thisObject.container.isClickeable = true + thisObject.container.isDraggeable = false + thisObject.container.frame.containerName = 'Panels Visible Button' + + let onMouseClickEventSuscriptionId + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseClickEventSuscriptionId) + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.parentContainer = undefined + thisObject.panels = undefined + } + + function initialize (panels) { + thisObject.panels = panels + setPanelVisibility() + + thisObject.container.frame.width = 25 + thisObject.container.frame.height = 25 + + let position = { + x: thisObject.container.frame.width * 1 / 8, + y: 45 + } + + thisObject.container.frame.position = position + + /* Lets listen to our own events to react when we have a Mouse Click */ + onMouseClickEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + } + + function onMouseClick (event) { + if (thisObject.showPanels === false) { + thisObject.showPanels = true + } else { + thisObject.showPanels = false + } + setPanelVisibility() + } + + function setPanelVisibility () { + for (let i = 0; i < thisObject.panels.length; i++) { + panel = canvas.panelsSpace.getPanel(thisObject.panels[i]) + panel.isVisible = thisObject.showPanels + } + } + + function getContainer (point) { + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } + } + + function draw () { + let icon + if (thisObject.showPanels === true) { + icon = canvas.designSpace.iconCollection.get('plotter-panel') + } else { + icon = canvas.designSpace.iconCollection.get('toggle-panel-off') + } + + drawIcon(icon, 1 / 2, 1 / 2, 0, 0, 14, thisObject.container) + } +} diff --git a/CanvasApp/Panels/PlotterPanel.js b/CanvasApp/Panels/PlotterPanel.js new file mode 100644 index 000000000..24ef4f91f --- /dev/null +++ b/CanvasApp/Panels/PlotterPanel.js @@ -0,0 +1,185 @@ + +function newPlotterPanel () { + const MODULE_NAME = 'Plotter Panel' + const ERROR_LOG = true + const INTENSIVE_LOG = false + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + fitFunction: undefined, + container: undefined, + isVisible: true, + onRecordChange: onRecordChange, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize('Plotter Panel') + + let heightFactor = 1 + let currentRecord + let panelTabButton + let panelNode + + return thisObject + + function finalize () { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.isVisible = undefined + + heightFactor = undefined + currentRecord = undefined + panelTabButton.finalize() + panelTabButton = undefined + panelNode = undefined + } + + function initialize (pPanelNode) { + panelNode = pPanelNode + thisObject.container.frame.containerName = panelNode.name + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL + + thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width - thisObject.container.frame.width * Math.random() * 8 + thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height - thisObject.container.frame.height * Math.random() * 1.5 + + panelTabButton = newPanelTabButton() + panelTabButton.parentContainer = thisObject.container + panelTabButton.container.frame.parentFrame = thisObject.container.frame + panelTabButton.fitFunction = thisObject.fitFunction + panelTabButton.initialize() + } + + function getContainer (point) { + if (thisObject.isVisible !== true) { return } + let container + + container = panelTabButton.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container + } + } + } + + function onRecordChange (pCurrentRecord) { + currentRecord = pCurrentRecord + } + + function draw () { + if (thisObject.isVisible !== true) { return } + + thisObject.container.frame.draw(false, false, true, thisObject.fitFunction) + plotCurrentRecordData() + panelTabButton.draw() + } + + function plotCurrentRecordData () { + const frameBodyHeight = thisObject.container.frame.getBodyHeight() + const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight + + const X_AXIS = thisObject.container.frame.width / 2 + const Y_AXIS = frameTitleHeight + frameBodyHeight / 2 + const PANEL_HEIGHT = frameTitleHeight + frameBodyHeight + + if (currentRecord === undefined) { return } + if (currentRecord.data === undefined) { return } + + let record = currentRecord.data + + /* First we execute code if provided. */ + if (panelNode.javascriptCode !== undefined) { + try { + eval(panelNode.javascriptCode.code) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> err = ' + err.stack) } + if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> javascriptCode.code = ' + panelNode.javascriptCode.code) } + } + } + + /* Second we go through the panel data. */ + if (panelNode.panelData === undefined) { return } + for (let i = 0; i < panelNode.panelData.length; i++) { + let panelData = panelNode.panelData[i] + + let labelText = panelData.name + let labelPosition = i * 10 + 10 + let valuePosition = i * 10 + 15 + let value = 'No value defined.' + + if (valuePosition > 100) { + heightFactor = 2 + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * heightFactor + } + + if (panelData.dataFormula !== undefined) { + try { + value = eval(panelData.dataFormula.code) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> err = ' + err.stack) } + if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> dataFormula.code = ' + panelNode.dataFormula.code) } + } + } + + if (panelData.code.valueDecimals !== undefined) { + value = value.toFixed(panelData.code.valueDecimals) + } + + if (panelData.code.labelText !== undefined) { + labelText = panelData.code.labelText + } + + if (panelData.code.labelPosition !== undefined) { + labelPosition = panelData.code.labelPosition + } + + if (panelData.code.valuePosition !== undefined) { + valuePosition = panelData.code.valuePosition + } + + printLabel(labelText, X_AXIS, PANEL_HEIGHT * labelPosition / 100 / heightFactor, '1') + printLabel(value, X_AXIS, PANEL_HEIGHT * valuePosition / 100 / heightFactor, '0.50') + } + + function printLabel (labelToPrint, x, y, opacity) { + let labelPoint + let fontSize = 10 + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + + let label = '' + labelToPrint + + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + + labelPoint = { + x: x - xOffset, + y: y + } + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint) + labelPoint = thisObject.fitFunction(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } + + browserCanvasContext.closePath() + browserCanvasContext.fill() + } +} diff --git a/CanvasApp/Plotter.js b/CanvasApp/Plotter.js new file mode 100644 index 000000000..e23e87dc3 --- /dev/null +++ b/CanvasApp/Plotter.js @@ -0,0 +1,821 @@ +function newPlotter () { + const MODULE_NAME = 'Plotter' + const ERROR_LOG = true + const INTENSIVE_LOG = false + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + currentRecord: undefined, + container: undefined, + fitFunction: undefined, + onDailyFileLoaded: onDailyFileLoaded, + initialize: initialize, + finalize: finalize, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw + } + + let container = newContainer() + container.initialize() + thisObject.container = container + + let coordinateSystem + let plotterModuleConfig + let slotHeight = (canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y) / 10 // This is the amount of slots available + let mustRecalculateDataPoints = false + let atMousePositionFillStyles = new Map() + let atMousePositionStrokeStyles = new Map() + + let timeFrame // This will hold the current Time Frame the user is at. + let datetime // This will hold the current Datetime the user is at. + + let marketFile // This is the current Market File being plotted. + let fileCursor // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles // This object will provide the different Market Files at different Time Periods. + let dailyFiles // This object will provide the different File Cursors at different Time Periods. + + let productDefinition // Here we store a snapshot of the product definition which references this plotter. + + let records = [] // We will have the information to be plotted here. + let userPositionDate + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let onDisplaceEventSubscriptionId + let scaleChangedEventSubscriptionId + + let logged = false + return thisObject + + function finalize () { + try { + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId) + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId) + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId) + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onDisplaceEventSubscriptionId) + thisObject.fitFunction = undefined + + /* Clear References */ + marketFiles = undefined + dailyFiles = undefined + + datetime = undefined + timeFrame = undefined + + marketFile = undefined + fileCursor = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + plotterModuleConfig = undefined + slotHeight = undefined + mustRecalculateDataPoints = undefined + atMousePositionFillStyles = undefined + atMousePositionStrokeStyles = undefined + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack.stack) } + } + } + + function initialize (pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction, pProductDefinition) { + try { + /* Store the information received. */ + marketFiles = pStorage.marketFiles[0] + dailyFiles = pStorage.dailyFiles[0] + + datetime = pDatetime + timeFrame = pTimeFrame + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + productDefinition = pProductDefinition + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS) // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + marketFile = marketFiles.getFile(pTimeFrame) + fileCursor = dailyFiles.getFileCursor(pTimeFrame) + + /* Listen to the necesary events. */ + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Zoom Changed', onViewportZoomChanged) + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Position Changed', onViewportPositionChanged) + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent('Drag Finished', onDragFinished) + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent('Files Updated', onMarketFilesUpdated) + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent('Files Updated', onDailyFilesUpdated) + onDisplaceEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDisplace', onDisplace) + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + recalculate() + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate() + }) + + callBackFunction() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function initializeCoordinateSystem () { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem () { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged () { + mustRecalculateDataPoints = true + recalculate() + } + + function onMouseOver (event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function onMarketFilesUpdated () { + try { + let newMarketFile = marketFiles.getFile(timeFrame) + if (newMarketFile !== undefined) { + marketFile = newMarketFile + mustRecalculateDataPoints = true + recalculate() + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onMarketFilesUpdated -> err = ' + err.stack.stack) } + } + } + + function onDailyFilesUpdated () { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame) + if (newFileCursor !== undefined) { + fileCursor = newFileCursor + mustRecalculateDataPoints = true + recalculate() + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDailyFilesUpdated -> err = ' + err.stack.stack) } + } + } + + function getContainer (point) { + try { + let container + /* First we check if this point is inside this space. */ + if (thisObject.container.frame.isThisPointHere(point) === true) { + return thisObject.container + } else { + /* This point does not belong to this space. */ + return undefined + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getContainer -> err = ' + err.stack) } + } + } + + function setTimeFrame (pTimeFrame) { + try { + if (timeFrame !== pTimeFrame) { + timeFrame = pTimeFrame + mustRecalculateDataPoints = true + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + let newMarketFile = marketFiles.getFile(pTimeFrame) + + if (newMarketFile !== undefined) { + marketFile = newMarketFile + recalculate() + } + } else { + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame) + + if (newFileCursor !== undefined) { + fileCursor = newFileCursor + recalculate() + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] setTimeFrame -> err = ' + err.stack) } + } + } + + function setDatetime (pDatetime) { + datetime = pDatetime + } + + function setCoordinateSystem (pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded (event) { + try { + if (event.currentValue === event.totalValue) { + /* This happens only when all of the files in the cursor have been loaded. */ + recalculate() + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDailyFileLoaded -> err = ' + err.stack) } + } + } + + function draw () { + try { + thisObject.container.frame.draw() + plotChart() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] draw -> err = ' + err.stack) } + } + } + + function recalculate () { + try { + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + recalculateUsingMarketFiles() + } else { + recalculateUsingDailyFiles() + } + thisObject.container.eventHandler.raiseEvent('CandleStairs Changed', records) // --> Check This + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] recalculate -> err = ' + err.stack) } + } + } + + function jsonifyDataFile (dataFile, recordDefinition, farLeftDate, farRightDate) { + /* + This function has as an input the raw data on files and creates with it an array of JSON objects + with not calculated properties for later being consumed by Formulas + */ + + let jsonifiedArray = [] + let previous + + for (let i = 0; i < dataFile.length; i++) { + let record = {} + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + record[property.code.codeName] = dataFile[i][j] + } + } + + if ( + (record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) && + (record.end >= coordinateSystem.min.x && record.begin <= coordinateSystem.max.x) + ) { + record.previous = previous + jsonifiedArray.push(record) + previous = record + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + thisObject.currentRecord = record + thisObject.container.eventHandler.raiseEvent('Current Record Changed', thisObject.currentRecord) + } + } + } + + return jsonifiedArray + } + + function calculationsProcedure (jsonArray, recordDefinition, calculationsProcedure, timeFrame) { + /* + This function has as an input an array of JSON objects, and it adds calculated properties to + complete the set of properties that will be available. + */ + + let system = { // These are the available system variables to be used in User Code and Formulas + timeFrame: timeFrame, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] + + /* This is Initialization Code */ + if (calculationsProcedure.initialization !== undefined) { + if (calculationsProcedure.initialization.javascriptCode !== undefined) { + try { + eval(calculationsProcedure.initialization.javascriptCode.code) + } catch (err) { + logger.write('[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = ' + err.stack.stack) + logger.write('[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = ' + calculationsProcedure.initialization.javascriptCode.code) + throw ('Error Executing User Code.') + } + } + } + + /* This is Initialization Code */ + if (calculationsProcedure.loop !== undefined) { + if (calculationsProcedure.loop.javascriptCode !== undefined) { + for (let index = 0; index < jsonArray.length; index++) { + let product = jsonArray[index] + + /* This is Loop Code */ + try { + eval(calculationsProcedure.loop.javascriptCode.code) + } catch (err) { + logger.write('[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = ' + err.stack.stack) + logger.write('[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = ' + JSON.stringify(product)) + logger.write('[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = ' + calculationsProcedure.loop.javascriptCode.code) + throw ('Error Executing User Code.') + } + + /* For each calculated property we apply its formula */ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated === true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + try { + let newValue = eval(property.formula.code) + let currentRecord = product + currentRecord[property.code.codeName] = newValue + } catch (err) { + logger.write('[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = ' + err.stack.stack) + logger.write('[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = ' + JSON.stringify(product)) + logger.write('[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = ' + property.formula.code) + throw ('Error Executing User Code.') + } + } + } + } + } + + /* Adding the new element to the resulting array */ + results.push(product) + } + } + } + return results + } + + function recalculateUsingDailyFiles () { + try { + if (fileCursor === undefined) { return } // We need to wait until there is a fileCursor + if (fileCursor.files.size === 0) { return } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame) + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem) + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem) + + let dateDiff = rightDate.valueOf() - leftDate.valueOf() + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5) + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5) + + let currentDate = new Date(farLeftDate.valueOf()) + + records = [] + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2) + + let dailyFile = fileCursor.files.get(stringDate) + + if (dailyFile !== undefined) { + /* Transform the current file content into an array of JSON objects */ + let jsonData = jsonifyDataFile(dailyFile, productDefinition.record, farLeftDate, farRightDate) + + /* Add the calculated properties */ + if (productDefinition.calculations !== undefined) { + let calculationsResult = calculationsProcedure(jsonData, productDefinition.record, productDefinition.calculations, timeFrame) + records.push(...calculationsResult) // This adds records to the current array. + } else { + records.push(...jsonData)// This adds records to the current array. + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS) + } + + /* Lests check if all the visible screen is going to be covered by candle-record. */ + let lowerEnd = leftDate.valueOf() + let upperEnd = rightDate.valueOf() + + if (records.length > 0) { + if (records[0].begin > lowerEnd || records[records.length - 1].end < upperEnd) { + setTimeout(recalculate, 2000) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] recalculateUsingDailyFiles -> err = ' + err.stack) } + } + } + + function recalculateUsingMarketFiles () { + try { + if (marketFile === undefined) { return } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame) + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem) + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem) + + let dateDiff = rightDate.valueOf() - leftDate.valueOf() + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5) + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5) + + for (let i = 0; i < records.length; i++) { + let record = records[i] + record.fillStyle = undefined + record.strokeStyle = undefined + record.dataPoints = undefined + record.previous = undefined + records[i] = {} + } + records = [] + + /* Transform the current file content into an array of JSON objects */ + let jsonData = jsonifyDataFile(marketFile, productDefinition.record, leftDate, rightDate) + + /* Add the calculated properties */ + if (productDefinition.calculations !== undefined) { + let calculationsResult = calculationsProcedure(jsonData, productDefinition.record, productDefinition.calculations, timeFrame) + records.push(...calculationsResult) // This adds records to the current array. + } else { + records.push(...jsonData)// This adds records to the current array. + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] recalculateUsingMarketFiles -> err = ' + err.stack) } + } + } + + function recalculateScale () { + try { + if (coordinateSystem.maxValue > 0) { return } // Already calculated. + /* First we calculate the default scale */ + let minValue = { + x: MIN_PLOTABLE_DATE.valueOf(), + y: 0 + } + + let maxValue = { + x: MAX_PLOTABLE_DATE.valueOf(), + y: nextPorwerOf10(MAX_DEFAULT_RATE_SCALE_VALUE) / 4 // TODO: This 4 is temporary + } + + coordinateSystem.initialize( + minValue, + maxValue, + thisObject.container.frame.width, + thisObject.container.frame.height + ) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] recalculateScale -> err = ' + err.stack) } + } + } + + function plotChart () { + try { + /* Clean the pannel at places where there is no record. */ + let currentRecord = { + data: undefined + } + thisObject.container.eventHandler.raiseEvent('Current Record Changed', currentRecord) + + for (let i = 0; i < records.length; i++) { + let record = records[i] + + if (i == 0) { + record.previous = record + } else { + record.previous = records[i - 1] + } + + let beginPoint = { + x: record.begin, + y: 0 + } + + let endPoint = { + x: record.end, + y: 0 + } + + beginPoint = coordinateSystem.transformThisPoint(beginPoint) + endPoint = coordinateSystem.transformThisPoint(endPoint) + + beginPoint = transformThisPoint(beginPoint, thisObject.container) + endPoint = transformThisPoint(endPoint, thisObject.container) + + beginPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(beginPoint) + endPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(endPoint) + + beginPoint = thisObject.fitFunction(beginPoint) + endPoint = thisObject.fitFunction(endPoint) + + if (endPoint.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || beginPoint.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + continue + } + + let atMousePosition = false + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + atMousePosition = true + + let currentRecord = { + data: record + } + thisObject.container.eventHandler.raiseEvent('Current Record Changed', currentRecord) + } + + /* If there is code we execute it now. */ + if (productDefinition.referenceParent.javascriptCode !== undefined) { + eval(productDefinition.referenceParent.javascriptCode.code) + } + + if (productDefinition.referenceParent.shapes === undefined) { continue } + if (productDefinition.referenceParent.shapes.chartPoints === undefined) { continue } + let dataPoints = new Map() + if (record.dataPoints !== undefined && mustRecalculateDataPoints === false) { + /* We use the datapoints already calculated. */ + dataPoints = record.dataPoints + } else { + if (logged === false) { + logged = true + } + /* Only calculate the datapoints for this record, if we have not calculate it before. */ + for (let k = 0; k < productDefinition.referenceParent.shapes.chartPoints.length; k++) { + let chartPoints = productDefinition.referenceParent.shapes.chartPoints[k] + for (let j = 0; j < chartPoints.points.length; j++) { + let point = chartPoints.points[j] + if (point.pointFormula !== undefined) { + let x = 0 + let y = 0 + eval(point.pointFormula.code) + let dataPoint = { + x: x, + y: y + } + /* Contributing to Auto-Scale */ + coordinateSystem.reportYValue(dataPoint.y) + + /* + The information we store in files is independent from the charing system and its coordinate systems. + That means that the first thing we allways need to do is to trasform these points to the coordinate system of the timeline. + */ + dataPoint = coordinateSystem.transformThisPoint(dataPoint) + dataPoint = transformThisPoint(dataPoint, thisObject.container) + dataPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(dataPoint) + dataPoint = thisObject.fitFunction(dataPoint) + + /* Store the data point at the local map */ + dataPoints.set(point.id, dataPoint) + + if (plotterModuleConfig !== undefined) { + if (plotterModuleConfig.slot !== undefined) { + /* We reset the y coordinate since it will be transformed with another coordinate system to fit into a slot. */ + dataPoint.y = (-1) * y * coordinateSystem.scale.y + (plotterModuleConfig.slot.number - 1) * slotHeight + canvas.chartingSpace.viewport.visibleArea.topLeft.y + } + } + } + } + } + + /* Remember this datapoints, so that we do not need to calculate them again. */ + record.dataPoints = dataPoints + } + + for (let j = 0; j < productDefinition.referenceParent.shapes.polygons.length; j++) { + let polygon = productDefinition.referenceParent.shapes.polygons[j] + let calculatedStyle + + /* Finding out the fill style */ + let fillStyle = { // default values + opacity: 1, + paletteColor: UI_COLOR.BLACK + } + if (polygon.polygonBody !== undefined) { + if (record.fillStyle === undefined) { + record.fillStyle = new Map() + } + calculatedStyle = record.fillStyle.get(polygon.id) + if (calculatedStyle !== undefined) { + /* We use the already calculated style */ + fillStyle = calculatedStyle + } else { + /* Get the default style if exists */ + if (polygon.polygonBody.style !== undefined) { + if (polygon.polygonBody.style.code !== undefined) { + if (polygon.polygonBody.style.code.default !== undefined) { + let bodyStyle = polygon.polygonBody.style.code.default + if (bodyStyle.opacity !== undefined) { fillStyle.opacity = bodyStyle.opacity } + if (bodyStyle.paletteColor !== undefined) { fillStyle.paletteColor = eval(bodyStyle.paletteColor) } + } + } + } + /* Apply conditional styles */ + if (polygon.polygonBody.styleConditions !== undefined) { + for (let k = 0; k < polygon.polygonBody.styleConditions.length; k++) { + let condition = polygon.polygonBody.styleConditions[k] + let value = eval(condition.code) + if (value === true) { + if (condition.style !== undefined) { + let bodyStyle = condition.style.code + if (bodyStyle.opacity !== undefined) { fillStyle.opacity = bodyStyle.opacity } + if (bodyStyle.paletteColor !== undefined) { fillStyle.paletteColor = eval(bodyStyle.paletteColor) } + } + } + } + } + /* Get the atMousePosition style if exists */ + if (polygon.polygonBody.style !== undefined) { + if (polygon.polygonBody.style.code.atMousePosition !== undefined) { + let atMousePositionStyleDefinition = polygon.polygonBody.style.code.atMousePosition + let atMousePositionStyle = {} + if (atMousePositionStyleDefinition.opacity !== undefined) { atMousePositionStyle.opacity = atMousePositionStyleDefinition.opacity } + if (atMousePositionStyleDefinition.paletteColor !== undefined) { atMousePositionStyle.paletteColor = eval(atMousePositionStyleDefinition.paletteColor) } + atMousePositionFillStyles.set(polygon.id, atMousePositionStyle) + } + } + + record.fillStyle.set(polygon.id, fillStyle) + } + } + + /* Finding out the stroke style */ + let strokeStyle = { // default values + opacity: 1, + paletteColor: UI_COLOR.BLACK, + lineWidth: 1, + lineDash: [0, 0] + } + if (polygon.polygonBorder !== undefined) { + if (record.strokeStyle === undefined) { + record.strokeStyle = new Map() + } + calculatedStyle = record.strokeStyle.get(polygon.id) + if (calculatedStyle !== undefined) { + /* We use the already calculated style */ + strokeStyle = calculatedStyle + } else { + /* Get the default style if exists */ + if (polygon.polygonBorder.style !== undefined) { + if (polygon.polygonBorder.style.code !== undefined) { + if (polygon.polygonBorder.style.code.default !== undefined) { + let borderStyle = polygon.polygonBorder.style.code.default + if (borderStyle.opacity !== undefined) { strokeStyle.opacity = borderStyle.opacity } + if (borderStyle.paletteColor !== undefined) { strokeStyle.paletteColor = eval(borderStyle.paletteColor) } + if (borderStyle.lineWidth !== undefined) { strokeStyle.lineWidth = borderStyle.lineWidth } + if (borderStyle.lineDash !== undefined) { strokeStyle.lineDash = borderStyle.lineDash } + } + } + } + /* Apply conditional styles */ + if (polygon.polygonBorder.styleConditions !== undefined) { + for (let k = 0; k < polygon.polygonBorder.styleConditions.length; k++) { + let condition = polygon.polygonBorder.styleConditions[k] + let value = eval(condition.code) + if (value === true) { + if (condition.style !== undefined) { + let borderStyle = condition.style.code + if (borderStyle.opacity !== undefined) { strokeStyle.opacity = borderStyle.opacity } + if (borderStyle.paletteColor !== undefined) { strokeStyle.paletteColor = eval(borderStyle.paletteColor) } + if (borderStyle.lineWidth !== undefined) { strokeStyle.lineWidth = borderStyle.lineWidth } + if (borderStyle.lineDash !== undefined) { strokeStyle.lineDash = borderStyle.lineDash } + } + } + } + } + /* Get the atMousePosition style if exists */ + if (polygon.polygonBorder.style !== undefined) { + if (polygon.polygonBorder.style.code.atMousePosition !== undefined) { + let atMousePositionStyleDefinition = polygon.polygonBorder.style.code.atMousePosition + let atMousePositionStyle = {} + if (atMousePositionStyleDefinition.opacity !== undefined) { atMousePositionStyle.opacity = atMousePositionStyleDefinition.opacity } + if (atMousePositionStyleDefinition.paletteColor !== undefined) { atMousePositionStyle.paletteColor = eval(atMousePositionStyleDefinition.paletteColor) } + if (atMousePositionStyleDefinition.lineWidth !== undefined) { atMousePositionStyle.lineWidth = atMousePositionStyleDefinition.lineWidth } + if (atMousePositionStyleDefinition.lineDash !== undefined) { atMousePositionStyle.lineDash = atMousePositionStyleDefinition.lineDash } + atMousePositionStrokeStyles.set(polygon.id, atMousePositionStyle) + } + } + + record.strokeStyle.set(polygon.id, strokeStyle) + } + } + + /* Draw the Polygon */ + browserCanvasContext.beginPath() + for (let k = 0; k < polygon.polygonVertexes.length; k++) { + let polygonVertex = polygon.polygonVertexes[k] + if (polygonVertex.referenceParent !== undefined) { + let dataPointObject = dataPoints.get(polygonVertex.referenceParent.id) + let dataPoint = { + x: dataPointObject.x, + y: dataPointObject.y + } + /* We make sure the points do not fall outside the viewport visible area. This step allways need to be done. */ + dataPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(dataPoint) + dataPoint = thisObject.fitFunction(dataPoint) + if (k === 0) { + browserCanvasContext.moveTo(dataPoint.x, dataPoint.y) + } else { + browserCanvasContext.lineTo(dataPoint.x, dataPoint.y) + } + } + } + browserCanvasContext.closePath() + + /* Apply the fill style to the canvas object */ + if (polygon.polygonBody !== undefined) { + /* Replace the fill style if we are at mouse position */ + if (atMousePosition === true) { + let atMousePositionFillStyle = atMousePositionFillStyles.get(polygon.id) + if (atMousePositionFillStyle !== undefined) { + fillStyle = atMousePositionFillStyle + } + } + + browserCanvasContext.fillStyle = 'rgba(' + fillStyle.paletteColor + ', ' + fillStyle.opacity + ')' + browserCanvasContext.fill() + } + + /* Apply the stroke style to the canvas object */ + if (polygon.polygonBorder !== undefined) { + /* Replace the stroke style if we are at mouse position */ + if (atMousePosition === true) { + let atMousePositionStrokeStyle = atMousePositionStrokeStyles.get(polygon.id) + if (atMousePositionStrokeStyle !== undefined) { + strokeStyle = atMousePositionStrokeStyle + } + } + + browserCanvasContext.lineWidth = strokeStyle.lineWidth + browserCanvasContext.setLineDash(strokeStyle.lineDash) + browserCanvasContext.strokeStyle = 'rgba(' + strokeStyle.paletteColor + ', ' + strokeStyle.opacity + ')' + browserCanvasContext.stroke() + } + } + } + + if (coordinateSystem.autoMinYScale === true || coordinateSystem.autosMaxScale === true) { + mustRecalculateDataPoints = true + } else { + mustRecalculateDataPoints = false + } + + logged = false + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] plotChart -> err = ' + err.stack) } + } + } + + function onViewportZoomChanged (event) { + try { + mustRecalculateDataPoints = true + recalculate() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onViewportZoomChanged -> err = ' + err.stack) } + } + } + + function onDragFinished () { + try { + recalculate() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragFinished -> err = ' + err.stack) } + } + } + + function onDisplace (event) { + recalculateAll(event) + } + + function onViewportPositionChanged (event) { + recalculateAll(event) + } + + function recalculateAll (event) { + mustRecalculateDataPoints = true + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + } + } +} diff --git a/CanvasApp/PostLoader.js b/CanvasApp/PostLoader.js new file mode 100644 index 000000000..bb34c97ea --- /dev/null +++ b/CanvasApp/PostLoader.js @@ -0,0 +1,82 @@ +let canvas +let markets +let ecosystem = newEcosystem() +let systemEventHandler + +let APP_SCHEMA_MAP = new Map() +let APP_SCHEMA_ARRAY = [] + +function newDashboard () { + const MODULE_NAME = 'Dashboard' + const ERROR_LOG = true + const INTENSIVE_LOG = false + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + start: start + } + + 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. + + return thisObject + + function start () { + try { + setBrowserEvents() + + systemEventHandler = newSystemEventHandler() + systemEventHandler.initialize(setUpAppSchema) + + function setUpAppSchema () { + APP_SCHEMA_ARRAY = getAppSchema() + for (let i = 0; i < APP_SCHEMA_ARRAY.length; i++) { + let nodeDefinition = APP_SCHEMA_ARRAY[i] + let key = nodeDefinition.type + + APP_SCHEMA_MAP.set(key, nodeDefinition) + } + + startCanvas() + } + + function startCanvas () { + /* If this method is executed for a second time, it should finalize the current execution structure */ + + if (canvas !== undefined) { canvas.finalize() } + + setTimeout(delayedStart, DEBUG_START_UP_DELAY) + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] start -> err = ' + err.stack) } + } + } + + function delayedStart () { + try { + /* For now, we are supporting only one market. */ + + let market = { + id: 2, + baseAsset: 'USDT', + quotedAsset: 'BTC' + } + + markets = new Map() + + markets.set(market.id, market) + + canvas = newCanvas() + canvas.initialize() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] delayedStart -> err = ' + err.stack) } + } + } + + function setBrowserEvents () { + window.onbeforeunload = onBrowserClosed + function onBrowserClosed () { + // canvas.designSpace.workspace.stopAllRunningTasks() + } + } +} diff --git a/CanvasApp/ProductStorage.js b/CanvasApp/ProductStorage.js new file mode 100644 index 000000000..4e0c4142a --- /dev/null +++ b/CanvasApp/ProductStorage.js @@ -0,0 +1,333 @@ +  +function newProductStorage (name) { + const MODULE_NAME = 'Product Storage' + const ERROR_LOG = true + const logger = newWebDebugLog() + 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 = { + eventHandler: undefined, + marketFiles: [], + dailyFiles: [], + singleFile: [], + fileSequences: [], + setDatetime: setDatetime, + setTimeFrame: setTimeFrame, + initialize: initialize, + finalize: finalize + } + + thisObject.eventHandler = newEventHandler() + + /* We name the event Handler to easy debugging. */ + + thisObject.eventHandler.name = 'Storage-' + name + + let datetime + let timeFrame + + return thisObject + + function finalize () { + try { + for (let i = 0; i < thisObject.marketFiles.length; i++) { + let marketFile = thisObject.marketFiles[i] + marketFile.finalize() + } + + thisObject.marketFiles = undefined + + for (let i = 0; i < thisObject.dailyFiles.length; i++) { + let dailyFile = thisObject.dailyFiles[i] + dailyFile.finalize() + } + + thisObject.dailyFiles = undefined + + for (let i = 0; i < thisObject.fileSequences.length; i++) { + let fileSequence = thisObject.fileSequences[i] + fileSequence.finalize() + } + + thisObject.fileSequences = undefined + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } + } + } + + function initialize (dataMine, bot, session, product, exchange, market, pDatetime, pTimeFrame, callBackFunction) { + try { + datetime = pDatetime + timeFrame = pTimeFrame + + let dataSetsToLoad = 0 + let dataSetsLoaded = 0 + + for (let i = 0; i < product.datasets.length; i++) { + let dataset = product.datasets[i] + + switch (dataset.code.type) { + case 'Market Files': { + dataSetsToLoad++ + + let marketFiles = newMarketFiles() + marketFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, onMarketFileReady) + thisObject.marketFiles.push(marketFiles) + } + break + + case 'Daily Files': { + dataSetsToLoad++ + + let dailyFiles = newDailyFiles() + dailyFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, pDatetime, pTimeFrame, onDailyFileReady) + thisObject.dailyFiles.push(dailyFiles) + } + break + + case 'Single File': { + dataSetsToLoad++ + + let singleFile = newSingleFile() + singleFile.initialize(dataMine, bot, session, product, dataset, exchange, market, onSingleFileReady) + thisObject.singleFile.push(singleFile) + } + break + + case 'File Sequence': { + dataSetsToLoad++ + + let fileSequences = newFileSequence() + fileSequences.initialize(dataMine, bot, session, product, dataset, exchange, market, onFileSequenceReady) + thisObject.fileSequences.push(fileSequences) + } + break + default: + if (ERROR_LOG === true) { logger.write('[WARN] initialize -> dataset with no type defined or type not supported -> dataset.code.type = ' + dataset.code.type) } + } + + function onMarketFileReady (err, pCaller) { + try { + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + } + let event = { + totalValue: pCaller.getExpectedFiles(), + currentValue: pCaller.getFilesLoaded(), + filesNotLoaded: pCaller.getFilesNotLoaded() + } + + thisObject.eventHandler.raiseEvent('Market File Loaded', event) + + if (event.filesNotLoaded === event.totalValue) { + dataSetsToLoad-- + checkInitializeComplete() + return + } + + if (event.currentValue + event.filesNotLoaded === event.totalValue) { + dataSetsLoaded++ + checkInitializeComplete() + } + } catch (err) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function onDailyFileReady (err, pCaller) { + try { + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + if (err.message === 'Dataset Unavailable.') { + dataSetsToLoad-- + checkInitializeComplete() + return + } else { + callBackFunction(err) + return + } + } + default: { + 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) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function onSingleFileReady (err, pCaller) { + try { + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + callBackFunction(err) + return + } + + default: { + 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) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function onFileSequenceReady (err, pCaller) { + try { + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + callBackFunction(err) + return + } + default: { + callBackFunction(err) + return + } + } + let event + if (pCaller !== undefined) { + event = { + totalValue: pCaller.getExpectedFiles(), + currentValue: pCaller.getFilesLoaded() + } + } else { // When there are no data the reference to the caller wont arrive here. + event = { + totalValue: 1, + currentValue: 1 + } + } + + thisObject.eventHandler.raiseEvent('File Sequence Loaded', event) + + if (event.currentValue === event.totalValue) { + dataSetsLoaded++ + checkInitializeComplete() + } + } catch (err) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function checkInitializeComplete () { + try { + if (dataSetsLoaded === dataSetsToLoad) { + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> checkInitializeComplete -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function setDatetime (pDatetime) { + /* 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 = new Date(pDatetime) + + if (currentDate !== newDate) { + if (timeFrame <= _1_HOUR_IN_MILISECONDS) { + for (let i = 0; i < thisObject.dailyFiles.length; i++) { + thisObject.dailyFiles[i].setDatetime(pDatetime) + } + } + } + } + + function setTimeFrame (pTimeFrame) { + /* We are going to filter out the cases in which the timeFrame received is the same that the one we already know. */ + + if (timeFrame !== pTimeFrame) { + timeFrame = pTimeFrame + + if (timeFrame <= _1_HOUR_IN_MILISECONDS) { + for (let i = 0; i < thisObject.dailyFiles.length; i++) { + thisObject.dailyFiles[i].setTimeFrame(pTimeFrame, datetime) + } + } + } + } +} diff --git a/CanvasApp/Scales/AutoScaleButton.js b/CanvasApp/Scales/AutoScaleButton.js new file mode 100644 index 000000000..1034443ca --- /dev/null +++ b/CanvasApp/Scales/AutoScaleButton.js @@ -0,0 +1,194 @@ +function newAutoScaleButton () { + let thisObject = { + container: undefined, + fitFunction: undefined, + parentContainer: undefined, + autoMinScale: undefined, + autoMaxScale: undefined, + setStatus: setStatus, + draw: draw, + getContainer: getContainer, // returns the inner most container that holds the point received by parameter. + initialize: initialize, + finalize: finalize + } + + /* Cointainer stuff */ + thisObject.container = newContainer() + thisObject.container.name = 'Auto Scale Button' + thisObject.container.initialize() + thisObject.container.isClickeable = true + thisObject.container.isDraggeable = false + thisObject.container.frame.containerName = 'Auto Scale Button' + + const ICON_SIZE = 36 + let onMouseWheelEventSubscriptionId + let coordinateSystem + let axis + let currentStatus + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId) + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.parentContainer = undefined + thisObject.panels = undefined + + coordinateSystem = undefined + axis = undefined + } + + function initialize (pAxis, pCoordinateSystem) { + axis = pAxis + coordinateSystem = pCoordinateSystem + thisObject.autoMinScale = coordinateSystem.autoMinYScale + thisObject.autoMaxScale = coordinateSystem.autoMaxYScale + + switch (axis) { + case 'X': { + thisObject.container.frame.width = ICON_SIZE + thisObject.container.frame.height = ICON_SIZE + + let position = { + x: thisObject.container.parentContainer.frame.width * 7 / 8, + y: thisObject.container.parentContainer.frame.height / 2 + } + + thisObject.container.frame.position = position + break + } + case 'Y': { + thisObject.container.frame.width = ICON_SIZE + thisObject.container.frame.height = ICON_SIZE + + let position = { + x: thisObject.container.parentContainer.frame.width * 7 / 8, + y: thisObject.container.parentContainer.frame.height / 2 + } + + thisObject.container.frame.position = position + break + } + } + onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel) + } + + function setStatus (autoMinScale, autoMaxScale) { + thisObject.autoMinScale = autoMinScale + thisObject.autoMaxScale = autoMaxScale + + if (thisObject.autoMinScale === false && thisObject.autoMaxScale === false) { + currentStatus = 1 + } + if (thisObject.autoMinScale === true && thisObject.autoMaxScale === false) { + currentStatus = 2 + } + if (thisObject.autoMinScale === false && thisObject.autoMaxScale === true) { + currentStatus = 3 + } + if (thisObject.autoMinScale === true && thisObject.autoMaxScale === true) { + currentStatus = 4 + } + } + function onMouseWheel (event) { + let factor + let delta = event.wheelDelta + if (delta < 0) { + delta = -1 + } else { + delta = 1 + } + currentStatus = currentStatus + delta + if (currentStatus === 5) { currentStatus = 1 } + if (currentStatus === 0) { currentStatus = 4 } + + switch (currentStatus) { + case 1: { + thisObject.autoMinScale = false + thisObject.autoMaxScale = false + update() + return + } + case 2: { + thisObject.autoMinScale = true + thisObject.autoMaxScale = false + update() + return + } + case 3: { + thisObject.autoMinScale = false + thisObject.autoMaxScale = true + update() + return + } + case 4: { + thisObject.autoMinScale = true + thisObject.autoMaxScale = true + update() + return + } + } + + function update () { + switch (axis) { + case 'X': { + coordinateSystem.autoMinXScale = thisObject.autoMinScale + coordinateSystem.autoMaxXScale = thisObject.autoMaxScale + break + } + case 'Y': { + coordinateSystem.autoMinYScale = thisObject.autoMinScale + coordinateSystem.autoMaxYScale = thisObject.autoMaxScale + break + } + } + + coordinateSystem.recalculateScale() + } + } + + function getContainer (point, purpose) { + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } + } + + function draw () { + let icon + switch (axis) { + case 'X': { + if (thisObject.autoMinScale === false && thisObject.autoMaxScale === false) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-manual') + } + if (thisObject.autoMinScale === true && thisObject.autoMaxScale === false) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-auto-min') + } + if (thisObject.autoMinScale === false && thisObject.autoMaxScale === true) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-auto-max') + } + if (thisObject.autoMinScale === true && thisObject.autoMaxScale === true) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-auto-min-max') + } + drawIcon(icon, 0, 0, 0, 0, ICON_SIZE, thisObject.container) + break + } + case 'Y': { + if (thisObject.autoMinScale === false && thisObject.autoMaxScale === false) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-manual') + } + if (thisObject.autoMinScale === true && thisObject.autoMaxScale === false) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-auto-min') + } + if (thisObject.autoMinScale === false && thisObject.autoMaxScale === true) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-auto-max') + } + if (thisObject.autoMinScale === true && thisObject.autoMaxScale === true) { + icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-auto-min-max') + } + drawIcon(icon, 0, 0, 0, 0, ICON_SIZE, thisObject.container) + break + } + } + } +} diff --git a/CanvasApp/Scales/Commons.js b/CanvasApp/Scales/Commons.js new file mode 100644 index 000000000..49dda6e71 --- /dev/null +++ b/CanvasApp/Scales/Commons.js @@ -0,0 +1,45 @@ + +function drawScaleDisplay (label1, label2, label3, xExtraOffSet1, xExtraOffSet2, xExtraOffSet3, icon1, icon2, container, backgroundColor) { + const RED_LINE_HIGHT = 4 + const OPACITY = 1 + + let params = { + cornerRadius: 5, + lineWidth: 1, + container: container, + borderColor: UI_COLOR.RUSTED_RED, + castShadow: false, + backgroundColor: backgroundColor, + opacity: OPACITY + } + + roundedCornersBackground(params) + + /* Place the Text */ + + label1 = label1.substring(0, 18) + label2 = label2.substring(0, 20) + label3 = label3.substring(0, 20) + + drawLabel(label1, 1 / 2, 92 / 100, 0, 0, 9, container) + drawLabel(label2, 1 / 2, 42 / 100, 0, 0, 17, container) + drawLabel(label3, 1 / 2, 67 / 100, 0, 0, 9, container) + + drawIcon(icon1, 1 / 8, 1 / 2, 0, 0, 28, container) + drawIcon(icon2, 7 / 8, 1 / 2, 0, 0, 28, container) +} + +function drawScaleDisplayCover (container) { + let params = { + cornerRadius: 5, + lineWidth: 1, + container: container, + borderColor: UI_COLOR.WHITE, + castShadow: false, + backgroundColor: UI_COLOR.WHITE, + opacity: '0.25' + } + + roundedCornersBackground(params) +} + diff --git a/CanvasApp/Scales/RateScale.js b/CanvasApp/Scales/RateScale.js new file mode 100644 index 000000000..07b2396a7 --- /dev/null +++ b/CanvasApp/Scales/RateScale.js @@ -0,0 +1,423 @@ +function newRateScale () { + const MODULE_NAME = 'Right Scale' + + let thisObject = { + container: undefined, + rate: undefined, + fitFunction: undefined, + payload: undefined, + minValue: undefined, + maxValue: undefined, + isVisible: true, + layersOn: undefined, + onUpstreamScaleChanged: onUpstreamScaleChanged, + onMouseOverSomeTimeMachineContainer: onMouseOverSomeTimeMachineContainer, + physics: physics, + draw: draw, + drawForeground: drawForeground, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isClickeable = false + thisObject.container.isWheelable = true + thisObject.container.detectMouseOver = true + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL + thisObject.container.frame.height = 40 + + let draggeableContainer = newContainer() + draggeableContainer.initialize(MODULE_NAME + ' Draggeable') + draggeableContainer.isDraggeable = true + + let visible = true + let isMouseOver + + let onMouseWheelEventSubscriptionId + let onMouseOverEventSubscriptionId + let onMouseNotOverEventSubscriptionId + let onScaleChangedEventSubscriptionId + + let coordinateSystem + let limitingContainer + let rateCalculationsContainer + + let mouse = { + position: { + x: 0, + y: 0 + } + } + + let scaleDisplayTimer = 0 + let autoScaleButton + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId) + coordinateSystem.eventHandler.stopListening(onScaleChangedEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.payload = undefined + + coordinateSystem = undefined + limitingContainer = undefined + rateCalculationsContainer = undefined + mouse = undefined + + autoScaleButton.finalize() + autoScaleButton = undefined + } + + function initialize (pCoordinateSystem, pLimitingContainer, pRateCalculationsContainer) { + coordinateSystem = pCoordinateSystem + limitingContainer = pLimitingContainer + rateCalculationsContainer = pRateCalculationsContainer + + thisObject.minValue = coordinateSystem.min.y + thisObject.maxValue = coordinateSystem.max.y + + onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel) + onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + onScaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + + autoScaleButton = newAutoScaleButton() + autoScaleButton.container.connectToParent(thisObject.container) + autoScaleButton.initialize('Y', coordinateSystem) + + readObjectState() + } + + function onMouseOverSomeTimeMachineContainer (event) { + if (event.containerId === undefined) { + /* This happens when the mouse over was not at the instance of a certain scale, but anywhere else. */ + visible = true + } else { + if (event.containerId === thisObject.container.id) { + visible = true + } else { + visible = false + turnOnCounter = 0 + } + } + mouse = { + position: { + x: event.x, + y: event.y + } + } + } + + function onScaleChanged () { + saveObjectState() + } + + function onMouseOver (event) { + isMouseOver = true + event.containerId = thisObject.container.id + thisObject.container.eventHandler.raiseEvent('onMouseOverScale', event) + } + + function onMouseNotOver () { + isMouseOver = false + scaleDisplayTimer = 0 + } + + function onMouseWheel (event) { + if (event.shiftKey === true) { + autoScaleButton.container.eventHandler.raiseEvent('onMouseWheel', event) + return + } + + let factor + let morePower = 10 + if (event.buttons === 4) { morePower = 1 } // Mouse wheel pressed. + + let delta = event.wheelDelta + if (delta < 0) { + factor = -0.01 * morePower + } else { + factor = 0.01 * morePower + } + + coordinateSystem.zoomY(factor, event, limitingContainer) + scaleDisplayTimer = 100 + } + + function getContainer (point, purpose) { + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + if (purpose === GET_CONTAINER_PURPOSE.DRAGGING) { + return draggeableContainer + } + + return thisObject.container + } + } + + function saveObjectState () { + try { + let code = JSON.parse(thisObject.payload.node.code) + code.minValue = coordinateSystem.min.y + code.maxValue = coordinateSystem.max.y + code.autoMinScale = coordinateSystem.autoMinYScale + code.autoMaxScale = coordinateSystem.autoMaxYScale + thisObject.payload.node.code = JSON.stringify(code, null, 4) + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + } + + function readObjectState () { + try { + let code = JSON.parse(thisObject.payload.node.code) + + if ( + (isNaN(code.minValue) || code.minValue === null || code.minValue === undefined) || + (isNaN(code.maxValue) || code.maxValue === null || code.maxValue === undefined) + ) { + // not using this value + } else { + if ( + thisObject.minValue !== code.minValue || + thisObject.maxValue !== code.maxValue || + coordinateSystem.autoMinYScale !== code.autoMinScale || + coordinateSystem.autoMaxYScale !== code.autoMaxScale + ) { + thisObject.minValue = code.minValue + thisObject.maxValue = code.maxValue + coordinateSystem.min.y = thisObject.minValue + coordinateSystem.max.y = thisObject.maxValue + + if (code.autoMinScale !== undefined && (code.autoMinScale === true || code.autoMinScale === false) && code.autoMaxScale !== undefined && (code.autoMaxScale === true || code.autoMaxScale === false)) { + coordinateSystem.autoMinYScale = code.autoMinScale + coordinateSystem.autoMaxYScale = code.autoMaxScale + autoScaleButton.setStatus(code.autoMinScale, code.autoMaxScale) + } + + coordinateSystem.recalculateScale() + } + } + saveObjectState() // this overrides any invalid value at the config. + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + } + + function physics () { + scaleDisplayTimer-- + readObjectState() + positioningPhysics() + draggingPhysics() + } + + function draggingPhysics () { + if (draggeableContainer.frame.position.y === 0) { return } + + let dragVector = { + x: draggeableContainer.frame.position.x, + y: draggeableContainer.frame.position.y + } + + draggeableContainer.frame.position.x = 0 + draggeableContainer.frame.position.y = 0 + + let point = { + x: 0, + y: -dragVector.y + } + + let newMaxRate = getRateFromPointAtContainer(point, limitingContainer, coordinateSystem) + let yDifferenceMaxMin = coordinateSystem.max.y - coordinateSystem.min.y + + coordinateSystem.min.y = newMaxRate - yDifferenceMaxMin + coordinateSystem.max.y = newMaxRate + coordinateSystem.recalculateScale() + } + + function onUpstreamScaleChanged (event) { + if (event === undefined) { return } + if (event.type === 'center dragged') { + let point = { + x: event.dragVector.x, + y: -event.dragVector.y + } + let newMinDate = getDateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem) + let newMaxRate = getRateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem) + let xDifferenceMaxMin = coordinateSystem.max.x - coordinateSystem.min.x + let yDifferenceMaxMin = coordinateSystem.max.y - coordinateSystem.min.y + coordinateSystem.min.x = newMinDate.valueOf() + coordinateSystem.max.x = newMinDate.valueOf() + xDifferenceMaxMin + coordinateSystem.min.y = newMaxRate - yDifferenceMaxMin + coordinateSystem.max.y = newMaxRate + + coordinateSystem.recalculateScale() + } + if (event.type === 'top dragged') { + let point = { + x: event.dragVector.x, + y: event.dragVector.y + } + let newMaxRate = getRateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem) + + coordinateSystem.max.y = newMaxRate + coordinateSystem.maxHeight = rateCalculationsContainer.frame.height + coordinateSystem.recalculateScale() + } + if (event.type === 'bottom dragged') { + let point = { + x: event.dragVector.x, + y: event.dragVector.y + rateCalculationsContainer.frame.height + } + let newMinRate = getRateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem) + + coordinateSystem.min.y = newMinRate + coordinateSystem.maxHeight = rateCalculationsContainer.frame.height + coordinateSystem.recalculateScale() + } + } + + function positioningPhysics () { + /* Container Limits */ + + let upCorner = { + x: 0, + y: 0 + } + + let bottonCorner = { + x: limitingContainer.frame.width, + y: limitingContainer.frame.height + } + + upCorner = transformThisPoint(upCorner, limitingContainer) + bottonCorner = transformThisPoint(bottonCorner, limitingContainer) + + upCorner = limitingContainer.fitFunction(upCorner, true) + bottonCorner = limitingContainer.fitFunction(bottonCorner, true) + + /* We will check if we need to change the bottom because of being one of many scales of the same type */ + let displaceFactor = 0 + if (thisObject.payload.parentNode === undefined) { return } // This happens when in the process of deleting the scale, timeline chart or time machine. + if (thisObject.payload.parentNode.type === 'Timeline Chart') { + if (thisObject.payload.parentNode.payload.parentNode !== undefined) { + if (thisObject.payload.parentNode.payload.parentNode.rateScale !== undefined) { + if (thisObject.payload.parentNode.payload.parentNode.rateScale.payload.isVisible === true) { + displaceFactor++ + } + } + for (let i = 0; i < thisObject.payload.parentNode.payload.parentNode.timelineCharts.length; i++) { + let timelineChart = thisObject.payload.parentNode.payload.parentNode.timelineCharts[i] + if (timelineChart.rateScale !== undefined) { + if (thisObject.payload.node.id !== timelineChart.rateScale.id) { + if (timelineChart.rateScale.payload.isVisible === true) { + displaceFactor++ + } + } else { + break + } + } + } + } + } + bottonCorner.x = bottonCorner.x - thisObject.container.frame.width * displaceFactor + + /* Mouse Position Rate Calculation */ + let ratePoint = { + x: 0, + y: mouse.position.y + } + + thisObject.rate = getRateFromPointAtBrowserCanvas(ratePoint, rateCalculationsContainer, coordinateSystem) + + /* rateScale Positioning */ + ratePoint = { + x: limitingContainer.frame.width, + y: 0 + } + + ratePoint = transformThisPoint(ratePoint, limitingContainer.frame.container) + ratePoint.y = mouse.position.y - thisObject.container.frame.height / 2 + thisObject.container.frame.height + + /* Checking against the container limits. */ + if (ratePoint.x < upCorner.x) { ratePoint.x = upCorner.x } + if (ratePoint.x + thisObject.container.frame.width > bottonCorner.x) { ratePoint.x = bottonCorner.x } + if (ratePoint.y < upCorner.y + thisObject.container.frame.height) { ratePoint.y = upCorner.y + thisObject.container.frame.height } + if (ratePoint.y > bottonCorner.y) { ratePoint.y = bottonCorner.y } + + thisObject.container.frame.position.y = ratePoint.y - thisObject.container.frame.height + thisObject.container.frame.position.x = ratePoint.x - thisObject.container.frame.width + + thisObject.isVisible = true + thisObject.payload.isVisible = true + if (thisObject.container.frame.position.y + thisObject.container.frame.height * 2 > bottonCorner.y || + thisObject.container.frame.position.y - thisObject.container.frame.height * 1 < upCorner.y || + thisObject.container.frame.position.x < upCorner.x + ) { + thisObject.isVisible = false + thisObject.payload.isVisible = false + } + if (thisObject.layersOn === 0) { + thisObject.isVisible = false + thisObject.payload.isVisible = false + } + } + + function draw () { + drawScaleBox() + autoScaleButton.draw() + if (visible === false) { + drawScaleDisplayCover(thisObject.container) + } + } + + function drawForeground () { + if (isMouseOver === true) { + drawScaleBox() + autoScaleButton.draw() + } + } + + function drawScaleBox () { + if (thisObject.rate === undefined) { return } + if (thisObject.payload === undefined) { return } + if (thisObject.payload.node === undefined) { return } + + let rate = thisObject.rate + + if (rate < coordinateSystem.min.y) { + rate = coordinateSystem.min.y + } + if (rate > coordinateSystem.max.y) { + rate = coordinateSystem.max.y + } + + let label = (rate - Math.trunc(rate)).toFixed(2) + let labelArray = label.split('.') + let label1 = thisObject.payload.node.payload.parentNode.name + let label2 = (Math.trunc(rate)).toLocaleString() + let label3 = labelArray[1] + if (label3 === undefined) { label3 = '00' } + + let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type) + let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type) + + let backgroundColor = UI_COLOR.BLACK + + if (scaleDisplayTimer > 0) { + label2 = (coordinateSystem.scale.y * 10000).toFixed(2) + label3 = 'SCALE' + } + + drawScaleDisplay(label1, label2, label3, 0, 0, 0, icon1, icon2, thisObject.container, backgroundColor) + } +} diff --git a/CanvasApp/Scales/TimeFrameScale.js b/CanvasApp/Scales/TimeFrameScale.js new file mode 100644 index 000000000..5015c1e31 --- /dev/null +++ b/CanvasApp/Scales/TimeFrameScale.js @@ -0,0 +1,350 @@ +function newTimeFrameScale () { + const MODULE_NAME = 'Time Frame Scale' + + let thisObject = { + timeFrame: undefined, + container: undefined, + fitFunction: undefined, + payload: undefined, + isVisible: true, + layersOn: undefined, + onMouseOverSomeTimeMachineContainer: onMouseOverSomeTimeMachineContainer, + draw: draw, + drawForeground: drawForeground, + physics: physics, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + const FILES_PERIOD_DEFAULT_VALUE = 0 + const TIME_PERIOD_DEFAULT_VALUE = 0 + const MIN_HEIGHT = 50 + + let previousIsVisible = true + let greyOutCoverLayer = false + let isMouseOver + + let filePeriodIndex = FILES_PERIOD_DEFAULT_VALUE + let timeFrameIndex = TIME_PERIOD_DEFAULT_VALUE + let timeFramesMasterArray = [marketFilesPeriods, dailyFilePeriods] + let timeFrameLabel = '24-hs' + + let onMouseWheelEventSubscriptionId + let onViewportZoomChangedEventSubscriptionId + let onMouseOverEventSubscriptionId + let onMouseNotOverEventSubscriptionId + + let limitingContainer + + let mouse = { + position: { + x: 0, + y: 0 + } + } + setupContainer() + return thisObject + + function setupContainer () { + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isClickeable = false + thisObject.container.isWheelable = true + thisObject.container.detectMouseOver = true + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL + thisObject.container.frame.height = 40 + } + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(onViewportZoomChangedEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.payload = undefined + + limitingContainer = undefined + mouse = undefined + } + + function initialize (pLimitingContainer) { + limitingContainer = pLimitingContainer + + readObjectState() + newTimeFrame() + + onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel) + onViewportZoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Zoom Changed', onViewportZoomChanged) + onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + } + + function onMouseOverSomeTimeMachineContainer (event) { + if (event.containerId === undefined) { + /* This happens when the mouse over was not at the instance of a certain scale, but anywhere else. */ + greyOutCoverLayer = false + } else { + if (event.containerId === thisObject.container.id) { + greyOutCoverLayer = false + } else { + greyOutCoverLayer = true + turnOnCounter = 0 + } + } + mouse = { + position: { + x: event.x, + y: event.y + } + } + } + + function onMouseOver (event) { + isMouseOver = true + event.containerId = thisObject.container.id + thisObject.container.eventHandler.raiseEvent('onMouseOverScale', event) + } + + function onMouseNotOver () { + isMouseOver = false + } + + function physics () { + readObjectState() + positioningPhysics() + zoomOutPhysics() + } + + function zoomOutPhysics () { + if ((thisObject.isVisible === true && previousIsVisible === false) && (canvas.chartingSpace.viewport.zoomTargetLevel >= ZOOM_OUT_THRESHOLD_FOR_CHANGING_TIME_FRAME)) { + let event = {} + event.timeFrame = thisObject.timeFrame + thisObject.container.eventHandler.raiseEvent('Time Frame Value Changed', event) + previousIsVisible = true + } + if ((thisObject.isVisible === false && previousIsVisible === true) || (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_CHANGING_TIME_FRAME)) { + let event = {} + event.timeFrame = ONE_DAY_IN_MILISECONDS + thisObject.container.eventHandler.raiseEvent('Time Frame Value Changed', event) + previousIsVisible = false + } + } + + function positioningPhysics () { + /* Container Limits */ + + let upCorner = { + x: 0, + y: 0 + } + + let bottonCorner = { + x: limitingContainer.frame.width, + y: limitingContainer.frame.height + } + + upCorner = transformThisPoint(upCorner, limitingContainer) + bottonCorner = transformThisPoint(bottonCorner, limitingContainer) + + upCorner = limitingContainer.fitFunction(upCorner, true) + bottonCorner = limitingContainer.fitFunction(bottonCorner, true) + + /* We will check if we need to change the bottom because of being one of many scales of the same type */ + let displaceFactor = 0 + if (thisObject.payload.parentNode === undefined) { return } // This happens when in the process of deleting the scale, timeline chart or time machine. + if (thisObject.payload.parentNode.type === 'Timeline Chart') { + if (thisObject.payload.parentNode.payload.parentNode !== undefined) { + if (thisObject.payload.parentNode.payload.parentNode.timeFrameScale !== undefined) { + if (thisObject.payload.parentNode.payload.parentNode.timeFrameScale.payload.isVisible === true) { + displaceFactor++ + } + } + for (let i = 0; i < thisObject.payload.parentNode.payload.parentNode.timelineCharts.length; i++) { + let timelineChart = thisObject.payload.parentNode.payload.parentNode.timelineCharts[i] + if (timelineChart.timeFrameScale !== undefined) { + if (thisObject.payload.node.id !== timelineChart.timeFrameScale.id) { + if (timelineChart.timeFrameScale.payload.isVisible === true) { + displaceFactor++ + } + } else { + break + } + } + } + } + } + bottonCorner.y = bottonCorner.y - thisObject.container.frame.height * displaceFactor + + /* Positioning */ + timePoint = { + x: 0, + y: limitingContainer.frame.height + } + + timePoint = transformThisPoint(timePoint, limitingContainer.frame.container) + timePoint.x = mouse.position.x - thisObject.container.frame.width / 2 + + /* Checking against the container limits. */ + if (timePoint.x < upCorner.x) { timePoint.x = upCorner.x } + if (timePoint.x + thisObject.container.frame.width > bottonCorner.x) { timePoint.x = bottonCorner.x - thisObject.container.frame.width } + if (timePoint.y < upCorner.y + thisObject.container.frame.height) { timePoint.y = upCorner.y + thisObject.container.frame.height } + if (timePoint.y > bottonCorner.y) { timePoint.y = bottonCorner.y } + + thisObject.container.frame.position.x = timePoint.x + thisObject.container.frame.position.y = timePoint.y - thisObject.container.frame.height + + thisObject.isVisible = true + thisObject.payload.isVisible = true + if (thisObject.container.frame.position.y + thisObject.container.frame.height > bottonCorner.y || + thisObject.container.frame.position.y - thisObject.container.frame.height * 3 < upCorner.y || + thisObject.container.frame.position.x < upCorner.x) { + thisObject.isVisible = false + thisObject.payload.isVisible = false + } + if (thisObject.layersOn === 0) { + thisObject.isVisible = false + thisObject.payload.isVisible = false + } + } + + function onViewportZoomChanged (event) { + if (event !== undefined) { // it is undefined when the level is just being animated. + if (event.shiftKey === false) { return } // with no shft key pressed, no automatic change in scale + let currentTimeFrame = thisObject.timeFrame + let timeFrame = recalculatePeriod(event.newLevel) + if (timeFrame !== currentTimeFrame) { + for (let i = 0; i < timeFramesMasterArray.length; i++) { + let timeFrameArray = timeFramesMasterArray[i] + for (let j = 0; j < timeFrameArray.length; j++) { + let record = timeFrameArray[j] + if (timeFrame === record[0]) { + filePeriodIndex = i + timeFrameIndex = j + } + } + } + newTimeFrame() + saveObjectState() + } + } + } + + function onMouseWheel (event) { + delta = event.wheelDelta + if (delta < 0) { + timeFrameIndex-- + if (timeFrameIndex < 0) { + filePeriodIndex-- + if (filePeriodIndex < 0) { + filePeriodIndex = 0 + timeFrameIndex = 0 + } else { + timeFrameIndex = timeFramesMasterArray[filePeriodIndex].length - 1 + } + } + } else { + timeFrameIndex++ + if (timeFrameIndex > timeFramesMasterArray[filePeriodIndex].length - 1) { + filePeriodIndex++ + if (filePeriodIndex > timeFramesMasterArray.length - 1) { + filePeriodIndex = timeFramesMasterArray.length - 1 + timeFrameIndex = timeFramesMasterArray[filePeriodIndex].length - 1 + } else { + timeFrameIndex = 0 + } + } + } + + newTimeFrame() + saveObjectState() + } + + function getContainer (point) { + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } + } + + function saveObjectState () { + try { + let code = JSON.parse(thisObject.payload.node.code) + code.value = timeFrameLabel + thisObject.payload.node.code = JSON.stringify(code) + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + } + + function readObjectState () { + try { + let code = JSON.parse(thisObject.payload.node.code) + if (code.value !== timeFrameLabel) { + let found = false + for (let i = 0; i < timeFramesMasterArray.length; i++) { + let timeFrameArray = timeFramesMasterArray[i] + for (let j = 0; j < timeFrameArray.length; j++) { + let record = timeFrameArray[j] + if (code.value === record[1]) { + filePeriodIndex = i + timeFrameIndex = j + found = true + } + } + } + if (found === true) { + newTimeFrame() + } else { + saveObjectState() + } + } + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + } + + function newTimeFrame () { + let timeFrameArray = timeFramesMasterArray[filePeriodIndex] + thisObject.timeFrame = timeFrameArray[timeFrameIndex][0] + timeFrameLabel = timeFrameArray[timeFrameIndex][1] + // console.log('NEW TIME FRAME LABEL', timeFrameLabel) + let event = {} + event.timeFrame = thisObject.timeFrame + thisObject.container.eventHandler.raiseEvent('Time Frame Value Changed', event) + } + + function draw () { + drawScaleBox() + if (greyOutCoverLayer === true) { + drawScaleDisplayCover(thisObject.container) + } + } + + function drawForeground () { + if (isMouseOver === true) { + drawScaleBox() + } + } + + function drawScaleBox () { + if (timeFrameLabel === undefined) { return } + + let label = timeFrameLabel.split('-') + let label1 = thisObject.payload.node.payload.parentNode.name + let label2 = label[0] + let label3 = label[1].toUpperCase() + + let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type) + let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type) + + let backgroundColor = UI_COLOR.BLACK + + drawScaleDisplay(label1, label2, label3, 0, 0, 0, icon1, icon2, thisObject.container, backgroundColor) + } +} diff --git a/CanvasApp/Scales/TimeScale.js b/CanvasApp/Scales/TimeScale.js new file mode 100644 index 000000000..f87354e66 --- /dev/null +++ b/CanvasApp/Scales/TimeScale.js @@ -0,0 +1,281 @@ +function newTimeScale () { + const MODULE_NAME = 'Time Scale' + + let thisObject = { + container: undefined, + date: undefined, + fitFunction: undefined, + payload: undefined, + isVisible: true, + fromDate: undefined, + toDate: undefined, + onMouseOverSomeTimeMachineContainer: onMouseOverSomeTimeMachineContainer, + physics: physics, + draw: draw, + drawForeground: drawForeground, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isClickeable = false + thisObject.container.isWheelable = true + thisObject.container.detectMouseOver = true + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL + thisObject.container.frame.height = 40 + + let visible = true + let autoScaleButton + let isMouseOver + + let onMouseWheelEventSubscriptionId + let onMouseOverEventSubscriptionId + let onMouseNotOverEventSubscriptionId + let onScaleChangedEventSubscriptionId + + let coordinateSystem + let limitingContainer + + let mouse = { + position: { + x: 0, + y: 0 + } + } + + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId) + coordinateSystem.eventHandler.stopListening(onScaleChangedEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.payload = undefined + + coordinateSystem = undefined + limitingContainer = undefined + mouse = undefined + + autoScaleButton.finalize() + autoScaleButton = undefined + } + + function initialize (pCoordinateSystem, pLimitingContainer) { + coordinateSystem = pCoordinateSystem + limitingContainer = pLimitingContainer + + onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel) + onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + onScaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + + autoScaleButton = newAutoScaleButton() + autoScaleButton.container.connectToParent(thisObject.container) + autoScaleButton.initialize('X', coordinateSystem) + + readObjectState() + } + + function onMouseOverSomeTimeMachineContainer (event) { + if (event.containerId === undefined) { + /* This happens when the mouse over was not at the instance of a certain scale, but anywhere else. */ + visible = true + } else { + if (event.containerId === thisObject.container.id) { + visible = true + } else { + visible = false + turnOnCounter = 0 + } + } + mouse = { + position: { + x: event.x, + y: event.y + } + } + } + + function onMouseOver (event) { + isMouseOver = true + event.containerId = thisObject.container.id + thisObject.container.eventHandler.raiseEvent('onMouseOverScale', event) + } + + function onMouseNotOver () { + isMouseOver = false + } + + function onMouseWheel (event) { + if (event.shiftKey === true) { + autoScaleButton.container.eventHandler.raiseEvent('onMouseWheel', event) + return + } + let factor + let morePower = 10 + if (event.buttons === 4) { morePower = 1 } // Mouse wheel pressed. + + let delta = event.wheelDelta + if (delta < 0) { + factor = -0.01 * morePower + } else { + factor = 0.01 * morePower + } + + coordinateSystem.zoomX(factor, event, limitingContainer, MODULE_NAME) + } + + function onScaleChanged () { + saveObjectState() + } + + function getContainer (point, purpose) { + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } + } + + function saveObjectState () { + try { + let code = JSON.parse(thisObject.payload.node.code) + code.fromDate = (new Date(coordinateSystem.min.x)).toISOString() + code.toDate = (new Date(coordinateSystem.max.x)).toISOString() + code.autoMinScale = coordinateSystem.autoMinXScale + code.autoMaxScale = coordinateSystem.autoMaxXScale + thisObject.payload.node.code = JSON.stringify(code, null, 4) + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + } + + function readObjectState () { + try { + let code = JSON.parse(thisObject.payload.node.code) + + if ( + (isNaN(Date.parse(code.fromDate)) || code.fromDate === null || code.fromDate === undefined) || + (isNaN(Date.parse(code.toDate)) || code.toDate === null || code.toDate === undefined) + ) { + // not using this value + } else { + if (thisObject.fromDate !== Date.parse(code.fromDate) || thisObject.toDate !== Date.parse(code.toDate)) { + thisObject.fromDate = Date.parse(code.fromDate) + thisObject.toDate = Date.parse(code.toDate) + coordinateSystem.min.x = thisObject.fromDate + coordinateSystem.max.x = thisObject.toDate + + if (code.autoMinScale !== undefined && (code.autoMinScale === true || code.autoMinScale === false) && code.autoMaxScale !== undefined && (code.autoMaxScale === true || code.autoMaxScale === false)) { + coordinateSystem.autoMinXScale = code.autoMinScale + coordinateSystem.autoMaxXScale = code.autoMaxScale + autoScaleButton.setStatus(code.autoMinScale, code.autoMaxScale) + } + coordinateSystem.recalculateScale() + } + } + saveObjectState() // this overrides any invalid value at the config. + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } + } + + function physics () { + readObjectState() + positioningPhysics() + } + + function positioningPhysics () { + /* Container Limits */ + + let upCorner = { + x: 0, + y: 0 + } + + let bottonCorner = { + x: limitingContainer.frame.width, + y: limitingContainer.frame.height + } + + upCorner = transformThisPoint(upCorner, limitingContainer) + bottonCorner = transformThisPoint(bottonCorner, limitingContainer) + + upCorner = limitingContainer.fitFunction(upCorner, true) + bottonCorner = limitingContainer.fitFunction(bottonCorner, true) + + /* Mouse Position Date Calculation */ + let timePoint = { + x: mouse.position.x, + y: 0 + } + + let mouseDate = getDateFromPointAtBrowserCanvas(timePoint, limitingContainer, coordinateSystem) + + thisObject.date = new Date(mouseDate) + + /* timeScale Positioning */ + timePoint = { + x: 0, + y: 0 + } + + timePoint = transformThisPoint(timePoint, limitingContainer.frame.container) + timePoint.x = mouse.position.x - thisObject.container.frame.width / 2 + + /* Checking against the container limits. */ + if (timePoint.x < upCorner.x) { timePoint.x = upCorner.x } + if (timePoint.x + thisObject.container.frame.width > bottonCorner.x) { timePoint.x = bottonCorner.x - thisObject.container.frame.width } + if (timePoint.y < upCorner.y) { timePoint.y = upCorner.y } + if (timePoint.y + thisObject.container.frame.height > bottonCorner.y) { timePoint.y = bottonCorner.y - thisObject.container.frame.height } + + thisObject.container.frame.position.x = timePoint.x + thisObject.container.frame.position.y = timePoint.y + + thisObject.isVisible = true + if (thisObject.container.frame.position.y + thisObject.container.frame.height * 4 > bottonCorner.y || + thisObject.container.frame.position.y < upCorner.y || + thisObject.container.frame.position.x < upCorner.x) { + thisObject.isVisible = false + } + } + + function draw () { + drawScaleBox() + autoScaleButton.draw() + if (visible === false) { + drawScaleDisplayCover(thisObject.container) + } + } + + function drawForeground () { + if (isMouseOver === true) { + drawScaleBox() + autoScaleButton.draw() + } + } + + function drawScaleBox () { + if (thisObject.date === undefined) { return } + + let label = thisObject.date.toUTCString() + let labelArray = label.split(' ') + let label1 = thisObject.payload.node.payload.parentNode.name + let label2 = labelArray[1] + ' ' + labelArray[2] + ' ' + labelArray[3] + let label3 = labelArray[4] + + let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type) + let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type) + + let backgroundColor = UI_COLOR.BLACK + + drawScaleDisplay(label1, label2, label3, 0, 0, 0, icon1, icon2, thisObject.container, backgroundColor) + } +} diff --git a/CanvasApp/Spaces/ChartingSpace.js b/CanvasApp/Spaces/ChartingSpace.js new file mode 100644 index 000000000..be86e976b --- /dev/null +++ b/CanvasApp/Spaces/ChartingSpace.js @@ -0,0 +1,403 @@ +/* + +The chart space is where all the charts live. What this space contains is a type of object we call Time Machine. A Time Machine is a cointainer of +charts objects. The idea is simple, all the chart objects are ploted according to the time of the Time Machine object. When the time of the Time Machine +changes, then all charts in it are replotted with the corresponging data. + +*/ + +function newChartingSpace () { + const MODULE_NAME = 'Charting Space' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + visible: undefined, + container: undefined, + inViewport: undefined, + timeMachines: undefined, + viewport: undefined, + payload: undefined, + reset: reset, + oneScreenUp: oneScreenUp, + oneScreenDown: oneScreenDown, + oneScreenLeft: oneScreenLeft, + oneScreenRight: oneScreenRight, + fitFunction: fitFunction, + isThisPointVisible: isThisPointVisible, + physics: physics, + draw: draw, + getContainer: getContainer, // returns the inner most container that holds the point received by parameter. + initialize: initialize, + finalize: finalize + } + + let canvasBrowserResizedEventSubscriptionId + let timeMachinesMap + let syncWithDesignerLoop + + const PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT = 25 + + initialSetup() + return thisObject + + function reset () { + finalize() + initialSetup() + initialize() + } + + function initialSetup () { + thisObject.timeMachines = [] + thisObject.visible = true + timeMachinesMap = new Map() + syncWithDesignerLoop = 0 + thisObject.inViewport = new Map() + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isWheelable = false + } + + function finalize () { + for (let i = 0; i < thisObject.timeMachines.length; i++) { + let timeMachine = thisObject.timeMachines[i] + timeMachine.finalize() + } + + thisObject.inViewport = undefined + thisObject.timeMachines = undefined + timeMachinesMap = undefined + + thisObject.container.eventHandler.stopListening(canvasBrowserResizedEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + + thisObject.viewport.finalize() + thisObject.viewport = undefined + + thisObject.payload = undefined + } + + function initialize () { + let rootNodes = canvas.designSpace.workspace.workspaceNode.rootNodes + for (let i = 0; i < rootNodes.length; i++) { + let rootNode = rootNodes[i] + if (rootNode.type === 'Charting Space') { + thisObject.payload = rootNode.payload + } + } + + if (thisObject.payload === undefined) { + if (ERROR_LOG === true) { logger.write('[WARN] initialize -> There must exist a Charting Space at the Design Space in order to enable Charts. PLease create one and refreash your browser. ') } + return + } + + thisObject.viewport = newViewport() + if (thisObject.payload.node.viewport === undefined) { + if (ERROR_LOG === true) { logger.write('[WARN] initialize -> There must exist a Viewport node attached to the Charting Space node in order for the Viewport save its properties. ') } + return + } else { + thisObject.viewport.payload = thisObject.payload.node.viewport.payload + } + + thisObject.viewport.initialize() + + canvasBrowserResizedEventSubscriptionId = canvas.eventHandler.listenToEvent('Browser Resized', resize) + } + + function getContainer (point, purpose) { + if (thisObject.visible !== true) { return } + if (thisObject.viewport.isThisPointInViewport(point) === false) { return } + + let container + + /* This first step is not actually to get the container but to allow things to be turned off in case there is a switch of focus from one time machine to another */ + for (let i = 0; i < thisObject.timeMachines.length; i++) { + container = thisObject.timeMachines[i].container + if (purpose === GET_CONTAINER_PURPOSE.MOUSE_OVER) { + container.eventHandler.raiseEvent('onMouseNotOver') + } + } + + /* + Now we see which is the inner most container that has it. + In this case we will ask all the time machines even if one of them already returned a matching container. + This will help the others know they are not on ofcus + */ + let containerFound + for (let i = 0; i < thisObject.timeMachines.length; i++) { + container = thisObject.timeMachines[i].getContainer(point, purpose) + if (container !== undefined) { + if (purpose !== undefined) { + containerFound = container + } else { + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + containerFound = container + } + } + } + } + if (containerFound !== undefined) { + containerFound.space = 'Charting Space' + return containerFound + } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + thisObject.container.space = 'Charting Space' + return thisObject.container + } else { + return undefined + } + } + + function resize () { + thisObject.viewport.resize() + thisObject.container.frame.width = browserCanvas.width + thisObject.container.frame.height = COCKPIT_SPACE_POSITION + } + + function oneScreenUp () { + let displaceVector = { + x: 0, + y: browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100 + } + + canvas.chartingSpace.viewport.displace(displaceVector) + } + + function oneScreenDown () { + let displaceVector = { + x: 0, + y: -browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100 + } + + canvas.chartingSpace.viewport.displace(displaceVector) + } + + function oneScreenLeft () { + let displaceVector = { + x: browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100, + y: 0 + } + + canvas.chartingSpace.viewport.displace(displaceVector, true) + } + + function oneScreenRight () { + let displaceVector = { + x: -browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100, + y: 0 + } + + canvas.chartingSpace.viewport.displace(displaceVector, true) + } + + function fitFunction (point, fullVisible) { + /* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */ + + let returnPoint = { + x: point.x, + y: point.y + } + + if (point.x > browserCanvas.width) { + returnPoint.x = browserCanvas.width + } + + if (point.x < 0) { + returnPoint.x = 0 + } + + if (fullVisible === true) { + if (point.y > COCKPIT_SPACE_POSITION) { + returnPoint.y = COCKPIT_SPACE_POSITION + } + } else { + if (point.y > COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT / 2) { + returnPoint.y = COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT / 2 + } + } + + if (point.y < TOP_SPACE_HEIGHT) { + returnPoint.y = TOP_SPACE_HEIGHT + } + + return returnPoint + } + + function isThisPointVisible (point) { + if (point.x > browserCanvas.width) { + return false + } + + if (point.x < 0) { + return false + } + + if (point.y > COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT / 2) { + return false + } + + if (point.y < 0) { + return false + } + + return true + } + + function physics () { + if (thisObject.viewport !== undefined) { + thisObject.viewport.physics() + } + + thisObjectPhysics() + childrenPhysics() + syncWithDesigner() + } + + function syncWithDesigner () { + if (thisObject.payload === undefined) { return } + if (thisObject.payload.node === undefined) { return } + syncWithDesignerLoop = syncWithDesignerLoop + 0.00000000001 + + if (thisObject.payload.node.dashboards !== undefined) { + for (let i = 0; i < thisObject.payload.node.dashboards.length; i++) { + let dashboard = thisObject.payload.node.dashboards[i] + if (dashboard.timeMachines !== undefined) { + for (let j = 0; j < dashboard.timeMachines.length; j++) { + let node = dashboard.timeMachines[j] + let timeMachine = timeMachinesMap.get(node.id) + if (timeMachine === undefined) { + /* The time machine node is new, thus we need to initialize a new timeMachine */ + initializeTimeMachine(node, syncWithDesignerLoop) + } else { + /* The time machine already exists, we tag it as existing at the current loop. */ + timeMachine.syncWithDesignerLoop = syncWithDesignerLoop + } + } + } + } + } + + /* We check all the timeMachines we have to see if we need to remove any of them */ + for (let i = 0; i < thisObject.timeMachines.length; i++) { + let timeMachine = thisObject.timeMachines[i] + if (timeMachine.syncWithDesignerLoop < syncWithDesignerLoop) { + /* Must be removed */ + timeMachine.finalize() + timeMachinesMap.delete(timeMachine.nodeId) + thisObject.timeMachines.splice(i, 1) + /* We remove one at the time */ + return + } + } + + function initializeTimeMachine (node, syncWithDesignerLoop) { + if (node.payload === undefined) { return } // not ready to consider this Time Machine + if (node.payload.uiObject === undefined) { return } + + let timeMachine = newTimeMachine() + timeMachine.syncWithDesignerLoop = syncWithDesignerLoop + timeMachine.payload = node.payload + timeMachine.nodeId = node.id + + timeMachinesMap.set(node.id, timeMachine) + timeMachine.payload.uiObject.setValue('Loading...') + + /* Setting up the new time machine. */ + timeMachine.container.frame.position.x = browserCanvas.width / 2 - TIME_MACHINE_WIDTH / 2 + timeMachine.container.frame.position.y = browserCanvas.height / 2 - TIME_MACHINE_HEIGHT / 2 + timeMachine.initialize(onTimeMachineInitialized) + + function onTimeMachineInitialized () { + thisObject.timeMachines.push(timeMachine) + timeMachine.payload.uiObject.setValue('') + if (canvas.chartingSpace.viewport !== undefined) { + canvas.chartingSpace.viewport.raiseEvents() // These events will impacts on objects just initialized. + } + } + } + } + + function thisObjectPhysics () { + thisObject.container.frame.height = COCKPIT_SPACE_POSITION + + if (thisObject.container.frame.height <= 0 / 100) { + thisObject.visible = false + } else { + thisObject.visible = true + } + + if (canvas.chartingSpace.viewport !== undefined) { + canvas.chartingSpace.viewport.resize() + } + } + + function childrenPhysics () { + for (let i = 0; i < thisObject.timeMachines.length; i++) { + let timeMachine = thisObject.timeMachines[i] + if (timeMachine.payload.node === undefined) { + continue + } + + if (timeMachine.container.frame.isInViewPort()) { + thisObject.inViewport.set(timeMachine.payload.node.id, timeMachine) + } else { + thisObject.inViewport.delete(timeMachine.payload.node.id) + } + timeMachine.physics() + } + } + + function drawBackground () { + drawSpaceBackground() + + for (let i = 0; i < thisObject.timeMachines.length; i++) { + let timeMachine = thisObject.timeMachines[thisObject.timeMachines.length - i - 1] + timeMachine.drawBackground() + } + } + + function draw () { + if (thisObject.visible !== true) { return } + drawBackground() + + for (let i = 0; i < thisObject.timeMachines.length; i++) { + let timeMachine = thisObject.timeMachines[thisObject.timeMachines.length - i - 1] + timeMachine.draw() + } + + drawForeground() + } + + function drawForeground () { + for (let i = 0; i < thisObject.timeMachines.length; i++) { + let timeMachine = thisObject.timeMachines[thisObject.timeMachines.length - i - 1] + timeMachine.drawForeground() + } + } + + function drawSpaceBackground () { + let opacity = '1' + let fromPoint = { + x: 0, + y: 0 + } + + let toPoint = { + x: browserCanvas.width, + y: COCKPIT_SPACE_POSITION + } + + browserCanvasContext.beginPath() + browserCanvasContext.rect(fromPoint.x, fromPoint.y, toPoint.x - fromPoint.x, toPoint.y - fromPoint.y) + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')' + browserCanvasContext.closePath() + browserCanvasContext.fill() + } +} diff --git a/CanvasApp/Spaces/CockpitSpace.js b/CanvasApp/Spaces/CockpitSpace.js new file mode 100644 index 000000000..01fea0e08 --- /dev/null +++ b/CanvasApp/Spaces/CockpitSpace.js @@ -0,0 +1,314 @@ + + function newCockpitSpace () { + const MODULE_NAME = 'CockpitSpace' + + let thisObject = { + container: undefined, + status: 'BOTTOM', + assetBalances: undefined, + fullscreen: undefined, + toTop: toTop, + toBottom: toBottom, + toMiddle: toMiddle, + moveUp: moveUp, + moveDown: moveDown, + draw: draw, + physics: physics, + getContainer: getContainer, + finalize: finalize, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.detectMouseOver = true + thisObject.container.isDraggeable = true + thisObject.container.notDraggingOnX = true + + controlsMap = new Map() + resize() + + let selfMouseClickEventSubscriptionId + let canvasBrowserResizedEventSubscriptionId + + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId) + thisObject.container.eventHandler.stopListening(canvasBrowserResizedEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.assetBalances = undefined + thisObject.fullscreen = undefined + } + + function initialize () { + thisObject.container.frame.position.x = 0 + + let INITIAL_POSITION + if (canvas.designSpace.workspace.enabled === true) { + let localStorage = window.localStorage.getItem(MODULE_NAME) + if (localStorage !== null) { + storage = JSON.parse(localStorage) + thisObject.container.frame.position.y = storage.spacePosition + } else { + INITIAL_POSITION = 55 + thisObject.container.frame.position.y = browserCanvas.height * INITIAL_POSITION / 100 - COCKPIT_SPACE_HEIGHT + } + } else { + INITIAL_POSITION = 100 + thisObject.container.frame.position.y = browserCanvas.height * INITIAL_POSITION / 100 - COCKPIT_SPACE_HEIGHT + } + + canvasBrowserResizedEventSubscriptionId = canvas.eventHandler.listenToEvent('Browser Resized', resize) + selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + + thisObject.assetBalances = newQuotedAssetalances() + thisObject.assetBalances.initialize() + + thisObject.fullscreen = newFullScreen() + thisObject.fullscreen.container.connectToParent(thisObject.container) + thisObject.fullscreen.initialize() + } + + function onMouseClick (event) { + + } + + function resize () { + thisObject.container.frame.width = browserCanvas.width + thisObject.container.frame.height = COCKPIT_SPACE_HEIGHT + + switch (thisObject.status) { + case 'BOTTOM': { + thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT + break + } + case 'TOP': { + thisObject.container.frame.position.y = TOP_SPACE_HEIGHT + break + } + } + } + + function toTop () { + thisObject.container.frame.position.y = COCKPIT_SPACE_HEIGHT + } + + function toBottom () { + thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT + } + + function toMiddle () { + thisObject.container.frame.position.y = browserCanvas.height / 2 - COCKPIT_SPACE_HEIGHT + } + + function moveUp () { + thisObject.container.frame.position.y = thisObject.container.frame.position.y - COCKPIT_SPACE_HEIGHT * 2 + if (thisObject.container.frame.position.y < 0) { + thisObject.container.frame.position.y = 0 + } + } + + function moveDown () { + thisObject.container.frame.position.y = thisObject.container.frame.position.y + COCKPIT_SPACE_HEIGHT * 2 + if (thisObject.container.frame.position.y > browserCanvas.height - COCKPIT_SPACE_HEIGHT) { + thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT + } + } + + function physics () { + thisObjectPhysics() + childrenPhysics() + } + + function childrenPhysics () { + thisObject.fullscreen.physics() + } + function thisObjectPhysics () { + /* Check the limits */ + + thisObject.status = 'MIDDLE' + + if (canvas.designSpace.workspace.enabled === true) { + thisObject.container.isDraggeable = true + } else { + thisObject.container.isDraggeable = false + } + + if (thisObject.container.frame.position.y > browserCanvas.height * 99.5 / 100 - COCKPIT_SPACE_HEIGHT) { + thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT + thisObject.status = 'BOTTOM' + canvas.designSpace.makeInvisible() + canvas.floatingSpace.makeInvisible() + } else { + canvas.designSpace.makeVisible() + canvas.floatingSpace.makeVisible() + } + + if (thisObject.container.frame.position.y < browserCanvas.height * 0.5 / 100 + TOP_SPACE_HEIGHT) { + thisObject.container.frame.position.y = TOP_SPACE_HEIGHT + thisObject.status = 'TOP' + canvas.panelsSpace.visible = false + } else { + canvas.panelsSpace.visible = true + } + + COCKPIT_SPACE_POSITION = thisObject.container.frame.position.y + let storage = { + spacePosition: COCKPIT_SPACE_POSITION + } + window.localStorage.setItem(MODULE_NAME, JSON.stringify(storage)) + if (canvas.chartingSpace.viewport !== undefined) { + canvas.chartingSpace.viewport.resize() + } + } + + function getContainer (point) { + let container + + // container = thisObject.fullscreen.getContainer(point) + // if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + thisObject.container.space = 'Cockpit Space' + return thisObject.container + } else { + return undefined + } + } + + function draw () { + thisObject.container.frame.draw(false, false) + + drawBackground() + + thisObject.assetBalances.draw() + // thisObject.fullscreen.draw() + } + + function drawBackground () { + let opacity = 1 + + let zeroPoint = { + x: 0, + y: 0 + } + + const RED_LINE_HIGHT = 2 + + zeroPoint = thisObject.container.frame.frameThisPoint(zeroPoint) + + browserCanvasContext.beginPath() + browserCanvasContext.rect(zeroPoint.x, zeroPoint.y, thisObject.container.frame.width, thisObject.container.frame.height) + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + opacity + ')' + browserCanvasContext.closePath() + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.rect(zeroPoint.x, zeroPoint.y, thisObject.container.frame.width, RED_LINE_HIGHT) + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')' + browserCanvasContext.closePath() + browserCanvasContext.fill() + + browserCanvasContext.beginPath() + browserCanvasContext.rect(zeroPoint.x, zeroPoint.y - RED_LINE_HIGHT + thisObject.container.frame.height, thisObject.container.frame.width, RED_LINE_HIGHT) + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')' + browserCanvasContext.closePath() + browserCanvasContext.fill() + + if (canvas.designSpace.workspace.enabled === true) { + arrow() + } + } + + function arrow () { + const X_OFFSET = thisObject.container.frame.width / 2 + const Y_OFFSET = thisObject.container.frame.height / 2 + const HEIGHT = 6 + const WIDTH = 18 + const LINE_WIDTH = 3 + const OPACITY = 1 + let DISTANCE_BETWEEN_ARROWS = 0 + let ARROW_1_DIRECTION = 0 + let ARROW_2_DIRECTION = 0 + + switch (thisObject.status) { + + case ('TOP'): { + ARROW_1_DIRECTION = -1 + ARROW_2_DIRECTION = -1 + DISTANCE_BETWEEN_ARROWS = 6 + break + } + case ('MIDDLE'): { + ARROW_1_DIRECTION = 1 + ARROW_2_DIRECTION = -1 + DISTANCE_BETWEEN_ARROWS = 9 + break + } + case ('BOTTOM'): { + ARROW_1_DIRECTION = 1 + ARROW_2_DIRECTION = 1 + DISTANCE_BETWEEN_ARROWS = 6 + break + } + } + + point1 = { + x: X_OFFSET - WIDTH / 2, + y: Y_OFFSET + HEIGHT / 2 * ARROW_1_DIRECTION - DISTANCE_BETWEEN_ARROWS / 2 + } + + point2 = { + x: X_OFFSET, + y: Y_OFFSET - HEIGHT / 2 * ARROW_1_DIRECTION - DISTANCE_BETWEEN_ARROWS / 2 + } + + point3 = { + x: X_OFFSET + WIDTH / 2, + y: Y_OFFSET + HEIGHT / 2 * ARROW_1_DIRECTION - DISTANCE_BETWEEN_ARROWS / 2 + } + + point1 = thisObject.container.frame.frameThisPoint(point1) + point2 = thisObject.container.frame.frameThisPoint(point2) + point3 = thisObject.container.frame.frameThisPoint(point3) + + point4 = { + x: X_OFFSET - WIDTH / 2, + y: Y_OFFSET + HEIGHT / 2 * ARROW_2_DIRECTION + DISTANCE_BETWEEN_ARROWS / 2 + } + + point5 = { + x: X_OFFSET, + y: Y_OFFSET - HEIGHT / 2 * ARROW_2_DIRECTION + DISTANCE_BETWEEN_ARROWS / 2 + } + + point6 = { + x: X_OFFSET + WIDTH / 2, + y: Y_OFFSET + HEIGHT / 2 * ARROW_2_DIRECTION + DISTANCE_BETWEEN_ARROWS / 2 + } + + point4 = thisObject.container.frame.frameThisPoint(point4) + point5 = thisObject.container.frame.frameThisPoint(point5) + point6 = thisObject.container.frame.frameThisPoint(point6) + + browserCanvasContext.setLineDash([0, 0]) + + browserCanvasContext.beginPath() + + browserCanvasContext.moveTo(point1.x, point1.y) + browserCanvasContext.lineTo(point2.x, point2.y) + browserCanvasContext.lineTo(point3.x, point3.y) + + browserCanvasContext.moveTo(point4.x, point4.y) + browserCanvasContext.lineTo(point5.x, point5.y) + browserCanvasContext.lineTo(point6.x, point6.y) + + browserCanvasContext.lineWidth = LINE_WIDTH + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.stroke() + } + } diff --git a/CanvasApp/Spaces/DesignSpace.js b/CanvasApp/Spaces/DesignSpace.js new file mode 100644 index 000000000..d1c1f7faf --- /dev/null +++ b/CanvasApp/Spaces/DesignSpace.js @@ -0,0 +1,324 @@ + +function newDesignSpace () { + const MODULE_NAME = 'Strategy Space' + let thisObject = { + sidePanel: undefined, + container: undefined, + iconCollection: undefined, + iconByUiObjectType: undefined, + workspace: undefined, + physics: physics, + draw: draw, + getContainer: getContainer, + makeVisible: makeVisible, + makeInvisible: makeInvisible, + initialize: initialize + } + + let container = newContainer() + container.initialize() + thisObject.container = container + + container.isDraggeable = false + + thisObject.iconCollection = new Map() + thisObject.iconByUiObjectType = new Map() + + return thisObject + + function initialize () { + loadIconCollection() + buildIconByUiObjectTypeMap() + + thisObject.workspace = newWorkspace() + thisObject.workspace.initialize() + } + + function buildIconByUiObjectTypeMap () { + /* Take types-icons relationships defined at the schema */ + for (let i = 0; i < APP_SCHEMA_ARRAY.length; i++) { + let nodeDefinition = APP_SCHEMA_ARRAY[i] + let iconName = nodeDefinition.icon + if (iconName !== undefined) { + let icon = thisObject.iconCollection.get(iconName) + thisObject.iconByUiObjectType.set(nodeDefinition.type, icon) + } + } + } + + function loadIconCollection () { + const iconsNames = [ + 'analysis', + 'analysis-1', + 'approve', + 'attach', + 'attractive', + 'pyramid', + 'brainstorming', + 'broken-link', + 'chat', + 'chronometer', + 'compass', + 'competition', + 'content', + 'design-tool', + 'grid', + 'headphones', + 'html', + 'image', + 'layout', + 'loading', + 'login', + 'pantone', + 'paper-plane', + 'photo-camera', + 'piggy-bank', + 'pipette', + 'pixel', + 'placeholder', + 'planning', + 'promotion', + 'pyramid', + 'quality', + 'responsive', + 'schedule', + 'search', + 'security', + 'settings', + 'sitemap', + 'startup', + 'support', + 'tap', + 'menu-fix-pinned', + 'menu-fix-unpinned', + 'targeting', + 'task', + 'testing', + 'text', + 'timeline', + 'tools', + 'delete', + 'upload', + 'vector', + 'video-player', + 'menu-mobility-freeze', + 'menu-mobility-unfreeze', + 'menu-tree-minus', + 'menu-tree-plus', + 'menu-backup', + 'menu-share', + 'stop', + 'stage-open', + 'stage-close', + 'stage-trigger', + 'stage-trigger-trigger-on', + 'stage-trigger-trigger-off', + 'stage-trigger-take-position', + 'menu-tensor-fixed-angles', + 'menu-tensor-free-angles', + 'stage-open-position-size', + 'stage-open-take-profit', + 'stage-open-postion-rate', + 'session-live-trading', + 'session-paper-trading', + 'session-backtesting', + 'session-forward-testing', + 'network', + 'network-node', + 'bot-trading', + 'layer-manager', + 'process', + 'bot-indicator', + 'bot-sensor', + 'time-period', + 'data-mine', + 'procedure-loop', + 'calculations-procedure', + 'data-building-procedure', + 'procedure-initialization', + 'process-definition', + 'output-dataset', + 'data-dependency', + 'product-definition', + 'plotter-module', + 'status-dependency', + 'record-property', + 'record-definition', + 'plotter', + 'plotter-panel', + 'dataset-definition', + 'status-report', + 'execution-finished-event', + 'process-dependencies', + 'process-output', + 'social-bot', + 'execution-started-event', + 'shapes', + 'chart-point', + 'style', + 'chart-points', + 'shapes-polygon-vertex', + 'shapes-polygon-border', + 'shapes-polygon-body', + 'chart-point-formula', + 'style-condition', + 'shapes-polygon', + 'data-entry', + 'data-formula', + 'clone', + 'exchange-account-asset', + 'exchange-account-key', + 'user-keys', + 'user-assets', + 'asset', + 'exchange-accounts', + 'user-account', + 'market', + 'exchange-assets', + 'crypto-exchanges', + 'crypto-exchange', + 'exchange-markets', + 'charting-space', + 'time-line-chart', + 'time-frame-scale', + 'time-scale', + 'rate-scale', + 'time-machine', + 'data-product', + 'single-market-data', + 'data-storage', + 'dataset', + 'viewport', + 'bitfinex', + 'binance', + 'kucoin', + 'bittrex', + 'kraken', + 'poloniex', + 'upbit', + 'monero', + 'tether', + 'tron', + 'stellar', + 'qtum', + 'neo', + 'litecoin', + 'ethereum-classic', + 'ethereum', + 'eos', + 'dash', + 'cardano', + 'bitcoin-cash', + 'bitcoin', + 'binance-coin', + 'trend', + 'oscillator', + 'moving-average', + 'histogram', + 'band', + 'volume', + 'candles', + 'bitmex', + 'liquid', + 'hitbtc', + 'us-dollar', + 'distance-to-parent-200', + 'distance-to-parent-150', + 'distance-to-parent-100', + 'distance-to-parent-050', + 'distance-to-parent-025', + 'distance-to-parent-000', + 'angle-to-parent-360', + 'angle-to-parent-180', + 'angle-to-parent-090', + 'angle-to-parent-045', + 'angle-to-parent-000', + 'data-mining', + 'initial-definition', + 'next-phase-event', + 'testing-environment', + 'production-environment', + 'task-manager', + 'session-based-data', + 'session-independent-data', + 'session-reference', + 'dashboard', + 'toggle-auto-scale-manual', + 'toggle-auto-scale-auto-min-max', + 'toggle-auto-scale-auto-max', + 'toggle-auto-scale-auto-min', + 'toggle-panel-off', + 'toggle-auto-time-scale-auto-min', + 'toggle-auto-time-scale-auto-max', + 'toggle-auto-time-scale-auto-min-max', + 'toggle-auto-time-scale-manual', + 'mouse-pointer', + 'mouse-left-click', + 'mouse-right-click', + 'mouse-wheel-up', + 'mouse-wheel-down', + 'mouse-wheel-click', + 'key-alt', + 'key-shift', + 'key-ctrl', + 'hand-drag', + 'key-down', + 'key-left', + 'key-right', + 'key-up' + ] + + for (let i = 0; i < iconsNames.length; i++) { + let name = iconsNames[i] + loadImage(name) + } + + function loadImage (name) { + const PATH = 'Images/Icons/style-01/' + let image = new Image() + image.onload = onImageLoad + image.fileName = name + + function onImageLoad () { + image.canDrawIcon = true + } + image.src = window.canvasApp.urlPrefix + PATH + name + '.png' + thisObject.iconCollection.set(name, image) + } + } + + function physics () { + thisObject.workspace.physics() + } + + function makeVisible () { + visible = true + } + + function makeInvisible () { + visible = false + } + + function getContainer (point) { + let container + + if (thisObject.sidePanel !== undefined) { + container = thisObject.sidePanel.getContainer(point) + if (container !== undefined) { + container.space = 'Design Space' + return container + } + } + + return + } + + function draw () { + if (thisObject.sidePanel !== undefined) { + thisObject.sidePanel.draw() + } + if (thisObject.workspace !== undefined) { + thisObject.workspace.draw() + } + } +} diff --git a/CanvasApp/Spaces/FloatingSpace.js b/CanvasApp/Spaces/FloatingSpace.js new file mode 100644 index 000000000..bd84fd983 --- /dev/null +++ b/CanvasApp/Spaces/FloatingSpace.js @@ -0,0 +1,283 @@ + +function newFloatingSpace () { + const MODULE_NAME = 'Floating Space' + const ERROR_LOG = true + /* + 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 + uiObjectConstructor: undefined, + container: undefined, + oneScreenUp: oneScreenUp, + oneScreenDown: oneScreenDown, + oneScreenLeft: oneScreenLeft, + oneScreenRight: oneScreenRight, + positionAtNode: positionAtNode, + fitIntoVisibleArea: fitIntoVisibleArea, + isThisPointVisible: isThisPointVisible, + isItFar: isItFar, + makeVisible: makeVisible, + makeInvisible: makeInvisible, + draw: draw, + physics: physics, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = true + thisObject.container.isWheelable = true + thisObject.container.detectMouseOver = true + thisObject.container.frame.radius = 0 + + let devicePixelRatio = window.devicePixelRatio + const SPACE_SIZE = 50000 + + thisObject.container.frame.width = SPACE_SIZE + thisObject.container.frame.height = SPACE_SIZE + thisObject.container.frame.position.x = browserCanvas.width / 2 - thisObject.container.frame.width / 2 + thisObject.container.frame.position.y = browserCanvas.height / 2 - thisObject.container.frame.height / 2 + + let visible = false + + const PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT = 25 + let eventSubscriptionId + + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(eventSubscriptionId) + + thisObject.floatingLayer.finalize() + thisObject.uiObjectConstructor.finalize() + thisObject.container.finalize() + + thisObject.floatingLayer = undefined + thisObject.uiObjectConstructor = undefined + thisObject.container = undefined + } + + function initialize (callBackFunction) { + thisObject.floatingLayer = newFloatingLayer() + thisObject.floatingLayer.initialize() + + thisObject.uiObjectConstructor = newUiObjectConstructor() + thisObject.uiObjectConstructor.initialize(thisObject.floatingLayer) + + eventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel) + } + + function isItFar (payload, dontCheckParent) { + /* If for any reason the paylaod is undefined we return false */ + if (payload === undefined) { return false } + + let radarFactor = 2 // How big is the margin + + /* If the chain parent is not far, they we dont consither this far. */ + if (dontCheckParent !== true) { + if (payload.chainParent !== undefined) { + if (isItFar(payload.chainParent.payload, true) === false) { return false } + } + } + + /* Exceptions that are never considered far. */ + if ( + payload.node.type === 'Trading System' || + payload.node.type === 'Network' || + payload.node.type === 'Crypto Ecosystem' || + payload.node.type === 'Charting Space' || + payload.node.type === 'Data Mine' + ) { + return false + } + + /* Another exception are the ones who have reference parents */ + if (payload.referenceParent !== undefined) { return false } + + /* Here we will check the position of a floatingobject to see if it is outside the screen, with a margin of one screen around. */ + let point = thisObject.container.frame.frameThisPoint(payload.position) + + if (point.x > browserCanvas.width + browserCanvas.width * radarFactor) { + return true + } + + if (point.x < 0 - browserCanvas.width * radarFactor) { + return true + } + + let bottom = COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT + let heightDiff = browserCanvas.height - bottom + if (point.y < bottom - heightDiff * radarFactor) { + return true + } + + if (point.y > browserCanvas.height + heightDiff * radarFactor) { + return true + } + return false + } + + function fitIntoVisibleArea (point) { + /* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */ + + let returnPoint = { + x: point.x, + y: point.y + } + + if (point.x > browserCanvas.width) { + returnPoint.x = browserCanvas.width + } + + if (point.x < 0) { + returnPoint.x = 0 + } + + if (point.y < COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT) { + returnPoint.y = COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT + } + + if (point.y > browserCanvas.height) { + returnPoint.y = browserCanvas.height + } + + return returnPoint + } + + function oneScreenUp () { + let displaceVector = { + x: 0, + y: browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100 + } + + thisObject.container.displace(displaceVector) + return displaceVector + } + + function oneScreenDown () { + let displaceVector = { + x: 0, + y: -browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100 + } + + thisObject.container.displace(displaceVector) + return displaceVector + } + + function oneScreenLeft () { + let displaceVector = { + x: browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100, + y: 0 + } + + thisObject.container.displace(displaceVector) + return displaceVector + } + + function oneScreenRight () { + let displaceVector = { + x: -browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100, + y: 0 + } + + thisObject.container.displace(displaceVector) + return displaceVector + } + + function positionAtNode (node) { + let position = thisObject.container.frame.frameThisPoint(node.payload.position) + + let displaceVector = { + x: browserCanvas.width / 2 - position.x, + y: (COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT) + (browserCanvas.height - (COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT)) / 2 - position.y + } + + thisObject.container.displace(displaceVector) + } + + function isThisPointVisible (point) { + if (point.x > browserCanvas.width) { + return false + } + + if (point.x < 0) { + return false + } + + if (point.y < COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT) { + return false + } + + if (point.y > browserCanvas.height) { + return false + } + + return true + } + + function onMouseWheel (event) { + thisObject.floatingLayer.changeTargetRepulsion(event.wheelDelta) + } + + function makeVisible () { + visible = true + } + + function makeInvisible () { + visible = false + } + + function getContainer (point) { + let container + + container = thisObject.floatingLayer.getContainer(point) + if (container !== undefined) { + container.space = 'Floating Space' + return container + } + + if (visible === true) { + thisObject.container.space = 'Floating Space' + return thisObject.container + } + } + + function physics () { + if (visible === true) { + browserZoomPhysics() + thisObject.floatingLayer.physics() + } + } + + function browserZoomPhysics () { + if (devicePixelRatio !== window.devicePixelRatio) { + devicePixelRatio = window.devicePixelRatio + + thisObject.container.frame.position.x = browserCanvas.width / 2 - thisObject.container.frame.width / 2 + thisObject.container.frame.position.y = browserCanvas.height / 2 - thisObject.container.frame.height / 2 + } + } + + function draw () { + if (visible === true) { + drawBackground() + thisObject.floatingLayer.draw() + } + } + + function drawBackground () { + browserCanvasContext.beginPath() + + browserCanvasContext.rect(thisObject.container.frame.position.x, thisObject.container.frame.position.y, thisObject.container.frame.width, thisObject.container.frame.height) + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)' + + browserCanvasContext.closePath() + browserCanvasContext.fill() + } +} diff --git a/CanvasApp/Spaces/PanelsSpace.js b/CanvasApp/Spaces/PanelsSpace.js new file mode 100644 index 000000000..b1bd3ebc6 --- /dev/null +++ b/CanvasApp/Spaces/PanelsSpace.js @@ -0,0 +1,353 @@ + /* + +The Panel Space y the place wehre all panels live, no matter who create them. + +*/ + +function newPanelsSpace () { + let thisObject = { + visible: true, + container: undefined, + unHide: unHide, + hide: hide, + createNewPanel: createNewPanel, + destroyPanel: destroyPanel, + getPanel: getPanel, + physics: physics, + draw: draw, + panels: [], + 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.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, pOwner, pSession) { + let panel + + switch (pType) { + + case 'Layers Panel': + { + panel = newLayersPanel() + panel.fitFunction = canvas.chartingSpace.fitFunction + panel.container.isVisibleFunction = canvas.chartingSpace.isThisPointVisible + panel.type = 'Layers Panel' + break + } + case 'Plotter Panel': + { + if (pParameters.panelNode.code.isLegacy !== true) { + panel = newPlotterPanel() + panel.fitFunction = canvas.chartingSpace.fitFunction + panel.container.isVisibleFunction = canvas.chartingSpace.isThisPointVisible + panel.session = pSession + panel.initialize(pParameters.panelNode) + } else { + panel = getNewPlotterPanel(pParameters.dataMine, pParameters.plotterCodeName, pParameters.moduleCodeName, pParameters.panelNode.code.codeName) + panel.fitFunction = canvas.chartingSpace.fitFunction + panel.container.isVisibleFunction = canvas.chartingSpace.isThisPointVisible + panel.session = pSession + panel.initialize() + } + panel.type = 'Plotter Panel' + break + } + } + + panel.owner = pOwner + thisObject.panels.push(panel) + + panel.handle = Math.floor((Math.random() * 10000000) + 1) + + return panel.handle + } + + function unHide (owner, type) { + for (let i = 0; i < thisObject.panels.length; i++) { + let panel = thisObject.panels[i] + if (panel.owner === owner && panel.type === type) { + panel.isHidden = false + } + } + } + + function hide (owner, type) { + for (let i = 0; i < thisObject.panels.length; i++) { + let panel = thisObject.panels[i] + if (panel.owner === owner && panel.type === type) { + panel.isHidden = true + } + } + } + + function destroyPanel (pPanelHandle) { + for (let i = 0; i < thisObject.panels.length; i++) { + let panel = thisObject.panels[i] + + if (panel.handle === pPanelHandle) { + if (panel.finalize !== undefined) { + panel.finalize() + } + + 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 physics () { + if (thisObject.visible !== true) { return } + + childrenPhysics() + positioningPhysics() + } + + function childrenPhysics () { + if (thisObject.panels !== undefined) { + for (let i = 0; i < thisObject.panels.length; i++) { + let panel = thisObject.panels[i] + let owner = canvas.chartingSpace.inViewport.get(panel.owner) + if (owner !== undefined) { + if (panel.physics !== undefined) { + panel.physics() + } + } + } + } + } + + function positioningPhysics () { + if (thisObject.panels !== undefined) { + for (let i = 0; i < thisObject.panels.length; i++) { + let panel = thisObject.panels[i] + if (panel.isVisible === false || panel.isHidden === true) { continue } + /* setting the speed of the panel */ + if (panel.container.speed === undefined) { + panel.container.speed = { + x: 2, + y: 2 + } + } + + /* Trying to move the panel and see if it is possible */ + let centerPoint = { + x: panel.container.frame.position.x, + y: panel.container.frame.position.y + } + centerPoint.x = centerPoint.x + panel.container.frame.width / 2 + centerPoint.y = centerPoint.y + panel.container.frame.height / 2 + + /* Lets see which quadrant the panel is at */ + let verticalLine = (canvas.chartingSpace.viewport.visibleArea.topRight.x - canvas.chartingSpace.viewport.visibleArea.topLeft.x) / 2 + canvas.chartingSpace.viewport.visibleArea.topLeft.x + let horizontalLine = (canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topRight.y) / 2 + canvas.chartingSpace.viewport.visibleArea.topRight.y + + if (panel.panelTabButton !== undefined) { + if (panel.panelTabButton.status === 'up') { + if (centerPoint.x < verticalLine) { + panel.gravitatesTowards = 'topLeft' + } else { + panel.gravitatesTowards = 'topRight' + } + } else { + if (centerPoint.x < verticalLine) { + panel.gravitatesTowards = 'bottomLeft' + } else { + panel.gravitatesTowards = 'bottomRight' + } + } + } else { + if (centerPoint.x < verticalLine && centerPoint.y < horizontalLine) { + panel.gravitatesTowards = 'topLeft' + } + if (centerPoint.x >= verticalLine && centerPoint.y < horizontalLine) { + panel.gravitatesTowards = 'topRight' + } + if (centerPoint.x < verticalLine && centerPoint.y >= horizontalLine) { + panel.gravitatesTowards = 'bottomLeft' + } + if (centerPoint.x >= verticalLine && centerPoint.y >= horizontalLine) { + panel.gravitatesTowards = 'bottomRight' + } + } + + /* According to the quadrant we push the panels to the sides */ + if (panel.gravitatesTowards === 'topLeft' || panel.gravitatesTowards === 'bottomLeft') { + panel.container.frame.position.x = panel.container.frame.position.x - panel.container.speed.x + isOverlapping(i, panel.container) + if (panel.container.frame.position.x < 0) { + panel.container.frame.position.x = 0 + } + } else { + panel.container.frame.position.x = panel.container.frame.position.x + panel.container.speed.x + isOverlapping(i, panel.container) + if (panel.container.frame.position.x + panel.container.frame.width > browserCanvas.width) { + panel.container.frame.position.x = browserCanvas.width - panel.container.frame.width + } + } + if (panel.container.frame.height <= canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topRight.y) { + if (panel.gravitatesTowards === 'topLeft' || panel.gravitatesTowards === 'topRight') { + panel.container.frame.position.y = panel.container.frame.position.y - panel.container.speed.y + isOverlapping(i, panel.container) + if (panel.container.frame.position.y < canvas.chartingSpace.viewport.visibleArea.topLeft.y) { + panel.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y + } + } else { + panel.container.frame.position.y = panel.container.frame.position.y + panel.container.speed.y + isOverlapping(i, panel.container) + if (panel.container.frame.position.y + panel.container.frame.height > canvas.chartingSpace.viewport.visibleArea.bottomRight.y) { + panel.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - panel.container.frame.height + } + } + } + } + } + + function isOverlapping (currentIndex, currentContainer) { + let corner1 = { + x: currentContainer.frame.position.x, + y: currentContainer.frame.position.y + } + let corner2 = { + x: currentContainer.frame.position.x + currentContainer.frame.width, + y: currentContainer.frame.position.y + } + let corner3 = { + x: currentContainer.frame.position.x + currentContainer.frame.width, + y: currentContainer.frame.position.y + currentContainer.frame.height + } + let corner4 = { + x: currentContainer.frame.position.x, + y: currentContainer.frame.position.y + currentContainer.frame.height + } + + for (let i = 0; i < currentIndex; i++) { + let panel = thisObject.panels[i] + if (panel.isVisible === true && panel.isHidden === false) { + if (isThisPointInsideThisFrame(corner1, panel.container.frame) === true) { + pushOut(currentContainer, panel.container, panel.gravitatesTowards) + } + if (isThisPointInsideThisFrame(corner2, panel.container.frame) === true) { + pushOut(currentContainer, panel.container, panel.gravitatesTowards) + } + if (isThisPointInsideThisFrame(corner3, panel.container.frame) === true) { + pushOut(currentContainer, panel.container, panel.gravitatesTowards) + } + if (isThisPointInsideThisFrame(corner4, panel.container.frame) === true) { + pushOut(currentContainer, panel.container, panel.gravitatesTowards) + } + } + } + + function pushOut (currentContainer, controlContainer, corner) { + switch (corner) { + case 'topLeft': { + currentContainer.frame.position.x = controlContainer.frame.position.x + controlContainer.frame.width + break + } + case 'topRight': { + currentContainer.frame.position.x = controlContainer.frame.position.x - currentContainer.frame.width + break + } + case 'bottomLeft': { + currentContainer.frame.position.x = controlContainer.frame.position.x + controlContainer.frame.width + break + } + case 'bottomRight': { + currentContainer.frame.position.x = controlContainer.frame.position.x - currentContainer.frame.width + break + } + } + } + + function isThisPointInsideThisFrame (point, frame) { + let corner1 = { + x: frame.position.x, + y: frame.position.y + } + let corner2 = { + x: frame.position.x + frame.width, + y: frame.position.y + } + let corner3 = { + x: frame.position.x + frame.width, + y: frame.position.y + frame.height + } + let corner4 = { + x: frame.position.x, + y: frame.position.y + frame.height + } + if (point.x >= corner1.x && point.y >= corner1.y && point.x <= corner3.x && point.y <= corner3.y) { + return true + } else { + return false + } + } + return false + } + } + + function draw () { + if (thisObject.visible !== true) { return } + + thisObject.container.frame.draw(false, false) + + for (let i = 0; i < thisObject.panels.length; i++) { + let panel = thisObject.panels[i] + let owner = canvas.chartingSpace.inViewport.get(panel.owner) + if (owner !== undefined) { + if (panel.isVisible === true && panel.isHidden === false) { + panel.draw() + } + } + } + } + + function getContainer (point) { + if (thisObject.visible !== true) { return } + + 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 (let i = thisObject.panels.length - 1; i >= 0; i--) { + let panel = thisObject.panels[i] + let owner = canvas.chartingSpace.inViewport.get(panel.owner) + if (owner !== undefined) { + if (panel.isVisible === true && panel.isHidden === false) { + container = panel.getContainer(point) + } + } + if (container !== undefined) { + container.space = 'Panels Space' + return container + } + } + thisObject.container.space = 'Panels Space' + return thisObject.container + } +} diff --git a/CanvasApp/Spaces/TopSpace.js b/CanvasApp/Spaces/TopSpace.js new file mode 100644 index 000000000..905a93894 --- /dev/null +++ b/CanvasApp/Spaces/TopSpace.js @@ -0,0 +1,108 @@ + +function newTopSpace () { + let thisObject = { + container: undefined, + draw: draw, + getContainer: getContainer, + initialize: initialize + } + + thisObject.container = newContainer() + thisObject.container.initialize() + + resize() + + thisObject.container.isDraggeable = false + let backgroundImage + let logoImage + return thisObject + + function initialize () { + canvas.eventHandler.listenToEvent('Browser Resized', resize) + + backgroundImage = new Image() + logoImage = new Image() + + loadImage('superalgos-header-background', backgroundImage) + loadImage('superalgos-logo-for-color-background', logoImage) + + function loadImage (name, image) { + const PATH = 'Images/' + + image.onload = onImageLoad + image.fileName = name + + function onImageLoad () { + image.canDraw = true + } + image.src = window.canvasApp.urlPrefix + PATH + name + '.png' + } + } + + function resize () { + thisObject.container.frame.width = browserCanvas.width + thisObject.container.frame.height = TOP_SPACE_HEIGHT + + thisObject.container.frame.position.x = 0 + try { + thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y + } catch (e) { } + } + + function getContainer (point) { + + } + + function draw () { + thisObject.container.frame.draw(false, false) + + drawBackground() + return + } + + function drawBackground () { + /* Solid Color */ + let opacity = 1 + browserCanvasContext.beginPath() + browserCanvasContext.rect(0, 0, thisObject.container.frame.width, thisObject.container.frame.height) + if (areWeRecording === true) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')' + } else { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')' + } + browserCanvasContext.closePath() + browserCanvasContext.fill() + + /* Background Image */ + if (backgroundImage !== undefined) { + if (backgroundImage.canDraw === true) { + let imagePosition = { + x: 0, + y: 0 + } + + browserCanvasContext.drawImage( + backgroundImage, imagePosition.x, + imagePosition.y, + browserCanvas.width, + TOP_SPACE_HEIGHT - 2) + } + } + + /* Logo Image */ + if (logoImage !== undefined) { + if (logoImage.canDraw === true) { + let imagePosition = { + x: 10, + y: 0 + } + + browserCanvasContext.drawImage( + logoImage, imagePosition.x, + imagePosition.y, + 150, + TOP_SPACE_HEIGHT - 2) + } + } + } +} diff --git a/CanvasApp/SplashScreen.js b/CanvasApp/SplashScreen.js new file mode 100644 index 000000000..5fe6da376 --- /dev/null +++ b/CanvasApp/SplashScreen.js @@ -0,0 +1,157 @@ + /* + +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 () { + let thisObject = { + 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() + thisObject.container = container + + resize() + + container.frame.containerName = 'Splash Screen' + + container.frame.position.x = 0 + container.frame.position.y = 0 + + container.isDraggeable = false + + let fadeOutCounter = 0 + let opacity = 1 + + let logo + let canDrawLogo = false + + return thisObject + + function initialize () { + logo = new Image() + + logo.onload = onImageLoad + + function onImageLoad () { + canDrawLogo = true + } + + logo.src = window.canvasApp.urlPrefix + 'Images/superalgos-logo.png' + + canvas.eventHandler.listenToEvent('Browser Resized', resize) + } + + function resize () { + thisObject.container.frame.width = browserCanvas.width + thisObject.container.frame.height = browserCanvas.height + } + + function draw () { + if (splashScreenNeeded === false) { + stepsInitializationCounter = 100 + canDrawLogo = false + + fadeOutCounter++ + + if (fadeOutCounter > 2) { + return + } + opacity = opacity - 1 + } + + thisObject.container.frame.draw(false, false) + + /* Set the background. */ + + browserCanvasContext.beginPath() + browserCanvasContext.rect(0, 0, thisObject.container.frame.width, thisObject.container.frame.height) + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')' + browserCanvasContext.closePath() + browserCanvasContext.fill() + + if (canDrawLogo === false) { return } + + /* First the AA Logo. */ + + let imageHeight = 192 / 2 + let imageWidth = 720 / 2 + let yDisplacement = -60 + let xDisplacement = -60 + + let imagePoint = { + x: thisObject.container.frame.width * 1 / 2 - imageWidth / 2 + xDisplacement, + y: thisObject.container.frame.height * 1 / 2 - imageHeight / 2 + yDisplacement + } + + imagePoint = thisObject.container.frame.frameThisPoint(imagePoint) + + browserCanvasContext.drawImage(logo, imagePoint.x, imagePoint.y, imageWidth, imageHeight) + + /* Second the % of Advance. */ + + let label = '' + Math.trunc(stepsInitializationCounter) + ' %' + stepsInitializationCounter = stepsInitializationCounter + 5.0 + + if (stepsInitializationCounter > 99) { + splashScreenNeeded = false + stepsInitializationCounter = 99 + } + + let fontSize = 15 + + let labelPoint = { + x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO - 22, + y: thisObject.container.frame.height / 2 + fontSize / 2 + fontSize * 0.1 + 90 + yDisplacement + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(0, 0, 0, ' + opacity + ')' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + + let title + let titlePoint + + /* Algo Bots Competitions Sub Title */ + + fontSize = 35 + + title = 'Beta' + + titlePoint = { + x: thisObject.container.frame.width / 2 - title.length / 2 * fontSize * FONT_ASPECT_RATIO - 20, + y: thisObject.container.frame.height / 2 + 70 + yDisplacement + } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.font = 'Saira Condensed' + 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 (thisObject.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 thisObject.container + } else { + /* This point does not belong to this space. */ + + return undefined + } + } +} diff --git a/CanvasApp/SystemEventHandler.js b/CanvasApp/SystemEventHandler.js new file mode 100644 index 000000000..07618cbf8 --- /dev/null +++ b/CanvasApp/SystemEventHandler.js @@ -0,0 +1,156 @@ + +function newSystemEventHandler () { + /* Web Sockets Connection */ + + const MODULE_NAME = 'System Event Handler' + const ERROR_LOG = true + const INFO_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + const WEB_SOCKETS_URL = 'ws://localhost:8080' + let WEB_SOCKETS_CONNECTION + + let thisObject = { + initialize: initialize, + physics: physics, + createEventHandler: createEventHandler, + deleteEventHandler: deleteEventHandler, + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent + } + + let eventListeners = new Map() + let responseWaiters = new Map() + + return thisObject + + function initialize (callBackFunction) { + setuptWebSockets(callBackFunction) + } + + function sendCommand (command, responseCallBack, eventsCallBack) { + if (command.action === 'listenToEvent') { + let key + if (command.callerId !== undefined) { + key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId + } else { + key = command.eventHandlerName + '-' + command.eventType + } + eventListeners.set(key, eventsCallBack) + } + if (command.callerId && responseCallBack) { + responseWaiters.set(command.callerId, responseCallBack) + } + + WEB_SOCKETS_CONNECTION.send(JSON.stringify(command)) + } + + function createEventHandler (eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'createEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function deleteEventHandler (eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'deleteEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function listenToEvent (eventHandlerName, eventType, extraData, callerId, responseCallBack, eventsCallBack) { + let eventCommand = { + action: 'listenToEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + extraData: extraData, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack, eventsCallBack) + } + + function stopListening (eventHandlerName, eventSubscriptionId, callerId, responseCallBack) { + let eventCommand = { + action: 'stopListening', + eventHandlerName: eventHandlerName, + eventSubscriptionId: eventSubscriptionId, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function raiseEvent (eventHandlerName, eventType, event, callerId, responseCallBack) { + let eventCommand = { + action: 'raiseEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + event: event, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function physics () { + if (WEB_SOCKETS_CONNECTION !== undefined) { + if (WEB_SOCKETS_CONNECTION.readyState === 3) { // Connection closed. May happen after computer goes to sleep. + setuptWebSockets(onOpen) + function onOpen () { + if (INFO_LOG === true) { logger.write('[INFO] setuptWebSockets -> Found Web Sockets Connection Closed. Reconnected to WebSockets Server.') } + } + } + } + } + + function setuptWebSockets (callBackFunction) { + try { + WEB_SOCKETS_CONNECTION = new WebSocket(WEB_SOCKETS_URL) + WEB_SOCKETS_CONNECTION.onopen = () => { + console.log('Websocket connection opened.') + } + WEB_SOCKETS_CONNECTION.onerror = error => { + console.log('WebSocket error:' + JSON.stringify(error)) + } + WEB_SOCKETS_CONNECTION.onopen = () => { + if (callBackFunction !== undefined) { + callBackFunction() + } + } + WEB_SOCKETS_CONNECTION.onmessage = e => { + // console.log('Websoked Message Received: ' + e.data) + + let message = JSON.parse(e.data) + + if (message.action === 'Event Raised') { + let key + if (message.callerId !== undefined) { + key = message.eventHandlerName + '-' + message.eventType + '-' + message.callerId + } else { + key = message.eventHandlerName + '-' + message.eventType + } + let handler = eventListeners.get(key) + if (handler) { + handler(message) + } + return + } + + if (message.action === 'Event Server Response') { + let handler = responseWaiters.get(message.callerId) + if (handler) { + handler(message) + } + return + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] setuptWebSockets -> err = ' + err.stack) } + } + } +} diff --git a/CanvasApp/Utilities.js b/CanvasApp/Utilities.js new file mode 100644 index 000000000..2588156b9 --- /dev/null +++ b/CanvasApp/Utilities.js @@ -0,0 +1,247 @@ +function drawContainerBackground (container, color, opacity, fitFunction) { + let fromPoint = { + x: 0, + y: 0 + } + + let toPoint = { + x: container.frame.width, + y: container.frame.height + } + + fromPoint = transformThisPoint(fromPoint, container) + toPoint = transformThisPoint(toPoint, container) + + fromPoint = fitFunction(fromPoint) + toPoint = fitFunction(toPoint) + + browserCanvasContext.beginPath() + browserCanvasContext.rect(fromPoint.x, fromPoint.y, toPoint.x - fromPoint.x, toPoint.y - fromPoint.y) + browserCanvasContext.fillStyle = 'rgba(' + color + ', ' + opacity + ')' + browserCanvasContext.closePath() + browserCanvasContext.fill() +} + +function savePropertyAtNodeConfig (payload, propertyName, value) { + try { + let code = JSON.parse(payload.node.code) + code[propertyName] = value + payload.node.code = JSON.stringify(code, null, 4) + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } +} + +function loadPropertyFromNodeConfig (payload, propertyName) { + try { + let code = JSON.parse(payload.node.code) + return code[propertyName] + } catch (err) { + // we ignore errors here since most likely they will be parsing errors. + } +} + +function saveFrame (payload, frame) { + payload.frame = { + position: { + x: frame.position.x, + y: frame.position.y + }, + width: frame.width, + height: frame.height, + radius: frame.radius + } +} + +function loadFrame (payload, frame) { + if (payload.node.savedPayload !== undefined) { + if (payload.node.savedPayload.frame !== undefined) { + frame.position.x = payload.node.savedPayload.frame.position.x + frame.position.y = payload.node.savedPayload.frame.position.y + frame.width = payload.node.savedPayload.frame.width + frame.height = payload.node.savedPayload.frame.height + frame.radius = payload.node.savedPayload.frame.radius + payload.node.savedPayload.frame = undefined + } + } +} + +function convertTimeFrameToName (pTimeFrame) { + for (let i = 0; i < dailyFilePeriods.length; i++) { + let period = dailyFilePeriods[i] + if (period[0] === pTimeFrame) { + return period[1] + } + } + + for (let i = 0; i < marketFilesPeriods.length; i++) { + let period = marketFilesPeriods[i] + if (period[0] === pTimeFrame) { + return period[1] + } + } +} + +function downloadText (filename, text) { + let element = document.createElement('a') + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)) + element.setAttribute('download', filename) + element.style.display = 'none' + document.body.appendChild(element) + element.click() + document.body.removeChild(element) +} + +function downloadCanvas (filename) { + let data = browserCanvas.toDataURL('image/png', 1) + /* Change MIME type to trick the browser to downlaod the file instead of displaying it */ + data = data.replace(/^data:image\/[^;]*/, 'data:application/octet-stream') + + /* In addition to 's "download" attribute, you can define HTTP-style headers */ + data = data.replace(/^data:application\/octet-stream/, 'data:application/octet-stream;headers=Content-Disposition%3A%20attachment%3B%20filename=' + filename + '.png') + + let element = document.createElement('a') + element.setAttribute('href', data) + element.setAttribute('download', filename + '.PNG') + element.style.display = 'none' + document.body.appendChild(element) + element.click() + document.body.removeChild(element) +}; + +function transformThisPoint (point, container) { + /* We make the point relative to the current frame */ + + point = container.frame.frameThisPoint(point) + + /* We pass this point through the canvas.chartingSpace.viewport lends, meaning we apply the canvas.chartingSpace.viewport zoom and displacement. */ + + point = canvas.chartingSpace.viewport.transformThisPoint(point) + + return point +} + +function unTransformThisPoint (point, container) { + point = canvas.chartingSpace.viewport.unTransformThisPoint(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 getDateFromPointAtBrowserCanvas (point, container, coordinateSystem) { + point = unTransformThisPoint(point, container) + point = coordinateSystem.unInverseTransform(point, container.frame.height) + + let date = new Date(point.x) + + return date +} + +function getDateFromPointAtChartsSpace (point, container, coordinateSystem) { + point = container.frame.unframeThisPoint(point) + point = coordinateSystem.unInverseTransform(point, container.frame.height) + + let date = new Date(point.x) + + return date +} + +function getDateFromPointAtContainer (point, container, coordinateSystem) { + point = coordinateSystem.unInverseTransform(point, container.frame.height) + + let date = new Date(point.x) + + return date +} + +function getRateFromPointAtBrowserCanvas (point, container, coordinateSystem) { + point = unTransformThisPoint(point, container) + point = coordinateSystem.unInverseTransform(point, container.frame.height) + + return point.y +} + +function getRateFromPointAtChartingSpace (point, container, coordinateSystem) { + point = container.frame.unframeThisPoint(point) + point = coordinateSystem.unInverseTransform(point, container.frame.height) + + return point.y +} + +function getRateFromPointAtContainer (point, container, coordinateSystem) { + point = coordinateSystem.unInverseTransform(point, container.frame.height) + + return point.y +} + +function getMilisecondsFromPoint (point, container, coordinateSystem) { + point = unTransformThisPoint(point, container) + point = coordinateSystem.unInverseTransform(point, container.frame.height) + + return point.x +} + +function moveToUserPosition (container, currentDate, currentRate, coordinateSystem, ignoreX, ignoreY, mousePosition) { + let targetPoint = { + x: currentDate.valueOf(), + y: currentRate + } + + /* Put this point in the coordinate system of the canvas.chartingSpace.viewport */ + targetPoint = coordinateSystem.transformThisPoint(targetPoint) + targetPoint = transformThisPoint(targetPoint, container) + + let displaceVector + + let targetNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(targetPoint) + let mouseNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(mousePosition) + + displaceVector = { + x: mouseNoZoom.x - targetNoZoom.x, + y: mouseNoZoom.y - targetNoZoom.y + } + + if (ignoreX) { displaceVector.x = 0 } + if (ignoreY) { displaceVector.y = 0 } + + container.displace(displaceVector) +} + +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 +} + +function printLabel (labelToPrint, x, y, opacity, fontSize, color) { + let labelPoint + if (color === undefined) { color = UI_COLOR.DARK } + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + let label = '' + labelToPrint + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + browserCanvasContext.fillStyle = 'rgba(' + color + ', ' + opacity + ')' + browserCanvasContext.fillText(label, x, y) +} + +function newUniqueId () { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8) + return v.toString(16) + }) +} diff --git a/CanvasApp/Utilities/RoundedCornersBackground.js b/CanvasApp/Utilities/RoundedCornersBackground.js new file mode 100644 index 000000000..7ce98884c --- /dev/null +++ b/CanvasApp/Utilities/RoundedCornersBackground.js @@ -0,0 +1,142 @@ +function roundedCornersBackground (params) { + if (params.xOffset === undefined) { params.xOffset = 0 } + borderPoint1 = { + x: params.cornerRadius + params.xOffset, + y: params.cornerRadius + } + + borderPoint2 = { + x: params.container.frame.width - params.cornerRadius, + y: params.cornerRadius + } + + borderPoint3 = { + x: params.container.frame.width - params.cornerRadius, + y: params.container.frame.height - params.cornerRadius + } + + borderPoint4 = { + x: params.cornerRadius + params.xOffset, + y: params.container.frame.height - params.cornerRadius + } + + /* Now the transformations. */ + + if (params.coordinateSystem !== undefined) { // For instance Candles would use this + borderPoint1 = params.coordinateSystem.transformThisPoint(borderPoint1) + borderPoint2 = params.coordinateSystem.transformThisPoint(borderPoint2) + borderPoint3 = params.coordinateSystem.transformThisPoint(borderPoint3) + borderPoint4 = params.coordinateSystem.transformThisPoint(borderPoint4) + } else { + if (params.outsideViewPort === true) { // If the element is not affected by zoom + borderPoint1 = transformThisPoint(borderPoint1, params.container) + borderPoint2 = transformThisPoint(borderPoint2, params.container) + borderPoint3 = transformThisPoint(borderPoint3, params.container) + borderPoint4 = transformThisPoint(borderPoint4, params.container) + } else { // For instance Panels use this + borderPoint1 = params.container.frame.frameThisPoint(borderPoint1) + borderPoint2 = params.container.frame.frameThisPoint(borderPoint2) + borderPoint3 = params.container.frame.frameThisPoint(borderPoint3) + borderPoint4 = params.container.frame.frameThisPoint(borderPoint4) + } + } + + if (params.fitFunction !== undefined) { + borderPoint1 = params.fitFunction(borderPoint1) + borderPoint2 = params.fitFunction(borderPoint2) + borderPoint3 = params.fitFunction(borderPoint3) + borderPoint4 = params.fitFunction(borderPoint4) + } + + browserCanvasContext.setLineDash([0, 0]) + + /* Shadow */ + + if (params.castShadow === true) { + let SHADOW_OFFSET = 2 + + browserCanvasContext.beginPath() + + browserCanvasContext.arc(borderPoint1.x + SHADOW_OFFSET, borderPoint1.y + SHADOW_OFFSET, params.cornerRadius, 1.0 * Math.PI, 1.5 * Math.PI) + browserCanvasContext.lineTo(borderPoint2.x + SHADOW_OFFSET, borderPoint2.y - params.cornerRadius + SHADOW_OFFSET) + browserCanvasContext.arc(borderPoint2.x + SHADOW_OFFSET, borderPoint2.y + SHADOW_OFFSET, params.cornerRadius, 1.5 * Math.PI, 2.0 * Math.PI) + browserCanvasContext.lineTo(borderPoint3.x + params.cornerRadius + SHADOW_OFFSET, borderPoint3.y + SHADOW_OFFSET) + browserCanvasContext.arc(borderPoint3.x + SHADOW_OFFSET, borderPoint3.y + SHADOW_OFFSET, params.cornerRadius, 0 * Math.PI, 0.5 * Math.PI) + browserCanvasContext.lineTo(borderPoint4.x + SHADOW_OFFSET, borderPoint4.y + params.cornerRadius + SHADOW_OFFSET) + browserCanvasContext.arc(borderPoint4.x + SHADOW_OFFSET, borderPoint4.y + SHADOW_OFFSET, params.cornerRadius, 0.5 * Math.PI, 1.0 * Math.PI) + browserCanvasContext.lineTo(borderPoint1.x - params.cornerRadius + SHADOW_OFFSET, borderPoint1.y + SHADOW_OFFSET) + + browserCanvasContext.closePath() + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + 0.01 + ')' + browserCanvasContext.stroke() + + browserCanvasContext.lineWidth = 5 + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + 0.01 + ')' + browserCanvasContext.stroke() + + browserCanvasContext.lineWidth = 1 + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + 0.15 + ')' + browserCanvasContext.stroke() + } + + /* Background */ + + browserCanvasContext.beginPath() + + browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, params.cornerRadius, 1.0 * Math.PI, 1.5 * Math.PI) + browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - params.cornerRadius) + browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, params.cornerRadius, 1.5 * Math.PI, 2.0 * Math.PI) + browserCanvasContext.lineTo(borderPoint3.x + params.cornerRadius, borderPoint3.y) + browserCanvasContext.arc(borderPoint3.x, borderPoint3.y, params.cornerRadius, 0 * Math.PI, 0.5 * Math.PI) + browserCanvasContext.lineTo(borderPoint4.x, borderPoint4.y + params.cornerRadius) + browserCanvasContext.arc(borderPoint4.x, borderPoint4.y, params.cornerRadius, 0.5 * Math.PI, 1.0 * Math.PI) + browserCanvasContext.lineTo(borderPoint1.x - params.cornerRadius, borderPoint1.y) + + browserCanvasContext.closePath() + + browserCanvasContext.fillStyle = 'rgba(' + params.backgroundColor + ', ' + params.opacity + ')' + + browserCanvasContext.fill() + + browserCanvasContext.lineWidth = params.lineWidth + browserCanvasContext.strokeStyle = 'rgba(' + params.borderColor + ', ' + params.opacity + ')' + browserCanvasContext.stroke() +} + +function drawLabel (label, xFactor, yFactor, xOffset, yOffset, fontSize, container) { + if (label === undefined) { return } + let fontOffset = label.length * fontSize * FONT_ASPECT_RATIO + 10 + + let labelPoint = { + x: container.frame.width * xFactor + xOffset - fontOffset / 2, + y: container.frame.height * yFactor + yOffset + } + + labelPoint = container.frame.frameThisPoint(labelPoint) + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' + + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) +} + +function drawIcon (icon, xFactor, yFactor, xOffset, yOffset, imageSize, container) { + if (icon !== undefined) { + if (icon.canDrawIcon === true) { + let imagePosition = { + x: container.frame.width * xFactor + xOffset - imageSize / 2, + y: container.frame.height * yFactor + yOffset - imageSize / 2 + } + + imagePosition = container.frame.frameThisPoint(imagePosition) + + browserCanvasContext.drawImage( + icon, imagePosition.x, + imagePosition.y, + imageSize, + imageSize) + } + } +} diff --git a/CanvasApp/VideoRecorder.js b/CanvasApp/VideoRecorder.js new file mode 100644 index 000000000..e84336c95 --- /dev/null +++ b/CanvasApp/VideoRecorder.js @@ -0,0 +1,173 @@ +function newVideoRecorder () { + let thisObject = { + recordCanvas: recordCanvas + } + let lastAction + return thisObject + + function recordCanvas () { + { + const DISTANCE_BETWEEN_ICONS = 35 + const ICON_SIZES = 50 + + let mousePointerIcon = canvas.designSpace.iconCollection.get('mouse-pointer') + let draggingIcon = canvas.designSpace.iconCollection.get('hand-drag') + + let leftClickIcon = canvas.designSpace.iconCollection.get('mouse-left-click') + let rightClickIcon = canvas.designSpace.iconCollection.get('mouse-right-click') + let wheelClickIcon = canvas.designSpace.iconCollection.get('mouse-wheel-click') + + let imagePosition + let buttonPressedIcon + + if (canvas.mouse.action === 'key down' && lastAction === 'dragging') { + canvas.mouse.action = 'dragging' + } + switch (canvas.mouse.action) { + case 'moving': { + pointerIcon = mousePointerIcon + break + } + case 'dragging': { + pointerIcon = draggingIcon + break + } + case 'wheel': { + if (canvas.mouse.event.delta > 0) { + pointerIcon = canvas.designSpace.iconCollection.get('mouse-wheel-up') + } else { + pointerIcon = canvas.designSpace.iconCollection.get('mouse-wheel-down') + } + + break + } + case 'key down': { + pointerIcon = mousePointerIcon + break + } + case 'key up': { + pointerIcon = mousePointerIcon + break + } + } + + lastAction = canvas.mouse.action + + imagePosition = { + x: canvas.mouse.position.x, + y: canvas.mouse.position.y + } + drawMousePointer(pointerIcon, imagePosition, ICON_SIZES) + + switch (canvas.mouse.event.buttons) { + case 0: { + buttonPressedIcon = undefined + break + } + case 1: { + buttonPressedIcon = leftClickIcon + break + } + case 2: { + buttonPressedIcon = rightClickIcon + break + } + case 3: { + buttonPressedIcon = undefined + break + } + case 4: { + buttonPressedIcon = wheelClickIcon + break + } + } + + let firstRow = DISTANCE_BETWEEN_ICONS * 1 + ICON_SIZES * 1 + let secondRow = DISTANCE_BETWEEN_ICONS * 1 + ICON_SIZES * 2 + let icon + + imagePosition = { + x: canvas.mouse.position.x + DISTANCE_BETWEEN_ICONS * 2, + y: canvas.mouse.position.y + } + drawMousePointer(buttonPressedIcon, imagePosition, ICON_SIZES) + + if (canvas.mouse.event.shiftKey === true) { + icon = canvas.designSpace.iconCollection.get('key-shift') + imagePosition = { + x: canvas.mouse.position.x - DISTANCE_BETWEEN_ICONS * 3, + y: canvas.mouse.position.y + firstRow + } + drawMousePointer(icon, imagePosition, ICON_SIZES) + } + + if (canvas.mouse.event.ctrlKey === true || canvas.mouse.event.metaKey === true) { + icon = canvas.designSpace.iconCollection.get('key-ctrl') + imagePosition = { + x: canvas.mouse.position.x - DISTANCE_BETWEEN_ICONS * 3, + y: canvas.mouse.position.y + secondRow + } + drawMousePointer(icon, imagePosition, ICON_SIZES) + } + + if (canvas.mouse.event.altKey === true) { + icon = canvas.designSpace.iconCollection.get('key-alt') + imagePosition = { + x: canvas.mouse.position.x + DISTANCE_BETWEEN_ICONS * 3 + ICON_SIZES * 0, + y: canvas.mouse.position.y + secondRow + } + drawMousePointer(icon, imagePosition, ICON_SIZES) + } + + if (canvas.mouse.event.code === 'ArrowUp') { + icon = canvas.designSpace.iconCollection.get('key-up') + imagePosition = { + x: canvas.mouse.position.x - 0, + y: canvas.mouse.position.y + firstRow + } + drawMousePointer(icon, imagePosition, ICON_SIZES) + } + + if (canvas.mouse.event.code === 'ArrowDown') { + icon = canvas.designSpace.iconCollection.get('key-down') + imagePosition = { + x: canvas.mouse.position.x - 0, + y: canvas.mouse.position.y + secondRow + } + drawMousePointer(icon, imagePosition, ICON_SIZES) + } + + if (canvas.mouse.event.code === 'ArrowLeft') { + icon = canvas.designSpace.iconCollection.get('key-left') + imagePosition = { + x: canvas.mouse.position.x - DISTANCE_BETWEEN_ICONS * 0 - ICON_SIZES * 1, + y: canvas.mouse.position.y + secondRow + } + drawMousePointer(icon, imagePosition, ICON_SIZES) + } + + if (canvas.mouse.event.code === 'ArrowRight') { + icon = canvas.designSpace.iconCollection.get('key-right') + imagePosition = { + x: canvas.mouse.position.x + DISTANCE_BETWEEN_ICONS * 0 + ICON_SIZES * 1, + y: canvas.mouse.position.y + secondRow + } + drawMousePointer(icon, imagePosition, ICON_SIZES) + } + + function drawMousePointer (icon, imagePosition, imageSize) { + if (icon !== undefined) { + if (icon.canDrawIcon === true) { + browserCanvasContext.drawImage( + icon, imagePosition.x, + imagePosition.y, + imageSize, + imageSize) + } + } + } + mediaRecorder.capture(browserCanvas) + } + } +} + diff --git a/CanvasApp/WebDebugLog.js b/CanvasApp/WebDebugLog.js new file mode 100644 index 000000000..6a758304e --- /dev/null +++ b/CanvasApp/WebDebugLog.js @@ -0,0 +1,21 @@ + function newWebDebugLog () { + try { + let thisObject = { + write: write, + fileName: 'undefined' + } + + return thisObject + + function write (pText) { + console.log(spacePad(thisObject.fileName, 50) + ' : ' + pText) + + function spacePad (str, max) { + str = str.toString() + return str.length < max ? spacePad(' ' + str, max) : str + } + } + } catch (err) { + console.log(spacePad(MODULE_NAME, 50) + ' : ' + '[ERROR] WebDebugLog --> err = ' + err.stack) + } + } diff --git a/EventsServer/node_modules/easy-stack/es5.js b/EventsServer/node_modules/easy-stack/es5.js new file mode 100644 index 000000000..a756a7bbf --- /dev/null +++ b/EventsServer/node_modules/easy-stack/es5.js @@ -0,0 +1,74 @@ +function Stack(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToStack + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearStack + }, + contents:{ + enumerable:false, + get:getStack, + set:setStack + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var stack=[]; + var running=false; + var stop=false; + + function clearStack(){ + stack=[]; + return stack; + } + + function getStack(){ + return stack; + } + + function setStack(val){ + stack=val; + return stack; + } + + function addToStack(){ + for(var i in arguments){ + stack.unshift(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(stack.length<1 || this.stop){ + running=false; + return; + } + + stack.shift().bind(this)(); + } +} diff --git a/EventsServer/node_modules/easy-stack/example/basic.js b/EventsServer/node_modules/easy-stack/example/basic.js new file mode 100644 index 000000000..f0d7fbb11 --- /dev/null +++ b/EventsServer/node_modules/easy-stack/example/basic.js @@ -0,0 +1,18 @@ +var Stack=require('../stack'); +//create a new Stack instance +var stack=new Stack; +stack.autoRun=false; + +for(var i=0; i<50; i++){ + //add a bunch of stuff to the stack + stack.add(makeRequest.bind(stack,i)); +} + +stack.next(); + +function makeRequest(index){ + //do stuff + console.log(`making LIFO request ${index}`); + + this.next(); +} diff --git a/EventsServer/node_modules/easy-stack/package.json b/EventsServer/node_modules/easy-stack/package.json new file mode 100644 index 000000000..8ac84376a --- /dev/null +++ b/EventsServer/node_modules/easy-stack/package.json @@ -0,0 +1,59 @@ +{ + "_from": "easy-stack@^1.0.0", + "_id": "easy-stack@1.0.0", + "_inBundle": false, + "_integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "_location": "/easy-stack", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "easy-stack@^1.0.0", + "name": "easy-stack", + "escapedName": "easy-stack", + "rawSpec": "^1.0.0", + "saveSpec": null, + "fetchSpec": "^1.0.0" + }, + "_requiredBy": [ + "/js-queue" + ], + "_resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "_shasum": "12c91b3085a37f0baa336e9486eac4bf94e3e788", + "_spec": "easy-stack@^1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\js-queue", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/easy-stack/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Simple JS stack with auto run for node and browsers", + "engines": { + "node": ">=6.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/easy-stack#readme", + "keywords": [ + "stack", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "stack.js", + "name": "easy-stack", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/easy-stack.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.0" +} diff --git a/EventsServer/node_modules/easy-stack/stack.js b/EventsServer/node_modules/easy-stack/stack.js new file mode 100644 index 000000000..bfadd1dee --- /dev/null +++ b/EventsServer/node_modules/easy-stack/stack.js @@ -0,0 +1,39 @@ +class Stack{ + constructor(){ + this.stack=[]; + this.autoRun=true; + this.running=false; + this.stop=false; + } + + clear(){ + this.stack=[]; + return this.stack; + } + + contents(val){ + if(val){ + this.stack=val; + } + return this.stack; + } + + add(...callbacks){ + this.stack.push(...callbacks); + if(!this.running && !this.stop && this.autoRun){ + this.next(); + } + } + + next(){ + this.running=true; + if(this.stack.length<1 || this.stop){ + this.running=false; + return; + } + + this.stack.pop().bind(this)(); + } +} + +module.exports=Stack; diff --git a/EventsServer/node_modules/event-pubsub/bower.json b/EventsServer/node_modules/event-pubsub/bower.json new file mode 100644 index 000000000..a207b0a5d --- /dev/null +++ b/EventsServer/node_modules/event-pubsub/bower.json @@ -0,0 +1,28 @@ +{ + "name": "event-pubsub", + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "main": "event-pubsub.js", + "authors": [ + "Brandon Nozaki Miller" + ], + "license": "DBAD", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "moduleType": [ + "globals", + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/EventsServer/node_modules/event-pubsub/es5.js b/EventsServer/node_modules/event-pubsub/es5.js new file mode 100644 index 000000000..7faf8b737 --- /dev/null +++ b/EventsServer/node_modules/event-pubsub/es5.js @@ -0,0 +1,143 @@ +'use strict'; + +function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} + +module.exports=EventPubSub; diff --git a/EventsServer/node_modules/event-pubsub/es6.js b/EventsServer/node_modules/event-pubsub/es6.js new file mode 100644 index 000000000..e9e859f9b --- /dev/null +++ b/EventsServer/node_modules/event-pubsub/es6.js @@ -0,0 +1,99 @@ +'use strict'; + +class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +module.exports = EventPubSub; diff --git a/EventsServer/node_modules/event-pubsub/event-pubsub-browser-es5.js b/EventsServer/node_modules/event-pubsub/event-pubsub-browser-es5.js new file mode 100644 index 000000000..03f27625e --- /dev/null +++ b/EventsServer/node_modules/event-pubsub/event-pubsub-browser-es5.js @@ -0,0 +1,141 @@ +'use strict'; + +window.EventPubSub=function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/EventsServer/node_modules/event-pubsub/event-pubsub-browser.js b/EventsServer/node_modules/event-pubsub/event-pubsub-browser.js new file mode 100644 index 000000000..d0bcd7b56 --- /dev/null +++ b/EventsServer/node_modules/event-pubsub/event-pubsub-browser.js @@ -0,0 +1,130 @@ +'use strict'; + +window.EventPubSub=class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/EventsServer/node_modules/event-pubsub/event-pubsub.js b/EventsServer/node_modules/event-pubsub/event-pubsub.js new file mode 100644 index 000000000..65bd02419 --- /dev/null +++ b/EventsServer/node_modules/event-pubsub/event-pubsub.js @@ -0,0 +1,8 @@ +'use strict'; + +let EventPubSub = require('./es5'); +if(process.version[1]>5){ + EventPubSub = require('./es6'); +} + +module.exports=EventPubSub; diff --git a/EventsServer/node_modules/event-pubsub/package.json b/EventsServer/node_modules/event-pubsub/package.json new file mode 100644 index 000000000..d1daf8055 --- /dev/null +++ b/EventsServer/node_modules/event-pubsub/package.json @@ -0,0 +1,59 @@ +{ + "_from": "event-pubsub@4.3.0", + "_id": "event-pubsub@4.3.0", + "_inBundle": false, + "_integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "_location": "/event-pubsub", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "event-pubsub@4.3.0", + "name": "event-pubsub", + "escapedName": "event-pubsub", + "rawSpec": "4.3.0", + "saveSpec": null, + "fetchSpec": "4.3.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "_shasum": "f68d816bc29f1ec02c539dc58c8dd40ce72cb36e", + "_spec": "event-pubsub@4.3.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/event-pubsub/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "directories": { + "example": "examples" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "license": "Unlicense", + "main": "event-pubsub.js", + "name": "event-pubsub", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/event-pubsub.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "4.3.0" +} diff --git a/EventsServer/node_modules/js-message/Message.js b/EventsServer/node_modules/js-message/Message.js new file mode 100644 index 000000000..9a4698232 --- /dev/null +++ b/EventsServer/node_modules/js-message/Message.js @@ -0,0 +1,71 @@ +function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } +} + +module.exports=Message; diff --git a/EventsServer/node_modules/js-message/js-message-vanilla.js b/EventsServer/node_modules/js-message/js-message-vanilla.js new file mode 100644 index 000000000..0c49c2b6c --- /dev/null +++ b/EventsServer/node_modules/js-message/js-message-vanilla.js @@ -0,0 +1,75 @@ +( + function(){ + function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } + } + + window.message=Message; + } +)(); diff --git a/EventsServer/node_modules/js-message/package.json b/EventsServer/node_modules/js-message/package.json new file mode 100644 index 000000000..34ca57de4 --- /dev/null +++ b/EventsServer/node_modules/js-message/package.json @@ -0,0 +1,81 @@ +{ + "_from": "js-message@1.0.5", + "_id": "js-message@1.0.5", + "_inBundle": false, + "_integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "_location": "/js-message", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-message@1.0.5", + "name": "js-message", + "escapedName": "js-message", + "rawSpec": "1.0.5", + "saveSpec": null, + "fetchSpec": "1.0.5" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "_shasum": "2300d24b1af08e89dd095bc1a4c9c9cfcb892d15", + "_spec": "js-message@1.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-message/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "normalized JS Object and JSON message and event protocol for node.js, vanialla js, react.js, components, actions, stores and dispatchers", + "devDependencies": {}, + "engines": { + "node": ">=0.6.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-message#readme", + "keywords": [ + "message", + "normalize", + "events", + "js", + "json", + "protocol", + "ipc", + "node", + "nodejs", + "node.js", + "react", + "react.js", + "reactjs", + "websocket", + "websockets", + "web", + "socket", + "sockets", + "ws", + "flux", + "reflux", + "component", + "components", + "store", + "stores", + "action", + "actions" + ], + "license": "DBAD", + "main": "Message.js", + "name": "js-message", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-message.git" + }, + "scripts": { + "start": "node devServer.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.5" +} diff --git a/EventsServer/node_modules/js-queue/package.json b/EventsServer/node_modules/js-queue/package.json new file mode 100644 index 000000000..63fdbaa7a --- /dev/null +++ b/EventsServer/node_modules/js-queue/package.json @@ -0,0 +1,62 @@ +{ + "_from": "js-queue@2.0.0", + "_id": "js-queue@2.0.0", + "_inBundle": false, + "_integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "_location": "/js-queue", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-queue@2.0.0", + "name": "js-queue", + "escapedName": "js-queue", + "rawSpec": "2.0.0", + "saveSpec": null, + "fetchSpec": "2.0.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "_shasum": "362213cf860f468f0125fc6c96abc1742531f948", + "_spec": "js-queue@2.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-queue/issues" + }, + "bundleDependencies": false, + "dependencies": { + "easy-stack": "^1.0.0" + }, + "deprecated": false, + "description": "Simple JS queue with auto run for node and browsers", + "engines": { + "node": ">=1.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-queue#readme", + "keywords": [ + "queue", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "queue.js", + "name": "js-queue", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-queue.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "2.0.0" +} diff --git a/EventsServer/node_modules/js-queue/queue-vanilla.js b/EventsServer/node_modules/js-queue/queue-vanilla.js new file mode 100644 index 000000000..6f15495ae --- /dev/null +++ b/EventsServer/node_modules/js-queue/queue-vanilla.js @@ -0,0 +1,74 @@ +function Queue(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} diff --git a/EventsServer/node_modules/js-queue/queue.js b/EventsServer/node_modules/js-queue/queue.js new file mode 100644 index 000000000..a8698d7e9 --- /dev/null +++ b/EventsServer/node_modules/js-queue/queue.js @@ -0,0 +1,76 @@ +function Queue(asStack){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} + +module.exports=Queue; diff --git a/EventsServer/node_modules/js-queue/stack.js b/EventsServer/node_modules/js-queue/stack.js new file mode 100644 index 000000000..a9c25640e --- /dev/null +++ b/EventsServer/node_modules/js-queue/stack.js @@ -0,0 +1,3 @@ +const Stack=require('easy-stack'); + +module.exports = Stack; diff --git a/EventsServer/node_modules/node-ipc/dao/client.js b/EventsServer/node_modules/node-ipc/dao/client.js new file mode 100644 index 000000000..41ce50411 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/dao/client.js @@ -0,0 +1,257 @@ +'use strict'; + +const net = require('net'), + tls = require('tls'), + EventParser = require('../entities/EventParser.js'), + Message = require('js-message'), + fs = require('fs'), + Queue = require('js-queue'); + +let Events = require('event-pubsub/es5'); +if(process.version[1]>4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Client extends Events{ + constructor(config,log){ + super(); + Object.assign( + this, + { + Client : Client, + config : config, + queue : new Queue, + socket : false, + connect : connect, + emit : emit, + log : log, + retriesRemaining:config.maxRetries||0, + explicitlyDisconnected: false + } + ); + + eventParser=new EventParser(this.config); + } +} + +function emit(type,data){ + this.log('dispatching event to ', this.id, this.path, ' : ', type, ',', data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(!this.config.sync){ + this.socket.write(message); + return; + } + + this.queue.add( + syncEmit.bind(this,message) + ); +} + +function syncEmit(message){ + this.log('dispatching event to ', this.id, this.path, ' : ', message); + this.socket.write(message); +} + +function connect(){ + //init client object for scope persistance especially inside of socket events. + let client=this; + + client.log('requested connection to ', client.id, client.path); + if(!this.path){ + client.log('\n\n######\nerror: ', client.id ,' client has not specified socket path it wishes to connect to.'); + return; + } + + const options={}; + + if(!client.port){ + client.log('Connecting client on Unix Socket :', client.path); + + options.path=client.path; + + if (process.platform ==='win32' && !client.path.startsWith('\\\\.\\pipe\\')){ + options.path = options.path.replace(/^\//, ''); + options.path = options.path.replace(/\//g, '-'); + options.path= `\\\\.\\pipe\\${options.path}`; + } + + client.socket = net.connect(options); + }else{ + options.host=client.path; + options.port=client.port; + + if(client.config.interface.localAddress){ + options.localAddress=client.config.interface.localAddress; + } + + if(client.config.interface.localPort){ + options.localPort=client.config.interface.localPort; + } + + if(client.config.interface.family){ + options.family=client.config.interface.family; + } + + if(client.config.interface.hints){ + options.hints=client.config.interface.hints; + } + + if(client.config.interface.lookup){ + options.lookup=client.config.interface.lookup; + } + + if(!client.config.tls){ + client.log('Connecting client via TCP to', options); + client.socket = net.connect(options); + }else{ + client.log('Connecting client via TLS to', client.path ,client.port,client.config.tls); + if(client.config.tls.private){ + client.config.tls.key=fs.readFileSync(client.config.tls.private); + } + if(client.config.tls.public){ + client.config.tls.cert=fs.readFileSync(client.config.tls.public); + } + if(client.config.tls.trustedConnections){ + if(typeof client.config.tls.trustedConnections === 'string'){ + client.config.tls.trustedConnections=[client.config.tls.trustedConnections]; + } + client.config.tls.ca=[]; + for(let i=0; i4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Server extends Events{ + constructor(path,config,log,port){ + super(); + Object.assign( + this, + { + config : config, + path : path, + port : port, + udp4 : false, + udp6 : false, + log : log, + server : false, + sockets : [], + emit : emit, + broadcast : broadcast + } + ); + + eventParser=new EventParser(this.config); + + this.on( + 'close', + serverClosed.bind(this) + ); + } + + onStart(socket){ + this.trigger( + 'start', + socket + ); + } + + stop(){ + this.server.close(); + } + + start(){ + if(!this.path){ + this.log('Socket Server Path not specified, refusing to start'); + return; + } + + if(this.config.unlink){ + fs.unlink( + this.path, + startServer.bind(this) + ); + }else{ + startServer.bind(this)(); + } + } +} + +function emit(socket, type, data){ + this.log('dispatching event to socket', ' : ', type, data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + this.log(this.config.encoding) + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + + if(!socket.address || !socket.port){ + this.log('Attempting to emit to a single UDP socket without supplying socket address or port. Redispatching event as broadcast to all connected sockets'); + this.broadcast(type,data); + return; + } + + this.server.write( + message, + socket + ); + return; + } + + socket.write(message); +} + +function broadcast(type,data){ + this.log('broadcasting event to all known sockets listening to ', this.path,' : ', ((this.port)?this.port:''), type, data); + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + for(let i=1, count=this.sockets.length; i0){ + let message=new Message; + message.load(data.shift()); + + // Only set the sock id if it is specified. + if (message.data && message.data.id){ + sock.id=message.data.id; + } + + this.log('received event of : ',message.type,message.data); + + this.publish( + message.type, + message.data, + sock + ); + } +} + +function socketClosed(socket){ + this.publish( + 'close', + socket + ); +} + +function serverCreated(socket) { + this.sockets.push(socket); + + if(socket.setEncoding){ + socket.setEncoding(this.config.encoding); + } + + this.log('## socket connection to server detected ##'); + socket.on( + 'close', + socketClosed.bind(this) + ); + + socket.on( + 'error', + function(err){ + this.log('server socket error',err); + + this.publish('error',err); + }.bind(this) + ); + + socket.on( + 'data', + gotData.bind(this,socket) + ); + + socket.on( + 'message', + function(msg,rinfo) { + if (!rinfo){ + return; + } + + this.log('Received UDP message from ', rinfo.address, rinfo.port); + let data; + + if(this.config.rawSocket){ + data=new Buffer(msg,this.config.encoding); + }else{ + data=msg.toString(); + } + socket.emit('data',data,rinfo); + }.bind(this) + ); + + this.publish( + 'connect', + socket + ); + + if(this.config.rawBuffer){ + return; + } +} + +function startServer() { + this.log( + 'starting server on ',this.path, + ((this.port)?`:${this.port}`:'') + ); + + if(!this.udp4 && !this.udp6){ + this.log('starting TLS server',this.config.tls); + if(!this.config.tls){ + this.server=net.createServer( + serverCreated.bind(this) + ); + }else{ + startTLSServer.bind(this)(); + } + }else{ + this.server=dgram.createSocket( + ((this.udp4)? 'udp4':'udp6') + ); + this.server.write=UDPWrite.bind(this); + this.server.on( + 'listening', + function UDPServerStarted() { + serverCreated.bind(this)(this.server); + }.bind(this) + ); + } + + this.server.on( + 'error', + function(err){ + this.log('server error',err); + + this.publish( + 'error', + err + ); + }.bind(this) + ); + + this.server.maxConnections=this.config.maxConnections; + + if(!this.port){ + this.log('starting server as', 'Unix || Windows Socket'); + if (process.platform ==='win32'){ + this.path = this.path.replace(/^\//, ''); + this.path = this.path.replace(/\//g, '-'); + this.path= `\\\\.\\pipe\\${this.path}`; + } + + this.server.listen( + this.path, + this.onStart.bind(this) + ); + + return; + } + + if(!this.udp4 && !this.udp6){ + this.log('starting server as', (this.config.tls?'TLS':'TCP')); + this.server.listen( + this.port, + this.path, + this.onStart.bind(this) + ); + return; + } + + this.log('starting server as',((this.udp4)? 'udp4':'udp6')); + + this.server.bind( + this.port, + this.path + ); + + this.onStart( + { + address : this.path, + port : this.port + } + ); +} + +function startTLSServer(){ + this.log('starting TLS server',this.config.tls); + if(this.config.tls.private){ + this.config.tls.key=fs.readFileSync(this.config.tls.private); + }else{ + this.config.tls.key=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/private/server.key`); + } + if(this.config.tls.public){ + this.config.tls.cert=fs.readFileSync(this.config.tls.public); + }else{ + this.config.tls.cert=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/server.pub`); + } + if(this.config.tls.dhparam){ + this.config.tls.dhparam=fs.readFileSync(this.config.tls.dhparam); + } + if(this.config.tls.trustedConnections){ + if(typeof this.config.tls.trustedConnections === 'string'){ + this.config.tls.trustedConnections=[this.config.tls.trustedConnections]; + } + this.config.tls.ca=[]; + for(let i=0; i 0) { + // getting the family of first network interface available + IPType = networkInterfaces [ + Object.keys( networkInterfaces )[0] + ][0].family; + } + return IPType; +} + +module.exports=Defaults; diff --git a/EventsServer/node_modules/node-ipc/entities/EventParser.js b/EventsServer/node_modules/node-ipc/entities/EventParser.js new file mode 100644 index 000000000..8ac1f2bda --- /dev/null +++ b/EventsServer/node_modules/node-ipc/entities/EventParser.js @@ -0,0 +1,32 @@ +'use strict'; + +const Defaults = require('./Defaults.js'); + +class Parser{ + constructor(config){ + if(!config){ + config=new Defaults; + } + this.delimiter=config.delimiter; + } + + format(message){ + if(!message.data && message.data!==false && message.data!==0){ + message.data={}; + } + if(message.data['_maxListeners']){ + message.data={}; + } + + message=message.JSON+this.delimiter; + return message; + } + + parse(data){ + let events=data.split(this.delimiter); + events.pop(); + return events; + } +} + +module.exports=Parser; diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..9d5ce05d6 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..74136333d --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,50 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..2bef44794 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js new file mode 100644 index 000000000..6ae692edf --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js new file mode 100644 index 000000000..6c8790170 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.maxConnections=1; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log('DISCONNECTED\n\n',arguments); + } + ); + } +); + +ipc.server.on( + 'error', + function(err){ + ipc.log('Got an ERROR!',err); + } +); + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js new file mode 100644 index 000000000..292415053 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js @@ -0,0 +1,46 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data,'\n\n'); + } + ); + } +); + +console.log(ipc); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js new file mode 100644 index 000000000..3e817da13 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync = true; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + //fake some synch procedural code + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..b6ac81f6b --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js b/EventsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..5da4de4d5 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js new file mode 100644 index 000000000..508702d01 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js new file mode 100644 index 000000000..16fd21fc3 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js new file mode 100644 index 000000000..0c52c268d --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js @@ -0,0 +1,55 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js new file mode 100644 index 000000000..3cbcd8e38 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +//node-ipc will default to its local certs +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js new file mode 100644 index 000000000..bda3eb701 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js new file mode 100644 index 000000000..56eee122b --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js new file mode 100644 index 000000000..26d5b1da6 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js new file mode 100644 index 000000000..ee984a4e1 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js new file mode 100644 index 000000000..a52c3602a --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js new file mode 100644 index 000000000..405db4468 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js new file mode 100644 index 000000000..aec87c100 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js new file mode 100644 index 000000000..f0c66ad22 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js b/EventsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js new file mode 100644 index 000000000..f0d0b29af --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + console.log('connection detected'); + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js b/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..d98a7b061 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8002, //we set the port here because the hello client and world server are already using the default of 8000 and the port 8001. So we can not bind to those while hello and world are connected to them. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Goodbye' + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js b/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..ecc0b45a4 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js b/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..97f927088 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,63 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + 'udp4', + function(){ + console.log(123); + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + messages[data.id]=true; + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling evryone how muchg I am loved!'); + ipc.server.broadcast( + 'message', + { + id : ipc.config.id, + message : 'Everybody Loves The World! Got messages from hello and goodbye!' + } + ); + } + } + ); + + console.log(ipc.server); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js b/EventsServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js new file mode 100644 index 000000000..f731b1ba8 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js @@ -0,0 +1,52 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js b/EventsServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js new file mode 100644 index 000000000..0f7aa4543 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js b/EventsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..73cd13ac5 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data){ + ipc.log('got a message from world ', data, data.toString()); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'hello' + ); + } +); + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js b/EventsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..d0c34f7e2 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js b/EventsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js new file mode 100644 index 000000000..eca2a889c --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const ipc = require('../../node-ipc'); + +const socketPath = '/tmp/ipc.sock'; + +//loop forever so you can see the pid of the cluster sever change in the logs +setInterval( + function() { + ipc.connectTo( + 'world', + socketPath, + connecting + ); + }, + 2000 +); + +function connecting(socket) { + ipc.of.world.on( + 'connect', + function() { + ipc.of.world.emit( + 'currentDate', + { + message: new Date().toISOString() + } + ); + ipc.disconnect('world'); + } + ); +} diff --git a/EventsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js b/EventsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js new file mode 100644 index 000000000..b0942a338 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const ipc=require('../../../node-ipc'); +const cpuCount = require('os').cpus().length; +const cluster = require('cluster'); +const socketPath = '/tmp/ipc.sock'; + +ipc.config.unlink = false; + +if (cluster.isMaster) { + if (fs.existsSync(socketPath)) { + fs.unlinkSync(socketPath); + } + + for (let i = 0; i < cpuCount; i++) { + cluster.fork(); + } +}else{ + ipc.serve( + socketPath, + function() { + ipc.server.on( + 'currentDate', + function(data,socket) { + console.log(`pid ${process.pid} got: `, data); + } + ); + } + ); + + ipc.server.start(); + console.log(`pid ${process.pid} listening on ${socketPath}`); +} diff --git a/EventsServer/node_modules/node-ipc/example/rawBuffer/hello-client.js b/EventsServer/node_modules/node-ipc/example/rawBuffer/hello-client.js new file mode 100644 index 000000000..f22ab5376 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/rawBuffer/hello-client.js @@ -0,0 +1,43 @@ +'use strict'; + +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + //make a 6 byte buffer for example + const myBuffer=new Buffer(6).fill(0); + + myBuffer.writeUInt16BE(0x02,0); + myBuffer.writeUInt32BE(0xffeecc,2); + + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + myBuffer + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/rawBuffer/world.server.js b/EventsServer/node_modules/node-ipc/example/rawBuffer/world.server.js new file mode 100644 index 000000000..f27a02cd5 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + [0xaa] + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data); + ipc.server.emit( + socket, + [0x0d,0xee] + ); + } + ); + } +); + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..57d455eec --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..d556c4694 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..69cfb31c0 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js new file mode 100644 index 000000000..c6d87cb27 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js new file mode 100644 index 000000000..6b3052242 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js @@ -0,0 +1,33 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js new file mode 100644 index 000000000..3142677a2 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; +ipc.config.sync= true; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello'+i + } + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js new file mode 100644 index 000000000..c76e1bb6f --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + setTimeout( + function(){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + }, + 2000 + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..9c7042a90 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..c3ed83c9a --- /dev/null +++ b/EventsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/EventsServer/node_modules/node-ipc/node-ipc.js b/EventsServer/node_modules/node-ipc/node-ipc.js new file mode 100644 index 000000000..1c6336061 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/node-ipc.js @@ -0,0 +1,21 @@ +'use strict'; + +const IPC = require('./services/IPC.js'); + +class IPCModule extends IPC{ + constructor(){ + super(); + //include IPC to make extensible + Object.defineProperty( + this, + 'IPC', + { + enumerable:true, + writable:false, + value:IPC + } + ) + } +} + +module.exports=new IPCModule; diff --git a/EventsServer/node_modules/node-ipc/package.json b/EventsServer/node_modules/node-ipc/package.json new file mode 100644 index 000000000..1f21a0052 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/package.json @@ -0,0 +1,92 @@ +{ + "_from": "node-ipc", + "_id": "node-ipc@9.1.1", + "_inBundle": false, + "_integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "_location": "/node-ipc", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "node-ipc", + "name": "node-ipc", + "escapedName": "node-ipc", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "_shasum": "4e245ed6938e65100e595ebc5dc34b16e8dd5d69", + "_spec": "node-ipc", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/node-ipc/issues" + }, + "bundleDependencies": false, + "dependencies": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + }, + "deprecated": false, + "description": "A nodejs module for local and remote Inter Process Communication (IPC), Neural Networking, and able to facilitate machine learning.", + "devDependencies": { + "codacy-coverage": "2.0.0", + "istanbul": "0.4.1", + "jasmine": "2.4.1", + "node-cmd": "2.0.0" + }, + "directories": { + "example": "example" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "http://riaevangelist.github.io/node-ipc/", + "keywords": [ + "IPC", + "Neural Networking", + "Machine Learning", + "inter", + "process", + "communication", + "unix", + "windows", + "win", + "socket", + "TCP", + "UDP", + "domain", + "sockets", + "threaded", + "communication", + "multi", + "process", + "shared", + "memory" + ], + "license": "DBAD", + "main": "node-ipc.js", + "name": "node-ipc", + "pre-commit": [ + "cover" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/node-ipc.git" + }, + "scripts": { + "cover": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "coverup": "cat ./spec/coverage/lcov.info | codacy-coverage", + "test": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "test-windows": "istanbul cover -x **/spec/** -dir ./spec/coverage ./node_modules/jasmine/bin/jasmine.js" + }, + "version": "9.1.1" +} diff --git a/EventsServer/node_modules/node-ipc/services/IPC.js b/EventsServer/node_modules/node-ipc/services/IPC.js new file mode 100644 index 000000000..3a6edc7f5 --- /dev/null +++ b/EventsServer/node_modules/node-ipc/services/IPC.js @@ -0,0 +1,337 @@ +'use strict'; + +const Defaults = require('../entities/Defaults.js'), + Client = require('../dao/client.js'), + Server = require('../dao/socketServer.js'), + util = require('util'); + +class IPC{ + constructor(){ + Object.defineProperties( + this, + { + config : { + enumerable:true, + writable:true, + value:new Defaults + }, + connectTo : { + enumerable:true, + writable:false, + value:connect + }, + connectToNet: { + enumerable:true, + writable:false, + value:connectNet + }, + disconnect : { + enumerable:true, + writable:false, + value:disconnect + }, + serve : { + enumerable:true, + writable:false, + value:serve + }, + serveNet : { + enumerable:true, + writable:false, + value:serveNet + }, + of : { + enumerable:true, + writable:true, + value:{} + }, + server : { + enumerable:true, + writable:true, + configurable:true, + value:false + }, + log : { + enumerable:true, + writable:false, + value:log + } + } + ); + } +} + +function log(...args){ + if(this.config.silent){ + return; + } + + for(let i=0, count=args.length; i Got a new message : '.debug, data); + + try { + let command = JSON.parse(data) + + switch (command.action) { + case "greetings": { + sendResponse(global.DEFAULT_OK_RESPONSE) + return + } + case "createEventHandler": { + let eventHandler = eventHandlers.get(command.eventHandlerName) + if (eventHandler === undefined) { + /* + We will only create event handlers which were not created before. Remember that someone trying to listen events + at an event handler that does not exist, will create that eventhandler, so it is possible that the emitter + when trying to create the event handler in fact was late and the event handler is already there if the listener + arrived here first. An event handler created with this method is not destroyed if there are no listeners or they + all leave. + */ + eventHandler = newEventHandler() + eventHandler.deleteWhenAllListenersAreGone = false + eventHandlers.set(command.eventHandlerName, eventHandler) + } + sendResponse(global.DEFAULT_OK_RESPONSE) + return + } + case "deleteEventHandler": { + let eventHandler = eventHandlers.get(command.eventHandlerName) + if (eventHandler !== undefined) { + /* + We will only delete event handlers which have noone listening, allowing a listener to continue listening even + when the event emitter is gone, asuming that it could come back at some point in time and resume emitting. + */ + if (eventHandler.listeners.length === 0) { + eventHandlers.delete(command.eventHandlerName) + } else { + eventHandler.deleteWhenAllListenersAreGone = true + } + } + + sendResponse(global.DEFAULT_OK_RESPONSE) + return + } + case "listenToEvent": { + let eventHandler = eventHandlers.get(command.eventHandlerName) + if (eventHandler === undefined) { + /* + We will create event handlers which were not created before if a listener tries to listen to a handler that does noet + exist before the emitter comes and create it. This is to aviod syncronization problems and also problems with emitters life + cycles with higher frecuency that the ones of listeners. Created in this way, event handlers are marked for deletion if + all listeners stop listening at one point in time. + */ + eventHandler = newEventHandler() + eventHandler.deleteWhenAllListenersAreGone = true + eventHandlers.set(command.eventHandlerName, eventHandler) + } + eventSubscriptionId = eventHandler.listenToEvent(command.eventHandlerName, command.eventType, command.callerId, handlerFunction, command.extraData) + + function handlerFunction(event, extraData) { + let outputData = { + action: 'Event Raised', + eventHandlerName: command.eventHandlerName, + eventType: command.eventType, + event: event, + callerId: command.callerId, + extraData: extraData + } + ipc.server.emit( + socket, + 'message', + JSON.stringify(outputData) + ); + } + + let response = { + result: global.DEFAULT_OK_RESPONSE.result, + message: global.DEFAULT_OK_RESPONSE.message, + eventSubscriptionId: eventSubscriptionId + } + + sendResponse(response) + return + } + case "stopListening": { + let eventHandler = eventHandlers.get(command.eventHandlerName) + + if (eventHandler === undefined) { + if (ipc.config.silent !== true) { + console.log("[WARN] Events Server -> Cannot to stop listening to events at an Event Handler that does not exist. Command = "+ JSON.stringify(command)) + } + sendResponse(global.DEFAULT_FAIL_RESPONSE) + return + } + + eventHandler.stopListening(command.eventHandlerName, command.eventType, command.callerId, command.eventSubscriptionId) + + /* + We check here if there are no more listeners and the event handler original emmiter is also gone, then we need to delete + this event handlers since chances are that is not needed anymore. + */ + if (eventHandler.listeners.length === 0 && eventHandler.deleteWhenAllListenersAreGone === true) { + eventHandlers.delete(command.eventHandlerName) + } + + sendResponse(global.DEFAULT_OK_RESPONSE) + return + } + case "raiseEvent": { + let eventHandler = eventHandlers.get(command.eventHandlerName) + + if (eventHandler === undefined) { + if (ipc.config.silent !== true) { + console.log("[WARN] Events Server -> Cannot raise events at an Event Handler that does not exist. Command = " + JSON.stringify(command)) + } + sendResponse(global.DEFAULT_FAIL_RESPONSE) + return + } + //console.log("[INFO] Events Server -> Raising Event " + command.eventHandlerName + " " + command.eventType) + eventHandler.raiseEvent(command.eventType, command.event) + sendResponse(global.DEFAULT_OK_RESPONSE) + return + } + } + + console.log("[WARN] Events Server -> Unknown Command Received:" + data) + + function sendResponse(message) { + if (command.callerId) { + message.callerId = command.callerId + } + message.action = 'Event Server Response' + ipc.server.emit( + socket, + 'message', + JSON.stringify(message) + ); + } + + } catch (err) { + console.log("[ERROR] Events Server -> Bad Command Received:" + data) + console.log("[ERROR] Events Server -> An Error Happened:" + err.stack) + sendResponse(global.DEFAULT_FAIL_RESPONSE) + } + + } + ); + ipc.server.on( + 'connect', + function (socket) { + ipc.log('[INFO] Events Server -> client has connected!'); + } + ); + ipc.server.on( + 'socket.disconnected', + function (socket) { + ipc.log('[INFO] Events Server -> client has disconnected!'); + } + ); + } + + +); + +ipc.server.start(); +console.log('Events Server Started.') + +function newEventHandler() { + let thisObject = { + name: undefined, // This is for debugging purposes only. + deleteWhenAllListenersAreGone: false, + listeners: [], // Here we store all the functions we will call when an event is raiseed. + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent, + finalize: finalize + } + + return thisObject + + function finalize() { + thisObject.listeners = [] + } + + function listenToEvent(eventHandlerName, eventType, callerId, handler, extraData) { + let eventSubscriptionId = Math.trunc(Math.random() * 1000000) + thisObject.listeners.push([eventHandlerName, eventType, callerId, handler, extraData, eventSubscriptionId]) + return eventSubscriptionId + } + + function stopListening(eventHandlerName, eventType, callerId, eventSubscriptionId) { + + if (eventSubscriptionId !== undefined) { + for (let i = 0; i < thisObject.listeners.length; i++) { + if (eventSubscriptionId === thisObject.listeners[i][5]) { + thisObject.listeners.splice(i, 1) + return + } + } + } + if (eventHandlerName !== undefined && eventType !== undefined && callerId !== undefined) { + for (let i = 0; i < thisObject.listeners.length; i++) { + if (eventHandlerName === thisObject.listeners[i][0] && eventType === thisObject.listeners[i][1] && callerId === thisObject.listeners[i][2]) { + thisObject.listeners.splice(i, 1) + return + } + } + } + } + + function raiseEvent(eventType, event) { + for (let i = 0; i < thisObject.listeners.length; i++) { + /* We will execute all the functions listening to this event type. */ + + if (thisObject.listeners[i][1] === eventType) { + let handler = thisObject.listeners[i][3] + let extraData = thisObject.listeners[i][4] + + handler(event, extraData) + } + } + } +} diff --git a/Files-Component/src/DailyFiles.js b/Files-Component/src/DailyFiles.js new file mode 100644 index 000000000..37b7c7464 --- /dev/null +++ b/Files-Component/src/DailyFiles.js @@ -0,0 +1,237 @@ +  +function newDailyFiles () { + const MODULE_NAME = 'Daily Files' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + eventHandler: undefined, + getFileCursor: getFileCursor, + setDatetime: setDatetime, + setTimeFrame: setTimeFrame, + getExpectedFiles: getExpectedFiles, + getFilesLoaded: getFilesLoaded, + initialize: initialize, + finalize: finalize + } + + let filesLoaded = 0 + let expectedFiles = 0 + let fileCloud + let fileCursors = new Map() + let callBackWhenFileReceived + + thisObject.eventHandler = newEventHandler() + + return thisObject + + function finalize () { + try { + thisObject.eventHandler.finalize() + thisObject.eventHandler = undefined + + filesLoaded = undefined + expectedFiles = undefined + fileCloud = undefined + + fileCursors.forEach(finalizeEach) + + function finalizeEach (item, key, mapObj) { + item.finalize() + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } + } + } + + function initialize (pDataMine, pBot, pSession, pProduct, pDataset, pExchange, pMarket, pDatetime, pTimeFrame, callBackFunction) { + try { + callBackWhenFileReceived = callBackFunction + + let exchange = pExchange + + fileCloud = newFileCloud() + fileCloud.initialize(pBot) + + /* First we will get the Data Range */ + + fileCloud.getFile(pDataMine, pBot, pSession, pDataset, exchange, pMarket, undefined, undefined, undefined, true, onDataRangeReceived) + + function onDataRangeReceived (err, pFile) { + try { + let beginDateRange + let endDateRange + + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + beginDateRange = pFile.begin + endDateRange = pFile.end + + break + } + + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + if (err.message === 'File does not exist.') { + beginDateRange = new Date() + 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: { + 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 (pDataset.code.validTimeFrames.includes(periodName) === true) { + let fileCursor = newFileCursor() + fileCursor.eventHandler = thisObject.eventHandler // We share our event handler with each file cursor, so that they can raise events there when files are changed.s + fileCursor.initialize(fileCloud, pDataMine, pBot, pSession, pProduct, pDataset, exchange, pMarket, periodName, periodTime, pDatetime, pTimeFrame, beginDateRange, endDateRange, onInitialized) + function onInitialized (err) { + try { + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackWhenFileReceived(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + default: { + callBackWhenFileReceived(err) + return + } + } + fileCursors.set(periodTime, fileCursor) + expectedFiles = expectedFiles + fileCursor.getExpectedFiles() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onFileReceived -> onInitialized -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } + } + loadThemAll() + + function loadThemAll () { + try { + for (i = 0; i < dailyFilePeriods.length; i++) { + let periodTime = dailyFilePeriods[i][0] + let periodName = dailyFilePeriods[i][1] + + if (pDataset.code.validTimeFrames.includes(periodName) === true) { + let fileCursor = fileCursors.get(periodTime) + fileCursor.reload(onFileReceived) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onFileReceived -> loadThemAll -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onFileReceived -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function onFileReceived (err) { + try { + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackWhenFileReceived(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + callBackWhenFileReceived(err) + return + } + default: { + 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.stack) } + callBackWhenFileReceived(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function getFileCursor (pPeriod) { + try { + return fileCursors.get(pPeriod) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFileCursor -> err = ' + err.stack) } + } + } + + function setDatetime (pDatetime) { + try { + filesLoaded = 0 + expectedFiles = 0 + fileCursors.forEach(setDatetimeToEach) + + function setDatetimeToEach (fileCursor, key, map) { + fileCursor.setDatetime(pDatetime) + expectedFiles = expectedFiles + fileCursor.getExpectedFiles() + fileCursor.reload(onFileReceived) + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] setDatetime -> err = ' + err.stack) } + } + } + + function setTimeFrame (pTimeFrame, pDatetime) { + try { + filesLoaded = 0 + expectedFiles = 0 + fileCursors.forEach(setTimeFrameToEach) + + function setTimeFrameToEach (fileCursor, key, map) { + fileCursor.setTimeFrame(pTimeFrame, pDatetime) + expectedFiles = expectedFiles + fileCursor.getExpectedFiles() + fileCursor.reload(onFileReceived) + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] setTimeFrame -> err = ' + err.stack) } + } + } + + function getExpectedFiles () { + return expectedFiles + } + + function getFilesLoaded () { + return filesLoaded + } +} diff --git a/Files-Component/src/FileCloud.js b/Files-Component/src/FileCloud.js new file mode 100644 index 000000000..79caf7511 --- /dev/null +++ b/Files-Component/src/FileCloud.js @@ -0,0 +1,208 @@ + +function newFileCloud () { + const MODULE_NAME = 'File Cloud' + const INFO_LOG = false + const ERROR_LOG = true + const logger = newWebDebugLog() + 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. + + */ + + let thisObject = { + getFile: getFile, + initialize: initialize + } + + let blobService + + return thisObject + + function initialize (pBot) { + blobService = newFileStorage() + } + + function getFile (pDataMine, pBot, pSession, pDataset, pExchange, pMarket, pPeriodName, pDatetime, pSequence, pDataRange, callBackFunction) { + try { + if (INFO_LOG === true) { logger.write('[INFO] getFile -> Entering function.') } + + const MAX_RETRIES = 3 + + getFileRecursively(0, pDataMine, pBot, pSession, pDataset, pExchange, pMarket, pPeriodName, pDatetime, pSequence, pDataRange, callBackFunction) + + function getFileRecursively (pRetryCounter, pDataMine, pBot, pSession, pDataset, pExchange, pMarket, pPeriodName, pDatetime, pSequence, pDataRange, callBackFunction) { + try { + if (INFO_LOG === true) { logger.write('[INFO] getFile -> getFileRecursively -> Entering function.') } + if (INFO_LOG === true) { logger.write('[INFO] getFile -> getFileRecursively -> key = ' + pDataMine.code.codeName + '-' + pBot.code.codeName + '-' + pDataset.code.filePath + '-' + pDataset.code.fileName) } + + let fileName + let filePath + + if (pDataRange === undefined) { + fileName = pDataset.code.fileName + filePath = pDataset.code.filePath + } else { + if (pDataset.code.dataRange !== undefined) { + fileName = pDataset.code.dataRange.fileName + filePath = pDataset.code.dataRange.filePath + } else { + let customErr = { + result: GLOBAL.CUSTOM_FAIL_RESPONSE.result, + message: 'Missing Configuration.' + } + + if (ERROR_LOG === true) { logger.write('[WARN] getFile -> getFileRecursively -> onFileReceived -> customErr.message = ' + customErr.message) } + if (ERROR_LOG === true) { logger.write('[WARN] getFile -> getFileRecursively -> onFileReceived -> Data Range configuration could not be found. ') } + + callBackFunction(customErr) + return + } + } + + if (fileName === undefined) { + logger.write('[ERROR] getFile -> getFileRecursively -> Inconsistant data. Check the following: ') + logger.write('[ERROR] getFile -> getFileRecursively -> pDataMine = ' + JSON.stringify(pDataMine)) + logger.write('[ERROR] getFile -> getFileRecursively -> pBot = ' + JSON.stringify(pBot)) + logger.write('[ERROR] getFile -> getFileRecursively -> pDataset = ' + JSON.stringify(pDataset)) + logger.write('[ERROR] getFile -> getFileRecursively -> pExchange = ' + JSON.stringify(pExchange)) + logger.write('[ERROR] getFile -> getFileRecursively -> pMarket = ' + JSON.stringify(pMarket)) + logger.write('[ERROR] getFile -> getFileRecursively -> pPeriodName = ' + JSON.stringify(pPeriodName)) + + throw ('Inconsistant data received.') + } + + if (pMarket !== undefined) { + fileName = fileName.replace('@BaseAsset', pMarket.baseAsset) + fileName = fileName.replace('@QuotedAsset', pMarket.quotedAsset) + } + + if (pDataMine !== undefined) { + filePath = filePath.replace('@DataMine', pDataMine.code.codeName) + } + + if (pBot !== undefined) { + filePath = filePath.replace('@Bot', pBot.code.codeName) + } + + if (pSession !== undefined) { + let code + let sessionFolderName = pSession.id + if (pSession.code !== undefined) { + if (pSession.code.folderName !== undefined) { + sessionFolderName = pSession.code.folderName + '-' + pSession.id + } + } + filePath = filePath.replace('@Session', sessionFolderName) + } + + if (pExchange !== undefined) { + filePath = filePath.replace('@Exchange', pExchange.code.codeName) + } + + 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 -> getFileRecursively -> filePath = ' + filePath) } + if (INFO_LOG === true) { logger.write('[INFO] getFile -> getFileRecursively -> fileName = ' + fileName) } + + blobService.getBlobToText(filePath + '/' + fileName, undefined, onFileReceived) + + function onFileReceived (err, text, response) { + try { + if (INFO_LOG === true) { logger.write('[INFO] getFile -> getFileRecursively -> onFileReceived -> Entering function.') } + if (INFO_LOG === true) { logger.write('[INFO] getFile -> getFileRecursively -> onFileReceived -> filePath = ' + filePath) } + if (INFO_LOG === true) { logger.write('[INFO] getFile -> getFileRecursively -> onFileReceived -> fileName = ' + fileName) } + + let data + + if (err && err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) { + if (err.code === 'BlobNotFound' | err.code === 'FileNotFound' | err.code === 'ParentNotFound' | err.code === 'The specified key does not exist.') { + let customErr = { + result: GLOBAL.CUSTOM_FAIL_RESPONSE.result, + message: 'File does not exist.' + } + + if (INFO_LOG === true) { logger.write('[WARN] getFile -> getFileRecursively -> onFileReceived -> customErr.message = ' + customErr.message) } + + callBackFunction(customErr) + return + } + + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> filePath = ' + filePath) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> fileName = ' + fileName) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> Unexpected Error Ocurred.') } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> err = ' + err.stack) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> text = ' + text) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> response = ' + response) } + + if (err.message === 'XHR error') { + if (pRetryCounter < MAX_RETRIES) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> Retrying to get this file. ') } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> MAX_RETRIES = ' + MAX_RETRIES) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> pRetryCounter = ' + pRetryCounter) } + + getFileRecursively(pRetryCounter + 1, pDataMine, pBot, pDataset, pExchange, pMarket, pPeriodName, pDatetime, pSequence, pDataRange, callBackFunction) + return + } else { + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> Could not get this file from storage. ') } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> MAX_RETRIES = ' + MAX_RETRIES) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> pRetryCounter = ' + pRetryCounter) } + } + } + + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } else { + try { + data = JSON.parse(text) + + if (INFO_LOG === true) { logger.write('[INFO] getFile -> getFileRecursively -> onFileReceived -> Data received is valid JSON.') } + + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, data) + return + } catch (err) { + if (ERROR_LOG === true) { logger.write('[WARN] getFile -> getFileRecursively -> onFileReceived -> err = ' + err.stack) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> filePath = ' + filePath) } + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> fileName = ' + fileName) } + + let customErr = { + result: GLOBAL.CUSTOM_OK_RESPONSE.result, + message: 'Data not in JSON Format.' + } + + if (ERROR_LOG === true) { logger.write('[WARN] getFile -> getFileRecursively -> onFileReceived -> customErr.message = ' + customErr.message) } + + callBackFunction(customErr, text) + return + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> onFileReceived -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> getFileRecursively -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFile -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } +} diff --git a/Files-Component/src/FileCursor.js b/Files-Component/src/FileCursor.js new file mode 100644 index 000000000..439ffe270 --- /dev/null +++ b/Files-Component/src/FileCursor.js @@ -0,0 +1,558 @@ + +function newFileCursor () { + const MODULE_NAME = 'File Cursor' + const INFO_LOG = false + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let cursorDate + + let thisObject = { + eventHandler: undefined, // received from parent + reload: reload, + setDatetime: setDatetime, + setTimeFrame: setTimeFrame, + files: new Map(), + getExpectedFiles: getExpectedFiles, + initialize: initialize, + finalize: finalize + } + + let minCursorSize = 10 + let maxCursorSize = 30 + + let market + let exchange + let fileCloud + let dataMine + let bot + let session + let product + let dataset + let periodName + let timeFrame + let beginDateRange + let endDateRange + + let finalized = false + + let eventSubscriptionIdDatasetUpdated + let eventSubscriptionIdDataRangeUpdated + + return thisObject + + function finalize () { + try { + systemEventHandler.stopListening('Dataset Updated', eventSubscriptionIdDatasetUpdated) + systemEventHandler.stopListening('Data Range Updated', eventSubscriptionIdDataRangeUpdated) + + thisObject.eventHandler = undefined + + market = undefined + exchange = undefined + fileCloud = undefined + dataMine = undefined + bot = undefined + session = undefined + product = undefined + dataset = undefined + periodName = undefined + timeFrame = undefined + beginDateRange = undefined + endDateRange = undefined + + thisObject.files = undefined + cursorDate = undefined + fileCloud = undefined + periodName = undefined + timeFrame = undefined + finalized = true + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } + } + } + + function initialize (pFileCloud, pDataMine, pBot, pSession, pProduct, pDataset, pExchange, pMarket, pPeriodName, pTimeFrame, pCursorDate, pCurrentTimeFrame, pBeginDateRange, pEndDateRange, callBackFunction) { + try { + market = pMarket + exchange = pExchange + fileCloud = pFileCloud + dataMine = pDataMine + bot = pBot + session = pSession + product = pProduct + dataset = pDataset + periodName = pPeriodName + cursorDate = removeTime(pCursorDate) + timeFrame = pTimeFrame + beginDateRange = pBeginDateRange + endDateRange = pEndDateRange + + let key = dataMine.code.codeName + '-' + bot.code.codeName + '-' + product.code.codeName + '-' + dataset.code.codeName + systemEventHandler.listenToEvent(key, 'Dataset Updated', undefined, key + '-' + periodName, onResponseDataSet, updateFiles) + + key = dataMine.code.codeName + '-' + bot.code.codeName + '-' + product.code.codeName + '-' + pPeriodName + systemEventHandler.listenToEvent(key, 'Data Range Updated', undefined, key, onResponseDataRange, updateDataRange) + + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) + + function onResponseDataSet (message) { + eventSubscriptionIdDatasetUpdated = message.eventSubscriptionId + } + + function onResponseDataRange (message) { + eventSubscriptionIdDataRangeUpdated = message.eventSubscriptionId + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function updateDataRange (message) { + try { + if (finalized === true) { return } + + if (message.event === undefined || message.event.dateRange === undefined) { return } + + beginDateRange = new Date(message.event.dateRange.begin) + endDateRange = new Date(message.event.dateRange.end) + + let minDate = beginDateRange.valueOf() + let maxDate = endDateRange.valueOf() + ONE_DAY_IN_MILISECONDS + + for (let key of thisObject.files.keys()) { + let keyDate = new Date(key) + + if (keyDate.valueOf() < minDate || keyDate.valueOf() > maxDate) { + thisObject.files.delete(key) + thisObject.eventHandler.raiseEvent('Files Updated') + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateDataRange -> err = ' + err.stack) } + } + } + + function updateFiles (message) { + try { + if (finalized === true) { return } + + if (message.event === undefined || message.event.lastFile === undefined) { return } + + let targetDate = new Date(message.event.lastFile) + let dateString + let file + + dateString = targetDate.getUTCFullYear() + '-' + pad(targetDate.getUTCMonth() + 1, 2) + '-' + pad(targetDate.getUTCDate(), 2) + + fileCloud.getFile(dataMine, bot, session, dataset, exchange, market, periodName, targetDate, undefined, undefined, onFileReceived) + + function onFileReceived (err, file) { + try { + if (finalized === true) { return } + + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + thisObject.files.set(dateString, file) + thisObject.eventHandler.raiseEvent('Files Updated') + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateFiles -> onFileReceived -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateFiles -> err = ' + err.stack) } + } + } + + function setTimeFrame (pTimeFrame, pDatetime) { + try { + if (finalized === true) { return } + + /* + + 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 Frame 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 = removeTime(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] === pTimeFrame) { + positionA = i + } + + if (period[0] === timeFrame) { + positionB = i + } + } + + if (Math.abs(positionB - positionA) <= 1) { + exitSavingMode() + } else { + enterSavingMode() + } + + function enterSavingMode () { + try { + switch (timeFrame) { + + 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] setTimeFrame -> enterSavingMode -> err = ' + err.stack) } + } + } + + function exitSavingMode () { + try { + switch (timeFrame) { + + 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] setTimeFrame -> exitSavingMode -> err = ' + err.stack) } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] setTimeFrame -> err = ' + err.stack) } + } + } + + function setDatetime (pDatetime) { + try { + if (finalized === true) { return } + + if (pDatetime === undefined) { + if (ERROR_LOG === true) { logger.write('[ERROR] setDatetime -> Received undefined datetime.') } + return + } + + cursorDate = removeTime(pDatetime) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] setDatetime -> err = ' + err.stack) } + } + } + + function reload (callBackFunction) { + try { + if (finalized === true) { return } + + getFiles(callBackFunction) + + collectGarbage(callBackFunction) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] reload -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function getFiles (callBackFunction) { + try { + if (finalized === true) { return } + + let i = 0 + let j = 0 + + let dateString + + getNextFile() + + function getNextFile () { + try { + 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.valueOf()) + + if (beginDateRange !== undefined) { + let compareBeginDate = removeTime(beginDateRange) + + if (compareTargetDate.valueOf() < compareBeginDate) { + controlLoop() + return + } + } + + if (endDateRange !== undefined) { + let compareEndDate = removeTime(endDateRange) + + if (compareTargetDate.valueOf() > 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(dataMine, bot, session, dataset, exchange, market, periodName, targetDate, undefined, undefined, onFileReceived) + } else { + controlLoop() + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFiles -> getNextFile -> err = ' + err.stack) } + 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] getFiles -> 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.stack) } + 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.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] getFiles -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function collectGarbage (callBackFunction) { + try { + if (finalized === true) { return } + + 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.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function getExpectedFiles () { + if (finalized === true) { return } + if (INFO_LOG === true) { logger.write('[INFO] getExpectedFiles -> Entering function.') } + + return minCursorSize + } +} diff --git a/Files-Component/src/FileSequence.js b/Files-Component/src/FileSequence.js new file mode 100644 index 000000000..3646cfcc1 --- /dev/null +++ b/Files-Component/src/FileSequence.js @@ -0,0 +1,282 @@ + +function newFileSequence () { + const MODULE_NAME = 'File Sequence' + const INFO_LOG = false + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + eventHandler: undefined, + getFile: getFile, + getExpectedFiles: getExpectedFiles, + getFilesLoaded: getFilesLoaded, + initialize: initialize, + finalize: finalize + } + + 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 so when you add 1 it gives you the amount of files in the sequence, zero. + let market + let dataMine + let bot + let session + let dataset + let product + let finalized = false + let initialized = false + + thisObject.eventHandler = newEventHandler() + + let eventSubscriptionIdDatasetUpdated + let callerId + + return thisObject + + function finalize () { + try { + systemEventHandler.stopListening('Dataset Updated', eventSubscriptionIdDatasetUpdated) + + thisObject.eventHandler.finalize() + thisObject.eventHandler = undefined + + filesLoaded = undefined + fileCloud = undefined + files = undefined + maxSequence = undefined + market = undefined + dataMine = undefined + bot = undefined + session = undefined + dataset = undefined + product = undefined + + finalized = true + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } + } + } + + function initialize (pDataMine, pBot, pSession, pProduct, pDataset, pExchange, pMarket, callBackFunction) { + try { + exchange = pExchange + market = pMarket + dataMine = pDataMine + bot = pBot + session = pSession + dataset = pDataset + product = pProduct + + fileCloud = newFileCloud() + fileCloud.initialize(bot) + + callerId = newUniqueId() + + let key = dataMine.code.codeName + '-' + bot.code.codeName + '-' + product.code.codeName + '-' + dataset.code.codeName + systemEventHandler.listenToEvent(key, 'Dataset Updated', undefined, callerId, onResponse, updateFiles) + + function onResponse (message) { + eventSubscriptionIdDatasetUpdated = message.eventSubscriptionId + } + + /* First we will get the sequence max number */ + + fileCloud.getFile(dataMine, bot, session, dataset, exchange, market, undefined, undefined, 'Sequence', undefined, onSequenceFileReceived) + + function onSequenceFileReceived (err, file) { + try { + if (finalized === true) { return } + + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + case GLOBAL.CUSTOM_OK_RESPONSE.result: { + if (ERROR_LOG === true) { logger.write('[INFO] initialize -> onSequenceFileReceived -> err.message = ' + err.message) } + initialized = true + callBackFunction(err) + return + } + + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + if (err.message === 'File does not exist.') { // We will assume that the process which generates the files has never been started, that does not imply that we can wait for data to come later. + initialized = true + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) + return + } + if (ERROR_LOG === true) { logger.write('[INFO] initialize -> onSequenceFileReceived -> err.message = ' + err.message) } + callBackFunction(err, thisObject) + return + } + + default: { + callBackFunction(err) + return + } + } + initialized = true + maxSequence = Number(file) + + /* Now we will get the sequence of files */ + + for (let i = 0; i <= maxSequence; i++) { + fileCloud.getFile(dataMine, bot, session, dataset, exchange, market, undefined, undefined, i, undefined, onFileReceived) + + function onFileReceived (err, file) { + try { + if (finalized === true) { return } + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + if (ERROR_LOG === true) { logger.write('[INFO] initialize -> onSequenceFileReceived -> onFileReceived -> err.message = ' + err.message) } + callBackFunction(err) + return + } + + default: { + 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.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onSequenceFileReceived -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function updateFiles () { + try { + if (finalized === true || initialized === false) { return } + + /* + + To keep this data structure up-to-date we need to: + + 1. Re-read the sequence file. + 2. Re-load the current last file. + 3. Re-load all new files after our last one. + + */ + + let currentMaxSequence = maxSequence + + fileCloud.getFile(dataMine, bot, session, dataset, exchange, market, undefined, undefined, 'Sequence', undefined, onSequenceFileReceived) + + function onSequenceFileReceived (err, sequenceFile) { + try { + if (finalized === true) { return } + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + return + } + + case GLOBAL.CUSTOM_OK_RESPONSE.result: { + if (ERROR_LOG === true) { logger.write('[WARN] updateFiles -> onSequenceFileReceived -> err.message = ' + err.message) } + return + } + + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + if (ERROR_LOG === true) { logger.write('[WARN] updateFiles -> onSequenceFileReceived -> err.message = ' + err.message) } + return + } + + default: { + if (ERROR_LOG === true) { logger.write('[WARN] updateFiles -> onSequenceFileReceived -> Received Unexpected Response.') } + return + } + } + + maxSequence = Number(sequenceFile) + filesLoaded = 0 + + /* Now we will get the sequence of files, but in this case only from the currentMaxSequence and above. */ + + for (let i = currentMaxSequence; i <= maxSequence; i++) { + fileCloud.getFile(dataMine, bot, session, dataset, exchange, market, undefined, undefined, i, undefined, onFileReceived) + + function onFileReceived (err, file) { + try { + if (finalized === true) { return } + switch (err.result) { + case GLOBAL.DEFAULT_OK_RESPONSE.result: { + break + } + case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { + return + } + case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { + if (ERROR_LOG === true) { logger.write('[WARN] updateFiles -> onSequenceFileReceived -> onFileReceived -> err.message = ' + err.message) } + return + } + } + + files.set(i, file) + + filesLoaded++ + + if (filesLoaded === maxSequence + 1) { + thisObject.eventHandler.raiseEvent('Files Updated', undefined) + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateFiles -> onSequenceFileReceived -> onFileReceived -> err = ' + err.stack) } + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateFiles -> onSequenceFileReceived -> err = ' + err.stack) } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateFiles -> err = ' + err.stack) } + } + } + + function getFile (pSequence) { + return files.get(pSequence) + } + + function getExpectedFiles () { + return maxSequence + 1 + } + + function getFilesLoaded () { + return filesLoaded + } +} diff --git a/Files-Component/src/FileStorage.js b/Files-Component/src/FileStorage.js new file mode 100644 index 000000000..9a9abd787 --- /dev/null +++ b/Files-Component/src/FileStorage.js @@ -0,0 +1,67 @@ +function newFileStorage () { + const MODULE_NAME = 'File Storage' + const INFO_LOG = false + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + const MAX_RETRY = 2 + let currentRetry = 0 + + const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + 'ENOTFOUND', + 'ECONNREFUSED', + 'CONNREFUSED', + 'NOTFOUND', + 'ESOCKETTIMEDOUT', + 'ECONNRESET', + 'ETIMEDOUT', + 'EAI_AGAIN' + ] + + let thisObject = { + getBlobToText: getBlobToText + } + + return thisObject + + async function getBlobToText (filePath, host, callBackFunction) { + try { + if (INFO_LOG === true) { logger.write('[INFO] getBlobToText -> Entering function.') } + + callServer(undefined, 'Storage/' + filePath, (response, fileContent) => { + if (response.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, fileContent) + } else { + callBackFunction(response) + } + }) + } catch (err) { + if (verifyRetry(err.code) && currentRetry < MAX_RETRY) { + currentRetry++ + if (INFO_LOG === true) { console.log('[INFO] getTextFile -> Retrying connection to the server because received error: ' + err.code + '. Retry #: ' + currentRetry) } + getBlobToText(filePath, host, callBackFunction) + } else if (err.message === 'Request aborted') { + let err = { code: 'The specified key does not exist.' } + callBackFunction(err) + } else { + console.log('Error geting the file from the serever:', err) + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } + + function verifyRetry (errorCode) { + for (let i = 0; i < recoverableErrors.length; i++) { + const error = recoverableErrors[i] + if (error === errorCode) { + return true + } + } + return false + } +} diff --git a/Files-Component/src/MarketFiles.js b/Files-Component/src/MarketFiles.js new file mode 100644 index 000000000..272f35476 --- /dev/null +++ b/Files-Component/src/MarketFiles.js @@ -0,0 +1,172 @@ +  +function newMarketFiles () { + const MODULE_NAME = 'Market Files' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + eventHandler: undefined, + getFile: getFile, + getExpectedFiles: getExpectedFiles, + getFilesLoaded: getFilesLoaded, + getFilesNotLoaded: getFilesNotLoaded, + initialize: initialize, + finalize: finalize + } + + let filesLoaded = 0 + let filesNotLoaded = 0 + + let fileCloud + + let files = new Map() + + let market + let exchange + let dataMine + let bot + let session + let dataset + let product + let periodName + + thisObject.eventHandler = newEventHandler() + + let intervalHandle + let finalized = false + + let eventSubscriptionIdDatasetUpdated + + return thisObject + + function finalize () { + try { + systemEventHandler.stopListening('Dataset Updated', eventSubscriptionIdDatasetUpdated) + + thisObject.eventHandler.finalize() + thisObject.eventHandler = undefined + + filesLoaded = undefined + files = undefined + + market = undefined + exchange = undefined + dataMine = undefined + bot = undefined + session = undefined + dataset = undefined + product = undefined + periodName = undefined + + finalized = true + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } + } + } + + function initialize (pDataMine, pBot, pSession, pProduct, pDataset, pExchange, pMarket, callBackFunction) { + try { + exchange = pExchange + market = pMarket + dataMine = pDataMine + bot = pBot + session = pSession + dataset = pDataset + product = pProduct + + 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 (dataset.code.validTimeFrames.includes(periodName) === true) { + fileCloud.getFile(dataMine, bot, session, dataset, exchange, market, periodName, undefined, undefined, undefined, onFileReceived) + + function onFileReceived (err, file) { + try { + if (finalized === true) { return } + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + files.set(periodTime, file) + filesLoaded++ + } else { + filesNotLoaded++ + } + + if (filesLoaded + filesNotLoaded === marketFilesPeriods.length) { + let key = dataMine.code.codeName + '-' + bot.code.codeName + '-' + product.code.codeName + '-' + dataset.code.codeName + systemEventHandler.listenToEvent(key, 'Dataset Updated', undefined, key + '-' + periodName, onResponse, updateFiles) + + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, thisObject) + + function onResponse (message) { + eventSubscriptionIdDatasetUpdated = message.eventSubscriptionId + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onFileReceived -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function updateFiles () { + try { + if (finalized === true) { return } + let updatedFiles = 0 + + /* 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 (dataset.code.validTimeFrames.includes(periodName) === true) { + fileCloud.getFile(dataMine, bot, session, dataset, exchange, market, periodName, undefined, undefined, undefined, onFileReceived) + + function onFileReceived (err, file) { + try { + if (finalized === true) { return } + files.set(periodTime, file) + updatedFiles++ + + if (updatedFiles === marketFilesPeriods.length) { + thisObject.eventHandler.raiseEvent('Files Updated') + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateFiles -> onFileReceived -> err = ' + err.stack) } + } + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] updateFiles -> err = ' + err.stack) } + } + } + + function getFile (pPeriod) { + return files.get(pPeriod) + } + + function getExpectedFiles () { + return marketFilesPeriods.length + } + + function getFilesLoaded () { + return filesLoaded + } + + function getFilesNotLoaded () { + return filesNotLoaded + } +} diff --git a/Files-Component/src/SingleFile.js b/Files-Component/src/SingleFile.js new file mode 100644 index 000000000..1b12b44ef --- /dev/null +++ b/Files-Component/src/SingleFile.js @@ -0,0 +1,83 @@ +  +function newSingleFile () { + const MODULE_NAME = 'Single File' + const INFO_LOG = false + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + getFile: getFile, + initialize: initialize + } + + let fileCloud + let file + + return thisObject + + function initialize (pDataMine, pBot, pSession, pProduct, pDataset, pExchange, pMarket, callBackFunction) { + try { + if (INFO_LOG === true) { logger.write('[INFO] initialize -> Entering function.') } + if (INFO_LOG === true) { logger.write('[INFO] initialize -> key = ' + pDataMine.code.codeName + '-' + pBot.code.codeName + '-' + pProduct.code.codeName) } + + let exchange = pExchange + + fileCloud = newFileCloud() + fileCloud.initialize(pBot) + + /* Now we will get the file */ + + fileCloud.getFile(pDataMine, pBot, pSession, pDataset, 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.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function getFile () { + if (INFO_LOG === true) { logger.write('[INFO] getFile -> Entering function.') } + + return file + } +} diff --git a/Inter-Process-Files/AppSchema.json b/Inter-Process-Files/AppSchema.json new file mode 100644 index 000000000..c4fd20aba --- /dev/null +++ b/Inter-Process-Files/AppSchema.json @@ -0,0 +1,5599 @@ +[ + { + "type": "Plotter Module", + "properties": [ + { + "name": "javascriptCode", + "type": "node", + "childType": "Javascript Code" + }, + { + "name": "shapes", + "type": "node", + "childType": "Shapes", + "autoAdd": true + }, + { + "name": "panels", + "type": "array", + "childType": "Plotter Panel", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Config Plotter Module", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Plotter Module", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "shapes", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Shapes", + "relatedUiObject": "Shapes" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "javascriptCode", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Javascript Code", + "relatedUiObject": "Javascript Code" + }, + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Plotter Panel", + "relatedUiObject": "Plotter Panel" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Plotter Panel", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "initialValues": { + "code": "{ \n\"codeName\": \"\"\n}" + }, + "editors": { + "config": true + }, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Plotter->" + }, + "icon": "plotter-module", + "propertyNameAtParent": "modules" + }, + { + "type": "Shapes", + "icon": "shapes", + "level": 4, + "propertyNameAtParent": "shapes", + "chainAttachesTo": { + "compatibleTypes": "->Plotter Module->" + }, + "properties": [ + { + "name": "chartPoints", + "type": "array", + "childType": "Chart Points", + "autoAdd": true + }, + { + "name": "polygons", + "type": "array", + "childType": "Polygon", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Chart Points", + "relatedUiObject": "Chart Points" + }, + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Polygon", + "relatedUiObject": "Polygon" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Shapes", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Plotter Panel", + "icon": "plotter-panel", + "level": 4, + "propertyNameAtParent": "panels", + "chainAttachesTo": { + "compatibleTypes": "->Plotter Module->" + }, + "initialValues": { + "code": "{ \n\"codeName\": \"\"\n}" + }, + "editors": { + "config": true + }, + "properties": [ + { + "name": "javascriptCode", + "type": "node", + "childType": "Javascript Code" + }, + { + "name": "panelData", + "type": "array", + "childType": "Panel Data", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Config Plotter Panel", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Plotter Panel", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "javascriptCode", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Code", + "relatedUiObject": "Javascript Code" + }, + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Panel Data", + "relatedUiObject": "Panel Data" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Plotter Panel", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Panel Data", + "icon": "data-entry", + "level": 4, + "propertyNameAtParent": "panelData", + "isTitleAllwaysVisible": true, + "editors": { + "config": true + }, + "chainAttachesTo": { + "compatibleTypes": "->Plotter Panel->" + }, + "initialValues": { + "code": "{\n \"valueDecimals\": 2\n}" + }, + "properties": [ + { + "name": "dataFormula", + "type": "node", + "childType": "Data Formula", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Config Panel Data", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Panel Data", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "dataFormula", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Data Formula", + "relatedUiObject": "Data Formula" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Panel Data", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Data Formula", + "icon": "data-formula", + "level": 4, + "propertyNameAtParent": "dataFormula", + "editors": { + "formula": true + }, + "initialValues": { + "code": "// Type you code here" + }, + "chainAttachesTo": { + "compatibleTypes": "->Panel Data->" + }, + "menuItems": [ + { + "action": "Edit Formula", + "actionFunction": "uiObject.formulaEditor.activate", + "label": "Edit Formula", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Data Formula", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Javascript Code", + "icon": "html", + "level": 4, + "propertyNameAtParent": "javascriptCode", + "editors": { + "code": true + }, + "initialValues": { + "code": "// Type you code here" + }, + "chainAttachesTo": { + "compatibleTypes": "->Condition->Procedure Initialization->Procedure Loop->Plotter Module->Plotter Panel->" + }, + "menuItems": [ + { + "action": "Edit Code", + "actionFunction": "uiObject.codeEditor.activate", + "label": "Edit Code", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Data Formula", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Chart Points", + "icon": "chart-points", + "level": 4, + "propertyNameAtParent": "chartPoints", + "isTitleAllwaysVisible": true, + "chainAttachesTo": { + "compatibleTypes": "->Shapes->" + }, + "properties": [ + { + "name": "points", + "type": "array", + "childType": "Point", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Point", + "relatedUiObject": "Point" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Chart Points", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Point", + "icon": "chart-point", + "level": 4, + "propertyNameAtParent": "points", + "isTitleAllwaysVisible": true, + "isReferenceParent": true, + "chainAttachesTo": { + "compatibleTypes": "->Chart Points->" + }, + "properties": [ + { + "name": "pointFormula", + "type": "node", + "childType": "Point Formula", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "pointFormula", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Point Formula", + "relatedUiObject": "Point Formula" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Point", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Point Formula", + "icon": "chart-point-formula", + "level": 4, + "propertyNameAtParent": "pointFormula", + "chainAttachesTo": { + "compatibleTypes": "->Point->" + }, + "editors": { + "formula": true + }, + "initialValues": { + "code": "x = record.begin\ny = record.propertyA" + }, + "menuItems": [ + { + "action": "Edit Formula", + "actionFunction": "uiObject.formulaEditor.activate", + "label": "Edit Formula", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Point", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Polygon", + "icon": "shapes-polygon", + "level": 3, + "propertyNameAtParent": "polygons", + "isTitleAllwaysVisible": true, + "chainAttachesTo": { + "compatibleTypes": "->Shapes->" + }, + "properties": [ + { + "name": "polygonBody", + "type": "node", + "childType": "Polygon Body", + "autoAdd": true + }, + { + "name": "polygonBorder", + "type": "node", + "childType": "Polygon Border", + "autoAdd": true + }, + { + "name": "polygonVertexes", + "type": "array", + "childType": "Polygon Vertex", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "polygonBody", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Polygon Body", + "relatedUiObject": "Polygon Body" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "polygonBorder", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Polygon Border", + "relatedUiObject": "Polygon Border" + }, + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Polygon Vertex", + "relatedUiObject": "Polygon Vertex" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Polygon", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Polygon Body", + "icon": "shapes-polygon-body", + "level": 4, + "propertyNameAtParent": "polygonBody", + "chainAttachesTo": { + "compatibleTypes": "->Polygon->" + }, + "properties": [ + { + "name": "style", + "type": "node", + "childType": "Style", + "autoAdd": true + }, + { + "name": "styleConditions", + "type": "array", + "childType": "Style Condition" + } + ], + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "style", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Style", + "relatedUiObject": "Style" + }, + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Style Condition", + "relatedUiObject": "Style Condition" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Polygon Body", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Polygon Border", + "icon": "shapes-polygon-border", + "level": 4, + "propertyNameAtParent": "polygonBorder", + "chainAttachesTo": { + "compatibleTypes": "->Polygon->" + }, + "properties": [ + { + "name": "style", + "type": "node", + "childType": "Style", + "autoAdd": true + }, + { + "name": "styleConditions", + "type": "array", + "childType": "Style Condition" + } + ], + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "style", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Style", + "relatedUiObject": "Style" + }, + { + "action": "Add UI Object", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Style Condition", + "relatedUiObject": "Style Condition" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Polygon Border", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Style Condition", + "icon": "style-condition", + "level": 4, + "propertyNameAtParent": "styleConditions", + "chainAttachesTo": { + "compatibleTypes": "->Polygon Body->Polygon Border->" + }, + "editors": { + "code": true + }, + "initialValues": { + "code": "" + }, + "properties": [ + { + "name": "style", + "type": "node", + "childType": "Style", + "autoAdd": true + } + ], + "menuItems": [ + { + "action": "Edit Condition", + "actionFunction": "uiObject.codeEditor.activate", + "label": "Edit Condition", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "style", + "actionFunction": "payload.onMenuItemClick", + "label": "Add Style", + "relatedUiObject": "Style" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Style Condition", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Style", + "icon": "style", + "level": 4, + "propertyNameAtParent": "style", + "chainAttachesTo": { + "compatibleTypes": "->Style Condition->Polygon Body->Polygon Border->" + }, + "editors": { + "config": true + }, + "initialValues": { + "code": "{}" + }, + "menuItems": [ + { + "action": "Configure Style", + "actionFunction": "uiObject.configEditor.activate", + "label": "Configure Style", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Style", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Polygon Vertex", + "icon": "shapes-polygon-vertex", + "level": 4, + "propertyNameAtParent": "polygonVertexes", + "isReferenceChildren": true, + "referenceAttachesTo": { + "compatibleTypes": "->Point->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Polygon->" + }, + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "actionFunction": "payload.onMenuItemClick", + "label": "Delete Polygon Edge", + "iconPathOn": "delete", + "iconPathOff": "delete" + } + ], + "addLeftIcons": true + }, + { + "type": "Workspace", + "menuItems": [ + { + "action": "Pin / Unpin", + "currentStatus": true, + "iconPathOn": "menu-fix-unpinned", + "iconPathOff": "menu-fix-pinned", + "actionFunction": "payload.onMenuItemClick", + "ring": 1 + }, + { + "action": "Backup Workspace", + "iconPathOn": "menu-backup", + "iconPathOff": "menu-backup", + "actionFunction": "payload.onMenuItemClick", + "ring": 1 + }, + { + "action": "Add UI Object", + "label": "Add Trading System", + "relatedUiObject": "Trading System", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Network", + "relatedUiObject": "Network", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Data Mine", + "relatedUiObject": "Data Mine", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Crypto Ecosystem", + "relatedUiObject": "Crypto Ecosystem", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Charting Space", + "relatedUiObject": "Charting Space", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Share Workspace", + "label": "Share", + "iconPathOn": "menu-share", + "iconPathOff": "menu-share", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": false, + "isPinned": true, + "positionLocked": true, + "icon": "design-tool" + }, + { + "type": "Situation", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Condition", + "relatedUiObject": "Condition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Situation", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 0, + "chainAttachesTo": { + "compatibleTypes": "->Take Position Event->Trigger On Event->Trigger Off Event->Next Phase Event->" + }, + "icon": "pyramid", + "properties": [ + { + "name": "conditions", + "type": "array", + "childType": "Condition", + "autoAdd": true + } + ], + "propertyNameAtParent": "situations" + }, + { + "type": "Condition", + "menuItems": [ + { + "action": "Edit Condition", + "label": "Edit Condition", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.conditionEditor.activate" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "code", + "label": "Add Code", + "relatedUiObject": "Javascript Code", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Condition", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "editors": { + "condition": true + }, + "level": 4, + "chainAttachesTo": { + "compatibleTypes": "->Situation->" + }, + "icon": "testing", + "properties": [ + { + "name": "javascriptCode", + "type": "node", + "childType": "Javascript Code", + "autoAdd": true + } + ], + "propertyNameAtParent": "conditions" + }, + { + "type": "Network", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Network Node", + "relatedUiObject": "Network Node", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Network", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "isHierarchyHead": true, + "addLeftIcons": true, + "level": 1, + "icon": "network", + "properties": [ + { + "name": "networkNodes", + "type": "array", + "childType": "Network Node" + } + ] + }, + { + "type": "Network Node", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "dataStorage", + "label": "Add Data Storage", + "relatedUiObject": "Data Storage", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "dataMining", + "label": "Add Data Mining", + "relatedUiObject": "Data Mining", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "testingEnvironment", + "label": "Add Testing Environment", + "relatedUiObject": "Testing Environment", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "productionEnvironment", + "label": "Add Production Environment", + "relatedUiObject": "Production Environment", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Network Node", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Network->" + }, + "icon": "network-node", + "properties": [ + { + "name": "dataStorage", + "type": "node", + "childType": "Data Storage", + "autoAdd": true + }, + { + "name": "dataMining", + "type": "node", + "childType": "Data Mining", + "autoAdd": true + }, + { + "name": "testingEnvironment", + "type": "node", + "childType": "Testing Environment", + "autoAdd": true + }, + { + "name": "productionEnvironment", + "type": "node", + "childType": "Production Environment", + "autoAdd": true + } + ], + "propertyNameAtParent": "networkNodes" + }, + { + "type": "Task Manager", + "menuItems": [ + { + "action": "Run All Tasks", + "label": "Run All Tasks", + "iconPathOn": "paper-plane", + "iconPathOff": "paper-plane", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Stop All Tasks", + "label": "Stop All Tasks", + "iconPathOn": "stop", + "iconPathOff": "stop", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Task", + "relatedUiObject": "Task", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Task Manager", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Data Mining->Testing Environment->Production Environment->" + }, + "icon": "task-manager", + "properties": [ + { + "name": "tasks", + "type": "array", + "childType": "Task", + "autoAdd": true + } + ], + "propertyNameAtParent": "taskManagers" + }, + { + "type": "Task", + "menuItems": [ + { + "action": "Run Task", + "label": "Run", + "workingLabel": "Stop", + "workDoneLabel": "Task Running", + "workFailedLabel": "Task Cannot be Run", + "secondaryAction": "Stop Task", + "secondaryLabel": "Stop", + "secondaryWorkingLabel": "Stopping...", + "secondaryWorkDoneLabel": "Task Stopped", + "secondaryWorkFailedLabel": "Task Cannot be Stopped", + "secondaryIcon": "stop", + "iconPathOn": "paper-plane", + "iconPathOff": "paper-plane", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "bot", + "label": "Add Sensor Bot Instance", + "relatedUiObject": "Sensor Bot Instance", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "bot", + "label": "Add Indicator Bot Instance", + "relatedUiObject": "Indicator Bot Instance", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "bot", + "label": "Add Trading Bot Instance", + "relatedUiObject": "Trading Bot Instance", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Task", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Task Manager->" + }, + "icon": "timeline", + "properties": [ + { + "name": "bot", + "type": "node", + "childType": "Trading Bot Instance" + }, + { + "name": "bot", + "type": "node", + "childType": "Sensor Bot Instance" + }, + { + "name": "bot", + "type": "node", + "childType": "Indicator Bot Instance" + } + ], + "propertyNameAtParent": "tasks" + }, + { + "type": "Trading Bot Instance", + "menuItems": [ + { + "action": "Configure Bot Instance", + "label": "Configure Bot Instance", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Process Instance", + "relatedUiObject": "Trading Process Instance", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Bot Instance", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "editors": { + "config": true + }, + "initialValues": { + "code": "{}" + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Task->" + }, + "icon": "bot-trading", + "properties": [ + { + "name": "processes", + "type": "array", + "childType": "Trading Process Instance", + "autoAdd": true + } + ], + "propertyNameAtParent": "bot" + }, + { + "type": "Sensor Process Instance", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "marketReference", + "label": "Add Market Reference", + "relatedUiObject": "Market Reference", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Process Instance", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "title": [ + "Use Reference Parent" + ], + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Sensor Bot Instance->" + }, + "icon": "process", + "properties": [ + { + "name": "marketReference", + "type": "node", + "childType": "Market Reference", + "autoAdd": true + } + ], + "propertyNameAtParent": "processes" + }, + { + "type": "Indicator Process Instance", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "marketReference", + "label": "Add Market Reference", + "relatedUiObject": "Market Reference", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Process Instance", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "title": [ + "Use Reference Parent" + ], + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Indicator Bot Instance->" + }, + "icon": "process", + "properties": [ + { + "name": "marketReference", + "type": "node", + "childType": "Market Reference", + "autoAdd": true + } + ], + "propertyNameAtParent": "processes" + }, + { + "type": "Trading Process Instance", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "marketReference", + "label": "Add Market Reference", + "relatedUiObject": "Market Reference", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "session", + "label": "Add Backtesting", + "relatedUiObject": "Backtesting Session", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "session", + "label": "Add Live Trading", + "relatedUiObject": "Live Trading Session", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "session", + "label": "Add Fordward Testing", + "relatedUiObject": "Fordward Testing Session", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "session", + "label": "Add Paper Trading", + "relatedUiObject": "Paper Trading Session", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Process Instance", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "title": [ + "Use Reference Parent" + ], + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Trading Bot Instance->" + }, + "icon": "process", + "properties": [ + { + "name": "session", + "type": "node", + "childType": "Backtesting Session" + }, + { + "name": "session", + "type": "node", + "childType": "Paper Trading Session" + }, + { + "name": "session", + "type": "node", + "childType": "Fordward Testing Session" + }, + { + "name": "session", + "type": "node", + "childType": "Live Trading Session" + }, + { + "name": "marketReference", + "type": "node", + "childType": "Market Reference", + "autoAdd": true + } + ], + "propertyNameAtParent": "processes" + }, + { + "type": "Backtesting Session", + "menuItems": [ + { + "action": "Run Session", + "label": "Run", + "workingLabel": "Run Request Sent", + "workDoneLabel": "Session Running", + "workFailedLabel": "Session Cannot be Run", + "secondaryAction": "Stop Session", + "secondaryLabel": "Stop", + "secondaryWorkingLabel": "Stopping...", + "secondaryWorkDoneLabel": "Session Stopped", + "secondaryWorkFailedLabel": "Session Cannot be Stopped", + "secondaryIcon": "stop", + "iconPathOn": "paper-plane", + "iconPathOff": "paper-plane", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "parameters", + "label": "Add Parameters", + "relatedUiObject": "Parameters", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "socialBots", + "label": "Add Social Bots", + "relatedUiObject": "Social Bots", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Configure Session", + "label": "Configure Session", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Session", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "initialValues": { + "code": "{ \n\"folderName\": \"Type here the folder name where you want the logs of this process to be stored to.\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "referenceAttachesTo": { + "compatibleTypes": "->Trading System->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Trading Process Instance->" + }, + "icon": "session-backtesting", + "properties": [ + { + "name": "parameters", + "type": "node", + "childType": "Parameters", + "autoAdd": true + }, + { + "name": "socialBots", + "type": "node", + "childType": "Social Bots" + } + ], + "propertyNameAtParent": "session" + }, + { + "type": "Parameters", + "menuItems": [ + { + "action": "Add Missing Children", + "label": "Add Missing Params", + "relatedUiObject": "Parameters", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Parameters", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Trading System->Backtesting Session->Live Trading Session->Paper Trading Session->Fordward Testing Session->" + }, + "icon": "settings", + "properties": [ + { + "name": "baseAsset", + "type": "node", + "childType": "Base Asset", + "autoAdd": true + }, + { + "name": "quotedAsset", + "type": "node", + "childType": "Quoted Asset", + "autoAdd": true + }, + { + "name": "timeRange", + "type": "node", + "childType": "Time Range", + "autoAdd": true + }, + { + "name": "timeFrame", + "type": "node", + "childType": "Time Frame", + "autoAdd": true + }, + { + "name": "slippage", + "type": "node", + "childType": "Slippage", + "autoAdd": true + }, + { + "name": "feeStructure", + "type": "node", + "childType": "Fee Structure", + "autoAdd": true + } + ], + "propertyNameAtParent": "parameters" + }, + { + "type": "Base Asset", + "menuItems": [ + { + "action": "Configure Base Asset", + "label": "Configure Base Asset", + "iconPathOn": "settings", + "iconPathOff": "settings", + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Base Asset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "initialValues": { + "code": "{\n\"initialBalance\": 0.001,\n\"minimumBalance\": 0.0001,\n\"maximumBalance\": 0.1\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Exchange Account Asset->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Parameters->" + }, + "icon": "loading", + "alternativeIcons": "Use Reference Grandparent", + "title": [ + "Use Reference Grandparent" + ], + "propertyNameAtParent": "baseAsset" + }, + { + "type": "Quoted Asset", + "menuItems": [ + { + "action": "Configure Quoted Asset", + "label": "Configure Quoted Asset", + "iconPathOn": "settings", + "iconPathOff": "settings", + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Quoted Asset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "initialValues": { + "code": "{\n\"initialBalance\": 0,\n\"minimumBalance\": 0,\n\"maximumBalance\": 100\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Exchange Account Asset->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Parameters->" + }, + "icon": "loading", + "alternativeIcons": "Use Reference Grandparent", + "title": [ + "Use Reference Grandparent" + ], + "propertyNameAtParent": "quotedAsset" + }, + { + "type": "Market Base Asset", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Base Asset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Asset->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Market->" + }, + "icon": "loading", + "alternativeIcons": "Use Reference Parent", + "title": [ + "Use Reference Parent" + ], + "propertyNameAtParent": "baseAsset" + }, + { + "type": "Market Quoted Asset", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Base Asset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Asset->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Market->" + }, + "icon": "loading", + "alternativeIcons": "Use Reference Parent", + "title": [ + "Use Reference Parent" + ], + "propertyNameAtParent": "quotedAsset" + }, + { + "type": "Market Reference", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "keyInstance", + "label": "Add Key Instance", + "relatedUiObject": "Key Instance", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Base Asset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "title": [ + "Use Reference Parent Parent Parent", + "Use Reference Parent" + ], + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Market->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Sensor Process Instance->Indicator Process Instance->Trading Process Instance->" + }, + "properties": [ + { + "name": "keyInstance", + "type": "node", + "childType": "Key Instance" + } + ], + "icon": "market", + "propertyNameAtParent": "marketReference" + }, + { + "type": "Time Range", + "menuItems": [ + { + "action": "Configure Time Range", + "label": "Configure Time Range", + "iconPathOn": "settings", + "iconPathOff": "settings", + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Time Range", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"initialDatetime\": \"2019-07-01T00:00:00.000Z\",\n\"finalDatetime\": \"2019-09-01T00:00:00.000Z\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Parameters->" + }, + "icon": "schedule", + "propertyNameAtParent": "timeRange" + }, + { + "type": "Time Frame", + "menuItems": [ + { + "action": "Configure Time Frame", + "label": "Configure Time Frame", + "iconPathOn": "settings", + "iconPathOff": "settings", + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Time Frame", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"value\": \"01-hs\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Parameters->" + }, + "icon": "time-period", + "propertyNameAtParent": "timeFrame" + }, + { + "type": "Slippage", + "menuItems": [ + { + "action": "Configure Slippage", + "label": "Configure Slippage", + "iconPathOn": "settings", + "iconPathOff": "settings", + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Slippage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"positionRate\": 0.1,\n\"stopLoss\": 0.2,\n\"takeProfit\": 0.3\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Parameters->" + }, + "icon": "support", + "propertyNameAtParent": "slippage" + }, + { + "type": "Fee Structure", + "menuItems": [ + { + "action": "Configure Fee Structure", + "label": "Configure Fee Structure", + "iconPathOn": "settings", + "iconPathOff": "settings", + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Fee Structure", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"maker\": 0.15,\n\"taker\": 0.25\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Parameters->" + }, + "icon": "sitemap", + "propertyNameAtParent": "feeStructure" + }, + { + "type": "Paper Trading Session", + "menuItems": [ + { + "action": "Run Session", + "label": "Run", + "workingLabel": "Run Request Sent", + "workDoneLabel": "Session Running", + "workFailedLabel": "Session Cannot be Run", + "secondaryAction": "Stop Session", + "secondaryLabel": "Stop", + "secondaryWorkingLabel": "Stopping...", + "secondaryWorkDoneLabel": "Session Stopped", + "secondaryWorkFailedLabel": "Session Cannot be Stopped", + "secondaryIcon": "stop", + "iconPathOn": "paper-plane", + "iconPathOff": "paper-plane", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "parameters", + "label": "Add Parameters", + "relatedUiObject": "Parameters", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "socialBots", + "label": "Add Social Bots", + "relatedUiObject": "Social Bots", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Configure Session", + "label": "Configure Session", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Session", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "initialValues": { + "code": "{ \n\"folderName\": \"Type here the folder name where you want the logs of this process to be stored to.\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "referenceAttachesTo": { + "compatibleTypes": "->Trading System->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Process Instance->" + }, + "icon": "session-paper-trading", + "properties": [ + { + "name": "parameters", + "type": "node", + "childType": "Parameters", + "autoAdd": true + }, + { + "name": "socialBots", + "type": "node", + "childType": "Social Bots" + } + ], + "propertyNameAtParent": "session" + }, + { + "type": "Fordward Testing Session", + "menuItems": [ + { + "action": "Run Session", + "label": "Run", + "workingLabel": "Run Request Sent", + "workDoneLabel": "Session Running", + "workFailedLabel": "Session Cannot be Run", + "secondaryAction": "Stop Session", + "secondaryLabel": "Stop", + "secondaryWorkingLabel": "Stopping...", + "secondaryWorkDoneLabel": "Session Stopped", + "secondaryWorkFailedLabel": "Session Cannot be Stopped", + "secondaryIcon": "stop", + "iconPathOn": "paper-plane", + "iconPathOff": "paper-plane", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "parameters", + "label": "Add Parameters", + "relatedUiObject": "Parameters", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "socialBots", + "label": "Add Social Bots", + "relatedUiObject": "Social Bots", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Configure Session", + "label": "Configure Session", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Session", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "initialValues": { + "code": "{ \n\"folderName\": \"Type here the folder name where you want the logs of this process to be stored to.\",\n\"balancePercentage\": \"Type here the the % of the balance to be used in this test.\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "referenceAttachesTo": { + "compatibleTypes": "->Trading System->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Process Instance->" + }, + "icon": "session-forward-testing", + "properties": [ + { + "name": "parameters", + "type": "node", + "childType": "Parameters", + "autoAdd": true + }, + { + "name": "socialBots", + "type": "node", + "childType": "Social Bots" + } + ], + "propertyNameAtParent": "session" + }, + { + "type": "Key Instance", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Key Instance", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "title": [ + "Use Reference Parent" + ], + "addLeftIcons": true, + "level": 5, + "chainAttachesTo": { + "compatibleTypes": "->Market Reference->" + }, + "referenceAttachesTo": { + "compatibleTypes": "->Exchange Account Key->" + }, + "icon": "login", + "propertyNameAtParent": "keyInstance" + }, + { + "type": "Live Trading Session", + "menuItems": [ + { + "action": "Run Session", + "label": "Run", + "workingLabel": "Run Request Sent", + "workDoneLabel": "Session Running", + "workFailedLabel": "Session Cannot be Run", + "secondaryAction": "Stop Session", + "secondaryLabel": "Stop", + "secondaryWorkingLabel": "Stopping...", + "secondaryWorkDoneLabel": "Session Stopped", + "secondaryWorkFailedLabel": "Session Cannot be Stopped", + "secondaryIcon": "stop", + "iconPathOn": "paper-plane", + "iconPathOff": "paper-plane", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "parameters", + "label": "Add Parameters", + "relatedUiObject": "Parameters", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "socialBots", + "label": "Add Social Bots", + "relatedUiObject": "Social Bots", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Configure Session", + "label": "Configure Session", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Session", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "referenceAttachesTo": { + "compatibleTypes": "->Trading System->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Process Instance->" + }, + "icon": "session-live-trading", + "properties": [ + { + "name": "parameters", + "type": "node", + "childType": "Parameters", + "autoAdd": true + }, + { + "name": "socialBots", + "type": "node", + "childType": "Social Bots" + } + ], + "propertyNameAtParent": "session" + }, + { + "type": "Sensor Bot Instance", + "menuItems": [ + { + "action": "Configure Bot Instance", + "label": "Configure Bot Instance", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Process Instance", + "relatedUiObject": "Sensor Process Instance", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Bot Instance", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "initialValues": { + "code": "{ \n\"startDate\": \"2020-01-01\"\n}" + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Task->" + }, + "icon": "bot-sensor", + "properties": [ + { + "name": "processes", + "type": "array", + "childType": "Sensor Process Instance", + "autoAdd": true + } + ], + "propertyNameAtParent": "bot" + }, + { + "type": "Indicator Bot Instance", + "menuItems": [ + { + "action": "Configure Bot Instance", + "label": "Configure Bot Instance", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Process Instance", + "relatedUiObject": "Indicator Process Instance", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Bot Instance", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "initialValues": { + "code": "{}" + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Task->" + }, + "icon": "bot-indicator", + "properties": [ + { + "name": "processes", + "type": "array", + "childType": "Indicator Process Instance", + "autoAdd": true + } + ], + "propertyNameAtParent": "bot" + }, + { + "type": "Data Mine", + "menuItems": [ + { + "action": "Configure Data Mine", + "label": "Configigure Data Mine", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Sensor Bot", + "relatedUiObject": "Sensor Bot", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Indicator Bot", + "relatedUiObject": "Indicator Bot", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Trading Bot", + "relatedUiObject": "Trading Bot", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Plotter", + "relatedUiObject": "Plotter", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Mine", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "isHierarchyHead": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "icon": "data-mine", + "properties": [ + { + "name": "sensorBots", + "type": "array", + "childType": "Sensor Bot" + }, + { + "name": "indicatorBots", + "type": "array", + "childType": "Indicator Bot" + }, + { + "name": "tradingBots", + "type": "array", + "childType": "Trading Bot" + }, + { + "name": "plotters", + "type": "array", + "childType": "Plotter" + } + ] + }, + { + "type": "Sensor Bot", + "menuItems": [ + { + "action": "Configure Bot", + "label": "Configure Bot", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Process Definition", + "relatedUiObject": "Process Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Product Definition", + "relatedUiObject": "Product Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Sensor Bot", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "initialValues": { + "code": "{ \n\"repo\": \"If this is a legacy bot, write here the repository name in order to locate its code.\"\n}" + }, + "level": 0, + "chainAttachesTo": { + "compatibleTypes": "->Data Mine->" + }, + "icon": "bot-sensor", + "properties": [ + { + "name": "processes", + "type": "array", + "childType": "Process Definition", + "autoAdd": true + }, + { + "name": "products", + "type": "array", + "childType": "Product Definition", + "autoAdd": true + } + ], + "propertyNameAtParent": "sensorBots" + }, + { + "type": "Process Definition", + "menuItems": [ + { + "action": "Configure Process", + "label": "Configure Process", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add Missing Children", + "label": "Add Missing Items", + "relatedUiObject": "Calculations Procedure", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Process Definition", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Sensor Bot->Indicator Bot->Trading Bot->" + }, + "icon": "process-definition", + "properties": [ + { + "name": "processOutput", + "type": "node", + "childType": "Process Output", + "autoAdd": true + }, + { + "name": "processDependencies", + "type": "node", + "childType": "Process Dependencies", + "autoAdd": true + }, + { + "name": "statusReport", + "type": "node", + "childType": "Status Report", + "autoAdd": true + }, + { + "name": "executionStartedEvent", + "type": "node", + "childType": "Execution Started Event", + "autoAdd": true + }, + { + "name": "executionFinishedEvent", + "type": "node", + "childType": "Execution Finished Event", + "autoAdd": true + } + ], + "propertyNameAtParent": "processes" + }, + { + "type": "Process Output", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Output Dataset", + "relatedUiObject": "Output Dataset", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Process Output", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "icon": "process-output", + "properties": [ + { + "name": "outputDatasets", + "type": "array", + "childType": "Output Dataset", + "autoAdd": true + } + ], + "propertyNameAtParent": "processOutput" + }, + { + "type": "Process Dependencies", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Status Dependency", + "relatedUiObject": "Status Dependency", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Data Dependency", + "relatedUiObject": "Data Dependency", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Process Dependencies", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "icon": "process-dependencies", + "properties": [ + { + "name": "statusDependencies", + "type": "array", + "childType": "Status Dependency", + "autoAdd": true + }, + { + "name": "dataDependencies", + "type": "array", + "childType": "Data Dependency", + "autoAdd": true + } + ], + "propertyNameAtParent": "processDependencies" + }, + { + "type": "Status Dependency", + "menuItems": [ + { + "action": "Configure Status Dependency", + "label": "Config Status Dependency", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Status Dependency", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"mainUtility\": \"Self Reference|Market Starting Point|Market Ending Point\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Status Report->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Process Dependencies->" + }, + "icon": "status-dependency", + "propertyNameAtParent": "statusDependencies" + }, + { + "type": "Status Report", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Status Report", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "icon": "status-report", + "propertyNameAtParent": "statusReport" + }, + { + "type": "Execution Started Event", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Event", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Execution Finished Event->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "icon": "execution-started-event", + "propertyNameAtParent": "executionStartedEvent" + }, + { + "type": "Execution Finished Event", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Event", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Process Definition->" + }, + "icon": "execution-finished-event", + "propertyNameAtParent": "executionFinishedEvent" + }, + { + "type": "Product Definition", + "menuItems": [ + { + "action": "Configure Product", + "label": "Configure Product", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "record", + "label": "Add Record Definition", + "relatedUiObject": "Record Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Dataset Definition", + "relatedUiObject": "Dataset Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add Missing Children", + "label": "Add Missing Items", + "relatedUiObject": "Calculations Procedure", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Product Definition", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Plotter Module->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Sensor Bot->Indicator Bot->Trading Bot->" + }, + "icon": "product-definition", + "properties": [ + { + "name": "datasets", + "type": "array", + "childType": "Dataset Definition", + "autoAdd": true + }, + { + "name": "record", + "type": "node", + "childType": "Record Definition", + "autoAdd": true + }, + { + "name": "calculations", + "type": "node", + "childType": "Calculations Procedure" + }, + { + "name": "dataBuilding", + "type": "node", + "childType": "Data Building Procedure" + } + ], + "propertyNameAtParent": "products" + }, + { + "type": "Dataset Definition", + "menuItems": [ + { + "action": "Configure Definition", + "label": "Configure Definition", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Dataset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Product Definition->" + }, + "icon": "dataset-definition", + "propertyNameAtParent": "datasets" + }, + { + "type": "Indicator Bot", + "menuItems": [ + { + "action": "Configure Bot", + "label": "Configure Bot", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Process Definition", + "relatedUiObject": "Process Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Product Definition", + "relatedUiObject": "Product Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Indicator Bot", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Data Mine->" + }, + "icon": "bot-indicator", + "properties": [ + { + "name": "processes", + "type": "array", + "childType": "Process Definition", + "autoAdd": true + }, + { + "name": "products", + "type": "array", + "childType": "Product Definition", + "autoAdd": true + } + ], + "propertyNameAtParent": "indicatorBots" + }, + { + "type": "Output Dataset", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Output Dataset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Dataset Definition->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Process Output->" + }, + "icon": "output-dataset", + "propertyNameAtParent": "outputDatasets" + }, + { + "type": "Data Dependency", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Dependency", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "referenceAttachesTo": { + "compatibleTypes": "->Dataset Definition->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Process Dependencies->" + }, + "icon": "data-dependency", + "propertyNameAtParent": "dataDependencies" + }, + { + "type": "Record Definition", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Record Property", + "relatedUiObject": "Record Property", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Record Definition", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Product Definition->" + }, + "icon": "record-definition", + "properties": [ + { + "name": "properties", + "type": "array", + "childType": "Record Property", + "autoAdd": true + } + ], + "propertyNameAtParent": "record" + }, + { + "type": "Record Property", + "menuItems": [ + { + "action": "Configure Property", + "label": "Configure Property", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "formula", + "label": "Add Formula", + "relatedUiObject": "Formula", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Record Property", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Record Definition->" + }, + "icon": "record-property", + "properties": [ + { + "name": "formula", + "type": "node", + "childType": "Formula" + } + ], + "propertyNameAtParent": "properties" + }, + { + "type": "Formula", + "menuItems": [ + { + "action": "Edit Formula", + "label": "Edit Formula", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.formulaEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Formula", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "editors": { + "formula": true + }, + "initialValues": { + "code": "// Write your Formula code here." + }, + "level": 4, + "chainAttachesTo": { + "compatibleTypes": "->Position Size->Position Rate->Phase->Announcement->Record Property->" + }, + "icon": "pipette", + "propertyNameAtParent": "formula" + }, + { + "type": "Calculations Procedure", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "initialization", + "label": "Add Procedure Initialization", + "relatedUiObject": "Procedure Initialization", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "loop", + "label": "Add Procedure Loop", + "relatedUiObject": "Procedure Loop", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Procedure", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Product Definition->" + }, + "icon": "calculations-procedure", + "properties": [ + { + "name": "initialization", + "type": "node", + "childType": "Procedure Initialization", + "autoAdd": true + }, + { + "name": "loop", + "type": "node", + "childType": "Procedure Loop", + "autoAdd": true + } + ], + "propertyNameAtParent": "calculations" + }, + { + "type": "Procedure Initialization", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "code", + "label": "Add Code", + "relatedUiObject": "Javascript Code", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Initialization", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Calculations Procedure->Data Building Procedure->" + }, + "icon": "procedure-initialization", + "properties": [ + { + "name": "javascriptCode", + "type": "node", + "childType": "Javascript Code", + "autoAdd": true + } + ], + "propertyNameAtParent": "initialization" + }, + { + "type": "Procedure Loop", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "code", + "label": "Add Code", + "relatedUiObject": "Javascript Code", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Procedure Loop", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 5, + "chainAttachesTo": { + "compatibleTypes": "->Calculations Procedure->Data Building Procedure->" + }, + "icon": "procedure-loop", + "properties": [ + { + "name": "javascriptCode", + "type": "node", + "childType": "Javascript Code", + "autoAdd": true + } + ], + "propertyNameAtParent": "loop" + }, + { + "type": "Data Building Procedure", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "initialization", + "label": "Add Procedure Initialization", + "relatedUiObject": "Procedure Initialization", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "loop", + "label": "Add Procedure Loop", + "relatedUiObject": "Procedure Loop", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Procedure", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 5, + "chainAttachesTo": { + "compatibleTypes": "->Product Definition->" + }, + "icon": "data-building-procedure", + "properties": [ + { + "name": "loop", + "type": "node", + "childType": "Procedure Loop", + "autoAdd": true + }, + { + "name": "initialization", + "type": "node", + "childType": "Procedure Initialization", + "autoAdd": true + } + ], + "propertyNameAtParent": "dataBuilding" + }, + { + "type": "Trading Bot", + "menuItems": [ + { + "action": "Configure Bot", + "label": "Configure Bot", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Process Definition", + "relatedUiObject": "Process Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Product Definition", + "relatedUiObject": "Product Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Trading Bot", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Data Mine->" + }, + "icon": "bot-trading", + "properties": [ + { + "name": "processes", + "type": "array", + "childType": "Process Definition", + "autoAdd": true + }, + { + "name": "products", + "type": "array", + "childType": "Product Definition", + "autoAdd": true + } + ], + "propertyNameAtParent": "tradingBots" + }, + { + "type": "Plotter", + "menuItems": [ + { + "action": "Configure Plotter", + "label": "Configure Plotter", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Plotter Module", + "relatedUiObject": "Plotter Module", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Plotter", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "icon": "plotter", + "chainAttachesTo": { + "compatibleTypes": "->Data Mine->" + }, + "properties": [ + { + "name": "modules", + "type": "array", + "childType": "Plotter Module", + "autoAdd": true + } + ], + "propertyNameAtParent": "plotters" + }, + { + "type": "Trading System", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Strategy", + "relatedUiObject": "Strategy", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "parameters", + "label": "Add Parameters", + "relatedUiObject": "Parameters", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Trading System", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "isHierarchyHead": true, + "addLeftIcons": true, + "level": 0, + "icon": "analysis", + "properties": [ + { + "name": "strategies", + "type": "array", + "childType": "Strategy", + "autoAdd": true + }, + { + "name": "parameters", + "type": "node", + "childType": "Parameters", + "autoAdd": true + } + ], + "propertyNameAtParent": "tradingSystem" + }, + { + "type": "Strategy", + "menuItems": [ + { + "action": "Add Missing Children", + "label": "Add Missing Stages", + "relatedUiObject": "Strategy", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Strategy", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Trading System->" + }, + "icon": "quality", + "properties": [ + { + "name": "triggerStage", + "type": "node", + "childType": "Trigger Stage", + "autoAdd": true + }, + { + "name": "openStage", + "type": "node", + "childType": "Open Stage", + "autoAdd": true + }, + { + "name": "manageStage", + "type": "node", + "childType": "Manage Stage", + "autoAdd": true + }, + { + "name": "closeStage", + "type": "node", + "childType": "Close Stage", + "autoAdd": true + } + ], + "propertyNameAtParent": "strategies" + }, + { + "type": "Trigger Stage", + "menuItems": [ + { + "action": "Add Missing Children", + "label": "Add Missing Events", + "relatedUiObject": "Trigger Stage", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Stage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Strategy->" + }, + "icon": "stage-trigger", + "properties": [ + { + "name": "triggerOn", + "type": "node", + "childType": "Trigger On Event", + "autoAdd": true + }, + { + "name": "triggerOff", + "type": "node", + "childType": "Trigger Off Event", + "autoAdd": true + }, + { + "name": "takePosition", + "type": "node", + "childType": "Take Position Event", + "autoAdd": true + } + ], + "propertyNameAtParent": "triggerStage" + }, + { + "type": "Trigger On Event", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Situation", + "relatedUiObject": "Situation", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Announcement", + "relatedUiObject": "Announcement", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Event", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Trigger Stage->" + }, + "icon": "stage-trigger-trigger-on", + "properties": [ + { + "name": "situations", + "type": "array", + "childType": "Situation", + "autoAdd": true + }, + { + "name": "announcements", + "type": "array", + "childType": "Announcement" + } + ], + "propertyNameAtParent": "triggerOn" + }, + { + "type": "Trigger Off Event", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Situation", + "relatedUiObject": "Situation", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Announcement", + "relatedUiObject": "Announcement", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Event", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Trigger Stage->" + }, + "icon": "stage-trigger-trigger-off", + "properties": [ + { + "name": "situations", + "type": "array", + "childType": "Situation", + "autoAdd": true + }, + { + "name": "announcements", + "type": "array", + "childType": "Announcement" + } + ], + "propertyNameAtParent": "triggerOff" + }, + { + "type": "Take Position Event", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Situation", + "relatedUiObject": "Situation", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Announcement", + "relatedUiObject": "Announcement", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Event", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Trigger Stage->" + }, + "icon": "stage-trigger-take-position", + "properties": [ + { + "name": "situations", + "type": "array", + "childType": "Situation", + "autoAdd": true + }, + { + "name": "announcements", + "type": "array", + "childType": "Announcement" + } + ], + "propertyNameAtParent": "takePosition" + }, + { + "type": "Open Stage", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "initialDefinition", + "label": "Add Initial Definition", + "relatedUiObject": "Initial Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "openExecution", + "label": "Add Open Execution", + "relatedUiObject": "Open Execution", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Stage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Strategy->" + }, + "icon": "stage-open", + "properties": [ + { + "name": "initialDefinition", + "type": "node", + "childType": "Initial Definition", + "autoAdd": true + }, + { + "name": "openExecution", + "type": "node", + "childType": "Open Execution", + "autoAdd": true + } + ], + "propertyNameAtParent": "openStage" + }, + { + "type": "Initial Definition", + "menuItems": [ + { + "action": "Add Missing Children", + "label": "Add Missing Items", + "relatedUiObject": "Initial Definition", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Definition", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Open Stage->" + }, + "icon": "initial-definition", + "properties": [ + { + "name": "stopLoss", + "type": "node", + "childType": "Initial Stop", + "autoAdd": true + }, + { + "name": "takeProfit", + "type": "node", + "childType": "Initial Take Profit", + "autoAdd": true + }, + { + "name": "positionSize", + "type": "node", + "childType": "Position Size" + }, + { + "name": "positionRate", + "type": "node", + "childType": "Position Rate" + } + ], + "propertyNameAtParent": "initialDefinition" + }, + { + "type": "Stop", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Phase", + "relatedUiObject": "Phase", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Item", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Manage Stage->Initial Definition->" + }, + "icon": "stop", + "properties": [ + { + "name": "phases", + "type": "array", + "childType": "Phase", + "autoAdd": true + } + ], + "propertyNameAtParent": "stopLoss" + }, + { + "type": "Initial Stop", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Phase", + "relatedUiObject": "Phase", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Item", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Manage Stage->Initial Definition->" + }, + "icon": "stop", + "properties": [ + { + "name": "phases", + "type": "array", + "maxItems": 1, + "childType": "Phase", + "autoAdd": true + } + ], + "propertyNameAtParent": "stopLoss" + }, + { + "type": "Phase", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "formula", + "label": "Add Formula", + "relatedUiObject": "Formula", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "nextPhaseEvent", + "label": "Add Next Phase Event", + "relatedUiObject": "Next Phase Event", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Announcement", + "relatedUiObject": "Announcement", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Phase", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 1, + "chainedToSameType": true, + "chainAttachesTo": { + "compatibleTypes": "->Stop->Take Profit->Initial Stop->Initial Take Profit->Phase->" + }, + "icon": "placeholder", + "properties": [ + { + "name": "formula", + "type": "node", + "childType": "Formula", + "autoAdd": true + }, + { + "name": "nextPhaseEvent", + "type": "node", + "childType": "Next Phase Event" + }, + { + "name": "announcements", + "type": "array", + "childType": "Announcement" + } + ], + "propertyNameAtParent": "phases" + }, + { + "type": "Next Phase Event", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Situation", + "relatedUiObject": "Situation", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Announcement", + "relatedUiObject": "Announcement", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Event", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Phase->" + }, + "icon": "next-phase-event", + "properties": [ + { + "name": "situations", + "type": "array", + "childType": "Situation", + "autoAdd": true + }, + { + "name": "announcements", + "type": "array", + "childType": "Announcement" + } + ], + "propertyNameAtParent": "nextPhaseEvent" + }, + { + "type": "Take Profit", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Phase", + "relatedUiObject": "Phase", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Item", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Manage Stage->Initial Definition->" + }, + "icon": "stage-open-take-profit", + "properties": [ + { + "name": "phases", + "type": "array", + "childType": "Phase", + "autoAdd": true + } + ], + "propertyNameAtParent": "takeProfit" + }, + { + "type": "Initial Take Profit", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Phase", + "relatedUiObject": "Phase", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Item", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "chainAttachesTo": { + "compatibleTypes": "->Manage Stage->Initial Definition->" + }, + "icon": "stage-open-take-profit", + "properties": [ + { + "name": "phases", + "type": "array", + "maxItems": 1, + "childType": "Phase", + "autoAdd": true + } + ], + "propertyNameAtParent": "takeProfit" + }, + { + "type": "Position Size", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "formula", + "label": "Add Formula", + "relatedUiObject": "Formula", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Position Size", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 4, + "chainAttachesTo": { + "compatibleTypes": "->Initial Definition->" + }, + "icon": "stage-open-position-size", + "properties": [ + { + "name": "formula", + "type": "node", + "childType": "Formula" + } + ], + "propertyNameAtParent": "positionSize" + }, + { + "type": "Position Rate", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "formula", + "label": "Add Formula", + "relatedUiObject": "Formula", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Position Rate", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 5, + "chainAttachesTo": { + "compatibleTypes": "->Initial Definition->" + }, + "icon": "stage-open-postion-rate", + "properties": [ + { + "name": "formula", + "type": "node", + "childType": "Formula" + } + ], + "propertyNameAtParent": "positionRate" + }, + { + "type": "Open Execution", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 5, + "chainAttachesTo": { + "compatibleTypes": "->Open Stage->" + }, + "icon": "piggy-bank", + "propertyNameAtParent": "openExecution" + }, + { + "type": "Manage Stage", + "menuItems": [ + { + "action": "Add Missing Children", + "label": "Add Missing Items", + "relatedUiObject": "Manage Stage", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Stage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 1, + "chainAttachesTo": { + "compatibleTypes": "->Strategy->" + }, + "icon": "video-player", + "properties": [ + { + "name": "stopLoss", + "type": "node", + "childType": "Stop", + "autoAdd": true + }, + { + "name": "takeProfit", + "type": "node", + "childType": "Take Profit", + "autoAdd": true + } + ], + "propertyNameAtParent": "manageStage" + }, + { + "type": "Close Stage", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "closeExecution", + "label": "Add Close Execution", + "relatedUiObject": "Close Execution", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Stage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Strategy->" + }, + "icon": "stage-close", + "properties": [ + { + "name": "closeExecution", + "type": "node", + "childType": "Close Execution", + "autoAdd": true + } + ], + "propertyNameAtParent": "closeStage" + }, + { + "type": "Close Execution", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "chainAttachesTo": { + "compatibleTypes": "->Close Stage->" + }, + "icon": "piggy-bank", + "propertyNameAtParent": "closeExecution" + }, + { + "type": "Social Bots", + "menuItems": [ + { + "action": "Add Telegram Bot", + "label": "Add Telegram Bot", + "relatedUiObject": "Telegram Bot", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Social Bots", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 4, + "chainAttachesTo": { + "compatibleTypes": "->Backtesting Session->Live Trading Session->Paper Trading Session->Fordward Testing Session->" + }, + "icon": "social-bot", + "properties": [ + { + "name": "bots", + "type": "array", + "childType": "Telegram Bot", + "autoAdd": true + } + ], + "propertyNameAtParent": "socialBots" + }, + { + "type": "Telegram Bot", + "editors": { + "config": true + }, + "menuItems": [ + { + "action": "Config Telegram Bot", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Telegram Bot", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Telegram Bot", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"botToken\": \"Paste here the bot token obtained from Telegram Bot Father\",\n\"chatId\": \"Write here the chat or group id where the announcements are going to be sent to, as it is a number with no quotes please.\"\n}" + }, + "addLeftIcons": true, + "level": 4, + "chainAttachesTo": { + "compatibleTypes": "->Social Bots->" + }, + "icon": "paper-plane", + "properties": [], + "propertyNameAtParent": "bots" + }, + { + "type": "Announcement", + "editors": { + "config": true + }, + "menuItems": [ + { + "action": "Config Announcement", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Announcement", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "formula", + "label": "Add Formula", + "relatedUiObject": "Formula", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Announcement", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "initialValues": { + "code": "{ \n\"text\": \"Write here what you want to announce when there is no Formula.\"\n}" + }, + "addLeftIcons": true, + "level": 4, + "referenceAttachesTo": { + "compatibleTypes": "->Telegram Bot->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Trigger On Event->Trigger Off Event->Take Position Event->Next Phase Event->Phase->" + }, + "properties": [ + { + "name": "formula", + "type": "nide", + "childType": "Formula", + "autoAdd": true + } + ], + "icon": "promotion", + "propertyNameAtParent": "announcements" + }, + { + "type": "Crypto Ecosystem", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "cryptoExchanges", + "label": "Add Crypto Exchanges", + "relatedUiObject": "Crypto Exchanges", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Crypto Ecosystem", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isHierarchyHead": true, + "addLeftIcons": true, + "level": 1, + "icon": "image", + "properties": [ + { + "name": "cryptoExchanges", + "type": "node", + "childType": "Network Node" + } + ] + }, + { + "type": "Crypto Exchanges", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Crypto Exchange", + "relatedUiObject": "Crypto Exchange", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Crypto Exchanges", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 1, + "icon": "crypto-exchanges", + "chainAttachesTo": { + "compatibleTypes": "->Crypto Ecosystem->" + }, + "propertyNameAtParent": "cryptoExchanges", + "properties": [ + { + "name": "exchanges", + "type": "array", + "childType": "Crypto Exchange", + "autoAdd": true + } + ] + }, + { + "type": "Crypto Exchange", + "menuItems": [ + { + "action": "Config Crypto Exchange", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Crypto Exchange", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "exchangeAssets", + "label": "Add Exchange Assets", + "relatedUiObject": "Exchange Assets", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "exchangeMarkets", + "label": "Add Exchange Markets", + "relatedUiObject": "Exchange Markets", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "exchangeAccounts", + "label": "Add Exchange Accounts", + "relatedUiObject": "Exchange Accounts", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Crypto Exchange", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 2, + "icon": "crypto-exchange", + "editors": { + "config": true + }, + "initialValues": { + "code": "{ \n\"codeName\": \"Write here the name of the exchange here.\"\n}" + }, + "chainAttachesTo": { + "compatibleTypes": "->Crypto Exchanges->" + }, + "propertyNameAtParent": "exchanges", + "properties": [ + { + "name": "exchangeAssets", + "type": "node", + "childType": "Exchange Assets", + "autoAdd": true + }, + { + "name": "exchangeMarkets", + "type": "node", + "childType": "Exchange Markets", + "autoAdd": true + }, + { + "name": "exchangeAccounts", + "type": "node", + "childType": "Exchange Accounts", + "autoAdd": true + } + ], + "alternativeIcons": [ + { + "codeName": "Bitfinex", + "iconName": "bitfinex" + }, + { + "codeName": "Binance", + "iconName": "binance" + }, + { + "codeName": "Kucoin", + "iconName": "kucoin" + }, + { + "codeName": "Bittrex", + "iconName": "bittrex" + }, + { + "codeName": "Kraken", + "iconName": "kraken" + }, + { + "codeName": "Poloniex", + "iconName": "poloniex" + }, + { + "codeName": "Upbit", + "iconName": "upbit" + }, + { + "codeName": "Bitmex", + "iconName": "bitmex" + }, + { + "codeName": "Liquid", + "iconName": "liquid" + }, + { + "codeName": "HitBTC", + "iconName": "hitbtc" + } + ] + }, + { + "type": "Exchange Assets", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Asset", + "relatedUiObject": "Asset", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Exchange Assets", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "exchange-assets", + "chainAttachesTo": { + "compatibleTypes": "->Crypto Exchange->" + }, + "propertyNameAtParent": "exchangeAssets", + "properties": [ + { + "name": "assets", + "type": "array", + "childType": "Asset", + "autoAdd": true + } + ] + }, + { + "type": "Asset", + "menuItems": [ + { + "action": "Config Asset", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Asset", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Asset", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 4, + "icon": "asset", + "editors": { + "config": true + }, + "initialValues": { + "code": "{ \n\"codeName\": \"Write here the code name of the asset here.\"\n}" + }, + "chainAttachesTo": { + "compatibleTypes": "->Exchange Assets->" + }, + "propertyNameAtParent": "assets", + "alternativeIcons": [ + { + "codeName": "XMR", + "iconName": "monero" + }, + { + "codeName": "USDT", + "iconName": "tether" + }, + { + "codeName": "TRX", + "iconName": "tron" + }, + { + "codeName": "XLM", + "iconName": "stellar" + }, + { + "codeName": "QTUM", + "iconName": "qtum" + }, + { + "codeName": "NEO", + "iconName": "neo" + }, + { + "codeName": "LTC", + "iconName": "litecoin" + }, + { + "codeName": "ETC", + "iconName": "ethereum-classic" + }, + { + "codeName": "ETH", + "iconName": "ethereum" + }, + { + "codeName": "EOS", + "iconName": "eos" + }, + { + "codeName": "DASH", + "iconName": "dash" + }, + { + "codeName": "ADA", + "iconName": "cardano" + }, + { + "codeName": "BCH", + "iconName": "bitcoin-cash" + }, + { + "codeName": "BTC", + "iconName": "bitcoin" + }, + { + "codeName": "BNB", + "iconName": "binance-coin" + }, + { + "codeName": "USD", + "iconName": "us-dollar" + } + ] + }, + { + "type": "Exchange Markets", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Market", + "relatedUiObject": "Market", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Exchange Markets", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "exchange-markets", + "chainAttachesTo": { + "compatibleTypes": "->Crypto Exchange->" + }, + "propertyNameAtParent": "exchangeMarkets", + "properties": [ + { + "name": "markets", + "type": "array", + "childType": "Market", + "autoAdd": true + } + ] + }, + { + "type": "Market", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "baseAsset", + "label": "Add Base Asset", + "relatedUiObject": "Market Base Asset", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "quotedAsset", + "label": "Add Quoted Asset", + "relatedUiObject": "Market Quoted Asset", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Config Market", + "actionFunction": "uiObject.configEditor.activate", + "label": "Config Market", + "iconPathOn": "html", + "iconPathOff": "html" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Market", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "properties": [ + { + "name": "baseAsset", + "type": "node", + "childType": "Market Base Asset", + "autoAdd": true + }, + { + "name": "quotedAsset", + "type": "node", + "childType": "Market Quoted Asset", + "autoAdd": true + } + ], + "addLeftIcons": true, + "isTitleAllwaysVisible": true, + "level": 4, + "icon": "market", + "editors": { + "config": true + }, + "initialValues": { + "code": "{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}" + }, + "chainAttachesTo": { + "compatibleTypes": "->Exchange Markets->" + }, + "propertyNameAtParent": "markets" + }, + { + "type": "Exchange Accounts", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add User Account", + "relatedUiObject": "User Account", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Exchange Accounts", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "exchange-accounts", + "chainAttachesTo": { + "compatibleTypes": "->Crypto Exchange->" + }, + "propertyNameAtParent": "exchangeAccounts", + "properties": [ + { + "name": "userAccounts", + "type": "array", + "childType": "User Account", + "autoAdd": true + } + ], + "isPersonalData": true + }, + { + "type": "User Account", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "userAssets", + "label": "Add User Assets", + "relatedUiObject": "User Assets", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "userKeys", + "label": "Add User Keys", + "relatedUiObject": "User Keys", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Account", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "user-account", + "chainAttachesTo": { + "compatibleTypes": "->Exchange Accounts->" + }, + "propertyNameAtParent": "userAccounts", + "properties": [ + { + "name": "userAssets", + "type": "node", + "childType": "User Assets", + "autoAdd": true + }, + { + "name": "userKeys", + "type": "node", + "childType": "User Keys", + "autoAdd": true + } + ], + "isPersonalData": true + }, + { + "type": "User Assets", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Asset", + "relatedUiObject": "Exchange Account Asset", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete User Assets", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "user-assets", + "chainAttachesTo": { + "compatibleTypes": "->User Account->" + }, + "propertyNameAtParent": "userAssets", + "properties": [ + { + "name": "assets", + "type": "array", + "childType": "Exchange Account Asset", + "autoAdd": true + } + ], + "isPersonalData": true + }, + { + "type": "User Keys", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Key", + "relatedUiObject": "Exchange Account Key", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete User Keys", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "user-keys", + "chainAttachesTo": { + "compatibleTypes": "->User Account->" + }, + "propertyNameAtParent": "userKeys", + "properties": [ + { + "name": "keys", + "type": "array", + "childType": "Exchange Account Key", + "autoAdd": true + } + ], + "isPersonalData": true + }, + { + "type": "Exchange Account Asset", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Asset", + "visible": true, + "iconPathOn": "delete", + "iconPathOff": "delete", + "rawRadius": 8, + "targetRadius": 0, + "currentRadius": 0, + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "addLeftIcons": true, + "level": 4, + "referenceAttachesTo": { + "compatibleTypes": "->Asset->" + }, + "chainAttachesTo": { + "compatibleTypes": "->User Assets->" + }, + "icon": "exchange-account-asset", + "title": [ + "Use Reference Parent" + ], + "propertyNameAtParent": "assets", + "alternativeIcons": "Use Reference Parent", + "isPersonalData": true + }, + { + "type": "Exchange Account Key", + "menuItems": [ + { + "action": "Configure Key", + "label": "Configure Key", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete This Key", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"codeName\": \"Paste your exchange API name or label here\",\n\"secret\": \"Paste your exchange API secret key here. To keep you exchange keys safe, never share data structures you may download from the system, as downloads contain all information in the data structure, including personal information such as exchange keys. Use the share option on the menu instead. The share option strips sensitive information and outputs a file that is safe for sharing.\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 4, + "chainAttachesTo": { + "compatibleTypes": "->User Keys->" + }, + "icon": "exchange-account-key", + "propertyNameAtParent": "keys", + "isPersonalData": true + }, + { + "type": "Charting Space", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "viewport", + "label": "Add Viewport", + "relatedUiObject": "Viewport", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Dashboard", + "relatedUiObject": "Dashboard", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Charting Space", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isHierarchyHead": true, + "addLeftIcons": true, + "level": 1, + "icon": "charting-space", + "properties": [ + { + "name": "viewport", + "type": "node", + "childType": "Viewport", + "autoAdd": true + }, + { + "name": "dashboards", + "type": "array", + "childType": "Dashboard" + } + ] + }, + { + "type": "Dashboard", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Time Machine", + "relatedUiObject": "Time Machine", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Dashboard", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 1, + "icon": "dashboard", + "chainAttachesTo": { + "compatibleTypes": "->Charting Space->" + }, + "propertyNameAtParent": "dashboards", + "properties": [ + { + "name": "timeMachines", + "type": "array", + "childType": "Time Machine" + } + ] + }, + { + "type": "Time Machine", + "menuItems": [ + { + "action": "Configure Time Machine", + "label": "Configure Time Machine", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "timeScale", + "label": "Add Time Scale", + "relatedUiObject": "Time Scale", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "rateScale", + "label": "Add Rate Scale", + "relatedUiObject": "Rate Scale", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "timeFrameScale", + "label": "Add Time Frame Scale", + "relatedUiObject": "Time Frame Scale", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "label": "Add Timeline Chart", + "relatedUiObject": "Timeline Chart", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Crypto Exchange", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 1, + "isTitleAllwaysVisible": true, + "icon": "time-machine", + "chainAttachesTo": { + "compatibleTypes": "->Dashboard->" + }, + "propertyNameAtParent": "timeMachines", + "properties": [ + { + "name": "timeScale", + "type": "node", + "childType": "Time Scale", + "autoAdd": true + }, + { + "name": "rateScale", + "type": "node", + "childType": "Rate Scale", + "autoAdd": true + }, + { + "name": "timeFrameScale", + "type": "node", + "childType": "Time Frame Scale", + "autoAdd": true + }, + { + "name": "timelineCharts", + "type": "array", + "childType": "Timeline Chart", + "autoAdd": true + } + ] + }, + { + "type": "Viewport", + "menuItems": [ + { + "action": "Configure Viewport", + "label": "Configure Viewport", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Viewport", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"topMargin\": 40,\n\"bottomMargin\": 40\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "icon": "viewport", + "chainAttachesTo": { + "compatibleTypes": "->Charting Space->" + }, + "propertyNameAtParent": "viewport" + }, + { + "type": "Time Scale", + "menuItems": [ + { + "action": "Configure Time Scale", + "label": "Configure Time Scale", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Time Scale", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"autoMinScale\": true,\n\"autoMaxScale\": true,\n\"fromDate\": \"2018-01-01\",\n\"toDate\": \"2020-12-31\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "icon": "time-scale", + "chainAttachesTo": { + "compatibleTypes": "->Time Machine->" + }, + "propertyNameAtParent": "timeScale" + }, + { + "type": "Rate Scale", + "menuItems": [ + { + "action": "Configure Rate Scale", + "label": "Configure Rate Scale", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Rate Scale", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"autoMinScale\": true,\n\"autoMaxScale\": true,\n\"minValue\": 0,\n\"maxValue\": 25000\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "icon": "rate-scale", + "chainAttachesTo": { + "compatibleTypes": "->Time Machine->Timeline Chart->" + }, + "propertyNameAtParent": "rateScale" + }, + { + "type": "Time Frame Scale", + "menuItems": [ + { + "action": "Configure Frame Scale", + "label": "Configure Frame Scale", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Time Frame Scale", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "initialValues": { + "code": "{ \n\"value\": \"24-hs\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 2, + "icon": "time-frame-scale", + "chainAttachesTo": { + "compatibleTypes": "->Time Machine->Timeline Chart->" + }, + "propertyNameAtParent": "timeFrameScale" + }, + { + "type": "Timeline Chart", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "rateScale", + "label": "Add Rate Scale", + "relatedUiObject": "Rate Scale", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "timeFrameScale", + "label": "Add Time Frame Scale", + "relatedUiObject": "Time Frame Scale", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "layersManager", + "label": "Add Layers Manager", + "relatedUiObject": "Layers Manager", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Time Timeline Chart", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "isTitleAllwaysVisible": true, + "icon": "time-line-chart", + "referenceAttachesTo": { + "compatibleTypes": "->Market->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Time Machine->" + }, + "propertyNameAtParent": "timelineCharts", + "properties": [ + { + "name": "rateScale", + "type": "node", + "childType": "Rate Scale" + }, + { + "name": "timeFrameScale", + "type": "node", + "childType": "Time Frame Scale" + }, + { + "name": "layersManager", + "type": "node", + "childType": "Layers Manager", + "autoAdd": true + } + ] + }, + { + "type": "Layers Manager", + "menuItems": [ + { + "action": "Configure Layers Manager", + "label": "Configure Layers Manager", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "label": "Add Layer", + "relatedUiObject": "Layer", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Layers Manager", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "editors": { + "config": true + }, + "initialValues": { + "code": "{ \n\"visibleLayers\": 5\n}" + }, + "level": 3, + "icon": "layer-manager", + "chainAttachesTo": { + "compatibleTypes": "->Timeline Chart->" + }, + "propertyNameAtParent": "layersManager", + "properties": [ + { + "name": "layers", + "type": "array", + "childType": "Layer", + "autoAdd": true + } + ] + }, + { + "type": "Layer", + "menuItems": [ + { + "action": "Configure Layer", + "label": "Configure Layer", + "iconPathOn": "html", + "iconPathOff": "html", + "dontShowAtFullscreen": true, + "actionFunction": "uiObject.configEditor.activate" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "timeFrameScale", + "label": "Add Time Frame Scale", + "relatedUiObject": "Time Frame Scale", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Layer", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "initialValues": { + "code": "{ \n\"status\": \"off\"\n}" + }, + "addLeftIcons": true, + "editors": { + "config": true + }, + "level": 3, + "title": [ + "Use Reference Parent" + ], + "referenceAttachesTo": { + "compatibleTypes": "->Data Product->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Layer Manager->Layers Manager->" + }, + "icon": "attractive", + "propertyNameAtParent": "layers", + "properties": [ + { + "name": "timeFrameScale", + "type": "node", + "childType": "Time Frame Scale" + } + ] + }, + { + "type": "Data Storage", + "menuItems": [ + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "sessionIndependentData", + "label": "Add Session Ind Data", + "relatedUiObject": "Session Independent Data", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Add UI Object", + "disableIfPropertyIsDefined": true, + "propertyToCheckFor": "sessionBasedData", + "label": "Add Session Based Data", + "relatedUiObject": "Session Based Data", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Storage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "icon": "data-storage", + "chainAttachesTo": { + "compatibleTypes": "->Network Node->" + }, + "propertyNameAtParent": "dataStorage", + "properties": [ + { + "name": "sessionIndependentData", + "type": "node", + "childType": "Session Independent Data", + "autoAdd": true + }, + { + "name": "sessionBasedData", + "type": "node", + "childType": "Session Based Data", + "autoAdd": true + } + ] + }, + { + "type": "Data Mining", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Task Manager", + "relatedUiObject": "Task Manager", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Mining", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "icon": "data-mining", + "chainAttachesTo": { + "compatibleTypes": "->Network Node->" + }, + "propertyNameAtParent": "dataMining", + "properties": [ + { + "name": "taskManagers", + "type": "array", + "childType": "Task Manager", + "autoAdd": true + } + ] + }, + { + "type": "Testing Environment", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Task Manager", + "relatedUiObject": "Task Manager", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Environment", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "icon": "testing-environment", + "chainAttachesTo": { + "compatibleTypes": "->Network Node->" + }, + "propertyNameAtParent": "testingEnvironment", + "properties": [ + { + "name": "taskManagers", + "type": "array", + "childType": "Task Manager", + "autoAdd": true + } + ] + }, + { + "type": "Production Environment", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Task Manager", + "relatedUiObject": "Task Manager", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Environment", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 2, + "icon": "production-environment", + "chainAttachesTo": { + "compatibleTypes": "->Network Node->" + }, + "propertyNameAtParent": "productionEnvironment", + "properties": [ + { + "name": "taskManagers", + "type": "array", + "childType": "Task Manager", + "autoAdd": true + } + ] + }, + { + "type": "Session Independent Data", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Single Market Data", + "relatedUiObject": "Single Market Data", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Storage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "session-independent-data", + "chainAttachesTo": { + "compatibleTypes": "->Data Storage->" + }, + "propertyNameAtParent": "sessionIndependentData", + "properties": [ + { + "name": "singleMarketData", + "type": "array", + "childType": "Single Market Data", + "autoAdd": true + } + ] + }, + { + "type": "Session Based Data", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Session Reference", + "relatedUiObject": "Session Reference", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Storage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "session-based-data", + "chainAttachesTo": { + "compatibleTypes": "->Data Storage->" + }, + "propertyNameAtParent": "sessionBasedData", + "properties": [ + { + "name": "sessionReference", + "type": "array", + "childType": "Session Reference", + "autoAdd": true + } + ] + }, + { + "type": "Session Reference", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Single Market Data", + "relatedUiObject": "Single Market Data", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Storage", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "isTitleAllwaysVisible": true, + "title": [ + "Use Reference Parent" + ], + "addLeftIcons": true, + "level": 3, + "icon": "session-reference", + "alternativeIcons": "Use Reference Parent", + "referenceAttachesTo": { + "compatibleTypes": "->Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Session Based Data->" + }, + "propertyNameAtParent": "sessionReference", + "properties": [ + { + "name": "singleMarketData", + "type": "array", + "childType": "Single Market Data", + "autoAdd": true + } + ] + }, + { + "type": "Single Market Data", + "menuItems": [ + { + "action": "Add UI Object", + "label": "Add Data Product", + "relatedUiObject": "Data Product", + "actionFunction": "payload.onMenuItemClick" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Single Market Data", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "isTitleAllwaysVisible": true, + "title": [ + "Use Reference Parent Parent Parent", + "Use Reference Parent" + ], + "icon": "single-market-data", + "referenceAttachesTo": { + "compatibleTypes": "->Market->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Session Independent Data->Session Reference->" + }, + "propertyNameAtParent": "singleMarketData", + "properties": [ + { + "name": "dataProducts", + "type": "array", + "childType": "Data Product", + "autoAdd": true + } + ] + }, + { + "type": "Data Product", + "menuItems": [ + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete Data Product", + "iconPathOn": "delete", + "iconPathOff": "delete", + "actionFunction": "payload.onMenuItemClick" + } + ], + "addLeftIcons": true, + "level": 3, + "icon": "data-product", + "isTitleAllwaysVisible": true, + "title": [ + "Use Reference Parent" + ], + "referenceAttachesTo": { + "compatibleTypes": "->Product Definition->" + }, + "chainAttachesTo": { + "compatibleTypes": "->Single Market Data->" + }, + "propertyNameAtParent": "dataProducts", + "properties": [] + } +] \ No newline at end of file diff --git a/Inter-Process-Files/ecosystem.json b/Inter-Process-Files/ecosystem.json new file mode 100644 index 000000000..d63dee25e --- /dev/null +++ b/Inter-Process-Files/ecosystem.json @@ -0,0 +1,773 @@ +{ + "dataMines": [ + { + "codeName": "AAMasters", + "displayName": "Masters", + "host": { + "url": "localhost", + "storage": "localStorage", + "container": "aamasters", + "accessKey": "09e890b76767ede3e10f0526b5036600a6b8d3faac540336e12b493fdb2aa9ed46f86094c34652f99fb6322e5f09806412f17725807f85e4dc4169bd22dd59f0b23aba5251f5544478a1aabf5a2dea5260e6eb0c29af3a788208af85025a02f94950437d501b91fd55b9d97b3d6ac3af4e1161321f78b73ca0fda524dc3a5d03" + }, + "bots": [ + { + "codeName": "AAOlivia", + "displayName": "Olivia", + "type": "Indicator Bot Instance", + "profilePicture": "Olivia.png", + "repo": "AAOlivia-Indicator-Bot", + "configFile": "this.bot.config.json", + "products": [ + { + "codeName": "Candles", + "displayName": "Candles", + "description": "Two sets of files per period that contains candles.", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersCandlesVolumes", + "moduleName": "Candles" + } + }, + { + "codeName": "Volumes", + "displayName": "Buy and Sell Volumes", + "description": "Two sets of files per period that contains buy and sell volumes.", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersCandlesVolumes", + "moduleName": "Volumes" + } + } + ] + }, + { + "codeName": "AATom", + "displayName": "Tom", + "type": "Indicator Bot Instance", + "profilePicture": "Tom.png", + "repo": "AATom-Indicator-Bot", + "configFile": "this.bot.config.json", + "products": [ + { + "codeName": "Candle-Stairs", + "displayName": "Candle-Stairs Patters", + "description": "Two sets of files per period that contains candle-stairs patterns.", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candle-Stairs/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candle-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candle-Stairs/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersStairsPatterns", + "moduleName": "CandleStairs" + } + }, + { + "codeName": "Volume-Stairs", + "displayName": "Volume-Stairs Patters", + "description": "Two sets of files per period that contains volume-stairs patterns.", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volume-Stairs/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volume-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volume-Stairs/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersStairsPatterns", + "moduleName": "VolumeStairs" + } + } + ] + }, + { + "codeName": "AAChris", + "displayName": "Chris", + "type": "Indicator Bot Instance", + "profilePicture": "Chris.png", + "repo": "AAChris-Indicator-Bot", + "configFile": "this.bot.config.json", + "products": [ + { + "codeName": "Bollinger-Bands", + "displayName": "Bollinger Bands", + "description": "Bollinger Bands", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersBollingerBands", + "moduleName": "BollingerBands" + } + } + ] + }, + { + "codeName": "AAJason", + "displayName": "Jason", + "type": "Trading Bot Instance", + "profilePicture": "jason.png", + "products": [ + { + "codeName": "Trading-Simulation", + "displayName": "Simulation", + "description": "Trading Simulation", + "storageAccount": "aajason", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Trading-Simulation/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Trading-Simulation/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Trading-Simulation/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "TradingSimulation" + } + }, + { + "codeName": "Simulation-Conditions", + "displayName": "Conditions", + "description": "Simulation Conditions", + "storageAccount": "aajason", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Conditions/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Conditions/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Conditions/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "Conditions" + } + }, + { + "codeName": "Simulation-Strategies", + "displayName": "Strategies", + "description": "Simulation Strategies", + "storageAccount": "aajason", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Strategies/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Strategies/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Strategies/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "Strategies" + } + }, + { + "codeName": "Simulation-Trades", + "displayName": "Trades", + "description": "Simulation Trades", + "storageAccount": "aajason", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Trades/Multi-Period-Market/@Period", + "fileName": "@BaseAsset_@QuotedAsset.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validTimeFrames": [ + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Trades/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@BaseAsset_@QuotedAsset.json", + "dataRange": { + "filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/@Session/Simulation-Trades/Multi-Period-Daily", + "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "Trades" + } + }, + { + "codeName": "Live-Trading-History", + "displayName": "Live", + "shortDisplayName": "Live", + "description": "Live Trading Execution.", + "dataSets": [ + { + "codeName": "Oficial History", + "type": "File Sequence", + "validTimeFrames": [ + "24-hs", + "12-hs", + "08-hs", + "06-hs", + "04-hs", + "03-hs", + "02-hs", + "01-hs", + "45-min", + "40-min", + "30-min", + "20-min", + "15-min", + "10-min", + "05-min", + "04-min", + "03-min", + "02-min", + "01-min" + ], + "filePath": "@DataMine/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/@Session/Trading-Process", + "fileName": "Execution.History.Live.@Sequence.json" + } + ], + "exchangeList": [ + { + "name": "Poloniex" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "SimulationExecution" + } + } + ] + } + ], + "plotters": [ + { + "displayName": "Plotters Candles & Volumes", + "codeName": "PlottersCandlesVolumes", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "Candles", + "moduleName": "Candles", + "description": "Plots traditional red and green candles.", + "profilePicture": "Candles.png", + "panels": [ + { + "codeName": "CandlePanel", + "moduleName": "CandlePanel", + "event": "Current Candle Changed" + } + ] + }, + { + "codeName": "Volumes", + "description": "Plots volumes separated in buy and sell.", + "moduleName": "Volumes", + "profilePicture": "Volumes.png", + "panels": [ + { + "codeName": "VolumePanel", + "moduleName": "VolumePanel", + "event": "Current Volume Changed" + } + ] + } + ], + "repo": "Plotters-Candles-Volumes", + "configFile": "this.plotter.config.json" + }, + { + "displayName": "Plotters Stairs Patterns", + "codeName": "PlottersStairsPatterns", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "CandleStairs", + "moduleName": "CandleStairs", + "description": "Plots Candle Stairs patterns.", + "profilePicture": "CandleStairs.png", + "panels": [] + }, + { + "codeName": "VolumeStairs", + "description": "Plots Volume Stairs patterns.", + "moduleName": "VolumeStairs", + "profilePicture": "VolumeStairs.png", + "panels": [] + } + ], + "repo": "Plotters-Stairs-Patterns", + "configFile": "this.plotter.config.json" + }, + { + "displayName": "Plotters Bollinger Bands", + "codeName": "PlottersBollingerBands", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "BollingerBands", + "moduleName": "BollingerBands", + "description": "Plots Bollinger Bands.", + "profilePicture": "BollingerBands.png", + "panels": [ + { + "codeName": "BollingerBandsPanel", + "moduleName": "BollingerBandsPanel", + "event": "Current Band Changed" + } + ] + } + ], + "repo": "Plotters-Bollinger-Bands", + "configFile": "this.plotter.config.json" + }, + { + "displayName": "Plotters Trading Simulation", + "codeName": "PlottersTradingSimulation", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "TradingSimulation", + "moduleName": "TradingSimulation", + "description": "Plots Bollinger Records.", + "profilePicture": "TradingSimulation.png", + "panels": [ + { + "codeName": "TradingSimulationPanel", + "moduleName": "TradingSimulationPanel", + "event": "Current Record Changed" + } + ] + }, + { + "codeName": "Conditions", + "moduleName": "Conditions", + "description": "Plots Simulation Conditions.", + "profilePicture": "Conditions.png", + "panels": [] + }, + { + "codeName": "Strategies", + "moduleName": "Strategies", + "description": "Plots Strategies.", + "profilePicture": "Strategies.png", + "panels": [] + }, + { + "codeName": "Trades", + "moduleName": "Trades", + "description": "Plots Trades.", + "profilePicture": "Trades.png", + "panels": [] + }, + { + "codeName": "SimulationExecution", + "moduleName": "SimulationExecution", + "description": "Plots Simulation Execution.", + "profilePicture": "SimulationExecution.png", + "panels": [ + { + "codeName": "SimulationExecutionPanel", + "moduleName": "SimulationExecutionPanel", + "event": "Current Record Changed" + } + ] + } + ], + "repo": "Plotters-Trading-Simulation", + "configFile": "this.plotter.config.json" + } + ] + } + ], + "hosts": [] +} \ No newline at end of file diff --git a/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/Commons.js b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/Commons.js new file mode 100644 index 000000000..10d791988 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/Commons.js @@ -0,0 +1,67 @@ +exports.newCommons = function newCommons(bot, logger, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "Commons"; + + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + const GMT_SECONDS = ':00.000 GMT+0000'; + + let thisObject = { + initializeStorage: initializeStorage + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + return thisObject; + + function initializeStorage(charlyStorage, bruceStorage, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initializeStorage -> Entering function."); } + + initializeBruceStorage(); + + function initializeBruceStorage() { + + bruceStorage.initialize(bot.dataMine, onBruceInizialized); + + function onBruceInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + initializeCharlyStorage(); + + } else { + logger.write(MODULE_NAME, "[ERROR] initializeStorage -> initializeBruceStorage -> onBruceInizialized -> err = " + err.stack); + callBackFunction(err); + } + } + } + + function initializeCharlyStorage() { + + charlyStorage.initialize(bot.dataMine, onCharlyInizialized); + + function onCharlyInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } else { + logger.write(MODULE_NAME, "[ERROR] initializeStorage -> initializeCharlyStorage -> onCharlyInizialized -> err = " + err.stack); + callBackFunction(err); + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] initializeStorage -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +}; \ No newline at end of file diff --git a/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/Single-Period-Daily/User.Bot.js b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/Single-Period-Daily/User.Bot.js new file mode 100644 index 000000000..a2f7345f1 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/Single-Period-Daily/User.Bot.js @@ -0,0 +1,813 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const GMT_SECONDS = ':00.000 GMT+0000'; + const GMT_MILI_SECONDS = '.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Candles"; + const CANDLES_ONE_MIN = "One-Min"; + + const VOLUMES_FOLDER_NAME = "Volumes"; + const VOLUMES_ONE_MIN = "One-Min"; + + thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let statusDependencies; + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + statusDependencies = pStatusDependencies; + + logger.fileName = MODULE_NAME + logger.initialize(); + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + + /* + This process is going to do the following: + Read the trades from Charly's Output and pack them into daily files with candles of one minute. + */ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let processDate = new Date((new Date()).getUTCFullYear() + "-" + ((new Date()).getUTCMonth() + 1) + "-1 00:00:00.000 GMT+0000"); + + let thisDatetime = new Date(); + + let market = bot.market; + + let lastTradeFileProcessed; // Datetime of the last file certified by the Hole Fixing process as without permanent holes. + let firstTradeFile; // Datetime of the first trade file in the whole market history. + let lastTradeFileSaved; // Datetime of the last verified file without holes. + let lastCandleClose; // Value of the last candle close. + let lastTradeFile; // Datetime pointing to the last Trade File sucessfuly processed and included in the last file. + let beginingOfMarket + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + + /* First Status Report */ + reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Historic-Trades" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.beginingOfMarket === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet."); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency does not exist." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS); + lastTradeFileSaved = new Date(thisReport.lastFileSaved.year + "-" + thisReport.lastFileSaved.month + "-" + thisReport.lastFileSaved.days + " " + thisReport.lastFileSaved.hours + ":" + thisReport.lastFileSaved.minutes + GMT_SECONDS); + + /* Final Status Report */ + + reportKey = "AAMasters" + "-" + "AABruce" + "-" + "Single-Period-Daily" + "-" + "dataSet.V1" + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile === undefined) { // Means this is the first time ever this process runs. + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey); + + lastCandleClose = 0; + lastTradeFileProcessed = new Date(firstTradeFile.valueOf() - ONE_DAY_IN_MILISECONDS) + beginingOfMarket = new Date(firstTradeFile.valueOf()) + + buildCandlesAndVolumes(); + + } else { + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey); + + lastTradeFileProcessed = new Date(thisReport.lastTradeFile.year + "-" + thisReport.lastTradeFile.month + "-" + thisReport.lastTradeFile.days + " " + "00:00" + GMT_SECONDS); + lastCandleClose = thisReport.candleClose; + beginingOfMarket = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS); + + if (beginingOfMarket.valueOf() !== firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(firstTradeFile.valueOf()) + lastTradeFileProcessed = new Date(firstTradeFile.valueOf() - ONE_DAY_IN_MILISECONDS) + lastCandleClose = 0 + buildCandlesAndVolumes() + return + } + + if (thisReport.fileComplete === true) { + buildCandlesAndVolumes(); + } else { + lastTradeFile = new Date(thisReport.lastTradeFile.year + "-" + thisReport.lastTradeFile.month + "-" + thisReport.lastTradeFile.days + " " + thisReport.lastTradeFile.hours + ":" + thisReport.lastTradeFile.minutes + GMT_SECONDS); + findPreviousContent(); + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function findPreviousContent() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> Entering function."); } + + let previousCandles; + let previousVolumes; + + getCandles(); + + function getCandles() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getCandles -> Entering function."); } + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let dateForPath = lastTradeFileProcessed.getUTCFullYear() + '/' + utilities.pad(lastTradeFileProcessed.getUTCMonth() + 1, 2) + '/' + utilities.pad(lastTradeFileProcessed.getUTCDate(), 2); + let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getCandles -> reading file at dateForPath = " + dateForPath); + + function onFileReceived(err, text) { + + let candlesFile; + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getCandles -> onFileReceived -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getCandles -> onFileReceived -> err = " + err.stack); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getCandles -> onFileReceived -> text = " + text); + } + + candlesFile = JSON.parse(text); + previousCandles = candlesFile; + getVolumes(); + + } catch (err) { + + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getCandles -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getCandles -> onFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[HINT] start -> findPreviousContent -> getCandles -> onFileReceived -> Empty or corrupt volume file found."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getVolumes() { + + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getVolumes -> Entering function."); } + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let dateForPath = lastTradeFileProcessed.getUTCFullYear() + '/' + utilities.pad(lastTradeFileProcessed.getUTCMonth() + 1, 2) + '/' + utilities.pad(lastTradeFileProcessed.getUTCDate(), 2); + let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getVolumes -> reading file at dateForPath = " + dateForPath); + + function onFileReceived(err, text) { + + let volumesFile; + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getVolumes -> onFileReceived -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getVolumes -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getVolumes -> onFileReceived -> text = " + text); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> getVolumes -> onFileReceived -> text = " + text); + } + + volumesFile = JSON.parse(text); + previousVolumes = volumesFile; + lastTradeFileProcessed = new Date(lastTradeFileProcessed.valueOf() - ONE_DAY_IN_MILISECONDS); // We know that after the next call a new day will be added. + buildCandlesAndVolumes(previousCandles, previousVolumes); + + } catch (err) { + + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getVolumes -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getVolumes -> onFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getVolumes -> onFileReceived -> text = " + text); + logger.write(MODULE_NAME, "[HINT] start -> findPreviousContent -> getVolumes -> onFileReceived -> Empty or corrupt volume file found."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> getVolumes -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildCandlesAndVolumes(previousCandles, previousVolumes) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> Entering function."); } + + /* + Here we are going to scan the trades files packing them in candles files every one day. + We need for this the last close value, bacause all candles that are empty of trades at the begining, they need to + have a valid open and close value. This was previously calculated before arriving to this function. + */ + + let canAddPrevious = true; + + try { + nextFile(); + + function nextFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> Entering function."); } + + lastTradeFileProcessed = new Date(lastTradeFileProcessed.valueOf() + ONE_DAY_IN_MILISECONDS); + + let date = new Date(lastTradeFileProcessed.valueOf() - 60 * 1000); + + if (date.valueOf() < firstTradeFile.valueOf()) { // At the special case where we are at the begining of the market, this might be true. + date = new Date(firstTradeFile.valueOf() - 60 * 1000); + } + + if (lastTradeFile !== undefined) { + date = new Date(lastTradeFile.valueOf()); + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> date = " + date); } + + let candles = []; + let volumes = []; + + if (previousCandles !== undefined && canAddPrevious === true) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> Adding Previous Candles. "); } + + for (let i = 0; i < previousCandles.length; i++) { + + let candle = { + open: previousCandles[i][2], + close: previousCandles[i][3], + min: previousCandles[i][0], + max: previousCandles[i][1], + begin: previousCandles[i][4], + end: previousCandles[i][5] + }; + + candles.push(candle); + } + } + + if (previousVolumes !== undefined && canAddPrevious === true) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> Adding Previous Volumes. "); } + + for (let i = 0; i < previousVolumes.length; i++) { + + let volume = { + begin: previousVolumes[i][2], + end: previousVolumes[i][3], + buy: previousVolumes[i][0], + sell: previousVolumes[i][1] + }; + + volumes.push(volume); + } + } + + canAddPrevious = false; // We add them only onece. + + nextDate(); + + function nextDate() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> nextDate -> Entering function."); } + + date = new Date(date.valueOf() + 60 * 1000); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> nextDate -> date = " + date); } + + /* Check if we are outside the current Day / File */ + + if (date.getUTCDate() !== lastTradeFileProcessed.getUTCDate()) { + + writeFiles(lastTradeFileProcessed, candles, volumes, true, onFilesWritten); + + return; + + function onFilesWritten() { + + nextFile(); + } + } + + /* Check if we have past the most recent hole fixed file */ + + if (date.valueOf() > lastTradeFileSaved.valueOf()) { + + writeFiles(lastTradeFileProcessed, candles, volumes, false, onFilesWritten); + return; + + function onFilesWritten() { + + if (FULL_LOG === true) { + logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> nextDate -> Head of the market reached for market " + market.baseAsset + '_' + market.quotedAsset + "."); + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + } + + readTrades(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> nextDate -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function readTrades() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> nextDate -> readTrades -> Entering function."); } + + lastTradeFile = new Date(date.valueOf()); + + let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + let filePathRoot = bot.dataMine + "/" + "AACharly" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + TRADES_FOLDER_NAME + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> nextDate -> readTrades -> reading file at dateForPath = " + dateForPath); + + function onFileReceived(err, text) { + + let tradesFile; + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> text = " + text); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> text = " + text); + } + + let candle = { + open: lastCandleClose, + close: lastCandleClose, + min: lastCandleClose, + max: lastCandleClose, + begin: date.valueOf(), + end: date.valueOf() + 60 * 1000 - 1 + }; + + let volume = { + begin: date.valueOf(), + end: date.valueOf() + 60 * 1000 - 1, + buy: 0, + sell: 0 + }; + + tradesFile = JSON.parse(text); + + let tradesCount = utilities.pad(tradesFile.length, 5); + + logger.write(MODULE_NAME, "[INFO] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> " + tradesCount + " trades found at " + filePath + " for market " + market.baseAsset + '_' + market.quotedAsset + ". "); + + if (tradesFile.length > 0) { + + /* Candle open and close Calculations */ + + candle.open = tradesFile[0][2]; + candle.close = tradesFile[tradesFile.length - 1][2]; + lastCandleClose = candle.close; + } + + for (let i = 0; i < tradesFile.length; i++) { + + let record = { + side: tradesFile[i][1], + price: tradesFile[i][2], + amount: tradesFile[i][3] + }; + + let trade = { + type: record.side, + rate: record.price, + amountA: record.amount, + amountB: record.amount * record.price + }; + + /* Candle min and max Calculations */ + + if (trade.rate < candle.min) { + candle.min = trade.rate; + } + + if (trade.rate > candle.max) { + candle.max = trade.rate; + } + + /* Volume Calculations */ + + if (trade.type === "sell") { + volume.sell = volume.sell + trade.amountA; + } else { + volume.buy = volume.buy + trade.amountA; + } + } + + candles.push(candle); + volumes.push(volume); + nextDate(); + + } catch (err) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> text = " + text); + logger.write(MODULE_NAME, "[HINT] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> onFileReceived -> Empty or corrupt volume file found."); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> readTrades -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> nextFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandlesAndVolumes -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFiles(date, candles, volumes, isFileComplete, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> Entering function."); } + + /* + Here we will write the contents of the Candles and Volumens files. If the File is declared as complete, we will also write the status report. + */ + + try { + + writeCandles(); + + function writeCandles() { + + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < candles.length; i++) { + + fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]"; + if (separator === "") { separator = ","; } + fileRecordCounter++; + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2); + let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> writing file at dateForPath = " + dateForPath); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> fileContent = " + fileContent); + } + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + ""); + + writeVolumes(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeVolumes() { + + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < volumes.length; i++) { + + /* Validation trying to find a BUG that changes the dates of rubish data. */ + + if (volumes[i].begin < 1262304000000 | volumes[i].end < 1262304000000) { // If it is storing a number that represents a date before the year 2010... + + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> Invalid Date trying to be recorded on output file. "); + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> volumes[i].begin = " + volumes[i].begin); + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> volumes[i].end = " + volumes[i].end); + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> i = " + i); + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> volumes = " + JSON.stringify(volumes)); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]"; + if (separator === "") { separator = ","; } + fileRecordCounter++; + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2); + let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> writing file at dateForPath = " + dateForPath); + + function onFileCreated(err) { + + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> fileContent = " + fileContent); + } + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + ""); + + writeReport(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeReport() { + + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeReport -> Entering function."); } + + writeStatusReport(date, lastTradeFile, lastCandleClose, isFileComplete, false, onStatusReportWritten); + + function onStatusReportWritten(err) { + + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeReport -> onStatusReportWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> onStatusReportWritten -> err = " + err.stack); + callBackFunction(err); + return; + } + + callBack(); + return; + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeReport -> onStatusReportWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(lastFileDate, lastTradeFile, candleClose, isFileComplete, isMonthComplete, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastTradeFile = " + lastTradeFile); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> candleClose = " + candleClose); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> isFileComplete = " + isFileComplete); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> isMonthComplete = " + isMonthComplete); } + + try { + + if (lastTradeFile === undefined) { + + /* At the begining of a new month, it can happen for reasons not 100% clear that this variable gets undifined. + Probably it is because the instance that was waiting to execute in coma state, wakes up before than Charly + writting the trades of this new month. Anyway, when this happens, a different path in code is taken and this + variable gets undefined. A workaround for that without further investigation of the real cause of the problem + is to RETRY the execution until the normal conditions arise and everything continues working well. That is what + we are going to do for now. */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] start -> writeStatusReport -> Cannot write the status report."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] start -> writeStatusReport -> lastTradeFile = undefined."); } + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + + return; + } + + let key = bot.dataMine + "-" + bot.codeName + "-" + bot.process + "-" + bot.dataSetVersion + let statusReport = statusDependencies.statusReports.get(key); + + statusReport.file = { + lastFile: { + year: lastFileDate.getUTCFullYear(), + month: (lastFileDate.getUTCMonth() + 1), + days: lastFileDate.getUTCDate() + }, + lastTradeFile: { + year: lastTradeFile.getUTCFullYear(), + month: (lastTradeFile.getUTCMonth() + 1), + days: lastTradeFile.getUTCDate(), + hours: lastTradeFile.getUTCHours(), + minutes: lastTradeFile.getUTCMinutes() + }, + beginingOfMarket: { + year: beginingOfMarket.getUTCFullYear(), + month: (beginingOfMarket.getUTCMonth() + 1), + days: beginingOfMarket.getUTCDate(), + hours: beginingOfMarket.getUTCHours(), + minutes: beginingOfMarket.getUTCMinutes() + }, + candleClose: candleClose, + fileComplete: isFileComplete + }; + + let fileContent = JSON.stringify(statusReport); + + statusReport.save(onSaved); + + function onSaved(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> onSaved -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack); + callBackFunction(err); + return; + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/package.json b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/package.json new file mode 100644 index 000000000..8aee0df58 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/package.json @@ -0,0 +1,13 @@ +{ + "name": "AABruce", + "version": "1.0.0", + "description": "Processes that get the order books from Poloniex.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Luis-Fernando-Molina", + "license": "ISC", + "dependencies": { + } +} diff --git a/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/this.bot.config.json b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/this.bot.config.json new file mode 100644 index 000000000..708d76f8b --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AABruce-Indicator-Bot/this.bot.config.json @@ -0,0 +1,143 @@ +{ + "displayName": "Bruce", + "codeName": "AABruce", + "type": "Indicator Bot", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "dataMine": "AAMasters", + "profilePicture": "Bruce.png", + "dataSetVersion": "dataSet.V1", + "processes": [ + { + "name": "Single-Period-Daily", + "description": "Produces two data-sets with candles and volumes.", + "startMode": { + "allMonths": { + "run": "true", + "minYear": "2019", + "maxYear": "2019" + }, + "oneMonth": { + "run": "false", + "year": "2018", + "month": "6" + }, + "noTime": { + "run": "false" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Historic-Trades", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Hole-Fixing", + "dataSetVersion": "dataSet.V1", + "dataSetSection": "Month", + "waitUntilNextUpdate": true + }, + { + "dataMine": "AAMasters", + "bot": "AABruce", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Single-Period-Daily", + "dataSetVersion": "dataSet.V1", + "dataSetSection": "Month" + } + ] + } + ], + "products": [ + { + "codeName": "Candles", + "displayName": "Candles", + "description": "01 Min Candles.", + "storageAccount": "aamasters", + "dataSets": [ + { + "codeName": "One-Min", + "type": "Daily Files", + "validPeriods": [ "01-min" ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/One-Min", + "fileName": "@AssetA_@AssetB.json" + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersCandlesVolumes", + "moduleName": "Candles" + } + }, + { + "codeName": "Volumes", + "displayName": "Buy and Sell Volumes", + "description": "01 Min Volumes.", + "storageAccount": "aamasters", + "dataSets": [ + { + "codeName": "One-Min", + "type": "Daily Files", + "validPeriods": [ "01-min" ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/One-Min", + "fileName": "@AssetA_@AssetB.json" + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersCandlesVolumes", + "moduleName": "Volumes" + } + } + ], + "storage": { + "sas": "?sv=2017-07-29&ss=b&srt=sco&sp=rl&se=2018-12-31T22:59:59Z&st=2018-04-01T12:59:54Z&spr=https&sig=Fre%2FUWrF9IAXNnJSOZ%2F5q5LUow9uno7dXg5pGZ3YDzo%3D", + "fileUri": "https://aamasters.blob.core.windows.net" + } +} + + + diff --git a/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Commons.js b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Commons.js new file mode 100644 index 000000000..271f6cbe2 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Commons.js @@ -0,0 +1,8 @@ +exports.newCommons = function newCommons(bot, logger, UTILITIES) { + + let thisObject = { + + }; + + return thisObject; +}; \ No newline at end of file diff --git a/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Historic-Trades/User.Bot.js b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Historic-Trades/User.Bot.js new file mode 100644 index 000000000..3e2470b0e --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Historic-Trades/User.Bot.js @@ -0,0 +1,500 @@ + +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage, STATUS_REPORT, EXCHANGE_API) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const GMT_SECONDS = ':00.000 GMT+0000'; + const GMT_MILI_SECONDS = '.000 GMT+0000'; + const MODULE_NAME = "User Bot"; + const TRADES_FOLDER_NAME = "Trades"; + + thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger) + let statusDependencies + + const ONE_MINUTE = 60000 + const MAX_TRADES_PER_EXECUTION = 100000 + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + const ccxt = require('ccxt') + + let allTrades = [] + let thisReport; + let since + let initialProcessTimestamp + let beginingOfMarket + let lastFileSaved + let exchangeId + let options = {} + let fetchType = "by Time" + let lastId + let firstId + let rateLimit + let exchange + let uiStartDate = new Date(bot.uiStartDate) + + const limit = 1000 + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + statusDependencies = pStatusDependencies; + + exchangeId = bot.exchange.toLowerCase() + + /* Applying the parameters defined by the user at the Exchange node */ + if (bot.exchangeNode.code.API !== undefined) { + for (let i = 0; i < bot.exchangeNode.code.API.length; i++) { + if (bot.exchangeNode.code.API[i].method === 'fetchTrades') { + if (bot.exchangeNode.code.API[i].class !== undefined) { + exchangeId = bot.exchangeNode.code.API[i].class + } + if (bot.exchangeNode.code.API[i].fetchType !== undefined) { + fetchType = bot.exchangeNode.code.API[i].fetchType + } + if (bot.exchangeNode.code.API[i].fetchTradesMethod !== undefined) { + options = { + 'fetchTradesMethod': bot.exchangeNode.code.API[i].fetchTradesMethod + } + } + if (bot.exchangeNode.code.API[i].firstId !== undefined) { + firstId = bot.exchangeNode.code.API[i].firstId + } + if (bot.exchangeNode.code.API[i].rateLimit !== undefined) { + rateLimit = bot.exchangeNode.code.API[i].rateLimit + } + } + } + } + + let key = process.env.KEY + let secret = process.env.SECRET + + if (key === "undefined") { key = undefined } + if (secret === "undefined") { secret = undefined } + + + const exchangeClass = ccxt[exchangeId] + const exchangeConstructorParams = { + 'apiKey': key, + 'secret': secret, + 'timeout': 30000, + 'enableRateLimit': true, + verbose: false, + options: options + } + if (rateLimit !== undefined) { + exchangeConstructorParams.rateLimit = rateLimit + } + + exchange = new exchangeClass(exchangeConstructorParams) + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(callBackFunction) { + try { + + if (global.STOP_TASK_GRACEFULLY === true) { + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + + let abort = false + begin() + + async function begin() { + + getContextVariables() + if (abort === true) { return } + await getFirstId() + await getTrades() + if (abort === true) { return} + if (global.STOP_TASK_GRACEFULLY === true) { + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + await saveTrades() + } + + function getContextVariables() { + + try { + let reportKey; + + reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Historic-Trades" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey) + + if (thisReport.file.beginingOfMarket !== undefined) { // This means this is not the first time this process run. + beginingOfMarket = new Date(thisReport.file.beginingOfMarket.year + "-" + thisReport.file.beginingOfMarket.month + "-" + thisReport.file.beginingOfMarket.days + " " + thisReport.file.beginingOfMarket.hours + ":" + thisReport.file.beginingOfMarket.minutes + GMT_SECONDS); + lastFileSaved = new Date(thisReport.file.lastFileSaved.year + "-" + thisReport.file.lastFileSaved.month + "-" + thisReport.file.lastFileSaved.days + " " + thisReport.file.lastFileSaved.hours + ":" + thisReport.file.lastFileSaved.minutes + GMT_SECONDS); + lastId = thisReport.file.lastId + } else { // This means this is the first time this process run. + beginingOfMarket = new Date(uiStartDate.valueOf()) + } + + defineSince() + function defineSince() { + if (thisReport.file.uiStartDate === undefined) { + thisReport.file.uiStartDate = uiStartDate + } else { + thisReport.file.uiStartDate = new Date(thisReport.file.uiStartDate) + } + + if (uiStartDate.valueOf() !== thisReport.file.uiStartDate.valueOf()) { + since = uiStartDate.valueOf() + initialProcessTimestamp = since + beginingOfMarket = new Date(uiStartDate.valueOf()) + } else { + if (lastFileSaved !== undefined) { + since = lastFileSaved.valueOf() + initialProcessTimestamp = lastFileSaved.valueOf() + } else { + since = uiStartDate.valueOf() + initialProcessTimestamp = uiStartDate.valueOf() + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot Status Dependencies. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + abort = true + } + } + + async function getFirstId() { + try { + /* We need the first id only when we are going to fetch trades based on id and it is the first time the process runs*/ + if (fetchType !== "by Id") { return } + if (lastId !== undefined) { return } + + const limit = 1 + const exchangeClass = ccxt[exchangeId] + const exchange = new exchangeClass({ + 'timeout': 30000, + 'enableRateLimit': true, + verbose: false + }) + + const trades = await exchange.fetchTrades(symbol, since, limit, undefined) + + let lastRecord = trades[trades.length - 1] + lastId = lastRecord.info[firstId] + } catch (err) { + /* If something fails trying to get an id close to since, we just will continue without an id.*/ + } + } + + async function getTrades() { + + try { + let lastTradeKey = '' + let params = undefined + let previousSince + + while (true) { + + /* Reporting we are doing well */ + let processingDate = new Date(since) + processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2); + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Fetching Trades @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit ) } + bot.processHeartBeat("Fetching " + allTrades.length.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well + + if (fetchType === "by Id") { + params = { + 'fromId': lastId + } + since = undefined + } + + /* Fetching the trades from the exchange.*/ + const trades = await exchange.fetchTrades(symbol, since, limit, params) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched = " + trades.length) } + if (trades.length > 0) { + let beginDate = new Date(trades[0].timestamp) + let endDate = new Date(trades[trades.length - 1].timestamp) + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched From " + beginDate) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched to " + endDate) } + } + + if (trades.length > 1) { + previousSince = since + since = trades[trades.length - 1]['timestamp'] + if (since === previousSince) { + since++ // this prevents requesting in a loop trades with the same timestamp, that can happen when all the records fetched comes with exactly the same timestamp. + } + + lastId = trades[trades.length - 1]['id'] + + for (let i = 0; i < trades.length; i++) { + let trade = trades[i] + let tradeKey = trade.id + '-' + trade.timestamp + '-' + trade.side + '-' + trade.price.toFixed(16) + '-' + trade.amount.toFixed(16) + if (tradeKey !== lastTradeKey) { + allTrades.push([trade.timestamp, trade.side, trade.price, trade.amount, trade.id]) + } + lastTradeKey = tradeKey + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Fetching Trades @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit) } + bot.processHeartBeat("Fetching " + allTrades.length.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well + + } + + if ( + trades.length < limit - 1 || + global.STOP_TASK_GRACEFULLY === true || + allTrades.length >= MAX_TRADES_PER_EXECUTION + ) { + break + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getTrades -> Retrying Later -> err = " + err.stack); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + abort = true + } + } + + async function saveTrades() { + + try { + + let fileContent = '[' + let previousRecordMinute = Math.trunc((initialProcessTimestamp - ONE_MINUTE) / ONE_MINUTE) + let currentRecordMinute + let needSeparator = false + let error + let separator + let heartBeatCounter = 0 + + let i = -1 + lastId = undefined + controlLoop() + + function loop() { + + let filesToCreate = 0 + let filesCreated = 0 + + let record = allTrades[i] + let trade = { + timestamp: record[0], + side: record[1], + price: record[2], + amount: record[3] + } + + if (lastId === undefined) { + lastId = record[4] + } + + let processingDate = new Date(trade.timestamp) + processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2); + + /* Reporting we are doing well */ + heartBeatCounter-- + if (heartBeatCounter <= 0) { + heartBeatCounter = 1000 + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveTrades -> Saving Trades @ " + processingDate + " -> i = " + i + " -> total = " + allTrades.length) } + bot.processHeartBeat("Saving " + i.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well + } + + /* Saving the trades in Files*/ + currentRecordMinute = Math.trunc(trade.timestamp / ONE_MINUTE) + + if ( + currentRecordMinute !== previousRecordMinute + ) { + /* There are no more trades at this minute or it is the last trade, so we save the file.*/ + saveFile(previousRecordMinute) + lastId = undefined + } + + if (needSeparator === false) { + needSeparator = true; + separator = ''; + } else { + separator = ','; + } + + /* Add the trade to the file content.*/ + fileContent = fileContent + separator + '[' + trade.timestamp + ',"' + trade.side + '",' + trade.price + ',' + trade.amount + ']'; + + if (i === allTrades.length - 1) { + /* This is the last trade, so we save the file.*/ + saveFile(currentRecordMinute) + /* It might happen that there are several minutes after the last trade without trades. We need to record empty files for them.*/ + if (allTrades.length < MAX_TRADES_PER_EXECUTION) { + let currentTimeMinute = Math.trunc((new Date()).valueOf() / ONE_MINUTE) + if (currentTimeMinute - currentRecordMinute > 1) { + createMissingEmptyFiles(currentRecordMinute, currentTimeMinute) + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveTrades -> Saving Trades @ " + processingDate + " -> i = " + i + " -> total = " + allTrades.length) } + bot.processHeartBeat("Saving " + i.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well + + return + } + previousRecordMinute = currentRecordMinute + if (error) { + callBackFunction(error); + return; + } + if (filesToCreate === 0) { + controlLoop() + } + function saveFile(minute) { + fileContent = fileContent + ']' + if (currentRecordMinute - previousRecordMinute > 1) { + createMissingEmptyFiles(previousRecordMinute, currentRecordMinute) + } + let fileName = bot.market.baseAsset + '_' + bot.market.quotedAsset + '.json' + if (previousRecordMinute >= initialProcessTimestamp / ONE_MINUTE) { + filesToCreate++ + fileStorage.createTextFile(getFilePath(minute * ONE_MINUTE) + '/' + fileName, fileContent + '\n', onFileCreated); + } + fileContent = '[' + needSeparator = false + + } + function createMissingEmptyFiles(begin, end) { + + /* + If this range is too wide, we will consider this means that the begin is before the begining of this market at this exchange. + In that case we will change the begin and the beginingOfMarket + */ + + if ((end - begin) / 60 / 24 > 7) { + begin = end + beginingOfMarket = new Date(end * ONE_MINUTE) + } + + for (let j = begin + 1; j < end; j++) { + let fileName = bot.market.baseAsset + '_' + bot.market.quotedAsset + '.json' + filesToCreate++ + fileStorage.createTextFile(getFilePath(j * ONE_MINUTE) + '/' + fileName, "[]" + '\n', onFileCreated); + + } + } + function onFileCreated(err) { + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> tradesReadyToBeSaved -> onFileBCreated -> err = " + JSON.stringify(err)); + error = err // This allows the loop to be breaked. + return; + } + filesCreated++ + lastFileSaved = new Date((currentRecordMinute * ONE_MINUTE)) + if (filesCreated === filesToCreate) { + controlLoop() + } + } + function getFilePath(timestamp) { + let datetime = new Date(timestamp) + let dateForPath = datetime.getUTCFullYear() + '/' + + utilities.pad(datetime.getUTCMonth() + 1, 2) + '/' + + utilities.pad(datetime.getUTCDate(), 2) + '/' + + utilities.pad(datetime.getUTCHours(), 2) + '/' + + utilities.pad(datetime.getUTCMinutes(), 2) + let filePath = bot.filePathRoot + "/Output/" + TRADES_FOLDER_NAME + '/' + dateForPath; + return filePath + } + + + } + function controlLoop() { + if (global.STOP_TASK_GRACEFULLY === true) { + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + + i++ + if (i < allTrades.length) { + setImmediate(loop) + } else { + writeStatusReport() + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> saveTrades -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + abort = true + } + } + + function writeStatusReport() { + try { + if (lastFileSaved === undefined) {return} + thisReport.file = { + lastFileSaved: { + year: lastFileSaved.getUTCFullYear(), + month: (lastFileSaved.getUTCMonth() + 1), + days: lastFileSaved.getUTCDate(), + hours: lastFileSaved.getUTCHours(), + minutes: lastFileSaved.getUTCMinutes() + }, + beginingOfMarket: { + year: beginingOfMarket.getUTCFullYear(), + month: (beginingOfMarket.getUTCMonth() + 1), + days: beginingOfMarket.getUTCDate(), + hours: beginingOfMarket.getUTCHours(), + minutes: beginingOfMarket.getUTCMinutes() + }, + uiStartDate: uiStartDate.toUTCString() + }; + + if (fetchType === "by Id") { + thisReport.file.lastId = lastId + } + + thisReport.save(onSaved); + + function onSaved(err) { + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack); + callBackFunction(err); + return; + } + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/package-lock.json b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/package-lock.json new file mode 100644 index 000000000..1306b696d --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "AACharly", + "version": "1.0.0", + "lockfileVersion": 1 +} diff --git a/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/package.json b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/package.json new file mode 100644 index 000000000..446c6b8b6 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/package.json @@ -0,0 +1,12 @@ +{ + "name": "AACharly", + "version": "1.0.0", + "description": "Processes that get the trades from Poloniex.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Luis-Fernando-Molina", + "license": "ISC", + "dependencies": {} +} diff --git a/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/this.bot.config.json b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/this.bot.config.json new file mode 100644 index 000000000..88e4476c6 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/this.bot.config.json @@ -0,0 +1,180 @@ +{ + "displayName": "Charly", + "codeName": "AACharly", + "type": "Sensor Bot", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "dataMine": "AAMasters", + "profilePicture": "Charly.png", + "dataSetVersion": "dataSet.V1", + "processes": [ + { + "name": "Live-Trades", + "description": "Retrieves the trades done at the current and the previous minute and saves them at the storage account.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "false" + }, + "fixedInterval": { + "run": "true", + "interval": 60000 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 60000, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Live-Trades", + "dataSetVersion": "dataSet.V1" + } + ] + }, + { + "name": "Historic-Trades", + "description": "Retrieves and saves the historical trades in batches going backwards from the current time until reaching the begining of the market.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "2014", + "maxYear": "2020" + }, + "oneMonth": { + "run": "true", + "year": "2018", + "month": "01" + }, + "noTime": { + "run": "false" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 60000, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Live-Trades", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Historic-Trades", + "dataSetVersion": "dataSet.V1" + } + ] + }, + { + "name": "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.", + "startMode": { + "allMonths": { + "run": "true", + "minYear": "2019", + "maxYear": "2019" + }, + "oneMonth": { + "run": "false", + "year": "2019", + "month": "10" + }, + "noTime": { + "run": "false" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Live-Trades", + "dataSetVersion": "dataSet.V1", + "waitUntilNextUpdate": true + }, + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Historic-Trades", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Hole-Fixing", + "dataSetVersion": "dataSet.V1", + "dataSetSection": "Month" + }, + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Hole-Fixing", + "dataSetVersion": "dataSet.V1" + } + ] + } + ] +} + + + diff --git a/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/Multi-Period-Daily/User.Bot.js b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/Multi-Period-Daily/User.Bot.js new file mode 100644 index 000000000..b773a7052 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/Multi-Period-Daily/User.Bot.js @@ -0,0 +1,886 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) { + + const FULL_LOG = true; + const INTENSIVE_LOG = false; + const LOG_FILE_CONTENT = false; + + const GMT_SECONDS = ':00.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Candles"; + const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands"; + const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth"; + + thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let statusDependencies; + let beginingOfMarket + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + + This process is going to do the following: + + Read the candles from Olivia and produce daily files with bollinger bands. + + */ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = bot.market; + + /* Context Variables */ + + let contextVariables = { + lastBandFile: undefined, // Datetime of the last file files sucessfully produced by this process. + firstTradeFile: undefined, // Datetime of the first trade file in the whole market history. + maxBandFile: undefined // Datetime of the last file available to be used as an input of this process. + }; + + let previousDay; // Holds the date of the previous day relative to the processing date. + let processDate; // Holds the processing date. + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + let statusReport; + + /* We look first for Charly in order to get when the market starts. */ + + reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Historic-Trades" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.beginingOfMarket === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet."); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency does not exist." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS); + + /* Second, we get the report from Olivia, to know when the marted ends. */ + + reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.maxBandFile = new Date(thisReport.lastFile.valueOf()); + + /* Finally we get our own Status Report. */ + + reportKey = "AAMasters" + "-" + "AAChris" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile !== undefined) { + + beginingOfMarket = new Date(thisReport.beginingOfMarket); + + if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastBandFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastBandFile = new Date(contextVariables.lastBandFile.valueOf() + ONE_DAY_IN_MILISECONDS); + + buildBands(); + return; + } + + contextVariables.lastBandFile = new Date(thisReport.lastFile); + + /* + The bands objects needs to be calculated over more than one day in some situations. In order to simplify the calculations we will + always load the last 2 days of candles, and run the calculations on that array. + + 1. So first we will load the candles file of processDay -1. + 2. Secondly we will load the candles file of processDay. + + After reading these two files we will add all candles to a unique array. + */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile !== undefined"); } + + buildBands(); + return; + + } else { + + /* + In the case when there is no status report, we take the date of the file with the first trades as the begining of the market. Then we will + go one day further in time, so that the previous day does fine a file at the begining of the market. + */ + + contextVariables.lastBandFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastBandFile = new Date(contextVariables.lastBandFile.valueOf() + ONE_DAY_IN_MILISECONDS); + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile === undefined"); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> contextVariables.lastBandFile = " + contextVariables.lastBandFile); } + + buildBands(); + return; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildBands() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> Entering function."); } + + let n; + processDate = new Date(contextVariables.lastBandFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> processDate = " + processDate); } + + advanceTime(); + + function advanceTime() { + + try { + + logger.newInternalLoop(bot.codeName, bot.process); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> Entering function."); } + + processDate = new Date(processDate.valueOf() + ONE_DAY_IN_MILISECONDS); + previousDay = new Date(processDate.valueOf() - ONE_DAY_IN_MILISECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> processDate = " + processDate); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> previousDay = " + previousDay); } + + /* Validation that we are not going past the head of the market. */ + + if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) { + + const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> " + logText); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + periodsLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> advanceTime -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible timeFrames. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function loopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> Entering function."); } + + const timeFrame = global.dailyFilePeriods[n][1]; + + let candles = []; // Here we will put all the candles of the 2 files read. + + let previousDayFile; + let processDayFile; + + getPreviousDayFile(); + + function getPreviousDayFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> Entering function."); } + + let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onCurrentDayFileReceived); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> text = " + text); } + + previousDayFile = JSON.parse(text); + getProcessDayFile() + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getProcessDayFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> Entering function."); } + + let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onCurrentDayFileReceived); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> text = " + text); } + + processDayFile = JSON.parse(text); + buildBands(); + + } catch (err) { + + if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) { + + processDayFile = []; // we are past the head of the market, then no worries if this file is non existent. + buildBands(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildBands() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> buildBands -> Entering function."); } + + addCandlesToSingleArray(previousDayFile); + addCandlesToSingleArray(processDayFile); + calculateBands(); + + function addCandlesToSingleArray(candlesFile) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> buildBands -> addCandlesToSingleArray -> Entering function."); } + + for (let i = 0; i < candlesFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = candlesFile[i][0]; + candle.max = candlesFile[i][1]; + + candle.open = candlesFile[i][2]; + candle.close = candlesFile[i][3]; + + candle.begin = candlesFile[i][4]; + candle.end = candlesFile[i][5]; + + if (candle.open > candle.close) { candle.direction = 'down'; } + if (candle.open < candle.close) { candle.direction = 'up'; } + if (candle.open === candle.close) { candle.direction = 'side'; } + + candles.push(candle); + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> addCandlesToSingleArray -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function calculateBands() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> calculateBands -> Entering function."); } + + let bandsArray = []; + let pBArray = []; + let numberOfPeriodsBB = 20; + let numberOfStandardDeviations = 2; + let numberOfPeriodsPB = 10; + + /* Building bands */ + + let band; + + for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them. + + /* Calculating the bollinger bands. */ + + let movingAverage = 0; + for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the moving average. + movingAverage = movingAverage + candles[j].close; + } + movingAverage = movingAverage / numberOfPeriodsBB; + + let standardDeviation = 0; + for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the standard deviation. + standardDeviation = standardDeviation + Math.pow(candles[j].close - movingAverage, 2); + } + standardDeviation = standardDeviation / numberOfPeriodsBB; + standardDeviation = Math.sqrt(standardDeviation); + if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later. + + band = { + begin: candles[i].begin, + end: candles[i].end, + movingAverage: movingAverage, + standardDeviation: standardDeviation, + deviation: standardDeviation * numberOfStandardDeviations + }; + + /* Will only add to the array the bands of the current day */ + + if (band.begin >= processDate.valueOf()) { bandsArray.push(band); } + + /* Calculating %B */ + + let lowerBB; + let upperBB; + + lowerBB = band.movingAverage - band.deviation; + upperBB = band.movingAverage + band.deviation; + + let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100; + + /* Moving Average Calculation */ + + let numberOfPreviousPeriods; + let currentPosition = pBArray.length; + + if (currentPosition < numberOfPeriodsPB) { // Avoinding to get into negative array indexes + numberOfPreviousPeriods = currentPosition; + } else { + numberOfPreviousPeriods = numberOfPeriodsPB; + } + + movingAverage = 0; + for (let j = currentPosition - numberOfPreviousPeriods; j < currentPosition; j++) { // go through the last numberOfPeriodsPBs to calculate the moving average. + movingAverage = movingAverage + pBArray[j].value; + } + movingAverage = movingAverage + value; + movingAverage = movingAverage / (numberOfPreviousPeriods + 1); + + let bandwidth = (upperBB - lowerBB) / band.movingAverage; + + let percentageBandwidth = { + begin: candles[i].begin, + end: candles[i].end, + value: value, + movingAverage: movingAverage, + bandwidth: bandwidth + }; + + /* Will only add to the array the pBs of the current day */ + + if (percentageBandwidth.begin >= processDate.valueOf()) { pBArray.push(percentageBandwidth); } + } + + writeBandsFile(bandsArray, pBArray); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> calculateBands -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function writeBandsFile(pBands, pPB) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < pBands.length; i++) { + + let band = pBands[i]; + + fileContent = fileContent + separator + '[' + + band.begin + "," + + band.end + "," + + band.movingAverage + "," + + band.standardDeviation + "," + + band.deviation + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + BOLLINGER_BANDS_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> Entering function."); } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> Content written = " + fileContent); + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + ""; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> " + logText); } + + writePBFile(pPB); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writePBFile(pPercentageBandwidths) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < pPercentageBandwidths.length; i++) { + + let pB = pPercentageBandwidths[i]; + + fileContent = fileContent + separator + '[' + + pB.begin + "," + + pB.end + "," + + pB.value + "," + + pB.movingAverage + "," + + pB.bandwidth + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> Entering function."); } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> Content written = " + fileContent); + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + ""; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> " + logText); } + + controlLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function controlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> Entering function."); } + + n++; + + if (n < global.dailyFilePeriods.length) { + + loopBody(); + + } else { + + n = 0; + + writeDataRanges(onWritten); + + function onWritten(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> onWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> controlLoop -> onWritten -> err = " + err.stack); + callBackFunction(err); + return; + } + + writeStatusReport(processDate, advanceTime); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> onWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> err.message = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeDataRanges(callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + writeDataRange(contextVariables.firstTradeFile, processDate, BOLLINGER_BANDS_FOLDER_NAME, onBandsBandsDataRangeWritten); + + function onBandsBandsDataRangeWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> err = " + err.stack); + callBack(err); + return; + } + + writeDataRange(contextVariables.firstTradeFile, processDate, PERCENTAGE_BANDWIDTH_FOLDER_NAME, onPercentageBandwidthDataRangeWritten); + + function onPercentageBandwidthDataRangeWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> onPercentageBandwidthDataRangeWritten -> err = " + err.stack); + callBack(err); + return; + } + + callBack(global.DEFAULT_OK_RESPONSE); + + } + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + } + + function writeDataRange(pBegin, pEnd, pProductFolder, callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); } + + let dataRange = { + begin: pBegin.valueOf(), + end: pEnd.valueOf() + }; + + let fileContent = JSON.stringify(dataRange); + + let fileName = 'Data.Range.' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process; + + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack); + callBack(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent); + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(lastFileDate, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); } + + try { + + let reportKey = "AAMasters" + "-" + "AAChris" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.file.lastFile = lastFileDate; + thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString() + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err.message = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/Multi-Period-Market/User.Bot.js b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/Multi-Period-Market/User.Bot.js new file mode 100644 index 000000000..3f0742040 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/Multi-Period-Market/User.Bot.js @@ -0,0 +1,488 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "User Bot"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Candles"; + const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands"; + const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth"; + + thisObject = { + initialize: initialize, + start: start + }; + + let statusDependencies; + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + + This process is going to do the following: + + Read the candles from Olivia and produce the bollinger bands out of them. + + */ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = bot.market; + + buildBands(); + + function buildBands() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> Entering function."); } + + try { + + let n; + + periodsLoop(); + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible periods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function loopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> Entering function."); } + + const outputPeriod = global.marketFilesPeriods[n][0]; + const timeFrame = global.marketFilesPeriods[n][1]; + + nextCandleFile(); + + function nextCandleFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> Entering function."); } + + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack); + callBackFunction(err); + return; + + } + + let marketFile = JSON.parse(text); + + let candles = []; + + buildCandles(); + + function buildCandles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> Entering function."); } + + try { + + for (let i = 0; i < marketFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = marketFile[i][0]; + candle.max = marketFile[i][1]; + + candle.open = marketFile[i][2]; + candle.close = marketFile[i][3]; + + candle.begin = marketFile[i][4]; + candle.end = marketFile[i][5]; + + candles.push(candle); + + } + + buildBands(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildBands() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> Entering function."); } + + let bandsArray = []; + let pBArray = []; + let numberOfPeriodsBB = 20; + let numberOfStandardDeviations = 2; + let numberOfPeriodsPB = 5; + + /* Building bands */ + + let band; + + for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them. + + let movingAverage = 0; + for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the moving average. + movingAverage = movingAverage + candles[j].close; + } + movingAverage = movingAverage / numberOfPeriodsBB; + + let standardDeviation = 0; + for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the standard deviation. + standardDeviation = standardDeviation + Math.pow (candles[j].close - movingAverage, 2); + } + standardDeviation = standardDeviation / numberOfPeriodsBB; + standardDeviation = Math.sqrt(standardDeviation); + if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later. + + band = { + begin: candles[i].begin, + end: candles[i].end, + movingAverage: movingAverage, + standardDeviation: standardDeviation, + deviation: standardDeviation * numberOfStandardDeviations + }; + + bandsArray.push(band); + + /* Calculating %B */ + + let lowerBB; + let upperBB; + + lowerBB = band.movingAverage - band.deviation; + upperBB = band.movingAverage + band.deviation; + + let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100; + + /* Moving Average Calculation */ + + let numberOfPreviousPeriods; + let currentPosition = pBArray.length; + + if (currentPosition < numberOfPeriodsPB) { // Avoinding to get into negative array indexes + numberOfPreviousPeriods = currentPosition; + } else { + numberOfPreviousPeriods = numberOfPeriodsPB; + } + + movingAverage = 0; + for (let j = currentPosition - numberOfPreviousPeriods; j < currentPosition; j++) { // go through the last numberOfPeriodsPBs to calculate the moving average. + movingAverage = movingAverage + pBArray[j].value; + } + movingAverage = movingAverage + value; + movingAverage = movingAverage / (numberOfPreviousPeriods + 1); + + let bandwidth = (upperBB- lowerBB) / band.movingAverage; + + let percentageBandwidth = { + begin: candles[i].begin, + end: candles[i].end, + value: value, + movingAverage: movingAverage, + bandwidth: bandwidth + }; + + pBArray.push(percentageBandwidth); + } + + writeBandsFile(bandsArray, pBArray); + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeBandsFile(pBands, pPB) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < pBands.length; i++) { + + let band = pBands[i]; + + fileContent = fileContent + separator + '[' + + band.begin + "," + + band.end + "," + + band.movingAverage + "," + + band.standardDeviation + "," + + band.deviation + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + BOLLINGER_BANDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writePBFile(pPB); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + function writePBFile(pPercentageBandwidths) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < pPercentageBandwidths.length; i++) { + + let pB = pPercentageBandwidths[i]; + + fileContent = fileContent + separator + '[' + + pB.begin + "," + + pB.end + "," + + pB.value + "," + + pB.movingAverage + "," + + pB.bandwidth + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + controlLoop(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function controlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> Entering function."); } + + n++; + + if (n < global.marketFilesPeriods.length) { + + loopBody(); + + } else { + + writeStatusReport(callBackFunction); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> controlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + + try { + + let reportKey = "AAMasters" + "-" + "AAChris" + "-" + "Multi-Period-Market" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/package.json b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/package.json new file mode 100644 index 000000000..11cc6a7f1 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/package.json @@ -0,0 +1,13 @@ +{ + "name": "AAToma", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Luis-Fernando-Molina", + "license": "ISC", + "dependencies": { + } +} diff --git a/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/this.bot.config.json b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/this.bot.config.json new file mode 100644 index 000000000..4252b243a --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAChris-Indicator-Bot/this.bot.config.json @@ -0,0 +1,232 @@ +{ + "displayName": "Chris", + "codeName": "AAChris", + "type": "Indicator Bot", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "dataMine": "AAMasters", + "profilePicture": "Chris.png", + "dataSetVersion": "dataSet.V1", + "processes": [ + { + "name": "Multi-Period-Market", + "description": "Produces a single file for each Time Frame covering all the market.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "updatesDatasets": [ + { + "product": "Bollinger-Bands", + "dataSet": "Multi-Period-Market" + }, + { + "product": "Percentage-Bandwidth", + "dataSet": "Multi-Period-Market" + } + ], + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AAChris", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Market", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AAOlivia", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Market", + "dataSetVersion": "dataSet.V1", + "waitUntilNextUpdate": true + } + ] + }, + { + "name": "Multi-Period-Daily", + "description": "Produces a Daily File at each Time Frame.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "updatesDatasets": [ + { + "product": "Bollinger-Bands", + "dataSet": "Multi-Period-Daily" + }, + { + "product": "Percentage-Bandwidth", + "dataSet": "Multi-Period-Daily" + } + ], + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Historic-Trades", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AAOlivia", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Daily", + "dataSetVersion": "dataSet.V1", + "waitUntilNextUpdate": true + }, + { + "dataMine": "AAMasters", + "bot": "AAChris", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Daily", + "dataSetVersion": "dataSet.V1" + } + ] + } + ], + "products": [ + { + "codeName": "Bollinger-Bands", + "displayName": "Bollinger Bands", + "description": "Bollinger Bands", + "storageAccount": "aachris", + "shareWith": "Public", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ], + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersBollingerBands", + "moduleName": "BollingerBands" + } + }, + { + "codeName": "Percentage-Bandwidth", + "displayName": "Percentage Bandwidth", + "description": "Percentage Bandwidth", + "storageAccount": "aachris", + "shareWith": "Public", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ], + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AAChris.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersBollingerBands", + "moduleName": "PercentageBandwidth" + } + } + ] +} + + + diff --git a/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/Multi-Period-Daily/User.Bot.js b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/Multi-Period-Daily/User.Bot.js new file mode 100644 index 000000000..9a1356a72 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/Multi-Period-Daily/User.Bot.js @@ -0,0 +1,792 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const GMT_SECONDS = ':00.000 GMT+0000'; + const GMT_MILI_SECONDS = '.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Candles"; + const CANDLES_ONE_MIN = "One-Min"; + + const VOLUMES_FOLDER_NAME = "Volumes"; + const VOLUMES_ONE_MIN = "One-Min"; + + thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let statusDependencies; + let beginingOfMarket + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +/* + +This process is going to do the following: + +Read the candles and volumes from Bruce and produce a file for each day and for each period with candles and volumes. + +*/ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = bot.market; + + /* Context Variables */ + + let contextVariables = { + lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process. + firstTradeFile: undefined, // Datetime of the first trade file in the whole market history. + maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process. + }; + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + let statusReport; + + /* We look first for Charly in order to get when the market starts. */ + + reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Historic-Trades" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.beginingOfMarket === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet."); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency does not exist." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS); + + /* Second, we get the report from Bruce, to know when the marted ends. */ + + reportKey = "AAMasters" + "-" + "AABruce" + "-" + "Single-Period-Daily" + "-" + "dataSet.V1" + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + "00:00" + GMT_SECONDS); + + /* Finally we get our own Status Report. */ + + reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile !== undefined) { + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(thisReport.beginingOfMarket); + + if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well. + + buildCandles(); + return; + } + + contextVariables.lastCandleFile = new Date(thisReport.lastFile); + + /* + Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached. + For that reason we go back one day, the partial information is discarded and added again with whatever new info is available. + */ + + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); + + buildCandles(); + return; + + } else { + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well. + + buildCandles(); + return; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildCandles() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> Entering function."); } + + let outputCandles; + let outputVolumes; + + advanceTime(); + + function advanceTime() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Entering function."); } + + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); } + + /* Validation that we are not going past the head of the market. */ + + if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); // Here is where we finish processing and wait for the platform to run this module again. + return; + } + + /* + + We prepere the arrays that will accumulate all the information for each output file. + + */ + + outputCandles = []; + outputVolumes = []; + + for (n = 0; n < global.dailyFilePeriods.length; n++) { + + const emptyArray1 = []; + const emptyArray2 = []; + + outputCandles.push(emptyArray1); + outputVolumes.push(emptyArray2); + + } + + periodsLoop(); + + } + + function periodsLoop() { + + /* + + We will iterate through all posible periods. + + */ + + let n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + function loopBody() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Entering function."); } + + const outputPeriod = global.dailyFilePeriods[n][0]; + const timeFrame = global.dailyFilePeriods[n][1]; + + nextCandleFile(); + + function nextCandleFile() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> Entering function."); } + + let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + let filePathRoot = bot.dataMine + "/" + "AABruce" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> getting file at dateForPath = " + dateForPath); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); } + + let candlesFile; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + try { + candlesFile = JSON.parse(text); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } else { + + if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') { + + logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + err.stack); + logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack); + callBackFunction(err); + return; + } + } + + const inputCandlesPerdiod = 60 * 1000; // 1 min + const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs + + let totalOutputCandles = inputFilePeriod / outputPeriod; + let beginingOutputTime = contextVariables.lastCandleFile.valueOf(); + + for (let i = 0; i < totalOutputCandles; i++) { + + let outputCandle = { + open: 0, + close: 0, + min: 0, + max: 0, + begin: 0, + end: 0 + }; + + let saveCandle = false; + + outputCandle.begin = beginingOutputTime + i * outputPeriod; + outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1; + + for (let j = 0; j < candlesFile.length; j++) { + + let candle = { + open: candlesFile[j][2], + close: candlesFile[j][3], + min: candlesFile[j][0], + max: candlesFile[j][1], + begin: candlesFile[j][4], + end: candlesFile[j][5] + }; + + /* Here we discard all the candles out of range. */ + + if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) { + + if (saveCandle === false) { // this will set the value only once. + + outputCandle.open = candle.open; + outputCandle.min = candle.min; + outputCandle.max = candle.max; + } + + saveCandle = true; + + outputCandle.close = candle.close; // only the last one will be saved + + if (candle.min < outputCandle.min) { + + outputCandle.min = candle.min; + } + + if (candle.max > outputCandle.max) { + + outputCandle.max = candle.max; + } + } + } + + if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range. + + outputCandles[n].push(outputCandle); + } + } + + nextVolumeFile(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function nextVolumeFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> Entering function."); } + + let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + let filePathRoot = bot.dataMine + "/" + "AABruce" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath); + + function onFileReceived(err, text) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> text = " + text); } + + let volumesFile; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + try { + volumesFile = JSON.parse(text); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } else { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack); + callBackFunction(err); + return; + } + + const inputVolumesPerdiod = 60 * 1000; // 1 min + const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs + + let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case. + let beginingOutputTime = contextVariables.lastCandleFile.valueOf(); + + for (let i = 0; i < totalOutputVolumes; i++) { + + let outputVolume = { + buy: 0, + sell: 0, + begin: 0, + end: 0 + }; + + let saveVolume = false; + + outputVolume.begin = beginingOutputTime + i * outputPeriod; + outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1; + + for (let j = 0; j < volumesFile.length; j++) { + + let volume = { + buy: volumesFile[j][0], + sell: volumesFile[j][1], + begin: volumesFile[j][2], + end: volumesFile[j][3] + }; + + /* Here we discard all the Volumes out of range. */ + + if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) { + + saveVolume = true; + + outputVolume.buy = outputVolume.buy + volume.buy; + outputVolume.sell = outputVolume.sell + volume.sell; + + } + } + + if (saveVolume === true) { + + outputVolumes[n].push(outputVolume); + + } + } + + writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop); + + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function controlLoop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> Entering function."); } + + n++; + + if (n < global.dailyFilePeriods.length) { + + loopBody(); + + } else { + + writeDataRanges(onWritten); + + function onWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> onWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack); + callBack(err); + return; + } + + writeStatusReport(contextVariables.lastCandleFile, advanceTime); + + } + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFiles(candles, volumes, timeFrame, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> Entering function."); } + + /* + + Here we will write the contents of the Candles and Volumens files. + + */ + + try { + + writeCandles(); + + function writeCandles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + let fileContent = ""; + + for (i = 0; i < candles.length; i++) { + + let candle = candles[i]; + + fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2); + let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Content written = " + fileContent); + } + + logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName); + + writeVolumes(); + } + } + + function writeVolumes() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + let fileContent = ""; + + for (i = 0; i < volumes.length; i++) { + + let candle = volumes[i]; + + fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2); + let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Content written = " + fileContent); + } + + logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName); + + callBack(); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeDataRanges(callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, CANDLES_FOLDER_NAME, onCandlesDataRangeWritten); + + function onCandlesDataRangeWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack); + callBack(err); + return; + } + + writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, VOLUMES_FOLDER_NAME, onVolumeDataRangeWritten); + + function onVolumeDataRangeWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] writeDataRanges -> writeDataRanges -> onVolumeDataRangeWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onVolumeDataRangeWritten -> err = " + err.stack); + callBack(err); + return; + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + } + + function writeDataRange(pBegin, pEnd, pProductFolder, callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); } + + let dataRange = { + begin: pBegin.valueOf(), + end:pEnd.valueOf() + }; + + let fileContent = JSON.stringify(dataRange); + + let fileName = 'Data.Range.' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack); + callBack(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent); + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(lastFileDate, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); } + + try { + + let reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.file.lastFile = lastFileDate; + thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString() + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] 'Start' - ERROR : " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/Multi-Period-Market/User.Bot.js b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/Multi-Period-Market/User.Bot.js new file mode 100644 index 000000000..cfddb306c --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/Multi-Period-Market/User.Bot.js @@ -0,0 +1,909 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const GMT_SECONDS = ':00.000 GMT+0000'; + const GMT_MILI_SECONDS = '.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Candles"; + const CANDLES_ONE_MIN = "One-Min"; + + const VOLUMES_FOLDER_NAME = "Volumes"; + const VOLUMES_ONE_MIN = "One-Min"; + + thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let statusDependencies; + let beginingOfMarket + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + callBackFunction(global.DEFAULT_OK_RESPONSE); + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +/* + + This process is going to do the following: + + Read the candles and volumes from Bruce and produce a single Index File for Market Period. But this is the situation: + + Bruce has a dataset organized with daily files with candles of 1 min. Olivia is writting in this process a single file for each timeFrame for the whole market. + Everytime this process run, must be able to resume its job and process everything pending until reaching the head of the market. So the tactic to do this is the + following: + + 1. First we need to read the last file written by this process, and load all the information into in-memory arrays. We will then append to this arrays the new + information we will get from Bruce. + + 2. We know from out status report which was the last DAY we processed from Bruce, but we must be carefull, because that day mightn not have been complete, if the + last run found the head of the market. That means that we have to be carefull not to append candles that are already there. To simplify what we do is to discard + all candles of the last processed day, and then we can process that full day again adding all the candles. + +*/ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = bot.market; + + /* Context Variables */ + + let contextVariables = { + lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process. + firstTradeFile: undefined, // Datetime of the first trade file in the whole market history. + maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process. + }; + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + let statusReport; + + /* We look first for Charly in order to get when the market starts. */ + + reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Historic-Trades" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.beginingOfMarket === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet."); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency does not exist." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS); + + /* Second, we get the report from Bruce, to know when the marted ends. */ + + reportKey = "AAMasters" + "-" + "AABruce" + "-" + "Single-Period-Daily" + "-" + "dataSet.V1" + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + "00:00" + GMT_SECONDS); + + /* Finally we get our own Status Report. */ + + reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Market" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile !== undefined) { + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(thisReport.beginingOfMarket); + + if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well. + + buildCandles(); + return; + } + + contextVariables.lastCandleFile = new Date(thisReport.lastFile); + + /* + Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached. + For that reason we go back one day, the partial information is discarded and added again with whatever new info is available. + */ + + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); + + findPreviousContent(); + return; + + } else { + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well. + + buildCandles(); + return; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> Double check that you are not running a process that only can be run at noTime mode at a certain month when it is not prepared to do so."); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function findPreviousContent() { + + /* + This is where we read the current files we have produced at previous runs of this same process. We just read all the content and organize it + in arrays and keep them in memory. + */ + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> Entering function."); } + + let n = 0 // loop Variable representing each possible period as defined at the periods array. + + let allPreviousCandles = []; + let allPreviousVolumes = []; + + loopBody(); + + function loopBody() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> Entering function."); } + + let timeFrame = global.marketFilesPeriods[n][1]; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> timeFrame = " + timeFrame); } + + let previousCandles; + let previousVolumes; + + getCandles(); + + function getCandles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> Entering function."); } + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> getting file."); + + function onFileReceived(err, text) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> text = " + text); } + + let candlesFile; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + try { + candlesFile = JSON.parse(text); + + previousCandles = candlesFile; + + getVolumes(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } else { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack); + callBackFunction(err); + } + } + } + + function getVolumes() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> Entering function."); } + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> getting file."); + + function onFileReceived(err, text) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> text = " + text); } + + let volumesFile; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + try { + volumesFile = JSON.parse(text); + + previousVolumes = volumesFile; + + allPreviousCandles.push(previousCandles); + allPreviousVolumes.push(previousVolumes); + + controlLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } else { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack); + callBackFunction(err); + } + } + } + + } + + function controlLoop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> controlLoop -> Entering function."); } + + n++; + + if (n < global.marketFilesPeriods.length) { + + loopBody(); + + } else { + + buildCandles(allPreviousCandles, allPreviousVolumes); + + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildCandles(allPreviousCandles, allPreviousVolumes) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> Entering function."); } + + /* + Firstly we prepere the arrays that will accumulate all the information for each output file. + */ + + let outputCandles = []; + let outputVolumes = []; + + for (n = 0; n < global.marketFilesPeriods.length; n++) { + + const emptyArray1 = []; + const emptyArray2 = []; + + outputCandles.push(emptyArray1); + outputVolumes.push(emptyArray2); + + } + + advanceTime(); + + function advanceTime() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Entering function."); } + + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); } + + /* Validation that we are not going past the head of the market. */ + + if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); // Here is where we finish processing and wait for the platform to run this module again. + return; + } + + periodsLoop(); + + } + + function periodsLoop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible periods. + + */ + + let n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + function loopBody() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Entering function."); } + + let previousCandles; + let previousVolumes; + + if (allPreviousCandles !== undefined) { + + previousCandles = allPreviousCandles[n]; + previousVolumes = allPreviousVolumes[n]; + + } + + const outputPeriod = global.marketFilesPeriods[n][0]; + const timeFrame = global.marketFilesPeriods[n][1]; + + /* + Here we are inside a Loop that is going to advance 1 day at the time, at each pass, will ready one of Bruce's daily files and + add all its candles to our in memory arrays. At the first iteration of this loop, we will add the candles that we are carrying + from our previous run, the ones we already have in-memory. You can see below how we discard from those candles the ones that + are belonging to the first day we are processing at this run, that it is exactly the same as the last day processed the privious + run. By discarding these candles, we are ready to run after that standard function that will just add ALL the candles found each + day at Bruce. + */ + + if (previousCandles !== undefined) { + + for (let i = 0; i < previousCandles.length; i++) { + + let candle = { + open: previousCandles[i][2], + close: previousCandles[i][3], + min: previousCandles[i][0], + max: previousCandles[i][1], + begin: previousCandles[i][4], + end: previousCandles[i][5] + }; + + if (candle.end < contextVariables.lastCandleFile.valueOf()) { + + outputCandles[n].push(candle); + + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Candle # " + i + " @ " + timeFrame + " discarded for closing past the current process time."); } + } + } + allPreviousCandles[n] = []; // erasing these so as not to duplicate them. + } + + if (previousVolumes !== undefined) { + + for (let i = 0; i < previousVolumes.length; i++) { + + let volume = { + begin: previousVolumes[i][2], + end: previousVolumes[i][3], + buy: previousVolumes[i][0], + sell: previousVolumes[i][1] + }; + + if (volume.end < contextVariables.lastCandleFile.valueOf()) { + + outputVolumes[n].push(volume); + + } else { + if (FULL_LOG === true) {logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Volume # " + i + " @ " + timeFrame + " discarded for closing past the current process time."); } + } + } + allPreviousVolumes[n] = []; // erasing these so as not to duplicate them. + } + + /* + From here on is where every iteration of the loop fully runs. Here is where we read Bruce's files and add their content to whatever + we already have in our arrays in-memory. In this way the process will run as many days needed and it should only stop when it reaches + the head of the market. + */ + + nextCandleFile(); + + function nextCandleFile() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> Entering function."); } + + let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + let filePathRoot = bot.dataMine + "/" + "AABruce" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> getting file at dateForPath = " + dateForPath); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); } + + let candlesFile; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + try { + candlesFile = JSON.parse(text); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } else { + + if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') { + + logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + JSON.stringify(err)); + logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack); + callBackFunction(err); + return; + } + } + + const inputCandlesPerdiod = 60 * 1000; // 1 min + const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs + + let totalOutputCandles = inputFilePeriod / outputPeriod; // this should be 2 in this case. + let beginingOutputTime = contextVariables.lastCandleFile.valueOf(); + + /* + The algorithm that follows is going to agregate candles of 1 min timeFrame read from Bruce, into candles of each timeFrame + that Olivia generates. For market files those timePediods goes from 1h to 24hs. + */ + + for (let i = 0; i < totalOutputCandles; i++) { + + let outputCandle = { + open: 0, + close: 0, + min: 0, + max: 0, + begin: 0, + end: 0 + }; + + let saveCandle = false; + + outputCandle.begin = beginingOutputTime + i * outputPeriod; + outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1; + + for (let j = 0; j < candlesFile.length; j++) { + + let candle = { + open: candlesFile[j][2], + close: candlesFile[j][3], + min: candlesFile[j][0], + max: candlesFile[j][1], + begin: candlesFile[j][4], + end: candlesFile[j][5] + }; + + /* Here we discard all the candles out of range. */ + + if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) { + + if (saveCandle === false) { // this will set the value only once. + + outputCandle.open = candle.open; + outputCandle.min = candle.min; + outputCandle.max = candle.max; + } + + saveCandle = true; + + outputCandle.close = candle.close; // only the last one will be saved + + if (candle.min < outputCandle.min) { + + outputCandle.min = candle.min; + } + + if (candle.max > outputCandle.max) { + + outputCandle.max = candle.max; + } + } + } + + if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range. + + outputCandles[n].push(outputCandle); + } + } + + nextVolumeFile(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function nextVolumeFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> Entering function."); } + + let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + let filePathRoot = bot.dataMine + "/" + "AABruce" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath); + + function onFileReceived(err, text) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> text = " + text); } + + let volumesFile; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + try { + volumesFile = JSON.parse(text); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } else { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack); + callBackFunction(err); + return; + } + + const inputVolumesPerdiod = 60 * 1000; // 1 min + const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs + + let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case. + let beginingOutputTime = contextVariables.lastCandleFile.valueOf(); + + for (let i = 0; i < totalOutputVolumes; i++) { + + let outputVolume = { + buy: 0, + sell: 0, + begin: 0, + end: 0 + }; + + let saveVolume = false; + + outputVolume.begin = beginingOutputTime + i * outputPeriod; + outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1; + + for (let j = 0; j < volumesFile.length; j++) { + + let volume = { + buy: volumesFile[j][0], + sell: volumesFile[j][1], + begin: volumesFile[j][2], + end: volumesFile[j][3] + }; + + /* Here we discard all the Volumes out of range. */ + + if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) { + + saveVolume = true; + + outputVolume.buy = outputVolume.buy + volume.buy; + outputVolume.sell = outputVolume.sell + volume.sell; + + } + } + + if (saveVolume === true) { + + outputVolumes[n].push(outputVolume); + } + } + + writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop); + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function controlLoop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> Entering function."); } + + n++; + + if (n < global.marketFilesPeriods.length) { + + loopBody(); + + } else { + + writeStatusReport(contextVariables.lastCandleFile, advanceTime); + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFiles(candles, volumes, timeFrame, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> Entering function."); } + + /* + + Here we will write the contents of the Candles and Volumens files. + + */ + + try { + + writeCandles(); + + function writeCandles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + let fileContent = ""; + + for (i = 0; i < candles.length; i++) { + + let candle = candles[i]; + + fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Content written = " + fileContent); + } + + logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName ); + + writeVolumes(); + } + } + + function writeVolumes() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + let fileContent = ""; + + for (i = 0; i < volumes.length; i++) { + + let candle = volumes[i]; + + fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack); + callBackFunction(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Content written = " + fileContent); + } + + logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName); + + callBack(); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(lastFileDate, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); } + + try { + + let reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Market" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.file.lastFile = lastFileDate; + thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString() + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/package.json b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/package.json new file mode 100644 index 000000000..90713cac8 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/package.json @@ -0,0 +1,12 @@ +{ + "name": "AAOlivia", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Luis-Fernando-Molina", + "license": "ISC", + "dependencies": {} +} diff --git a/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/this.bot.config.json b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/this.bot.config.json new file mode 100644 index 000000000..7b7b7e879 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AAOlivia-Indicator-Bot/this.bot.config.json @@ -0,0 +1,241 @@ +{ + "displayName": "Olivia", + "codeName": "AAOlivia", + "type": "Indicator Bot", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "dataMine": "AAMasters", + "profilePicture": "Olivia.png", + "dataSetVersion": "dataSet.V1", + "processes": [ + { + "name": "Multi-Period-Market", + "description": "Produces different files with multi-period candles and volumes for entire markets.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "updatesDatasets": [ + { + "product": "Candles", + "dataSet": "Multi-Period-Market" + }, + { + "product": "Volumes", + "dataSet": "Multi-Period-Market" + } + ], + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Historic-Trades", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AABruce", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Single-Period-Daily", + "dataSetVersion": "dataSet.V1", + "dataSetSection": "Month", + "waitUntilNextUpdate": true + }, + { + "dataMine": "AAMasters", + "bot": "AAOlivia", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Market", + "dataSetVersion": "dataSet.V1" + } + ] + }, + { + "name": "Multi-Period-Daily", + "description": "Produces different files with multi-period candles and volumes on a daily format.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "updatesDatasets": [ + { + "product": "Candles", + "dataSet": "Multi-Period-Daily" + }, + { + "product": "Volumes", + "dataSet": "Multi-Period-Daily" + } + ], + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Historic-Trades", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AABruce", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Single-Period-Daily", + "dataSetVersion": "dataSet.V1", + "dataSetSection": "Month", + "waitUntilNextUpdate": true + }, + { + "dataMine": "AAMasters", + "bot": "AAOlivia", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Daily", + "dataSetVersion": "dataSet.V1" + } + ] + } + ], + "products": [ + { + "codeName": "Candles", + "displayName": "Candles", + "description": "Two sets of files per period that contains candles.", + "storageAccount": "aaolivia", + "shareWith": "Public", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersCandlesVolumes", + "moduleName": "Candles" + } + }, + { + "codeName": "Volumes", + "displayName": "Buy and Sell Volumes", + "description": "Two sets of files per period that contains buy and sell volumes.", + "storageAccount": "aaolivia", + "shareWith": "Public", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AAOlivia.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersCandlesVolumes", + "moduleName": "Volumes" + } + } + ] +} \ No newline at end of file diff --git a/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Json.json b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Json.json new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Json.json @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Multi-Period-Daily/User.Bot.js b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Multi-Period-Daily/User.Bot.js new file mode 100644 index 000000000..35f8ecf7b --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Multi-Period-Daily/User.Bot.js @@ -0,0 +1,1640 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) { + + const FULL_LOG = true; + const INTENSIVE_LOG = false; + const LOG_FILE_CONTENT = false; + + const GMT_SECONDS = ':00.000 GMT+0000'; + const GMT_MILI_SECONDS = '.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Candles"; + const CANDLE_STAIRS_FOLDER_NAME = "Candle-Stairs"; + + const VOLUMES_FOLDER_NAME = "Volumes"; + const VOLUME_STAIRS_FOLDER_NAME = "Volume-Stairs"; + + thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let statusDependencies; + let beginingOfMarket + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + + This process is going to do the following: + + Read the candles and volumes from Olivia and produce for each market two files with candles stairs and volumes stairs respectively. + + */ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + /* One of the challenges of this process is that each imput file contains one day of candles. So if a stair spans more than one day + then we dont want to break the stais in two pieces. What we do is that we read to candles files at the time and record at the current + date all stairs of the day plus the ones thas spans to the second day without bigining at the second day. Then when we process the next + day, we must remember where the last stairs of each type endded, so as not to create overlapping stairs in the current day. */ + + let market = bot.market; + + /* Context Variables */ + + let contextVariables = { + lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process. + firstTradeFile: undefined, // Datetime of the first trade file in the whole market history. + maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process. + }; + + let previousDay; // Holds the date of the previous day relative to the processing date. + let processDate; // Holds the processing date. + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + let statusReport; + + /* We look first for Charly in order to get when the market starts. */ + + reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Historic-Trades" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.beginingOfMarket === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet."); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency does not exist." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS); + + /* Second, we get the report from Olivia, to know when the marted ends. */ + + reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.maxCandleFile = new Date(thisReport.lastFile.valueOf()); + + /* Finally we get our own Status Report. */ + + reportKey = "AAMasters" + "-" + "AATom" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } + + statusReport = statusDependencies.statusReports.get(reportKey); + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusDependencies.statusReports.get(reportKey).file; + + if (thisReport.lastFile !== undefined) { + + beginingOfMarket = new Date(thisReport.beginingOfMarket); + + if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS) + + buildStairs(); + return; + } + + contextVariables.lastCandleFile = new Date(thisReport.lastFile); + + /* + The stairs objects can span more than one day. In order not to cut these objects into two when this happens, this process will alsways read + 3 files. + + 1. The first file is a stairs file corresponding to processDay -2. From there we will know where the last staris ended. + 2. The second is a candle or volume file corresponding to processDay -1. + 3. The third is a candle of volume file at processDay. + + We will recalculate 2 and 3 considering the objects already in 1, so as to make the transition between 2 and 3 smooth. + */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile !== undefined"); } + + buildStairs(); + return; + + } else { + + /* + In the case when there is no status report, we take the date of the file with the first trades as the begining of the market. Then we will + go one day fordward in time, so that when we enter the loop, the "previous day" does not fall outside the begining of the market. + */ + + contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS) + + beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile === undefined"); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> contextVariables.lastCandleFile = " + contextVariables.lastCandleFile); } + + buildStairs(); + return; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildStairs() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> Entering function."); } + + let n; + processDate = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> processDate = " + processDate); } + + advanceTime(); + + function advanceTime() { + + try { + + logger.newInternalLoop(bot.codeName, bot.process); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> advanceTime -> Entering function."); } + + processDate = new Date(processDate.valueOf() + ONE_DAY_IN_MILISECONDS); + previousDay = new Date(processDate.valueOf() - ONE_DAY_IN_MILISECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> advanceTime -> processDate = " + processDate); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> advanceTime -> previousDay = " + previousDay); } + + /* Validation that we are not going past the head of the market. */ + + if (processDate.valueOf() > contextVariables.maxCandleFile.valueOf()) { + + const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> advanceTime -> " + logText); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + periodsLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> advanceTime -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible timeFrames. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function loopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> Entering function."); } + + const timeFrame = global.dailyFilePeriods[n][1]; + + let endOfLastCandleStair = new Date(previousDay.valueOf() - ONE_DAY_IN_MILISECONDS); + let endOfLastBuyVolumeStair = new Date(previousDay.valueOf() - ONE_DAY_IN_MILISECONDS); + let endOfLastSellVolumeStair = new Date(previousDay.valueOf() - ONE_DAY_IN_MILISECONDS); + + /* + By default we set the starting date of the processDay - 2 file. If we find the file this value shoulld be overwritten by the value of the end property + of the last object on the file. + */ + + endOfLastCandleStair = endOfLastCandleStair.valueOf(); + endOfLastBuyVolumeStair = endOfLastBuyVolumeStair.valueOf(); + endOfLastSellVolumeStair = endOfLastSellVolumeStair.valueOf(); + + getEndOfLastCandleStair(); + + function getEndOfLastCandleStair() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastCandleStair -> Entering function."); } + + let fileDate = new Date(previousDay.valueOf() - ONE_DAY_IN_MILISECONDS); + getCandleStairsFile(); + + function getCandleStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastCandleStair -> getCandleStairsFile -> Entering function."); } + + let dateForPath = fileDate.getUTCFullYear() + '/' + utilities.pad(fileDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(fileDate.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLE_STAIRS_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastCandleStair -> getCandleStairsFile -> onFileReceived -> Entering function."); } + + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastCandleStair -> getCandleStairsFile -> onFileReceived -> text = " + text); } + + if ( + err.result === global.CUSTOM_FAIL_RESPONSE.result && + (err.message === 'Folder does not exist.' || err.message === 'File does not exist.') + ) { + + getEndOfLastVolumeStair(); + return; + } + + let stairsFile = JSON.parse(text); + + if (stairsFile.length > 0) { + + endOfLastCandleStair = stairsFile[stairsFile.length - 1][5]; // We get from the last regord the end value. Position 5 = Stairs.end + getEndOfLastVolumeStair(); + + } else { + + getEndOfLastVolumeStair(); + return; + + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastCandleStair -> getCandleStairsFile -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastCandleStair -> getCandleStairsFile -> onFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastCandleStair -> getCandleStairsFile -> onFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastCandleStair -> getCandleStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastCandleStair -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getEndOfLastVolumeStair() { + + try { + + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastVolumeStair -> Entering function."); } + + let fileDate = new Date(previousDay.valueOf() - ONE_DAY_IN_MILISECONDS); + getCandleStairsFile(); + + function getCandleStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastVolumeStair -> getCandleStairsFile -> Entering function."); } + + let dateForPath = fileDate.getUTCFullYear() + '/' + utilities.pad(fileDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(fileDate.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUME_STAIRS_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastVolumeStair -> getCandleStairsFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> getEndOfLastVolumeStair -> getCandleStairsFile -> onFileReceived -> text = " + text); } + + if ( + err.result === global.CUSTOM_FAIL_RESPONSE.result && + (err.message === 'Folder does not exist.' || err.message === 'File does not exist.') + ) { + + processCandles(); + return; + } + + let stairsFile = JSON.parse(text); + + if (stairsFile.length > 0) { + + for (let i = 0; i < stairsFile.length; i++) { + + let stairs = { + type: stairsFile[i][0], + begin: stairsFile[i][1], + end: stairsFile[i][2], + direction: stairsFile[i][3], + barsCount: stairsFile[i][4], + firstAmount: stairsFile[i][5], + lastAmount: stairsFile[i][6] + }; + + if (stairs.type === 'buy') { + + endOfLastBuyVolumeStair = stairs.end; + + } else { + + endOfLastSellVolumeStair = stairs.end; + + } + } + + processCandles(); + + } else { + + processCandles(); + return; + + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastVolumeStair -> getCandleStairsFile -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastVolumeStair -> getCandleStairsFile -> onFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastVolumeStair -> getCandleStairsFile -> onFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastVolumeStair -> getCandleStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> getEndOfLastVolumeStair -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function processCandles() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> Entering function."); } + + let candles = []; // Here we will put all the candles of the 2 files read. + + let previousDayStairsArray = []; // Here All the stairs of the previous day. + let processDayStairsArray = []; // Here All the stairs of the process day. + + let previousDayFile; + let processDayFile; + + getCandleStairsFile(); + + function getCandleStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> getCandleStairsFile -> Entering function."); } + + let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onCurrentDayFileReceived); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> getCandleStairsFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> getCandleStairsFile -> onCurrentDayFileReceived -> text = " + text); } + + previousDayFile = JSON.parse(text); + getProcessDayFile() + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getCandleStairsFile -> onCurrentDayFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getCandleStairsFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getCandleStairsFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getCandleStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getProcessDayFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> getProcessDayFile -> Entering function."); } + + let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onCurrentDayFileReceived); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> getProcessDayFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> getProcessDayFile -> onCurrentDayFileReceived -> text = " + text); } + + processDayFile = JSON.parse(text); + buildCandles(); + + } catch (err) { + + if (processDate.valueOf() > contextVariables.maxCandleFile.valueOf()) { + + processDayFile = []; // we are past the head of the market, then no worries if this file is non existent. + buildCandles(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getProcessDayFile -> onCurrentDayFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getProcessDayFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getProcessDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> getProcessDayFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildCandles() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> buildCandles -> Entering function."); } + + pushCandles(previousDayFile); + pushCandles(processDayFile); + findCandleStairs(); + + function pushCandles(candlesFile) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> buildCandles -> pushCandles -> Entering function."); } + + for (let i = 0; i < candlesFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = candlesFile[i][0]; + candle.max = candlesFile[i][1]; + + candle.open = candlesFile[i][2]; + candle.close = candlesFile[i][3]; + + candle.begin = candlesFile[i][4]; + candle.end = candlesFile[i][5]; + + if (candle.open > candle.close) { candle.direction = 'down'; } + if (candle.open < candle.close) { candle.direction = 'up'; } + if (candle.open === candle.close) { candle.direction = 'side'; } + + candles.push(candle); + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> buildCandles -> pushCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> buildCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function findCandleStairs() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> findCandleStairs -> Entering function."); } + + /* Finding stairs */ + + let stairs; + + for (let i = 0; i < candles.length - 1; i++) { + + let currentCandle = candles[i]; + let nextCandle = candles[i + 1]; + + if (currentCandle.direction === nextCandle.direction && currentCandle.direction !== 'side') { + + if (stairs === undefined) { + + stairs = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined, + candleCount: 0, + firstMin: 0, + firstMax: 0, + lastMin: 0, + lastMax: 0 + }; + + stairs.direction = currentCandle.direction; + stairs.candleCount = 2; + + stairs.begin = currentCandle.begin; + stairs.end = nextCandle.end; + + stairs.open = currentCandle.open; + stairs.close = nextCandle.close; + + if (currentCandle.min < nextCandle.min) { stairs.min = currentCandle.min; } else { stairs.min = nextCandle.min; } + if (currentCandle.max > nextCandle.max) { stairs.max = currentCandle.max; } else { stairs.max = nextCandle.max; } + + if (stairs.direction === 'up') { + + stairs.firstMin = currentCandle.open; + stairs.firstMax = currentCandle.close; + + stairs.lastMin = nextCandle.open; + stairs.lastMax = nextCandle.close; + + } else { + + stairs.firstMin = currentCandle.close; + stairs.firstMax = currentCandle.open; + + stairs.lastMin = nextCandle.close; + stairs.lastMax = nextCandle.open; + + } + + + } else { + + stairs.candleCount++; + stairs.end = nextCandle.end; + stairs.close = nextCandle.close; + + if (stairs.min < nextCandle.min) { stairs.min = currentCandle.min; } + if (stairs.max > nextCandle.max) { stairs.max = currentCandle.max; } + + if (stairs.direction === 'up') { + + stairs.lastMin = nextCandle.open; + stairs.lastMax = nextCandle.close; + + } else { + + stairs.lastMin = nextCandle.close; + stairs.lastMax = nextCandle.open; + + } + + } + + } else { + + if (stairs !== undefined) { + + /* + Here we detect stairs that started at process day - 2. + */ + + if (stairs.begin > endOfLastCandleStair) { + + if (stairs.begin >= processDate.valueOf()) { + + processDayStairsArray.push(stairs); + + } else { + + previousDayStairsArray.push(stairs); + + } + } + + stairs = undefined; + } + } + } + + writeCandleStairsFile(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> findCandleStairs -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function writeCandleStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> Entering function."); } + + writeFile(previousDayStairsArray, previousDay, onPreviousFileWritten); + + function onPreviousFileWritten() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> onPreviousFileWritten -> Entering function."); } + + writeFile(processDayStairsArray, processDate, onProcessFileWritten); + + function onProcessFileWritten() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> onPreviousFileWritten -> onProcessFileWritten -> Entering function."); } + + processVolumes(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> writeCandleStairsFile -> onPreviousFileWritten -> onProcessFileWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> writeCandleStairsFile -> onPreviousFileWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFile(pStairs, pDate, callback) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> writeFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < pStairs.length; i++) { + + let stairs = pStairs[i]; + + fileContent = fileContent + separator + '[' + + stairs.open + "," + + stairs.close + "," + + stairs.min + "," + + stairs.max + "," + + stairs.begin + "," + + stairs.end + "," + + '"' + stairs.direction + '"' + "," + + stairs.candleCount + "," + + stairs.firstMin + "," + + stairs.firstMax + "," + + stairs.lastMin + "," + + stairs.lastMax + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = pDate.getUTCFullYear() + '/' + utilities.pad(pDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(pDate.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLE_STAIRS_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> writeFile -> onFileCreated -> Entering function."); } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> writeFile -> onFileCreated -> Content written = " + fileContent); + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> writeFile -> onFileCreated -> err = " + err.stack); + callBack(err); + return; + } + + const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + ""; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processCandles -> writeCandleStairsFile -> writeFile -> onFileCreated -> " + logText); } + + callback(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> writeCandleStairsFile -> writeFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> writeCandleStairsFile -> writeFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> writeCandleStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function processVolumes() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> Entering function."); } + + let volumes = []; + + let previousDayStairsArray = []; // Here All the stairs of the previous day. + let processDayStairsArray = []; // Here All the stairs of the process day. + + let previousDayFile; + let processDayFile; + + getCandleStairsFile(); + + function getCandleStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> getCandleStairsFile -> Entering function."); } + + let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onCurrentDayFileReceived); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> getCandleStairsFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> getCandleStairsFile -> onCurrentDayFileReceived -> text = " + text); } + + previousDayFile = JSON.parse(text); + getProcessDayFile() + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getCandleStairsFile -> onCurrentDayFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getCandleStairsFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getCandleStairsFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getCandleStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getProcessDayFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> getProcessDayFile -> Entering function."); } + + let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onCurrentDayFileReceived); + + function onCurrentDayFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> getProcessDayFile -> onCurrentDayFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> getProcessDayFile -> onCurrentDayFileReceived -> text = " + text); } + + processDayFile = JSON.parse(text); + buildVolumes(); + + } catch (err) { + + if (processDate.valueOf() > contextVariables.maxCandleFile.valueOf()) { + + processDayFile = []; // we are past the head of the market, then no worries if this file is non existent. + buildVolumes(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getProcessDayFile -> onCurrentDayFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getProcessDayFile -> onCurrentDayFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getProcessDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> getProcessDayFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildVolumes() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> buildVolumes -> Entering function."); } + + pushVolumes(previousDayFile); + pushVolumes(processDayFile); + findVolumesStairs(); + + function pushVolumes(volumesFile) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> buildVolumes -> pushVolumes -> Entering function."); } + + for (let i = 0; i < volumesFile.length; i++) { + + let volume = { + amountBuy: 0, + amountSell: 0, + begin: undefined, + end: undefined + }; + + volume.amountBuy = volumesFile[i][0]; + volume.amountSell = volumesFile[i][1]; + + volume.begin = volumesFile[i][2]; + volume.end = volumesFile[i][3]; + + volumes.push(volume); + + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> buildVolumes -> pushVolumes -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> buildVolumes -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function findVolumesStairs() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> findVolumesStairs -> Entering function."); } + + /* Finding stairs */ + + let buyUpStairs; + let buyDownStairs; + + let sellUpStairs; + let sellDownStairs; + + for (let i = 0; i < volumes.length - 1; i++) { + + let currentVolume = volumes[i]; + let nextVolume = volumes[i + 1]; + + + /* buy volume going up */ + + if (currentVolume.amountBuy < nextVolume.amountBuy) { + + if (buyUpStairs === undefined) { + + buyUpStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + buyUpStairs.type = 'buy'; + buyUpStairs.direction = 'up'; + buyUpStairs.barsCount = 2; + + buyUpStairs.begin = currentVolume.begin; + buyUpStairs.end = nextVolume.end; + + buyUpStairs.firstAmount = currentVolume.amountBuy; + buyUpStairs.lastAmount = nextVolume.amountBuy; + + } else { + + buyUpStairs.barsCount++; + buyUpStairs.end = nextVolume.end; + buyUpStairs.lastAmount = nextVolume.amountBuy; + + } + + } else { + + if (buyUpStairs !== undefined) { + + if (buyUpStairs.barsCount > 2) { + + pushToArray(buyUpStairs); + } + + buyUpStairs = undefined; + } + } + + /* buy volume going down */ + + if (currentVolume.amountBuy > nextVolume.amountBuy) { + + if (buyDownStairs === undefined) { + + buyDownStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + buyDownStairs.type = 'buy'; + buyDownStairs.direction = 'down'; + buyDownStairs.barsCount = 2; + + buyDownStairs.begin = currentVolume.begin; + buyDownStairs.end = nextVolume.end; + + buyDownStairs.firstAmount = currentVolume.amountBuy; + buyDownStairs.lastAmount = nextVolume.amountBuy; + + } else { + + buyDownStairs.barsCount++; + buyDownStairs.end = nextVolume.end; + buyDownStairs.lastAmount = nextVolume.amountBuy; + + } + + } else { + + if (buyDownStairs !== undefined) { + + if (buyDownStairs.barsCount > 2) { + + pushToArray(buyDownStairs); + } + + buyDownStairs = undefined; + } + } + + /* sell volume going up */ + + if (currentVolume.amountSell < nextVolume.amountSell) { + + if (sellUpStairs === undefined) { + + sellUpStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + sellUpStairs.type = 'sell'; + sellUpStairs.direction = 'up'; + sellUpStairs.barsCount = 2; + + sellUpStairs.begin = currentVolume.begin; + sellUpStairs.end = nextVolume.end; + + sellUpStairs.firstAmount = currentVolume.amountSell; + sellUpStairs.lastAmount = nextVolume.amountSell; + + } else { + + sellUpStairs.barsCount++; + sellUpStairs.end = nextVolume.end; + sellUpStairs.lastAmount = nextVolume.amountSell; + + } + + } else { + + if (sellUpStairs !== undefined) { + + if (sellUpStairs.barsCount > 2) { + + pushToArray(sellUpStairs); + } + + sellUpStairs = undefined; + } + } + + /* sell volume going down */ + + if (currentVolume.amountSell > nextVolume.amountSell) { + + if (sellDownStairs === undefined) { + + sellDownStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + sellDownStairs.type = 'sell'; + sellDownStairs.direction = 'down'; + sellDownStairs.barsCount = 2; + + sellDownStairs.begin = currentVolume.begin; + sellDownStairs.end = nextVolume.end; + + sellDownStairs.firstAmount = currentVolume.amountSell; + sellDownStairs.lastAmount = nextVolume.amountSell; + + } else { + + sellDownStairs.barsCount++; + sellDownStairs.end = nextVolume.end; + sellDownStairs.lastAmount = nextVolume.amountSell; + + } + + } else { + + if (sellDownStairs !== undefined) { + + if (sellDownStairs.barsCount > 2) { + + pushToArray(sellDownStairs); + } + + sellDownStairs = undefined; + } + } + + function pushToArray(stairs) { + + if (INTENSIVE_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> findVolumesStairs -> pushToArray -> Entering function."); } + + try { + + if (stairs !== undefined) { + + /* + Here we detect stairs that started at process day - 2. + */ + + if (stairs.type === 'sell') { + + if (stairs.begin > endOfLastSellVolumeStair) { + + if (stairs.begin >= processDate.valueOf()) { + + processDayStairsArray.push(stairs); + + } else { + + previousDayStairsArray.push(stairs); + + } + } + } + else { + + if (stairs.begin > endOfLastBuyVolumeStair) { + + if (stairs.begin >= processDate.valueOf()) { + + processDayStairsArray.push(stairs); + + } else { + + previousDayStairsArray.push(stairs); + + } + } + + } + + stairs = undefined; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> findVolumesStairs -> pushToArray -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + writeVolumeStairsFile(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> findVolumesStairs -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeVolumeStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> Entering function."); } + + writeFile(previousDayStairsArray, previousDay, onPreviousFileWritten); + + function onPreviousFileWritten() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> onPreviousFileWritten -> Entering function."); } + + writeFile(processDayStairsArray, processDate, onProcessFileWritten); + + function onProcessFileWritten() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> onPreviousFileWritten -> onProcessFileWritten -> Entering function."); } + + controlLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> writeVolumeStairsFile -> onPreviousFileWritten -> onProcessFileWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> writeVolumeStairsFile -> onPreviousFileWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFile(pStairs, pDate, callback) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> writeFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < pStairs.length; i++) { + + let stairs = pStairs[i]; + + fileContent = fileContent + separator + '[' + + '"' + stairs.type + '"' + "," + + stairs.begin + "," + + stairs.end + "," + + '"' + stairs.direction + '"' + "," + + stairs.barsCount + "," + + stairs.firstAmount + "," + + stairs.lastAmount + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = pDate.getUTCFullYear() + '/' + utilities.pad(pDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(pDate.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUME_STAIRS_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> writeFile -> onFileCreated -> Entering function."); } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> writeFile -> onFileCreated -> Content written = " + fileContent); + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> writeFile -> onFileCreated -> err = " + err.stack); + callBack(err); + return; + } + + const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + ""; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> processVolumes -> writeVolumeStairsFile -> writeFile -> onFileCreated -> " + logText); } + + callback(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> writeVolumeStairsFile -> onPreviousFileWritten -> writeFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> writeVolumeStairsFile -> onPreviousFileWritten -> writeFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> writeVolumeStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> processVolumes -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> loopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function controlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> controlLoop -> Entering function."); } + + n++; + + if (n < global.dailyFilePeriods.length) { + + loopBody(); + + } else { + + n = 0; + + writeDataRanges(onWritten); + + function onWritten(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> controlLoop -> onWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> controlLoop -> onWritten -> err = " + err.stack); + callBack(err); + return; + } + + writeStatusReport(processDate, advanceTime); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> controlLoop -> onWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> controlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> err.message = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeDataRanges(callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + writeDataRange(contextVariables.firstTradeFile, processDate, CANDLE_STAIRS_FOLDER_NAME, onCandlesStairsDataRangeWritten); + + function onCandlesStairsDataRangeWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onCandlesStairsDataRangeWritten -> err = " + err.stack); + callBack(err); + return; + } + + writeDataRange(contextVariables.firstTradeFile, processDate, VOLUME_STAIRS_FOLDER_NAME, onVolumeStairsDataRangeWritten); + + function onVolumeStairsDataRangeWritten(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] writeDataRanges -> writeDataRanges -> onVolumeStairsDataRangeWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onVolumeStairsDataRangeWritten -> err = " + err.stack); + callBack(err); + return; + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + } + + function writeDataRange(pBegin, pEnd, pProductFolder, callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); } + + let dataRange = { + begin: pBegin.valueOf(), + end: pEnd.valueOf() + }; + + let fileContent = JSON.stringify(dataRange); + + let fileName = 'Data.Range.' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack); + callBack(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent); + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(lastFileDate, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); } + + try { + + let reportKey = "AAMasters" + "-" + "AATom" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.file.lastFile = lastFileDate; + thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString() + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err.message = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Multi-Period-Market/User.Bot.js b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Multi-Period-Market/User.Bot.js new file mode 100644 index 000000000..bc193bb89 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/Multi-Period-Market/User.Bot.js @@ -0,0 +1,811 @@ +exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const GMT_SECONDS = ':00.000 GMT+0000'; + const GMT_MILI_SECONDS = '.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const TRADES_FOLDER_NAME = "Trades"; + + const CANDLES_FOLDER_NAME = "Candles"; + const CANDLE_STAIRS_FOLDER_NAME = "Candle-Stairs"; + + const VOLUMES_FOLDER_NAME = "Volumes"; + const VOLUME_STAIRS_FOLDER_NAME = "Volume-Stairs"; + + thisObject = { + initialize: initialize, + start: start + }; + + let statusDependencies; + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + + This process is going to do the following: + + Read the candles and volumes from Olivia and produce for each market two files with candles stairs and volumes stairs respectively. + + */ + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = bot.market; + + buildStairs(); + + function buildStairs() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> Entering function."); } + + try { + + let n; + + periodsLoop(); + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible periods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + loopBody(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> periodsLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function loopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> Entering function."); } + + const outputPeriod = global.marketFilesPeriods[n][0]; + const timeFrame = global.marketFilesPeriods[n][1]; + + nextCandleFile(); + + function nextCandleFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> Entering function."); } + + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack); + callBackFunction(err); + return; + + } + + let marketFile = JSON.parse(text); + + let candles = []; + let stairsArray = []; + + buildCandles(); + + function buildCandles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> Entering function."); } + + try { + + for (let i = 0; i < marketFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = marketFile[i][0]; + candle.max = marketFile[i][1]; + + candle.open = marketFile[i][2]; + candle.close = marketFile[i][3]; + + candle.begin = marketFile[i][4]; + candle.end = marketFile[i][5]; + + if (candle.open > candle.close) { candle.direction = 'down'; } + if (candle.open < candle.close) { candle.direction = 'up'; } + if (candle.open === candle.close) { candle.direction = 'side'; } + + candles.push(candle); + + } + + findCandleStairs(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function findCandleStairs() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> findCandleStairs -> Entering function."); } + + /* Finding stairs */ + + let stairs; + + for (let i = 0; i < candles.length - 1; i++) { + + let currentCandle = candles[i]; + let nextCandle = candles[i + 1]; + + if (currentCandle.direction === nextCandle.direction && currentCandle.direction !== 'side') { + + if (stairs === undefined) { + + stairs = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined, + candleCount: 0, + firstMin: 0, + firstMax: 0, + lastMin: 0, + lastMax: 0 + }; + + stairs.direction = currentCandle.direction; + stairs.candleCount = 2; + + stairs.begin = currentCandle.begin; + stairs.end = nextCandle.end; + + stairs.open = currentCandle.open; + stairs.close = nextCandle.close; + + if (currentCandle.min < nextCandle.min) { stairs.min = currentCandle.min; } else { stairs.min = nextCandle.min; } + if (currentCandle.max > nextCandle.max) { stairs.max = currentCandle.max; } else { stairs.max = nextCandle.max; } + + if (stairs.direction === 'up') { + + stairs.firstMin = currentCandle.open; + stairs.firstMax = currentCandle.close; + + stairs.lastMin = nextCandle.open; + stairs.lastMax = nextCandle.close; + + } else { + + stairs.firstMin = currentCandle.close; + stairs.firstMax = currentCandle.open; + + stairs.lastMin = nextCandle.close; + stairs.lastMax = nextCandle.open; + + } + + + } else { + + stairs.candleCount++; + stairs.end = nextCandle.end; + stairs.close = nextCandle.close; + + if (stairs.min < nextCandle.min) { stairs.min = currentCandle.min; } + if (stairs.max > nextCandle.max) { stairs.max = currentCandle.max; } + + if (stairs.direction === 'up') { + + stairs.lastMin = nextCandle.open; + stairs.lastMax = nextCandle.close; + + } else { + + stairs.lastMin = nextCandle.close; + stairs.lastMax = nextCandle.open; + + } + + } + + } else { + + if (stairs !== undefined) { + stairsArray.push(stairs); + stairs = undefined; + } + } + } + + writeCandleStairsFile(); + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> findCandleStairs -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeCandleStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < stairsArray.length; i++) { + + let stairs = stairsArray[i]; + + fileContent = fileContent + separator + '[' + + stairs.open + "," + + stairs.close + "," + + stairs.min + "," + + stairs.max + "," + + stairs.begin + "," + + stairs.end + "," + + '"' + stairs.direction + '"' + "," + + stairs.candleCount + "," + + stairs.firstMin + "," + + stairs.firstMax + "," + + stairs.lastMin + "," + + stairs.lastMax + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + CANDLE_STAIRS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + nextVolumeFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> writeCandleStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextCandleFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + function nextVolumeFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> Entering function."); } + + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json" + + let filePathRoot = bot.dataMine + "/" + "AAOlivia" + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> text = " + text); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack); + callBackFunction(err); + return; + + } + + let marketFile = JSON.parse(text); + + let volumes = []; + let stairsArray = []; + + buildVolumes(); + + function buildVolumes() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> buildVolumes -> Entering function."); } + + for (let i = 0; i < marketFile.length; i++) { + + let volume = { + amountBuy: 0, + amountSell: 0, + begin: undefined, + end: undefined + }; + + volume.amountBuy = marketFile[i][0]; + volume.amountSell = marketFile[i][1]; + + volume.begin = marketFile[i][2]; + volume.end = marketFile[i][3]; + + volumes.push(volume); + + } + + findVolumesStairs(); + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> buildVolumes -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function findVolumesStairs() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> findVolumesStairs -> Entering function."); } + + /* Finding stairs */ + + let buyUpStairs; + let buyDownStairs; + + let sellUpStairs; + let sellDownStairs; + + for (let i = 0; i < volumes.length - 1; i++) { + + let currentVolume = volumes[i]; + let nextVolume = volumes[i + 1]; + + + /* buy volume going up */ + + if (currentVolume.amountBuy < nextVolume.amountBuy) { + + if (buyUpStairs === undefined) { + + buyUpStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + buyUpStairs.type = 'buy'; + buyUpStairs.direction = 'up'; + buyUpStairs.barsCount = 2; + + buyUpStairs.begin = currentVolume.begin; + buyUpStairs.end = nextVolume.end; + + buyUpStairs.firstAmount = currentVolume.amountBuy; + buyUpStairs.lastAmount = nextVolume.amountBuy; + + } else { + + buyUpStairs.barsCount++; + buyUpStairs.end = nextVolume.end; + buyUpStairs.lastAmount = nextVolume.amountBuy; + + } + + } else { + + if (buyUpStairs !== undefined) { + + if (buyUpStairs.barsCount > 2) { + + stairsArray.push(buyUpStairs); + } + + buyUpStairs = undefined; + } + } + + /* buy volume going down */ + + if (currentVolume.amountBuy > nextVolume.amountBuy) { + + if (buyDownStairs === undefined) { + + buyDownStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + buyDownStairs.type = 'buy'; + buyDownStairs.direction = 'down'; + buyDownStairs.barsCount = 2; + + buyDownStairs.begin = currentVolume.begin; + buyDownStairs.end = nextVolume.end; + + buyDownStairs.firstAmount = currentVolume.amountBuy; + buyDownStairs.lastAmount = nextVolume.amountBuy; + + } else { + + buyDownStairs.barsCount++; + buyDownStairs.end = nextVolume.end; + buyDownStairs.lastAmount = nextVolume.amountBuy; + + } + + } else { + + if (buyDownStairs !== undefined) { + + if (buyDownStairs.barsCount > 2) { + + stairsArray.push(buyDownStairs); + } + + buyDownStairs = undefined; + } + } + + /* sell volume going up */ + + if (currentVolume.amountSell < nextVolume.amountSell) { + + if (sellUpStairs === undefined) { + + sellUpStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + sellUpStairs.type = 'sell'; + sellUpStairs.direction = 'up'; + sellUpStairs.barsCount = 2; + + sellUpStairs.begin = currentVolume.begin; + sellUpStairs.end = nextVolume.end; + + sellUpStairs.firstAmount = currentVolume.amountSell; + sellUpStairs.lastAmount = nextVolume.amountSell; + + } else { + + sellUpStairs.barsCount++; + sellUpStairs.end = nextVolume.end; + sellUpStairs.lastAmount = nextVolume.amountSell; + + } + + } else { + + if (sellUpStairs !== undefined) { + + if (sellUpStairs.barsCount > 2) { + + stairsArray.push(sellUpStairs); + } + + sellUpStairs = undefined; + } + } + + /* sell volume going down */ + + if (currentVolume.amountSell > nextVolume.amountSell) { + + if (sellDownStairs === undefined) { + + sellDownStairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + sellDownStairs.type = 'sell'; + sellDownStairs.direction = 'down'; + sellDownStairs.barsCount = 2; + + sellDownStairs.begin = currentVolume.begin; + sellDownStairs.end = nextVolume.end; + + sellDownStairs.firstAmount = currentVolume.amountSell; + sellDownStairs.lastAmount = nextVolume.amountSell; + + } else { + + sellDownStairs.barsCount++; + sellDownStairs.end = nextVolume.end; + sellDownStairs.lastAmount = nextVolume.amountSell; + + } + + } else { + + if (sellDownStairs !== undefined) { + + if (sellDownStairs.barsCount > 2) { + + stairsArray.push(sellDownStairs); + } + + sellDownStairs = undefined; + } + } + } + + writeVolumeStairsFile(); + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> findVolumesStairs -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeVolumeStairsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < stairsArray.length; i++) { + + let stairs = stairsArray[i]; + + fileContent = fileContent + separator + '[' + + '"' + stairs.type + '"' + "," + + stairs.begin + "," + + stairs.end + "," + + '"' + stairs.direction + '"' + "," + + stairs.barsCount + "," + + stairs.firstAmount + "," + + stairs.lastAmount + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + VOLUME_STAIRS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + controlLoop(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> onFileReceived -> writeVolumeStairsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> nextVolumeFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> loopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function controlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildStairs -> controlLoop -> Entering function."); } + + n++; + + if (n < global.marketFilesPeriods.length) { + + loopBody(); + + } else { + + writeStatusReport(callBackFunction); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> controlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> buildStairs -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + + try { + + let reportKey = "AAMasters" + "-" + "AATom" + "-" + "Multi-Period-Market" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/package.json b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/package.json new file mode 100644 index 000000000..11cc6a7f1 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/package.json @@ -0,0 +1,13 @@ +{ + "name": "AAToma", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Luis-Fernando-Molina", + "license": "ISC", + "dependencies": { + } +} diff --git a/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/this.bot.config.json b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/this.bot.config.json new file mode 100644 index 000000000..2b9156602 --- /dev/null +++ b/Legacy-Code/AAMasters/bots/AATom-Indicator-Bot/this.bot.config.json @@ -0,0 +1,232 @@ +{ + "displayName": "Tom", + "codeName": "AATom", + "type": "Indicator Bot", + "version": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "dataMine": "AAMasters", + "profilePicture": "Tom.png", + "dataSetVersion": "dataSet.V1", + "processes": [ + { + "name": "Multi-Period-Market", + "description": "Produces for each market two files with candles stairs and volumes stairs respectively.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "updatesDatasets": [ + { + "product": "Candle-Stairs", + "dataSet": "Multi-Period-Market" + }, + { + "product": "Volume-Stairs", + "dataSet": "Multi-Period-Market" + } + ], + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AATom", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Market", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AAOlivia", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Market", + "dataSetVersion": "dataSet.V1", + "waitUntilNextUpdate": true + } + ] + }, + { + "name": "Multi-Period-Daily", + "description": "Produces for each market two files with candles stairs and volumes stairs respectively at each day of the market history.", + "startMode": { + "allMonths": { + "run": "false", + "minYear": "", + "maxYear": "" + }, + "oneMonth": { + "run": "false", + "year": "", + "month": "" + }, + "noTime": { + "run": "true" + }, + "fixedInterval": { + "run": "false", + "interval": 0 + } + }, + "deadWaitTime": 0, + "normalWaitTime": 0, + "retryWaitTime": 10000, + "sleepWaitTime": 3600000, + "comaWaitTime": 86400000, + "updatesDatasets": [ + { + "product": "Candle-Stairs", + "dataSet": "Multi-Period-Daily" + }, + { + "product": "Volume-Stairs", + "dataSet": "Multi-Period-Daily" + } + ], + "statusDependencies": [ + { + "dataMine": "AAMasters", + "bot": "AACharly", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Historic-Trades", + "dataSetVersion": "dataSet.V1" + }, + { + "dataMine": "AAMasters", + "bot": "AAOlivia", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Daily", + "dataSetVersion": "dataSet.V1", + "waitUntilNextUpdate": true + }, + { + "dataMine": "AAMasters", + "bot": "AATom", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Daily", + "dataSetVersion": "dataSet.V1" + } + ] + } + ], + "products": [ + { + "codeName": "Candle-Stairs", + "displayName": "Candle-Stairs Patters", + "description": "Two sets of files per period that contains candle-stairs patterns.", + "storageAccount": "aatom", + "shareWith": "Public", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candle-Stairs/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candle-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candle-Stairs/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersStairsPatterns", + "moduleName": "CandleStairs" + } + }, + { + "codeName": "Volume-Stairs", + "displayName": "Volume-Stairs Patters", + "description": "Two sets of files per period that contains volume-stairs patterns.", + "storageAccount": "aatom", + "shareWith": "Public", + "dataSets": [ + { + "codeName": "Multi-Period-Market", + "type": "Market Files", + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volume-Stairs/Multi-Period-Market/@Period", + "fileName": "@AssetA_@AssetB.json" + }, + { + "codeName": "Multi-Period-Daily", + "type": "Daily Files", + "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volume-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "fileName": "@AssetA_@AssetB.json", + "dataRange": { + "filePath": "AAMasters/AATom.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volume-Stairs/Multi-Period-Daily", + "fileName": "Data.Range.@AssetA_@AssetB.json" + } + } + ], + "exchangeList": [ + { + "name": "Poloniex" + }, + { + "name": "Coss" + } + ], + "plotter": { + "dataMine": "AAMasters", + "codeName": "PlottersStairsPatterns", + "moduleName": "VolumeStairs" + } + } + ] +} + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/BollingerBands.js b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/BollingerBands.js new file mode 100644 index 000000000..d626b8f29 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/BollingerBands.js @@ -0,0 +1,713 @@ +function newAAMastersPlottersBollingerBandsBollingerBands() { + + const MODULE_NAME = "Bands Plotter"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw, + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Secondary functions and properties. + + currentBand: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let bands = []; // Here we keep the bands to be ploted every time the Draw() function is called by the AAWebPlatform. + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + + try { + + let container; + + /* First we check if this point is inside this space. */ + + if (this.container.frame.isThisPointHere(point) === true) { + + return this.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + let newMarketFile = marketFiles.getFile(pTimeFrame); + + if (newMarketFile !== undefined) { + + marketFile = newMarketFile; + recalculate(); + } + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + if (newFileCursor !== undefined) { + + fileCursor = newFileCursor; + recalculate(); + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + try { + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + } + } + + function draw() { + + try { + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Bands Changed", bands); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (fileCursor === undefined) { return; } // We need to wait + + if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + bands = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let band = { + begin: undefined, + end: undefined, + movingAverage: undefined, + standardDeviation: undefined, + deviation: undefined + }; + + band.begin = dailyFile[i][0]; + band.end = dailyFile[i][1]; + band.movingAverage = dailyFile[i][2]; + band.standardDeviation = dailyFile[i][3]; + band.deviation = dailyFile[i][4]; + + if ( + (band.begin >= farLeftDate.valueOf() && band.end <= farRightDate.valueOf()) && + (band.begin >= coordinateSystem.min.x && band.end <= coordinateSystem.max.x) + ) { + + bands.push(band); + + if (datetime.valueOf() >= band.begin && datetime.valueOf() <= band.end) { + + thisObject.currentBand = band; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentBand); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by bands. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + if (bands.length > 0) { + + if (bands[0].begin > lowerEnd || bands[bands.length - 1].end < upperEnd) { + + setTimeout(recalculate, 2000); + + //console.log("File missing while calculating bands, scheduling a recalculation in 2 seconds."); + + } + } + + //console.log("Olivia > recalculateUsingDailyFiles > total bands generated : " + bands.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + bands = []; + + for (let i = 0; i < marketFile.length; i++) { + + let band = { + begin: undefined, + end: undefined, + movingAverage: undefined, + standardDeviation: undefined, + deviation: undefined + }; + + band.begin = marketFile[i][0]; + band.end = marketFile[i][1]; + band.movingAverage = marketFile[i][2]; + band.standardDeviation = marketFile[i][3]; + band.deviation = marketFile[i][4]; + + if ( + (band.begin >= leftDate.valueOf() && band.end <= rightDate.valueOf()) && + (band.begin >= coordinateSystem.min.x && band.end <= coordinateSystem.max.x) + ) { + + bands.push(band); + + if (datetime.valueOf() >= band.begin && datetime.valueOf() <= band.end) { + + thisObject.currentBand = band; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentBand); + + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + let band; + let previousBand; + + if (bands.length > 0) { + + /* Now we calculate and plot the bands */ + + for (let i = 1; i < bands.length; i++) { + + band = bands[i]; + previousBand = bands[i - 1]; + + /* From here we draw the actual Bollinger Bands and lines it contains. */ + + let bandPoint1 = { + x: band.begin, + y: previousBand.movingAverage - previousBand.deviation + }; + + let bandPoint2 = { + x: band.begin, + y: previousBand.movingAverage + previousBand.deviation + }; + + let bandPoint3 = { + x: band.end, + y: band.movingAverage + band.deviation + }; + + let bandPoint4 = { + x: band.end, + y: band.movingAverage - band.deviation + }; + + let bandPoint5 = { + x: band.begin, + y: previousBand.movingAverage + }; + + let bandPoint6 = { + x: band.end, + y: band.movingAverage + }; + + bandPoint1 = coordinateSystem.transformThisPoint(bandPoint1); + bandPoint2 = coordinateSystem.transformThisPoint(bandPoint2); + bandPoint3 = coordinateSystem.transformThisPoint(bandPoint3); + bandPoint4 = coordinateSystem.transformThisPoint(bandPoint4); + bandPoint5 = coordinateSystem.transformThisPoint(bandPoint5); + bandPoint6 = coordinateSystem.transformThisPoint(bandPoint6); + + bandPoint1 = transformThisPoint(bandPoint1, thisObject.container); + bandPoint2 = transformThisPoint(bandPoint2, thisObject.container); + bandPoint3 = transformThisPoint(bandPoint3, thisObject.container); + bandPoint4 = transformThisPoint(bandPoint4, thisObject.container); + bandPoint5 = transformThisPoint(bandPoint5, thisObject.container); + bandPoint6 = transformThisPoint(bandPoint6, thisObject.container); + + if (bandPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || bandPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + continue; + } + + if (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT) { + let diffA = bandPoint1.y - canvas.chartingSpace.viewport.visibleArea.bottomLeft.y + if (diffA > 0) { + bandPoint1.y = bandPoint1.y - diffA + bandPoint2.y = bandPoint2.y - diffA + bandPoint3.y = bandPoint3.y - diffA + bandPoint4.y = bandPoint4.y - diffA + bandPoint5.y = bandPoint5.y - diffA + bandPoint6.y = bandPoint6.y - diffA + } + + let diffB = bandPoint2.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y + if (diffB < 0) { + bandPoint1.y = bandPoint1.y - diffB + bandPoint2.y = bandPoint2.y - diffB + bandPoint3.y = bandPoint3.y - diffB + bandPoint4.y = bandPoint4.y - diffB + bandPoint5.y = bandPoint5.y - diffB + bandPoint6.y = bandPoint6.y - diffB + } + } + + bandPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint1); + bandPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint2); + bandPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint3); + bandPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint4); + bandPoint5 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint5); + bandPoint6 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint6); + + bandPoint1 = thisObject.fitFunction(bandPoint1); + bandPoint2 = thisObject.fitFunction(bandPoint2); + bandPoint3 = thisObject.fitFunction(bandPoint3); + bandPoint4 = thisObject.fitFunction(bandPoint4); + bandPoint5 = thisObject.fitFunction(bandPoint5); + bandPoint6 = thisObject.fitFunction(bandPoint6); + + + /* First we are drawing a semi-transparent background */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y); + browserCanvasContext.lineTo(bandPoint2.x, bandPoint2.y); + browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y); + browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 0.05)'; + + if (userPositionDate >= band.begin && userPositionDate <= band.end) { + /* highlight the current band */ + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.05)'; // Current band accroding to time + } + + if ( + bandPoint1.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 + || + bandPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50 + ) { + // we leave this bands without fill. + } else { + browserCanvasContext.fill(); + } + + + + + /* Next we are drawing the outter bands lines */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y); + browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y); + browserCanvasContext.moveTo(bandPoint2.x, bandPoint2.y); + browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'; + + if (userPositionDate >= band.begin && userPositionDate <= band.end) { + + /* highlight the current band */ + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current band accroding to time + + let currentBand = { + bodyWidth: bandPoint3.x - bandPoint2.x, + leftBodyHeight: bandPoint2.y - bandPoint1.y, + rightBodyHeight: bandPoint3.y - bandPoint4.y, + topDelta: bandPoint3.y - bandPoint2.y, + bottomDelta: bandPoint4.y - bandPoint1.y, + period: timeFrame, + innerBand: band + }; + + thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentBand); + } + + browserCanvasContext.lineWidth = 0.2; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + + + /* Finally we draw the moving average line */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(bandPoint5.x, bandPoint5.y); + browserCanvasContext.lineTo(bandPoint6.x, bandPoint6.y); + + browserCanvasContext.closePath(); + + if (bandPoint5.y > bandPoint6.y) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)'; + } + + if (userPositionDate >= band.begin && userPositionDate <= band.end) { + /* highlight the current band */ + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current band accroding to time + } + + browserCanvasContext.lineWidth = 0.2; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + /* Contributing to Auto-Scale*/ + coordinateSystem.reportYValue(band.movingAverage + band.deviation) + coordinateSystem.reportYValue(band.movingAverage - band.deviation) + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + + function onViewportZoomChanged(event) { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + } + } + + function onViewportPositionChanged(event) { + + try { + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/BollingerBandsPanel.js b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/BollingerBandsPanel.js new file mode 100644 index 000000000..75c9543d3 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/BollingerBandsPanel.js @@ -0,0 +1,243 @@ + +function newAAMastersPlottersBollingerBandsBollingerBandsBollingerBandsPanel () { + + var thisObject = { + fitFunction: undefined, + onRecordChange: onRecordChange, + container: undefined, + isVisible: true, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + }; + + thisObject.container = newContainer(); + thisObject.container.initialize(); + thisObject.container.frame.containerName = "Bollinger Band"; + + let currentBand; + let panelTabButton + + return thisObject; + + function finalize() { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.isVisible = undefined + + currentBand = undefined + panelTabButton.finalize() + panelTabButton = undefined + } + + function initialize() { + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL; + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL; + + thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 3; + thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height; + + panelTabButton = newPanelTabButton() + panelTabButton.parentContainer = thisObject.container + panelTabButton.container.frame.parentFrame = thisObject.container.frame + panelTabButton.fitFunction = thisObject.fitFunction + panelTabButton.initialize() + } + + function getContainer(point) { + if (thisObject.isVisible !== true) { return } + let container; + + container = panelTabButton.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container; + } + } + } + + + function onRecordChange(lastCurrentBand) { + + currentBand = lastCurrentBand; + + } + + + function draw() { + if (thisObject.isVisible !== true) { return } + thisObject.container.frame.draw(false, false, true, thisObject.fitFunction); + + plotCurrentBandInfo(); + + panelTabButton.draw() + } + + + function plotCurrentBandInfo() { + + if (currentBand === undefined) { return; } + if (currentBand.innerBand === undefined) { return; } + + const frameBodyHeight = thisObject.container.frame.getBodyHeight(); + const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight; + + const X_AXIS = thisObject.container.frame.width / 2; + const Y_AXIS = frameTitleHeight + frameBodyHeight / 2; + + var bandPoint1 = { + x: X_AXIS - currentBand.bodyWidth / 2, + y: Y_AXIS - currentBand.leftBodyHeight / 2 - currentBand.bottomDelta / 4 + }; + + var bandPoint2 = { + x: X_AXIS - currentBand.bodyWidth / 2, + y: Y_AXIS + currentBand.leftBodyHeight / 2 - currentBand.topDelta / 4 + }; + + var bandPoint3 = { + x: X_AXIS + currentBand.bodyWidth / 2, + y: Y_AXIS + currentBand.rightBodyHeight / 2 + currentBand.topDelta / 4 + }; + + var bandPoint4 = { + x: X_AXIS + currentBand.bodyWidth / 2, + y: Y_AXIS - currentBand.rightBodyHeight / 2 + currentBand.bottomDelta / 4 + }; + + + + /* Extra bounderies due to constrained space */ + + const upperLimit = frameTitleHeight + frameBodyHeight * 0.15; + const downLimit = frameTitleHeight + frameBodyHeight * 0.85; + + if (bandPoint1.y > downLimit) { bandPoint1.y = downLimit; } + if (bandPoint4.y > downLimit) { bandPoint4.y = downLimit; } + + if (bandPoint2.y < upperLimit) { bandPoint2.y = upperLimit; } + if (bandPoint3.y < upperLimit) { bandPoint3.y = upperLimit; } + + bandPoint1 = thisObject.container.frame.frameThisPoint(bandPoint1); + bandPoint2 = thisObject.container.frame.frameThisPoint(bandPoint2); + bandPoint3 = thisObject.container.frame.frameThisPoint(bandPoint3); + bandPoint4 = thisObject.container.frame.frameThisPoint(bandPoint4); + + bandPoint1 = thisObject.fitFunction(bandPoint1) + bandPoint2 = thisObject.fitFunction(bandPoint2) + bandPoint3 = thisObject.fitFunction(bandPoint3) + bandPoint4 = thisObject.fitFunction(bandPoint4) + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y); + browserCanvasContext.lineTo(bandPoint2.x, bandPoint2.y); + browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y); + browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y); + + browserCanvasContext.closePath(); + + const OPACITY = '0.1'; + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + OPACITY + ')'; + + browserCanvasContext.fill(); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y); + browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y); + browserCanvasContext.moveTo(bandPoint2.x, bandPoint2.y); + browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'; + + browserCanvasContext.lineWidth = 0.2; + browserCanvasContext.stroke(); + + browserCanvasContext.beginPath(); + + let y1 = bandPoint1.y + (bandPoint2.y - bandPoint1.y) / 2; + let y2 = bandPoint4.y - (bandPoint4.y - bandPoint3.y) / 2; + + browserCanvasContext.moveTo(bandPoint1.x, y1); + browserCanvasContext.lineTo(bandPoint4.x, y2); + + + browserCanvasContext.closePath(); + + if (y1 > y2) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)'; + } + + browserCanvasContext.lineWidth = 0.2; + browserCanvasContext.stroke(); + + + browserCanvasContext.beginPath(); + + /* put the labels with the bands values */ + + + + + let y; + + printLabel('Moving Average', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '1'); + printLabel(currentBand.innerBand.movingAverage, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '0.50'); + + printLabel('Deviation', X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '1'); + printLabel(currentBand.innerBand.deviation, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '0.50'); + + function printLabel(labelToPrint, x, y, opacity) { + + let labelPoint; + let fontSize = 10; + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira'; + + let label = '' + labelToPrint; + if (isNaN(label) === false) { + label = Number(label).toLocaleString(); + } + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; + + labelPoint = { + x: x - xOffset, + y: y + }; + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); + + labelPoint = thisObject.fitFunction(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'; + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); + + } + + browserCanvasContext.closePath(); + browserCanvasContext.fill(); + + } + + +} + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/PercentageBandwidth.js b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/PercentageBandwidth.js new file mode 100644 index 000000000..c7eb5f186 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/PercentageBandwidth.js @@ -0,0 +1,921 @@ +function newAAMastersPlottersBollingerBandsPercentageBandwidth() { + + const MODULE_NAME = "Bands Plotter"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + draw: draw, + recalculateScale: recalculateScale, + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Secondary functions and properties. + + currentPercentageBandwidth: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let plotAreaFrame = newCoordinateSystem(); // Used for full frame view. + let plotAreaViewport = newCoordinateSystem(); // Used for viewport view. + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let percentageBandwidthArray = []; // Here we keep the percentageBandwidthArray to be ploted every time the Draw() function is called by the AAWebPlatform. + + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, callBackFunction) { + + try { + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + recalculateScaleY(); + recalculateScaleX(); + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + + /* Get ready for plotting. */ + + recalculate(); + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculateScaleY(); + recalculateScaleX(); + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function recalculateScale() { + + recalculateScaleY(); + recalculateScaleX(); + + } + + function getContainer(point) { + + try { + + let container; + + /* First we check if this point is inside this space. */ + + if (this.container.frame.isThisPointHere(point) === true) { + + return this.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + let newMarketFile = marketFiles.getFile(pTimeFrame); + + if (newMarketFile !== undefined) { + + marketFile = newMarketFile; + recalculate(); + } + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + if (newFileCursor !== undefined) { + + fileCursor = newFileCursor; + recalculate(); + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + + datetime = pDatetime; + + } + + function onDailyFileLoaded(event) { + + try { + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + } + } + + function draw() { + + try { + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Bands Changed", percentageBandwidthArray); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (fileCursor === undefined) { return; } // We need to wait + + if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, plotAreaFrame); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, plotAreaFrame); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + percentageBandwidthArray = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let percentageBandwidth = { + begin: undefined, + end: undefined, + value: undefined, + movingAverage: undefined, + bandwidth: undefined + }; + + percentageBandwidth.begin = dailyFile[i][0]; + percentageBandwidth.end = dailyFile[i][1]; + percentageBandwidth.value = dailyFile[i][2]; + percentageBandwidth.movingAverage = dailyFile[i][3]; + percentageBandwidth.bandwidth = dailyFile[i][4]; + + if (percentageBandwidth.begin >= farLeftDate.valueOf() && percentageBandwidth.end <= farRightDate.valueOf()) { + + percentageBandwidthArray.push(percentageBandwidth); + + if (datetime.valueOf() >= percentageBandwidth.begin && datetime.valueOf() <= percentageBandwidth.end) { + + thisObject.currentPercentageBandwidth = percentageBandwidth; + thisObject.container.eventHandler.raiseEvent("Current Percentage Bandwidth Changed", thisObject.currentPercentageBandwidth); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by percentageBandwidthArray. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + if (percentageBandwidthArray.length > 0) { + + if (percentageBandwidthArray[0].begin > lowerEnd || percentageBandwidthArray[percentageBandwidthArray.length - 1].end < upperEnd) { + + setTimeout(recalculate, 2000); + + //console.log("File missing while calculating percentageBandwidthArray, scheduling a recalculation in 2 seconds."); + + } + } + + //console.log("Olivia > recalculateUsingDailyFiles > total percentageBandwidthArray generated : " + percentageBandwidthArray.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, plotAreaFrame); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, plotAreaFrame); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + percentageBandwidthArray = []; + + for (let i = 0; i < marketFile.length; i++) { + + let percentageBandwidth = { + begin: undefined, + end: undefined, + value: undefined, + movingAverage: undefined, + bandwidth: undefined + }; + + percentageBandwidth.begin = marketFile[i][0]; + percentageBandwidth.end = marketFile[i][1]; + percentageBandwidth.value = marketFile[i][2]; + percentageBandwidth.movingAverage = marketFile[i][3]; + percentageBandwidth.bandwidth = marketFile[i][4]; + + if (percentageBandwidth.begin >= leftDate.valueOf() && percentageBandwidth.end <= rightDate.valueOf()) { + + percentageBandwidthArray.push(percentageBandwidth); + + if (datetime.valueOf() >= percentageBandwidth.begin && datetime.valueOf() <= percentageBandwidth.end) { + + thisObject.currentPercentageBandwidth = percentageBandwidth; + thisObject.container.eventHandler.raiseEvent("Current Percentage Bandwidth Changed", thisObject.currentPercentageBandwidth); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total percentageBandwidthArray generated : " + percentageBandwidthArray.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function recalculateScaleX() { + + try { + + var minValue = { + x: MIN_PLOTABLE_DATE.valueOf() + }; + + var maxValue = { + x: MAX_PLOTABLE_DATE.valueOf() + }; + + plotAreaViewport.initializeX( + minValue, + maxValue, + thisObject.container.frame.width + ); + + plotAreaFrame.initializeX( + minValue, + maxValue, + thisObject.container.frame.width + ); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScaleX -> err = " + err.stack); } + } + } + + function recalculateScaleY() { + + try { + + var minValue = { + y: 0 + }; + + var maxValue = { + y: 0 + }; + + maxValue.y = 100; + + plotAreaViewport.initializeY( + minValue, + maxValue, + canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y + ); + + plotAreaFrame.initializeY( + minValue, + maxValue, + thisObject.container.frame.height + ); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScaleY -> err = " + err.stack); } + } + } + + + function plotChart() { + + try { + + let userPosition = getUserPosition() + let userPositionDate = userPosition.point.x + + let percentageBandwidth; + let previousBand; + + if (percentageBandwidthArray.length > 0) { + + /* This next section is to get ready in order to be able to plot dinamically constrained to the viewport */ + + let visibleHeight = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y; + + let frameCorner1 = { + x: 0, + y: 0 + }; + + let frameCorner2 = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + }; + + /* Now the transformations. */ + + frameCorner1 = transformThisPoint(frameCorner1, thisObject.container.frame.container); + frameCorner2 = transformThisPoint(frameCorner2, thisObject.container.frame.container); + + let frameHeightInViewPort = frameCorner2.y - frameCorner1.y; + let pbChartHeight = 20; + let pbOffset = 100 / 5 * 4; + + /* Now we calculate and plot the percentageBandwidthArray */ + + for (let i = 1; i < percentageBandwidthArray.length; i++) { + + percentageBandwidth = percentageBandwidthArray[i]; + previousBand = percentageBandwidthArray[i - 1]; + + /* Here we will draw the percent bandwidth chart */ + + let currentPercentBandwidth = percentageBandwidth.value * pbChartHeight / 100; + if (currentPercentBandwidth > pbChartHeight) { currentPercentBandwidth = pbChartHeight }; + if (currentPercentBandwidth < 0) { currentPercentBandwidth = 0 }; + + let previousPercentBandwidth = previousBand.value * pbChartHeight / 100; + if (previousPercentBandwidth > pbChartHeight) { previousPercentBandwidth = pbChartHeight }; + if (previousPercentBandwidth < 0) { previousPercentBandwidth = 0 }; + + let currentMovingAverage = percentageBandwidth.movingAverage * pbChartHeight / 100; + let previousMovingAverage = previousBand.movingAverage * pbChartHeight / 100; + + let currentBandwidth = percentageBandwidth.bandwidth * 100 * pbChartHeight / 100; + let previousBandwidth = previousBand.bandwidth * 100 * pbChartHeight / 100; + + let pbPoint1; + let pbPoint2; + let pbPoint3; + let pbPoint4; + let pbPoint5; + let pbPoint6; + let pbPoint7; + let pbPoint8; + let pbPoint9; + let pbPoint10; + + function calculateCoordinates(plot, height) { + + pbPoint1 = { + x: percentageBandwidth.begin, + y: previousPercentBandwidth + pbOffset + }; + + pbPoint2 = { + x: percentageBandwidth.end, + y: currentPercentBandwidth + pbOffset + }; + + /* Points needed for the lines */ + + pbPoint3 = { + x: 0, + y: 0 * pbChartHeight / 100 + pbOffset + }; + + pbPoint4 = { + x: 0, + y: 30 * pbChartHeight / 100 + pbOffset + }; + + pbPoint5 = { + x: 0, + y: 70 * pbChartHeight / 100 + pbOffset + }; + + pbPoint6 = { + x: 0, + y: 100 * pbChartHeight / 100 + pbOffset + }; + + /* Moving Average */ + + pbPoint7 = { + x: percentageBandwidth.begin, + y: previousMovingAverage + pbOffset + }; + + pbPoint8 = { + x: percentageBandwidth.end, + y: currentMovingAverage + pbOffset + }; + + /* Bandwidth */ + + pbPoint9 = { + x: percentageBandwidth.begin, + y: previousBandwidth + pbOffset + }; + + pbPoint10 = { + x: percentageBandwidth.end, + y: currentBandwidth + pbOffset + }; + + pbPoint1 = plot.transformThisPoint(pbPoint1); + pbPoint2 = plot.transformThisPoint(pbPoint2); + + pbPoint3 = plot.transformThisPoint(pbPoint3); + pbPoint4 = plot.transformThisPoint(pbPoint4); + pbPoint5 = plot.transformThisPoint(pbPoint5); + pbPoint6 = plot.transformThisPoint(pbPoint6); + + pbPoint7 = plot.transformThisPoint(pbPoint7); + pbPoint8 = plot.transformThisPoint(pbPoint8); + + pbPoint9 = plot.transformThisPoint(pbPoint9); + pbPoint10 = plot.transformThisPoint(pbPoint10); + + pbPoint1 = transformThisPoint(pbPoint1, thisObject.container); + pbPoint2 = transformThisPoint(pbPoint2, thisObject.container); + + pbPoint3 = transformThisPoint(pbPoint3, thisObject.container); + pbPoint4 = transformThisPoint(pbPoint4, thisObject.container); + pbPoint5 = transformThisPoint(pbPoint5, thisObject.container); + pbPoint6 = transformThisPoint(pbPoint6, thisObject.container); + + pbPoint7 = transformThisPoint(pbPoint7, thisObject.container); + pbPoint8 = transformThisPoint(pbPoint8, thisObject.container); + + pbPoint9 = transformThisPoint(pbPoint9, thisObject.container); + pbPoint10 = transformThisPoint(pbPoint10, thisObject.container); + + if (pbPoint1.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || pbPoint2.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + return false; + } + + return true; + + } + + if (calculateCoordinates(plotAreaFrame, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area. + + if (frameHeightInViewPort > visibleHeight * 2 / 3) { + + if (calculateCoordinates(plotAreaViewport, visibleHeight) === false) { + continue; + } // We snap t to the view port. + + /* Now we set the real value of y. */ + + pbPoint1.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (previousPercentBandwidth + pbOffset) * plotAreaViewport.scale.y; + pbPoint2.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (currentPercentBandwidth + pbOffset) * plotAreaViewport.scale.y; + + pbPoint3.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (0 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y; + pbPoint4.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (30 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y; + pbPoint5.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (70 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y; + pbPoint6.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (100 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y; + + pbPoint7.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (previousMovingAverage + pbOffset) * plotAreaViewport.scale.y; + pbPoint8.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (currentMovingAverage + pbOffset) * plotAreaViewport.scale.y; + + pbPoint9.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (previousBandwidth + pbOffset) * plotAreaViewport.scale.y; + pbPoint10.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (currentBandwidth + pbOffset) * plotAreaViewport.scale.y; + + } + + /* Everything must fit within the visible area */ + + pbPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint1); + pbPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint2); + + pbPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint3); + pbPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint4); + pbPoint5 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint5); + pbPoint6 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint6); + + pbPoint7 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint7); + pbPoint8 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint8); + + pbPoint9 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint9); + pbPoint10 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint10); + + + /* Now the drawing of the lines */ + + let bgOpacityA = 0.05; + let bgOpacityB = 0.05; + let bgOpacityC = 0.05; + + let fgOpacity = '1'; + + let lineWidthA = 0.2; + let lineWidthB = 0.2; + let lineWidthC = 0.2; + + if (pbPoint1.y > pbPoint4.y || pbPoint2.y > pbPoint4.y) { + bgOpacityA = bgOpacityA * 2; + } + + if (pbPoint1.y < pbPoint5.y || pbPoint2.y < pbPoint5.y) { + bgOpacityC = bgOpacityC * 2; + } + + if (pbPoint1.y >= pbPoint3.y || pbPoint2.y >= pbPoint3.y) { + bgOpacityA = bgOpacityA * 1.5; + lineWidthA = lineWidthA * 2; + } + + if (pbPoint1.y <= pbPoint6.y || pbPoint2.y <= pbPoint6.y) { + bgOpacityC = bgOpacityC * 1.5; + lineWidthC = lineWidthC * 2; + } + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint1.x, pbPoint3.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint3.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint4.y); + browserCanvasContext.lineTo(pbPoint1.x, pbPoint4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ',' + bgOpacityA + ' )'; + browserCanvasContext.fill(); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint1.x, pbPoint4.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint4.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint5.y); + browserCanvasContext.lineTo(pbPoint1.x, pbPoint5.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + bgOpacityB + ')'; + browserCanvasContext.fill(); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint1.x, pbPoint5.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint5.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint6.y); + browserCanvasContext.lineTo(pbPoint1.x, pbPoint6.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + bgOpacityC + ')'; + browserCanvasContext.fill(); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint1.x, pbPoint3.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint3.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + fgOpacity + ')'; + browserCanvasContext.lineWidth = lineWidthA; + browserCanvasContext.stroke(); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint1.x, pbPoint4.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint4.y); + browserCanvasContext.moveTo(pbPoint1.x, pbPoint5.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint5.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + fgOpacity + ')'; + browserCanvasContext.lineWidth = lineWidthB; + browserCanvasContext.stroke(); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint1.x, pbPoint6.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint6.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + fgOpacity + ')'; + browserCanvasContext.lineWidth = lineWidthC; + browserCanvasContext.stroke(); + + /* Now the drawing of the percentage bandwidth*/ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint1.x, pbPoint1.y); + browserCanvasContext.lineTo(pbPoint2.x, pbPoint2.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', 1)'; + + if (userPositionDate >= percentageBandwidth.begin && userPositionDate <= percentageBandwidth.end) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)'; + } + + browserCanvasContext.lineWidth = 0.2; + browserCanvasContext.stroke(); + + /* Now the drawing of the moving average*/ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint7.x, pbPoint7.y); + browserCanvasContext.lineTo(pbPoint8.x, pbPoint8.y); + + browserCanvasContext.closePath(); + + if (pbPoint7.y > pbPoint8.y) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)'; + } + + if (userPositionDate >= percentageBandwidth.begin && userPositionDate <= percentageBandwidth.end) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)'; + } + + browserCanvasContext.lineWidth = 0.2; + browserCanvasContext.stroke(); + + /* Now the drawing of the bandwidth*/ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(pbPoint9.x, pbPoint9.y); + browserCanvasContext.lineTo(pbPoint10.x, pbPoint10.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', 1)'; + + if (userPositionDate >= percentageBandwidth.begin && userPositionDate <= percentageBandwidth.end) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)'; + } + + browserCanvasContext.setLineDash([1, 4]) + browserCanvasContext.lineWidth = 1 + browserCanvasContext.stroke() + browserCanvasContext.setLineDash([0, 0]) + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + + function onViewportZoomChanged(event) { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + } + } + + function onViewportPositionChanged(event) { + + try { + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/this.plotter.config.json b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/this.plotter.config.json new file mode 100644 index 000000000..19c0016e5 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Bollinger-Bands/this.plotter.config.json @@ -0,0 +1,28 @@ +{ + "displayName": "Plotters Bollinger Bands", + "codeName": "PlottersBollingerBands", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "BollingerBands", + "moduleName": "BollingerBands", + "description": "Plots Bollinger Bands.", + "profilePicture": "BollingerBands.png", + "panels": [ + { + "codeName": "BollingerBandsPanel", + "moduleName": "BollingerBandsPanel", + "event": "Current Record Changed" + } + ] + }, + { + "codeName": "PercentageBandwidth", + "moduleName": "PercentageBandwidth", + "description": "Plots Bollinger Bands.", + "profilePicture": "PercentageBandwidth.png", + "panels": [ + ] + } + ] +} \ No newline at end of file diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/CandlePanel.js b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/CandlePanel.js new file mode 100644 index 000000000..4ee162d6d --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/CandlePanel.js @@ -0,0 +1,333 @@ + +function newAAMastersPlottersCandlesVolumesCandlesCandlePanel() { + + let thisObject = { + fitFunction: undefined, + container: undefined, + isVisible: true, + onRecordChange: onRecordChange, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + }; + + thisObject.container = newContainer(); + thisObject.container.initialize(); + thisObject.container.frame.containerName = "Candle"; + + let currentCandle; + let panelTabButton + + return thisObject; + + function finalize() { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.isVisible = undefined + + currentCandle = undefined + panelTabButton.finalize() + panelTabButton = undefined + } + + function initialize() { + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL; + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL; + + thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 2; + thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height; + + panelTabButton = newPanelTabButton() + panelTabButton.parentContainer = thisObject.container + panelTabButton.container.frame.parentFrame = thisObject.container.frame + panelTabButton.fitFunction = thisObject.fitFunction + panelTabButton.initialize() + } + + function getContainer(point) { + if (thisObject.isVisible !== true) { return } + let container; + + container = panelTabButton.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container; + } + } + } + + + function onRecordChange(lastCurrentCandle) { + + currentCandle = lastCurrentCandle; + + } + + + function draw() { + if (thisObject.isVisible !== true) { return } + thisObject.container.frame.draw(false, false, true, thisObject.fitFunction); + + plotCurrentCandleInfo(); + + panelTabButton.draw() + + } + + + function plotCurrentCandleInfo() { + + if (currentCandle === undefined) { return; } + if (currentCandle.innerCandle === undefined) { return; } + + const frameBodyHeight = thisObject.container.frame.getBodyHeight(); + const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight; + + const X_AXIS = thisObject.container.frame.width / 2; + const Y_AXIS = frameTitleHeight + frameBodyHeight / 2; + + let candlePoint1 = { + x: X_AXIS - currentCandle.bodyWidth / 2, + y: Y_AXIS - currentCandle.bodyHeight / 2 + }; + + let candlePoint2 = { + x: X_AXIS + currentCandle.bodyWidth / 2, + y: Y_AXIS - currentCandle.bodyHeight / 2 + }; + + let candlePoint3 = { + x: X_AXIS + currentCandle.bodyWidth / 2, + y: Y_AXIS + currentCandle.bodyHeight / 2 + }; + + let candlePoint4 = { + x: X_AXIS - currentCandle.bodyWidth / 2, + y: Y_AXIS + currentCandle.bodyHeight / 2 + }; + + + let stickPoint1 = { + x: X_AXIS - currentCandle.stickWidth / 2, + y: candlePoint1.y - currentCandle.stickStart + }; + + let stickPoint2 = { + x: X_AXIS + currentCandle.stickWidth / 2, + y: candlePoint1.y - currentCandle.stickStart + }; + + let stickPoint3 = { + x: X_AXIS + currentCandle.stickWidth / 2, + y: candlePoint1.y - currentCandle.stickStart + currentCandle.stickHeight + }; + + let stickPoint4 = { + x: X_AXIS - currentCandle.stickWidth / 2, + y: candlePoint1.y - currentCandle.stickStart + currentCandle.stickHeight + }; + + + /* Extra bounderies due to constrained space */ + + const upperLimit = frameTitleHeight + frameBodyHeight * 0.15; + const downLimit = frameTitleHeight + frameBodyHeight * 0.85; + + if (candlePoint1.y < upperLimit) { + + candlePoint1.y = upperLimit; + candlePoint2.y = upperLimit; + + } + + if (stickPoint1.y < upperLimit) { + + stickPoint1.y = upperLimit; + stickPoint2.y = upperLimit; + + } + + if (candlePoint3.y < upperLimit) { + + candlePoint3.y = upperLimit; + candlePoint4.y = upperLimit; + + } + + if (stickPoint3.y < upperLimit) { + + stickPoint3.y = upperLimit; + stickPoint4.y = upperLimit; + + } + + if (candlePoint1.y > downLimit) { + + candlePoint1.y = downLimit; + candlePoint2.y = downLimit; + + } + + if (stickPoint1.y > downLimit) { + + stickPoint1.y = downLimit; + stickPoint2.y = downLimit; + + } + + if (candlePoint3.y > downLimit) { + + candlePoint3.y = downLimit; + candlePoint4.y = downLimit; + + } + + if (stickPoint3.y > downLimit) { + + stickPoint3.y = downLimit; + stickPoint4.y = downLimit; + + } + + + candlePoint1 = thisObject.container.frame.frameThisPoint(candlePoint1); + candlePoint2 = thisObject.container.frame.frameThisPoint(candlePoint2); + candlePoint3 = thisObject.container.frame.frameThisPoint(candlePoint3); + candlePoint4 = thisObject.container.frame.frameThisPoint(candlePoint4); + + candlePoint1 = thisObject.fitFunction(candlePoint1) + candlePoint2 = thisObject.fitFunction(candlePoint2) + candlePoint3 = thisObject.fitFunction(candlePoint3) + candlePoint4 = thisObject.fitFunction(candlePoint4) + + stickPoint1 = thisObject.container.frame.frameThisPoint(stickPoint1); + stickPoint2 = thisObject.container.frame.frameThisPoint(stickPoint2); + stickPoint3 = thisObject.container.frame.frameThisPoint(stickPoint3); + stickPoint4 = thisObject.container.frame.frameThisPoint(stickPoint4); + + stickPoint1 = thisObject.fitFunction(stickPoint1) + stickPoint2 = thisObject.fitFunction(stickPoint2) + stickPoint3 = thisObject.fitFunction(stickPoint3) + stickPoint4 = thisObject.fitFunction(stickPoint4) + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(stickPoint1.x, stickPoint1.y); + browserCanvasContext.lineTo(stickPoint2.x, stickPoint2.y); + browserCanvasContext.lineTo(stickPoint3.x, stickPoint3.y); + browserCanvasContext.lineTo(stickPoint4.x, stickPoint4.y); + + browserCanvasContext.closePath(); + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'; + browserCanvasContext.fill(); + + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(candlePoint1.x, candlePoint1.y); + browserCanvasContext.lineTo(candlePoint2.x, candlePoint2.y); + browserCanvasContext.lineTo(candlePoint3.x, candlePoint3.y); + browserCanvasContext.lineTo(candlePoint4.x, candlePoint4.y); + + browserCanvasContext.closePath(); + + const OPACITY = '1'; + + if (currentCandle.innerCandle.direction === 'up') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + OPACITY + ')'; } + if (currentCandle.innerCandle.direction === 'down') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')'; } + if (currentCandle.innerCandle.direction === 'side') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', ' + OPACITY + ')'; } + + if (currentCandle.innerCandle.direction === 'up') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')'; } + if (currentCandle.innerCandle.direction === 'down') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')'; } + if (currentCandle.innerCandle.direction === 'side') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + OPACITY + ')'; } + + browserCanvasContext.fill(); + + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + + browserCanvasContext.beginPath(); + + /* put the labels with the candles values */ + + + + + let y; + + printLabel('Max', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '1'); + printLabel(currentCandle.innerCandle.max, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '0.50'); + + printLabel('Min', X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '1'); + printLabel(currentCandle.innerCandle.min, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '0.50'); + + + y = Y_AXIS - currentCandle.bodyHeight / 2; + + if (y < frameTitleHeight + frameBodyHeight * 0.15) { y = frameTitleHeight + frameBodyHeight * 0.15; } + if (y > frameTitleHeight + frameBodyHeight * 0.80) { y = frameTitleHeight + frameBodyHeight * 0.80; } + + printLabel('Open', X_AXIS * 1 / 2, y, '1'); + printLabel(currentCandle.innerCandle.open, X_AXIS * 1 / 2, y + frameBodyHeight * 0.05, '0.50'); + + y = Y_AXIS + currentCandle.bodyHeight / 2; + + if (y < frameTitleHeight + frameBodyHeight * 0.15) { y = frameTitleHeight + frameBodyHeight * 0.15; } + if (y > frameTitleHeight + frameBodyHeight * 0.80) { y = frameTitleHeight + frameBodyHeight * 0.80; } + + printLabel('Close', X_AXIS * 3 / 2, y, '1'); + printLabel(currentCandle.innerCandle.close, X_AXIS * 3 / 2, y + frameBodyHeight * 0.05, '0.50'); + + + + function printLabel(labelToPrint, x, y, opacity) { + + let labelPoint; + let fontSize = 10; + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira'; + + let label = '' + labelToPrint; + if (isNaN(label) === false) { + label = Number(label).toLocaleString(); + } + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; + + labelPoint = { + x: x - xOffset, + y: y + }; + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); + labelPoint = thisObject.fitFunction(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'; + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); + + } + + browserCanvasContext.closePath(); + browserCanvasContext.fill(); + + } + + +} + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/Candles.js b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/Candles.js new file mode 100644 index 000000000..adabe9ed2 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/Candles.js @@ -0,0 +1,786 @@ +function newAAMastersPlottersCandlesVolumesCandles() { + + const MODULE_NAME = "Candles Plotter"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw, + onDailyFileLoaded: onDailyFileLoaded, + currentCandle: undefined + }; + + /* this is part of the module template */ + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let candles = []; // Here we keep the candles to be ploted every time the Draw() function is called by the AAWebPlatform. + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + + try { + + let container; + + /* First we check if this point is inside this space. */ + + if (thisObject.container.frame.isThisPointHere(point) === true) { + + return thisObject.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + if (timeFrame !== pTimeFrame) { + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + let newMarketFile = marketFiles.getFile(pTimeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + + } else { + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + try { + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + thisObject.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Candles Changed", candles); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (fileCursor === undefined) { return; } // We need to wait + + if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + candles = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = dailyFile[i][0]; + candle.max = dailyFile[i][1]; + + candle.open = dailyFile[i][2]; + candle.close = dailyFile[i][3]; + + candle.begin = dailyFile[i][4]; + candle.end = dailyFile[i][5]; + + if (candle.open > candle.close) { candle.direction = 'down'; } + if (candle.open < candle.close) { candle.direction = 'up'; } + if (candle.open === candle.close) { candle.direction = 'side'; } + + /* Contributing to Auto-Scale*/ + if (i === 0) { + coordinateSystem.reportXValue(candle.begin) + } + if (i === dailyFile.length - 1) { + coordinateSystem.reportXValue(candle.end) + } + + if ( + (candle.begin >= farLeftDate.valueOf() && candle.end <= farRightDate.valueOf()) && + (candle.begin >= coordinateSystem.min.x && candle.end <= coordinateSystem.max.x) + ) { + + candles.push(candle); + + if (datetime.valueOf() >= candle.begin && datetime.valueOf() <= candle.end) { + + thisObject.currentCandle = candle; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentCandle); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by candles. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + if (candles.length > 0) { + if (candles[0].begin > lowerEnd || candles[candles.length - 1].end < upperEnd) { + setTimeout(recalculate, 2000); + } + } + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + candles = []; + + for (let i = 0; i < marketFile.length; i++) { + + let candle = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined + }; + + candle.min = marketFile[i][0]; + candle.max = marketFile[i][1]; + + candle.open = marketFile[i][2]; + candle.close = marketFile[i][3]; + + candle.begin = marketFile[i][4]; + candle.end = marketFile[i][5]; + + if (candle.open > candle.close) { candle.direction = 'down'; } + if (candle.open < candle.close) { candle.direction = 'up'; } + if (candle.open === candle.close) { candle.direction = 'side'; } + + /* Contributing to Auto-Scale*/ + if (i === 0) { + coordinateSystem.reportXValue(candle.begin) + } + if (i === marketFile.length - 1) { + coordinateSystem.reportXValue(candle.end) + } + + if ( + (candle.begin >= leftDate.valueOf() && candle.end <= rightDate.valueOf()) && + (candle.begin >= coordinateSystem.min.x && candle.end <= coordinateSystem.max.x) + ) + { + + candles.push(candle); + + if (datetime.valueOf() >= candle.begin && datetime.valueOf() <= candle.end) { + + thisObject.currentCandle = candle; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentCandle); + + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + if (candles.length > 0) { + + /* Now we calculate all the points. */ + for (let i = 0; i < candles.length; i++) { + + candle = candles[i]; + + candle.candlePoint1 = { + x: candle.begin + timeFrame / 7 * 1.5, + y: candle.open + }; + + candle.candlePoint2 = { + x: candle.begin + timeFrame / 7 * 5.5, + y: candle.open + }; + + candle.candlePoint3 = { + x: candle.begin + timeFrame / 7 * 5.5, + y: candle.close + }; + + candle.candlePoint4 = { + x: candle.begin + timeFrame / 7 * 1.5, + y: candle.close + }; + + candle.candlePoint1 = coordinateSystem.transformThisPoint(candle.candlePoint1); + candle.candlePoint2 = coordinateSystem.transformThisPoint(candle.candlePoint2); + candle.candlePoint3 = coordinateSystem.transformThisPoint(candle.candlePoint3); + candle.candlePoint4 = coordinateSystem.transformThisPoint(candle.candlePoint4); + + candle.candlePoint1 = transformThisPoint(candle.candlePoint1, thisObject.container); + candle.candlePoint2 = transformThisPoint(candle.candlePoint2, thisObject.container); + candle.candlePoint3 = transformThisPoint(candle.candlePoint3, thisObject.container); + candle.candlePoint4 = transformThisPoint(candle.candlePoint4, thisObject.container); + + candle.stickPoint1 = { + x: candle.begin + timeFrame / 7 * 3.2, + y: candle.max + }; + + candle.stickPoint2 = { + x: candle.begin + timeFrame / 7 * 3.8, + y: candle.max + }; + + candle.stickPoint3 = { + x: candle.begin + timeFrame / 7 * 3.8, + y: candle.min + }; + + candle.stickPoint4 = { + x: candle.begin + timeFrame / 7 * 3.2, + y: candle.min + }; + + candle.stickPoint1 = coordinateSystem.transformThisPoint(candle.stickPoint1); + candle.stickPoint2 = coordinateSystem.transformThisPoint(candle.stickPoint2); + candle.stickPoint3 = coordinateSystem.transformThisPoint(candle.stickPoint3); + candle.stickPoint4 = coordinateSystem.transformThisPoint(candle.stickPoint4); + + candle.stickPoint1 = transformThisPoint(candle.stickPoint1, thisObject.container); + candle.stickPoint2 = transformThisPoint(candle.stickPoint2, thisObject.container); + candle.stickPoint3 = transformThisPoint(candle.stickPoint3, thisObject.container); + candle.stickPoint4 = transformThisPoint(candle.stickPoint4, thisObject.container); + + if (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT) { + let diffA = candle.stickPoint3.y - canvas.chartingSpace.viewport.visibleArea.bottomLeft.y + if (diffA > 0) { + candle.candlePoint1.y = candle.candlePoint1.y - diffA + candle.candlePoint2.y = candle.candlePoint2.y - diffA + candle.candlePoint3.y = candle.candlePoint3.y - diffA + candle.candlePoint4.y = candle.candlePoint4.y - diffA + + candle.stickPoint1.y = candle.stickPoint1.y - diffA + candle.stickPoint2.y = candle.stickPoint2.y - diffA + candle.stickPoint3.y = candle.stickPoint3.y - diffA + candle.stickPoint4.y = candle.stickPoint4.y - diffA + } + + let diffB = candle.stickPoint1.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y + if (diffB < 0) { + candle.candlePoint1.y = candle.candlePoint1.y - diffB + candle.candlePoint2.y = candle.candlePoint2.y - diffB + candle.candlePoint3.y = candle.candlePoint3.y - diffB + candle.candlePoint4.y = candle.candlePoint4.y - diffB + + candle.stickPoint1.y = candle.stickPoint1.y - diffB + candle.stickPoint2.y = candle.stickPoint2.y - diffB + candle.stickPoint3.y = candle.stickPoint3.y - diffB + candle.stickPoint4.y = candle.stickPoint4.y - diffB + } + } + + candle.candlePoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint1); + candle.candlePoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint2); + candle.candlePoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint3); + candle.candlePoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint4); + + candle.candlePoint1 = thisObject.fitFunction(candle.candlePoint1); + candle.candlePoint2 = thisObject.fitFunction(candle.candlePoint2); + candle.candlePoint3 = thisObject.fitFunction(candle.candlePoint3); + candle.candlePoint4 = thisObject.fitFunction(candle.candlePoint4); + + candle.stickPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint1); + candle.stickPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint2); + candle.stickPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint3); + candle.stickPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint4); + + candle.stickPoint1 = thisObject.fitFunction(candle.stickPoint1); + candle.stickPoint2 = thisObject.fitFunction(candle.stickPoint2); + candle.stickPoint3 = thisObject.fitFunction(candle.stickPoint3); + candle.stickPoint4 = thisObject.fitFunction(candle.stickPoint4); + + } + + /* On screen candles */ + let onScreenCandles = [] + let mouseCandle + + for (let i = 0; i < candles.length; i++) { + + candle = candles[i]; + + if (candle.candlePoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || candle.candlePoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + continue + } + + /* Contributing to Auto-Scale*/ + coordinateSystem.reportYValue(candle.max) + coordinateSystem.reportYValue(candle.min) + + + //if (onScreenCandles.length < 15000) { + onScreenCandles.push(candle) + //} + + if (userPositionDate >= candle.begin && userPositionDate <= candle.end) { + mouseCandle = candle + } + } + + /* We draw the sticks */ + browserCanvasContext.beginPath(); + + for (let i = 0; i < onScreenCandles.length; i++) { + + candle = onScreenCandles[i]; + + browserCanvasContext.moveTo(candle.stickPoint1.x, candle.stickPoint1.y); + browserCanvasContext.lineTo(candle.stickPoint2.x, candle.stickPoint2.y); + browserCanvasContext.lineTo(candle.stickPoint3.x, candle.stickPoint3.y); + browserCanvasContext.lineTo(candle.stickPoint4.x, candle.stickPoint4.y); + } + + browserCanvasContext.closePath(); + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'; + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.LIGHT + ', 1)'; + browserCanvasContext.lineWidth = 1; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + /* The stick at the mouse candle */ + if (mouseCandle) { + browserCanvasContext.beginPath(); + + candle = mouseCandle + + browserCanvasContext.moveTo(candle.stickPoint1.x, candle.stickPoint1.y); + browserCanvasContext.lineTo(candle.stickPoint2.x, candle.stickPoint2.y); + browserCanvasContext.lineTo(candle.stickPoint3.x, candle.stickPoint3.y); + browserCanvasContext.lineTo(candle.stickPoint4.x, candle.stickPoint4.y); + + browserCanvasContext.closePath(); + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'; + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; + browserCanvasContext.lineWidth = 1; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + } + + + /* We draw the candles. */ + drawCandlesByDirection('up') + drawCandlesByDirection('down') + drawCandlesByDirection('side') + + function drawCandlesByDirection(direction) { + + browserCanvasContext.beginPath(); + + for (let i = 0; i < onScreenCandles.length; i++) { + + candle = onScreenCandles[i]; + + if (candle.direction !== direction) { continue } + + browserCanvasContext.moveTo(candle.candlePoint1.x, candle.candlePoint1.y); + browserCanvasContext.lineTo(candle.candlePoint2.x, candle.candlePoint2.y); + browserCanvasContext.lineTo(candle.candlePoint3.x, candle.candlePoint3.y); + browserCanvasContext.lineTo(candle.candlePoint4.x, candle.candlePoint4.y); + } + + browserCanvasContext.closePath(); + + if (direction === 'up') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'; } + if (direction === 'down') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', 1)'; } + if (direction === 'side') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'; } + + if (direction === 'up') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', 1)'; } + if (direction === 'down') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)'; } + if (direction === 'side') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'; } + + browserCanvasContext.fill(); + + browserCanvasContext.lineWidth = 1; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + } + + /* Draw mouse candel and Raise Event. */ + if (mouseCandle) { + + /* highlight the current candle */ + + browserCanvasContext.beginPath(); + + candle = mouseCandle + + browserCanvasContext.moveTo(candle.candlePoint1.x, candle.candlePoint1.y); + browserCanvasContext.lineTo(candle.candlePoint2.x, candle.candlePoint2.y); + browserCanvasContext.lineTo(candle.candlePoint3.x, candle.candlePoint3.y); + browserCanvasContext.lineTo(candle.candlePoint4.x, candle.candlePoint4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current candle accroding to time + + browserCanvasContext.fill(); + + let currentCandle = { + bodyWidth: candle.candlePoint2.x - candle.candlePoint1.x, + bodyHeight: candle.candlePoint3.y - candle.candlePoint2.y, + stickHeight: candle.stickPoint4.y - candle.stickPoint2.y, + stickWidth: candle.stickPoint2.x - candle.stickPoint1.x, + stickStart: candle.candlePoint2.y - candle.stickPoint2.y, + period: timeFrame, + innerCandle: candle + }; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentCandle); + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + + function onViewportZoomChanged(event) { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + } + } + + function onViewportPositionChanged(event) { + + try { + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/VolumePanel.js b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/VolumePanel.js new file mode 100644 index 000000000..0ac28e3f7 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/VolumePanel.js @@ -0,0 +1,254 @@ + +function newAAMastersPlottersCandlesVolumesVolumesVolumePanel() { + + let thisObject = { + fitFunction: undefined, + container: undefined, + isVisible: true, + onRecordChange: onRecordChange, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + }; + + thisObject.container = newContainer(); + thisObject.container.initialize(); + thisObject.container.frame.containerName = "Volume"; + + let currentVolume; + let panelTabButton + + return thisObject; + + function finalize() { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.isVisible = undefined + + currentVolume = undefined + panelTabButton.finalize() + panelTabButton = undefined + } + + function initialize() { + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL; + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL; + + thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width; + thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height; + + panelTabButton = newPanelTabButton() + panelTabButton.parentContainer = thisObject.container + panelTabButton.container.frame.parentFrame = thisObject.container.frame + panelTabButton.fitFunction = thisObject.fitFunction + panelTabButton.initialize() + } + + function getContainer(point) { + if (thisObject.isVisible !== true) { return } + let container; + + container = panelTabButton.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container; + } + } + } + + + function onRecordChange(lastCurrentVolume) { + + currentVolume = lastCurrentVolume; + + } + + + function draw() { + if (thisObject.isVisible !== true) { return } + thisObject.container.frame.draw(false, false, true, thisObject.fitFunction); + + plotCurrentVolumeInfo(); + + panelTabButton.draw() + } + + function plotCurrentVolumeInfo() { + + if (currentVolume === undefined) { return; } + if (currentVolume.innerVolumeBar === undefined) { return; } + + const frameBodyHeight = thisObject.container.frame.getBodyHeight(); + const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight; + + const X_AXIS = thisObject.container.frame.width / 2; + const Y_AXIS_BUY = frameTitleHeight + frameBodyHeight * 0.85; + const Y_AXIS_SELL = frameTitleHeight + frameBodyHeight * 0.15; + + let buyVolumePoint1 = { + x: X_AXIS - currentVolume.buyInfo.baseWidth / 2, + y: Y_AXIS_BUY + }; + + let buyVolumePoint2 = { + x: X_AXIS - currentVolume.buyInfo.topWidth / 2, + y: Y_AXIS_BUY + currentVolume.buyInfo.height + }; + + let buyVolumePoint3 = { + x: X_AXIS + currentVolume.buyInfo.topWidth / 2, + y: Y_AXIS_BUY + currentVolume.buyInfo.height + }; + + let buyVolumePoint4 = { + x: X_AXIS + currentVolume.buyInfo.baseWidth / 2, + y: Y_AXIS_BUY + }; + + buyVolumePoint1 = thisObject.container.frame.fitIntoFrame(buyVolumePoint1); + buyVolumePoint2 = thisObject.container.frame.fitIntoFrame(buyVolumePoint2); + buyVolumePoint3 = thisObject.container.frame.fitIntoFrame(buyVolumePoint3); + buyVolumePoint4 = thisObject.container.frame.fitIntoFrame(buyVolumePoint4); + + buyVolumePoint1 = thisObject.container.frame.frameThisPoint(buyVolumePoint1); + buyVolumePoint2 = thisObject.container.frame.frameThisPoint(buyVolumePoint2); + buyVolumePoint3 = thisObject.container.frame.frameThisPoint(buyVolumePoint3); + buyVolumePoint4 = thisObject.container.frame.frameThisPoint(buyVolumePoint4); + + buyVolumePoint1 = thisObject.fitFunction(buyVolumePoint1) + buyVolumePoint2 = thisObject.fitFunction(buyVolumePoint2) + buyVolumePoint3 = thisObject.fitFunction(buyVolumePoint3) + buyVolumePoint4 = thisObject.fitFunction(buyVolumePoint4) + + let sellVolumePoint1 = { + x: X_AXIS - currentVolume.buyInfo.baseWidth / 2, + y: Y_AXIS_SELL + }; + + let sellVolumePoint2 = { + x: X_AXIS - currentVolume.buyInfo.topWidth / 2, + y: Y_AXIS_SELL + currentVolume.sellInfo.height + }; + + let sellVolumePoint3 = { + x: X_AXIS + currentVolume.buyInfo.topWidth / 2, + y: Y_AXIS_SELL + currentVolume.sellInfo.height + }; + + let sellVolumePoint4 = { + x: X_AXIS + currentVolume.buyInfo.baseWidth / 2, + y: Y_AXIS_SELL + }; + + sellVolumePoint1 = thisObject.container.frame.fitIntoFrame(sellVolumePoint1); + sellVolumePoint2 = thisObject.container.frame.fitIntoFrame(sellVolumePoint2); + sellVolumePoint3 = thisObject.container.frame.fitIntoFrame(sellVolumePoint3); + sellVolumePoint4 = thisObject.container.frame.fitIntoFrame(sellVolumePoint4); + + sellVolumePoint1 = thisObject.container.frame.frameThisPoint(sellVolumePoint1); + sellVolumePoint2 = thisObject.container.frame.frameThisPoint(sellVolumePoint2); + sellVolumePoint3 = thisObject.container.frame.frameThisPoint(sellVolumePoint3); + sellVolumePoint4 = thisObject.container.frame.frameThisPoint(sellVolumePoint4); + + sellVolumePoint1 = thisObject.fitFunction(sellVolumePoint1) + sellVolumePoint2 = thisObject.fitFunction(sellVolumePoint2) + sellVolumePoint3 = thisObject.fitFunction(sellVolumePoint3) + sellVolumePoint4 = thisObject.fitFunction(sellVolumePoint4) + + const OPACITY = '0.40'; + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(buyVolumePoint1.x, buyVolumePoint1.y); + browserCanvasContext.lineTo(buyVolumePoint2.x, buyVolumePoint2.y); + browserCanvasContext.lineTo(buyVolumePoint3.x, buyVolumePoint3.y); + browserCanvasContext.lineTo(buyVolumePoint4.x, buyVolumePoint4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + OPACITY + ')'; + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')'; + + browserCanvasContext.fill(); + + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(sellVolumePoint1.x, sellVolumePoint1.y); + browserCanvasContext.lineTo(sellVolumePoint2.x, sellVolumePoint2.y); + browserCanvasContext.lineTo(sellVolumePoint3.x, sellVolumePoint3.y); + browserCanvasContext.lineTo(sellVolumePoint4.x, sellVolumePoint4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')'; + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')'; + + browserCanvasContext.fill(); + + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + + /* put the labels with the volumes values */ + + browserCanvasContext.beginPath(); + + printLabel('Buy Volume', X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '1'); + printLabel(currentVolume.innerVolumeBar.amountBuy, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '0.50'); + + printLabel('Sell Volume', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '1'); + printLabel(currentVolume.innerVolumeBar.amountSell, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '0.50'); + + + function printLabel(labelToPrint, x, y, opacity) { + + let labelPoint; + let fontSize = 10; + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira'; + + let label = '' + labelToPrint; + if (isNaN(label) === false) { + label = Number(label).toLocaleString(); + } + + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; + + labelPoint = { + x: x - xOffset, + y: y + }; + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); + labelPoint = thisObject.fitFunction(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'; + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); + + } + + browserCanvasContext.closePath(); + browserCanvasContext.fill(); + + } + + +} + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/Volumes.js b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/Volumes.js new file mode 100644 index 000000000..19ff0c473 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/Volumes.js @@ -0,0 +1,744 @@ +function newAAMastersPlottersCandlesVolumesVolumes() { + + const MODULE_NAME = "Volumes Plotter"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + recalculateScale: recalculateScale, + draw: draw + }; + + /* this is part of the module template */ + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + let scaleFile; // This file is used to calculate the scale. + + /* these are module specific variables: */ + + let volumes = []; // Here we keep the volumes to be ploted every time the Draw() function is called by the AAWebPlatform. + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + scaleFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function recalculateScale() { + recalculate(); + } + + function getContainer(point) { + + try { + + let container; + + /* First we check if this point is inside this space. */ + + if (thisObject.container.frame.isThisPointHere(point) === true) { + + return thisObject.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + let newMarketFile = marketFiles.getFile(pTimeFrame); + + if (newMarketFile !== undefined) { + + marketFile = newMarketFile; + recalculate(); + } + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + if (newFileCursor !== undefined) { + + fileCursor = newFileCursor; + recalculate(); + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + if (event.currentValue === event.totalValue) { + /* This happens only when all of the files in the cursor have been loaded. */ + recalculate(); + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + thisObject.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + recalculateUsingMarketFiles(); + } else { + recalculateUsingDailyFiles(); + } + thisObject.container.eventHandler.raiseEvent("Volumes Changed", volumes); + } + + function recalculateUsingDailyFiles() { + + try { + + if (fileCursor === undefined) { return; } // We need to wait + + if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + volumes = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let volume = { + amountBuy: 0, + amountSell: 0, + begin: undefined, + end: undefined + }; + + volume.amountBuy = dailyFile[i][0]; + volume.amountSell = dailyFile[i][1]; + + volume.begin = dailyFile[i][2]; + volume.end = dailyFile[i][3]; + + if ( + (volume.begin >= farLeftDate.valueOf() && volume.end <= farRightDate.valueOf()) && + (volume.begin >= coordinateSystem.min.x && volume.end <= coordinateSystem.max.x) + ) { + + volumes.push(volume); + + if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) { + + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentCandle); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by volumes. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + if (volumes.length > 0) { + + if (volumes[0].begin > lowerEnd || volumes[volumes.length - 1].end < upperEnd) { + + setTimeout(recalculate, 2000); + + //console.log("File missing while calculating volumes, scheduling a recalculation in 2 seconds."); + + } + } + + //console.log("Olivia > recalculateUsingDailyFiles > total volumes generated : " + volumes.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + volumes = []; + + for (let i = 0; i < marketFile.length; i++) { + + let volume = { + amountBuy: 0, + amountSell: 0, + begin: undefined, + end: undefined + }; + + volume.amountBuy = marketFile[i][0]; + volume.amountSell = marketFile[i][1]; + + volume.begin = marketFile[i][2]; + volume.end = marketFile[i][3]; + + if ( + (volume.begin >= leftDate.valueOf() && volume.end <= rightDate.valueOf()) && + (volume.begin >= coordinateSystem.min.x && volume.end <= coordinateSystem.max.x) + ) { + + volumes.push(volume); + + if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) { + + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentCandle); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total volumes generated : " + volumes.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + if (volumes.length > 0) { + + /* Now we calculate and plot the volumes */ + + let visibleHeight = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y; + + let frameCorner1 = { + x: 0, + y: 0 + }; + + let frameCorner2 = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + }; + + + /* Now the transformations. */ + + frameCorner1 = transformThisPoint(frameCorner1, thisObject.container.frame.container); + frameCorner2 = transformThisPoint(frameCorner2, thisObject.container.frame.container); + + let frameHeightInViewPort = frameCorner2.y - frameCorner1.y; + + if (volumes.length > 0) { + + for (let i = 0; i < volumes.length; i++) { + + volume = volumes[i]; + + let volumePointA1; + let volumePointA2; + let volumePointA3; + let volumePointA4; + + function calculateBuys(plot, height) { + + volumePointA1 = { + x: volume.begin + timeFrame / 7 * 2, + y: 0 + }; + + volumePointA2 = { + x: volume.begin + timeFrame / 7 * 2, + y: volume.amountBuy + }; + + volumePointA3 = { + x: volume.begin + timeFrame / 7 * 5, + y: volume.amountBuy + }; + + volumePointA4 = { + x: volume.begin + timeFrame / 7 * 5, + y: 0 + }; + + volumePointA1 = plot.transformThisPoint(volumePointA1); + volumePointA2 = plot.transformThisPoint(volumePointA2); + volumePointA3 = plot.transformThisPoint(volumePointA3); + volumePointA4 = plot.transformThisPoint(volumePointA4); + + volumePointA1 = transformThisPoint(volumePointA1, thisObject.container); + volumePointA2 = transformThisPoint(volumePointA2, thisObject.container); + volumePointA3 = transformThisPoint(volumePointA3, thisObject.container); + volumePointA4 = transformThisPoint(volumePointA4, thisObject.container); + + let baseIncrement = (volumePointA3.x - volumePointA1.x) * WIDHTER_VOLUME_BAR_BASE_FACTOR; + + volumePointA1.x = volumePointA1.x - baseIncrement; + volumePointA4.x = volumePointA4.x + baseIncrement; + + if (volumePointA4.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || volumePointA1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + return false; + } + + return true; + + } + + if (calculateBuys(coordinateSystem, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area. + + let diffA = volumePointA1.y - canvas.chartingSpace.viewport.visibleArea.bottomLeft.y + if (diffA > 0) { + volumePointA1.y = volumePointA1.y - diffA + volumePointA2.y = volumePointA2.y - diffA + volumePointA3.y = volumePointA3.y - diffA + volumePointA4.y = volumePointA4.y - diffA + } + + let volumePointB1; + let volumePointB2; + let volumePointB3; + let volumePointB4; + + function calculateSells(plot, height) { + + volumePointB1 = { + x: volume.begin + timeFrame / 7 * 2, + y: height + }; + + volumePointB2 = { + x: volume.begin + timeFrame / 7 * 2, + y: height - volume.amountSell + }; + + volumePointB3 = { + x: volume.begin + timeFrame / 7 * 5, + y: height - volume.amountSell + }; + + volumePointB4 = { + x: volume.begin + timeFrame / 7 * 5, + y: height + }; + + volumePointB1 = plot.transformThisPoint2(volumePointB1); + volumePointB2 = plot.transformThisPoint2(volumePointB2); + volumePointB3 = plot.transformThisPoint2(volumePointB3); + volumePointB4 = plot.transformThisPoint2(volumePointB4); + + volumePointB1 = transformThisPoint(volumePointB1, thisObject.container); + volumePointB2 = transformThisPoint(volumePointB2, thisObject.container); + volumePointB3 = transformThisPoint(volumePointB3, thisObject.container); + volumePointB4 = transformThisPoint(volumePointB4, thisObject.container); + + } + + calculateSells(coordinateSystem, thisObject.container.frame.height); // We try to see if it fits in the visible area. + + let diffB = volumePointB1.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y + if (diffB < 0) { + volumePointB1.y = volumePointB1.y - diffB + volumePointB2.y = volumePointB2.y - diffB + volumePointB3.y = volumePointB3.y - diffB + volumePointB4.y = volumePointB4.y - diffB + } + + /* We put a wider base */ + + let baseIncrement = (volumePointB3.x - volumePointB1.x) * WIDHTER_VOLUME_BAR_BASE_FACTOR; + + volumePointB1.x = volumePointB1.x - baseIncrement; + volumePointB4.x = volumePointB4.x + baseIncrement; + + /* We put a less wider top */ + + let baseDencrement = (volumePointA3.x - volumePointA2.x) * LESS_WIDHTER_VOLUME_BAR_TOP_FACTOR; + + volumePointA2.x = volumePointA2.x + baseDencrement; + volumePointA3.x = volumePointA3.x - baseDencrement; + + volumePointB2.x = volumePointB2.x + baseDencrement; + volumePointB3.x = volumePointB3.x - baseDencrement; + + + /* Everything must fit within the visible area */ + + volumePointA1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA1); + volumePointA2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA2); + volumePointA3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA3); + volumePointA4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA4); + + volumePointB1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB1); + volumePointB2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB2); + volumePointB3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB3); + volumePointB4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB4); + + volumePointA1 = thisObject.fitFunction(volumePointA1); + volumePointA2 = thisObject.fitFunction(volumePointA2); + volumePointA3 = thisObject.fitFunction(volumePointA3); + volumePointA4 = thisObject.fitFunction(volumePointA4); + + volumePointB1 = thisObject.fitFunction(volumePointB1); + volumePointB2 = thisObject.fitFunction(volumePointB2); + volumePointB3 = thisObject.fitFunction(volumePointB3); + volumePointB4 = thisObject.fitFunction(volumePointB4); + + /* Everything must fit within the Frame. We know that that is equivalent to say that each bar con not be higher than the base of the opposite . */ + + if (volumePointA2.y < volumePointB1.y) { + + volumePointA2.y = volumePointB1.y; + volumePointA3.y = volumePointB1.y; + + } + + if (volumePointB2.y > volumePointA1.y) { + + volumePointB2.y = volumePointA1.y; + volumePointB3.y = volumePointA1.y; + + } + + /* Now the drawing of the volume bars*/ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(volumePointA1.x, volumePointA1.y); + browserCanvasContext.lineTo(volumePointA2.x, volumePointA2.y); + browserCanvasContext.lineTo(volumePointA3.x, volumePointA3.y); + browserCanvasContext.lineTo(volumePointA4.x, volumePointA4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', 0.40)'; + + if (userPositionDate >= volume.begin && userPositionDate <= volume.end) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.40)'; // Current bar accroding to time + } + + browserCanvasContext.fill(); + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 0.40)'; + browserCanvasContext.lineWidth = 1; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(volumePointB1.x, volumePointB1.y); + browserCanvasContext.lineTo(volumePointB2.x, volumePointB2.y); + browserCanvasContext.lineTo(volumePointB3.x, volumePointB3.y); + browserCanvasContext.lineTo(volumePointB4.x, volumePointB4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 0.40)'; + + if (userPositionDate >= volume.begin && userPositionDate <= volume.end) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.40)'; // Current volume accroding to time + } + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', 0.40)'; + + browserCanvasContext.fill(); + browserCanvasContext.lineWidth = 1; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + if (userPositionDate >= volume.begin && userPositionDate <= volume.end) { + + let buyInfo = { + baseWidth: volumePointA4.x - volumePointA1.x, + topWidth: volumePointA3.x - volumePointA2.x, + height: volumePointA2.y - volumePointA1.y + }; + + let sellInfo = { + baseWidth: volumePointB4.x - volumePointB1.x, + topWidth: volumePointB3.x - volumePointB2.x, + height: volumePointB2.y - volumePointB1.y + }; + + let currentVolume = { + buyInfo: buyInfo, + sellInfo: sellInfo, + period: timeFrame, + innerVolumeBar: volume + }; + + thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentVolume); + + } + /* Contributing to Auto-Scale*/ + coordinateSystem.reportYValue(volume.amountBuy * 5) + coordinateSystem.reportYValue(volume.amountSell * 5) + coordinateSystem.reportYValue(0) + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + function onViewportZoomChanged(event) { + recalculate() + } + + function onViewportPositionChanged(event) { + + try { + if (event !== undefined) { + if (event.recalculate === true) { + recalculate(); + return + } + } + if (Math.random() * 100 > 95) { + recalculate(); + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + recalculate(); + } +} + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/this.plotter.config.json b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/this.plotter.config.json new file mode 100644 index 000000000..1f777a5af --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Candles-Volumes/this.plotter.config.json @@ -0,0 +1,33 @@ +{ + "displayName": "Plotters Candles & Volumes", + "codeName": "PlottersCandlesVolumes", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "Candles", + "moduleName": "Candles", + "description": "Plots traditional red and green candles.", + "profilePicture": "Candles.png", + "panels": [ + { + "codeName": "CandlePanel", + "moduleName": "CandlePanel", + "event": "Current Record Changed" + } + ] + }, + { + "codeName": "Volumes", + "description": "Plots volumes separated in buy and sell.", + "moduleName": "Volumes", + "profilePicture": "Volumes.png", + "panels": [ + { + "codeName": "VolumePanel", + "moduleName": "VolumePanel", + "event": "Current Record Changed" + } + ] + } + ] +} \ No newline at end of file diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/CandleStairs.js b/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/CandleStairs.js new file mode 100644 index 000000000..990f68f3b --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/CandleStairs.js @@ -0,0 +1,679 @@ +function newAAMastersPlottersStairsPatternsCandleStairs() { + + const MODULE_NAME = "AAMasters Plotters Stairs Patterns Candle Stairs"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Main functions and properties. + + container: undefined, + initialize: initialize, + finalize: finalize, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw, + + // Secondary functions and properties. + + currentStair: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let stairsArray = []; // Here we keep the candle-stairs to be ploted every time the Draw() function is called by the AAWebPlatform. + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err.message = " + err.message); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); + + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + + try { + + let container; + + /* First we check if this point is inside this space. */ + + if (this.container.frame.isThisPointHere(point) === true) { + + return this.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + let newMarketFile = marketFiles.getFile(pTimeFrame); + + if (newMarketFile !== undefined) { + + marketFile = newMarketFile; + recalculate(); + } + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + if (newFileCursor !== undefined) { + + fileCursor = newFileCursor; + recalculate(); + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + try { + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); } + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + + } + } + + function recalculate() { + + try { + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("CandleStairs Changed", stairsArray); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (fileCursor === undefined) { return; } // We need to wait + + if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + stairsArray = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let stairs = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined, + candleCount: 0, + firstMin: 0, + firstMax: 0, + lastMin: 0, + lastMax: 0 + }; + + stairs.open = dailyFile[i][0]; + stairs.close = dailyFile[i][1]; + + stairs.min = dailyFile[i][2]; + stairs.max = dailyFile[i][3]; + + stairs.begin = dailyFile[i][4]; + stairs.end = dailyFile[i][5]; + + stairs.direction = dailyFile[i][6]; + stairs.candleCount = dailyFile[i][7]; + + stairs.firstMin = dailyFile[i][8]; + stairs.firstMax = dailyFile[i][9]; + + stairs.lastMin = dailyFile[i][10]; + stairs.lastMax = dailyFile[i][11]; + + if ( + (stairs.begin >= farLeftDate.valueOf() && stairs.end <= farRightDate.valueOf()) && + (stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x) + ) { + + stairsArray.push(stairs); + + if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.end) { + + thisObject.currentStair = stairs; + thisObject.container.eventHandler.raiseEvent("Current Candle-Stairs Changed", thisObject.currentStair); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by candle-stairs. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + if (stairsArray.length > 0) { + + if (stairsArray[0].begin > lowerEnd || stairsArray[stairsArray.length - 1].end < upperEnd) { + + setTimeout(recalculate, 2000); + + //console.log("File missing while calculating candle-stairs, scheduling a recalculation in 2 seconds."); + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + stairsArray = []; + + for (let i = 0; i < marketFile.length; i++) { + + let stairs = { + open: undefined, + close: undefined, + min: 10000000000000, + max: 0, + begin: undefined, + end: undefined, + direction: undefined, + candleCount: 0, + firstMin: 0, + firstMax: 0, + lastMin: 0, + lastMax: 0 + }; + + stairs.open = marketFile[i][0]; + stairs.close = marketFile[i][1]; + + stairs.min = marketFile[i][2]; + stairs.max = marketFile[i][3]; + + stairs.begin = marketFile[i][4]; + stairs.end = marketFile[i][5]; + + stairs.direction = marketFile[i][6]; + stairs.candleCount = marketFile[i][7]; + + stairs.firstMin = marketFile[i][8]; + stairs.firstMax = marketFile[i][9]; + + stairs.lastMin = marketFile[i][10]; + stairs.lastMax = marketFile[i][11]; + + if ( + (stairs.begin >= leftDate.valueOf() && stairs.end <= rightDate.valueOf()) && + (stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x) + ) { + + stairsArray.push(stairs); + + if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.end) { + + thisObject.currentStair = stairs; + thisObject.container.eventHandler.raiseEvent("Current Candle-Stairs Changed", thisObject.currentStair); + + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + + } + } + + function plotChart() { + + try { + + if (stairsArray.length > 0) { + + for (var i = 0; i < stairsArray.length; i++) { + + stairs = stairsArray[i]; + + let stairsPoint1; + let stairsPoint2; + let stairsPoint3; + let stairsPoint4; + + if (stairs.direction === 'up') { + + stairsPoint1 = { + x: stairs.begin + timeFrame / 7 * 5.5, + y: stairs.firstMin + }; + + stairsPoint2 = { + x: stairs.end - timeFrame / 7 * 1.5, + y: stairs.lastMin + }; + + stairsPoint3 = { + x: stairs.end - timeFrame / 7 * 5.5, + y: stairs.lastMax + }; + + stairsPoint4 = { + x: stairs.begin + timeFrame / 7 * 1.5, + y: stairs.firstMax + }; + + } else { + + stairsPoint1 = { + x: stairs.begin + timeFrame / 7 * 1.5, + y: stairs.firstMin + }; + + stairsPoint2 = { + x: stairs.end - timeFrame / 7 * 5.5, + y: stairs.lastMin + }; + + stairsPoint3 = { + x: stairs.end - timeFrame / 7 * 1.5, + y: stairs.lastMax + }; + + stairsPoint4 = { + x: stairs.begin + timeFrame / 7 * 5.5, + y: stairs.firstMax + }; + } + + stairsPoint1 = coordinateSystem.transformThisPoint(stairsPoint1); + stairsPoint2 = coordinateSystem.transformThisPoint(stairsPoint2); + stairsPoint3 = coordinateSystem.transformThisPoint(stairsPoint3); + stairsPoint4 = coordinateSystem.transformThisPoint(stairsPoint4); + + stairsPoint1 = transformThisPoint(stairsPoint1, thisObject.container); + stairsPoint2 = transformThisPoint(stairsPoint2, thisObject.container); + stairsPoint3 = transformThisPoint(stairsPoint3, thisObject.container); + stairsPoint4 = transformThisPoint(stairsPoint4, thisObject.container); + + if (stairsPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || stairsPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + continue; + } + + stairsPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint1); + stairsPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint2); + stairsPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint3); + stairsPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint4); + + stairsPoint1 = thisObject.fitFunction(stairsPoint1); + stairsPoint2 = thisObject.fitFunction(stairsPoint2); + stairsPoint3 = thisObject.fitFunction(stairsPoint3); + stairsPoint4 = thisObject.fitFunction(stairsPoint4); + + /* Contributing to Auto-Scale*/ + coordinateSystem.reportYValue(stairs.firstMin) + coordinateSystem.reportYValue(stairs.firstMax) + coordinateSystem.reportYValue(stairs.lastMin) + coordinateSystem.reportYValue(stairs.lastMax) + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(stairsPoint1.x, stairsPoint1.y); + browserCanvasContext.lineTo(stairsPoint2.x, stairsPoint2.y); + browserCanvasContext.lineTo(stairsPoint3.x, stairsPoint3.y); + browserCanvasContext.lineTo(stairsPoint4.x, stairsPoint4.y); + + browserCanvasContext.closePath(); + + let opacity = '0.25'; + + if (stairs.direction === 'up') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; } + if (stairs.direction === 'down') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; } + + if (stairs.direction === 'up') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')'; } + if (stairs.direction === 'down') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; } + + if (userPositionDate >= stairs.begin && userPositionDate <= stairs.end) { + /* highlight the current stairs */ + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.1)'; // Current stairs accroding to time + } + + browserCanvasContext.fill(); + + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + + } + } + + function onViewportZoomChanged(event) { + + try { + + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + + } + } + + function onDragFinished() { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + + } + } + + function onViewportPositionChanged(event) { + + try { + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + + } + } +} + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/VolumeStairs.js b/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/VolumeStairs.js new file mode 100644 index 000000000..01b747dee --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/VolumeStairs.js @@ -0,0 +1,752 @@ +function newAAMastersPlottersStairsPatternsVolumeStairs() { + + const MODULE_NAME = "AAMasters Plotters Stairs Patterns Volume Stairs"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Main functions and properties. + + container: undefined, + initialize: initialize, + finalize: finalize, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + let scaleFile; // This file is used to calculate the scale. + + /* these are module specific variables: */ + + let stairsArray = []; // Here we keep the stairsArray to be ploted every time the Draw() function is called by the AAWebPlatform. + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err.message = " + err.message); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + scaleFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); + + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + + try { + + let container; + + /* First we check if this point is inside this space. */ + + if (this.container.frame.isThisPointHere(point) === true) { + + return this.container; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + let newMarketFile = marketFiles.getFile(pTimeFrame); + + if (newMarketFile !== undefined) { + + marketFile = newMarketFile; + recalculate(); + } + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + if (newFileCursor !== undefined) { + + fileCursor = newFileCursor; + recalculate(); + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + try { + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + recalculate(); + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + + } + } + + function recalculate() { + + try { + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Volumes Changed", stairsArray); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (fileCursor === undefined) { return; } // We need to wait + + if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + stairsArray = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let stairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + stairs.type = dailyFile[i][0]; + + stairs.begin = dailyFile[i][1]; + stairs.end = dailyFile[i][2]; + + stairs.direction = dailyFile[i][3]; + stairs.barsCount = dailyFile[i][4]; + stairs.firstAmount = dailyFile[i][5]; + stairs.lastAmount = dailyFile[i][6]; + + if ( + (stairs.begin >= farLeftDate.valueOf() && stairs.end <= farRightDate.valueOf()) && + (stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x) + ) { + + stairsArray.push(stairs); + + if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.end) { + + thisObject.container.eventHandler.raiseEvent("Current Candle Changed", thisObject.currentCandle); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by stairsArray. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + if (stairsArray.length > 0) { + + if (stairsArray[0].begin > lowerEnd || stairsArray[stairsArray.length - 1].end < upperEnd) { + + setTimeout(recalculate, 2000); + + //console.log("File missing while calculating stairsArray, scheduling a recalculation in 2 seconds."); + + } + } + + //console.log("Olivia > recalculateUsingDailyFiles > total stairsArray generated : " + stairsArray.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + stairsArray = []; + + for (let i = 0; i < marketFile.length; i++) { + + let stairs = { + type: undefined, + begin: undefined, + end: undefined, + direction: undefined, + barsCount: 0, + firstAmount: 0, + lastAmount: 0 + }; + + stairs.type = marketFile[i][0]; + + stairs.begin = marketFile[i][1]; + stairs.end = marketFile[i][2]; + + stairs.direction = marketFile[i][3]; + stairs.barsCount = marketFile[i][4]; + stairs.firstAmount = marketFile[i][5]; + stairs.lastAmount = marketFile[i][6]; + + if ( + (stairs.begin >= leftDate.valueOf() && stairs.end <= rightDate.valueOf()) && + (stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x) + ) { + + stairsArray.push(stairs); + + if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.end) { + + thisObject.container.eventHandler.raiseEvent("Current Volume-Stairs Changed", thisObject.currentCandle); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total stairsArray generated : " + stairsArray.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + + } + } + + function plotChart() { + + try { + + let opacity = '0.25'; + + let visibleHeight = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y; + + let frameCorner1 = { + x: 0, + y: 0 + }; + + let frameCorner2 = { + x: thisObject.container.frame.width, + y: thisObject.container.frame.height + }; + + /* Now the transformations. */ + + frameCorner1 = transformThisPoint(frameCorner1, thisObject.container.frame.container); + frameCorner2 = transformThisPoint(frameCorner2, thisObject.container.frame.container); + + let frameHeightInViewPort = frameCorner2.y - frameCorner1.y; + + + if (stairsArray.length > 0) { + + for (var i = 0; i < stairsArray.length; i++) { + + stairs = stairsArray[i]; + + let volumeBarPointA1; + let volumeBarPointA2; + let volumeBarPointA3; + let volumeBarPointA4; + + if (stairs.type === 'buy') { + + function calculateBuys(plot, height) { + + /* Contributing to Auto-Scale*/ + coordinateSystem.reportYValue(stairs.firstAmount * 2) + coordinateSystem.reportYValue(stairs.lastAmount * 2) + + volumeBarPointA1 = { + x: stairs.begin + timeFrame / 2, + y: 0 + }; + + volumeBarPointA2 = { + x: stairs.begin + timeFrame / 2, + y: stairs.firstAmount * 2 + }; + + volumeBarPointA3 = { + x: stairs.end - timeFrame / 2, + y: stairs.lastAmount * 2 + }; + + volumeBarPointA4 = { + x: stairs.end - timeFrame / 2, + y: 0 + }; + + + volumeBarPointA1 = plot.transformThisPoint(volumeBarPointA1); + volumeBarPointA2 = plot.transformThisPoint(volumeBarPointA2); + volumeBarPointA3 = plot.transformThisPoint(volumeBarPointA3); + volumeBarPointA4 = plot.transformThisPoint(volumeBarPointA4); + + volumeBarPointA1 = transformThisPoint(volumeBarPointA1, thisObject.container); + volumeBarPointA2 = transformThisPoint(volumeBarPointA2, thisObject.container); + volumeBarPointA3 = transformThisPoint(volumeBarPointA3, thisObject.container); + volumeBarPointA4 = transformThisPoint(volumeBarPointA4, thisObject.container); + + if (volumeBarPointA4.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || volumeBarPointA1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + return false; + } + + return true; + } + + if (calculateBuys(coordinateSystem, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area. + + if (volumeBarPointA1.y > canvas.chartingSpace.viewport.visibleArea.bottomLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) { + + if (calculateBuys(coordinateSystem, visibleHeight) === false) { continue; } // We snap t to the view port. + + /* Now we set the real value of y. */ + + volumeBarPointA1.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y; + volumeBarPointA2.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - stairs.firstAmount * 2 * coordinateSystem.scale.y; + volumeBarPointA3.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - stairs.lastAmount * 2 * coordinateSystem.scale.y; + volumeBarPointA4.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y; + + } + } + + let volumeBarPointB1; + let volumeBarPointB2; + let volumeBarPointB3; + let volumeBarPointB4; + + if (stairs.type === 'sell') { + + function calculateSells(plot, height) { + + /* Contributing to Auto-Scale*/ + coordinateSystem.reportYValue(stairs.firstAmount * 2) + coordinateSystem.reportYValue(stairs.lastAmount * 2) + + volumeBarPointB1 = { + x: stairs.begin + timeFrame / 2, + y: height + }; + + volumeBarPointB2 = { + x: stairs.begin + timeFrame / 2, + y: height - stairs.firstAmount * 2 + }; + + volumeBarPointB3 = { + x: stairs.end - timeFrame / 2, + y: height - stairs.lastAmount * 2 + }; + + volumeBarPointB4 = { + x: stairs.end - timeFrame / 2, + y: height + }; + + volumeBarPointB1 = plot.transformThisPoint2(volumeBarPointB1); + volumeBarPointB2 = plot.transformThisPoint2(volumeBarPointB2); + volumeBarPointB3 = plot.transformThisPoint2(volumeBarPointB3); + volumeBarPointB4 = plot.transformThisPoint2(volumeBarPointB4); + + volumeBarPointB1 = transformThisPoint(volumeBarPointB1, thisObject.container); + volumeBarPointB2 = transformThisPoint(volumeBarPointB2, thisObject.container); + volumeBarPointB3 = transformThisPoint(volumeBarPointB3, thisObject.container); + volumeBarPointB4 = transformThisPoint(volumeBarPointB4, thisObject.container); + + } + + calculateSells(coordinateSystem, thisObject.container.frame.height); // We try to see if it fits in the visible area. + + if (volumeBarPointB1.y < canvas.chartingSpace.viewport.visibleArea.topLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) { + + calculateSells(coordinateSystem, visibleHeight); // We snap it to the view port. + + /* Now we set the real value of y. */ + + volumeBarPointB1.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y; + volumeBarPointB2.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y + stairs.firstAmount * 2 * coordinateSystem.scale.y; + volumeBarPointB3.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y + stairs.lastAmount * 2 * coordinateSystem.scale.y; + volumeBarPointB4.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y; + + } + } + + /* Everything must fit within the visible area */ + + if (stairs.type === 'buy') { + + volumeBarPointA1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA1); + volumeBarPointA2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA2); + volumeBarPointA3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA3); + volumeBarPointA4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA4); + + volumeBarPointA1 = thisObject.fitFunction(volumeBarPointA1); + volumeBarPointA2 = thisObject.fitFunction(volumeBarPointA2); + volumeBarPointA3 = thisObject.fitFunction(volumeBarPointA3); + volumeBarPointA4 = thisObject.fitFunction(volumeBarPointA4); + + } else { + + volumeBarPointB1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB1); + volumeBarPointB2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB2); + volumeBarPointB3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB3); + volumeBarPointB4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB4); + + volumeBarPointB1 = thisObject.fitFunction(volumeBarPointB1); + volumeBarPointB2 = thisObject.fitFunction(volumeBarPointB2); + volumeBarPointB3 = thisObject.fitFunction(volumeBarPointB3); + volumeBarPointB4 = thisObject.fitFunction(volumeBarPointB4); + + } + + /* Now the drawing */ + + if (stairs.type === 'buy') { + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(volumeBarPointA1.x, volumeBarPointA1.y); + browserCanvasContext.lineTo(volumeBarPointA2.x, volumeBarPointA2.y); + browserCanvasContext.lineTo(volumeBarPointA3.x, volumeBarPointA3.y); + browserCanvasContext.lineTo(volumeBarPointA4.x, volumeBarPointA4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')'; + + if (userPositionDate >= stairs.begin && userPositionDate <= stairs.end) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; // Current bar accroding to time + } + + browserCanvasContext.fill(); + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + } else { + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(volumeBarPointB1.x, volumeBarPointB1.y); + browserCanvasContext.lineTo(volumeBarPointB2.x, volumeBarPointB2.y); + browserCanvasContext.lineTo(volumeBarPointB3.x, volumeBarPointB3.y); + browserCanvasContext.lineTo(volumeBarPointB4.x, volumeBarPointB4.y); + + browserCanvasContext.closePath(); + + if (userPositionDate >= stairs.begin && userPositionDate <= stairs.end) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; // Current candle accroding to time + } + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; + + browserCanvasContext.fill(); + browserCanvasContext.lineWidth = 1; + browserCanvasContext.stroke(); + + } + + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + + } + } + + function onViewportZoomChanged(event) { + recalculate(); + } + + function onDragFinished() { + recalculate(); + } + + function onViewportPositionChanged(event) { + + try { + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate(); + return + } + } + if (Math.random() * 100 > 95) { + recalculate(); + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + + } + } +} + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/this.plotter.config.json b/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/this.plotter.config.json new file mode 100644 index 000000000..cf638f14e --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Stairs-Patterns/this.plotter.config.json @@ -0,0 +1,21 @@ +{ + "displayName": "Plotters Stairs Patterns", + "codeName": "PlottersStairsPatterns", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "CandleStairs", + "moduleName": "CandleStairs", + "description": "Plots Candle Stairs patterns.", + "profilePicture": "CandleStairs.png", + "panels": [] + }, + { + "codeName": "VolumeStairs", + "description": "Plots Volume Stairs patterns.", + "moduleName": "VolumeStairs", + "profilePicture": "VolumeStairs.png", + "panels": [] + } + ] +} \ No newline at end of file diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Conditions.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Conditions.js new file mode 100644 index 000000000..4e77bac9f --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Conditions.js @@ -0,0 +1,1005 @@ +function newAAMastersPlottersTradingSimulationConditions() { + + const MODULE_NAME = "Conditions Plotter"; + const INFO_LOG = false; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + draw: draw, + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Secondary functions and properties. + + currentRecord: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem = newCoordinateSystem(); // Needed to be able to plot on the timeline, otherwise not. + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let conditions = []; + let headers; + + let noDefinitionOnFocusErrorDisplayed = false + + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + + return thisObject; + + function finalize() { + try { + + if (INFO_LOG === true) { logger.write("[INFO] finalize -> Entering function."); } + + /* Stop listening to the necesary events. */ + + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, callBackFunction) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + + /* Get ready for plotting. */ + + recalculate(); + + /* Ready for when dimmension changes. */ + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function getContainer(point) { + + try { + + 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; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + marketFile = marketFiles.getFile(pTimeFrame); + + recalculate(); + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing. + recalculate(); + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + + if (INFO_LOG === true) { logger.write("[INFO] setDatetime -> Entering function."); } + + datetime = pDatetime; + + } + + function onDailyFileLoaded(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); } + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + conditions = [] + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Conditions Changed", conditions); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingDailyFiles -> Entering function."); } + + if (fileCursor === undefined) { + conditions = []; + return; + } // We need to wait + + if (fileCursor.files.size === 0) { + conditions = []; + return; + } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + conditions = []; + + + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + headers = dailyFile[0]; + + for (let i = 0; i < dailyFile[1].length; i++) { + + let record = { + begin: undefined, + end: undefined, + conditions: undefined + }; + + record.begin = dailyFile[1][i][0]; + record.end = dailyFile[1][i][1]; + record.strategyNumber = dailyFile[1][i][2]; + record.strategyPhase = dailyFile[1][i][3]; + record.stopLossPhase = dailyFile[1][i][4]; + record.takeProfitPhase = dailyFile[1][i][5]; + record.conditions = dailyFile[1][i][6]; + record.formulaErrors = dailyFile[1][i][7]; + record.formulaValues = dailyFile[1][i][8]; + + if (record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) { + + conditions.push(record); + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Condition Changed", thisObject.currentRecord); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by conditions. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + setTimeout(recalculate, 2000); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingMarketFiles -> Entering function."); } + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + conditions = []; + headers = marketFile[0]; + + for (let i = 0; i < marketFile[1].length; i++) { + + let record = { + begin: undefined, + end: undefined, + conditions: undefined + }; + + record.begin = marketFile[1][i][0]; + record.end = marketFile[1][i][1]; + record.strategyNumber = marketFile[1][i][2]; + record.strategyPhase = marketFile[1][i][3]; + record.stopLossPhase = marketFile[1][i][4]; + record.takeProfitPhase = marketFile[1][i][5]; + record.conditions = marketFile[1][i][6]; + record.formulaErrors = marketFile[1][i][7]; + record.formulaValues = marketFile[1][i][8]; + + if (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) { + + conditions.push(record); + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Condition Changed", thisObject.currentRecord); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total conditions generated : " + conditions.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + let userPosition = getUserPosition() + let userPositionDate = userPosition.point.x + + thisObject.container.eventHandler.raiseEvent("Current Condition Record Changed", undefined); + + let conditionRecord; + + for (let i = 0; i < conditions.length; i++) { + + conditionRecord = conditions[i]; + + /* Send the current record to the panel */ + + if (userPositionDate >= conditionRecord.begin && userPositionDate <= conditionRecord.end) { + + let currentRecord = { + conditionsNames: headers, + strategyNumber: conditionRecord.strategyNumber, + strategyPhase: conditionRecord.strategyPhase, + stopLossPhase: conditionRecord.stopLossPhase, + takeProfitPhase: conditionRecord.takeProfitPhase, + conditionsValues: conditionRecord.conditions, + formulaErrors: conditionRecord.formulaErrors, + formulaValues: conditionRecord.formulaValues + }; + + sendRecordInfoToDesignerSpace(currentRecord) + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + function sendRecordInfoToDesignerSpace(currentRecord) { + + if (currentRecord === undefined) { return; } + if (currentRecord.conditionsNames === undefined) { return; } + if (canvas.designSpace.workspace === undefined) { return; } + + /* We will get now the designer trading system */ + let designerTradingSystem + let fileTradingSystem = currentRecord.conditionsNames; + + for (let i = 0; i < canvas.designSpace.workspace.workspaceNode.rootNodes.length; i++) { + let rootNode = canvas.designSpace.workspace.workspaceNode.rootNodes[i] + if (rootNode.type === 'Definition') { + if (rootNode.tradingSystem.id === fileTradingSystem.id) { + designerTradingSystem = rootNode.tradingSystem + } + } + } + + if (designerTradingSystem === undefined) { return; } + + try { + + browserCanvasContext.beginPath(); + + + let conditionIndex = 0; + let formulaErrorsIndex = 0; + let formulaValuesIndex = 0; + + designerTradingSystem.payload.uiObject.setErrorMessage(fileTradingSystem.error) + + if (fileTradingSystem.parameters !== undefined) { + designerTradingSystem.parameters.payload.uiObject.setErrorMessage(fileTradingSystem.parameters.error) + if (fileTradingSystem.parameters.baseAsset !== undefined) { + designerTradingSystem.parameters.baseAsset.payload.uiObject.setErrorMessage(fileTradingSystem.parameters.baseAsset.error) + } + if (fileTradingSystem.parameters.timeRange !== undefined) { + designerTradingSystem.parameters.timeRange.payload.uiObject.setErrorMessage(fileTradingSystem.parameters.timeRange.error) + } + if (fileTradingSystem.parameters.slippage !== undefined) { + designerTradingSystem.parameters.slippage.payload.uiObject.setErrorMessage(fileTradingSystem.parameters.slippage.error) + } + if (fileTradingSystem.parameters.feeStructure !== undefined) { + designerTradingSystem.parameters.feeStructure.payload.uiObject.setErrorMessage(fileTradingSystem.parameters.feeStructure.error) + } + } + + for (let j = 0; j < fileTradingSystem.strategies.length; j++) { + + let strategy = fileTradingSystem.strategies[j]; + designerTradingSystem.strategies[j].payload.uiObject.setErrorMessage(strategy.error) + + if (currentRecord.strategyNumber === j) { + designerTradingSystem.strategies[j].payload.uiObject.isExecuting = true + } else { + designerTradingSystem.strategies[j].payload.uiObject.isExecuting = false + } + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + designerTradingSystem.strategies[j].triggerStage.payload.uiObject.setErrorMessage(triggerStage.error) + + if (triggerStage.triggerOn !== undefined) { + designerTradingSystem.strategies[j].triggerStage.triggerOn.payload.uiObject.setErrorMessage(triggerStage.triggerOn.error) + + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + + let situation = triggerStage.triggerOn.situations[k]; + designerTradingSystem.strategies[j].triggerStage.triggerOn.situations[k].payload.uiObject.setErrorMessage(triggerStage.triggerOn.situations[k].error) + processSituation(situation, designerTradingSystem.strategies[j].triggerStage.triggerOn.situations[k]); + } + + for (let k = 0; k < triggerStage.triggerOn.announcements.length; k++) { + + let announcement = triggerStage.triggerOn.announcements[k]; + designerTradingSystem.strategies[j].triggerStage.triggerOn.announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].triggerStage.triggerOn.announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + } + + if (triggerStage.triggerOff !== undefined) { + + for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) { + + let situation = triggerStage.triggerOff.situations[k]; + designerTradingSystem.strategies[j].triggerStage.triggerOff.situations[k].payload.uiObject.setErrorMessage(triggerStage.triggerOff.situations[k].error) + processSituation(situation, designerTradingSystem.strategies[j].triggerStage.triggerOff.situations[k]); + } + + for (let k = 0; k < triggerStage.triggerOff.announcements.length; k++) { + + let announcement = triggerStage.triggerOff.announcements[k]; + designerTradingSystem.strategies[j].triggerStage.triggerOff.announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].triggerStage.triggerOff.announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + } + + if (triggerStage.takePosition !== undefined) { + + for (let k = 0; k < triggerStage.takePosition.situations.length; k++) { + + let situation = triggerStage.takePosition.situations[k]; + designerTradingSystem.strategies[j].triggerStage.takePosition.situations[k].payload.uiObject.setErrorMessage(triggerStage.takePosition.situations[k].error) + processSituation(situation, designerTradingSystem.strategies[j].triggerStage.takePosition.situations[k]); + } + + for (let k = 0; k < triggerStage.takePosition.announcements.length; k++) { + + let announcement = triggerStage.takePosition.announcements[k]; + designerTradingSystem.strategies[j].triggerStage.takePosition.announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].triggerStage.takePosition.announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + } + } + + let openStage = strategy.openStage + + if (openStage !== undefined) { + designerTradingSystem.strategies[j].openStage.payload.uiObject.setErrorMessage(openStage.error) + + let initialDefinition = openStage.initialDefinition + + if (initialDefinition !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.payload.uiObject.setErrorMessage(openStage.initialDefinition.error) + + if (initialDefinition.stopLoss !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.payload.uiObject.setErrorMessage(openStage.initialDefinition.stopLoss.error) + + for (let p = 0; p < initialDefinition.stopLoss.phases.length; p++) { + + if (currentRecord.strategyNumber === j && currentRecord.stopLossPhase === p) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].payload.uiObject.isExecuting = true + } else { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].payload.uiObject.isExecuting = false + } + + let phase = initialDefinition.stopLoss.phases[p]; + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].payload.uiObject.setErrorMessage(openStage.initialDefinition.stopLoss.phases[p].error) + + if (phase.formula !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].formula.payload.uiObject.setErrorMessage(openStage.initialDefinition.stopLoss.phases[p].formula.error) + + if (currentRecord.formulaErrors[formulaErrorsIndex] !== "") { + if (currentRecord.strategyNumber === j && currentRecord.stopLossPhase === p) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].formula.payload.uiObject.setErrorMessage(currentRecord.formulaErrors[formulaErrorsIndex]) + } + } + formulaErrorsIndex++ + + if (currentRecord.strategyNumber === j && currentRecord.stopLossPhase === p) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].formula.payload.uiObject.setValue(currentRecord.formulaValues[formulaValuesIndex]) + } + formulaValuesIndex++ + } + + for (let k = 0; k < phase.announcements.length; k++) { + + let announcement = phase.announcements[k]; + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].nextPhaseEvent.payload.uiObject.setErrorMessage(openStage.initialDefinition.stopLoss.phases[p].nextPhaseEvent.error) + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + processSituation(situation, designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].nextPhaseEvent.situations[k]); + } + + for (let k = 0; k < nextPhaseEvent.announcements.length; k++) { + + let announcement = nextPhaseEvent.announcements[k]; + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].nextPhaseEvent.announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.stopLoss.phases[p].nextPhaseEvent.announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + } + } + } + + if (initialDefinition.takeProfit !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.payload.uiObject.setErrorMessage(openStage.initialDefinition.takeProfit.error) + + for (let p = 0; p < initialDefinition.takeProfit.phases.length; p++) { + + if (currentRecord.strategyNumber === j && currentRecord.takeProfitPhase === p) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].payload.uiObject.isExecuting = true + } else { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].payload.uiObject.isExecuting = false + } + + let phase = initialDefinition.takeProfit.phases[p]; + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].payload.uiObject.setErrorMessage(openStage.initialDefinition.takeProfit.phases[p].error) + + if (phase.formula !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].formula.payload.uiObject.setErrorMessage(openStage.initialDefinition.takeProfit.phases[p].formula.error) + + if (currentRecord.formulaErrors[formulaErrorsIndex] !== "") { + if (currentRecord.strategyNumber === j && currentRecord.takeProfitPhase === p) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].formula.payload.uiObject.setErrorMessage(currentRecord.formulaErrors[formulaErrorsIndex]) + } + } + formulaErrorsIndex++ + + if (currentRecord.strategyNumber === j && currentRecord.takeProfitPhase === p) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].formula.payload.uiObject.setValue(currentRecord.formulaValues[formulaValuesIndex]) + } + formulaValuesIndex++ + } + + for (let k = 0; k < phase.announcements.length; k++) { + + let announcement = phase.announcements[k]; + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].nextPhaseEvent.payload.uiObject.setErrorMessage(openStage.initialDefinition.takeProfit.phases[p].nextPhaseEvent.error) + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + processSituation(situation, designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].nextPhaseEvent.situations[k]); + } + + for (let k = 0; k < nextPhaseEvent.announcements.length; k++) { + + let announcement = nextPhaseEvent.announcements[k]; + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].nextPhaseEvent.announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].openStage.initialDefinition.takeProfit.phases[p].nextPhaseEvent.announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + } + } + } + } + + let openExecution = openStage.openExecution + + if (openExecution !== undefined) { + designerTradingSystem.strategies[j].openStage.openExecution.payload.uiObject.setErrorMessage(openStage.openExecution.error) + } + } + + let manageStage = strategy.manageStage + + if (manageStage !== undefined) { + designerTradingSystem.strategies[j].manageStage.payload.uiObject.setErrorMessage(manageStage.error) + + if (manageStage.stopLoss !== undefined) { + designerTradingSystem.strategies[j].manageStage.stopLoss.payload.uiObject.setErrorMessage(manageStage.stopLoss.error) + + for (let p = 0; p < manageStage.stopLoss.phases.length; p++) { + + if (currentRecord.strategyNumber === j && currentRecord.stopLossPhase - 1 === p) { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].payload.uiObject.isExecuting = true + } else { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].payload.uiObject.isExecuting = false + } + + let phase = manageStage.stopLoss.phases[p]; + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].payload.uiObject.setErrorMessage(manageStage.stopLoss.phases[p].error) + + if (phase.formula !== undefined) { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].formula.payload.uiObject.setErrorMessage(manageStage.stopLoss.phases[p].formula.error) + + if (currentRecord.formulaErrors[formulaErrorsIndex] !== "") { + if (currentRecord.strategyNumber === j && currentRecord.stopLossPhase - 1 === p) { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].formula.payload.uiObject.setErrorMessage(currentRecord.formulaErrors[formulaErrorsIndex]) + } + } + formulaErrorsIndex++ + if (currentRecord.strategyNumber === j && currentRecord.stopLossPhase - 1 === p) { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].formula.payload.uiObject.setValue(currentRecord.formulaValues[formulaValuesIndex]) + } + formulaValuesIndex++ + } + + for (let k = 0; k < phase.announcements.length; k++) { + + let announcement = phase.announcements[k]; + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].nextPhaseEvent.payload.uiObject.setErrorMessage(manageStage.stopLoss.phases[p].nextPhaseEvent.error) + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + processSituation(situation, designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].nextPhaseEvent.situations[k]); + } + + for (let k = 0; k < nextPhaseEvent.announcements.length; k++) { + + let announcement = nextPhaseEvent.announcements[k]; + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].nextPhaseEvent.announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].manageStage.stopLoss.phases[p].nextPhaseEvent.announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + } + } + } + + if (manageStage.takeProfit !== undefined) { + designerTradingSystem.strategies[j].manageStage.takeProfit.payload.uiObject.setErrorMessage(manageStage.takeProfit.error) + + for (let p = 0; p < manageStage.takeProfit.phases.length; p++) { + + if (currentRecord.strategyNumber === j && currentRecord.takeProfitPhase - 1 === p) { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].payload.uiObject.isExecuting = true + } else { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].payload.uiObject.isExecuting = false + } + + let phase = manageStage.takeProfit.phases[p]; + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].payload.uiObject.setErrorMessage(manageStage.takeProfit.phases[p].error) + + if (phase.formula !== undefined) { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].formula.payload.uiObject.setErrorMessage(manageStage.takeProfit.phases[p].formula.error) + + if (currentRecord.formulaErrors[formulaErrorsIndex] !== "") { + if (currentRecord.strategyNumber === j && currentRecord.takeProfitPhase - 1 === p) { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].formula.payload.uiObject.setErrorMessage(currentRecord.formulaErrors[formulaErrorsIndex]) + } + } + formulaErrorsIndex++ + + if (currentRecord.strategyNumber === j && currentRecord.takeProfitPhase - 1 === p) { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].formula.payload.uiObject.setValue(currentRecord.formulaValues[formulaValuesIndex]) + } + formulaValuesIndex++ + } + + for (let k = 0; k < phase.announcements.length; k++) { + + let announcement = phase.announcements[k]; + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].nextPhaseEvent.payload.uiObject.setErrorMessage(manageStage.takeProfit.phases[p].nextPhaseEvent.error) + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + processSituation(situation, designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].nextPhaseEvent.situations[k]); + } + + for (let k = 0; k < nextPhaseEvent.announcements.length; k++) { + + let announcement = nextPhaseEvent.announcements[k]; + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].nextPhaseEvent.announcements[k].payload.uiObject.setErrorMessage(announcement.error) + if (announcement.formula !== undefined) { + designerTradingSystem.strategies[j].manageStage.takeProfit.phases[p].nextPhaseEvent.announcements[k].formula.payload.uiObject.setErrorMessage(announcement.formula.error) + } + } + } + } + } + } + + + let closeStage = strategy.closeStage + + if (closeStage !== undefined) { + designerTradingSystem.strategies[j].closeStage.payload.uiObject.setErrorMessage(closeStage.error) + + let closeExecution = closeStage.closeExecution + + if (closeExecution !== undefined) { + designerTradingSystem.strategies[j].closeStage.closeExecution.payload.uiObject.setErrorMessage(closeStage.closeExecution.error) + } + } + } + + function processSituation(situation, node) { + + node.payload.uiObject.setErrorMessage(situation.error) + + let highlightSituation = true + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = node.conditions[m] + if (condition !== undefined) { + condition.payload.uiObject.setErrorMessage(situation.conditions[m].error) + + if (situation.conditions[m].code !== undefined) { + if (condition.code !== undefined) { + condition.code.payload.uiObject.setErrorMessage(situation.conditions[m].code.error) + } + } + + if (currentRecord.conditionsValues[conditionIndex] === 1) { + condition.payload.uiObject.highlight() + } else { + highlightSituation = false + } + conditionIndex++; + } + } + + if (highlightSituation === true) { + node.payload.uiObject.highlight() + } + } + + + browserCanvasContext.closePath(); + browserCanvasContext.fill(); + + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] sendRecordInfoToDesignerSpace -> err = " + err.stack); } + } + } + + + + function onViewportZoomChanged(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onViewportZoomChanged -> Entering function."); } + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + } + } + + function onViewportPositionChanged(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onViewportPositionChanged -> Entering function."); } + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); } + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/FinalResults.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/FinalResults.js new file mode 100644 index 000000000..dcc9ce8d2 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/FinalResults.js @@ -0,0 +1,348 @@ +function newAAMastersPlottersTradingSimulationFinalResults() { + + const MODULE_NAME = "Final Results Plotter"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + getContainer: getContainer, + setTimePeriod: setTimePeriod, + setDatetime: setDatetime, + draw: draw, + recalculateScale: recalculateScale, + currentRecord: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let timeLineCoordinateSystem = newTimeLineCoordinateSystem(); // Needed to be able to plot on the timeline, otherwise not. + + let timePeriod; // This will hold the current Time Period the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let dataFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let singleFile; // This object will provide the different Market Files at different Time Periods. + + /* these are module specific variables: */ + + let records = []; // Here we keep the records to be ploted every time the Draw() function is called by the AAWebPlatform. + + let imageStopLossPhase; + let imageTakeProfitPhase; + let imageRecord; + + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let filesUpdatedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + + viewPort.eventHandler.stopListening(zoomChangedEventSubscriptionId); + viewPort.eventHandler.stopListening(offsetChangedEventSubscriptionId); + singleFile.eventHandler.stopListening(filesUpdatedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + + /* icons */ + + imageStopLossPhase = undefined; + imageTakeProfitPhase = undefined; + imageRecord = undefined; + + /* Destroy References */ + + singleFile = undefined; + + datetime = undefined; + timePeriod = undefined; + + dataFile = undefined; + fileCursor = undefined; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) { + + try { + + /* Store the information received. */ + + singleFile = pStorage.singleFile; + + datetime = pDatetime; + timePeriod = pTimePeriod; + + dataFile = singleFile.getFile(timePeriod); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = viewPort.eventHandler.listenToEvent("Zoom Changed", onZoomChanged); + offsetChangedEventSubscriptionId = viewPort.eventHandler.listenToEvent("Offset Changed", onOffsetChanged); + filesUpdatedEventSubscriptionId = singleFile.eventHandler.listenToEvent("Files Updated", onFilesUpdated); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + + /* Get ready for plotting. */ + + recalculate(); + + /* Ready for when dimmension changes. */ + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function getContainer(point) { + let container; + /* First we check if this point is inside this space. */ + if (this.container.frame.isThisPointHere(point) === true) { + return this.container; + } else { + /* This point does not belong to this space. */ + return undefined; + } + } + + function onFilesUpdated() { + try { + dataFile = singleFile.getFile(timePeriod); + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onFilesUpdated -> err = " + err.stack); } + } + } + + function setTimePeriod(pTimePeriod) { + try { + if (timePeriod !== pTimePeriod) { + timePeriod = pTimePeriod; + dataFile = singleFile.getFile(timePeriod); + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function draw() { + try { + // Nothing to draw for now + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + records = [] + + if (timePeriod >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Records Changed", records); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + if (dataFile === undefined) { return; } // Initialization not complete yet. + + records = []; + + for (let i = 0; i < dataFile.length; i++) { + + let record = {}; + + record.begin = dataFile[i][0]; + record.end = dataFile[i][1]; + record.type = dataFile[i][2]; + record.rate = dataFile[i][3]; + record.amount = dataFile[i][4]; + record.balanceA = dataFile[i][5]; + record.balanceB = dataFile[i][6]; + record.profit = dataFile[i][7]; + record.lastTradeProfitLoss = dataFile[i][8]; + record.stopLoss = dataFile[i][9]; + record.roundtrips = dataFile[i][10]; + record.hits = dataFile[i][11]; + record.fails = dataFile[i][12]; + record.hitRatio = dataFile[i][13]; + record.ROI = dataFile[i][14]; + record.periods = dataFile[i][15]; + record.days = dataFile[i][16]; + record.anualizedRateOfReturn = dataFile[i][17]; + record.sellRate = dataFile[i][18]; + record.lastTradeROI = dataFile[i][19]; + record.strategy = dataFile[i][20]; + record.strategyPhase = dataFile[i][21]; + record.takeProfit = dataFile[i][22]; + record.stopLossPhase = dataFile[i][23]; + record.takeProfitPhase = dataFile[i][24]; + record.positionSize = dataFile[i][26]; // 25 is the message for the executor + record.initialBalanceA = dataFile[i][27]; + record.minimunBalanceA = dataFile[i][28]; + record.maximunBalanceA = dataFile[i][29]; + record.initialBalanceB = dataFile[i][30]; + record.minimunBalanceB = dataFile[i][31]; + record.maximunBalanceB = dataFile[i][32]; + record.baseAsset = dataFile[i][33]; + record.positionPeriods = dataFile[i][34]; + record.positionDays = dataFile[i][35]; + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentRecord); + } + + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateScale() { + try { + // No scale needed + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScale -> err = " + err.stack); } + } + } + + function plotChart() { + try { + let userPosition = getUserPosition() + let userPositionDate = userPosition.point.x + + let record; + + /* Now we calculate and plot the records */ + for (let i = 0; i < records.length; i++) { // We do not start in 0 so as to be able to read the previous record i - 1 + + record = records[i]; + + /* Send the current record to the panel */ + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + + let eventRecords = { + currentRecord: record, + lastRecord: records[records.length - 1] + }; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", eventRecords); + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + function onZoomChanged(event) { + try { + recalculate(); + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onZoomChanged -> err = " + err.stack); } + } + } + + function onOffsetChanged() { + try { + if (Math.random() * 100 > 95) { + recalculate() + }; + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onOffsetChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + try { + recalculate(); + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/FinalResultsPanel.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/FinalResultsPanel.js new file mode 100644 index 000000000..0907ae991 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/FinalResultsPanel.js @@ -0,0 +1,360 @@ + +function newAAMastersPlottersTradingSimulationTradingSimulationFinalResultsPanel() { + + let thisObject = { + fitFunction: undefined, + onEventRaised: onEventRaised, + container: undefined, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + }; + + let container = newContainer(); + container.initialize(); + thisObject.container = container; + + container.displacement.containerName = "Final Results"; + container.frame.containerName = "Final Results"; + + let currentRecord; + let lastRecord; + let panelTabButton + + + return thisObject; + + function initialize() { + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL * 2; + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * 2.5; + + thisObject.container.frame.position.x = viewPort.visibleArea.topLeft.x + thisObject.container.frame.width * 1; + thisObject.container.frame.position.y = viewPort.visibleArea.topRight.y; + + panelTabButton = newPanelTabButton() + panelTabButton.parentContainer = thisObject.container + panelTabButton.container.frame.parentFrame = thisObject.container.frame + panelTabButton.fitFunction = thisObject.fitFunction + panelTabButton.initialize() + + } + + function finalize() { + + } + + function getContainer(point) { + + let container; + + container = panelTabButton.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container; + } + } + } + + + function onEventRaised(records) { + + currentRecord = records.currentRecord; + lastRecord = records.lastRecord + + } + + + function draw() { + + thisObject.container.frame.draw(false, false, true, thisObject.fitFunction); + + plotCurrentRecordInfo(); + + panelTabButton.draw() + } + + + function plotCurrentRecordInfo() { + + if (currentRecord === undefined) { return; } + if (lastRecord === undefined) { return; } + + const frameBodyHeight = thisObject.container.frame.getBodyHeight(); + const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight; + + const X_AXIS = thisObject.container.frame.width / 2; + const Y_AXIS = frameTitleHeight + frameBodyHeight / 2; + + + /* put the labels with the records values */ + + browserCanvasContext.beginPath(); + + let y = 0; + let increment = 0.024; + + y = y + increment; + y = y + increment; + printLabel('Current Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14); + + y = y + increment; + printLabel('Size', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.positionSize, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Rate', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.sellRate, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Stop', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.stopLoss, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Take Profit', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.takeProfit, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Periods', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.positionPeriods), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Days', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.positionDays).toFixed(2), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + y = y + increment; + printLabel('Latest Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14); + + y = y + increment; + printLabel('Profit / Loss', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.lastTradeProfitLoss), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('ROI', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.lastTradeROI).toFixed(2) + ' % ', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + + + let bifurcationLevel = y + y = y + increment; + y = y + increment; + printLabel('Partial Results', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1', 14); + + y = y + increment; + printLabel('Profit / Loss', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.profit, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Trades', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.roundtrips, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Hits', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.hits, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Fails', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.fails, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Hit Ratio', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.hitRatio * 100).toFixed(2) + ' %', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('ROI', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.ROI * 100).toFixed(2) + ' %', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Periods', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.periods, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Days', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.days, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Anualized Rate of Return', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.anualizedRateOfReturn * 100).toFixed(2) + ' %', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + + y = bifurcationLevel + + y = y + increment; + y = y + increment; + printLabel('Final Results', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1', 14); + + y = y + increment; + printLabel('Profit / Loss', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(lastRecord.profit, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Trades', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(lastRecord.roundtrips, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Hits', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(lastRecord.hits, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Fails', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(lastRecord.fails, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Hit Ratio', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((lastRecord.hitRatio * 100).toFixed(2) + ' %', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('ROI', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((lastRecord.ROI * 100).toFixed(2) + ' %', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Periods', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(lastRecord.periods, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Days', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(lastRecord.days, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Anualized Rate of Return', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((lastRecord.anualizedRateOfReturn * 100).toFixed(2) + ' %', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + + + function printLabel(labelToPrint, x, y, opacity, fontSize) { + + let labelPoint; + if (fontSize === undefined) { fontSize = 10 }; + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY; + + let label = '' + labelToPrint; + if (isNaN(label) === false) { + label = Number(label).toLocaleString(); + if ((label === "-0" || label === "0") && (labelToPrint !== 0) && (labelToPrint !== "0.00")) { + label = Number(labelToPrint).toFixed(6); + } + } + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; + + labelPoint = { + x: x - xOffset, + y: y + }; + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); + labelPoint = thisObject.fitFunction(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'; + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); + + } + + browserCanvasContext.closePath(); + browserCanvasContext.fill(); + + /* Parameters */ + let params + let paramsArray = [] + let positionTaken = false + + if (currentRecord.positionSize > 0) { + positionTaken = true + } + + params = {} + params.VALUE = currentRecord.balanceA; + params.INIT_VALUE = currentRecord.initialBalanceA + params.MIN_VALUE = currentRecord.minimunBalanceA + params.MAX_VALUE = currentRecord.maximunBalanceA + params.ASSET_LABEL = 'Asset A' + params.ASSET_NAME = DEFAULT_MARKET.assetB + params.LEFT_OFFSET = 100 + params.POSITION_TAKEN = positionTaken + params.BASE_ASSET = currentRecord.baseAsset + params.DECIMALS = 4 + + paramsArray.push(params) + + params = {} + params.VALUE = currentRecord.balanceB; + params.MIN_VALUE = currentRecord.minimunBalanceB + params.INIT_VALUE = currentRecord.initialBalanceB + params.MAX_VALUE = currentRecord.maximunBalanceB + params.ASSET_LABEL = 'Asset B' + params.ASSET_NAME = DEFAULT_MARKET.assetA + params.LEFT_OFFSET = 220 + params.POSITION_TAKEN = positionTaken + params.BASE_ASSET = currentRecord.baseAsset + params.DECIMALS = 2 + + paramsArray.push(params) + + canvas.cockpitSpace.assetBalances.setParamsArray(paramsArray) + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/SimulationExecution.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/SimulationExecution.js new file mode 100644 index 000000000..2717579d5 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/SimulationExecution.js @@ -0,0 +1,799 @@ +function newAAMastersPlottersTradingSimulationSimulationExecution() { + + const MODULE_NAME = "Simulation Execution Plotter"; + const INTENSIVE_LOG = false; + const ERROR_LOG = true; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + /* these are module specific variables: */ + + let fileSequence; + let plotElements = []; // This is where the elements to be plotted are stored before plotting. + let plotLines = []; // Here we store the lines of open positions. + + let previousNotesSetKey; + + let onMouseOverEventSuscriptionId + let offsetChangedEventSubscriptionId + let filesUpdatedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + fileSequence.eventHandler.stopListening(filesUpdatedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + + /* Destroyd References */ + + datetime = undefined; + timeFrame = undefined; + + fileSequence = undefined; + plotElements = undefined; + plotLines = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> finalize -> err = " + err.stack.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + fileSequence = pStorage.fileSequences[0]; + + recalculate(); + + filesUpdatedEventSubscriptionId = fileSequence.eventHandler.listenToEvent("Files Updated", onFilesUpdated); // Only the first sequence is supported right now. + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Ready for when dimmension changes. */ + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE); + + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> initialize -> err = " + err.stack); } + callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE); + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + try { + let container; + /* First we check if this point is inside this space. */ + if (this.container.frame.isThisPointHere(point) === true) { + return this.container; + } else { + /* This point does not belong to this space. */ + return undefined; + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> initialize -> err = " + err.stack); } + } + } + + function onFilesUpdated() { + recalculate(); + } + + function setTimeFrame(pTimeFrame) { + timeFrame = pTimeFrame; + recalculate(); + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onViewportPositionChanged() { + if (Math.random() * 100 > 95) { + recalculate() + } + } + + function recalculate() { + try { + if (fileSequence === undefined) { return; } + /* + + We are going to filter the records depending on the Time Frame. We want that for a 1 min time peroid all the records appears on screen, + but for higher periods, we will filter out some records, so that they do not overlap ever. + + */ + plotElements = []; + plotLines = []; + + let lastSellRate; + let lastSellDate; + let sellExecRate; + + let lastBuyRate; + let lastBuyDate; + let buyExecRate; + + let maxSequence = fileSequence.getFilesLoaded(); + + for (let j = 0; j < maxSequence; j++) { + + let file = fileSequence.getFile(j); + + let history = []; + let lines = []; + + /* First the small balls */ + + const ONE_MIN_IN_MILISECONDS = 60 * 1000; + let step = timeFrame / ONE_MIN_IN_MILISECONDS; + step = 1; // For now we dont discard records depending on the zoom level. + + let i = 0; + let lastRecordPushed = 0; + + for (i = 0; i < file.length; i = i + step) { + + let newHistoryRecord = { + + date: Math.trunc(file[i][0] / 60000) * 60000 + 30000, + buyAvgRate: file[i][1], + sellAvgRate: file[i][2], + + lastSellRate: file[i][3], + sellExecRate: file[i][4], + lastBuyRate: file[i][5], + buyExecRate: file[i][6], + + marketRate: file[i][7], + newPositions: file[i][8], + newTrades: file[i][9], + movedPositions: file[i][10], + profitsAssetA: file[i][11], + profitsAssetB: file[i][12], + combinedProfitsA: file[i][13], + combinedProfitsB: file[i][14], + datetime: file[i][0] + }; + + /* Finally we add this History Record to the Array */ + + history.push(newHistoryRecord); + lastRecordPushed = i; + + /* Here we build the lines. */ + + //if (timeFrame <= 1 * 60 * 1000) { + + if (newHistoryRecord.lastSellRate > 0) { + lastSellRate = newHistoryRecord.lastSellRate; + lastSellDate = newHistoryRecord.date; + } + + if (newHistoryRecord.sellExecRate > 0) { + let newLine = { + type: "sell", + x1: lastSellDate, + y1: lastSellRate, + x2: newHistoryRecord.date, + y2: newHistoryRecord.sellExecRate + }; + lines.push(newLine); + } + + if (newHistoryRecord.lastBuyRate > 0) { + lastBuyRate = newHistoryRecord.lastBuyRate; + lastBuyDate = newHistoryRecord.date; + } + + if (newHistoryRecord.buyExecRate > 0) { + let newLine = { + type: "buy", + x1: lastBuyDate, + y1: lastBuyRate, + x2: newHistoryRecord.date, + y2: newHistoryRecord.buyExecRate + }; + lines.push(newLine); + } + //} + } + + /* We allways want to put the last record of the file on the filterd dataset, so as to allways show the latest advance of the bot. */ + + i = file.length - 1; + + if (lastRecordPushed !== i) { + + let newHistoryRecord = { + + date: Math.trunc(file[i][0] / 60000) * 60000 + 30000, + buyAvgRate: file[i][1], + sellAvgRate: file[i][2], + + lastSellRate: file[i][3], + sellExecRate: file[i][4], + lastBuyRate: file[i][5], + buyExecRate: file[i][6], + + marketRate: file[i][7], + newPositions: file[i][8], + newTrades: file[i][9], + movedPositions: file[i][10], + profitsAssetA: file[i][11], + profitsAssetB: file[i][12], + combinedProfitsA: file[i][13], + combinedProfitsB: file[i][14] + }; + + history.push(newHistoryRecord); + + } + + + for (let i = 0; i < file.length; i++) { + + let newHistoryRecord = { + + date: Math.trunc(file[i][0] / 60000) * 60000 + 30000, + buyAvgRate: file[i][1], + sellAvgRate: file[i][2], + + lastSellRate: file[i][3], + sellExecRate: file[i][4], + lastBuyRate: file[i][5], + buyExecRate: file[i][6], + + marketRate: file[i][7], + newPositions: file[i][8], + newTrades: file[i][9], + movedPositions: file[i][10], + profitsAssetA: file[i][11], + profitsAssetB: file[i][12], + combinedProfitsA: file[i][13], + combinedProfitsB: file[i][14] + }; + + } + plotElements.push(history); + plotLines.push(lines); + } + + thisObject.container.eventHandler.raiseEvent("History Changed", history); + + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> recalculate -> err = " + err.stack); } + } + } + + function draw() { + plotChart(); + } + + function plotChart() { + try { + + let currentRecord + + let point = { + x: 0, + y: 0 + }; + + let history; + + for (let j = 0; j < plotElements.length; j++) { + + let history = plotElements[j]; + + for (let i = 0; i < history.length; i++) { + + record = history[i]; + currentRecord = record + + let timestamp = record.date // Math.trunc(record.date / timeFrame) * timeFrame + timeFrame / 2; + + point = { + x: timestamp, + y: record.marketRate + }; + + point = coordinateSystem.transformThisPoint(point); + point = transformThisPoint(point, thisObject.container); + + if (point.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { continue; } + + point = canvas.chartingSpace.viewport.fitIntoVisibleArea(point); + point = thisObject.fitFunction(point); + + let isCurrentRecord = false; + + if (userPositionDate >= record.date - 30000 && userPositionDate <= record.date + 30000 - 1) { + isCurrentRecord = true; + } + + let opacity = '1'; + let currentRadius = 10 + + /* Draw a red inverted triangle on exec sell */ + if (record.sellExecRate > 0) { + + opacity = '1'; + + let normalSize = 5 + let extraSize = 0 + if (isCurrentRecord) { extraSize = 5 } + + let point1 = { + x: record.date, + y: record.sellExecRate + }; + + let point2 = { + x: record.date + timeFrame / 7 * (normalSize + extraSize), + y: record.sellExecRate + }; + + let point3 = { + x: record.date - timeFrame / 7 * (normalSize + extraSize), + y: record.sellExecRate + }; + + point1 = coordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1); + point1 = thisObject.fitFunction(point1); + + point2 = coordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point2 = thisObject.fitFunction(point2); + + point3 = coordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y - diff; + point3.y = point3.y - diff; + + currentRecord.timeFrame = diff + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point2 = thisObject.fitFunction(point2); + point3 = thisObject.fitFunction(point3); + + 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(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; + + if (isCurrentRecord === false) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 0.25; + browserCanvasContext.stroke(); + + } + + /* Draw a green triangle on exec buy */ + if (record.buyExecRate > 0) { + + opacity = '1'; + + let normalSize = 5 + let extraSize = 0 + if (isCurrentRecord) { extraSize = 5 } + + let point1 = { + x: record.date, + y: record.buyExecRate + }; + + let point2 = { + x: record.date + timeFrame / 7 * (normalSize + extraSize), + y: record.buyExecRate + }; + + let point3 = { + x: record.date - timeFrame / 7 * (normalSize + extraSize), + y: record.buyExecRate + }; + + point1 = coordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1); + point1 = thisObject.fitFunction(point1); + + point2 = coordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point2 = thisObject.fitFunction(point2); + + point3 = coordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y + diff; + point3.y = point3.y + diff; + + currentRecord.timeFrame = diff + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point2 = thisObject.fitFunction(point2); + point3 = thisObject.fitFunction(point3); + + 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(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')'; + + if (isCurrentRecord === false) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 0.25; + browserCanvasContext.stroke(); + } + + /* Draw a red inverted triangle on sell */ + if (record.lastSellRate > 0) { + opacity = '1'; + + let normalSize = 5 + let extraSize = 0 + if (isCurrentRecord) { extraSize = 5 } + + let point1 = { + x: record.date, + y: record.lastSellRate + }; + + let point2 = { + x: record.date + timeFrame / 7 * (normalSize + extraSize), + y: record.lastSellRate + }; + + let point3 = { + x: record.date - timeFrame / 7 * (normalSize + extraSize), + y: record.lastSellRate + }; + + point1 = coordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1); + point1 = thisObject.fitFunction(point1); + + point2 = coordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point2 = thisObject.fitFunction(point2); + + point3 = coordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y - diff; + point3.y = point3.y - diff; + + currentRecord.timeFrame = diff + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point2 = thisObject.fitFunction(point2); + point3 = thisObject.fitFunction(point3); + + 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 (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 0.25; + browserCanvasContext.stroke(); + } + + /* Draw a green triangle on buy */ + if (record.lastBuyRate > 0) { + + opacity = '1'; + + let normalSize = 5 + let extraSize = 0 + if (isCurrentRecord) { extraSize = 5 } + + let point1 = { + x: record.date, + y: record.lastBuyRate + }; + + let point2 = { + x: record.date + timeFrame / 7 * (normalSize + extraSize), + y: record.lastBuyRate + }; + + let point3 = { + x: record.date - timeFrame / 7 * (normalSize + extraSize), + y: record.lastBuyRate + }; + + point1 = coordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1); + point1 = thisObject.fitFunction(point1); + + point2 = coordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point2 = thisObject.fitFunction(point2); + + point3 = coordinateSystem.transformThisPoint(point3); + point3 = transformThisPoint(point3, thisObject.container); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y + diff; + point3.y = point3.y + diff; + + currentRecord.timeFrame = diff + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + point2 = thisObject.fitFunction(point2); + point3 = thisObject.fitFunction(point3); + + 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 (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 0.25; + browserCanvasContext.stroke(); + } + + /* Send the current record to the panel */ + + if (userPositionDate >= record.date - 30000 && userPositionDate < record.date + 30000) { + thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentRecord); + } + + /* Circles */ + + let radius = 2; + currentRadius = 10 + + /* Outer Circle */ + + if (isCurrentRecord === true) { + + browserCanvasContext.beginPath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + + browserCanvasContext.arc(point.x, point.y, currentRadius, 0, Math.PI * 2, true); + browserCanvasContext.closePath(); + + if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else { + browserCanvasContext.fill(); + } + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 0.25; + browserCanvasContext.stroke(); + } + + /* Inner Circle */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + opacity + ')'; + + browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true); + browserCanvasContext.closePath(); + + if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else { + browserCanvasContext.fill(); + } + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.lineWidth = 0.25; + browserCanvasContext.stroke(); + + } + + /* Draw the lines connecting plot elements. */ + let lines = plotLines[j]; + + for (let i = 0; i < lines.length; i++) { + + let line = lines[i]; + + opacity = '1'; + + let point1 = { + x: line.x1, + y: line.y1 + }; + + let point2 = { + x: line.x2, + y: line.y2 + }; + + point1 = coordinateSystem.transformThisPoint(point1); + point1 = transformThisPoint(point1, thisObject.container); + point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1); + point1 = thisObject.fitFunction(point1); + + point2 = coordinateSystem.transformThisPoint(point2); + point2 = transformThisPoint(point2, thisObject.container); + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point2 = thisObject.fitFunction(point2); + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(point1.x, point1.y); + browserCanvasContext.lineTo(point2.x, point2.y); + + browserCanvasContext.closePath(); + + if (line.type === "sell") { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; + } + + browserCanvasContext.setLineDash([4, 1]) + browserCanvasContext.lineWidth = 0.25; + browserCanvasContext.stroke(); + } + + + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> plotChart -> err = " + err.stack); } + } + } +} + + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/SimulationExecutionPanel.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/SimulationExecutionPanel.js new file mode 100644 index 000000000..24ee27d23 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/SimulationExecutionPanel.js @@ -0,0 +1,465 @@ + +function newAAMastersPlottersTradingSimulationSimulationExecutionSimulationExecutionPanel() { + + + let thisObject = { + fitFunction: undefined, + container: undefined, + isVisible: true, + onRecordChange: onRecordChange, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + }; + + let container = newContainer(); + container.initialize(); + thisObject.container = container; + + container.frame.containerName = "Trading Execution"; + + let currentRecord; + let panelTabButton + + return thisObject; + + function finalize() { + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.isVisible = undefined + + currentRecord = undefined + panelTabButton.finalize() + panelTabButton = undefined + } + + function initialize() { + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL; + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * 2.5; + + thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topLeft.x + thisObject.container.frame.width * 4; + thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y; + + panelTabButton = newPanelTabButton() + panelTabButton.parentContainer = thisObject.container + panelTabButton.container.frame.parentFrame = thisObject.container.frame + panelTabButton.fitFunction = thisObject.fitFunction + panelTabButton.initialize() + } + + function getContainer(point) { + if (thisObject.isVisible !== true) { return } + let container; + + container = panelTabButton.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container; + } + } + } + + + function onRecordChange(lastCurrentCandle) { + + currentRecord = lastCurrentCandle; + + } + + + function draw() { + if (thisObject.isVisible !== true) { return } + thisObject.container.frame.draw(false, false, true, thisObject.fitFunction); + + plot(); + + panelTabButton.draw() + + } + + + function plot() { + + if (currentRecord === undefined) { return; } + + const frameBodyHeight = thisObject.container.frame.getBodyHeight(); + const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight; + + const X_AXIS = thisObject.container.frame.width / 2; + const Y_AXIS = frameTitleHeight + frameBodyHeight / 2; + + record = currentRecord + + point = { + x: X_AXIS, + y: Y_AXIS + }; + + point = thisObject.container.frame.frameThisPoint(point); + point = thisObject.fitFunction(point); + + let isCurrentRecord = true; + + let radius = 2; + let currentRadius = 10 + let opacity = '1'; + + /* Draw a red inverted triangle on exec sell */ + if (record.sellExecRate > 0) { + + opacity = '1'; + + let point1 = { + x: X_AXIS, + y: Y_AXIS + }; + + let point2 = { + x: X_AXIS + record.timeFrame / 4, + y: Y_AXIS + }; + + let point3 = { + x: X_AXIS - record.timeFrame / 4, + y: Y_AXIS + }; + + point1 = thisObject.container.frame.frameThisPoint(point1); + point1 = thisObject.fitFunction(point1); + + point2 = thisObject.container.frame.frameThisPoint(point2); + point2 = thisObject.fitFunction(point2); + + point3 = thisObject.container.frame.frameThisPoint(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y - diff; + point3.y = point3.y - diff; + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + + 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(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; + + if (isCurrentRecord === false) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + } + + /* Draw a green triangle on exec buy */ + if (record.buyExecRate > 0) { + + opacity = '1'; + + let point1 = { + x: X_AXIS, + y: Y_AXIS + }; + + let point2 = { + x: X_AXIS + record.timeFrame / 4, + y: Y_AXIS + }; + + let point3 = { + x: X_AXIS - record.timeFrame / 4, + y: Y_AXIS + }; + + point1 = thisObject.container.frame.frameThisPoint(point1); + point1 = thisObject.fitFunction(point1); + + point2 = thisObject.container.frame.frameThisPoint(point2); + point2 = thisObject.fitFunction(point2); + + point3 = thisObject.container.frame.frameThisPoint(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y + diff; + point3.y = point3.y + diff; + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + + 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(); + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')'; + + if (isCurrentRecord === false) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + } + + /* Draw a red inverted triangle on sell */ + if (record.lastSellRate > 0) { + opacity = '1'; + + let point1 = { + x: X_AXIS, + y: Y_AXIS + }; + + let point2 = { + x: X_AXIS + record.timeFrame / 4, + y: Y_AXIS + }; + + let point3 = { + x: X_AXIS - record.timeFrame / 4, + y: Y_AXIS + }; + + point1 = thisObject.container.frame.frameThisPoint(point1); + point1 = thisObject.fitFunction(point1); + + point2 = thisObject.container.frame.frameThisPoint(point2); + point2 = thisObject.fitFunction(point2); + + point3 = thisObject.container.frame.frameThisPoint(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y - diff; + point3.y = point3.y - diff; + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + + 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 (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + } + + /* Draw a green triangle on buy */ + if (record.lastBuyRate > 0) { + + opacity = '1'; + + let point1 = { + x: X_AXIS, + y: Y_AXIS + }; + + let point2 = { + x: X_AXIS + record.timeFrame / 4, + y: Y_AXIS + }; + + let point3 = { + x: X_AXIS - record.timeFrame / 4, + y: Y_AXIS + }; + + point1 = thisObject.container.frame.frameThisPoint(point1); + point1 = thisObject.fitFunction(point1); + + point2 = thisObject.container.frame.frameThisPoint(point2); + point2 = thisObject.fitFunction(point2); + + point3 = thisObject.container.frame.frameThisPoint(point3); + point3 = thisObject.fitFunction(point3); + + let diff = point2.x - point3.x; + point2.y = point2.y + diff; + point3.y = point3.y + diff; + + point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2); + point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3); + + 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 (isCurrentRecord === false) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')'; + } else { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + } + + browserCanvasContext.fill(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + } + + browserCanvasContext.lineWidth = 0.25; + + /* Outer Circle */ + + if (isCurrentRecord === true) { + + browserCanvasContext.beginPath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */ + + browserCanvasContext.arc(point.x, point.y, currentRadius, 0, Math.PI * 2, true); + browserCanvasContext.closePath(); + + if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else { + browserCanvasContext.fill(); + } + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + } + + /* Inner Circle */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + opacity + ')'; + + browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true); + browserCanvasContext.closePath(); + + if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else { + browserCanvasContext.fill(); + } + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + /* put the labels */ + + browserCanvasContext.beginPath(); + + let y; + let recordDate = new Date(currentRecord.datetime) + + printLabel('Market Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05 / 2, '1'); + printLabel(currentRecord.marketRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10 / 2, '0.50'); + + printLabel('Sell Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.15 / 2, '1'); + printLabel(currentRecord.lastSellRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.20 / 2, '0.50'); + + printLabel('Sell Executed Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.25 / 2, '1'); + printLabel(currentRecord.sellExecRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.30 / 2, '0.50'); + + printLabel('Buy Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.35 / 2, '1'); + printLabel(currentRecord.lastBuyRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.40 / 2, '0.40'); + + printLabel('Buy Executed Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.45 / 2, '1'); + printLabel(currentRecord.buyExecRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.50 / 2, '0.50'); + + printLabel('ROI Asset A', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.25 / 2), '1'); + printLabel((currentRecord.combinedProfitsB).toFixed(2) + '%', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.30 / 2), '0.50'); + + printLabel('ROI Asset B', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.15 / 2), '1'); + printLabel((currentRecord.combinedProfitsA).toFixed(2) + '%', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.20 / 2), '0.50'); + + printLabel('Datetime', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.05 / 2), '1'); + printLabel(recordDate.toUTCString(), X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.1 / 2), '0.50'); + + + function printLabel(labelToPrint, x, y, opacity) { + + let labelPoint; + let fontSize = 10; + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira'; + + let label = '' + labelToPrint; + if (isNaN(label) === false) { + label = Number(label).toLocaleString(); + } + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; + + labelPoint = { + x: x - xOffset, + y: y + }; + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); + labelPoint = thisObject.fitFunction(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'; + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); + + } + + browserCanvasContext.closePath(); + browserCanvasContext.fill(); + + } + + +} + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Strategies.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Strategies.js new file mode 100644 index 000000000..be159d9de --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Strategies.js @@ -0,0 +1,686 @@ +function newAAMastersPlottersTradingSimulationStrategies() { + + const MODULE_NAME = "Strategies Plotter"; + const INFO_LOG = false; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw, + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Secondary functions and properties. + + currentRecord: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let strategies = []; + let strategyImages = []; + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + if (INFO_LOG === true) { logger.write("[INFO] finalize -> Entering function."); } + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + strategyImages = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + /* Ready for when dimmension changes. */ + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + for (let i = 1; i < 15; i++) { + let strategyImage = canvas.designSpace.iconByUiObjectType.get('Strategy'); + strategyImages.push(strategyImage); + } + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + + try { + + 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; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + marketFile = marketFiles.getFile(pTimeFrame); + + recalculate(); + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing. + recalculate(); + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); } + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + strategies = [] + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Strategies Changed", strategies); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingDailyFiles -> Entering function."); } + + if (fileCursor === undefined) { + strategies = []; + return; + } // We need to wait + + if (fileCursor.files.size === 0) { + strategies = []; + return; + } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + strategies = []; + + + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let record = { + begin: undefined, + end: undefined, + status: undefined, + number: undefined, + beginRate: undefined, + endRate: undefined + }; + + record.begin = dailyFile[i][0]; + record.end = dailyFile[i][1]; + record.status = dailyFile[i][2]; + record.number = dailyFile[i][3]; + record.beginRate = dailyFile[i][4]; + record.endRate = dailyFile[i][5]; + + if ( + (record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) && + (record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x) + ) { + + strategies.push(record); + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Strategy Changed", thisObject.currentRecord); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by strategies. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + setTimeout(recalculate, 2000); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingMarketFiles -> Entering function."); } + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + strategies = []; + + for (let i = 0; i < marketFile.length; i++) { + + let record = { + begin: undefined, + end: undefined, + status: undefined, + number: undefined, + beginRate: undefined, + endRate: undefined + }; + + record.begin = marketFile[i][0]; + record.end = marketFile[i][1]; + record.status = marketFile[i][2]; + record.number = marketFile[i][3]; + record.beginRate = marketFile[i][4]; + record.endRate = marketFile[i][5]; + + if (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) { + + strategies.push(record); + + if ( + (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) && + (record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x) + ) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Strategy Changed", thisObject.currentRecord); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total strategies generated : " + strategies.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + thisObject.container.eventHandler.raiseEvent("Current Strategy Record Changed", undefined); + + let record; + + for (let i = 0; i < strategies.length; i++) { + + record = strategies[i]; + + let recordPoint1 = { + x: record.begin, + y: record.beginRate + }; + + let recordPoint2 = { + x: record.end, + y: record.endRate + }; + + let recordPoint3 = { + x: record.begin, + y: record.beginRate + }; + + let recordPoint4 = { + x: record.end, + y: record.endRate + }; + + recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1); + recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2); + recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3); + recordPoint4 = coordinateSystem.transformThisPoint(recordPoint4); + + recordPoint1 = transformThisPoint(recordPoint1, thisObject.container); + recordPoint2 = transformThisPoint(recordPoint2, thisObject.container); + recordPoint3 = transformThisPoint(recordPoint3, thisObject.container); + recordPoint4 = transformThisPoint(recordPoint4, thisObject.container); + + if (recordPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + continue; + } + + recordPoint3.y = recordPoint1.y + 2000; + recordPoint4.y = recordPoint1.y + 2000; + + recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1); + recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2); + recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3); + recordPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint4); + + recordPoint1 = thisObject.fitFunction(recordPoint1, undefined, 30); + recordPoint2 = thisObject.fitFunction(recordPoint2, undefined, 30); + recordPoint3 = thisObject.fitFunction(recordPoint3, undefined, 30); + recordPoint4 = thisObject.fitFunction(recordPoint4, undefined, 30); + + let imageSize = 20; + let imageToDraw = strategyImages[record.number]; + + + /* Draw the line that represents the duration of closed strategy */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(recordPoint3.x + imageSize / 2, recordPoint3.y); + browserCanvasContext.lineTo(recordPoint4.x - imageSize / 2, recordPoint4.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', 1)'; + browserCanvasContext.lineWidth = 1 + + if (record.status === 1) { + browserCanvasContext.setLineDash([0, 0]) + } else { + browserCanvasContext.setLineDash([2, 4]) + } + browserCanvasContext.stroke() + + + + drawStick(recordPoint1, recordPoint3); + drawStick(recordPoint2, recordPoint4); + + if (imageToDraw.canDrawIcon === true) { + browserCanvasContext.drawImage(imageToDraw, recordPoint3.x - imageSize / 2, recordPoint3.y - imageSize / 2, imageSize, imageSize); + if (record.status === 1) { + browserCanvasContext.drawImage(imageToDraw, recordPoint4.x - imageSize / 2, recordPoint4.y - imageSize / 2, imageSize, imageSize); + } + } + + /* Send the current record to the panel */ + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + + let currentRecord = { + }; + thisObject.container.eventHandler.raiseEvent("Current Strategy Record Changed", currentRecord); + } + + function drawStick(point1, point2) { + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(point1.x, point1.y); + browserCanvasContext.lineTo(point2.x, point2.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', 0.25)'; + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + + /* highlight the current record */ + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time + } + + browserCanvasContext.setLineDash([4, 3]) + browserCanvasContext.lineWidth = 0.5 + browserCanvasContext.stroke() + browserCanvasContext.setLineDash([0, 0]) + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + + function onViewportZoomChanged(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onViewportZoomChanged -> Entering function."); } + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + } + } + + function onViewportPositionChanged(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onViewportPositionChanged -> Entering function."); } + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); } + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + + + + + + + + + + + + + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Trades.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Trades.js new file mode 100644 index 000000000..d5bdcb9a1 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/Trades.js @@ -0,0 +1,683 @@ +function newAAMastersPlottersTradingSimulationTrades() { + + const MODULE_NAME = "Trades Plotter"; + const INFO_LOG = false; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw, + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Secondary functions and properties. + + currentRecord: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let trades = []; + let headers; + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + if (INFO_LOG === true) { logger.write("[INFO] finalize -> Entering function."); } + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* Destroyd References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + /* Ready for when dimmension changes. */ + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + + try { + + 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; + + } else { + + /* This point does not belong to this space. */ + + return undefined; + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); } + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + marketFile = marketFiles.getFile(pTimeFrame); + + recalculate(); + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing. + recalculate(); + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); } + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + trades = [] + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Trades Changed", trades); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingDailyFiles -> Entering function."); } + + if (fileCursor === undefined) { + trades = []; + return; + } // We need to wait + + if (fileCursor.files.size === 0) { + trades = []; + return; + } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + trades = []; + + + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let record = {}; + + record.begin = dailyFile[i][0]; + record.end = dailyFile[i][1]; + record.status = dailyFile[i][2]; + record.lastTradeROI = dailyFile[i][3]; + record.beginRate = dailyFile[i][4]; + record.endRate = dailyFile[i][5]; + record.exitType = dailyFile[i][6]; + + if ( + (record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) && + (record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x) + ) { + + trades.push(record); + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Trade Changed", thisObject.currentRecord); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by trades. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + setTimeout(recalculate, 2000); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingMarketFiles -> Entering function."); } + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + trades = []; + + for (let i = 0; i < marketFile.length; i++) { + + let record = {}; + + record.begin = marketFile[i][0]; + record.end = marketFile[i][1]; + record.status = marketFile[i][2]; + record.lastTradeROI = marketFile[i][3]; + record.beginRate = marketFile[i][4]; + record.endRate = marketFile[i][5]; + record.exitType = marketFile[i][6]; + + if ( + (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) && + (record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x) + ) { + + trades.push(record); + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Trade Changed", thisObject.currentRecord); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total trades generated : " + trades.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + thisObject.container.eventHandler.raiseEvent("Current Trade Record Changed", undefined); + + let record; + + for (let i = 0; i < trades.length; i++) { + + record = trades[i]; + + /* Send the current record to the panel */ + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + + let currentRecord = { + }; + thisObject.container.eventHandler.raiseEvent("Current Trade Record Changed", currentRecord); + } + + let recordPoint1 = { + x: record.begin, + y: record.beginRate + }; + + let recordPoint2 = { + x: record.end, + y: record.beginRate + }; + + let recordPoint3 = { + x: record.end, + y: record.endRate + }; + + recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1); + recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2); + recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3); + + recordPoint1 = transformThisPoint(recordPoint1, thisObject.container); + recordPoint2 = transformThisPoint(recordPoint2, thisObject.container); + recordPoint3 = transformThisPoint(recordPoint3, thisObject.container); + + if (recordPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + continue; + } + + recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1); + recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2); + recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3); + + recordPoint1 = thisObject.fitFunction(recordPoint1); + recordPoint2 = thisObject.fitFunction(recordPoint2); + recordPoint3 = thisObject.fitFunction(recordPoint3); + + let line1 = ''; + let line2 = ''; + + if (record.status === 1) { + switch (record.exitType) { + case 1: { + line1 = 'Exit: STOP'; + break; + } + case 2: { + line1 = 'Exit: TP'; + break; + } + } + } else { + line1 = 'Open Position'; + } + + if (record.lastTradeROI < 0) { + line2 = 'ROI:' + (record.lastTradeROI).toFixed(2) + ' %'; + } else { + line2 = 'ROI:' + (record.lastTradeROI).toFixed(2) + ' %'; + } + + /* Draw the triangle that represents the trade. */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(recordPoint1.x, recordPoint1.y); + browserCanvasContext.lineTo(recordPoint2.x, recordPoint2.y); + browserCanvasContext.lineTo(recordPoint3.x, recordPoint3.y); + + browserCanvasContext.closePath(); + + let opacity = '0.25'; + + if (record.lastTradeROI > 0) { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')'; + } else { + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; + } + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; + } + + browserCanvasContext.fill(); + + browserCanvasContext.lineWidth = 1; + browserCanvasContext.setLineDash([0, 0]) + browserCanvasContext.stroke(); + + let point = {} + + if (record.beginRate > record.endRate) { + point.x = recordPoint3.x + point.y = recordPoint3.y + 100 + } else { + point.x = recordPoint3.x + point.y = recordPoint3.y - 100 + } + + point = canvas.chartingSpace.viewport.fitIntoVisibleArea(point); + point = thisObject.fitFunction(point, undefined, 50); + + if ( + recordPoint3.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 250 + || + recordPoint3.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 250 + || + recordPoint3.y > canvas.chartingSpace.viewport.visibleArea.bottomRight.y - 150 + || + recordPoint3.y < canvas.chartingSpace.viewport.visibleArea.topLeft.y + 150 + ) { + // we do not write any text + } else { + + + printLabel(line1, recordPoint2.x - (recordPoint2.x - recordPoint1.x) / 2 - line1.length * FONT_ASPECT_RATIO, point.y, '1', 12); + printLabel(line2, recordPoint2.x - (recordPoint2.x - recordPoint1.x) / 2 - line2.length * FONT_ASPECT_RATIO, point.y + 15, '1', 12); + + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + + function onViewportZoomChanged(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onViewportZoomChanged -> Entering function."); } + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + } + } + + function onViewportPositionChanged(event) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onViewportPositionChanged -> Entering function."); } + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); } + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + + + + + + + + + + + + + + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulation.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulation.js new file mode 100644 index 000000000..b82eae6b0 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulation.js @@ -0,0 +1,859 @@ +function newAAMastersPlottersTradingSimulationTradingSimulation() { + + const MODULE_NAME = "Simulation Plotter"; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + + // Main functions and properties. + + initialize: initialize, + finalize: finalize, + container: undefined, + fitFunction: undefined, + getContainer: getContainer, + setTimeFrame: setTimeFrame, + setDatetime: setDatetime, + setCoordinateSystem: setCoordinateSystem, + draw: draw, + + /* Events declared outside the plotter. */ + + onDailyFileLoaded: onDailyFileLoaded, + + // Secondary functions and properties. + + currentRecord: undefined + }; + + /* this is part of the module template */ + + let container = newContainer(); // Do not touch this 3 lines, they are just needed. + container.initialize(); + thisObject.container = container; + + let coordinateSystem + + let timeFrame; // This will hold the current Time Frame the user is at. + let datetime; // This will hold the current Datetime the user is at. + + let marketFile; // This is the current Market File being plotted. + let fileCursor; // This is the current File Cursor being used to retrieve Daily Files. + + let marketFiles; // This object will provide the different Market Files at different Time Frames. + let dailyFiles; // This object will provide the different File Cursors at different Time Frames. + + /* these are module specific variables: */ + + let records = []; // Here we keep the records to be ploted every time the Draw() function is called by the AAWebPlatform. + + let imageStopLossPhase; + let imageTakeProfitPhase; + let imageRecord; + + let onMouseOverEventSuscriptionId + let zoomChangedEventSubscriptionId + let offsetChangedEventSubscriptionId + let dragFinishedEventSubscriptionId + let dimmensionsChangedEventSubscriptionId + let marketFilesUpdatedEventSubscriptionId + let dailyFilesUpdatedEventSubscriptionId + let scaleChangedEventSubscriptionId + + let userPositionDate + return thisObject; + + function finalize() { + try { + + /* Stop listening to the necesary events. */ + thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId) + canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId); + canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId); + canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId); + thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId) + marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId); + dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId); + + /* icons */ + + imageStopLossPhase = undefined; + imageTakeProfitPhase = undefined; + imageRecord = undefined; + + /* Destroy References */ + + marketFiles = undefined; + dailyFiles = undefined; + + datetime = undefined; + timeFrame = undefined; + + marketFile = undefined; + fileCursor = undefined; + + thisObject.fitFunction = undefined + + finalizeCoordinateSystem() + coordinateSystem = undefined + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); } + } + } + + function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) { + + try { + + /* Store the information received. */ + + marketFiles = pStorage.marketFiles[0]; + dailyFiles = pStorage.dailyFiles[0]; + + datetime = pDatetime; + timeFrame = pTimeFrame; + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + + /* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */ + + marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster. + + /* Now we set the right files according to current Period. */ + + marketFile = marketFiles.getFile(pTimeFrame); + fileCursor = dailyFiles.getFileCursor(pTimeFrame); + + /* Listen to the necesary events. */ + + zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged); + offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged); + dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); + marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated); + dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated); + onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + + /* Get ready for plotting. */ + + recalculate(); + + /* Ready for when dimmension changes. */ + + dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () { + recalculate(); + }) + + /* Loading a few icons */ + + imageStopLossPhase = canvas.designSpace.iconByUiObjectType.get('Stop'); + imageTakeProfitPhase = canvas.designSpace.iconByUiObjectType.get('Phase'); + imageRecord = canvas.designSpace.iconByUiObjectType.get('Trading System'); + + callBackFunction(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); } + } + } + + function initializeCoordinateSystem() { + scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged) + } + + function finalizeCoordinateSystem() { + coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId) + } + + function onScaleChanged() { + recalculate(); + } + + function onMouseOver(event) { + let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem) + userPositionDate = userPosition.valueOf() + } + + function getContainer(point) { + let container; + /* First we check if this point is inside this space. */ + if (this.container.frame.isThisPointHere(point) === true) { + return this.container; + } else { + /* This point does not belong to this space. */ + return undefined; + } + } + + function onMarketFilesUpdated() { + try { + let newMarketFile = marketFiles.getFile(timeFrame); + if (newMarketFile !== undefined) { + marketFile = newMarketFile; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); } + } + } + + function onDailyFilesUpdated() { + try { + let newFileCursor = dailyFiles.getFileCursor(timeFrame); + if (newFileCursor !== undefined) { + fileCursor = newFileCursor; + recalculate(); + } + } catch (err) { + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); } + } + } + + function setTimeFrame(pTimeFrame) { + + try { + + if (timeFrame !== pTimeFrame) { + + timeFrame = pTimeFrame; + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + marketFile = marketFiles.getFile(pTimeFrame); + + recalculate(); + + } else { + + let newFileCursor = dailyFiles.getFileCursor(pTimeFrame); + + fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing. + recalculate(); + + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); } + } + } + + function setDatetime(pDatetime) { + datetime = pDatetime; + } + + function setCoordinateSystem(pCoordinateSystem) { + finalizeCoordinateSystem() + coordinateSystem = pCoordinateSystem + initializeCoordinateSystem() + } + + function onDailyFileLoaded(event) { + + try { + + if (event.currentValue === event.totalValue) { + + /* This happens only when all of the files in the cursor have been loaded. */ + + recalculate(); + + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); } + } + } + + function draw() { + + try { + + if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } + + this.container.frame.draw(); + + plotChart(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); } + } + } + + function recalculate() { + + try { + + records = [] + + if (timeFrame >= _1_HOUR_IN_MILISECONDS) { + + recalculateUsingMarketFiles(); + + } else { + + recalculateUsingDailyFiles(); + + } + + thisObject.container.eventHandler.raiseEvent("Records Changed", records); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); } + } + } + + function recalculateUsingDailyFiles() { + + try { + + if (fileCursor === undefined) { + records = []; + return; + } // We need to wait + + if (fileCursor.files.size === 0) { + records = []; + return; + } // We need to wait until there are files in the cursor + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + let currentDate = new Date(farLeftDate.valueOf()); + + records = []; + + while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) { + + let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2); + + let dailyFile = fileCursor.files.get(stringDate); + + if (dailyFile !== undefined) { + + for (let i = 0; i < dailyFile.length; i++) { + + let record = {}; + + record.begin = dailyFile[i][0]; + record.end = dailyFile[i][1]; + record.type = dailyFile[i][2]; + record.rate = dailyFile[i][3]; + record.amount = dailyFile[i][4]; + record.balanceA = dailyFile[i][5]; + record.balanceB = dailyFile[i][6]; + record.profit = dailyFile[i][7]; + record.lastTradeProfitLoss = dailyFile[i][8]; + record.stopLoss = dailyFile[i][9]; + record.roundtrips = dailyFile[i][10]; + record.hits = dailyFile[i][11]; + record.fails = dailyFile[i][12]; + record.hitRatio = dailyFile[i][13]; + record.ROI = dailyFile[i][14]; + record.periods = dailyFile[i][15]; + record.days = dailyFile[i][16]; + record.anualizedRateOfReturn = dailyFile[i][17]; + record.sellRate = dailyFile[i][18]; + record.lastTradeROI = dailyFile[i][19]; + record.strategy = dailyFile[i][20]; + record.strategyPhase = dailyFile[i][21]; + record.takeProfit = dailyFile[i][22]; + record.stopLossPhase = dailyFile[i][23]; + record.takeProfitPhase = dailyFile[i][24]; + record.positionSize = dailyFile[i][26]; // 25 is the message for the executor + record.initialBalanceA = dailyFile[i][27]; + record.minimunBalanceA = dailyFile[i][28]; + record.maximunBalanceA = dailyFile[i][29]; + record.initialBalanceB = dailyFile[i][30]; + record.minimunBalanceB = dailyFile[i][31]; + record.maximunBalanceB = dailyFile[i][32]; + record.baseAsset = dailyFile[i][33]; + record.positionPeriods = dailyFile[i][34]; + record.positionDays = dailyFile[i][35]; + + if ( + (record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) && + (record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x) + ) { + + records.push(record); + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentRecord); + + } + } + } + } + + currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS); + } + + /* Lests check if all the visible screen is going to be covered by records. */ + + let lowerEnd = leftDate.valueOf(); + let upperEnd = rightDate.valueOf(); + + setTimeout(recalculate, 2000); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); } + } + } + + function recalculateUsingMarketFiles() { + + try { + + if (marketFile === undefined) { return; } // Initialization not complete yet. + + let daysOnSides = getSideDays(timeFrame); + + let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem); + let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem); + + let dateDiff = rightDate.valueOf() - leftDate.valueOf(); + + leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5); + rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5); + + records = []; + + for (let i = 0; i < marketFile.length; i++) { + + let record = {}; + + record.begin = marketFile[i][0]; + record.end = marketFile[i][1]; + record.type = marketFile[i][2]; + record.rate = marketFile[i][3]; + record.amount = marketFile[i][4]; + record.balanceA = marketFile[i][5]; + record.balanceB = marketFile[i][6]; + record.profit = marketFile[i][7]; + record.lastTradeProfitLoss = marketFile[i][8]; + record.stopLoss = marketFile[i][9]; + record.roundtrips = marketFile[i][10]; + record.hits = marketFile[i][11]; + record.fails = marketFile[i][12]; + record.hitRatio = marketFile[i][13]; + record.ROI = marketFile[i][14]; + record.periods = marketFile[i][15]; + record.days = marketFile[i][16]; + record.anualizedRateOfReturn = marketFile[i][17]; + record.sellRate = marketFile[i][18]; + record.lastTradeROI = marketFile[i][19]; + record.strategy = marketFile[i][20]; + record.strategyPhase = marketFile[i][21]; + record.takeProfit = marketFile[i][22]; + record.stopLossPhase = marketFile[i][23]; + record.takeProfitPhase = marketFile[i][24]; + record.positionSize = marketFile[i][26]; // 25 is the message for the executor + record.initialBalanceA = marketFile[i][27]; + record.minimunBalanceA = marketFile[i][28]; + record.maximunBalanceA = marketFile[i][29]; + record.initialBalanceB = marketFile[i][30]; + record.minimunBalanceB = marketFile[i][31]; + record.maximunBalanceB = marketFile[i][32]; + record.baseAsset = marketFile[i][33]; + record.positionPeriods = marketFile[i][34]; + record.positionDays = marketFile[i][35]; + + if ( + (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) && + (record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x) + ) { + + records.push(record); + + if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) { + + thisObject.currentRecord = record; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentRecord); + + } + } + } + + //console.log("Olivia > recalculateUsingMarketFiles > total records generated : " + records.length); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); } + } + } + + function plotChart() { + + try { + + let record; + + /* Now we calculate and plot the records */ + + for (let i = 0; i < records.length; i++) { // We do not start in 0 so as to be able to read the previous record i - 1 + + record = records[i]; + + /* Send the current record to the panel */ + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + + let eventRecords = { + currentRecord: record + }; + thisObject.container.eventHandler.raiseEvent("Current Record Changed", eventRecords); + } + + let stopLossPhase = -1; + let takeProfitPhase = -1; + + if (i > 0) { + if (record.stopLossPhase !== records[i - 1].stopLossPhase) { + stopLossPhase = record.stopLossPhase; + } + + if (record.takeProfitPhase !== records[i - 1].takeProfitPhase) { + takeProfitPhase = record.takeProfitPhase; + } + } + + let recordPoint1 = { + x: record.begin + timeFrame / 7 * 1.5, + y: thisObject.container.frame.height + }; + + let recordPoint2 = { + x: record.end + timeFrame / 7 * 5.5, + y: thisObject.container.frame.height + }; + + let recordPoint3 = { + x: recordPoint1.x + (recordPoint2.x - recordPoint1.x) / 2, + y: 0 + }; + + let recordPoint4 = { + x: record.begin + timeFrame / 7 * 1.5, + y: thisObject.container.frame.height * 100 + }; + + let recordPoint5 = { + x: record.begin, + y: record.stopLoss + }; + + let recordPoint6 = { + x: record.end, + y: record.stopLoss + }; + + if (record.stopLoss === 0) { // Put these points out of range if stopLoss is zero. + + recordPoint5.x = 0; + recordPoint6.x = 0; + } + + let recordPoint7 = { + x: record.begin, + y: record.sellRate + }; + + let recordPoint8 = { + x: record.end, + y: record.sellRate + }; + + if (record.sellRate === 0) { // Put these points out of range if sellRate is zero. + + recordPoint7.x = 0; + recordPoint8.x = 0; + } + + let recordPoint9 = { + x: record.begin, + y: record.takeProfit + }; + + let recordPoint10 = { + x: record.end, + y: record.takeProfit + }; + + if (record.takeProfit === 0) { // Put these points out of range if takeProfit is zero. + + recordPoint9.x = 0; + recordPoint10.x = 0; + } + + recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1); + recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2); + recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3); + recordPoint4 = coordinateSystem.transformThisPoint(recordPoint4); + recordPoint5 = coordinateSystem.transformThisPoint(recordPoint5); + recordPoint6 = coordinateSystem.transformThisPoint(recordPoint6); + recordPoint7 = coordinateSystem.transformThisPoint(recordPoint7); + recordPoint8 = coordinateSystem.transformThisPoint(recordPoint8); + recordPoint9 = coordinateSystem.transformThisPoint(recordPoint9); + recordPoint10 = coordinateSystem.transformThisPoint(recordPoint10); + + recordPoint1 = transformThisPoint(recordPoint1, thisObject.container); + recordPoint2 = transformThisPoint(recordPoint2, thisObject.container); + recordPoint3 = transformThisPoint(recordPoint3, thisObject.container); + recordPoint4 = transformThisPoint(recordPoint4, thisObject.container); + recordPoint5 = transformThisPoint(recordPoint5, thisObject.container); + recordPoint6 = transformThisPoint(recordPoint6, thisObject.container); + recordPoint7 = transformThisPoint(recordPoint7, thisObject.container); + recordPoint8 = transformThisPoint(recordPoint8, thisObject.container); + recordPoint9 = transformThisPoint(recordPoint9, thisObject.container); + recordPoint10 = transformThisPoint(recordPoint10, thisObject.container); + + recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1); + recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2); + recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3); + recordPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint4); + recordPoint5 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint5); + recordPoint6 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint6); + recordPoint7 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint7); + recordPoint8 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint8); + recordPoint9 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint9); + recordPoint10 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint10); + + recordPoint1 = thisObject.fitFunction(recordPoint1); + recordPoint2 = thisObject.fitFunction(recordPoint2); + recordPoint3 = thisObject.fitFunction(recordPoint3); + recordPoint4 = thisObject.fitFunction(recordPoint4); + recordPoint5 = thisObject.fitFunction(recordPoint5); + recordPoint6 = thisObject.fitFunction(recordPoint6); + recordPoint7 = thisObject.fitFunction(recordPoint7); + recordPoint8 = thisObject.fitFunction(recordPoint8); + recordPoint9 = thisObject.fitFunction(recordPoint9); + recordPoint10 = thisObject.fitFunction(recordPoint10); + + /* Image representing a Record */ + + let imageOffset = { + x: 0, + y: -30 + } + let imageSize = (recordPoint2.x - recordPoint1.x) / 2; + let recordLabel = record.periods.toLocaleString() + if (imageSize < 8) { + if (recordLabel[recordLabel.length - 1] !== '0' || imageSize < 4) + recordLabel = '' + } + let labelOffset = { + x: 0 - imageSize / 2 + 3 + imageOffset.x, + y: -2 - imageOffset.y + } + + let imagePosition = { + x: recordPoint4.x + imageOffset.x, + y: 0 + } + + imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition) + imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize) + + if (imageRecord.canDrawIcon === true) { + browserCanvasContext.drawImage(imageRecord, imagePosition.x, imagePosition.y, imageSize, imageSize); + browserCanvasContext.save(); + browserCanvasContext.translate(recordPoint3.x + labelOffset.x, recordPoint3.y + labelOffset.y); + browserCanvasContext.rotate(-Math.PI / 2); + printLabel(recordLabel, 0, 0, '1', 8); + browserCanvasContext.restore(); + } + + if (recordPoint5.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint5.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { + continue; + } + + /* Next we are drawing the position Rate */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(recordPoint7.x, recordPoint7.y); + browserCanvasContext.lineTo(recordPoint8.x, recordPoint8.y); + + browserCanvasContext.closePath(); + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', 1)'; + browserCanvasContext.lineWidth = 1 + browserCanvasContext.setLineDash([1, 4]) + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + browserCanvasContext.lineWidth = 5 + browserCanvasContext.setLineDash([3, 4]) + + /* highlight the current record */ + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time + } + + browserCanvasContext.stroke() + browserCanvasContext.setLineDash([0, 0]) + + imageSize = 20; + let imageToDraw; + + /* Depending on what is on top, it is also the text and image of the take profit / stop loss */ + + let yOffset = 0 + if (record.takeProfit > record.stopLoss) { yOffset = imageSize * 2 } + + /* Next we are drawing the stopLoss floor / ceilling */ + + browserCanvasContext.beginPath(); + + + browserCanvasContext.moveTo(recordPoint5.x, recordPoint5.y); + browserCanvasContext.lineTo(recordPoint6.x, recordPoint6.y); + + + browserCanvasContext.closePath(); + + let opacity = 1 + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ''; + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + + /* highlight the current record */ + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time + } + + browserCanvasContext.lineWidth = 1 + browserCanvasContext.stroke() + + imagePosition = { + x: recordPoint6.x - imageSize, + y: recordPoint6.y - imageSize * 1.25 + yOffset + } + + imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition) + imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize) + + let labelPosition = { + x: recordPoint6.x - imageSize / 2 - 3, + y: recordPoint6.y - imageSize * 1.5 + yOffset * 2 + } + labelPosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(labelPosition) + labelPosition = thisObject.fitFunction(labelPosition, undefined, 30) + + if (imageStopLossPhase.canDrawIcon === true && stopLossPhase > -1) { + browserCanvasContext.drawImage(imageStopLossPhase, imagePosition.x, imagePosition.y, imageSize, imageSize); + printLabel(stopLossPhase, labelPosition.x, labelPosition.y, '1', 9); + } + + /* Next we are drawing the Take Profit */ + + browserCanvasContext.beginPath(); + + browserCanvasContext.moveTo(recordPoint9.x, recordPoint9.y); + browserCanvasContext.lineTo(recordPoint10.x, recordPoint10.y); + + browserCanvasContext.closePath(); + + opacity = 1 + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ''; + + if (userPositionDate >= record.begin && userPositionDate <= record.end) { + + /* highlight the current record */ + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time + } + + browserCanvasContext.lineWidth = 1 + browserCanvasContext.stroke() + + imagePosition = { + x: recordPoint10.x - imageSize * 2 / 3, + y: recordPoint10.y + imageSize / 4 - yOffset + } + imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition) + imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize) + + labelPosition = { + x: recordPoint10.x - imageSize * 1 / 3, + y: recordPoint10.y + imageSize * 1.9 - yOffset * 2 + } + labelPosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(labelPosition) + labelPosition = thisObject.fitFunction(labelPosition, undefined, 30) + + if (imageTakeProfitPhase.canDrawIcon === true && takeProfitPhase > -1) { + browserCanvasContext.drawImage(imageTakeProfitPhase, imagePosition.x, imagePosition.y, imageSize, imageSize); + printLabel(takeProfitPhase, labelPosition.x, labelPosition.y, '1', 9); + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); } + } + } + + + function onViewportZoomChanged(event) { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); } + } + } + + function onViewportPositionChanged(event) { + + try { + + if (event !== undefined) { + if (event.recalculate === true) { + recalculate() + return + } + } + if (Math.random() * 100 > 95) { + recalculate() + }; + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); } + } + } + + function onDragFinished() { + + try { + + recalculate(); + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); } + } + } +} + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulationPanel.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulationPanel.js new file mode 100644 index 000000000..5781fa645 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulationPanel.js @@ -0,0 +1,324 @@ + +function newAAMastersPlottersTradingSimulationTradingSimulationTradingSimulationPanel() { + + let thisObject = { + fitFunction: undefined, + onRecordChange: onRecordChange, + container: undefined, + session: undefined, + isVisible: true, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + }; + + let container = newContainer(); + container.initialize(); + thisObject.container = container; + + container.frame.containerName = "Simulation"; + + let currentRecord; + let lastRecord; + let panelTabButton + + + return thisObject; + + function initialize() { + + thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL * 1; + thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * 2.5; + + thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 1; + thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y; + + panelTabButton = newPanelTabButton() + panelTabButton.parentContainer = thisObject.container + panelTabButton.container.frame.parentFrame = thisObject.container.frame + panelTabButton.fitFunction = thisObject.fitFunction + panelTabButton.initialize() + + } + + function finalize() { + thisObject.session = undefined + + thisObject.container.finalize() + thisObject.container = undefined + thisObject.fitFunction = undefined + thisObject.onRecordChange = undefined + thisObject.isVisible = undefined + + currentRecord = undefined + lastRecord = undefined + panelTabButton = undefined + } + + function getContainer(point) { + if (thisObject.isVisible !== true) { return } + let container; + + container = panelTabButton.getContainer(point) + if (container !== undefined) { return container } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + + let checkPoint = { + x: point.x, + y: point.y + } + + checkPoint = thisObject.fitFunction(checkPoint) + + if (point.x === checkPoint.x && point.y === checkPoint.y) { + return thisObject.container; + } + } + } + + + function onRecordChange(records) { + + currentRecord = records.currentRecord; + lastRecord = records.lastRecord + + } + + + function draw() { + if (thisObject.isVisible !== true) { return } + thisObject.container.frame.draw(false, false, true, thisObject.fitFunction); + + plotCurrentRecordInfo(); + + panelTabButton.draw() + + /* Define panel name */ + if (thisObject.session !== undefined) { + const MAX_LABEL_LENGTH = 25 + if (thisObject.session.name.length > MAX_LABEL_LENGTH) { + container.frame.containerName = thisObject.session.name.substring(0, MAX_LABEL_LENGTH) + '...' + } else { + container.frame.containerName = thisObject.session.name + } + } + } + + + function plotCurrentRecordInfo() { + + if (currentRecord === undefined) { return; } + + const frameBodyHeight = thisObject.container.frame.getBodyHeight(); + const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight; + + const X_AXIS = thisObject.container.frame.width / 2; + const Y_AXIS = frameTitleHeight + frameBodyHeight / 2; + + + /* put the labels with the records values */ + + browserCanvasContext.beginPath(); + + let y = 0; + let increment = 0.024; + + y = y + increment; + y = y + increment; + printLabel('Current Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14); + + y = y + increment; + printLabel('Size', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.positionSize, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Rate', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.sellRate, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Stop', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.stopLoss, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Take Profit', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.takeProfit, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Periods', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.positionPeriods), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Days', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.positionDays).toFixed(2), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + y = y + increment; + printLabel('Latest Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14); + + y = y + increment; + printLabel('Profit / Loss', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.lastTradeProfitLoss), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('ROI', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.lastTradeROI).toFixed(2) + ' % ', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + y = y + increment; + printLabel('Partial Results', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1', 14); + + y = y + increment; + printLabel('Profit / Loss', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.profit, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Trades', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.roundtrips, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Hits', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.hits, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Fails', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.fails, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Hit Ratio', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.hitRatio * 100).toFixed(2) + ' %', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('ROI', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.ROI * 100).toFixed(2) + ' %', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Periods', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.periods, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Days', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel(currentRecord.days, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + y = y + increment; + printLabel('Anualized Rate of Return', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60'); + y = y + increment; + printLabel((currentRecord.anualizedRateOfReturn * 100).toFixed(2) + ' %', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14); + + + function printLabel(labelToPrint, x, y, opacity, fontSize) { + + let labelPoint; + if (fontSize === undefined) { fontSize = 10 }; + + browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY; + + let label = '' + labelToPrint; + if (isNaN(label) === false) { + label = Number(label).toLocaleString(); + if ((label === "-0" || label === "0") && (labelToPrint !== 0) && (labelToPrint !== "0.00")) { + label = Number(labelToPrint).toFixed(6); + } + } + let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; + + labelPoint = { + x: x - xOffset, + y: y + }; + + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); + labelPoint = thisObject.fitFunction(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'; + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); + + } + + browserCanvasContext.closePath(); + browserCanvasContext.fill(); + + /* Parameters */ + let params + let paramsArray = [] + let positionTaken = false + + if (currentRecord.positionSize > 0) { + positionTaken = true + } + + params = {} + params.VALUE = currentRecord.balanceA; + params.INIT_VALUE = currentRecord.initialBalanceA + params.MIN_VALUE = currentRecord.minimunBalanceA + params.MAX_VALUE = currentRecord.maximunBalanceA + params.ASSET_LABEL = 'Asset A' + params.ASSET_NAME = DEFAULT_MARKET.assetB + params.LEFT_OFFSET = 100 + params.POSITION_TAKEN = positionTaken + params.BASE_ASSET = currentRecord.baseAsset + params.DECIMALS = 4 + + paramsArray.push(params) + + params = {} + params.VALUE = currentRecord.balanceB; + params.MIN_VALUE = currentRecord.minimunBalanceB + params.INIT_VALUE = currentRecord.initialBalanceB + params.MAX_VALUE = currentRecord.maximunBalanceB + params.ASSET_LABEL = 'Asset B' + params.ASSET_NAME = DEFAULT_MARKET.assetA + params.LEFT_OFFSET = 220 + params.POSITION_TAKEN = positionTaken + params.BASE_ASSET = currentRecord.baseAsset + params.DECIMALS = 2 + + paramsArray.push(params) + + canvas.cockpitSpace.assetBalances.setParamsArray(paramsArray) + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/this.plotter.config.json b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/this.plotter.config.json new file mode 100644 index 000000000..d1c9d6830 --- /dev/null +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/this.plotter.config.json @@ -0,0 +1,56 @@ +{ + "displayName": "Plotters Trading Simulation", + "codeName": "PlottersTradingSimulation", + "dataMine": "AAMasters", + "modules": [ + { + "codeName": "TradingSimulation", + "moduleName": "TradingSimulation", + "description": "Plots Bollinger Records.", + "profilePicture": "TradingSimulation.png", + "panels": [ + { + "codeName": "TradingSimulationPanel", + "moduleName": "TradingSimulationPanel", + "event": "Current Record Changed" + } + ] + }, + { + "codeName": "Conditions", + "moduleName": "Conditions", + "description": "Plots Simulation Conditions.", + "profilePicture": "Conditions.png", + "panels": [] + }, + { + "codeName": "Strategies", + "moduleName": "Strategies", + "description": "Plots Strategies.", + "profilePicture": "Strategies.png", + "panels": [ + ] + }, + { + "codeName": "Trades", + "moduleName": "Trades", + "description": "Plots Trades.", + "profilePicture": "Trades.png", + "panels": [ + ] + }, + { + "codeName": "SimulationExecution", + "moduleName": "SimulationExecution", + "description": "Plots Simulation Execution.", + "profilePicture": "SimulationExecution.png", + "panels": [ + { + "codeName": "SimulationExecutionPanel", + "moduleName": "SimulationExecutionPanel", + "event": "Current Record Changed" + } + ] + } + ] +} \ No newline at end of file diff --git a/Libraries/SystemEventsClient/SystemEventHandler.js b/Libraries/SystemEventsClient/SystemEventHandler.js new file mode 100644 index 000000000..7620ca72e --- /dev/null +++ b/Libraries/SystemEventsClient/SystemEventHandler.js @@ -0,0 +1,178 @@ +exports.newSystemEventHandler = function newSystemEventHandler(ipc) { + let thisObject = { + initialize: initialize, + finalize: finalize, + createEventHandler: createEventHandler, + deleteEventHandler: deleteEventHandler, + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent + } + + let eventListeners = new Map() + let responseWaiters = new Map() + + ipc.config.id = 'Task Server'; + ipc.config.retry = 1500; + ipc.config.silent = true; + + return thisObject + + function initialize(canllerName, callBackFunction) { + + ipc.connectTo( + 'world', + function () { + ipc.of.world.on( + 'connect', + function () { + ipc.log('## Connected to Superalgos Event Server ##'.rainbow, ipc.config.delay); + + let eventCommand = { + action: 'greetings', + from: canllerName + } + + sendCommand(eventCommand) + + callBackFunction() + } + ); + ipc.of.world.on( + 'disconnect', + function () { + ipc.log('Disconnected from Superalgos Event Server '.notice); + } + ); + ipc.of.world.on( + 'message', // here is where all incomming messages are processed. + function (data) { + ipc.log('Got a message from Superalgos Event Server: '.debug, data); + + let message = JSON.parse(data) + + if (message.action === 'Event Raised') { + let key + if (message.callerId) { + key = message.eventHandlerName + '-' + message.eventType + '-' + message.callerId + } else { + key = message.eventHandlerName + '-' + message.eventType + } + let handler = eventListeners.get(key) + if (handler) { + handler.callBack(message) + } else { + console.log(key + ' not found so could not deliver event raised.') + console.log(' Message = ' + data) + } + return + } + + if (message.action === 'Event Server Response') { + let handler = responseWaiters.get(message.callerId) + if (handler) { + handler(message) + } + return + } + } + ); + } + ); + } + + function finalize() { + /* Before disconnecting we will forze all eventListeners to stop listening. */ + const eventListenersArray = [...eventListeners.values()] + for (let i = 0; i < eventListenersArray.length; i++) { + let handler = eventListenersArray[i] + let eventCommand = { + action: 'stopListening', + eventHandlerName: handler.eventHandlerName, + eventType: handler.eventType, + callerId: handler.callerId + } + sendCommand(eventCommand) + } + ipc.disconnect('world'); + } + + function sendCommand(command, responseCallBack, eventsCallBack) { + + if (command.action === 'listenToEvent') { + let key + if (command.callerId) { + key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId + } else { + key = command.eventHandlerName + '-' + command.eventType + } + let handler = { + eventHandlerName: command.eventHandlerName, + eventType: command.eventType, + callerId: command.callerId, + callBack: eventsCallBack + } + eventListeners.set(key, handler) + } + if (command.callerId && responseCallBack) { + responseWaiters.set(command.callerId, responseCallBack) + } + + ipc.of.world.emit( + 'message', + JSON.stringify(command) + ) + } + + function createEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'createEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function deleteEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'deleteEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function listenToEvent(eventHandlerName, eventType, extraData, callerId, responseCallBack, eventsCallBack) { + let eventCommand = { + action: 'listenToEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + extraData: extraData, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack, eventsCallBack) + } + + function stopListening(eventHandlerName, eventType, eventSubscriptionId, callerId, responseCallBack) { + /* User either needs to specify a valid eventSubscriptionId OR the 3 params: eventHandlerName, eventType, callerId which were used when start listening to events. */ + let eventCommand = { + action: 'stopListening', + eventHandlerName: eventHandlerName, + eventType: eventType, + eventSubscriptionId: eventSubscriptionId, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function raiseEvent(eventHandlerName, eventType, event, callerId, responseCallBack) { + let eventCommand = { + action: 'raiseEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + event: event, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } +} \ No newline at end of file diff --git a/README.md b/README.md index 0928d9827..c0e3117aa 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,43 @@ The Superalgos Platform provides a visual environment for developing, testing an ![Illustration](https://user-images.githubusercontent.com/13994516/63528460-4550ae80-c503-11e9-8db6-22995e0b9c16.gif) -## Users Manual +## Testing Exchanges -Visit the [Users Manual](https://github.com/Superalgos/Platform/wiki) for instructions on how to download, install, fire up and use the app. +The version in development opens up the door to working with multiple exchanges and multiple markets. For your reference, this is a quick report on the progress of our tests: -## Code and Development Environment +| Exchange | Historic Data Capability [*] | Trading Capability [**] | +| :--- | :---: | :---: | +| Binance | ☑ | ☑ | +| Bitfinex | ☑ | ☑ | +| Bitmex | ☑ | ☐ | +| HitBTC | ☐ | ☐ | +| Kraken | ☑ | ☐ | +| Poloniex | ☐ | ☐ | -The Superalgos Platform draws code from several repositories within the Superalgos Github organization, as well as from organizations belonging to Teams. +[*] The ability to fetch historic data for backtesting purposes has been verified. -The concerned code is distributed with the app's release and constitutes a fully functional development environment right out of the package. The app runs uncompilled on the actual code. +[**] The ability to run strategies in live-trading mode has been verified. + +## Documentation + +The [Users Manual](https://github.com/Superalgos/Platform/wiki) in this repository's wiki features the documentation corresponding to the latest release. + +A [new documentation site](https://superalgos.github.io/Documentation/) is in the making, covering the functionality in development. + +## Codebase and Development Environment + +To build each release, the Superalgos Platform used to draw code from several repositories within the Superalgos Github organization, as well as from organizations belonging to Teams. This has changed. + +The ```develop``` branch in this repository now features the latest codebase and development environment. + +The codebase is distributed with the platform's release, and constitutes a fully functional development environment right out of the package. The platform runs uncompilled on the actual code (```node run``` from the root folder). + +> If you are testing the version in development, you need to use the workspace in the root directory. Workspaces of older versions do not work in the new one. ## Contributing -We'd love you to join us in the Dev Team. Please see [CONTRIBUTING.md](https://github.com/Superalgos/Platform/blob/master/CONTRIBUTING.md) for the details on how to engage with it. +We'd love you to join us in the Dev Team. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for the details on how to engage with it. ## License -The Superalgos Desktop App is open-source software released under [Apache License 2.0](https://github.com/Superalgos/Platform/blob/master/LICENSE). +The Superalgos Desktop App is open-source software released under [Apache License 2.0](LICENSE). diff --git a/Share - Pre-Release Testing Workspace.json b/Share - Pre-Release Testing Workspace.json new file mode 100644 index 000000000..b338e86cb --- /dev/null +++ b/Share - Pre-Release Testing Workspace.json @@ -0,0 +1 @@ +{"type":"Workspace","name":"My Workspace","rootNodes":[{"type":"Network","name":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","dataStorage":{"type":"Data Storage","name":"New Data Storage","sessionIndependentData":{"type":"Session Independent Data","name":"New Session Independent Data","singleMarketData":[{"type":"Single Market Data","name":"Poloniex BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502","savedPayload":{"position":{"x":20539.95488621884,"y":29594.63790500938},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candles","id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"}}},{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c","savedPayload":{"position":{"x":20443.01671365366,"y":29528.01414484369},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"}}},{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9","savedPayload":{"position":{"x":20359.12034429072,"y":29445.56941228539},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candle-Stairs","id":"642474e9-da61-4168-97dc-7f7869bebc02"}}},{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086","savedPayload":{"position":{"x":20290.81492685951,"y":29349.808748814212},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volume-Stairs","id":"846e9572-c4d6-4c7e-942e-c82402c5ecea"}}},{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41","savedPayload":{"position":{"x":20240.175886904468,"y":29243.641793732495},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2","savedPayload":{"position":{"x":20208.741866030035,"y":29130.294376247326},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951","savedPayload":{"position":{"x":20197.467971054095,"y":29013.21050028243},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0","savedPayload":{"position":{"x":20206.69675357063,"y":28895.947700161698},"targetPosition":{"x":20872.24233158646,"y":29007.32183364947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}}],"id":"49a747b4-2eb7-41d6-b0e2-493f43ceed44","savedPayload":{"position":{"x":20872.24233158646,"y":29007.32183364947},"targetPosition":{"x":21481.306930973227,"y":28716.812921909375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},{"type":"Single Market Data","name":"Kraken ETH/BTC","dataProducts":[{"type":"Data Product","name":"Candles","id":"fce50098-d674-4006-94ad-cce46f3d59a4","savedPayload":{"position":{"x":20165.65867678313,"y":28960.65391280681},"targetPosition":{"x":20807.43166614526,"y":28752.12922805278},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candles","id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"}}},{"type":"Data Product","name":"Volumes","id":"601b3615-7045-4e8f-aaa5-a87648fa0f39","savedPayload":{"position":{"x":20147.377611517957,"y":28611.830407684844},"targetPosition":{"x":20807.43166614526,"y":28752.12922805278},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"}}}],"id":"b20c85c2-6f51-4005-a8dc-c8553567852b","savedPayload":{"position":{"x":20807.43166614526,"y":28752.12922805278},"targetPosition":{"x":21481.306930973227,"y":28716.812921909375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Market","name":"ETH/BTC","id":"f43663e0-c14d-4fb9-9655-25dff3f938c7"}}},{"type":"Single Market Data","name":"Kraken BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"6e33e35c-8107-4ca4-affd-c54b61ab27bd","savedPayload":{"position":{"x":20194.954518398597,"y":28671.892505824704},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candles","id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"}}},{"type":"Data Product","name":"Volumes","id":"499cfa98-062c-467f-b5b8-7cc935dc7caf","savedPayload":{"position":{"x":20173.51899182323,"y":28556.236754046728},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"}}},{"type":"Data Product","name":"Candle-Stairs","id":"3d597096-5047-4c1c-9dd6-4a718e0fc207","savedPayload":{"position":{"x":20172.492529594852,"y":28438.61583288818},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candle-Stairs","id":"642474e9-da61-4168-97dc-7f7869bebc02"}}},{"type":"Data Product","name":"Volume-Stairs","id":"c1ea8a7a-8178-45ba-816c-014039b9cc9b","savedPayload":{"position":{"x":20191.906320248858,"y":28322.6035945194},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volume-Stairs","id":"846e9572-c4d6-4c7e-942e-c82402c5ecea"}}},{"type":"Data Product","name":"Bollinger Bands","id":"042f75fe-2c93-43dd-a2fb-56c9f5c28bb6","savedPayload":{"position":{"x":20231.17048558008,"y":28211.725012098203},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"a786bd1e-f68d-41f3-88c7-67d658096f90","savedPayload":{"position":{"x":20289.092003793554,"y":28109.349075244187},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"2f60b6e6-c460-4b25-b5b7-b824ce38f63b","savedPayload":{"position":{"x":20363.910958868066,"y":28018.58642499393},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"a6578dfd-194a-4ea9-bd4f-35545816d9ad","savedPayload":{"position":{"x":20453.354014713896,"y":27942.194838547177},"targetPosition":{"x":20845.212401079632,"y":28491.56003498887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}}],"id":"49195672-1b26-4197-94cb-ce57f1d3e34e","savedPayload":{"position":{"x":20845.212401079632,"y":28491.56003498887},"targetPosition":{"x":21481.306930973227,"y":28716.812921909375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},{"type":"Single Market Data","name":"Bitfinex BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"19c78a01-0c97-48eb-a8d5-09ca3c42d9fd","savedPayload":{"position":{"x":20310.062564409563,"y":28183.04611242168},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candles","id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"}}},{"type":"Data Product","name":"Volumes","id":"23311eba-8ecd-41c9-aeed-86d7045b7be7","savedPayload":{"position":{"x":20334.518260201137,"y":28067.99110965239},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"}}},{"type":"Data Product","name":"Candle-Stairs","id":"1f860844-028c-41c8-a7df-f764306947a0","savedPayload":{"position":{"x":20378.58151058434,"y":27958.930737910134},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candle-Stairs","id":"642474e9-da61-4168-97dc-7f7869bebc02"}}},{"type":"Data Product","name":"Volume-Stairs","id":"a4267a80-ee05-41e7-ad62-30e092a91812","savedPayload":{"position":{"x":20440.91347599336,"y":27859.17874140309},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volume-Stairs","id":"846e9572-c4d6-4c7e-942e-c82402c5ecea"}}},{"type":"Data Product","name":"Bollinger Bands","id":"49c9d3df-b50e-4218-b585-0785f895ad9c","savedPayload":{"position":{"x":20519.620231200734,"y":27771.766034068183},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"699b13e8-75e0-4730-8dc8-c0797fb894ac","savedPayload":{"position":{"x":20612.31031127703,"y":27699.348606784817},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"b31d164e-1b77-450f-a2c1-67b19b67c482","savedPayload":{"position":{"x":20716.167375042573,"y":27644.12682643601},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"039ef46f-756f-4af9-8aad-0bde8230c53f","savedPayload":{"position":{"x":21654.221746868854,"y":28288.833734325213},"targetPosition":{"x":20979.83276197903,"y":28265.2835520361},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}}],"id":"b1f30d62-1935-4ed0-9f9c-28b5c250a9f4","savedPayload":{"position":{"x":20979.83276197903,"y":28265.2835520361},"targetPosition":{"x":21481.306930973227,"y":28716.812921909375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},{"type":"Single Market Data","name":"Bitmex BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"276e341d-6cad-45fa-8d56-8be162013ca7","savedPayload":{"position":{"x":20603.481947873224,"y":27775.460877154987},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candles","id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"}}},{"type":"Data Product","name":"Volumes","id":"dc7512a4-5c3f-4d91-bf2d-8730c6c5c7fe","savedPayload":{"position":{"x":20670.10570803891,"y":27678.522704589806},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"}}},{"type":"Data Product","name":"Candle-Stairs","id":"84c28b2f-52f3-4d6f-9754-00b3da6e91af","savedPayload":{"position":{"x":20752.550440597213,"y":27594.62633522687},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candle-Stairs","id":"642474e9-da61-4168-97dc-7f7869bebc02"}}},{"type":"Data Product","name":"Volume-Stairs","id":"7bafe96e-4bba-4a79-9cf9-21055feaa862","savedPayload":{"position":{"x":20848.31110406839,"y":27526.32091779566},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volume-Stairs","id":"846e9572-c4d6-4c7e-942e-c82402c5ecea"}}},{"type":"Data Product","name":"Bollinger Bands","id":"b4e6199d-7047-4ee7-87ad-4722464e27cf","savedPayload":{"position":{"x":20954.478059150108,"y":27475.681877840616},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"073c035c-0f1d-4cd8-b3ea-d99ca30edff3","savedPayload":{"position":{"x":21067.825476635277,"y":27444.247856966183},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"6b994fd7-655f-4cac-8893-823eab0dd8ed","savedPayload":{"position":{"x":21184.909352600174,"y":27432.973961990243},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"690cc9af-8154-4b0d-8fd3-38a2be809b9d","savedPayload":{"position":{"x":21804.839934732685,"y":28387.583345413277},"targetPosition":{"x":21190.798019233134,"y":28107.74832252261},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}}],"id":"e2b11c1b-0419-456f-943d-c137a8493779","savedPayload":{"position":{"x":21190.798019233134,"y":28107.74832252261},"targetPosition":{"x":21481.306930973227,"y":28716.812921909375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},{"type":"Single Market Data","name":"HitBTC BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"4ec36066-027d-4eb7-bbe9-52adeb6a76f2","savedPayload":{"position":{"x":21030.56046035702,"y":27511.17726044234},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candles","id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"}}},{"type":"Data Product","name":"Volumes","id":"79b93fca-2746-4df3-9831-c36a6aa855cb","savedPayload":{"position":{"x":21129.209421357482,"y":27447.113876107593},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"}}},{"type":"Data Product","name":"Candle-Stairs","id":"3229c3dd-abe8-40f2-8bff-a982f31ccbc8","savedPayload":{"position":{"x":21237.48417292224,"y":27401.153970837004},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candle-Stairs","id":"642474e9-da61-4168-97dc-7f7869bebc02"}}},{"type":"Data Product","name":"Volume-Stairs","id":"386d17a3-ca20-4a30-a8ed-84fa3397daa2","savedPayload":{"position":{"x":21352.094841514667,"y":27374.694013095377},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volume-Stairs","id":"846e9572-c4d6-4c7e-942e-c82402c5ecea"}}},{"type":"Data Product","name":"Bollinger Bands","id":"c7913df8-85e1-4cd2-a666-1a9ff987dc04","savedPayload":{"position":{"x":21469.55903996538,"y":27368.53797530931},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"38f60687-06b7-4475-b914-ebed74ba29d2","savedPayload":{"position":{"x":21586.307678044206,"y":27382.872905571832},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"fb98f659-fc48-4d9d-8093-6a8e83b9c394","savedPayload":{"position":{"x":21698.79340746078,"y":27417.263244280737},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"75264d4c-647f-47c8-b445-56dc3255c1d8","savedPayload":{"position":{"x":21803.598406247304,"y":27470.664058396706},"targetPosition":{"x":21446.00885767627,"y":28042.926960199136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}}],"id":"ff1435d2-b318-4620-8b31-784b6913d959","savedPayload":{"position":{"x":21446.00885767627,"y":28042.926960199136},"targetPosition":{"x":21481.306930973227,"y":28716.812921909375},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},{"type":"Single Market Data","name":"Binance BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"d5399ddc-ada6-44f1-a5f2-6488d0ac3c0b","savedPayload":{"position":{"x":21526.2273470579,"y":27430.460509334745},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candles","id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"}}},{"type":"Data Product","name":"Volumes","id":"a3af37cc-6af5-4497-a818-ce500c5999ea","savedPayload":{"position":{"x":21641.883098835875,"y":27409.02498275938},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"}}},{"type":"Data Product","name":"Candle-Stairs","id":"cbc5ab4a-c6db-4cf7-bdbd-739ba15a7e5d","savedPayload":{"position":{"x":21759.504019994423,"y":27407.998520531},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Candle-Stairs","id":"642474e9-da61-4168-97dc-7f7869bebc02"}}},{"type":"Data Product","name":"Volume-Stairs","id":"54e00d9b-202d-4cdf-9638-81be6e16719a","savedPayload":{"position":{"x":21875.5162583632,"y":27427.412311185006},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Volume-Stairs","id":"846e9572-c4d6-4c7e-942e-c82402c5ecea"}}},{"type":"Data Product","name":"Bollinger Bands","id":"dc1cae8d-cf3c-4ac2-9419-67d2399fef5b","savedPayload":{"position":{"x":21986.3948407844,"y":27466.67647651623},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"4cbc0c33-2abe-44f6-8862-ac17cee611ae","savedPayload":{"position":{"x":22088.770777638416,"y":27524.597994729702},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"1eaf4681-66c5-4619-9782-6593baf891d6","savedPayload":{"position":{"x":22179.533427888673,"y":27599.416949804217},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"47730810-5628-4d58-922f-0d242f2c9f5d","savedPayload":{"position":{"x":22255.925014335426,"y":27688.860005650044},"targetPosition":{"x":21706.559817893733,"y":28080.71839201578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}}],"id":"151e6826-766d-430a-99b6-7bda6049c2d1","savedPayload":{"position":{"x":21706.559817893733,"y":28080.71839201578},"targetPosition":{"x":21481.306930973227,"y":28716.812921909375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}}],"id":"bf04ccea-266d-4115-b50e-fefdf29b873a","savedPayload":{"position":{"x":21481.306930973227,"y":28716.812921909375},"targetPosition":{"x":21816.07394323707,"y":29017.820119040538},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"sessionBasedData":{"type":"Session Based Data","name":"New Session Based Data","sessionReference":[{"type":"Session Reference","name":"Back WHB","singleMarketData":[{"type":"Single Market Data","name":"Poloniex BTC/USDT","dataProducts":[{"type":"Data Product","name":"Simulation","id":"d34404c3-392b-4af3-a7ac-d6381ac1b5b1","savedPayload":{"position":{"x":21385.2039738597,"y":27345.208934620907},"targetPosition":{"x":21630.218965382945,"y":27722.498923768115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Simulation","id":"a3201051-46ba-4a92-aef3-a6c787b771fd"}}},{"type":"Data Product","name":"Conditions","id":"5ef03988-16a2-43b6-a2c9-7783782534d9","savedPayload":{"position":{"x":21436.546333167636,"y":27316.455831863917},"targetPosition":{"x":21630.218965382945,"y":27722.498923768115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Conditions","id":"7fb3e69f-4343-47f8-ab7b-6fcf8a805fc0"}}},{"type":"Data Product","name":"Strategies","id":"30c98460-b2bf-4ab9-bfb7-d131e7373967","savedPayload":{"position":{"x":21491.20248449168,"y":27294.650238547005},"targetPosition":{"x":21630.218965382945,"y":27722.498923768115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Strategies","id":"36051f0f-5161-48a6-9d13-eb1766b5566b"}}},{"type":"Data Product","name":"Trades","id":"4cae29ae-970e-481f-9436-9a9584f5f2b5","savedPayload":{"position":{"x":21548.23724592913,"y":27280.165254417476},"targetPosition":{"x":21630.218965382945,"y":27722.498923768115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Trades","id":"a26dc922-ca08-43ef-983e-029b76f39163"}}},{"type":"Data Product","name":"Execution","id":"0d066d54-5a5d-4af5-99e1-8a5fd05b2730","savedPayload":{"position":{"x":21606.674736898764,"y":27273.248721569784},"targetPosition":{"x":21630.218965382945,"y":27722.498923768115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Execution","id":"05ce9ca7-8b5b-4974-a45e-ca319fc85330"}}}],"id":"d6937865-7848-4270-b1a1-ab7035e8378c","savedPayload":{"position":{"x":21630.218965382945,"y":27722.498923768115},"targetPosition":{"x":21769.23543860489,"y":28150.34761077814},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}}],"id":"22d1722d-a9f5-4b43-8732-c88fd45a3cb8","savedPayload":{"position":{"x":21769.23546093308,"y":28150.347633767906},"targetPosition":{"x":21908.896378604575,"y":28577.889001719563},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Backtesting Session","name":"Back WHB","id":"e5a4324d-479f-45ae-b882-54939c919a63"}}},{"type":"Session Reference","name":"Live Trading WHB","singleMarketData":[{"type":"Single Market Data","name":"Poloniex BTC/USDT","dataProducts":[{"type":"Data Product","name":"Simulation","id":"fda48255-a101-43e4-8932-d720dea69871","savedPayload":{"position":{"x":21722.51073984509,"y":27251.62548670104},"targetPosition":{"x":21861.527217139854,"y":27679.474174942796},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Simulation","id":"a3201051-46ba-4a92-aef3-a6c787b771fd"}}},{"type":"Data Product","name":"Conditions","id":"56e374e5-2b2d-47a2-a665-4b2b3091fa95","savedPayload":{"position":{"x":21779.545501282537,"y":27237.14050257151},"targetPosition":{"x":21861.527217139854,"y":27679.474174942796},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Conditions","id":"7fb3e69f-4343-47f8-ab7b-6fcf8a805fc0"}}},{"type":"Data Product","name":"Strategies","id":"232d5b14-f5aa-4723-804d-199afd9792e3","savedPayload":{"position":{"x":21837.98299225217,"y":27230.22396972382},"targetPosition":{"x":21861.527217139854,"y":27679.474174942796},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Strategies","id":"36051f0f-5161-48a6-9d13-eb1766b5566b"}}},{"type":"Data Product","name":"Trades","id":"471a887b-fb87-4978-86f6-075b95356847","savedPayload":{"position":{"x":21896.82333108157,"y":27230.99423195261},"targetPosition":{"x":21861.527217139854,"y":27679.474174942796},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Trades","id":"a26dc922-ca08-43ef-983e-029b76f39163"}}},{"type":"Data Product","name":"Execution","id":"be409a2d-83ca-4c12-b72f-9ce1622efa65","savedPayload":{"position":{"x":21955.05974325973,"y":27239.438109857594},"targetPosition":{"x":21861.527217139854,"y":27679.474174942796},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Execution","id":"05ce9ca7-8b5b-4974-a45e-ca319fc85330"}}}],"id":"2d6a3009-cb74-4aeb-9d37-5baa9838d516","savedPayload":{"position":{"x":21861.527217139854,"y":27679.474174942796},"targetPosition":{"x":21885.07143817832,"y":28128.724379582058},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}}],"id":"44aca2ca-b3dc-4a2a-80ae-de36a4924d10","savedPayload":{"position":{"x":21885.071457080834,"y":28128.72440540892},"targetPosition":{"x":21908.896378604575,"y":28577.889001719563},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Live Trading Session","name":"Live Trading WHB","id":"1be42eb3-bfaf-432f-8da6-15f15d547f50"}}},{"type":"Session Reference","name":"Live Trading WHB","singleMarketData":[{"type":"Single Market Data","name":"Binance BTC/USDT","dataProducts":[{"type":"Data Product","name":"Simulation","id":"2aa7aa29-0891-4ca7-a82f-4c92577fbf4f","savedPayload":{"position":{"x":22072.041150095734,"y":27248.623149439692},"targetPosition":{"x":22095.585372531157,"y":27697.87335610188},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Simulation","id":"a3201051-46ba-4a92-aef3-a6c787b771fd"}}},{"type":"Data Product","name":"Conditions","id":"895ecc79-e2a2-48c8-ad9a-d88db161cca9","savedPayload":{"position":{"x":22130.881488925133,"y":27249.393411668483},"targetPosition":{"x":22095.585372531157,"y":27697.87335610188},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Conditions","id":"7fb3e69f-4343-47f8-ab7b-6fcf8a805fc0"}}},{"type":"Data Product","name":"Strategies","id":"cae5b2d1-7cdd-4544-b0b0-d640f64db909","savedPayload":{"position":{"x":22189.117901103295,"y":27257.83728957347},"targetPosition":{"x":22095.585372531157,"y":27697.87335610188},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Strategies","id":"36051f0f-5161-48a6-9d13-eb1766b5566b"}}},{"type":"Data Product","name":"Trades","id":"44fba0b1-916d-4504-a61f-276f58926166","savedPayload":{"position":{"x":22245.753945471675,"y":27273.810306062605},"targetPosition":{"x":22095.585372531157,"y":27697.87335610188},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Trades","id":"a26dc922-ca08-43ef-983e-029b76f39163"}}},{"type":"Data Product","name":"Execution","id":"99e9dd0d-709c-4b54-b62a-4077bcd85091","savedPayload":{"position":{"x":22299.82056360865,"y":27297.03915839521},"targetPosition":{"x":22095.585372531157,"y":27697.87335610188},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Execution","id":"05ce9ca7-8b5b-4974-a45e-ca319fc85330"}}}],"id":"f12b6d50-17e7-45df-a091-6451410207ff","savedPayload":{"position":{"x":22095.585372531157,"y":27697.87335610188},"targetPosition":{"x":22002.052839279306,"y":28137.90942168748},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}}],"id":"9e479e37-1dd3-4266-a72e-2afca5dfd523","savedPayload":{"position":{"x":22002.05285601805,"y":28137.909448832328},"targetPosition":{"x":21908.896378604575,"y":28577.889001719563},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Live Trading Session","name":"Live Trading WHB","id":"2327ab2a-13d0-4834-a979-a017bcff5829"}}},{"type":"Session Reference","name":"Live Trading WHB","singleMarketData":[{"type":"Single Market Data","name":"Bitfinex BTC/USDT","dataProducts":[{"type":"Data Product","name":"Simulation","id":"4d7d0e2c-e474-42b8-881b-e2df11706587","savedPayload":{"position":{"x":22410.583785063547,"y":27336.29861429271},"targetPosition":{"x":22317.05125269141,"y":27776.334680147927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Simulation","id":"a3201051-46ba-4a92-aef3-a6c787b771fd"}}},{"type":"Data Product","name":"Conditions","id":"8c774977-c5b7-40ad-9953-9355a51cbb84","savedPayload":{"position":{"x":22467.219829431928,"y":27352.27163078185},"targetPosition":{"x":22317.05125269141,"y":27776.334680147927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Conditions","id":"7fb3e69f-4343-47f8-ab7b-6fcf8a805fc0"}}},{"type":"Data Product","name":"Strategies","id":"b3280b17-225c-4bc0-8435-d072c51649da","savedPayload":{"position":{"x":22521.286447568902,"y":27375.500483114454},"targetPosition":{"x":22317.05125269141,"y":27776.334680147927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Strategies","id":"36051f0f-5161-48a6-9d13-eb1766b5566b"}}},{"type":"Data Product","name":"Trades","id":"a464e80f-f0eb-4a2a-af9c-a3e127a927db","savedPayload":{"position":{"x":22571.858544648047,"y":27405.58771918686},"targetPosition":{"x":22317.05125269141,"y":27776.334680147927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Trades","id":"a26dc922-ca08-43ef-983e-029b76f39163"}}},{"type":"Data Product","name":"Execution","id":"c432f129-7eb3-4edf-be4b-d822cc8898aa","savedPayload":{"position":{"x":22618.070818067106,"y":27442.018538048113},"targetPosition":{"x":22317.05125269141,"y":27776.334680147927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Execution","id":"05ce9ca7-8b5b-4974-a45e-ca319fc85330"}}}],"id":"5345e341-776f-4f7c-9983-96b33cfbc2c3","savedPayload":{"position":{"x":22317.05125269141,"y":27776.334680147927},"targetPosition":{"x":22112.816053932173,"y":28177.168876849104},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}}],"id":"2d979116-50c5-4381-aa1c-5fc91fe49392","savedPayload":{"position":{"x":22112.816067212756,"y":28177.168903359056},"targetPosition":{"x":21908.896378604575,"y":28577.889001719563},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Live Trading Session","name":"Live Trading WHB","id":"b8813ec9-6e81-4ee3-87a3-7e44e5c79fb3"}}},{"type":"Session Reference","name":"Live Trading WHB","singleMarketData":[{"type":"Single Market Data","name":"Bitmex BTC/USDT","dataProducts":[{"type":"Data Product","name":"Simulation","id":"c53da960-e2c9-49ca-97a8-896c3071428f","savedPayload":{"position":{"x":22715.03235684247,"y":27508.880641741544},"targetPosition":{"x":22510.797157751684,"y":27909.714836002695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Simulation","id":"a3201051-46ba-4a92-aef3-a6c787b771fd"}}},{"type":"Data Product","name":"Conditions","id":"2e907269-aa81-4f17-97ac-01ef8bbc02d4","savedPayload":{"position":{"x":22765.604453921613,"y":27538.96787781395},"targetPosition":{"x":22510.797157751684,"y":27909.714836002695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Conditions","id":"7fb3e69f-4343-47f8-ab7b-6fcf8a805fc0"}}},{"type":"Data Product","name":"Strategies","id":"debb9e02-384e-4eaa-9c48-be0831cf5a4d","savedPayload":{"position":{"x":22811.816727340673,"y":27575.3986966752},"targetPosition":{"x":22510.797157751684,"y":27909.714836002695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Strategies","id":"36051f0f-5161-48a6-9d13-eb1766b5566b"}}},{"type":"Data Product","name":"Trades","id":"80c06cfa-2746-4d56-807b-3a1e55305d44","savedPayload":{"position":{"x":22852.878472288983,"y":27617.549756914053},"targetPosition":{"x":22510.797157751684,"y":27909.714836002695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Trades","id":"a26dc922-ca08-43ef-983e-029b76f39163"}}},{"type":"Data Product","name":"Execution","id":"b73ee177-28e0-4b32-a2a3-cb053a9d6f19","savedPayload":{"position":{"x":22888.08711092601,"y":27664.699842203336},"targetPosition":{"x":22510.797157751684,"y":27909.714836002695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Product Definition","name":"Execution","id":"05ce9ca7-8b5b-4974-a45e-ca319fc85330"}}}],"id":"39b706e7-776f-4314-a813-dd781901a519","savedPayload":{"position":{"x":22510.797157751684,"y":27909.714836002695},"targetPosition":{"x":22209.777586263754,"y":28244.030978579573},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}}],"id":"729bdc74-5003-4113-9be9-2098fc5a1f02","savedPayload":{"position":{"x":22209.777595653566,"y":28244.03100241868},"targetPosition":{"x":21908.896378604575,"y":28577.889001719563},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Live Trading Session","name":"Live Trading WHB","id":"a15bdeb7-f697-448c-8895-8e95c1383f1e"}}}],"id":"29ca14e5-dabe-470a-be64-ed7996bd1e55","savedPayload":{"position":{"x":21908.896378604575,"y":28577.889001719563},"targetPosition":{"x":21816.07394323707,"y":29017.820119040538},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"id":"0726a9e4-23b6-4d27-a8de-e91a9665a8ba","savedPayload":{"position":{"x":21816.07394323707,"y":29017.820119040538},"targetPosition":{"x":21955.090399739755,"y":29445.668778615294},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":"d"}}},"dataMining":{"type":"Data Mining","name":"New Data Mining","taskManagers":[{"type":"Task Manager","name":"Masters Kraken BTC/USDT","tasks":[{"type":"Task","name":"Trades","bot":{"type":"Sensor Bot Instance","name":"Charly","code":" {\n \"repo\": \"AACharly-Sensor-Bot\",\n \"startDate\": \"2020-01-01\"\n }","processes":[{"type":"Sensor Process Instance","name":"Historic-Trades","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"a2614a09-8997-4bb2-a6bc-031ce1fafa8c","savedPayload":{"position":{"x":22800.903426803525,"y":25981.414722396858},"targetPosition":{"x":22776.31870724561,"y":26317.91787256176},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"87bbd0f1-98ae-4231-8d39-2170cde6e44b","savedPayload":{"position":{"x":22776.31870724561,"y":26317.91787256176},"targetPosition":{"x":22751.733987687694,"y":26654.421022726663},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Historic-Trades","id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629"}}}],"id":"6fbca8a6-be5d-48aa-90b5-bffab7cf43e9","savedPayload":{"position":{"x":22751.733987687694,"y":26654.421022726663},"targetPosition":{"x":22727.14926812978,"y":26990.924172891566},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"0eda6317-7134-4800-9279-6e49b9ee9d50","savedPayload":{"position":{"x":22727.14926812978,"y":26990.924172891566},"targetPosition":{"x":22628.810389898117,"y":28336.936773551177},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Trades into 1 min Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Bruce","code":" {\n \"repo\": \"AABruce-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Single-Period-Daily","code":" {\n \"process\": \"Single-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"c3bfda5b-df1e-4271-918b-ba3f2f2040b9","savedPayload":{"position":{"x":23063.55613431591,"y":26015.494057364496},"targetPosition":{"x":23001.449599399082,"y":26347.12873110545},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"4d7da0cc-1d36-4291-a0be-459f224a0b2f","savedPayload":{"position":{"x":23001.449599399082,"y":26347.12873110545},"targetPosition":{"x":22939.343064482255,"y":26678.763404846406},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f"}}}],"id":"d6fc3b6e-a9f2-41f3-b96c-ab1b2aac2886","savedPayload":{"position":{"x":22939.343064482255,"y":26678.763404846406},"targetPosition":{"x":22877.236529565427,"y":27010.39807858736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{"shortcutKey":"p"}}},"id":"916f11ac-3279-4566-8766-6dc79027ff5d","savedPayload":{"position":{"x":22877.236529565427,"y":27010.39807858736},"targetPosition":{"x":22628.810389898117,"y":28336.936773551177},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":" {\n \"repo\": \"AAOlivia-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"131bd5a8-057c-4f1a-8348-d6bbc65aaa69","savedPayload":{"position":{"x":23234.835980316293,"y":26058.482253495826},"targetPosition":{"x":23178.94149651748,"y":26391.220266334018},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"f332d00b-afa5-42e0-b98b-2a960300aeb1","savedPayload":{"position":{"x":23178.94149651748,"y":26391.220266334018},"targetPosition":{"x":23123.047012718667,"y":26723.95827917221},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"9d6303fe-0283-446e-8192-529777805a0d","savedPayload":{"position":{"x":23403.264733119588,"y":26110.090913041797},"targetPosition":{"x":23263.155872919127,"y":26417.024596107003},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"dbcb7c34-ff82-4404-8119-8875aed784eb","savedPayload":{"position":{"x":23263.155872919127,"y":26417.024596107003},"targetPosition":{"x":23123.047012718667,"y":26723.95827917221},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577"}}}],"id":"770041d3-aae1-4e07-9629-3d8b024b16bc","savedPayload":{"position":{"x":23123.047012718667,"y":26723.95827917221},"targetPosition":{"x":23024.199688154556,"y":27046.553978048003},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"a510c76a-5155-4ca6-8920-ebf4ce0f8a50","savedPayload":{"position":{"x":23024.199688154556,"y":27046.553978048003},"targetPosition":{"x":22628.810389898117,"y":28336.936773551177},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Candle & Volume Stairs Patters","bot":{"type":"Indicator Bot Instance","name":"Tom","code":" {\n \"repo\": \"AATom-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"8f110ecb-9d28-4239-af25-b7703b8b67df","savedPayload":{"position":{"x":23486.13138526762,"y":26140.66203502209},"targetPosition":{"x":23393.33351696398,"y":26465.04966447942},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"4dc482e1-b1fb-4b50-98ad-34011aa37683","savedPayload":{"position":{"x":23393.33351696398,"y":26465.04966447942},"targetPosition":{"x":23300.535648660338,"y":26789.437293936746},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"80c87e44-7b3a-449d-9bf4-d14502bfa837"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"befb2712-8d55-4df9-8ca6-4de07a649b93","savedPayload":{"position":{"x":23647.722756896892,"y":26210.8042272175},"targetPosition":{"x":23474.129202778615,"y":26500.120760577123},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"9d3e2161-434e-4495-9ce5-6341e68fc45c","savedPayload":{"position":{"x":23474.129202778615,"y":26500.120760577123},"targetPosition":{"x":23300.535648660338,"y":26789.437293936746},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"2924712f-7d80-4f36-8822-71a12cb485f6"}}}],"id":"7c8a19d8-dd94-43d8-9b16-f4e5d5aea7ab","savedPayload":{"position":{"x":23300.535648660338,"y":26789.437293936746},"targetPosition":{"x":23166.190596907894,"y":27098.937189859633},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"5d9c329a-724f-40a1-8ce1-0a1eebcbecd9","savedPayload":{"position":{"x":23166.190596907894,"y":27098.937189859633},"targetPosition":{"x":22628.810389898117,"y":28336.936773551177},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Bollinger Bands & Percentage Bandwidth","bot":{"type":"Indicator Bot Instance","name":"Chris","code":" {\n \"repo\": \"AAChris-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"983c5887-e706-45a5-916a-99e47474d98d","savedPayload":{"position":{"x":23726.64548127296,"y":26250.46124569365},"targetPosition":{"x":23598.111215500983,"y":26562.4191291156},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"a8a1c4e1-2d3b-4333-bd4e-a3ed4b894ef1","savedPayload":{"position":{"x":23598.111215500983,"y":26562.4191291156},"targetPosition":{"x":23469.576949729006,"y":26874.37701253755},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"36b56b85-0e5f-40e9-b491-e79769a04814","savedPayload":{"position":{"x":23879.367366472467,"y":26338.25489177419},"targetPosition":{"x":23674.472158100736,"y":26606.31595215587},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"21bb0d5a-9d13-411c-b3ce-1c6d0aa258ab","savedPayload":{"position":{"x":23674.472158100736,"y":26606.31595215587},"targetPosition":{"x":23469.576949729006,"y":26874.37701253755},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}}],"id":"0a53ee0e-a7aa-446e-b791-8e2a39e625a6","savedPayload":{"position":{"x":23469.576949729006,"y":26874.37701253755},"targetPosition":{"x":23301.42363776283,"y":27166.888964740276},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"8ad437c8-4a0b-4d63-8043-eabb2067b1ea","savedPayload":{"position":{"x":23301.42363776283,"y":27166.888964740276},"targetPosition":{"x":22628.810389898117,"y":28336.936773551177},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":" {\n \"repo\": \"AAPaula-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"44a5a74e-0912-4635-9f53-a9bcb3a50743","savedPayload":{"position":{"x":23953.353664025504,"y":26386.49909672932},"targetPosition":{"x":23790.699393754592,"y":26682.10418272968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"8b1ac3e4-17c8-4b4b-81e2-7774a24a4780","savedPayload":{"position":{"x":23790.699393754592,"y":26682.10418272968},"targetPosition":{"x":23628.04512348368,"y":26977.70926873004},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Kraken BTC/USDT","id":"6dbef496-68a4-4b7a-b075-db4742d167a6","savedPayload":{"position":{"x":24095.28549647756,"y":26490.840140656474},"targetPosition":{"x":23861.66530998062,"y":26734.274704693256},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e"}}},"id":"2119c986-d0f2-46b0-b312-fe2a836a8ee5","savedPayload":{"position":{"x":23861.66530998062,"y":26734.274704693256},"targetPosition":{"x":23628.04512348368,"y":26977.70926873004},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}}}],"id":"fcf01486-f963-48be-b58d-26a5cfc0227c","savedPayload":{"position":{"x":23628.04512348368,"y":26977.70926873004},"targetPosition":{"x":23428.198176766567,"y":27249.554769694267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{"shortcutKey":""}}},"id":"73bc5e1a-fbb7-4955-b220-00e645b26ad7","savedPayload":{"position":{"x":23428.198176766567,"y":27249.554769694267},"targetPosition":{"x":22628.810389898117,"y":28336.936773551177},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{"shortcutKey":""}}}],"id":"778736fa-88e3-4818-9b6e-ba44bc05436d","savedPayload":{"position":{"x":22628.810389898117,"y":28336.936773551177},"targetPosition":{"x":22318.66936473428,"y":29181.45602262725},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":5},"uiObject":{}}},{"type":"Task Manager","name":"Masters Bitfinex BTC/USDT","tasks":[{"type":"Task","name":"Trades","bot":{"type":"Sensor Bot Instance","name":"Charly","code":" {\n \"repo\": \"AACharly-Sensor-Bot\",\n \"startDate\": \"2019-05-01\"\n }","processes":[{"type":"Sensor Process Instance","name":"Historic-Trades","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"33ee1d19-8649-4bb9-9697-88a9cc262520","savedPayload":{"position":{"x":22775.631241130057,"y":27289.52464182512},"targetPosition":{"x":22778.15493448243,"y":27514.44383540691},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"b908471e-fd73-4f0f-a0a2-53565e085e29","savedPayload":{"position":{"x":22778.15493448243,"y":27514.44383540691},"targetPosition":{"x":22780.678627834805,"y":27739.3630289887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Historic-Trades","id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629"}}}],"id":"d7c3f337-1013-4f42-ba48-e77d0d1b63d4","savedPayload":{"position":{"x":22780.678627834805,"y":27739.3630289887},"targetPosition":{"x":22783.20242768002,"y":27964.282251909455},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"c7c668e8-b306-4d55-b4b1-5464c23ec7e6","savedPayload":{"position":{"x":22783.20242768002,"y":27964.282251909455},"targetPosition":{"x":22788.249922938026,"y":28414.120669713295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Trades into 1 min Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Bruce","code":" {\n \"repo\": \"AABruce-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Single-Period-Daily","code":" {\n \"process\": \"Single-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"80ded1d9-234d-4b4f-bb16-88b8609e7afb","savedPayload":{"position":{"x":23026.193757539437,"y":27314.912737683553},"targetPosition":{"x":22978.604947609936,"y":27534.754312093657},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"e4f59f0a-89df-493b-b43f-54e13942ca64","savedPayload":{"position":{"x":22978.604947609936,"y":27534.754312093657},"targetPosition":{"x":22931.016137680435,"y":27754.59588650376},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f"}}}],"id":"24ca81e8-440d-4c89-a695-ba01163488dd","savedPayload":{"position":{"x":22931.016137680435,"y":27754.59588650376},"targetPosition":{"x":22883.427434243775,"y":27974.43749025283},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":""}}},"id":"e204f943-b44b-47bc-9432-0ace8385328c","savedPayload":{"position":{"x":22883.427434243775,"y":27974.43749025283},"targetPosition":{"x":22788.249922938026,"y":28414.120669713295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":" {\n \"repo\": \"AAOlivia-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"3afbec49-1438-4783-bd64-c5d8fe69970b","savedPayload":{"position":{"x":23210.00703947448,"y":27374.02354607575},"targetPosition":{"x":23142.10089103513,"y":27588.461806826374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"f4d577d1-9ded-4e55-aa15-658af8a9528e","savedPayload":{"position":{"x":23142.10089103513,"y":27588.461806826374},"targetPosition":{"x":23074.19474259578,"y":27802.900067576997},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"b14fa9e6-b00e-4b29-9608-2ade46f0c69a","savedPayload":{"position":{"x":23316.380759440402,"y":27423.78796816125},"targetPosition":{"x":23195.28775101809,"y":27613.344017869123},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"8bfbce7e-a25d-4ba8-a280-a6ee3f0ddc1f","savedPayload":{"position":{"x":23195.28775101809,"y":27613.344017869123},"targetPosition":{"x":23074.19474259578,"y":27802.900067576997},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577"}}}],"id":"d023822c-481d-40ed-8dfe-a2bcec6677d4","savedPayload":{"position":{"x":23074.19474259578,"y":27802.900067576997},"targetPosition":{"x":22978.87983752067,"y":28006.640277634986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"9b3a4292-670d-4bdb-80d5-4170ee30222a","savedPayload":{"position":{"x":22978.87983752067,"y":28006.640277634986},"targetPosition":{"x":22788.249922938026,"y":28414.120669713295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Candle & Volume Stairs Patters","bot":{"type":"Indicator Bot Instance","name":"Tom","code":" {\n \"repo\": \"AATom-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"0b864dd7-da65-4a10-8b5a-554a6cee3f0a","savedPayload":{"position":{"x":23430.876072629555,"y":27493.95078632189},"targetPosition":{"x":23316.955471049074,"y":27687.902092595297},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"0ba1eeed-193a-4898-9dbf-a5d5611fe2d6","savedPayload":{"position":{"x":23316.955471049074,"y":27687.902092595297},"targetPosition":{"x":23203.034869468593,"y":27881.853398868705},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"80c87e44-7b3a-449d-9bf4-d14502bfa837"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"047b5249-3c0c-4f5f-b67b-b796e1507941","savedPayload":{"position":{"x":23523.509155666678,"y":27566.137889961865},"targetPosition":{"x":23363.272012567635,"y":27723.995644415285},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"1d61294c-bed2-4f9d-8aef-4ab802b405c7","savedPayload":{"position":{"x":23363.272012567635,"y":27723.995644415285},"targetPosition":{"x":23203.034869468593,"y":27881.853398868705},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"2924712f-7d80-4f36-8822-71a12cb485f6"}}}],"id":"fe81bef7-aa6f-4386-b3dc-f8f0616bdb8f","savedPayload":{"position":{"x":23203.034869468593,"y":27881.853398868705},"targetPosition":{"x":23064.773255435877,"y":28059.27583182946},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"827234f9-2f2f-4565-a3ef-872c181e58cd","savedPayload":{"position":{"x":23064.773255435877,"y":28059.27583182946},"targetPosition":{"x":22788.249922938026,"y":28414.120669713295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Bollinger Bands & Percentage Bandwidth","bot":{"type":"Indicator Bot Instance","name":"Chris","code":" {\n \"repo\": \"AAChris-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"b48a635d-764a-4eae-ae74-8bbf76946fd3","savedPayload":{"position":{"x":23619.521136482676,"y":27660.019183808483},"targetPosition":{"x":23465.298536415314,"y":27823.758007238},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"ae635552-5ee7-4b50-a81b-fe6df16e0f5a","savedPayload":{"position":{"x":23465.298536415314,"y":27823.758007238},"targetPosition":{"x":23311.075936347952,"y":27987.49683066752},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"8b600f35-2606-4ffc-a8a2-889b25a58598","savedPayload":{"position":{"x":23693.768573005458,"y":27751.00920619933},"targetPosition":{"x":23502.422254676705,"y":27869.253018433425},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"da565660-aca8-4b16-99d2-38744ffecb66","savedPayload":{"position":{"x":23502.422254676705,"y":27869.253018433425},"targetPosition":{"x":23311.075936347952,"y":27987.49683066752},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}}],"id":"b1804417-a45a-4db4-bef0-511ef6f2d4f2","savedPayload":{"position":{"x":23311.075936347952,"y":27987.49683066752},"targetPosition":{"x":23136.80063335545,"y":28129.704786362},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"6d55df89-61a7-4deb-9b93-bc76b8001f79","savedPayload":{"position":{"x":23136.80063335545,"y":28129.704786362},"targetPosition":{"x":22788.249922938026,"y":28414.120669713295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":" {\n \"repo\": \"AAPaula-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"89988045-44ef-4bbf-ab61-88d141b9e417","savedPayload":{"position":{"x":23766.48277981906,"y":27863.901375644313},"targetPosition":{"x":23579.69154640969,"y":27989.2171679094},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"2093e487-3f20-4724-b378-315668e89f43","savedPayload":{"position":{"x":23579.69154640969,"y":27989.2171679094},"targetPosition":{"x":23392.90031300032,"y":28114.532960174485},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","id":"9acb52e1-76ed-4c8d-8dc6-d3311cddf3f6","savedPayload":{"position":{"x":23818.62149332995,"y":27969.131697122113},"targetPosition":{"x":23605.760903165134,"y":28041.8323286483},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"ca19467c-6123-498a-82c8-f1984197ac0e","savedPayload":{"position":{"x":23605.760903165134,"y":28041.8323286483},"targetPosition":{"x":23392.90031300032,"y":28114.532960174485},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}}}],"id":"dee2f3e4-d9c0-44a6-b270-85a5e227453d","savedPayload":{"position":{"x":23392.90031300032,"y":28114.532960174485},"targetPosition":{"x":23191.35021779036,"y":28214.395539366644},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":""}}},"id":"b3a25d72-af74-4729-b279-f2af6b285882","savedPayload":{"position":{"x":23191.35021779036,"y":28214.395539366644},"targetPosition":{"x":22788.249922938026,"y":28414.120669713295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{"shortcutKey":""}}}],"id":"d10b8ef0-3e4c-4475-b85b-a8bd918f363c","savedPayload":{"position":{"x":22788.250033420394,"y":28414.120701833377},"targetPosition":{"x":22318.66936473428,"y":29181.45602262725},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":5},"uiObject":{}}},{"type":"Task Manager","name":"Masters Bitmex BTC/USD","tasks":[{"type":"Task","name":"Trades","bot":{"type":"Sensor Bot Instance","name":"Charly","code":" {\n \"repo\": \"AACharly-Sensor-Bot\",\n \"startDate\": \"2020-02-07\"\n }","processes":[{"type":"Sensor Process Instance","name":"Historic-Trades","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"6a45facc-fbc9-4037-8c49-771db1abb780","savedPayload":{"position":{"x":23731.82198207845,"y":26910.65256510148},"targetPosition":{"x":23631.245777405617,"y":27111.983850514185},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"2d577dde-853b-445a-a401-cce2d6f4535a","savedPayload":{"position":{"x":23631.245777405617,"y":27111.983850514185},"targetPosition":{"x":23530.931787046207,"y":27313.258810301017},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Historic-Trades","id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629"}}}],"id":"0a2ce755-cbbe-4daa-84d9-67950b2bf240","savedPayload":{"position":{"x":23530.931787046207,"y":27313.258810301017},"targetPosition":{"x":23329.805022411787,"y":27715.41230779333},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"34bdbc75-21fd-4706-93f9-208cd0c5a599","savedPayload":{"position":{"x":23329.805022411787,"y":27715.41230779333},"targetPosition":{"x":22929.180739706946,"y":28520.824676588596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Trades into 1 min Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Bruce","code":" {\n \"repo\": \"AABruce-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Single-Period-Daily","code":" {\n \"process\": \"Single-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"e71e6fab-19f0-45d6-ab8d-ecdec2737f49","savedPayload":{"position":{"x":23909.4350348902,"y":27012.07060839693},"targetPosition":{"x":23786.870331972346,"y":27201.022526252917},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"6db22c79-4e10-47c6-b0b3-43ba21c2295b","savedPayload":{"position":{"x":23786.870331972346,"y":27201.022526252917},"targetPosition":{"x":23664.57725393792,"y":27389.758025858533},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f"}}}],"id":"5d57ef3b-e849-4c52-b9d9-8304058ebeb4","savedPayload":{"position":{"x":23664.57725393792,"y":27389.758025858533},"targetPosition":{"x":23419.428964730327,"y":27766.602198214576},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":""}}},"id":"285ac11a-06c8-4142-a254-4a9c97bffba5","savedPayload":{"position":{"x":23419.428964730327,"y":27766.602198214576},"targetPosition":{"x":22929.180739706946,"y":28520.824676588596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":" {\n \"repo\": \"AAOlivia-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"5df5e85f-13cf-4cc2-b09d-3831f9f2458e","savedPayload":{"position":{"x":24024.462103427082,"y":27097.053797694756},"targetPosition":{"x":23905.265589033123,"y":27288.332072116522},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"91800c6e-c00e-4aa1-8371-56669c72e16d","savedPayload":{"position":{"x":23905.265589033123,"y":27288.332072116522},"targetPosition":{"x":23787.401085371,"y":27480.072626617017},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"7b3403c1-fa49-4af2-ae87-22f69937453c","savedPayload":{"position":{"x":24117.02027952998,"y":27173.290197109473},"targetPosition":{"x":23952.600423353935,"y":27327.39902071548},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"615685de-ff60-4d77-a6e7-93c845381de6","savedPayload":{"position":{"x":23952.600423353935,"y":27327.39902071548},"targetPosition":{"x":23787.401085371,"y":27480.072626617017},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577"}}}],"id":"beaea3e3-7b5c-484e-bd2f-594cb2f4e774","savedPayload":{"position":{"x":23787.401085371,"y":27480.072626617017},"targetPosition":{"x":23501.474457870747,"y":27826.840991601333},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"d9d74ad6-0368-4485-be1e-8775b50daa2a","savedPayload":{"position":{"x":23501.474457870747,"y":27826.840991601333},"targetPosition":{"x":22929.180739706946,"y":28520.824676588596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Candle & Volume Stairs Patters","bot":{"type":"Indicator Bot Instance","name":"Tom","code":" {\n \"repo\": \"AATom-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"607dd735-b1f9-4e88-97d0-bc925e1c0584","savedPayload":{"position":{"x":24178.803726288017,"y":27230.35922343958},"targetPosition":{"x":24038.23507800618,"y":27406.56259189089},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"6249a65a-93d2-4248-9aa5-c5531f488e45","savedPayload":{"position":{"x":24038.23507800618,"y":27406.56259189089},"targetPosition":{"x":23899.257574236464,"y":27583.464814534756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"80c87e44-7b3a-449d-9bf4-d14502bfa837"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"1263fc88-06f7-43c5-aea7-39419f964067","savedPayload":{"position":{"x":24261.658749585706,"y":27316.04389144403},"targetPosition":{"x":24080.774902066627,"y":27450.56880887864},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"001629fb-7dde-4a01-9b76-0ec8a75732c0","savedPayload":{"position":{"x":24080.774902066627,"y":27450.56880887864},"targetPosition":{"x":23899.257574236464,"y":27583.464814534756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"2924712f-7d80-4f36-8822-71a12cb485f6"}}}],"id":"ef324f03-f768-4779-bb31-da98d1c963d4","savedPayload":{"position":{"x":23899.257574236464,"y":27583.464814534756},"targetPosition":{"x":23576.065714479224,"y":27895.803484469772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"26df3dec-d9ef-4197-86a0-24c33bbc8de0","savedPayload":{"position":{"x":23576.065714479224,"y":27895.803484469772},"targetPosition":{"x":22929.180739706946,"y":28520.824676588596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Bollinger Bands & Percentage Bandwidth","bot":{"type":"Indicator Bot Instance","name":"Chris","code":" {\n \"repo\": \"AAChris-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"2cbcf91d-6788-48aa-b1d7-baa5f0f9081b","savedPayload":{"position":{"x":24316.685049643755,"y":27379.968848648896},"targetPosition":{"x":24157.10355038787,"y":27539.12882271692},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"a60877e7-2f6f-4035-826b-f34aa21da6ef","savedPayload":{"position":{"x":24157.10355038787,"y":27539.12882271692},"targetPosition":{"x":23998.99157252279,"y":27699.08714212975},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"ce426fbe-ba1c-416d-901c-fb84f09e3755","savedPayload":{"position":{"x":24389.420121240255,"y":27474.503465984424},"targetPosition":{"x":24194.459166712644,"y":27587.649234761968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"f15d2d13-45f9-4e46-9708-bd336467743d","savedPayload":{"position":{"x":24194.459166712644,"y":27587.649234761968},"targetPosition":{"x":23998.99157252279,"y":27699.08714212975},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}}],"id":"16e7ff96-5609-46e4-a037-8bb023564529","savedPayload":{"position":{"x":23998.99157252279,"y":27699.08714212975},"targetPosition":{"x":23642.559741854326,"y":27972.891497955225},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"9275654d-10a9-42ef-9f16-74bed4357d6e","savedPayload":{"position":{"x":23642.559741854326,"y":27972.891497955225},"targetPosition":{"x":22929.180739706946,"y":28520.824676588596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":" {\n \"repo\": \"AAPaula-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"6052e52e-d517-4012-a562-b63a2024a91a","savedPayload":{"position":{"x":24437.729865602836,"y":27545.803322375952},"targetPosition":{"x":24261.223798576262,"y":27685.764859522817},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"8a8d2e17-3d36-4c9d-9e23-3ed83ba832fc","savedPayload":{"position":{"x":24261.223798576262,"y":27685.764859522817},"targetPosition":{"x":24085.915743289333,"y":27826.580971932108},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","id":"41be12d8-248f-4f84-864d-badd7a650228","savedPayload":{"position":{"x":24499.820125475813,"y":27648.85654457614},"targetPosition":{"x":24293.051818021606,"y":27738.418598347478},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"4baa4aa5-f868-4a50-9cde-f19857d9a782","savedPayload":{"position":{"x":24293.051818021606,"y":27738.418598347478},"targetPosition":{"x":24085.915743289333,"y":27826.580971932108},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}}}],"id":"9f1f9b46-e79a-41b0-9fad-1f5bb4a37855","savedPayload":{"position":{"x":24085.915743289333,"y":27826.580971932108},"targetPosition":{"x":23700.657939342225,"y":28058.282812779096},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":""}}},"id":"3988f843-9a19-4d7b-842d-49c697da0edf","savedPayload":{"position":{"x":23700.657939342225,"y":28058.282812779096},"targetPosition":{"x":22929.180739706946,"y":28520.824676588596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{"shortcutKey":""}}}],"id":"19cc48a3-d513-48aa-810b-49054c9c5276","savedPayload":{"position":{"x":22929.180739706946,"y":28520.824676588596},"targetPosition":{"x":22318.66936473428,"y":29181.45602262725},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":5},"uiObject":{}}},{"type":"Task Manager","name":"Masters HitBTC BTC/USDT","tasks":[{"type":"Task","name":"Trades","bot":{"type":"Sensor Bot Instance","name":"Charly","code":" {\n \"repo\": \"AACharly-Sensor-Bot\",\n \"startDate\": \"2020-01-01\"\n }","processes":[{"type":"Sensor Process Instance","name":"Historic-Trades","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"5a9d5a35-4d80-4889-9342-09fcced05cc7","savedPayload":{"position":{"x":24498.386535469715,"y":26789.937601955986},"targetPosition":{"x":24290.993954681384,"y":27056.07120738867},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"17ff999d-04bc-4ff2-9f0e-3fdbc9d0018c","savedPayload":{"position":{"x":24290.993954681384,"y":27056.07120738867},"targetPosition":{"x":24083.601373893052,"y":27322.204812821357},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Historic-Trades","id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629"}}}],"id":"bcb9eecd-d837-4327-a3b3-00e253aaa788","savedPayload":{"position":{"x":24083.601373893052,"y":27322.204812821357},"targetPosition":{"x":23876.20879310472,"y":27588.338418254043},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"c057d059-49e6-44af-a5b4-e90f1f9e9d39","savedPayload":{"position":{"x":23876.20879310472,"y":27588.338418254043},"targetPosition":{"x":23046.63846410514,"y":28652.87278076317},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Trades into 1 min Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Bruce","code":" {\n \"repo\": \"AABruce-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Single-Period-Daily","code":" {\n \"process\": \"Single-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"c6052f27-66f6-4027-9437-1b660afcb851","savedPayload":{"position":{"x":24698.28848468662,"y":26963.511592858013},"targetPosition":{"x":24461.954766809737,"y":27205.435170830635},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"403c2cd7-ca9c-4ab4-9f25-b99b61f1bc94","savedPayload":{"position":{"x":24461.954766809737,"y":27205.435170830635},"targetPosition":{"x":24226.068717333346,"y":27446.674782356327},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f"}}}],"id":"9d64ac26-d202-4795-b4e1-a68a88b97417","savedPayload":{"position":{"x":24226.068717333346,"y":27446.674782356327},"targetPosition":{"x":23990.182667856956,"y":27687.91439388202},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{"shortcutKey":""}}},"id":"f1f0fce4-7c94-4341-b98e-ba9a02d4fdd6","savedPayload":{"position":{"x":23990.182667856956,"y":27687.91439388202},"targetPosition":{"x":23046.63846410514,"y":28652.87278076317},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":" {\n \"repo\": \"AAOlivia-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"eb4e142c-6142-4893-9678-696b8ba54f01","savedPayload":{"position":{"x":24816.819509694025,"y":27094.412955582913},"targetPosition":{"x":24585.037948391888,"y":27340.70521847028},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"c2714988-2aa9-48f9-b6bf-6c83fe1a6b1e","savedPayload":{"position":{"x":24585.037948391888,"y":27340.70521847028},"targetPosition":{"x":24353.704048290845,"y":27586.313453614555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"b0ff8547-ce2b-4036-badf-f10d2991091b","savedPayload":{"position":{"x":24928.1906644782,"y":27230.897989169185},"targetPosition":{"x":24640.723525783975,"y":27408.947735263417},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"3742543b-d3d3-41b9-a4e8-faed6e98463f","savedPayload":{"position":{"x":24640.723525783975,"y":27408.947735263417},"targetPosition":{"x":24353.704048290845,"y":27586.313453614555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577"}}}],"id":"15bcfaf4-b936-4a60-affe-b15a86c725f8","savedPayload":{"position":{"x":24353.704048290845,"y":27586.313453614555},"targetPosition":{"x":24092.290926863436,"y":27799.62528185167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"ba70d04c-cfad-4c4b-aa85-72219f2614b4","savedPayload":{"position":{"x":24092.290926863436,"y":27799.62528185167},"targetPosition":{"x":23046.63846410514,"y":28652.87278076317},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Candle & Volume Stairs Patters","bot":{"type":"Indicator Bot Instance","name":"Tom","code":" {\n \"repo\": \"AATom-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"603fa7d3-cc38-4b70-b0e1-4d6a494e6612","savedPayload":{"position":{"x":25778.5175565775,"y":26683.328100222796},"targetPosition":{"x":25263.31245844197,"y":27120.337574332785},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"d2ee8b23-3cd0-4447-96f5-4b99f920ba42","savedPayload":{"position":{"x":25263.31245844197,"y":27120.337574332785},"targetPosition":{"x":24748.55502150754,"y":27556.66302069968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"80c87e44-7b3a-449d-9bf4-d14502bfa837"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"64fd71c7-1ee9-4b64-9985-3a33383fb12c","savedPayload":{"position":{"x":25969.296358692525,"y":26979.521014904392},"targetPosition":{"x":25358.701859499484,"y":27268.434031673583},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"e9ae7121-dd36-46c2-ba14-28dbc611eb42","savedPayload":{"position":{"x":25358.701859499484,"y":27268.434031673583},"targetPosition":{"x":24748.55502150754,"y":27556.66302069968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"2924712f-7d80-4f36-8822-71a12cb485f6"}}}],"id":"581a3423-170e-4967-89a8-f548610ed3e8","savedPayload":{"position":{"x":24748.55502150754,"y":27556.66302069968},"targetPosition":{"x":24181.249499522557,"y":27922.066252930606},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"f7861a1a-42f3-4ca9-a686-6de825c1b4d4","savedPayload":{"position":{"x":24181.249499522557,"y":27922.066252930606},"targetPosition":{"x":23046.63846410514,"y":28652.87278076317},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Bollinger Bands & Percentage Bandwidth","bot":{"type":"Indicator Bot Instance","name":"Chris","code":" {\n \"repo\": \"AAChris-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"1a7b0937-ce0d-4c42-a479-c912f46d132b","savedPayload":{"position":{"x":25119.08635135096,"y":27527.272372843625},"targetPosition":{"x":24838.451840035814,"y":27715.930088984347},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"eab711ed-7079-496a-8eed-63669bbb2933","savedPayload":{"position":{"x":24838.451840035814,"y":27715.930088984347},"targetPosition":{"x":24558.26498992176,"y":27903.903777381976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"9eb0640c-df7d-45cb-94af-a3a5cafebb50","savedPayload":{"position":{"x":25197.2944216173,"y":27685.11785506032},"targetPosition":{"x":24877.555875168982,"y":27794.852830092696},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"a159cc0a-a022-4928-bced-12eed4632d19","savedPayload":{"position":{"x":24877.555875168982,"y":27794.852830092696},"targetPosition":{"x":24558.26498992176,"y":27903.903777381976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}}],"id":"25104527-ebcf-4b48-a258-a71bc0fa0d76","savedPayload":{"position":{"x":24558.26498992176,"y":27903.903777381976},"targetPosition":{"x":24255.93968016817,"y":28053.697540865607},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"0b6c23f3-b308-4871-969a-2ff8166a3a34","savedPayload":{"position":{"x":24255.93968016817,"y":28053.697540865607},"targetPosition":{"x":23046.63846410514,"y":28652.87278076317},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":" {\n \"repo\": \"AAPaula-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"6424a542-fece-48fb-8abb-b827b1221e8e","savedPayload":{"position":{"x":26148.14760629844,"y":27340.492220654298},"targetPosition":{"x":25548.757005992324,"y":27652.017799228062},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"f2442a8a-6335-4fe3-b657-66afcd08ac15","savedPayload":{"position":{"x":25548.757005992324,"y":27652.017799228062},"targetPosition":{"x":24949.814066887302,"y":27962.859350058734},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"HitBTC BTC/USDT","id":"329aeb85-b45b-41ef-81f4-8abeee5a0223","savedPayload":{"position":{"x":26268.2340615268,"y":27671.711237793577},"targetPosition":{"x":25608.800233606504,"y":27817.627307797702},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"5c44fd86-9fb9-40ed-af02-78f93b420562"}}},"id":"c510b36f-ee07-4754-a628-e2752d529024","savedPayload":{"position":{"x":25608.800233606504,"y":27817.627307797702},"targetPosition":{"x":24949.814066887302,"y":27962.859350058734},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}}}],"id":"5acbc379-3b64-41d5-9c5d-5a6b549e06db","savedPayload":{"position":{"x":24949.814066887302,"y":27962.859350058734},"targetPosition":{"x":24315.4221964424,"y":28192.86380583664},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":""}}},"id":"03fffa5d-096a-4fd6-850d-c1eac5af70e5","savedPayload":{"position":{"x":24315.4221964424,"y":28192.86380583664},"targetPosition":{"x":23046.63846410514,"y":28652.87278076317},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{"shortcutKey":""}}}],"id":"71d7d936-9395-4977-918b-e3bbe89df320","savedPayload":{"position":{"x":23046.638459351914,"y":28652.872723636865},"targetPosition":{"x":22318.66936473428,"y":29181.45602262725},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":5},"uiObject":{}}},{"type":"Task Manager","name":"Masters Binance BTC/USDT","tasks":[{"type":"Task","name":"Trades","bot":{"type":"Sensor Bot Instance","name":"Charly","code":" {\n \"repo\": \"AACharly-Sensor-Bot\",\n \"startDate\": \"2020-01-01\"\n }","processes":[{"type":"Sensor Process Instance","name":"Historic-Trades","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","keyInstance":{"type":"Key Instance","name":"Key 1","id":"9b4d1d7f-c7f6-47d0-9b26-6a72d79790b3","savedPayload":{"position":{"x":23996.042452886584,"y":27486.38110044979},"targetPosition":{"x":23873.18241843104,"y":27674.796664040492},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Key","name":"Key 1","id":"d2893836-c3d5-449f-a132-971542ef0c0d"}}},"id":"f4d16eef-26b2-495c-bb73-6b6837a36343","savedPayload":{"position":{"x":23873.18241843104,"y":27674.796664040492},"targetPosition":{"x":23750.322383975494,"y":27863.212227631193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"774ace4c-9119-47e9-bc16-41261642cfe4","savedPayload":{"position":{"x":23750.322383975494,"y":27863.212227631193},"targetPosition":{"x":23627.46234951995,"y":28051.627791221894},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Historic-Trades","id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629"}}}],"id":"20f34189-e14b-45a6-9d44-61b2fcac6468","savedPayload":{"position":{"x":23627.46234951995,"y":28051.627791221894},"targetPosition":{"x":23381.742280608854,"y":28428.458918403296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7c58fe24-d0fb-42a1-b18f-9a9448969f36","savedPayload":{"position":{"x":23381.742280608854,"y":28428.458918403296},"targetPosition":{"x":23136.02221169776,"y":28805.290045584698},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"1 min Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Bruce","code":" {\n \"repo\": \"AABruce-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Single-Period-Daily","code":" {\n \"process\": \"Single-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"7f3abd43-5e58-4b2c-b289-7e140cfaefb3","savedPayload":{"position":{"x":24106.258716084405,"y":27867.17407105815},"targetPosition":{"x":23944.552632019964,"y":28023.52673347924},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"cf171ace-f35a-4494-9cd3-9cfae9ba62c5","savedPayload":{"position":{"x":23944.552632019964,"y":28023.52673347924},"targetPosition":{"x":23782.846547955523,"y":28179.87939590033},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f"}}}],"id":"d131874d-ffc9-4107-b543-e201d73f8dfc","savedPayload":{"position":{"x":23782.846547955523,"y":28179.87939590033},"targetPosition":{"x":23459.43437982664,"y":28492.584720742514},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"id":"df041d6f-d7c4-4ea4-a46b-e4f5daf4782a","savedPayload":{"position":{"x":23459.43437982664,"y":28492.584720742514},"targetPosition":{"x":23136.02221169776,"y":28805.290045584698},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":" {\n \"repo\": \"AAOlivia-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"55d0a691-73f4-4f88-a6a4-c65ddfc49aab","savedPayload":{"position":{"x":24217.289855263843,"y":28014.912283325677},"targetPosition":{"x":24061.543064265883,"y":28177.201992683535},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"68d2f35c-7701-498e-8693-e930b0c497e3","savedPayload":{"position":{"x":24061.543064265883,"y":28177.201992683535},"targetPosition":{"x":23905.796273267923,"y":28339.491702041392},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"9d721132-2575-4db0-9d5d-de9d112f17ca","savedPayload":{"position":{"x":24337.847337750056,"y":28214.144470885956},"targetPosition":{"x":24121.82180550899,"y":28276.818086463674},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"317f9e0e-eee4-459f-8b74-839b545e7a7c","savedPayload":{"position":{"x":24121.82180550899,"y":28276.818086463674},"targetPosition":{"x":23905.796273267923,"y":28339.491702041392},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577"}}}],"id":"05327546-27c2-47a3-8d2f-c8e2e1c66049","savedPayload":{"position":{"x":23905.796273267923,"y":28339.491702041392},"targetPosition":{"x":23520.90924248284,"y":28572.390873813045},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"aa951cb7-57b5-4422-961a-f01c53fae27d","savedPayload":{"position":{"x":23520.90924248284,"y":28572.390873813045},"targetPosition":{"x":23136.02221169776,"y":28805.290045584698},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Candle & Volume Stairs Patters","bot":{"type":"Indicator Bot Instance","name":"Tom","code":" {\n \"repo\": \"AATom-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"67d88fb2-3529-4a31-b6ce-a35185854941","savedPayload":{"position":{"x":24366.055815785585,"y":28275.33338989364},"targetPosition":{"x":24178.101064311115,"y":28398.897236012926},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"3e6fd2d2-d460-45e8-82e5-69052b824be8","savedPayload":{"position":{"x":24178.101064311115,"y":28398.897236012926},"targetPosition":{"x":23990.146312836645,"y":28522.461082132213},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"80c87e44-7b3a-449d-9bf4-d14502bfa837"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"9b94164b-0237-45b2-9064-8dcf4ff696d7","savedPayload":{"position":{"x":24439.25733803369,"y":28496.39697414946},"targetPosition":{"x":24214.701825435168,"y":28509.429028140836},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"dea7e82e-87d9-446a-aaf7-0ab3cc5f4a0c","savedPayload":{"position":{"x":24214.701825435168,"y":28509.429028140836},"targetPosition":{"x":23990.146312836645,"y":28522.461082132213},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"2924712f-7d80-4f36-8822-71a12cb485f6"}}}],"id":"4172d8b6-044e-4043-9562-619ea34fd428","savedPayload":{"position":{"x":23990.146312836645,"y":28522.461082132213},"targetPosition":{"x":23563.084262267203,"y":28663.875563858455},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"81c85ae5-51f5-4820-849d-921fbeaa27e9","savedPayload":{"position":{"x":23563.084262267203,"y":28663.875563858455},"targetPosition":{"x":23136.02221169776,"y":28805.290045584698},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Bollinger Bands & Percentage Bandwidth","bot":{"type":"Indicator Bot Instance","name":"Chris","code":" {\n \"repo\": \"AAChris-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"c0b1805e-a8d1-4322-8eb3-f159c92c9e56","savedPayload":{"position":{"x":24453.14290855839,"y":28562.329032767408},"targetPosition":{"x":24242.405032677038,"y":28640.971008446013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"08f1fbcb-c0e4-4254-891f-3c55c7dc0487","savedPayload":{"position":{"x":24242.405032677038,"y":28640.971008446013},"targetPosition":{"x":24031.667156795684,"y":28719.61298412462},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"11323886-c114-4a5e-90d4-deaa87869cf4","savedPayload":{"position":{"x":24475.317840579937,"y":28794.138962523033},"targetPosition":{"x":24253.49249868781,"y":28756.875973323826},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"f31dec21-307f-49fd-b25f-2583becc67ac","savedPayload":{"position":{"x":24253.49249868781,"y":28756.875973323826},"targetPosition":{"x":24031.667156795684,"y":28719.61298412462},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}}],"id":"a86e66c7-909a-4e03-93bb-08ab3cb76f17","savedPayload":{"position":{"x":24031.667156795684,"y":28719.61298412462},"targetPosition":{"x":23583.844760913256,"y":28762.451663196247},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a95948a2-7c60-47f4-a220-d6249a7b2b32","savedPayload":{"position":{"x":23583.844760913256,"y":28762.451663196247},"targetPosition":{"x":23136.02228829247,"y":28805.290193974502},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Task","name":"Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":" {}","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"7d9e0fe8-6f0e-4bf6-bcf7-33f710c2eafc","savedPayload":{"position":{"x":24474.18400098588,"y":28861.50762170121},"targetPosition":{"x":24251.230277759063,"y":28891.284289898318},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"6b0e423e-7a8d-4fbc-8e07-bbeaa60415bc","savedPayload":{"position":{"x":24251.230277759063,"y":28891.284289898318},"targetPosition":{"x":24028.276554532247,"y":28921.060958095426},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","id":"86e5d09e-51e8-46d7-b89b-74b97ec2231a","savedPayload":{"position":{"x":24444.220399094847,"y":29092.439979124734},"targetPosition":{"x":24236.248476813547,"y":29006.75046861008},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"408bee51-9d81-47f7-b0c6-99d92829caab","savedPayload":{"position":{"x":24236.248476813547,"y":29006.75046861008},"targetPosition":{"x":24028.276554532247,"y":28921.060958095426},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}}}],"id":"c82a3b14-2e19-4d02-ac80-bb02e5b7409f","savedPayload":{"position":{"x":24028.276554532247,"y":28921.060958095426},"targetPosition":{"x":23582.14942135322,"y":28863.175576098256},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"id":"7641c46a-fdeb-476a-96e3-fa4adf39a965","savedPayload":{"position":{"x":23582.14942135322,"y":28863.175576098256},"targetPosition":{"x":23136.02228829247,"y":28805.290193974502},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{"shortcutKey":""}}}],"id":"cd543d7f-97cc-4685-b8cd-aa8cc5f1d5a6","savedPayload":{"position":{"x":23136.02221169776,"y":28805.290045584698},"targetPosition":{"x":22318.66936473428,"y":29181.45602262725},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":5},"uiObject":{"shortcutKey":""}}},{"type":"Task Manager","name":"Masters Poloniex Draft","tasks":[{"type":"Task","name":"Trades","bot":{"type":"Sensor Bot Instance","name":"Charly","code":" {\n \"repo\": \"AACharly-Sensor-Bot\",\n \"startDate\": \"2020-01-01\"\n }","processes":[{"type":"Sensor Process Instance","name":"","code":" {\n \"process\": \"Live-Trades\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"22839b31-f532-47a3-b19b-d286c569e7d8","savedPayload":{"position":{"x":20106.937793647776,"y":28542.306474826204},"targetPosition":{"x":20940.73525027565,"y":28204.220680929448},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"338266fa-14c2-42eb-b0e5-d0a8ed9309fc","savedPayload":{"position":{"x":20940.73525027565,"y":28204.220680929448},"targetPosition":{"x":21774.532706903527,"y":27866.13488703269},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e"}}},{"type":"Sensor Process Instance","name":"","code":" {\n \"process\": \"Hole-Fixing\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"eeb69692-1ec6-41c1-a1eb-123aa8f37039","savedPayload":{"position":{"x":22022.988674490807,"y":26083.010328380024},"targetPosition":{"x":21898.88083235009,"y":26974.143004378995},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"3d7e0bdd-64e3-4442-8742-cc9282a82631","savedPayload":{"position":{"x":21898.88083235009,"y":26974.143004378995},"targetPosition":{"x":21774.532706903527,"y":27866.13488703269},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Hole-Fixing","id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18"}}}],"id":"85c4f582-56ee-4b8e-95b2-aa61baedfa39","savedPayload":{"position":{"x":21774.532706903527,"y":27866.13488703269},"targetPosition":{"x":22484.222321390684,"y":28419.18176913491},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2fcbea08-a941-4891-80fd-236eacd83c11","savedPayload":{"position":{"x":22484.222321390684,"y":28419.18176913491},"targetPosition":{"x":23193.91193587784,"y":28972.22865123713},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Trades into 1 min Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Bruce","code":" {\n \"repo\": \"AABruce-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Single-Period-Daily","code":" {\n \"process\": \"Single-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"80241632-7131-4f5f-80ae-5409b90feaea","savedPayload":{"position":{"x":23153.53284223983,"y":25373.521553928476},"targetPosition":{"x":23163.627615649333,"y":26273.19832825564},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"fdb6700e-c193-4a9c-962d-322edab4dedc","savedPayload":{"position":{"x":23163.627615649333,"y":26273.19832825564},"targetPosition":{"x":23173.722389058836,"y":27172.875102582802},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f"}}}],"id":"456f7bdf-0021-4752-8025-68f640b54ff4","savedPayload":{"position":{"x":23173.722389058836,"y":27172.875102582802},"targetPosition":{"x":23183.81716246834,"y":28072.551876909965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b864d5bb-2086-4c23-b04a-bc1f37eea874","savedPayload":{"position":{"x":23183.81716246834,"y":28072.551876909965},"targetPosition":{"x":23193.91193587784,"y":28972.22865123713},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":" {\n \"repo\": \"AAOlivia-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"5177620c-24c3-45c9-b7ec-0061acc0ad67","savedPayload":{"position":{"x":24299.97143495483,"y":26058.31808001698},"targetPosition":{"x":24444.043323357695,"y":26946.44166020498},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"050d715f-2851-48b4-86b6-d93ab4baa1b2","savedPayload":{"position":{"x":24444.043323357695,"y":26946.44166020498},"targetPosition":{"x":24588.11521176056,"y":27834.565240392978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"3babaa9a-e18f-440d-9160-8dbc4037b442","savedPayload":{"position":{"x":26270.46226444901,"y":28473.14898992482},"targetPosition":{"x":25429.288738104784,"y":28153.8571151589},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"111b43d5-d4ff-414c-8b9f-24f79e2a0254","savedPayload":{"position":{"x":25429.288738104784,"y":28153.8571151589},"targetPosition":{"x":24588.11521176056,"y":27834.565240392978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577"}}}],"id":"1e587326-ad4d-4ea8-9273-367b3009ea3d","savedPayload":{"position":{"x":24588.11521176056,"y":27834.565240392978},"targetPosition":{"x":23891.0135738192,"y":28403.396945815053},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e4164330-478a-4de8-b3ae-db67225bfa7a","savedPayload":{"position":{"x":23891.0135738192,"y":28403.396945815053},"targetPosition":{"x":23193.91193587784,"y":28972.22865123713},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Candle & Volume Stairs Patters","bot":{"type":"Indicator Bot Instance","name":"Tom","code":" {\n \"repo\": \"AATom-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"017b2e6b-1afc-4dd2-8272-c37d9ae6b786","savedPayload":{"position":{"x":26161.71746395306,"y":28020.19097501388},"targetPosition":{"x":25557.18184179358,"y":28686.566908143344},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"14b4277e-82c4-4391-af7d-cc456fc0a29e","savedPayload":{"position":{"x":25557.18184179358,"y":28686.566908143344},"targetPosition":{"x":24952.646219634098,"y":29352.94284127281},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"80c87e44-7b3a-449d-9bf4-d14502bfa837"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"8e802d95-1c52-4fe4-b669-47db83d7ee60","savedPayload":{"position":{"x":25502.307570595967,"y":31066.405186967757},"targetPosition":{"x":25227.476895115033,"y":30209.674014120283},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"c5de37ae-dfa5-4bac-b7f3-7d75a36adf1e","savedPayload":{"position":{"x":25227.476895115033,"y":30209.674014120283},"targetPosition":{"x":24952.646219634098,"y":29352.94284127281},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"2924712f-7d80-4f36-8822-71a12cb485f6"}}}],"id":"824572fe-b6af-4608-ae8b-23a8ad8133e3","savedPayload":{"position":{"x":24952.646219634098,"y":29352.94284127281},"targetPosition":{"x":24073.279816418977,"y":29162.587368827713},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"83565cd7-dbb8-47b7-8672-df205da6c9ab","savedPayload":{"position":{"x":24073.27844321819,"y":29162.58435291254},"targetPosition":{"x":23193.912040638745,"y":28972.228882097148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Bollinger Bands & Percentage Bandwidth","bot":{"type":"Indicator Bot Instance","name":"Chris","code":" {\n \"repo\": \"AAChris-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"4857ff00-a64e-4b1e-bc50-04a7441a3496","savedPayload":{"position":{"x":25788.646249962498,"y":30698.969554074574},"targetPosition":{"x":24890.73077100611,"y":30641.803170907213},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"1c1d8ed7-90df-478d-b647-6d442032d4c2","savedPayload":{"position":{"x":24890.73077100611,"y":30641.803170907213},"targetPosition":{"x":23992.815292049723,"y":30584.63678773985},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"9c549877-3fe8-4e0c-adbc-2cc96cb3a137","savedPayload":{"position":{"x":22995.884732962513,"y":32082.705635027494},"targetPosition":{"x":23494.35001250612,"y":31333.671211383673},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"ee814dbc-2af1-4489-ba89-99d992e28ed4","savedPayload":{"position":{"x":23494.35001250612,"y":31333.671211383673},"targetPosition":{"x":23992.815292049723,"y":30584.63678773985},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}}],"id":"d80f2c6c-99c3-4147-a738-1011de2bd42e","savedPayload":{"position":{"x":23992.815292049723,"y":30584.63678773985},"targetPosition":{"x":23593.36498640749,"y":29778.43574506989},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"df43162b-1971-4bac-9aed-d3ddcf61b216","savedPayload":{"position":{"x":23593.362344990557,"y":29778.429920005714},"targetPosition":{"x":23193.912040638745,"y":28972.228882097148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":" {\n \"repo\": \"AAPaula-Indicator-Bot\"\n }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"129bd62a-3bdc-48a7-9807-13444c3174e4","savedPayload":{"position":{"x":23461.688132603216,"y":32077.47911378461},"targetPosition":{"x":22946.541466641884,"y":31339.81786682715},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c","savedPayload":{"position":{"x":22946.541466641884,"y":31339.81786682715},"targetPosition":{"x":22431.39480068055,"y":30602.15661986969},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"b1f9ae5f-13c4-4117-bcb8-f3bd271db278","savedPayload":{"position":{"x":20638.58137621428,"y":30756.75557170697},"targetPosition":{"x":21534.988088447415,"y":30679.45609578833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"81f0108b-8af9-4b22-94dd-81ef4a5c8065","savedPayload":{"position":{"x":21534.988088447415,"y":30679.45609578833},"targetPosition":{"x":22431.39480068055,"y":30602.15661986969},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}}}],"id":"27564838-24b1-4814-af8c-ec65accb05d1","savedPayload":{"position":{"x":22431.39480068055,"y":30602.15661986969},"targetPosition":{"x":22812.654740722905,"y":29787.19566113481},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178","savedPayload":{"position":{"x":22812.65209930597,"y":29787.189836070633},"targetPosition":{"x":23193.912040638745,"y":28972.228882097148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}}],"id":"91469c80-5e9e-4a0f-8306-afbfb07e997b","savedPayload":{"position":{"x":23193.91193587784,"y":28972.22865123713},"targetPosition":{"x":22318.66936473428,"y":29181.45602262725},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":5},"uiObject":{"shortcutKey":""}}},{"type":"Task Manager","name":"Sparta Datasets Draft","tasks":[{"type":"Task","name":"Generates Exponential Moving Averages (EMAs)","bot":{"type":"Indicator Bot Instance","name":"EMA","code":"{ }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"62bf2557-9f4c-4318-8289-11b0c41e69c2","savedPayload":{"position":{"x":20821.695737653165,"y":27155.034774982712},"targetPosition":{"x":21708.43344793434,"y":27307.404180964404},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"033c4a8d-7063-4d49-bd8c-60e35625ffe4","savedPayload":{"position":{"x":21708.43344793434,"y":27307.404180964404},"targetPosition":{"x":22595.17115821552,"y":27459.773586946096},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"943ea24f-ee24-4eae-9b4b-e8a73122b1c2"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"89a8c00c-9164-413f-82cb-b7e0d83d5376","savedPayload":{"position":{"x":23745.820421176075,"y":26076.268225733504},"targetPosition":{"x":23170.495789695797,"y":26768.0209063398},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"9c27f4ca-ddd1-4788-a45d-b9c785963a32","savedPayload":{"position":{"x":23170.495789695797,"y":26768.0209063398},"targetPosition":{"x":22595.17115821552,"y":27459.773586946096},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"278c34a7-199d-400e-8a06-087149e3d391"}}}],"id":"6d36d506-48f6-471b-880e-e94acefc4a2e","savedPayload":{"position":{"x":22595.17115821552,"y":27459.773586946096},"targetPosition":{"x":22906.584237016417,"y":28303.895673534083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0d8eea02-dfa3-43b8-82f4-9f589d5e8711","savedPayload":{"position":{"x":22906.584237016417,"y":28303.895673534083},"targetPosition":{"x":23217.997315817316,"y":29148.01776012207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Generates Relative Stregth Index (RSI)","bot":{"type":"Indicator Bot Instance","name":"RSI","code":"{ }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"c2e4bc83-3c04-4cfb-af3f-f9819263b2d8","savedPayload":{"position":{"x":24372.938859399248,"y":26253.13391729252},"targetPosition":{"x":24502.04396498635,"y":27143.556330771236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"57d721b9-6c5f-4ea6-8078-f084d90598ee","savedPayload":{"position":{"x":24502.04396498635,"y":27143.556330771236},"targetPosition":{"x":24631.149070573454,"y":28033.97874424995},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"f1177bcd-22dc-425b-8f30-878844aa82b8"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"bb1874ca-c66e-4d59-935c-246feafa120e","savedPayload":{"position":{"x":26302.51103650379,"y":28700.78455533526},"targetPosition":{"x":25466.830053538622,"y":28367.381649792605},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"603c5fcb-5684-4801-8a64-96f66d31d39c","savedPayload":{"position":{"x":25466.830053538622,"y":28367.381649792605},"targetPosition":{"x":24631.149070573454,"y":28033.97874424995},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"96f31416-5090-428a-be83-985a0b3ab275"}}}],"id":"c33fe94a-4efe-485f-9b08-9c737d24dd17","savedPayload":{"position":{"x":24631.149070573454,"y":28033.97874424995},"targetPosition":{"x":23924.573193195385,"y":28590.99825218601},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"r"}}},"id":"3391ef51-3c4a-42d3-9626-60fb5c15b97e","savedPayload":{"position":{"x":23924.573193195385,"y":28590.99825218601},"targetPosition":{"x":23217.997315817316,"y":29148.01776012207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Generates Simple Moving Averages (SMAs)","bot":{"type":"Indicator Bot Instance","name":"SMA","code":"{ }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"2b0a9d1d-012b-4057-b9fd-fe64a83bf7b8","savedPayload":{"position":{"x":26328.092910360003,"y":29351.869999449824},"targetPosition":{"x":25521.1465434528,"y":29749.81190934269},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"02397e0f-d27e-48c2-8444-816446a47b6c","savedPayload":{"position":{"x":25521.1465434528,"y":29749.81190934269},"targetPosition":{"x":24714.200176545597,"y":30147.753819235557},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"333ae5d7-7ec1-4f08-b206-91e3b4f2c99f"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"bd098871-960a-4c3f-ac6a-e1ac552d27af","savedPayload":{"position":{"x":24596.50941603383,"y":31943.367835594814},"targetPosition":{"x":24655.354796289714,"y":31045.560827415185},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"142d7887-3e34-4e05-bedb-d4e357612026","savedPayload":{"position":{"x":24655.354796289714,"y":31045.560827415185},"targetPosition":{"x":24714.200176545597,"y":30147.753819235557},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"456269f6-9849-4d5f-9c8c-7cd7d1eef297"}}}],"id":"dc9e5645-feeb-472d-b4aa-d20df2985a3a","savedPayload":{"position":{"x":24714.200176545597,"y":30147.753819235557},"targetPosition":{"x":23966.099109224087,"y":29647.888186130247},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b411ad3e-bb9f-4337-9d52-5ddcadfe73d7","savedPayload":{"position":{"x":23966.09846404256,"y":29647.88392123323},"targetPosition":{"x":23217.997396627095,"y":29148.018291386776},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},{"type":"Task","name":"Generates Moving Average Convergence Divergence (MACD)","bot":{"type":"Indicator Bot Instance","name":"MACD","code":"{ }","processes":[{"type":"Indicator Process Instance","name":"Multi-Period-Daily","code":" {\n \"process\": \"Multi-Period-Daily\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"20dd192d-1235-4f9e-96e0-673417c768cc","savedPayload":{"position":{"x":23985.201545144784,"y":32168.89574743306},"targetPosition":{"x":23357.376157710787,"y":31524.414959816186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"240ef848-9942-4843-a7ca-2e9e5e0ce33e","savedPayload":{"position":{"x":23357.376157710787,"y":31524.414959816186},"targetPosition":{"x":22729.55077027679,"y":30879.93417219931},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"8e5db945-8507-4489-9288-21cfa1e9301c"}}},{"type":"Indicator Process Instance","name":"Multi-Period-Market","code":" {\n \"process\": \"Multi-Period-Market\"\n }","marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"a3b800f6-f26d-468a-94cf-0b055186d4c5","savedPayload":{"position":{"x":20985.45191418836,"y":31322.878853899732},"targetPosition":{"x":21857.501342232576,"y":31101.406513049522},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"8d142a18-680f-44ee-acf7-9e36d520d072","savedPayload":{"position":{"x":21857.501342232576,"y":31101.406513049522},"targetPosition":{"x":22729.55077027679,"y":30879.93417219931},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"44019efe-950a-4eec-a546-cd2ee26818b9"}}}],"id":"26b04b09-73b2-4c9e-9b89-0ccbca411971","savedPayload":{"position":{"x":22729.55077027679,"y":30879.93417219931},"targetPosition":{"x":22973.77472941221,"y":30013.98050583413},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d91eb798-593c-4c3b-9505-f50cd4ae7778","savedPayload":{"position":{"x":22973.77343678102,"y":30013.971951413554},"targetPosition":{"x":23217.997396627095,"y":29148.018291386776},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"8bd58fd7-29b8-44db-9557-89d7fc00a3cf","savedPayload":{"position":{"x":23217.997315817316,"y":29148.01776012207},"targetPosition":{"x":22318.66936473428,"y":29181.45602262725},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":5},"uiObject":{"shortcutKey":"i"}}}],"id":"7449a608-32ca-4285-94d1-c96791e2817f","savedPayload":{"position":{"x":22318.66936473428,"y":29181.45602262725},"targetPosition":{"x":21955.090399739755,"y":29445.668778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"testingEnvironment":{"type":"Testing Environment","name":"New Testing Environment","taskManagers":[{"type":"Task Manager","name":"Testing Sessions","tasks":[{"type":"Task","name":"Back BRR","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Backtesting Session","name":"Back BRR","code":"{\n\"folderName\": \"Back-BRR\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"USDT\",\n\"initialBalance\": 100,\n\"minimumBalance\": 1,\n\"maximumBalance\": 10000\n}","id":"666f6f10-2f17-4f5e-951c-b532bdb574d2","savedPayload":{"position":{"x":25264.233660015056,"y":29799.583702241333},"targetPosition":{"x":24854.790125880503,"y":30097.0618426069},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"initialDatetime\": \"2019-01-01T00:00:00.000Z\",\n\"finalDatetime\": \"2020-12-31T23:59:59.999Z\"\n}","id":"172958dd-b268-4444-8163-70979538ea6e","savedPayload":{"position":{"x":25327.275218136427,"y":29915.691808599462},"targetPosition":{"x":24854.790125880503,"y":30097.0618426069},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-hs\"\n}","id":"37ee839d-9a7c-410c-9251-c33b8f1b99f7","savedPayload":{"position":{"x":25358.132988025784,"y":30044.164745234124},"targetPosition":{"x":24854.80129322332,"y":30097.066239278578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"86ebd973-6ad9-4070-aece-a87623f24c2e","savedPayload":{"position":{"x":25354.67452567398,"y":30176.238093986252},"targetPosition":{"x":24854.80129322332,"y":30097.066239278578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"a0ec3f6f-6047-4740-87fe-393b1c714adb","savedPayload":{"position":{"x":25317.150809561605,"y":30302.916036587},"targetPosition":{"x":24854.80129322332,"y":30097.066239278578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"09db5e1e-a7e8-468c-ad70-0d2ab1c8b8b8","savedPayload":{"position":{"x":24854.790125880503,"y":30097.0618426069},"targetPosition":{"x":24351.462553721914,"y":30149.96370216676},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":4},"uiObject":{}}},"id":"121202ea-6735-4bf3-8a95-aaa55f00f6e7","savedPayload":{"position":{"x":24351.462553721914,"y":30149.96370216676},"targetPosition":{"x":24015.910838949523,"y":30185.231608539998},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","id":"a239a374-497a-43f9-8382-5277410b1c2d"}}},"marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"f71a1cc8-d617-48f9-ad89-0d0612a4c6e8","savedPayload":{"position":{"x":24324.141101412464,"y":30322.46456306868},"targetPosition":{"x":24015.910838949523,"y":30185.231608539998},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"3f483482-d8a1-4755-ba50-7cac6296fa5f","savedPayload":{"position":{"x":24015.910838949523,"y":30185.231608539998},"targetPosition":{"x":23682.664765745,"y":30132.45061574515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"25fe9099-b53d-4757-8a63-3f8a15875f33","savedPayload":{"position":{"x":23682.664765745,"y":30132.45061574515},"targetPosition":{"x":23349.41562128832,"y":30079.667190434622},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":""}}},"id":"5b21d2d2-0f52-4408-83eb-ddaa844ec58c","savedPayload":{"position":{"x":23349.41298708382,"y":30079.664360277493},"targetPosition":{"x":22682.460895071003,"y":29974.779643490823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":4},"uiObject":{"shortcutKey":""}}},{"type":"Task","name":"Paper WHB","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Paper Trading Session","name":"Paper WHB","code":"{\n\"folderName\": \"Paper-WHB\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"BTC\",\n\"initialBalance\": 0.01,\n\"minimumBalance\": 0.0001,\n\"maximumBalance\": 100\n}","id":"25b4fd3d-097a-4e88-a283-45bc2e2bec6e","savedPayload":{"position":{"x":25191.786570638964,"y":30606.548521247096},"targetPosition":{"x":24710.453358742492,"y":30762.93900776469},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"finalDatetime\": \"2020-12-31T23:59:59.999Z\"\n}","id":"fa9412b3-c41b-4ab7-8831-5dada63d06e4","savedPayload":{"position":{"x":25215.886143842665,"y":30736.455682543125},"targetPosition":{"x":24710.477585833098,"y":30762.9463364653},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"7fb83e5e-a7a9-4948-afc0-ee398f921dde","savedPayload":{"position":{"x":25205.520236192166,"y":30868.167027426633},"targetPosition":{"x":24710.477585833098,"y":30762.9463364653},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"9f2d1305-153b-431e-b63c-4a968ee61d34","savedPayload":{"position":{"x":25161.418133767704,"y":30992.707522745153},"targetPosition":{"x":24710.477585833098,"y":30762.9463364653},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"867c37a1-8474-4db7-8718-e0aa068736de","savedPayload":{"position":{"x":25086.585321967337,"y":31101.589939555637},"targetPosition":{"x":24710.477585833098,"y":30762.9463364653},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"87e38a84-7c06-421b-afa5-1fd38017a2ea","savedPayload":{"position":{"x":24710.46424612319,"y":30762.953034611834},"targetPosition":{"x":24215.423705152713,"y":30657.72891933588},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":4},"uiObject":{}}},"id":"c5eaba2a-7e87-4224-924b-342df9717159","savedPayload":{"position":{"x":24215.423705152713,"y":30657.72891933588},"targetPosition":{"x":23885.39667783906,"y":30587.579509151914},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{"shortcutKey":"h"},"referenceParent":{"type":"Trading System","name":"Bull run rider - USDT- SDA v.0.0.3","id":"56432995-eb04-4929-850f-6530233a3be3"}}},"marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"929befe8-6067-4e4b-b5cf-accd8e37b158","savedPayload":{"position":{"x":24136.133762336158,"y":30813.34419408855},"targetPosition":{"x":23885.39667783906,"y":30587.579509151914},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"ed2a205f-2369-496d-8460-4fa2c1bec49f","savedPayload":{"position":{"x":23885.39667783906,"y":30587.579509151914},"targetPosition":{"x":23584.771052141718,"y":30434.40310208893},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"b7835d6b-653f-4f9e-9b67-8c78ad1082dc","savedPayload":{"position":{"x":23584.771052141718,"y":30434.40310208893},"targetPosition":{"x":23284.141255483944,"y":30281.226716026318},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{"shortcutKey":""}}},"id":"54bc0b34-6b2c-4457-9c39-14d9adead46b","savedPayload":{"position":{"x":23284.120339838682,"y":30281.21548591148},"targetPosition":{"x":22682.460895071003,"y":29974.779643490823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Paper BRR","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Paper Trading Session","name":"Paper BRR","code":"{\n\"folderName\": \"Paper-BRR\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"USDT\",\n\"initialBalance\": 100,\n\"minimumBalance\": 1,\n\"maximumBalance\": 10000\n}","id":"5eab19f8-f1ed-4e1f-9c1d-b0a60720aa0e","savedPayload":{"position":{"x":24873.512913478233,"y":31351.165296448842},"targetPosition":{"x":24367.41597916868,"y":31351.167548658777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"finalDatetime\": \"2020-12-31T23:59:59.999Z\"\n}","id":"bf95030a-86e1-4b60-8db9-c2eef3b3555c","savedPayload":{"position":{"x":24856.267972761387,"y":31482.153625822542},"targetPosition":{"x":24367.41597916868,"y":31351.167548658777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"84b7fbf3-55c6-4105-bbc1-6f916e594419","savedPayload":{"position":{"x":24805.708364822083,"y":31604.215317017875},"targetPosition":{"x":24367.41597916868,"y":31351.167548658777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"9dea69ed-955c-4302-be56-c618eb55ede5","savedPayload":{"position":{"x":24725.279643387687,"y":31709.03206749636},"targetPosition":{"x":24367.41597916868,"y":31351.167548658777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"dccbd8b7-f2ed-45a6-878c-f323b2a9fee3","savedPayload":{"position":{"x":24620.462892909203,"y":31789.460788930755},"targetPosition":{"x":24367.41597916868,"y":31351.167548658777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"f17cb007-672f-4a78-8dd9-24aa1b7ce7a7","savedPayload":{"position":{"x":24367.393412736357,"y":31351.175781680897},"targetPosition":{"x":23929.097920254444,"y":31098.125761111864},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":4},"uiObject":{}}},"id":"8b83403b-914b-45b7-8111-5696c36f4f7e","savedPayload":{"position":{"x":23929.097920254444,"y":31098.125761111864},"targetPosition":{"x":23636.900925266502,"y":30929.425747399175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Trading System","name":"Bull run rider - USDT- SDA v.0.0.3","id":"56432995-eb04-4929-850f-6530233a3be3"}}},"marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"891b0ad6-818d-4768-94e1-a70d4f9881f6","savedPayload":{"position":{"x":23805.60093897919,"y":31221.622742387117},"targetPosition":{"x":23636.900925266502,"y":30929.425747399175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"4c7a189b-2d01-4552-977c-e6877a285e62","savedPayload":{"position":{"x":23636.900925266502,"y":30929.425747399175},"targetPosition":{"x":23398.323077901492,"y":30690.847900034165},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"17e12190-f40c-443a-9ac2-9b49408702dc","savedPayload":{"position":{"x":23398.323077901492,"y":30690.847900034165},"targetPosition":{"x":23159.745246051545,"y":30452.265408170053},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"d744ef6a-91ba-426c-941a-011a09dc6128","savedPayload":{"position":{"x":23159.73911612189,"y":30452.28306184686},"targetPosition":{"x":22682.460895071003,"y":29974.779643490823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":4},"uiObject":{}}},{"type":"Task","name":"Back WHB","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Backtesting Session","name":"Back WHB","code":"{\n\"folderName\": \"Back-WHB\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Bitcoin","code":"{\n\"initialBalance\": 0.001,\n\"minimumBalance\": 0.0001,\n\"maximumBalance\": 0.1\n}","id":"592ab3f6-3a01-4679-a197-15f69d1f05b5","savedPayload":{"position":{"x":24345.14886782918,"y":31942.27865276289},"targetPosition":{"x":23858.332466152584,"y":31804.691419754905},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Bitcoin","id":"9b4040e3-0955-4eea-b668-642fbcf3caa1"}}},"quotedAsset":{"type":"Quoted Asset","name":"Tether","code":"{\n\"initialBalance\": 0,\n\"minimumBalance\": 0,\n\"maximumBalance\": 100\n}","id":"5d5c120c-d0c2-437f-a4e7-a74eff6252d1","savedPayload":{"position":{"x":24302.125911484032,"y":32047.601449335572},"targetPosition":{"x":23858.332466152584,"y":31804.691419754905},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Tether","id":"7e333b79-62ea-4251-8e77-32b9605c4a19"}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"initialDatetime\": \"2018-01-01T00:00:00.000Z\",\n\"finalDatetime\": \"2019-12-31T23:59:59.999Z\"\n}","id":"0461ebd0-083d-4995-a27d-7c0db0bcfe59","savedPayload":{"position":{"x":24237.019740542553,"y":32140.844438694014},"targetPosition":{"x":23858.351625586274,"y":31804.948702000114},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-hs\"\n}","id":"d23bfc6a-40b5-44c8-b044-5b07354210d8","savedPayload":{"position":{"x":24152.678487151195,"y":32216.780291200204},"targetPosition":{"x":23858.351625586274,"y":31804.948702000114},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"2e07b5ba-515a-46a2-9fcb-9c52c91cbd06","savedPayload":{"position":{"x":24053.4763886435,"y":32272.028158927595},"targetPosition":{"x":23858.351625586274,"y":31804.948702000114},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"c5da66da-6fca-4d9b-b091-d76067415ef8","savedPayload":{"position":{"x":23943.99139512426,"y":32303.81416396267},"targetPosition":{"x":23858.351625586274,"y":31804.948702000114},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"e2d866ba-b44a-45aa-8173-ee280626e3c0","savedPayload":{"position":{"x":23858.332466152584,"y":31804.691419754905},"targetPosition":{"x":23519.906563116518,"y":31428.770939012826},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":4},"uiObject":{}}},"id":"e5a4324d-479f-45ae-b882-54939c919a63","savedPayload":{"position":{"x":23519.906563116518,"y":31428.770939012826},"targetPosition":{"x":23294.15012958854,"y":31178.21861522993},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","id":"a239a374-497a-43f9-8382-5277410b1c2d"}}},"marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"e9c0900c-b4c9-4821-a5f6-b052464e2bef","savedPayload":{"position":{"x":23363.820135873502,"y":31508.317548373045},"targetPosition":{"x":23294.15012958854,"y":31178.21861522993},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"bd6770b7-bcd6-402b-b301-5a0f0922f46b","savedPayload":{"position":{"x":23294.15012958854,"y":31178.21861522993},"targetPosition":{"x":23141.16608767363,"y":30877.7302637135},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"e2af488f-d970-4605-b63d-bd9b1514718f","savedPayload":{"position":{"x":23141.16608767363,"y":30877.7302637135},"targetPosition":{"x":22988.03568324626,"y":30576.640936211486},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"f7baff84-f86a-4d45-808b-72e0a9c35585","savedPayload":{"position":{"x":22988.050632109644,"y":30576.66474073285},"targetPosition":{"x":22682.460895071003,"y":29974.779643490823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{"shortcutKey":""}}}],"id":"2b8b44d1-c9c2-465c-b408-72ae6a5159db","savedPayload":{"position":{"x":22682.460895071003,"y":29974.779643490823},"targetPosition":{"x":22318.695405615283,"y":29710.2112107552},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}}],"id":"2ae685a1-25c6-4bd5-b559-d83e373e8d45","savedPayload":{"position":{"x":22318.695405615283,"y":29710.2112107552},"targetPosition":{"x":21955.090399739755,"y":29445.668778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"productionEnvironment":{"type":"Production Environment","name":"New Production Environment","taskManagers":[{"type":"Task Manager","name":"Live Sessions","tasks":[{"type":"Task","name":"Fwd WHB","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Fordward Testing Session","name":"Fwd WHB","code":"{\n\"folderName\": \"Fwd-WHB\",\n\"balancePercentage\": 10\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"BTC\",\n\"initialBalance\": 0.01,\n\"minimumBalance\": 0.0001,\n\"maximumBalance\": 100\n}","id":"27cc7654-df0a-49f5-9306-5e3478d88bbd","savedPayload":{"position":{"x":22619.541286469608,"y":32720.855074852956},"targetPosition":{"x":22255.591478350005,"y":32456.430061194675},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"finalDatetime\": \"2020-12-31T23:59:59.999Z\"\n}","id":"1df09c03-5998-4e66-89c0-6eb021b64bed","savedPayload":{"position":{"x":22538.701767949424,"y":32806.04215280681},"targetPosition":{"x":22255.591478350005,"y":32456.430061194675},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"d9e63d80-13aa-4638-9bbe-05c4eb427b85","savedPayload":{"position":{"x":22438.56875105491,"y":32867.40374447859},"targetPosition":{"x":22255.591478350005,"y":32456.430061194675},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"415915dd-9bda-4dbc-b5bf-590cc1bd2436","savedPayload":{"position":{"x":22325.966135409803,"y":32900.75815880071},"targetPosition":{"x":22255.591478350005,"y":32456.430061194675},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"f0251102-c8d4-499e-aca0-bfe579682d3d","savedPayload":{"position":{"x":22208.567603185686,"y":32903.832347557865},"targetPosition":{"x":22255.591478350005,"y":32456.430061194675},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"ac7ada1f-9f3a-46df-ad1c-a0a013ae91ef","savedPayload":{"position":{"x":22255.591478350005,"y":32456.430061194675},"targetPosition":{"x":22072.6142056451,"y":32045.456377910752},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cc68bed4-8472-4d26-9573-1c7b40648b71","savedPayload":{"position":{"x":22072.6142056451,"y":32045.456377910752},"targetPosition":{"x":21889.636932940193,"y":31634.48269462683},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","id":"a239a374-497a-43f9-8382-5277410b1c2d"}}},"marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"f89d1108-523a-4b69-b5db-60b7832508db","savedPayload":{"position":{"x":21842.613057775874,"y":32081.88498099002},"targetPosition":{"x":21889.636932940193,"y":31634.48269462683},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"9cfa0c18-d720-42b4-bd11-c8f3f0eb86a3","savedPayload":{"position":{"x":21889.636932940193,"y":31634.48269462683},"targetPosition":{"x":21819.262275880395,"y":31190.1545970208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"54a45585-1950-486e-b41e-58fbf68ccc87","savedPayload":{"position":{"x":21819.262275880395,"y":31190.1545970208},"targetPosition":{"x":21748.888201918668,"y":30745.82168373506},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2f75b6d3-47ee-4638-a981-52b567f07c92","savedPayload":{"position":{"x":21748.8299893783,"y":30745.84363892616},"targetPosition":{"x":21676.71961714057,"y":30301.04250264062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Fwd BRR","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Fordward Testing Session","name":"Fwd BRR","code":"{\n\"folderName\": \"Fwd-BRR\",\n\"balancePercentage\": 10\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"USDT\",\n\"initialBalance\": 100,\n\"minimumBalance\": 1,\n\"maximumBalance\": 10000\n}","id":"719732be-15c6-4312-bf23-c628fdcc4764","savedPayload":{"position":{"x":21824.200955820084,"y":32893.80364613361},"targetPosition":{"x":21559.78077978861,"y":32529.853625112624},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"finalDatetime\": \"2020-12-31T23:59:59.999Z\"\n}","id":"729c6352-8e51-4c06-bacd-d923c7da44b6","savedPayload":{"position":{"x":21720.993750168418,"y":32949.84058668594},"targetPosition":{"x":21559.78077978861,"y":32529.853625112624},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"627fabc2-d723-4476-85a4-f4a6feb34340","savedPayload":{"position":{"x":21606.799817326122,"y":32977.25612437719},"targetPosition":{"x":21559.78077978861,"y":32529.853625112624},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"ef88bd71-3928-40ad-ba8f-b5353753a97e","savedPayload":{"position":{"x":21489.401285102005,"y":32974.181935620036},"targetPosition":{"x":21559.78077978861,"y":32529.853625112624},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"d9f5b274-8966-4eb8-a79c-33d6201619e3","savedPayload":{"position":{"x":21376.798669456897,"y":32940.82752129793},"targetPosition":{"x":21559.78077978861,"y":32529.853625112624},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"e573e921-7346-49a8-836b-bf370e877009","savedPayload":{"position":{"x":21559.79451053405,"y":32529.848751167017},"targetPosition":{"x":21512.770635369732,"y":32082.446464803827},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4d3a8e33-af23-4b2f-bf7c-45cadc64099f","savedPayload":{"position":{"x":21512.770635369732,"y":32082.446464803827},"targetPosition":{"x":21465.746760205413,"y":31635.044178440636},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Trading System","name":"Bull run rider - USDT- SDA v.0.0.3","id":"56432995-eb04-4929-850f-6530233a3be3"}}},"marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"7d4c2e3a-43ac-47f0-9e53-0ad85df76b06","savedPayload":{"position":{"x":21282.769487500507,"y":32046.01786172456},"targetPosition":{"x":21465.746760205413,"y":31635.044178440636},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"2635e474-9d0b-4acd-b78e-d39d053228e0","savedPayload":{"position":{"x":21465.746760205413,"y":31635.044178440636},"targetPosition":{"x":21536.12141726521,"y":31190.716080834605},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"dd7b90d8-4548-4010-ae1f-95d470378a12","savedPayload":{"position":{"x":21536.12141726521,"y":31190.716080834605},"targetPosition":{"x":21606.500056559315,"y":30746.385050430825},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b6a93695-d43d-4c4f-9ddd-d32a2cf71c55","savedPayload":{"position":{"x":21606.465094236843,"y":30746.381369956613},"targetPosition":{"x":21676.71961714057,"y":30301.04250264062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Live Trading BRR","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Live Trading Session","name":"Live Trading BRR","code":"{\n\"folderName\": \"Live-BRR\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"USDT\",\n\"initialBalance\": 100,\n\"minimumBalance\": 1,\n\"maximumBalance\": 10000\n}","id":"8acf67b1-638b-4c02-a80b-0bc70dfdf5ec","savedPayload":{"position":{"x":21015.090737183127,"y":32812.110365533124},"targetPosition":{"x":20876.074027959836,"y":32384.26659162979},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"finalDatetime\": \"2020-12-31T23:59:59.999Z\"\n}","id":"8d80c530-c3fe-496f-8229-3855e7408acd","savedPayload":{"position":{"x":20899.618484776045,"y":32833.51188251035},"targetPosition":{"x":20876.074027959836,"y":32384.26659162979},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"1bca99d0-fa30-4ca6-8968-4f85516355e0","savedPayload":{"position":{"x":20782.541733768485,"y":32824.29774237657},"targetPosition":{"x":20876.074027959836,"y":32384.26659162979},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"d8164781-f37a-4cc2-9497-11db1edb4d76","savedPayload":{"position":{"x":20671.83907126313,"y":32785.09587355483},"targetPosition":{"x":20876.074027959836,"y":32384.26659162979},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"9234691b-d12c-48c0-9644-59541829291f","savedPayload":{"position":{"x":20575.054700764926,"y":32718.57781862117},"targetPosition":{"x":20876.074027959836,"y":32384.26659162979},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"49b8a9d0-d3af-4df8-a093-95f67ba30dc5","savedPayload":{"position":{"x":20876.126413595746,"y":32384.293819416438},"targetPosition":{"x":20969.658960507702,"y":31944.257782998237},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"59a383af-80c5-4a9a-921b-e04c91c64f48","savedPayload":{"position":{"x":20969.658960507702,"y":31944.257782998237},"targetPosition":{"x":21063.19150741966,"y":31504.221746580035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Trading System","name":"Bull run rider - USDT- SDA v.0.0.3","id":"56432995-eb04-4929-850f-6530233a3be3"}}},"marketReference":{"type":"Market Reference","name":"Poloniex BTC/USDT","id":"0e2bb99a-fadc-4a77-87b7-7e7a0d6cec55","savedPayload":{"position":{"x":20762.171927504143,"y":31838.537859242835},"targetPosition":{"x":21063.19150741966,"y":31504.221746580035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},"id":"086319ff-c1a5-4a91-97f8-82d187f46a6f","savedPayload":{"position":{"x":21063.19150741966,"y":31504.221746580035},"targetPosition":{"x":21267.42671683697,"y":31103.387578983577},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"d6ea855d-94cb-4a0c-b1f6-2616cd95ef39","savedPayload":{"position":{"x":21267.42671683697,"y":31103.387578983577},"targetPosition":{"x":21471.666181961027,"y":30702.550574693447},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"203c786d-2efa-4618-b4d7-61cec6cc4178","savedPayload":{"position":{"x":21471.63981201808,"y":30702.5499284351},"targetPosition":{"x":21676.71961714057,"y":30301.04250264062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Live Trading WHB","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Live Trading Session","name":"Live Trading WHB","code":"{\n\"folderName\": \"Live-WHB\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Bitcoin","code":"{\n\"initialBalance\": 0.001,\n\"minimumBalance\": 0.0001,\n\"maximumBalance\": 0.1\n}","id":"7581ccf4-7bc7-40a1-98ab-84d6f26aa59d","savedPayload":{"position":{"x":20270.518913630483,"y":32483.443428360446},"targetPosition":{"x":20270.518913630483,"y":32033.57672512661},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Bitcoin","id":"9b4040e3-0955-4eea-b668-642fbcf3caa1"}}},"quotedAsset":{"type":"Quoted Asset","name":"Tether","code":"{\n\"initialBalance\": 0,\n\"minimumBalance\": 0,\n\"maximumBalance\": 100\n}","id":"c5c9a448-4f16-4a83-a2e7-220acd4fe851","savedPayload":{"position":{"x":20154.08484307608,"y":32468.11459216769},"targetPosition":{"x":20270.518913630483,"y":32033.57672512661},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Tether","id":"7e333b79-62ea-4251-8e77-32b9605c4a19"}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"49b79a71-12bc-4c47-877a-08e19a5c5f70","savedPayload":{"position":{"x":20045.533074969626,"y":32423.16320675386},"targetPosition":{"x":20270.463035212506,"y":32033.570692072753},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"86b5925f-2b09-4032-be64-a1f5412c7cd5","savedPayload":{"position":{"x":19952.362630099862,"y":32351.671009923284},"targetPosition":{"x":20270.463035212506,"y":32033.570692072753},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"c6fbca0c-1280-4d0f-b611-d957c7f70046","savedPayload":{"position":{"x":19880.870433269287,"y":32258.50056505352},"targetPosition":{"x":20270.463035212506,"y":32033.570692072753},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"6f976714-c92f-4c5c-ad5d-5ed2d8a3d287","savedPayload":{"position":{"x":20270.518913630483,"y":32033.57672512661},"targetPosition":{"x":20495.4522652474,"y":31643.980731809355},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"id":"1be42eb3-bfaf-432f-8da6-15f15d547f50","savedPayload":{"position":{"x":20495.4522652474,"y":31643.980731809355},"targetPosition":{"x":20720.385616864318,"y":31254.3847384921},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","id":"a239a374-497a-43f9-8382-5277410b1c2d"}}},"marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","keyInstance":{"type":"Key Instance","name":"Key 1","id":"999f7554-da10-42b1-8e25-db3a04e1acbf","savedPayload":{"position":{"x":20006.579113471584,"y":31802.10773274024},"targetPosition":{"x":20363.48236516795,"y":31528.24623561617},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Key","name":"Key 1","id":"d2893836-c3d5-449f-a132-971542ef0c0d"}}},"id":"026f8fc4-b3c4-46a5-a8ae-7064da0fdf47","savedPayload":{"position":{"x":20363.48236516795,"y":31528.24623561617},"targetPosition":{"x":20720.385616864318,"y":31254.3847384921},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"16d02098-221f-4d83-9682-a5f83262bb74","savedPayload":{"position":{"x":20720.385616864318,"y":31254.3847384921},"targetPosition":{"x":21038.489413351,"y":30936.28094200542},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"72cdfb23-b7c6-4feb-ba2d-d56415d30162","savedPayload":{"position":{"x":21038.489413351,"y":30936.28094200542},"targetPosition":{"x":21356.589074336276,"y":30618.175717427097},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c6fb2087-d2a2-49a0-9790-2c4d1cb015d7","savedPayload":{"position":{"x":21356.575292946658,"y":30618.239034253136},"targetPosition":{"x":21676.71961714057,"y":30301.04250264062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"id":"a10117c3-fc8f-49e4-a424-0795068fbe7a","savedPayload":{"position":{"x":21676.71961714057,"y":30301.04250264062},"targetPosition":{"x":21815.815113930454,"y":29873.460732419524},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"l"}}}],"id":"32656457-f603-416a-b805-546642094dc4","savedPayload":{"position":{"x":21815.815113930454,"y":29873.460732419524},"targetPosition":{"x":21955.090399739755,"y":29445.668778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"ce601334-52c5-4ffb-ab61-49ca1943a15a","savedPayload":{"position":{"x":21955.090399739755,"y":29445.668778615294},"targetPosition":{"x":21055.356993272086,"y":29445.668778615294},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}}],"id":"efbbc81b-93c8-4ce0-873e-675f39d333e8","savedPayload":{"position":{"x":21055.356993272086,"y":29445.668778615294},"targetPosition":{"x":21055.356993272086,"y":29445.668778615294},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"n"}}},{"type":"Data Mine","name":"Masters","code":"{\n\"codeName\": \"AAMasters\"\n}","sensorBots":[{"type":"Sensor Bot","name":"Charly","code":"{\n\"codeName\": \"AACharly\",\n\"repo\": \"AACharly-Sensor-Bot\"\n}","processes":[{"type":"Process Definition","name":"Historic-Trades","code":"{\n \"codeName\": \"Historic-Trades\",\n \"description\": \"Retrieves and saves the historical trades in batches going backwards from the current time until reaching the begining of the market.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"2014\",\n \"maxYear\": \"2020\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"2018\",\n \"month\": \"01\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 60000,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"a4c30d1a-514e-4fe5-b8dd-273790ad194a","savedPayload":{"position":{"x":12389.365735719206,"y":17548.460000920502},"targetPosition":{"x":12184.157779433028,"y":17753.667957206682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"e637e168-9aa2-4167-9fda-d6eaf8ae1551","savedPayload":{"position":{"x":12726.965609047318,"y":17608.564019763293},"targetPosition":{"x":12435.485171463275,"y":17608.564019763293},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}}],"dataDependencies":[],"id":"d2f61094-66fd-4386-862b-9dd55c3f7243","savedPayload":{"position":{"x":12435.485171463275,"y":17608.564019763293},"targetPosition":{"x":12184.157779433028,"y":17753.667957206682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7","savedPayload":{"position":{"x":12464.477060778634,"y":17678.556632147254},"targetPosition":{"x":12184.157779433028,"y":17753.667957206682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"600ab7d6-ca03-4504-a4b2-351a493065cd","savedPayload":{"position":{"x":12474.365654319807,"y":17753.667957206682},"targetPosition":{"x":12184.157779433028,"y":17753.667957206682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"0c63c520-011d-4c1f-95c1-c966a92a583b","savedPayload":{"position":{"x":12464.477060778634,"y":17828.77928226611},"targetPosition":{"x":12184.157779433028,"y":17753.667957206682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629","savedPayload":{"position":{"x":12184.157779433028,"y":17753.667957206682},"targetPosition":{"x":11903.838498087422,"y":17828.77928226611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Trades","code":"{\n\"codeName\": \"Trades\"\n}","datasets":[{"type":"Dataset Definition","name":"Trades","code":"{\n\"codeName\": \"Trades\"\n}","id":"80c4ee03-aed4-4d8e-8145-a49f8b85df04","savedPayload":{"position":{"x":12464.477060778634,"y":17979.001932384966},"targetPosition":{"x":12184.157779433028,"y":17903.89060732554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"851e927a-fcf2-4405-913a-84e011be27b1","savedPayload":{"position":{"x":12184.157779433028,"y":17903.89060732554},"targetPosition":{"x":11903.838498087422,"y":17828.77928226611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}}],"id":"957bef47-cd90-4dfa-bbb1-3696c527b010","savedPayload":{"position":{"x":11903.838498087422,"y":17828.77928226611},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"indicatorBots":[{"type":"Indicator Bot","name":"Bruce","code":"{\n\"codeName\": \"AABruce\",\n\"repo\": \"AABruce-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Single-Period-Daily","code":"{\n \"codeName\": \"Single-Period-Daily\",\n \"description\": \"Produces two data-sets with candles and volumes.\",\n \"startMode\": {\n \"noTime\": {\n \"run\": \"true\"\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"597f9031-4a09-4bf1-b231-cd3de597ca5e","savedPayload":{"position":{"x":12802.799908697734,"y":18088.417968341095},"targetPosition":{"x":12287.96502645195,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"429872d3-959d-4b7a-af3e-baf9d7c2f364","savedPayload":{"position":{"x":12300.430734991918,"y":18366.12553107},"targetPosition":{"x":12807.24239860192,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Market Starting Point","code":"{\n \"mainUtility\": \"Market Starting Point\"\n}","id":"728daa6a-9297-4f53-974f-fa725d0aca2d","savedPayload":{"position":{"x":12307.152300360896,"y":18222.035376886022},"targetPosition":{"x":12807.24239860192,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"4aff6e01-e4ce-48e5-b1da-b2172599b36a","savedPayload":{"position":{"x":12310.397536622399,"y":18090.786399440796},"targetPosition":{"x":12807.24239860192,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9"}}}],"dataDependencies":[],"id":"36df0b5c-0849-4310-bae0-5ea595b335d3","savedPayload":{"position":{"x":12807.24239860192,"y":18156.197266433865},"targetPosition":{"x":12287.96502645195,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9","savedPayload":{"position":{"x":12802.799908697734,"y":18223.976564526634},"targetPosition":{"x":12287.96502645195,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"a81eb239-9b38-4a3e-b8c3-061b7462a624","savedPayload":{"position":{"x":11748.295201654313,"y":18113.724330100547},"targetPosition":{"x":12287.96502645195,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"0c63c520-011d-4c1f-95c1-c966a92a583b"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f3b263bb-1051-40d8-a69a-d8640cf9afbe","savedPayload":{"position":{"x":12767.714762277554,"y":18354.916113557738},"targetPosition":{"x":12287.96502645195,"y":18156.197266433865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","savedPayload":{"position":{"x":12287.96502645195,"y":18156.197266433865},"targetPosition":{"x":11773.130144206165,"y":18088.417968341095},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Candles","code":"{\n\"codeName\": \"Candles\"\n}","datasets":[{"type":"Dataset Definition","name":"One-Min","code":"{\n\"codeName\": \"One-Min\"\n}","id":"20d5740b-5711-4b8c-9675-a5391f5ba245","savedPayload":{"position":{"x":12222.837540098464,"y":18348.05665441608},"targetPosition":{"x":11997.983842152315,"y":18218.237311378587},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"3b42a93e-c933-420d-9504-25092a0d1279","savedPayload":{"position":{"x":11997.983842152315,"y":18218.237311378587},"targetPosition":{"x":11773.130144206165,"y":18088.417968341095},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}},{"type":"Product Definition","name":"Volumes","code":"{\n\"codeName\": \"Volumes\"\n}","datasets":[{"type":"Dataset Definition","name":"One-Min","code":"{\n\"codeName\": \"One-Min\"\n}","id":"2788ed30-ed62-4638-9a1d-35dc7e826870","savedPayload":{"position":{"x":12089.246179328073,"y":18500.388406073926},"targetPosition":{"x":11931.188161767119,"y":18294.40318720751},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"4e714023-0cfd-4945-a573-52922711cd09","savedPayload":{"position":{"x":11931.188161767119,"y":18294.40318720751},"targetPosition":{"x":11773.130144206165,"y":18088.417968341095},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}}],"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd","savedPayload":{"position":{"x":11773.130144206165,"y":18088.417968341095},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Indicator Bot","name":"Olivia","code":"{\n\"codeName\": \"AAOlivia\",\n\"repo\": \"AAOlivia-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Produces different files with multi-period candles and volumes for entire markets.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"7c42483e-6b75-4b61-91c2-597b3fea1aa0","savedPayload":{"position":{"x":12133.546073980671,"y":18383.52939990209},"targetPosition":{"x":12340.854814050695,"y":18297.659308166607},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"5aa52a69-6139-41c0-bce5-68f1419ab7f2","savedPayload":{"position":{"x":12106.47898719124,"y":18297.659308166607},"targetPosition":{"x":12340.854814050695,"y":18297.659308166607},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"8f6d7c41-f829-4157-8365-707a4789138e"}}}],"id":"2912df2e-2d79-4a8b-af79-e79d26be51d0","savedPayload":{"position":{"x":12340.854814050695,"y":18297.659308166607},"targetPosition":{"x":11907.21410914722,"y":18366.34124851468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":"{\n \"mainUtility\": \"Market Starting Point\"\n}","id":"92e1f19f-986b-43c8-a742-2b4832e061cc","savedPayload":{"position":{"x":11950.804635380291,"y":18518.589101380614},"targetPosition":{"x":12346.109749854208,"y":18354.84836169007},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"701ead56-8e85-4138-bfe8-263b1d59869d","savedPayload":{"position":{"x":11925.70826708394,"y":18455.777827988997},"targetPosition":{"x":12346.109749854208,"y":18354.84836169007},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9"}}},{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"757b878a-99bd-402c-bd1c-4ae7961e35bf","savedPayload":{"position":{"x":11981.429793078545,"y":18619.803859398748},"targetPosition":{"x":12346.109749854208,"y":18354.84836169007},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"72f6e0db-ed45-4ee8-aac0-188a7db38c1b","savedPayload":{"position":{"x":11904.900319310253,"y":18320.124426439143},"targetPosition":{"x":12346.109749854208,"y":18354.84836169007},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"One-Min","id":"20d5740b-5711-4b8c-9675-a5391f5ba245"}}},{"type":"Data Dependency","name":"New Data Dependency","id":"0bcc9ab5-7534-4aae-ab82-5dde001673db","savedPayload":{"position":{"x":11934.51667094943,"y":18256.033606060646},"targetPosition":{"x":12346.109749854208,"y":18354.84836169007},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"One-Min","id":"2788ed30-ed62-4638-9a1d-35dc7e826870"}}}],"id":"af72941b-880c-47b4-8a23-fc7279809d4d","savedPayload":{"position":{"x":12346.109749854208,"y":18354.84836169007},"targetPosition":{"x":11907.21410914722,"y":18366.34124851468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559","savedPayload":{"position":{"x":12343.855059560361,"y":18412.234061693533},"targetPosition":{"x":11907.21410914722,"y":18366.34124851468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"d54f53d2-7a7e-4ff7-afdf-33d25405865c","savedPayload":{"position":{"x":11482.42784262922,"y":18332.909844304057},"targetPosition":{"x":11907.21410914722,"y":18366.34124851468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f3b263bb-1051-40d8-a69a-d8640cf9afbe"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51","savedPayload":{"position":{"x":12317.098945882659,"y":18523.681295843777},"targetPosition":{"x":11907.21410914722,"y":18366.34124851468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"id":"383c3970-13d5-4313-bd8f-7e5d28948577","savedPayload":{"position":{"x":11907.21410914722,"y":18366.34124851468},"targetPosition":{"x":11616.120142205127,"y":18335.74603972878},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Produces different files with multi-period candles and volumes on a daily format.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"f349a029-5381-4f8e-92d4-1b7e7df76966","savedPayload":{"position":{"x":12029.101833896506,"y":18811.994685115653},"targetPosition":{"x":12224.82962726121,"y":18730.921578570633},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"15fd40ad-ceca-46ea-8fdd-4a396f9bde14","savedPayload":{"position":{"x":11995.69693599404,"y":18730.921578570633},"targetPosition":{"x":12224.82962726121,"y":18730.921578570633},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"404b8d12-db14-462c-b4e4-6ee5093560c3"}}}],"id":"e130a389-0b18-4433-809d-f44162a00a4a","savedPayload":{"position":{"x":12224.82962726121,"y":18730.921578570633},"targetPosition":{"x":11833.636695906138,"y":18531.598824382898},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":"{\n \"mainUtility\": \"Market Starting Point\"\n}","id":"93f6d813-594f-49cd-a32f-181a7b8352ee","savedPayload":{"position":{"x":11791.225354955483,"y":18947.719258220848},"targetPosition":{"x":12195.466077376945,"y":18780.277268530383},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"cb413af9-e1f6-4dbe-ab8c-9f7eb949bd93","savedPayload":{"position":{"x":11761.225057010513,"y":18884.52931384164},"targetPosition":{"x":12195.466077376945,"y":18780.277268530383},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}},{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"fbaa8e58-7e12-48d8-81e5-1050c034c736","savedPayload":{"position":{"x":11759.879270968944,"y":18814.558693664978},"targetPosition":{"x":12195.466077376945,"y":18780.277268530383},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"c9f72b18-362c-4663-9dab-02b592aba3a1","savedPayload":{"position":{"x":11733.307813299349,"y":18743.904624324372},"targetPosition":{"x":12195.466077376945,"y":18780.277268530383},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"One-Min","id":"20d5740b-5711-4b8c-9675-a5391f5ba245"}}},{"type":"Data Dependency","name":"New Data Dependency","id":"9aff9fc8-4935-4043-9a95-ee2fe02f2cc1","savedPayload":{"position":{"x":11769.192440776102,"y":18677.938022826685},"targetPosition":{"x":12195.466077376945,"y":18780.277268530383},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"One-Min","id":"2788ed30-ed62-4638-9a1d-35dc7e826870"}}}],"id":"dab7d491-7a05-47ac-9f86-a551eb24ef67","savedPayload":{"position":{"x":12195.466077376945,"y":18780.277268530383},"targetPosition":{"x":11833.636695906138,"y":18531.598824382898},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1","savedPayload":{"position":{"x":12159.911526457654,"y":18825.378001364075},"targetPosition":{"x":11833.636695906138,"y":18531.598824382898},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"b83c7f7c-d0c4-4daa-aa6f-41adaa2626b0","savedPayload":{"position":{"x":11402.636683481534,"y":18497.67838776365},"targetPosition":{"x":11833.636695906138,"y":18531.598824382898},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f3b263bb-1051-40d8-a69a-d8640cf9afbe"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f","savedPayload":{"position":{"x":12072.758335146664,"y":18899.813858739297},"targetPosition":{"x":11833.636695906138,"y":18531.598824382898},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","savedPayload":{"position":{"x":11833.636695906138,"y":18531.598824382898},"targetPosition":{"x":11616.120142205127,"y":18335.74603972878},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Candles","code":"{\n \"codeName\": \"Candles\",\n \"singularVariableName\": \"candle\",\n \"pluralVariableName\": \"candles\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AAOlivia/@Exchange/Output/Candles/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"7160ac78-d806-4bac-955b-a201c21c2e7e","savedPayload":{"position":{"x":11792.515850285274,"y":18890.97508604534},"targetPosition":{"x":11676.97535226701,"y":18622.047293304557},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AAOlivia/@Exchange/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAOlivia/@Exchange/Output/Candles/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2","savedPayload":{"position":{"x":11737.830562328894,"y":18908.348546880334},"targetPosition":{"x":11676.97535226701,"y":18622.047293304557},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Min","code":"{\n \"codeName\": \"min\",\n \"isString\": false\n}","id":"e138eefe-d936-4471-99bc-bd1313f69f32","savedPayload":{"position":{"x":11797.853764057614,"y":19114.65511440962},"targetPosition":{"x":11684.637933288657,"y":19207.39192889545},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Max","code":"{\n \"codeName\": \"max\",\n \"isString\": false\n}","id":"6ea0c4a1-bce0-496e-908d-08bca05b74ec","savedPayload":{"position":{"x":11827.731410330885,"y":19238.08717161561},"targetPosition":{"x":11684.637933288657,"y":19207.39192889545},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Open","code":"{\n \"codeName\": \"open\",\n \"isString\": false\n}","id":"618de9d2-e015-4d55-aa06-43fba70e915d","savedPayload":{"position":{"x":11749.856749816374,"y":19338.40508498448},"targetPosition":{"x":11684.637933288657,"y":19207.39192889545},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Close","code":"{\n \"codeName\": \"close\",\n \"isString\": false\n}","id":"bb4312b6-07c1-432a-89a5-cda0b2c4ef50","savedPayload":{"position":{"x":11622.870990235082,"y":19340.067419636962},"targetPosition":{"x":11684.637933288657,"y":19207.39192889545},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"4ebd2a25-25a1-491c-9061-bf314fbdabf1","savedPayload":{"position":{"x":11542.396998589154,"y":19241.82240367429},"targetPosition":{"x":11684.637933288657,"y":19207.39192889545},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"4c0e4d68-c8bb-4e60-ab9a-add55081f0bc","savedPayload":{"position":{"x":11569.033331958244,"y":19117.65053794946},"targetPosition":{"x":11684.637933288657,"y":19207.39192889545},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"3594c8a8-d72e-4142-b34f-c4bfd8a61560","savedPayload":{"position":{"x":11684.637933288657,"y":19207.39192889545},"targetPosition":{"x":11676.97535226701,"y":18622.047293304557},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":5},"uiObject":{}}},"id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1","savedPayload":{"position":{"x":11676.97535226701,"y":18622.047293304557},"targetPosition":{"x":11616.120142205127,"y":18335.74603972878},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Candles","id":"0aed75a3-fee8-4ddc-97ff-b6768e6f12a3"}}},{"type":"Product Definition","name":"Volumes","code":"{\n \"codeName\": \"Volumes\",\n \"singularVariableName\": \"volume\",\n \"pluralVariableName\": \"volumes\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AAOlivia/@Exchange/Output/Volumes/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAOlivia/@Exchange/Output/Volumes/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"404b8d12-db14-462c-b4e4-6ee5093560c3","savedPayload":{"position":{"x":11432.471822707479,"y":18888.618569746668},"targetPosition":{"x":11497.069386776759,"y":18603.13841438197},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AAOlivia/@Exchange/Output/Volumes/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"8f6d7c41-f829-4157-8365-707a4789138e","savedPayload":{"position":{"x":11378.01863134839,"y":18870.53078903516},"targetPosition":{"x":11497.069386776759,"y":18603.13841438197},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Buy","code":"{\n \"codeName\": \"buy\",\n \"isString\": false\n}","id":"4a510fc5-f837-4cca-9839-c6444d0db145","savedPayload":{"position":{"x":11348.500490566892,"y":18994.23055160658},"targetPosition":{"x":11243.676047245594,"y":18961.681696790667},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Sell","code":"{\n \"codeName\": \"sell\",\n \"isString\": false\n}","id":"cb2d0dbb-d62d-4233-8182-8d12b0912e68","savedPayload":{"position":{"x":11245.112781187152,"y":19071.43381596396},"targetPosition":{"x":11243.676047245594,"y":18961.681696790667},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"de34f0c3-a6c9-4bcb-b787-9bf77b413526","savedPayload":{"position":{"x":11139.739554332968,"y":18996.96338196118},"targetPosition":{"x":11243.676047245594,"y":18961.681696790667},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"c2abb280-6367-4e21-80df-14024349a62b","savedPayload":{"position":{"x":11178.00302801257,"y":18873.734858233125},"targetPosition":{"x":11243.676047245594,"y":18961.681696790667},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"4a64f87c-eb3a-47f2-804d-99dc3559a995","savedPayload":{"position":{"x":11243.676047245594,"y":18961.681696790667},"targetPosition":{"x":11497.069386776759,"y":18603.13841438197},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"id":"b2d5ba69-67ac-490f-9e74-28239b35a92e","savedPayload":{"position":{"x":11497.069386776759,"y":18603.13841438197},"targetPosition":{"x":11616.120142205127,"y":18335.74603972878},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Volumes","id":"a6177fb7-51d1-4b7c-bcbb-04861a5bf0c1"}}}],"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781","savedPayload":{"position":{"x":11616.120142205127,"y":18335.74603972878},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Indicator Bot","name":"Tom","code":"{\n\"codeName\": \"AATom\",\n\"repo\": \"AATom-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Produces for each market two files with candles stairs and volumes stairs respectively.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"dc5b175b-eef7-44b1-a863-7de93d17cf3a","savedPayload":{"position":{"x":11586.16466980889,"y":18755.53207619984},"targetPosition":{"x":11866.463885198784,"y":18639.42833966281},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"8fc77cc9-3f73-4a14-b5b0-8cc86d0a18d1"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"47cd9826-4b62-4108-ae8f-9aa3912f91d7","savedPayload":{"position":{"x":11555.00614770378,"y":18639.42833966281},"targetPosition":{"x":11866.463885198784,"y":18639.42833966281},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"0d84339c-22b1-4b26-978c-729a65eacf8c"}}}],"id":"8714647d-617e-4ac3-a74b-00056295c54d","savedPayload":{"position":{"x":11866.463885198784,"y":18639.42833966281},"targetPosition":{"x":11567.015781368527,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"10b6d300-4fa3-467c-b3e9-3bcb9e732fec","savedPayload":{"position":{"x":11573.859539648865,"y":18829.458934793194},"targetPosition":{"x":11848.114558368412,"y":18715.85878649067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559"}}},{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"ef7219af-7454-4a57-97a0-19bc098ad645","savedPayload":{"position":{"x":11541.452873934562,"y":18756.231563160745},"targetPosition":{"x":11848.114558368412,"y":18715.85878649067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"8e9083d9-2a83-4591-af42-945fefb62a34"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"92e8f5d6-c2ff-4591-8128-842c1776983a","savedPayload":{"position":{"x":11564.256185329417,"y":18678.48812271899},"targetPosition":{"x":11848.114558368412,"y":18715.85878649067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}}],"id":"955136c6-aab5-4118-a414-ac3bf24a3d33","savedPayload":{"position":{"x":11848.114558368412,"y":18715.85878649067},"targetPosition":{"x":11567.015781368527,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"8e9083d9-2a83-4591-af42-945fefb62a34","savedPayload":{"position":{"x":11810.60881442319,"y":18784.935773748002},"targetPosition":{"x":11567.015781368527,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"eced2d21-5c6f-4e3d-986c-131d4c2a9768","savedPayload":{"position":{"x":11286.34675896104,"y":18563.50146967753},"targetPosition":{"x":11567.015781368527,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"16259ff0-c3a4-4775-a95e-b8d1973dba74","savedPayload":{"position":{"x":11689.483187158754,"y":18883.021372717474},"targetPosition":{"x":11567.015781368527,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"2924712f-7d80-4f36-8822-71a12cb485f6","savedPayload":{"position":{"x":11567.015781368527,"y":18607.955075707974},"targetPosition":{"x":11323.422748313864,"y":18430.974377667946},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Produces for each market two files with candles stairs and volumes stairs respectively at each day of the market history.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"0aa6c915-996c-435b-9832-131bfdca2a70","savedPayload":{"position":{"x":11316.426117148001,"y":19052.93108821861},"targetPosition":{"x":11640.22609205594,"y":18918.808747115676},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"b15413b6-1175-452a-aa67-08e12d613041"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"45ea1cb9-4d50-4652-abd3-37c5d49f7428","savedPayload":{"position":{"x":11346.317626051816,"y":18918.808747115676},"targetPosition":{"x":11640.22609205594,"y":18918.808747115676},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"63d69b29-5bca-43f6-be0e-67a50fb23827"}}}],"id":"f8c053de-33af-4986-be52-3e55a7155b74","savedPayload":{"position":{"x":11640.22609205594,"y":18918.808747115676},"targetPosition":{"x":11416.46700751807,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":"{\n \"mainUtility\": \"Market Starting Point\"\n}","id":"cc29c13b-dce7-4e7b-810a-fc709bc16adf","savedPayload":{"position":{"x":11298.740931454016,"y":19086.54721693908},"targetPosition":{"x":11580.456485343288,"y":18969.856813786697},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"5fd2b4e4-4826-49f5-b6bc-f9b7b6dc6eb7","savedPayload":{"position":{"x":11293.535016927844,"y":19007.630741715035},"targetPosition":{"x":11580.456485343288,"y":18969.856813786697},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}},{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"0ddf959e-3b96-4528-b180-4577e69dd6d4","savedPayload":{"position":{"x":11281.724396467453,"y":18930.527988176695},"targetPosition":{"x":11580.456485343288,"y":18969.856813786697},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"5bde8446-e23e-4384-a028-b6913bf7109c"}}}],"dataDependencies":[],"id":"98aa9f6a-a680-443e-a052-96a23e52b357","savedPayload":{"position":{"x":11580.456485343288,"y":18969.856813786697},"targetPosition":{"x":11416.46700751807,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"5bde8446-e23e-4384-a028-b6913bf7109c","savedPayload":{"position":{"x":11509.511266722275,"y":19003.695947230844},"targetPosition":{"x":11416.46700751807,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"4838b38c-df75-44dc-8d3b-9a0c4df26911","savedPayload":{"position":{"x":11120.189796996876,"y":18670.40946228008},"targetPosition":{"x":11416.46700751807,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63c476bb-a46a-4417-8549-b8ba98c5922e","savedPayload":{"position":{"x":11353.865307274491,"y":19011.85300631988},"targetPosition":{"x":11416.46700751807,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"80c87e44-7b3a-449d-9bf4-d14502bfa837","savedPayload":{"position":{"x":11416.46700751807,"y":18717.335162449395},"targetPosition":{"x":11323.422748313864,"y":18430.974377667946},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Candle-Stairs","code":"{\n \"codeName\": \"Candle-Stairs\",\n \"singularVariableName\": \"candleStair\",\n \"pluralVariableName\": \"candleStairs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AATom/@Exchange/Output/Candle-Stairs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"8fc77cc9-3f73-4a14-b5b0-8cc86d0a18d1","savedPayload":{"position":{"x":11254.002330428144,"y":19017.504526896977},"targetPosition":{"x":11230.378489109658,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AATom/@Exchange/Output/Candle-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AATom/@Exchange/Output/Candle-Stairs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"b15413b6-1175-452a-aa67-08e12d613041","savedPayload":{"position":{"x":11137.334229905453,"y":19003.695947230844},"targetPosition":{"x":11230.378489109658,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Open","code":"{\n \"codeName\": \"open\",\n \"isString\": false\n}","id":"0b931c5d-1de2-4122-9215-844a4db0415d","savedPayload":{"position":{"x":10979.058955081266,"y":19119.320167022644},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Close","code":"{\n \"codeName\": \"close\",\n \"isString\": false\n}","id":"a96e4cc4-c543-4d0e-9ad5-6f1912e32661","savedPayload":{"position":{"x":10989.039499055172,"y":19190.682985217856},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Min","code":"{\n \"codeName\": \"min\",\n \"isString\": false\n}","id":"f6cf2806-5925-43f3-83a6-1bd037916b1f","savedPayload":{"position":{"x":10964.712876618501,"y":19258.509812650682},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Max","code":"{\n \"codeName\": \"max\",\n \"isString\": false\n}","id":"bb402063-7e84-4041-848f-2c9f19b2f163","savedPayload":{"position":{"x":10911.652023803914,"y":19307.262340558133},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"310a32cb-6610-400d-9976-438720232ded","savedPayload":{"position":{"x":10842.012542538636,"y":19325.771952328272},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"e9b94bbc-9b6d-4ba2-81d7-3c4f853d0283","savedPayload":{"position":{"x":10771.747998733814,"y":19309.79831896956},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Direction","code":"{\n \"codeName\": \"direction\",\n \"isString\": true\n}","id":"3861f1de-12c6-4e6c-988a-e084ad9072db","savedPayload":{"position":{"x":10716.955152595587,"y":19263.000807381326},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Candle Count","code":"{\n \"codeName\": \"candleCount\",\n \"isString\": false\n}","id":"27eec18a-7158-4130-a0a4-808f2b18a184","savedPayload":{"position":{"x":10690.186384848841,"y":19196.100163497285},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"First Min","code":"{\n \"codeName\": \"firstMin\",\n \"isString\": false\n}","id":"47e5d2f3-fa36-4131-9871-d8bcd18c1256","savedPayload":{"position":{"x":10697.574097585735,"y":19124.422518591113},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"First Max","code":"{\n \"codeName\": \"firstMax\",\n \"isString\": false\n}","id":"ff6e9655-245a-40c7-85c9-416172d7467a","savedPayload":{"position":{"x":10737.425854849838,"y":19064.388357301552},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Last Min","code":"{\n \"codeName\": \"lastMin\",\n \"isString\": false\n}","id":"6f671d69-4565-45c8-bf9e-7e08dcdef842","savedPayload":{"position":{"x":10800.61209931768,"y":19029.750782489962},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Last Max","code":"{\n \"codeName\": \"lastMax\",\n \"isString\": false\n}","id":"55c3ed69-91e6-4302-bc5b-0aa566a69d13","savedPayload":{"position":{"x":10872.657623858471,"y":19028.444845117654},"targetPosition":{"x":10839.284059686928,"y":19175.24790544803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"e7377c71-41cf-461e-b02c-39d6071cf6fe","savedPayload":{"position":{"x":10839.284059686928,"y":19175.24790544803},"targetPosition":{"x":11230.378489109658,"y":18717.335162449395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":5},"uiObject":{}}},"id":"642474e9-da61-4168-97dc-7f7869bebc02","savedPayload":{"position":{"x":11230.378489109658,"y":18717.335162449395},"targetPosition":{"x":11323.422748313864,"y":18430.974377667946},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Candles Patterns","id":"a41043f2-f2a5-4c8f-b372-d8eda1ca58df"}}},{"type":"Product Definition","name":"Volume-Stairs","code":"{\n \"codeName\": \"Volume-Stairs\",\n \"singularVariableName\": \"volumeStair\",\n \"pluralVariableName\": \"volumeStairs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AATom/@Exchange/Output/Volume-Stairs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"0d84339c-22b1-4b26-978c-729a65eacf8c","savedPayload":{"position":{"x":10922.506678745978,"y":18864.682938266298},"targetPosition":{"x":11079.8297152592,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AATom/@Exchange/Output/Volume-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AATom/@Exchange/Output/Volume-Stairs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"63d69b29-5bca-43f6-be0e-67a50fb23827","savedPayload":{"position":{"x":10836.236682204537,"y":18784.935773748002},"targetPosition":{"x":11079.8297152592,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"New Record Property","code":"{\n \"codeName\": \"type\",\n \"isString\": true\n}","id":"16846a74-8f33-492a-9c4e-9c0249ad0325","savedPayload":{"position":{"x":10622.637249558607,"y":18827.196247122905},"targetPosition":{"x":10494.273318279445,"y":18748.534728866296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"0223719c-db20-45db-9e65-000acf7bcc12","savedPayload":{"position":{"x":10529.418231569027,"y":18894.923828111234},"targetPosition":{"x":10494.273318279445,"y":18748.534728866296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"c28009dd-8548-415b-8150-e19f086fff46","savedPayload":{"position":{"x":10415.611800022834,"y":18876.89866014546},"targetPosition":{"x":10494.273318279445,"y":18748.534728866296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Direction","code":"{\n \"codeName\": \"direction\",\n \"isString\": true\n}","id":"8e2fa372-284e-4a46-aab9-26e53899594c","savedPayload":{"position":{"x":10347.884219034508,"y":18783.679642155876},"targetPosition":{"x":10494.273318279445,"y":18748.534728866296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Bars Count","code":"{\n \"codeName\": \"barsCount\",\n \"isString\": false\n}","id":"aae7b07a-5726-4370-b86f-0b676c5cb929","savedPayload":{"position":{"x":10365.909387000283,"y":18669.873210609687},"targetPosition":{"x":10494.273318279445,"y":18748.534728866296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"First Amount","code":"{\n \"codeName\": \"firstAmount\",\n \"isString\": false\n}","id":"cfbcb334-5dce-4186-9d91-889a6eaa21fa","savedPayload":{"position":{"x":10459.128404989864,"y":18602.14562962136},"targetPosition":{"x":10494.273318279445,"y":18748.534728866296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Record Property","name":"Last Amount","code":"{\n \"codeName\": \"lastAmount\",\n \"isString\": false\n}","id":"707911c8-e786-4d6b-a13a-8966263c8df2","savedPayload":{"position":{"x":10572.934836536057,"y":18620.170797587132},"targetPosition":{"x":10494.273318279445,"y":18748.534728866296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"af6daf93-5959-410e-ad1f-75e0e5475be8","savedPayload":{"position":{"x":10494.273318279445,"y":18748.534728866296},"targetPosition":{"x":11079.8297152592,"y":18607.955075707974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":5},"uiObject":{}}},"id":"846e9572-c4d6-4c7e-942e-c82402c5ecea","savedPayload":{"position":{"x":11079.8297152592,"y":18607.955075707974},"targetPosition":{"x":11323.422748313864,"y":18430.974377667946},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Volume Patterns","id":"cfd94d21-cb45-446b-9764-cc67cca712b2"}}}],"id":"841b4675-d110-4269-abb9-d53f9124e5c1","savedPayload":{"position":{"x":11323.422748313864,"y":18430.974377667946},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"P"}}},{"type":"Indicator Bot","name":"Chris","code":"{\n\"codeName\": \"AAChris\",\n\"repo\": \"AAChris-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Produces a single file for each time frame covering all the market.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"b8c1f684-7327-488b-8c76-4a73d811e312","savedPayload":{"position":{"x":11239.02420778522,"y":19035.46610318864},"targetPosition":{"x":11427.286286255528,"y":18957.48539700569},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"fa1205c0-3b6e-4064-aca4-ffdaba7e1bd4","savedPayload":{"position":{"x":11195.716653640746,"y":18957.48539700569},"targetPosition":{"x":11427.286286255528,"y":18957.48539700569},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"207e9481-0730-4304-a938-cfe223d70856"}}}],"id":"72887b4d-8868-4f0c-a051-81b83c366c99","savedPayload":{"position":{"x":11427.286286255528,"y":18957.48539700569},"targetPosition":{"x":11147.730121970159,"y":18612.262284637844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"603f6167-ef54-4464-9b7b-5ff7f59c24e7","savedPayload":{"position":{"x":10965.80567458911,"y":19162.517508930803},"targetPosition":{"x":11379.83398619253,"y":18991.02136705823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}},{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"79c51f8f-95eb-451c-98ac-cce4028e23e2","savedPayload":{"position":{"x":10937.342486131716,"y":19049.27647820243},"targetPosition":{"x":11379.83398619253,"y":18991.02136705823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"25d943b0-45aa-45b3-b8b1-f83d5a8b636f","savedPayload":{"position":{"x":10946.860905153555,"y":18934.01937955134},"targetPosition":{"x":11379.83398619253,"y":18991.02136705823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}}],"id":"bd55a584-c9dd-4f2e-836c-a34a16ef01dc","savedPayload":{"position":{"x":11379.83398619253,"y":18991.02136705823},"targetPosition":{"x":11147.730121970159,"y":18612.262284637844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c","savedPayload":{"position":{"x":11328.41032466134,"y":19018.076664198703},"targetPosition":{"x":11147.730121970159,"y":18612.262284637844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"a2cf5c7f-ffbf-4c38-9640-699fd3a37454","savedPayload":{"position":{"x":10707.077023796246,"y":18577.582133694},"targetPosition":{"x":11147.730121970159,"y":18612.262284637844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67","savedPayload":{"position":{"x":11217.2213058419,"y":19051.012352064616},"targetPosition":{"x":11147.730121970159,"y":18612.262284637844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":4},"uiObject":{}}},"id":"941a0543-0cc0-4a5d-a782-728d2835c100","savedPayload":{"position":{"x":11147.730121970159,"y":18612.262284637844},"targetPosition":{"x":11027.27665350937,"y":18341.719364930603},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Produces a Daily File at each time frame.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"1f8cdcbe-c7dc-49fd-b106-e59bb4fb4ad9","savedPayload":{"position":{"x":11557.926727683633,"y":18539.87978091794},"targetPosition":{"x":11247.356091448668,"y":18539.87978091794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"506104fa-e99a-4a46-930e-176bce311aff"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"6362cf0d-c71e-4d08-8dea-c9b8d5d0e38b","savedPayload":{"position":{"x":10961.96021292558,"y":18539.87978091794},"targetPosition":{"x":11247.356091448668,"y":18539.87978091794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"e60d62dd-87f7-4f2d-ac21-3ac5c30a50ea"}}}],"id":"3ab24229-73fe-439a-9684-986aac94f220","savedPayload":{"position":{"x":11247.356091448668,"y":18539.87978091794},"targetPosition":{"x":10965.704418209492,"y":18631.393957030305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":"{\n \"mainUtility\": \"Market Starting Point\"\n}","id":"aee6dca7-b8e8-4a33-9bc6-37830080a3a2","savedPayload":{"position":{"x":11492.336709261624,"y":18829.554373017643},"targetPosition":{"x":11185.78385614879,"y":18829.554373017643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"40701c78-7ace-40a8-a943-7b76d73147ce","savedPayload":{"position":{"x":11185.78385614879,"y":19136.631524923854},"targetPosition":{"x":11185.78385614879,"y":18829.554373017643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}},{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"442d1d63-f28a-4bff-8dda-9bab7a5ad708","savedPayload":{"position":{"x":10907.814984414546,"y":18829.554373017643},"targetPosition":{"x":11185.78385614879,"y":18829.554373017643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"7323fef7-870a-48b6-b96f-586111253b40"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"603dbf64-0ee0-45b6-a1b8-14065b398893","savedPayload":{"position":{"x":11185.78385614879,"y":18545.620358223783},"targetPosition":{"x":11185.78385614879,"y":18829.554373017643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}}}],"id":"312620aa-b556-43c4-9603-6a80acb7ab00","savedPayload":{"position":{"x":11185.78385614879,"y":18829.554373017643},"targetPosition":{"x":10965.704418209492,"y":18631.393957030305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"7323fef7-870a-48b6-b96f-586111253b40","savedPayload":{"position":{"x":10904.132182909614,"y":18921.068549130006},"targetPosition":{"x":10965.704418209492,"y":18631.393957030305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"7ec89351-b852-416a-961a-7e66717c6473","savedPayload":{"position":{"x":10706.552308321918,"y":18443.108927974663},"targetPosition":{"x":10965.704418209492,"y":18631.393957030305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"acebc3ed-4f3f-4a4b-b403-f16cbe987dde","savedPayload":{"position":{"x":10745.624980270195,"y":18433.233541042966},"targetPosition":{"x":10965.704418209492,"y":18631.393957030305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca","savedPayload":{"position":{"x":10965.704418209492,"y":18631.393957030305},"targetPosition":{"x":11027.27665350937,"y":18341.719364930603},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":2},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Bollinger Bands","code":"{\n\"codeName\": \"Bollinger-Bands\",\n\"singularVariableName\": \"bollingerBand\",\n\"pluralVariableName\": \"bollingerBands\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AAChris/@Exchange/Output/Bollinger-Bands/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719","savedPayload":{"position":{"x":11088.848888809249,"y":18631.393957030305},"targetPosition":{"x":10807.197215570073,"y":18539.87978091794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AAChris/@Exchange/Output/Bollinger-Bands/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAChris/@Exchange/Output/Bollinger-Bands/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"506104fa-e99a-4a46-930e-176bce311aff","savedPayload":{"position":{"x":10868.769450869951,"y":18829.554373017643},"targetPosition":{"x":10807.197215570073,"y":18539.87978091794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"1e7186ee-c481-401d-827f-a30ee7434d92","savedPayload":{"position":{"x":10882.85801450477,"y":18753.539285964485},"targetPosition":{"x":10587.117777630776,"y":18738.04019690528},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"15f90f2e-80f0-4575-94d8-6d8957eceee1","savedPayload":{"position":{"x":10785.278193618115,"y":18958.119634844577},"targetPosition":{"x":10587.117777630776,"y":18738.04019690528},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Moving Average","code":"{\n \"codeName\": \"movingAverage\",\n \"isString\": false\n}","id":"9786a9cd-1eff-4b5c-ae7c-1dde51659f82","savedPayload":{"position":{"x":10571.618688571569,"y":19033.780433779277},"targetPosition":{"x":10587.117777630776,"y":18738.04019690528},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Standard Deviation","code":"{\n \"codeName\": \"standardDeviation\",\n \"isString\": false\n}","id":"e92d7855-bb18-494e-ab37-a895bbcb8287","savedPayload":{"position":{"x":10367.038339691479,"y":18936.20061289262},"targetPosition":{"x":10587.117777630776,"y":18738.04019690528},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Deviation","code":"{\n \"codeName\": \"deviation\",\n \"isString\": false\n}","id":"03043530-caa1-416e-8303-840b732e516e","savedPayload":{"position":{"x":10291.377540756781,"y":18722.541107846075},"targetPosition":{"x":10587.117777630776,"y":18738.04019690528},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Direction","code":"{\n \"codeName\": \"direction\",\n \"isString\": true,\n \"isCalculated\": true\n}","formula":{"type":"Formula","code":"variable.direction","id":"f72926d9-c869-4b7c-ba1e-b36d134e2bc6","savedPayload":{"position":{"x":10190.796945656099,"y":18297.881321026685},"targetPosition":{"x":10388.957361643437,"y":18517.960758965983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"856f24c9-f5f1-491b-98e7-6e1d3c5c5874","savedPayload":{"position":{"x":10388.957361643437,"y":18517.960758965983},"targetPosition":{"x":10587.117777630776,"y":18738.04019690528},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Slope","code":"{\n \"codeName\": \"slope\",\n \"isString\": true,\n \"isCalculated\": true\n}","formula":{"type":"Formula","code":"variable.slope","id":"fac935fe-b448-4b34-aed7-b35f70808911","savedPayload":{"position":{"x":10618.11595574919,"y":18146.559723157286},"targetPosition":{"x":10602.616866689983,"y":18442.299960031283},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4c027b0c-da8a-4146-a51a-d0a7a126150b","savedPayload":{"position":{"x":10602.616866689983,"y":18442.299960031283},"targetPosition":{"x":10587.117777630776,"y":18738.04019690528},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b5a5ec97-7246-42bd-97e4-28fb4a148b2c","savedPayload":{"position":{"x":10587.117777630776,"y":18738.04019690528},"targetPosition":{"x":10807.197215570073,"y":18539.87978091794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"variable.lastMovingAverage = 0\nvariable.SIDE_TOLERANCE = 0.5 * system.timeFrame / system.ONE_DAY_IN_MILISECONDS\nvariable.SMALL_SLOPE = 1.0 * system.timeFrame / system.ONE_DAY_IN_MILISECONDS\nvariable.MEDIUM_SLOPE = 2.0 * system.timeFrame / system.ONE_DAY_IN_MILISECONDS\nvariable.HIGH_SLOPE = 4.0 * system.timeFrame / system.ONE_DAY_IN_MILISECONDS\n","id":"f00ac417-cd99-493c-b959-35eddd6fe4f3","savedPayload":{"position":{"x":10085.386666452303,"y":18844.686436780255},"targetPosition":{"x":10305.4661043916,"y":18646.526020792917},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d144ab2f-5b4e-4153-806a-d471e9f281dd","savedPayload":{"position":{"x":10305.4661043916,"y":18646.526020792917},"targetPosition":{"x":10525.545542330898,"y":18448.36560480558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"\nif (variable.lastMovingAverage > product.bollingerBand.movingAverage) { variable.direction = \"Down\" }\nif (variable.lastMovingAverage < product.bollingerBand.movingAverage) { variable.direction = \"Up\" }\nif (variable.lastMovingAverage === product.bollingerBand.movingAverage) { variable.direction = \"Side\" }\n\nvariable.delta = Math.abs(product.bollingerBand.movingAverage - variable.lastMovingAverage)\n\nvariable.slope = \"Extreme\"\nif (variable.delta < product.bollingerBand.movingAverage * variable.HIGH_SLOPE / 100) { variable.slope = \"Steep\" }\nif (variable.delta < product.bollingerBand.movingAverage * variable.MEDIUM_SLOPE / 100) { variable.slope = \"Medium\" }\nif (variable.delta < product.bollingerBand.movingAverage * variable.SMALL_SLOPE / 100) { variable.slope = \"Gentle\" }\nif (variable.delta < product.bollingerBand.movingAverage * variable.SIDE_TOLERANCE / 100) { variable.slope = \"Side\" }\n\nvariable.lastMovingAverage = product.bollingerBand.movingAverage","id":"14fc0c9c-cd10-4931-95b5-4bc7d549942a","savedPayload":{"position":{"x":10402.401071731141,"y":17869.016420606175},"targetPosition":{"x":10463.97330703102,"y":18158.691012705876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8a669548-845b-4d80-ae96-29f68e9c9ac7","savedPayload":{"position":{"x":10463.97330703102,"y":18158.691012705876},"targetPosition":{"x":10525.545542330898,"y":18448.36560480558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d72c7cb2-8886-4101-a83a-f9788b8400a0","savedPayload":{"position":{"x":10525.545542330898,"y":18448.36560480558},"targetPosition":{"x":10807.197215570073,"y":18539.87978091794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"139484b3-3a3f-4268-801e-a8c441746c7a","savedPayload":{"position":{"x":10745.624980270195,"y":18250.20518881824},"targetPosition":{"x":10807.197215570073,"y":18539.87978091794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"29088877-8d63-4217-8d34-8742afc262f4","savedPayload":{"position":{"x":10807.197215570073,"y":18539.87978091794},"targetPosition":{"x":11027.27665350937,"y":18341.719364930603},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Bollinger Bands","id":"a11bfdf8-595c-4b50-b259-6a9f777d0ea8"}}},{"type":"Product Definition","name":"Percentage Bandwidth","code":"{\n \"codeName\": \"Percentage-Bandwidth\",\n \"singularVariableName\": \"percentageBandwidth\",\n \"pluralVariableName\": \"percentageBandwidths\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"AAMasters/AAChris/@Exchange/Output/Percentage-Bandwidth/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"207e9481-0730-4304-a938-cfe223d70856","savedPayload":{"position":{"x":10763.708574191123,"y":18667.19883663411},"targetPosition":{"x":10732.752877998493,"y":18372.675061123235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"AAMasters/AAChris/@Exchange/Output/Percentage-Bandwidth/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAChris/@Exchange/Output/Percentage-Bandwidth/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"e60d62dd-87f7-4f2d-ac21-3ac5c30a50ea","savedPayload":{"position":{"x":10438.229102487616,"y":18403.630757315867},"targetPosition":{"x":10732.752877998493,"y":18372.675061123235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"7c793ff3-0356-4f05-b471-4510e9cdaf58","savedPayload":{"position":{"x":10462.209958291254,"y":18252.22159266245},"targetPosition":{"x":10701.797181805863,"y":18078.15128561236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"b02c38c3-216c-4a83-903d-d50a599b55dc","savedPayload":{"position":{"x":10431.254262098622,"y":17957.697817151573},"targetPosition":{"x":10701.797181805863,"y":18078.15128561236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Value","code":"{\n \"codeName\": \"value\",\n \"isString\": false\n}","id":"2c279136-20b1-4f9c-9d35-6a4fd36a4a99","savedPayload":{"position":{"x":10670.841485613233,"y":17783.627510101483},"targetPosition":{"x":10701.797181805863,"y":18078.15128561236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Moving Average","code":"{\n \"codeName\": \"movingAverage\",\n \"isString\": false\n}","id":"a9e3bbb1-9eaf-4ab1-93bb-ee27357daa76","savedPayload":{"position":{"x":10941.384405320472,"y":17904.08097856227},"targetPosition":{"x":10701.797181805863,"y":18078.15128561236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Bandwidth","code":"{\n \"codeName\": \"bandwidth\",\n \"isString\": false\n}","id":"90d9694f-ed29-4b2e-8c75-c4bdcf35e36a","savedPayload":{"position":{"x":10972.340101513104,"y":18198.604754073145},"targetPosition":{"x":10701.797181805863,"y":18078.15128561236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"d04d8ca7-18d0-4360-babf-7c49c3c97f50","savedPayload":{"position":{"x":10701.797181805863,"y":18078.15128561236},"targetPosition":{"x":10732.752877998493,"y":18372.675061123235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068","savedPayload":{"position":{"x":10732.752877998493,"y":18372.675061123235},"targetPosition":{"x":11027.27665350937,"y":18341.719364930603},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":2},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Percentage B","id":"06b2f3f6-b138-415f-8974-31c22a930f92"}}}],"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23","savedPayload":{"position":{"x":11027.27665350937,"y":18341.719364930603},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"c"}}},{"type":"Indicator Bot","name":"Paula","code":"{\n\"codeName\": \"AAPaula\",\n\"repo\": \"AAPaula-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Produces for each market two files with bollinger channels respectively.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Bollinger Channels Objects","id":"d804e799-7c12-42f5-8de2-52a3a7d5daa4","savedPayload":{"position":{"x":11799.751708699006,"y":17764.310722823226},"targetPosition":{"x":11279.451714955725,"y":17764.310722823226},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Market","id":"610cd062-ab88-4892-b711-0fb430305c61"}}},{"type":"Output Dataset","name":"Bollinger Channels","id":"e524685d-8da0-41f6-839d-9a4100e6e3fe","savedPayload":{"position":{"x":11279.451714955725,"y":18286.02555118803},"targetPosition":{"x":11279.451714955725,"y":17764.310722823226},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Market","id":"22c1f2c6-3838-4657-add8-2e01d7056917"}}},{"type":"Output Dataset","name":"Bollinger Sub-Channels Objects","id":"eb0e7947-95e4-4f4a-87ea-1fb430b8b503","savedPayload":{"position":{"x":10746.55099033864,"y":17764.310722823226},"targetPosition":{"x":11279.451714955725,"y":17764.310722823226},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Market","id":"869f615e-0cc0-44fd-8648-4a951c23a410"}}},{"type":"Output Dataset","name":"Bollinger Sub-Channels Objects","id":"dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1","savedPayload":{"position":{"x":11279.451714955725,"y":17248.93567222272},"targetPosition":{"x":11279.451714955725,"y":17764.310722823226},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Market","id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}}],"id":"dd39a54a-3714-446a-9a41-dfd83557664c","savedPayload":{"position":{"x":11279.451714955725,"y":17764.310722823226},"targetPosition":{"x":11357.301964528056,"y":18280.79998446012},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"ae627a69-4089-432d-b379-1b12ce4bd75e","savedPayload":{"position":{"x":12295.709373131394,"y":17955.146594597045},"targetPosition":{"x":11765.693586210033,"y":17955.146594597045},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"af8aded9-002a-475a-88f5-d29573740969"}}},{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"4a603114-d464-46db-90a1-62304274117a","savedPayload":{"position":{"x":11507.382108956988,"y":18402.55519737749},"targetPosition":{"x":11765.693586210033,"y":17955.146594597045},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Chris Bollinger Bands","id":"ea27be54-93a3-48e3-8c52-72bc397b6c66","savedPayload":{"position":{"x":11510.898078964163,"y":17513.827830506914},"targetPosition":{"x":11765.693586210033,"y":17955.146594597045},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}}],"id":"8b509294-6028-40e2-88a8-655a34245ed8","savedPayload":{"position":{"x":11765.693586210033,"y":17955.146594597045},"targetPosition":{"x":11357.301964528056,"y":18280.79998446012},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"af8aded9-002a-475a-88f5-d29573740969","savedPayload":{"position":{"x":11843.545808927709,"y":18471.66234337387},"targetPosition":{"x":11357.301964528056,"y":18280.79998446012},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"f4720e04-5def-4c0b-be42-33e50ac6a6b7","savedPayload":{"position":{"x":10932.299340287775,"y":17972.017503435676},"targetPosition":{"x":11357.301964528056,"y":18280.79998446012},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"671530dc-5ba5-4c8d-bc23-f228e22c56b3","savedPayload":{"position":{"x":10948.914289136766,"y":18606.506348603052},"targetPosition":{"x":11357.301964528056,"y":18280.79998446012},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","savedPayload":{"position":{"x":11357.301964528056,"y":18280.79998446012},"targetPosition":{"x":10871.086550697519,"y":18089.948868648757},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Produces for each market two files with bollinger channels respectively at each day of the market history.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Daily\",\n \"startDate\": {\n \"takeItFromStatusDependency\": 0\n },\n \"endDate\": {\n \"takeItFromStatusDependency\": 1\n }\n }\n }","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"64430a93-38c2-4c71-8085-71661bbf804e","savedPayload":{"position":{"x":11905.314899841907,"y":18206.24248453912},"targetPosition":{"x":11380.296140369866,"y":18206.24248453912},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Daily","id":"64910fea-c92a-4a55-850a-aa62d98b478e"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"b536a078-e32a-4489-93f6-75107a90b527","savedPayload":{"position":{"x":11380.296140369866,"y":18718.072230734},"targetPosition":{"x":11380.296140369866,"y":18206.24248453912},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Daily","id":"ca71e765-0f66-4b18-b500-847b9d16169b"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"d5bcc7c4-2798-4c34-9504-cc4617b64a04","savedPayload":{"position":{"x":10864.645032090311,"y":18206.24248453912},"targetPosition":{"x":11380.296140369866,"y":18206.24248453912},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Daily","id":"029479ae-275d-4854-ad3e-66948656a053"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"57898dc1-83a2-4ee9-8a0b-6674f988f0fa","savedPayload":{"position":{"x":11380.296140369866,"y":17700.196450476018},"targetPosition":{"x":11380.296140369866,"y":18206.24248453912},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Daily","id":"96b4e65b-e6db-44a0-80a5-ceb9d68a09ec"}}}],"id":"b8fc5ac0-021d-4230-aa59-9876c5f0f3e1","savedPayload":{"position":{"x":11380.296140369866,"y":18206.24248453912},"targetPosition":{"x":11025.02544870051,"y":18589.107535606883},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Ending Point","code":"{\n \"mainUtility\": \"Market Ending Point\"\n}","id":"3cb29ee1-02de-452a-a79f-2451700fe335","savedPayload":{"position":{"x":12054.28516929249,"y":18705.385907459706},"targetPosition":{"x":11534.261495796629,"y":18705.385907459706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"7323fef7-870a-48b6-b96f-586111253b40"}}},{"type":"Status Dependency","name":"Market Starting Point","code":"{\n \"mainUtility\": \"Market Starting Point\"\n}","id":"d5f34469-0ae1-45e6-acd9-4155d5940c77","savedPayload":{"position":{"x":11534.261495796629,"y":19236.132367513415},"targetPosition":{"x":11534.261495796629,"y":18705.385907459706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"7f6266a5-bddb-4d49-962d-100b1993043d","savedPayload":{"position":{"x":10994.653787759424,"y":18705.385907459706},"targetPosition":{"x":11534.261495796629,"y":18705.385907459706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"b1dd07bf-a698-4bbb-995a-356c929a1e94"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"073e24ab-6b47-482a-b117-0d3554f63c1f","savedPayload":{"position":{"x":11534.261495796629,"y":18218.21830059686},"targetPosition":{"x":11534.261495796629,"y":18705.385907459706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"506104fa-e99a-4a46-930e-176bce311aff"}}}],"id":"c0cb34fe-9268-4ebb-a8ce-7d53a13a6d4d","savedPayload":{"position":{"x":11534.261495796629,"y":18705.385907459706},"targetPosition":{"x":11025.02544870051,"y":18589.107535606883},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"b1dd07bf-a698-4bbb-995a-356c929a1e94","savedPayload":{"position":{"x":11178.973289128864,"y":19088.295528022274},"targetPosition":{"x":11025.02544870051,"y":18589.107535606883},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"4548cff9-f388-4f4f-b148-665d22ffaf9e","savedPayload":{"position":{"x":10612.908606609559,"y":18289.68712332054},"targetPosition":{"x":11025.02544870051,"y":18589.107535606883},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"acebc3ed-4f3f-4a4b-b403-f16cbe987dde"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"34e17fad-2c62-4c45-875c-774ac6b6cc99","savedPayload":{"position":{"x":10515.754371607574,"y":18472.918302743674},"targetPosition":{"x":11025.02544870051,"y":18589.107535606883},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bc15b49d-4547-4bac-a28b-193eb0158fe3","savedPayload":{"position":{"x":11025.02544870051,"y":18589.107535606883},"targetPosition":{"x":10871.086550697519,"y":18089.948868648757},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Bollinger Channels Objects","code":"{\n \"codeName\": \"Bollinger-Channels-Objects\",\n \"singularVariableName\": \"bollingerChannelsObject\",\n \"pluralVariableName\": \"bollingerChannelsObjects\"\n}","datasets":[{"type":"Dataset Definition","name":"Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Channels-Objects/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"610cd062-ab88-4892-b711-0fb430305c61","savedPayload":{"position":{"x":11095.439386411685,"y":18608.08566783657},"targetPosition":{"x":10630.458328896633,"y":18471.77447482788},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Channels-Objects/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Channels-Objects/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"64910fea-c92a-4a55-850a-aa62d98b478e","savedPayload":{"position":{"x":10644.578899935435,"y":18956.729851488355},"targetPosition":{"x":10630.458328896633,"y":18471.77447482788},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"06edb099-ff64-4528-b8c6-599154bc2ddf","savedPayload":{"position":{"x":10574.91152188271,"y":18878.945570259544},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"94656d1f-508a-4e94-a8d2-013690aac596","savedPayload":{"position":{"x":10343.876039550149,"y":19151.510999841244},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Direction","code":"{\n \"codeName\": \"direction\",\n \"isString\": true\n}","id":"edc0f697-f5e2-446f-bae8-8db7adf91d23","savedPayload":{"position":{"x":9991.69091118197,"y":19211.80148711732},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Period","code":"{\n \"codeName\": \"period\",\n \"isString\": false\n}","id":"eb733f85-6cf5-485e-9bd3-2bf78733c492","savedPayload":{"position":{"x":9683.147472443356,"y":19031.606443037166},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"First Moving Average","code":"{\n \"codeName\": \"firstMovingAverage\",\n \"isString\": false\n}","id":"a67a15ee-6f7f-48bd-9d55-82c57efc993b","savedPayload":{"position":{"x":9562.616627398465,"y":18695.241131370723},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Last Moving Average","code":"{\n \"codeName\": \"lastMovingAverage\",\n \"isString\": false\n}","id":"0f05bffe-d668-4d25-ab67-d55a4a7c8f5d","savedPayload":{"position":{"x":9686.496097994932,"y":18360.09461973076},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"First Deviation","code":"{\n \"codeName\": \"firstDeviation\",\n \"isString\": false\n}","id":"eae84509-b689-4a76-9277-eed72af491dd","savedPayload":{"position":{"x":9996.821303173709,"y":18182.98568565219},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Last Deviation","code":"{\n \"codeName\": \"lastDeviation\",\n \"isString\": false\n}","id":"d5c32d1f-9e3c-48e8-8025-a17d57c0812e","savedPayload":{"position":{"x":10348.387630551162,"y":18246.78556773693},"targetPosition":{"x":10085.032887747822,"y":18697.79482338733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"6c3e68b0-b781-4c53-9ff7-67c3f747ac8a","savedPayload":{"position":{"x":10173.675024791683,"y":18635.6730256235},"targetPosition":{"x":10630.458328896633,"y":18471.77447482788},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"\n\nif (record.next !== undefined) {\n\n if (\n record.current.direction === record.next.direction) {\n\n if (variable.channel === undefined) {\n\n variable.channel = {\n begin: undefined,\n end: undefined,\n direction: undefined,\n period: 0,\n firstMovingAverage: 0,\n lastMovingAverage: 0,\n firstDeviation: 0,\n lastDeviation: 0\n }\n\n variable.channel.direction = record.current.direction\n variable.channel.period = 2\n\n variable.channel.begin = record.current.begin\n variable.channel.end = record.next.end\n\n variable.channel.firstMovingAverage = record.current.movingAverage\n variable.channel.lastMovingAverage = record.next.movingAverage\n\n variable.channel.firstDeviation = record.current.deviation\n variable.channel.lastDeviation = record.next.deviation\n\n } else {\n\n variable.channel.period++\n variable.channel.end = record.next.end\n variable.channel.lastMovingAverage = record.next.movingAverage\n variable.channel.lastDeviation = record.next.deviation\n\n }\n } else {\n\n if (variable.channel !== undefined) {\n results.push(variable.channel)\n variable.channel = undefined\n } else {\n /* The variable.channel has only one period */\n\n variable.channel = {}\n\n variable.channel.direction = record.current.direction\n variable.channel.period = 1\n\n variable.channel.begin = record.current.begin\n variable.channel.end = record.current.end\n\n variable.channel.firstMovingAverage = record.current.movingAverage\n variable.channel.lastMovingAverage = record.current.movingAverage\n\n variable.channel.firstDeviation = record.current.deviation\n variable.channel.lastDeviation = record.current.deviation\n\n results.push(variable.channel)\n variable.channel = undefined\n }\n }\n} else {\n if (variable.channel !== undefined) {\n variable.channel.period++\n variable.channel.end = record.current.end\n variable.channel.lastMovingAverage = record.current.movingAverage\n variable.channel.lastDeviation = record.current.deviation\n results.push(variable.channel)\n variable.channel = undefined\n } else {\n /* The variable.channel has only one period */\n\n variable.channel = {}\n\n variable.channel.direction = record.current.direction\n variable.channel.period = 1\n\n variable.channel.begin = record.current.begin\n variable.channel.end = record.current.end\n\n variable.channel.firstMovingAverage = record.current.movingAverage\n variable.channel.lastMovingAverage = record.current.movingAverage\n\n variable.channel.firstDeviation = record.current.deviation\n variable.channel.lastDeviation = record.current.deviation\n\n results.push(variable.channel)\n variable.channel = undefined\n }\n}","id":"b81d6adc-e721-4089-b4c3-2fd772f33d25","savedPayload":{"position":{"x":9618.01924900738,"y":17281.951563440198},"targetPosition":{"x":9937.63505377876,"y":17695.175165758246},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a2b92c75-e272-4d05-b098-121a70371450","savedPayload":{"position":{"x":9937.63505377876,"y":17695.175165758246},"targetPosition":{"x":10257.23190986305,"y":18108.374587527487},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"116ccf8c-26bc-4b86-99e6-bb3075527713","savedPayload":{"position":{"x":10333.500910995055,"y":18088.604811267076},"targetPosition":{"x":10630.458328896633,"y":18471.77447482788},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1f111f72-624f-4166-9f99-ab8d785d5267","savedPayload":{"position":{"x":10630.458328896633,"y":18471.77447482788},"targetPosition":{"x":10903.797320980731,"y":18071.066056881453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Plotter Module","name":"Bollinger Channels Objects","id":"52881346-3e4b-45e0-92d2-af130d381b8e"}}},{"type":"Product Definition","name":"Bollinger Channels","code":"{\n \"codeName\": \"Bollinger-Channels\",\n \"singularVariableName\": \"bollingerChannel\",\n \"pluralVariableName\": \"bollingerChannels\"\n}","datasets":[{"type":"Dataset Definition","name":"Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Channels/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"22c1f2c6-3838-4657-add8-2e01d7056917","savedPayload":{"position":{"x":10452.930907953034,"y":18641.725306316785},"targetPosition":{"x":10254.842500307843,"y":18158.392636126617},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Channels/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"96b4e65b-e6db-44a0-80a5-ceb9d68a09ec","savedPayload":{"position":{"x":9856.378499136183,"y":18496.14391604549},"targetPosition":{"x":10254.842500307843,"y":18158.392636126617},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"b243e3c4-e528-4e7b-9644-b03e47c8ca34","savedPayload":{"position":{"x":9562.752469505247,"y":18898.71481371841},"targetPosition":{"x":9686.624807875294,"y":18391.24342059404},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"74268e18-735f-405b-b9db-412ce6cf7438","savedPayload":{"position":{"x":9686.624807875294,"y":18391.24342059404},"targetPosition":{"x":9810.489796645288,"y":17883.80173667009},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"bc705d0f-6df6-45f6-8d3a-44b81bf785bd","savedPayload":{"position":{"x":8768.628297229225,"y":17961.84228672683},"targetPosition":{"x":9289.574365316852,"y":17922.820893677737},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ad08f6a7-5d37-4fd3-9d84-5eebddcd8cb5","savedPayload":{"position":{"x":9289.574365316852,"y":17922.820893677737},"targetPosition":{"x":9810.489796645288,"y":17883.80173667009},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Direction","code":"{\n \"codeName\": \"direction\",\n \"isString\": true\n}","formula":{"type":"Formula","code":"record.current.direction","id":"7b7fd5fc-c820-4c1b-ae3e-400d7b90bb0b","savedPayload":{"position":{"x":9414.249154085248,"y":16917.075785532827},"targetPosition":{"x":9612.375327545395,"y":17400.452961619463},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"31720cda-be3c-453e-8c6e-c4816299c440","savedPayload":{"position":{"x":9612.375327545395,"y":17400.452961619463},"targetPosition":{"x":9810.489796645288,"y":17883.80173667009},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Period","code":"{\n \"codeName\": \"period\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.period","id":"9c44699b-a743-4f46-82f6-0351e8b1a206","savedPayload":{"position":{"x":10607.38895974412,"y":17208.247104479204},"targetPosition":{"x":10208.927736362246,"y":17546.034351890758},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8af98ad4-3062-49a1-bff6-593bd9638f72","savedPayload":{"position":{"x":10208.927736362246,"y":17546.034351890758},"targetPosition":{"x":9810.489796645288,"y":17883.80173667009},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"9106250d-b042-4e96-9121-c760b4c1eab2","savedPayload":{"position":{"x":9810.489796645288,"y":17883.80173667009},"targetPosition":{"x":10254.842500307843,"y":18158.392636126617},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"if (record.previous !== undefined) {\n if (record.current.direction === record.previous.direction) {\n variable.period++\n } else {\n variable.period = 1\n }\n}","id":"8f05a74a-f85c-4eb1-9de1-3363d2e3cb8a","savedPayload":{"position":{"x":9623.570088986067,"y":16928.900949227238},"targetPosition":{"x":10001.136818045286,"y":17289.928533325652},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7e54fecc-8443-4aad-a541-0b6a3fcac877","savedPayload":{"position":{"x":10001.136818045286,"y":17289.928533325652},"targetPosition":{"x":10378.681427623134,"y":17650.934847342207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"variable.period = 1","id":"491600d9-228d-41b8-bedb-22b8ba22a7c9","savedPayload":{"position":{"x":11381.506985049704,"y":17357.904157588633},"targetPosition":{"x":10880.079490916443,"y":17504.423847390564},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3c3de682-9ac0-4bf9-b1ff-97a3e0cb24b4","savedPayload":{"position":{"x":10880.079490916443,"y":17504.423847390564},"targetPosition":{"x":10378.681427623134,"y":17650.934847342207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9ace6abd-5ef9-4c37-9dbc-7aefc8b8b905","savedPayload":{"position":{"x":10378.681427623134,"y":17650.934847342207},"targetPosition":{"x":10254.842500307843,"y":18158.392636126617},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"94bc3958-cc85-43a1-a178-103e84f42910","savedPayload":{"position":{"x":10254.842500307843,"y":18158.392636126617},"targetPosition":{"x":10871.086550697519,"y":18089.948868648757},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","code":"{\n \"codeName\": \"Bollinger-Sub-Channels-Objects\",\n \"singularVariableName\": \"bollingerSubChannelsObject\",\n \"pluralVariableName\": \"bollingerSubChannelsObjects\"\n}","datasets":[{"type":"Dataset Definition","name":"Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Sub-Channels-Objects/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"869f615e-0cc0-44fd-8648-4a951c23a410","savedPayload":{"position":{"x":10337.291322450608,"y":18139.184337063933},"targetPosition":{"x":10573.806140905248,"y":17715.80356307869},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Sub-Channels-Objects/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Sub-Channels-Objects/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"029479ae-275d-4854-ad3e-66948656a053","savedPayload":{"position":{"x":10097.714272923327,"y":17622.047234933732},"targetPosition":{"x":10573.806140905248,"y":17715.80356307869},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"8f0513f5-d78e-4efd-a554-a6ee2a4781ac","savedPayload":{"position":{"x":10198.929140721386,"y":17644.620910607908},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"677f9a11-1fa3-4880-ad4f-81a4490d82e8","savedPayload":{"position":{"x":9979.34895319871,"y":17407.969750480916},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Direction","code":"{\n \"codeName\": \"direction\",\n \"isString\": true\n}","id":"610f6b9f-9404-4603-a42b-80de31498f2f","savedPayload":{"position":{"x":9940.80491172538,"y":17087.448944278185},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Slope","code":"{\n \"codeName\": \"slope\",\n \"isString\": true\n}","id":"65600005-8272-4297-8716-b08841eb5e73","savedPayload":{"position":{"x":10098.019530080419,"y":16805.486545867647},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Period","code":"{\n \"codeName\": \"period\",\n \"isString\": false\n}","id":"ef18438f-7a1f-4432-8fce-494bc9b5ae01","savedPayload":{"position":{"x":10390.942167580548,"y":16669.78260789268},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"First Moving Average","code":"{\n \"codeName\": \"firstMovingAverage\",\n \"isString\": false\n}","id":"e5694d55-735b-4cb2-919e-02a71679c682","savedPayload":{"position":{"x":10707.686332774982,"y":16732.171422252522},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Last Moving Average","code":"{\n \"codeName\": \"lastMovingAverage\",\n \"isString\": false\n}","id":"d49bae2d-b519-49ba-8fd2-9a3a4c2e8dc3","savedPayload":{"position":{"x":10927.266520297657,"y":16968.822582379515},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"First Deviation","code":"{\n \"codeName\": \"firstDeviation\",\n \"isString\": false\n}","id":"a11b4d44-937d-456e-b2bd-9cd025289c34","savedPayload":{"position":{"x":10965.810561770988,"y":17289.343388582245},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Last Deviation","code":"{\n \"codeName\": \"lastDeviation\",\n \"isString\": false\n}","id":"4d1b7a3f-c452-4002-ab8c-bfa7d396ac2c","savedPayload":{"position":{"x":10808.595943415949,"y":17571.305786992783},"targetPosition":{"x":10453.35893452526,"y":17188.43377033526},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a2ff91c2-cfee-487c-959d-fd3ed3dea861","savedPayload":{"position":{"x":10515.507503956112,"y":17234.391767846},"targetPosition":{"x":10573.806140905248,"y":17715.80356307869},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"if (record.next !== undefined) {\n\n if (\n record.current.direction === record.next.direction &&\n record.current.slope === record.next.slope) {\n\n if (variable.channel === undefined) {\n\n variable.channel = {\n begin: undefined,\n end: undefined,\n direction: undefined,\n slope: undefined,\n period: 0,\n firstMovingAverage: 0,\n lastMovingAverage: 0,\n firstDeviation: 0,\n lastDeviation: 0\n }\n\n variable.channel.direction = record.current.direction\n variable.channel.slope = record.current.slope\n variable.channel.period = 2\n\n variable.channel.begin = record.current.begin\n variable.channel.end = record.next.end\n\n variable.channel.firstMovingAverage = record.current.movingAverage\n variable.channel.lastMovingAverage = record.next.movingAverage\n\n variable.channel.firstDeviation = record.current.deviation\n variable.channel.lastDeviation = record.next.deviation\n\n } else {\n\n variable.channel.period++\n variable.channel.end = record.next.end\n variable.channel.lastMovingAverage = record.next.movingAverage\n variable.channel.lastDeviation = record.next.deviation\n\n }\n } else {\n\n if (variable.channel !== undefined) {\n results.push(variable.channel)\n variable.channel = undefined\n } else {\n /* The channel has only one period */\n\n variable.channel = {}\n\n variable.channel.direction = record.current.direction\n variable.channel.slope = record.current.slope\n variable.channel.period = 1\n\n variable.channel.begin = record.current.begin\n variable.channel.end = record.current.end\n\n variable.channel.firstMovingAverage = record.current.movingAverage\n variable.channel.lastMovingAverage = record.current.movingAverage\n\n variable.channel.firstDeviation = record.current.deviation\n variable.channel.lastDeviation = record.current.deviation\n\n results.push(variable.channel)\n variable.channel = undefined\n }\n }\n\n} else {\n if (variable.channel !== undefined) {\n variable.channel.period++\n variable.channel.end = record.current.end\n variable.channel.lastMovingAverage = record.current.movingAverage\n variable.channel.lastDeviation = record.current.deviation\n results.push(variable.channel)\n variable.channel = undefined\n } else {\n /* The channel has only one period */\n\n variable.channel = {}\n\n variable.channel.direction = record.current.direction\n variable.channel.slope = record.current.slope\n variable.channel.period = 1\n\n variable.channel.begin = record.current.begin\n variable.channel.end = record.current.end\n\n variable.channel.firstMovingAverage = record.current.movingAverage\n variable.channel.lastMovingAverage = record.current.movingAverage\n\n variable.channel.firstDeviation = record.current.deviation\n variable.channel.lastDeviation = record.current.deviation\n\n results.push(variable.channel)\n variable.channel = undefined\n }\n}","id":"a1af769c-2a2d-4e78-8f0d-8dfb773982f0","savedPayload":{"position":{"x":11937.589102392605,"y":17048.273501890377},"targetPosition":{"x":11463.622328641588,"y":17267.88009567434},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"128aac86-66b5-4685-a75d-4e60ba697c90","savedPayload":{"position":{"x":11463.622328641588,"y":17267.88009567434},"targetPosition":{"x":10989.68328724237,"y":17487.473744822095},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5bfe184e-396e-431a-a640-76c05d1c6e06","savedPayload":{"position":{"x":11013.29497053129,"y":17511.94461539127},"targetPosition":{"x":10573.806140905248,"y":17715.80356307869},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"080c87fc-4546-4482-ab11-180a33f6d9ab","savedPayload":{"position":{"x":10573.806140905248,"y":17715.80356307869},"targetPosition":{"x":10903.797320980731,"y":18071.066056881453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Bollinger Sub Channels Objects","id":"f8c4b699-1f40-4832-b427-68da3341daa8"}}},{"type":"Product Definition","name":"Bollinger Sub-Channels","code":"{\n \"codeName\": \"Bollinger-Sub-Channels\",\n \"singularVariableName\": \"bollingerSubChannel\",\n \"pluralVariableName\": \"bollingerSubChannels\"\n}","datasets":[{"type":"Dataset Definition","name":"Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3","savedPayload":{"position":{"x":10396.932547937586,"y":17634.0002824789},"targetPosition":{"x":10912.22587645048,"y":17548.430001968267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Sub-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAPaula/@Exchange/Output/Bollinger-Sub-Channels/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"ca71e765-0f66-4b18-b500-847b9d16169b","savedPayload":{"position":{"x":10671.60930797112,"y":17084.799594973876},"targetPosition":{"x":10912.22587645048,"y":17548.430001968267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"421aeb2e-dd1b-44ec-bf78-00d8691d4110","savedPayload":{"position":{"x":10267.726512134628,"y":16913.436267866004},"targetPosition":{"x":10773.283039425549,"y":17044.882209777126},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d2a101e7-8183-42b4-94c4-99bfcbf2c3c2","savedPayload":{"position":{"x":10773.283039425549,"y":17044.882209777126},"targetPosition":{"x":11278.809987386765,"y":17176.320371717153},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"febf55a5-de85-4830-b4fe-df78acfc60b5","savedPayload":{"position":{"x":11000.91623430442,"y":16169.195182992746},"targetPosition":{"x":11139.867150361833,"y":16672.772579526012},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0cc9b985-b780-41ef-838b-7f764824f244","savedPayload":{"position":{"x":11139.867150361833,"y":16672.772579526012},"targetPosition":{"x":11278.809987386765,"y":17176.320371717153},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Direction","code":"{\n \"codeName\": \"direction\",\n \"isString\": true\n}","formula":{"type":"Formula","code":"record.current.direction","id":"73113162-c56d-4ca1-9e8b-f6a5bf84c30f","savedPayload":{"position":{"x":12012.04278142965,"y":16432.03556574881},"targetPosition":{"x":11645.415598620275,"y":16804.18891709501},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"91f3e346-3d02-48f9-aa0c-e55bae62f870","savedPayload":{"position":{"x":11645.415598620275,"y":16804.18891709501},"targetPosition":{"x":11278.809987386765,"y":17176.320371717153},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Slope","code":"{\n \"codeName\": \"slope\",\n \"isString\": true\n}","formula":{"type":"Formula","code":"record.current.slope","id":"f9e484d0-138a-4d19-a876-a6067f5a1619","savedPayload":{"position":{"x":12289.979606385084,"y":17439.117033378134},"targetPosition":{"x":11784.379935942432,"y":17307.71488491512},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"478a7021-7233-4eb4-958d-f127c6a4891c","savedPayload":{"position":{"x":11784.379935942432,"y":17307.71488491512},"targetPosition":{"x":11278.809987386765,"y":17176.320371717153},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Period","code":"{\n \"codeName\": \"period\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.period","id":"35c1cfa6-c691-40b0-b680-bd7c94be19e3","savedPayload":{"position":{"x":11556.789884215292,"y":18183.35811825139},"targetPosition":{"x":11417.795825006147,"y":17679.824515166234},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"61f63128-268f-4679-a0ef-e9b53a0a655b","savedPayload":{"position":{"x":11417.795825006147,"y":17679.824515166234},"targetPosition":{"x":11278.809987386765,"y":17176.320371717153},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"afe778f1-4343-4102-8eb4-0b9340d7ad5b","savedPayload":{"position":{"x":11278.809987386765,"y":17176.320371717153},"targetPosition":{"x":10912.22587645048,"y":17548.430001968267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"if (record.previous !== undefined) {\n if (record.current.direction === record.previous.direction && record.current.slope === record.previous.slope) {\n variable.period++\n } else {\n variable.period = 1\n }\n}","id":"c450c940-f800-414b-a2c4-bda3481f55c7","savedPayload":{"position":{"x":12251.36296635238,"y":17206.57254644072},"targetPosition":{"x":11815.370596366176,"y":17494.33669411417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a580c9a8-c70a-483e-8dd3-d3b62464f8f4","savedPayload":{"position":{"x":11815.370596366176,"y":17494.33669411417},"targetPosition":{"x":11379.403885224125,"y":17782.08400992631},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"variable.period = 1","id":"e0efa286-41a0-45e0-8ce3-dfc27b4dce3e","savedPayload":{"position":{"x":11441.938004156955,"y":18824.972099672603},"targetPosition":{"x":11410.669983190737,"y":18303.51274148898},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2decc091-6c76-458b-be8e-39e3e72cecd6","savedPayload":{"position":{"x":11410.669983190737,"y":18303.51274148898},"targetPosition":{"x":11379.403885224125,"y":17782.08400992631},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f16f2ab8-7ba2-4acf-bf85-8ed89bd1e5af","savedPayload":{"position":{"x":11379.403885224125,"y":17782.08400992631},"targetPosition":{"x":10912.22587645048,"y":17548.430001968267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"79ce9c05-8a4c-4b58-850f-288037acc702","savedPayload":{"position":{"x":10912.22587645048,"y":17548.430001968267},"targetPosition":{"x":10871.086550697519,"y":18089.948868648757},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80","savedPayload":{"position":{"x":10904.028748732937,"y":18070.932583389982},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}}],"tradingBots":[{"type":"Trading Bot","name":"Jason","code":"{\n\"codeName\": \"AAJason\",\n\"repo\": \"AAJason-Trading-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period","code":"{\n \"codeName\": \"Multi-Period\",\n \"startMode\": {\n \"userDefined\": {\n \"run\": \"true\"\n }\n },\n \"deadWaitTime\": 0,\n \"liveWaitTime\": 60000,\n \"normalWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period\",\n \"validPeriods\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"startDate\": {},\n \"endDate\": {}\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"2448e10f-8851-4f99-bbc3-4114045de8c0","savedPayload":{"position":{"x":11891.584462734592,"y":17624.361563949147},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"d7335089-1f50-4d48-bcbe-115136a88e6a"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"d1b73084-294b-46f5-a2c4-da08b994b770","savedPayload":{"position":{"x":11757.060114759017,"y":17984.40862666956},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"274082a4-1695-4e9b-a0ff-9464fa2d986f"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"156aa7e6-0514-465e-9734-3983a51ab69c","savedPayload":{"position":{"x":11427.266711540067,"y":18187.48569210096},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"52f26c43-552c-4557-af7a-878b951f5731"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"8067fbac-fbc3-4712-9c24-4b4b9a24d78d","savedPayload":{"position":{"x":11047.680696149742,"y":18109.841614911365},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"afb38dc6-0437-4215-8a7d-3091cd3c74fb"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"72529350-c20e-4f28-a308-2611df7a6468","savedPayload":{"position":{"x":10846.106571189952,"y":17799.746504206334},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"cb317eb0-047c-4076-bd46-7add2423aa87"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"1b491b01-465a-4f98-831d-a587fc428b58","savedPayload":{"position":{"x":10801.647548414967,"y":17432.794862757302},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"41498979-9228-4151-8b94-0d8acdf257e8"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"5711ecc2-5192-4d97-a13d-5269264ea825","savedPayload":{"position":{"x":11044.82010356216,"y":17133.926821285484},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"823a30de-093f-4c39-9181-5727c81bbad3"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"3be12bcb-4634-4933-9ae1-bad5089b8062","savedPayload":{"position":{"x":11429.83425267623,"y":17046.676186430694},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"486698b4-c159-4bb7-8d4a-7057aa94e903"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"43aa8bfe-8aed-4cbb-98d7-342d857fb3d9","savedPayload":{"position":{"x":11756.172391873306,"y":17265.059389174723},"targetPosition":{"x":11327.972734258972,"y":17624.361563949147},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Oficial History","id":"3ad0f044-95e9-4ae7-9169-9dacfc13c011"}}}],"id":"58e53ed7-a9ab-4327-a7b5-cc80d2a3e222","savedPayload":{"position":{"x":11327.972734258972,"y":17624.361563949147},"targetPosition":{"x":11181.19202879262,"y":18172.154614320378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"8788fac1-b7a9-403b-a7d6-367c71ac9919","savedPayload":{"position":{"x":12290.730550028626,"y":18025.373908854024},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"2addf3d0-ce67-4700-a2bb-aa9998cd5e77"}}},{"type":"Status Dependency","name":"New Status Dependency","code":"{\"mainUtility\": \"Self Reference|Market Starting Point|Market Ending Point\"}","id":"7c757472-f226-4e5c-a8fc-e598947424a3","savedPayload":{"position":{"x":12227.70452137602,"y":18265.54453412925},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a1317697-3b4f-4b41-bbb6-0a43fd08fa28"}}},{"type":"Status Dependency","name":"Olivia","code":"{\n\"mainUtility\": \"Market Ending Point\"\n}","id":"f1fdebab-b7be-411a-ac75-0280185b09d3","savedPayload":{"position":{"x":12096.13463807567,"y":18485.7648937198},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}},{"type":"Status Dependency","name":"Charly","code":"{\"mainUtility\": \"Market Starting Point\"}","id":"1915e12c-8285-453f-874d-131b5617a999","savedPayload":{"position":{"x":11849.864658705572,"y":18554.981950725545},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Bollinger Bands Multi-Period-Market","id":"5bc225fd-2469-426b-8690-9d1ddff24bcd","savedPayload":{"position":{"x":11596.147861084954,"y":18607.371788240638},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}},{"type":"Data Dependency","name":"Percentage Bandwidth Multi-Period-Market","id":"713606e5-61b8-4597-a878-6d16f4aa9c16","savedPayload":{"position":{"x":11368.765090463015,"y":18477.07548751818},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"207e9481-0730-4304-a938-cfe223d70856"}}},{"type":"Data Dependency","name":"Bollinger Channels Market","id":"23995773-d176-4440-bb23-f1fbbffbb61f","savedPayload":{"position":{"x":11219.904016130491,"y":18270.534427726445},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Market","id":"22c1f2c6-3838-4657-add8-2e01d7056917"}}},{"type":"Data Dependency","name":"Bollinger Sub-Channels Market","id":"3c59a984-a0ac-4fa4-a70b-ba5b5e2e2133","savedPayload":{"position":{"x":11158.429352899337,"y":18025.373908854024},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Market","id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}},{"type":"Data Dependency","name":"Candles Multi-Period-Market","id":"71c00672-f101-4b5b-b8db-b42b20b1fa92","savedPayload":{"position":{"x":11225.371401665558,"y":17782.846344086523},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}},{"type":"Data Dependency","name":"Bollinger Bands Multi-Period-Daily","id":"85a073ae-2e57-4e5f-881f-b4ebd47fb6df","savedPayload":{"position":{"x":11368.458591891458,"y":17573.287993137583},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"506104fa-e99a-4a46-930e-176bce311aff"}}},{"type":"Data Dependency","name":"Percentage Bandwidth Multi-Period-Daily","id":"782c081a-df9b-4a3f-a74f-c104146d6f35","savedPayload":{"position":{"x":11603.106762681904,"y":17473.86496947126},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"e60d62dd-87f7-4f2d-ac21-3ac5c30a50ea"}}},{"type":"Data Dependency","name":"Bollinger Channels Daily","id":"f04756ad-8cff-46d2-91e1-9fbdbfcdfdb7","savedPayload":{"position":{"x":11855.780383514078,"y":17469.847383116485},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Daily","id":"96b4e65b-e6db-44a0-80a5-ceb9d68a09ec"}}},{"type":"Data Dependency","name":"Bollinger Sub-Channels Daily","id":"63d8b075-b89d-41da-8d1e-231c2288fd1c","savedPayload":{"position":{"x":12077.44656767007,"y":17588.41702306432},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Daily","id":"ca71e765-0f66-4b18-b500-847b9d16169b"}}},{"type":"Data Dependency","name":"Candles Multi-Period-Daily","id":"e0b50719-a6c6-4d8a-bf0f-4120bb02cda0","savedPayload":{"position":{"x":12233.915139072475,"y":17782.21240772907},"targetPosition":{"x":11728.985079163853,"y":18025.373908854024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}}}],"id":"0c08b52a-8f7a-40cb-b424-3471225a2e7b","savedPayload":{"position":{"x":11728.985079163853,"y":18025.373908854024},"targetPosition":{"x":11181.19202879262,"y":18172.154614320378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"2addf3d0-ce67-4700-a2bb-aa9998cd5e77","savedPayload":{"position":{"x":11582.2043736975,"y":18573.166959225255},"targetPosition":{"x":11181.19202879262,"y":18172.154614320378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"3245ffee-1ffd-405c-8c81-bd960be73d42","savedPayload":{"position":{"x":11034.411323326267,"y":18719.94766469161},"targetPosition":{"x":11181.19202879262,"y":18172.154614320378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"92813b2f-21f3-4891-ae0d-db922ec3cc18","savedPayload":{"position":{"x":10633.398978421388,"y":18318.93531978673},"targetPosition":{"x":11181.19202879262,"y":18172.154614320378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6f95cc67-bad0-48dc-bb82-0075c43cb202","savedPayload":{"position":{"x":11181.19202879262,"y":18172.154614320378},"targetPosition":{"x":10756.388529249149,"y":17828.786639050923},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},{"type":"Process Definition","name":"Context","code":"{\n\"codeName\": \"Context\"\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"441aec7b-d710-4aa3-8c81-1acd8912b8fa","savedPayload":{"position":{"x":11247.360315097809,"y":18112.4283875861},"targetPosition":{"x":10756.305651518394,"y":18395.903761315727},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[],"dataDependencies":[],"id":"d982ba6b-a59d-4934-abb2-912dc8e20660","savedPayload":{"position":{"x":11247.360315097809,"y":18679.54548442962},"targetPosition":{"x":10756.305651518394,"y":18395.903761315727},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"a1317697-3b4f-4b41-bbb6-0a43fd08fa28","savedPayload":{"position":{"x":10756.222502310844,"y":18963.104032851377},"targetPosition":{"x":10756.305651518394,"y":18395.903761315727},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"c3d58fd6-f0ce-41a4-b9cd-05b9f30ad640","savedPayload":{"position":{"x":10265.08468952388,"y":18679.54548442962},"targetPosition":{"x":10756.305651518394,"y":18395.903761315727},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"c8086f00-19a8-4f0d-9a5d-44469da5caf2","savedPayload":{"position":{"x":10265.08468952388,"y":18112.4283875861},"targetPosition":{"x":10756.305651518394,"y":18395.903761315727},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"24317ebe-1ed4-48bc-a7ae-518de665b43f","savedPayload":{"position":{"x":10756.305651518394,"y":18395.903761315727},"targetPosition":{"x":10756.388529249149,"y":17828.786639050923},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Simulation","code":"{\n \"codeName\": \"Trading-Simulation\",\n \"singularVariableName\": \"tradingSimulation\",\n \"pluralVariableName\": \"tradingSimulations\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Trading-Simulation/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"d7335089-1f50-4d48-bcbe-115136a88e6a","savedPayload":{"position":{"x":10818.5378644877,"y":18542.350967667633},"targetPosition":{"x":10462.29534561288,"y":18185.47231065405},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Trading-Simulation/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Trading-Simulation/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"274082a4-1695-4e9b-a0ff-9464fa2d986f","savedPayload":{"position":{"x":10105.15656622332,"y":18542.350967667633},"targetPosition":{"x":10462.29534561288,"y":18185.47231065405},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"5332a4a9-98de-4406-ad5e-d9a6a1ff5cee","savedPayload":{"position":{"x":10289.03621479482,"y":18286.372601112194},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"70392162-bb77-48e0-9bba-22e8ed84f055","savedPayload":{"position":{"x":10215.267850096774,"y":18332.163111086345},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Type","code":"{\n \"codeName\": \"type\",\n \"isString\": true\n}","id":"6a4b1d81-2b7d-4a02-9d13-d3cb2442710f","savedPayload":{"position":{"x":10135.374130799835,"y":18366.156287619568},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Market Rate","code":"{\n \"codeName\": \"marketRate\",\n \"isString\": false\n}","id":"6a4b5c8e-8294-438c-a2da-ad812e59a131","savedPayload":{"position":{"x":10051.227697969545,"y":18387.555359465197},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Amount","code":"{\n \"codeName\": \"amount\",\n \"isString\": false\n}","id":"c7186621-e504-4cf5-9f74-0ff177d7aa24","savedPayload":{"position":{"x":9964.800872672238,"y":18395.85875051619},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Balance A","code":"{\n \"codeName\": \"balanceA\",\n \"isString\": false\n}","id":"64bddfff-11f9-4052-98c0-ab7b42a7615f","savedPayload":{"position":{"x":9878.119426440902,"y":18390.87183632393},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Balance B","code":"{\n \"codeName\": \"balanceB\",\n \"isString\": false\n}","id":"69dff994-7889-4c0c-9847-dd3949ff6a6f","savedPayload":{"position":{"x":9793.215098907413,"y":18372.711505930383},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Profit","code":"{\n \"codeName\": \"profit\",\n \"isString\": false\n}","id":"1fa2f30d-669a-45bd-9d11-9a62b82640ec","savedPayload":{"position":{"x":9712.077975547585,"y":18341.80342208798},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Last Trade Profit Loss","code":"{\n \"codeName\": \"lastTradeProfitLoss\",\n \"isString\": false\n}","id":"0805787c-8b11-4cda-8166-ab8d9cad0a7b","savedPayload":{"position":{"x":9636.609841764335,"y":18298.872044085518},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Stop Loss","code":"{\n \"codeName\": \"stopLoss\",\n \"isString\": false\n}","id":"92c16033-3882-4c75-bfe7-3b1b793e59ab","savedPayload":{"position":{"x":9568.579606649717,"y":18244.923647036423},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Roundtrips","code":"{\n \"codeName\": \"roundtrips\",\n \"isString\": false\n}","id":"6381add6-79c2-44e6-b590-c1dcfb99e2a1","savedPayload":{"position":{"x":9509.581841255067,"y":18181.222735642983},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Hits","code":"{\n \"codeName\": \"hits\",\n \"isString\": false\n}","id":"b4290398-e1e5-44b5-b61a-d267ad9b8316","savedPayload":{"position":{"x":9460.999403197047,"y":18109.262405277874},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Fails","code":"{\n \"codeName\": \"fails\",\n \"isString\": false\n}","id":"56099414-6c12-4e67-8f88-5182242967c4","savedPayload":{"position":{"x":9423.971023647173,"y":18030.729345094107},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Hit Ratio","code":"{\n \"codeName\": \"hitRatio\",\n \"isString\": false\n}","id":"2213cf96-502e-4aa4-938a-96acb0f92557","savedPayload":{"position":{"x":9399.364616438292,"y":17947.464303460853},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"ROI","code":"{\n \"codeName\": \"ROI\",\n \"isString\": false\n}","id":"b41213a7-07ad-426d-8bd3-fd4925594f11","savedPayload":{"position":{"x":9387.756934899959,"y":17861.418942381817},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Periods","code":"{\n \"codeName\": \"periods\",\n \"isString\": false\n}","id":"f41a7319-6b9a-46b3-ba04-8a58bc91e397","savedPayload":{"position":{"x":9389.420053249218,"y":17774.610092192026},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Days","code":"{\n \"codeName\": \"days\",\n \"isString\": false\n}","id":"67d293ba-6aea-40cc-bbb8-99f8f0cd5876","savedPayload":{"position":{"x":9404.314989401555,"y":17689.072478764163},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Anualized Rate Of Return","code":"{\n \"codeName\": \"anualizedRateOfReturn\",\n \"isString\": false\n}","id":"d78c501d-91c3-415a-850c-9115e0552230","savedPayload":{"position":{"x":9432.092618677922,"y":17606.811031258654},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Trade Position Rate","code":"{\n \"codeName\": \"tradePositionRate\",\n \"isString\": false\n}","id":"7912c4d4-d85d-48c3-9953-f0c194fbe4ac","savedPayload":{"position":{"x":9472.101856991309,"y":17529.75388828346},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Last Trade ROI","code":"{\n \"codeName\": \"lastTradeROI\",\n \"isString\": false\n}","id":"9996b0ef-da23-4c37-bbc5-b3d78baa8b4c","savedPayload":{"position":{"x":9523.404921705906,"y":17459.70720395931},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Current Strategy Index","code":"{\n \"codeName\": \"currentStrategyIndex\",\n \"isString\": false\n}","id":"6ff9ddc3-5989-425b-b406-6649c152ab24","savedPayload":{"position":{"x":9584.799312467263,"y":17398.312813197954},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Strategy Stage Number","code":"{\n \"codeName\": \"strategyStageNumber\",\n \"isString\": false\n}","id":"288a10b5-4c77-43aa-a548-c24629b3f7ee","savedPayload":{"position":{"x":9654.845996791411,"y":17347.009748483357},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Take Profit","code":"{\n \"codeName\": \"takeProfit\",\n \"isString\": false\n}","id":"66f26d4d-28d0-4896-88b0-890fc9a25ea9","savedPayload":{"position":{"x":9731.903139766606,"y":17307.00051016997},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Stop Loss Phase","code":"{\n \"codeName\": \"stopLossPhase\",\n \"isString\": false\n}","id":"71f92e69-779c-4643-9f7a-2a82f960f4e5","savedPayload":{"position":{"x":9814.164587272115,"y":17279.2228808936},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Take Profit Phase","code":"{\n \"codeName\": \"takeProfitPhase\",\n \"isString\": false\n}","id":"7d9ebb36-97b5-4bdf-95c9-40c52cb85f0e","savedPayload":{"position":{"x":9899.70220069998,"y":17264.327944741268},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Execution Record","code":"{\n \"codeName\": \"executionRecord\",\n \"isString\": true\n}","id":"a138bd75-7a4c-4dda-8977-5ac63f432b1f","savedPayload":{"position":{"x":9986.511050889769,"y":17262.664826392007},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Trade Position Size","code":"{\n \"codeName\": \"tradePositionSize\",\n \"isString\": false\n}","id":"d8c2810d-7c00-4383-b033-881a2c85fff2","savedPayload":{"position":{"x":10072.556411968804,"y":17274.27250793034},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Initial Balance A","code":"{\n \"codeName\": \"initialBalanceA\",\n \"isString\": false\n}","id":"9539791d-7864-48e4-801e-a798dbbdc4bb","savedPayload":{"position":{"x":10155.821453602059,"y":17298.87891513922},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Minimum Balance A","code":"{\n \"codeName\": \"minimumBalanceA\",\n \"isString\": false\n}","id":"e523f9bf-5f97-4089-b693-f861949641f8","savedPayload":{"position":{"x":10234.354513785827,"y":17335.907294689096},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Maximum Balance A","code":"{\n \"codeName\": \"maximumBalanceA\",\n \"isString\": false\n}","id":"0ff4c6e9-93c6-4da2-b5ed-86e225524f66","savedPayload":{"position":{"x":10306.314844150937,"y":17384.489732747115},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Initial Balance B","code":"{\n \"codeName\": \"initialBalanceB\",\n \"isString\": false\n}","id":"7c3b05c2-530e-453b-b776-7d55514456ef","savedPayload":{"position":{"x":10370.015755544375,"y":17443.487498141767},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Minimum Balance B","code":"{\n \"codeName\": \"minimumBalanceB\",\n \"isString\": false\n}","id":"f56fac84-b690-4aa2-a305-a430dde115c6","savedPayload":{"position":{"x":10423.96415259347,"y":17511.517733256384},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Maximum Balance B","code":"{\n \"codeName\": \"maximumBalanceB\",\n \"isString\": false\n}","id":"42605779-f1a6-48b7-992d-05124c2f5c99","savedPayload":{"position":{"x":10466.895530595933,"y":17586.98586703963},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Base Asset","code":"{\n \"codeName\": \"baseAsset\",\n \"isString\": true\n}","id":"3a26c1da-2c55-421a-a311-ba66a095cf70","savedPayload":{"position":{"x":10497.803614438333,"y":17668.12299039946},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Position Periods","code":"{\n \"codeName\": \"positionPeriods\",\n \"isString\": false\n}","id":"ad4cc73a-3cfe-48f3-9f77-8eea811acb0f","savedPayload":{"position":{"x":10515.963944831883,"y":17753.027317932952},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Position Days","code":"{\n \"codeName\": \"positionDays\",\n \"isString\": false\n}","id":"29a59888-daca-4440-aca1-7942f27426ad","savedPayload":{"position":{"x":10520.95085902414,"y":17839.708764164287},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Distance To Last Trigger On","code":"{\n \"codeName\": \"distanceToLastTriggerOn\",\n \"isString\": false\n}","id":"b3c41567-e36b-46ff-824d-aca57b0c6f57","savedPayload":{"position":{"x":10512.64746797315,"y":17926.135589461595},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Distance ToLast Trigger Off","code":"{\n \"codeName\": \"distanceToLastTriggerOff\",\n \"isString\": false\n}","id":"30280e51-780a-4593-b392-181ec71e6c95","savedPayload":{"position":{"x":10491.24839612752,"y":18010.282022291885},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Distance To Last Take Position","code":"{\n \"codeName\": \"distanceToLastTakePosition\",\n \"isString\": false\n}","id":"28dd29f5-c161-4132-be23-59f08a2ca8cc","savedPayload":{"position":{"x":10457.255219594297,"y":18090.175741588824},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Distance To Last Close Position","code":"{\n \"codeName\": \"distanceToLastClosePosition\",\n \"isString\": false\n}","id":"da0f85d8-ae29-4a25-b88e-f65fbc7b643c","savedPayload":{"position":{"x":10411.464709620144,"y":18163.94410628687},"targetPosition":{"x":9954.139017132347,"y":17828.845485325208},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"9886dcec-c49a-4b0e-af1d-8996a68fc2e1","savedPayload":{"position":{"x":10105.15656622332,"y":17828.969669403254},"targetPosition":{"x":10462.29534561288,"y":18185.47231065405},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"q"}}},"id":"a3201051-46ba-4a92-aef3-a6c787b771fd","savedPayload":{"position":{"x":10462.29534561288,"y":18185.47231065405},"targetPosition":{"x":10819.248107885714,"y":17828.779389845768},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Trading Simulation","id":"7864b9e4-3150-4d1d-abd9-0a43a9fe4c4f"}}},{"type":"Product Definition","name":"Conditions","code":"{\n \"codeName\": \"Simulation-Conditions\",\n \"singularVariableName\": \"simulationCondition\",\n \"pluralVariableName\": \"simulationConditions\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Conditions/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"52f26c43-552c-4557-af7a-878b951f5731","savedPayload":{"position":{"x":10314.024223913651,"y":18333.220801340354},"targetPosition":{"x":10314.549241170664,"y":17828.78166152186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Conditions/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Conditions/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"afb38dc6-0437-4215-8a7d-3091cd3c74fb","savedPayload":{"position":{"x":9809.587470339246,"y":17828.78404776595},"targetPosition":{"x":10314.549241170664,"y":17828.78166152186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Current Strategy Index","code":"{\n \"codeName\": \"currentStrategyIndex\",\n \"isString\": false\n}","id":"bbfaa8e4-eac8-4f0d-a8b1-85844f96c564","savedPayload":{"position":{"x":9787.843067989543,"y":17662.598814351746},"targetPosition":{"x":10189.022256259452,"y":17261.669593112947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Strategy Stage Number","code":"{\n \"codeName\": \"strategyStageNumber\",\n \"isString\": false\n}","id":"b9b14fcf-e09b-4036-85d9-8f40304a7d13","savedPayload":{"position":{"x":9621.738316050905,"y":17261.58646944687},"targetPosition":{"x":10189.022256259452,"y":17261.669593112947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Stop Loss Phase","code":"{\n \"codeName\": \"stopLossPhase\",\n \"isString\": false\n}","id":"115e6942-c0f3-45e7-a2ba-8b661a95af3e","savedPayload":{"position":{"x":9787.843067989543,"y":16860.574124541992},"targetPosition":{"x":10189.022256259452,"y":17261.669593112947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Take Profit Phase","code":"{\n \"codeName\": \"takeProfitPhase\",\n \"isString\": false\n}","id":"599f27b7-7cd8-483b-81c9-bd131e9ab964","savedPayload":{"position":{"x":10188.855412894422,"y":16694.46937260335},"targetPosition":{"x":10189.022256259452,"y":17261.669593112947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Conditions Array Values","code":"{\n \"codeName\": \"conditionsArrayValues\",\n \"isString\": false\n}","id":"1359d846-f978-4a6a-9e94-4f23fcdde80b","savedPayload":{"position":{"x":10589.8677577993,"y":16860.57412454199},"targetPosition":{"x":10189.022256259452,"y":17261.669593112947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Formulas Errors","code":"{\n \"codeName\": \"formulasErrors\",\n \"isString\": false\n}","id":"1d4b7300-013b-4874-ba78-c77bb9aac9be","savedPayload":{"position":{"x":10755.972509737938,"y":17261.58646944687},"targetPosition":{"x":10189.022256259452,"y":17261.669593112947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Formulas Values","code":"{\n \"codeName\": \"formulasValues\",\n \"isString\": false\n}","id":"5306f8cc-6c59-477a-ac58-303eea4142cf","savedPayload":{"position":{"x":10589.8677577993,"y":17662.598814351746},"targetPosition":{"x":10189.022256259452,"y":17261.669593112947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"2f2a6b6d-6c24-4b04-936b-3fdb66afc498","savedPayload":{"position":{"x":10314.024223913651,"y":17324.347294191542},"targetPosition":{"x":10314.549241170664,"y":17828.78166152186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7fb3e69f-4343-47f8-ab7b-6fcf8a805fc0","savedPayload":{"position":{"x":10314.549241170664,"y":17828.78166152186},"targetPosition":{"x":10819.248107885714,"y":17828.779389845768},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Conditions","id":"546d0a94-e878-41c1-a6e6-7dd28832f79b"}}},{"type":"Product Definition","name":"Strategies","code":"{\n \"codeName\": \"Simulation-Strategies\",\n \"singularVariableName\": \"simulationStrategy\",\n \"pluralVariableName\": \"simulationStrategies\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Strategies/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"cb317eb0-047c-4076-bd46-7add2423aa87","savedPayload":{"position":{"x":10105.15656622332,"y":17828.598426128643},"targetPosition":{"x":10462.29534561288,"y":17472.091012389672},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Strategies/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Strategies/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"41498979-9228-4151-8b94-0d8acdf257e8","savedPayload":{"position":{"x":10105.15656622332,"y":17115.217127864264},"targetPosition":{"x":10462.29534561288,"y":17472.091012389672},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"77ad2d38-fbe8-480b-a8a0-0757a09f09d4","savedPayload":{"position":{"x":10192.528909018361,"y":16963.08847827619},"targetPosition":{"x":10756.163706942105,"y":17026.70320477797},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"23b7aa0b-5be6-4e07-a858-d89b588eec97","savedPayload":{"position":{"x":10454.355605929968,"y":16546.393677220007},"targetPosition":{"x":10756.163706942105,"y":17026.70320477797},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Status","code":"{\n \"codeName\": \"status\",\n \"isString\": false\n}","id":"fae7e152-d3b6-4508-a4de-a0db478ca92d","savedPayload":{"position":{"x":10943.386995355188,"y":16491.293072870034},"targetPosition":{"x":10756.163706942105,"y":17026.70320477797},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Number","code":"{\n \"codeName\": \"number\",\n \"isString\": false\n}","id":"6c18d6ae-089b-483f-979f-e434991bcae0","savedPayload":{"position":{"x":11291.372466634446,"y":16839.278544149292},"targetPosition":{"x":10756.163706942105,"y":17026.70320477797},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Begin Rate","code":"{\n \"codeName\": \"beginRate\",\n \"isString\": false\n}","id":"2d17ff8b-946c-45bd-b665-426e7ed5ec64","savedPayload":{"position":{"x":11236.27186228447,"y":17328.30993357451},"targetPosition":{"x":10756.163706942105,"y":17026.70320477797},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End Rate","code":"{\n \"codeName\": \"endRate\",\n \"isString\": false\n}","id":"60112244-d5d6-41df-8e89-685366751ecd","savedPayload":{"position":{"x":10819.577061228287,"y":17590.136630486115},"targetPosition":{"x":10756.163706942105,"y":17026.70320477797},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"8d502c73-b428-4428-93cc-6336d63dd21d","savedPayload":{"position":{"x":10818.5378644877,"y":17115.217127864264},"targetPosition":{"x":10462.29534561288,"y":17472.091012389672},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"36051f0f-5161-48a6-9d13-eb1766b5566b","savedPayload":{"position":{"x":10462.29534561288,"y":17472.091012389672},"targetPosition":{"x":10819.248107885714,"y":17828.779389845768},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Strategies","id":"9b0c4f4b-d67c-48ae-97f1-bd614e85ced8"}}},{"type":"Product Definition","name":"Trades","code":"{\n \"codeName\": \"Simulation-Trades\",\n \"singularVariableName\": \"simulationTrade\",\n \"pluralVariableName\": \"simulationTrades\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Trades/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"823a30de-093f-4c39-9181-5727c81bbad3","savedPayload":{"position":{"x":10314.286732550601,"y":17324.084785554594},"targetPosition":{"x":10818.98599474507,"y":17324.344907947456},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validTimeFrames\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Trades/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"AAMasters/AAJason/@Exchange/Output/@Session/Simulation-Trades/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"486698b4-c159-4bb7-8d4a-7057aa94e903","savedPayload":{"position":{"x":10818.723486125007,"y":16819.64803198019},"targetPosition":{"x":10818.98599474507,"y":17324.344907947456},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","id":"1481ecbc-72f4-4ae6-a80e-63e57be003c8","savedPayload":{"position":{"x":10922.326981775006,"y":16860.490702859315},"targetPosition":{"x":11323.33959915436,"y":17261.58644390507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","id":"cdc28bbd-202e-4363-bf88-dc6a981b54b5","savedPayload":{"position":{"x":11323.339326679885,"y":16694.385950920674},"targetPosition":{"x":11323.33959915436,"y":17261.58644390507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Status","code":"{\n \"codeName\": \"status\",\n \"isString\": false\n}","id":"6b5b99b5-421a-42b2-a621-8b778742783d","savedPayload":{"position":{"x":11724.351671584764,"y":16860.49070285931},"targetPosition":{"x":11323.33959915436,"y":17261.58644390507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Last Trade ROI","code":"{\n \"codeName\": \"lastTradeROI\",\n \"isString\": false\n}","id":"4219b9f2-492d-4eab-9472-1f8195eb216a","savedPayload":{"position":{"x":11890.456423523401,"y":17261.503047764192},"targetPosition":{"x":11323.33959915436,"y":17261.58644390507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Exit Type","code":"{\n \"codeName\": \"exitType\",\n \"isString\": false\n}","id":"c5612569-dc10-4153-aff5-dccfadbf2d16","savedPayload":{"position":{"x":11724.351671584764,"y":17662.51539266907},"targetPosition":{"x":11323.33959915436,"y":17261.58644390507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Begin Rate","code":"{\n \"codeName\": \"beginRate\",\n \"isString\": false\n}","id":"fafe1ef3-e00e-482b-8bfb-c61171478260","savedPayload":{"position":{"x":11323.339326679885,"y":17828.62014460771},"targetPosition":{"x":11323.33959915436,"y":17261.58644390507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End Rate","code":"{\n \"codeName\": \"endRate\",\n \"isString\": false\n}","id":"24385194-d717-4c93-a938-4ba3f6be3c67","savedPayload":{"position":{"x":10922.326981775006,"y":17662.51539266907},"targetPosition":{"x":11323.33959915436,"y":17261.58644390507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"7201c4b3-e6c0-49eb-8f8d-bb24c51f3786","savedPayload":{"position":{"x":11323.160239699413,"y":17324.084785554594},"targetPosition":{"x":10818.98599474507,"y":17324.344907947456},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a26dc922-ca08-43ef-983e-029b76f39163","savedPayload":{"position":{"x":10818.98599474507,"y":17324.344907947456},"targetPosition":{"x":10819.248107885714,"y":17828.779389845768},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Trades","id":"382004c6-e318-4d9d-9791-505f329f1e54"}}},{"type":"Product Definition","name":"Execution","code":"{\n \"codeName\": \"Live-Trading-History\",\n \"singularVariableName\": \"liveTradingHistory\",\n \"pluralVariableName\": \"liveTradingHistory\"\n}","datasets":[{"type":"Dataset Definition","name":"Oficial History","code":"{\n \"codeName\": \"Oficial History\",\n \"type\": \"File Sequence\",\n \"validTimeFrames\": [\n \"24-hs\",\n \"12-hs\",\n \"08-hs\",\n \"06-hs\",\n \"04-hs\",\n \"03-hs\",\n \"02-hs\",\n \"01-hs\",\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"filePath\": \"@DataMine/@Bot/Poloniex/Output/@Session/Trading-Process\",\n \"fileName\": \"Execution.History.Live.@Sequence.json\"\n}","id":"3ad0f044-95e9-4ae7-9169-9dacfc13c011","savedPayload":{"position":{"x":11045.04191801976,"y":16984.65928898952},"targetPosition":{"x":11175.67664387726,"y":17472.091012389672},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[],"id":"848b3eb1-afc1-406a-ab21-15ecbc893766","savedPayload":{"position":{"x":11662.848244901437,"y":17602.46561587119},"targetPosition":{"x":11175.67664387726,"y":17472.091012389672},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"05ce9ca7-8b5b-4974-a45e-ca319fc85330","savedPayload":{"position":{"x":11175.67664387726,"y":17472.091012389672},"targetPosition":{"x":10819.248107885714,"y":17828.779389845768},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"Simulation Execution","id":"96dede74-e7bc-4c50-a9ba-2bec8a5f4884"}}}],"id":"b4f01468-c3e6-405e-8152-e460f597f98a","savedPayload":{"position":{"x":10819.509822020444,"y":17828.777226948936},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"j"}}}],"plotters":[{"type":"Plotter","name":"Plotters-Candles-Volumes","code":"{\n\"codeName\": \"PlottersCandlesVolumes\"\n}","modules":[{"type":"Plotter Module","name":"Candles","code":"{\n\"codeName\": \"Candles\",\n\"isLegacy\": true,\n\"icon\": \"candles\"\n}","panels":[{"type":"Plotter Panel","name":"New Plotter Panel","code":"{ \n\"codeName\": \"CandlePanel\" ,\n\"isLegacy\": true\n}","panelData":[],"id":"9336f4e6-d56b-4ed4-819d-0bb762ba1fdd","savedPayload":{"position":{"x":9482.517320739482,"y":18183.061696987013},"targetPosition":{"x":10096.152463264276,"y":17828.77928226611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"0aed75a3-fee8-4ddc-97ff-b6768e6f12a3","savedPayload":{"position":{"x":10096.152463264276,"y":17828.77928226611},"targetPosition":{"x":10709.78760578907,"y":17474.496867545207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Volumes","code":"{\n\"codeName\": \"Volumes\",\n\"isLegacy\": true,\n\"icon\": \"volume\"\n}","panels":[{"type":"Plotter Panel","name":"New Plotter Panel","code":"{ \n\"codeName\": \"VolumePanel\" ,\n\"isLegacy\": true\n}","panelData":[],"id":"42dbd098-4550-44a0-987f-54a28dd5f40b","savedPayload":{"position":{"x":10709.78760578907,"y":16057.367208661593},"targetPosition":{"x":10709.78760578907,"y":16765.9320381034},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a6177fb7-51d1-4b7c-bcbb-04861a5bf0c1","savedPayload":{"position":{"x":10709.78760578907,"y":16765.9320381034},"targetPosition":{"x":10709.78760578907,"y":17474.496867545207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"e4347543-21b5-4274-b690-e6b3b5899862","savedPayload":{"position":{"x":10709.78760578907,"y":17474.496867545207},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},{"type":"Plotter","name":"Plotters-Stairs-Patterns","code":"{\n\"codeName\": \"PlottersStairsPatterns\"\n}","modules":[{"type":"Plotter Module","name":"Candles Patterns","code":"{\n\"codeName\": \"CandleStairs\",\n\"isLegacy\": true,\n\"icon\": \"trend\"\n}","panels":[],"id":"a41043f2-f2a5-4c8f-b372-d8eda1ca58df","savedPayload":{"position":{"x":10561.041146843563,"y":17388.618059432014},"targetPosition":{"x":11069.295547823764,"y":17388.618059432014},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Volume Patterns","code":"{\n\"codeName\": \"VolumeStairs\",\n\"isLegacy\": true,\n\"icon\": \"trend\"\n}","panels":[],"id":"cfd94d21-cb45-446b-9764-cc67cca712b2","savedPayload":{"position":{"x":11323.422748313864,"y":16948.456836597918},"targetPosition":{"x":11069.295547823764,"y":17388.618059432014},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b3b5fed4-ddff-406a-83b9-b33968b820a8","savedPayload":{"position":{"x":11069.295547823764,"y":17388.618059432014},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter","name":"Plotters-Bollinger-Bands","code":"{\n\"codeName\" : \"PlottersBollingerBands\"\n}","modules":[{"type":"Plotter Module","name":"Percentage B","code":"{\n\t\"codeName\": \"Percentage Bandwidth\",\n \"icon\": \"histogram\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"Begin Previous Value","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.value","id":"ad85e84e-166f-41c7-bc08-b6a50ca4524d","savedPayload":{"position":{"x":10150.623995278484,"y":16928.623422992503},"targetPosition":{"x":10561.897826823455,"y":17166.072480345643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"534c2cd4-d020-43cf-9284-433a57fdcfce","savedPayload":{"position":{"x":10561.897826823455,"y":17166.072480345643},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End Value","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.value","id":"1e029480-c6d7-4455-9ae9-5677b99752de","savedPayload":{"position":{"x":10414.895442036728,"y":16635.12024691078},"targetPosition":{"x":10694.033550202577,"y":17019.320892304782},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5b05abcc-76cf-4f9d-82e5-671ab7471678","savedPayload":{"position":{"x":10694.033550202577,"y":17019.320892304782},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin Previous MA","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.movingAverage","id":"7bafa19b-086c-41cf-b2e4-a41485841f4c","savedPayload":{"position":{"x":10775.697918378928,"y":16474.480634712876},"targetPosition":{"x":10874.434788373677,"y":16939.00108620583},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0993c95f-2054-48d8-9977-2d4eb24dabf7","savedPayload":{"position":{"x":10874.434788373677,"y":16939.00108620583},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End MA","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.movingAverage","id":"412d8820-facc-4065-a235-d35d86c3b569","savedPayload":{"position":{"x":11170.645398357921,"y":16474.480634712876},"targetPosition":{"x":11071.908528363174,"y":16939.00108620583},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"35bdf97d-abf8-4f1e-922c-d853e6980b0c","savedPayload":{"position":{"x":11071.908528363174,"y":16939.00108620583},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Being Previous Bandwidth","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.bandwidth * 250","id":"cbd72abc-e6c4-44d6-bd58-1bab7c0b2960","savedPayload":{"position":{"x":11531.447874700125,"y":16635.12024691078},"targetPosition":{"x":11252.309766534276,"y":17019.320892304782},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"943da81e-c0ca-4213-ba8a-5dbafd3ead18","savedPayload":{"position":{"x":11252.309766534276,"y":17019.320892304782},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End Bandwidth","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.bandwidth * 250","id":"6ac14886-32ee-490f-a600-4f704458f24e","savedPayload":{"position":{"x":11795.71932145837,"y":16928.623422992503},"targetPosition":{"x":11384.445489913398,"y":17166.072480345643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cdf38478-d5d4-410e-98fd-14b7b74bfd8b","savedPayload":{"position":{"x":11384.445489913398,"y":17166.072480345643},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 0","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 0","id":"54cece5b-472f-4c86-b6b6-83342e2890f7","savedPayload":{"position":{"x":11917.764804657438,"y":17304.240797420876},"targetPosition":{"x":11445.468231512932,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8e21c88b-bfb7-41dd-9cbe-678d5dc4fc54","savedPayload":{"position":{"x":11445.468231512932,"y":17353.88116755983},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 0","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 0","id":"1572cfc8-0318-43b5-8405-a04cc00cbd16","savedPayload":{"position":{"x":11876.4815515038,"y":17697.024713780505},"targetPosition":{"x":11424.826604936114,"y":17550.273125739644},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"75670aaa-849d-4156-af74-61a25ce49713","savedPayload":{"position":{"x":11424.826604936114,"y":17550.273125739644},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 30","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 30","id":"6039db7e-9c71-4906-b999-3d7848989c5a","savedPayload":{"position":{"x":11679.007811514306,"y":18039.05926460296},"targetPosition":{"x":11326.089734941366,"y":17721.290401150873},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ffea0ed5-39b4-4c14-b1fc-2164a6e189fd","savedPayload":{"position":{"x":11326.089734941366,"y":17721.290401150873},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 30","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 30","id":"89393793-42d5-4dea-bca5-aee8f3349659","savedPayload":{"position":{"x":11359.488588325743,"y":18271.203568764693},"targetPosition":{"x":11166.330123347085,"y":17837.36255323174},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4bc7f337-74a7-42b7-89b7-9d4edc10fca5","savedPayload":{"position":{"x":11166.330123347085,"y":17837.36255323174},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 70","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 70","id":"c616cf49-abaf-490e-a3b7-f978442c8b4a","savedPayload":{"position":{"x":10973.171658368427,"y":18353.317767111337},"targetPosition":{"x":10973.171658368427,"y":17878.41965240506},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"827e424f-3036-4645-9246-94b4bb0ebaea","savedPayload":{"position":{"x":10973.171658368427,"y":17878.41965240506},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 70","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 70","id":"d3003e73-9318-4282-a293-d61c0d98bd4b","savedPayload":{"position":{"x":10586.85472841111,"y":18271.203568764693},"targetPosition":{"x":10780.013193389768,"y":17837.36255323174},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"84bdd434-48af-4f4a-8f14-62df15022d05","savedPayload":{"position":{"x":10780.013193389768,"y":17837.36255323174},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 100","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 100","id":"8d13712b-3f9e-424e-b85c-e6794ce39ca9","savedPayload":{"position":{"x":10267.335505222547,"y":18039.05926460296},"targetPosition":{"x":10620.253581795487,"y":17721.290401150873},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ec589261-f564-4ad0-b866-8c63b8a15150","savedPayload":{"position":{"x":10620.253581795487,"y":17721.290401150873},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 100","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 100","id":"8c06033c-c2b7-42a7-b509-e45a1b7edf42","savedPayload":{"position":{"x":10069.861765233052,"y":17697.024713780505},"targetPosition":{"x":10521.51671180074,"y":17550.273125739644},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8949ce4a-bdad-47c2-a85f-0728ed7f9746","savedPayload":{"position":{"x":10521.51671180074,"y":17550.273125739644},"targetPosition":{"x":10973.171658368427,"y":17403.521537698784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"9388e24f-4147-4878-8754-b6f6b8807813","savedPayload":{"position":{"x":10973.171658368427,"y":17403.521537698784},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"Area From 0 to 30","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.2,\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n }\n}","id":"05578d4b-01e3-42fc-a4dd-b473414f467d","savedPayload":{"position":{"x":11051.266901786355,"y":16742.60912802601},"targetPosition":{"x":10952.530031791608,"y":17207.129579518965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"14810299-01f6-443c-8bbf-de8450c9efd6","savedPayload":{"position":{"x":10952.530031791608,"y":17207.129579518965},"targetPosition":{"x":10853.79316179686,"y":17671.65003101192},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"d8eda99d-df16-4e21-b621-6f547914c461","savedPayload":{"position":{"x":10989.569775064896,"y":18089.527478400363},"targetPosition":{"x":10853.79316179686,"y":17671.65003101192},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 0","id":"8e21c88b-bfb7-41dd-9cbe-678d5dc4fc54"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"3a844186-fa2d-45e3-9453-d11750bafb51","savedPayload":{"position":{"x":10472.264214314988,"y":17948.847037022624},"targetPosition":{"x":10853.79316179686,"y":17671.65003101192},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 0","id":"75670aaa-849d-4156-af74-61a25ce49713"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"95d52cc3-0a06-4181-bcd7-1e65b41194d8","savedPayload":{"position":{"x":10475.47577061491,"y":17396.786357234192},"targetPosition":{"x":10853.79316179686,"y":17671.65003101192},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 30","id":"4bc7f337-74a7-42b7-89b7-9d4edc10fca5"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a65fe0bd-5517-45b2-ac29-16bd198653a5","savedPayload":{"position":{"x":11000.184858959165,"y":17221.10271467635},"targetPosition":{"x":10853.79316179686,"y":17671.65003101192},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 30","id":"ffea0ed5-39b4-4c14-b1fc-2164a6e189fd"}}}],"id":"d76f185f-5219-4f0b-adb3-9a339b0468c6","savedPayload":{"position":{"x":10853.79316179686,"y":17671.65003101192},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Area From 30 to 70","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.1,\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.2,\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n }\n}","id":"2bdd38d2-0e04-4760-a8e7-af12d8a3baa7","savedPayload":{"position":{"x":11305.448108364548,"y":17182.863892148605},"targetPosition":{"x":10952.530031791608,"y":17500.632755600695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"de6f5e6b-a281-4d29-af86-72f7a545257d","savedPayload":{"position":{"x":10952.530031791608,"y":17500.632755600695},"targetPosition":{"x":10599.611955218668,"y":17818.401619052784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e9bfd52e-bc47-469f-8187-4e79d79ba866","savedPayload":{"position":{"x":10752.245764841278,"y":18288.160182144635},"targetPosition":{"x":10599.611955218668,"y":17818.401619052784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 30","id":"ffea0ed5-39b4-4c14-b1fc-2164a6e189fd"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"f562f71b-2e79-4fa0-a9d5-e1d5c4ba12b4","savedPayload":{"position":{"x":10217.641356659069,"y":18095.919503353995},"targetPosition":{"x":10599.611955218668,"y":17818.401619052784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 30","id":"4bc7f337-74a7-42b7-89b7-9d4edc10fca5"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"3c79161c-e899-4147-bd75-21805b23f76f","savedPayload":{"position":{"x":10214.622807943546,"y":17538.690630736888},"targetPosition":{"x":10599.611955218668,"y":17818.401619052784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 70","id":"84bdd434-48af-4f4a-8f14-62df15022d05"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"4cd041c2-e445-46eb-99bf-c3ceee94f101","savedPayload":{"position":{"x":10753.509946515336,"y":17344.75230483469},"targetPosition":{"x":10599.611955218668,"y":17818.401619052784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 70","id":"827e424f-3036-4645-9246-94b4bb0ebaea"}}}],"id":"1a7969dd-af18-4e44-b4c9-96dd2be57f7c","savedPayload":{"position":{"x":10599.611955218668,"y":17818.401619052784},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Area From 70 to 100","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.2,\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n }\n}","id":"b34d9e0e-ed4a-4b99-8e29-b7ed66a3658e","savedPayload":{"position":{"x":11252.309766534274,"y":17688.441442814878},"targetPosition":{"x":10780.013193389768,"y":17738.08181295383},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2c35b477-500f-4c85-867f-630e2105278f","savedPayload":{"position":{"x":10780.013193389768,"y":17738.08181295383},"targetPosition":{"x":10307.716620245263,"y":17787.722183092785},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"1777fb76-c1e2-415d-a517-2e365401490c","savedPayload":{"position":{"x":10451.890764319267,"y":18231.44457279568},"targetPosition":{"x":10307.716620245263,"y":17787.722183092785},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 70","id":"827e424f-3036-4645-9246-94b4bb0ebaea"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"4652d8cf-d922-44a3-b8cf-c6d329883ca0","savedPayload":{"position":{"x":9916.510771894213,"y":18071.949869124237},"targetPosition":{"x":10307.716620245263,"y":17787.722183092785},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 70","id":"84bdd434-48af-4f4a-8f14-62df15022d05"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"ef3e750e-8380-40a2-830b-3f364960e44e","savedPayload":{"position":{"x":9940.230876307745,"y":17520.72816168649},"targetPosition":{"x":10307.716620245263,"y":17787.722183092785},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 100","id":"8949ce4a-bdad-47c2-a85f-0728ed7f9746"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"c8905f10-3c25-4fec-978a-ef356c0ad395","savedPayload":{"position":{"x":10446.454541889769,"y":17360.73076566558},"targetPosition":{"x":10307.716620245263,"y":17787.722183092785},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 100","id":"ec589261-f564-4ad0-b866-8c63b8a15150"}}}],"id":"cb1bcd11-0fb0-4377-bff2-dee4630e0f3f","savedPayload":{"position":{"x":10307.716620245263,"y":17787.722183092785},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Line 30","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [3,3],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n }\n}","id":"3f69a3f7-af79-47a2-9e0b-17524fc986d8","savedPayload":{"position":{"x":10564.499368385226,"y":18413.87788800291},"targetPosition":{"x":10327.050311032088,"y":18002.604056457938},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"30e98617-1cfd-4fa7-814c-a5898caac0da","savedPayload":{"position":{"x":10327.050311032088,"y":18002.604056457938},"targetPosition":{"x":10089.60125367895,"y":17591.330224912967},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"2f5d4ab1-0d70-4baa-988a-2b7077e7b1e6","savedPayload":{"position":{"x":9846.642997892028,"y":18012.14626805423},"targetPosition":{"x":10089.60125367895,"y":17591.330224912967},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 30","id":"ffea0ed5-39b4-4c14-b1fc-2164a6e189fd"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"b23626ca-e521-4fad-9a8b-8c9f30711de7","savedPayload":{"position":{"x":9855.088576978707,"y":17185.142353849173},"targetPosition":{"x":10089.60125367895,"y":17591.330224912967},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 30","id":"4bc7f337-74a7-42b7-89b7-9d4edc10fca5"}}}],"id":"857043cc-f467-44d2-89eb-2e73f35f638d","savedPayload":{"position":{"x":10089.60125367895,"y":17591.330224912967},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Line 30","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [3,3],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n }\n}","id":"9e67e8dc-0a07-4d57-be52-6023ff9087da","savedPayload":{"position":{"x":9929.297771801508,"y":18248.83394370989},"targetPosition":{"x":9978.938141940462,"y":17776.537370565384},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"767e0eba-ad98-49a9-a08d-58b5ebfaf655","savedPayload":{"position":{"x":9978.938141940462,"y":17776.537370565384},"targetPosition":{"x":10028.578512079415,"y":17304.240797420876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"9bed0cf3-56c3-4508-88c8-5dede6e574df","savedPayload":{"position":{"x":9786.882506497019,"y":17722.870559076036},"targetPosition":{"x":10028.578512079415,"y":17304.240797420876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 70","id":"84bdd434-48af-4f4a-8f14-62df15022d05"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"1ce29315-82a5-4b62-8e85-be087a037d73","savedPayload":{"position":{"x":9782.10447534757,"y":16877.335243054724},"targetPosition":{"x":10028.578512079415,"y":17304.240797420876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 70","id":"827e424f-3036-4645-9246-94b4bb0ebaea"}}}],"id":"984a1917-1d81-4142-a50f-269a18a07c9d","savedPayload":{"position":{"x":10028.578512079415,"y":17304.240797420876},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Bandwidth","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 2,\n \"lineDash\": [2,2],\n \"paletteColor\": \"UI_COLOR.GREY\"\n },\n \"atMousePosition\": {\n \"opacity\": 1,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GREY\"\n }\n}","id":"ccb32f55-1b5e-4813-b614-0581593434a7","savedPayload":{"position":{"x":9512.419281746803,"y":17741.94845725362},"targetPosition":{"x":9830.188145198892,"y":17389.03038068068},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"57a26967-b019-4352-9d9d-cc10668837c4","savedPayload":{"position":{"x":9830.188145198892,"y":17389.03038068068},"targetPosition":{"x":10147.957008650981,"y":17036.11230410774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"0d8a9c6b-b0e6-4ce1-b1ad-71bd2a506a55","savedPayload":{"position":{"x":9916.779947271552,"y":17436.52271996138},"targetPosition":{"x":10147.957008650981,"y":17036.11230410774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Being Previous Bandwidth","id":"943da81e-c0ca-4213-ba8a-5dbafd3ead18"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"38f87d68-5678-46bd-9e26-45b8a8ee75f1","savedPayload":{"position":{"x":9906.560464341346,"y":16618.001224591902},"targetPosition":{"x":10147.957008650981,"y":17036.11230410774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End Bandwidth","id":"cdf38478-d5d4-410e-98fd-14b7b74bfd8b"}}}],"id":"0f7a4004-177c-4412-b0ef-2608f3c13da6","savedPayload":{"position":{"x":10147.957008650981,"y":17036.11230410774},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Value","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.85,\n \"lineWidth\": 2.25,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"6e43dca6-e750-49ff-b025-2a76185e5914","savedPayload":{"position":{"x":9473.097312243268,"y":17086.83445605637},"targetPosition":{"x":9937.61776373622,"y":16988.097586061624},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"56c0efd0-d13a-4c29-98f2-d27cb6071099","savedPayload":{"position":{"x":9937.61776373622,"y":16988.097586061624},"targetPosition":{"x":10402.138215229172,"y":16889.360716066876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"68e8f2a3-54ee-4fb7-abd2-add6496c08e0","savedPayload":{"position":{"x":10157.40190749224,"y":17313.256435524054},"targetPosition":{"x":10402.138215229172,"y":16889.360716066876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin Previous Value","id":"534c2cd4-d020-43cf-9284-433a57fdcfce"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a01aa765-6bd5-49d7-afbe-44d543191f76","savedPayload":{"position":{"x":10156.655779503297,"y":16464.1726650239},"targetPosition":{"x":10402.138215229172,"y":16889.360716066876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End Value","id":"5b05abcc-76cf-4f9d-82e5-671ab7471678"}}}],"id":"4598a671-83b9-4460-9e65-175df73eba71","savedPayload":{"position":{"x":10402.138215229172,"y":16889.360716066876},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Moving Average","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.55,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"544bf8f4-dfd5-4cd7-aba3-a47f6b477da8","savedPayload":{"position":{"x":9875.991889275621,"y":17006.019795214066},"targetPosition":{"x":10260.192534669623,"y":16726.88168704822},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[{"type":"Style Condition","name":"New Style Condition","code":"record.previous.movingAverage > record.movingAverage","style":{"type":"Style","name":"New Style","code":"{\n \"opacity\": 0.55,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RED\"\n}","id":"c89114e1-7181-4091-8c2d-0301a8b00478","savedPayload":{"position":{"x":10160.911794391715,"y":15782.288540759208},"targetPosition":{"x":10210.552164530669,"y":16254.585113903713},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"12891c27-2ec5-4a56-892e-1d29a277d5da","savedPayload":{"position":{"x":10210.552164530669,"y":16254.585113903713},"targetPosition":{"x":10260.192534669623,"y":16726.88168704822},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"c9847967-ecf5-4f6a-9fe7-2759ca4795b6","savedPayload":{"position":{"x":10260.192534669623,"y":16726.88168704822},"targetPosition":{"x":10694.033550202577,"y":16920.040152026875},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"a"}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"81660820-6035-481b-a909-9e89e145d61f","savedPayload":{"position":{"x":10467.302749965713,"y":17312.749417677875},"targetPosition":{"x":10694.033550202577,"y":16920.040152026875},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin Previous MA","id":"0993c95f-2054-48d8-9977-2d4eb24dabf7"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"2d18f4cf-e5d4-4bde-9a69-85b87c410125","savedPayload":{"position":{"x":10460.732966404825,"y":16515.951687453686},"targetPosition":{"x":10694.033550202577,"y":16920.040152026875},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End MA","id":"35bdf97d-abf8-4f1e-922c-d853e6980b0c"}}}],"id":"69c19382-ed5b-48d1-9249-426a8e1e4e51","savedPayload":{"position":{"x":10694.033550202577,"y":16920.040152026875},"targetPosition":{"x":10500.87508522392,"y":17353.88116755983},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"da344cd9-3c37-449a-b5be-6c65814f8562","savedPayload":{"position":{"x":10500.87508522392,"y":17353.88116755983},"targetPosition":{"x":10912.148916768892,"y":17116.432110206693},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"%B Panel","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"%B Value","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.value","id":"c6ca5acc-cc3d-4802-8627-d3461747e731","savedPayload":{"position":{"x":9962.352687356339,"y":16641.533995500416},"targetPosition":{"x":10437.250802062616,"y":16641.533995500416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d34065b9-ce7c-4971-8270-d0819da6b188","savedPayload":{"position":{"x":10437.250802062616,"y":16641.533995500416},"targetPosition":{"x":10912.148916768892,"y":16641.533995500416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"Bandwidth","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.bandwidth","id":"5193f538-5133-4561-a9e4-939cfa4d9af9","savedPayload":{"position":{"x":10912.148916768892,"y":15691.737766087863},"targetPosition":{"x":10912.148916768892,"y":16166.63588079414},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ba215bbb-99c2-4433-83f8-47f2b00ee378","savedPayload":{"position":{"x":10912.148916768892,"y":16166.63588079414},"targetPosition":{"x":10912.148916768892,"y":16641.533995500416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"Moving Average","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.movingAverage","id":"d721b3b0-ffe7-459f-b146-954073605f0e","savedPayload":{"position":{"x":11861.945146181446,"y":16641.533995500416},"targetPosition":{"x":11387.04703147517,"y":16641.533995500416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8705ee2c-1bd8-4e90-b841-d2864f83f14b","savedPayload":{"position":{"x":11387.04703147517,"y":16641.533995500416},"targetPosition":{"x":10912.148916768892,"y":16641.533995500416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"36548f31-d96c-4c80-b923-dea5dd5b56c7","savedPayload":{"position":{"x":10912.148916768892,"y":16641.533995500416},"targetPosition":{"x":10912.148916768892,"y":17116.432110206693},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"06b2f3f6-b138-415f-8974-31c22a930f92","savedPayload":{"position":{"x":10912.148916768892,"y":17116.432110206693},"targetPosition":{"x":11323.422748313864,"y":17353.881167559834},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Bollinger Bands","code":"{\n\"codeName\": \"BollingerBands\",\n\"isLegacy\": true,\n\"icon\": \"band\"\n}","panels":[{"type":"Plotter Panel","name":"New Plotter Panel","code":"{ \n\"codeName\": \"BollingerBandsPanel\" ,\n\"isLegacy\": true\n}","panelData":[],"id":"de7b7008-6f66-4d7d-96dd-1aa7b1ede335","savedPayload":{"position":{"x":12145.970411403807,"y":16878.983052853553},"targetPosition":{"x":11734.696579858835,"y":17116.432110206693},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a11bfdf8-595c-4b50-b259-6a9f777d0ea8","savedPayload":{"position":{"x":11734.696579858835,"y":17116.432110206693},"targetPosition":{"x":11323.422748313864,"y":17353.881167559834},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"abd0f050-2f42-4c50-81a9-ffb0d6442cef","savedPayload":{"position":{"x":11323.422748313864,"y":17353.881167559834},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter","name":"Plotters-Bollinger-Channels-Objects","code":"{\n\"codeName\" : \"Plotter Bollinger Channels\"\n}","modules":[{"type":"Plotter Module","name":"Bollinger Channels Objects","code":"{\n\"codeName\": \"Bollinger-Channels-Objects\",\n \"icon\": \"trend\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"New Point","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.firstMovingAverage - record.firstDeviation","id":"8117b381-e9c7-41e3-8a51-ed49a1ca6868","savedPayload":{"position":{"x":11089.943458004851,"y":15493.678789352289},"targetPosition":{"x":11273.024275255104,"y":15685.297939083286},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9be61e7d-ae1e-4b39-9ebf-27285a5a67a9","savedPayload":{"position":{"x":11273.024275255104,"y":15685.297939083286},"targetPosition":{"x":11460.69447999367,"y":15872.661401612058},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}},{"type":"Point","name":"New Point","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.lastMovingAverage - record.lastDeviation","id":"69cb3498-4e18-458a-b48e-e65c98b183be","savedPayload":{"position":{"x":11160.838988036045,"y":15434.857668851837},"targetPosition":{"x":11311.293295983962,"y":15653.119999825396},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f87b3204-8901-4774-afcb-b4e5394e255d","savedPayload":{"position":{"x":11311.293295983962,"y":15653.119999825396},"targetPosition":{"x":11460.69447999367,"y":15872.661401612058},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}},{"type":"Point","name":"New Point","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.lastMovingAverage + record.lastDeviation","id":"4c7b46d8-f76b-4039-af87-345c3f72a0a4","savedPayload":{"position":{"x":11234.50650106856,"y":15392.851743126912},"targetPosition":{"x":11348.562258352003,"y":15632.144365896409},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5ce82101-6f77-4f54-bef5-d97edae6d44b","savedPayload":{"position":{"x":11348.562258352003,"y":15632.144365896409},"targetPosition":{"x":11460.69447999367,"y":15872.661401612058},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}},{"type":"Point","name":"New Point","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.firstMovingAverage + record.lastDeviation","id":"89e5dbc2-33ed-42b9-9b9b-376097e04aff","savedPayload":{"position":{"x":11315.22506772331,"y":15363.004236396482},"targetPosition":{"x":11389.766954911436,"y":15617.310092424661},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"45dbe6c3-9433-4b6d-8aa5-691cf6eba8a1","savedPayload":{"position":{"x":11389.766954911436,"y":15617.310092424661},"targetPosition":{"x":11460.69447999367,"y":15872.661401612058},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}}],"id":"703b0e1b-e2d6-457a-95d6-7b6516b2f772","savedPayload":{"position":{"x":11460.69447999367,"y":15872.661401612058},"targetPosition":{"x":11724.918520693143,"y":16330.0426526243},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"New Polygon","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.2\n },\n \"atMousePosition\": {\n \"opacity\": 0.2,\n \"paletteColor\": \"UI_COLOR.TITANIUM_YELLOW\"\n }\n}","id":"09105647-a665-4fb4-910d-49bcf90eb91e","savedPayload":{"position":{"x":11811.915535872633,"y":14838.933196927333},"targetPosition":{"x":11969.243396069864,"y":15343.978252040546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"styleConditions":[{"type":"Style Condition","name":"New Style Condition","code":"record.direction === 'Side'","style":{"type":"Style","name":"New Style","code":"{\n \"paletteColor\": \"UI_COLOR.LIGHT\"\n}","id":"ad17706f-09f0-4424-b5f3-b66fb2c76160","savedPayload":{"position":{"x":11791.314861388939,"y":14301.252839991928},"targetPosition":{"x":11880.095773494155,"y":14822.469246845927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3975ecc2-db5b-4cab-a9ab-9e13b2da3015","savedPayload":{"position":{"x":11880.095773494155,"y":14822.469246845927},"targetPosition":{"x":11969.243396069864,"y":15343.978252040546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Style Condition","name":"New Style Condition","code":"record.direction === 'Up'","style":{"type":"Style","name":"New Style","code":"{\n \"paletteColor\": \"UI_COLOR. GREEN\"\n}","id":"0b263642-8fc7-4fdc-a915-9480c8493e68","savedPayload":{"position":{"x":11929.541858761027,"y":14286.826945482286},"targetPosition":{"x":11949.242431062146,"y":14815.22527452787},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e218ed70-9da7-43c5-9c23-9e010e08b5b5","savedPayload":{"position":{"x":11949.242431062146,"y":14815.22527452787},"targetPosition":{"x":11969.243396069864,"y":15343.978252040546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Style Condition","name":"New Style Condition","code":"record.direction === 'Down'","style":{"type":"Style","name":"New Style","code":"{\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n}","id":"ce3abe3f-eb38-4e0f-94bb-9c397ed78f49","savedPayload":{"position":{"x":12067.61854623178,"y":14290.660229449577},"targetPosition":{"x":12018.336189126043,"y":14817.124566696122},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"251d5031-13f9-493b-aa35-126f5221bb21","savedPayload":{"position":{"x":12018.336189126043,"y":14817.124566696122},"targetPosition":{"x":11969.243396069864,"y":15343.978252040546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Style Condition","name":"New Style Condition","code":"atMousePosition === true","style":{"type":"Style","name":"New Style","code":"{\n \"paletteColor\": \"UI_COLOR.TITANIUM_YELLOW\",\n \"opacity\": 0.1\n}","id":"2ea26fbc-74c5-4fb5-b64f-c4c7b289003c","savedPayload":{"position":{"x":12204.494103530913,"y":14312.972832765105},"targetPosition":{"x":12086.630543635747,"y":14828.407327657918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"id":"8a0f9c00-67be-47fa-9cde-ec6c499c9bec","savedPayload":{"position":{"x":12086.630543635747,"y":14828.407327657918},"targetPosition":{"x":11969.243396069864,"y":15343.978252040546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"f2d2ac14-9859-4228-81d9-280c639c1b37","savedPayload":{"position":{"x":11969.243396069864,"y":15343.978252040546},"targetPosition":{"x":11989.482756590687,"y":15872.006759374886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.25,\n \"lineWidth\": 1,\n \"lineDash\": [0,0]\n },\n \"atMousePosition\": {\n \"opacity\": 0.35,\n \"lineWidth\": 1,\n \"lineDash\": [0,0]\n }\n}","id":"d85d9603-7ff2-4d45-aa58-9b8ebe23a65f","savedPayload":{"position":{"x":12068.03197194864,"y":15088.478852453649},"targetPosition":{"x":12087.730985466773,"y":15352.89757054276},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}},"styleConditions":[{"type":"Style Condition","name":"New Style Condition","code":"record.direction === 'Side'","style":{"type":"Style","name":"New Style","code":"{\n \"paletteColor\": \"UI_COLOR.DARK\"\n}","id":"d09de0b4-f477-42e9-a944-6bde9a9a68dd","savedPayload":{"position":{"x":12147.673842243628,"y":14825.357952673165},"targetPosition":{"x":12118.022120313864,"y":15089.023048797266},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"id":"08167019-4685-45ca-8572-74706f2fb971","savedPayload":{"position":{"x":12118.022120313864,"y":15089.023048797266},"targetPosition":{"x":12087.730985466773,"y":15352.89757054276},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}},{"type":"Style Condition","name":"New Style Condition","code":"record.direction === 'Up'","style":{"type":"Style","name":"New Style","code":"{\n \"paletteColor\": \"UI_COLOR.PATINATED_TURQUOISE\"\n}","id":"ebab2c53-c230-4622-a5d1-a3e0bd1d4139","savedPayload":{"position":{"x":12230.073924133634,"y":14841.682617772187},"targetPosition":{"x":12159.9431673098,"y":15097.434783865474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5b721945-b84b-4e19-a7db-777bc1c1fee1","savedPayload":{"position":{"x":12159.9431673098,"y":15097.434783865474},"targetPosition":{"x":12087.730985466773,"y":15352.89757054276},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}},{"type":"Style Condition","name":"New Style Condition","code":"record.direction === 'Down'","style":{"type":"Style","name":"New Style","code":"{\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n}","id":"8bef9d11-9184-420e-a841-97a19f6a160b","savedPayload":{"position":{"x":12310.616848014355,"y":14871.703978713105},"targetPosition":{"x":12200.849997436157,"y":15113.030325478972},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a530140d-d0bb-4989-8ca8-d9846adbc91d","savedPayload":{"position":{"x":12200.849997436157,"y":15113.030325478972},"targetPosition":{"x":12087.730985466773,"y":15352.89757054276},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":2},"uiObject":{}}}],"id":"add24099-1310-4e86-99fc-9db01a272b6e","savedPayload":{"position":{"x":12087.730985466773,"y":15352.89757054276},"targetPosition":{"x":11989.482756590687,"y":15872.006759374886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"7be4c16d-772a-4750-9735-09d3d9e16037","savedPayload":{"position":{"x":11749.039059303836,"y":15936.560918367039},"targetPosition":{"x":11989.482756590687,"y":15872.006759374886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"New Point","id":"9be61e7d-ae1e-4b39-9ebf-27285a5a67a9"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"1a592de1-eb28-41eb-8ddb-b22e5034be53","savedPayload":{"position":{"x":11904.109778896562,"y":15872.089727431881},"targetPosition":{"x":11989.482756590687,"y":15872.006759374886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"New Point","id":"f87b3204-8901-4774-afcb-b4e5394e255d"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e90a30b1-cd5d-4331-bf74-7cfd4242db34","savedPayload":{"position":{"x":11826.89432542803,"y":15828.363783509814},"targetPosition":{"x":11989.482756590687,"y":15872.006759374886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"New Point","id":"5ce82101-6f77-4f54-bef5-d97edae6d44b"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"ae4311e5-a02d-46d7-a41c-79e6945f9f21","savedPayload":{"position":{"x":11750.939307395174,"y":15734.120876188674},"targetPosition":{"x":11989.482756590687,"y":15872.006759374886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"New Point","id":"45dbe6c3-9433-4b6d-8aa5-691cf6eba8a1"}}}],"id":"6e21446c-93e7-44ad-814d-3cd2fa8f8baf","savedPayload":{"position":{"x":11989.482756590687,"y":15872.006759374886},"targetPosition":{"x":11724.918520693143,"y":16330.0426526243},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"id":"0b08f6f6-2317-42fb-ab3a-db8957d76dfa","savedPayload":{"position":{"x":11724.918520693143,"y":16330.0426526243},"targetPosition":{"x":11724.918988992453,"y":16858.576123416715},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{"shortcutKey":"o"}}},"panels":[{"type":"Plotter Panel","name":"Channel Panel","code":"{ \n\"codeName\": \"\"\n}","javascriptCode":{"type":"Javascript Code","code":" printLabel('Direction', X_AXIS, PANEL_HEIGHT * 0.05, '1')\n printLabel(currentRecord.data.direction, X_AXIS, PANEL_HEIGHT * 0.10, '0.50')\n\n printLabel('Periods', X_AXIS, PANEL_HEIGHT * 0.15, '1')\n printLabel(currentRecord.data.period, X_AXIS, PANEL_HEIGHT * 0.20, '0.50')\n\n let fixedValue = ''\n\n fixedValue = currentRecord.data.firstMovingAverage\n if (fixedValue !== undefined) { fixedValue = fixedValue.toFixed(2) } else { fixedValue = '' }\n\n printLabel('Initial Moving Average', X_AXIS, PANEL_HEIGHT * 0.25, '1')\n printLabel(fixedValue, X_AXIS, PANEL_HEIGHT * 0.30, '0.50')\n\n fixedValue = currentRecord.data.lastMovingAverage\n if (fixedValue !== undefined) { fixedValue = fixedValue.toFixed(2) } else { fixedValue = '' }\n\n printLabel('Final Moving Average', X_AXIS, PANEL_HEIGHT * 0.35, '1')\n printLabel(fixedValue, X_AXIS, PANEL_HEIGHT * 0.40, '0.50')","id":"ab8e6fc4-e617-4f43-bd23-9887616a3bf2","savedPayload":{"position":{"x":11989.576247626113,"y":16400.122682656915},"targetPosition":{"x":11857.20645378497,"y":16629.223276726},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"id":"09c8f781-3e54-4bed-bd9a-83e8179fdd66","savedPayload":{"position":{"x":11857.20645378497,"y":16629.223276726},"targetPosition":{"x":11724.918988992453,"y":16858.576123416715},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}}],"id":"52881346-3e4b-45e0-92d2-af130d381b8e","savedPayload":{"position":{"x":11724.918988992453,"y":16858.576123416715},"targetPosition":{"x":11588.258105378642,"y":17370.07098818927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Bollinger Sub Channels Objects","code":"{\n\"codeName\": \"Bollinger-Sub-Channels-Objects\",\n\"icon\": \"trend\"\n}","javascriptCode":{"type":"Javascript Code","code":"/* Definition of the points we are going to use while drawing */\nlet point1\nlet point2\nlet point3\nlet point4\n\npoint1 = {\n x: record.begin,\n y: record.firstMovingAverage - record.firstDeviation\n}\n\npoint2 = {\n x: record.end,\n y: record.lastMovingAverage - record.lastDeviation\n}\n\npoint3 = {\n x: record.end,\n y: record.lastMovingAverage + record.lastDeviation\n}\n\npoint4 = {\n x: record.begin,\n y: record.firstMovingAverage + record.firstDeviation\n}\n\n/* Contributing to Auto-Scale*/\ncoordinateSystem.reportValue(record.firstMovingAverage - record.firstDeviation)\ncoordinateSystem.reportValue(record.lastMovingAverage - record.lastDeviation)\ncoordinateSystem.reportValue(record.lastMovingAverage + record.lastDeviation)\ncoordinateSystem.reportValue(record.firstMovingAverage + record.firstDeviation)\n\n/*\nThe information we store in files is independent from the charing system and its coordinate systems.\nThat means that the first thing we allways need to do is to trasform these points to the coordinate system of the timeline.\n*/\npoint1 = coordinateSystem.transformThisPoint(point1)\npoint2 = coordinateSystem.transformThisPoint(point2)\npoint3 = coordinateSystem.transformThisPoint(point3)\npoint4 = coordinateSystem.transformThisPoint(point4)\n\n/*\nThe browser canvas object does not care about our timeline and its coordinate system. In order to draw on a html canvas we\nneed the points to be converted into the canvas coordinate system.\nNext we transform again to the screen coordinate system.\n*/\npoint1 = transformThisPoint(point1, thisObject.container)\npoint2 = transformThisPoint(point2, thisObject.container)\npoint3 = transformThisPoint(point3, thisObject.container)\npoint4 = transformThisPoint(point4, thisObject.container)\n\n/* We make sure the points do not fall outside the canvas.chartingSpace.viewport visible area. This step allways need to be done. */\npoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1)\npoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2)\npoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3)\npoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point4)\n\npoint1 = thisObject.fitFunction(point1);\npoint2 = thisObject.fitFunction(point2);\npoint3 = thisObject.fitFunction(point3);\npoint4 = thisObject.fitFunction(point4);\n\n/*\nNow that we have our 4 points as coordinates of the actual screen (or html canvas object), we can actaully start drawing.\nFirst we are going to draw the shape backgound.\n*/\nbrowserCanvasContext.beginPath()\n\nbrowserCanvasContext.moveTo(point1.x, point1.y)\nbrowserCanvasContext.lineTo(point2.x, point2.y)\nbrowserCanvasContext.lineTo(point3.x, point3.y)\nbrowserCanvasContext.lineTo(point4.x, point4.y)\n\nbrowserCanvasContext.closePath()\n\nlet opacity = '0.25' // The backgound will be quite transparent. Possible values goes from 0..1\n\n/* Depending on the direction of the channel, we use a different color for the border lines. */\nif (record.direction === 'Side') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')' }\nif (record.direction === 'Up') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')' }\nif (record.direction === 'Down') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')' }\n\nswitch (record.slope) {\n case 'Side': {\n opacity = '0.1';\n break;\n }\n case 'Gentle': {\n opacity = '0.1';\n break;\n }\n case 'Medium': {\n opacity = '0.15';\n break;\n }\n case 'Steep': {\n opacity = '0.2';\n break;\n }\n case 'Extreme': {\n opacity = '0.25';\n break;\n }\n}\n\n/* Depending on the direction of the channel, we use a different color for the background. */\nif (record.direction === 'Side' || record.slope === 'Side') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.LIGHT + ', ' + opacity + ')'; }\nif (record.direction === 'Up') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')' }\nif (record.direction === 'Down') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')' }\n\n/* If the mouse is positioned in betwheen the begining and ending of our object, we can opt for changing the color of that specific object. */\nif (atMousePosition) {\n browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.1)'\n}\n\nbrowserCanvasContext.fill() // This is what actually apply the previous instructions on the html canvas object of the browser.\n\n/* Second we are going to draw the shape borders. */\nbrowserCanvasContext.beginPath()\n\nbrowserCanvasContext.moveTo(point1.x, point1.y)\nbrowserCanvasContext.lineTo(point2.x, point2.y)\nbrowserCanvasContext.lineTo(point3.x, point3.y)\nbrowserCanvasContext.lineTo(point4.x, point4.y)\n\nbrowserCanvasContext.closePath()\n\nbrowserCanvasContext.lineWidth = 1 // We use 1 as standard line width\nbrowserCanvasContext.setLineDash([0, 0]) // We do not want dashed lines in this case.\nbrowserCanvasContext.stroke() // This actually draw the borders of our shape.","id":"63406674-a797-430a-96c7-e5bcacb286bc","savedPayload":{"position":{"x":12227.465921681665,"y":16538.188884673058},"targetPosition":{"x":11962.966222422501,"y":16995.918311630838},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"Sub-Channel Panel","code":"{ \n\"codeName\": \"\"\n}","javascriptCode":{"type":"Javascript Code","code":"printLabel('Direction', X_AXIS, PANEL_HEIGHT * 0.05, '1');\nprintLabel(currentRecord.data.direction, X_AXIS, PANEL_HEIGHT * 0.10, '0.50');\n\nprintLabel('Periods', X_AXIS, PANEL_HEIGHT * 0.15, '1');\nprintLabel(currentRecord.data.period, X_AXIS, PANEL_HEIGHT * 0.20, '0.50');\n\nlet fixedValue = '';\n\nfixedValue = currentRecord.data.firstMovingAverage;\nif (fixedValue !== undefined) { fixedValue = fixedValue.toFixed(2); } else { fixedValue = ''; }\n\nprintLabel('Initial Moving Average', X_AXIS, PANEL_HEIGHT * 0.25, '1');\nprintLabel(fixedValue, X_AXIS, PANEL_HEIGHT * 0.30, '0.50');\n\nfixedValue = currentRecord.data.lastMovingAverage;\nif (fixedValue !== undefined) { fixedValue = fixedValue.toFixed(2); } else { fixedValue = ''; }\n\nprintLabel('Final Moving Average', X_AXIS, PANEL_HEIGHT * 0.35, '1');\nprintLabel(fixedValue, X_AXIS, PANEL_HEIGHT * 0.40, '0.50');\n\nprintLabel('Slope', X_AXIS, PANEL_HEIGHT * 0.45, '1');\nprintLabel(currentRecord.data.slope, X_AXIS, PANEL_HEIGHT * 0.50, '0.50');","id":"60b78d10-ba3e-4530-b6d6-22dd3bafbdc0","savedPayload":{"position":{"x":12878.821743625873,"y":16467.62262468272},"targetPosition":{"x":12420.975250853648,"y":16731.77856925905},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0ce6611c-1e9a-4fb6-8a5f-809c8b65f89a","savedPayload":{"position":{"x":12420.975250853648,"y":16731.77856925905},"targetPosition":{"x":11962.966222422501,"y":16995.918311630838},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"id":"f8c4b699-1f40-4832-b427-68da3341daa8","savedPayload":{"position":{"x":11962.966222422501,"y":16995.918311630838},"targetPosition":{"x":11588.258105378642,"y":17370.07098818927},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{"shortcutKey":"x"}}}],"id":"343f37be-cc77-4513-a296-7925edfcf0c4","savedPayload":{"position":{"x":11588.258105378642,"y":17370.07098818927},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter","name":"Plotters-Trading-Simulation","code":"{\n\"codeName\" : \"PlottersTradingSimulation\"\n}","modules":[{"type":"Plotter Module","name":"Trading Simulation","code":"{\n\"codeName\": \"TradingSimulation\",\n\"isLegacy\": true,\n\"icon\": \"placeholder\"\n}","panels":[{"type":"Plotter Panel","name":"Trading Simulation Panel","code":"{ \n\"codeName\": \"TradingSimulationPanel\" ,\n\"isLegacy\": true\n}","panelData":[],"id":"0061c7b0-341d-41a7-a0a6-b675f25b7da8","savedPayload":{"position":{"x":10924.693160650875,"y":17138.15937795282},"targetPosition":{"x":11323.422748313864,"y":17368.366012723916},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"7864b9e4-3150-4d1d-abd9-0a43a9fe4c4f","savedPayload":{"position":{"x":11323.422748313864,"y":17368.366012723916},"targetPosition":{"x":11722.152335976853,"y":17598.572647495013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Conditions","code":"{\n\"codeName\": \"Conditions\",\n\"isLegacy\": true,\n\"icon\": \"band\"\n}","panels":[],"id":"546d0a94-e878-41c1-a6e6-7dd28832f79b","savedPayload":{"position":{"x":11722.152335976853,"y":17138.159377952823},"targetPosition":{"x":11722.152335976853,"y":17598.572647495013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Strategies","code":"{\n\"codeName\": \"Strategies\",\n\"isLegacy\": true,\n\"icon\": \"quality\"\n}","panels":[],"id":"9b0c4f4b-d67c-48ae-97f1-bd614e85ced8","savedPayload":{"position":{"x":12120.881923639841,"y":17368.366012723916},"targetPosition":{"x":11722.152335976853,"y":17598.572647495013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Trades","code":"{\n\"codeName\": \"Trades\",\n\"isLegacy\": true,\n\"icon\": \"band\"\n}","panels":[],"id":"382004c6-e318-4d9d-9791-505f329f1e54","savedPayload":{"position":{"x":12120.881923639841,"y":17828.77928226611},"targetPosition":{"x":11722.152335976853,"y":17598.572647495013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"Simulation Execution","code":"{\n\"codeName\": \"SimulationExecution\",\n\"isLegacy\": true,\n\"icon\": \"band\"\n}","panels":[{"type":"Plotter Panel","name":"Simulation Execution Panel","code":"{ \n\"codeName\": \"SimulationExecutionPanel\" ,\n\"isLegacy\": true\n}","panelData":[],"id":"7a1177aa-d297-4452-bad5-58b681a740ca","savedPayload":{"position":{"x":11722.152335976853,"y":18519.399186579394},"targetPosition":{"x":11722.152335976853,"y":18058.985917037204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"96dede74-e7bc-4c50-a9ba-2bec8a5f4884","savedPayload":{"position":{"x":11722.152335976853,"y":18058.985917037204},"targetPosition":{"x":11722.152335976853,"y":17598.572647495013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"90f0675d-f4d1-480f-aa75-c8162d53ea0f","savedPayload":{"position":{"x":11722.152335976853,"y":17598.572647495013},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959","savedPayload":{"position":{"x":11323.422748313864,"y":17828.77928226611},"targetPosition":{"x":11323.422748313864,"y":17828.77928226611},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"m"}}},{"type":"Data Mine","name":"Sparta","code":"{\n\"codeName\": \"Sparta\"\n}","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"SMA","code":"{\n\"codeName\": \"SMA\",\n\"repo\": \"SMA-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates SMAs for Time Frames of 01-hs and above.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"5302effb-1f09-4a30-9f71-6956b8b9ca88","savedPayload":{"position":{"x":28869.168930679727,"y":18288.28951081023},"targetPosition":{"x":27714.069146229263,"y":18288.28951081023},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"3b21f9d7-2cbc-4c23-80c8-cc5e5b06a22c"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"8221593c-6298-4b4c-bfc2-cffbc9e5cd0f","savedPayload":{"position":{"x":27133.35738316016,"y":19294.11178899882},"targetPosition":{"x":27714.069146229263,"y":18288.28951081023},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"af9b9cef-2a74-486f-b79e-047d68669a1f"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"aabd0857-b4fc-404a-8967-967475ed3993","savedPayload":{"position":{"x":27133.35738316016,"y":17282.467232621642},"targetPosition":{"x":27714.069146229263,"y":18288.28951081023},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"78a6ef37-4df8-4ac8-83a0-c9a732467c59"}}}],"id":"85a56739-9351-444b-95e9-a57470dca38a","savedPayload":{"position":{"x":27714.069146229263,"y":18288.28951081023},"targetPosition":{"x":27835.47096261939,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"d49d1e94-1960-4121-8e40-be8af52947ee","savedPayload":{"position":{"x":27931.91972505695,"y":17605.62189048067},"targetPosition":{"x":26774.45777591658,"y":17605.62189048067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"81346f2f-13f2-46f4-85f5-a04b4d80f455"}}},{"type":"Status Dependency","name":"Ending: Olivia","code":"{\"mainUtility\": \"Market Ending Point\"}","id":"f7cf0949-06fc-4c98-a399-61fac1671c6e","savedPayload":{"position":{"x":26192.239908368574,"y":18614.052818148226},"targetPosition":{"x":26774.45777591658,"y":17605.62189048067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Olivia Candles","id":"4228fdf3-e992-46b2-a91e-de95108a753d","savedPayload":{"position":{"x":26196.723552975564,"y":16604.956863075506},"targetPosition":{"x":26774.45777591658,"y":17605.62189048067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}}],"id":"f404e748-b631-4cc5-975c-2a0736e55c0d","savedPayload":{"position":{"x":26774.45777591658,"y":17605.62189048067},"targetPosition":{"x":27835.47096261939,"y":17133.228384269958},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"81346f2f-13f2-46f4-85f5-a04b4d80f455","savedPayload":{"position":{"x":26895.859592306708,"y":16450.5607639404},"targetPosition":{"x":27835.47096261939,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Olivia","id":"2bce272e-2712-4f28-9736-03b51929aba2","savedPayload":{"position":{"x":26898.58103172783,"y":16452.538005417235},"targetPosition":{"x":27835.47096261939,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"b569db86-9ab5-4ba2-be41-357a9bce84b4","savedPayload":{"position":{"x":28896.4841493222,"y":16660.834878059246},"targetPosition":{"x":27835.47096261939,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"456269f6-9849-4d5f-9c8c-7cd7d1eef297","savedPayload":{"position":{"x":27835.47096261939,"y":17133.228384269958},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Describe what the process does.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Daily\",\n \"startDate\": {\n \"takeItFromStatusDependency\": 0\n },\n \"endDate\": {\n \"takeItFromStatusDependency\": 1\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"417d3141-1e22-4ee6-b2d6-531a55b95c88","savedPayload":{"position":{"x":28799.24399878378,"y":17574.422475524596},"targetPosition":{"x":27639.038697404194,"y":17574.422475524596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"2c338fe0-385f-45ab-b210-b98ef013228d"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"022edfeb-ec62-4cd9-85bf-20e9f444e3c1","savedPayload":{"position":{"x":27058.32693433509,"y":18580.24475371318},"targetPosition":{"x":27639.038697404194,"y":17574.422475524596},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"de5cfa30-edbf-4d5b-8d7f-facfb69a0f3e"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"42e89cd9-acbe-49cd-b6f6-7671317796d6","savedPayload":{"position":{"x":27058.32693433509,"y":16568.60019733601},"targetPosition":{"x":27639.038697404194,"y":17574.422475524596},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"f155e2ed-7082-4021-a4ff-a6f3cfd5683d"}}}],"id":"ad03dff7-56b2-4980-bc14-1b6c41fdf6cf","savedPayload":{"position":{"x":27639.038697404194,"y":17574.422475524596},"targetPosition":{"x":28416.182725688494,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"d674a833-b9c8-4bdb-84de-e007ab243860","savedPayload":{"position":{"x":28451.04008990263,"y":16469.84306281236},"targetPosition":{"x":27280.13909016061,"y":16469.84306281236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"d2727d60-b26b-4e8b-a635-fbe78bc81240"}}},{"type":"Status Dependency","name":"Ending: Olivia","code":"{\"mainUtility\": \"Market Ending Point\"}","id":"ba857809-2273-4280-aa19-4b50837abccb","savedPayload":{"position":{"x":27280.13909016061,"y":17631.01727363667},"targetPosition":{"x":27280.13909016061,"y":16469.84306281236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}},{"type":"Status Dependency","name":"Starting: Charly Historic Trades","code":"{\"mainUtility\": \"Market Starting Point\"}","id":"40e12ba8-59dc-4eaf-a30d-07bb8192e0a6","savedPayload":{"position":{"x":26120.03097902003,"y":16469.84306281236},"targetPosition":{"x":27280.13909016061,"y":16469.84306281236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Olivia Candles","id":"5f5e40cb-841e-4b18-a249-a04d541d31a0","savedPayload":{"position":{"x":27280.13909016061,"y":15308.21898296788},"targetPosition":{"x":27280.13909016061,"y":16469.84306281236},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}}}],"id":"b6aae3c7-2d70-4ffd-a5e5-951f0a565b13","savedPayload":{"position":{"x":27280.13909016061,"y":16469.84306281236},"targetPosition":{"x":28416.182725688494,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"d2727d60-b26b-4e8b-a635-fbe78bc81240","savedPayload":{"position":{"x":28057.283118444913,"y":15606.737179175045},"targetPosition":{"x":28416.182725688494,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Olivia","id":"8b753072-e4c9-4671-a5ce-eea9e1c1ba0f","savedPayload":{"position":{"x":27469.865061670094,"y":16023.776563975225},"targetPosition":{"x":28416.182725688494,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"87c9e783-adc5-40e3-b1fb-fdb2994c1217","savedPayload":{"position":{"x":29552.226361216377,"y":16952.7901209622},"targetPosition":{"x":28416.182725688494,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"333ae5d7-7ec1-4f08-b206-91e3b4f2c99f","savedPayload":{"position":{"x":28416.182725688494,"y":16711.31659188728},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Popular SMAs","code":"{\n \"codeName\": \"Popular-SMAs\",\n \"singularVariableName\": \"popularSMA\",\n \"pluralVariableName\": \"popularSMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Popular-SMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"3b21f9d7-2cbc-4c23-80c8-cc5e5b06a22c","savedPayload":{"position":{"x":27972.55841403745,"y":16711.31659188728},"targetPosition":{"x":29133.981940175654,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Popular-SMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Popular-SMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"2c338fe0-385f-45ab-b210-b98ef013228d","savedPayload":{"position":{"x":28775.082332932074,"y":15606.737179175045},"targetPosition":{"x":29133.981940175654,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"673a7fbf-1a73-4220-a561-84cf8b11c3a7","savedPayload":{"position":{"x":27834.455021017035,"y":15410.686069036254},"targetPosition":{"x":28954.024165752686,"y":15719.667520296987},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"305fc918-e6df-48d7-a7b5-726e3fa8f958","savedPayload":{"position":{"x":28954.024165752686,"y":15719.667520296987},"targetPosition":{"x":30073.593310488337,"y":16028.64897155772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"002ffd5e-583c-4af5-87eb-a886fd3bc50e","savedPayload":{"position":{"x":29160.65627424018,"y":13892.72659560029},"targetPosition":{"x":29617.12479236426,"y":14960.687783579006},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a305a94d-3af9-430a-8642-5ffb1229710f","savedPayload":{"position":{"x":29617.12479236426,"y":14960.687783579006},"targetPosition":{"x":30073.593310488337,"y":16028.64897155772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA20","code":"{\n \"codeName\": \"sma20\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma20","id":"5adb676d-35a8-47b2-aa47-dc0e6edf8a81","savedPayload":{"position":{"x":31174.317736279903,"y":13983.160236136517},"targetPosition":{"x":30623.95552338412,"y":15005.904603847119},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1657736e-6bc4-4279-9e65-5b0b0cf737cb","savedPayload":{"position":{"x":30623.95552338412,"y":15005.904603847119},"targetPosition":{"x":30073.593310488337,"y":16028.64897155772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA50","code":"{\n \"codeName\": \"sma50\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma50","id":"85033109-c11c-43d2-84cc-4510dae276ca","savedPayload":{"position":{"x":32359.111255012205,"y":15613.888614811081},"targetPosition":{"x":31216.35228275027,"y":15821.268793184401},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e7f1f78d-3a6d-49a2-b522-4423bd8d6a82","savedPayload":{"position":{"x":31216.35228275027,"y":15821.268793184401},"targetPosition":{"x":30073.593310488337,"y":16028.64897155772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA100","code":"{\n \"codeName\": \"sma100\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma100","id":"43eebecb-65db-4e64-902a-582515e06daa","savedPayload":{"position":{"x":31822.863145448304,"y":17556.940001685278},"targetPosition":{"x":30948.22822796832,"y":16792.7944866215},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"090fa9ea-f050-4315-9360-3958ce5debaa","savedPayload":{"position":{"x":30948.22822796832,"y":16792.7944866215},"targetPosition":{"x":30073.593310488337,"y":16028.64897155772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA200","code":"{\n \"codeName\": \"sma200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma200","id":"5bdf982c-5480-4f90-9ec2-7708bf2d4943","savedPayload":{"position":{"x":29969.37917155776,"y":18349.157071417787},"targetPosition":{"x":30021.48624102305,"y":17188.903021487753},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8146fe70-3592-4f27-af4a-d3d8e93268d1","savedPayload":{"position":{"x":30021.48624102305,"y":17188.903021487753},"targetPosition":{"x":30073.593310488337,"y":16028.64897155772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"1b29d760-f7fe-48a5-928c-212a6f9a2486","savedPayload":{"position":{"x":30073.593310488337,"y":16028.64897155772},"targetPosition":{"x":29133.981940175654,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code. This goes at the Code of the Procedure Loop Object */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last200.push(candle.close) // Add the current close value to the last 200 array.\n\nif (variable.last200.length > 200) {\n variable.last200.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 200 elements.\n}\n\nvariable.sma20 = calculateSMA(20)\nvariable.sma50 = calculateSMA(50)\nvariable.sma100 = calculateSMA(100)\nvariable.sma200 = calculateSMA(200)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last200.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last200.length - periods; i < variable.last200.length; i++) { // Iterate through the last periods\n sum = sum + variable.last200[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last200.length=\" + variable.last200.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last200[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}","id":"a9904080-6677-45e0-bcac-f336e882380a","savedPayload":{"position":{"x":32195.619683893958,"y":16449.197199795413},"targetPosition":{"x":31134.606497191147,"y":16921.590706006125},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"f"}}},"id":"52a2ea69-6420-40f6-90f0-3330d33e6f67","savedPayload":{"position":{"x":31134.606497191147,"y":16921.590706006125},"targetPosition":{"x":30073.593310488337,"y":17393.984212216837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code. This goes at the Code of the Procedure Initialization Object */\n\n/* \nThis is the array where we are going to store the last 200 elements, since we will not \nhave access to such long backwards history while processing daily files. In those situations\nwe only have access to the last 2 days of candles, which in 45 min for example, means 36\ncandles from the current day and 36 from the previous one. For that reason we will keep\nthis array with the last 200 candle.close values so that we overecome that limitation.\n*/\n\nvariable.last200 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")","id":"2790fd06-6fe8-4175-bbfc-854d9909aeb4","savedPayload":{"position":{"x":29830.789677708082,"y":19704.106465297384},"targetPosition":{"x":29952.19149409821,"y":18549.04533875711},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6d5772df-819e-4968-9e27-61981c08f0e2","savedPayload":{"position":{"x":29952.19149409821,"y":18549.04533875711},"targetPosition":{"x":30073.593310488337,"y":17393.984212216837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f1f881ff-a04f-4663-9747-2042d4581953","savedPayload":{"position":{"x":30073.593310488337,"y":17393.984212216837},"targetPosition":{"x":29133.981940175654,"y":16711.31659188728},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4029d5bf-a8ad-4d80-b874-2af506e0ab34","savedPayload":{"position":{"x":29133.981940175654,"y":16711.31659188728},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"SMA Popular","id":"ddf21e96-c312-4ebe-9e2b-f56e8ea072aa"}}},{"type":"Product Definition","name":"Base7 SMAs","code":"{\n \"codeName\": \"Base7-SMAs\",\n \"singularVariableName\": \"base7SMA\",\n \"pluralVariableName\": \"base7SMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base7-SMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"78a6ef37-4df8-4ac8-83a0-c9a732467c59","savedPayload":{"position":{"x":28775.082332932074,"y":16450.5607639404},"targetPosition":{"x":29714.693703244757,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base7-SMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base7-SMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"f155e2ed-7082-4021-a4ff-a6f3cfd5683d","savedPayload":{"position":{"x":30073.593310488337,"y":16028.648971557723},"targetPosition":{"x":29714.693703244757,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"6d7e37c5-d80e-44a3-8484-f532bcfc7819","savedPayload":{"position":{"x":28754.09085597734,"y":16188.441371848534},"targetPosition":{"x":29815.104042680152,"y":16660.834878059246},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"87203f0b-0fb8-4875-8127-d715198bf01b","savedPayload":{"position":{"x":29815.104042680152,"y":16660.834878059246},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"bf74ad1e-a569-4428-a387-b0f39bb63759","savedPayload":{"position":{"x":29321.829172814363,"y":15407.016616995326},"targetPosition":{"x":30098.973201098663,"y":16270.122500632642},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0bc477f0-8b7d-4c75-8613-0cc63a25a79d","savedPayload":{"position":{"x":30098.973201098663,"y":16270.122500632642},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA7","code":"{\n \"codeName\": \"sma7\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma7","id":"1a89def9-4270-4584-93c3-86ca30cb184c","savedPayload":{"position":{"x":31680.961148277587,"y":16028.528971557722},"targetPosition":{"x":30517.217622139382,"y":16028.648971557723},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a87dddd8-9806-45be-b9a9-0e3196da48a7","savedPayload":{"position":{"x":30517.217622139382,"y":16028.648971557723},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA14","code":"{\n \"codeName\": \"sma14\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma14","id":"c0b78d39-a652-4d13-9fce-1b56d4e6ffbb","savedPayload":{"position":{"x":31118.92086216321,"y":14823.106131189415},"targetPosition":{"x":30997.519045773086,"y":15978.167257729687},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"114a04b9-e5b6-4f60-ad99-760c10275f9e","savedPayload":{"position":{"x":30997.519045773086,"y":15978.167257729687},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA21","code":"{\n \"codeName\": \"sma21\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma21","id":"4740139a-d2d7-4050-a0f6-145eef002bed","savedPayload":{"position":{"x":32037.540755521168,"y":15121.583827892788},"targetPosition":{"x":31456.828992452065,"y":16127.406106081373},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ac8182cd-7f7b-44e9-a91c-b470be594ef0","savedPayload":{"position":{"x":31456.828992452065,"y":16127.406106081373},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA28","code":"{\n \"codeName\": \"sma28\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma28","id":"0a2f17a1-2663-477a-8c28-015592a67a7e","savedPayload":{"position":{"x":32755.33997000833,"y":15767.893143610841},"targetPosition":{"x":31815.728599695645,"y":16450.5607639404},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"81c852eb-9b3e-4505-b81a-f236b39d770d","savedPayload":{"position":{"x":31815.728599695645,"y":16450.5607639404},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA35","code":"{\n \"codeName\": \"sma35\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma35","id":"1a30763f-6398-475f-b232-ff0da4ad41fa","savedPayload":{"position":{"x":33148.204500438726,"y":16650.28132612012},"targetPosition":{"x":32012.160864910846,"y":16891.75485519504},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a8a4dd40-6286-46b0-8f4d-61893d2d9234","savedPayload":{"position":{"x":32012.160864910846,"y":16891.75485519504},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA70","code":"{\n \"codeName\": \"sma70\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma70","id":"07471d43-2432-4961-81b5-4aa1bb399df4","savedPayload":{"position":{"x":33148.204500438726,"y":17616.175442419797},"targetPosition":{"x":32012.160864910846,"y":17374.701913344878},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9dbc330c-e654-4591-92d0-c3e92262436e","savedPayload":{"position":{"x":32012.160864910846,"y":17374.701913344878},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA140","code":"{\n \"codeName\": \"sma140\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma140","id":"0ef6719d-cfa8-458e-a137-6bd2e77aa6fd","savedPayload":{"position":{"x":32755.33997000833,"y":18498.563624929073},"targetPosition":{"x":31815.728599695645,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"93814c2c-8fff-4415-9934-1356bf555015","savedPayload":{"position":{"x":31815.728599695645,"y":17815.896004599515},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA210","code":"{\n \"codeName\": \"sma210\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma210","id":"037c98d5-1eef-4928-ade0-a70021337aa5","savedPayload":{"position":{"x":32037.540755521168,"y":19144.87294064713},"targetPosition":{"x":31456.828992452065,"y":18139.050662458543},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"efbcaa0c-2bf3-44e6-aae3-4f73c6255c69","savedPayload":{"position":{"x":31456.828992452065,"y":18139.050662458543},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA280","code":"{\n \"codeName\": \"sma280\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma280","id":"2bdc1e1e-b6a3-4e68-90b5-e88e64684804","savedPayload":{"position":{"x":32158.9425719113,"y":18288.28951081023},"targetPosition":{"x":30997.51904577309,"y":18288.28951081023},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6b302071-d15f-43b3-9ab2-4b970a379ebc","savedPayload":{"position":{"x":30997.51904577309,"y":18288.28951081023},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA350","code":"{\n \"codeName\": \"sma350\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma350","id":"18baf5c6-5aa5-4f66-8637-9966fdf4acf4","savedPayload":{"position":{"x":31678.641148277587,"y":18237.807796982193},"targetPosition":{"x":30517.217622139382,"y":18237.807796982193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f1de76b8-1665-4142-b8fc-712203bad655","savedPayload":{"position":{"x":30517.217622139382,"y":18237.807796982193},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA700","code":"{\n \"codeName\": \"sma700\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma700","id":"c8f08ae8-f608-40f1-8df3-e4eb537a89ed","savedPayload":{"position":{"x":29321.829172814363,"y":18859.44015154459},"targetPosition":{"x":30098.973201098663,"y":17996.334267907274},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"83b3c070-c0cb-4a4b-86ea-0b6292f57c13","savedPayload":{"position":{"x":30098.973201098663,"y":17996.334267907274},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA1400","code":"{\n \"codeName\": \"sma1400\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma1400","id":"ca4c686e-61d0-4dc0-96f9-9ee80d856422","savedPayload":{"position":{"x":28754.09085597734,"y":18078.015396691382},"targetPosition":{"x":29815.104042680152,"y":17605.62189048067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e07922ab-0abb-4216-a534-07db74399c2a","savedPayload":{"position":{"x":29815.104042680152,"y":17605.62189048067},"targetPosition":{"x":30876.117229382962,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"f3c4fe44-c417-4c98-9547-4b0c062f7bd7","savedPayload":{"position":{"x":30876.117229382962,"y":17133.228384269958},"targetPosition":{"x":29714.693703244757,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code. This goes at the Code of the Procedure Loop Object */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last1400.push(candle.close) // Add the current close value to the last 1400 array.\n\nif (variable.last1400.length > 1400) {\n variable.last1400.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 1400 elements.\n}\n\nvariable.sma7 = calculateSMA(7)\nvariable.sma14 = calculateSMA(14)\nvariable.sma21 = calculateSMA(21)\nvariable.sma28 = calculateSMA(28)\nvariable.sma35 = calculateSMA(35)\nvariable.sma70 = calculateSMA(70)\nvariable.sma140 = calculateSMA(140)\nvariable.sma210 = calculateSMA(210)\nvariable.sma280 = calculateSMA(280)\nvariable.sma350 = calculateSMA(350)\nvariable.sma700 = calculateSMA(700)\nvariable.sma1400 = calculateSMA(1400)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last1400.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last1400.length - periods; i < variable.last1400.length; i++) { // Iterate through the last periods\n sum = sum + variable.last1400[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last1400.length=\" + variable.last1400.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last1400[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}","id":"27796e5a-2553-47c6-bddd-c8deaed05c2f","savedPayload":{"position":{"x":32345.680581544104,"y":18720.754855132032},"targetPosition":{"x":31209.63694601622,"y":18479.281326057113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e5d799ba-3a4f-45fb-8533-4c3de5d7886f","savedPayload":{"position":{"x":31209.63694601622,"y":18479.281326057113},"targetPosition":{"x":30073.593310488337,"y":18237.807796982193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code. This goes at the Code of the Procedure Initialization Object */\n\n/* \nThis is the array where we are going to store the last 1400 elements, since we will not \nhave access to such long backwards history while processing daily files. In those situations\nwe only have access to the last 2 days of candles, which in 45 min for example, means 36\ncandles from the current day and 36 from the previous one. For that reason we will keep\nthis array with the last 200 candle.close values so that we overecome that limitation.\n*/\n\nvariable.last1400 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")","id":"17ee3425-2a07-4523-b005-bac9dbf75184","savedPayload":{"position":{"x":30457.872808342243,"y":19100.91368061951},"targetPosition":{"x":29296.449282204037,"y":19100.91368061951},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9e9e5c8c-98e2-4bbf-884a-071449177692","savedPayload":{"position":{"x":29296.449282204037,"y":19100.91368061951},"targetPosition":{"x":30073.593310488337,"y":18237.807796982193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6bd8fef6-49f7-45c5-9f9f-944785f8578e","savedPayload":{"position":{"x":30073.593310488337,"y":18237.807796982193},"targetPosition":{"x":29714.693703244757,"y":17133.228384269958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a03ef0ea-5be9-4d40-952e-24789e77dbb6","savedPayload":{"position":{"x":29714.693703244757,"y":17133.228384269958},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"SMA Base 7","id":"515565ff-dee2-4adf-adaf-b4cfea5f5dbd"}}},{"type":"Product Definition","name":"Base11 SMAs","code":"{\n \"codeName\": \"Base11-SMAs\",\n \"singularVariableName\": \"base11SMA\",\n \"pluralVariableName\": \"base11SMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base11-SMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"af9b9cef-2a74-486f-b79e-047d68669a1f","savedPayload":{"position":{"x":29577.6062518267,"y":16711.31659188728},"targetPosition":{"x":29936.50585907028,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base11-SMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base11-SMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"de5cfa30-edbf-4d5b-8d7f-facfb69a0f3e","savedPayload":{"position":{"x":30876.117229382962,"y":17133.228384269958},"targetPosition":{"x":29936.50585907028,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"a6c93c64-52da-4dad-bff9-9f6d9b4606b9","savedPayload":{"position":{"x":30512.743893389827,"y":16204.314674409876},"targetPosition":{"x":30694.430561386394,"y":17351.439149669473},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5e8f3d9c-8284-4b92-b7f5-ad7498e5ef3d","savedPayload":{"position":{"x":30694.430561386394,"y":17351.439149669473},"targetPosition":{"x":30876.117229382962,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"ac16b3d1-e3b0-4b79-9047-cd137c6eeafc","savedPayload":{"position":{"x":32241.452470042077,"y":16619.340884303707},"targetPosition":{"x":31558.78484971252,"y":17558.95225461639},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"43337725-24dd-4c1a-ae8d-2562a028d02d","savedPayload":{"position":{"x":31558.78484971252,"y":17558.95225461639},"targetPosition":{"x":30876.117229382962,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA11","code":"{\n \"codeName\": \"sma11\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma11","id":"87a1e288-0966-46e9-8a4f-1ddbb3f8976c","savedPayload":{"position":{"x":33170.36617990216,"y":18135.190288935937},"targetPosition":{"x":32023.24170464256,"y":18316.876956932505},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6a67dbd9-1e8e-4100-849f-5ea77ea67985","savedPayload":{"position":{"x":32023.24170464256,"y":18316.876956932505},"targetPosition":{"x":30876.117229382962,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA22","code":"{\n \"codeName\": \"sma22\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma22","id":"8652042b-c373-4024-9fbf-c582a55134e0","savedPayload":{"position":{"x":32977.15212583385,"y":19181.23124525863},"targetPosition":{"x":31815.728599695645,"y":19181.23124525863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a5665ca1-e399-4e8a-8e04-8c0b153702e4","savedPayload":{"position":{"x":31815.728599695645,"y":19181.23124525863},"targetPosition":{"x":30876.117229382962,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA33","code":"{\n \"codeName\": \"sma33\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma33","id":"738317b6-dcb4-4699-a48c-170491515eb5","savedPayload":{"position":{"x":32219.227423517736,"y":19645.688100188672},"targetPosition":{"x":31057.80389737953,"y":19645.688100188672},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f35c54ed-6469-4cfa-94cf-f94f1c916e91","savedPayload":{"position":{"x":31057.80389737953,"y":19645.688100188672},"targetPosition":{"x":30876.117229382962,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA55","code":"{\n \"codeName\": \"sma55\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma55","id":"675e9f6c-1e0d-4d09-ab69-88252ef60fe2","savedPayload":{"position":{"x":31354.873135191607,"y":19438.174995241756},"targetPosition":{"x":30193.449609053405,"y":19438.174995241756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c17d77b3-4d80-462d-88ba-ef421928dde1","savedPayload":{"position":{"x":30193.449609053405,"y":19438.174995241756},"targetPosition":{"x":30876.117229382962,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA111","code":"{\n \"codeName\": \"sma111\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma111","id":"b32a4356-312c-4536-b30b-63bad302e857","savedPayload":{"position":{"x":28581.868278863767,"y":18861.93696092221},"targetPosition":{"x":29728.992754123363,"y":18680.25029292564},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f49152b4-5306-41d2-966b-dba92eef2590","savedPayload":{"position":{"x":29728.992754123363,"y":18680.25029292564},"targetPosition":{"x":30876.117229382962,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a099f207-16db-4f66-adfb-1529fa350385","savedPayload":{"position":{"x":30876.117229382962,"y":18498.563624929073},"targetPosition":{"x":29936.50585907028,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code. This goes at the Code of the Procedure Loop Object */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last200.push(candle.close) // Add the current close value to the last 200 array.\n\nif (variable.last200.length > 200) {\n variable.last200.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 200 elements.\n}\n\nvariable.sma11 = calculateSMA(11)\nvariable.sma22 = calculateSMA(22)\nvariable.sma33 = calculateSMA(33)\nvariable.sma55 = calculateSMA(55)\nvariable.sma111 = calculateSMA(111)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last200.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last200.length - periods; i < variable.last200.length; i++) { // Iterate through the last periods\n sum = sum + variable.last200[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last200.length=\" + variable.last200.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last200[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}","id":"317788d5-11f5-48a6-ab0c-252cdcef7728","savedPayload":{"position":{"x":31131.894308395298,"y":20646.687184586383},"targetPosition":{"x":30354.750280111,"y":19783.581300949067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cce46065-5c04-428f-823f-4cb5e67b212a","savedPayload":{"position":{"x":30354.750280111,"y":19783.581300949067},"targetPosition":{"x":29577.6062518267,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code. This goes at the Code of the Procedure Initialization Object */\n\n/* \nThis is the array where we are going to store the last 200 elements, since we will not \nhave access to such long backwards history while processing daily files. In those situations\nwe only have access to the last 2 days of candles, which in 45 min for example, means 36\ncandles from the current day and 36 from the previous one. For that reason we will keep\nthis array with the last 200 candle.close values so that we overecome that limitation.\n*/\n\nvariable.last200 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")","id":"70d5537e-f682-4322-992b-99a55edb2582","savedPayload":{"position":{"x":27305.518980770932,"y":19403.42247546159},"targetPosition":{"x":28441.562616298816,"y":19161.94894638667},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f0707196-a2ac-4aee-99df-48a7fe77c365","savedPayload":{"position":{"x":28441.562616298816,"y":19161.94894638667},"targetPosition":{"x":29577.6062518267,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"663d58c6-e03e-4923-8cc7-03cc860aa318","savedPayload":{"position":{"x":29577.6062518267,"y":18920.47541731175},"targetPosition":{"x":29936.50585907028,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"05fabbf9-7798-4a53-995e-53ae74bef3de","savedPayload":{"position":{"x":29936.50585907028,"y":17815.896004599515},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"SMA Base 11","id":"4a61ac35-a1ae-40f4-b3da-f81ae796ee00"}}},{"type":"Product Definition","name":"Base5 SMAs","code":"{\n \"codeName\": \"Base5-SMAs\",\n \"singularVariableName\": \"base5SMA\",\n \"pluralVariableName\": \"base5SMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Muti-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base5-SMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"a5d28568-fb58-469a-9820-b9f0e8eab705","savedPayload":{"position":{"x":30073.593310488337,"y":17393.984212216837},"targetPosition":{"x":29714.693703244757,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base5-SMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base5-SMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"01f32aa3-55e9-4f8c-80c7-997b52073274","savedPayload":{"position":{"x":30876.117229382962,"y":18498.563624929073},"targetPosition":{"x":29714.693703244757,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"949a2e58-5d07-475e-92b9-e0ef4b9fd9e7","savedPayload":{"position":{"x":30316.396943268584,"y":17293.020784560762},"targetPosition":{"x":30194.99512687846,"y":18448.081911101035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"65545f82-9022-4d4c-bd09-b900182bae75","savedPayload":{"position":{"x":30194.99512687846,"y":18448.081911101035},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"551c49da-26d4-438e-aff4-2a0ecc18ce7f","savedPayload":{"position":{"x":31627.881367056936,"y":21329.35480491594},"targetPosition":{"x":30654.30507355744,"y":18597.320759452723},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"74445dab-2c42-4c71-b179-04b98b5a8e27","savedPayload":{"position":{"x":30654.30507355744,"y":18597.320759452723},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA5","code":"{\n \"codeName\": \"sma5\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma5","id":"1c996969-c249-4b55-845c-62205e91fc68","savedPayload":{"position":{"x":32892.427421426386,"y":18920.47541731175},"targetPosition":{"x":31013.20468080102,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3b9f6fbf-20d8-4952-b5e8-34937746fef7","savedPayload":{"position":{"x":31013.20468080102,"y":18920.47541731175},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA10","code":"{\n \"codeName\": \"sma10\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma10","id":"d16592d7-2f02-49ae-8857-dd7098990f20","savedPayload":{"position":{"x":32609.46881255045,"y":19361.66950856639},"targetPosition":{"x":31209.63694601622,"y":19361.66950856639},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"73cb2d83-d59b-457e-af00-68a8d0e2b016","savedPayload":{"position":{"x":31209.63694601622,"y":19361.66950856639},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA15","code":"{\n \"codeName\": \"sma15\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma15","id":"147ea31e-2735-4bae-b6a9-99072ed3d7a5","savedPayload":{"position":{"x":29556.711214978117,"y":21867.751464634854},"targetPosition":{"x":29815.152262733227,"y":20735.44725113808},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"666f82ef-a002-4067-9edb-6ddbe2861056","savedPayload":{"position":{"x":29815.152262733227,"y":20735.44725113808},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA20","code":"{\n \"codeName\": \"sma20\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma20","id":"f2507a4f-7f65-4d22-b062-1834c9269285","savedPayload":{"position":{"x":28625.321862116376,"y":21419.217992069065},"targetPosition":{"x":29349.457586302357,"y":20511.180514855187},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a6a82705-0a6c-425e-afab-099a63570ada","savedPayload":{"position":{"x":29349.457586302357,"y":20511.180514855187},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA25","code":"{\n \"codeName\": \"sma25\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma25","id":"53aead0b-0c80-4e74-b19b-7290672f7444","savedPayload":{"position":{"x":27980.78043148839,"y":20610.988602081194},"targetPosition":{"x":29027.186870988364,"y":20107.06581986125},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cbc6da93-d064-4762-a32f-ae1071a1ebe2","savedPayload":{"position":{"x":29027.186870988364,"y":20107.06581986125},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA50","code":"{\n \"codeName\": \"sma50\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma50","id":"0c126131-e3bd-4d43-a037-53e54e268300","savedPayload":{"position":{"x":27750.746258211926,"y":19603.14303764131},"targetPosition":{"x":28912.16978435013,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1cda317e-4b82-4bd9-8b07-71ad7365e524","savedPayload":{"position":{"x":28912.16978435013,"y":19603.14303764131},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA100","code":"{\n \"codeName\": \"sma100\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma100","id":"77c897e8-ab38-4c88-a215-e67d53401196","savedPayload":{"position":{"x":27980.78043148839,"y":18595.297473201423},"targetPosition":{"x":29027.186870988364,"y":19099.220255421365},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f7a66586-158d-40e2-be75-1077a91b0d7e","savedPayload":{"position":{"x":29027.186870988364,"y":19099.220255421365},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA150","code":"{\n \"codeName\": \"sma150\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma150","id":"1e5d6df6-a1af-49f0-a06e-08c362b1cd34","savedPayload":{"position":{"x":28572.313558018057,"y":19558.211444064746},"targetPosition":{"x":29349.457586302357,"y":18695.10556042743},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"83260971-1089-4349-9019-29ea469a9d24","savedPayload":{"position":{"x":29349.457586302357,"y":18695.10556042743},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA200","code":"{\n \"codeName\": \"sma200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma200","id":"bc63f986-17dd-48fb-892b-7f2d2b033cc5","savedPayload":{"position":{"x":28754.139076030417,"y":18943.232330355248},"targetPosition":{"x":29815.152262733227,"y":18470.838824144535},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"07a55760-46cf-44e8-b2f9-f55a6bb5a81c","savedPayload":{"position":{"x":29815.152262733227,"y":18470.838824144535},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA250","code":"{\n \"codeName\": \"sma250\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma250","id":"1bdc84fd-9eac-4b07-9ff6-56f80ec08553","savedPayload":{"position":{"x":27750.746258211926,"y":19603.14303764131},"targetPosition":{"x":28912.16978435013,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8b672a62-1ae1-4e92-819e-597fa42da35b","savedPayload":{"position":{"x":28912.16978435013,"y":19603.14303764131},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA 500","code":"{\n \"codeName\": \"sma500\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma500","id":"0d17399e-97d1-459d-8864-6c72967f0fd4","savedPayload":{"position":{"x":27951.566937082716,"y":18658.356025219884},"targetPosition":{"x":29012.580123785527,"y":19130.749531430596},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"92405f1d-9adf-4a70-af18-f2537ba2b0a0","savedPayload":{"position":{"x":29012.580123785527,"y":19130.749531430596},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA 1000","code":"{\n \"codeName\": \"sma1000\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma1000","id":"e2364abc-b940-4b52-a506-587c1a5941e3","savedPayload":{"position":{"x":28519.305253919738,"y":17876.931270366676},"targetPosition":{"x":29296.449282204037,"y":18740.037154003992},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5318bf3f-6e8a-46c8-8b04-3769bde03438","savedPayload":{"position":{"x":29296.449282204037,"y":18740.037154003992},"targetPosition":{"x":30073.593310488337,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"69298c3e-479d-4eb6-a972-35f8f079ce86","savedPayload":{"position":{"x":30073.593310488337,"y":19603.14303764131},"targetPosition":{"x":29714.693703244757,"y":18498.563624929073},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code. This goes at the Code of the Procedure Loop Object */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last1000.push(candle.close) // Add the current close value to the last 1000 array.\n\nif (variable.last1000.length > 1000) {\n variable.last1000.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 1000 elements.\n}\n\nvariable.sma5 = calculateSMA(5)\nvariable.sma10 = calculateSMA(10)\nvariable.sma15 = calculateSMA(15)\nvariable.sma20 = calculateSMA(20)\nvariable.sma25 = calculateSMA(25)\nvariable.sma50 = calculateSMA(50)\nvariable.sma100 = calculateSMA(100)\nvariable.sma150 = calculateSMA(150)\nvariable.sma200 = calculateSMA(200)\nvariable.sma250 = calculateSMA(250)\nvariable.sma500 = calculateSMA(500)\nvariable.sma1000 = calculateSMA(1000)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last1000.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last1000.length - periods; i < variable.last1000.length; i++) { // Iterate through the last periods\n sum = sum + variable.last1000[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last1000.length=\" + variable.last1000.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last1000[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}","id":"45ebc09e-bada-4bcd-8780-a2f56f23f037","savedPayload":{"position":{"x":31097.929385208492,"y":19181.23124525863},"targetPosition":{"x":29936.505859070283,"y":19181.23124525863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f65693cf-81ef-497e-86d2-a7ad9b74a1ca","savedPayload":{"position":{"x":29936.505859070283,"y":19181.23124525863},"targetPosition":{"x":28775.082332932074,"y":19181.23124525863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code. This goes at the Code of the Procedure Initialization Object */\n\n/* \nThis is the array where we are going to store the last 1000 elements, since we will not \nhave access to such long backwards history while processing daily files. In those situations\nwe only have access to the last 2 days of candles, which in 45 min for example, means 36\ncandles from the current day and 36 from the previous one. For that reason we will keep\nthis array with the last 200 candle.close values so that we overecome that limitation.\n*/\n\nvariable.last1000 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")","id":"22db43d3-db5d-43b6-80fc-1faa358bfadb","savedPayload":{"position":{"x":26653.055959526453,"y":18236.444232837206},"targetPosition":{"x":27714.069146229263,"y":18708.837739047918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"12255c95-c680-43af-be1b-61ee37e0e827","savedPayload":{"position":{"x":27714.069146229263,"y":18708.837739047918},"targetPosition":{"x":28775.082332932074,"y":19181.23124525863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cd8c13e4-2b9e-4a42-a576-539d412f239a","savedPayload":{"position":{"x":28775.082332932074,"y":19181.23124525863},"targetPosition":{"x":29714.693703244757,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2dcb55de-1673-409e-aa52-f51e7c7f8fa3","savedPayload":{"position":{"x":29714.693703244757,"y":18498.563624929073},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"Base6 SMAs","code":"{\n \"codeName\": \"Base6-SMAs\",\n \"singularVariableName\": \"base6SMA\",\n \"pluralVariableName\": \"base6SMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base6-SMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"c0862b3d-d35a-4d3e-8d80-258b8a01c37c","savedPayload":{"position":{"x":30073.593310488337,"y":18237.807796982193},"targetPosition":{"x":29133.981940175654,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base6-SMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base6-SMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"f5050eb2-0a99-464e-b5e2-2d8f40453e9a","savedPayload":{"position":{"x":30073.593310488337,"y":19603.14303764131},"targetPosition":{"x":29133.981940175654,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Being","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"fc90c194-682d-4d5c-923b-89b19e49f234","savedPayload":{"position":{"x":30329.370389500673,"y":18298.843062749354},"targetPosition":{"x":29552.226361216373,"y":19161.94894638667},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"37fc0238-a202-4c33-89ca-6a4e24d4e1ac","savedPayload":{"position":{"x":29552.226361216373,"y":19161.94894638667},"targetPosition":{"x":28775.082332932074,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"c1b3c2c1-68aa-443a-8c69-c50dcd34998f","savedPayload":{"position":{"x":30897.108706337694,"y":19080.267817602562},"targetPosition":{"x":29836.095519634884,"y":19552.661323813274},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7757e0dd-c3b4-48b4-bdf7-e517ce8df601","savedPayload":{"position":{"x":29836.095519634884,"y":19552.661323813274},"targetPosition":{"x":28775.082332932074,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA6","code":"{\n \"codeName\": \"sma6\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma6","id":"8a633674-750f-492d-8bfc-ac6f2e79793a","savedPayload":{"position":{"x":28690.35762852461,"y":21129.63424273622},"targetPosition":{"x":28331.45802128103,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"12d5b8f1-c83c-43a3-bf45-6f85e85a6df9","savedPayload":{"position":{"x":28331.45802128103,"y":20025.054830023986},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA12","code":"{\n \"codeName\": \"sma12\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma12","id":"11e2a0cd-851a-425e-b2bd-f3cff1b00c77","savedPayload":{"position":{"x":27729.754781257194,"y":21230.597670392297},"targetPosition":{"x":27851.15659764732,"y":20075.536543852024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6c1f3e32-9310-4ece-a796-cf9225172978","savedPayload":{"position":{"x":27851.15659764732,"y":20075.536543852024},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA18","code":"{\n \"codeName\": \"sma18\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma18","id":"0cbf493f-fa5f-41a8-a928-754ec9720fe0","savedPayload":{"position":{"x":26811.134887899243,"y":20932.11997368892},"targetPosition":{"x":27391.846650968346,"y":19926.297695500336},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"12bf3180-d354-451d-ba14-81cdd9d95c19","savedPayload":{"position":{"x":27391.846650968346,"y":19926.297695500336},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA24","code":"{\n \"codeName\": \"sma24\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma24","id":"4d677df1-74ec-417e-a518-2b8946c84542","savedPayload":{"position":{"x":26093.335673412083,"y":20285.810657970866},"targetPosition":{"x":27032.947043724766,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3e3a6434-aaa4-4170-8cdc-cca21c8d0863","savedPayload":{"position":{"x":27032.947043724766,"y":19603.14303764131},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA30","code":"{\n \"codeName\": \"sma30\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma30","id":"3a92b258-6c21-4747-ba08-16cf9ba814ec","savedPayload":{"position":{"x":25700.47114298168,"y":19403.42247546159},"targetPosition":{"x":26836.514778509565,"y":19161.94894638667},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"22bc84a6-f431-464c-a7e0-b9249b32abdf","savedPayload":{"position":{"x":26836.514778509565,"y":19161.94894638667},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA60","code":"{\n \"codeName\": \"sma60\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma60","id":"d9a44db6-45eb-47f8-975b-ee8dccd0659d","savedPayload":{"position":{"x":26502.995061876307,"y":19542.107771874147},"targetPosition":{"x":26836.514778509565,"y":18679.00188823683},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c3d4d569-0b7e-44b9-b2f0-84d9a91ee6b0","savedPayload":{"position":{"x":26836.514778509565,"y":18679.00188823683},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA120","code":"{\n \"codeName\": \"sma120\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma120","id":"90fee0af-0e23-44de-aa16-d446857485f4","savedPayload":{"position":{"x":26682.269453932127,"y":19017.2092655841},"targetPosition":{"x":27728.6758934321,"y":19521.132047804043},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4f572d61-ff96-4ccb-90ca-7a26d5cc8322","savedPayload":{"position":{"x":27728.6758934321,"y":19521.132047804043},"targetPosition":{"x":28775.082332932074,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA180","code":"{\n \"codeName\": \"sma180\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma180","id":"031e5277-e225-4307-a247-f49dae98ac26","savedPayload":{"position":{"x":26914.902973218206,"y":19358.490881885024},"targetPosition":{"x":28050.94660874609,"y":19117.017352810104},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9e3eb2e1-3cb4-4f0b-9a5a-5196bb4af3af","savedPayload":{"position":{"x":28050.94660874609,"y":19117.017352810104},"targetPosition":{"x":28775.082332932074,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA240","code":"{\n \"codeName\": \"sma240\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma240","id":"cb9604b0-3004-48d5-84fe-680747edb943","savedPayload":{"position":{"x":26502.995061876307,"y":19542.107771874147},"targetPosition":{"x":27639.03869740419,"y":19783.581300949067},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"83c2137c-676d-4a43-88c1-e9a5f69cafeb","savedPayload":{"position":{"x":28653.680516541946,"y":18869.993703483713},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA300","code":"{\n \"codeName\": \"sma300\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma300","id":"8c3259f1-e88b-40a2-9af2-a0e554affbd2","savedPayload":{"position":{"x":26895.859592306708,"y":18659.71958936487},"targetPosition":{"x":27835.47096261939,"y":19342.38720969443},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"829bfd3a-8c69-4e51-8951-a1d7cb0b8c6f","savedPayload":{"position":{"x":27835.47096261939,"y":19342.38720969443},"targetPosition":{"x":28775.082332932074,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA600","code":"{\n \"codeName\": \"sma600\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma600","id":"501b1e82-840a-4288-801d-38a5e5c7f7cb","savedPayload":{"position":{"x":27613.65880679387,"y":18013.410273646816},"targetPosition":{"x":28194.37056986297,"y":19019.2325518354},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f7174f99-231c-4188-8f4a-95a34c0c35f8","savedPayload":{"position":{"x":28194.37056986297,"y":19019.2325518354},"targetPosition":{"x":28775.082332932074,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA1200","code":"{\n \"codeName\": \"sma1200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma1200","id":"0554601a-009b-47a2-8e0c-c7d6f3901a9e","savedPayload":{"position":{"x":28532.27870015182,"y":17714.93257694344},"targetPosition":{"x":28653.680516541946,"y":18869.993703483713},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f6f0c544-4054-4cb4-9be1-14ffa23c5251","savedPayload":{"position":{"x":28653.680516541946,"y":18869.993703483713},"targetPosition":{"x":28775.082332932074,"y":20025.054830023986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a84f4438-cabc-4c9a-bd9c-0338e55a6e15","savedPayload":{"position":{"x":27972.55841403745,"y":18920.47541731175},"targetPosition":{"x":29133.981940175654,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code. This goes at the Code of the Procedure Loop Object */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last1200.push(candle.close) // Add the current close value to the last 1200 array.\n\nif (variable.last1200.length > 1200) {\n variable.last1200.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 1200 elements.\n}\n\nvariable.sma6 = calculateSMA(6)\nvariable.sma12 = calculateSMA(12)\nvariable.sma18 = calculateSMA(18)\nvariable.sma24 = calculateSMA(24)\nvariable.sma30 = calculateSMA(30)\nvariable.sma60 = calculateSMA(60)\nvariable.sma120 = calculateSMA(120)\nvariable.sma180 = calculateSMA(180)\nvariable.sma240 = calculateSMA(240)\nvariable.sma300 = calculateSMA(300)\nvariable.sma600 = calculateSMA(600)\nvariable.sma1200 = calculateSMA(1200)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last1200.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last1200.length - periods; i < variable.last1200.length; i++) { // Iterate through the last periods\n sum = sum + variable.last1200[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last1200.length=\" + variable.last1200.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last1200[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}","id":"f2c97b70-717a-4f98-be7c-d76dd94b8227","savedPayload":{"position":{"x":30295.40546631386,"y":18920.47541731175},"targetPosition":{"x":29133.981940175654,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"96841e7b-90d9-4508-8933-4f0f9769800e","savedPayload":{"position":{"x":29133.981940175654,"y":18920.47541731175},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code. This goes at the Code of the Procedure Initialization Object */\n\n/* \nThis is the array where we are going to store the last 1200 elements, since we will not \nhave access to such long backwards history while processing daily files. In those situations\nwe only have access to the last 2 days of candles, which in 45 min for example, means 36\ncandles from the current day and 36 from the previous one. For that reason we will keep\nthis array with the last 200 candle.close values so that we overecome that limitation.\n*/\n\nvariable.last1200 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")","id":"14dcff53-0599-4529-8a5d-5cf92a5c33c8","savedPayload":{"position":{"x":26811.134887899243,"y":16908.83086093458},"targetPosition":{"x":27391.846650968346,"y":17914.653139123166},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d2863a8f-8f38-4c47-9f48-0bd45b628fa0","savedPayload":{"position":{"x":27391.846650968346,"y":17914.653139123166},"targetPosition":{"x":27972.55841403745,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"948be5d9-da96-488a-bb15-f8cd94777231","savedPayload":{"position":{"x":27972.55841403745,"y":18920.47541731175},"targetPosition":{"x":29133.981940175654,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4ae22e54-cd02-4e2e-b638-1c5ea9a171ac","savedPayload":{"position":{"x":29133.981940175654,"y":18920.47541731175},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"Base12 SMAs","code":"{\n \"codeName\": \"Base12-SMAs\",\n \"singularVariableName\": \"base12SMA\",\n \"pluralVariableName\": \"base12SMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base12-SMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"6ba76363-0f50-4e67-8678-3eb4fd6d07d6","savedPayload":{"position":{"x":29577.6062518267,"y":18920.47541731175},"targetPosition":{"x":28416.182725688494,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base12-SMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base12-SMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"92d92b3e-6870-499b-bfec-33bab220bf62","savedPayload":{"position":{"x":28416.182725688494,"y":20081.898943449956},"targetPosition":{"x":28416.182725688494,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"d44292f3-8960-448b-8339-32147b8bb1c3","savedPayload":{"position":{"x":29762.08929989968,"y":19188.382680894665},"targetPosition":{"x":28619.330327637745,"y":19395.762859267987},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bcec1860-262e-4125-bdf4-a8f798d9d4ff","savedPayload":{"position":{"x":28619.330327637745,"y":19395.762859267987},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"91264df6-db08-498e-a039-fec9b0cd09a5","savedPayload":{"position":{"x":29533.350274484757,"y":20682.62388794772},"targetPosition":{"x":28504.960814930284,"y":20142.883462794514},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0ef8d741-e876-446e-86ac-146b4f33d8e8","savedPayload":{"position":{"x":28504.960814930284,"y":20142.883462794514},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA12","code":"{\n \"codeName\": \"sma12\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma12","id":"c694dcde-b622-4d71-b2cf-e42e1b4934ec","savedPayload":{"position":{"x":28796.09887782242,"y":21514.80868460343},"targetPosition":{"x":28136.335116599115,"y":20558.97586112237},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"daf8aa00-f032-412e-b319-e7c581611db0","savedPayload":{"position":{"x":28136.335116599115,"y":20558.97586112237},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA24","code":"{\n \"codeName\": \"sma24\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma24","id":"445b00ba-46a1-4e79-b762-72c521223b5f","savedPayload":{"position":{"x":27756.559627798073,"y":21909.053919608607},"targetPosition":{"x":27616.56549158694,"y":20756.098478624957},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"255fdffa-cdbb-428b-a54e-14448f7cda29","savedPayload":{"position":{"x":27616.56549158694,"y":20756.098478624957},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA36","code":"{\n \"codeName\": \"sma36\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma36","id":"c9c11106-98b6-4fcb-b30b-d49263a05d95","savedPayload":{"position":{"x":26652.878438786247,"y":21775.04276079369},"targetPosition":{"x":27064.72489708103,"y":20689.0928992175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"628c3c4f-7ef7-4008-bada-76e4edf6de42","savedPayload":{"position":{"x":27064.72489708103,"y":20689.0928992175},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA48","code":"{\n \"codeName\": \"sma48\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma48","id":"8e1cfb60-cb51-4625-a36b-75d64381840d","savedPayload":{"position":{"x":25737.895370389357,"y":21143.47554963364},"targetPosition":{"x":26607.233362882584,"y":20373.309293637474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1aa9fd38-51e5-4158-9bb2-31dbc8f933d0","savedPayload":{"position":{"x":26607.233362882584,"y":20373.309293637474},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA60","code":"{\n \"codeName\": \"sma60\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma60","id":"6fe04671-6298-4ef4-9ef8-779eafc1645d","savedPayload":{"position":{"x":25221.22201683581,"y":20159.036722995228},"targetPosition":{"x":26348.89668610581,"y":19881.089880318268},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d76d7819-b542-4ee8-98fd-539ec3325e2c","savedPayload":{"position":{"x":26348.89668610581,"y":19881.089880318268},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA120","code":"{\n \"codeName\": \"sma120\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma120","id":"f8abde07-3312-4983-bff2-65ed4f475a3f","savedPayload":{"position":{"x":25206.137713843873,"y":19532.57637333767},"targetPosition":{"x":26348.896686105807,"y":19325.196194964345},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0e696147-e020-4056-8ad4-40ad79cba391","savedPayload":{"position":{"x":26348.896686105807,"y":19325.196194964345},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA240","code":"{\n \"codeName\": \"sma240\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma240","id":"1ea2c145-6dcf-4e8a-908a-b12e08a8d658","savedPayload":{"position":{"x":25237.433065904508,"y":18985.18013511984},"targetPosition":{"x":26357.00221064016,"y":19294.161586380575},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e02938c9-f5b1-49f7-8e63-f8637f6d1d4d","savedPayload":{"position":{"x":26357.00221064016,"y":19294.161586380575},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA360","code":"{\n \"codeName\": \"sma360\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma360","id":"16ab9e8b-cb14-4a59-b7b3-0a1d13dbea5b","savedPayload":{"position":{"x":25727.301520415844,"y":18074.852007513753},"targetPosition":{"x":26601.936437895827,"y":18838.99752257753},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fe671bcf-1d71-4081-a100-f4bbc59203c7","savedPayload":{"position":{"x":26601.936437895827,"y":18838.99752257753},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA480","code":"{\n \"codeName\": \"sma480\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma480","id":"135a34fc-3cc8-4555-b1b9-c8ea03150aff","savedPayload":{"position":{"x":26563.634319127654,"y":17467.220661683878},"targetPosition":{"x":27020.102837251732,"y":18535.181849662593},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"85a59bf2-d419-43c9-a287-68902b8cb199","savedPayload":{"position":{"x":27020.102837251732,"y":18535.181849662593},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA600","code":"{\n \"codeName\": \"sma600\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma600","id":"cacf8e3b-77d3-4697-9ca5-51cff50afb6c","savedPayload":{"position":{"x":27580.785494306387,"y":17282.63493778124},"targetPosition":{"x":27528.6784248411,"y":18442.888987711274},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"608a2a98-bd6e-49e0-b202-afff3553337d","savedPayload":{"position":{"x":27528.6784248411,"y":18442.888987711274},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA1200","code":"{\n \"codeName\": \"sma1200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma1200","id":"2e1c84ea-2cb3-4936-8106-623dcdff8782","savedPayload":{"position":{"x":28577.295781167377,"y":17557.654302220104},"targetPosition":{"x":28026.933568271594,"y":18580.398669930706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"dda31bad-8835-446a-9015-4e15ae4cb845","savedPayload":{"position":{"x":28026.933568271594,"y":18580.398669930706},"targetPosition":{"x":27476.57135537581,"y":19603.14303764131},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"f46175dc-dfd2-4e1e-a60c-554867b5f0a5","savedPayload":{"position":{"x":27476.57135537581,"y":19603.14303764131},"targetPosition":{"x":28416.182725688494,"y":18920.47541731175},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code. This goes at the Code of the Procedure Loop Object */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last1200.push(candle.close) // Add the current close value to the last 1200 array.\n\nif (variable.last1200.length > 1200) {\n variable.last1200.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 1200 elements.\n}\n\nvariable.sma12 = calculateSMA(12)\nvariable.sma24 = calculateSMA(24)\nvariable.sma36 = calculateSMA(36)\nvariable.sma48 = calculateSMA(48)\nvariable.sma60 = calculateSMA(60)\nvariable.sma120 = calculateSMA(120)\nvariable.sma240 = calculateSMA(240)\nvariable.sma360 = calculateSMA(360)\nvariable.sma480 = calculateSMA(480)\nvariable.sma600 = calculateSMA(600)\nvariable.sma1200 = calculateSMA(1200)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last1200.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last1200.length - periods; i < variable.last1200.length; i++) { // Iterate through the last periods\n sum = sum + variable.last1200[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last1200.length=\" + variable.last1200.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last1200[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}","id":"fba7176b-58ff-4bd6-95de-ad6f88de4cda","savedPayload":{"position":{"x":25354.54498197019,"y":19182.594809403618},"targetPosition":{"x":26415.558168673,"y":18710.201303192905},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"daf2fb19-bfda-4248-a0c6-8e364fe863ee","savedPayload":{"position":{"x":26415.558168673,"y":18710.201303192905},"targetPosition":{"x":27476.57135537581,"y":18237.807796982193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code. This goes at the Code of the Procedure Initialization Object */\n\n/* \nThis is the array where we are going to store the last 1200 elements, since we will not \nhave access to such long backwards history while processing daily files. In those situations\nwe only have access to the last 2 days of candles, which in 45 min for example, means 36\ncandles from the current day and 36 from the previous one. For that reason we will keep\nthis array with the last 200 candle.close values so that we overecome that limitation.\n*/\n\nvariable.last1200 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")","id":"4d3f1fee-e71d-46cd-a50b-69176de5e1e8","savedPayload":{"position":{"x":27719.374988156058,"y":15927.685543901649},"targetPosition":{"x":27597.973171765934,"y":17082.74667044192},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"08365c70-c9ba-481d-abb8-62dc21067754","savedPayload":{"position":{"x":27597.973171765934,"y":17082.74667044192},"targetPosition":{"x":27476.57135537581,"y":18237.807796982193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4aa28d66-d2c5-40d1-9b42-b6adb1e94d1c","savedPayload":{"position":{"x":27476.57135537581,"y":18237.807796982193},"targetPosition":{"x":28416.182725688494,"y":18920.47541731175},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"723d28e9-a010-48c5-bda0-43a4c2343ddf","savedPayload":{"position":{"x":28416.182725688494,"y":18920.47541731175},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"Base30 SMAs","code":"{\n \"codeName\": \"Base30-SMAs\",\n \"singularVariableName\": \"base30SMA\",\n \"pluralVariableName\": \"base30SMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base30-SMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"4c486321-22e0-4991-9a1c-571bada649ad","savedPayload":{"position":{"x":28775.082332932074,"y":19181.23124525863},"targetPosition":{"x":27835.47096261939,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base30-SMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/SMA/@Exchange/Output/Base30-SMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"819688c9-c7bd-444b-9658-2a4379125c72","savedPayload":{"position":{"x":27476.57135537581,"y":19603.14303764131},"targetPosition":{"x":27835.47096261939,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"16806650-2bfb-4709-acbd-aca1549c16e8","savedPayload":{"position":{"x":28730.826355590132,"y":19578.044475235485},"targetPosition":{"x":27702.43689603566,"y":19038.30405008228},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"265d3765-cd1d-4652-a0ed-01931674a151","savedPayload":{"position":{"x":27702.43689603566,"y":19038.30405008228},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"d0b81562-348b-433f-8524-f9bc909de69e","savedPayload":{"position":{"x":28495.234723842696,"y":19454.396448410134},"targetPosition":{"x":27333.81119770449,"y":19454.396448410134},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fe6074be-18c6-46c0-abd4-5d31bc067798","savedPayload":{"position":{"x":27333.81119770449,"y":19454.396448410134},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA30","code":"{\n \"codeName\": \"sma30\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma30","id":"2ef589ba-7f7d-4df5-91fd-9107d67a7792","savedPayload":{"position":{"x":27975.465098830522,"y":19651.519065912722},"targetPosition":{"x":26814.041572692317,"y":19651.519065912722},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"72debf77-1c4b-4e8d-9a99-96951bd6d24c","savedPayload":{"position":{"x":26814.041572692317,"y":19651.519065912722},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA60","code":"{\n \"codeName\": \"sma60\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma60","id":"d51798bf-2373-4f5e-b6e4-56aad8117820","savedPayload":{"position":{"x":27423.62450432461,"y":19584.513486505264},"targetPosition":{"x":26262.200978186404,"y":19584.513486505264},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"78e59381-fcda-4204-a70a-4113eebdbba7","savedPayload":{"position":{"x":26262.200978186404,"y":19584.513486505264},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA90","code":"{\n \"codeName\": \"sma90\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma90","id":"a35a40dd-e5b4-4444-a8da-14eeb79e76b3","savedPayload":{"position":{"x":26966.132970126164,"y":19268.72988092524},"targetPosition":{"x":25804.70944398796,"y":19268.72988092524},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"90b3598c-8f08-4dc1-ad41-a64caf0cab39","savedPayload":{"position":{"x":25804.70944398796,"y":19268.72988092524},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA120","code":"{\n \"codeName\": \"sma120\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma120","id":"046bdfe3-d838-425e-8cc0-e117151bdbf8","savedPayload":{"position":{"x":24418.69809794118,"y":19054.457310282996},"targetPosition":{"x":25546.37276721118,"y":18776.510467606036},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"aa9f7095-66e7-4e42-b6b2-d3a51e3b35d9","savedPayload":{"position":{"x":25546.37276721118,"y":18776.510467606036},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA150","code":"{\n \"codeName\": \"sma150\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma150","id":"8129644d-2b0a-4936-8255-22dc2aada9e7","savedPayload":{"position":{"x":24418.69809794118,"y":17942.669939575153},"targetPosition":{"x":25546.37276721118,"y":18220.616782252113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b0c06b10-549c-4280-aac9-0b649ede898c","savedPayload":{"position":{"x":25546.37276721118,"y":18220.616782252113},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA300","code":"{\n \"codeName\": \"sma300\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma300","id":"23ddb87c-8211-4032-8273-222af79863b5","savedPayload":{"position":{"x":24935.37145149473,"y":16958.23111293674},"targetPosition":{"x":25804.70944398796,"y":17728.397368932907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"38b8e859-eda7-4cd9-afc2-ee0e3ed297b7","savedPayload":{"position":{"x":25804.70944398796,"y":17728.397368932907},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA600","code":"{\n \"codeName\": \"sma600\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma600","id":"8f07691b-ccdc-47cc-9c07-3701b61322f9","savedPayload":{"position":{"x":25850.35451989162,"y":16326.66390177669},"targetPosition":{"x":26262.200978186404,"y":17412.61376335288},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cebd7a57-eebb-4e4f-9315-6f1411970e55","savedPayload":{"position":{"x":26262.200978186404,"y":17412.61376335288},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA900","code":"{\n \"codeName\": \"sma900\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma900","id":"ed66265d-f971-448f-b395-e0c12c178173","savedPayload":{"position":{"x":26954.035708903448,"y":16192.652742961774},"targetPosition":{"x":26814.041572692317,"y":17345.608183945424},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fcbefbb9-2a6d-459a-8d3b-595d71caaa88","savedPayload":{"position":{"x":26814.041572692317,"y":17345.608183945424},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA1200","code":"{\n \"codeName\": \"sma1200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma1200","id":"3e936d3b-8f40-49da-830c-f73ea5bf271a","savedPayload":{"position":{"x":27993.574958927795,"y":16586.89797796695},"targetPosition":{"x":27333.81119770449,"y":17542.73080144801},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"311d2e2d-163e-4b8e-ae99-2b2d0fbbb2aa","savedPayload":{"position":{"x":27333.81119770449,"y":17542.73080144801},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"SMA1500","code":"{\n \"codeName\": \"sma1500\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.sma1500","id":"d4db0dc9-12a2-41c6-8511-43cbfdcfb136","savedPayload":{"position":{"x":28730.826355590132,"y":17419.08277462266},"targetPosition":{"x":27702.43689603566,"y":17958.823199775867},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b71b603d-38de-401d-b1df-ca20710dba64","savedPayload":{"position":{"x":27702.43689603566,"y":17958.823199775867},"targetPosition":{"x":26674.047436481185,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"229b4129-39f9-4f46-bbb5-a86a714023f8","savedPayload":{"position":{"x":26674.047436481185,"y":18498.563624929073},"targetPosition":{"x":27835.47096261939,"y":18498.563624929073},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code. This goes at the Code of the Procedure Loop Object */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last1500.push(candle.close) // Add the current close value to the last 1500 array.\n\nif (variable.last1500.length > 1500) {\n variable.last1500.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 1500 elements.\n}\n\nvariable.sma30 = calculateSMA(30)\nvariable.sma60 = calculateSMA(60)\nvariable.sma90 = calculateSMA(90)\nvariable.sma120 = calculateSMA(120)\nvariable.sma150 = calculateSMA(150)\nvariable.sma300 = calculateSMA(300)\nvariable.sma600 = calculateSMA(600)\nvariable.sma900 = calculateSMA(900)\nvariable.sma1200 = calculateSMA(1200)\nvariable.sma1500 = calculateSMA(1500)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last1500.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last1500.length - periods; i < variable.last1500.length; i++) { // Iterate through the last periods\n sum = sum + variable.last1500[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last1500.length=\" + variable.last1500.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last1500[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}","id":"382c5abf-a91a-40c0-88c2-7e230e93ed44","savedPayload":{"position":{"x":25204.484084320044,"y":16911.037154067},"targetPosition":{"x":26340.527719847927,"y":17152.510683141918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d3dab655-2db2-4d4c-a776-91a6ae116304","savedPayload":{"position":{"x":26340.527719847927,"y":17152.510683141918},"targetPosition":{"x":27476.57135537581,"y":17393.984212216837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code. This goes at the Code of the Procedure Initialization Object */\n\n/* \nThis is the array where we are going to store the last 1500 elements, since we will not \nhave access to such long backwards history while processing daily files. In those situations\nwe only have access to the last 2 days of candles, which in 45 min for example, means 36\ncandles from the current day and 36 from the previous one. For that reason we will keep\nthis array with the last 200 candle.close values so that we overecome that limitation.\n*/\n\nvariable.last1500 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")","id":"41af43f4-4f30-404d-8d9e-3ad5e752688c","savedPayload":{"position":{"x":29030.85941194441,"y":15667.772444942204},"targetPosition":{"x":28253.71538366011,"y":16530.87832857952},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f8611aff-7f4f-4df8-a2ac-b92ef5633ad4","savedPayload":{"position":{"x":28253.71538366011,"y":16530.87832857952},"targetPosition":{"x":27476.57135537581,"y":17393.984212216837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e8bebfde-97db-42d7-93a2-972366bc002c","savedPayload":{"position":{"x":27476.57135537581,"y":17393.984212216837},"targetPosition":{"x":27835.47096261939,"y":18498.563624929073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"607ebabd-9602-4e5c-803d-d4207dbd6794","savedPayload":{"position":{"x":27835.47096261939,"y":18498.563624929073},"targetPosition":{"x":28775.082332932074,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"c1c6f0e0-49f7-4723-bf22-dd24dfee6102","savedPayload":{"position":{"x":28775.082332932074,"y":17815.896004599515},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Indicator Bot","name":"EMA","code":"{\n\"codeName\": \"EMA\",\n\"repo\": \"EMA-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates EMAs based on SMAs\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"4e72640b-d384-434c-a607-47b1c8e4c9ef","savedPayload":{"position":{"x":28520.043654471207,"y":18232.702737304695},"targetPosition":{"x":27342.981349679278,"y":18232.702737304695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"ea1af06a-2584-43f0-aecf-ec8ad9169e9c"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"e554af82-9965-4a5d-a59a-3dc873526ad9","savedPayload":{"position":{"x":26734.95538030119,"y":19285.834608588862},"targetPosition":{"x":27342.981349679278,"y":18232.702737304695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"4e36a5a5-7a03-43a9-b3e0-0c5763c29199"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"fcefc8e1-6900-485f-a5b0-80366450aedf","savedPayload":{"position":{"x":26735.49598838811,"y":17180.50722669406},"targetPosition":{"x":27342.981349679278,"y":18232.702737304695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"5586aa74-2ecb-4b71-83ea-d621aa400660"}}}],"id":"268cf8f3-e871-4fed-bbdb-725a070e7cb6","savedPayload":{"position":{"x":27342.981349679278,"y":18232.702737304695},"targetPosition":{"x":28271.813012919833,"y":17480.549686886534},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"6c163397-f3a4-4acc-b281-567f2809b8a0","savedPayload":{"position":{"x":28311.842482780256,"y":17052.234395889944},"targetPosition":{"x":27156.01353210347,"y":17052.234395889944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"8e7c0f51-727f-4a28-b516-087443ee369f"}}},{"type":"Status Dependency","name":"Ending: Olivia","code":"{ \n\"mainUtility\": \"Market Ending Point.\"\n}","id":"eb2d5251-f873-4785-80c8-3a75186e6fa9","savedPayload":{"position":{"x":26574.98667255277,"y":18058.60243719394},"targetPosition":{"x":27156.01353210347,"y":17052.234395889944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Olivia: Candles","id":"7715ed1a-35f7-4b1c-ad79-fcd2486fe2ca","savedPayload":{"position":{"x":26549.09077170412,"y":16001.013338608322},"targetPosition":{"x":27156.01353210347,"y":17052.234395889944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}}],"id":"39426478-777d-4ef5-a72a-2ad862c979c7","savedPayload":{"position":{"x":27156.01353210347,"y":17052.234395889944},"targetPosition":{"x":28271.813012919833,"y":17480.549686886534},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"8e7c0f51-727f-4a28-b516-087443ee369f","savedPayload":{"position":{"x":28084.845195344024,"y":16300.08134547178},"targetPosition":{"x":28271.813012919833,"y":17480.549686886534},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Olivia","id":"19e3e53c-0752-4cae-93bd-162e0839b32f","savedPayload":{"position":{"x":27309.319924212683,"y":16781.257525029556},"targetPosition":{"x":28271.813012919833,"y":17480.549686886534},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f865a93d-38fd-433f-92e6-ccb7f2d33b05","savedPayload":{"position":{"x":29387.612493736196,"y":17908.864977883124},"targetPosition":{"x":28271.813012919833,"y":17480.549686886534},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"278c34a7-199d-400e-8a06-087149e3d391","savedPayload":{"position":{"x":28271.813012919833,"y":17480.549686886534},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Calculates EMAs based on SMAs.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Daily\",\n \"startDate\": {\n \"takeItFromStatusDependency\": 0\n },\n \"endDate\": {\n \"takeItFromStatusDependency\": 1\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"24f5e41a-5839-4174-8e74-2fcde5c2f6a4","savedPayload":{"position":{"x":29010.705323050588,"y":17658.653199594937},"targetPosition":{"x":27807.837499579615,"y":17658.653199594937},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"e7f5899c-fc4c-412d-a8db-77c1bef8ed21"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"cf348b8d-e3cb-4841-b064-6bf2a8b54062","savedPayload":{"position":{"x":27203.24756905498,"y":18705.833677008144},"targetPosition":{"x":27807.837499579615,"y":17658.653199594937},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"0198fa3f-98e3-4c57-a4a4-a7a8e27905d3"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"49d8444c-a216-4d81-8039-fa15683ed461","savedPayload":{"position":{"x":27203.996244023037,"y":16612.769465264762},"targetPosition":{"x":27807.837499579615,"y":17658.653199594937},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"15ccbcff-318d-46db-93f2-d649b57fd52a"}}}],"id":"3864c37f-f9dc-433d-96e4-e36a6720adf1","savedPayload":{"position":{"x":27807.837499579615,"y":17658.653199594937},"targetPosition":{"x":29001.382570557365,"y":17596.102152950774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"2b523347-3f95-43ab-9ac8-72470000c774","savedPayload":{"position":{"x":29542.920810052758,"y":16593.737324244423},"targetPosition":{"x":28350.43923964134,"y":16593.737324244423},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"fb2c8ec5-75ff-41e9-8782-60a952bd06e8"}}},{"type":"Status Dependency","name":"Starting: Charly Historic Trades","code":"{\"mainUtility\": \"Market Starting Point\"}","id":"5c6a74d3-7f45-409c-a408-fe3c67c050e9","savedPayload":{"position":{"x":28350.43923964134,"y":17800.548634673105},"targetPosition":{"x":28350.43923964134,"y":16593.737324244423},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}},{"type":"Status Dependency","name":"Ending: Olivia","code":"{ \n\"mainUtility\": \"Market Ending Point\"\n}","id":"dbfe665f-e805-40f4-a494-231cc265acc8","savedPayload":{"position":{"x":27148.161138370015,"y":16593.737324244423},"targetPosition":{"x":28350.43923964134,"y":16593.737324244423},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"70d56a4f-6375-456e-b797-d590ab2109a3","savedPayload":{"position":{"x":28350.43923964134,"y":15398.407907785815},"targetPosition":{"x":28350.43923964134,"y":16593.737324244423},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}}}],"id":"b5b3313a-d8e6-4b64-9300-fdc6693a2cb7","savedPayload":{"position":{"x":28350.43923964134,"y":16593.737324244423},"targetPosition":{"x":29001.382570557365,"y":17596.102152950774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"fb2c8ec5-75ff-41e9-8782-60a952bd06e8","savedPayload":{"position":{"x":29543.98431061909,"y":16531.18627760026},"targetPosition":{"x":29001.382570557365,"y":17596.102152950774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Olivia","id":"41fe3adc-42c9-4f5d-b757-13cedd7e0155","savedPayload":{"position":{"x":28033.696685875573,"y":16893.03720397896},"targetPosition":{"x":29001.382570557365,"y":17596.102152950774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"e232777d-86ee-4bb4-9a6d-659f06493aa5","savedPayload":{"position":{"x":29652.325901473392,"y":18598.466981657126},"targetPosition":{"x":29001.382570557365,"y":17596.102152950774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"943ea24f-ee24-4eae-9b4b-e8a73122b1c2","savedPayload":{"position":{"x":29001.382570557365,"y":17596.102152950774},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"Popular EMAs","code":"{\n \"codeName\": \"Popular-EMAs\",\n \"singularVariableName\": \"popularEMA\",\n \"pluralVariableName\": \"popularEMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Popular-EMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"ea1af06a-2584-43f0-aecf-ec8ad9169e9c","savedPayload":{"position":{"x":28678.574682144383,"y":17273.29426453779},"targetPosition":{"x":29523.696705846156,"y":18118.416288239565},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Popular-EMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Popular-EMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"e7f5899c-fc4c-412d-a8db-77c1bef8ed21","savedPayload":{"position":{"x":30066.29844590788,"y":17053.50041288905},"targetPosition":{"x":29523.696705846156,"y":18118.416288239565},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"a88a6b8e-0c81-45f5-b69f-33ead2c9af2a","savedPayload":{"position":{"x":29524.49975466074,"y":16226.384423997024},"targetPosition":{"x":30114.332400960826,"y":17265.8842649062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"60b742b7-4a6b-4c3a-a924-7ae2c7c284d8","savedPayload":{"position":{"x":30114.332400960826,"y":17265.8842649062},"targetPosition":{"x":30704.16504726091,"y":18305.384105815374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"f12e6638-8bbd-4bff-b969-232c1861b45c","savedPayload":{"position":{"x":31594.08317100461,"y":16086.849541604406},"targetPosition":{"x":31149.12410913276,"y":17196.11682370989},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"87688525-3fc6-4a15-806d-fd8d0f3fd876","savedPayload":{"position":{"x":31149.12410913276,"y":17196.11682370989},"targetPosition":{"x":30704.16504726091,"y":18305.384105815374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA20","code":"{\n \"codeName\": \"ema20\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema20","id":"71d926a1-6f74-452c-a8af-2df52ccfceb2","savedPayload":{"position":{"x":32993.54008914799,"y":17617.916435920426},"targetPosition":{"x":31848.85256820445,"y":17961.6502708679},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5058b626-2b62-47c3-a915-12d6d451487c","savedPayload":{"position":{"x":31848.85256820445,"y":17961.6502708679},"targetPosition":{"x":30704.16504726091,"y":18305.384105815374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA50","code":"{\n \"codeName\": \"ema50\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema50","id":"7d744a34-29cc-46fa-89c9-a3258bbf79f6","savedPayload":{"position":{"x":32881.79100441481,"y":18986.022306633775},"targetPosition":{"x":31686.607976635565,"y":18986.022306633775},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1293683f-1686-47f2-8856-ce40dc753450","savedPayload":{"position":{"x":31686.607976635565,"y":18986.022306633775},"targetPosition":{"x":30704.16504726091,"y":18305.384105815374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA100","code":"{\n \"codeName\": \"ema100\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema100","id":"c0465aae-99b4-488f-801b-f0c96c30a695","savedPayload":{"position":{"x":31979.74684884325,"y":19497.859894694342},"targetPosition":{"x":30784.563821064006,"y":19497.859894694342},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d2b72da7-da22-46bb-8f7b-1f2b4fdc0324","savedPayload":{"position":{"x":30784.563821064006,"y":19497.859894694342},"targetPosition":{"x":30704.16504726091,"y":18305.384105815374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA200","code":"{\n \"codeName\": \"ema200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema200","id":"4ef9d626-2bf5-4a0b-bb72-6528f0ece6eb","savedPayload":{"position":{"x":31017.160776761855,"y":19111.738891655947},"targetPosition":{"x":29821.977748982612,"y":19111.738891655947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9e3b4c1a-8cff-4999-b8af-d8403ee3f614","savedPayload":{"position":{"x":29821.977748982612,"y":19111.738891655947},"targetPosition":{"x":30704.16504726091,"y":18305.384105815374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"3a6ab821-f693-4929-a0d7-ad0499d23e68","savedPayload":{"position":{"x":30704.16504726091,"y":18305.384105815374},"targetPosition":{"x":29523.696705846156,"y":18118.416288239565},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\nlet candle = record.current \n\nvariable.ema20 = calculateEMA(20, candle.close, variable.previousEMA20)\nvariable.previousEMA20 = variable.ema20 \n \nvariable.ema50 = calculateEMA(50, candle.close, variable.previousEMA50)\nvariable.previousEMA50 = variable.ema50 \n\nvariable.ema100 = calculateEMA(100, candle.close, variable.previousEMA100)\nvariable.previousEMA100 = variable.ema100 \n\nvariable.ema200 = calculateEMA(200, candle.close, variable.previousEMA200)\nvariable.previousEMA200 = variable.ema200 \n\nfunction calculateEMA(periods, price, previousEMA) {\n let k = 2 / (periods + 1)\n return price * k + previousEMA * (1-k)\n}","id":"25aad3fa-6c8f-46b0-8852-07a95544d6bd","savedPayload":{"position":{"x":31942.263485054693,"y":20155.5152116475},"targetPosition":{"x":30826.46400423833,"y":19727.19992065091},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d4805ae2-446b-4e54-89c6-0b2e9df3fade","savedPayload":{"position":{"x":30826.46400423833,"y":19727.19992065091},"targetPosition":{"x":29710.664523421965,"y":19298.88462965432},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\nvariable.previousEMA20 = 0\nvariable.previousEMA50 = 0\nvariable.previousEMA100 = 0\nvariable.previousEMA200 = 0","id":"c5b50764-185a-43a8-b399-62dd6d81a249","savedPayload":{"position":{"x":27853.001196940855,"y":20803.19073049065},"targetPosition":{"x":28781.83286018141,"y":20051.037680072484},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6dd7dc20-5c47-4226-8abc-8551b6bd698f","savedPayload":{"position":{"x":28781.83286018141,"y":20051.037680072484},"targetPosition":{"x":29710.664523421965,"y":19298.88462965432},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4c2e867a-f31c-4356-be35-138cdc902c15","savedPayload":{"position":{"x":29710.664523421965,"y":19298.88462965432},"targetPosition":{"x":29523.696705846156,"y":18118.416288239565},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d5894707-b065-429b-ba6b-78c71e6529fe","savedPayload":{"position":{"x":29523.696705846156,"y":18118.416288239565},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"EMA Popular","id":"79bf7438-f77a-4c8d-a450-67027d5fd8bc"}}},{"type":"Product Definition","name":"Base5 EMAs","code":"{\n \"codeName\": \"Base5-EMAs\",\n \"singularVariableName\": \"base5EMA\",\n \"pluralVariableName\": \"base5EMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base5-EMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"85e3f0ce-5331-4c0b-900a-e73aaf53d96e","savedPayload":{"position":{"x":29452.281354334587,"y":17667.517504462343},"targetPosition":{"x":29639.249171910396,"y":18847.985845877098},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base5-EMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base5-EMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"e04edb48-09ab-4f92-beb7-14ba647de7d6","savedPayload":{"position":{"x":30704.16504726091,"y":18305.384105815374},"targetPosition":{"x":29639.249171910396,"y":18847.985845877098},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"6d1e00cd-ea21-4c5d-9f21-bbe4e18eaaa1","savedPayload":{"position":{"x":32874.737251170656,"y":19693.10786957887},"targetPosition":{"x":31679.554223391413,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7804f5d6-913b-48d3-941a-10135819f0d7","savedPayload":{"position":{"x":31679.554223391413,"y":19693.10786957887},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"615487b1-0964-4bfc-800e-f8d3471dfeb0","savedPayload":{"position":{"x":32638.01659456107,"y":20730.248831624278},"targetPosition":{"x":31561.19389508662,"y":20211.678350601574},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5aaf85b9-35bd-47f1-a2a4-dcfa881bc542","savedPayload":{"position":{"x":31561.19389508662,"y":20211.678350601574},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA5","code":"{\n \"codeName\": \"ema5\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema5","id":"1abd53a1-2924-4db2-b50c-06e36aa91e47","savedPayload":{"position":{"x":31974.740053962174,"y":21561.97130643742},"targetPosition":{"x":31229.555624787172,"y":20627.539588008145},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4912e0cb-1bba-4750-a063-d8d205091281","savedPayload":{"position":{"x":31229.555624787172,"y":20627.539588008145},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA10","code":"{\n \"codeName\": \"ema10\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema10","id":"00ca97c2-f4e9-4d08-af83-3fee7e5d1887","savedPayload":{"position":{"x":31016.27768279252,"y":22023.542457474814},"targetPosition":{"x":30750.324439202344,"y":20858.325163526843},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f7e888a6-a986-45fe-804d-c4cba934bd9b","savedPayload":{"position":{"x":30750.324439202344,"y":20858.325163526843},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA15","code":"{\n \"codeName\": \"ema15\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema15","id":"f259ce57-d1c2-4196-8e55-dae9f1eb78f5","savedPayload":{"position":{"x":33093.30122249071,"y":19386.75173960631},"targetPosition":{"x":31640.319349286507,"y":19386.75173960631},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9720b3bd-fcb9-4997-9fe8-9d8682aa9dc9","savedPayload":{"position":{"x":31640.319349286507,"y":19386.75173960631},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA20","code":"{\n \"codeName\": \"ema20\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema20","id":"5c7d8482-a86c-4ebc-8524-31b1a73900c3","savedPayload":{"position":{"x":28991.340733989506,"y":21565.308851375932},"targetPosition":{"x":29737.855964800838,"y":20629.2083604774},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"716e98b8-640e-4653-9c50-ca0286c851c0","savedPayload":{"position":{"x":29737.855964800838,"y":20629.2083604774},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA25","code":"{\n \"codeName\": \"ema25\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema25","id":"031e3d2f-b487-4df2-9892-845d3e43916a","savedPayload":{"position":{"x":28333.444757410616,"y":20728.93944913882},"targetPosition":{"x":29408.907976511393,"y":20211.023659358845},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"56be5d1d-2bd8-42dd-9861-729f4bb72ccf","savedPayload":{"position":{"x":29408.907976511393,"y":20211.023659358845},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA50","code":"{\n \"codeName\": \"ema50\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema50","id":"309f068f-74e5-4405-8267-418ea12ca20d","savedPayload":{"position":{"x":28089.686014412633,"y":19693.10786957887},"targetPosition":{"x":29287.0286050124,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ed5849ae-b0fe-4915-b8c3-8bdb004e5acf","savedPayload":{"position":{"x":29287.0286050124,"y":19693.10786957887},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA100","code":"{\n \"codeName\": \"ema100\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema100","id":"6b164582-e3a2-4ea0-bc0a-e362929d556f","savedPayload":{"position":{"x":28327.239665735076,"y":18654.288075360608},"targetPosition":{"x":29405.805430673623,"y":19173.69797246974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7f690611-834a-4048-bf59-5685995167e9","savedPayload":{"position":{"x":29405.805430673623,"y":19173.69797246974},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA150","code":"{\n \"codeName\": \"ema150\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema150","id":"829731e3-421a-4b49-ac4d-69cc76cdbfab","savedPayload":{"position":{"x":28994.002337262165,"y":17824.244432720323},"targetPosition":{"x":29739.186766437168,"y":18758.676151149597},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6b51dbbf-76eb-484b-9535-6043984a7ed6","savedPayload":{"position":{"x":29739.186766437168,"y":18758.676151149597},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA200","code":"{\n \"codeName\": \"ema200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema200","id":"3786f9d4-064e-43ba-8098-d336cd64cfb4","savedPayload":{"position":{"x":31552.96357817407,"y":18507.132179479864},"targetPosition":{"x":30359.440550394826,"y":18504.472179479864},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9f96cb9c-ac78-4d23-80e7-650dcff0a56a","savedPayload":{"position":{"x":30359.440550394826,"y":18504.472179479864},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA250","code":"{\n \"codeName\": \"ema250\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema250","id":"710f1d8e-a59f-41d9-9576-e73e292d5cac","savedPayload":{"position":{"x":32130.316843181095,"y":18554.20126284405},"targetPosition":{"x":30853.70306258446,"y":18556.42126284405},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9e3fc138-8765-46af-95a3-6ee316f49a72","savedPayload":{"position":{"x":30853.70306258446,"y":18556.42126284405},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA500","code":"{\n \"codeName\": \"ema500\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema500","id":"8be9f984-f4ad-471c-885d-b0291c74f04f","savedPayload":{"position":{"x":32478.31776747915,"y":18805.00378698632},"targetPosition":{"x":31284.10473969991,"y":18804.91378698632},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"05a21cb3-24bc-42cf-8269-62f14e8f3ed1","savedPayload":{"position":{"x":31284.10473969991,"y":18804.91378698632},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA1000","code":"{\n \"codeName\": \"ema1000\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema1000","id":"1001e9de-822a-4a75-9526-390dc6be8fe7","savedPayload":{"position":{"x":28251.172233979443,"y":18836.557287585692},"targetPosition":{"x":31576.225221691428,"y":19206.98313699877},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cc878593-3f3d-4533-9ed1-a7c7fd0cdd99","savedPayload":{"position":{"x":31576.225221691428,"y":19206.98313699877},"targetPosition":{"x":30484.37119561217,"y":19693.10786957887},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"7133bdf5-a6d6-4ca5-bc27-390fe276eca6","savedPayload":{"position":{"x":30484.37119561217,"y":19693.10786957887},"targetPosition":{"x":29639.249171910396,"y":18847.985845877098},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\nlet candle = record.current \n\nvariable.ema5 = calculateEMA(5, candle.close, variable.previousEMA5)\nvariable.previousEMA5 = variable.ema5\n\nvariable.ema10 = calculateEMA(10, candle.close, variable.previousEMA10)\nvariable.previousEMA10 = variable.ema10\n\nvariable.ema15 = calculateEMA(15, candle.close, variable.previousEMA15)\nvariable.previousEMA15 = variable.ema15\n\nvariable.ema20 = calculateEMA(20, candle.close, variable.previousEMA20)\nvariable.previousEMA20 = variable.ema20\n\nvariable.ema25 = calculateEMA(25, candle.close, variable.previousEMA25)\nvariable.previousEMA25 = variable.ema25\n\nvariable.ema50 = calculateEMA(50, candle.close, variable.previousEMA50)\nvariable.previousEMA50 = variable.ema50\n\nvariable.ema100 = calculateEMA(100, candle.close, variable.previousEMA100)\nvariable.previousEMA100 = variable.ema100\n\nvariable.ema150 = calculateEMA(150, candle.close, variable.previousEMA150)\nvariable.previousEMA150 = variable.ema150\n\nvariable.ema200 = calculateEMA(200, candle.close, variable.previousEMA200)\nvariable.previousEMA200 = variable.ema200\n\nvariable.ema250 = calculateEMA(250, candle.close, variable.previousEMA250)\nvariable.previousEMA250 = variable.ema250\n\nvariable.ema500 = calculateEMA(500, candle.close, variable.previousEMA500)\nvariable.previousEMA500 = variable.ema500\n\nvariable.ema1000 = calculateEMA(1000, candle.close, variable.previousEMA1000)\nvariable.previousEMA1000 = variable.ema1000\n\nfunction calculateEMA(periods, price, previousEMA) {\n let k = 2 / (periods + 1)\n return price * k + previousEMA * (1-k)\n}\n","id":"3ea21697-ef5d-421a-825c-db6f04a041ce","savedPayload":{"position":{"x":29256.771595399092,"y":19315.31020733799},"targetPosition":{"x":28061.588567619845,"y":19315.31020733799},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d955ad88-0ca4-443b-8d4e-fa0c322d7797","savedPayload":{"position":{"x":28061.588567619845,"y":19315.31020733799},"targetPosition":{"x":29639.249171910396,"y":17652.802818097854},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\nvariable.previousEMA5 = 0\nvariable.previousEMA10 = 0\nvariable.previousEMA15 = 0\nvariable.previousEMA20 = 0\nvariable.previousEMA25 = 0\nvariable.previousEMA50 = 0\nvariable.previousEMA100 = 0\nvariable.previousEMA150 = 0\nvariable.previousEMA200 = 0\nvariable.previousEMA250 = 0\nvariable.previousEMA500 = 0\nvariable.previousEMA1000 = 0","id":"b951bfdc-9216-4494-8bcf-66099ea9eb8f","savedPayload":{"position":{"x":28936.523268298253,"y":19315.31020733799},"targetPosition":{"x":30131.7062960775,"y":19315.31020733799},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2aa36b6f-bde7-433d-a7b6-e646b19e2c20","savedPayload":{"position":{"x":30131.7062960775,"y":19315.31020733799},"targetPosition":{"x":29639.249171910396,"y":17652.802818097854},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"54d9ab04-cfe7-433e-8c85-36a511b38d6d","savedPayload":{"position":{"x":29639.249171910396,"y":17652.802818097854},"targetPosition":{"x":29639.249171910396,"y":18847.985845877098},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9a37881e-7d9a-4279-95b2-8743de82eec5","savedPayload":{"position":{"x":29639.249171910396,"y":18847.985845877098},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"Base6 EMAs","code":"{\n \"codeName\": \"Base6-EMAs\",\n \"singularVariableName\": \"base6EMA\",\n \"pluralVariableName\": \"base6EMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base6-EMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"6b6840f7-3b76-474a-9d27-9515cbcd6499","savedPayload":{"position":{"x":29846.50459425914,"y":18441.224176652548},"targetPosition":{"x":29303.902854197415,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base6-EMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base6-EMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"ef3d5a7d-11eb-4ead-8e24-49ac7c80b0c7","savedPayload":{"position":{"x":30484.37119561217,"y":19693.10786957887},"targetPosition":{"x":29303.902854197415,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"611ac809-a75a-4550-b9a7-2f5f43cae1e8","savedPayload":{"position":{"x":28727.392086363296,"y":17197.22374465439},"targetPosition":{"x":28418.055956390734,"y":18351.681898328727},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"77290003-4665-4c78-bce6-7cb50a44bb31","savedPayload":{"position":{"x":28418.055956390734,"y":18351.681898328727},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"59bd5c2f-dd37-4724-8b40-593ba56967ba","savedPayload":{"position":{"x":29613.025927254494,"y":17648.476725521952},"targetPosition":{"x":28860.872876836333,"y":18577.308388762507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"70c54561-94ce-4d2a-a56e-cf1cd4d2a2f5","savedPayload":{"position":{"x":28860.872876836333,"y":18577.308388762507},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA6","code":"{\n \"codeName\": \"ema6\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema6","id":"330b3cad-7015-4691-98ff-e6c0aaeaa807","savedPayload":{"position":{"x":30238.5515771192,"y":18420.936571879614},"targetPosition":{"x":29173.635701768686,"y":18963.538311941338},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a7e48821-628f-4393-9272-055bcc8ea5f7","savedPayload":{"position":{"x":29173.635701768686,"y":18963.538311941338},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA12","code":"{\n \"codeName\": \"ema12\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema12","id":"7679a1d7-49b9-4798-82d2-f00dbfd7db05","savedPayload":{"position":{"x":30495.809968373673,"y":19381.037958714736},"targetPosition":{"x":29302.264897395922,"y":19443.5890053589},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"53cda9a0-2acf-4fa9-b52b-c860da5dbb32","savedPayload":{"position":{"x":29302.264897395922,"y":19443.5890053589},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA18","code":"{\n \"codeName\": \"ema18\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema18","id":"092a466e-c714-41b3-8053-a4c060520f3c","savedPayload":{"position":{"x":27576.813339237826,"y":21836.574639898998},"targetPosition":{"x":27842.766582828,"y":20671.35734595103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"446296f9-21fa-4a92-bc49-23fb81af896b","savedPayload":{"position":{"x":27842.766582828,"y":20671.35734595103},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA24","code":"{\n \"codeName\": \"ema24\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema24","id":"f666cf13-3e37-4649-a2fc-338fabb1f7d0","savedPayload":{"position":{"x":26618.350968068167,"y":21375.00348886161},"targetPosition":{"x":27363.53539724317,"y":20440.571770432336},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"518927a0-e986-4c9e-a645-75c02c2f571b","savedPayload":{"position":{"x":27363.53539724317,"y":20440.571770432336},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA30","code":"{\n \"codeName\": \"ema30\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema30","id":"3c76eaaf-56ac-4e91-9ee5-11b32219d2ea","savedPayload":{"position":{"x":25955.07442746927,"y":20543.28101404847},"targetPosition":{"x":27031.89712694372,"y":20024.710533025765},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3b056114-3148-432d-908e-d82df0355b7f","savedPayload":{"position":{"x":27031.89712694372,"y":20024.710533025765},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA60","code":"{\n \"codeName\": \"ema60\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema60","id":"ee78d4d5-f202-4d8e-ae88-eea38f2d0bc0","savedPayload":{"position":{"x":25718.353770859678,"y":19506.140052003062},"targetPosition":{"x":26913.536798638925,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"83802e58-4437-4ddc-b329-9ae962866387","savedPayload":{"position":{"x":26913.536798638925,"y":19506.140052003062},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA120","code":"{\n \"codeName\": \"ema120\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema120","id":"ab81fc7a-4873-49d2-a3ef-c9c1eed99dba","savedPayload":{"position":{"x":25955.07442746927,"y":18468.999089957655},"targetPosition":{"x":27031.89712694372,"y":18987.56957098036},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bec21f4d-ac0c-4b70-a565-16d065d1e89c","savedPayload":{"position":{"x":27031.89712694372,"y":18987.56957098036},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA180","code":"{\n \"codeName\": \"ema180\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema180","id":"ce0ce81c-9577-4cc6-8edd-ad99c52916aa","savedPayload":{"position":{"x":26434.703734002615,"y":19323.86138399195},"targetPosition":{"x":27363.53539724317,"y":18571.708333573788},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7318a961-0663-4a33-9295-21a8ab990854","savedPayload":{"position":{"x":27363.53539724317,"y":18571.708333573788},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA240","code":"{\n \"codeName\": \"ema240\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema240","id":"f9c4fe37-e76d-4601-9933-ce1ad3e8ad3d","savedPayload":{"position":{"x":26688.30842915366,"y":18650.258888027656},"targetPosition":{"x":27842.766582828,"y":18340.922758055094},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b2bfa489-cb63-498e-8a80-a82ebf19d0e6","savedPayload":{"position":{"x":27842.766582828,"y":18340.922758055094},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA300","code":"{\n \"codeName\": \"ema300\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema300","id":"8b93fa9d-ea11-4654-a6ac-70411f0db75a","savedPayload":{"position":{"x":25747.783143588662,"y":19132.204416851444},"targetPosition":{"x":26928.251485003417,"y":19319.172234427253},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a6926058-2ae5-454e-b696-b9d49162e901","savedPayload":{"position":{"x":26928.251485003417,"y":19319.172234427253},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA600","code":"{\n \"codeName\": \"ema600\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema600","id":"b67f8d22-c54d-4a21-a726-a8975c10d710","savedPayload":{"position":{"x":26103.99016900547,"y":18204.25339017101},"targetPosition":{"x":27106.35499771182,"y":18855.196721087035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cd299cac-3be1-4899-8816-70baccd30168","savedPayload":{"position":{"x":27106.35499771182,"y":18855.196721087035},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA1200","code":"{\n \"codeName\": \"ema1200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema1200","id":"f7b1d5d4-86c8-4a90-a98a-774fe1171e73","savedPayload":{"position":{"x":26806.833164586118,"y":17501.41039459036},"targetPosition":{"x":27457.776495502145,"y":18503.77522329671},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"717ba1d7-3b27-4f87-8e25-c2a26f435e67","savedPayload":{"position":{"x":27457.776495502145,"y":18503.77522329671},"targetPosition":{"x":28108.71982641817,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"003162cf-ab76-4dea-ac72-a4dd3a0e87ac","savedPayload":{"position":{"x":28108.71982641817,"y":19506.140052003062},"targetPosition":{"x":29303.902854197415,"y":19506.140052003062},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\nlet candle = record.current \n\nvariable.ema6 = calculateEMA(6, candle.close, variable.previousEMA6)\nvariable.previousEMA6 = variable.ema6\n\nvariable.ema12 = calculateEMA(12, candle.close, variable.previousEMA12)\nvariable.previousEMA12 = variable.ema12\n\nvariable.ema18 = calculateEMA(18, candle.close, variable.previousEMA18)\nvariable.previousEMA18 = variable.ema18\n\nvariable.ema24 = calculateEMA(24, candle.close, variable.previousEMA24)\nvariable.previousEMA24 = variable.ema24\n\nvariable.ema30 = calculateEMA(30, candle.close, variable.previousEMA30)\nvariable.previousEMA30 = variable.ema30\n\nvariable.ema60 = calculateEMA(60, candle.close, variable.previousEMA60)\nvariable.previousEMA60 = variable.ema60\n\nvariable.ema120 = calculateEMA(120, candle.close, variable.previousEMA120)\nvariable.previousEMA120 = variable.ema120\n\nvariable.ema180 = calculateEMA(180, candle.close, variable.previousEMA180)\nvariable.previousEMA180 = variable.ema180\n\nvariable.ema240 = calculateEMA(240, candle.close, variable.previousEMA240)\nvariable.previousEMA240 = variable.ema240\n\nvariable.ema300 = calculateEMA(300, candle.close, variable.previousEMA300)\nvariable.previousEMA300 = variable.ema300\n\nvariable.ema600 = calculateEMA(600, candle.close, variable.previousEMA600)\nvariable.previousEMA600 = variable.ema600\n\nvariable.ema1200 = calculateEMA(1200, candle.close, variable.previousEMA1200)\nvariable.previousEMA1200 = variable.ema1200\n\nfunction calculateEMA(periods, price, previousEMA) {\n let k = 2 / (periods + 1)\n return price * k + previousEMA * (1-k)\n}","id":"49789f74-3691-45b3-8c29-9a72500ae81c","savedPayload":{"position":{"x":28399.11114239732,"y":19451.150278175162},"targetPosition":{"x":27203.928114618073,"y":19451.150278175162},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ed1c9899-0b00-4cc5-b30d-faefdbe914d6","savedPayload":{"position":{"x":27203.928114618073,"y":19451.150278175162},"targetPosition":{"x":29303.902854197415,"y":18310.95702422382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\nvariable.previousEMA6 = 0\nvariable.previousEMA12 = 0\nvariable.previousEMA18 = 0\nvariable.previousEMA24 = 0\nvariable.previousEMA30 = 0\nvariable.previousEMA60 = 0\nvariable.previousEMA120 = 0\nvariable.previousEMA180 = 0\nvariable.previousEMA240 = 0\nvariable.previousEMA300 = 0\nvariable.previousEMA600 = 0\nvariable.previousEMA1200 = 0","id":"db307137-4eec-41a0-93ca-af5623c7ecf5","savedPayload":{"position":{"x":26234.257321434197,"y":18746.855130232732},"targetPosition":{"x":27236.62215014055,"y":19397.79846114876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2c17eb75-3069-47ad-b303-be44390e4736","savedPayload":{"position":{"x":27236.62215014055,"y":19397.79846114876},"targetPosition":{"x":28238.9869788469,"y":20048.741792064786},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bd1f404b-64bb-4f2e-95f6-8d52f40bebad","savedPayload":{"position":{"x":29303.902854197415,"y":18310.95702422382},"targetPosition":{"x":29303.902854197415,"y":19506.140052003062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e4267570-575b-4e59-9667-cb78aaae052c","savedPayload":{"position":{"x":29303.902854197415,"y":19506.140052003062},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"Base7 EMAs","code":"{\n \"codeName\": \"Base7-EMAs\",\n \"singularVariableName\": \"base7EMA\",\n \"pluralVariableName\": \"base7EMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base7-EMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"4e36a5a5-7a03-43a9-b3e0-0c5763c29199","savedPayload":{"position":{"x":29710.664523421965,"y":19298.88462965432},"targetPosition":{"x":28645.74864807145,"y":19841.486369716044},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base7-EMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base7-EMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"15ccbcff-318d-46db-93f2-d649b57fd52a","savedPayload":{"position":{"x":29490.870671773224,"y":20686.608393417817},"targetPosition":{"x":28645.74864807145,"y":19841.486369716044},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"b91e2094-b8f7-4f7f-a956-30de55c693b7","savedPayload":{"position":{"x":29960.810234490837,"y":19402.09614423023},"targetPosition":{"x":29031.97857125028,"y":20154.249194648393},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5163d7f4-3b6c-41c3-b7f6-ec13109fcb0e","savedPayload":{"position":{"x":29031.97857125028,"y":20154.249194648393},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"ea560ee8-06c9-4348-a5b4-24992035245e","savedPayload":{"position":{"x":30412.063215358397,"y":20287.729985121434},"targetPosition":{"x":29257.605061684062,"y":20597.066115093996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4b5dcfda-66ec-4a20-9c4c-0718b51cf761","savedPayload":{"position":{"x":29257.605061684062,"y":20597.066115093996},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA7","code":"{\n \"codeName\": \"ema7\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema7","id":"90e20354-4dd5-4e56-aaf5-cf725f02c1e6","savedPayload":{"position":{"x":30464.083590839236,"y":21280.337880218176},"targetPosition":{"x":29283.61524942448,"y":21093.370062642367},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4adf5737-3d1a-4610-9a31-89faddbe679e","savedPayload":{"position":{"x":29283.61524942448,"y":21093.370062642367},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA14","code":"{\n \"codeName\": \"ema14\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema14","id":"b158c3b7-a93f-4e13-8d80-dff1a7172416","savedPayload":{"position":{"x":30107.87656542243,"y":22208.28890689861},"targetPosition":{"x":29105.51173671608,"y":21557.345575982585},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6c0f0982-caf3-4c7e-a729-54c511ffb65e","savedPayload":{"position":{"x":29105.51173671608,"y":21557.345575982585},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA21","code":"{\n \"codeName\": \"ema21\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema21","id":"3ce74000-2892-4b42-a12e-c8efc1504710","savedPayload":{"position":{"x":29951.883266705,"y":21908.367073772908},"targetPosition":{"x":28754.090238925754,"y":21908.76707377291},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ff999b95-7623-4eee-b11c-f53dae69f69c","savedPayload":{"position":{"x":28754.090238925754,"y":21908.76707377291},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA28","code":"{\n \"codeName\": \"ema28\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema28","id":"95ccd057-5e33-4b72-a7b0-e44479f3b195","savedPayload":{"position":{"x":29483.17775336478,"y":22089.480586481313},"targetPosition":{"x":28290.114725585536,"y":22086.870586481313},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a30ef925-4ab9-45f6-98f3-6723e57fbf97","savedPayload":{"position":{"x":28290.114725585536,"y":22086.870586481313},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA35","code":"{\n \"codeName\": \"ema35\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema35","id":"83513896-dfd7-45a8-aa9a-1922dc9b5b97","savedPayload":{"position":{"x":28989.583805816408,"y":22062.860398740893},"targetPosition":{"x":27793.810778037165,"y":22060.860398740893},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d2d2b734-e754-4b6c-856b-053eb5b13694","savedPayload":{"position":{"x":27793.810778037165,"y":22060.860398740893},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA70","code":"{\n \"codeName\": \"ema70\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema70","id":"23ca8b8e-400b-4638-946c-c871a632d17b","savedPayload":{"position":{"x":28546.17688537081,"y":21835.233908307113},"targetPosition":{"x":27350.993857591566,"y":21835.233908307113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"863c717d-d171-4616-ac79-dd4a5956bfb5","savedPayload":{"position":{"x":27350.993857591566,"y":21835.233908307113},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA140","code":"{\n \"codeName\": \"ema140\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema140","id":"fb446f49-e024-4246-9783-d41ea25a8a63","savedPayload":{"position":{"x":25973.315157308698,"y":21991.605725190006},"targetPosition":{"x":27038.231032659212,"y":21449.00398512828},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ba2fbf20-a074-4d89-b1f8-2e00f141b25e","savedPayload":{"position":{"x":27038.231032659212,"y":21449.00398512828},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EM210","code":"{\n \"codeName\": \"ema210\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema210","id":"dd3ed495-e64d-4b76-ba8a-8a6fbaebc223","savedPayload":{"position":{"x":25716.056766054226,"y":21031.504338354884},"targetPosition":{"x":26909.601837031976,"y":20968.95329171072},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fbc7524a-d8ff-40c4-9691-1397bd774535","savedPayload":{"position":{"x":26909.601837031976,"y":20968.95329171072},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA280","code":"{\n \"codeName\": \"ema280\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema280","id":"45655778-7860-46a8-9ee1-12ab9d4f463b","savedPayload":{"position":{"x":25871.547946377,"y":20049.771663073378},"targetPosition":{"x":26987.347427193363,"y":20478.086954069968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"49670f83-5516-4fed-bfc9-3ea4ac3a4865","savedPayload":{"position":{"x":26987.347427193363,"y":20478.086954069968},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA350","code":"{\n \"codeName\": \"ema350\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema350","id":"6203288d-a807-49ee-a209-4466b959be33","savedPayload":{"position":{"x":26412.90286060618,"y":19216.15819766301},"targetPosition":{"x":27258.024884307953,"y":20061.280221364785},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4f06f627-f385-494a-bb6a-a06c62d0d20f","savedPayload":{"position":{"x":27258.024884307953,"y":20061.280221364785},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA700","code":"{\n \"codeName\": \"ema700\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema700","id":"1663af80-49b7-4593-ad91-6c79fc61f61c","savedPayload":{"position":{"x":27246.516326016546,"y":18674.80328343383},"targetPosition":{"x":27674.831617013137,"y":19790.602764250194},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b3810873-e8ea-4466-b011-095b648f0064","savedPayload":{"position":{"x":27674.831617013137,"y":19790.602764250194},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA1400","code":"{\n \"codeName\": \"ema1400\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema1400","id":"92eee4c6-788b-4aa2-8608-5a026f9f5590","savedPayload":{"position":{"x":28228.249001298052,"y":18519.312103111057},"targetPosition":{"x":28165.69795465389,"y":19712.857174088807},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"26a199f6-c167-4de7-9513-0d9c453d8108","savedPayload":{"position":{"x":28165.69795465389,"y":19712.857174088807},"targetPosition":{"x":28103.146908009727,"y":20906.402245066558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"e78e6e7a-03db-4e3b-ae8e-b1c309b6ec9e","savedPayload":{"position":{"x":28103.146908009727,"y":20906.402245066558},"targetPosition":{"x":28645.74864807145,"y":19841.486369716044},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\nlet candle = record.current \n\nvariable.ema7 = calculateEMA(7, candle.close, variable.previousEMA7)\nvariable.previousEMA7 = variable.ema7\n\nvariable.ema14 = calculateEMA(14, candle.close, variable.previousEMA14)\nvariable.previousEMA14 = variable.ema14\n\nvariable.ema21 = calculateEMA(21, candle.close, variable.previousEMA21)\nvariable.previousEMA21 = variable.ema21\n\nvariable.ema28 = calculateEMA(28, candle.close, variable.previousEMA28)\nvariable.previousEMA28 = variable.ema28\n\nvariable.ema35 = calculateEMA(35, candle.close, variable.previousEMA35)\nvariable.previousEMA35 = variable.ema35\n\nvariable.ema70 = calculateEMA(70, candle.close, variable.previousEMA70)\nvariable.previousEMA70 = variable.ema70\n\nvariable.ema140 = calculateEMA(140, candle.close, variable.previousEMA140)\nvariable.previousEMA140 = variable.ema140\n\nvariable.ema210 = calculateEMA(210, candle.close, variable.previousEMA210)\nvariable.previousEMA210 = variable.ema210\n\nvariable.ema280 = calculateEMA(280, candle.close, variable.previousEMA280)\nvariable.previousEMA280 = variable.ema280\n\nvariable.ema350 = calculateEMA(350, candle.close, variable.previousEMA350)\nvariable.previousEMA350 = variable.ema350\n\nvariable.ema700 = calculateEMA(700, candle.close, variable.previousEMA700)\nvariable.previousEMA700 = variable.ema700\n\nvariable.ema1400 = calculateEMA(1400, candle.close, variable.previousEMA1400)\nvariable.previousEMA1400 = variable.ema1400\n\n\nfunction calculateEMA(periods, price, previousEMA) {\n let k = 2 / (periods + 1)\n return price * k + previousEMA * (1-k)\n}","id":"9eb652ba-7df8-41a2-bc5f-c6a1922d8b9c","savedPayload":{"position":{"x":25960.974205820374,"y":21512.181878621344},"targetPosition":{"x":26713.127256238535,"y":20583.35021538079},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"659c5fa3-33f6-4127-b4f0-dc1bb0c25691","savedPayload":{"position":{"x":26713.127256238535,"y":20583.35021538079},"targetPosition":{"x":27465.280306656696,"y":19654.518552140235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\nvariable.previousEMA7 = 0\nvariable.previousEMA14 = 0\nvariable.previousEMA21 = 0\nvariable.previousEMA28 = 0\nvariable.previousEMA35 = 0\nvariable.previousEMA70 = 0\nvariable.previousEMA140 = 0\nvariable.previousEMA210 = 0\nvariable.previousEMA280 = 0\nvariable.previousEMA350 = 0\nvariable.previousEMA700 = 0\nvariable.previousEMA1400 = 0","id":"03253e10-8b5a-4d6d-abc1-1b171138444c","savedPayload":{"position":{"x":26608.649724663515,"y":17422.919590507507},"targetPosition":{"x":27036.965015660106,"y":18538.71907132387},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"07d4aaa2-43ba-42a3-9a5b-73e5866c1307","savedPayload":{"position":{"x":27036.965015660106,"y":18538.71907132387},"targetPosition":{"x":27465.280306656696,"y":19654.518552140235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"91a31871-1901-4a45-9b00-c5aa30a37bf2","savedPayload":{"position":{"x":27465.280306656696,"y":19654.518552140235},"targetPosition":{"x":28645.74864807145,"y":19841.486369716044},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b42348a4-6a26-4d46-aa2e-cbf5fa0a4984","savedPayload":{"position":{"x":28645.74864807145,"y":19841.486369716044},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"EMA Base 7","id":"a52a9764-3ef2-44e6-82bd-e6538058119d"}}},{"type":"Product Definition","name":"Base11 EMAs","code":"{\n \"codeName\": \"Base11-EMAs\",\n \"singularVariableName\": \"base11EMA\",\n \"pluralVariableName\": \"base11EMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base11-EMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"5586aa74-2ecb-4b71-83ea-d621aa400660","savedPayload":{"position":{"x":29096.647431848673,"y":19912.901721227612},"targetPosition":{"x":27916.179090433918,"y":19725.933903651803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base11-EMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base11-EMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"0198fa3f-98e3-4c57-a4a4-a7a8e27905d3","savedPayload":{"position":{"x":28103.146908009727,"y":20906.402245066558},"targetPosition":{"x":27916.179090433918,"y":19725.933903651803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"b0559af0-2d8b-401c-8619-1eb20942ec2a","savedPayload":{"position":{"x":29124.636428553044,"y":21007.199377658108},"targetPosition":{"x":27987.949821818223,"y":20637.867510685817},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"33f46215-3b1a-422a-9305-89c6ffb342f6","savedPayload":{"position":{"x":27987.949821818223,"y":20637.867510685817},"targetPosition":{"x":26851.263215083403,"y":20268.535643713527},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"7bfaab7e-73a5-4086-9eb8-d4a6fdfbbbcf","savedPayload":{"position":{"x":28589.04798292437,"y":21333.45151906404},"targetPosition":{"x":27393.864955145127,"y":21333.45151906404},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6155e3b0-2648-407e-b085-4eac778ca3e8","savedPayload":{"position":{"x":27393.864955145127,"y":21333.45151906404},"targetPosition":{"x":26851.263215083403,"y":20268.535643713527},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA11","code":"{\n \"codeName\": \"ema11\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema11","id":"16f9f1d2-c73c-4eb6-9240-0d4015e3252a","savedPayload":{"position":{"x":27677.114375890356,"y":21405.222250448347},"targetPosition":{"x":26481.931348111113,"y":21405.222250448347},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b925a6aa-04ee-465b-aa93-fc02066b4e3c","savedPayload":{"position":{"x":26481.931348111113,"y":21405.222250448347},"targetPosition":{"x":26851.263215083403,"y":20268.535643713527},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA22","code":"{\n \"codeName\": \"ema22\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema22","id":"21cca6ba-53d3-4fd0-b3c9-43a71702e4f3","savedPayload":{"position":{"x":24721.431464382375,"y":21353.739123836975},"targetPosition":{"x":25786.34733973289,"y":20811.13738377525},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7f02db91-5147-438c-98c9-e024fba210a2","savedPayload":{"position":{"x":25786.34733973289,"y":20811.13738377525},"targetPosition":{"x":26851.263215083403,"y":20268.535643713527},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA33","code":"{\n \"codeName\": \"ema33\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema33","id":"69184123-7d64-41a6-ad3f-8f53bf358f95","savedPayload":{"position":{"x":24577.890001613763,"y":19529.871909768946},"targetPosition":{"x":25714.576608348583,"y":19899.203776741237},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"55be1211-a38f-4ff4-8d12-fa78fa8ba2dd","savedPayload":{"position":{"x":25714.576608348583,"y":19899.203776741237},"targetPosition":{"x":26851.263215083403,"y":20268.535643713527},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA55","code":"{\n \"codeName\": \"ema55\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema55","id":"5281da7d-c090-4637-9a42-dc28599f0385","savedPayload":{"position":{"x":25766.059734959956,"y":18138.703893012498},"targetPosition":{"x":26308.66147502168,"y":19203.619768363013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"710e13ba-afd9-41c2-b7e8-f0ef7be4bb53","savedPayload":{"position":{"x":26308.66147502168,"y":19203.619768363013},"targetPosition":{"x":26851.263215083403,"y":20268.535643713527},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA111","code":"{\n \"codeName\": \"ema111\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema111","id":"f0dd0fa6-ace3-44ea-a701-e36808f6e348","savedPayload":{"position":{"x":27589.926949027984,"y":17995.162430243887},"targetPosition":{"x":27220.595082055694,"y":19131.849036978707},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"461f6d65-e227-4362-b62c-16b7225014f6","savedPayload":{"position":{"x":27220.595082055694,"y":19131.849036978707},"targetPosition":{"x":26851.263215083403,"y":20268.535643713527},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"6a451383-677d-4da8-a15d-f746cda23991","savedPayload":{"position":{"x":26851.263215083403,"y":20268.535643713527},"targetPosition":{"x":27916.179090433918,"y":19725.933903651803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\nlet candle = record.current \n\nvariable.ema11 = calculateEMA(11, candle.close, variable.previousEMA11)\nvariable.previousEMA11 = variable.ema11\n\nvariable.ema22 = calculateEMA(22, candle.close, variable.previousEMA22)\nvariable.previousEMA22 = variable.ema22\n\nvariable.ema33 = calculateEMA(33, candle.close, variable.previousEMA33)\nvariable.previousEMA33 = variable.ema33\n\nvariable.ema55 = calculateEMA(55, candle.close, variable.previousEMA55)\nvariable.previousEMA55 = variable.ema55\n\nvariable.ema111 = calculateEMA(111, candle.close, variable.previousEMA111)\nvariable.previousEMA111 = variable.ema111\n\n\nfunction calculateEMA(periods, price, previousEMA) {\n let k = 2 / (periods + 1)\n return price * k + previousEMA * (1-k)\n}","id":"38ceaace-d9e5-482a-a15b-c7bb06767056","savedPayload":{"position":{"x":24762.140759383474,"y":19499.484139895154},"targetPosition":{"x":25916.59891305781,"y":19190.148009922592},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"db87f908-271b-44be-8756-706c3ff98c29","savedPayload":{"position":{"x":25916.59891305781,"y":19190.148009922592},"targetPosition":{"x":27071.057066732144,"y":18880.81187995003},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\nvariable.previousEMA11 = 0\nvariable.previousEMA22 = 0\nvariable.previousEMA33 = 0\nvariable.previousEMA55 = 0\nvariable.previousEMA111 = 0","id":"5c34d18a-ac4e-4af6-aa52-cb4c0f016e6d","savedPayload":{"position":{"x":27689.72932667727,"y":16571.89557260136},"targetPosition":{"x":27380.393196704706,"y":17726.353726275694},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f34dd488-d3ea-4bd0-b4f8-68ea81c4a635","savedPayload":{"position":{"x":27380.393196704706,"y":17726.353726275694},"targetPosition":{"x":27071.057066732144,"y":18880.81187995003},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e02efb2a-bb39-4a12-89c8-37c254e9bf5f","savedPayload":{"position":{"x":27071.057066732144,"y":18880.81187995003},"targetPosition":{"x":27916.179090433918,"y":19725.933903651803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"21df4f44-96eb-453b-8652-4ce2dc03424c","savedPayload":{"position":{"x":27916.179090433918,"y":19725.933903651803},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"EMA Base 11","id":"cef26be6-07a9-415a-8439-99d06b1e1cfb"}}},{"type":"Product Definition","name":"Base12 EMAs","code":"{\n \"codeName\": \"Base12-EMAs\",\n \"singularVariableName\": \"base12EMA\",\n \"pluralVariableName\": \"base12EMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base12-EMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"ea61dfed-8f93-4b4f-8575-61ca2547ea90","savedPayload":{"position":{"x":28589.04798292437,"y":19203.619768363013},"targetPosition":{"x":27393.864955145127,"y":19203.619768363013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base12-EMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base12-EMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"b251aca0-1996-4d13-99ed-6fb9796ec8d6","savedPayload":{"position":{"x":27393.864955145127,"y":20395.82588356123},"targetPosition":{"x":27393.864955145127,"y":19203.619768363013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"f1df9aec-05c4-40d0-a82c-d17a0e2f1596","savedPayload":{"position":{"x":28391.02257088427,"y":19697.2901516056},"targetPosition":{"x":27195.839543105027,"y":19697.2901516056},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2c0c9e78-796b-4845-8cbb-c153a6b7c791","savedPayload":{"position":{"x":27195.839543105027,"y":19697.2901516056},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"355a63aa-8c8c-4eca-a246-272eb464d094","savedPayload":{"position":{"x":28329.96064114153,"y":20127.510446472097},"targetPosition":{"x":27271.67862743595,"y":19572.08119862965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"256d27f8-b34e-46ca-9b5f-531823534e11","savedPayload":{"position":{"x":27271.67862743595,"y":19572.08119862965},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA12","code":"{\n \"codeName\": \"ema12\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema12","id":"121d3c83-9fb4-44a1-93ed-e9755dfcc59e","savedPayload":{"position":{"x":27571.279301675953,"y":20983.884648091695},"targetPosition":{"x":26892.337957703163,"y":20000.26829943945},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b386dd09-a2a1-4669-a73b-6df619dee15d","savedPayload":{"position":{"x":26892.337957703163,"y":20000.26829943945},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA24","code":"{\n \"codeName\": \"ema24\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema24","id":"e4418d5e-74e5-440c-bd58-f8f18245ba6d","savedPayload":{"position":{"x":26501.523402662402,"y":21389.589546482875},"targetPosition":{"x":26357.460008196387,"y":20203.12074863504},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c23ef82a-9c9a-477f-8d48-14d93f06e3c6","savedPayload":{"position":{"x":26357.460008196387,"y":20203.12074863504},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA36","code":"{\n \"codeName\": \"ema36\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema36","id":"21dc0fd4-01f5-428b-9a9e-45307981af50","savedPayload":{"position":{"x":25365.76112860874,"y":21251.68303869826},"targetPosition":{"x":25789.578871169557,"y":20134.16749474273},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e613c9c6-61da-4830-9289-8b400c3b41d7","savedPayload":{"position":{"x":25789.578871169557,"y":20134.16749474273},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA48","code":"{\n \"codeName\": \"ema48\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema48","id":"6afd224b-ba9c-49eb-973f-e7e3d609e82e","savedPayload":{"position":{"x":23924.021571843296,"y":19704.11962068215},"targetPosition":{"x":25068.709092786834,"y":19360.385785734677},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e49e8056-f659-4090-91e7-0e83b4a3654e","savedPayload":{"position":{"x":25068.709092786834,"y":19360.385785734677},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA60","code":"{\n \"codeName\": \"ema60\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema60","id":"2db84126-00b9-4cde-bb35-66f9fcb8bfc6","savedPayload":{"position":{"x":23852.459930900863,"y":18642.716315635585},"targetPosition":{"x":25032.928272315618,"y":18829.684133211395},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"77a0ccf0-aa97-4a11-b1db-bd74d33c28b7","savedPayload":{"position":{"x":25032.928272315618,"y":18829.684133211395},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA120","code":"{\n \"codeName\": \"ema120\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema120","id":"e3f30f2b-23c8-4bac-b19e-69ae740a4773","savedPayload":{"position":{"x":24248.510754981064,"y":17655.3755491504},"targetPosition":{"x":25230.953684355718,"y":18336.013749968803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fca691ac-2651-4619-af01-48a0fafcfd75","savedPayload":{"position":{"x":25230.953684355718,"y":18336.013749968803},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA240","code":"{\n \"codeName\": \"ema240\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema240","id":"99b8a3c3-7e86-40aa-ad44-82a0bdb1f208","savedPayload":{"position":{"x":25033.731321130203,"y":16937.652268968854},"targetPosition":{"x":25623.563967430287,"y":17977.15210987803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6f194951-d813-4418-be45-420680399c17","savedPayload":{"position":{"x":25623.563967430287,"y":17977.15210987803},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA360","code":"{\n \"codeName\": \"ema360\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema360","id":"fd430a5b-2a43-40c5-ae9d-4c7f901b527d","savedPayload":{"position":{"x":26052.599066124174,"y":16631.700373029267},"targetPosition":{"x":26132.997839927273,"y":17824.176161908235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d9b00ca8-b18b-43da-9f3d-889fe101120c","savedPayload":{"position":{"x":26132.997839927273,"y":17824.176161908235},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA480","code":"{\n \"codeName\": \"ema480\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema480","id":"4601277a-9f86-41a4-81b1-21b3c13d1d34","savedPayload":{"position":{"x":27103.314737474073,"y":16798.117386576236},"targetPosition":{"x":26658.355675602223,"y":17907.38466868172},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2eccd071-3101-47bb-a8e6-008703cf6848","savedPayload":{"position":{"x":26658.355675602223,"y":17907.38466868172},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA600","code":"{\n \"codeName\": \"ema600\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema600","id":"5c687030-e498-4126-8ce9-0c99a427168e","savedPayload":{"position":{"x":27977.77121028697,"y":17403.942379106058},"targetPosition":{"x":27095.58391200867,"y":18210.29716494663},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e715e341-88d4-477c-9d7f-b0bb85ee3022","savedPayload":{"position":{"x":27095.58391200867,"y":18210.29716494663},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA1200","code":"{\n \"codeName\": \"ema1200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema1200","id":"7c39560e-7434-4f50-aa3d-b93e131ce066","savedPayload":{"position":{"x":28502.77165561745,"y":18329.184280892256},"targetPosition":{"x":27358.08413467391,"y":18672.91811583973},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f1939b18-3af9-4376-b8ba-6f506b215f82","savedPayload":{"position":{"x":27358.08413467391,"y":18672.91811583973},"targetPosition":{"x":26213.396613730372,"y":19016.651950787204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"512144fe-195c-47ae-b5b7-8167ba61a4bb","savedPayload":{"position":{"x":26213.396613730372,"y":19016.651950787204},"targetPosition":{"x":27393.864955145127,"y":19203.619768363013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\nlet candle = record.current \n\nvariable.ema12 = calculateEMA(12, candle.close, variable.previousEMA12)\nvariable.previousEMA12 = variable.ema12\n\nvariable.ema24 = calculateEMA(24, candle.close, variable.previousEMA24)\nvariable.previousEMA24 = variable.ema24\n\nvariable.ema36 = calculateEMA(36, candle.close, variable.previousEMA36)\nvariable.previousEMA36 = variable.ema36\n\nvariable.ema48 = calculateEMA(48, candle.close, variable.previousEMA48)\nvariable.previousEMA48 = variable.ema48\n\nvariable.ema60 = calculateEMA(60, candle.close, variable.previousEMA60)\nvariable.previousEMA60 = variable.ema60\n\nvariable.ema120 = calculateEMA(120, candle.close, variable.previousEMA120)\nvariable.previousEMA120 = variable.ema120\n\nvariable.ema240 = calculateEMA(240, candle.close, variable.previousEMA240)\nvariable.previousEMA240 = variable.ema240\n\nvariable.ema360 = calculateEMA(360, candle.close, variable.previousEMA360)\nvariable.previousEMA360 = variable.ema360\n\nvariable.ema480 = calculateEMA(480, candle.close, variable.previousEMA480)\nvariable.previousEMA480 = variable.ema480\n\nvariable.ema600 = calculateEMA(600, candle.close, variable.previousEMA600)\nvariable.previousEMA600 = variable.ema600\n\nvariable.ema1200 = calculateEMA(1200, candle.close, variable.previousEMA1200)\nvariable.previousEMA1200 = variable.ema1200\n\nfunction calculateEMA(periods, price, previousEMA) {\n let k = 2 / (periods + 1)\n return price * k + previousEMA * (1-k)\n}","id":"c3936de6-e8b4-4459-8c31-d97bea84cbfa","savedPayload":{"position":{"x":24975.29817593659,"y":17166.520844955077},"targetPosition":{"x":26091.097656752954,"y":17594.836135951668},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c4c50d01-1dff-4700-853a-986821fd55c5","savedPayload":{"position":{"x":26091.097656752954,"y":17594.836135951668},"targetPosition":{"x":27206.897137569318,"y":18023.151426948258},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\nvariable.previousEMA12 = 0\nvariable.previousEMA24 = 0\nvariable.previousEMA36 = 0\nvariable.previousEMA48 = 0\nvariable.previousEMA60 = 0\nvariable.previousEMA120 = 0\nvariable.previousEMA240 = 0\nvariable.previousEMA360 = 0\nvariable.previousEMA480 = 0\nvariable.previousEMA600 = 0\nvariable.previousEMA1200 = 0\n","id":"c1639309-d20d-47cb-bb44-f6d45fd498a2","savedPayload":{"position":{"x":29064.560464050428,"y":16518.84532611193},"targetPosition":{"x":28135.728800809873,"y":17270.998376530093},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6a845db8-4418-4417-80ac-11114304a476","savedPayload":{"position":{"x":28135.728800809873,"y":17270.998376530093},"targetPosition":{"x":27206.897137569318,"y":18023.151426948258},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1f5b9ce3-8278-4359-a57c-2c9e275a64cc","savedPayload":{"position":{"x":27206.897137569318,"y":18023.151426948258},"targetPosition":{"x":27393.864955145127,"y":19203.619768363013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"43d6f23d-d206-47e9-95d4-6bef34377014","savedPayload":{"position":{"x":27393.864955145127,"y":19203.619768363013},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"Base30 EMAs","code":"{\n \"codeName\": \"Base30-EMAs\",\n \"singularVariableName\": \"base30EMA\",\n \"pluralVariableName\": \"base30EMAs\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base30-EMAs/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"dea3491f-aae7-4419-a98f-108cf5d69b87","savedPayload":{"position":{"x":27465.280306656696,"y":19654.518552140235},"targetPosition":{"x":27278.312489080887,"y":18474.05021072548},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base30-EMAs/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/EMA/@Exchange/Output/Base30-EMAs/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"6624337b-4afd-44b8-a2ea-3f1779ddda7e","savedPayload":{"position":{"x":26213.396613730372,"y":19016.651950787204},"targetPosition":{"x":27278.312489080887,"y":18474.05021072548},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"d3c4e9c3-9823-4809-997e-dc36dac93b22","savedPayload":{"position":{"x":27983.94409383859,"y":18769.994362941412},"targetPosition":{"x":26788.761066059345,"y":18769.994362941412},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cdf3538e-a8e5-44e5-9263-e5ffe18e65c2","savedPayload":{"position":{"x":26788.761066059345,"y":18769.994362941412},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"af0bf0dd-e517-4723-b0cc-832261c78c76","savedPayload":{"position":{"x":27412.935731291476,"y":18804.53400559273},"targetPosition":{"x":26217.75270351223,"y":18804.53400559273},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"27b3ce25-c547-4003-9a1c-2214b22e4fd8","savedPayload":{"position":{"x":26217.75270351223,"y":18804.53400559273},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA30","code":"{\n \"codeName\": \"ema30\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema30","id":"d49aaf82-1063-490d-bedb-3c63d639e097","savedPayload":{"position":{"x":24959.006606425544,"y":19510.58485856796},"targetPosition":{"x":25696.09853590233,"y":18569.756522795833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c92e3720-fc04-45ff-99e7-95abc2ddabea","savedPayload":{"position":{"x":25696.09853590233,"y":18569.756522795833},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA60","code":"{\n \"codeName\": \"ema60\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema60","id":"47f6b7dc-d0e0-4707-9e20-4113af4916ce","savedPayload":{"position":{"x":24253.416027450607,"y":18609.965025944508},"targetPosition":{"x":25343.30324641486,"y":18119.446606484107},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d489aab4-6b93-490a-95b0-5b586a80960a","savedPayload":{"position":{"x":25343.30324641486,"y":18119.446606484107},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA90","code":"{\n \"codeName\": \"ema90\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema90","id":"d2ac9ce8-32ab-4802-ab41-b959112a696b","savedPayload":{"position":{"x":24047.185503075416,"y":17484.60147629985},"targetPosition":{"x":25240.187984227265,"y":17556.764831661778},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7b763dc2-e990-44b7-b67f-93d27e5b7507","savedPayload":{"position":{"x":25240.187984227265,"y":17556.764831661778},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA120","code":"{\n \"codeName\": \"ema120\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema120","id":"41283546-a88c-41a6-87a6-48f178b378f5","savedPayload":{"position":{"x":24387.559961087078,"y":16392.30143675661},"targetPosition":{"x":25410.375213233096,"y":17010.61481189016},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"19b80a14-7510-4066-aa0a-43e8d92f8bec","savedPayload":{"position":{"x":25410.375213233096,"y":17010.61481189016},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA150","code":"{\n \"codeName\": \"ema150\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema150","id":"d82aeb70-2857-4d97-8f42-6fa22acc3ec1","savedPayload":{"position":{"x":25196.563715112017,"y":15583.29768273167},"targetPosition":{"x":25814.877090245565,"y":16606.11293487769},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2dcea6aa-8e40-44d9-b540-e831f54b0e19","savedPayload":{"position":{"x":25814.877090245565,"y":16606.11293487769},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA300","code":"{\n \"codeName\": \"ema300\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema300","id":"3bfc5b00-3b36-4b7e-b549-4cac3826de4f","savedPayload":{"position":{"x":26288.863754655256,"y":15242.923224720009},"targetPosition":{"x":26361.027110017185,"y":16435.925705871858},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7cc56b79-f74d-465e-8e4d-070be004c71b","savedPayload":{"position":{"x":26361.027110017185,"y":16435.925705871858},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA600","code":"{\n \"codeName\": \"ema600\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema600","id":"22bc84a5-61f6-479e-a87d-b1aee1b8416d","savedPayload":{"position":{"x":27414.227304299915,"y":15449.153749095201},"targetPosition":{"x":26923.708884839514,"y":16539.040968059453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0e28ed26-69c1-4a8e-b13f-cef5faf27fc8","savedPayload":{"position":{"x":26923.708884839514,"y":16539.040968059453},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA900","code":"{\n \"codeName\": \"ema900\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema900","id":"bd0354df-70f8-44e7-b988-49c514433ee3","savedPayload":{"position":{"x":28314.847136923367,"y":16154.744328070137},"targetPosition":{"x":27374.01880115124,"y":16891.836257546922},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1dfbb7b7-1ddc-4079-a17d-abef03fcd978","savedPayload":{"position":{"x":27374.01880115124,"y":16891.836257546922},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA1200","code":"{\n \"codeName\": \"ema1200\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema1200","id":"fed49d03-931b-45a8-ac59-60859500e8ea","savedPayload":{"position":{"x":28784.402102517168,"y":17198.05266328994},"targetPosition":{"x":27608.79628394814,"y":17413.490425156822},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2854d03b-388d-42be-bd49-0b0ed1293a30","savedPayload":{"position":{"x":27608.79628394814,"y":17413.490425156822},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA1500","code":"{\n \"codeName\": \"ema1500\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema1500","id":"1486d92f-10f4-4e34-b603-b0a18408713c","savedPayload":{"position":{"x":28715.322817214525,"y":18340.069388384178},"targetPosition":{"x":27574.25664129682,"y":17984.498787703942},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9936d329-ffe9-4c8a-929c-b6f4a3a38954","savedPayload":{"position":{"x":27574.25664129682,"y":17984.498787703942},"targetPosition":{"x":26433.190465379113,"y":17628.928187023706},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"3dda31b0-7d30-43c2-9b91-74c25cffed38","savedPayload":{"position":{"x":26433.190465379113,"y":17628.928187023706},"targetPosition":{"x":27278.312489080887,"y":18474.05021072548},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\nvariable.ema30 = calculateEMA(30, candle.close, variable.previousEMA30)\nvariable.previousEMA30 = variable.ema30\n\nvariable.ema60 = calculateEMA(60, candle.close, variable.previousEMA60)\nvariable.previousEMA60 = variable.ema60\n\nvariable.ema90 = calculateEMA(90, candle.close, variable.previousEMA90)\nvariable.previousEMA90 = variable.ema90\n\nvariable.ema120 = calculateEMA(120, candle.close, variable.previousEMA120)\nvariable.previousEMA120 = variable.ema120\n\nvariable.ema150 = calculateEMA(150, candle.close, variable.previousEMA150)\nvariable.previousEMA150 = variable.ema150\n\nvariable.ema300 = calculateEMA(300, candle.close, variable.previousEMA300)\nvariable.previousEMA300 = variable.ema300\n\nvariable.ema600 = calculateEMA(600, candle.close, variable.previousEMA600)\nvariable.previousEMA600 = variable.ema600\n\nvariable.ema900 = calculateEMA(900, candle.close, variable.previousEMA900)\nvariable.previousEMA900 = variable.ema900\n\nvariable.ema1200 = calculateEMA(1200, candle.close, variable.previousEMA1200)\nvariable.previousEMA1200 = variable.ema1200\n\nvariable.ema1500 = calculateEMA(1500, candle.close, variable.previousEMA1500)\nvariable.previousEMA1500 = variable.ema1500\n\nfunction calculateEMA(periods, price, previousEMA) {\n let k = 2 / (periods + 1)\n return price * k + previousEMA * (1-k)\n}","id":"df900089-7554-4b01-88d1-801706c194a5","savedPayload":{"position":{"x":26519.027567310557,"y":15404.404677962264},"targetPosition":{"x":27169.970898226584,"y":16406.769506668614},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ff38e18a-c36c-4c4a-8d5b-b7230451c098","savedPayload":{"position":{"x":27169.970898226584,"y":16406.769506668614},"targetPosition":{"x":27820.91422914261,"y":17409.134335374965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\nvariable.previousEMA30 = 0\nvariable.previousEMA60 = 0\nvariable.previousEMA90 = 0\nvariable.previousEMA120 = 0\nvariable.previousEMA150 = 0\nvariable.previousEMA300 = 0\nvariable.previousEMA600 = 0\nvariable.previousEMA900 = 0\nvariable.previousEMA1200 = 0\nvariable.previousEMA1500 = 0\n","id":"14cfa602-002c-439e-b658-24cc8d2d1c14","savedPayload":{"position":{"x":30208.00437109811,"y":17284.03224208664},"targetPosition":{"x":29014.45930012036,"y":17346.583288730802},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2489fa2c-03cb-4007-b426-88158b8ab442","savedPayload":{"position":{"x":29014.45930012036,"y":17346.583288730802},"targetPosition":{"x":27820.91422914261,"y":17409.134335374965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ff57a6a1-1133-4cce-abab-d958d5936360","savedPayload":{"position":{"x":27820.91422914261,"y":17409.134335374965},"targetPosition":{"x":27278.312489080887,"y":18474.05021072548},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4394346c-7c25-478f-9be7-04f071dc673c","savedPayload":{"position":{"x":27278.312489080887,"y":18474.05021072548},"targetPosition":{"x":28458.78083049564,"y":18661.01802830129},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"274a20f7-97c3-452d-9ef7-aa35f482f5e3","savedPayload":{"position":{"x":28458.78083049564,"y":18661.01802830129},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Indicator Bot","name":"MACD","code":"{\n\"codeName\": \"MACD\",\n\"repo\": \"MACD-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Produces the MACD indicator.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"b1bf9ad6-e21f-43a9-b7a5-7d5dd0485b2e","savedPayload":{"position":{"x":28622.901832689266,"y":17829.29835889351},"targetPosition":{"x":27434.81681755315,"y":17829.29835889351},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"01d43ed0-2a80-4eca-bee6-db2fe40470fc"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"a8aa057b-aa19-45cb-bca0-7c3b57a350f7","savedPayload":{"position":{"x":27434.81681755315,"y":19042.121591581876},"targetPosition":{"x":27434.81681755315,"y":17829.29835889351},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"19b1d791-d2ea-4f25-a6e1-fe0a64450745"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"150637f5-1917-439e-916f-71e86edccd49","savedPayload":{"position":{"x":26237.941914873285,"y":17829.29835889351},"targetPosition":{"x":27434.81681755315,"y":17829.29835889351},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"c390cbd3-0ba0-4507-8614-42c88f4ce7e6"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"6854acc9-b22d-46e6-8967-e3843ca6db76","savedPayload":{"position":{"x":27434.81681755315,"y":16640.077023574977},"targetPosition":{"x":27434.81681755315,"y":17829.29835889351},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"89254c6c-9be2-4037-b063-467edc660d10"}}}],"id":"af39ab1b-b1c0-4869-9093-47ad8ff63494","savedPayload":{"position":{"x":27434.81681755315,"y":17829.29835889351},"targetPosition":{"x":28551.810789871775,"y":18267.686188233783},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"7e17f51a-deed-4976-a53b-d34c2e45f92f","savedPayload":{"position":{"x":29570.818130881584,"y":17081.147117661625},"targetPosition":{"x":28372.968800631057,"y":17081.147117661625},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"101cc7d6-d35b-4907-b530-f28d5e02ff0a"}}},{"type":"Status Dependency","name":"Olivia: Candles","code":"{\"mainUtility\": \"Market Ending Point\"}","id":"473dbe5e-6332-4005-8457-c77c3f5c65d0","savedPayload":{"position":{"x":27771.432066288242,"y":18123.039304162445},"targetPosition":{"x":28372.968800631057,"y":17081.147117661625},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"fa10270c-f94c-479d-9094-f974018e2029","savedPayload":{"position":{"x":27771.476629488257,"y":16039.332116887384},"targetPosition":{"x":28372.968800631057,"y":17081.147117661625},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}}],"id":"7a34fbb2-19de-4b82-88f2-c016f6959f74","savedPayload":{"position":{"x":28372.968800631057,"y":17081.147117661625},"targetPosition":{"x":28551.810789871775,"y":18267.686188233783},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"101cc7d6-d35b-4907-b530-f28d5e02ff0a","savedPayload":{"position":{"x":29489.962772949682,"y":17519.5349470019},"targetPosition":{"x":28551.810789871775,"y":18267.686188233783},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"1bc17bd6-0c20-4dfe-87dd-68db36113733","savedPayload":{"position":{"x":27585.383110034738,"y":17565.535378590626},"targetPosition":{"x":28551.810789871775,"y":18267.686188233783},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f2eca95a-37ae-448b-8ff9-c6dc4b133245","savedPayload":{"position":{"x":28730.652779112494,"y":19454.225258805942},"targetPosition":{"x":28551.810789871775,"y":18267.686188233783},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"44019efe-950a-4eec-a546-cd2ee26818b9","savedPayload":{"position":{"x":28551.810789871775,"y":18267.686188233783},"targetPosition":{"x":27613.65880679387,"y":19015.83742946567},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Produces the MACD indicator.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Daily\",\n \"startDate\": {\n \"takeItFromStatusDependency\": 0\n },\n \"endDate\": {\n \"takeItFromStatusDependency\": 1\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"c79ae046-983f-4acf-9c73-ab7e5d24fd85","savedPayload":{"position":{"x":29626.100209744563,"y":18136.218121881386},"targetPosition":{"x":28429.82625090982,"y":18136.218121881386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"dd5d3067-ff7b-4353-ba4f-c030bd3da796"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"daa8862c-2eda-4846-97d5-8684673baf3f","savedPayload":{"position":{"x":28429.82625090982,"y":19338.10732437144},"targetPosition":{"x":28429.82625090982,"y":18136.218121881386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"29e18539-f188-4d29-9531-25cd3ee8f282"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"59cb9a88-ccd4-4306-8822-bf9d0e526c02","savedPayload":{"position":{"x":27234.5339855368,"y":18136.218121881386},"targetPosition":{"x":28429.82625090982,"y":18136.218121881386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"8a9dde13-e4b8-40f3-80bc-ea51050b2f08"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"b09b8313-baaf-45f4-a0fb-b33614d58699","savedPayload":{"position":{"x":28429.82625090982,"y":16945.604644832372},"targetPosition":{"x":28429.82625090982,"y":18136.218121881386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"ec2611a6-699b-45a5-beec-679dd4b7734a"}}}],"id":"a95e4b61-4e49-4b99-bfb8-8bb917afe8df","savedPayload":{"position":{"x":28429.82625090982,"y":18136.218121881386},"targetPosition":{"x":28783.515194879114,"y":19282.84951601976},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"e02fb6a2-c775-46de-bce6-a40f7d40cd5c","savedPayload":{"position":{"x":30797.43533703721,"y":18403.230208435474},"targetPosition":{"x":29599.682638995066,"y":18403.230208435474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a9d544cb-9643-4f7e-9220-d3ec7397bcdf"}}},{"type":"Status Dependency","name":"Ending: Oliva Candles","code":"{\"mainUtility\": \"Market Ending Point\"}","id":"650627ec-3cab-4024-a17d-a43174f869e9","savedPayload":{"position":{"x":29599.682638995066,"y":19610.117662451532},"targetPosition":{"x":29599.682638995066,"y":18403.230208435474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}},{"type":"Status Dependency","name":"Starting: Charly Historic Trades","code":"{\"mainUtility\": \"Market Starting Point\"}","id":"62a1b4d7-e2fd-44bf-9e37-3108a7b5b956","savedPayload":{"position":{"x":28400.400697968547,"y":18403.230208435474},"targetPosition":{"x":29599.682638995066,"y":18403.230208435474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"38aa26d5-f3b2-43e6-8533-cd51d7128221","savedPayload":{"position":{"x":29599.682638995066,"y":17209.897364774406},"targetPosition":{"x":29599.682638995066,"y":18403.230208435474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}}}],"id":"e8291604-ea73-4217-b00c-51f65bdec1d6","savedPayload":{"position":{"x":29599.682638995066,"y":18403.230208435474},"targetPosition":{"x":28783.515194879114,"y":19282.84951601976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"a9d544cb-9643-4f7e-9220-d3ec7397bcdf","savedPayload":{"position":{"x":29953.37158296436,"y":19549.86160257385},"targetPosition":{"x":28783.515194879114,"y":19282.84951601976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"35b430c4-60bd-41ff-8a3a-7907146b0238","savedPayload":{"position":{"x":27804.7339250695,"y":18571.723297887987},"targetPosition":{"x":28783.515194879114,"y":19282.84951601976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"c6191bb8-66bf-4d67-9d1b-9364e7e6e84e","savedPayload":{"position":{"x":29154.317487417244,"y":18141.63740472831},"targetPosition":{"x":28783.515194879114,"y":19282.84951601976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8e5db945-8507-4489-9288-21cfa1e9301c","savedPayload":{"position":{"x":28783.515194879114,"y":19282.84951601976},"targetPosition":{"x":27613.65880679387,"y":19015.83742946567},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"MACD 122609","code":"{\n \"codeName\": \"MACD122609\",\n \"singularVariableName\": \"macd122609\",\n \"pluralVariableName\": \"macd122609s\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-122609/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"01d43ed0-2a80-4eca-bee6-db2fe40470fc","savedPayload":{"position":{"x":29001.605377723823,"y":19267.71259687947},"targetPosition":{"x":28134.293878936845,"y":20096.94729657655},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-122609/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-122609/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"dd5d3067-ff7b-4353-ba4f-c030bd3da796","savedPayload":{"position":{"x":29190.95693616379,"y":20665.56193462382},"targetPosition":{"x":28134.293878936845,"y":20096.94729657655},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"c30a9207-3220-4aa4-b96e-64a82200e356","savedPayload":{"position":{"x":29892.7447474843,"y":19910.909100744095},"targetPosition":{"x":28906.3904058284,"y":20594.257134915828},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"16c55335-8817-4121-a068-1c2d184fd814","savedPayload":{"position":{"x":28906.3904058284,"y":20594.257134915828},"targetPosition":{"x":27920.036064172495,"y":21277.60516908756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"0f82ce78-91ac-4b61-b514-bb1a67c2ef0e","savedPayload":{"position":{"x":30281.351809194515,"y":21706.120798616263},"targetPosition":{"x":29100.693936683507,"y":21491.862983851912},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"dedc2ef0-61e4-45bd-921d-187c9a6f3ae1","savedPayload":{"position":{"x":29100.693936683507,"y":21491.862983851912},"targetPosition":{"x":27920.036064172495,"y":21277.60516908756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Line","code":"{\n \"codeName\": \"line\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.line","id":"42754005-58a0-4a8b-b839-938d11aeb0ab","savedPayload":{"position":{"x":29804.205523210385,"y":22263.899510743464},"targetPosition":{"x":28603.384098344228,"y":22263.959510743465},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5fe10f78-a397-4fb1-bfeb-a506d44d60ef","savedPayload":{"position":{"x":28603.384098344228,"y":22263.959510743465},"targetPosition":{"x":27920.036064172495,"y":21277.60516908756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Signal","code":"{\n \"codeName\": \"signal\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.signal","id":"588045df-e0fb-4c07-adf3-1acd832088af","savedPayload":{"position":{"x":28908.469674274296,"y":22456.99304159857},"targetPosition":{"x":27705.778249408144,"y":22458.26304159857},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2926733e-3962-4e7b-a492-7fcb437f536e","savedPayload":{"position":{"x":26838.92619706162,"y":21798.24024123054},"targetPosition":{"x":27920.036064172495,"y":21277.60516908756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA12","code":"{\n \"codeName\": \"ema12\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema12","id":"d9f6340c-15cd-499b-ba74-007b4c569b22","savedPayload":{"position":{"x":28133.623147382747,"y":21960.953203259294},"targetPosition":{"x":26933.68172251659,"y":21960.953203259294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"29fb3e14-c1fd-4fa6-af1a-62b1c61ff1fc","savedPayload":{"position":{"x":26933.68172251659,"y":21960.953203259294},"targetPosition":{"x":27920.036064172495,"y":21277.60516908756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA26","code":"{\n \"codeName\": \"ema26\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema26","id":"748ed791-c88b-4776-b2cb-742758821641","savedPayload":{"position":{"x":25558.72031915047,"y":20849.08953955886},"targetPosition":{"x":26739.378191661483,"y":21063.34735432321},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d1b7a8ce-c89b-4e0f-ac04-0f45d575524d","savedPayload":{"position":{"x":26739.378191661483,"y":21063.34735432321},"targetPosition":{"x":27920.036064172495,"y":21277.60516908756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Histogram","code":"{\n \"codeName\": \"histogram\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.histogram","id":"9652e52e-55c0-47ee-a90c-a1a700f6b989","savedPayload":{"position":{"x":26553.33999582903,"y":19304.89648577576},"targetPosition":{"x":27236.68803000076,"y":20291.25082743166},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6157c979-57a2-473c-b19f-f79bb2f12c09","savedPayload":{"position":{"x":27236.68803000076,"y":20291.25082743166},"targetPosition":{"x":27920.036064172495,"y":21277.60516908756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"7985ad16-8c8a-460f-9445-711df5e062a7","savedPayload":{"position":{"x":27920.036064172495,"y":21277.60516908756},"targetPosition":{"x":28134.293878936845,"y":20096.94729657655},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\n/* SMA Calculation */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last100.push(candle.close) // Add the current close value to the last 100 array.\n\nif (variable.last100.length > 100) {\n variable.last100.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 100 elements.\n}\n\nvariable.sma12 = calculateSMA(12)\nvariable.sma26 = calculateSMA(26)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last100.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last100.length - periods; i < variable.last100.length; i++) { // Iterate through the last periods\n sum = sum + variable.last100[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last100.length=\" + variable.last100.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last100[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}\n\n/* EMA12 and EMA26 Calculation */\n\nvariable.ema12 = calculateEMA(12, variable.sma12, variable.previousEMA12)\nvariable.previousEMA12 = variable.ema12 \n \nvariable.ema26 = calculateEMA(26, variable.sma26, variable.previousEMA26)\nvariable.previousEMA26 = variable.ema26 \n\nfunction calculateEMA(periods, currentSMA, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSMA * k + previousEMA * (1-k)\n}\n\n\n/* MACD Line Calculation */\n\nmacdLine = variable.ema12 - variable.ema26\n\nvariable.line = macdLine.toFixed(2)\n\n/* Signal Line Calculation (EMA9 of MACD Line) */\n\nmacdSignal = calculateSignalEMA(9, variable.line, variable.previousSignal)\nvariable.signal = macdSignal.toFixed(2)\nvariable.previousSignal = variable.signal \n \nfunction calculateSignalEMA(periods, currentSignal, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSignal * k + variable.previousSignal * (1-k)\n}\n\n/* Histogram Calculation */\n\nhistogram = variable.line - variable.signal\nvariable.histogram = histogram.toFixed(2)","id":"e1e871f6-1e87-423f-8214-ece80c4295eb","savedPayload":{"position":{"x":27690.105292695127,"y":21368.330313571772},"targetPosition":{"x":26490.16386782897,"y":21368.330313571772},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c3390f8c-4865-42b1-aa00-eaa49d293121","savedPayload":{"position":{"x":26490.16386782897,"y":21368.330313571772},"targetPosition":{"x":26945.212209830253,"y":20258.019352826224},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\n/* SMA Calculation */\n\n/* \nAlthough we don't really need to set an array to access a number of candles,\nwe will do so in order to reuse the code built for the SMA indicator, and also\nso that the same code may be used for other MACD products that may require\naccessing more candles than are available in the span of two days.\n*/\n\nvariable.last100 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")\n\n/* EMA Calculation */\n\nvariable.previousEMA12 = 0\nvariable.previousEMA26 = 0\n\n/* MACD Line calculation */\n\nvariable.previousSignal = 0","id":"a9600d4d-ddc7-4639-a8bd-d5f6840a1f04","savedPayload":{"position":{"x":25477.145555619627,"y":18359.54154383448},"targetPosition":{"x":26211.17888272494,"y":19308.78044833035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f8ff5320-c74f-4f6e-8b15-f7284242f24e","savedPayload":{"position":{"x":26211.17888272494,"y":19308.78044833035},"targetPosition":{"x":26945.212209830253,"y":20258.019352826224},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c69aae16-d38f-4d9a-8174-4e664a22e579","savedPayload":{"position":{"x":26945.212209830253,"y":20258.019352826224},"targetPosition":{"x":28134.293878936845,"y":20096.94729657655},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6c0c9bf0-90af-4497-b4fb-49b5872b1119","savedPayload":{"position":{"x":28134.293878936845,"y":20096.94729657655},"targetPosition":{"x":27613.65880679387,"y":19015.83742946567},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"MACD 031016","code":"{\n \"codeName\": \"MACD031016\",\n \"singularVariableName\": \"macd031016\",\n \"pluralVariableName\": \"macd031016s\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-031016/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"19b1d791-d2ea-4f25-a6e1-fe0a64450745","savedPayload":{"position":{"x":28282.105403757483,"y":20258.019352826224},"targetPosition":{"x":27093.02373465089,"y":20096.94729657655},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-031016/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-031016/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"ec2611a6-699b-45a5-beec-679dd4b7734a","savedPayload":{"position":{"x":27307.281549415242,"y":21277.60516908756},"targetPosition":{"x":27093.02373465089,"y":20096.94729657655},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"7e2e4448-6bc6-4c1c-9a7f-54185fea3831","savedPayload":{"position":{"x":28334.850436703222,"y":21355.766628122463},"targetPosition":{"x":27185.605557063584,"y":21010.66428137314},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"399e67a5-1b3c-4f13-b1a8-c8333b013471","savedPayload":{"position":{"x":27185.605557063584,"y":21010.66428137314},"targetPosition":{"x":26036.360677423945,"y":20665.56193462382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"0e12c2f0-25f8-48c6-9920-4291c0154f92","savedPayload":{"position":{"x":27804.916740337372,"y":21722.224991850766},"targetPosition":{"x":26604.975315471216,"y":21722.224991850766},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1588b3ba-04e6-498f-baeb-857c55bf0c2f","savedPayload":{"position":{"x":26604.975315471216,"y":21722.224991850766},"targetPosition":{"x":26036.360677423945,"y":20665.56193462382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Line","code":"{\n \"codeName\": \"line\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.line","id":"5d5b5bb6-fcd8-4834-9da9-858c8fc756d6","savedPayload":{"position":{"x":26891.539755540776,"y":21817.61681426346},"targetPosition":{"x":25691.258330674624,"y":21814.80681426346},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a1c75f48-5568-46da-a828-6d26a69b5021","savedPayload":{"position":{"x":25691.258330674624,"y":21814.80681426346},"targetPosition":{"x":26036.360677423945,"y":20665.56193462382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Signal","code":"{\n \"codeName\": \"signal\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.signal","id":"a42bb9fd-fb33-4c25-b3d3-7ce7c8839e08","savedPayload":{"position":{"x":23923.034562970053,"y":21802.791210718362},"targetPosition":{"x":24979.697620197,"y":21234.17657267109},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7d0b5eee-4b62-4e35-a421-e3825b0cdb2c","savedPayload":{"position":{"x":24979.697620197,"y":21234.17657267109},"targetPosition":{"x":26036.360677423945,"y":20665.56193462382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA3","code":"{\n \"codeName\": \"ema3\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema3","id":"c725639d-6fa1-480f-b962-90be561f8464","savedPayload":{"position":{"x":23737.87091814467,"y":19975.35724112517},"targetPosition":{"x":24887.115797784307,"y":20320.459587874495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4406003b-bf04-4d4e-b9e5-fb444be155ee","savedPayload":{"position":{"x":24887.115797784307,"y":20320.459587874495},"targetPosition":{"x":26036.360677423945,"y":20665.56193462382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA10","code":"{\n \"codeName\": \"ema10\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema10","id":"fbfc968a-1b7e-48f6-935b-b40fe63bdc85","savedPayload":{"position":{"x":24899.13140132941,"y":18552.235820169928},"targetPosition":{"x":25467.746039376678,"y":19608.898877396874},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d5cc4f53-cc80-457a-842f-a2259450f02f","savedPayload":{"position":{"x":25467.746039376678,"y":19608.898877396874},"targetPosition":{"x":26036.360677423945,"y":20665.56193462382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Histogram","code":"{\n \"codeName\": \"histogram\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.histogram","id":"9724655b-b624-425d-b21c-87c69ede1d0a","savedPayload":{"position":{"x":26726.565370922595,"y":18367.072175344543},"targetPosition":{"x":26381.46302417327,"y":19516.31705498418},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d0f070bb-08a6-4c05-8e49-49f7b6a3aa15","savedPayload":{"position":{"x":26381.46302417327,"y":19516.31705498418},"targetPosition":{"x":26036.360677423945,"y":20665.56193462382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b5df9321-87e6-4c0b-892b-0af0e9b47d8d","savedPayload":{"position":{"x":26036.360677423945,"y":20665.56193462382},"targetPosition":{"x":27093.02373465089,"y":20096.94729657655},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\n/* SMA Calculation */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last100.push(candle.close) // Add the current close value to the last 100 array.\n\nif (variable.last100.length > 100) {\n variable.last100.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 100 elements.\n}\n\nvariable.sma3 = calculateSMA(3)\nvariable.sma10 = calculateSMA(10)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last100.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last100.length - periods; i < variable.last100.length; i++) { // Iterate through the last periods\n sum = sum + variable.last100[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last100.length=\" + variable.last100.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last100[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}\n\n/* EMA3 and EMA10 Calculation */\n\nvariable.ema3 = calculateEMA(3, variable.sma3, variable.previousEMA3)\nvariable.previousEMA3 = variable.ema3 \n \nvariable.ema10 = calculateEMA(10, variable.sma10, variable.previousEMA10)\nvariable.previousEMA10 = variable.ema10 \n\nfunction calculateEMA(periods, currentSMA, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSMA * k + previousEMA * (1-k)\n}\n\n\n/* MACD Line Calculation */\n\nmacdLine = variable.ema3 - variable.ema10\n\nvariable.line = macdLine.toFixed(2)\n\n/* Signal Line Calculation (EMA16 of MACD Line) */\n\nmacdSignal = calculateSignalEMA(16, variable.line, variable.previousSignal)\nvariable.signal = macdSignal.toFixed(2)\nvariable.previousSignal = variable.signal \n \nfunction calculateSignalEMA(periods, currentSignal, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSignal * k + variable.previousSignal * (1-k)\n}\n\n/* Histogram Calculation */\n\nhistogram = variable.line - variable.signal\nvariable.histogram = histogram.toFixed(2)","id":"6e3e0c8a-7600-4cf4-aa00-a29c8e94b42a","savedPayload":{"position":{"x":26273.85959569935,"y":19604.209037974808},"targetPosition":{"x":25073.918170833193,"y":19604.209037974808},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ba2052e5-b0b2-4cae-b4a8-3212eb5cbe62","savedPayload":{"position":{"x":25073.918170833193,"y":19604.209037974808},"targetPosition":{"x":26225.712235863914,"y":19267.712596879468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\n/* SMA Calculation */\n\n/* \nAlthough we don't really need to set an array to access a number of candles,\nwe will do so in order to reuse the code built for the SMA indicator, and also\nso that the same code may be used for other MACD products that may require\naccessing more candles than are available in the span of two days.\n*/\n\nvariable.last100 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")\n\n/* EMA Calculation */\n\nvariable.previousEMA3 = 0\nvariable.previousEMA10 = 0\n\n/* MACD Line calculation */\n\nvariable.previousSignal = 0","id":"e009ff8c-ac89-4cff-b3c4-27777ff2173c","savedPayload":{"position":{"x":26794.6773683514,"y":16936.250315294626},"targetPosition":{"x":26510.194802107657,"y":18101.981456087047},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fba3e3c4-5cf5-4925-9e02-36981e662f0d","savedPayload":{"position":{"x":26510.194802107657,"y":18101.981456087047},"targetPosition":{"x":26225.712235863914,"y":19267.712596879468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6659182a-7112-4c25-8fac-69f8745f9d2f","savedPayload":{"position":{"x":26225.712235863914,"y":19267.712596879468},"targetPosition":{"x":27093.02373465089,"y":20096.94729657655},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d1482dc0-e9f1-47ba-b75e-961fa914a53a","savedPayload":{"position":{"x":27093.02373465089,"y":20096.94729657655},"targetPosition":{"x":27613.65880679387,"y":19015.83742946567},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"MACD 081709","code":"{\n \"codeName\": \"MACD081709\",\n \"singularVariableName\": \"macd081709\",\n \"pluralVariableName\": \"macd081709s\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-081709/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"c390cbd3-0ba0-4507-8614-42c88f4ce7e6","savedPayload":{"position":{"x":27643.74384357478,"y":19282.84951601976},"targetPosition":{"x":26443.802418708623,"y":19282.84951601976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-081709/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-081709/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"29e18539-f188-4d29-9531-25cd3ee8f282","savedPayload":{"position":{"x":26443.802418708623,"y":20482.790940885916},"targetPosition":{"x":26443.802418708623,"y":19282.84951601976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"94ec28d4-c1bc-4c6d-8887-75877f9a2343","savedPayload":{"position":{"x":27740.305802841543,"y":18811.242499528886},"targetPosition":{"x":26540.364377975387,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5135ffe3-47fa-4642-8c46-0994438d7e7a","savedPayload":{"position":{"x":26540.364377975387,"y":18811.242499528886},"targetPosition":{"x":25340.42295310923,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"d3388f28-900d-452f-b83c-936c50b4344e","savedPayload":{"position":{"x":26836.725435573007,"y":20687.5464656847},"targetPosition":{"x":26088.57419434112,"y":19749.394482606793},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8056d769-ea76-4ec0-88ff-952921e48d2c","savedPayload":{"position":{"x":26088.57419434112,"y":19749.394482606793},"targetPosition":{"x":25340.42295310923,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Line","code":"{\n \"codeName\": \"line\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.line","id":"3864df8c-ee73-4f0e-be03-718954a4dbfc","savedPayload":{"position":{"x":23113.055709582888,"y":19704.703665391815},"targetPosition":{"x":24226.73933134606,"y":19257.97308246035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a69347ff-6f46-464a-834a-4d441b8ff291","savedPayload":{"position":{"x":24226.73933134606,"y":19257.97308246035},"targetPosition":{"x":25340.42295310923,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Signal","code":"{\n \"codeName\": \"signal\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.signal","id":"9db26074-5aaf-4f3e-83aa-bb1064981c64","savedPayload":{"position":{"x":23133.66402191045,"y":17868.028466547137},"targetPosition":{"x":24237.04348750984,"y":18339.63548303801},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cf022fb7-6c7e-432d-ae6a-fe0f4ed28116","savedPayload":{"position":{"x":24237.04348750984,"y":18339.63548303801},"targetPosition":{"x":25340.42295310923,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA8","code":"{\n \"codeName\": \"ema8\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema8","id":"93f62cc4-1f47-4b75-8a7c-c8dbd75b0c9f","savedPayload":{"position":{"x":24446.961787246302,"y":16583.875256002542},"targetPosition":{"x":24893.692370177767,"y":17697.558877765714},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"80e1f38f-36c5-440e-a12b-f9154efede9f","savedPayload":{"position":{"x":24893.692370177767,"y":17697.558877765714},"targetPosition":{"x":25340.42295310923,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA17","code":"{\n \"codeName\": \"ema17\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema17","id":"73b43260-1688-460b-862c-b8fb7406cc10","savedPayload":{"position":{"x":26283.63698609098,"y":16604.483568330103},"targetPosition":{"x":25812.029969600106,"y":17707.863033929494},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a2552af4-df0a-4fcf-85ae-1e35dbcb2608","savedPayload":{"position":{"x":25812.029969600106,"y":17707.863033929494},"targetPosition":{"x":25340.42295310923,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Histogram","code":"{\n \"codeName\": \"histogram\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.histogram","id":"94cb6751-f4a9-4bad-a8a9-d53d3375a77c","savedPayload":{"position":{"x":27567.790196635575,"y":17917.781333665956},"targetPosition":{"x":26454.106574872403,"y":18364.51191659742},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"52e3a47c-f282-4edc-83cb-8bce988e054b","savedPayload":{"position":{"x":26454.106574872403,"y":18364.51191659742},"targetPosition":{"x":25340.42295310923,"y":18811.242499528886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"f5a29a3d-06eb-4298-80d0-3d6e22957829","savedPayload":{"position":{"x":25340.42295310923,"y":18811.242499528886},"targetPosition":{"x":26443.802418708623,"y":19282.84951601976},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\n/* SMA Calculation */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last100.push(candle.close) // Add the current close value to the last 100 array.\n\nif (variable.last100.length > 100) {\n variable.last100.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 100 elements.\n}\n\nvariable.sma8 = calculateSMA(8)\nvariable.sma17 = calculateSMA(17)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last100.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last100.length - periods; i < variable.last100.length; i++) { // Iterate through the last periods\n sum = sum + variable.last100[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last100.length=\" + variable.last100.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last100[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}\n\n/* EMA8 and EMA17 Calculation */\n\nvariable.ema8 = calculateEMA(8, variable.sma8, variable.previousEMA8)\nvariable.previousEMA8 = variable.ema8 \n \nvariable.ema17 = calculateEMA(17, variable.sma17, variable.previousEMA17)\nvariable.previousEMA17 = variable.ema17 \n\nfunction calculateEMA(periods, currentSMA, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSMA * k + previousEMA * (1-k)\n}\n\n\n/* MACD Line Calculation */\n\nmacdLine = variable.ema8 - variable.ema17\n\nvariable.line = macdLine.toFixed(2)\n\n/* Signal Line Calculation (EMA9 of MACD Line) */\n\nmacdSignal = calculateSignalEMA(9, variable.line, variable.previousSignal)\nvariable.signal = macdSignal.toFixed(2)\nvariable.previousSignal = variable.signal \n \nfunction calculateSignalEMA(periods, currentSignal, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSignal * k + variable.previousSignal * (1-k)\n}\n\n/* Histogram Calculation */\n\nhistogram = variable.line - variable.signal\nvariable.histogram = histogram.toFixed(2)","id":"0290fa4a-851b-4e21-b35e-61e106fc8f81","savedPayload":{"position":{"x":24588.933609208514,"y":16706.32517860076},"targetPosition":{"x":25570.14897398337,"y":17397.031940576904},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d2a30807-83b5-4c99-8f27-55783b4487ce","savedPayload":{"position":{"x":25570.14897398337,"y":17397.031940576904},"targetPosition":{"x":26551.364338758227,"y":18087.738702553048},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\n/* SMA Calculation */\n\n/* \nAlthough we don't really need to set an array to access a number of candles,\nwe will do so in order to reuse the code built for the SMA indicator, and also\nso that the same code may be used for other MACD products that may require\naccessing more candles than are available in the span of two days.\n*/\n\nvariable.last100 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")\n\n/* EMA Calculation */\n\nvariable.previousEMA8 = 0\nvariable.previousEMA17 = 0\n\n/* MACD Line calculation */\n\nvariable.previousSignal = 0","id":"b8dca759-eb3d-4b8b-8a20-99679727f272","savedPayload":{"position":{"x":28728.918908407148,"y":17078.93059957191},"targetPosition":{"x":27640.141623582687,"y":17583.33465106248},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a16923a7-9b13-4526-818d-9278fdc509a0","savedPayload":{"position":{"x":27640.141623582687,"y":17583.33465106248},"targetPosition":{"x":26551.364338758227,"y":18087.738702553048},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8136f190-cf6a-4ca3-8620-7153ad82a295","savedPayload":{"position":{"x":26551.364338758227,"y":18087.738702553048},"targetPosition":{"x":26443.802418708623,"y":19282.84951601976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"683e6b29-9f82-4a8e-93eb-20ac0e81a750","savedPayload":{"position":{"x":26443.802418708623,"y":19282.84951601976},"targetPosition":{"x":27613.65880679387,"y":19015.83742946567},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Product Definition","name":"MACD 245209","code":"{\n \"codeName\": \"MACD245209\",\n \"singularVariableName\": \"macd245209\",\n \"pluralVariableName\": \"macd245209s\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-245209/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"89254c6c-9be2-4037-b063-467edc660d10","savedPayload":{"position":{"x":26253.8776166457,"y":19391.113192918616},"targetPosition":{"x":26675.50682371596,"y":18267.686188233787},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-245209/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/MACD/@Exchange/Output/MACD-245209/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"8a9dde13-e4b8-40f3-80bc-ea51050b2f08","savedPayload":{"position":{"x":25476.773660018967,"y":18213.85101977661},"targetPosition":{"x":26675.50682371596,"y":18267.686188233787},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.begin","id":"adee537e-2c4d-4454-9006-05171c09313a","savedPayload":{"position":{"x":25171.916283445207,"y":19198.264048819452},"targetPosition":{"x":25764.097237812497,"y":18154.6256342359},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"38a52afc-8a7c-49c9-8068-dbe438ac64d0","savedPayload":{"position":{"x":25764.097237812497,"y":18154.6256342359},"targetPosition":{"x":26356.278192179787,"y":17110.98721965235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"83439522-7972-454e-af41-d516ee51b26f","savedPayload":{"position":{"x":24042.880255016913,"y":17749.4444827247},"targetPosition":{"x":25199.57922359835,"y":17430.215851188525},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"03c98b2f-4e9c-4800-9d9b-5c6ef3f94665","savedPayload":{"position":{"x":25199.57922359835,"y":17430.215851188525},"targetPosition":{"x":26356.278192179787,"y":17110.98721965235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Line","code":"{\n \"codeName\": \"line\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.line","id":"6aa9ee37-3044-444c-bc27-e09dd444f182","savedPayload":{"position":{"x":24269.001363012685,"y":15926.62531091777},"targetPosition":{"x":25312.639777596236,"y":16518.80626528506},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c9237bcd-4d76-4460-bcbb-8474843efc03","savedPayload":{"position":{"x":25312.639777596236,"y":16518.80626528506},"targetPosition":{"x":26356.278192179787,"y":17110.98721965235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Signal","code":"{\n \"codeName\": \"signal\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.signal","id":"35ac625e-5516-4a15-b92d-f3a5dc4d8ec0","savedPayload":{"position":{"x":25717.820929107438,"y":14797.589282489473},"targetPosition":{"x":26037.049560643612,"y":15954.288251070911},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8dd22ed9-76d1-4108-8a7f-ed4c03d7eede","savedPayload":{"position":{"x":26037.049560643612,"y":15954.288251070911},"targetPosition":{"x":26356.278192179787,"y":17110.98721965235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA24","code":"{\n \"codeName\": \"ema24\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema24","id":"e56fbdd1-4b1c-484b-bcbb-6b9ec6414080","savedPayload":{"position":{"x":27540.640100914366,"y":15023.710390485248},"targetPosition":{"x":26948.459146547077,"y":16067.3488050688},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"57f32362-cb33-471d-a855-2c3cddb88bf9","savedPayload":{"position":{"x":26948.459146547077,"y":16067.3488050688},"targetPosition":{"x":26356.278192179787,"y":17110.98721965235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"EMA52","code":"{\n \"codeName\": \"ema52\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.ema52","id":"619feddc-3ff2-4a97-aa8b-a924b80bd099","savedPayload":{"position":{"x":28669.67612934266,"y":16472.52995658},"targetPosition":{"x":27512.977160761224,"y":16791.758588116176},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"93f3c29f-178d-44fb-8f64-21e617d2387e","savedPayload":{"position":{"x":27512.977160761224,"y":16791.758588116176},"targetPosition":{"x":26356.278192179787,"y":17110.98721965235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Histogram","code":"{\n \"codeName\": \"histogram\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.histogram","id":"615f9b7a-676f-444b-8fc8-8355c8c8aef6","savedPayload":{"position":{"x":28443.55502134689,"y":18295.34912838693},"targetPosition":{"x":27399.916606763338,"y":17703.16817401964},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"650fc7dc-1ea8-426d-a720-89efdec4292e","savedPayload":{"position":{"x":27399.916606763338,"y":17703.16817401964},"targetPosition":{"x":26356.278192179787,"y":17110.98721965235},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"371d2f8e-8868-4933-baee-9827c0496fc1","savedPayload":{"position":{"x":26356.278192179787,"y":17110.98721965235},"targetPosition":{"x":26675.50682371596,"y":18267.686188233787},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\n/* SMA Calculation */\n\nlet candle = record.current // Our main dependencies are candles \nvariable.last100.push(candle.close) // Add the current close value to the last 100 array.\n\nif (variable.last100.length > 100) {\n variable.last100.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 100 elements.\n}\n\nvariable.sma24 = calculateSMA(24)\nvariable.sma52 = calculateSMA(52)\n\nfunction calculateSMA(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last100.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sum = 0 // Initialize sum variable. \n for (let i = variable.last100.length - periods; i < variable.last100.length; i++) { // Iterate through the last periods\n sum = sum + variable.last100[i]\n /*\n if (index > 18 && index < 22) { // You can debug what is going on by console logging whatever you need. \n console.log(\"index=\" + index + \" i=\" + i + \" variable.last100.length=\" + variable.last100.length + \" periods=\" + periods + \" sum=\" + sum + \" sum/periods=\" + (sum / periods) + \" currentValue=\" + (variable.last100[i]))\n }\n */\n }\n let sma = sum / periods\n return sma\n}\n\n/* EMA24 and EMA52 Calculation */\n\nvariable.ema24 = calculateEMA(24, variable.sma24, variable.previousEMA24)\nvariable.previousEMA24 = variable.ema24 \n \nvariable.ema52 = calculateEMA(52, variable.sma52, variable.previousEMA52)\nvariable.previousEMA52 = variable.ema52 \n\nfunction calculateEMA(periods, currentSMA, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSMA * k + previousEMA * (1-k)\n}\n\n\n/* MACD Line Calculation */\n\nmacdLine = variable.ema24 - variable.ema52\n\nvariable.line = macdLine.toFixed(2)\n\n/* Signal Line Calculation (EMA9 of MACD Line) */\n\nmacdSignal = calculateSignalEMA(9, variable.line, variable.previousSignal)\nvariable.signal = macdSignal.toFixed(2)\nvariable.previousSignal = variable.signal \n \nfunction calculateSignalEMA(periods, currentSignal, previousEMA) {\n let k = 2 / (periods + 1)\n return currentSignal * k + variable.previousSignal * (1-k)\n}\n\n/* Histogram Calculation */\n\nhistogram = variable.line - variable.signal\nvariable.histogram = histogram.toFixed(2)","id":"4f4e66a9-4791-494a-ada7-72f3d5db7415","savedPayload":{"position":{"x":27533.422879546048,"y":15211.054708497035},"targetPosition":{"x":27605.184361243766,"y":16408.84839392636},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d8e07801-1e77-44a5-a232-fd672264f252","savedPayload":{"position":{"x":27605.184361243766,"y":16408.84839392636},"targetPosition":{"x":27676.945842941484,"y":17606.642079355686},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\n/* SMA Calculation */\n\n/* \nAlthough we don't really need to set an array to access a number of candles,\nwe will do so in order to reuse the code built for the SMA indicator, and also\nso that the same code may be used for other MACD products that may require\naccessing more candles than are available in the span of two days.\n*/\n\nvariable.last100 = []\n//console.log(timeFrameLabel + \" - Initialization of SMA - *************\")\n\n/* EMA Calculation */\n\nvariable.previousEMA24 = 0\nvariable.previousEMA52 = 0\n\n/* MACD Line calculation */\n\nvariable.previousSignal = 0","id":"5f25ca07-07ce-49a7-a397-5d2f3e89c0c3","savedPayload":{"position":{"x":29823.34684478797,"y":18680.141232458136},"targetPosition":{"x":28750.146343864726,"y":18143.39165590691},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"dd01ebc2-8204-4b51-83d9-92d416521553","savedPayload":{"position":{"x":28750.146343864726,"y":18143.39165590691},"targetPosition":{"x":27676.945842941484,"y":17606.642079355686},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e1a8b841-84d8-4ddd-822b-d81fe2b9a313","savedPayload":{"position":{"x":27676.945842941484,"y":17606.642079355686},"targetPosition":{"x":26675.50682371596,"y":18267.686188233787},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"47984765-8180-45cf-a9eb-f0a8b22d165d","savedPayload":{"position":{"x":26675.50682371596,"y":18267.686188233787},"targetPosition":{"x":27613.65880679387,"y":19015.83742946567},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"7724b857-30aa-4d3c-9ab7-e70895229f2a","savedPayload":{"position":{"x":27613.65880679387,"y":19015.83742946567},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Indicator Bot","name":"RSI","code":"{\n\"codeName\": \"RSI\",\n\"repo\": \"RSI-Indicator-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates RSI.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"41eb05b7-0222-49b7-9b62-f14f43302f8f","savedPayload":{"position":{"x":29158.219782656666,"y":18368.96660141731},"targetPosition":{"x":27932.97426474749,"y":18368.96660141731},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"5ec62760-e144-4496-98ab-0415a4b315ec"}}}],"id":"35d2683b-470d-44e0-97bb-6075e6b5cc92","savedPayload":{"position":{"x":27932.97426474749,"y":18368.96660141731},"targetPosition":{"x":27613.65880679387,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Ending: Olivia","code":"{\"mainUtility\": \"Market Ending Point\"}","id":"ea692963-60c5-445b-921c-f2be614186d7","savedPayload":{"position":{"x":30028.68064564513,"y":19241.352656188723},"targetPosition":{"x":28805.360319518903,"y":19241.352656188723},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f71d561-5872-4539-8408-f481be275559"}}},{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"4211e16a-a54f-4049-941e-fd3be8244852","savedPayload":{"position":{"x":28185.019479280967,"y":20315.814509490796},"targetPosition":{"x":28805.360319518903,"y":19241.352656188723},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"bbff3247-937a-429e-9959-c896b8207f26"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"9d9b2836-9eab-437b-9426-2cef828cd0e3","savedPayload":{"position":{"x":28192.279523197187,"y":18179.465567814725},"targetPosition":{"x":28805.360319518903,"y":19241.352656188723},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}}}],"id":"1a42abe3-eaab-444f-8687-c3930dc28a06","savedPayload":{"position":{"x":28805.360319518903,"y":19241.352656188723},"targetPosition":{"x":27613.65880679387,"y":19560.668114142343},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"bbff3247-937a-429e-9959-c896b8207f26","savedPayload":{"position":{"x":26615.542026211628,"y":20285.842403151135},"targetPosition":{"x":27613.65880679387,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"b2babf3e-22f6-4064-b4e5-840eec9ff707","savedPayload":{"position":{"x":26598.162320956595,"y":18822.86673014157},"targetPosition":{"x":27613.65880679387,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ae4d9310-3446-41b3-88b9-90978defd305","savedPayload":{"position":{"x":27994.905492234662,"y":18387.311466758576},"targetPosition":{"x":27613.65880679387,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"96f31416-5090-428a-be83-985a0b3ab275","savedPayload":{"position":{"x":27613.65880679387,"y":19560.668114142343},"targetPosition":{"x":26741.272752022454,"y":18688.28205937093},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Process Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"description\": \"Calculates RSI\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Daily\",\n \"startDate\": {\n \"takeItFromStatusDependency\": 0\n },\n \"endDate\": {\n \"takeItFromStatusDependency\": 1\n }\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"7539f591-7ca7-4fd5-9142-a6b470f8ef8e","savedPayload":{"position":{"x":28327.931485788635,"y":19560.668114142343},"targetPosition":{"x":27102.62688753393,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"f1556d09-f134-4678-83fb-04002d1dcd34"}}}],"id":"f5a20865-68a4-4e3f-8547-afc741d7fb85","savedPayload":{"position":{"x":27102.62688753393,"y":19560.668114142343},"targetPosition":{"x":25868.88669725104,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","code":"{\"mainUtility\": \"Self Reference\"}","id":"632dbd10-6cf7-453a-8e5c-4a74d33fcaf4","savedPayload":{"position":{"x":27493.21899048338,"y":20734.02476152611},"targetPosition":{"x":26250.133382691834,"y":20734.02476152611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"2d1d4e5a-49c3-4d9a-958a-195d3f0949c2"}}},{"type":"Status Dependency","name":"Ending: Olivia","code":"{\"mainUtility\": \"Market Ending Point\"}","id":"5a1a8917-5d85-4f33-b552-a314ee6c54db","savedPayload":{"position":{"x":26250.133382691834,"y":21970.18336861625},"targetPosition":{"x":26250.133382691834,"y":20734.02476152611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}}},{"type":"Status Dependency","name":"Starting: Charly Historic Trades","code":"{\"mainUtility\": \"Market Starting Point\"}","id":"9ea493a4-33b1-445e-975d-b202994079c8","savedPayload":{"position":{"x":25034.27606372321,"y":20734.02476152611},"targetPosition":{"x":26250.133382691834,"y":20734.02476152611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"86af23c8-eb30-499a-9d88-39e0e9df06e5","savedPayload":{"position":{"x":26250.133382691834,"y":19490.093365497043},"targetPosition":{"x":26250.133382691834,"y":20734.02476152611},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}}}],"id":"6e2f9722-24f9-40dc-9e38-f4dcbbbeb38a","savedPayload":{"position":{"x":26250.133382691834,"y":20734.02476152611},"targetPosition":{"x":25868.88669725104,"y":19560.668114142343},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"2d1d4e5a-49c3-4d9a-958a-195d3f0949c2","savedPayload":{"position":{"x":24870.7699166688,"y":20285.842403151135},"targetPosition":{"x":25868.88669725104,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"525140eb-d037-4b8d-9eef-f01574ca49ff","savedPayload":{"position":{"x":24873.29115053651,"y":18837.325608761486},"targetPosition":{"x":25868.88669725104,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"9162d141-5486-4a0d-a3e4-71c74060beb8","savedPayload":{"position":{"x":25549.57123929742,"y":18368.96660141731},"targetPosition":{"x":25868.88669725104,"y":19560.668114142343},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f1177bcd-22dc-425b-8f30-878844aa82b8","savedPayload":{"position":{"x":25868.88669725104,"y":19560.668114142343},"targetPosition":{"x":26741.272752022454,"y":18688.28205937093},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"products":[{"type":"Product Definition","name":"RSI14","code":"{\n \"codeName\": \"RSI-14\",\n \"singularVariableName\": \"rsi14\",\n \"pluralVariableName\": \"rsi14s\"\n}","datasets":[{"type":"Dataset Definition","name":"Multi-Period-Market","code":"{\n \"codeName\": \"Multi-Period-Market\",\n \"type\": \"Market Files\",\n \"validPeriods\": [ \"24-hs\", \"12-hs\", \"08-hs\", \"06-hs\", \"04-hs\", \"03-hs\", \"02-hs\", \"01-hs\" ],\n \"filePath\": \"Sparta/RSI/@Exchange/Output/RSI-14/Multi-Period-Market/@Period\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\"\n}","id":"5ec62760-e144-4496-98ab-0415a4b315ec","savedPayload":{"position":{"x":25308.780371715748,"y":18915.16656329497},"targetPosition":{"x":25868.88669725104,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Dataset Definition","name":"Multi-Period-Daily","code":"{\n \"codeName\": \"Multi-Period-Daily\",\n \"type\": \"Daily Files\",\n \"validPeriods\": [ \"45-min\", \"40-min\", \"30-min\", \"20-min\", \"15-min\", \"10-min\", \"05-min\", \"04-min\", \"03-min\", \"02-min\", \"01-min\" ],\n \"filePath\": \"Sparta/RSI/@Exchange/Output/RSI-14/Multi-Period-Daily/@Period/@Year/@Month/@Day\",\n \"fileName\": \"@BaseAsset_@QuotedAsset.json\",\n \"dataRange\": {\n \"filePath\": \"Sparta/RSI/@Exchange/Output/RSI-14/Multi-Period-Daily\",\n \"fileName\": \"Data.Range.@BaseAsset_@QuotedAsset.json\"\n }\n}","id":"f1556d09-f134-4678-83fb-04002d1dcd34","savedPayload":{"position":{"x":24650.335895985,"y":17622.89651793398},"targetPosition":{"x":25868.88669725104,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":"{\n \"codeName\": \"begin\",\n \"isString\": false\n}","formula":{"type":"Formula","name":"Begin","code":"record.current.begin","id":"069fdb18-b72b-4fd2-9255-061f7a4f0b7e","savedPayload":{"position":{"x":23238.785608053426,"y":16983.34417666455},"targetPosition":{"x":24457.336409319465,"y":16790.344689999012},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"309f6917-c246-4d84-89ac-abca872eb828","savedPayload":{"position":{"x":24457.336409319465,"y":16790.344689999012},"targetPosition":{"x":25675.887210585504,"y":16597.345203333476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"End","code":"{\n \"codeName\": \"end\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"record.current.end","id":"2128f049-e006-4e71-b9a5-91655bfcb290","savedPayload":{"position":{"x":25289.888237254432,"y":14160.243600801401},"targetPosition":{"x":25482.88772391997,"y":15378.794402067439},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"38a593cc-3232-4977-9597-18939c86bb5d","savedPayload":{"position":{"x":25482.88772391997,"y":15378.794402067439},"targetPosition":{"x":25675.887210585504,"y":16597.345203333476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Record Property","name":"Value","code":"{\n \"codeName\": \"value\",\n \"isString\": false\n}","formula":{"type":"Formula","code":"variable.value","id":"c5e2ca09-53a7-4655-b0dc-242b7fbd11f0","savedPayload":{"position":{"x":28112.988813117583,"y":16211.346230002402},"targetPosition":{"x":26894.438011851544,"y":16404.34571666794},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5988292a-69bf-4e2d-833b-dccb78aa32a8","savedPayload":{"position":{"x":26894.438011851544,"y":16404.34571666794},"targetPosition":{"x":25675.887210585504,"y":16597.345203333476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b12a20d9-1b6a-4525-b50c-8edf1fc3bff8","savedPayload":{"position":{"x":25675.887210585504,"y":16597.345203333476},"targetPosition":{"x":25868.88669725104,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","loop":{"type":"Procedure Loop","name":"New Procedure Loop","javascriptCode":{"type":"Javascript Code","code":"/* Loop Code */\n\n/* \nCalculation method credit: https://www.macroption.com/rsi-calculation/\n\nRSI Calculation Formula\n\n RSI = 100 – 100 / ( 1 + RS )\n RS = Relative Strength = AvgU / AvgD\n AvgU = average of all up moves in the last N price bars\n AvgD = average of all down moves in the last N price bars\n N = the period of RSI\n There are 3 different commonly used methods for the exact calculation of AvgU and AvgD (see details below)\n\nRSI Calculation Step by Step\n\n Calculate up moves and down moves (get U and D)\n Average the up moves and down moves (get AvgU and AvgD)\n Calculate Relative Strength (get RS)\n Calculate the Relative Strength Index (get RSI)\n*/\n\n/* Calculating Up Moves and Down Moves */\n\n//let candle = record.current // Our main dependencies are candles \n//let candlePrevious = record.previous\n\nif (record.previous !== undefined) {\n if (record.current.close > record.previous.close) {\n variable.last14U.push(record.current.close - record.previous.close) // Add win to last 14 array.\n } else {\n variable.last14U.push(0) // Add 0 to keep proper count of records in the last 14 array.\n }\n \n if (record.current.close < record.previous.close) {\n variable.last14D.push(record.previous.close - record.current.close) // Add win to last 14 array.\n } else {\n variable.last14D.push(0) // Add 0 to keep proper count of records in the last 14 array.\n }\n \n if (variable.last14U.length > 14) {\n variable.last14U.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 14 elements.\n }\n \n if (variable.last14D.length > 14) {\n variable.last14D.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 14 elements.\n }\n \n /* Calculating Averages of Up Moves and Down Moves */\n \n variable.avgU = calculateAVGU(14)\n variable.avgD = calculateAVGD(14)\n \n /* Calculating Relative Strength */\n \n if (variable.avgD > 0) {\n relativeStrength = variable.avgU / variable.avgD\n } else {\n relativeStrength = 0\n }\n\n /* Calculating RSI */\n\n relativeStrengthIndex = 100 - 100 / ( 1 + relativeStrength)\n\n if (relativeStrengthIndex !== undefined) {\n variable.value = relativeStrengthIndex.toFixed(2)\n } else {\n variable.value = 0\n }\n} else {\n variable.value = 0\n}\n\nfunction calculateAVGU(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last14U.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sumU = 0 // Initialize sum variable. \n for (let j = variable.last14U.length - periods; j < variable.last14U.length; j++) { // Iterate through the last periods\n sumU = sumU + variable.last14U[j]\n }\n let averageU = sumU / periods\n return averageU\n}\n\nfunction calculateAVGD(periods) { // Having a function avoids us to repeat code.\n /* We check we have enough values to make the calculation */\n if (variable.last14D.length < periods) { return 0 } // If we dont, we define the value is zero.\n\n let sumD = 0 // Initialize sum variable. \n for (let i = variable.last14D.length - periods; i < variable.last14D.length; i++) { // Iterate through the last periods\n sumD = sumD + variable.last14D[i]\n }\n let averageD = sumD / periods\n return averageD\n}","id":"a00ed092-3bc1-4288-979d-34c204713d65","savedPayload":{"position":{"x":27097.29520117411,"y":14791.690894603777},"targetPosition":{"x":27032.726228560303,"y":16023.740286834},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c2f8b23a-4e60-4181-9c19-ed243eb151cb","savedPayload":{"position":{"x":27032.726228560303,"y":16023.740286834},"targetPosition":{"x":26968.157255946495,"y":17255.789679064223},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","javascriptCode":{"type":"Javascript Code","code":"/* Initialization Code*/\n\n/* \nWe keep two separate arrays to store last 14 price increments or losses.\n*/\n\nvariable.last14U = []\nvariable.last14D = []","id":"59da2324-377e-4307-a6d1-b9cf170733ba","savedPayload":{"position":{"x":29037.560428109788,"y":18599.675812454083},"targetPosition":{"x":28002.85884202814,"y":17927.732745759153},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"aca0a380-6355-42b9-876b-6932d6d13105","savedPayload":{"position":{"x":28002.85884202814,"y":17927.732745759153},"targetPosition":{"x":26968.157255946495,"y":17255.789679064223},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5c066df5-2a29-4c74-b7bc-dc5ea1329089","savedPayload":{"position":{"x":26968.157255946495,"y":17255.789679064223},"targetPosition":{"x":25868.88669725104,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e24248b9-90df-4b26-b5cc-0bac80efa0c7","savedPayload":{"position":{"x":25868.88669725104,"y":17815.896004599515},"targetPosition":{"x":26741.272752022454,"y":18688.28205937093},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Plotter Module","name":"RSI 14","id":"72403d40-2a64-4e51-8eaf-b1a872e4474e"}}}],"id":"276e2181-0c8d-4d56-94e0-1999ec317cb5","savedPayload":{"position":{"x":26741.272752022454,"y":18688.28205937093},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"tradingBots":[],"plotters":[{"type":"Plotter","name":"SMA","code":"{\n\"codeName\": \"Plotter SMA\"\n}","modules":[{"type":"Plotter Module","name":"SMA Popular","code":"{\n\"codeName\": \"SMA-Popular\",\n\"banner\": \"Moving-Average\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"SMA 20 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma20","id":"81557791-d18b-472c-b152-612294f281cc","savedPayload":{"position":{"x":27865.054652344596,"y":19241.632692998777},"targetPosition":{"x":26738.91986604367,"y":19241.632692998777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b932c738-3b05-4b1f-a822-2ba7bae30252","savedPayload":{"position":{"x":26738.91986604367,"y":19241.632692998777},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 20 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma20","id":"dd573845-585b-4a89-8518-425173e02916","savedPayload":{"position":{"x":25244.73281654311,"y":18770.065904646966},"targetPosition":{"x":26353.759085029007,"y":18574.51465119847},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"44ba79ce-2ad1-4bb7-87db-df0aa58e6440","savedPayload":{"position":{"x":26353.759085029007,"y":18574.51465119847},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 50 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma50","id":"054b7477-497f-46f9-b33e-de27dc3d494f","savedPayload":{"position":{"x":25512.262687470982,"y":17252.828611449047},"targetPosition":{"x":26487.524020492943,"y":17815.89600459951},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"00efabf8-701d-44b1-a590-b8399149dfbb","savedPayload":{"position":{"x":26487.524020492943,"y":17815.89600459951},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 50 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma50","id":"2e18404b-0350-4720-95d1-77533ced3986","savedPayload":{"position":{"x":26692.463791485578,"y":16262.522300355384},"targetPosition":{"x":27077.62457250024,"y":17320.74284905268},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"084b4468-49d2-4ad2-a716-ebc338c34248","savedPayload":{"position":{"x":27077.62457250024,"y":17320.74284905268},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 100 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma100","id":"9e730b8b-7f20-41df-9126-9d9aae2f8ca6","savedPayload":{"position":{"x":28233.10691554423,"y":16262.522300355386},"targetPosition":{"x":27847.946134529568,"y":17320.74284905268},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bbacba1b-6b2d-48b7-a440-986a04c61858","savedPayload":{"position":{"x":27847.946134529568,"y":17320.74284905268},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 100 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma100","id":"922d462b-1e9f-4766-859d-330041428bc0","savedPayload":{"position":{"x":29413.308019558826,"y":17252.828611449047},"targetPosition":{"x":28438.046686536865,"y":17815.89600459951},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"270a8be8-ad51-4061-abe1-a4c7234eb88a","savedPayload":{"position":{"x":28438.046686536865,"y":17815.89600459951},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 200 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma200","id":"4278cffb-da8c-458e-b014-449f8487204b","savedPayload":{"position":{"x":29680.8378904867,"y":18770.06590464696},"targetPosition":{"x":28571.8116220008,"y":18574.514651198468},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cd242031-8d7a-4cee-a680-156b0be7c7c7","savedPayload":{"position":{"x":28571.8116220008,"y":18574.514651198468},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 200 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma200","id":"a3b73c57-a9b2-4a55-9232-9bacf741a933","savedPayload":{"position":{"x":29312.785627287063,"y":19241.632692998777},"targetPosition":{"x":28186.650840986138,"y":19241.632692998777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"328b048e-eb4a-4a02-ad95-a17cd170c4a7","savedPayload":{"position":{"x":28186.650840986138,"y":19241.632692998777},"targetPosition":{"x":27462.785353514904,"y":18378.963397749976},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"6755d98f-e1de-4ae9-b688-d06ab9cc543e","savedPayload":{"position":{"x":27462.785353514904,"y":18378.963397749976},"targetPosition":{"x":27462.785353514904,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"SMA 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"6368c66e-47fc-486d-8c03-e2ed303de825","savedPayload":{"position":{"x":27311.911900235937,"y":16991.508124856515},"targetPosition":{"x":27874.9792933864,"y":17966.769457878476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"b0d22693-8100-49e9-b92e-11a8a55a5480","savedPayload":{"position":{"x":27874.9792933864,"y":17966.769457878476},"targetPosition":{"x":28438.046686536865,"y":18942.030790900437},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"d9dae274-33d5-403d-a5ff-5514104df62e","savedPayload":{"position":{"x":27880.54209918475,"y":19907.657061647024},"targetPosition":{"x":28438.046686536865,"y":18942.030790900437},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 20 End","id":"44ba79ce-2ad1-4bb7-87db-df0aa58e6440"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"45823ead-7d67-409c-8160-ea36496c412e","savedPayload":{"position":{"x":27878.500872261593,"y":17972.86901141317},"targetPosition":{"x":28438.046686536865,"y":18942.030790900437},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 20 Begin","id":"b932c738-3b05-4b1f-a822-2ba7bae30252"}}}],"id":"ffe11ebb-d04f-4bf6-b6ff-43382be7c347","savedPayload":{"position":{"x":28438.046686536865,"y":18942.030790900437},"targetPosition":{"x":27462.785353514904,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 50","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"b0de67a4-9a26-40ce-aa19-f5336145f871","savedPayload":{"position":{"x":27875.574012403096,"y":21989.147963680618},"targetPosition":{"x":27213.648592921876,"y":21078.08578360637},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"f06c2d68-11d4-40e0-99df-43a758ca0123","savedPayload":{"position":{"x":27213.648592921876,"y":21078.08578360637},"targetPosition":{"x":26551.723173440656,"y":20167.02360353212},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"baf11fde-1f46-4e77-a8fe-d19a3ee8a4b2","savedPayload":{"position":{"x":25994.645962398536,"y":21131.90963679584},"targetPosition":{"x":26551.723173440656,"y":20167.02360353212},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 50 End","id":"084b4468-49d2-4ad2-a716-ebc338c34248"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"bb6ef8cd-1473-4200-9c0e-35b5eab40259","savedPayload":{"position":{"x":25988.64973605762,"y":19191.75180159225},"targetPosition":{"x":26551.723173440656,"y":20167.02360353212},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 50 Begin","id":"00efabf8-701d-44b1-a590-b8399149dfbb"}}}],"id":"c867bef2-aa91-4753-9b27-68f88d42370e","savedPayload":{"position":{"x":26551.723173440656,"y":20167.02360353212},"targetPosition":{"x":27462.785353514904,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 100","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"b03ef9cb-fef3-4a54-bf5a-a85528fa2699","savedPayload":{"position":{"x":25227.872334478216,"y":20665.297124718178},"targetPosition":{"x":25889.797753959436,"y":19754.23494464393},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"baf25122-e7de-4283-b638-cfe2766220ac","savedPayload":{"position":{"x":25889.797753959436,"y":19754.23494464393},"targetPosition":{"x":26551.723173440656,"y":18843.17276456968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"8657980b-1796-4291-bba6-87520122b302","savedPayload":{"position":{"x":25993.019203792817,"y":19810.876426390158},"targetPosition":{"x":26551.723173440656,"y":18843.17276456968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 100 Begin","id":"bbacba1b-6b2d-48b7-a440-986a04c61858"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"45403abf-a4b1-4f8a-9d18-8ee72fef1792","savedPayload":{"position":{"x":25993.79898580819,"y":17876.81972481866},"targetPosition":{"x":26551.723173440656,"y":18843.17276456968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 100 End","id":"270a8be8-ad51-4061-abe1-a4c7234eb88a"}}}],"id":"7473f9b9-7737-4ee1-8418-39b96d8365c3","savedPayload":{"position":{"x":26551.723173440656,"y":18843.17276456968},"targetPosition":{"x":27462.785353514904,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 200","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"9475aa23-1561-45f6-8129-9d7ffaf0bb22","savedPayload":{"position":{"x":24345.488367016653,"y":19372.176003353794},"targetPosition":{"x":26739.762313700543,"y":18086.085570388972},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"9007d9ad-f2b5-45fb-a7b3-b9579c74866d","savedPayload":{"position":{"x":26739.762313700543,"y":18086.085570388972},"targetPosition":{"x":27810.78014043869,"y":18434.080357312756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"0b3d001b-060a-4d72-94e7-d8e9f76ef1ae","savedPayload":{"position":{"x":27246.11402050986,"y":19412.110766342266},"targetPosition":{"x":27810.78014043869,"y":18434.080357312756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 200 Begin","id":"cd242031-8d7a-4cee-a680-156b0be7c7c7"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"178f2776-a5db-4cb9-b8c7-11ecb471bf12","savedPayload":{"position":{"x":27249.536137489802,"y":17461.97722876195},"targetPosition":{"x":27810.78014043869,"y":18434.080357312756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 200 End","id":"328b048e-eb4a-4a02-ad95-a17cd170c4a7"}}}],"id":"5dbe47bb-9414-4edd-be08-5ab50755a852","savedPayload":{"position":{"x":27810.78014043869,"y":18434.080357312756},"targetPosition":{"x":27462.785353514904,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"340dc986-ccc9-4b47-ac54-294187daa2e0","savedPayload":{"position":{"x":27462.785353514904,"y":19505.0981840509},"targetPosition":{"x":26487.524020492943,"y":18942.03079090044},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"Popular SMA","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"SMA 20","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma20","id":"4f901876-b274-426e-9755-529b6241890d","savedPayload":{"position":{"x":27764.532260072832,"y":19505.0981840509},"targetPosition":{"x":26638.397473771907,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"57d75985-febe-441b-be12-31f5eb10881b","savedPayload":{"position":{"x":26638.397473771907,"y":19505.0981840509},"targetPosition":{"x":25512.262687470982,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 50","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma50","id":"dc2d66e9-8dc1-4092-bfa8-e147c7cf7226","savedPayload":{"position":{"x":25512.262687470982,"y":21757.36775665275},"targetPosition":{"x":25512.262687470982,"y":20631.232970351826},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"74e3faab-246a-4c05-ae0a-b68f2095aa73","savedPayload":{"position":{"x":25512.262687470982,"y":20631.232970351826},"targetPosition":{"x":25512.262687470982,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 100","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma100","id":"1b045b3c-13a9-4091-a804-0040340cf1be","savedPayload":{"position":{"x":23272.331283246698,"y":19269.671906762338},"targetPosition":{"x":24392.29698535884,"y":19387.38504540662},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f46c7caf-8907-4cc4-b048-efe691a24049","savedPayload":{"position":{"x":24392.29698535884,"y":19387.38504540662},"targetPosition":{"x":25512.262687470982,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 200","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma200","id":"ef7a7e7d-dec3-4da6-9646-d087e03b7a71","savedPayload":{"position":{"x":25043.98951245394,"y":17302.04610540465},"targetPosition":{"x":25278.12609996246,"y":18403.572144727776},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8e1108c8-e8a4-4ae8-b6b1-765c4292580a","savedPayload":{"position":{"x":25278.12609996246,"y":18403.572144727776},"targetPosition":{"x":25512.262687470982,"y":19505.0981840509},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"7fc74763-c245-4b23-8121-7fe0d1e8e1db","savedPayload":{"position":{"x":25512.262687470982,"y":19505.0981840509},"targetPosition":{"x":26487.524020492943,"y":18942.03079090044},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"ddf21e96-c312-4ebe-9e2b-f56e8ea072aa","savedPayload":{"position":{"x":26487.524020492943,"y":18942.03079090044},"targetPosition":{"x":26487.524020492943,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"SMA Base 11","code":"{\n\"codeName\": \"SMA-Base11\",\n\"banner\": \"Moving-Average\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"SMA 11 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma11","id":"ab8c79b6-26ed-436e-a9ed-5581e48403a9","savedPayload":{"position":{"x":23856.827336272625,"y":17701.641962790938},"targetPosition":{"x":24884.352984246492,"y":18162.47880992115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"77d02772-853e-4e30-9efe-7bea8e829d51","savedPayload":{"position":{"x":24884.352984246492,"y":18162.47880992115},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 11 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma11","id":"e7f8c6de-4085-4886-ad93-c207274eb90c","savedPayload":{"position":{"x":24681.353888847305,"y":16736.909793082847},"targetPosition":{"x":25296.616260533832,"y":17680.112725067105},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"185ca2e7-b561-4ce4-b219-bd54496885b5","savedPayload":{"position":{"x":25296.616260533832,"y":17680.112725067105},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 22 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma22","id":"0c62dd54-916a-4187-be47-0715604a0632","savedPayload":{"position":{"x":25896.563352970566,"y":16371.098156477845},"targetPosition":{"x":25904.220992595463,"y":17497.206906764604},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"08b23cb2-cf78-48ae-bd9a-efe0e8df4578","savedPayload":{"position":{"x":25904.220992595463,"y":17497.206906764604},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 22 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma22","id":"f1d537b7-09a0-4031-8565-02ea33b66261","savedPayload":{"position":{"x":27116.635310043042,"y":16720.34966289651},"targetPosition":{"x":26514.2569711317,"y":17671.832659973938},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3871971f-7f4b-49a0-a0d7-b1d08a4db951","savedPayload":{"position":{"x":26514.2569711317,"y":17671.832659973938},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 33 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma33","id":"5cbdb800-4b2b-49e6-a8ef-678587ecd626","savedPayload":{"position":{"x":27954.205534310735,"y":17673.77942674414},"targetPosition":{"x":26933.042083265547,"y":18148.54754189775},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2734c9f2-1347-4b5b-8dbf-f7f42eb2c810","savedPayload":{"position":{"x":26933.042083265547,"y":18148.54754189775},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 33 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma33","id":"d7051dab-455d-4d02-8e0b-59139c3546d3","savedPayload":{"position":{"x":28143.351397557053,"y":18928.680234611904},"targetPosition":{"x":27027.615014888706,"y":18775.997945831634},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ce242fe6-2b52-4b89-a203-ce398b3545fd","savedPayload":{"position":{"x":27027.615014888706,"y":18775.997945831634},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 55 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma55","id":"2c8a5860-2c8c-4a04-b0aa-d353817d3dab","savedPayload":{"position":{"x":27894.084314732158,"y":19354.972801879285},"targetPosition":{"x":26767.949528431232,"y":19354.972801879285},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bdc5f0cf-2c2f-466c-963b-19d81a13b532","savedPayload":{"position":{"x":26767.949528431232,"y":19354.972801879285},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 55 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma55","id":"62dfcbf0-e773-41db-80e0-bf46d47d977f","savedPayload":{"position":{"x":27362.62236743564,"y":19701.65168408642},"targetPosition":{"x":26236.487581134716,"y":19701.65168408642},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d2e58c59-c5d7-4b42-bb66-f012da23c12a","savedPayload":{"position":{"x":26236.487581134716,"y":19701.65168408642},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 111 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma111","id":"b496251d-8005-4d21-9be0-1dec58cb58ba","savedPayload":{"position":{"x":25949.959373058908,"y":18634.94867013006},"targetPosition":{"x":26259.873419144144,"y":17552.29783031322},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"62ed1060-3233-4175-a4cc-8ad04a123105","savedPayload":{"position":{"x":26259.873419144144,"y":17552.29783031322},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 11 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma111","id":"3e765b91-17a0-4b30-bcfb-d7491afa9635","savedPayload":{"position":{"x":26191.97189732051,"y":19366.547317653432},"targetPosition":{"x":25065.837111019584,"y":19366.547317653432},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bdee0fe7-c635-483d-b547-6271410cc35a","savedPayload":{"position":{"x":25065.837111019584,"y":19366.547317653432},"targetPosition":{"x":25911.87863222036,"y":18623.315657051364},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"853ae08f-85ed-48e9-82d9-7f00184ff7e7","savedPayload":{"position":{"x":25911.87863222036,"y":18623.315657051364},"targetPosition":{"x":24798.321841041557,"y":18791.157337621476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"SMA 11","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"a36a0be4-c3be-4e17-8459-ce24d08c60d4","savedPayload":{"position":{"x":27155.76941546687,"y":17906.093092257695},"targetPosition":{"x":26389.8032347695,"y":18731.60830528827},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"bc76dbe0-28fe-4811-b396-583cc810ad8c","savedPayload":{"position":{"x":26389.8032347695,"y":18731.60830528827},"targetPosition":{"x":25623.83705407213,"y":19557.123518318847},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"007b0aa7-d129-4b66-97b1-a223b61465a9","savedPayload":{"position":{"x":25063.291215671954,"y":20528.017390399244},"targetPosition":{"x":25623.83705407213,"y":19557.123518318847},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 11 Begin","id":"77d02772-853e-4e30-9efe-7bea8e829d51"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"8bf6c449-35d6-44c0-ac25-0da059e0c7d0","savedPayload":{"position":{"x":25063.616853989955,"y":18586.79366835012},"targetPosition":{"x":25623.83705407213,"y":19557.123518318847},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 11 End","id":"185ca2e7-b561-4ce4-b219-bd54496885b5"}}}],"id":"82eff706-25ff-45ba-b066-0f20b64e26e1","savedPayload":{"position":{"x":25623.83705407213,"y":19557.123518318847},"targetPosition":{"x":24798.321841041557,"y":18791.157337621476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 22","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"48b5e69c-d187-40f8-aee9-89f02ac7b442","savedPayload":{"position":{"x":26185.77711393502,"y":20892.55345694436},"targetPosition":{"x":25210.515780913058,"y":20329.4860637939},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"13b6ae03-77c1-473a-9cd9-eee42cab5005","savedPayload":{"position":{"x":25210.515780913058,"y":20329.4860637939},"targetPosition":{"x":24235.254447891097,"y":19766.418670643438},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"5101118b-d0b2-46b3-b752-c1e8f3e09842","savedPayload":{"position":{"x":23672.500591512,"y":20741.136942047357},"targetPosition":{"x":24235.254447891097,"y":19766.418670643438},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 22 End","id":"3871971f-7f4b-49a0-a0d7-b1d08a4db951"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"5d1290b9-98c2-4925-9da4-fcdf50d13c5b","savedPayload":{"position":{"x":23654.47572148055,"y":18760.480408545227},"targetPosition":{"x":24235.254447891097,"y":19766.418670643438},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 22 Begin","id":"08b23cb2-cf78-48ae-bd9a-efe0e8df4578"}}}],"id":"b7186c69-0a02-4820-bb58-1e2cee5790b3","savedPayload":{"position":{"x":24235.254447891097,"y":19766.418670643438},"targetPosition":{"x":24798.321841041557,"y":18791.157337621476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 33","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"a8183072-9dc8-4f7c-b72b-742477e4c8fd","savedPayload":{"position":{"x":26120.135188095526,"y":19425.53165082798},"targetPosition":{"x":24994.0004017946,"y":19425.53165082798},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"98a87528-cb66-4e15-a3b0-aadb8d25fcec","savedPayload":{"position":{"x":24994.0004017946,"y":19425.53165082798},"targetPosition":{"x":23867.865615493676,"y":19425.53165082798},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"242e470b-2971-40b2-9f06-a95bf95e23fc","savedPayload":{"position":{"x":23310.631384951506,"y":20390.68964984357},"targetPosition":{"x":23867.865615493676,"y":19425.53165082798},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 33 End","id":"ce242fe6-2b52-4b89-a203-ce398b3545fd"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"106e4253-deab-440c-828a-ea55aea78c97","savedPayload":{"position":{"x":23301.16929225969,"y":18443.984826524236},"targetPosition":{"x":23867.865615493676,"y":19425.53165082798},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 33 Begin","id":"2734c9f2-1347-4b5b-8dbf-f7f42eb2c810"}}}],"id":"53f19bad-dcb0-476f-8b32-0d332d1ad130","savedPayload":{"position":{"x":23867.865615493676,"y":19425.53165082798},"targetPosition":{"x":24798.321841041557,"y":18791.157337621476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 55","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"ea853676-6bdc-4723-a515-1a4c683fa1fa","savedPayload":{"position":{"x":24516.418794557216,"y":19535.327059714113},"targetPosition":{"x":23390.28400825629,"y":19535.327059714113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"1fd3b244-b5ae-4a96-add5-50b228eac931","savedPayload":{"position":{"x":23390.28400825629,"y":19535.327059714113},"targetPosition":{"x":23722.218063602606,"y":18459.22328227516},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"c0102a15-8594-4582-9714-75e555e3f2ac","savedPayload":{"position":{"x":23152.49864279122,"y":19446.006265179196},"targetPosition":{"x":23722.218063602606,"y":18459.22328227516},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 55 End","id":"d2e58c59-c5d7-4b42-bb66-f012da23c12a"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"aa781190-aa71-4407-bc72-db67ffd67c7e","savedPayload":{"position":{"x":23156.548443198608,"y":17479.454759437238},"targetPosition":{"x":23722.218063602606,"y":18459.22328227516},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 55 Begin","id":"bdc5f0cf-2c2f-466c-963b-19d81a13b532"}}}],"id":"8341d899-4df1-47f8-8e0d-bb87b76ed622","savedPayload":{"position":{"x":23722.218063602606,"y":18459.22328227516},"targetPosition":{"x":24798.321841041557,"y":18791.157337621476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 111","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GREEN\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"85056d46-5dd2-4dc2-9c40-55ef936aeea8","savedPayload":{"position":{"x":22290.319984634578,"y":18565.714500422513},"targetPosition":{"x":23338.60929463716,"y":18154.291264020703},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"bd5b197d-7425-461d-a2ab-5ad6446d00ee","savedPayload":{"position":{"x":23338.60929463716,"y":18154.291264020703},"targetPosition":{"x":24386.898604639744,"y":17742.868027618893},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"fbd2d02d-d58e-4d04-a5a6-9bbc6e877244","savedPayload":{"position":{"x":23822.334201242593,"y":18720.72225844757},"targetPosition":{"x":24386.898604639744,"y":17742.868027618893},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 11 End","id":"bdee0fe7-c635-483d-b547-6271410cc35a"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e79d988b-7d4a-4201-839a-a95f99dc26fe","savedPayload":{"position":{"x":23811.081637915093,"y":16745.523785391604},"targetPosition":{"x":24386.898604639744,"y":17742.868027618893},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 111 Begin","id":"62ed1060-3233-4175-a4cc-8ad04a123105"}}}],"id":"5745a180-5f94-4109-b8e5-41b2c21e9f1a","savedPayload":{"position":{"x":24386.898604639744,"y":17742.868027618893},"targetPosition":{"x":24798.321841041557,"y":18791.157337621476},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"ade8af76-e9e6-4dda-8921-c3c5cc8a0ecc","savedPayload":{"position":{"x":24798.321841041557,"y":18791.157337621476},"targetPosition":{"x":25361.38923419202,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"SMA Base 11","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"SMA 11","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma11","id":"bce6e5ba-e93b-4c0f-9013-db467727e721","savedPayload":{"position":{"x":23672.187054740632,"y":18791.157337621476},"targetPosition":{"x":24235.254447891093,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f4794965-6a35-4289-9e0e-718b931028de","savedPayload":{"position":{"x":24235.254447891093,"y":17815.896004599515},"targetPosition":{"x":24798.321841041554,"y":16840.634671577554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 22","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma22","id":"e8837d50-025b-4bbc-8199-328df7afbdda","savedPayload":{"position":{"x":22546.052268439704,"y":16840.634671577554},"targetPosition":{"x":23672.18705474063,"y":16840.634671577554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2b4712d6-5952-4bd0-bcac-7d396d95f842","savedPayload":{"position":{"x":23672.18705474063,"y":16840.634671577554},"targetPosition":{"x":24798.321841041554,"y":16840.634671577554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 33","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma33","id":"825bc681-cb31-4d26-85af-4f692538fc9d","savedPayload":{"position":{"x":23672.187054740625,"y":14890.112005533632},"targetPosition":{"x":24235.25444789109,"y":15865.373338555593},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"377ff3cb-9d7b-45aa-a2c1-3cabf3540c8e","savedPayload":{"position":{"x":24235.25444789109,"y":15865.373338555593},"targetPosition":{"x":24798.321841041554,"y":16840.634671577554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 55","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma55","id":"24c4a804-6686-43ab-a664-db08d860034c","savedPayload":{"position":{"x":25924.456627342483,"y":14890.112005533632},"targetPosition":{"x":25361.38923419202,"y":15865.373338555593},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4b0485d9-4d37-4402-939b-eb878c6750ec","savedPayload":{"position":{"x":25361.38923419202,"y":15865.373338555593},"targetPosition":{"x":24798.321841041554,"y":16840.634671577554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 111","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma111","id":"3d21e771-7edd-4460-aa23-da3c7880a629","savedPayload":{"position":{"x":27050.591413643404,"y":16840.634671577554},"targetPosition":{"x":25924.45662734248,"y":16840.634671577554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"738b01bd-a4da-4f90-a816-c021eeb81030","savedPayload":{"position":{"x":25924.45662734248,"y":16840.634671577554},"targetPosition":{"x":24798.321841041554,"y":16840.634671577554},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"fc702308-8514-4e8d-85d3-5d052aad782c","savedPayload":{"position":{"x":24798.321841041554,"y":16840.634671577554},"targetPosition":{"x":25361.38923419202,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"4a61ac35-a1ae-40f4-b3da-f81ae796ee00","savedPayload":{"position":{"x":25361.38923419202,"y":17815.896004599515},"targetPosition":{"x":26487.524020492943,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"SMA Base 7","code":"{\n\"codeName\": \"SMA-Base7\",\n\"banner\": \"Moving-Average\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"SMA 7 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma7","id":"b459dee1-24d0-4794-9c4d-d9c58972f6e6","savedPayload":{"position":{"x":25574.152951804692,"y":14906.154686082718},"targetPosition":{"x":25824.74151621307,"y":16004.054922126403},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8d6e2d59-fe23-4ba5-ad87-d9d15ad60340","savedPayload":{"position":{"x":25824.74151621307,"y":16004.054922126403},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 7 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma7","id":"3994ef79-ab0b-4552-bbde-f754a86f2e04","savedPayload":{"position":{"x":26243.64239656384,"y":14855.983388095889},"targetPosition":{"x":26159.486238592643,"y":15978.969273132989},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a95fee99-4a06-44d7-a6c4-0eea9ccec4d9","savedPayload":{"position":{"x":26159.486238592643,"y":15978.969273132989},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 14 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma14","id":"78e7fbfb-c210-44b6-8399-1567f81c73c9","savedPayload":{"position":{"x":26898.176553425074,"y":15005.376538164925},"targetPosition":{"x":26486.75331702326,"y":16053.665848167506},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d5f823d6-8b99-44b5-99dc-04724a531323","savedPayload":{"position":{"x":26486.75331702326,"y":16053.665848167506},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 14 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma14","id":"40d386cb-84a9-49b0-b28e-3d39e9e274cb","savedPayload":{"position":{"x":27479.59719017543,"y":15341.059899305146},"targetPosition":{"x":26777.46363539844,"y":16221.507528737617},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c6d631e7-8cd0-4ccd-b7b4-90b8dda615d8","savedPayload":{"position":{"x":26777.46363539844,"y":16221.507528737617},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 21 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma21","id":"6d2cd604-9a38-4ab7-b19d-9e9e3ae8ad25","savedPayload":{"position":{"x":27936.24253171721,"y":15833.206531757083},"targetPosition":{"x":27005.786306169328,"y":16467.580844963584},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4f1044fa-67dd-42dc-ba74-61e299e1f1e1","savedPayload":{"position":{"x":27005.786306169328,"y":16467.580844963584},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 21 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma21","id":"1de80488-5eeb-491c-bd5f-e5bd7569dc59","savedPayload":{"position":{"x":28227.53763549935,"y":16438.087047477457},"targetPosition":{"x":27151.433858060398,"y":16770.02110282377},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"df7e58c1-c042-443b-bfd3-69d57894d358","savedPayload":{"position":{"x":27151.433858060398,"y":16770.02110282377},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 28 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma28","id":"93b50d69-b003-4590-b098-ff59dc226e22","savedPayload":{"position":{"x":28327.599653223297,"y":17101.955158170087},"targetPosition":{"x":27201.46486692237,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7bd742dd-7c4c-4dfe-9415-e655a5e1cc88","savedPayload":{"position":{"x":27201.46486692237,"y":17101.955158170087},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 28 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma28","id":"36ddca01-d7c2-44e4-bac6-1efd92721b56","savedPayload":{"position":{"x":28227.53763549935,"y":17765.823268862718},"targetPosition":{"x":27151.433858060398,"y":17433.889213516402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7678774b-b068-4757-962b-8e126342898a","savedPayload":{"position":{"x":27151.433858060398,"y":17433.889213516402},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 35 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma35","id":"e631b84b-646b-41f7-b87b-7cb4fbf8ac76","savedPayload":{"position":{"x":27936.24253171721,"y":18370.703784583093},"targetPosition":{"x":27005.786306169328,"y":17736.32947137659},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"93613bd9-2dbb-493c-a72e-414b0398733e","savedPayload":{"position":{"x":27005.786306169328,"y":17736.32947137659},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 35 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma35","id":"89e3900f-eeaf-47cb-97f6-9093b2f39811","savedPayload":{"position":{"x":27479.59719017543,"y":18862.850417035028},"targetPosition":{"x":26777.46363539844,"y":17982.402787602558},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f620fdbe-48b5-4831-995d-b3c721738d1b","savedPayload":{"position":{"x":26777.46363539844,"y":17982.402787602558},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 70 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma70","id":"cacaceaf-b19d-4b2a-81b3-5ac6b44877b7","savedPayload":{"position":{"x":26898.176553425066,"y":19198.533778175253},"targetPosition":{"x":26486.753317023256,"y":18150.24446817267},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9e506a85-5f72-4a3f-a773-cd85d8b15f21","savedPayload":{"position":{"x":26486.753317023256,"y":18150.24446817267},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 70 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma70","id":"8658d660-52c4-41f8-b423-db569ff19941","savedPayload":{"position":{"x":26243.64239656384,"y":19347.926928244284},"targetPosition":{"x":26159.486238592643,"y":18224.941043207185},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8392e1ff-33a6-4b95-baad-3f146bd3c121","savedPayload":{"position":{"x":26159.486238592643,"y":18224.941043207185},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 140 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma140","id":"a56e674e-ec4f-4448-a8bc-bfbc3a02eea4","savedPayload":{"position":{"x":25574.152951804695,"y":19297.75563025746},"targetPosition":{"x":25824.74151621307,"y":18199.855394213773},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"39e65adb-6125-4e9a-8f15-5403b21aac40","savedPayload":{"position":{"x":25824.74151621307,"y":18199.855394213773},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 140 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma140","id":"9aeb4594-c7e0-41e1-83b3-8df31b0d44b0","savedPayload":{"position":{"x":24949.195294320525,"y":19052.47782421401},"targetPosition":{"x":25512.262687470986,"y":18077.216491192048},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7559cbf5-acba-4a60-91b4-d95f90293f22","savedPayload":{"position":{"x":25512.262687470986,"y":18077.216491192048},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 210 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma210","id":"8856ee41-b264-4468-9dac-eb808b483df3","savedPayload":{"position":{"x":24424.299654560302,"y":18633.88751956483},"targetPosition":{"x":25249.814867590874,"y":17867.921338867458},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"479df802-576f-45f1-8322-6b2e493c5ecc","savedPayload":{"position":{"x":25249.814867590874,"y":17867.921338867458},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 210 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma210","id":"f4fa8a22-fe87-4174-bf51-cb574381ca0d","savedPayload":{"position":{"x":24046.10531358329,"y":18079.178301831285},"targetPosition":{"x":25060.71769710237,"y":17590.566730000686},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"45a926f2-4bb2-4562-b40f-bd2532ff1d10","savedPayload":{"position":{"x":25060.71769710237,"y":17590.566730000686},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 280 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma280","id":"49f183b2-f8d1-4055-947d-291b7787d846","savedPayload":{"position":{"x":23848.216498263842,"y":17437.638519310312},"targetPosition":{"x":24961.773289442644,"y":17269.7968387402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d5e455bf-bb27-41fa-ba41-c8e7d0aa4d5e","savedPayload":{"position":{"x":24961.773289442644,"y":17269.7968387402},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 280 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma280","id":"012f6580-f9e9-47f6-b6f4-82598b8e8aa7","savedPayload":{"position":{"x":23848.216498263842,"y":16766.27179702986},"targetPosition":{"x":24961.773289442644,"y":16934.113477599974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"298d0ca9-1b4a-48d8-8efe-fda6b2fc2c34","savedPayload":{"position":{"x":24961.773289442644,"y":16934.113477599974},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 350 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.sma350","id":"4a3f6588-26e3-446b-ac4e-54ad3bc9ef3c","savedPayload":{"position":{"x":24046.10531358329,"y":16124.732014508889},"targetPosition":{"x":25060.71769710237,"y":16613.343586339488},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f3c05d30-da2e-449e-8471-dcc216f4e630","savedPayload":{"position":{"x":25060.71769710237,"y":16613.343586339488},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"SMA 350 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.sma350","id":"1157762f-1236-43b1-a399-06d59a992d55","savedPayload":{"position":{"x":24424.299654560302,"y":15570.022796775349},"targetPosition":{"x":25249.814867590874,"y":16335.988977472718},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d6598381-9b15-4cb3-afba-b85ed6770ce3","savedPayload":{"position":{"x":25249.814867590874,"y":16335.988977472718},"targetPosition":{"x":26075.330080621447,"y":17101.955158170087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"e561ee2d-8458-4001-8904-61cd2a3a815e","savedPayload":{"position":{"x":26075.330080621447,"y":17101.955158170087},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"SMA 7","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"38522e0c-85c4-49ae-93ac-9a04470855e0","savedPayload":{"position":{"x":27764.532260072832,"y":17252.828611449055},"targetPosition":{"x":26638.397473771907,"y":17252.828611449055},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"fcc0c4b0-28ea-4482-8da8-16a0c88c1e52","savedPayload":{"position":{"x":26638.397473771907,"y":17252.828611449055},"targetPosition":{"x":25512.262687470982,"y":17252.828611449055},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"f11dbba2-2bd3-4d0a-a182-2ef78a492d32","savedPayload":{"position":{"x":24949.617587141565,"y":18227.358511849292},"targetPosition":{"x":25512.262687470982,"y":17252.828611449055},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 7 End","id":"a95fee99-4a06-44d7-a6c4-0eea9ccec4d9"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"c50700bc-305d-4cb6-b1bf-1c03c7459d2b","savedPayload":{"position":{"x":24947.568275011894,"y":16274.749198319656},"targetPosition":{"x":25512.262687470982,"y":17252.828611449055},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 7 Begin","id":"8d6e2d59-fe23-4ba5-ad87-d9d15ad60340"}}}],"id":"62ad882d-f16d-4476-8c17-0f6e2d6e2b76","savedPayload":{"position":{"x":25512.262687470982,"y":17252.828611449055},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 14","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"14a21030-fa6b-44ef-8cb2-e4f3cb79126e","savedPayload":{"position":{"x":26899.717960364444,"y":18228.089944471012},"targetPosition":{"x":25924.456627342483,"y":17665.02255132055},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"ca2fa8f2-f6a1-4084-8606-a35495dfd596","savedPayload":{"position":{"x":25924.456627342483,"y":17665.02255132055},"targetPosition":{"x":24949.19529432052,"y":17101.95515817009},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e7d170e8-3d4f-4a55-883a-3a3394fef2af","savedPayload":{"position":{"x":24379.813378227554,"y":18088.15356575403},"targetPosition":{"x":24949.19529432052,"y":17101.95515817009},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 14 Begin","id":"d5f823d6-8b99-44b5-99dc-04724a531323"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"2383789a-4600-4b8b-a49b-51cbeb46d4f1","savedPayload":{"position":{"x":24358.221986225133,"y":16078.359362631825},"targetPosition":{"x":24949.19529432052,"y":17101.95515817009},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 14 End","id":"c6d631e7-8cd0-4ccd-b7b4-90b8dda615d8"}}}],"id":"ab179cef-53c3-46f7-a83b-af29ccd8bc90","savedPayload":{"position":{"x":24949.19529432052,"y":17101.95515817009},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 21","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"f8f0733e-0890-4d54-8d77-1b1fc8b8c92b","savedPayload":{"position":{"x":25663.13614074995,"y":18640.283884342512},"targetPosition":{"x":25100.068747599486,"y":17665.02255132055},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"a0f2de63-1abd-4945-9c8c-84e21fd121e0","savedPayload":{"position":{"x":25100.068747599486,"y":17665.02255132055},"targetPosition":{"x":24537.00135444902,"y":16689.76121829859},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"69c3f249-07bf-4a03-b8a1-9d74d0255f23","savedPayload":{"position":{"x":23973.90447001356,"y":17665.07363172455},"targetPosition":{"x":24537.00135444902,"y":16689.76121829859},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 21 End","id":"df7e58c1-c042-443b-bfd3-69d57894d358"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"959961b9-9bf9-48ce-9ee0-08d2d6a62b8e","savedPayload":{"position":{"x":23992.377695530853,"y":15746.445370048263},"targetPosition":{"x":24537.00135444902,"y":16689.76121829859},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 21 Begin","id":"4f1044fa-67dd-42dc-ba74-61e299e1f1e1"}}}],"id":"9a17429f-8213-4a7e-8167-dc36d761973e","savedPayload":{"position":{"x":24537.00135444902,"y":16689.76121829859},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 28","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"713c60e9-ee11-4551-ab62-58ce9c16e066","savedPayload":{"position":{"x":24386.127901170057,"y":18378.96339774998},"targetPosition":{"x":24386.127901170057,"y":17252.828611449055},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"725a4d2f-0838-4d78-a8fa-a2b129feda2f","savedPayload":{"position":{"x":24386.127901170057,"y":17252.828611449055},"targetPosition":{"x":24386.127901170057,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"f4c161f4-6510-47a2-97f7-3f9f848d9892","savedPayload":{"position":{"x":23820.361212095424,"y":17106.630475855414},"targetPosition":{"x":24386.127901170057,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 28 Begin","id":"7bd742dd-7c4c-4dfe-9415-e655a5e1cc88"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"b1b22a6f-0a69-401f-90ea-2306baee0ea2","savedPayload":{"position":{"x":23827.767361260387,"y":15159.585001082989},"targetPosition":{"x":24386.127901170057,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 28 End","id":"7678774b-b068-4757-962b-8e126342898a"}}}],"id":"4f6e0c60-00a8-4a76-b1b1-4eaa25c5824c","savedPayload":{"position":{"x":24386.127901170057,"y":16126.693825148128},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 35","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GREEN\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"73f591c5-286b-4441-aa59-f647f738e40e","savedPayload":{"position":{"x":23410.8665681481,"y":17514.149098041587},"targetPosition":{"x":23973.93396129856,"y":16538.887765019626},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2c56cb10-43c9-447b-8b8a-30dae8e7fab8","savedPayload":{"position":{"x":23973.93396129856,"y":16538.887765019626},"targetPosition":{"x":24537.00135444902,"y":15563.626431997665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"3ad24108-1339-4c6d-abc6-27a9aa5bc046","savedPayload":{"position":{"x":23977.722280119408,"y":16532.326204346646},"targetPosition":{"x":24537.00135444902,"y":15563.626431997665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 35 End","id":"f620fdbe-48b5-4831-995d-b3c721738d1b"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"abfdefe3-b9d4-4816-b2cc-7aa64bb69734","savedPayload":{"position":{"x":23978.981676853462,"y":14597.107998778956},"targetPosition":{"x":24537.00135444902,"y":15563.626431997665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 35 Begin","id":"93613bd9-2dbb-493c-a72e-414b0398733e"}}}],"id":"a8a09eca-7bee-4f17-b7db-1f43f419441c","savedPayload":{"position":{"x":24537.00135444902,"y":15563.626431997665},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 70","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GREY\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"9ac33f9d-bce7-407a-8de9-3334bb263b69","savedPayload":{"position":{"x":22998.672628276596,"y":16277.567278427092},"targetPosition":{"x":23973.933961298557,"y":15714.49988527663},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ea184e1f-5a0f-4ed3-b3ed-6ba18e7237f9","savedPayload":{"position":{"x":23973.933961298557,"y":15714.49988527663},"targetPosition":{"x":24949.195294320518,"y":15151.432492126167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"5505dd50-5ad7-48fc-9256-14cab581d92f","savedPayload":{"position":{"x":24390.246806153897,"y":16119.559672444566},"targetPosition":{"x":24949.195294320518,"y":15151.432492126167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 70 Begin","id":"9e506a85-5f72-4a3f-a773-cd85d8b15f21"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"b255b251-b73a-469c-a41f-e273d2909af2","savedPayload":{"position":{"x":24384.539964444593,"y":14173.420772016501},"targetPosition":{"x":24949.195294320518,"y":15151.432492126167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 70 End","id":"8392e1ff-33a6-4b95-baad-3f146bd3c121"}}}],"id":"31cf093b-6de4-4e4a-a8f4-f3ff989dd374","savedPayload":{"position":{"x":24949.195294320518,"y":15151.432492126167},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 140","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.PATINATED_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"e9088da2-eb17-4e48-9fba-5bd00fba6a2b","savedPayload":{"position":{"x":23259.993114869132,"y":15000.559038847203},"targetPosition":{"x":24386.127901170057,"y":15000.559038847203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b93ae9ca-6c4e-47db-a998-045e845ab9cd","savedPayload":{"position":{"x":24386.127901170057,"y":15000.559038847203},"targetPosition":{"x":25512.262687470982,"y":15000.559038847203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e1bdea5b-664c-4146-adf6-858405354f8c","savedPayload":{"position":{"x":24950.209994933804,"y":15974.062858852485},"targetPosition":{"x":25512.262687470982,"y":15000.559038847203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 140 Begin","id":"39e65adb-6125-4e9a-8f15-5403b21aac40"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"3d994072-d0b7-4065-a8be-3c718de28086","savedPayload":{"position":{"x":24946.329041705718,"y":14020.333210669078},"targetPosition":{"x":25512.262687470982,"y":15000.559038847203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 140 End","id":"7559cbf5-acba-4a60-91b4-d95f90293f22"}}}],"id":"41a149aa-efdf-4f53-82cf-cce9e904a000","savedPayload":{"position":{"x":25512.262687470982,"y":15000.559038847203},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 210","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.TITANIUM_YELLOW\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"08949257-633e-4013-b770-bbb798972d2d","savedPayload":{"position":{"x":24124.807414577524,"y":14025.297705825242},"targetPosition":{"x":25100.068747599486,"y":14588.365098975704},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"53e088d8-8ec5-4dbe-b732-c65f31005105","savedPayload":{"position":{"x":25100.068747599486,"y":14588.365098975704},"targetPosition":{"x":26075.330080621447,"y":15151.432492126167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"15882e16-4782-48e8-9cac-fbb1d07ae51d","savedPayload":{"position":{"x":25510.799327828143,"y":16129.228438399272},"targetPosition":{"x":26075.330080621447,"y":15151.432492126167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 210 Begin","id":"479df802-576f-45f1-8322-6b2e493c5ecc"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a6f51174-ea46-4282-a476-4e809f456682","savedPayload":{"position":{"x":25516.36887174905,"y":14183.283278899062},"targetPosition":{"x":26075.330080621447,"y":15151.432492126167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 210 End","id":"45a926f2-4bb2-4562-b40f-bd2532ff1d10"}}}],"id":"c3ee15c5-a5b3-4318-bc09-a9fd03592d83","savedPayload":{"position":{"x":26075.330080621447,"y":15151.432492126167},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 280","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RED\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"942d9600-7935-4ca6-81d8-9bd014df736f","savedPayload":{"position":{"x":25361.389234192015,"y":13613.103765953743},"targetPosition":{"x":25924.45662734248,"y":14588.365098975704},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1c6e0c3f-8ac7-4b0b-9da8-0f7faf9befd1","savedPayload":{"position":{"x":25924.45662734248,"y":14588.365098975704},"targetPosition":{"x":26487.524020492943,"y":15563.626431997665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"cc54382b-cf2b-42c7-8958-8816809dd7ba","savedPayload":{"position":{"x":25925.59850383037,"y":16536.909976926636},"targetPosition":{"x":26487.524020492943,"y":15563.626431997665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 280 Begin","id":"d5e455bf-bb27-41fa-ba41-c8e7d0aa4d5e"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"3629786a-8eed-4cff-9780-0e685aeed577","savedPayload":{"position":{"x":25927.657465939694,"y":14593.909114052907},"targetPosition":{"x":26487.524020492943,"y":15563.626431997665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 280 End","id":"298d0ca9-1b4a-48d8-8efe-fda6b2fc2c34"}}}],"id":"3bbf5574-e89c-4538-a95c-2da58e5a74d7","savedPayload":{"position":{"x":26487.524020492943,"y":15563.626431997665},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 350","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.BLACK\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"8142bd21-31e4-4edd-97d5-217e30321e0a","savedPayload":{"position":{"x":26638.397473771907,"y":13874.424252546278},"targetPosition":{"x":26638.397473771907,"y":15000.559038847203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6eade211-19cf-48a7-87ac-faf30e387e10","savedPayload":{"position":{"x":26638.397473771907,"y":15000.559038847203},"targetPosition":{"x":26638.397473771907,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"f16d8eac-9e78-4167-b087-9803637412a6","savedPayload":{"position":{"x":26069.033275179245,"y":17112.86154512135},"targetPosition":{"x":26638.397473771907,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 350 End","id":"d6598381-9b15-4cb3-afba-b85ed6770ce3"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"b94a339d-5abf-4563-a693-edb5d812c24e","savedPayload":{"position":{"x":26070.43493092899,"y":15142.953844148184},"targetPosition":{"x":26638.397473771907,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"SMA 350 Begin","id":"f3c05d30-da2e-449e-8471-dcc216f4e630"}}}],"id":"046240f2-f70b-471d-b07b-0e7490222f0d","savedPayload":{"position":{"x":26638.397473771907,"y":16126.693825148128},"targetPosition":{"x":25512.262687470982,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"391b803a-4b4e-4c0b-bb6b-b4d68e989c9c","savedPayload":{"position":{"x":25512.262687470982,"y":16126.693825148128},"targetPosition":{"x":26487.524020492943,"y":16689.76121829859},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"SMA Base 7","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"SMA 7","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma7","id":"1c683838-c882-49bd-997a-2f4255347854","savedPayload":{"position":{"x":25213.06683840724,"y":16019.526523932209},"targetPosition":{"x":26337.926095961073,"y":16073.110174540168},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c26c14bd-b9ab-4121-89b2-eb3044534cf2","savedPayload":{"position":{"x":26337.926095961073,"y":16073.110174540168},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 14","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma14","id":"1060ea54-bb29-4c3b-8db0-522893543a36","savedPayload":{"position":{"x":25628.14072213875,"y":14820.249297343455},"targetPosition":{"x":26545.463037826827,"y":15473.471561245791},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"94b29f18-cad5-49df-8b87-d6ef10f5ae61","savedPayload":{"position":{"x":26545.463037826827,"y":15473.471561245791},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 21","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma21","id":"c0287e9c-4b88-4b92-b6ac-f17474649958","savedPayload":{"position":{"x":26625.701313352663,"y":14035.758977436753},"targetPosition":{"x":27044.243333433784,"y":15081.22640129244},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ffcb7173-b2bf-4d5d-90e5-6f8b26af4b86","savedPayload":{"position":{"x":27044.243333433784,"y":15081.22640129244},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 28","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma28","id":"9f1615e0-e437-4221-8e9a-a6dcb4556022","savedPayload":{"position":{"x":27889.03017276489,"y":13915.12569783876},"targetPosition":{"x":27675.907763139898,"y":15020.909761493444},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"186d8c1a-5eef-44a1-aa09-b28e4f5b6b0e","savedPayload":{"position":{"x":27675.907763139898,"y":15020.909761493444},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 35","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma35","id":"423f3069-d5a3-4d29-bb35-68802f367c50","savedPayload":{"position":{"x":29017.029313767227,"y":14496.649672467809},"targetPosition":{"x":28239.907333641066,"y":15311.671748807968},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6b047d8e-451d-4267-adc4-6e9496aa0025","savedPayload":{"position":{"x":28239.907333641066,"y":15311.671748807968},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 70","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma70","id":"62ed074e-5b1a-4306-b411-cd436680147d","savedPayload":{"position":{"x":29651.5669791525,"y":15595.701148231243},"targetPosition":{"x":28557.176166333702,"y":15861.197486689685},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9676685c-f20e-4d65-a0fe-8551e80132ee","savedPayload":{"position":{"x":28557.176166333702,"y":15861.197486689685},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 140","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma140","id":"2200abd2-2897-4eff-9804-f599c0e18f42","savedPayload":{"position":{"x":29591.181943589792,"y":16863.339047100817},"targetPosition":{"x":28526.98364855235,"y":16495.01643612447},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a44d3c89-8463-4493-b195-70ee4146bbdb","savedPayload":{"position":{"x":28526.98364855235,"y":16495.01643612447},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 210","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma210","id":"38e3066f-1951-4c96-8a36-3bcf778328bd","savedPayload":{"position":{"x":28855.04602920998,"y":17897.097292886825},"targetPosition":{"x":28158.915691362443,"y":17011.895559017477},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"44198c4d-4d98-43a2-9c03-2d8794b9edac","savedPayload":{"position":{"x":28158.915691362443,"y":17011.895559017477},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 280","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma280","id":"eca55243-4492-4983-9e03-04694437c8ae","savedPayload":{"position":{"x":27676.877187540842,"y":18368.76494673895},"targetPosition":{"x":27569.831270527873,"y":17247.729385943538},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"96706885-fa55-4fad-82bd-fb43a2f331d7","savedPayload":{"position":{"x":27569.831270527873,"y":17247.729385943538},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"SMA 350","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.sma350","id":"64ea1867-2fb1-4191-a4c7-b5ac37f7be7a","savedPayload":{"position":{"x":26430.735701269077,"y":18128.590861203527},"targetPosition":{"x":26946.76052739199,"y":17127.642343175827},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9f208b33-3d80-4d2e-ad88-13139a8e93f8","savedPayload":{"position":{"x":26946.76052739199,"y":17127.642343175827},"targetPosition":{"x":27462.785353514904,"y":16126.693825148128},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b73ac725-eea1-4aa2-974c-937de300b456","savedPayload":{"position":{"x":27462.785353514904,"y":16126.693825148128},"targetPosition":{"x":26487.524020492943,"y":16689.76121829859},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"515565ff-dee2-4adf-adaf-b4cfea5f5dbd","savedPayload":{"position":{"x":26487.524020492943,"y":16689.76121829859},"targetPosition":{"x":26487.524020492943,"y":17815.896004599515},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"9b2443b7-9276-4fa3-b4b3-b9b991d886d8","savedPayload":{"position":{"x":26487.524020492943,"y":17815.896004599515},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter","name":"RSI","code":"{\n\"codeName\": \"Plotter RSI\"\n}","modules":[{"type":"Plotter Module","name":"RSI 14","code":"{\n\t\"codeName\": \"RSI 14\",\n\t\"slot\": {\n\t\t\"number\": 3,\n\t\t\"minValue\": 0,\n\t\t\"maxValue\": 100\n\t},\n \"banner\": \"Oscillator\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"Grid","points":[{"type":"Point","name":"Begin 0","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 0","id":"88e15149-215e-42c8-bc00-78b63343daa0","savedPayload":{"position":{"x":24418.55655724605,"y":15678.746889496062},"targetPosition":{"x":25284.059069091752,"y":16259.656097042565},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e376a028-b688-4bb5-a100-00be6d161bd6","savedPayload":{"position":{"x":25284.059069091752,"y":16259.656097042565},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 0","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 0","id":"6904030c-e751-4eb9-844a-d1cf45caa1f2","savedPayload":{"position":{"x":24955.144687460906,"y":15131.890757967642},"targetPosition":{"x":25552.35313419918,"y":15986.228031278355},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f562d9d7-357b-4677-ac4b-3ddbb735ca2b","savedPayload":{"position":{"x":25552.35313419918,"y":15986.228031278355},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 20","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 20","id":"4fc2797d-7023-461e-912e-23f2834d4141","savedPayload":{"position":{"x":25653.045437632256,"y":14815.800592052958},"targetPosition":{"x":25901.303509284855,"y":15828.182948321013},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8227114d-f503-4f27-ae0b-4bc2804e2cdb","savedPayload":{"position":{"x":25901.303509284855,"y":15828.182948321013},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 20","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 20","id":"e6640285-69e5-4a37-8907-9d8bbcc2008b","savedPayload":{"position":{"x":26418.003444247897,"y":14773.166120174861},"targetPosition":{"x":26283.782512592676,"y":15806.865712381965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c9de9989-11da-44a7-bcbf-137435767d90","savedPayload":{"position":{"x":26283.782512592676,"y":15806.865712381965},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 30","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 30","id":"db815155-dbec-4824-9132-470a173d328b","savedPayload":{"position":{"x":27146.70688973575,"y":15009.745364006256},"targetPosition":{"x":26648.1342353366,"y":15925.155334297662},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0f0a70c3-9f37-4e58-a93c-fcfcf9686c09","savedPayload":{"position":{"x":26648.1342353366,"y":15925.155334297662},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 30","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 30","id":"db6c3539-81f5-465d-9d79-8004a60ac5fb","savedPayload":{"position":{"x":27740.740335897495,"y":15493.586985736863},"targetPosition":{"x":26945.150958417475,"y":16167.076145162966},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ac82bb81-5d37-4eea-ab52-0f698de71014","savedPayload":{"position":{"x":26945.150958417475,"y":16167.076145162966},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 50","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 50","id":"a6a8718f-2c9a-4c5f-af7a-7029bddfb839","savedPayload":{"position":{"x":28119.876274176033,"y":16159.345493292985},"targetPosition":{"x":27134.718927556743,"y":16499.955398941027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a0cdc721-996a-44b2-b619-bbc0ef97cd62","savedPayload":{"position":{"x":27134.718927556743,"y":16499.955398941027},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 50","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 50","id":"c996c1b4-2b1f-4d11-acd4-8aba4d3e3e5d","savedPayload":{"position":{"x":28232.910295242084,"y":16917.10651113393},"targetPosition":{"x":27191.23593808977,"y":16878.8359078615},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"59a77a0b-bcee-4c1e-855c-854c3d3bc521","savedPayload":{"position":{"x":27191.23593808977,"y":16878.8359078615},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 70","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 70","id":"bdcd3323-0da6-4667-95c2-b017623500af","savedPayload":{"position":{"x":28064.576528207555,"y":17664.530214901522},"targetPosition":{"x":27107.069054572505,"y":17252.547759745295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"228edebc-3b5b-4760-9791-2229cb76a662","savedPayload":{"position":{"x":27107.069054572505,"y":17252.547759745295},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 70","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 70","id":"9977888b-2cf4-472e-8468-d6705690a57c","savedPayload":{"position":{"x":27637.60938108005,"y":18300.672891784237},"targetPosition":{"x":26893.58548100875,"y":17570.619098186653},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4cbfd9aa-d595-4e06-ac82-a87bfdf9f680","savedPayload":{"position":{"x":26893.58548100875,"y":17570.619098186653},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 80","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 80","id":"75cda528-f689-4d21-a8ed-ba08705d5ec7","savedPayload":{"position":{"x":27009.673132985783,"y":18739.619948280797},"targetPosition":{"x":26579.61735696162,"y":17790.092626434933},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e7b453a3-5312-48e6-8797-6d96e058766b","savedPayload":{"position":{"x":26579.61735696162,"y":17790.092626434933},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Eng 80","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 80","id":"df4c7f58-96c7-4ffb-9ce3-ff1b35ddf3c3","savedPayload":{"position":{"x":26265.574053744745,"y":18922.089152121745},"targetPosition":{"x":26207.5678173411,"y":17881.327228355407},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d64e2ceb-abb9-4cc0-b929-211bcbd934a7","savedPayload":{"position":{"x":26207.5678173411,"y":17881.327228355407},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"Begin 100","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = 100","id":"79f20ec4-c499-41ca-aa79-f56fe5b7bcbc","savedPayload":{"position":{"x":25505.80684720378,"y":18823.437026231597},"targetPosition":{"x":25827.684214070618,"y":17832.001165410333},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7d5d4922-1d7d-45a3-b5ff-75d14016ee1d","savedPayload":{"position":{"x":25827.684214070618,"y":17832.001165410333},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"End 100","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = 100","id":"58d10fb8-87e5-4a4c-95ab-99be2f5b14ea","savedPayload":{"position":{"x":24832.982284621674,"y":18456.98708686211},"targetPosition":{"x":25491.271932779564,"y":17648.77619572559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"dc11ae4c-bce1-4e88-986a-457a3612102a","savedPayload":{"position":{"x":25491.271932779564,"y":17648.77619572559},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"RSI Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.value","id":"e068d3fa-623c-4349-a10f-4abdd6263b07","savedPayload":{"position":{"x":24337.969051424745,"y":17872.230428894352},"targetPosition":{"x":25243.7653161811,"y":17356.39786674171},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7f687c2f-3a91-4b53-b00d-8a651e82c97d","savedPayload":{"position":{"x":25243.7653161811,"y":17356.39786674171},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"RSI End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.value","id":"bb45d3f0-e6e7-473a-b2f4-7016c65bd7ee","savedPayload":{"position":{"x":24087.62142771924,"y":17148.141679235367},"targetPosition":{"x":25118.591504328346,"y":16994.353491912218},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f5d97a45-0db5-4d48-bf32-efdd6b2726c0","savedPayload":{"position":{"x":25118.591504328346,"y":16994.353491912218},"targetPosition":{"x":26149.561580937454,"y":16840.56530458907},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"ff9f35c7-b1ca-446e-905c-c971ca51dfbe","savedPayload":{"position":{"x":26149.561580937454,"y":16840.56530458907},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"Box 0-20","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.25,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.35,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n }\n}","id":"9e7202ce-11e7-4526-80d5-52f0dbdcb91c","savedPayload":{"position":{"x":26302.43487484468,"y":15236.30555394246},"targetPosition":{"x":26114.54114376115,"y":16261.608461010921},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"8716fe80-7aa5-4707-93d9-f47ac26e9a53","savedPayload":{"position":{"x":26114.54114376115,"y":16261.608461010921},"targetPosition":{"x":25926.647412677623,"y":17286.91136807938},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"89a7b82e-5d1d-4359-80a8-0b85d997a62c","savedPayload":{"position":{"x":26246.933503166867,"y":18272.65059596435},"targetPosition":{"x":25926.647412677623,"y":17286.91136807938},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 0","id":"e376a028-b688-4bb5-a100-00be6d161bd6"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"4ddfe945-d3dd-431a-a0bf-477d2f0c0af1","savedPayload":{"position":{"x":25085.1279248983,"y":17898.311064096346},"targetPosition":{"x":25926.647412677623,"y":17286.91136807938},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 0","id":"f562d9d7-357b-4677-ac4b-3ddbb735ca2b"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"1fd4c8e3-47e3-4e05-8739-a34e8239b178","savedPayload":{"position":{"x":25076.704687875183,"y":16669.391832141653},"targetPosition":{"x":25926.647412677623,"y":17286.91136807938},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 20","id":"c9de9989-11da-44a7-bcbf-137435767d90"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"2920b1c1-1d61-44b8-b31a-31e2d87fa9e4","savedPayload":{"position":{"x":26252.32795527934,"y":16284.56972373577},"targetPosition":{"x":25926.647412677623,"y":17286.91136807938},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 20","id":"8227114d-f503-4f27-ae0b-4bc2804e2cdb"}}}],"id":"0dce63e6-acf6-482e-bcf9-3e44b7487cbf","savedPayload":{"position":{"x":25926.647412677623,"y":17286.91136807938},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Box 80-100","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.25,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.35,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n }\n}","id":"f95287c0-8123-4025-8018-370eb91b6189","savedPayload":{"position":{"x":26807.54667203187,"y":15937.45366722177},"targetPosition":{"x":26164.693016382942,"y":16757.995733689015},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c46a4519-e917-410e-98d7-faf0ad80a99a","savedPayload":{"position":{"x":26164.693016382942,"y":16757.995733689015},"targetPosition":{"x":25521.839360734015,"y":17578.53780015626},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"eb76ec36-eae3-424a-9105-d26eefca1b67","savedPayload":{"position":{"x":25846.12318984432,"y":18576.5808023812},"targetPosition":{"x":25521.839360734015,"y":17578.53780015626},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 80","id":"e7b453a3-5312-48e6-8797-6d96e058766b"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"6facb2b7-b324-4e01-a53f-5d72541ba64f","savedPayload":{"position":{"x":24666.882398358895,"y":18199.700392936065},"targetPosition":{"x":25521.839360734015,"y":17578.53780015626},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Eng 80","id":"d64e2ceb-abb9-4cc0-b929-211bcbd934a7"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"2e7bfaf4-7828-4021-9895-58882a03987e","savedPayload":{"position":{"x":24676.078826174282,"y":16964.056803290066},"targetPosition":{"x":25521.839360734015,"y":17578.53780015626},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 100","id":"dc11ae4c-bce1-4e88-986a-457a3612102a"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"d2a601ff-7a5d-414e-bc06-be93e13e6ddb","savedPayload":{"position":{"x":25846.85927236522,"y":16578.229368874745},"targetPosition":{"x":25521.839360734015,"y":17578.53780015626},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 100","id":"7d5d4922-1d7d-45a3-b5ff-75d14016ee1d"}}}],"id":"2429edaa-20db-4fd9-b834-c84953e78bf3","savedPayload":{"position":{"x":25521.839360734015,"y":17578.53780015626},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Box 20-30","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.15,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.2,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n }\n}","id":"f1ac3d45-3f00-4269-a41f-872bcbc99620","savedPayload":{"position":{"x":26928.961181204126,"y":16793.026645625872},"targetPosition":{"x":25978.41762627258,"y":17220.83157266813},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1aa28d17-1653-496a-8b8a-35fdf6691e0b","savedPayload":{"position":{"x":25978.41762627258,"y":17220.83157266813},"targetPosition":{"x":25027.874071341037,"y":17648.636499710385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"2e88069b-6333-45f8-8199-021fe3896667","savedPayload":{"position":{"x":25352.279294757758,"y":18647.05311519369},"targetPosition":{"x":25027.874071341037,"y":17648.636499710385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 20","id":"8227114d-f503-4f27-ae0b-4bc2804e2cdb"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"cdea2bf9-a545-4877-8d65-5a027352bb8a","savedPayload":{"position":{"x":24172.369424774348,"y":18270.19700834728},"targetPosition":{"x":25027.874071341037,"y":17648.636499710385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 20","id":"c9de9989-11da-44a7-bcbf-137435767d90"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"bfb22589-c1af-4822-8944-1767e3e691bc","savedPayload":{"position":{"x":24182.341514783253,"y":17034.321138558058},"targetPosition":{"x":25027.874071341037,"y":17648.636499710385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 30","id":"ac82bb81-5d37-4eea-ab52-0f698de71014"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"208c9dfc-a7fb-41ab-becc-3b68104e07ed","savedPayload":{"position":{"x":25346.842989869903,"y":16666.951110283502},"targetPosition":{"x":25027.874071341037,"y":17648.636499710385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 30","id":"0f0a70c3-9f37-4e58-a93c-fcfcf9686c09"}}}],"id":"4a0db3d1-061d-4587-9c05-f42c09c0b271","savedPayload":{"position":{"x":25027.874071341037,"y":17648.636499710385},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Box 70-80","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.15,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.2,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n }\n}","id":"6335d57f-9462-4d19-b954-2f511308b9c8","savedPayload":{"position":{"x":26638.86380151353,"y":17607.023030574514},"targetPosition":{"x":25598.38842044254,"y":17544.085865014666},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b01a727e-5773-47c2-b094-c19ad0ac71f4","savedPayload":{"position":{"x":25598.38842044254,"y":17544.085865014666},"targetPosition":{"x":24557.913039371553,"y":17481.148699454818},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"c59e52e4-9b7e-4985-ab53-35f5cd29f250","savedPayload":{"position":{"x":24880.631649862844,"y":18474.37445410394},"targetPosition":{"x":24557.913039371553,"y":17481.148699454818},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 70","id":"228edebc-3b5b-4760-9791-2229cb76a662"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"957f8c1e-cb14-4241-b805-abfdc50c07d0","savedPayload":{"position":{"x":23720.64265099057,"y":18089.46124405317},"targetPosition":{"x":24557.913039371553,"y":17481.148699454818},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 70","id":"4cbfd9aa-d595-4e06-ac82-a87bfdf9f680"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"37688deb-5377-4f52-bde8-5e8abbf6e25f","savedPayload":{"position":{"x":23718.044241379455,"y":16870.948299768814},"targetPosition":{"x":24557.913039371553,"y":17481.148699454818},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Eng 80","id":"d64e2ceb-abb9-4cc0-b929-211bcbd934a7"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"6d346213-f33f-4b5a-a519-5e1c3fb42e50","savedPayload":{"position":{"x":24875.87195327585,"y":16502.571784633437},"targetPosition":{"x":24557.913039371553,"y":17481.148699454818},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 80","id":"e7b453a3-5312-48e6-8797-6d96e058766b"}}}],"id":"5835d367-23bc-4039-b2c1-1a23f3ca652f","savedPayload":{"position":{"x":24557.913039371553,"y":17481.148699454818},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Line 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.15,\n \"lineWidth\": 1,\n \"lineDash\": [3,3],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n }\n}","id":"6ad882da-10d9-427c-8fb5-3a7c55b678a1","savedPayload":{"position":{"x":25225.428005138037,"y":18940.518317018683},"targetPosition":{"x":24722.523339371706,"y":18027.481076499993},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fa05777f-6288-4dd2-9b7a-61194bc43681","savedPayload":{"position":{"x":24722.523339371706,"y":18027.481076499993},"targetPosition":{"x":24219.618673605375,"y":17114.443835981303},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a9a9dd2d-d7f8-4a25-b81d-d1e2a2f9d5a6","savedPayload":{"position":{"x":23693.926856924798,"y":18024.968771595246},"targetPosition":{"x":24219.618673605375,"y":17114.443835981303},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 20","id":"8227114d-f503-4f27-ae0b-4bc2804e2cdb"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a717c7d2-3bc7-4d8c-9e9f-e1fb75fd0d42","savedPayload":{"position":{"x":23696.309754014655,"y":16208.0461991962},"targetPosition":{"x":24219.618673605375,"y":17114.443835981303},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 20","id":"c9de9989-11da-44a7-bcbf-137435767d90"}}}],"id":"27aab97e-ce7f-47fc-bd22-d9b70ffec2d1","savedPayload":{"position":{"x":24219.618673605375,"y":17114.443835981303},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Line 30","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [3,3],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n }\n}","id":"0b1d6f25-076c-4495-b462-435a8d1900d2","savedPayload":{"position":{"x":24132.47094439843,"y":18716.861129786135},"targetPosition":{"x":24111.48054037445,"y":17674.695352012543},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6cb61271-a82a-47a1-ac0c-404dc28b05e5","savedPayload":{"position":{"x":24111.48054037445,"y":17674.695352012543},"targetPosition":{"x":24090.490136350472,"y":16632.52957423895},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e656c0ce-1b57-420e-84f5-cd62a9bb602d","savedPayload":{"position":{"x":23577.2855792253,"y":17521.425941855632},"targetPosition":{"x":24090.490136350472,"y":16632.52957423895},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 30","id":"ac82bb81-5d37-4eea-ab52-0f698de71014"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"8e2ee705-7be7-4705-a32f-242a5c1d286d","savedPayload":{"position":{"x":23570.550290317755,"y":15731.967344030745},"targetPosition":{"x":24090.490136350472,"y":16632.52957423895},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 30","id":"0f0a70c3-9f37-4e58-a93c-fcfcf9686c09"}}}],"id":"18e4afd3-7536-405b-97b6-d36ec7ddc692","savedPayload":{"position":{"x":24090.490136350472,"y":16632.52957423895},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Line 50","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [3,3],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n }\n}","id":"68ca10ec-d83d-4f61-b9ee-6a0f3a74cb12","savedPayload":{"position":{"x":23268.64420668741,"y":18010.900053495883},"targetPosition":{"x":23734.37671300589,"y":17078.35335869619},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"fefbf0c0-287c-4bb7-81a7-6b745dcfcf3b","savedPayload":{"position":{"x":23734.37671300589,"y":17078.35335869619},"targetPosition":{"x":24200.10921932437,"y":16145.806663896494},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"cfa25adf-05eb-49d0-956a-69e4fde9bacf","savedPayload":{"position":{"x":23674.374887217826,"y":17056.405238388317},"targetPosition":{"x":24200.10921932437,"y":16145.806663896494},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 50","id":"a0cdc721-996a-44b2-b619-bbc0ef97cd62"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"de1447aa-e70f-419b-bab5-d94b333612c7","savedPayload":{"position":{"x":23687.666438777567,"y":15258.229732017562},"targetPosition":{"x":24200.10921932437,"y":16145.806663896494},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 50","id":"59a77a0b-bcee-4c1e-855c-854c3d3bc521"}}}],"id":"bbe48886-b71e-4507-b6e8-7f073b691a6a","savedPayload":{"position":{"x":24200.10921932437,"y":16145.806663896494},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Line 70","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [3,3],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n }\n}","id":"913293ac-040f-4348-ade4-5b3103fe1375","savedPayload":{"position":{"x":22831.840087373876,"y":16984.36226297277},"targetPosition":{"x":23677.601799522392,"y":16375.069860519614},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8640a48e-f7ca-419c-a687-3f52c3a5afa9","savedPayload":{"position":{"x":23677.601799522392,"y":16375.069860519614},"targetPosition":{"x":24523.36351167091,"y":15765.777458066457},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"bc77b107-0a70-4038-86a6-3034ab4e86dd","savedPayload":{"position":{"x":24000.492062150344,"y":16671.41737446326},"targetPosition":{"x":24523.36351167091,"y":15765.777458066457},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 70","id":"4cbfd9aa-d595-4e06-ac82-a87bfdf9f680"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"36882cd2-2ec4-4d2d-af00-98b4c54bf1e7","savedPayload":{"position":{"x":23997.895637873993,"y":14855.640402905012},"targetPosition":{"x":24523.36351167091,"y":15765.777458066457},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 70","id":"228edebc-3b5b-4760-9791-2229cb76a662"}}}],"id":"508b71cf-79d3-4db4-a6d3-0b26226ad5b9","savedPayload":{"position":{"x":24523.36351167091,"y":15765.777458066457},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Line 80","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.15,\n \"lineWidth\": 1,\n \"lineDash\": [3,3],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.3,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n }\n}","id":"9506c6ad-440b-45a1-aadf-89004f5311fb","savedPayload":{"position":{"x":22922.125146132268,"y":15872.41519490418},"targetPosition":{"x":23954.162248391145,"y":15725.958631430138},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"59804538-89e9-485a-a2b1-88ff78eb5661","savedPayload":{"position":{"x":23954.162248391145,"y":15725.958631430138},"targetPosition":{"x":24986.199350650022,"y":15579.502067956095},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"23df3ac4-6427-4a69-a5a3-8230186b46f3","savedPayload":{"position":{"x":24469.89743900424,"y":16473.763210971527},"targetPosition":{"x":24986.199350650022,"y":15579.502067956095},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 80","id":"e7b453a3-5312-48e6-8797-6d96e058766b"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"7a5139be-30b6-4eab-994a-bb8b74be455d","savedPayload":{"position":{"x":24464.21229272082,"y":14675.393962729317},"targetPosition":{"x":24986.199350650022,"y":15579.502067956095},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Eng 80","id":"d64e2ceb-abb9-4cc0-b929-211bcbd934a7"}}}],"id":"8499947d-a392-40ed-be9d-225a28429642","savedPayload":{"position":{"x":24986.199350650022,"y":15579.502067956095},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"RSI Line","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.35,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"90fe9ece-e33a-4e2e-904a-779327e7155a","savedPayload":{"position":{"x":23706.709895137472,"y":15955.09542923824},"targetPosition":{"x":24500.70139369103,"y":15279.723231373831},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[{"type":"Style Condition","name":"New Style Condition","code":"(record.previous.value < 20 && record.value < 20) || (record.previous.value > 80 && record.value > 80)","style":{"type":"Style","name":"New Style","code":"{\n \"opacity\": 1,\n \"lineWidth\": 1,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RED\"\n}","id":"58f58e7a-3056-49c7-9117-f2a514d4479e","savedPayload":{"position":{"x":24124.913931523974,"y":13229.11741723691},"targetPosition":{"x":24312.8076626075,"y":14254.42032430537},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9fcf2614-7ec8-407f-93a2-3c749f6683ef","savedPayload":{"position":{"x":24312.8076626075,"y":14254.42032430537},"targetPosition":{"x":24500.70139369103,"y":15279.723231373831},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"6dc77d6e-5aa6-41ab-b247-43c4391fcc96","savedPayload":{"position":{"x":24500.70139369103,"y":15279.723231373831},"targetPosition":{"x":25482.586623328116,"y":15629.653940577884},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"5312ee43-9c15-4d4b-9255-5fb7259c5734","savedPayload":{"position":{"x":24961.14657561482,"y":16532.81459631845},"targetPosition":{"x":25482.586623328116,"y":15629.653940577884},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"RSI Begin","id":"7f687c2f-3a91-4b53-b00d-8a651e82c97d"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"01981eaa-b1c6-41e1-84b4-2dc8106ac095","savedPayload":{"position":{"x":24962.66868675704,"y":14729.129658670408},"targetPosition":{"x":25482.586623328116,"y":15629.653940577884},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"RSI End","id":"f5d97a45-0db5-4d48-bf32-efdd6b2726c0"}}}],"id":"e8eb9949-277b-4493-a9f4-cbcb502aacbc","savedPayload":{"position":{"x":25482.586623328116,"y":15629.653940577884},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"Box 30-70","polygonBody":{"type":"Polygon Body","name":"New Polygon Body","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.05,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.1,\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n }\n}","id":"5b04f4bb-2cc4-48a5-b421-667c96b2dd27","savedPayload":{"position":{"x":23908.45057185017,"y":15284.523120493404},"targetPosition":{"x":24903.62977974997,"y":15594.633503401583},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"208ca141-b231-4a70-8b31-eeae09576822","savedPayload":{"position":{"x":24903.62977974997,"y":15594.633503401583},"targetPosition":{"x":25898.808987649765,"y":15904.743886309761},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"7956c253-3704-46c0-b188-f6662bb58904","savedPayload":{"position":{"x":26219.43894840569,"y":16891.541438053417},"targetPosition":{"x":25898.808987649765,"y":15904.743886309761},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 30","id":"0f0a70c3-9f37-4e58-a93c-fcfcf9686c09"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"b043ae0f-a172-4ebe-922b-a895b8a888c9","savedPayload":{"position":{"x":25056.542947090802,"y":16516.685984670534},"targetPosition":{"x":25898.808987649765,"y":15904.743886309761},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 30","id":"ac82bb81-5d37-4eea-ab52-0f698de71014"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"5830f66c-dca5-4f62-8102-d7d8ea588d0d","savedPayload":{"position":{"x":25048.635114977067,"y":15287.056411614032},"targetPosition":{"x":25898.808987649765,"y":15904.743886309761},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"End 70","id":"4cbfd9aa-d595-4e06-ac82-a87bfdf9f680"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a8eb0500-2421-4879-9459-2ecdb4ae09d5","savedPayload":{"position":{"x":26222.92209768984,"y":14907.22630335675},"targetPosition":{"x":25898.808987649765,"y":15904.743886309761},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"Begin 70","id":"228edebc-3b5b-4760-9791-2229cb76a662"}}}],"id":"b294fd86-15d6-4bc8-9379-0532df288eee","savedPayload":{"position":{"x":25898.808987649765,"y":15904.743886309761},"targetPosition":{"x":25132.655914124065,"y":16611.53917021497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"bc1170cc-a04b-4453-bd28-0e522f747617","savedPayload":{"position":{"x":25132.655914124065,"y":16611.53917021497},"targetPosition":{"x":26139.51491597213,"y":16341.752113777777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"RSI 14","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"RSI 14","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.value","id":"fbec83cc-a090-491b-8cfb-833d40b2b843","savedPayload":{"position":{"x":26948.876085283715,"y":13321.175108233583},"targetPosition":{"x":26679.08902884652,"y":14328.034110081648},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e5fd8d57-8920-417e-8b9b-c1e0d6868a7a","savedPayload":{"position":{"x":26679.08902884652,"y":14328.034110081648},"targetPosition":{"x":26409.301972409325,"y":15334.893111929712},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"ff06d653-f886-44f4-8b28-82bab51aa98d","savedPayload":{"position":{"x":26409.301972409325,"y":15334.893111929712},"targetPosition":{"x":26139.51491597213,"y":16341.752113777777},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"72403d40-2a64-4e51-8eaf-b1a872e4474e","savedPayload":{"position":{"x":26139.51491597213,"y":16341.752113777777},"targetPosition":{"x":26876.586861383,"y":17078.824059188646},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}}],"id":"bbc529df-b938-47b5-8cb7-a5579c859b99","savedPayload":{"position":{"x":26876.586861383,"y":17078.824059188646},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter","name":"MACD PENDING","code":"{}","modules":[],"id":"24146ce7-bc0d-461a-8a43-0d2bd3059886","savedPayload":{"position":{"x":27613.65880679387,"y":16751.29274796419},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter","name":"EMA","code":"{\n\"codeName\": \"Plotter EMA\"\n}","modules":[{"type":"Plotter Module","name":"EMA Popular","code":"{\n\"codeName\": \"EMA-Popular\",\n\"banner\": \"Moving-Average\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"EMA 20 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema20","id":"7bf83ec0-5a84-4a81-a111-19d913e12135","savedPayload":{"position":{"x":27162.225765126634,"y":15272.084789014236},"targetPosition":{"x":27253.064390593732,"y":16310.375029214107},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6e1ff74e-36ba-460c-9f78-c8ee0b5e0837","savedPayload":{"position":{"x":27253.064390593732,"y":16310.375029214107},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 20 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema20","id":"12164218-7531-4063-bbd3-32855e7f7847","savedPayload":{"position":{"x":28539.530370859437,"y":15641.132445852005},"targetPosition":{"x":27941.716693460134,"y":16494.89885763299},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"83f1d130-42bd-45ae-9078-ed3f825531bb","savedPayload":{"position":{"x":27941.716693460134,"y":16494.89885763299},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 50 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema50","id":"580ad5c4-aee7-4af2-b85d-0ef86d020823","savedPayload":{"position":{"x":29357.38764936405,"y":16809.153687947903},"targetPosition":{"x":28350.64533271244,"y":17078.90947868094},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7084c0a2-8874-46a7-b240-d9605a7baf49","savedPayload":{"position":{"x":28350.64533271244,"y":17078.90947868094},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 50 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema50","id":"1e7a4d05-3daf-4d0a-97ff-b53e35eab2aa","savedPayload":{"position":{"x":29233.113090556995,"y":18229.618395015117},"targetPosition":{"x":28288.508053308913,"y":17789.141832214547},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3861b379-3e51-43e2-b607-e3b7fba16a34","savedPayload":{"position":{"x":28288.508053308913,"y":17789.141832214547},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 100 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema100","id":"b50c3599-e7bd-45ef-b348-4ddf85460bbf","savedPayload":{"position":{"x":28224.85614166197,"y":19237.875343910142},"targetPosition":{"x":27784.3795788614,"y":18293.27030666206},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d3b1e9cb-8098-4e62-94ea-4e89852b4f6a","savedPayload":{"position":{"x":27784.3795788614,"y":18293.27030666206},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 100 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema100","id":"f5cf8bb1-98bf-4c38-9e7c-f31678a656e4","savedPayload":{"position":{"x":26804.391434594756,"y":19362.149902717196},"targetPosition":{"x":27074.147225327793,"y":18355.407586065587},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"32ca337a-699d-4c2f-9b6a-73d9a4481e78","savedPayload":{"position":{"x":27074.147225327793,"y":18355.407586065587},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 200 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema200","id":"451d4c20-a9ad-4b8d-a116-fae9406ee356","savedPayload":{"position":{"x":25636.370192498856,"y":18544.292624212583},"targetPosition":{"x":26490.136604279844,"y":17946.47894681328},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"33d1fe7c-ff86-440a-8b42-0de9c296a632","savedPayload":{"position":{"x":26490.136604279844,"y":17946.47894681328},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 200 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema200","id":"24e42cf3-2dc8-4257-a511-7dc52dc6f060","savedPayload":{"position":{"x":25267.322535661093,"y":17166.98801847978},"targetPosition":{"x":26305.612775860962,"y":17257.82664394688},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"87e2a3ab-afc2-44cb-b2ca-03a715a4d673","savedPayload":{"position":{"x":26305.612775860962,"y":17257.82664394688},"targetPosition":{"x":27343.90301606083,"y":17348.665269413978},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"dd70bea3-9a52-4b2b-a364-31917b08d98d","savedPayload":{"position":{"x":27343.90301606083,"y":17348.665269413978},"targetPosition":{"x":26606.916490142255,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"SMA 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"d468c2f1-2586-4fe4-aa0c-fc3737d992f5","savedPayload":{"position":{"x":28350.645332712436,"y":18157.932641613086},"targetPosition":{"x":27343.903016060827,"y":17888.17685088005},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"23236076-e29a-451e-8c95-e3812064279c","savedPayload":{"position":{"x":27343.903016060827,"y":17888.17685088005},"targetPosition":{"x":26337.160699409218,"y":17618.42106014701},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"5f877b66-2ca6-4a8f-967e-70948cefc9ea","savedPayload":{"position":{"x":25812.236298011383,"y":18527.616793500743},"targetPosition":{"x":26337.160699409218,"y":17618.42106014701},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 20 Begin","id":"6e1ff74e-36ba-460c-9f78-c8ee0b5e0837"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"d5971215-2792-4415-bfea-2e4b2b5b98fe","savedPayload":{"position":{"x":25818.98038177672,"y":16720.906422525346},"targetPosition":{"x":26337.160699409218,"y":17618.42106014701},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 20 End","id":"83f1d130-42bd-45ae-9078-ed3f825531bb"}}}],"id":"17ca2f43-9ddf-47f8-9929-e4612b00dd4f","savedPayload":{"position":{"x":26337.160699409218,"y":17618.42106014701},"targetPosition":{"x":26606.916490142255,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 50","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"330fba24-ff59-48d2-a297-b4e6903efc3d","savedPayload":{"position":{"x":26139.68575495672,"y":18894.919167531658},"targetPosition":{"x":25869.929964223684,"y":17888.17685088005},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"0806f6fe-730c-4fde-b730-dcdf31825733","savedPayload":{"position":{"x":25869.929964223684,"y":17888.17685088005},"targetPosition":{"x":25600.174173490646,"y":16881.43453422844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"b00cf575-af36-4653-97ec-441912af5bb8","savedPayload":{"position":{"x":25074.059146220792,"y":17792.692492085313},"targetPosition":{"x":25600.174173490646,"y":16881.43453422844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 50 End","id":"3861b379-3e51-43e2-b607-e3b7fba16a34"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"da71a6f7-2917-4c07-bd77-dc1b0334c5ec","savedPayload":{"position":{"x":25081.584104085545,"y":15983.210185718135},"targetPosition":{"x":25600.174173490646,"y":16881.43453422844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 50 Begin","id":"7084c0a2-8874-46a7-b240-d9605a7baf49"}}}],"id":"36482493-d7f7-4a41-9588-a348e3204025","savedPayload":{"position":{"x":25600.174173490646,"y":16881.43453422844},"targetPosition":{"x":26606.916490142255,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 100","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"278d84a3-2d10-4db3-90cc-b38f6af88ecf","savedPayload":{"position":{"x":24395.95691238653,"y":17348.665269413978},"targetPosition":{"x":25132.943438305105,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"612138e2-6896-4f8f-956a-d5bf1b8c043a","savedPayload":{"position":{"x":25132.943438305105,"y":16611.678743495402},"targetPosition":{"x":25869.92996422368,"y":15874.692217576829},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"fc01c5ca-4323-4a71-9d83-1f288b80e8ed","savedPayload":{"position":{"x":25365.267420745324,"y":16748.793383558277},"targetPosition":{"x":25869.92996422368,"y":15874.692217576829},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 100 Begin","id":"d3b1e9cb-8098-4e62-94ea-4e89852b4f6a"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"fbd76a9e-5a2e-4cb8-a2ed-35d2616d9a2e","savedPayload":{"position":{"x":25345.12021150623,"y":14965.695061562546},"targetPosition":{"x":25869.92996422368,"y":15874.692217576829},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 100 End","id":"32ca337a-699d-4c2f-9b6a-73d9a4481e78"}}}],"id":"37a16b86-6ee7-4dfa-b32a-c8b9bcb9b484","savedPayload":{"position":{"x":25869.92996422368,"y":15874.692217576829},"targetPosition":{"x":26606.916490142255,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 200","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"b170fc33-32c4-452d-919d-cf2df82c224f","savedPayload":{"position":{"x":24863.187647572075,"y":15065.424845377716},"targetPosition":{"x":25869.929964223684,"y":15335.180636110754},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"ad1ad1fa-9fe8-4a56-aade-dcade3d90c38","savedPayload":{"position":{"x":25869.929964223684,"y":15335.180636110754},"targetPosition":{"x":26876.672280875293,"y":15604.936426843791},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a98d63cf-bb2b-43f0-80cd-680c59ef741a","savedPayload":{"position":{"x":26352.12759158645,"y":16513.4744795325},"targetPosition":{"x":26876.672280875293,"y":15604.936426843791},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 200 End","id":"87e2a3ab-afc2-44cb-b2ca-03a715a4d673"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"c9ce7524-76dd-4399-92d8-58d1d757ed89","savedPayload":{"position":{"x":26353.674134127556,"y":14699.07706441235},"targetPosition":{"x":26876.672280875293,"y":15604.936426843791},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 200 Begin","id":"33d1fe7c-ff86-440a-8b42-0de9c296a632"}}}],"id":"2e225665-d05d-46cd-91d0-4b36803bcf67","savedPayload":{"position":{"x":26876.672280875293,"y":15604.936426843791},"targetPosition":{"x":26606.916490142255,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"5ea59fb9-f0d6-438b-b1a7-a62ed1750004","savedPayload":{"position":{"x":26606.916490142255,"y":16611.678743495402},"targetPosition":{"x":27613.658806793865,"y":16341.922952762367},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"EMA Popular","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"EMA 20","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema20","id":"c79a60a8-e960-4a7d-86b0-bb8d65272b30","savedPayload":{"position":{"x":25801.758669228595,"y":15444.275600544246},"targetPosition":{"x":26842.58663337775,"y":15389.7281183275},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"80fa0565-6440-406f-a49d-57adcdaf20ae","savedPayload":{"position":{"x":26842.58663337775,"y":15389.7281183275},"targetPosition":{"x":27883.414597526902,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 50","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema50","id":"ad4b2bc1-bc45-4a8f-b07f-aa0f79f5e884","savedPayload":{"position":{"x":27136.392062421906,"y":13389.120398828894},"targetPosition":{"x":27509.903329974404,"y":14362.150517469825},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f43975ff-c552-42b8-a504-d2b6a3a46b18","savedPayload":{"position":{"x":27509.903329974404,"y":14362.150517469825},"targetPosition":{"x":27883.414597526902,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 100","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema100","id":"2bfe0bed-a18d-452e-a59a-9ddbd135dd0a","savedPayload":{"position":{"x":29503.385208768206,"y":14023.354300882387},"targetPosition":{"x":28693.399903147554,"y":14679.267468496571},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9507a099-6a7f-4d00-8b6e-623a7003055c","savedPayload":{"position":{"x":28693.399903147554,"y":14679.267468496571},"targetPosition":{"x":27883.414597526902,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 200","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema200","id":"ee01f76f-2b56-4d87-a278-4dd98436dca7","savedPayload":{"position":{"x":29631.63403115497,"y":16470.487610884273},"targetPosition":{"x":28757.524314340935,"y":15902.834123497514},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5bf57692-7070-4a12-9bc3-487a841125b0","savedPayload":{"position":{"x":28757.524314340935,"y":15902.834123497514},"targetPosition":{"x":27883.414597526902,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a819c354-b152-4946-a403-62a8cf0f650f","savedPayload":{"position":{"x":27883.414597526902,"y":15335.180636110756},"targetPosition":{"x":27613.658806793865,"y":16341.922952762367},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"79bf7438-f77a-4c8d-a450-67027d5fd8bc","savedPayload":{"position":{"x":27613.658806793865,"y":16341.922952762367},"targetPosition":{"x":28350.64533271244,"y":17078.90947868094},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"EMA Base 11","code":"{\n\"codeName\": \"EMA-Base11\",\n\"banner\": \"Moving-Average\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"EMA 11 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema11","id":"41b049dc-e0da-471f-82f7-5f2c1eb61f90","savedPayload":{"position":{"x":30147.048147013847,"y":15432.051266622468},"targetPosition":{"x":29173.00568076759,"y":15802.914520019141},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c961312c-6bb2-4b38-a142-9f03d0b3eba4","savedPayload":{"position":{"x":29173.00568076759,"y":15802.914520019141},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 11 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema11","id":"55db8355-8155-4c70-85b5-4a629aeefa45","savedPayload":{"position":{"x":30238.804171197095,"y":16603.011959557065},"targetPosition":{"x":29218.883692859214,"y":16388.39486648644},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"750547e8-4963-4008-aa2b-8edc565afd79","savedPayload":{"position":{"x":29218.883692859214,"y":16388.39486648644},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 22 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema22","id":"bda3c179-331d-444e-9b1e-e3cfd1a26bb7","savedPayload":{"position":{"x":29682.925104463262,"y":17637.69383121565},"targetPosition":{"x":28940.944159492297,"y":16905.735802315732},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8ac0329a-2d0d-483f-a047-305198817a24","savedPayload":{"position":{"x":28940.944159492297,"y":16905.735802315732},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 22 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema22","id":"08508d15-658f-40da-a580-76a5b512414d","savedPayload":{"position":{"x":28655.898622846536,"y":18207.59269805938},"targetPosition":{"x":28427.430918683935,"y":17190.685235737597},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"adc1fb05-93dd-4a32-98dd-30577e4a68de","savedPayload":{"position":{"x":28427.430918683935,"y":17190.685235737597},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 33 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema33","id":"bceee9f7-3824-498c-beb1-866463df7a7e","savedPayload":{"position":{"x":27483.798377637875,"y":18131.76969657834},"targetPosition":{"x":27841.380796079604,"y":17152.773734997078},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"25fd3a99-95c4-4514-b7f3-d195c9ee3189","savedPayload":{"position":{"x":27841.380796079604,"y":17152.773734997078},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 33 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema33","id":"6c2cc6ab-b520-4d47-8165-db2abfd53ed8","savedPayload":{"position":{"x":26538.757915026443,"y":17434.29809400304},"targetPosition":{"x":27368.860564773888,"y":16804.037933709427},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0fe0c9b1-3079-499b-ad49-1b13e1e0af82","savedPayload":{"position":{"x":27368.860564773888,"y":16804.037933709427},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 55 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema55","id":"5df6eee6-f3a0-406f-be6c-2e6bce745054","savedPayload":{"position":{"x":26120.820904554537,"y":16336.620196052278},"targetPosition":{"x":27159.892059537935,"y":16255.198984734046},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"27b05f8b-84e9-4ab2-944d-46b10c2a978c","savedPayload":{"position":{"x":27159.892059537935,"y":16255.198984734046},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 55 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema55","id":"508f7fdf-3889-4b3f-bec4-9ee071f7aa21","savedPayload":{"position":{"x":26362.67939404519,"y":15187.240979504015},"targetPosition":{"x":27280.82130428326,"y":15680.509376459915},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1eeef11e-809a-4e49-8b3b-48a955f42ce3","savedPayload":{"position":{"x":27280.82130428326,"y":15680.509376459915},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 111 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema111","id":"b9f04b17-af03-4983-8266-0e63f8f3271c","savedPayload":{"position":{"x":27187.545021975544,"y":14351.080224486857},"targetPosition":{"x":27693.25411824844,"y":15262.428998951336},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cb15539c-b3a0-4641-9417-861c48bd62ac","savedPayload":{"position":{"x":27693.25411824844,"y":15262.428998951336},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 11 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema111","id":"abc69976-c994-40d1-a923-fd64ded26938","savedPayload":{"position":{"x":28333.52877969972,"y":14093.613062689175},"targetPosition":{"x":28266.245997110527,"y":15133.695418052495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"afa38489-5525-4f32-b766-cfe85d683a65","savedPayload":{"position":{"x":28266.245997110527,"y":15133.695418052495},"targetPosition":{"x":28198.963214521333,"y":16173.777773415815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"573429d9-05d9-41ea-9fac-5eaf95fcc06c","savedPayload":{"position":{"x":28198.963214521333,"y":16173.777773415815},"targetPosition":{"x":28817.876067897974,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"SMA 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"02c6627c-956c-4022-a3f9-a132627d02d8","savedPayload":{"position":{"x":27854.290991132995,"y":17457.206836158985},"targetPosition":{"x":27815.31978182934,"y":16415.679340786697},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"48f091c0-5895-4615-a56d-f779d35b60c6","savedPayload":{"position":{"x":27815.31978182934,"y":16415.679340786697},"targetPosition":{"x":27776.348572525687,"y":15374.151845414412},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"1143164d-0f70-46f0-80fe-6107db66ce29","savedPayload":{"position":{"x":27249.697219348818,"y":16286.33874699165},"targetPosition":{"x":27776.348572525687,"y":15374.151845414412},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 11 End","id":"750547e8-4963-4008-aa2b-8edc565afd79"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"31fd051d-973f-4d8c-b9d3-ac1c13621ab0","savedPayload":{"position":{"x":27256.562188290533,"y":14473.855418836603},"targetPosition":{"x":27776.348572525687,"y":15374.151845414412},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 11 Begin","id":"c961312c-6bb2-4b38-a142-9f03d0b3eba4"}}}],"id":"01f54960-957d-490a-91c7-cfd01f2bf806","savedPayload":{"position":{"x":27776.348572525687,"y":15374.151845414412},"targetPosition":{"x":28817.876067897974,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"76712d22-b3a8-490e-a964-b1f51641d472","savedPayload":{"position":{"x":26558.023709492045,"y":15904.881182066641},"targetPosition":{"x":27348.02454366324,"y":15225.030492003101},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"61487dfb-c30e-41b5-b3bb-a52bbe476c22","savedPayload":{"position":{"x":27348.02454366324,"y":15225.030492003101},"targetPosition":{"x":28138.025377834434,"y":14545.179801939561},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"bd3f3676-7a98-43ca-b661-395e53cc2d9c","savedPayload":{"position":{"x":27617.399775952275,"y":15446.929796120588},"targetPosition":{"x":28138.025377834434,"y":14545.179801939561},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 22 End","id":"adc1fb05-93dd-4a32-98dd-30577e4a68de"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"6a2c8c60-7c69-4372-bc67-b5bac89eb8a5","savedPayload":{"position":{"x":27611.84533811909,"y":13633.809239223972},"targetPosition":{"x":28138.025377834434,"y":14545.179801939561},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 22 Begin","id":"8ac0329a-2d0d-483f-a047-305198817a24"}}}],"id":"c9043797-16f8-4dc7-85c0-bd9d995dcec1","savedPayload":{"position":{"x":28138.025377834434,"y":14545.179801939561},"targetPosition":{"x":28817.876067897974,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"9ee24b06-2b0b-472a-9272-23f52d3ddc05","savedPayload":{"position":{"x":26963.47134631794,"y":13923.559397096216},"targetPosition":{"x":27987.55748275286,"y":14117.326948386024},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"12acb041-6cf9-4cab-bc99-7d3413860838","savedPayload":{"position":{"x":27987.55748275286,"y":14117.326948386024},"targetPosition":{"x":29011.643619187784,"y":14311.094499675832},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e5ec4acb-cef0-48cc-8eb4-432547dc0541","savedPayload":{"position":{"x":28486.438340804645,"y":15220.776726238786},"targetPosition":{"x":29011.643619187784,"y":14311.094499675832},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 33 End","id":"0fe0c9b1-3079-499b-ad49-1b13e1e0af82"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"46dfb895-487c-4a14-a8f4-e39f77a98f34","savedPayload":{"position":{"x":28487.646550239784,"y":13403.504953240727},"targetPosition":{"x":29011.643619187784,"y":14311.094499675832},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 33 Begin","id":"25fd3a99-95c4-4514-b7f3-d195c9ee3189"}}}],"id":"d6e73bfe-e6af-4fff-b482-df4889701cf7","savedPayload":{"position":{"x":29011.643619187784,"y":14311.094499675832},"targetPosition":{"x":28817.876067897974,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"cb42e26b-ace9-47f5-a272-15645af64e39","savedPayload":{"position":{"x":28765.32356145623,"y":13005.217196929365},"targetPosition":{"x":29252.337251869205,"y":13926.692071313571},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"cd8235f2-8641-4f92-9029-d30375e43f1f","savedPayload":{"position":{"x":29252.337251869205,"y":13926.692071313571},"targetPosition":{"x":29739.35094228218,"y":14848.166945697778},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"fff3b40f-45a5-424f-bbe3-b62c4811423d","savedPayload":{"position":{"x":29211.71482374061,"y":15762.059510920215},"targetPosition":{"x":29739.35094228218,"y":14848.166945697778},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 55 End","id":"1eeef11e-809a-4e49-8b3b-48a955f42ce3"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"e644af79-d267-4c8b-bebd-2d919eb5efd9","savedPayload":{"position":{"x":29217.694492320134,"y":13944.6314702675},"targetPosition":{"x":29739.35094228218,"y":14848.166945697778},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 55 Begin","id":"27b05f8b-84e9-4ab2-944d-46b10c2a978c"}}}],"id":"001084ac-f732-465a-aea1-bb8c63770971","savedPayload":{"position":{"x":29739.35094228218,"y":14848.166945697778},"targetPosition":{"x":28817.876067897974,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 20","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GREEN\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"5d5e3413-6b2a-49e0-8b7e-14441b448203","savedPayload":{"position":{"x":30606.748885820925,"y":13841.384989058697},"targetPosition":{"x":30189.958888062156,"y":14796.677811464111},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"d1c5bddf-9fc3-47b8-b026-a0fd18fc7efd","savedPayload":{"position":{"x":30189.958888062156,"y":14796.677811464111},"targetPosition":{"x":29773.168890303386,"y":15751.970633869525},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"2aed0f5f-77bd-4297-a131-77c0c08914d5","savedPayload":{"position":{"x":29248.17932204486,"y":16661.279239536943},"targetPosition":{"x":29773.168890303386,"y":15751.970633869525},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 11 End","id":"afa38489-5525-4f32-b766-cfe85d683a65"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"dff4ca7d-4499-4a8a-834b-7315e9e784e4","savedPayload":{"position":{"x":29257.6046798074,"y":14858.987226726336},"targetPosition":{"x":29773.168890303386,"y":15751.970633869525},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 111 Begin","id":"cb15539c-b3a0-4641-9417-861c48bd62ac"}}}],"id":"47ab5422-37ad-4070-a2eb-248bbe9b007f","savedPayload":{"position":{"x":29773.168890303386,"y":15751.970633869525},"targetPosition":{"x":28817.876067897974,"y":15335.180636110756},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"951aba38-5a6a-43df-8f70-f311d621c98d","savedPayload":{"position":{"x":28817.876067897974,"y":15335.180636110756},"targetPosition":{"x":29087.63185863101,"y":16341.922952762367},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"EMA 11","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"EMA 11","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema11","id":"ee21f97e-daa4-45b8-bebd-5c53d872534b","savedPayload":{"position":{"x":29554.862593816546,"y":14598.19411019218},"targetPosition":{"x":29824.618384549583,"y":15604.936426843791},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a96b4bae-0ce3-4dc6-88df-af120452af6b","savedPayload":{"position":{"x":29824.618384549583,"y":15604.936426843791},"targetPosition":{"x":30094.37417528262,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 22","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema22","id":"583909a2-6932-4bfa-952d-a0c81123cc3f","savedPayload":{"position":{"x":31568.347227119764,"y":15137.705691658255},"targetPosition":{"x":30831.360701201193,"y":15874.692217576829},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2a2f4d65-a013-4c73-b851-1c9ac3dceb9f","savedPayload":{"position":{"x":30831.360701201193,"y":15874.692217576829},"targetPosition":{"x":30094.37417528262,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 33","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema33","id":"067efa03-229a-49e1-9dff-579ead9e6603","savedPayload":{"position":{"x":32107.85880858584,"y":17151.190324961477},"targetPosition":{"x":31101.11649193423,"y":16881.43453422844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a51bde01-b164-49bd-99fc-d048468e4cc0","savedPayload":{"position":{"x":31101.11649193423,"y":16881.43453422844},"targetPosition":{"x":30094.37417528262,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 55","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema55","id":"4663f0f2-cf79-4a80-b4c0-33c41bca9d88","savedPayload":{"position":{"x":30633.885756748696,"y":18625.16337679862},"targetPosition":{"x":30364.12996601566,"y":17618.42106014701},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"782092b0-9e6c-4de0-aec6-0d6b47fac8b4","savedPayload":{"position":{"x":30364.12996601566,"y":17618.42106014701},"targetPosition":{"x":30094.37417528262,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 111","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema111","id":"8064c04e-1499-4c01-b056-aac2a1aecc01","savedPayload":{"position":{"x":28620.40112344547,"y":18085.651795332546},"targetPosition":{"x":29357.387649364046,"y":17348.665269413974},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2c4a358f-23b6-433c-98de-623caad85051","savedPayload":{"position":{"x":29357.387649364046,"y":17348.665269413974},"targetPosition":{"x":30094.37417528262,"y":16611.678743495402},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"efc451e7-a5c0-4377-877c-a6b1a60a6604","savedPayload":{"position":{"x":30094.37417528262,"y":16611.678743495402},"targetPosition":{"x":29087.63185863101,"y":16341.922952762367},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"cef26be6-07a9-415a-8439-99d06b1e1cfb","savedPayload":{"position":{"x":29087.63185863101,"y":16341.922952762367},"targetPosition":{"x":28350.64533271244,"y":17078.90947868094},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Plotter Module","name":"EMA Base 7","code":"{\n\"codeName\": \"EMA-Base7\",\n\"banner\": \"Moving-Average\"\n}","shapes":{"type":"Shapes","name":"New Shapes","chartPoints":[{"type":"Chart Points","name":"New Chart Points","points":[{"type":"Point","name":"EMA 7 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema7","id":"d41ccc81-8740-46e9-9ed3-9ffc1df75ee0","savedPayload":{"position":{"x":29997.503544770912,"y":18875.312292982348},"targetPosition":{"x":29626.64029137424,"y":17901.26982673609},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d2bb8ca1-5fcc-4286-aba3-1d307d4f7168","savedPayload":{"position":{"x":29626.64029137424,"y":17901.26982673609},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 7 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema7","id":"91431058-cccd-40c3-bb98-dd9484a0807d","savedPayload":{"position":{"x":28826.542851836315,"y":18967.068317165595},"targetPosition":{"x":29041.15994490694,"y":17947.147838827714},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8ea750de-2b74-41ea-9897-637291621ae9","savedPayload":{"position":{"x":29041.15994490694,"y":17947.147838827714},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 14 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema14","id":"753c8686-38ea-4fc2-9343-3598039aa890","savedPayload":{"position":{"x":27791.86098017773,"y":18411.189250431762},"targetPosition":{"x":28523.819009077648,"y":17669.208305460797},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"77265bb0-8ba2-4c0b-a788-970cd44ddcc9","savedPayload":{"position":{"x":28523.819009077648,"y":17669.208305460797},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 14 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema14","id":"c64e3670-c41d-44d0-bc7a-05c8f0a841b6","savedPayload":{"position":{"x":27221.962113334,"y":17384.162768815037},"targetPosition":{"x":28238.869575655783,"y":17155.695064652435},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cd04ee65-bd26-4fbc-abed-76c6fc5c964b","savedPayload":{"position":{"x":28238.869575655783,"y":17155.695064652435},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 21 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema21","id":"be27efec-c4ed-43ec-925a-f7d862f999c1","savedPayload":{"position":{"x":27297.78511481504,"y":16212.062523606377},"targetPosition":{"x":28276.781076396303,"y":16569.644942048104},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2f13bba0-f9c2-435f-ad66-8ad73082c502","savedPayload":{"position":{"x":28276.781076396303,"y":16569.644942048104},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 21 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema21","id":"e6af4547-c256-4f33-95fc-9d9f3cd5d111","savedPayload":{"position":{"x":27995.25671739034,"y":15267.022060994947},"targetPosition":{"x":28625.516877683953,"y":16097.12471074239},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a5e04840-b217-44cb-9f8f-2629bf4d1f6e","savedPayload":{"position":{"x":28625.516877683953,"y":16097.12471074239},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 28 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema28","id":"a42e77a3-9107-4c33-853d-4ed8966f339d","savedPayload":{"position":{"x":29092.934615341106,"y":14849.085050523041},"targetPosition":{"x":29174.355826659335,"y":15888.156205506437},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0d436286-6b79-4dca-a2f3-b7bd3b9a5671","savedPayload":{"position":{"x":29174.355826659335,"y":15888.156205506437},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 28 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema28","id":"0e7f0922-d9b4-4ea3-9f5f-57ad9d4c9328","savedPayload":{"position":{"x":30242.313831889365,"y":15090.94354001369},"targetPosition":{"x":29749.045434933465,"y":16009.085450251761},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f50599e9-c04f-4b07-aca0-f276dd08ea94","savedPayload":{"position":{"x":29749.045434933465,"y":16009.085450251761},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 35 Begin","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.begin\ny = record.previous.ema35","id":"db011cc5-705a-444a-9f7a-159466b19247","savedPayload":{"position":{"x":31078.47458690652,"y":15915.809167944042},"targetPosition":{"x":30167.125812442042,"y":16421.51826421694},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d7ac980a-16ab-4077-b3f1-05cf1f4c3a39","savedPayload":{"position":{"x":30167.125812442042,"y":16421.51826421694},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Point","name":"EMA 35 End","pointFormula":{"type":"Point Formula","name":"New Point Formula","code":"x = record.end\ny = record.ema35","id":"6b3dc821-e0e9-4c09-965b-c8600bd3e9d5","savedPayload":{"position":{"x":31335.941748704205,"y":17061.792925668222},"targetPosition":{"x":30295.859393340885,"y":16994.510143079027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8992ec1b-4e8c-4f47-a08e-b6e18a0d9c31","savedPayload":{"position":{"x":30295.859393340885,"y":16994.510143079027},"targetPosition":{"x":29255.777037977565,"y":16927.227360489833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"5a4d5b2f-63dc-403c-a545-6025652d9e81","savedPayload":{"position":{"x":29255.777037977565,"y":16927.227360489833},"targetPosition":{"x":30094.374175282624,"y":17546.140213866474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"polygons":[{"type":"Polygon","name":"SMA 7","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"496c35a4-5cf7-4def-97bc-ce71a6fc45f7","savedPayload":{"position":{"x":27972.347975234392,"y":16582.555137101495},"targetPosition":{"x":29013.87547060668,"y":16543.58392779784},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"671a56e0-b5eb-4833-aa19-3dca720b5b0e","savedPayload":{"position":{"x":29013.87547060668,"y":16543.58392779784},"targetPosition":{"x":30055.402965978967,"y":16504.612718494187},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"b0be4203-a40d-4ee7-83e0-d1ac87f0bd31","savedPayload":{"position":{"x":29535.508889034812,"y":17405.095674315588},"targetPosition":{"x":30055.402965978967,"y":16504.612718494187},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 7 End","id":"8ea750de-2b74-41ea-9897-637291621ae9"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"afe09e1e-c8fd-4fc7-8f09-324ee7d7d012","savedPayload":{"position":{"x":29529.615637316485,"y":15593.922351274854},"targetPosition":{"x":30055.402965978967,"y":16504.612718494187},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 7 Begin","id":"d2bb8ca1-5fcc-4286-aba3-1d307d4f7168"}}}],"id":"45946a01-a9fb-4ea8-bb82-ef6088fad08e","savedPayload":{"position":{"x":30055.402965978967,"y":16504.612718494187},"targetPosition":{"x":30094.374175282624,"y":17546.140213866474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 14","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.DARK_TURQUOISE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"9c0f7800-bf53-4a63-94ba-fccbec15d0ea","savedPayload":{"position":{"x":29524.67362932674,"y":15286.287855460545},"targetPosition":{"x":30204.52431939028,"y":16076.28868963174},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"651877b1-61a2-48a2-8fc4-86e809f89d42","savedPayload":{"position":{"x":30204.52431939028,"y":16076.28868963174},"targetPosition":{"x":30884.37500945382,"y":16866.289523802934},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"72f9e17d-9c36-408d-9a2e-13da010f4d97","savedPayload":{"position":{"x":30353.59715295287,"y":17785.623738795086},"targetPosition":{"x":30884.37500945382,"y":16866.289523802934},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 14 End","id":"cd04ee65-bd26-4fbc-abed-76c6fc5c964b"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"5a24fd6f-de93-4fcd-94f7-1f7fe268e8be","savedPayload":{"position":{"x":30360.08816657868,"y":15958.198074203316},"targetPosition":{"x":30884.37500945382,"y":16866.289523802934},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 14 Begin","id":"77265bb0-8ba2-4c0b-a788-970cd44ddcc9"}}}],"id":"b1689975-de58-4f84-a802-c6f13e885dc6","savedPayload":{"position":{"x":30884.37500945382,"y":16866.289523802934},"targetPosition":{"x":30094.374175282624,"y":17546.140213866474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 21","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.MANGANESE_PURPLE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"14146df5-5a4a-46b7-9db5-4957b8445915","savedPayload":{"position":{"x":31505.995414297166,"y":15691.735492286438},"targetPosition":{"x":31312.227863007356,"y":16715.82162872136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"2552b9ce-3620-4245-9803-d55ad53969da","savedPayload":{"position":{"x":31312.227863007356,"y":16715.82162872136},"targetPosition":{"x":31118.460311717547,"y":17739.907765156284},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"803ffc35-a2e2-426d-a8d2-7c770e5d0a6a","savedPayload":{"position":{"x":30599.200291303496,"y":18639.29250285267},"targetPosition":{"x":31118.460311717547,"y":17739.907765156284},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 21 Begin","id":"2f13bba0-f9c2-435f-ad66-8ad73082c502"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"a0449481-80c2-4160-adb3-49d4327bf921","savedPayload":{"position":{"x":30597.693304771292,"y":16837.912850219796},"targetPosition":{"x":31118.460311717547,"y":17739.907765156284},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 21 End","id":"a5e04840-b217-44cb-9f8f-2629bf4d1f6e"}}}],"id":"15f599b3-7620-4a0c-b9e0-29830383cd36","savedPayload":{"position":{"x":31118.460311717547,"y":17739.907765156284},"targetPosition":{"x":30094.374175282624,"y":17546.140213866474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 28","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GOLDEN_ORANGE\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"e4a06d7f-ecad-400a-acca-f3c340642354","savedPayload":{"position":{"x":32424.337614464013,"y":17493.58770742473},"targetPosition":{"x":31502.862740079807,"y":17980.601397837705},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"65f345ac-2766-411d-af4f-9390e525667c","savedPayload":{"position":{"x":31502.862740079807,"y":17980.601397837705},"targetPosition":{"x":30581.3878656956,"y":18467.61508825068},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"ca1f7537-47e7-4c48-a056-fedd8820a1de","savedPayload":{"position":{"x":30064.80043079471,"y":19362.370772050705},"targetPosition":{"x":30581.3878656956,"y":18467.61508825068},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 28 Begin","id":"0d436286-6b79-4dca-a2f3-b7bd3b9a5671"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"80c45995-463a-442a-a6d8-92cc9971a8b9","savedPayload":{"position":{"x":30065.36155335251,"y":17573.831297230045},"targetPosition":{"x":30581.3878656956,"y":18467.61508825068},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 28 End","id":"f50599e9-c04f-4b07-aca0-f276dd08ea94"}}}],"id":"e3c75e9c-a5ec-4755-9a86-909db1b690ca","savedPayload":{"position":{"x":30581.3878656956,"y":18467.61508825068},"targetPosition":{"x":30094.374175282624,"y":17546.140213866474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Polygon","name":"SMA 35","polygonBorder":{"type":"Polygon Border","name":"New Polygon Border","style":{"type":"Style","name":"New Style","code":"{\n \"default\": {\n \"opacity\": 0.5,\n \"lineWidth\": 0.5,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.GREEN\"\n },\n \"atMousePosition\": {\n \"opacity\": 0.75,\n \"lineWidth\": 2,\n \"lineDash\": [0,0],\n \"paletteColor\": \"UI_COLOR.RUSTED_RED\"\n }\n}","id":"9d05ca49-9de9-4c97-b4da-891614ed84a5","savedPayload":{"position":{"x":31675.133340169545,"y":18918.22303403066},"targetPosition":{"x":30632.87699992927,"y":18918.22303403066},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"styleConditions":[],"id":"56422e5e-ccaa-4c0c-b5a1-79e62ebc1e74","savedPayload":{"position":{"x":30632.87699992927,"y":18918.22303403066},"targetPosition":{"x":29677.584177523855,"y":18501.43303627189},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"polygonVertexes":[{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"155db5be-8214-4154-a918-ab9469587cc7","savedPayload":{"position":{"x":29156.05479920585,"y":19404.748417158487},"targetPosition":{"x":29677.584177523855,"y":18501.43303627189},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 35 Begin","id":"d7ac980a-16ab-4077-b3f1-05cf1f4c3a39"}}},{"type":"Polygon Vertex","name":"New Polygon Vertex","id":"ee71f62a-adc7-4411-aeb1-54ca5712ce34","savedPayload":{"position":{"x":29149.618765397783,"y":17586.97011783049},"targetPosition":{"x":29677.584177523855,"y":18501.43303627189},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Point","name":"EMA 35 End","id":"8992ec1b-4e8c-4f47-a08e-b6e18a0d9c31"}}}],"id":"3511cc8f-eb39-4dd0-8043-9f06a8faa698","savedPayload":{"position":{"x":29677.584177523855,"y":18501.43303627189},"targetPosition":{"x":30094.374175282624,"y":17546.140213866474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"8e7cd7e8-5c59-4cc2-8d9e-ea86d7554205","savedPayload":{"position":{"x":30094.374175282624,"y":17546.140213866474},"targetPosition":{"x":29087.631858631015,"y":17815.89600459951},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"panels":[{"type":"Plotter Panel","name":"EMA Base 7","code":"{ \n\"codeName\": \"\"\n}","panelData":[{"type":"Panel Data","name":"EMA 7","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema7","id":"9d9efe26-4895-475c-ac3c-149f0331c6f7","savedPayload":{"position":{"x":30831.360701201196,"y":18283.126739785046},"targetPosition":{"x":29824.618384549587,"y":18552.882530518084},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"aeaebf9b-54bf-41f3-a833-b0e4fcc55c00","savedPayload":{"position":{"x":29824.618384549587,"y":18552.882530518084},"targetPosition":{"x":28817.876067897978,"y":18822.63832125112},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 14","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema14","id":"7c922a65-b8a6-4ebf-a964-af3851b30367","savedPayload":{"position":{"x":30597.11893405683,"y":19559.624847169693},"targetPosition":{"x":29554.862593816553,"y":19559.624847169693},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f749c1c5-25f9-4c3f-af87-c8df30a177e0","savedPayload":{"position":{"x":29554.862593816553,"y":19559.624847169693},"targetPosition":{"x":28817.876067897978,"y":18822.63832125112},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 21","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema21","id":"f5a9c1c1-9331-4492-a263-1f69ff44711a","savedPayload":{"position":{"x":27131.46988439914,"y":20047.884133054242},"targetPosition":{"x":27974.67297614856,"y":19435.26122715268},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"92f7a1a7-bd42-403f-9d0e-2eb907e8abea","savedPayload":{"position":{"x":27974.67297614856,"y":19435.26122715268},"targetPosition":{"x":28817.876067897978,"y":18822.63832125112},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 28","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema28","id":"0fcdb422-d014-4dc8-927a-5484c5d7c34b","savedPayload":{"position":{"x":26804.39143459476,"y":19362.149902717196},"targetPosition":{"x":27811.13375124637,"y":19092.39411198416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4c51ef79-f3dd-4f24-ad22-4e38f42f3ff2","savedPayload":{"position":{"x":27811.13375124637,"y":19092.39411198416},"targetPosition":{"x":28817.876067897978,"y":18822.63832125112},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Panel Data","name":"EMA 35","code":"{\n \"valueDecimals\": 2\n}","dataFormula":{"type":"Data Formula","name":"New Data Formula","code":"record.ema35","id":"c23e6dd3-f9d5-498f-a01c-c951b11cbdf6","savedPayload":{"position":{"x":27343.903016060834,"y":17348.66526941397},"targetPosition":{"x":28080.889541979406,"y":18085.651795332546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8cdd88af-871c-4f1b-966b-cf9d00483caa","savedPayload":{"position":{"x":28080.889541979406,"y":18085.651795332546},"targetPosition":{"x":28817.876067897978,"y":18822.63832125112},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"481ef93a-bef5-4fe3-b87b-f8032a63c69f","savedPayload":{"position":{"x":28817.876067897978,"y":18822.63832125112},"targetPosition":{"x":29087.631858631015,"y":17815.89600459951},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a52a9764-3ef2-44e6-82bd-e6538058119d","savedPayload":{"position":{"x":29087.631858631015,"y":17815.89600459951},"targetPosition":{"x":28350.64533271244,"y":17078.90947868094},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"6cb2807d-22bb-4f90-b4d8-c84cf84a0d5c","savedPayload":{"position":{"x":28350.64533271244,"y":17078.90947868094},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"a"}}}],"id":"9cfffc48-486a-48cb-b641-98c82e314d3a","savedPayload":{"position":{"x":27613.65880679387,"y":17815.896004599515},"targetPosition":{"x":27613.65880679387,"y":17815.896004599515},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":"s"}}},{"type":"Crypto Ecosystem","name":"My Crypto Ecosystem","cryptoExchanges":{"type":"Crypto Exchanges","name":"New Crypto Exchanges","exchanges":[{"type":"Crypto Exchange","name":"Poloniex","code":"{ \n\"codeName\": \"Poloniex\"\n}","exchangeAssets":{"type":"Exchange Assets","name":"New Exchange Assets","assets":[{"type":"Asset","name":"Bitcoin","code":"{ \n\"codeName\": \"BTC\"\n}","id":"0c232a81-4c3d-4668-ae3e-cdd93819231e","savedPayload":{"position":{"x":27062.06836084158,"y":22991.14520625145},"targetPosition":{"x":27000.499494096326,"y":22893.15891615083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Tether","code":"{ \n\"codeName\": \"USDT\"\n}","id":"4b921ee0-6a74-47a1-8027-7223dcefb12c","savedPayload":{"position":{"x":26962.27828817308,"y":23002.388847111855},"targetPosition":{"x":27000.499494096326,"y":22893.15891615083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ether","code":"{ \n\"codeName\": \"ETH\"\n}","id":"534af20f-cc39-46a8-847c-d78d59aafcb0","savedPayload":{"position":{"x":26891.2695631353,"y":22931.380122074075},"targetPosition":{"x":27000.499494096326,"y":22893.15891615083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Monero","code":"{ \n\"codeName\": \"XMR\"\n}","id":"f7c3766b-8b5f-466e-abc7-3d0e1efeacd6","savedPayload":{"position":{"x":26902.513203995706,"y":22831.59004940558},"targetPosition":{"x":27000.499494096326,"y":22893.15891615083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Stellar","code":"{ \n\"codeName\": \"XLM\"\n}","id":"ca1b7306-bbf9-42c1-8f71-e6820851b86d","savedPayload":{"position":{"x":26987.542519857936,"y":22778.162589172254},"targetPosition":{"x":27000.499494096326,"y":22893.15891615083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ethereum Classic","code":"{ \n\"codeName\": \"ETC\"\n}","id":"210ec456-911e-49ad-8ed5-deba22eace15","savedPayload":{"position":{"x":27082.32870159578,"y":22811.329708651378},"targetPosition":{"x":27000.499494096326,"y":22893.15891615083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"3bab42fe-22d0-49f5-a83b-6b6538d1440b","savedPayload":{"position":{"x":27000.499494096326,"y":22893.15891615083},"targetPosition":{"x":27437.463525490664,"y":22795.081888151453},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"exchangeMarkets":{"type":"Exchange Markets","name":"New Exchange Markets","markets":[{"type":"Market","name":"BTC/USDT","code":"{ \n\"baseAsset\": \"BTC/USDT\"\n}","baseAsset":{"type":"Market Base Asset","name":"Tether","id":"488c24af-d8b7-4c53-b05b-2e32939f7dab","savedPayload":{"position":{"x":26982.163178680796,"y":22272.605936674656},"targetPosition":{"x":27148.853022533167,"y":22433.175445104593},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Tether","id":"4b921ee0-6a74-47a1-8027-7223dcefb12c"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Bitcoin","id":"7a0c1e41-58e2-4b01-a0b3-65ac24429592","savedPayload":{"position":{"x":27029.401467533888,"y":22234.934658377973},"targetPosition":{"x":27148.853022533167,"y":22433.175445104593},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"0c232a81-4c3d-4668-ae3e-cdd93819231e"}}},"id":"d4098b73-7e25-434e-8fd9-342393cbb400","savedPayload":{"position":{"x":27148.853022533167,"y":22433.175445104593},"targetPosition":{"x":27293.158274323945,"y":22614.12866876876},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b16004a9-18c8-43f2-bcde-51162bd591f4","savedPayload":{"position":{"x":27293.158274323945,"y":22614.12866876876},"targetPosition":{"x":27437.463525490664,"y":22795.081888151453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":1},"uiObject":{}}},"id":"1298f3db-a526-48c3-af5e-f27a984f6c40","savedPayload":{"position":{"x":27437.463339516697,"y":22795.08180946362},"targetPosition":{"x":28014.01564388284,"y":23518.2348844148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},{"type":"Crypto Exchange","name":"Kraken","code":"{ \n\"codeName\": \"Kraken\"\n}","exchangeAssets":{"type":"Exchange Assets","name":"New Exchange Assets","assets":[{"type":"Asset","name":"Bitcoin","code":"{ \n\"codeName\": \"BTC\"\n}","id":"ce20aa9e-765b-42be-96ab-4120acc82706","savedPayload":{"position":{"x":27935.16514492206,"y":22333.47626713653},"targetPosition":{"x":27973.386459434558,"y":22224.246175189735},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Tether","code":"{ \n\"codeName\": \"USDT\"\n}","id":"ed3fe790-e3b2-4337-8ddf-5b435fce6fbe","savedPayload":{"position":{"x":27864.17476239036,"y":22262.440039784895},"targetPosition":{"x":27973.40479057096,"y":22224.218690185495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ether","code":"{ \n\"codeName\": \"ETH\"\n}","id":"74777dca-b325-42e6-ab7e-26524c929651","savedPayload":{"position":{"x":27875.418403250766,"y":22162.6499671164},"targetPosition":{"x":27973.40479057096,"y":22224.218690185495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Monero","code":"{ \n\"codeName\": \"XMR\"\n}","id":"25555ed6-3c5c-4ade-85f6-a64c3e371bb1","savedPayload":{"position":{"x":27960.447719112995,"y":22109.222506883074},"targetPosition":{"x":27973.40479057096,"y":22224.218690185495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Stellar","code":"{ \n\"codeName\": \"XLM\"\n}","id":"e26e4a14-2213-4178-a7c0-955cc1ae621c","savedPayload":{"position":{"x":28055.23390085084,"y":22142.389626362197},"targetPosition":{"x":27973.40479057096,"y":22224.218690185495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ethereum Classic","code":"{ \n\"codeName\": \"ETC\"\n}","id":"a99904cd-ab9f-40fa-a3d9-587b76e99905","savedPayload":{"position":{"x":28088.401020329962,"y":22237.17580810004},"targetPosition":{"x":27973.40479057096,"y":22224.218690185495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"821b3105-448a-4d79-a74f-0d84893cf56e","savedPayload":{"position":{"x":27973.379808148107,"y":22224.52440915255},"targetPosition":{"x":28219.655380184417,"y":22616.46947995193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"exchangeMarkets":{"type":"Exchange Markets","name":"New Exchange Markets","markets":[{"type":"Market","name":"ETH/BTC","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Ether","id":"ce6df5a7-a674-4238-9a6f-fe084bcbe82a","savedPayload":{"position":{"x":27988.468433414553,"y":22258.21936638514},"targetPosition":{"x":28202.943459536196,"y":22169.380901775232},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Ether","id":"74777dca-b325-42e6-ab7e-26524c929651"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Bitcoin","id":"8f3aaa50-9d72-48af-83cf-d49f381b7d08","savedPayload":{"position":{"x":27970.94746978569,"y":22169.380901775232},"targetPosition":{"x":28202.943459536196,"y":22169.380901775232},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"ce20aa9e-765b-42be-96ab-4120acc82706"}}},"id":"f43663e0-c14d-4fb9-9655-25dff3f938c7","savedPayload":{"position":{"x":28202.943459536196,"y":22169.380901775232},"targetPosition":{"x":28271.16404894103,"y":22390.54610797115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Market","name":"BTC/USDT","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Bitcoin","id":"5fd7461d-eda6-46f2-814a-e7ae0cf3c2a2","savedPayload":{"position":{"x":28214.59648712599,"y":22309.52132065442},"targetPosition":{"x":28428.5885262942,"y":22220.882915791073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"ce20aa9e-765b-42be-96ab-4120acc82706"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Tether","id":"0d1a00db-5ddd-4b34-a920-b68a9dd4f418","savedPayload":{"position":{"x":28200.139862292537,"y":22220.882915791073},"targetPosition":{"x":28428.5885262942,"y":22220.882915791073},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Tether","id":"ed3fe790-e3b2-4337-8ddf-5b435fce6fbe"}}},"id":"cbb2229a-28ab-4b24-bafd-56f58fc1e38e","savedPayload":{"position":{"x":28428.5885262942,"y":22220.882915791073},"targetPosition":{"x":28271.16404894103,"y":22390.54610797115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"id":"9826526e-8e01-4ab6-a5f3-1609a48dba34","savedPayload":{"position":{"x":28271.157289144965,"y":22390.82450279703},"targetPosition":{"x":28219.655380184417,"y":22616.46947995193},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":1},"uiObject":{}}},"id":"7f859060-6e77-4607-aadc-fdbd4fd785a5","savedPayload":{"position":{"x":28219.65548546836,"y":22616.469385155895},"targetPosition":{"x":28014.01564388284,"y":23518.2348844148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Crypto Exchange","name":"Bitfinex","code":"{\n \"codeName\": \"Bitfinex\",\n \"API\": [\n {\n \"method\": \"fetchTrades\",\n \"class\": \"bitfinex2\"\n }\n ]\n}","exchangeAssets":{"type":"Exchange Assets","name":"New Exchange Assets","assets":[{"type":"Asset","name":"Bitcoin","code":"{ \n\"codeName\": \"BTC\"\n}","id":"66046275-e148-4d18-8bbd-ad8dcd2ade99","savedPayload":{"position":{"x":28923.446577434144,"y":22592.320497638793},"targetPosition":{"x":28999.185671261053,"y":22680.54947334531},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Tether","code":"{ \n\"codeName\": \"USDT\"\n}","id":"27ae18d2-032b-41f8-bafc-a24fa4933194","savedPayload":{"position":{"x":29113.20664622071,"y":22658.747590505103},"targetPosition":{"x":28999.185671261053,"y":22680.54947334531},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"dc376d73-e403-43ce-9044-5ff5fcd6de4e","savedPayload":{"position":{"x":28999.185671261053,"y":22680.54947334531},"targetPosition":{"x":28846.764765815336,"y":23116.999725258083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"exchangeMarkets":{"type":"Exchange Markets","name":"New Exchange Markets","markets":[{"type":"Market","name":"BTC/USDT","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Bitcoin","id":"4d9d7d28-c531-41af-96bd-0fd83d3a958d","savedPayload":{"position":{"x":29456.080447902186,"y":22787.704468479384},"targetPosition":{"x":29263.118182711252,"y":22915.85206121817},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"66046275-e148-4d18-8bbd-ad8dcd2ade99"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Tether","id":"3c286c18-238f-446c-9d0c-f454ee45aaf1","savedPayload":{"position":{"x":29483.58785149798,"y":22844.776588175024},"targetPosition":{"x":29263.118182711252,"y":22915.85206121817},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Tether","id":"27ae18d2-032b-41f8-bafc-a24fa4933194"}}},"id":"b734f59f-f29c-450b-a6e4-6d2345a1082a","savedPayload":{"position":{"x":29263.118182711252,"y":22915.85206121817},"targetPosition":{"x":29054.93956400818,"y":23016.31404810811},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"dc6b67a9-30d0-4b69-9e10-dd6856e9dd4c","savedPayload":{"position":{"x":29054.93956400818,"y":23016.31404810811},"targetPosition":{"x":28846.764765815336,"y":23116.999725258083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":1},"uiObject":{}}},"id":"3ff59cad-42ae-4527-b060-4585cc4e7e1a","savedPayload":{"position":{"x":28846.764765815336,"y":23116.999725258083},"targetPosition":{"x":28014.01564388284,"y":23518.2348844148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Crypto Exchange","name":"Bitmex","code":"{\n \"codeName\": \"Bitmex\"\n}","exchangeAssets":{"type":"Exchange Assets","name":"New Exchange Assets","assets":[{"type":"Asset","name":"Bitcoin","code":"{ \n\"codeName\": \"BTC\"\n}","id":"5dcc2338-6347-4c17-a1cc-7dce7790b69e","savedPayload":{"position":{"x":29304.786407777858,"y":23652.812413517473},"targetPosition":{"x":29283.214617122227,"y":23766.637244581743},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"US Dollar","code":"{ \n\"codeName\": \"USD\"\n}","id":"1b6defee-80cf-402d-9c1b-36e8f7b993e2","savedPayload":{"position":{"x":29371.404745531436,"y":23842.219787174625},"targetPosition":{"x":29283.214617122227,"y":23766.637244581743},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"49ea825d-f7d2-4c41-803b-e454050a1e51","savedPayload":{"position":{"x":29283.214617122227,"y":23766.637244581743},"targetPosition":{"x":28846.917001423873,"y":23919.147756600378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"exchangeMarkets":{"type":"Exchange Markets","name":"New Exchange Markets","markets":[{"type":"Market","name":"BTC/USDT","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Bitcoin","id":"0dbd88b2-8ff2-43b2-9401-e4c2827d467b","savedPayload":{"position":{"x":29484.286681402715,"y":24190.315981042077},"targetPosition":{"x":29263.771831707556,"y":24119.389904155334},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"5dcc2338-6347-4c17-a1cc-7dce7790b69e"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"US Dollar","id":"297606e3-eff2-49c2-b0c3-1e408ecac868","savedPayload":{"position":{"x":29456.806607363684,"y":24247.39904886264},"targetPosition":{"x":29263.771831707556,"y":24119.389904155334},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"US Dollar","id":"1b6defee-80cf-402d-9c1b-36e8f7b993e2"}}},"id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b","savedPayload":{"position":{"x":29263.771831707556,"y":24119.389904155334},"targetPosition":{"x":29055.408394152582,"y":24019.216996467258},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"4ac812f4-ceef-409b-b698-31e82cacaf24","savedPayload":{"position":{"x":29055.408394152582,"y":24019.216996467258},"targetPosition":{"x":28846.917001423873,"y":23919.147756600378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":1},"uiObject":{}}},"id":"d57edcc8-cb45-4d6f-99e8-2c1f611691f6","savedPayload":{"position":{"x":28846.917001423873,"y":23919.147756600378},"targetPosition":{"x":28014.01564388284,"y":23518.2348844148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Crypto Exchange","name":"HitBTC","code":"{\n \"codeName\": \"HitBTC\",\n \"API\": [\n {\n \"method\": \"fetchTrades\",\n \"class\": \"hitbtc2\"\n }\n ]\n}","exchangeAssets":{"type":"Exchange Assets","name":"New Exchange Assets","assets":[{"type":"Asset","name":"Bitcoin","code":"{ \n\"codeName\": \"BTC\"\n}","id":"70be8065-7936-4dc2-b1ee-bfcec4f19cba","savedPayload":{"position":{"x":28598.761981067026,"y":24551.48500829746},"targetPosition":{"x":28611.719007251686,"y":24666.48144736945},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Tether","code":"{ \n\"codeName\": \"USDT\"\n}","id":"2b4ca7b9-2db8-4fbb-97ac-faf95ec775ab","savedPayload":{"position":{"x":28693.54816280487,"y":24584.652127776582},"targetPosition":{"x":28611.719007251686,"y":24666.48144736945},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ether","code":"{ \n\"codeName\": \"ETH\"\n}","id":"69204d74-e6c0-4ea2-a4e0-2796100fbbc9","savedPayload":{"position":{"x":28726.715282283993,"y":24679.438309514426},"targetPosition":{"x":28611.719007251686,"y":24666.48144736945},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Monero","code":"{ \n\"codeName\": \"XMR\"\n}","id":"27da6570-6ccd-4efb-a882-cd744c0579f3","savedPayload":{"position":{"x":28673.28782205067,"y":24764.467625376656},"targetPosition":{"x":28611.719007251686,"y":24666.48144736945},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Stellar","code":"{ \n\"codeName\": \"XLM\"\n}","id":"75339f18-5482-4302-9cf2-d0b1befc928c","savedPayload":{"position":{"x":28573.49774938217,"y":24775.71126623706},"targetPosition":{"x":28611.719007251686,"y":24666.48144736945},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ethereum Classic","code":"{ \n\"codeName\": \"ETC\"\n}","id":"05da99c0-f458-4799-9d14-df4139973233","savedPayload":{"position":{"x":28502.48902434439,"y":24704.70254119928},"targetPosition":{"x":28611.719007251686,"y":24666.48144736945},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"192d1d43-de1a-4ed6-9560-0b7cc3edd29b","savedPayload":{"position":{"x":28611.719007251686,"y":24666.48144736945},"targetPosition":{"x":28219.77389874882,"y":24420.20609308416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"exchangeMarkets":{"type":"Exchange Markets","name":"New Exchange Markets","markets":[{"type":"Market","name":"ETH/BTC","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Ether","id":"e34e52e9-7f72-4267-9c3c-cfbb06c485e9","savedPayload":{"position":{"x":28513.356408354262,"y":25039.23741035569},"targetPosition":{"x":28379.424175072985,"y":24850.477630520858},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Ether","id":"69204d74-e6c0-4ea2-a4e0-2796100fbbc9"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Bitcoin","id":"2f4837e9-6e5f-474d-85cb-a42dea3c0f36","savedPayload":{"position":{"x":28459.938121582287,"y":25067.469799213548},"targetPosition":{"x":28379.424175072985,"y":24850.477630520858},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"70be8065-7936-4dc2-b1ee-bfcec4f19cba"}}},"id":"751ae593-f933-4a44-97c5-50f02aed2ffa","savedPayload":{"position":{"x":28379.424175072985,"y":24850.477630520858},"targetPosition":{"x":28271.27586086505,"y":24645.851047146436},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Market","name":"BTC/USDT","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Bitcoin","id":"4153fe7c-0403-4053-a837-8307ee8f3573","savedPayload":{"position":{"x":28284.230457447164,"y":25107.573926952726},"targetPosition":{"x":28262.621693652185,"y":24877.13703469773},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"70be8065-7936-4dc2-b1ee-bfcec4f19cba"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Tether","id":"b6758271-06b6-4356-b767-c473bbca620a","savedPayload":{"position":{"x":28223.85266964706,"y":25105.31474952716},"targetPosition":{"x":28262.621693652185,"y":24877.13703469773},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Tether","id":"2b4ca7b9-2db8-4fbb-97ac-faf95ec775ab"}}},"id":"5c44fd86-9fb9-40ed-af02-78f93b420562","savedPayload":{"position":{"x":28262.621693652185,"y":24877.13703469773},"targetPosition":{"x":28271.27586086505,"y":24645.851047146436},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"id":"6e33c5a2-1b71-481b-a28a-b4d9cece0226","savedPayload":{"position":{"x":28271.27586086505,"y":24645.851047146436},"targetPosition":{"x":28219.77389874882,"y":24420.20609308416},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":1},"uiObject":{}}},"id":"f1b1fcc1-9839-4833-aefe-2e3bee456e04","savedPayload":{"position":{"x":28219.77395060305,"y":24420.20620635044},"targetPosition":{"x":28014.01564388284,"y":23518.2348844148},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Crypto Exchange","name":"Binance","code":"{\n \"codeName\": \"Binance\",\n \"API\": [\n {\n \"method\": \"fetchTrades\",\n \"fetchType\": \"by Id\",\n \"fetchTradesMethod\": \"publicGetHistoricalTrades\",\n \"firstId\": \"f\"\n }\n ]\n}","exchangeAssets":{"type":"Exchange Assets","name":"New Exchange Assets","assets":[{"type":"Asset","name":"Bitcoin","code":"{ \n\"codeName\": \"BTC\"\n}","id":"b2814a75-fd57-45c4-a3cb-af276793e573","savedPayload":{"position":{"x":27497.456515360525,"y":24456.468046378595},"targetPosition":{"x":27415.627307861072,"y":24538.29725387805},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Tether","code":"{ \n\"codeName\": \"USDT\"\n}","id":"04c5f172-6976-4557-b95f-38004b5201a6","savedPayload":{"position":{"x":27530.62363483965,"y":24551.25422811644},"targetPosition":{"x":27415.627307861072,"y":24538.29725387805},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ether","code":"{ \n\"codeName\": \"ETH\"\n}","id":"4175d47c-e12c-4902-b026-f4999a6d07a4","savedPayload":{"position":{"x":27477.196174606324,"y":24636.28354397867},"targetPosition":{"x":27415.627307861072,"y":24538.29725387805},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Monero","code":"{ \n\"codeName\": \"XMR\"\n}","id":"e3b61070-77b9-4dcf-9862-7f05ffa237f3","savedPayload":{"position":{"x":27377.406101937828,"y":24647.527184839073},"targetPosition":{"x":27415.627307861072,"y":24538.29725387805},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Stellar","code":"{ \n\"codeName\": \"XLM\"\n}","id":"6dee1e7e-80f3-4597-a28a-881af206ec14","savedPayload":{"position":{"x":27306.397376900048,"y":24576.518459801293},"targetPosition":{"x":27415.627307861072,"y":24538.29725387805},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},{"type":"Asset","name":"Ethereum Classic","code":"{ \n\"codeName\": \"ETC\"\n}","id":"59378d33-4833-495a-89bb-f739710be7d9","savedPayload":{"position":{"x":27317.641017760452,"y":24476.728387132796},"targetPosition":{"x":27415.627307861072,"y":24538.29725387805},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}}],"id":"f4451bbc-50ea-4558-9b3c-abd3e757a76e","savedPayload":{"position":{"x":27415.627307861072,"y":24538.29725387805},"targetPosition":{"x":27363.79941090751,"y":24078.31194596375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"exchangeMarkets":{"type":"Exchange Markets","name":"New Exchange Markets","markets":[{"type":"Market","name":"ETH/BTC","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Ether","id":"2ad20954-48bd-40db-a175-71337f616e19","savedPayload":{"position":{"x":27062.86677345042,"y":24693.804039512463},"targetPosition":{"x":27126.93978508125,"y":24471.401695144275},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Ether","id":"4175d47c-e12c-4902-b026-f4999a6d07a4"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Bitcoin","id":"01fc2c5c-9438-4c30-91a6-03a7374ef87e","savedPayload":{"position":{"x":27007.488045980976,"y":24669.64254770961},"targetPosition":{"x":27126.93978508125,"y":24471.401695144275},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"b2814a75-fd57-45c4-a3cb-af276793e573"}}},"id":"b3179fa1-f2ec-44eb-83bc-a6f9025c0441","savedPayload":{"position":{"x":27126.93978508125,"y":24471.401695144275},"targetPosition":{"x":27219.49397436657,"y":24259.26523989519},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Market","name":"BTC/USDT","code":"{ \n\"codeName\": \"Write here the name of the market as it is defined at the CCXT library.\"\n}","baseAsset":{"type":"Market Base Asset","name":"Bitcoin","id":"b85b1934-28e5-41f6-a6f0-4178453c48d8","savedPayload":{"position":{"x":26866.5814396409,"y":24557.27327883567},"targetPosition":{"x":27033.271467594266,"y":24396.70370456702},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Bitcoin","id":"b2814a75-fd57-45c4-a3cb-af276793e573"}}},"quotedAsset":{"type":"Market Quoted Asset","name":"Tether","id":"0d96bd23-cc52-4429-81c2-b2e229e290ae","savedPayload":{"position":{"x":26830.70280072453,"y":24508.659449406347},"targetPosition":{"x":27033.271467594266,"y":24396.70370456702},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Asset","name":"Tether","id":"04c5f172-6976-4557-b95f-38004b5201a6"}}},"id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f","savedPayload":{"position":{"x":27033.271467594266,"y":24396.70370456702},"targetPosition":{"x":27219.49397436657,"y":24259.26523989519},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"id":"c422d50d-ba95-4687-9be9-2c0650bef79c","savedPayload":{"position":{"x":27219.49397436657,"y":24259.26523989519},"targetPosition":{"x":27363.79941090751,"y":24078.31194596375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":1},"uiObject":{}}},"id":"22d2b36e-7a77-40a6-8948-b7d0fb744923","savedPayload":{"position":{"x":27363.79941090751,"y":24078.31194596375},"targetPosition":{"x":28014.01564388284,"y":23518.2348844148},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}}],"id":"0975e979-d4d5-4195-8fac-061788e6d425","savedPayload":{"position":{"x":28014.01564388284,"y":23518.2348844148},"targetPosition":{"x":27088.301618063546,"y":23506.2348844148},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9f4e7bfb-ea29-47cd-92bd-11f31d177ff8","savedPayload":{"position":{"x":27088.301618063546,"y":23506.2348844148},"targetPosition":{"x":27088.301618063546,"y":23506.2348844148},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},{"type":"Charting Space","name":"New Charting Space","viewport":{"type":"Viewport","name":"New Viewport","code":"{\"zoom\":\"0.00\"}","id":"c3db9bcf-39ee-47d1-ab4f-14bd5de815da","savedPayload":{"position":{"x":24800.582779654065,"y":24277.516045704255},"targetPosition":{"x":24062.806237131383,"y":24277.60640053908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":-70.92002521716984,"y":3.6288335632248163}}}},"dashboards":[{"type":"Dashboard","name":"New Dashboard","timeMachines":[{"type":"Time Machine","name":"Bitfinex Live Test","code":"{}","timeScale":{"type":"Time Scale","name":"New Time Scale","code":"{\n \"autoMinScale\": true,\n \"autoMaxScale\": true,\n \"fromDate\": \"2019-05-01T00:00:00.000Z\",\n \"toDate\": \"2019-06-17T23:59:59.999Z\"\n}","id":"25538237-c355-464a-ac1c-da2567f3b1b8","savedPayload":{"position":{"x":24277.437882745264,"y":24722.898558949793},"targetPosition":{"x":23917.081445003023,"y":24581.6763466278},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"offset\": 0,\n \"minValue\": 0,\n \"maxValue\": 9326.5,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"aa51daba-99f9-408e-a245-48a2d6281104","savedPayload":{"position":{"x":24299.843199505864,"y":24523.80747968788},"targetPosition":{"x":23917.081445003023,"y":24581.6763466278},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"01-hs\"}","id":"94a68900-c7ff-42ed-a5fb-bad0e50c158a","savedPayload":{"position":{"x":24158.422250767533,"y":24884.141730024436},"targetPosition":{"x":23917.081445003023,"y":24581.6763466278},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timelineCharts":[{"type":"Timeline Chart","name":"Chart 01","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 8,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"d63f5906-e6bf-4717-ae97-8e319feaf0a4","savedPayload":{"position":{"x":24300.343995885964,"y":25625.403278350488},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"19c78a01-0c97-48eb-a8d5-09ca3c42d9fd"}}},{"type":"Layer","name":"Volumes","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"7ba4932d-6708-45b5-9d4e-469925c08649","savedPayload":{"position":{"x":24246.841705182476,"y":25668.49733045015},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"23311eba-8ecd-41c9-aeed-86d7045b7be7"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\n \"status\": \"off\"\n}","id":"44544a3e-1427-46d3-aaae-4b3919e429b3","savedPayload":{"position":{"x":24187.323527692108,"y":25701.053043170054},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"1f860844-028c-41c8-a7df-f764306947a0"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\"status\":\"off\"}","id":"93bd61f8-4a6b-4764-aa8d-c98f5c8d742b","savedPayload":{"position":{"x":24123.199815059237,"y":25722.656512056918},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"a4267a80-ee05-41e7-ad62-30e092a91812"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"ac48d091-3c65-4f1f-a111-74253be952fe","savedPayload":{"position":{"x":24056.341335633628,"y":25732.747850435822},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"49c9d3df-b50e-4218-b585-0785f895ad9c"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"4a6945b8-6c16-41fe-bd96-5395485101d8","savedPayload":{"position":{"x":23988.906871263385,"y":25731.027779334712},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"699b13e8-75e0-4730-8dc8-c0797fb894ac"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"c33ce924-944c-4e12-8252-fdaf6627c55c","savedPayload":{"position":{"x":23922.50459117341,"y":25717.484796101235},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"b31d164e-1b77-450f-a2c1-67b19b67c482"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\"status\":\"off\"}","id":"ee760352-a918-41a1-9119-4848fa548401","savedPayload":{"position":{"x":23858.818680242843,"y":25692.07698921922},"targetPosition":{"x":24032.295549135673,"y":25346.40527963724},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"039ef46f-756f-4af9-8aad-0bde8230c53f"}}}],"id":"cb86deb6-e512-4da1-8194-ba1cdbe8d15f","savedPayload":{"position":{"x":24032.295549135673,"y":25346.40527963724},"targetPosition":{"x":23974.691667722793,"y":24964.256019255576},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"d6ebe53f-aad6-444d-8b65-a5b58cf262dc","savedPayload":{"position":{"x":23974.691667722793,"y":24964.256019255576},"targetPosition":{"x":23917.081445003023,"y":24581.6763466278},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"WHB LIVE","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 5,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Simulation","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"fd948d8f-6c7c-45ba-b5ef-19441faa27f9","savedPayload":{"position":{"x":23691.987271393125,"y":25683.881695235686},"targetPosition":{"x":23634.225624857172,"y":25301.56413080654},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Simulation","id":"4d7d0e2c-e474-42b8-881b-e2df11706587"}}},{"type":"Layer","name":"Conditions","code":"{ \n\"status\": \"off\"\n}","id":"22ecb49f-68dc-4d97-9b58-141ae7b1df00","savedPayload":{"position":{"x":23590.864239275095,"y":25685.99137403793},"targetPosition":{"x":23634.225624857172,"y":25301.56413080654},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Conditions","id":"8c774977-c5b7-40ad-9953-9355a51cbb84"}}},{"type":"Layer","name":"Strategies","code":"{\n \"status\": \"off\"\n}","id":"ce1df5e0-5cb6-4f47-b6b0-714ec6087565","savedPayload":{"position":{"x":23492.71960726423,"y":25661.66466578212},"targetPosition":{"x":23634.225624857172,"y":25301.56413080654},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Strategies","id":"b3280b17-225c-4bc0-8435-d072c51649da"}}},{"type":"Layer","name":"Trades","code":"{\n \"status\": \"off\"\n}","id":"cbbbcd69-981b-40ae-aefb-205642326e5a","savedPayload":{"position":{"x":23404.20324263679,"y":25612.676965573286},"targetPosition":{"x":23634.225624857172,"y":25301.56413080654},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Trades","id":"a464e80f-f0eb-4a2a-af9c-a3e127a927db"}}},{"type":"Layer","name":"Execution","code":"{ \n\"status\": \"off\"\n}","id":"76eb4c2a-1450-4f99-804e-d62d29a9f113","savedPayload":{"position":{"x":23331.19177169057,"y":25542.070718820796},"targetPosition":{"x":23634.225624857172,"y":25301.56413080654},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Execution","id":"c432f129-7eb3-4edf-be4b-d822cc8898aa"}}}],"id":"06e1de9d-9f1f-4a53-8a7a-ab5db07eeb74","savedPayload":{"position":{"x":23634.225624857172,"y":25301.56413080654},"targetPosition":{"x":23775.571479586357,"y":24941.7722050786},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"8e89a880-35e7-4d23-86b9-b4dc227269e1","savedPayload":{"position":{"x":23775.571479586357,"y":24941.7722050786},"targetPosition":{"x":23917.081445003023,"y":24581.6763466278},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"id":"fe3684a6-8b0a-4690-98f8-6787ce41f577","savedPayload":{"position":{"x":23917.081445003023,"y":24581.6763466278},"targetPosition":{"x":23675.998191454153,"y":24279.477557385657},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":800,"y":424.5},"width":320,"height":120,"radius":0}}},{"type":"Time Machine","name":"Binance Live Test","code":"{}","timeScale":{"type":"Time Scale","name":"New Time Scale","code":"{\n \"fromDate\": \"2020-01-01T00:00:00.000Z\",\n \"toDate\": \"2020-02-07T23:59:59.999Z\",\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"0f96450e-feb6-40eb-91ae-1ab452f1f451","savedPayload":{"position":{"x":23873.32920659412,"y":24919.650766822793},"targetPosition":{"x":23589.775576900633,"y":24656.551413445322},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"offset\": 0,\n \"minValue\": 0,\n \"maxValue\": 9885,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"941062b2-4753-4bfa-9767-096f2f550abe","savedPayload":{"position":{"x":23703.790534009266,"y":25026.178975308136},"targetPosition":{"x":23589.775576900633,"y":24656.551413445322},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"03-hs\"}","id":"309d46c8-dd5b-4288-b2a8-17862bf70048","savedPayload":{"position":{"x":23503.701632384218,"y":25033.665773783658},"targetPosition":{"x":23589.775576900633,"y":24656.551413445322},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timelineCharts":[{"type":"Timeline Chart","name":"Chart 01","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 8,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"2e7779a0-8796-498a-9785-e0829940acd0","savedPayload":{"position":{"x":22984.25868105515,"y":25798.430494333003},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"d5399ddc-ada6-44f1-a5f2-6488d0ac3c0b"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\"}","id":"781a7e70-a258-4ae9-bec3-bf755f4da1b3","savedPayload":{"position":{"x":22885.655631298505,"y":25775.92499167108},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"a3af37cc-6af5-4497-a818-ce500c5999ea"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\n \"status\": \"off\"\n}","id":"5ba76cdc-7275-4b04-8223-6714d204fbf7","savedPayload":{"position":{"x":22792.458622952232,"y":25736.63915826154},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"cbc5ab4a-c6db-4cf7-bdbd-739ba15a7e5d"}}},{"type":"Layer","name":"Volume-Stairs","code":"{ \n\"status\": \"off\"\n}","id":"4c022ed5-0ab8-4e02-9585-44f9ca519508","savedPayload":{"position":{"x":22707.499399954973,"y":25681.766674272938},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"54e00d9b-202d-4cdf-9638-81be6e16719a"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"f297b1a2-3e9f-4ad9-9dab-d71cc5e57005","savedPayload":{"position":{"x":22633.359405306055,"y":25612.974812364453},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"dc1cae8d-cf3c-4ac2-9419-67d2399fef5b"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"aa6bc614-404e-4634-8b02-d2e93d78b6c3","savedPayload":{"position":{"x":22572.291345226244,"y":25532.35377844981},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4cbc0c33-2abe-44f6-8862-ac17cee611ae"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"c5f851a4-9f6c-452b-b2bd-27cbef6e3178","savedPayload":{"position":{"x":22526.150741819132,"y":25442.353201848295},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"1eaf4681-66c5-4619-9782-6593baf891d6"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"f34daac1-6b33-4498-a37e-c03999f18196","savedPayload":{"position":{"x":22496.33955397097,"y":25345.70770453746},"targetPosition":{"x":23063.57674461455,"y":25223.658747692207},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"47730810-5628-4d58-922f-0d242f2c9f5d"}}}],"id":"65446d4a-17f4-4c5d-b16a-8c4723a439f9","savedPayload":{"position":{"x":23063.57674461455,"y":25223.658747692207},"targetPosition":{"x":23326.67616132384,"y":24940.105080643953},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"378a143e-2717-427b-9970-2ceff332c583","savedPayload":{"position":{"x":23326.67616132384,"y":24940.105080643953},"targetPosition":{"x":23589.775576900633,"y":24656.551413445322},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"WHB LIVE","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 5,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Simulation","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"7d68da73-3f59-4f91-8ead-14fbf7598057","savedPayload":{"position":{"x":22522.996710660038,"y":25090.378085475804},"targetPosition":{"x":22850.520327643866,"y":24884.581402522497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Simulation","id":"2aa7aa29-0891-4ca7-a82f-4c92577fbf4f"}}},{"type":"Layer","name":"Conditions","code":"{\"status\":\"off\"}","id":"8ddd7d74-0014-4341-a9cd-773bb27fed19","savedPayload":{"position":{"x":22498.936861111062,"y":25045.86706243921},"targetPosition":{"x":22850.520327643866,"y":24884.581402522497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Conditions","id":"895ecc79-e2a2-48c8-ad9a-d88db161cca9"}}},{"type":"Layer","name":"Strategies","code":"{\n \"status\": \"off\"\n}","id":"5db22942-b61c-49ca-9478-b6e8703eeb5c","savedPayload":{"position":{"x":22480.89270125909,"y":24998.596396828074},"targetPosition":{"x":22850.520327643866,"y":24884.581402522497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Strategies","id":"cae5b2d1-7cdd-4544-b0b0-d640f64db909"}}},{"type":"Layer","name":"Trades","code":"{\n \"status\": \"off\"\n}","id":"6db3ddec-733e-42d5-b766-75214530b30a","savedPayload":{"position":{"x":22469.172971681975,"y":24949.374902836906},"targetPosition":{"x":22850.520327643866,"y":24884.581402522497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Trades","id":"44fba0b1-916d-4504-a61f-276f58926166"}}},{"type":"Layer","name":"Execution","code":"{ \n\"status\": \"off\"\n}","id":"03e7c5ff-2c9a-42a8-84b7-dcb04d71a5ff","savedPayload":{"position":{"x":22463.978200202102,"y":24899.044773874666},"targetPosition":{"x":22850.520327643866,"y":24884.581402522497},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Execution","id":"99e9dd0d-709c-4b54-b62a-4077bcd85091"}}}],"id":"58c1708c-835c-404e-8300-ff800bafe85b","savedPayload":{"position":{"x":22850.520327643866,"y":24884.581402522497},"targetPosition":{"x":23220.1479528385,"y":24770.566408059098},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"b5844fdb-54a9-4a68-99a3-20d7d11fbb73","savedPayload":{"position":{"x":23220.1479528385,"y":24770.566408059098},"targetPosition":{"x":23589.775576900633,"y":24656.551413445322},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"id":"3d929f5e-247d-4c44-bcc7-8fdff09c550f","savedPayload":{"position":{"x":23589.77562567522,"y":24656.551425649108},"targetPosition":{"x":23675.998191454153,"y":24279.477557385657},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":1228,"y":208.5},"width":320,"height":120,"radius":0}}},{"type":"Time Machine","name":"Bitmex Live Test","code":"{}","timeScale":{"type":"Time Scale","name":"New Time Scale","code":"{\n \"fromDate\": \"2020-02-07T00:00:00.000Z\",\n \"toDate\": \"2020-02-10T23:59:59.999Z\",\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"36a3d5e9-ef1e-44fd-9e61-7f65d8e50834","savedPayload":{"position":{"x":23298.38032022449,"y":24832.947639174272},"targetPosition":{"x":23327.286924861688,"y":24447.21670134453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"offset\": 0,\n \"minValue\": 0,\n \"maxValue\": 10199,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"4011801f-a2ad-4078-b00f-dc513a22315f","savedPayload":{"position":{"x":23109.387579682407,"y":24766.816219052485},"targetPosition":{"x":23327.286924861688,"y":24447.21670134453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"40-min\"}","id":"7c6bc97c-2388-4942-aa51-fd0e8b94dffe","savedPayload":{"position":{"x":22978.780775303017,"y":24615.048358967633},"targetPosition":{"x":23327.286924861688,"y":24447.21670134453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timelineCharts":[{"type":"Timeline Chart","name":"Chart 01","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 4,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"ad966a7c-0908-408c-95d5-3a4d85b235dd","savedPayload":{"position":{"x":22056.996168110138,"y":24685.754551016988},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"276e341d-6cad-45fa-8d56-8be162013ca7"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\"}","id":"38cd78f6-a0ec-41d0-b8c2-c54bf6d222e9","savedPayload":{"position":{"x":22013.11368266456,"y":24594.631631054468},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"dc7512a4-5c3f-4d91-bf2d-8730c6c5c7fe"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"044fff53-a138-4d7d-a14e-f572bb033dc4","savedPayload":{"position":{"x":21985.721199771488,"y":24497.272959369147},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"84c28b2f-52f3-4d6f-9754-00b3da6e91af"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\"status\":\"off\"}","id":"8304971a-318c-4f75-8616-b1758edc3a6e","savedPayload":{"position":{"x":21975.651026162366,"y":24396.63672993432},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"7bafe96e-4bba-4a79-9cf9-21055feaa862"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"loading\",\n \"showPanels\": false\n}","id":"238e787b-ceef-40ee-8f21-3e484315452c","savedPayload":{"position":{"x":21983.20913896655,"y":24295.78072365697},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"b4e6199d-7047-4ee7-87ad-4722464e27cf"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{\"status\":\"off\"}","id":"c0f8b20c-4f37-4da3-b6ad-01d187aa492c","savedPayload":{"position":{"x":22008.165888751082,"y":24197.769399252236},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"073c035c-0f1d-4cd8-b3ea-d99ca30edff3"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"1ab3f3bb-eec2-412d-a932-606ecb72129c","savedPayload":{"position":{"x":22049.76297730248,"y":24105.580781216035},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6b994fd7-655f-4cac-8893-823eab0dd8ed"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"5521bf18-66f2-4376-920f-42ef6923a017","savedPayload":{"position":{"x":22106.736498134247,"y":24022.015974057704},"targetPosition":{"x":22555.82486355342,"y":24389.403567179113},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"690cc9af-8154-4b0d-8fd3-38a2be809b9d"}}}],"id":"9b23f0b9-f662-4456-9615-b0c36f143e4f","savedPayload":{"position":{"x":22555.82486355342,"y":24389.403567179113},"targetPosition":{"x":22941.555894845787,"y":24418.310134466457},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"318b8e8e-d3de-4252-97c4-142f5c249ac6","savedPayload":{"position":{"x":22941.555894845787,"y":24418.310134466457},"targetPosition":{"x":23327.286924861688,"y":24447.21670134453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"WHB LIVE","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 4,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Simulation","code":"{\n \"status\": \"loading\",\n \"showPanels\": true\n}","id":"a4ee02af-92f3-4053-a567-84c03aa1e3a5","savedPayload":{"position":{"x":22322.981698065403,"y":23883.661964792882},"targetPosition":{"x":22688.08770379699,"y":24011.41808609495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Simulation","id":"c53da960-e2c9-49ca-97a8-896c3071428f"}}},{"type":"Layer","name":"Conditions","code":"{ \n\"status\": \"off\"\n}","id":"39512f2b-7f75-4d55-9d52-5280eb2006b4","savedPayload":{"position":{"x":22342.780746364228,"y":23837.099048018434},"targetPosition":{"x":22688.08770379699,"y":24011.41808609495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Conditions","id":"2e907269-aa81-4f17-97ac-01ef8bbc02d4"}}},{"type":"Layer","name":"Strategies","code":"{\n \"status\": \"loading\"\n}","id":"6b5896ec-748d-4f90-b70b-9dd03591a48a","savedPayload":{"position":{"x":22368.48809128542,"y":23793.518777835856},"targetPosition":{"x":22688.08770379699,"y":24011.41808609495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Strategies","id":"debb9e02-384e-4eaa-9c48-be0831cf5a4d"}}},{"type":"Layer","name":"Trades","code":"{ \n\"status\": \"off\"\n}","id":"f76d8897-bd57-4477-a740-863004c88740","savedPayload":{"position":{"x":22399.663873029953,"y":23753.66682475071},"targetPosition":{"x":22688.08770379699,"y":24011.41808609495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Trades","id":"80c06cfa-2746-4d56-807b-3a1e55305d44"}}},{"type":"Layer","name":"Execution","code":"{ \n\"status\": \"off\"\n}","id":"04928db6-ea0f-45d6-a8e6-b7bf008e11e6","savedPayload":{"position":{"x":22435.77466532862,"y":23718.22506672933},"targetPosition":{"x":22688.08770379699,"y":24011.41808609495},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Execution","id":"b73ee177-28e0-4b32-a2a3-cb053a9d6f19"}}}],"id":"7a46f847-f668-45d6-84b4-701997d9f923","savedPayload":{"position":{"x":22688.08770379699,"y":24011.41808609495},"targetPosition":{"x":23007.687314967574,"y":24229.317393924375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"c3170781-1707-40b2-94d7-3a765604a530","savedPayload":{"position":{"x":23007.687314967574,"y":24229.317393924375},"targetPosition":{"x":23327.286924861688,"y":24447.21670134453},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"id":"ff931ce2-9428-4bdd-b9b4-668fb4b5b87e","savedPayload":{"position":{"x":23327.286988619253,"y":24447.216728168176},"targetPosition":{"x":23675.998191454153,"y":24279.477557385657},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":801,"y":206.5},"width":320,"height":120,"radius":0}}},{"type":"Time Machine","name":"WHB Backtest 2018 Poloniex","code":"{}","timeScale":{"type":"Time Scale","name":"New Time Scale","code":"{\n \"fromDate\": \"2018-01-01T06:32:50.416Z\",\n \"toDate\": \"2018-01-17T20:19:25.334Z\",\n \"autoMinScale\": false,\n \"autoMaxScale\": false\n}","id":"d73c0096-a7f7-4898-88d0-25860ba928c2","savedPayload":{"position":{"x":23060.015244701663,"y":24391.03083111897},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"offset\": 0,\n \"minValue\": 10000,\n \"maxValue\": 17257.08719135,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"94ca6cd9-af03-4651-974f-5b3db94392d2","savedPayload":{"position":{"x":22986.701542905215,"y":24294.74304707282},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"01-hs\"}","id":"653aaa99-992c-4593-95d5-0b4e1eb7def2","savedPayload":{"position":{"x":22946.73063069895,"y":24180.51274284047},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timelineCharts":[{"type":"Timeline Chart","name":"Popular Indicators","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 5,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"2535ada5-6d11-4175-985f-279c3fc199d1","savedPayload":{"position":{"x":22177.013725545086,"y":24056.652845713375},"targetPosition":{"x":22560.703261650076,"y":24007.598392879692},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"c70ed901-954a-4073-9cc2-3af722f4c619","savedPayload":{"position":{"x":22203.89110748858,"y":23858.236044396566},"targetPosition":{"x":22560.703261650076,"y":24007.598392879692},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41"}}}],"id":"0efd1306-895a-47b8-bb17-ec56b02e7ef6","savedPayload":{"position":{"x":22560.703261650076,"y":24007.598392879692},"targetPosition":{"x":22944.015139463336,"y":24059.521576489507},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"d64bbe48-b65d-42c8-aef5-8ca17212aa3d","savedPayload":{"position":{"x":22944.015139463336,"y":24059.521576489507},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Volumes","rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"offset\": 0,\n \"minValue\": -164166666.66666597,\n \"maxValue\": -64166666.666666165,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"84f82d6c-ebbb-4b96-9f3a-31cc09d00d37","savedPayload":{"position":{"x":22604.268852778114,"y":23846.996255685524},"targetPosition":{"x":22978.820880400453,"y":23943.613006377396},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{ \n\"value\": \"24-hs\"\n}","id":"d799d1de-0515-4374-a538-cc201db39981","savedPayload":{"position":{"x":22630.31474352431,"y":23775.78125265547},"targetPosition":{"x":22978.820880400453,"y":23943.613006377396},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 2,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Volumes","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"b0727c58-36f1-46f3-9922-3b3c647e5b8e","savedPayload":{"position":{"x":22311.01685063886,"y":23566.33690099292},"targetPosition":{"x":22669.753527656685,"y":23711.015927152588},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\n \"status\": \"off\"\n}","id":"78d06c3e-7e5c-4fa2-9378-67bd0fcbba25","savedPayload":{"position":{"x":22431.417922858403,"y":23406.351893841264},"targetPosition":{"x":22669.753527656685,"y":23711.015927152588},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}}],"id":"fbbbaf41-d383-4dc2-a7aa-13efff4b6913","savedPayload":{"position":{"x":22669.753527656685,"y":23711.015927152588},"targetPosition":{"x":22978.820880400453,"y":23943.613006377396},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"b00c53b4-89a0-49ed-9b88-6eaf99ca28c2","savedPayload":{"position":{"x":22978.820880400453,"y":23943.613006377396},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}},{"type":"Timeline Chart","name":"Oscilators","rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"offset\": -27,\n \"minValue\": -856.6666666666626,\n \"maxValue\": -456.66666666666316,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"c80b2cb2-45b3-43a1-910d-d5935197bb4e","savedPayload":{"position":{"x":22721.376872578825,"y":23636.50205157458},"targetPosition":{"x":23047.740825081528,"y":23844.132970929197},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"12-hs\"}","id":"9be9c2e6-0b03-40da-811b-29024991ac5b","savedPayload":{"position":{"x":22768.15463288646,"y":23576.82118175907},"targetPosition":{"x":23047.740825081528,"y":23844.132970929197},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 1,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Percentage Bandwidth","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"b5d6dab4-fb15-4dfa-8760-4ef6168d4d3d","savedPayload":{"position":{"x":22603.61263738401,"y":23210.6929305131},"targetPosition":{"x":22825.676731756925,"y":23527.41295144397},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2"}}}],"id":"a47f0fa7-a232-4281-8419-5fb0232519f8","savedPayload":{"position":{"x":22825.676731756925,"y":23527.41295144397},"targetPosition":{"x":23047.740825081528,"y":23844.132970929197},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"bc9dc438-16e5-4989-afe4-29fd35b2aaf4","savedPayload":{"position":{"x":23047.740825081528,"y":23844.132970929197},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Backtest WHB","timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"01-hs\"}","id":"19635bca-41c5-422c-98a7-53643de78cb9","savedPayload":{"position":{"x":22917.837804505467,"y":23457.03311230213},"targetPosition":{"x":23144.028609127676,"y":23770.81926913275},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 4,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Simulation","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"ba84d5b5-5e13-478e-bd55-122d6c2082c3","savedPayload":{"position":{"x":22788.859549595632,"y":23089.583024188818},"targetPosition":{"x":23006.758883140697,"y":23409.18263944124},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Simulation","id":"d34404c3-392b-4af3-a7ac-d6381ac1b5b1"}}},{"type":"Layer","name":"Conditions","code":"{\"status\":\"off\"}","id":"55022ab6-d6a7-4c13-b465-d9013c4b6ccd","savedPayload":{"position":{"x":22841.538635154993,"y":23059.430867600066},"targetPosition":{"x":23006.758883140697,"y":23409.18263944124},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Conditions","id":"5ef03988-16a2-43b6-a2c9-7783782534d9"}}},{"type":"Layer","name":"Strategies","code":"{\n \"status\": \"on\"\n}","id":"1b95aa04-a524-436b-abc4-907e4728bd65","savedPayload":{"position":{"x":22898.28599024096,"y":23037.89075866184},"targetPosition":{"x":23006.758883140697,"y":23409.18263944124},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Strategies","id":"30c98460-b2bf-4ab9-bfb7-d131e7373967"}}},{"type":"Layer","name":"Trades","code":"{\n \"status\": \"on\"\n}","id":"5234dc78-f11a-4472-a74b-35bae1a6c69e","savedPayload":{"position":{"x":22957.70430663764,"y":23025.493086343726},"targetPosition":{"x":23006.758883140697,"y":23409.18263944124},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Trades","id":"4cae29ae-970e-481f-9436-9a9584f5f2b5"}}}],"id":"6fab9886-0ab7-4e9c-b051-4b25098967df","savedPayload":{"position":{"x":23006.758883140697,"y":23409.18263944124},"targetPosition":{"x":23144.028609127676,"y":23770.81926913275},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"ba5a4409-49bc-458f-851c-3fb5834c4d10","savedPayload":{"position":{"x":23144.028609127676,"y":23770.81926913275},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"WHB LIVE","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 0,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Simulation","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"5d5109f8-907d-49c3-a95b-9826be3cadc4","savedPayload":{"position":{"x":23023.970561457754,"y":23000.50018191247},"targetPosition":{"x":23189.19080944346,"y":23350.251953753643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Simulation","id":"fda48255-a101-43e4-8932-d720dea69871"}}},{"type":"Layer","name":"Conditions","code":"{\"status\":\"off\"}","id":"5ef1a669-3e59-4058-b56b-52a86a7fda68","savedPayload":{"position":{"x":23071.035800947422,"y":22981.926793318467},"targetPosition":{"x":23189.19080944346,"y":23350.251953753643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Conditions","id":"56e374e5-2b2d-47a2-a665-4b2b3091fa95"}}},{"type":"Layer","name":"Strategies","code":"{\n \"status\": \"off\"\n}","id":"34a0ab4b-1bbb-49b0-b5f7-81610874633d","savedPayload":{"position":{"x":23120.122704478577,"y":22969.65554913516},"targetPosition":{"x":23189.19080944346,"y":23350.251953753643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Strategies","id":"232d5b14-f5aa-4723-804d-199afd9792e3"}}},{"type":"Layer","name":"Trades","code":"{\"status\":\"off\"}","id":"15ee25c1-d31a-40c6-898c-b59803891002","savedPayload":{"position":{"x":23170.391381522346,"y":22963.89641375275},"targetPosition":{"x":23189.19080944346,"y":23350.251953753643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Trades","id":"471a887b-fb87-4978-86f6-075b95356847"}}},{"type":"Layer","name":"Execution","code":"{ \n\"status\": \"off\"\n}","id":"70bddc4b-808c-448b-9795-8bf7cb62303f","savedPayload":{"position":{"x":23220.981721077394,"y":22964.747927574368},"targetPosition":{"x":23189.19080944346,"y":23350.251953753643},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Execution","id":"be409a2d-83ca-4c12-b72f-9ce1622efa65"}}}],"id":"b259da65-347f-45aa-9ccc-8c30f3ece59b","savedPayload":{"position":{"x":23189.19080944346,"y":23350.251953753643},"targetPosition":{"x":23258.258913360027,"y":23730.848356926483},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"10231a40-04b3-456c-aae8-d6f8fe65616a","savedPayload":{"position":{"x":23258.258913360027,"y":23730.848356926483},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Other Indicators","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 0,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Volumes","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"1c19d384-167b-48eb-89d4-88baa014ffba","savedPayload":{"position":{"x":23382.118565642326,"y":22961.131418184905},"targetPosition":{"x":23431.173142145384,"y":23344.820971282417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"cf022d3a-585e-4f42-a3e9-8051430582c5","savedPayload":{"position":{"x":23432.61976106864,"y":22958.011034779534},"targetPosition":{"x":23431.173142145384,"y":23344.820971282417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\"status\":\"off\"}","id":"8c9bf61d-0338-4145-abe7-006826e92d35","savedPayload":{"position":{"x":23483.096203531466,"y":22961.509075428323},"targetPosition":{"x":23431.173142145384,"y":23344.820971282417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\"status\":\"off\",\"showPanels\":false}","id":"5f43995b-1778-4db7-83e0-c4417526aff2","savedPayload":{"position":{"x":23532.684227105547,"y":22971.565687685925},"targetPosition":{"x":23431.173142145384,"y":23344.820971282417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\"status\":\"off\"}","id":"c4508640-379d-4082-a391-120d5e966bab","savedPayload":{"position":{"x":23580.535366959135,"y":22988.0088001284},"targetPosition":{"x":23431.173142145384,"y":23344.820971282417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0"}}}],"id":"d55ec066-71e7-478b-8379-969da3d771f4","savedPayload":{"position":{"x":23431.173142145384,"y":23344.820971282417},"targetPosition":{"x":23379.25007971099,"y":23728.13286569087},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"4f88eaee-75b3-414e-b5c7-3471da938525","savedPayload":{"position":{"x":23379.25007971099,"y":23728.13286569087},"targetPosition":{"x":23327.3270162788,"y":24111.44475872315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"d4098b73-7e25-434e-8fd9-342393cbb400"}}}],"id":"c9e23a11-0825-481b-9fb9-13fc37555cf4","savedPayload":{"position":{"x":23327.327066621383,"y":24111.444824922237},"targetPosition":{"x":23675.998191454153,"y":24279.477557385657},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""},"frame":{"position":{"x":1235,"y":644.5},"width":320,"height":120,"radius":0}}},{"type":"Time Machine","name":"Multiple Exchanges Demo 2","code":"{}","timeScale":{"type":"Time Scale","name":"New Time Scale","code":"{\n \"fromDate\": \"2017-12-14T23:21:57.951Z\",\n \"toDate\": \"2018-03-08T03:45:02.862Z\",\n \"autoMinScale\": false,\n \"autoMaxScale\": false\n}","id":"281e3c20-2ae9-42c8-a6a0-b40d31ee4aa2","savedPayload":{"position":{"x":23220.319470879665,"y":23787.9646718579},"targetPosition":{"x":23589.947047529615,"y":23901.979793985138},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"minValue\": 2398.3362243450465,\n \"maxValue\": 40192.45200326161,\n \"autoMinScale\": false,\n \"autoMaxScale\": false\n}","id":"1641be02-2ca2-4984-86d4-b54e5cb64635","savedPayload":{"position":{"x":23887.662836464235,"y":24817.670933581143},"targetPosition":{"x":23589.947047529615,"y":23901.979793985138},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"08-hs\"}","id":"1f12ffa2-f50b-49cd-bd25-81094d27f34b","savedPayload":{"position":{"x":23503.873150425385,"y":23524.86530613334},"targetPosition":{"x":23589.947047529615,"y":23901.979793985138},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"timelineCharts":[{"type":"Timeline Chart","name":"Chart 02B","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 5,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"38bcaa22-42ef-4ec6-b5ec-5e466f735274","savedPayload":{"position":{"x":23611.45862073629,"y":22981.211246284784},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502"}}},{"type":"Layer","name":"Volumes","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"bbecd696-8d7c-433c-8da4-35b48223cc6c","savedPayload":{"position":{"x":23710.061670492938,"y":22958.70574362286},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\n \"status\": \"off\"\n}","id":"a6c1dbe2-bfeb-489f-8745-5a32deae592d","savedPayload":{"position":{"x":23811.07475788865,"y":22953.664390018606},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\n \"status\": \"off\"\n}","id":"30ffc00c-11e1-4b6e-ac5c-ecb1c0982cd3","savedPayload":{"position":{"x":23911.428651378003,"y":22966.240364450237},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"1a38ab32-4dbd-4a72-9d5a-bb13006167fa","savedPayload":{"position":{"x":24008.07414868884,"y":22996.0515522984},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"96236259-3c56-495d-8368-6bdf9ca9b812","savedPayload":{"position":{"x":24098.074725290357,"y":23042.19215570551},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"0f2496df-a03e-4d6f-bf5c-fe105f046e8e","savedPayload":{"position":{"x":24178.695759205,"y":23103.26021578532},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"872bcfd9-6aaf-4c42-afe6-8261a974ef81","savedPayload":{"position":{"x":24247.487621113483,"y":23177.40021043424},"targetPosition":{"x":23789.379681877464,"y":23533.477502918944},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0"}}}],"id":"016eb53b-a005-4776-b8ff-0bbdce64674c","savedPayload":{"position":{"x":23789.379681877464,"y":23533.477502918944},"targetPosition":{"x":23675.3646749166,"y":23903.105077128846},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"479aaf5b-edfa-4504-93bb-d525ecaaa5c4","savedPayload":{"position":{"x":23675.3646749166,"y":23903.105077128846},"targetPosition":{"x":23589.947047529615,"y":23901.979793985138},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 02","rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"autoMinScale\": false,\n \"autoMaxScale\": false,\n \"minValue\": -3112.7235219945396,\n \"maxValue\": 27985.706592784303\n}","id":"faae36dd-0aa6-49ec-b72e-2e7fd0d7e6e4","savedPayload":{"position":{"x":23530.751353501266,"y":24255.78638734018},"targetPosition":{"x":23416.7363465404,"y":24625.413961550083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 5,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": true\n}","id":"17888307-9764-43c1-82a7-0deb4bfc9344","savedPayload":{"position":{"x":23927.249670850575,"y":23855.12287059095},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\",\"showPanels\":false}","id":"9f0a928f-3697-4924-80bf-745a66e7c6d8","savedPayload":{"position":{"x":24019.177211698945,"y":23897.29378835365},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\n \"status\": \"off\"\n}","id":"b6427adf-80ef-43db-8e1f-83ff86caf1db","savedPayload":{"position":{"x":24102.38526362173,"y":23954.787085063726},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9"}}},{"type":"Layer","name":"Volume-Stairs","code":"{ \n\"status\": \"off\"\n}","id":"60b0bb29-80e5-4404-9356-95e5ca55295c","savedPayload":{"position":{"x":24174.345592066562,"y":24025.855855993643},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"5809d9f1-a6f7-4175-ac54-871159a3e946","savedPayload":{"position":{"x":24232.87171886733,"y":24108.340712501235},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"5bda6800-1ce7-455d-9055-f8294fcd791c","savedPayload":{"position":{"x":24276.185357276838,"y":24199.73539396087},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"394c37fd-79fa-49e4-8924-d84b128c3627","savedPayload":{"position":{"x":24302.970444309776,"y":24297.26291922433},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"7625effd-7e1c-4504-b581-ebac27058582","savedPayload":{"position":{"x":24312.41312865055,"y":24397.959963787802},"targetPosition":{"x":23732.205543433178,"y":24401.576629452844},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0"}}}],"id":"d3f1a3b0-cef0-4abb-b641-33a8ae91222f","savedPayload":{"position":{"x":23732.205543433178,"y":24401.576629452844},"targetPosition":{"x":23416.7363465404,"y":24625.413961550083},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"88b86f1f-b5a2-4514-819f-c1afcfd2963f","savedPayload":{"position":{"x":23416.7363465404,"y":24625.413961550083},"targetPosition":{"x":23589.947047529615,"y":23901.979793985138},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"id":"2d16f029-31a7-482f-a0f5-74fe86e661c9","savedPayload":{"position":{"x":23589.94704950955,"y":23901.979907280762},"targetPosition":{"x":23675.998191454153,"y":24279.477557385657},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":807.6666666666666,"y":644.5333333333334},"width":320,"height":120,"radius":0}}},{"type":"Time Machine","name":"Multiple Exchanges Demo","code":"{}","timeScale":{"type":"Time Scale","name":"New Time Scale","code":"{\n \"fromDate\": \"2017-03-25T00:57:44.345Z\",\n \"toDate\": \"2018-10-09T23:05:56.515Z\",\n \"autoMinScale\": false,\n \"autoMaxScale\": false\n}","id":"db04bedc-5d44-4358-bf33-1bdc3a3e1b0a","savedPayload":{"position":{"x":23974.904605965286,"y":23594.21192065097},"targetPosition":{"x":23917.25315484826,"y":23976.704222405842},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"minValue\": 42686.92843336587,\n \"maxValue\": 59941.705024458,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"c270df5f-c70f-40e6-be14-194ada11d17c","savedPayload":{"position":{"x":24071.936445959378,"y":23622.166312184763},"targetPosition":{"x":23917.25315484826,"y":23976.704222405842},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timeFrameScale":{"type":"Time Frame Scale","name":"New Time Frame Scale","code":"{\"value\":\"24-hs\"}","id":"b88bdf1d-3e4f-40a0-a552-c6f482b326a9","savedPayload":{"position":{"x":24158.426877258822,"y":23674.281869097264},"targetPosition":{"x":23917.25315484826,"y":23976.704222405842},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"timelineCharts":[{"type":"Timeline Chart","name":"Chart 02B","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 5,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"33692829-a33d-46cb-aea5-209af8579c6a","savedPayload":{"position":{"x":24724.502275122773,"y":22967.304347535915},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502"}}},{"type":"Layer","name":"Volumes","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"e18ad12f-f822-433a-9aaf-7c9a0f30fd63","savedPayload":{"position":{"x":24817.20229180232,"y":23007.748951366204},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\n \"status\": \"off\"\n}","id":"7e6e8bbb-9b76-4eda-bfd1-eb77afb82faf","savedPayload":{"position":{"x":24901.47085518111,"y":23063.676299751878},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\n \"status\": \"off\"\n}","id":"18b6e861-e27d-4eb3-8dbb-2717c4e7c9bd","savedPayload":{"position":{"x":24974.747507602824,"y":23133.387068512842},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"9034e0fa-f884-4aa8-884a-89b30d82828d","savedPayload":{"position":{"x":25034.805775063407,"y":23214.763131215645},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"45c29a47-800c-42fa-84a1-8181ed91c097","savedPayload":{"position":{"x":25079.82081749702,"y":23305.331917373336},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"61a40b88-9671-4483-a8f2-2e430ef039c9","savedPayload":{"position":{"x":25108.424875618028,"y":23402.34154024853},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"679266f7-ce9e-4a6b-8196-0861d109c91e","savedPayload":{"position":{"x":25119.748829594773,"y":23502.844411539405},"targetPosition":{"x":24539.7102853015,"y":23517.309780224736},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0"}}}],"id":"81d1d3fb-a27d-43cc-a418-ddadbb68cbd5","savedPayload":{"position":{"x":24539.7102853015,"y":23517.309780224736},"targetPosition":{"x":24228.481719902757,"y":23747.007002309914},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"ead2480e-7528-4ad7-8b1f-a22718e2267c","savedPayload":{"position":{"x":24228.481719902757,"y":23747.007002309914},"targetPosition":{"x":23917.25315484826,"y":23976.704222405842},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 02","rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"autoMinScale\": true,\n \"autoMaxScale\": true,\n \"minValue\": 948.42378187,\n \"maxValue\": 19903.4402144\n}","id":"56322bb6-0e64-400c-8c43-ada8f7dc9059","savedPayload":{"position":{"x":24588.55541753484,"y":23605.688392093056},"targetPosition":{"x":24277.3268521361,"y":23835.385614178234},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 5,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": false\n}","id":"fc87fd38-3e72-4b48-8675-1d1fea5f8f37","savedPayload":{"position":{"x":25096.744478850953,"y":23408.15352642721},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\",\"showPanels\":false}","id":"d1f15fba-c0ff-49eb-8210-4c3f2dfc3f7a","savedPayload":{"position":{"x":25156.802746311536,"y":23489.529589130012},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"382733f4-482f-4b9f-b1f9-6840b367ebec","savedPayload":{"position":{"x":25201.81778874515,"y":23580.098375287704},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9"}}},{"type":"Layer","name":"Volume-Stairs","code":"{ \n\"status\": \"off\"\n}","id":"9afe2165-380c-40b5-909c-09546ceb83eb","savedPayload":{"position":{"x":25230.421846866157,"y":23677.107998162897},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"6bdd20a7-daed-4b93-b529-02bf3d1386b4","savedPayload":{"position":{"x":25241.745800842902,"y":23777.610869453772},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"59bb247c-f542-439e-a67e-31659a1379eb","savedPayload":{"position":{"x":25235.445578064,"y":23878.553260273056},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\"status\":\"loading\"}","id":"dfaeb715-cd33-4d5d-9bd0-1e50723b33a3","savedPayload":{"position":{"x":25211.71260761052,"y":23976.868087155028},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{ \n\"status\": \"off\"\n}","id":"4973ccf3-bd5b-4b49-b353-dd200c75679f","savedPayload":{"position":{"x":25171.26800378023,"y":24069.568103834576},"targetPosition":{"x":24661.707256549627,"y":23792.076238139103},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0"}}}],"id":"b69c46da-1753-4f8d-962b-df133d58ea32","savedPayload":{"position":{"x":24661.707256549627,"y":23792.076238139103},"targetPosition":{"x":24277.3268521361,"y":23835.385614178234},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":0,"y":80},"width":150,"height":40,"radius":0}}},"id":"a0933f6f-9b4c-4d25-8017-3d1a8b14b811","savedPayload":{"position":{"x":24277.3268521361,"y":23835.385614178234},"targetPosition":{"x":23917.25315484826,"y":23976.704222405842},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"id":"a01d6e3e-3c99-4d45-a197-1a51fb544933","savedPayload":{"position":{"x":23917.2531262931,"y":23976.704327649855},"targetPosition":{"x":23675.998191454153,"y":24279.477557385657},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{},"frame":{"position":{"x":1232,"y":423.5},"width":320,"height":120,"radius":0}}}],"id":"c3785682-fa75-45e2-9ab7-6fe18517e89a","savedPayload":{"position":{"x":23675.998191454153,"y":24279.477557385657},"targetPosition":{"x":24062.806237131383,"y":24277.60640053908},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":0},"uiObject":{}}}],"id":"c53a4385-3fb3-49d5-b506-99bcaef2c141","savedPayload":{"position":{"x":24062.806237131383,"y":24277.60640053908},"targetPosition":{"x":24062.806237131383,"y":24277.60640053908},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":0},"uiObject":{}}},{"type":"Trading System","name":"Bull run rider - USDT- SDA v.0.0.3","strategies":[{"type":"Strategy","name":"My Strategy","triggerStage":{"type":"Trigger Stage","triggerOn":{"type":"Trigger On Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.max > chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.deviation","id":"1e07e38a-c469-4f3f-b7e8-dccd9f94c546","savedPayload":{"position":{"x":26536.85735869531,"y":26359.93488799773},"targetPosition":{"x":26571.110901332057,"y":26198.784639952584},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a1c434c4-5787-4906-8157-1e355999bba8","savedPayload":{"position":{"x":26571.110901332057,"y":26198.784639952584},"targetPosition":{"x":26605.364443968803,"y":26037.63439190744},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.previous.max > chart.at01hs.bollingerBand.previous.movingAverage + chart.at01hs.bollingerBand.previous.deviation\n","id":"3fb95a71-efe8-45cf-be43-8b0d94d2f420","savedPayload":{"position":{"x":26360.497569339033,"y":25817.155267676226},"targetPosition":{"x":26482.93100665392,"y":25927.394829791832},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f69f2608-49bd-4b04-b8f8-497675f819fe","savedPayload":{"position":{"x":26482.93100665392,"y":25927.394829791832},"targetPosition":{"x":26605.364443968803,"y":26037.63439190744},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"1dfb0689-df0c-4184-9c09-683d012c9dfe","savedPayload":{"position":{"x":26605.364443968803,"y":26037.63439190744},"targetPosition":{"x":26762.051423920435,"y":25986.723705977904},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"32d2ee8c-71bd-4e4c-8671-04bbc4a16734","savedPayload":{"position":{"x":26762.051423920435,"y":25986.723705977904},"targetPosition":{"x":26918.738403872067,"y":25935.81302004837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"triggerOff":{"type":"Trigger Off Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage","id":"a1dd4467-b96b-4084-bd9b-056c6495de95","savedPayload":{"position":{"x":26715.095660153926,"y":25309.06510024184},"targetPosition":{"x":26766.00634608346,"y":25465.752080193473},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b527026c-4be3-4baf-bbde-83ebfd17e567","savedPayload":{"position":{"x":26766.00634608346,"y":25465.752080193473},"targetPosition":{"x":26816.917032012996,"y":25622.439060145105},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"41cbb7ba-020c-4e96-a6a7-c3ef8c25b6fd","savedPayload":{"position":{"x":26816.917032012996,"y":25622.439060145105},"targetPosition":{"x":26867.82771794253,"y":25779.126040096737},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"1af9fdb8-7316-4374-84f2-1b946741b5c6","savedPayload":{"position":{"x":26867.82771794253,"y":25779.126040096737},"targetPosition":{"x":26918.738403872067,"y":25935.81302004837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"takePosition":{"type":"Take Position Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.previous.max > chart.at01hs.bollingerBand.previous.movingAverage + chart.at01hs.bollingerBand.previous.deviation\n","id":"0c15bd8b-b59c-4a2c-b65a-b063aec45216","savedPayload":{"position":{"x":26938.524929169183,"y":25684.401378605686},"targetPosition":{"x":27085.318646472257,"y":25759.19651339749},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e022ab25-c7d3-439b-bfd7-1217c6abfbfe","savedPayload":{"position":{"x":27085.318646472257,"y":25759.19651339749},"targetPosition":{"x":27232.11236377533,"y":25833.991648189298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.max > chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.deviation","id":"cbce2ad1-a528-444c-9a41-3d592f7260aa","savedPayload":{"position":{"x":27130.29099191626,"y":25520.617688286035},"targetPosition":{"x":27181.201677845795,"y":25677.304668237666},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"793426fb-7c1e-40c6-a41e-2acb0943aece","savedPayload":{"position":{"x":27181.201677845795,"y":25677.304668237666},"targetPosition":{"x":27232.11236377533,"y":25833.991648189298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close > chart.at01hs.candle.open","id":"9f72065b-dd6c-4d64-8de5-7fe2110bf66b","savedPayload":{"position":{"x":27381.702633358942,"y":25540.40421358315},"targetPosition":{"x":27306.907498567136,"y":25687.197930886225},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"abdce7c6-67e2-4fbf-a4c3-e5526c13b6ca","savedPayload":{"position":{"x":27306.907498567136,"y":25687.197930886225},"targetPosition":{"x":27232.11236377533,"y":25833.991648189298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.max-chart.at01hs.candle.min < 500","id":"68d20d82-b34a-49b7-be79-2d859997ec8e","savedPayload":{"position":{"x":27545.486323678593,"y":25732.170276330227},"targetPosition":{"x":27388.79934372696,"y":25783.080962259763},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2d4d8745-13d1-47bd-8f42-faace8bfd7cb","savedPayload":{"position":{"x":27388.79934372696,"y":25783.080962259763},"targetPosition":{"x":27232.11236377533,"y":25833.991648189298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at04hs.bollingerChannel.direction === \"Up\"","id":"01a5065a-b7af-4cbe-8678-033694f77d69","savedPayload":{"position":{"x":27525.699798381476,"y":25983.58191777291},"targetPosition":{"x":27378.906081078403,"y":25908.786782981104},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5f433ebc-f0ae-4095-93f5-6de3e03c0683","savedPayload":{"position":{"x":27378.906081078403,"y":25908.786782981104},"targetPosition":{"x":27232.11236377533,"y":25833.991648189298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at04hs.percentageBandwidth.movingAverage >= 85","id":"98b5b2b2-fbc0-4d1f-af0f-0d7eee6dc94b","savedPayload":{"position":{"x":27333.9337356344,"y":26147.36560809256},"targetPosition":{"x":27283.023049704865,"y":25990.67862814093},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7ef29297-9eb3-4b6e-a69e-58e5afbfaff9","savedPayload":{"position":{"x":27283.023049704865,"y":25990.67862814093},"targetPosition":{"x":27232.11236377533,"y":25833.991648189298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"(chart.at12hs.bollingerSubChannel.slope === \"Side\" || chart.at12hs.bollingerSubChannel.slope === \"Gentle\") && (chart.at08hs.bollingerSubChannel.slope === \"Side\" || chart.at08hs.bollingerSubChannel.slope === \"Gentle\") && (chart.at06hs.bollingerSubChannel.slope === \"Side\" || chart.at06hs.bollingerSubChannel.slope === \"Gentle\")","id":"ea3dc59e-40da-4b52-b5d6-2724fb12eb20","savedPayload":{"position":{"x":27082.522094191718,"y":26127.579082795444},"targetPosition":{"x":27157.317228983524,"y":25980.78536549237},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"24a4f2dc-268f-4e27-97f7-f4a0cd89e710","savedPayload":{"position":{"x":27157.317228983524,"y":25980.78536549237},"targetPosition":{"x":27232.11236377533,"y":25833.991648189298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"c3b9135a-1e58-486e-b444-1bdd743effa5","savedPayload":{"position":{"x":27232.11236377533,"y":25833.991648189298},"targetPosition":{"x":27075.4253838237,"y":25884.902334118833},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"830b1089-fef6-4e79-9ad2-a45c14574b84","savedPayload":{"position":{"x":27075.4253838237,"y":25884.902334118833},"targetPosition":{"x":26918.738403872067,"y":25935.81302004837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"8ffee2fe-23fb-4e58-8bb9-09b0c2aaa621","savedPayload":{"position":{"x":26918.738403872067,"y":25935.81302004837},"targetPosition":{"x":26969.6490898016,"y":26092.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"openStage":{"type":"Open Stage","initialDefinition":{"type":"Initial Definition","stopLoss":{"type":"Initial Stop","phases":[{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"positionRate - positionRate * 2.5 / 100","id":"5df1f00d-cb38-4ffb-84ac-3ce85f4e23dc","savedPayload":{"position":{"x":26767.47874219625,"y":26129.682202982385},"targetPosition":{"x":26784.699852559064,"y":25965.83428268417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.previous.close > positionRate + positionRate * 2 / 100","id":"7aeace08-1969-4deb-aa89-726658f778ba","savedPayload":{"position":{"x":26251.556227942685,"y":25578.48276586544},"targetPosition":{"x":26384.84213409678,"y":25675.320645070122},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e7ca02e3-34a6-4898-98e7-c44051e3b1a4","savedPayload":{"position":{"x":26384.84213409678,"y":25675.320645070122},"targetPosition":{"x":26518.128040250875,"y":25772.158524274804},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"9111e54c-f9c1-491c-96c4-0b15f5f46326","savedPayload":{"position":{"x":26518.128040250875,"y":25772.158524274804},"targetPosition":{"x":26651.41394640497,"y":25868.996403479487},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"15653ae3-7298-4da5-89a3-6da07b194d05","savedPayload":{"position":{"x":26651.41394640497,"y":25868.996403479487},"targetPosition":{"x":26784.699852559064,"y":25965.83428268417},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"c7844fec-1bb6-4412-b95d-f47b437d9a67","savedPayload":{"position":{"x":26784.699852559064,"y":25965.83428268417},"targetPosition":{"x":26935.206869075973,"y":25898.824241590635},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"2f0906a5-35ac-4681-9a75-d455789d198d","savedPayload":{"position":{"x":26935.206869075973,"y":25898.824241590635},"targetPosition":{"x":27085.713885592882,"y":25831.8142004971},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"takeProfit":{"type":"Initial Take Profit","phases":[{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"positionRate * 2","id":"54b0c16e-a747-4e2d-8350-fd147abc9955","savedPayload":{"position":{"x":26704.084513316524,"y":25621.20086850408},"targetPosition":{"x":26865.23476136167,"y":25586.94732586733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[],"announcements":[],"id":"c6bffce6-17a9-484d-a8f7-5a3a4f5bd18d","savedPayload":{"position":{"x":26916.145447291205,"y":25430.2603459157},"targetPosition":{"x":26865.23476136167,"y":25586.94732586733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"73450f7c-f2df-4c92-a14d-e39d460d09c7","savedPayload":{"position":{"x":26865.23476136167,"y":25586.94732586733},"targetPosition":{"x":26975.474323477276,"y":25709.380763182216},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"784fc53e-e6ea-4658-80c7-62139549c49a","savedPayload":{"position":{"x":26975.474323477276,"y":25709.380763182216},"targetPosition":{"x":27085.713885592882,"y":25831.8142004971},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"positionSize":{"type":"Position Size","name":"Position Size","formula":{"type":"Formula","code":"balanceQuotedAsset","id":"51c900a5-97d0-4d54-9af2-d2a8b56338ef","savedPayload":{"position":{"x":27250.464326042,"y":25546.458067069878},"targetPosition":{"x":27168.089105817442,"y":25689.13613378349},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"31b90dc3-5e56-4485-afd1-7f0017805c7a","savedPayload":{"position":{"x":27168.089105817442,"y":25689.13613378349},"targetPosition":{"x":27085.713885592882,"y":25831.8142004971},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"positionRate":{"type":"Position Rate","name":"Position Rate","formula":{"type":"Formula","name":"My Formula","code":"chart.at01hs.candle.close","id":"bdd26201-c1e2-403b-afca-7c48da10cd7a","savedPayload":{"position":{"x":27408.014381683173,"y":25900.321285770595},"targetPosition":{"x":27246.864133638028,"y":25866.067743133848},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"5368a5cc-a6a1-46fa-996c-76a00c003dd1","savedPayload":{"position":{"x":27246.864133638028,"y":25866.067743133848},"targetPosition":{"x":27085.713885592882,"y":25831.8142004971},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b0e61bb8-915c-4089-839c-0cbb27143a5a","savedPayload":{"position":{"x":27085.713885592882,"y":25831.8142004971},"targetPosition":{"x":27102.934995955697,"y":25995.662120795318},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"openExecution":{"type":"Open Execution","id":"adafb7bb-4f7f-4275-b6dd-36e9d03441ce","savedPayload":{"position":{"x":27253.442012472606,"y":26062.67216188885},"targetPosition":{"x":27102.934995955697,"y":25995.662120795318},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"4743e572-e0ec-4641-8881-86eef4337ed7","savedPayload":{"position":{"x":27102.934995955697,"y":25995.662120795318},"targetPosition":{"x":26969.6490898016,"y":26092.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"manageStage":{"type":"Manage Stage","stopLoss":{"type":"Stop","phases":[{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"if (chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.movingAverage * 3.5 / 100 > stopLoss) {chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.movingAverage * 3.5 / 100} else {stopLoss}","id":"f8666b4c-a5f5-463f-b358-5e6930274156","savedPayload":{"position":{"x":27421.17013935233,"y":25891.4698767194},"targetPosition":{"x":27403.949028989515,"y":26055.317797017615},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[],"announcements":[],"id":"49858221-6783-4a5c-abdd-a715ee64491d","savedPayload":{"position":{"x":27537.23493514361,"y":26152.155676222297},"targetPosition":{"x":27403.949028989515,"y":26055.317797017615},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"1c0100eb-a29a-4024-b7b9-d24164bc43eb","savedPayload":{"position":{"x":27403.949028989515,"y":26055.317797017615},"targetPosition":{"x":27253.442012472606,"y":26122.32783811115},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"17cd97a5-9ae0-46fb-86a7-a6b2824510c4","savedPayload":{"position":{"x":27253.442012472606,"y":26122.32783811115},"targetPosition":{"x":27102.934995955697,"y":26189.337879204682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"takeProfit":{"type":"Take Profit","phases":[{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.movingAverage * 12.5 / 100","id":"77754c17-f3b3-478f-b837-79bd0a2b533b","savedPayload":{"position":{"x":27201.778681384163,"y":26613.871599005797},"targetPosition":{"x":27068.49277523007,"y":26517.033719801115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.min < chart.at01hs.candle.previous.min","id":"8d2df34f-70ab-42e3-a055-c33f1a2e0c33","savedPayload":{"position":{"x":26733.03652147062,"y":26978.749642584615},"targetPosition":{"x":26750.257631833436,"y":26814.9017222864},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6b58a29a-27a3-4728-96bf-f1cffc915b93","savedPayload":{"position":{"x":26750.257631833436,"y":26814.9017222864},"targetPosition":{"x":26767.47874219625,"y":26651.053801988182},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.previous.min < chart.at01hs.candle.previous.previous.min","id":"b3f4b456-e22f-4461-a912-674d9d59d25e","savedPayload":{"position":{"x":26500.90692988806,"y":26457.378043578818},"targetPosition":{"x":26634.192836042155,"y":26554.2159227835},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d6f645b9-dded-4dc8-9361-edbb5e9362d7","savedPayload":{"position":{"x":26634.192836042155,"y":26554.2159227835},"targetPosition":{"x":26767.47874219625,"y":26651.053801988182},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"11d3b6ff-1092-4d0c-aee7-06fd7fa1fdd4","savedPayload":{"position":{"x":26767.47874219625,"y":26651.053801988182},"targetPosition":{"x":26917.98575871316,"y":26584.04376089465},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"262eb093-e91f-48c0-b975-43b2bb998c3e","savedPayload":{"position":{"x":26917.98575871316,"y":26584.04376089465},"targetPosition":{"x":27068.49277523007,"y":26517.033719801115},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"f23e15b8-7fc2-4b64-a091-7a889776d29a","savedPayload":{"position":{"x":27068.49277523007,"y":26517.033719801115},"targetPosition":{"x":27085.713885592882,"y":26353.1857995029},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.movingAverage * 10 / 100","id":"b99edd1c-9f5b-4024-87cc-5bae52679cc8","savedPayload":{"position":{"x":27184.55757102135,"y":26777.719519304013},"targetPosition":{"x":27051.271664867254,"y":26680.88164009933},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.percentageBandwidth.direction === 'Down'","id":"994de580-782e-4e71-ad72-5a78f025e9b4","savedPayload":{"position":{"x":27150.11535029572,"y":27105.415359900446},"targetPosition":{"x":27016.829444141626,"y":27008.577480695763},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f3b04fbc-e688-4e48-9d5f-96ea5ef0c8cb","savedPayload":{"position":{"x":27016.829444141626,"y":27008.577480695763},"targetPosition":{"x":26883.54353798753,"y":26911.73960149108},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.percentageBandwidth.movingAverage < 70","id":"fe5f5c24-785d-4b05-aa61-50d738d9cd81","savedPayload":{"position":{"x":26582.529504953713,"y":27045.75968367815},"targetPosition":{"x":26733.03652147062,"y":26978.749642584615},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"6b3364b5-d25b-4e29-b126-b7f85b401c3f","savedPayload":{"position":{"x":26733.03652147062,"y":26978.749642584615},"targetPosition":{"x":26883.54353798753,"y":26911.73960149108},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"04b8d0ea-5b40-4f88-9482-75170e16b454","savedPayload":{"position":{"x":26883.54353798753,"y":26911.73960149108},"targetPosition":{"x":26900.764648350345,"y":26747.891681192865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close > positionRate + positionRate * 13 / 100","id":"8eb895f9-4d9c-4314-8351-d47d57dea06a","savedPayload":{"position":{"x":26500.90692988806,"y":26457.378043578818},"targetPosition":{"x":26634.192836042155,"y":26554.2159227835},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7f353c88-8c2e-4bcb-85ce-28276752daeb","savedPayload":{"position":{"x":26634.192836042155,"y":26554.2159227835},"targetPosition":{"x":26767.47874219625,"y":26651.053801988182},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"f9d261e8-528e-4469-9eb0-1765ec1adea4","savedPayload":{"position":{"x":26767.47874219625,"y":26651.053801988182},"targetPosition":{"x":26900.764648350345,"y":26747.891681192865},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"41adecfe-df45-4c0b-a6c6-9af24919657d","savedPayload":{"position":{"x":26900.764648350345,"y":26747.891681192865},"targetPosition":{"x":27051.271664867254,"y":26680.88164009933},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"cc74b1fd-2a75-4d59-a30a-070a6f530237","savedPayload":{"position":{"x":27051.271664867254,"y":26680.88164009933},"targetPosition":{"x":27068.49277523007,"y":26517.033719801115},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.movingAverage * 8.5 / 100","id":"a1a934f5-46c4-4a92-9513-11e3976469d5","savedPayload":{"position":{"x":27167.336460658535,"y":26941.56743960223},"targetPosition":{"x":27034.05055450444,"y":26844.729560397547},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.deviation","id":"14b2b7a5-bce7-4081-b88f-13b77e46f00f","savedPayload":{"position":{"x":26432.022488436804,"y":27112.769724771682},"targetPosition":{"x":26582.529504953713,"y":27045.75968367815},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"63de0e84-99e1-4ebb-a639-070aaec25769","savedPayload":{"position":{"x":26582.529504953713,"y":27045.75968367815},"targetPosition":{"x":26733.03652147062,"y":26978.749642584615},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b2193069-9399-49ef-b0a5-a27cdf12f0f5","savedPayload":{"position":{"x":26733.03652147062,"y":26978.749642584615},"targetPosition":{"x":26883.54353798753,"y":26911.73960149108},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"e7f20b10-01fe-4aa8-9857-421f22ea3a4c","savedPayload":{"position":{"x":26883.54353798753,"y":26911.73960149108},"targetPosition":{"x":27034.05055450444,"y":26844.729560397547},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"1c5a8e29-6bfc-4010-be09-19c2b96bfe70","savedPayload":{"position":{"x":27034.05055450444,"y":26844.729560397547},"targetPosition":{"x":27051.271664867254,"y":26680.88164009933},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.movingAverage * 7 / 100","id":"feaaddbf-e88d-4090-a7b2-eb74b86dcd4a","savedPayload":{"position":{"x":27150.11535029572,"y":27105.415359900446},"targetPosition":{"x":27016.829444141626,"y":27008.577480695763},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[],"announcements":[],"id":"e39fdef7-ea4f-4b5d-bb01-7edac296c69c","savedPayload":{"position":{"x":26866.322427624717,"y":27075.587521789297},"targetPosition":{"x":27016.829444141626,"y":27008.577480695763},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"f9b759a6-ab55-4344-963a-9608c2fbe429","savedPayload":{"position":{"x":27016.829444141626,"y":27008.577480695763},"targetPosition":{"x":27034.05055450444,"y":26844.729560397547},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"b6bb374a-2c9d-4b84-8ddd-84f075656cca","savedPayload":{"position":{"x":27085.713885592882,"y":26353.1857995029},"targetPosition":{"x":27102.934995955697,"y":26189.337879204682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1d90afee-eb99-4627-a823-027589c6d2c9","savedPayload":{"position":{"x":27102.934995955697,"y":26189.337879204682},"targetPosition":{"x":26969.6490898016,"y":26092.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"closeStage":{"type":"Close Stage","closeExecution":{"type":"Close Execution","id":"17ad00f9-a9c4-4e9a-b399-39db314232e1","savedPayload":{"position":{"x":26867.82771794253,"y":26405.873959903263},"targetPosition":{"x":26918.738403872067,"y":26249.18697995163},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"575f8df0-55d4-4e22-a8e0-84b2973be70a","savedPayload":{"position":{"x":26918.738403872067,"y":26249.18697995163},"targetPosition":{"x":26969.6490898016,"y":26092.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"97bedd6e-ed17-4ea9-a3be-b6cadfea4d4d","savedPayload":{"position":{"x":26969.6490898016,"y":26092.5},"targetPosition":{"x":26804.898649352483,"y":26092.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"USDT\",\n\"initialBalance\": 1000,\n\"minimumBalance\": 10,\n\"maximumBalance\": 100000\n}","id":"8150ec53-592d-4200-93d1-b16a5a9f8c81","savedPayload":{"position":{"x":26061.097818966115,"y":27116.254015375245},"targetPosition":{"x":25728.45997527317,"y":26092.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"initialDatetime\": \"2019-01-01T00:00:00.000Z\",\n\"finalDatetime\": \"2020-12-31T23:59:59.999Z\"\n}","id":"23db4e5b-5955-48a7-a6ee-753b5052a325","savedPayload":{"position":{"x":24857.602794540573,"y":26725.214777621084},"targetPosition":{"x":25728.45997527317,"y":26092.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"71d8af67-a7b7-4154-acac-bc664f841010","savedPayload":{"position":{"x":24857.60279454057,"y":25459.785222378916},"targetPosition":{"x":25728.45997527317,"y":26092.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"032309c9-4c3b-4a42-8d67-b297db6e17e7","savedPayload":{"position":{"x":26061.09781896611,"y":25068.745984624755},"targetPosition":{"x":25728.45997527317,"y":26092.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ddfbcdad-885d-4490-9087-a9a70c758158","savedPayload":{"position":{"x":25728.45997527317,"y":26092.5},"targetPosition":{"x":26804.898649352483,"y":26092.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"56432995-eb04-4929-850f-6530233a3be3","savedPayload":{"position":{"x":26804.898649352483,"y":26092.5},"targetPosition":{"x":26804.898649352483,"y":26092.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","strategies":[{"type":"Strategy","name":"My Strategy","triggerStage":{"type":"Trigger Stage","triggerOn":{"type":"Trigger On Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage && chart.at01hs.candle.previous.close < chart.at01hs.bollingerBand.previous.movingAverage && chart.at01hs.candle.previous.previous.close < chart.at01hs.bollingerBand.previous.previous.movingAverage","id":"91f3043a-8e9e-43e0-aca3-d740f527e043","savedPayload":{"position":{"x":26060.50133323033,"y":28261.73081291619},"targetPosition":{"x":26366.566485190426,"y":28085.024015042465},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d61ab29a-c3c4-4e3b-8825-245a322c431a","savedPayload":{"position":{"x":26366.566485190426,"y":28085.024015042465},"targetPosition":{"x":26672.63163715052,"y":27908.31721716874},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.bollingerBand.deviation > 200","id":"03a5e03d-c910-4e6c-b63f-0d018e413b02","savedPayload":{"position":{"x":25981.250315656373,"y":28055.274853668416},"targetPosition":{"x":26326.940976403446,"y":27981.796035418578},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"16fc43c1-d0c3-42e3-9ba8-cba5c21bafd5","savedPayload":{"position":{"x":26326.940976403446,"y":27981.796035418578},"targetPosition":{"x":26672.63163715052,"y":27908.31721716874},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.deviation","id":"09012cab-d69d-4763-b014-7f984c1784dc","savedPayload":{"position":{"x":25969.676518967175,"y":27834.433657045985},"targetPosition":{"x":26321.154078058848,"y":27871.375437107363},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"89b8b2f6-b215-4e21-a2c8-4aebb11c2e92","savedPayload":{"position":{"x":26321.154078058848,"y":27871.375437107363},"targetPosition":{"x":26672.63163715052,"y":27908.31721716874},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at02hs.percentageBandwidth.movingAverage >= 30 || chart.at03hs.percentageBandwidth.movingAverage >= 30 || chart.at04hs.percentageBandwidth.movingAverage >= 30 || chart.at06hs.percentageBandwidth.movingAverage >= 30 || chart.at08hs.percentageBandwidth.movingAverage >= 30","id":"5c98aae6-6601-405b-bc15-e8242092ccd1","savedPayload":{"position":{"x":26026.912867022078,"y":27620.824698065408},"targetPosition":{"x":26349.7722520863,"y":27764.570957617074},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"895c4234-e3a8-477d-a5a9-582d81feecc0","savedPayload":{"position":{"x":26349.7722520863,"y":27764.570957617074},"targetPosition":{"x":26672.63163715052,"y":27908.31721716874},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"id":"aac48055-7ba2-43af-9b39-7bf71195a3e2","savedPayload":{"position":{"x":26672.63163715052,"y":27908.31721716874},"targetPosition":{"x":27025.560891847737,"y":27889.82097868604},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","name":"New Javascript Code","code":"true // This was added as part of my tests of Live Trading @ new exchanges. Delete if you want this Strategy to work as designed","id":"04335f5e-ebf7-4b21-9335-638e670fa17d","savedPayload":{"position":{"x":26688.715922286843,"y":27371.12521182203},"targetPosition":{"x":26736.836632224113,"y":27445.224607088316},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"id":"b3c2e935-25c4-4062-9098-50c9291e8577","savedPayload":{"position":{"x":26736.836632224113,"y":27445.224607088316},"targetPosition":{"x":26833.078052098655,"y":27593.42339762089},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}}],"id":"44cd326c-e10e-43e3-a7c0-ba8f0f0141b8","savedPayload":{"position":{"x":26833.078052098655,"y":27593.42339762089},"targetPosition":{"x":27025.560891847737,"y":27889.82097868604},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"42e0cd22-e34e-4d51-9252-10bc615949b5","savedPayload":{"position":{"x":27025.560891847737,"y":27889.82097868604},"targetPosition":{"x":27340.454711395585,"y":28050.267393634174},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"triggerOff":{"type":"Trigger Off Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"false // This was added as part of my tests of Live Trading @ new exchanges. Delete if you want this Strategy to work as designed\n// Original code follows /// chart.at01hs.candle.close > chart.at01hs.bollingerBand.movingAverage","id":"33996f6e-f51b-4667-9f26-36fc8b586eca","savedPayload":{"position":{"x":26739.795272185427,"y":26201.627726128154},"targetPosition":{"x":26903.611482879107,"y":26705.802180902523},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c5a3356f-a6a5-4f5e-8854-a9af8dc981d6","savedPayload":{"position":{"x":26903.611482879107,"y":26705.802180902523},"targetPosition":{"x":27067.427693572787,"y":27209.976635676892},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"1986b226-45b8-41f1-8a36-dee2dcd5724c","savedPayload":{"position":{"x":27067.427693572787,"y":27209.976635676892},"targetPosition":{"x":27231.243904266466,"y":27714.15109045126},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":4},"uiObject":{}}}],"announcements":[],"id":"d006b092-ccb8-4385-bc24-cd732f749b80","savedPayload":{"position":{"x":27231.243904266466,"y":27714.15109045126},"targetPosition":{"x":27340.454711395585,"y":28050.267393634174},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"takePosition":{"type":"Take Position Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage && chart.at01hs.candle.previous.close < chart.at01hs.bollingerBand.previous.movingAverage && chart.at01hs.candle.previous.previous.close < chart.at01hs.bollingerBand.previous.previous.movingAverage","id":"c370c93e-2300-48ad-acc3-42f530112d42","savedPayload":{"position":{"x":27315.274587895194,"y":26171.280337756838},"targetPosition":{"x":27428.88424047466,"y":26689.083851238393},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0bd29ab1-c151-4577-9054-7a0fc2532f7e","savedPayload":{"position":{"x":27428.88424047466,"y":26689.083851238393},"targetPosition":{"x":27542.49389305413,"y":27206.887364719947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.deviation","id":"7e5c95e6-9024-4f7a-b99b-98509b0cf8bd","savedPayload":{"position":{"x":27417.875950243015,"y":26153.995682132507},"targetPosition":{"x":27480.18492164857,"y":26680.441523426227},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0dc65813-69aa-4ce5-bbfe-dc08e6ee5fdc","savedPayload":{"position":{"x":27480.18492164857,"y":26680.441523426227},"targetPosition":{"x":27542.49389305413,"y":27206.887364719947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at02hs.bollingerChannel.direction === \"Down\"","id":"a347b184-3b9c-43d7-9c58-18e272fa7cc7","savedPayload":{"position":{"x":27521.677451503198,"y":26146.850948980642},"targetPosition":{"x":27532.085672278663,"y":26676.869156850295},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"abd817cd-4111-41fa-be60-ea1919c64715","savedPayload":{"position":{"x":27532.085672278663,"y":26676.869156850295},"targetPosition":{"x":27542.49389305413,"y":27206.887364719947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.bollingerBand.deviation > 200","id":"3e2c9f8c-1b2d-419c-9d19-934b87857165","savedPayload":{"position":{"x":27625.679426474944,"y":26149.914945987188},"targetPosition":{"x":27584.086659764536,"y":26678.401155353567},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"28755f4a-480d-45a5-8144-5fb7bbdf09f5","savedPayload":{"position":{"x":27584.086659764536,"y":26678.401155353567},"targetPosition":{"x":27542.49389305413,"y":27206.887364719947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.bollingerSubChannel.slope != \"Extreme\" || (chart.at01hs.bollingerSubChannel.period <=6 && chart.at08hs.bollingerChannel.direction === \"Down\" && chart.at06hs.bollingerChannel.direction === \"Down\" && chart.at04hs.bollingerChannel.direction === \"Down\" && chart.at02hs.bollingerSubChannel.slope === (\"Extreme\" || \"Steep\"))","id":"6dd2ef8e-c7ea-4188-b2f4-d549c336ca5d","savedPayload":{"position":{"x":27728.880279286004,"y":26163.158165186036},"targetPosition":{"x":27635.687086170066,"y":26685.02276495299},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c3b52801-d812-4aa8-8450-797dbade3f60","savedPayload":{"position":{"x":27635.687086170066,"y":26685.02276495299},"targetPosition":{"x":27542.49389305413,"y":27206.887364719947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.max - chart.at01hs.candle.min < 750","id":"0eee7c13-aee8-4238-86fd-1311ed4aa052","savedPayload":{"position":{"x":27830.28612930849,"y":26186.45306713681},"targetPosition":{"x":27686.39001118131,"y":26696.670215928378},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"542e7619-1192-474e-a8a0-fb917c49cf1e","savedPayload":{"position":{"x":27686.39001118131,"y":26696.670215928378},"targetPosition":{"x":27542.49389305413,"y":27206.887364719947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.bollingerBand.deviation < 1000","id":"990141cd-cb75-4d22-85f8-7f611655a6db","savedPayload":{"position":{"x":27928.920382772612,"y":26219.575309199437},"targetPosition":{"x":27735.70713791337,"y":26713.231336959692},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"930e07f4-c28b-4c5d-90dc-d373911dd2cb","savedPayload":{"position":{"x":27735.70713791337,"y":26713.231336959692},"targetPosition":{"x":27542.49389305413,"y":27206.887364719947},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"0b1fc454-e677-4b35-939b-1f23ae0f5045","savedPayload":{"position":{"x":27542.49389305413,"y":27206.887364719947},"targetPosition":{"x":27500.90112634372,"y":27735.373574086327},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage && chart.at01hs.candle.previous.close < chart.at01hs.bollingerBand.previous.movingAverage && chart.at01hs.candle.previous.previous.close < chart.at01hs.bollingerBand.previous.previous.movingAverage","id":"a3a29826-879d-4388-a108-39441c36102d","savedPayload":{"position":{"x":27916.560952336113,"y":26217.332622144928},"targetPosition":{"x":27829.06585055102,"y":26740.18273345474},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"98cbac39-2c67-472b-ac27-aec6ddb40bd8","savedPayload":{"position":{"x":27829.06585055102,"y":26740.18273345474},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.deviation","id":"11c71b5d-7044-4d4b-ae10-5c25cb2eabdc","savedPayload":{"position":{"x":28007.033841374345,"y":26236.563228363248},"targetPosition":{"x":27874.302295070134,"y":26749.7980365639},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"cca2a8bc-e4a0-42df-8a2e-a4a457860a54","savedPayload":{"position":{"x":27874.302295070134,"y":26749.7980365639},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.bollingerBand.previous.previous.previous.deviation > 200","id":"1e76f618-a3ac-4fb9-83eb-97de1129e93a","savedPayload":{"position":{"x":28095.48639598683,"y":26263.60588816168},"targetPosition":{"x":27918.528572376377,"y":26763.319366463118},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7a85ad66-8767-4f51-98d0-19683ad79e55","savedPayload":{"position":{"x":27918.528572376377,"y":26763.319366463118},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at02hs.bollingerChannel.direction === \"Down\"","id":"1cd7c818-45ae-447d-ad12-663c69d88d5c","savedPayload":{"position":{"x":28181.245438823855,"y":26298.254790570358},"targetPosition":{"x":27961.40809379489,"y":26780.643817667456},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"1c3eb902-1840-4ff9-a143-2040c3da6fbc","savedPayload":{"position":{"x":27961.40809379489,"y":26780.643817667456},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"if (chart.at01hs.bollingerSubChannel.slope != \"Extreme\") {true} else {if (chart.at01hs.bollingerSubChannel.period <=7) {true} else {false}}","id":"b85b9bdf-ebf6-4c3a-ab00-0b82484cff3d","savedPayload":{"position":{"x":28263.658291786705,"y":26340.246236520372},"targetPosition":{"x":28002.614520276315,"y":26801.639540642464},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2dd164fe-9197-4b40-b8c2-c73503cc1fa7","savedPayload":{"position":{"x":28002.614520276315,"y":26801.639540642464},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at02hs.percentageBandwidth.movingAverage >= 70 || chart.at02hs.percentageBandwidth.previous.movingAverage >= 70 || chart.at02hs.percentageBandwidth.previous.previous.movingAverage >= 70 || chart.at02hs.percentageBandwidth.previous.previous.previous.movingAverage >= 70 || chart.at02hs.percentageBandwidth.previous.previous.previous.previous.movingAverage >= 70 || chart.at02hs.percentageBandwidth.previous.previous.previous.previous.previous.movingAverage >= 70","id":"3cda3e1f-537c-4a9c-9fba-c221c12dd775","savedPayload":{"position":{"x":28342.09774330208,"y":26389.260645752915},"targetPosition":{"x":28041.834246034003,"y":26826.146745258735},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"508ccff3-0500-4d97-8795-84ec971e9b6e","savedPayload":{"position":{"x":28041.834246034003,"y":26826.146745258735},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.bollingerBand.deviation < 1000","id":"49fa1f6c-31c8-4903-baca-54b00ced89ad","savedPayload":{"position":{"x":28415.96682178093,"y":26444.92498901801},"targetPosition":{"x":28078.768785273427,"y":26853.978916891283},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d906e914-4695-4a87-9b40-bc09d420b3fa","savedPayload":{"position":{"x":28078.768785273427,"y":26853.978916891283},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.max - chart.at01hs.candle.min < 750","id":"cd3f2a10-647d-4971-a96e-2b7c24df3f88","savedPayload":{"position":{"x":28484.703338932653,"y":26506.815627052365},"targetPosition":{"x":28113.13704384929,"y":26884.92423590846},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"273ada5d-4442-448f-a2b4-d6505e96e201","savedPayload":{"position":{"x":28113.13704384929,"y":26884.92423590846},"targetPosition":{"x":27741.570748765924,"y":27263.032844764555},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"1cc080d9-4e49-4e5a-918a-9d0b4b06d6b1","savedPayload":{"position":{"x":27741.570748765924,"y":27263.032844764555},"targetPosition":{"x":27500.90112634372,"y":27735.373574086327},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","code":"undefined","javascriptCode":{"type":"Javascript Code","name":"New Javascript Code","code":"true // This was added as part of my tests of Live Trading @ new exchanges. Delete if you want this Strategy to work as designed\n","id":"cc092501-7af9-4183-ae0f-444565a0ed4c","savedPayload":{"position":{"x":27736.046707057925,"y":27534.540275326235},"targetPosition":{"x":27702.45448124161,"y":27563.230746577676},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"id":"cadebf25-c0bb-42e1-8bac-23c39120c5c9","savedPayload":{"position":{"x":27702.45448124161,"y":27563.230746577676},"targetPosition":{"x":27635.27002960898,"y":27620.61168908056},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}}],"id":"f926d43f-55d2-4d11-9c33-b4d2956e50a7","savedPayload":{"position":{"x":27635.27002960898,"y":27620.61168908056},"targetPosition":{"x":27500.90112634372,"y":27735.373574086327},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2},"uiObject":{}}}],"announcements":[],"id":"7ee4a63d-8296-40a7-ab94-2f408bbe162d","savedPayload":{"position":{"x":27500.90112634372,"y":27735.373574086327},"targetPosition":{"x":27340.454711395585,"y":28050.267393634174},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2},"uiObject":{}}},"id":"8f05c030-e6a1-4e65-b44d-213f0e12fd83","savedPayload":{"position":{"x":27340.454711395585,"y":28050.267393634174},"targetPosition":{"x":27558.876325653826,"y":28722.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"openStage":{"type":"Open Stage","initialDefinition":{"type":"Initial Definition","stopLoss":{"type":"Initial Stop","phases":[{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"positionRate \n/*\nif (positionRate + positionRate * 3 / 100 > 150) {positionRate + positionRate * 3 / 100} else {150}\n*/","id":"325e27c0-49b3-4f75-bae6-a7da8b747138","savedPayload":{"position":{"x":27700.60661588382,"y":27936.217251409216},"targetPosition":{"x":27755.21201944838,"y":28104.275403000673},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at01hs.candle.previous.close < positionRate - positionRate * 4 / 100","id":"bb5c170c-3e88-41fe-a96b-c77686546a45","savedPayload":{"position":{"x":27902.169655948055,"y":27412.894081506525},"targetPosition":{"x":27865.430246823136,"y":27585.739411880062},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"419d0bf5-f46a-453c-9e1b-c68aac94ce15","savedPayload":{"position":{"x":27865.430246823136,"y":27585.739411880062},"targetPosition":{"x":27828.690837698217,"y":27758.5847422536},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"a60d1fc4-6804-41a4-ab5b-0869fd254957","savedPayload":{"position":{"x":27828.690837698217,"y":27758.5847422536},"targetPosition":{"x":27791.9514285733,"y":27931.430072627136},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"6b8bfecb-d38f-4fec-b165-5a395dd96cf8","savedPayload":{"position":{"x":27791.9514285733,"y":27931.430072627136},"targetPosition":{"x":27755.21201944838,"y":28104.275403000673},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"2c76cf2c-d0b3-4e0c-a095-0cd2e35667c3","savedPayload":{"position":{"x":27755.21201944838,"y":28104.275403000673},"targetPosition":{"x":27764.46013868973,"y":28280.74003034928},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"2187bbac-923c-43ef-878f-f4bdd505ca64","savedPayload":{"position":{"x":27764.46013868973,"y":28280.74003034928},"targetPosition":{"x":27773.70825793108,"y":28457.204657697886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"takeProfit":{"type":"Initial Take Profit","phases":[{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"positionRate\n/*\nchart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.movingAverage * 0.4\n*/","id":"1455910b-3b56-4ca5-bbb5-a68978564d82","savedPayload":{"position":{"x":27865.17842730868,"y":27939.126540330017},"targetPosition":{"x":27865.17842730868,"y":28115.83333820374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at03hs.percentageBandwidth.previous.movingAverage <= 10","id":"eb95ec28-6163-4243-b3cc-9f84bbe818d6","savedPayload":{"position":{"x":28090.737080311945,"y":27450.743782982478},"targetPosition":{"x":28045.001995623144,"y":27621.42944272955},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"19e37ed7-4d46-4d54-8541-e9b8df470f15","savedPayload":{"position":{"x":28045.001995623144,"y":27621.42944272955},"targetPosition":{"x":27999.266910934344,"y":27792.115102476622},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"0609439f-0080-4227-9ce5-4f86272cab7f","savedPayload":{"position":{"x":27999.266910934344,"y":27792.115102476622},"targetPosition":{"x":27953.531826245544,"y":27962.800762223695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at03hs.percentageBandwidth.movingAverage <= 30","id":"c02650b3-da09-40de-a2ba-0dcd3a471c94","savedPayload":{"position":{"x":28255.18919917754,"y":27531.78503520533},"targetPosition":{"x":28166.83580024068,"y":27684.817611185375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a67c51ff-cc4e-4819-895d-ad4700edbdf6","savedPayload":{"position":{"x":28166.83580024068,"y":27684.817611185375},"targetPosition":{"x":28078.482401303816,"y":27837.850187165423},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.bollingerChannel.direction === \"Up\"","id":"8d7bd4e0-2bca-4f1f-848e-b85f7ab26f29","savedPayload":{"position":{"x":28384.54755326391,"y":27661.143389291698},"targetPosition":{"x":28231.514977283863,"y":27749.49678822856},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ad777013-234f-4377-bf76-8c66bf69e3d3","savedPayload":{"position":{"x":28231.514977283863,"y":27749.49678822856},"targetPosition":{"x":28078.482401303816,"y":27837.850187165423},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"id":"93197552-441f-4654-8fad-88fdcf099c11","savedPayload":{"position":{"x":28078.482401303816,"y":27837.850187165423},"targetPosition":{"x":27953.531826245544,"y":27962.800762223695},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"7b34638f-0350-43df-987c-752b1d90a1c4","savedPayload":{"position":{"x":27953.531826245544,"y":27962.800762223695},"targetPosition":{"x":27865.17842730868,"y":28115.83333820374},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"d9e9829b-0e96-421e-bfdc-06fef4b99f36","savedPayload":{"position":{"x":27865.17842730868,"y":28115.83333820374},"targetPosition":{"x":27819.44334261988,"y":28286.518997950814},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"f3939260-87b7-4954-a257-2e959cdbf750","savedPayload":{"position":{"x":27819.44334261988,"y":28286.518997950814},"targetPosition":{"x":27773.70825793108,"y":28457.204657697886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"positionSize":{"type":"Position Size","name":"Position Size","formula":{"type":"Formula","code":"balanceBaseAsset","id":"e1f74751-8a7a-44f0-bed8-bd2ea6b950d6","savedPayload":{"position":{"x":27966.19109768016,"y":28160.80707663274},"targetPosition":{"x":27869.94967780562,"y":28309.005867165313},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"aef470eb-c910-4327-b38f-0a2a75160629","savedPayload":{"position":{"x":27869.94967780562,"y":28309.005867165313},"targetPosition":{"x":27773.70825793108,"y":28457.204657697886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"positionRate":{"type":"Position Rate","name":"Position Rate","formula":{"type":"Formula","name":"My Formula","code":"candle.close","id":"30e1f940-ac96-4379-a4a2-4deec14620d0","savedPayload":{"position":{"x":28048.362206590195,"y":28234.79427541977},"targetPosition":{"x":27911.035232260638,"y":28345.999466558827},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"d50dbb9e-fd30-499d-b142-99a1fe28c88d","savedPayload":{"position":{"x":27911.035232260638,"y":28345.999466558827},"targetPosition":{"x":27773.70825793108,"y":28457.204657697886},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"7cb35db4-3b57-4733-b0aa-c70860bcef15","savedPayload":{"position":{"x":27773.70825793108,"y":28457.204657697886},"targetPosition":{"x":27701.83512815525,"y":28618.634350229997},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"openExecution":{"type":"Open Execution","id":"6d182dc1-af8d-4ac7-9589-2254ed651497","savedPayload":{"position":{"x":27877.573907701084,"y":28600.16346019931},"targetPosition":{"x":27701.83512815525,"y":28618.634350229997},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},"id":"74ce460e-e723-4d53-83a9-934a6a37dd56","savedPayload":{"position":{"x":27701.83512815525,"y":28618.634350229997},"targetPosition":{"x":27558.876325653826,"y":28722.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"manageStage":{"type":"Manage Stage","stopLoss":{"type":"Stop","phases":[{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"if (chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.movingAverage * 4 / 100 < stopLoss) {chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.movingAverage * 4 / 100} else {stopLoss}","id":"6c22a375-194a-49d8-bb00-a8bf22103525","savedPayload":{"position":{"x":28813.372085849336,"y":28498.992520885004},"targetPosition":{"x":28776.43030578796,"y":28850.470079976676},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[],"announcements":[],"id":"973915ac-44e5-42ac-9d03-9497fdb44b27","savedPayload":{"position":{"x":29062.347910790802,"y":29058.201379516682},"targetPosition":{"x":28776.43030578796,"y":28850.470079976676},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"b49ec2a3-2a5e-4d55-b23e-389f3554c50d","savedPayload":{"position":{"x":28776.43030578796,"y":28850.470079976676},"targetPosition":{"x":28453.570920723738,"y":28994.216339528342},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"9a369d8b-f429-4cb8-8bee-b89c5c92b61f","savedPayload":{"position":{"x":28453.570920723738,"y":28994.216339528342},"targetPosition":{"x":28130.711535659517,"y":29137.96259908001},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"takeProfit":{"type":"Take Profit","phases":[{"type":"Phase","name":"New Phase 1","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.movingAverage * 0.3","id":"fa97bc24-2ce8-4d4a-ac9b-2f8690c8cae3","savedPayload":{"position":{"x":28342.745580539606,"y":30048.64901680336},"targetPosition":{"x":28056.827975536762,"y":29840.917717263354},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at04hs.percentageBandwidth.previous.movingAverage <= 15","id":"80d65317-000c-41b9-b69e-6a9a5d8910f6","savedPayload":{"position":{"x":26829.96471138331,"y":30538.63826640246},"targetPosition":{"x":27131.299337746386,"y":30353.980169873314},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9b45566d-9aab-472c-bfde-22e94e52d9a1","savedPayload":{"position":{"x":27131.299337746386,"y":30353.980169873314},"targetPosition":{"x":27432.633964109464,"y":30169.322073344167},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"971cc26a-de82-431e-9b17-8d85bbd5b111","savedPayload":{"position":{"x":27432.633964109464,"y":30169.322073344167},"targetPosition":{"x":27733.96859047254,"y":29984.66397681502},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at04hs.percentageBandwidth.movingAverage <= 50","id":"8475e302-cb70-47bb-81c1-48834f1fe37d","savedPayload":{"position":{"x":26749.389890170714,"y":30372.531380137618},"targetPosition":{"x":27072.249275234935,"y":30228.78512058595},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e2523a8d-1a30-4a32-8895-1fb6b90744cc","savedPayload":{"position":{"x":27072.249275234935,"y":30228.78512058595},"targetPosition":{"x":27395.108660299156,"y":30085.038861034285},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.bollingerChannel.direction === \"Up\"","id":"57b4ab24-3a0e-49a3-b3ab-b76dd6e0500b","savedPayload":{"position":{"x":26696.983684444036,"y":30195.610994611678},"targetPosition":{"x":27046.046172371596,"y":30140.32492782298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7015bad5-00e9-4194-9840-ad54ada782c2","savedPayload":{"position":{"x":27046.046172371596,"y":30140.32492782298},"targetPosition":{"x":27395.108660299156,"y":30085.038861034285},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"34091fe6-7a1d-426a-a67c-94a6d31e4614","savedPayload":{"position":{"x":27395.108660299156,"y":30085.038861034285},"targetPosition":{"x":27733.96859047254,"y":29984.66397681502},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"66ce8ebb-a8b6-445b-83fb-5c48d92e25ef","savedPayload":{"position":{"x":27733.96859047254,"y":29984.66397681502},"targetPosition":{"x":28056.827975536762,"y":29840.917717263354},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"26e213d6-e8f7-409e-b3a5-f2826a07f10d","savedPayload":{"position":{"x":28056.827975536762,"y":29840.917717263354},"targetPosition":{"x":28093.76975559814,"y":29489.44015817168},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase 2","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.movingAverage * 0.2","id":"be41660e-6e88-4e66-8e74-62e044974953","savedPayload":{"position":{"x":28305.80380047823,"y":30400.126575895032},"targetPosition":{"x":28019.886195475385,"y":30192.395276355026},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at06hs.percentageBandwidth.previous.movingAverage <= 15","id":"f2789e09-9ec0-409f-a697-488fef0f8b58","savedPayload":{"position":{"x":26793.02293132193,"y":30890.115825494133},"targetPosition":{"x":27094.35755768501,"y":30705.457728964986},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2b7e6de2-2c6a-4fe9-974b-919780e238ea","savedPayload":{"position":{"x":27094.35755768501,"y":30705.457728964986},"targetPosition":{"x":27395.692184048086,"y":30520.79963243584},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"e08fce1d-d263-4c1c-bb1e-a5d0ede90bfc","savedPayload":{"position":{"x":27395.692184048086,"y":30520.79963243584},"targetPosition":{"x":27697.026810411164,"y":30336.141535906692},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at06hs.percentageBandwidth.movingAverage <= 50","id":"8b2740e7-f41f-490b-bfbc-da0fa4865257","savedPayload":{"position":{"x":26712.448110109337,"y":30724.00893922929},"targetPosition":{"x":27035.307495173558,"y":30580.262679677624},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"f10e85cf-e5c7-4f13-954f-32cc0a247fd9","savedPayload":{"position":{"x":27035.307495173558,"y":30580.262679677624},"targetPosition":{"x":27358.16688023778,"y":30436.516420125958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.bollingerChannel.direction === \"Up\"","id":"10074471-5735-4022-8ff8-a167e4f96e69","savedPayload":{"position":{"x":26660.04190438266,"y":30547.08855370335},"targetPosition":{"x":27009.10439231022,"y":30491.802486914654},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9111b1f9-0ff8-480a-b19c-f099114eb82e","savedPayload":{"position":{"x":27009.10439231022,"y":30491.802486914654},"targetPosition":{"x":27358.16688023778,"y":30436.516420125958},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"1b3328cc-7832-4e31-9a3f-f0557c3f3740","savedPayload":{"position":{"x":27358.16688023778,"y":30436.516420125958},"targetPosition":{"x":27697.026810411164,"y":30336.141535906692},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"cb4c3269-cfe5-4b10-8fce-3693ebce8fb3","savedPayload":{"position":{"x":27697.026810411164,"y":30336.141535906692},"targetPosition":{"x":28019.886195475385,"y":30192.395276355026},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"c45fab06-15c4-4ae3-8af9-ca3148944fb7","savedPayload":{"position":{"x":28019.886195475385,"y":30192.395276355026},"targetPosition":{"x":28056.827975536762,"y":29840.917717263354},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase 3","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.movingAverage * 0.15","id":"f257c92b-e243-4153-9b75-91abf0bcc4be","savedPayload":{"position":{"x":28268.86202041685,"y":30751.604134986705},"targetPosition":{"x":27982.944415414007,"y":30543.8728354467},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at08hs.percentageBandwidth.previous.movingAverage <= 15 && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >= 5","id":"eed4bc15-1ffe-4dfb-aa88-1bec01929e28","savedPayload":{"position":{"x":26756.081151260554,"y":31241.593384585805},"targetPosition":{"x":27057.41577762363,"y":31056.93528805666},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"712dddfd-f468-4ad5-b569-e28f243367b8","savedPayload":{"position":{"x":27057.41577762363,"y":31056.93528805666},"targetPosition":{"x":27358.75040398671,"y":30872.27719152751},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"9035c77b-e5cf-4bc8-8a84-76ecc54827f7","savedPayload":{"position":{"x":27358.75040398671,"y":30872.27719152751},"targetPosition":{"x":27660.085030349786,"y":30687.619094998365},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at08hs.percentageBandwidth.movingAverage <= 40 && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >= 3","id":"40836274-d061-4e98-84dc-5c188a0fefe4","savedPayload":{"position":{"x":26675.50633004796,"y":31075.486498320963},"targetPosition":{"x":26998.36571511218,"y":30931.740238769296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"2d0105b0-0df8-441f-852b-fb0896f88ec9","savedPayload":{"position":{"x":26998.36571511218,"y":30931.740238769296},"targetPosition":{"x":27321.2251001764,"y":30787.99397921763},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.bollingerChannel.previous.direction === \"Up\"","id":"e1001e25-b7e5-4d7b-b5ca-775cc6cda9bf","savedPayload":{"position":{"x":26623.10012432128,"y":30898.566112795022},"targetPosition":{"x":26972.16261224884,"y":30843.280046006326},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ccf1b2b7-163e-4a85-93c1-19257fd6e20a","savedPayload":{"position":{"x":26972.16261224884,"y":30843.280046006326},"targetPosition":{"x":27321.2251001764,"y":30787.99397921763},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"8b04526c-dfd1-47b4-bcad-9ba28baefe45","savedPayload":{"position":{"x":27321.2251001764,"y":30787.99397921763},"targetPosition":{"x":27660.085030349786,"y":30687.619094998365},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"78aa3111-f3c0-4feb-a53a-04180e8ba325","savedPayload":{"position":{"x":27660.085030349786,"y":30687.619094998365},"targetPosition":{"x":27982.944415414007,"y":30543.8728354467},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"fcbdcd00-6012-47ca-a8da-285cd600dd83","savedPayload":{"position":{"x":27982.944415414007,"y":30543.8728354467},"targetPosition":{"x":28019.886195475385,"y":30192.395276355026},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase 4","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.movingAverage * 0.1","id":"b00ddf15-fa75-4934-bf11-c5b99aec50a5","savedPayload":{"position":{"x":28231.920240355474,"y":31103.081694078377},"targetPosition":{"x":27946.00263535263,"y":30895.35039453837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at12hs.percentageBandwidth.previous.movingAverage <= 15 && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >= 15","id":"9d7506a6-637d-4a5e-963e-8c490b6334ae","savedPayload":{"position":{"x":26719.139371199177,"y":31593.070943677478},"targetPosition":{"x":27020.473997562254,"y":31408.41284714833},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"9faa3fee-2cf4-4b5b-963b-66b89f9f5d39","savedPayload":{"position":{"x":27020.473997562254,"y":31408.41284714833},"targetPosition":{"x":27321.80862392533,"y":31223.754750619184},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"51f36e74-2151-434f-928b-0c0c812da7fc","savedPayload":{"position":{"x":27321.80862392533,"y":31223.754750619184},"targetPosition":{"x":27623.14325028841,"y":31039.096654090037},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"(chart.at24hs.percentageBandwidth.movingAverage <= 70 || chart.at12hs.percentageBandwidth.movingAverage <= 40 ) && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >=5","id":"29020fef-a868-4770-90dd-2d7d54f62a17","savedPayload":{"position":{"x":26596.36090201408,"y":31295.132489317264},"targetPosition":{"x":26935.220832187464,"y":31194.757605098},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"0c67fea5-096a-4244-adb7-e25fb99c8fa9","savedPayload":{"position":{"x":26935.220832187464,"y":31194.757605098},"targetPosition":{"x":27274.08076236085,"y":31094.382720878733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.bollingerChannel.previous.previous.direction === \"Up\"","id":"bb21a25d-5adc-4778-90e1-8e88707f5690","savedPayload":{"position":{"x":26567.49578289107,"y":31112.885299733098},"targetPosition":{"x":26920.78827262596,"y":31103.634010305916},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"51a7a52b-b5a0-444a-9c3f-a5c1107364d4","savedPayload":{"position":{"x":26920.78827262596,"y":31103.634010305916},"targetPosition":{"x":27274.08076236085,"y":31094.382720878733},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"1ecbddb7-0567-4982-b153-3cfdf52da4ea","savedPayload":{"position":{"x":27274.08076236085,"y":31094.382720878733},"targetPosition":{"x":27623.14325028841,"y":31039.096654090037},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"27cc93d7-5c3a-4627-b3db-9063e1776674","savedPayload":{"position":{"x":27623.14325028841,"y":31039.096654090037},"targetPosition":{"x":27946.00263535263,"y":30895.35039453837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"b648d940-c4d5-4299-a857-6f1570099fe0","savedPayload":{"position":{"x":27946.00263535263,"y":30895.35039453837},"targetPosition":{"x":27982.944415414007,"y":30543.8728354467},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase 5","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.deviation * 1.75","id":"37c7be8d-2dcb-4b20-93e4-61fd7f879a6d","savedPayload":{"position":{"x":28194.978460294096,"y":31454.55925317005},"targetPosition":{"x":27909.060855291253,"y":31246.827953630043},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.percentageBandwidth.previous.movingAverage <= 30 && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >=10","id":"51509a6b-a17e-41f1-a622-1a8d828bea9f","savedPayload":{"position":{"x":26682.1975911378,"y":31944.54850276915},"targetPosition":{"x":26983.532217500877,"y":31759.890406240003},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"bbdb3a24-2801-4804-b576-9400a1090ebd","savedPayload":{"position":{"x":26983.532217500877,"y":31759.890406240003},"targetPosition":{"x":27284.866843863954,"y":31575.232309710856},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"e4b34544-1f64-4353-a5ad-2a0aea1c58c6","savedPayload":{"position":{"x":27284.866843863954,"y":31575.232309710856},"targetPosition":{"x":27586.20147022703,"y":31390.57421318171},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"(chart.at24hs.percentageBandwidth.movingAverage <= 60 || chart.at12hs.percentageBandwidth.movingAverage <= 30 ) && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >=5","id":"6a21d221-46fd-4414-ba7a-72cdb98a9414","savedPayload":{"position":{"x":26601.622769925205,"y":31778.441616504308},"targetPosition":{"x":26924.482154989426,"y":31634.69535695264},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"c44b4d80-d0d6-4203-b3d8-980cf6f9cd68","savedPayload":{"position":{"x":26924.482154989426,"y":31634.69535695264},"targetPosition":{"x":27247.341540053647,"y":31490.949097400975},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.bollingerChannel.previous.previous.direction === \"Up\"","id":"5eb799d7-3e87-497d-81cc-7f71c1cb6ff9","savedPayload":{"position":{"x":26549.216564198527,"y":31601.521230978367},"targetPosition":{"x":26898.279052126087,"y":31546.23516418967},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3d5f2f28-18ab-492c-92c3-83935147c659","savedPayload":{"position":{"x":26898.279052126087,"y":31546.23516418967},"targetPosition":{"x":27247.341540053647,"y":31490.949097400975},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"18043e6e-16ab-4838-bb12-29838d4cce6b","savedPayload":{"position":{"x":27247.341540053647,"y":31490.949097400975},"targetPosition":{"x":27586.20147022703,"y":31390.57421318171},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"5fee368f-24dd-427d-bd52-a53ecfb4459d","savedPayload":{"position":{"x":27586.20147022703,"y":31390.57421318171},"targetPosition":{"x":27909.060855291253,"y":31246.827953630043},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"68fdfa18-7975-4055-89bc-53b9b616d7cf","savedPayload":{"position":{"x":27909.060855291253,"y":31246.827953630043},"targetPosition":{"x":27946.00263535263,"y":30895.35039453837},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase 6","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.deviation * 1.25","id":"7fac596f-9e80-469a-91f7-8c60321ce178","savedPayload":{"position":{"x":28158.03668023272,"y":31806.036812261722},"targetPosition":{"x":27872.119075229875,"y":31598.305512721716},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"nextPhaseEvent":{"type":"Next Phase Event","situations":[{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.percentageBandwidth.previous.movingAverage <= 30 && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >=10","id":"461b716a-4ac7-46b7-b0b7-57fe129a34e9","savedPayload":{"position":{"x":26645.255811076422,"y":32296.026061860823},"targetPosition":{"x":26946.5904374395,"y":32111.367965331676},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ec86d2bb-5e22-4f31-aeee-9ff98dd3f006","savedPayload":{"position":{"x":26946.5904374395,"y":32111.367965331676},"targetPosition":{"x":27247.925063802577,"y":31926.70986880253},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"d7a37efb-0d64-40ad-8e83-da1f3da2f2db","savedPayload":{"position":{"x":27247.925063802577,"y":31926.70986880253},"targetPosition":{"x":27549.259690165654,"y":31742.051772273382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Situation","name":"New Situation","conditions":[{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"(chart.at24hs.percentageBandwidth.movingAverage <= 50 || chart.at12hs.percentageBandwidth.movingAverage <= 17.5 ) && chart.at01hs.bollingerSubChannel.slope === \"Extreme\" && chart.at01hs.bollingerSubChannel.period >=5","id":"b0e55bcf-7dde-4ae3-a63b-ce82f1f0e9a5","savedPayload":{"position":{"x":26564.680989863828,"y":32129.91917559598},"targetPosition":{"x":26887.54037492805,"y":31986.172916044314},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"e3fba252-1cc9-48b1-b9cc-bb6a32faf7b3","savedPayload":{"position":{"x":26887.54037492805,"y":31986.172916044314},"targetPosition":{"x":27210.39975999227,"y":31842.426656492647},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},{"type":"Condition","name":"New Condition","javascriptCode":{"type":"Javascript Code","code":"chart.at24hs.bollingerChannel.previous.previous.direction === \"Up\"","id":"42a3cac4-9fe8-4791-92ab-2805c4a57798","savedPayload":{"position":{"x":26512.27478413715,"y":31952.99879007004},"targetPosition":{"x":26861.33727206471,"y":31897.712723281344},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"ee9a30e4-4128-4911-b03c-e1aebc1aa173","savedPayload":{"position":{"x":26861.33727206471,"y":31897.712723281344},"targetPosition":{"x":27210.39975999227,"y":31842.426656492647},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"id":"684a6ca0-83ad-4856-a759-fcd342f4ac8c","savedPayload":{"position":{"x":27210.39975999227,"y":31842.426656492647},"targetPosition":{"x":27549.259690165654,"y":31742.051772273382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}}],"announcements":[],"id":"1e0fe692-2762-4a39-bcd2-292d4156874a","savedPayload":{"position":{"x":27549.259690165654,"y":31742.051772273382},"targetPosition":{"x":27872.119075229875,"y":31598.305512721716},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"9a426f1b-c3fd-4ec5-aa9e-7c1198587936","savedPayload":{"position":{"x":27872.119075229875,"y":31598.305512721716},"targetPosition":{"x":27909.060855291253,"y":31246.827953630043},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Phase","name":"New Phase","formula":{"type":"Formula","code":"chart.at01hs.bollingerBand.movingAverage - chart.at01hs.bollingerBand.deviation","id":"08b4002d-8c18-418a-9772-598e73d909da","savedPayload":{"position":{"x":27798.23551510712,"y":32301.26063090506},"targetPosition":{"x":27835.177295168498,"y":31949.78307181339},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"announcements":[],"id":"35945d37-6095-4690-b29c-7dd41c2ad489","savedPayload":{"position":{"x":27835.177295168498,"y":31949.78307181339},"targetPosition":{"x":27872.119075229875,"y":31598.305512721716},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"id":"4710a2d0-16e7-40d3-b0c3-b2116c569f8d","savedPayload":{"position":{"x":28093.76975559814,"y":29489.44015817168},"targetPosition":{"x":28130.711535659517,"y":29137.96259908001},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"0301fddf-32a4-4b93-beda-a5ca48eb26b1","savedPayload":{"position":{"x":28130.711535659517,"y":29137.96259908001},"targetPosition":{"x":27558.876325653826,"y":28722.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"closeStage":{"type":"Close Stage","closeExecution":{"type":"Close Execution","id":"a5785489-49fe-4a19-8671-c1d875a1dee4","savedPayload":{"position":{"x":27340.45471139559,"y":29394.732606365826},"targetPosition":{"x":27449.665518524707,"y":29058.616303182913},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"927e7d87-c522-4bf4-a872-04d5a18180ff","savedPayload":{"position":{"x":27449.665518524707,"y":29058.616303182913},"targetPosition":{"x":27558.876325653826,"y":28722.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2},"uiObject":{}}},"id":"a04f9080-32e5-4e0b-8896-713016ba3fda","savedPayload":{"position":{"x":27558.876325653826,"y":28722.5},"targetPosition":{"x":26852.049134158926,"y":28722.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}}],"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"","code":"{ \n\"name\": \"BTC\",\n\"initialBalance\": 0.1,\n\"minimumBalance\": 0.001,\n\"maximumBalance\": 100\n}","id":"346fb744-2838-4913-99eb-3f68e7be0407","savedPayload":{"position":{"x":26328.327945827372,"y":28857.447369881},"targetPosition":{"x":26284.480887398207,"y":28722.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"timeRange":{"type":"Time Range","name":"Time Range","code":"{\n\"initialDatetime\": \"2019-07-01T00:00:00.000Z\",\n\"finalDatetime\": \"2019-08-01T00:00:00.000Z\"\n}","id":"e4604c77-6bdf-4be6-a7bd-10a9fca4135c","savedPayload":{"position":{"x":26169.687798123952,"y":28805.902061278863},"targetPosition":{"x":26284.480887398207,"y":28722.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"fbd3a8c4-08e6-4ee3-a9ec-9a87e1c6102f","savedPayload":{"position":{"x":26169.687798123952,"y":28639.097938721137},"targetPosition":{"x":26284.480887398207,"y":28722.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"df98a4fc-c1ad-4eee-bf9e-d8a6825ed2fe","savedPayload":{"position":{"x":26328.327945827372,"y":28587.552630119},"targetPosition":{"x":26284.480887398207,"y":28722.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":1},"uiObject":{}}},"id":"5a021e03-57ff-4915-a551-c6ef14c963c3","savedPayload":{"position":{"x":26284.480887398207,"y":28722.5},"targetPosition":{"x":26852.049134158926,"y":28722.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"a239a374-497a-43f9-8382-5277410b1c2d","savedPayload":{"position":{"x":26852.049134158926,"y":28722.5},"targetPosition":{"x":26852.049134158926,"y":28722.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Task for Debugging -Live Trading WHB @ Binance","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Live Trading Session","name":"Live Trading WHB","code":"{\n\"folderName\": \"Live-WHB\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Bitcoin","code":"{\n \"initialBalance\": 0.002,\n \"minimumBalance\": 0.0001,\n \"maximumBalance\": 0.1\n}","id":"3f95798e-47a9-4fac-8943-1137a494592f","savedPayload":{"position":{"x":26536.664569897555,"y":24970.34240502993},"targetPosition":{"x":26385.788076712546,"y":25231.668156894186},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Bitcoin","id":"537ea617-72a2-4a68-af3c-6c21897b7f55"}}},"quotedAsset":{"type":"Quoted Asset","name":"Tether","code":"{\n\"initialBalance\": 0,\n\"minimumBalance\": 0,\n\"maximumBalance\": 100\n}","id":"b7851815-f763-42ea-a1fe-2356e273d1e0","savedPayload":{"position":{"x":26599.15965961808,"y":25018.296573988653},"targetPosition":{"x":26385.788076712546,"y":25231.668156894186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Tether","id":"7fd8a533-ea74-4dc3-8b01-11c232ef1512"}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"e0c6fb36-2810-4061-bb01-e190aae73583","savedPayload":{"position":{"x":26647.113828576803,"y":25080.791663709177},"targetPosition":{"x":26385.788076712546,"y":25231.668156894186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"0c88b3c3-4ca2-4669-a76e-f5d4dfc5ca0f","savedPayload":{"position":{"x":26677.2590794072,"y":25153.568737105063},"targetPosition":{"x":26385.788076712546,"y":25231.668156894186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"a5b819a4-f9a8-4206-8dc0-418a8c78e808","savedPayload":{"position":{"x":26687.541063082565,"y":25231.668156894186},"targetPosition":{"x":26385.788076712546,"y":25231.668156894186},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"cc46d6a6-f2d5-465a-a30a-59b193587e6c","savedPayload":{"position":{"x":26385.788076712546,"y":25231.668156894186},"targetPosition":{"x":26124.46232484829,"y":25382.544650079195},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"id":"2327ab2a-13d0-4834-a979-a017bcff5829","savedPayload":{"position":{"x":26124.46232484829,"y":25382.544650079195},"targetPosition":{"x":25863.136572984033,"y":25533.421143264204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","id":"a239a374-497a-43f9-8382-5277410b1c2d"}}},"marketReference":{"type":"Market Reference","name":"Binance BTC/USDT","keyInstance":{"type":"Key Instance","name":"Key 1","id":"234ce9d0-1da8-4845-81bc-5ff2e1532b09","savedPayload":{"position":{"x":26385.788076712546,"y":25835.174129634222},"targetPosition":{"x":26124.46232484829,"y":25684.297636449213},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Key","name":"Key 1","id":"d2893836-c3d5-449f-a132-971542ef0c0d"}}},"id":"0bc317a1-56ce-495e-89df-525189e45f83","savedPayload":{"position":{"x":26124.46232484829,"y":25684.297636449213},"targetPosition":{"x":25863.136572984033,"y":25533.421143264204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"6062ad43-605a-4508-b06a-f1d5bd9a4c4f"}}},"id":"5fafbdbf-1115-4bd2-9f5f-8ff22344b3b8","savedPayload":{"position":{"x":25863.136572984033,"y":25533.421143264204},"targetPosition":{"x":25561.383586614014,"y":25533.421143264204},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"f365449e-b6f3-442f-8e72-3c2a8b9facb9","savedPayload":{"position":{"x":25561.383586614014,"y":25533.421143264204},"targetPosition":{"x":25259.630600243996,"y":25533.421143264204},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"7c01efbc-eabb-417f-be35-3151cad424c9","savedPayload":{"position":{"x":25259.630600243996,"y":25533.421143264204},"targetPosition":{"x":25259.630600243996,"y":25533.421143264204},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Task for Debugging -Live Trading WHB @ Bitfinex","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Live Trading Session","name":"Live Trading WHB","code":"{\n\"folderName\": \"Live-WHB\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Bitcoin","code":"{\n \"initialBalance\": 0.002,\n \"minimumBalance\": 0.0001,\n \"maximumBalance\": 0.1\n}","id":"f2eee43f-68de-4f02-b7a2-f22136507689","savedPayload":{"position":{"x":26550.37724569572,"y":25421.12535878779},"targetPosition":{"x":26401.391345522003,"y":25680.193967549203},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Bitcoin","id":"ece1e81f-6b55-4696-b0fb-27136f930db3"}}},"quotedAsset":{"type":"Quoted Asset","name":"Tether","code":"{\n\"initialBalance\": 0,\n\"minimumBalance\": 0,\n\"maximumBalance\": 100\n}","id":"b89ade92-13fb-4e26-8a16-c592b3c9145f","savedPayload":{"position":{"x":26613.38622410859,"y":25468.199088962614},"targetPosition":{"x":26401.391345522003,"y":25680.193967549203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Tether","id":"977da1ed-2bdd-49c3-9a03-51f8c5cd26e9"}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"2b2cc82c-0286-4b9c-bb65-dc878a88086e","savedPayload":{"position":{"x":26661.03098583221,"y":25530.290951323805},"targetPosition":{"x":26401.391345522003,"y":25680.193967549203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"62c6f79e-0c71-417e-a2ff-f32abd4dc912","savedPayload":{"position":{"x":26690.981735143483,"y":25602.59845651431},"targetPosition":{"x":26401.391345522003,"y":25680.193967549203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"672f08f4-d723-4562-ac41-7cb006bcd0f8","savedPayload":{"position":{"x":26701.197377972796,"y":25680.193967549203},"targetPosition":{"x":26401.391345522003,"y":25680.193967549203},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"419ead68-f8e6-4dbf-84c4-dc3ebf942b05","savedPayload":{"position":{"x":26401.391345522003,"y":25680.193967549203},"targetPosition":{"x":26141.751705211795,"y":25830.0969837746},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"id":"b8813ec9-6e81-4ee3-87a3-7e44e5c79fb3","savedPayload":{"position":{"x":26141.751705211795,"y":25830.0969837746},"targetPosition":{"x":25882.112064901587,"y":25980},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","id":"a239a374-497a-43f9-8382-5277410b1c2d"}}},"marketReference":{"type":"Market Reference","name":"Bitfinex BTC/USDT","keyInstance":{"type":"Key Instance","name":"New Exchange Account Key","id":"2cc7b063-89a0-4a7a-bd41-322c305cd830","savedPayload":{"position":{"x":26460.92445293962,"y":26135.319372398422},"targetPosition":{"x":26171.33406331814,"y":26057.72386136353},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Key","name":"New Exchange Account Key","id":"cb05f082-72d0-43e5-8253-a9d409330146"}}},"id":"60ccbb55-21ee-48d0-8e81-331a872dc91f","savedPayload":{"position":{"x":26171.33406331814,"y":26057.72386136353},"targetPosition":{"x":25882.112064901587,"y":25980},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"b734f59f-f29c-450b-a6e4-6d2345a1082a"}}},"id":"d0ebb65c-25c5-4a2f-9067-a2349246d632","savedPayload":{"position":{"x":25882.112064901587,"y":25980},"targetPosition":{"x":25582.306032450793,"y":25980},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"9f15a1f1-1f52-4bd5-ab3d-ed7e883c9f07","savedPayload":{"position":{"x":25582.306032450793,"y":25980},"targetPosition":{"x":25282.5,"y":25980},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"3b51b2ce-e410-4cb0-a3f5-b064405e4b23","savedPayload":{"position":{"x":25282.5,"y":25980},"targetPosition":{"x":25282.5,"y":25980},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Task","name":"Task for Debugging -Live Trading WHB @ Bitmex","bot":{"type":"Trading Bot Instance","code":" {}","processes":[{"type":"Trading Process Instance","name":"Multi-Period","code":" {\n \"process\": \"Multi-Period\"\n }","session":{"type":"Live Trading Session","name":"Live Trading WHB","code":"{\n\"folderName\": \"Live-WHB\"\n}","parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Bitcoin","code":"{\n \"initialBalance\": 0.01,\n \"minimumBalance\": 0.0001,\n \"maximumBalance\": 0.1\n}","id":"c8231bdb-4141-4ae0-a4f8-c141e8e744f7","savedPayload":{"position":{"x":26530.758952071727,"y":25816.647847266002},"targetPosition":{"x":26381.96571845036,"y":26077.162254478808},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"Bitcoin","id":"3224c8e5-1a8f-42be-bd31-c20bb0a9df7c"}}},"quotedAsset":{"type":"Quoted Asset","name":"US Dollar","code":"{\n\"initialBalance\": 0,\n\"minimumBalance\": 0,\n\"maximumBalance\": 100\n}","id":"0979c041-fc28-4072-b1cb-71b6d2173681","savedPayload":{"position":{"x":26593.825214877546,"y":25864.617997047277},"targetPosition":{"x":26381.96571845036,"y":26077.162254478808},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Asset","name":"US Dollar","id":"9d778a04-e921-4a42-8b45-25e94155df33"}}},"timeFrame":{"type":"Time Frame","name":"Time Frame","code":"{\n\"value\": \"01-min\"\n}","id":"7d1bc479-599f-4517-898a-e63a57bc2d7c","savedPayload":{"position":{"x":26641.675592859767,"y":25926.904577625304},"targetPosition":{"x":26381.96571845036,"y":26077.162254478808},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"slippage":{"type":"Slippage","name":"Slippage","code":"{\n\"positionRate\": 0.1,\n\"stopLoss\": 0.1,\n\"takeProfit\": 0.1\n}","id":"692354a1-ccca-4572-9a4b-4dce017fcc64","savedPayload":{"position":{"x":26671.37537754458,"y":25999.491672602286},"targetPosition":{"x":26381.96571845036,"y":26077.162254478808},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":"{\n\"maker\": 0.15,\n\"taker\": 0.25\n}","id":"e0d7552a-c4cc-423e-95f4-84bd5220e6c7","savedPayload":{"position":{"x":26680.738739767683,"y":26078.254578704324},"targetPosition":{"x":26381.96571845036,"y":26077.162254478808},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},"id":"6a8d9fb3-fdea-445b-905e-d9ea6ebcdd4d","savedPayload":{"position":{"x":26381.96571845036,"y":26077.162254478808},"targetPosition":{"x":26122.697253392813,"y":26226.81611076928},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{"shortcutKey":""}}},"id":"a15bdeb7-f697-448c-8895-8e95c1383f1e","savedPayload":{"position":{"x":26122.697253392813,"y":26226.81611076928},"targetPosition":{"x":25863.33896599368,"y":26376.53841482277},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3},"uiObject":{"shortcutKey":""},"referenceParent":{"type":"Trading System","name":"Weak-hands buster - BTC - SDA v.0.0.3","id":"a239a374-497a-43f9-8382-5277410b1c2d"}}},"marketReference":{"type":"Market Reference","name":"Bitmex BTC/USDT","keyInstance":{"type":"Key Instance","name":"New Exchange Account Key","id":"4af883fc-cd5b-46d1-871a-87c85edef0c6","savedPayload":{"position":{"x":26442.11236090972,"y":26532.034129405623},"targetPosition":{"x":26152.775911766916,"y":26454.36514052807},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Exchange Account Key","name":"New Exchange Account Key","id":"a4da723a-efb5-468d-9a06-af80124a3934"}}},"id":"215faa6e-2c13-4533-930e-58031335b8a8","savedPayload":{"position":{"x":26152.775911766916,"y":26454.36514052807},"targetPosition":{"x":25863.33896599368,"y":26376.53841482277},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Market","name":"BTC/USDT","id":"2dbc8c9c-ba14-4340-8b97-8d358d32df6b"}}},"id":"da3a75f7-1d65-41f5-be58-dde3d79897d3","savedPayload":{"position":{"x":25863.33896599368,"y":26376.53841482277},"targetPosition":{"x":25563.806032450793,"y":26376.5},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{},"referenceParent":{"type":"Process Definition","name":"Multi-Period","id":"6f95cc67-bad0-48dc-bb82-0075c43cb202"}}}],"id":"4368283b-ff99-40a7-8710-8ac2925ac12e","savedPayload":{"position":{"x":25563.806032450793,"y":26376.5},"targetPosition":{"x":25264,"y":26376.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"448a6180-ad5b-4b67-89ec-db88a96b6194","savedPayload":{"position":{"x":25264,"y":26376.5},"targetPosition":{"x":25264,"y":26376.5},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 01","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 2,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"c94d9770-d5ba-43ff-a93f-fc04a4fb635b","savedPayload":{"position":{"x":23520.444849099018,"y":23700.50369171384},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"6e33e35c-8107-4ca4-affd-c54b61ab27bd"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\"}","id":"526f819e-cb98-4846-bc92-8eef18e1de8a","savedPayload":{"position":{"x":23576.844960765207,"y":23798.191550672243},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"499cfa98-062c-467f-b5b8-7cc935dc7caf"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"e71457ef-64f5-4f16-bf7f-91481b3ded63","savedPayload":{"position":{"x":23615.42490931652,"y":23904.18908816069},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"3d597096-5047-4c1c-9dd6-4a718e0fc207"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\"status\":\"off\"}","id":"2d0e4eb8-678a-4d63-adca-d8ed001c0e75","savedPayload":{"position":{"x":23635.01246253861,"y":24015.27562263993},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"c1ea8a7a-8178-45ba-816c-014039b9cc9b"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\"status\":\"off\",\"showPanels\":true}","id":"3e9f78d7-5608-4f30-b0d7-e270403e6032","savedPayload":{"position":{"x":23635.01246253861,"y":24128.075845972315},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"042f75fe-2c93-43dd-a2fb-56c9f5c28bb6"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{\"status\":\"off\",\"showPanels\":true}","id":"bdc8ef14-6b43-4d8a-b56e-9f7709c599c3","savedPayload":{"position":{"x":23615.42490931652,"y":24239.162380451555},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"a786bd1e-f68d-41f3-88c7-67d658096f90"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\"status\":\"off\",\"showPanels\":true}","id":"568b003e-57d7-4c35-af11-3e30660c3181","savedPayload":{"position":{"x":23576.844960765207,"y":24345.15991794},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"2f60b6e6-c460-4b25-b5b7-b824ce38f63b"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\"status\":\"off\",\"showPanels\":true}","id":"f11d21c8-ce22-4387-a4be-f51681ad92c5","savedPayload":{"position":{"x":23520.444849099018,"y":24442.8477768984},"targetPosition":{"x":22990.35623631245,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"a6578dfd-194a-4ea9-bd4f-35545816d9ad"}}}],"id":"5bf0b7b8-e3eb-457e-9f29-03b5a0c87c27","savedPayload":{"position":{"x":22990.35623631245,"y":24071.67573430612},"targetPosition":{"x":22558.943764124684,"y":24071.67573430612},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"494bee2e-e884-46c1-bba3-3939511932ca","savedPayload":{"position":{"x":22558.943764124684,"y":24071.67573430612},"targetPosition":{"x":22558.943764124684,"y":24071.67573430612},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 03","rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"minValue\": 414.140001,\n \"maxValue\": 19903.4402144,\n \"autoMinScale\": true,\n \"autoMaxScale\": true\n}","id":"cd54273e-fc3c-41e1-bfe9-d1136aa3ed66","savedPayload":{"position":{"x":23011.113712767674,"y":24565.698751652835},"targetPosition":{"x":22664.845181282166,"y":24451.9375468143},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 0,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\"status\":\"off\",\"showPanels\":false}","id":"175d62d3-8710-47a2-aba0-a5fc406798ff","savedPayload":{"position":{"x":21613.240586579057,"y":24857.892601379634},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"19c78a01-0c97-48eb-a8d5-09ca3c42d9fd"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\"}","id":"1a6b940f-24c1-4854-beb6-31f20eb5a1ee","savedPayload":{"position":{"x":21551.555147532963,"y":24751.050286864607},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"23311eba-8ecd-41c9-aeed-86d7045b7be7"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"842a0e34-fd1c-43b1-9af0-015605442010","savedPayload":{"position":{"x":21509.359822125658,"y":24635.119583101503},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"1f860844-028c-41c8-a7df-f764306947a0"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\"status\":\"off\"}","id":"b1501597-74f8-4b16-8caa-0a4096e74293","savedPayload":{"position":{"x":21487.936693967782,"y":24513.622985860395},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"a4267a80-ee05-41e7-ad62-30e092a91812"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\"status\":\"off\"}","id":"7db184de-2609-49be-beb4-72f7acd30392","savedPayload":{"position":{"x":21487.936693967782,"y":24390.252107768207},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"49c9d3df-b50e-4218-b585-0785f895ad9c"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{\"status\":\"off\"}","id":"b1735d26-97c6-492c-bf01-004ef9ccd74f","savedPayload":{"position":{"x":21509.359822125658,"y":24268.7555105271},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"699b13e8-75e0-4730-8dc8-c0797fb894ac"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\"status\":\"off\"}","id":"9aaf0608-bba3-4512-b513-e075e8da7045","savedPayload":{"position":{"x":21551.555147532963,"y":24152.824806763994},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"b31d164e-1b77-450f-a2c1-67b19b67c482"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\"status\":\"off\"}","id":"2e05b3dd-bf52-4150-b855-36a6dbc28e37","savedPayload":{"position":{"x":21613.240586579057,"y":24045.982492248968},"targetPosition":{"x":22193.00448858342,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"039ef46f-756f-4af9-8aad-0bde8230c53f"}}}],"id":"7e449c12-24b3-4e70-a8a9-b4fd4cc150c0","savedPayload":{"position":{"x":22193.00448858342,"y":24451.9375468143},"targetPosition":{"x":22664.845181282166,"y":24451.9375468143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"573b74ce-c217-4e21-b240-455e55653316","savedPayload":{"position":{"x":22664.845181282166,"y":24451.9375468143},"targetPosition":{"x":22664.845181282166,"y":24451.9375468143},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 04","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 1,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\"status\":\"off\"}","id":"d615b88c-95a0-494d-bf48-7b16881edf5d","savedPayload":{"position":{"x":23540.069657742937,"y":24052.42511345858},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"276e341d-6cad-45fa-8d56-8be162013ca7"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\"}","id":"056b9e67-6924-4ae3-8014-b10610ec841d","savedPayload":{"position":{"x":23601.41029110941,"y":24158.670207017763},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"dc7512a4-5c3f-4d91-bf2d-8730c6c5c7fe"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"4b8cd243-ed4e-4de3-b4f3-1967896d27e0","savedPayload":{"position":{"x":23643.369755540785,"y":24273.952888075382},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"84c28b2f-52f3-4d6f-9754-00b3da6e91af"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\"status\":\"off\"}","id":"fed6c9f6-4df6-4f17-9a7c-b4aa8dfa778d","savedPayload":{"position":{"x":23664.673133942833,"y":24394.770350703348},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"7bafe96e-4bba-4a79-9cf9-21055feaa862"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\"status\":\"off\"}","id":"b2f47042-0a68-49c4-8011-d506499a47d6","savedPayload":{"position":{"x":23664.673133942833,"y":24517.45161743629},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"b4e6199d-7047-4ee7-87ad-4722464e27cf"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{\"status\":\"off\"}","id":"2d782ad6-b911-47a4-8aad-05119470bd32","savedPayload":{"position":{"x":23643.369755540785,"y":24638.269080064256},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"073c035c-0f1d-4cd8-b3ea-d99ca30edff3"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\"status\":\"off\"}","id":"3cd0b919-d3e3-4db2-b5cf-17250219a905","savedPayload":{"position":{"x":23601.41029110941,"y":24753.551761121875},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6b994fd7-655f-4cac-8893-823eab0dd8ed"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\"status\":\"off\"}","id":"f4e64053-52dd-49cb-af0f-2ec752b0970f","savedPayload":{"position":{"x":23540.069657742937,"y":24859.79685468106},"targetPosition":{"x":22963.546486279538,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"690cc9af-8154-4b0d-8fd3-38a2be809b9d"}}}],"id":"fcdb42e1-cc9a-4a68-a682-84115998b294","savedPayload":{"position":{"x":22963.546486279538,"y":24456.11098406982},"targetPosition":{"x":22494.343261243084,"y":24456.11098406982},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"b1d32a10-1074-461d-a271-4dd8d5a38526","savedPayload":{"position":{"x":22494.343261243084,"y":24456.11098406982},"targetPosition":{"x":22494.343261243084,"y":24456.11098406982},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 05","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 2,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\"status\":\"off\"}","id":"788e5eac-f585-41f6-a6a8-f06c0d92d1a8","savedPayload":{"position":{"x":23551.553916234,"y":23926.7242601367},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"4ec36066-027d-4eb7-bbe9-52adeb6a76f2"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\"}","id":"d5dcecbd-05d6-4948-a5f0-409238732d2c","savedPayload":{"position":{"x":23602.251087346907,"y":24014.534336304274},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"79b93fca-2746-4df3-9831-c36a6aa855cb"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"cc880d06-5e82-47b7-8cba-7f3227fb57fa","savedPayload":{"position":{"x":23636.929994807397,"y":24109.81385148332},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"3229c3dd-abe8-40f2-8bff-a982f31ccbc8"}}},{"type":"Layer","name":"Volume-Stairs","code":"{\"status\":\"off\"}","id":"a9b6e590-e863-4102-ac79-38a1df4813ff","savedPayload":{"position":{"x":23654.536937560646,"y":24209.667785818878},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"386d17a3-ca20-4a30-a8ed-84fa3397daa2"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\"status\":\"off\"}","id":"24690e81-aa81-4094-a990-b04cf5942ca5","savedPayload":{"position":{"x":23654.536937560646,"y":24311.0621280447},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"c7913df8-85e1-4cd2-a666-1a9ff987dc04"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"e713340d-9e5b-4e4b-a519-9fef4d8c7fec","savedPayload":{"position":{"x":23636.929994807397,"y":24410.91606238026},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"38f60687-06b7-4475-b914-ebed74ba29d2"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{ \n\"status\": \"off\"\n}","id":"675a47f0-d53c-4def-b2dc-4602f3cdc043","savedPayload":{"position":{"x":23602.251087346907,"y":24506.195577559305},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"fb98f659-fc48-4d9d-8093-6a8e83b9c394"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{ \n\"status\": \"off\"\n}","id":"aad750e5-709c-49cf-9d48-31c9ae649b17","savedPayload":{"position":{"x":23551.553916234,"y":24594.00565372688},"targetPosition":{"x":23075.06562013804,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"75264d4c-647f-47c8-b445-56dc3255c1d8"}}}],"id":"4caa95c3-3e97-4e8c-b669-ef8e7b53ab89","savedPayload":{"position":{"x":23075.06562013804,"y":24260.36495693179},"targetPosition":{"x":22687.275750993915,"y":24260.36495693179},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"81d4f33e-a8d4-4078-9874-bd5b46c6e84d","savedPayload":{"position":{"x":22687.275750993915,"y":24260.36495693179},"targetPosition":{"x":22687.275750993915,"y":24260.36495693179},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 06","layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 3,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"off\",\n \"showPanels\": false\n}","id":"e56c5a99-fcd6-4326-8b10-72c095759ac9","savedPayload":{"position":{"x":23516.427436339258,"y":23863.541493883702},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"d5399ddc-ada6-44f1-a5f2-6488d0ac3c0b"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\"}","id":"2ebf28ac-a9ee-433c-ad1a-5d410ce1d067","savedPayload":{"position":{"x":23576.505710082845,"y":23967.600116438633},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"a3af37cc-6af5-4497-a818-ce500c5999ea"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"f6171610-7f5f-41fb-90e2-f8d0a6e19443","savedPayload":{"position":{"x":23617.60166967593,"y":24080.510337451447},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"cbc5ab4a-c6db-4cf7-bdbd-739ba15a7e5d"}}},{"type":"Layer","name":"Volume-Stairs","code":"{ \n\"status\": \"off\"\n}","id":"e263ecf2-b6b4-4f3d-8ba7-dfb81f85e54e","savedPayload":{"position":{"x":23638.466635181827,"y":24198.841436992},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"54e00d9b-202d-4cdf-9638-81be6e16719a"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\"status\":\"off\"}","id":"fc751bc6-10a2-4ee3-82ed-48769873f93a","savedPayload":{"position":{"x":23638.466635181827,"y":24318.99798447918},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"dc1cae8d-cf3c-4ac2-9419-67d2399fef5b"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"d3d1ac2d-5879-4d57-8b2c-556c1653a1db","savedPayload":{"position":{"x":23617.60166967593,"y":24437.329084019733},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4cbc0c33-2abe-44f6-8862-ac17cee611ae"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{ \n\"status\": \"off\"\n}","id":"b6a39479-b1bf-49fc-94fe-7caf6ca190e2","savedPayload":{"position":{"x":23576.505710082845,"y":24550.239305032548},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"1eaf4681-66c5-4619-9782-6593baf891d6"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{ \n\"status\": \"off\"\n}","id":"6c4b89c2-1922-46e5-b5cb-cc011d2b4bbd","savedPayload":{"position":{"x":23516.427436339258,"y":24654.29792758748},"targetPosition":{"x":22951.76882403298,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"47730810-5628-4d58-922f-0d242f2c9f5d"}}}],"id":"07001adc-e18a-4488-9f65-701f31d47b14","savedPayload":{"position":{"x":22951.76882403298,"y":24258.91971073559},"targetPosition":{"x":22492.221567213775,"y":24258.91971073559},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3},"uiObject":{}}},"id":"be3a0a59-3f90-41c1-89ad-40cb2d52332f","savedPayload":{"position":{"x":22492.221567213775,"y":24258.91971073559},"targetPosition":{"x":22492.221567213775,"y":24258.91971073559},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 02D","rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"minValue\": 7175.776732815815,\n \"maxValue\": 26835.622620978218\n}","id":"d7217775-c551-47f2-a96d-d7d394e873ac","savedPayload":{"position":{"x":22231.578653718898,"y":24437.670532522352},"targetPosition":{"x":22270.540970939346,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 0,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": true\n}","id":"bfa41b92-106e-4520-8e41-aeee83ae8ec4","savedPayload":{"position":{"x":22089.869034219497,"y":24491.27731009218},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\",\"showPanels\":false}","id":"ef1caf41-fb38-4f38-8c64-9487560ed116","savedPayload":{"position":{"x":22079.271108158686,"y":24472.921163699997},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"f2e891e3-574e-4b61-af53-34f9875cc8df","savedPayload":{"position":{"x":22072.02169977814,"y":24453.00357787004},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9"}}},{"type":"Layer","name":"Volume-Stairs","code":"{ \n\"status\": \"off\"\n}","id":"3a8fc31a-607b-4d9c-a0c5-0b9a00a7b604","savedPayload":{"position":{"x":22068.341078683123,"y":24432.12973836897},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"29371c3f-21ac-4bc7-b8ad-be73eb254754","savedPayload":{"position":{"x":22068.341078683123,"y":24410.93388624735},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"6b0f6e98-14a0-4c99-a006-1f808ed81a43","savedPayload":{"position":{"x":22072.02169977814,"y":24390.06004674628},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\n \"status\": \"on\",\n \"showPanels\": true\n}","id":"bbb7a9a0-7226-41a7-8add-ea6475aca3db","savedPayload":{"position":{"x":22079.271108158686,"y":24370.142460916322},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{ \n\"status\": \"off\"\n}","id":"f730e85a-743d-4ede-a0bd-452a8fc43983","savedPayload":{"position":{"x":22089.869034219497,"y":24351.78631452414},"targetPosition":{"x":22189.47592785898,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0"}}}],"id":"b1075711-414d-4ffc-9509-852654091df1","savedPayload":{"position":{"x":22189.47592785898,"y":24421.53181230816},"targetPosition":{"x":22270.540970939346,"y":24421.53181230816},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"id":"4c32fcc6-a368-4223-b6a9-42023020d24b","savedPayload":{"position":{"x":22270.540970939346,"y":24421.53181230816},"targetPosition":{"x":22270.540970939346,"y":24421.53181230816},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}},{"type":"Timeline Chart","name":"Chart 02C","rateScale":{"type":"Rate Scale","name":"New Rate Scale","code":"{\n \"minValue\": 3174.4576465002638,\n \"maxValue\": 20650.88418479389\n}","id":"ff83f5df-c60f-4bd9-9858-ce8f4cb61f40","savedPayload":{"position":{"x":22227.863719397905,"y":24246.90351329475},"targetPosition":{"x":22270.039481764263,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"layersManager":{"type":"Layers Manager","name":"New Layers Manager","code":"{\n \"visibleLayers\": 0,\n \"panelLocation\": \"up\"\n}","layers":[{"type":"Layer","name":"Candles","code":"{\n \"status\": \"on\",\n \"showPanels\": true\n}","id":"eac312b0-785c-43e7-b922-4268423b063d","savedPayload":{"position":{"x":22077.404885843753,"y":24303.797230648433},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candles","id":"55e735a5-60c9-4e2d-a582-a6c6eddf3502"}}},{"type":"Layer","name":"Volumes","code":"{\"status\":\"off\",\"showPanels\":false}","id":"9a7cc22a-d931-4708-826c-354fabebb78e","savedPayload":{"position":{"x":22066.105249378983,"y":24284.2256861844},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volumes","id":"ffaf5a8e-12d6-4040-922d-57845eb3377c"}}},{"type":"Layer","name":"Candle-Stairs","code":"{\"status\":\"off\"}","id":"8daed8c1-67d7-4be4-9d79-f8adc5469654","savedPayload":{"position":{"x":22058.37584281257,"y":24262.98931617739},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Candle-Stairs","id":"e1c46e1f-09d1-46a2-b738-e761b4e04cd9"}}},{"type":"Layer","name":"Volume-Stairs","code":"{ \n\"status\": \"off\"\n}","id":"bc5f01e3-b3e2-4077-8179-7518f903cc50","savedPayload":{"position":{"x":22054.451520251758,"y":24240.733376983946},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Volume-Stairs","id":"eefaf182-ffe4-457e-a7fa-f43dfabdc086"}}},{"type":"Layer","name":"Bollinger Bands","code":"{\n \"status\": \"off\",\n \"showPanels\": true\n}","id":"6987defe-519a-44c4-ae26-ceb0064a60e5","savedPayload":{"position":{"x":22054.451520251758,"y":24218.134104054414},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Bands","id":"3576cfd0-1774-4094-b8a0-66e182fc4c41"}}},{"type":"Layer","name":"Percentage Bandwidth","code":"{ \n\"status\": \"off\"\n}","id":"449aa705-b3ae-459a-ac9f-116bb3ab50a0","savedPayload":{"position":{"x":22058.37584281257,"y":24195.87816486097},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Percentage Bandwidth","id":"4dfde44c-0bbf-4204-91de-ce99ecba4cf2"}}},{"type":"Layer","name":"Bollinger Channels Objects","code":"{\"status\":\"loading\"}","id":"fb058756-3ddc-4299-af44-1d0eaa98b9d3","savedPayload":{"position":{"x":22066.105249378983,"y":24174.64179485396},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Channels Objects","id":"6986bcb1-119c-48c9-a00b-d046e9e07951"}}},{"type":"Layer","name":"Bollinger Sub-Channels Objects","code":"{\n \"status\": \"on\",\n \"showPanels\": true\n}","id":"c54e490a-0eae-41c7-807d-9eceec9056d0","savedPayload":{"position":{"x":22077.404885843753,"y":24155.070250389927},"targetPosition":{"x":22183.60695604623,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":4},"uiObject":{},"referenceParent":{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"85098ab8-a125-49f1-b24d-416c2090c7a0"}}}],"id":"e036dda6-20cf-4772-9b2f-4463be7a1852","savedPayload":{"position":{"x":22183.60695604623,"y":24229.43374051918},"targetPosition":{"x":22270.039481764263,"y":24229.43374051918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":4,"distanceToParent":3},"uiObject":{}}},"id":"661b8623-473c-4e4f-9c1e-4e97ad3ba7e6","savedPayload":{"position":{"x":22270.039481764263,"y":24229.43374051918},"targetPosition":{"x":22270.039481764263,"y":24229.43374051918},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3},"uiObject":{}}}]} \ No newline at end of file diff --git a/Superalgos.exe b/Superalgos.exe new file mode 100644 index 000000000..183257b13 Binary files /dev/null and b/Superalgos.exe differ diff --git a/TaskManager/.env b/TaskManager/.env new file mode 100644 index 000000000..65f0b300d --- /dev/null +++ b/TaskManager/.env @@ -0,0 +1,12 @@ +TASK_SERVER_PATH=../TaskServer + + +# Task Manager Child process variables +INTER_PROCESS_FILES_PATH=../Inter-Process-Files +STORAGE_PATH=../Data-Storage +PLOTTERS_PATH=../Teams-Source-Code + +USE_KEYVAULT=false +WRITE_LOGS_TO_FILES=true +BOTS_PATH=../Teams-Source-Code +LOG_PATH=../Log-Files \ No newline at end of file diff --git a/TaskManager/SystemEventHandler.js b/TaskManager/SystemEventHandler.js new file mode 100644 index 000000000..7620ca72e --- /dev/null +++ b/TaskManager/SystemEventHandler.js @@ -0,0 +1,178 @@ +exports.newSystemEventHandler = function newSystemEventHandler(ipc) { + let thisObject = { + initialize: initialize, + finalize: finalize, + createEventHandler: createEventHandler, + deleteEventHandler: deleteEventHandler, + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent + } + + let eventListeners = new Map() + let responseWaiters = new Map() + + ipc.config.id = 'Task Server'; + ipc.config.retry = 1500; + ipc.config.silent = true; + + return thisObject + + function initialize(canllerName, callBackFunction) { + + ipc.connectTo( + 'world', + function () { + ipc.of.world.on( + 'connect', + function () { + ipc.log('## Connected to Superalgos Event Server ##'.rainbow, ipc.config.delay); + + let eventCommand = { + action: 'greetings', + from: canllerName + } + + sendCommand(eventCommand) + + callBackFunction() + } + ); + ipc.of.world.on( + 'disconnect', + function () { + ipc.log('Disconnected from Superalgos Event Server '.notice); + } + ); + ipc.of.world.on( + 'message', // here is where all incomming messages are processed. + function (data) { + ipc.log('Got a message from Superalgos Event Server: '.debug, data); + + let message = JSON.parse(data) + + if (message.action === 'Event Raised') { + let key + if (message.callerId) { + key = message.eventHandlerName + '-' + message.eventType + '-' + message.callerId + } else { + key = message.eventHandlerName + '-' + message.eventType + } + let handler = eventListeners.get(key) + if (handler) { + handler.callBack(message) + } else { + console.log(key + ' not found so could not deliver event raised.') + console.log(' Message = ' + data) + } + return + } + + if (message.action === 'Event Server Response') { + let handler = responseWaiters.get(message.callerId) + if (handler) { + handler(message) + } + return + } + } + ); + } + ); + } + + function finalize() { + /* Before disconnecting we will forze all eventListeners to stop listening. */ + const eventListenersArray = [...eventListeners.values()] + for (let i = 0; i < eventListenersArray.length; i++) { + let handler = eventListenersArray[i] + let eventCommand = { + action: 'stopListening', + eventHandlerName: handler.eventHandlerName, + eventType: handler.eventType, + callerId: handler.callerId + } + sendCommand(eventCommand) + } + ipc.disconnect('world'); + } + + function sendCommand(command, responseCallBack, eventsCallBack) { + + if (command.action === 'listenToEvent') { + let key + if (command.callerId) { + key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId + } else { + key = command.eventHandlerName + '-' + command.eventType + } + let handler = { + eventHandlerName: command.eventHandlerName, + eventType: command.eventType, + callerId: command.callerId, + callBack: eventsCallBack + } + eventListeners.set(key, handler) + } + if (command.callerId && responseCallBack) { + responseWaiters.set(command.callerId, responseCallBack) + } + + ipc.of.world.emit( + 'message', + JSON.stringify(command) + ) + } + + function createEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'createEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function deleteEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'deleteEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function listenToEvent(eventHandlerName, eventType, extraData, callerId, responseCallBack, eventsCallBack) { + let eventCommand = { + action: 'listenToEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + extraData: extraData, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack, eventsCallBack) + } + + function stopListening(eventHandlerName, eventType, eventSubscriptionId, callerId, responseCallBack) { + /* User either needs to specify a valid eventSubscriptionId OR the 3 params: eventHandlerName, eventType, callerId which were used when start listening to events. */ + let eventCommand = { + action: 'stopListening', + eventHandlerName: eventHandlerName, + eventType: eventType, + eventSubscriptionId: eventSubscriptionId, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function raiseEvent(eventHandlerName, eventType, event, callerId, responseCallBack) { + let eventCommand = { + action: 'raiseEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + event: event, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } +} \ No newline at end of file diff --git a/TaskManager/node_modules/child_process/package.json b/TaskManager/node_modules/child_process/package.json new file mode 100644 index 000000000..3d6309506 --- /dev/null +++ b/TaskManager/node_modules/child_process/package.json @@ -0,0 +1,46 @@ +{ + "_from": "child_process", + "_id": "child_process@1.0.2", + "_inBundle": false, + "_integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=", + "_location": "/child_process", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "child_process", + "name": "child_process", + "escapedName": "child_process", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "_shasum": "b1f7e7fc73d25e7fd1d455adc94e143830182b5a", + "_spec": "child_process", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\BackendServer", + "author": "", + "bugs": { + "url": "https://github.com/npm/security-holder/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "This package name is not currently in use, but was formerly occupied by another package. To avoid malicious use, npm is hanging on to the package name, but loosely, and we'll probably give it to you if you want it.", + "homepage": "https://github.com/npm/security-holder#readme", + "keywords": [], + "license": "ISC", + "main": "index.js", + "name": "child_process", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/security-holder.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.2" +} diff --git a/TaskManager/node_modules/dotenv/config.js b/TaskManager/node_modules/dotenv/config.js new file mode 100644 index 000000000..c7e6447fc --- /dev/null +++ b/TaskManager/node_modules/dotenv/config.js @@ -0,0 +1,11 @@ +/* @flow */ + +(function () { + require('./lib/main').config( + Object.assign( + {}, + require('./lib/env-options'), + require('./lib/cli-options')(process.argv) + ) + ) +})() diff --git a/TaskManager/node_modules/dotenv/lib/cli-options.js b/TaskManager/node_modules/dotenv/lib/cli-options.js new file mode 100644 index 000000000..ad69fabd9 --- /dev/null +++ b/TaskManager/node_modules/dotenv/lib/cli-options.js @@ -0,0 +1,13 @@ +/* @flow */ + +const re = /^dotenv_config_(encoding|path|debug)=(.+)$/ + +module.exports = function optionMatcher (args /*: Array */) { + return args.reduce(function (acc, cur) { + const matches = cur.match(re) + if (matches) { + acc[matches[1]] = matches[2] + } + return acc + }, {}) +} diff --git a/TaskManager/node_modules/dotenv/lib/env-options.js b/TaskManager/node_modules/dotenv/lib/env-options.js new file mode 100644 index 000000000..b4c4a6068 --- /dev/null +++ b/TaskManager/node_modules/dotenv/lib/env-options.js @@ -0,0 +1,18 @@ +/* @flow */ + +// ../config.js accepts options via environment variables +const options = {} + +if (process.env.DOTENV_CONFIG_ENCODING != null) { + options.encoding = process.env.DOTENV_CONFIG_ENCODING +} + +if (process.env.DOTENV_CONFIG_PATH != null) { + options.path = process.env.DOTENV_CONFIG_PATH +} + +if (process.env.DOTENV_CONFIG_DEBUG != null) { + options.debug = process.env.DOTENV_CONFIG_DEBUG +} + +module.exports = options diff --git a/TaskManager/node_modules/dotenv/lib/main.js b/TaskManager/node_modules/dotenv/lib/main.js new file mode 100644 index 000000000..e6c591c6b --- /dev/null +++ b/TaskManager/node_modules/dotenv/lib/main.js @@ -0,0 +1,113 @@ +/* @flow */ +/*:: + +type DotenvParseOptions = { + debug?: boolean +} + +// keys and values from src +type DotenvParseOutput = { [string]: string } + +type DotenvConfigOptions = { + path?: string, // path to .env file + encoding?: string, // encoding of .env file + debug?: string // turn on logging for debugging purposes +} + +type DotenvConfigOutput = { + parsed?: DotenvParseOutput, + error?: Error +} + +*/ + +const fs = require('fs') +const path = require('path') + +function log (message /*: string */) { + console.log(`[dotenv][DEBUG] ${message}`) +} + +const NEWLINE = '\n' +const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/ +const RE_NEWLINES = /\\n/g +const NEWLINES_MATCH = /\n|\r|\r\n/ + +// Parses src into an Object +function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ { + const debug = Boolean(options && options.debug) + const obj = {} + + // convert Buffers before splitting into lines and processing + src.toString().split(NEWLINES_MATCH).forEach(function (line, idx) { + // matching "KEY' and 'VAL' in 'KEY=VAL' + const keyValueArr = line.match(RE_INI_KEY_VAL) + // matched? + if (keyValueArr != null) { + const key = keyValueArr[1] + // default undefined or missing values to empty string + let val = (keyValueArr[2] || '') + const end = val.length - 1 + const isDoubleQuoted = val[0] === '"' && val[end] === '"' + const isSingleQuoted = val[0] === "'" && val[end] === "'" + + // if single or double quoted, remove quotes + if (isSingleQuoted || isDoubleQuoted) { + val = val.substring(1, end) + + // if double quoted, expand newlines + if (isDoubleQuoted) { + val = val.replace(RE_NEWLINES, NEWLINE) + } + } else { + // remove surrounding whitespace + val = val.trim() + } + + obj[key] = val + } else if (debug) { + log(`did not match key and value when parsing line ${idx + 1}: ${line}`) + } + }) + + return obj +} + +// Populates process.env from .env file +function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ { + let dotenvPath = path.resolve(process.cwd(), '.env') + let encoding /*: string */ = 'utf8' + let debug = false + + if (options) { + if (options.path != null) { + dotenvPath = options.path + } + if (options.encoding != null) { + encoding = options.encoding + } + if (options.debug != null) { + debug = true + } + } + + try { + // specifying an encoding returns a string instead of a buffer + const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug }) + + Object.keys(parsed).forEach(function (key) { + if (!Object.prototype.hasOwnProperty.call(process.env, key)) { + process.env[key] = parsed[key] + } else if (debug) { + log(`"${key}" is already defined in \`process.env\` and will not be overwritten`) + } + }) + + return { parsed } + } catch (e) { + return { error: e } + } +} + +module.exports.config = config +module.exports.parse = parse diff --git a/TaskManager/node_modules/dotenv/package.json b/TaskManager/node_modules/dotenv/package.json new file mode 100644 index 000000000..bf0ee8863 --- /dev/null +++ b/TaskManager/node_modules/dotenv/package.json @@ -0,0 +1,77 @@ +{ + "_from": "dotenv", + "_id": "dotenv@8.1.0", + "_inBundle": false, + "_integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==", + "_location": "/dotenv", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "dotenv", + "name": "dotenv", + "escapedName": "dotenv", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz", + "_shasum": "d811e178652bfb8a1e593c6dd704ec7e90d85ea2", + "_spec": "dotenv", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\BackendServer", + "bugs": { + "url": "https://github.com/motdotla/dotenv/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Loads environment variables from .env file", + "devDependencies": { + "decache": "^4.5.1", + "flow-bin": "^0.105.2", + "sinon": "^7.4.1", + "standard": "^13.1.0", + "standard-markdown": "^5.1.0", + "standard-version": "^7.0.0", + "tap": "^14.6.1" + }, + "engines": { + "node": ">=8" + }, + "homepage": "https://github.com/motdotla/dotenv#readme", + "keywords": [ + "dotenv", + "env", + ".env", + "environment", + "variables", + "config", + "settings" + ], + "license": "BSD-2-Clause", + "main": "lib/main.js", + "name": "dotenv", + "repository": { + "type": "git", + "url": "git://github.com/motdotla/dotenv.git" + }, + "scripts": { + "flow": "flow", + "lint": "standard", + "postlint": "standard-markdown", + "prerelease": "npm test", + "pretest": "npm run lint", + "release": "standard-version", + "test": "tap tests/*.js --100" + }, + "standard": { + "ignore": [ + "flow-typed/" + ] + }, + "version": "8.1.0" +} diff --git a/TaskManager/node_modules/easy-stack/es5.js b/TaskManager/node_modules/easy-stack/es5.js new file mode 100644 index 000000000..a756a7bbf --- /dev/null +++ b/TaskManager/node_modules/easy-stack/es5.js @@ -0,0 +1,74 @@ +function Stack(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToStack + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearStack + }, + contents:{ + enumerable:false, + get:getStack, + set:setStack + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var stack=[]; + var running=false; + var stop=false; + + function clearStack(){ + stack=[]; + return stack; + } + + function getStack(){ + return stack; + } + + function setStack(val){ + stack=val; + return stack; + } + + function addToStack(){ + for(var i in arguments){ + stack.unshift(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(stack.length<1 || this.stop){ + running=false; + return; + } + + stack.shift().bind(this)(); + } +} diff --git a/TaskManager/node_modules/easy-stack/example/basic.js b/TaskManager/node_modules/easy-stack/example/basic.js new file mode 100644 index 000000000..f0d7fbb11 --- /dev/null +++ b/TaskManager/node_modules/easy-stack/example/basic.js @@ -0,0 +1,18 @@ +var Stack=require('../stack'); +//create a new Stack instance +var stack=new Stack; +stack.autoRun=false; + +for(var i=0; i<50; i++){ + //add a bunch of stuff to the stack + stack.add(makeRequest.bind(stack,i)); +} + +stack.next(); + +function makeRequest(index){ + //do stuff + console.log(`making LIFO request ${index}`); + + this.next(); +} diff --git a/TaskManager/node_modules/easy-stack/package.json b/TaskManager/node_modules/easy-stack/package.json new file mode 100644 index 000000000..9ba6fd25f --- /dev/null +++ b/TaskManager/node_modules/easy-stack/package.json @@ -0,0 +1,59 @@ +{ + "_from": "easy-stack@^1.0.0", + "_id": "easy-stack@1.0.0", + "_inBundle": false, + "_integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "_location": "/easy-stack", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "easy-stack@^1.0.0", + "name": "easy-stack", + "escapedName": "easy-stack", + "rawSpec": "^1.0.0", + "saveSpec": null, + "fetchSpec": "^1.0.0" + }, + "_requiredBy": [ + "/js-queue" + ], + "_resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "_shasum": "12c91b3085a37f0baa336e9486eac4bf94e3e788", + "_spec": "easy-stack@^1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskManager\\node_modules\\js-queue", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/easy-stack/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Simple JS stack with auto run for node and browsers", + "engines": { + "node": ">=6.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/easy-stack#readme", + "keywords": [ + "stack", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "stack.js", + "name": "easy-stack", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/easy-stack.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.0" +} diff --git a/TaskManager/node_modules/easy-stack/stack.js b/TaskManager/node_modules/easy-stack/stack.js new file mode 100644 index 000000000..bfadd1dee --- /dev/null +++ b/TaskManager/node_modules/easy-stack/stack.js @@ -0,0 +1,39 @@ +class Stack{ + constructor(){ + this.stack=[]; + this.autoRun=true; + this.running=false; + this.stop=false; + } + + clear(){ + this.stack=[]; + return this.stack; + } + + contents(val){ + if(val){ + this.stack=val; + } + return this.stack; + } + + add(...callbacks){ + this.stack.push(...callbacks); + if(!this.running && !this.stop && this.autoRun){ + this.next(); + } + } + + next(){ + this.running=true; + if(this.stack.length<1 || this.stop){ + this.running=false; + return; + } + + this.stack.pop().bind(this)(); + } +} + +module.exports=Stack; diff --git a/TaskManager/node_modules/event-pubsub/bower.json b/TaskManager/node_modules/event-pubsub/bower.json new file mode 100644 index 000000000..a207b0a5d --- /dev/null +++ b/TaskManager/node_modules/event-pubsub/bower.json @@ -0,0 +1,28 @@ +{ + "name": "event-pubsub", + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "main": "event-pubsub.js", + "authors": [ + "Brandon Nozaki Miller" + ], + "license": "DBAD", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "moduleType": [ + "globals", + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/TaskManager/node_modules/event-pubsub/es5.js b/TaskManager/node_modules/event-pubsub/es5.js new file mode 100644 index 000000000..7faf8b737 --- /dev/null +++ b/TaskManager/node_modules/event-pubsub/es5.js @@ -0,0 +1,143 @@ +'use strict'; + +function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} + +module.exports=EventPubSub; diff --git a/TaskManager/node_modules/event-pubsub/es6.js b/TaskManager/node_modules/event-pubsub/es6.js new file mode 100644 index 000000000..e9e859f9b --- /dev/null +++ b/TaskManager/node_modules/event-pubsub/es6.js @@ -0,0 +1,99 @@ +'use strict'; + +class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +module.exports = EventPubSub; diff --git a/TaskManager/node_modules/event-pubsub/event-pubsub-browser-es5.js b/TaskManager/node_modules/event-pubsub/event-pubsub-browser-es5.js new file mode 100644 index 000000000..03f27625e --- /dev/null +++ b/TaskManager/node_modules/event-pubsub/event-pubsub-browser-es5.js @@ -0,0 +1,141 @@ +'use strict'; + +window.EventPubSub=function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/TaskManager/node_modules/event-pubsub/event-pubsub-browser.js b/TaskManager/node_modules/event-pubsub/event-pubsub-browser.js new file mode 100644 index 000000000..d0bcd7b56 --- /dev/null +++ b/TaskManager/node_modules/event-pubsub/event-pubsub-browser.js @@ -0,0 +1,130 @@ +'use strict'; + +window.EventPubSub=class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/TaskManager/node_modules/event-pubsub/event-pubsub.js b/TaskManager/node_modules/event-pubsub/event-pubsub.js new file mode 100644 index 000000000..65bd02419 --- /dev/null +++ b/TaskManager/node_modules/event-pubsub/event-pubsub.js @@ -0,0 +1,8 @@ +'use strict'; + +let EventPubSub = require('./es5'); +if(process.version[1]>5){ + EventPubSub = require('./es6'); +} + +module.exports=EventPubSub; diff --git a/TaskManager/node_modules/event-pubsub/package.json b/TaskManager/node_modules/event-pubsub/package.json new file mode 100644 index 000000000..0d216088c --- /dev/null +++ b/TaskManager/node_modules/event-pubsub/package.json @@ -0,0 +1,59 @@ +{ + "_from": "event-pubsub@4.3.0", + "_id": "event-pubsub@4.3.0", + "_inBundle": false, + "_integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "_location": "/event-pubsub", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "event-pubsub@4.3.0", + "name": "event-pubsub", + "escapedName": "event-pubsub", + "rawSpec": "4.3.0", + "saveSpec": null, + "fetchSpec": "4.3.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "_shasum": "f68d816bc29f1ec02c539dc58c8dd40ce72cb36e", + "_spec": "event-pubsub@4.3.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskManager\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/event-pubsub/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "directories": { + "example": "examples" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "license": "Unlicense", + "main": "event-pubsub.js", + "name": "event-pubsub", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/event-pubsub.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "4.3.0" +} diff --git a/TaskManager/node_modules/js-message/Message.js b/TaskManager/node_modules/js-message/Message.js new file mode 100644 index 000000000..9a4698232 --- /dev/null +++ b/TaskManager/node_modules/js-message/Message.js @@ -0,0 +1,71 @@ +function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } +} + +module.exports=Message; diff --git a/TaskManager/node_modules/js-message/js-message-vanilla.js b/TaskManager/node_modules/js-message/js-message-vanilla.js new file mode 100644 index 000000000..0c49c2b6c --- /dev/null +++ b/TaskManager/node_modules/js-message/js-message-vanilla.js @@ -0,0 +1,75 @@ +( + function(){ + function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } + } + + window.message=Message; + } +)(); diff --git a/TaskManager/node_modules/js-message/package.json b/TaskManager/node_modules/js-message/package.json new file mode 100644 index 000000000..8a9abd0bb --- /dev/null +++ b/TaskManager/node_modules/js-message/package.json @@ -0,0 +1,81 @@ +{ + "_from": "js-message@1.0.5", + "_id": "js-message@1.0.5", + "_inBundle": false, + "_integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "_location": "/js-message", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-message@1.0.5", + "name": "js-message", + "escapedName": "js-message", + "rawSpec": "1.0.5", + "saveSpec": null, + "fetchSpec": "1.0.5" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "_shasum": "2300d24b1af08e89dd095bc1a4c9c9cfcb892d15", + "_spec": "js-message@1.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskManager\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-message/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "normalized JS Object and JSON message and event protocol for node.js, vanialla js, react.js, components, actions, stores and dispatchers", + "devDependencies": {}, + "engines": { + "node": ">=0.6.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-message#readme", + "keywords": [ + "message", + "normalize", + "events", + "js", + "json", + "protocol", + "ipc", + "node", + "nodejs", + "node.js", + "react", + "react.js", + "reactjs", + "websocket", + "websockets", + "web", + "socket", + "sockets", + "ws", + "flux", + "reflux", + "component", + "components", + "store", + "stores", + "action", + "actions" + ], + "license": "DBAD", + "main": "Message.js", + "name": "js-message", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-message.git" + }, + "scripts": { + "start": "node devServer.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.5" +} diff --git a/TaskManager/node_modules/js-queue/package.json b/TaskManager/node_modules/js-queue/package.json new file mode 100644 index 000000000..94ecd47a5 --- /dev/null +++ b/TaskManager/node_modules/js-queue/package.json @@ -0,0 +1,62 @@ +{ + "_from": "js-queue@2.0.0", + "_id": "js-queue@2.0.0", + "_inBundle": false, + "_integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "_location": "/js-queue", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-queue@2.0.0", + "name": "js-queue", + "escapedName": "js-queue", + "rawSpec": "2.0.0", + "saveSpec": null, + "fetchSpec": "2.0.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "_shasum": "362213cf860f468f0125fc6c96abc1742531f948", + "_spec": "js-queue@2.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskManager\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-queue/issues" + }, + "bundleDependencies": false, + "dependencies": { + "easy-stack": "^1.0.0" + }, + "deprecated": false, + "description": "Simple JS queue with auto run for node and browsers", + "engines": { + "node": ">=1.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-queue#readme", + "keywords": [ + "queue", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "queue.js", + "name": "js-queue", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-queue.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "2.0.0" +} diff --git a/TaskManager/node_modules/js-queue/queue-vanilla.js b/TaskManager/node_modules/js-queue/queue-vanilla.js new file mode 100644 index 000000000..6f15495ae --- /dev/null +++ b/TaskManager/node_modules/js-queue/queue-vanilla.js @@ -0,0 +1,74 @@ +function Queue(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} diff --git a/TaskManager/node_modules/js-queue/queue.js b/TaskManager/node_modules/js-queue/queue.js new file mode 100644 index 000000000..a8698d7e9 --- /dev/null +++ b/TaskManager/node_modules/js-queue/queue.js @@ -0,0 +1,76 @@ +function Queue(asStack){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} + +module.exports=Queue; diff --git a/TaskManager/node_modules/js-queue/stack.js b/TaskManager/node_modules/js-queue/stack.js new file mode 100644 index 000000000..a9c25640e --- /dev/null +++ b/TaskManager/node_modules/js-queue/stack.js @@ -0,0 +1,3 @@ +const Stack=require('easy-stack'); + +module.exports = Stack; diff --git a/TaskManager/node_modules/node-ipc/dao/client.js b/TaskManager/node_modules/node-ipc/dao/client.js new file mode 100644 index 000000000..41ce50411 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/dao/client.js @@ -0,0 +1,257 @@ +'use strict'; + +const net = require('net'), + tls = require('tls'), + EventParser = require('../entities/EventParser.js'), + Message = require('js-message'), + fs = require('fs'), + Queue = require('js-queue'); + +let Events = require('event-pubsub/es5'); +if(process.version[1]>4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Client extends Events{ + constructor(config,log){ + super(); + Object.assign( + this, + { + Client : Client, + config : config, + queue : new Queue, + socket : false, + connect : connect, + emit : emit, + log : log, + retriesRemaining:config.maxRetries||0, + explicitlyDisconnected: false + } + ); + + eventParser=new EventParser(this.config); + } +} + +function emit(type,data){ + this.log('dispatching event to ', this.id, this.path, ' : ', type, ',', data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(!this.config.sync){ + this.socket.write(message); + return; + } + + this.queue.add( + syncEmit.bind(this,message) + ); +} + +function syncEmit(message){ + this.log('dispatching event to ', this.id, this.path, ' : ', message); + this.socket.write(message); +} + +function connect(){ + //init client object for scope persistance especially inside of socket events. + let client=this; + + client.log('requested connection to ', client.id, client.path); + if(!this.path){ + client.log('\n\n######\nerror: ', client.id ,' client has not specified socket path it wishes to connect to.'); + return; + } + + const options={}; + + if(!client.port){ + client.log('Connecting client on Unix Socket :', client.path); + + options.path=client.path; + + if (process.platform ==='win32' && !client.path.startsWith('\\\\.\\pipe\\')){ + options.path = options.path.replace(/^\//, ''); + options.path = options.path.replace(/\//g, '-'); + options.path= `\\\\.\\pipe\\${options.path}`; + } + + client.socket = net.connect(options); + }else{ + options.host=client.path; + options.port=client.port; + + if(client.config.interface.localAddress){ + options.localAddress=client.config.interface.localAddress; + } + + if(client.config.interface.localPort){ + options.localPort=client.config.interface.localPort; + } + + if(client.config.interface.family){ + options.family=client.config.interface.family; + } + + if(client.config.interface.hints){ + options.hints=client.config.interface.hints; + } + + if(client.config.interface.lookup){ + options.lookup=client.config.interface.lookup; + } + + if(!client.config.tls){ + client.log('Connecting client via TCP to', options); + client.socket = net.connect(options); + }else{ + client.log('Connecting client via TLS to', client.path ,client.port,client.config.tls); + if(client.config.tls.private){ + client.config.tls.key=fs.readFileSync(client.config.tls.private); + } + if(client.config.tls.public){ + client.config.tls.cert=fs.readFileSync(client.config.tls.public); + } + if(client.config.tls.trustedConnections){ + if(typeof client.config.tls.trustedConnections === 'string'){ + client.config.tls.trustedConnections=[client.config.tls.trustedConnections]; + } + client.config.tls.ca=[]; + for(let i=0; i4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Server extends Events{ + constructor(path,config,log,port){ + super(); + Object.assign( + this, + { + config : config, + path : path, + port : port, + udp4 : false, + udp6 : false, + log : log, + server : false, + sockets : [], + emit : emit, + broadcast : broadcast + } + ); + + eventParser=new EventParser(this.config); + + this.on( + 'close', + serverClosed.bind(this) + ); + } + + onStart(socket){ + this.trigger( + 'start', + socket + ); + } + + stop(){ + this.server.close(); + } + + start(){ + if(!this.path){ + this.log('Socket Server Path not specified, refusing to start'); + return; + } + + if(this.config.unlink){ + fs.unlink( + this.path, + startServer.bind(this) + ); + }else{ + startServer.bind(this)(); + } + } +} + +function emit(socket, type, data){ + this.log('dispatching event to socket', ' : ', type, data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + this.log(this.config.encoding) + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + + if(!socket.address || !socket.port){ + this.log('Attempting to emit to a single UDP socket without supplying socket address or port. Redispatching event as broadcast to all connected sockets'); + this.broadcast(type,data); + return; + } + + this.server.write( + message, + socket + ); + return; + } + + socket.write(message); +} + +function broadcast(type,data){ + this.log('broadcasting event to all known sockets listening to ', this.path,' : ', ((this.port)?this.port:''), type, data); + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + for(let i=1, count=this.sockets.length; i0){ + let message=new Message; + message.load(data.shift()); + + // Only set the sock id if it is specified. + if (message.data && message.data.id){ + sock.id=message.data.id; + } + + this.log('received event of : ',message.type,message.data); + + this.publish( + message.type, + message.data, + sock + ); + } +} + +function socketClosed(socket){ + this.publish( + 'close', + socket + ); +} + +function serverCreated(socket) { + this.sockets.push(socket); + + if(socket.setEncoding){ + socket.setEncoding(this.config.encoding); + } + + this.log('## socket connection to server detected ##'); + socket.on( + 'close', + socketClosed.bind(this) + ); + + socket.on( + 'error', + function(err){ + this.log('server socket error',err); + + this.publish('error',err); + }.bind(this) + ); + + socket.on( + 'data', + gotData.bind(this,socket) + ); + + socket.on( + 'message', + function(msg,rinfo) { + if (!rinfo){ + return; + } + + this.log('Received UDP message from ', rinfo.address, rinfo.port); + let data; + + if(this.config.rawSocket){ + data=new Buffer(msg,this.config.encoding); + }else{ + data=msg.toString(); + } + socket.emit('data',data,rinfo); + }.bind(this) + ); + + this.publish( + 'connect', + socket + ); + + if(this.config.rawBuffer){ + return; + } +} + +function startServer() { + this.log( + 'starting server on ',this.path, + ((this.port)?`:${this.port}`:'') + ); + + if(!this.udp4 && !this.udp6){ + this.log('starting TLS server',this.config.tls); + if(!this.config.tls){ + this.server=net.createServer( + serverCreated.bind(this) + ); + }else{ + startTLSServer.bind(this)(); + } + }else{ + this.server=dgram.createSocket( + ((this.udp4)? 'udp4':'udp6') + ); + this.server.write=UDPWrite.bind(this); + this.server.on( + 'listening', + function UDPServerStarted() { + serverCreated.bind(this)(this.server); + }.bind(this) + ); + } + + this.server.on( + 'error', + function(err){ + this.log('server error',err); + + this.publish( + 'error', + err + ); + }.bind(this) + ); + + this.server.maxConnections=this.config.maxConnections; + + if(!this.port){ + this.log('starting server as', 'Unix || Windows Socket'); + if (process.platform ==='win32'){ + this.path = this.path.replace(/^\//, ''); + this.path = this.path.replace(/\//g, '-'); + this.path= `\\\\.\\pipe\\${this.path}`; + } + + this.server.listen( + this.path, + this.onStart.bind(this) + ); + + return; + } + + if(!this.udp4 && !this.udp6){ + this.log('starting server as', (this.config.tls?'TLS':'TCP')); + this.server.listen( + this.port, + this.path, + this.onStart.bind(this) + ); + return; + } + + this.log('starting server as',((this.udp4)? 'udp4':'udp6')); + + this.server.bind( + this.port, + this.path + ); + + this.onStart( + { + address : this.path, + port : this.port + } + ); +} + +function startTLSServer(){ + this.log('starting TLS server',this.config.tls); + if(this.config.tls.private){ + this.config.tls.key=fs.readFileSync(this.config.tls.private); + }else{ + this.config.tls.key=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/private/server.key`); + } + if(this.config.tls.public){ + this.config.tls.cert=fs.readFileSync(this.config.tls.public); + }else{ + this.config.tls.cert=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/server.pub`); + } + if(this.config.tls.dhparam){ + this.config.tls.dhparam=fs.readFileSync(this.config.tls.dhparam); + } + if(this.config.tls.trustedConnections){ + if(typeof this.config.tls.trustedConnections === 'string'){ + this.config.tls.trustedConnections=[this.config.tls.trustedConnections]; + } + this.config.tls.ca=[]; + for(let i=0; i 0) { + // getting the family of first network interface available + IPType = networkInterfaces [ + Object.keys( networkInterfaces )[0] + ][0].family; + } + return IPType; +} + +module.exports=Defaults; diff --git a/TaskManager/node_modules/node-ipc/entities/EventParser.js b/TaskManager/node_modules/node-ipc/entities/EventParser.js new file mode 100644 index 000000000..8ac1f2bda --- /dev/null +++ b/TaskManager/node_modules/node-ipc/entities/EventParser.js @@ -0,0 +1,32 @@ +'use strict'; + +const Defaults = require('./Defaults.js'); + +class Parser{ + constructor(config){ + if(!config){ + config=new Defaults; + } + this.delimiter=config.delimiter; + } + + format(message){ + if(!message.data && message.data!==false && message.data!==0){ + message.data={}; + } + if(message.data['_maxListeners']){ + message.data={}; + } + + message=message.JSON+this.delimiter; + return message; + } + + parse(data){ + let events=data.split(this.delimiter); + events.pop(); + return events; + } +} + +module.exports=Parser; diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..9d5ce05d6 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..74136333d --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,50 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..2bef44794 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js new file mode 100644 index 000000000..6ae692edf --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/basic/world-server.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/basic/world-server.js new file mode 100644 index 000000000..6c8790170 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/basic/world-server.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.maxConnections=1; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log('DISCONNECTED\n\n',arguments); + } + ); + } +); + +ipc.server.on( + 'error', + function(err){ + ipc.log('Got an ERROR!',err); + } +); + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js new file mode 100644 index 000000000..292415053 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js @@ -0,0 +1,46 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data,'\n\n'); + } + ); + } +); + +console.log(ipc); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js new file mode 100644 index 000000000..3e817da13 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync = true; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + //fake some synch procedural code + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..b6ac81f6b --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js b/TaskManager/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..5da4de4d5 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js new file mode 100644 index 000000000..508702d01 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js new file mode 100644 index 000000000..16fd21fc3 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js new file mode 100644 index 000000000..0c52c268d --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js @@ -0,0 +1,55 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js new file mode 100644 index 000000000..3cbcd8e38 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +//node-ipc will default to its local certs +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js new file mode 100644 index 000000000..bda3eb701 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js new file mode 100644 index 000000000..56eee122b --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js new file mode 100644 index 000000000..26d5b1da6 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js new file mode 100644 index 000000000..ee984a4e1 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basic/world-server.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic/world-server.js new file mode 100644 index 000000000..a52c3602a --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basic/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js new file mode 100644 index 000000000..405db4468 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js new file mode 100644 index 000000000..aec87c100 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js new file mode 100644 index 000000000..f0c66ad22 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js b/TaskManager/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js new file mode 100644 index 000000000..f0d0b29af --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + console.log('connection detected'); + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js b/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..d98a7b061 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8002, //we set the port here because the hello client and world server are already using the default of 8000 and the port 8001. So we can not bind to those while hello and world are connected to them. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Goodbye' + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js b/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..ecc0b45a4 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js b/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..97f927088 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,63 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + 'udp4', + function(){ + console.log(123); + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + messages[data.id]=true; + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling evryone how muchg I am loved!'); + ipc.server.broadcast( + 'message', + { + id : ipc.config.id, + message : 'Everybody Loves The World! Got messages from hello and goodbye!' + } + ); + } + } + ); + + console.log(ipc.server); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js b/TaskManager/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js new file mode 100644 index 000000000..f731b1ba8 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js @@ -0,0 +1,52 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/UDPSocket/basic/world-server.js b/TaskManager/node_modules/node-ipc/example/UDPSocket/basic/world-server.js new file mode 100644 index 000000000..0f7aa4543 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/UDPSocket/basic/world-server.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js b/TaskManager/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..73cd13ac5 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data){ + ipc.log('got a message from world ', data, data.toString()); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'hello' + ); + } +); + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js b/TaskManager/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..d0c34f7e2 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js b/TaskManager/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js new file mode 100644 index 000000000..eca2a889c --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const ipc = require('../../node-ipc'); + +const socketPath = '/tmp/ipc.sock'; + +//loop forever so you can see the pid of the cluster sever change in the logs +setInterval( + function() { + ipc.connectTo( + 'world', + socketPath, + connecting + ); + }, + 2000 +); + +function connecting(socket) { + ipc.of.world.on( + 'connect', + function() { + ipc.of.world.emit( + 'currentDate', + { + message: new Date().toISOString() + } + ); + ipc.disconnect('world'); + } + ); +} diff --git a/TaskManager/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js b/TaskManager/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js new file mode 100644 index 000000000..b0942a338 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const ipc=require('../../../node-ipc'); +const cpuCount = require('os').cpus().length; +const cluster = require('cluster'); +const socketPath = '/tmp/ipc.sock'; + +ipc.config.unlink = false; + +if (cluster.isMaster) { + if (fs.existsSync(socketPath)) { + fs.unlinkSync(socketPath); + } + + for (let i = 0; i < cpuCount; i++) { + cluster.fork(); + } +}else{ + ipc.serve( + socketPath, + function() { + ipc.server.on( + 'currentDate', + function(data,socket) { + console.log(`pid ${process.pid} got: `, data); + } + ); + } + ); + + ipc.server.start(); + console.log(`pid ${process.pid} listening on ${socketPath}`); +} diff --git a/TaskManager/node_modules/node-ipc/example/rawBuffer/hello-client.js b/TaskManager/node_modules/node-ipc/example/rawBuffer/hello-client.js new file mode 100644 index 000000000..f22ab5376 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/rawBuffer/hello-client.js @@ -0,0 +1,43 @@ +'use strict'; + +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + //make a 6 byte buffer for example + const myBuffer=new Buffer(6).fill(0); + + myBuffer.writeUInt16BE(0x02,0); + myBuffer.writeUInt32BE(0xffeecc,2); + + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + myBuffer + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/rawBuffer/world.server.js b/TaskManager/node_modules/node-ipc/example/rawBuffer/world.server.js new file mode 100644 index 000000000..f27a02cd5 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + [0xaa] + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data); + ipc.server.emit( + socket, + [0x0d,0xee] + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..57d455eec --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..d556c4694 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..69cfb31c0 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js new file mode 100644 index 000000000..c6d87cb27 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js new file mode 100644 index 000000000..6b3052242 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js @@ -0,0 +1,33 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js new file mode 100644 index 000000000..3142677a2 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; +ipc.config.sync= true; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello'+i + } + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js new file mode 100644 index 000000000..c76e1bb6f --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + setTimeout( + function(){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + }, + 2000 + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..9c7042a90 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..c3ed83c9a --- /dev/null +++ b/TaskManager/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskManager/node_modules/node-ipc/node-ipc.js b/TaskManager/node_modules/node-ipc/node-ipc.js new file mode 100644 index 000000000..1c6336061 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/node-ipc.js @@ -0,0 +1,21 @@ +'use strict'; + +const IPC = require('./services/IPC.js'); + +class IPCModule extends IPC{ + constructor(){ + super(); + //include IPC to make extensible + Object.defineProperty( + this, + 'IPC', + { + enumerable:true, + writable:false, + value:IPC + } + ) + } +} + +module.exports=new IPCModule; diff --git a/TaskManager/node_modules/node-ipc/package.json b/TaskManager/node_modules/node-ipc/package.json new file mode 100644 index 000000000..4a7634127 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/package.json @@ -0,0 +1,92 @@ +{ + "_from": "node-ipc", + "_id": "node-ipc@9.1.1", + "_inBundle": false, + "_integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "_location": "/node-ipc", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "node-ipc", + "name": "node-ipc", + "escapedName": "node-ipc", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "_shasum": "4e245ed6938e65100e595ebc5dc34b16e8dd5d69", + "_spec": "node-ipc", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskManager", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/node-ipc/issues" + }, + "bundleDependencies": false, + "dependencies": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + }, + "deprecated": false, + "description": "A nodejs module for local and remote Inter Process Communication (IPC), Neural Networking, and able to facilitate machine learning.", + "devDependencies": { + "codacy-coverage": "2.0.0", + "istanbul": "0.4.1", + "jasmine": "2.4.1", + "node-cmd": "2.0.0" + }, + "directories": { + "example": "example" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "http://riaevangelist.github.io/node-ipc/", + "keywords": [ + "IPC", + "Neural Networking", + "Machine Learning", + "inter", + "process", + "communication", + "unix", + "windows", + "win", + "socket", + "TCP", + "UDP", + "domain", + "sockets", + "threaded", + "communication", + "multi", + "process", + "shared", + "memory" + ], + "license": "DBAD", + "main": "node-ipc.js", + "name": "node-ipc", + "pre-commit": [ + "cover" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/node-ipc.git" + }, + "scripts": { + "cover": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "coverup": "cat ./spec/coverage/lcov.info | codacy-coverage", + "test": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "test-windows": "istanbul cover -x **/spec/** -dir ./spec/coverage ./node_modules/jasmine/bin/jasmine.js" + }, + "version": "9.1.1" +} diff --git a/TaskManager/node_modules/node-ipc/services/IPC.js b/TaskManager/node_modules/node-ipc/services/IPC.js new file mode 100644 index 000000000..3a6edc7f5 --- /dev/null +++ b/TaskManager/node_modules/node-ipc/services/IPC.js @@ -0,0 +1,337 @@ +'use strict'; + +const Defaults = require('../entities/Defaults.js'), + Client = require('../dao/client.js'), + Server = require('../dao/socketServer.js'), + util = require('util'); + +class IPC{ + constructor(){ + Object.defineProperties( + this, + { + config : { + enumerable:true, + writable:true, + value:new Defaults + }, + connectTo : { + enumerable:true, + writable:false, + value:connect + }, + connectToNet: { + enumerable:true, + writable:false, + value:connectNet + }, + disconnect : { + enumerable:true, + writable:false, + value:disconnect + }, + serve : { + enumerable:true, + writable:false, + value:serve + }, + serveNet : { + enumerable:true, + writable:false, + value:serveNet + }, + of : { + enumerable:true, + writable:true, + value:{} + }, + server : { + enumerable:true, + writable:true, + configurable:true, + value:false + }, + log : { + enumerable:true, + writable:false, + value:log + } + } + ); + } +} + +function log(...args){ + if(this.config.silent){ + return; + } + + for(let i=0, count=args.length; i server -> runTask -> Entering function.') + + if (message.event === undefined) { + console.log('[WARN] Task Manager -> server -> runTask -> Message Received Without Event -> message = ' + JSON.stringify(message)) + return + } + + if (message.event.taskId === undefined) { + console.log('[WARN] Task Manager -> server -> runTask -> Message Received Without taskId -> message = ' + JSON.stringify(message)) + return + } + + if (message.event.definition === undefined) { + console.log('[WARN] Task Manager -> server -> runTask -> Message Received Without Definition -> message = ' + JSON.stringify(message)) + return + } + + if (tasks.get(message.event.taskId) !== undefined) { + //console.log('[WARN] Task Manager -> server -> runTask -> Task Already Running -> taskId = ' + message.event.taskId) + return + } + //console.log('[INFO] Task Manager -> server -> runTask -> Task Name = ' + message.event.taskName) + //console.log('[INFO] Task Manager -> server -> runTask -> Task Id = ' + message.event.taskId) + + let path = process.env.TASK_SERVER_PATH + '/server.js' + + /* Workarround to avoid having the same debug port at the forked process which makes it crash. */ + for (let i = 0; i < process.execArgv.length; i++) { + let argument = process.execArgv[i] + if (argument.indexOf('--inspect') > -1) { + process.execArgv[i] = '' + } + } + + /* Forking this process. */ + let task = { + childProcess: fork(path, [message.event.taskId], { stdio: 'inherit' }), + id: message.event.taskId, + name: message.event.taskName + } + + tasks.set(message.event.taskId, task) + + task.childProcess.on('error', (err) => { + console.log('[ERROR] Task Manager -> server -> runTask -> Problem with Task Name = ' + task.name) + console.log('[ERROR] Task Manager -> server -> runTask -> Problem with Task Id = ' + task.id) + console.log(`[ERROR] Task Manager -> server -> runTask -> Task Manager exited with error ${err}`) + }) + task.childProcess.on('close', (code, signal) => { + //console.log('[INFO] Task Manager -> server -> runTask -> Task Terminated. -> Task Name = ' + task.name) + //console.log('[INFO] Task Manager -> server -> runTask -> Task Terminated. -> Task Id = ' + task.id) + tasks.delete(task.id) + }) + + setTimeout(sendStartingEvent, 3000) + + function sendStartingEvent() { + //console.log('[INFO] Task Manager -> server -> runTask -> Emitting Event -> key = ' + 'Task Server - ' + task.id) + global.SYSTEM_EVENT_HANDLER.raiseEvent('Task Server - ' + task.id, 'Run Task', message.event) + } +} + +function stopTask(message) { + + //console.log('[INFO] Task Manager -> server -> stopTask -> Entering function.') + + if (message.event === undefined) { + console.log('[WARN] Task Manager -> server -> stopTask -> Message Received Without Event -> message = ' + JSON.stringify(message)) + return + } + + if (message.event.taskId === undefined) { + console.log('[WARN] Task Manager -> server -> stopTask -> Message Received Without taskId -> message = ' + JSON.stringify(message)) + return + } + + //console.log('[INFO] Task Manager -> server -> stopTask -> Task Name = ' + message.event.taskName) + //console.log('[INFO] Task Manager -> server -> stopTask -> Task Id = ' + message.event.taskId) + + let task = tasks.get(message.event.taskId) + + if (task) { + task.childProcess.send('Stop this Task'); + //console.log('[INFO] Task Manager -> server -> stopTask -> Child Process instructed to finish.') + } else { + console.log('[WARN] Task Manager -> server -> stopTask -> Cannot delete Task that does not exist.') + } +} \ No newline at end of file diff --git a/TaskServer/.env b/TaskServer/.env new file mode 100644 index 000000000..4bc79e391 --- /dev/null +++ b/TaskServer/.env @@ -0,0 +1,10 @@ + +INTER_PROCESS_FILES_PATH=../Inter-Process-Files +STORAGE_PATH=../Data-Storage +PLOTTERS_PATH=../Legacy-Code + +USE_KEYVAULT=false +WRITE_LOGS_TO_FILES=true +BOTS_PATH=../Legacy-Code +LOG_PATH=../Log-Files + diff --git a/TaskServer/Assistant.js b/TaskServer/Assistant.js new file mode 100644 index 000000000..9a98b1cbd --- /dev/null +++ b/TaskServer/Assistant.js @@ -0,0 +1,1127 @@ +exports.newAssistant = function newAssistant(BOT, logger, UTILITIES) { + + /* + This module allows trading bots to execute actions on the exchange. + */ + + const MODULE_NAME = "Assistant"; + + let bot = BOT; + + let thisObject = { + dataDependencies: undefined, + initialize: initialize, + createOrder: createOrder, + getPositions: getPositions, + getBalance: getBalance, + getAvailableBalance: getAvailableBalance, + getInitialBalance: getInitialBalance, + getProfits: getProfits, + getCombinedProfits: getCombinedProfits, + getROI: getROI, + getMarketRate: getMarketRate, + getTicker: getTicker, + sendMessage: sendMessage, + rememberThis: rememberThis, + remindMeOf: remindMeOf, + truncDecimals: truncDecimals, + sendEmail: sendEmail, + addExtraData: addExtraData + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + + let openOrders = []; // These are the open positions at the exchange at the account the bot is authorized to use. + let openPositions = []; // These are the open positions the bot knows it made by itself. + + let context; + let exchangeAPI; + + let marketRate; + let ticker; + + return thisObject; + + function initialize(pContext, pExchangeAPI, pDataDependencies, callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + /* Save local values. */ + + context = pContext; + exchangeAPI = pExchangeAPI; + thisObject.dataDependencies = pDataDependencies; + + switch (bot.startMode) { + + case 'Live': { + getMarketRateFromExchange(); + break; + } + + case 'Backtest': { + validateExchangeSyncronicity(); + break; + } + + default: { + logger.write(MODULE_NAME, "[ERROR] initialize -> Unexpected bot.startMode."); + logger.write(MODULE_NAME, "[ERROR] initialize -> bot.startMode = " + bot.startMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function getMarketRateFromExchange() { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> Entering function."); } + + exchangeAPI.getTicker(bot.market, onTicker); + + return; + + function onTicker(err, pTicker) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> onTicker -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> onTicker -> We could not get the Market Price now."); } + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + ticker = pTicker; + marketRate = pTicker.last; + + context.newHistoryRecord.marketRate = marketRate; + + validateExchangeSyncronicity(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromExchange -> onTicker -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromExchange -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function validateExchangeSyncronicity() { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> validateExchangeSyncronicity -> Entering function."); } + + /* Procedure to validate we are in sync with the exchange. */ + + getPositionsAtExchange(onDone); + + function onDone(err) { + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> validateExchangeSyncronicity -> onDone -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> validateExchangeSyncronicity -> onDone -> Execution finished well."); } + calculateProfits(); + return; + } + break; + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] initialize -> validateExchangeSyncronicity -> onDone -> Retry Later. Requesting Execution Retry."); + callBackFunction(err); + return; + } + break; + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] initialize -> validateExchangeSyncronicity -> onDone -> Operation Failed. Aborting the process."); + callBackFunction(err); + return; + } + break; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> validateExchangeSyncronicity -> onDone -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function calculateProfits() { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> calculateProfits -> Entering function."); } + + /* Calculate Profits */ + + if (context.executionContext.initialBalance.baseAsset > 0) { + + context.executionContext.profits.baseAsset = (thisObject.truncDecimals(context.executionContext.balance.baseAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset); + context.executionContext.profits.baseAsset = thisObject.truncDecimals(context.executionContext.profits.baseAsset); + } + + if (context.executionContext.initialBalance.quotedAsset > 0) { + + context.executionContext.profits.quotedAsset = (thisObject.truncDecimals(context.executionContext.balance.quotedAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset); + context.executionContext.profits.quotedAsset = thisObject.truncDecimals(context.executionContext.profits.quotedAsset); + } + + context.newHistoryRecord.profitsBaseAsset = context.executionContext.profits.baseAsset; + context.newHistoryRecord.profitsQuotedAsset = context.executionContext.profits.quotedAsset; + + /* Calculate Combined Profits */ + + if (context.executionContext.initialBalance.baseAsset > 0) { + + let convertedAssetsB = (thisObject.truncDecimals(context.executionContext.balance.quotedAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset)) * marketRate; + convertedAssetsB = thisObject.truncDecimals(convertedAssetsB); + + context.executionContext.combinedProfits.baseAsset = (thisObject.truncDecimals(context.executionContext.balance.baseAsset) + convertedAssetsB - thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset) * 100; + context.executionContext.combinedProfits.baseAsset = thisObject.truncDecimals(context.executionContext.combinedProfits.baseAsset); + } + + if (context.executionContext.initialBalance.quotedAsset > 0) { + + let convertedAssetsA = (thisObject.truncDecimals(context.executionContext.balance.baseAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset)) / marketRate; + convertedAssetsA = thisObject.truncDecimals(convertedAssetsA); + + context.executionContext.combinedProfits.quotedAsset = (thisObject.truncDecimals(context.executionContext.balance.quotedAsset) + convertedAssetsA - thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset) * 100; + context.executionContext.combinedProfits.quotedAsset = thisObject.truncDecimals(context.executionContext.combinedProfits.quotedAsset); + } + + context.newHistoryRecord.combinedProfitsA = context.executionContext.combinedProfits.baseAsset; + context.newHistoryRecord.combinedProfitsB = context.executionContext.combinedProfits.quotedAsset; + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> calculateProfits -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getPositionsAtExchange(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Entering function."); } + + /* + + Here we grab all the positions at the exchange for the account we are using for trading. We will not asume all the positions + were made by this bot, but we need them all to later check if all the ones made by the bot are still there, were executed or + manually cancelled by the exchange account owner. + + */ + + switch (bot.startMode) { + + case "Live": { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Live Mode Detected."); } + exchangeAPI.getOpenOrders(bot.market, onResponse); + break; + } + + case "Backtest": { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Backtest Mode Detected."); } + let openOrders = []; // We simulate all positions were executed. + onResponse(global.DEFAULT_OK_RESPONSE, openOrders); + break; + } + + default: { + logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> Unexpected bot.startMode."); + logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> bot.startMode = " + bot.startMode); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function onResponse(err, pOpenOrders) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> pOpenOrders = " + JSON.stringify(pOpenOrders)); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> Execution finished well."); } + openOrders = pOpenOrders; + ordersExecutionCheck(callBack); + return; + } + break; + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> onResponse -> Retry Later. Requesting Execution Retry."); + callBack(err); + return; + } + break; + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> onResponse -> Operation Failed. Aborting the process."); + callBack(err); + return; + } + break; + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function ordersExecutionCheck(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> Entering function."); } + + /* + + Here we check that all the positions we know are still valid at the exchange. If they are not, we will try to take appropiate + actions. Reasons why the positions might not be there are: + + 1. The user / account owner closed the positions manually. + 2. The exchange for some reason closed the positions. In some exchanges positions have an expiration time. + 3. The orders were executed. + + Situations 1 and 2 are similar unexpected and we will stop the bot execution when we detect them. Number 3 is an + expected behaviour and we will take appropiate action. + + Also, the position might still be there but it might have been partially executed. To detect that we need to compare the + position amounts we have on file to the one we are receiving from the exchange. + + Before we begin, we have to remove all the orders that have been already closed at the last execution of the bot. + + */ + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> JSON.stringify(context.executionContext.positions) = " + JSON.stringify(context.executionContext.positions)); } + + let openPositions = []; + + for (let a = 0; a < context.executionContext.positions.length; a++) { + + if (context.executionContext.positions[a].status === "open") { + + openPositions.push(context.executionContext.positions[a]); + + } + } + + /* + + This removes all orders leaving only the not executed position. Consider that we do want executed positions to be recored on the file so as to + have a record of their execution. But at next execution these records must be deleted before the new Execution Context file is created and + this is what we do here. + + */ + + context.executionContext.positions = openPositions; + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> Removing executed positions."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> JSON.stringify(context.executionContext.positions) = " + JSON.stringify(context.executionContext.positions)); } + + /* Now we can start checking what happened at the exchange. */ + + let i = 0; + + controlLoop(); + + function loopBody() { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> Entering function."); } + + let position = context.executionContext.positions[i]; + let exchangePosition; + + for (let j = 0; j < openOrders.length; j++) { + + if (position.id === openOrders[j].id) { + + exchangePosition = openOrders[j]; + positionFound(); + return; + } + } + + positionNotFound(); + return; + + function positionFound() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> positionFound -> Entering function."); } + + /* + We update the final amountA based on what we got from the exchange. + This is needed since the values could be different from what we calculate. + */ + position.amountA = exchangePosition.amountA; + + /* + We need to know if the order was partially executed. To know that, we compare the amounts on file with the ones + received from the exchange. If they are the same, then the order is intact. Otherwise, to confirm a partial execution, + we will request the associated trades from the exchange. + */ + + if (position.amountB === exchangePosition.amountB) { + + /* Position is still there, untouched. Nothing to do here. */ + + next(); + return; + + } else { + + getPositionTradesAtExchange(position.id, confirmOrderWasPartiallyExecuted); + return; + + } + } + + function positionNotFound() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> Entering function."); } + + /* Position not found: we need to know if the order was executed. */ + + getPositionTradesAtExchange(position.id, confirmOrderWasExecuted); + + } + + function getPositionTradesAtExchange(pOrderId, innerCallBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> pOrderId = " + pOrderId); } + + /* + + Given one position, we request all the associated trades to it. + + */ + + switch (bot.startMode) { + + case "Live": { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Live Mode Detected."); } + exchangeAPI.getOrder(pOrderId, bot.market, onResponse); + return; + + } + + case "Backtest": { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Backtest Mode Detected."); } + + let trades = []; + + /* We look for the position at the executionContext */ + + for (let i = 0; i < context.executionContext.positions.length; i++) { + + let thisOrder = context.executionContext.positions[i]; + + if (thisOrder.id === pOrderId) { + + let feeRate = 0.002; // Default backtesting fee simulation + + let trade = { + id: Math.trunc(Math.random(1) * 1000000), + type: thisOrder.type, + rate: thisOrder.rate.toString(), + amountA: thisObject.truncDecimals(thisOrder.amountA).toString(), + amountB: thisObject.truncDecimals(thisOrder.amountB).toString(), + fee: thisObject.truncDecimals(feeRate).toString(), + date: (new Date()).valueOf() + } + + trades.push(trade); + + let order = { + trades: trades + } + onResponse(global.DEFAULT_OK_RESPONSE, order); + + return; + } + } + + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Position not found at Executioin Context."); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + + default: { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Unexpected bot.startMode."); + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> bot.startMode = " + bot.startMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function onResponse(err, order) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Execution finished well."); } + innerCallBack(order); + } + break; + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Retry Later. Requesting Execution Retry."); + callBack(global.DEFAULT_RETRY_RESPONSE); + return; + } + break; + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Operation Failed. Aborting the process."); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + break; + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function confirmOrderWasExecuted(order) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> trades = " + JSON.stringify(trades)); } + + let trades = order.trades + /* + + To confirm everything is ok, we will add all the amounts on trades asociated to the order and + they must be equal to the one on file. Otherwise something very strange could have happened, + in which case we will halt the bot execution. + + */ + + let sumBaseAsset = 0; + let sumQuotedAsset = 0; + let sumRate = 0; + + for (let k = 0; k < trades.length; k++) { + let trade = trades[k]; + sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(trade.amountA); + sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(trade.amountB); + sumRate = sumRate + thisObject.truncDecimals(trade.rate); + } + + sumBaseAsset = thisObject.truncDecimals(sumBaseAsset); + sumQuotedAsset = thisObject.truncDecimals(sumQuotedAsset); + + if (position.amountB !== sumQuotedAsset) { + logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> position.amountB = " + position.amountB); + logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> sumQuotedAsset = " + sumQuotedAsset); + + logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Cannot be confirmed that the order was executed. It must be manually cancelled by the user or cancelled by the exchange itself."); + logger.write(MODULE_NAME, "[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> If the process was abruptally cancelled and then restarted, it is possible that now is not sincronized with the exchange."); + logger.write(MODULE_NAME, "[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> In any case, to continue, you must manually delete the orders at the exchange."); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + + /* + + Confirmed that order was executed. Next thing to do is to remember the trades and change its status. + + */ + + position.status = "executed"; + + if (position.type === "sell") { + context.newHistoryRecord.sellExecRate = sumRate / trades.length; + } else { + context.newHistoryRecord.buyExecRate = sumRate / trades.length; + } + + applyTradesToContext(trades); + + let newTransaction = { + type: position.type + " executed", + position: position + }; + + context.executionContext.transactions.push(newTransaction); + + /* All done. */ + + next(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function confirmOrderWasPartiallyExecuted(trades) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Entering function."); } + + let trades = order.trades + + /* + + To confirm everything is ok, we will add all the amounts on trades plus the remaining amounts + at the order at the exchange and they must be equal to the one on file. Otherwise an inconsistent + state has been detected, in which case we will halt the bot execution. + + */ + + let sumBaseAsset = 0; + let sumQuotedAsset = 0; + + for (let k = 0; k < trades.length; k++) { + let trade = trades[k]; + sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(trade.amountA); + sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(trade.amountB); + } + + /* To this we add the current position amounts. */ + + sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(exchangePosition.amountA); + sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(exchangePosition.amountB); + + sumBaseAsset = thisObject.truncDecimals(sumBaseAsset); + sumQuotedAsset = thisObject.truncDecimals(sumQuotedAsset); + + /* + * Next let's get the max decimal positions for the pair being trade returned by the exchange and validate that at least + * one decimal position less than the max matches. The reason for this is because there could be some + * rounding on the exchange when there are partial executed orders that will interfiere with the exact validation. + * + * IE: For Poloniex USDT/BTC the max returned decimal positions is 8, it means we will validate that the diference is less than 0.0000001 + */ + + let exchangeParam = exchangeAPI.getMaxDecimalPositions(); + let minValue = '0.' + (1).toPrecision(exchangeParam - 1).split('.').reverse().join(''); + let exchangePrecision = parseFloat(parseFloat(minValue).toFixed(exchangeParam)); + + if (Math.abs(position.amountA - sumBaseAsset) > exchangePrecision || Math.abs(position.amountB - sumQuotedAsset) > exchangePrecision) { + + logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountA = " + position.amountA); + logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumBaseAsset = " + sumBaseAsset); + logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountB = " + position.amountB); + logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumQuotedAsset = " + sumQuotedAsset); + + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Cannot confirm that a partial execution was done well."); + + /* There are diferences on the responses between the getPosition and getTrades that causes some issues, let's retry. */ + callBack(global.DEFAULT_RETRY_RESPONSE); + return; + } + + /* + + Confirmed that order was partially executed. Next thing to do is to remember the trades and the new position. + + */ + + position.amountA = exchangePosition.amountA; + position.amountB = exchangePosition.amountB; + position.date = (new Date(exchangePosition.date)).valueOf(); + + applyTradesToContext(trades); + + let newTransaction = { + type: position.type + " partially executed", + position: position + }; + + context.executionContext.transactions.push(newTransaction); + + /* All done. */ + + next(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function applyTradesToContext(trades) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> applyTradesToContext -> Entering function."); } + + /* Here we apply the trades that already happened at the exchange to the balance and available balance of the bot. We also calculate its profits. */ + + for (k = 0; k < trades.length; k++) { + + let trade = trades[k]; + + position.trades.push(trade); + + context.newHistoryRecord.newTrades++; + + /* Calculate Balances */ + + let baseAsset = 0; + let quotedAsset = 0; + + if (trade.type === 'buy') { + + /* + + The fee received at each trade is the factor by which if we multiply asset B, we get the amount of Asset B extracted as fees by the exchange. + This happens with each individual trade. Remeber that one order can potentially be executed with 1 to many trades. + + */ + + let feeAmount = thisObject.truncDecimals(Number(trade.fee) * Number(trade.amountB)); + + baseAsset = Number(trade.amountA); + quotedAsset = Number(trade.amountB) - feeAmount; + + context.executionContext.balance.baseAsset = thisObject.truncDecimals(context.executionContext.balance.baseAsset - baseAsset); + context.executionContext.balance.quotedAsset = thisObject.truncDecimals(context.executionContext.balance.quotedAsset + quotedAsset); + + context.executionContext.availableBalance.quotedAsset = thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset + quotedAsset); + + /* Not the available balance for asset A is not affected since it was already reduced when the order was placed. */ + + } + + if (trade.type === 'sell') { + + /* + + The fee received at each trade is the factor by which if we multiply asset A, we get the amount of Asset A extracted as fees by the exchange. + This happens with each individual trade. Remeber that one order can potentially be executed with 1 to many trades. + + */ + + let feeAmount = thisObject.truncDecimals(Number(trade.fee) * Number(trade.amountA)); + + baseAsset = Number(trade.amountA) - feeAmount; + quotedAsset = Number(trade.amountB); + + context.executionContext.balance.baseAsset = thisObject.truncDecimals(context.executionContext.balance.baseAsset + baseAsset); + context.executionContext.balance.quotedAsset = thisObject.truncDecimals(context.executionContext.balance.quotedAsset - quotedAsset); + + context.executionContext.availableBalance.baseAsset = thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset + baseAsset); + + /* Not the available balance for asset B is not affected since it was already reduced when the order was placed. */ + + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> applyTradesToContext -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function next() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> next -> Entering function."); } + + i++; + controlLoop(); + + } + + function controlLoop() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> controlLoop -> Entering function."); } + + if (i < context.executionContext.positions.length) { + + loopBody(); + + } else { + + final(); + + } + } + + function final() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> final -> Entering function."); } + + callBack(global.DEFAULT_OK_RESPONSE); + + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function createOrder(pType, pRate, pAmountA, pAmountB, callBackFunction) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> Entering function."); } + + /* Removing extra decimals. */ + + pRate = thisObject.truncDecimals(pRate); + pAmountA = thisObject.truncDecimals(pAmountA); + pAmountB = thisObject.truncDecimals(pAmountB); + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pType = " + pType); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pRate = " + pRate); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pAmountA = " + pAmountA); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pAmountB = " + pAmountB); } + + /* Validations that the limits are not surpassed. */ + + if (pType === 'buy') { + + if (pAmountA > thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset)) { + + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountA is grater than the Available Balance."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountA = " + pAmountA); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset)); + + let err = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: 'Not Enough Available Balance to Buy.' + }; + + callBackFunction(err); + return; + } + } + + if (pType === 'sell') { + + if (pAmountB > thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset)) { + + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountB is grater than the Available Balance."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountB = " + pAmountB); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset)); + + let err = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: 'Not Enough Available Balance to Sell.' + }; + + callBackFunction(err); + return; + } + } + + /* All validations passed, we proceed. */ + + switch (bot.startMode) { + + case "Live": { + + exchangeAPI.createOrder(bot.market, pType, pRate, pAmountA, pAmountB, onResponse); + return; + } + + case "Backtest": { + + if (pRate !== marketRate) { + + logger.write(MODULE_NAME, "[WARNING] createOrder -> Input Validations -> createOrder Rate can is different to marketRate while in Backtesting Mode. "); + //onResponse(global.DEFAULT_FAIL_RESPONSE, positionId); + } + + let positionId = Math.trunc(Math.random(1) * 1000000); + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> Simulating Exchange Response -> orderId = " + positionId); } + onResponse(global.DEFAULT_OK_RESPONSE, positionId); + return; + } + + default: { + logger.write(MODULE_NAME, "[ERROR] createOrder -> Unexpected bot.startMode."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> bot.startMode = " + bot.startMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + function onResponse(err, order) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> order.id = " + order.id); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> Execution finished well."); } + + let position = { + id: order.id, + type: pType, + rate: pRate, + amountA: pAmountA, + amountB: pAmountB, + date: (bot.processDatetime.valueOf()), + status: "open", + trades: [] + }; + + context.executionContext.positions.push(position); + + let newTransaction = { + type: "newPosition", + position: position + }; + + context.executionContext.transactions.push(newTransaction); + + context.newHistoryRecord.newPositions++; + + recalculateRateAverages(); + + /* Recalculate Available Balances */ + + if (position.type === 'buy') { + + context.executionContext.availableBalance.baseAsset = thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset - pAmountA); + context.newHistoryRecord.lastBuyRate = pRate; + } + + if (position.type === 'sell') { + + context.executionContext.availableBalance.quotedAsset = thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset - pAmountB); + context.newHistoryRecord.lastSellRate = pRate; + } + + callBackFunction(global.DEFAULT_OK_RESPONSE, position); + return; + } + break; + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> Retry Later. Requesting Execution Retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + break; + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> Operation Failed. Aborting the process."); + callBackFunction(err); + return; + } + break; + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] createOrder -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getPublicTradeHistory(baseAsset, quotedAsset, startTime, endTime, callback) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTradeHistory -> Entering function."); } + + exchangeAPI.getPublicTradeHistory(baseAsset, quotedAsset, startTime, endTime, callback); + } + + function recalculateRateAverages() { + + /* This function calculates the weighted rates of buy and sell positions. This is a way to summarize with only one rate, where the positions are. */ + + let sumBuyWeightedRates = 0; + let sumSellWeightedRates = 0; + + let sumBuyWeights = 0; + let sumSellWeights = 0; + + for (let i = 0; i < context.executionContext.positions.length; i++) { + + let position = context.executionContext.positions[i]; + + if (position.type === 'buy') { + + sumBuyWeightedRates = sumBuyWeightedRates + position.rate * position.amountB + sumBuyWeights = sumBuyWeights + position.amountB; + + } + + if (position.type === 'sell') { + + sumSellWeightedRates = sumSellWeightedRates + position.rate * position.amountB + sumSellWeights = sumSellWeights + position.amountB; + + } + + } + + context.newHistoryRecord.buyAvgRate = (sumBuyWeightedRates / sumBuyWeights || 0); + context.newHistoryRecord.sellAvgRate = (sumSellWeightedRates / sumSellWeights || 0); + } + + function getPositions() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositions -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositions -> JSON.stringify(context.executionContext.positions) = " + JSON.stringify(context.executionContext.positions)); } + + return JSON.parse(JSON.stringify(context.executionContext.positions)); + } + + function getBalance() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getBalance -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getBalance -> JSON.stringify(context.executionContext.balance) = " + JSON.stringify(context.executionContext.balance)); } + + return JSON.parse(JSON.stringify(context.executionContext.balance)); + } + + function getAvailableBalance() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getAvailableBalance -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getAvailableBalance -> JSON.stringify(context.executionContext.availableBalance) = " + JSON.stringify(context.executionContext.availableBalance)); } + + return JSON.parse(JSON.stringify(context.executionContext.availableBalance)); + } + + function getInitialBalance() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getInitialBalance -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getInitialBalance -> JSON.stringify(context.executionContext.initialBalance) = " + JSON.stringify(context.executionContext.initialBalance)); } + + return JSON.parse(JSON.stringify(context.executionContext.initialBalance)); + } + + function getProfits() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getProfits -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getProfits -> JSON.stringify(context.executionContext.profits) = " + JSON.stringify(context.executionContext.profits)); } + + return JSON.parse(JSON.stringify(context.executionContext.profits)); + } + + function getCombinedProfits() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getCombinedProfits -> Entering function."); } + + let combinedProfits = { + baseAsset: context.newHistoryRecord.combinedProfitsA, + quotedAsset: context.newHistoryRecord.combinedProfitsB + } + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getCombinedProfits -> JSON.stringify(combinedProfits) = " + JSON.stringify(combinedProfits)); } + + return JSON.parse(JSON.stringify(combinedProfits)); + } + + function getROI() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getROI -> Entering function."); } + + let ROI = { + baseAsset: (context.executionContext.balance.baseAsset - context.executionContext.initialBalance.baseAsset) / context.executionContext.initialBalance.baseAsset * 100, + quotedAsset: (context.executionContext.balance.quotedAsset - context.executionContext.initialBalance.quotedAsset) / context.executionContext.initialBalance.quotedAsset * 100 + } + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getROI -> JSON.stringify(ROI) = " + JSON.stringify(ROI)); } + + return JSON.parse(JSON.stringify(ROI)); + } + + function getMarketRate() { + return marketRate; + } + + function getTicker() { + return ticker; + } + + function sendMessage(pRelevance, pTitle, pBody) { + let runIndex = context.statusReport.runs.length - 1; + + context.newHistoryRecord.messageRelevance = pRelevance; + context.newHistoryRecord.messageTitle = bot.startMode + "." + runIndex + ": " + pTitle; + context.newHistoryRecord.messageBody = pBody; + } + + function rememberThis(pKey, pValue) { + if (context.executionContext.remember === undefined) { + context.executionContext.remember = {}; + } + context.executionContext.remember[pKey] = pValue; + } + + function remindMeOf(pKey) { + if (context.executionContext.remember === undefined) { + context.executionContext.remember = {}; + } + return context.executionContext.remember[pKey]; + } + + function sendEmail(pTitle, pBody, pTo) { + logger.write(MODULE_NAME, "[WARN] sendEmail -> Send emails is currently disabled."); + return; + } + + function addExtraData(pExtraDataArray) { + context.extraData.push(pExtraDataArray); + } + + function truncDecimals(pFloatValue, pDecimals) { + if (!pDecimals) pDecimals = 8; // Default value + return parseFloat(parseFloat(pFloatValue).toFixed(pDecimals)); + } +}; diff --git a/TaskServer/CloudUtilities.js b/TaskServer/CloudUtilities.js new file mode 100644 index 000000000..1c8a9ccb6 --- /dev/null +++ b/TaskServer/CloudUtilities.js @@ -0,0 +1,85 @@ + +exports.newCloudUtilities = function newCloudUtilities(logger) { + + const FULL_LOG = true; + + const MODULE_NAME = "Cloud Utilities"; + + let utilities = { + pad: pad, + createFolderIfNeeded: createFolderIfNeeded, + calculatePresicion: calculatePresicion + }; + + return utilities; + + function createFolderIfNeeded(path, azureFileStorage, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] createFolderIfNeeded -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] createFolderIfNeeded -> path = " + path); } + + let splittedPath = path.split("/"); + + let partialPath = ''; + let separator = ""; + + let i = 0; + loop(); + + function loop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] createFolderIfNeeded -> path = " + path); } + + partialPath = partialPath + separator + splittedPath[i]; + separator = "/"; + + i++; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] createFolderIfNeeded -> partialPath = " + partialPath); } + + azureFileStorage.createFolder(partialPath, checkLoop); + + function checkLoop(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] createFolderIfNeeded -> loop -> checkLoop -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] createFolderIfNeeded -> loop -> checkLoop -> err = "+ err.stack); + callBackFunction(err); + return; + } + + if (i === splittedPath.length) { + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } else { + + loop(); + + } + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] createFolderIfNeeded -> Error = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + function calculatePresicion(number) { + + for (let i = -10; i <= 10; i++) { + if (number < Math.pow(10, i)) { + return Math.pow(10, i - 3); + } + } + } + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad("0" + str, max) : str; + } +} diff --git a/TaskServer/Commons.js b/TaskServer/Commons.js new file mode 100644 index 000000000..5ad2fb509 --- /dev/null +++ b/TaskServer/Commons.js @@ -0,0 +1,518 @@ +exports.newCommons = function newCommons(bot, logger, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "Commons"; + + let thisObject = { + validateDataDependencies: validateDataDependencies, + validateOutputDatasets: validateOutputDatasets, + inflateDatafiles: inflateDatafiles, + dataBuildingProcedure: dataBuildingProcedure, + calculationsProcedure: calculationsProcedure, + generateFileContent: generateFileContent, + writeFile: writeFile + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + return thisObject; + + function validateDataDependencies(dataDependencies, callBackFunction) { + for (let i = 0; i < dataDependencies.length; i++) { + + let dataDependencyNode = dataDependencies[i] + + /* Basic validations to see if we have everything we need. */ + if (dataDependencyNode.referenceParent.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataDependencyNode.referenceParent.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataDependencyNode.referenceParent.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + return true + } + + function validateOutputDatasets(outputDatasets, callBackFunction) { + for (let i = 0; i < outputDatasets.length; i++) { + let outputDatasetNode = outputDatasets[i] + + if (outputDatasetNode.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Output Dataset without Reference Parent. Output Dataset = " + JSON.stringify(outputDatasetNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.dataBuilding === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> Product Definition " + outputDatasetNode.referenceParent.parentNode.name + " without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + } + if (outputDatasetNode.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot witn no codeName defined. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Data Mine. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Data Mine witn no codeName defined. Data Mine = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + return true + } + + function inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timeFrame) { + /* + For each dataDependencyNode in our data dependencies, we should have a dataFile containing the records needed as an imput for this process. + What we need to do first is transform those records into JSON objects that can be used by user-defined formulas. + The first step does that but with the not calculated properties, the second step adds the calculated properties. + */ + for (let i = 0; i < dataDependencies.length; i++) { + + let dataFile + let jsonData // Datafile converted into Json objects + let inputData // Includes calculated properties + let singularVariableName // name of the variable for this product + let recordDefinition + + let dataDependencyNode = dataDependencies[i] + dataFile = dataFiles.get(dataDependencyNode.id) + + if (dataFile === undefined) { continue } // When a datafile is not found it might be because we are processing market or daily and at the dependency array there are both types mixed up. + + recordDefinition = dataDependencyNode.referenceParent.parentNode.record + singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName + /* Transform the raw data into JSON objects */ + jsonData = jsonifyDataFile(dataFile, recordDefinition) + /* Add the calculated properties */ + if (dataDependencyNode.referenceParent.parentNode.calculations !== undefined) { + inputData = calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.referenceParent.parentNode.calculations, singularVariableName, timeFrame) + } else { + inputData = jsonData + } + products[dataDependencyNode.referenceParent.parentNode.code.pluralVariableName] = inputData + + /* The main dependency is defined as the first dependency processed. */ + if (mainDependency.records === undefined) { + mainDependency.records = inputData + } + } + } + + function jsonifyDataFile(dataFile, recordDefinition) { + + /* + This function has as an input the raw data on files and creates with it an array of JSON objects + with not calculated properties for later being consumed by Formulas + */ + + let jsonifiedArray = [] + let previous; + + for (let i = 0; i < dataFile.length; i++) { + + let record = {} + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + record[property.code.codeName] = dataFile[i][j] + } + } + + record.previous = previous; + jsonifiedArray.push(record); + previous = record; + } + + return jsonifiedArray + } + + function calculationsProcedure(jsonArray, recordDefinition, calculationsProcedure, variableName, timeFrame) { + + /* + This function has as an input an array of JSON objects, and it adds calculated properties to + complete the set of properties that will be available for Formulas. + */ + + let system = { // These are the available system variables to be used in User Code and Formulas + timeFrame: timeFrame, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] + + /* This is Initialization Code */ + if (calculationsProcedure.initialization !== undefined) { + if (calculationsProcedure.initialization.javascriptCode !== undefined) { + try { + eval(calculationsProcedure.initialization.javascriptCode.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = " + calculationsProcedure.initialization.javascriptCode.code); + throw ("Error Executing User Code.") + } + } + } + + /* This is Initialization Code */ + if (calculationsProcedure.loop !== undefined) { + if (calculationsProcedure.loop.javascriptCode !== undefined) { + for (let index = 0; index < jsonArray.length; index++) { + + let product = {} + product[variableName] = jsonArray[index] + + /* This is Loop Code */ + try { + eval(calculationsProcedure.loop.javascriptCode.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = " + calculationsProcedure.loop.javascriptCode.code); + throw ("Error Executing User Code.") + } + + /* For each calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated === true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + try { + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + } + } + } + } + + /* Adding the new element to the resulting array */ + results.push(product[variableName]); + } + } + } + return results + } + + function dataBuildingProcedure( + products, + mainDependency, + recordDefinition, + dataBuildingProcedure, + variableName, + productName, + timeFrame, + timeFrameLabel, + resultsWithIrregularPeriods, + interExecutionMemory, + processingDailyFiles, + currentDay + ) { + + /* + This function has as an input the products object, with all the information + of all products calculated so far by the process. Based on that information + the function will evaluate user supplied code and formulas in order to build + a new set of information. + */ + + let lastInstantOfTheDay + let yesterday = {} + let system = { // These are the available system variables to be used in User Code and Formulas + timeFrame: timeFrame, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] + + /* This is Initialization Code */ + if (dataBuildingProcedure.initialization !== undefined) { + if (dataBuildingProcedure.initialization.javascriptCode !== undefined) { + try { + eval(dataBuildingProcedure.initialization.javascriptCode.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Code = " + dataBuildingProcedure.initialization.javascriptCode.code); + throw ("Error Executing User Code.") + } + } + } + + if (processingDailyFiles) { + /* Initialization of Last Instance */ + lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + + if (interExecutionMemory[productName] === undefined) { + /* The first time the intialization variables goes to the Inter Execution Memory. */ + interExecutionMemory[productName] = {} + interExecutionMemory[productName].variable = JSON.parse(JSON.stringify(variable)) + } + else { + /* We override the initialization, since the valid stuff is already at the Inter Execution Memory */ + variable = JSON.parse(JSON.stringify(interExecutionMemory[productName].variable)) + } + } + + /* This is Initialization Code */ + if (dataBuildingProcedure.loop !== undefined) { + if (dataBuildingProcedure.loop.javascriptCode !== undefined) { + let lastRecord + for (let index = 0; index < mainDependency.records.length; index++) { + + let record = { + previous: mainDependency.records[index - 1], + current: mainDependency.records[index], + next: mainDependency.records[index + 1] + } + lastRecord = record + let product = {} + product[variableName] = {} + + /* Here is how we know if we are processing Yesterday. */ + let positionedAtYesterday = false + if (processingDailyFiles) { + positionedAtYesterday = (record.current.end < currentDay.valueOf()) + } + + /* This is Loop Code */ + try { + eval(dataBuildingProcedure.loop.javascriptCode.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Code = " + dataBuildingProcedure.loop.javascriptCode.code); + throw ("Error Executing User Code.") + } + + /* For each non-calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + try { + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + } + } + } + } + + /* If within the code the new data structure was not yet pushed to the results array, we do it */ + if (resultsWithIrregularPeriods !== true) { + /* Adding the new element to the resulting array */ + results.push(product[variableName]); + } + + /* While we are positioned at Yesterday, we keey updating this data structure. */ + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.variable = JSON.parse(JSON.stringify(variable)) + } + } + } + /* + Before returning we need to see if we have to record some of our counters at the interExecutionMemory. + To do that, the condition to be met is that this execution must include all candles of the current day. + */ + if (processingDailyFiles) { + if (lastRecord.current.end === lastInstantOfTheDay && yesterday.variable !== undefined) { + interExecutionMemory[productName] = {} + interExecutionMemory[productName].variable = JSON.parse(JSON.stringify(yesterday.variable)) + } + } + } + } + return results + } + + function generateFileContent(records, recordDefinition, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> generateFileContent -> Entering function."); } + + let fileContent = ""; + let recordSeparator = ""; + + for (let i = 0; i < records.length; i++) { + let record = records[i]; + + if (processingDailyFiles === true) { + + if (resultsWithIrregularPeriods === true) { + /* + Here we have an special problem that occurs when an object spans several time peridos. If not taken care of + it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of + its properties. + + To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it + at the next day, in whole, even if it starts in the previous day. + */ + + let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + + if (record.end < currentDay.valueOf() - 1) { continue; } + if (record.end === lastInstantOdDay) { continue; } + } else { + /* + The normal situation is that objects are respecting the time size of its main dependency, in which case we only + care of not adding objects of the previous day we are currently processing (while on daily files) + */ + if (record.end < currentDay.valueOf()) { continue; } + } + } + + fileContent = fileContent + recordSeparator + '[' + let propertySeparator = "" + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + fileContent = fileContent + propertySeparator + if (property.code.isString === true) { + fileContent = fileContent + '"' + } + fileContent = fileContent + record[property.code.codeName] + if (property.code.isString === true) { + fileContent = fileContent + '"' + } + if (propertySeparator === "") { propertySeparator = ","; } + } + } + fileContent = fileContent + ']' + if (recordSeparator === "") { recordSeparator = ","; } + } + fileContent = "[" + fileContent + "]"; + return fileContent + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> generateFileContent -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timeFrameLabel, currentDay, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> Entering function."); } + + let market = bot.market; + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let dateForPath = '' + + if (processingDailyFiles === true) { + dateForPath = "/" + currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + } + + let filePathRoot = contextSummary.dataMine + "/" + contextSummary.bot + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + timeFrameLabel + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + anotherFileWritten(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +} + diff --git a/TaskServer/Context.js b/TaskServer/Context.js new file mode 100644 index 000000000..bc347977b --- /dev/null +++ b/TaskServer/Context.js @@ -0,0 +1,551 @@ +exports.newContext = function newContext(BOT, logger, UTILITIES) { + + let bot = BOT; + + const MODULE_NAME = "Context"; + + /* + + This module deals with keeping the context between different executions of the bot. Conceptually this context is divided in 3 main parts: + + I. The non-business information which helps the mechanisms of giving continuety to the bots execution over time. This information goes in a file + called the Status Report, and there is a module, child of this one called Status Report that deals with loading and saving the contents of that file. + + II. The business information around the bot execution, including its balance, positions at exchanges, trades executed and many other things. These + are platform data mantained on behalf of the executing bot. All these is kept in one file per execution called the Execution Context. The collection + of all these files can later reconstruct the vital business information the bot had at each moment in time. + + II. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files + with all the business informatino the bot had each time it executed. We call this index the Execution History and there is one file to store it. + + */ + + let thisObject = { + statusReport: undefined, // Here is the information that defines which was the last sucessfull execution and some other details. + executionHistory: [], // This is the record of bot execution. + executionContext: undefined, // Here is the business information of the last execution of this bot process. + newHistoryRecord : { + date: undefined, + buyAvgRate: 0, + sellAvgRate: 0, + lastSellRate: 0, + sellExecRate: 0, + lastBuyRate: 0, + buyExecRate: 0, + marketRate: 0, + newPositions: 0, + newTrades: 0, + movedPositions: 0, + profitsBaseAsset: 0, + profitsQuotedAsset: 0, + combinedProfitsA: 0, + combinedProfitsB: 0, + messageRelevance: 0, + messageTitle: "", + messageBody: "" + }, + initialize: initialize, + saveThemAll: saveThemAll, + extraData: [] + }; + + /* + + During the process we will create a new History Record. This will go to the Context History file which essentially mantains an + index of all the bots executions. This file will later be plotted by the bot s plotter on the timeline, allowing end users to + know where there is information related to the actions taken by the bot. + + */ + + let statusReportModule; + + /* Utilities needed. */ + + let utilities = UTILITIES.newCloudUtilities(logger); + + /* Storage account to be used here. */ + + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + let statusDependencies; + + let runIndex; // This is the index for this run and depends on the startMode. + let sessionPath = '' + + return thisObject; + + function initialize(pStatusDependencies, callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + + if (bot.SESSION !== undefined) { + sessionPath = bot.SESSION.folderName + "/" + } + + /* + Here we get the positions the bot did and that are recorded at the bot storage account. We will use them through out the rest + of the process. + */ + thisObject.newHistoryRecord.date = bot.processDatetime; + + getStatusReport(onDone); + + function onDone(err) { + try { + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onDone -> Execution finished well."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> Retry Later. Requesting Execution Retry."); + callBackFunction(err); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> Operation Failed. Aborting the process."); + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> Operation Failed with custom response."); + logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = " + JSON.stringify(err)); + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getStatusReport(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getStatusReport -> Entering function."); } + + let key = bot.dataMine + "-" + bot.codeName + "-" + "Context" + "-" + bot.dataSetVersion; + + statusReportModule = statusDependencies.statusReports.get(key); + thisObject.statusReport = statusReportModule.file; + + /* The context is created in 2 situations: 1) when at the bot config is set not to continue using the same context after a new execution. 2) when the status report does not exist. */ + + if (bot.hasTheBotJustStarted === true || thisObject.statusReport.runs === undefined) { + + createConext(callBack); + + } else { + + runIndex = thisObject.statusReport.runs.length - 1; + + getExecutionHistory(callBack); + + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> getStatusReport -> err = "+ err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getExecutionHistory(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionHistory -> Entering function."); } + + let fileName = "Execution.History." + bot.startMode + "." + runIndex + ".json"; + let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + fileName; + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> onFileReceived -> err = "+ JSON.stringify(err)); + callBack(err); + return; + } + + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { + logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionHistory -> onFileReceived -> Content received = " + text); + } + + try { + + thisObject.executionHistory = JSON.parse(text); + + /* Move some values to the new record, in case there are no transactions that re-calculate them. */ + + thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[thisObject.executionHistory.length-1][1]; + thisObject.newHistoryRecord.sellAvgRate = thisObject.executionHistory[thisObject.executionHistory.length - 1][2]; + + getExecutionContext(callBack); + + } catch (err) { + + /* + + It might happen that the file content is corrupt or it does not exist. The bot can not run without an Execution Hitory, + since it is risky to ignore its own history unless it is its first execution in this mode ever. + + */ + + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> onFileReceived -> Bot cannot execute without the Execution History. -> Err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> err = "+ err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getExecutionContext(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionContext -> Entering function."); } + + let date; + + date = new Date(thisObject.statusReport.runs[runIndex].lastExecution); + thisObject.statusReport.runs[runIndex].lastExecution = bot.processDatetime; + thisObject.statusReport.runs[runIndex].endDatetime = bot.processDatetime; + + let fileName = "/Execution.Context." + bot.startMode + "." + runIndex + ".json"; + let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2); + let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + dateForPath + fileName; + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> onFileReceived -> err = "+ err.stack); + callBack(err); + return; + } + + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { + logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionContext -> onFileReceived -> Content received = " + text); + } + + try { + + thisObject.executionContext = JSON.parse(text); + thisObject.executionContext.transactions = []; // We record here the transactions that happened duting this execution. + + /* Update the new History Record, in case there are no trades during this execution that updates it. */ + + thisObject.newHistoryRecord.profitsBaseAsset = thisObject.executionContext.profits.baseAsset; + thisObject.newHistoryRecord.profitsQuotedAsset = thisObject.executionContext.profits.quotedAsset; + + thisObject.newHistoryRecord.combinedProfitsA = thisObject.executionContext.combinedProfits.baseAsset; + thisObject.newHistoryRecord.combinedProfitsB = thisObject.executionContext.combinedProfits.quotedAsset; + + callBack(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + + /* + + It might happen that the file content is corrupt or it does not exist. The bot can not run without a Status Report, + since it is risky to ignore its own execution history. Except when the bot is running for the fist time in this mode, + an execution context file with the right format is needed. + + */ + + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> onFileReceived -> Response from storage. -> Text = " + text); + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> onFileReceived -> Bot cannot execute without the Execution Context. -> Err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> err = "+ err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function createConext(callBack) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> createConext -> Entering function."); } + /* + + When the bot is executed for the very first time, there are a few files that do not exist and need to be created, and that + content is what we are going to set up now. + + */ + + if (thisObject.statusReport.runs === undefined) { // This means that the Status Report does not exist. + + thisObject.statusReport.runs = []; + } + + let runContent = { + beginDatetime: bot.processDatetime, + endDatetime: bot.processDatetime, + lastExecution: bot.processDatetime + }; + + thisObject.statusReport.runs.push(runContent); + runIndex = thisObject.statusReport.runs.length - 1; + + thisObject.executionHistory = []; + + if (bot.VALUES_TO_USE.initialBalanceA === undefined || bot.VALUES_TO_USE.initialBalanceB === undefined) { throw new Error("Global Variables INITIAL_BALANCE_A and INITIAL_BALANCE_B cannot be undefined.") } + + const INITIAL_BALANCE_A = Number(bot.VALUES_TO_USE.initialBalanceB); // NOTE THAT THIS IS INVERTED BECAUSE OF POLONIEX THAT IS THE ONLY EXCHANGE SUPPORTED RIGHT NOW + const INITIAL_BALANCE_B = Number(bot.VALUES_TO_USE.initialBalanceA); + + + thisObject.executionContext = { + initialBalance: { // This is used to calculate profits. + baseAsset: INITIAL_BALANCE_A, + quotedAsset: INITIAL_BALANCE_B + }, + balance: { // This is the total balance that includes positions at the order book + funds available to be traded. + baseAsset: INITIAL_BALANCE_A, + quotedAsset: INITIAL_BALANCE_B // It starts with the initial initialBalance. + }, + availableBalance: { // This is the balance the bot has at any moment in time available to be traded (not in positions at the order book). + baseAsset: INITIAL_BALANCE_A, + quotedAsset: INITIAL_BALANCE_B // It starts with the initial initialBalance. + }, + profits: { + baseAsset: 0, + quotedAsset: 0 + }, + combinedProfits: { + baseAsset: 0, + quotedAsset: 0 + }, + positions: [], + transactions: [] + }; + + callBack(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> createConext -> err = "+ err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function saveThemAll(callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> Entering function."); } + + writeExecutionContext(onDone); + + function onDone(err) { + try { + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> onDone -> Execution finished well."); } + + bot.hasTheBotJustStarted = false; + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + break; + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> onDone -> Retry Later. Requesting Execution Retry."); + callBackFunction(err); + return; + } + break; + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> onDone -> Operation Failed. Aborting the process."); + callBackFunction(err); + return; + } + break; + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> onDone -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeExecutionContext(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExecutionContext -> Entering function."); } + + let fileName = "/Execution.Context." + bot.startMode + "." + runIndex +".json"; + let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCHours(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCMinutes(), 2); + let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + dateForPath + fileName; + let fileContent = JSON.stringify(thisObject.executionContext); + + if(fileContent === undefined){ + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExecutionContext -> executionContext is undefined."); + callBackFunction(global.DEFAULT_ERROR_RESPONSE); + } + + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExecutionContext -> fileContent = " + fileContent); } + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExecutionContext -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExecutionContext -> onFileCreated -> err = "+ err.stack); + callBack(err); + return; + } + + writeExucutionHistory(callBack); + return; + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExecutionContext -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeExucutionHistory(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> Entering function."); } + + let fileName = "Execution.History." + bot.startMode + "." + runIndex + ".json"; + let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + fileName; + + let newRecord = [ + thisObject.newHistoryRecord.date.valueOf(), + thisObject.newHistoryRecord.buyAvgRate, + thisObject.newHistoryRecord.sellAvgRate, + + thisObject.newHistoryRecord.lastSellRate, + thisObject.newHistoryRecord.sellExecRate, + thisObject.newHistoryRecord.lastBuyRate, + thisObject.newHistoryRecord.buyExecRate, + + thisObject.newHistoryRecord.marketRate, + thisObject.newHistoryRecord.newPositions, + thisObject.newHistoryRecord.newTrades, + thisObject.newHistoryRecord.movedPositions, + thisObject.newHistoryRecord.profitsBaseAsset, + thisObject.newHistoryRecord.profitsQuotedAsset, + thisObject.newHistoryRecord.combinedProfitsA, + thisObject.newHistoryRecord.combinedProfitsB, + + thisObject.newHistoryRecord.messageRelevance, + thisObject.newHistoryRecord.messageTitle, + thisObject.newHistoryRecord.messageBody, + thisObject.extraData + ]; + + thisObject.executionHistory.push(newRecord); + + let fileContent = JSON.stringify(thisObject.executionHistory); + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExucutionHistory -> onFileCreated -> err = "+ err.stack); + callBack(err); + return; + } + + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { + logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> onFileCreated -> Content written = " + fileContent); + } + + /* Here we will write the file containing the max sequence number. */ + + fileContent = runIndex; + fileName = "Execution.History." + bot.startMode + "." + "Sequence" + ".json"; + filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + fileName; + + fileStorage.createTextFile(filePath, fileContent + '\n', onSequenceFileCreated); + + function onSequenceFileCreated(err) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> onFileCreated -> onSequenceFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExucutionHistory -> onFileCreated -> onSequenceFileCreated -> err = "+ err.stack); + callBack(err); + return; + } + + writeStatusReport(callBack); + return; + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExucutionHistory -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(callBack) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeStatusReport -> Entering function."); } + + statusReportModule.file = thisObject.statusReport; + statusReportModule.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeStatusReport -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] saveThemAll -> Error = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function truncDecimals(pFloatValue, pDecimals) { + + if (!pDecimals) pDecimals = 8; // Default value + + return parseFloat(parseFloat(pFloatValue).toFixed(pDecimals)); + + } + +}; diff --git a/TaskServer/DataDependencies.js b/TaskServer/DataDependencies.js new file mode 100644 index 000000000..084d4704e --- /dev/null +++ b/TaskServer/DataDependencies.js @@ -0,0 +1,104 @@ +exports.newDataDependencies = function newDataDependencies(BOT, logger, DATA_SET) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const MODULE_NAME = "Data Dependencies"; + + let bot = BOT + + let thisObject = { + nodeArray: undefined, + dataSets: new Map(), + initialize: initialize, + keys: [] + }; + + + return thisObject; + + function initialize(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + /* Basic Valdidations */ + if (bot.processNode.referenceParent.processDependencies !== undefined) { + if (bot.processNode.referenceParent.processDependencies.dataDependencies !== undefined) { + thisObject.nodeArray = bot.processNode.referenceParent.processDependencies.dataDependencies + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have data dependencies at all."); + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not data dependencies."); + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + } + + if (thisObject.nodeArray.length === 0) { + + // We allow old indicators not to declare their data dependencies. + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + /* + + For each dependency declared at the nodeArray, we will initialize a DataSet as part of this initialization process. + + */ + let alreadyCalledBack = false; + let addCount = 0; + + for (let i = 0; i < thisObject.nodeArray.length; i++) { + + let dataSetModule = DATA_SET.newDataSet(BOT, logger); + + dataSetModule.initialize(thisObject.nodeArray[i], onInitilized); + + function onInitilized(err) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> err = "+ err.stack); + + alreadyCalledBack = true; + callBackFunction(err); + return; + } + + addDataSet(); + } + + function addDataSet() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addDataSet -> Entering function."); } + + addCount++; + + let key; + + key = thisObject.nodeArray[i].dataMine + "-" + thisObject.nodeArray[i].bot + "-" + thisObject.nodeArray[i].product + "-" + thisObject.nodeArray[i].dataSet + "-" + thisObject.nodeArray[i].dataSetVersion; + + thisObject.keys.push(key); + thisObject.dataSets.set(key, dataSetModule); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addDataSet -> DataSet added to Map. -> key = " + key); } + + if (addCount === thisObject.nodeArray.length) { + if (alreadyCalledBack === false) { + alreadyCalledBack = true + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/DataSet.js b/TaskServer/DataSet.js new file mode 100644 index 000000000..4ecbbb6b3 --- /dev/null +++ b/TaskServer/DataSet.js @@ -0,0 +1,168 @@ +exports.newDataSet = function newDataSet(BOT, logger) { + + const MODULE_NAME = "Dataset"; + + let bot = BOT; + + let thisObject = { + initialize: initialize, + getTextFile: getTextFile, + createTextFile: createTextFile + }; + + let dataDependencyNode; + + /* Storage account to be used here. */ + + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + return thisObject; + + function initialize(pDataDependencyNode, callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + dataDependencyNode = pDataDependencyNode; + logger.fileName = MODULE_NAME + "." + dataDependencyNode.type + "." + dataDependencyNode.name + "." + dataDependencyNode.id; + + /* Some very basic validations that we have all the information needed. */ + if (dataDependencyNode.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Data Dependency without Reference Parent. Data Dependency = " + JSON.stringify(dataDependencyNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(dataDependencyNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(dataDependencyNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Data Mine. Bot = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Data Mine witn no codeName defined. Data Mine = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* Simplifying the access to basic info */ + dataDependencyNode.dataSet = dataDependencyNode.referenceParent.code.codeName + dataDependencyNode.product = dataDependencyNode.referenceParent.parentNode.code.codeName + dataDependencyNode.bot = dataDependencyNode.referenceParent.parentNode.parentNode.code.codeName + dataDependencyNode.dataMine = dataDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + dataDependencyNode.botVersion = { + "major": 1, + "minor": 0 + } + dataDependencyNode.dataSetVersion = "dataSet.V1" + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getTextFile(pFolderPath, pFileName, callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTextFile -> Entering function."); } + + let filePathRoot = dataDependencyNode.dataMine + "/" + dataDependencyNode.bot + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + pFolderPath; + filePath += '/' + pFileName + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTextFile -> onFileReceived -> Entering function."); } + + callBackFunction(err, text); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] 'getTextFile' -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function createTextFile(pFolderPath, pFileName, pFileContent, callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> pFolderPath = " + pFolderPath); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> pFileName = " + pFileName); } + + let ownerId = dataDependencyNode.dataMine + "-" + dataDependencyNode.bot + "-" + dataDependencyNode.botVersion.major + "-" + dataDependencyNode.botVersion.minor + "-" + dataDependencyNode.dataSetVersion; + let botId = bot.dataMine + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.dataSetVersion; + + if (ownerId !== botId) { + + let customErr = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: "Only bots owners of a Data Set can create text files on them." + }; + logger.write(MODULE_NAME, "[ERROR] save -> customErr = " + customErr.message); + callBackFunction(customErr); + return; + } + + let filePathRoot = dataDependencyNode.dataMine + "/" + dataDependencyNode.bot + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + pFolderPath + '/' + pFileName; + + fileStorage.createTextFile(filePath, pFileContent, onFileCreated); + + function onFileCreated(err) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> onFileCreated -> Entering function."); } + + callBackFunction(err); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] 'createTextFile' -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +}; diff --git a/TaskServer/DebugLog.js b/TaskServer/DebugLog.js new file mode 100644 index 000000000..ba2c83788 --- /dev/null +++ b/TaskServer/DebugLog.js @@ -0,0 +1,172 @@ +exports.newDebugLog = function newDebugLog() { + + const ROOT_DIR = './'; + const MODULE_NAME = "DebugLog"; + + let executionDatetime = "D." + global.EXECUTION_DATETIME.getUTCFullYear() + + "." + pad(global.EXECUTION_DATETIME.getUTCMonth() + 1, 2) + + "." + pad(global.EXECUTION_DATETIME.getUTCDate(), 2) + + ".T." + pad(global.EXECUTION_DATETIME.getUTCHours(), 2) + + "." + pad(global.EXECUTION_DATETIME.getUTCMinutes(), 2) + + "." + pad(global.EXECUTION_DATETIME.getUTCSeconds(), 2); + + let messageId = 0; + + let thisObject = { + bot: undefined, + write: write, + newInternalLoop: newInternalLoop, + persist: persist, // This method is executed at the end of each Main Loop. + initialize: initialize + }; + + let accumulatedLog = "["; + let disableLogging; + let internalLoopCounter = -1; + + return thisObject; + + function initialize(pdisableLogging) { + + try { + + disableLogging = pdisableLogging + + } catch (err) { + console.log("[ERROR] Debug Log -> initialize -> err = "+ err.stack); + } + } + + function newInternalLoop(pBot, pProcess, date) { + + if (date === undefined) { date = thisObject.bot.processDatetime } + + console.log(new Date().toISOString() + " " + strPad(pBot, 20) + " " + strPad(pProcess, 30) + " " + strPad(thisObject.bot.exchange, 20) + " " + strPad(thisObject.bot.market.baseAsset + '/' + thisObject.bot.market.quotedAsset, 10) + + " Entered into Internal Loop # " + strPad(internalLoopCounter + 1, 8) + " " + strPad(date.toISOString(), 30)) + + persist(); + + function strPad(str, max) { + str = str.toString(); + return str.length < max ? strPad(" " + str, max) : str; + } + } + + function persist() { + + /* Here we actually write the content of the in-memory log to a blob */ + + try { + + internalLoopCounter++; + + let contentToPersist = accumulatedLog; + accumulatedLog = "["; + + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(); + + let filePath = thisObject.bot.filePathRoot + "/Logs/" + thisObject.bot.process + "/" + + if (thisObject.bot.SESSION !== undefined) { + filePath = filePath + thisObject.bot.SESSION.folderName + "/" + executionDatetime; + } else { + filePath = filePath + executionDatetime; + } + + if (thisObject.bot.debug.year !== undefined) { + + filePath = filePath + "/" + thisObject.bot.debug.year + "/" + thisObject.bot.debug.month; + } + + let fileName; + if (internalLoopCounter >= 0) { + fileName = "Loop." + pad(thisObject.bot.loopCounter, 8) + "." + pad(internalLoopCounter, 4) + ".json"; + } else { + fileName = "Loop." + pad(thisObject.bot.loopCounter, 8) + ".json"; + } + + writeLog(); + + /* This is the implementation of the mechanism to auto-mantain logs. */ + thisObject.bot.LOGS_TO_DELETE_QUEUE.push(filePath + '/' + fileName) + if (thisObject.bot.LOGS_TO_DELETE_QUEUE.length > thisObject.bot.DELETE_QUEUE_SIZE) { + let fileToDelete = thisObject.bot.LOGS_TO_DELETE_QUEUE[0] + thisObject.bot.LOGS_TO_DELETE_QUEUE.splice(0, 1) + /* Will delete this file only if it does not contains ERROR inside. */ + let fileContent = fileStorage.getTextFile(fileToDelete, onGetFile, true) + function onGetFile(err, fileContent) { + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + if (fileContent.indexOf("ERROR") < 0) { + /* Logs will only be deleted when they contain no ERROR in them. */ + fileStorage.deleteTextFile(fileToDelete); + } + } else { + fileStorage.deleteTextFile(fileToDelete); + } + } + } + + function writeLog() { + + fileStorage.createTextFile(filePath + '/' + fileName, contentToPersist + '\r\n' + "]", onFileCreated); + function onFileCreated(err) { + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + console.log("[ERROR] DebugLog -> persist -> onInizialized -> onFileCreated -> err = "+ err.message); + setTimeout(writeLog, 10000); // Lets retry until we make it. + return; + } + contentToPersist = ""; + } + } + } catch (err) { + console.log("[ERROR] DebugLog -> persist -> err = "+ err.stack); + console.log("[ERROR] DebugLog -> persist -> onInizialized -> contentToPersist = " + contentToPersist); + } + } + + + function write(pModule, pMessage) { + + try { + + if (disableLogging === true) { return; } + + if (thisObject.bot === undefined) { return; } + + if (pModule === "") { // For debugging purposes + console.log(pMessage) + } + + let newDate = new Date(); + newDate = newDate.toISOString(); + + messageId++; + + /* When writting one file for all modules we use this. */ + + let message = "['" + newDate + "'," + messageId + ",'" + pModule + "','" + pMessage + "']" + let logLine = '\r\n' + message; + + if (process.env.CONSOLE_LOG === "true" || message.indexOf("ERROR") > 0) { + let key = '' + if (thisObject.bot) { + key = thisObject.bot.dataMine + '-' + thisObject.bot.codeName + '-' + thisObject.bot.process + } + console.log('*********** ' + message + ' @ ' + key) + } + + accumulatedLog = accumulatedLog + logLine; + + } catch (err) { + console.log("[ERROR] DebugLog -> write -> err = "+ err.stack); + } + } + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad("0" + str, max) : str; + } + +}; + diff --git a/TaskServer/ExchangeAPI.js b/TaskServer/ExchangeAPI.js new file mode 100644 index 000000000..4677a2c45 --- /dev/null +++ b/TaskServer/ExchangeAPI.js @@ -0,0 +1,160 @@ +exports.newExchangeAPI = function newExchangeAPI(logger, BOT) { + + let MODULE_NAME = "Exchange API"; + let LOG_INFO = true; + let apiClient; + let bot = BOT + + let thisObject = { + initialize: initialize, + getTicker: getTicker, + getOpenOrders: getOpenOrders, + getOrder: getOrder, + createOrder: createOrder, + getMaxDecimalPositions: getMaxDecimalPositions + }; + + let exchangeId + let options = {} + let exchange + + const ccxt = require('ccxt') + + return thisObject; + + function initialize(callBackFunction) { + try { + exchangeId = bot.exchange.toLowerCase() + + let key = process.env.KEY + let secret = process.env.SECRET + + if (key === "undefined") { key = undefined } + if (secret === "undefined") { secret = undefined } + + const exchangeClass = ccxt[exchangeId] + const exchangeConstructorParams = { + 'apiKey': key, + 'secret': secret, + 'timeout': 30000, + 'enableRateLimit': true, + verbose: true, + options: options + } + + exchange = new exchangeClass(exchangeConstructorParams) + + callBackFunction(global.DEFAULT_OK_RESPONSE) + } catch (err) { + logError("initialize -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getMaxDecimalPositions() { + return 8 + } + + async function getTicker(market, callBackFunction) { + try { + logInfo("getTicker -> Entering function."); + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + let ticker + + if (exchange.has['fetchTicker']) { + ticker = await (exchange.fetchTicker(symbol)) + callBackFunction(global.DEFAULT_OK_RESPONSE, ticker) + } else { + logError("getTicker -> Exchange does not support fetchTicker command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + } catch (err) { + logError("getTicker -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + async function getOpenOrders(market, callBackFunction) { + try { + logInfo("getOpenOrders -> Entering function. market = " + JSON.stringify(market)); + + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + let openOrders + + if (exchange.has['fetchOpenOrders']) { + openOrders = await(exchange.fetchOpenOrders (symbol)) + callBackFunction(global.DEFAULT_OK_RESPONSE, openOrders) + } else { + logError("getOpenOrders -> Exchange does not support fetchOpenOrders command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + } catch (err) { + logError("getOpenOrders -> Error = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + async function getOrder(orderId, market, callBackFunction) { + try { + logInfo("getOrder -> Entering function. orderId = " + orderId); + + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + let order + + if (exchange.has['fetchOrder']) { + order = await (exchange.fetchOrder(orderId, symbol)) + callBackFunction(global.DEFAULT_OK_RESPONSE, order) + } else { + logError("getOrder -> Exchange does not support fetchOrder command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } catch (err) { + logError("getOrder -> Error = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + async function createOrder(market, side, price, cost, amount, callBackFunction) { + try { + logInfo("createOrder -> Entering function."); + + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + + logInfo("createOrder -> market = " + market.assetA + "_" + market.assetB); + logInfo("createOrder -> side = " + side); + logInfo("createOrder -> cost = " + cost); + logInfo("createOrder -> amount = " + amount); + logInfo("createOrder -> price = " + price); + + if (side !== "buy" && side !== "sell") { + logError("createOrder -> side must be either 'buy' or 'sell'."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + let type = 'market' + let order + + if (exchange.has['createOrder']) { + order = await(exchange.createOrder(symbol, type, side, amount)) + callBackFunction(global.DEFAULT_OK_RESPONSE, order) + } else { + logError("createOrder -> Exchange does not support createOrder command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + } catch (err) { + logError("createOrder -> err = " + JSON.stringify(err.stack) || err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function logInfo(message) { + if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] ' + message) } + } + + function logError(message) { + logger.write(MODULE_NAME, '[ERROR] ' + message) + } +}; diff --git a/TaskServer/FileStorage.js b/TaskServer/FileStorage.js new file mode 100644 index 000000000..8bca500eb --- /dev/null +++ b/TaskServer/FileStorage.js @@ -0,0 +1,289 @@ + +const path = require('path') + +exports.newFileStorage = function newFileStorage(logger) { + + const MODULE_NAME = 'FileStorage' + const MAX_RETRY = 30 + const RETRY_TIME_IN_MILISECONDS = 250 + let currentRetryGetTextFile = 0 + let currentRetryWriteTextFile = 0 + + if (logger === undefined) { // Dummy logger + logger = {} + function write() { + + } + logger.write = write + } + + let thisObject = { + getTextFile: getTextFile, + createTextFile: createTextFile, + deleteTextFile: deleteTextFile + } + + return thisObject + + function getTextFile(filePath, callBackFunction, noRetry) { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> getTextFile -> Entering Function.') + + let fileDoesNotExist = false + + /* Choose path for either bots or data */ + let fileLocation + if (filePath.indexOf("/bots/") > 0) { + fileLocation = process.env.BOTS_PATH + '/' + filePath + } else { + fileLocation = process.env.STORAGE_PATH + '/' + filePath + } + + try { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> getTextFile -> fileLocation: ' + fileLocation) + + /* Here we actually write the file. */ + const fs = require('fs') + fs.readFile(fileLocation, onFileRead) + + function onFileRead(err, text) { + + if (err) { + + if (err.code === 'ENOENT') { // since files are deleted before being replaced, it can happen that it does not exist and after a retry it does. + if (noRetry === true) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Not Retrying. ') + let customResponse = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: 'File does not exist.' + } + callBackFunction(customResponse) + return + } else { + fileDoesNotExist = true + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Retrying. ') + setTimeout(retry, RETRY_TIME_IN_MILISECONDS) + return + } + } + + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> err = ' + err.stack) + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> filePath = ' + filePath) + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> err = ' + err.stack) + setTimeout(retry, RETRY_TIME_IN_MILISECONDS) + return + } + + if (text.toString() === "") { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Read and Empty File -> filePath = ' + filePath) + setTimeout(retry, RETRY_TIME_IN_MILISECONDS) + return + } + + callBackFunction(global.DEFAULT_OK_RESPONSE, text.toString()) + } + + } catch (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> Error writing file -> err = ' + err.stack) + retry() + } + + function retry() { + if (currentRetryGetTextFile < MAX_RETRY) { + currentRetryGetTextFile++ + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Will try to read the file again -> Retry #: ' + currentRetryGetTextFile) + getTextFile(filePath, callBackFunction) + } else { + currentRetryGetTextFile = 0 + + if (fileDoesNotExist === true) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Max retries reached reading a file. File Not Found.') + let customResponse = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: 'File does not exist.' + } + callBackFunction(customResponse) + return + } else { + logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> Max retries reached reading a file. Giving up.') + logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> file = ' + fileLocation) + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + return + } + } + } + } + + function createTextFile(filePath, fileContent, callBackFunction) { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> createTextFile -> Entering Function.') + + /* Choose path for either logs or data */ + let fileLocation + if (filePath.indexOf("/Logs/") > 0) { + fileLocation = process.env.LOG_PATH + '/' + filePath + } else { + fileLocation = process.env.STORAGE_PATH + '/' + filePath + } + + try { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> createTextFile -> fileLocation: ' + fileLocation) + + /* If necesary a folder or folders are created before writing the file to disk. */ + let directoryPath = fileLocation.substring(0, fileLocation.lastIndexOf('/') + 1); + mkDirByPathSync(directoryPath) + + /* + Here we write the file with a temporary name so as to avoid dirty read from other processes. + Then we delete the original file, if exists, and finally we rename the temporary into the original name. + */ + const fs = require('fs') + fs.writeFile(fileLocation + '.tmp', fileContent, onFileWritenn) + + function onFileWritenn(err) { + if (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> err = ' + err.stack) + setTimeout(retry, RETRY_TIME_IN_MILISECONDS) + } else { + + const fs = require('fs') + fs.unlink(fileLocation, onUnlinked) + + function onUnlinked(err) { + let code = '' + if (err) { + code = err.code + } + if (code !== '' && code !== 'ENOENT') { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> err = ' + err.stack) + setTimeout(retry, RETRY_TIME_IN_MILISECONDS) + } else { + + const fs = require('fs') + fs.rename(fileLocation + '.tmp', fileLocation, onRenamed) + + function onRenamed(err) { + if (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> file = ' + fileLocation + '.tmp') + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> err = ' + err.stack) + setTimeout(retry, RETRY_TIME_IN_MILISECONDS) + } else { + + callBackFunction(global.DEFAULT_OK_RESPONSE) + + } + } + } + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> Error writing file -> err = ' + err.stack) + retry() + } + + function retry() { + if (currentRetryWriteTextFile < MAX_RETRY) { + currentRetryWriteTextFile++ + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> retry -> Will try to write the file again -> Retry #: ' + currentRetryWriteTextFile) + createTextFile(filePath, fileContent, callBackFunction) + } else { + currentRetryWriteTextFile = 0 + logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> Max retries reached writting a file. Giving up.') + logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> file = ' + fileLocation) + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } + } + + function deleteTextFile(filePath, callBackFunction) { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> Entering Function.') + + /* Choose path for either logs or data */ + let fileLocation + if (filePath.indexOf("/Logs/") > 0) { + fileLocation = process.env.LOG_PATH + '/' + filePath + } else { + fileLocation = process.env.STORAGE_PATH + '/' + filePath + } + + try { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> fileLocation: ' + fileLocation) + + const fs = require('fs') + fs.unlink(fileLocation, onUnlinked) + + function onUnlinked(err) { + if (err) { + if (callBackFunction !== undefined) { + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } else { + if (callBackFunction !== undefined) { + callBackFunction(global.DEFAULT_OK_RESPONSE) + } + } + } + } catch (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> Error writing file -> err = ' + err.stack) + if (callBackFunction !== undefined) { + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } + + function retry() { + if (currentRetryWriteTextFile < MAX_RETRY) { + currentRetryWriteTextFile++ + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> retry -> Will try to write the file again -> Retry #: ' + currentRetryWriteTextFile) + createTextFile(filePath, fileContent, callBackFunction) + } else { + currentRetryWriteTextFile = 0 + logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> Max retries reached writting a file. Giving up.') + logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> file = ' + fileLocation) + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } + } + + /* Function to create folders of missing folders at any path. */ + + function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) { + const sep = '/'; + const initDir = path.isAbsolute(targetDir) ? sep : ''; + const baseDir = isRelativeToScript ? __dirname : '.'; + + return targetDir.split(sep).reduce((parentDir, childDir) => { + const curDir = path.resolve(baseDir, parentDir, childDir); + try { + const fs = require('fs') + fs.mkdirSync(curDir); + } catch (err) { + if (err.code === 'EEXIST') { // curDir already exists! + return curDir; + } + + // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows. + if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure. + throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`); + } + + const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1; + if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) { + throw err; // Throw if it's just the last created dir. + } + } + + return curDir; + }, initDir); + } +} diff --git a/TaskServer/IndicatorBot.js b/TaskServer/IndicatorBot.js new file mode 100644 index 000000000..004ed077f --- /dev/null +++ b/TaskServer/IndicatorBot.js @@ -0,0 +1,168 @@ +exports.newIndicatorBot = function newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "Indicator Bot"; + + let thisObject = { + initialize: initialize, + finalize: finalize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + const COMMONS = require('./Commons.js'); + let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE); + + return thisObject; + + function finalize() { + thisObject = undefined + utilities = undefined + fileStorage = undefined + commons = undefined + } + + function initialize(callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(dataFiles, timeFrame, timeFrameLabel, currentDay, interExecutionMemory, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let products = {} + let mainDependency = {} + let processingDailyFiles + + if (currentDay !== undefined) { + processingDailyFiles = true + } else { + processingDailyFiles = false + } + + /* The first phase here is about checking that we have everything we need at the definition level. */ + let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies + if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) { return } + + let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets + if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return } + + /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */ + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timeFrame) + + /* During the third phase, we need to generate the data of the different products this process produces an output */ + for (let i = 0; i < outputDatasets.length; i++) { + + let outputDatasetNode = outputDatasets[i] + let jsonData // Just build data as Json objects + let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure + let singularVariableName // Name of the variable for this product + let recordDefinition // Record as defined by the user at the UI + let fileContent // Here we store the contents of the new data built just before writing it to a file. + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. + + /* + For each outputDatasetNode in our process output, we will build the information based on our input products. + */ + + recordDefinition = outputDatasetNode.referenceParent.parentNode.record + singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName + + /* Check Irregular Periods */ + + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.javascriptCode !== undefined) { + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.javascriptCode.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + } + + /* Build the data */ + jsonData = commons.dataBuildingProcedure( + products, + mainDependency, + recordDefinition, + outputDatasetNode.referenceParent.parentNode.dataBuilding, + singularVariableName, + outputDatasetNode.referenceParent.parentNode.code.codeName, + timeFrame, + timeFrameLabel, + resultsWithIrregularPeriods, + interExecutionMemory, + processingDailyFiles, + currentDay + ) + + /* Add the calculated properties */ + if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { + outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timeFrame) + } else { + outputData = jsonData + } + products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData + } + + /*At the fourth and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ + let totalFilesWritten = 0 + for (let i = 0; i < outputDatasets.length; i++) { + let outputDatasetNode = outputDatasets[i] + let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. + let contextSummary = {} + + /* Check Irregular Periods */ + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.javascriptCode.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + + /* Simplifying the access to basic info */ + + contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName + contextSummary.product = outputDatasetNode.referenceParent.parentNode.code.codeName + contextSummary.bot = outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName + contextSummary.dataMine = outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + contextSummary.botVersion = { + "major": 1, + "minor": 0 + } + contextSummary.dataSetVersion = "dataSet.V1" + + let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction) + commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timeFrameLabel, currentDay, callBackFunction) + } + + + function anotherFileWritten() { + totalFilesWritten++ + if (totalFilesWritten === outputDatasets.length) { + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/IndicatorBotProcessMainLoop.js b/TaskServer/IndicatorBotProcessMainLoop.js new file mode 100644 index 000000000..aa2cd6ac2 --- /dev/null +++ b/TaskServer/IndicatorBotProcessMainLoop.js @@ -0,0 +1,1176 @@ +exports.newIndicatorBotProcessMainLoop = function newIndicatorBotProcessMainLoop(bot, parentLogger) { + + const ROOT_DIR = './'; + + const MODULE_NAME = "Indicator Process Main Loop"; + const FULL_LOG = true; + + let USER_BOT_MODULE; + let COMMONS_MODULE; + + const MULTI_PERIOD_MARKET = require(ROOT_DIR + 'MultiPeriodMarket'); + const MULTI_PERIOD_DAILY = require(ROOT_DIR + 'MultiPeriodDaily'); + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(parentLogger); + + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble. + + let nextLoopTimeoutHandle; + let checkLoopHealthHandle; + + let thisObject = { + initialize: initialize, + run: run + }; + + let processConfig; + + return thisObject; + + function initialize(pProcessConfig, callBackFunction) { + + try { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + processConfig = pProcessConfig; + + if (bot.definedByUI === true) { + /* The code of the bot is defined at the UI. No need to load a file with the code. */ + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + + /* This bot is not ready for taking its code from the UI, then we need to load it from its repo. */ + + let filePath = bot.dataMine + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName + filePath += "/User.Bot.js" + + fileStorage.getTextFile(filePath, onBotDownloaded); + + function onBotDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message); + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + USER_BOT_MODULE = {} + USER_BOT_MODULE.newUserBot = eval(text); // TODO This needs to be changed function + + filePath = bot.dataMine + "/" + "bots" + "/" + bot.repo; + filePath += "/Commons.js" + + fileStorage.getTextFile(filePath, onCommonsDownloaded); + + function onCommonsDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.code || err.message); + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + COMMONS_MODULE = {} + COMMONS_MODULE.newCommons = eval(text); // TODO This needs to be changed function + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function run(callBackFunction) { + + try { + + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] run -> Entering function."); } + + bot.enableCheckLoopHealth = true; + + let fixedTimeLoopIntervalHandle; + + if (bot.runAtFixedInterval === true) { + + fixedTimeLoopIntervalHandle = setInterval(loop, bot.fixedInterval); + + } else { + + loop(); + + } + + function loop() { + + try { + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } + + /* For each loop we want to create a new log file. */ + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = bot; + logger.initialize(); + + bot.loopCounter++; + bot.loopStartTime = new Date().valueOf(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Entering function."); } + + /* Loop Heartbeat sent to the UI */ + bot.processHeartBeat = processHeartBeat + processHeartBeat() + + /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */ + + const UTILITIES = require(ROOT_DIR + 'CloudUtilities'); + const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); + const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); + const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); + const DATA_SET = require(ROOT_DIR + 'DataSet'); + const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput'); + + /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ + + bot.processDatetime = new Date(); // This will be considered the process date and time, so as to have it consistenly all over the execution. + + /* High level log entry */ + + console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + pad(bot.exchange, 20) + " " + pad(bot.market.baseAsset + '/' + bot.market.quotedAsset, 10) + + " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8)); + + /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + + let processOutput + let processExecutionEvents + let userBot; + let processFramework; + let statusDependencies; + let dataDependencies; + + let nextWaitTime; + + initializeProcessExecutionEvents(); + + function initializeProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } + + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) + + processExecutionEvents.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well."); + startProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.start(onStarted); + + function onStarted(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well."); + initializeStatusDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeStatusDependencies() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> Entering function."); } + + statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES); + + statusDependencies.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well."); + initializeDataDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeStatusDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeDataDependencies() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> Entering function."); } + + dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET); + + dataDependencies.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); } + + /* If the process is configured to run inside a framework, we continue there, otherwise we run the bot directly. */ + if (processConfig.framework === undefined) { + initializeUserBot(); + return; + } + + switch (processConfig.framework.name) { + case 'Multi-Period-Market': { + processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, UTILITIES, FILE_STORAGE); + intitializeProcessFramework(); + break; + } + case 'Multi-Period-Daily': { + processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, UTILITIES, FILE_STORAGE); + intitializeProcessFramework(); + break; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework not Supported."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework Name = " + processConfig.framework.name); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeDataDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeUserBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> Entering function."); } + + usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); + + usertBot.initialize(statusDependencies, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Execution finished well."); + startUserBot(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeUserBot -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized > Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startUserBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> Entering function."); } + + usertBot.start(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Execution finished well."); + raiseEventsProcessOutput() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Dependency does not exist.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Dependency does not exist. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Dependency not ready.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished -> Dependency not ready. Will Retry Later."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function intitializeProcessFramework() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); } + + processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Execution finished well."); + startProcessFramework(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> intitializeProcessFramework -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized > Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessFramework() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> Entering function."); } + + processFramework.start(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Entering function."); } + processFramework.finalize() + processFramework = undefined + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); + raiseEventsProcessOutput() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Dependency does not exist.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency does not exist. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Dependency not ready.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency not ready. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function raiseEventsProcessOutput() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); } + + processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger) + + processOutput.raiseEvents(statusDependencies, onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well."); + finishProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> raiseEventsProcessOutput -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function finishProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.finish(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function loopControl(nextWaitTime) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> nextWaitTime = " + nextWaitTime); } + + /* We show we reached the end of the loop. */ + + processHeartBeat() + + /* Here we check if we must stop the loop gracefully. */ + + shallWeStop(onStop, onContinue); + + function onStop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Entering function."); } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Stopping the Loop Gracefully. See you next time!"); } + + if(global.WRITE_LOGS_TO_FILES === 'true'){ + logger.persist(); + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + function onContinue() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onContinue -> Entering function."); } + + /* Indicator bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */ + + switch (nextWaitTime) { + case 'Normal': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.normalWaitTime / 1000) + " seconds."); } + if (processConfig.deadWaitTime > 0) { + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.deadWaitTime, bot.loopCounter); + } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.normalWaitTime); + if(global.WRITE_LOGS_TO_FILES === 'true'){ + logger.persist(); + } + } + break; + case 'Retry': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.retryWaitTime / 1000) + " seconds."); } + if (processConfig.deadWaitTime > 0) { + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.deadWaitTime, bot.loopCounter); + } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.retryWaitTime); + logger.persist(); + } + break; + case 'Sleep': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.sleepWaitTime / 60000) + " minutes."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.sleepWaitTime); + logger.persist(); + } + break; + case 'Coma': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.comaWaitTime / 3600000) + " hours."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.comaWaitTime); + logger.persist(); + } + break; + } + } + } + + function checkLoopHealth(pLastLoop) { + + if (bot.enableCheckLoopHealth === false || global.STOP_TASK_GRACEFULLY === true) { + + logger.write(MODULE_NAME, "[WARN] run -> loop -> checkLoopHealth -> bot.enableCheckLoopHealth = " + bot.enableCheckLoopHealth); + + return; + } // This gets disabled anytime the Main Loop is shut down by any condition. + + if (bot.loopCounter <= pLastLoop + 1) { // This means that the next loop started but also stopped executing abruptally. + + let now = new Date().valueOf(); + + if (now - bot.loopStartTime > processConfig.deadWaitTime) { + + logger.write(MODULE_NAME, "[ERROR] run -> loop -> checkLoopHealth -> Dead loop found -> pLastLoop = " + pLastLoop); + console.log((new Date().toISOString() + " [ERROR] run -> loop -> checkLoopHealth -> " + pad(bot.codeName,20) + " " + pad(bot.process,30) + " Loop # " + pad(Number(bot.loopCounter),5) + " found dead. Resurrecting it now.")); + + logger.persist(); // We persist the logs of the failed execution. + clearTimeout(nextLoopTimeoutHandle); // We cancel the timeout in case the original loop was still running and schedulled to reexecute. + loop(); // We restart the loop so that the processing can continue. + + } else { + + console.log((new Date().toISOString() + " [WARN] run -> loop -> checkLoopHealth -> " + pad(bot.codeName,20) + " " + pad(bot.process,30) + " Loop # " + pad(Number(bot.loopCounter),5) + " found delayed but still alive. No action taken.")); + + } + } + } + + function shallWeStop(stopCallBack, continueCallBack) { + + try { + + /* IMPORTANT: This function is exactly the same on the 3 modules. */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> shallWeStop -> Entering function. "); } + + if (!global.STOP_TASK_GRACEFULLY) { + continueCallBack(); + } else { + stopCallBack(); + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = "+ err.stack); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function processHeartBeat(processingDate) { + let event = { + seconds: (new Date()).getSeconds(), + processingDate: processingDate + } + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.processKey, 'Heartbeat', event) + } + + function processStopped() { + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.processKey, 'Stopped') + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> err = "+ err.stack); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/MultiPeriod.js b/TaskServer/MultiPeriod.js new file mode 100644 index 000000000..6364f346a --- /dev/null +++ b/TaskServer/MultiPeriod.js @@ -0,0 +1,1083 @@ +exports.newMultiPeriod = function newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const MODULE_NAME = "Multi Period"; + const GMT_SECONDS = ':00.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + thisObject = { + initialize: initialize, + finalize: finalize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + + let statusDependencies; + let dataDependencies; + let datasets = []; + let dataFiles = new Map(); + let multiPeriodDataFiles = new Map(); + + let botInstance; + + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + let processConfig; + + return thisObject; + + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, exchangeAPI, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + dataDependencies = pDataDependencies; + processConfig = pProcessConfig; + + for (let i = 0; i < dataDependencies.nodeArray.length; i++) { + + let key; + let dataset; + let dependency = dataDependencies.nodeArray[i]; + + key = dependency.dataMine + "-" + + dependency.bot + "-" + + dependency.product + "-" + + dependency.dataSet + "-" + + dependency.dataSetVersion + + dataset = dataDependencies.dataSets.get(key); + + datasets.push(dataset); + + } + + let USER_BOT_MODULE = require("./TradingBot") + + botInstance = USER_BOT_MODULE.newTradingBot(bot, logger, UTILITIES, FILE_STORAGE); + botInstance.initialize(exchangeAPI, callBackFunction); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function finalize() { + datasets = undefined + dataFiles = undefined + multiPeriodDataFiles = undefined + statusDependencies = undefined + dataDependencies = undefined + botInstance.finalize() + botInstance = undefined + fileStorage = undefined + processConfig = undefined + thisObject = undefined + } + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let currentTimeFrame + let currentOutputPeriodName + + let market = bot.market; + let botNeverRan = true; + + /* Context Variables */ + + let contextVariables = { + lastFile: undefined, // Datetime of the last file files sucessfully produced by this process. + dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history. + dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process. + }; + + let previousDay; // Holds the date of the previous day relative to the processing date. + + let interExecutionMemoryArray = []; + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + let statusReport; + + /* We are going to use the start date as beging of market date. */ + contextVariables.dateBeginOfMarket = bot.VALUES_TO_USE.timeRange.initialDatetime + + /* + Here we get the status report from the bot who knows which is the end of the market. + */ + + statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point") + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusReport.file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.dateEndOfMarket = new Date(thisReport.lastFile.valueOf()); + + /* Validation that the data is not up-to-date. */ + + if (bot.multiPeriodProcessDatetime.valueOf() - ONE_DAY_IN_MILISECONDS > contextVariables.dateEndOfMarket.valueOf() && bot.SESSION.type !== "Backtesting Session") { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> " + "Head of the market is @ " + contextVariables.dateEndOfMarket); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> You need to UPDATE your datasets in order to run a " + bot.SESSION.type) } + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + + } + + /* Finally we get our own Status Report. */ + + statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusReport.file; + + if (thisReport.lastFile !== undefined) { + + if (bot.hasTheBotJustStarted === true && bot.resumeExecution === false) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Starting from the begining because bot has just started and resume execution was true."); } + startFromBegining(); + return; + } + + contextVariables.lastFile = new Date(thisReport.lastFile); + interExecutionMemoryArray = thisReport.interExecutionMemoryArray; + + processTimeFramesMarketFiles(); + return; + + } else { + + /* + In the case when there is no status report, we assume like the last processed file is the one on the date of Begining of Market. + */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Starting from the begining of the market because own status report not found or lastFile was undefined."); } + startFromBegining(); + return; + } + + function startFromBegining() { + + contextVariables.lastFile = new Date(contextVariables.dateBeginOfMarket.getUTCFullYear() + "-" + (contextVariables.dateBeginOfMarket.getUTCMonth() + 1) + "-" + contextVariables.dateBeginOfMarket.getUTCDate() + " " + "00:00" + GMT_SECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> startFromBegining -> contextVariables.lastFile = " + contextVariables.lastFile); } + + /* + The first time the process is running is the right time to create the data structure that is going to be shared across different executions. + This data structure has one object per each timeFrame. + */ + + interExecutionMemoryArray = []; + + for (let i = 0; i < global.dailyFilePeriods.length; i++) { + let emptyObject = {}; + interExecutionMemoryArray.push(emptyObject); + } + + processTimeFramesMarketFiles(); + + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function processTimeFramesMarketFiles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> Entering function."); } + + try { + + let n; + + periodsLoop(); + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible periods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + periodsLoopBody(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsLoopBody -> Entering function."); } + + const timeFrame = global.marketFilesPeriods[n][0]; + const timeFrameLabel = global.marketFilesPeriods[n][1]; + + let dependencyIndex = 0; + dataFiles = new Map(); + + if (bot.VALUES_TO_USE.timeFrame === timeFrameLabel) { + currentTimeFrame = global.marketFilesPeriods[n][0]; + currentOutputPeriodName = global.marketFilesPeriods[n][1]; + } + dependencyLoopBody(); + + function dependencyLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } + + let dependency = dataDependencies.nodeArray[dependencyIndex]; + let dataset = datasets[dependencyIndex]; + + getFile(); + + function getFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> Entering function."); } + + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + if (dependency.dataSet !== "Multi-Period-Market") { + + dependencyControlLoop(); + return + } + + let filePath = dependency.product + '/' + dependency.dataSet + "/" + timeFrameLabel; + + dataset.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> text = " + text); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = " + err.message); + callBackFunction(err); + return; + } + + let dataFile = JSON.parse(text); + dataFiles.set(dependency.id, dataFile); + + dependencyControlLoop(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoop -> dependencyLoopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function dependencyControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> dependencyControlLoop -> Entering function."); } + + dependencyIndex++; + + if (dependencyIndex < dataDependencies.nodeArray.length) { + + dependencyLoopBody(); + + } else { + + let mapKey = global.marketFilesPeriods[n][1]; + multiPeriodDataFiles.set(mapKey, dataFiles) + + periodsControlLoop(); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> dependencyControlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsControlLoop -> Entering function."); } + + n++; + + if (n < global.marketFilesPeriods.length) { + + periodsLoopBody(); + + } else { + + processTimeFramesDailyFiles(); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsControlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function processTimeFramesDailyFiles() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> Entering function."); } + + try { + + let n = 0; + + if (currentTimeFrame) { + callTheBot() + return + } + + bot.multiPeriodProcessDatetime = new Date(contextVariables.lastFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop. + + advanceTime(); + + function advanceTime() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> advanceTime -> Entering function."); } + + bot.multiPeriodProcessDatetime = new Date(bot.multiPeriodProcessDatetime.valueOf() + ONE_DAY_IN_MILISECONDS); + previousDay = new Date(bot.multiPeriodProcessDatetime.valueOf() - ONE_DAY_IN_MILISECONDS); + + if (global.WRITE_LOGS_TO_FILES === 'true') { + logger.newInternalLoop(bot.codeName, bot.process); + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> advanceTime -> bot.multiPeriodProcessDatetime = " + bot.multiPeriodProcessDatetime); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> advanceTime -> previousDay = " + previousDay); } + + /* Validation that we are not going past the user defined end date. */ + + if (bot.multiPeriodProcessDatetime.valueOf() >= bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { + + const logText = "User Defined End Datetime reached @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> advanceTime -> " + logText); } + + bot.STOP_SESSION = true + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + /* Validation that we are not going past the head of the market. */ + + if (bot.multiPeriodProcessDatetime.valueOf() > contextVariables.dateEndOfMarket.valueOf() + ONE_DAY_IN_MILISECONDS - 1) { + + const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> advanceTime -> " + logText); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + checkStopTaskGracefully(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> advanceTime -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function checkStopTaskGracefully() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> checkStopTaskGracefully -> Entering function."); } + + /* Validation that we dont need to stop. */ + + if (global.STOP_TASK_GRACEFULLY === true) { + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + checkStopProcessing(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> checkStopTaskGracefully -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function checkStopProcessing() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> checkStopProcessing -> Entering function."); } + + /* Validation that we dont need to stop. */ + + if (bot.STOP_SESSION === true) { + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + periodsLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> checkStopProcessing -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoop -> Entering function."); } + + /* Telling the world we are alive and doing well */ + let processingDate = bot.multiPeriodProcessDatetime.getUTCFullYear() + '-' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCMonth() + 1, 2) + '-' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCDate(), 2); + bot.processHeartBeat(processingDate) + + /* + + We will iterate through all posible periods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + periodsLoopBody(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> Entering function."); } + + const timeFrame = global.dailyFilePeriods[n][0]; + const timeFrameLabel = global.dailyFilePeriods[n][1]; + + if (processConfig.framework.validPeriods !== undefined) { + let validPeriod = false; + for (let i = 0; i < processConfig.framework.validPeriods.length; i++) { + let period = processConfig.framework.validPeriods[i]; + if (period === timeFrameLabel) { validPeriod = true } + } + if (validPeriod === false) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> timeFrameLabel = " + timeFrameLabel); } + periodsControlLoop(); + return; + } + } + + if (bot.VALUES_TO_USE.timeFrame === timeFrameLabel) { + currentTimeFrame = global.dailyFilePeriods[n][0]; + currentOutputPeriodName = global.dailyFilePeriods[n][1]; + } + + let dependencyIndex = 0; + dataFiles = new Map(); + dependencyLoopBody(); + + function dependencyLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } + + let dependency = dataDependencies.nodeArray[dependencyIndex]; + let dataset = datasets[dependencyIndex]; + + let previousFile; + let currentFile; + + if (bot.multiPeriodProcessDatetime.valueOf() > contextVariables.dateBeginOfMarket.valueOf()) { + getPreviousFile(); + } else { + previousFile = []; + getCurrentFile() + } + + function getPreviousFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> Entering function."); } + + if (dependency.dataSet === "Multi-Period-Market") { + + dependencyControlLoop(); + return + } + + let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2); + let filePath + + if (dependency.dataSet === "Multi-Period-Daily") { + filePath = dependency.product + '/' + dependency.dataSet + "/" + timeFrameLabel + "/" + dateForPath; + } else { + filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; + } + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + dataset.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> text = " + text); } + + if ((err.message === "File does not exist." && botNeverRan === true) || err.code === 'The specified key does not exist.') { + + /* + Sometimes one of the dependencies of an indicator for some reasons are not calculated from the begining of the market. + When that happens we can not find those files. What we do in this situation is to move the time fordward until we can find + all the dependencies and the first run of the bot is successful. + + After that, we will not accept more missing files on any of the dependencies, and if any is missing we will abort the processing. + */ + logger.write(MODULE_NAME, "[WARN] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> Skipping day because file " + filePath + "/" + fileName + " was not found."); + + advanceTime(); + return; + } + + if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = " + err.stack); + callBackFunction(err); + return; + } + + previousFile = JSON.parse(text); + + getCurrentFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getCurrentFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> Entering function."); } + + if (dependency.dataSet === "Multi-Period-Market") { + + dependencyControlLoop(); + return + } + + let dateForPath = bot.multiPeriodProcessDatetime.getUTCFullYear() + '/' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCDate(), 2); + let filePath + if (dependency.dataSet === "Multi-Period-Daily") { + filePath = dependency.product + '/' + dependency.dataSet + "/" + timeFrameLabel + "/" + dateForPath; + } else { + filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; + } + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + dataset.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> text = " + text); } + + if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Fail Because -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> fileName = " + fileName); + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> You are trying to use data that does not exist. "); + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Check that your Datasets building processes are running well. "); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Not Ok -> err = " + err.code); + callBackFunction(err); + return; + } + + currentFile = JSON.parse(text); + + let dataFile = previousFile.concat(currentFile); + + dataFiles.set(dependency.id, dataFile); + + dependencyControlLoop(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoop -> dependencyLoopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function dependencyControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> dependencyControlLoop -> Entering function."); } + + dependencyIndex++; + + if (dependencyIndex < dataDependencies.nodeArray.length) { + + dependencyLoopBody(); + + } else { + + let mapKey = global.dailyFilePeriods[n][1]; + multiPeriodDataFiles.set(mapKey, dataFiles) + + periodsControlLoop(); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> dependencyControlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> periodsControlLoop -> Entering function."); } + + n++; + + if (n < global.dailyFilePeriods.length) { + + periodsLoopBody(); + + } else { + + n = 0; + + if (currentTimeFrame !== undefined) { + callTheBot(); + } else { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsControlLoop -> Time Frame not Recognized. Can not continue." ); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsControlLoop -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function callTheBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> callTheBot -> Entering function."); } + + botInstance.start( + multiPeriodDataFiles, + currentTimeFrame, + currentOutputPeriodName, + bot.multiPeriodProcessDatetime, + interExecutionMemoryArray[n], + onBotFinished); + + function onBotFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + callBackFunction(err); + return; + } + + botNeverRan = false; + + if (currentTimeFrame > global.dailyFilePeriods[0][0]) { + writeMarketStatusReport(onMarketStatusReport) + + } else { + writeDataRanges(currentOutputPeriodName, onWritten); + } + + function onWritten(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onWritten -> err = " + err.stack); + callBackFunction(err); + return; + } + + writeDailyStatusReport(bot.multiPeriodProcessDatetime, onDailyStatusReport); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onWritten -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + + function onDailyStatusReport() { + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onDailyStatusReport -> Entering function."); } + + /* The next run we need the process to continue at the date it finished. */ + processConfig.framework.startDate.resumeExecution = true; + + advanceTime(); + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onDailyStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function onMarketStatusReport() { + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onMarketStatusReport -> Entering function."); } + + /* This is where we check if we need reached the user defined end datetime. */ + + const now = new Date() + if (now.valueOf() >= bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { + + const logText = "User Defined End Datetime reached @ " + now.getUTCFullYear() + "/" + (now.getUTCMonth() + 1) + "/" + now.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onMarketStatusReport -> " + logText); } + + bot.STOP_SESSION = true + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> onMarketStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> callTheBot -> onBotFinished -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> callTheBot -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeDataRanges(currentOutputPeriodName, callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + let outputDatasetIndex = -1; + controlLoop(); + + function productLoopBody() { + let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; + writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodProcessDatetime, productCodeName, currentOutputPeriodName, controlLoop); + } + + function controlLoop() { + + outputDatasetIndex++; + + if (outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length) { + productLoopBody(); + } else { + callBack(global.DEFAULT_OK_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + } + + function writeDataRange(pBegin, pEnd, productCodeName, currentOutputPeriodName, callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); } + + let dataRange = { + begin: pBegin.valueOf(), + end: pEnd.valueOf() + ONE_DAY_IN_MILISECONDS + }; + + let fileContent = JSON.stringify(dataRange); + + let fileName = '/Data.Range.' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName; + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack); + callBack(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent); + } + + let key = bot.dataMine + "-" + bot.codeName + "-" + productCodeName + "-" + currentOutputPeriodName + let event = { + dateRange: dataRange + } + + global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Data Range Updated', event) + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeDailyStatusReport(lastFileDate, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyStatusReport -> lastFileDate = " + lastFileDate); } + + try { + + let reportKey = bot.dataMine + "-" + bot.codeName + "-" + "Multi-Period" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.currentDaytime; + thisReport.file.lastFile = lastFileDate; + thisReport.file.interExecutionMemoryArray = interExecutionMemoryArray; + thisReport.save(callBack); + + bot.hasTheBotJustStarted = false; + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeMarketStatusReport(callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketStatusReport -> Entering function."); } + + try { + + let reportKey = bot.dataMine + "-" + bot.codeName + "-" + "Multi-Period" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketStatusReport -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } +}; diff --git a/TaskServer/MultiPeriodDaily.js b/TaskServer/MultiPeriodDaily.js new file mode 100644 index 000000000..71ad6181a --- /dev/null +++ b/TaskServer/MultiPeriodDaily.js @@ -0,0 +1,802 @@ +exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const MODULE_NAME = "Multi Period Daily"; + const GMT_SECONDS = ':00.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + thisObject = { + initialize: initialize, + finalize: finalize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + + let statusDependencies; + let dataDependencies; + let datasets = []; + let dataFiles = new Map; + + let botInstance; + + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + let bootstrappingTheProcess = false + + let processConfig; + let beginingOfMarket + + return thisObject; + + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + dataDependencies = pDataDependencies; + processConfig = pProcessConfig; + + for (let i = 0; i < dataDependencies.nodeArray.length; i++) { + + let key; + let dataset; + let dependency = dataDependencies.nodeArray[i]; + + key = dependency.dataMine + "-" + + dependency.bot + "-" + + dependency.product + "-" + + dependency.dataSet + "-" + + dependency.dataSetVersion + + dataset = dataDependencies.dataSets.get(key); + + datasets.push(dataset); + + } + + let USER_BOT_MODULE = require("./IndicatorBot") + + botInstance = USER_BOT_MODULE.newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE); + botInstance.initialize(callBackFunction); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function finalize() { + datasets = undefined + dataFiles = undefined + statusDependencies = undefined + dataDependencies = undefined + botInstance = undefined + fileStorage = undefined + processConfig = undefined + thisObject = undefined + } + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = bot.market; + + /* Context Variables */ + + let contextVariables = { + lastFile: undefined, // Datetime of the last file files sucessfully produced by this process. + dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history. + dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process. + }; + + let previousDay; // Holds the date of the previous day relative to the processing date. + + let interExecutionMemoryArray; + + getContextVariables(); + + function getContextVariables() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); } + + let thisReport; + let reportKey; + let statusReport; + + if (processConfig.framework.startDate.fixedDate !== undefined) { + + /* The starting date is fixed, we will start from there. */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> We have got a user defined startDate. -> startDate = " + processConfig.framework.startDate.fixedDate); } + contextVariables.dateBeginOfMarket = new Date(processConfig.framework.startDate.fixedDate); + + } else { + + /* + We look first for the bot who knows the begining of the market in order to get when the market starts. + */ + + statusReport = statusDependencies.reportsByMainUtility.get("Market Starting Point") + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Market Starting Point -> Status Report does not exist or Market Starting Point not defined. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusReport.file; + + if (thisReport.beginingOfMarket === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet."); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency does not exist." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.dateBeginOfMarket = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS); + + } + + if (processConfig.framework.endDate.fixedDate !== undefined) { + + /* The ending date is fixed, we will end there. */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> We have got a user defined endDate. -> endDate = " + processConfig.framework.endDate.fixedDate); } + contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate); + + } else { + + /* + Here we get the status report from the bot who knows which is the end of the market. + */ + + statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point") + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Market Ending Point -> Status Report does not exist or Market Ending Point not defined. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + thisReport = statusReport.file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + contextVariables.dateEndOfMarket = new Date(thisReport.lastFile.valueOf()); + } + + /* Finally we get our own Status Report. */ + + statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Self Reference -> Status Report does not exist or Self Reference not defined. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + thisReport = statusReport.file; + + if (thisReport.lastFile !== undefined) { + + beginingOfMarket = new Date(thisReport.beginingOfMarket); + + if (beginingOfMarket.valueOf() !== contextVariables.dateBeginOfMarket.valueOf()) { // Reset Mechanism for Begining of the Market + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey); + + beginingOfMarket = new Date(contextVariables.dateBeginOfMarket.valueOf()); + startFromBegining(); + return; + } + + if (bot.hasTheBotJustStarted === true && processConfig.framework.startDate.resumeExecution === false) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Starting from the begining because bot has just started and resume execution was true."); } + startFromBegining(); + return; + } + + contextVariables.lastFile = new Date(thisReport.lastFile); + interExecutionMemoryArray = thisReport.interExecutionMemoryArray; + + processTimeFrames(); + return; + + } else { + + /* + In the case when there is no status report, we assume like the last processed file is the one on the date of Begining of Market. + */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Starting from the begining of the market because own status report not found or lastFile was undefined."); } + beginingOfMarket = new Date(contextVariables.dateBeginOfMarket.valueOf()); + startFromBegining(); + return; + } + + function startFromBegining() { + + contextVariables.lastFile = new Date(contextVariables.dateBeginOfMarket.getUTCFullYear() + "-" + (contextVariables.dateBeginOfMarket.getUTCMonth() + 1) + "-" + contextVariables.dateBeginOfMarket.getUTCDate() + " " + "00:00" + GMT_SECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> startFromBegining -> contextVariables.lastFile = " + contextVariables.lastFile); } + + /* + The first time the process is running is the right time to create the data structure that is going to be shared across different executions. + This data structure has one object per each timeFrame. + */ + + interExecutionMemoryArray = []; + /* + Also, we will remember that we are bootstrapping the process, this will allow us, to advanceTime if necesary. + It can happen that a process depends on data from another process that does not produce a file during the first day, or even more days, + in that situation we want the process not to just wait for that data that will never arrive, but to advance time until it finds valid data. + */ + + bootstrappingTheProcess = true + + for (let i = 0; i < global.dailyFilePeriods.length; i++) { + let emptyObject = {}; + interExecutionMemoryArray.push(emptyObject); + } + + processTimeFrames(); + + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = "+ err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function processTimeFrames() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> Entering function."); } + + try { + + let n; + let botNeverRan = true; + + bot.multiPeriodDailyProcessDatetime = new Date(contextVariables.lastFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop. + + advanceTime(); + + function advanceTime() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> advanceTime -> Entering function."); } + + bot.multiPeriodDailyProcessDatetime = new Date(bot.multiPeriodDailyProcessDatetime.valueOf() + ONE_DAY_IN_MILISECONDS); + previousDay = new Date(bot.multiPeriodDailyProcessDatetime.valueOf() - ONE_DAY_IN_MILISECONDS); + + if (global.WRITE_LOGS_TO_FILES === 'true') { + logger.newInternalLoop(bot.codeName, bot.process); + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> advanceTime -> bot.multiPeriodDailyProcessDatetime = " + bot.multiPeriodDailyProcessDatetime); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> advanceTime -> previousDay = " + previousDay); } + + /* Validation that we are not going past the head of the market. */ + + if (bot.multiPeriodDailyProcessDatetime.valueOf() > contextVariables.dateEndOfMarket.valueOf()) { + + const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> advanceTime -> " + logText); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + periodsLoop(); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> advanceTime -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoop -> Entering function."); } + + /* Telling the world we are alive and doing well */ + let processingDate = bot.multiPeriodDailyProcessDatetime.getUTCFullYear() + '-' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCMonth() + 1, 2) + '-' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCDate(), 2); + bot.processHeartBeat(processingDate) + + /* + + We will iterate through all posible periods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + periodsLoopBody(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoop -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> Entering function."); } + + const timeFrame = global.dailyFilePeriods[n][0]; + const timeFrameLabel = global.dailyFilePeriods[n][1]; + + if (processConfig.framework.validPeriods !== undefined) { + let validPeriod = false; + for (let i = 0; i < processConfig.framework.validPeriods.length; i++) { + let period = processConfig.framework.validPeriods[i]; + if (period === timeFrameLabel) { validPeriod = true } + } + if (validPeriod === false) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> timeFrameLabel = " + timeFrameLabel); } + periodsControlLoop(); + return; + } + } + + let dependencyIndex = 0; + dataFiles = new Map(); + + dependencyLoopBody(); + + function dependencyLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } + + let dependency = dataDependencies.nodeArray[dependencyIndex]; + let dataset = datasets[dependencyIndex]; + + let previousFile; + let currentFile; + + if (bot.multiPeriodDailyProcessDatetime.valueOf() > contextVariables.dateBeginOfMarket.valueOf()) { + getPreviousFile(); + } else { + previousFile = []; + getCurrentFile() + } + + function getPreviousFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> Entering function."); } + + let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2); + let filePath + if (dependency.dataSet === "Multi-Period-Daily") { + filePath = dependency.product + '/' + dependency.dataSet + "/" + timeFrameLabel + "/" + dateForPath; + } else { + filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; + } + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + dataset.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> text = " + text); } + + if ((err.message === "File does not exist." && botNeverRan === true) || err.code === 'The specified key does not exist.') { + + /* + Sometimes one of the dependencies of an indicator for some reasons are not calculated from the begining of the market. + When that happens we can not find those files. What we do in this situation is to move the time fordward until we can find + all the dependencies and the first run of the bot is successful. + + After that, we will not accept more missing files on any of the dependencies, and if any is missing we will abort the processing. + */ + logger.write(MODULE_NAME, "[WARN] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> Skipping day because file " + filePath + "/" + fileName + " was not found."); + + advanceTime(); + return; + } + + if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = "+ err.stack); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = "+ err.stack); + callBackFunction(err); + return; + } + + previousFile = JSON.parse(text); + + getCurrentFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getCurrentFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> Entering function."); } + + let dateForPath = bot.multiPeriodDailyProcessDatetime.getUTCFullYear() + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCDate(), 2); + let filePath + if (dependency.dataSet === "Multi-Period-Daily") { + filePath = dependency.product + '/' + dependency.dataSet + "/" + timeFrameLabel + "/" + dateForPath; + } else { + filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; + } + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + dataset.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> text = " + text); } + + if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { + + if (bootstrappingTheProcess === true) { + /* + In the special situation where we are running this process for the first time (no status report found), + we will consider the possibility that a depanant process does not produce a file during the first day of the market, + or even more than one day. That is why we are going to advance time here. + */ + advanceTime() + return + } + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.code); + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> fileName = " + fileName); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> Not OK -> onFileReceived -> err = " + err.code); + callBackFunction(err); + return; + } + + currentFile = JSON.parse(text); + + let dataFile = previousFile.concat(currentFile); + + dataFiles.set(dependency.id, dataFile); + dependencyControlLoop(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> Catch Error -> onFileReceived -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoop -> dependencyLoopBody -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function dependencyControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> dependencyControlLoop -> Entering function."); } + + dependencyIndex++; + + if (dependencyIndex < dataDependencies.nodeArray.length) { + + dependencyLoopBody(); + + } else { + + callTheBot(); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> dependencyControlLoop -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function callTheBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> Entering function."); } + + const timeFrame = global.dailyFilePeriods[n][0]; + const timeFrameLabel = global.dailyFilePeriods[n][1]; + + botInstance.start( + dataFiles, + timeFrame, + timeFrameLabel, + bot.multiPeriodDailyProcessDatetime, + interExecutionMemoryArray[n], + onBotFinished); + + function onBotFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> onBotFinished -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + callBackFunction(err); + return; + } + + botNeverRan = false; + periodsControlLoop(); + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> onBotFinished -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsControlLoop -> Entering function."); } + + n++; + + if (n < global.dailyFilePeriods.length) { + + periodsLoopBody(); + + } else { + + n = 0; + + writeDataRanges(onWritten); + + function onWritten(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> controlLoop -> onWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> controlLoop -> onWritten -> err = "+ err.stack); + callBackFunction(err); + return; + } + + writeStatusReport(bot.multiPeriodDailyProcessDatetime, advanceTime); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> controlLoop -> onWritten -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsControlLoop -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeDataRanges(callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } + + let outputDatasetIndex = -1; + controlLoop(); + + function productLoopBody() { + let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; + writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, productCodeName, controlLoop); + } + + function controlLoop() { + + outputDatasetIndex++; + + if (outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length) { + productLoopBody(); + } else { + callBack(global.DEFAULT_OK_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = "+ err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + } + + function writeDataRange(pBegin, pEnd, productCodeName, callBack) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); } + + let dataRange = { + begin: pBegin.valueOf(), + end: pEnd.valueOf() + }; + + let fileContent = JSON.stringify(dataRange); + + let fileName = '/Data.Range.' + market.baseAsset + '_' + market.quotedAsset + '.json'; + let filePath = bot.filePathRoot + "/Output/" + productCodeName + "/" + bot.process + fileName; + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = "+ err.stack); + callBack(err); + return; + } + + if (LOG_FILE_CONTENT === true) { + logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent); + } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = "+ err.stack); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(lastFileDate, callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); } + + try { + + let reportKey = bot.dataMine + "-" + bot.codeName + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.currentDaytime; + thisReport.file.lastFile = lastFileDate; + thisReport.file.interExecutionMemoryArray = interExecutionMemoryArray; + thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString() + thisReport.save(callBack); + + bot.hasTheBotJustStarted = false; + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } +}; diff --git a/TaskServer/MultiPeriodMarket.js b/TaskServer/MultiPeriodMarket.js new file mode 100644 index 000000000..9ba755bb1 --- /dev/null +++ b/TaskServer/MultiPeriodMarket.js @@ -0,0 +1,354 @@ +exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const MODULE_NAME = "Multi Period Market"; + + thisObject = { + initialize: initialize, + finalize: finalize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + + let statusDependencies; + let dataDependencies; + let datasets = []; + let dataFiles = new Map(); + + let botInstance; + + let processConfig; + + return thisObject; + + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencies = pStatusDependencies; + dataDependencies = pDataDependencies; + processConfig = pProcessConfig; + + for (let i = 0; i < dataDependencies.nodeArray.length; i++) { + + let key; + let dataset; + let dependency = dataDependencies.nodeArray[i]; + + key = dependency.dataMine + "-" + + dependency.bot + "-" + + dependency.product + "-" + + dependency.dataSet + "-" + + dependency.dataSetVersion + + dataset = dataDependencies.dataSets.get(key); + + datasets.push(dataset); + + } + + let USER_BOT_MODULE = require("./IndicatorBot") + + botInstance = USER_BOT_MODULE.newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE); + botInstance.initialize(callBackFunction); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function finalize() { + datasets = undefined + dataFiles = undefined + statusDependencies = undefined + dataDependencies = undefined + botInstance = undefined + processConfig = undefined + thisObject = undefined + } + + function start(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = bot.market; + + /* Context Variables */ + + let contextVariables = { + dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history. + dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process. + }; + + if (processConfig.framework.startDate.fixedDate !== undefined) { + + /* The starting date is fixed, we will start from there. */ + + contextVariables.dateBeginOfMarket = new Date(processConfig.framework.startDate.fixedDate); + + } + + if (processConfig.framework.endDate.fixedDate !== undefined) { + + /* The ending date is fixed, we will end there. */ + + contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate); + + } + + processTimeFrames(); + + function processTimeFrames() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> Entering function."); } + + try { + + let n; + + periodsLoop(); + + function periodsLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoop -> Entering function."); } + + /* + + We will iterate through all posible periods. + + */ + + n = 0 // loop Variable representing each possible period as defined at the periods array. + + periodsLoopBody(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoop -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> Entering function."); } + + const timeFrame = global.marketFilesPeriods[n][0]; + const timeFrameLabel = global.marketFilesPeriods[n][1]; + + let dependencyIndex = 0; + dataFiles = new Map; + + dependencyLoopBody(); + + function dependencyLoopBody() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } + + let dependency = dataDependencies.nodeArray[dependencyIndex]; + let dataset = datasets[dependencyIndex]; + + getFile(); + + function getFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getFile -> Entering function."); } + + let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2); + let fileName = market.baseAsset + '_' + market.quotedAsset + ".json"; + + let filePath + if (dependency.dataSet === "Multi-Period-Market") { + filePath = dependency.product + '/' + dependency.dataSet + "/" + timeFrameLabel; + } else { + filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; + } + dataset.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> text = " + text); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = "+ err.stack); + callBackFunction(err); + return; + } + + let dataFile = JSON.parse(text); + dataFiles.set(dependency.id, dataFile); + + dependencyControlLoop(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> dependencyLoopBody -> getFile -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoop -> dependencyLoopBody -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function dependencyControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> dependencyControlLoop -> Entering function."); } + + dependencyIndex++; + + if (dependencyIndex < dataDependencies.nodeArray.length) { + + dependencyLoopBody(); + + } else { + + callTheBot(); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> dependencyControlLoop -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function callTheBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> Entering function."); } + + const timeFrame = global.marketFilesPeriods[n][0]; + const timeFrameLabel = global.marketFilesPeriods[n][1]; + + botInstance.start( + dataFiles, + timeFrame, + timeFrameLabel, + undefined, + undefined, + onBotFinished); + + function onBotFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> onBotFinished -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + callBackFunction(err); + return; + } + + periodsControlLoop(); + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> onBotFinished -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch(err){ + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> callTheBot -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsLoopBody -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function periodsControlLoop() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFrames -> periodsControlLoop -> Entering function."); } + + n++; + + if (n < global.marketFilesPeriods.length) { + + periodsLoopBody(); + + } else { + + writeStatusReport(callBackFunction); + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> periodsControlLoop -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFrames -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStatusReport(callBack) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } + + try { + + let reportKey = bot.dataMine + "-" + bot.codeName + "-" + "Multi-Period-Market" + "-" + "dataSet.V1"; + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime; + thisReport.save(callBack); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/ProcessExecutionEvents.js b/TaskServer/ProcessExecutionEvents.js new file mode 100644 index 000000000..ed79e1be7 --- /dev/null +++ b/TaskServer/ProcessExecutionEvents.js @@ -0,0 +1,161 @@ +exports.newProcessExecutionEvents = function newProcessExecutionEvents(BOT, logger) { + + /* + + Here we manage the process execution events. This allow us to emit events when the process starts and finishes. Also to wait for a dependent event + of another process. + + */ + + const MODULE_NAME = "Process Execution Events"; + + let bot = BOT; + + let thisObject = { + initialize, initialize, + start: start, + finish: finish + }; + + let processThisDependsOn; // This is the process this process needs to wait for it to finishes in order to start. + + let currentProcessKey + + return thisObject; + + function initialize(callBackFunction) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + let name = 'Not Depends on any Process' + + if (bot.processNode.referenceParent !== undefined) { + + let market = bot.market.baseAsset + '/' + bot.market.quotedAsset + currentProcessKey = bot.processNode.referenceParent.name + "-" + bot.processNode.referenceParent.type + "-" + bot.processNode.referenceParent.id + "-" + bot.exchange + "-" + market + + if (bot.processNode.referenceParent.executionStartedEvent !== undefined) { + if (bot.processNode.referenceParent.executionStartedEvent.referenceParent !== undefined) { + if (bot.processNode.referenceParent.executionStartedEvent.referenceParent.parentNode !== undefined) { + processThisDependsOn = bot.processNode.referenceParent.executionStartedEvent.referenceParent.parentNode + + name = processThisDependsOn.name + if (processThisDependsOn.code.monthlyInstances === true) { + logger.fileName = MODULE_NAME + "." + name; + } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> " + currentProcessKey + " depends on " + name); } + } + } + } + } + + logger.fileName = MODULE_NAME + "." + name; + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(callBackFunction) { + + try { + + if (processThisDependsOn !== undefined) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> Entering Code Block."); } + + /* This forces this process to wait until the process that this one depends on, emits its event signaling that the process execution has finished. */ + + let extraCallerId = '-' + Math.trunc(Math.random() * 10000) + '-' + + let market = bot.market.baseAsset + '/' + bot.market.quotedAsset + let key = processThisDependsOn.name + "-" + processThisDependsOn.type + "-" + processThisDependsOn.id + "-" + bot.exchange + "-" + market + let callerId = bot.dataMine + "-" + bot.codeName + "-" + bot.process + extraCallerId + + let subscriptionId + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> started listening to Process Execution Finished. "); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> key = " + key); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> callerId = " + callerId); } + + global.SYSTEM_EVENT_HANDLER.listenToEvent(key, 'Process Execution Finished', undefined, callerId, responseCallBack, eventsCallBack) + + function responseCallBack(message) { + if (message.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> responseCallBack -> message = " + JSON.stringify(message)) + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } else { + subscriptionId = message.eventSubscriptionId + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> subscriptionId = " + subscriptionId); } + } + } + + function eventsCallBack(message) { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> message = " + JSON.stringify(message)); } + + /* We continue the normal flow after we learn the dependent process has finished its execution. */ + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> stopListening to Process Execution Finished. "); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> subscriptionId = " + subscriptionId); } + global.SYSTEM_EVENT_HANDLER.stopListening(key, 'Process Execution Finished', subscriptionId) + + if (message.event.err.result === global.DEFAULT_OK_RESPONSE.result) { + /* We emit our own event that the process started. */ + let event = { + err: global.DEFAULT_OK_RESPONSE + } + global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started') + global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> " + currentProcessKey + " Process Execution Started because " + key + " Finished."); } + } + + callBackFunction(message.event.err); + } + } else { + /* In this case, the Process does not depends on a process which needs to wait for. */ + /* We emit our own event that the process started. */ + let event = { + err: global.DEFAULT_OK_RESPONSE + } + global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started') + global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> " + currentProcessKey + " Process Execution Started " ); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function finish(callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> Entering function."); } + + let event = { + err: global.DEFAULT_OK_RESPONSE + } + + global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Finished') + global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Finished', event) + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> " + currentProcessKey + " Process Execution Finished " ); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] finish -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +}; diff --git a/TaskServer/ProcessOutput.js b/TaskServer/ProcessOutput.js new file mode 100644 index 000000000..6a07bbc34 --- /dev/null +++ b/TaskServer/ProcessOutput.js @@ -0,0 +1,104 @@ +exports.newProcessOutput = function newProcessOutput(BOT, logger) { + + /* + Here we emit the events that signals that a Dataset was updated. + */ + + const MODULE_NAME = "Process Output"; + + let bot = BOT; + + let thisObject = { + raiseEvents, raiseEvents + }; + + return thisObject; + + function raiseEvents(statusDependencies, callBackFunction) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] raiseEvents -> Entering function."); } + + /* We will reaise the events for the datasets impacted by the process that just finished. */ + + if (bot.processNode.referenceParent.processOutput !== undefined) { + if (bot.processNode.referenceParent.processOutput.outputDatasets !== undefined) { + let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets + + for (let j = 0; j < outputDatasets.length; j++) { + let outputDataset = outputDatasets[j] + + /* Some validations to verify that everything is in place. */ + if (outputDataset.referenceParent !== undefined) { + if (outputDataset.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Dataset Definition not defined. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDataset.referenceParent.parentNode !== undefined) { + if (outputDataset.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Product not defined. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDataset.referenceParent.parentNode.parentNode !== undefined) { + if (outputDataset.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Bot not defined. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDataset.referenceParent.parentNode.parentNode.parentNode !== undefined) { + if (outputDataset.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Data Mine not defined. Data Mine = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Data Mine. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Product not attached to a Bot. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Dataset Definition not attached to a Product. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Output Dataset with no reference to a Dataset Definition. Output Dataset = " + JSON.stringify(outputDataset)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* All good, lets emit the event. */ + + let dataMine = outputDataset.referenceParent.parentNode.parentNode.parentNode.code.codeName + let botName = outputDataset.referenceParent.parentNode.parentNode.code.codeName + let product = outputDataset.referenceParent.parentNode.code.codeName + let dataset = outputDataset.referenceParent.code.codeName + + let statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") + + key = dataMine + "-" + botName + "-" + product + "-" + dataset + let event = { + lastFile: statusReport.file.lastFile + } + global.SYSTEM_EVENT_HANDLER.createEventHandler(key, 'Dataset Updated') + global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Dataset Updated', event) + + } + } + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/Root.js b/TaskServer/Root.js new file mode 100644 index 000000000..41e18541b --- /dev/null +++ b/TaskServer/Root.js @@ -0,0 +1,574 @@ +exports.newRoot = function newRoot() { + + /* This is the Execution Datetime */ + + global.EXECUTION_DATETIME = new Date(); + + /* Time Frames Definitions. */ + + global.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"' + ']' + ']'; + + global.marketFilesPeriods = JSON.parse(global.marketFilesPeriods); + + global.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"' + ']' + ']'; + + global.dailyFilePeriods = JSON.parse(global.dailyFilePeriods); + + const ROOT_DIR = './'; + const MODULE_NAME = "Root"; + const WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START = 10000 // This avoid a race condition that could happen if one process finished before all the other even started. + + let thisObject = { + initialize: initialize, + start: start + } + + const FULL_LOG = true; + + let logDisplace = "Task Server" + " "; + + return thisObject; + + function initialize(callBackFunction) { + + try { + + /* Global control of logging. */ + + global.LOG_CONTROL = { + "Assistant": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false + }, + "Exchange API": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false + }, + "Status Report": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false + }, + "Dataset": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false + }, + "Context": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false + }, + "Process Execution Events": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false + }, + "Process Output": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false + } + }; + + callBackFunction(); + + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] initialize -> err = " + err.stack); + return; + } + } + + function start(processIndex) { + + try { + + /* Now we will run according to what we see at the config file. */ + + /* Small Function to fix numbers into strings in a cool way. */ + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad("0" + str, max) : str; + } + + /* Process Loops Declarations. */ + + const INDICATOR_BOT_MAIN_LOOP_MODULE = require('./IndicatorBotProcessMainLoop'); + const SENSOR_BOT_MAIN_LOOP_MODULE = require('./SensorBotProcessMainLoop'); + const TRADING_ENGINE_MAIN_LOOP_MODULE = require('./TradingBotProcessMainLoop'); + + let botConfig; + let processInstance = global.TASK_NODE.bot.processes[processIndex] + + /* Here we will check if we need to load the configuration and code of the bot from a file or we will take that from the UI. */ + if ( + processInstance.referenceParent.code.framework !== undefined && + ( processInstance.referenceParent.code.framework.name === 'Multi-Period-Market' || + processInstance.referenceParent.code.framework.name === 'Multi-Period-Daily' || + processInstance.referenceParent.code.framework.name === 'Multi-Period') + ) { + botConfig = processInstance.referenceParent.parentNode.code + botConfig.definedByUI = true + bootingBot(processIndex) + return + } else { + getBotConfigFromFile(); + return + } + + function getBotConfigFromFile() { + + try { + + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(); + + let filePath = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName + '/bots/' + global.TASK_NODE.bot.code.repo + '/this.bot.config.json'; + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> filePath = " + filePath); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> dataMine = " + global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName); + return; + } + + try { + botConfig = JSON.parse(text); + botConfig.repo = global.TASK_NODE.bot.code.repo; + bootingBot(processIndex); + } catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); + return; + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> err = " + err.stack); + return; + } + } + + function bootingBot(processIndex) { + + try { + + botConfig.process = global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName + botConfig.debug = {}; + botConfig.processNode = global.TASK_NODE.bot.processes[processIndex] + + /* Logs Mantainance Stuff */ + botConfig.LOGS_TO_DELETE_QUEUE = [] + botConfig.DELETE_QUEUE_SIZE = 10 // This number represents how many log files can be at the queue at any point in time, which means how many logs are not still deleted. + + /* Simplifying the access to basic info */ + botConfig.dataMine = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName + botConfig.exchange = global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.parentNode.parentNode.code.codeName + botConfig.exchangeNode = global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.parentNode.parentNode + botConfig.market = { + baseAsset: global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.baseAsset.referenceParent.code.codeName, + quotedAsset: global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.quotedAsset.referenceParent.code.codeName + } + botConfig.uiStartDate = global.TASK_NODE.bot.code.startDate + + /* This stuff is still hardcoded and unresolved. */ + botConfig.version = { + "major": 1, + "minor": 0 + } + botConfig.dataSetVersion = "dataSet.V1" + + /* Loop Counter */ + botConfig.loopCounter = 0; + + /* File Path Root */ + botConfig.filePathRoot = botConfig.dataMine + "/" + botConfig.codeName + "/" + botConfig.exchange; + + /* Process Key */ + botConfig.processKey = global.TASK_NODE.bot.processes[processIndex].name + '-' + global.TASK_NODE.bot.processes[processIndex].type + '-' + global.TASK_NODE.bot.processes[processIndex].id + + /* Bot Type */ + botConfig.type = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.type + + /* Setting the Key to use */ + process.env.KEY = undefined + process.env.SECRET = undefined + + if (botConfig.processNode) { + if (botConfig.processNode.marketReference) { + if (botConfig.processNode.marketReference.keyInstance !== undefined) { + if (botConfig.processNode.marketReference.keyInstance.referenceParent !== undefined) { + let key = botConfig.processNode.marketReference.keyInstance.referenceParent + + process.env.KEY = key.code.codeName + process.env.SECRET = key.code.secret + } + } + } + } + + let processConfig = global.TASK_NODE.bot.processes[processIndex].referenceParent.code + + if (processConfig.framework !== undefined) { + if (processConfig.framework.name === "Multi-Period-Daily" || processConfig.framework.name === "Multi-Period-Market" || processConfig.framework.name === "Multi-Period") { + processConfig.framework.startDate.resumeExecution = true; + if (processConfig.startMode.noTime !== undefined) { + if (processConfig.startMode.noTime.run === "true") { + if (processConfig.startMode.noTime.beginDatetime !== undefined) { + processConfig.framework.startDate.fixedDate = processConfig.startMode.noTime.beginDatetime; + processConfig.framework.startDate.resumeExecution = true; + } + } + } + if (processConfig.startMode.userDefined !== undefined) { + if (processConfig.startMode.userDefined.run === "true") { + if (processConfig.startMode.userDefined.beginDatetime !== undefined) { + processConfig.framework.startDate.fixedDate = processConfig.startMode.userDefined.beginDatetime; + processConfig.framework.startDate.resumeExecution = processConfig.startMode.userDefined.resumeExecution; + } + } + } + } + } + + try { + + if (processConfig.startMode.noTime !== undefined) { + + if (processConfig.startMode.noTime.run === "true") { + + if (processConfig.startMode.noTime.resumeExecution === true) { + botConfig.hasTheBotJustStarted = false; + } else { + botConfig.hasTheBotJustStarted = true; + } + + switch (botConfig.type) { + case 'Sensor Bot': { + runSensorBot(botConfig, processConfig); + break; + } + case 'Indicator Bot': { + runIndicatorBot(botConfig, processConfig); + break; + } + case 'Trading Bot': { + runTradingBot(botConfig, processConfig); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + if (processConfig.startMode.fixedInterval !== undefined) { + + if (processConfig.startMode.fixedInterval.run === "true") { + + botConfig.runAtFixedInterval = true; + botConfig.fixedInterval = processConfig.startMode.fixedInterval.interval; + + switch (botConfig.type) { + case 'Sensor Bot': { + runSensorBot(botConfig, processConfig); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + if (processConfig.startMode.userDefined !== undefined) { + + if (processConfig.startMode.userDefined.run === "true") { + + botConfig.startMode = "User Defined"; + + if (processConfig.startMode.userDefined.resumeExecution === true) { + botConfig.hasTheBotJustStarted = false; + } else { + botConfig.hasTheBotJustStarted = true; + } + + switch (botConfig.type) { + case 'Trading Bot': { + runTradingBot(botConfig, processConfig); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + } catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected exception. -> err = " + err.stack); + } + + + function runSensorBot(pBotConfig, pProcessConfig) { + + try { + global.TOTAL_PROCESS_INSTANCES_CREATED++ + + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = pBotConfig; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> Entering function."); } + + let extractionBotMainLoop = SENSOR_BOT_MAIN_LOOP_MODULE.newSensorBotProcessMainLoop(pBotConfig, logger); + extractionBotMainLoop.initialize(pProcessConfig, onInitializeReady); + + function onInitializeReady(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + extractionBotMainLoop.run(whenRunFinishes); + + function whenRunFinishes(err) { + + pBotConfig.loopCounter = 0; + + let botId = pBotConfig.dataMine + "." + pBotConfig.codeName + "." + pBotConfig.process; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.persist(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution was aborted."); + logger.persist(); + } + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> Bot will not be started. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> err = " + err.message); + + logger.persist(); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> err = " + err.stack); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + + function runIndicatorBot(pBotConfig, pProcessConfig) { + + try { + global.TOTAL_PROCESS_INSTANCES_CREATED++ + + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = pBotConfig; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> Entering function."); } + + let indicatorBotMainLoop = INDICATOR_BOT_MAIN_LOOP_MODULE.newIndicatorBotProcessMainLoop(pBotConfig, logger); + indicatorBotMainLoop.initialize(pProcessConfig, onInitializeReady); + + function onInitializeReady(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + indicatorBotMainLoop.run(whenRunFinishes); + + function whenRunFinishes(err) { + + pBotConfig.loopCounter = 0; + + let botId = pBotConfig.dataMine + "." + pBotConfig.codeName + "." + pBotConfig.process; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + + + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.persist(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.persist(); + } + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> err = " + err.message); + logger.persist(); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> err = " + err.stack); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + + function runTradingBot(pBotConfig, pProcessConfig) { + + global.TOTAL_PROCESS_INSTANCES_CREATED++ + + try { + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = pBotConfig; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> Entering function."); } + + let tradingBotMainLoop = TRADING_ENGINE_MAIN_LOOP_MODULE.newTradingBotProcessMainLoop(pBotConfig, logger); + tradingBotMainLoop.initialize(pProcessConfig, onInitializeReady); + + function onInitializeReady(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + tradingBotMainLoop.run(whenRunFinishes); + + function whenRunFinishes(err) { + + pBotConfig.loopCounter = 0; + + let botId; + + botId = pBotConfig.dataMine + "." + pBotConfig.codeName + "." + pBotConfig.process; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.persist(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.persist(); + } + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> err = " + err.message); + logger.persist(); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> err = " + err.stack); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> err = " + err.stack); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + + function exitProcessInstance() { + + global.ENDED_PROCESSES_COUNTER++ + console.log("[INFO] Task Server -> " + global.TASK_NODE.name + " -> exitProcessInstance -> Process #" + global.ENDED_PROCESSES_COUNTER + " from " + global.TOTAL_PROCESS_INSTANCES_CREATED + " exiting."); + + if (global.ENDED_PROCESSES_COUNTER === global.TOTAL_PROCESS_INSTANCES_CREATED) { + global.EXIT_NODE_PROCESS() + } + } + } catch (err) { + console.log("[ERROR] Task Server -> " + global.TASK_NODE.name + " -> Root -> Start -> Err = " + err.stack); + } + } +} + + + + diff --git a/TaskServer/SensorBotProcessMainLoop.js b/TaskServer/SensorBotProcessMainLoop.js new file mode 100644 index 000000000..b8e2c49fc --- /dev/null +++ b/TaskServer/SensorBotProcessMainLoop.js @@ -0,0 +1,915 @@ +exports.newSensorBotProcessMainLoop = function newSensorBotProcessMainLoop(bot, parentLogger) { + + const ROOT_DIR = './'; + + const MODULE_NAME = "Sensor Process Main Loop"; + const FULL_LOG = true; + + let USER_BOT_MODULE; + let COMMONS_MODULE; + + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(parentLogger); + + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble. + + let nextLoopTimeoutHandle; + let checkLoopHealthHandle; + + let thisObject = { + initialize: initialize, + run: run + }; + + let processConfig; + + return thisObject; + + function initialize(pProcessConfig, callBackFunction) { + + /* This function is exactly the same in the 3 modules representing the 2 different bot types loops. */ + + try { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + processConfig = pProcessConfig; + + if (bot.definedByUI === true) { + /* The code of the bot is defined at the UI. No need to load a file with the code. */ + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + + let filePath = bot.dataMine + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName + filePath += "/User.Bot.js" + + fileStorage.getTextFile(filePath, onBotDownloaded); + + function onBotDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message); + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + USER_BOT_MODULE = {} + if (bot.process === "Hole-Fixing") { + //USER_BOT_MODULE.newUserBot = require(process.env.BOTS_PATH + '/aamasters/AAMasters/bots/AACharly-Sensor-Bot/Hole-Fixing/User.Bot').newUserBot + } + try { + USER_BOT_MODULE.newUserBot = eval(text); // TODO This needs to be changed function + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onBotDownloaded -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + filePath = bot.dataMine + "/" + "bots" + "/" + bot.repo; + filePath += "/Commons.js" + + fileStorage.getTextFile(filePath, onCommonsDownloaded); + + function onCommonsDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.code || err.message); + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + COMMONS_MODULE = {} + + COMMONS_MODULE.newCommons = eval(text); // TODO This needs to be changed function + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function run(callBackFunction) { + + try { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] run -> Entering function."); } + + bot.enableCheckLoopHealth = true; + + let fixedTimeLoopIntervalHandle; + + if (bot.runAtFixedInterval === true) { + + fixedTimeLoopIntervalHandle = setInterval(loop, bot.fixedInterval); + loop(); // First run. + + } else { + + loop(); + + } + + function loop() { + + try { + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } + + /* For each loop we want to create a new log file. */ + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = bot; + logger.initialize(); + + bot.loopCounter++; + bot.loopStartTime = new Date().valueOf(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Entering function."); } + + /* Loop Heartbeat sent to the UI */ + bot.processHeartBeat = processHeartBeat + processHeartBeat() + + /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */ + + const UTILITIES = require(ROOT_DIR + 'CloudUtilities'); + const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); + const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); + const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput'); + + /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ + + bot.processDatetime = new Date(); // This will be considered the process date and time, so as to have it consistenly all over the execution. + + /* High level log entry */ + + console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + pad(bot.exchange, 20) + " " + pad(bot.market.baseAsset + '/' + bot.market.quotedAsset, 10) + + " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8) ) + + /* We will prepare first the infraestructure needed for the bot to run. There are 4 modules we need to sucessfullly initialize first. */ + + let processOutput + let processExecutionEvents + let userBot; + let statusDependencies; + let exchangeAPI; + + let nextWaitTime; + + initializeProcessExecutionEvents(); + + function initializeProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } + + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) + + processExecutionEvents.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well."); + startProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.start(onStarted); + + function onStarted(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well."); + initializeStatusDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeStatusDependencies() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> Entering function."); } + + statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES); + + statusDependencies.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well."); + initializeUserBot(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeStatusDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeUserBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> Entering function."); } + + usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage, STATUS_REPORT, exchangeAPI); + + usertBot.initialize(statusDependencies, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized > Execution finished well."); + startUserBot(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeUserBot -> onInizialized > Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized > Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized > Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized > Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized > Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startUserBot() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> Entering function."); } + + usertBot.start(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished > Execution finished well."); + raiseEventsProcessOutput() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished > Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished > Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Dependency does not exist.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished > Dependency does not exist. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Dependency not ready.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startUserBot -> onFinished > Dependency not ready. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished > Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished > Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished > Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function raiseEventsProcessOutput() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); } + + processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger) + + processOutput.raiseEvents(statusDependencies, onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well."); + finishProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> raiseEventsProcessOutput -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function finishProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.finish(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function loopControl(nextWaitTime) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> nextWaitTime = " + nextWaitTime); } + + /* We show we reached the end of the loop. */ + + processHeartBeat() + + /* Here we check if we must stop the loop gracefully. */ + + shallWeStop(onStop, onContinue); + + function onStop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Entering function."); } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Stopping the Loop Gracefully. See you next time!"); } + + if(global.WRITE_LOGS_TO_FILES === 'true'){ + logger.persist(); + } + + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + function onContinue() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onContinue -> Entering function."); } + + /* Sensor bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */ + + switch (nextWaitTime) { + case 'Normal': { + if (bot.runAtFixedInterval === true) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Fixed Interval Normal exit point reached."); } + if (processConfig.deadWaitTime > 0) { + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.deadWaitTime, bot.loopCounter); + } + if(global.WRITE_LOGS_TO_FILES === 'true'){ + logger.persist(); + } + return; + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.normalWaitTime / 1000) + " seconds."); } + if (processConfig.deadWaitTime > 0) { + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.deadWaitTime, bot.loopCounter); + } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.normalWaitTime); + if(global.WRITE_LOGS_TO_FILES === 'true'){ + logger.persist(); + } + } + } + break; + case 'Retry': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.retryWaitTime / 1000) + " seconds."); } + if (processConfig.deadWaitTime > 0) { + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.retryWaitTime * 5, bot.loopCounter); + } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.retryWaitTime); + logger.persist(); + } + break; + case 'Sleep': { + if (bot.runAtFixedInterval === true) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Fixed Interval Sleep exit point reached."); } + logger.persist(); + return; + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.sleepWaitTime / 60000) + " minutes."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.sleepWaitTime); + logger.persist(); + } + } + break; + case 'Coma': { + if (bot.runAtFixedInterval === true) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Fixed Interval Coma exit point reached."); } + logger.persist(); + return; + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.comaWaitTime / 3600000) + " hours."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.comaWaitTime); + logger.persist(); + } + } + break; + } + } + } + + function checkLoopHealth(pLastLoop) { + + if (bot.enableCheckLoopHealth === false || global.STOP_TASK_GRACEFULLY === true) { + + logger.write(MODULE_NAME, "[WARN] run -> loop -> checkLoopHealth -> bot.enableCheckLoopHealth = " + bot.enableCheckLoopHealth); + + return; + } // This gets disabled anytime the Main Loop is shut down by any condition. + + if (bot.loopCounter <= pLastLoop + 1) { // This means that the next loop started but also stopped executing abruptally. + + let now = new Date().valueOf(); + + if (now - bot.loopStartTime > processConfig.deadWaitTime) { + + logger.write(MODULE_NAME, "[ERROR] run -> loop -> checkLoopHealth -> Dead loop found -> pLastLoop = " + pLastLoop); + console.log((new Date().toISOString() + " [ERROR] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found dead. Resurrecting it now.")); + + logger.persist(); // We persist the logs of the failed execution. + clearTimeout(nextLoopTimeoutHandle); // We cancel the timeout in case the original loop was still running and schedulled to reexecute. + loop(); // We restart the loop so that the processing can continue. + + } else { + + console.log((new Date().toISOString() + " [WARN] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found delayed but still alive. No action taken.")); + + } + } + } + + function shallWeStop(stopCallBack, continueCallBack) { + + try { + + /* IMPORTANT: This function is exactly the same on the 3 modules. */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> shallWeStop -> Entering function. "); } + + if (!global.STOP_TASK_GRACEFULLY) { + continueCallBack(); + } else { + stopCallBack(); + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = "+ err.stack); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function processHeartBeat(processingDate) { + let event = { + seconds: (new Date()).getSeconds(), + processingDate: processingDate + } + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.processKey, 'Heartbeat', event) + } + + function processStopped() { + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.processKey, 'Stopped') + } + } + + catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> err = "+ err.stack); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/Session.js b/TaskServer/Session.js new file mode 100644 index 000000000..3d61e42aa --- /dev/null +++ b/TaskServer/Session.js @@ -0,0 +1,538 @@ +exports.newSession = function newSession(bot, parentLogger) { + + const MODULE_NAME = "Session" + const FULL_LOG = true; + const ONE_YEAR_IN_MILISECONDS = 365 * 24 * 60 * 60 * 1000 + + let thisObject = { + initialize: initialize + } + + return thisObject; + + function initialize(callBackFunction) { + try { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + /* Initialize this info so that everything is logged propeerly */ + + bot.SESSION = { + name: bot.processNode.session.name, + id: bot.processNode.session.id + } + + /* Set the folderName for early logging */ + if (bot.processNode.session.code.folderName === undefined) { + bot.SESSION.folderName = bot.SESSION.id + } else { + bot.SESSION.folderName = bot.processNode.session.code.folderName + "-" + bot.SESSION.id + } + + /* Check if there is a session */ + if (bot.processNode.session === undefined) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> Cannot run without a Session."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + /* Listen to event to start or stop the session. */ + bot.sessionKey = bot.processNode.session.name + '-' + bot.processNode.session.type + '-' + bot.processNode.session.id + + global.SYSTEM_EVENT_HANDLER.listenToEvent(bot.sessionKey, 'Run Session', undefined, undefined, undefined, runSession) + global.SYSTEM_EVENT_HANDLER.listenToEvent(bot.sessionKey, 'Stop Session', undefined, undefined, undefined, stopSession) + + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + + function runSession(message) { + if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { return } // This happens when the UI is reloaded, the session was running and tries to run it again. + + /* We are going to run the Definition comming at the event. */ + bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem) + bot.SESSION = JSON.parse(message.event.session) + + /* Set the folderName for logging, reports, context and data output */ + let code + if (bot.SESSION.code !== undefined) { + code = bot.SESSION.code + if (code.folderName === undefined) { + bot.SESSION.folderName = bot.SESSION.id + } else { + bot.SESSION.folderName = code.folderName + "-" + bot.SESSION.id + } + } + + /* Set up Social Bots */ + setUpSocialBots() + + /* Extract values from different sources and consolidate them under one structure that is going to be used later on. */ + setValuesToUse(message) + let allGood + switch (bot.SESSION.type) { + case 'Backtesting Session': { + allGood = startBackTesting(message) + break + } + case 'Live Trading Session': { + allGood = startLiveTrading(message) + break + } + case 'Fordward Testing Session': { + allGood = startFordwardTesting(message) + break + } + case 'Paper Trading Session': { + allGood = startPaperTrading(message) + break + } + } + if (allGood === true) { + bot.SESSION_STATUS = 'Idle' + bot.STOP_SESSION = false + } else { + bot.STOP_SESSION = true + } + } + + function stopSession(message) { + bot.STOP_SESSION = true + } + + function checkDatetimes() { + if (bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() < (new Date()).valueOf()) { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Overriding initialDatetime with present datetime because " + bot.VALUES_TO_USE.timeRange.initialDatetime + " is in the past."); } + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() + } + if (bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf() < (new Date()).valueOf()) { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Overriding finalDatetime with present datetime plus one year because " + bot.VALUES_TO_USE.timeRange.finalDatetime + " is in the past."); } + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date() + ONE_YEAR_IN_MILISECONDS + } + } + + function startBackTesting(message) { + bot.startMode = "Backtest" + if (bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf() > (new Date()).valueOf()) { + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date() + } + bot.resumeExecution = false; + bot.hasTheBotJustStarted = true + bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) + return true + } + + function startLiveTrading(message) { + + if (process.env.KEY === undefined || process.env.SECRET === undefined) { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Key name or Secret not provided, not possible to run the process in Live mode."); } + console.log("Key 'codeName' or 'secret' not provided. Plese check that and try again.") + return + } + + bot.startMode = "Live" + checkDatetimes() + bot.resumeExecution = false; + bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) + bot.hasTheBotJustStarted = true + return true + } + + function startFordwardTesting(message) { + + if (process.env.KEY === undefined || process.env.SECRET === undefined) { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Key name or Secret not provided, not possible to run the process in Forward Testing mode."); } + console.log("Key 'codeName' or 'secret' not provided. Plese check that and try again.") + return + } + + bot.startMode = "Live" + checkDatetimes() + bot.resumeExecution = false; + bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) + bot.hasTheBotJustStarted = true + + /* Reduce the balance */ + + let balancePercentage = 1 // This is the default value + + if (bot.SESSION.code.balancePercentage !== undefined) { + balancePercentage = bot.SESSION.code.balancePercentage + } + + bot.VALUES_TO_USE.initialBalanceA = bot.VALUES_TO_USE.initialBalanceA * balancePercentage / 100 + bot.VALUES_TO_USE.initialBalanceB = bot.VALUES_TO_USE.initialBalanceB * balancePercentage / 100 + + bot.VALUES_TO_USE.minimumBalanceA = bot.VALUES_TO_USE.minimumBalanceA * balancePercentage / 100 + bot.VALUES_TO_USE.minimumBalanceB = bot.VALUES_TO_USE.minimumBalanceB * balancePercentage / 100 + + bot.VALUES_TO_USE.maximumBalanceA = bot.VALUES_TO_USE.maximumBalanceA * balancePercentage / 100 + bot.VALUES_TO_USE.maximumBalanceB = bot.VALUES_TO_USE.maximumBalanceB * balancePercentage / 100 + return true + } + + function startPaperTrading(message) { + bot.startMode = "Backtest" + + checkDatetimes() + bot.resumeExecution = false; + bot.hasTheBotJustStarted = true + bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) + return true + } + + function setValuesToUse(message) { + /* + Base on the information received we will determined which values ultimatelly are going to be used, + and once we do, they will become constants across the multiple loops executions. + */ + + /* Set all default values */ + bot.VALUES_TO_USE = { + baseAsset: "BTC", + initialBalanceA: 0.001, + initialBalanceB: 0, + minimumBalanceA: 0.0005, + minimumBalanceB: 0, + maximumBalanceA: 0.002, + maximumBalanceB: 0, + slippage: { + positionRate: 0, + stopLoss: 0, + takeProfit: 0 + }, + feeStructure: { + maker: 0, + taker: 0 + }, + timeRange: { + initialDatetime: new Date(), + finalDatetime: new Date() + } + } + + /* Session Type Dependant Default Values */ + + switch (bot.SESSION.type) { + case 'Backtesting Session': { + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date((new Date()).valueOf() - ONE_YEAR_IN_MILISECONDS) + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date() + break + } + case 'Live Trading Session': { + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date((new Date()).valueOf() + ONE_YEAR_IN_MILISECONDS) + break + } + case 'Fordward Testing Session': { + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date((new Date()).valueOf() + ONE_YEAR_IN_MILISECONDS) + break + } + case 'Paper Trading Session': { + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date((new Date()).valueOf() + ONE_YEAR_IN_MILISECONDS) + break + } + } + + let tradingSystem = bot.TRADING_SYSTEM + + if (tradingSystem !== undefined) { + + /* Applying Trading System Level Parameters */ + if (tradingSystem.parameters !== undefined) { + + /* Base Asset and Initial Balances. */ + { + if (tradingSystem.parameters.baseAsset !== undefined) { + if (tradingSystem.parameters.baseAsset.referenceParent !== undefined) { + if (tradingSystem.parameters.baseAsset.referenceParent.referenceParent !== undefined) { + + let code = tradingSystem.parameters.baseAsset.referenceParent.referenceParent.code + + if (code.codeName !== undefined) { + bot.VALUES_TO_USE.baseAsset = code.codeName; + } + + code = tradingSystem.parameters.baseAsset.code + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (code.initialBalance !== undefined) { + bot.VALUES_TO_USE.initialBalanceA = code.initialBalance; + bot.VALUES_TO_USE.initialBalanceB = 0 + } + if (code.minimumBalance !== undefined) { + bot.VALUES_TO_USE.minimumBalanceA = code.minimumBalance; + bot.VALUES_TO_USE.minimumBalanceB = 0 + } + if (code.maximumBalance !== undefined) { + bot.VALUES_TO_USE.maximumBalanceA = code.maximumBalance; + bot.VALUES_TO_USE.maximumBalanceB = 0 + } + } else { + if (code.initialBalance !== undefined) { + bot.VALUES_TO_USE.initialBalanceB = code.initialBalance; + bot.VALUES_TO_USE.initialBalanceA = 0 + } + if (code.minimumBalance !== undefined) { + bot.VALUES_TO_USE.minimumBalanceB = code.minimumBalance; + bot.VALUES_TO_USE.minimumBalanceA = 0 + } + if (code.maximumBalance !== undefined) { + bot.VALUES_TO_USE.maximumBalanceB = code.maximumBalance; + bot.VALUES_TO_USE.maximumBalanceA = 0 + } + } + } + } + } + } + + /* Time Frame */ + if (tradingSystem.parameters.timeFrame !== undefined) { + bot.VALUES_TO_USE.timeFrame = tradingSystem.parameters.timeFrame.code.value + } + + /* Slippage */ + if (tradingSystem.parameters.slippage !== undefined) { + if (tradingSystem.parameters.slippage.code !== undefined) { + + let code = tradingSystem.parameters.slippage.code + + if (code.positionRate !== undefined) { + bot.VALUES_TO_USE.slippage.positionRate = code.positionRate + } + if (code.stopLoss !== undefined) { + bot.VALUES_TO_USE.slippage.stopLoss = code.stopLoss + } + if (code.takeProfit !== undefined) { + bot.VALUES_TO_USE.slippage.takeProfit = code.takeProfit + } + } + } + + /* Fee Structure */ + if (tradingSystem.parameters.feeStructure !== undefined) { + if (tradingSystem.parameters.feeStructure.code !== undefined) { + + let code = tradingSystem.parameters.feeStructure.code + + if (code.maker !== undefined) { + bot.VALUES_TO_USE.feeStructure.maker = code.maker + } + if (code.taker !== undefined) { + bot.VALUES_TO_USE.feeStructure.taker = code.taker + } + } + } + + /* Time Range */ + if (tradingSystem.parameters.timeRange !== undefined) { + if (tradingSystem.parameters.timeRange.code !== undefined) { + + let code = tradingSystem.parameters.timeRange.code + if (code.initialDatetime !== undefined) { + if (isNaN(Date.parse(code.initialDatetime)) === true) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> runSession -> setValuesToUse -> Cannot use initialDatatime provided at Trading System Parameters because it is not a valid Date."); + } else { + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date(code.initialDatetime) + } + } + if (code.finalDatetime !== undefined) { + if (isNaN(Date.parse(code.finalDatetime)) === true) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> runSession -> setValuesToUse -> Cannot use finalDatetime provided at Trading System Parameters because it is not a valid Date."); + } else { + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date(code.finalDatetime) + } + } + } + } + } + + /* Applying Session Level Parameters */ + if (bot.SESSION !== undefined) { + if (bot.SESSION.parameters !== undefined) { + + /* Base Asset and Initial Balances. */ + { + if (bot.SESSION.parameters.baseAsset !== undefined) { + if (bot.SESSION.parameters.baseAsset.referenceParent !== undefined) { + if (bot.SESSION.parameters.baseAsset.referenceParent.referenceParent !== undefined) { + + let code = bot.SESSION.parameters.baseAsset.referenceParent.referenceParent.code + + if (code.codeName !== undefined) { + bot.VALUES_TO_USE.baseAsset = code.codeName; + } + + code = bot.SESSION.parameters.baseAsset.code + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (code.initialBalance !== undefined) { + bot.VALUES_TO_USE.initialBalanceA = code.initialBalance; + bot.VALUES_TO_USE.initialBalanceB = 0 + } + if (code.minimumBalance !== undefined) { + bot.VALUES_TO_USE.minimumBalanceA = code.minimumBalance; + bot.VALUES_TO_USE.minimumBalanceB = 0 + } + if (code.maximumBalance !== undefined) { + bot.VALUES_TO_USE.maximumBalanceA = code.maximumBalance; + bot.VALUES_TO_USE.maximumBalanceB = 0 + } + } else { + if (code.initialBalance !== undefined) { + bot.VALUES_TO_USE.initialBalanceB = code.initialBalance; + bot.VALUES_TO_USE.initialBalanceA = 0 + } + if (code.minimumBalance !== undefined) { + bot.VALUES_TO_USE.minimumBalanceB = code.minimumBalance; + bot.VALUES_TO_USE.minimumBalanceA = 0 + } + if (code.maximumBalance !== undefined) { + bot.VALUES_TO_USE.maximumBalanceB = code.maximumBalance; + bot.VALUES_TO_USE.maximumBalanceA = 0 + } + } + } + } + } + } + + /* Time Frame */ + if (bot.SESSION.parameters.timeFrame !== undefined) { + bot.VALUES_TO_USE.timeFrame = bot.SESSION.parameters.timeFrame.code.value + } + + /* Slippage */ + if (bot.SESSION.parameters.slippage !== undefined) { + if (bot.SESSION.parameters.slippage.code !== undefined) { + + let code = bot.SESSION.parameters.slippage.code + + if (code.positionRate !== undefined) { + bot.VALUES_TO_USE.slippage.positionRate = code.positionRate + } + if (code.stopLoss !== undefined) { + bot.VALUES_TO_USE.slippage.stopLoss = code.stopLoss + } + if (code.takeProfit !== undefined) { + bot.VALUES_TO_USE.slippage.takeProfit = code.takeProfit + } + } + } + + /* Fee Structure */ + if (bot.SESSION.parameters.feeStructure !== undefined) { + if (bot.SESSION.parameters.feeStructure.code !== undefined) { + + let code = bot.SESSION.parameters.feeStructure.code + + if (code.maker !== undefined) { + bot.VALUES_TO_USE.feeStructure.maker = code.maker + } + if (code.taker !== undefined) { + bot.VALUES_TO_USE.feeStructure.taker = code.taker + } + } + } + + /* Time Range */ + if (bot.SESSION.parameters.timeRange !== undefined) { + if (bot.SESSION.parameters.timeRange.code !== undefined) { + + let code = bot.SESSION.parameters.timeRange.code + if (code.initialDatetime !== undefined) { + if (isNaN(Date.parse(code.initialDatetime)) === true) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> runSession -> setValuesToUse -> Cannot use initialDatatime provided at Session Parameters because it is not a valid Date."); + } else { + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date(code.initialDatetime) + } + } + if (code.finalDatetime !== undefined) { + if (isNaN(Date.parse(code.finalDatetime)) === true) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> runSession -> setValuesToUse -> Cannot use finalDatetime provided at Session Parameters because it is not a valid Date."); + } else { + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date(code.finalDatetime) + } + } + } + } + } + } + } + } + + function setUpSocialBots() { + + if (bot.SESSION.socialBots !== undefined) { + if (bot.SESSION.socialBots.bots !== undefined) { + for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) { + let socialBot = bot.SESSION.socialBots.bots[i] + if (socialBot.type === "Telegram Bot") { + let code = socialBot.code + socialBot.botInstance = setUpTelegramBot(code.botToken, code.chatId) + } + } + } + + function announce(announcement) { + if (bot.SESSION.socialBots.bots !== undefined) { + for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) { + let socialBot = bot.SESSION.socialBots.bots[i] + try { + let code = JSON.parse(announcement.code) + if (socialBot.id === announcement.referenceParent.id) { + if (socialBot.type === "Telegram Bot") { + if (announcement.formulaValue !== undefined) { + socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err)) + } else { + socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err)) + } + } + } + } catch (err) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> err = " + err.stack); + } + } + } + } + + bot.SESSION.socialBots.announce = announce + } + } + + function setUpTelegramBot(botToken, chatId) { + /* Telegram Bot Initialization */ + try { + const Telegraf = require('telegraf') + let telegramBot + telegramBot = new Telegraf(botToken) + telegramBot.start((ctx) => ctx.reply('Hi! I am a Telegram Bot powered by Superalgos')) + telegramBot.help((ctx) => ctx.reply('Send me a sticker')) + telegramBot.on('sticker', (ctx) => ctx.reply('??')) + telegramBot.hears('hi', (ctx) => ctx.reply('Hey there')) + telegramBot.launch() + + const Telegram = require('telegraf/telegram') + let telegramAPI + telegramAPI = new Telegram(botToken) + telegramAPI.sendMessage(chatId, bot.SESSION.type + " '" + bot.SESSION.name + "' was Started.").catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpTelegramBot -> Telegram API error -> err = " + err)) + + let botInstance = { + telegramBot: telegramBot, + telegramAPI: telegramAPI, + chatId: chatId + } + return botInstance + } catch (err) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpTelegramBot -> err = " + err.stack); + } + + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/StatusDependencies.js b/TaskServer/StatusDependencies.js new file mode 100644 index 000000000..176188c16 --- /dev/null +++ b/TaskServer/StatusDependencies.js @@ -0,0 +1,168 @@ +exports.newStatusDependencies = function newStatusDependencies(BOT, logger, STATUS_REPORT, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const MODULE_NAME = "Status Dependencies"; + + let bot = BOT + + let thisObject = { + nodeArray: undefined, + statusReports: new Map(), + reportsByMainUtility: new Map(), + initialize: initialize, + keys: [] + }; + + return thisObject; + + function initialize(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + /* Basic Valdidations */ + if (bot.processNode.referenceParent.processDependencies !== undefined) { + if (bot.processNode.referenceParent.processDependencies.statusDependencies !== undefined) { + thisObject.nodeArray = bot.processNode.referenceParent.processDependencies.statusDependencies + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have status dependencies at all."); + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not status dependencies."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + return + } + + /*For each dependency we will initialize the status report, and load it as part of this initialization process.*/ + + let alreadyCalledBack = false; + let loadCount = 0; + let dependenciesToProcess = [] + for (let i = 0; i < thisObject.nodeArray.length; i++) { + let statusDependency = { + dependency: thisObject.nodeArray[i] + } + + dependenciesToProcess.push(statusDependency) + + } + + for (let i = 0; i < dependenciesToProcess.length; i++) { + + let statusReportModule = STATUS_REPORT.newStatusReport(BOT, logger, UTILITIES); + + logger.write(MODULE_NAME, "[INFO] initialize -> onInitilized -> Initializing Status Report # " + (i + 1)); + let statusDependency = dependenciesToProcess[i] + statusReportModule.initialize(statusDependency.dependency, onInitilized); + + function onInitilized(err) { + + logger.write(MODULE_NAME, "[INFO] initialize -> onInitilized -> Initialized Status Report # " + (i + 1)); + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + if (alreadyCalledBack === false) { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> err.message = " + err.message); + alreadyCalledBack = true; + callBackFunction(err); + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] initialize -> Can not call back because I already did."); } + } + return; + } + + logger.write(MODULE_NAME, "[INFO] initialize -> onInitilized -> Loading Status Report # " + (i + 1)); + statusReportModule.load(onLoad); + } + + function onLoad(err) { + + try { + logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Loaded Status Report # " + (i + 1)); + statusReportModule.status = err.message; + + switch (err.message) { + case global.DEFAULT_OK_RESPONSE.message: { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> Status Dependency = " + JSON.stringify(statusDependency.dependency)) } + + addReport(); + return; + } + case "Status Report was never created.": { + + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack); + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(statusDependency.dependency)) + + addReport(); + return; + } + + case "Status Report is corrupt.": { + + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack); + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(statusDependency.dependency)) + + addReport(); + return; + } + default: + { + logger.write(MODULE_NAME, "[ERROR] initialize -> onLoad -> Operation Failed."); + + if (alreadyCalledBack === false) { + alreadyCalledBack = true; + callBackFunction(err); + } + return; + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> onLoad -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function addReport() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Entering function."); } + logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Adding Status Report # " + (i + 1)); + + loadCount++; + logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Total Added = " + loadCount); + + let key = statusDependency.dependency.dataMine + "-" + statusDependency.dependency.bot + "-" + statusDependency.dependency.process + "-" + statusDependency.dependency.dataSetVersion; + + thisObject.keys.push(key); + thisObject.statusReports.set(key, statusReportModule); + + if (statusReportModule.mainUtility !== undefined) { + thisObject.reportsByMainUtility.set(statusReportModule.mainUtility, statusReportModule) + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Report added to Map. -> key = " + key); } + + if (loadCount === dependenciesToProcess.length) { + if (alreadyCalledBack === false) { + alreadyCalledBack = true + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] initialize -> addReport -> Can not call back because I already did."); } + } + } + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +}; diff --git a/TaskServer/StatusReport.js b/TaskServer/StatusReport.js new file mode 100644 index 000000000..d6cd7210e --- /dev/null +++ b/TaskServer/StatusReport.js @@ -0,0 +1,269 @@ +exports.newStatusReport = function newStatusReport(BOT, logger, UTILITIES) { + + /* + + A Status Report is a file that every bot reads at the begining of its execution and saves after it finishes its job. + The purpose of the file is to record checkpoint information of what was the last thing done by the bot and helpfull enough to start the next execution. + It usually does not include business related context data. + + */ + + const MODULE_NAME = "Status Report"; + + let bot = BOT; + + let thisObject = { + mainUtility: undefined, + file: undefined, // Here we have the JSON object representing the file content. + initialize: initialize, + load: load, + save: save, + status: undefined + }; + + let statusDependencyNode; + + /* Utilities needed. */ + + let utilities = UTILITIES.newCloudUtilities(logger); + + /* Storage account to be used here. */ + + const FILE_STORAGE = require('./FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + let sessionPath = '' + + return thisObject; + + function initialize(pStatusDependencyNode, callBackFunction) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + statusDependencyNode = pStatusDependencyNode; + logger.fileName = MODULE_NAME + "." + statusDependencyNode.type + "." + statusDependencyNode.name + "." + statusDependencyNode.id; + + /* Some very basic validations that we have all the information needed. */ + if (statusDependencyNode.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Status Dependency without Reference Parent. Status Dependency = " + JSON.stringify(statusDependencyNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (statusDependencyNode.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Status Report not attached to a Process Definition. Status Report = " + JSON.stringify(statusDependencyNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (statusDependencyNode.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (statusDependencyNode.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition not attached to a Bot. Process Definition = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (statusDependencyNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (statusDependencyNode.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Data Mine. Bot = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (statusDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Data Mine witn no codeName defined. Data Mine = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* Simplifying the access to basic info */ + statusDependencyNode.bot = statusDependencyNode.referenceParent.parentNode.parentNode.code.codeName + statusDependencyNode.process = statusDependencyNode.referenceParent.parentNode.code.codeName + statusDependencyNode.bottype = statusDependencyNode.referenceParent.parentNode.parentNode.type + statusDependencyNode.dataMine = statusDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* Let's see if the process is run monthly or not. */ + if (statusDependencyNode.referenceParent.parentNode.code.startMode !== undefined) { + if (statusDependencyNode.referenceParent.parentNode.code.startMode.allMonths !== undefined) { + if (statusDependencyNode.referenceParent.parentNode.code.startMode.oneMonth !== undefined) { + if (statusDependencyNode.referenceParent.parentNode.code.startMode.allMonths.run === "true" || statusDependencyNode.referenceParent.parentNode.code.startMode.oneMonth.run === "true") { + statusDependencyNode.processRunMonthly = true + } + } + } + } + + /* We retrieve the report main utility */ + if (statusDependencyNode.code !== undefined) { + if (statusDependencyNode.code.mainUtility !== undefined) { + thisObject.mainUtility = statusDependencyNode.code.mainUtility + } + } + + /* This stuff is still hardcoded and unresolved. */ + statusDependencyNode.botVersion = { + "major": 1, + "minor": 0 + } + statusDependencyNode.dataSetVersion = "dataSet.V1" + + if (bot.SESSION !== undefined && statusDependencyNode.bottype === "Trading Bot") { + sessionPath = bot.SESSION.folderName + "/" + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function load(callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] load -> Entering function."); } + + let fileName = "Status.Report." + bot.market.baseAsset + '_' + bot.market.quotedAsset + ".json"; + let filePath; + + let ownerId = statusDependencyNode.dataMine + "-" + statusDependencyNode.bot + "-" + statusDependencyNode.botVersion.major + "-" + statusDependencyNode.botVersion.minor + "-" + statusDependencyNode.process + "-" + statusDependencyNode.dataSetVersion; + let botId = bot.dataMine + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.process + "-" + bot.dataSetVersion; + + if (ownerId !== botId) { + let rootPath = statusDependencyNode.dataMine + "/" + statusDependencyNode.bot + "/" + bot.exchange; + filePath = rootPath + "/Reports/" + sessionPath + statusDependencyNode.process ; + } else { + filePath = bot.filePathRoot + "/Reports/" + sessionPath + statusDependencyNode.process ; + } + + filePath += '/' + fileName + + fileStorage.getTextFile(filePath, onFileReceived); + + function onFileReceived(err, text) { + + if ( err.result === global.CUSTOM_FAIL_RESPONSE.result && (err.message === 'Folder does not exist.' || err.message === 'File does not exist.') + || err.code === "The specified key does not exist.") { + + logger.write(MODULE_NAME, "[INFO] load -> onFileReceived -> err = "+ err.stack); + + /* In this case we can assume that this is the first execution ever of this bot.*/ + + thisObject.file = JSON.parse('{}'); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Status Report was never created." + }; + logger.write(MODULE_NAME, "[WARN] load -> onFileReceived -> customOK = " + customOK.message); + callBackFunction(customOK); + return; + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] load -> onFileReceived -> err = "+ err.stack); + callBackFunction(err); + return; + } + + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { + logger.write(MODULE_NAME, "[INFO] load -> onFileReceived -> Content received = " + text); + } + + try { + + thisObject.file = JSON.parse(text); + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + + /* + It might happen that the file content is corrupt. We will consider this as a temporary situation, since sometimes the file + is being updated at the moment of the read. The bot can not run without a valid Status Report but we can request the platform to retry later. + */ + + logger.write(MODULE_NAME, "[ERROR] load -> onFileReceived -> Error Parsing the Status report. -> Err = " + err.message); + + let customFail = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: "Status Report is corrupt." + }; + logger.write(MODULE_NAME, "[WARN] load -> onFileReceived -> customFail = " + customFail.message); + callBackFunction(customFail); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] load -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function save(callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] save -> Entering function."); } + + let ownerId = statusDependencyNode.dataMine + "-" + statusDependencyNode.bot + "-" + statusDependencyNode.botVersion.major + "-" + statusDependencyNode.botVersion.minor + "-" + statusDependencyNode.process + "-" + statusDependencyNode.dataSetVersion; + let botId = bot.dataMine + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.process + "-" + bot.dataSetVersion; + + if (ownerId !== botId && statusDependencyNode.process !== "Context") { // Context is a special case where the report is created by the Context.js module itself. + + let customErr = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: "Only bots owners of a Status Report can save them." + }; + logger.write(MODULE_NAME, "[ERROR] save -> customErr = " + customErr.message); + callBackFunction(customErr); + return; + } + + let fileName = "Status.Report." + bot.market.baseAsset + '_' + bot.market.quotedAsset + ".json"; + let filePath = bot.filePathRoot + "/Reports/" + sessionPath + statusDependencyNode.process; + + filePath += '/' + fileName + let fileContent = JSON.stringify(thisObject.file); + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] save -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] save -> onFileCreated -> err = "+ err.stack); + callBackFunction(err); + return; + } + + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { + logger.write(MODULE_NAME, "[INFO] save -> onFileCreated -> Content written = " + fileContent); + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] save -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +}; diff --git a/TaskServer/SystemEventHandler.js b/TaskServer/SystemEventHandler.js new file mode 100644 index 000000000..f86270062 --- /dev/null +++ b/TaskServer/SystemEventHandler.js @@ -0,0 +1,179 @@ +exports.newSystemEventHandler = function newSystemEventHandler(ipc) { + let thisObject = { + initialize: initialize, + finalize: finalize, + createEventHandler: createEventHandler, + deleteEventHandler: deleteEventHandler, + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent + } + + let eventListeners = new Map() + let responseWaiters = new Map() + + ipc.config.id = 'Task Server'; + ipc.config.retry = 1500; + ipc.config.silent = true; + + return thisObject + + function initialize(canllerName, callBackFunction) { + + ipc.connectTo( + 'world', + function () { + ipc.of.world.on( + 'connect', + function () { + ipc.log('## Connected to Superalgos Event Server ##'.rainbow, ipc.config.delay); + + let eventCommand = { + action: 'greetings', + from: canllerName + } + + sendCommand(eventCommand) + + callBackFunction() + } + ); + ipc.of.world.on( + 'disconnect', + function () { + ipc.log('Disconnected from Superalgos Event Server '.notice); + } + ); + ipc.of.world.on( + 'message', // here is where all incomming messages are processed. + function (data) { + ipc.log('Got a message from Superalgos Event Server: '.debug, data); + + let message = JSON.parse(data) + + if (message.action === 'Event Raised') { + + let key + if (message.callerId) { + key = message.eventHandlerName + '-' + message.eventType + '-' + message.callerId + } else { + key = message.eventHandlerName + '-' + message.eventType + } + let handler = eventListeners.get(key) + if (handler) { + handler.callBack(message) + } else { + console.log(key + ' not found so could not deliver event raised.') + console.log(' Message = ' + data) + } + return + } + + if (message.action === 'Event Server Response') { + let handler = responseWaiters.get(message.callerId) + if (handler) { + handler(message) + } + return + } + } + ); + } + ); + } + + function finalize() { + /* Before disconnecting we will forze all eventListeners to stop listening. */ + const eventListenersArray = [...eventListeners.values()] + for (let i = 0; i < eventListenersArray.length; i++) { + let handler = eventListenersArray[i] + let eventCommand = { + action: 'stopListening', + eventHandlerName: handler.eventHandlerName, + eventType: handler.eventType, + callerId: handler.callerId + } + sendCommand(eventCommand) + } + ipc.disconnect('world'); + } + + function sendCommand(command, responseCallBack, eventsCallBack) { + + if (command.action === 'listenToEvent') { + let key + if (command.callerId) { + key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId + } else { + key = command.eventHandlerName + '-' + command.eventType + } + let handler = { + eventHandlerName: command.eventHandlerName, + eventType: command.eventType, + callerId: command.callerId, + callBack: eventsCallBack + } + eventListeners.set(key, handler) + } + if (command.callerId && responseCallBack) { + responseWaiters.set(command.callerId, responseCallBack) + } + + ipc.of.world.emit( + 'message', + JSON.stringify(command) + ) + } + + function createEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'createEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function deleteEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'deleteEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function listenToEvent(eventHandlerName, eventType, extraData, callerId, responseCallBack, eventsCallBack) { + let eventCommand = { + action: 'listenToEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + extraData: extraData, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack, eventsCallBack) + } + + function stopListening(eventHandlerName, eventType, eventSubscriptionId, callerId, responseCallBack) { + /* User either needs to specify a valid eventSubscriptionId OR the 3 params: eventHandlerName, eventType, callerId which were used when start listening to events. */ + let eventCommand = { + action: 'stopListening', + eventHandlerName: eventHandlerName, + eventType: eventType, + eventSubscriptionId: eventSubscriptionId, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function raiseEvent(eventHandlerName, eventType, event, callerId, responseCallBack) { + let eventCommand = { + action: 'raiseEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + event: event, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } +} \ No newline at end of file diff --git a/TaskServer/TradingBot.js b/TaskServer/TradingBot.js new file mode 100644 index 000000000..3129df6d7 --- /dev/null +++ b/TaskServer/TradingBot.js @@ -0,0 +1,973 @@ +exports.newTradingBot = function newTradingBot(bot, logger, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "Trading Bot"; + + let thisObject = { + initialize: initialize, + finalize: finalize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + const COMMONS = require('./Commons.js'); + let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE); + + let exchangeAPI; + + return thisObject; + + function finalize() { + thisObject = undefined + utilities = undefined + fileStorage = undefined + commons = undefined + } + + function initialize(pExchangeAPI, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + exchangeAPI = pExchangeAPI; + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(multiPeriodDataFiles, timeFrame, timeFrameLabel, currentDay, interExecutionMemory, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let chart = {} + let mainDependency = {} + let processingDailyFiles + + if (currentDay !== undefined) { + processingDailyFiles = true + } else { + processingDailyFiles = false + } + + /* The first phase here is about checking that we have everything we need at the definition level. */ + let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies + if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) {return} + + let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets + if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return } + + /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */ + for (let j = 0; j < global.marketFilesPeriods.length; j++) { + + let timeFrameLabel = marketFilesPeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(timeFrameLabel) + let products = {} + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Inflating Data Files for timeFrame = " + timeFrameLabel); } + + if (dataFiles !== undefined) { + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timeFrame) + + let propertyName = 'at' + timeFrameLabel.replace('-', ''); + chart[propertyName] = products + } + } + + for (let j = 0; j < global.dailyFilePeriods.length; j++) { + + let timeFrameLabel = global.dailyFilePeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(timeFrameLabel) + let products = {} + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Inflating Data Files for timeFrame = " + timeFrameLabel); } + + if (dataFiles !== undefined) { + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timeFrame) + + let propertyName = 'at' + timeFrameLabel.replace('-', ''); + chart[propertyName] = products + } + } + + /* From here we have more or less the same we used to have at JASON */ + const TRADING_SIMULATION = require('./TradingSimulation.js'); + let tradingSimulation = TRADING_SIMULATION.newTradingSimulation(bot, logger, UTILITIES); + + let market = bot.market; + + const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation"; + const CONDITIONS_FOLDER_NAME = "Simulation-Conditions"; + const STRATEGIES_FOLDER_NAME = "Simulation-Strategies"; + const TRADES_FOLDER_NAME = "Simulation-Trades"; + + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + let recordsArray + let conditionsArray + let strategiesArray + let tradesArray + + let tradingSystem = {}; + + tradingSimulation.runSimulation( + chart, + dataDependencies, + timeFrame, + timeFrameLabel, + currentDay, + interExecutionMemory, + exchangeAPI, + writeFiles, + callBackFunction) + + function writeFiles(pTradingSystem, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray) { + + tradingSystem = pTradingSystem + recordsArray = pRecordsArray + conditionsArray = pConditionsArray + strategiesArray = pStrategiesArray + tradesArray = pTradesArray + + if (timeFrame > global.dailyFilePeriods[0][0]) { + writeMarketFiles() + } else { + writeDailyFiles() + } + } + + function writeMarketFiles() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); } + + writeRecordsFile(); + + function writeRecordsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < recordsArray.length; i++) { + + let record = recordsArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.type + "," + + record.marketRate + "," + + record.amount + "," + + record.balanceA + "," + + record.balanceB + "," + + record.profit + "," + + record.lastTradeProfitLoss + "," + + record.stopLoss + "," + + record.roundtrips + "," + + record.hits + "," + + record.fails + "," + + record.hitRatio + "," + + record.ROI + "," + + record.periods + "," + + record.days + "," + + record.anualizedRateOfReturn + "," + + record.positionRate + "," + + record.lastTradeROI + "," + + record.strategy + "," + + record.strategyStageNumber + "," + + record.takeProfit + "," + + record.stopLossPhase + "," + + record.takeProfitPhase + "," + + JSON.stringify(record.executionRecord) + "," + + record.positionSize + "," + + record.initialBalanceA + "," + + record.minimumBalanceA + "," + + record.maximumBalanceA + "," + + record.initialBalanceB + "," + + record.minimumBalanceB + "," + + record.maximumBalanceB + "," + + record.baseAsset + "," + + record.positionPeriods + "," + + record.positionDays + "," + + record.distanceToLastTriggerOn + "," + + record.distanceToLastTriggerOff + "," + + record.distanceToLastTakePosition + "," + + record.distanceToLastClosePosition + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrameLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writeConditionsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeConditionsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < conditionsArray.length; i++) { + + let record = conditionsArray[i]; + + let conditions = ""; + let conditionsSeparator = ""; + + for (let j = 0; j < record.length - 3; j++) { + conditions = conditions + conditionsSeparator + record[j]; + if (conditionsSeparator === "") { conditionsSeparator = ","; } + } + + conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues. + + fileContent = fileContent + separator + '[' + conditions + ']'; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + fileContent = "[" + JSON.stringify(tradingSystem) + "," + fileContent + "]"; + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrameLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writeStrategiesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStrategiesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < strategiesArray.length; i++) { + let record = strategiesArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.number + "," + + record.beginRate + "," + + record.endRate + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrameLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writeTradesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeTradesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < tradesArray.length; i++) { + + let record = tradesArray[i]; + if (record.stopRate === undefined) { record.stopRate = 0 } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.lastTradeROI + "," + + record.beginRate + "," + + record.endRate + "," + + record.exitType + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrameLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + /* Emit event that signals that simulation files were updated */ + global.SYSTEM_EVENT_HANDLER.raiseEvent('Jason-Multi-Period', 'Simulation Files Updated') + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + + function writeDailyFiles() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); } + + writeRecordsFile(); + + /* TODO : NEXT function is a work in progress, not used today */ + function writeFinalResults() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> Entering function."); } + + if ( // We only write this file if we are at the head of the market. + Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS + !== + Math.trunc((new Date()).valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS + ) { + writeRecordsFile(); + return; + } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < recordsArray.length; i++) { + + let record = recordsArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.type + "," + + record.marketRate + "," + + record.amount + "," + + record.balanceA + "," + + record.balanceB + "," + + record.profit + "," + + record.lastTradeProfitLoss + "," + + record.stopLoss + "," + + record.roundtrips + "," + + record.hits + "," + + record.fails + "," + + record.hitRatio + "," + + record.ROI + "," + + record.periods + "," + + record.days + "," + + record.anualizedRateOfReturn + "," + + record.positionRate + "," + + record.lastTradeROI + "," + + record.strategy + "," + + record.strategyStageNumber + "," + + record.takeProfit + "," + + record.stopLossPhase + "," + + record.takeProfitPhase + "," + + JSON.stringify(record.executionRecord) + "," + + record.positionSize + "," + + record.initialBalanceA + "," + + record.minimumBalanceA + "," + + record.maximumBalanceA + "," + + record.initialBalanceB + "," + + record.minimumBalanceB + "," + + record.maximumBalanceB + "," + + record.baseAsset + "," + + record.positionPeriods + "," + + record.positionDays + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timeFrameLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writeRecordsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeRecordsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < recordsArray.length; i++) { + + let record = recordsArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.type + "," + + record.marketRate + "," + + record.amount + "," + + record.balanceA + "," + + record.balanceB + "," + + record.profit + "," + + record.lastTradeProfitLoss + "," + + record.stopLoss + "," + + record.roundtrips + "," + + record.hits + "," + + record.fails + "," + + record.hitRatio + "," + + record.ROI + "," + + record.periods + "," + + record.days + "," + + record.anualizedRateOfReturn + "," + + record.positionRate + "," + + record.lastTradeROI + "," + + record.strategy + "," + + record.strategyStageNumber + "," + + record.takeProfit + "," + + record.stopLossPhase + "," + + record.takeProfitPhase + "," + + JSON.stringify(record.executionRecord) + "," + + record.positionSize + "," + + record.initialBalanceA + "," + + record.minimumBalanceA + "," + + record.maximumBalanceA + "," + + record.initialBalanceB + "," + + record.minimumBalanceB + "," + + record.maximumBalanceB + "," + + record.baseAsset + "," + + record.positionPeriods + "," + + record.positionDays + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timeFrameLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writeConditionsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeConditionsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < conditionsArray.length; i++) { + + let record = conditionsArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + let conditions = ""; + let conditionsSeparator = ""; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + for (let j = 0; j < record.length - 3; j++) { + conditions = conditions + conditionsSeparator + record[j]; + if (conditionsSeparator === "") { conditionsSeparator = ","; } + } + + conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues. + + fileContent = fileContent + separator + '[' + conditions + ']'; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + fileContent = "[" + JSON.stringify(tradingSystem) + "," + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timeFrameLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writeStrategiesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStrategiesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < strategiesArray.length; i++) { + let record = strategiesArray[i]; + + /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends + at the current date. */ + + if (record.end < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.number + "," + + record.beginRate + "," + + record.endRate + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timeFrameLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + writeTradesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeTradesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < tradesArray.length; i++) { + + let record = tradesArray[i]; + + /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends + at the current date. */ + + if (record.end < currentDay.valueOf()) { continue; } + if (record.stopRate === undefined) { record.stopRate = 0 } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.lastTradeROI + "," + + record.beginRate + "," + + record.endRate + "," + + record.exitType + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.baseAsset + '_' + market.quotedAsset + '.json'; + + let filePathRoot = bot.dataMine + "/" + bot.codeName + "/" + bot.exchange; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timeFrameLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset); + + callBackFunction(err); + return; + + } + + /* Emit event that signals that simulation files were updated */ + global.SYSTEM_EVENT_HANDLER.raiseEvent('Jason-Multi-Period', 'Simulation Files Updated') + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; + diff --git a/TaskServer/TradingBotProcessMainLoop.js b/TaskServer/TradingBotProcessMainLoop.js new file mode 100644 index 000000000..85ae4182d --- /dev/null +++ b/TaskServer/TradingBotProcessMainLoop.js @@ -0,0 +1,1121 @@ +exports.newTradingBotProcessMainLoop = function newTradingBotProcessMainLoop(bot, parentLogger) { + + const ROOT_DIR = './'; + + const MODULE_NAME = "Trading Process Main Loop"; + const FULL_LOG = true; + + let USER_BOT_MODULE; + let USER_BOT_COMMONS; + + const MULTI_PERIOD_MARKET = require(ROOT_DIR + 'MultiPeriodMarket'); + const MULTI_PERIOD_DAILY = require(ROOT_DIR + 'MultiPeriodDaily'); + const MULTI_PERIOD = require(ROOT_DIR + 'MultiPeriod'); + const FILE_STORAGE = require('./FileStorage.js'); + const SESSION = require(ROOT_DIR + 'Session'); + + let fileStorage = FILE_STORAGE.newFileStorage(parentLogger); + let session = SESSION.newSession(bot, parentLogger) + + const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog'); + let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble. + + let nextLoopTimeoutHandle; + let checkLoopHealthHandle; + + let thisObject = { + initialize: initialize, + run: run + }; + + let processConfig; + + return thisObject; + + function initialize(pProcessConfig, callBackFunction) { + + /* This function is exactly the same in the 3 modules representing the 2 different bot types loops. */ + + try { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + processConfig = pProcessConfig; + + if (bot.definedByUI === true) { + /* The code of the bot is defined at the UI. No need to load a file with the code. */ + session.initialize(onSessionInitialized) + + function onSessionInitialized(err) { + callBackFunction(err); + } + return + } + + bot.resumeExecution = processConfig.framework.startDate.resumeExecution // We are inherating this from root, but from here we need it at bot + + let filePath = bot.dataMine + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName + filePath += "/User.Bot.js" + + fileStorage.getTextFile(filePath, onBotDownloaded); + + function onBotDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message); + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + USER_BOT_MODULE = {} + //USER_BOT_MODULE.newUserBot = require(process.env.BOTS_PATH + '/aamasters/AAMasters/bots/AAJason-Trading-Engine-Bot/Multi-Period/User.Bot').newUserBot // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. + USER_BOT_MODULE.newUserBot = eval(text); // Use this for production + + filePath = bot.dataMine + "/" + "bots" + "/" + bot.repo; + filePath += "/Commons.js" + + fileStorage.getTextFile(filePath, onCommonsDownloaded); + + function onCommonsDownloaded(err, text) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.code || err.message); + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + + USER_BOT_COMMONS = {} + //USER_BOT_COMMONS.newCommons = require(process.env.BOTS_PATH + '/aamasters/AAMasters/bots/AAJason-Trading-Engine-Bot/Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. + USER_BOT_COMMONS.newCommons = eval(text); // Use this for production + + session.initialize(onSessionInitialized) + + function onSessionInitialized(err) { + callBackFunction(err); + } + } + } + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function run(callBackFunction) { + + try { + + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] run -> Entering function."); } + + /* Some initial values*/ + bot.enableCheckLoopHealth = true; + let fixedTimeLoopIntervalHandle; + bot.STOP_SESSION = true; + + loop(); + + function loop() { + + try { + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } + + /* For each loop we want to create a new log file. */ + + logger = DEBUG_MODULE.newDebugLog(); + logger.bot = bot; + logger.initialize(); + + bot.loopCounter++; + bot.loopStartTime = new Date().valueOf(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Entering function."); } + + let nextWaitTime; + + /* Heartbeats sent to the UI */ + bot.sessionHeartBeat = sessionHeartBeat + bot.processHeartBeat = processHeartBeat + processHeartBeat() + + /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */ + + const UTILITIES = require(ROOT_DIR + 'CloudUtilities'); + const EXCHANGE_API = require(ROOT_DIR + 'ExchangeAPI'); + const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); + const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); + const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); + const DATA_SET = require(ROOT_DIR + 'DataSet'); + const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput'); + + /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ + + bot.processDatetime = new Date(); // This will be considered the process date and time, so as to have it consistenly all over the execution. + + /* High level log entry */ + + console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + pad(bot.exchange, 20) + " " + pad(bot.market.baseAsset + '/' + bot.market.quotedAsset, 10) + + " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8) + " " + bot.sessionKey ) + + /* Checking if we need to need to emit any event */ + + if (bot.SESSION_STATUS === 'Idle' && bot.STOP_SESSION === false) { + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.sessionKey, 'Running') + bot.SESSION_STATUS = 'Running' + } + + if (bot.SESSION_STATUS === 'Running' && bot.STOP_SESSION === true) { + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.sessionKey, 'Stopped') + bot.SESSION_STATUS = 'Stopped' + } + + /* Checking if we should process this loop or not.*/ + if (bot.STOP_SESSION === true) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be ran."); } + + console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + + " Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be ran. "); + + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return + } + + /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + + let processOutput + let processExecutionEvents + let userBot; + let processFramework; + let statusDependencies; + let dataDependencies; + + initializeProcessExecutionEvents(); + + function initializeProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } + + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) + + processExecutionEvents.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well."); + startProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.start(onStarted); + + function onStarted(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well."); + initializeStatusDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeStatusDependencies() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> Entering function."); } + + statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES); + + statusDependencies.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well."); + initializeDataDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeStatusDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + sessionStopped() + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeDataDependencies() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> Entering function."); } + + dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET); + + dataDependencies.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); } + initializeExchangeAPI(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeDataDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + sessionStopped() + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function initializeExchangeAPI() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> Entering function."); } + + exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, bot); + + exchangeAPI.initialize(onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> onInizialized -> Execution finished well."); } + + switch (processConfig.framework.name) { + case 'Multi-Period': { + processFramework = MULTI_PERIOD.newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS); + intitializeProcessFramework(); + break; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Process Framework not Supported."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Process Framework Name = " + processConfig.framework.name); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeExchangeAPI -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + sessionStopped() + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function intitializeProcessFramework() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); } + + processFramework.initialize(processConfig, statusDependencies, dataDependencies, exchangeAPI, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Execution finished well."); + startProcessFramework(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> intitializeProcessFramework -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + sessionStopped() + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized > Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessFramework() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> Entering function."); } + + processFramework.start(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Entering function."); } + processFramework.finalize() + processFramework = undefined + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + raiseEventsProcessOutput() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + sessionStopped() + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Dependency does not exist.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency does not exist. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Dependency not ready.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency not ready. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled custom response received. -> err = " + err.message); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> err = "+ err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function raiseEventsProcessOutput() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); } + + processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger) + + processOutput.raiseEvents(statusDependencies, onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well."); + finishProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> raiseEventsProcessOutput -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function finishProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.finish(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function loopControl(nextWaitTime) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> nextWaitTime = " + nextWaitTime); } + + /* We show we reached the end of the loop. */ + + processHeartBeat() + + /* Here we check if we must stop the loop gracefully. */ + + shallWeStop(onStop, onContinue); + + function onStop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Entering function."); } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Stopping the Loop Gracefully. See you next time!"); } + + if (global.WRITE_LOGS_TO_FILES === 'true') { + logger.persist(); + } + + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.sessionKey, 'Stopped') + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + + } + + function onContinue() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onContinue -> Entering function."); } + + /* Indicator bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */ + + switch (nextWaitTime) { + case 'Normal': { + let waitTime + if (bot.startMode === "Live") { + waitTime = processConfig.liveWaitTime + } else { + waitTime = processConfig.normalWaitTime + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (waitTime / 1000) + " seconds."); } + if (processConfig.deadWaitTime > 0) { + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.deadWaitTime, bot.loopCounter); + } + nextLoopTimeoutHandle = setTimeout(loop, waitTime); + if (global.WRITE_LOGS_TO_FILES === 'true') { + logger.persist(); + } + } + break; + case 'Retry': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.retryWaitTime / 1000) + " seconds."); } + if (processConfig.deadWaitTime > 0) { + checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.deadWaitTime, bot.loopCounter); + } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.retryWaitTime); + logger.persist(); + } + break; + case 'Sleep': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.sleepWaitTime / 60000) + " minutes."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.sleepWaitTime); + logger.persist(); + } + break; + case 'Coma': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.comaWaitTime / 3600000) + " hours."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.comaWaitTime); + logger.persist(); + } + break; + } + } + } + + function checkLoopHealth(pLastLoop) { + + if (bot.enableCheckLoopHealth === false || global.STOP_TASK_GRACEFULLY === true) { + + logger.write(MODULE_NAME, "[WARN] run -> loop -> checkLoopHealth -> bot.enableCheckLoopHealth = " + bot.enableCheckLoopHealth); + + return; + } // This gets disabled anytime the Main Loop is shut down by any condition. + + if (bot.loopCounter <= pLastLoop + 1) { // This means that the next loop started but also stopped executing abruptally. + + let now = new Date().valueOf(); + + if (now - bot.loopStartTime > processConfig.deadWaitTime) { + + logger.write(MODULE_NAME, "[ERROR] run -> loop -> checkLoopHealth -> Dead loop found -> pLastLoop = " + pLastLoop); + console.log((new Date().toISOString() + " [ERROR] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found dead. Resurrecting it now.")); + + logger.persist(); // We persist the logs of the failed execution. + clearTimeout(nextLoopTimeoutHandle); // We cancel the timeout in case the original loop was still running and schedulled to reexecute. + loop(); // We restart the loop so that the processing can continue. + + } else { + + console.log((new Date().toISOString() + " [WARN] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found delayed but still alive. No action taken.")); + + } + } + } + + function shallWeStop(stopCallBack, continueCallBack) { + + try { + + /* IMPORTANT: This function is exactly the same on the 3 modules. */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> shallWeStop -> Entering function. "); } + + if (!global.STOP_TASK_GRACEFULLY) { + continueCallBack(); + } else { + stopCallBack(); + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = "+ err.stack); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function processHeartBeat(processingDate) { + let event = { + seconds: (new Date()).getSeconds(), + processingDate: processingDate + } + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.processKey, 'Heartbeat', event) + } + + function sessionHeartBeat(processingDate) { + let event = { + seconds: (new Date()).getSeconds(), + processingDate: processingDate + } + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.sessionKey, 'Heartbeat', event) + + if (global.STOP_TASK_GRACEFULLY === true) { + bot.STOP_SESSION = true + } + } + + function processStopped() { + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.processKey, 'Stopped') + } + + function sessionStopped() { + if (bot.SESSION_STATUS === 'Running') { + global.SYSTEM_EVENT_HANDLER.raiseEvent(bot.sessionKey, 'Stopped') + bot.SESSION_STATUS = 'Stopped' + } + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> err = "+ err.stack); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/TaskServer/TradingSimulation.js b/TaskServer/TradingSimulation.js new file mode 100644 index 000000000..06c48ab4a --- /dev/null +++ b/TaskServer/TradingSimulation.js @@ -0,0 +1,2874 @@ +exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "Trading Simulation -> " + bot.SESSION.name; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + const GMT_SECONDS = ':00.000 GMT+0000'; + + let thisObject = { + finalize: finalize, + runSimulation: runSimulation + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let percentageBandwidthAt = {} + let bollingerBandsAt = {} + let bollingerChannelsAt = {} + let bollingerSubChannelsAt = {} + let candlesAt = {} + + + return thisObject; + + function finalize() { + percentageBandwidthAt = undefined + bollingerBandsAt = undefined + bollingerChannelsAt = undefined + bollingerSubChannelsAt = undefined + candlesAt = undefined + thisObject = undefined + } + + function runSimulation( + chart, + dataDependencies, + timeFrame, + timeFrameLabel, + currentDay, + interExecutionMemory, + exchangeAPI, + callback, + callBackFunction) { + + try { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> Entering function."); } + let processingDailyFiles + if (timeFrame > global.dailyFilePeriods[0][0]) { + processingDailyFiles = false + } else { + processingDailyFiles = true + } + + let recordsArray = []; + let conditionsArray = []; + let strategiesArray = []; + let tradesArray = []; + + let tradingSystem = bot.TRADING_SYSTEM + + /* Initial Default Values */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.initialDatetime = " + bot.VALUES_TO_USE.timeRange.initialDatetime); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.finalDatetime = " + bot.VALUES_TO_USE.timeRange.finalDatetime); } + + let timerToCloseStage = 0 + + /* Stop Loss Management */ + + const MIN_STOP_LOSS_VALUE = 1 // We can not let the stop be zero to avoid division by 0 error or infinity numbers as a result. + const MAX_STOP_LOSS_VALUE = Number.MAX_SAFE_INTEGER + let stopLoss = 0; + + /* Take Profit Management */ + + const MIN_TAKE_PROFIT_VALUE = 1 // We can not let the buy order be zero to avoid division by 0 error or infinity numbers as a result. + const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER + let takeProfit = 0; + + /* Simulation Records */ + + let hitRatio = 0; + let ROI = 0; + let days = 0; + let positionDays = 0; + let anualizedRateOfReturn = 0; + let type = '""'; + let marketRate = 0; + let takePositionNow = false + let closePositionNow = false + + /* In some cases we need to know if we are positioned at the last candle of the calendar day, for that we need these variables. */ + + let lastInstantOfTheDay = 0 + if (currentDay) { + lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + } + + /* + The following counters need to survive multiple executions of the similator and keep themselves reliable. + This is challenging when the simulation is executed using Daily Files, since each execution means a new + day and these counters are meant to be kept along the whole market. + + To overcome this problem, we use the interExecutionMemory to record the values of the current execution + when finish. But there are a few details: + + 1. When the process is at the head of the market, it executes multple times at the same day. + 2. The same code serves execution from Market Files. + 3. In Daily Files we are receiving candles from the current day and previous day, so we need to take care of + not adding to the counters duplicate info when processing the day before candles. + + To overcome these challenges we record the values of the counters and variables on the interExecutionMemory only when + the day is complete and if we have a current Day. That menas that for Market Files we will never use + interExecutionMemory. + */ + + /*Needed for statistics */ + let previousBalanceBaseAsset = 0; + let previousBalanceQuotedAsset = 0; + + /* Position Management */ + let tradePositionRate = 0; + let tradePositionSize = 0; + + /* Strategy and Phase Management */ + let currentStrategyIndex = -1; + let strategyStage = 'No Stage'; + + let stopLossPhase = -1; + let stopLossStage = 'No Stage'; + + let takeProfitPhase = -1; + let takeProfitStage = 'No Stage'; + + /* These 2 objects will allow us to create separate files for each one of them. */ + + let currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + let currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + let balanceBaseAsset = bot.VALUES_TO_USE.initialBalanceA; + let balanceQuotedAsset = bot.VALUES_TO_USE.initialBalanceB; + + let lastTradeProfitLoss = 0; + let profit = 0; + let lastTradeROI = 0; + + let roundtrips = 0; + let fails = 0; + let hits = 0; + let periods = 0; + let positionPeriods = 0; + + let closeRate + + /* Usefull counters for conditions and formulas */ + + let distanceToLast = { + triggerOn: 0, + triggerOff: 0, + takePosition: 0, + closePosition: 0 + } + + /* Message to the Simulation Executor */ + + let orderId = 0; + let messageId = 0; + + let yesterday = {}; + + /* Initialization */ + + yesterday.stopLoss = 0 + yesterday.takeProfit = 0 + + yesterday.previousBalanceBaseAsset = previousBalanceBaseAsset + yesterday.previousBalanceQuotedAsset = previousBalanceQuotedAsset + + yesterday.tradePositionRate = tradePositionRate + yesterday.tradePositionSize = tradePositionSize + + yesterday.currentStrategyIndex = currentStrategyIndex + yesterday.strategyStage = strategyStage + + yesterday.stopLossPhase = stopLossPhase + yesterday.stopLossStage = stopLossStage + + yesterday.takeProfitPhase = takeProfitPhase + yesterday.takeProfitStage = takeProfitStage + + yesterday.currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + yesterday.currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + yesterday.balanceBaseAsset = balanceBaseAsset; + yesterday.balanceQuotedAsset = balanceQuotedAsset; + + yesterday.lastTradeProfitLoss = 0; + yesterday.profit = 0; + yesterday.lastTradeROI = 0; + + yesterday.roundtrips = 0; + yesterday.fails = 0; + yesterday.hits = 0; + yesterday.periods = 0; + yesterday.positionPeriods = 0; + + yesterday.distanceToLast = { + triggerOn: 0, + triggerOff: 0, + takePosition: 0, + closePosition: 0 + } + + yesterday.orderId = 0; + yesterday.messageId = 0; + + yesterday.hitRatio = 0; + yesterday.ROI = 0; + yesterday.anualizedRateOfReturn = 0; + + if (interExecutionMemory.roundtrips === undefined) { // This just means that the inter execution memory was never used before. + + /* Initialize the data structure we will use inter execution. */ + + interExecutionMemory.stopLoss = 0 + interExecutionMemory.takeProfit = 0 + + interExecutionMemory.previousBalanceBaseAsset = previousBalanceBaseAsset + interExecutionMemory.previousBalanceQuotedAsset = previousBalanceQuotedAsset + + interExecutionMemory.tradePositionRate = tradePositionRate + interExecutionMemory.tradePositionSize = tradePositionSize + + interExecutionMemory.currentStrategyIndex = currentStrategyIndex + interExecutionMemory.strategyStage = strategyStage + + interExecutionMemory.stopLossPhase = stopLossPhase + interExecutionMemory.stopLossStage = stopLossStage + + interExecutionMemory.takeProfitPhase = takeProfitPhase + interExecutionMemory.takeProfitStage = takeProfitStage + + interExecutionMemory.currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + interExecutionMemory.currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + interExecutionMemory.balanceBaseAsset = balanceBaseAsset; + interExecutionMemory.balanceQuotedAsset = balanceQuotedAsset; + + interExecutionMemory.lastTradeProfitLoss = lastTradeProfitLoss; + interExecutionMemory.profit = profit; + interExecutionMemory.lastTradeROI = lastTradeROI; + + interExecutionMemory.roundtrips = 0; + interExecutionMemory.fails = 0; + interExecutionMemory.hits = 0; + interExecutionMemory.periods = 0; + interExecutionMemory.positionPeriods = 0; + + interExecutionMemory.distanceToLast = { + triggerOn: 0, + triggerOff: 0, + takePosition: 0, + closePosition: 0 + } + + interExecutionMemory.orderId = 0; + interExecutionMemory.messageId = 0; + + interExecutionMemory.hitRatio = 0; + interExecutionMemory.ROI = 0; + interExecutionMemory.anualizedRateOfReturn = 0; + + interExecutionMemory.announcements = [] + + } else { + + /* We get the initial values from the day previous to the candles we receive at the current execution */ + + stopLoss = interExecutionMemory.stopLoss + takeProfit = interExecutionMemory.takeProfit + + previousBalanceBaseAsset = interExecutionMemory.previousBalanceBaseAsset + previousBalanceQuotedAsset = interExecutionMemory.previousBalanceQuotedAsset + + tradePositionRate = interExecutionMemory.tradePositionRate + tradePositionSize = interExecutionMemory.tradePositionSize + + currentStrategyIndex = interExecutionMemory.currentStrategyIndex + strategyStage = interExecutionMemory.strategyStage + + stopLossPhase = interExecutionMemory.stopLossPhase + stopLossStage = interExecutionMemory.stopLossStage + + takeProfitPhase = interExecutionMemory.takeProfitPhase + takeProfitStage = interExecutionMemory.takeProfitStage + + currentStrategy = { + begin: interExecutionMemory.currentStrategy.begin, + end: interExecutionMemory.currentStrategy.end, + status: interExecutionMemory.currentStrategy.status, + number: interExecutionMemory.currentStrategy.number, + beginRate: interExecutionMemory.currentStrategy.beginRate, + endRate: interExecutionMemory.currentStrategy.endRate + } + + currentTrade = { + begin: interExecutionMemory.currentTrade.begin, + end: interExecutionMemory.currentTrade.end, + status: interExecutionMemory.currentTrade.status, + profit: interExecutionMemory.currentTrade.profit, + exitType: interExecutionMemory.currentTrade.exitType, + beginRate: interExecutionMemory.currentTrade.beginRate, + endRate: interExecutionMemory.currentTrade.endRate + } + + if (currentDay) { + if (currentDay.valueOf() >= bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() + ONE_DAY_IN_MILISECONDS) { // Only after the first day we start grabbing the balance from this memory. + + balanceBaseAsset = interExecutionMemory.balanceBaseAsset; + balanceQuotedAsset = interExecutionMemory.balanceQuotedAsset; + + yesterday.balanceBaseAsset = balanceBaseAsset; + yesterday.balanceQuotedAsset = balanceQuotedAsset; + + } + } + + lastTradeProfitLoss = interExecutionMemory.lastTradeProfitLoss; + profit = interExecutionMemory.profit; + lastTradeROI = interExecutionMemory.lastTradeROI; + + roundtrips = interExecutionMemory.roundtrips; + fails = interExecutionMemory.fails; + hits = interExecutionMemory.hits; + periods = interExecutionMemory.periods; + positionPeriods = interExecutionMemory.positionPeriods; + + distanceToLast.triggerOn = interExecutionMemory.distanceToLast.triggerOn + distanceToLast.triggerOff = interExecutionMemory.distanceToLast.triggerOff + distanceToLast.takePosition = interExecutionMemory.distanceToLast.takePosition + distanceToLast.closePosition = interExecutionMemory.distanceToLast.closePosition + + orderId = interExecutionMemory.orderId; // to be deprecated + messageId = interExecutionMemory.messageId; // to be deprecated + + hitRatio = interExecutionMemory.hitRatio; + ROI = interExecutionMemory.ROI; + anualizedRateOfReturn = interExecutionMemory.anualizedRateOfReturn; + + /* For the case that any of these variables are not updated during the main loop, we need to store their value at the yesterday structure, otherwise it would be lost. */ + + yesterday.stopLoss = stopLoss + yesterday.takeProfit = takeProfit + + yesterday.previousBalanceBaseAsset = previousBalanceBaseAsset + yesterday.previousBalanceQuotedAsset = previousBalanceQuotedAsset + + yesterday.tradePositionRate = tradePositionRate + yesterday.tradePositionSize = tradePositionSize + + yesterday.currentStrategyIndex = currentStrategyIndex; + yesterday.strategyStage = strategyStage; + + yesterday.stopLossPhase = stopLossPhase; + yesterday.stopLossStage = stopLossStage; + + yesterday.takeProfitPhase = takeProfitPhase; + yesterday.takeProfitStage = takeProfitStage; + + yesterday.currentStrategy = { + begin: currentStrategy.begin, + end: currentStrategy.end, + status: currentStrategy.status, + number: currentStrategy.number, + beginRate: currentStrategy.beginRate, + endRate: currentStrategy.endRate + } + + yesterday.currentTrade = { + begin: currentTrade.begin, + end: currentTrade.end, + status: currentTrade.status, + profit: currentTrade.profit, + exitType: currentTrade.exitType, + beginRate: currentTrade.beginRate, + endRate: currentTrade.endRate + } + + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.profit = profit; + yesterday.lastTradeROI = lastTradeROI; + + yesterday.roundtrips = roundtrips; + yesterday.fails = fails; + yesterday.hits = hits; + yesterday.periods = periods; + yesterday.positionPeriods = positionPeriods; + + yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + yesterday.distanceToLast.takePosition = distanceToLast.takePosition + yesterday.distanceToLast.closePosition = distanceToLast.closePosition + + yesterday.hitRatio = hitRatio; + yesterday.ROI = ROI; + yesterday.anualizedRateOfReturn = anualizedRateOfReturn; + } + + /* Main Array and Maps */ + + let propertyName = 'at' + timeFrameLabel.replace('-', ''); + let candles = chart[propertyName].candles + let currentChart = chart[propertyName] + + /* Last Candle */ + let lastCandle = candles[candles.length - 1]; + + /* Main Simulation Loop: We go thourgh all the candles at this time period. */ + let currentCandleIndex + + /* For Loop Level heartbeat */ + let loopingDay + let previousLoopingDay + + initializeLoop() + + function initializeLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> initializeLoop -> Entering function."); } + + /* Estimate Initial Candle */ + + let firstEnd = candles[0].end + let targetEnd = bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() + let diff = targetEnd - firstEnd + let amount = diff / timeFrame + + currentCandleIndex = Math.trunc(amount) + if (currentCandleIndex < 0) { currentCandleIndex = 0 } + if (currentCandleIndex > candles.length - 1) { + /* This will happen when the bot.VALUES_TO_USE.timeRange.initialDatetime is beyond the last candle available, meaning that the dataSet needs to be updated with more up-to-date data. */ + currentCandleIndex = candles.length - 1 + } + + loop() + } + + function loop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + currentCandleIndex); } + + let announcementsToBeMade = [] + let candle = candles[currentCandleIndex]; + + /* Not processing while out of user-defined time range */ + + if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle before the bot.VALUES_TO_USE.timeRange.initialDatetime."); } + controlLoop(); + return + } + if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle after the bot.VALUES_TO_USE.timeRange.finalDatetime."); } + afterLoop(); + return + } + + /* Here we update at the chart data structure the objects for each product representing where we are currently standing at the simulation loop */ + if (processingDailyFiles) { + for (let j = 0; j < global.dailyFilePeriods.length; j++) { + + let mapKey = dailyFilePeriods[j][1] + let propertyName = 'at' + mapKey.replace('-', ''); + let thisChart = chart[propertyName] + + for (let k = 0; k < dataDependencies.length; k++) { + let dataDependencyNode = dataDependencies[k] + let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName + let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName + let currentElement = getElement(thisChart[pluralVariableName], candle, 'Daily' + '-' + mapKey + '-' + pluralVariableName) + thisChart[singularVariableName] = currentElement + } + } + } + + for (let j = 0; j < global.marketFilesPeriods.length; j++) { + + let mapKey = marketFilesPeriods[j][1] + let propertyName = 'at' + mapKey.replace('-', ''); + let thisChart = chart[propertyName] + + for (let k = 0; k < dataDependencies.length; k++) { + let dataDependencyNode = dataDependencies[k] + let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName + let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName + let currentElement = getElement(thisChart[pluralVariableName], candle, 'Market' + '-' + mapKey + '-' + pluralVariableName) + thisChart[singularVariableName] = currentElement + } + } + + /* While we are processing the previous day. */ + let positionedAtYesterday = false + if (currentDay) { + positionedAtYesterday = (candle.end < currentDay.valueOf()) + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle Begin @ " + (new Date(candle.begin)).toLocaleString()) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle End @ " + (new Date(candle.end)).toLocaleString()) } + + let ticker = { + bid: candle.close, + ask: candle.close, + last: candle.close + } + + /* We will produce a simulation level heartbeat in order to inform the user this is running. */ + + loopingDay = new Date(Math.trunc(candle.begin / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + if (loopingDay.valueOf() !== previousLoopingDay) { + + let processingDate = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + currentCandleIndex + " @ " + processingDate) } + console.log("Jason -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + currentCandleIndex + " @ " + processingDate) + + bot.sessionHeartBeat(processingDate) // tell the world we are alive and doing well + } + previousLoopingDay = loopingDay.valueOf() + + /* If any of the needed data dependencies is missing for this particular candle, then we jump the candle*/ + for (let k = 0; k < dataDependencies.length; k++) { + let dataDependencyNode = dataDependencies[k] + let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName + let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName + let product = currentChart[pluralVariableName] + let currentElement = currentChart[singularVariableName] + + if (currentElement === undefined) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because " + singularVariableName + " is undefined."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> For some indicators it is normal to start after a few candles."); } + controlLoop(); + return + } + } + + periods++; + days = periods * timeFrame / ONE_DAY_IN_MILISECONDS; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.periods = periods + } + + /* We skip the candle at the head of the market because currentCandleIndex has not closed yet. */ + let candlesPerDay = ONE_DAY_IN_MILISECONDS / timeFrame + if (currentCandleIndex === candles.length - 1) { + if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) { + /*We are at the head of the market, thus we skip the last candle because it has not close yet. */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } + controlLoop(); + return + /* Note here that in the last candle of the first day or the second day it will use an incomplete candle and partially calculated indicators. + if we skip these two periods, then there will be a hole in the file since the last period will be missing. */ + } + } + + } else { // We are processing Market Files + if (currentCandleIndex === candles.length - 1) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } + controlLoop(); + return + } + } + + let conditions = new Map; // Here we store the conditions values that will be use in the simulator for decision making. + let formulas = new Map; + let conditionsArrayRecord = []; // These are the records that will be saved in a file for the plotter to consume. + let conditionsArrayValues = []; // Here we store the conditions values that will be written on file for the plotter. + let formulasErrors = []; // Here we store the errors produced by all phase formulas. + let formulasValues = []; // Here we store the values produced by all phase formulas. + + /* We define and evaluate all conditions to be used later during the simulation loop. */ + + conditionsArrayRecord.push(candle.begin); + conditionsArrayRecord.push(candle.end); + + for (let j = 0; j < tradingSystem.strategies.length; j++) { + + let strategy = tradingSystem.strategies[j]; + + let positionSize = 0 + let positionRate = 0 + + /* Continue with the rest of the formulas and conditions */ + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOn !== undefined) { + + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + + let situation = triggerStage.triggerOn.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m; + + if (condition.javascriptCode !== undefined) { + newCondition(key, condition.javascriptCode); + } + } + } + } + + if (triggerStage.triggerOff !== undefined) { + + for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) { + + let situation = triggerStage.triggerOff.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m; + + if (condition.javascriptCode !== undefined) { + newCondition(key, condition.javascriptCode); + } + } + } + } + + if (triggerStage.takePosition !== undefined) { + + for (let k = 0; k < triggerStage.takePosition.situations.length; k++) { + + let situation = triggerStage.takePosition.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m; + + if (condition.javascriptCode !== undefined) { + newCondition(key, condition.javascriptCode); + } + } + } + } + } + + let openStage = strategy.openStage + + if (openStage !== undefined) { + + /* Default Values*/ + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + positionSize = balanceBaseAsset; + positionRate = candle.close; + } else { + positionSize = balanceQuotedAsset; + positionRate = candle.close; + } + + let initialDefinition = openStage.initialDefinition + + if (initialDefinition !== undefined) { + + if (tradePositionSize !== 0) { + positionSize = tradePositionSize + } else { + if (initialDefinition.positionSize !== undefined) { + if (initialDefinition.positionSize.formula !== undefined) { + try { + positionSize = eval(initialDefinition.positionSize.formula.code); + } catch (err) { + initialDefinition.positionSize.formula.error = err.message + } + if (isNaN(positionSize)) { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + positionSize = balanceBaseAsset; + } else { + positionSize = balanceQuotedAsset; + } + } else { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (positionSize > balanceBaseAsset) { positionSize = balanceBaseAsset } + } else { + if (positionSize > balanceQuotedAsset) { positionSize = balanceQuotedAsset } + } + } + } + } + } + + if (tradePositionRate !== 0) { + positionRate = tradePositionRate + } else { + if (initialDefinition.positionRate !== undefined) { + if (initialDefinition.positionRate.formula !== undefined) { + try { + positionRate = eval(initialDefinition.positionRate.formula.code); + } catch (err) { + initialDefinition.positionRate.formula.error = err.message + } + if (isNaN(positionRate)) { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + positionRate = candle.close; + } else { + positionRate = candle.close; + } + } + } + } + } + + if (initialDefinition.stopLoss !== undefined) { + + for (let p = 0; p < initialDefinition.stopLoss.phases.length; p++) { + + let phase = initialDefinition.stopLoss.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_STOP_LOSS_VALUE + if (stopLossStage === 'Open Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.stopLoss -> MAX_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_STOP_LOSS_VALUE) { + formulaValue = MIN_STOP_LOSS_VALUE + if (stopLossStage === 'Open Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_STOP_LOSS_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.stopLoss -> MIN_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + if (condition.javascriptCode !== undefined) { + newCondition(key, condition.javascriptCode); + } + } + } + } + } + } + + if (initialDefinition.takeProfit !== undefined) { + + for (let p = 0; p < initialDefinition.takeProfit.phases.length; p++) { + + let phase = initialDefinition.takeProfit.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaValue = MAX_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Open Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.takeProfit -> MAX_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_TAKE_PROFIT_VALUE) { + formulaValue = MIN_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Open Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_TAKE_PROFIT_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.takeProfit -> MIN_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + if (condition.javascriptCode !== undefined) { + newCondition(key, condition.javascriptCode); + } + } + } + } + } + } + } + + strategy.positionSize = positionSize + strategy.positionRate = positionRate + } + + let manageStage = strategy.manageStage + + if (manageStage !== undefined) { + + if (manageStage.stopLoss !== undefined) { + + for (let p = 0; p < manageStage.stopLoss.phases.length; p++) { + + let phase = manageStage.stopLoss.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "" + formulaValue = MAX_STOP_LOSS_VALUE + if (stopLossStage === 'Manage Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.stopLoss -> MAX_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_STOP_LOSS_VALUE) { + formulaValue = MIN_STOP_LOSS_VALUE + if (stopLossStage === 'Manage Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_STOP_LOSS_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.stopLoss -> MIN_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + if (condition.javascriptCode !== undefined) { + newCondition(key, condition.javascriptCode); + } + } + } + } + } + } + + if (manageStage.takeProfit !== undefined) { + + for (let p = 0; p < manageStage.takeProfit.phases.length; p++) { + + let phase = manageStage.takeProfit.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Manage Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.takeProfit -> MAX_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_TAKE_PROFIT_VALUE) { + formulaValue = MIN_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Manage Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_TAKE_PROFIT_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.takeProfit -> MIN_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + if (condition.javascriptCode !== undefined) { + newCondition(key, condition.javascriptCode); + } + } + } + } + } + } + } + + function newCondition(key, node) { + + let condition; + let error = '' + let value + + try { + value = eval(node.code); + } catch (err) { + /* + One possible error is that the conditions references a .previous that is undefined. For this + reason and others, we will simply set the value to false. + */ + value = false + + if (node.code.indexOf('previous') > -1 && err.message.indexOf('of undefined') > -1 || + node.code.indexOf('chart') > -1 && err.message.indexOf('of undefined') > -1 + ) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + node.error = err.message + " @ " + (new Date(candle.begin)).toLocaleString() + } + } + + condition = { + key: key, + value: value + }; + + conditions.set(condition.key, condition); + + if (condition.value) { + conditionsArrayValues.push(1); + } else { + conditionsArrayValues.push(0); + } + } + } + + /* Trigger On Conditions */ + if ( + strategyStage === 'No Stage' && + currentStrategyIndex === -1 + ) { + let minimumBalance + let maximumBalance + let balance + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + balance = balanceBaseAsset + minimumBalance = bot.VALUES_TO_USE.minimumBalanceA + maximumBalance = bot.VALUES_TO_USE.maximumBalanceA + } else { + balance = balanceQuotedAsset + minimumBalance = bot.VALUES_TO_USE.minimumBalanceB + maximumBalance = bot.VALUES_TO_USE.maximumBalanceB + } + + if (balance > minimumBalance && balance < maximumBalance) { + + /* + Here we need to pick a strategy, or if there is not suitable strategy for the current + market conditions, we pass until the next period. + + To pick a new strategy we will evaluate what we call the trigger on. Once we enter + into one strategy, we will ignore market conditions for others. However there is also + a strategy trigger off which can be hit before taking a position. If hit, we would + be outside a strategy again and looking for the condition to enter all over again. + + */ + + for (let j = 0; j < tradingSystem.strategies.length; j++) { + + let strategy = tradingSystem.strategies[j]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOn !== undefined) { + + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + + let situation = triggerStage.triggerOn.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + strategyStage = 'Trigger Stage'; + currentStrategyIndex = j; + currentStrategy.begin = candle.begin; + currentStrategy.beginRate = candle.min; + currentStrategy.endRate = candle.min; // In case the strategy does not get exited + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.currentStrategyIndex = currentStrategyIndex; + yesterday.currentStrategy.begin = currentStrategy.begin; + yesterday.currentStrategy.beginRate = currentStrategy.beginRate; + yesterday.currentStrategy.endRate = currentStrategy.endRate; + } + } + + distanceToLast.triggerOn = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn + } + } + + checkAnnouncements(triggerStage.triggerOn) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Switching to Trigger Stage because conditions at Trigger On Event were met."); } + break; + } + } + } + } + } + } else { + let stopRunningDate = new Date(candle.begin) + if (balance <= minimumBalance) { + tradingSystem.error = "Min Balance @ " + stopRunningDate.toLocaleString() + } + if (balance >= maximumBalance) { + tradingSystem.error = "Max Balance @ " + stopRunningDate.toLocaleString() + } + } + } + + /* Trigger Off Condition */ + if (strategyStage === 'Trigger Stage') { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOff !== undefined) { + + for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) { + + let situation = triggerStage.triggerOff.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + currentStrategy.number = currentStrategyIndex + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; // This means the strategy is closed, i.e. that has a begin and end. + strategyStage = 'No Stage'; + currentStrategyIndex = -1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentStrategy.number = currentStrategy.number; + yesterday.currentStrategy.end = currentStrategy.end; + yesterday.currentStrategy.endRate = currentStrategy.endRate; + yesterday.currentStrategy.status = currentStrategy.status; + yesterday.strategyStage = strategyStage; + yesterday.currentStrategyIndex = currentStrategyIndex; + } + } + + distanceToLast.triggerOff = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + } + } + + checkAnnouncements(triggerStage.triggerOff) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Switching to No Stage because conditions at the Trigger Off Event were met."); } + break; + } + } + } + } + } + + /* Take Position Condition */ + if (strategyStage === 'Trigger Stage') { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.takePosition !== undefined) { + + for (let k = 0; k < triggerStage.takePosition.situations.length; k++) { + + let situation = triggerStage.takePosition.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + type = '"Take Position"'; + + strategyStage = 'Open Stage'; + stopLossStage = 'Open Stage'; + takeProfitStage = 'Open Stage'; + stopLossPhase = 0; + takeProfitPhase = 0; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + yesterday.stopLossPhase = stopLossPhase; + yesterday.takeProfitPhase = takeProfitPhase; + } + } + + takePositionNow = true + + checkAnnouncements(triggerStage.takePosition) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Conditions at the Take Position Event were met."); } + break; + } + } + } + } + } + + /* Stop Loss Management */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + + checkStopPhases() + calculateStopLoss(); + + } + + function checkStopPhases() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let parentNode + let j = currentStrategyIndex + let stageKey + let initialDefinitionKey = '' + let p + + if (stopLossStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.stopLoss !== undefined) { + parentNode = openStage.initialDefinition + initialDefinitionKey = '-' + 'initialDefinition' + stageKey = 'openStage' + p = stopLossPhase + } + } + } + + if (stopLossStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.stopLoss !== undefined) { + parentNode = manageStage + stageKey = 'manageStage' + p = stopLossPhase - 1 + } + } + + if (parentNode !== undefined) { + let phase = parentNode.stopLoss.phases[p]; + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + stopLossPhase++; + stopLossStage = 'Manage Stage' + if (takeProfitPhase > 0) { strategyStage = 'Manage Stage' } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLossPhase = stopLossPhase; + yesterday.stopLossStage = stopLossStage; + yesterday.strategyStage = strategyStage; + } + } + + checkAnnouncements(nextPhaseEvent) + return; + } + } + } + } + } + + function calculateStopLoss() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let phase + let key + + if (stopLossStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.stopLoss !== undefined) { + phase = openStage.initialDefinition.stopLoss.phases[stopLossPhase]; + key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + (stopLossPhase); + } + } + } + + if (stopLossStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.stopLoss !== undefined) { + phase = manageStage.stopLoss.phases[stopLossPhase - 1]; + key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + (stopLossPhase - 1); + } + } + + if (phase !== undefined) { + if (phase.formula !== undefined) { + let previousValue = stopLoss + + stopLoss = formulas.get(key) + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + + if (stopLoss !== previousValue) { + checkAnnouncements(phase, stopLoss) + } + } + } + } + + /* Take Profit Management */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + + checkTakeProfitPhases(); + calculateTakeProfit(); + + } + + function checkTakeProfitPhases() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let parentNode + let j = currentStrategyIndex + let stageKey + let initialDefinitionKey = '' + let p + + if (takeProfitStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.takeProfit !== undefined) { + parentNode = openStage.initialDefinition + initialDefinitionKey = '-' + 'initialDefinition' + stageKey = 'openStage' + p = takeProfitPhase + } + } + } + + if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.takeProfit !== undefined) { + parentNode = manageStage + stageKey = 'manageStage' + p = takeProfitPhase - 1 + } + } + + if (parentNode !== undefined) { + let phase = parentNode.takeProfit.phases[p]; + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + takeProfitPhase++; + takeProfitStage = 'Manage Stage' + if (stopLossPhase > 0) { strategyStage = 'Manage Stage' } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfitPhase = takeProfitPhase; + yesterday.takeProfitStage = takeProfitStage; + yesterday.strategyStage = strategyStage; + } + } + + checkAnnouncements(nextPhaseEvent) + return; + } + } + } + } + } + + function calculateTakeProfit() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let phase + let key + + if (takeProfitStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.takeProfit !== undefined) { + phase = openStage.initialDefinition.takeProfit.phases[takeProfitPhase]; + key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + (takeProfitPhase); + } + } + } + + if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.takeProfit !== undefined) { + phase = manageStage.takeProfit.phases[takeProfitPhase - 1]; + key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + (takeProfitPhase - 1); + } + } + + if (phase !== undefined) { + if (phase.formula !== undefined) { + + let previousValue = stopLoss + + takeProfit = formulas.get(key) + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + + if (takeProfit !== previousValue) { + checkAnnouncements(phase, takeProfit) + } + } + } + } + + /* Keeping Position Counters Up-to-date */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') + ) { + + if (takePositionNow === true) { + positionPeriods = 0 + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.positionPeriods = 0 + } + } + } + + positionPeriods++; + positionDays = positionPeriods * timeFrame / ONE_DAY_IN_MILISECONDS; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.positionPeriods = positionPeriods + } + } + } else { + positionPeriods = 0 + positionDays = 0 + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.positionPeriods = 0 + } + } + } + + /* Keeping Distance Counters Up-to-date */ + if ( + distanceToLast.triggerOn > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.triggerOn++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn + } + } + } + + if ( + distanceToLast.triggerOff > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.triggerOff++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + } + } + } + + if ( + distanceToLast.takePosition > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.takePosition++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.takePosition = distanceToLast.takePosition + } + } + } + + if ( + distanceToLast.closePosition > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.closePosition++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.takePosition = distanceToLast.closePosition + } + } + } + + /* Checking if Stop or Take Profit were hit */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + /* Checking what happened since the last execution. We need to know if the Stop Loss + or our Take Profit were hit. */ + + /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ + + if ((bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset && candle.max >= stopLoss) || (bot.VALUES_TO_USE.baseAsset !== bot.market.baseAsset && candle.min <= stopLoss)) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Stop Loss was hit."); } + /* + Hit Point Validation + + This prevents misscalculations when a formula places the stop loss in this case way beyond the market price. + If we take the stop loss value at those situation would be a huge distortion of facts. + */ + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (stopLoss < candle.min) { + stopLoss = candle.min + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + } + } else { + if (stopLoss > candle.max) { + stopLoss = candle.max + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + } + } + + let slippedStopLoss = stopLoss + + /* Apply the Slippage */ + let slippageAmount = slippedStopLoss * bot.VALUES_TO_USE.slippage.stopLoss / 100 + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + slippedStopLoss = slippedStopLoss + slippageAmount + } else { + slippedStopLoss = slippedStopLoss - slippageAmount + } + + closeRate = slippedStopLoss; + + marketRate = closeRate; + type = '"Close@StopLoss"'; + strategyStage = 'Close Stage'; + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + currentTrade.end = candle.end; + currentTrade.status = 1; + currentTrade.exitType = 1; + currentTrade.endRate = closeRate; + + closePositionNow = true; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + yesterday.currentTrade.end = currentTrade.end; + yesterday.currentTrade.status = currentTrade.status; + yesterday.currentTrade.exitType = currentTrade.exitType; + yesterday.currentTrade.endRate = currentTrade.endRate; + } + } + } + + /* Take Profit condition: Here we verify if the Take Profit was hit or not. */ + + if ((bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset && candle.min <= takeProfit) || (bot.VALUES_TO_USE.baseAsset !== bot.market.baseAsset && candle.max >= takeProfit)) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Take Profit was hit."); } + /* + Hit Point Validation: + + This prevents misscalculations when a formula places the take profit in this case way beyond the market price. + If we take the stop loss value at those situation would be a huge distortion of facts. + */ + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (takeProfit > candle.max) { + takeProfit = candle.max + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + } + } else { + if (takeProfit < candle.min) { + takeProfit = candle.min + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + } + } + + let slippedTakeProfit = takeProfit + /* Apply the Slippage */ + let slippageAmount = slippedTakeProfit * bot.VALUES_TO_USE.slippage.takeProfit / 100 + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + slippedTakeProfit = slippedTakeProfit + slippageAmount + } else { + slippedTakeProfit = slippedTakeProfit - slippageAmount + } + + closeRate = slippedTakeProfit; + + marketRate = closeRate; + type = '"Close@TakeProfit"'; + strategyStage = 'Close Stage'; + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + + currentTrade.end = candle.end; + currentTrade.status = 1; + currentTrade.exitType = 2; + currentTrade.endRate = closeRate; + + closePositionNow = true; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + + yesterday.currentTrade.end = currentTrade.end; + yesterday.currentTrade.status = currentTrade.status; + yesterday.currentTrade.exitType = currentTrade.exitType; + yesterday.currentTrade.endRate = currentTrade.endRate; + } + } + } + } + + /* Taking a Position */ + if ( + takePositionNow === true + ) { + takePositionNow = false + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Entering code block."); } + + /* Inicializing this counter */ + distanceToLast.takePosition = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.takePosition = distanceToLast.takePosition + } + } + + /* Position size and rate */ + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + tradePositionSize = strategy.positionSize; + tradePositionRate = strategy.positionRate; + + /* We take what was calculated at the formula and apply the slippage. */ + let slippageAmount = tradePositionRate * bot.VALUES_TO_USE.slippage.positionRate / 100 + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + tradePositionRate = tradePositionRate - slippageAmount + } else { + tradePositionRate = tradePositionRate + slippageAmount + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.tradePositionSize = tradePositionSize; + yesterday.tradePositionRate = tradePositionRate; + } + } + + /* Update the trade record information. */ + currentTrade.begin = candle.begin; + currentTrade.beginRate = tradePositionRate; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentTrade.begin = currentTrade.begin; + yesterday.currentTrade.beginRate = currentTrade.beginRate; + } + } + + /* Check if we need to execute. */ + if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + /* Check that we are in LIVE MODE */ + if (bot.startMode === "Live") { + /* We see if we need to put the actual order at the exchange. */ + if (interExecutionMemory.executionContext !== undefined) { + switch (interExecutionMemory.executionContext.status) { + case "Without a Position": { // We need to put the order because It was not put yet. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + break + } + case "Position Closed": { // Waiting for a confirmation that the position was closed. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + break + } + case "Taking Position": { // Waiting for a confirmation that the position was taken. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is Taking Position."); } + break + } + case "In a Position": { // This should mean that we already put the order at the exchange. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is In a Position."); } + break + } + } + } else { // The context does not exist so it means we are not in a position. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Not trading live."); } + } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Not the last closed candle."); } + } + + takePositionAtSimulation() + return + + function putOpeningOrder() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function."); } + + /* We wont take a position unless we are withing the bot.VALUES_TO_USE.timeRange.initialDatetime and the bot.VALUES_TO_USE.timeRange.finalDatetime range */ + if (bot.VALUES_TO_USE.timeRange.initialDatetime !== undefined) { + if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> bot.VALUES_TO_USE.timeRange.initialDatetime = " + bot.VALUES_TO_USE.timeRange.initialDatetime); } + takePositionAtSimulation() + return; + } + } + + /*We wont take a position if we are past the final datetime */ + if (bot.VALUES_TO_USE.timeRange.finalDatetime !== undefined) { + if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> bot.VALUES_TO_USE.timeRange.finalDatetime = " + bot.VALUES_TO_USE.timeRange.finalDatetime); } + takePositionAtSimulation() + return; + } + } + + /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ + if (interExecutionMemory.executionContext !== undefined) { + if (interExecutionMemory.executionContext.periods !== undefined) { + if (periods <= interExecutionMemory.executionContext.periods) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution."); } + takePositionAtSimulation() + return; + } + } + } + + /* We are not going to place orders based on outdated information. The next filter prevents firing orders when backtesting. */ + if (currentDay) { + let today = new Date(Math.trunc((new Date().valueOf()) / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + let processDay = new Date(Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + if (today.valueOf() !== processDay.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the current candle belongs to the previous day and that is considered simulation and not live trading."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> today = " + today); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> processDay = " + processDay); } + takePositionAtSimulation() + return; + } + } + + let orderPrice + let amountA + let amountB + let orderSide + + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + orderSide = "sell" + + orderPrice = tradePositionRate - 100 // This is going to be ingnored at the Exchange API for now since we only put market orders. + + amountA = tradePositionSize * orderPrice + amountB = tradePositionSize + + } else { + orderSide = "buy" + + orderPrice = tradePositionRate // This is going to be ingnored at the Exchange API for now since we only put market orders. + + amountA = tradePositionSize + amountB = tradePositionSize / orderPrice + + } + + interExecutionMemory.executionContext = { + status: "Taking Position", + periods: periods, + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to create order."); } + exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated) + + function onOrderCreated(err, order) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Entering function."); } + + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE "); } + interExecutionMemory.executionContext = { + status: "In a Position", + periods: periods, + amountA: amountA, + amountB: amountB, + orderId: order.id + } + takePositionAtSimulation() + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = " + err.message); } + strategy.openStage.openExecution.error = err.message + afterLoop() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = " + err.message); } + strategy.openStage.openExecution.error = err.message + afterLoop() + return; + } + } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Unexpected Response -> Message = " + err.message); } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + } + + function takePositionAtSimulation() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionAtSimulation -> Entering function."); } + + /* Continue with the simulation */ + calculateTakeProfit(); + calculateStopLoss(); + + marketRate = candle.close; + + previousBalanceBaseAsset = balanceBaseAsset; + previousBalanceQuotedAsset = balanceQuotedAsset; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.previousBalanceBaseAsset = previousBalanceBaseAsset; + yesterday.previousBalanceQuotedAsset = previousBalanceQuotedAsset; + } + } + + lastTradeProfitLoss = 0; + lastTradeROI = 0; + + let feePaid = 0 + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + + feePaid = tradePositionSize * tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceQuotedAsset = balanceQuotedAsset + tradePositionSize * tradePositionRate - feePaid; + balanceBaseAsset = balanceBaseAsset - tradePositionSize; + } else { + + feePaid = tradePositionSize / tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceBaseAsset = balanceBaseAsset + tradePositionSize / tradePositionRate - feePaid; + balanceQuotedAsset = balanceQuotedAsset - tradePositionSize; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.balanceBaseAsset = balanceBaseAsset; + yesterday.balanceQuotedAsset = balanceQuotedAsset; + + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.lastTradeROI = lastTradeROI; + } + } + + addRecord(); + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionAtSimulation -> Exiting Loop Body after taking position at simulation."); } + controlLoop(); + return + } + } + + if (closePositionNow === true) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Entering code block."); } + + closePositionNow = false + + /* Inicializing this counter */ + distanceToLast.closePosition = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.closePosition = distanceToLast.closePosition + } + } + + /* Position size and rate */ + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + /* Check that we are in LIVE MODE */ + if (bot.startMode === "Live") { + /* We see if we need to put the actual order at the exchange. */ + if (interExecutionMemory.executionContext !== undefined) { + switch (interExecutionMemory.executionContext.status) { + case "Without a Position": { // No way to close anything at the exchange. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Without a Position."); } + break + } + case "In a Position": { // This should mean that we already put the order at the exchange. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return + } + } + break + } + case "Closing Position": { // Waiting for a confirmation that the position was taken. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return + } + } + break + } + + case "Position Closed": { // + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Position Closed."); } + break + } + } + } + } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Not within the last 10 candles."); } + } + + closePositionAtSimulation() + return + + function putClosingOrder() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> Entering function."); } + + /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ + if (interExecutionMemory.executionContext !== undefined) { + if (interExecutionMemory.executionContext.periods !== undefined) { + if (periods <= interExecutionMemory.executionContext.periods) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution."); } + closePositionAtSimulation() + return; + } + } + } + + let orderPrice + let amountA + let amountB + let orderSide + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + orderSide = "buy" + + orderPrice = ticker.last + 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + + amountA = balanceQuotedAsset + amountB = balanceQuotedAsset / orderPrice + + } else { + orderSide = "sell" + + orderPrice = ticker.last - 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + + amountA = balanceBaseAsset * orderPrice + amountB = balanceBaseAsset + + } + + interExecutionMemory.executionContext = { + status: "Closing Position", + periods: periods, + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange."); } + exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated) + + function onOrderCreated(err, order) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Entering function."); } + + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE "); } + interExecutionMemory.executionContext = { + status: "Position Closed", + periods: periods, + amountA: amountA, + amountB: amountB, + orderId: order.id + } + closePositionAtSimulation() + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE "); } + /* We will assume that the problem is temporary, and expect that it will work at the next execution.*/ + strategy.closeStage.closeExecution.error = err.message + afterLoop() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE "); } + strategy.closeStage.closeExecution.error = err.message + afterLoop() + return; + } + } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Unexpected Response -> Message = " + err.message); } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + } + + function closePositionAtSimulation() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> closePositionAtSimulation -> Entering function."); } + + roundtrips++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.roundtrips = roundtrips + } + } + + let feePaid = 0 + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + strategy.positionSize = balanceQuotedAsset / closeRate; + strategy.positionRate = closeRate; + + feePaid = balanceQuotedAsset / closeRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceBaseAsset = balanceBaseAsset + balanceQuotedAsset / closeRate - feePaid; + balanceQuotedAsset = 0; + } else { + strategy.positionSize = balanceBaseAsset * closeRate; + strategy.positionRate = closeRate; + + feePaid = balanceBaseAsset * closeRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceQuotedAsset = balanceQuotedAsset + balanceBaseAsset * closeRate - feePaid; + balanceBaseAsset = 0; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.balanceBaseAsset = balanceBaseAsset; + yesterday.balanceQuotedAsset = balanceQuotedAsset; + + } + } + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + lastTradeProfitLoss = balanceBaseAsset - previousBalanceBaseAsset; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; + if (isNaN(lastTradeROI)) { lastTradeROI = 0; } + profit = balanceBaseAsset - bot.VALUES_TO_USE.initialBalanceA; + } else { + lastTradeProfitLoss = balanceQuotedAsset - previousBalanceQuotedAsset; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; + if (isNaN(lastTradeROI)) { lastTradeROI = 0; } + profit = balanceQuotedAsset - bot.VALUES_TO_USE.initialBalanceB; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.profit = profit; + yesterday.lastTradeROI = lastTradeROI; + } + } + + currentTrade.lastTradeROI = lastTradeROI; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentTrade.lastTradeROI = currentTrade.lastTradeROI; + } + } + + if (lastTradeProfitLoss > 0) { + hits++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.hits = hits + } + } + + } else { + fails++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.fails = fails + } + } + } + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + ROI = (bot.VALUES_TO_USE.initialBalanceA + profit) / bot.VALUES_TO_USE.initialBalanceA - 1; + hitRatio = hits / roundtrips; + anualizedRateOfReturn = ROI / days * 365; + } else { + ROI = (bot.VALUES_TO_USE.initialBalanceB + profit) / bot.VALUES_TO_USE.initialBalanceB - 1; + hitRatio = hits / roundtrips; + anualizedRateOfReturn = ROI / days * 365; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.ROI = ROI; + yesterday.hitRatio = hitRatio; + yesterday.anualizedRateOfReturn = anualizedRateOfReturn; + } + } + + addRecord(); + + stopLoss = 0; + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + takeProfit = 0; + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + + tradePositionRate = 0; + tradePositionSize = 0; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.tradePositionSize = tradePositionSize; + yesterday.tradePositionRate = tradePositionRate; + } + } + + timerToCloseStage = candle.begin + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + stopLossPhase = -1; + takeProfitPhase = -1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + yesterday.stopLossPhase = stopLossPhase; + yesterday.takeProfitPhase = takeProfitPhase; + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> closePositionAtSimulation -> Exiting Loop Body after closing position at simulation."); } + controlLoop(); + return + } + } + + /* Closing the Closing Stage */ + if (strategyStage === 'Close Stage') { + if (candle.begin - 5 * 60 * 1000 > timerToCloseStage) { + + currentStrategy.number = currentStrategyIndex + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; // This means the strategy is closed, i.e. that has a begin and end. + + if (processingDailyFiles) { + if (positionedAtYesterday) { + + yesterday.currentStrategy.number = currentStrategy.number; + yesterday.currentStrategy.end = currentStrategy.end; + yesterday.currentStrategy.endRate = currentStrategy.endRate; + yesterday.currentStrategy.status = currentStrategy.status; + } + } + + currentStrategyIndex = -1; + strategyStage = 'No Stage'; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentStrategyIndex = currentStrategyIndex; + yesterday.strategyStage = strategyStage; + } + } + + timerToCloseStage = 0 + + distanceToLast.triggerOff = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing the Closing Stage -> Exiting Close Stage."); } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing the Closing Stage -> Waiting for timer."); } + } + } + + /* Not a buy or sell condition */ + + marketRate = candle.close; + addRecord(); + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Exiting Loop Body after adding a record."); } + controlLoop(); + return + + function addRecord() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> addRecord -> Entering function."); } + + // Since we are going to write the message to a file that the Simulation Executor is going to read, we use the abbreviations. + let messageType; + let message; + let simulationRecord; + let executionRecord; + + let strategyStageNumber + switch (strategyStage) { + case 'No Stage': { + strategyStageNumber = 0 + break + } + case 'Trigger Stage': { + strategyStageNumber = 1 + break + } + case 'Open Stage': { + strategyStageNumber = 2 + break + } + case 'Manage Stage': { + strategyStageNumber = 3 + break + } + case 'Close Stage': { + strategyStageNumber = 4 + break + } + } + + if (balanceBaseAsset === Infinity) { + balanceBaseAsset = Number.MAX_SAFE_INTEGER + } + + if (balanceQuotedAsset === Infinity) { + balanceQuotedAsset = Number.MAX_SAFE_INTEGER + } + + let quotedBaseAsset = '"' + bot.VALUES_TO_USE.baseAsset + '"' + + simulationRecord = { + begin: candle.begin, + end: candle.end, + type: type, + marketRate: marketRate, + amount: 1, + balanceA: balanceBaseAsset, + balanceB: balanceQuotedAsset, + profit: profit, + lastTradeProfitLoss: lastTradeProfitLoss, + stopLoss: stopLoss, + roundtrips: roundtrips, + hits: hits, + fails: fails, + hitRatio: hitRatio, + ROI: ROI, + periods: periods, + days: days, + anualizedRateOfReturn: anualizedRateOfReturn, + positionRate: tradePositionRate, + lastTradeROI: lastTradeROI, + strategy: currentStrategyIndex, + strategyStageNumber: strategyStageNumber, + takeProfit: takeProfit, + stopLossPhase: stopLossPhase, + takeProfitPhase: takeProfitPhase, + executionRecord: '', + positionSize: tradePositionSize, + initialBalanceA: bot.VALUES_TO_USE.initialBalanceA, + minimumBalanceA: bot.VALUES_TO_USE.minimumBalanceA, + maximumBalanceA: bot.VALUES_TO_USE.maximumBalanceA, + initialBalanceB: bot.VALUES_TO_USE.initialBalanceB, + minimumBalanceB: bot.VALUES_TO_USE.minimumBalanceB, + maximumBalanceB: bot.VALUES_TO_USE.maximumBalanceB, + baseAsset: quotedBaseAsset, + positionPeriods: positionPeriods, + positionDays: positionDays, + distanceToLastTriggerOn: distanceToLast.triggerOn, + distanceToLastTriggerOff: distanceToLast.triggerOff, + distanceToLastTakePosition: distanceToLast.takePosition, + distanceToLastClosePosition: distanceToLast.closePosition + } + + recordsArray.push(simulationRecord); + + type = '""'; + + /* Prepare the information for the Conditions File */ + + conditionsArrayRecord.push(currentStrategyIndex); + conditionsArrayRecord.push(strategyStageNumber); + conditionsArrayRecord.push(stopLossPhase); + conditionsArrayRecord.push(takeProfitPhase); + conditionsArrayRecord.push(conditionsArrayValues); + conditionsArrayRecord.push(formulasErrors); + conditionsArrayRecord.push(formulasValues); + + conditionsArray.push(conditionsArrayRecord); + + /* + Lets see if there will be an open strategy ... + Except if we are at the head of the market (remember we skipped the last candle for not being closed.) + */ + if (currentStrategy.begin !== 0 && currentStrategy.end === 0 && currentCandleIndex === candles.length - 2 && lastCandle.end !== lastInstantOfTheDay) { + currentStrategy.status = 2; // This means the strategy is open, i.e. that has a begin but no end. + currentStrategy.end = candle.end + } + + /* Prepare the information for the Strategies File*/ + if (currentStrategy.begin !== 0 && currentStrategy.end !== 0) + { + strategiesArray.push(currentStrategy); + + currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + } + } + } + + /* + Lets see if there will be an open trade ... + Except if we are at the head of the market (remember we skipped the last candle for not being closed.) + */ + if (currentTrade.begin !== 0 && currentTrade.end === 0 && currentCandleIndex === candles.length - 2 && lastCandle.end !== lastInstantOfTheDay) { + currentTrade.status = 2; // This means the trade is open + currentTrade.end = candle.end + currentTrade.endRate = candle.close + + /* Here we will calculate the ongoing ROI */ + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + currentTrade.lastTradeROI = (tradePositionRate - candle.close) / tradePositionRate * 100 + } else { + currentTrade.lastTradeROI = (candle.close - tradePositionRate) / tradePositionRate * 100 + } + } + + /* Prepare the information for the Trades File */ + if (currentTrade.begin !== 0 && currentTrade.end !== 0) { + + currentTrade.profit = lastTradeProfitLoss; + + tradesArray.push(currentTrade); + + currentTrade = { + begin: 0, + end: 0, + status: 0, + lastTradeROI: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentTrade = { + begin: 0, + end: 0, + status: 0, + lastTradeROI: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + } + } + } + + makeAnnoucements() // After everything at the simulation level was done, we will do the annoucements that are pending. + } + + function checkAnnouncements(node, value) { + /* + Value is an optional parameter that represents the value that the announcement is monitoring for change. + If we do receive this value, we will only make the annoucement if the variance is grater than the user pre-defined value + for this variance. + */ + + if (node.announcements !== undefined) { + for (let i = 0; i < node.announcements.length; i++) { + let announcement = node.announcements[i] + let key = node.type + "-" + announcement.name + "-" + announcement.id + + let lastPeriodAnnounced = -1 + let newAnnouncementRecord = {} + + for (let j = 0; j < interExecutionMemory.announcements.length; j++) { + let announcementRecord = interExecutionMemory.announcements[j] + if (announcementRecord.key === key) { + lastPeriodAnnounced = announcementRecord.periods + newAnnouncementRecord = announcementRecord + break + } + } + + if (periods > lastPeriodAnnounced) { + + /* The Value Variation is what tells us how much the value already announced must change in order to annouce it again. */ + let valueVariation + try { + let code = JSON.parse(announcement.code) + valueVariation = code.valueVariation + } catch (err) { + announcement.error = err.message + } + + if (newAnnouncementRecord.value !== undefined && valueVariation !== undefined) { + let upperLimit = newAnnouncementRecord.value + newAnnouncementRecord.value * valueVariation / 100 + let lowerLimit = newAnnouncementRecord.value - newAnnouncementRecord.value * valueVariation / 100 + if (value > lowerLimit && value < upperLimit) { + /* There is not enough variation to announce this again. */ + return + } + } + + /* + We store the announcement temporarily at an Array to differ its execution, becasue we need to evaulate its formula + and at this point in time the potential variables used at the formula are still not set. + */ + announcementsToBeMade.push(announcement) + + /* Next, we will remmeber this announcement was already done, so that it is not announced again in further processing of the same day. */ + if (newAnnouncementRecord.periods !== undefined) { + newAnnouncementRecord.periods = periods + newAnnouncementRecord.value = value + } else { + newAnnouncementRecord = { + key: key, + periods: periods, + value: value + } + interExecutionMemory.announcements.push(newAnnouncementRecord) + } + } + } + } + } + + function makeAnnoucements() { + /* Here we go through all the annoucements that need to be done during this loop, and we just do them. */ + for (let i = 0; i < announcementsToBeMade.length; i++) { + announcement = announcementsToBeMade[i] + /* Here we check if there is a formula attached to the annoucement, we evaluate it to get the annoucement text. */ + let formulaValue + if (announcement.formula !== undefined) { + try { + formulaValue = eval(announcement.formula.code); + } catch (err) { + announcement.formula.error = err.message + } + } + announcement.formulaValue = formulaValue + + bot.SESSION.socialBots.announce(announcement) + } + } + } + + + + function controlLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> Entering function."); } + + /* Checking if we should continue processing this loop or not.*/ + if (bot.STOP_SESSION === true) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing."); } + console.log("[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing.") + afterLoop() + return + } + + currentCandleIndex++ + if (currentCandleIndex < candles.length) { + setImmediate(loop) // This will execute the next loop in the next iteration of the NodeJs event loop allowing for other callbacks to be executed. + } else { + afterLoop() + } + } + + function afterLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> afterLoop -> Entering function."); } + + /* + Before returning we need to see if we have to record some of our counters at the interExecutionMemory. + To do that, the condition to be met is that this execution must include all candles of the current day. + */ + + if (processingDailyFiles) { + + if (lastCandle.end === lastInstantOfTheDay) { + + interExecutionMemory.stopLoss = yesterday.stopLoss + interExecutionMemory.takeProfit = yesterday.takeProfit + + interExecutionMemory.previousBalanceBaseAsset = yesterday.previousBalanceBaseAsset + interExecutionMemory.previousBalanceQuotedAsset = yesterday.previousBalanceQuotedAsset + + interExecutionMemory.tradePositionRate = yesterday.tradePositionRate + interExecutionMemory.tradePositionSize = yesterday.tradePositionSize + + interExecutionMemory.currentStrategyIndex = yesterday.currentStrategyIndex + interExecutionMemory.strategyStage = yesterday.strategyStage + + interExecutionMemory.stopLossPhase = yesterday.stopLossPhase + interExecutionMemory.stopLossStage = yesterday.stopLossStage + + interExecutionMemory.takeProfitPhase = yesterday.takeProfitPhase + interExecutionMemory.takeProfitStage = yesterday.takeProfitStage + + interExecutionMemory.currentStrategy = { + begin: yesterday.currentStrategy.begin, + end: yesterday.currentStrategy.end, + status: yesterday.currentStrategy.status, + number: yesterday.currentStrategy.number, + beginRate: yesterday.currentStrategy.beginRate, + endRate: yesterday.currentStrategy.endRate + } + + interExecutionMemory.currentTrade = { + begin: yesterday.currentTrade.begin, + end: yesterday.currentTrade.end, + status: yesterday.currentTrade.status, + profit: yesterday.currentTrade.profit, + exitType: yesterday.currentTrade.exitType, + beginRate: yesterday.currentTrade.beginRate, + endRate: yesterday.currentTrade.endRate + } + + interExecutionMemory.balanceBaseAsset = yesterday.balanceBaseAsset; + interExecutionMemory.balanceQuotedAsset = yesterday.balanceQuotedAsset; + interExecutionMemory.lastTradeProfitLoss = yesterday.lastTradeProfitLoss; + interExecutionMemory.profit = yesterday.profit; + interExecutionMemory.lastTradeROI = yesterday.lastTradeROI; + + interExecutionMemory.roundtrips = yesterday.roundtrips; + interExecutionMemory.fails = yesterday.fails; + interExecutionMemory.hits = yesterday.hits; + interExecutionMemory.periods = yesterday.periods; + interExecutionMemory.positionPeriods = yesterday.positionPeriods; + + interExecutionMemory.distanceToLast.triggerOn = yesterday.distanceToLast.triggerOn + interExecutionMemory.distanceToLast.triggerOff = yesterday.distanceToLast.triggerOff + interExecutionMemory.distanceToLast.takePosition = yesterday.distanceToLast.takePosition + interExecutionMemory.distanceToLast.closePosition = yesterday.distanceToLast.closePosition + + interExecutionMemory.messageId = interExecutionMemory.messageId + yesterday.messageId; + interExecutionMemory.orderId = interExecutionMemory.orderId + yesterday.orderId; + + interExecutionMemory.hitRatio = yesterday.hitRatio; + interExecutionMemory.ROI = yesterday.ROI; + interExecutionMemory.anualizedRateOfReturn = yesterday.anualizedRateOfReturn; + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> callback -> recordsArray.length = " + recordsArray.length); } + + callback(tradingSystem, recordsArray, conditionsArray, strategiesArray, tradesArray); + } + + function getElement(pArray, currentCandle, datasetName) { + + try { + let element; + for (let i = 0; i < pArray.length; i++) { + element = pArray[i]; + + if (currentCandle.end === element.end) { // when there is an exact match at the end we take that element + return element + } else { + if ( + i > 0 && + element.end > currentCandle.end + ) { + let previousElement = pArray[i - 1] + if (previousElement.end < currentCandle.end) { + return previousElement // If one elements goes into the future of currentCandle, then we stop and take the previous element. + } else { + return + } + } + if ( + i === pArray.length - 1 // If we reach the end of the array, then we return the last element. + && + element.end < currentCandle.end + ) { + return element + } + } + } + return + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> getElement -> datasetName = " + datasetName); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; + + + diff --git a/TaskServer/node_modules/@types/node/package.json b/TaskServer/node_modules/@types/node/package.json new file mode 100644 index 000000000..695ce2c04 --- /dev/null +++ b/TaskServer/node_modules/@types/node/package.json @@ -0,0 +1,220 @@ +{ + "_from": "@types/node@^13.1.0", + "_id": "@types/node@13.7.0", + "_inBundle": false, + "_integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==", + "_location": "/@types/node", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "@types/node@^13.1.0", + "name": "@types/node", + "escapedName": "@types%2fnode", + "scope": "@types", + "rawSpec": "^13.1.0", + "saveSpec": null, + "fetchSpec": "^13.1.0" + }, + "_requiredBy": [ + "/telegraf" + ], + "_resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", + "_shasum": "b417deda18cf8400f278733499ad5547ed1abec4", + "_spec": "@types/node@^13.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\telegraf", + "bugs": { + "url": "https://github.com/DefinitelyTyped/DefinitelyTyped/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Microsoft TypeScript", + "url": "https://github.com/Microsoft" + }, + { + "name": "DefinitelyTyped", + "url": "https://github.com/DefinitelyTyped" + }, + { + "name": "Alberto Schiabel", + "url": "https://github.com/jkomyno" + }, + { + "name": "Alexander T.", + "url": "https://github.com/a-tarasyuk" + }, + { + "name": "Alvis HT Tang", + "url": "https://github.com/alvis" + }, + { + "name": "Andrew Makarov", + "url": "https://github.com/r3nya" + }, + { + "name": "Benjamin Toueg", + "url": "https://github.com/btoueg" + }, + { + "name": "Bruno Scheufler", + "url": "https://github.com/brunoscheufler" + }, + { + "name": "Chigozirim C.", + "url": "https://github.com/smac89" + }, + { + "name": "Christian Vaagland Tellnes", + "url": "https://github.com/tellnes" + }, + { + "name": "David Junger", + "url": "https://github.com/touffy" + }, + { + "name": "Deividas Bakanas", + "url": "https://github.com/DeividasBakanas" + }, + { + "name": "Eugene Y. Q. Shen", + "url": "https://github.com/eyqs" + }, + { + "name": "Flarna", + "url": "https://github.com/Flarna" + }, + { + "name": "Hannes Magnusson", + "url": "https://github.com/Hannes-Magnusson-CK" + }, + { + "name": "Hoàng Văn Khải", + "url": "https://github.com/KSXGitHub" + }, + { + "name": "Huw", + "url": "https://github.com/hoo29" + }, + { + "name": "Kelvin Jin", + "url": "https://github.com/kjin" + }, + { + "name": "Klaus Meinhardt", + "url": "https://github.com/ajafff" + }, + { + "name": "Lishude", + "url": "https://github.com/islishude" + }, + { + "name": "Mariusz Wiktorczyk", + "url": "https://github.com/mwiktorczyk" + }, + { + "name": "Mohsen Azimi", + "url": "https://github.com/mohsen1" + }, + { + "name": "Nicolas Even", + "url": "https://github.com/n-e" + }, + { + "name": "Nicolas Voigt", + "url": "https://github.com/octo-sniffle" + }, + { + "name": "Nikita Galkin", + "url": "https://github.com/galkin" + }, + { + "name": "Parambir Singh", + "url": "https://github.com/parambirs" + }, + { + "name": "Sebastian Silbermann", + "url": "https://github.com/eps1lon" + }, + { + "name": "Simon Schick", + "url": "https://github.com/SimonSchick" + }, + { + "name": "Thomas den Hollander", + "url": "https://github.com/ThomasdenH" + }, + { + "name": "Wilco Bakker", + "url": "https://github.com/WilcoBakker" + }, + { + "name": "wwwy3y3", + "url": "https://github.com/wwwy3y3" + }, + { + "name": "Zane Hannan AU", + "url": "https://github.com/ZaneHannanAU" + }, + { + "name": "Samuel Ainsworth", + "url": "https://github.com/samuela" + }, + { + "name": "Kyle Uehlein", + "url": "https://github.com/kuehlein" + }, + { + "name": "Jordi Oliveras Rovira", + "url": "https://github.com/j-oliveras" + }, + { + "name": "Thanik Bhongbhibhat", + "url": "https://github.com/bhongy" + }, + { + "name": "Marcin Kopacz", + "url": "https://github.com/chyzwar" + }, + { + "name": "Trivikram Kamat", + "url": "https://github.com/trivikr" + }, + { + "name": "Minh Son Nguyen", + "url": "https://github.com/nguymin4" + }, + { + "name": "Junxiao Shi", + "url": "https://github.com/yoursunny" + }, + { + "name": "Ilia Baryshnikov", + "url": "https://github.com/qwelias" + } + ], + "dependencies": {}, + "deprecated": false, + "description": "TypeScript definitions for Node.js", + "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped#readme", + "license": "MIT", + "main": "", + "name": "@types/node", + "repository": { + "type": "git", + "url": "git+https://github.com/DefinitelyTyped/DefinitelyTyped.git", + "directory": "types/node" + }, + "scripts": {}, + "typeScriptVersion": "2.8", + "types": "index.d.ts", + "typesPublisherContentHash": "b0cd8dccd6d2eca8bb1de5a05bebf13796984567eb809f63164972122c4ddbaf", + "typesVersions": { + ">=3.5.0-0": { + "*": [ + "ts3.5/*" + ] + } + }, + "version": "13.7.0" +} diff --git a/TaskServer/node_modules/ccxt/build/cleanup-old-tags.js b/TaskServer/node_modules/ccxt/build/cleanup-old-tags.js new file mode 100644 index 000000000..d807ab18a --- /dev/null +++ b/TaskServer/node_modules/ccxt/build/cleanup-old-tags.js @@ -0,0 +1,94 @@ +"use strict" + +const { execSync } = require ('child_process') +const log = require ('ololog').noLocate +const { groupBy } = require ('../ccxt.js') +const { values } = Object +const assert = require ('assert') + +function cleanupOldTags () { + + const tags = execSync ('git tag').toString ().split ('\n').filter (s => s).map (t => { + + const [major, minor, patch] = t.replace ('v', '').split ('.').map (Number) + + assert (major < 100) + assert (minor < 100) + + return { + key: (major * 100) + minor, + tag: t, + major, + minor, + patch, + } + }) + + const tagsByMajorMinor = values (groupBy (tags, 'key')).sort ((a, b) => a[0].key - b[0].key) + + // Preserve all tags for first 3 minor versions + + for (let i = 0; i < 3; i++) { + + const tags = tagsByMajorMinor.pop () + + if (tags) { + log.green.bright ('Preserving', tags[0].tag, '...', tags[tags.length - 1].tag) + } + } + + // For older versions, leave only "round" numbered versions (1/10th) + + let tagsToDelete = [] + + for (const tags of tagsByMajorMinor) { + + for (const { tag, patch } of tags) { + + if (patch === 1) { + log.green ('Preserving', tag) + + } else { + tagsToDelete.push (tag) + } + } + } + + log.bright.red ('Deleting', tagsToDelete.length, 'tags...') + + if (!process.argv.includes ('--paper')) { + + /* If it happens on a CI server, we don't want it to fail the build because of a super + long execution time (one tag deletion takes ~5 sec...), hence that limit here */ + + if (process.argv.includes ('--limit')) { + tagsToDelete = tagsToDelete.slice (-500) + } + + for (const tag of tagsToDelete) { + + log.dim ('Deleting', tag) + execSync (`git tag -d ${tag} && git push origin :refs/tags/${tag}`) + } + } +} + +// ============================================================================ +// main entry point + +if (require.main === module) { + + // if called directly like `node module` + + cleanupOldTags () + +} else { + + // do nothing if required as a module +} + +// ============================================================================ + +module.exports = { + cleanupOldTags, +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/build/countries.js b/TaskServer/node_modules/ccxt/build/countries.js new file mode 100644 index 000000000..e28cdd635 --- /dev/null +++ b/TaskServer/node_modules/ccxt/build/countries.js @@ -0,0 +1,256 @@ +"use strict"; + +module.exports = { + + AF: 'Afghanistan', + AX: 'Ã…land Islands', + AL: 'Albania', + DZ: 'Algeria', + AS: 'American Samoa', + AD: 'Andorra', + AO: 'Angola', + AI: 'Anguilla', + AQ: 'Antarctica', + AG: 'Antigua and Barbuda', + AR: 'Argentina', + AM: 'Armenia', + AW: 'Aruba', + AU: 'Australia', + AT: 'Austria', + AZ: 'Azerbaijan', + BS: 'Bahamas', + BH: 'Bahrain', + BD: 'Bangladesh', + BB: 'Barbados', + BY: 'Belarus', + BE: 'Belgium', + BZ: 'Belize', + BJ: 'Benin', + BM: 'Bermuda', + BT: 'Bhutan', + BO: 'Bolivia', + BQ: 'Bonaire, Sint Eustatius and Saba', + BA: 'Bosnia and Herzegovina', + BW: 'Botswana', + BV: 'Bouvet Island', + BR: 'Brazil', + IO: 'British Indian Ocean Territory', + BN: 'Brunei Darussalam', + BG: 'Bulgaria', + BF: 'Burkina Faso', + BI: 'Burundi', + KH: 'Cambodia', + CM: 'Cameroon', + CA: 'Canada', + CV: 'Cabo Verde', + KY: 'Cayman Islands', + CF: 'Central African Republic', + TD: 'Chad', + CL: 'Chile', + CN: 'China', + CX: 'Christmas Island', + CC: 'Cocos (Keeling) Islands', + CO: 'Colombia', + KM: 'Comoros', + CG: 'Congo', + CD: 'Congo', + CK: 'Cook Islands', + CR: 'Costa Rica', + CI: 'Côte d\'Ivoire', + HR: 'Croatia', + CU: 'Cuba', + CW: 'Curaçao', + CY: 'Cyprus', + CZ: 'Czech Republic', + DK: 'Denmark', + DJ: 'Djibouti', + DM: 'Dominica', + DO: 'Dominican Republic', + EC: 'Ecuador', + EG: 'Egypt', + SV: 'El Salvador', + GQ: 'Equatorial Guinea', + ER: 'Eritrea', + EE: 'Estonia', + ET: 'Ethiopia', + EU: 'EU', + FK: 'Falkland Islands', + FO: 'Faroe Islands', + FJ: 'Fiji', + FI: 'Finland', + FR: 'France', + GF: 'French Guiana', + PF: 'French Polynesia', + TF: 'French Southern Territories', + GA: 'Gabon', + GM: 'Gambia', + GE: 'Georgia', + DE: 'Germany', + GH: 'Ghana', + GI: 'Gibraltar', + GR: 'Greece', + GL: 'Greenland', + GD: 'Grenada', + GP: 'Guadeloupe', + GU: 'Guam', + GT: 'Guatemala', + GG: 'Guernsey', + GN: 'Guinea', + GW: 'Guinea-Bissau', + GY: 'Guyana', + HT: 'Haiti', + HM: 'Heard Island and McDonald Islands', + VA: 'Holy See', + HN: 'Honduras', + HK: 'Hong Kong', + HU: 'Hungary', + IS: 'Iceland', + IN: 'India', + ID: 'Indonesia', + IR: 'Iran', + IQ: 'Iraq', + IE: 'Ireland', + IM: 'Isle of Man', + IL: 'Israel', + IT: 'Italy', + JM: 'Jamaica', + JP: 'Japan', + JE: 'Jersey', + JO: 'Jordan', + KZ: 'Kazakhstan', + KE: 'Kenya', + KI: 'Kiribati', + KP: 'North Korea (DPRK)', + KR: 'South Korea', + KW: 'Kuwait', + KG: 'Kyrgyzstan', + LA: 'Lao People\'s Democratic Republic', + LV: 'Latvia', + LB: 'Lebanon', + LS: 'Lesotho', + LR: 'Liberia', + LY: 'Libya', + LI: 'Liechtenstein', + LT: 'Lithuania', + LU: 'Luxembourg', + MO: 'Macao', + MK: 'Macedonia', + MG: 'Madagascar', + MW: 'Malawi', + MY: 'Malaysia', + MV: 'Maldives', + ML: 'Mali', + MT: 'Malta', + MH: 'Marshall Islands', + MQ: 'Martinique', + MR: 'Mauritania', + MU: 'Mauritius', + YT: 'Mayotte', + MX: 'Mexico', + FM: 'Micronesia (Federated States of)', + MD: 'Moldova', + MC: 'Monaco', + MN: 'Mongolia', + ME: 'Montenegro', + MS: 'Montserrat', + MA: 'Morocco', + MZ: 'Mozambique', + MM: 'Myanmar', + NA: 'Namibia', + NR: 'Nauru', + NP: 'Nepal', + NL: 'Netherlands', + NC: 'New Caledonia', + NZ: 'New Zealand', + NI: 'Nicaragua', + NE: 'Niger', + NG: 'Nigeria', + NU: 'Niue', + NF: 'Norfolk Island', + MP: 'Northern Mariana Islands', + NO: 'Norway', + OM: 'Oman', + PK: 'Pakistan', + PW: 'Palau', + PS: 'Palestine, State of', + PA: 'Panama', + PG: 'Papua New Guinea', + PY: 'Paraguay', + PE: 'Peru', + PH: 'Philippines', + PN: 'Pitcairn', + PL: 'Poland', + PT: 'Portugal', + PR: 'Puerto Rico', + QA: 'Qatar', + RE: 'Réunion', + RO: 'Romania', + RU: 'Russia', + RW: 'Rwanda', + BL: 'Saint Barthélemy', + SH: 'Saint Helena, Ascension and Tristan da Cunha', + KN: 'Saint Kitts and Nevis', + LC: 'Saint Lucia', + MF: 'Saint Martin (French)', + PM: 'Saint Pierre and Miquelon', + VC: 'St. Vincent & Grenadines', + WS: 'Samoa', + SM: 'San Marino', + ST: 'Sao Tome and Principe', + SA: 'Saudi Arabia', + SN: 'Senegal', + RS: 'Serbia', + SC: 'Seychelles', + SL: 'Sierra Leone', + SG: 'Singapore', + SX: 'Sint Maarten (Dutch)', + SK: 'Slovakia', + SI: 'Slovenia', + SB: 'Solomon Islands', + SO: 'Somalia', + ZA: 'South Africa', + GS: 'South Georgia and the South Sandwich Islands', + SS: 'South Sudan', + ES: 'Spain', + LK: 'Sri Lanka', + SD: 'Sudan', + SR: 'Suriname', + SJ: 'Svalbard and Jan Mayen', + SZ: 'Swaziland', + SE: 'Sweden', + CH: 'Switzerland', + SY: 'Syrian Arab Republic', + TW: 'Taiwan', + TJ: 'Tajikistan', + TZ: 'Tanzania', + TH: 'Thailand', + TL: 'Timor-Leste', + TG: 'Togo', + TK: 'Tokelau', + TO: 'Tonga', + TT: 'Trinidad and Tobago', + TN: 'Tunisia', + TR: 'Turkey', + TM: 'Turkmenistan', + TC: 'Turks and Caicos Islands', + TV: 'Tuvalu', + UG: 'Uganda', + UA: 'Ukraine', + AE: 'United Arab Emirates', + GB: 'UK', + UK: 'UK', + US: 'US', + UM: 'US Minor Islands', + UY: 'Uruguay', + UZ: 'Uzbekistan', + VU: 'Vanuatu', + VE: 'Venezuela', + VN: 'Vietnam', + VG: 'British Virgin Islands', + VI: 'US Virgin Islands', + WF: 'Wallis and Futuna', + EH: 'Western Sahara', + YE: 'Yemen', + ZM: 'Zambia', + ZW: 'Zimbabwe', +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/build/export-exchanges.js b/TaskServer/node_modules/ccxt/build/export-exchanges.js new file mode 100644 index 000000000..cee147922 --- /dev/null +++ b/TaskServer/node_modules/ccxt/build/export-exchanges.js @@ -0,0 +1,370 @@ +// ---------------------------------------------------------------------------- +// Usage: +// +// npm run export-exchanges +// ---------------------------------------------------------------------------- + +"use strict"; + +const fs = require ('fs') + , countries = require ('./countries') + , asTable = require ('as-table') + , execSync = require ('child_process').execSync + , log = require ('ololog').unlimited + , ansi = require ('ansicolor').nice + , { keys, values, entries } = Object + , { replaceInFile } = require ('./fs.js') + +// ---------------------------------------------------------------------------- + +function cloneGitHubWiki (gitWikiPath) { + + if (!fs.existsSync (gitWikiPath)) { + log.bright.cyan ('Cloning ccxt.wiki...') + execSync ('git clone https://github.com/ccxt/ccxt.wiki.git ' + gitWikiPath) + } +} + +// ---------------------------------------------------------------------------- + +function logExportExchanges (filename, regex, replacement) { + log.bright.cyan ('Exporting exchanges →', filename.yellow) + replaceInFile (filename, regex, replacement) +} + +// ---------------------------------------------------------------------------- + +function getIncludedExchangeIds () { + + const includedIds = fs.readFileSync ('exchanges.cfg') + .toString () // Buffer → String + .split ('\n') // String → Array + .map (line => line.split ('#')[0].trim ()) // trim comments + .filter (exchange => exchange); // filter empty lines + + const isIncluded = (id) => ((includedIds.length === 0) || includedIds.includes (id)) + + const ids = fs.readdirSync ('./js/') + .filter (file => file.match (/[a-zA-Z0-9_-]+.js$/)) + .map (file => file.slice (0, -3)) + .filter (isIncluded); + + return ids +} + +// ---------------------------------------------------------------------------- + +function exportExchanges (replacements) { + + log.bright.yellow ('Exporting exchanges...') + + replacements.forEach (({ file, regex, replacement }) => { + logExportExchanges (file, regex, replacement) + }) + + log.bright.green ('Base sources updated successfully.') +} + +// ---------------------------------------------------------------------------- + +function createExchanges (ids) { + + const ccxt = require ('../ccxt.js') + + const createExchange = (id) => { + ccxt[id].prototype.checkRequiredDependencies = () => {} // suppress it + return new (ccxt)[id] () + } + + return ccxt.indexBy (ids.map (createExchange), 'id') +} + +// // ---------------------------------------------------------------------------- +// // strategically placed exactly here (we can require it AFTER the export) +// const ccxt = require ('../ccxt.js') +// // ---------------------------------------------------------------------------- +// // create exchanges +// const createExchange = (id) => { +// ccxt[id].prototype.checkRequiredDependencies = () => {} // suppress it +// return new (ccxt)[id] () +// } +// const exchanges = ccxt.indexBy (ids.map (createExchange), 'id') + +// ---------------------------------------------------------------------------- +// TODO: REWRITE THIS ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ + +function exportSupportedAndCertifiedExchanges (exchanges, wikiPath) { + + // ............................................................................ + // markup constants and helper functions + + const countryName = (code) => countries[code] || code + + const ccxtCertifiedBadge = '[![CCXT Certified](https://img.shields.io/badge/CCXT-certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification)' + const logoHeading = ' '.repeat (7) + 'logo' + ' '.repeat (7) + const tableHeadings = [ logoHeading, 'id', 'name', 'ver', 'doc', 'certified', ] + const exchangesByCountryHeading = [ 'country / region', ... tableHeadings ] + + // ---------------------------------------------------------------------------- + // list all supported exchanges + + const exchangesNotListedInDocs = [] + + let tableData = values (exchanges) + .filter (exchange => !exchangesNotListedInDocs.includes (exchange.id)) + .map (exchange => { + let logo = exchange.urls['logo'] + let website = Array.isArray (exchange.urls.www) ? exchange.urls.www[0] : exchange.urls.www + let url = exchange.urls.referral || website + let countries = Array.isArray (exchange.countries) ? exchange.countries.map (countryName).join (', ') : countryName (exchange.countries) + let doc = Array.isArray (exchange.urls.doc) ? exchange.urls.doc[0] : exchange.urls.doc + let version = exchange.version ? exchange.version : '\*' + let matches = version.match (/[^0-9]*([0-9].*)/) + if (matches) { + version = matches[1]; + } + return [ + '[![' + exchange.id + '](' + logo + ')](' + url + ')', + exchange.id, + '[' + exchange.name + '](' + url + ')', + version, + '[API](' + doc + ')', + exchange.certified ? ccxtCertifiedBadge : '', + countries, + ] + }) + + // prepend the table header + tableData.splice (0, 0, tableHeadings) + + function makeTable (jsonArray) { + let table = asTable.configure ({ 'delimiter': ' | ' }) (jsonArray) + let lines = table.split ("\n") + lines.splice (1,0, lines[0].replace (/[^\|]/g, '-')) + let headerLine = lines[1].split ('|') + headerLine[3] = ':' + headerLine[3].slice (1, headerLine[3].length - 1) + ':' + headerLine[4] = ':' + headerLine[4].slice (1, headerLine[4].length - 1) + ':' + lines[1] = headerLine.join ('|') + return lines.map (line => '|' + line + '|').join ("\n") + } + + const exchangesTable = makeTable (tableData) + const numExchanges = keys (exchanges).length + const beginning = "The ccxt library currently supports the following " + const ending = " cryptocurrency exchange markets and trading APIs:\n\n" + const totalString = beginning + numExchanges + ending + const allExchanges = totalString + exchangesTable + "$1" + const allExchangesRegex = new RegExp ("[^\n]+[\n]{2}\\|[^`]+\\|([\n][\n]|[\n]$|$)", 'm') + logExportExchanges ('README.md', allExchangesRegex, allExchanges) + logExportExchanges (wikiPath + '/Manual.md', allExchangesRegex, allExchanges) + logExportExchanges (wikiPath + '/Exchange-Markets.md', allExchangesRegex, allExchanges) + + const certifiedFieldIndex = tableHeadings.indexOf ('certified') + const certified = tableData.filter ((x) => x[certifiedFieldIndex] !== '' ) + const certifiedExchangesRegex = new RegExp ("^(## Certified Cryptocurrency Exchanges\n{3})(?:\\|.+\\|$\n)+", 'm') + const certifiedExchangesTable = makeTable (certified) + const certifiedExchanges = '$1' + certifiedExchangesTable + "\n" + logExportExchanges ('README.md', certifiedExchangesRegex, certifiedExchanges) + + + let exchangesByCountries = [] + keys (countries).forEach (code => { + let country = countries[code] + let result = [] + keys (exchanges).forEach (id => { + let exchange = exchanges[id] + let logo = exchange.urls['logo'] + let website = Array.isArray (exchange.urls.www) ? exchange.urls.www[0] : exchange.urls.www + let url = exchange.urls.referral || website + let doc = Array.isArray (exchange.urls.doc) ? exchange.urls.doc[0] : exchange.urls.doc + let version = exchange.version ? exchange.version : '\*' + let matches = version.match (/[^0-9]*([0-9].*)/) + if (matches) + version = matches[1]; + let shouldInclude = false + if (Array.isArray (exchange.countries)) { + if (exchange.countries.indexOf (code) > -1) + shouldInclude = true + } else { + if (code == exchange.countries) + shouldInclude = true + } + if (shouldInclude) { + let entry = [ + country, + '[![' + exchange.id + '](' + logo + ')](' + url + ')', + exchange.id, + '[' + exchange.name + '](' + url + ')', + version, + '[API](' + doc + ')', + // doesn't fit in width + // exchange.certified ? ccxtCertifiedBadge : '', + ] + result.push (entry) + } + }) + exchangesByCountries = exchangesByCountries.concat (result) + }); + + let countryKeyIndex = exchangesByCountryHeading.indexOf ('country / region') + exchangesByCountries = exchangesByCountries.sort ((a, b) => { + let countryA = a[countryKeyIndex].toLowerCase () + let countryB = b[countryKeyIndex].toLowerCase () + if (countryA > countryB) { + return 1 + } else if (countryA < countryB) { + return -1; + } else { + if (a['id'] > b['id']) + return 1; + else if (a['id'] < b['id']) + return -1; + else + return 0; + } + }) + + exchangesByCountries.splice (0, 0, exchangesByCountryHeading) + let lines = makeTable (exchangesByCountries) + let result = "# Exchanges By Country\n\nThe ccxt library currently supports the following cryptocurrency exchange markets and trading APIs:\n\n" + lines + "\n\n" + let filename = wikiPath + '/Exchange-Markets-By-Country.md' + fs.truncateSync (filename) + fs.writeFileSync (filename, result) +} + +// TODO: REWRITE THIS ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ +// ---------------------------------------------------------------------------- + +function exportExchangeIdsToExchangesJson (exchanges) { + log.bright ('Exporting exchange ids to'.cyan, 'exchanges.json'.yellow) + fs.writeFileSync ('exchanges.json', JSON.stringify ({ ids: keys (exchanges) }, null, 4)) +} + +// ---------------------------------------------------------------------------- + +function exportWikiToGitHub (wikiPath, gitWikiPath) { + + log.bright.cyan ('Exporting wiki to GitHub') + + const ccxtWikiFiles = { + 'README.md': 'Home.md', + 'Install.md': 'Install.md', + 'Manual.md': 'Manual.md', + 'Exchange-Markets.md': 'Exchange-Markets.md', + 'Exchange-Markets-By-Country.md': 'Exchange-Markets-By-Country.md', + } + + for (const [ sourceFile, destinationFile ] of entries (ccxtWikiFiles)) { + + const sourcePath = wikiPath + '/' + sourceFile + const destinationPath = gitWikiPath + '/' + destinationFile + log.bright.cyan ('Exporting', sourcePath.yellow, '→', destinationPath.yellow) + fs.writeFileSync (destinationPath, fs.readFileSync (sourcePath)) + } +} + +// ---------------------------------------------------------------------------- + +function exportKeywordsToPackageJson (exchanges) { + + log.bright ('Exporting exchange keywords to'.cyan, 'package.json'.yellow) + + // const packageJSON = require ('../package.json') + const packageJSON = JSON.parse (fs.readFileSync ('./package.json')) + const keywords = new Set (packageJSON.keywords) + + for (const ex of values (exchanges)) { + for (const url of Array.isArray (ex.urls.www) ? ex.urls.www : [ex.urls.www]) { + keywords.add (url.replace (/(http|https):\/\/(www\.)?/, '').replace (/\/.*/, '')) + } + keywords.add (ex.name) + } + + packageJSON.keywords = [...keywords] + fs.writeFileSync ('./package.json', JSON.stringify (packageJSON, null, 2)) +} + +// ---------------------------------------------------------------------------- + +function exportEverything () { + + const wikiPath = 'wiki' + , gitWikiPath = 'build/ccxt.wiki' + + cloneGitHubWiki (gitWikiPath) + + const ids = getIncludedExchangeIds () + + const replacements = [ + { + file: './ccxt.js', + regex: /(?:const|var)\s+exchanges\s+\=\s+\{[^\}]+\}/, + replacement: "const exchanges = {\n" + ids.map (id => (" '" + id + "':").padEnd (30) + " require ('./js/" + id + ".js'),").join ("\n") + " \n}", + }, + { + file: './python/ccxt/__init__.py', + regex: /exchanges \= \[[^\]]+\]/, + replacement: "exchanges = [\n" + " '" + ids.join ("',\n '") + "'," + "\n]", + }, + { + file: './python/ccxt/__init__.py', + regex: /(?:from ccxt\.[^\.]+ import [^\s]+\s+\# noqa\: F401[\r]?[\n])+[\r]?[\n]exchanges/, + replacement: ids.map (id => ('from ccxt.' + id + ' import ' + id).padEnd (60) + '# noqa: F401').join ("\n") + "\n\nexchanges", + }, + { + file: './python/ccxt/async_support/__init__.py', + regex: /(?:from ccxt\.async_support\.[^\.]+ import [^\s]+\s+\# noqa\: F401[\r]?[\n])+[\r]?[\n]exchanges/, + replacement: ids.map (id => ('from ccxt.async_support.' + id + ' import ' + id).padEnd (74) + '# noqa: F401').join ("\n") + "\n\nexchanges", + }, + { + file: './python/ccxt/async_support/__init__.py', + regex: /exchanges \= \[[^\]]+\]/, + replacement: "exchanges = [\n" + " '" + ids.join ("',\n '") + "'," + "\n]", + }, + { + file: './php/base/Exchange.php', + regex: /public static \$exchanges \= array\s*\([^\)]+\)/, + replacement: "public static $exchanges = array(\n '" + ids.join ("',\n '") + "',\n )", + }, + ] + + exportExchanges (replacements) + + // strategically placed exactly here (we can require it AFTER the export) + const exchanges = createExchanges (ids) + + exportSupportedAndCertifiedExchanges (exchanges, wikiPath) + exportExchangeIdsToExchangesJson (exchanges) + exportWikiToGitHub (wikiPath, gitWikiPath) + exportKeywordsToPackageJson (exchanges) + + log.bright.green ('Exported successfully.') +} + +// ============================================================================ +// main entry point + +if (require.main === module) { + + // if called directly like `node module` + + exportEverything () + +} else { + + // do nothing if required as a module +} + +// ============================================================================ + +module.exports = { + cloneGitHubWiki, + getIncludedExchangeIds, + exportExchanges, + createExchanges, + exportSupportedAndCertifiedExchanges, + exportExchangeIdsToExchangesJson, + exportWikiToGitHub, + exportKeywordsToPackageJson, + exportEverything, +} diff --git a/TaskServer/node_modules/ccxt/build/fs.js b/TaskServer/node_modules/ccxt/build/fs.js new file mode 100644 index 000000000..3ef2e0690 --- /dev/null +++ b/TaskServer/node_modules/ccxt/build/fs.js @@ -0,0 +1,49 @@ +"use strict"; + +const fs = require ('fs') + , path = require ('path') + +function replaceInFile (filename, regex, replacement) { + const contents = fs.readFileSync (filename, 'utf8') + const newContents = contents.replace (regex, replacement) + fs.truncateSync (filename) + fs.writeFileSync (filename, newContents) +} + +function copyFile (oldName, newName) { + const contents = fs.readFileSync (oldName, 'utf8') + fs.truncateSync (newName) + fs.writeFileSync (newName, contents) +} + +function overwriteFile (filename, contents) { + // log.cyan ('Overwriting → ' + filename.yellow) + fs.closeSync (fs.openSync (filename, 'a')); + fs.truncateSync (filename) + fs.writeFileSync (filename, contents) +} + +function createFolder (folder) { + try { + fs.mkdirSync (folder) + } catch (err) { + if (err.code !== 'EEXIST') { + throw err + } + } +} + +function createFolderRecursively (folder) { + const parts = folder.split (path.sep) + for (let i = 1; i <= parts.length; i++) { + createFolder (path.join.apply (null, parts.slice (0, i))) + } +} + +module.exports = { + replaceInFile, + copyFile, + overwriteFile, + createFolder, + createFolderRecursively, +} diff --git a/TaskServer/node_modules/ccxt/build/transpile.js b/TaskServer/node_modules/ccxt/build/transpile.js new file mode 100644 index 000000000..64bf249cd --- /dev/null +++ b/TaskServer/node_modules/ccxt/build/transpile.js @@ -0,0 +1,1231 @@ +// --------------------------------------------------------------------------- +// Usage: npm run transpile +// --------------------------------------------------------------------------- + +"use strict"; + +const fs = require ('fs') + , log = require ('ololog') + , _ = require ('ansicolor').nice + , errors = require ('../js/base/errors.js') + , { unCamelCase, precisionConstants, safeString } = require ('../js/base/functions.js') + , { basename } = require ('path') + , { + createFolderRecursively, + replaceInFile, + overwriteFile, + } = require ('./fs.js') + +class Transpiler { + + getCommonRegexes () { + return [ + + [ /\.deepExtend\s/g, '.deep_extend'], + [ /\.safeFloat2\s/g, '.safe_float_2'], + [ /\.safeInteger2\s/g, '.safe_integer_2'], + [ /\.safeIntegerProduct2\s/g, '.safe_integer_product_2'], + [ /\.safeTimestamp2\s/g, '.safe_timestamp_2'], + [ /\.safeString2\s/g, '.safe_string_2'], + [ /\.safeStringLower2\s/g, '.safe_string_lower_2'], + [ /\.safeStringUpper2\s/g, '.safe_string_upper_2'], + [ /\.safeValue2\s/g, '.safe_value_2'], + [ /\.safeFloat\s/g, '.safe_float'], + [ /\.safeInteger\s/g, '.safe_integer'], + [ /\.safeIntegerProduct\s/g, '.safe_integer_product'], + [ /\.safeTimestamp\s/g, '.safe_timestamp'], + [ /\.safeString\s/g, '.safe_string'], + [ /\.safeStringLower\s/g, '.safe_string_lower'], + [ /\.safeStringUpper\s/g, '.safe_string_upper'], + [ /\.safeValue\s/g, '.safe_value'], + [ /\.inArray\s/g, '.in_array'], + [ /\.toArray\s/g, '.to_array'], + [ /\.isEmpty\s/g, '.is_empty'], + [ /\.arrayConcat\s/g, '.array_concat'], + [ /\.binaryConcat\s/g, '.binary_concat'], + [ /\.binaryConcatArray\s/g, '.binary_concat_array'], + [ /\.binaryToString\s/g, '.binary_to_string' ], + [ /\.precisionFromString\s/g, '.precision_from_string'], + [ /\.implodeParams\s/g, '.implode_params'], + [ /\.extractParams\s/g, '.extract_params'], + [ /\.parseBalance\s/g, '.parse_balance'], + [ /\.parseOHLCVs\s/g, '.parse_ohlcvs'], + [ /\.parseOHLCV\s/g, '.parse_ohlcv'], + [ /\.parseDate\s/g, '.parse_date'], + [ /\.parseLedgerEntry\s/g, '.parse_ledger_entry'], + [ /\.parseLedger\s/g, '.parse_ledger'], + [ /\.parseTicker\s/g, '.parse_ticker'], + [ /\.parseTimeframe\s/g, '.parse_timeframe'], + [ /\.parseTradesData\s/g, '.parse_trades_data'], + [ /\.parseTrades\s/g, '.parse_trades'], + [ /\.parseTrade\s/g, '.parse_trade'], + [ /\.parseTradingViewOHLCV\s/g, '.parse_trading_view_ohlcv'], + [ /\.parseTransaction\s/g, '.parse_transaction'], + [ /\.parseTransactions\s/g, '.parse_transactions'], + [ /\.parseOrderBook\s/g, '.parse_order_book'], + [ /\.parseBidsAsks\s/g, '.parse_bids_asks'], + [ /\.parseBidAsk\s/g, '.parse_bid_ask'], + [ /\.parseOrders\s/g, '.parse_orders'], + [ /\.parseOrderStatus\s/g, '.parse_order_status'], + [ /\.parseOrder\s/g, '.parse_order'], + [ /\.parseJson\s/g, '.parse_json'], + [ /\.filterByArray\s/g, '.filter_by_array'], + [ /\.filterBySymbolSinceLimit\s/g, '.filter_by_symbol_since_limit'], + [ /\.filterBySinceLimit\s/g, '.filter_by_since_limit'], + [ /\.filterBySymbol\s/g, '.filter_by_symbol'], + [ /\.getVersionString\s/g, '.get_version_string'], + [ /\.indexBy\s/g, '.index_by'], + [ /\.sortBy\s/g, '.sort_by'], + [ /\.filterBy\s/g, '.filter_by'], + [ /\.groupBy\s/g, '.group_by'], + [ /\.marketIds\s/g, '.market_ids'], + [ /\.marketId\s/g, '.market_id'], + [ /\.fetchFundingFees\s/g, '.fetch_funding_fees'], + [ /\.fetchTradingFees\s/g, '.fetch_trading_fees'], + [ /\.fetchTradingFee\s/g, '.fetch_trading_fee'], + [ /\.fetchFees\s/g, '.fetch_fees'], + [ /\.fetchL2OrderBook\s/g, '.fetch_l2_order_book'], + [ /\.fetchOrderBook\s/g, '.fetch_order_book'], + [ /\.fetchMyTrades\s/g, '.fetch_my_trades'], + [ /\.fetchOrderStatus\s/g, '.fetch_order_status'], + [ /\.fetchOpenOrders\s/g, '.fetch_open_orders'], + [ /\.fetchOpenOrder\s/g, '.fetch_open_order'], + [ /\.fetchOrders\s/g, '.fetch_orders'], + [ /\.fetchOrderTrades\s/g, '.fetch_order_trades'], + [ /\.fetchOrder\s/g, '.fetch_order'], + [ /\.fetchBidsAsks\s/g, '.fetch_bids_asks'], + [ /\.fetchTickers\s/g, '.fetch_tickers'], + [ /\.fetchTicker\s/g, '.fetch_ticker'], + [ /\.fetchCurrencies\s/g, '.fetch_currencies'], + [ /\.numberToString\s/g, '.number_to_string' ], + [ /\.decimalToPrecision\s/g, '.decimal_to_precision'], + [ /\.priceToPrecision\s/g, '.price_to_precision'], + [ /\.amountToPrecision\s/g, '.amount_to_precision'], + [ /\.amountToLots\s/g, '.amount_to_lots'], + [ /\.feeToPrecision\s/g, '.fee_to_precision'], + [ /\.currencyToPrecision\s/g, '.currency_to_precision'], + [ /\.costToPrecision\s/g, '.cost_to_precision'], + [ /\.commonCurrencyCode\s/g, '.common_currency_code'], + [ /\.loadAccounts\s/g, '.load_accounts'], + [ /\.loadFees\s/g, '.load_fees'], + [ /\.loadMarkets\s/g, '.load_markets'], + [ /\.fetchMarkets\s/g, '.fetch_markets'], + [ /\.appendInactiveMarkets\s/g, '.append_inactive_markets'], + [ /\.fetchCategories\s/g, '.fetch_categories'], + [ /\.calculateFee\s/g, '.calculate_fee'], + [ /\.editLimitBuyOrder\s/g, '.edit_limit_buy_order'], + [ /\.editLimitSellOrder\s/g, '.edit_limit_sell_order'], + [ /\.editLimitOrder\s/g, '.edit_limit_order'], + [ /\.editOrder\s/g, '.edit_order'], + [ /\.encodeURIComponent\s/g, '.encode_uri_component'], + [ /\.throwExceptionOnError\s/g, '.throw_exception_on_error'], + [ /\.handleErrors\s/g, '.handle_errors'], + [ /\.checkRequiredCredentials\s/g, '.check_required_credentials'], + [ /\.checkRequiredDependencies\s/g, '.check_required_dependencies'], + [ /\.checkAddress\s/g, '.check_address'], + [ /\.convertTradingViewToOHLCV\s/g, '.convert_trading_view_to_ohlcv'], + [ /\.convertOHLCVToTradingView\s/g, '.convert_ohlcv_to_trading_view'], + [ /\.signBodyWithSecret\s/g, '.sign_body_with_secret'], + [ /\.isJsonEncodedObject\s/g, '.is_json_encoded_object'], + [ /\.setSandboxMode\s/g, '.set_sandbox_mode'], + [ /\.safeCurrencyCode\s/g, '.safe_currency_code'], + [ /\.roundTimeframe/g, '.round_timeframe'], + [ /\.integerDivide/g, '.integer_divide'], + [ /\.integerModulo/g, '.integer_modulo'], + [ /\.integerPow/g, '.integer_pow'], + [ /\.findBroadlyMatchedKey\s/g, '.find_broadly_matched_key'], + [ /\.throwBroadlyMatchedException\s/g, '.throw_broadly_matched_exception'], + [ /\.throwExactlyMatchedException\s/g, '.throw_exactly_matched_exception'], + [ /errorHierarchy/g, 'error_hierarchy'], + [ /\.base16ToBinary/g, '.base16_to_binary'], + [ /\'use strict\';?\s+/g, '' ], + [ /\.urlencodeWithArrayRepeat\s/g, '.urlencode_with_array_repeat' ], + [ /\.bind\s*\(this\)([,;])$/mg, '$1' ], + [ /\.call\s*\(this, /g, '(' ] + ] + } + + getPythonRegexes () { + + return [ + + [ /Array\.isArray\s*\(([^\)]+)\)/g, 'isinstance($1, list)' ], + [ /([^\(\s]+)\s+instanceof\s+([^\)\s]+)/g, 'isinstance($1, $2)' ], + + [ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'undefined\'/g, '$1[$2] is None' ], + [ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'undefined\'/g, '$1[$2] is not None' ], + [ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'undefined\'/g, '$1 is None' ], + [ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'undefined\'/g, '$1 is not None' ], + [ /typeof\s+(.+?)\s+\=\=\=?\s+\'undefined\'/g, '$1 is None' ], + [ /typeof\s+(.+?)\s+\!\=\=?\s+\'undefined\'/g, '$1 is not None' ], + + [ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+undefined/g, '$1[$2] is None' ], + [ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+undefined/g, '$1[$2] is not None' ], + [ /([^\s]+)\s+\=\=\=?\s+undefined/g, '$1 is None' ], + [ /([^\s]+)\s+\!\=\=?\s+undefined/g, '$1 is not None' ], + [ /(.+?)\s+\=\=\=?\s+undefined/g, '$1 is None' ], + [ /(.+?)\s+\!\=\=?\s+undefined/g, '$1 is not None' ], + + [ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'string\'/g, 'isinstance($1[$2], basestring)' ], + [ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'string\'/g, 'not isinstance($1[$2], basestring)' ], + [ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'string\'/g, 'isinstance($1, basestring)' ], + [ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'string\'/g, 'not isinstance($1, basestring)' ], + [ /undefined/g, 'None' ], + [ /\=\=\=?/g, '==' ], + [ /\!\=\=?/g, '!=' ], + [ /this\.stringToBinary\s*\((.*)\)/g, '$1' ], + [ /this\.stringToBase64\s/g, 'base64.b64encode' ], + [ /this\.binaryToBase16\s/g, 'base64.b16encode' ], + [ /this\.base64ToBinary\s/g, 'base64.b64decode' ], + [ /\.shift\s*\(\)/g, '.pop(0)' ], + [ /Number\.MAX_SAFE_INTEGER/g, 'float(\'inf\')'] + + // insert common regexes in the middle (critical) + ].concat (this.getCommonRegexes ()).concat ([ + + // [ /this\.urlencode\s/g, '_urlencode.urlencode ' ], // use self.urlencode instead + [ /this\./g, 'self.' ], + [ /([^a-zA-Z\'])this([^a-zA-Z])/g, '$1self$2' ], + [ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\[\s*([^\]]+)\s\]/g, '$1$2' ], + [ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\{\s*([^\}]+)\s\}\s\=\s([^\;]+)/g, '$1$2 = (lambda $2: ($2))(**$3)' ], + [ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ], + [ /Object\.keys\s*\((.*)\)\.length/g, '$1' ], + [ /Object\.keys\s*\((.*)\)/g, 'list($1.keys())' ], + [ /\[([^\]]+)\]\.join\s*\(([^\)]+)\)/g, "$2.join([$1])" ], + [ /hash \(([^,]+)\, \'(sha[0-9])\'/g, "hash($1, '$2'" ], + [ /hmac \(([^,]+)\, ([^,]+)\, \'(md5)\'/g, 'hmac($1, $2, hashlib.$3' ], + [ /hmac \(([^,]+)\, ([^,]+)\, \'(sha[0-9]+)\'/g, 'hmac($1, $2, hashlib.$3' ], + [ /throw new ([\S]+) \((.*)\)/g, 'raise $1($2)'], + [ /throw ([\S]+)/g, 'raise $1'], + [ /try {/g, 'try:'], + [ /\}\s+catch \(([\S]+)\) {/g, 'except Exception as $1:'], + [ /([\s\(])extend(\s)/g, '$1self.extend$2' ], + [ /\} else if/g, 'elif' ], + [ /else if/g, 'elif' ], + [ /if\s+\((.*)\)\s+\{/g, 'if $1:' ], + [ /if\s+\((.*)\)\s*[\n]/g, "if $1:\n" ], + [ /\}\s*else\s*\{/g, 'else:' ], + [ /else\s*[\n]/g, "else:\n" ], + [ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(?:\<=|\>=|<|>)\s*(.*)\.length\s*\;[^\)]+\)\s*{/g, 'for $1 in range($2, len($3)):'], + [ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(?:\<=|\>=|<|>)\s*(.*)\s*\;[^\)]+\)\s*{/g, 'for $1 in range($2, $3):'], + [ /\s\|\|\s/g, ' or ' ], + [ /\s\&\&\s/g, ' and ' ], + [ /\!([^\='"])/g, 'not $1'], + [ /([^\s(]+)\.length/g, 'len($1)' ], + [ /\.push\s*\(([\s\S]+?)\);/g, '.append($1);' ], + [ /^(\s*}\s*$)+/gm, '' ], + [ /\;(\s+?\/\/.+?)/g, '$1' ], + [ /\;$/gm, '' ], + [ /\.toUpperCase\s*/g, '.upper' ], + [ /\.toLowerCase\s*/g, '.lower' ], + [ /JSON\.stringify\s*/g, 'json.dumps' ], + [ /JSON\.parse\s*/g, "json.loads" ], + // [ /([^\(\s]+)\.includes\s+\(([^\)]+)\)/g, '$2 in $1' ], + // [ /\'%([^\']+)\'\.sprintf\s*\(([^\)]+)\)/g, "'{:$1}'.format($2)" ], + [ /([^\s]+)\.toFixed\s*\(([0-9]+)\)/g, "'{:.$2f}'.format($1)" ], + [ /([^\s]+)\.toFixed\s*\(([^\)]+)\)/g, "('{:.' + str($2) + 'f}').format($1)" ], + [ /parseFloat\s*/g, 'float'], + [ /parseInt\s*/g, 'int'], + [ /self\[([^\]+]+)\]/g, 'getattr(self, $1)' ], + [ /([^\s]+)\.slice \(([^\,\)]+)\,\s?([^\)]+)\)/g, '$1[$2:$3]' ], + [ /([^\s]+)\.slice \(([^\)\:]+)\)/g, '$1[$2:]' ], + [ /Math\.floor\s*\(([^\)]+)\)/g, 'int(math.floor($1))' ], + [ /Math\.abs\s*\(([^\)]+)\)/g, 'abs($1)' ], + [ /Math\.pow\s*\(([^\)]+)\)/g, 'math.pow($1)' ], + [ /Math\.round\s*\(([^\)]+)\)/g, 'int(round($1))' ], + [ /Math\.ceil\s*\(([^\)]+)\)/g, 'int(math.ceil($1))' ], + [ /Math\.log/g, 'math.log' ], + [ /([a-zA-Z0-9_\.]*\([^\)]+\)|[^\s]+)\s*\?\s*([^\:]+)\s+\:\s*([^\n]+)/g, '$2 if $1 else $3'], + [ /(^|\s)\/\//g, '$1#' ], + [ /([^\n\s]) #/g, '$1 #' ], // PEP8 E261 + [ /\.indexOf/g, '.find'], + [ /(\s|\()true/g, '$1True'], + [ /(\s|\()false/g, '$1False'], + [ /([^\s]+\s*\(\))\.toString\s+\(\)/g, 'str($1)' ], + [ /([^\s]+)\.toString \(\)/g, 'str($1)' ], + [ /([^\s]+)\.join\s*\(\s*([^\)\[\]]+?)\s*\)/g, '$2.join($1)' ], + [ /Math\.(max|min)\s/g, '$1' ], + [ / = new /g, ' = ' ], // python does not have a 'new' keyword + [ /console\.log\s/g, 'print' ], + [ /process\.exit\s+/g, 'sys.exit' ], + [ /([^:+=\/\*\s-]+) \(/g, '$1(' ], // PEP8 E225 remove whitespaces before left ( round bracket + [ /\sand\(/g, ' and (' ], + [ /\sor\(/g, ' or (' ], + [ /\snot\(/g, ' not (' ], + [ /\[ /g, '[' ], // PEP8 E201 remove whitespaces after left [ square bracket + [ /\{ /g, '{' ], // PEP8 E201 remove whitespaces after left { bracket + [ /(?<=[^\s#]) \]/g, ']' ], // PEP8 E202 remove whitespaces before right ] square bracket + [ /(?<=[^\s#]) \}/g, '}' ], // PEP8 E202 remove whitespaces before right } bracket + [ /([^a-z])(elif|if|or|else)\(/g, '$1$2 \(' ], // a correction for PEP8 E225 side-effect for compound and ternary conditionals + [ /\=\=\sTrue/g, 'is True' ], // a correction for PEP8 E712, it likes "is True", not "== True" + [ /\sdelete\s/g, ' del ' ], + [ /(?' ], + [ / this;/g, ' $this;' ], + [ /([^'])this_\./g, '$1$this_->' ], + [ /\{\}/g, 'array()' ], + [ /\[\]/g, 'array()' ], + + // add {}-array syntax conversions up to 20 levels deep in the same line + ]).concat ([ ... Array (20) ].map (x => [ /\{([^\n\}]+)\}/g, 'array($1)' ] )).concat ([ + [ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\[\s*([^\]]+)\s\]/g, '$1list($2)' ], + [ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\{\s*([^\}]+)\s\}/g, '$1array_values(list($2))' ], + [ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ], + [ /Object\.keys\s*\((.*)\)\.length/g, '$1' ], + [ /Object\.keys\s*\((.*)\)/g, 'is_array($1) ? array_keys($1) : array()' ], + [ /([^\s]+\s*\(\))\.toString \(\)/g, '(string) $1' ], + [ /([^\s]+)\.toString \(\)/g, '(string) $1' ], + [ /throw new Error \((.*)\)/g, 'throw new \\Exception($1)' ], + [ /throw new ([\S]+) \((.*)\)/g, 'throw new $1($2)' ], + [ /throw ([\S]+)\;/g, 'throw $$$1;' ], + [ '([^a-z]+) (' + Object.keys (errors).join ('|') + ')([^\\s])', "$1 '\\\\ccxt\\\\$2'$3" ], + [ /\}\s+catch \(([\S]+)\) {/g, '} catch (Exception $$$1) {' ], + [ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(\<=|\>=|<|>)\s*(.*)\.length\s*\;([^\)]+)\)\s*{/g, 'for ($1 = $2; $1 $3 count($4);$5) {' ], + [ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(\<=|\>=|<|>)\s*(.*)\s*\;([^\)]+)\)\s*{/g, 'for ($1 = $2; $1 $3 $4;$5) {' ], + [ /([^\s]+)\.length\;/g, 'is_array($1) ? count($1) : 0;' ], + [ /([^\s\(]+)\.length/g, 'strlen($1)' ], + [ /\.push\s*\(([\s\S]+?)\)\;/g, '[] = $1;' ], + [ /(\s)await(\s)/g, '$1' ], + [ /([\S])\: /g, '$1 => ' ], + + // add {}-array syntax conversions up to 20 levels deep + ]).concat ([ ... Array (20) ].map (x => [ /\{([^\;\{]+?)\}([^\s])/g, 'array($1)$2' ])).concat ([ + + [ /\[\s*([^\]]+?)\s*\]\.join\s*\(\s*([^\)]+?)\s*\)/g, "implode($2, array($1))" ], + + // add []-array syntax conversions up to 20 levels deep + ]).concat ([ ... Array (20) ].map (x => [ /\[(\s[^\]]+?\s)\]/g, 'array($1)' ])).concat ([ + + [ /JSON\.stringify/g, 'json_encode' ], + [ /JSON\.parse\s+\(([^\)]+)\)/g, 'json_decode($1, $$as_associative_array = true)' ], + [ /([^\(\s]+)\.includes\s+\(([^\)]+)\)/g, 'mb_strpos($1, $2)' ], + // [ /\'([^\']+)\'\.sprintf\s*\(([^\)]+)\)/g, "sprintf ('$1', $2)" ], + [ /([^\s]+)\.toFixed\s*\(([0-9]+)\)/g, "sprintf('%.$2f', $1)" ], + [ /([^\s]+)\.toFixed\s*\(([^\)]+)\)/g, "sprintf('%.' . $2 . 'f', $1)" ], + [ /parseFloat\s/g, 'floatval '], + [ /parseInt\s/g, 'intval '], + [ / \+ /g, ' . ' ], + [ / \+\= /g, ' .= ' ], + [ /([^\s\(]+(?:\s*\(.+\))?)\.toUpperCase\s*\(\)/g, 'strtoupper($1)' ], + [ /([^\s\(]+(?:\s*\(.+\))?)\.toLowerCase\s*\(\)/g, 'strtolower($1)' ], + [ /([^\s\(]+(?:\s*\(.+\))?)\.replace\s*\(([^\)]+)\)/g, 'str_replace($2, $1)' ], + [ /this\[([^\]+]+)\]/g, '$$this->$$$1' ], + [ /([^\s\(]+).slice \(([^\)\:,]+)\)/g, 'mb_substr($1, $2)' ], + [ /([^\s\(]+).slice \(([^\,\)]+)\,\s*([^\)]+)\)/g, 'mb_substr($1, $2, $3 - $2)' ], + [ /([^\s\(]+).split \(('[^']*'|[^\,]+?)\)/g, 'explode($2, $1)' ], + [ /Math\.floor\s*\(([^\)]+)\)/g, '(int) floor($1)' ], + [ /Math\.abs\s*\(([^\)]+)\)/g, 'abs($1)' ], + [ /Math\.round\s*\(([^\)]+)\)/g, '(int) round($1)' ], + [ /Math\.ceil\s*\(([^\)]+)\)/g, '(int) ceil($1)' ], + [ /Math\.pow\s*\(([^\)]+)\)/g, 'pow($1)' ], + [ /Math\.log/g, 'log' ], + [ /([^\(\s]+)\s+%\s+([^\s\,\;\)]+)/g, 'fmod($1, $2)' ], + [ /\(([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\>\=\s*0\)/g, '(mb_strpos($1, $2) !== false)' ], + [ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\>\=\s*0/g, 'mb_strpos($1, $2) !== false' ], + [ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)/g, 'mb_strpos($1, $2)' ], + [ /\(([^\s\(]+)\sin\s([^\)]+)\)/g, '(is_array($2) && array_key_exists($1, $2))' ], + [ /([^\s]+)\.join\s*\(\s*([^\)]+?)\s*\)/g, 'implode($2, $1)' ], + [ 'new ccxt\\.', 'new \\ccxt\\' ], // a special case for test_exchange_datetime_functions.php (and for other files, maybe) + [ /Math\.(max|min)/g, '$1' ], + [ /console\.log/g, 'var_dump'], + [ /process\.exit/g, 'exit'], + [ /super\./g, 'parent::'], + [ /\sdelete\s([^\n]+)\;/g, ' unset($1);' ], + [ /\~([a-zA-Z0-9_]+?)\~/g, '{$1}' ], // resolve the "arrays vs url params" conflict (both are in {}-brackets) + ]) + } + + getPythonBaseMethods () { + return [] + } + + getPHPBaseMethods () { + return [] + } + + //------------------------------------------------------------------------- + // the following common headers are used for transpiled tests + + getPythonPreamble () { + return [ + "import os", + "import sys", + "", + "root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))", + "sys.path.append(root)", + "", + "# ----------------------------------------------------------------------------", + "", + "# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:", + "# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code", + "", + "# ----------------------------------------------------------------------------", + "", + ].join ("\n") + } + + getPHPPreamble () { + return [ + "= 0) { + precisionImports.push ('from ccxt.base.decimal_to_precision import ' + constant) + } + } + + header = header.concat (libraries, errorImports, precisionImports) + + methods = methods.concat (this.getPythonBaseMethods ()) + + for (let method of methods) { + const regex = new RegExp ('self\\.(' + method + ')([^a-zA-Z0-9_])', 'g') + bodyAsString = bodyAsString.replace (regex, + (match, p1, p2) => ('self.' + unCamelCase (p1) + p2)) + } + + header.push ("\n\n" + this.createPythonClassDeclaration (className, baseClass)) + + const footer = [ + '', // footer (last empty line) + ] + + const result = header.join ("\n") + "\n" + bodyAsString + "\n" + footer.join ('\n') + return result + } + + // ------------------------------------------------------------------------ + + createPHPClassDeclaration (className, baseClass) { + return 'class ' + className + ' extends ' + baseClass + ' {' + } + + createPHPClassHeader (className, baseClass, bodyAsString) { + return [ + "(' + method + ')(\\s\\(|[^a-zA-Z0-9_])', 'g') + bodyAsString = bodyAsString.replace (regex, + (match, p1, p2) => { + return ((p2 === ' (') ? + ('$this->' + unCamelCase (p1) + p2) : // support direct php calls + ("array($this, '" + unCamelCase (p1) + "')" + p2)) // as well as passing instance methods as callables + }) + } + + header.push ("\n" + this.createPHPClassDeclaration (className, baseClass)) + + const footer =[ + "}\n", + ] + + const result = header.join ("\n") + "\n" + bodyAsString + "\n" + footer.join ('\n') + return result + } + + // ======================================================================== + + transpileJavaScriptToPython3 ({ js, className, removeEmptyLines }) { + + // transpile JS → Python 3 + let python3Body = this.regexAll (js, this.getPythonRegexes ()) + + if (removeEmptyLines) + python3Body = python3Body.replace (/$\s*$/gm, '') + + python3Body = python3Body.replace (/\'([абвгдеёжзийклмнопрÑ�туфхцчшщъыьÑ�ÑŽÑ�æœ�务端忙碌]+)\'/gm, "u'$1'") + + // special case for Python OrderedDicts + let orderedDictRegex = /\.ordered\s+\(\{([^\}]+)\}\)/g + let orderedDictMatches = undefined + while (orderedDictMatches = orderedDictRegex.exec (python3Body)) { + let replaced = orderedDictMatches[1].replace (/^(\s+)([^\:]+)\:\s*([^\,]+)\,$/gm, '$1($2, $3),') + python3Body = python3Body.replace (orderedDictRegex, '\.ordered([' + replaced + '])') + } + + // special case for Python super + if (className) { + python3Body = python3Body.replace (/super\./g, 'super(' + className + ', self).') + } + + return python3Body + } + + // ------------------------------------------------------------------------ + + transpilePython3ToPython2 (py) { + + // remove await from Python 2 body (transpile Python 3 → Python 2) + let python2Body = this.regexAll (py, this.getPython2Regexes ()) + + return python2Body + } + + // ------------------------------------------------------------------------ + + transpileJavaScriptToPHP ({ js, variables }) { + + // match all local variables (let, const or var) + let localVariablesRegex = /(?:^|[^a-zA-Z0-9_])(?:let|const|var)\s+(?:\[([^\]]+)\]|([a-zA-Z0-9_]+))/g // local variables + + let allVariables = (variables || []).map (x => x); // clone the array + // process the variables created in destructuring assignments as well + let localVariablesMatches + while (localVariablesMatches = localVariablesRegex.exec (js)) { + if (localVariablesMatches[1]) { + // this is a destructuring assignment like + // let [ a, b, c ] = 'a-b-c'.split ('-') + let matches = localVariablesMatches[1].trim ().split (', ') // split the destructuring assignment by comma + matches.forEach (x => allVariables.push (x.trim ())) // trim each variable name + } else { + // this is a single variable assignment + allVariables.push (localVariablesMatches[2].trim ()) // add it to the list of local variables + } + } + + // match all variables instantiated in the catch()-block of a try-catch clause + let catchClauseRegex = /catch \(([^)]+)\)/g + let catchClauseMatches + while (catchClauseMatches = catchClauseRegex.exec (js)) { + allVariables.push (catchClauseMatches[1]) + } + + allVariables = allVariables.map (error => this.regexAll (error, this.getCommonRegexes ())) + + // append $ to all variables in the method (PHP syntax demands $ at the beginning of a variable name) + let phpVariablesRegexes = allVariables.map (x => [ "(^|[^$$a-zA-Z0-9\\.\\>'_/])" + x + "([^a-zA-Z0-9'_/])", '$1$$' + x + '$2' ]) + + // support for php syntax for object-pointer dereference + // convert all $variable.property to $variable->property + let variablePropertiesRegexes = allVariables.map (x => [ "(^|[^a-zA-Z0-9\\.\\>'_])" + x + '\\.', '$1' + x + '->' ]) + + // transpile JS → PHP + const phpRegexes = this.getPHPRegexes () + let phpBody = this.regexAll (js, phpRegexes.concat (phpVariablesRegexes).concat (variablePropertiesRegexes)) + + return phpBody + } + + // ------------------------------------------------------------------------ + + transpileJavaScriptToPythonAndPHP (args) { + + // transpile JS → Python 3 + let python3Body = this.transpileJavaScriptToPython3 (args) + + // remove await from Python 2 body (transpile Python 3 → Python 2) + let python2Body = this.transpilePython3ToPython2 (python3Body) + + // transpile JS → PHP + let phpBody = this.transpileJavaScriptToPHP (args) + + return { python3Body, python2Body, phpBody } + } + + //----------------------------------------------------------------------------- + + transpilePythonAsyncToSync (oldName, newName) { + + log.magenta ('Transpiling ' + oldName.yellow + ' → ' + newName.yellow) + const fileContents = fs.readFileSync (oldName, 'utf8') + let lines = fileContents.split ("\n") + + lines = lines.filter (line => ![ 'import asyncio' ].includes (line)) + .map (line => { + return ( + line.replace ('asyncio.get_event_loop().run_until_complete(main())', 'main()') + .replace ('import ccxt.async_support as ccxt', 'import ccxt') + .replace (/.*token\_bucket.*/g, '') + .replace ('await asyncio.sleep', 'time.sleep') + .replace ('async ', '') + .replace ('await ', '')) + }) + + // lines.forEach (line => log (line)) + + function deleteFunction (f, from) { + // the following regexes make a technical error + // since it won't cut away a single function + // it will delete everything up to the beginning of the next comment + const re1 = new RegExp ('def ' + f + '[^\#]+', 'g') + const re2 = new RegExp ('[\\s]+' + f + '\\(exchange\\)', 'g') + return from.replace (re1, '').replace (re2, '') + } + + let newContents = lines.join ('\n') + + newContents = deleteFunction ('test_tickers_async', newContents) + newContents = deleteFunction ('test_l2_order_books_async', newContents) + + fs.truncateSync (newName) + fs.writeFileSync (newName, newContents) + } + + // ------------------------------------------------------------------------ + + transpileDerivedExchangeClass (contents) { + + let exchangeClassDeclarationMatches = contents.match (/^module\.exports\s*=\s*class\s+([\S]+)\s+extends\s+([\S]+)\s+{([\s\S]+?)^};*/m) + + // log.green (file, exchangeClassDeclarationMatches[3]) + + let className = exchangeClassDeclarationMatches[1] + let baseClass = exchangeClassDeclarationMatches[2] + + let methods = exchangeClassDeclarationMatches[3].trim ().split (/\n\s*\n/) + + let python2 = [] + let python3 = [] + let php = [] + + let methodNames = [] + + // run through all methods + for (let i = 0; i < methods.length; i++) { + // parse the method signature + let part = methods[i].trim () + let lines = part.split ("\n") + let signature = lines[0].trim () + let methodSignatureRegex = /(async |)([\S]+)\s\(([^)]*)\)\s*{/ // signature line + let matches = methodSignatureRegex.exec (signature) + + if (!matches) { + log.red (methods[i]) + log.yellow.bright ("\nMake sure your methods don't have empty lines!\n") + } + + // async or not + let keyword = matches[1] + // try { + // keyword = matches[1] + // } catch (e) { + // log.red (e) + // log.green (methods[i]) + // log.yellow (exchangeClassDeclarationMatches[3].trim ().split (/\n\s*\n/)) + // process.exit () + // } + + // method name + let method = matches[2] + + methodNames.push (method) + + method = unCamelCase (method) + + // method arguments + let args = matches[3].trim () + + // extract argument names and local variables + args = args.length ? args.split (',').map (x => x.trim ()) : [] + + // get names of all method arguments for later substitutions + let variables = args.map (arg => arg.split ('=').map (x => x.trim ()) [0]) + + // add $ to each argument name in PHP method signature + let phpArgs = args.join (', $').trim ().replace (/undefined/g, 'null').replace ('{}', 'array ()') + phpArgs = phpArgs.length ? ('$' + phpArgs) : '' + + // remove excessive spacing from argument defaults in Python method signature + let pythonArgs = args.map (x => x.replace (' = ', '=')) + .join (', ') + .replace (/undefined/g, 'None') + .replace (/false/g, 'False') + .replace (/true/g, 'True') + + // method body without the signature (first line) + // and without the closing bracket (last line) + let js = lines.slice (1, -1).join ("\n") + + // transpile everything + let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, className, variables, removeEmptyLines: true }) + + // compile the final Python code for the method signature + let pythonString = 'def ' + method + '(self' + (pythonArgs.length ? ', ' + pythonArgs : '') + '):' + + // compile signature + body for Python 2 + python2.push (''); + python2.push (' ' + pythonString); + python2.push (python2Body); + + // compile signature + body for Python 3 + python3.push (''); + python3.push (' ' + keyword + pythonString); + python3.push (python3Body); + + // compile signature + body for PHP + php.push (''); + php.push (' public function ' + method + ' (' + phpArgs + ') {'); + php.push (phpBody); + php.push (' }') + + } + + return { + + // altogether in PHP, Python 2 and 3 (async) + python2: this.createPythonClass (className, baseClass, python2, methodNames), + python3: this.createPythonClass (className, baseClass, python3, methodNames, true), + php: this.createPHPClass (className, baseClass, php, methodNames), + + className, + baseClass, + } + } + + // ======================================================================== + + transpileDerivedExchangeFile (jsFolder, filename, options) { + + // todo normalize jsFolder and other arguments + + try { + + const { python2Folder, python3Folder, phpFolder } = options + const contents = fs.readFileSync (jsFolder + filename, 'utf8') + const { python2, python3, php, className, baseClass } = this.transpileDerivedExchangeClass (contents) + + log.cyan ('Transpiling from', filename.yellow) + + ;[ + [ python2Folder, filename.replace ('.js', '.py'), python2 ], + [ python3Folder, filename.replace ('.js', '.py'), python3 ], + [ phpFolder, filename.replace ('.js', '.php'), php ], + ].forEach (([ folder, filename, code ]) => { + if (folder) { + overwriteFile (folder + filename, code) + } + }) + + return { className, baseClass } + + } catch (e) { + + log.red ('\nFailed to transpile source code from', filename.yellow) + log.red ('See https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md on how to build this library properly\n') + throw e // rethrow it + } + } + + //------------------------------------------------------------------------- + + transpileDerivedExchangeFiles (jsFolder, options, pattern = '.js') { + + // todo normalize jsFolder and other arguments + + const { python2Folder, python3Folder, phpFolder } = options + + // exchanges.json accounts for ids included in exchanges.cfg + let ids = undefined + try { + ids = require ('../exchanges.json').ids; + } catch (e) { + } + const classNames = fs.readdirSync (jsFolder) + .filter (file => file.includes (pattern) && (!ids || ids.includes (basename (file, pattern)))) + .map (file => this.transpileDerivedExchangeFile (jsFolder, file, options)) + + if (classNames.length === 0) + return null + + let classes = {} + classNames.forEach (({ className, baseClass }) => { + classes[className] = baseClass + }) + + function deleteOldTranspiledFiles (folder, pattern) { + fs.readdirSync (folder) + .filter (file => + !fs.lstatSync (folder + file).isDirectory () && + file.match (pattern) && + !(file.replace (/\.[a-z]+$/, '') in classes) && + !file.match (/^Exchange|errors|__init__|\\./)) + .map (file => folder + file) + .forEach (file => log.red ('Deleting ' + file.yellow) && fs.unlinkSync (file)) + } + + [ + [ python2Folder, /\.pyc?$/ ], + [ python3Folder, /\.pyc?$/ ], + [ phpFolder, /\.php$/ ], + ].forEach (([ folder, pattern ]) => { + if (folder) { + deleteOldTranspiledFiles (folder, pattern) + } + }) + + return classes + } + + // ======================================================================== + + exportTypeScriptDeclarations (classes) { + + const file = './ccxt.d.ts' + const regex = /(?: export class [^\s]+ extends [^\s]+ \{\}[\r]?[\n])+/ + const replacement = Object.keys (classes).map (className => { + const baseClass = classes[className] + return ' export class ' + className + ' extends ' + baseClass + " {}" + }).join ("\n") + "\n" + + replaceInFile (file, regex, replacement) + } + + // ======================================================================== + + transpileErrorHierarchy () { + + const errorHierarchyFilename = './js/base/errorHierarchy.js' + const errorHierarchy = require ('.' + errorHierarchyFilename) + + let js = fs.readFileSync (errorHierarchyFilename, 'utf8') + + js = this.regexAll (js, [ + [ /module\.exports = [^\;]+\;\n/s, '' ], + ]).trim () + + const { python3Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js }) + + const python3BodyIntellisense = python3Body + '\n\n' + Array.from (function* intellisense (map) { + for (const key in map) { + yield key + ' = Exception' + yield* intellisense (map[key]) + } + } (errorHierarchy['BaseError'])).join ('\n') + + const message = 'Transpiling error hierachy →' + + const python = { + filename: './python/ccxt/base/errors.py', + regex: /error_hierarchy = [\S\s]+= Exception/s, + replacement: python3BodyIntellisense, + } + + log.bright.cyan (message, python.filename.yellow) + replaceInFile (... Object.values (python)) + + const php = { + filename:'./php/base/errors.php', + regex: /\$error_hierarchy = .+?\n\)\;/s, + replacement: phpBody, + } + + log.bright.cyan (message, php.filename.yellow) + replaceInFile (... Object.values (php)) + } + + //----------------------------------------------------------------------------- + + transpileDateTimeTests () { + const jsFile = './js/test/base/functions/test.datetime.js' + const pyFile = './python/test/test_exchange_datetime_functions.py' + const phpFile = './php/test/test_exchange_datetime_functions.php' + + log.magenta ('Transpiling from', jsFile.yellow) + + let js = fs.readFileSync (jsFile).toString () + + js = this.regexAll (js, [ + [ /[^\n]+require[^\n]+\n/g, '' ], + [/^\/\*.*\s+/mg, ''], + ]) + + let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false }) + + // phpBody = phpBody.replace (/exchange\./g, 'Exchange::') + + const pythonHeader = [ + "", + "import ccxt # noqa: F402", + "from ccxt.base.decimal_to_precision import ROUND_UP, ROUND_DOWN # noqa F401", + "", + "# ----------------------------------------------------------------------------", + "", + "", + ].join ("\n") + + const python = this.getPythonPreamble () + pythonHeader + python2Body + const php = this.getPHPPreamble () + phpBody + + log.magenta ('→', pyFile.yellow) + log.magenta ('→', phpFile.yellow) + + overwriteFile (pyFile, python) + overwriteFile (phpFile, php) + } + + //------------------------------------------------------------------------- + + transpilePrecisionTests () { + + const jsFile = './js/test/base/functions/test.number.js' + const pyFile = './python/test/test_decimal_to_precision.py' + const phpFile = './php/test/decimal_to_precision.php' + + log.magenta ('Transpiling from', jsFile.yellow) + + let js = fs.readFileSync (jsFile).toString () + + js = this.regexAll (js, [ + [ /\'use strict\';?\s+/g, '' ], + [ /[^\n]+require[^\n]+\n/g, '' ], + [ /decimalToPrecision/g, 'decimal_to_precision' ], + [ /numberToString/g, 'number_to_string' ], + ]) + + let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false }) + + const pythonHeader = [ + "", + "from ccxt.base.decimal_to_precision import decimal_to_precision # noqa F401", + "from ccxt.base.decimal_to_precision import TRUNCATE # noqa F401", + "from ccxt.base.decimal_to_precision import ROUND # noqa F401", + "from ccxt.base.decimal_to_precision import DECIMAL_PLACES # noqa F401", + "from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS # noqa F401", + "from ccxt.base.decimal_to_precision import TICK_SIZE # noqa F401", + "from ccxt.base.decimal_to_precision import PAD_WITH_ZERO # noqa F401", + "from ccxt.base.decimal_to_precision import NO_PADDING # noqa F401", + "from ccxt.base.decimal_to_precision import number_to_string # noqa F401", + "", + "# ----------------------------------------------------------------------------", + "", + "", + ].join ("\n") + + const phpHeader = [ + "// testDecimalToPrecisionErrorHandling", + "//", + "// $this->expectException ('ccxt\\\\BaseError');", + "// $this->expectExceptionMessageRegExp ('/Negative precision is not yet supported/');", + "// Exchange::decimalToPrecision ('123456.789', TRUNCATE, -2, DECIMAL_PLACES);", + "//", + "// $this->expectException ('ccxt\\\\BaseError');", + "// $this->expectExceptionMessageRegExp ('/Invalid number/');", + "// Exchange::decimalToPrecision ('foo');", + "", + "// ----------------------------------------------------------------------------", + "", + "function decimal_to_precision ($x, $roundingMode = ROUND, $numPrecisionDigits = null, $countingMode = DECIMAL_PLACES, $paddingMode = NO_PADDING) {", + " return Exchange::decimal_to_precision ($x, $roundingMode, $numPrecisionDigits, $countingMode, $paddingMode);", + "}", + "function number_to_string ($x) {", + " return Exchange::number_to_string ($x);", + "}", + "", + ].join ("\n") + + const python = this.getPythonPreamble () + pythonHeader + python2Body + const php = this.getPHPPreamble () + phpHeader + phpBody + + log.magenta ('→', pyFile.yellow) + log.magenta ('→', phpFile.yellow) + + overwriteFile (pyFile, python) + overwriteFile (phpFile, php) + } + + //------------------------------------------------------------------------- + + transpileCryptoTests () { + const jsFile = './js/test/base/functions/test.crypto.js' + const pyFile = './python/test/test_crypto.py' + const phpFile = './php/test/test_crypto.php' + + log.magenta ('Transpiling from', jsFile.yellow) + let js = fs.readFileSync (jsFile).toString () + + js = this.regexAll (js, [ + [ /\'use strict\';?\s+/g, '' ], + [ /[^\n]+require[^\n]+\n/g, '' ], + [ /function equals \([\S\s]+?return true\n}\n/g, '' ], + ]) + + let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false }) + + const pythonHeader = [ + "", + "import ccxt # noqa: F402", + "", + "Exchange = ccxt.Exchange", + "hash = Exchange.hash", + "ecdsa = Exchange.ecdsa", + "jwt = Exchange.jwt", + "encode = Exchange.encode", + "", + "", + "def equals(a, b):", + " return a == b", + "", + ].join ("\n") + + const phpHeader = [ + "", + "function hash(...$args) {", + " return Exchange::hash(...$args);", + "}", + "", + "function encode(...$args) {", + " return Exchange::encode(...$args);", + "}", + "", + "function ecdsa(...$args) {", + " return Exchange::ecdsa(...$args);", + "}", + "", + "function jwt(...$args) {", + " return Exchange::jwt(...$args);", + "}", + "", + "function equals($a, $b) {", + " return $a === $b;", + "}", + ].join ("\n") + + const python = this.getPythonPreamble () + pythonHeader + python2Body + const php = this.getPHPPreamble () + phpHeader + phpBody + + log.magenta ('→', pyFile.yellow) + log.magenta ('→', phpFile.yellow) + + overwriteFile (pyFile, python) + overwriteFile (phpFile, php) + } + + // ============================================================================ + + transpileEverything () { + + // default pattern is '.js' + const [ /* node */, /* script */, pattern ] = process.argv + , python2Folder = './python/ccxt/' + , python3Folder = './python/ccxt/async_support/' + , phpFolder = './php/' + , options = { python2Folder, python3Folder, phpFolder } + + createFolderRecursively (python2Folder) + createFolderRecursively (python3Folder) + createFolderRecursively (phpFolder) + + const classes = this.transpileDerivedExchangeFiles ('./js/', options, pattern) + + if (classes === null) { + log.bright.yellow ('0 files transpiled.') + return; + } + + // HINT: if we're going to support specific class definitions + // this process won't work anymore as it will override the definitions + this.exportTypeScriptDeclarations (classes) + + this.transpileErrorHierarchy () + + this.transpilePrecisionTests () + this.transpileDateTimeTests () + this.transpileCryptoTests () + + this.transpilePythonAsyncToSync ('./python/test/test_async.py', './python/test/test.py') + + log.bright.green ('Transpiled successfully.') + } +} + +// ============================================================================ +// main entry point + +if (require.main === module) { // called directly like `node module` + + const transpiler = new Transpiler () + transpiler.transpileEverything () + +} else { // if required as a module + + // do nothing +} + +// ============================================================================ + +module.exports = Transpiler diff --git a/TaskServer/node_modules/ccxt/build/vss.js b/TaskServer/node_modules/ccxt/build/vss.js new file mode 100644 index 000000000..418ff327d --- /dev/null +++ b/TaskServer/node_modules/ccxt/build/vss.js @@ -0,0 +1,87 @@ +// --------------------------------------------------------------------------- +// Usage: +// +// npm run vss +// --------------------------------------------------------------------------- + +"use strict"; + +const fs = require ('fs') +const log = require ('ololog') +const ansi = require ('ansicolor').nice +const { execSync } = require ('child_process') + +//----------------------------------------------------------------------------- + +function incrementVersionPatchNumber (version) { + + let [ major, minor, patch ] = version.split ('.') + + // we don't increment it here anymore, because + // npm version patch will be explicitly called before + + // patch = (parseInt (patch) + 1).toString () + + version = [ major, minor, patch ].join ('.') + + return version +} + +//----------------------------------------------------------------------------- + +function vss (filename, template, version) { + log.bright.cyan ('Single-sourcing version', version, './package.json → ' + filename.yellow) + const content = fs.readFileSync (filename, 'utf8') + const regexp = new RegExp (template.replace (/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // escape string for use in regexp + .replace ('\\{version\\}', '\\d+\\.\\d+\\.\\d+'), 'g') + fs.truncateSync (filename) + fs.writeFileSync (filename, content.replace (regexp, template.replace ('{version}', version))) +} + +// ---------------------------------------------------------------------------- + +function vssEverything () { + + let { version } = require ('../package.json') + + log.bright ('Old version: '.dim, version) + version = incrementVersionPatchNumber (version) + log.bright ('New version: '.cyan, version) + + vss ('./ccxt.js', "const version = '{version}'", version) + vss ('./php/base/Exchange.php', "$version = '{version}'", version) + vss ('./php/base/Exchange.php', "VERSION = '{version}'", version) + vss ('./python/ccxt/__init__.py', "__version__ = '{version}'", version) + vss ('./python/ccxt/base/exchange.py', "__version__ = '{version}'", version) + vss ('./python/ccxt/async_support/__init__.py', "__version__ = '{version}'", version) + vss ('./python/ccxt/async_support/base/exchange.py', "__version__ = '{version}'", version) + + vss ('./README.md', "ccxt@{version}", version) + vss ('./wiki/Install.md', "ccxt@{version}", version) + + execSync ('cp ./package.json ./LICENSE.txt ./keys.json ./python/') + + log.bright.green ('Version single-sourced successfully.') +} + +// ============================================================================ +// main entry point + +if (require.main === module) { + + // if called directly like `node module` + + vssEverything () + +} else { + + // do nothing if required as a module +} + +// ============================================================================ + +module.exports = { + incrementVersionPatchNumber, + vss, + vssEverything, +} diff --git a/TaskServer/node_modules/ccxt/ccxt.browser.js b/TaskServer/node_modules/ccxt/ccxt.browser.js new file mode 100644 index 000000000..ab6625575 --- /dev/null +++ b/TaskServer/node_modules/ccxt/ccxt.browser.js @@ -0,0 +1,6 @@ +/* A entry point for the browser bundle version. This gets compiled by: + + browserify --debug ./ccxt.browser.js > ./dist/ccxt.browser.js + */ + +window.ccxt = require ('./ccxt') \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/ccxt.js b/TaskServer/node_modules/ccxt/ccxt.js new file mode 100644 index 000000000..5eecc7da5 --- /dev/null +++ b/TaskServer/node_modules/ccxt/ccxt.js @@ -0,0 +1,172 @@ +"use strict"; + +/* + +MIT License + +Copyright (c) 2017 Igor Kroitor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +//----------------------------------------------------------------------------- + +const Exchange = require ('./js/base/Exchange') + , functions = require ('./js/base/functions') + , errors = require ('./js/base/errors') + +//----------------------------------------------------------------------------- +// this is updated by vss.js when building + +const version = '1.22.39' + +Exchange.ccxtVersion = version + +//----------------------------------------------------------------------------- + +const exchanges = { + '_1btcxe': require ('./js/_1btcxe.js'), + 'acx': require ('./js/acx.js'), + 'adara': require ('./js/adara.js'), + 'anxpro': require ('./js/anxpro.js'), + 'bcex': require ('./js/bcex.js'), + 'bequant': require ('./js/bequant.js'), + 'bibox': require ('./js/bibox.js'), + 'bigone': require ('./js/bigone.js'), + 'binance': require ('./js/binance.js'), + 'binanceje': require ('./js/binanceje.js'), + 'binanceus': require ('./js/binanceus.js'), + 'bit2c': require ('./js/bit2c.js'), + 'bitbank': require ('./js/bitbank.js'), + 'bitbay': require ('./js/bitbay.js'), + 'bitfinex': require ('./js/bitfinex.js'), + 'bitfinex2': require ('./js/bitfinex2.js'), + 'bitflyer': require ('./js/bitflyer.js'), + 'bitforex': require ('./js/bitforex.js'), + 'bithumb': require ('./js/bithumb.js'), + 'bitkk': require ('./js/bitkk.js'), + 'bitlish': require ('./js/bitlish.js'), + 'bitmart': require ('./js/bitmart.js'), + 'bitmax': require ('./js/bitmax.js'), + 'bitmex': require ('./js/bitmex.js'), + 'bitso': require ('./js/bitso.js'), + 'bitstamp': require ('./js/bitstamp.js'), + 'bitstamp1': require ('./js/bitstamp1.js'), + 'bittrex': require ('./js/bittrex.js'), + 'bitz': require ('./js/bitz.js'), + 'bl3p': require ('./js/bl3p.js'), + 'bleutrade': require ('./js/bleutrade.js'), + 'braziliex': require ('./js/braziliex.js'), + 'btcalpha': require ('./js/btcalpha.js'), + 'btcbox': require ('./js/btcbox.js'), + 'btcchina': require ('./js/btcchina.js'), + 'btcmarkets': require ('./js/btcmarkets.js'), + 'btctradeim': require ('./js/btctradeim.js'), + 'btctradeua': require ('./js/btctradeua.js'), + 'btcturk': require ('./js/btcturk.js'), + 'buda': require ('./js/buda.js'), + 'bw': require ('./js/bw.js'), + 'bytetrade': require ('./js/bytetrade.js'), + 'cex': require ('./js/cex.js'), + 'chilebit': require ('./js/chilebit.js'), + 'cobinhood': require ('./js/cobinhood.js'), + 'coinbase': require ('./js/coinbase.js'), + 'coinbaseprime': require ('./js/coinbaseprime.js'), + 'coinbasepro': require ('./js/coinbasepro.js'), + 'coincheck': require ('./js/coincheck.js'), + 'coinegg': require ('./js/coinegg.js'), + 'coinex': require ('./js/coinex.js'), + 'coinfalcon': require ('./js/coinfalcon.js'), + 'coinfloor': require ('./js/coinfloor.js'), + 'coingi': require ('./js/coingi.js'), + 'coinmarketcap': require ('./js/coinmarketcap.js'), + 'coinmate': require ('./js/coinmate.js'), + 'coinone': require ('./js/coinone.js'), + 'coinspot': require ('./js/coinspot.js'), + 'coolcoin': require ('./js/coolcoin.js'), + 'coss': require ('./js/coss.js'), + 'crex24': require ('./js/crex24.js'), + 'deribit': require ('./js/deribit.js'), + 'digifinex': require ('./js/digifinex.js'), + 'dsx': require ('./js/dsx.js'), + 'exmo': require ('./js/exmo.js'), + 'exx': require ('./js/exx.js'), + 'fcoin': require ('./js/fcoin.js'), + 'fcoinjp': require ('./js/fcoinjp.js'), + 'flowbtc': require ('./js/flowbtc.js'), + 'foxbit': require ('./js/foxbit.js'), + 'ftx': require ('./js/ftx.js'), + 'fybse': require ('./js/fybse.js'), + 'gateio': require ('./js/gateio.js'), + 'gemini': require ('./js/gemini.js'), + 'hitbtc': require ('./js/hitbtc.js'), + 'hitbtc2': require ('./js/hitbtc2.js'), + 'huobipro': require ('./js/huobipro.js'), + 'huobiru': require ('./js/huobiru.js'), + 'ice3x': require ('./js/ice3x.js'), + 'idex': require ('./js/idex.js'), + 'independentreserve': require ('./js/independentreserve.js'), + 'indodax': require ('./js/indodax.js'), + 'itbit': require ('./js/itbit.js'), + 'kkex': require ('./js/kkex.js'), + 'kraken': require ('./js/kraken.js'), + 'kucoin': require ('./js/kucoin.js'), + 'kuna': require ('./js/kuna.js'), + 'lakebtc': require ('./js/lakebtc.js'), + 'latoken': require ('./js/latoken.js'), + 'lbank': require ('./js/lbank.js'), + 'liquid': require ('./js/liquid.js'), + 'livecoin': require ('./js/livecoin.js'), + 'luno': require ('./js/luno.js'), + 'lykke': require ('./js/lykke.js'), + 'mercado': require ('./js/mercado.js'), + 'mixcoins': require ('./js/mixcoins.js'), + 'oceanex': require ('./js/oceanex.js'), + 'okcoincny': require ('./js/okcoincny.js'), + 'okcoinusd': require ('./js/okcoinusd.js'), + 'okex': require ('./js/okex.js'), + 'okex3': require ('./js/okex3.js'), + 'paymium': require ('./js/paymium.js'), + 'poloniex': require ('./js/poloniex.js'), + 'rightbtc': require ('./js/rightbtc.js'), + 'southxchange': require ('./js/southxchange.js'), + 'stex': require ('./js/stex.js'), + 'stronghold': require ('./js/stronghold.js'), + 'surbitcoin': require ('./js/surbitcoin.js'), + 'theocean': require ('./js/theocean.js'), + 'therock': require ('./js/therock.js'), + 'tidebit': require ('./js/tidebit.js'), + 'tidex': require ('./js/tidex.js'), + 'timex': require ('./js/timex.js'), + 'upbit': require ('./js/upbit.js'), + 'vaultoro': require ('./js/vaultoro.js'), + 'vbtc': require ('./js/vbtc.js'), + 'whitebit': require ('./js/whitebit.js'), + 'xbtce': require ('./js/xbtce.js'), + 'yobit': require ('./js/yobit.js'), + 'zaif': require ('./js/zaif.js'), + 'zb': require ('./js/zb.js'), +} + +//----------------------------------------------------------------------------- + +module.exports = Object.assign ({ version, Exchange, exchanges: Object.keys (exchanges) }, exchanges, functions, errors) + +//----------------------------------------------------------------------------- diff --git a/TaskServer/node_modules/ccxt/dist/ccxt.browser.js b/TaskServer/node_modules/ccxt/dist/ccxt.browser.js new file mode 100644 index 000000000..8bd364f89 --- /dev/null +++ b/TaskServer/node_modules/ccxt/dist/ccxt.browser.js @@ -0,0 +1,123405 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o ./dist/ccxt.browser.js + */ + +window.ccxt = require ('./ccxt') +},{"./ccxt":2}],2:[function(require,module,exports){ +"use strict"; + +/* + +MIT License + +Copyright (c) 2017 Igor Kroitor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +//----------------------------------------------------------------------------- + +const Exchange = require ('./js/base/Exchange') + , functions = require ('./js/base/functions') + , errors = require ('./js/base/errors') + +//----------------------------------------------------------------------------- +// this is updated by vss.js when building + +const version = '1.22.39' + +Exchange.ccxtVersion = version + +//----------------------------------------------------------------------------- + +const exchanges = { + '_1btcxe': require ('./js/_1btcxe.js'), + 'acx': require ('./js/acx.js'), + 'adara': require ('./js/adara.js'), + 'anxpro': require ('./js/anxpro.js'), + 'bcex': require ('./js/bcex.js'), + 'bequant': require ('./js/bequant.js'), + 'bibox': require ('./js/bibox.js'), + 'bigone': require ('./js/bigone.js'), + 'binance': require ('./js/binance.js'), + 'binanceje': require ('./js/binanceje.js'), + 'binanceus': require ('./js/binanceus.js'), + 'bit2c': require ('./js/bit2c.js'), + 'bitbank': require ('./js/bitbank.js'), + 'bitbay': require ('./js/bitbay.js'), + 'bitfinex': require ('./js/bitfinex.js'), + 'bitfinex2': require ('./js/bitfinex2.js'), + 'bitflyer': require ('./js/bitflyer.js'), + 'bitforex': require ('./js/bitforex.js'), + 'bithumb': require ('./js/bithumb.js'), + 'bitkk': require ('./js/bitkk.js'), + 'bitlish': require ('./js/bitlish.js'), + 'bitmart': require ('./js/bitmart.js'), + 'bitmax': require ('./js/bitmax.js'), + 'bitmex': require ('./js/bitmex.js'), + 'bitso': require ('./js/bitso.js'), + 'bitstamp': require ('./js/bitstamp.js'), + 'bitstamp1': require ('./js/bitstamp1.js'), + 'bittrex': require ('./js/bittrex.js'), + 'bitz': require ('./js/bitz.js'), + 'bl3p': require ('./js/bl3p.js'), + 'bleutrade': require ('./js/bleutrade.js'), + 'braziliex': require ('./js/braziliex.js'), + 'btcalpha': require ('./js/btcalpha.js'), + 'btcbox': require ('./js/btcbox.js'), + 'btcchina': require ('./js/btcchina.js'), + 'btcmarkets': require ('./js/btcmarkets.js'), + 'btctradeim': require ('./js/btctradeim.js'), + 'btctradeua': require ('./js/btctradeua.js'), + 'btcturk': require ('./js/btcturk.js'), + 'buda': require ('./js/buda.js'), + 'bw': require ('./js/bw.js'), + 'bytetrade': require ('./js/bytetrade.js'), + 'cex': require ('./js/cex.js'), + 'chilebit': require ('./js/chilebit.js'), + 'cobinhood': require ('./js/cobinhood.js'), + 'coinbase': require ('./js/coinbase.js'), + 'coinbaseprime': require ('./js/coinbaseprime.js'), + 'coinbasepro': require ('./js/coinbasepro.js'), + 'coincheck': require ('./js/coincheck.js'), + 'coinegg': require ('./js/coinegg.js'), + 'coinex': require ('./js/coinex.js'), + 'coinfalcon': require ('./js/coinfalcon.js'), + 'coinfloor': require ('./js/coinfloor.js'), + 'coingi': require ('./js/coingi.js'), + 'coinmarketcap': require ('./js/coinmarketcap.js'), + 'coinmate': require ('./js/coinmate.js'), + 'coinone': require ('./js/coinone.js'), + 'coinspot': require ('./js/coinspot.js'), + 'coolcoin': require ('./js/coolcoin.js'), + 'coss': require ('./js/coss.js'), + 'crex24': require ('./js/crex24.js'), + 'deribit': require ('./js/deribit.js'), + 'digifinex': require ('./js/digifinex.js'), + 'dsx': require ('./js/dsx.js'), + 'exmo': require ('./js/exmo.js'), + 'exx': require ('./js/exx.js'), + 'fcoin': require ('./js/fcoin.js'), + 'fcoinjp': require ('./js/fcoinjp.js'), + 'flowbtc': require ('./js/flowbtc.js'), + 'foxbit': require ('./js/foxbit.js'), + 'ftx': require ('./js/ftx.js'), + 'fybse': require ('./js/fybse.js'), + 'gateio': require ('./js/gateio.js'), + 'gemini': require ('./js/gemini.js'), + 'hitbtc': require ('./js/hitbtc.js'), + 'hitbtc2': require ('./js/hitbtc2.js'), + 'huobipro': require ('./js/huobipro.js'), + 'huobiru': require ('./js/huobiru.js'), + 'ice3x': require ('./js/ice3x.js'), + 'idex': require ('./js/idex.js'), + 'independentreserve': require ('./js/independentreserve.js'), + 'indodax': require ('./js/indodax.js'), + 'itbit': require ('./js/itbit.js'), + 'kkex': require ('./js/kkex.js'), + 'kraken': require ('./js/kraken.js'), + 'kucoin': require ('./js/kucoin.js'), + 'kuna': require ('./js/kuna.js'), + 'lakebtc': require ('./js/lakebtc.js'), + 'latoken': require ('./js/latoken.js'), + 'lbank': require ('./js/lbank.js'), + 'liquid': require ('./js/liquid.js'), + 'livecoin': require ('./js/livecoin.js'), + 'luno': require ('./js/luno.js'), + 'lykke': require ('./js/lykke.js'), + 'mercado': require ('./js/mercado.js'), + 'mixcoins': require ('./js/mixcoins.js'), + 'oceanex': require ('./js/oceanex.js'), + 'okcoincny': require ('./js/okcoincny.js'), + 'okcoinusd': require ('./js/okcoinusd.js'), + 'okex': require ('./js/okex.js'), + 'okex3': require ('./js/okex3.js'), + 'paymium': require ('./js/paymium.js'), + 'poloniex': require ('./js/poloniex.js'), + 'rightbtc': require ('./js/rightbtc.js'), + 'southxchange': require ('./js/southxchange.js'), + 'stex': require ('./js/stex.js'), + 'stronghold': require ('./js/stronghold.js'), + 'surbitcoin': require ('./js/surbitcoin.js'), + 'theocean': require ('./js/theocean.js'), + 'therock': require ('./js/therock.js'), + 'tidebit': require ('./js/tidebit.js'), + 'tidex': require ('./js/tidex.js'), + 'timex': require ('./js/timex.js'), + 'upbit': require ('./js/upbit.js'), + 'vaultoro': require ('./js/vaultoro.js'), + 'vbtc': require ('./js/vbtc.js'), + 'whitebit': require ('./js/whitebit.js'), + 'xbtce': require ('./js/xbtce.js'), + 'yobit': require ('./js/yobit.js'), + 'zaif': require ('./js/zaif.js'), + 'zb': require ('./js/zb.js'), +} + +//----------------------------------------------------------------------------- + +module.exports = Object.assign ({ version, Exchange, exchanges: Object.keys (exchanges) }, exchanges, functions, errors) + +//----------------------------------------------------------------------------- + +},{"./js/_1btcxe.js":3,"./js/acx.js":4,"./js/adara.js":5,"./js/anxpro.js":6,"./js/base/Exchange":7,"./js/base/errors":9,"./js/base/functions":10,"./js/bcex.js":21,"./js/bequant.js":22,"./js/bibox.js":23,"./js/bigone.js":24,"./js/binance.js":25,"./js/binanceje.js":26,"./js/binanceus.js":27,"./js/bit2c.js":28,"./js/bitbank.js":29,"./js/bitbay.js":30,"./js/bitfinex.js":31,"./js/bitfinex2.js":32,"./js/bitflyer.js":33,"./js/bitforex.js":34,"./js/bithumb.js":35,"./js/bitkk.js":36,"./js/bitlish.js":37,"./js/bitmart.js":38,"./js/bitmax.js":39,"./js/bitmex.js":40,"./js/bitso.js":41,"./js/bitstamp.js":42,"./js/bitstamp1.js":43,"./js/bittrex.js":44,"./js/bitz.js":45,"./js/bl3p.js":46,"./js/bleutrade.js":47,"./js/braziliex.js":48,"./js/btcalpha.js":49,"./js/btcbox.js":50,"./js/btcchina.js":51,"./js/btcmarkets.js":52,"./js/btctradeim.js":53,"./js/btctradeua.js":54,"./js/btcturk.js":55,"./js/buda.js":56,"./js/bw.js":57,"./js/bytetrade.js":58,"./js/cex.js":59,"./js/chilebit.js":60,"./js/cobinhood.js":61,"./js/coinbase.js":62,"./js/coinbaseprime.js":63,"./js/coinbasepro.js":64,"./js/coincheck.js":65,"./js/coinegg.js":66,"./js/coinex.js":67,"./js/coinfalcon.js":68,"./js/coinfloor.js":69,"./js/coingi.js":70,"./js/coinmarketcap.js":71,"./js/coinmate.js":72,"./js/coinone.js":73,"./js/coinspot.js":74,"./js/coolcoin.js":75,"./js/coss.js":76,"./js/crex24.js":77,"./js/deribit.js":78,"./js/digifinex.js":79,"./js/dsx.js":80,"./js/exmo.js":81,"./js/exx.js":82,"./js/fcoin.js":83,"./js/fcoinjp.js":84,"./js/flowbtc.js":85,"./js/foxbit.js":86,"./js/ftx.js":87,"./js/fybse.js":88,"./js/gateio.js":89,"./js/gemini.js":90,"./js/hitbtc.js":91,"./js/hitbtc2.js":92,"./js/huobipro.js":93,"./js/huobiru.js":94,"./js/ice3x.js":95,"./js/idex.js":96,"./js/independentreserve.js":97,"./js/indodax.js":98,"./js/itbit.js":99,"./js/kkex.js":100,"./js/kraken.js":101,"./js/kucoin.js":102,"./js/kuna.js":103,"./js/lakebtc.js":104,"./js/latoken.js":105,"./js/lbank.js":106,"./js/liquid.js":107,"./js/livecoin.js":108,"./js/luno.js":109,"./js/lykke.js":110,"./js/mercado.js":111,"./js/mixcoins.js":112,"./js/oceanex.js":113,"./js/okcoincny.js":114,"./js/okcoinusd.js":115,"./js/okex.js":116,"./js/okex3.js":117,"./js/paymium.js":118,"./js/poloniex.js":119,"./js/rightbtc.js":120,"./js/southxchange.js":121,"./js/stex.js":159,"./js/stronghold.js":160,"./js/surbitcoin.js":161,"./js/theocean.js":162,"./js/therock.js":163,"./js/tidebit.js":164,"./js/tidex.js":165,"./js/timex.js":166,"./js/upbit.js":167,"./js/vaultoro.js":168,"./js/vbtc.js":169,"./js/whitebit.js":170,"./js/xbtce.js":171,"./js/yobit.js":172,"./js/zaif.js":173,"./js/zb.js":174}],3:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class _1btcxe extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': '_1btcxe', + 'name': '1BTCXE', + 'countries': [ 'PA' ], // Panama + 'comment': 'Crypto Capital API', + 'has': { + 'CORS': true, + 'withdraw': true, + }, + 'timeframes': { + '1d': '1year', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766049-2b294408-5ecc-11e7-85cc-adaff013dc1a.jpg', + 'api': 'https://1btcxe.com/api', + 'www': 'https://1btcxe.com', + 'doc': 'https://1btcxe.com/api-docs.php', + }, + 'api': { + 'public': { + 'get': [ + 'stats', + 'historical-prices', + 'order-book', + 'transactions', + ], + }, + 'private': { + 'post': [ + 'balances-and-info', + 'open-orders', + 'user-transactions', + 'btc-deposit-address/get', + 'btc-deposit-address/new', + 'deposits/get', + 'withdrawals/get', + 'orders/new', + 'orders/edit', + 'orders/cancel', + 'orders/status', + 'withdrawals/new', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + return [ + { 'id': 'USD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'BTC', 'quoteId': 'USD' }, + { 'id': 'EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR' }, + { 'id': 'CNY', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'baseId': 'BTC', 'quoteId': 'CNY' }, + { 'id': 'RUB', 'symbol': 'BTC/RUB', 'base': 'BTC', 'quote': 'RUB', 'baseId': 'BTC', 'quoteId': 'RUB' }, + { 'id': 'CHF', 'symbol': 'BTC/CHF', 'base': 'BTC', 'quote': 'CHF', 'baseId': 'BTC', 'quoteId': 'CHF' }, + { 'id': 'JPY', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'BTC', 'quoteId': 'JPY' }, + { 'id': 'GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'BTC', 'quoteId': 'GBP' }, + { 'id': 'CAD', 'symbol': 'BTC/CAD', 'base': 'BTC', 'quote': 'CAD', 'baseId': 'BTC', 'quoteId': 'CAD' }, + { 'id': 'AUD', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'BTC', 'quoteId': 'AUD' }, + { 'id': 'AED', 'symbol': 'BTC/AED', 'base': 'BTC', 'quote': 'AED', 'baseId': 'BTC', 'quoteId': 'AED' }, + { 'id': 'BGN', 'symbol': 'BTC/BGN', 'base': 'BTC', 'quote': 'BGN', 'baseId': 'BTC', 'quoteId': 'BGN' }, + { 'id': 'CZK', 'symbol': 'BTC/CZK', 'base': 'BTC', 'quote': 'CZK', 'baseId': 'BTC', 'quoteId': 'CZK' }, + { 'id': 'DKK', 'symbol': 'BTC/DKK', 'base': 'BTC', 'quote': 'DKK', 'baseId': 'BTC', 'quoteId': 'DKK' }, + { 'id': 'HKD', 'symbol': 'BTC/HKD', 'base': 'BTC', 'quote': 'HKD', 'baseId': 'BTC', 'quoteId': 'HKD' }, + { 'id': 'HRK', 'symbol': 'BTC/HRK', 'base': 'BTC', 'quote': 'HRK', 'baseId': 'BTC', 'quoteId': 'HRK' }, + { 'id': 'HUF', 'symbol': 'BTC/HUF', 'base': 'BTC', 'quote': 'HUF', 'baseId': 'BTC', 'quoteId': 'HUF' }, + { 'id': 'ILS', 'symbol': 'BTC/ILS', 'base': 'BTC', 'quote': 'ILS', 'baseId': 'BTC', 'quoteId': 'ILS' }, + { 'id': 'INR', 'symbol': 'BTC/INR', 'base': 'BTC', 'quote': 'INR', 'baseId': 'BTC', 'quoteId': 'INR' }, + { 'id': 'MUR', 'symbol': 'BTC/MUR', 'base': 'BTC', 'quote': 'MUR', 'baseId': 'BTC', 'quoteId': 'MUR' }, + { 'id': 'MXN', 'symbol': 'BTC/MXN', 'base': 'BTC', 'quote': 'MXN', 'baseId': 'BTC', 'quoteId': 'MXN' }, + { 'id': 'NOK', 'symbol': 'BTC/NOK', 'base': 'BTC', 'quote': 'NOK', 'baseId': 'BTC', 'quoteId': 'NOK' }, + { 'id': 'NZD', 'symbol': 'BTC/NZD', 'base': 'BTC', 'quote': 'NZD', 'baseId': 'BTC', 'quoteId': 'NZD' }, + { 'id': 'PLN', 'symbol': 'BTC/PLN', 'base': 'BTC', 'quote': 'PLN', 'baseId': 'BTC', 'quoteId': 'PLN' }, + { 'id': 'RON', 'symbol': 'BTC/RON', 'base': 'BTC', 'quote': 'RON', 'baseId': 'BTC', 'quoteId': 'RON' }, + { 'id': 'SEK', 'symbol': 'BTC/SEK', 'base': 'BTC', 'quote': 'SEK', 'baseId': 'BTC', 'quoteId': 'SEK' }, + { 'id': 'SGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD', 'baseId': 'BTC', 'quoteId': 'SGD' }, + { 'id': 'THB', 'symbol': 'BTC/THB', 'base': 'BTC', 'quote': 'THB', 'baseId': 'BTC', 'quoteId': 'THB' }, + { 'id': 'TRY', 'symbol': 'BTC/TRY', 'base': 'BTC', 'quote': 'TRY', 'baseId': 'BTC', 'quoteId': 'TRY' }, + { 'id': 'ZAR', 'symbol': 'BTC/ZAR', 'base': 'BTC', 'quote': 'ZAR', 'baseId': 'BTC', 'quoteId': 'ZAR' }, + ]; + } + + async fetchBalance (params = {}) { + const response = await this.privatePostBalancesAndInfo (params); + const balance = response['balances-and-info']; + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const account = this.account (); + account['free'] = this.safeFloat (balance['available'], currencyId); + account['used'] = this.safeFloat (balance['on_hold'], currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + const request = { + 'currency': this.marketId (symbol), + }; + const response = await this.publicGetOrderBook (this.extend (request, params)); + return this.parseOrderBook (response['order-book'], undefined, 'bid', 'ask', 'price', 'order_amount'); + } + + async fetchTicker (symbol, params = {}) { + const request = { + 'currency': this.marketId (symbol), + }; + const response = await this.publicGetStats (this.extend (request, params)); + const ticker = this.safeValue (response, 'stats', {}); + const last = this.safeFloat (ticker, 'last_price'); + return { + 'symbol': symbol, + 'timestamp': undefined, + 'datetime': undefined, + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'daily_change'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'total_btc_traded'), + 'info': ticker, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + return [ + this.parse8601 (ohlcv['date'] + ' 00:00:00'), + undefined, + undefined, + undefined, + this.safeFloat (ohlcv, 'price'), + undefined, + ]; + } + + async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { + const market = this.market (symbol); + const response = await this.publicGetHistoricalPrices (this.extend ({ + 'currency': market['id'], + 'timeframe': this.timeframes[timeframe], + }, params)); + const ohlcvs = this.toArray (this.omit (response['historical-prices'], 'request_currency')); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const id = this.safeString (trade, 'id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = undefined; + const side = this.safeString (trade, 'maker_type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTransactions (this.extend (request, params)); + const trades = this.toArray (this.omit (response['transactions'], 'request_currency')); + return this.parseTrades (trades, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + const request = { + 'side': side, + 'type': type, + 'currency': this.marketId (symbol), + 'amount': amount, + }; + if (type === 'limit') { + request['limit_price'] = price; + } + const result = await this.privatePostOrdersNew (this.extend (request, params)); + return { + 'info': result, + 'id': result, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostOrdersCancel (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + }; + const response = await this.privatePostWithdrawalsNew (this.extend (request, params)); + return { + 'info': response, + 'id': response['result']['uuid'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (this.id === 'cryptocapital') { + throw new ExchangeError (this.id + ' is an abstract base API for _1btcxe'); + } + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const query = this.extend ({ + 'api_key': this.apiKey, + 'nonce': this.nonce (), + }, params); + const request = this.json (query); + query['signature'] = this.hmac (this.encode (request), this.encode (this.secret)); + body = this.json (query); + headers = { 'Content-Type': 'application/json' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response === 'string') { + if (response.indexOf ('Maintenance') >= 0) { + throw new ExchangeNotAvailable (this.id + ' on maintenance'); + } + } + if ('errors' in response) { + let errors = []; + for (let e = 0; e < response['errors'].length; e++) { + const error = response['errors'][e]; + errors.push (error['code'] + ': ' + error['message']); + } + errors = errors.join (' '); + throw new ExchangeError (this.id + ' ' + errors); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],4:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class acx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'acx', + 'name': 'ACX', + 'countries': [ 'AU' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'withdraw': true, + 'fetchOrder': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '2h': '120', + '4h': '240', + '12h': '720', + '1d': '1440', + '3d': '4320', + '1w': '10080', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg', + 'extension': '.json', + 'api': 'https://acx.io/api', + 'www': 'https://acx.io', + 'doc': 'https://acx.io/documents/api_v2', + }, + 'api': { + 'public': { + 'get': [ + 'depth', // Get depth or specified market Both asks and bids are sorted from highest price to lowest. + 'k_with_pending_trades', // Get K data with pending trades, which are the trades not included in K data yet, because there's delay between trade generated and processed by K data generator + 'k', // Get OHLC(k line) of specific market + 'markets', // Get all available markets + 'order_book', // Get the order book of specified market + 'order_book/{market}', + 'tickers', // Get ticker of all markets + 'tickers/{market}', // Get ticker of specific market + 'timestamp', // Get server current time, in seconds since Unix epoch + 'trades', // Get recent trades on market, each trade is included only once Trades are sorted in reverse creation order. + 'trades/{market}', + ], + }, + 'private': { + 'get': [ + 'members/me', // Get your profile and accounts info + 'deposits', // Get your deposits history + 'deposit', // Get details of specific deposit + 'deposit_address', // Where to deposit The address field could be empty when a new address is generating (e.g. for bitcoin), you should try again later in that case. + 'orders', // Get your orders, results is paginated + 'order', // Get information of specified order + 'trades/my', // Get your executed trades Trades are sorted in reverse creation order. + 'withdraws', // Get your cryptocurrency withdraws + 'withdraw', // Get your cryptocurrency withdraw + ], + 'post': [ + 'orders', // Create a Sell/Buy order + 'orders/multi', // Create multiple sell/buy orders + 'orders/clear', // Cancel all my orders + 'order/delete', // Cancel an order + 'withdraw', // Create a withdraw + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': true, + 'withdraw': {}, // There is only 1% fee on withdrawals to your bank account. + }, + }, + 'exceptions': { + '2002': InsufficientFunds, + '2003': OrderNotFound, + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetMarkets (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = market['id']; + const symbol = market['name']; + let baseId = this.safeString (market, 'base_unit'); + let quoteId = this.safeString (market, 'quote_unit'); + if ((baseId === undefined) || (quoteId === undefined)) { + const ids = symbol.split ('/'); + baseId = ids[0].toLowerCase (); + quoteId = ids[1].toLowerCase (); + } + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + // todo: find out their undocumented precision and limits + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetMembersMe (params); + const balances = this.safeValue (response, 'accounts'); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 300 + } + const orderbook = await this.publicGetDepth (this.extend (request, params)); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'at'); + ticker = ticker['ticker']; + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + let symbol = id; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + let base = id.slice (0, 3); + let quote = id.slice (3, 6); + base = base.toUpperCase (); + quote = quote.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + symbol = base + '/' + quote; + } + result[symbol] = this.parseTicker (response[id], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTickersMarket (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': this.safeFloat (trade, 'price'), + 'amount': this.safeFloat (trade, 'volume'), + 'cost': this.safeFloat (trade, 'funds'), + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 500; // default is 30 + } + const request = { + 'market': market['id'], + 'period': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.publicGetK (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'done': 'closed', + 'wait': 'open', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const marketId = this.safeString (order, 'market'); + symbol = this.markets_by_id[marketId]['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + const id = this.safeString (order, 'id'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'amount': this.safeFloat (order, 'volume'), + 'filled': this.safeFloat (order, 'executed_volume'), + 'remaining': this.safeFloat (order, 'remaining_volume'), + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': parseInt (id), + }; + const response = await this.privateGetOrder (this.extend (request, params)); + return this.parseOrder (response); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'side': side, + 'volume': amount.toString (), + 'ord_type': type, + }; + if (type === 'limit') { + request['price'] = price.toString (); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const marketId = this.safeValue (response, 'market'); + const market = this.safeValue (this.markets_by_id, marketId); + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePostOrderDelete (this.extend (request, params)); + const order = this.parseOrder (response); + const status = order['status']; + if (status === 'closed' || status === 'canceled') { + throw new OrderNotFound (this.id + ' ' + this.json (order)); + } + return order; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // they have XRP but no docs on memo/tag + const request = { + 'currency': currency['id'], + 'sum': amount, + 'address': address, + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + // withdrawal response is undocumented + return { + 'info': response, + 'id': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + encodeParams (params) { + if ('orders' in params) { + const orders = params['orders']; + let query = this.urlencode (this.keysort (this.omit (params, 'orders'))); + for (let i = 0; i < orders.length; i++) { + const order = orders[i]; + const keys = Object.keys (order); + for (let k = 0; k < keys.length; k++) { + const key = keys[k]; + const value = order[key]; + query += '&orders%5B%5D%5B' + key + '%5D=' + value.toString (); + } + } + return query; + } + return this.urlencode (this.keysort (params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/api/' + this.version + '/' + this.implodeParams (path, params); + if ('extension' in this.urls) { + request += this.urls['extension']; + } + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const query = this.encodeParams (this.extend ({ + 'access_key': this.apiKey, + 'tonce': nonce, + }, params)); + const auth = method + '|' + request + '|' + query; + const signed = this.hmac (this.encode (auth), this.encode (this.secret)); + const suffix = query + '&signature=' + signed; + if (method === 'GET') { + url += '?' + suffix; + } else { + body = suffix; + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code === 400) { + const error = this.safeValue (response, 'error'); + const errorCode = this.safeString (error, 'code'); + const feedback = this.id + ' ' + this.json (response); + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + // fallback to default error handler + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],5:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class adara extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'adara', + 'name': 'Adara', + 'countries': [ 'MT' ], + 'version': 'v1', + 'rateLimit': 1000, + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': true, + 'fetchCurrencies': true, + 'fetchOrderBooks': false, + 'createMarketOrder': false, + 'fetchDepositAddress': false, + 'fetchClosedOrders': true, + 'fetchMyTrades': false, + 'fetchOHLCV': false, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchTickers': true, + 'withdraw': false, + 'fetchDeposits': false, + 'fetchWithdrawals': false, + 'fetchTransactions': false, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'token': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/49189583-0466a780-f380-11e8-9248-57a631aad2d6.jpg', + 'api': 'https://api.adara.io', + 'www': 'https://adara.io', + 'doc': 'https://api.adara.io/v1', + 'fees': 'https://adara.io/fees', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'limits', + 'market', + 'marketDepth', + 'marketInfo', + 'orderBook', + 'quote/', + 'quote/{id}', + 'symbols', + 'trade', + ], + 'post': [ + 'confirmContactEmail', + 'restorePassword', + 'user', // sign up + ], + }, + 'private': { + 'get': [ + 'balance', + 'order', + 'order/{id}', + 'currencyBalance', + 'apiKey', // the list of apiKeys + 'user/{id}', + ], + 'post': [ + 'order', + 'recovery', + 'user', + 'apiKey', // sign in and optionally create an apiKey + 'contact', + ], + 'patch': [ + 'order/{id}', + 'user/{id}', // change password + 'customer', // update user info + ], + 'delete': [ + 'apiKey', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.001, + 'taker': 0.001, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'exceptions': { + 'exact': { + 'Insufficient funds': InsufficientFunds, + 'Amount is too small': InvalidOrder, + 'operation has invalid value': InvalidOrder, + "closed order can't be changed": InvalidOrder, + 'Order is not found': OrderNotFound, + 'AUTH': AuthenticationError, + 'You are not authorized': AuthenticationError, + 'Bad Request': BadRequest, + '500': ExchangeError, + }, + 'broad': {}, + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { + 'include': 'from,to', + }; + const response = await this.publicGetSymbols (this.extend (request, params)); + const included = this.safeValue (response, 'included', []); + const includedByType = this.groupBy (included, 'type'); + const currencies = this.safeValue (includedByType, 'currency', []); + const currenciesById = this.indexBy (currencies, 'id'); + // + // { meta: { total: 61 }, + // data: [ { id: "XRPUSD", + // type: "symbol", + // attributes: { allowTrade: false, + // createdAt: "2018-10-23T09:31:06.830Z", + // digits: 5, + // fullName: "XRPUSD", + // makerFee: "0.0250", + // name: "XRPUSD", + // takerFee: "0.0250", + // updatedAt: "2018-10-23T09:31:06.830Z" }, + // relationships: { from: { data: { id: "XRP", type: "currency" } }, + // to: { data: { id: "USD", type: "currency" } } } }, + // { id: "XRPETH", + // type: "symbol", + // attributes: { allowTrade: true, + // createdAt: "2018-10-09T22:34:28.268Z", + // digits: 8, + // fullName: "XRPETH", + // makerFee: "0.0025", + // name: "XRPETH", + // takerFee: "0.0025", + // updatedAt: "2018-10-09T22:34:28.268Z" }, + // relationships: { from: { data: { id: "XRP", type: "currency" } }, + // to: { data: { id: "ETH", type: "currency" } } } } ], + // included: [ { id: "XRP", + // type: "currency", + // attributes: { accuracy: 4, + // active: true, + // allowDeposit: true, + // allowTrade: false, + // allowWallet: true, + // allowWithdraw: true, + // name: "Ripple", + // shortName: "XRP", + // transactionUriTemplate: "https://www.ripplescan.com/transactions/:txId", + // walletUriTemplate: "https://www.ripplescan.com/accounts/:address", + // withdrawFee: "0.20000000", + // withdrawMinAmount: "22.00000000" }, + // relationships: { } }, + // { id: "ETH", + // type: "currency", + // attributes: { accuracy: 8, + // active: true, + // allowDeposit: true, + // allowTrade: true, + // allowWallet: true, + // allowWithdraw: true, + // name: "Ethereum", + // shortName: "ETH", + // transactionUriTemplate: "https://etherscan.io/tx/:txId", + // walletUriTemplate: "https://etherscan.io/address/:address", + // withdrawFee: "0.00800000", + // withdrawMinAmount: "0.02000000" }, + // relationships: { } }, + // { id: "USD", + // type: "currency", + // attributes: { accuracy: 6, + // active: true, + // allowDeposit: false, + // allowTrade: true, + // allowWallet: false, + // allowWithdraw: false, + // name: "USD", + // shortName: "USD", + // transactionUriTemplate: null, + // walletUriTemplate: null, + // withdrawFee: "0.00000000", + // withdrawMinAmount: "0.00000000" }, + // relationships: { } } ] } + // + const result = []; + const markets = response['data']; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const attributes = this.safeValue (market, 'attributes', {}); + const relationships = this.safeValue (market, 'relationships', {}); + const fromRelationship = this.safeValue (relationships, 'from', {}); + const toRelationship = this.safeValue (relationships, 'to', {}); + const fromRelationshipData = this.safeValue (fromRelationship, 'data', {}); + const toRelationshipData = this.safeValue (toRelationship, 'data', {}); + const baseId = this.safeString (fromRelationshipData, 'id'); + const quoteId = this.safeString (toRelationshipData, 'id'); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + const baseCurrency = this.safeValue (currenciesById, baseId, {}); + const baseCurrencyAttributes = this.safeValue (baseCurrency, 'attributes', {}); + const symbol = base + '/' + quote; + const amountPrecision = this.safeInteger (baseCurrencyAttributes, 'accuracy', 8); + const pricePrecision = this.safeInteger (attributes, 'digits', 8); + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const active = this.safeValue (attributes, 'allowTrade'); + const maker = this.safeFloat (attributes, 'makerFee'); + const taker = this.safeFloat (attributes, 'takerFee'); + result.push ({ + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'maker': maker, + 'taker': taker, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { meta: { total: 22 }, + // data: [ { id: "USD", + // type: "currency", + // attributes: { accuracy: 6, + // active: true, + // allowDeposit: false, + // allowTrade: true, + // allowWallet: false, + // allowWithdraw: false, + // name: "USD", + // shortName: "USD", + // transactionUriTemplate: null, + // walletUriTemplate: null, + // withdrawFee: "0.00000000", + // withdrawMinAmount: "0.00000000" }, + // relationships: { } }, + // { id: "BTC", + // type: "currency", + // attributes: { accuracy: 8, + // active: true, + // allowDeposit: true, + // allowTrade: true, + // allowWallet: true, + // allowWithdraw: true, + // name: "Bitcoin", + // shortName: "BTC", + // transactionUriTemplate: "https://blockexplorer.com/tx/:txId", + // walletUriTemplate: "https://blockexplorer.com/address/:address", + // withdrawFee: "0.00050000", + // withdrawMinAmount: "0.00200000" }, + // relationships: { } } ], + // included: [] } + // + const currencies = response['data']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'id'); + const attributes = this.safeValue (currency, 'attributes', {}); + const code = this.commonCurrencyCode (id); + const precision = this.safeInteger (attributes, 'accuracy'); + const fee = this.safeFloat (attributes, 'withdrawFee'); + const active = this.safeValue (attributes, 'active'); + const allowDeposit = this.safeValue (attributes, 'allowDeposit'); + const allowWithdraw = this.safeValue (attributes, 'allowWithdraw'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': this.safeString (attributes, 'name'), + 'active': (active && allowDeposit && allowWithdraw), + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (attributes, 'withdrawMinAmount'), + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + // + // { data: [ { type: "balance", + // id: "U4f0f0940-39bf-45a8-90bc-12d2899db4f1_BALANCE_FOR_ETH", + // attributes: { totalBalance: 10000, + // onOrders: 0, + // normalizedTotalBalance: 310, + // normalizedOnOrders: 0, + // percentage: 3.004116443856034, + // serializedAt: 1543324487949 }, + // relationships: { currency: { data: { type: "currency", id: "ETH" } }, + // normalizedCurrency: { data: { type: "currency", id: "BTC" } } } } ], + // included: [ { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543324487948 }, + // relationships: { } }, + // { type: "currency", + // id: "ETH", + // attributes: { name: "Ethereum", + // shortName: "ETH", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543324487948 } } ] } + // + const result = { 'info': response }; + const data = this.safeValue (response, 'data'); + if (data !== undefined) { + for (let i = 0; i < data.length; i++) { + const balance = data[i]; + const attributes = this.safeValue (balance, 'attributes', {}); + const relationships = this.safeValue (balance, 'relationships', {}); + const currencyRelationship = this.safeValue (relationships, 'currency', {}); + const currencyRelationshipData = this.safeValue (currencyRelationship, 'data'); + const currencyId = this.safeString (currencyRelationshipData, 'id'); + const code = this.commonCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (attributes, 'totalBalance'); + account['used'] = this.safeFloat (attributes, 'onOrders'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + getSymbolFromMarketId (marketId, market = undefined) { + if (marketId === undefined) { + return undefined; + } + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + return market['symbol']; + } + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + return base + '/' + quote; + } + + parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 'price', amountKey = 'amount') { + const bids = []; + const asks = []; + const numBidAsks = orderbook.length; + if (numBidAsks > 0) { + timestamp = this.safeInteger (orderbook[0]['attributes'], 'serializedAt'); + } + for (let i = 0; i < orderbook.length; i++) { + const bidask = orderbook[i]; + const attributes = this.safeValue (bidask, 'attributes', {}); + const currenTimestamp = this.safeInteger (attributes, 'serializedAt'); + timestamp = Math.max (timestamp, currenTimestamp); + const id = this.safeString (bidask, 'id'); + if (id.indexOf ('OBID') >= 0) { + bids.push (this.parseBidAsk (bidask['attributes'], priceKey, amountKey)); + } else if (id.indexOf ('OSID') >= 0) { + asks.push (this.parseBidAsk (bidask['attributes'], priceKey, amountKey)); + } else { + throw new ExchangeError (this.id + ' parseOrderBook encountered an unrecognized bidask format: ' + this.json (bidask)); + } + } + return { + 'bids': this.sortBy (bids, 0, true), + 'asks': this.sortBy (asks, 0), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const filters = 'filters[symbol]'; + const request = {}; + request[filters] = market['id']; + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { data: [ { type: "orderBook", + // id: "OBID0SLTCETHS0", + // attributes: { price: 1, + // amount: 4, + // serializedAt: 1543116143473 } }, + // { type: "orderBook", + // id: "OSID3SLTCETHS0", + // attributes: { price: 12, + // amount: 12, + // serializedAt: 1543116143474 } } ] } + // + return this.parseOrderBook (response['data'], undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseTicker (ticker, market = undefined) { + // + // { type: "quote", + // id: "XRPETH", + // attributes: { currentPrice: 1, + // low: 1, + // high: 1, + // baseVolume: 0, + // quoteVolume: 0, + // change: 0, + // percentChange: 0, + // serializedAt: 1543109275996 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } + // + const symbol = this.getSymbolFromMarketId (this.safeString (ticker, 'id'), market); + const attributes = this.safeValue (ticker, 'attributes', {}); + const timestamp = this.safeInteger (attributes, 'serializedAt'); + const last = this.safeFloat (attributes, 'currentPrice'); + const change = this.safeFloat (attributes, 'change'); + let open = undefined; + if (change !== undefined) { + if (last !== undefined) { + open = last - change; + } + } + const percentage = this.safeFloat (attributes, 'percentChange'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (attributes, 'high'), + 'low': this.safeFloat (attributes, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetQuote (params); + const data = this.safeValue (response, 'data', []); + // + // { data: [ { type: "quote", + // id: "XRPETH", + // attributes: { currentPrice: 1, + // low: 1, + // high: 1, + // baseVolume: 0, + // quoteVolume: 0, + // change: 0, + // percentChange: 0, + // serializedAt: 1543109275996 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } ], + // included: [ { type: "currency", + // id: "XRP", + // attributes: { name: "Ripple", + // shortName: "XRP", + // active: true, + // accuracy: 4, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543109275996 }, + // relationships: { } }, + // { type: "symbol", + // id: "XRPETH", + // attributes: { fullName: "XRPETH", + // digits: 8, + // allowTrade: true, + // serializedAt: 1543109275996 }, + // relationships: { from: { data: { type: "currency", id: "XRP" } }, + // to: { data: { type: "currency", id: "ETH" } } } } ] } + // + const result = {}; + for (let t = 0; t < data.length; t++) { + const ticker = this.parseTicker (data[t]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetQuoteId (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "ETH", + // attributes: { name: "Ethereum", + // shortName: "ETH", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543111444033 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543111444033 } }, + // { type: "symbol", + // id: "ETHBTC", + // attributes: { fullName: "ETHBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543111444033 }, + // relationships: { from: { data: { type: "currency", id: "ETH" } }, + // to: { data: { type: "currency", id: "BTC" } } } } ], + // data: { type: "quote", + // id: "ETHBTC", + // attributes: { currentPrice: 34, + // low: 34, + // high: 34, + // baseVolume: 0, + // quoteVolume: 0, + // change: 0, + // percentChange: 0, + // serializedAt: 1543111444033 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } } (fetchTicker @ adara.js:546) + // + return this.parseTicker (response['data']); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades + // + // + // { type: "trade", + // id: "1542988964359136846136847", + // attributes: { price: 34, + // amount: 4, + // total: 136, + // operation: "buy", + // createdAt: "2018-11-23T16:02:44.359Z", + // serializedAt: 1543112364995 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } ], + // + const id = this.safeString (trade, 'id', 'uuid'); + const attributes = this.safeValue (trade, 'attributes', {}); + const relationships = this.safeValue (trade, 'relationships', {}); + const symbolRelationship = this.safeValue (relationships, 'symbol', {}); + const symbolRelationshipData = this.safeValue (symbolRelationship, 'data', {}); + const marketId = this.safeString (symbolRelationshipData, 'id'); + market = this.safeValue (this.markets_by_id, marketId, market); + let symbol = undefined; + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else if (marketId !== undefined) { + const baseIdLength = marketId.length - 3; + const baseId = marketId.slice (0, baseIdLength); + const quoteId = marketId.slice (baseIdLength); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const orderId = undefined; + const timestamp = this.parse8601 (this.safeString (attributes, 'createdAt')); + const side = this.safeString (attributes, 'operation'); + const price = this.safeFloat (attributes, 'price'); + const amount = this.safeFloat (attributes, 'amount'); + let cost = this.safeFloat (attributes, 'total'); + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + } + const feeCost = this.safeFloat (attributes, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'id': id, + 'info': trade, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + // 'id': market['id'], + }; + const response = await this.publicGetTrade (this.extend (request, params)); + // + // { data: [ { type: "trade", + // id: "1542988964359136846136847", + // attributes: { price: 34, + // amount: 4, + // total: 136, + // operation: "buy", + // createdAt: "2018-11-23T16:02:44.359Z", + // serializedAt: 1543112364995 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } ], + // included: [ { type: "currency", + // id: "ETH", + // attributes: { name: "Ethereum", + // shortName: "ETH", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543112364995 }, + // relationships: { } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // ... + // serializedAt: 1543112364995 }, + // relationships: { } }, + // { type: "symbol", + // id: "ETHBTC", + // attributes: { fullName: "ETHBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543112364995 }, + // relationships: { from: { data: { type: "currency", id: "ETH" } }, + // to: { data: { type: "currency", id: "BTC" } } } } } + // + return this.parseTrades (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'data': { + 'type': 'order', + 'attributes': { + 'amount': parseFloat (this.amountToPrecision (symbol, amount)), + 'operation': side, + 'orderType': type, + 'price': parseFloat (this.priceToPrecision (symbol, price)), + }, + 'relationships': { + 'symbol': { + 'data': { + 'id': market['id'], + 'type': 'symbol', + }, + }, + }, + }, + 'included': [ + { + 'id': market['id'], + 'type': 'symbol', + }, + ], + }; + const response = await this.privatePostOrder (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543434477449 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543434477449 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543434477449 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } } ], + // data: { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543434477449, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 220, + // price: 0.000035, + // averagePrice: 0, + // fee: 0, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: null, + // status: "open", + // filled: 0, + // flags: [] }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } } } } } + // + return this.parseOrder (response['data']); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + 'data': { + 'attributes': { + 'status': 'canceled', + }, + }, + }; + const response = await this.privatePatchOrderId (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543437874742 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543437874742 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543437874742 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } } ], + // data: { type: "order", + // id: "34794", + // attributes: { serializedAt: 1543437874742, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 110, + // price: 0.000034, + // averagePrice: 0, + // fee: 0, + // timeOpen: "2018-11-28T20:42:35.486Z", + // timeClose: null, + // status: "canceled", + // filled: 0, + // flags: [] }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } } } } } + // + return this.parseOrder (response['data']); + } + + parseOrderStatus (status) { + const statuses = { + 'open': 'open', + 'closed': 'closed', + 'canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543435013349, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 220, + // price: 0.000035, + // averagePrice: 0.000035, + // fee: 0.0001925, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: "2018-11-28T19:47:57.452Z", + // status: "closed", + // filled: 220, + // flags: null }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } } } } + // + const id = this.safeString (order, 'id'); + const attributes = this.safeValue (order, 'attributes', {}); + const relationships = this.safeValue (order, 'relationships', {}); + const symbolRelationship = this.safeValue (relationships, 'symbol', {}); + const symbolRelationshipData = this.safeValue (symbolRelationship, 'data', {}); + const tradesRelationship = this.safeValue (relationships, 'trades', {}); + const tradesRelationshipData = this.safeValue (tradesRelationship, 'data'); + const marketId = this.safeString (symbolRelationshipData, 'id'); + market = this.safeValue (this.markets_by_id, marketId, market); + let feeCurrency = undefined; + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else if (marketId !== undefined) { + const baseIdLength = marketId.length - 3; + const baseId = marketId.slice (0, baseIdLength); + const quoteId = marketId.slice (baseIdLength); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const timestamp = this.parse8601 (this.safeString (attributes, 'timeOpen')); + const side = this.safeString (attributes, 'operation'); + const type = this.safeString (attributes, 'orderType'); + const status = this.parseOrderStatus (this.safeString (attributes, 'status')); + const lastTradeTimestamp = this.parse8601 (this.safeString (attributes, 'timeClose')); + const price = this.safeFloat (attributes, 'price'); + const amount = this.safeFloat (attributes, 'amount'); + const filled = this.safeFloat (attributes, 'filled'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + let cost = undefined; + const average = this.safeFloat (attributes, 'averagePrice'); + if (cost === undefined) { + if ((average !== undefined) && (filled !== undefined)) { + cost = parseFloat (this.costToPrecision (symbol, average * filled)); + } + } + let fee = undefined; + const feeCost = this.safeFloat (attributes, 'fee'); + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + } + let trades = undefined; + if (tradesRelationshipData !== undefined) { + const numTrades = tradesRelationshipData.length; + if (numTrades > 0) { + trades = this.parseTrades (tradesRelationshipData, market); + } + } + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + return result; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'include': 'trades', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + const filters = 'filters[symbol]'; + request[filters] = market['id']; + } + const response = await this.privateGetOrder (this.extend (request, params)); + // + // { data: [ { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543436770259, + // operation: "buy", + // orderType: "limit", + // amount: 220, + // price: 0.000035, + // averagePrice: 0.000035, + // fee: 0.0001925, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: "2018-11-28T19:47:57.452Z", + // status: "closed", + // filled: 220, + // flags: null }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } }, + // trades: { data: [{ type: "trade", id: "34789_34793" }] } } } ], + // included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543436770259 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543436770259 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543436770259 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } }, + // { type: "trade", + // id: "34789_34793", + // attributes: { fee: 0.0001925, + // price: 0.000035, + // amount: 220, + // total: 0.0077, + // operation: "buy", + // createdAt: "2018-11-28T19:47:57.451Z" } } ], + // meta: { total: 1 } } + // + return this.parseOrdersResponse (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const filters = 'filters[status][]'; + const request = {}; + request[filters] = 'open'; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const filters = 'filters[status][]'; + const request = {}; + request[filters] = 'closed'; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + parseOrdersResponse (response, market = undefined, since = undefined, limit = undefined) { + const included = this.safeValue (response, 'included', []); + const includedByType = this.groupBy (included, 'type'); + const unparsedTrades = this.safeValue (includedByType, 'trade', []); + const trades = this.parseTrades (unparsedTrades, market); + const tradesById = this.indexBy (trades, 'id'); + const orders = this.parseOrders (this.safeValue (response, 'data', []), market, since, limit); + const result = []; + for (let i = 0; i < orders.length; i++) { + const order = orders[i]; + const orderTrades = []; + const orderFee = this.safeValue (order, 'fee', {}); + const orderFeeCurrency = this.safeString (orderFee, 'currency'); + if (order['trades'] !== undefined) { + for (let j = 0; j < order['trades'].length; j++) { + const orderTrade = order['trades'][j]; + const orderTradeId = orderTrade['id']; + if (orderTradeId in tradesById) { + orderTrades.push (this.deepExtend (tradesById[orderTradeId], { + 'order': order['id'], + 'type': order['type'], + 'symbol': order['symbol'], + 'fee': { + 'currency': orderFeeCurrency, + }, + })); + } + } + } + const numOrderTrades = orderTrades.length; + if (numOrderTrades > 0) { + order['trades'] = orderTrades; + } + result.push (order); + } + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + 'include': 'trades', + }; + const response = await this.privateGetOrderId (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543436451996 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543436451996 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543436451996 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } }, + // { type: "trade", + // id: "34789_34793", + // attributes: { fee: 0.0001925, + // price: 0.000035, + // amount: 220, + // total: 0.0077, + // operation: "buy", + // createdAt: "2018-11-28T19:47:57.451Z" } } ], + // data: { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543436451996, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 220, + // price: 0.000035, + // averagePrice: 0.000035, + // fee: 0.0001925, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: "2018-11-28T19:47:57.452Z", + // status: "closed", + // filled: 220, + // flags: null }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } }, + // trades: { data: [{ type: "trade", id: "34789_34793" }] } } } } + // + const data = this.safeValue (response, 'data'); + response['data'] = []; + response['data'].push (data); + const orders = this.parseOrdersResponse (response); + const ordersById = this.indexBy (orders, 'id'); + if (id in ordersById) { + return ordersById[id]; + } + throw new OrderNotFound (this.id + ' fetchOrder could not find order id ' + id.toString ()); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let payload = '/' + this.implodeParams (path, params); + let url = this.urls['api'] + '/' + this.version + payload; + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + if (api === 'private') { + const nonce = this.nonce (); + let expiredAt = this.sum (nonce, this.safeInteger (this.options, 'expiredAt', 10000)); + expiredAt = expiredAt.toString (); + if ((method === 'POST') || (method === 'PATCH')) { + body = this.json (query); + payload = body; + } + if (this.token) { + headers = { + 'Cookie': 'token=' + this.token, + }; + } else { + this.checkRequiredCredentials (); + const auth = method + payload + 'expiredAt=' + expiredAt; + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512', 'base64'); + headers = { + 'X-ADX-EXPIRE': expiredAt, + 'X-ADX-APIKEY': this.apiKey, + 'X-ADX-SIGNATURE': signature, + }; + } + if (method !== 'GET') { + headers['Content-Type'] = 'application/json'; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const errors = this.safeValue (response, 'errors', []); + const numErrors = errors.length; + if (numErrors > 0) { + const error = errors[0]; + const code = this.safeString (error, 'code'); + const status = this.safeString (error, 'status'); + const title = this.safeString (error, 'title'); + const detail = this.safeString (error, 'detail'); + const feedback = this.id + ' ' + this.json (response); + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], status, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], title, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], detail, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],6:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, BadRequest, OrderNotFound, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class anxpro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'anxpro', + 'name': 'ANXPro', + 'countries': [ 'JP', 'SG', 'HK', 'NZ' ], + 'rateLimit': 1500, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + 'fetchCurrencies': true, + 'fetchOHLCV': false, + 'fetchTrades': false, + 'fetchOpenOrders': true, + 'fetchDepositAddress': true, + 'fetchTransactions': true, + 'fetchMyTrades': true, + 'createDepositAddress': false, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27765983-fd8595da-5ec9-11e7-82e3-adb3ab8c2612.jpg', + 'api': { + 'public': 'https://anxpro.com/api/2', + 'private': 'https://anxpro.com/api/2', + 'v3public': 'https://anxpro.com/api/3', + 'v3private': 'https://anxpro.com/api/3', + }, + 'www': 'https://anxpro.com', + 'doc': [ + 'https://anxv2.docs.apiary.io', + 'https://anxv3.docs.apiary.io', + 'https://anxpro.com/pages/api', + ], + }, + 'api': { + 'v3public': { + 'get': [ + 'currencyStatic', + ], + }, + 'v3private': { + 'post': [ + 'register/register', + 'register/verifyRegistration', + 'register/resendVerification', + 'register/autoRegister', + 'account', + 'subaccount/new', + 'transaction/list', + 'order/list', + 'trade/list', + 'send', + 'receive', + 'receive/create', + 'batch/new', + 'batch/add', + 'batch/list', + 'batch/info', + 'batch/closeForSend', + 'order/new', + 'order/info', + 'order/cancel', + 'retail/quote', + 'retail/trade', + 'validateAddress', + 'address/check', + 'alert/create', + 'alert/delete', + 'alert/list', + 'kyc/personal', + 'kyc/document', + 'kyc/status', + 'kyc/verifyCode', + 'news/list', + 'press/list', + 'announcements/list', + 'apiDoc/list', + ], + }, + 'public': { + 'get': [ + '{currency_pair}/money/ticker', + '{currency_pair}/money/depth/full', + '{currency_pair}/money/trade/fetch', // disabled by ANXPro + ], + }, + 'private': { + 'post': [ + '{currency_pair}/money/order/add', + '{currency_pair}/money/order/cancel', + '{currency_pair}/money/order/quote', + '{currency_pair}/money/order/result', + '{currency_pair}/money/orders', + 'money/{currency}/address', + 'money/{currency}/send_simple', + 'money/info', + 'money/trade/list', + 'money/wallet/history', + ], + }, + }, + 'httpExceptions': { + '403': AuthenticationError, + }, + 'exceptions': { + 'exact': { + // v2 + 'Insufficient Funds': InsufficientFunds, + 'Trade value too small': InvalidOrder, + 'The currency pair is not supported': BadRequest, + 'Order amount is too low': InvalidOrder, + 'Order amount is too high': InvalidOrder, + 'order rate is too low': InvalidOrder, + 'order rate is too high': InvalidOrder, + 'Too many open orders': InvalidOrder, + 'Unexpected error': ExchangeError, + 'Order Engine is offline': ExchangeNotAvailable, + 'No executed order with that identifer found': OrderNotFound, + 'Unknown server error, please contact support.': ExchangeError, + 'Not available': ExchangeNotAvailable, // { "status": "Not available" } + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + }, + 'options': { + 'fetchMyTradesMethod': 'private_post_money_trade_list', // or 'v3private_post_trade_list' + }, + }); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + // todo: migrate this to fetchLedger + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['from'] = since; + } + if (limit !== undefined) { + request['max'] = limit; + } + const currency = (code === undefined) ? undefined : this.currency (code); + if (currency !== undefined) { + request['ccy'] = currency['id']; + } + const response = await this.v3privatePostTransactionList (this.extend (request, params)); + // + // { + // transactions: [ + // { + // transactionClass: 'COIN', + // uuid: '7896857b-2ed6-4c62-ba4c-619837438d9c', + // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', + // amount: -17865.72689976, + // fee: 1, + // balanceBefore: 17865.72689976, + // balanceAfter: 17865.72689976, + // ccy: 'XRP', + // transactionState: 'PROCESSED', + // transactionType: 'WITHDRAWAL', + // received: '1551357946000', + // processed: '1551357946000', + // timestampMillis: '1557441435932', + // displayTitle: 'Coin Withdrawal', + // displayDescription: 'Withdraw to: rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345', + // coinAddress: 'rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345', + // coinTransactionId: '68444611753E9D8F5C33DCBBF43F01391070F79CAFCF7625397D1CEFA519064A', + // subAccount: [ + // Object + // ] + // }, + // { + // transactionClass: 'FILL', + // uuid: 'a5ae54de-c14a-4ef8-842d-56000c9dc7ab', + // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', + // amount: 0.09006364, + // fee: 0.00018013, + // balanceBefore: 0.3190001, + // balanceAfter: 0.40888361, + // ccy: 'BTC', + // transactionState: 'PROCESSED', + // transactionType: 'FILL_CREDIT', + // received: '1551357057000', + // processed: '1551357057000', + // timestampMillis: '1557441435956', + // displayTitle: 'Order Fill', + // displayDescription: 'Buy BTC @ 3008.53930 EUR/BTC' + // } + // ], + // count: ..., + // timestamp: '1557441435971', + // resultCode: 'OK' + // } + // + const transactions = this.safeValue (response, 'transactions', []); + const grouped = this.groupBy (transactions, 'transactionType', []); + const depositsAndWithdrawals = this.arrayConcat (this.safeValue (grouped, 'DEPOSIT', []), this.safeValue (grouped, 'WITHDRAWAL', [])); + return this.parseTransactions (depositsAndWithdrawals, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // withdrawal + // + // { + // transactionClass: 'COIN', + // uuid: 'bff91938-4dad-4c48-9db6-468324ce96c1', + // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', + // amount: -0.40888361, + // fee: 0.002, + // balanceBefore: 0.40888361, + // balanceAfter: 0.40888361, + // ccy: 'BTC', + // transactionState: 'PROCESSED', + // transactionType: 'WITHDRAWAL', + // received: '1551357156000', + // processed: '1551357156000', + // timestampMillis: '1557441846213', + // displayTitle: 'Coin Withdrawal', + // displayDescription: 'Withdraw to: 1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', + // coinAddress: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', + // coinTransactionId: + // 'ab80abcb62bf6261ebc827c73dd59a4ce15d740b6ba734af6542f43b6485b923', + // subAccount: { + // uuid: '652e1add-0d0b-462c-a03c-d6197c825c1a', + // name: 'DEFAULT' + // } + // } + // + // deposit + // + // { + // "transactionClass": "COIN", + // "uuid": "eb65576f-c1a8-423c-8e2f-fa50109b2eab", + // "userUuid": "82027ee9-cb59-4f29-80d6-f7e793f39ad4", + // "amount": 3.99287184, + // "fee": 0, + // "balanceBefore": 8.39666034, + // "balanceAfter": 12.38953218, + // "ccy": "ETH", + // "transactionState": "PROCESSED", + // "transactionType": "DEPOSIT", + // "received": "1529420056000", + // "processed": "1529420766000", + // "timestampMillis": "1557442743854", + // "displayTitle": "Coin Deposit", + // "displayDescription": "Deposit to: 0xf123aa44fadea913a7da99cc2ee202db684ce0e3", + // "coinTransactionId": "0x33a3e5ea7c034dc5324a88aa313962df0a5d571ab4bcc3cb00b876b1bdfc54f7", + // "coinConfirmations": 51, + // "coinConfirmationsRequired": 45, + // "subAccount": {"uuid": "aba1de05-c7c6-49d7-84ab-a6aca0e827b6", "name": "DEFAULT"} + // } + // + const timestamp = this.safeInteger (transaction, 'received'); + const updated = this.safeInteger (transaction, 'processed'); + const transactionType = this.safeString (transaction, 'transactionType'); + let type = undefined; + let amount = this.safeFloat (transaction, 'amount'); + let address = this.safeString (transaction, 'coinAddress'); + let tag = undefined; + if (transactionType === 'WITHDRAWAL') { + type = 'withdrawal'; + amount = -amount; + if (address) { + // xrp: "coinAddress": "rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345", + if (address.indexOf ('?dt=') >= 0) { + const parts = address.split ('?dt='); + address = parts[0]; + tag = parts[1]; + } + } + } else if (transactionType === 'DEPOSIT') { + if (!address) { + const displayDescription = this.safeString (transaction, 'displayDescription'); + const addressText = displayDescription.replace ('Deposit to: ', ''); + if (addressText.length > 0) { + // eth: "displayDescription": "Deposit to: 0xf123aa44fadea913a7da99cc2ee202db684ce0e3", + // xrp: "displayDescription": "Deposit to: rUjxty1WWLwX1evhKf3C2XNZDMcXEZ9ToJ?dt=504562345", + if (addressText.indexOf ('?dt=') >= 0) { + const parts = addressText.split ('?dt='); + address = parts[0]; + tag = parts[1]; + } else { + address = addressText; + } + } + } + type = 'deposit'; + } + const currencyId = this.safeString (transaction, 'ccy'); + const code = this.safeCurrencyCode (currencyId); + const transactionState = this.safeString (transaction, 'transactionState'); + const status = this.parseTransactionStatus (transactionState); + const feeCost = this.safeFloat (transaction, 'fee'); + const netAmount = amount - feeCost; + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'id': this.safeString (transaction, 'uuid'), + 'currency': code, + 'amount': netAmount, + 'address': address, + 'tag': tag, + 'status': status, + 'type': type, + 'updated': updated, + 'txid': this.safeString (transaction, 'coinTransactionId'), + 'fee': { + 'cost': feeCost, + 'currency': code, + }, + 'info': transaction, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'PROCESSED': 'ok', + 'REVERSED': 'canceled', + 'CANCELLED_INSUFFICIENT_FUNDS': 'canceled', + 'CANCELLED_LIMIT_BREACH': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // + // v2 + // + // { + // result: 'success', + // data: [ + // { + // tradeId: 'c2ed821d-717a-4b7e-beb0-a9ba60e8f5a0', + // orderId: '5a65ae21-c7a8-4009-b3af-306c2ad21a02', + // timestamp: '1551357057000', + // tradedCurrencyFillAmount: '0.09006364', + // settlementCurrencyFillAmount: '270.96', + // settlementCurrencyFillAmountUnrounded: '270.96000000', + // price: '3008.53930', + // ccyPair: 'BTCEUR', + // side: 'BUY' // missing in v3 + // }, + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD', + // side: 'BUY' // missing in v3 + // }, + // ] + // } + // + // v3 + // + // { + // trades: [ + // { + // tradeId: 'c2ed821d-717a-4b7e-beb0-a9ba60e8f5a0', + // orderId: '5a65ae21-c7a8-4009-b3af-306c2ad21a02', + // timestamp: '1551357057000', + // tradedCurrencyFillAmount: '0.09006364', + // settlementCurrencyFillAmount: '270.96', + // settlementCurrencyFillAmountUnrounded: '270.96000000', + // price: '3008.53930', + // ccyPair: 'BTCEUR' + // }, + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD' + // }, + // ], + // count: 3, + // timestamp: '1557438456732', + // resultCode: 'OK' + // } + // + const request = {}; + if (limit !== undefined) { + request['max'] = limit; + } + const method = this.safeString (this.options, 'fetchMyTradesMethod', 'private_post_money_trade_list'); + const response = await this[method] (this.extend (request, params)); + const trades = this.safeValue2 (response, 'trades', 'data', []); + const market = (symbol === undefined) ? undefined : this.market (symbol); + return this.parseTrades (trades, market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // v2 + // + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD', + // side: 'BUY', // missing in v3 + // } + // + // v3 + // + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD' + // } + // + const id = this.safeString (trade, 'tradeId'); + const orderId = this.safeString (trade, 'orderId'); + const timestamp = this.safeInteger (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'tradedCurrencyFillAmount'); + const cost = this.safeFloat (trade, 'settlementCurrencyFillAmount'); + const side = this.safeStringLower (trade, 'side'); + let symbol = undefined; + const marketId = this.safeString (trade, 'ccyPair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + return { + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + 'info': trade, + }; + } + + async fetchCurrencies (params = {}) { + const response = await this.v3publicGetCurrencyStatic (params); + // + // { + // "currencyStatic": { + // "currencies": { + // "HKD": { + // "decimals": 2, + // "minOrderSize": 1.00000000, + // "maxOrderSize": 10000000000.00000000, + // "displayDenominator": 1, + // "summaryDecimals": 0, + // "displayUnit": "HKD", + // "symbol": "$", + // "type": "FIAT", + // "engineSettings": { + // "depositsEnabled": false, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 1.00000000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 36000.00000000, + // "maxMarketOrderSize": 36000.00000000, + // "assetDivisibility": 0 + // }, + // "ETH": { + // "decimals": 8, + // "minOrderSize": 0.00010000, + // "maxOrderSize": 1000000000.00000000, + // "type": "CRYPTO", + // "confirmationThresholds": [ + // { "confosRequired": 30, "threshold": 0.50000000 }, + // { "confosRequired": 45, "threshold": 10.00000000 }, + // { "confosRequired": 70 } + // ], + // "networkFee": 0.00500000, + // "engineSettings": { + // "depositsEnabled": true, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 0.00010000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 10000000000.00000000, + // "maxMarketOrderSize": 1000000000.00000000, + // "digitalCurrencyType": "ETHEREUM", + // "assetDivisibility": 0, + // "assetIcon": "/images/currencies/crypto/ETH.svg" + // }, + // }, + // "currencyPairs": { + // "ETHUSD": { + // "priceDecimals": 5, + // "engineSettings": { + // "tradingEnabled": true, + // "displayEnabled": true, + // "cancelOnly": true, + // "verifyRequired": false, + // "restrictedBuy": false, + // "restrictedSell": false + // }, + // "minOrderRate": 10.00000000, + // "maxOrderRate": 10000.00000000, + // "displayPriceDecimals": 5, + // "tradedCcy": "ETH", + // "settlementCcy": "USD", + // "preferredMarket": "ANX", + // "chartEnabled": true, + // "simpleTradeEnabled": false + // }, + // }, + // }, + // "timestamp": "1549840691039", + // "resultCode": "OK" + // } + // + const currencyStatic = this.safeValue (response, 'currencyStatic', {}); + const currencies = this.safeValue (currencyStatic, 'currencies', {}); + const result = {}; + const ids = Object.keys (currencies); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = currencies[id]; + const code = this.safeCurrencyCode (id); + const engineSettings = this.safeValue (currency, 'engineSettings'); + const depositsEnabled = this.safeValue (engineSettings, 'depositsEnabled'); + const withdrawalsEnabled = this.safeValue (engineSettings, 'withdrawalsEnabled'); + const displayEnabled = this.safeValue (engineSettings, 'displayEnabled'); + const active = depositsEnabled && withdrawalsEnabled && displayEnabled; + const precision = this.safeInteger (currency, 'decimals'); + const fee = this.safeFloat (currency, 'networkFee'); + const type = this.safeStringLower (currency, 'type'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'type': type, + 'active': active, + 'precision': precision, + 'fee': fee, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minOrderSize'), + 'max': this.safeFloat (currency, 'maxOrderSize'), + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (currency, 'minOrderValue'), + 'max': this.safeFloat (currency, 'maxOrderValue'), + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.v3publicGetCurrencyStatic (params); + // + // { + // "currencyStatic": { + // "currencies": { + // "HKD": { + // "decimals": 2, + // "minOrderSize": 1.00000000, + // "maxOrderSize": 10000000000.00000000, + // "displayDenominator": 1, + // "summaryDecimals": 0, + // "displayUnit": "HKD", + // "symbol": "$", + // "type": "FIAT", + // "engineSettings": { + // "depositsEnabled": false, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 1.00000000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 36000.00000000, + // "maxMarketOrderSize": 36000.00000000, + // "assetDivisibility": 0 + // }, + // "ETH": { + // "decimals": 8, + // "minOrderSize": 0.00010000, + // "maxOrderSize": 1000000000.00000000, + // "type": "CRYPTO", + // "confirmationThresholds": [ + // { "confosRequired": 30, "threshold": 0.50000000 }, + // { "confosRequired": 45, "threshold": 10.00000000 }, + // { "confosRequired": 70 } + // ], + // "networkFee": 0.00500000, + // "engineSettings": { + // "depositsEnabled": true, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 0.00010000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 10000000000.00000000, + // "maxMarketOrderSize": 1000000000.00000000, + // "digitalCurrencyType": "ETHEREUM", + // "assetDivisibility": 0, + // "assetIcon": "/images/currencies/crypto/ETH.svg" + // }, + // }, + // "currencyPairs": { + // "ETHUSD": { + // "priceDecimals": 5, + // "engineSettings": { + // "tradingEnabled": true, + // "displayEnabled": true, + // "cancelOnly": true, + // "verifyRequired": false, + // "restrictedBuy": false, + // "restrictedSell": false + // }, + // "minOrderRate": 10.00000000, + // "maxOrderRate": 10000.00000000, + // "displayPriceDecimals": 5, + // "tradedCcy": "ETH", + // "settlementCcy": "USD", + // "preferredMarket": "ANX", + // "chartEnabled": true, + // "simpleTradeEnabled": false + // }, + // }, + // }, + // "timestamp": "1549840691039", + // "resultCode": "OK" + // } + // + const currencyStatic = this.safeValue (response, 'currencyStatic', {}); + const currencies = this.safeValue (currencyStatic, 'currencies', {}); + const currencyPairs = this.safeValue (currencyStatic, 'currencyPairs', {}); + const result = []; + const ids = Object.keys (currencyPairs); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = currencyPairs[id]; + // + // "ETHUSD": { + // "priceDecimals": 5, + // "engineSettings": { + // "tradingEnabled": true, + // "displayEnabled": true, + // "cancelOnly": true, + // "verifyRequired": false, + // "restrictedBuy": false, + // "restrictedSell": false + // }, + // "minOrderRate": 10.00000000, + // "maxOrderRate": 10000.00000000, + // "displayPriceDecimals": 5, + // "tradedCcy": "ETH", + // "settlementCcy": "USD", + // "preferredMarket": "ANX", + // "chartEnabled": true, + // "simpleTradeEnabled": false + // }, + // + const baseId = this.safeString (market, 'tradedCcy'); + const quoteId = this.safeString (market, 'settlementCcy'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const baseCurrency = this.safeValue (currencies, baseId, {}); + const quoteCurrency = this.safeValue (currencies, quoteId, {}); + const precision = { + 'price': this.safeInteger (market, 'priceDecimals'), + 'amount': this.safeInteger (baseCurrency, 'decimals'), + }; + const engineSettings = this.safeValue (market, 'engineSettings'); + const displayEnabled = this.safeValue (engineSettings, 'displayEnabled'); + const tradingEnabled = this.safeValue (engineSettings, 'tradingEnabled'); + const active = displayEnabled && tradingEnabled; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'active': active, + 'limits': { + 'price': { + 'min': this.safeFloat (market, 'minOrderRate'), + 'max': this.safeFloat (market, 'maxOrderRate'), + }, + 'amount': { + 'min': this.safeFloat (baseCurrency, 'minOrderSize'), + 'max': this.safeFloat (baseCurrency, 'maxOrderSize'), + }, + 'cost': { + 'min': this.safeFloat (quoteCurrency, 'minOrderValue'), + 'max': this.safeFloat (quoteCurrency, 'maxOrderValue'), + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostMoneyInfo (params); + const balance = this.safeValue (response, 'data', {}); + const wallets = this.safeValue (balance, 'Wallets', {}); + const currencyIds = Object.keys (wallets); + const result = { 'info': balance }; + for (let c = 0; c < currencyIds.length; c++) { + const currencyId = currencyIds[c]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + const wallet = this.safeValue (wallets, currencyId); + account['free'] = this.safeFloat (wallet['Available_Balance'], 'value'); + account['total'] = this.safeFloat (wallet['Balance'], 'value'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currency_pair': this.marketId (symbol), + }; + const response = await this.publicGetCurrencyPairMoneyDepthFull (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data', {}); + const timestamp = this.safeIntegerProduct (orderbook, 'dataUpdateTime', 0.001); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'currency_pair': this.marketId (symbol), + }; + const response = await this.publicGetCurrencyPairMoneyTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'data', {}); + const timestamp = this.safeIntegerProduct (ticker, 'dataUpdateTime', 0.001); + const bid = this.safeFloat (ticker['buy'], 'value'); + const ask = this.safeFloat (ticker['sell'], 'value'); + const baseVolume = this.safeFloat (ticker['vol'], 'value'); + const last = this.safeFloat (ticker['last'], 'value'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker['high'], 'value'), + 'low': this.safeFloat (ticker['low'], 'value'), + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker['avg'], 'value'), + 'baseVolume': baseVolume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' switched off the trades endpoint, see their docs at https://docs.anxv2.apiary.io'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['max'] = limit; + } + const response = await this.v3privatePostOrderList (this.extend (request, params)); + const orders = this.safeValue (response, 'orders', []); + const market = (symbol === undefined) ? undefined : this.market (symbol); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency_pair': market['id'], + }; + // ANXPro will return all symbol pairs regardless of what is specified in request + const response = await this.privatePostCurrencyPairMoneyOrders (this.extend (request, params)); + // + // { + // "result": "success", + // "data": [ + // { + // "oid": "e74305c7-c424-4fbc-a8a2-b41d8329deb0", + // "currency": "HKD", + // "item": "BTC", + // "type": "offer", + // "amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "effective_amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "price": { + // "currency": "HKD", + // "display": "412.34567 HKD", + // "display_short": "412.35 HKD", + // "value": "412.34567", + // "value_int": "41234567" + // }, + // "status": "open", + // "date": 1393411075000, + // "priority": 1393411075000000, + // "actions": [] + // }, + // ... + // ] + // } + // + return this.parseOrders (this.safeValue (response, 'data', {}), market, since, limit); + } + + parseOrder (order, market = undefined) { + if ('orderId' in order) { + return this.parseOrderV3 (order, market); + } else { + return this.parseOrderV2 (order, market); + } + } + + parseOrderStatus (status) { + const statuses = { + 'ACTIVE': 'open', + 'FULL_FILL': 'closed', + 'CANCEL': 'canceled', + 'USER_CANCEL_PARTIAL': 'canceled', + 'PARTIAL_FILL': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrderV3 (order, market = undefined) { + // + // v3 + // + // { + // orderType: 'LIMIT', + // tradedCurrency: 'XRP', + // settlementCurrency: 'BTC', + // tradedCurrencyAmount: '400.00000000', + // buyTradedCurrency: true, + // limitPriceInSettlementCurrency: '0.00007129', + // timestamp: '1522547850000', + // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', + // tradedCurrencyAmountOutstanding: '0.00000000', + // orderStatus: 'FULL_FILL', + // executedAverageRate: '0.00007127', + // trades: [ + // { + // tradeId: 'fe16b796-df57-41a2-b6d9-3489f189749e', + // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', + // timestamp: '1522547850000', + // tradedCurrencyFillAmount: '107.91298639', + // settlementCurrencyFillAmount: '0.00768772', + // settlementCurrencyFillAmountUnrounded: '0.00768772', + // price: '0.00007124', + // ccyPair: 'XRPBTC' + // }, + // { + // tradeId: 'e2962f67-c094-4243-8b88-0cdc70a1b1c7', + // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', + // timestamp: '1522547851000', + // tradedCurrencyFillAmount: '292.08701361', + // settlementCurrencyFillAmount: '0.02082288', + // settlementCurrencyFillAmountUnrounded: '0.02082288', + // price: '0.00007129', + // ccyPair: 'XRPBTC' + // } + // ] + // } + // + const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); + const base = this.safeCurrencyCode (this.safeString (order, 'tradedCurrency')); + const quote = this.safeCurrencyCode (this.safeString (order, 'settlementCurrency')); + const symbol = base + '/' + quote; + const buyTradedCurrency = this.safeString (order, 'buyTradedCurrency'); + const side = (buyTradedCurrency === 'true') ? 'buy' : 'sell'; + const timestamp = this.safeInteger (order, 'timestamp'); + let lastTradeTimestamp = undefined; + const trades = []; + let filled = 0; + const type = this.safeStringLower (order, 'orderType'); + for (let i = 0; i < order['trades'].length; i++) { + const trade = order['trades'][i]; + const tradeTimestamp = this.safeInteger (trade, 'timestamp'); + if (!lastTradeTimestamp || lastTradeTimestamp < tradeTimestamp) { + lastTradeTimestamp = tradeTimestamp; + } + const parsedTrade = this.extend (this.parseTrade (trade), { 'side': side, 'type': type }); + trades.push (parsedTrade); + filled = this.sum (filled, parsedTrade['amount']); + } + const price = this.safeFloat (order, 'limitPriceInSettlementCurrency'); + const executedAverageRate = this.safeFloat (order, 'executedAverageRate'); + const remaining = (type === 'market') ? 0 : this.safeFloat (order, 'tradedCurrencyAmountOutstanding'); + let amount = this.safeFloat (order, 'tradedCurrencyAmount'); + if (!amount) { + const settlementCurrencyAmount = this.safeFloat (order, 'settlementCurrencyAmount'); + amount = settlementCurrencyAmount / executedAverageRate; + } + const cost = executedAverageRate * filled; + return { + 'id': this.safeString (order, 'orderId'), + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': undefined, + 'trades': trades, + 'info': order, + }; + } + + parseOrderV2 (order, market = undefined) { + // + // v2 + // + // { + // "oid": "e74305c7-c424-4fbc-a8a2-b41d8329deb0", + // "currency": "HKD", + // "item": "BTC", + // "type": "offer", <-- bid/offer + // "amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "effective_amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "price": { + // "currency": "HKD", + // "display": "412.34567 HKD", + // "display_short": "412.35 HKD", + // "value": "412.34567", + // "value_int": "41234567" + // }, + // "status": "open", + // "date": 1393411075000, + // "priority": 1393411075000000, + // "actions": [] + // } + // + const id = this.safeString (order, 'oid'); + const status = this.safeString (order, 'status'); + const timestamp = this.safeInteger (order, 'date'); + const baseId = this.safeString (order, 'item'); + const quoteId = this.safeString (order, 'currency'); + const marketId = baseId + '/' + quoteId; + market = this.safeValue (this.markets_by_id, marketId); + let symbol = undefined; + if (typeof market !== 'undefined') { + symbol = market['symbol']; + } + const amount_info = this.safeValue (order, 'amount', {}); + const effective_info = this.safeValue (order, 'effective_amount', {}); + const price_info = this.safeValue (order, 'price', {}); + const remaining = this.safeFloat (effective_info, 'value'); + const amount = this.safeFloat (amount_info, 'volume'); + const price = this.safeFloat (price_info, 'value'); + let filled = undefined; + let cost = undefined; + if (typeof amount !== 'undefined') { + if (typeof remaining !== 'undefined') { + filled = amount - remaining; + cost = price * filled; + } + } + const orderType = 'limit'; + let side = this.safeString (order, 'type'); + if (side === 'offer') { + side = 'sell'; + } else { + side = 'buy'; + } + const fee = undefined; + const trades = undefined; // todo parse trades + const lastTradeTimestamp = undefined; + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const amountMultiplier = Math.pow (10, market['precision']['amount']); + const request = { + 'currency_pair': market['id'], + 'amount_int': parseInt (amount * amountMultiplier), // 10^8 + }; + if (type === 'limit') { + const priceMultiplier = Math.pow (10, market['precision']['price']); + request['price_int'] = parseInt (price * priceMultiplier); // 10^5 or 10^8 + } + request['type'] = (side === 'buy') ? 'bid' : 'ask'; + const response = await this.privatePostCurrencyPairMoneyOrderAdd (this.extend (request, params)); + return { + 'info': response, + 'id': response['data'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostCurrencyPairMoneyOrderCancel ({ 'oid': id }); + } + + getAmountMultiplier (code) { + const multipliers = { + 'BTC': 100000000, + 'LTC': 100000000, + 'STR': 100000000, + 'XRP': 100000000, + 'DOGE': 100000000, + }; + const defaultValue = 100; + return this.safeInteger (multipliers, code, defaultValue); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const multiplier = this.getAmountMultiplier (code); + const request = { + 'currency': currency, + 'amount_int': parseInt (amount * multiplier), + 'address': address, + }; + if (tag !== undefined) { + request['destinationTag'] = tag; + } + const response = await this.privatePostMoneyCurrencySendSimple (this.extend (request, params)); + return { + 'info': response, + 'id': response['data']['transactionId'], + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostMoneyCurrencyAddress (this.extend (request, params)); + const data = this.safeValue (response, 'data', {}); + const address = this.safeString (data, 'addr'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + '/' + request; + if (api === 'public' || api === 'v3public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + let auth = undefined; + let contentType = undefined; + if (api === 'v3private') { + body = this.json (this.extend ({ 'tonce': nonce * 1000 }, query)); + const path = url.replace ('https://anxpro.com/', ''); + auth = path + '\0' + body; + contentType = 'application/json'; + } else { + body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); + // eslint-disable-next-line quotes + auth = request + "\0" + body; + contentType = 'application/x-www-form-urlencoded'; + } + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); + headers = { + 'Content-Type': contentType, + 'Rest-Key': this.apiKey, + 'Rest-Sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined || response === '') { + return; + } + const result = this.safeString (response, 'result'); + const code = this.safeString (response, 'resultCode'); + const status = this.safeString (response, 'status'); + if (((result !== undefined) && (result !== 'success')) || ((code !== undefined) && (code !== 'OK')) || (status !== undefined)) { + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], status, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],7:[function(require,module,exports){ +(function (Buffer){ +"use strict"; + +// ---------------------------------------------------------------------------- + +const functions = require ('./functions') + +const { + isNode + , keys + , values + , deepExtend + , extend + , clone + , flatten + , unique + , indexBy + , sortBy + , groupBy + , aggregate + , uuid + , unCamelCase + , precisionFromString + , throttle + , capitalize + , now + , timeout + , TimedOut + , buildOHLCVC + , decimalToPrecision + , defaultFetch +} = functions + +const { + ExchangeError + , BadSymbol + , InvalidAddress + , NotSupported + , AuthenticationError + , DDoSProtection + , RequestTimeout + , ExchangeNotAvailable + , RateLimitExceeded } = require ('./errors') + +const { TRUNCATE, ROUND, DECIMAL_PLACES } = functions.precisionConstants + +const BN = require ('../static_dependencies/BN/bn') + +// ---------------------------------------------------------------------------- +// web3 / 0x imports + +let Web3 = undefined + , ethAbi = undefined + , ethUtil = undefined + , BigNumber = undefined + +try { + const requireFunction = require; + Web3 = requireFunction ('web3') // eslint-disable-line global-require + ethAbi = requireFunction ('ethereumjs-abi') // eslint-disable-line global-require + ethUtil = requireFunction ('ethereumjs-util') // eslint-disable-line global-require + BigNumber = requireFunction ('bignumber.js') // eslint-disable-line global-require + // we prefer bignumber.js over BN.js + // BN = requireFunction ('bn.js') // eslint-disable-line global-require +} catch (e) { + // nothing +} + +// ---------------------------------------------------------------------------- + +module.exports = class Exchange { + + describe () { + return { + 'id': undefined, + 'name': undefined, + 'countries': undefined, + 'enableRateLimit': false, + 'rateLimit': 2000, // milliseconds = seconds * 1000 + 'certified': false, + 'has': { + 'cancelAllOrders': false, + 'cancelOrder': true, + 'cancelOrders': false, + 'CORS': false, + 'createDepositAddress': false, + 'createLimitOrder': true, + 'createMarketOrder': true, + 'createOrder': true, + 'deposit': false, + 'editOrder': 'emulated', + 'fetchBalance': true, + 'fetchBidsAsks': false, + 'fetchClosedOrders': false, + 'fetchCurrencies': false, + 'fetchDepositAddress': false, + 'fetchDeposits': false, + 'fetchFundingFees': false, + 'fetchL2OrderBook': true, + 'fetchLedger': false, + 'fetchMarkets': true, + 'fetchMyTrades': false, + 'fetchOHLCV': 'emulated', + 'fetchOpenOrders': false, + 'fetchOrder': false, + 'fetchOrderBook': true, + 'fetchOrderBooks': false, + 'fetchOrders': false, + 'fetchOrderTrades': false, + 'fetchStatus': 'emulated', + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchTime': false, + 'fetchTrades': true, + 'fetchTradingFee': false, + 'fetchTradingFees': false, + 'fetchTradingLimits': false, + 'fetchTransactions': false, + 'fetchWithdrawals': false, + 'privateAPI': true, + 'publicAPI': true, + 'withdraw': false, + }, + 'urls': { + 'logo': undefined, + 'api': undefined, + 'www': undefined, + 'doc': undefined, + 'fees': undefined, + }, + 'api': undefined, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': false, + 'login': false, + 'password': false, + 'twofa': false, // 2-factor authentication (one-time password key) + 'privateKey': false, // a "0x"-prefixed hexstring private key for a wallet + 'walletAddress': false, // the wallet address "0x"-prefixed hexstring + 'token': false, // reserved for HTTP auth in some cases + }, + 'markets': undefined, // to be filled manually or by fetchMarkets + 'currencies': {}, // to be filled manually or by fetchMarkets + 'timeframes': undefined, // redefine if the exchange has.fetchOHLCV + 'fees': { + 'trading': { + 'tierBased': undefined, + 'percentage': undefined, + 'taker': undefined, + 'maker': undefined, + }, + 'funding': { + 'tierBased': undefined, + 'percentage': undefined, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'status': { + 'status': 'ok', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'exceptions': undefined, + 'httpExceptions': { + '422': ExchangeError, + '418': DDoSProtection, + '429': RateLimitExceeded, + '404': ExchangeNotAvailable, + '409': ExchangeNotAvailable, + '500': ExchangeNotAvailable, + '501': ExchangeNotAvailable, + '502': ExchangeNotAvailable, + '520': ExchangeNotAvailable, + '521': ExchangeNotAvailable, + '522': ExchangeNotAvailable, + '525': ExchangeNotAvailable, + '526': ExchangeNotAvailable, + '400': ExchangeNotAvailable, + '403': ExchangeNotAvailable, + '405': ExchangeNotAvailable, + '503': ExchangeNotAvailable, + '530': ExchangeNotAvailable, + '408': RequestTimeout, + '504': RequestTimeout, + '401': AuthenticationError, + '511': AuthenticationError, + }, + // some exchanges report only 'free' on `fetchBlance` call (i.e. report no 'used' funds) + // in this case ccxt will try to infer 'used' funds from open order cache, which might be stale + // still, some exchanges report number of open orders together with balance + // if you set the following flag to 'true' ccxt will leave 'used' funds undefined in case of discrepancy + 'dontGetUsedBalanceFromStaleCache': false, + 'commonCurrencies': { // gets extended/overwritten in subclasses + 'XBT': 'BTC', + 'BCC': 'BCH', + 'DRK': 'DASH', + 'BCHABC': 'BCH', + 'BCHSV': 'BSV', + }, + 'precisionMode': DECIMAL_PLACES, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + } // return + } // describe () + + constructor (userConfig = {}) { + Object.assign (this, functions) + // if (isNode) { + // this.nodeVersion = process.version.match (/\d+\.\d+\.\d+/)[0] + // this.userAgent = { + // 'User-Agent': 'ccxt/' + Exchange.ccxtVersion + + // ' (+https://github.com/ccxt/ccxt)' + + // ' Node.js/' + this.nodeVersion + ' (JavaScript)' + // } + // } + + this.options = {} // exchange-specific options, if any + + // fetch implementation options (JS only) + this.fetchOptions = { + // keepalive: true, // does not work in Chrome, https://github.com/ccxt/ccxt/issues/6368 + } + + this.userAgents = { + 'chrome': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', + 'chrome39': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36', + } + + this.headers = {} + + // prepended to URL, like https://proxy.com/https://exchange.com/api... + this.proxy = '' + this.origin = '*' // CORS origin + + this.minFundingAddressLength = 1 // used in checkAddress + this.substituteCommonCurrencyCodes = true // reserved + + // do not delete this line, it is needed for users to be able to define their own fetchImplementation + this.fetchImplementation = defaultFetch + + this.timeout = 10000 // milliseconds + this.verbose = false + this.debug = false + this.userAgent = undefined + this.twofa = undefined // two-factor authentication (2FA) + + this.apiKey = undefined + this.secret = undefined + this.uid = undefined + this.login = undefined + this.password = undefined + this.privateKey = undefined // a "0x"-prefixed hexstring private key for a wallet + this.walletAddress = undefined // a wallet address "0x"-prefixed hexstring + this.token = undefined // reserved for HTTP auth in some cases + + this.balance = {} + this.orderbooks = {} + this.tickers = {} + this.orders = {} + this.trades = {} + this.transactions = {} + this.ohlcvs = {} + + this.requiresWeb3 = false + this.precision = {} + + this.enableLastJsonResponse = true + this.enableLastHttpResponse = true + this.enableLastResponseHeaders = true + this.last_http_response = undefined + this.last_json_response = undefined + this.last_response_headers = undefined + + this.arrayConcat = (a, b) => a.concat (b) + + const unCamelCaseProperties = (obj = this) => { + if (obj !== null) { + for (const k of Object.getOwnPropertyNames (obj)) { + this[unCamelCase (k)] = this[k] + } + unCamelCaseProperties (Object.getPrototypeOf (obj)) + } + } + unCamelCaseProperties () + + // merge configs + const config = deepExtend (this.describe (), userConfig) + + // merge to this + for (const [property, value] of Object.entries (config)) + this[property] = deepExtend (this[property], value) + + if (!this.httpAgent) { + this.httpAgent = defaultFetch.http ? new defaultFetch.http.Agent ({ 'keepAlive': true }) : undefined + } + + if (!this.httpsAgent) { + this.httpsAgent = defaultFetch.https ? new defaultFetch.https.Agent ({ 'keepAlive': true }) : undefined + } + + // generate old metainfo interface + for (const k in this.has) { + this['has' + capitalize (k)] = !!this.has[k] // converts 'emulated' to true + } + + if (this.api) + this.defineRestApi (this.api, 'request') + + this.initRestRateLimiter () + + if (this.markets) + this.setMarkets (this.markets) + + if (this.requiresWeb3 && !this.web3 && Web3) { + const provider = (this.web3ProviderURL) ? new Web3.providers.HttpProvider (this.web3ProviderURL) : new Web3.providers.HttpProvider () + this.web3 = new Web3 (Web3.givenProvider || provider) + } + } + + defaults () { + return { /* override me */ } + } + + nonce () { + return this.seconds () + } + + encodeURIComponent (...args) { + return encodeURIComponent (...args) + } + + checkRequiredCredentials (error = true) { + const keys = Object.keys (this.requiredCredentials) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (this.requiredCredentials[key] && !this[key]) { + if (error) { + throw new AuthenticationError (this.id + ' requires `' + key + '` credential') + } else { + return error + } + } + } + return true + } + + checkAddress (address) { + + if (address === undefined) + throw new InvalidAddress (this.id + ' address is undefined') + + // check the address is not the same letter like 'aaaaa' nor too short nor has a space + if ((unique (address).length === 1) || address.length < this.minFundingAddressLength || address.includes (' ')) + throw new InvalidAddress (this.id + ' address is invalid or has less than ' + this.minFundingAddressLength.toString () + ' characters: "' + this.json (address) + '"') + + return address + } + + initRestRateLimiter () { + + if (this.rateLimit === undefined) + throw new Error (this.id + '.rateLimit property is not configured') + + this.tokenBucket = this.extend ({ + refillRate: 1 / this.rateLimit, + delay: 1, + capacity: 1, + defaultCost: 1, + maxCapacity: 1000, + }, this.tokenBucket) + + this.throttle = throttle (this.tokenBucket) + + this.executeRestRequest = (url, method = 'GET', headers = undefined, body = undefined) => { + + // fetchImplementation cannot be called on this. in browsers: + // TypeError Failed to execute 'fetch' on 'Window': Illegal invocation + const fetchImplementation = this.fetchImplementation + + const params = { method, headers, body, timeout: this.timeout } + + if (this.agent) { + this.agent.keepAlive = true + params['agent'] = this.agent + } else if (this.httpAgent && url.indexOf ('http://') === 0) { + params['agent'] = this.httpAgent + } else if (this.httpsAgent && url.indexOf ('https://') === 0) { + params['agent'] = this.httpsAgent + } + + const promise = + fetchImplementation (url, this.extend (params, this.fetchOptions)) + .catch ((e) => { + if (isNode) + throw new ExchangeNotAvailable ([ this.id, method, url, e.type, e.message ].join (' ')) + throw e // rethrow all unknown errors + }) + .then (response => this.handleRestResponse (response, url, method, headers, body)) + + return timeout (this.timeout, promise).catch ((e) => { + if (e instanceof TimedOut) + throw new RequestTimeout (this.id + ' ' + method + ' ' + url + ' request timed out (' + this.timeout + ' ms)') + throw e + }) + } + } + + setSandboxMode (enabled) { + if (!!enabled) { // eslint-disable-line no-extra-boolean-cast + if ('test' in this.urls) { + if (typeof this.urls['api'] === 'string') { + this.urls['api_backup'] = this.urls['api'] + this.urls['api'] = this.urls['test'] + } else { + this.urls['api_backup'] = clone (this.urls['api']) + this.urls['api'] = clone (this.urls['test']) + } + } else { + throw new NotSupported (this.id + ' does not have a sandbox URL') + } + } else if ('api_backup' in this.urls) { + if (typeof this.urls['api'] === 'string') { + this.urls['api'] = this.urls['api_backup'] + } else { + this.urls['api'] = clone (this.urls['api_backup']) + } + } + } + + defineRestApi (api, methodName, options = {}) { + + for (const type of Object.keys (api)) { + for (const httpMethod of Object.keys (api[type])) { + + let paths = api[type][httpMethod] + for (let i = 0; i < paths.length; i++) { + let path = paths[i].trim () + let splitPath = path.split (/[^a-zA-Z0-9]/) + + let uppercaseMethod = httpMethod.toUpperCase () + let lowercaseMethod = httpMethod.toLowerCase () + let camelcaseMethod = this.capitalize (lowercaseMethod) + let camelcaseSuffix = splitPath.map (this.capitalize).join ('') + let underscoreSuffix = splitPath.map (x => x.trim ().toLowerCase ()).filter (x => x.length > 0).join ('_') + + let camelcase = type + camelcaseMethod + this.capitalize (camelcaseSuffix) + let underscore = type + '_' + lowercaseMethod + '_' + underscoreSuffix + + if ('suffixes' in options) { + if ('camelcase' in options['suffixes']) + camelcase += options['suffixes']['camelcase'] + if ('underscore' in options.suffixes) + underscore += options['suffixes']['underscore'] + } + + if ('underscore_suffix' in options) + underscore += options.underscoreSuffix; + if ('camelcase_suffix' in options) + camelcase += options.camelcaseSuffix; + + let partial = async params => this[methodName] (path, type, uppercaseMethod, params || {}) + + this[camelcase] = partial + this[underscore] = partial + } + } + } + } + + fetch (url, method = 'GET', headers = undefined, body = undefined) { + + if (isNode && this.userAgent) { + if (typeof this.userAgent === 'string') + headers = extend ({ 'User-Agent': this.userAgent }, headers) + else if ((typeof this.userAgent === 'object') && ('User-Agent' in this.userAgent)) + headers = extend (this.userAgent, headers) + } + + if (typeof this.proxy === 'function') { + + url = this.proxy (url) + if (isNode) + headers = extend ({ 'Origin': this.origin }, headers) + + } else if (typeof this.proxy === 'string') { + + if (this.proxy.length) + if (isNode) + headers = extend ({ 'Origin': this.origin }, headers) + + url = this.proxy + url + } + + headers = extend (this.headers, headers) + + if (this.verbose) + console.log ("fetch:\n", this.id, method, url, "\nRequest:\n", headers, "\n", body, "\n") + + return this.executeRestRequest (url, method, headers, body) + } + + async fetch2 (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + + if (this.enableRateLimit) + await this.throttle () + + const request = this.sign (path, type, method, params, headers, body) + return this.fetch (request.url, request.method, request.headers, request.body) + } + + request (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + return this.fetch2 (path, type, method, params, headers, body) + } + + parseJson (jsonString) { + try { + if (this.isJsonEncodedObject (jsonString)) { + return JSON.parse (jsonString) + } + } catch (e) { + // SyntaxError + return undefined + } + } + + throwExactlyMatchedException (exact, string, message) { + if (string in exact) { + throw new exact[string] (message) + } + } + + throwBroadlyMatchedException (broad, string, message) { + const broadKey = this.findBroadlyMatchedKey (broad, string) + if (broadKey !== undefined) { + throw new broad[broadKey] (message) + } + } + + // a helper for matching error strings exactly vs broadly + findBroadlyMatchedKey (broad, string) { + const keys = Object.keys (broad) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (string.indexOf (key) >= 0) { + return key + } + } + return undefined + } + + handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { + // override me + } + + defaultErrorHandler (code, reason, url, method, headers, body, response) { + if ((code >= 200) && (code <= 299)) { + return + } + let details = body + const codeAsString = code.toString () + let ErrorClass = undefined + if (codeAsString in this.httpExceptions) { + ErrorClass = this.httpExceptions[codeAsString] + } + if (response === undefined) { + const maintenance = body.match (/offline|busy|retry|wait|unavailable|maintain|maintenance|maintenancing/i) + const ddosProtection = body.match (/cloudflare|incapsula|overload|ddos/i) + if (maintenance) { + ErrorClass = ExchangeNotAvailable + details += ' offline, on maintenance, or unreachable from this location at the moment' + } + if (ddosProtection) { + ErrorClass = DDoSProtection + } + } + if (ErrorClass === ExchangeNotAvailable) { + details += ' (possible reasons: ' + [ + 'invalid API keys', + 'bad or old nonce', + 'exchange is down or offline', + 'on maintenance', + 'DDoS protection', + 'rate-limiting', + ].join (', ') + ')' + } + if (ErrorClass !== undefined) { + throw new ErrorClass ([ this.id, method, url, code, reason, details ].join (' ')) + } + } + + getResponseHeaders (response) { + const result = {} + response.headers.forEach ((value, key) => { + key = key.split ('-').map (word => capitalize (word)).join ('-') + result[key] = value + }) + return result + } + + handleRestResponse (response, url, method = 'GET', requestHeaders = undefined, requestBody = undefined) { + + return response.text ().then ((responseBody) => { + + const json = this.parseJson (responseBody) + + const responseHeaders = this.getResponseHeaders (response) + + if (this.enableLastResponseHeaders) { + this.last_response_headers = responseHeaders + } + + if (this.enableLastHttpResponse) { + this.last_http_response = responseBody // FIXME: for those classes that haven't switched to handleErrors yet + } + + if (this.enableLastJsonResponse) { + this.last_json_response = json // FIXME: for those classes that haven't switched to handleErrors yet + } + + if (this.verbose) + console.log ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponse:\n", responseHeaders, "\n", responseBody, "\n") + + this.handleErrors (response.status, response.statusText, url, method, responseHeaders, responseBody, json, requestHeaders, requestBody) + this.defaultErrorHandler (response.status, response.statusText, url, method, responseHeaders, responseBody, json) + + return json || responseBody + }) + } + + setMarkets (markets, currencies = undefined) { + const values = Object.values (markets).map (market => deepExtend ({ + 'limits': this.limits, + 'precision': this.precision, + }, this.fees['trading'], market)) + this.markets = indexBy (values, 'symbol') + this.marketsById = indexBy (markets, 'id') + this.markets_by_id = this.marketsById + this.symbols = Object.keys (this.markets).sort () + this.ids = Object.keys (this.markets_by_id).sort () + if (currencies) { + this.currencies = deepExtend (currencies, this.currencies) + } else { + const baseCurrencies = + values.filter (market => 'base' in market) + .map (market => ({ + id: market.baseId || market.base, + numericId: (market.baseNumericId !== undefined) ? market.baseNumericId : undefined, + code: market.base, + precision: market.precision ? (market.precision.base || market.precision.amount) : 8, + })) + const quoteCurrencies = + values.filter (market => 'quote' in market) + .map (market => ({ + id: market.quoteId || market.quote, + numericId: (market.quoteNumericId !== undefined) ? market.quoteNumericId : undefined, + code: market.quote, + precision: market.precision ? (market.precision.quote || market.precision.price) : 8, + })) + const allCurrencies = baseCurrencies.concat (quoteCurrencies) + const groupedCurrencies = groupBy (allCurrencies, 'code') + const currencies = Object.keys (groupedCurrencies).map (code => + groupedCurrencies[code].reduce ((previous, current) => + ((previous.precision > current.precision) ? previous : current), groupedCurrencies[code][0])) + const sortedCurrencies = sortBy (flatten (currencies), 'code') + this.currencies = deepExtend (indexBy (sortedCurrencies, 'code'), this.currencies) + } + this.currencies_by_id = indexBy (this.currencies, 'id') + return this.markets + } + + async loadMarkets (reload = false, params = {}) { + if (!reload && this.markets) { + if (!this.markets_by_id) { + return this.setMarkets (this.markets) + } + return this.markets + } + let currencies = undefined + if (this.has.fetchCurrencies) { + currencies = await this.fetchCurrencies () + } + const markets = await this.fetchMarkets (params) + return this.setMarkets (markets, currencies) + } + + async loadAccounts (reload = false, params = {}) { + if (reload) { + this.accounts = await this.fetchAccounts (params) + } else { + if (this.accounts) { + return this.accounts + } else { + this.accounts = await this.fetchAccounts (params) + } + } + this.accountsById = this.indexBy (this.accounts, 'id') + return this.accounts + } + + fetchBidsAsks (symbols = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchBidsAsks not supported yet') + } + + async fetchOHLCVC (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) { + if (!this.has['fetchTrades']) + throw new NotSupported (this.id + ' fetchOHLCV() not supported yet') + await this.loadMarkets () + const trades = await this.fetchTrades (symbol, since, limits, params) + const ohlcvc = buildOHLCVC (trades, timeframe, since, limits) + return ohlcvc + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) { + if (!this.has['fetchTrades']) + throw new NotSupported (this.id + ' fetchOHLCV() not supported yet') + await this.loadMarkets () + const trades = await this.fetchTrades (symbol, since, limits, params) + const ohlcvc = buildOHLCVC (trades, timeframe, since, limits) + return ohlcvc.map (c => c.slice (0, -1)) + } + + parseTradingViewOHLCV (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + const result = this.convertTradingViewToOHLCV (ohlcvs); + return this.parseOHLCVs (result, market, timeframe, since, limit); + } + + convertTradingViewToOHLCV (ohlcvs) { + const result = []; + for (let i = 0; i < ohlcvs['t'].length; i++) { + result.push ([ + ohlcvs['t'][i] * 1000, + ohlcvs['o'][i], + ohlcvs['h'][i], + ohlcvs['l'][i], + ohlcvs['c'][i], + ohlcvs['v'][i], + ]); + } + return result; + } + + convertOHLCVToTradingView (ohlcvs) { + const result = { + 't': [], + 'o': [], + 'h': [], + 'l': [], + 'c': [], + 'v': [], + }; + for (let i = 0; i < ohlcvs.length; i++) { + result['t'].push (parseInt (ohlcvs[i][0] / 1000)); + result['o'].push (ohlcvs[i][1]); + result['h'].push (ohlcvs[i][2]); + result['l'].push (ohlcvs[i][3]); + result['c'].push (ohlcvs[i][4]); + result['v'].push (ohlcvs[i][5]); + } + return result; + } + + fetchTicker (symbol, params = {}) { + throw new NotSupported (this.id + ' fetchTicker not supported yet') + } + + fetchTickers (symbols = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchTickers not supported yet') + } + + purgeCachedOrders (before) { + const orders = Object + .values (this.orders) + .filter (order => + (order.status === 'open') || + (order.timestamp >= before)) + this.orders = indexBy (orders, 'id') + return this.orders + } + + fetchOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrder not supported yet'); + } + + fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrders not supported yet'); + } + + fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOpenOrders not supported yet'); + } + + fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchClosedOrders not supported yet'); + } + + fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchMyTrades not supported yet'); + } + + fetchTransactions (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchTransactions not supported yet'); + } + + fetchDeposits (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchDeposits not supported yet'); + } + + fetchWithdrawals (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchWithdrawals not supported yet'); + } + + fetchCurrencies (params = {}) { + // markets are returned as a list + // currencies are returned as a dict + // this is for historical reasons + // and may be changed for consistency later + return new Promise ((resolve, reject) => resolve (this.currencies)); + } + + fetchMarkets (params = {}) { + // markets are returned as a list + // currencies are returned as a dict + // this is for historical reasons + // and may be changed for consistency later + return new Promise ((resolve, reject) => resolve (Object.values (this.markets))) + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + const order = await this.fetchOrder (id, symbol, params); + return order['status']; + } + + account () { + return { + 'free': undefined, + 'used': undefined, + 'total': undefined, + } + } + + commonCurrencyCode (currency) { + if (!this.substituteCommonCurrencyCodes) + return currency + return this.safeString (this.commonCurrencies, currency, currency) + } + + currencyId (commonCode) { + + if (this.currencies === undefined) { + throw new ExchangeError (this.id + ' currencies not loaded') + } + + if (commonCode in this.currencies) { + return this.currencies[commonCode]['id']; + } + + const currencyIds = {} + const distinct = Object.keys (this.commonCurrencies) + for (let i = 0; i < distinct.length; i++) { + const k = distinct[i] + currencyIds[this.commonCurrencies[k]] = k + } + + return this.safeString (currencyIds, commonCode, commonCode) + } + + currency (code) { + + if (this.currencies === undefined) + throw new ExchangeError (this.id + ' currencies not loaded') + + if ((typeof code === 'string') && (code in this.currencies)) + return this.currencies[code] + + throw new ExchangeError (this.id + ' does not have currency code ' + code) + } + + market (symbol) { + + if (this.markets === undefined) + throw new ExchangeError (this.id + ' markets not loaded') + + if ((typeof symbol === 'string') && (symbol in this.markets)) + return this.markets[symbol] + + throw new BadSymbol (this.id + ' does not have market symbol ' + symbol) + } + + marketId (symbol) { + const market = this.market (symbol) + return (market !== undefined ? market['id'] : symbol) + } + + marketIds (symbols) { + return symbols.map (symbol => this.marketId (symbol)); + } + + symbol (symbol) { + return this.market (symbol).symbol || symbol + } + + url (path, params = {}) { + let result = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)) + if (Object.keys (query).length) + result += '?' + this.urlencode (query) + return result + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1) { + const price = parseFloat (bidask[priceKey]) + const amount = parseFloat (bidask[amountKey]) + return [ price, amount ] + } + + parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) { + return Object.values (bidasks || []).map (bidask => this.parseBidAsk (bidask, priceKey, amountKey)) + } + + async fetchL2OrderBook (symbol, limit = undefined, params = {}) { + const orderbook = await this.fetchOrderBook (symbol, limit, params) + return extend (orderbook, { + 'bids': sortBy (aggregate (orderbook.bids), 0, true), + 'asks': sortBy (aggregate (orderbook.asks), 0), + }) + } + + parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1) { + return { + 'bids': sortBy ((bidsKey in orderbook) ? this.parseBidsAsks (orderbook[bidsKey], priceKey, amountKey) : [], 0, true), + 'asks': sortBy ((asksKey in orderbook) ? this.parseBidsAsks (orderbook[asksKey], priceKey, amountKey) : [], 0), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + } + } + + parseBalance (balance) { + + const currencies = Object.keys (this.omit (balance, [ 'info', 'free', 'used', 'total' ])); + + balance['free'] = {} + balance['used'] = {} + balance['total'] = {} + + for (const currency of currencies) { + + if (balance[currency].total === undefined) { + if (balance[currency].free !== undefined && balance[currency].used !== undefined) { + balance[currency].total = this.sum (balance[currency].free, balance[currency].used) + } + } + if (balance[currency].free === undefined) { + if (balance[currency].total !== undefined && balance[currency].used !== undefined) { + balance[currency].free = this.sum (balance[currency].total, -balance[currency].used) + } + } + if (balance[currency].used === undefined) { + if (balance[currency].total !== undefined && balance[currency].free !== undefined) { + balance[currency].used = this.sum (balance[currency].total, -balance[currency].free) + } + } + + balance.free[currency] = balance[currency].free + balance.used[currency] = balance[currency].used + balance.total[currency] = balance[currency].total + } + + return balance + } + + async fetchPartialBalance (part, params = {}) { + const balance = await this.fetchBalance (params) + return balance[part] + } + + fetchFreeBalance (params = {}) { + return this.fetchPartialBalance ('free', params) + } + + fetchUsedBalance (params = {}) { + return this.fetchPartialBalance ('used', params) + } + + fetchTotalBalance (params = {}) { + return this.fetchPartialBalance ('total', params) + } + + async fetchStatus (params = {}) { + if (this.has['fetchTime']) { + const time = await this.fetchTime(params) + return this.status = this.extend(this.status, { + 'updated': time, + }) + } + return this.status + } + + async fetchTradingFees (params = {}) { + throw new NotSupported (this.id + ' fetchTradingFees not supported yet') + } + + async fetchTradingFee (symbol, params = {}) { + if (!this.has['fetchTradingFees']) { + throw new NotSupported (this.id + ' fetchTradingFee not supported yet') + } + return await this.fetchTradingFees (params) + } + + async loadTradingLimits (symbols = undefined, reload = false, params = {}) { + if (this.has['fetchTradingLimits']) { + if (reload || !('limitsLoaded' in this.options)) { + const response = await this.fetchTradingLimits (symbols); + for (let i = 0; i < symbols.length; i++) { + const symbol = symbols[i]; + this.markets[symbol] = this.deepExtend (this.markets[symbol], response[symbol]); + } + this.options['limitsLoaded'] = this.milliseconds (); + } + } + return this.markets; + } + + filterBySinceLimit (array, since = undefined, limit = undefined, key = 'timestamp') { + if (since !== undefined && since !== null) + array = array.filter (entry => entry[key] >= since) + if (limit !== undefined && limit !== null) + array = array.slice (0, limit) + return array + } + + filterByValueSinceLimit (array, field, value = undefined, since = undefined, limit = undefined) { + + const valueIsDefined = value !== undefined && value !== null + const sinceIsDefined = since !== undefined && since !== null + + // single-pass filter for both symbol and since + if (valueIsDefined || sinceIsDefined) + array = Object.values (array).filter (entry => + ((valueIsDefined ? (entry[field] === value) : true) && + (sinceIsDefined ? (entry.timestamp >= since) : true))) + + if (limit !== undefined && limit !== null) + array = Object.values (array).slice (0, limit) + + return array + } + + filterBySymbolSinceLimit (array, symbol = undefined, since = undefined, limit = undefined) { + return this.filterByValueSinceLimit (array, 'symbol', symbol, since, limit) + } + + filterByCurrencySinceLimit (array, code = undefined, since = undefined, limit = undefined) { + return this.filterByValueSinceLimit (array, 'currency', code, since, limit) + } + + filterByArray (objects, key, values = undefined, indexed = true) { + + objects = Object.values (objects) + + // return all of them if no values were passed + if (values === undefined || values === null) + return indexed ? indexBy (objects, key) : objects + + const result = [] + for (let i = 0; i < objects.length; i++) { + if (values.includes (objects[i][key])) + result.push (objects[i]) + } + + return indexed ? indexBy (result, key) : result + } + + parseTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (trades)) { + // throw new ExchangeError (this.id + ' parseTrades expected an array in the trades argument, but got ' + typeof trades); + // } + let result = Object.values (trades || []).map (trade => this.extend (this.parseTrade (trade, market), params)) + result = sortBy (result, 'timestamp') + const symbol = (market !== undefined) ? market['symbol'] : undefined + return this.filterBySymbolSinceLimit (result, symbol, since, limit) + } + + parseTransactions (transactions, currency = undefined, since = undefined, limit = undefined, params = {}) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (transactions)) { + // throw new ExchangeError (this.id + ' parseTransactions expected an array in the transactions argument, but got ' + typeof transactions); + // } + let result = Object.values (transactions || []).map (transaction => this.extend (this.parseTransaction (transaction, currency), params)) + result = this.sortBy (result, 'timestamp'); + const code = (currency !== undefined) ? currency['code'] : undefined; + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + parseLedger (data, currency = undefined, since = undefined, limit = undefined, params = {}) { + let result = []; + const array = Object.values (data || []); + for (let i = 0; i < array.length; i++) { + const itemOrItems = this.parseLedgerEntry (array[i], currency); + if (Array.isArray (itemOrItems)) { + for (let j = 0; j < itemOrItems.length; j++) { + result.push (this.extend (itemOrItems[j], params)); + } + } else { + result.push (this.extend (itemOrItems, params)); + } + } + result = this.sortBy (result, 'timestamp'); + const code = (currency !== undefined) ? currency['code'] : undefined; + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (orders)) { + // throw new ExchangeError (this.id + ' parseOrders expected an array in the orders argument, but got ' + typeof orders); + // } + let result = Object.values (orders).map (order => this.extend (this.parseOrder (order, market), params)) + result = sortBy (result, 'timestamp') + const symbol = (market !== undefined) ? market['symbol'] : undefined + return this.filterBySymbolSinceLimit (result, symbol, since, limit) + } + + safeCurrencyCode (currencyId, currency = undefined) { + let code = undefined + if (currencyId !== undefined) { + if (this.currencies_by_id !== undefined && currencyId in this.currencies_by_id) { + code = this.currencies_by_id[currencyId]['code'] + } else { + code = this.commonCurrencyCode (currencyId.toUpperCase ()) + } + } + if (code === undefined && currency !== undefined) { + code = currency['code'] + } + return code + } + + filterBySymbol (array, symbol = undefined) { + return ((symbol !== undefined) ? array.filter (entry => entry.symbol === symbol) : array) + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return Array.isArray (ohlcv) ? ohlcv.slice (0, 6) : ohlcv + } + + parseOHLCVs (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (ohlcvs)) { + // throw new ExchangeError (this.id + ' parseOHLCVs expected an array in the ohlcvs argument, but got ' + typeof ohlcvs); + // } + ohlcvs = Object.values (ohlcvs || []) + const result = [] + for (let i = 0; i < ohlcvs.length; i++) { + if (limit && (result.length >= limit)) { + break; + } + const ohlcv = this.parseOHLCV (ohlcvs[i], market, timeframe, since, limit) + if (since && (ohlcv[0] < since)) { + continue + } + result.push (ohlcv) + } + return this.sortBy (result, 0) + } + + editLimitBuyOrder (id, symbol, ...args) { + return this.editLimitOrder (id, symbol, 'buy', ...args) + } + + editLimitSellOrder (id, symbol, ...args) { + return this.editLimitOrder (id, symbol, 'sell', ...args) + } + + editLimitOrder (id, symbol, ...args) { + return this.editOrder (id, symbol, 'limit', ...args) + } + + async editOrder (id, symbol, ...args) { + if (!this.enableRateLimit) + throw new ExchangeError (this.id + ' editOrder() requires enableRateLimit = true') + await this.cancelOrder (id, symbol); + return this.createOrder (symbol, ...args) + } + + createLimitOrder (symbol, ...args) { + return this.createOrder (symbol, 'limit', ...args) + } + + createMarketOrder (symbol, ...args) { + return this.createOrder (symbol, 'market', ...args) + } + + createLimitBuyOrder (symbol, ...args) { + return this.createOrder (symbol, 'limit', 'buy', ...args) + } + + createLimitSellOrder (symbol, ...args) { + return this.createOrder (symbol, 'limit', 'sell', ...args) + } + + createMarketBuyOrder (symbol, amount, params = {}) { + return this.createOrder (symbol, 'market', 'buy', amount, undefined, params) + } + + createMarketSellOrder (symbol, amount, params = {}) { + return this.createOrder (symbol, 'market', 'sell', amount, undefined, params) + } + + costToPrecision (symbol, cost) { + return decimalToPrecision (cost, ROUND, this.markets[symbol].precision.price, this.precisionMode) + } + + priceToPrecision (symbol, price) { + return decimalToPrecision (price, ROUND, this.markets[symbol].precision.price, this.precisionMode) + } + + amountToPrecision (symbol, amount) { + return decimalToPrecision (amount, TRUNCATE, this.markets[symbol].precision.amount, this.precisionMode) + } + + feeToPrecision (symbol, fee) { + return decimalToPrecision (fee, ROUND, this.markets[symbol].precision.price, this.precisionMode) + } + + currencyToPrecision (currency, fee) { + return decimalToPrecision (fee, ROUND, this.currencies[currency]['precision'], this.precisionMode); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol] + const rate = market[takerOrMaker] + const cost = parseFloat (this.costToPrecision (symbol, amount * price)) + return { + 'type': takerOrMaker, + 'currency': market['quote'], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), + } + } + + // ------------------------------------------------------------------------ + // web3 / 0x methods + static hasWeb3 () { + return Web3 && ethUtil && ethAbi && BigNumber + } + + checkRequiredDependencies () { + if (!Exchange.hasWeb3 ()) { + throw new ExchangeError ("Required dependencies missing: \nnpm i web3 ethereumjs-util ethereumjs-abi bignumber.js --no-save"); + } + } + + ethDecimals (unit = 'ether') { + const units = { + 'wei': 0, // 1 + 'kwei': 3, // 1000 + 'babbage': 3, // 1000 + 'femtoether': 3, // 1000 + 'mwei': 6, // 1000000 + 'lovelace': 6, // 1000000 + 'picoether': 6, // 1000000 + 'gwei': 9, // 1000000000 + 'shannon': 9, // 1000000000 + 'nanoether': 9, // 1000000000 + 'nano': 9, // 1000000000 + 'szabo': 12, // 1000000000000 + 'microether': 12, // 1000000000000 + 'micro': 12, // 1000000000000 + 'finney': 15, // 1000000000000000 + 'milliether': 15, // 1000000000000000 + 'milli': 15, // 1000000000000000 + 'ether': 18, // 1000000000000000000 + 'kether': 21, // 1000000000000000000000 + 'grand': 21, // 1000000000000000000000 + 'mether': 24, // 1000000000000000000000000 + 'gether': 27, // 1000000000000000000000000000 + 'tether': 30, // 1000000000000000000000000000000 + } + return this.safeValue (units, unit) + } + + ethUnit (decimals = 18) { + const units = { + 0: 'wei', // 1000000000000000000 + 3: 'kwei', // 1000000000000000 + 6: 'mwei', // 1000000000000 + 9: 'gwei', // 1000000000 + 12: 'szabo', // 1000000 + 15: 'finney', // 1000 + 18: 'ether', // 1 + 21: 'kether', // 0.001 + 24: 'mether', // 0.000001 + 27: 'gether', // 0.000000001 + 30: 'tether', // 0.000000000001 + } + return this.safeValue (units, decimals) + } + + fromWei (amount, unit = 'ether', decimals = 18) { + if (amount === undefined) { + return amount + } + if (decimals !== 18) { + amount = new BigNumber (amount).times (new BigNumber (10 ** (18 - decimals))).toFixed () + } else { + amount = new BigNumber (amount).toFixed () + } + return parseFloat (this.web3.utils.fromWei (amount, unit)) + } + + toWei (amount, unit = 'ether', decimals = 18) { + if (amount === undefined) { + return amount + } + if (decimals !== 18) { + amount = new BigNumber (this.numberToString (amount)).div (new BigNumber (10 ** (18 - decimals))).toFixed () + } else { + amount = this.numberToString (amount) + } + return this.web3.utils.toWei (amount, unit) + } + + soliditySha3 (array) { + const values = this.solidityValues (array); + const types = this.solidityTypes (values); + return '0x' + ethAbi.soliditySHA3 (types, values).toString ('hex') + } + + solidityTypes (array) { + return array.map (value => (this.web3.utils.isAddress (value) ? 'address' : 'uint256')) + } + + solidityValues (array) { + return array.map (value => (this.web3.utils.isAddress (value) ? value : (new BigNumber (value).toFixed ()))) + } + + getZeroExOrderHash (order) { + return this.soliditySha3 ([ + order['exchangeContractAddress'], // address + order['maker'], // address + order['taker'], // address + order['makerTokenAddress'], // address + order['takerTokenAddress'], // address + order['feeRecipient'], // address + order['makerTokenAmount'], // uint256 + order['takerTokenAmount'], // uint256 + order['makerFee'], // uint256 + order['takerFee'], // uint256 + order['expirationUnixTimestampSec'], // uint256 + order['salt'], // uint256 + ]); + } + + getZeroExOrderHashV2 (order) { + // https://github.com/0xProject/0x-monorepo/blob/development/python-packages/order_utils/src/zero_ex/order_utils/__init__.py + const addressPadding = '000000000000000000000000'; + const header = '1901'; + const domainStructHeader = '91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766f0f24618f4c4be1e62e026fb039a20ef96f4495294817d1027ffaa6d1f70e61ead7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5'; + const orderSchemaHash = '770501f88a26ede5c04a20ef877969e961eb11fc13b78aaf414b633da0d4f86f'; + + const domainStructHash = ethAbi.soliditySHA3 ( + [ + 'bytes', + 'bytes', + 'address' + ], + [ + Buffer.from (domainStructHeader, 'hex'), + Buffer.from (addressPadding, 'hex'), + order['exchangeAddress'] + ] + ); + const orderStructHash = ethAbi.soliditySHA3 ( + [ + 'bytes', + 'bytes', + 'address', + 'bytes', + 'address', + 'bytes', + 'address', + 'bytes', + 'address', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'string', + 'string' + ], + [ + Buffer.from (orderSchemaHash, 'hex'), + Buffer.from (addressPadding, 'hex'), + order['makerAddress'], + Buffer.from (addressPadding, 'hex'), + order['takerAddress'], + Buffer.from (addressPadding, 'hex'), + order['feeRecipientAddress'], + Buffer.from (addressPadding, 'hex'), + order['senderAddress'], + order['makerAssetAmount'], + order['takerAssetAmount'], + order['makerFee'], + order['takerFee'], + order['expirationTimeSeconds'], + order['salt'], + ethUtil.keccak (Buffer.from (order['makerAssetData'].slice (2), 'hex')), + ethUtil.keccak (Buffer.from (order['takerAssetData'].slice (2), 'hex')), + ] + ); + return '0x' + ethUtil.keccak (Buffer.concat ([ + Buffer.from (header, 'hex'), + domainStructHash, + orderStructHash + ])).toString ('hex'); + } + + signZeroExOrder (order, privateKey) { + const orderHash = this.getZeroExOrderHash (order); + const signature = this.signMessage (orderHash, privateKey); + return this.extend (order, { + 'orderHash': orderHash, + 'ecSignature': signature, // todo fix v if needed + }) + } + + signZeroExOrderV2 (order, privateKey) { + const orderHash = this.getZeroExOrderHashV2 (order); + const signature = this.signMessage (orderHash, privateKey); + return this.extend (order, { + 'orderHash': orderHash, + 'signature': this.convertECSignatureToSignatureHex (signature), + }) + } + + convertECSignatureToSignatureHex (signature) { + // https://github.com/0xProject/0x-monorepo/blob/development/packages/order-utils/src/signature_utils.ts + let v = signature.v; + if (v !== 27 && v !== 28) { + v = v + 27; + } + return '0x' + v.toString (16) + signature['r'].slice (-64) + signature['s'].slice (-64) + '03' + } + + static remove0xPrefix (hexData) { + if (hexData.slice (0, 2) === '0x') { + return hexData.slice (2) + } else { + return hexData + } + } + + hashMessage (message) { + // takes a hex encoded message + const binaryMessage = this.base16ToBinary (Exchange.remove0xPrefix (message)) + const prefix = this.stringToBinary ('\x19Ethereum Signed Message:\n' + binaryMessage.sigBytes) + return '0x' + this.hash (this.binaryConcat (prefix, binaryMessage), 'keccak', 'hex') + } + + signHash (hash, privateKey) { + const signature = this.ecdsa (hash.slice (-64), privateKey.slice (-64), 'secp256k1', undefined) + return { + 'r': '0x' + signature['r'], + 's': '0x' + signature['s'], + 'v': 27 + signature['v'], + } + } + + signMessage (message, privateKey) { + return this.signHash (this.hashMessage (message), privateKey.slice (-64)) + } + + oath () { + if (typeof this.twofa !== 'undefined') { + return this.totp (this.twofa) + } else { + throw new ExchangeError (this.id + ' this.twofa has not been set') + } + } + + // the following functions take and return numbers represented as strings + // this is useful for arbitrary precision maths that floats lack + integerDivide (a, b) { + return new BN (a).div (new BN (b)) + } + + integerModulo (a, b) { + return new BN (a).mod (new BN (b)) + } + + integerPow (a, b) { + return new BN (a).pow (new BN (b)) + } +} + + +}).call(this,require("buffer").Buffer) +},{"../static_dependencies/BN/bn":122,"./errors":9,"./functions":10,"buffer":180}],8:[function(require,module,exports){ +'use strict'; + +const errorHierarchy = { + 'BaseError': { + 'ExchangeError': { + 'AuthenticationError': { + 'PermissionDenied': {}, + 'AccountSuspended': {}, + }, + 'ArgumentsRequired': {}, + 'BadRequest': { + 'BadSymbol': {}, + }, + 'BadResponse': { + 'NullResponse': {}, + }, + 'InsufficientFunds': {}, + 'InvalidAddress': { + 'AddressPending': {}, + }, + 'InvalidOrder': { + 'OrderNotFound': {}, + 'OrderNotCached': {}, + 'CancelPending': {}, + 'OrderImmediatelyFillable': {}, + 'OrderNotFillable': {}, + 'DuplicateOrderId': {}, + }, + 'NotSupported': {}, + }, + 'NetworkError': { + 'DDoSProtection': { + 'RateLimitExceeded': {}, + }, + 'ExchangeNotAvailable': { + 'OnMaintenance': {}, + }, + 'InvalidNonce': {}, + 'RequestTimeout': {}, + }, + }, +}; + +module.exports = errorHierarchy; + +},{}],9:[function(require,module,exports){ +'use strict'; + +const errorHierarchy = require ('./errorHierarchy.js') + +/* ------------------------------------------------------------------------ */ + +function subclass (BaseClass, classes, namespace = {}) { + + for (const [className, subclasses] of Object.entries (classes)) { + + const Class = Object.assign (namespace, { + + /* By creating a named property, we trick compiler to assign our class constructor function a name. + Otherwise, all our error constructors would be shown as [Function: Error] in the debugger! And + the super-useful `e.constructor.name` magic wouldn't work — we then would have no chance to + obtain a error type string from an error instance programmatically! */ + + [className]: class extends BaseClass { + + constructor (message) { + + super (message) + + /* A workaround to make `instanceof` work on custom Error classes in transpiled ES5. + See my blog post for the explanation of this hack: + + https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801 */ + + this.constructor = Class + this.__proto__ = Class.prototype + this.name = className + this.message = message + } + } + + })[className] + + subclass (Class, subclasses, namespace) + } + + return namespace +} + +/* ------------------------------------------------------------------------ */ + +module.exports = subclass ( + // Root class + Error, + // Derived class hierarchy + errorHierarchy +) + +},{"./errorHierarchy.js":8}],10:[function(require,module,exports){ +'use strict'; + +/* ------------------------------------------------------------------------ */ + +const { unCamelCase } = require ('./functions/string') + +const unCamelCasePropertyNames = x => { + for (const k in x) x[unCamelCase (k)] = x[k] // camel_case_method = camelCaseMethod + return x +} + +/* ------------------------------------------------------------------------ */ + +module.exports = unCamelCasePropertyNames (Object.assign ({} + + , require ('./functions/platform') + , require ('./functions/generic') + , require ('./functions/string') + , require ('./functions/type') + , require ('./functions/number') + , require ('./functions/encode') + , require ('./functions/crypto') + , require ('./functions/time') + , require ('./functions/throttle') + , require ('./functions/misc') +)) + +/* ------------------------------------------------------------------------ */ + +},{"./functions/crypto":11,"./functions/encode":12,"./functions/generic":13,"./functions/misc":14,"./functions/number":15,"./functions/platform":16,"./functions/string":17,"./functions/throttle":18,"./functions/time":19,"./functions/type":20}],11:[function(require,module,exports){ +'use strict' + +/* ------------------------------------------------------------------------ */ + +const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js') +const { capitalize } = require ('./string') +const { stringToBase64, utf16ToBase64, urlencodeBase64 } = require ('./encode') +const NodeRSA = require ('./../../static_dependencies/node-rsa/NodeRSA') +const { numberToLE } = require ('./encode') +const EC = require ('./../../static_dependencies/elliptic/lib/elliptic').ec +const { ArgumentsRequired } = require ('./../errors') +const BN = require ('../../static_dependencies/BN/bn.js') + +/* ------------------------------------------------------------------------ */ + +const hash = (request, hash = 'md5', digest = 'hex') => { + const options = {} + if (hash === 'keccak') { + hash = 'SHA3' + options['outputLength'] = 256 + } + const result = CryptoJS[hash.toUpperCase ()] (request, options) + return (digest === 'binary') ? result : result.toString (CryptoJS.enc[capitalize (digest)]) +} + +/* ............................................. */ + +const hmac = (request, secret, hash = 'sha256', digest = 'hex') => { + const result = CryptoJS['Hmac' + hash.toUpperCase ()] (request, secret) + if (digest) { + const encoding = (digest === 'binary') ? 'Latin1' : capitalize (digest) + return result.toString (CryptoJS.enc[capitalize (encoding)]) + } + return result +} + +/* ............................................. */ + +function rsa (request, secret, alg = 'RS256') { + const algos = { + 'RS256': 'pkcs1-sha256', + 'RS512': 'pkcs1-sha512', + } + if (!(alg in algos)) { + throw new ExchangeError (alg + ' is not a supported rsa signing algorithm.') + } + const algorithm = algos[alg] + let key = new NodeRSA (secret, { + 'environment': 'browser', + 'signingScheme': algorithm, + }) + return key.sign (request, 'base64', 'binary') +} + + +/** + * @return {string} + */ +function jwt (request, secret, alg = 'HS256') { + const algos = { + 'HS256': 'sha256', + 'HS384': 'sha384', + 'HS512': 'sha512', + }; + const encodedHeader = urlencodeBase64 (stringToBase64 (JSON.stringify ({ 'alg': alg, 'typ': 'JWT' }))) + const encodedData = urlencodeBase64 (stringToBase64 (JSON.stringify (request))) + const token = [ encodedHeader, encodedData ].join ('.') + const algoType = alg.slice (0, 2); + const algorithm = algos[alg] + let signature = undefined + if (algoType === 'HS') { + signature = urlencodeBase64 (hmac (token, secret, algorithm, 'base64')) + } else if (algoType === 'RS') { + signature = urlencodeBase64 (rsa (token, secret, alg)) + } + return [ token, signature ].join ('.') +} + +function ecdsa (request, secret, algorithm = 'p256', hashFunction = undefined, fixedLength = false) { + let digest = request + if (hashFunction !== undefined) { + digest = hash (request, hashFunction, 'hex') + } + const curve = new EC (algorithm) + let signature = curve.sign (digest, secret, 'hex', { 'canonical': true }) + let counter = new BN ('0') + const minimum_size = new BN ('1').shln (8 * 31).sub (new BN ('1')) + while (fixedLength && (signature.r.gt (curve.nh) || signature.r.lte (minimum_size) || signature.s.lte (minimum_size))) { + signature = curve.sign (digest, secret, 'hex', { 'canonical': true, 'extraEntropy': counter.toArray ('le', 32)}) + counter = counter.add (new BN ('1')) + } + return { + 'r': signature.r.toString (16).padStart (64, '0'), + 's': signature.s.toString (16).padStart (64, '0'), + 'v': signature.recoveryParam, + } +} + +/* ------------------------------------------------------------------------ */ + +const totp = (secret) => { + + const dec2hex = s => ((s < 15.5 ? '0' : '') + Math.round (s).toString (16)) + , hex2dec = s => parseInt (s, 16) + , leftpad = (s, p) => (p + s).slice (-p.length) // both s and p are short strings + + const base32tohex = (base32) => { + let base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' + let bits = '' + let hex = '' + for (let i = 0; i < base32.length; i++) { + let val = base32chars.indexOf (base32.charAt (i).toUpperCase ()) + bits += leftpad (val.toString (2), '00000') + } + for (let i = 0; i + 4 <= bits.length; i += 4) { + let chunk = bits.substr (i, 4) + hex = hex + parseInt (chunk, 2).toString (16) + } + return hex + } + + const getOTP = (secret) => { + secret = secret.replace (' ', '') // support 2fa-secrets with spaces like "4TDV WOGO" → "4TDVWOGO" + let epoch = Math.round (new Date ().getTime () / 1000.0) + let time = leftpad (dec2hex (Math.floor (epoch / 30)), '0000000000000000') + let hmacRes = hmac (CryptoJS.enc.Hex.parse (time), CryptoJS.enc.Hex.parse (base32tohex (secret)), 'sha1', 'hex') + let offset = hex2dec (hmacRes.substring (hmacRes.length - 1)) + let otp = (hex2dec (hmacRes.substr (offset * 2, 8)) & hex2dec ('7fffffff')) + '' + otp = (otp).substr (otp.length - 6, 6) + return otp + } + + return getOTP (secret) +} + +/* ------------------------------------------------------------------------ */ + +module.exports = { + hash, + hmac, + jwt, + totp, + rsa, + ecdsa, +} + +/* ------------------------------------------------------------------------ */ + +},{"../../static_dependencies/BN/bn.js":122,"../../static_dependencies/crypto-js/crypto-js":123,"./../../static_dependencies/elliptic/lib/elliptic":124,"./../../static_dependencies/node-rsa/NodeRSA":139,"./../errors":9,"./encode":12,"./string":17}],12:[function(require,module,exports){ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js') +const qs = require ('../../static_dependencies/qs/index') +const BN = require ('../../static_dependencies/BN/bn') + +/* ------------------------------------------------------------------------ */ + +module.exports = + + { json: (data, params = undefined) => JSON.stringify (data) + , unjson: JSON.parse + + , isJsonEncodedObject: object => ( + (typeof object === 'string') && + (object.length >= 2) && + ((object[0] === '{') || (object[0] === '[')) + ) + + , stringToBinary (str) { + const arr = new Uint8Array (str.length) + for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt (i); } + return CryptoJS.lib.WordArray.create (arr) + } + + , stringToBase64: string => CryptoJS.enc.Latin1.parse (string).toString (CryptoJS.enc.Base64) + , utf16ToBase64: string => CryptoJS.enc.Utf16 .parse (string).toString (CryptoJS.enc.Base64) + , base64ToBinary: string => CryptoJS.enc.Base64.parse (string) + , base64ToString: string => CryptoJS.enc.Base64.parse (string).toString (CryptoJS.enc.Utf8) + , binaryToBase64: binary => binary.toString (CryptoJS.enc.Base64) + , base16ToBinary: string => CryptoJS.enc.Hex.parse (string) + , binaryToBase16: binary => binary.toString (CryptoJS.enc.Hex) + , binaryConcat: (...args) => args.reduce ((a, b) => a.concat (b)) + , binaryConcatArray: (arr) => arr.reduce ((a, b) => a.concat (b)) + + , urlencode: object => qs.stringify (object) + , urlencodeWithArrayRepeat: object => qs.stringify (object, { arrayFormat: 'repeat' }) + , rawencode: object => qs.stringify (object, { encode: false }) + , encode: x => x + , decode: x => x + + // Url-safe-base64 without equals signs, with + replaced by - and slashes replaced by underscores + + , urlencodeBase64: base64string => base64string.replace (/[=]+$/, '') + .replace (/\+/g, '-') + .replace (/\//g, '_') + + , numberToLE: (n, padding) => { + const hexArray = new BN (n).toArray ('le', padding) + return byteArrayToWordArray (hexArray) + } + + , numberToBE: (n, padding) => { + const hexArray = new BN (n).toArray ('be', padding) + return byteArrayToWordArray (hexArray) + } +} + +function byteArrayToWordArray(ba) { + const wa = [] + for (let i = 0; i < ba.length; i++) { + wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i) + } + return CryptoJS.lib.WordArray.create (wa, ba.length) +} + +module.exports['byteArrayToWordArray'] = byteArrayToWordArray + +/* ------------------------------------------------------------------------ */ + +},{"../../static_dependencies/BN/bn":122,"../../static_dependencies/crypto-js/crypto-js":123,"../../static_dependencies/qs/index":155}],13:[function(require,module,exports){ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const { isObject, isNumber, isDictionary, isArray } = require ('./type') + +/* ------------------------------------------------------------------------ */ + +const keys = Object.keys + + , values = x => !isArray (x) // don't copy arrays if they're already arrays! + ? Object.values (x) + : x + + , index = x => new Set (values (x)) + + , extend = (...args) => Object.assign ({}, ...args) // NB: side-effect free + + , clone = x => isArray (x) + ? Array.from (x) // clones arrays + : extend (x) // clones objects + +/* ------------------------------------------------------------------------ */ + +module.exports = + + { keys + , values + , extend + , clone + , index + , ordered: x => x // a stub to keep assoc keys in order (in JS it does nothing, it's mostly for Python) + , unique: x => Array.from (index (x)) + + /* ............................................. */ + + , inArray (needle, haystack) { + + return haystack.includes (needle) + } + + , toArray (object) { + + return Object.values (object) + } + + , isEmpty (object) { + if (!object) + return true; + return (Array.isArray (object) ? object : Object.keys (object)).length < 1; + } + +/* ............................................. */ + + , keysort (x, out = {}) { + + for (const k of keys (x).sort ()) + out[k] = x[k] + + return out + } + +/* ............................................. */ + + /* + Accepts a map/array of objects and a key name to be used as an index: + array = [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value2', anotherKey: 'anotherValue2' }, + { someKey: 'value3', anotherKey: 'anotherValue3' }, + ] + key = 'someKey' + + Returns a map: + { + value1: { someKey: 'value1', anotherKey: 'anotherValue1' }, + value2: { someKey: 'value2', anotherKey: 'anotherValue2' }, + value3: { someKey: 'value3', anotherKey: 'anotherValue3' }, + } + */ + + , indexBy (x, k, out = {}) { + + for (const v of values (x)) + if (k in v) + out[v[k]] = v + + return out + } + +/* ............................................. */ + + /* + Accepts a map/array of objects and a key name to be used as a grouping parameter: + array = [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value1', anotherKey: 'anotherValue2' }, + { someKey: 'value3', anotherKey: 'anotherValue3' }, + ] + key = 'someKey' + + Returns a map: + { + value1: [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value1', anotherKey: 'anotherValue2' }, + ] + value3: [ + { someKey: 'value3', anotherKey: 'anotherValue3' } + ], + } + */ + + , groupBy (x, k, out = {}) { + + for (const v of values (x)) { + if (k in v) { + const p = v[k] + out[p] = out[p] || [] + out[p].push (v) + } + } + return out + } + +/* ............................................. */ + + /* + Accepts a map/array of objects, a key name and a key value to be used as a filter: + array = [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value2', anotherKey: 'anotherValue2' }, + { someKey: 'value3', anotherKey: 'anotherValue3' }, + ] + key = 'someKey' + value = 'value1' + + Returns an array: + [ + value1: { someKey: 'value1', anotherKey: 'anotherValue1' }, + ] + */ + + , filterBy (x, k, value = undefined, out = []) { + + for (const v of values (x)) + if (v[k] === value) + out.push (v) + + return out + } + +/* ............................................. */ + + , sortBy: (array, // NB: MUTATES ARRAY! + key, + descending = false, + direction = descending ? -1 : 1) => array.sort ((a, b) => + ((a[key] < b[key]) ? -direction : + ((a[key] > b[key]) ? direction : 0))) + +/* ............................................. */ + + , flatten: function flatten (x, out = []) { + + for (const v of x) { + if (isArray (v)) flatten (v, out) + else out.push (v) + } + + return out + } + +/* ............................................. */ + + , pluck: (x, k) => values (x) + .filter (v => k in v) + .map (v => v[k]) + +/* ............................................. */ + + , omit (x, ...args) { + + if (!Array.isArray (x)) { + + const out = clone (x) + + for (const k of args) { + + if (isArray (k)) { // omit (x, ['a', 'b']) + + for (const kk of k) { + delete out[kk] + } + } + + else delete out[k] // omit (x, 'a', 'b') + } + + return out + } + + return x + } + +/* ............................................. */ + + , sum (...xs) { + + const ns = xs.filter (isNumber) // leave only numbers + + return (ns.length > 0) + ? ns.reduce ((a, b) => a + b, 0) + : undefined + } + +/* ............................................. */ + + , deepExtend: function deepExtend (...xs) { + + let out = undefined + + for (const x of xs) { + + if (isDictionary (x)) { + + if (!isObject (out)) + out = {} + + for (const k in x) + out[k] = deepExtend (out[k], x[k]) + + } else out = x + } + + return out + } + +/* ------------------------------------------------------------------------ */ + +} + +},{"./type":20}],14:[function(require,module,exports){ +'use strict'; + +const { ROUND_UP, ROUND_DOWN } = require ('./number') +const { NotSupported } = require ('./../errors') + +//------------------------------------------------------------------------- +// converts timeframe to seconds +const parseTimeframe = (timeframe) => { + + const amount = timeframe.slice (0, -1) + const unit = timeframe.slice (-1) + let scale = undefined; + + if (unit === 'y') { + scale = 60 * 60 * 24 * 365 + } else if (unit === 'M') { + scale = 60 * 60 * 24 * 30 + } else if (unit === 'w') { + scale = 60 * 60 * 24 * 7 + } else if (unit === 'd') { + scale = 60 * 60 * 24 + } else if (unit === 'h') { + scale = 60 * 60 + } else if (unit === 'm') { + scale = 60 + } else if (unit === 's') { + scale = 1 + } else { + throw new NotSupported ('timeframe unit ' + unit + ' is not supported') + } + + return amount * scale +} + +const roundTimeframe = (timeframe, timestamp, direction = ROUND_DOWN) => { + const ms = parseTimeframe (timeframe) * 1000 + // Get offset based on timeframe in milliseconds + const offset = timestamp % ms + return timestamp - offset + ((direction === ROUND_UP) ? ms : 0); +} + +// given a sorted arrays of trades (recent last) and a timeframe builds an array of OHLCV candles +const buildOHLCVC = (trades, timeframe = '1m', since = -Infinity, limit = Infinity) => { + const ms = parseTimeframe (timeframe) * 1000; + const ohlcvs = []; + const [ timestamp, /* open */, high, low, close, volume, count ] = [ 0, 1, 2, 3, 4, 5, 6 ]; + const oldest = Math.min (trades.length - 1, limit); + + for (let i = 0; i <= oldest; i++) { + const trade = trades[i]; + if (trade.timestamp < since) + continue; + const openingTime = Math.floor (trade.timestamp / ms) * ms; // shift to the edge of m/h/d (but not M) + const candle = ohlcvs.length - 1; + + if (candle === -1 || openingTime >= ohlcvs[candle][timestamp] + ms) { + // moved to a new timeframe -> create a new candle from opening trade + ohlcvs.push ([ + openingTime, // timestamp + trade.price, // O + trade.price, // H + trade.price, // L + trade.price, // C + trade.amount, // V + 1, // count + ]); + } else { + // still processing the same timeframe -> update opening trade + ohlcvs[candle][high] = Math.max (ohlcvs[candle][high], trade.price); + ohlcvs[candle][low] = Math.min (ohlcvs[candle][low], trade.price); + ohlcvs[candle][close] = trade.price; + ohlcvs[candle][volume] += trade.amount; + ohlcvs[candle][count]++; + } // if + } // for + return ohlcvs; +} + +const extractParams = (string) => { + const re = /{([\w-]+)}/g + const matches = [] + let match = re.exec (string) + while (match) { + matches.push (match[1]) + match = re.exec (string) + } + return matches +} + +const implodeParams = (string, params) => { + if (!Array.isArray (params)) { + const keys = Object.keys (params) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (!Array.isArray (params[key])) { + string = string.replace ('{' + key + '}', params[key]) + } + } + } + return string +} + +/* ------------------------------------------------------------------------ */ + +module.exports = { + + aggregate (bidasks) { + + const result = {} + + for (let i = 0; i < bidasks.length; i++) { + const [ price, volume ] = bidasks[i]; + if (volume > 0) { + result[price] = (result[price] || 0) + volume + } + } + + return Object.keys (result).map (price => [parseFloat (price), parseFloat (result[price])]) + }, + + parseTimeframe, + roundTimeframe, + buildOHLCVC, + ROUND_UP, + ROUND_DOWN, + + implodeParams, + extractParams +} + +/* ------------------------------------------------------------------------ */ + +},{"./../errors":9,"./number":15}],15:[function(require,module,exports){ +'use strict' + +/* ------------------------------------------------------------------------ + + NB: initially, I used objects for options passing: + + decimalToPrecision ('123.456', { digits: 2, round: true, afterPoint: true }) + + ...but it turns out it's hard to port that across different languages and it is also + probably has a performance penalty -- while it's a performance critical code! So + I switched to using named constants instead, as it is actually more readable and + succinct, and surely doesn't come with any inherent performance downside: + + decimalToPrecision ('123.456', ROUND, 2, DECIMAL_PLACES) */ + +const ROUND = 0 // rounding mode + , TRUNCATE = 1 + , ROUND_UP = 2 + , ROUND_DOWN = 3 + +const DECIMAL_PLACES = 0 // digits counting mode + , SIGNIFICANT_DIGITS = 1 + , TICK_SIZE = 2 + +const NO_PADDING = 0 // zero-padding mode + , PAD_WITH_ZERO = 1 + +const precisionConstants = { + ROUND, + TRUNCATE, + ROUND_UP, + ROUND_DOWN, + DECIMAL_PLACES, + SIGNIFICANT_DIGITS, + TICK_SIZE, + NO_PADDING, + PAD_WITH_ZERO, +} + +/* ------------------------------------------------------------------------ */ + +// See https://stackoverflow.com/questions/1685680/how-to-avoid-scientific-notation-for-large-numbers-in-javascript for discussion + +function numberToString (x) { // avoids scientific notation for too large and too small numbers + + if (typeof x === 'string') return x + + if (Math.abs (x) < 1.0) { + const s = x.toString () + const e = parseInt (s.split ('e-')[1]) + const neg = (s[0] === '-') + if (e) { + x *= Math.pow (10, e - 1) + x = (neg ? '-' : '') + '0.' + (new Array (e)).join ('0') + x.toString ().substring (neg ? 3 : 2) + } + } else { + let e = parseInt (x.toString ().split ('+')[1]) + if (e > 20) { + e -= 20 + x /= Math.pow (10, e) + x += (new Array (e + 1)).join ('0') + } + } + return x.toString () +} + +//----------------------------------------------------------------------------- +// expects non-scientific notation + +const truncate_regExpCache = [] + , truncate_to_string = (num, precision = 0) => { + num = numberToString (num) + if (precision > 0) { + const re = truncate_regExpCache[precision] || (truncate_regExpCache[precision] = new RegExp ("([-]*\\d+\\.\\d{" + precision + "})(\\d)")) + const [ , result] = num.toString ().match (re) || [null, num] + return result.toString () + } + return parseInt (num).toString () + } + , truncate = (num, precision = 0) => parseFloat (truncate_to_string (num, precision)) + +function precisionFromString (string) { + const split = string.replace (/0+$/g, '').split ('.') + return (split.length > 1) ? (split[1].length) : 0 +} + +/* ------------------------------------------------------------------------ */ + +const decimalToPrecision = (x, roundingMode + , numPrecisionDigits + , countingMode = DECIMAL_PLACES + , paddingMode = NO_PADDING) => { + + if (numPrecisionDigits < 0) { + if (countingMode === TICK_SIZE) { + throw new Error (`TICK_SIZE cant be used with negative numPrecisionDigits`) + } + const toNearest = Math.pow (10, -numPrecisionDigits) + if (roundingMode === ROUND) { + return (toNearest * decimalToPrecision (x / toNearest, roundingMode, 0, countingMode, paddingMode)).toString () + } + if (roundingMode === TRUNCATE) { + return (x - (x % toNearest)).toString () + } + } + +/* handle tick size */ + if (countingMode === TICK_SIZE) { + const missing = x % numPrecisionDigits + const reminder = x / numPrecisionDigits + if (reminder !== Math.floor (reminder)) { + if (roundingMode === ROUND) { + if (x > 0) { + if (missing >= numPrecisionDigits / 2) { + x = x - missing + numPrecisionDigits + } else { + x = x - missing + } + } else { + if (missing >= numPrecisionDigits / 2) { + x = Number (x) - missing + } else { + x = Number (x) - missing - numPrecisionDigits + } + } + } else if (roundingMode === TRUNCATE) { + x = x - missing + } + } + const precisionDigitsString = decimalToPrecision (numPrecisionDigits, ROUND, 100, DECIMAL_PLACES, NO_PADDING) + const newNumPrecisionDigits = precisionFromString (precisionDigitsString) + return decimalToPrecision (x, ROUND, newNumPrecisionDigits, DECIMAL_PLACES, paddingMode); + } + +/* Convert to a string (if needed), skip leading minus sign (if any) */ + + const str = numberToString (x) + , isNegative = str[0] === '-' + , strStart = isNegative ? 1 : 0 + , strEnd = str.length + +/* Find the dot position in the source buffer */ + + for (var strDot = 0; strDot < strEnd; strDot++) + if (str[strDot] === '.') + break + + const hasDot = strDot < str.length + +/* Char code constants */ + + const MINUS = 45 + , DOT = 46 + , ZERO = 48 + , ONE = (ZERO + 1) + , FIVE = (ZERO + 5) + , NINE = (ZERO + 9) + +/* For -123.4567 the `chars` array will hold 01234567 (leading zero is reserved for rounding cases when 099 → 100) */ + + const chars = new Uint8Array ((strEnd - strStart) + (hasDot ? 0 : 1)) + chars[0] = ZERO + +/* Validate & copy digits, determine certain locations in the resulting buffer */ + + let afterDot = chars.length + , digitsStart = -1 // significant digits + , digitsEnd = -1 + + for (var i = 1, j = strStart; j < strEnd; j++, i++) { + + const c = str.charCodeAt (j) + + if (c === DOT) { + afterDot = i-- + + } else if ((c < ZERO) || (c > NINE)) { + throw new Error (`${str}: invalid number (contains an illegal character '${str[i - 1]}')`) + + } else { + chars[i] = c + if ((c !== ZERO) && (digitsStart < 0)) digitsStart = i + } + } + + if (digitsStart < 0) digitsStart = 1 + +/* Determine the range to cut */ + + let precisionStart = (countingMode === DECIMAL_PLACES) ? afterDot // 0.(0)001234567 + : digitsStart // 0.00(1)234567 + , precisionEnd = precisionStart + + numPrecisionDigits + +/* Reset the last significant digit index, as it will change during the rounding/truncation. */ + + digitsEnd = -1 + +/* Perform rounding/truncation per digit, from digitsEnd to digitsStart, by using the following + algorithm (rounding 999 → 1000, as an example): + + step = i=3 i=2 i=1 i=0 + + chars = 0999 0999 0900 1000 + memo = ---0 --1- -1-- 0--- */ + + let allZeros = true; + let signNeeded = isNegative; + + for (let i = chars.length - 1, memo = 0; i >= 0; i--) { + + let c = chars[i] + + if (i !== 0) { + c += memo + + if (i >= (precisionStart + numPrecisionDigits)) { + + const ceil = (roundingMode === ROUND) && + (c >= FIVE) && + !((c === FIVE) && memo) // prevents rounding of 1.45 to 2 + + c = ceil ? (NINE + 1) : ZERO + } + if (c > NINE) { c = ZERO; memo = 1; } + else memo = 0 + + } else if (memo) c = ONE // leading extra digit (0900 → 1000) + + chars[i] = c + + if (c !== ZERO) { + allZeros = false + digitsStart = i + digitsEnd = (digitsEnd < 0) ? (i + 1) : digitsEnd + } + } + +/* Update the precision range, as `digitsStart` may have changed... & the need for a negative sign if it is only 0 */ + + if (countingMode === SIGNIFICANT_DIGITS) { + precisionStart = digitsStart + precisionEnd = precisionStart + numPrecisionDigits + } + + if (allZeros) { + signNeeded = false + } + +/* Determine the input character range */ + + const readStart = ((digitsStart >= afterDot) || allZeros) ? (afterDot - 1) : digitsStart // 0.000(1)234 ----> (0).0001234 + , readEnd = (digitsEnd < afterDot) ? (afterDot ) : digitsEnd // 12(3)000 ----> 123000( ) + +/* Compute various sub-ranges */ + + const nSign = (signNeeded ? 1 : 0) // (-)123.456 + , nBeforeDot = (nSign + (afterDot - readStart)) // (-123).456 + , nAfterDot = Math.max (readEnd - afterDot, 0) // -123.(456) + , actualLength = (readEnd - readStart) // -(123.456) + , desiredLength = (paddingMode === NO_PADDING) + ? (actualLength) // -(123.456) + : (precisionEnd - readStart) // -(123.456 ) + + , pad = Math.max (desiredLength - actualLength, 0) // -123.456( ) + , padStart = (nBeforeDot + 1 + nAfterDot) // -123.456( ) + , padEnd = (padStart + pad) // -123.456 ( ) + , isInteger = (nAfterDot + pad) === 0 // -123 + +/* Fill the output buffer with characters */ + + const out = new Uint8Array (nBeforeDot + (isInteger ? 0 : 1) + nAfterDot + pad) + // ------------------------------------------------------------------------------------------ // --------------------- + if (signNeeded) out[0] = MINUS // - minus sign + for (i = nSign, j = readStart; i < nBeforeDot; i++, j++) out[i] = chars[j] // 123 before dot + if (!isInteger) out[nBeforeDot] = DOT // . dot + for (i = nBeforeDot + 1, j = afterDot; i < padStart; i++, j++) out[i] = chars[j] // 456 after dot + for (i = padStart; i < padEnd; i++) out[i] = ZERO // 000 padding + +/* Build a string from the output buffer */ + + return String.fromCharCode (...out) +} + +/* ------------------------------------------------------------------------ */ + +module.exports = { + + numberToString, + precisionFromString, + decimalToPrecision, + truncate_to_string, + truncate, + precisionConstants, + ROUND, + TRUNCATE, + ROUND_UP, + ROUND_DOWN, + DECIMAL_PLACES, + SIGNIFICANT_DIGITS, + TICK_SIZE, + NO_PADDING, + PAD_WITH_ZERO, +} + +/* ------------------------------------------------------------------------ */ + +},{}],16:[function(require,module,exports){ +(function (process){ +"use strict"; + +// ---------------------------------------------------------------------------- +// There's been a lot of messing with this code... +// The problem is to satisfy the following requirements: +// - properly detect isNode == true on server side and isNode == false in the browser (on client side) +// - make sure create-react-app, react-starter-kit and other react frameworks work +// - make sure it does not break the browserified version (when linked into a html from a cdn) +// - make sure it does not break the webpacking and babel-transpiled scripts +// - make sure it works in Electron +// - make sure it works with Angular.js +// - make sure it does not break other possible usage scenarios + +const isBrowser = typeof window !== 'undefined' + +const isElectron = typeof process !== 'undefined' && + typeof process.versions !== 'undefined' && + typeof process.versions.electron !== 'undefined' + +const isWebWorker = typeof WorkerGlobalScope !== 'undefined' && (self instanceof WorkerGlobalScope) + +const isWindows = typeof process !== 'undefined' && process.platform === "win32" + +const isNode = !(isBrowser || isWebWorker) + +const defaultFetch = typeof (fetch) === "undefined" ? require ('../../static_dependencies/fetch-ponyfill/fetch-node') ().fetch : fetch + +// ---------------------------------------------------------------------------- + +module.exports = { + + isBrowser, + isElectron, + isWebWorker, + isNode, + isWindows, + defaultFetch, +} +}).call(this,require('_process')) +},{"../../static_dependencies/fetch-ponyfill/fetch-node":137,"_process":202}],17:[function(require,module,exports){ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const uuid = a => a ? (a ^ Math.random () * 16 >> a / 4).toString (16) + : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid) + +module.exports = + + { uuid + + // hasFetchOHLCV → has_fetch_ohlcv; parseHTTPResponse → parse_http_response + , unCamelCase: s => s.match (/^[A-Z0-9_]+$/) ? s : (s.replace (/[a-z0-9][A-Z]/g, x => x[0] + '_' + x[1]).replace(/[A-Z0-9][A-Z0-9][a-z]/g, x => x[0] + '_' + x[1] + x[2]).toLowerCase ()) + + , capitalize: s => s.length + ? (s.charAt (0).toUpperCase () + s.slice (1)) + : s + , strip: s => s.replace(/^\s+|\s+$/g, '') + } + +/* ------------------------------------------------------------------------ */ + +},{}],18:[function(require,module,exports){ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const { sleep + , now } = require ('./time') + +/* ------------------------------------------------------------------------ */ + +module.exports = { + + throttle: function throttle (cfg) { + + let lastTimestamp = now () + , numTokens = (cfg.numTokens !== undefined) ? cfg.numTokens : cfg.capacity + , running = false + , counter = 0 + + const queue = [] + + return Object.assign ((cost) => { + + if (queue.length > cfg.maxCapacity) + throw new Error ('Backlog is over max capacity of ' + cfg.maxCapacity) + + return new Promise (async (resolve, reject) => { + + try { + queue.push ({ cost, resolve, reject }) + + if (!running) { + running = true + while (queue.length > 0) { + const hasEnoughTokens = cfg.capacity ? (numTokens > 0) : (numTokens >= 0) + if (hasEnoughTokens) { + if (queue.length > 0) { + let { cost, resolve, reject } = queue[0] + cost = (cost || cfg.defaultCost) + if (numTokens >= Math.min (cost, cfg.capacity)) { + numTokens -= cost + queue.shift () + resolve () + } + } + } + const t = now () + , elapsed = t - lastTimestamp + lastTimestamp = t + numTokens = Math.min (cfg.capacity, numTokens + elapsed * cfg.refillRate) + await sleep (cfg.delay) + } + running = false + } + + } catch (e) { + reject (e) + } + }) + + }, cfg, { configure: newCfg => throttle (Object.assign ({}, cfg, newCfg)) }) + } +} + +/* ------------------------------------------------------------------------ */ + +},{"./time":19}],19:[function(require,module,exports){ +'use strict'; + +/* ------------------------------------------------------------------------ */ + +const now = Date.now // TODO: figure out how to utilize performance.now () properly – it's not as easy as it does not return a unix timestamp... +const microseconds = () => now () * 1000 // TODO: utilize performance.now for that purpose +const milliseconds = now +const seconds = () => Math.floor (now () / 1000) + +/* ------------------------------------------------------------------------ */ + +const setTimeout_original = setTimeout +const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = now () + ms) => { + +/* The built-in setTimeout function can fire its callback earlier than specified, so we + need to ensure that it does not happen: sleep recursively until `targetTime` is reached... */ + + let clearInnerTimeout = () => {} + let active = true + + const id = setTimeout (() => { + active = true + const rest = targetTime - now () + if (rest > 0) { + clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime) // try sleep more + } else { + done () + } + }, ms) + + return function clear () { + if (active) { + active = false // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion) + clearTimeout (id) + } + clearInnerTimeout () + } +} + +/* ------------------------------------------------------------------------ */ + +class TimedOut extends Error { + + constructor () { + const message = 'timed out' + super (message) + this.constructor = TimedOut + this.__proto__ = TimedOut.prototype + this.message = message + } +} + +/* ------------------------------------------------------------------------ */ + +const iso8601 = (timestamp) => { + let _timestampNumber = undefined; + if (typeof timestamp === 'number') { + _timestampNumber = Math.floor (timestamp); + } else { + _timestampNumber = parseInt (timestamp, 10); + } + + // undefined, null and lots of nasty non-numeric values yield NaN + if (Number.isNaN (_timestampNumber) || _timestampNumber < 0) { + return undefined; + } + + // last line of defence + try { + return new Date (_timestampNumber).toISOString (); + } catch (e) { + return undefined; + } +} + +const parse8601 = (x) => { + if (typeof x !== 'string' || !x) { + return undefined; + } + + if (x.match (/^[0-9]+$/)) { + // a valid number in a string, not a date. + return undefined; + } + + if (x.indexOf ('-') < 0 || x.indexOf (':') < 0) { // no date can be without a dash and a colon + return undefined; + } + + // last line of defence + try { + const candidate = Date.parse (((x.indexOf ('+') >= 0) || (x.slice (-1) === 'Z')) ? x : (x + 'Z').replace (/\s(\d\d):/, 'T$1:')); + if (Number.isNaN (candidate)) { + return undefined; + } + return candidate; + } catch (e) { + return undefined; + } +} + +const parseDate = (x) => { + if (typeof x !== 'string' || !x) { + return undefined; + } + + if (x.indexOf ('GMT') >= 0) { + try { + return Date.parse (x); + } catch (e) { + return undefined; + } + } + + return parse8601 (x); +} + +const mdy = (timestamp, infix = '-') => { + infix = infix || '' + const date = new Date (timestamp) + const Y = date.getUTCFullYear ().toString () + let m = date.getUTCMonth () + 1 + let d = date.getUTCDate () + m = m < 10 ? ('0' + m) : m.toString () + d = d < 10 ? ('0' + d) : d.toString () + return m + infix + d + infix + Y +} + +const ymd = (timestamp, infix = '-') => { + infix = infix || '' + const date = new Date (timestamp) + const Y = date.getUTCFullYear ().toString () + let m = date.getUTCMonth () + 1 + let d = date.getUTCDate () + m = m < 10 ? ('0' + m) : m.toString () + d = d < 10 ? ('0' + d) : d.toString () + return Y + infix + m + infix + d +} + +const ymdhms = (timestamp, infix = ' ') => { + const date = new Date (timestamp) + const Y = date.getUTCFullYear () + let m = date.getUTCMonth () + 1 + let d = date.getUTCDate () + let H = date.getUTCHours () + let M = date.getUTCMinutes () + let S = date.getUTCSeconds () + m = m < 10 ? ('0' + m) : m + d = d < 10 ? ('0' + d) : d + H = H < 10 ? ('0' + H) : H + M = M < 10 ? ('0' + M) : M + S = S < 10 ? ('0' + S) : S + return Y + '-' + m + '-' + d + infix + H + ':' + M + ':' + S +} + +module.exports = + + { + now + , microseconds + , milliseconds + , seconds + , iso8601 + , parse8601 + , parseDate + , mdy + , ymd + , ymdhms + , setTimeout_safe + , sleep: ms => new Promise (resolve => setTimeout_safe (resolve, ms)) + , TimedOut + , timeout: async (ms, promise) => { + + let clear = () => {} + const expires = new Promise (resolve => (clear = setTimeout_safe (resolve, ms))) + + try { + return await Promise.race ([promise, expires.then (() => { throw new TimedOut () })]) + } finally { + clear () // fixes https://github.com/ccxt/ccxt/issues/749 + } + } +} + +/* ------------------------------------------------------------------------ */ + +},{}],20:[function(require,module,exports){ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const isNumber = Number.isFinite + , isArray = Array.isArray + , hasProps = o => ((o !== undefined) && (o !== null)) + , isString = s => (typeof s === 'string') + , isObject = o => ((o !== null) && (typeof o === 'object')) + , isDictionary = o => (isObject (o) && !isArray (o)) + , isStringCoercible = x => ((hasProps (x) && x.toString) || isNumber (x)) + +/* ............................................. */ + +const prop = (o, k) => (isObject (o) ? o[k] : undefined) + , prop2 = (o, k1, k2) => (!isObject (o) ? undefined : (((k1 in o) && (o[k1] !== null)) ? o[k1] : o[k2])) + +/* ............................................. */ + +const asFloat = x => ((isNumber (x) || isString (x)) ? parseFloat (x) : NaN) + , asInteger = x => ((isNumber (x) || isString (x)) ? parseInt (x, 10) : NaN) + +/* ............................................. */ + +module.exports = { + + isNumber + , isArray + , isObject + , isString + , isStringCoercible + , isDictionary + + , hasProps + , prop + + , asFloat + , asInteger + + , safeFloat: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? n : $default) + , safeInteger: (o, k, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? n : $default) + , safeIntegerProduct: (o, k, $factor, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? parseInt (n * $factor) : $default) + , safeTimestamp: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? parseInt (n * 1000) : $default) + , safeValue: (o, k, $default, x = prop (o, k)) => (hasProps (x) ? x : $default) + , safeString: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x) : $default) + , safeStringLower: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default) + , safeStringUpper: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default) + + // not using safeFloats with an array argument as we're trying to save some cycles here + // we're not using safeFloat3 either because those cases are too rare to deserve their own optimization + + , safeFloat2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default) + , safeInteger2: (o, k1, k2, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default) + , safeIntegerProduct2: (o, k1, k2, $factor, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * $factor) : $default) + , safeTimestamp2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * 1000) : $default) + , safeValue2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (hasProps (x) ? x : $default) + , safeString2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x) : $default) + , safeStringLower2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default) + , safeStringUpper2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default) + +} + +/* ------------------------------------------------------------------------ */ + +},{}],21:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bcex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bcex', + 'name': 'BCEX', + 'countries': [ 'CN', 'CA' ], + 'version': '1', + 'has': { + 'fetchBalance': true, + 'fetchMarkets': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchTrades': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchClosedOrders': 'emulated', + 'fetchOpenOrders': true, + 'fetchTradingLimits': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/43362240-21c26622-92ee-11e8-9464-5801ec526d77.jpg', + 'api': 'https://www.bcex.top', + 'www': 'https://www.bcex.top', + 'doc': 'https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface', + 'fees': 'https://bcex.udesk.cn/hc/articles/57085', + 'referral': 'https://www.bcex.top/register?invite_code=758978&lang=en', + }, + 'status': { + 'status': 'error', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'api': { + 'public': { + 'get': [ + 'Api_Market/getPriceList', // tickers + 'Api_Order/ticker', // last ohlcv candle (ticker) + 'Api_Order/depth', // orderbook + 'Api_Market/getCoinTrade', // ticker + 'Api_Order/marketOrder', // trades... + ], + 'post': [ + 'Api_Market/getPriceList', // tickers + 'Api_Order/ticker', // last ohlcv candle (ticker) + 'Api_Order/depth', // orderbook + 'Api_Market/getCoinTrade', // ticker + 'Api_Order/marketOrder', // trades... + ], + }, + 'private': { + 'post': [ + 'Api_Order/cancel', + 'Api_Order/coinTrust', // limit order + 'Api_Order/orderList', // open / all orders (my trades?) + 'Api_Order/orderInfo', + 'Api_Order/tradeList', // open / all orders + 'Api_Order/trustList', // ? + 'Api_User/userBalance', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'buy': 0.0, + 'sell': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'ckusd': 0.0, + 'other': 0.05 / 100, + }, + 'deposit': {}, + }, + }, + 'exceptions': { + '该å¸�ä¸�存在,é�žæ³•æ“�作': ExchangeError, // { code: 1, msg: "该å¸�ä¸�存在,é�žæ³•æ“�作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well) + '公钥ä¸�å�ˆæ³•': AuthenticationError, // { code: 1, msg: '公钥ä¸�å�ˆæ³•' } - wrong public key + '您的å�¯ç”¨ä½™é¢�ä¸�è¶³': InsufficientFunds, // { code: 1, msg: '您的å�¯ç”¨ä½™é¢�ä¸�è¶³' } - your available balance is insufficient + '您的btcä¸�è¶³': InsufficientFunds, // { code: 1, msg: '您的btcä¸�è¶³' } - your btc is insufficient + 'å�‚æ•°é�žæ³•': InvalidOrder, // {'code': 1, 'msg': 'å�‚æ•°é�žæ³•'} - 'Parameter illegal' + '订å�•ä¿¡æ�¯ä¸�存在': OrderNotFound, // {'code': 1, 'msg': '订å�•ä¿¡æ�¯ä¸�存在'} - 'Order information does not exist' + }, + 'options': { + 'limits': { + // hardcoding is deprecated, using these predefined values is not recommended, use loadTradingLimits instead + 'AFC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}}, + 'AFC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}}, + 'AFT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}}, + 'AICC/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}}, + 'AIDOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}}, + 'AISI/ETH': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 500 }}}, + 'AIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}}, + 'ANS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 500 }}}, + 'ANS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}}, + 'ARC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 60, 'max': 600000 }}}, + 'AXF/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 1000000 }}}, + 'BASH/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 3000000 }}}, + 'BATT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 60, 'max': 1500000 }}}, + 'BCD/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.3, 'max': 7000 }}}, + 'BHPC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 70000 }}}, + 'BHPC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 60000 }}}, + 'BOPO/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 2000000 }}}, + 'BOPO/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 10000000 }}}, + 'BTC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 10 }}}, + 'BTC/CNET': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0005, 'max': 5 }}}, + 'BTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0002, 'max': 4 }}}, + 'BTE/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}}, + 'BU/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}}, + 'CIC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3000, 'max': 30000000 }}}, + 'CIT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}}, + 'CIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}}, + 'CMT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 2500000 }}}, + 'CNET/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 12, 'max': 120000 }}}, + 'CNMC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 50000 }}}, + 'CTC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 550000 }}}, + 'CZR/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 12, 'max': 500000 }}}, + 'DCON/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 8, 'max': 300000 }}}, + 'DCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 40000 }}}, + 'DCT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 2, 'max': 2000 }}}, + 'DOGE/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 3000, 'max': 14000000 }}}, + 'DOGE/CKUSD': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 500, 'max': 2000000 }}}, + 'DRCT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 16, 'max': 190000 }}}, + 'ELA/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 500 }}}, + 'ELF/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 100000 }}}, + 'ELF/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.01, 'max': 100000 }}}, + 'EOS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}}, + 'EOS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 30000 }}}, + 'EOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.18, 'max': 1800 }}}, + 'ETC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}}, + 'ETC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}}, + 'ETF/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 150000 }}}, + 'ETH/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.015, 'max': 100 }}}, + 'ETH/CKUSD': { 'precision': { 'amount': 4, 'price': 4 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}}, + 'ETH/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}}, + 'FCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}}, + 'FCT/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}}, + 'GAME/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 1, 'max': 10000 }}}, + 'GOOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}}, + 'GP/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 600, 'max': 6000000 }}}, + 'HSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 1000, 'max': 20000000 }}}, + 'IFISH/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 300, 'max': 8000000 }}}, + 'IIC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 4000000 }}}, + 'IMOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}}, + 'JC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}}, + 'LBTC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 3000 }}}, + 'LEC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 500, 'max': 5000000 }}}, + 'LKY/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 70000 }}}, + 'LKY/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, + 'LMC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}}, + 'LSK/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.3, 'max': 3000 }}}, + 'LTC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}}, + 'LTC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}}, + 'LTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 450 }}}, + 'MC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 10000, 'max': 100000000 }}}, + 'MCC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 30, 'max': 350000 }}}, + 'MOC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 25, 'max': 600000 }}}, + 'MRYC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}}, + 'MT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 6000000 }}}, + 'MXI/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 5000, 'max': 60000000 }}}, + 'NAI/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, + 'NAS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 15000 }}}, + 'NAS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}}, + 'NEWOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 65, 'max': 700000 }}}, + 'NKN/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 3, 'max': 350000 }}}, + 'NTK/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 30000 }}}, + 'ONT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2000 }}}, + 'ONT/ETH': { 'precision': { 'amount': 3, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 1000 }}}, + 'PNT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 80, 'max': 800000 }}}, + 'PST/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}}, + 'PTT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 450, 'max': 10000000 }}}, + 'QTUM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.4, 'max': 2800 }}}, + 'QTUM/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}}, + 'RATING/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}}, + 'RHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}}, + 'SDA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 500000 }}}, + 'SDD/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, + 'SHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 2500000 }}}, + 'SHE/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 5000000 }}}, + 'SMC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}}, + 'SOP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 1000000 }}}, + 'TAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 35, 'max': 800000 }}}, + 'TIP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 200000 }}}, + 'TKT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 40, 'max': 400000 }}}, + 'TLC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}}, + 'TNC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 110000 }}}, + 'TUB/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 8000000 }}}, + 'UC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 3000000 }}}, + 'UDB/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 2000, 'max': 40000000 }}}, + 'UIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}}, + 'VAAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 250000 }}}, + 'VPN/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}}, + 'VSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 30, 'max': 650000 }}}, + 'WAVES/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.15, 'max': 1500 }}}, + 'WDNA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 250000 }}}, + 'WIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3, 'max': 30000 }}}, + 'XAS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 25000 }}}, + 'XLM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 300000 }}}, + 'XLM/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1, 'max': 300000 }}}, + 'XLM/USDT': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}}, + 'XRP/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 24, 'max': 100000 }}}, + 'XRP/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}}, + 'YBCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 200000 }}}, + 'YBCT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 200000 }}}, + 'YBY/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 25000, 'max': 250000000 }}}, + 'ZEC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}}, + 'ZEC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}}, + }, + }, + }); + } + + async fetchTradingLimits (symbols = undefined, params = {}) { + // this method should not be called directly, use loadTradingLimits () instead + // by default it will try load withdrawal fees of all currencies (with separate requests, sequentially) + // however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const result = {}; + for (let i = 0; i < symbols.length; i++) { + const symbol = symbols[i]; + result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params); + } + return result; + } + + async fetchTradingLimitsById (id, params = {}) { + const request = { + 'symbol': id, + }; + const response = await this.publicPostApiOrderTicker (this.extend (request, params)); + // + // { code: 0, + // msg: "获å�–牌价信æ�¯æˆ�功", + // data: { high: 0.03721392, + // low: 0.03335362, + // buy: "0.03525757", + // sell: "0.03531160", + // last: 0.0352634, + // vol: "184742.4176", + // min_trade: "0.01500000", + // max_trade: "100.00000000", + // number_float: "4", + // price_float: "8" } } } + // + return this.parseTradingLimits (this.safeValue (response, 'data', {})); + } + + parseTradingLimits (limits, symbol = undefined, params = {}) { + // + // { high: 0.03721392, + // low: 0.03335362, + // buy: "0.03525757", + // sell: "0.03531160", + // last: 0.0352634, + // vol: "184742.4176", + // min_trade: "0.01500000", + // max_trade: "100.00000000", + // number_float: "4", + // price_float: "8" } + // + return { + 'info': limits, + 'precision': { + 'amount': this.safeInteger (limits, 'number_float'), + 'price': this.safeInteger (limits, 'price_float'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (limits, 'min_trade'), + 'max': this.safeFloat (limits, 'max_trade'), + }, + }, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetApiMarketGetPriceList (params); + const result = []; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const currentMarketId = keys[i]; + const currentMarkets = response[currentMarketId]; + for (let j = 0; j < currentMarkets.length; j++) { + const market = currentMarkets[j]; + const baseId = this.safeString (market, 'coin_from'); + const quoteId = this.safeString (market, 'coin_to'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const id = baseId + '2' + quoteId; + const symbol = base + '/' + quote; + const active = true; + const defaults = this.safeValue (this.options['limits'], symbol, {}); + result.push (this.extend ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + // overrided by defaults from this.options['limits'] + 'precision': { + 'amount': undefined, + 'price': undefined, + }, + // overrided by defaults from this.options['limits'] + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + 'info': market, + }, defaults)); + } + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp2 (trade, 'date', 'created'); + const id = this.safeString (trade, 'tid'); + const orderId = this.safeString (trade, 'order_id'); + const amount = this.safeFloat2 (trade, 'number', 'amount'); + const price = this.safeFloat (trade, 'price'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let side = this.safeString (trade, 'side'); + if (side === 'sale') { + side = 'sell'; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'order': orderId, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const market = this.market (symbol); + const response = await this.publicPostApiOrderMarketOrder (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostApiUserUserBalance (params); + const data = this.safeValue (response, 'data'); + let keys = Object.keys (data); + const result = { }; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const amount = this.safeFloat (data, key); + const parts = key.split ('_'); + const currencyId = parts[0]; + const lockOrOver = parts[1]; + const code = this.safeCurrencyCode (currencyId); + if (!(code in result)) { + result[code] = this.account (); + } + if (lockOrOver === 'lock') { + result[code]['used'] = parseFloat (amount); + } else { + result[code]['free'] = parseFloat (amount); + } + } + keys = Object.keys (result); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const total = this.sum (result[key]['used'], result[key]['free']); + result[key]['total'] = total; + } + result['info'] = data; + return this.parseBalance (result); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.markets[symbol]; + const request = { + 'part': market['quoteId'], + 'coin': market['baseId'], + }; + const response = await this.publicPostApiMarketGetCoinTrade (this.extend (request, params)); + const timestamp = this.milliseconds (); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (response, 'max'), + 'low': this.safeFloat (response, 'min'), + 'bid': this.safeFloat (response, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (response, 'sale'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': this.safeFloat (response, 'price'), + 'last': this.safeFloat (response, 'price'), + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (response, 'change_24h'), + 'average': undefined, + 'baseVolume': this.safeFloat (response, 'volume_24h'), + 'quoteVolume': undefined, + 'info': response, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'symbol': marketId, + }; + const response = await this.publicPostApiOrderDepth (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + const timestamp = this.safeTimestamp (data, 'date'); + return this.parseOrderBook (data, timestamp); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.privatePostApiOrderOrderList (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', // partially filled + '2': 'closed', + '3': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a `symbol` argument'); + } + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'trust_id': id, + }; + const response = await this.privatePostApiOrderOrderInfo (this.extend (request, params)); + const order = this.safeValue (response, 'data'); + const timestamp = this.safeTimestamp (order, 'created'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let side = this.safeString (order, 'flag'); + if (side === 'sale') { + side = 'sell'; + } + // Can't use parseOrder because the data format is different btw endpoint for fetchOrder and fetchOrders + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'cost': undefined, + 'average': this.safeFloat (order, 'avg_price'), + 'amount': this.safeFloat (order, 'number'), + 'filled': this.safeFloat (order, 'numberdeal'), + 'remaining': this.safeFloat (order, 'numberover'), + 'status': status, + 'fee': undefined, + }; + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const timestamp = this.safeTimestamp (order, 'datetime'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = undefined; + let side = this.safeString (order, 'type'); + if (side === 'sale') { + side = 'sell'; + } + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avg_price'); + const amount = this.safeFloat (order, 'amount'); + const remaining = this.safeFloat (order, 'amount_outstanding'); + const filled = amount - remaining; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const cost = filled * price; + const fee = undefined; + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + return result; + } + + async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'type': type, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privatePostApiOrderTradeList (this.extend (request, params)); + if ('data' in response) { + return this.parseOrders (response['data'], market, since, limit); + } + return []; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByType ('open', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByType ('all', symbol, since, limit, params); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'type': side, + 'price': this.priceToPrecision (symbol, price), + 'number': this.amountToPrecision (symbol, amount), + }; + const response = await this.privatePostApiOrderCoinTrust (this.extend (request, params)); + const data = this.safeValue (response, 'data', {}); + const id = this.safeString (data, 'order_id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument'); + } + await this.loadMarkets (); + const request = {}; + if (symbol !== undefined) { + request['symbol'] = this.marketId (symbol); + } + if (id !== undefined) { + request['order_id'] = id; + } + return await this.privatePostApiOrderCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + let payload = this.urlencode ({ 'api_key': this.apiKey }); + if (Object.keys (query).length) { + payload += '&' + this.urlencode (this.keysort (query)); + } + const auth = payload + '&secret_key=' + this.secret; + const signature = this.hash (this.encode (auth)); + body = payload + '&sign=' + signature; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const errorCode = this.safeValue (response, 'code'); + if (errorCode !== undefined) { + if (errorCode !== 0) { + // + // { code: 1, msg: "该å¸�ä¸�存在,é�žæ³•æ“�作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well) + // { code: 1, msg: '公钥ä¸�å�ˆæ³•' } - wrong public key + // { code: 1, msg: '价格输入有误,请检查你的数值精度' } - 'The price input is incorrect, please check your numerical accuracy' + // { code: 1, msg: 'å�•笔最å°�交易数é‡�ä¸�能å°�于0.00100000,请您é‡�新挂å�•'} - + // 'The minimum number of single transactions cannot be less than 0.00100000. Please re-post the order' + // + const message = this.safeString (response, 'msg'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + if (message.indexOf ('请您é‡�新挂å�•') >= 0) { // minimum limit + throw new InvalidOrder (feedback); + } else { + throw new ExchangeError (feedback); + } + } + } + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[side]; + const cost = parseFloat (this.costToPrecision (symbol, amount * price)); + return { + 'type': takerOrMaker, + 'currency': market['quote'], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), + }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],22:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const hitbtc2 = require ('./hitbtc2'); +// --------------------------------------------------------------------------- + +module.exports = class bequant extends hitbtc2 { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bequant', + 'name': 'Bequant', + 'countries': [ 'MT' ], // Malta + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg', + 'api': 'https://api.bequant.io', + 'www': 'https://bequant.io', + 'doc': [ + 'https://api.bequant.io/', + ], + 'fees': [ + 'https://bequant.io/fees-and-limits', + ], + 'referral': 'https://bequant.io', + }, + }); + } +}; + +},{"./hitbtc2":92}],23:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AccountSuspended, ArgumentsRequired, AuthenticationError, DDoSProtection, ExchangeNotAvailable, InvalidOrder, OrderNotFound, PermissionDenied, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bibox extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bibox', + 'name': 'Bibox', + 'countries': [ 'CN', 'US', 'KR' ], + 'version': 'v1', + 'has': { + 'CORS': false, + 'publicAPI': false, + 'fetchBalance': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchFundingFees': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'createMarketOrder': false, // or they will return https://github.com/ccxt/ccxt/issues/2338 + 'withdraw': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': 'day', + '1w': 'week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/34902611-2be8bf1a-f830-11e7-91a2-11b2f292e750.jpg', + 'api': 'https://api.bibox.com', + 'www': 'https://www.bibox.com', + 'doc': [ + 'https://biboxcom.github.io/en/', + ], + 'fees': 'https://bibox.zendesk.com/hc/en-us/articles/360002336133', + 'referral': 'https://www.bibox.com/signPage?id=11114745&lang=en', + }, + 'api': { + 'public': { + 'post': [ + // TODO: rework for full endpoint/cmd paths here + 'mdata', + ], + 'get': [ + 'mdata', + ], + }, + 'private': { + 'post': [ + 'user', + 'orderpending', + 'transfer', + ], + }, + 'v2private': { + 'post': [ + 'assets/transfer/spot', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'exceptions': { + '2011': AccountSuspended, // Account is locked + '2015': AuthenticationError, // Google authenticator is wrong + '2021': InsufficientFunds, // Insufficient balance available for withdrawal + '2027': InsufficientFunds, // Insufficient balance available (for trade) + '2033': OrderNotFound, // operation failed! Orders have been completed or revoked + '2067': InvalidOrder, // Does not support market orders + '2068': InvalidOrder, // The number of orders can not be less than + '2085': InvalidOrder, // Order quantity is too small + '3012': AuthenticationError, // invalid apiKey + '3024': PermissionDenied, // wrong apikey permissions + '3025': AuthenticationError, // signature failed + '4000': ExchangeNotAvailable, // current network is unstable + '4003': DDoSProtection, // server busy please try again later + }, + 'commonCurrencies': { + 'KEY': 'Bihu', + 'MTC': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025 + 'PAI': 'PCHAIN', + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { + 'cmd': 'marketAll', + }; + const response = await this.publicGetMdata (this.extend (request, params)); + // + // { + // "result": [ + // { + // "is_hide":0, + // "high_cny":"1.9478", + // "amount":"272.41", + // "coin_symbol":"BIX", + // "last":"0.00002487", + // "currency_symbol":"BTC", + // "change":"+0.00000073", + // "low_cny":"1.7408", + // "base_last_cny":"1.84538041", + // "area_id":7, + // "percent":"+3.02%", + // "last_cny":"1.8454", + // "high":"0.00002625", + // "low":"0.00002346", + // "pair_type":0, + // "last_usd":"0.2686", + // "vol24H":"10940613", + // "id":1, + // "high_usd":"0.2835", + // "low_usd":"0.2534" + // } + // ], + // "cmd":"marketAll", + // "ver":"1.1" + // } + // + const markets = this.safeValue (response, 'result'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const numericId = this.safeInteger (market, 'id'); + const baseId = this.safeString (market, 'coin_symbol'); + const quoteId = this.safeString (market, 'currency_symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = baseId + '_' + quoteId; + const precision = { + 'amount': 4, + 'price': 8, + }; + result.push ({ + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // we don't set values that are not defined by the exchange + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const baseId = this.safeString (ticker, 'coin_symbol'); + const quoteId = this.safeString (ticker, 'currency_symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const last = this.safeFloat (ticker, 'last'); + const change = this.safeFloat (ticker, 'change'); + const baseVolume = this.safeFloat2 (ticker, 'vol', 'vol24H'); + let open = undefined; + if ((last !== undefined) && (change !== undefined)) { + open = last - change; + } + let percentage = this.safeString (ticker, 'percent'); + if (percentage !== undefined) { + percentage = percentage.replace ('%', ''); + percentage = parseFloat (percentage); + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': this.safeFloat (ticker, 'amount'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'ticker', + 'pair': market['id'], + }; + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseTicker (response['result'], market); + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + const ticker = this.parseTicker (rawTickers[i]); + if ((symbols === undefined) || (this.inArray (ticker['symbol'], symbols))) { + tickers.push (ticker); + } + } + return tickers; + } + + async fetchTickers (symbols = undefined, params = {}) { + const request = { + 'cmd': 'marketAll', + }; + const response = await this.publicGetMdata (this.extend (request, params)); + const tickers = this.parseTickers (response['result'], symbols); + return this.indexBy (tickers, 'symbol'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger2 (trade, 'time', 'createdAt'); + let side = this.safeInteger2 (trade, 'side', 'order_side'); + side = (side === 1) ? 'buy' : 'sell'; + let symbol = undefined; + if (market === undefined) { + let marketId = this.safeString (trade, 'pair'); + if (marketId === undefined) { + const baseId = this.safeString (trade, 'coin_symbol'); + const quoteId = this.safeString (trade, 'currency_symbol'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + marketId = baseId + '_' + quoteId; + } + } + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + let feeCurrency = this.safeString (trade, 'fee_symbol'); + if (feeCurrency !== undefined) { + if (feeCurrency in this.currencies_by_id) { + feeCurrency = this.currencies_by_id[feeCurrency]['code']; + } else { + feeCurrency = this.safeCurrencyCode (feeCurrency); + } + } + const feeRate = undefined; // todo: deduce from market if market is defined + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined && amount !== undefined) { + cost = price * amount; + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }; + } + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'id': id, + 'order': undefined, // Bibox does not have it (documented) yet + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': 'limit', + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'deals', + 'pair': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; // default = 200 + } + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseTrades (response['result'], market, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'depth', + 'pair': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; // default = 200 + } + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseOrderBook (response['result'], this.safeFloat (response['result'], 'update_time'), 'bids', 'asks', 'price', 'volume'); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv['time'], + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'vol'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'kline', + 'pair': market['id'], + 'period': this.timeframes[timeframe], + 'size': limit, + }; + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + + async fetchCurrencies (params = {}) { + if (!this.apiKey || !this.secret) { + throw new AuthenticationError (this.id + " fetchCurrencies is an authenticated endpoint, therefore it requires 'apiKey' and 'secret' credentials. If you don't need currency details, set exchange.has['fetchCurrencies'] = false before calling its methods."); + } + const request = { + 'cmd': 'transfer/coinList', + 'body': {}, + }; + const response = await this.privatePostTransfer (this.extend (request, params)); + const currencies = this.safeValue (response, 'result'); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'symbol'); + const name = currency['name']; // contains hieroglyphs causing python ASCII bug + const code = this.safeCurrencyCode (id); + const precision = 8; + const deposit = this.safeValue (currency, 'enable_deposit'); + const withdraw = this.safeValue (currency, 'enable_withdraw'); + const active = (deposit && withdraw); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': name, + 'active': active, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const type = this.safeString (params, 'type', 'assets'); + params = this.omit (params, 'type'); + const request = { + 'cmd': 'transfer/' + type, // assets, mainAssets + 'body': this.extend ({ + 'select': 1, // return full info + }, params), + }; + const response = await this.privatePostTransfer (request); + const balances = this.safeValue (response, 'result'); + const result = { 'info': balances }; + let indexed = undefined; + if ('assets_list' in balances) { + indexed = this.indexBy (balances['assets_list'], 'coin_symbol'); + } else { + indexed = balances; + } + const keys = Object.keys (indexed); + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + let code = id.toUpperCase (); + if (code.indexOf ('TOTAL_') >= 0) { + code = code.slice (6); + } + if (code in this.currencies_by_id) { + code = this.currencies_by_id[code]['code']; + } + const account = this.account (); + let balance = indexed[id]; + if (typeof balance === 'string') { + balance = parseFloat (balance); + account['free'] = balance; + account['used'] = 0.0; + account['total'] = balance; + } else { + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'freeze'); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = { + 'page': 1, + }; + if (code !== undefined) { + currency = this.currency (code); + request['symbol'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; + } else { + request['size'] = 100; + } + const response = await this.privatePostTransfer ({ + 'cmd': 'transfer/transferInList', + 'body': this.extend (request, params), + }); + const deposits = this.safeValue (response['result'], 'items', []); + for (let i = 0; i < deposits.length; i++) { + deposits[i]['type'] = 'deposit'; + } + return this.parseTransactions (deposits, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = { + 'page': 1, + }; + if (code !== undefined) { + currency = this.currency (code); + request['symbol'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; + } else { + request['size'] = 100; + } + const response = await this.privatePostTransfer ({ + 'cmd': 'transfer/transferOutList', + 'body': this.extend (request, params), + }); + const withdrawals = this.safeValue (response['result'], 'items', []); + for (let i = 0; i < withdrawals.length; i++) { + withdrawals[i]['type'] = 'withdrawal'; + } + return this.parseTransactions (withdrawals, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // 'id': 1023291, + // 'coin_symbol': 'ETH', + // 'to_address': '0x7263....', + // 'amount': '0.49170000', + // 'confirmCount': '16', + // 'createdAt': 1553123867000, + // 'status': 2 + // } + // + // fetchWithdrawals + // + // { + // 'id': 521844, + // 'coin_symbol': 'ETH', + // 'to_address': '0xfd4e....', + // 'addr_remark': '', + // 'amount': '0.39452750', + // 'fee': '0.00600000', + // 'createdAt': 1553226906000, + // 'memo': '', + // 'status': 3 + // } + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'to_address'); + const currencyId = this.safeString (transaction, 'coin_symbol'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeString (transaction, 'createdAt'); + let tag = this.safeString (transaction, 'addr_remark'); + const type = this.safeString (transaction, 'type'); + const status = this.parseTransactionStatusByType (this.safeString (transaction, 'status'), type); + const amount = this.safeFloat (transaction, 'amount'); + let feeCost = this.safeFloat (transaction, 'fee'); + if (type === 'deposit') { + feeCost = 0; + tag = undefined; + } + const fee = { + 'cost': feeCost, + 'currency': code, + }; + return { + 'info': transaction, + 'id': id, + 'txid': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + parseTransactionStatusByType (status, type = undefined) { + const statuses = { + 'deposit': { + '1': 'pending', + '2': 'ok', + }, + 'withdrawal': { + '0': 'pending', + '3': 'ok', + }, + }; + return this.safeString (this.safeValue (statuses, type, {}), status, status); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const orderType = (type === 'limit') ? 2 : 1; + const orderSide = (side === 'buy') ? 1 : 2; + const request = { + 'cmd': 'orderpending/trade', + 'body': this.extend ({ + 'pair': market['id'], + 'account_type': 0, + 'order_type': orderType, + 'order_side': orderSide, + 'pay_bix': 0, + 'amount': amount, + 'price': price, + }, params), + }; + const response = await this.privatePostOrderpending (request); + return { + 'info': response, + 'id': this.safeString (response, 'result'), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'cmd': 'orderpending/cancelTrade', + 'body': this.extend ({ + 'orders_id': id, + }, params), + }; + const response = await this.privatePostOrderpending (request); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'cmd': 'orderpending/order', + 'body': this.extend ({ + 'id': id.toString (), + 'account_type': 0, // 0 = spot account + }, params), + }; + const response = await this.privatePostOrderpending (request); + const order = this.safeValue (response, 'result'); + if (this.isEmpty (order)) { + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + return this.parseOrder (order); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market === undefined) { + let marketId = undefined; + const baseId = this.safeString (order, 'coin_symbol'); + const quoteId = this.safeString (order, 'currency_symbol'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + marketId = baseId + '_' + quoteId; + } + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const type = (order['order_type'] === 1) ? 'market' : 'limit'; + const timestamp = order['createdAt']; + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'deal_price'); + const filled = this.safeFloat (order, 'deal_amount'); + const amount = this.safeFloat (order, 'amount'); + let cost = this.safeFloat2 (order, 'deal_money', 'money'); + let remaining = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (cost === undefined) { + cost = price * filled; + } + } + const side = (order['order_side'] === 1) ? 'buy' : 'sell'; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString (order, 'id'); + const feeCost = this.safeFloat (order, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': undefined, + }; + } + cost = cost ? cost : (parseFloat (price) * filled); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + parseOrderStatus (status) { + const statuses = { + // original comments from bibox: + '1': 'open', // pending + '2': 'open', // part completed + '3': 'closed', // completed + '4': 'canceled', // part canceled + '5': 'canceled', // canceled + '6': 'canceled', // canceling + }; + return this.safeString (statuses, status, status); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let pair = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + pair = market['id']; + } + const size = limit ? limit : 200; + const request = { + 'cmd': 'orderpending/orderPendingList', + 'body': this.extend ({ + 'pair': pair, + 'account_type': 0, // 0 - regular, 1 - margin + 'page': 1, + 'size': size, + }, params), + }; + const response = await this.privatePostOrderpending (request); + const orders = this.safeValue (response['result'], 'items', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = 200, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'orderpending/pendingHistoryList', + 'body': this.extend ({ + 'pair': market['id'], + 'account_type': 0, // 0 - regular, 1 - margin + 'page': 1, + 'size': limit, + }, params), + }; + const response = await this.privatePostOrderpending (request); + const orders = this.safeValue (response['result'], 'items', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const size = limit ? limit : 200; + const request = { + 'cmd': 'orderpending/orderHistoryList', + 'body': this.extend ({ + 'pair': market['id'], + 'account_type': 0, // 0 - regular, 1 - margin + 'page': 1, + 'size': size, + 'coin_symbol': market['baseId'], + 'currency_symbol': market['quoteId'], + }, params), + }; + const response = await this.privatePostOrderpending (request); + const trades = this.safeValue (response['result'], 'items', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'cmd': 'transfer/transferIn', + 'body': this.extend ({ + 'coin_symbol': currency['id'], + }, params), + }; + const response = await this.privatePostTransfer (request); + // + // { + // "result":"3Jx6RZ9TNMsAoy9NUzBwZf68QBppDruSKW","cmd":"transfer/transferIn" + // } + // + // { + // "result":"{\"account\":\"PERSONALLY OMITTED\",\"memo\":\"PERSONALLY OMITTED\"}","cmd":"transfer/transferIn" + // } + // + const result = this.safeString (response, 'result'); + let address = result; + let tag = undefined; + if (this.isJsonEncodedObject (result)) { + const parsed = JSON.parse (result); + address = this.safeString (parsed, 'account'); + tag = this.safeString (parsed, 'memo'); + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (this.password === undefined) { + if (!('trade_pwd' in params)) { + throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a trade_pwd parameter'); + } + } + if (!('totp_code' in params)) { + throw new ExchangeError (this.id + ' withdraw() requires a totp_code parameter for 2FA authentication'); + } + const request = { + 'trade_pwd': this.password, + 'coin_symbol': currency['id'], + 'amount': amount, + 'addr': address, + }; + if (tag !== undefined) { + request['address_remark'] = tag; + } + const response = await this.privatePostTransfer ({ + 'cmd': 'transfer/transferOut', + 'body': this.extend (request, params), + }); + return { + 'info': response, + 'id': undefined, + }; + } + + async fetchFundingFees (codes = undefined, params = {}) { + // by default it will try load withdrawal fees of all currencies (with separate requests) + // however if you define codes = [ 'ETH', 'BTC' ] in args it will only load those + await this.loadMarkets (); + const withdrawFees = {}; + const info = {}; + if (codes === undefined) { + codes = Object.keys (this.currencies); + } + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const request = { + 'cmd': 'transfer/coinConfig', + 'body': this.extend ({ + 'coin_symbol': currency['id'], + }, params), + }; + const response = await this.privatePostTransfer (request); + info[code] = response; + withdrawFees[code] = this.safeFloat (response['result'], 'withdraw_fee'); + } + return { + 'info': info, + 'withdraw': withdrawFees, + 'deposit': {}, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + const cmds = this.json ([ params ]); + if (api === 'public') { + if (method !== 'GET') { + body = { 'cmds': cmds }; + } else if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v2private') { + this.checkRequiredCredentials (); + url = this.urls['api'] + '/v2/' + path; + const json_params = this.json (params); + body = { + 'body': json_params, + 'apikey': this.apiKey, + 'sign': this.hmac (this.encode (json_params), this.encode (this.secret), 'md5'), + }; + } else { + this.checkRequiredCredentials (); + body = { + 'cmds': cmds, + 'apikey': this.apiKey, + 'sign': this.hmac (this.encode (cmds), this.encode (this.secret), 'md5'), + }; + } + if (body !== undefined) { + body = this.json (body, { 'convertArraysToObjects': true }); + } + headers = { 'Content-Type': 'application/json' }; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if ('error' in response) { + if ('code' in response['error']) { + const code = this.safeString (response['error'], 'code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } + throw new ExchangeError (this.id + ' ' + body); + } + if (!('result' in response)) { + throw new ExchangeError (this.id + ' ' + body); + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (method === 'GET') { + return response; + } else { + return response['result'][0]; + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],24:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, OrderNotFound, InsufficientFunds, PermissionDenied, BadRequest, RateLimitExceeded, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bigone extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bigone', + 'name': 'BigONE', + 'countries': [ 'CN' ], + 'version': 'v3', + 'rateLimit': 1200, // 500 request per 10 minutes + 'has': { + 'cancelAllOrders': true, + 'createMarketOrder': false, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchTickers': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': 'min1', + '5m': 'min5', + '15m': 'min15', + '30m': 'min30', + '1h': 'hour1', + '3h': 'hour3', + '4h': 'hour4', + '6h': 'hour6', + '12h': 'hour12', + '1d': 'day1', + '1w': 'week1', + '1M': 'month1', + }, + 'hostname': 'big.one', // set to 'b1.run' for China mainland + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/69354403-1d532180-0c91-11ea-88ed-44c06cefdf87.jpg', + 'api': { + 'public': 'https://{hostname}/api/v3', + 'private': 'https://{hostname}/api/v3/viewer', + }, + 'www': 'https://big.one', + 'doc': 'https://open.big.one/docs/api.html', + 'fees': 'https://bigone.zendesk.com/hc/en-us/articles/115001933374-BigONE-Fee-Policy', + 'referral': 'https://b1.run/users/new?code=D3LLBVFT', + }, + 'api': { + 'public': { + 'get': [ + 'ping', + 'asset_pairs', + 'asset_pairs/{asset_pair_name}/depth', + 'asset_pairs/{asset_pair_name}/trades', + 'asset_pairs/{asset_pair_name}/ticker', + 'asset_pairs/{asset_pair_name}/candles', + 'asset_pairs/tickers', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'assets/{asset_symbol}/address', + 'orders', + 'orders/{id}', + 'orders/multi', + 'trades', + 'withdrawals', + 'deposits', + ], + 'post': [ + 'orders', + 'orders/{id}/cancel', + 'orders/cancel', + 'withdrawals', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + // HARDCODING IS DEPRECATED THE FEES BELOW ARE TO BE REMOVED SOON + 'withdraw': { + 'BTC': 0.001, + 'ETH': 0.005, + 'EOS': 0.01, + 'ZEC': 0.003, + 'LTC': 0.01, + 'QTUM': 0.01, + // 'INK': 0.01 QTUM, + // 'BOT': 0.01 QTUM, + 'ETC': 0.01, + 'GAS': 0.0, + 'BTS': 1.0, + 'GXS': 0.1, + 'BITCNY': 19.0, + }, + }, + }, + 'exceptions': { + 'exact': { + '10001': BadRequest, // syntax error + '10005': ExchangeError, // internal error + "Amount's scale must greater than AssetPair's base scale": InvalidOrder, + '10007': BadRequest, // parameter error, {"code":10007,"message":"Amount's scale must greater than AssetPair's base scale"} + '10011': ExchangeError, // system error + '10013': OrderNotFound, // {"code":10013,"message":"Resource not found"} + '10014': InsufficientFunds, // {"code":10014,"message":"Insufficient funds"} + '10403': PermissionDenied, // permission denied + '10429': RateLimitExceeded, // too many requests + '40004': AuthenticationError, // {"code":40004,"message":"invalid jwt"} + '40103': AuthenticationError, // invalid otp code + '40104': AuthenticationError, // invalid asset pin code + '40301': PermissionDenied, // {"code":40301,"message":"Permission denied withdrawal create"} + '40302': ExchangeError, // already requested + '40601': ExchangeError, // resource is locked + '40602': ExchangeError, // resource is depleted + '40603': InsufficientFunds, // insufficient resource + '40120': InvalidOrder, // Order is in trading + '40121': InvalidOrder, // Order is already cancelled or filled + }, + 'broad': { + }, + }, + 'commonCurrencies': { + 'ONE': 'BigONE Token', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAssetPairs (params); + // + // { + // "code":0, + // "data":[ + // { + // "id":"01e48809-b42f-4a38-96b1-c4c547365db1", + // "name":"PCX-BTC", + // "quote_scale":7, + // "quote_asset":{ + // "id":"0df9c3c3-255a-46d7-ab82-dedae169fba9", + // "symbol":"BTC", + // "name":"Bitcoin", + // }, + // "base_asset":{ + // "id":"405484f7-4b03-4378-a9c1-2bd718ecab51", + // "symbol":"PCX", + // "name":"ChainX", + // }, + // "base_scale":3, + // "min_quote_value":"0.0001", + // }, + // ] + // } + // + const markets = this.safeValue (response, 'data', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const uuid = this.safeString (market, 'id'); + const baseAsset = this.safeValue (market, 'base_asset', {}); + const quoteAsset = this.safeValue (market, 'quote_asset', {}); + const baseId = this.safeString (baseAsset, 'symbol'); + const quoteId = this.safeString (quoteAsset, 'symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'base_scale'), + 'price': this.safeInteger (market, 'quote_scale'), + }; + const minCost = this.safeInteger (market, 'min_quote_value'); + const entry = { + 'id': id, + 'uuid': uuid, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }; + result.push (entry); + } + return result; + } + + async loadMarkets (reload = false, params = {}) { + const markets = await super.loadMarkets (reload, params); + let marketsByUuid = this.safeValue (this.options, 'marketsByUuid'); + if ((marketsByUuid === undefined) || reload) { + marketsByUuid = {}; + for (let i = 0; i < this.symbols.length; i++) { + const symbol = this.symbols[i]; + const market = this.markets[symbol]; + const uuid = this.safeString (market, 'uuid'); + marketsByUuid[uuid] = market; + } + this.options['marketsByUuid'] = marketsByUuid; + } + return markets; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "asset_pair_name":"ETH-BTC", + // "bid":{"price":"0.021593","order_count":1,"quantity":"0.20936"}, + // "ask":{"price":"0.021613","order_count":1,"quantity":"2.87064"}, + // "open":"0.021795", + // "high":"0.021795", + // "low":"0.021471", + // "close":"0.021613", + // "volume":"117078.90431", + // "daily_change":"-0.000182" + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'asset_pair_name'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const close = this.safeFloat (ticker, 'close'); + const bid = this.safeValue (ticker, 'bid', {}); + const ask = this.safeValue (ticker, 'ask', {}); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (bid, 'price'), + 'bidVolume': this.safeFloat (bid, 'quantity'), + 'ask': this.safeFloat (ask, 'price'), + 'askVolume': this.safeFloat (ask, 'quantity'), + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'daily_change'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + const response = await this.publicGetAssetPairsAssetPairNameTicker (this.extend (request, params)); + // + // { + // "code":0, + // "data":{ + // "asset_pair_name":"ETH-BTC", + // "bid":{"price":"0.021593","order_count":1,"quantity":"0.20936"}, + // "ask":{"price":"0.021613","order_count":1,"quantity":"2.87064"}, + // "open":"0.021795", + // "high":"0.021795", + // "low":"0.021471", + // "close":"0.021613", + // "volume":"117078.90431", + // "daily_change":"-0.000182" + // } + // } + // + const ticker = this.safeValue (response, 'data', {}); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['pair_names'] = ids.join (','); + } + const response = await this.publicGetAssetPairsTickers (this.extend (request, params)); + // + // { + // "code":0, + // "data":[ + // { + // "asset_pair_name":"PCX-BTC", + // "bid":{"price":"0.000234","order_count":1,"quantity":"0.518"}, + // "ask":{"price":"0.0002348","order_count":1,"quantity":"2.348"}, + // "open":"0.0002343", + // "high":"0.0002348", + // "low":"0.0002162", + // "close":"0.0002348", + // "volume":"12887.016", + // "daily_change":"0.0000005" + // }, + // { + // "asset_pair_name":"GXC-USDT", + // "bid":{"price":"0.5054","order_count":1,"quantity":"40.53"}, + // "ask":{"price":"0.5055","order_count":1,"quantity":"38.53"}, + // "open":"0.5262", + // "high":"0.5323", + // "low":"0.5055", + // "close":"0.5055", + // "volume":"603963.05", + // "daily_change":"-0.0207" + // } + // ] + // } + // + const tickers = this.safeValue (response, 'data', []); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 50, max 200 + } + const response = await this.publicGetAssetPairsAssetPairNameDepth (this.extend (request, params)); + // + // { + // "code":0, + // "data": { + // "asset_pair_name": "EOS-BTC", + // "bids": [ + // { "price": "42", "order_count": 4, "quantity": "23.33363711" } + // ], + // "asks": [ + // { "price": "45", "order_count": 2, "quantity": "4193.3283464" } + // ] + // } + // } + // + const orderbook = this.safeValue (response, 'data', {}); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'quantity'); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "id": 38199941, + // "price": "3378.67", + // "amount": "0.019812", + // "taker_side": "ASK", + // "created_at": "2019-01-29T06:05:56Z" + // } + // + // fetchMyTrades (private) + // + // { + // "id": 10854280, + // "asset_pair_name": "XIN-USDT", + // "price": "70", + // "amount": "1", + // "taker_side": "ASK", + // "maker_order_id": 58284908, + // "taker_order_id": 58284909, + // "maker_fee": "0.0008", + // "taker_fee": "0.07", + // "side": "SELF_TRADING", + // "inserted_at": "2019-04-16T12:00:01Z" + // }, + // + // { + // "id": 10854263, + // "asset_pair_name": "XIN-USDT", + // "price": "75.7", + // "amount": "12.743149", + // "taker_side": "BID", + // "maker_order_id": null, + // "taker_order_id": 58284888, + // "maker_fee": null, + // "taker_fee": "0.0025486298", + // "side": "BID", + // "inserted_at": "2019-04-15T06:20:57Z" + // } + // + const timestamp = this.parse8601 (this.safeString2 (trade, 'created_at', 'inserted_at')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const marketId = this.safeString (trade, 'asset_pair_name'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = this.costToPrecision (symbol, price * amount); + } + } + let side = this.safeString (trade, 'side'); + const takerSide = this.safeString (trade, 'taker_side'); + let takerOrMaker = undefined; + if ((takerSide !== undefined) && (side !== undefined) && (side !== 'SELF_TRADING')) { + takerOrMaker = (takerSide === side) ? 'taker' : 'maker'; + } + if (side === undefined) { + // taker side is not related to buy/sell side + // the following code is probably a mistake + side = (takerSide === 'ASK') ? 'sell' : 'buy'; + } else { + if (side === 'BID') { + side = 'buy'; + } else if (side === 'ASK') { + side = 'sell'; + } + } + const makerOrderId = this.safeString (trade, 'maker_order_id'); + const takerOrderId = this.safeString (trade, 'taker_order_id'); + let orderId = undefined; + if (makerOrderId !== undefined) { + if (takerOrderId !== undefined) { + orderId = [ makerOrderId, takerOrderId ]; + } else { + orderId = makerOrderId; + } + } else if (takerOrderId !== undefined) { + orderId = takerOrderId; + } + const id = this.safeString (trade, 'id'); + const result = { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': parseFloat (cost), + 'info': trade, + }; + let makerCurrencyCode = undefined; + let takerCurrencyCode = undefined; + if ((market !== undefined) && (takerOrMaker !== undefined)) { + if (side === 'buy') { + if (takerOrMaker === 'maker') { + makerCurrencyCode = market['base']; + takerCurrencyCode = market['quote']; + } else { + makerCurrencyCode = market['quote']; + takerCurrencyCode = market['base']; + } + } else { + if (takerOrMaker === 'maker') { + makerCurrencyCode = market['quote']; + takerCurrencyCode = market['base']; + } else { + makerCurrencyCode = market['base']; + takerCurrencyCode = market['quote']; + } + } + } else if (side === 'SELF_TRADING') { + if (takerSide === 'BID') { + makerCurrencyCode = market['quote']; + takerCurrencyCode = market['base']; + } else if (takerSide === 'ASK') { + makerCurrencyCode = market['base']; + takerCurrencyCode = market['quote']; + } + } + const makerFeeCost = this.safeFloat (trade, 'maker_fee'); + const takerFeeCost = this.safeFloat (trade, 'taker_fee'); + if (makerFeeCost !== undefined) { + if (takerFeeCost !== undefined) { + result['fees'] = [ + { 'cost': makerFeeCost, 'currency': makerCurrencyCode }, + { 'cost': takerFeeCost, 'currency': takerCurrencyCode }, + ]; + } else { + result['fee'] = { 'cost': makerFeeCost, 'currency': makerCurrencyCode }; + } + } else if (takerFeeCost !== undefined) { + result['fee'] = { 'cost': takerFeeCost, 'currency': takerCurrencyCode }; + } else { + result['fee'] = undefined; + } + return result; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + const response = await this.publicGetAssetPairsAssetPairNameTrades (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "id": 38199941, + // "price": "3378.67", + // "amount": "0.019812", + // "taker_side": "ASK", + // "created_at": "2019-01-29T06:05:56Z" + // }, + // { + // "id": 38199934, + // "price": "3376.14", + // "amount": "0.019384", + // "taker_side": "ASK", + // "created_at": "2019-01-29T06:05:40Z" + // } + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // close: '0.021562', + // high: '0.021563', + // low: '0.02156', + // open: '0.021563', + // time: '2019-11-21T07:54:00Z', + // volume: '59.84376' + // } + // + return [ + this.parse8601 (this.safeString (ohlcv, 'time')), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 100; // default 100, max 500 + } + const request = { + 'asset_pair_name': market['id'], + 'period': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + // const start = parseInt (since / 1000); + const end = this.sum (since, limit * this.parseTimeframe (timeframe) * 1000); + request['time'] = this.iso8601 (end); + } + const response = await this.publicGetAssetPairsAssetPairNameCandles (this.extend (request, params)); + // + // { + // code: 0, + // data: [ + // { + // close: '0.021656', + // high: '0.021658', + // low: '0.021652', + // open: '0.021652', + // time: '2019-11-21T09:30:00Z', + // volume: '53.08664' + // }, + // { + // close: '0.021652', + // high: '0.021656', + // low: '0.021652', + // open: '0.021656', + // time: '2019-11-21T09:29:00Z', + // volume: '88.39861' + // }, + // ] + // } + // + const ohlcvs = this.safeValue (response, 'data', []); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + // + // { + // "code":0, + // "data":[ + // {"asset_symbol":"NKC","balance":"0","locked_balance":"0"}, + // {"asset_symbol":"UBTC","balance":"0","locked_balance":"0"}, + // {"asset_symbol":"READ","balance":"0","locked_balance":"0"}, + // ], + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const symbol = this.safeString (balance, 'asset_symbol'); + const code = this.safeCurrencyCode (symbol); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + // + // { + // "id": 10, + // "asset_pair_name": "EOS-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "FILLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z", + // } + // + const id = this.safeString (order, 'id'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'asset_pair_name'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'filled_amount'); + let remaining = undefined; + if (amount !== undefined && filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let side = this.safeString (order, 'side'); + if (side === 'BID') { + side = 'buy'; + } else { + side = 'sell'; + } + let cost = undefined; + if (filled !== undefined) { + if (price !== undefined) { + cost = filled * price; + } + } + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'updated_at')); + const average = this.safeFloat (order, 'avg_deal_price'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + side = (side === 'buy') ? 'BID' : 'ASK'; + const request = { + 'asset_pair_name': market['id'], // asset pair name BTC-USDT, required + 'side': side, // order side one of "ASK"/"BID", required + 'amount': this.amountToPrecision (symbol, amount), // order amount, string, required + 'price': this.priceToPrecision (symbol, price), // order price, string, required + }; + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "id": 10, + // "asset_pair_name": "EOS-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "FILLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z" + // } + // + const order = this.safeValue (response, 'data'); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { 'id': id }; + const response = await this.privatePostOrdersIdCancel (this.extend (request, params)); + // { + // "id": 10, + // "asset_pair_name": "EOS-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "CANCELLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z" + // } + const order = this.safeValue (response, 'data'); + return this.parseOrder (order); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + const response = await this.privatePostOrdersCancel (this.extend (request, params)); + // + // { + // "code":0, + // "data": { + // "cancelled":[ + // 58272370, + // 58272377 + // ], + // "failed": [] + // } + // } + // + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { 'id': id }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + const order = this.safeValue (response, 'data', {}); + return this.parseOrder (order); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + // 'page_token': 'dxzef', // request page after this page token + // 'side': 'ASK', // 'ASK' or 'BID', optional + // 'state': 'FILLED', // 'CANCELLED', 'FILLED', 'PENDING' + // 'limit' 20, // default 20, max 200 + }; + if (limit !== undefined) { + request['limit'] = limit; // default 20, max 200 + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "code":0, + // "data": [ + // { + // "id": 10, + // "asset_pair_name": "ETH-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "FILLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z", + // }, + // ], + // "page_token":"dxzef", + // } + // + const orders = this.safeValue (response, 'data', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument'); + } + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + // 'page_token': 'dxzef', // request page after this page token + }; + if (limit !== undefined) { + request['limit'] = limit; // default 20, max 200 + } + const response = await this.privateGetTrades (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "id": 10854280, + // "asset_pair_name": "XIN-USDT", + // "price": "70", + // "amount": "1", + // "taker_side": "ASK", + // "maker_order_id": 58284908, + // "taker_order_id": 58284909, + // "maker_fee": "0.0008", + // "taker_fee": "0.07", + // "side": "SELF_TRADING", + // "inserted_at": "2019-04-16T12:00:01Z" + // }, + // { + // "id": 10854263, + // "asset_pair_name": "XIN-USDT", + // "price": "75.7", + // "amount": "12.743149", + // "taker_side": "BID", + // "maker_order_id": null, + // "taker_order_id": 58284888, + // "maker_fee": null, + // "taker_fee": "0.0025486298", + // "side": "BID", + // "inserted_at": "2019-04-15T06:20:57Z" + // } + // ], + // "page_token":"dxfv" + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit, params); + } + + parseOrderStatus (status) { + const statuses = { + 'PENDING': 'open', + 'FILLED': 'closed', + 'CANCELLED': 'canceled', + }; + return this.safeString (statuses, status); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'PENDING', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'FILLED', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + nonce () { + return this.microseconds () * 1000; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + const baseUrl = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname }); + let url = baseUrl + '/' + this.implodeParams (path, params); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const request = { + 'type': 'OpenAPIV2', + 'sub': this.apiKey, + 'nonce': nonce, + // 'recv_window': '30', // default 30 + }; + const jwt = this.jwt (request, this.encode (this.secret)); + headers = { + 'Authorization': 'Bearer ' + jwt, + }; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (method === 'POST') { + headers['Content-Type'] = 'application/json'; + body = this.json (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset_symbol': currency['id'], + }; + const response = await this.privateGetAssetsAssetSymbolAddress (this.extend (request, params)); + // + // the actual response format is not the same as the documented one + // the data key contains an array in the actual response + // + // { + // "code":0, + // "message":"", + // "data":[ + // { + // "id":5521878, + // "chain":"Bitcoin", + // "value":"1GbmyKoikhpiQVZ1C9sbF17mTyvBjeobVe", + // "memo":"" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const dataLength = data.length; + if (dataLength < 1) { + throw new ExchangeError (this.id + 'fetchDepositAddress() returned empty address response'); + } + const firstElement = data[0]; + const address = this.safeString (firstElement, 'value'); + const tag = this.safeString (firstElement, 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + // what are other statuses here? + 'WITHHOLD': 'ok', // deposits + 'UNCONFIRMED': 'pending', + 'CONFIRMED': 'ok', // withdrawals + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "amount": "25.0", + // "asset_symbol": "BTS" + // "confirms": 100, + // "id": 5, + // "inserted_at": "2018-02-16T11:39:58.000Z", + // "is_internal": false, + // "kind": "default", + // "memo": "", + // "state": "WITHHOLD", + // "txid": "72e03037d144dae3d32b68b5045462b1049a0755", + // "updated_at": "2018-11-09T10:20:09.000Z", + // } + // + // fetchWithdrawals + // + // { + // "amount": "5", + // "asset_symbol": "ETH", + // "completed_at": "2018-03-15T16:13:45.610463Z", + // "customer_id": "10", + // "id": 10, + // "inserted_at": "2018-03-15T16:13:45.610463Z", + // "is_internal": true, + // "note": "2018-03-15T16:13:45.610463Z", + // "state": "CONFIRMED", + // "target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7" + // "txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0", + // } + // + const currencyId = this.safeString (transaction, 'asset_symbol'); + const code = this.safeCurrencyCode (currencyId); + const id = this.safeInteger (transaction, 'id'); + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const timestamp = this.parse8601 (this.safeString (transaction, 'inserted_at')); + const updated = this.parse8601 (this.safeString2 (transaction, 'updated_at', 'completed_at')); + const txid = this.safeString (transaction, 'txid'); + const address = this.safeString (transaction, 'target_address'); + const tag = this.safeString (transaction, 'memo'); + const type = ('customer_id' in transaction) ? 'deposit' : 'withdrawal'; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': undefined, + 'addressTo': address, + 'tagFrom': undefined, + 'tag': tag, + 'tagTo': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': undefined, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page_token': 'dxzef', // request page after this page token + // 'limit': 50, // optional, default 50 + // 'kind': 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining + // 'asset_symbol': 'BTC', // optional + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['asset_symbol'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 50 + } + const response = await this.privateGetDeposits (this.extend (request, params)); + // + // { + // "code": 0, + // "page_token": "NQ==", + // "data": [ + // { + // "id": 5, + // "amount": "25.0", + // "confirms": 100, + // "txid": "72e03037d144dae3d32b68b5045462b1049a0755", + // "is_internal": false, + // "inserted_at": "2018-02-16T11:39:58.000Z", + // "updated_at": "2018-11-09T10:20:09.000Z", + // "kind": "default", + // "memo": "", + // "state": "WITHHOLD", + // "asset_symbol": "BTS" + // } + // ] + // } + // + const deposits = this.safeValue (response, 'data', []); + return this.parseTransactions (deposits, code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page_token': 'dxzef', // request page after this page token + // 'limit': 50, // optional, default 50 + // 'kind': 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining + // 'asset_symbol': 'BTC', // optional + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['asset_symbol'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 50 + } + const response = await this.privateGetWithdrawals (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "id": 10, + // "customer_id": "10", + // "asset_symbol": "ETH", + // "amount": "5", + // "state": "CONFIRMED", + // "note": "2018-03-15T16:13:45.610463Z", + // "txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0", + // "completed_at": "2018-03-15T16:13:45.610463Z", + // "inserted_at": "2018-03-15T16:13:45.610463Z", + // "is_internal": true, + // "target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7" + // } + // ], + // "page_token":"dxvf" + // } + // + const withdrawals = this.safeValue (response, 'data', []); + return this.parseTransactions (withdrawals, code, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'symbol': currency['id'], + 'target_address': address, + 'amount': this.currencyToPrecision (code, amount), + }; + if (tag !== undefined) { + request['memo'] = tag; + } + // requires write permission on the wallet + const response = await this.privatePostWithdrawals (this.extend (request, params)); + // + // { + // "code":0, + // "message":"", + // "data":[ + // { + // "id":1, + // "customer_id":7, + // "asset_uuid":"50293b12-5be8-4f5b-b31d-d43cdd5ccc29", + // "amount":"100", + // "recipient":null, + // "state":"PENDING", + // "is_internal":true, + // "note":"asdsadsad", + // "kind":"on_chain", + // "txid":"asdasdasdsadsadsad", + // "confirms":5, + // "inserted_at":null, + // "updated_at":null, + // "completed_at":null, + // "commision":null, + // "explain":"" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const dataLength = data.length; + if (dataLength < 1) { + throw new ExchangeError (this.id + ' withdraw() returned an empty response'); + } + const transaction = data[0]; + return this.parseTransaction (transaction, currency); + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"code":10013,"message":"Resource not found"} + // {"code":40004,"message":"invalid jwt"} + // + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + if (code !== '0') { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],25:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress } = require ('./base/errors'); +const { ROUND } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class binance extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'binance', + 'name': 'Binance', + 'countries': [ 'JP', 'MT' ], // Japan, Malta + 'rateLimit': 500, + 'certified': true, + // new metainfo interface + 'has': { + 'fetchDepositAddress': true, + 'CORS': false, + 'fetchBidsAsks': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchOHLCV': true, + 'fetchMyTrades': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': 'emulated', + 'withdraw': true, + 'fetchFundingFees': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + }, + 'timeframes': { + '1m': '1m', + '3m': '3m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '4h': '4h', + '6h': '6h', + '8h': '8h', + '12h': '12h', + '1d': '1d', + '3d': '3d', + '1w': '1w', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg', + 'api': { + 'web': 'https://www.binance.com', + 'wapi': 'https://api.binance.com/wapi/v3', + 'sapi': 'https://api.binance.com/sapi/v1', + 'fapiPublic': 'https://fapi.binance.com/fapi/v1', + 'fapiPrivate': 'https://fapi.binance.com/fapi/v1', + 'public': 'https://api.binance.com/api/v3', + 'private': 'https://api.binance.com/api/v3', + 'v3': 'https://api.binance.com/api/v3', + 'v1': 'https://api.binance.com/api/v1', + }, + 'www': 'https://www.binance.com', + 'referral': 'https://www.binance.com/?ref=10205187', + 'doc': [ + 'https://binance-docs.github.io/apidocs/spot/en', + ], + 'api_management': 'https://www.binance.com/en/usercenter/settings/api-management', + 'fees': 'https://www.binance.com/en/fee/schedule', + }, + 'api': { + 'web': { + 'get': [ + 'exchange/public/product', + 'assetWithdraw/getAllAsset.html', + ], + }, + // the API structure below will need 3-layer apidefs + 'sapi': { + 'get': [ + 'accountSnapshot', + // these endpoints require this.apiKey + 'margin/asset', + 'margin/pair', + 'margin/allAssets', + 'margin/allPairs', + 'margin/priceIndex', + // these endpoints require this.apiKey + this.secret + 'asset/assetDividend', + 'margin/loan', + 'margin/repay', + 'margin/account', + 'margin/transfer', + 'margin/interestHistory', + 'margin/forceLiquidationRec', + 'margin/order', + 'margin/openOrders', + 'margin/allOrders', + 'margin/myTrades', + 'margin/maxBorrowable', + 'margin/maxTransferable', + 'futures/transfer', + // https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi + 'capital/config/getall', // get networks for withdrawing USDT ERC20 vs USDT Omni + 'capital/deposit/address', + 'capital/deposit/hisrec', + 'capital/deposit/subAddress', + 'capital/deposit/subHisrec', + 'capital/withdraw/history', + 'sub-account/futures/account', + 'sub-account/futures/accountSummary', + 'sub-account/futures/positionRisk', + 'sub-account/margin/account', + 'sub-account/margin/accountSummary', + 'sub-account/status', + ], + 'post': [ + 'asset/dust', + 'account/disableFastWithdrawSwitch', + 'account/enableFastWithdrawSwitch', + 'capital/withdraw/apply', + 'margin/transfer', + 'margin/loan', + 'margin/repay', + 'margin/order', + 'sub-account/margin/enable', + 'sub-account/margin/enable', + 'sub-account/futures/enable', + 'userDataStream', + 'futures/transfer', + ], + 'put': [ + 'userDataStream', + ], + 'delete': [ + 'margin/order', + 'userDataStream', + ], + }, + 'wapi': { + 'post': [ + 'withdraw', + 'sub-account/transfer', + ], + 'get': [ + 'depositHistory', + 'withdrawHistory', + 'depositAddress', + 'accountStatus', + 'systemStatus', + 'apiTradingStatus', + 'userAssetDribbletLog', + 'tradeFee', + 'assetDetail', + 'sub-account/list', + 'sub-account/transfer/history', + 'sub-account/assets', + ], + }, + 'fapiPublic': { + 'get': [ + 'ping', + 'time', + 'exchangeInfo', + 'depth', + 'trades', + 'historicalTrades', + 'aggTrades', + 'klines', + 'premiumIndex', + 'ticker/24hr', + 'ticker/price', + 'ticker/bookTicker', + ], + 'put': [ 'listenKey' ], + 'post': [ 'listenKey' ], + 'delete': [ 'listenKey' ], + }, + 'fapiPrivate': { + 'get': [ + 'allOrders', + 'openOrders', + 'order', + 'account', + 'balance', + 'positionMargin/history', + 'positionRisk', + 'userTrades', + 'income', + ], + 'post': [ + 'positionMargin', + 'marginType', + 'order', + 'leverage', + ], + 'delete': [ + 'order', + 'allOpenOrders', + ], + }, + 'v3': { + 'get': [ + 'ticker/price', + 'ticker/bookTicker', + ], + }, + 'public': { + 'get': [ + 'ping', + 'time', + 'depth', + 'trades', + 'aggTrades', + 'historicalTrades', + 'klines', + 'ticker/24hr', + 'ticker/price', + 'ticker/bookTicker', + 'exchangeInfo', + ], + 'put': [ 'userDataStream' ], + 'post': [ 'userDataStream' ], + 'delete': [ 'userDataStream' ], + }, + 'private': { + 'get': [ + 'allOrderList', // oco + 'openOrderList', // oco + 'orderList', // oco + 'order', + 'openOrders', + 'allOrders', + 'account', + 'myTrades', + ], + 'post': [ + 'order/oco', + 'order', + 'order/test', + ], + 'delete': [ + 'orderList', // oco + 'order', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + }, + 'commonCurrencies': { + 'BCC': 'BCC', // kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848 + 'YOYO': 'YOYOW', + }, + // exchange-specific options + 'options': { + 'fetchTradesMethod': 'publicGetAggTrades', + 'fetchTickersMethod': 'publicGetTicker24hr', + 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel + 'defaultLimitOrderType': 'limit', // or 'limit_maker' + 'defaultType': 'spot', // 'spot', 'future' + 'hasAlreadyAuthenticatedSuccessfully': false, + 'warnOnFetchOpenOrdersWithoutSymbol': true, + 'recvWindow': 5 * 1000, // 5 sec, binance default + 'timeDifference': 0, // the difference between system clock and Binance clock + 'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation + 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision + 'newOrderRespType': { + 'market': 'FULL', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills + 'limit': 'RESULT', // we change it from 'ACK' by default to 'RESULT' + }, + }, + 'exceptions': { + 'API key does not exist': AuthenticationError, + 'Order would trigger immediately.': InvalidOrder, + 'Account has insufficient balance for requested action.': InsufficientFunds, + 'Rest API trading is not enabled.': ExchangeNotAvailable, + '-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."} + '-1013': InvalidOrder, // createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL + '-1021': InvalidNonce, // 'your time is ahead of server' + '-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."} + '-1100': InvalidOrder, // createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price' + '-1104': ExchangeError, // Not all sent parameters were read, read 8 parameters but was sent 9 + '-1128': ExchangeError, // {"code":-1128,"msg":"Combination of optional parameters invalid."} + '-2010': ExchangeError, // generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc... + '-2011': OrderNotFound, // cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER' + '-2013': OrderNotFound, // fetchOrder (1, 'BTC/USDT') -> 'Order does not exist' + '-2014': AuthenticationError, // { "code":-2014, "msg": "API-key format invalid." } + '-2015': AuthenticationError, // "Invalid API-key, IP, or permissions for action." + }, + }); + } + + nonce () { + return this.milliseconds () - this.options['timeDifference']; + } + + async fetchTime (params = {}) { + const type = this.safeString2 (this.options, 'fetchTime', 'defaultType', 'spot'); + const method = (type === 'spot') ? 'publicGetTime' : 'fapiPublicGetTime'; + const response = await this[method] (params); + return this.safeFloat (response, 'serverTime'); + } + + async loadTimeDifference () { + const serverTime = await this.fetchTime (); + const after = this.milliseconds (); + this.options['timeDifference'] = parseInt (after - serverTime); + return this.options['timeDifference']; + } + + async fetchMarkets (params = {}) { + const defaultType = this.safeString2 (this.options, 'fetchMarkets', 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const method = (type === 'spot') ? 'publicGetExchangeInfo' : 'fapiPublicGetExchangeInfo'; + const response = await this[method] (query); + // + // spot + // + // { + // "timezone":"UTC", + // "serverTime":1575416692969, + // "rateLimits":[ + // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, + // {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":100}, + // {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":200000} + // ], + // "exchangeFilters":[], + // "symbols":[ + // { + // "symbol":"ETHBTC", + // "status":"TRADING", + // "baseAsset":"ETH", + // "baseAssetPrecision":8, + // "quoteAsset":"BTC", + // "quotePrecision":8, + // "baseCommissionPrecision":8, + // "quoteCommissionPrecision":8, + // "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"], + // "icebergAllowed":true, + // "ocoAllowed":true, + // "quoteOrderQtyMarketAllowed":true, + // "isSpotTradingAllowed":true, + // "isMarginTradingAllowed":true, + // "filters":[ + // {"filterType":"PRICE_FILTER","minPrice":"0.00000100","maxPrice":"100000.00000000","tickSize":"0.00000100"}, + // {"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5}, + // {"filterType":"LOT_SIZE","minQty":"0.00100000","maxQty":"100000.00000000","stepSize":"0.00100000"}, + // {"filterType":"MIN_NOTIONAL","minNotional":"0.00010000","applyToMarket":true,"avgPriceMins":5}, + // {"filterType":"ICEBERG_PARTS","limit":10}, + // {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"63100.00000000","stepSize":"0.00000000"}, + // {"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5} + // ] + // }, + // ], + // } + // + // futures (fapi) + // + // { + // "timezone":"UTC", + // "serverTime":1575417244353, + // "rateLimits":[ + // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, + // {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200} + // ], + // "exchangeFilters":[], + // "symbols":[ + // { + // "symbol":"BTCUSDT", + // "status":"TRADING", + // "maintMarginPercent":"2.5000", + // "requiredMarginPercent":"5.0000", + // "baseAsset":"BTC", + // "quoteAsset":"USDT", + // "pricePrecision":2, + // "quantityPrecision":3, + // "baseAssetPrecision":8, + // "quotePrecision":8, + // "filters":[ + // {"minPrice":"0.01","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.01"}, + // {"stepSize":"0.001","filterType":"LOT_SIZE","maxQty":"1000","minQty":"0.001"}, + // {"stepSize":"0.001","filterType":"MARKET_LOT_SIZE","maxQty":"1000","minQty":"0.001"}, + // {"limit":200,"filterType":"MAX_NUM_ORDERS"}, + // {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"} + // ], + // "orderTypes":["LIMIT","MARKET","STOP"], + // "timeInForce":["GTC","IOC","FOK","GTX"] + // } + // ] + // } + // + if (this.options['adjustForTimeDifference']) { + await this.loadTimeDifference (); + } + const markets = this.safeValue (response, 'symbols'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const future = ('maintMarginPercent' in market); + const spot = !future; + const marketType = spot ? 'spot' : 'future'; + const id = this.safeString (market, 'symbol'); + const baseId = market['baseAsset']; + const quoteId = market['quoteAsset']; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const filters = this.indexBy (market['filters'], 'filterType'); + const precision = { + 'base': market['baseAssetPrecision'], + 'quote': market['quotePrecision'], + 'amount': market['baseAssetPrecision'], + 'price': market['quotePrecision'], + }; + const status = this.safeString (market, 'status'); + const active = (status === 'TRADING'); + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'type': marketType, + 'spot': spot, + 'future': future, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': -1 * Math.log10 (precision['amount']), + 'max': undefined, + }, + }, + }; + if ('PRICE_FILTER' in filters) { + const filter = filters['PRICE_FILTER']; + // PRICE_FILTER reports zero values for maxPrice + // since they updated filter types in November 2018 + // https://github.com/ccxt/ccxt/issues/4286 + // therefore limits['price']['max'] doesn't have any meaningful value except undefined + entry['limits']['price'] = { + 'min': this.safeFloat (filter, 'minPrice'), + 'max': undefined, + }; + const maxPrice = this.safeFloat (filter, 'maxPrice'); + if ((maxPrice !== undefined) && (maxPrice > 0)) { + entry['limits']['price']['max'] = maxPrice; + } + entry['precision']['price'] = this.precisionFromString (filter['tickSize']); + } + if ('LOT_SIZE' in filters) { + const filter = this.safeValue (filters, 'LOT_SIZE', {}); + const stepSize = this.safeString (filter, 'stepSize'); + entry['precision']['amount'] = this.precisionFromString (stepSize); + entry['limits']['amount'] = { + 'min': this.safeFloat (filter, 'minQty'), + 'max': this.safeFloat (filter, 'maxQty'), + }; + } + if ('MIN_NOTIONAL' in filters) { + entry['limits']['cost']['min'] = this.safeFloat (filters['MIN_NOTIONAL'], 'minNotional'); + } + result.push (entry); + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let precision = market['precision']['price']; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + precision = market['precision']['amount']; + } + cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (cost), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + const method = (type === 'spot') ? 'privateGetAccount' : 'fapiPrivateGetAccount'; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + // + // spot + // + // { + // makerCommission: 10, + // takerCommission: 10, + // buyerCommission: 0, + // sellerCommission: 0, + // canTrade: true, + // canWithdraw: true, + // canDeposit: true, + // updateTime: 1575357359602, + // accountType: "MARGIN", + // balances: [ + // { asset: "BTC", free: "0.00219821", locked: "0.00000000" }, + // ] + // } + // + // futures (fapi) + // + // { + // "feeTier":0, + // "canTrade":true, + // "canDeposit":true, + // "canWithdraw":true, + // "updateTime":0, + // "totalInitialMargin":"0.00000000", + // "totalMaintMargin":"0.00000000", + // "totalWalletBalance":"4.54000000", + // "totalUnrealizedProfit":"0.00000000", + // "totalMarginBalance":"4.54000000", + // "totalPositionInitialMargin":"0.00000000", + // "totalOpenOrderInitialMargin":"0.00000000", + // "maxWithdrawAmount":"4.54000000", + // "assets":[ + // { + // "asset":"USDT", + // "walletBalance":"4.54000000", + // "unrealizedProfit":"0.00000000", + // "marginBalance":"4.54000000", + // "maintMargin":"0.00000000", + // "initialMargin":"0.00000000", + // "positionInitialMargin":"0.00000000", + // "openOrderInitialMargin":"0.00000000", + // "maxWithdrawAmount":"4.54000000" + // } + // ], + // "positions":[ + // { + // "symbol":"BTCUSDT", + // "initialMargin":"0.00000", + // "maintMargin":"0.00000", + // "unrealizedProfit":"0.00000000", + // "positionInitialMargin":"0.00000", + // "openOrderInitialMargin":"0.00000" + // } + // ] + // } + // + const result = { 'info': response }; + if (type === 'spot') { + const balances = this.safeValue (response, 'balances', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'free'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + } else { + const balances = this.safeValue (response, 'assets', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'initialMargin'); + account['total'] = this.safeFloat (balance, 'marginBalance'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 5000, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book + } + const method = market['spot'] ? 'publicGetDepth' : 'fapiPublicGetDepth'; + const response = await this[method] (this.extend (request, params)); + const orderbook = this.parseOrderBook (response); + orderbook['nonce'] = this.safeInteger (response, 'lastUpdateId'); + return orderbook; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'closeTime'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highPrice'), + 'low': this.safeFloat (ticker, 'lowPrice'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': this.safeFloat (ticker, 'bidQty'), + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': this.safeFloat (ticker, 'askQty'), + 'vwap': this.safeFloat (ticker, 'weightedAvgPrice'), + 'open': this.safeFloat (ticker, 'openPrice'), + 'close': last, + 'last': last, + 'previousClose': this.safeFloat (ticker, 'prevClosePrice'), // previous day close + 'change': this.safeFloat (ticker, 'priceChange'), + 'percentage': this.safeFloat (ticker, 'priceChangePercent'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchStatus (params = {}) { + const response = await this.wapiGetSystemStatus (); + let status = this.safeValue (response, 'status'); + if (status !== undefined) { + status = (status === 0) ? 'ok' : 'maintenance'; + this.status = this.extend (this.status, { + 'status': status, + 'updated': this.milliseconds (), + }); + } + return this.status; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const method = market['spot'] ? 'publicGetTicker24hr' : 'fapiPublicGetTicker24hr'; + const response = await this[method] (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchBidsAsks (symbols = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchOpenOrders', 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const method = (type === 'spot') ? 'publicGetTickerBookTicker' : 'fapiPublicGetTickerBookTicker'; + const response = await this[method] (query); + return this.parseTickers (response, symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const method = this.options['fetchTickersMethod']; + const response = await this[method] (params); + return this.parseTickers (response, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0], + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'interval': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default == max == 500 + } + const method = market['spot'] ? 'publicGetKlines' : 'fapiPublicGetKlines'; + const response = await this[method] (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + if ('isDustTrade' in trade) { + return this.parseDustTrade (trade, market); + } + // + // aggregate trades + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list + // + // { + // "a": 26129, // Aggregate tradeId + // "p": "0.01633102", // Price + // "q": "4.70443515", // Quantity + // "f": 27781, // First tradeId + // "l": 27781, // Last tradeId + // "T": 1498793709153, // Timestamp + // "m": true, // Was the buyer the maker? + // "M": true // Was the trade the best price match? + // } + // + // recent public trades and old public trades + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#recent-trades-list + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#old-trade-lookup-market_data + // + // { + // "id": 28457, + // "price": "4.00000100", + // "qty": "12.00000000", + // "time": 1499865549590, + // "isBuyerMaker": true, + // "isBestMatch": true + // } + // + // private trades + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#account-trade-list-user_data + // + // { + // "symbol": "BNBBTC", + // "id": 28457, + // "orderId": 100234, + // "price": "4.00000100", + // "qty": "12.00000000", + // "commission": "10.10000000", + // "commissionAsset": "BNB", + // "time": 1499865549590, + // "isBuyer": true, + // "isMaker": false, + // "isBestMatch": true + // } + // + // futures trades + // https://binance-docs.github.io/apidocs/futures/en/#account-trade-list-user_data + // + // { + // "accountId": 20, + // "buyer": False, + // "commission": "-0.07819010", + // "commissionAsset": "USDT", + // "counterPartyId": 653, + // "id": 698759, + // "maker": False, + // "orderId": 25851813, + // "price": "7819.01", + // "qty": "0.002", + // "quoteQty": "0.01563", + // "realizedPnl": "-0.91539999", + // "side": "SELL", + // "symbol": "BTCUSDT", + // "time": 1569514978020 + // } + // + const timestamp = this.safeInteger2 (trade, 'T', 'time'); + const price = this.safeFloat2 (trade, 'p', 'price'); + const amount = this.safeFloat2 (trade, 'q', 'qty'); + const id = this.safeString2 (trade, 'a', 'id'); + let side = undefined; + const orderId = this.safeString (trade, 'orderId'); + if ('m' in trade) { + side = trade['m'] ? 'sell' : 'buy'; // this is reversed intentionally + } else if ('isBuyerMaker' in trade) { + side = trade['isBuyerMaker'] ? 'sell' : 'buy'; + } else if ('side' in trade) { + side = this.safeStringLower (trade, 'side'); + } else { + if ('isBuyer' in trade) { + side = trade['isBuyer'] ? 'buy' : 'sell'; // this is a true side + } + } + let fee = undefined; + if ('commission' in trade) { + fee = { + 'cost': this.safeFloat (trade, 'commission'), + 'currency': this.safeCurrencyCode (this.safeString (trade, 'commissionAsset')), + }; + } + let takerOrMaker = undefined; + if ('isMaker' in trade) { + takerOrMaker = trade['isMaker'] ? 'maker' : 'taker'; + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': price * amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + // 'fromId': 123, // ID to get aggregate trades from INCLUSIVE. + // 'startTime': 456, // Timestamp in ms to get aggregate trades from INCLUSIVE. + // 'endTime': 789, // Timestamp in ms to get aggregate trades until INCLUSIVE. + // 'limit': 500, // default = 500, maximum = 1000 + }; + if (this.options['fetchTradesMethod'] === 'publicGetAggTrades') { + if (since !== undefined) { + request['startTime'] = since; + // https://github.com/ccxt/ccxt/issues/6400 + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list + request['endTime'] = this.sum (since, 3600000); + } + } + if (limit !== undefined) { + request['limit'] = limit; // default = 500, maximum = 1000 + } + // + // Caveats: + // - default limit (500) applies only if no other parameters set, trades up + // to the maximum limit may be returned to satisfy other parameters + // - if both limit and time window is set and time window contains more + // trades than the limit then the last trades from the window are returned + // - 'tradeId' accepted and returned by this method is "aggregate" trade id + // which is different from actual trade id + // - setting both fromId and time window results in error + const method = this.safeValue (this.options, 'fetchTradesMethod', 'publicGetTrades'); + const response = await this[method] (this.extend (request, params)); + // + // aggregate trades + // + // [ + // { + // "a": 26129, // Aggregate tradeId + // "p": "0.01633102", // Price + // "q": "4.70443515", // Quantity + // "f": 27781, // First tradeId + // "l": 27781, // Last tradeId + // "T": 1498793709153, // Timestamp + // "m": true, // Was the buyer the maker? + // "M": true // Was the trade the best price match? + // } + // ] + // + // recent public trades and historical public trades + // + // [ + // { + // "id": 28457, + // "price": "4.00000100", + // "qty": "12.00000000", + // "time": 1499865549590, + // "isBuyerMaker": true, + // "isBestMatch": true + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'NEW': 'open', + 'PARTIALLY_FILLED': 'open', + 'FILLED': 'closed', + 'CANCELED': 'canceled', + 'PENDING_CANCEL': 'canceling', // currently unused + 'REJECTED': 'rejected', + 'EXPIRED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = undefined; + if ('time' in order) { + timestamp = this.safeInteger (order, 'time'); + } else if ('transactTime' in order) { + timestamp = this.safeInteger (order, 'transactTime'); + } + let price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'origQty'); + const filled = this.safeFloat (order, 'executedQty'); + let remaining = undefined; + // - Spot/Margin market: cummulativeQuoteQty + // - Futures market: cumQuote. + // Note this is not the actual cost, since Binance futures uses leverage to calculate margins. + let cost = this.safeFloat2 (order, 'cummulativeQuoteQty', 'cumQuote'); + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + if (this.options['parseOrderToPrecision']) { + remaining = parseFloat (this.amountToPrecision (symbol, remaining)); + } + remaining = Math.max (remaining, 0.0); + } + if (price !== undefined) { + if (cost === undefined) { + cost = price * filled; + } + } + } + const id = this.safeString (order, 'orderId'); + let type = this.safeStringLower (order, 'type'); + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + if (this.options['parseOrderToPrecision']) { + price = parseFloat (this.priceToPrecision (symbol, price)); + } + } + } + } + } else if (type === 'limit_maker') { + type = 'limit'; + } + const side = this.safeStringLower (order, 'side'); + let fee = undefined; + let trades = undefined; + const fills = this.safeValue (order, 'fills'); + if (fills !== undefined) { + trades = this.parseTrades (fills, market); + const numTrades = trades.length; + if (numTrades > 0) { + cost = trades[0]['cost']; + fee = { + 'cost': trades[0]['fee']['cost'], + 'currency': trades[0]['fee']['currency'], + }; + for (let i = 1; i < trades.length; i++) { + cost = this.sum (cost, trades[i]['cost']); + fee['cost'] = this.sum (fee['cost'], trades[i]['fee']['cost']); + } + } + } + let average = undefined; + if (cost !== undefined) { + if (filled) { + average = cost / filled; + if (this.options['parseOrderToPrecision']) { + average = parseFloat (this.priceToPrecision (symbol, average)); + } + } + if (this.options['parseOrderToPrecision']) { + cost = parseFloat (this.costToPrecision (symbol, cost)); + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the next 5 lines are added to support for testing orders + let method = market['spot'] ? 'privatePostOrder' : 'fapiPrivatePostOrder'; + if (market['spot']) { + const test = this.safeValue (params, 'test', false); + if (test) { + method += 'Test'; + params = this.omit (params, 'test'); + } + } + const uppercaseType = type.toUpperCase (); + const validOrderTypes = this.safeValue (market['info'], 'orderTypes'); + if (!this.inArray (uppercaseType, validOrderTypes)) { + throw new InvalidOrder (this.id + ' ' + type + ' is not a valid order type in ' + market['type'] + ' market ' + symbol); + } + const request = { + 'symbol': market['id'], + 'type': uppercaseType, + 'side': side.toUpperCase (), + }; + if (uppercaseType === 'MARKET') { + const quoteOrderQty = this.safeFloat (params, 'quoteOrderQty'); + if (quoteOrderQty !== undefined) { + request['quoteOrderQty'] = this.costToPrecision (symbol, quoteOrderQty); + } else if (price !== undefined) { + request['quoteOrderQty'] = this.costToPrecision (symbol, amount * price); + } else { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + } else { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + if (market['spot']) { + request['newOrderRespType'] = this.safeValue (this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills + } + let timeInForceIsRequired = false; + let priceIsRequired = false; + let stopPriceIsRequired = false; + if (uppercaseType === 'LIMIT') { + priceIsRequired = true; + timeInForceIsRequired = true; + } else if ((uppercaseType === 'STOP_LOSS') || (uppercaseType === 'TAKE_PROFIT')) { + stopPriceIsRequired = true; + } else if ((uppercaseType === 'STOP_LOSS_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) { + stopPriceIsRequired = true; + priceIsRequired = true; + timeInForceIsRequired = true; + } else if (uppercaseType === 'LIMIT_MAKER') { + priceIsRequired = true; + } else if (uppercaseType === 'STOP') { + stopPriceIsRequired = true; + priceIsRequired = true; + } + if (priceIsRequired) { + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order'); + } + request['price'] = this.priceToPrecision (symbol, price); + } + if (timeInForceIsRequired) { + request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel + } + if (stopPriceIsRequired) { + const stopPrice = this.safeFloat (params, 'stopPrice'); + if (stopPrice === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order'); + } else { + params = this.omit (params, 'stopPrice'); + request['stopPrice'] = this.priceToPrecision (symbol, stopPrice); + } + } + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['spot'] ? 'privateGetOrder' : 'fapiPrivateGetOrder'; + const request = { + 'symbol': market['id'], + }; + const origClientOrderId = this.safeValue (params, 'origClientOrderId'); + if (origClientOrderId !== undefined) { + request['origClientOrderId'] = origClientOrderId; + } else { + request['orderId'] = parseInt (id); + } + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const method = market['spot'] ? 'privateGetAllOrders' : 'fapiPrivateGetAllOrders'; + const response = await this[method] (this.extend (request, params)); + // + // Spot: + // [ + // { + // "symbol": "LTCBTC", + // "orderId": 1, + // "clientOrderId": "myOrder1", + // "price": "0.1", + // "origQty": "1.0", + // "executedQty": "0.0", + // "cummulativeQuoteQty": "0.0", + // "status": "NEW", + // "timeInForce": "GTC", + // "type": "LIMIT", + // "side": "BUY", + // "stopPrice": "0.0", + // "icebergQty": "0.0", + // "time": 1499827319559, + // "updateTime": 1499827319559, + // "isWorking": true + // } + // ] + // + // Futures: + // [ + // { + // "symbol": "BTCUSDT", + // "orderId": 1, + // "clientOrderId": "myOrder1", + // "price": "0.1", + // "origQty": "1.0", + // "executedQty": "1.0", + // "cumQuote": "10.0", + // "status": "NEW", + // "timeInForce": "GTC", + // "type": "LIMIT", + // "side": "BUY", + // "stopPrice": "0.0", + // "updateTime": 1499827319559 + // } + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let query = undefined; + let type = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + type = market['type']; + query = params; + } else if (this.options['warnOnFetchOpenOrdersWithoutSymbol']) { + const symbols = this.symbols; + const numSymbols = symbols.length; + const fetchOpenOrdersRateLimit = parseInt (numSymbols / 2); + throw new ExchangeError (this.id + ' fetchOpenOrders WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + fetchOpenOrdersRateLimit.toString () + ' seconds. Do not call this method frequently to avoid ban. Set ' + this.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = false to suppress this warning message.'); + } else { + const defaultType = this.safeString2 (this.options, 'fetchOpenOrders', 'defaultType', 'spot'); + type = this.safeString (params, 'type', defaultType); + query = this.omit (params, 'type'); + } + const method = (type === 'spot') ? 'privateGetOpenOrders' : 'fapiPrivateGetOpenOrders'; + const response = await this[method] (this.extend (request, query)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'orderId': parseInt (id), + // 'origClientOrderId': id, + }; + const method = market['spot'] ? 'privateDeleteOrder' : 'fapiPrivateDeleteOrder'; + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['spot'] ? 'privateGetMyTrades' : 'fapiPrivateGetUserTrades'; + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this[method] (this.extend (request, params)); + // + // spot trade + // [ + // { + // "symbol": "BNBBTC", + // "id": 28457, + // "orderId": 100234, + // "price": "4.00000100", + // "qty": "12.00000000", + // "commission": "10.10000000", + // "commissionAsset": "BNB", + // "time": 1499865549590, + // "isBuyer": true, + // "isMaker": false, + // "isBestMatch": true, + // } + // ] + // + // futures trade + // + // [ + // { + // "accountId": 20, + // "buyer": False, + // "commission": "-0.07819010", + // "commissionAsset": "USDT", + // "counterPartyId": 653, + // "id": 698759, + // "maker": False, + // "orderId": 25851813, + // "price": "7819.01", + // "qty": "0.002", + // "quoteQty": "0.01563", + // "realizedPnl": "-0.91539999", + // "side": "SELL", + // "symbol": "BTCUSDT", + // "time": 1569514978020 + // } + // ] + return this.parseTrades (response, market, since, limit); + } + + async fetchMyDustTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // + // Binance provides an opportunity to trade insignificant (i.e. non-tradable and non-withdrawable) + // token leftovers (of any asset) into `BNB` coin which in turn can be used to pay trading fees with it. + // The corresponding trades history is called the `Dust Log` and can be requested via the following end-point: + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/wapi-api.md#dustlog-user_data + // + await this.loadMarkets (); + const response = await this.wapiGetUserAssetDribbletLog (params); + // { success: true, + // results: { total: 1, + // rows: [ { transfered_total: "1.06468458", + // service_charge_total: "0.02172826", + // tran_id: 2701371634, + // logs: [ { tranId: 2701371634, + // serviceChargeAmount: "0.00012819", + // uid: "35103861", + // amount: "0.8012", + // operateTime: "2018-10-07 17:56:07", + // transferedAmount: "0.00628141", + // fromAsset: "ADA" } ], + // operate_time: "2018-10-07 17:56:06" } ] } } + const results = this.safeValue (response, 'results', {}); + const rows = this.safeValue (results, 'rows', []); + const data = []; + for (let i = 0; i < rows.length; i++) { + const logs = rows[i]['logs']; + for (let j = 0; j < logs.length; j++) { + logs[j]['isDustTrade'] = true; + data.push (logs[j]); + } + } + const trades = this.parseTrades (data, undefined, since, limit); + return this.filterBySinceLimit (trades, since, limit); + } + + parseDustTrade (trade, market = undefined) { + // { tranId: 2701371634, + // serviceChargeAmount: "0.00012819", + // uid: "35103861", + // amount: "0.8012", + // operateTime: "2018-10-07 17:56:07", + // transferedAmount: "0.00628141", + // fromAsset: "ADA" }, + const orderId = this.safeString (trade, 'tranId'); + const timestamp = this.parse8601 (this.safeString (trade, 'operateTime')); + const tradedCurrency = this.safeCurrencyCode (this.safeString (trade, 'fromAsset')); + const earnedCurrency = this.currency ('BNB')['code']; + const applicantSymbol = earnedCurrency + '/' + tradedCurrency; + let tradedCurrencyIsQuote = false; + if (applicantSymbol in this.markets) { + tradedCurrencyIsQuote = true; + } + // + // Warning + // Binance dust trade `fee` is already excluded from the `BNB` earning reported in the `Dust Log`. + // So the parser should either set the `fee.cost` to `0` or add it on top of the earned + // BNB `amount` (or `cost` depending on the trade `side`). The second of the above options + // is much more illustrative and therefore preferable. + // + const fee = { + 'currency': earnedCurrency, + 'cost': this.safeFloat (trade, 'serviceChargeAmount'), + }; + let symbol = undefined; + let amount = undefined; + let cost = undefined; + let side = undefined; + if (tradedCurrencyIsQuote) { + symbol = applicantSymbol; + amount = this.sum (this.safeFloat (trade, 'transferedAmount'), fee['cost']); + cost = this.safeFloat (trade, 'amount'); + side = 'buy'; + } else { + symbol = tradedCurrency + '/' + earnedCurrency; + amount = this.safeFloat (trade, 'amount'); + cost = this.sum (this.safeFloat (trade, 'transferedAmount'), fee['cost']); + side = 'sell'; + } + let price = undefined; + if (cost !== undefined) { + if (amount) { + price = cost / amount; + } + } + const id = undefined; + const type = undefined; + const takerOrMaker = undefined; + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'amount': amount, + 'price': price, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + const response = await this.wapiGetDepositHistory (this.extend (request, params)); + // + // { success: true, + // depositList: [ { insertTime: 1517425007000, + // amount: 0.3, + // address: "0x0123456789abcdef", + // addressTag: "", + // txId: "0x0123456789abcdef", + // asset: "ETH", + // status: 1 } ] } + // + return this.parseTransactions (response['depositList'], currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + const response = await this.wapiGetWithdrawHistory (this.extend (request, params)); + // + // { withdrawList: [ { amount: 14, + // address: "0x0123456789abcdef...", + // successTime: 1514489710000, + // transactionFee: 0.01, + // addressTag: "", + // txId: "0x0123456789abcdef...", + // id: "0123456789abcdef...", + // asset: "ETH", + // applyTime: 1514488724000, + // status: 6 }, + // { amount: 7600, + // address: "0x0123456789abcdef...", + // successTime: 1515323226000, + // transactionFee: 0.01, + // addressTag: "", + // txId: "0x0123456789abcdef...", + // id: "0123456789abcdef...", + // asset: "ICN", + // applyTime: 1515322539000, + // status: 6 } ], + // success: true } + // + return this.parseTransactions (response['withdrawList'], currency, since, limit); + } + + parseTransactionStatusByType (status, type = undefined) { + if (type === undefined) { + return status; + } + const statuses = { + 'deposit': { + '0': 'pending', + '1': 'ok', + }, + 'withdrawal': { + '0': 'pending', // Email Sent + '1': 'canceled', // Cancelled (different from 1 = ok in deposits) + '2': 'pending', // Awaiting Approval + '3': 'failed', // Rejected + '4': 'pending', // Processing + '5': 'failed', // Failure + '6': 'ok', // Completed + }, + }; + return (status in statuses[type]) ? statuses[type][status] : status; + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // { insertTime: 1517425007000, + // amount: 0.3, + // address: "0x0123456789abcdef", + // addressTag: "", + // txId: "0x0123456789abcdef", + // asset: "ETH", + // status: 1 } + // + // fetchWithdrawals + // + // { amount: 14, + // address: "0x0123456789abcdef...", + // successTime: 1514489710000, + // transactionFee: 0.01, + // addressTag: "", + // txId: "0x0123456789abcdef...", + // id: "0123456789abcdef...", + // asset: "ETH", + // applyTime: 1514488724000, + // status: 6 } + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + let tag = this.safeString (transaction, 'addressTag'); // set but unused + if (tag !== undefined) { + if (tag.length < 1) { + tag = undefined; + } + } + const txid = this.safeValue (transaction, 'txId'); + const currencyId = this.safeString (transaction, 'asset'); + const code = this.safeCurrencyCode (currencyId, currency); + let timestamp = undefined; + const insertTime = this.safeInteger (transaction, 'insertTime'); + const applyTime = this.safeInteger (transaction, 'applyTime'); + let type = this.safeString (transaction, 'type'); + if (type === undefined) { + if ((insertTime !== undefined) && (applyTime === undefined)) { + type = 'deposit'; + timestamp = insertTime; + } else if ((insertTime === undefined) && (applyTime !== undefined)) { + type = 'withdrawal'; + timestamp = applyTime; + } + } + const status = this.parseTransactionStatusByType (this.safeString (transaction, 'status'), type); + const amount = this.safeFloat (transaction, 'amount'); + const feeCost = this.safeFloat (transaction, 'transactionFee'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { 'currency': code, 'cost': feeCost }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.wapiGetDepositAddress (this.extend (request, params)); + const success = this.safeValue (response, 'success'); + if ((success === undefined) || !success) { + throw new InvalidAddress (this.id + ' fetchDepositAddress returned an empty response – create the deposit address in the user settings first.'); + } + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'addressTag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async fetchFundingFees (codes = undefined, params = {}) { + const response = await this.wapiGetAssetDetail (params); + // + // { + // "success": true, + // "assetDetail": { + // "CTR": { + // "minWithdrawAmount": "70.00000000", //min withdraw amount + // "depositStatus": false,//deposit status + // "withdrawFee": 35, // withdraw fee + // "withdrawStatus": true, //withdraw status + // "depositTip": "Delisted, Deposit Suspended" //reason + // }, + // "SKY": { + // "minWithdrawAmount": "0.02000000", + // "depositStatus": true, + // "withdrawFee": 0.01, + // "withdrawStatus": true + // } + // } + // } + // + const detail = this.safeValue (response, 'assetDetail', {}); + const ids = Object.keys (detail); + const withdrawFees = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + withdrawFees[code] = this.safeFloat (detail[id], 'withdrawFee'); + } + return { + 'withdraw': withdrawFees, + 'deposit': {}, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // name is optional, can be overrided via params + const name = address.slice (0, 20); + const request = { + 'asset': currency['id'], + 'address': address, + 'amount': parseFloat (amount), + 'name': name, // name is optional, can be overrided via params + // https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi + // issue sapiGetCapitalConfigGetall () to get networks for withdrawing USDT ERC20 vs USDT Omni + // 'network': 'ETH', // 'BTC', 'TRX', etc, optional + }; + if (tag !== undefined) { + request['addressTag'] = tag; + } + const response = await this.wapiPostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response, 'id'), + }; + } + + parseTradingFee (fee, market = undefined) { + // + // { + // "symbol": "ADABNB", + // "maker": 0.9000, + // "taker": 1.0000 + // } + // + const marketId = this.safeString (fee, 'symbol'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + const market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + return { + 'info': fee, + 'symbol': symbol, + 'maker': this.safeFloat (fee, 'maker'), + 'taker': this.safeFloat (fee, 'taker'), + }; + } + + async fetchTradingFee (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.wapiGetTradeFee (this.extend (request, params)); + // + // { + // "tradeFee": [ + // { + // "symbol": "ADABNB", + // "maker": 0.9000, + // "taker": 1.0000 + // } + // ], + // "success": true + // } + // + const tradeFee = this.safeValue (response, 'tradeFee', []); + const first = this.safeValue (tradeFee, 0, {}); + return this.parseTradingFee (first); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.wapiGetTradeFee (params); + // + // { + // "tradeFee": [ + // { + // "symbol": "ADABNB", + // "maker": 0.9000, + // "taker": 1.0000 + // } + // ], + // "success": true + // } + // + const tradeFee = this.safeValue (response, 'tradeFee', []); + const result = {}; + for (let i = 0; i < tradeFee.length; i++) { + const fee = this.parseTradingFee (tradeFee[i]); + const symbol = fee['symbol']; + result[symbol] = fee; + } + return result; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + url += '/' + path; + if (api === 'wapi') { + url += '.html'; + } + const userDataStream = ((path === 'userDataStream') || (path === 'listenKey')); + if (path === 'historicalTrades') { + if (this.apiKey) { + headers = { + 'X-MBX-APIKEY': this.apiKey, + }; + } else { + throw new AuthenticationError (this.id + ' historicalTrades endpoint requires `apiKey` credential'); + } + } else if (userDataStream) { + if (this.apiKey) { + // v1 special case for userDataStream + body = this.urlencode (params); + headers = { + 'X-MBX-APIKEY': this.apiKey, + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } else { + throw new AuthenticationError (this.id + ' userDataStream endpoint requires `apiKey` credential'); + } + } + if ((api === 'private') || (api === 'sapi') || (api === 'wapi' && path !== 'systemStatus') || (api === 'fapiPrivate')) { + this.checkRequiredCredentials (); + let query = undefined; + if ((api === 'sapi') && (path === 'asset/dust')) { + query = this.urlencodeWithArrayRepeat (this.extend ({ + 'timestamp': this.nonce (), + 'recvWindow': this.options['recvWindow'], + }, params)); + } else { + query = this.urlencode (this.extend ({ + 'timestamp': this.nonce (), + 'recvWindow': this.options['recvWindow'], + }, params)); + } + const signature = this.hmac (this.encode (query), this.encode (this.secret)); + query += '&' + 'signature=' + signature; + headers = { + 'X-MBX-APIKEY': this.apiKey, + }; + if ((method === 'GET') || (method === 'DELETE') || (api === 'wapi')) { + url += '?' + query; + } else { + body = query; + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + } else { + // userDataStream endpoints are public, but POST, PUT, DELETE + // therefore they don't accept URL query arguments + // https://github.com/ccxt/ccxt/issues/5224 + if (!userDataStream) { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 418) || (code === 429)) { + throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); + } + // error response in a form: { "code": -1013, "msg": "Invalid quantity." } + // following block cointains legacy checks against message patterns in "msg" property + // will switch "code" checks eventually, when we know all of them + if (code >= 400) { + if (body.indexOf ('Price * QTY is zero or less') >= 0) { + throw new InvalidOrder (this.id + ' order cost = amount * price is zero or less ' + body); + } + if (body.indexOf ('LOT_SIZE') >= 0) { + throw new InvalidOrder (this.id + ' order amount should be evenly divisible by lot size ' + body); + } + if (body.indexOf ('PRICE_FILTER') >= 0) { + throw new InvalidOrder (this.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use this.priceToPrecision (symbol, amount) ' + body); + } + } + if (body.length > 0) { + if (body[0] === '{') { + // check success value for wapi endpoints + // response in format {'msg': 'The coin does not exist.', 'success': true/false} + const success = this.safeValue (response, 'success', true); + if (!success) { + const message = this.safeString (response, 'msg'); + let parsedMessage = undefined; + if (message !== undefined) { + try { + parsedMessage = JSON.parse (message); + } catch (e) { + // do nothing + parsedMessage = undefined; + } + if (parsedMessage !== undefined) { + response = parsedMessage; + } + } + } + const message = this.safeString (response, 'msg'); + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions, message, this.id + ' ' + message); + } + // checks against error codes + const error = this.safeString (response, 'code'); + if (error !== undefined) { + // a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} + // despite that their message is very confusing, it is raised by Binance + // on a temporary ban (the API key is valid, but disabled for a while) + if ((error === '-2015') && this.options['hasAlreadyAuthenticatedSuccessfully']) { + throw new DDoSProtection (this.id + ' temporary banned: ' + body); + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + throw new ExchangeError (feedback); + } + if (!success) { + throw new ExchangeError (this.id + ' ' + body); + } + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + // a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} + if ((api === 'private') || (api === 'wapi')) { + this.options['hasAlreadyAuthenticatedSuccessfully'] = true; + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],26:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const binance = require ('./binance.js'); + +// --------------------------------------------------------------------------- + +module.exports = class binanceje extends binance { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'binanceje', + 'name': 'Binance Jersey', + 'countries': [ 'JE' ], // Jersey + 'certified': false, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/54874009-d526eb00-4df3-11e9-928c-ce6a2b914cd1.jpg', + 'api': { + 'web': 'https://www.binance.je', + 'wapi': 'https://api.binance.je/wapi/v3', + 'public': 'https://api.binance.je/api/v1', + 'private': 'https://api.binance.je/api/v3', + 'v3': 'https://api.binance.je/api/v3', + 'v1': 'https://api.binance.je/api/v1', + }, + 'www': 'https://www.binance.je', + 'referral': 'https://www.binance.je/?ref=35047921', + 'doc': 'https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md', + 'fees': 'https://www.binance.je/fees.html', + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.0005, + 'maker': 0.0005, + }, + // should be deleted, these are outdated and inaccurate + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.0005, + 'ETH': 0.01, + }, + 'deposit': {}, + }, + }, + }); + } +}; + + +},{"./binance.js":25}],27:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const binance = require ('./binance.js'); + +// --------------------------------------------------------------------------- + +module.exports = class binanceus extends binance { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'binanceus', + 'name': 'Binance US', + 'countries': [ 'US' ], // US + 'certified': false, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/65177307-217b7c80-da5f-11e9-876e-0b748ba0a358.jpg', + 'api': { + 'web': 'https://www.binance.us', + 'wapi': 'https://api.binance.us/wapi/v3', + 'public': 'https://api.binance.us/api/v1', + 'private': 'https://api.binance.us/api/v3', + 'v3': 'https://api.binance.us/api/v3', + 'v1': 'https://api.binance.us/api/v1', + }, + 'www': 'https://www.binance.us', + 'referral': 'https://www.binance.us/?ref=35005074', + 'doc': 'https://github.com/binance-us/binance-official-api-docs', + 'fees': 'https://www.binance.us/en/fee/schedule', + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.0, // 0.1% trading fee, zero fees for all trading pairs before November 1 + 'maker': 0.0, // 0.1% trading fee, zero fees for all trading pairs before November 1 + }, + }, + }); + } +}; + + +},{"./binance.js":25}],28:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bit2c extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bit2c', + 'name': 'Bit2C', + 'countries': [ 'IL' ], // Israel + 'rateLimit': 3000, + 'has': { + 'CORS': false, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766119-3593220e-5ece-11e7-8b3a-5a041f6bcc3f.jpg', + 'api': 'https://bit2c.co.il', + 'www': 'https://www.bit2c.co.il', + 'referral': 'https://bit2c.co.il/Aff/63bfed10-e359-420c-ab5a-ad368dab0baf', + 'doc': [ + 'https://www.bit2c.co.il/home/api', + 'https://github.com/OferE/bit2c', + ], + }, + 'api': { + 'public': { + 'get': [ + 'Exchanges/{pair}/Ticker', + 'Exchanges/{pair}/orderbook', + 'Exchanges/{pair}/trades', + 'Exchanges/{pair}/lasttrades', + ], + }, + 'private': { + 'post': [ + 'Merchant/CreateCheckout', + 'Order/AddCoinFundsRequest', + 'Order/AddFund', + 'Order/AddOrder', + 'Order/AddOrderMarketPriceBuy', + 'Order/AddOrderMarketPriceSell', + 'Order/CancelOrder', + 'Order/AddCoinFundsRequest', + 'Order/AddStopOrder', + 'Payment/GetMyId', + 'Payment/Send', + 'Payment/Pay', + ], + 'get': [ + 'Account/Balance', + 'Account/Balance/v2', + 'Order/MyOrders', + 'Order/GetById', + 'Order/AccountHistory', + 'Order/OrderHistory', + ], + }, + }, + 'markets': { + 'BTC/NIS': { 'id': 'BtcNis', 'symbol': 'BTC/NIS', 'base': 'BTC', 'quote': 'NIS', 'baseId': 'Btc', 'quoteId': 'Nis' }, + 'ETH/NIS': { 'id': 'EthNis', 'symbol': 'ETH/NIS', 'base': 'ETH', 'quote': 'NIS', 'baseId': 'Eth', 'quoteId': 'Nis' }, + 'BCH/NIS': { 'id': 'BchabcNis', 'symbol': 'BCH/NIS', 'base': 'BCH', 'quote': 'NIS', 'baseId': 'Bchabc', 'quoteId': 'Nis' }, + 'LTC/NIS': { 'id': 'LtcNis', 'symbol': 'LTC/NIS', 'base': 'LTC', 'quote': 'NIS', 'baseId': 'Ltc', 'quoteId': 'Nis' }, + 'ETC/NIS': { 'id': 'EtcNis', 'symbol': 'ETC/NIS', 'base': 'ETC', 'quote': 'NIS', 'baseId': 'Etc', 'quoteId': 'Nis' }, + 'BTG/NIS': { 'id': 'BtgNis', 'symbol': 'BTG/NIS', 'base': 'BTG', 'quote': 'NIS', 'baseId': 'Btg', 'quoteId': 'Nis' }, + 'BSV/NIS': { 'id': 'BchsvNis', 'symbol': 'BSV/NIS', 'base': 'BSV', 'quote': 'NIS', 'baseId': 'Bchsv', 'quoteId': 'Nis' }, + 'GRIN/NIS': { 'id': 'GrinNis', 'symbol': 'GRIN/NIS', 'base': 'GRIN', 'quote': 'NIS', 'baseId': 'Grin', 'quoteId': 'Nis' }, + }, + 'fees': { + 'trading': { + 'maker': 0.5 / 100, + 'taker': 0.5 / 100, + }, + }, + 'options': { + 'fetchTradesMethod': 'public_get_exchanges_pair_lasttrades', + }, + 'exceptions': { + // { "error" : "Please provide valid APIkey" } + // { "error" : "Please provide valid nonce in Request UInt64.TryParse failed for nonce :" } + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balance = await this.privateGetAccountBalanceV2 (params); + // + // { + // "AVAILABLE_NIS": 0.0, + // "NIS": 0.0, + // "LOCKED_NIS": 0.0, + // "AVAILABLE_BTC": 0.0, + // "BTC": 0.0, + // "LOCKED_BTC": 0.0, + // "AVAILABLE_ETH": 0.0, + // "ETH": 0.0, + // "LOCKED_ETH": 0.0, + // "AVAILABLE_BCHSV": 0.0, + // "BCHSV": 0.0, + // "LOCKED_BCHSV": 0.0, + // "AVAILABLE_BCHABC": 0.0, + // "BCHABC": 0.0, + // "LOCKED_BCHABC": 0.0, + // "AVAILABLE_LTC": 0.0, + // "LTC": 0.0, + // "LOCKED_LTC": 0.0, + // "AVAILABLE_ETC": 0.0, + // "ETC": 0.0, + // "LOCKED_ETC": 0.0, + // "AVAILABLE_BTG": 0.0, + // "BTG": 0.0, + // "LOCKED_BTG": 0.0, + // "AVAILABLE_GRIN": 0.0, + // "GRIN": 0.0, + // "LOCKED_GRIN": 0.0, + // "Fees": { + // "BtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "EthNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "BchabcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "LtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "EtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "BtgNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "LtcBtc": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "BchsvNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "GrinNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 } + // } + // } + // + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const account = this.account (); + const currencyId = this.currencyId (code); + const uppercase = currencyId.toUpperCase (); + if (uppercase in balance) { + account['free'] = this.safeFloat (balance, 'AVAILABLE_' + uppercase); + account['total'] = this.safeFloat (balance, uppercase); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const orderbook = await this.publicGetExchangesPairOrderbook (this.extend (request, params)); + return this.parseOrderBook (orderbook); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const ticker = await this.publicGetExchangesPairTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const averagePrice = this.safeFloat (ticker, 'av'); + const baseVolume = this.safeFloat (ticker, 'a'); + let quoteVolume = undefined; + if (baseVolume !== undefined && averagePrice !== undefined) { + quoteVolume = baseVolume * averagePrice; + } + const last = this.safeFloat (ticker, 'll'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'h'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'l'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': averagePrice, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = this.options['fetchTradesMethod']; + const request = { + 'pair': market['id'], + }; + const response = await this[method] (this.extend (request, params)); + if (typeof response === 'string') { + throw new ExchangeError (response); + } + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let method = 'privatePostOrderAddOrder'; + const request = { + 'Amount': amount, + 'Pair': this.marketId (symbol), + }; + if (type === 'market') { + method += 'MarketPrice' + this.capitalize (side); + } else { + request['Price'] = price; + request['Total'] = amount * price; + request['IsBid'] = (side === 'buy'); + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['NewOrder']['id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostOrderCancelOrder (this.extend (request, params)); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.privateGetOrderMyOrders (this.extend (request, params)); + const orders = this.safeValue (response, market['id'], {}); + const asks = this.safeValue (orders, 'ask', []); + const bids = this.safeValue (orders, 'bid', []); + return this.parseOrders (this.arrayConcat (asks, bids), market, since, limit); + } + + parseOrder (order, market = undefined) { + const timestamp = this.safeInteger (order, 'created'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeValue (order, 'type'); + if (side === 0) { + side = 'buy'; + } else if (side === 1) { + side = 'sell'; + } + const id = this.safeString (order, 'id'); + const status = this.safeString (order, 'status'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': cost, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (limit !== undefined) { + request['take'] = limit; + } + request['take'] = limit; + if (since !== undefined) { + request['toTime'] = this.ymd (this.milliseconds (), '.'); + request['fromTime'] = this.ymd (since, '.'); + } + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privateGetOrderOrderHistory (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseTrade (trade, market = undefined) { + let timestamp = undefined; + let id = undefined; + let price = undefined; + let amount = undefined; + let orderId = undefined; + let feeCost = undefined; + let side = undefined; + const reference = this.safeString (trade, 'reference'); + if (reference !== undefined) { + timestamp = this.safeTimestamp (trade, 'ticks'); + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'firstAmount'); + const reference_parts = reference.split ('|'); // reference contains 'pair|orderId|tradeId' + if (market === undefined) { + const marketId = this.safeString (trade, 'pair'); + if (marketId in this.markets_by_id[marketId]) { + market = this.markets_by_id[marketId]; + } else if (reference_parts[0] in this.markets_by_id) { + market = this.markets_by_id[reference_parts[0]]; + } + } + orderId = reference_parts[1]; + id = reference_parts[2]; + side = this.safeInteger (trade, 'action'); + if (side === 0) { + side = 'buy'; + } else if (side === 1) { + side = 'sell'; + } + feeCost = this.safeFloat (trade, 'feeAmount'); + } else { + timestamp = this.safeTimestamp (trade, 'date'); + id = this.safeString (trade, 'tid'); + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'amount'); + side = this.safeValue (trade, 'isBid'); + if (side !== undefined) { + if (side) { + side = 'buy'; + } else { + side = 'sell'; + } + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': price * amount, + 'fee': { + 'cost': feeCost, + 'currency': 'NIS', + 'rate': undefined, + }, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + if (api === 'public') { + url += '.json'; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const query = this.extend ({ + 'nonce': nonce, + }, params); + const auth = this.urlencode (query); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + auth; + } + } else { + body = auth; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512', 'base64'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'key': this.apiKey, + 'sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + + +},{"./base/Exchange":7,"./base/errors":9}],29:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, InvalidNonce, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitbank extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitbank', + 'name': 'bitbank', + 'countries': [ 'JP' ], + 'version': 'v1', + 'has': { + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '4h': '4hour', + '8h': '8hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/37808081-b87f2d9c-2e59-11e8-894d-c1900b7584fe.jpg', + 'api': { + 'public': 'https://public.bitbank.cc', + 'private': 'https://api.bitbank.cc', + }, + 'www': 'https://bitbank.cc/', + 'doc': 'https://docs.bitbank.cc/', + 'fees': 'https://bitbank.cc/docs/fees/', + }, + 'api': { + 'public': { + 'get': [ + '{pair}/ticker', + '{pair}/depth', + '{pair}/transactions', + '{pair}/transactions/{yyyymmdd}', + '{pair}/candlestick/{candletype}/{yyyymmdd}', + ], + }, + 'private': { + 'get': [ + 'user/assets', + 'user/spot/order', + 'user/spot/active_orders', + 'user/spot/trade_history', + 'user/withdrawal_account', + ], + 'post': [ + 'user/spot/order', + 'user/spot/cancel_order', + 'user/spot/cancel_orders', + 'user/spot/orders_info', + 'user/request_withdrawal', + ], + }, + }, + 'markets': { + 'BCH/BTC': { 'id': 'bcc_btc', 'symbol': 'BCH/BTC', 'base': 'BCH', 'quote': 'BTC', 'baseId': 'bcc', 'quoteId': 'btc' }, + 'BCH/JPY': { 'id': 'bcc_jpy', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bcc', 'quoteId': 'jpy' }, + 'MONA/BTC': { 'id': 'mona_btc', 'symbol': 'MONA/BTC', 'base': 'MONA', 'quote': 'BTC', 'baseId': 'mona', 'quoteId': 'btc' }, + 'MONA/JPY': { 'id': 'mona_jpy', 'symbol': 'MONA/JPY', 'base': 'MONA', 'quote': 'JPY', 'baseId': 'mona', 'quoteId': 'jpy' }, + 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' }, + 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, + 'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' }, + 'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' }, + }, + 'fees': { + 'trading': { + 'maker': -0.02 / 100, + 'taker': 0.12 / 100, + }, + 'funding': { + 'withdraw': { + // 'JPY': (amount > 30000) ? 756 : 540, + 'BTC': 0.001, + 'LTC': 0.001, + 'XRP': 0.15, + 'ETH': 0.0005, + 'MONA': 0.001, + 'BCC': 0.001, + }, + }, + }, + 'precision': { + 'price': 8, + 'amount': 8, + }, + 'exceptions': { + '20001': AuthenticationError, + '20002': AuthenticationError, + '20003': AuthenticationError, + '20005': AuthenticationError, + '20004': InvalidNonce, + '40020': InvalidOrder, + '40021': InvalidOrder, + '40025': ExchangeError, + '40013': OrderNotFound, + '40014': OrderNotFound, + '50008': PermissionDenied, + '50009': OrderNotFound, + '50010': OrderNotFound, + '60001': InsufficientFunds, + '60005': InvalidOrder, + }, + }); + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTicker (this.extend (request, params)); + return this.parseTicker (response['data'], market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this.publicGetPairDepth (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data', {}); + const timestamp = this.safeInteger (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'executed_at'); + let symbol = undefined; + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const id = this.safeString2 (trade, 'transaction_id', 'trade_id'); + const takerOrMaker = this.safeString (trade, 'maker_taker'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee_amount_quote'); + if (feeCost !== undefined) { + fee = { + 'currency': market['quote'], + 'cost': feeCurrency, + }; + } + const orderId = this.safeString (trade, 'order_id'); + const type = this.safeString (trade, 'type'); + const side = this.safeString (trade, 'side'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTransactions (this.extend (request, params)); + return this.parseTrades (response['data']['transactions'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + ohlcv[5], + parseFloat (ohlcv[0]), + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let date = this.milliseconds (); + date = this.ymd (date); + date = date.split ('-'); + const request = { + 'pair': market['id'], + 'candletype': this.timeframes[timeframe], + 'yyyymmdd': date.join (''), + }; + const response = await this.publicGetPairCandlestickCandletypeYyyymmdd (this.extend (request, params)); + const ohlcv = this.safeValue (response['data']['candlestick'][0], 'ohlcv'); + return this.parseOHLCVs (ohlcv, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetUserAssets (params); + const result = { 'info': response }; + const balances = response['data']['assets']; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'free_amount'), + 'used': this.safeFloat (balance, 'locked_amount'), + 'total': this.safeFloat (balance, 'onhand_amount'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'UNFILLED': 'open', + 'PARTIALLY_FILLED': 'open', + 'FULLY_FILLED': 'closed', + 'CANCELED_UNFILLED': 'canceled', + 'CANCELED_PARTIALLY_FILLED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'order_id'); + const marketId = this.safeString (order, 'pair'); + let symbol = undefined; + if (marketId && !market && (marketId in this.marketsById)) { + market = this.marketsById[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'ordered_at'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'start_amount'); + const filled = this.safeFloat (order, 'executed_amount'); + const remaining = this.safeFloat (order, 'remaining_amount'); + const average = this.safeFloat (order, 'average_price'); + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = filled * average; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const type = this.safeStringLower (order, 'type'); + const side = this.safeStringLower (order, 'side'); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder requires a price argument for both market and limit orders'); + } + const request = { + 'pair': market['id'], + 'amount': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + 'side': side, + 'type': type, + }; + const response = await this.privatePostUserSpotOrder (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'pair': market['id'], + }; + const response = await this.privatePostUserSpotCancelOrder (this.extend (request, params)); + return response['data']; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'pair': market['id'], + }; + const response = await this.privateGetUserSpotOrder (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privateGetUserSpotActiveOrders (this.extend (request, params)); + return this.parseOrders (response['data']['orders'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = {}; + if (market !== undefined) { + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['count'] = limit; + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privateGetUserSpotTradeHistory (this.extend (request, params)); + return this.parseTrades (response['data']['trades'], market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privateGetUserWithdrawalAccount (this.extend (request, params)); + // Not sure about this if there could be more than one account... + const accounts = response['data']['accounts']; + const address = this.safeString (accounts[0], 'address'); + return { + 'currency': currency, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + if (!('uuid' in params)) { + throw new ExchangeError (this.id + ' uuid is required for withdrawal'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + 'amount': amount, + }; + const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params)); + const txid = this.safeString (response['data'], 'txid'); + return { + 'info': response, + 'id': txid, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + '/'; + if (api === 'public') { + url += this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = nonce; + url += this.version + '/' + this.implodeParams (path, params); + if (method === 'POST') { + body = this.json (query); + auth += body; + } else { + auth += '/' + this.version + '/' + path; + if (Object.keys (query).length) { + query = this.urlencode (query); + url += '?' + query; + auth += '?' + query; + } + } + headers = { + 'Content-Type': 'application/json', + 'ACCESS-KEY': this.apiKey, + 'ACCESS-NONCE': nonce, + 'ACCESS-SIGNATURE': this.hmac (this.encode (auth), this.encode (this.secret)), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const success = this.safeInteger (response, 'success'); + const data = this.safeValue (response, 'data'); + if (!success || !data) { + const errorMessages = { + '10000': 'URL does not exist', + '10001': 'A system error occurred. Please contact support', + '10002': 'Invalid JSON format. Please check the contents of transmission', + '10003': 'A system error occurred. Please contact support', + '10005': 'A timeout error occurred. Please wait for a while and try again', + '20001': 'API authentication failed', + '20002': 'Illegal API key', + '20003': 'API key does not exist', + '20004': 'API Nonce does not exist', + '20005': 'API signature does not exist', + '20011': 'Two-step verification failed', + '20014': 'SMS authentication failed', + '30001': 'Please specify the order quantity', + '30006': 'Please specify the order ID', + '30007': 'Please specify the order ID array', + '30009': 'Please specify the stock', + '30012': 'Please specify the order price', + '30013': 'Trade Please specify either', + '30015': 'Please specify the order type', + '30016': 'Please specify asset name', + '30019': 'Please specify uuid', + '30039': 'Please specify the amount to be withdrawn', + '40001': 'The order quantity is invalid', + '40006': 'Count value is invalid', + '40007': 'End time is invalid', + '40008': 'end_id Value is invalid', + '40009': 'The from_id value is invalid', + '40013': 'The order ID is invalid', + '40014': 'The order ID array is invalid', + '40015': 'Too many specified orders', + '40017': 'Incorrect issue name', + '40020': 'The order price is invalid', + '40021': 'The trading classification is invalid', + '40022': 'Start date is invalid', + '40024': 'The order type is invalid', + '40025': 'Incorrect asset name', + '40028': 'uuid is invalid', + '40048': 'The amount of withdrawal is illegal', + '50003': 'Currently, this account is in a state where you can not perform the operation you specified. Please contact support', + '50004': 'Currently, this account is temporarily registered. Please try again after registering your account', + '50005': 'Currently, this account is locked. Please contact support', + '50006': 'Currently, this account is locked. Please contact support', + '50008': 'User identification has not been completed', + '50009': 'Your order does not exist', + '50010': 'Can not cancel specified order', + '50011': 'API not found', + '60001': 'The number of possessions is insufficient', + '60002': 'It exceeds the quantity upper limit of the tender buying order', + '60003': 'The specified quantity exceeds the limit', + '60004': 'The specified quantity is below the threshold', + '60005': 'The specified price is above the limit', + '60006': 'The specified price is below the lower limit', + '70001': 'A system error occurred. Please contact support', + '70002': 'A system error occurred. Please contact support', + '70003': 'A system error occurred. Please contact support', + '70004': 'We are unable to accept orders as the transaction is currently suspended', + '70005': 'Order can not be accepted because purchase order is currently suspended', + '70006': 'We can not accept orders because we are currently unsubscribed ', + '70009': 'We are currently temporarily restricting orders to be carried out. Please use the limit order.', + '70010': 'We are temporarily raising the minimum order quantity as the system load is now rising.', + }; + const errorClasses = this.exceptions; + const code = this.safeString (data, 'code'); + const message = this.safeString (errorMessages, code, 'Error'); + const ErrorClass = this.safeValue (errorClasses, code); + if (ErrorClass !== undefined) { + throw new ErrorClass (message); + } else { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],30:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitbay extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitbay', + 'name': 'BitBay', + 'countries': [ 'MT', 'EU' ], // Malta + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'withdraw': true, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + }, + 'urls': { + 'referral': 'https://auth.bitbay.net/ref/jHlbB4mIkdS1', + 'logo': 'https://user-images.githubusercontent.com/1294454/27766132-978a7bd8-5ece-11e7-9540-bc96d1e9bbb8.jpg', + 'www': 'https://bitbay.net', + 'api': { + 'public': 'https://bitbay.net/API/Public', + 'private': 'https://bitbay.net/API/Trading/tradingApi.php', + 'v1_01Public': 'https://api.bitbay.net/rest', + 'v1_01Private': 'https://api.bitbay.net/rest', + }, + 'doc': [ + 'https://bitbay.net/public-api', + 'https://bitbay.net/en/private-api', + 'https://bitbay.net/account/tab-api', + 'https://github.com/BitBayNet/API', + 'https://docs.bitbay.net/v1.0.1-en/reference', + ], + 'fees': 'https://bitbay.net/en/fees', + }, + 'api': { + 'public': { + 'get': [ + '{id}/all', + '{id}/market', + '{id}/orderbook', + '{id}/ticker', + '{id}/trades', + ], + }, + 'private': { + 'post': [ + 'info', + 'trade', + 'cancel', + 'orderbook', + 'orders', + 'transfer', + 'withdraw', + 'history', + 'transactions', + ], + }, + 'v1_01Public': { + 'get': [ + 'trading/ticker', + 'trading/ticker/{symbol}', + 'trading/stats', + 'trading/orderbook/{symbol}', + 'trading/transactions/{symbol}', + 'trading/candle/history/{symbol}/{resolution}', + ], + }, + 'v1_01Private': { + 'get': [ + 'payments/withdrawal/{detailId}', + 'payments/deposit/{detailId}', + 'trading/offer', + 'trading/config/{symbol}', + 'trading/history/transactions', + 'balances/BITBAY/history', + 'balances/BITBAY/balance', + 'fiat_cantor/rate/{baseId}/{quoteId}', + 'fiat_cantor/history', + ], + 'post': [ + 'trading/offer/{symbol}', + 'trading/config/{symbol}', + 'balances/BITBAY/balance', + 'balances/BITBAY/balance/transfer/{source}/{destination}', + 'fiat_cantor/exchange', + ], + 'delete': [ + 'trading/offer/{symbol}/{id}/{side}/{price}', + ], + 'put': [ + 'balances/BITBAY/balance/{id}', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.30 / 100, + 'taker': 0.43 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.0009, + 'LTC': 0.005, + 'ETH': 0.00126, + 'LSK': 0.2, + 'BCH': 0.0006, + 'GAME': 0.005, + 'DASH': 0.001, + 'BTG': 0.0008, + 'PLN': 4, + 'EUR': 1.5, + }, + }, + }, + 'exceptions': { + '400': ExchangeError, // At least one parameter wasn't set + '401': InvalidOrder, // Invalid order type + '402': InvalidOrder, // No orders with specified currencies + '403': InvalidOrder, // Invalid payment currency name + '404': InvalidOrder, // Error. Wrong transaction type + '405': InvalidOrder, // Order with this id doesn't exist + '406': InsufficientFunds, // No enough money or crypto + // code 407 not specified are not specified in their docs + '408': InvalidOrder, // Invalid currency name + '501': AuthenticationError, // Invalid public key + '502': AuthenticationError, // Invalid sign + '503': InvalidNonce, // Invalid moment parameter. Request time doesn't match current server time + '504': ExchangeError, // Invalid method + '505': AuthenticationError, // Key has no permission for this action + '506': AuthenticationError, // Account locked. Please contact with customer service + // codes 507 and 508 are not specified in their docs + '509': ExchangeError, // The BIC/SWIFT is required for this currency + '510': ExchangeError, // Invalid market name + 'FUNDS_NOT_SUFFICIENT': InsufficientFunds, + 'OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS': InvalidOrder, + 'OFFER_NOT_FOUND': OrderNotFound, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.v1_01PublicGetTradingTicker (params); + // + // { + // status: 'Ok', + // items: { + // 'BSV-USD': { + // market: { + // code: 'BSV-USD', + // first: { currency: 'BSV', minOffer: '0.00035', scale: 8 }, + // second: { currency: 'USD', minOffer: '5', scale: 2 } + // }, + // time: '1557569762154', + // highestBid: '52.31', + // lowestAsk: '62.99', + // rate: '63', + // previousRate: '51.21', + // }, + // }, + // } + // + const result = []; + const items = this.safeValue (response, 'items'); + const keys = Object.keys (items); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const item = items[key]; + const market = this.safeValue (item, 'market', {}); + const first = this.safeValue (market, 'first', {}); + const second = this.safeValue (market, 'second', {}); + const baseId = this.safeString (first, 'currency'); + const quoteId = this.safeString (second, 'currency'); + const id = baseId + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (first, 'scale'), + 'price': this.safeInteger (second, 'scale'), + }; + // todo: check that the limits have ben interpreted correctly + // todo: parse the fees page + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'active': undefined, + 'fee': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (first, 'minOffer'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (second, 'minOffer'), + 'max': undefined, + }, + }, + 'info': item, + }); + } + return result; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + const response = await this.v1_01PrivateGetTradingOffer (this.extend (request, params)); + const items = this.safeValue (response, 'items', []); + return this.parseOrders (items, undefined, since, limit, { 'status': 'open' }); + } + + parseOrder (order, market = undefined) { + // + // { + // market: 'ETH-EUR', + // offerType: 'Sell', + // id: '93d3657b-d616-11e9-9248-0242ac110005', + // currentAmount: '0.04', + // lockedAmount: '0.04', + // rate: '280', + // startAmount: '0.04', + // time: '1568372806924', + // postOnly: false, + // hidden: false, + // mode: 'limit', + // receivedAmount: '0.0', + // firstBalanceId: '5b816c3e-437c-4e43-9bef-47814ae7ebfc', + // secondBalanceId: 'ab43023b-4079-414c-b340-056e3430a3af' + // } + // + const marketId = this.safeString (order, 'market'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const timestamp = this.safeInteger (order, 'time'); + const amount = this.safeFloat (order, 'startAmount'); + const remaining = this.safeFloat (order, 'currentAmount'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = Math.max (0, amount - remaining); + } + } + return { + 'id': this.safeString (order, 'id'), + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': this.safeString (order, 'mode'), + 'side': this.safeStringLower (order, 'offerType'), + 'price': this.safeFloat (order, 'rate'), + 'amount': amount, + 'cost': undefined, + 'filled': filled, + 'remaining': remaining, + 'average': undefined, + 'fee': undefined, + 'trades': undefined, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbol) { + const markets = [ this.marketId (symbol) ]; + request['markets'] = markets; + } + const query = { 'query': this.json (this.extend (request, params)) }; + const response = await this.v1_01PrivateGetTradingHistoryTransactions (query); + // + // { + // status: 'Ok', + // totalRows: '67', + // items: [ + // { + // id: 'b54659a0-51b5-42a0-80eb-2ac5357ccee2', + // market: 'BTC-EUR', + // time: '1541697096247', + // amount: '0.00003', + // rate: '4341.44', + // initializedBy: 'Sell', + // wasTaker: false, + // userAction: 'Buy', + // offerId: 'bd19804a-6f89-4a69-adb8-eb078900d006', + // commissionValue: null + // }, + // ] + // } + // + const items = this.safeValue (response, 'items'); + const result = this.parseTrades (items, undefined, since, limit); + if (symbol === undefined) { + return result; + } + return this.filterBySymbol (result, symbol); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.v1_01PrivateGetBalancesBITBAYBalance (params); + const balances = this.safeValue (response, 'balances'); + if (balances === undefined) { + throw new ExchangeError (this.id + ' empty balance response ' + this.json (response)); + } + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'lockedFunds'); + account['free'] = this.safeFloat (balance, 'availableFunds'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const orderbook = await this.publicGetIdOrderbook (this.extend (request, params)); + return this.parseOrderBook (orderbook); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const ticker = await this.publicGetIdTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const baseVolume = this.safeFloat (ticker, 'volume'); + const vwap = this.safeFloat (ticker, 'vwap'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'average'), + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + const balanceCurrencies = []; + if (code !== undefined) { + const currency = this.currency (code); + balanceCurrencies.push (currency['id']); + } + let request = { + 'balanceCurrencies': balanceCurrencies, + }; + if (since !== undefined) { + request['fromTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + request = this.extend (request, params); + const response = await this.v1_01PrivateGetBalancesBITBAYHistory ({ 'query': this.json (request) }); + const items = response['items']; + return this.parseLedger (items, undefined, since, limit); + } + + parseLedgerEntry (item, currency = undefined) { + // + // FUNDS_MIGRATION + // { + // "historyId": "84ea7a29-7da5-4de5-b0c0-871e83cad765", + // "balance": { + // "id": "821ec166-cb88-4521-916c-f4eb44db98df", + // "currency": "LTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "LTC" + // }, + // "detailId": null, + // "time": 1506128252968, + // "type": "FUNDS_MIGRATION", + // "value": 0.0009957, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 0.0009957, "available": 0.0009957, "locked": 0 }, + // "change": { "total": 0.0009957, "available": 0.0009957, "locked": 0 } + // } + // + // CREATE_BALANCE + // { + // "historyId": "d0fabd8d-9107-4b5e-b9a6-3cab8af70d49", + // "balance": { + // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", + // "currency": "BTG", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTG" + // }, + // "detailId": null, + // "time": 1508895244751, + // "type": "CREATE_BALANCE", + // "value": 0, + // "fundsBefore": { "total": null, "available": null, "locked": null }, + // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, + // "change": { "total": 0, "available": 0, "locked": 0 } + // } + // + // BITCOIN_GOLD_FORK + // { + // "historyId": "2b4d52d3-611c-473d-b92c-8a8d87a24e41", + // "balance": { + // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", + // "currency": "BTG", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTG" + // }, + // "detailId": null, + // "time": 1508895244778, + // "type": "BITCOIN_GOLD_FORK", + // "value": 0.00453512, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, + // "change": { "total": 0.00453512, "available": 0.00453512, "locked": 0 } + // } + // + // ADD_FUNDS + // { + // "historyId": "3158236d-dae5-4a5d-81af-c1fa4af340fb", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "8e83a960-e737-4380-b8bb-259d6e236faa", + // "time": 1520631178816, + // "type": "ADD_FUNDS", + // "value": 0.628405, + // "fundsBefore": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, + // "fundsAfter": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, + // "change": { "total": 0.628405, "available": 0.628405, "locked": 0 } + // } + // + // TRANSACTION_PRE_LOCKING + // { + // "historyId": "e7d19e0f-03b3-46a8-bc72-dde72cc24ead", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": null, + // "time": 1520706403868, + // "type": "TRANSACTION_PRE_LOCKING", + // "value": -0.1, + // "fundsBefore": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, + // "fundsAfter": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, + // "change": { "total": 0, "available": -0.1, "locked": 0.1 } + // } + // + // TRANSACTION_POST_OUTCOME + // { + // "historyId": "c4010825-231d-4a9c-8e46-37cde1f7b63c", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "bf2876bc-b545-4503-96c8-ef4de8233876", + // "time": 1520706404032, + // "type": "TRANSACTION_POST_OUTCOME", + // "value": -0.01771415, + // "fundsBefore": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, + // "fundsAfter": { "total": 0.61522597, "available": 0.53294012, "locked": 0.08228585 }, + // "change": { "total": -0.01771415, "available": 0, "locked": -0.01771415 } + // } + // + // TRANSACTION_POST_INCOME + // { + // "historyId": "7f18b7af-b676-4125-84fd-042e683046f6", + // "balance": { + // "id": "ab43023b-4079-414c-b340-056e3430a3af", + // "currency": "EUR", + // "type": "FIAT", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "EUR" + // }, + // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", + // "time": 1520706404035, + // "type": "TRANSACTION_POST_INCOME", + // "value": 628.78, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 628.78, "available": 628.78, "locked": 0 }, + // "change": { "total": 628.78, "available": 628.78, "locked": 0 } + // } + // + // TRANSACTION_COMMISSION_OUTCOME + // { + // "historyId": "843177fa-61bc-4cbf-8be5-b029d856c93b", + // "balance": { + // "id": "ab43023b-4079-414c-b340-056e3430a3af", + // "currency": "EUR", + // "type": "FIAT", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "EUR" + // }, + // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", + // "time": 1520706404050, + // "type": "TRANSACTION_COMMISSION_OUTCOME", + // "value": -2.71, + // "fundsBefore": { "total": 766.06, "available": 766.06, "locked": 0 }, + // "fundsAfter": { "total": 763.35,"available": 763.35, "locked": 0 }, + // "change": { "total": -2.71, "available": -2.71, "locked": 0 } + // } + // + // TRANSACTION_OFFER_COMPLETED_RETURN + // { + // "historyId": "cac69b04-c518-4dc5-9d86-e76e91f2e1d2", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": null, + // "time": 1520714886425, + // "type": "TRANSACTION_OFFER_COMPLETED_RETURN", + // "value": 0.00000196, + // "fundsBefore": { "total": 0.00941208, "available": 0.00941012, "locked": 0.00000196 }, + // "fundsAfter": { "total": 0.00941208, "available": 0.00941208, "locked": 0 }, + // "change": { "total": 0, "available": 0.00000196, "locked": -0.00000196 } + // } + // + // WITHDRAWAL_LOCK_FUNDS + // { + // "historyId": "03de2271-66ab-4960-a786-87ab9551fc14", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", + // "time": 1522245654481, + // "type": "WITHDRAWAL_LOCK_FUNDS", + // "value": -0.8, + // "fundsBefore": { "total": 0.8, "available": 0.8, "locked": 0 }, + // "fundsAfter": { "total": 0.8, "available": 0, "locked": 0.8 }, + // "change": { "total": 0, "available": -0.8, "locked": 0.8 } + // } + // + // WITHDRAWAL_SUBTRACT_FUNDS + // { + // "historyId": "b0308c89-5288-438d-a306-c6448b1a266d", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", + // "time": 1522246526186, + // "type": "WITHDRAWAL_SUBTRACT_FUNDS", + // "value": -0.8, + // "fundsBefore": { "total": 0.8, "available": 0, "locked": 0.8 }, + // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, + // "change": { "total": -0.8, "available": 0, "locked": -0.8 } + // } + // + // TRANSACTION_OFFER_ABORTED_RETURN + // { + // "historyId": "b1a3c075-d403-4e05-8f32-40512cdd88c0", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": null, + // "time": 1522512298662, + // "type": "TRANSACTION_OFFER_ABORTED_RETURN", + // "value": 0.0564931, + // "fundsBefore": { "total": 0.44951311, "available": 0.39302001, "locked": 0.0564931 }, + // "fundsAfter": { "total": 0.44951311, "available": 0.44951311, "locked": 0 }, + // "change": { "total": 0, "available": 0.0564931, "locked": -0.0564931 } + // } + // + // WITHDRAWAL_UNLOCK_FUNDS + // { + // "historyId": "0ed569a2-c330-482e-bb89-4cb553fb5b11", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "0c7be256-c336-4111-bee7-4eb22e339700", + // "time": 1527866360785, + // "type": "WITHDRAWAL_UNLOCK_FUNDS", + // "value": 0.05045, + // "fundsBefore": { "total": 0.86001578, "available": 0.80956578, "locked": 0.05045 }, + // "fundsAfter": { "total": 0.86001578, "available": 0.86001578, "locked": 0 }, + // "change": { "total": 0, "available": 0.05045, "locked": -0.05045 } + // } + // + // TRANSACTION_COMMISSION_RETURN + // { + // "historyId": "07c89c27-46f1-4d7a-8518-b73798bf168a", + // "balance": { + // "id": "ab43023b-4079-414c-b340-056e3430a3af", + // "currency": "EUR", + // "type": "FIAT", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "EUR" + // }, + // "detailId": null, + // "time": 1528304043063, + // "type": "TRANSACTION_COMMISSION_RETURN", + // "value": 0.6, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 0.6, "available": 0.6, "locked": 0 }, + // "change": { "total": 0.6, "available": 0.6, "locked": 0 } + // } + // + const timestamp = this.safeInteger (item, 'time'); + const balance = this.safeValue (item, 'balance', {}); + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const change = this.safeValue (item, 'change', {}); + let amount = this.safeFloat (change, 'total'); + let direction = 'in'; + if (amount < 0) { + direction = 'out'; + amount = -amount; + } + const id = this.safeString (item, 'historyId'); + // there are 2 undocumented api calls: (v1_01PrivateGetPaymentsDepositDetailId and v1_01PrivateGetPaymentsWithdrawalDetailId) + // that can be used to enrich the transfers with txid, address etc (you need to use info.detailId as a parameter) + const referenceId = this.safeString (item, 'detailId'); + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const fundsBefore = this.safeValue (item, 'fundsBefore', {}); + const before = this.safeFloat (fundsBefore, 'total'); + const fundsAfter = this.safeValue (item, 'fundsAfter', {}); + const after = this.safeFloat (fundsAfter, 'total'); + return { + 'info': item, + 'id': id, + 'direction': direction, + 'account': undefined, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, + 'after': after, + 'status': 'ok', + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + parseLedgerEntryType (type) { + const types = { + 'ADD_FUNDS': 'transaction', + 'BITCOIN_GOLD_FORK': 'transaction', + 'CREATE_BALANCE': 'transaction', + 'FUNDS_MIGRATION': 'transaction', + 'WITHDRAWAL_LOCK_FUNDS': 'transaction', + 'WITHDRAWAL_SUBTRACT_FUNDS': 'transaction', + 'WITHDRAWAL_UNLOCK_FUNDS': 'transaction', + 'TRANSACTION_COMMISSION_OUTCOME': 'fee', + 'TRANSACTION_COMMISSION_RETURN': 'fee', + 'TRANSACTION_OFFER_ABORTED_RETURN': 'trade', + 'TRANSACTION_OFFER_COMPLETED_RETURN': 'trade', + 'TRANSACTION_POST_INCOME': 'trade', + 'TRANSACTION_POST_OUTCOME': 'trade', + 'TRANSACTION_PRE_LOCKING': 'trade', + }; + return this.safeString (types, type, type); + } + + parseTrade (trade, market = undefined) { + // + // createOrder trades + // + // { + // "rate": "0.02195928", + // "amount": "0.00167952" + // } + // + // fetchMyTrades (private) + // + // { + // amount: "0.29285199", + // commissionValue: "0.00125927", + // id: "11c8203a-a267-11e9-b698-0242ac110007", + // initializedBy: "Buy", + // market: "ETH-EUR", + // offerId: "11c82038-a267-11e9-b698-0242ac110007", + // rate: "277", + // time: "1562689917517", + // userAction: "Buy", + // wasTaker: true, + // } + // + // fetchTrades (public) + // + // { + // id: 'df00b0da-e5e0-11e9-8c19-0242ac11000a', + // t: '1570108958831', + // a: '0.04776653', + // r: '0.02145854', + // ty: 'Sell' + // } + // + const timestamp = this.safeInteger2 (trade, 'time', 't'); + const userAction = this.safeString (trade, 'userAction'); + const side = (userAction === 'Buy') ? 'buy' : 'sell'; + const wasTaker = this.safeValue (trade, 'wasTaker'); + let takerOrMaker = undefined; + if (wasTaker !== undefined) { + takerOrMaker = wasTaker ? 'taker' : 'maker'; + } + const price = this.safeFloat2 (trade, 'rate', 'r'); + const amount = this.safeFloat2 (trade, 'amount', 'a'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const feeCost = this.safeFloat (trade, 'commissionValue'); + const marketId = this.safeString (trade, 'market'); + let base = undefined; + let quote = undefined; + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + base = market['base']; + quote = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + if (symbol === undefined) { + symbol = market['symbol']; + } + if (base === undefined) { + base = market['base']; + } + } + let fee = undefined; + if (feeCost !== undefined) { + const feeCcy = (side === 'buy') ? base : quote; + fee = { + 'currency': feeCcy, + 'cost': feeCost, + }; + } + const order = this.safeString (trade, 'offerId'); + // todo: check this logic + let type = undefined; + if (order !== undefined) { + type = order ? 'limit' : 'market'; + } + return { + 'id': this.safeString (trade, 'id'), + 'order': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tradingSymbol = market['baseId'] + '-' + market['quoteId']; + const request = { + 'symbol': tradingSymbol, + }; + if (since !== undefined) { + request['fromTime'] = since - 1; // result does not include exactly `since` time therefore decrease by 1 + } + if (limit !== undefined) { + request['limit'] = limit; // default - 10, max - 300 + } + const response = await this.v1_01PublicGetTradingTransactionsSymbol (this.extend (request, params)); + const items = this.safeValue (response, 'items'); + return this.parseTrades (items, symbol, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tradingSymbol = market['baseId'] + '-' + market['quoteId']; + const request = { + 'symbol': tradingSymbol, + 'offerType': side, + 'amount': amount, + 'mode': type, + }; + if (type === 'limit') { + request['rate'] = price; + } + const response = await this.v1_01PrivatePostTradingOfferSymbol (this.extend (request, params)); + // + // unfilled (open order) + // + // { + // status: 'Ok', + // completed: false, // can deduce status from here + // offerId: 'ce9cc72e-d61c-11e9-9248-0242ac110005', + // transactions: [], // can deduce order info from here + // } + // + // filled (closed order) + // + // { + // "status": "Ok", + // "offerId": "942a4a3e-e922-11e9-8c19-0242ac11000a", + // "completed": true, + // "transactions": [ + // { + // "rate": "0.02195928", + // "amount": "0.00167952" + // }, + // { + // "rate": "0.02195928", + // "amount": "0.00167952" + // }, + // { + // "rate": "0.02196207", + // "amount": "0.27704177" + // } + // ] + // } + // + // partially-filled (open order) + // + // { + // "status": "Ok", + // "offerId": "d0ebefab-f4d7-11e9-8c19-0242ac11000a", + // "completed": false, + // "transactions": [ + // { + // "rate": "0.02106404", + // "amount": "0.0019625" + // }, + // { + // "rate": "0.02106404", + // "amount": "0.0019625" + // }, + // { + // "rate": "0.02105901", + // "amount": "0.00975256" + // } + // ] + // } + // + const timestamp = this.milliseconds (); // the real timestamp is missing in the response + const id = this.safeString (response, 'offerId'); + const completed = this.safeValue (response, 'completed', false); + const status = completed ? 'closed' : 'open'; + let filled = 0; + let cost = undefined; + const transactions = this.safeValue (response, 'transactions'); + let trades = undefined; + if (transactions !== undefined) { + trades = this.parseTrades (transactions, market, undefined, undefined, { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'side': side, + 'type': type, + 'orderId': id, + }); + cost = 0; + for (let i = 0; i < trades.length; i++) { + filled = this.sum (filled, trades[i]['amount']); + cost = this.sum (cost, trades[i]['cost']); + } + } + const remaining = amount - filled; + return { + 'id': id, + 'info': response, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': parseFloat (price), + 'amount': parseFloat (amount), + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'average': undefined, + 'fee': undefined, + 'trades': trades, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const side = this.safeString (params, 'side'); + if (side === undefined) { + throw new ExchangeError (this.id + ' cancelOrder() requires a `side` parameter ("buy" or "sell")'); + } + const price = this.safeValue (params, 'price'); + if (price === undefined) { + throw new ExchangeError (this.id + ' cancelOrder() requires a `price` parameter (float or string)'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const tradingSymbol = market['baseId'] + '-' + market['quoteId']; + const request = { + 'symbol': tradingSymbol, + 'id': id, + 'side': side, + 'price': price, + }; + // { status: 'Fail', errors: [ 'NOT_RECOGNIZED_OFFER_TYPE' ] } -- if required params are missing + // { status: 'Ok', errors: [] } + return this.v1_01PrivateDeleteTradingOfferSymbolIdSidePrice (this.extend (request, params)); + } + + isFiat (currency) { + const fiatCurrencies = { + 'USD': true, + 'EUR': true, + 'PLN': true, + }; + return this.safeValue (fiatCurrencies, currency, false); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + let method = undefined; + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'quantity': amount, + }; + if (this.isFiat (code)) { + method = 'privatePostWithdraw'; + // request['account'] = params['account']; // they demand an account number + // request['express'] = params['express']; // whatever it means, they don't explain + // request['bic'] = ''; + } else { + method = 'privatePostTransfer'; + if (tag !== undefined) { + address += '?dt=' + tag.toString (); + } + request['address'] = address; + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api === 'public') { + const query = this.omit (params, this.extractParams (path)); + url += '/' + this.implodeParams (path, params) + '.json'; + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'v1_01Public') { + const query = this.omit (params, this.extractParams (path)); + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'v1_01Private') { + this.checkRequiredCredentials (); + const query = this.omit (params, this.extractParams (path)); + url += '/' + this.implodeParams (path, params); + const nonce = this.milliseconds ().toString (); + let payload = undefined; + if (method !== 'POST') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + payload = this.apiKey + nonce; + } else if (body === undefined) { + body = this.json (query); + payload = this.apiKey + nonce + body; + } + headers = { + 'Request-Timestamp': nonce, + 'Operation-Id': this.uuid (), + 'API-Key': this.apiKey, + 'API-Hash': this.hmac (this.encode (payload), this.encode (this.secret), 'sha512'), + 'Content-Type': 'application/json', + }; + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'method': path, + 'moment': this.nonce (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'API-Key': this.apiKey, + 'API-Hash': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('code' in response) { + // + // bitbay returns the integer 'success': 1 key from their private API + // or an integer 'code' value from 0 to 510 and an error message + // + // { 'success': 1, ... } + // { 'code': 502, 'message': 'Invalid sign' } + // { 'code': 0, 'message': 'offer funds not exceeding minimums' } + // + // 400 At least one parameter wasn't set + // 401 Invalid order type + // 402 No orders with specified currencies + // 403 Invalid payment currency name + // 404 Error. Wrong transaction type + // 405 Order with this id doesn't exist + // 406 No enough money or crypto + // 408 Invalid currency name + // 501 Invalid public key + // 502 Invalid sign + // 503 Invalid moment parameter. Request time doesn't match current server time + // 504 Invalid method + // 505 Key has no permission for this action + // 506 Account locked. Please contact with customer service + // 509 The BIC/SWIFT is required for this currency + // 510 Invalid market name + // + const code = this.safeString (response, 'code'); // always an integer + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } else if ('status' in response) { + // + // {"status":"Fail","errors":["OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS"]} + // + const status = this.safeString (response, 'status'); + if (status === 'Fail') { + const errors = this.safeValue (response, 'errors'); + const feedback = this.id + ' ' + body; + for (let i = 0; i < errors.length; i++) { + const error = errors[i]; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + } + throw new ExchangeError (feedback); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],31:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { NotSupported, RateLimitExceeded, AuthenticationError, PermissionDenied, ArgumentsRequired, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, InvalidNonce } = require ('./base/errors'); +const { SIGNIFICANT_DIGITS } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bitfinex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitfinex', + 'name': 'Bitfinex', + 'countries': [ 'VG' ], + 'version': 'v1', + 'rateLimit': 1500, + 'certified': true, + // new metainfo interface + 'has': { + 'CORS': false, + 'cancelAllOrders': true, + 'createDepositAddress': true, + 'deposit': true, + 'fetchClosedOrders': true, + 'fetchDepositAddress': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchFundingFees': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchTransactions': true, + 'fetchDeposits': false, + 'fetchWithdrawals': false, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '3h': '3h', + '6h': '6h', + '12h': '12h', + '1d': '1D', + '1w': '7D', + '2w': '14D', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg', + 'api': { + 'v2': 'https://api-pub.bitfinex.com', // https://github.com/ccxt/ccxt/issues/5109 + 'public': 'https://api.bitfinex.com', + 'private': 'https://api.bitfinex.com', + }, + 'www': 'https://www.bitfinex.com', + 'referral': 'https://www.bitfinex.com/?refcode=P61eYxFL', + 'doc': [ + 'https://docs.bitfinex.com/v1/docs', + 'https://github.com/bitfinexcom/bitfinex-api-node', + ], + }, + 'api': { + // v2 symbol ids require a 't' prefix + // just the public part of it (use bitfinex2 for everything else) + 'v2': { + 'get': [ + 'platform/status', + 'tickers', + 'ticker/{symbol}', + 'trades/{symbol}/hist', + 'book/{symbol}/{precision}', + 'book/{symbol}/P0', + 'book/{symbol}/P1', + 'book/{symbol}/P2', + 'book/{symbol}/P3', + 'book/{symbol}/R0', + 'stats1/{key}:{size}:{symbol}:{side}/{section}', + 'stats1/{key}:{size}:{symbol}/{section}', + 'stats1/{key}:{size}:{symbol}:long/last', + 'stats1/{key}:{size}:{symbol}:long/hist', + 'stats1/{key}:{size}:{symbol}:short/last', + 'stats1/{key}:{size}:{symbol}:short/hist', + 'candles/trade:{timeframe}:{symbol}/{section}', + 'candles/trade:{timeframe}:{symbol}/last', + 'candles/trade:{timeframe}:{symbol}/hist', + ], + }, + 'public': { + 'get': [ + 'book/{symbol}', + // 'candles/{symbol}', + 'lendbook/{currency}', + 'lends/{currency}', + 'pubticker/{symbol}', + 'stats/{symbol}', + 'symbols', + 'symbols_details', + 'tickers', + 'trades/{symbol}', + ], + }, + 'private': { + 'post': [ + 'account_fees', + 'account_infos', + 'balances', + 'basket_manage', + 'credits', + 'deposit/new', + 'funding/close', + 'history', + 'history/movements', + 'key_info', + 'margin_infos', + 'mytrades', + 'mytrades_funding', + 'offer/cancel', + 'offer/new', + 'offer/status', + 'offers', + 'offers/hist', + 'order/cancel', + 'order/cancel/all', + 'order/cancel/multi', + 'order/cancel/replace', + 'order/new', + 'order/new/multi', + 'order/status', + 'orders', + 'orders/hist', + 'position/claim', + 'position/close', + 'positions', + 'summary', + 'taken_funds', + 'total_taken_funds', + 'transfer', + 'unused_taken_funds', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + 'tiers': { + 'taker': [ + [0, 0.2 / 100], + [500000, 0.2 / 100], + [1000000, 0.2 / 100], + [2500000, 0.2 / 100], + [5000000, 0.2 / 100], + [7500000, 0.2 / 100], + [10000000, 0.18 / 100], + [15000000, 0.16 / 100], + [20000000, 0.14 / 100], + [25000000, 0.12 / 100], + [30000000, 0.1 / 100], + ], + 'maker': [ + [0, 0.1 / 100], + [500000, 0.08 / 100], + [1000000, 0.06 / 100], + [2500000, 0.04 / 100], + [5000000, 0.02 / 100], + [7500000, 0], + [10000000, 0], + [15000000, 0], + [20000000, 0], + [25000000, 0], + [30000000, 0], + ], + }, + }, + 'funding': { + 'tierBased': false, // true for tier-based/progressive + 'percentage': false, // fixed commission + // Actually deposit fees are free for larger deposits (> $1000 USD equivalent) + // these values below are deprecated, we should not hardcode fees and limits anymore + // to be reimplemented with bitfinex funding fees from their API or web endpoints + 'deposit': { + 'BTC': 0.0004, + 'IOTA': 0.5, + 'ETH': 0.0027, + 'BCH': 0.0001, + 'LTC': 0.001, + 'EOS': 0.24279, + 'XMR': 0.04, + 'SAN': 0.99269, + 'DASH': 0.01, + 'ETC': 0.01, + 'XRP': 0.02, + 'YYW': 16.915, + 'NEO': 0, + 'ZEC': 0.001, + 'BTG': 0, + 'OMG': 0.14026, + 'DATA': 20.773, + 'QASH': 1.9858, + 'ETP': 0.01, + 'QTUM': 0.01, + 'EDO': 0.95001, + 'AVT': 1.3045, + 'USDT': 0, + 'TRX': 28.184, + 'ZRX': 1.9947, + 'RCN': 10.793, + 'TNB': 31.915, + 'SNT': 14.976, + 'RLC': 1.414, + 'GNT': 5.8952, + 'SPK': 10.893, + 'REP': 0.041168, + 'BAT': 6.1546, + 'ELF': 1.8753, + 'FUN': 32.336, + 'SNG': 18.622, + 'AID': 8.08, + 'MNA': 16.617, + 'NEC': 1.6504, + 'XTZ': 0.2, + }, + 'withdraw': { + 'BTC': 0.0004, + 'IOTA': 0.5, + 'ETH': 0.0027, + 'BCH': 0.0001, + 'LTC': 0.001, + 'EOS': 0.24279, + 'XMR': 0.04, + 'SAN': 0.99269, + 'DASH': 0.01, + 'ETC': 0.01, + 'XRP': 0.02, + 'YYW': 16.915, + 'NEO': 0, + 'ZEC': 0.001, + 'BTG': 0, + 'OMG': 0.14026, + 'DATA': 20.773, + 'QASH': 1.9858, + 'ETP': 0.01, + 'QTUM': 0.01, + 'EDO': 0.95001, + 'AVT': 1.3045, + 'USDT': 20, + 'TRX': 28.184, + 'ZRX': 1.9947, + 'RCN': 10.793, + 'TNB': 31.915, + 'SNT': 14.976, + 'RLC': 1.414, + 'GNT': 5.8952, + 'SPK': 10.893, + 'REP': 0.041168, + 'BAT': 6.1546, + 'ELF': 1.8753, + 'FUN': 32.336, + 'SNG': 18.622, + 'AID': 8.08, + 'MNA': 16.617, + 'NEC': 1.6504, + 'XTZ': 0.2, + }, + }, + }, + 'commonCurrencies': { + 'ABS': 'ABYSS', + 'AIO': 'AION', + 'ALG': 'ALGO', // https://github.com/ccxt/ccxt/issues/6034 + 'AMP': 'AMPL', + 'ATM': 'ATMI', + 'ATO': 'ATOM', // https://github.com/ccxt/ccxt/issues/5118 + 'BAB': 'BCH', + 'CTX': 'CTXC', + 'DAD': 'DADI', + 'DAT': 'DATA', + 'DSH': 'DASH', + 'DRK': 'DRK', + 'GSD': 'GUSD', + 'HOT': 'Hydro Protocol', + 'IOS': 'IOST', + 'IOT': 'IOTA', + 'IQX': 'IQ', + 'MIT': 'MITH', + 'MNA': 'MANA', + 'NCA': 'NCASH', + 'ORS': 'ORS Group', // conflict with Origin Sport #3230 + 'POY': 'POLY', + 'QSH': 'QASH', + 'QTM': 'QTUM', + 'SEE': 'SEER', + 'SNG': 'SNGLS', + 'SPK': 'SPANK', + 'STJ': 'STORJ', + 'TSD': 'TUSD', + 'YYW': 'YOYOW', + 'UDC': 'USDC', + 'UST': 'USDT', + 'UTN': 'UTNP', + 'VSY': 'VSYS', + 'XCH': 'XCHF', + }, + 'exceptions': { + 'exact': { + 'temporarily_unavailable': ExchangeNotAvailable, // Sorry, the service is temporarily unavailable. See https://www.bitfinex.com/ for more info. + 'Order could not be cancelled.': OrderNotFound, // non-existent order + 'No such order found.': OrderNotFound, // ? + 'Order price must be positive.': InvalidOrder, // on price <= 0 + 'Could not find a key matching the given X-BFX-APIKEY.': AuthenticationError, + 'Key price should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (price) + 'Key amount should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (amount) + 'ERR_RATE_LIMIT': RateLimitExceeded, + 'Ratelimit': RateLimitExceeded, + 'Nonce is too small.': InvalidNonce, + 'No summary found.': ExchangeError, // fetchTradingFees (summary) endpoint can give this vague error message + 'Cannot evaluate your available balance, please try again': ExchangeNotAvailable, + }, + 'broad': { + 'This API key does not have permission': PermissionDenied, // authenticated but not authorized + 'not enough exchange balance for ': InsufficientFunds, // when buying cost is greater than the available quote currency + 'minimum size for ': InvalidOrder, // when amount below limits.amount.min + 'Invalid order': InvalidOrder, // ? + 'The available balance is only': InsufficientFunds, // {"status":"error","message":"Cannot withdraw 1.0027 ETH from your exchange wallet. The available balance is only 0.0 ETH. If you have limit orders, open positions, unused or active margin funding, this will decrease your available balance. To increase it, you can cancel limit orders or reduce/close your positions.","withdrawal_id":0,"fees":"0.0027"} + }, + }, + 'precisionMode': SIGNIFICANT_DIGITS, + 'options': { + 'currencyNames': { + 'AGI': 'agi', + 'AID': 'aid', + 'AIO': 'aio', + 'ANT': 'ant', + 'AVT': 'aventus', // #1811 + 'BAT': 'bat', + // https://github.com/ccxt/ccxt/issues/5833 + 'BCH': 'bab', // undocumented + // 'BCH': 'bcash', // undocumented + 'BCI': 'bci', + 'BFT': 'bft', + 'BTC': 'bitcoin', + 'BTG': 'bgold', + 'CFI': 'cfi', + 'DAI': 'dai', + 'DADI': 'dad', + 'DASH': 'dash', + 'DATA': 'datacoin', + 'DTH': 'dth', + 'EDO': 'eidoo', // #1811 + 'ELF': 'elf', + 'EOS': 'eos', + 'ETC': 'ethereumc', + 'ETH': 'ethereum', + 'ETP': 'metaverse', + 'FUN': 'fun', + 'GNT': 'golem', + 'IOST': 'ios', + 'IOTA': 'iota', + // https://github.com/ccxt/ccxt/issues/5833 + 'LEO': 'let', // ETH chain + // 'LEO': 'les', // EOS chain + 'LRC': 'lrc', + 'LTC': 'litecoin', + 'LYM': 'lym', + 'MANA': 'mna', + 'MIT': 'mit', + 'MKR': 'mkr', + 'MTN': 'mtn', + 'NEO': 'neo', + 'ODE': 'ode', + 'OMG': 'omisego', + 'OMNI': 'mastercoin', + 'QASH': 'qash', + 'QTUM': 'qtum', // #1811 + 'RCN': 'rcn', + 'RDN': 'rdn', + 'REP': 'rep', + 'REQ': 'req', + 'RLC': 'rlc', + 'SAN': 'santiment', + 'SNGLS': 'sng', + 'SNT': 'status', + 'SPANK': 'spk', + 'STORJ': 'stj', + 'TNB': 'tnb', + 'TRX': 'trx', + 'USD': 'wire', + 'USDC': 'udc', // https://github.com/ccxt/ccxt/issues/5833 + 'UTK': 'utk', + 'USDT': 'tetheruso', // Tether on Omni + // 'USDT': 'tetheruse', // Tether on ERC20 + // 'USDT': 'tetherusl', // Tether on Liquid + // 'USDT': 'tetherusx', // Tether on Tron + // 'USDT': 'tetheruss', // Tether on EOS + 'VEE': 'vee', + 'WAX': 'wax', + 'XLM': 'xlm', + 'XMR': 'monero', + 'XRP': 'ripple', + 'XVG': 'xvg', + 'YOYOW': 'yoyow', + 'ZEC': 'zcash', + 'ZRX': 'zrx', + 'XTZ': 'tezos', + }, + 'orderTypes': { + 'limit': 'exchange limit', + 'market': 'exchange market', + }, + }, + }); + } + + async fetchFundingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostAccountFees (params); + const fees = response['withdraw']; + const withdraw = {}; + const ids = Object.keys (fees); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + withdraw[code] = this.safeFloat (fees, id); + } + return { + 'info': response, + 'withdraw': withdraw, + 'deposit': withdraw, // only for deposits of less than $1000 + }; + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostSummary (params); + // + // { + // time: '2019-02-20T15:50:19.152000Z', + // trade_vol_30d: [ + // { + // curr: 'Total (USD)', + // vol: 0, + // vol_maker: 0, + // vol_BFX: 0, + // vol_BFX_maker: 0, + // vol_ETHFX: 0, + // vol_ETHFX_maker: 0 + // } + // ], + // fees_funding_30d: {}, + // fees_funding_total_30d: 0, + // fees_trading_30d: {}, + // fees_trading_total_30d: 0, + // maker_fee: 0.001, + // taker_fee: 0.002 + // } + // + return { + 'info': response, + 'maker': this.safeFloat (response, 'maker_fee'), + 'taker': this.safeFloat (response, 'taker_fee'), + }; + } + + async fetchMarkets (params = {}) { + const ids = await this.publicGetSymbols (); + const details = await this.publicGetSymbolsDetails (); + const result = []; + for (let i = 0; i < details.length; i++) { + const market = details[i]; + let id = this.safeString (market, 'pair'); + if (!this.inArray (id, ids)) { + continue; + } + id = id.toUpperCase (); + let baseId = undefined; + let quoteId = undefined; + if (id.indexOf (':') >= 0) { + const parts = id.split (':'); + baseId = parts[0]; + quoteId = parts[1]; + } else { + baseId = id.slice (0, 3); + quoteId = id.slice (3, 6); + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': market['price_precision'], + 'amount': undefined, + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_order_size'), + 'max': this.safeFloat (market, 'maximum_order_size'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + }; + limits['cost'] = { + 'min': limits['amount']['min'] * limits['price']['min'], + 'max': undefined, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + amountToPrecision (symbol, amount) { + return this.numberToString (amount); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let key = 'quote'; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + const code = market[key]; + const currency = this.safeValue (this.currencies, code); + if (currency !== undefined) { + const precision = this.safeInteger (currency, 'precision'); + if (precision !== undefined) { + cost = parseFloat (this.currencyToPrecision (code, cost)); + } + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balanceType = this.safeString (params, 'type', 'exchange'); + const query = this.omit (params, 'type'); + const response = await this.privatePostBalances (query); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + if (balance['type'] === balanceType) { + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + // bitfinex had BCH previously, now it's BAB, but the old + // BCH symbol is kept for backward-compatibility + // we need a workaround here so that the old BCH balance + // would not override the new BAB balance (BAB is unified to BCH) + // https://github.com/ccxt/ccxt/issues/4989 + if (!(code in result)) { + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'amount'); + result[code] = account; + } + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit_bids'] = limit; + request['limit_asks'] = limit; + } + const response = await this.publicGetBookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.publicGetPubtickerSymbol (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + let timestamp = this.safeFloat (ticker, 'timestamp'); + if (timestamp !== undefined) { + timestamp *= 1000; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else if ('pair' in ticker) { + const marketId = this.safeString (ticker, 'pair'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const baseId = marketId.slice (0, 3); + const quoteId = marketId.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + const last = this.safeFloat (ticker, 'last_price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'mid'), + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market) { + const id = this.safeString (trade, 'tid'); + let timestamp = this.safeFloat (trade, 'timestamp'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp) * 1000; + } + const type = undefined; + const side = this.safeStringLower (trade, 'type'); + const orderId = this.safeString (trade, 'order_id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let fee = undefined; + if ('fee_amount' in trade) { + const feeCost = -this.safeFloat (trade, 'fee_amount'); + const feeCurrencyId = this.safeString (trade, 'fee_currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': type, + 'order': orderId, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'limit_trades': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.publicGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit_trades'] = limit; + } + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.privatePostMytrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'side': side, + 'amount': this.amountToPrecision (symbol, amount), + 'type': this.safeString (this.options['orderTypes'], type, type), + 'ocoorder': false, + 'buy_price_oco': 0, + 'sell_price_oco': 0, + }; + if (type === 'market') { + request['price'] = this.nonce ().toString (); + } else { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrderNew (this.extend (request, params)); + return this.parseOrder (response); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const order = { + 'order_id': id, + }; + if (price !== undefined) { + order['price'] = this.priceToPrecision (symbol, price); + } + if (amount !== undefined) { + order['amount'] = this.numberToString (amount); + } + if (symbol !== undefined) { + order['symbol'] = this.marketId (symbol); + } + if (side !== undefined) { + order['side'] = side; + } + if (type !== undefined) { + order['type'] = this.safeString (this.options['orderTypes'], type, type); + } + const response = await this.privatePostOrderCancelReplace (this.extend (order, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + return await this.privatePostOrderCancelAll (params); + } + + parseOrder (order, market = undefined) { + const side = this.safeString (order, 'side'); + const open = this.safeValue (order, 'is_live'); + const canceled = this.safeValue (order, 'is_cancelled'); + let status = undefined; + if (open) { + status = 'open'; + } else if (canceled) { + status = 'canceled'; + } else { + status = 'closed'; + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeStringUpper (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let orderType = order['type']; + const exchange = orderType.indexOf ('exchange ') >= 0; + if (exchange) { + const parts = order['type'].split (' '); + orderType = parts[1]; + } + let timestamp = this.safeFloat (order, 'timestamp'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp) * 1000; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'average': this.safeFloat (order, 'avg_execution_price'), + 'amount': this.safeFloat (order, 'original_amount'), + 'remaining': this.safeFloat (order, 'remaining_amount'), + 'filled': this.safeFloat (order, 'executed_amount'), + 'status': status, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (symbol !== undefined) { + if (!(symbol in this.markets)) { + throw new ExchangeError (this.id + ' has no symbol ' + symbol); + } + } + const response = await this.privatePostOrders (params); + let orders = this.parseOrders (response, undefined, since, limit); + if (symbol !== undefined) { + orders = this.filterBy (orders, 'symbol', symbol); + } + return orders; + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostOrdersHist (this.extend (request, params)); + let orders = this.parseOrders (response, undefined, since, limit); + if (symbol !== undefined) { + orders = this.filterBy (orders, 'symbol', symbol); + } + orders = this.filterByArray (orders, 'status', [ 'closed', 'canceled' ], false); + return orders; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0], + ohlcv[1], + ohlcv[3], + ohlcv[4], + ohlcv[2], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const market = this.market (symbol); + const v2id = 't' + market['id']; + const request = { + 'symbol': v2id, + 'timeframe': this.timeframes[timeframe], + 'sort': 1, + 'limit': limit, + }; + if (since !== undefined) { + request['start'] = since; + } + const response = await this.v2GetCandlesTradeTimeframeSymbolHist (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + getCurrencyName (code) { + if (code in this.options['currencyNames']) { + return this.options['currencyNames'][code]; + } + throw new NotSupported (this.id + ' ' + code + ' not supported for withdrawal'); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const request = { + 'renew': 1, + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'info': response['info'], + 'currency': code, + 'address': address, + 'tag': undefined, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const name = this.getCurrencyName (code); + const request = { + 'method': name, + 'wallet_name': 'exchange', + 'renew': 0, // a value of 1 will generate a new address + }; + const response = await this.privatePostDepositNew (this.extend (request, params)); + let address = this.safeValue (response, 'address'); + let tag = undefined; + if ('address_pool' in response) { + tag = address; + address = response['address_pool']; + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency `code` argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostHistoryMovements (this.extend (request, params)); + // + // [ + // { + // "id":581183, + // "txid": 123456, + // "currency":"BTC", + // "method":"BITCOIN", + // "type":"WITHDRAWAL", + // "amount":".01", + // "description":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ, offchain transfer ", + // "address":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ", + // "status":"COMPLETED", + // "timestamp":"1443833327.0", + // "timestamp_created": "1443833327.1", + // "fee": 0.1, + // } + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // crypto + // + // { + // "id": 12042490, + // "fee": "-0.02", + // "txid": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D", + // "type": "DEPOSIT", + // "amount": "2099.849999", + // "method": "RIPPLE", + // "status": "COMPLETED", + // "address": "2505189261", + // "currency": "XRP", + // "timestamp": "1551730524.0", + // "description": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D", + // "timestamp_created": "1551730523.0" + // } + // + // fiat + // + // { + // "id": 12725095, + // "fee": "-60.0", + // "txid": null, + // "type": "WITHDRAWAL", + // "amount": "9943.0", + // "method": "WIRE", + // "status": "SENDING", + // "address": null, + // "currency": "EUR", + // "timestamp": "1561802484.0", + // "description": "Name: bob, AccountAddress: some address, Account: someaccountno, Bank: bank address, SWIFT: foo, Country: UK, Details of Payment: withdrawal name, Intermediary Bank Name: , Intermediary Bank Address: , Intermediary Bank City: , Intermediary Bank Country: , Intermediary Bank Account: , Intermediary Bank SWIFT: , Fee: -60.0", + // "timestamp_created": "1561716066.0" + // } + // + let timestamp = this.safeFloat (transaction, 'timestamp_created'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp * 1000); + } + let updated = this.safeFloat (transaction, 'timestamp'); + if (updated !== undefined) { + updated = parseInt (updated * 1000); + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeStringLower (transaction, 'type'); // DEPOSIT or WITHDRAWAL + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + feeCost = Math.abs (feeCost); + } + return { + 'info': transaction, + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'txid'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'address'), // todo: this is actually the tag for XRP transfers (the address is missing) + 'tag': undefined, // refix it properly for the tag from description + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': { + 'currency': code, + 'cost': feeCost, + 'rate': undefined, + }, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'SENDING': 'pending', + 'CANCELED': 'canceled', + 'ZEROCONFIRMED': 'failed', // ZEROCONFIRMED happens e.g. in a double spend attempt (I had one in my movements!) + 'COMPLETED': 'ok', + }; + return this.safeString (statuses, status, status); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + const name = this.getCurrencyName (code); + const request = { + 'withdraw_type': name, + 'walletselected': 'exchange', + 'amount': amount.toString (), + 'address': address, + }; + if (tag !== undefined) { + request['payment_id'] = tag; + } + const responses = await this.privatePostWithdraw (this.extend (request, params)); + const response = responses[0]; + const id = this.safeString (response, 'withdrawal_id'); + const message = this.safeString (response, 'message'); + const errorMessage = this.findBroadlyMatchedKey (this.exceptions['broad'], message); + if (id === 0) { + if (errorMessage !== undefined) { + const ExceptionClass = this.exceptions['broad'][errorMessage]; + throw new ExceptionClass (this.id + ' ' + message); + } + throw new ExchangeError (this.id + ' withdraw returned an id of zero: ' + this.json (response)); + } + return { + 'info': response, + 'id': id, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.implodeParams (path, params); + if (api === 'v2') { + request = '/' + api + request; + } else { + request = '/' + this.version + request; + } + let query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + request; + if ((api === 'public') || (path.indexOf ('/hist') >= 0)) { + if (Object.keys (query).length) { + const suffix = '?' + this.urlencode (query); + url += suffix; + request += suffix; + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + query = this.extend ({ + 'nonce': nonce.toString (), + 'request': request, + }, query); + body = this.json (query); + query = this.encode (body); + const payload = this.stringToBase64 (query); + const secret = this.encode (this.secret); + const signature = this.hmac (payload, secret, 'sha384'); + headers = { + 'X-BFX-APIKEY': this.apiKey, + 'X-BFX-PAYLOAD': this.decode (payload), + 'X-BFX-SIGNATURE': signature, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code >= 400) { + if (body[0] === '{') { + const feedback = this.id + ' ' + body; + const message = this.safeString2 (response, 'message', 'error'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],32:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const bitfinex = require ('./bitfinex.js'); +const { ExchangeError, NotSupported, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitfinex2 extends bitfinex { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitfinex2', + 'name': 'Bitfinex', + 'countries': [ 'VG' ], + 'version': 'v2', + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': true, + 'createLimitOrder': false, + 'createMarketOrder': false, + 'createOrder': false, + 'deposit': false, + 'editOrder': false, + 'fetchDepositAddress': false, + 'fetchClosedOrders': false, + 'fetchFundingFees': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': false, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchTradingFee': false, + 'fetchTradingFees': false, + 'fetchTransactions': false, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '3h': '3h', + '6h': '6h', + '12h': '12h', + '1d': '1D', + '1w': '7D', + '2w': '14D', + '1M': '1M', + }, + 'rateLimit': 1500, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg', + 'api': { + 'v1': 'https://api.bitfinex.com', + 'public': 'https://api-pub.bitfinex.com', + 'private': 'https://api.bitfinex.com', + }, + 'www': 'https://www.bitfinex.com', + 'doc': [ + 'https://docs.bitfinex.com/v2/docs/', + 'https://github.com/bitfinexcom/bitfinex-api-node', + ], + 'fees': 'https://www.bitfinex.com/fees', + }, + 'api': { + 'v1': { + 'get': [ + 'symbols', + 'symbols_details', + ], + }, + 'public': { + 'get': [ + 'conf/pub:map:currency:label', + 'platform/status', + 'tickers', + 'ticker/{symbol}', + 'trades/{symbol}/hist', + 'book/{symbol}/{precision}', + 'book/{symbol}/P0', + 'book/{symbol}/P1', + 'book/{symbol}/P2', + 'book/{symbol}/P3', + 'book/{symbol}/R0', + 'stats1/{key}:{size}:{symbol}:{side}/{section}', + 'stats1/{key}:{size}:{symbol}/{section}', + 'stats1/{key}:{size}:{symbol}:long/last', + 'stats1/{key}:{size}:{symbol}:long/hist', + 'stats1/{key}:{size}:{symbol}:short/last', + 'stats1/{key}:{size}:{symbol}:short/hist', + 'candles/trade:{timeframe}:{symbol}/{section}', + 'candles/trade:{timeframe}:{symbol}/last', + 'candles/trade:{timeframe}:{symbol}/hist', + ], + 'post': [ + 'calc/trade/avg', + 'calc/fx', + ], + }, + 'private': { + 'post': [ + 'auth/r/wallets', + 'auth/r/orders/{symbol}', + 'auth/r/orders/{symbol}/new', + 'auth/r/orders/{symbol}/hist', + 'auth/r/order/{symbol}:{id}/trades', + 'auth/w/order/submit', + 'auth/r/trades/hist', + 'auth/r/trades/{symbol}/hist', + 'auth/r/positions', + 'auth/r/positions/hist', + 'auth/r/positions/audit', + 'auth/r/funding/offers/{symbol}', + 'auth/r/funding/offers/{symbol}/hist', + 'auth/r/funding/loans/{symbol}', + 'auth/r/funding/loans/{symbol}/hist', + 'auth/r/funding/credits/{symbol}', + 'auth/r/funding/credits/{symbol}/hist', + 'auth/r/funding/trades/{symbol}/hist', + 'auth/r/info/margin/{key}', + 'auth/r/info/funding/{key}', + 'auth/r/ledgers/hist', + 'auth/r/movements/hist', + 'auth/r/movements/{currency}/hist', + 'auth/r/stats/perf:{timeframe}/hist', + 'auth/r/alerts', + 'auth/w/alert/set', + 'auth/w/alert/{type}:{symbol}:{price}/del', + 'auth/calc/order/avail', + 'auth/r/ledgers/{symbol}/hist', + 'auth/r/settings', + 'auth/w/settings/set', + 'auth/w/settings/del', + 'auth/r/info/user', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.0004, + 'BCH': 0.0001, + 'ETH': 0.00135, + 'EOS': 0.0, + 'LTC': 0.001, + 'OMG': 0.15097, + 'IOT': 0.0, + 'NEO': 0.0, + 'ETC': 0.01, + 'XRP': 0.02, + 'ETP': 0.01, + 'ZEC': 0.001, + 'BTG': 0.0, + 'DASH': 0.01, + 'XMR': 0.0001, + 'QTM': 0.01, + 'EDO': 0.23687, + 'DAT': 9.8858, + 'AVT': 1.1251, + 'SAN': 0.35977, + 'USDT': 5.0, + 'SPK': 16.971, + 'BAT': 1.1209, + 'GNT': 2.8789, + 'SNT': 9.0848, + 'QASH': 1.726, + 'YYW': 7.9464, + }, + }, + }, + 'options': { + 'precision': 'R0', // P0, P1, P2, P3, P4, R0 + 'orderTypes': { + 'MARKET': undefined, + 'EXCHANGE MARKET': 'market', + 'LIMIT': undefined, + 'EXCHANGE LIMIT': 'limit', + 'STOP': undefined, + 'EXCHANGE STOP': 'stopOrLoss', + 'TRAILING STOP': undefined, + 'EXCHANGE TRAILING STOP': undefined, + 'FOK': undefined, + 'EXCHANGE FOK': 'limit FOK', + 'STOP LIMIT': undefined, + 'EXCHANGE STOP LIMIT': 'limit stop', + 'IOC': undefined, + 'EXCHANGE IOC': 'limit ioc', + }, + 'fiat': { + 'USD': 'USD', + 'EUR': 'EUR', + 'JPY': 'JPY', + 'GBP': 'GBP', + }, + }, + }); + } + + isFiat (code) { + return (code in this.options['fiat']); + } + + getCurrencyId (code) { + return 'f' + code; + } + + async fetchMarkets (params = {}) { + const response = await this.v1GetSymbolsDetails (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + let id = this.safeString (market, 'pair'); + id = id.toUpperCase (); + let baseId = undefined; + let quoteId = undefined; + if (id.indexOf (':') >= 0) { + const parts = id.split (':'); + baseId = parts[0]; + quoteId = parts[1]; + } else { + baseId = id.slice (0, 3); + quoteId = id.slice (3, 6); + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + id = 't' + id; + baseId = this.getCurrencyId (baseId); + quoteId = this.getCurrencyId (quoteId); + const precision = { + 'price': this.safeInteger (market, 'price_precision'), + 'amount': this.safeInteger (market, 'price_precision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_order_size'), + 'max': this.safeFloat (market, 'maximum_order_size'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + }; + limits['cost'] = { + 'min': limits['amount']['min'] * limits['price']['min'], + 'max': undefined, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + 'swap': false, + 'spot': false, + 'futures': false, + }); + } + return result; + } + + async fetchBalance (params = {}) { + // this api call does not return the 'used' amount - use the v1 version instead (which also returns zero balances) + await this.loadMarkets (); + const response = await this.privatePostAuthRWallets (params); + const balanceType = this.safeString (params, 'type', 'exchange'); + const result = { 'info': response }; + for (let b = 0; b < response.length; b++) { + const balance = response[b]; + const accountType = balance[0]; + let currency = balance[1]; + const total = balance[2]; + const available = balance[4]; + if (accountType === balanceType) { + if (currency[0] === 't') { + currency = currency.slice (1); + } + const code = this.safeCurrencyCode (currency); + const account = this.account (); + // do not fill in zeroes and missing values in the parser + // rewrite and unify the following to use the unified parseBalance + account['total'] = total; + if (!available) { + if (available === 0) { + account['free'] = 0; + account['used'] = total; + } else { + account['free'] = total; + } + } else { + account['free'] = available; + account['used'] = account['total'] - account['free']; + } + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const precision = this.safeValue (this.options, 'precision', 'R0'); + const request = { + 'symbol': this.marketId (symbol), + 'precision': precision, + }; + if (limit !== undefined) { + request['len'] = limit; // 25 or 100 + } + const fullRequest = this.extend (request, params); + const orderbook = await this.publicGetBookSymbolPrecision (fullRequest); + const timestamp = this.milliseconds (); + const result = { + 'bids': [], + 'asks': [], + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + const priceIndex = (fullRequest['precision'] === 'R0') ? 1 : 0; + for (let i = 0; i < orderbook.length; i++) { + const order = orderbook[i]; + const price = order[priceIndex]; + const amount = Math.abs (order[2]); + const side = (order[2] > 0) ? 'bids' : 'asks'; + result[side].push ([ price, amount ]); + } + result['bids'] = this.sortBy (result['bids'], 0, true); + result['asks'] = this.sortBy (result['asks'], 0); + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const length = ticker.length; + const last = ticker[length - 4]; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': ticker[length - 2], + 'low': ticker[length - 1], + 'bid': ticker[length - 10], + 'bidVolume': undefined, + 'ask': ticker[length - 8], + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': ticker[length - 6], + 'percentage': ticker[length - 5] * 100, + 'average': undefined, + 'baseVolume': ticker[length - 3], + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['symbols'] = ids.join (','); + } else { + request['symbols'] = 'ALL'; + } + const tickers = await this.publicGetTickers (this.extend (request, params)); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const id = ticker[0]; + if (id in this.markets_by_id) { + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.publicGetTickerSymbol (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // [ + // ID, + // MTS, // timestamp + // AMOUNT, + // PRICE + // ] + // + // fetchMyTrades (private) + // + // [ + // ID, + // PAIR, + // MTS_CREATE, + // ORDER_ID, + // EXEC_AMOUNT, + // EXEC_PRICE, + // ORDER_TYPE, + // ORDER_PRICE, + // MAKER, + // FEE, + // FEE_CURRENCY, + // ... + // ] + // + const tradeLength = trade.length; + const isPrivate = (tradeLength > 5); + const id = trade[0].toString (); + const amountIndex = isPrivate ? 4 : 2; + let amount = trade[amountIndex]; + let cost = undefined; + const priceIndex = isPrivate ? 5 : 3; + const price = trade[priceIndex]; + let side = undefined; + let orderId = undefined; + let takerOrMaker = undefined; + let type = undefined; + let fee = undefined; + let symbol = undefined; + const timestampIndex = isPrivate ? 2 : 1; + const timestamp = trade[timestampIndex]; + if (isPrivate) { + const marketId = trade[1]; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + orderId = trade[3].toString (); + takerOrMaker = (trade[8] === 1) ? 'maker' : 'taker'; + const feeCost = trade[9]; + const feeCurrency = this.safeCurrencyCode (trade[10]); + if (feeCost !== undefined) { + fee = { + 'cost': Math.abs (feeCost), + 'currency': feeCurrency, + }; + } + const orderType = trade[6]; + type = this.safeString (this.options['orderTypes'], orderType); + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + if (amount !== undefined) { + side = (amount < 0) ? 'sell' : 'buy'; + amount = Math.abs (amount); + if (cost === undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'side': side, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let sort = '-1'; + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['start'] = since; + sort = '1'; + } + if (limit !== undefined) { + request['limit'] = limit; // default 120, max 5000 + } + request['sort'] = sort; + const response = await this.publicGetTradesSymbolHist (this.extend (request, params)); + // + // [ + // [ + // ID, + // MTS, // timestamp + // AMOUNT, + // PRICE + // ] + // ] + // + const trades = this.sortBy (response, 1); + return this.parseTrades (trades, market, undefined, limit); + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 100, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 100; // default 100, max 5000 + } + if (since === undefined) { + since = this.milliseconds () - this.parseTimeframe (timeframe) * limit * 1000; + } + const request = { + 'symbol': market['id'], + 'timeframe': this.timeframes[timeframe], + 'sort': 1, + 'start': since, + 'limit': limit, + }; + const response = await this.publicGetCandlesTradeTimeframeSymbolHist (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + throw new NotSupported (this.id + ' createOrder not implemented yet'); + } + + cancelOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' cancelOrder not implemented yet'); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrder not implemented yet'); + } + + async fetchDepositAddress (currency, params = {}) { + throw new NotSupported (this.id + ' fetchDepositAddress() not implemented yet.'); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + throw new NotSupported (this.id + ' withdraw not implemented yet'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + 'end': this.milliseconds (), + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default 25, max 1000 + } + let method = 'privatePostAuthRTradesHist'; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + method = 'privatePostAuthRTradesSymbolHist'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'v1') { + request = api + request; + } else { + request = this.version + request; + } + let url = this.urls['api'][api] + '/' + request; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + body = this.json (query); + const auth = '/api/' + request + nonce + body; + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha384'); + headers = { + 'bfx-nonce': nonce, + 'bfx-apikey': this.apiKey, + 'bfx-signature': signature, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (response) { + if ('message' in response) { + if (response['message'].indexOf ('not enough exchange balance') >= 0) { + throw new InsufficientFunds (this.id + ' ' + this.json (response)); + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } else if (response === '') { + throw new ExchangeError (this.id + ' returned empty response'); + } + return response; + } +}; + +},{"./base/errors":9,"./bitfinex.js":31}],33:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitflyer extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitflyer', + 'name': 'bitFlyer', + 'countries': [ 'JP' ], + 'version': 'v1', + 'rateLimit': 1000, // their nonce-timestamp is in seconds... + 'has': { + 'CORS': false, + 'withdraw': true, + 'fetchMyTrades': true, + 'fetchOrders': true, + 'fetchOrder': 'emulated', + 'fetchOpenOrders': 'emulated', + 'fetchClosedOrders': 'emulated', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28051642-56154182-660e-11e7-9b0d-6042d1e6edd8.jpg', + 'api': 'https://api.bitflyer.jp', + 'www': 'https://bitflyer.jp', + 'doc': 'https://lightning.bitflyer.com/docs?lang=en', + }, + 'api': { + 'public': { + 'get': [ + 'getmarkets/usa', // new (wip) + 'getmarkets/eu', // new (wip) + 'getmarkets', // or 'markets' + 'getboard', // ... + 'getticker', + 'getexecutions', + 'gethealth', + 'getboardstate', + 'getchats', + ], + }, + 'private': { + 'get': [ + 'getpermissions', + 'getbalance', + 'getbalancehistory', + 'getcollateral', + 'getcollateralhistory', + 'getcollateralaccounts', + 'getaddresses', + 'getcoinins', + 'getcoinouts', + 'getbankaccounts', + 'getdeposits', + 'getwithdrawals', + 'getchildorders', + 'getparentorders', + 'getparentorder', + 'getexecutions', + 'getpositions', + 'gettradingcommission', + ], + 'post': [ + 'sendcoin', + 'withdraw', + 'sendchildorder', + 'cancelchildorder', + 'sendparentorder', + 'cancelparentorder', + 'cancelallchildorders', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'BTC/JPY': { + 'maker': 0.15 / 100, + 'taker': 0.15 / 100, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const jp_markets = await this.publicGetGetmarkets (params); + const us_markets = await this.publicGetGetmarketsUsa (params); + const eu_markets = await this.publicGetGetmarketsEu (params); + let markets = this.arrayConcat (jp_markets, us_markets); + markets = this.arrayConcat (markets, eu_markets); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'product_code'); + const currencies = id.split ('_'); + let baseId = undefined; + let quoteId = undefined; + let base = undefined; + let quote = undefined; + const numCurrencies = currencies.length; + if (numCurrencies === 1) { + baseId = id.slice (0, 3); + quoteId = id.slice (3, 6); + } else if (numCurrencies === 2) { + baseId = currencies[0]; + quoteId = currencies[1]; + } else { + baseId = currencies[1]; + quoteId = currencies[2]; + } + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + const symbol = (numCurrencies === 2) ? (base + '/' + quote) : id; + const fees = this.safeValue (this.fees, symbol, this.fees['trading']); + let maker = this.safeValue (fees, 'maker', this.fees['trading']['maker']); + let taker = this.safeValue (fees, 'taker', this.fees['trading']['taker']); + let spot = true; + let future = false; + let type = 'spot'; + if (('alias' in market) || (currencies[0] === 'FX')) { + type = 'future'; + future = true; + spot = false; + maker = 0.0; + taker = 0.0; + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'maker': maker, + 'taker': taker, + 'type': type, + 'spot': spot, + 'future': future, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetGetbalance (params); + // + // [ + // { + // "currency_code": "JPY", + // "amount": 1024078, + // "available": 508000 + // }, + // { + // "currency_code": "BTC", + // "amount": 10.24, + // "available": 4.12 + // }, + // { + // "currency_code": "ETH", + // "amount": 20.48, + // "available": 16.38 + // } + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'amount'); + account['free'] = this.safeFloat (balance, 'available'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + }; + const orderbook = await this.publicGetGetboard (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'size'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + }; + const ticker = await this.publicGetGetticker (this.extend (request, params)); + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + const last = this.safeFloat (ticker, 'ltp'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'best_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'best_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume_by_product'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + let side = this.safeStringLower (trade, 'side'); + if (side !== undefined) { + if (side.length < 1) { + side = undefined; + } + } + let order = undefined; + if (side !== undefined) { + const id = side + '_child_order_acceptance_id'; + if (id in trade) { + order = trade[id]; + } + } + if (order === undefined) { + order = this.safeString (trade, 'child_order_acceptance_id'); + } + const timestamp = this.parse8601 (this.safeString (trade, 'exec_date')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const id = this.safeString (trade, 'id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': order, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_code': market['id'], + }; + const response = await this.publicGetGetexecutions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + 'child_order_type': type.toUpperCase (), + 'side': side.toUpperCase (), + 'price': price, + 'size': amount, + }; + const result = await this.privatePostSendchildorder (this.extend (request, params)); + // { "status": - 200, "error_message": "Insufficient funds", "data": null } + const id = this.safeString (result, 'child_order_acceptance_id'); + return { + 'info': result, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder() requires a `symbol` argument'); + } + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + 'child_order_acceptance_id': id, + }; + return await this.privatePostCancelchildorder (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'ACTIVE': 'open', + 'COMPLETED': 'closed', + 'CANCELED': 'canceled', + 'EXPIRED': 'canceled', + 'REJECTED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const timestamp = this.parse8601 (this.safeString (order, 'child_order_date')); + const amount = this.safeFloat (order, 'size'); + const remaining = this.safeFloat (order, 'outstanding_size'); + const filled = this.safeFloat (order, 'executed_size'); + const price = this.safeFloat (order, 'price'); + const cost = price * filled; + const status = this.parseOrderStatus (this.safeString (order, 'child_order_state')); + const type = this.safeStringLower (order, 'child_order_type'); + const side = this.safeStringLower (order, 'side'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'product_code'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCost = this.safeFloat (order, 'total_commission'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': undefined, + 'rate': undefined, + }; + } + const id = this.safeString (order, 'child_order_acceptance_id'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_code': market['id'], + 'count': limit, + }; + const response = await this.privateGetGetchildorders (this.extend (request, params)); + let orders = this.parseOrders (response, market, since, limit); + if (symbol !== undefined) { + orders = this.filterBy (orders, 'symbol', symbol); + } + return orders; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { + const request = { + 'child_order_state': 'ACTIVE', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { + const request = { + 'child_order_state': 'COMPLETED', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a `symbol` argument'); + } + const orders = await this.fetchOrders (symbol); + const ordersById = this.indexBy (orders, 'id'); + if (id in ordersById) { + return ordersById[id]; + } + throw new OrderNotFound (this.id + ' No order found with id ' + id); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_code': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privateGetGetexecutions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + if (code !== 'JPY' && code !== 'USD' && code !== 'EUR') { + throw new ExchangeError (this.id + ' allows withdrawing JPY, USD, EUR only, ' + code + ' is not supported'); + } + const currency = this.currency (code); + const request = { + 'currency_code': currency['id'], + 'amount': amount, + // 'bank_account_id': 1234, + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + const id = this.safeString (response, 'message_id'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.version + '/'; + if (api === 'private') { + request += 'me/'; + } + request += path; + if (method === 'GET') { + if (Object.keys (params).length) { + request += '?' + this.urlencode (params); + } + } + const url = this.urls['api'] + request; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = [ nonce, method, request ].join (''); + if (Object.keys (params).length) { + if (method !== 'GET') { + body = this.json (params); + auth += body; + } + } + headers = { + 'ACCESS-KEY': this.apiKey, + 'ACCESS-TIMESTAMP': nonce, + 'ACCESS-SIGN': this.hmac (this.encode (auth), this.encode (this.secret)), + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],34:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, OrderNotFound, InsufficientFunds, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitforex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitforex', + 'name': 'Bitforex', + 'countries': [ 'CN' ], + 'version': 'v1', + 'has': { + 'fetchBalance': true, + 'fetchMarkets': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchMyTrades': false, + 'fetchTrades': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOHLCV': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + '1M': '1month', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/44310033-69e9e600-a3d8-11e8-873d-54d74d1bc4e4.jpg', + 'api': 'https://api.bitforex.com', + 'www': 'https://www.bitforex.com', + 'doc': 'https://github.com/githubdev2020/API_Doc_en/wiki', + 'fees': 'https://help.bitforex.com/en_us/?cat=13', + 'referral': 'https://www.bitforex.com/en/invitationRegister?inviterId=1867438', + }, + 'api': { + 'public': { + 'get': [ + 'api/v1/market/symbols', + 'api/v1/market/ticker', + 'api/v1/market/depth', + 'api/v1/market/trades', + 'api/v1/market/kline', + ], + }, + 'private': { + 'post': [ + 'api/v1/fund/mainAccount', + 'api/v1/fund/allAccount', + 'api/v1/trade/placeOrder', + 'api/v1/trade/placeMultiOrder', + 'api/v1/trade/cancelOrder', + 'api/v1/trade/orderInfo', + 'api/v1/trade/orderInfos', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': true, + 'deposit': {}, + 'withdraw': { + 'BTC': 0.0005, + 'ETH': 0.01, + 'BCH': 0.0001, + 'LTC': 0.001, + 'ETC': 0.005, + 'USDT': 5, + 'CMCT': 30, + 'AION': 3, + 'LVT': 0, + 'DATA': 40, + 'RHP': 50, + 'NEO': 0, + 'AIDOC': 10, + 'BQT': 2, + 'R': 2, + 'DPY': 0.8, + 'GTC': 40, + 'AGI': 30, + 'DENT': 100, + 'SAN': 1, + 'SPANK': 8, + 'AID': 5, + 'OMG': 0.1, + 'BFT': 5, + 'SHOW': 150, + 'TRX': 20, + 'ABYSS': 10, + 'THM': 25, + 'ZIL': 20, + 'PPT': 0.2, + 'WTC': 0.4, + 'LRC': 7, + 'BNT': 1, + 'CTXC': 1, + 'MITH': 20, + 'TRUE': 4, + 'LYM': 10, + 'VEE': 100, + 'AUTO': 200, + 'REN': 50, + 'TIO': 2.5, + 'NGC': 1.5, + 'PST': 10, + 'CRE': 200, + 'IPC': 5, + 'PTT': 1000, + 'XMCT': 20, + 'ATMI': 40, + 'TERN': 40, + 'XLM': 0.01, + 'ODE': 15, + 'FTM': 100, + 'RTE': 100, + 'DCC': 100, + 'IMT': 500, + 'GOT': 3, + 'EGT': 500, + 'DACC': 1000, + 'UBEX': 500, + 'ABL': 100, + 'OLT': 100, + 'DAV': 40, + 'THRT': 10, + 'RMESH': 3, + 'UPP': 20, + 'SDT': 0, + 'SHR': 10, + 'MTV': 3, + 'ESS': 100, + 'MET': 3, + 'TTC': 20, + 'LXT': 10, + 'XCLP': 100, + 'LUK': 100, + 'UBC': 100, + 'DTX': 10, + 'BEAT': 20, + 'DEED': 2, + 'BGX': 3000, + 'PRL': 20, + 'ELY': 50, + 'CARD': 300, + 'SQR': 15, + 'VRA': 400, + 'BWX': 3500, + 'MAS': 75, + 'FLP': 0.6, + 'UNC': 300, + 'CRNC': 15, + 'MFG': 70, + 'ZXC': 70, + 'TRT': 30, + 'ZIX': 35, + 'XRA': 10, + 'AMO': 1600, + 'IPG': 3, + 'uDoo': 50, + 'URB': 30, + 'ARCONA': 3, + 'CRAD': 5, + 'NOBS': 1000, + 'ADF': 2, + 'ELF': 5, + 'LX': 20, + 'PATH': 15, + 'SILK': 120, + 'SKYFT': 50, + 'EDN': 50, + 'ADE': 50, + 'EDR': 10, + 'TIME': 0.25, + 'SPRK': 20, + 'QTUM': 0.01, + 'BF': 5, + 'ZPR': 100, + 'HYB': 10, + 'CAN': 30, + 'CEL': 10, + 'ATS': 50, + 'KCASH': 1, + 'ACT': 0.01, + 'MT': 300, + 'DXT': 30, + 'WAB': 4000, + 'HYDRO': 400, + 'LQD': 5, + 'OPTC': 200, + 'EQUAD': 80, + 'LATX': 50, + 'LEDU': 100, + 'RIT': 70, + 'ACDC': 500, + 'FSN': 2, + }, + }, + }, + 'exceptions': { + '4004': OrderNotFound, + '1013': AuthenticationError, + '1016': AuthenticationError, + '3002': InsufficientFunds, + '10204': DDoSProtection, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetApiV1MarketSymbols (params); + const data = response['data']; + const result = []; + for (let i = 0; i < data.length; i++) { + const market = data[i]; + const id = this.safeString (market, 'symbol'); + const symbolParts = id.split ('-'); + const baseId = symbolParts[2]; + const quoteId = symbolParts[1]; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = true; + const precision = { + 'amount': this.safeInteger (market, 'amountPrecision'), + 'price': this.safeInteger (market, 'pricePrecision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minOrderAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'time'); + const id = this.safeString (trade, 'tid'); + const orderId = undefined; + const amount = this.safeFloat (trade, 'amount'); + const price = this.safeFloat (trade, 'price'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const sideId = this.safeInteger (trade, 'direction'); + const side = this.parseSide (sideId); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'order': orderId, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['size'] = limit; + } + const market = this.market (symbol); + const response = await this.publicGetApiV1MarketTrades (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostApiV1FundAllAccount (params); + const data = response['data']; + const result = { 'info': response }; + for (let i = 0; i < data.length; i++) { + const balance = data[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'frozen'); + account['free'] = this.safeFloat (balance, 'active'); + account['total'] = this.safeFloat (balance, 'fix'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.markets[symbol]; + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetApiV1MarketTicker (this.extend (request, params)); + const data = response['data']; + const timestamp = this.safeInteger (data, 'date'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (data, 'high'), + 'low': this.safeFloat (data, 'low'), + 'bid': this.safeFloat (data, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (data, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': this.safeFloat (data, 'last'), + 'last': this.safeFloat (data, 'last'), + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (data, 'vol'), + 'quoteVolume': undefined, + 'info': response, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'vol'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'ktype': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['size'] = limit; // default 1, max 600 + } + const response = await this.publicGetApiV1MarketKline (this.extend (request, params)); + const ohlcvs = this.safeValue (response, 'data', []); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'symbol': marketId, + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetApiV1MarketDepth (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (response, 'time'); + return this.parseOrderBook (data, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'canceled', + '4': 'canceled', + }; + return (status in statuses) ? statuses[status] : status; + } + + parseSide (sideId) { + if (sideId === 1) { + return 'buy'; + } else if (sideId === 2) { + return 'sell'; + } else { + return undefined; + } + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'orderId'); + const timestamp = this.safeFloat (order, 'createTime'); + const lastTradeTimestamp = this.safeFloat (order, 'lastTime'); + const symbol = market['symbol']; + const sideId = this.safeInteger (order, 'tradeType'); + const side = this.parseSide (sideId); + const type = undefined; + const price = this.safeFloat (order, 'orderPrice'); + const average = this.safeFloat (order, 'avgPrice'); + const amount = this.safeFloat (order, 'orderAmount'); + const filled = this.safeFloat (order, 'dealAmount'); + const remaining = amount - filled; + const status = this.parseOrderStatus (this.safeString (order, 'orderState')); + const cost = filled * price; + const feeSide = (side === 'buy') ? 'base' : 'quote'; + const feeCurrency = market[feeSide]; + const fee = { + 'cost': this.safeFloat (order, 'tradeFee'), + 'currency': feeCurrency, + }; + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': this.marketId (symbol), + 'orderId': id, + }; + const response = await this.privatePostApiV1TradeOrderInfo (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + return order; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': this.marketId (symbol), + 'state': 0, + }; + const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': this.marketId (symbol), + 'state': 1, + }; + const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let sideId = undefined; + if (side === 'buy') { + sideId = 1; + } else if (side === 'sell') { + sideId = 2; + } + const request = { + 'symbol': this.marketId (symbol), + 'price': price, + 'amount': amount, + 'tradeType': sideId, + }; + const response = await this.privatePostApiV1TradePlaceOrder (this.extend (request, params)); + const data = response['data']; + return { + 'info': response, + 'id': this.safeString (data, 'orderId'), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + if (symbol !== undefined) { + request['symbol'] = this.marketId (symbol); + } + const results = await this.privatePostApiV1TradeCancelOrder (this.extend (request, params)); + const success = results['success']; + const returnVal = { 'info': results, 'success': success }; + return returnVal; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + let payload = this.urlencode ({ 'accessKey': this.apiKey }); + query['nonce'] = this.milliseconds (); + if (Object.keys (query).length) { + payload += '&' + this.urlencode (this.keysort (query)); + } + // let message = '/' + 'api/' + this.version + '/' + path + '?' + payload; + const message = '/' + path + '?' + payload; + const signature = this.hmac (this.encode (message), this.encode (this.secret)); + body = payload + '&signData=' + signature; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (typeof body !== 'string') { + return; // fallback to default error handler + } + if ((body[0] === '{') || (body[0] === '[')) { + const feedback = this.id + ' ' + body; + const success = this.safeValue (response, 'success'); + if (success !== undefined) { + if (!success) { + const code = this.safeString (response, 'code'); + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],35:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, AuthenticationError, BadRequest, PermissionDenied, InvalidAddress } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bithumb extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bithumb', + 'name': 'Bithumb', + 'countries': [ 'KR' ], // South Korea + 'rateLimit': 500, + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30597177-ea800172-9d5e-11e7-804c-b9d4fa9b56b0.jpg', + 'api': { + 'public': 'https://api.bithumb.com/public', + 'private': 'https://api.bithumb.com', + }, + 'www': 'https://www.bithumb.com', + 'doc': 'https://apidocs.bithumb.com', + 'fees': 'https://en.bithumb.com/customer_support/info_fee', + }, + 'api': { + 'public': { + 'get': [ + 'ticker/{currency}', + 'ticker/all', + 'orderbook/{currency}', + 'orderbook/all', + 'transaction_history/{currency}', + 'transaction_history/all', + ], + }, + 'private': { + 'post': [ + 'info/account', + 'info/balance', + 'info/wallet_address', + 'info/ticker', + 'info/orders', + 'info/user_transactions', + 'trade/place', + 'info/order_detail', + 'trade/cancel', + 'trade/btc_withdrawal', + 'trade/krw_deposit', + 'trade/krw_withdrawal', + 'trade/market_buy', + 'trade/market_sell', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.25 / 100, + 'taker': 0.25 / 100, + }, + }, + 'exceptions': { + 'Bad Request(SSL)': BadRequest, + 'Bad Request(Bad Method)': BadRequest, + 'Bad Request.(Auth Data)': AuthenticationError, // { "status": "5100", "message": "Bad Request.(Auth Data)" } + 'Not Member': AuthenticationError, + 'Invalid Apikey': AuthenticationError, // {"status":"5300","message":"Invalid Apikey"} + 'Method Not Allowed.(Access IP)': PermissionDenied, + 'Method Not Allowed.(BTC Adress)': InvalidAddress, + 'Method Not Allowed.(Access)': PermissionDenied, + 'Database Fail': ExchangeNotAvailable, + 'Invalid Parameter': BadRequest, + '5600': ExchangeError, + 'Unknown Error': ExchangeError, + 'After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions': ExchangeError, // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"} + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTickerAll (params); + const data = this.safeValue (response, 'data'); + const currencyIds = Object.keys (data); + const result = []; + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + if (currencyId === 'date') { + continue; + } + const market = data[currencyId]; + const base = currencyId; + const quote = 'KRW'; + const symbol = currencyId + '/' + quote; + let active = true; + if (Array.isArray (market)) { + const numElements = market.length; + if (numElements === 0) { + active = false; + } + } + result.push ({ + 'id': currencyId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'info': market, + 'active': active, + 'precision': { + 'amount': undefined, + 'price': undefined, + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'currency': 'ALL', + }; + const response = await this.privatePostInfoBalance (this.extend (request, params)); + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const account = this.account (); + const currency = this.currency (code); + const lowerCurrencyId = this.safeStringLower (currency, 'id'); + account['total'] = this.safeFloat (balances, 'total_' + lowerCurrencyId); + account['used'] = this.safeFloat (balances, 'in_use_' + lowerCurrencyId); + account['free'] = this.safeFloat (balances, 'available_' + lowerCurrencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['base'], + }; + if (limit !== undefined) { + request['count'] = limit; // max = 50 + } + const response = await this.publicGetOrderbookCurrency (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'quantity'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const open = this.safeFloat (ticker, 'opening_price'); + const close = this.safeFloat (ticker, 'closing_price'); + let change = undefined; + let percentage = undefined; + let average = undefined; + if ((close !== undefined) && (open !== undefined)) { + change = close - open; + if (open > 0) { + percentage = change / open * 100; + } + average = this.sum (open, close) / 2; + } + const baseVolume = this.safeFloat (ticker, 'units_traded_24H'); + const quoteVolume = this.safeFloat (ticker, 'acc_trade_value_24H'); + let vwap = undefined; + if (quoteVolume !== undefined && baseVolume !== undefined) { + vwap = quoteVolume / baseVolume; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'max_price'), + 'low': this.safeFloat (ticker, 'min_price'), + 'bid': this.safeFloat (ticker, 'buy_price'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell_price'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': open, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickerAll (params); + const result = {}; + const timestamp = this.safeInteger (response['data'], 'date'); + const tickers = this.omit (response['data'], 'date'); + const ids = Object.keys (tickers); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + const ticker = tickers[id]; + const isArray = Array.isArray (ticker); + if (!isArray) { + ticker['date'] = timestamp; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['base'], + }; + const response = await this.publicGetTickerCurrency (this.extend (request, params)); + return this.parseTicker (response['data'], market); + } + + parseTrade (trade, market = undefined) { + // a workaround for their bug in date format, hours are not 0-padded + const parts = trade['transaction_date'].split (' '); + const transaction_date = parts[0]; + let transaction_time = parts[1]; + if (transaction_time.length < 8) { + transaction_time = '0' + transaction_time; + } + let timestamp = this.parse8601 (transaction_date + ' ' + transaction_time); + timestamp -= 9 * 3600000; // they report UTC + 9 hours (server in Korean timezone) + const type = undefined; + let side = this.safeString (trade, 'type'); + side = (side === 'ask') ? 'sell' : 'buy'; + const id = this.safeString (trade, 'cont_no'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'units_traded'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['base'], + }; + if (limit === undefined) { + request['count'] = limit; // default 20, max 100 + } + const response = await this.publicGetTransactionHistoryCurrency (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let request = undefined; + let method = 'privatePostTrade'; + if (type === 'limit') { + request = { + 'order_currency': market['id'], + 'Payment_currency': market['quote'], + 'units': amount, + 'price': price, + 'type': (side === 'buy') ? 'bid' : 'ask', + }; + method += 'Place'; + } else if (type === 'market') { + request = { + 'currency': market['id'], + 'units': amount, + }; + method += 'Market' + this.capitalize (side); + } + const response = await this[method] (this.extend (request, params)); + const id = this.safeString (response, 'order_id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const side_in_params = ('side' in params); + if (!side_in_params) { + throw new ExchangeError (this.id + ' cancelOrder requires a `side` parameter (sell or buy) and a `currency` parameter'); + } + const currency = this.safeString (params, 'currency'); + if (currency === undefined) { + throw new ExchangeError (this.id + ' cancelOrder requires a `currency` parameter (a currency id)'); + } + const side = (params['side'] === 'buy') ? 'bid' : 'ask'; + params = this.omit (params, [ 'side', 'currency' ]); + const request = { + 'order_id': id, + 'type': side, + 'currency': currency, + }; + return await this.privatePostTradeCancel (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'units': amount, + 'address': address, + 'currency': currency['id'], + }; + if (currency === 'XRP' || currency === 'XMR') { + const destination = this.safeString (params, 'destination'); + if ((tag === undefined) && (destination === undefined)) { + throw new ExchangeError (this.id + ' ' + code + ' withdraw() requires a tag argument or an extra destination param'); + } else if (tag !== undefined) { + request['destination'] = tag; + } + } + const response = await this.privatePostTradeBtcWithdrawal (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const endpoint = '/' + this.implodeParams (path, params); + let url = this.urls['api'][api] + endpoint; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'endpoint': endpoint, + }, query)); + const nonce = this.nonce ().toString (); + const auth = endpoint + "\0" + body + "\0" + nonce; // eslint-disable-line quotes + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); + const signature64 = this.decode (this.stringToBase64 (this.encode (signature))); + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Api-Key': this.apiKey, + 'Api-Sign': signature64.toString (), + 'Api-Nonce': nonce, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('status' in response) { + // + // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"} + // + const status = this.safeString (response, 'status'); + const message = this.safeString (response, 'message'); + if (status !== undefined) { + if (status === '0000') { + return; // no error + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, status, feedback); + this.throwExactlyMatchedException (this.exceptions, message, feedback); + throw new ExchangeError (feedback); + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('status' in response) { + if (response['status'] === '0000') { + return response; + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],36:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const zb = require ('./zb.js'); + +// --------------------------------------------------------------------------- + +module.exports = class bitkk extends zb { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitkk', + 'name': 'bitkk', + 'comment': 'a Chinese ZB clone', + 'urls': { + 'api': { + 'public': 'http://api.bitkk.com/data', // no https for public API + 'private': 'https://trade.bitkk.com/api', + }, + 'www': 'https://www.bitkk.com', + 'doc': 'https://www.bitkk.com/i/developer', + 'fees': 'https://www.bitkk.com/i/rate', + }, + }); + } +}; + +},{"./zb.js":174}],37:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitlish extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitlish', + 'name': 'Bitlish', + 'countries': [ 'GB', 'EU', 'RU' ], + 'rateLimit': 1500, + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'withdraw': true, + }, + 'timeframes': { + '1h': 3600, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766275-dcfc6c30-5ed3-11e7-839d-00a846385d0b.jpg', + 'api': 'https://bitlish.com/api', + 'www': 'https://bitlish.com', + 'doc': 'https://bitlish.com/api', + 'fees': 'https://bitlish.com/fees', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.3 / 100, // anonymous 0.3%, verified 0.2% + 'maker': 0.2 / 100, // anonymous 0.2%, verified 0.1% + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'LTC': 0.001, + 'DOGE': 0.001, + 'ETH': 0.001, + 'XMR': 0, + 'ZEC': 0.001, + 'DASH': 0.0001, + 'EUR': 50, + }, + 'deposit': { + 'BTC': 0, + 'LTC': 0, + 'DOGE': 0, + 'ETH': 0, + 'XMR': 0, + 'ZEC': 0, + 'DASH': 0, + 'EUR': 0, + }, + }, + }, + 'api': { + 'public': { + 'get': [ + 'instruments', + 'ohlcv', + 'pairs', + 'tickers', + 'trades_depth', + 'trades_history', + ], + 'post': [ + 'instruments', + 'ohlcv', + 'pairs', + 'tickers', + 'trades_depth', + 'trades_history', + ], + }, + 'private': { + 'post': [ + 'accounts_operations', + 'balance', + 'cancel_trade', + 'cancel_trades_by_ids', + 'cancel_all_trades', + 'create_bcode', + 'create_template_wallet', + 'create_trade', + 'deposit', + 'list_accounts_operations_from_ts', + 'list_active_trades', + 'list_bcodes', + 'list_my_matches_from_ts', + 'list_my_trades', + 'list_my_trads_from_ts', + 'list_payment_methods', + 'list_payments', + 'redeem_code', + 'resign', + 'signin', + 'signout', + 'trade_details', + 'trade_options', + 'withdraw', + 'withdraw_by_id', + ], + }, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + 'XDG': 'DOGE', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetPairs (params); + const result = []; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = response[key]; + const id = this.safeString (market, 'id'); + const name = this.safeString (market, 'name'); + const [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'first'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'prc') * 100, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'sum'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTickers (params); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.safeValue (this.markets_by_id, id); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTickers (params); + const marketId = market['id']; + return this.parseTicker (response[marketId], market); + } + + async fetchOHLCV (symbol, timeframe = '1h', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // let market = this.market (symbol); + const now = this.seconds (); + let start = now - 86400 * 30; // last 30 days + if (since !== undefined) { + start = parseInt (since / 1000); + } + const interval = [ start.toString (), undefined ]; + const request = { + 'time_range': interval, + }; + return await this.publicPostOhlcv (this.extend (request, params)); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_id': this.marketId (symbol), + }; + const response = await this.publicGetTradesDepth (this.extend (request, params)); + let timestamp = undefined; + const last = this.safeInteger (response, 'last'); + if (last !== undefined) { + timestamp = parseInt (last / 1000); + } + return this.parseOrderBook (response, timestamp, 'bid', 'ask', 'price', 'volume'); + } + + parseTrade (trade, market = undefined) { + const side = (trade['dir'] === 'bid') ? 'buy' : 'sell'; + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = this.safeInteger (trade, 'created'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp / 1000); + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTradesHistory (this.extend ({ + 'pair_id': market['id'], + }, params)); + return this.parseTrades (response['list'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const currencyIds = Object.keys (response); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + const balance = this.safeValue (response, currencyId, {}); + account['free'] = this.safeFloat (balance, 'funds'); + account['used'] = this.safeFloat (balance, 'holded'); + result[code] = account; + } + return this.parseBalance (result); + } + + async signIn (params = {}) { + const request = { + 'login': this.login, + 'passwd': this.password, + }; + return await this.privatePostSignin (this.extend (request, params)); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_id': this.marketId (symbol), + 'dir': (side === 'buy') ? 'bid' : 'ask', + 'amount': amount, + }; + if (type === 'limit') { + request['price'] = price; + } + const response = await this.privatePostCreateTrade (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelTrade (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + if (code !== 'BTC') { + // they did not document other types... + throw new NotSupported (this.id + ' currently supports BTC withdrawals only, until they document other currencies...'); + } + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'account': address, + 'payment_method': 'bitcoin', // they did not document other types... + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': response['message_id'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (method === 'GET') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + body = this.json (params); + headers = { 'Content-Type': 'application/json' }; + } + } else { + this.checkRequiredCredentials (); + body = this.json (this.extend ({ 'token': this.apiKey }, params)); + headers = { 'Content-Type': 'application/json' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],38:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ArgumentsRequired, ExchangeError, InvalidOrder, BadRequest, OrderNotFound, DDoSProtection, BadSymbol } = require ('./base/errors'); +// --------------------------------------------------------------------------- + +module.exports = class bitmart extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitmart', + 'name': 'BitMart', + 'countries': [ 'US', 'CN', 'HK', 'KR' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': true, + 'fetchMarkets': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchCurrencies': true, + 'fetchOrderBook': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchBalance': true, + 'createOrder': true, + 'cancelOrder': true, + 'cancelAllOrders': true, + 'fetchOrders': false, + 'fetchOrderTrades': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchCanceledOrders': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg', + 'api': 'https://openapi.bitmart.com', + 'www': 'https://www.bitmart.com/', + 'doc': 'https://github.com/bitmartexchange/bitmart-official-api-docs', + 'referral': 'http://www.bitmart.com/?r=rQCFLh', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'token': { + 'post': [ + 'authentication', + ], + }, + 'public': { + 'get': [ + 'currencies', + 'ping', + 'steps', + 'symbols', + 'symbols_details', + 'symbols/{symbol}/kline', + 'symbols/{symbol}/orders', + 'symbols/{symbol}/trades', + 'ticker', + 'time', + ], + }, + 'private': { + 'get': [ + 'orders', + 'orders/{id}', + 'trades', + 'wallet', + ], + 'post': [ + 'orders', + ], + 'delete': [ + 'orders', + 'orders/{id}', + ], + }, + }, + 'timeframes': { + '1m': 1, + '3m': 3, + '5m': 5, + '15m': 15, + '30m': 30, + '45m': 45, + '1h': 60, + '2h': 120, + '3h': 180, + '4h': 240, + '1d': 1440, + '1w': 10080, + '1M': 43200, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.002, + 'maker': 0.001, + 'tiers': { + 'taker': [ + [0, 0.20 / 100], + [10, 0.18 / 100], + [50, 0.16 / 100], + [250, 0.14 / 100], + [1000, 0.12 / 100], + [5000, 0.10 / 100], + [25000, 0.08 / 100], + [50000, 0.06 / 100], + ], + 'maker': [ + [0, 0.1 / 100], + [10, 0.09 / 100], + [50, 0.08 / 100], + [250, 0.07 / 100], + [1000, 0.06 / 100], + [5000, 0.05 / 100], + [25000, 0.04 / 100], + [50000, 0.03 / 100], + ], + }, + }, + }, + 'exceptions': { + 'exact': { + 'Place order error': InvalidOrder, // {"message":"Place order error"} + 'Not found': OrderNotFound, // {"message":"Not found"} + 'Visit too often, please try again later': DDoSProtection, // {"code":-30,"msg":"Visit too often, please try again later","subMsg":"","data":{}} + 'Unknown symbol': BadSymbol, // {"message":"Unknown symbol"} + }, + 'broad': { + 'Invalid limit. limit must be in the range': InvalidOrder, + 'Maximum price is': InvalidOrder, // {"message":"Maximum price is 0.112695"} + // {"message":"Required Integer parameter 'status' is not present"} + // {"message":"Required String parameter 'symbol' is not present"} + // {"message":"Required Integer parameter 'offset' is not present"} + // {"message":"Required Integer parameter 'limit' is not present"} + // {"message":"Required Long parameter 'from' is not present"} + // {"message":"Required Long parameter 'to' is not present"} + 'is not present': BadRequest, + }, + }, + 'commonCurrencies': { + 'ONE': 'Menlo One', + }, + }); + } + + async fetchTime (params = {}) { + const response = await this.publicGetTime (params); + // + // { + // "server_time": 1527777538000 + // } + // + return this.safeInteger (response, 'server_time'); + } + + async signIn (params = {}) { + const message = this.apiKey + ':' + this.secret + ':' + this.uid; + const data = { + 'grant_type': 'client_credentials', + 'client_id': this.apiKey, + 'client_secret': this.hmac (this.encode (message), this.encode (this.secret), 'sha256'), + }; + const response = await this.tokenPostAuthentication (this.extend (data, params)); + const accessToken = this.safeString (response, 'access_token'); + if (!accessToken) { + throw new AuthenticationError (this.id + ' signIn() failed to authenticate. Access token missing from response.'); + } + const expiresIn = this.safeInteger (response, 'expires_in'); + this.options['expires'] = this.sum (this.nonce (), expiresIn * 1000); + this.options['accessToken'] = accessToken; + return response; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetSymbolsDetails (params); + // + // [ + // { + // "id":"1SG_BTC", + // "base_currency":"1SG", + // "quote_currency":"BTC", + // "quote_increment":"0.1", + // "base_min_size":"0.1000000000", + // "base_max_size":"10000000.0000000000", + // "price_min_precision":4, + // "price_max_precision":6, + // "expiration":"NA" + // } + // ] + // + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + // + // https://github.com/bitmartexchange/bitmart-official-api-docs/blob/master/rest/public/symbols_details.md#response-details + // from the above API doc: + // quote_increment Minimum order price as well as the price increment + // price_min_precision Minimum price precision (digit) used to query price and kline + // price_max_precision Maximum price precision (digit) used to query price and kline + // + // the docs are wrong: https://github.com/ccxt/ccxt/issues/5612 + // + const quoteIncrement = this.safeString (market, 'quote_increment'); + const amountPrecision = this.precisionFromString (quoteIncrement); + const pricePrecision = this.safeInteger (market, 'price_max_precision'); + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'base_min_size'), + 'max': this.safeFloat (market, 'base_max_size'), + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + const marketId = this.safeString (ticker, 'symbol_id'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const last = this.safeFloat (ticker, 'current_price'); + const percentage = this.safeFloat (ticker, 'fluctuation'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highest_price'), + 'low': this.safeFloat (ticker, 'lowest_price'), + 'bid': this.safeFloat (ticker, 'bid_1'), + 'bidVolume': this.safeFloat (ticker, 'bid_1_amount'), + 'ask': this.safeFloat (ticker, 'ask_1'), + 'askVolume': this.safeFloat (ticker, 'ask_1_amount'), + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': percentage * 100, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'base_volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + // + // { + // "volume":"97487.38", + // "ask_1":"0.00148668", + // "base_volume":"144.59", + // "lowest_price":"0.00144362", + // "bid_1":"0.00148017", + // "highest_price":"0.00151000", + // "ask_1_amount":"92.03", + // "current_price":"0.00148230", + // "fluctuation":"+0.0227", + // "symbol_id":"XRP_ETH", + // "url":"https://www.bitmart.com/trade?symbol=XRP_ETH", + // "bid_1_amount":"134.78" + // } + // + return this.parseTicker (response); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTicker (params); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchCurrencies (params = {}) { + const currencies = await this.publicGetCurrencies (params); + // + // [ + // { + // "name":"CNY1", + // "withdraw_enabled":false, + // "id":"CNY1", + // "deposit_enabled":false + // } + // ] + // + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const currencyId = this.safeString (currency, 'id'); + const code = this.safeCurrencyCode (currencyId); + const name = this.safeString (currency, 'name'); + const withdrawEnabled = this.safeValue (currency, 'withdraw_enabled'); + const depositEnabled = this.safeValue (currency, 'deposit_enabled'); + const active = withdrawEnabled && depositEnabled; + result[code] = { + 'id': currencyId, + 'code': code, + 'name': name, + 'info': currency, // the original payload + 'active': active, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'withdraw': { 'min': undefined, 'max': undefined }, + }, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + // 'precision': 4, // optional price precision / depth level whose range is defined in symbol details + }; + const response = await this.publicGetSymbolsSymbolOrders (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'buys', 'sells', 'price', 'amount'); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "amount":"2.29275119", + // "price":"0.021858", + // "count":"104.8930", + // "order_time":1563997286061, + // "type":"sell" + // } + // + // fetchMyTrades (private) + // + // { + // active: true, + // amount: '0.2000', + // entrustType: 1, + // entrust_id: 979648824, + // fees: '0.0000085532', + // price: '0.021383', + // symbol: 'ETH_BTC', + // timestamp: 1574343514000, + // trade_id: 329418828 + // }, + // + const id = this.safeString (trade, 'trade_id'); + const timestamp = this.safeInteger2 (trade, 'timestamp', 'order_time'); + const type = undefined; + let side = this.safeStringLower (trade, 'type'); + if ((side === undefined) && ('entrustType' in trade)) { + side = trade['entrustType'] ? 'sell' : 'buy'; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const orderId = this.safeInteger (trade, 'entrust_id'); + const marketId = this.safeString (trade, 'symbol'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const feeCost = this.safeFloat (trade, 'fees'); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrencyCode = undefined; + if (market !== undefined) { + feeCurrencyCode = (side === 'buy') ? market['base'] : market['quote']; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': undefined, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetSymbolsSymbolTrades (this.extend (request, params)); + // + // [ + // { + // "amount":"2.29275119", + // "price":"0.021858", + // "count":"104.8930", + // "order_time":1563997286061, + // "type":"sell" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // limit is required, must be in the range (0, 50) + const maxLimit = 50; + limit = (limit === undefined) ? maxLimit : Math.min (limit, maxLimit); + const request = { + 'symbol': market['id'], + 'offset': 0, // current page, starts from 0 + 'limit': limit, // required + }; + const response = await this.privateGetTrades (this.extend (request, params)); + // + // { + // "total_trades": 216, + // "total_pages": 22, + // "current_page": 0, + // "trades": [ + // { + // "symbol": "BMX_ETH", + // "amount": "1.0", + // "fees": "0.0005000000", + // "trade_id": 2734956, + // "price": "0.00013737", + // "active": true, + // "entrust_id": 5576623, + // "timestamp": 1545292334000 + // }, + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'entrust_id': id, + }; + return await this.fetchMyTrades (symbol, since, limit, this.extend (request, params)); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeInteger (ohlcv, 'timestamp'), + this.safeFloat (ohlcv, 'open_price'), + this.safeFloat (ohlcv, 'highest_price'), + this.safeFloat (ohlcv, 'lowest_price'), + this.safeFloat (ohlcv, 'current_price'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + if (since === undefined && limit === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOHLCV requires either a `since` argument or a `limit` argument (or both)'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const periodInSeconds = this.parseTimeframe (timeframe); + const duration = periodInSeconds * limit * 1000; + let to = this.milliseconds (); + if (since === undefined) { + since = to - duration; + } else { + to = this.sum (since, duration); + } + const request = { + 'symbol': market['id'], + 'from': since, // start time of k-line data (in milliseconds, required) + 'to': to, // end time of k-line data (in milliseconds, required) + 'step': this.timeframes[timeframe], // steps of sampling (in minutes, default 1 minute, optional) + }; + const response = await this.publicGetSymbolsSymbolKline (this.extend (request, params)); + // + // [ + // { + // "timestamp":1525761000000, + // "open_price":"0.010130", + // "highest_price":"0.010130", + // "lowest_price":"0.010130", + // "current_price":"0.010130", + // "volume":"0.000000" + // } + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetWallet (params); + // + // [ + // { + // "name":"Bitcoin", + // "available":"0.0000000000", + // "frozen":"0.0000000000", + // "id":"BTC" + // } + // ] + // + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'id'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'frozen'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "entrust_id":1223181 + // } + // + // cancelOrder + // + // {} + // + // fetchOrder, fetchOrdersByStatus, fetchOpenOrders, fetchClosedOrders + // + // { + // "entrust_id":1223181, + // "symbol":"BMX_ETH", + // "timestamp":1528060666000, + // "side":"buy", + // "price":"1.000000", + // "fees":"0.1", + // "original_amount":"1", + // "executed_amount":"1", + // "remaining_amount":"0", + // "status":3 + // } + // + const id = this.safeString (order, 'entrust_id'); + const timestamp = this.milliseconds (); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'original_amount'); + let cost = undefined; + let filled = this.safeFloat (order, 'executed_amount'); + let remaining = this.safeFloat (order, 'remaining_amount'); + if (amount !== undefined) { + if (remaining !== undefined) { + if (filled === undefined) { + filled = amount - remaining; + } + } + if (filled !== undefined) { + if (remaining === undefined) { + remaining = amount - filled; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } + } + } + const side = this.safeString (order, 'side'); + const type = undefined; + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'all', + '1': 'open', + '2': 'open', + '3': 'closed', + '4': 'canceled', + '5': 'open', + '6': 'closed', + }; + return this.safeString (statuses, status, status); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'side': side.toLowerCase (), + 'amount': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "entrust_id":1223181 + // } + // + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const intId = parseInt (id); + const request = { + 'id': intId, + 'entrust_id': intId, + }; + const response = await this.privateDeleteOrdersId (this.extend (request, params)); + // + // responds with an empty object {} + // + return this.parseOrder (response); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); + } + const side = this.safeString (params, 'side'); + if (side === undefined) { + throw new ArgumentsRequired (this.id + " cancelAllOrders requires a `side` parameter ('buy' or 'sell')"); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'side': side, // 'buy' or 'sell' + }; + const response = await this.privateDeleteOrders (this.extend (request, params)); + // + // responds with an empty object {} + // + return response; + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrdersByStatus requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 500; // default 500, max 1000 + } + const request = { + 'symbol': market['id'], + 'status': status, + 'offset': 0, // current page, starts from 0 + 'limit': limit, + }; + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "orders":[ + // { + // "entrust_id":1223181, + // "symbol":"BMX_ETH", + // "timestamp":1528060666000, + // "side":"buy", + // "price":"1.000000", + // "fees":"0.1", + // "original_amount":"1", + // "executed_amount":"1", + // "remaining_amount":"0", + // "status":3 + // } + // ], + // "total_pages":1, + // "total_orders":1, + // "current_page":0, + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // 5 = pending & partially filled orders + return await this.fetchOrdersByStatus (5, symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // 3 = closed orders + return await this.fetchOrdersByStatus (3, symbol, since, limit, params); + } + + async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // 4 = canceled orders + return await this.fetchOrdersByStatus (4, symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + // + // { + // "entrust_id":1223181, + // "symbol":"BMX_ETH", + // "timestamp":1528060666000, + // "side":"buy", + // "price":"1.000000", + // "fees":"0.1", + // "original_amount":"1", + // "executed_amount":"1", + // "remaining_amount":"0", + // "status":3 + // } + // + return this.parseOrder (response); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'token') { + this.checkRequiredCredentials (); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } else { + const nonce = this.nonce (); + this.checkRequiredCredentials (); + const token = this.safeString (this.options, 'accessToken'); + if (token === undefined) { + throw new AuthenticationError (this.id + ' ' + path + ' endpoint requires an accessToken option or a prior call to signIn() method'); + } + const expires = this.safeInteger (this.options, 'expires'); + if (expires !== undefined) { + if (nonce >= expires) { + throw new AuthenticationError (this.id + ' accessToken expired, supply a new accessToken or call the signIn() method'); + } + } + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + headers = { + 'Content-Type': 'application/json', + 'X-BM-TIMESTAMP': nonce.toString (), + 'X-BM-AUTHORIZATION': 'Bearer ' + token, + }; + if (method !== 'GET') { + query = this.keysort (query); + body = this.json (query); + const message = this.urlencode (query); + headers['X-BM-SIGNATURE'] = this.hmac (this.encode (message), this.encode (this.secret), 'sha256'); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // + // {"message":"Maximum price is 0.112695"} + // {"message":"Required Integer parameter 'status' is not present"} + // {"message":"Required String parameter 'symbol' is not present"} + // {"message":"Required Integer parameter 'offset' is not present"} + // {"message":"Required Integer parameter 'limit' is not present"} + // {"message":"Required Long parameter 'from' is not present"} + // {"message":"Required Long parameter 'to' is not present"} + // {"message":"Invalid status. status=6 not support any more, please use 3:deal_success orders, 4:cancelled orders"} + // {"message":"Not found"} + // {"message":"Place order error"} + // + const feedback = this.id + ' ' + body; + const message = this.safeString2 (response, 'message', 'msg'); + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],39:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, AuthenticationError, ExchangeError, InsufficientFunds, InvalidOrder, BadSymbol } = require ('./base/errors'); +const { ROUND } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bitmax extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitmax', + 'name': 'BitMax', + 'countries': [ 'CN' ], // China + 'rateLimit': 500, + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': false, + 'fetchAccounts': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchMyTrades': false, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchOrderTrades': true, + 'fetchClosedOrders': true, + 'fetchTransactions': false, + 'fetchCurrencies': true, + 'cancelAllOrders': true, + 'fetchDepositAddress': true, + }, + 'timeframes': { + '1m': '1', + '3m': '3', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '2h': '120', + '4h': '240', + '6h': '360', + '12h': '720', + '1d': '1d', + '1w': '1w', + '1M': '1m', + }, + 'version': 'v1', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/66820319-19710880-ef49-11e9-8fbe-16be62a11992.jpg', + 'api': 'https://bitmax.io', + 'test': 'https://bitmax-test.io/api', + 'www': 'https://bitmax.io', + 'doc': [ + 'https://github.com/bitmax-exchange/api-doc/blob/master/bitmax-api-doc-v1.2.md', + ], + 'fees': 'https://bitmax.io/#/feeRate/tradeRate', + 'referral': 'https://bitmax.io/#/register?inviteCode=EL6BXBQM', + }, + 'api': { + 'public': { + 'get': [ + 'assets', + 'depth', + 'fees', + 'quote', + 'depth', + 'trades', + 'products', + 'ticker/24hr', + 'barhist', + 'barhist/info', + 'margin/ref-price', + ], + }, + 'private': { + 'get': [ + 'deposit', + 'user/info', + 'balance', + 'order/batch', + 'order/open', + 'order', + 'order/history', + 'order/{coid}', + 'order/fills/{coid}', + 'transaction', + 'margin/balance', + 'margin/order/open', + 'margin/order', + ], + 'post': [ + 'margin/order', + 'order', + 'order/batch', + ], + 'delete': [ + 'margin/order', + 'order', + 'order/all', + 'order/batch', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + }, + 'options': { + 'accountGroup': undefined, + 'parseOrderToPrecision': false, + }, + 'exceptions': { + 'exact': { + '2100': AuthenticationError, // {"code":2100,"message":"ApiKeyFailure"} + '5002': BadSymbol, // {"code":5002,"message":"Invalid Symbol"} + '6010': InsufficientFunds, // {'code': 6010, 'message': 'Not enough balance.'} + '60060': InvalidOrder, // { 'code': 60060, 'message': 'The order is already filled or canceled.' } + '600503': InvalidOrder, // {"code":600503,"message":"Notional is too small."} + }, + 'broad': {}, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetAssets (params); + // + // [ + // { + // "assetCode" : "LTO", + // "assetName" : "LTO", + // "precisionScale" : 9, + // "nativeScale" : 3, + // "withdrawalFee" : 5.0, + // "minWithdrawalAmt" : 10.0, + // "status" : "Normal" + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'assetCode'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'precisionScale'); + const fee = this.safeFloat (currency, 'withdrawalFee'); // todo: redesign + const status = this.safeString (currency, 'status'); + const active = (status === 'Normal'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': this.safeString (currency, 'assetName'), + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawalAmt'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetProducts (params); + // + // [ + // { + // "symbol" : "BCH/USDT", + // "domain" : "USDS", + // "baseAsset" : "BCH", + // "quoteAsset" : "USDT", + // "priceScale" : 2, + // "qtyScale" : 3, + // "notionalScale" : 9, + // "minQty" : "0.000000001", + // "maxQty" : "1000000000", + // "minNotional" : "5", + // "maxNotional" : "200000", + // "status" : "Normal", + // "miningStatus" : "", + // "marginTradable" : true, + // "commissionType" : "Quote", + // "commissionReserveRate" : 0.0010000000 + // }, + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseAsset'); + const quoteId = this.safeString (market, 'quoteAsset'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'qtyScale'), + 'price': this.safeInteger (market, 'notionalScale'), + }; + const status = this.safeString (market, 'status'); + const active = (status === 'Normal'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minQty'), + 'max': this.safeFloat (market, 'maxQty'), + }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { + 'min': this.safeFloat (market, 'minNotional'), + 'max': this.safeFloat (market, 'maxNotional'), + }, + }, + }); + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let precision = market['precision']['price']; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + precision = market['precision']['amount']; + } + cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (cost), + }; + } + + async fetchAccounts (params = {}) { + let accountGroup = this.safeString (this.options, 'accountGroup'); + let response = undefined; + if (accountGroup === undefined) { + response = await this.privateGetUserInfo (params); + // + // { + // "accountGroup": 5 + // } + // + accountGroup = this.safeString (response, 'accountGroup'); + } + return [ + { + 'id': accountGroup, + 'type': undefined, + 'currency': undefined, + 'info': response, + }, + ]; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const response = await this.privateGetBalance (params); + // + // { + // "code": 0, + // "status": "success", // this field will be deprecated soon + // "email": "foo@bar.com", // this field will be deprecated soon + // "data": [ + // { + // "assetCode": "TSC", + // "assetName": "Ethereum", + // "totalAmount": "20.03", // total balance amount + // "availableAmount": "20.03", // balance amount available to trade + // "inOrderAmount": "0.000", // in order amount + // "btcValue": "70.81" // the current BTC value of the balance, may be missing + // }, + // ] + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const code = this.safeCurrencyCode (this.safeString (balance, 'assetCode')); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableAmount'); + account['used'] = this.safeFloat (balance, 'inOrderAmount'); + account['total'] = this.safeFloat (balance, 'totalAmount'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['n'] = limit; // default = maximum = 100 + } + const response = await this.publicGetDepth (this.extend (request, params)); + // + // { + // "m":"depth", + // "ts":1570866464777, + // "seqnum":5124140078, + // "s":"ETH/USDT", + // "asks":[ + // ["183.57","5.92"], + // ["183.6","10.185"] + // ], + // "bids":[ + // ["183.54","0.16"], + // ["183.53","10.8"], + // ] + // } + // + const timestamp = this.safeInteger (response, 'ts'); + const result = this.parseOrderBook (response, timestamp); + result['nonce'] = this.safeInteger (response, 'seqnum'); + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "symbol":"BCH/USDT", + // "interval":"1d", + // "barStartTime":1570866600000, + // "openPrice":"225.16", + // "closePrice":"224.05", + // "highPrice":"226.08", + // "lowPrice":"218.92", + // "volume":"8607.036" + // } + // + const timestamp = this.safeInteger (ticker, 'barStartTime'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'closePrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highPrice'), + 'low': this.safeFloat (ticker, 'lowPrice'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'openPrice'), + 'close': last, + 'last': last, + 'previousClose': undefined, // previous day close + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTicker24hr (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker24hr (params); + return this.parseTickers (response, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "m":"bar", + // "s":"ETH/BTC", + // "ba":"ETH", + // "qa":"BTC", + // "i":"1", + // "t":1570867020000, + // "o":"0.022023", + // "c":"0.022018", + // "h":"0.022023", + // "l":"0.022018", + // "v":"2.510", + // } + // + return [ + this.safeInteger (ohlcv, 't'), + this.safeFloat (ohlcv, 'o'), + this.safeFloat (ohlcv, 'h'), + this.safeFloat (ohlcv, 'l'), + this.safeFloat (ohlcv, 'c'), + this.safeFloat (ohlcv, 'v'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'interval': this.timeframes[timeframe], + }; + // if since and limit are not specified + // the exchange will return just 1 last candle by default + const duration = this.parseTimeframe (timeframe); + if (since !== undefined) { + request['from'] = since; + if (limit !== undefined) { + request['to'] = this.sum (since, limit * duration * 1000, 1); + } + } else if (limit !== undefined) { + request['to'] = this.milliseconds (); + request['from'] = request['to'] - limit * duration * 1000 - 1; + } + const response = await this.publicGetBarhist (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // public fetchTrades + // + // { + // "p": "13.75", // price + // "q": "6.68", // quantity + // "t": 1528988084944, // timestamp + // "bm": False, // if true, the buyer is the market maker, we only use this field to "define the side" of a public trade + // } + // + // private fetchOrderTrades + // + // { + // "ap": "0.029062965", // average filled price + // "bb": "36851.981", // base asset total balance + // "bc": "0", // if possitive, this is the BTMX commission charged by reverse mining, if negative, this is the mining output of the current fill. + // "bpb": "36851.981", // base asset pending balance + // "btmxBal": "0.0", // optional, the BTMX balance of the current account. This field is only available when bc is non-zero. + // "cat": "CASH", // account category: CASH/MARGIN + // "coid": "41g6wtPRFrJXgg6YxjqI6Qoog139Dmoi", // client order id, (needed to cancel order) + // "ei": "NULL_VAL", // execution instruction + // "errorCode": "NULL_VAL", // if the order is rejected, this field explains why + // "execId": "12562285", // for each user, this is a strictly increasing long integer (represented as string) + // "f": "78.074", // filled quantity, this is the aggregated quantity executed by all past fills + // "fa": "BTC", // fee asset + // "fee": "0.000693608", // fee + // 'lp': "0.029064", // last price, the price executed by the last fill + // "l": "11.932", // last quantity, the quantity executed by the last fill + // "m": "order", // message type + // "orderType": "Limit", // Limit, Market, StopLimit, StopMarket + // "p": "0.029066", // limit price, only available for limit and stop limit orders + // "q": "100.000", // order quantity + // "qb": "98878.642957097", // quote asset total balance + // "qpb": "98877.967247508", // quote asset pending balance + // "s": "ETH/BTC", // symbol + // "side": "Buy", // side + // "status": "PartiallyFilled", // order status + // "t": 1561131458389, // timestamp + // } + // + const timestamp = this.safeInteger (trade, 't'); + const price = this.safeFloat (trade, 'p'); + const amount = this.safeFloat (trade, 'q'); + let cost = undefined; + if ((price !== undefined) && (amount !== undefined)) { + cost = price * amount; + } + const buyerIsMaker = this.safeValue (trade, 'bm'); + let symbol = undefined; + const marketId = this.safeString (trade, 's'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = market.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fa'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const orderId = this.safeString (trade, 'coid'); + let side = this.safeStringLower (trade, 'side'); + if ((side === undefined) && (buyerIsMaker !== undefined)) { + side = buyerIsMaker ? 'buy' : 'sell'; + } + const type = this.safeStringLower (trade, 'orderType'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': undefined, + 'order': orderId, + 'type': type, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['n'] = limit; // currently limited to 100 or fewer + } + const response = await this.publicGetTrades (this.extend (request, params)); + // + // { + // "m": "marketTrades", // message type + // "s": "ETH/BTC", // symbol + // "trades": [ + // { + // "p": "13.75", // price + // "q": "6.68", // quantity + // "t": 1528988084944, // timestamp + // "bm": False, // if true, the buyer is the market maker + // }, + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'PendingNew': 'open', + 'New': 'open', + 'PartiallyFilled': 'open', + 'Filled': 'closed', + 'Canceled': 'canceled', + 'Rejected': 'rejected', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "coid": "xxx...xxx", + // "action": "new", + // "success": true // success = true means the order has been submitted to the matching engine. + // } + // + // fetchOrder, fetchOpenOrders, fetchClosedOrders + // + // { + // "accountCategory": "CASH", + // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", + // "avgPrice": "0.000000000", + // "baseAsset": "ETH", + // "btmxCommission": "0.000000000", + // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order + // "errorCode": "NULL_VAL", + // "execId": "12452288", + // "execInst": "NULL_VAL", + // "fee": "0.000000000", // cumulative fee paid for this order + // "feeAsset": "", // the asset + // "filledQty": "0.000000000", // filled quantity + // "notional": "0.000000000", + // "orderPrice": "0.310000000", // only available for limit and stop limit orders + // "orderQty": "1.000000000", + // "orderType": "StopLimit", + // "quoteAsset": "BTC", + // "side": "Buy", + // "status": "PendingNew", + // "stopPrice": "0.300000000", // only available for stop market and stop limit orders + // "symbol": "ETH/BTC", + // "time": 1566091628227, // The last execution time of the order + // "sendingTime": 1566091503547, // The sending time of the order + // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" + // } + // + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const marketId = this.safeString (order, 'symbol'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'sendingTime'); + let price = this.safeFloat (order, 'orderPrice'); + const amount = this.safeFloat (order, 'orderQty'); + const filled = this.safeFloat (order, 'filledQty'); + let remaining = undefined; + let cost = this.safeFloat (order, 'cummulativeQuoteQty'); + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + if (this.options['parseOrderToPrecision']) { + remaining = parseFloat (this.amountToPrecision (symbol, remaining)); + } + remaining = Math.max (remaining, 0.0); + } + if (price !== undefined) { + if (cost === undefined) { + cost = price * filled; + } + } + } + const id = this.safeString (order, 'coid'); + let type = this.safeString (order, 'orderType'); + if (type !== undefined) { + type = type.toLowerCase (); + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + } + } + } + const side = this.safeStringLower (order, 'side'); + const fee = { + 'cost': this.safeFloat (order, 'fee'), + 'currency': this.safeString (order, 'feeAsset'), + }; + const average = this.safeFloat (order, 'avgPrice'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': undefined, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const market = this.market (symbol); + const request = { + 'coid': this.coid (), // a unique identifier of length 32 + // 'time': this.milliseconds (), // milliseconds since UNIX epoch in UTC, this is filled in the private section of the sign() method below + 'symbol': market['id'], + // 'orderPrice': this.priceToPrecision (symbol, price), // optional, limit price of the order. This field is required for limit orders and stop limit orders + // 'stopPrice': '15.7', // optional, stopPrice of the order. This field is required for stop_market orders and stop limit orders + 'orderQty': this.amountToPrecision (symbol, amount), + 'orderType': type, // order type, you shall specify one of the following: "limit", "market", "stop_market", "stop_limit" + 'side': side, // "buy" or "sell" + // 'postOnly': true, // optional, if true, the order will either be posted to the limit order book or be cancelled, i.e. the order cannot take liquidity, default is false + // 'timeInForce': 'GTC', // optional, supports "GTC" good-till-canceled and "IOC" immediate-or-cancel + }; + if ((type === 'limit') || (type === 'stop_limit')) { + request['orderPrice'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "email": "foo@bar.com", // this field will be deprecated soon + // "status": "success", // this field will be deprecated soon + // "data": { + // "coid": "xxx...xxx", + // "action": "new", + // "success": true, // success = true means the order has been submitted to the matching engine + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'coid': id, + }; + const response = await this.privateGetOrderCoid (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // "data": { + // "accountCategory": "CASH", + // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", + // "avgPrice": "0.000000000", + // "baseAsset": "ETH", + // "btmxCommission": "0.000000000", + // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order + // "errorCode": "NULL_VAL", + // "execId": "12452288", + // "execInst": "NULL_VAL", + // "fee": "0.000000000", // cumulative fee paid for this order + // "feeAsset": "", // the asset + // "filledQty": "0.000000000", // filled quantity + // "notional": "0.000000000", + // "orderPrice": "0.310000000", // only available for limit and stop limit orders + // "orderQty": "1.000000000", + // "orderType": "StopLimit", + // "quoteAsset": "BTC", + // "side": "Buy", + // "status": "PendingNew", + // "stopPrice": "0.300000000", // only available for stop market and stop limit orders + // "symbol": "ETH/BTC", + // "time": 1566091628227, // The last execution time of the order + // "sendingTime": 1566091503547, // The sending time of the order + // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'coid': id, + }; + const response = await this.privateGetOrderFillsCoid (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // "data": [ + // { + // "ap": "0.029062965", // average filled price + // "bb": "36851.981", // base asset total balance + // "bc": "0", // if possitive, this is the BTMX commission charged by reverse mining, if negative, this is the mining output of the current fill. + // "bpb": "36851.981", // base asset pending balance + // "btmxBal": "0.0", // optional, the BTMX balance of the current account. This field is only available when bc is non-zero. + // "cat": "CASH", // account category: CASH/MARGIN + // "coid": "41g6wtPRFrJXgg6YxjqI6Qoog139Dmoi", // client order id, (needed to cancel order) + // "ei": "NULL_VAL", // execution instruction + // "errorCode": "NULL_VAL", // if the order is rejected, this field explains why + // "execId": "12562285", // for each user, this is a strictly increasing long integer (represented as string) + // "f": "78.074", // filled quantity, this is the aggregated quantity executed by all past fills + // "fa": "BTC", // fee asset + // "fee": "0.000693608", // fee + // 'lp': "0.029064", // last price, the price executed by the last fill + // "l": "11.932", // last quantity, the quantity executed by the last fill + // "m": "order", // message type + // "orderType": "Limit", // Limit, Market, StopLimit, StopMarket + // "p": "0.029066", // limit price, only available for limit and stop limit orders + // "q": "100.000", // order quantity + // "qb": "98878.642957097", // quote asset total balance + // "qpb": "98877.967247508", // quote asset pending balance + // "s": "ETH/BTC", // symbol + // "side": "Buy", // side + // "status": "PartiallyFilled", // order status + // "t": 1561131458389, // timestamp + // }, + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseTrades (data, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + const request = { + // 'side': 'buy', // or 'sell', optional + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privateGetOrderOpen (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // "data": [ + // { + // "accountCategory": "CASH", + // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", + // "avgPrice": "0.000000000", + // "baseAsset": "ETH", + // "btmxCommission": "0.000000000", + // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order + // "errorCode": "NULL_VAL", + // "execId": "12452288", + // "execInst": "NULL_VAL", + // "fee": "0.000000000", // cumulative fee paid for this order + // "feeAsset": "", // the asset + // "filledQty": "0.000000000", // filled quantity + // "notional": "0.000000000", + // "orderPrice": "0.310000000", // only available for limit and stop limit orders + // "orderQty": "1.000000000", + // "orderType": "StopLimit", + // "quoteAsset": "BTC", + // "side": "Buy", + // "status": "PendingNew", + // "stopPrice": "0.300000000", // only available for stop market and stop limit orders + // "symbol": "ETH/BTC", + // "time": 1566091628227, // The last execution time of the order + // "sendingTime": 1566091503547, // The sending time of the order + // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" + // }, + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + const request = { + // 'symbol': 'ETH/BTC', // optional + // 'category': 'CASH', // optional, string + // 'orderType': 'Market', // optional, string + // 'page': 1, // optional, integer type, starts at 1 + // 'pageSize': 100, // optional, integer type + // 'side': 'buy', // or 'sell', optional, case insensitive. + // 'startTime': 1566091628227, // optional, integer milliseconds since UNIX epoch representing the start of the range + // 'endTime': 1566091628227, // optional, integer milliseconds since UNIX epoch representing the end of the range + // 'status': 'Filled', // optional, can only be one of "Filled", "Canceled", "Rejected" + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['n'] = limit; // default 15, max 50 + } + const response = await this.privateGetOrderHistory (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // 'data': { + // 'page': 1, + // 'pageSize': 20, + // 'limit': 500, + // 'hasNext': False, + // 'data': [ + // { + // 'time': 1566091429000, // The last execution time of the order (This timestamp is in second level resolution) + // 'coid': 'QgQIMJhPFrYfUf60ZTihmseTqhzzwOCx', + // 'execId': '331', + // 'symbol': 'BTMX/USDT', + // 'orderType': 'Market', + // 'baseAsset': 'BTMX', + // 'quoteAsset': 'USDT', + // 'side': 'Buy', + // 'stopPrice': '0.000000000', // only meaningful for stop market and stop limit orders + // 'orderPrice': '0.123000000', // only meaningful for limit and stop limit orders + // 'orderQty': '9229.409000000', + // 'filledQty': '9229.409000000', + // 'avgPrice': '0.095500000', + // 'fee': '0.352563424', + // 'feeAsset': 'USDT', + // 'btmxCommission': '0.000000000', + // 'status': 'Filled', + // 'notional': '881.408559500', + // 'userId': '5DNEppWy33SayHjFQpgQUTjwNMSjEhD3', + // 'accountId': 'ACPHERRWRIA3VQADMEAB2ZTLYAXNM3PJ', + // 'accountCategory': 'CASH', + // 'errorCode': 'NULL_VAL', + // 'execInst': 'NULL_VAL', + // "sendingTime": 1566091382736, // The sending time of the order + // }, + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const orders = this.safeValue (data, 'data', []); + return this.parseOrders (orders, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + await this.loadAccounts (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'coid': this.coid (), + 'origCoid': id, + // 'time': this.milliseconds (), // this is filled in the private section of the sign() method below + }; + const response = await this.privateDeleteOrder (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // 'data': { + // 'action': 'cancel', + // 'coid': 'gaSRTi3o3Yo4PaXpVK0NSLP47vmJuLea', + // 'success': True, + // } + // } + // + const order = this.safeValue (response, 'data', {}); + return this.parseOrder (order); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const request = { + // 'side': 'buy', // optional string field (case-insensitive), either "buy" or "sell" + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; // optional + } + const response = await this.privateDeleteOrderAll (this.extend (request, params)); + // + // ? + // + return response; + } + + coid () { + const uuid = this.uuid (); + const parts = uuid.split ('-'); + const clientOrderId = parts.join (''); + const coid = clientOrderId.slice (0, 32); + return coid; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const currency = this.currency (code); + const request = { + 'requestId': this.coid (), + // 'time': this.milliseconds (), // this is filled in the private section of the sign() method below + 'assetCode': currency['id'], + }; + // note: it is highly recommended to use V2 version of this route, + // especially for assets with multiple block chains such as USDT. + const response = await this.privateGetDeposit (this.extend (request, params)); + // + // v1 + // + // { + // "data": { + // "address": "0x26a3CB49578F07000575405a57888681249c35Fd" + // }, + // "email": "igor.kroitor@gmial.com", + // "status": "success", + // } + // + // v2 + // + // { + // "code": 0, + // "data": [ + // { + // "asset": "XRP", + // "blockChain": "Ripple", + // "addressData": { + // "address": "rpinhtY4p35bPmVXPbfWRUtZ1w1K1gYShB", + // "destTag": "54301" + // } + // } + // ], + // "email": "xxx@xxx.com", + // "status": "success" // the request has been submitted to the server + // } + // + let addressData = this.safeValue (response, 'data'); + if (Array.isArray (addressData)) { + const firstElement = this.safeValue (addressData, 0, {}); + addressData = this.safeValue (firstElement, 'addressData', {}); + } + const address = this.safeString (addressData, 'address'); + const tag = this.safeString (addressData, 'destTag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/api/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + let accountGroup = this.safeString (this.options, 'accountGroup'); + if (accountGroup === undefined) { + if (this.accounts !== undefined) { + accountGroup = this.accounts[0]['id']; + } + } + if (accountGroup !== undefined) { + url = '/' + accountGroup + url; + } + const coid = this.safeString (query, 'coid'); + query['time'] = this.milliseconds ().toString (); + let auth = query['time'] + '+' + path.replace ('/{coid}', ''); // fix sign error + headers = { + 'x-auth-key': this.apiKey, + 'x-auth-timestamp': query['time'], + 'Content-Type': 'application/json', + }; + if (coid !== undefined) { + auth += '+' + coid; + headers['x-auth-coid'] = coid; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); + headers['x-auth-signature'] = signature; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + } + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"code":2100,"message":"ApiKeyFailure"} + // {'code': 6010, 'message': 'Not enough balance.'} + // {'code': 60060, 'message': 'The order is already filled or canceled.'} + // + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + const error = (code !== undefined) && (code !== '0'); + if (error || (message !== undefined)) { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],40:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { TICK_SIZE } = require ('./base/functions/number'); +const { AuthenticationError, BadRequest, DDoSProtection, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitmex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitmex', + 'name': 'BitMEX', + 'countries': [ 'SC' ], // Seychelles + 'version': 'v1', + 'userAgent': undefined, + 'rateLimit': 2000, + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'withdraw': true, + 'editOrder': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchLedger': true, + 'fetchTransactions': 'emulated', + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '1h': '1h', + '1d': '1d', + }, + 'urls': { + 'test': { + 'public': 'https://testnet.bitmex.com', + 'private': 'https://testnet.bitmex.com', + }, + 'logo': 'https://user-images.githubusercontent.com/1294454/27766319-f653c6e6-5ed4-11e7-933d-f0bc3699ae8f.jpg', + 'api': { + 'public': 'https://www.bitmex.com', + 'private': 'https://www.bitmex.com', + }, + 'www': 'https://www.bitmex.com', + 'doc': [ + 'https://www.bitmex.com/app/apiOverview', + 'https://github.com/BitMEX/api-connectors/tree/master/official-http', + ], + 'fees': 'https://www.bitmex.com/app/fees', + 'referral': 'https://www.bitmex.com/register/rm3C16', + }, + 'api': { + 'public': { + 'get': [ + 'announcement', + 'announcement/urgent', + 'funding', + 'instrument', + 'instrument/active', + 'instrument/activeAndIndices', + 'instrument/activeIntervals', + 'instrument/compositeIndex', + 'instrument/indices', + 'insurance', + 'leaderboard', + 'liquidation', + 'orderBook', + 'orderBook/L2', + 'quote', + 'quote/bucketed', + 'schema', + 'schema/websocketHelp', + 'settlement', + 'stats', + 'stats/history', + 'trade', + 'trade/bucketed', + ], + }, + 'private': { + 'get': [ + 'apiKey', + 'chat', + 'chat/channels', + 'chat/connected', + 'execution', + 'execution/tradeHistory', + 'notification', + 'order', + 'position', + 'user', + 'user/affiliateStatus', + 'user/checkReferralCode', + 'user/commission', + 'user/depositAddress', + 'user/margin', + 'user/minWithdrawalFee', + 'user/wallet', + 'user/walletHistory', + 'user/walletSummary', + ], + 'post': [ + 'apiKey', + 'apiKey/disable', + 'apiKey/enable', + 'chat', + 'order', + 'order/bulk', + 'order/cancelAllAfter', + 'order/closePosition', + 'position/isolate', + 'position/leverage', + 'position/riskLimit', + 'position/transferMargin', + 'user/cancelWithdrawal', + 'user/confirmEmail', + 'user/confirmEnableTFA', + 'user/confirmWithdrawal', + 'user/disableTFA', + 'user/logout', + 'user/logoutAll', + 'user/preferences', + 'user/requestEnableTFA', + 'user/requestWithdrawal', + ], + 'put': [ + 'order', + 'order/bulk', + 'user', + ], + 'delete': [ + 'apiKey', + 'order', + 'order/all', + ], + }, + }, + 'exceptions': { + 'exact': { + 'Invalid API Key.': AuthenticationError, + 'This key is disabled.': PermissionDenied, + 'Access Denied': PermissionDenied, + 'Duplicate clOrdID': InvalidOrder, + 'orderQty is invalid': InvalidOrder, + 'Invalid price': InvalidOrder, + 'Invalid stopPx for ordType': InvalidOrder, + }, + 'broad': { + 'Signature not valid': AuthenticationError, + 'overloaded': ExchangeNotAvailable, + 'Account has insufficient Available Balance': InsufficientFunds, + 'Service unavailable': ExchangeNotAvailable, // {"error":{"message":"Service unavailable","name":"HTTPError"}} + }, + }, + 'precisionMode': TICK_SIZE, + 'options': { + // https://blog.bitmex.com/api_announcement/deprecation-of-api-nonce-header/ + // https://github.com/ccxt/ccxt/issues/4789 + 'api-expires': 5, // in seconds + 'fetchOHLCVOpenTimestamp': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInstrumentActiveAndIndices (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const active = (market['state'] !== 'Unlisted'); + const id = market['symbol']; + const baseId = market['underlying']; + const quoteId = market['quoteCurrency']; + const basequote = baseId + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const swap = (id === basequote); + // 'positionCurrency' may be empty ("", as Bitmex currently returns for ETHUSD) + // so let's take the quote currency first and then adjust if needed + const positionId = this.safeString2 (market, 'positionCurrency', 'quoteCurrency'); + let type = undefined; + let future = false; + let prediction = false; + const position = this.safeCurrencyCode (positionId); + let symbol = id; + if (swap) { + type = 'swap'; + symbol = base + '/' + quote; + } else if (id.indexOf ('B_') >= 0) { + prediction = true; + type = 'prediction'; + } else { + future = true; + type = 'future'; + } + const precision = { + 'amount': undefined, + 'price': undefined, + }; + const lotSize = this.safeFloat (market, 'lotSize'); + const tickSize = this.safeFloat (market, 'tickSize'); + if (lotSize !== undefined) { + precision['amount'] = lotSize; + } + if (tickSize !== undefined) { + precision['price'] = tickSize; + } + const limits = { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': tickSize, + 'max': this.safeFloat (market, 'maxPrice'), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + const limitField = (position === quote) ? 'cost' : 'amount'; + limits[limitField] = { + 'min': lotSize, + 'max': this.safeFloat (market, 'maxOrderQty'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'taker': this.safeFloat (market, 'takerFee'), + 'maker': this.safeFloat (market, 'makerFee'), + 'type': type, + 'spot': false, + 'swap': swap, + 'future': future, + 'prediction': prediction, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'currency': 'all', + }; + const response = await this.privateGetUserMargin (this.extend (request, params)); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableMargin'); + account['total'] = this.safeFloat (balance, 'marginBalance'); + if (code === 'BTC') { + account['free'] = account['free'] * 0.00000001; + account['total'] = account['total'] * 0.00000001; + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['depth'] = limit; + } + const response = await this.publicGetOrderBookL2 (this.extend (request, params)); + const result = { + 'bids': [], + 'asks': [], + 'timestamp': undefined, + 'datetime': undefined, + 'nonce': undefined, + }; + for (let i = 0; i < response.length; i++) { + const order = response[i]; + const side = (order['side'] === 'Sell') ? 'asks' : 'bids'; + const amount = this.safeFloat (order, 'size'); + const price = this.safeFloat (order, 'price'); + // https://github.com/ccxt/ccxt/issues/4926 + // https://github.com/ccxt/ccxt/issues/4927 + // the exchange sometimes returns null price in the orderbook + if (price !== undefined) { + result[side].push ([ price, amount ]); + } + } + result['bids'] = this.sortBy (result['bids'], 0, true); + result['asks'] = this.sortBy (result['asks'], 0); + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const filter = { + 'filter': { + 'orderID': id, + }, + }; + const response = await this.fetchOrders (symbol, undefined, undefined, this.deepExtend (filter, params)); + const numResults = response.length; + if (numResults === 1) { + return response[0]; + } + throw new OrderNotFound (this.id + ': The order ' + id + ' not found.'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startTime'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['count'] = limit; + } + request = this.deepExtend (request, params); + // why the hassle? urlencode in python is kinda broken for nested dicts. + // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" + // Bitmex doesn't like that. Hence resorting to this hack. + if ('filter' in request) { + request['filter'] = this.json (request['filter']); + } + const response = await this.privateGetOrder (request); + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'filter': { + 'open': true, + }, + }; + return await this.fetchOrders (symbol, since, limit, this.deepExtend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // Bitmex barfs if you set 'open': false in the filter... + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startTime'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['count'] = limit; + } + request = this.deepExtend (request, params); + // why the hassle? urlencode in python is kinda broken for nested dicts. + // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" + // Bitmex doesn't like that. Hence resorting to this hack. + if ('filter' in request) { + request['filter'] = this.json (request['filter']); + } + const response = await this.privateGetExecutionTradeHistory (request); + // + // [ + // { + // "execID": "string", + // "orderID": "string", + // "clOrdID": "string", + // "clOrdLinkID": "string", + // "account": 0, + // "symbol": "string", + // "side": "string", + // "lastQty": 0, + // "lastPx": 0, + // "underlyingLastPx": 0, + // "lastMkt": "string", + // "lastLiquidityInd": "string", + // "simpleOrderQty": 0, + // "orderQty": 0, + // "price": 0, + // "displayQty": 0, + // "stopPx": 0, + // "pegOffsetValue": 0, + // "pegPriceType": "string", + // "currency": "string", + // "settlCurrency": "string", + // "execType": "string", + // "ordType": "string", + // "timeInForce": "string", + // "execInst": "string", + // "contingencyType": "string", + // "exDestination": "string", + // "ordStatus": "string", + // "triggered": "string", + // "workingIndicator": true, + // "ordRejReason": "string", + // "simpleLeavesQty": 0, + // "leavesQty": 0, + // "simpleCumQty": 0, + // "cumQty": 0, + // "avgPx": 0, + // "commission": 0, + // "tradePublishIndicator": "string", + // "multiLegReportingType": "string", + // "text": "string", + // "trdMatchID": "string", + // "execCost": 0, + // "execComm": 0, + // "homeNotional": 0, + // "foreignNotional": 0, + // "transactTime": "2019-03-05T12:47:02.762Z", + // "timestamp": "2019-03-05T12:47:02.762Z" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + parseLedgerEntryType (type) { + const types = { + 'Withdrawal': 'transaction', + 'RealisedPNL': 'margin', + 'UnrealisedPNL': 'margin', + 'Deposit': 'transaction', + 'Transfer': 'transfer', + 'AffiliatePayout': 'referral', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // { + // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", + // account: 24321, + // currency: "XBt", + // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... + // amount: -1000000, + // fee: 300000, + // transactStatus: "Completed", // "Canceled", ... + // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", + // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", + // text: "", + // transactTime: "2017-03-21T20:05:14.388Z", + // walletBalance: 0, // balance after + // marginBalance: null, + // timestamp: "2017-03-22T13:09:23.514Z" + // } + // + // ButMEX returns the unrealized pnl from the wallet history endpoint. + // The unrealized pnl transaction has an empty timestamp. + // It is not related to historical pnl it has status set to "Pending". + // Therefore it's not a part of the history at all. + // https://github.com/ccxt/ccxt/issues/6047 + // + // { + // "transactID":"00000000-0000-0000-0000-000000000000", + // "account":121210, + // "currency":"XBt", + // "transactType":"UnrealisedPNL", + // "amount":-5508, + // "fee":0, + // "transactStatus":"Pending", + // "address":"XBTUSD", + // "tx":"", + // "text":"", + // "transactTime":null, # �---------------------------- null + // "walletBalance":139198767, + // "marginBalance":139193259, + // "timestamp":null # �---------------------------- null + // } + // + const id = this.safeString (item, 'transactID'); + const account = this.safeString (item, 'account'); + const referenceId = this.safeString (item, 'tx'); + const referenceAccount = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'transactType')); + const currencyId = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let amount = this.safeFloat (item, 'amount'); + if (amount !== undefined) { + amount = amount * 1e-8; + } + let timestamp = this.parse8601 (this.safeString (item, 'transactTime')); + if (timestamp === undefined) { + // https://github.com/ccxt/ccxt/issues/6047 + // set the timestamp to zero, 1970 Jan 1 00:00:00 + // for unrealized pnl and other transactions without a timestamp + timestamp = 0; // see comments above + } + let feeCost = this.safeFloat (item, 'fee', 0); + if (feeCost !== undefined) { + feeCost = feeCost * 1e-8; + } + const fee = { + 'cost': feeCost, + 'currency': code, + }; + let after = this.safeFloat (item, 'walletBalance'); + if (after !== undefined) { + after = after * 1e-8; + } + const before = this.sum (after, -amount); + let direction = undefined; + if (amount < 0) { + direction = 'out'; + amount = Math.abs (amount); + } else { + direction = 'in'; + } + const status = this.parseTransactionStatus (this.safeString (item, 'transactStatus')); + return { + 'id': id, + 'info': item, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'direction': direction, + 'account': account, + 'referenceId': referenceId, + 'referenceAccount': referenceAccount, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, + 'after': after, + 'status': status, + 'fee': fee, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const request = { + // 'start': 123, + }; + // + // if (since !== undefined) { + // // date-based pagination not supported + // } + // + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privateGetUserWalletHistory (this.extend (request, params)); + // + // [ + // { + // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", + // account: 24321, + // currency: "XBt", + // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... + // amount: -1000000, + // fee: 300000, + // transactStatus: "Completed", // "Canceled", ... + // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", + // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", + // text: "", + // transactTime: "2017-03-21T20:05:14.388Z", + // walletBalance: 0, // balance after + // marginBalance: null, + // timestamp: "2017-03-22T13:09:23.514Z" + // } + // ] + // + return this.parseLedger (response, currency, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'start': 123, + }; + // + // if (since !== undefined) { + // // date-based pagination not supported + // } + // + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privateGetUserWalletHistory (this.extend (request, params)); + const transactions = this.filterByArray (response, 'transactType', [ 'Withdrawal', 'Deposit' ], false); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + return this.parseTransactions (transactions, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'Canceled': 'canceled', + 'Completed': 'ok', + 'Pending': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // 'transactID': 'ffe699c2-95ee-4c13-91f9-0faf41daec25', + // 'account': 123456, + // 'currency': 'XBt', + // 'transactType': 'Withdrawal', + // 'amount': -100100000, + // 'fee': 100000, + // 'transactStatus': 'Completed', + // 'address': '385cR5DM96n1HvBDMzLHPYcw89fZAXULJP', + // 'tx': '3BMEXabcdefghijklmnopqrstuvwxyz123', + // 'text': '', + // 'transactTime': '2019-01-02T01:00:00.000Z', + // 'walletBalance': 99900000, + // 'marginBalance': None, + // 'timestamp': '2019-01-02T13:00:00.000Z' + // } + // + const id = this.safeString (transaction, 'transactID'); + // For deposits, transactTime == timestamp + // For withdrawals, transactTime is submission, timestamp is processed + const transactTime = this.parse8601 (this.safeString (transaction, 'transactTime')); + const timestamp = this.parse8601 (this.safeString (transaction, 'timestamp')); + const type = this.safeStringLower (transaction, 'transactType'); + // Deposits have no from address or to address, withdrawals have both + let address = undefined; + let addressFrom = undefined; + let addressTo = undefined; + if (type === 'withdrawal') { + address = this.safeString (transaction, 'address'); + addressFrom = this.safeString (transaction, 'tx'); + addressTo = address; + } + let amount = this.safeInteger (transaction, 'amount'); + if (amount !== undefined) { + amount = Math.abs (amount) * 1e-8; + } + let feeCost = this.safeInteger (transaction, 'fee'); + if (feeCost !== undefined) { + feeCost = feeCost * 1e-8; + } + const fee = { + 'cost': feeCost, + 'currency': 'BTC', + }; + let status = this.safeString (transaction, 'transactStatus'); + if (status !== undefined) { + status = this.parseTransactionStatus (status); + } + return { + 'info': transaction, + 'id': id, + 'txid': undefined, + 'timestamp': transactTime, + 'datetime': this.iso8601 (transactTime), + 'addressFrom': addressFrom, + 'address': address, + 'addressTo': addressTo, + 'tagFrom': undefined, + 'tag': undefined, + 'tagTo': undefined, + 'type': type, + 'amount': amount, + // BTC is the only currency on Bitmex + 'currency': 'BTC', + 'status': status, + 'updated': timestamp, + 'comment': undefined, + 'fee': fee, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (!market['active']) { + throw new ExchangeError (this.id + ': symbol ' + symbol + ' is delisted'); + } + const tickers = await this.fetchTickers ([ symbol ], params); + const ticker = this.safeValue (tickers, symbol); + if (ticker === undefined) { + throw new ExchangeError (this.id + ' ticker symbol ' + symbol + ' not found'); + } + return ticker; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetInstrumentActiveAndIndices (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = this.safeString (ticker, 'symbol'); + if (symbol !== undefined) { + result[symbol] = ticker; + } + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { symbol: "ETHH19", + // rootSymbol: "ETH", + // state: "Open", + // typ: "FFCCSX", + // listing: "2018-12-17T04:00:00.000Z", + // front: "2019-02-22T12:00:00.000Z", + // expiry: "2019-03-29T12:00:00.000Z", + // settle: "2019-03-29T12:00:00.000Z", + // relistInterval: null, + // inverseLeg: "", + // sellLeg: "", + // buyLeg: "", + // optionStrikePcnt: null, + // optionStrikeRound: null, + // optionStrikePrice: null, + // optionMultiplier: null, + // positionCurrency: "ETH", + // underlying: "ETH", + // quoteCurrency: "XBT", + // underlyingSymbol: "ETHXBT=", + // reference: "BMEX", + // referenceSymbol: ".BETHXBT30M", + // calcInterval: null, + // publishInterval: null, + // publishTime: null, + // maxOrderQty: 100000000, + // maxPrice: 10, + // lotSize: 1, + // tickSize: 0.00001, + // multiplier: 100000000, + // settlCurrency: "XBt", + // underlyingToPositionMultiplier: 1, + // underlyingToSettleMultiplier: null, + // quoteToSettleMultiplier: 100000000, + // isQuanto: false, + // isInverse: false, + // initMargin: 0.02, + // maintMargin: 0.01, + // riskLimit: 5000000000, + // riskStep: 5000000000, + // limit: null, + // capped: false, + // taxed: true, + // deleverage: true, + // makerFee: -0.0005, + // takerFee: 0.0025, + // settlementFee: 0, + // insuranceFee: 0, + // fundingBaseSymbol: "", + // fundingQuoteSymbol: "", + // fundingPremiumSymbol: "", + // fundingTimestamp: null, + // fundingInterval: null, + // fundingRate: null, + // indicativeFundingRate: null, + // rebalanceTimestamp: null, + // rebalanceInterval: null, + // openingTimestamp: "2019-02-13T08:00:00.000Z", + // closingTimestamp: "2019-02-13T09:00:00.000Z", + // sessionInterval: "2000-01-01T01:00:00.000Z", + // prevClosePrice: 0.03347, + // limitDownPrice: null, + // limitUpPrice: null, + // bankruptLimitDownPrice: null, + // bankruptLimitUpPrice: null, + // prevTotalVolume: 1386531, + // totalVolume: 1387062, + // volume: 531, + // volume24h: 17118, + // prevTotalTurnover: 4741294246000, + // totalTurnover: 4743103466000, + // turnover: 1809220000, + // turnover24h: 57919845000, + // homeNotional24h: 17118, + // foreignNotional24h: 579.19845, + // prevPrice24h: 0.03349, + // vwap: 0.03383564, + // highPrice: 0.03458, + // lowPrice: 0.03329, + // lastPrice: 0.03406, + // lastPriceProtected: 0.03406, + // lastTickDirection: "ZeroMinusTick", + // lastChangePcnt: 0.017, + // bidPrice: 0.03406, + // midPrice: 0.034065, + // askPrice: 0.03407, + // impactBidPrice: 0.03406, + // impactMidPrice: 0.034065, + // impactAskPrice: 0.03407, + // hasLiquidity: true, + // openInterest: 83679, + // openValue: 285010674000, + // fairMethod: "ImpactMidPrice", + // fairBasisRate: 0, + // fairBasis: 0, + // fairPrice: 0.03406, + // markMethod: "FairPrice", + // markPrice: 0.03406, + // indicativeTaxRate: 0, + // indicativeSettlePrice: 0.03406, + // optionUnderlyingPrice: null, + // settledPrice: null, + // timestamp: "2019-02-13T08:40:30.000Z", + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + const open = this.safeFloat (ticker, 'prevPrice24h'); + const last = this.safeFloat (ticker, 'lastPrice'); + let change = undefined; + let percentage = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + if (open > 0) { + percentage = change / open * 100; + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highPrice'), + 'low': this.safeFloat (ticker, 'lowPrice'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': undefined, + 'vwap': this.safeFloat (ticker, 'vwap'), + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': this.sum (open, last) / 2, + 'baseVolume': this.safeFloat (ticker, 'homeNotional24h'), + 'quoteVolume': this.safeFloat (ticker, 'foreignNotional24h'), + 'info': ticker, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (this.safeString (ohlcv, 'timestamp')); + return [ + timestamp, + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // send JSON key/value pairs, such as {"key": "value"} + // filter by individual fields and do advanced queries on timestamps + // let filter = { 'key': 'value' }; + // send a bare series (e.g. XBU) to nearest expiring contract in that series + // you can also send a timeframe, e.g. XBU:monthly + // timeframes: daily, weekly, monthly, quarterly, and biquarterly + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'binSize': this.timeframes[timeframe], + 'partial': true, // true == include yet-incomplete current bins + // 'filter': filter, // filter by individual fields and do advanced queries + // 'columns': [], // will return all columns if omitted + // 'start': 0, // starting point for results (wtf?) + // 'reverse': false, // true == newest first + // 'endTime': '', // ending date filter for results + }; + if (limit !== undefined) { + request['count'] = limit; // default 100, max 500 + } + const duration = this.parseTimeframe (timeframe) * 1000; + const fetchOHLCVOpenTimestamp = this.safeValue (this.options, 'fetchOHLCVOpenTimestamp', true); + // if since is not set, they will return candles starting from 2017-01-01 + if (since !== undefined) { + let timestamp = since; + if (fetchOHLCVOpenTimestamp) { + timestamp = this.sum (timestamp, duration); + } + const ymdhms = this.ymdhms (timestamp); + request['startTime'] = ymdhms; // starting date filter for results + } + const response = await this.publicGetTradeBucketed (this.extend (request, params)); + const result = this.parseOHLCVs (response, market, timeframe, since, limit); + if (fetchOHLCVOpenTimestamp) { + // bitmex returns the candle's close timestamp - https://github.com/ccxt/ccxt/issues/4446 + // we can emulate the open timestamp by shifting all the timestamps one place + // so the previous close becomes the current open, and we drop the first candle + for (let i = 0; i < result.length; i++) { + result[i][0] = result[i][0] - duration; + } + } + return result; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // timestamp: '2018-08-28T00:00:02.735Z', + // symbol: 'XBTUSD', + // side: 'Buy', + // size: 2000, + // price: 6906.5, + // tickDirection: 'PlusTick', + // trdMatchID: 'b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8', + // grossValue: 28958000, + // homeNotional: 0.28958, + // foreignNotional: 2000 + // } + // + // fetchMyTrades (private) + // + // { + // "execID": "string", + // "orderID": "string", + // "clOrdID": "string", + // "clOrdLinkID": "string", + // "account": 0, + // "symbol": "string", + // "side": "string", + // "lastQty": 0, + // "lastPx": 0, + // "underlyingLastPx": 0, + // "lastMkt": "string", + // "lastLiquidityInd": "string", + // "simpleOrderQty": 0, + // "orderQty": 0, + // "price": 0, + // "displayQty": 0, + // "stopPx": 0, + // "pegOffsetValue": 0, + // "pegPriceType": "string", + // "currency": "string", + // "settlCurrency": "string", + // "execType": "string", + // "ordType": "string", + // "timeInForce": "string", + // "execInst": "string", + // "contingencyType": "string", + // "exDestination": "string", + // "ordStatus": "string", + // "triggered": "string", + // "workingIndicator": true, + // "ordRejReason": "string", + // "simpleLeavesQty": 0, + // "leavesQty": 0, + // "simpleCumQty": 0, + // "cumQty": 0, + // "avgPx": 0, + // "commission": 0, + // "tradePublishIndicator": "string", + // "multiLegReportingType": "string", + // "text": "string", + // "trdMatchID": "string", + // "execCost": 0, + // "execComm": 0, + // "homeNotional": 0, + // "foreignNotional": 0, + // "transactTime": "2019-03-05T12:47:02.762Z", + // "timestamp": "2019-03-05T12:47:02.762Z" + // } + // + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'size', 'lastQty'); + const id = this.safeString (trade, 'trdMatchID'); + const order = this.safeString (trade, 'orderID'); + const side = this.safeStringLower (trade, 'side'); + // price * amount doesn't work for all symbols (e.g. XBT, ETH) + let cost = this.safeFloat (trade, 'execCost'); + if (cost !== undefined) { + cost = Math.abs (cost) / 100000000; + } + let fee = undefined; + if ('execComm' in trade) { + let feeCost = this.safeFloat (trade, 'execComm'); + feeCost = feeCost / 100000000; + const currencyId = this.safeString (trade, 'settlCurrency'); + const feeCurrency = this.safeCurrencyCode (currencyId); + const feeRate = this.safeFloat (trade, 'commission'); + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }; + } + let takerOrMaker = undefined; + if (fee !== undefined) { + takerOrMaker = (fee['cost'] < 0) ? 'maker' : 'taker'; + } + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + const type = this.safeStringLower (trade, 'ordType'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': order, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'fee': fee, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'New': 'open', + 'PartiallyFilled': 'open', + 'Filled': 'closed', + 'DoneForDay': 'open', + 'Canceled': 'canceled', + 'PendingCancel': 'open', + 'PendingNew': 'open', + 'Rejected': 'rejected', + 'Expired': 'expired', + 'Stopped': 'open', + 'Untriggered': 'open', + 'Triggered': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeString (order, 'ordStatus')); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + } + const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'transactTime')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'orderQty'); + const filled = this.safeFloat (order, 'cumQty', 0.0); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = Math.max (amount - filled, 0.0); + } + } + const average = this.safeFloat (order, 'avgPx'); + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = average * filled; + } else if (price !== undefined) { + cost = price * filled; + } + } + const id = this.safeString (order, 'orderID'); + const type = this.safeStringLower (order, 'ordType'); + const side = this.safeStringLower (order, 'side'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startTime'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.publicGetTrade (this.extend (request, params)); + // + // [ + // { + // timestamp: '2018-08-28T00:00:02.735Z', + // symbol: 'XBTUSD', + // side: 'Buy', + // size: 2000, + // price: 6906.5, + // tickDirection: 'PlusTick', + // trdMatchID: 'b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8', + // grossValue: 28958000, + // homeNotional: 0.28958, + // foreignNotional: 2000 + // }, + // { + // timestamp: '2018-08-28T00:00:03.778Z', + // symbol: 'XBTUSD', + // side: 'Sell', + // size: 1000, + // price: 6906, + // tickDirection: 'MinusTick', + // trdMatchID: '0d4f1682-5270-a800-569b-4a0eb92db97c', + // grossValue: 14480000, + // homeNotional: 0.1448, + // foreignNotional: 1000 + // }, + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'side': this.capitalize (side), + 'orderQty': amount, + 'ordType': this.capitalize (type), + }; + if (price !== undefined) { + request['price'] = price; + } + const response = await this.privatePostOrder (this.extend (request, params)); + const order = this.parseOrder (response); + const id = this.safeString (order, 'id'); + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderID': id, + }; + if (amount !== undefined) { + request['orderQty'] = amount; + } + if (price !== undefined) { + request['price'] = price; + } + const response = await this.privatePutOrder (this.extend (request, params)); + const order = this.parseOrder (response); + this.orders[order['id']] = order; + return this.extend ({ 'info': response }, order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateDeleteOrder (this.extend ({ 'orderID': id }, params)); + let order = response[0]; + const error = this.safeString (order, 'error'); + if (error !== undefined) { + if (error.indexOf ('Unable to cancel order due to existing state') >= 0) { + throw new OrderNotFound (this.id + ' cancelOrder() failed: ' + error); + } + } + order = this.parseOrder (order); + this.orders[order['id']] = order; + return this.extend ({ 'info': response }, order); + } + + isFiat (currency) { + if (currency === 'EUR') { + return true; + } + if (currency === 'PLN') { + return true; + } + return false; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + // let currency = this.currency (code); + if (code !== 'BTC') { + throw new ExchangeError (this.id + ' supoprts BTC withdrawals only, other currencies coming soon...'); + } + const request = { + 'currency': 'XBt', // temporarily + 'amount': amount, + 'address': address, + // 'otpToken': '123456', // requires if two-factor auth (OTP) is enabled + // 'fee': 0.001, // bitcoin network fee + }; + const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params)); + return { + 'info': response, + 'id': response['transactID'], + }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code === 429) { + throw new DDoSProtection (this.id + ' ' + body); + } + if (code >= 400) { + const error = this.safeValue (response, 'error', {}); + const message = this.safeString (error, 'message'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + if (code === 400) { + throw new BadRequest (feedback); + } + throw new ExchangeError (feedback); // unknown message + } + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let query = '/api/' + this.version + '/' + path; + if (method === 'GET') { + if (Object.keys (params).length) { + query += '?' + this.urlencode (params); + } + } else { + const format = this.safeString (params, '_format'); + if (format !== undefined) { + query += '?' + this.urlencode ({ '_format': format }); + params = this.omit (params, '_format'); + } + } + const url = this.urls['api'][api] + query; + if (this.apiKey && this.secret) { + let auth = method + query; + let expires = this.safeInteger (this.options, 'api-expires'); + headers = { + 'Content-Type': 'application/json', + 'api-key': this.apiKey, + }; + expires = this.sum (this.seconds (), expires); + expires = expires.toString (); + auth += expires; + headers['api-expires'] = expires; + if (method === 'POST' || method === 'PUT' || method === 'DELETE') { + if (Object.keys (params).length) { + body = this.json (params); + auth += body; + } + } + headers['api-signature'] = this.hmac (this.encode (auth), this.encode (this.secret)); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],41:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InvalidNonce, AuthenticationError, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitso extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitso', + 'name': 'Bitso', + 'countries': [ 'MX' ], // Mexico + 'rateLimit': 2000, // 30 requests per minute + 'version': 'v3', + 'has': { + 'CORS': true, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766335-715ce7aa-5ed5-11e7-88a8-173a27bb30fe.jpg', + 'api': 'https://api.bitso.com', + 'www': 'https://bitso.com', + 'doc': 'https://bitso.com/api_info', + 'fees': 'https://bitso.com/fees?l=es', + 'referral': 'https://bitso.com/?ref=itej', + }, + 'options': { + 'precision': { + 'XRP': 6, + 'MXN': 2, + 'TUSD': 2, + }, + 'defaultPrecision': 8, + }, + 'api': { + 'public': { + 'get': [ + 'available_books', + 'ticker', + 'order_book', + 'trades', + ], + }, + 'private': { + 'get': [ + 'account_status', + 'balance', + 'fees', + 'fundings', + 'fundings/{fid}', + 'funding_destination', + 'kyc_documents', + 'ledger', + 'ledger/trades', + 'ledger/fees', + 'ledger/fundings', + 'ledger/withdrawals', + 'mx_bank_codes', + 'open_orders', + 'order_trades/{oid}', + 'orders/{oid}', + 'user_trades', + 'user_trades/{tid}', + 'withdrawals/', + 'withdrawals/{wid}', + ], + 'post': [ + 'bitcoin_withdrawal', + 'debit_card_withdrawal', + 'ether_withdrawal', + 'ripple_withdrawal', + 'bcash_withdrawal', + 'litecoin_withdrawal', + 'orders', + 'phone_number', + 'phone_verification', + 'phone_withdrawal', + 'spei_withdrawal', + 'ripple_withdrawal', + 'bcash_withdrawal', + 'litecoin_withdrawal', + ], + 'delete': [ + 'orders/{oid}', + 'orders/all', + ], + }, + }, + 'exceptions': { + '0201': AuthenticationError, // Invalid Nonce or Invalid Credentials + '104': InvalidNonce, // Cannot perform request - nonce must be higher than 1520307203724237 + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAvailableBooks (params); + const markets = this.safeValue (response, 'payload'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'book'); + const [ baseId, quoteId ] = id.split ('_'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_amount'), + 'max': this.safeFloat (market, 'maximum_amount'), + }, + 'price': { + 'min': this.safeFloat (market, 'minimum_price'), + 'max': this.safeFloat (market, 'maximum_price'), + }, + 'cost': { + 'min': this.safeFloat (market, 'minimum_value'), + 'max': this.safeFloat (market, 'maximum_value'), + }, + }; + const precision = { + 'amount': this.safeInteger (this.options['precision'], base, this.options['defaultPrecision']), + 'price': this.safeInteger (this.options['precision'], quote, this.options['defaultPrecision']), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'limits': limits, + 'precision': precision, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const balances = this.safeValue (response['payload'], 'balances'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'available'), + 'used': this.safeFloat (balance, 'locked'), + 'total': this.safeFloat (balance, 'total'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'book': this.marketId (symbol), + }; + const response = await this.publicGetOrderBook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'payload'); + const timestamp = this.parse8601 (this.safeString (orderbook, 'updated_at')); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'book': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'payload'); + const timestamp = this.parse8601 (this.safeString (ticker, 'created_at')); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'book'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString2 (trade, 'side', 'maker_side'); + let amount = this.safeFloat2 (trade, 'amount', 'major'); + if (amount !== undefined) { + amount = Math.abs (amount); + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fees_amount'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fees_currency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + let cost = this.safeFloat (trade, 'minor'); + if (cost !== undefined) { + cost = Math.abs (cost); + } + const price = this.safeFloat (trade, 'price'); + const orderId = this.safeString (trade, 'oid'); + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'book': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response['payload'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = 25, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the don't support fetching trades starting from a date yet + // use the `marker` extra param for that + // this is not a typo, the variable name is 'marker' (don't confuse with 'market') + const markerInParams = ('marker' in params); + // warn the user with an exception if the user wants to filter + // starting from since timestamp, but does not set the trade id with an extra 'marker' param + if ((since !== undefined) && !markerInParams) { + throw new ExchangeError (this.id + ' fetchMyTrades does not support fetching trades starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id'); + } + // convert it to an integer unconditionally + if (markerInParams) { + params = this.extend (params, { + 'marker': parseInt (params['marker']), + }); + } + const request = { + 'book': market['id'], + 'limit': limit, // default = 25, max = 100 + // 'sort': 'desc', // default = desc + // 'marker': id, // integer id to start from + }; + const response = await this.privateGetUserTrades (this.extend (request, params)); + return this.parseTrades (response['payload'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'book': this.marketId (symbol), + 'side': side, + 'type': type, + 'major': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const id = this.safeString (response['payload'], 'oid'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'oid': id, + }; + return await this.privateDeleteOrdersOid (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'partial-fill': 'open', // this is a common substitution in ccxt + 'completed': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'oid'); + const side = this.safeString (order, 'side'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'book'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const orderType = this.safeString (order, 'type'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'original_amount'); + const remaining = this.safeFloat (order, 'unfilled_amount'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = 25, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the don't support fetching trades starting from a date yet + // use the `marker` extra param for that + // this is not a typo, the variable name is 'marker' (don't confuse with 'market') + const markerInParams = ('marker' in params); + // warn the user with an exception if the user wants to filter + // starting from since timestamp, but does not set the trade id with an extra 'marker' param + if ((since !== undefined) && !markerInParams) { + throw new ExchangeError (this.id + ' fetchOpenOrders does not support fetching orders starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id'); + } + // convert it to an integer unconditionally + if (markerInParams) { + params = this.extend (params, { + 'marker': parseInt (params['marker']), + }); + } + const request = { + 'book': market['id'], + 'limit': limit, // default = 25, max = 100 + // 'sort': 'desc', // default = desc + // 'marker': id, // integer id to start from + }; + const response = await this.privateGetOpenOrders (this.extend (request, params)); + const orders = this.parseOrders (response['payload'], market, since, limit); + return orders; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetOrdersOid ({ + 'oid': id, + }); + const payload = this.safeValue (response, 'payload'); + if (Array.isArray (payload)) { + const numOrders = response['payload'].length; + if (numOrders === 1) { + return this.parseOrder (payload[0]); + } + } + throw new OrderNotFound (this.id + ': The order ' + id + ' not found.'); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'oid': id, + }; + const response = await this.privateGetOrderTradesOid (this.extend (request, params)); + return this.parseTrades (response['payload'], market); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'fund_currency': currency['id'], + }; + const response = await this.privateGetFundingDestination (this.extend (request, params)); + let address = this.safeString (response['payload'], 'account_identifier'); + let tag = undefined; + if (code === 'XRP') { + const parts = address.split ('?dt='); + address = parts[0]; + tag = parts[1]; + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const methods = { + 'BTC': 'Bitcoin', + 'ETH': 'Ether', + 'XRP': 'Ripple', + 'BCH': 'Bcash', + 'LTC': 'Litecoin', + }; + const method = (code in methods) ? methods[code] : undefined; + if (method === undefined) { + throw new ExchangeError (this.id + ' not valid withdraw coin: ' + code); + } + const request = { + 'amount': amount, + 'address': address, + 'destination_tag': tag, + }; + const classMethod = 'privatePost' + method + 'Withdrawal'; + const response = await this[classMethod] (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response['payload'], 'wid'), + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let endpoint = '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + endpoint += '?' + this.urlencode (query); + } + } + const url = this.urls['api'] + endpoint; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let request = [ nonce, method, endpoint ].join (''); + if (method !== 'GET') { + if (Object.keys (query).length) { + body = this.json (query); + request += body; + } + } + const signature = this.hmac (this.encode (request), this.encode (this.secret)); + const auth = this.apiKey + ':' + nonce + ':' + signature; + headers = { + 'Authorization': 'Bitso ' + auth, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // {"success":false,"error":{"code":104,"message":"Cannot perform request - nonce must be higher than 1520307203724237"}} + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const feedback = this.id + ' ' + this.json (response); + const error = this.safeValue (response, 'error'); + if (error === undefined) { + throw new ExchangeError (feedback); + } + const code = this.safeString (error, 'code'); + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('success' in response) { + if (response['success']) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],42:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ExchangeError, NotSupported, PermissionDenied, InvalidNonce, OrderNotFound, InsufficientFunds, InvalidAddress, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitstamp extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitstamp', + 'name': 'Bitstamp', + 'countries': [ 'GB' ], + 'rateLimit': 1000, + 'version': 'v2', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': true, + 'fetchDepositAddress': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchTransactions': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg', + 'api': { + 'public': 'https://www.bitstamp.net/api', + 'private': 'https://www.bitstamp.net/api', + }, + 'www': 'https://www.bitstamp.net', + 'doc': 'https://www.bitstamp.net/api', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'order_book/{pair}/', + 'ticker_hour/{pair}/', + 'ticker/{pair}/', + 'transactions/{pair}/', + 'trading-pairs-info/', + ], + }, + 'private': { + 'post': [ + 'balance/', + 'balance/{pair}/', + 'bch_withdrawal/', + 'bch_address/', + 'user_transactions/', + 'user_transactions/{pair}/', + 'open_orders/all/', + 'open_orders/{pair}/', + 'order_status/', + 'cancel_order/', + 'buy/{pair}/', + 'buy/market/{pair}/', + 'buy/instant/{pair}/', + 'sell/{pair}/', + 'sell/market/{pair}/', + 'sell/instant/{pair}/', + 'ltc_withdrawal/', + 'ltc_address/', + 'eth_withdrawal/', + 'eth_address/', + 'xrp_withdrawal/', + 'xrp_address/', + 'transfer-to-main/', + 'transfer-from-main/', + 'withdrawal-requests/', + 'withdrawal/open/', + 'withdrawal/status/', + 'withdrawal/cancel/', + 'liquidation_address/new/', + 'liquidation_address/info/', + ], + }, + 'v1': { + 'post': [ + 'bitcoin_deposit_address/', + 'unconfirmed_btc/', + 'bitcoin_withdrawal/', + 'ripple_withdrawal/', + 'ripple_address/', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.5 / 100, + 'maker': 0.5 / 100, + 'tiers': { + 'taker': [ + [0, 0.5 / 100], + [20000, 0.25 / 100], + [100000, 0.24 / 100], + [200000, 0.22 / 100], + [400000, 0.20 / 100], + [600000, 0.15 / 100], + [1000000, 0.14 / 100], + [2000000, 0.13 / 100], + [4000000, 0.12 / 100], + [20000000, 0.11 / 100], + [20000001, 0.10 / 100], + ], + 'maker': [ + [0, 0.5 / 100], + [20000, 0.25 / 100], + [100000, 0.24 / 100], + [200000, 0.22 / 100], + [400000, 0.20 / 100], + [600000, 0.15 / 100], + [1000000, 0.14 / 100], + [2000000, 0.13 / 100], + [4000000, 0.12 / 100], + [20000000, 0.11 / 100], + [20000001, 0.10 / 100], + ], + }, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0, + 'BCH': 0, + 'LTC': 0, + 'ETH': 0, + 'XRP': 0, + 'USD': 25, + 'EUR': 0.90, + }, + 'deposit': { + 'BTC': 0, + 'BCH': 0, + 'LTC': 0, + 'ETH': 0, + 'XRP': 0, + 'USD': 25, + 'EUR': 0, + }, + }, + }, + 'exceptions': { + 'exact': { + 'No permission found': PermissionDenied, + 'API key not found': AuthenticationError, + 'IP address not allowed': PermissionDenied, + 'Invalid nonce': InvalidNonce, + 'Invalid signature': AuthenticationError, + 'Authentication failed': AuthenticationError, + 'Missing key, signature and nonce parameters': AuthenticationError, + 'Your account is frozen': PermissionDenied, + 'Please update your profile with your FATCA information, before using API.': PermissionDenied, + 'Order not found': OrderNotFound, + 'Price is more than 20% below market price.': InvalidOrder, + }, + 'broad': { + 'Minimum order size is': InvalidOrder, // Minimum order size is 5.0 EUR. + 'Check your account balance for details.': InsufficientFunds, // You have only 0.00100000 BTC available. Check your account balance for details. + 'Ensure this value has at least': InvalidAddress, // Ensure this value has at least 25 characters (it has 4). + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTradingPairsInfo (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const name = this.safeString (market, 'name'); + let [ base, quote ] = name.split ('/'); + const baseId = base.toLowerCase (); + const quoteId = quote.toLowerCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const symbolId = baseId + '_' + quoteId; + const id = this.safeString (market, 'url_symbol'); + const precision = { + 'amount': market['base_decimals'], + 'price': market['counter_decimals'], + }; + const parts = market['minimum_order'].split (' '); + const cost = parts[0]; + // let [ cost, currency ] = market['minimum_order'].split (' '); + const active = (market['trading'] === 'Enabled'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'symbolId': symbolId, + 'info': market, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': parseFloat (cost), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this.publicGetOrderBookPair (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const ticker = await this.publicGetTickerPair (this.extend (request, params)); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + getCurrencyIdFromTransaction (transaction) { + // + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "datetime": XXX, + // "usd": 0.0, + // "btc": 0.0, + // "eth": "0.05000000", + // "type": "0", + // "id": XXX, + // "eur": 0.0 + // } + // + const currencyId = this.safeStringLower (transaction, 'currency'); + if (currencyId !== undefined) { + return currencyId; + } + transaction = this.omit (transaction, [ + 'fee', + 'price', + 'datetime', + 'type', + 'status', + 'id', + ]); + const ids = Object.keys (transaction); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + if (id.indexOf ('_') < 0) { + const value = this.safeFloat (transaction, id); + if ((value !== undefined) && (value !== 0)) { + return id; + } + } + } + return undefined; + } + + getMarketFromTrade (trade) { + trade = this.omit (trade, [ + 'fee', + 'price', + 'datetime', + 'tid', + 'type', + 'order_id', + 'side', + ]); + const currencyIds = Object.keys (trade); + const numCurrencyIds = currencyIds.length; + if (numCurrencyIds > 2) { + throw new ExchangeError (this.id + ' getMarketFromTrade too many keys: ' + this.json (currencyIds) + ' in the trade: ' + this.json (trade)); + } + if (numCurrencyIds === 2) { + let marketId = currencyIds[0] + currencyIds[1]; + if (marketId in this.markets_by_id) { + return this.markets_by_id[marketId]; + } + marketId = currencyIds[1] + currencyIds[0]; + if (marketId in this.markets_by_id) { + return this.markets_by_id[marketId]; + } + } + return undefined; + } + + getMarketFromTrades (trades) { + const tradesBySymbol = this.indexBy (trades, 'symbol'); + const symbols = Object.keys (tradesBySymbol); + const numSymbols = symbols.length; + if (numSymbols === 1) { + return this.markets[symbols[0]]; + } + return undefined; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // date: '1551814435', + // tid: '83581898', + // price: '0.03532850', + // type: '1', + // amount: '0.85945907' + // }, + // + // fetchMyTrades, trades returned within fetchOrder (private) + // + // { + // "usd": "6.0134400000000000", + // "price": "4008.96000000", + // "datetime": "2019-03-28 23:07:37.233599", + // "fee": "0.02", + // "btc": "0.00150000", + // "tid": 84452058, + // "type": 2 + // } + // + // from fetchOrder: + // { fee: '0.000019', + // price: '0.00015803', + // datetime: '2018-01-07 10:45:34.132551', + // btc: '0.0079015000000000', + // tid: 42777395, + // type: 2, //(0 - deposit; 1 - withdrawal; 2 - market trade) NOT buy/sell + // xrp: '50.00000000' } + const id = this.safeString2 (trade, 'id', 'tid'); + let symbol = undefined; + let side = undefined; + let price = this.safeFloat (trade, 'price'); + let amount = this.safeFloat (trade, 'amount'); + const orderId = this.safeString (trade, 'order_id'); + const type = undefined; + let cost = this.safeFloat (trade, 'cost'); + if (market === undefined) { + const keys = Object.keys (trade); + for (let i = 0; i < keys.length; i++) { + if (keys[i].indexOf ('_') >= 0) { + const marketId = keys[i].replace ('_', ''); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + } + // if the market is still not defined + // try to deduce it from used keys + if (market === undefined) { + market = this.getMarketFromTrade (trade); + } + } + const feeCost = this.safeFloat (trade, 'fee'); + let feeCurrency = undefined; + if (market !== undefined) { + price = this.safeFloat (trade, market['symbolId'], price); + amount = this.safeFloat (trade, market['baseId'], amount); + cost = this.safeFloat (trade, market['quoteId'], cost); + feeCurrency = market['quote']; + symbol = market['symbol']; + } + let timestamp = this.safeString2 (trade, 'date', 'datetime'); + if (timestamp !== undefined) { + if (timestamp.indexOf (' ') >= 0) { + // iso8601 + timestamp = this.parse8601 (timestamp); + } else { + // string unix epoch in seconds + timestamp = parseInt (timestamp); + timestamp = timestamp * 1000; + } + } + // if it is a private trade + if ('id' in trade) { + if (amount !== undefined) { + if (amount < 0) { + side = 'sell'; + amount = -amount; + } else { + side = 'buy'; + } + } + } else { + side = this.safeString (trade, 'type'); + if (side === '1') { + side = 'sell'; + } else if (side === '0') { + side = 'buy'; + } + } + if (cost === undefined) { + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + } + if (cost !== undefined) { + cost = Math.abs (cost); + } + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'time': 'hour', + }; + const response = await this.publicGetTransactionsPair (this.extend (request, params)); + // + // [ + // { + // date: '1551814435', + // tid: '83581898', + // price: '0.03532850', + // type: '1', + // amount: '0.85945907' + // }, + // { + // date: '1551814434', + // tid: '83581896', + // price: '0.03532851', + // type: '1', + // amount: '11.34130961' + // }, + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balance = await this.privatePostBalance (params); + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const account = this.account (); + account['free'] = this.safeFloat (balance, currencyId + '_available'); + account['used'] = this.safeFloat (balance, currencyId + '_reserved'); + account['total'] = this.safeFloat (balance, currencyId + '_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = 'privatePost' + this.capitalize (side); + const request = { + 'pair': market['id'], + 'amount': this.amountToPrecision (symbol, amount), + }; + if (type === 'market') { + method += 'Market'; + } else { + request['price'] = this.priceToPrecision (symbol, price); + } + method += 'Pair'; + const response = await this[method] (this.extend (request, params)); + const order = this.parseOrder (response, market); + return this.extend (order, { + 'type': type, + }); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'In Queue': 'open', + 'Open': 'open', + 'Finished': 'closed', + 'Canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrderStatus (this.safeString (response, 'status')); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { 'id': id }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + // + // { + // "status": "Finished", + // "id": 3047704374, + // "transactions": [ + // { + // "usd": "6.0134400000000000", + // "price": "4008.96000000", + // "datetime": "2019-03-28 23:07:37.233599", + // "fee": "0.02", + // "btc": "0.00150000", + // "tid": 84452058, + // "type": 2 + // } + // ] + // } + return this.parseOrder (response, market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'privatePostUserTransactions'; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + method += 'Pair'; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this[method] (this.extend (request, params)); + const result = this.filterBy (response, 'type', '2'); + return this.parseTrades (result, market, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostUserTransactions (this.extend (request, params)); + // + // [ + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "id": 1234567894, + // "usd": 0, + // "btc": 0, + // "datetime": "2018-09-08 09:00:31", + // "type": "1", + // "xrp": "-20.00000000", + // "eur": 0, + // }, + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "id": 1134567891, + // "usd": 0, + // "btc": 0, + // "datetime": "2018-09-07 18:47:52", + // "type": "0", + // "xrp": "20.00000000", + // "eur": 0, + // }, + // ] + // + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const transactions = this.filterByArray (response, 'type', [ '0', '1' ], false); + return this.parseTransactions (transactions, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['timedelta'] = this.milliseconds () - since; + } + const response = await this.privatePostWithdrawalRequests (this.extend (request, params)); + // + // [ + // { + // status: 2, + // datetime: '2018-10-17 10:58:13', + // currency: 'BTC', + // amount: '0.29669259', + // address: 'aaaaa', + // type: 1, + // id: 111111, + // transaction_id: 'xxxx', + // }, + // { + // status: 2, + // datetime: '2018-10-17 10:55:17', + // currency: 'ETH', + // amount: '1.11010664', + // address: 'aaaa', + // type: 16, + // id: 222222, + // transaction_id: 'xxxxx', + // }, + // ] + // + return this.parseTransactions (response, undefined, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchTransactions + // + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "id": 1234567894, + // "usd": 0, + // "btc": 0, + // "datetime": "2018-09-08 09:00:31", + // "type": "1", + // "xrp": "-20.00000000", + // "eur": 0, + // } + // + // fetchWithdrawals + // + // { + // status: 2, + // datetime: '2018-10-17 10:58:13', + // currency: 'BTC', + // amount: '0.29669259', + // address: 'aaaaa', + // type: 1, + // id: 111111, + // transaction_id: 'xxxx', + // } + // + // { + // "id": 3386432, + // "type": 14, + // "amount": "863.21332500", + // "status": 2, + // "address": "rE1sdh25BJQ3qFwngiTBwaq3zPGGYcrjp1?dt=1455", + // "currency": "XRP", + // "datetime": "2018-01-05 15:27:55", + // "transaction_id": "001743B03B0C79BA166A064AC0142917B050347B4CB23BA2AB4B91B3C5608F4C" + // } + // + const timestamp = this.parse8601 (this.safeString (transaction, 'datetime')); + const id = this.safeString (transaction, 'id'); + const currencyId = this.getCurrencyIdFromTransaction (transaction); + const code = this.safeCurrencyCode (currencyId, currency); + const feeCost = this.safeFloat (transaction, 'fee'); + let feeCurrency = undefined; + let amount = undefined; + if ('amount' in transaction) { + amount = this.safeFloat (transaction, 'amount'); + } else if (currency !== undefined) { + amount = this.safeFloat (transaction, currency['id'], amount); + feeCurrency = currency['code']; + } else if ((code !== undefined) && (currencyId !== undefined)) { + amount = this.safeFloat (transaction, currencyId, amount); + feeCurrency = code; + } + if (amount !== undefined) { + // withdrawals have a negative amount + amount = Math.abs (amount); + } + let status = 'ok'; + if ('status' in transaction) { + status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + } + let type = undefined; + if ('type' in transaction) { + // from fetchTransactions + const rawType = this.safeString (transaction, 'type'); + if (rawType === '0') { + type = 'deposit'; + } else if (rawType === '1') { + type = 'withdrawal'; + } + } else { + // from fetchWithdrawals + type = 'withdrawal'; + } + const txid = this.safeString (transaction, 'transaction_id'); + let tag = undefined; + let address = this.safeString (transaction, 'address'); + if (address !== undefined) { + // dt (destination tag) is embedded into the address field + const addressParts = address.split ('?dt='); + const numParts = addressParts.length; + if (numParts > 1) { + address = addressParts[0]; + tag = addressParts[1]; + } + } + const addressFrom = undefined; + const addressTo = address; + const tagFrom = undefined; + const tagTo = tag; + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + 'rate': undefined, + }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': addressFrom, + 'addressTo': addressTo, + 'address': address, + 'tagFrom': tagFrom, + 'tagTo': tagTo, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + parseTransactionStatus (status) { + // withdrawals: + // 0 (open), 1 (in process), 2 (finished), 3 (canceled) or 4 (failed). + const statuses = { + '0': 'pending', // Open + '1': 'pending', // In process + '2': 'ok', // Finished + '3': 'canceled', // Canceled + '4': 'failed', // Failed + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // from fetch order: + // { status: 'Finished', + // id: 731693945, + // transactions: + // [ { fee: '0.000019', + // price: '0.00015803', + // datetime: '2018-01-07 10:45:34.132551', + // btc: '0.0079015000000000', + // tid: 42777395, + // type: 2, + // xrp: '50.00000000' } ] } + // + // partially filled order: + // { "id": 468646390, + // "status": "Canceled", + // "transactions": [{ + // "eth": "0.23000000", + // "fee": "0.09", + // "tid": 25810126, + // "usd": "69.8947000000000000", + // "type": 2, + // "price": "303.89000000", + // "datetime": "2017-11-11 07:22:20.710567" + // }]} + // + // from create order response: + // { + // price: '0.00008012', + // currency_pair: 'XRP/BTC', + // datetime: '2019-01-31 21:23:36', + // amount: '15.00000000', + // type: '0', + // id: '2814205012' + // } + // + const id = this.safeString (order, 'id'); + let side = this.safeString (order, 'type'); + if (side !== undefined) { + side = (side === '1') ? 'sell' : 'buy'; + } + // there is no timestamp from fetchOrder + const timestamp = this.parse8601 (this.safeString (order, 'datetime')); + let lastTradeTimestamp = undefined; + let symbol = undefined; + let marketId = this.safeStringLower (order, 'currency_pair'); + if (marketId !== undefined) { + marketId = marketId.replace ('/', ''); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + } + let amount = this.safeFloat (order, 'amount'); + let filled = 0.0; + const trades = []; + const transactions = this.safeValue (order, 'transactions', []); + let feeCost = undefined; + let cost = undefined; + const numTransactions = transactions.length; + if (numTransactions > 0) { + feeCost = 0.0; + for (let i = 0; i < numTransactions; i++) { + const trade = this.parseTrade (this.extend ({ + 'order_id': id, + 'side': side, + }, transactions[i]), market); + filled = this.sum (filled, trade['amount']); + feeCost = this.sum (feeCost, trade['fee']['cost']); + if (cost === undefined) { + cost = 0.0; + } + cost = this.sum (cost, trade['cost']); + trades.push (trade); + } + lastTradeTimestamp = trades[numTransactions - 1]['timestamp']; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + if ((status === 'closed') && (amount === undefined)) { + amount = filled; + } + let remaining = undefined; + if (amount !== undefined) { + remaining = amount - filled; + } + let price = this.safeFloat (order, 'price'); + if (market === undefined) { + market = this.getMarketFromTrades (trades); + } + let feeCurrency = undefined; + if (market !== undefined) { + if (symbol === undefined) { + symbol = market['symbol']; + } + feeCurrency = market['quote']; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } else if (price === undefined) { + if (filled > 0) { + price = cost / filled; + } + } + let fee = undefined; + if (feeCost !== undefined) { + if (feeCurrency !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': fee, + 'info': order, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + let market = undefined; + await this.loadMarkets (); + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privatePostOpenOrdersAll (params); + // [ + // { + // price: '0.00008012', + // currency_pair: 'XRP/BTC', + // datetime: '2019-01-31 21:23:36', + // amount: '15.00000000', + // type: '0', + // id: '2814205012', + // } + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const order = this.parseOrder (response[i], market); + result.push (this.extend (order, { + 'status': 'open', + 'type': 'limit', + })); + } + if (symbol === undefined) { + return this.filterBySinceLimit (result, since, limit); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + getCurrencyName (code) { + if (code === 'BTC') { + return 'bitcoin'; + } + return code.toLowerCase (); + } + + isFiat (code) { + if (code === 'USD') { + return true; + } + if (code === 'EUR') { + return true; + } + return false; + } + + async fetchDepositAddress (code, params = {}) { + if (this.isFiat (code)) { + throw new NotSupported (this.id + ' fiat fetchDepositAddress() for ' + code + ' is not implemented yet'); + } + const name = this.getCurrencyName (code); + const v1 = (code === 'BTC'); + let method = v1 ? 'v1' : 'private'; // v1 or v2 + method += 'Post' + this.capitalize (name); + method += v1 ? 'Deposit' : ''; + method += 'Address'; + const response = await this[method] (params); + const address = v1 ? response : this.safeString (response, 'address'); + const tag = v1 ? undefined : this.safeString (response, 'destination_tag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + if (this.isFiat (code)) { + throw new NotSupported (this.id + ' fiat withdraw() for ' + code + ' is not implemented yet'); + } + const name = this.getCurrencyName (code); + const request = { + 'amount': amount, + 'address': address, + }; + const v1 = (code === 'BTC'); + let method = v1 ? 'v1' : 'private'; // v1 or v2 + method += 'Post' + this.capitalize (name) + 'Withdrawal'; + if (code === 'XRP') { + if (tag !== undefined) { + request['destination_tag'] = tag; + } + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/'; + if (api !== 'v1') { + url += this.version + '/'; + } + url += this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret))); + query = this.extend ({ + 'key': this.apiKey, + 'signature': signature.toUpperCase (), + 'nonce': nonce, + }, query); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // + // {"error": "No permission found"} // fetchDepositAddress returns this on apiKeys that don't have the permission required + // {"status": "error", "reason": {"__all__": ["Minimum order size is 5.0 EUR."]}} + // reuse of a nonce gives: { status: 'error', reason: 'Invalid nonce', code: 'API0004' } + const status = this.safeString (response, 'status'); + const error = this.safeValue (response, 'error'); + if ((status === 'error') || (error !== undefined)) { + let errors = []; + if (typeof error === 'string') { + errors.push (error); + } else if (error !== undefined) { + const keys = Object.keys (error); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = this.safeValue (error, key); + if (Array.isArray (value)) { + errors = this.arrayConcat (errors, value); + } else { + errors.push (value); + } + } + } + const reason = this.safeValue (response, 'reason', {}); + if (typeof reason === 'string') { + errors.push (reason); + } else { + const all = this.safeValue (reason, '__all__', []); + for (let i = 0; i < all.length; i++) { + errors.push (all[i]); + } + } + const code = this.safeString (response, 'code'); + if (code === 'API0005') { + throw new AuthenticationError (this.id + ' invalid signature, use the uid for the main account if you have subaccounts'); + } + const feedback = this.id + ' ' + body; + for (let i = 0; i < errors.length; i++) { + const value = errors[i]; + this.throwExactlyMatchedException (this.exceptions['exact'], value, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], value, feedback); + } + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],43:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitstamp1 extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitstamp1', + 'name': 'Bitstamp', + 'countries': [ 'GB' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg', + 'api': 'https://www.bitstamp.net/api', + 'www': 'https://www.bitstamp.net', + 'doc': 'https://www.bitstamp.net/api', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'ticker', + 'ticker_hour', + 'order_book', + 'transactions', + 'eur_usd', + ], + }, + 'private': { + 'post': [ + 'balance', + 'user_transactions', + 'open_orders', + 'order_status', + 'cancel_order', + 'cancel_all_orders', + 'buy', + 'sell', + 'bitcoin_deposit_address', + 'unconfirmed_btc', + 'ripple_withdrawal', + 'ripple_address', + 'withdrawal_requests', + 'bitcoin_withdrawal', + ], + }, + }, + 'markets': { + 'BTC/USD': { 'id': 'btcusd', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'btc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'BTC/EUR': { 'id': 'btceur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'EUR/USD': { 'id': 'eurusd', 'symbol': 'EUR/USD', 'base': 'EUR', 'quote': 'USD', 'baseId': 'eur', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'XRP/USD': { 'id': 'xrpusd', 'symbol': 'XRP/USD', 'base': 'XRP', 'quote': 'USD', 'baseId': 'xrp', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'XRP/EUR': { 'id': 'xrpeur', 'symbol': 'XRP/EUR', 'base': 'XRP', 'quote': 'EUR', 'baseId': 'xrp', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'XRP/BTC': { 'id': 'xrpbtc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/USD': { 'id': 'ltcusd', 'symbol': 'LTC/USD', 'base': 'LTC', 'quote': 'USD', 'baseId': 'ltc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/EUR': { 'id': 'ltceur', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'ltc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, + 'ETH/USD': { 'id': 'ethusd', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD', 'baseId': 'eth', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'ETH/EUR': { 'id': 'etheur', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR', 'baseId': 'eth', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'ETH/BTC': { 'id': 'ethbtc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, + }, + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + if (symbol !== 'BTC/USD') { + throw new ExchangeError (this.id + ' ' + this.version + " fetchOrderBook doesn't support " + symbol + ', use it for BTC/USD only'); + } + await this.loadMarkets (); + const orderbook = await this.publicGetOrderBook (params); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + async fetchTicker (symbol, params = {}) { + if (symbol !== 'BTC/USD') { + throw new ExchangeError (this.id + ' ' + this.version + " fetchTicker doesn't support " + symbol + ', use it for BTC/USD only'); + } + await this.loadMarkets (); + const ticker = await this.publicGetTicker (params); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp2 (trade, 'date', 'datetime'); + const side = (trade['type'] === 0) ? 'buy' : 'sell'; + const orderId = this.safeString (trade, 'order_id'); + if ('currency_pair' in trade) { + if (trade['currency_pair'] in this.markets_by_id) { + market = this.markets_by_id[trade['currency_pair']]; + } + } + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + if (symbol !== 'BTC/USD') { + throw new BadSymbol (this.id + ' ' + this.version + " fetchTrades doesn't support " + symbol + ', use it for BTC/USD only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'time': 'minute', + }; + const response = await this.publicGetTransactions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + const balance = await this.privatePostBalance (params); + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const account = this.account (); + account['free'] = this.safeFloat (balance, currencyId + '_available'); + account['used'] = this.safeFloat (balance, currencyId + '_reserved'); + account['total'] = this.safeFloat (balance, currencyId + '_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' ' + this.version + ' accepts limit orders only'); + } + if (symbol !== 'BTC/USD') { + throw new ExchangeError (this.id + ' v1 supports BTC/USD orders only'); + } + await this.loadMarkets (); + const method = 'privatePost' + this.capitalize (side); + const request = { + 'amount': amount, + 'price': price, + }; + const response = await this[method] (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostCancelOrder ({ 'id': id }); + } + + parseOrderStatus (status) { + const statuses = { + 'In Queue': 'open', + 'Open': 'open', + 'Finished': 'closed', + 'Canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrderStatus (response); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const pair = market ? market['id'] : 'all'; + const request = { + 'id': pair, + }; + const response = await this.privatePostOpenOrdersId (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrder is not implemented yet'); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret))); + query = this.extend ({ + 'key': this.apiKey, + 'signature': signature.toUpperCase (), + 'nonce': nonce, + }, query); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('status' in response) { + if (response['status'] === 'error') { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],44:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, ExchangeNotAvailable, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, DDoSProtection, PermissionDenied, AddressPending, OnMaintenance } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bittrex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bittrex', + 'name': 'Bittrex', + 'countries': [ 'US' ], + 'version': 'v1.1', + 'rateLimit': 1500, + 'certified': true, + // new metainfo interface + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchDepositAddress': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchMyTrades': 'emulated', + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchTickers': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + }, + 'timeframes': { + '1m': 'oneMin', + '5m': 'fiveMin', + '30m': 'thirtyMin', + '1h': 'hour', + '1d': 'day', + }, + 'hostname': 'bittrex.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766352-cf0b3c26-5ed5-11e7-82b7-f3826b7a97d8.jpg', + 'api': { + 'public': 'https://{hostname}/api', + 'account': 'https://{hostname}/api', + 'market': 'https://{hostname}/api', + 'v2': 'https://{hostname}/api/v2.0/pub', + 'v3': 'https://api.bittrex.com/v3', + 'v3public': 'https://api.bittrex.com/v3', + }, + 'www': 'https://bittrex.com', + 'doc': [ + 'https://bittrex.github.io/api/', + 'https://bittrex.github.io/api/v3', + 'https://www.npmjs.com/package/bittrex-node', + ], + 'fees': [ + 'https://bittrex.zendesk.com/hc/en-us/articles/115003684371-BITTREX-SERVICE-FEES-AND-WITHDRAWAL-LIMITATIONS', + 'https://bittrex.zendesk.com/hc/en-us/articles/115000199651-What-fees-does-Bittrex-charge-', + ], + }, + 'api': { + 'v3': { + 'get': [ + 'account', + 'addresses', + 'addresses/{currencySymbol}', + 'balances', + 'balances/{currencySymbol}', + 'currencies', + 'currencies/{symbol}', + 'deposits/open', + 'deposits/closed', + 'deposits/ByTxId/{txId}', + 'deposits/{depositId}', + 'orders/closed', + 'orders/open', + 'orders/{orderId}', + 'ping', + 'subaccounts/{subaccountId}', + 'subaccounts', + 'withdrawals/open', + 'withdrawals/closed', + 'withdrawals/ByTxId/{txId}', + 'withdrawals/{withdrawalId}', + ], + 'post': [ + 'addresses', + 'orders', + 'subaccounts', + 'withdrawals', + ], + 'delete': [ + 'orders/{orderId}', + 'withdrawals/{withdrawalId}', + ], + }, + 'v3public': { + 'get': [ + 'markets', + 'markets/summaries', + 'markets/{marketSymbol}', + 'markets/{marketSymbol}/summary', + 'markets/{marketSymbol}/orderbook', + 'markets/{marketSymbol}/trades', + 'markets/{marketSymbol}/ticker', + 'markets/{marketSymbol}/candles', + ], + }, + 'v2': { + 'get': [ + 'currencies/GetBTCPrice', + 'currencies/GetWalletHealth', + 'general/GetLatestAlert', + 'market/GetTicks', + 'market/GetLatestTick', + 'Markets/GetMarketSummaries', + 'market/GetLatestTick', + ], + }, + 'public': { + 'get': [ + 'currencies', + 'markethistory', + 'markets', + 'marketsummaries', + 'marketsummary', + 'orderbook', + 'ticker', + ], + }, + 'account': { + 'get': [ + 'balance', + 'balances', + 'depositaddress', + 'deposithistory', + 'order', + 'orders', + 'orderhistory', + 'withdrawalhistory', + 'withdraw', + ], + }, + 'market': { + 'get': [ + 'buylimit', + 'buymarket', + 'cancel', + 'openorders', + 'selllimit', + 'sellmarket', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0025, + 'taker': 0.0025, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.0005, + 'LTC': 0.01, + 'DOGE': 2, + 'VTC': 0.02, + 'PPC': 0.02, + 'FTC': 0.2, + 'RDD': 2, + 'NXT': 2, + 'DASH': 0.05, + 'POT': 0.002, + 'BLK': 0.02, + 'EMC2': 0.2, + 'XMY': 0.2, + 'GLD': 0.0002, + 'SLR': 0.2, + 'GRS': 0.2, + }, + 'deposit': { + 'BTC': 0, + 'LTC': 0, + 'DOGE': 0, + 'VTC': 0, + 'PPC': 0, + 'FTC': 0, + 'RDD': 0, + 'NXT': 0, + 'DASH': 0, + 'POT': 0, + 'BLK': 0, + 'EMC2': 0, + 'XMY': 0, + 'GLD': 0, + 'SLR': 0, + 'GRS': 0, + }, + }, + }, + 'exceptions': { + 'exact': { + // 'Call to Cancel was throttled. Try again in 60 seconds.': DDoSProtection, + // 'Call to GetBalances was throttled. Try again in 60 seconds.': DDoSProtection, + 'APISIGN_NOT_PROVIDED': AuthenticationError, + 'INVALID_SIGNATURE': AuthenticationError, + 'INVALID_CURRENCY': ExchangeError, + 'INVALID_PERMISSION': AuthenticationError, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + 'QUANTITY_NOT_PROVIDED': InvalidOrder, + 'MIN_TRADE_REQUIREMENT_NOT_MET': InvalidOrder, + 'ORDER_NOT_OPEN': OrderNotFound, + 'INVALID_ORDER': InvalidOrder, + 'UUID_INVALID': OrderNotFound, + 'RATE_NOT_PROVIDED': InvalidOrder, // createLimitBuyOrder ('ETH/BTC', 1, 0) + 'INVALID_MARKET': BadSymbol, // {"success":false,"message":"INVALID_MARKET","result":null,"explanation":null} + 'WHITELIST_VIOLATION_IP': PermissionDenied, + 'DUST_TRADE_DISALLOWED_MIN_VALUE': InvalidOrder, + 'RESTRICTED_MARKET': BadSymbol, + 'We are down for scheduled maintenance, but we\u2019ll be back up shortly.': OnMaintenance, // {"success":false,"message":"We are down for scheduled maintenance, but we\u2019ll be back up shortly.","result":null,"explanation":null} + }, + 'broad': { + 'throttled': DDoSProtection, + 'problem': ExchangeNotAvailable, + }, + }, + 'options': { + 'parseOrderStatus': false, + 'hasAlreadyAuthenticatedSuccessfully': false, // a workaround for APIKEY_INVALID + 'symbolSeparator': '-', + // With certain currencies, like + // AEON, BTS, GXS, NXT, SBD, STEEM, STR, XEM, XLM, XMR, XRP + // an additional tag / memo / payment id is usually required by exchanges. + // With Bittrex some currencies imply the "base address + tag" logic. + // The base address for depositing is stored on this.currencies[code] + // The base address identifies the exchange as the recipient + // while the tag identifies the user account within the exchange + // and the tag is retrieved with fetchDepositAddress. + 'tag': { + 'NXT': true, // NXT, BURST + 'CRYPTO_NOTE_PAYMENTID': true, // AEON, XMR + 'BITSHAREX': true, // BTS + 'RIPPLE': true, // XRP + 'NEM': true, // XEM + 'STELLAR': true, // XLM + 'STEEM': true, // SBD, GOLOS + // https://github.com/ccxt/ccxt/issues/4794 + // 'LISK': true, // LSK + }, + 'subaccountId': undefined, + // see the implementation of fetchClosedOrdersV3 below + 'fetchClosedOrdersMethod': 'fetch_closed_orders_v3', + 'fetchClosedOrdersFilterBySince': true, + }, + 'commonCurrencies': { + 'BITS': 'SWIFT', + 'CPC': 'Capricoin', + }, + }); + } + + costToPrecision (symbol, cost) { + return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); + } + + feeToPrecision (symbol, fee) { + return this.decimalToPrecision (fee, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); + } + + async fetchMarkets (params = {}) { + const response = await this.v3publicGetMarkets (params); + // + // [ + // { + // "symbol":"LTC-BTC", + // "baseCurrencySymbol":"LTC", + // "quoteCurrencySymbol":"BTC", + // "minTradeSize":"0.01686767", + // "precision":8, + // "status":"ONLINE", // "OFFLINE" + // "createdAt":"2014-02-13T00:00:00Z" + // }, + // { + // "symbol":"VDX-USDT", + // "baseCurrencySymbol":"VDX", + // "quoteCurrencySymbol":"USDT", + // "minTradeSize":"300.00000000", + // "precision":8, + // "status":"ONLINE", // "OFFLINE" + // "createdAt":"2019-05-23T00:41:21.843Z", + // "notice":"USDT has swapped to an ERC20-based token as of August 5, 2019." + // } + // ] + // + const result = []; + // const markets = this.safeValue (response, 'result'); + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const baseId = this.safeString (market, 'baseCurrencySymbol'); + const quoteId = this.safeString (market, 'quoteCurrencySymbol'); + // bittrex v2 uses inverted pairs, v3 uses regular pairs + // we use v3 for fetchMarkets and v2 throughout the rest of this implementation + // therefore we swap the base �→ quote here to be v2-compatible + // https://github.com/ccxt/ccxt/issues/5634 + // const id = this.safeString (market, 'symbol'); + const id = quoteId + this.options['symbolSeparator'] + baseId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const pricePrecision = this.safeInteger (market, 'precision', 8); + const precision = { + 'amount': 8, + 'price': pricePrecision, + }; + const status = this.safeString (market, 'status'); + const active = (status === 'ONLINE'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minTradeSize'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.accountGetBalances (params); + const balances = this.safeValue (response, 'result'); + const result = { 'info': balances }; + const indexed = this.indexBy (balances, 'Currency'); + const currencyIds = Object.keys (indexed); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + const balance = indexed[currencyId]; + account['free'] = this.safeFloat (balance, 'Available'); + account['total'] = this.safeFloat (balance, 'Balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'type': 'both', + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + let orderbook = response['result']; + if ('type' in params) { + if (params['type'] === 'buy') { + orderbook = { + 'buy': response['result'], + 'sell': [], + }; + } else if (params['type'] === 'sell') { + orderbook = { + 'buy': [], + 'sell': response['result'], + }; + } + } + return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity'); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // "success": true, + // "message": "", + // "result": [ + // { + // "Currency": "BTC", + // "CurrencyLong":"Bitcoin", + // "MinConfirmation":2, + // "TxFee":0.00050000, + // "IsActive":true, + // "IsRestricted":false, + // "CoinType":"BITCOIN", + // "BaseAddress":"1N52wHoVR79PMDishab2XmRHsbekCdGquK", + // "Notice":null + // }, + // ..., + // ] + // } + // + const currencies = this.safeValue (response, 'result', []); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'Currency'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (id); + const precision = 8; // default precision, todo: fix "magic constants" + const address = this.safeValue (currency, 'BaseAddress'); + const fee = this.safeFloat (currency, 'TxFee'); // todo: redesign + result[code] = { + 'id': id, + 'code': code, + 'address': address, + 'info': currency, + 'type': currency['CoinType'], + 'name': currency['CurrencyLong'], + 'active': currency['IsActive'], + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': fee, + 'max': undefined, + }, + }, + }; + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "MarketName":"BTC-ETH", + // "High":0.02127099, + // "Low":0.02035064, + // "Volume":10288.40271571, + // "Last":0.02070510, + // "BaseVolume":214.64663206, + // "TimeStamp":"2019-09-18T21:03:59.897", + // "Bid":0.02070509, + // "Ask":0.02070510, + // "OpenBuyOrders":1228, + // "OpenSellOrders":5899, + // "PrevDay":0.02082823, + // "Created":"2015-08-14T09:02:24.817" + // } + // + const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp')); + let symbol = undefined; + const marketId = this.safeString (ticker, 'MarketName'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + symbol = this.parseSymbol (marketId); + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const previous = this.safeFloat (ticker, 'PrevDay'); + const last = this.safeFloat (ticker, 'Last'); + let change = undefined; + let percentage = undefined; + if (last !== undefined) { + if (previous !== undefined) { + change = last - previous; + if (previous > 0) { + percentage = (change / previous) * 100; + } + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'High'), + 'low': this.safeFloat (ticker, 'Low'), + 'bid': this.safeFloat (ticker, 'Bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'Ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': previous, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume'), + 'quoteVolume': this.safeFloat (ticker, 'BaseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketsummaries (params); + const result = this.safeValue (response, 'result'); + const tickers = []; + for (let i = 0; i < result.length; i++) { + const ticker = this.parseTicker (result[i]); + tickers.push (ticker); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketsummary (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result":[ + // { + // "MarketName":"BTC-ETH", + // "High":0.02127099, + // "Low":0.02035064, + // "Volume":10288.40271571, + // "Last":0.02070510, + // "BaseVolume":214.64663206, + // "TimeStamp":"2019-09-18T21:03:59.897", + // "Bid":0.02070509, + // "Ask":0.02070510, + // "OpenBuyOrders":1228, + // "OpenSellOrders":5899, + // "PrevDay":0.02082823, + // "Created":"2015-08-14T09:02:24.817" + // } + // ] + // } + // + const ticker = response['result'][0]; + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (trade['TimeStamp'] + '+00:00'); + let side = undefined; + if (trade['OrderType'] === 'BUY') { + side = 'buy'; + } else if (trade['OrderType'] === 'SELL') { + side = 'sell'; + } + const id = this.safeString2 (trade, 'Id', 'ID'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + const price = this.safeFloat (trade, 'Price'); + const amount = this.safeFloat (trade, 'Quantity'); + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': 'limit', + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarkethistory (this.extend (request, params)); + if ('result' in response) { + if (response['result'] !== undefined) { + return this.parseTrades (response['result'], market, since, limit); + } + } + throw new ExchangeError (this.id + ' fetchTrades() returned undefined response'); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (ohlcv['T'] + '+00:00'); + return [ + timestamp, + ohlcv['O'], + ohlcv['H'], + ohlcv['L'], + ohlcv['C'], + ohlcv['V'], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'tickInterval': this.timeframes[timeframe], + 'marketName': market['id'], + }; + const response = await this.v2GetMarketGetTicks (this.extend (request, params)); + if ('result' in response) { + if (response['result']) { + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + } + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.marketGetOpenorders (this.extend (request, params)); + const result = this.safeValue (response, 'result', []); + const orders = this.parseOrders (result, market, since, limit); + return this.filterBySymbol (orders, symbol); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'marketGet' + this.capitalize (side) + type; + const request = { + 'market': market['id'], + 'quantity': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + }; + // if (type == 'limit') + // order['rate'] = this.priceToPrecision (symbol, price); + const response = await this[method] (this.extend (request, params)); + const orderIdField = this.getOrderIdField (); + const orderId = this.safeString (response['result'], orderIdField); + return { + 'info': response, + 'id': orderId, + 'symbol': symbol, + 'type': type, + 'side': side, + 'status': 'open', + }; + } + + getOrderIdField () { + return 'uuid'; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const orderIdField = this.getOrderIdField (); + const request = {}; + request[orderIdField] = id; + const response = await this.marketGetCancel (this.extend (request, params)); + // + // { + // "success": true, + // "message": "''", + // "result": { + // "uuid": "614c34e4-8d71-11e3-94b5-425861b86ab6" + // } + // } + // + return this.extend (this.parseOrder (response), { + 'status': 'canceled', + }); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://support.bittrex.com/hc/en-us/articles/115003723911 + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + const response = await this.accountGetDeposithistory (this.extend (request, params)); + // + // { success: true, + // message: "", + // result: [ { Id: 22578097, + // Amount: 0.3, + // Currency: "ETH", + // Confirmations: 15, + // LastUpdated: "2018-06-10T07:12:10.57", + // TxId: "0xf50b5ba2ca5438b58f93516eaa523eaf35b4420ca0f24061003df1be7…", + // CryptoAddress: "0xb25f281fa51f1635abd4a60b0870a62d2a7fa404" } ] } + // + // we cannot filter by `since` timestamp, as it isn't set by Bittrex + // see https://github.com/ccxt/ccxt/issues/4067 + // return this.parseTransactions (response['result'], currency, since, limit); + return this.parseTransactions (response['result'], currency, undefined, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://support.bittrex.com/hc/en-us/articles/115003723911 + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + const response = await this.accountGetWithdrawalhistory (this.extend (request, params)); + // + // { + // "success" : true, + // "message" : "", + // "result" : [{ + // "PaymentUuid" : "b32c7a5c-90c6-4c6e-835c-e16df12708b1", + // "Currency" : "BTC", + // "Amount" : 17.00000000, + // "Address" : "1DfaaFBdbB5nrHj87x3NHS4onvw1GPNyAu", + // "Opened" : "2014-07-09T04:24:47.217", + // "Authorized" : true, + // "PendingPayment" : false, + // "TxCost" : 0.00020000, + // "TxId" : null, + // "Canceled" : true, + // "InvalidAddress" : false + // }, { + // "PaymentUuid" : "d193da98-788c-4188-a8f9-8ec2c33fdfcf", + // "Currency" : "XC", + // "Amount" : 7513.75121715, + // "Address" : "TcnSMgAd7EonF2Dgc4c9K14L12RBaW5S5J", + // "Opened" : "2014-07-08T23:13:31.83", + // "Authorized" : true, + // "PendingPayment" : false, + // "TxCost" : 0.00002000, + // "TxId" : "d8a575c2a71c7e56d02ab8e26bb1ef0a2f6cf2094f6ca2116476a569c1e84f6e", + // "Canceled" : false, + // "InvalidAddress" : false + // } + // ] + // } + // + return this.parseTransactions (response['result'], currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // Id: 72578097, + // Amount: 0.3, + // Currency: "ETH", + // Confirmations: 15, + // LastUpdated: "2018-06-17T07:12:14.57", + // TxId: "0xb31b5ba2ca5438b58f93516eaa523eaf35b4420ca0f24061003df1be7…", + // CryptoAddress: "0x2d5f281fa51f1635abd4a60b0870a62d2a7fa404" + // } + // + // fetchWithdrawals + // + // { + // "PaymentUuid" : "e293da98-788c-4188-a8f9-8ec2c33fdfcf", + // "Currency" : "XC", + // "Amount" : 7513.75121715, + // "Address" : "EVnSMgAd7EonF2Dgc4c9K14L12RBaW5S5J", + // "Opened" : "2014-07-08T23:13:31.83", + // "Authorized" : true, + // "PendingPayment" : false, + // "TxCost" : 0.00002000, + // "TxId" : "b4a575c2a71c7e56d02ab8e26bb1ef0a2f6cf2094f6ca2116476a569c1e84f6e", + // "Canceled" : false, + // "InvalidAddress" : false + // } + // + const id = this.safeString2 (transaction, 'Id', 'PaymentUuid'); + const amount = this.safeFloat (transaction, 'Amount'); + const address = this.safeString2 (transaction, 'CryptoAddress', 'Address'); + const txid = this.safeString (transaction, 'TxId'); + const updated = this.parse8601 (this.safeString (transaction, 'LastUpdated')); + const opened = this.parse8601 (this.safeString (transaction, 'Opened')); + const timestamp = opened ? opened : updated; + const type = (opened === undefined) ? 'deposit' : 'withdrawal'; + const currencyId = this.safeString (transaction, 'Currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let status = 'pending'; + if (type === 'deposit') { + // + // deposits numConfirmations never reach the minConfirmations number + // we set all of them to 'ok', otherwise they'd all be 'pending' + // + // const numConfirmations = this.safeInteger (transaction, 'Confirmations', 0); + // const minConfirmations = this.safeInteger (currency['info'], 'MinConfirmation'); + // if (numConfirmations >= minConfirmations) { + // status = 'ok'; + // } + // + status = 'ok'; + } else { + const authorized = this.safeValue (transaction, 'Authorized', false); + const pendingPayment = this.safeValue (transaction, 'PendingPayment', false); + const canceled = this.safeValue (transaction, 'Canceled', false); + const invalidAddress = this.safeValue (transaction, 'InvalidAddress', false); + if (invalidAddress) { + status = 'failed'; + } else if (canceled) { + status = 'canceled'; + } else if (pendingPayment) { + status = 'pending'; + } else if (authorized && (txid !== undefined)) { + status = 'ok'; + } + } + let feeCost = this.safeFloat (transaction, 'TxCost'); + if (feeCost === undefined) { + if (type === 'deposit') { + // according to https://support.bittrex.com/hc/en-us/articles/115000199651-What-fees-does-Bittrex-charge- + feeCost = 0; // FIXME: remove hardcoded value that may change any time + } + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': updated, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + parseSymbol (id) { + const [ quoteId, baseId ] = id.split (this.options['symbolSeparator']); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + return base + '/' + quote; + } + + parseOrder (order, market = undefined) { + if ('marketSymbol' in order) { + return this.parseOrderV3 (order, market); + } else { + return this.parseOrderV2 (order, market); + } + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + if (this.options['fetchClosedOrdersFilterBySince']) { + return super.parseOrders (orders, market, since, limit, params); + } else { + return super.parseOrders (orders, market, undefined, limit, params); + } + } + + parseOrderStatus (status) { + const statuses = { + 'CLOSED': 'closed', + 'OPEN': 'open', + 'CANCELLED': 'canceled', + 'CANCELED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrderV3 (order, market = undefined) { + // + // { + // id: '1be35109-b763-44ce-b6ea-05b6b0735c0c', + // marketSymbol: 'LTC-ETH', + // direction: 'BUY', + // type: 'LIMIT', + // quantity: '0.50000000', + // limit: '0.17846699', + // timeInForce: 'GOOD_TIL_CANCELLED', + // fillQuantity: '0.50000000', + // commission: '0.00022286', + // proceeds: '0.08914915', + // status: 'CLOSED', + // createdAt: '2018-06-23T13:14:28.613Z', + // updatedAt: '2018-06-23T13:14:30.19Z', + // closedAt: '2018-06-23T13:14:30.19Z' + // } + // + const marketSymbol = this.safeString (order, 'marketSymbol'); + let symbol = undefined; + let feeCurrency = undefined; + if (marketSymbol !== undefined) { + const [ baseId, quoteId ] = marketSymbol.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const direction = this.safeStringLower (order, 'direction'); + const createdAt = this.safeString (order, 'createdAt'); + const updatedAt = this.safeString (order, 'updatedAt'); + const closedAt = this.safeString (order, 'closedAt'); + let lastTradeTimestamp = undefined; + if (closedAt !== undefined) { + lastTradeTimestamp = this.parse8601 (closedAt); + } else if (updatedAt) { + lastTradeTimestamp = this.parse8601 (updatedAt); + } + const timestamp = this.parse8601 (createdAt); + const type = this.safeStringLower (order, 'type'); + const quantity = this.safeFloat (order, 'quantity'); + const limit = this.safeFloat (order, 'limit'); + const fillQuantity = this.safeFloat (order, 'fillQuantity'); + const commission = this.safeFloat (order, 'commission'); + const proceeds = this.safeFloat (order, 'proceeds'); + const status = this.safeStringLower (order, 'status'); + let average = undefined; + let remaining = undefined; + if (fillQuantity !== undefined) { + if (proceeds !== undefined) { + if (fillQuantity > 0) { + average = proceeds / fillQuantity; + } else if (proceeds === 0) { + average = 0; + } + } + if (quantity !== undefined) { + remaining = quantity - fillQuantity; + } + } + return { + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': direction, + 'price': limit, + 'cost': proceeds, + 'average': average, + 'amount': quantity, + 'filled': fillQuantity, + 'remaining': remaining, + 'status': status, + 'fee': { + 'cost': commission, + 'currency': feeCurrency, + }, + 'info': order, + }; + } + + parseOrderV2 (order, market = undefined) { + // + // { + // "Uuid": "string (uuid)", + // "OrderUuid": "8925d746-bc9f-4684-b1aa-e507467aaa99", + // "Exchange": "BTC-LTC", + // "OrderType": "string", + // "Quantity": 100000, + // "QuantityRemaining": 100000, + // "Limit": 1e-8, + // "CommissionPaid": 0, + // "Price": 0, + // "PricePerUnit": null, + // "Opened": "2014-07-09T03:55:48.583", + // "Closed": null, + // "CancelInitiated": "boolean", + // "ImmediateOrCancel": "boolean", + // "IsConditional": "boolean" + // } + // + let side = this.safeString2 (order, 'OrderType', 'Type'); + const isBuyOrder = (side === 'LIMIT_BUY') || (side === 'BUY'); + const isSellOrder = (side === 'LIMIT_SELL') || (side === 'SELL'); + if (isBuyOrder) { + side = 'buy'; + } + if (isSellOrder) { + side = 'sell'; + } + // We parse different fields in a very specific order. + // Order might well be closed and then canceled. + let status = undefined; + if (('Opened' in order) && order['Opened']) { + status = 'open'; + } + if (('Closed' in order) && order['Closed']) { + status = 'closed'; + } + if (('CancelInitiated' in order) && order['CancelInitiated']) { + status = 'canceled'; + } + if (('Status' in order) && this.options['parseOrderStatus']) { + status = this.parseOrderStatus (this.safeString (order, 'Status')); + } + let symbol = undefined; + if ('Exchange' in order) { + const marketId = this.safeString (order, 'Exchange'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = this.parseSymbol (marketId); + } + } + } else { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let timestamp = undefined; + const opened = this.safeString (order, 'Opened'); + if (opened !== undefined) { + timestamp = this.parse8601 (opened + '+00:00'); + } + const created = this.safeString (order, 'Created'); + if (created !== undefined) { + timestamp = this.parse8601 (created + '+00:00'); + } + let lastTradeTimestamp = undefined; + const lastTimestamp = this.safeString (order, 'TimeStamp'); + if (lastTimestamp !== undefined) { + lastTradeTimestamp = this.parse8601 (lastTimestamp + '+00:00'); + } + const closed = this.safeString (order, 'Closed'); + if (closed !== undefined) { + lastTradeTimestamp = this.parse8601 (closed + '+00:00'); + } + if (timestamp === undefined) { + timestamp = lastTradeTimestamp; + } + let fee = undefined; + const feeCost = this.safeFloat2 (order, 'Commission', 'CommissionPaid'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + }; + if (market !== undefined) { + fee['currency'] = market['quote']; + } else if (symbol !== undefined) { + const currencyIds = symbol.split ('/'); + const quoteCurrencyId = currencyIds[1]; + fee['currency'] = this.safeCurrencyCode (quoteCurrencyId); + } + } + let price = this.safeFloat (order, 'Limit'); + let cost = this.safeFloat (order, 'Price'); + const amount = this.safeFloat (order, 'Quantity'); + const remaining = this.safeFloat (order, 'QuantityRemaining'); + let filled = undefined; + if (amount !== undefined && remaining !== undefined) { + filled = amount - remaining; + if ((status === 'closed') && (remaining > 0)) { + status = 'canceled'; + } + } + if (!cost) { + if (price && filled) { + cost = price * filled; + } + } + if (!price) { + if (cost && filled) { + price = cost / filled; + } + } + const average = this.safeFloat (order, 'PricePerUnit'); + const id = this.safeString2 (order, 'OrderUuid', 'OrderId'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let response = undefined; + try { + const orderIdField = this.getOrderIdField (); + const request = {}; + request[orderIdField] = id; + response = await this.accountGetOrder (this.extend (request, params)); + } catch (e) { + if (this.last_json_response) { + const message = this.safeString (this.last_json_response, 'message'); + if (message === 'UUID_INVALID') { + throw new OrderNotFound (this.id + ' fetchOrder() error: ' + this.last_http_response); + } + } + throw e; + } + if (!response['result']) { + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + return this.parseOrder (response['result']); + } + + orderToTrade (order) { + // this entire method should be moved to the base class + const timestamp = this.safeInteger2 (order, 'lastTradeTimestamp', 'timestamp'); + return { + 'id': this.safeString (order, 'id'), + 'side': this.safeString (order, 'side'), + 'order': this.safeString (order, 'id'), + 'type': this.safeString (order, 'type'), + 'price': this.safeFloat (order, 'average'), + 'amount': this.safeFloat (order, 'filled'), + 'cost': this.safeFloat (order, 'cost'), + 'symbol': this.safeString (order, 'symbol'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': this.safeValue (order, 'fee'), + 'info': order, + }; + } + + ordersToTrades (orders) { + // this entire method should be moved to the base class + const result = []; + for (let i = 0; i < orders.length; i++) { + result.push (this.orderToTrade (orders[i])); + } + return result; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchClosedOrders (symbol, since, limit, params); + return this.ordersToTrades (orders); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const method = this.safeString (this.options, 'fetchClosedOrdersMethod', 'fetch_closed_orders_v3'); + return await this[method] (symbol, since, limit, params); + } + + async fetchClosedOrdersV2 (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.accountGetOrderhistory (this.extend (request, params)); + const result = this.safeValue (response, 'result', []); + const orders = this.parseOrders (result, market, since, limit); + if (symbol !== undefined) { + return this.filterBySymbol (orders, symbol); + } + return orders; + } + + async fetchClosedOrdersV3 (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['pageSize'] = limit; + } + if (since !== undefined) { + request['startDate'] = this.ymdhms (since, 'T') + 'Z'; + } + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + // because of this line we will have to rethink the entire v3 + // in other words, markets define all the rest of the API + // and v3 market ids are reversed in comparison to v2 + // v3 has to be a completely separate implementation + // otherwise we will have to shuffle symbols and currencies everywhere + // which is prone to errors, as was shown here + // https://github.com/ccxt/ccxt/pull/5219#issuecomment-499646209 + request['marketSymbol'] = market['base'] + '-' + market['quote']; + } + const response = await this.v3GetOrdersClosed (this.extend (request, params)); + const orders = this.parseOrders (response, market, since, limit); + if (symbol !== undefined) { + return this.filterBySymbol (orders, symbol); + } + return orders; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.accountGetDepositaddress (this.extend (request, params)); + // + // { "success": false, "message": "ADDRESS_GENERATING", "result": null } + // + // { success: true, + // message: "", + // result: { Currency: "INCNT", + // Address: "3PHvQt9bK21f7eVQVdJzrNPcsMzXabEA5Ha" } } } + // + let address = this.safeString (response['result'], 'Address'); + const message = this.safeString (response, 'message'); + if (!address || message === 'ADDRESS_GENERATING') { + throw new AddressPending (this.id + ' the address for ' + code + ' is being generated (pending, not ready yet, retry again later)'); + } + let tag = undefined; + if (currency['type'] in this.options['tag']) { + tag = address; + address = currency['address']; + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'quantity': amount, + 'address': address, + }; + if (tag !== undefined) { + request['paymentid'] = tag; + } + const response = await this.accountGetWithdraw (this.extend (request, params)); + const result = this.safeValue (response, 'result', {}); + const id = this.safeString (result, 'uuid'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }) + '/'; + if (api !== 'v2' && api !== 'v3' && api !== 'v3public') { + url += this.version + '/'; + } + if (api === 'public') { + url += api + '/' + method.toLowerCase () + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v3public') { + url += path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v2') { + url += path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v3') { + url += path; + if (Object.keys (params).length) { + url += '?' + this.rawencode (params); + } + const contentHash = this.hash (this.encode (''), 'sha512', 'hex'); + const timestamp = this.milliseconds ().toString (); + let auth = timestamp + url + method + contentHash; + const subaccountId = this.safeValue (this.options, 'subaccountId'); + if (subaccountId !== undefined) { + auth += subaccountId; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); + headers = { + 'Api-Key': this.apiKey, + 'Api-Timestamp': timestamp, + 'Api-Content-Hash': contentHash, + 'Api-Signature': signature, + }; + if (subaccountId !== undefined) { + headers['Api-Subaccount-Id'] = subaccountId; + } + } else { + this.checkRequiredCredentials (); + url += api + '/'; + if (((api === 'account') && (path !== 'withdraw')) || (path === 'openorders')) { + url += method.toLowerCase (); + } + const request = { + 'apikey': this.apiKey, + }; + const disableNonce = this.safeValue (this.options, 'disableNonce'); + if ((disableNonce === undefined) || !disableNonce) { + request['nonce'] = this.nonce (); + } + url += path + '?' + this.urlencode (this.extend (request, params)); + const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512'); + headers = { 'apisign': signature }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // { success: false, message: "message" } + // + if (body[0] === '{') { + let success = this.safeValue (response, 'success'); + if (success === undefined) { + // throw new ExchangeError (this.id + ' malformed response ' + this.json (response)); + return; + } + if (typeof success === 'string') { + // bleutrade uses string instead of boolean + success = (success === 'true') ? true : false; + } + if (!success) { + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + if (message === 'APIKEY_INVALID') { + if (this.options['hasAlreadyAuthenticatedSuccessfully']) { + throw new DDoSProtection (feedback); + } else { + throw new AuthenticationError (feedback); + } + } + // https://github.com/ccxt/ccxt/issues/4932 + // the following two lines are now redundant, see line 171 in describe() + // + // if (message === 'DUST_TRADE_DISALLOWED_MIN_VALUE_50K_SAT') + // throw new InvalidOrder (this.id + ' order cost should be over 50k satoshi ' + this.json (response)); + // + if (message === 'INVALID_ORDER') { + // Bittrex will return an ambiguous INVALID_ORDER message + // upon canceling already-canceled and closed orders + // therefore this special case for cancelOrder + // let url = 'https://bittrex.com/api/v1.1/market/cancel?apikey=API_KEY&uuid=ORDER_UUID' + const cancel = 'cancel'; + const indexOfCancel = url.indexOf (cancel); + if (indexOfCancel >= 0) { + const urlParts = url.split ('?'); + const numParts = urlParts.length; + if (numParts > 1) { + const query = urlParts[1]; + const params = query.split ('&'); + const numParams = params.length; + let orderId = undefined; + for (let i = 0; i < numParams; i++) { + const param = params[i]; + const keyValue = param.split ('='); + if (keyValue[0] === 'uuid') { + orderId = keyValue[1]; + break; + } + } + if (orderId !== undefined) { + throw new OrderNotFound (this.id + ' cancelOrder ' + orderId + ' ' + this.json (response)); + } else { + throw new OrderNotFound (this.id + ' cancelOrder ' + this.json (response)); + } + } + } + } + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + if (message !== undefined) { + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + throw new ExchangeError (feedback); + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + // a workaround for APIKEY_INVALID + if ((api === 'account') || (api === 'market')) { + this.options['hasAlreadyAuthenticatedSuccessfully'] = true; + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],45:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, PermissionDenied, InvalidOrder, AuthenticationError, InsufficientFunds, OrderNotFound, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitz extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitz', + 'name': 'Bit-Z', + 'countries': [ 'HK' ], + 'rateLimit': 2000, + 'version': 'v2', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrders': true, + 'fetchOrder': true, + 'createMarketOrder': false, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '60min', + '4h': '4hour', + '1d': '1day', + '5d': '5day', + '1w': '1week', + '1M': '1mon', + }, + 'hostname': 'apiv2.bitz.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/35862606-4f554f14-0b5d-11e8-957d-35058c504b6f.jpg', + 'api': { + 'market': 'https://{hostname}', + 'trade': 'https://{hostname}', + 'assets': 'https://{hostname}', + }, + 'www': 'https://www.bitz.com', + 'doc': 'https://apidoc.bitz.com/en/', + 'fees': 'https://www.bitz.com/fee?type=1', + 'referral': 'https://u.bitz.com/register?invite_code=1429193', + }, + 'api': { + 'market': { + 'get': [ + 'ticker', + 'depth', + 'order', // trades + 'tickerall', + 'kline', + 'symbolList', + 'currencyRate', + 'currencyCoinRate', + 'coinRate', + ], + }, + 'trade': { + 'post': [ + 'addEntrustSheet', + 'cancelEntrustSheet', + 'cancelAllEntrustSheet', + 'getUserHistoryEntrustSheet', // closed orders + 'getUserNowEntrustSheet', // open orders + 'getEntrustSheetInfo', // order + 'depositOrWithdraw', // transactions + ], + }, + 'assets': { + 'post': [ + 'getUserAssets', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.002, + 'taker': 0.002, + }, + 'funding': { + 'withdraw': { + 'BTC': '0.5%', + 'DKKT': '0.5%', + 'ETH': 0.01, + 'USDT': '0.5%', + 'LTC': '0.5%', + 'FCT': '0.5%', + 'LSK': '0.5%', + 'HXI': '0.8%', + 'ZEC': '0.5%', + 'DOGE': '0.5%', + 'MZC': '0.5%', + 'ETC': '0.5%', + 'GXS': '0.5%', + 'XPM': '0.5%', + 'PPC': '0.5%', + 'BLK': '0.5%', + 'XAS': '0.5%', + 'HSR': '0.5%', + 'NULS': 5.0, + 'VOISE': 350.0, + 'PAY': 1.5, + 'EOS': 0.6, + 'YBCT': 35.0, + 'OMG': 0.3, + 'OTN': 0.4, + 'BTX': '0.5%', + 'QTUM': '0.5%', + 'DASH': '0.5%', + 'GAME': '0.5%', + 'BCH': '0.5%', + 'GNT': 9.0, + 'SSS': 1500.0, + 'ARK': '0.5%', + 'PART': '0.5%', + 'LEO': '0.5%', + 'DGB': '0.5%', + 'ZSC': 130.0, + 'VIU': 350.0, + 'BTG': '0.5%', + 'ARN': 10.0, + 'VTC': '0.5%', + 'BCD': '0.5%', + 'TRX': 200.0, + 'HWC': '0.5%', + 'UNIT': '0.5%', + 'OXY': '0.5%', + 'MCO': 0.3500, + 'SBTC': '0.5%', + 'BCX': '0.5%', + 'ETF': '0.5%', + 'PYLNT': 0.4000, + 'XRB': '0.5%', + 'ETP': '0.5%', + }, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'options': { + 'fetchOHLCVVolume': true, + 'fetchOHLCVWarning': true, + 'lastNonceTimestamp': 0, + }, + 'commonCurrencies': { + // https://github.com/ccxt/ccxt/issues/3881 + // https://support.bit-z.pro/hc/en-us/articles/360007500654-BOX-BOX-Token- + 'BOX': 'BOX Token', + 'XRB': 'NANO', + 'PXC': 'Pixiecoin', + 'VTC': 'VoteCoin', + 'TTC': 'TimesChain', + }, + 'exceptions': { + // '200': Success + '-102': ExchangeError, // Invalid parameter + '-103': AuthenticationError, // Verification failed + '-104': ExchangeNotAvailable, // Network Error-1 + '-105': AuthenticationError, // Invalid api signature + '-106': ExchangeNotAvailable, // Network Error-2 + '-109': AuthenticationError, // Invalid scretKey + '-110': DDoSProtection, // The number of access requests exceeded + '-111': PermissionDenied, // Current IP is not in the range of trusted IP + '-112': ExchangeNotAvailable, // Service is under maintenance + '-100015': AuthenticationError, // Trade password error + '-100044': ExchangeError, // Fail to request data + '-100101': ExchangeError, // Invalid symbol + '-100201': ExchangeError, // Invalid symbol + '-100301': ExchangeError, // Invalid symbol + '-100401': ExchangeError, // Invalid symbol + '-100302': ExchangeError, // Type of K-line error + '-100303': ExchangeError, // Size of K-line error + '-200003': AuthenticationError, // Please set trade password + '-200005': PermissionDenied, // This account can not trade + '-200025': ExchangeNotAvailable, // Temporary trading halt + '-200027': InvalidOrder, // Price Error + '-200028': InvalidOrder, // Amount must be greater than 0 + '-200029': InvalidOrder, // Number must be between %s and %d + '-200030': InvalidOrder, // Over price range + '-200031': InsufficientFunds, // Insufficient assets + '-200032': ExchangeError, // System error. Please contact customer service + '-200033': ExchangeError, // Fail to trade + '-200034': OrderNotFound, // The order does not exist + '-200035': OrderNotFound, // Cancellation error, order filled + '-200037': InvalidOrder, // Trade direction error + '-200038': ExchangeError, // Trading Market Error + '-200055': OrderNotFound, // Order record does not exist + '-300069': AuthenticationError, // api_key is illegal + '-300101': ExchangeError, // Transaction type error + '-300102': InvalidOrder, // Price or number cannot be less than 0 + '-300103': AuthenticationError, // Trade password error + '-301001': ExchangeNotAvailable, // Network Error-3 + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.marketGetSymbolList (params); + // + // { status: 200, + // msg: "", + // data: { ltc_btc: { id: "1", + // name: "ltc_btc", + // coinFrom: "ltc", + // coinTo: "btc", + // numberFloat: "4", + // priceFloat: "8", + // status: "1", + // minTrade: "0.010", + // maxTrade: "500000000.000" }, + // qtum_usdt: { id: "196", + // name: "qtum_usdt", + // coinFrom: "qtum", + // coinTo: "usdt", + // numberFloat: "4", + // priceFloat: "2", + // status: "1", + // minTrade: "0.100", + // maxTrade: "500000000.000" }, }, + // time: 1535969146, + // microtime: "0.66955600 1535969146", + // source: "api" } + // + const markets = this.safeValue (response, 'data'); + const ids = Object.keys (markets); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = markets[id]; + const numericId = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'coinFrom'); + const quoteId = this.safeString (market, 'coinTo'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'numberFloat'), + 'price': this.safeInteger (market, 'priceFloat'), + }; + result.push ({ + 'info': market, + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minTrade'), + 'max': this.safeFloat (market, 'maxTrade'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.assetsPostGetUserAssets (params); + // + // { + // status: 200, + // msg: "", + // data: { + // cny: 0, + // usd: 0, + // btc_total: 0, + // info: [{ + // "name": "zpr", + // "num": "37.49067275", + // "over": "37.49067275", + // "lock": "0.00000000", + // "btc": "0.00000000", + // "usd": "0.00000000", + // "cny": "0.00000000", + // }], + // }, + // time: 1535983966, + // microtime: "0.70400500 1535983966", + // source: "api", + // } + // + const balances = this.safeValue (response['data'], 'info'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'name'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'lock'); + account['total'] = this.safeFloat (balance, 'num'); + account['free'] = this.safeFloat (balance, 'over'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseTicker (ticker, market = undefined) { + // + // { symbol: "eth_btc", + // quoteVolume: "3905.72", + // volume: "97058.21", + // priceChange: "-1.72", + // priceChange24h: "-1.65", + // askPrice: "0.03971272", + // askQty: "0.0663", + // bidPrice: "0.03961469", + // bidQty: "19.5451", + // open: "0.04036769", + // high: "0.04062988", + // low: "0.03956123", + // now: "0.03970100", + // firstId: 115567767, + // lastId: 115795316, + // dealCount: 14078, + // numberPrecision: 4, + // pricePrecision: 8, + // cny: "1959.05", + // usd: "287.10", + // krw: "318655.82" } + // + const timestamp = undefined; + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'now'); + const open = this.safeFloat (ticker, 'open'); + let change = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': this.safeFloat (ticker, 'bidQty'), + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': this.safeFloat (ticker, 'askQty'), + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': this.safeFloat (ticker, 'priceChange24h'), + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + parseMicrotime (microtime) { + if (microtime === undefined) { + return microtime; + } + const parts = microtime.split (' '); + const milliseconds = parseFloat (parts[0]); + const seconds = parseInt (parts[1]); + const total = this.sum (seconds, milliseconds); + return parseInt (total * 1000); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetTicker (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { symbol: "eth_btc", + // quoteVolume: "3905.72", + // volume: "97058.21", + // priceChange: "-1.72", + // priceChange24h: "-1.65", + // askPrice: "0.03971272", + // askQty: "0.0663", + // bidPrice: "0.03961469", + // bidQty: "19.5451", + // open: "0.04036769", + // high: "0.04062988", + // low: "0.03956123", + // now: "0.03970100", + // firstId: 115567767, + // lastId: 115795316, + // dealCount: 14078, + // numberPrecision: 4, + // pricePrecision: 8, + // cny: "1959.05", + // usd: "287.10", + // krw: "318655.82" }, + // time: 1535970397, + // microtime: "0.76341900 1535970397", + // source: "api" } + // + const ticker = this.parseTicker (response['data'], market); + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + return this.extend (ticker, { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + }); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['symbols'] = ids.join (','); + } + const response = await this.marketGetTickerall (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { ela_btc: { symbol: "ela_btc", + // quoteVolume: "0.00", + // volume: "3.28", + // priceChange: "0.00", + // priceChange24h: "0.00", + // askPrice: "0.00147984", + // askQty: "5.4580", + // bidPrice: "0.00120230", + // bidQty: "12.5384", + // open: "0.00149078", + // high: "0.00149078", + // low: "0.00149078", + // now: "0.00149078", + // firstId: 115581219, + // lastId: 115581219, + // dealCount: 1, + // numberPrecision: 4, + // pricePrecision: 8, + // cny: "73.66", + // usd: "10.79", + // krw: "11995.03" } }, + // time: 1535971578, + // microtime: "0.39854200 1535971578", + // source: "api" } + // + const tickers = this.safeValue (response, 'data'); + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + const result = {}; + const ids = Object.keys (tickers); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let ticker = tickers[id]; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + } + ticker = this.parseTicker (tickers[id], market); + let symbol = ticker['symbol']; + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol !== undefined) { + result[symbol] = this.extend (ticker, { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + }); + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.marketGetDepth (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { asks: [ ["10.00000000", "0.4426", "4.4260"], + // ["1.00000000", "0.8339", "0.8339"], + // ["0.91700000", "0.0500", "0.0458"], + // ["0.20000000", "0.1000", "0.0200"], + // ["0.03987120", "16.1262", "0.6429"], + // ["0.03986120", "9.7523", "0.3887"] ], + // bids: [ ["0.03976145", "0.0359", "0.0014"], + // ["0.03973401", "20.9493", "0.8323"], + // ["0.03967970", "0.0328", "0.0013"], + // ["0.00000002", "10000.0000", "0.0002"], + // ["0.00000001", "231840.7500", "0.0023"] ], + // coinPair: "eth_btc" }, + // time: 1535974778, + // microtime: "0.04017400 1535974778", + // source: "api" } + // + const orderbook = this.safeValue (response, 'data'); + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { id: 115807453, + // t: "19:36:24", + // T: 1535974584, + // p: "0.03983296", + // n: "0.1000", + // s: "buy" }, + // + const id = this.safeString (trade, 'id'); + const timestamp = this.safeTimestamp (trade, 'T'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'p'); + const amount = this.safeFloat (trade, 'n'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = this.priceToPrecision (symbol, amount * price); + } + } + const side = this.safeString (trade, 's'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetOrder (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: [ { id: 115807453, + // t: "19:36:24", + // T: 1535974584, + // p: "0.03983296", + // n: "0.1000", + // s: "buy" }, + // { id: 115806811, + // t: "19:33:19", + // T: 1535974399, + // p: "0.03981135", + // n: "9.4612", + // s: "sell" } ], + // time: 1535974583, + // microtime: "0.57118100 1535974583", + // source: "api" } + // + return this.parseTrades (response['data'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { time: "1535973420000", + // open: "0.03975084", + // high: "0.03975084", + // low: "0.03967700", + // close: "0.03967700", + // volume: "12.4733", + // datetime: "2018-09-03 19:17:00" } + // + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const duration = this.parseTimeframe (timeframe) * 1000; + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'resolution': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['size'] = Math.min (limit, 300); // 1-300 + if (since !== undefined) { + request['to'] = this.sum (since, limit * duration * 1000); + } + } else { + if (since !== undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a limit argument if the since argument is specified'); + } + } + const response = await this.marketGetKline (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { bars: [ { time: "1535973420000", + // open: "0.03975084", + // high: "0.03975084", + // low: "0.03967700", + // close: "0.03967700", + // volume: "12.4733", + // datetime: "2018-09-03 19:17:00" }, + // { time: "1535955480000", + // open: "0.04009900", + // high: "0.04016745", + // low: "0.04009900", + // close: "0.04012074", + // volume: "74.4803", + // datetime: "2018-09-03 14:18:00" } ], + // resolution: "1min", + // symbol: "eth_btc", + // from: "1535973420000", + // to: "1535955480000", + // size: 300 }, + // time: 1535973435, + // microtime: "0.56462100 1535973435", + // source: "api" } + // + const bars = this.safeValue (response['data'], 'bars', undefined); + if (bars === undefined) { + return []; + } + return this.parseOHLCVs (bars, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', // partially filled + '2': 'closed', // filled + '3': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "id": "693248739", // order id + // "uId": "2074056", // uid + // "price": "100", // price + // "number": "10", // number + // "numberOver": "10", // undealed + // "flag": "sale", // flag + // "status": "0", // unfilled + // "coinFrom": "vtc", + // "coinTo": "dkkt", + // "numberDeal": "0" // dealed + // } + // + const id = this.safeString (order, 'id'); + let symbol = undefined; + if (market === undefined) { + const baseId = this.safeString (order, 'coinFrom'); + const quoteId = this.safeString (order, 'coinTo'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const marketId = baseId + '_' + quoteId; + if (marketId in this.markets_by_id) { + market = this.safeValue (this.markets_by_id, marketId); + } else { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeString (order, 'flag'); + if (side !== undefined) { + side = (side === 'sale') ? 'sell' : 'buy'; + } + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'number'); + const remaining = this.safeFloat (order, 'numberOver'); + const filled = this.safeFloat (order, 'numberDeal'); + let timestamp = this.safeInteger (order, 'timestamp'); + if (timestamp === undefined) { + timestamp = this.safeTimestamp (order, 'created'); + } + let cost = this.safeFloat (order, 'orderTotalPrice'); + if (price !== undefined) { + if (filled !== undefined) { + cost = filled * price; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' createOrder allows limit orders only'); + } + const market = this.market (symbol); + const orderType = (side === 'buy') ? '1' : '2'; + if (!this.password) { + throw new ExchangeError (this.id + ' createOrder() requires you to set exchange.password = "YOUR_TRADING_PASSWORD" (a trade password is NOT THE SAME as your login password)'); + } + const request = { + 'symbol': market['id'], + 'type': orderType, + 'price': this.priceToPrecision (symbol, price), + 'number': this.amountToPrecision (symbol, amount), + 'tradePwd': this.password, + }; + const response = await this.tradePostAddEntrustSheet (this.extend (request, params)); + // + // { + // "status": 200, + // "msg": "", + // "data": { + // "id": "693248739", // order id + // "uId": "2074056", // uid + // "price": "100", // price + // "number": "10", // number + // "numberOver": "10", // undealed + // "flag": "sale", // flag + // "status": "0", // unfilled + // "coinFrom": "vtc", + // "coinTo": "dkkt", + // "numberDeal": "0" // dealed + // }, + // "time": "1533035297", + // "microtime": "0.41892000 1533035297", + // "source": "api", + // } + // + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + const order = this.extend ({ + 'timestamp': timestamp, + }, response['data']); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'entrustSheetId': id, + }; + const response = await this.tradePostCancelEntrustSheet (this.extend (request, params)); + // + // { + // "status":200, + // "msg":"", + // "data":{ + // "updateAssetsData":{ + // "coin":"bz", + // "over":"1000.00000000", + // "lock":"-1000.00000000" + // }, + // "assetsInfo":{ + // "coin":"bz", + // "over":"9999.99999999", + // "lock":"9999.99999999" + // } + // }, + // "time":"1535464383", + // "microtime":"0.91558000 1535464383", + // "source":"api" + // } + // + return response; + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'ids': ids.join (','), + }; + const response = await this.tradePostCancelEntrustSheet (this.extend (request, params)); + // + // { + // "status":200, + // "msg":"", + // "data":{ + // "744173808":{ + // "updateAssetsData":{ + // "coin":"bz", + // "over":"100.00000000", + // "lock":"-100.00000000" + // }, + // "assetsInfo":{ + // "coin":"bz", + // "over":"899.99999999", + // "lock":"19099.99999999" + // } + // }, + // "744173809":{ + // "updateAssetsData":{ + // "coin":"bz", + // "over":"100.00000000", + // "lock":"-100.00000000" + // }, + // "assetsInfo":{ + // "coin":"bz", + // "over":"999.99999999", + // "lock":"18999.99999999" + // } + // } + // }, + // "time":"1535525649", + // "microtime":"0.05009400 1535525649", + // "source":"api" + // } + // + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'entrustSheetId': id, + }; + const response = await this.tradePostGetEntrustSheetInfo (this.extend (request, params)); + // + // { + // "status":200, + // "msg":"", + // "data":{ + // "id":"708279852", + // "uId":"2074056", + // "price":"100.00000000", + // "number":"10.0000", + // "total":"0.00000000", + // "numberOver":"10.0000", + // "numberDeal":"0.0000", + // "flag":"sale", + // "status":"0", //0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled + // "coinFrom":"bz", + // "coinTo":"usdt", + // "orderTotalPrice":"0", + // "created":"1533279876" + // }, + // "time":"1533280294", + // "microtime":"0.36859200 1533280294", + // "source":"api" + // } + // + return this.parseOrder (response['data']); + } + + async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coinFrom': market['baseId'], + 'coinTo': market['quoteId'], + // 'type': 1, // optional integer, 1 = buy, 2 = sell + // 'page': 1, // optional integer + // 'pageSize': 100, // optional integer, max 100 + // 'startTime': 1510235730, // optional integer timestamp in seconds + // 'endTime': 1510235730, // optional integer timestamp in seconds + }; + if (limit !== undefined) { + request['page'] = 1; + request['pageSize'] = limit; + } + if (since !== undefined) { + request['startTime'] = parseInt (since / 1000); + // request['endTime'] = parseInt (since / 1000); + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "status": 200, + // "msg": "", + // "data": { + // "data": [ + // { + // "id": "693248739", + // "uid": "2074056", + // "price": "100.00000000", + // "number": "10.0000", + // "total": "0.00000000", + // "numberOver": "0.0000", + // "numberDeal": "0.0000", + // "flag": "sale", + // "status": "3", // 0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled + // "isNew": "N", + // "coinFrom": "vtc", + // "coinTo": "dkkt", + // "created": "1533035300", + // }, + // { + // "id": "723086996", + // "uid": "2074056", + // "price": "100.00000000", + // "number": "10.0000", + // "total": "0.00000000", + // "numberOver": "0.0000", + // "numberDeal": "0.0000", + // "flag": "sale", + // "status": "3", + // "isNew": "N", + // "coinFrom": "bz", + // "coinTo": "usdt", + // "created": "1533523568", + // }, + // ], + // "pageInfo": { + // "limit": "10", + // "offest": "0", + // "current_page": "1", + // "page_size": "10", + // "total_count": "17", + // "page_count": "2", + // } + // }, + // "time": "1533279329", + // "microtime": "0.15305300 1533279329", + // "source": "api" + // } + // + const orders = this.safeValue (response['data'], 'data', []); + return this.parseOrders (orders, undefined, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersWithMethod ('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersWithMethod ('tradePostGetUserNowEntrustSheet', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersWithMethod ('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params); + } + + parseTransactionStatus (status) { + const statuses = { + '1': 'pending', + '2': 'pending', + '3': 'pending', + '4': 'ok', + '5': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // "id": '96275', + // "uid": '2109073', + // "wallet": '0xf4c4141c0127bc37b1d0c409a091920eba13ada7', + // "txid": '0xb7adfa52aa566f9ac112e3c01f77bd91179b19eab12092a9a5a8b33d5086e31d', + // "confirm": '12', + // "number": '0.50000000', + // "status": 4, + // "updated": '1534944168605', + // "addressUrl": 'https://etherscan.io/address/', + // "txidUrl": 'https://etherscan.io/tx/', + // "description": 'Ethereum', + // "coin": 'eth', + // "memo": '' + // } + // + // { + // "id":"397574", + // "uid":"2033056", + // "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k", + // "txid":"", + // "confirm":"0", + // "number":"1000.00000000", + // "status":1, + // "updated":"0", + // "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=", + // "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=", + // "description":"Tether", + // "coin":"usdt", + // "memo":"" + // } + // + // { + // "id":"153606", + // "uid":"2033056", + // "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k", + // "txid":"aa2b179f84cd6dedafd41845e0fbf7f01e14c0d71ea3140d03d6f5a9ccd93199", + // "confirm":"0", + // "number":"761.11110000", + // "status":4, + // "updated":"1536726133579", + // "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=", + // "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=", + // "description":"Tether", + // "coin":"usdt", + // "memo":"" + // } + // + let timestamp = this.safeInteger (transaction, 'updated'); + if (timestamp === 0) { + timestamp = undefined; + } + const currencyId = this.safeString (transaction, 'coin'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeStringLower (transaction, 'type'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + return { + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'txid'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'wallet'), + 'tag': this.safeString (transaction, 'memo'), + 'type': type, + 'amount': this.safeFloat (transaction, 'number'), + 'currency': code, + 'status': status, + 'updated': timestamp, + 'fee': undefined, + 'info': transaction, + }; + } + + parseTransactionsByType (type, transactions, code = undefined, since = undefined, limit = undefined) { + const result = []; + for (let i = 0; i < transactions.length; i++) { + const transaction = this.parseTransaction (this.extend ({ + 'type': type, + }, transactions[i])); + result.push (transaction); + } + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + parseTransactionType (type) { + const types = { + 'deposit': 1, + 'withdrawal': 2, + }; + return this.safeInteger (types, type, type); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsForType ('deposit', code, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsForType ('withdrawal', code, since, limit, params); + } + + async fetchTransactionsForType (type, code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency `code` argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + 'type': this.parseTransactionType (type), + }; + if (since !== undefined) { + request['startTime'] = parseInt (since / 1000).toString (); + } + if (limit !== undefined) { + request['page'] = 1; + request['pageSize'] = limit; + } + const response = await this.tradePostDepositOrWithdraw (this.extend (request, params)); + const transactions = this.safeValue (response['data'], 'data', []); + return this.parseTransactionsByType (type, transactions, code, since, limit); + } + + nonce () { + const currentTimestamp = this.seconds (); + if (currentTimestamp > this.options['lastNonceTimestamp']) { + this.options['lastNonceTimestamp'] = currentTimestamp; + this.options['lastNonce'] = 100000; + } + this.options['lastNonce'] = this.sum (this.options['lastNonce'], 1); + return this.options['lastNonce']; + } + + sign (path, api = 'market', method = 'GET', params = {}, headers = undefined, body = undefined) { + const baseUrl = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname }); + let url = baseUrl + '/' + this.capitalize (api) + '/' + path; + let query = undefined; + if (api === 'market') { + query = this.urlencode (params); + if (query.length) { + url += '?' + query; + } + } else { + this.checkRequiredCredentials (); + body = this.rawencode (this.keysort (this.extend ({ + 'apiKey': this.apiKey, + 'timeStamp': this.seconds (), + 'nonce': this.nonce (), + }, params))); + body += '&sign=' + this.hash (this.encode (body + this.secret)); + headers = { 'Content-type': 'application/x-www-form-urlencoded' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const status = this.safeString (response, 'status'); + if (status !== undefined) { + const feedback = this.id + ' ' + body; + // + // {"status":-107,"msg":"","data":"","time":1535968848,"microtime":"0.89092200 1535968848","source":"api"} + // + if (status === '200') { + // + // {"status":200,"msg":"","data":-200031,"time":1535999806,"microtime":"0.85476800 1535999806","source":"api"} + // + const code = this.safeInteger (response, 'data'); + if (code !== undefined) { + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } else { + return; // no error + } + } + this.throwExactlyMatchedException (this.exceptions, status, feedback); + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],46:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class bl3p extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bl3p', + 'name': 'BL3P', + 'countries': [ 'NL', 'EU' ], // Netherlands, EU + 'rateLimit': 1000, + 'version': '1', + 'comment': 'An exchange market by BitonicNL', + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg', + 'api': 'https://api.bl3p.eu', + 'www': [ + 'https://bl3p.eu', + 'https://bitonic.nl', + ], + 'doc': [ + 'https://github.com/BitonicNL/bl3p-api/tree/master/docs', + 'https://bl3p.eu/api', + 'https://bitonic.nl/en/api', + ], + }, + 'api': { + 'public': { + 'get': [ + '{market}/ticker', + '{market}/orderbook', + '{market}/trades', + ], + }, + 'private': { + 'post': [ + '{market}/money/depth/full', + '{market}/money/order/add', + '{market}/money/order/cancel', + '{market}/money/order/result', + '{market}/money/orders', + '{market}/money/orders/history', + '{market}/money/trades/fetch', + 'GENMKT/money/info', + 'GENMKT/money/deposit_address', + 'GENMKT/money/new_deposit_address', + 'GENMKT/money/wallet/history', + 'GENMKT/money/withdraw', + ], + }, + }, + 'markets': { + 'BTC/EUR': { 'id': 'BTCEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/EUR': { 'id': 'LTCEUR', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'LTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGENMKTMoneyInfo (params); + const data = this.safeValue (response, 'data', {}); + const wallets = this.safeValue (data, 'wallets'); + const result = { 'info': data }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const wallet = this.safeValue (wallets, currencyId, {}); + const available = this.safeValue (wallet, 'available', {}); + const balance = this.safeValue (wallet, 'balance', {}); + const account = this.account (); + account['free'] = this.safeFloat (available, 'value'); + account['total'] = this.safeFloat (balance, 'value'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1) { + return [ + bidask[priceKey] / 100000.0, + bidask[amountKey] / 100000000.0, + ]; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketOrderbook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data'); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price_int', 'amount_int'); + } + + async fetchTicker (symbol, params = {}) { + const request = { + 'market': this.marketId (symbol), + }; + const ticker = await this.publicGetMarketTicker (this.extend (request, params)); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker['volume'], '24h'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const id = this.safeString (trade, 'trade_id'); + const timestamp = this.safeInteger (trade, 'date'); + let price = this.safeFloat (trade, 'price_int'); + if (price !== undefined) { + price /= 100000.0; + } + let amount = this.safeFloat (trade, 'amount_int'); + if (amount !== undefined) { + amount /= 100000000.0; + } + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + const market = this.market (symbol); + const response = await this.publicGetMarketTrades (this.extend ({ + 'market': market['id'], + }, params)); + const result = this.parseTrades (response['data']['trades'], market, since, limit); + return result; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + const market = this.market (symbol); + const order = { + 'market': market['id'], + 'amount_int': parseInt (amount * 100000000), + 'fee_currency': market['quote'], + 'type': (side === 'buy') ? 'bid' : 'ask', + }; + if (type === 'limit') { + order['price_int'] = parseInt (price * 100000.0); + } + const response = await this.privatePostMarketMoneyOrderAdd (this.extend (order, params)); + const orderId = this.safeString (response['data'], 'order_id'); + return { + 'info': response, + 'id': orderId, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order_id': id, + }; + return await this.privatePostMarketMoneyOrderCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = this.implodeParams (path, params); + let url = this.urls['api'] + '/' + this.version + '/' + request; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); + const secret = this.base64ToBinary (this.secret); + // eslint-disable-next-line quotes + const auth = request + "\0" + body; + const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Rest-Key': this.apiKey, + 'Rest-Sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7}],47:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const bittrex = require ('./bittrex.js'); +const { ExchangeError, AuthenticationError, InvalidOrder, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bleutrade extends bittrex { + describe () { + const timeframes = { + '15m': '15m', + '20m': '20m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '3h': '3h', + '4h': '4h', + '6h': '6h', + '8h': '8h', + '12h': '12h', + '1d': '1d', + }; + const result = this.deepExtend (super.describe (), { + 'id': 'bleutrade', + 'name': 'Bleutrade', + 'countries': [ 'BR' ], // Brazil + 'rateLimit': 1000, + 'version': 'v2', + 'certified': false, + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'fetchOrders': true, + 'fetchClosedOrders': true, + 'fetchOrderTrades': true, + 'fetchLedger': true, + }, + 'timeframes': timeframes, + 'hostname': 'bleutrade.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30303000-b602dbe6-976d-11e7-956d-36c5049c01e7.jpg', + 'api': { + 'public': 'https://{hostname}/api/v2', + 'account': 'https://{hostname}/api/v2', + 'market': 'https://{hostname}/api/v2', + 'v3Private': 'https://{hostname}/api/v3/private', + 'v3Public': 'https://{hostname}/api/v3/public', + }, + 'www': 'https://bleutrade.com', + 'doc': [ + 'https://app.swaggerhub.com/apis-docs/bleu/white-label/3.0.0', + ], + 'fees': 'https://bleutrade.com/help/fees_and_deadlines', + }, + 'api': { + 'account': { + 'get': [ + 'balance', + 'balances', + 'depositaddress', + 'deposithistory', + 'order', + 'orders', + 'orderhistory', + 'withdrawhistory', + 'withdraw', + ], + }, + 'public': { + 'get': [ + 'candles', + 'currencies', + 'markethistory', + 'markets', + 'marketsummaries', + 'marketsummary', + 'orderbook', + 'ticker', + ], + }, + 'v3Public': { + 'get': [ + 'assets', + 'markets', + 'ticker', + 'marketsummary', + 'marketsummaries', + 'orderbook', + 'markethistory', + 'candles', + ], + }, + 'v3Private': { + 'get': [ + 'getbalance', + 'getbalances', + 'buylimit', + 'selllimit', + 'buylimitami', + 'selllimitami', + 'buystoplimit', + 'sellstoplimit', + 'ordercancel', + 'getopenorders', + 'getdeposithistory', + 'getdepositaddress', + 'getmytransactions', + 'withdraw', + 'directtransfer', + 'getwithdrawhistory', + 'getlimits', + ], + }, + }, + 'fees': { + 'funding': { + 'withdraw': { + 'ADC': 0.1, + 'BTA': 0.1, + 'BITB': 0.1, + 'BTC': 0.001, + 'BCC': 0.001, + 'BTCD': 0.001, + 'BTG': 0.001, + 'BLK': 0.1, + 'CDN': 0.1, + 'CLAM': 0.01, + 'DASH': 0.001, + 'DCR': 0.05, + 'DGC': 0.1, + 'DP': 0.1, + 'DPC': 0.1, + 'DOGE': 10.0, + 'EFL': 0.1, + 'ETH': 0.01, + 'EXP': 0.1, + 'FJC': 0.1, + 'BSTY': 0.001, + 'GB': 0.1, + 'NLG': 0.1, + 'HTML': 1.0, + 'LTC': 0.001, + 'MONA': 0.01, + 'MOON': 1.0, + 'NMC': 0.015, + 'NEOS': 0.1, + 'NVC': 0.05, + 'OK': 0.1, + 'PPC': 0.1, + 'POT': 0.1, + 'XPM': 0.001, + 'QTUM': 0.1, + 'RDD': 0.1, + 'SLR': 0.1, + 'START': 0.1, + 'SLG': 0.1, + 'TROLL': 0.1, + 'UNO': 0.01, + 'VRC': 0.1, + 'VTC': 0.1, + 'XVP': 0.1, + 'WDC': 0.001, + 'ZET': 0.1, + }, + }, + }, + 'commonCurrencies': { + 'EPC': 'Epacoin', + }, + 'exceptions': { + 'exact': { + 'Insufficient funds!': InsufficientFunds, + 'Invalid Order ID': InvalidOrder, + 'Invalid apikey or apisecret': AuthenticationError, + }, + }, + 'options': { + // price precision by quote currency code + 'pricePrecisionByCode': { + 'USD': 3, + }, + 'parseOrderStatus': true, + 'disableNonce': false, + 'symbolSeparator': '_', + }, + }); + // bittrex inheritance override + result['timeframes'] = timeframes; + return result; + } + + async fetchMarkets (params = {}) { + // https://github.com/ccxt/ccxt/issues/5668 + const response = await this.publicGetMarkets (params); + const result = []; + const markets = this.safeValue (response, 'result'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'MarketName'); + const baseId = this.safeString (market, 'MarketCurrency'); + const quoteId = this.safeString (market, 'BaseCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + let pricePrecision = 8; + if (quote in this.options['pricePrecisionByCode']) { + pricePrecision = this.options['pricePrecisionByCode'][quote]; + } + const precision = { + 'amount': 8, + 'price': pricePrecision, + }; + // bittrex uses boolean values, bleutrade uses strings + let active = this.safeValue (market, 'IsActive', false); + if ((active !== 'false') && active) { + active = true; + } else { + active = false; + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'MinTradeSize'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseOrderStatus (status) { + const statuses = { + 'OK': 'closed', + 'OPEN': 'open', + 'CANCELED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // Possible params + // orderstatus (ALL, OK, OPEN, CANCELED) + // ordertype (ALL, BUY, SELL) + // depth (optional, default is 500, max is 20000) + await this.loadMarkets (); + let market = undefined; + let marketId = 'ALL'; + if (symbol !== undefined) { + market = this.market (symbol); + marketId = market['id']; + } + const request = { + 'market': marketId, + 'orderstatus': 'ALL', + }; + const response = await this.accountGetOrders (this.extend (request, params)); + return this.parseOrders (response['result'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (response, 'status', 'closed'); + } + + getOrderIdField () { + return 'orderid'; + } + + parseSymbol (id) { + let [ base, quote ] = id.split (this.options['symbolSeparator']); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + return base + '/' + quote; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'type': 'ALL', + }; + if (limit !== undefined) { + request['depth'] = limit; // 50 + } + const response = await this.publicGetOrderbook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'result'); + if (!orderbook) { + throw new ExchangeError (this.id + ' publicGetOrderbook() returneded no result ' + this.json (response)); + } + return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity'); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + // Currently we can't set the makerOrTaker field, but if the user knows the order side then it can be + // determined (if the side of the trade is different to the side of the order, then the trade is maker). + // Similarly, the correct 'side' for the trade is that of the order. + // The trade fee can be set by the user, it is always 0.25% and is taken in the quote currency. + await this.loadMarkets (); + const request = { + 'orderid': id, + }; + const response = await this.accountGetOrderhistory (this.extend (request, params)); + return this.parseTrades (response['result'], undefined, since, limit, { + 'order': id, + }); + } + + async fetchTransactionsByType (type, code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const method = (type === 'deposit') ? 'accountGetDeposithistory' : 'accountGetWithdrawhistory'; + const response = await this[method] (params); + const result = this.parseTransactions (response['result']); + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('deposit', code, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('withdrawal', code, since, limit, params); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (ohlcv['TimeStamp'] + '+00:00'); + return [ + timestamp, + this.safeFloat (ohlcv, 'Open'), + this.safeFloat (ohlcv, 'High'), + this.safeFloat (ohlcv, 'Low'), + this.safeFloat (ohlcv, 'Close'), + this.safeFloat (ohlcv, 'Volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'period': this.timeframes[timeframe], + 'market': market['id'], + 'count': limit, + }; + const response = await this.publicGetCandles (this.extend (request, params)); + if ('result' in response) { + if (response['result']) { + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + } + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (trade['TimeStamp'] + '+00:00'); + let side = undefined; + if (trade['OrderType'] === 'BUY') { + side = 'buy'; + } else if (trade['OrderType'] === 'SELL') { + side = 'sell'; + } + const id = this.safeString2 (trade, 'TradeID', 'ID'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + const price = this.safeFloat (trade, 'Price'); + const amount = this.safeFloat (trade, 'Quantity'); + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + parseLedgerEntryType (type) { + // deposits don't seem to appear in here + const types = { + 'TRADE': 'trade', + 'WITHDRAW': 'transaction', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // trade (both sides) + // + // { + // ID: 109660527, + // TimeStamp: '2018-11-14 15:12:57.140776', + // Asset: 'ETH', + // AssetName: 'Ethereum', + // Amount: 0.01, + // Type: 'TRADE', + // Description: 'Trade +, order id 133111123', + // Comments: '', + // CoinSymbol: 'ETH', + // CoinName: 'Ethereum' + // } + // + // { + // ID: 109660526, + // TimeStamp: '2018-11-14 15:12:57.140776', + // Asset: 'BTC', + // AssetName: 'Bitcoin', + // Amount: -0.00031776, + // Type: 'TRADE', + // Description: 'Trade -, order id 133111123, fee -0.00000079', + // Comments: '', + // CoinSymbol: 'BTC', + // CoinName: 'Bitcoin' + // } + // + // withdrawal + // + // { + // ID: 104672316, + // TimeStamp: '2018-05-03 08:18:19.031831', + // Asset: 'DOGE', + // AssetName: 'Dogecoin', + // Amount: -61893.87864686, + // Type: 'WITHDRAW', + // Description: 'Withdraw: 61883.87864686 to address DD8tgehNNyYB2iqVazi2W1paaztgcWXtF6; fee 10.00000000', + // Comments: '', + // CoinSymbol: 'DOGE', + // CoinName: 'Dogecoin' + // } + // + const code = this.safeCurrencyCode (this.safeString (item, 'CoinSymbol'), currency); + const description = this.safeString (item, 'Description'); + const type = this.parseLedgerEntryType (this.safeString (item, 'Type')); + let referenceId = undefined; + let fee = undefined; + const delimiter = (type === 'trade') ? ', ' : '; '; + const parts = description.split (delimiter); + for (let i = 0; i < parts.length; i++) { + let part = parts[i]; + if (part.indexOf ('fee') === 0) { + part = part.replace ('fee ', ''); + let feeCost = parseFloat (part); + if (feeCost < 0) { + feeCost = -feeCost; + } + fee = { + 'cost': feeCost, + 'currency': code, + }; + } else if (part.indexOf ('order id') === 0) { + referenceId = part.replace ('order id', ''); + } + // + // does not belong to Ledger, related to parseTransaction + // + // if (part.indexOf ('Withdraw') === 0) { + // const details = part.split (' to address '); + // if (details.length > 1) { + // address = details[1]; + // } + // + } + const timestamp = this.parse8601 (this.safeString (item, 'TimeStamp')); + let amount = this.safeFloat (item, 'Amount'); + let direction = undefined; + if (amount !== undefined) { + direction = 'in'; + if (amount < 0) { + direction = 'out'; + amount = -amount; + } + } + const id = this.safeString (item, 'ID'); + return { + 'id': id, + 'info': item, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'direction': direction, + 'account': undefined, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': undefined, + 'after': undefined, + 'status': 'ok', + 'fee': fee, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + // + // if (code === undefined) { + // throw new ExchangeError (this.id + ' fetchClosedOrders requires a `symbol` argument'); + // } + // + await this.loadMarkets (); + const request = {}; + // + // if (code !== undefined) { + // const currency = this.market (code); + // request['asset'] = currency['id']; + // } + // + const response = await this.v3PrivateGetGetmytransactions (this.extend (request, params)); + return this.parseLedger (response['result'], code, since, limit); + } + + parseOrder (order, market = undefined) { + // + // fetchOrders + // + // { + // OrderId: '107220258', + // Exchange: 'LTC_BTC', + // Type: 'SELL', + // Quantity: '2.13040000', + // QuantityRemaining: '0.00000000', + // Price: '0.01332672', + // Status: 'OK', + // Created: '2018-06-30 04:55:50', + // QuantityBaseTraded: '0.02839125', + // Comments: '' + // } + // + let side = this.safeString2 (order, 'OrderType', 'Type'); + const isBuyOrder = (side === 'LIMIT_BUY') || (side === 'BUY'); + const isSellOrder = (side === 'LIMIT_SELL') || (side === 'SELL'); + if (isBuyOrder) { + side = 'buy'; + } + if (isSellOrder) { + side = 'sell'; + } + // We parse different fields in a very specific order. + // Order might well be closed and then canceled. + let status = undefined; + if (('Opened' in order) && order['Opened']) { + status = 'open'; + } + if (('Closed' in order) && order['Closed']) { + status = 'closed'; + } + if (('CancelInitiated' in order) && order['CancelInitiated']) { + status = 'canceled'; + } + if (('Status' in order) && this.options['parseOrderStatus']) { + status = this.parseOrderStatus (this.safeString (order, 'Status')); + } + let symbol = undefined; + const marketId = this.safeString (order, 'Exchange'); + if (marketId === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } else { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = this.parseSymbol (marketId); + } + } + let timestamp = undefined; + if ('Opened' in order) { + timestamp = this.parse8601 (order['Opened'] + '+00:00'); + } + if ('Created' in order) { + timestamp = this.parse8601 (order['Created'] + '+00:00'); + } + let lastTradeTimestamp = undefined; + if (('TimeStamp' in order) && (order['TimeStamp'] !== undefined)) { + lastTradeTimestamp = this.parse8601 (order['TimeStamp'] + '+00:00'); + } + if (('Closed' in order) && (order['Closed'] !== undefined)) { + lastTradeTimestamp = this.parse8601 (order['Closed'] + '+00:00'); + } + if (timestamp === undefined) { + timestamp = lastTradeTimestamp; + } + let fee = undefined; + let commission = undefined; + if ('Commission' in order) { + commission = 'Commission'; + } else if ('CommissionPaid' in order) { + commission = 'CommissionPaid'; + } + if (commission) { + fee = { + 'cost': this.safeFloat (order, commission), + }; + if (market !== undefined) { + fee['currency'] = market['quote']; + } else if (symbol !== undefined) { + const currencyIds = symbol.split ('/'); + const quoteCurrencyId = currencyIds[1]; + fee['currency'] = this.safeCurrencyCode (quoteCurrencyId); + } + } + let price = this.safeFloat (order, 'Price'); + let cost = undefined; + const amount = this.safeFloat (order, 'Quantity'); + const remaining = this.safeFloat (order, 'QuantityRemaining'); + let filled = undefined; + if (amount !== undefined && remaining !== undefined) { + filled = amount - remaining; + } + if (!cost) { + if (price && filled) { + cost = price * filled; + } + } + if (!price) { + if (cost && filled) { + price = cost / filled; + } + } + const average = this.safeFloat (order, 'PricePerUnit'); + const id = this.safeString2 (order, 'OrderUuid', 'OrderId'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + parseTransaction (transaction, currency = undefined) { + // + // deposit: + // + // { + // Id: '96974373', + // Coin: 'DOGE', + // Amount: '12.05752192', + // TimeStamp: '2017-09-29 08:10:09', + // Label: 'DQqSjjhzCm3ozT4vAevMUHgv4vsi9LBkoE', + // } + // + // withdrawal: + // + // { + // Id: '98009125', + // Coin: 'DOGE', + // Amount: '-483858.64312050', + // TimeStamp: '2017-11-22 22:29:05', + // Label: '483848.64312050;DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT;10.00000000', + // TransactionId: '8563105276cf798385fee7e5a563c620fea639ab132b089ea880d4d1f4309432', + // } + // + // { + // "Id": "95820181", + // "Coin": "BTC", + // "Amount": "-0.71300000", + // "TimeStamp": "2017-07-19 17:14:24", + // "Label": "0.71200000;PER9VM2txt4BTdfyWgvv3GziECRdVEPN63;0.00100000", + // "TransactionId": "CANCELED" + // } + // + const id = this.safeString (transaction, 'Id'); + let amount = this.safeFloat (transaction, 'Amount'); + let type = 'deposit'; + if (amount < 0) { + amount = Math.abs (amount); + type = 'withdrawal'; + } + const currencyId = this.safeString (transaction, 'Coin'); + const code = this.safeCurrencyCode (currencyId, currency); + const label = this.safeString (transaction, 'Label'); + const timestamp = this.parse8601 (this.safeString (transaction, 'TimeStamp')); + let txid = this.safeString (transaction, 'TransactionId'); + let address = undefined; + let feeCost = undefined; + const labelParts = label.split (';'); + if (labelParts.length === 3) { + amount = parseFloat (labelParts[0]); + address = labelParts[1]; + feeCost = parseFloat (labelParts[2]); + } else { + address = label; + } + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'currency': code, + 'cost': feeCost, + }; + } + let status = 'ok'; + if (txid === 'CANCELED') { + txid = undefined; + status = 'canceled'; + } + return { + 'info': transaction, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }) + '/'; + if (api === 'v3Private' || api === 'account') { + this.checkRequiredCredentials (); + if (api === 'account') { + url += api + '/'; + } + if (((api === 'account') && (path !== 'withdraw')) || (path === 'openorders')) { + url += method.toLowerCase (); + } + const request = { + 'apikey': this.apiKey, + }; + request['nonce'] = this.nonce (); + url += path + '?' + this.urlencode (this.extend (request, params)); + const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512'); + headers = { 'apisign': signature }; + } else { + url += api + '/' + method.toLowerCase () + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/errors":9,"./bittrex.js":44}],48:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InvalidOrder, AuthenticationError, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class braziliex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'braziliex', + 'name': 'Braziliex', + 'countries': [ 'BR' ], + 'rateLimit': 1000, + 'has': { + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/34703593-c4498674-f504-11e7-8d14-ff8e44fb78c1.jpg', + 'api': 'https://braziliex.com/api/v1', + 'www': 'https://braziliex.com/', + 'doc': 'https://braziliex.com/exchange/api.php', + 'fees': 'https://braziliex.com/exchange/fees.php', + 'referral': 'https://braziliex.com/?ref=5FE61AB6F6D67DA885BC98BA27223465', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'ticker', + 'ticker/{market}', + 'orderbook/{market}', + 'tradehistory/{market}', + ], + }, + 'private': { + 'post': [ + 'balance', + 'complete_balance', + 'open_orders', + 'trade_history', + 'deposit_address', + 'sell', + 'buy', + 'cancel_order', + 'order_status', + ], + }, + }, + 'commonCurrencies': { + 'EPC': 'Epacoin', + 'ABC': 'Anti Bureaucracy Coin', + }, + 'fees': { + 'trading': { + 'maker': 0.005, + 'taker': 0.005, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'options': { + 'fetchCurrencies': { + 'expires': 1000, // 1 second + }, + }, + }); + } + + async fetchCurrenciesFromCache (params = {}) { + // this method is now redundant + // currencies are now fetched before markets + const options = this.safeValue (this.options, 'fetchCurrencies', {}); + const timestamp = this.safeInteger (options, 'timestamp'); + const expires = this.safeInteger (options, 'expires', 1000); + const now = this.milliseconds (); + if ((timestamp === undefined) || ((now - timestamp) > expires)) { + const response = await this.publicGetCurrencies (params); + this.options['fetchCurrencies'] = this.extend (options, { + 'response': response, + 'timestamp': now, + }); + } + return this.safeValue (this.options['fetchCurrencies'], 'response'); + } + + async fetchCurrencies (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + // + // { + // brl: { + // name: "Real", + // withdrawal_txFee: 0.0075, + // txWithdrawalFee: 9, + // MinWithdrawal: 30, + // minConf: 1, + // minDeposit: 0, + // txDepositFee: 0, + // txDepositPercentageFee: 0, + // minAmountTradeFIAT: 5, + // minAmountTradeBTC: 0.0001, + // minAmountTradeUSDT: 0.0001, + // decimal: 8, + // decimal_withdrawal: 8, + // active: 1, + // dev_active: 1, + // under_maintenance: 0, + // order: "010", + // is_withdrawal_active: 1, + // is_deposit_active: 1, + // is_token_erc20: 0, + // is_fiat: 1, + // gateway: 0, + // }, + // btc: { + // name: "Bitcoin", + // txWithdrawalMinFee: 0.000125, + // txWithdrawalFee: 0.00015625, + // MinWithdrawal: 0.0005, + // minConf: 1, + // minDeposit: 0, + // txDepositFee: 0, + // txDepositPercentageFee: 0, + // minAmountTradeFIAT: 5, + // minAmountTradeBTC: 0.0001, + // minAmountTradeUSDT: 0.0001, + // decimal: 8, + // decimal_withdrawal: 8, + // active: 1, + // dev_active: 1, + // under_maintenance: 0, + // order: "011", + // is_withdrawal_active: 1, + // is_deposit_active: 1, + // is_token_erc20: 0, + // is_fiat: 0, + // gateway: 1, + // } + // } + // + this.options['currencies'] = { + 'timestamp': this.milliseconds (), + 'response': response, + }; + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = response[id]; + const precision = this.safeInteger (currency, 'decimal'); + const code = this.safeCurrencyCode (id); + let active = this.safeInteger (currency, 'active') === 1; + const maintenance = this.safeInteger (currency, 'under_maintenance'); + if (maintenance !== 0) { + active = false; + } + const canWithdraw = this.safeInteger (currency, 'is_withdrawal_active') === 1; + const canDeposit = this.safeInteger (currency, 'is_deposit_active') === 1; + if (!canWithdraw || !canDeposit) { + active = false; + } + result[code] = { + 'id': id, + 'code': code, + 'name': currency['name'], + 'active': active, + 'precision': precision, + 'funding': { + 'withdraw': { + 'active': canWithdraw, + 'fee': this.safeFloat (currency, 'txWithdrawalFee'), + }, + 'deposit': { + 'active': canDeposit, + 'fee': this.safeFloat (currency, 'txDepositFee'), + }, + }, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'MinWithdrawal'), + 'max': Math.pow (10, precision), + }, + 'deposit': { + 'min': this.safeFloat (currency, 'minDeposit'), + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const currencies = await this.fetchCurrenciesFromCache (params); + const response = await this.publicGetTicker (); + // + // { + // btc_brl: { + // active: 1, + // market: 'btc_brl', + // last: 14648, + // percentChange: -0.95, + // baseVolume24: 27.856, + // quoteVolume24: 409328.039, + // baseVolume: 27.856, + // quoteVolume: 409328.039, + // highestBid24: 14790, + // lowestAsk24: 14450.01, + // highestBid: 14450.37, + // lowestAsk: 14699.98 + // }, + // ... + // } + // + const ids = Object.keys (response); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = response[id]; + const [ baseId, quoteId ] = id.split ('_'); + const uppercaseBaseId = baseId.toUpperCase (); + const uppercaseQuoteId = quoteId.toUpperCase (); + const base = this.safeCurrencyCode (uppercaseBaseId); + const quote = this.safeCurrencyCode (uppercaseQuoteId); + const symbol = base + '/' + quote; + const baseCurrency = this.safeValue (currencies, baseId, {}); + const quoteCurrency = this.safeValue (currencies, quoteId, {}); + const quoteIsFiat = this.safeInteger (quoteCurrency, 'is_fiat', 0); + let minCost = undefined; + if (quoteIsFiat) { + minCost = this.safeFloat (baseCurrency, 'minAmountTradeFIAT'); + } else { + minCost = this.safeFloat (baseCurrency, 'minAmountTrade' + uppercaseQuoteId); + } + const isActive = this.safeInteger (market, 'active'); + const active = (isActive === 1); + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highestBid24'), + 'low': this.safeFloat (ticker, 'lowestAsk24'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'percentChange'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume24'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume24'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTickerMarket (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const marketId = ids[i]; + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (response[marketId], market); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + }; + const response = await this.publicGetOrderbookMarket (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString2 (trade, 'date_exec', 'date')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const cost = this.safeFloat (trade, 'total'); + const orderId = this.safeString (trade, 'order_number'); + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, '_id'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTradehistoryMarket (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privatePostCompleteBalance (params); + const result = { 'info': balances }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = balances[currencyId]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + // + // { + // "order_number":"58ee441d05f8233fadabfb07", + // "type":"buy", + // "market":"ltc_btc", + // "price":"0.01000000", + // "amount":"0.00200000", + // "total":"0.00002000", + // "progress":"1.0000", + // "date":"2017-03-12 15:13:33" + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'market'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = this.safeInteger (order, 'timestamp'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (order, 'date')); + } + const price = this.safeFloat (order, 'price'); + const cost = this.safeFloat (order, 'total', 0.0); + const amount = this.safeFloat (order, 'amount'); + const filledPercentage = this.safeFloat (order, 'progress'); + const filled = amount * filledPercentage; + const remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); + let info = order; + if ('info' in info) { + info = order['info']; + } + const id = this.safeString (order, 'order_number'); + const fee = this.safeValue (order, 'fee'); // propagated from createOrder + const status = (filledPercentage === 1.0) ? 'closed' : 'open'; + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': order['type'], + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': info, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side); + const request = { + 'market': market['id'], + // 'price': this.priceToPrecision (symbol, price), + // 'amount': this.amountToPrecision (symbol, amount), + 'price': price, + 'amount': amount, + }; + const response = await this[method] (this.extend (request, params)); + const success = this.safeInteger (response, 'success'); + if (success !== 1) { + throw new InvalidOrder (this.id + ' ' + this.json (response)); + } + let parts = response['message'].split (' / '); + parts = parts.slice (1); + const feeParts = parts[5].split (' '); + const order = this.parseOrder ({ + 'timestamp': this.milliseconds (), + 'order_number': response['order_number'], + 'type': parts[0].toLowerCase (), + 'market': parts[0].toLowerCase (), + 'amount': parts[2].split (' ')[1], + 'price': parts[3].split (' ')[1], + 'total': parts[4].split (' ')[1], + 'fee': { + 'cost': parseFloat (feeParts[1]), + 'currency': feeParts[2], + }, + 'progress': '0.0', + 'info': response, + }, market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_number': id, + 'market': market['id'], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_number': id, + 'market': market['id'], + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privatePostOpenOrders (this.extend (request, params)); + return this.parseOrders (response['order_open'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseTrades (response['trade_history'], market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostDepositAddress (this.extend (request, params)); + const address = this.safeString (response, 'deposit_address'); + this.checkAddress (address); + const tag = this.safeString (response, 'payment_id'); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + api; + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + query = this.extend ({ + 'command': path, + 'nonce': this.nonce (), + }, query); + body = this.urlencode (query); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Content-type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ((typeof response === 'string') && (response.length < 1)) { + throw new ExchangeError (this.id + ' returned empty response'); + } + if ('success' in response) { + const success = this.safeInteger (response, 'success'); + if (success === 0) { + const message = this.safeString (response, 'message'); + if (message === 'Invalid APIKey') { + throw new AuthenticationError (message); + } + throw new ExchangeError (message); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],49:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, DDoSProtection, InvalidOrder, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcalpha extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcalpha', + 'name': 'BTC-Alpha', + 'countries': [ 'US' ], + 'version': 'v1', + 'has': { + 'fetchTicker': false, + 'fetchOHLCV': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '4h': '240', + '1d': 'D', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/42625213-dabaa5da-85cf-11e8-8f99-aa8f8f7699f0.jpg', + 'api': 'https://btc-alpha.com/api', + 'www': 'https://btc-alpha.com', + 'doc': 'https://btc-alpha.github.io/api-docs', + 'fees': 'https://btc-alpha.com/fees/', + 'referral': 'https://btc-alpha.com/?r=123788', + }, + 'api': { + 'public': { + 'get': [ + 'currencies/', + 'pairs/', + 'orderbook/{pair_name}/', + 'exchanges/', + 'charts/{pair}/{type}/chart/', + ], + }, + 'private': { + 'get': [ + 'wallets/', + 'orders/own/', + 'order/{id}/', + 'exchanges/own/', + 'deposits/', + 'withdraws/', + ], + 'post': [ + 'order/', + 'order-cancel/', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.00135, + 'LTC': 0.0035, + 'XMR': 0.018, + 'ZEC': 0.002, + 'ETH': 0.01, + 'ETC': 0.01, + 'SIB': 1.5, + 'CCRB': 4, + 'PZM': 0.05, + 'ITI': 0.05, + 'DCY': 5, + 'R': 5, + 'ATB': 0.05, + 'BRIA': 0.05, + 'KZC': 0.05, + 'HWC': 1, + 'SPA': 1, + 'SMS': 0.001, + 'REC': 0.01, + 'SUP': 1, + 'BQ': 100, + 'GDS': 0.1, + 'EVN': 300, + 'TRKC': 0.01, + 'UNI': 1, + 'STN': 1, + 'BCH': undefined, + 'QBIC': 0.5, + }, + }, + }, + 'commonCurrencies': { + 'CBC': 'Cashbery', + }, + 'exceptions': { + 'exact': {}, + 'broad': { + 'Out of balance': InsufficientFunds, // {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"} + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetPairs (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'currency1'); + const quoteId = this.safeString (market, 'currency2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': this.safeInteger (market, 'price_precision'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minimum_order_size'), + 'max': this.safeFloat (market, 'maximum_order_size'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_name': this.marketId (symbol), + }; + if (limit) { + request['limit_sell'] = limit; + request['limit_buy'] = limit; + } + const response = await this.publicGetOrderbookPairName (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'buy', 'sell', 'price', 'amount'); + } + + parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) { + const result = []; + for (let i = 0; i < bidasks.length; i++) { + const bidask = bidasks[i]; + if (bidask) { + result.push (this.parseBidAsk (bidask, priceKey, amountKey)); + } + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market === undefined) { + market = this.safeValue (this.marketsById, trade['pair']); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const id = this.safeString2 (trade, 'id', 'tid'); + const side = this.safeString2 (trade, 'my_side', 'side'); + const orderId = this.safeString (trade, 'o_id'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const trades = await this.publicGetExchanges (this.extend (request, params)); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.publicGetChartsPairTypeChart (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWallets (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'reserve'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + '1': 'open', + '2': 'canceled', + '3': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market === undefined) { + market = this.safeValue (this.marketsById, order['pair']); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'date'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString2 (order, 'oid', 'id'); + let trades = this.safeValue (order, 'trades', []); + trades = this.parseTrades (trades, market); + const side = this.safeString2 (order, 'my_side', 'type'); + let filled = undefined; + const numTrades = trades.length; + if (numTrades > 0) { + filled = 0.0; + for (let i = 0; i < numTrades; i++) { + filled = this.sum (filled, trades[i]['amount']); + } + } + let remaining = undefined; + if ((amount !== undefined) && (amount > 0) && (filled !== undefined)) { + remaining = Math.max (0, amount - filled); + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'amount': amount, + 'price': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostOrder (this.extend (request, params)); + if (!response['success']) { + throw new InvalidOrder (this.id + ' ' + this.json (response)); + } + const order = this.parseOrder (response, market); + amount = (order['amount'] > 0) ? order['amount'] : amount; + return this.extend (order, { + 'amount': amount, + }); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order': id, + }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const order = await this.privateGetOrderId (this.extend (request, params)); + return this.parseOrder (order); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const orders = await this.privateGetOrdersOwn (this.extend (request, params)); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': '1', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': '3', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbol !== undefined) { + const market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const trades = await this.privateGetExchangesOwn (this.extend (request, params)); + return this.parseTrades (trades, undefined, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.urlencode (this.keysort (this.omit (params, this.extractParams (path)))); + let url = this.urls['api'] + '/'; + if (path !== 'charts/{pair}/{type}/chart/') { + url += 'v1/'; + } + url += this.implodeParams (path, params); + headers = { 'Accept': 'application/json' }; + if (api === 'public') { + if (query.length) { + url += '?' + query; + } + } else { + this.checkRequiredCredentials (); + let payload = this.apiKey; + if (method === 'POST') { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + body = query; + payload += body; + } else if (query.length) { + url += '?' + query; + } + headers['X-KEY'] = this.apiKey; + headers['X-SIGN'] = this.hmac (this.encode (payload), this.encode (this.secret)); + headers['X-NONCE'] = this.nonce ().toString (); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"} + // + const error = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + if (error !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + } + if (code === 401 || code === 403) { + throw new AuthenticationError (feedback); + } else if (code === 429) { + throw new DDoSProtection (feedback); + } + if (code < 400) { + return; + } + throw new ExchangeError (feedback); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],50:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InsufficientFunds, InvalidOrder, AuthenticationError, PermissionDenied, InvalidNonce, OrderNotFound, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcbox extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcbox', + 'name': 'BtcBox', + 'countries': [ 'JP' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchTickers': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/31275803-4df755a8-aaa1-11e7-9abb-11ec2fad9f2d.jpg', + 'api': 'https://www.btcbox.co.jp/api', + 'www': 'https://www.btcbox.co.jp/', + 'doc': 'https://www.btcbox.co.jp/help/asm', + 'fees': 'https://support.btcbox.co.jp/hc/en-us/articles/360001235694-Fees-introduction', + }, + 'api': { + 'public': { + 'get': [ + 'depth', + 'orders', + 'ticker', + ], + }, + 'private': { + 'post': [ + 'balance', + 'trade_add', + 'trade_cancel', + 'trade_list', + 'trade_view', + 'wallet', + ], + }, + }, + 'markets': { + 'BTC/JPY': { 'id': 'btc', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy', 'taker': 0.05 / 100, 'maker': 0.05 / 100 }, + 'ETH/JPY': { 'id': 'eth', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, + 'LTC/JPY': { 'id': 'ltc', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, + 'BCH/JPY': { 'id': 'bch', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bch', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, + }, + 'exceptions': { + '104': AuthenticationError, + '105': PermissionDenied, + '106': InvalidNonce, + '107': InvalidOrder, // price should be an integer + '200': InsufficientFunds, + '201': InvalidOrder, // amount too small + '202': InvalidOrder, // price should be [0 : 1000000] + '203': OrderNotFound, + '401': OrderNotFound, // cancel canceled, closed or non-existent order + '402': DDoSProtection, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const free = currencyId + '_balance'; + if (free in response) { + const account = this.account (); + const used = currencyId + '_lock'; + account['free'] = this.safeFloat (response, free); + account['used'] = this.safeFloat (response, used); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + const numSymbols = this.symbols.length; + if (numSymbols > 1) { + request['coin'] = market['baseId']; + } + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + const numSymbols = this.symbols.length; + if (numSymbols > 1) { + request['coin'] = market['baseId']; + } + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const type = undefined; + const side = this.safeString (trade, 'type'); + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + const numSymbols = this.symbols.length; + if (numSymbols > 1) { + request['coin'] = market['baseId']; + } + const response = await this.publicGetOrders (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'amount': amount, + 'price': price, + 'type': side, + 'coin': market['baseId'], + }; + const response = await this.privatePostTradeAdd (this.extend (request, params)); + // + // { + // "result":true, + // "id":"11" + // } + // + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // a special case for btcbox – default symbol is BTC/JPY + if (symbol === undefined) { + symbol = 'BTC/JPY'; + } + const market = this.market (symbol); + const request = { + 'id': id, + 'coin': market['baseId'], + }; + const response = await this.privatePostTradeCancel (this.extend (request, params)); + // + // {"result":true, "id":"11"} + // + return this.parseOrder (response, market); + } + + parseOrderStatus (status) { + const statuses = { + // TODO: complete list + 'part': 'open', // partially or not at all executed + 'all': 'closed', // fully executed + 'cancelled': 'canceled', + 'closed': 'closed', // never encountered, seems to be bug in the doc + 'no': 'closed', // not clarified in the docs... + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // {"id":11,"datetime":"2014-10-21 10:47:20","type":"sell","price":42000,"amount_original":1.2,"amount_outstanding":1.2,"status":"closed","trades":[]} + // + const id = this.safeString (order, 'id'); + const datetimeString = this.safeString (order, 'datetime'); + let timestamp = undefined; + if (datetimeString !== undefined) { + timestamp = this.parse8601 (order['datetime'] + '+09:00'); // Tokyo time + } + const amount = this.safeFloat (order, 'amount_original'); + const remaining = this.safeFloat (order, 'amount_outstanding'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + const price = this.safeFloat (order, 'price'); + let cost = undefined; + if (price !== undefined) { + if (filled !== undefined) { + cost = filled * price; + } + } + // status is set by fetchOrder method only + let status = this.parseOrderStatus (this.safeString (order, 'status')); + // fetchOrders do not return status, use heuristic + if (status === undefined) { + if (remaining !== undefined && remaining === 0) { + status = 'closed'; + } + } + const trades = undefined; // todo: this.parseTrades (order['trades']); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (order, 'type'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'side': side, + 'type': undefined, + 'status': status, + 'symbol': symbol, + 'price': price, + 'cost': cost, + 'trades': trades, + 'fee': undefined, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // a special case for btcbox – default symbol is BTC/JPY + if (symbol === undefined) { + symbol = 'BTC/JPY'; + } + const market = this.market (symbol); + const request = this.extend ({ + 'id': id, + 'coin': market['baseId'], + }, params); + const response = await this.privatePostTradeView (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // a special case for btcbox – default symbol is BTC/JPY + if (symbol === undefined) { + symbol = 'BTC/JPY'; + } + const market = this.market (symbol); + const request = { + 'type': type, // 'open' or 'all' + 'coin': market['baseId'], + }; + const response = await this.privatePostTradeList (this.extend (request, params)); + const orders = this.parseOrders (response, market, since, limit); + // status (open/closed/canceled) is undefined + // btcbox does not return status, but we know it's 'open' as we queried for open orders + if (type === 'open') { + for (let i = 0; i < orders.length; i++) { + orders[i]['status'] = 'open'; + } + } + return orders; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('all', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('open', symbol, since, limit, params); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const query = this.extend ({ + 'key': this.apiKey, + 'nonce': nonce, + }, params); + const request = this.urlencode (query); + const secret = this.hash (this.encode (this.secret)); + query['signature'] = this.hmac (this.encode (request), this.encode (secret)); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // resort to defaultErrorHandler + } + // typical error response: {"result":false,"code":"401"} + if (httpCode >= 400) { + return; // resort to defaultErrorHandler + } + const result = this.safeValue (response, 'result'); + if (result === undefined || result === true) { + return; // either public API (no error codes expected) or success + } + const code = this.safeValue (response, 'code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); // unknown message + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response === 'string') { + // sometimes the exchange returns whitespace prepended to json + response = this.strip (response); + if (!this.isJsonEncodedObject (response)) { + throw new ExchangeError (this.id + ' ' + response); + } + response = JSON.parse (response); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],51:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class btcchina extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcchina', + 'name': 'BTCChina', + 'countries': [ 'CN' ], + 'rateLimit': 1500, + 'version': 'v1', + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766368-465b3286-5ed6-11e7-9a11-0f6467e1d82b.jpg', + 'api': { + 'plus': 'https://plus-api.btcchina.com/market', + 'public': 'https://data.btcchina.com/data', + 'private': 'https://api.btcchina.com/api_trade_v1.php', + }, + 'www': 'https://www.btcchina.com', + 'doc': 'https://www.btcchina.com/apidocs', + }, + 'api': { + 'plus': { + 'get': [ + 'orderbook', + 'ticker', + 'trade', + ], + }, + 'public': { + 'get': [ + 'historydata', + 'orderbook', + 'ticker', + 'trades', + ], + }, + 'private': { + 'post': [ + 'BuyIcebergOrder', + 'BuyOrder', + 'BuyOrder2', + 'BuyStopOrder', + 'CancelIcebergOrder', + 'CancelOrder', + 'CancelStopOrder', + 'GetAccountInfo', + 'getArchivedOrder', + 'getArchivedOrders', + 'GetDeposits', + 'GetIcebergOrder', + 'GetIcebergOrders', + 'GetMarketDepth', + 'GetMarketDepth2', + 'GetOrder', + 'GetOrders', + 'GetStopOrder', + 'GetStopOrders', + 'GetTransactions', + 'GetWithdrawal', + 'GetWithdrawals', + 'RequestWithdrawal', + 'SellIcebergOrder', + 'SellOrder', + 'SellOrder2', + 'SellStopOrder', + ], + }, + }, + 'markets': { + 'BTC/CNY': { 'id': 'btccny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'api': 'public', 'plus': false }, + 'LTC/CNY': { 'id': 'ltccny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY', 'api': 'public', 'plus': false }, + 'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'api': 'public', 'plus': false }, + 'BCH/CNY': { 'id': 'bcccny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY', 'api': 'plus', 'plus': true }, + 'ETH/CNY': { 'id': 'ethcny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY', 'api': 'plus', 'plus': true }, + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { + 'market': 'all', + }; + const markets = await this.publicGetTicker (this.extend (request, params)); + const result = []; + const keys = Object.keys (markets); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = markets[key]; + const parts = key.split ('_'); + const id = parts[1]; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const balances = this.safeValue (response, 'result'); + const result = { 'info': balances }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const account = this.account (); + const currencyId = currency['id']; + if (currencyId in balances['balance']) { + account['total'] = parseFloat (balances['balance'][currencyId]['amount']); + } + if (currencyId in balances['frozen']) { + account['used'] = parseFloat (balances['frozen'][currencyId]['amount']); + } + result[code] = account; + } + return this.parseBalance (result); + } + + createMarketRequest (market) { + const request = {}; + const field = (market['plus']) ? 'symbol' : 'market'; + request[field] = market['id']; + return request; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['api'] + 'GetOrderbook'; + const request = this.createMarketRequest (market); + const response = await this[method] (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'date'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market) { + const timestamp = this.safeTimestamp (ticker, 'date'); + const last = this.safeFloat (ticker, 'last'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'ask': this.safeFloat (ticker, 'sell'), + 'vwap': this.safeFloat (ticker, 'vwap'), + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTickerPlus (ticker, market) { + const timestamp = this.safeInteger (ticker, 'Timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'High'), + 'low': this.safeFloat (ticker, 'Low'), + 'bid': this.safeFloat (ticker, 'BidPrice'), + 'ask': this.safeFloat (ticker, 'AskPrice'), + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'Open'), + 'last': this.safeFloat (ticker, 'Last'), + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume24H'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['api'] + 'GetTicker'; + const request = this.createMarketRequest (market); + const response = await this[method] (this.extend (request, params)); + const ticker = this.safeValue (response, 'ticker'); + if (market['plus']) { + return this.parseTickerPlus (ticker, market); + } + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'date'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': undefined, + 'side': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + } + + parseTradePlus (trade, market) { + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + const side = this.safeStringLower (trade, 'side'); + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + } + + parseTradesPlus (trades, market = undefined) { + const result = []; + for (let i = 0; i < trades.length; i++) { + result.push (this.parseTradePlus (trades[i], market)); + } + return result; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = market['api'] + 'GetTrade'; + const request = this.createMarketRequest (market); + if (market['plus']) { + const now = this.milliseconds (); + request['start_time'] = now - 86400000; + request['end_time'] = now; + } else { + method += 's'; // trades vs trade + } + const response = await this[method] (this.extend (request, params)); + if (market['plus']) { + return this.parseTradesPlus (response['trades'], market); + } + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side) + 'Order2'; + const request = {}; + const id = market['id'].toUpperCase (); + if (type === 'market') { + request['params'] = [ undefined, amount, id ]; + } else { + request['params'] = [ price, amount, id ]; + } + const response = await this[method] (this.extend (request, params)); + const orderId = this.safeString (response, 'id'); + return { + 'info': response, + 'id': orderId, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = params['market']; // TODO fixme + const request = { + 'params': [ id, market ], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + nonce () { + return this.microseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'private') { + this.checkRequiredCredentials (); + let p = []; + if ('params' in params) { + p = params['params']; + } + const nonce = this.nonce (); + const request = { + 'method': path, + 'id': nonce, + 'params': p, + }; + p = p.join (','); + body = this.json (request); + const query = [ + 'tonce=' + nonce, + 'accesskey=' + this.apiKey, + 'requestmethod=' + method.toLowerCase (), + 'id=' + nonce, + 'method=' + path, + 'params=' + p, + ].join ('&'); + const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha1'); + const auth = this.encode (this.apiKey + ':' + signature); + headers = { + 'Authorization': 'Basic ' + this.stringToBase64 (auth), + 'Json-Rpc-Tonce': nonce, + }; + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7}],52:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, OrderNotFound, ArgumentsRequired, InvalidOrder, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcmarkets extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcmarkets', + 'name': 'BTC Markets', + 'countries': [ 'AU' ], // Australia + 'rateLimit': 1000, // market data cached for 1 second (trades cached for 2 seconds) + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchClosedOrders': 'emulated', + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'cancelOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/29142911-0e1acfc2-7d5c-11e7-98c4-07d9532b29d7.jpg', + 'api': { + 'public': 'https://api.btcmarkets.net', + 'private': 'https://api.btcmarkets.net', + 'web': 'https://btcmarkets.net/data', + }, + 'www': 'https://btcmarkets.net', + 'doc': 'https://github.com/BTCMarkets/API', + }, + 'api': { + 'public': { + 'get': [ + 'market/{id}/tick', + 'market/{id}/orderbook', + 'market/{id}/trades', + 'v2/market/{id}/tickByTime/{timeframe}', + 'v2/market/{id}/trades', + 'v2/market/active', + ], + }, + 'private': { + 'get': [ + 'account/balance', + 'account/{id}/tradingfee', + 'fundtransfer/history', + 'v2/order/open', + 'v2/order/open/{id}', + 'v2/order/history/{instrument}/{currency}/', + 'v2/order/trade/history/{id}', + 'v2/transaction/history/{currency}', + ], + 'post': [ + 'fundtransfer/withdrawCrypto', + 'fundtransfer/withdrawEFT', + 'order/create', + 'order/cancel', + 'order/history', + 'order/open', + 'order/trade/history', + 'order/createBatch', // they promise it's coming soon... + 'order/detail', + ], + }, + 'web': { + 'get': [ + 'market/BTCMarkets/{id}/tickByTime', + ], + }, + }, + 'timeframes': { + '1m': 'minute', + '1h': 'hour', + '1d': 'day', + }, + 'exceptions': { + '3': InvalidOrder, + '6': DDoSProtection, + }, + 'fees': { + 'percentage': true, + 'tierBased': true, + 'maker': -0.05 / 100, + 'taker': 0.20 / 100, + }, + 'options': { + 'fees': { + 'AUD': { + 'maker': 0.85 / 100, + 'taker': 0.85 / 100, + }, + }, + }, + }); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['since'] = since; + } + const response = await this.privateGetFundtransferHistory (this.extend (request, params)); + const transactions = response['fundTransfers']; + return this.parseTransactions (transactions, undefined, since, limit); + } + + parseTransactionStatus (status) { + // todo: find more statuses + const statuses = { + 'Complete': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (item, currency = undefined) { + // + // { + // status: 'Complete', + // fundTransferId: 1904311906, + // description: 'ETH withdraw from [me@email.com] to Address: 0xF123aa44FadEa913a7da99cc2eE202Db684Ce0e3 amount: 8.28965701 fee: 0.00000000', + // creationTime: 1529418358525, + // currency: 'ETH', + // amount: 828965701, + // fee: 0, + // transferType: 'WITHDRAW', + // errorMessage: null, + // lastUpdate: 1529418376754, + // cryptoPaymentDetail: { + // address: '0xF123aa44FadEa913a7da99cc2eE202Db684Ce0e3', + // txId: '0x8fe483b6f9523559b9ebffb29624f98e86227d2660d4a1fd4785d45e51c662c2' + // } + // } + // + // { + // status: 'Complete', + // fundTransferId: 494077500, + // description: 'BITCOIN Deposit, B 0.1000', + // creationTime: 1501077601015, + // currency: 'BTC', + // amount: 10000000, + // fee: 0, + // transferType: 'DEPOSIT', + // errorMessage: null, + // lastUpdate: 1501077601133, + // cryptoPaymentDetail: null + // } + // + // { + // "fee": 0, + // "amount": 56, + // "status": "Complete", + // "currency": "BCHABC", + // "lastUpdate": 1542339164044, + // "description": "BitcoinCashABC Deposit, P 0.00000056", + // "creationTime": 1542339164003, + // "errorMessage": null, + // "transferType": "DEPOSIT", + // "fundTransferId": 2527326972, + // "cryptoPaymentDetail": null + // } + // + const timestamp = this.safeInteger (item, 'creationTime'); + const lastUpdate = this.safeInteger (item, 'lastUpdate'); + const transferType = this.safeString (item, 'transferType'); + const cryptoPaymentDetail = this.safeValue (item, 'cryptoPaymentDetail', {}); + const address = this.safeString (cryptoPaymentDetail, 'address'); + const txid = this.safeString (cryptoPaymentDetail, 'txId'); + let type = undefined; + if (transferType === 'DEPOSIT') { + type = 'deposit'; + } else if (transferType === 'WITHDRAW') { + type = 'withdrawal'; + } else { + type = transferType; + } + const fee = this.safeFloat (item, 'fee'); + const status = this.parseTransactionStatus (this.safeString (item, 'status')); + const ccy = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (ccy); + // todo: this logic is duplicated below + let amount = this.safeFloat (item, 'amount'); + if (amount !== undefined) { + amount = amount * 1e-8; + } + return { + 'id': this.safeString (item, 'fundTransferId'), + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': lastUpdate, + 'fee': { + 'currency': code, + 'cost': fee, + }, + 'info': item, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetV2MarketActive (params); + const result = []; + const markets = this.safeValue (response, 'markets'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = this.safeString (market, 'instrument'); + const quoteId = this.safeString (market, 'currency'); + const id = baseId + '/' + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const fees = this.safeValue (this.safeValue (this.options, 'fees', {}), quote, this.fees); + let pricePrecision = 2; + let amountPrecision = 4; + const minAmount = 0.001; // where does it come from? + let minPrice = undefined; + if (quote === 'AUD') { + if ((base === 'XRP') || (base === 'OMG')) { + pricePrecision = 4; + } + amountPrecision = -Math.log10 (minAmount); + minPrice = Math.pow (10, -pricePrecision); + } + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const limits = { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'maker': fees['maker'], + 'taker': fees['taker'], + 'limits': limits, + 'precision': precision, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetAccountBalance (params); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const multiplier = 100000000; + let total = this.safeFloat (balance, 'balance'); + if (total !== undefined) { + total /= multiplier; + } + let used = this.safeFloat (balance, 'pendingFunds'); + if (used !== undefined) { + used /= multiplier; + } + const account = this.account (); + account['used'] = used; + account['total'] = total; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "timestamp":1572307200000, + // "open":1962218, + // "high":1974850, + // "low":1962208, + // "close":1974850, + // "volume":305211315, + // } + // + const multiplier = 100000000; // for price and volume + const keys = [ 'open', 'high', 'low', 'close', 'volume' ]; + const result = [ + this.safeInteger (ohlcv, 'timestamp'), + ]; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + let value = this.safeFloat (ohlcv, key); + if (value !== undefined) { + value = value / multiplier; + } + result.push (value); + } + return result; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.load_markets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + 'timeframe': this.timeframes[timeframe], + // set to true to see candles more recent than the timestamp in the + // since parameter, if a since parameter is used, default is false + 'indexForward': true, + // set to true to see the earliest candles first in the list of + // returned candles in chronological order, default is false + 'sortForward': true, + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default is 3000 + } + const response = await this.publicGetV2MarketIdTickByTimeTimeframe (this.extend (request, params)); + // + // { + // "success":true, + // "paging":{ + // "newer":"/v2/market/ETH/BTC/tickByTime/day?indexForward=true&since=1572307200000", + // "older":"/v2/market/ETH/BTC/tickByTime/day?since=1457827200000" + // }, + // "ticks":[ + // {"timestamp":1572307200000,"open":1962218,"high":1974850,"low":1962208,"close":1974850,"volume":305211315}, + // {"timestamp":1572220800000,"open":1924700,"high":1951276,"low":1909328,"close":1951276,"volume":1086067595}, + // {"timestamp":1572134400000,"open":1962155,"high":1962734,"low":1900905,"close":1930243,"volume":790141098}, + // ], + // } + // + const ticks = this.safeValue (response, 'ticks', []); + return this.parseOHLCVs (ticks, market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetMarketIdOrderbook (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'bestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume24h'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetMarketIdTick (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + // 'since': 59868345231, + 'id': market['id'], + }; + const response = await this.publicGetMarketIdTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const multiplier = 100000000; // for price and volume + const orderSide = (side === 'buy') ? 'Bid' : 'Ask'; + const request = this.ordered ({ + 'currency': market['quote'], + }); + request['currency'] = market['quote']; + request['instrument'] = market['base']; + request['price'] = parseInt (price * multiplier); + request['volume'] = parseInt (amount * multiplier); + request['orderSide'] = orderSide; + request['ordertype'] = this.capitalize (type); + request['clientRequestId'] = this.nonce ().toString (); + const response = await this.privatePostOrderCreate (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + for (let i = 0; i < ids.length; i++) { + ids[i] = parseInt (ids[i]); + } + const request = { + 'orderIds': ids, + }; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.cancelOrders ([ id ]); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let currency = undefined; + let cost = undefined; + if (market['quote'] === 'AUD') { + currency = market['quote']; + cost = parseFloat (this.costToPrecision (symbol, amount * price)); + } else { + currency = market['base']; + cost = parseFloat (this.amountToPrecision (symbol, amount)); + } + return { + 'type': takerOrMaker, + 'currency': currency, + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), + }; + } + + parseMyTrade (trade, market) { + const multiplier = 100000000; + const timestamp = this.safeInteger (trade, 'creationTime'); + let side = this.safeFloat (trade, 'side'); + side = (side === 'Bid') ? 'buy' : 'sell'; + // BTCMarkets always charge in AUD for AUD-related transactions. + let feeCurrencyCode = undefined; + let symbol = undefined; + if (market !== undefined) { + feeCurrencyCode = (market['quote'] === 'AUD') ? market['quote'] : market['base']; + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + let price = this.safeFloat (trade, 'price'); + if (price !== undefined) { + price /= multiplier; + } + let amount = this.safeFloat (trade, 'volume'); + if (amount !== undefined) { + amount /= multiplier; + } + let feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + feeCost /= multiplier; + } + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const orderId = this.safeString (trade, 'orderId'); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'order': orderId, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': { + 'currency': feeCurrencyCode, + 'cost': feeCost, + }, + }; + } + + parseMyTrades (trades, market = undefined, since = undefined, limit = undefined) { + const result = []; + for (let i = 0; i < trades.length; i++) { + const trade = this.parseMyTrade (trades[i], market); + result.push (trade); + } + return result; + } + + parseOrder (order, market = undefined) { + const multiplier = 100000000; + const side = (order['orderSide'] === 'Bid') ? 'buy' : 'sell'; + const type = (order['ordertype'] === 'Limit') ? 'limit' : 'market'; + const timestamp = this.safeInteger (order, 'creationTime'); + if (market === undefined) { + market = this.market (order['instrument'] + '/' + order['currency']); + } + let status = 'open'; + if (order['status'] === 'Failed' || order['status'] === 'Cancelled' || order['status'] === 'Partially Cancelled' || order['status'] === 'Error') { + status = 'canceled'; + } else if (order['status'] === 'Fully Matched' || order['status'] === 'Partially Matched') { + status = 'closed'; + } + const price = this.safeFloat (order, 'price') / multiplier; + const amount = this.safeFloat (order, 'volume') / multiplier; + const remaining = this.safeFloat (order, 'openVolume', 0.0) / multiplier; + const filled = amount - remaining; + const trades = this.parseMyTrades (order['trades'], market); + const numTrades = trades.length; + let cost = filled * price; + let average = undefined; + let lastTradeTimestamp = undefined; + if (numTrades > 0) { + cost = 0; + for (let i = 0; i < numTrades; i++) { + const trade = trades[i]; + cost = this.sum (cost, trade['cost']); + } + if (filled > 0) { + average = cost / filled; + } + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': market['symbol'], + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'average': average, + 'status': status, + 'trades': trades, + 'fee': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const ids = [ parseInt (id) ]; + const request = { + 'orderIds': ids, + }; + const response = await this.privatePostOrderDetail (this.extend (request, params)); + const numOrders = response['orders'].length; + if (numOrders < 1) { + throw new OrderNotFound (this.id + ' No matching order found: ' + id); + } + const order = response['orders'][0]; + return this.parseOrder (order); + } + + createPaginatedRequest (market, since = undefined, limit = undefined) { + limit = (limit === undefined) ? 100 : limit; + since = (since === undefined) ? 0 : since; + const request = this.ordered ({ + 'currency': market['quoteId'], + 'instrument': market['baseId'], + 'limit': limit, + 'since': since, + }); + return request; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ': fetchOrders requires a `symbol` argument.'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.createPaginatedRequest (market, since, limit); + const response = await this.privatePostOrderHistory (this.extend (request, params)); + return this.parseOrders (response['orders'], market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ': fetchOpenOrders requires a `symbol` argument.'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.createPaginatedRequest (market, since, limit); + const response = await this.privatePostOrderOpen (this.extend (request, params)); + return this.parseOrders (response['orders'], market); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ': fetchMyTrades requires a `symbol` argument.'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.createPaginatedRequest (market, since, limit); + const response = await this.privatePostOrderTradeHistory (this.extend (request, params)); + return this.parseMyTrades (response['trades'], market); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const uri = '/' + this.implodeParams (path, params); + let url = this.urls['api'][api] + uri; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = undefined; + headers = { + 'apikey': this.apiKey, + 'timestamp': nonce, + }; + if (method === 'POST') { + headers['Content-Type'] = 'application/json'; + auth = uri + "\n" + nonce + "\n"; // eslint-disable-line quotes + body = this.json (params); + auth += body; + } else { + const query = this.keysort (this.omit (params, this.extractParams (path))); + let queryString = ''; + if (Object.keys (query).length) { + queryString = this.urlencode (query); + url += '?' + queryString; + queryString += "\n"; // eslint-disable-line quotes + } + auth = uri + "\n" + queryString + nonce + "\n"; // eslint-disable-line quotes + } + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); + headers['signature'] = this.decode (signature); + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + if (!response['success']) { + const error = this.safeString (response, 'errorCode'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + throw new ExchangeError (feedback); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],53:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const coinegg = require ('./coinegg.js'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btctradeim extends coinegg { + describe () { + const result = this.deepExtend (super.describe (), { + 'id': 'btctradeim', + 'name': 'BtcTrade.im', + 'countries': [ 'HK' ], + 'urls': { + 'referral': 'https://m.baobi.com/invite?inv=1765b2', + 'logo': 'https://user-images.githubusercontent.com/1294454/36770531-c2142444-1c5b-11e8-91e2-a4d90dc85fe8.jpg', + 'api': { + 'web': 'https://api.btctrade.im/coin', + 'rest': 'https://api.btctrade.im/api/v1', + }, + 'www': 'https://www.btctrade.im', + 'doc': 'https://www.btctrade.im/help.api.html', + 'fees': 'https://www.btctrade.im/spend.price.html', + }, + 'status': { + 'status': 'error', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.001, + }, + }, + }, + // see the fix below + // 'options': { + // 'quoteIds': [ 'btc', 'eth', 'usc' ], + // }, + }); + // a fix for PHP array_merge not overwriting "lists" (integer-indexed arrays) + // https://github.com/ccxt/ccxt/issues/3343 + result['options']['quoteIds'] = [ 'btc', 'eth', 'usc' ]; + return result; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (api === 'web') { + return response; + } + const data = this.safeValue (response, 'data'); + if (data) { + const code = this.safeString (response, 'code'); + if (code !== '0') { + const message = this.safeString (response, 'msg', 'Error'); + throw new ExchangeError (message); + } + return data; + } + return response; + } +}; + +},{"./base/errors":9,"./coinegg.js":66}],54:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btctradeua extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btctradeua', + 'name': 'BTC Trade UA', + 'countries': [ 'UA' ], // Ukraine, + 'rateLimit': 3000, + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchOpenOrders': true, + }, + 'urls': { + 'referral': 'https://btc-trade.com.ua/registration/22689', + 'logo': 'https://user-images.githubusercontent.com/1294454/27941483-79fc7350-62d9-11e7-9f61-ac47f28fcd96.jpg', + 'api': 'https://btc-trade.com.ua/api', + 'www': 'https://btc-trade.com.ua', + 'doc': 'https://docs.google.com/document/d/1ocYA0yMy_RXd561sfG3qEPZ80kyll36HUxvCRe5GbhE/edit', + }, + 'api': { + 'public': { + 'get': [ + 'deals/{symbol}', + 'trades/sell/{symbol}', + 'trades/buy/{symbol}', + 'japan_stat/high/{symbol}', + ], + }, + 'private': { + 'post': [ + 'auth', + 'ask/{symbol}', + 'balance', + 'bid/{symbol}', + 'buy/{symbol}', + 'my_orders/{symbol}', + 'order/status/{id}', + 'remove/order/{id}', + 'sell/{symbol}', + ], + }, + }, + 'markets': { + 'BCH/UAH': { 'id': 'bch_uah', 'symbol': 'BCH/UAH', 'base': 'BCH', 'quote': 'UAH', 'baseId': 'bch', 'quoteId': 'uah' }, + 'BTC/UAH': { 'id': 'btc_uah', 'symbol': 'BTC/UAH', 'base': 'BTC', 'quote': 'UAH', 'baseId': 'btc', 'quoteId': 'uah', 'precision': { 'price': 1 }, 'limits': { 'amount': { 'min': 0.0000000001 }}}, + 'DASH/BTC': { 'id': 'dash_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'dash', 'quoteId': 'btc' }, + 'DASH/UAH': { 'id': 'dash_uah', 'symbol': 'DASH/UAH', 'base': 'DASH', 'quote': 'UAH', 'baseId': 'dash', 'quoteId': 'uah' }, + 'DOGE/BTC': { 'id': 'doge_btc', 'symbol': 'DOGE/BTC', 'base': 'DOGE', 'quote': 'BTC', 'baseId': 'doge', 'quoteId': 'btc' }, + 'DOGE/UAH': { 'id': 'doge_uah', 'symbol': 'DOGE/UAH', 'base': 'DOGE', 'quote': 'UAH', 'baseId': 'doge', 'quoteId': 'uah' }, + 'ETH/UAH': { 'id': 'eth_uah', 'symbol': 'ETH/UAH', 'base': 'ETH', 'quote': 'UAH', 'baseId': 'eth', 'quoteId': 'uah' }, + 'ITI/UAH': { 'id': 'iti_uah', 'symbol': 'ITI/UAH', 'base': 'ITI', 'quote': 'UAH', 'baseId': 'iti', 'quoteId': 'uah' }, + 'KRB/UAH': { 'id': 'krb_uah', 'symbol': 'KRB/UAH', 'base': 'KRB', 'quote': 'UAH', 'baseId': 'krb', 'quoteId': 'uah' }, + 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, + 'LTC/UAH': { 'id': 'ltc_uah', 'symbol': 'LTC/UAH', 'base': 'LTC', 'quote': 'UAH', 'baseId': 'ltc', 'quoteId': 'uah' }, + 'NVC/BTC': { 'id': 'nvc_btc', 'symbol': 'NVC/BTC', 'base': 'NVC', 'quote': 'BTC', 'baseId': 'nvc', 'quoteId': 'btc' }, + 'NVC/UAH': { 'id': 'nvc_uah', 'symbol': 'NVC/UAH', 'base': 'NVC', 'quote': 'UAH', 'baseId': 'nvc', 'quoteId': 'uah' }, + 'PPC/BTC': { 'id': 'ppc_btc', 'symbol': 'PPC/BTC', 'base': 'PPC', 'quote': 'BTC', 'baseId': 'ppc', 'quoteId': 'btc' }, + 'SIB/UAH': { 'id': 'sib_uah', 'symbol': 'SIB/UAH', 'base': 'SIB', 'quote': 'UAH', 'baseId': 'sib', 'quoteId': 'uah' }, + 'XMR/UAH': { 'id': 'xmr_uah', 'symbol': 'XMR/UAH', 'base': 'XMR', 'quote': 'UAH', 'baseId': 'xmr', 'quoteId': 'uah' }, + 'ZEC/UAH': { 'id': 'zec_uah', 'symbol': 'ZEC/UAH', 'base': 'ZEC', 'quote': 'UAH', 'baseId': 'zec', 'quoteId': 'uah' }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.0006, + 'LTC': 0.01, + 'NVC': 0.01, + 'DOGE': 10, + }, + }, + }, + }); + } + + async signIn (params = {}) { + return await this.privatePostAuth (params); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'accounts'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const bids = await this.publicGetTradesBuySymbol (this.extend (request, params)); + const asks = await this.publicGetTradesSellSymbol (this.extend (request, params)); + const orderbook = { + 'bids': [], + 'asks': [], + }; + if (bids) { + if ('list' in bids) { + orderbook['bids'] = bids['list']; + } + } + if (asks) { + if ('list' in asks) { + orderbook['asks'] = asks['list']; + } + } + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'currency_trade'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetJapanStatHighSymbol (this.extend (request, params)); + const ticker = this.safeValue (response, 'trades'); + const timestamp = this.milliseconds (); + const result = { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': undefined, + 'last': undefined, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': undefined, + 'info': ticker, + }; + const tickerLength = ticker.length; + if (tickerLength > 0) { + const start = Math.max (tickerLength - 48, 0); + for (let i = start; i < ticker.length; i++) { + const candle = ticker[i]; + if (result['open'] === undefined) { + result['open'] = candle[1]; + } + if ((result['high'] === undefined) || (result['high'] < candle[2])) { + result['high'] = candle[2]; + } + if ((result['low'] === undefined) || (result['low'] > candle[3])) { + result['low'] = candle[3]; + } + if (result['baseVolume'] === undefined) { + result['baseVolume'] = -candle[5]; + } else { + result['baseVolume'] -= candle[5]; + } + } + const last = tickerLength - 1; + result['last'] = ticker[last][4]; + result['close'] = result['last']; + result['baseVolume'] = -1 * result['baseVolume']; + } + return result; + } + + convertCyrillicMonthNameToString (cyrillic) { + const months = { + '�нвар�': '01', + 'феврал�': '02', + 'марта': '03', + 'апрел�': '04', + 'ма�': '05', + 'июн�': '06', + 'июл�': '07', + 'авгу�та': '08', + '�ент�бр�': '09', + 'окт�бр�': '10', + 'но�бр�': '11', + 'декабр�': '12', + }; + return this.safeString (months, cyrillic); + } + + parseCyrillicDatetime (cyrillic) { + const parts = cyrillic.split (' '); + let day = parts[0]; + const month = this.convertCyrillicMonthNameToString (parts[1]); + if (!month) { + throw new ExchangeError (this.id + ' parseTrade() undefined month name: ' + cyrillic); + } + const year = parts[2]; + let hms = parts[4]; + const hmsLength = hms.length; + if (hmsLength === 7) { + hms = '0' + hms; + } + if (day.length === 1) { + day = '0' + day; + } + const ymd = [ year, month, day ].join ('-'); + const ymdhms = ymd + 'T' + hms; + const timestamp = this.parse8601 (ymdhms); + // server reports local time, adjust to UTC + let md = [ month, day ].join (''); + md = parseInt (md); + // a special case for DST + // subtract 2 hours during winter + if (md < 325 || md > 1028) { + return timestamp - 7200000; + } + // subtract 3 hours during summer + return timestamp - 10800000; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parseCyrillicDatetime (this.safeString (trade, 'pub_date')); + const id = this.safeString (trade, 'id'); + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amnt_trade'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetDealsSymbol (this.extend (request, params)); + // they report each trade twice (once for both of the two sides of the fill) + // deduplicate trades for that reason + const trades = []; + for (let i = 0; i < response.length; i++) { + if (response[i]['id'] % 2) { + trades.push (response[i]); + } + } + return this.parseTrades (trades, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side) + 'Id'; + const request = { + 'count': amount, + 'currency1': market['quoteId'], + 'currency': market['baseId'], + 'price': price, + }; + return this[method] (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostRemoveOrderId (this.extend (request, params)); + } + + parseOrder (order, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, // until they fix their timestamp + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': 'open', + 'symbol': symbol, + 'type': undefined, + 'side': this.safeString (order, 'type'), + 'price': this.safeFloat (order, 'price'), + 'amount': this.safeFloat (order, 'amnt_trade'), + 'filled': 0, + 'remaining': this.safeFloat (order, 'amnt_trade'), + 'trades': undefined, + 'info': order, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.privatePostMyOrdersSymbol (this.extend (request, params)); + const orders = this.safeValue (response, 'your_open_orders'); + return this.parseOrders (orders, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += this.implodeParams (path, query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'out_order_id': nonce, + 'nonce': nonce, + }, query)); + const auth = body + this.secret; + headers = { + 'public-key': this.apiKey, + 'api-sign': this.hash (this.encode (auth), 'sha256'), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],55:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcturk extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcturk', + 'name': 'BTCTurk', + 'countries': [ 'TR' ], // Turkey + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + }, + 'timeframes': { + '1d': '1d', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27992709-18e15646-64a3-11e7-9fa2-b0950ec7712f.jpg', + 'api': 'https://www.btcturk.com/api', + 'www': 'https://www.btcturk.com', + 'doc': 'https://github.com/BTCTrader/broker-api-docs', + }, + 'api': { + 'public': { + 'get': [ + 'ohlcdata', // ?last=COUNT + 'orderbook', + 'ticker', + 'trades', // ?last=COUNT (max 50) + ], + }, + 'private': { + 'get': [ + 'balance', + 'openOrders', + 'userTransactions', // ?offset=0&limit=25&sort=asc + ], + 'post': [ + 'exchange', + 'cancelOrder', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.002 * 1.18, + 'taker': 0.003 * 1.18, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTicker (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'pair'); + let baseId = id.slice (0, 3); + let quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + baseId = baseId.toLowerCase (); + quoteId = quoteId.toLowerCase (); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + const active = true; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const result = { 'info': response }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currencies[code]; + const free = currency['id'] + '_available'; + const total = currency['id'] + '_balance'; + const used = currency['id'] + '_reserved'; + if (free in response) { + const account = this.account (); + account['free'] = this.safeFloat (response, free); + account['total'] = this.safeFloat (response, total); + account['used'] = this.safeFloat (response, used); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pairSymbol': market['id'], + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'average'), + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTicker (params); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const marketId = this.safeString (ticker, 'pair'); + let symbol = marketId; + let market = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[symbol]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tickers = await this.fetchTickers (params); + return this.safeValue2 (tickers, market['id'], symbol); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // let maxCount = 50; + const request = { + 'pairSymbol': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (this.safeString (ohlcv, 'Time')); + return [ + timestamp, + this.safeFloat (ohlcv, 'Open'), + this.safeFloat (ohlcv, 'High'), + this.safeFloat (ohlcv, 'Low'), + this.safeFloat (ohlcv, 'Close'), + this.safeFloat (ohlcv, 'Volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + if (limit !== undefined) { + request['last'] = limit; + } + const response = await this.publicGetOhlcdata (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'PairSymbol': this.marketId (symbol), + 'OrderType': (side === 'buy') ? 0 : 1, + 'OrderMethod': (type === 'market') ? 1 : 0, + }; + if (type === 'market') { + if (!('Total' in params)) { + throw new ExchangeError (this.id + ' createOrder requires the "Total" extra parameter for market orders (amount and price are both ignored)'); + } + } else { + request['Price'] = price; + request['Amount'] = amount; + } + const response = await this.privatePostExchange (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (this.id === 'btctrader') { + throw new ExchangeError (this.id + ' is an abstract base API for BTCExchange, BTCTurk'); + } + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + body = this.urlencode (params); + const secret = this.base64ToBinary (this.secret); + const auth = this.apiKey + nonce; + headers = { + 'X-PCK': this.apiKey, + 'X-Stamp': nonce, + 'X-Signature': this.stringToBase64 (this.hmac (this.encode (auth), secret, 'sha256', 'binary')), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],56:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AddressPending, AuthenticationError, ExchangeError, NotSupported, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class buda extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'buda', + 'name': 'Buda', + 'countries': [ 'AR', 'CL', 'CO', 'PE' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': false, + 'createDepositAddress': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': true, + 'fetchMyTrades': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchTrades': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/47380619-8a029200-d706-11e8-91e0-8a391fe48de3.jpg', + 'api': 'https://www.buda.com/api', + 'www': 'https://www.buda.com', + 'doc': 'https://api.buda.com', + 'fees': 'https://www.buda.com/comisiones', + }, + 'status': { + 'status': 'error', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'api': { + 'public': { + 'get': [ + 'pairs', + 'markets', + 'currencies', + 'markets/{market}', + 'markets/{market}/ticker', + 'markets/{market}/volume', + 'markets/{market}/order_book', + 'markets/{market}/trades', + 'currencies/{currency}/fees/deposit', + 'currencies/{currency}/fees/withdrawal', + 'tv/history', + ], + 'post': [ + 'markets/{market}/quotations', + ], + }, + 'private': { + 'get': [ + 'balances', + 'balances/{currency}', + 'currencies/{currency}/balances', + 'orders', + 'orders/{id}', + 'markets/{market}/orders', + 'deposits', + 'currencies/{currency}/deposits', + 'withdrawals', + 'currencies/{currency}/withdrawals', + 'currencies/{currency}/receive_addresses', + 'currencies/{currency}/receive_addresses/{id}', + ], + 'post': [ + 'markets/{market}/orders', + 'currencies/{currency}/deposits', + 'currencies/{currency}/withdrawals', + 'currencies/{currency}/simulated_withdrawals', + 'currencies/{currency}/receive_addresses', + ], + 'put': [ + 'orders/{id}', + ], + }, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '30m': '30', + '1h': '60', + '2h': '120', + '1d': 'D', + '1w': 'W', + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.008, // 0.8% + 'maker': 0.004, // 0.4% + 'tiers': { + 'taker': [ + [0, 0.008], // 0.8% + [2000, 0.007], // 0.7% + [20000, 0.006], // 0.6% + [100000, 0.005], // 0.5% + [500000, 0.004], // 0.4% + [2500000, 0.003], // 0.3% + [12500000, 0.002], // 0.2% + ], + 'maker': [ + [0, 0.004], // 0.4% + [2000, 0.0035], // 0.35% + [20000, 0.003], // 0.3% + [100000, 0.0025], // 0.25% + [500000, 0.002], // 0.2% + [2500000, 0.0015], // 0.15% + [12500000, 0.001], // 0.1% + ], + }, + }, + }, + 'exceptions': { + 'not_authorized': AuthenticationError, // { message: 'Invalid credentials', code: 'not_authorized' } + 'forbidden': PermissionDenied, // { message: 'You dont have access to this resource', code: 'forbidden' } + 'invalid_record': ExchangeError, // { message: 'Validation Failed', code: 'invalid_record', errors: [] } + 'not_found': ExchangeError, // { message: 'Not found', code: 'not_found' } + 'parameter_missing': ExchangeError, // { message: 'Parameter missing', code: 'parameter_missing' } + 'bad_parameter': ExchangeError, // { message: 'Bad Parameter format', code: 'bad_parameter' } + }, + }); + } + + async fetchCurrencyInfo (currency, currencies = undefined) { + if (!currencies) { + const response = await this.publicGetCurrencies (); + currencies = this.safeValue (response, 'currencies'); + } + for (let i = 0; i < currencies.length; i++) { + const currencyInfo = currencies[i]; + if (currencyInfo['id'] === currency) { + return currencyInfo; + } + } + return undefined; + } + + async fetchMarkets (params = {}) { + const marketsResponse = await this.publicGetMarkets (params); + const markets = this.safeValue (marketsResponse, 'markets'); + const currenciesResponse = await this.publicGetCurrencies (); + const currencies = this.safeValue (currenciesResponse, 'currencies'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const baseInfo = await this.fetchCurrencyInfo (baseId, currencies); + const quoteInfo = await this.fetchCurrencyInfo (quoteId, currencies); + const symbol = base + '/' + quote; + const precision = { + 'amount': baseInfo['input_decimals'], + 'price': quoteInfo['input_decimals'], + }; + const limits = { + 'amount': { + 'min': parseFloat (market['minimum_order_amount'][0]), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }; + limits['cost'] = { + 'min': limits['amount']['min'] * limits['price']['min'], + 'max': undefined, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (); + const currencies = response['currencies']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + if (!currency['managed']) { + continue; + } + const id = this.safeString (currency, 'id'); + const code = this.safeCurrencyCode (id); + const precision = this.safeFloat (currency, 'input_decimals'); + const minimum = Math.pow (10, -precision); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': undefined, + 'active': true, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minimum, + 'max': undefined, + }, + 'price': { + 'min': minimum, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'deposit': { + 'min': parseFloat (currency['deposit_minimum'][0]), + 'max': undefined, + }, + 'withdraw': { + 'min': parseFloat (currency['withdrawal_minimum'][0]), + }, + }, + }; + } + return result; + } + + async fetchFundingFees (codes = undefined, params = {}) { + // by default it will try load withdrawal fees of all currencies (with separate requests) + // however if you define codes = [ 'ETH', 'BTC' ] in args it will only load those + await this.loadMarkets (); + const withdrawFees = {}; + const depositFees = {}; + const info = {}; + if (codes === undefined) { + codes = Object.keys (this.currencies); + } + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const request = { 'currency': currency['id'] }; + const withdrawResponse = await this.publicGetCurrenciesCurrencyFeesWithdrawal (request); + const depositResponse = await this.publicGetCurrenciesCurrencyFeesDeposit (request); + withdrawFees[code] = this.parseFundingFee (withdrawResponse['fee']); + depositFees[code] = this.parseFundingFee (depositResponse['fee']); + info[code] = { + 'withdraw': withdrawResponse, + 'deposit': depositResponse, + }; + } + return { + 'withdraw': withdrawFees, + 'deposit': depositFees, + 'info': info, + }; + } + + parseFundingFee (fee, type = undefined) { + if (type === undefined) { + type = fee['name']; + } + if (type === 'withdrawal') { + type = 'withdraw'; + } + return { + 'type': type, + 'currency': fee['base'][1], + 'rate': fee['percent'], + 'cost': parseFloat (fee['base'][0]), + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketsMarketTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'ticker'); + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = parseFloat (ticker['last_price'][0]); + const percentage = parseFloat (ticker['price_variation_24h']); + const open = parseFloat (this.priceToPrecision (symbol, last / (percentage + 1))); + const change = last - open; + const average = this.sum (last, open) / 2; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': parseFloat (ticker['max_bid'][0]), + 'bidVolume': undefined, + 'ask': parseFloat (ticker['min_ask'][0]), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': open, + 'change': change, + 'percentage': percentage * 100, + 'average': average, + 'baseVolume': parseFloat (ticker['volume'][0]), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + // the since argument works backwards – returns trades up to the specified timestamp + // therefore not implemented here + // the method is still available for users to be able to traverse backwards in time + // by using the timestamp from the first received trade upon each iteration + if (limit !== undefined) { + request['limit'] = limit; // 50 max + } + const response = await this.publicGetMarketsMarketTrades (this.extend (request, params)); + // + // { trades: { market_id: "ETH-BTC", + // timestamp: null, + // last_timestamp: "1536901277302", + // entries: [ [ "1540077456791", "0.0063767", "0.03", "sell", 479842 ], + // [ "1539916642772", "0.01888263", "0.03019563", "sell", 479438 ], + // [ "1539834081787", "0.023718648", "0.031001", "sell", 479069 ], + // ... ] + // + return this.parseTrades (response['trades']['entries'], market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // [ "1540077456791", "0.0063767", "0.03", "sell", 479842 ] + // + let timestamp = undefined; + let side = undefined; + const type = undefined; + let price = undefined; + let amount = undefined; + let id = undefined; + const order = undefined; + const fee = undefined; + let symbol = undefined; + let cost = undefined; + if (market) { + symbol = market['symbol']; + } + if (Array.isArray (trade)) { + timestamp = parseInt (trade[0]); + price = parseFloat (trade[1]); + amount = parseFloat (trade[2]); + cost = price * amount; + side = trade[3]; + id = trade[4].toString (); + } + return { + 'id': id, + 'order': order, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketsMarketOrderBook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'order_book'); + return this.parseOrderBook (orderbook); + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (since === undefined) { + since = this.milliseconds () - 86400000; + } + const request = { + 'symbol': market['id'], + 'resolution': this.timeframes[timeframe], + 'from': since / 1000, + 'to': this.seconds (), + }; + const response = await this.publicGetTvHistory (this.extend (request, params)); + return this.parseTradingViewOHLCV (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalances (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'balances'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'id'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = parseFloat (balance['available_amount'][0]); + account['total'] = parseFloat (balance['amount'][0]); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': parseInt (id), + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + const order = this.safeValue (response, 'order'); + return this.parseOrder (order); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'market': market['id'], + 'per': limit, + }; + const response = await this.privateGetMarketsMarketOrders (this.extend (request, params)); + const orders = this.safeValue (response, 'orders'); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'pending', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'traded', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + side = (side === 'buy') ? 'Bid' : 'Ask'; + const request = { + 'market': this.marketId (symbol), + 'price_type': type, + 'type': side, + 'amount': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['limit'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostMarketsMarketOrders (this.extend (request, params)); + const order = this.safeValue (response, 'order'); + return this.parseOrder (order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': parseInt (id), + 'state': 'canceling', + }; + const response = await this.privatePutOrdersId (this.extend (request, params)); + const order = this.safeValue (response, 'order'); + return this.parseOrder (order); + } + + parseOrderStatus (status) { + const statuses = { + 'traded': 'closed', + 'received': 'open', + 'canceling': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + let symbol = undefined; + if (market === undefined) { + const marketId = order['market_id']; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const type = this.safeString (order, 'price_type'); + const side = this.safeStringLower (order, 'type'); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const amount = parseFloat (order['original_amount'][0]); + const remaining = parseFloat (order['amount'][0]); + const filled = parseFloat (order['traded_amount'][0]); + const cost = parseFloat (order['total_exchanged'][0]); + let price = this.safeFloat (order, 'limit'); + if (price !== undefined) { + price = parseFloat (price[0]); + } + if (cost > 0 && filled > 0) { + price = this.priceToPrecision (symbol, cost / filled); + } + const fee = { + 'cost': parseFloat (order['paid_fee'][0]), + 'currency': order['paid_fee'][1], + }; + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + isFiat (code) { + const fiats = { + 'ARS': true, + 'CLP': true, + 'COP': true, + 'PEN': true, + }; + return this.safeValue (fiats, code, false); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + if (this.isFiat (code)) { + throw new NotSupported (this.id + ' fetchDepositAddress() for fiat ' + code + ' is not supported'); + } + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetCurrenciesCurrencyReceiveAddresses (this.extend (request, params)); + const receiveAddresses = this.safeValue (response, 'receive_addresses'); + const addressPool = []; + for (let i = 1; i < receiveAddresses.length; i++) { + const receiveAddress = receiveAddresses[i]; + if (receiveAddress['ready']) { + const address = receiveAddress['address']; + this.checkAddress (address); + addressPool.push (address); + } + } + const addressPoolLength = addressPool.length; + if (addressPoolLength < 1) { + throw new AddressPending (this.id + ': there are no addresses ready for receiving ' + code + ', retry again later)'); + } + const address = addressPool[0]; + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': receiveAddresses, + }; + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + if (this.isFiat (code)) { + throw new NotSupported (this.id + ': fiat fetchDepositAddress() for ' + code + ' is not supported'); + } + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostCurrenciesCurrencyReceiveAddresses (this.extend (request, params)); + const address = this.safeString (response['receive_address'], 'address'); // the creation is async and returns a null address, returns only the id + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'rejected': 'failed', + 'confirmed': 'ok', + 'anulled': 'canceled', + 'retained': 'canceled', + 'pending_confirmation': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + const id = this.safeString (transaction, 'id'); + const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const amount = parseFloat (transaction['amount'][0]); + const fee = parseFloat (transaction['fee'][0]); + const feeCurrency = transaction['fee'][1]; + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const type = ('deposit_data' in transaction) ? 'deposit' : 'withdrawal'; + const data = this.safeValue (transaction, type + '_data', {}); + const address = this.safeValue (data, 'target_address'); + const txid = this.safeString (data, 'tx_hash'); + const updated = this.parse8601 (this.safeString (data, 'updated_at')); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': { + 'cost': fee, + 'rate': feeCurrency, + }, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ': fetchDeposits() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'per': limit, + }; + const response = await this.privateGetCurrenciesCurrencyDeposits (this.extend (request, params)); + const deposits = this.safeValue (response, 'deposits'); + return this.parseTransactions (deposits, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ': fetchDeposits() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'per': limit, + }; + const response = await this.privateGetCurrenciesCurrencyWithdrawals (this.extend (request, params)); + const withdrawals = this.safeValue (response, 'withdrawals'); + return this.parseTransactions (withdrawals, currency, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'withdrawal_data': { + 'target_address': address, + }, + }; + const response = await this.privatePostCurrenciesCurrencyWithdrawals (this.extend (request, params)); + const withdrawal = this.safeValue (response, 'withdrawal'); + return this.parseTransaction (withdrawal); + } + + nonce () { + return this.microseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (Object.keys (query).length) { + if (method === 'GET') { + request += '?' + this.urlencode (query); + } else { + body = this.json (query); + } + } + const url = this.urls['api'] + '/' + this.version + '/' + request; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const components = [ method, '/api/' + this.version + '/' + request ]; + if (body) { + const base64Body = this.stringToBase64 (this.encode (body)); + components.push (this.decode (base64Body)); + } + components.push (nonce); + const message = components.join (' '); + const signature = this.hmac (this.encode (message), this.encode (this.secret), 'sha384'); + headers = { + 'X-SBTC-APIKEY': this.apiKey, + 'X-SBTC-SIGNATURE': signature, + 'X-SBTC-NONCE': nonce, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if (code >= 400) { + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'message', body); + const feedback = this.id + ' ' + message; + if (errorCode !== undefined) { + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],57:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, AuthenticationError, ArgumentsRequired, ExchangeNotAvailable } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bw extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bw', + 'name': 'BW', + 'countries': [ 'CN' ], + 'rateLimit': 1500, + 'version': 'v1', + 'has': { + 'cancelAllOrders': false, + 'cancelOrder': true, + 'cancelOrders': false, + 'CORS': false, + 'createDepositAddress': false, + 'createLimitOrder': true, + 'createMarketOrder': false, + 'createOrder': true, + 'deposit': false, + 'editOrder': false, + 'fetchBalance': true, + 'fetchBidsAsks': false, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': false, + 'fetchL2OrderBook': false, + 'fetchLedger': false, + 'fetchMarkets': true, + 'fetchMyTrades': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBook': true, + 'fetchOrderBooks': false, + 'fetchOrders': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTrades': true, + 'fetchTradingFee': false, + 'fetchTradingFees': false, + 'fetchTradingLimits': false, + 'fetchTransactions': false, + 'fetchWithdrawals': true, + 'privateAPI': false, + 'publicAPI': false, + 'withdraw': false, + }, + 'timeframes': { + '1m': '1M', + '5m': '5M', + '15m': '15M', + '30m': '30M', + '1h': '1H', + '1w': '1W', + }, + 'hostname': 'bw.com', // set to 'bw.io' for China mainland + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/69436317-31128c80-0d52-11ea-91d1-eb7bb5818812.jpg', + 'api': 'https://www.{hostname}', + 'www': 'https://www.bw.com', + 'doc': 'https://github.com/bw-exchange/api_docs_en/wiki', + 'fees': 'https://www.bw.com/feesRate', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.2 / 100, + 'maker': 0.2 / 100, + 'tiers': { + 'taker': [ + [ 0, 0.2 / 100 ], + ], + 'maker': [ + [ 0, 0.2 / 100 ], + ], + }, + }, + 'funding': { + }, + }, + 'exceptions': { + 'exact': { + '999': AuthenticationError, + '1000': ExchangeNotAvailable, // {"datas":null,"resMsg":{"message":"getKlines error:data not exitsts\uff0cplease wait ,dataType=4002_KLINE_1M","method":null,"code":"1000"}} + '5017': BadSymbol, // {"datas":null,"resMsg":{"message":"market not exist","method":null,"code":"5017"}} + }, + }, + 'api': { + 'public': { + 'get': [ + 'api/data/v1/klines', + 'api/data/v1/ticker', + 'api/data/v1/tickers', + 'api/data/v1/trades', + 'api/data/v1/entrusts', + 'exchange/config/controller/website/marketcontroller/getByWebId', + 'exchange/config/controller/website/currencycontroller/getCurrencyList', + ], + }, + 'private': { + 'get': [ + 'exchange/entrust/controller/website/EntrustController/getEntrustById', + 'exchange/entrust/controller/website/EntrustController/getUserEntrustRecordFromCacheWithPage', + 'exchange/entrust/controller/website/EntrustController/getUserEntrustList', + 'exchange/fund/controller/website/fundwebsitecontroller/getwithdrawaddress', + 'exchange/fund/controller/website/fundwebsitecontroller/getpayoutcoinrecord', + 'exchange/entrust/controller/website/EntrustController/getUserEntrustList', + // the docs say that the following URLs are HTTP POST + // in the docs header and HTTP GET in the docs body + // the docs contradict themselves, a typo most likely + // the actual HTTP method is POST for this endpoint + // 'exchange/fund/controller/website/fundcontroller/getPayinAddress', + // 'exchange/fund/controller/website/fundcontroller/getPayinCoinRecord', + ], + 'post': [ + 'exchange/fund/controller/website/fundcontroller/getPayinAddress', // see the comment above + 'exchange/fund/controller/website/fundcontroller/getPayinCoinRecord', // see the comment above + 'exchange/fund/controller/website/fundcontroller/findbypage', + 'exchange/entrust/controller/website/EntrustController/addEntrust', + 'exchange/entrust/controller/website/EntrustController/cancelEntrust', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeConfigControllerWebsiteMarketcontrollerGetByWebId (params); + // + // { + // "datas": [ + // { + // "orderNum":null, + // "leverEnable":true, + // "leverMultiple":10, + // "marketId":"291", + // "webId":"102", + // "serverId":"entrust_bw_23", + // "name":"eos_usdt", + // "leverType":"2", + // "buyerCurrencyId":"11", + // "sellerCurrencyId":"7", + // "amountDecimal":4, + // "priceDecimal":3, + // "minAmount":"0.0100000000", + // "state":1, + // "openTime":1572537600000, + // "defaultFee":"0.00200000", + // "createUid":null, + // "createTime":0, + // "modifyUid":null, + // "modifyTime":1574160113735, + // "combineMarketId":"", + // "isCombine":0, + // "isMining":0 + // } + // ], + // "resMsg": { "message":"success !", "method":null, "code":"1" } + // } + // + const markets = this.safeValue (response, 'datas', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'marketId'); + const numericId = parseInt (id); + const name = this.safeStringUpper (market, 'name'); + let [ base, quote ] = name.split ('_'); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const baseId = this.safeString (market, 'sellerCurrencyId'); + const quoteId = this.safeString (market, 'buyerCurrencyId'); + const baseNumericId = parseInt (baseId); + const quoteNumericId = parseInt (quoteId); + const symbol = base + '/' + quote; + const state = this.safeInteger (market, 'state'); + const active = (state === 1); + const fee = this.safeFloat (market, 'defaultFee'); + result.push ({ + 'id': id, + 'active': active, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'baseNumericId': baseNumericId, + 'quoteNumericId': quoteNumericId, + 'maker': fee, + 'taker': fee, + 'info': market, + 'precision': { + 'amount': this.safeInteger (market, 'amountDecimal'), + 'price': this.safeInteger (market, 'priceDecimal'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minAmount'), + 'max': undefined, + }, + 'price': { + 'min': 0, + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetExchangeConfigControllerWebsiteCurrencycontrollerGetCurrencyList (params); + // + // { + // "datas":[ + // { + // "currencyId":"456", + // "name":"pan", + // "alias":"pan", + // "logo":"pan.svg", + // "description":"pan", + // "descriptionEnglish":"pan", + // "defaultDecimal":2, + // "createUid":null, + // "createTime":1574068133762, + // "modifyUid":null, + // "modifyTime":0, + // "state":1, + // "mark":"pan", + // "totalNumber":"0", + // "publishNumber":"0", + // "marketValue":"0", + // "isLegalCoin":0, + // "needBlockUrl":1, + // "blockChainUrl":"https://etherscan.io/tx/", + // "tradeSearchUrl":null, + // "tokenCoinsId":0, + // "isMining":"0", + // "arithmetic":null, + // "founder":"bw_nxwal", + // "teamAddress":null, + // "remark":null, + // "tokenName":"ethw2", + // "isMemo":0, + // "websiteCurrencyId":"7rhqoHLohkG", + // "drawFlag":0, + // "rechargeFlag":1, + // "drawFee":"0.03000000", + // "onceDrawLimit":100, + // "dailyDrawLimit":500, + // "timesFreetrial":"0", + // "hourFreetrial":"0", + // "dayFreetrial":"0", + // "minFee":"0", + // "inConfigTimes":7, + // "outConfigTimes":7, + // "minCash":"0.06000000", + // "limitAmount":"0", + // "zbExist":false, + // "zone":1 + // }, + // ], + // "resMsg": { "message":"success !", "method":null, "code":"1" } + // } + // + const currencies = this.safeValue (response, 'datas', []); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'currencyId'); + const code = this.safeCurrencyCode (this.safeStringUpper (currency, 'name')); + const state = this.safeInteger (currency, 'state'); + const active = state === 1; + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': this.safeFloat (currency, 'drawFee'), + 'precision': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'limitAmount', 0), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': parseFloat (this.safeInteger (currency, 'onceDrawLimit')), + }, + }, + }; + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // [ + // "281", // market id + // "9754.4", // last + // "9968.8", // high + // "9631.5", // low + // "47865.6432", // base volume + // "-2.28", // change + // // closing price for last 6 hours + // "[[1, 9750.1], [2, 9737.1], [3, 9727.5], [4, 9722], [5, 9722.1], [6, 9754.4]]", + // "9752.12", // bid + // "9756.69", // ask + // "469849357.2364" // quote volume + // ] + // + let symbol = undefined; + const marketId = this.safeString (ticker, 0); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const close = parseFloat (this.safeValue (ticker, 1)); + const bid = this.safeValue (ticker, 'bid', {}); + const ask = this.safeValue (ticker, 'ask', {}); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': parseFloat (this.safeValue (ticker, 2)), + 'low': parseFloat (this.safeValue (ticker, 3)), + 'bid': parseFloat (this.safeValue (ticker, 7)), + 'bidVolume': this.safeFloat (bid, 'quantity'), + 'ask': parseFloat (this.safeValue (ticker, 8)), + 'askVolume': this.safeFloat (ask, 'quantity'), + 'vwap': undefined, + 'open': undefined, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': parseFloat (this.safeValue (ticker, 5)), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': parseFloat (this.safeValue (ticker, 4)), + 'quoteVolume': parseFloat (this.safeValue (ticker, 9)), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + const response = await this.publicGetApiDataV1Ticker (this.extend (request, params)); + // + // { + // "datas": [ + // "281", + // "7601.99", + // "8126.5", + // "7474.68", + // "47004.8708", + // "-6.18", + // "[[1, 7800.34], [2, 7626.41], [3, 7609.97], [4, 7569.04], [5, 7577.93], [6, 7601.99]]", + // "7600.24", + // "7603.69", + // "371968300.0119", + // ], + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + const ticker = this.safeValue (response, 'datas', []); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetApiDataV1Tickers (params); + // + // { + // "datas": [ + // [ + // "4051", + // "0.00194", + // "0.00863", + // "0.0012", + // "1519020", + // "-38.22", + // "[[1, 0.0023], [2, 0.00198], [3, 0.00199], [4, 0.00195], [5, 0.00199], [6, 0.00194]]", + // "0.00123", + // "0.0045", + // "4466.8104", + // ], + // ], + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const datas = this.safeValue (response, 'datas', []); + const result = {}; + for (let i = 0; i < datas.length; i++) { + const ticker = this.parseTicker (datas[i]); + const symbol = ticker['symbol']; + if ((symbols === undefined) || this.inArray (symbol, symbols)) { + result[symbol] = ticker; + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + if (limit !== undefined) { + request['dataSize'] = limit; + } + const response = await this.publicGetApiDataV1Entrusts (this.extend (request, params)); + // + // { + // "datas": { + // "asks": [ + // [ "9740.43", "0.0083" ], + // ], + // "bids": [ + // [ "9734.33", "0.0133" ], + // ], + // "timestamp": "1569303520", + // }, + // "resMsg": { + // "message": "success !", + // "method": null, + // "code": "1", + // }, + // } + // + const orderbook = this.safeValue (response, 'datas', []); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // [ + // "T", // trade + // "281", // market id + // "1569303302", // timestamp + // "BTC_USDT", // market name + // "ask", // side + // "9745.08", // price + // "0.0026" // amount + // ] + // + // fetchMyTrades (private) + // + // ... + // + const timestamp = this.safeTimestamp (trade, 2); + const price = this.safeFloat (trade, 5); + const amount = this.safeFloat (trade, 6); + const marketId = this.safeString (trade, 1); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const marketName = this.safeString (trade, 3); + const [ baseId, quoteId ] = marketName.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = this.costToPrecision (symbol, price * amount); + } + } + const sideString = this.safeString (trade, 4); + const side = (sideString === 'ask') ? 'sell' : 'buy'; + return { + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': parseFloat (cost), + 'fee': undefined, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + if (limit !== undefined) { + request['dataSize'] = limit; // max 20 + } + const response = await this.publicGetApiDataV1Trades (this.extend (request, params)); + // + // { + // "datas": [ + // [ + // "T", // trade + // "281", // market id + // "1569303302", // timestamp + // "BTC_USDT", // market name + // "ask", // side + // "9745.08", // price + // "0.0026" // amount + // ], + // ], + // "resMsg": { "code": "1", "method": null, "message": "success !" }, + // } + // + const trades = this.safeValue (response, 'datas', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 3), + this.safeFloat (ohlcv, 4), + this.safeFloat (ohlcv, 5), + this.safeFloat (ohlcv, 6), + this.safeFloat (ohlcv, 7), + this.safeFloat (ohlcv, 8), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'type': this.timeframes[timeframe], + 'dataSize': 500, + }; + if (limit !== undefined) { + request['dataSize'] = limit; + } + const response = await this.publicGetApiDataV1Klines (this.extend (request, params)); + const data = this.safeValue (response, 'datas', []); + const ohlcvs = this.parseOHLCVs (data, market, timeframe, since, limit); + return this.sortBy (ohlcvs, 0); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerFindbypage (params); + // + // { + // "datas": { + // "totalRow": 6, + // "pageSize": 99, + // "list": [ + // { + // "amount": "0.000090000000000000", // The current number of tokens available + // "currencyTypeId": 2, // Token ID + // "freeze": "0.009900000000000000", // Current token freezing quantity + // }, + // ], + // "pageNum": 1, + // }, + // "resMsg": { "code": "1", "message": "success !" } + // } + // + const data = this.safeValue (response, 'datas', {}); + const balances = this.safeValue (data, 'list', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeInteger (balance, 'currencyTypeId'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'amount'); + account['used'] = this.safeFloat (balance, 'freeze'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (price === undefined) { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'amount': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + 'type': (side === 'buy') ? 1 : 0, + 'rangeType': 0, // limit order + 'marketId': market['id'], + }; + const response = await this.privatePostExchangeEntrustControllerWebsiteEntrustControllerAddEntrust (this.extend (request, params)); + // + // { + // "datas": { + // "entrustId": "E6581105708337483776", + // }, + // "resMsg": { + // "message": "success !", + // "method": null, + // "code": "1" + // } + // } + // + const data = this.safeValue (response, 'datas'); + const id = this.safeString (data, 'entrustId'); + return { + 'id': id, + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'average': undefined, + 'filled': undefined, + 'remaining': undefined, + 'status': 'open', + 'fee': undefined, + 'trades': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + '-3': 'canceled', + '-2': 'canceled', + '-1': 'canceled', + '0': 'open', + '1': 'canceled', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder, fetchOpenOrders, fetchClosedOrders + // + // { + // "entrustId": "E6581108027628212224", // Order id + // "price": "1450", // price + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "amount": "14.05", // Order quantity + // "totalMoney": "20372.50", // Total order amount + // "completeAmount": "0", // Quantity sold + // "completeTotalMoney": "0", // Total dealt amount + // "type": 1, // 0 = sell, 1 = buy, -1 = cancel + // "entrustType": 0, // 0 = ordinary current price commission, 1 = lever commission + // "status": 0, // + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "availabelAmount": "14.05" // Outstanding quantity, typo in the docs or in the API, availabel vs available + // } + // + const marketId = this.safeString (order, 'marketId'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + const timestamp = this.safeInteger (order, 'createTime'); + let side = this.safeString (order, 'type'); + if (side === '0') { + side = 'sell'; + } else if (side === '1') { + side = 'buy'; + } + const amount = this.safeFloat (order, 'amount'); + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'completeAmount'); + let remaining = this.safeFloat2 (order, 'availabelAmount', 'availableAmount'); // typo in the docs or in the API, availabel vs available + let cost = this.safeFloat (order, 'totalMoney'); + if (filled !== undefined) { + if (amount !== undefined) { + if (remaining === undefined) { + remaining = amount - filled; + } + } + if (cost === undefined) { + if (price !== undefined) { + cost = filled * cost; + } + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + return { + 'info': order, + 'id': this.safeString (order, 'entrustId'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': this.safeString (market, 'symbol'), + 'type': 'limit', + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'entrustId': id, + }; + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetEntrustById (this.extend (request, params)); + // + // { + // "datas": { + // "entrustId": "E6581108027628212224", // Order id + // "price": "1450", // price + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "amount": "14.05", // Order quantity + // "totalMoney": "20372.50", // Total order amount + // "completeAmount": "0", // Quantity sold + // "completeTotalMoney": "0", // Total dealt amount + // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel + // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission + // "status": 0, // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "availabelAmount": "14.05" // Outstanding quantity + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + const order = this.safeValue (response, 'datas', {}); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'entrustId': id, + }; + const response = await this.privatePostExchangeEntrustControllerWebsiteEntrustControllerCancelEntrust (this.extend (request, params)); + // + // { + // "datas": null, + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + return { + 'info': response, + 'id': id, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageIndex': 0, // also works without it, most likely a typo in the docs + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default limit is 20 + } + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustRecordFromCacheWithPage (this.extend (request, params)); + // + // { + // "datas": { + // "pageNum": 1, + // "pageSize": 2, + // "totalPage": 20, + // "totalRow": 40, + // "entrustList": [ + // { + // "amount": "14.050000000000000000", // Order quantity + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "totalMoney": "20372.500000000000000000", // Total order amount + // "entrustId": "E6581108027628212224", // Order id + // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel + // "completeAmount": "0", // Quantity sold + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "price": "1450.000000000", // price + // "completeTotalMoney": "0", // Quantity sold + // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission + // "status": 0 // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt + // }, + // ], + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const orders = this.safeValue (data, 'entrustList', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default limit is 20 + } + if (since !== undefined) { + request['startDateTime'] = since; + } + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustList (this.extend (request, params)); + const data = this.safeValue (response, 'datas', {}); + const orders = this.safeValue (data, 'entrustList', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageIndex': 0, // also works without it, most likely a typo in the docs + // 'type': 0, // 0 = sell, 1 = buy, -1 = cancel + // 'status': -1, // -1 = insufficient funds, failed orders, 0 = pending orders, 1 = canceled, 2 = closed, 3 = partial + // 'startDateTime': since, + // 'endDateTime': this.milliseconds (), + }; + if (since !== undefined) { + request['startDateTime'] = since; + } + if (limit !== undefined) { + request['pageSize'] = limit; // default limit is 20 + } + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustList (this.extend (request, params)); + // + // { + // "datas": { + // "pageNum": 1, + // "pageSize": 2, + // "totalPage": 20, + // "totalRow": 40, + // "entrustList": [ + // { + // "amount": "14.050000000000000000", // Order quantity + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "totalMoney": "20372.500000000000000000", // Total order amount + // "entrustId": "E6581108027628212224", // Order id + // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel + // "completeAmount": "0", // Quantity sold + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "price": "1450.000000000", // price + // "completeTotalMoney": "0", // Quantity sold + // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission + // "status": 0 // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt + // }, + // ], + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const orders = this.safeValue (data, 'entrustList', []); + return this.parseOrders (orders, market, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'], { 'hostname': this.hostname }) + '/' + path; + if (method === 'GET') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + body = this.json (params); + } + if (api === 'private') { + const ms = this.milliseconds ().toString (); + let content = ''; + if (method === 'GET') { + const sortedParams = this.keysort (params); + const keys = Object.keys (sortedParams); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + content += key + sortedParams[key].toString (); + } + } else { + content = body; + } + const signature = this.apiKey + ms + content + this.secret; + const hash = this.hash (this.encode (signature), 'md5'); + if (!headers) { + headers = {}; + } + headers['Apiid'] = this.apiKey; + headers['Timestamp'] = ms; + headers['Sign'] = hash; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyTypeName': currency['name'], + }; + const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerGetPayinAddress (this.extend (request, params)); + // + // { + // "datas": { + // "isMemo": true, // 是�为memo 格�,false:�,true :是 + // "address": "bweosdeposit_787928102918558272", // 充�地� + // "memo": "787928102918558272", // ��memo + // "account": "bweosdeposit" // ��账户 + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + const data = this.safeValue (response, 'datas', {}); + const address = this.safeString (data, 'address'); + const tag = this.safeString (data, 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + '-1': 'canceled', // or auditing failed + '0': 'pending', + '1': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "depositId": "D6574268549744189441", // Deposit ID + // "amount": "54.753589700000000000", // Deposit amount + // "txId": "INNER_SYSTEM_TRANSFER_1198941", // Trading ID + // "confirmTimes": 0, // Confirmation number + // "depositAddress": "bweosdeposit_787928102918558272", // Deposit address + // "createTime": "2019-09-02 20:36:08.0", // Deposit time + // "status": 1, // Deposit status, 0: not received, 1: received + // "currencyTypeId": 7, // Token ID + // } + // + // fetchWithdrawals + // + // { + // "withdrawalId": "W6527498439872634880", // Withdrawal ID + // "fees": "0.500000000000000000", // Withdrawal fee + // "withdrawalAddress": "okbtothemoon_941657", // Withdrawal address + // "currencyId": "7", // Token ID + // "amount": "10.000000000000000000", // Withdrawal amount + // "state": 1, // Status, 1: normal, -1: delete + // "verifyStatus": 1, // Audit status, 0: to be audited, 1: auditing passed, -1: auditing failed + // "createTime": 1556276903656, // WIthdrawal time + // "actuallyAmount": "9.500000000000000000", // Actual amount received + // } + // + const id = this.safeString (transaction, 'depositId', 'withdrawalId'); + const address = this.safeString2 (transaction, 'depositAddress', 'withdrawalAddress'); + const currencyId = this.safeString2 (transaction, 'currencyId', 'currencyTypeId'); + let code = undefined; + if (currencyId in this.currencies_by_id) { + currency = this.currencies_by_id[currencyId]; + } + if ((code === undefined) && (currency !== undefined)) { + code = currency['code']; + } + const type = ('depositId' in transaction) ? 'deposit' : 'withdrawal'; + const amount = this.safeFloat2 (transaction, 'actuallyAmount', 'amount'); + const status = this.parseTransactionStatus (this.safeString2 (transaction, 'verifyStatus', 'state')); + const timestamp = this.safeInteger (transaction, 'createTime'); + const txid = this.safeString (transaction, 'txId'); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fees'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': code, + }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': address, + 'addressTo': undefined, + 'tagFrom': undefined, + 'tag': undefined, + 'tagTo': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyTypeName': currency['name'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageNum': 0, // also works without it, most likely a typo in the docs + // 'sort': 1, // 1 = asc, 0 = desc + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default 50 + } + const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerGetPayinCoinRecord (this.extend (request, params)); + // + // { + // "datas": { + // "totalRow":2, + // "totalPage": 1, + // "pageSize": 2, + // "pageNum": 1, + // "list": [ + // { + // "depositId": "D6574268549744189441", // Deposit ID + // "amount": "54.753589700000000000", // Deposit amount + // "txId": "INNER_SYSTEM_TRANSFER_1198941", // Trading ID + // "confirmTimes": 0, // Confirmation number + // "depositAddress": "bweosdeposit_787928102918558272", // Deposit address + // "createTime": "2019-09-02 20:36:08.0", // Deposit time + // "status": 1, // Deposit status, 0: not received, 1: received + // "currencyTypeId": 7, // Token ID + // }, + // ] + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const deposits = this.safeValue (data, 'list', []); + return this.parseTransactions (deposits, code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyId': currency['id'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageIndex': 0, // also works without it, most likely a typo in the docs + // 'tab': 'all', // all, wait (submitted, not audited), success (auditing passed), fail (auditing failed), cancel (canceled by user) + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default 50 + } + const response = await this.privateGetExchangeFundControllerWebsiteFundwebsitecontrollerGetpayoutcoinrecord (this.extend (request, params)); + // + // { + // "datas": { + // "totalRow": 1, + // "totalPage": 1, + // "pageSize": 2, + // "pageNum": 1, + // "list": [ + // { + // "withdrawalId": "W6527498439872634880", // Withdrawal ID + // "fees": "0.500000000000000000", // Withdrawal fee + // "withdrawalAddress": "okbtothemoon_941657", // Withdrawal address + // "currencyId": "7", // Token ID + // "amount": "10.000000000000000000", // Withdrawal amount + // "state": 1, // Status, 1: normal, -1: delete + // "verifyStatus": 1, // Audit status, 0: to be audited, 1: auditing passed, -1: auditing failed + // "createTime": 1556276903656, // WIthdrawal time + // "actuallyAmount": "9.500000000000000000", // Actual amount received + // }, + // ], + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const withdrawals = this.safeValue (data, 'list', []); + return this.parseTransactions (withdrawals, code, since, limit); + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // default error handler + } + const resMsg = this.safeValue (response, 'resMsg'); + const errorCode = this.safeString (resMsg, 'code'); + if (errorCode !== '1') { + const feedback = this.id + ' ' + this.json (response); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + throw new ExchangeError (feedback); // unknown error + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],58:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, BadRequest, AuthenticationError, DDoSProtection, BadResponse } = require ('./base/errors'); +const { TRUNCATE, NO_PADDING, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bytetrade extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bytetrade', + 'name': 'ByteTrade', + 'countries': ['HK'], + 'rateLimit': 500, + 'requiresWeb3': true, + 'certified': true, + // new metainfo interface + 'has': { + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'CORS': false, + 'fetchBidsAsks': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchMyTrades': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '4h': '4h', + '1d': '1d', + '5d': '5d', + '1w': '1w', + '1M': '1M', + }, + 'urls': { + 'test': 'https://api-v2-test.bytetrade.com', + 'logo': 'https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg', + 'api': 'https://api-v2.bytetrade.com', + 'www': 'https://www.bytetrade.com', + 'doc': 'https://github.com/Bytetrade/bytetrade-official-api-docs/wiki', + }, + 'api': { + 'market': { + 'get': [ + 'klines', // Kline of a symbol + 'depth', // Market Depth of a symbol + 'trades', // Trade records of a symbol + 'tickers', + ], + }, + 'public': { + 'get': [ + 'symbols', // Reference information of trading instrument, including base currency, quote precision, etc. + 'currencies', // The list of currencies available + 'balance', // Get the balance of an account + 'orders/open', // Get the open orders of an account + 'orders/closed', // Get the closed orders of an account + 'orders/all', // Get the open and closed orders of an account + 'orders', // Get the details of an order of an account + 'orders/trades', // Get detail match results + 'depositaddress', // Get deposit address + 'withdrawals', // Get withdrawals info + 'deposits', // Get deposit info + 'transfers', // Get transfer info + ], + 'post': [ + 'transaction/createorder', // Post create order transaction to blockchain + 'transaction/cancelorder', // Post cancel order transaction to blockchain + 'transaction/withdraw', // Post withdraw transaction to blockchain + 'transaction/transfer', // Post transfer transaction to blockchain + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.0008, + 'maker': 0.0008, + }, + }, + 'commonCurrencies': { + '48': 'Blocktonic', + }, + 'exceptions': { + 'vertify error': AuthenticationError, // typo on the exchange side, 'vertify' + 'verify error': AuthenticationError, // private key signature is incorrect + 'transaction already in network': BadRequest, // same transaction submited + 'invalid argument': BadRequest, + }, + }); + } + + async fetchCurrencies (params = {}) { + const currencies = await this.publicGetCurrencies (params); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'code'); + let code = undefined; + if (id in this.commonCurrencies) { + code = this.commonCurrencies[id]; + } else { + code = this.safeString (currency, 'name'); + } + const name = this.safeString (currency, 'fullname'); + // in bytetrade.com DEX, request https://api-v2.bytetrade.com/currencies will return currencies, + // the api doc is https://github.com/Bytetrade/bytetrade-official-api-docs/wiki/rest-api#get-currencies-get-currencys-supported-in-bytetradecom + // we can see the coin name is none-unique in the result, the coin which code is 18 is the CyberMiles ERC20, and the coin which code is 35 is the CyberMiles main chain, but their name is same. + // that is because bytetrade is a DEX, supports people create coin with the same name, but the id(code) of coin is unique, so we should use the id or name and id as the identity of coin. + // For coin name and symbol is same with CCXT, I use name@id as the key of commonCurrencies dict. + // [{ + // "name": "CMT", // currency name, non-unique + // "code": "18", // currency id, unique + // "type": "crypto", + // "fullname": "CyberMiles", + // "active": true, + // "chainType": "ethereum", + // "basePrecision": 18, + // "transferPrecision": 10, + // "externalPrecision": 18, + // "chainContractAddress": "0xf85feea2fdd81d51177f6b8f35f0e6734ce45f5f", + // "limits": { + // "deposit": { + // "min": "0", + // "max": "-1" + // }, + // "withdraw": { + // "min": "0", + // "max": "-1" + // } + // } + // }, + // { + // "name": "CMT", + // "code": "35", + // "type": "crypto", + // "fullname": "CyberMiles", + // "active": true, + // "chainType": "cmt", + // "basePrecision": 18, + // "transferPrecision": 10, + // "externalPrecision": 18, + // "chainContractAddress": "0x0000000000000000000000000000000000000000", + // "limits": { + // "deposit": { + // "min": "1", + // "max": "-1" + // }, + // "withdraw": { + // "min": "10", + // "max": "-1" + // } + // } + // } + // ] + const active = this.safeValue (currency, 'active'); + const limits = this.safeValue (currency, 'limits'); + const deposit = this.safeValue (limits, 'deposit'); + const amountPrecision = this.safeInteger (currency, 'basePrecision'); + let maxDeposit = this.safeFloat (deposit, 'max'); + if (maxDeposit === -1.0) { + maxDeposit = undefined; + } + const withdraw = this.safeValue (limits, 'withdraw'); + let maxWithdraw = this.safeFloat (withdraw, 'max'); + if (maxWithdraw === -1.0) { + maxWithdraw = undefined; + } + result[code] = { + 'id': id, + 'code': code, + 'name': name, + 'active': active, + 'precision': { + 'amount': amountPrecision, + 'price': undefined, + }, + 'fee': undefined, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'deposit': { + 'min': this.safeFloat (deposit, 'min'), + 'max': maxDeposit, + }, + 'withdraw': { + 'min': this.safeFloat (withdraw, 'min'), + 'max': maxWithdraw, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetSymbols (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + let base = this.safeString (market, 'baseName'); + let quote = this.safeString (market, 'quoteName'); + const baseId = this.safeString (market, 'base'); + const quoteId = this.safeString (market, 'quote'); + if (baseId in this.commonCurrencies) { + base = this.commonCurrencies[baseId]; + } + if (quoteId in this.commonCurrencies) { + quote = this.commonCurrencies[quoteId]; + } + const symbol = base + '/' + quote; + const limits = this.safeValue (market, 'limits', {}); + const amount = this.safeValue (limits, 'amount', {}); + const price = this.safeValue (limits, 'price', {}); + const precision = this.safeValue (market, 'precision', {}); + const active = this.safeString (market, 'active'); + const normalBase = base.split ('@')[0]; + const normalQuote = quote.split ('@')[0]; + const normalSymbol = normalBase + '/' + normalQuote; + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': { + 'amount': this.safeInteger (precision, 'amount'), + 'price': this.safeInteger (precision, 'price'), + }, + 'normalSymbol': normalSymbol, + 'limits': { + 'amount': { + 'min': this.safeFloat (amount, 'min'), + 'max': this.safeFloat (amount, 'max'), + }, + 'price': { + 'min': this.safeFloat (price, 'min'), + 'max': this.safeFloat (price, 'max'), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + result.push (entry); + } + return result; + } + + async fetchBalance (params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired (this.id + ' fetchDeposits requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const request = { + 'userid': this.apiKey, + }; + const balances = await this.publicGetBalance (this.extend (request, params)); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'code'); + const code = this.safeCurrencyCode (currencyId, undefined); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'free'); + account['used'] = this.safeFloat (balance, 'used'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = maximum = 100 + } + const response = await this.marketGetDepth (this.extend (request, params)); + const timestamp = this.safeValue (response, 'timestamp'); + const orderbook = this.parseOrderBook (response, timestamp); + return orderbook; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'timestamp'); + // + // [ + // { + // "symbol":"68719476706", + // "name":"ETH/BTC", + // "base":"2", + // "quote":"32", + // "timestamp":1575905991933, + // "datetime":"2019-12-09T15:39:51.933Z", + // "high":"0", + // "low":"0", + // "open":"0", + // "close":"0", + // "last":"0", + // "change":"0", + // "percentage":"0", + // "baseVolume":"0", + // "quoteVolume":"0" + // } + // ] + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (ticker, 'base'); + const quoteId = this.safeString (ticker, 'quote'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': this.safeFloat (ticker, 'weightedAvgPrice'), + 'open': this.safeFloat (ticker, 'open'), + 'close': this.safeFloat (ticker, 'close'), + 'last': this.safeFloat (ticker, 'last'), + 'previousClose': undefined, // previous day close + 'change': this.safeFloat (ticker, 'change'), + 'percentage': this.safeFloat (ticker, 'percentage'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetTickers (this.extend (request, params)); + // + // [ + // { + // "symbol":"68719476706", + // "name":"ETH/BTC", + // "base":"2", + // "quote":"32", + // "timestamp":1575905991933, + // "datetime":"2019-12-09T15:39:51.933Z", + // "high":"0", + // "low":"0", + // "open":"0", + // "close":"0", + // "last":"0", + // "change":"0", + // "percentage":"0", + // "baseVolume":"0", + // "quoteVolume":"0" + // } + // ] + // + if (Array.isArray (response)) { + const ticker = this.safeValue (response, 0); + if (ticker === undefined) { + throw new BadResponse (this.id + ' fetchTicker() returned an empty response'); + } + return this.parseTicker (ticker, market); + } + return this.parseTicker (response, market); + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchBidsAsks (symbols = undefined, params = {}) { + await this.loadMarkets (); + const rawTickers = await this.marketGetDepth (params); + return this.parseTickers (rawTickers, symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const rawTickers = await this.marketGetTickers (params); + return this.parseTickers (rawTickers, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0], + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'timeframe': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.marketGetKlines (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const cost = this.safeFloat (trade, 'cost'); + const id = this.safeString (trade, 'id'); + const type = this.safeString (trade, 'type'); + const takerOrMaker = this.safeString (trade, 'takerOrMaker'); + const side = this.safeString (trade, 'side'); + const datetime = this.safeString (trade, 'datetime'); + const order = this.safeString (trade, 'order'); + const fee = this.safeValue (trade, 'fee'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': datetime, + 'symbol': symbol, + 'id': id, + 'order': order, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default = 100, maximum = 500 + } + const response = await this.marketGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + const status = this.safeString (order, 'status'); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (order, 'base'); + const quoteId = this.safeString (order, 'quote'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'timestamp'); + const datetime = this.safeString (order, 'datetime'); + const lastTradeTimestamp = this.safeInteger (order, 'lastTradeTimestamp'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'filled'); + const remaining = this.safeFloat (order, 'remaining'); + const cost = this.safeFloat (order, 'cost'); + const average = this.safeFloat (order, 'average'); + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + const fee = this.safeValue (order, 'fee'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': datetime, + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + this.checkRequiredDependencies (); + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('createOrder requires this.apiKey or userid in params'); + } + await this.loadMarkets (); + const market = this.market (symbol); + let sideNum = undefined; + let typeNum = undefined; + if (side === 'sell') { + sideNum = 1; + } else { + sideNum = 2; + } + if (type === 'limit') { + typeNum = 1; + } else { + typeNum = 2; + } + const normalSymbol = market['normalSymbol']; + const baseId = market['baseId']; + const baseCurrency = this.currency (market['base']); + const amountTruncated = this.amountToPrecision (symbol, amount); + const amountChain = this.toWei (amountTruncated, 'ether', baseCurrency['precision']['amount']); + const quoteId = market['quoteId']; + const quoteCurrency = this.currency (market['quote']); + const priceRounded = this.priceToPrecision (symbol, price); + const priceChain = this.toWei (priceRounded, 'ether', quoteCurrency['precision']['amount']); + const now = this.milliseconds (); + const expiration = this.milliseconds (); + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const chainName = 'Sagittarius'; + const defaultFee = this.safeString (this.options, 'fee', '300000000000000'); + const fee = this.safeString (params, 'fee', defaultFee); + const eightBytes = this.integerPow ('2', '64'); + const allByteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (32, 1), + this.numberToLE (0, 8), + this.numberToLE (fee, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (sideNum, 1), + this.numberToLE (typeNum, 1), + this.numberToLE (normalSymbol.length, 1), + this.stringToBinary (this.encode (normalSymbol)), + this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (this.integerDivide (priceChain, eightBytes), 8), + this.numberToLE (this.integerModulo (priceChain, eightBytes), 8), + this.numberToLE (0, 2), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (0, 2), + this.numberToLE (parseInt (quoteId), 4), + this.numberToLE (parseInt (baseId), 4), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const txByteStringArray = [ + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (32, 1), + this.numberToLE (0, 8), + this.numberToLE (fee, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (sideNum, 1), + this.numberToLE (typeNum, 1), + this.numberToLE (normalSymbol.length, 1), + this.stringToBinary (this.encode (normalSymbol)), + this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (this.integerDivide (priceChain, eightBytes), 8), + this.numberToLE (this.integerModulo (priceChain, eightBytes), 8), + this.numberToLE (0, 2), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (0, 2), + this.numberToLE (parseInt (quoteId), 4), + this.numberToLE (parseInt (baseId), 4), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const txbytestring = this.binaryConcatArray (txByteStringArray); + const txidhash = this.hash (txbytestring, 'sha256', 'hex'); + const txid = txidhash.slice (0, 40); + const orderidByteStringArray = [ + this.numberToLE (txid.length, 1), + this.stringToBinary (this.encode (txid)), + this.numberToBE (0, 4), + ]; + const orderidbytestring = this.binaryConcatArray (orderidByteStringArray); + const orderidhash = this.hash (orderidbytestring, 'sha256', 'hex'); + const orderid = orderidhash.slice (0, 40); + const bytestring = this.binaryConcatArray (allByteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + const operation = { + 'now': datetime, + 'expiration': expirationDatetime, + 'fee': fee, + 'creator': this.apiKey, + 'side': sideNum, + 'order_type': typeNum, + 'market_name': normalSymbol, + 'amount': amountChain, + 'price': priceChain, + 'use_btt_as_fee': false, + 'money_id': parseInt (quoteId), + 'stock_id': parseInt (baseId), + }; + const fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + 32, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + const request = { + 'trObj': this.json (fatty), + }; + const response = await this.publicPostTransactionCreateorder (request); + const timestamp = this.milliseconds (); + const statusCode = this.safe_string (response, 'code'); + const status = (statusCode === '0') ? 'open' : 'failed'; + return { + 'info': response, + 'id': orderid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': undefined, + 'type': undefined, + 'side': undefined, + 'price': undefined, + 'amount': undefined, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchOrder requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const request = { + 'userid': this.apiKey, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.markets[symbol]; + request['symbol'] = market['id']; + } + request['id'] = id; + const response = await this.publicGetOrders (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchOpenOrders requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const request = { + 'userid': this.apiKey, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersOpen (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchClosedOrders requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + let market = undefined; + const request = { + 'userid': this.apiKey, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersClosed (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchOrders requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + let market = undefined; + const request = { + 'userid': this.apiKey, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersAll (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('cancelOrder requires hasAlreadyAuthenticatedSuccessfully'); + } + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const baseId = market['baseId']; + const quoteId = market['quoteId']; + const normalSymbol = market['normalSymbol']; + const feeAmount = '300000000000000'; + const now = this.milliseconds (); + const expiration = 0; + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const chainName = 'Sagittarius'; + const byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (expiration, 4), + this.numberToLE (1, 1), + this.numberToLE (33, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (normalSymbol.length, 1), + this.stringToBinary (this.encode (normalSymbol)), + this.base16ToBinary (id), + this.numberToLE (parseInt (quoteId), 4), + this.numberToLE (parseInt (baseId), 4), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const bytestring = this.binaryConcatArray (byteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + const operation = { + 'fee': feeAmount, + 'creator': this.apiKey, + 'order_id': id, + 'market_name': normalSymbol, + 'money_id': parseInt (quoteId), + 'stock_id': parseInt (baseId), + }; + const fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + 33, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + const request = { + 'trObj': this.json (fatty), + }; + const response = await this.publicPostTransactionCancelorder (request); + const timestamp = this.milliseconds (); + const statusCode = this.safe_string (response, 'code'); + const status = (statusCode === '0') ? 'canceled' : 'failed'; + return { + 'info': response, + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': undefined, + 'type': undefined, + 'side': undefined, + 'price': undefined, + 'amount': undefined, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async transfer (code, amount, address, params = {}) { + this.checkRequiredDependencies (); + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('transfer requires this.apiKey'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING); + const amountChain = this.toWei (amountTruncate, 'ether', currency['precision']['amount']); + const assetType = parseInt (currency['id']); + const now = this.milliseconds (); + const expiration = now; + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const feeAmount = '300000000000000'; + const chainName = 'Sagittarius'; + const eightBytes = this.integerPow ('2', '64'); + const byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (0, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (address.length, 1), + this.stringToBinary (this.encode (address)), + this.numberToLE (assetType, 4), + this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const bytestring = this.binaryConcatArray (byteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + const operation = { + 'fee': '300000000000000', + 'from': this.apiKey, + 'to': address, + 'asset_type': parseInt (currency['id']), + 'amount': amountChain.toString (), + }; + const fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + 0, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + const request = { + 'trObj': this.json (fatty), + }; + const response = await this.publicPostTransactionTransfer (request); + const timestamp = this.milliseconds (); + const statusCode = this.safe_string (response, 'code'); + let status = ''; + if (statusCode === '0') { + status = 'submit success'; + } else { + status = 'submit fail'; + } + return { + 'info': response, + 'id': '', + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': undefined, + 'type': undefined, + 'side': undefined, + 'price': undefined, + 'amount': undefined, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'fee': undefined, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchMyTrades requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'userid': this.apiKey, + }; + if (symbol !== undefined) { + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchDeposits requires this.apiKey or userid argument'); + } + let currency = undefined; + const request = { + 'userid': this.apiKey, + }; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetDeposits (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchWithdrawals requires this.apiKey or userid argument'); + } + let currency = undefined; + const request = { + 'userid': this.apiKey, + }; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetWithdrawals (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'DEPOSIT_FAILED': 'failed', + 'FEE_SEND_FAILED': 'failed', + 'FEE_FAILED': 'failed', + 'PAY_SEND_FAILED': 'failed', + 'PAY_FAILED': 'failed', + 'BTT_FAILED': 'failed', + 'WITHDDRAW_FAILED': 'failed', + 'USER_FAILED': 'failed', + 'FEE_EXECUED': 'pending', + 'PAY_EXECUED': 'pending', + 'WITHDDRAW_EXECUTED': 'pending', + 'USER_EXECUED': 'pending', + 'BTT_SUCCED': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + let tag = this.safeString (transaction, 'tag'); + if (tag !== undefined) { + if (tag.length < 1) { + tag = undefined; + } + } + const txid = this.safeValue (transaction, 'txid'); + const currencyId = this.safeString (transaction, 'code'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeInteger (transaction, 'timestamp'); + const datetime = this.safeString (transaction, 'datetime'); + const type = this.safeString (transaction, 'type'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const amount = this.safeFloat (transaction, 'amount'); + const feeInfo = this.safeValue (transaction, 'fee'); + const feeCost = this.safeFloat (feeInfo, 'cost'); + const feeCurrencyId = this.safeString (feeInfo, 'code'); + const feeCode = this.safeCurrencyCode (feeCurrencyId, currency); + const fee = { + 'cost': feeCost, + 'currency': feeCode, + }; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': datetime, + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchDepositAddress requires this.apiKey or userid argument'); + } + const currency = this.currency (code); + const request = { + 'userid': this.apiKey, + 'code': currency['id'], + }; + const response = await this.publicGetDepositaddress (request); + const address = this.safeString (response[0], 'address'); + const tag = this.safeString (response[0], 'tag'); + const chainType = this.safeString (response[0], 'chainType'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'chainType': chainType, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkRequiredDependencies (); + this.checkAddress (address); + await this.loadMarkets (); + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('withdraw requires this.apiKey'); + } + const addressResponse = await this.fetchDepositAddress (code); + const chainTypeString = this.safeString (addressResponse, 'chainType'); + const chainId = this.safeString (addressResponse['info'][0], 'chainId'); + let middleAddress = ''; + if (chainTypeString === 'eos') { + middleAddress = address; + } else { + middleAddress = this.safeString (addressResponse, 'address'); + } + let operationId = 18; + if (chainTypeString !== 'ethereum' && chainTypeString !== 'etc' && chainTypeString !== 'eos' && chainTypeString !== 'cmt' && chainTypeString !== 'naka') { + operationId = 26; + } + const now = this.milliseconds (); + const expiration = 0; + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const chainName = 'Sagittarius'; + const feeAmount = '300000000000000'; + const currency = this.currency (code); + const coinId = currency['id']; + const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING); + const amountChain = this.toWei (amountTruncate, 'ether', currency['info']['externalPrecision']); + const eightBytes = this.integerPow ('2', '64'); + let assetFee = 0; + let byteStringArray = []; + if (operationId === 26) { + assetFee = currency['info']['fee']; + byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (operationId, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (address.length, 1), + this.stringToBinary (this.encode (address)), + this.numberToLE (parseInt (coinId), 4), + this.numberToLE (Math.floor (parseInt (parseFloat (this.integerDivide (amountChain, eightBytes)))), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (1, 1), + this.numberToLE (this.integerDivide (assetFee, eightBytes), 8), + this.numberToLE (this.integerModulo (assetFee, eightBytes), 8), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + } else { + byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (operationId, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (4, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (middleAddress.length, 1), + this.stringToBinary (this.encode (middleAddress)), + this.numberToLE (parseInt (coinId), 4), + this.numberToLE (Math.floor (parseInt (parseFloat (this.integerDivide (amountChain, eightBytes)))), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + } + const bytestring = this.binaryConcatArray (byteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + let fatty = undefined; + let request = undefined; + let operation = undefined; + const chainContractAddress = this.safeString (currency['info'], 'chainContractAddress'); + if (operationId === 26) { + operation = { + 'fee': feeAmount, + 'from': this.apiKey, + 'to_external_address': address, + 'asset_type': parseInt (coinId), + 'amount': amountChain, + 'asset_fee': assetFee, + }; + fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + operationId, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + request = { + 'chainType': chainId, + 'trObj': this.json (fatty), + 'chainContractAddresss': chainContractAddress, + }; + } else { + operation = { + 'fee': feeAmount, + 'from': this.apiKey, + 'to_external_address': middleAddress, + 'asset_type': parseInt (coinId), + 'amount': amountChain, + 'asset_fee': assetFee, + }; + const middle = { + 'fee': feeAmount, + 'proposaler': this.apiKey, + 'expiration_time': datetime, + 'proposed_ops': [{ + 'op': [4, operation], + }], + }; + fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + operationId, + middle, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + if (chainTypeString === 'eos') { + request = { + 'chainType': chainId, + 'toExternalAddress': 'noneed', + 'trObj': this.json (fatty), + 'chainContractAddresss': chainContractAddress, + }; + } else { + request = { + 'chainType': chainId, + 'toExternalAddress': address, + 'trObj': this.json (fatty), + 'chainContractAddresss': chainContractAddress, + }; + } + } + const response = await this.publicPostTransactionWithdraw (request); + return { + 'info': response, + 'id': this.safeString (response, 'id'), + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api']; + url += '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code === 503) { + throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); + } + if (response === undefined) { + return; // fallback to default error handler + } + if ('code' in response) { + const status = this.safeString (response, 'code'); + if (status === '1') { + const message = this.safeString (response, 'msg'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + throw new ExchangeError (feedback); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],59:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, NullResponse, InvalidOrder, NotSupported, InsufficientFunds, InvalidNonce, OrderNotFound, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class cex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'cex', + 'name': 'CEX.IO', + 'countries': [ 'GB', 'EU', 'CY', 'RU' ], + 'rateLimit': 1500, + 'has': { + 'CORS': true, + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchDepositAddress': true, + 'fetchOrders': true, + }, + 'timeframes': { + '1m': '1m', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg', + 'api': 'https://cex.io/api', + 'www': 'https://cex.io', + 'doc': 'https://cex.io/cex-api', + 'fees': [ + 'https://cex.io/fee-schedule', + 'https://cex.io/limits-commissions', + ], + 'referral': 'https://cex.io/r/0/up105393824/0/', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'currency_profile', + 'currency_limits/', + 'last_price/{pair}/', + 'last_prices/{currencies}/', + 'ohlcv/hd/{yyyymmdd}/{pair}', + 'order_book/{pair}/', + 'ticker/{pair}/', + 'tickers/{currencies}/', + 'trade_history/{pair}/', + ], + 'post': [ + 'convert/{pair}', + 'price_stats/{pair}', + ], + }, + 'private': { + 'post': [ + 'active_orders_status/', + 'archived_orders/{pair}/', + 'balance/', + 'cancel_order/', + 'cancel_orders/{pair}/', + 'cancel_replace_order/{pair}/', + 'close_position/{pair}/', + 'get_address/', + 'get_myfee/', + 'get_order/', + 'get_order_tx/', + 'open_orders/{pair}/', + 'open_orders/', + 'open_position/{pair}/', + 'open_positions/{pair}/', + 'place_order/{pair}/', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.16 / 100, + 'taker': 0.25 / 100, + }, + 'funding': { + 'withdraw': { + // 'USD': undefined, + // 'EUR': undefined, + // 'RUB': undefined, + // 'GBP': undefined, + 'BTC': 0.001, + 'ETH': 0.01, + 'BCH': 0.001, + 'DASH': 0.01, + 'BTG': 0.001, + 'ZEC': 0.001, + 'XRP': 0.02, + }, + 'deposit': { + // 'USD': amount => amount * 0.035 + 0.25, + // 'EUR': amount => amount * 0.035 + 0.24, + // 'RUB': amount => amount * 0.05 + 15.57, + // 'GBP': amount => amount * 0.035 + 0.2, + 'BTC': 0.0, + 'ETH': 0.0, + 'BCH': 0.0, + 'DASH': 0.0, + 'BTG': 0.0, + 'ZEC': 0.0, + 'XRP': 0.0, + 'XLM': 0.0, + }, + }, + }, + 'exceptions': { + 'exact': {}, + 'broad': { + 'Insufficient funds': InsufficientFunds, + 'Nonce must be incremented': InvalidNonce, + 'Invalid Order': InvalidOrder, + 'Order not found': OrderNotFound, + 'Rate limit exceeded': RateLimitExceeded, + }, + }, + 'options': { + 'fetchOHLCVWarning': true, + 'createMarketBuyOrderRequiresPrice': true, + 'order': { + 'status': { + 'c': 'canceled', + 'd': 'closed', + 'cd': 'closed', + 'a': 'open', + }, + }, + }, + }); + } + + async fetchCurrenciesFromCache (params = {}) { + // this method is now redundant + // currencies are now fetched before markets + const options = this.safeValue (this.options, 'fetchCurrencies', {}); + const timestamp = this.safeInteger (options, 'timestamp'); + const expires = this.safeInteger (options, 'expires', 1000); + const now = this.milliseconds (); + if ((timestamp === undefined) || ((now - timestamp) > expires)) { + const response = await this.publicGetCurrencyProfile (params); + this.options['fetchCurrencies'] = this.extend (options, { + 'response': response, + 'timestamp': now, + }); + } + return this.safeValue (this.options['fetchCurrencies'], 'response'); + } + + async fetchCurrencies (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + this.options['currencies'] = { + 'timestamp': this.milliseconds (), + 'response': response, + }; + // + // { + // "e":"currency_profile", + // "ok":"ok", + // "data":{ + // "symbols":[ + // { + // "code":"GHS", + // "contract":true, + // "commodity":true, + // "fiat":false, + // "description":"CEX.IO doesn't provide cloud mining services anymore.", + // "precision":8, + // "scale":0, + // "minimumCurrencyAmount":"0.00000001", + // "minimalWithdrawalAmount":-1 + // }, + // { + // "code":"BTC", + // "contract":false, + // "commodity":false, + // "fiat":false, + // "description":"", + // "precision":8, + // "scale":0, + // "minimumCurrencyAmount":"0.00000001", + // "minimalWithdrawalAmount":0.002 + // }, + // { + // "code":"ETH", + // "contract":false, + // "commodity":false, + // "fiat":false, + // "description":"", + // "precision":8, + // "scale":2, + // "minimumCurrencyAmount":"0.00000100", + // "minimalWithdrawalAmount":0.01 + // } + // ], + // "pairs":[ + // { + // "symbol1":"BTC", + // "symbol2":"USD", + // "pricePrecision":1, + // "priceScale":"/1000000", + // "minLotSize":0.002, + // "minLotSizeS2":20 + // }, + // { + // "symbol1":"ETH", + // "symbol2":"USD", + // "pricePrecision":2, + // "priceScale":"/10000", + // "minLotSize":0.1, + // "minLotSizeS2":20 + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', []); + const currencies = this.safeValue (data, 'symbols', []); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'code'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'precision'); + const active = true; + result[code] = { + 'id': id, + 'code': code, + 'name': id, + 'active': active, + 'precision': precision, + 'fee': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minimumCurrencyAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimalWithdrawalAmount'), + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const currenciesResponse = await this.fetchCurrenciesFromCache (params); + const currenciesData = this.safeValue (currenciesResponse, 'data', {}); + const currencies = this.safeValue (currenciesData, 'symbols', []); + const currenciesById = this.indexBy (currencies, 'code'); + const pairs = this.safeValue (currenciesData, 'pairs', []); + const response = await this.publicGetCurrencyLimits (params); + // + // { + // "e":"currency_limits", + // "ok":"ok", + // "data": { + // "pairs":[ + // { + // "symbol1":"BTC", + // "symbol2":"USD", + // "minLotSize":0.002, + // "minLotSizeS2":20, + // "maxLotSize":30, + // "minPrice":"1500", + // "maxPrice":"35000" + // }, + // { + // "symbol1":"BCH", + // "symbol2":"EUR", + // "minLotSize":0.1, + // "minLotSizeS2":20, + // "maxLotSize":null, + // "minPrice":"25", + // "maxPrice":"8192" + // } + // ] + // } + // } + // + const result = []; + const markets = this.safeValue (response['data'], 'pairs'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = this.safeString (market, 'symbol1'); + const quoteId = this.safeString (market, 'symbol2'); + const id = baseId + '/' + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const baseCurrency = this.safeValue (currenciesById, baseId, {}); + const quoteCurrency = this.safeValue (currenciesById, quoteId, {}); + let pricePrecision = this.safeInteger (quoteCurrency, 'precision', 8); + for (let j = 0; j < pairs.length; j++) { + const pair = pairs[j]; + if ((pair['symbol1'] === baseId) && (pair['symbol2'] === quoteId)) { + // we might need to account for `priceScale` here + pricePrecision = this.safeInteger (pair, 'pricePrecision', pricePrecision); + } + } + const baseCcyPrecision = this.safeInteger (baseCurrency, 'precision', 8); + const baseCcyScale = this.safeInteger (baseCurrency, 'scale', 0); + const amountPrecision = baseCcyPrecision - baseCcyScale; + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + result.push ({ + 'id': id, + 'info': market, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minLotSize'), + 'max': this.safeFloat (market, 'maxLotSize'), + }, + 'price': { + 'min': this.safeFloat (market, 'minPrice'), + 'max': this.safeFloat (market, 'maxPrice'), + }, + 'cost': { + 'min': this.safeFloat (market, 'minLotSizeS2'), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const ommited = [ 'username', 'timestamp' ]; + const balances = this.omit (response, ommited); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + // https://github.com/ccxt/ccxt/issues/5484 + account['used'] = this.safeFloat (balance, 'orders', 0.0); + const code = this.safeCurrencyCode (currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + if (limit !== undefined) { + request['depth'] = limit; + } + const response = await this.publicGetOrderBookPair (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (since === undefined) { + since = this.milliseconds () - 86400000; // yesterday + } else { + if (this.options['fetchOHLCVWarning']) { + throw new ExchangeError (this.id + " fetchOHLCV warning: CEX can return historical candles for a certain date only, this might produce an empty or null reply. Set exchange.options['fetchOHLCVWarning'] = false or add ({ 'options': { 'fetchOHLCVWarning': false }}) to constructor params to suppress this warning message."); + } + } + let ymd = this.ymd (since); + ymd = ymd.split ('-'); + ymd = ymd.join (''); + const request = { + 'pair': market['id'], + 'yyyymmdd': ymd, + }; + try { + const response = await this.publicGetOhlcvHdYyyymmddPair (this.extend (request, params)); + const key = 'data' + this.timeframes[timeframe]; + const ohlcvs = JSON.parse (response[key]); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } catch (e) { + if (e instanceof NullResponse) { + return []; + } + } + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const volume = this.safeFloat (ticker, 'volume'); + const high = this.safeFloat (ticker, 'high'); + const low = this.safeFloat (ticker, 'low'); + const bid = this.safeFloat (ticker, 'bid'); + const ask = this.safeFloat (ticker, 'ask'); + const last = this.safeFloat (ticker, 'last'); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': high, + 'low': low, + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': volume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const currencies = Object.keys (this.currencies); + const request = { + 'currencies': currencies.join ('/'), + }; + const response = await this.publicGetTickersCurrencies (this.extend (request, params)); + const tickers = response['data']; + const result = {}; + for (let t = 0; t < tickers.length; t++) { + const ticker = tickers[t]; + const symbol = ticker['pair'].replace (':', '/'); + const market = this.markets[symbol]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const ticker = await this.publicGetTickerPair (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTradeHistoryPair (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + // for market buy it requires the amount of quote currency to spend + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + amount = amount * price; + } + } + } + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + 'type': side, + 'amount': amount, + }; + if (type === 'limit') { + request['price'] = price; + } else { + request['order_type'] = type; + } + const response = await this.privatePostPlaceOrderPair (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + parseOrder (order, market = undefined) { + // Depending on the call, 'time' can be a unix int, unix string or ISO string + // Yes, really + let timestamp = this.safeValue (order, 'time'); + if (typeof timestamp === 'string' && timestamp.indexOf ('T') >= 0) { + // ISO8601 string + timestamp = this.parse8601 (timestamp); + } else { + // either integer or string integer + timestamp = parseInt (timestamp); + } + let symbol = undefined; + if (market === undefined) { + const baseId = this.safeString (order, 'symbol1'); + const quoteId = this.safeString (order, 'symbol2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + if (symbol in this.markets) { + market = this.market (symbol); + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'price'); + let amount = this.safeFloat (order, 'amount'); + // sell orders can have a negative amount + // https://github.com/ccxt/ccxt/issues/5338 + if (amount !== undefined) { + amount = Math.abs (amount); + } + const remaining = this.safeFloat2 (order, 'pending', 'remains'); + const filled = amount - remaining; + let fee = undefined; + let cost = undefined; + if (market !== undefined) { + symbol = market['symbol']; + cost = this.safeFloat (order, 'ta:' + market['quote']); + if (cost === undefined) { + cost = this.safeFloat (order, 'tta:' + market['quote']); + } + const baseFee = 'fa:' + market['base']; + const baseTakerFee = 'tfa:' + market['base']; + const quoteFee = 'fa:' + market['quote']; + const quoteTakerFee = 'tfa:' + market['quote']; + let feeRate = this.safeFloat (order, 'tradingFeeMaker'); + if (!feeRate) { + feeRate = this.safeFloat (order, 'tradingFeeTaker', feeRate); + } + if (feeRate) { + feeRate /= 100.0; // convert to mathematically-correct percentage coefficients: 1.0 = 100% + } + if ((baseFee in order) || (baseTakerFee in order)) { + const baseFeeCost = this.safeFloat2 (order, baseFee, baseTakerFee); + fee = { + 'currency': market['base'], + 'rate': feeRate, + 'cost': baseFeeCost, + }; + } else if ((quoteFee in order) || (quoteTakerFee in order)) { + const quoteFeeCost = this.safeFloat2 (order, quoteFee, quoteTakerFee); + fee = { + 'currency': market['quote'], + 'rate': feeRate, + 'cost': quoteFeeCost, + }; + } + } + if (!cost) { + cost = price * filled; + } + const side = order['type']; + let trades = undefined; + const orderId = order['id']; + if ('vtx' in order) { + trades = []; + for (let i = 0; i < order['vtx'].length; i++) { + const item = order['vtx'][i]; + const tradeSide = this.safeString (item, 'type'); + if (tradeSide === 'cancel') { + // looks like this might represent the cancelled part of an order + // { id: '4426729543', + // type: 'cancel', + // time: '2017-09-22T00:24:30.476Z', + // user: 'up106404164', + // c: 'user:up106404164:a:BCH', + // d: 'order:4426728375:a:BCH', + // a: '0.09935956', + // amount: '0.09935956', + // balance: '0.42580261', + // symbol: 'BCH', + // order: '4426728375', + // buy: null, + // sell: null, + // pair: null, + // pos: null, + // cs: '0.42580261', + // ds: 0 } + continue; + } + const tradePrice = this.safeFloat (item, 'price'); + if (tradePrice === undefined) { + // this represents the order + // { + // "a": "0.47000000", + // "c": "user:up106404164:a:EUR", + // "d": "order:6065499239:a:EUR", + // "cs": "1432.93", + // "ds": "476.72", + // "id": "6065499249", + // "buy": null, + // "pos": null, + // "pair": null, + // "sell": null, + // "time": "2018-04-22T13:07:22.152Z", + // "type": "buy", + // "user": "up106404164", + // "order": "6065499239", + // "amount": "-715.97000000", + // "symbol": "EUR", + // "balance": "1432.93000000" } + continue; + } + // todo: deal with these + if (tradeSide === 'costsNothing') { + continue; + } + // -- + // if (side !== tradeSide) + // throw new Error (JSON.stringify (order, null, 2)); + // if (orderId !== item['order']) + // throw new Error (JSON.stringify (order, null, 2)); + // -- + // partial buy trade + // { + // "a": "0.01589885", + // "c": "user:up106404164:a:BTC", + // "d": "order:6065499239:a:BTC", + // "cs": "0.36300000", + // "ds": 0, + // "id": "6067991213", + // "buy": "6065499239", + // "pos": null, + // "pair": null, + // "sell": "6067991206", + // "time": "2018-04-22T23:09:11.773Z", + // "type": "buy", + // "user": "up106404164", + // "order": "6065499239", + // "price": 7146.5, + // "amount": "0.01589885", + // "symbol": "BTC", + // "balance": "0.36300000", + // "symbol2": "EUR", + // "fee_amount": "0.19" } + // -- + // trade with zero amount, but non-zero fee + // { + // "a": "0.00000000", + // "c": "user:up106404164:a:EUR", + // "d": "order:5840654423:a:EUR", + // "cs": 559744, + // "ds": 0, + // "id": "5840654429", + // "buy": "5807238573", + // "pos": null, + // "pair": null, + // "sell": "5840654423", + // "time": "2018-03-15T03:20:14.010Z", + // "type": "sell", + // "user": "up106404164", + // "order": "5840654423", + // "price": 730, + // "amount": "0.00000000", + // "symbol": "EUR", + // "balance": "5597.44000000", + // "symbol2": "BCH", + // "fee_amount": "0.01" } + // -- + // trade which should have an amount of exactly 0.002BTC + // { + // "a": "16.70000000", + // "c": "user:up106404164:a:GBP", + // "d": "order:9927386681:a:GBP", + // "cs": "86.90", + // "ds": 0, + // "id": "9927401610", + // "buy": "9927401601", + // "pos": null, + // "pair": null, + // "sell": "9927386681", + // "time": "2019-08-21T15:25:37.777Z", + // "type": "sell", + // "user": "up106404164", + // "order": "9927386681", + // "price": 8365, + // "amount": "16.70000000", + // "office": "UK", + // "symbol": "GBP", + // "balance": "86.90000000", + // "symbol2": "BTC", + // "fee_amount": "0.03" + // } + const tradeTimestamp = this.parse8601 (this.safeString (item, 'time')); + const tradeAmount = this.safeFloat (item, 'amount'); + const feeCost = this.safeFloat (item, 'fee_amount'); + let absTradeAmount = (tradeAmount < 0) ? -tradeAmount : tradeAmount; + let tradeCost = undefined; + if (tradeSide === 'sell') { + tradeCost = absTradeAmount; + absTradeAmount = this.sum (feeCost, tradeCost) / tradePrice; + } else { + tradeCost = absTradeAmount * tradePrice; + } + trades.push ({ + 'id': this.safeString (item, 'id'), + 'timestamp': tradeTimestamp, + 'datetime': this.iso8601 (tradeTimestamp), + 'order': orderId, + 'symbol': symbol, + 'price': tradePrice, + 'amount': absTradeAmount, + 'cost': tradeCost, + 'side': tradeSide, + 'fee': { + 'cost': feeCost, + 'currency': market['quote'], + }, + 'info': item, + }); + } + } + return { + 'id': orderId, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': (price === undefined) ? 'market' : 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': fee, + 'info': order, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'privatePostOpenOrders'; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + method += 'Pair'; + } + const orders = await this[method] (this.extend (request, params)); + for (let i = 0; i < orders.length; i++) { + orders[i] = this.extend (orders[i], { 'status': 'open' }); + } + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostArchivedOrdersPair'; + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a symbol argument'); + } + const market = this.market (symbol); + const request = { 'pair': market['id'] }; + const response = await this[method] (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id.toString (), + }; + const response = await this.privatePostGetOrderTx (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'limit': limit, + 'pair': market['id'], + 'dateFrom': since, + }; + const response = await this.privatePostArchivedOrdersPair (this.extend (request, params)); + const results = []; + for (let i = 0; i < response.length; i++) { + // cancelled (unfilled): + // { id: '4005785516', + // type: 'sell', + // time: '2017-07-18T19:08:34.223Z', + // lastTxTime: '2017-07-18T19:08:34.396Z', + // lastTx: '4005785522', + // pos: null, + // status: 'c', + // symbol1: 'ETH', + // symbol2: 'GBP', + // amount: '0.20000000', + // price: '200.5625', + // remains: '0.20000000', + // 'a:ETH:cds': '0.20000000', + // tradingFeeMaker: '0', + // tradingFeeTaker: '0.16', + // tradingFeeUserVolumeAmount: '10155061217', + // orderId: '4005785516' } + // -- + // cancelled (partially filled buy): + // { id: '4084911657', + // type: 'buy', + // time: '2017-08-05T03:18:39.596Z', + // lastTxTime: '2019-03-19T17:37:46.404Z', + // lastTx: '8459265833', + // pos: null, + // status: 'cd', + // symbol1: 'BTC', + // symbol2: 'GBP', + // amount: '0.05000000', + // price: '2241.4692', + // tfacf: '1', + // remains: '0.03910535', + // 'tfa:GBP': '0.04', + // 'tta:GBP': '24.39', + // 'a:BTC:cds': '0.01089465', + // 'a:GBP:cds': '112.26', + // 'f:GBP:cds': '0.04', + // tradingFeeMaker: '0', + // tradingFeeTaker: '0.16', + // tradingFeeUserVolumeAmount: '13336396963', + // orderId: '4084911657' } + // -- + // cancelled (partially filled sell): + // { id: '4426728375', + // type: 'sell', + // time: '2017-09-22T00:24:20.126Z', + // lastTxTime: '2017-09-22T00:24:30.476Z', + // lastTx: '4426729543', + // pos: null, + // status: 'cd', + // symbol1: 'BCH', + // symbol2: 'BTC', + // amount: '0.10000000', + // price: '0.11757182', + // tfacf: '1', + // remains: '0.09935956', + // 'tfa:BTC': '0.00000014', + // 'tta:BTC': '0.00007537', + // 'a:BCH:cds': '0.10000000', + // 'a:BTC:cds': '0.00007537', + // 'f:BTC:cds': '0.00000014', + // tradingFeeMaker: '0', + // tradingFeeTaker: '0.18', + // tradingFeeUserVolumeAmount: '3466715450', + // orderId: '4426728375' } + // -- + // filled: + // { id: '5342275378', + // type: 'sell', + // time: '2018-01-04T00:28:12.992Z', + // lastTxTime: '2018-01-04T00:28:12.992Z', + // lastTx: '5342275393', + // pos: null, + // status: 'd', + // symbol1: 'BCH', + // symbol2: 'BTC', + // amount: '0.10000000', + // kind: 'api', + // price: '0.17', + // remains: '0.00000000', + // 'tfa:BTC': '0.00003902', + // 'tta:BTC': '0.01699999', + // 'a:BCH:cds': '0.10000000', + // 'a:BTC:cds': '0.01699999', + // 'f:BTC:cds': '0.00003902', + // tradingFeeMaker: '0.15', + // tradingFeeTaker: '0.23', + // tradingFeeUserVolumeAmount: '1525951128', + // orderId: '5342275378' } + // -- + // market order (buy): + // { "id": "6281946200", + // "pos": null, + // "time": "2018-05-23T11:55:43.467Z", + // "type": "buy", + // "amount": "0.00000000", + // "lastTx": "6281946210", + // "status": "d", + // "amount2": "20.00", + // "orderId": "6281946200", + // "remains": "0.00000000", + // "symbol1": "ETH", + // "symbol2": "EUR", + // "tfa:EUR": "0.05", + // "tta:EUR": "19.94", + // "a:ETH:cds": "0.03764100", + // "a:EUR:cds": "20.00", + // "f:EUR:cds": "0.05", + // "lastTxTime": "2018-05-23T11:55:43.467Z", + // "tradingFeeTaker": "0.25", + // "tradingFeeUserVolumeAmount": "55998097" } + // -- + // market order (sell): + // { "id": "6282200948", + // "pos": null, + // "time": "2018-05-23T12:42:58.315Z", + // "type": "sell", + // "amount": "-0.05000000", + // "lastTx": "6282200958", + // "status": "d", + // "orderId": "6282200948", + // "remains": "0.00000000", + // "symbol1": "ETH", + // "symbol2": "EUR", + // "tfa:EUR": "0.07", + // "tta:EUR": "26.49", + // "a:ETH:cds": "0.05000000", + // "a:EUR:cds": "26.49", + // "f:EUR:cds": "0.07", + // "lastTxTime": "2018-05-23T12:42:58.315Z", + // "tradingFeeTaker": "0.25", + // "tradingFeeUserVolumeAmount": "56294576" } + const order = response[i]; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const baseId = this.safeString (order, 'symbol1'); + const quoteId = this.safeString (order, 'symbol2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const side = this.safeString (order, 'type'); + const baseAmount = this.safeFloat (order, 'a:' + baseId + ':cds'); + const quoteAmount = this.safeFloat (order, 'a:' + quoteId + ':cds'); + const fee = this.safeFloat (order, 'f:' + quoteId + ':cds'); + const amount = this.safeFloat (order, 'amount'); + const price = this.safeFloat (order, 'price'); + const remaining = this.safeFloat (order, 'remains'); + const filled = amount - remaining; + let orderAmount = undefined; + let cost = undefined; + let average = undefined; + let type = undefined; + if (!price) { + type = 'market'; + orderAmount = baseAmount; + cost = quoteAmount; + average = orderAmount / cost; + } else { + const ta = this.safeFloat (order, 'ta:' + quoteId, 0); + const tta = this.safeFloat (order, 'tta:' + quoteId, 0); + const fa = this.safeFloat (order, 'fa:' + quoteId, 0); + const tfa = this.safeFloat (order, 'tfa:' + quoteId, 0); + if (side === 'sell') { + cost = this.sum (this.sum (ta, tta), this.sum (fa, tfa)); + } else { + cost = this.sum (ta, tta) - this.sum (fa, tfa); + } + type = 'limit'; + orderAmount = amount; + average = cost / filled; + } + const time = this.safeString (order, 'time'); + const lastTxTime = this.safeString (order, 'lastTxTime'); + const timestamp = this.parse8601 (time); + results.push ({ + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastUpdated': this.parse8601 (lastTxTime), + 'status': status, + 'symbol': symbol, + 'side': side, + 'price': price, + 'amount': orderAmount, + 'average': average, + 'type': type, + 'filled': filled, + 'cost': cost, + 'remaining': remaining, + 'fee': { + 'cost': fee, + 'currency': quote, + }, + 'info': order, + }); + } + return results; + } + + parseOrderStatus (status) { + return this.safeString (this.options['order']['status'], status, status); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + if (amount === undefined) { + throw new ArgumentsRequired (this.id + ' editOrder requires a amount argument'); + } + if (price === undefined) { + throw new ArgumentsRequired (this.id + ' editOrder requires a price argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // see: https://cex.io/rest-api#/definitions/CancelReplaceOrderRequest + const request = { + 'pair': market['id'], + 'type': side, + 'amount': amount, + 'price': price, + 'order_id': id, + }; + const response = await this.privatePostCancelReplaceOrderPair (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchDepositAddress (code, params = {}) { + if (code === 'XRP' || code === 'XLM') { + // https://github.com/ccxt/ccxt/pull/2327#issuecomment-375204856 + throw new NotSupported (this.id + ' fetchDepositAddress does not support XRP and XLM addresses yet (awaiting docs from CEX.io)'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostGetAddress (this.extend (request, params)); + const address = this.safeString (response, 'data'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.json (this.extend ({ + 'key': this.apiKey, + 'signature': signature.toUpperCase (), + 'nonce': nonce, + }, query)); + headers = { + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (Array.isArray (response)) { + return response; // public endpoints may return []-arrays + } + if (response === undefined) { + throw new NullResponse (this.id + ' returned ' + this.json (response)); + } + if (response === true || response === 'true') { + return; + } + if ('e' in response) { + if ('ok' in response) { + if (response['ok'] === 'ok') { + return; + } + } + } + if ('error' in response) { + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],60:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const foxbit = require ('./foxbit.js'); + +// --------------------------------------------------------------------------- + +module.exports = class chilebit extends foxbit { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'chilebit', + 'name': 'ChileBit', + 'countries': [ 'CL' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991414-1298f0d8-647f-11e7-9c40-d56409266336.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://chilebit.net', + 'doc': 'https://blinktrade.com/docs', + }, + 'options': { + 'brokerId': '9', // https://blinktrade.com/docs/#brokers + }, + }); + } +}; + +},{"./foxbit.js":86}],61:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InvalidAddress, InsufficientFunds, InvalidNonce, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class cobinhood extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'cobinhood', + 'name': 'COBINHOOD', + 'countries': [ 'TW' ], + 'rateLimit': 1000 / 10, + 'version': 'v1', + 'has': { + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrderTrades': true, + 'fetchOrder': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'withdraw': true, + 'fetchMyTrades': true, + 'editOrder': true, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + }, + 'timeframes': { + // the first two don't seem to work at all + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '3h': '3h', + '6h': '6h', + '12h': '12h', + '1d': '1D', + '1w': '7D', + '2w': '14D', + '1M': '1M', + }, + 'urls': { + 'referral': 'https://cobinhood.com?referrerId=a9d57842-99bb-4d7c-b668-0479a15a458b', + 'logo': 'https://user-images.githubusercontent.com/1294454/35755576-dee02e5c-0878-11e8-989f-1595d80ba47f.jpg', + 'api': 'https://api.cobinhood.com', + 'www': 'https://cobinhood.com', + 'doc': 'https://cobinhood.github.io/api-public', + }, + 'api': { + 'system': { + 'get': [ + 'info', + 'time', + 'messages', + 'messages/{message_id}', + ], + }, + 'admin': { + 'get': [ + 'system/messages', + 'system/messages/{message_id}', + ], + 'post': [ + 'system/messages', + ], + 'patch': [ + 'system/messages/{message_id}', + ], + 'delete': [ + 'system/messages/{message_id}', + ], + }, + 'public': { + 'get': [ + 'market/fundingbook/precisions/{currency_id}', + 'market/fundingbooks/{currency_id}', + 'market/tickers', + 'market/currencies', + 'market/quote_currencies', + 'market/trading_pairs', + 'market/orderbook/precisions/{trading_pair_id}', + 'market/orderbooks/{trading_pair_id}', + 'market/stats', + 'market/tickers', // fetchTickers + 'market/tickers/{trading_pair_id}', + 'market/trades/{trading_pair_id}', + 'market/trades_history/{trading_pair_id}', + 'market/trading_pairs', + 'chart/candles/{trading_pair_id}', + 'system/time', + ], + }, + 'private': { + 'get': [ + 'funding/auto_offerings', + 'funding/auto_offerings/{currency_id}', + 'funding/funding_history', + 'funding/fundings', + 'funding/loans', + 'funding/loans/{loan_id}', + 'trading/orders/{order_id}', + 'trading/orders/{order_id}/trades', + 'trading/orders', + 'trading/order_history', + 'trading/positions', + 'trading/positions/{trading_pair_id}', + 'trading/positions/{trading_pair_id}/claimable_size', + 'trading/trades', + 'trading/trades/{trade_id}', + 'trading/volume', + 'wallet/balances', + 'wallet/ledger', + 'wallet/limits/withdrawal', + 'wallet/generic_deposits', + 'wallet/generic_deposits/{generic_deposit_id}', + 'wallet/generic_withdrawals', + 'wallet/generic_withdrawals/{generic_withdrawal_id}', + // older endpoints + 'wallet/deposit_addresses', + 'wallet/deposit_addresses/iota', + 'wallet/withdrawal_addresses', + 'wallet/withdrawal_frozen', + 'wallet/withdrawals/{withdrawal_id}', + 'wallet/withdrawals', + 'wallet/deposits/{deposit_id}', + 'wallet/deposits', + ], + 'patch': [ + 'trading/positions/{trading_pair_id}', + ], + 'post': [ + 'funding/auto_offerings', + 'funding/fundings', + 'trading/check_order', + 'trading/orders', + // older endpoints + 'wallet/deposit_addresses', + 'wallet/transfer', + 'wallet/withdrawal_addresses', + 'wallet/withdrawals', + 'wallet/withdrawals/fee', + ], + 'put': [ + 'funding/fundings/{funding_id}', + 'trading/orders/{order_id}', + ], + 'delete': [ + 'funding/auto_offerings/{currency_id}', + 'funding/fundings/{funding_id}', + 'funding/loans/{loan_id}', + 'trading/orders/{order_id}', + 'trading/positions/{trading_pair_id}', + 'wallet/generic_withdrawals/{generic_withdrawal_id}', + 'wallet/withdrawal_addresses/{wallet_id}', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.0, + 'taker': 0.0, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'exceptions': { + 'insufficient_balance': InsufficientFunds, + 'invalid_order_size': InvalidOrder, + 'invalid_nonce': InvalidNonce, + 'unauthorized_scope': PermissionDenied, + 'invalid_address': InvalidAddress, + 'parameter_error': OrderNotFound, + }, + 'commonCurrencies': { + 'SMT': 'SocialMedia.Market', + 'MTN': 'Motion Token', + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetMarketCurrencies (params); + const currencies = response['result']['currencies']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'currency'); + const name = this.safeString (currency, 'name'); + const code = this.safeCurrencyCode (id); + const minUnit = this.safeFloat (currency, 'min_unit'); + result[code] = { + 'id': id, + 'code': code, + 'name': name, + 'active': true, + 'fiat': false, + 'precision': this.precisionFromString (currency['min_unit']), + 'limits': { + 'amount': { + 'min': minUnit, + 'max': undefined, + }, + 'price': { + 'min': minUnit, + 'max': undefined, + }, + 'deposit': { + 'min': minUnit, + 'max': undefined, + }, + 'withdraw': { + 'min': minUnit, + 'max': undefined, + }, + }, + 'funding': { + 'withdraw': { + 'fee': this.safeFloat (currency, 'withdrawal_fee'), + }, + 'deposit': { + 'fee': this.safeFloat (currency, 'deposit_fee'), + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketTradingPairs (params); + const markets = this.safeValue (response['result'], 'trading_pairs'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const [ baseId, quoteId ] = id.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': this.precisionFromString (market['quote_increment']), + }; + const active = this.safeValue (market, 'is_active', true); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'base_min_size'), + 'max': this.safeFloat (market, 'base_max_size'), + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'trading_pair_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last_trade_price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, '24h_high'), + 'low': this.safeFloat (ticker, '24h_low'), + 'bid': this.safeFloat (ticker, 'highest_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowest_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'percentChanged24hr'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, '24h_volume'), + 'quoteVolume': this.safeFloat (ticker, 'quote_volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair_id': market['id'], + }; + const response = await this.publicGetMarketTickersTradingPairId (this.extend (request, params)); + const ticker = this.safeValue (response['result'], 'ticker'); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketTickers (params); + const tickers = this.safeValue (response['result'], 'tickers'); + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.indexBy (result, 'symbol'); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'trading_pair_id': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit'] = limit; // 100 + } + const response = await this.publicGetMarketOrderbooksTradingPairId (this.extend (request, params)); + return this.parseOrderBook (response['result']['orderbook'], undefined, 'bids', 'asks', 0, 2); + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + // you can't determine your side from maker/taker side and vice versa + // you can't determine if your order/trade was a maker or a taker based + // on just the side of your order/trade + // https://github.com/ccxt/ccxt/issues/4300 + // let side = (trade['maker_side'] === 'bid') ? 'sell' : 'buy'; + const side = undefined; + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair_id': market['id'], + 'limit': limit, // default 20, but that seems too little + }; + const response = await this.publicGetMarketTradesTradingPairId (this.extend (request, params)); + const trades = this.safeValue (response['result'], 'trades'); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + // they say that timestamps are Unix Timestamps in seconds, but in fact those are milliseconds + ohlcv['timestamp'], + parseFloat (ohlcv['open']), + parseFloat (ohlcv['high']), + parseFloat (ohlcv['low']), + parseFloat (ohlcv['close']), + parseFloat (ohlcv['volume']), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // + // they say in their docs that end_time defaults to current server time + // but if you don't specify it, their range limits does not allow you to query anything + // + // they also say that start_time defaults to 0, + // but most calls fail if you do not specify any of end_time + // + // to make things worse, their docs say it should be a Unix Timestamp + // but with seconds it fails, so we set milliseconds (somehow it works that way) + // + const endTime = this.milliseconds (); + const request = { + 'trading_pair_id': market['id'], + 'timeframe': this.timeframes[timeframe], + 'end_time': endTime, + }; + if (since !== undefined) { + request['start_time'] = since; + } + const response = await this.publicGetChartCandlesTradingPairId (this.extend (request, params)); + const ohlcv = this.safeValue (response['result'], 'candles'); + return this.parseOHLCVs (ohlcv, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletBalances (params); + const result = { 'info': response }; + const balances = this.safeValue (response['result'], 'balances'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'on_order'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'filled': 'closed', + 'rejected': 'closed', + 'partially_filled': 'open', + 'pending_cancellation': 'open', + 'pending_modification': 'open', + 'open': 'open', + 'new': 'open', + 'queued': 'open', + 'cancelled': 'canceled', + 'triggered': 'triggered', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // 'completed_at': None, + // 'eq_price': '0', + // 'filled': '0', + // 'id': '88426800-beae-4407-b4a1-f65cef693542', + // 'price': '0.00000507', + // 'side': 'bid', + // 'size': '3503.6489', + // 'source': 'exchange', + // 'state': 'open', + // 'timestamp': 1535258403597, + // 'trading_pair_id': 'ACT-BTC', + // 'type': 'limit', + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString2 (order, 'trading_pair', 'trading_pair_id'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'timestamp'); + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'eq_price'); + const amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat (order, 'filled'); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined && average !== undefined) { + cost = average * filled; + } else if (average !== undefined) { + cost = average * amount; + } + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let side = this.safeString (order, 'side'); + if (side === 'bid') { + side = 'buy'; + } else if (side === 'ask') { + side = 'sell'; + } + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': this.safeString (order, 'type'), // market, limit, stop, stop_limit, trailing_stop, fill_or_kill + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + side = (side === 'sell') ? 'ask' : 'bid'; + const request = { + 'trading_pair_id': market['id'], + 'type': type, // market, limit, stop, stop_limit + 'side': side, + 'size': this.amountToPrecision (symbol, amount), + }; + if (type !== 'market') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostTradingOrders (this.extend (request, params)); + const order = this.parseOrder (response['result']['order'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async editOrder (id, symbol, type, side, amount, price, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + 'price': this.priceToPrecision (symbol, price), + 'size': this.amountToPrecision (symbol, amount), + }; + const response = await this.privatePutTradingOrdersOrderId (this.extend (request, params)); + return this.parseOrder (this.extend (response, { + 'id': id, + })); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateDeleteTradingOrdersOrderId (this.extend (request, params)); + return this.parseOrder (this.extend (response, { + 'id': id, + })); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id.toString (), + }; + const response = await this.privateGetTradingOrdersOrderId (this.extend (request, params)); + return this.parseOrder (response['result']['order']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const result = await this.privateGetTradingOrders (params); + const orders = this.parseOrders (result['result']['orders'], undefined, since, limit); + if (symbol !== undefined) { + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + return this.filterBySinceLimit (orders, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['trading_pair_id'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 50, max 100 + } + const response = await this.privateGetTradingOrderHistory (this.extend (request, params)); + const orders = this.parseOrders (response['result']['orders'], market, since, limit); + if (symbol !== undefined) { + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + return this.filterBySinceLimit (orders, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateGetTradingOrdersOrderIdTrades (this.extend (request, params)); + const market = (symbol === undefined) ? undefined : this.market (symbol); + return this.parseTrades (response['result']['trades'], market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + if (symbol !== undefined) { + request['trading_pair_id'] = market['id']; + } + const response = await this.privateGetTradingTrades (this.extend (request, params)); + return this.parseTrades (response['result']['trades'], market, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + // 'ledger_type' is required, see: https://cobinhood.github.io/api-public/#create-new-deposit-address + const ledgerType = this.safeString (params, 'ledger_type', 'exchange'); + const request = { + 'currency': currency['id'], + 'ledger_type': ledgerType, + }; + const response = await this.privatePostWalletDepositAddresses (this.extend (request, params)); + const address = this.safeString (response['result']['deposit_address'], 'address'); + const tag = this.safeString (response['result']['deposit_address'], 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetWalletDepositAddresses (this.extend (request, params)); + // + // { success: true, + // result: { deposit_addresses: [ { address: "abcdefg", + // blockchain_id: "eosio", + // created_at: 1536768050235, + // currency: "EOS", + // memo: "12345678", + // type: "exchange" } ] } } + // + const addresses = this.safeValue (response['result'], 'deposit_addresses', []); + let address = undefined; + let tag = undefined; + if (addresses.length > 0) { + address = this.safeString (addresses[0], 'address'); + tag = this.safeString2 (addresses[0], 'memo', 'tag'); + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const response = await this.privatePostWalletWithdrawals (this.extend (request, params)); + return { + 'id': undefined, + 'info': response, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ' fetchDeposits() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetWalletDeposits (this.extend (request, params)); + return this.parseTransactions (response['result']['deposits'], currency); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ' fetchWithdrawals() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetWalletWithdrawals (this.extend (request, params)); + return this.parseTransactions (response['result']['withdrawals'], currency); + } + + parseTransactionStatus (status) { + const statuses = { + 'tx_pending_two_factor_auth': 'pending', + 'tx_pending_email_auth': 'pending', + 'tx_pending_approval': 'pending', + 'tx_approved': 'pending', + 'tx_processing': 'pending', + 'tx_pending': 'pending', + 'tx_sent': 'pending', + 'tx_cancelled': 'canceled', + 'tx_timeout': 'failed', + 'tx_invalid': 'failed', + 'tx_rejected': 'failed', + 'tx_confirmed': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + const timestamp = this.safeInteger (transaction, 'created_at'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let id = undefined; + const withdrawalId = this.safeString (transaction, 'withdrawal_id'); + const depositId = this.safeString (transaction, 'deposit_id'); + let type = undefined; + let address = undefined; + if (withdrawalId !== undefined) { + type = 'withdrawal'; + id = withdrawalId; + address = this.safeString (transaction, 'to_address'); + } else if (depositId !== undefined) { + type = 'deposit'; + id = depositId; + address = this.safeString (transaction, 'from_address'); + } + const additionalInfo = this.safeValue (transaction, 'additional_info', {}); + const tag = this.safeString (additionalInfo, 'memo'); + return { + 'info': transaction, + 'id': id, + 'txid': this.safeString (transaction, 'txhash'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, // refix it properly + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': this.parseTransactionStatus (transaction['status']), + 'updated': undefined, + 'fee': { + 'cost': this.safeFloat (transaction, 'fee'), + 'rate': undefined, + }, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + headers = {}; + if (api === 'private') { + this.checkRequiredCredentials (); + // headers['device_id'] = this.apiKey; + headers['nonce'] = this.nonce ().toString (); + headers['Authorization'] = this.apiKey; + } + if (method === 'GET') { + query = this.urlencode (query); + if (query.length) { + url += '?' + query; + } + } else { + headers['Content-type'] = 'application/json; charset=UTF-8'; + body = this.json (query); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code < 400 || code >= 600) { + return; + } + if (body[0] !== '{') { + throw new ExchangeError (this.id + ' ' + body); + } + const feedback = this.id + ' ' + this.json (response); + const errorCode = this.safeValue (response['error'], 'error_code'); + if (method === 'DELETE' || method === 'GET') { + if (errorCode === 'parameter_error') { + if (url.indexOf ('trading/orders/') >= 0) { + // Cobinhood returns vague "parameter_error" on fetchOrder() and cancelOrder() calls + // for invalid order IDs as well as orders that are not "open" + throw new InvalidOrder (feedback); + } + } + } + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + + nonce () { + return this.milliseconds (); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],62:[function(require,module,exports){ +'use strict'; + +// ---------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, RateLimitExceeded } = require ('./base/errors'); + +// ---------------------------------------------------------------------------- + +module.exports = class coinbase extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinbase', + 'name': 'Coinbase', + 'countries': [ 'US' ], + 'rateLimit': 400, // 10k calls per hour + 'version': 'v2', + 'userAgent': this.userAgents['chrome'], + 'headers': { + 'CB-VERSION': '2018-05-30', + }, + 'has': { + 'CORS': true, + 'cancelOrder': false, + 'createDepositAddress': true, + 'createOrder': false, + 'deposit': false, + 'fetchBalance': true, + 'fetchClosedOrders': false, + 'fetchCurrencies': true, + 'fetchDepositAddress': false, + 'fetchMarkets': true, + 'fetchMyTrades': false, + 'fetchOHLCV': false, + 'fetchOpenOrders': false, + 'fetchOrder': false, + 'fetchOrderBook': false, + 'fetchL2OrderBook': false, + 'fetchLedger': true, + 'fetchOrders': false, + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchTime': true, + 'fetchBidsAsks': false, + 'fetchTrades': false, + 'withdraw': false, + 'fetchTransactions': false, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchMySells': true, + 'fetchMyBuys': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/40811661-b6eceae2-653a-11e8-829e-10bfadb078cf.jpg', + 'api': 'https://api.coinbase.com', + 'www': 'https://www.coinbase.com', + 'doc': 'https://developers.coinbase.com/api/v2', + 'fees': 'https://support.coinbase.com/customer/portal/articles/2109597-buy-sell-bank-transfer-fees', + 'referral': 'https://www.coinbase.com/join/58cbe25a355148797479dbd2', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'time', + 'exchange-rates', + 'users/{user_id}', + 'prices/{symbol}/buy', + 'prices/{symbol}/sell', + 'prices/{symbol}/spot', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/{account_id}', + 'accounts/{account_id}/addresses', + 'accounts/{account_id}/addresses/{address_id}', + 'accounts/{account_id}/addresses/{address_id}/transactions', + 'accounts/{account_id}/transactions', + 'accounts/{account_id}/transactions/{transaction_id}', + 'accounts/{account_id}/buys', + 'accounts/{account_id}/buys/{buy_id}', + 'accounts/{account_id}/sells', + 'accounts/{account_id}/sells/{sell_id}', + 'accounts/{account_id}/deposits', + 'accounts/{account_id}/deposits/{deposit_id}', + 'accounts/{account_id}/withdrawals', + 'accounts/{account_id}/withdrawals/{withdrawal_id}', + 'payment-methods', + 'payment-methods/{payment_method_id}', + 'user', + 'user/auth', + ], + 'post': [ + 'accounts', + 'accounts/{account_id}/primary', + 'accounts/{account_id}/addresses', + 'accounts/{account_id}/transactions', + 'accounts/{account_id}/transactions/{transaction_id}/complete', + 'accounts/{account_id}/transactions/{transaction_id}/resend', + 'accounts/{account_id}/buys', + 'accounts/{account_id}/buys/{buy_id}/commit', + 'accounts/{account_id}/sells', + 'accounts/{account_id}/sells/{sell_id}/commit', + 'accounts/{account_id}/deposists', + 'accounts/{account_id}/deposists/{deposit_id}/commit', + 'accounts/{account_id}/withdrawals', + 'accounts/{account_id}/withdrawals/{withdrawal_id}/commit', + ], + 'put': [ + 'accounts/{account_id}', + 'user', + ], + 'delete': [ + 'accounts/{id}', + 'accounts/{account_id}/transactions/{transaction_id}', + ], + }, + }, + 'exceptions': { + 'two_factor_required': AuthenticationError, // 402 When sending money over 2fa limit + 'param_required': ExchangeError, // 400 Missing parameter + 'validation_error': ExchangeError, // 400 Unable to validate POST/PUT + 'invalid_request': ExchangeError, // 400 Invalid request + 'personal_details_required': AuthenticationError, // 400 User’s personal detail required to complete this request + 'identity_verification_required': AuthenticationError, // 400 Identity verification is required to complete this request + 'jumio_verification_required': AuthenticationError, // 400 Document verification is required to complete this request + 'jumio_face_match_verification_required': AuthenticationError, // 400 Document verification including face match is required to complete this request + 'unverified_email': AuthenticationError, // 400 User has not verified their email + 'authentication_error': AuthenticationError, // 401 Invalid auth (generic) + 'invalid_token': AuthenticationError, // 401 Invalid Oauth token + 'revoked_token': AuthenticationError, // 401 Revoked Oauth token + 'expired_token': AuthenticationError, // 401 Expired Oauth token + 'invalid_scope': AuthenticationError, // 403 User hasn’t authenticated necessary scope + 'not_found': ExchangeError, // 404 Resource not found + 'rate_limit_exceeded': RateLimitExceeded, // 429 Rate limit exceeded + 'internal_server_error': ExchangeError, // 500 Internal server error + }, + 'options': { + 'fetchCurrencies': { + 'expires': 5000, + }, + 'accounts': [ + 'wallet', + 'fiat', + // 'vault', + ], + }, + }); + } + + async fetchTime (params = {}) { + const response = await this.publicGetTime (params); + const data = this.safeValue (response, 'data', {}); + return this.parse8601 (this.safeString (data, 'iso')); + } + + async fetchAccounts (params = {}) { + const response = await this.privateGetAccounts (params); + // + // { + // "id": "XLM", + // "name": "XLM Wallet", + // "primary": false, + // "type": "wallet", + // "currency": { + // "code": "XLM", + // "name": "Stellar Lumens", + // "color": "#000000", + // "sort_index": 127, + // "exponent": 7, + // "type": "crypto", + // "address_regex": "^G[A-Z2-7]{55}$", + // "asset_id": "13b83335-5ede-595b-821e-5bcdfa80560f", + // "destination_tag_name": "XLM Memo ID", + // "destination_tag_regex": "^[ -~]{1,28}$" + // }, + // "balance": { + // "amount": "0.0000000", + // "currency": "XLM" + // }, + // "created_at": null, + // "updated_at": null, + // "resource": "account", + // "resource_path": "/v2/accounts/XLM", + // "allow_deposits": true, + // "allow_withdrawals": true + // } + // + const data = this.safeValue (response, 'data', []); + const result = []; + for (let i = 0; i < data.length; i++) { + const account = data[i]; + const currency = this.safeValue (account, 'currency', {}); + const currencyId = this.safeString (currency, 'code'); + const code = this.safeCurrencyCode (currencyId); + result.push ({ + 'id': this.safeString (account, 'id'), + 'type': this.safeString (account, 'type'), + 'code': code, + 'info': account, + }); + } + return result; + } + + async createDepositAddress (code, params = {}) { + let accountId = this.safeString (params, 'account_id'); + params = this.omit (params, 'account_id'); + if (accountId === undefined) { + await this.loadAccounts (); + for (let i = 0; i < this.accounts.length; i++) { + const account = this.accounts[i]; + if (account['code'] === code && account['type'] === 'wallet') { + accountId = account['id']; + break; + } + } + } + if (accountId === undefined) { + throw new ExchangeError (this.id + ' createDepositAddress could not find the account with matching currency code, specify an `account_id` extra param'); + } + const request = { + 'account_id': accountId, + }; + const response = await this.privatePostAccountsAccountIdAddresses (this.extend (request, params)); + // + // { + // "data": { + // "id": "05b1ebbf-9438-5dd4-b297-2ddedc98d0e4", + // "address": "coinbasebase", + // "address_info": { + // "address": "coinbasebase", + // "destination_tag": "287594668" + // }, + // "name": null, + // "created_at": "2019-07-01T14:39:29Z", + // "updated_at": "2019-07-01T14:39:29Z", + // "network": "eosio", + // "uri_scheme": "eosio", + // "resource": "address", + // "resource_path": "/v2/accounts/14cfc769-e852-52f3-b831-711c104d194c/addresses/05b1ebbf-9438-5dd4-b297-2ddedc98d0e4", + // "warnings": [ + // { + // "title": "Only send EOS (EOS) to this address", + // "details": "Sending any other cryptocurrency will result in permanent loss.", + // "image_url": "https://dynamic-assets.coinbase.com/deaca3d47b10ed4a91a872e9618706eec34081127762d88f2476ac8e99ada4b48525a9565cf2206d18c04053f278f693434af4d4629ca084a9d01b7a286a7e26/asset_icons/1f8489bb280fb0a0fd643c1161312ba49655040e9aaaced5f9ad3eeaf868eadc.png" + // }, + // { + // "title": "Both an address and EOS memo are required to receive EOS", + // "details": "If you send funds without an EOS memo or with an incorrect EOS memo, your funds cannot be credited to your account.", + // "image_url": "https://www.coinbase.com/assets/receive-warning-2f3269d83547a7748fb39d6e0c1c393aee26669bfea6b9f12718094a1abff155.png" + // } + // ], + // "warning_title": "Only send EOS (EOS) to this address", + // "warning_details": "Sending any other cryptocurrency will result in permanent loss.", + // "destination_tag": "287594668", + // "deposit_uri": "eosio:coinbasebase?dt=287594668", + // "callback_url": null + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const tag = this.safeString (data, 'destination_tag'); + const address = this.safeString (data, 'address'); + return { + 'currency': code, + 'tag': tag, + 'address': address, + 'info': response, + }; + } + + async fetchMySells (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // they don't have an endpoint for all historical trades + const request = await this.prepareAccountRequest (limit, params); + await this.loadMarkets (); + const query = this.omit (params, [ 'account_id', 'accountId' ]); + const sells = await this.privateGetAccountsAccountIdSells (this.extend (request, query)); + return this.parseTrades (sells['data'], undefined, since, limit); + } + + async fetchMyBuys (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // they don't have an endpoint for all historical trades + const request = await this.prepareAccountRequest (limit, params); + await this.loadMarkets (); + const query = this.omit (params, [ 'account_id', 'accountId' ]); + const buys = await this.privateGetAccountsAccountIdBuys (this.extend (request, query)); + return this.parseTrades (buys['data'], undefined, since, limit); + } + + async fetchTransactionsWithMethod (method, code = undefined, since = undefined, limit = undefined, params = {}) { + const request = await this.prepareAccountRequestWithCurrencyCode (code, limit, params); + await this.loadMarkets (); + const query = this.omit (params, [ 'account_id', 'accountId' ]); + const response = await this[method] (this.extend (request, query)); + return this.parseTransactions (response['data'], undefined, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + // fiat only, for crypto transactions use fetchLedger + return await this.fetchTransactionsWithMethod ('privateGetAccountsAccountIdWithdrawals', code, since, limit, params); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + // fiat only, for crypto transactions use fetchLedger + return await this.fetchTransactionsWithMethod ('privateGetAccountsAccountIdDeposits', code, since, limit, params); + } + + parseTransactionStatus (status) { + const statuses = { + 'created': 'pending', + 'completed': 'ok', + 'canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, market = undefined) { + // + // fiat deposit + // + // { + // "id": "f34c19f3-b730-5e3d-9f72", + // "status": "completed", + // "payment_method": { + // "id": "a022b31d-f9c7-5043-98f2", + // "resource": "payment_method", + // "resource_path": "/v2/payment-methods/a022b31d-f9c7-5043-98f2" + // }, + // "transaction": { + // "id": "04ed4113-3732-5b0c-af86-b1d2146977d0", + // "resource": "transaction", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/04ed4113-3732-5b0c-af86" + // }, + // "user_reference": "2VTYTH", + // "created_at": "2017-02-09T07:01:18Z", + // "updated_at": "2017-02-09T07:01:26Z", + // "resource": "deposit", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/deposits/f34c19f3-b730-5e3d-9f72", + // "committed": true, + // "payout_at": "2017-02-12T07:01:17Z", + // "instant": false, + // "fee": { "amount": "0.00", "currency": "EUR" }, + // "amount": { "amount": "114.02", "currency": "EUR" }, + // "subtotal": { "amount": "114.02", "currency": "EUR" }, + // "hold_until": null, + // "hold_days": 0, + // "hold_business_days": 0, + // "next_step": null + // } + // + // fiat_withdrawal + // + // { + // "id": "cfcc3b4a-eeb6-5e8c-8058", + // "status": "completed", + // "payment_method": { + // "id": "8b94cfa4-f7fd-5a12-a76a", + // "resource": "payment_method", + // "resource_path": "/v2/payment-methods/8b94cfa4-f7fd-5a12-a76a" + // }, + // "transaction": { + // "id": "fcc2550b-5104-5f83-a444", + // "resource": "transaction", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/fcc2550b-5104-5f83-a444" + // }, + // "user_reference": "MEUGK", + // "created_at": "2018-07-26T08:55:12Z", + // "updated_at": "2018-07-26T08:58:18Z", + // "resource": "withdrawal", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/withdrawals/cfcc3b4a-eeb6-5e8c-8058", + // "committed": true, + // "payout_at": "2018-07-31T08:55:12Z", + // "instant": false, + // "fee": { "amount": "0.15", "currency": "EUR" }, + // "amount": { "amount": "13130.69", "currency": "EUR" }, + // "subtotal": { "amount": "13130.84", "currency": "EUR" }, + // "idem": "e549dee5-63ed-4e79-8a96", + // "next_step": null + // } + // + const subtotalObject = this.safeValue (transaction, 'subtotal', {}); + const feeObject = this.safeValue (transaction, 'fee', {}); + const id = this.safeString (transaction, 'id'); + const timestamp = this.parse8601 (this.safeValue (transaction, 'created_at')); + const updated = this.parse8601 (this.safeValue (transaction, 'updated_at')); + const type = this.safeString (transaction, 'resource'); + const amount = this.safeFloat (subtotalObject, 'amount'); + const currencyId = this.safeString (subtotalObject, 'currency'); + const currency = this.safeCurrencyCode (currencyId); + const feeCost = this.safeFloat (feeObject, 'amount'); + const feeCurrencyId = this.safeString (feeObject, 'currency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + let status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + if (status === undefined) { + const committed = this.safeValue (transaction, 'committed'); + status = committed ? 'ok' : 'pending'; + } + return { + 'info': transaction, + 'id': id, + 'txid': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': undefined, + 'tag': undefined, + 'type': type, + 'amount': amount, + 'currency': currency, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + parseTrade (trade, market = undefined) { + // + // { + // "id": "67e0eaec-07d7-54c4-a72c-2e92826897df", + // "status": "completed", + // "payment_method": { + // "id": "83562370-3e5c-51db-87da-752af5ab9559", + // "resource": "payment_method", + // "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" + // }, + // "transaction": { + // "id": "441b9494-b3f0-5b98-b9b0-4d82c21c252a", + // "resource": "transaction", + // "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" + // }, + // "amount": { "amount": "1.00000000", "currency": "BTC" }, + // "total": { "amount": "10.25", "currency": "USD" }, + // "subtotal": { "amount": "10.10", "currency": "USD" }, + // "created_at": "2015-01-31T20:49:02Z", + // "updated_at": "2015-02-11T16:54:02-08:00", + // "resource": "buy", + // "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/67e0eaec-07d7-54c4-a72c-2e92826897df", + // "committed": true, + // "instant": false, + // "fee": { "amount": "0.15", "currency": "USD" }, + // "payout_at": "2015-02-18T16:54:00-08:00" + // } + // + let symbol = undefined; + const totalObject = this.safeValue (trade, 'total', {}); + const amountObject = this.safeValue (trade, 'amount', {}); + const subtotalObject = this.safeValue (trade, 'subtotal', {}); + const feeObject = this.safeValue (trade, 'fee', {}); + const id = this.safeString (trade, 'id'); + const timestamp = this.parse8601 (this.safeValue (trade, 'created_at')); + if (market === undefined) { + const baseId = this.safeString (totalObject, 'currency'); + const quoteId = this.safeString (amountObject, 'currency'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const orderId = undefined; + const side = this.safeString (trade, 'resource'); + const type = undefined; + const cost = this.safeFloat (subtotalObject, 'amount'); + const amount = this.safeFloat (amountObject, 'amount'); + let price = undefined; + if (cost !== undefined) { + if (amount !== undefined) { + price = cost / amount; + } + } + const feeCost = this.safeFloat (feeObject, 'amount'); + const feeCurrencyId = this.safeString (feeObject, 'currency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + const currencies = this.safeValue (response, 'currencies', {}); + const exchangeRates = this.safeValue (response, 'exchangeRates', {}); + const data = this.safeValue (currencies, 'data', []); + const dataById = this.indexBy (data, 'id'); + const rates = this.safeValue (this.safeValue (exchangeRates, 'data', {}), 'rates', {}); + const baseIds = Object.keys (rates); + const result = []; + for (let i = 0; i < baseIds.length; i++) { + const baseId = baseIds[i]; + const base = this.safeCurrencyCode (baseId); + const type = (baseId in dataById) ? 'fiat' : 'crypto'; + // https://github.com/ccxt/ccxt/issues/6066 + if (type === 'crypto') { + for (let j = 0; j < data.length; j++) { + const quoteCurrency = data[j]; + const quoteId = this.safeString (quoteCurrency, 'id'); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = baseId + '-' + quoteId; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': quoteCurrency, + 'precision': { + 'amount': undefined, + 'price': undefined, + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (quoteCurrency, 'min_size'), + 'max': undefined, + }, + }, + }); + } + } + } + return result; + } + + async fetchCurrenciesFromCache (params = {}) { + const options = this.safeValue (this.options, 'fetchCurrencies', {}); + const timestamp = this.safeInteger (options, 'timestamp'); + const expires = this.safeInteger (options, 'expires', 1000); + const now = this.milliseconds (); + if ((timestamp === undefined) || ((now - timestamp) > expires)) { + const currencies = await this.publicGetCurrencies (params); + const exchangeRates = await this.publicGetExchangeRates (params); + this.options['fetchCurrencies'] = this.extend (options, { + 'currencies': currencies, + 'exchangeRates': exchangeRates, + 'timestamp': now, + }); + } + return this.safeValue (this.options, 'fetchCurrencies', {}); + } + + async fetchCurrencies (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + const currencies = this.safeValue (response, 'currencies', {}); + // + // { + // "data":[ + // {"id":"AED","name":"United Arab Emirates Dirham","min_size":"0.01000000"}, + // {"id":"AFN","name":"Afghan Afghani","min_size":"0.01000000"}, + // {"id":"ALL","name":"Albanian Lek","min_size":"0.01000000"}, + // {"id":"AMD","name":"Armenian Dram","min_size":"0.01000000"}, + // {"id":"ANG","name":"Netherlands Antillean Gulden","min_size":"0.01000000"}, + // // ... + // ], + // } + // + const exchangeRates = this.safeValue (response, 'exchangeRates', {}); + // + // { + // "data":{ + // "currency":"USD", + // "rates":{ + // "AED":"3.67", + // "AFN":"78.21", + // "ALL":"110.42", + // "AMD":"474.18", + // "ANG":"1.75", + // // ... + // }, + // } + // } + // + const data = this.safeValue (currencies, 'data', []); + const dataById = this.indexBy (data, 'id'); + const rates = this.safeValue (this.safeValue (exchangeRates, 'data', {}), 'rates', {}); + const keys = Object.keys (rates); + const result = {}; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const type = (key in dataById) ? 'fiat' : 'crypto'; + const currency = this.safeValue (dataById, key, {}); + const id = this.safeString (currency, 'id', key); + const name = this.safeString (currency, 'name'); + const code = this.safeCurrencyCode (id); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, // the original payload + 'type': type, + 'name': name, + 'active': true, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'min_size'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const timestamp = this.seconds (); + const market = this.market (symbol); + const request = this.extend ({ + 'symbol': market['id'], + }, params); + const buy = await this.publicGetPricesSymbolBuy (request); + const sell = await this.publicGetPricesSymbolSell (request); + const spot = await this.publicGetPricesSymbolSpot (request); + const ask = this.safeFloat (buy['data'], 'amount'); + const bid = this.safeFloat (sell['data'], 'amount'); + const last = this.safeFloat (spot['data'], 'amount'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'bid': bid, + 'ask': ask, + 'last': last, + 'high': undefined, + 'low': undefined, + 'bidVolume': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': undefined, + 'info': { + 'buy': buy, + 'sell': sell, + 'spot': spot, + }, + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + const balances = this.safeValue (response, 'data'); + const accounts = this.safeValue (params, 'type', this.options['accounts']); + const result = { 'info': response }; + for (let b = 0; b < balances.length; b++) { + const balance = balances[b]; + if (this.inArray (balance['type'], accounts)) { + const currencyId = this.safeString (balance['balance'], 'currency'); + const code = this.safeCurrencyCode (currencyId); + const total = this.safeFloat (balance['balance'], 'amount'); + const free = total; + const used = undefined; + if (code in result) { + result[code]['free'] = this.sum (result[code]['free'], total); + result[code]['total'] = this.sum (result[code]['total'], total); + } else { + const account = { + 'free': free, + 'used': used, + 'total': total, + }; + result[code] = account; + } + } + } + return this.parseBalance (result); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = await this.prepareAccountRequestWithCurrencyCode (code, limit, params); + const query = this.omit (params, ['account_id', 'accountId']); + // for pagination use parameter 'starting_after' + // the value for the next page can be obtained from the result of the previous call in the 'pagination' field + // eg: instance.last_json_response.pagination.next_starting_after + const response = await this.privateGetAccountsAccountIdTransactions (this.extend (request, query)); + return this.parseLedger (response['data'], undefined, since, limit); + } + + parseLedgerEntryStatus (status) { + const types = { + 'completed': 'ok', + }; + return this.safeString (types, status, status); + } + + parseLedgerEntryType (type) { + const types = { + 'buy': 'trade', + 'sell': 'trade', + 'fiat_deposit': 'transaction', + 'fiat_withdrawal': 'transaction', + 'exchange_deposit': 'transaction', // fiat withdrawal (from coinbase to coinbasepro) + 'exchange_withdrawal': 'transaction', // fiat deposit (to coinbase from coinbasepro) + 'send': 'transaction', // crypto deposit OR withdrawal + 'pro_deposit': 'transaction', // crypto withdrawal (from coinbase to coinbasepro) + 'pro_withdrawal': 'transaction', // crypto deposit (to coinbase from coinbasepro) + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // crypto deposit transaction + // + // { + // id: '34e4816b-4c8c-5323-a01c-35a9fa26e490', + // type: 'send', + // status: 'completed', + // amount: { amount: '28.31976528', currency: 'BCH' }, + // native_amount: { amount: '2799.65', currency: 'GBP' }, + // description: null, + // created_at: '2019-02-28T12:35:20Z', + // updated_at: '2019-02-28T12:43:24Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/34e4816b-4c8c-5323-a01c-35a9fa26e490', + // instant_exchange: false, + // network: { + // status: 'confirmed', + // hash: '56222d865dae83774fccb2efbd9829cf08c75c94ce135bfe4276f3fb46d49701', + // transaction_url: 'https://bch.btc.com/56222d865dae83774fccb2efbd9829cf08c75c94ce135bfe4276f3fb46d49701' + // }, + // from: { resource: 'bitcoin_cash_network', currency: 'BCH' }, + // details: { title: 'Received Bitcoin Cash', subtitle: 'From Bitcoin Cash address' } + // } + // + // crypto withdrawal transaction + // + // { + // id: '459aad99-2c41-5698-ac71-b6b81a05196c', + // type: 'send', + // status: 'completed', + // amount: { amount: '-0.36775642', currency: 'BTC' }, + // native_amount: { amount: '-1111.65', currency: 'GBP' }, + // description: null, + // created_at: '2019-03-20T08:37:07Z', + // updated_at: '2019-03-20T08:49:33Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/459aad99-2c41-5698-ac71-b6b81a05196c', + // instant_exchange: false, + // network: { + // status: 'confirmed', + // hash: '2732bbcf35c69217c47b36dce64933d103895277fe25738ffb9284092701e05b', + // transaction_url: 'https://blockchain.info/tx/2732bbcf35c69217c47b36dce64933d103895277fe25738ffb9284092701e05b', + // transaction_fee: { amount: '0.00000000', currency: 'BTC' }, + // transaction_amount: { amount: '0.36775642', currency: 'BTC' }, + // confirmations: 15682 + // }, + // to: { + // resource: 'bitcoin_address', + // address: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', + // currency: 'BTC', + // address_info: { address: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX' } + // }, + // idem: 'da0a2f14-a2af-4c5a-a37e-d4484caf582bsend', + // application: { + // id: '5756ab6e-836b-553b-8950-5e389451225d', + // resource: 'application', + // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' + // }, + // details: { title: 'Sent Bitcoin', subtitle: 'To Bitcoin address' } + // } + // + // withdrawal transaction from coinbase to coinbasepro + // + // { + // id: '5b1b9fb8-5007-5393-b923-02903b973fdc', + // type: 'pro_deposit', + // status: 'completed', + // amount: { amount: '-0.00001111', currency: 'BCH' }, + // native_amount: { amount: '0.00', currency: 'GBP' }, + // description: null, + // created_at: '2019-02-28T13:31:58Z', + // updated_at: '2019-02-28T13:31:58Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/5b1b9fb8-5007-5393-b923-02903b973fdc', + // instant_exchange: false, + // application: { + // id: '5756ab6e-836b-553b-8950-5e389451225d', + // resource: 'application', + // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' + // }, + // details: { title: 'Transferred Bitcoin Cash', subtitle: 'To Coinbase Pro' } + // } + // + // withdrawal transaction from coinbase to gdax + // + // { + // id: 'badb7313-a9d3-5c07-abd0-00f8b44199b1', + // type: 'exchange_deposit', + // status: 'completed', + // amount: { amount: '-0.43704149', currency: 'BCH' }, + // native_amount: { amount: '-51.90', currency: 'GBP' }, + // description: null, + // created_at: '2019-03-19T10:30:40Z', + // updated_at: '2019-03-19T10:30:40Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/badb7313-a9d3-5c07-abd0-00f8b44199b1', + // instant_exchange: false, + // details: { title: 'Transferred Bitcoin Cash', subtitle: 'To GDAX' } + // } + // + // deposit transaction from gdax to coinbase + // + // { + // id: '9c4b642c-8688-58bf-8962-13cef64097de', + // type: 'exchange_withdrawal', + // status: 'completed', + // amount: { amount: '0.57729420', currency: 'BTC' }, + // native_amount: { amount: '4418.72', currency: 'GBP' }, + // description: null, + // created_at: '2018-02-17T11:33:33Z', + // updated_at: '2018-02-17T11:33:33Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/9c4b642c-8688-58bf-8962-13cef64097de', + // instant_exchange: false, + // details: { title: 'Transferred Bitcoin', subtitle: 'From GDAX' } + // } + // + // deposit transaction from coinbasepro to coinbase + // + // { + // id: '8d6dd0b9-3416-568a-889d-8f112fae9e81', + // type: 'pro_withdrawal', + // status: 'completed', + // amount: { amount: '0.40555386', currency: 'BTC' }, + // native_amount: { amount: '1140.27', currency: 'GBP' }, + // description: null, + // created_at: '2019-03-04T19:41:58Z', + // updated_at: '2019-03-04T19:41:58Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/8d6dd0b9-3416-568a-889d-8f112fae9e81', + // instant_exchange: false, + // application: { + // id: '5756ab6e-836b-553b-8950-5e389451225d', + // resource: 'application', + // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' + // }, + // details: { title: 'Transferred Bitcoin', subtitle: 'From Coinbase Pro' } + // } + // + // sell trade + // + // { + // id: 'a9409207-df64-585b-97ab-a50780d2149e', + // type: 'sell', + // status: 'completed', + // amount: { amount: '-9.09922880', currency: 'BTC' }, + // native_amount: { amount: '-7285.73', currency: 'GBP' }, + // description: null, + // created_at: '2017-03-27T15:38:34Z', + // updated_at: '2017-03-27T15:38:34Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/a9409207-df64-585b-97ab-a50780d2149e', + // instant_exchange: false, + // sell: { + // id: 'e3550b4d-8ae6-5de3-95fe-1fb01ba83051', + // resource: 'sell', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/sells/e3550b4d-8ae6-5de3-95fe-1fb01ba83051' + // }, + // details: { + // title: 'Sold Bitcoin', + // subtitle: 'Using EUR Wallet', + // payment_method_name: 'EUR Wallet' + // } + // } + // + // buy trade + // + // { + // id: '63eeed67-9396-5912-86e9-73c4f10fe147', + // type: 'buy', + // status: 'completed', + // amount: { amount: '2.39605772', currency: 'ETH' }, + // native_amount: { amount: '98.31', currency: 'GBP' }, + // description: null, + // created_at: '2017-03-27T09:07:56Z', + // updated_at: '2017-03-27T09:07:57Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/8902f85d-4a69-5d74-82fe-8e390201bda7/transactions/63eeed67-9396-5912-86e9-73c4f10fe147', + // instant_exchange: false, + // buy: { + // id: '20b25b36-76c6-5353-aa57-b06a29a39d82', + // resource: 'buy', + // resource_path: '/v2/accounts/8902f85d-4a69-5d74-82fe-8e390201bda7/buys/20b25b36-76c6-5353-aa57-b06a29a39d82' + // }, + // details: { + // title: 'Bought Ethereum', + // subtitle: 'Using EUR Wallet', + // payment_method_name: 'EUR Wallet' + // } + // } + // + // fiat deposit transaction + // + // { + // id: '04ed4113-3732-5b0c-af86-b1d2146977d0', + // type: 'fiat_deposit', + // status: 'completed', + // amount: { amount: '114.02', currency: 'EUR' }, + // native_amount: { amount: '97.23', currency: 'GBP' }, + // description: null, + // created_at: '2017-02-09T07:01:21Z', + // updated_at: '2017-02-09T07:01:22Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/04ed4113-3732-5b0c-af86-b1d2146977d0', + // instant_exchange: false, + // fiat_deposit: { + // id: 'f34c19f3-b730-5e3d-9f72-96520448677a', + // resource: 'fiat_deposit', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/deposits/f34c19f3-b730-5e3d-9f72-96520448677a' + // }, + // details: { + // title: 'Deposited funds', + // subtitle: 'From SEPA Transfer (GB47 BARC 20..., reference CBADVI)', + // payment_method_name: 'SEPA Transfer (GB47 BARC 20..., reference CBADVI)' + // } + // } + // + // fiat withdrawal transaction + // + // { + // id: '957d98e2-f80e-5e2f-a28e-02945aa93079', + // type: 'fiat_withdrawal', + // status: 'completed', + // amount: { amount: '-11000.00', currency: 'EUR' }, + // native_amount: { amount: '-9698.22', currency: 'GBP' }, + // description: null, + // created_at: '2017-12-06T13:19:19Z', + // updated_at: '2017-12-06T13:19:19Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/957d98e2-f80e-5e2f-a28e-02945aa93079', + // instant_exchange: false, + // fiat_withdrawal: { + // id: 'f4bf1fd9-ab3b-5de7-906d-ed3e23f7a4e7', + // resource: 'fiat_withdrawal', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/withdrawals/f4bf1fd9-ab3b-5de7-906d-ed3e23f7a4e7' + // }, + // details: { + // title: 'Withdrew funds', + // subtitle: 'To HSBC BANK PLC (GB74 MIDL...)', + // payment_method_name: 'HSBC BANK PLC (GB74 MIDL...)' + // } + // } + // + const amountInfo = this.safeValue (item, 'amount', {}); + let amount = this.safeFloat (amountInfo, 'amount'); + let direction = undefined; + if (amount < 0) { + direction = 'out'; + amount = -amount; + } else { + direction = 'in'; + } + const currencyId = this.safeString (amountInfo, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + // + // the address and txid do not belong to the unified ledger structure + // + // let address = undefined; + // if (item['to']) { + // address = this.safeString (item['to'], 'address'); + // } + // let txid = undefined; + // + let fee = undefined; + const networkInfo = this.safeValue (item, 'network', {}); + // txid = network['hash']; // txid does not belong to the unified ledger structure + const feeInfo = this.safeValue (networkInfo, 'transaction_fee'); + if (feeInfo !== undefined) { + const feeCurrencyId = this.safeString (feeInfo, 'currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId, currency); + const feeAmount = this.safeFloat (feeInfo, 'amount'); + fee = { + 'cost': feeAmount, + 'currency': feeCurrencyCode, + }; + } + const timestamp = this.parse8601 (this.safeValue (item, 'created_at')); + const id = this.safeString (item, 'id'); + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const status = this.parseLedgerEntryStatus (this.safeString (item, 'status')); + const path = this.safeString (item, 'resource_path'); + let accountId = undefined; + if (path !== undefined) { + const parts = path.split ('/'); + const numParts = parts.length; + if (numParts > 3) { + accountId = parts[3]; + } + } + return { + 'info': item, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'direction': direction, + 'account': accountId, + 'referenceId': undefined, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': undefined, + 'after': undefined, + 'status': status, + 'fee': fee, + }; + } + + async findAccountId (code) { + await this.loadMarkets (); + await this.loadAccounts (); + for (let i = 0; i < this.accounts.length; i++) { + const account = this.accounts[i]; + if (account['code'] === code) { + return account['id']; + } + } + return undefined; + } + + prepareAccountRequest (limit = undefined, params = {}) { + const accountId = this.safeString2 (params, 'account_id', 'accountId'); + if (accountId === undefined) { + throw new ArgumentsRequired (this.id + ' method requires an account_id (or accountId) parameter'); + } + const request = { + 'account_id': accountId, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + return request; + } + + async prepareAccountRequestWithCurrencyCode (code = undefined, limit = undefined, params = {}) { + let accountId = this.safeString2 (params, 'account_id', 'accountId'); + if (accountId === undefined) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' method requires an account_id (or accountId) parameter OR a currency code argument'); + } + accountId = await this.findAccountId (code); + if (accountId === undefined) { + throw new ExchangeError (this.id + ' could not find account id for ' + code); + } + } + const request = { + 'account_id': accountId, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + return request; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let fullPath = '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + fullPath += '?' + this.urlencode (query); + } + } + const url = this.urls['api'] + fullPath; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let payload = ''; + if (method !== 'GET') { + if (Object.keys (query).length) { + body = this.json (query); + payload = body; + } + } + const auth = nonce + method + fullPath + payload; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + headers = { + 'CB-ACCESS-KEY': this.apiKey, + 'CB-ACCESS-SIGN': signature, + 'CB-ACCESS-TIMESTAMP': nonce, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const feedback = this.id + ' ' + body; + // + // {"error": "invalid_request", "error_description": "The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed."} + // + // or + // + // { + // "errors": [ + // { + // "id": "not_found", + // "message": "Not found" + // } + // ] + // } + // + let errorCode = this.safeString (response, 'error'); + if (errorCode !== undefined) { + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + const errors = this.safeValue (response, 'errors'); + if (errors !== undefined) { + if (Array.isArray (errors)) { + const numErrors = errors.length; + if (numErrors > 0) { + errorCode = this.safeString (errors[0], 'id'); + if (errorCode !== undefined) { + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + } + } + } + const data = this.safeValue (response, 'data'); + if (data === undefined) { + throw new ExchangeError (this.id + ' failed due to a malformed response ' + this.json (response)); + } + } +}; + + +},{"./base/Exchange":7,"./base/errors":9}],63:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const coinbasepro = require ('./coinbasepro.js'); + +// --------------------------------------------------------------------------- + +module.exports = class coinbaseprime extends coinbasepro { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinbaseprime', + 'name': 'Coinbase Prime', + 'urls': { + 'test': { + 'public': 'https://api-public.sandbox.prime.coinbase.com', + 'private': 'https://api-public.sandbox.prime.coinbase.com', + }, + 'logo': 'https://user-images.githubusercontent.com/1294454/44539184-29f26e00-a70c-11e8-868f-e907fc236a7c.jpg', + 'api': { + 'public': 'https://api.prime.coinbase.com', + 'private': 'https://api.prime.coinbase.com', + }, + 'www': 'https://prime.coinbase.com', + 'doc': 'https://docs.prime.coinbase.com', + 'fees': 'https://support.prime.coinbase.com/customer/en/portal/articles/2945629-fees?b_id=17475', + }, + }); + } +}; + +},{"./coinbasepro.js":64}],64:[function(require,module,exports){ +'use strict'; + +// ---------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InsufficientFunds, ArgumentsRequired, ExchangeError, InvalidOrder, InvalidAddress, AuthenticationError, NotSupported, OrderNotFound, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); + +// ---------------------------------------------------------------------------- + +module.exports = class coinbasepro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinbasepro', + 'name': 'Coinbase Pro', + 'countries': [ 'US' ], + 'rateLimit': 1000, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'cancelAllOrders': true, + 'CORS': true, + 'deposit': true, + 'fetchAccounts': true, + 'fetchClosedOrders': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderTrades': true, + 'fetchOrders': true, + 'fetchTime': true, + 'fetchTransactions': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': 60, + '5m': 300, + '15m': 900, + '1h': 3600, + '6h': 21600, + '1d': 86400, + }, + 'urls': { + 'test': { + 'public': 'https://api-public.sandbox.pro.coinbase.com', + 'private': 'https://api-public.sandbox.pro.coinbase.com', + }, + 'logo': 'https://user-images.githubusercontent.com/1294454/41764625-63b7ffde-760a-11e8-996d-a6328fa9347a.jpg', + 'api': { + 'public': 'https://api.pro.coinbase.com', + 'private': 'https://api.pro.coinbase.com', + }, + 'www': 'https://pro.coinbase.com/', + 'doc': 'https://docs.pro.coinbase.com', + 'fees': [ + 'https://docs.pro.coinbase.com/#fees', + 'https://support.pro.coinbase.com/customer/en/portal/articles/2945310-fees', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'products', + 'products/{id}/book', + 'products/{id}/candles', + 'products/{id}/stats', + 'products/{id}/ticker', + 'products/{id}/trades', + 'time', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/{id}', + 'accounts/{id}/holds', + 'accounts/{id}/ledger', + 'accounts/{id}/transfers', + 'coinbase-accounts', + 'coinbase-accounts/{id}/addresses', + 'fills', + 'funding', + 'fees', + 'orders', + 'orders/{id}', + 'otc/orders', + 'payment-methods', + 'position', + 'reports/{id}', + 'users/self/trailing-volume', + ], + 'post': [ + 'conversions', + 'deposits/coinbase-account', + 'deposits/payment-method', + 'coinbase-accounts/{id}/addresses', + 'funding/repay', + 'orders', + 'position/close', + 'profiles/margin-transfer', + 'reports', + 'withdrawals/coinbase', + 'withdrawals/crypto', + 'withdrawals/payment-method', + ], + 'delete': [ + 'orders', + 'orders/{id}', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, // complicated tier system per coin + 'percentage': true, + 'maker': 0.5 / 100, // highest fee of all tiers + 'taker': 0.5 / 100, // highest fee of all tiers + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BCH': 0, + 'BTC': 0, + 'LTC': 0, + 'ETH': 0, + 'EUR': 0.15, + 'USD': 25, + }, + 'deposit': { + 'BCH': 0, + 'BTC': 0, + 'LTC': 0, + 'ETH': 0, + 'EUR': 0.15, + 'USD': 10, + }, + }, + }, + 'exceptions': { + 'exact': { + 'Insufficient funds': InsufficientFunds, + 'NotFound': OrderNotFound, + 'Invalid API Key': AuthenticationError, + 'invalid signature': AuthenticationError, + 'Invalid Passphrase': AuthenticationError, + 'Invalid order id': InvalidOrder, + 'Private rate limit exceeded': RateLimitExceeded, + }, + 'broad': { + 'Order already done': OrderNotFound, + 'order not found': OrderNotFound, + 'price too small': InvalidOrder, + 'price too precise': InvalidOrder, + 'under maintenance': OnMaintenance, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetProducts (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const priceLimits = { + 'min': this.safeFloat (market, 'quote_increment'), + 'max': undefined, + }; + const precision = { + 'amount': this.precisionFromString (this.safeString (market, 'base_increment')), + 'price': this.precisionFromString (this.safeString (market, 'quote_increment')), + }; + const active = market['status'] === 'online'; + result.push (this.extend (this.fees['trading'], { + 'id': id, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'base_min_size'), + 'max': this.safeFloat (market, 'base_max_size'), + }, + 'price': priceLimits, + 'cost': { + 'min': this.safeFloat (market, 'min_market_funds'), + 'max': this.safeFloat (market, 'max_market_funds'), + }, + }, + 'active': active, + 'info': market, + })); + } + return result; + } + + async fetchAccounts (params = {}) { + const response = await this.privateGetAccounts (params); + // + // [ + // { + // id: '4aac9c60-cbda-4396-9da4-4aa71e95fba0', + // currency: 'BTC', + // balance: '0.0000000000000000', + // available: '0', + // hold: '0.0000000000000000', + // profile_id: 'b709263e-f42a-4c7d-949a-a95c83d065da' + // }, + // { + // id: 'f75fa69a-1ad1-4a80-bd61-ee7faa6135a3', + // currency: 'USDC', + // balance: '0.0000000000000000', + // available: '0', + // hold: '0.0000000000000000', + // profile_id: 'b709263e-f42a-4c7d-949a-a95c83d065da' + // }, + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const account = response[i]; + const accountId = this.safeString (account, 'id'); + const currencyId = this.safeString (account, 'currency'); + const code = this.safeCurrencyCode (currencyId); + result.push ({ + 'id': accountId, + 'type': undefined, + 'currency': code, + 'info': account, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'available'), + 'used': this.safeFloat (balance, 'hold'), + 'total': this.safeFloat (balance, 'balance'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + // level 1 - only the best bid and ask + // level 2 - top 50 bids and asks (aggregated) + // level 3 - full order book (non aggregated) + const request = { + 'id': this.marketId (symbol), + 'level': 2, // 1 best bidask, 2 aggregated, 3 full + }; + const response = await this.publicGetProductsIdBook (this.extend (request, params)); + // + // { + // "sequence":1924393896, + // "bids":[ + // ["0.01825","24.34811287",2], + // ["0.01824","72.5463",3], + // ["0.01823","424.54298049",6], + // ], + // "asks":[ + // ["0.01826","171.10414904",4], + // ["0.01827","22.60427028",1], + // ["0.01828","397.46018784",7], + // ] + // } + // + const orderbook = this.parseOrderBook (response); + orderbook['nonce'] = this.safeInteger (response, 'sequence'); + return orderbook; + } + + parseTicker (ticker, market = undefined) { + // + // publicGetProductsIdTicker + // + // { + // "trade_id":843439, + // "price":"0.997999", + // "size":"80.29769", + // "time":"2020-01-28T02:13:33.012523Z", + // "bid":"0.997094", + // "ask":"0.998", + // "volume":"1903188.03750000" + // } + // + // publicGetProductsIdStats + // + // { + // "open": "34.19000000", + // "high": "95.70000000", + // "low": "7.06000000", + // "volume": "2.41000000" + // } + // + const timestamp = this.parse8601 (this.safeValue (ticker, 'time')); + const bid = this.safeFloat (ticker, 'bid'); + const ask = this.safeFloat (ticker, 'ask'); + const last = this.safeFloat (ticker, 'price'); + const symbol = (market === undefined) ? undefined : market['symbol']; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + // publicGetProductsIdTicker or publicGetProductsIdStats + const method = this.safeString (this.options, 'fetchTickerMethod', 'publicGetProductsIdTicker'); + const response = await this[method] (this.extend (request, params)); + // + // publicGetProductsIdTicker + // + // { + // "trade_id":843439, + // "price":"0.997999", + // "size":"80.29769", + // "time":"2020-01-28T02:13:33.012523Z", + // "bid":"0.997094", + // "ask":"0.998", + // "volume":"1903188.03750000" + // } + // + // publicGetProductsIdStats + // + // { + // "open": "34.19000000", + // "high": "95.70000000", + // "low": "7.06000000", + // "volume": "2.41000000" + // } + // + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + // + // { + // type: 'match', + // trade_id: 82047307, + // maker_order_id: '0f358725-2134-435e-be11-753912a326e0', + // taker_order_id: '252b7002-87a3-425c-ac73-f5b9e23f3caf', + // side: 'sell', + // size: '0.00513192', + // price: '9314.78', + // product_id: 'BTC-USD', + // sequence: 12038915443, + // time: '2020-01-31T20:03:41.158814Z' + // } + // + const timestamp = this.parse8601 (this.safeString2 (trade, 'time', 'created_at')); + let symbol = undefined; + const marketId = this.safeString (trade, 'product_id'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let feeRate = undefined; + let feeCurrency = undefined; + let takerOrMaker = undefined; + if (market !== undefined) { + feeCurrency = market['quote']; + if ('liquidity' in trade) { + takerOrMaker = (trade['liquidity'] === 'T') ? 'taker' : 'maker'; + feeRate = market[takerOrMaker]; + } + } + const feeCost = this.safeFloat2 (trade, 'fill_fees', 'fee'); + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }; + const type = undefined; + const id = this.safeString (trade, 'trade_id'); + let side = (trade['side'] === 'buy') ? 'sell' : 'buy'; + const orderId = this.safeString (trade, 'order_id'); + // Coinbase Pro returns inverted side to fetchMyTrades vs fetchTrades + if (orderId !== undefined) { + side = (trade['side'] === 'buy') ? 'buy' : 'sell'; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + return { + 'id': id, + 'order': orderId, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'fee': fee, + 'cost': price * amount, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // as of 2018-08-23 + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_id': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetFills (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], // fixes issue #2 + }; + const response = await this.publicGetProductsIdTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[3], + ohlcv[2], + ohlcv[1], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const granularity = this.timeframes[timeframe]; + const request = { + 'id': market['id'], + 'granularity': granularity, + }; + if (since !== undefined) { + request['start'] = this.iso8601 (since); + if (limit === undefined) { + // https://docs.pro.coinbase.com/#get-historic-rates + limit = 300; // max = 300 + } + request['end'] = this.iso8601 (this.sum ((limit - 1) * granularity * 1000, since)); + } + const response = await this.publicGetProductsIdCandles (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchTime (params = {}) { + const response = await this.publicGetTime (params); + return this.parse8601 (this.safeString (response, 'iso')); + } + + parseOrderStatus (status) { + const statuses = { + 'pending': 'open', + 'active': 'open', + 'open': 'open', + 'done': 'closed', + 'canceled': 'canceled', + 'canceling': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + let symbol = undefined; + const marketId = this.safeString (order, 'product_id'); + let quote = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'filled_size'); + const amount = this.safeFloat (order, 'size', filled); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const cost = this.safeFloat (order, 'executed_value'); + const feeCost = this.safeFloat (order, 'fill_fees'); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrencyCode = undefined; + if (market !== undefined) { + feeCurrencyCode = market['quote']; + } else if (quote !== undefined) { + feeCurrencyCode = quote; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + 'rate': undefined, + }; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'order_id': id, + }; + const response = await this.privateGetFills (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'all', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'done', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + // let oid = this.nonce ().toString (); + const request = { + 'product_id': this.marketId (symbol), + 'side': side, + 'size': this.amountToPrecision (symbol, amount), + 'type': type, + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.privateDeleteOrdersId ({ 'id': id }); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + return await this.privateDeleteOrders (params); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + const cost = amount * price; + const currency = market['quote']; + return { + 'type': takerOrMaker, + 'currency': currency, + 'rate': rate, + 'cost': parseFloat (this.currencyToPrecision (currency, rate * cost)), + }; + } + + async fetchPaymentMethods (params = {}) { + return await this.privateGetPaymentMethods (params); + } + + async deposit (code, amount, address, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + }; + let method = 'privatePostDeposits'; + if ('payment_method_id' in params) { + // deposit from a payment_method, like a bank account + method += 'PaymentMethod'; + } else if ('coinbase_account_id' in params) { + // deposit into Coinbase Pro account from a Coinbase account + method += 'CoinbaseAccount'; + } else { + // deposit methodotherwise we did not receive a supported deposit location + // relevant docs link for the Googlers + // https://docs.pro.coinbase.com/#deposits + throw new NotSupported (this.id + ' deposit() requires one of `coinbase_account_id` or `payment_method_id` extra params'); + } + const response = await this[method] (this.extend (request, params)); + if (!response) { + throw new ExchangeError (this.id + ' deposit() error: ' + this.json (response)); + } + return { + 'info': response, + 'id': response['id'], + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + }; + let method = 'privatePostWithdrawals'; + if ('payment_method_id' in params) { + method += 'PaymentMethod'; + } else if ('coinbase_account_id' in params) { + method += 'CoinbaseAccount'; + } else { + method += 'Crypto'; + request['crypto_address'] = address; + } + const response = await this[method] (this.extend (request, params)); + if (!response) { + throw new ExchangeError (this.id + ' withdraw() error: ' + this.json (response)); + } + return { + 'info': response, + 'id': response['id'], + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let currency = undefined; + let id = this.safeString (params, 'id'); // account id + if (id === undefined) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency code argument if no account id specified in params'); + } + currency = this.currency (code); + const accountsByCurrencyCode = this.indexBy (this.accounts, 'currency'); + const account = this.safeValue (accountsByCurrencyCode, code); + if (account === undefined) { + throw new ExchangeError (this.id + ' fetchTransactions() could not find account id for ' + code); + } + id = account['id']; + } + const request = { + 'id': id, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetAccountsIdTransfers (this.extend (request, params)); + for (let i = 0; i < response.length; i++) { + response[i]['currency'] = code; + } + return this.parseTransactions (response, currency, since, limit); + } + + parseTransactionStatus (transaction) { + const canceled = this.safeValue (transaction, 'canceled_at'); + if (canceled) { + return 'canceled'; + } + const processed = this.safeValue (transaction, 'processed_at'); + const completed = this.safeValue (transaction, 'completed_at'); + if (completed) { + return 'ok'; + } else if (processed && !completed) { + return 'failed'; + } else { + return 'pending'; + } + } + + parseTransaction (transaction, currency = undefined) { + const details = this.safeValue (transaction, 'details', {}); + const id = this.safeString (transaction, 'id'); + const txid = this.safeString (details, 'crypto_transaction_hash'); + const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); + const updated = this.parse8601 (this.safeString (transaction, 'processed_at')); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const fee = undefined; + const status = this.parseTransactionStatus (transaction); + const amount = this.safeFloat (transaction, 'amount'); + let type = this.safeString (transaction, 'type'); + let address = this.safeString (details, 'crypto_address'); + const tag = this.safeString (details, 'destination_tag'); + address = this.safeString (transaction, 'crypto_address', address); + if (type === 'withdraw') { + type = 'withdrawal'; + address = this.safeString (details, 'sent_to_address', address); + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } + const url = this.urls['api'][api] + request; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let payload = ''; + if (method !== 'GET') { + if (Object.keys (query).length) { + body = this.json (query); + payload = body; + } + } + const what = nonce + method + request + payload; + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (this.encode (what), secret, 'sha256', 'base64'); + headers = { + 'CB-ACCESS-KEY': this.apiKey, + 'CB-ACCESS-SIGN': this.decode (signature), + 'CB-ACCESS-TIMESTAMP': nonce, + 'CB-ACCESS-PASSPHRASE': this.password, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + let accounts = this.safeValue (this.options, 'coinbaseAccounts'); + if (accounts === undefined) { + accounts = await this.privateGetCoinbaseAccounts (); + this.options['coinbaseAccounts'] = accounts; // cache it + this.options['coinbaseAccountsByCurrencyId'] = this.indexBy (accounts, 'currency'); + } + const currencyId = currency['id']; + const account = this.safeValue (this.options['coinbaseAccountsByCurrencyId'], currencyId); + if (account === undefined) { + // eslint-disable-next-line quotes + throw new InvalidAddress (this.id + " fetchDepositAddress() could not find currency code " + code + " with id = " + currencyId + " in this.options['coinbaseAccountsByCurrencyId']"); + } + const request = { + 'id': account['id'], + }; + const response = await this.privateGetCoinbaseAccountsIdAddresses (this.extend (request, params)); + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'destination_tag'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + let accounts = this.safeValue (this.options, 'coinbaseAccounts'); + if (accounts === undefined) { + accounts = await this.privateGetCoinbaseAccounts (); + this.options['coinbaseAccounts'] = accounts; // cache it + this.options['coinbaseAccountsByCurrencyId'] = this.indexBy (accounts, 'currency'); + } + const currencyId = currency['id']; + const account = this.safeValue (this.options['coinbaseAccountsByCurrencyId'], currencyId); + if (account === undefined) { + // eslint-disable-next-line quotes + throw new InvalidAddress (this.id + " fetchDepositAddress() could not find currency code " + code + " with id = " + currencyId + " in this.options['coinbaseAccountsByCurrencyId']"); + } + const request = { + 'id': account['id'], + }; + const response = await this.privatePostCoinbaseAccountsIdAddresses (this.extend (request, params)); + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'destination_tag'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 400) || (code === 404)) { + if (body[0] === '{') { + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + throw new ExchangeError (this.id + ' ' + body); + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response !== 'string') { + if ('message' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],65:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coincheck extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coincheck', + 'name': 'coincheck', + 'countries': [ 'JP', 'ID' ], + 'rateLimit': 1500, + 'has': { + 'CORS': false, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766464-3b5c3c74-5ed9-11e7-840e-31b32968e1da.jpg', + 'api': 'https://coincheck.com/api', + 'www': 'https://coincheck.com', + 'doc': 'https://coincheck.com/documents/exchange/api', + 'fees': [ + 'https://coincheck.com/exchange/fee', + 'https://coincheck.com/info/fee', + ], + }, + 'api': { + 'public': { + 'get': [ + 'exchange/orders/rate', + 'order_books', + 'rate/{pair}', + 'ticker', + 'trades', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/balance', + 'accounts/leverage_balance', + 'bank_accounts', + 'deposit_money', + 'exchange/orders/opens', + 'exchange/orders/transactions', + 'exchange/orders/transactions_pagination', + 'exchange/leverage/positions', + 'lending/borrows/matches', + 'send_money', + 'withdraws', + ], + 'post': [ + 'bank_accounts', + 'deposit_money/{id}/fast', + 'exchange/orders', + 'exchange/transfers/to_leverage', + 'exchange/transfers/from_leverage', + 'lending/borrows', + 'lending/borrows/{id}/repay', + 'send_money', + 'withdraws', + ], + 'delete': [ + 'bank_accounts/{id}', + 'exchange/orders/{id}', + 'withdraws/{id}', + ], + }, + }, + 'markets': { + 'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' }, // the only real pair + // 'ETH/JPY': { 'id': 'eth_jpy', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy' }, + // 'ETC/JPY': { 'id': 'etc_jpy', 'symbol': 'ETC/JPY', 'base': 'ETC', 'quote': 'JPY', 'baseId': 'etc', 'quoteId': 'jpy' }, + // 'DAO/JPY': { 'id': 'dao_jpy', 'symbol': 'DAO/JPY', 'base': 'DAO', 'quote': 'JPY', 'baseId': 'dao', 'quoteId': 'jpy' }, + // 'LSK/JPY': { 'id': 'lsk_jpy', 'symbol': 'LSK/JPY', 'base': 'LSK', 'quote': 'JPY', 'baseId': 'lsk', 'quoteId': 'jpy' }, + // 'FCT/JPY': { 'id': 'fct_jpy', 'symbol': 'FCT/JPY', 'base': 'FCT', 'quote': 'JPY', 'baseId': 'fct', 'quoteId': 'jpy' }, + // 'XMR/JPY': { 'id': 'xmr_jpy', 'symbol': 'XMR/JPY', 'base': 'XMR', 'quote': 'JPY', 'baseId': 'xmr', 'quoteId': 'jpy' }, + // 'REP/JPY': { 'id': 'rep_jpy', 'symbol': 'REP/JPY', 'base': 'REP', 'quote': 'JPY', 'baseId': 'rep', 'quoteId': 'jpy' }, + // 'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' }, + // 'ZEC/JPY': { 'id': 'zec_jpy', 'symbol': 'ZEC/JPY', 'base': 'ZEC', 'quote': 'JPY', 'baseId': 'zec', 'quoteId': 'jpy' }, + // 'XEM/JPY': { 'id': 'xem_jpy', 'symbol': 'XEM/JPY', 'base': 'XEM', 'quote': 'JPY', 'baseId': 'xem', 'quoteId': 'jpy' }, + // 'LTC/JPY': { 'id': 'ltc_jpy', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy' }, + // 'DASH/JPY': { 'id': 'dash_jpy', 'symbol': 'DASH/JPY', 'base': 'DASH', 'quote': 'JPY', 'baseId': 'dash', 'quoteId': 'jpy' }, + // 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' }, + // 'ETC/BTC': { 'id': 'etc_btc', 'symbol': 'ETC/BTC', 'base': 'ETC', 'quote': 'BTC', 'baseId': 'etc', 'quoteId': 'btc' }, + // 'LSK/BTC': { 'id': 'lsk_btc', 'symbol': 'LSK/BTC', 'base': 'LSK', 'quote': 'BTC', 'baseId': 'lsk', 'quoteId': 'btc' }, + // 'FCT/BTC': { 'id': 'fct_btc', 'symbol': 'FCT/BTC', 'base': 'FCT', 'quote': 'BTC', 'baseId': 'fct', 'quoteId': 'btc' }, + // 'XMR/BTC': { 'id': 'xmr_btc', 'symbol': 'XMR/BTC', 'base': 'XMR', 'quote': 'BTC', 'baseId': 'xmr', 'quoteId': 'btc' }, + // 'REP/BTC': { 'id': 'rep_btc', 'symbol': 'REP/BTC', 'base': 'REP', 'quote': 'BTC', 'baseId': 'rep', 'quoteId': 'btc' }, + // 'XRP/BTC': { 'id': 'xrp_btc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc' }, + // 'ZEC/BTC': { 'id': 'zec_btc', 'symbol': 'ZEC/BTC', 'base': 'ZEC', 'quote': 'BTC', 'baseId': 'zec', 'quoteId': 'btc' }, + // 'XEM/BTC': { 'id': 'xem_btc', 'symbol': 'XEM/BTC', 'base': 'XEM', 'quote': 'BTC', 'baseId': 'xem', 'quoteId': 'btc' }, + // 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, + // 'DASH/BTC': { 'id': 'dash_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'dash', 'quoteId': 'btc' }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0, + 'taker': 0, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetAccountsBalance (params); + const result = { 'info': balances }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currencyId = this.currencyId (code); + if (currencyId in balances) { + const account = this.account (); + const reserved = currencyId + '_reserved'; + account['free'] = this.safeFloat (balances, currencyId); + account['used'] = this.safeFloat (balances, reserved); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // Only BTC/JPY is meaningful + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privateGetExchangeOrdersOpens (params); + const rawOrders = this.safeValue (response, 'orders', []); + const parsedOrders = this.parseOrders (rawOrders, market, since, limit); + const result = []; + for (let i = 0; i < parsedOrders.length; i++) { + result.push (this.extend (parsedOrders[i], { 'status': 'open' })); + } + return result; + } + + parseOrder (order, market = undefined) { + // + // fetchOpenOrders + // + // { id: 202835, + // order_type: "buy", + // rate: 26890, + // pair: "btc_jpy", + // pending_amount: "0.5527", + // pending_market_buy_amount: null, + // stop_loss_rate: null, + // created_at: "2015-01-10T05:55:38.000Z" } + // + // todo: add formats for fetchOrder, fetchClosedOrders here + // + const id = this.safeString (order, 'id'); + const side = this.safeString (order, 'order_type'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const amount = this.safeFloat (order, 'pending_amount'); + const remaining = this.safeFloat (order, 'pending_amount'); + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if (remaining !== undefined) { + if (amount !== undefined) { + filled = Math.max (amount - remaining, 0); + if (price !== undefined) { + cost = filled * price; + } + } + } + const status = undefined; + const marketId = this.safeString (order, 'pair'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'side': side, + 'type': undefined, + 'status': status, + 'symbol': symbol, + 'price': price, + 'cost': cost, + 'fee': undefined, + 'info': order, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + if (symbol !== 'BTC/JPY') { + throw new BadSymbol (this.id + ' fetchOrderBook () supports BTC/JPY only'); + } + await this.loadMarkets (); + const response = await this.publicGetOrderBooks (params); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + if (symbol !== 'BTC/JPY') { + throw new BadSymbol (this.id + ' fetchTicker () supports BTC/JPY only'); + } + await this.loadMarkets (); + const ticker = await this.publicGetTicker (params); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'rate'); + const marketId = this.safeString (trade, 'pair'); + market = this.safeValue (this.markets_by_id, marketId, market); + let symbol = undefined; + let baseId = undefined; + let quoteId = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + baseId = market['baseId']; + quoteId = market['quoteId']; + symbol = market['symbol']; + } else { + const ids = marketId.split ('_'); + baseId = ids[0]; + quoteId = ids[1]; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let takerOrMaker = undefined; + let amount = undefined; + let cost = undefined; + let side = undefined; + let fee = undefined; + let orderId = undefined; + if ('liquidity' in trade) { + if (this.safeString (trade, 'liquidity') === 'T') { + takerOrMaker = 'taker'; + } else if (this.safeString (trade, 'liquidity') === 'M') { + takerOrMaker = 'maker'; + } + const funds = this.safeValue (trade, 'funds', {}); + amount = this.safeFloat (funds, baseId); + cost = this.safeFloat (funds, quoteId); + fee = { + 'currency': this.safeString (trade, 'fee_currency'), + 'cost': this.safeFloat (trade, 'fee'), + }; + side = this.safeString (trade, 'side'); + orderId = this.safeString (trade, 'order_id'); + } else { + amount = this.safeFloat (trade, 'amount'); + side = this.safeString (trade, 'order_type'); + } + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + } + return { + 'id': id, + 'info': trade, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': orderId, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.privateGetExchangeOrdersTransactions (this.extend ({}, params)); + const transactions = this.safeValue (response, 'transactions', []); + return this.parseTrades (transactions, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + if (symbol !== 'BTC/JPY') { + throw new BadSymbol (this.id + ' fetchTrades () supports BTC/JPY only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTrades (this.extend (request, params)); + const data = this.safeValue (response, 'data', []); + return this.parseTrades (data, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + if (type === 'market') { + const order_type = type + '_' + side; + request['order_type'] = order_type; + const prefix = (side === 'buy') ? (order_type + '_') : ''; + request[prefix + 'amount'] = amount; + } else { + request['order_type'] = side; + request['rate'] = price; + request['amount'] = amount; + } + const response = await this.privatePostExchangeOrders (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privateDeleteExchangeOrdersId (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let queryString = ''; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (this.keysort (query)); + } + } else { + if (Object.keys (query).length) { + body = this.urlencode (this.keysort (query)); + queryString = body; + } + } + const auth = nonce + url + queryString; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'ACCESS-KEY': this.apiKey, + 'ACCESS-NONCE': nonce, + 'ACCESS-SIGNATURE': this.hmac (this.encode (auth), this.encode (this.secret)), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (api === 'public') { + return response; + } + if ('success' in response) { + if (response['success']) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],66:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, InvalidNonce, InsufficientFunds, InvalidOrder, OrderNotFound, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinegg extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinegg', + 'name': 'CoinEgg', + 'countries': [ 'CN', 'UK' ], + 'has': { + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': 'emulated', + 'fetchMyTrades': false, + 'fetchTickers': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/36770310-adfa764e-1c5a-11e8-8e09-449daac3d2fb.jpg', + 'api': { + 'web': 'https://trade.coinegg.com/web', + 'rest': 'https://api.coinegg.com/api/v1', + }, + 'www': 'https://www.coinegg.com', + 'doc': 'https://www.coinegg.com/explain.api.html', + 'fees': 'https://www.coinegg.com/fee.html', + 'referral': 'https://www.coinegg.com/user/register?invite=523218', + }, + 'api': { + 'web': { + 'get': [ + 'symbol/ticker?right_coin={quote}', + '{quote}/trends', + '{quote}/{base}/order', + '{quote}/{base}/trades', + '{quote}/{base}/depth.js', + ], + }, + 'public': { + 'get': [ + 'ticker/region/{quote}', + 'depth/region/{quote}', + 'orders/region/{quote}', + ], + }, + 'private': { + 'post': [ + 'balance', + 'trade_add/region/{quote}', + 'trade_cancel/region/{quote}', + 'trade_view/region/{quote}', + 'trade_list/region/{quote}', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.008, + 'BCH': 0.002, + 'LTC': 0.001, + 'ETH': 0.01, + 'ETC': 0.01, + 'NEO': 0, + 'QTUM': '1%', + 'XRP': '1%', + 'DOGE': '1%', + 'LSK': '1%', + 'XAS': '1%', + 'BTS': '1%', + 'GAME': '1%', + 'GOOC': '1%', + 'NXT': '1%', + 'IFC': '1%', + 'DNC': '1%', + 'BLK': '1%', + 'VRC': '1%', + 'XPM': '1%', + 'VTC': '1%', + 'TFC': '1%', + 'PLC': '1%', + 'EAC': '1%', + 'PPC': '1%', + 'FZ': '1%', + 'ZET': '1%', + 'RSS': '1%', + 'PGC': '1%', + 'SKT': '1%', + 'JBC': '1%', + 'RIO': '1%', + 'LKC': '1%', + 'ZCC': '1%', + 'MCC': '1%', + 'QEC': '1%', + 'MET': '1%', + 'YTC': '1%', + 'HLB': '1%', + 'MRYC': '1%', + 'MTC': '1%', + 'KTC': 0, + }, + }, + }, + 'exceptions': { + '103': AuthenticationError, + '104': AuthenticationError, + '105': AuthenticationError, + '106': InvalidNonce, + '200': InsufficientFunds, + '201': InvalidOrder, + '202': InvalidOrder, + '203': OrderNotFound, + '402': DDoSProtection, + }, + 'errorMessages': { + '100': 'Required parameters can not be empty', + '101': 'Illegal parameter', + '102': 'coin does not exist', + '103': 'Key does not exist', + '104': 'Signature does not match', + '105': 'Insufficient permissions', + '106': 'Request expired(nonce error)', + '200': 'Lack of balance', + '201': 'Too small for the number of trading', + '202': 'Price must be in 0 - 1000000', + '203': 'Order does not exist', + '204': 'Pending order amount must be above 0.001 BTC', + '205': 'Restrict pending order prices', + '206': 'Decimal place error', + '401': 'System error', + '402': 'Requests are too frequent', + '403': 'Non-open API', + '404': 'IP restriction does not request the resource', + '405': 'Currency transactions are temporarily closed', + }, + 'options': { + 'quoteIds': [ 'btc', 'eth', 'usc', 'usdt' ], + }, + 'commonCurrencies': { + 'JBC': 'JubaoCoin', + }, + }); + } + + async fetchMarkets (params = {}) { + const quoteIds = this.options['quoteIds']; + const result = []; + for (let b = 0; b < quoteIds.length; b++) { + const quoteId = quoteIds[b]; + const response = await this.webGetSymbolTickerRightCoinQuote ({ + 'quote': quoteId, + }); + const tickers = this.safeValue (response, 'data', []); + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const id = ticker['symbol']; + const baseId = id.split ('_')[0]; + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': ticker, + }); + } + } + return result; + } + + parseTicker (ticker, market = undefined) { + const symbol = market['symbol']; + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'change'); + let open = undefined; + let change = undefined; + let average = undefined; + if (percentage !== undefined) { + const relativeChange = percentage / 100; + open = last / this.sum (1, relativeChange); + change = last - open; + average = this.sum (last, open) / 2; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVol'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.publicGetTickerRegionQuote (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.publicGetDepthRegionQuote (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const symbol = market['symbol']; + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = this.costToPrecision (symbol, price * amount); + } + } + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.publicGetOrdersRegionQuote (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const data = this.safeValue (response, 'data', {}); + const balances = this.omit (data, 'uid'); + const keys = Object.keys (balances); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const [ currencyId, accountType ] = key.split ('_'); + const code = this.safeCurrencyCode (currencyId); + if (!(code in result)) { + result[code] = this.account (); + } + const type = (accountType === 'lock') ? 'used' : 'free'; + result[code][type] = this.safeFloat (balances, key); + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'datetime')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount_original'); + const remaining = this.safeFloat (order, 'amount_outstanding'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + let status = this.safeString (order, 'status'); + if (status === 'cancelled') { + status = 'canceled'; + } else { + status = remaining ? 'open' : 'closed'; + } + const info = this.safeValue (order, 'info', order); + const type = 'limit'; + const side = this.safeString (order, 'type'); + const id = this.safeString (order, 'id'); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': info, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + 'type': side, + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostTradeAddRegionQuote (this.extend (request, params)); + const id = this.safeString (response, 'id'); + const order = this.parseOrder ({ + 'id': id, + 'datetime': this.ymdhms (this.milliseconds ()), + 'amount_original': amount, + 'amount_outstanding': amount, + 'price': price, + 'type': side, + 'info': response, + }, market); + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + return await this.privatePostTradeCancelRegionQuote (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.privatePostTradeViewRegionQuote (this.extend (request, params)); + return this.parseOrder (response['data'], market); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + if (since !== undefined) { + request['since'] = since / 1000; + } + const response = await this.privatePostTradeListRegionQuote (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'open', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let apiType = 'rest'; + if (api === 'web') { + apiType = api; + } + let url = this.urls['api'][apiType] + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public' || api === 'web') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + query = this.urlencode (this.extend ({ + 'key': this.apiKey, + 'nonce': this.nonce (), + }, query)); + const secret = this.hash (this.encode (this.secret)); + const signature = this.hmac (this.encode (query), this.encode (secret)); + query += '&' + 'signature=' + signature; + if (method === 'GET') { + url += '?' + query; + } else { + headers = { + 'Content-type': 'application/x-www-form-urlencoded', + }; + body = query; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // private endpoints return the following structure: + // {"result":true,"data":{...}} - success + // {"result":false,"code":"103"} - failure + // {"code":0,"msg":"Suceess","data":{"uid":"2716039","btc_balance":"0.00000000","btc_lock":"0.00000000","xrp_balance":"0.00000000","xrp_lock":"0.00000000"}} + const result = this.safeValue (response, 'result'); + if (result === undefined) { + // public endpoint � this comment left here by the contributor, in fact a missing result does not necessarily mean a public endpoint... + // we should just check the code and don't rely on the result at all here... + return; + } + if (result === true) { + // success + return; + } + const errorCode = this.safeString (response, 'code'); + const errorMessages = this.errorMessages; + const message = this.safeString (errorMessages, errorCode, 'Unknown Error'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (this.id + ' ' + message); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],67:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InsufficientFunds, OrderNotFound, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinex', + 'name': 'CoinEx', + 'version': 'v1', + 'countries': [ 'CN' ], + 'rateLimit': 1000, + 'has': { + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + }, + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': '1day', + '3d': '3day', + '1w': '1week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38046312-0b450aac-32c8-11e8-99ab-bc6b136b6cc7.jpg', + 'api': 'https://api.coinex.com', + 'www': 'https://www.coinex.com', + 'doc': 'https://github.com/coinexcom/coinex_exchange_api/wiki', + 'fees': 'https://www.coinex.com/fees', + 'referral': 'https://www.coinex.com/register?refer_code=yw5fz', + }, + 'api': { + 'public': { + 'get': [ + 'common/currency/rate', + 'common/asset/config', + 'market/info', + 'market/list', + 'market/ticker', + 'market/ticker/all', + 'market/depth', + 'market/deals', + 'market/kline', + ], + }, + 'private': { + 'get': [ + 'balance/coin/deposit', + 'balance/coin/withdraw', + 'balance/info', + 'future/account', + 'future/config', + 'future/limitprice', + 'future/loan/history', + 'future/market', + 'margin/account', + 'margin/config', + 'margin/loan/history', + 'margin/market', + 'order', + 'order/deals', + 'order/finished', + 'order/finished/{id}', + 'order/pending', + 'order/status', + 'order/status/batch', + 'order/user/deals', + ], + 'post': [ + 'balance/coin/withdraw', + 'future/flat', + 'future/loan', + 'future/transfer', + 'margin/flat', + 'margin/loan', + 'margin/transfer', + 'order/batchlimit', + 'order/ioc', + 'order/limit', + 'order/market', + 'sub_account/transfer', + ], + 'delete': [ + 'balance/coin/withdraw', + 'order/pending/batch', + 'order/pending', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.001, + 'taker': 0.001, + }, + 'funding': { + 'withdraw': { + 'BCH': 0.0, + 'BTC': 0.001, + 'LTC': 0.001, + 'ETH': 0.001, + 'ZEC': 0.0001, + 'DASH': 0.0001, + }, + }, + }, + 'limits': { + 'amount': { + 'min': 0.001, + 'max': undefined, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketInfo (params); + // + // { + // "code": 0, + // "data": { + // "WAVESBTC": { + // "name": "WAVESBTC", + // "min_amount": "1", + // "maker_fee_rate": "0.001", + // "taker_fee_rate": "0.001", + // "pricing_name": "BTC", + // "pricing_decimal": 8, + // "trading_name": "WAVES", + // "trading_decimal": 8 + // } + // } + // } + // + const markets = this.safeValue (response, 'data', {}); + const result = []; + const keys = Object.keys (markets); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = markets[key]; + const id = this.safeString (market, 'name'); + const tradingName = this.safeString (market, 'trading_name'); + const baseId = tradingName; + const quoteId = this.safeString (market, 'pricing_name'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + let symbol = base + '/' + quote; + if (tradingName === id) { + symbol = id; + } + const precision = { + 'amount': this.safeInteger (market, 'trading_decimal'), + 'price': this.safeInteger (market, 'pricing_decimal'), + }; + const active = undefined; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'taker': this.safeFloat (market, 'taker_fee_rate'), + 'maker': this.safeFloat (market, 'maker_fee_rate'), + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + ticker = this.safeValue (ticker, 'ticker', {}); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat2 (ticker, 'vol', 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketTicker (this.extend (request, params)); + return this.parseTicker (response['data'], market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketTickerAll (params); + const data = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (data, 'date'); + const tickers = this.safeValue (data, 'ticker'); + const marketIds = Object.keys (tickers); + const result = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = marketId; + let market = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + const ticker = { + 'date': timestamp, + 'ticker': tickers[marketId], + }; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchOrderBook (symbol, limit = 20, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 20; // default + } + const request = { + 'market': this.marketId (symbol), + 'merge': '0.0000000001', + 'limit': limit.toString (), + }; + const response = await this.publicGetMarketDepth (this.extend (request, params)); + return this.parseOrderBook (response['data']); + } + + parseTrade (trade, market = undefined) { + // this method parses both public and private trades + let timestamp = this.safeTimestamp (trade, 'create_time'); + if (timestamp === undefined) { + timestamp = this.safeInteger (trade, 'date_ms'); + } + const tradeId = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'order_id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const marketId = this.safeString (trade, 'market'); + market = this.safeValue (this.markets_by_id, marketId, market); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = this.safeFloat (trade, 'deal_money'); + if (!cost) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fee_asset'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const takerOrMaker = this.safeString (trade, 'role'); + const side = this.safeString (trade, 'type'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': tradeId, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketDeals (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + parseFloat (ohlcv[1]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'type': this.timeframes[timeframe], + }; + const response = await this.publicGetMarketKline (this.extend (request, params)); + return this.parseOHLCVs (response['data'], market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalanceInfo (params); + // + // { + // "code": 0, + // "data": { + // "BCH": { # BCH account + // "available": "13.60109", # Available BCH + // "frozen": "0.00000" # Frozen BCH + // }, + // "BTC": { # BTC account + // "available": "32590.16", # Available BTC + // "frozen": "7000.00" # Frozen BTC + // }, + // "ETH": { # ETH account + // "available": "5.06000", # Available ETH + // "frozen": "0.00000" # Frozen ETH + // } + // }, + // "message": "Ok" + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'frozen'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'not_deal': 'open', + 'part_deal': 'open', + 'done': 'closed', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder + // + // { + // "amount": "0.1", + // "asset_fee": "0.22736197736197736197", + // "avg_price": "196.85000000000000000000", + // "create_time": 1537270135, + // "deal_amount": "0.1", + // "deal_fee": "0", + // "deal_money": "19.685", + // "fee_asset": "CET", + // "fee_discount": "0.5", + // "id": 1788259447, + // "left": "0", + // "maker_fee_rate": "0", + // "market": "ETHUSDT", + // "order_type": "limit", + // "price": "170.00000000", + // "status": "done", + // "taker_fee_rate": "0.0005", + // "type": "sell", + // } + // + const timestamp = this.safeTimestamp (order, 'create_time'); + const price = this.safeFloat (order, 'price'); + const cost = this.safeFloat (order, 'deal_money'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'deal_amount'); + const average = this.safeFloat (order, 'avg_price'); + let symbol = undefined; + const marketId = this.safeString (order, 'market'); + market = this.safeValue (this.markets_by_id, marketId); + const feeCurrencyId = this.safeString (order, 'fee_asset'); + let feeCurrency = this.safeCurrencyCode (feeCurrencyId); + if (market !== undefined) { + symbol = market['symbol']; + if (feeCurrency === undefined) { + feeCurrency = market['quote']; + } + } + const remaining = this.safeFloat (order, 'left'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const type = this.safeString (order, 'order_type'); + const side = this.safeString (order, 'type'); + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': { + 'currency': feeCurrency, + 'cost': this.safeFloat (order, 'deal_fee'), + }, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostOrder' + this.capitalize (type); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'type': side, + }; + amount = parseFloat (amount); + // for market buy it requires the amount of quote currency to spend + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + price = parseFloat (price); + request['amount'] = this.costToPrecision (symbol, amount * price); + } + } else { + request['amount'] = this.costToPrecision (symbol, amount); + } + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + if ((type === 'limit') || (type === 'ioc')) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this[method] (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'market': market['id'], + }; + const response = await this.privateDeleteOrderPending (this.extend (request, params)); + return this.parseOrder (response['data'], market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'market': market['id'], + }; + const response = await this.privateGetOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "data": { + // "amount": "0.1", + // "asset_fee": "0.22736197736197736197", + // "avg_price": "196.85000000000000000000", + // "create_time": 1537270135, + // "deal_amount": "0.1", + // "deal_fee": "0", + // "deal_money": "19.685", + // "fee_asset": "CET", + // "fee_discount": "0.5", + // "id": 1788259447, + // "left": "0", + // "maker_fee_rate": "0", + // "market": "ETHUSDT", + // "order_type": "limit", + // "price": "170.00000000", + // "status": "done", + // "taker_fee_rate": "0.0005", + // "type": "sell", + // }, + // "message": "Ok" + // } + // + return this.parseOrder (response['data'], market); + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const request = { + 'page': 1, + 'limit': limit, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const method = 'privateGetOrder' + this.capitalize (status); + const response = await this[method] (this.extend (request, params)); + return this.parseOrders (response['data']['data'], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('pending', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('finished', symbol, since, limit, params); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const request = { + 'page': 1, + 'limit': limit, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateGetOrderUserDeals (this.extend (request, params)); + return this.parseTrades (response['data']['data'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (tag) { + address = address + ':' + tag; + } + const request = { + 'coin_type': currency['id'], + 'coin_address': address, // must be authorized, inter-user transfer by a registered mobile phone number or an email address is supported + 'actual_amount': parseFloat (amount), // the actual amount without fees, https://www.coinex.com/fees + 'transfer_method': '1', // '1' = normal onchain transfer, '2' = internal local transfer from one user to another + }; + const response = await this.privatePostBalanceCoinWithdraw (this.extend (request, params)); + // + // { + // "code": 0, + // "data": { + // "actual_amount": "1.00000000", + // "amount": "1.00000000", + // "coin_address": "1KAv3pazbTk2JnQ5xTo6fpKK7p1it2RzD4", + // "coin_type": "BCH", + // "coin_withdraw_id": 206, + // "confirmations": 0, + // "create_time": 1524228297, + // "status": "audit", + // "tx_fee": "0", + // "tx_id": "" + // }, + // "message": "Ok" + // } + // + const transaction = this.safeValue (response, 'data', {}); + return this.parseTransaction (transaction, currency); + } + + parseTransactionStatus (status) { + const statuses = { + 'audit': 'pending', + 'pass': 'pending', + 'processing': 'pending', + 'confirming': 'pending', + 'not_pass': 'failed', + 'cancel': 'canceled', + 'finish': 'ok', + 'fail': 'failed', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "actual_amount": "120.00000000", + // "actual_amount_display": "120", + // "add_explorer": "XXX", + // "amount": "120.00000000", + // "amount_display": "120", + // "coin_address": "XXXXXXXX", + // "coin_address_display": "XXXXXXXX", + // "coin_deposit_id": 1866, + // "coin_type": "USDT", + // "confirmations": 0, + // "create_time": 1539595701, + // "explorer": "", + // "remark": "", + // "status": "finish", + // "status_display": "finish", + // "transfer_method": "local", + // "tx_id": "", + // "tx_id_display": "XXXXXXXXXX" + // } + // + // fetchWithdrawals + // + // { + // "actual_amount": "0.10000000", + // "amount": "0.10000000", + // "coin_address": "15sr1VdyXQ6sVLqeJUJ1uPzLpmQtgUeBSB", + // "coin_type": "BCH", + // "coin_withdraw_id": 203, + // "confirmations": 11, + // "create_time": 1515806440, + // "status": "finish", + // "tx_fee": "0", + // "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6" + // } + // + const id = this.safeString2 (transaction, 'coin_withdraw_id', 'coin_deposit_id'); + const address = this.safeString (transaction, 'coin_address'); + let tag = this.safeString (transaction, 'remark'); // set but unused + if (tag !== undefined) { + if (tag.length < 1) { + tag = undefined; + } + } + let txid = this.safeValue (transaction, 'tx_id'); + if (txid !== undefined) { + if (txid.length < 1) { + txid = undefined; + } + } + const currencyId = this.safeString (transaction, 'coin_type'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeTimestamp (transaction, 'create_time'); + const type = ('coin_withdraw_id' in transaction) ? 'withdraw' : 'deposit'; + const status = this.parseTransactionStatus (this.safeString (transaction, 'status'), type); + const amount = this.safeFloat (transaction, 'amount'); + let feeCost = this.safeFloat (transaction, 'tx_fee'); + if (type === 'deposit') { + feeCost = 0; + } + const fee = { + 'cost': feeCost, + 'currency': code, + }; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin_type': currency['id'], + }; + if (limit !== undefined) { + request['Limit'] = limit; + } + const response = await this.privateGetBalanceCoinWithdraw (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "actual_amount": "1.00000000", + // "amount": "1.00000000", + // "coin_address": "1KAv3pazbTk2JnQ5xTo6fpKK7p1it2RzD4", + // "coin_type": "BCH", + // "coin_withdraw_id": 206, + // "confirmations": 0, + // "create_time": 1524228297, + // "status": "audit", + // "tx_fee": "0", + // "tx_id": "" + // }, + // { + // "actual_amount": "0.10000000", + // "amount": "0.10000000", + // "coin_address": "15sr1VdyXQ6sVLqeJUJ1uPzLpmQtgUeBSB", + // "coin_type": "BCH", + // "coin_withdraw_id": 203, + // "confirmations": 11, + // "create_time": 1515806440, + // "status": "finish", + // "tx_fee": "0", + // "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6" + // }, + // { + // "actual_amount": "0.00100000", + // "amount": "0.00100000", + // "coin_address": "1GVVx5UBddLKrckTprNi4VhHSymeQ8tsLF", + // "coin_type": "BCH", + // "coin_withdraw_id": 27, + // "confirmations": 0, + // "create_time": 1513933541, + // "status": "cancel", + // "tx_fee": "0", + // "tx_id": "" + // } + // ], + // "message": "Ok" + // } + // + return this.parseTransactions (response['data'], currency, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin_type': currency['id'], + }; + if (limit !== undefined) { + request['Limit'] = limit; + } + const response = await this.privateGetBalanceCoinDeposit (this.extend (request, params)); + // { + // "code": 0, + // "data": [ + // { + // "actual_amount": "4.65397682", + // "actual_amount_display": "4.65397682", + // "add_explorer": "https://etherscan.io/address/0x361XXXXXX", + // "amount": "4.65397682", + // "amount_display": "4.65397682", + // "coin_address": "0x36dabcdXXXXXX", + // "coin_address_display": "0x361X*****XXXXX", + // "coin_deposit_id": 966191, + // "coin_type": "ETH", + // "confirmations": 30, + // "create_time": 1531661445, + // "explorer": "https://etherscan.io/tx/0x361XXXXXX", + // "remark": "", + // "status": "finish", + // "status_display": "finish", + // "transfer_method": "onchain", + // "tx_id": "0x361XXXXXX", + // "tx_id_display": "0x361XXXXXX" + // } + // ], + // "message": "Ok" + // } + // + return this.parseTransactions (response['data'], currency, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + path = this.implodeParams (path, params); + let url = this.urls['api'] + '/' + this.version + '/' + path; + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + query = this.extend ({ + 'access_id': this.apiKey, + 'tonce': nonce.toString (), + }, query); + query = this.keysort (query); + const urlencoded = this.urlencode (query); + const signature = this.hash (this.encode (urlencoded + '&secret_key=' + this.secret)); + headers = { + 'Authorization': signature.toUpperCase (), + 'Content-Type': 'application/json', + }; + if ((method === 'GET') || (method === 'DELETE')) { + url += '?' + urlencoded; + } else { + body = this.json (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const code = this.safeString (response, 'code'); + const data = this.safeValue (response, 'data'); + const message = this.safeString (response, 'message'); + if ((code !== '0') || (data === undefined) || ((message !== 'Ok') && !data)) { + const responseCodes = { + '24': AuthenticationError, + '25': AuthenticationError, + '107': InsufficientFunds, + '600': OrderNotFound, + '601': InvalidOrder, + '602': InvalidOrder, + '606': InvalidOrder, + }; + const ErrorClass = this.safeValue (responseCodes, code, ExchangeError); + throw new ErrorClass (response['message']); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],68:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, RateLimitExceeded, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinfalcon extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinfalcon', + 'name': 'CoinFalcon', + 'countries': [ 'GB' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'fetchTickers': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/41822275-ed982188-77f5-11e8-92bb-496bcd14ca52.jpg', + 'api': 'https://coinfalcon.com', + 'www': 'https://coinfalcon.com', + 'doc': 'https://docs.coinfalcon.com', + 'fees': 'https://coinfalcon.com/fees', + 'referral': 'https://coinfalcon.com/?ref=CFJSVGTUPASB', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'markets/{market}/orders', + 'markets/{market}/trades', + ], + }, + 'private': { + 'get': [ + 'user/accounts', + 'user/orders', + 'user/orders/{id}', + 'user/trades', + ], + 'post': [ + 'user/orders', + ], + 'delete': [ + 'user/orders/{id}', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'maker': 0.0, + 'taker': 0.002, // tiered fee starts at 0.2% + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + const markets = this.safeValue (response, 'data'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const [ baseId, quoteId ] = market['name'].split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'size_precision'), + 'price': this.safeInteger (market, 'price_precision'), + }; + result.push ({ + 'id': market['name'], + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + if (market === undefined) { + const marketId = this.safeString (ticker, 'name'); + market = this.safeValue (this.markets_by_id, marketId, market); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const last = parseFloat (ticker['last_price']); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'change_in_24h'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const tickers = await this.fetchTickers (params); + return tickers[symbol]; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarkets (params); + const tickers = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'level': '3', + }; + const response = await this.publicGetMarketsMarketOrders (this.extend (request, params)); + return this.parseOrderBook (response['data'], undefined, 'bids', 'asks', 'price', 'size'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + const symbol = market['symbol']; + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const tradeId = this.safeString (trade, 'id'); + const side = this.safeString (trade, 'side'); + const orderId = this.safeString (trade, 'order_id'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyCode = this.safeString (trade, 'fee_currency_code'); + fee = { + 'cost': feeCost, + 'currency': this.safeCurrencyCode (feeCurrencyCode), + }; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': tradeId, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (since !== undefined) { + request['start_time'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetUserTrades (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (since !== undefined) { + request['since'] = this.iso8601 (since); + } + const response = await this.publicGetMarketsMarketTrades (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetUserAccounts (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'available_balance'), + 'used': this.safeFloat (balance, 'hold_balance'), + 'total': this.safeFloat (balance, 'balance'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'fulfilled': 'closed', + 'canceled': 'canceled', + 'pending': 'open', + 'open': 'open', + 'partially_filled': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + if (market === undefined) { + const marketId = this.safeString (order, 'market'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat (order, 'size_filled'); + let remaining = undefined; + let cost = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); + } + if (price !== undefined) { + cost = parseFloat (this.priceToPrecision (symbol, filled * price)); + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let type = this.safeString (order, 'operation_type'); + if (type !== undefined) { + type = type.split ('_'); + type = type[0]; + } + const side = this.safeString (order, 'order_type'); + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // price/size must be string + const request = { + 'market': market['id'], + 'size': this.amountToPrecision (symbol, amount), + 'order_type': side, + }; + if (type === 'limit') { + price = this.priceToPrecision (symbol, price); + request['price'] = price.toString (); + } + request['operation_type'] = type + '_order'; + const response = await this.privatePostUserOrders (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateDeleteUserOrdersId (this.extend (request, params)); + const market = this.market (symbol); + return this.parseOrder (response['data'], market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetUserOrdersId (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbol !== undefined) { + request['market'] = this.marketId (symbol); + } + if (since !== undefined) { + request['since_time'] = this.iso8601 (this.milliseconds ()); + } + // TODO: test status=all if it works for closed orders too + const response = await this.privateGetUserOrders (this.extend (request, params)); + return this.parseOrders (response['data']); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/api/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + if (method === 'GET') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + } + const seconds = this.seconds ().toString (); + let payload = [ seconds, method, request ].join ('|'); + if (body) { + payload += '|' + body; + } + const signature = this.hmac (this.encode (payload), this.encode (this.secret)); + headers = { + 'CF-API-KEY': this.apiKey, + 'CF-API-TIMESTAMP': seconds, + 'CF-API-SIGNATURE': signature, + 'Content-Type': 'application/json', + }; + } + const url = this.urls['api'] + request; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code < 400) { + return; + } + const ErrorClass = this.safeValue ({ + '401': AuthenticationError, + '429': RateLimitExceeded, + }, code, ExchangeError); + throw new ErrorClass (body); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],69:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InsufficientFunds, ExchangeError, InvalidNonce, InvalidOrder, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinfloor extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinfloor', + 'name': 'coinfloor', + 'rateLimit': 1000, + 'countries': [ 'UK' ], + 'has': { + 'CORS': false, + 'fetchOpenOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28246081-623fc164-6a1c-11e7-913f-bac0d5576c90.jpg', + 'api': 'https://webapi.coinfloor.co.uk/bist', + 'www': 'https://www.coinfloor.co.uk', + 'doc': [ + 'https://github.com/coinfloor/api', + 'https://www.coinfloor.co.uk/api', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + 'password': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + '{id}/ticker/', + '{id}/order_book/', + '{id}/transactions/', + ], + }, + 'private': { + 'post': [ + '{id}/balance/', + '{id}/user_transactions/', + '{id}/open_orders/', + '{symbol}/cancel_order/', + '{id}/buy/', + '{id}/sell/', + '{id}/buy_market/', + '{id}/sell_market/', + '{id}/estimate_sell_market/', + '{id}/estimate_buy_market/', + ], + }, + }, + 'markets': { + 'BTC/GBP': { 'id': 'XBT/GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'XBT', 'quoteId': 'GBP', 'precision': { 'price': 0, 'amount': 4 }}, + 'BTC/EUR': { 'id': 'XBT/EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'XBT', 'quoteId': 'EUR', 'precision': { 'price': 0, 'amount': 4 }}, + 'ETH/GBP': { 'id': 'ETH/GBP', 'symbol': 'ETH/GBP', 'base': 'ETH', 'quote': 'GBP', 'baseId': 'ETH', 'quoteId': 'GBP', 'precision': { 'price': 0, 'amount': 4 }}, + }, + 'exceptions': { + 'exact': { + 'You have insufficient funds.': InsufficientFunds, + 'Tonce is out of sequence.': InvalidNonce, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + let market = undefined; + let query = params; + const symbol = this.safeString (params, 'symbol'); + if (symbol !== undefined) { + market = this.market (params['symbol']); + query = this.omit (params, 'symbol'); + } + const marketId = this.safeString (params, 'id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market === undefined) { + throw new ArgumentsRequired (this.id + ' fetchBalance requires a symbol param'); + } + const request = { + 'id': market['id'], + }; + const response = await this.privatePostIdBalance (this.extend (request, query)); + const result = { + 'info': response, + }; + // base/quote used for keys e.g. "xbt_reserved" + const base = market['base']; + const quote = market['quote']; + const baseIdLower = this.safeStringLower (market, 'baseId'); + const quoteIdLower = this.safeStringLower (market, 'quoteId'); + result[base] = { + 'free': this.safeFloat (response, baseIdLower + '_available'), + 'used': this.safeFloat (response, baseIdLower + '_reserved'), + 'total': this.safeFloat (response, baseIdLower + '_balance'), + }; + result[quote] = { + 'free': this.safeFloat (response, quoteIdLower + '_available'), + 'used': this.safeFloat (response, quoteIdLower + '_reserved'), + 'total': this.safeFloat (response, quoteIdLower + '_balance'), + }; + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetIdOrderBook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + // rewrite to get the timestamp from HTTP headers + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetIdTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetIdTransactions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + // code is actually a market symbol in this situation, not a currency code + await this.loadMarkets (); + let market = undefined; + if (code !== undefined) { + market = this.market (code); + if (market === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions requires a code argument (a market symbol)'); + } + } + const request = { + 'id': market['id'], + 'limit': limit, + }; + const response = await this.privatePostIdUserTransactions (this.extend (request, params)); + return this.parseLedger (response, undefined, since, undefined); + } + + parseLedgerEntryStatus (status) { + const types = { + 'completed': 'ok', + }; + return this.safeString (types, status, status); + } + + parseLedgerEntryType (type) { + const types = { + '0': 'transaction', // deposit + '1': 'transaction', // withdrawal + '2': 'trade', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // trade + // + // { + // "datetime": "2017-07-25 06:41:24", + // "id": 1500964884381265, + // "type": 2, + // "xbt": "0.1000", + // "xbt_eur": "2322.00", + // "eur": "-232.20", + // "fee": "0.00", + // "order_id": 84696745 + // } + // + // transaction (withdrawal) + // + // { + // "datetime": "2017-07-25 13:19:46", + // "id": 97669, + // "type": 1, + // "xbt": "-3.0000", + // "xbt_eur": null, + // "eur": "0", + // "fee": "0.0000", + // "order_id": null + // } + // + // transaction (deposit) + // + // { + // "datetime": "2017-07-27 16:44:55", + // "id": 98277, + // "type": 0, + // "xbt": "0", + // "xbt_eur": null, + // "eur": "4970.04", + // "fee": "0.00", + // "order_id": null + // } + // + const keys = Object.keys (item); + let baseId = undefined; + let quoteId = undefined; + let baseAmount = undefined; + let quoteAmount = undefined; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (key.indexOf ('_') > 0) { + const parts = key.split ('_'); + const numParts = parts.length; + if (numParts === 2) { + const tmpBaseAmount = this.safeFloat (item, parts[0]); + const tmpQuoteAmount = this.safeFloat (item, parts[1]); + if (tmpBaseAmount !== undefined && tmpQuoteAmount !== undefined) { + baseId = parts[0]; + quoteId = parts[1]; + baseAmount = tmpBaseAmount; + quoteAmount = tmpQuoteAmount; + } + } + } + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const referenceId = this.safeString (item, 'id'); + const timestamp = this.parse8601 (this.safeString (item, 'datetime')); + let fee = undefined; + const feeCost = this.safeFloat (item, 'fee'); + const result = { + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'amount': undefined, + 'direction': undefined, + 'currency': undefined, + 'type': type, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'before': undefined, + 'after': undefined, + 'status': 'ok', + 'fee': fee, + 'info': item, + }; + if (type === 'trade') { + // + // it's a trade so let's make multiple entries, we have several options: + // + // if fee is always in quote currency (the exchange uses this) + // https://github.com/coinfloor/API/blob/master/IMPL-GUIDE.md#how-fees-affect-trade-quantities + // + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': quote, + }; + } + return [ + this.extend (result, { 'currency': base, 'amount': Math.abs (baseAmount), 'direction': (baseAmount > 0) ? 'in' : 'out' }), + this.extend (result, { 'currency': quote, 'amount': Math.abs (quoteAmount), 'direction': (quoteAmount > 0) ? 'in' : 'out', 'fee': fee }), + ]; + // + // if fee is base or quote depending on buy/sell side + // + // const baseFee = (baseAmount > 0) ? { 'currency': base, 'cost': feeCost } : undefined; + // const quoteFee = (quoteAmount > 0) ? { 'currency': quote, 'cost': feeCost } : undefined; + // return [ + // this.extend (result, { 'currency': base, 'amount': baseAmount, 'direction': (baseAmount > 0) ? 'in' : 'out', 'fee': baseFee }), + // this.extend (result, { 'currency': quote, 'amount': quoteAmount, 'direction': (quoteAmount > 0) ? 'in' : 'out', 'fee': quoteFee }), + // ]; + // + // fee as the 3rd item + // + // return [ + // this.extend (result, { 'currency': base, 'amount': baseAmount, 'direction': (baseAmount > 0) ? 'in' : 'out' }), + // this.extend (result, { 'currency': quote, 'amount': quoteAmount, 'direction': (quoteAmount > 0) ? 'in' : 'out' }), + // this.extend (result, { 'currency': feeCurrency, 'amount': feeCost, 'direction': 'out', 'type': 'fee' }), + // ]; + // + } else { + // + // it's a regular transaction (deposit or withdrawal) + // + const amount = (baseAmount === 0) ? quoteAmount : baseAmount; + const code = (baseAmount === 0) ? quote : base; + const direction = (amount > 0) ? 'in' : 'out'; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': code, + }; + } + return this.extend (result, { + 'currency': code, + 'amount': Math.abs (amount), + 'direction': direction, + 'fee': fee, + }); + } + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + let method = 'privatePostId' + this.capitalize (side); + if (type === 'market') { + request['quantity'] = amount; + method += 'Market'; + } else { + request['price'] = price; + request['amount'] = amount; + } + return await this[method] (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'id': id, + }; + const response = await this.privatePostSymbolCancelOrder (request); + if (response === 'false') { + // unfortunately the exchange does not give much info in the response + throw new InvalidOrder (this.id + ' cancel was rejected'); + } + return response; + } + + parseOrder (order, market = undefined) { + const timestamp = this.parse8601 (this.safeString (order, 'datetime')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let side = undefined; + const status = this.safeString (order, 'status'); + if (order['type'] === 0) { + side = 'buy'; + } else if (order['type'] === 1) { + side = 'sell'; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol param'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.privatePostIdOpenOrders (this.extend (request, params)); + // { + // "amount": "1.0000", + // "datetime": "2019-07-12 13:28:16", + // "id": 233123443, + // "price": "1000.00", + // "type": 0 + // } + return this.parseOrders (response, market, since, limit, { 'status': 'open' }); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code < 400) { + return; + } + if (response === undefined) { + return; + } + const message = this.safeString (response, 'error_msg'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (feedback); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + // curl -k -u '[User ID]/[API key]:[Passphrase]' https://webapi.coinfloor.co.uk:8090/bist/XBT/GBP/balance/ + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); + const auth = this.uid + '/' + this.apiKey + ':' + this.password; + const signature = this.decode (this.stringToBase64 (this.encode (auth))); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ' + signature, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],70:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coingi extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coingi', + 'name': 'Coingi', + 'rateLimit': 1000, + 'countries': [ 'PA', 'BG', 'CN', 'US' ], // Panama, Bulgaria, China, US + 'has': { + 'CORS': false, + 'fetchTickers': true, + }, + 'urls': { + 'referral': 'https://www.coingi.com/?r=XTPPMC', + 'logo': 'https://user-images.githubusercontent.com/1294454/28619707-5c9232a8-7212-11e7-86d6-98fe5d15cc6e.jpg', + 'api': { + 'www': 'https://coingi.com', + 'current': 'https://api.coingi.com', + 'user': 'https://api.coingi.com', + }, + 'www': 'https://coingi.com', + 'doc': 'https://coingi.docs.apiary.io', + }, + 'api': { + 'www': { + 'get': [ + '', + ], + }, + 'current': { + 'get': [ + 'order-book/{pair}/{askCount}/{bidCount}/{depth}', + 'transactions/{pair}/{maxCount}', + '24hour-rolling-aggregation', + ], + }, + 'user': { + 'post': [ + 'balance', + 'add-order', + 'cancel-order', + 'orders', + 'transactions', + 'create-crypto-withdrawal', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.2 / 100, + 'maker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'LTC': 0.01, + 'DOGE': 2, + 'PPC': 0.02, + 'VTC': 0.2, + 'NMC': 2, + 'DASH': 0.002, + 'USD': 10, + 'EUR': 10, + }, + 'deposit': { + 'BTC': 0, + 'LTC': 0, + 'DOGE': 0, + 'PPC': 0, + 'VTC': 0, + 'NMC': 0, + 'DASH': 0, + 'USD': 5, + 'EUR': 1, + }, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.wwwGet (params); + const parts = response.split ('do=currencyPairSelector-selectCurrencyPair" class="active">'); + const currencyParts = parts[1].split ('
'); + const result = []; + for (let i = 1; i < currencyParts.length; i++) { + const currencyPart = currencyParts[i]; + const idParts = currencyPart.split ('
'); + let id = idParts[0]; + id = id.replace ('/', '-'); + id = id.toLowerCase (); + const [ baseId, quoteId ] = id.split ('-'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': id, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const lowercaseCurrencies = []; + const currencies = Object.keys (this.currencies); + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + lowercaseCurrencies.push (currency.toLowerCase ()); + } + const request = { + 'currencies': lowercaseCurrencies.join (','), + }; + const response = await this.userPostBalance (this.extend (request, params)); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance['currency'], 'name'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + const blocked = this.safeFloat (balance, 'blocked'); + const inOrders = this.safeFloat (balance, 'inOrders'); + const withdrawing = this.safeFloat (balance, 'withdrawing'); + account['used'] = this.sum (blocked, inOrders, withdrawing); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = 512, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'depth': 32, // maximum number of depth range steps 1-32 + 'askCount': limit, // maximum returned number of asks 1-512 + 'bidCount': limit, // maximum returned number of bids 1-512 + }; + const orderbook = await this.currentGetOrderBookPairAskCountBidCountDepth (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'baseAmount'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': undefined, + 'last': undefined, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'counterVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.currentGet24hourRollingAggregation (params); + const result = {}; + for (let t = 0; t < response.length; t++) { + const ticker = response[t]; + const base = ticker['currencyPair']['base'].toUpperCase (); + const quote = ticker['currencyPair']['counter'].toUpperCase (); + const symbol = base + '/' + quote; + let market = undefined; + if (symbol in this.markets) { + market = this.markets[symbol]; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const tickers = await this.fetchTickers (undefined, params); + if (symbol in tickers) { + return tickers[symbol]; + } + throw new ExchangeError (this.id + ' return did not contain ' + symbol); + } + + parseTrade (trade, market = undefined) { + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const timestamp = this.safeInteger (trade, 'timestamp'); + const id = this.safeString (trade, 'id'); + const marketId = this.safeString (trade, 'currencyPair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, // type + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'maxCount': 128, + }; + const response = await this.currentGetTransactionsPairMaxCount (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + 'volume': amount, + 'price': price, + 'orderType': (side === 'buy') ? 0 : 1, + }; + const response = await this.userPostAddOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response['result'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + return await this.userPostCancelOrder (this.extend (request, params)); + } + + sign (path, api = 'current', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api !== 'www') { + url += '/' + api + '/' + this.implodeParams (path, params); + } + const query = this.omit (params, this.extractParams (path)); + if (api === 'current') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'user') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = this.extend ({ + 'token': this.apiKey, + 'nonce': nonce, + }, query); + const auth = nonce.toString () + '$' + this.apiKey; + request['signature'] = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.json (request); + headers = { + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'current', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response !== 'string') { + if ('errors' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],71:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinmarketcap extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinmarketcap', + 'name': 'CoinMarketCap', + 'rateLimit': 10000, + 'version': 'v1', + 'countries': [ 'US' ], + 'has': { + 'CORS': true, + 'privateAPI': false, + 'createOrder': false, + 'createMarketOrder': false, + 'createLimitOrder': false, + 'cancelOrder': false, + 'editOrder': false, + 'fetchBalance': false, + 'fetchOrderBook': false, + 'fetchL2OrderBook': false, + 'fetchOHLCV': false, + 'fetchTrades': false, + 'fetchTickers': true, + 'fetchCurrencies': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28244244-9be6312a-69ed-11e7-99c1-7c1797275265.jpg', + 'api': { + 'public': 'https://api.coinmarketcap.com', + 'files': 'https://files.coinmarketcap.com', + 'charts': 'https://graph.coinmarketcap.com', + }, + 'www': 'https://coinmarketcap.com', + 'doc': 'https://coinmarketcap.com/api', + }, + 'requiredCredentials': { + 'apiKey': false, + 'secret': false, + }, + 'api': { + 'files': { + 'get': [ + 'generated/stats/global.json', + ], + }, + 'graphs': { + 'get': [ + 'currencies/{name}/', + ], + }, + 'public': { + 'get': [ + 'ticker/', + 'ticker/{id}/', + 'global/', + ], + }, + }, + 'currencyCodes': [ + 'AUD', + 'BRL', + 'CAD', + 'CHF', + 'CNY', + 'EUR', + 'GBP', + 'HKD', + 'IDR', + 'INR', + 'JPY', + 'KRW', + 'MXN', + 'RUB', + 'USD', + 'BTC', + 'ETH', + 'LTC', + ], + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + throw new ExchangeError ('Fetching order books is not supported by the API of ' + this.id); + } + + currencyCode (base, name) { + const currencies = { + 'ACChain': 'ACChain', + 'AdCoin': 'AdCoin', + 'BatCoin': 'BatCoin', + 'Bitgem': 'Bitgem', + 'BlazeCoin': 'BlazeCoin', + 'BlockCAT': 'BlockCAT', + 'Blocktrade Token': 'Blocktrade Token', + 'Catcoin': 'Catcoin', + 'CanYaCoin': 'CanYaCoin', // conflict with CAN (Content and AD Network) + 'Comet': 'Comet', // conflict with CMT (CyberMiles) + 'CPChain': 'CPChain', + 'CrowdCoin': 'CrowdCoin', // conflict with CRC CryCash + 'Cryptaur': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 + 'Cubits': 'Cubits', // conflict with QBT (Qbao) + 'DAO.Casino': 'DAO.Casino', // conflict with BET (BetaCoin) + 'E-Dinar Coin': 'E-Dinar Coin', // conflict with EDR Endor Protocol and EDRCoin + 'EDRcoin': 'EDRcoin', // conflict with EDR Endor Protocol and E-Dinar Coin + 'ENTCash': 'ENTCash', // conflict with ENT (Eternity) + 'FairCoin': 'FairCoin', // conflict with FAIR (FairGame) https://github.com/ccxt/ccxt/pull/5865 + 'Fabric Token': 'Fabric Token', + // 'GET Protocol': 'GET Protocol', + 'Global Tour Coin': 'Global Tour Coin', // conflict with GTC (Game.com) + 'GuccioneCoin': 'GuccioneCoin', // conflict with GCC (Global Cryptocurrency) + 'HarmonyCoin': 'HarmonyCoin', // conflict with HMC (Hi Mutual Society) + 'Harvest Masternode Coin': 'Harvest Masternode Coin', // conflict with HC (HyperCash) + 'HOT Token': 'HOT Token', + 'Hydro Protocol': 'Hydro Protocol', // conflict with HOT (Holo) + 'Huncoin': 'Huncoin', // conflict with HNC (Helleniccoin) + 'iCoin': 'iCoin', + 'Infinity Economics': 'Infinity Economics', // conflict with XIN (Mixin) + 'KingN Coin': 'KingN Coin', // conflict with KNC (Kyber Network) + 'LiteBitcoin': 'LiteBitcoin', // conflict with LBTC (LightningBitcoin) + 'Maggie': 'Maggie', + 'MTC Mesh Network': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025 + 'IOTA': 'IOTA', // a special case, most exchanges list it as IOTA, therefore we change just the Coinmarketcap instead of changing them all + 'NetCoin': 'NetCoin', + 'PCHAIN': 'PCHAIN', // conflict with PAI (Project Pai) + 'Polcoin': 'Polcoin', + 'PutinCoin': 'PutinCoin', // conflict with PUT (Profile Utility Token) + 'Rcoin': 'Rcoin', // conflict with RCN (Ripio Credit Network) + // https://github.com/ccxt/ccxt/issues/6081 + // https://github.com/ccxt/ccxt/issues/3365 + // https://github.com/ccxt/ccxt/issues/2873 + 'Themis': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol) + 'Menlo One': 'Menlo One', // conflict with Harmony (ONE) + 'BigONE Token': 'BigONE Token', // conflict with Harmony (ONE) + }; + return this.safeValue (currencies, name, base); + } + + async fetchMarkets (params = {}) { + const request = { + 'limit': 0, + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const currencies = this.currencyCodes; + for (let j = 0; j < currencies.length; j++) { + const quote = currencies[j]; + const quoteId = quote.toLowerCase (); + const baseId = market['id']; + const base = this.currencyCode (market['symbol'], market['name']); + const symbol = base + '/' + quote; + const id = baseId + '/' + quoteId; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + } + return result; + } + + async fetchGlobal (currency = 'USD') { + await this.loadMarkets (); + const request = {}; + if (currency) { + request['convert'] = currency; + } + return await this.publicGetGlobal (request); + } + + parseTicker (ticker, market = undefined) { + let timestamp = this.safeTimestamp (ticker, 'last_updated'); + if (timestamp === undefined) { + timestamp = this.milliseconds (); + } + const change = this.safeFloat (ticker, 'percent_change_24h'); + let last = undefined; + let symbol = undefined; + let volume = undefined; + if (market !== undefined) { + symbol = market['symbol']; + const priceKey = 'price_' + market['quoteId']; + last = this.safeFloat (ticker, priceKey); + const volumeKey = '24h_volume_' + market['quoteId']; + volume = this.safeFloat (ticker, volumeKey); + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': change, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': volume, + 'info': ticker, + }; + } + + async fetchTickers (currency = 'USD', params = {}) { + await this.loadMarkets (); + const request = { + 'limit': 10000, + }; + if (currency) { + request['convert'] = currency; + } + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + for (let t = 0; t < response.length; t++) { + const ticker = response[t]; + const currencyId = currency.toLowerCase (); + const id = ticker['id'] + '/' + currencyId; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'convert': market['quote'], + 'id': market['baseId'], + }; + const response = await this.publicGetTickerId (this.extend (request, params)); + const ticker = response[0]; + return this.parseTicker (ticker, market); + } + + async fetchCurrencies (params = {}) { + const request = { + 'limit': 0, + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const name = this.safeString (currency, 'name'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const precision = 8; // default precision, todo: fix "magic constants" + const code = this.currencyCode (id, name); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': name, + 'active': true, + 'fee': undefined, // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + if (response['error']) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],72:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinmate extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinmate', + 'name': 'CoinMate', + 'countries': [ 'GB', 'CZ', 'EU' ], // UK, Czech Republic + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'fetchMyTrades': true, + 'fetchTransactions': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27811229-c1efb510-606c-11e7-9a36-84ba2ce412d8.jpg', + 'api': 'https://coinmate.io/api', + 'www': 'https://coinmate.io', + 'fees': 'https://coinmate.io/fees', + 'doc': [ + 'https://coinmate.docs.apiary.io', + 'https://coinmate.io/developers', + ], + 'referral': 'https://coinmate.io?referral=YTFkM1RsOWFObVpmY1ZjMGREQmpTRnBsWjJJNVp3PT0', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'orderBook', + 'ticker', + 'transactions', + 'tradingPairs', + ], + }, + 'private': { + 'post': [ + 'balances', + 'bitcoinCashWithdrawal', + 'bitcoinCashDepositAddresses', + 'bitcoinDepositAddresses', + 'bitcoinWithdrawal', + 'bitcoinWithdrawalFees', + 'buyInstant', + 'buyLimit', + 'cancelOrder', + 'cancelOrderWithInfo', + 'createVoucher', + 'dashDepositAddresses', + 'dashWithdrawal', + 'ethereumWithdrawal', + 'ethereumDepositAddresses', + 'litecoinWithdrawal', + 'litecoinDepositAddresses', + 'openOrders', + 'order', + 'orderHistory', + 'pusherAuth', + 'redeemVoucher', + 'replaceByBuyLimit', + 'replaceByBuyInstant', + 'replaceBySellLimit', + 'replaceBySellInstant', + 'rippleDepositAddresses', + 'rippleWithdrawal', + 'sellInstant', + 'sellLimit', + 'transactionHistory', + 'traderFees', + 'tradeHistory', + 'transfer', + 'transferHistory', + 'unconfirmedBitcoinDeposits', + 'unconfirmedBitcoinCashDeposits', + 'unconfirmedDashDeposits', + 'unconfirmedEthereumDeposits', + 'unconfirmedLitecoinDeposits', + 'unconfirmedRippleDeposits', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.05 / 100, + 'taker': 0.15 / 100, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTradingPairs (params); + // + // { + // "error":false, + // "errorMessage":null, + // "data": [ + // { + // "name":"BTC_EUR", + // "firstCurrency":"BTC", + // "secondCurrency":"EUR", + // "priceDecimals":2, + // "lotDecimals":8, + // "minAmount":0.0002, + // "tradesWebSocketChannelId":"trades-BTC_EUR", + // "orderBookWebSocketChannelId":"order_book-BTC_EUR", + // "tradeStatisticsWebSocketChannelId":"statistics-BTC_EUR" + // }, + // ] + // } + // + const data = this.safeValue (response, 'data'); + const result = []; + for (let i = 0; i < data.length; i++) { + const market = data[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'firstCurrency'); + const quoteId = this.safeString (market, 'secondCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': market, + 'precision': { + 'price': this.safeInteger (market, 'priceDecimals'), + 'amount': this.safeInteger (market, 'lotDecimals'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalances (params); + const balances = this.safeValue (response, 'data'); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (balances, currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'reserved'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + 'groupByPriceLimit': 'False', + }; + const response = await this.publicGetOrderBook (this.extend (request, params)); + const orderbook = response['data']; + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'data'); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'vwap': undefined, + 'askVolume': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'amount'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'limit': 1000, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['timestampFrom'] = since; + } + if (code !== undefined) { + request['currency'] = this.currencyId (code); + } + const response = await this.privatePostTransferHistory (this.extend (request, params)); + const items = response['data']; + return this.parseTransactions (items, undefined, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + // any other types ? + 'COMPLETED': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (item, currency = undefined) { + // + // deposits + // + // { + // transactionId: 1862815, + // timestamp: 1516803982388, + // amountCurrency: 'LTC', + // amount: 1, + // fee: 0, + // walletType: 'LTC', + // transferType: 'DEPOSIT', + // transferStatus: 'COMPLETED', + // txid: + // 'ccb9255dfa874e6c28f1a64179769164025329d65e5201849c2400abd6bce245', + // destination: 'LQrtSKA6LnhcwRrEuiborQJnjFF56xqsFn', + // destinationTag: null + // } + // + // withdrawals + // + // { + // transactionId: 2140966, + // timestamp: 1519314282976, + // amountCurrency: 'EUR', + // amount: 8421.7228, + // fee: 16.8772, + // walletType: 'BANK_WIRE', + // transferType: 'WITHDRAWAL', + // transferStatus: 'COMPLETED', + // txid: null, + // destination: null, + // destinationTag: null + // } + // + const timestamp = this.safeInteger (item, 'timestamp'); + const amount = this.safeFloat (item, 'amount'); + const fee = this.safeFloat (item, 'fee'); + const txid = this.safeString (item, 'txid'); + const address = this.safeString (item, 'destination'); + const tag = this.safeString (item, 'destinationTag'); + const currencyId = this.safeString (item, 'amountCurrency'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeStringLower (item, 'transferType'); + const status = this.parseTransactionStatus (this.safeString (item, 'transferStatus')); + const id = this.safeString (item, 'transactionId'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'currency': code, + 'amount': amount, + 'type': type, + 'txid': txid, + 'address': address, + 'tag': tag, + 'status': status, + 'fee': { + 'cost': fee, + 'currency': currency, + }, + 'info': item, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 1000; + } + const request = { + 'limit': limit, + }; + if (since !== undefined) { + request['timestampFrom'] = since; + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + const items = response['data']; + return this.parseTrades (items, undefined, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchMyTrades (private) + // + // { + // transactionId: 2671819, + // createdTimestamp: 1529649127605, + // currencyPair: 'LTC_BTC', + // type: 'BUY', + // orderType: 'LIMIT', + // orderId: 101810227, + // amount: 0.01, + // price: 0.01406, + // fee: 0, + // feeType: 'MAKER' + // } + // + // fetchTrades (public) + // + // { + // "timestamp":1561598833416, + // "transactionId":"4156303", + // "price":10950.41, + // "amount":0.004, + // "currencyPair":"BTC_EUR", + // "tradeType":"BUY" + // } + // + let symbol = undefined; + const marketId = this.safeString (trade, 'currencyPair'); + let quote = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id[marketId]) { + market = this.markets_by_id[marketId]; + quote = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const side = this.safeStringLower2 (trade, 'type', 'tradeType'); + const type = this.safeStringLower (trade, 'orderType'); + const orderId = this.safeString (trade, 'orderId'); + const id = this.safeString (trade, 'transactionId'); + const timestamp = this.safeInteger2 (trade, 'timestamp', 'createdTimestamp'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': quote, + }; + } + let takerOrMaker = this.safeString (trade, 'feeType'); + takerOrMaker = (takerOrMaker === 'MAKER') ? 'maker' : 'taker'; + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': orderId, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'minutesIntoHistory': 10, + }; + const response = await this.publicGetTransactions (this.extend (request, params)); + // + // { + // "error":false, + // "errorMessage":null, + // "data":[ + // { + // "timestamp":1561598833416, + // "transactionId":"4156303", + // "price":10950.41, + // "amount":0.004, + // "currencyPair":"BTC_EUR", + // "tradeType":"BUY" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseTrades (data, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let method = 'privatePost' + this.capitalize (side); + const request = { + 'currencyPair': this.marketId (symbol), + }; + if (type === 'market') { + if (side === 'buy') { + request['total'] = amount; // amount in fiat + } else { + request['amount'] = amount; // amount in fiat + } + method += 'Instant'; + } else { + request['amount'] = amount; // amount in crypto + request['price'] = price; + method += this.capitalize (type); + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['data'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostCancelOrder ({ 'orderId': id }); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.urlencode (this.extend ({ + 'clientId': this.uid, + 'nonce': nonce, + 'publicKey': this.apiKey, + 'signature': signature.toUpperCase (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + if (response['error']) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],73:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, BadRequest, ExchangeError, ArgumentsRequired, InvalidOrder, OrderNotFound, OnMaintenance } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinone extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinone', + 'name': 'CoinOne', + 'countries': [ 'KR' ], // Korea + 'rateLimit': 667, + 'version': 'v2', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchTickers': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38003300-adc12fba-323f-11e8-8525-725f53c4a659.jpg', + 'api': 'https://api.coinone.co.kr', + 'www': 'https://coinone.co.kr', + 'doc': 'https://doc.coinone.co.kr', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + }, + 'api': { + 'public': { + 'get': [ + 'orderbook/', + 'trades/', + 'ticker/', + ], + }, + 'private': { + 'post': [ + 'account/btc_deposit_address/', + 'account/balance/', + 'account/daily_balance/', + 'account/user_info/', + 'account/virtual_account/', + 'order/cancel_all/', + 'order/cancel/', + 'order/limit_buy/', + 'order/limit_sell/', + 'order/complete_orders/', + 'order/limit_orders/', + 'order/order_info/', + 'transaction/auth_number/', + 'transaction/history/', + 'transaction/krw/history/', + 'transaction/btc/', + 'transaction/coin/', + ], + }, + }, + 'markets': { + 'BCH/KRW': { 'id': 'bch', 'symbol': 'BCH/KRW', 'base': 'BCH', 'quote': 'KRW', 'baseId': 'bch', 'quoteId': 'krw' }, + 'BTC/KRW': { 'id': 'btc', 'symbol': 'BTC/KRW', 'base': 'BTC', 'quote': 'KRW', 'baseId': 'btc', 'quoteId': 'krw' }, + 'BTG/KRW': { 'id': 'btg', 'symbol': 'BTG/KRW', 'base': 'BTG', 'quote': 'KRW', 'baseId': 'btg', 'quoteId': 'krw' }, + 'ETC/KRW': { 'id': 'etc', 'symbol': 'ETC/KRW', 'base': 'ETC', 'quote': 'KRW', 'baseId': 'etc', 'quoteId': 'krw' }, + 'ETH/KRW': { 'id': 'eth', 'symbol': 'ETH/KRW', 'base': 'ETH', 'quote': 'KRW', 'baseId': 'eth', 'quoteId': 'krw' }, + 'IOTA/KRW': { 'id': 'iota', 'symbol': 'IOTA/KRW', 'base': 'IOTA', 'quote': 'KRW', 'baseId': 'iota', 'quoteId': 'krw' }, + 'LTC/KRW': { 'id': 'ltc', 'symbol': 'LTC/KRW', 'base': 'LTC', 'quote': 'KRW', 'baseId': 'ltc', 'quoteId': 'krw' }, + 'OMG/KRW': { 'id': 'omg', 'symbol': 'OMG/KRW', 'base': 'OMG', 'quote': 'KRW', 'baseId': 'omg', 'quoteId': 'krw' }, + 'QTUM/KRW': { 'id': 'qtum', 'symbol': 'QTUM/KRW', 'base': 'QTUM', 'quote': 'KRW', 'baseId': 'qtum', 'quoteId': 'krw' }, + 'XRP/KRW': { 'id': 'xrp', 'symbol': 'XRP/KRW', 'base': 'XRP', 'quote': 'KRW', 'baseId': 'xrp', 'quoteId': 'krw' }, + 'EOS/KRW': { 'id': 'eos', 'symbol': 'EOS/KRW', 'base': 'EOS', 'quote': 'KRW', 'baseId': 'eos', 'quoteId': 'krw' }, + 'DATA/KRW': { 'id': 'data', 'symbol': 'DATA/KRW', 'base': 'DATA', 'quote': 'KRW', 'baseId': 'data', 'quoteId': 'krw' }, + 'ZIL/KRW': { 'id': 'zil', 'symbol': 'ZIL/KRW', 'base': 'ZIL', 'quote': 'KRW', 'baseId': 'zil', 'quoteId': 'krw' }, + 'KNC/KRW': { 'id': 'knc', 'symbol': 'KNC/KRW', 'base': 'KNC', 'quote': 'KRW', 'baseId': 'knc', 'quoteId': 'krw' }, + 'ZRX/KRW': { 'id': 'zrx', 'symbol': 'ZRX/KRW', 'base': 'ZRX', 'quote': 'KRW', 'baseId': 'zrx', 'quoteId': 'krw' }, + 'LUNA/KRW': { 'id': 'luna', 'symbol': 'LUNA/KRW', 'base': 'LUNA', 'quote': 'KRW', 'baseId': 'luna', 'quoteId': 'krw' }, + 'ATOM/KRW': { 'id': 'atom', 'symbol': 'ATOM/KRW', 'base': 'ATOM', 'quote': 'KRW', 'baseId': 'atom', 'quoteId': 'krw' }, + 'VNT/KRW': { 'id': 'vnt', 'symbol': 'VNT/KRW', 'base': 'VNT', 'quote': 'KRW', 'baseId': 'vnt', 'quoteId': 'krw' }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + 'tiers': { + 'taker': [ + [0, 0.001], + [100000000, 0.0009], + [1000000000, 0.0008], + [5000000000, 0.0007], + [10000000000, 0.0006], + [20000000000, 0.0005], + [30000000000, 0.0004], + [40000000000, 0.0003], + [50000000000, 0.0002], + ], + 'maker': [ + [0, 0.001], + [100000000, 0.0008], + [1000000000, 0.0006], + [5000000000, 0.0004], + [10000000000, 0.0002], + [20000000000, 0], + [30000000000, 0], + [40000000000, 0], + [50000000000, 0], + ], + }, + }, + }, + 'exceptions': { + '405': OnMaintenance, // {"errorCode":"405","status":"maintenance","result":"error"} + '104': OrderNotFound, + '108': BadSymbol, // {"errorCode":"108","errorMsg":"Unknown CryptoCurrency","result":"error"} + '107': BadRequest, // {"errorCode":"107","errorMsg":"Parameter error","result":"error"} + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostAccountBalance (params); + const result = { 'info': response }; + const balances = this.omit (response, [ + 'errorCode', + 'result', + 'normalWallets', + ]); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = balances[currencyId]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'avail'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'format': 'json', + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'qty'); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currency': 'all', + 'format': 'json', + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'format': 'json', + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + const previousClose = this.safeFloat (ticker, 'yesterday_last'); + let change = undefined; + if (last !== undefined && previousClose !== undefined) { + change = previousClose - last; + } + const symbol = (market !== undefined) ? market['symbol'] : undefined; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'first'), + 'close': last, + 'last': last, + 'previousClose': previousClose, + 'change': change, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const symbol = (market !== undefined) ? market['symbol'] : undefined; + const is_ask = this.safeString (trade, 'is_ask'); + let side = undefined; + if (is_ask === '1') { + side = 'sell'; + } else if (is_ask === '0') { + side = 'buy'; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'qty'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'order': undefined, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'period': 'hour', + 'format': 'json', + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response['completeOrders'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const request = { + 'price': price, + 'currency': this.marketId (symbol), + 'qty': amount, + }; + const method = 'privatePostOrder' + this.capitalize (type) + this.capitalize (side); + const response = await this[method] (this.extend (request, params)); + let id = this.safeString (response, 'orderId'); + if (id !== undefined) { + id = id.toUpperCase (); + } + const timestamp = this.milliseconds (); + const cost = price * amount; + const order = { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': undefined, + 'amount': amount, + 'filled': undefined, + 'remaining': amount, + 'status': 'open', + 'fee': undefined, + }; + this.orders[id] = order; + return order; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let result = undefined; + let market = undefined; + if (symbol === undefined) { + if (id in this.orders) { + market = this.market (this.orders[id]['symbol']); + } else { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument for order ids missing in the .orders cache (the order was created with a different instance of this class or within a different run of this code).'); + } + } else { + market = this.market (symbol); + } + try { + const request = { + 'order_id': id, + 'currency': market['id'], + }; + const response = await this.privatePostOrderOrderInfo (this.extend (request, params)); + result = this.parseOrder (response); + this.orders[id] = result; + } catch (e) { + if (e instanceof OrderNotFound) { + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + result = this.orders[id]; + } else { + throw e; + } + } else { + throw e; + } + } + return result; + } + + parseOrderStatus (status) { + const statuses = { + 'live': 'open', + 'partially_filled': 'open', + 'filled': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const info = this.safeValue (order, 'info'); + const id = this.safeStringUpper (info, 'orderId'); + const timestamp = this.safeTimestamp (info, 'timestamp'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let cost = undefined; + let side = this.safeString (info, 'type'); + if (side.indexOf ('ask') >= 0) { + side = 'sell'; + } else { + side = 'buy'; + } + const price = this.safeFloat (info, 'price'); + const amount = this.safeFloat (info, 'qty'); + const remaining = this.safeFloat (info, 'remainQty'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + if (price !== undefined) { + cost = price * amount; + } + } + const currency = this.safeString (info, 'currency'); + const fee = { + 'currency': currency, + 'cost': this.safeFloat (info, 'fee'), + 'rate': this.safeFloat (info, 'feeRate'), + }; + let symbol = undefined; + if (market === undefined) { + const marketId = currency.toLowerCase (); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const order = this.safeValue (this.orders, id); + let amount = undefined; + let price = undefined; + let side = undefined; + if (order === undefined) { + if (symbol === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `symbol` argument is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + price = this.safeFloat (params, 'price'); + if (price === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `price` parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + amount = this.safeFloat (params, 'qty'); + if (amount === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `qty` (amount) parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + side = this.safeFloat (params, 'is_ask'); + if (side === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `is_ask` (side) parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + } else { + price = order['price']; + amount = order['amount']; + side = (order['side'] === 'buy') ? 0 : 1; + symbol = order['symbol']; + } + const request = { + 'order_id': id, + 'price': price, + 'qty': amount, + 'is_ask': side, + 'currency': this.marketId (symbol), + }; + this.orders[id]['status'] = 'canceled'; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + '/'; + if (api === 'public') { + url += request; + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + url += this.version + '/' + request; + const nonce = this.nonce ().toString (); + const json = this.json (this.extend ({ + 'access_token': this.apiKey, + 'nonce': nonce, + }, params)); + const payload = this.stringToBase64 (this.encode (json)); + body = this.decode (payload); + const secret = this.secret.toUpperCase (); + const signature = this.hmac (payload, this.encode (secret), 'sha512'); + headers = { + 'content-type': 'application/json', + 'X-COINONE-PAYLOAD': payload, + 'X-COINONE-SIGNATURE': signature, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if ('result' in response) { + const result = response['result']; + if (result !== 'success') { + // + // { "errorCode": "405", "status": "maintenance", "result": "error"} + // + const errorCode = this.safeString (response, 'errorCode'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + } else { + throw new ExchangeError (this.id + ' ' + body); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],74:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinspot extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinspot', + 'name': 'CoinSpot', + 'countries': [ 'AU' ], // Australia + 'rateLimit': 1000, + 'has': { + 'CORS': false, + 'createMarketOrder': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28208429-3cacdf9a-6896-11e7-854e-4c79a772a30f.jpg', + 'api': { + 'public': 'https://www.coinspot.com.au/pubapi', + 'private': 'https://www.coinspot.com.au/api', + }, + 'www': 'https://www.coinspot.com.au', + 'doc': 'https://www.coinspot.com.au/api', + 'referral': 'https://www.coinspot.com.au/register?code=PJURCU', + }, + 'api': { + 'public': { + 'get': [ + 'latest', + ], + }, + 'private': { + 'post': [ + 'orders', + 'orders/history', + 'my/coin/deposit', + 'my/coin/send', + 'quote/buy', + 'quote/sell', + 'my/balances', + 'my/orders', + 'my/buy', + 'my/sell', + 'my/buy/cancel', + 'my/sell/cancel', + ], + }, + }, + 'markets': { + 'BTC/AUD': { 'id': 'btc', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'btc', 'quoteId': 'aud' }, + 'LTC/AUD': { 'id': 'ltc', 'symbol': 'LTC/AUD', 'base': 'LTC', 'quote': 'AUD', 'baseId': 'ltc', 'quoteId': 'aud' }, + 'DOGE/AUD': { 'id': 'doge', 'symbol': 'DOGE/AUD', 'base': 'DOGE', 'quote': 'AUD', 'baseId': 'doge', 'quoteId': 'aud' }, + }, + 'commonCurrencies': { + 'DRK': 'DASH', + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostMyBalances (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'balance', {}); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balances, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cointype': market['id'], + }; + const orderbook = await this.privatePostOrders (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'buyorders', 'sellorders', 'rate', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetLatest (params); + let id = this.marketId (symbol); + id = id.toLowerCase (); + const ticker = response['prices'][id]; + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cointype': market['id'], + }; + const response = await this.privatePostOrdersHistory (this.extend (request, params)); + const trades = this.safeValue (response, 'orders', []); + return this.parseTrades (trades, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostMy' + this.capitalize (side); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'cointype': this.marketId (symbol), + 'amount': amount, + 'rate': price, + }; + return await this[method] (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' cancelOrder () is not fully implemented yet'); + // let method = 'privatePostMyBuy'; + // return await this[method] ({ 'id': id }); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (!this.apiKey) { + throw new AuthenticationError (this.id + ' requires apiKey for all requests'); + } + const url = this.urls['api'][api] + '/' + path; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.json (this.extend ({ 'nonce': nonce }, params)); + headers = { + 'Content-Type': 'application/json', + 'key': this.apiKey, + 'sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],75:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const coinegg = require ('./coinegg.js'); + +// --------------------------------------------------------------------------- + +module.exports = class coolcoin extends coinegg { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coolcoin', + 'name': 'CoolCoin', + 'countries': [ 'HK' ], + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/36770529-be7b1a04-1c5b-11e8-9600-d11f1996b539.jpg', + 'api': { + 'web': 'https://www.coolcoin.com/coin', + 'rest': 'https://www.coolcoin.com/api/v1', + }, + 'www': 'https://www.coolcoin.com', + 'doc': 'https://www.coolcoin.com/help.api.html', + 'fees': 'https://www.coolcoin.com/spend.price.html', + 'referral': 'https://www.coolcoin.com/user/register?invite_code=bhaega', + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.001, + 'BCH': 0.002, + 'ETH': 0.01, + 'ETC': 0.01, + 'LTC': 0.001, + 'TBC': '1%', + 'HSR': '1%', + 'NEO': '1%', + 'SDC': '1%', + 'EOS': '1%', + 'BTM': '1%', + 'XAS': '1%', + 'ACT': '1%', + 'SAK': '1%', + 'GCS': '1%', + 'HCC': '1%', + 'QTUM': '1%', + 'GEC': '1%', + 'TRX': '1%', + 'IFC': '1%', + 'PAY': '1%', + 'PGC': '1%', + 'KTC': '1%', + 'INT': '1%', + 'LSK': '0.5%', + 'SKT': '1%', + 'SSS': '1%', + 'BT1': '1%', + 'BT2': '1%', + }, + }, + }, + 'options': { + 'quoteIds': ['btc', 'usdt'], + }, + }); + } +}; + +},{"./coinegg.js":66}],76:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coss extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coss', + 'name': 'COSS', + 'countries': [ 'SG', 'NL' ], + 'rateLimit': 1000, + 'version': 'v1', + 'certified': false, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/50328158-22e53c00-0503-11e9-825c-c5cfd79bfa74.jpg', + 'api': { + 'trade': 'https://trade.coss.io/c/api/v1', + 'engine': 'https://engine.coss.io/api/v1', + 'public': 'https://trade.coss.io/c/api/v1', + 'web': 'https://trade.coss.io/c', // undocumented + 'exchange': 'https://exchange.coss.io/api', + }, + 'www': 'https://www.coss.io', + 'doc': 'https://api.coss.io/v1/spec', + 'referral': 'https://www.coss.io/c/reg?r=OWCMHQVW2Q', + }, + 'has': { + 'fetchTrades': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchMarkets': true, + 'fetchCurrencies': true, + 'fetchBalance': true, + 'fetchOrderBook': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOrderTrades': true, + 'fetchClosedOrders': true, + 'fetchOpenOrders': true, + 'fetchOHLCV': true, + 'createOrder': true, + 'cancelOrder': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '4h': '4h', + '6h': '6h', + '12h': '12h', + '1d': '1d', + '1w': '1w', + }, + 'api': { + 'exchange': { + 'get': [ + 'getmarketsummaries', + ], + }, + 'public': { + 'get': [ + 'market-price', + 'exchange-info', + ], + }, + 'web': { + 'get': [ + 'coins/getinfo/all', // undocumented + 'order/symbols', // undocumented + 'coins/get_base_list', // undocumented + ], + }, + 'engine': { + 'get': [ + 'dp', + 'ht', + 'cs', + ], + }, + 'trade': { + 'get': [ + 'ping', + 'time', + 'account/balances', + 'account/details', + ], + 'post': [ + 'order/add', + 'order/details', + 'order/list/open', + 'order/list/completed', + 'order/list/all', + 'order/trade-detail', + ], + 'delete': [ + 'order/cancel', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.0025, + 'maker': 0.0, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'commonCurrencies': { + 'COS': 'COSS', + 'COSS': 'COSS.io', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeInfo (params); + // + // { timezone: "UTC", + // server_time: 1545171487108, + // rate_limits: [ { type: "REQUESTS", + // interval: "MINUTE", + // limit: 1000 } ], + // base_currencies: [ { currency_code: "BTC", minimum_total_order: "0.0001" }, + // { currency_code: "USDT", minimum_total_order: "1" }, + // { currency_code: "EUR", minimum_total_order: "1" } ], + // coins: [ { currency_code: "ADI", + // name: "Aditus", + // minimum_order_amount: "0.00000001" }, + // ... + // { currency_code: "NPXSXEM", + // name: "PundiX-XEM", + // minimum_order_amount: "0.00000001" } ], + // symbols: [ { symbol: "ADI_BTC", + // amount_limit_decimal: 0, + // price_limit_decimal: 8, + // allow_trading: true }, + // ... + // { symbol: "ETH_GUSD", + // amount_limit_decimal: 5, + // price_limit_decimal: 3, + // allow_trading: true } ] } + // + const result = []; + const markets = this.safeValue (response, 'symbols', []); + const baseCurrencies = this.safeValue (response, 'base_currencies', []); + const baseCurrenciesByIds = this.indexBy (baseCurrencies, 'currency_code'); + const currencies = this.safeValue (response, 'coins', []); + const currenciesByIds = this.indexBy (currencies, 'currency_code'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const marketId = market['symbol']; + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'amount_limit_decimal'), + 'price': this.safeInteger (market, 'price_limit_decimal'), + }; + const active = this.safeValue (market, 'allow_trading', false); + const baseCurrency = this.safeValue (baseCurrenciesByIds, baseId, {}); + const minCost = this.safeFloat (baseCurrency, 'minimum_total_order'); + const currency = this.safeValue (currenciesByIds, baseId, {}); + const defaultMinAmount = Math.pow (10, -precision['amount']); + const minAmount = this.safeFloat (currency, 'minimum_order_amount', defaultMinAmount); + result.push ({ + 'symbol': symbol, + 'id': marketId, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.webGetCoinsGetinfoAll (params); + // + // [ { currency_code: "VET", + // name: "VeChain", + // buy_limit: 0, + // sell_limit: 0, + // usdt: 0, + // transaction_time_limit: 5, + // status: "trade", + // withdrawn_fee: "0.6", + // minimum_withdrawn_amount: "1.2", + // minimum_deposit_amount: "0.6", + // minimum_order_amount: "0.00000001", + // decimal_format: "0.########", + // token_type: null, // "erc", "eos", "stellar", "tron", "ripple"... + // buy_at: 0, + // sell_at: 0, + // min_rate: 0, + // max_rate: 0, + // allow_withdrawn: false, + // allow_deposit: false, + // explorer_website_mainnet_link: null, + // explorer_website_testnet_link: null, + // deposit_block_confirmation: "6", + // withdraw_block_confirmation: "0", + // icon_url: "https://s2.coinmarketcap.com/static/img/coins/32x32/3077.png", + // is_fiat: false, + // allow_sell: true, + // allow_buy: true }] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const currencyId = this.safeString (currency, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const name = this.safeString (currency, 'name'); + const allowBuy = this.safeValue (currency, 'allow_buy'); + const allowSell = this.safeValue (currency, 'allow_sell'); + const allowWithdrawals = this.safeValue (currency, 'allow_withdrawn'); + const allowDeposits = this.safeValue (currency, 'allow_deposit'); + const active = allowBuy && allowSell && allowWithdrawals && allowDeposits; + const fee = this.safeFloat (currency, 'withdrawn_fee'); + const type = this.safeString (currency, 'token_type'); + // + // decimal_format can be anything... + // + // 0.######## + // #.######## + // 0.## + // '' (empty string) + // 0.000000 + // null (undefined) + // 0.0000 + // 0.### + // + const decimalFormat = this.safeString (currency, 'decimal_format'); + let precision = 8; + if (decimalFormat !== undefined) { + const parts = decimalFormat.split ('.'); + const numParts = parts.length; // transpiler workaround for array lengths + if (numParts > 1) { + if (parts[1].length > 1) { + precision = parts[1].length; + } + } + } + result[code] = { + 'id': currencyId, + 'code': code, + 'info': currency, + 'name': name, + 'active': active, + 'fee': fee, + 'precision': precision, + 'type': type, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minimum_order_amount'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimum_withdrawn_amount'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.tradeGetAccountBalances (params); + // + // [ { currency_code: "ETH", + // address: "0x6820511d43111a941d3e187b9e36ec64af763bde", // deposit address + // total: "0.20399125", + // available: "0.20399125", + // in_order: "0", + // memo: null }, // tag, if any + // { currency_code: "ICX", + // address: "", + // total: "0", + // available: "0", + // in_order: "0", + // memo: null } ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const total = this.safeFloat (balance, 'total'); + const used = this.safeFloat (balance, 'in_order'); + const free = this.safeFloat (balance, 'available'); + result[code] = { + 'total': total, + 'used': used, + 'free': free, + }; + } + return this.parseBalance (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + parseInt (ohlcv[0]), // timestamp + parseFloat (ohlcv[1]), // Open + parseFloat (ohlcv[2]), // High + parseFloat (ohlcv[3]), // Low + parseFloat (ohlcv[4]), // Close + parseFloat (ohlcv[5]), // base Volume + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'tt': this.timeframes[timeframe], + }; + const response = await this.engineGetCs (this.extend (request, params)); + // + // { tt: "1m", + // symbol: "ETH_BTC", + // nextTime: 1545138960000, + // series: [ [ 1545138960000, + // "0.02705000", + // "0.02705000", + // "0.02705000", + // "0.02705000", + // "0.00000000" ], + // ... + // [ 1545168900000, + // "0.02684000", + // "0.02684000", + // "0.02684000", + // "0.02684000", + // "0.00000000" ] ], + // limit: 500 } + // + return this.parseOHLCVs (response['series'], market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { 'symbol': marketId }; + // limit argument is not supported on COSS's end + const response = await this.engineGetDp (this.extend (request, params)); + // + // { symbol: "COSS_ETH", + // asks: [ ["0.00065200", "214.15000000"], + // ["0.00065300", "645.45000000"], + // ... + // ["0.00076400", "380.00000000"], + // ["0.00076900", "25.00000000"] ], + // limit: 100, + // bids: [ ["0.00065100", "666.99000000"], + // ["0.00065000", "1171.93000000"], + // ... + // ["0.00037700", "3300.00000000"], + // ["0.00037600", "2010.82000000"] ], + // time: 1545180569354 } + // + const timestamp = this.safeInteger (response, 'time'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + // + // { MarketName: "COSS-ETH", + // High: 0.00066, + // Low: 0.000628, + // BaseVolume: 131.09652674, + // Last: 0.000636, + // TimeStamp: "2018-12-19T05:16:41.369Z", + // Volume: 206126.6143710692, + // Ask: "0.00063600", + // Bid: "0.00063400", + // PrevDay: 0.000636 } + // + const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp')); + let symbol = undefined; + let marketId = this.safeString (ticker, 'MarketName'); + if (marketId !== undefined) { + marketId = marketId.replace ('-', '_'); + } + market = this.safeValue (this.markets_by_id, marketId, market); + if (market === undefined) { + if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const previous = this.safeFloat (ticker, 'PrevDay'); + const last = this.safeFloat (ticker, 'Last'); + let change = undefined; + let percentage = undefined; + if (last !== undefined) { + if (previous !== undefined) { + change = last - previous; + if (previous > 0) { + percentage = (change / previous) * 100; + } + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'High'), + 'low': this.safeFloat (ticker, 'Low'), + 'bid': this.safeFloat (ticker, 'Bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'Ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': previous, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume'), + 'quoteVolume': this.safeFloat (ticker, 'BaseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.exchangeGetGetmarketsummaries (params); + // + // { success: true, + // message: "", + // result: [ { MarketName: "COSS-ETH", + // High: 0.00066, + // Low: 0.000628, + // BaseVolume: 131.09652674, + // Last: 0.000636, + // TimeStamp: "2018-12-19T05:16:41.369Z", + // Volume: 206126.6143710692, + // Ask: "0.00063600", + // Bid: "0.00063400", + // PrevDay: 0.000636 }, + // ... + // { MarketName: "XLM-BTC", + // High: 0.0000309, + // Low: 0.0000309, + // BaseVolume: 0, + // Last: 0.0000309, + // TimeStamp: "2018-12-19T02:00:02.145Z", + // Volume: 0, + // Ask: "0.00003300", + // Bid: "0.00003090", + // PrevDay: 0.0000309 } ], + // volumes: [ { CoinName: "ETH", Volume: 668.1928095999999 }, // these are overall exchange volumes + // { CoinName: "USD", Volume: 9942.58480324 }, + // { CoinName: "BTC", Volume: 43.749184570000004 }, + // { CoinName: "COSS", Volume: 909909.26644574 }, + // { CoinName: "EUR", Volume: 0 }, + // { CoinName: "TUSD", Volume: 2613.3395026999997 }, + // { CoinName: "USDT", Volume: 1017152.07416519 }, + // { CoinName: "GUSD", Volume: 1.80438 }, + // { CoinName: "XRP", Volume: 15.95508 }, + // { CoinName: "GBP", Volume: 0 }, + // { CoinName: "USDC", Volume: 0 } ], + // t: 1545196604371 } + // + const tickers = this.safeValue (response, 'result', []); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.engineGetHt (this.extend (request, params)); + // + // { symbol: "COSS_ETH", + // limit: 100, + // history: [ { id: 481321, + // price: "0.00065100", + // qty: "272.92000000", + // isBuyerMaker: false, + // time: 1545180845019 }, + // { id: 481322, + // price: "0.00065200", + // qty: "1.90000000", + // isBuyerMaker: true, + // time: 1545180847535 }, + // ... + // { id: 481420, + // price: "0.00065300", + // qty: "2.00000000", + // isBuyerMaker: true, + // time: 1545181167702 } ], + // time: 1545181171274 } + // + return this.parseTrades (response['history'], market, since, limit); + } + + parseTradeFee (fee) { + if (fee === undefined) { + return fee; + } + const parts = fee.split (' '); + const numParts = parts.length; + const cost = parts[0]; + let code = undefined; + if (numParts > 1) { + code = this.safeCurrencyCode (parts[1]); + } + return { + 'cost': cost, + 'currency': code, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { id: 481322, + // price: "0.00065200", + // qty: "1.90000000", + // isBuyerMaker: true, + // time: 1545180847535 } + // + // fetchOrderTrades (private) + // + // [ { hex_id: null, + // symbol: "COSS_ETH", + // order_id: "ad6f6b47-3def-4add-a5d5-2549a9df1593", + // order_side: "BUY", + // price: "0.00065900", + // quantity: "10", + // fee: "0.00700000 COSS", + // additional_fee: "0.00000461 ETH", + // total: "0.00659000 ETH", + // timestamp: 1545152356075 } ] + // + const id = this.safeString (trade, 'id'); + const timestamp = this.safeInteger (trade, 'time'); + const orderId = this.safeString (trade, 'order_id'); + const side = this.safeStringLower (trade, 'order_side'); + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + market = this.safeValue (this.markets_by_id, marketId, market); + if (market === undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } else if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'qty', 'quantity'); + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const result = { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + const fee = this.parseTradeFee (this.safeString (trade, 'fee')); + if (fee !== undefined) { + const additionalFee = this.parseTradeFee (this.safeString (trade, 'additional_fee')); + if (additionalFee === undefined) { + result['fee'] = fee; + } else { + result['fees'] = [ + fee, + additionalFee, + ]; + } + } + return result; + } + + async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + // 'from_id': 'b2a2d379-f9b6-418b-9414-cbf8330b20d1', // string (uuid), fetchOrders (all orders) only + // 'page': 0, // different pagination in fetchOpenOrders and fetchClosedOrders + // 'limit': 50, // optional, max = default = 50 + 'symbol': market['id'], // required + }; + if (limit !== undefined) { + request['limit'] = limit; // max = default = 50 + } + const method = 'tradePostOrderList' + type; + const response = await this[method] (this.extend (request, params)); + // + // fetchOrders, fetchClosedOrders + // + // [ { hex_id: "5c192784330fe51149f556bb", + // order_id: "5e46e1b1-93d5-4656-9b43-a5635b08eae9", + // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", + // order_symbol: "COSS_ETH", + // order_side: "BUY", + // status: "filled", + // createTime: 1545152388019, + // type: "limit", + // timeMatching: 0, + // order_price: "0.00065900", + // order_size: "10", + // executed: "10", + // stop_price: "0.00000000", + // avg: "0.00065900", + // total: "0.00659000 ETH" } ] + // + // fetchOpenOrders + // + // { + // "total": 2, + // "list": [ + // { + // "order_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4ty", + // "account_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4a6", + // "order_symbol": "eth-btc", + // "order_side": "BUY", + // "status": "OPEN", + // "createTime": 1538114348750, + // "type": "limit", + // "order_price": "0.12345678", + // "order_size": "10.12345678", + // "executed": "0", + // "stop_price": "02.12345678", + // "avg": "1.12345678", + // "total": "2.12345678" + // } + // ] + // } + // + // the following code is to handle the above difference in response formats + let orders = undefined; + if (Array.isArray (response)) { + orders = response; + } else { + orders = this.safeValue (response, 'list', []); + } + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('All', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('Completed', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('Open', symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.tradePostOrderDetails (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'order_id': id, + }; + const response = await this.tradePostOrderTradeDetail (this.extend (request, params)); + // + // [ { hex_id: null, + // symbol: "COSS_ETH", + // order_id: "ad6f6b47-3def-4add-a5d5-2549a9df1593", + // order_side: "BUY", + // price: "0.00065900", + // quantity: "10", + // fee: "0.00700000 COSS", + // additional_fee: "0.00000461 ETH", + // total: "0.00659000 ETH", + // timestamp: 1545152356075 } ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOrderStatus (status) { + if (status === undefined) { + return status; + } + const statuses = { + 'OPEN': 'open', + 'CANCELLED': 'canceled', + 'FILLED': 'closed', + 'PARTIAL_FILL': 'closed', + 'CANCELLING': 'open', + }; + return this.safeString (statuses, status.toUpperCase (), status); + } + + parseOrder (order, market = undefined) { + // + // { hex_id: "5c192784330fe51149f556bb", // missing in fetchOpenOrders + // order_id: "5e46e1b1-93d5-4656-9b43-a5635b08eae9", + // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", + // order_symbol: "COSS_ETH", // coss-eth in docs + // order_side: "BUY", + // status: "filled", + // createTime: 1545152388019, + // type: "limit", + // timeMatching: 0, // missing in fetchOpenOrders + // order_price: "0.00065900", + // order_size: "10", + // executed: "10", + // stop_price: "0.00000000", + // avg: "0.00065900", + // total: "0.00659000 ETH" } + // + const id = this.safeString (order, 'order_id'); + let symbol = undefined; + let marketId = this.safeString (order, 'order_symbol'); + if (marketId === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } else { + // a minor workaround for lowercase eth-btc symbols + marketId = marketId.toUpperCase (); + marketId = marketId.replace ('-', '_'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market === undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else { + symbol = market['symbol']; + } + } + const timestamp = this.safeInteger (order, 'createTime'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'order_price'); + const filled = this.safeFloat (order, 'executed'); + const type = this.safeString (order, 'type'); + const amount = this.safeFloat (order, 'order_size'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const average = this.safeFloat (order, 'avg'); + const side = this.safeStringLower (order, 'order_side'); + const cost = this.safeFloat (order, 'total'); + const fee = undefined; + const trades = undefined; + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_symbol': market['id'], + 'order_size': this.amountToPrecision (symbol, amount), + 'order_side': side.toUpperCase (), + 'type': type, + }; + if (price !== undefined) { + request['order_price'] = this.priceToPrecision (symbol, price); + } + const response = await this.tradePostOrderAdd (this.extend (request, params)); + // + // { + // "order_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4ty", + // "account_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4a6", + // "order_symbol": "eth-btc", + // "order_side": "BUY", + // "status": "OPEN", + // "createTime": 1538114348750, + // "type": "limit", + // "order_price": "0.12345678", + // "order_size": "10.12345678", + // "executed": "0", + // "stop_price": "02.12345678", + // "avg": "1.12345678", + // "total": "2.12345678" + // } + // + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'order_symbol': market['id'], + }; + const response = await this.tradeDeleteOrderCancel (this.extend (request, params)); + // + // { order_symbol: "COSS_ETH", + // order_id: "30f2d698-39a0-4b9f-a3a6-a179542373bd", + // order_size: 0, + // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", + // timestamp: 1545202728814, + // recvWindow: null } + // + return this.parseOrder (response); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'trade') { + this.checkRequiredCredentials (); + const timestamp = this.nonce (); + const query = this.extend ({ + 'timestamp': timestamp, // required (int64) + // 'recvWindow': 10000, // optional (int32) + }, params); + let request = undefined; + if (method === 'GET') { + request = this.urlencode (query); + url += '?' + request; + } else { + request = this.json (query); + body = request; + } + headers = { + 'Signature': this.hmac (this.encode (request), this.encode (this.secret)), + 'Authorization': this.apiKey, + 'X-Requested-With': 'XMLHttpRequest', + }; + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],77:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest, InvalidNonce, RequestTimeout, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, AuthenticationError, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class crex24 extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'crex24', + 'name': 'CREX24', + 'countries': [ 'EE' ], // Estonia + 'rateLimit': 500, + 'version': 'v2', + // new metainfo interface + 'has': { + 'cancelAllOrders': true, + 'CORS': false, + 'editOrder': true, + 'fetchBidsAsks': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOrderTrades': true, + 'fetchTickers': true, + 'fetchTradingFee': false, // actually, true, but will be implemented later + 'fetchTradingFees': false, // actually, true, but will be implemented later + 'fetchTransactions': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '3m': '3m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '4h': '4h', + '1d': '1d', + '1w': '1w', + '1M': '1mo', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/47813922-6f12cc00-dd5d-11e8-97c6-70f957712d47.jpg', + 'api': 'https://api.crex24.com', + 'www': 'https://crex24.com', + 'referral': 'https://crex24.com/?refid=slxsjsjtil8xexl9hksr', + 'doc': 'https://docs.crex24.com/trade-api/v2', + 'fees': 'https://crex24.com/fees', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'instruments', + 'tickers', + 'recentTrades', + 'orderBook', + 'ohlcv', + ], + }, + 'trading': { + 'get': [ + 'orderStatus', + 'orderTrades', + 'activeOrders', + 'orderHistory', + 'tradeHistory', + 'tradeFee', + // this is in trading API according to their docs, but most likely a typo in their docs + 'moneyTransferStatus', + ], + 'post': [ + 'placeOrder', + 'modifyOrder', + 'cancelOrdersById', + 'cancelOrdersByInstrument', + 'cancelAllOrders', + ], + }, + 'account': { + 'get': [ + 'balance', + 'depositAddress', + 'moneyTransfers', + // this is in trading API according to their docs, but most likely a typo in their docs + 'moneyTransferStatus', + 'previewWithdrawal', + ], + 'post': [ + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.001, + 'maker': -0.0001, + }, + // should be deleted, these are outdated and inaccurate + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'commonCurrencies': { + 'YOYO': 'YOYOW', + 'BCC': 'BCH', + }, + // exchange-specific options + 'options': { + 'fetchOrdersMethod': 'tradingGetOrderHistory', // or 'tradingGetActiveOrders' + 'fetchClosedOrdersMethod': 'tradingGetOrderHistory', // or 'tradingGetActiveOrders' + 'fetchTickersMethod': 'publicGetTicker24hr', + 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel + 'defaultLimitOrderType': 'limit', // or 'limit_maker' + 'hasAlreadyAuthenticatedSuccessfully': false, + 'warnOnFetchOpenOrdersWithoutSymbol': true, + 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision + 'newOrderRespType': 'RESULT', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills + }, + 'exceptions': { + 'exact': { + "Parameter 'filter' contains invalid value.": BadRequest, // eslint-disable-quotes + "Mandatory parameter 'instrument' is missing.": BadRequest, // eslint-disable-quotes + "The value of parameter 'till' must be greater than or equal to the value of parameter 'from'.": BadRequest, // eslint-disable-quotes + 'Failed to verify request signature.': AuthenticationError, // eslint-disable-quotes + "Nonce error. Make sure that the value passed in the 'X-CREX24-API-NONCE' header is greater in each consecutive request than in the previous one for the corresponding API-Key provided in 'X-CREX24-API-KEY' header.": InvalidNonce, + 'Market orders are not supported by the instrument currently.': InvalidOrder, + "Parameter 'instrument' contains invalid value.": BadSymbol, + }, + 'broad': { + 'API Key': AuthenticationError, // "API Key '9edc48de-d5b0-4248-8e7e-f59ffcd1c7f1' doesn't exist." + 'Insufficient funds': InsufficientFunds, // "Insufficient funds: new order requires 10 ETH which is more than the available balance." + 'has been delisted.': BadSymbol, // {"errorDescription":"Instrument '$PAC-BTC' has been delisted."} + }, + }, + }); + } + + nonce () { + return this.milliseconds (); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInstruments (params); + // + // [ { symbol: "$PAC-BTC", + // baseCurrency: "$PAC", + // quoteCurrency: "BTC", + // feeCurrency: "BTC", + // tickSize: 1e-8, + // minPrice: 1e-8, + // minVolume: 1, + // supportedOrderTypes: ["limit"], + // state: "active" }, + // { symbol: "ZZC-USD", + // baseCurrency: "ZZC", + // quoteCurrency: "USD", + // feeCurrency: "USD", + // tickSize: 0.0001, + // minPrice: 0.0001, + // minVolume: 1, + // supportedOrderTypes: ["limit"], + // state: "active" } ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const tickSize = this.safeValue (market, 'tickSize'); + const minPrice = this.safeValue (market, 'minPrice'); + const minAmount = this.safeFloat (market, 'minVolume'); + const precision = { + 'amount': this.precisionFromString (this.numberToString (minAmount)), + 'price': this.precisionFromString (this.numberToString (tickSize)), + }; + const active = (market['state'] === 'active'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // [ { symbol: "$PAC", + // name: "PACCoin", + // isFiat: false, + // depositsAllowed: true, + // depositConfirmationCount: 8, + // minDeposit: 0, + // withdrawalsAllowed: true, + // withdrawalPrecision: 8, + // minWithdrawal: 4, + // maxWithdrawal: 1000000000, + // flatWithdrawalFee: 2, + // isDelisted: false }, + // { symbol: "ZZC", + // name: "Zozo", + // isFiat: false, + // depositsAllowed: false, + // depositConfirmationCount: 8, + // minDeposit: 0, + // withdrawalsAllowed: false, + // withdrawalPrecision: 8, + // minWithdrawal: 0.2, + // maxWithdrawal: 1000000000, + // flatWithdrawalFee: 0.1, + // isDelisted: false } ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'withdrawalPrecision'); + const address = this.safeValue (currency, 'BaseAddress'); + const active = (currency['depositsAllowed'] && currency['withdrawalsAllowed'] && !currency['isDelisted']); + const type = currency['isFiat'] ? 'fiat' : 'crypto'; + result[code] = { + 'id': id, + 'code': code, + 'address': address, + 'info': currency, + 'type': type, + 'name': this.safeString (currency, 'name'), + 'active': active, + 'fee': this.safeFloat (currency, 'flatWithdrawalFee'), // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'deposit': { + 'min': this.safeFloat (currency, 'minDeposit'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawal'), + 'max': this.safeFloat (currency, 'maxWithdrawal'), + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + // 'currency': 'ETH', // comma-separated list of currency ids + // 'nonZeroOnly': 'false', // true by default + }; + const response = await this.accountGetBalance (this.extend (request, params)); + // + // [ + // { + // "currency": "ETH", + // "available": 0.0, + // "reserved": 0.0 + // } + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = maximum = 100 + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { buyLevels: [ { price: 0.03099, volume: 0.00610063 }, + // { price: 0.03097, volume: 1.33455158 }, + // { price: 0.03096, volume: 0.0830889 }, + // { price: 0.03095, volume: 0.0820356 }, + // { price: 0.03093, volume: 0.5499419 }, + // { price: 0.03092, volume: 0.23317494 }, + // { price: 0.03091, volume: 0.62105322 }, + // { price: 0.00620041, volume: 0.003 } ], + // sellLevels: [ { price: 0.03117, volume: 5.47492315 }, + // { price: 0.03118, volume: 1.97744139 }, + // { price: 0.03119, volume: 0.012 }, + // { price: 0.03121, volume: 0.741242 }, + // { price: 0.03122, volume: 0.96178089 }, + // { price: 0.03123, volume: 0.152326 }, + // { price: 0.03124, volume: 2.63462933 }, + // { price: 0.069, volume: 0.004 } ] } + // + return this.parseOrderBook (response, undefined, 'buyLevels', 'sellLevels', 'price', 'volume'); + } + + parseTicker (ticker, market = undefined) { + // + // { instrument: "ZZC-USD", + // last: 0.065, + // percentChange: 0, + // low: 0.065, + // high: 0.065, + // baseVolume: 0, + // quoteVolume: 0, + // volumeInBtc: 0, + // volumeInUsd: 0, + // ask: 0.5, + // bid: 0.0007, + // timestamp: "2018-10-31T09:21:25Z" } ] + // + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + let symbol = undefined; + const marketId = this.safeString (ticker, 'instrument'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } else if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, // previous day close + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'percentChange'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ { instrument: "$PAC-BTC", + // last: 3.3e-7, + // percentChange: 3.125, + // low: 2.7e-7, + // high: 3.3e-7, + // baseVolume: 191700.79823187, + // quoteVolume: 0.0587930939346704, + // volumeInBtc: 0.0587930939346704, + // volumeInUsd: 376.2006339435353, + // ask: 3.3e-7, + // bid: 3.1e-7, + // timestamp: "2018-10-31T09:21:25Z" } ] + // + const numTickers = response.length; + if (numTickers < 1) { + throw new ExchangeError (this.id + ' fetchTicker could not load quotes for symbol ' + symbol); + } + return this.parseTicker (response[0], market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['instrument'] = ids.join (','); + } + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ { instrument: "$PAC-BTC", + // last: 3.3e-7, + // percentChange: 3.125, + // low: 2.7e-7, + // high: 3.3e-7, + // baseVolume: 191700.79823187, + // quoteVolume: 0.0587930939346704, + // volumeInBtc: 0.0587930939346704, + // volumeInUsd: 376.2006339435353, + // ask: 3.3e-7, + // bid: 3.1e-7, + // timestamp: "2018-10-31T09:21:25Z" }, + // { instrument: "ZZC-USD", + // last: 0.065, + // percentChange: 0, + // low: 0.065, + // high: 0.065, + // baseVolume: 0, + // quoteVolume: 0, + // volumeInBtc: 0, + // volumeInUsd: 0, + // ask: 0.5, + // bid: 0.0007, + // timestamp: "2018-10-31T09:21:25Z" } ] + // + return this.parseTickers (response, symbols); + } + + parseTickers (tickers, symbols = undefined) { + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.filterByArray (result, 'symbol', symbols); + } + + parseTrade (trade, market = undefined) { + // + // public fetchTrades + // + // { price: 0.03105, + // volume: 0.11, + // side: "sell", + // timestamp: "2018-10-31T04:19:35Z" } ] + // + // private fetchMyTrades + // + // { + // "id": 3005866, + // "orderId": 468533093, + // "timestamp": "2018-06-02T16:26:27Z", + // "instrument": "BCH-ETH", + // "side": "buy", + // "price": 1.78882, + // "volume": 0.027, + // "fee": 0.0000483, + // "feeCurrency": "ETH" + // } + // + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const id = this.safeString (trade, 'id'); + const side = this.safeString (trade, 'side'); + const orderId = this.safeString (trade, 'orderId'); + let symbol = undefined; + const marketId = this.safeString (trade, 'instrument'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCurrencyId = this.safeString (trade, 'feeCurrency'); + const feeCode = this.safeCurrencyCode (feeCurrencyId); + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCode, + }; + } + const takerOrMaker = undefined; + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // min 1, max 1000, default 100 + } + const response = await this.publicGetRecentTrades (this.extend (request, params)); + // + // [ { price: 0.03117, + // volume: 0.02597403, + // side: "buy", + // timestamp: "2018-10-31T09:37:46Z" }, + // { price: 0.03105, + // volume: 0.11, + // side: "sell", + // timestamp: "2018-10-31T04:19:35Z" } ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // { timestamp: '2019-09-21T10:36:00Z', + // open: 0.02152, + // high: 0.02156, + // low: 0.02152, + // close: 0.02156, + // volume: 0.01741259 } + const date = this.safeString (ohlcv, 'timestamp'); + const timestamp = this.parse8601 (date); + return [ + timestamp, + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'granularity': this.timeframes[timeframe], + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // Accepted values: 1 - 1000. If the parameter is not specified, the number of results is limited to 100 + } + const response = await this.publicGetOhlcv (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'submitting': 'open', // A newly created limit order has a status "submitting" until it has been processed. + // This status changes during the lifetime of an order and can have different values depending on the value of the parameter Time In Force. + 'unfilledActive': 'open', // order is active, no trades have been made + 'partiallyFilledActive': 'open', // part of the order has been filled, the other part is active + 'filled': 'closed', // order has been filled entirely + 'partiallyFilledCancelled': 'canceled', // part of the order has been filled, the other part has been cancelled either by the trader or by the system (see the value of cancellationReason of an Order for more details on the reason of cancellation) + 'unfilledCancelled': 'canceled', // order has been cancelled, no trades have taken place (see the value of cancellationReason of an Order for more details on the reason of cancellation) + }; + return (status in statuses) ? statuses[status] : status; + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "id": 469594855, + // "timestamp": "2018-06-08T16:59:44Z", + // "instrument": "BTS-BTC", + // "side": "buy", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 4.0, + // "price": 0.000025, + // "stopPrice": null, + // "remainingVolume": 4.0, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'instrument'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); + let price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + const remaining = this.safeFloat (order, 'remainingVolume'); + let filled = undefined; + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'lastUpdate')); + let cost = undefined; + if (remaining !== undefined) { + if (amount !== undefined) { + filled = amount - remaining; + if (this.options['parseOrderToPrecision']) { + filled = parseFloat (this.amountToPrecision (symbol, filled)); + } + filled = Math.max (filled, 0.0); + if (price !== undefined) { + cost = price * filled; + } + } + } + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + } + } + const side = this.safeString (order, 'side'); + const fee = undefined; + const trades = undefined; + let average = undefined; + if (cost !== undefined) { + if (filled) { + average = cost / filled; + } + if (this.options['parseOrderToPrecision']) { + cost = parseFloat (this.costToPrecision (symbol, cost)); + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + 'volume': this.amountToPrecision (symbol, amount), + // The value must comply with the list of order types supported by the instrument (see the value of parameter supportedOrderTypes of the Instrument) + // If the parameter is not specified, the default value "limit" is used + // More about order types in the corresponding section of documentation + 'type': type, // 'limit', 'market', 'stopLimit', in fact as of 2018-10-31, only 'limit' orders are supported for all markets + 'side': side, // 'buy' or 'sell' + // "GTC" - Good-Til-Cancelled + // "IOC" - Immediate-Or-Cancel (currently not supported by the exchange API, reserved for future use) + // "FOK" - Fill-Or-Kill (currently not supported by the exchange API, reserved for future use) + // 'timeInForce': 'GTC', // IOC', 'FOK' + // 'strictValidation': false, // false - prices will be rounded to meet the requirement, true - execution of the method will be aborted and an error message will be returned + }; + let priceIsRequired = false; + let stopPriceIsRequired = false; + if (type === 'limit') { + priceIsRequired = true; + } else if (type === 'stopLimit') { + priceIsRequired = true; + stopPriceIsRequired = true; + } + if (priceIsRequired) { + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order'); + } + request['price'] = this.priceToPrecision (symbol, price); + } + if (stopPriceIsRequired) { + const stopPrice = this.safeFloat (params, 'stopPrice'); + if (stopPrice === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order'); + } else { + request['stopPrice'] = this.priceToPrecision (symbol, stopPrice); + } + } + const response = await this.tradingPostPlaceOrder (this.extend (request, params)); + // + // { + // "id": 469594855, + // "timestamp": "2018-06-08T16:59:44Z", + // "instrument": "BTS-BTC", + // "side": "buy", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 4.0, + // "price": 0.000025, + // "stopPrice": null, + // "remainingVolume": 4.0, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // + return this.parseOrder (response, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.tradingGetOrderStatus (this.extend (request, params)); + // + // [ + // { + // "id": 466747915, + // "timestamp": "2018-05-26T06:43:49Z", + // "instrument": "UNI-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 5700.0, + // "price": 0.000005, + // "stopPrice": null, + // "remainingVolume": 1.948051948052, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // ] + // + const numOrders = response.length; + if (numOrders < 1) { + throw new OrderNotFound (this.id + ' fetchOrder could not fetch order id ' + id); + } + return this.parseOrder (response[0]); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['from'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (symbol !== undefined) { + const market = this.market (symbol); + request['instrument'] = market['id']; + } + const method = this.safeString (this.options, 'fetchOrdersMethod', 'tradingGetOrderHistory'); + const response = await this[method] (this.extend (request, params)); + // + // [ + // { + // "id": 468535711, + // "timestamp": "2018-06-02T16:42:40Z", + // "instrument": "BTC-EUR", + // "side": "sell", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 0.00770733, + // "price": 6724.9, + // "stopPrice": null, + // "remainingVolume": 0.00770733, + // "lastUpdate": "2018-06-02T16:42:40Z", + // "parentOrderId": null, + // "childOrderId": null + // } + // ] + // + return this.parseOrders (response); + } + + async fetchOrdersByIds (ids = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': ids.join (','), + }; + const response = await this.tradingGetOrderStatus (this.extend (request, params)); + // + // [ + // { + // "id": 466747915, + // "timestamp": "2018-05-26T06:43:49Z", + // "instrument": "UNI-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 5700.0, + // "price": 0.000005, + // "stopPrice": null, + // "remainingVolume": 1.948051948052, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // ] + // + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['instrument'] = market['id']; + } + const response = await this.tradingGetActiveOrders (this.extend (request, params)); + // + // [ + // { + // "id": 466747915, + // "timestamp": "2018-05-26T06:43:49Z", + // "instrument": "UNI-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 5700.0, + // "price": 0.000005, + // "stopPrice": null, + // "remainingVolume": 1.948051948052, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // { + // "id": 466748077, + // "timestamp": "2018-05-26T06:45:29Z", + // "instrument": "PRJ-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 10000.0, + // "price": 0.0000007, + // "stopPrice": null, + // "remainingVolume": 9975.0, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // ... + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['instrument'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['limit'] = limit; // min 1, max 1000, default 100 + } + const method = this.safeString (this.options, 'fetchClosedOrdersMethod', 'tradingGetOrderHistory'); + const response = await this[method] (this.extend (request, params)); + // [ + // { + // "id": 468535711, + // "timestamp": "2018-06-02T16:42:40Z", + // "instrument": "BTC-EUR", + // "side": "sell", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 0.00770733, + // "price": 6724.9, + // "stopPrice": null, + // "remainingVolume": 0.00770733, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // { + // "id": 468535707, + // "timestamp": "2018-06-02T16:42:37Z", + // "instrument": "BTG-BTC", + // "side": "buy", + // "type": "limit", + // "status": "unfilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 0.0173737, + // "price": 0.00589027, + // "stopPrice": null, + // "remainingVolume": 0.0173737, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // ... + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'ids': [ + parseInt (id), + ], + }; + const response = await this.tradingPostCancelOrdersById (this.extend (request, params)); + // + // [ + // 465448358, + // 468364313 + // ] + // + return this.parseOrder (response); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + const response = await this.tradingPostCancelAllOrders (params); + // + // [ + // 465448358, + // 468364313 + // ] + // + return response; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['instrument'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['limit'] = limit; // min 1, max 1000, default 100 + } + const response = await this.tradingGetTradeHistory (this.extend (request, params)); + // + // [ + // { + // "id": 3005866, + // "orderId": 468533093, + // "timestamp": "2018-06-02T16:26:27Z", + // "instrument": "BCH-ETH", + // "side": "buy", + // "price": 1.78882, + // "volume": 0.027, + // "fee": 0.0000483, + // "feeCurrency": "ETH" + // }, + // { + // "id": 3005812, + // "orderId": 468515771, + // "timestamp": "2018-06-02T16:16:05Z", + // "instrument": "ETC-BTC", + // "side": "sell", + // "price": 0.00210958, + // "volume": 0.05994006, + // "fee": -0.000000063224, + // "feeCurrency": "BTC" + // }, + // ... + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['from'] = this.ymd (since, 'T'); + } + const response = await this.accountGetMoneyTransfers (this.extend (request, params)); + // + // [ + // { + // "id": 756446, + // "type": "deposit", + // "currency": "ETH", + // "address": "0x451d5a1b7519aa75164f440df78c74aac96023fe", + // "paymentId": null, + // "amount": 0.142, + // "fee": null, + // "txId": "0x2b49098749840a9482c4894be94f94864b498a1306b6874687a5640cc9871918", + // "createdAt": "2018-06-02T19:30:28Z", + // "processedAt": "2018-06-02T21:10:41Z", + // "confirmationsRequired": 12, + // "confirmationCount": 12, + // "status": "success", + // "errorDescription": null + // }, + // { + // "id": 754618, + // "type": "deposit", + // "currency": "BTC", + // "address": "1IgNfmERVcier4IhfGEfutkLfu4AcmeiUC", + // "paymentId": null, + // "amount": 0.09, + // "fee": null, + // "txId": "6876541687a9187e987c9187654f7198b9718af974641687b19a87987f91874f", + // "createdAt": "2018-06-02T16:19:44Z", + // "processedAt": "2018-06-02T16:20:50Z", + // "confirmationsRequired": 1, + // "confirmationCount": 1, + // "status": "success", + // "errorDescription": null + // }, + // ... + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'deposit', + }; + return this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'withdrawal', + }; + return this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + parseTransactionStatus (status) { + const statuses = { + 'pending': 'pending', // transfer is in progress + 'success': 'ok', // completed successfully + 'failed': 'failed', // aborted at some point (money will be credited back to the account of origin) + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // "id": 756446, + // "type": "deposit", + // "currency": "ETH", + // "address": "0x451d5a1b7519aa75164f440df78c74aac96023fe", + // "paymentId": null, + // "amount": 0.142, + // "fee": null, + // "txId": "0x2b49098749840a9482c4894be94f94864b498a1306b6874687a5640cc9871918", + // "createdAt": "2018-06-02T19:30:28Z", + // "processedAt": "2018-06-02T21:10:41Z", + // "confirmationsRequired": 12, + // "confirmationCount": 12, + // "status": "success", + // "errorDescription": null, + // } + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + const tag = this.safeString (transaction, 'paymentId'); + const txid = this.safeValue (transaction, 'txId'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeString (transaction, 'type'); + const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt')); + const updated = this.parse8601 (this.safeString (transaction, 'processedAt')); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const amount = this.safeFloat (transaction, 'amount'); + const feeCost = this.safeFloat (transaction, 'fee'); + const fee = { + 'cost': feeCost, + 'currency': code, + }; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.accountGetDepositAddress (this.extend (request, params)); + // + // { + // "currency": "BTS", + // "address": "crex24", + // "paymentId": "0fg4da4186741579" + // } + // + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'paymentId'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'address': address, + 'amount': parseFloat (this.currencyToPrecision (code, amount)), + // sets whether the specified amount includes fee, can have either of the two values + // true - balance will be decreased by amount, whereas [amount - fee] will be transferred to the specified address + // false - amount will be deposited to the specified address, whereas the balance will be decreased by [amount + fee] + // 'includeFee': false, // the default value is false + }; + if (tag !== undefined) { + request['paymentId'] = tag; + } + const response = await this.accountPostWithdraw (this.extend (request, params)); + return this.parseTransaction (response); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.version + '/' + api + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } + const url = this.urls['api'] + request; + if ((api === 'trading') || (api === 'account')) { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const secret = this.base64ToBinary (this.secret); + let auth = request + nonce; + headers = { + 'X-CREX24-API-KEY': this.apiKey, + 'X-CREX24-API-NONCE': nonce, + }; + if (method === 'POST') { + headers['Content-Type'] = 'application/json'; + body = this.json (params); + auth += body; + } + const signature = this.stringToBase64 (this.hmac (this.encode (auth), secret, 'sha512', 'binary')); + headers['X-CREX24-API-SIGN'] = this.decode (signature); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!this.isJsonEncodedObject (body)) { + return; // fallback to default error handler + } + if ((code >= 200) && (code < 300)) { + return; // no error + } + const message = this.safeString (response, 'errorDescription'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + if (code === 400) { + throw new BadRequest (feedback); + } else if (code === 401) { + throw new AuthenticationError (feedback); + } else if (code === 403) { + throw new AuthenticationError (feedback); + } else if (code === 429) { + throw new DDoSProtection (feedback); + } else if (code === 500) { + throw new ExchangeError (feedback); + } else if (code === 503) { + throw new ExchangeNotAvailable (feedback); + } else if (code === 504) { + throw new RequestTimeout (feedback); + } + throw new ExchangeError (feedback); // unknown message + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],78:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { TICK_SIZE } = require ('./base/functions/number'); +const { AuthenticationError, ExchangeError, ArgumentsRequired, PermissionDenied, InvalidOrder, OrderNotFound, DDoSProtection, NotSupported, ExchangeNotAvailable, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class deribit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'deribit', + 'name': 'Deribit', + 'countries': [ 'NL' ], // Netherlands + 'version': 'v1', + 'userAgent': undefined, + 'rateLimit': 2000, + 'has': { + 'CORS': true, + 'editOrder': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchTickers': false, + }, + 'urls': { + 'test': 'https://test.deribit.com', + 'logo': 'https://user-images.githubusercontent.com/1294454/41933112-9e2dd65a-798b-11e8-8440-5bab2959fcb8.jpg', + 'api': 'https://www.deribit.com', + 'www': 'https://www.deribit.com', + 'doc': [ + 'https://docs.deribit.com', + 'https://github.com/deribit', + ], + 'fees': 'https://www.deribit.com/pages/information/fees', + 'referral': 'https://www.deribit.com/reg-1189.4038', + }, + 'api': { + 'public': { + 'get': [ + 'ping', + 'test', + 'getinstruments', + 'index', + 'getcurrencies', + 'getorderbook', + 'getlasttrades', + 'getsummary', + 'stats', + 'getannouncments', + ], + }, + 'private': { + 'get': [ + 'account', + 'getopenorders', + 'positions', + 'orderhistory', + 'orderstate', + 'tradehistory', + 'newannouncements', + ], + 'post': [ + 'buy', + 'sell', + 'edit', + 'cancel', + 'cancelall', + ], + }, + }, + 'exceptions': { + // 0 or absent Success, No error + '9999': PermissionDenied, // "api_not_enabled" User didn't enable API for the Account + '10000': AuthenticationError, // "authorization_required" Authorization issue, invalid or absent signature etc + '10001': ExchangeError, // "error" Some general failure, no public information available + '10002': InvalidOrder, // "qty_too_low" Order quantity is too low + '10003': InvalidOrder, // "order_overlap" Rejection, order overlap is found and self-trading is not enabled + '10004': OrderNotFound, // "order_not_found" Attempt to operate with order that can't be found by specified id + '10005': InvalidOrder, // "price_too_low " Price is too low, defines current limit for the operation + '10006': InvalidOrder, // "price_too_low4idx " Price is too low for current index, defines current bottom limit for the operation + '10007': InvalidOrder, // "price_too_high " Price is too high, defines current up limit for the operation + '10008': InvalidOrder, // "price_too_high4idx " Price is too high for current index, defines current up limit for the operation + '10009': InsufficientFunds, // "not_enough_funds" Account has not enough funds for the operation + '10010': OrderNotFound, // "already_closed" Attempt of doing something with closed order + '10011': InvalidOrder, // "price_not_allowed" This price is not allowed for some reason + '10012': InvalidOrder, // "book_closed" Operation for instrument which order book had been closed + '10013': PermissionDenied, // "pme_max_total_open_orders " Total limit of open orders has been exceeded, it is applicable for PME users + '10014': PermissionDenied, // "pme_max_future_open_orders " Limit of count of futures' open orders has been exceeded, it is applicable for PME users + '10015': PermissionDenied, // "pme_max_option_open_orders " Limit of count of options' open orders has been exceeded, it is applicable for PME users + '10016': PermissionDenied, // "pme_max_future_open_orders_size " Limit of size for futures has been exceeded, it is applicable for PME users + '10017': PermissionDenied, // "pme_max_option_open_orders_size " Limit of size for options has been exceeded, it is applicable for PME users + '10019': PermissionDenied, // "locked_by_admin" Trading is temporary locked by admin + '10020': ExchangeError, // "invalid_or_unsupported_instrument" Instrument name is not valid + '10022': InvalidOrder, // "invalid_quantity" quantity was not recognized as a valid number + '10023': InvalidOrder, // "invalid_price" price was not recognized as a valid number + '10024': InvalidOrder, // "invalid_max_show" max_show parameter was not recognized as a valid number + '10025': InvalidOrder, // "invalid_order_id" Order id is missing or its format was not recognized as valid + '10026': InvalidOrder, // "price_precision_exceeded" Extra precision of the price is not supported + '10027': InvalidOrder, // "non_integer_contract_amount" Futures contract amount was not recognized as integer + '10028': DDoSProtection, // "too_many_requests" Allowed request rate has been exceeded + '10029': OrderNotFound, // "not_owner_of_order" Attempt to operate with not own order + '10030': ExchangeError, // "must_be_websocket_request" REST request where Websocket is expected + '10031': ExchangeError, // "invalid_args_for_instrument" Some of arguments are not recognized as valid + '10032': InvalidOrder, // "whole_cost_too_low" Total cost is too low + '10033': NotSupported, // "not_implemented" Method is not implemented yet + '10034': InvalidOrder, // "stop_price_too_high" Stop price is too high + '10035': InvalidOrder, // "stop_price_too_low" Stop price is too low + '11035': InvalidOrder, // "no_more_stops " Allowed amount of stop orders has been exceeded + '11036': InvalidOrder, // "invalid_stoppx_for_index_or_last" Invalid StopPx (too high or too low) as to current index or market + '11037': InvalidOrder, // "outdated_instrument_for_IV_order" Instrument already not available for trading + '11038': InvalidOrder, // "no_adv_for_futures" Advanced orders are not available for futures + '11039': InvalidOrder, // "no_adv_postonly" Advanced post-only orders are not supported yet + '11040': InvalidOrder, // "impv_not_in_range 0..499%" Implied volatility is out of allowed range + '11041': InvalidOrder, // "not_adv_order" Advanced order properties can't be set if the order is not advanced + '11042': PermissionDenied, // "permission_denied" Permission for the operation has been denied + '11044': OrderNotFound, // "not_open_order" Attempt to do open order operations with the not open order + '11045': ExchangeError, // "invalid_event" Event name has not been recognized + '11046': ExchangeError, // "outdated_instrument" At several minutes to instrument expiration, corresponding advanced implied volatility orders are not allowed + '11047': ExchangeError, // "unsupported_arg_combination" The specified combination of arguments is not supported + '11048': ExchangeError, // "not_on_this_server" The requested operation is not available on this server. + '11050': ExchangeError, // "invalid_request" Request has not been parsed properly + '11051': ExchangeNotAvailable, // "system_maintenance" System is under maintenance + '11030': ExchangeError, // "other_reject " Some rejects which are not considered as very often, more info may be specified in + '11031': ExchangeError, // "other_error " Some errors which are not considered as very often, more info may be specified in + }, + 'precisionMode': TICK_SIZE, + 'options': { + 'fetchTickerQuotes': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetGetinstruments (params); + const markets = this.safeValue (response, 'result'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'instrumentName'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const type = this.safeString (market, 'kind'); + const future = (type === 'future'); + const option = (type === 'option'); + const active = this.safeValue (market, 'isActive'); + const precision = { + 'amount': this.safeFloat (market, 'minTradeAmount'), + 'price': this.safeFloat (market, 'tickSize'), + }; + result.push ({ + 'id': id, + 'symbol': id, + 'base': base, + 'quote': quote, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minTradeAmount'), + 'max': undefined, + }, + 'price': { + 'min': this.safeFloat (market, 'tickSize'), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'type': type, + 'spot': false, + 'future': future, + 'option': option, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccount (params); + // + // { + // "usOut":1569048827943520, + // "usIn":1569048827943020, + // "usDiff":500, + // "testnet":false, + // "success":true, + // "result":{ + // "equity":2e-9, + // "maintenanceMargin":0.0, + // "initialMargin":0.0, + // "availableFunds":0.0, + // "balance":0.0, + // "marginBalance":0.0, + // "SUPL":0.0, + // "SRPL":0.0, + // "PNL":0.0, + // "optionsPNL":0.0, + // "optionsSUPL":0.0, + // "optionsSRPL":0.0, + // "optionsD":0.0, + // "optionsG":0.0, + // "optionsV":0.0, + // "optionsTh":0.0, + // "futuresPNL":0.0, + // "futuresSUPL":0.0, + // "futuresSRPL":0.0, + // "deltaTotal":0.0, + // "sessionFunding":0.0, + // "depositAddress":"13tUtNsJSZa1F5GeCmwBywVrymHpZispzw", + // "currency":"BTC" + // }, + // "message":"" + // } + // + const result = { + 'info': response, + }; + const balance = this.safeValue (response, 'result', {}); + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableFunds'); + account['used'] = this.safeFloat (balance, 'maintenanceMargin'); + account['total'] = this.safeFloat (balance, 'equity'); + result[code] = account; + return this.parseBalance (result); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetAccount (this.extend (request, params)); + const result = this.safeValue (response, 'result', {}); + const address = this.safeString (result, 'depositAddress'); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "currentFunding":0.0, + // "funding8h":0.0000017213784611422821, + // "instrumentName":"BTC-PERPETUAL", + // "openInterest":7600223, + // "openInterestAmount":76002230, + // "high":7665.5, + // "low":7450.0, + // "volume":12964792.0, + // "volumeUsd":129647920, + // "volumeBtc":17214.63595316, + // "last":7520.5, + // "bidPrice":7520.0, + // "askPrice":7520.5, + // "midPrice":7520.25, + // "estDelPrice":"", + // "markPrice":7521.0, + // "created":"2019-12-09 15:17:00 GMT" + // } + // + const timestamp = this.safeInteger (ticker, 'created'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'instrumentName'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.publicGetGetsummary (this.extend (request, params)); + // + // { + // "usOut":1575904620528163, + // "usIn":1575904620528129, + // "usDiff":34, + // "testnet":false, + // "success":true, + // "result": { + // "currentFunding":0.0, + // "funding8h":0.0000017213784611422821, + // "instrumentName":"BTC-PERPETUAL", + // "openInterest":7600223, + // "openInterestAmount":76002230, + // "high":7665.5, + // "low":7450.0, + // "volume":12964792.0, + // "volumeUsd":129647920, + // "volumeBtc":17214.63595316, + // "last":7520.5, + // "bidPrice":7520.0, + // "askPrice":7520.5, + // "midPrice":7520.25, + // "estDelPrice":"", + // "markPrice":7521.0, + // "created":"2019-12-09 15:17:00 GMT" + // }, + // "message":"" + // } + // + return this.parseTicker (response['result'], market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "tradeId":23197559, + // "instrument":"BTC-28JUN19", + // "timeStamp":1559643011379, + // "tradeSeq":1997200, + // "quantity":2, + // "amount":20.0, + // "price":8010.0, + // "direction":"sell", + // "tickDirection":2, + // "indexPrice":7969.01 + // } + // + // fetchMyTrades (private) + // + // { + // "quantity":54, + // "amount":540.0, + // "tradeId":23087297, + // "instrument":"BTC-PERPETUAL", + // "timeStamp":1559604178803, + // "tradeSeq":8265011, + // "price":8213.0, + // "side":"sell", + // "orderId":12373631800, + // "matchingId":0, + // "liquidity":"T", + // "fee":0.000049312, + // "feeCurrency":"BTC", + // "tickDirection":3, + // "indexPrice":8251.94, + // "selfTrade":false + // } + // + const id = this.safeString (trade, 'tradeId'); + const orderId = this.safeString (trade, 'orderId'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'timeStamp'); + const side = this.safeString2 (trade, 'side', 'direction'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'feeCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } else { + request['limit'] = 10000; + } + const response = await this.publicGetGetlasttrades (this.extend (request, params)); + // + // { + // "usOut":1559643108984527, + // "usIn":1559643108984470, + // "usDiff":57, + // "testnet":false, + // "success":true, + // "result": [ + // { + // "tradeId":23197559, + // "instrument":"BTC-28JUN19", + // "timeStamp":1559643011379, + // "tradeSeq":1997200, + // "quantity":2, + // "amount":20.0, + // "price":8010.0, + // "direction":"sell", + // "tickDirection":2, + // "indexPrice":7969.01 + // } + // ], + // "message":"" + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.publicGetGetorderbook (this.extend (request, params)); + const timestamp = this.safeInteger (response, 'usOut') / 1000; + const orderbook = this.parseOrderBook (response['result'], timestamp, 'bids', 'asks', 'price', 'quantity'); + return this.extend (orderbook, { + 'nonce': this.safeInteger (response, 'tstamp'), + }); + } + + parseOrderStatus (status) { + const statuses = { + 'open': 'open', + 'cancelled': 'canceled', + 'filled': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "orderId": 5258039, // ID of the order + // "type": "limit", // not documented, but present in the actual response + // "instrument": "BTC-26MAY17", // instrument name (market id) + // "direction": "sell", // order direction, "buy" or "sell" + // "price": 1860, // float, USD for futures, BTC for options + // "label": "", // label set by the owner, up to 32 chars + // "quantity": 10, // quantity, in contracts ($10 per contract for futures, ฿1 — for options) + // "filledQuantity": 3, // filled quantity, in contracts ($10 per contract for futures, ฿1 — for options) + // "avgPrice": 1860, // average fill price of the order + // "commission": -0.000001613, // in BTC units + // "created": 1494491899308, // creation timestamp + // "state": "open", // open, cancelled, etc + // "postOnly": false // true for post-only orders only + // open orders -------------------------------------------------------- + // "lastUpdate": 1494491988754, // timestamp of the last order state change (before this cancelorder of course) + // closed orders ------------------------------------------------------ + // "tstamp": 1494492913288, // timestamp of the last order state change, documented, but may be missing in the actual response + // "modified": 1494492913289, // timestamp of the last db write operation, e.g. trade that doesn't change order status, documented, but may missing in the actual response + // "adv": false // advanced type (false, or "usd" or "implv") + // "trades": [], // not documented, injected from the outside of the parseOrder method into the order + // } + // + const timestamp = this.safeInteger (order, 'created'); + const lastUpdate = this.safeInteger (order, 'lastUpdate'); + let lastTradeTimestamp = this.safeInteger2 (order, 'tstamp', 'modified'); + const id = this.safeString (order, 'orderId'); + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avgPrice'); + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'filledQuantity'); + if (lastTradeTimestamp === undefined) { + if (filled !== undefined) { + if (filled > 0) { + lastTradeTimestamp = lastUpdate; + } + } + } + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (price !== undefined) { + cost = price * filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const side = this.safeStringLower (order, 'direction'); + let feeCost = this.safeFloat (order, 'commission'); + if (feeCost !== undefined) { + feeCost = Math.abs (feeCost); + } + const fee = { + 'cost': feeCost, + 'currency': 'BTC', + }; + const type = this.safeString (order, 'type'); + const marketId = this.safeString (order, 'instrument'); + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': undefined, // todo: parse trades + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privateGetOrderstate (this.extend (request, params)); + const result = this.safeValue (response, 'result'); + if (result === undefined) { + throw new OrderNotFound (this.id + ' fetchOrder() ' + this.json (response)); + } + return this.parseOrder (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'instrument': this.marketId (symbol), + 'quantity': amount, + 'type': type, + // 'post_only': 'false' or 'true', https://github.com/ccxt/ccxt/issues/5159 + }; + if (price !== undefined) { + request['price'] = price; + } + const method = 'privatePost' + this.capitalize (side); + const response = await this[method] (this.extend (request, params)); + const order = this.safeValue (response['result'], 'order'); + if (order === undefined) { + return response; + } + return this.parseOrder (order); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + if (amount !== undefined) { + request['quantity'] = amount; + } + if (price !== undefined) { + request['price'] = price; + } + const response = await this.privatePostEdit (this.extend (request, params)); + return this.parseOrder (response['result']['order']); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privatePostCancel (this.extend (request, params)); + return this.parseOrder (response['result']['order']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.privateGetGetopenorders (this.extend (request, params)); + return this.parseOrders (response['result'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.privateGetOrderhistory (this.extend (request, params)); + return this.parseOrders (response['result'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; // default = 20 + } + const response = await this.privateGetTradehistory (this.extend (request, params)); + // + // { + // "usOut":1559611553394836, + // "usIn":1559611553394000, + // "usDiff":836, + // "testnet":false, + // "success":true, + // "result": [ + // { + // "quantity":54, + // "amount":540.0, + // "tradeId":23087297, + // "instrument":"BTC-PERPETUAL", + // "timeStamp":1559604178803, + // "tradeSeq":8265011, + // "price":8213.0, + // "side":"sell", + // "orderId":12373631800, + // "matchingId":0, + // "liquidity":"T", + // "fee":0.000049312, + // "feeCurrency":"BTC", + // "tickDirection":3, + // "indexPrice":8251.94, + // "selfTrade":false + // } + // ], + // "message":"", + // "has_more":true + // } + // + const trades = this.safeValue (response, 'result', []); + return this.parseTrades (trades, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = '/' + 'api/' + this.version + '/' + api + '/' + path; + let url = this.urls['api'] + query; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = '_=' + nonce + '&_ackey=' + this.apiKey + '&_acsec=' + this.secret + '&_action=' + query; + if (Object.keys (params).length) { + params = this.keysort (params); + auth += '&' + this.urlencode (params); + } + const hash = this.hash (this.encode (auth), 'sha256', 'base64'); + const signature = this.apiKey + '.' + nonce + '.' + this.decode (hash); + headers = { + 'x-deribit-sig': signature, + }; + if (method !== 'GET') { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + body = this.urlencode (params); + } else if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // fallback to default error handler + } + // + // {"usOut":1535877098645376,"usIn":1535877098643364,"usDiff":2012,"testnet":false,"success":false,"message":"order_not_found","error":10004} + // + const error = this.safeString (response, 'error'); + if ((error !== undefined) && (error !== '0')) { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],79:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AccountSuspended, BadRequest, BadResponse, NetworkError, DDoSProtection, AuthenticationError, PermissionDenied, ArgumentsRequired, ExchangeError, InsufficientFunds, InvalidOrder, InvalidNonce, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class digifinex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'digifinex', + 'name': 'DigiFinex', + 'countries': [ 'SG' ], + 'version': 'v3', + 'rateLimit': 900, // 300 for posts + 'has': { + 'cancelOrders': true, + 'fetchOrders': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchMyTrades': true, + 'fetchLedger': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '4h': '240', + '12h': '720', + '1d': '1D', + '1w': '1W', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/62184319-304e8880-b366-11e9-99fe-8011d6929195.jpg', + 'api': 'https://openapi.digifinex.vip', + 'www': 'https://www.digifinex.vip', + 'doc': [ + 'https://docs.digifinex.vip', + ], + 'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328482-Fee-Structure-on-DigiFinex', + 'referral': 'https://www.digifinex.vip/en-ww/from/DhOzBg/3798****5114', + }, + 'api': { + 'v2': { + 'get': [ + 'ticker', + ], + }, + 'public': { + 'get': [ + '{market}/symbols', + 'kline', + 'margin/currencies', + 'margin/symbols', + 'markets', // undocumented + 'order_book', + 'ping', + 'spot/symbols', + 'time', + 'trades', + ], + }, + 'private': { + 'get': [ + '{market}/financelog', + '{market}/mytrades', + '{market}/order', + '{market}/order/current', + '{market}/order/history', + 'margin/assets', + 'margin/financelog', + 'margin/mytrades', + 'margin/order', + 'margin/order/current', + 'margin/order/history', + 'margin/positions', + 'otc/financelog', + 'spot/assets', + 'spot/financelog', + 'spot/mytrades', + 'spot/order', + 'spot/order/current', + 'spot/order/history', + ], + 'post': [ + '{market}/order/cancel', + '{market}/order/new', + 'margin/order/cancel', + 'margin/order/new', + 'margin/position/close', + 'spot/order/cancel', + 'spot/order/new', + 'transfer', + ], + }, + }, + 'exceptions': { + 'exact': { + '10001': [ BadRequest, "Wrong request method, please check it's a GET ot POST request" ], + '10002': [ AuthenticationError, 'Invalid ApiKey' ], + '10003': [ AuthenticationError, "Sign doesn't match" ], + '10004': [ BadRequest, 'Illegal request parameters' ], + '10005': [ DDoSProtection, 'Request frequency exceeds the limit' ], + '10006': [ PermissionDenied, 'Unauthorized to execute this request' ], + '10007': [ PermissionDenied, 'IP address Unauthorized' ], + '10008': [ InvalidNonce, 'Timestamp for this request is invalid, timestamp must within 1 minute' ], + '10009': [ NetworkError, 'Unexist endpoint, please check endpoint URL' ], + '10011': [ AccountSuspended, 'ApiKey expired. Please go to client side to re-create an ApiKey' ], + '20001': [ PermissionDenied, 'Trade is not open for this trading pair' ], + '20002': [ PermissionDenied, 'Trade of this trading pair is suspended' ], + '20003': [ InvalidOrder, 'Invalid price or amount' ], + '20007': [ InvalidOrder, 'Price precision error' ], + '20008': [ InvalidOrder, 'Amount precision error' ], + '20009': [ InvalidOrder, 'Amount is less than the minimum requirement' ], + '20010': [ InvalidOrder, 'Cash Amount is less than the minimum requirement' ], + '20011': [ InsufficientFunds, 'Insufficient balance' ], + '20012': [ BadRequest, 'Invalid trade type, valid value: buy/sell)' ], + '20013': [ InvalidOrder, 'No order info found' ], + '20014': [ BadRequest, 'Invalid date, Valid format: 2018-07-25)' ], + '20015': [ BadRequest, 'Date exceeds the limit' ], + '20018': [ PermissionDenied, 'Your trading rights have been banned by the system' ], + '20019': [ BadRequest, 'Wrong trading pair symbol. Correct format:"usdt_btc". Quote asset is in the front' ], + '20020': [ DDoSProtection, "You have violated the API operation trading rules and temporarily forbid trading. At present, we have certain restrictions on the user's transaction rate and withdrawal rate." ], + '50000': [ ExchangeError, 'Exception error' ], + }, + 'broad': { + }, + }, + 'options': { + 'defaultType': 'spot', + 'types': [ 'spot', 'margin', 'otc' ], + }, + }); + } + + async fetchMarketsByType (type, params = {}) { + const method = 'publicGet' + this.capitalize (type) + 'Symbols'; + const response = await this[method] (params); + // + // { + // "symbol_list": [ + // { + // "order_types":["LIMIT","MARKET"], + // "quote_asset":"USDT", + // "minimum_value":2, + // "amount_precision":4, + // "status":"TRADING", + // "minimum_amount":0.001, + // "symbol":"LTC_USDT", + // "margin_rate":0.3, + // "zone":"MAIN", + // "base_asset":"LTC", + // "price_precision":2 + // }, + // ], + // "code":0 + // } + // + const markets = this.safeValue (response, 'symbols_list', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'base_asset'); + const quoteId = this.safeString (market, 'quote_asset'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'amount_precision'), + 'price': this.safeInteger (market, 'price_precision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_amount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (market, 'minimum_value'), + 'max': undefined, + }, + }; + // + // The status is documented in the exchange API docs as follows: + // TRADING, HALT (delisted), BREAK (trading paused) + // https://docs.digifinex.vip/en-ww/v3/#/public/spot/symbols + // However, all spot markets actually have status === 'HALT' + // despite that they appear to be active on the exchange website. + // Apparently, we can't trust this status. + // const status = this.safeString (market, 'status'); + // const active = (status === 'TRADING'); + // + const active = undefined; + const spot = (type === 'spot'); + const margin = (type === 'margin'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'type': type, + 'spot': spot, + 'margin': margin, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + // + // { + // "data": [ + // { + // "volume_precision":4, + // "price_precision":2, + // "market":"btc_usdt", + // "min_amount":2, + // "min_volume":0.0001 + // }, + // ], + // "date":1564507456, + // "code":0 + // } + // + const markets = this.safeValue (response, 'data', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'market'); + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'volume_precision'), + 'price': this.safeInteger (market, 'price_precision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'min_volume'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': undefined, + }, + }; + const active = undefined; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const method = 'privateGet' + this.capitalize (type) + 'Assets'; + const response = await this[method] (params); + // + // { + // "code": 0, + // "list": [ + // { + // "currency": "BTC", + // "free": 4723846.89208129, + // "total": 0 + // } + // ] + // } + const balances = this.safeValue (response, 'list', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'frozen'); + account['free'] = this.safeFloat (balance, 'free'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 150 + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { + // "bids": [ + // [9605.77,0.0016], + // [9605.46,0.0003], + // [9602.04,0.0127], + // ], + // "asks": [ + // [9627.22,0.025803], + // [9627.12,0.168543], + // [9626.52,0.0011529], + // ], + // "date":1564509499, + // "code":0 + // } + // + const timestamp = this.safeTimestamp (response, 'date'); + return this.parseOrderBook (response, timestamp); + } + + async fetchTickers (symbols = undefined, params = {}) { + const apiKey = this.safeValue (params, 'apiKey', this.apiKey); + if (!apiKey) { + throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter'); + } + await this.loadMarkets (); + const request = { + 'apiKey': apiKey, + }; + const response = await this.v2GetTicker (this.extend (request, params)); + // + // { + // "ticker":{ + // "btc_eth":{ + // "last":0.021957, + // "base_vol":2249.3521732227, + // "change":-0.6, + // "vol":102443.5111, + // "sell":0.021978, + // "low":0.021791, + // "buy":0.021946, + // "high":0.022266 + // } + // }, + // "date":1564518452, + // "code":0 + // } + // + const result = {}; + const tickers = this.safeValue (response, 'ticker', {}); + const date = this.safeInteger (response, 'date'); + const reversedMarketIds = Object.keys (tickers); + for (let i = 0; i < reversedMarketIds.length; i++) { + const reversedMarketId = reversedMarketIds[i]; + const ticker = this.extend ({ + 'date': date, + }, tickers[reversedMarketId]); + const [ quoteId, baseId ] = reversedMarketId.split ('_'); + const marketId = baseId + '_' + quoteId; + let market = undefined; + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const apiKey = this.safeValue (params, 'apiKey', this.apiKey); + if (!apiKey) { + throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // reversed base/quote in v2 + const marketId = market['quoteId'] + '_' + market['baseId']; + const request = { + 'symbol': marketId, + 'apiKey': apiKey, + }; + const response = await this.v2GetTicker (this.extend (request, params)); + // + // { + // "ticker":{ + // "btc_eth":{ + // "last":0.021957, + // "base_vol":2249.3521732227, + // "change":-0.6, + // "vol":102443.5111, + // "sell":0.021978, + // "low":0.021791, + // "buy":0.021946, + // "high":0.022266 + // } + // }, + // "date":1564518452, + // "code":0 + // } + // + const date = this.safeInteger (response, 'date'); + const ticker = this.safeValue (response, 'ticker', {}); + let result = this.safeValue (ticker, marketId, {}); + result = this.extend ({ 'date': date }, result); + return this.parseTicker (result, market); + } + + parseTicker (ticker, market = undefined) { + // + // fetchTicker, fetchTickers + // + // { + // "last":0.021957, + // "base_vol":2249.3521732227, + // "change":-0.6, + // "vol":102443.5111, + // "sell":0.021978, + // "low":0.021791, + // "buy":0.021946, + // "high":0.022266, + // "date"1564518452, // injected from fetchTicker/fetchTickers + // } + // + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (ticker, 'date'); + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'change'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'base_vol'), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "date":1564520003, + // "id":1596149203, + // "amount":0.7073, + // "type":"buy", + // "price":0.02193, + // } + // + // fetchMyTrades (private) + // + // { + // "symbol": "BTC_USDT", + // "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966", + // "id": 28457, + // "price": 0.1, + // "amount": 0, + // "fee": 0.096, + // "fee_currency": "USDT", + // "timestamp": 1499865549, + // "side": "buy", + // "is_maker": true + // } + // + const id = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'order_id'); + const timestamp = this.safeTimestamp2 (trade, 'date', 'timestamp'); + const side = this.safeString2 (trade, 'type', 'side'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[market]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const takerOrMaker = this.safeValue (trade, 'is_maker'); + const feeCost = this.safeFloat (trade, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fee_currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': orderId, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 500 + } + const response = await this.publicGetTrades (this.extend (request, params)); + // + // { + // "data":[ + // { + // "date":1564520003, + // "id":1596149203, + // "amount":0.7073, + // "type":"buy", + // "price":0.02193, + // }, + // { + // "date":1564520002, + // "id":1596149165, + // "amount":0.3232, + // "type":"sell", + // "price":0.021927, + // }, + // ], + // "code": 0, + // "date": 1564520003, + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseTrades (data, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, // timestamp + ohlcv[5], // open + ohlcv[3], // high + ohlcv[4], // low + ohlcv[2], // close + ohlcv[1], // volume + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'period': this.timeframes[timeframe], + // 'start_time': 1564520003, // starting timestamp, 200 candles before end_time by default + // 'end_time': 1564520003, // ending timestamp, current timestamp by default + }; + if (since !== undefined) { + const startTime = parseInt (since / 1000); + request['start_time'] = startTime; + if (limit !== undefined) { + const duration = this.parseTimeframe (timeframe); + request['end_time'] = this.sum (startTime, limit * duration); + } + } else if (limit !== undefined) { + const endTime = this.seconds (); + const duration = this.parseTimeframe (timeframe); + request['startTime'] = this.sum (endTime, -limit * duration); + } + const response = await this.publicGetKline (this.extend (request, params)); + // + // { + // "code":0, + // "data":[ + // [1556712900,2205.899,0.029967,0.02997,0.029871,0.029927], + // [1556713800,1912.9174,0.029992,0.030014,0.029955,0.02996], + // [1556714700,1556.4795,0.029974,0.030019,0.029969,0.02999], + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOHLCVs (data, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const request = { + 'market': orderType, + 'symbol': market['id'], + 'amount': this.amountToPrecision (symbol, amount), + // 'post_only': 0, // 0 by default, if set to 1 the order will be canceled if it can be executed immediately, making sure there will be no market taking + }; + let suffix = ''; + if (type === 'market') { + suffix = '_market'; + } else { + request['price'] = this.priceToPrecision (symbol, price); + } + request['type'] = side + suffix; + const response = await this.privatePostMarketOrderNew (this.extend (request, params)); + // + // { + // "code": 0, + // "order_id": "198361cecdc65f9c8c9bb2fa68faec40" + // } + // + const result = this.parseOrder (response, market); + return this.extend (result, { + 'symbol': symbol, + 'side': side, + 'type': type, + 'amount': amount, + 'price': price, + }); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const request = { + 'market': orderType, + 'order_id': id, + }; + const response = await this.privatePostMarketOrderCancel (this.extend (request, params)); + // + // { + // "code": 0, + // "success": [ + // "198361cecdc65f9c8c9bb2fa68faec40", + // "3fb0d98e51c18954f10d439a9cf57de0" + // ], + // "error": [ + // "78a7104e3c65cc0c5a212a53e76d0205" + // ] + // } + // + const canceledOrders = this.safeValue (response, 'success', []); + const numCanceledOrders = canceledOrders.length; + if (numCanceledOrders !== 1) { + throw new OrderNotFound (this.id + ' cancelOrder ' + id + ' not found'); + } + return response; + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const request = { + 'market': orderType, + 'order_id': ids.join (','), + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "success": [ + // "198361cecdc65f9c8c9bb2fa68faec40", + // "3fb0d98e51c18954f10d439a9cf57de0" + // ], + // "error": [ + // "78a7104e3c65cc0c5a212a53e76d0205" + // ] + // } + // + const canceledOrders = this.safeValue (response, 'success', []); + const numCanceledOrders = canceledOrders.length; + if (numCanceledOrders < 1) { + throw new OrderNotFound (this.id + ' cancelOrders error'); + } + return response; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', // partially filled + '2': 'closed', + '3': 'canceled', + '4': 'canceled', // partially filled and canceled + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "code": 0, + // "order_id": "198361cecdc65f9c8c9bb2fa68faec40" + // } + // + // fetchOrder, fetchOpenOrders, fetchOrders + // + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // + const id = this.safeString (order, 'order_id'); + const timestamp = this.safeTimestamp (order, 'created_date'); + const lastTradeTimestamp = this.safeTimestamp (order, 'finished_date'); + let side = this.safeString (order, 'type'); + let type = undefined; + if (side !== undefined) { + const parts = side.split ('_'); + const numParts = parts.length; + if (numParts > 1) { + side = parts[0]; + type = parts[1]; + } else { + type = 'limit'; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + if (market === undefined) { + const exchange = order['symbol'].toUpperCase (); + if (exchange in this.markets_by_id) { + market = this.markets_by_id[exchange]; + } + } + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'executed_amount'); + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avg_price'); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = filled * average; + } + if (amount !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'cost': cost, + 'average': average, + 'status': status, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + const request = { + 'market': orderType, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privateGetMarketOrderCurrent (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + const request = { + 'market': orderType, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); // default 3 days from now, max 30 days + } + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 100 + } + const response = await this.privateGetMarketOrderHistory (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'market': orderType, + 'order_id': id, + }; + const response = await this.privateGetMarketOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + const request = { + 'market': orderType, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); // default 3 days from now, max 30 days + } + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 100 + } + const response = await this.privateGetMarketMytrades (this.extend (request, params)); + // + // { + // "code": 0, + // "list": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966", + // "id": 28457, + // "price": 0.1, + // "amount": 0, + // "fee": 0.096, + // "fee_currency": "USDT", + // "timestamp": 1499865549, + // "side": "buy", + // "is_maker": true + // } + // ] + // } + // + const data = this.safeValue (response, 'list', []); + return this.parseTrades (data, market, since, limit); + } + + parseLedgerEntryType (type) { + const types = {}; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // { + // "currency_mark": "BTC", + // "type": 100234, + // "num": 28457, + // "balance": 0.1, + // "time": 1546272000 + // } + // + const id = this.safeString (item, 'num'); + const account = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const code = this.safeCurrencyCode (this.safeString (item, 'currency_mark'), currency); + const timestamp = this.safeTimestamp (item, 'time'); + const before = undefined; + const after = this.safeFloat (item, 'balance'); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'direction': undefined, + 'account': account, + 'referenceId': undefined, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': undefined, + 'before': before, + 'after': after, + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + const request = { + 'market': orderType, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency_mark'] = currency['id']; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 1000 + } + const response = await this.privateGetMarketFinancelog (this.extend (request, params)); + // + // { + // "code": 0, + // "data": { + // "total": 521, + // "finance": [ + // { + // "currency_mark": "BTC", + // "type": 100234, + // "num": 28457, + // "balance": 0.1, + // "time": 1546272000 + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const items = this.safeValue (data, 'finance', []); + return this.parseLedger (items, currency, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const version = (api === 'v2') ? api : this.version; + let url = this.urls['api'] + '/' + version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + const urlencoded = this.urlencode (this.keysort (query)); + if (api === 'private') { + const nonce = this.nonce ().toString (); + const auth = urlencoded; + // the signature is not time-limited :\ + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + if (method === 'GET') { + if (urlencoded) { + url += '?' + urlencoded; + } + } else if (method === 'POST') { + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + if (urlencoded) { + body = urlencoded; + } + } + headers = { + 'ACCESS-KEY': this.apiKey, + 'ACCESS-SIGN': signature, + 'ACCESS-TIMESTAMP': nonce, + }; + } else { + if (urlencoded) { + url += '?' + urlencoded; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + dateUTC8 (timestampMS) { + const timedelta = this.safeValue (this.options, 'timedelta', 8 * 60 * 60 * 1000); // eight hours + return this.ymd (timestampMS + timedelta); + } + + handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { + if (!response) { + return; // fall back to default error handler + } + const code = this.safeString (response, 'code'); + if (code === '0') { + return; // no error + } + const feedback = this.id + ' ' + responseBody; + if (code === undefined) { + throw new BadResponse (feedback); + } + const unknownError = [ ExchangeError, feedback ]; + const [ ExceptionClass, message ] = this.safeValue (this.exceptions['exact'], code, unknownError); + throw new ExceptionClass (message); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],80:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InvalidNonce, BadRequest, InsufficientFunds, PermissionDenied, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class dsx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'dsx', + 'name': 'DSX', + 'countries': [ 'UK' ], + 'rateLimit': 1500, + 'version': 'v3', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': false, + 'fetchOrderBooks': false, + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'fetchTransactions': true, + 'fetchTickers': true, + 'fetchMyTrades': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27990275-1413158a-645a-11e7-931c-94717f7510e3.jpg', + 'api': { + 'public': 'https://dsx.uk/mapi', // market data + 'private': 'https://dsx.uk/tapi', // trading + 'dwapi': 'https://dsx.uk/dwapi', // deposit/withdraw + }, + 'www': 'https://dsx.uk', + 'doc': [ + 'https://dsx.uk/developers/publicApi', + ], + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.15 / 100, + 'taker': 0.25 / 100, + }, + }, + 'timeframes': { + '1m': 'm', + '1h': 'h', + '1d': 'd', + }, + 'api': { + // market data (public) + 'public': { + 'get': [ + 'barsFromMoment/{pair}/{period}/{start}', + 'depth/{pair}', + 'info', + 'lastBars/{pair}/{period}/{amount}', // period is 'm', 'h' or 'd' + 'periodBars/{pair}/{period}/{start}/{end}', + 'ticker/{pair}', + 'trades/{pair}', + ], + }, + // trading (private) + 'private': { + 'post': [ + 'info/account', + 'history/transactions', + 'history/trades', + 'history/orders', + 'orders', + 'order/cancel', + // 'order/cancel/all', + 'order/status', + 'order/new', + 'volume', + 'fees', // trading fee schedule + ], + }, + // deposit / withdraw (private) + 'dwapi': { + 'post': [ + 'deposit/cryptoaddress', + 'withdraw/crypto', + 'withdraw/fiat', + 'withdraw/submit', + // 'withdraw/cancel', + 'transaction/status', // see 'history/transactions' in private tapi above + ], + }, + }, + 'exceptions': { + 'exact': { + 'Sign is invalid': AuthenticationError, // {"success":0,"error":"Sign is invalid"} + 'Order was rejected. Incorrect price.': InvalidOrder, // {"success":0,"error":"Order was rejected. Incorrect price."} + "Order was rejected. You don't have enough money.": InsufficientFunds, // {"success":0,"error":"Order was rejected. You don't have enough money."} + 'This method is blocked for your pair of keys': PermissionDenied, // {"success":0,"error":"This method is blocked for your pair of keys"} + }, + 'broad': { + 'INVALID_PARAMETER': BadRequest, + 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} + 'invalid api key': AuthenticationError, + 'invalid sign': AuthenticationError, + 'api key dont have trade permission': AuthenticationError, + 'invalid parameter': InvalidOrder, + 'invalid order': InvalidOrder, + 'Requests too often': DDoSProtection, + 'not available': ExchangeNotAvailable, + 'data unavailable': ExchangeNotAvailable, + 'external service unavailable': ExchangeNotAvailable, + 'nonce is invalid': InvalidNonce, // {"success":0,"error":"Parameter: nonce is invalid"} + }, + }, + 'options': { + 'fetchTickersMaxLength': 250, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInfo (params); + // + // { + // "server_time": 1522057909, + // "pairs": { + // "ethusd": { + // "decimal_places": 5, + // "min_price": 100, + // "max_price": 1500, + // "min_amount": 0.01, + // "hidden": 0, + // "fee": 0, + // "amount_decimal_places": 4, + // "quoted_currency": "USD", + // "base_currency": "ETH" + // } + // } + // } + // + const markets = this.safeValue (response, 'pairs'); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quoted_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'decimal_places'), + 'price': this.safeInteger (market, 'decimal_places'), + }; + const amountLimits = { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }; + const priceLimits = { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }; + const costLimits = { + 'min': this.safeFloat (market, 'min_total'), + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const hidden = this.safeInteger (market, 'hidden'); + const active = (hidden === 0); + // see parseMarket below + // https://github.com/ccxt/ccxt/pull/5786 + const otherId = base.toLowerCase () + quote.toLowerCase (); + result.push ({ + 'id': id, + 'otherId': otherId, // https://github.com/ccxt/ccxt/pull/5786 + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostInfoAccount (); + // + // { + // "success" : 1, + // "return" : { + // "funds" : { + // "BTC" : { + // "total" : 0, + // "available" : 0 + // }, + // "USD" : { + // "total" : 0, + // "available" : 0 + // }, + // "USDT" : { + // "total" : 0, + // "available" : 0 + // } + // }, + // "rights" : { + // "info" : 1, + // "trade" : 1 + // }, + // "transactionCount" : 0, + // "openOrders" : 0, + // "serverTime" : 1537451465 + // } + // } + // + const balances = this.safeValue (response, 'return'); + const result = { 'info': response }; + const funds = this.safeValue (balances, 'funds'); + const currencyIds = Object.keys (funds); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (funds, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseTicker (ticker, market = undefined) { + // + // { high: 0.03492, + // low: 0.03245, + // avg: 29.46133, + // vol: 500.8661, + // vol_cur: 17.000797104, + // last: 0.03364, + // buy: 0.03362, + // sell: 0.03381, + // updated: 1537521993, + // pair: "ethbtc" } + // + const timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'pair'); + market = this.parseMarket (marketId); + if (market !== undefined) { + symbol = market['symbol']; + } + // dsx average is inverted, liqui average is not + let average = this.safeFloat (ticker, 'avg'); + if (average !== undefined) { + if (average > 0) { + average = 1 / average; + } + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'vol_cur'), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "amount" : 0.0128, + // "price" : 6483.99000, + // "timestamp" : 1540334614, + // "tid" : 35684364, + // "type" : "ask" + // } + // + // fetchMyTrades (private) + // + // { + // "number": "36635882", // <-- this is present if the trade has come from the '/order/status' call + // "id": "36635882", // <-- this may have been artifically added by the parseTrades method + // "pair": "btcusd", + // "type": "buy", + // "volume": 0.0595, + // "rate": 9750, + // "orderId": 77149299, + // "timestamp": 1519612317, + // "commission": 0.00020825, + // "commissionCurrency": "btc" + // } + // + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let side = this.safeString (trade, 'type'); + if (side === 'ask') { + side = 'sell'; + } else if (side === 'bid') { + side = 'buy'; + } + const price = this.safeFloat2 (trade, 'rate', 'price'); + const id = this.safeString2 (trade, 'number', 'id'); + const orderId = this.safeString (trade, 'orderId'); + const marketId = this.safeString (trade, 'pair'); + market = this.parseMarket (marketId); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const amount = this.safeFloat2 (trade, 'amount', 'volume'); + const type = 'limit'; // all trades are still limit trades + let takerOrMaker = undefined; + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const isYourOrder = this.safeValue (trade, 'is_your_order'); + if (isYourOrder !== undefined) { + takerOrMaker = 'taker'; + if (isYourOrder) { + takerOrMaker = 'maker'; + } + if (fee === undefined) { + fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); + } + } + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + parseTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) { + let result = []; + if (Array.isArray (trades)) { + for (let i = 0; i < trades.length; i++) { + result.push (this.parseTrade (trades[i], market)); + } + } else { + const ids = Object.keys (trades); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const trade = this.parseTrade (trades[id], market); + result.push (this.extend (trade, { 'id': id }, params)); + } + } + result = this.sortBy (result, 'timestamp'); + const symbol = (market !== undefined) ? market['symbol'] : undefined; + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const market_id_in_reponse = (market['id'] in response); + if (!market_id_in_reponse) { + throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); + } + const orderbook = response[market['id']]; + return this.parseOrderBook (orderbook); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join ('-'); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const result = {}; + ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + if (id in this.markets_by_id) { + const market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseOrderBook (response[id]); + } + return result; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = this.ids; + if (symbols === undefined) { + const numIds = ids.length; + ids = ids.join ('-'); + const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); + // max URL length is 2048 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + const tickers = await this.publicGetTickerPair (this.extend (request, params)); + // + // { + // "bchbtc" : { + // "high" : 0.02989, + // "low" : 0.02736, + // "avg" : 33.90585, + // "vol" : 0.65982205, + // "vol_cur" : 0.0194604180960, + // "last" : 0.03000, + // "buy" : 0.02980, + // "sell" : 0.03001, + // "updated" : 1568104614, + // "pair" : "bchbtc" + // }, + // "ethbtc" : { + // "high" : 0.01772, + // "low" : 0.01742, + // "avg" : 56.89082, + // "vol" : 229.247115044, + // "vol_cur" : 4.02959737298943, + // "last" : 0.01769, + // "buy" : 0.01768, + // "sell" : 0.01776, + // "updated" : 1568104614, + // "pair" : "ethbtc" + // } + // } + // + const result = {}; + const keys = Object.keys (tickers); + for (let k = 0; k < keys.length; k++) { + const id = keys[k]; + const ticker = tickers[id]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTradesPair (this.extend (request, params)); + if (Array.isArray (response)) { + const numElements = response.length; + if (numElements === 0) { + return []; + } + } + return this.parseTrades (response[market['id']], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "high" : 0.01955, + // "open" : 0.01955, + // "low" : 0.01955, + // "close" : 0.01955, + // "amount" : 2.5, + // "timestamp" : 1565155740000 + // } + // + return [ + this.safeInteger (ohlcv, 'timestamp'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'amount'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'period': this.timeframes[timeframe], + }; + let method = 'publicGetLastBarsPairPeriodAmount'; + if (since === undefined) { + if (limit === undefined) { + limit = 100; // required, max 2000 + } + request['amount'] = limit; + } else { + method = 'publicGetPeriodBarsPairPeriodStartEnd'; + // in their docs they expect milliseconds + // but it returns empty arrays with milliseconds + // however, it does work properly with seconds + request['start'] = parseInt (since / 1000); + if (limit === undefined) { + request['end'] = this.seconds (); + } else { + const duration = this.parseTimeframe (timeframe) * 1000; + const end = this.sum (since, duration * limit); + request['end'] = parseInt (end / 1000); + } + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "ethbtc": [ + // { + // "high" : 0.01955, + // "open" : 0.01955, + // "low" : 0.01955, + // "close" : 0.01955, + // "amount" : 2.5, + // "timestamp" : 1565155740000 + // }, + // { + // "high" : 0.01967, + // "open" : 0.01967, + // "low" : 0.01967, + // "close" : 0.01967, + // "amount" : 0, + // "timestamp" : 1565155680000 + // } + // ] + // } + // + const candles = this.safeValue (response, market['id'], []); + return this.parseOHLCVs (candles, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (type === 'market' && price === undefined) { + throw new ArgumentsRequired (this.id + ' createOrder requires a price argument even for market orders, that is the worst price that you agree to fill your order for'); + } + const request = { + 'pair': market['id'], + 'type': side, + 'volume': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + 'orderType': type, + }; + price = parseFloat (price); + amount = parseFloat (amount); + const response = await this.privatePostOrderNew (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "received": 0, + // "remains": 10, + // "funds": { + // "BTC": { + // "total": 100, + // "available": 95 + // }, + // "USD": { + // "total": 10000, + // "available": 9995 + // }, + // "EUR": { + // "total": 1000, + // "available": 995 + // }, + // "LTC": { + // "total": 1000, + // "available": 995 + // } + // }, + // "orderId": 0, // https://github.com/ccxt/ccxt/issues/3677 + // } + // } + // + let status = 'open'; + let filled = 0.0; + let remaining = amount; + const responseReturn = this.safeValue (response, 'return'); + let id = this.safeString2 (responseReturn, 'orderId', 'order_id'); + if (id === '0') { + id = this.safeString (responseReturn, 'initOrderId', 'init_order_id'); + status = 'closed'; + } + filled = this.safeFloat (responseReturn, 'received', 0.0); + remaining = this.safeFloat (responseReturn, 'remains', amount); + const timestamp = this.milliseconds (); + return { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * filled, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'fee': undefined, + // 'trades': this.parseTrades (order['trades'], market), + }; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', // Active + '1': 'closed', // Filled + '2': 'canceled', // Killed + '3': 'canceling', // Killing + '7': 'canceled', // Rejected + }; + return this.safeString (statuses, status, status); + } + + parseMarket (id) { + if (id in this.markets_by_id) { + return this.markets_by_id[id]; + } else { + // the following is a fix for + // https://github.com/ccxt/ccxt/pull/5786 + // https://github.com/ccxt/ccxt/issues/5770 + let markets_by_other_id = this.safeValue (this.options, 'markets_by_other_id'); + if (markets_by_other_id === undefined) { + this.options['markets_by_other_id'] = this.indexBy (this.markets, 'otherId'); + markets_by_other_id = this.options['markets_by_other_id']; + } + if (id in markets_by_other_id) { + return markets_by_other_id[id]; + } + } + return undefined; + } + + parseOrder (order, market = undefined) { + // + // fetchOrder + // + // { + // "number": 36635882, + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit", + // "deals": [ + // { + // "pair": "btcusd", + // "type": "buy", + // "amount": 1, + // "rate": 1000.0, + // "orderId": 1, + // "timestamp": 1496672724, + // "commission": 0.001, + // "commissionCurrency": "btc" + // } + // ] + // } + // + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.safeTimestamp (order, 'timestampCreated'); + const marketId = this.safeString (order, 'pair'); + market = this.parseMarket (marketId); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const remaining = this.safeFloat (order, 'remainingVolume'); + const amount = this.safeFloat (order, 'volume'); + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + cost = price * filled; + } + } + const orderType = this.safeString (order, 'orderType'); + const side = this.safeString (order, 'type'); + let fee = undefined; + const deals = this.safeValue (order, 'deals', []); + const numDeals = deals.length; + let trades = undefined; + let lastTradeTimestamp = undefined; + if (numDeals > 0) { + trades = this.parseTrades (deals); + let feeCost = undefined; + let feeCurrency = undefined; + for (let i = 0; i < trades.length; i++) { + const trade = trades[i]; + if (feeCost === undefined) { + feeCost = 0; + } + feeCost = this.sum (feeCost, trade['fee']['cost']); + feeCurrency = trade['fee']['currency']; + lastTradeTimestamp = trade['timestamp']; + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + } + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': parseInt (id), + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit", + // "deals": [ + // { + // "pair": "btcusd", + // "type": "buy", + // "amount": 1, + // "rate": 1000.0, + // "orderId": 1, + // "timestamp": 1496672724, + // "commission": 0.001, + // "commissionCurrency": "btc" + // } + // ] + // } + // } + // + return this.parseOrder (this.extend ({ + 'id': id, + }, response['return'])); + } + + parseOrdersById (orders, symbol = undefined, since = undefined, limit = undefined) { + const ids = Object.keys (orders); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.parseOrder (this.extend ({ + 'id': id.toString (), + }, orders[id])); + result.push (order); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'count': 10, // Decimal, The maximum number of orders to return + // 'fromId': 123, // Decimal, ID of the first order of the selection + // 'endId': 321, // Decimal, ID of the last order of the selection + // 'order': 'ASC', // String, Order in which orders shown. Possible values are "ASC" — from first to last, "DESC" — from last to first. + }; + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "0": { + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit" + // } + // } + // } + // + return this.parseOrdersById (this.safeValue (response, 'return', {}), symbol, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'count': 10, // Decimal, The maximum number of orders to return + // 'fromId': 123, // Decimal, ID of the first order of the selection + // 'endId': 321, // Decimal, ID of the last order of the selection + // 'order': 'ASC', // String, Order in which orders shown. Possible values are "ASC" — from first to last, "DESC" — from last to first. + }; + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privatePostHistoryOrders (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "0": { + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit" + // } + // } + // } + // + return this.parseOrdersById (this.safeValue (response, 'return', {}), symbol, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + // some derived classes use camelcase notation for request fields + const request = { + // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) + // 'count': 1000, // the number of trades for display numerical, default = 1000 + // 'from_id': trade ID, from which the display starts numerical 0 + // 'end_id': trade ID on which the display ends numerical ∞ + // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) + // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) + // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) + // 'pair': 'eth_btc', // default = all markets + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['count'] = parseInt (limit); + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostHistoryTrades (this.extend (request, params)); + let trades = []; + if ('return' in response) { + trades = response['return']; + } + return this.parseTrades (trades, market, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privatePostHistoryTransactions (this.extend (request, params)); + // + // { + // "success": 1, + // "return": [ + // { + // "id": 1, + // "timestamp": 11, + // "type": "Withdraw", + // "amount": 1, + // "currency": "btc", + // "confirmationsCount": 6, + // "address": "address", + // "status": 2, + // "commission": 0.0001 + // } + // ] + // } + // + const transactions = this.safeValue (response, 'return', []); + return this.parseTransactions (transactions, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + '1': 'failed', + '2': 'ok', + '3': 'pending', + '4': 'failed', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // "id": 1, + // "timestamp": 11, // 11 in their docs ( + // "type": "Withdraw", + // "amount": 1, + // "currency": "btc", + // "confirmationsCount": 6, + // "address": "address", + // "status": 2, + // "commission": 0.0001 + // } + // + const timestamp = this.safeTimestamp (transaction, 'timestamp'); + let type = this.safeString (transaction, 'type'); + if (type !== undefined) { + if (type === 'Incoming') { + type = 'deposit'; + } else if (type === 'Withdraw') { + type = 'withdrawal'; + } + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + return { + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'txid'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'address'), + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'fee': { + 'currency': code, + 'cost': this.safeFloat (transaction, 'commission'), + 'rate': undefined, + }, + 'info': transaction, + }; + } + + async createDepositAddress (code, params = {}) { + const request = { + 'new': 1, + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + return response; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.dwapiPostDepositCryptoaddress (this.extend (request, params)); + const result = this.safeValue (response, 'return', {}); + const address = this.safeString (result, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, // not documented in DSX API + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const commission = this.safeValue (params, 'commission'); + if (commission === undefined) { + throw new ArgumentsRequired (this.id + ' withdraw() requires a `commission` (withdrawal fee) parameter (string)'); + } + params = this.omit (params, commission); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + 'commission': commission, + }; + if (tag !== undefined) { + request['address'] += ':' + tag; + } + const response = await this.dwapiPostWithdrawCrypto (this.extend (request, params)); + // + // [ + // { + // "success": 1, + // "return": { + // "transactionId": 2863073 + // } + // } + // ] + // + const data = this.safeValue (response, 'return', {}); + const id = this.safeString (data, 'transactionId'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.omit (params, this.extractParams (path)); + if (api === 'private' || api === 'dwapi') { + url += '/' + this.version + '/' + this.implodeParams (path, params); + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + }, query)); + const signature = this.decode (this.hmac (this.encode (body), this.encode (this.secret), 'sha512', 'base64')); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': signature, + }; + } else if (api === 'public') { + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + url += '/' + this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + if (Object.keys (query).length) { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // 1 - Liqui only returns the integer 'success' key from their private API + // + // { "success": 1, ... } httpCode === 200 + // { "success": 0, ... } httpCode === 200 + // + // 2 - However, exchanges derived from Liqui, can return non-integers + // + // It can be a numeric string + // { "sucesss": "1", ... } + // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) + // + // Or just a string + // { "success": "true", ... } + // { "success": "false", ... }, httpCode >= 200 + // + // Or a boolean + // { "success": true, ... } + // { "success": false, ... }, httpCode >= 200 + // + // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types + // + // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui + // + // To cover points 1, 2, 3 and 4 combined this handler should work like this: + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],81:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, NotSupported, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class exmo extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'exmo', + 'name': 'EXMO', + 'countries': [ 'ES', 'RU' ], // Spain, Russia + 'rateLimit': 350, // once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchClosedOrders': 'emulated', + 'fetchDepositAddress': true, + 'fetchOpenOrders': true, + 'fetchOrder': 'emulated', + 'fetchOrders': 'emulated', + 'fetchOrderTrades': true, + 'fetchOrderBooks': true, + 'fetchMyTrades': true, + 'fetchTickers': true, + 'withdraw': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchFundingFees': true, + 'fetchCurrencies': true, + 'fetchTransactions': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg', + 'api': { + 'public': 'https://api.exmo.com', + 'private': 'https://api.exmo.com', + 'web': 'https://exmo.me', + }, + 'www': 'https://exmo.me', + 'referral': 'https://exmo.me/?ref=131685', + 'doc': [ + 'https://exmo.me/en/api_doc?ref=131685', + 'https://github.com/exmo-dev/exmo_api_lib/tree/master/nodejs', + ], + 'fees': 'https://exmo.com/en/docs/fees', + }, + 'api': { + 'web': { + 'get': [ + 'ctrl/feesAndLimits', + 'en/docs/fees', + ], + }, + 'public': { + 'get': [ + 'currency', + 'order_book', + 'pair_settings', + 'ticker', + 'trades', + ], + }, + 'private': { + 'post': [ + 'user_info', + 'order_create', + 'order_cancel', + 'user_open_orders', + 'user_trades', + 'user_cancelled_orders', + 'order_trades', + 'required_amount', + 'deposit_address', + 'withdraw_crypt', + 'withdraw_get_txid', + 'excode_create', + 'excode_load', + 'wallet_history', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, // fixed funding fees for crypto, see fetchFundingFees below + }, + }, + 'options': { + 'useWebapiForFetchingFees': false, // TODO: figure why Exmo bans us when we try to fetch() their web urls + 'feesAndLimits': { + 'success': 1, + 'ctlr': 'feesAndLimits', + 'error': '', + 'data': { + 'limits': [ + { 'pair': 'BTC/USD', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'BTC/EUR', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'BTC/RUB', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '2000000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'BTC/UAH', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '1500000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'BTC/PLN', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.001', 'max_p': '90000', 'min_a': '1', 'max_a': '2000000' }, + { 'pair': 'BTC/TRY', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '800000', 'min_a': '40', 'max_a': '6000000' }, + { 'pair': 'ROOBEE/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.0001', 'max_a': '10' }, + { 'pair': 'ROOBEE/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'USDC/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'USDC/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '1000' }, + { 'pair': 'USDC/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'USDC/USDT', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'ETZ/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '10' }, + { 'pair': 'ETZ/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ETZ/USDT', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '1000' }, + { 'pair': 'PTI/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' }, + { 'pair': 'PTI/USDT', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' }, + { 'pair': 'PTI/EOS', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '5000', 'min_a': '0.01', 'max_a': '20000' }, + { 'pair': 'ATMCASH/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' }, + { 'pair': 'TRX/UAH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'ETH/TRY', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.1', 'max_p': '80000', 'min_a': '10', 'max_a': '6000000' }, + { 'pair': 'XRP/TRY', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '6000000' }, + { 'pair': 'XLM/TRY', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '6000000' }, + { 'pair': 'MNC/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'MNC/ETH', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '1000' }, + { 'pair': 'MNC/USD', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' }, + { 'pair': 'DAI/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'DAI/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '5000' }, + { 'pair': 'DAI/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'DAI/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '0.5', 'max_a': '30000000' }, + { 'pair': 'MKR/BTC', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'MKR/DAI', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.5', 'max_p': '500000', 'min_a': '0.005', 'max_a': '500000' }, + { 'pair': 'QTUM/BTC', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'QTUM/ETH', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'QTUM/USD', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '10000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'HB/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'SMART/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'SMART/USD', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'SMART/EUR', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'SMART/RUB', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'XEM/BTC', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00015', 'max_a': '100' }, + { 'pair': 'XEM/USD', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'XEM/EUR', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'GUSD/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' }, + { 'pair': 'GUSD/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'GUSD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'LSK/BTC', 'min_q': '1', 'max_q': '200000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' }, + { 'pair': 'LSK/USD', 'min_q': '1', 'max_q': '200000', 'min_p': '0.1', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'LSK/RUB', 'min_q': '1', 'max_q': '200000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '50000000' }, + { 'pair': 'NEO/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'NEO/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'NEO/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1500000', 'min_a': '50', 'max_a': '50000000' }, + { 'pair': 'ADA/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ADA/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'ADA/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'ZRX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ZRX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'GNT/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'GNT/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'TRX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'TRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'TRX/RUB', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'GAS/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'GAS/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'INK/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'INK/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'INK/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'MNX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'MNX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'MNX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'OMG/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'OMG/ETH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'OMG/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'XLM/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'XLM/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'XLM/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'EOS/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'EOS/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'BTG/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'BTG/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'DXT/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'DXT/USD', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'BTCZ/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.1', 'max_a': '100' }, + { 'pair': 'BCH/BTC', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '5', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'BCH/USD', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '30000', 'min_a': '0.0001', 'max_a': '500000' }, + { 'pair': 'BCH/RUB', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '2000000', 'min_a': '0.0001', 'max_a': '50000000' }, + { 'pair': 'BCH/ETH', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.0000001', 'max_p': '200', 'min_a': '0.0001', 'max_a': '5000' }, + { 'pair': 'DASH/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'DASH/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'DASH/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' }, + { 'pair': 'ETH/BTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ETH/LTC', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '100000' }, + { 'pair': 'ETH/USD', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'ETH/EUR', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '10000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'ETH/RUB', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '1', 'max_a': '50000000' }, + { 'pair': 'ETH/UAH', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '1000000', 'min_a': '90', 'max_a': '15000000' }, + { 'pair': 'ETH/PLN', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.001', 'max_p': '8000', 'min_a': '1', 'max_a': '2000000' }, + { 'pair': 'ETC/BTC', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '0.5', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ETC/USD', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'ETC/RUB', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '50000000' }, + { 'pair': 'LTC/BTC', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'LTC/USD', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'LTC/EUR', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'LTC/RUB', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' }, + { 'pair': 'ZEC/BTC', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ZEC/USD', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'ZEC/EUR', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'ZEC/RUB', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'XRP/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'XRP/USD', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, + { 'pair': 'XRP/RUB', 'min_q': '1', 'max_q': '100000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '50000000' }, + { 'pair': 'XMR/BTC', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'XMR/USD', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'XMR/EUR', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'BTC/USDT', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.01', 'max_p': '30000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'ETH/USDT', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'USDT/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'USDT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'USD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'DOGE/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'WAVES/BTC', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'WAVES/RUB', 'min_q': '0.5', 'max_q': '10000', 'min_p': '1', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' }, + { 'pair': 'KICK/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'KICK/ETH', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' }, + { 'pair': 'KICK/USDT', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' }, + { 'pair': 'EOS/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'BCH/EUR', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'XRP/EUR', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, + { 'pair': 'XRP/UAH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '15000000' }, + { 'pair': 'XEM/UAH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'BCH/USDT', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'DASH/USDT', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'BCH/UAH', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.1', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'XRP/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, + { 'pair': 'USDT/UAH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '1', 'max_p': '3000', 'min_a': '2', 'max_a': '15000000' }, + { 'pair': 'USDT/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'ZRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'BTG/ETH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'WAVES/USD', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.001', 'max_p': '3500', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'DOGE/USD', 'min_q': '100', 'max_q': '1000000000', 'min_p': '0.0000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'XRP/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' }, + { 'pair': 'DASH/UAH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '200000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'XMR/ETH', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'WAVES/ETH', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.00001', 'max_p': '30', 'min_a': '0.0035', 'max_a': '3500' }, + { 'pair': 'DCR/BTC', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'DCR/RUB', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '3000000' }, + { 'pair': 'DCR/UAH', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.25', 'max_a': '1000000' }, + { 'pair': 'ZAG/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'EXM/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0000001', 'max_a': '100' }, + { 'pair': 'VLX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'BTT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'BTT/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'BTT/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' }, + ], + 'fees': [ + { + 'group': 'crypto', + 'title': 'Криптовалюта', + 'items': [ + { 'prov': 'EXM', 'dep': '0%', 'wd': '1 EXM' }, + { 'prov': 'BTC', 'dep': '0%', 'wd': '0.0005 BTC' }, + { 'prov': 'LTC', 'dep': '0%', 'wd': '0.01 LTC' }, + { 'prov': 'DOGE', 'dep': '0%', 'wd': '1 DOGE' }, + { 'prov': 'DASH', 'dep': '0%', 'wd': '0.01 DASH' }, + { 'prov': 'ETH', 'dep': '0%', 'wd': '0.01 ETH' }, + { 'prov': 'WAVES', 'dep': '0%', 'wd': '0.001 WAVES' }, + { 'prov': 'ZEC', 'dep': '0%', 'wd': '0.001 ZEC' }, + { 'prov': 'USDT', 'dep': '0%', 'wd': '5 USDT' }, + { 'prov': 'XMR', 'dep': '0%', 'wd': '0.05 XMR' }, + { 'prov': 'XRP', 'dep': '0%', 'wd': '0.02 XRP' }, + { 'prov': 'KICK', 'dep': '0%', 'wd': '50 KICK' }, + { 'prov': 'ETC', 'dep': '0%', 'wd': '0.01 ETC' }, + { 'prov': 'BCH', 'dep': '0%', 'wd': '0.001 BCH' }, + { 'prov': 'BTG', 'dep': '0%', 'wd': '0.001 BTG' }, + { 'prov': 'EOS', 'dep': '0%', 'wd': '0.05 EOS' }, + { 'prov': 'BTCZ', 'dep': '0%', 'wd': '5 BTCZ' }, + { 'prov': 'DXT', 'dep': '20 DXT', 'wd': '20 DXT' }, + { 'prov': 'XLM', 'dep': '0%', 'wd': '0.01 XLM' }, + { 'prov': 'MNX', 'dep': '0%', 'wd': '0.01 MNX' }, + { 'prov': 'OMG', 'dep': '0.1 OMG', 'wd': '0.5 OMG' }, + { 'prov': 'TRX', 'dep': '0%', 'wd': '1 TRX' }, + { 'prov': 'ADA', 'dep': '0%', 'wd': '1 ADA' }, + { 'prov': 'INK', 'dep': '10 INK', 'wd': '50 INK' }, + { 'prov': 'NEO', 'dep': '0%', 'wd': '0%' }, + { 'prov': 'GAS', 'dep': '0%', 'wd': '0%' }, + { 'prov': 'ZRX', 'dep': '0%', 'wd': '1 ZRX' }, + { 'prov': 'GNT', 'dep': '0%', 'wd': '1 GNT' }, + { 'prov': 'GUSD', 'dep': '0%', 'wd': '0.5 GUSD' }, + { 'prov': 'LSK', 'dep': '0%', 'wd': '0.1 LSK' }, + { 'prov': 'XEM', 'dep': '0%', 'wd': '5 XEM' }, + { 'prov': 'SMART', 'dep': '0%', 'wd': '0.5 SMART' }, + { 'prov': 'QTUM', 'dep': '0%', 'wd': '0.01 QTUM' }, + { 'prov': 'HB', 'dep': '0%', 'wd': '10 HB' }, + { 'prov': 'DAI', 'dep': '0%', 'wd': '1 DAI' }, + { 'prov': 'MKR', 'dep': '0%', 'wd': '0.005 MKR' }, + { 'prov': 'MNC', 'dep': '0%', 'wd': '15 MNC' }, + { 'prov': 'PTI', 'dep': '0%', 'wd': '10 PTI' }, + { 'prov': 'ATMCASH', 'dep': '0%', 'wd': '5 ATMCASH' }, + { 'prov': 'ETZ', 'dep': '0%', 'wd': '1 ETZ' }, + { 'prov': 'USDC', 'dep': '0%', 'wd': '0.5 USDC' }, + { 'prov': 'ROOBEE', 'dep': '0%', 'wd': '200 ROOBEE' }, + { 'prov': 'DCR', 'dep': '0%', 'wd': '0.01 DCR' }, + { 'prov': 'ZAG', 'dep': '0%', 'wd': '0%' }, + { 'prov': 'BTT', 'dep': '0%', 'wd': '100 BTT' }, + { 'prov': 'VLX', 'dep': '0%', 'wd': '1 VLX' }, + ], + }, + { + 'group': 'usd', + 'title': 'USD', + 'items': [ + { 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + { 'prov': 'Perfect Money', 'dep': '-', 'wd': '0.5%' }, + { 'prov': 'AdvCash', 'dep': '3.45%', 'wd': '2.95%' }, + { 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' }, + { 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' }, + { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' }, + ], + }, + { + 'group': 'eur', + 'title': 'EUR', + 'items': [ + { 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' }, + { 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' }, + { 'prov': 'AdvCash', 'dep': '0.45%', 'wd': '-' }, + { 'prov': 'Wire Transfer', 'dep': '6.95 EUR', 'wd': '-' }, + { 'prov': 'SEPA', 'dep': '6.95 EUR', 'wd': '-' }, + { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' }, + { 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'rub', + 'title': 'RUB', + 'items': [ + { 'prov': 'Yandex Money', 'dep': '2.95%', 'wd': '1.95%' }, + { 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' }, + { 'prov': 'AdvCash', 'dep': '3.95%', 'wd': '0.95%' }, + { 'prov': 'Qiwi', 'dep': '3.95%', 'wd': '2.95%' }, + { 'prov': 'Visa/MasterCard', 'dep': '3.95%', 'wd': '4.95% + 60 RUB' }, + { 'prov': 'WebMoney', 'dep': '-', 'wd': '3.5%' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'pln', + 'title': 'PLN', + 'items': [ + { 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' }, + { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'try', + 'title': 'TRY', + 'items': [ + { 'prov': 'QR ile yatırma', 'dep': '5.95%', 'wd': '-' }, + { 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'uah', + 'title': 'UAH', + 'items': [ + { 'prov': 'Terminal', 'dep': '2.6%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + { 'prov': 'AdvCash', 'dep': '-', 'wd': '2.95%' }, + { 'prov': 'Visa/MasterCard', 'dep': '2.6%', 'wd': '3.95% + 20 UAH' }, + { 'prov': 'Enfins', 'dep': '0%', 'wd': '1.95%' }, + ], + }, + ], + }, + }, + }, + 'exceptions': { + 'exact': { + '40005': AuthenticationError, // Authorization error, incorrect signature + '40009': InvalidNonce, // + '40015': ExchangeError, // API function do not exist + '40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"} + '40017': AuthenticationError, // Wrong API Key + '50052': InsufficientFunds, + '50054': InsufficientFunds, + '50304': OrderNotFound, // "Order was not found '123456789'" (fetching order trades for an order that does not have trades yet) + '50173': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) + '50319': InvalidOrder, // Price by order is less than permissible minimum for this pair + '50321': InvalidOrder, // Price by order is more than permissible maximum for this pair + }, + 'broad': { + 'API rate limit exceeded': RateLimitExceeded, // {"result":false,"error":"API rate limit exceeded for 99.33.55.224. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400} + }, + }, + }); + } + + async fetchTradingFees (params = {}) { + if (this.options['useWebapiForFetchingFees']) { + const response = await this.webGetEnDocsFees (params); + let parts = response.split (''); + let numParts = parts.length; + if (numParts !== 2) { + throw new NotSupported (this.id + ' fetchTradingFees format has changed'); + } + const rest = parts[1]; + parts = rest.split (''); + numParts = parts.length; + if (numParts < 2) { + throw new NotSupported (this.id + ' fetchTradingFees format has changed'); + } + const fee = parseFloat (parts[0].replace ('%', '')) * 0.01; + const taker = fee; + const maker = fee; + return { + // 'info': response, + 'maker': maker, + 'taker': taker, + }; + } else { + return { + 'maker': this.fees['trading']['maker'], + 'taker': this.fees['trading']['taker'], + }; + } + } + + parseFixedFloatValue (input) { + if ((input === undefined) || (input === '-')) { + return undefined; + } + const isPercentage = (input.indexOf ('%') >= 0); + const parts = input.split (' '); + const value = parts[0].replace ('%', ''); + const result = parseFloat (value); + if ((result > 0) && isPercentage) { + throw new ExchangeError (this.id + ' parseFixedFloatValue detected an unsupported non-zero percentage-based fee ' + input); + } + return result; + } + + async fetchFundingFees (params = {}) { + let response = undefined; + if (this.options['useWebapiForFetchingFees']) { + response = await this.webGetCtrlFeesAndLimits (params); + } else { + response = this.options['feesAndLimits']; + } + // the code below assumes all non-zero crypto fees are fixed (for now) + const withdraw = {}; + const deposit = {}; + const groups = this.safeValue (response['data'], 'fees'); + const groupsByGroup = this.indexBy (groups, 'group'); + const items = groupsByGroup['crypto']['items']; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const code = this.safeCurrencyCode (this.safeString (item, 'prov')); + const withdrawalFee = this.safeString (item, 'wd'); + const depositFee = this.safeString (item, 'dep'); + if (withdrawalFee !== undefined) { + if (withdrawalFee.length > 0) { + withdraw[code] = this.parseFixedFloatValue (withdrawalFee); + } + } + if (depositFee !== undefined) { + if (depositFee.length > 0) { + deposit[code] = this.parseFixedFloatValue (depositFee); + } + } + } + // sets fiat fees to undefined + const fiatGroups = this.toArray (this.omit (groupsByGroup, 'crypto')); + for (let i = 0; i < fiatGroups.length; i++) { + const code = this.safeCurrencyCode (this.safeString (fiatGroups[i], 'title')); + withdraw[code] = undefined; + deposit[code] = undefined; + } + const result = { + 'info': response, + 'withdraw': withdraw, + 'deposit': deposit, + }; + // cache them for later use + this.options['fundingFees'] = result; + return result; + } + + async fetchCurrencies (params = {}) { + const fees = await this.fetchFundingFees (params); + // todo redesign the 'fee' property in currencies + const ids = Object.keys (fees['withdraw']); + const limitsByMarketId = this.indexBy (fees['info']['data']['limits'], 'pair'); + const marketIds = Object.keys (limitsByMarketId); + const minAmounts = {}; + const minPrices = {}; + const minCosts = {}; + const maxAmounts = {}; + const maxPrices = {}; + const maxCosts = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const limit = limitsByMarketId[marketId]; + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const maxAmount = this.safeFloat (limit, 'max_q'); + const maxPrice = this.safeFloat (limit, 'max_p'); + const maxCost = this.safeFloat (limit, 'max_a'); + const minAmount = this.safeFloat (limit, 'min_q'); + const minPrice = this.safeFloat (limit, 'min_p'); + const minCost = this.safeFloat (limit, 'min_a'); + minAmounts[base] = Math.min (this.safeFloat (minAmounts, base, minAmount), minAmount); + maxAmounts[base] = Math.max (this.safeFloat (maxAmounts, base, maxAmount), maxAmount); + minPrices[quote] = Math.min (this.safeFloat (minPrices, quote, minPrice), minPrice); + minCosts[quote] = Math.min (this.safeFloat (minCosts, quote, minCost), minCost); + maxPrices[quote] = Math.max (this.safeFloat (maxPrices, quote, maxPrice), maxPrice); + maxCosts[quote] = Math.max (this.safeFloat (maxCosts, quote, maxCost), maxCost); + } + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + const fee = this.safeValue (fees['withdraw'], code); + const active = true; + result[code] = { + 'id': id, + 'code': code, + 'name': code, + 'active': active, + 'fee': fee, + 'precision': 8, + 'limits': { + 'amount': { + 'min': this.safeFloat (minAmounts, code), + 'max': this.safeFloat (maxAmounts, code), + }, + 'price': { + 'min': this.safeFloat (minPrices, code), + 'max': this.safeFloat (maxPrices, code), + }, + 'cost': { + 'min': this.safeFloat (minCosts, code), + 'max': this.safeFloat (maxCosts, code), + }, + }, + 'info': id, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const fees = await this.fetchTradingFees (); + const response = await this.publicGetPairSettings (params); + const keys = Object.keys (response); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = response[id]; + const symbol = id.replace ('_', '/'); + const [ baseId, quoteId ] = symbol.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'taker': fees['taker'], + 'maker': fees['maker'], + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'min_quantity'), + 'max': this.safeFloat (market, 'max_quantity'), + }, + 'price': { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }, + 'cost': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserInfo (params); + const result = { 'info': response }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currencyId = this.currencyId (code); + const account = this.account (); + if (currencyId in response['balances']) { + account['free'] = this.safeFloat (response['balances'], currencyId); + } + if (currencyId in response['reserved']) { + account['used'] = this.safeFloat (response['reserved'], currencyId); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + const result = this.safeValue (response, market['id']); + return this.parseOrderBook (result, undefined, 'bid', 'ask'); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join (','); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join (','); + } + const request = { + 'pair': ids, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + const result = {}; + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = marketId; + if (marketId in this.markets_by_id) { + const market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + result[symbol] = this.parseOrderBook (response[marketId], undefined, 'bid', 'ask'); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last_trade'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy_price'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell_price'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'vol_curr'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const market = this.market (symbol); + return this.parseTicker (response[market['id']], market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let fee = undefined; + let symbol = undefined; + const id = this.safeString (trade, 'trade_id'); + const orderId = this.safeString (trade, 'order_id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + const cost = this.safeFloat (trade, 'amount'); + const side = this.safeString (trade, 'type'); + const type = undefined; + if (market !== undefined) { + symbol = market['symbol']; + if (market['taker'] !== market['maker']) { + throw new ExchangeError (this.id + ' parseTrade can not deduce proper fee costs, taker and maker fees now differ'); + } + if ((side === 'buy') && (amount !== undefined)) { + fee = { + 'currency': market['base'], + 'cost': amount * market['taker'], + 'rate': market['taker'], + }; + } else if ((side === 'sell') && (cost !== undefined)) { + fee = { + 'currency': market['quote'], + 'cost': cost * market['taker'], + 'rate': market['taker'], + }; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + const data = this.safeValue (response, market['id'], []); + return this.parseTrades (data, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // a symbol is required but it can be a single string, or a non-empty array + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument (a single symbol or an array)'); + } + await this.loadMarkets (); + let pair = undefined; + let market = undefined; + if (Array.isArray (symbol)) { + const numSymbols = symbol.length; + if (numSymbols < 1) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a non-empty symbol array'); + } + const marketIds = this.marketIds (symbol); + pair = marketIds.join (','); + } else { + market = this.market (symbol); + pair = market['id']; + } + const request = { + 'pair': pair, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostUserTrades (this.extend (request, params)); + let result = []; + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const items = response[marketId]; + const trades = this.parseTrades (items, market, since, limit, { + 'symbol': symbol, + }); + result = this.arrayConcat (result, trades); + } + return this.filterBySinceLimit (result, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const prefix = (type === 'market') ? (type + '_') : ''; + const market = this.market (symbol); + if ((type === 'market') && (price === undefined)) { + price = 0; + } + const request = { + 'pair': market['id'], + 'quantity': this.amountToPrecision (symbol, amount), + 'type': prefix + side, + 'price': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostOrderCreate (this.extend (request, params)); + const id = this.safeString (response, 'order_id'); + const timestamp = this.milliseconds (); + amount = parseFloat (amount); + price = parseFloat (price); + const status = 'open'; + const order = { + 'id': id, + 'info': response, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * amount, + 'amount': amount, + 'remaining': amount, + 'filled': 0.0, + 'fee': undefined, + 'trades': undefined, + }; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { 'order_id': id }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + try { + const request = { + 'order_id': id.toString (), + }; + const response = await this.privatePostOrderTrades (this.extend (request, params)); + // + // { + // "type": "buy", + // "in_currency": "BTC", + // "in_amount": "1", + // "out_currency": "USD", + // "out_amount": "100", + // "trades": [ + // { + // "trade_id": 3, + // "date": 1435488248, + // "type": "buy", + // "pair": "BTC_USD", + // "order_id": 12345, + // "quantity": 1, + // "price": 100, + // "amount": 100 + // } + // ] + // } + // + const order = this.parseOrder (response); + return this.extend (order, { + 'id': id.toString (), + }); + } catch (e) { + if (e instanceof OrderNotFound) { + if (id in this.orders) { + return this.orders[id]; + } + } + } + throw new OrderNotFound (this.id + ' fetchOrder order id ' + id.toString () + ' not found in cache.'); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'order_id': id.toString (), + }; + const response = await this.privatePostOrderTrades (this.extend (request, params)); + const trades = this.safeValue (response, 'trades'); + return this.parseTrades (trades, market, since, limit); + } + + updateCachedOrders (openOrders, symbol) { + // update local cache with open orders + for (let j = 0; j < openOrders.length; j++) { + const id = openOrders[j]['id']; + this.orders[id] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + for (let k = 0; k < cachedOrderIds.length; k++) { + // match each cached order to an order in the open orders array + // possible reasons why a cached order may be missing in the open orders array: + // - order was closed or canceled -> update cache + // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip + const id = cachedOrderIds[k]; + let order = this.orders[id]; + if (!(id in openOrdersIndexedById)) { + // cached order is not in open orders array + // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order + if (symbol !== undefined && symbol !== order['symbol']) { + continue; + } + // order is cached but not present in the list of open orders -> mark the cached order as closed + if (order['status'] === 'open') { + order = this.extend (order, { + 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") + 'cost': undefined, + 'filled': order['amount'], + 'remaining': 0.0, + }); + if (order['cost'] === undefined) { + if (order['filled'] !== undefined) { + order['cost'] = order['filled'] * order['price']; + } + } + this.orders[id] = order; + } + } + } + return this.toArray (this.orders); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserOpenOrders (params); + const marketIds = Object.keys (response); + let orders = []; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let market = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + const parsedOrders = this.parseOrders (response[marketId], market); + orders = this.arrayConcat (orders, parsedOrders); + } + this.updateCachedOrders (orders, symbol); + return this.filterBySymbolSinceLimit (this.toArray (this.orders), symbol, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.fetchOrders (symbol, since, limit, params); + const orders = this.filterBy (this.orders, 'status', 'open'); + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.fetchOrders (symbol, since, limit, params); + const orders = this.filterBy (this.orders, 'status', 'closed'); + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + + parseOrder (order, market = undefined) { + // + // fetchOrders, fetchOpenOrders, fetchClosedOrders + // + // { + // "order_id": "14", + // "created": "1435517311", + // "type": "buy", + // "pair": "BTC_USD", + // "price": "100", + // "quantity": "1", + // "amount": "100" + // } + // + // fetchOrder + // + // { + // "type": "buy", + // "in_currency": "BTC", + // "in_amount": "1", + // "out_currency": "USD", + // "out_amount": "100", + // "trades": [ + // { + // "trade_id": 3, + // "date": 1435488248, + // "type": "buy", + // "pair": "BTC_USD", + // "order_id": 12345, + // "quantity": 1, + // "price": 100, + // "amount": 100 + // } + // ] + // } + // + let id = this.safeString (order, 'order_id'); + let timestamp = this.safeTimestamp (order, 'created'); + let symbol = undefined; + const side = this.safeString (order, 'type'); + if (market === undefined) { + let marketId = undefined; + if ('pair' in order) { + marketId = order['pair']; + } else if (('in_currency' in order) && ('out_currency' in order)) { + if (side === 'buy') { + marketId = order['in_currency'] + '_' + order['out_currency']; + } else { + marketId = order['out_currency'] + '_' + order['in_currency']; + } + } + if ((marketId !== undefined) && (marketId in this.markets_by_id)) { + market = this.markets_by_id[marketId]; + } + } + let amount = this.safeFloat (order, 'quantity'); + if (amount === undefined) { + const amountField = (side === 'buy') ? 'in_amount' : 'out_amount'; + amount = this.safeFloat (order, amountField); + } + let price = this.safeFloat (order, 'price'); + let cost = this.safeFloat (order, 'amount'); + let filled = 0.0; + const trades = []; + const transactions = this.safeValue (order, 'trades', []); + let feeCost = undefined; + let lastTradeTimestamp = undefined; + let average = undefined; + const numTransactions = transactions.length; + if (numTransactions > 0) { + feeCost = 0; + for (let i = 0; i < numTransactions; i++) { + const trade = this.parseTrade (transactions[i], market); + if (id === undefined) { + id = trade['order']; + } + if (timestamp === undefined) { + timestamp = trade['timestamp']; + } + if (timestamp > trade['timestamp']) { + timestamp = trade['timestamp']; + } + filled = this.sum (filled, trade['amount']); + feeCost = this.sum (feeCost, trade['fee']['cost']); + trades.push (trade); + } + lastTradeTimestamp = trades[numTransactions - 1]['timestamp']; + } + let remaining = undefined; + if (amount !== undefined) { + remaining = amount - filled; + } + let status = this.safeString (order, 'status'); // in case we need to redefine it for canceled orders + if (filled >= amount) { + status = 'closed'; + } else { + status = 'open'; + } + if (market === undefined) { + market = this.getMarketFromTrades (trades); + } + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } else { + if (filled > 0) { + if (average === undefined) { + average = cost / filled; + } + if (price === undefined) { + price = cost / filled; + } + } + } + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'average': average, + 'trades': trades, + 'fee': fee, + 'info': order, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostDepositAddress (params); + const depositAddress = this.safeString (response, code); + let address = undefined; + let tag = undefined; + if (depositAddress) { + const addressAndTag = depositAddress.split (','); + address = addressAndTag[0]; + const numParts = addressAndTag.length; + if (numParts > 1) { + tag = addressAndTag[1]; + } + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + getMarketFromTrades (trades) { + const tradesBySymbol = this.indexBy (trades, 'pair'); + const symbols = Object.keys (tradesBySymbol); + const numSymbols = symbols.length; + if (numSymbols === 1) { + return this.markets[symbols[0]]; + } + return undefined; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + let key = 'quote'; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, cost)), + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'amount': amount, + 'currency': currency['id'], + 'address': address, + }; + if (tag !== undefined) { + request['invoice'] = tag; + } + const response = await this.privatePostWithdrawCrypt (this.extend (request, params)); + return { + 'info': response, + 'id': response['task_id'], + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'transferred': 'ok', + 'paid': 'ok', + 'pending': 'pending', + 'processing': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchTransactions + // + // { + // "dt": 1461841192, + // "type": "deposit", + // "curr": "RUB", + // "status": "processing", + // "provider": "Qiwi (LA) [12345]", + // "amount": "1", + // "account": "", + // "txid": "ec46f784ad976fd7f7539089d1a129fe46...", + // } + // + const timestamp = this.safeTimestamp (transaction, 'dt'); + let amount = this.safeFloat (transaction, 'amount'); + if (amount !== undefined) { + amount = Math.abs (amount); + } + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const txid = this.safeString (transaction, 'txid'); + const type = this.safeString (transaction, 'type'); + const currencyId = this.safeString (transaction, 'curr'); + const code = this.safeCurrencyCode (currencyId, currency); + let address = this.safeString (transaction, 'account'); + if (address !== undefined) { + const parts = address.split (':'); + const numParts = parts.length; + if (numParts === 2) { + address = parts[1].replace (' ', ''); + } + } + let fee = undefined; + // fixed funding fees only (for now) + if (!this.fees['funding']['percentage']) { + const key = (type === 'withdrawal') ? 'withdraw' : 'deposit'; + let feeCost = this.safeFloat (this.options['fundingFees'][key], code); + // users don't pay for cashbacks, no fees for that + const provider = this.safeString (transaction, 'provider'); + if (provider === 'cashback') { + feeCost = 0; + } + if (feeCost !== undefined) { + // withdrawal amount includes the fee + if (type === 'withdrawal') { + amount = amount - feeCost; + } + fee = { + 'cost': feeCost, + 'currency': code, + 'rate': undefined, + }; + } + } + return { + 'info': transaction, + 'id': undefined, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, // refix it properly + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['date'] = parseInt (since / 1000); + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const response = await this.privatePostWalletHistory (this.extend (request, params)); + // + // { + // "result": true, + // "error": "", + // "begin": "1493942400", + // "end": "1494028800", + // "history": [ + // { + // "dt": 1461841192, + // "type": "deposit", + // "curr": "RUB", + // "status": "processing", + // "provider": "Qiwi (LA) [12345]", + // "amount": "1", + // "account": "", + // "txid": "ec46f784ad976fd7f7539089d1a129fe46...", + // }, + // { + // "dt": 1463414785, + // "type": "withdrawal", + // "curr": "USD", + // "status": "paid", + // "provider": "EXCODE", + // "amount": "-1", + // "account": "EX-CODE_19371_USDda...", + // "txid": "", + // }, + // ], + // } + // + return this.parseTransactions (response['history'], currency, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/'; + if (api !== 'web') { + url += this.version + '/'; + } + url += path; + if ((api === 'public') || (api === 'web')) { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + nonce () { + return this.milliseconds (); + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('result' in response) { + // + // {"result":false,"error":"Error 50052: Insufficient funds"} + // + let success = this.safeValue (response, 'result', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + let code = undefined; + const message = this.safeString (response, 'error'); + const errorParts = message.split (':'); + const numParts = errorParts.length; + if (numParts > 1) { + const errorSubParts = errorParts[0].split (' '); + const numSubParts = errorSubParts.length; + code = (numSubParts > 1) ? errorSubParts[1] : errorSubParts[0]; + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],82:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, ExchangeNotAvailable } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class exx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'exx', + 'name': 'EXX', + 'countries': [ 'CN' ], + 'rateLimit': 1000 / 10, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchOpenOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/37770292-fbf613d0-2de4-11e8-9f79-f2dc451b8ccb.jpg', + 'api': { + 'public': 'https://api.exx.com/data/v1', + 'private': 'https://trade.exx.com/api', + }, + 'www': 'https://www.exx.com/', + 'doc': 'https://www.exx.com/help/restApi', + 'fees': 'https://www.exx.com/help/rate', + 'referral': 'https://www.exx.com/r/fde4260159e53ab8a58cc9186d35501f?recommQd=1', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'tickers', + 'ticker', + 'depth', + 'trades', + ], + }, + 'private': { + 'get': [ + 'order', + 'cancel', + 'getOrder', + 'getOpenOrders', + 'getBalance', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BCC': 0.0003, + 'BCD': 0.0, + 'BOT': 10.0, + 'BTC': 0.001, + 'BTG': 0.0, + 'BTM': 25.0, + 'BTS': 3.0, + 'EOS': 1.0, + 'ETC': 0.01, + 'ETH': 0.01, + 'ETP': 0.012, + 'HPY': 0.0, + 'HSR': 0.001, + 'INK': 20.0, + 'LTC': 0.005, + 'MCO': 0.6, + 'MONA': 0.01, + 'QASH': 5.0, + 'QCASH': 5.0, + 'QTUM': 0.01, + 'USDT': 5.0, + }, + }, + }, + 'commonCurrencies': { + 'TV': 'TIV', // Ti-Value + }, + 'exceptions': { + '103': AuthenticationError, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + const ids = Object.keys (response); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = response[id]; + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = market['isOpen'] === true; + const precision = { + 'amount': parseInt (market['amountScale']), + 'price': parseInt (market['priceScale']), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const symbol = market['symbol']; + const timestamp = this.safeInteger (ticker, 'date'); + ticker = ticker['ticker']; + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'riseRate'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const result = {}; + const timestamp = this.milliseconds (); + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + if (!(id in this.marketsById)) { + continue; + } + const market = this.marketsById[id]; + const symbol = market['symbol']; + const ticker = { + 'date': timestamp, + 'ticker': response[id], + }; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currency': this.marketId (symbol), + }; + const response = await this.publicGetDepth (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetGetBalance (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'funds'); + const currencies = Object.keys (balances); + for (let i = 0; i < currencies.length; i++) { + const currencyId = currencies[i]; + const balance = balances[currencyId]; + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'balance'), + 'used': this.safeFloat (balance, 'freeze'), + 'total': this.safeFloat (balance, 'total'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + const symbol = market['symbol']; + const timestamp = parseInt (order['trade_date']); + const price = this.safeFloat (order, 'price'); + const cost = this.safeFloat (order, 'trade_money'); + const amount = this.safeFloat (order, 'total_amount'); + const filled = this.safeFloat (order, 'trade_amount', 0.0); + const remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); + let status = this.safeInteger (order, 'status'); + if (status === 1) { + status = 'canceled'; + } else if (status === 2) { + status = 'closed'; + } else { + status = 'open'; + } + let fee = undefined; + if ('fees' in order) { + fee = { + 'cost': this.safeFloat (order, 'fees'), + 'currency': market['quote'], + }; + } + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': order['type'], + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'type': side, + 'price': price, + 'amount': amount, + }; + const response = await this.privateGetOrder (this.extend (request, params)); + const id = this.safeString (response, 'id'); + const order = this.parseOrder ({ + 'id': id, + 'trade_date': this.milliseconds (), + 'total_amount': amount, + 'price': price, + 'type': side, + 'info': response, + }, market); + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'currency': market['id'], + }; + const response = await this.privateGetCancel (this.extend (request, params)); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'currency': market['id'], + }; + const response = await this.privateGetGetOrder (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + const response = await this.privateGetGetOpenOrders (this.extend (request, params)); + if (!Array.isArray (response)) { + return []; + } + return this.parseOrders (response, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const query = this.urlencode (this.keysort (this.extend ({ + 'accesskey': this.apiKey, + 'nonce': this.nonce (), + }, params))); + const signed = this.hmac (this.encode (query), this.encode (this.secret), 'sha512'); + url += '?' + query + '&signature=' + signed; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"result":false,"message":"�务端忙碌"} + // ... and other formats + // + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + if (code === '100') { + return; + } + if (code !== undefined) { + this.throwExactlyMatchedException (this.exceptions, code, feedback); + if (code === '308') { + // this is returned by the exchange when there are no open orders + // {"code":308,"message":"Not Found Transaction Record"} + return; + } else { + throw new ExchangeError (feedback); + } + } + const result = this.safeValue (response, 'result'); + if (result !== undefined) { + if (!result) { + if (message === '�务端忙碌') { + throw new ExchangeNotAvailable (feedback); + } else { + throw new ExchangeError (feedback); + } + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],83:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, ExchangeNotAvailable, ArgumentsRequired, InsufficientFunds, InvalidOrder, RateLimitExceeded, InvalidNonce, AuthenticationError, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class fcoin extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'fcoin', + 'name': 'FCoin', + 'countries': [ 'CN' ], + 'rateLimit': 2000, + 'userAgent': this.userAgents['chrome39'], + 'version': 'v2', + 'accounts': undefined, + 'accountsById': undefined, + 'hostname': 'fcoin.com', + 'has': { + 'CORS': false, + 'fetchDepositAddress': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOrderBook': true, + 'fetchOrderBooks': false, + 'fetchTradingLimits': false, + 'withdraw': false, + 'fetchCurrencies': false, + }, + 'timeframes': { + '1m': 'M1', + '3m': 'M3', + '5m': 'M5', + '15m': 'M15', + '30m': 'M30', + '1h': 'H1', + '1d': 'D1', + '1w': 'W1', + '1M': 'MN', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/42244210-c8c42e1e-7f1c-11e8-8710-a5fb63b165c4.jpg', + 'api': { + 'public': 'https://api.{hostname}', + 'private': 'https://api.{hostname}', + 'market': 'https://api.{hostname}', + 'openapi': 'https://www.{hostname}', + }, + 'www': 'https://www.fcoin.com', + 'referral': 'https://www.fcoin.com/i/Z5P7V', + 'doc': 'https://developer.fcoin.com', + 'fees': 'https://fcoinjp.zendesk.com/hc/en-us/articles/360018727371', + }, + 'api': { + 'openapi': { + 'get': [ + 'symbols', + ], + }, + 'market': { + 'get': [ + 'ticker/{symbol}', + 'depth/{level}/{symbol}', + 'trades/{symbol}', + 'candles/{timeframe}/{symbol}', + ], + }, + 'public': { + 'get': [ + 'symbols', + 'currencies', + 'server-time', + ], + }, + 'private': { + 'get': [ + 'accounts/balance', + 'assets/accounts/balance', + 'broker/otc/suborders', + 'broker/otc/suborders/{id}', + 'broker/otc/suborders/{id}/payments', + 'broker/otc/users', + 'broker/otc/users/me/balances', + 'broker/otc/users/me/balance', + 'broker/leveraged_accounts/account', + 'broker/leveraged_accounts', + 'orders', + 'orders/{order_id}', + 'orders/{order_id}/match-results', // check order result + ], + 'post': [ + 'assets/accounts/assets-to-spot', + 'accounts/spot-to-assets', + 'broker/otc/assets/transfer/in', + 'broker/otc/assets/transfer/out', + 'broker/otc/suborders', + 'broker/otc/suborders/{id}/pay_confirm', + 'broker/otc/suborders/{id}/cancel', + 'broker/leveraged/assets/transfer/in', + 'broker/leveraged/assets/transfer/out', + 'orders', + 'orders/{order_id}/submit-cancel', // cancel order + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': -0.0002, + 'taker': 0.0003, + }, + }, + 'limits': { + 'amount': { 'min': 0.01, 'max': 100000 }, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + 'fetchMarketsMethod': 'fetch_markets_from_open_api', // or 'fetch_markets_from_api' + 'limits': { + 'BTM/USDT': { 'amount': { 'min': 0.1, 'max': 10000000 }}, + 'ETC/USDT': { 'amount': { 'min': 0.001, 'max': 400000 }}, + 'ETH/USDT': { 'amount': { 'min': 0.001, 'max': 10000 }}, + 'LTC/USDT': { 'amount': { 'min': 0.001, 'max': 40000 }}, + 'BCH/USDT': { 'amount': { 'min': 0.001, 'max': 5000 }}, + 'BTC/USDT': { 'amount': { 'min': 0.001, 'max': 1000 }}, + 'ICX/ETH': { 'amount': { 'min': 0.01, 'max': 3000000 }}, + 'OMG/ETH': { 'amount': { 'min': 0.01, 'max': 500000 }}, + 'FT/USDT': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'ZIL/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'ZIP/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'FT/BTC': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'FT/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, + }, + }, + 'exceptions': { + '400': NotSupported, // Bad Request + '401': AuthenticationError, + '405': NotSupported, + '429': RateLimitExceeded, // Too Many Requests, exceed api request limit + '1002': ExchangeNotAvailable, // System busy + '1016': InsufficientFunds, + '2136': AuthenticationError, // The API key is expired + '3008': InvalidOrder, + '6004': InvalidNonce, + '6005': AuthenticationError, // Illegal API Signature + '40003': BadSymbol, + }, + 'commonCurrencies': { + 'DAG': 'DAGX', + 'PAI': 'PCHAIN', + 'MT': 'Mariana Token', + }, + }); + } + + async fetchMarkets (params = {}) { + const method = this.safeString (this.options, 'fetchMarketsMethod', 'fetch_markets_from_open_api'); + return await this[method] (params); + } + + async fetchMarketsFromOpenAPI (params = {}) { + // https://github.com/ccxt/ccxt/issues/5648 + const response = await this.openapiGetSymbols (params); + // + // { + // "status":"ok", + // "data":{ + // "categories":[ "fone::coinforce", ... ], + // "symbols":{ + // "mdaeth":{ + // "price_decimal":8, + // "amount_decimal":2, + // "base_currency":"mda", + // "quote_currency":"eth", + // "symbol":"mdaeth", + // "category":"fone::bitangel", + // "leveraged_multiple":null, + // "tradeable":false, + // "market_order_enabled":false, + // "limit_amount_min":"1", + // "limit_amount_max":"10000000", + // "main_tag":"", + // "daily_open_at":"", + // "daily_close_at":"" + // }, + // } + // "category_ref":{ + // "fone::coinforce":[ "btcusdt", ... ], + // } + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const markets = this.safeValue (data, 'symbols', {}); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = markets[key]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': this.safeInteger (market, 'price_decimal'), + 'amount': this.safeInteger (market, 'amount_decimal'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'limit_amount_min'), + 'max': this.safeFloat (market, 'limit_amount_max'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + const active = this.safeValue (market, 'tradeable', false); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchMarketsFromAPI (params = {}) { + const response = await this.publicGetSymbols (params); + // + // { + // "status":0, + // "data":[ + // { + // "name":"dapusdt", + // "base_currency":"dap", + // "quote_currency":"usdt", + // "price_decimal":6, + // "amount_decimal":2, + // "tradable":true + // }, + // ] + // } + // + const result = []; + const markets = this.safeValue (response, 'data'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': market['price_decimal'], + 'amount': market['amount_decimal'], + }; + let limits = { + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + }; + const active = this.safeValue (market, 'tradable', false); + if (symbol in this.options['limits']) { + limits = this.extend (this.options['limits'][symbol], limits); + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountsBalance (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'frozen'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseBidsAsks (orders, priceKey = 0, amountKey = 1) { + const result = []; + const length = orders.length; + const halfLength = parseInt (length / 2); + // += 2 in the for loop below won't transpile + for (let i = 0; i < halfLength; i++) { + const index = i * 2; + const priceField = this.sum (index, priceKey); + const amountField = this.sum (index, amountKey); + result.push ([ + this.safeFloat (orders, priceField), + this.safeFloat (orders, amountField), + ]); + } + return result; + } + + async fetchOrderBook (symbol = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit !== undefined) { + if ((limit === 20) || (limit === 150)) { + limit = 'L' + limit.toString (); + } else { + throw new ExchangeError (this.id + ' fetchOrderBook supports limit of 20 or 150. Other values are not accepted'); + } + } else { + limit = 'L20'; + } + const request = { + 'symbol': this.marketId (symbol), + 'level': limit, // L20, L150 + }; + const response = await this.marketGetDepthLevelSymbol (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data'); + return this.parseOrderBook (orderbook, orderbook['ts'], 'bids', 'asks', 0, 1); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.marketGetTickerSymbol (this.extend (request, params)); + return this.parseTicker (ticker['data'], market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = undefined; + let symbol = undefined; + if (market === undefined) { + const tickerType = this.safeString (ticker, 'type'); + if (tickerType !== undefined) { + const parts = tickerType.split ('.'); + const id = parts[1]; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + } + } + } + const values = ticker['ticker']; + const last = this.safeFloat (values, 0); + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (values, 7), + 'low': this.safeFloat (values, 8), + 'bid': this.safeFloat (values, 2), + 'bidVolume': this.safeFloat (values, 3), + 'ask': this.safeFloat (values, 4), + 'askVolume': this.safeFloat (values, 5), + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (values, 9), + 'quoteVolume': this.safeFloat (values, 10), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'ts'); + const side = this.safeStringLower (trade, 'side'); + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const fee = undefined; + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'limit': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.marketGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'side': side, + 'type': type, + }; + // for market buy it requires the amount of quote currency to spend + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + request['amount'] = this.costToPrecision (symbol, amount * price); + } + } else { + request['amount'] = this.costToPrecision (symbol, amount); + } + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + if ((type === 'limit') || (type === 'ioc') || (type === 'fok')) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + return { + 'info': response, + 'id': response['data'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privatePostOrdersOrderIdSubmitCancel (this.extend (request, params)); + const order = this.parseOrder (response); + return this.extend (order, { + 'id': id, + 'status': 'canceled', + }); + } + + parseOrderStatus (status) { + const statuses = { + 'submitted': 'open', + 'canceled': 'canceled', + 'partial_filled': 'open', + 'partial_canceled': 'canceled', + 'filled': 'closed', + 'pending_cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const side = this.safeString (order, 'side'); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + const orderType = this.safeString (order, 'type'); + const timestamp = this.safeInteger (order, 'created_at'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'filled_amount'); + let remaining = undefined; + let price = this.safeFloat (order, 'price'); + let cost = this.safeFloat (order, 'executed_value'); + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } else if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + let feeCurrency = undefined; + let feeCost = undefined; + const feeRebate = this.safeFloat (order, 'fees_income'); + if ((feeRebate !== undefined) && (feeRebate > 0)) { + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = (side === 'buy') ? market['quote'] : market['base']; + } + feeCost = -feeRebate; + } else { + feeCost = this.safeFloat (order, 'fill_fees'); + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = (side === 'buy') ? market['base'] : market['quote']; + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'average': undefined, + 'status': status, + 'fee': { + 'cost': feeCost, + 'currency': feeCurrency, + }, + 'trades': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateGetOrdersOrderId (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'states': 'submitted,partial_filled' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'states': 'partial_canceled,filled' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'states': 'submitted,partial_filled,partial_canceled,filled,canceled', + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'id'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'base_vol'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 20; // default is 20 + } + const request = { + 'symbol': market['id'], + 'timeframe': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + const sinceInSeconds = parseInt (since / 1000); + const timerange = limit * this.parseTimeframe (timeframe); + request['before'] = this.sum (sinceInSeconds, timerange) - 1; + } + const response = await this.marketGetCandlesTimeframeSymbol (this.extend (request, params)); + return this.parseOHLCVs (response['data'], market, timeframe, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/'; + const openAPI = (api === 'openapi'); + const privateAPI = (api === 'private'); + request += openAPI ? (api + '/') : ''; + request += this.version + '/'; + request += (privateAPI || openAPI) ? '' : (api + '/'); + request += this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + let url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }); + url += request; + if (privateAPI) { + this.checkRequiredCredentials (); + const timestamp = this.nonce ().toString (); + query = this.keysort (query); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.rawencode (query); + } + } + // HTTP_METHOD + HTTP_REQUEST_URI + TIMESTAMP + POST_BODY + let auth = method + url + timestamp; + if (method === 'POST') { + if (Object.keys (query).length) { + body = this.json (query); + auth += this.urlencode (query); + } + } + const payload = this.stringToBase64 (this.encode (auth)); + let signature = this.hmac (payload, this.encode (this.secret), 'sha1', 'binary'); + signature = this.decode (this.stringToBase64 (signature)); + headers = { + 'FC-ACCESS-KEY': this.apiKey, + 'FC-ACCESS-SIGNATURE': signature, + 'FC-ACCESS-TIMESTAMP': timestamp, + 'Content-Type': 'application/json', + }; + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const status = this.safeString (response, 'status'); + if (status !== '0' && status !== 'ok') { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, status, feedback); + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],84:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const fcoin = require ('./fcoin.js'); + +// --------------------------------------------------------------------------- + +module.exports = class fcoinjp extends fcoin { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'fcoinjp', + 'name': 'FCoinJP', + 'countries': [ 'JP' ], + 'hostname': 'fcoinjp.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/54219174-08b66b00-4500-11e9-862d-f522d0fe08c6.jpg', + 'fees': 'https://fcoinjp.zendesk.com/hc/en-us/articles/360018727371', + 'www': 'https://www.fcoinjp.com', + 'referral': undefined, + }, + }); + } +}; + +},{"./fcoin.js":83}],85:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class flowbtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'flowbtc', + 'name': 'flowBTC', + 'countries': [ 'BR' ], // Brazil + 'version': 'v1', + 'rateLimit': 1000, + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28162465-cd815d4c-67cf-11e7-8e57-438bea0523a2.jpg', + 'api': 'https://publicapi.flowbtc.com.br', + 'www': 'https://www.flowbtc.com.br', + 'doc': 'https://www.flowbtc.com.br/api.html', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'post': [ + 'GetTicker', + 'GetTrades', + 'GetTradesByDate', + 'GetOrderBook', + 'GetProductPairs', + 'GetProducts', + ], + }, + 'private': { + 'post': [ + 'CreateAccount', + 'GetUserInfo', + 'SetUserInfo', + 'GetAccountInfo', + 'GetAccountTrades', + 'GetDepositAddresses', + 'Withdraw', + 'CreateOrder', + 'ModifyOrder', + 'CancelOrder', + 'CancelAllOrders', + 'GetAccountOpenOrders', + 'GetOrderFee', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0025, + 'taker': 0.005, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicPostGetProductPairs (params); + const markets = this.safeValue (response, 'productPairs'); + const result = {}; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'product1Label'); + const quoteId = this.safeString (market, 'product2Label'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const precision = { + 'amount': this.safeInteger (market, 'product1DecimalPlaces'), + 'price': this.safeInteger (market, 'product2DecimalPlaces'), + }; + const symbol = base + '/' + quote; + result[symbol] = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const balances = this.safeValue (response, 'currencies'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = balance['name']; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['total'] = this.safeFloat (balance, 'hold'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'productPair': market['id'], + }; + const response = await this.publicPostGetOrderBook (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'px', 'qty'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'productPair': market['id'], + }; + const ticker = await this.publicPostGetTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume24hr'), + 'quoteVolume': this.safeFloat (ticker, 'volume24hrProduct2'), + 'info': ticker, + }; + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'unixtime'); + const side = (trade['incomingOrderSide'] === 0) ? 'buy' : 'sell'; + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'px'); + const amount = this.safeFloat (trade, 'qty'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'id': id, + 'order': undefined, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'ins': market['id'], + 'startIndex': -1, + }; + const response = await this.publicPostGetTrades (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const orderType = (type === 'market') ? 1 : 0; + const request = { + 'ins': this.marketId (symbol), + 'side': side, + 'orderType': orderType, + 'qty': amount, + 'px': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostCreateOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response['serverOrderId'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + if ('ins' in params) { + const request = { + 'serverOrderId': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + throw new ExchangeError (this.id + ' requires `ins` symbol parameter for cancelling an order'); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + body = this.json (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const auth = nonce.toString () + this.uid + this.apiKey; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.json (this.extend ({ + 'apiKey': this.apiKey, + 'apiNonce': nonce, + 'apiSig': signature.toUpperCase (), + }, params)); + headers = { + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('isAccepted' in response) { + if (response['isAccepted']) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],86:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class foxbit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'foxbit', + 'name': 'FoxBit', + 'countries': [ 'BR' ], + 'has': { + 'CORS': false, + 'createMarketOrder': false, + }, + 'rateLimit': 1000, + 'version': 'v1', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991413-11b40d42-647f-11e7-91ee-78ced874dd09.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://foxbit.com.br/exchange', + 'doc': 'https://foxbit.com.br/api/', + }, + 'comment': 'Blinktrade API', + 'api': { + 'public': { + 'get': [ + '{currency}/ticker', // ?crypto_currency=BTC + '{currency}/orderbook', // ?crypto_currency=BTC + '{currency}/trades', // ?crypto_currency=BTC&since=&limit= + ], + }, + 'private': { + 'post': [ + 'D', // order + 'F', // cancel order + 'U2', // balance + 'U4', // my orders + 'U6', // withdraw + 'U18', // deposit + 'U24', // confirm withdrawal + 'U26', // list withdrawals + 'U30', // list deposits + 'U34', // ledger + 'U70', // cancel withdrawal + ], + }, + }, + 'markets': { + 'BTC/VEF': { 'id': 'BTCVEF', 'symbol': 'BTC/VEF', 'base': 'BTC', 'quote': 'VEF', 'brokerId': 1, 'broker': 'SurBitcoin' }, + 'BTC/VND': { 'id': 'BTCVND', 'symbol': 'BTC/VND', 'base': 'BTC', 'quote': 'VND', 'brokerId': 3, 'broker': 'VBTC' }, + 'BTC/BRL': { 'id': 'BTCBRL', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'brokerId': 4, 'broker': 'FoxBit' }, + 'BTC/PKR': { 'id': 'BTCPKR', 'symbol': 'BTC/PKR', 'base': 'BTC', 'quote': 'PKR', 'brokerId': 8, 'broker': 'UrduBit' }, + 'BTC/CLP': { 'id': 'BTCCLP', 'symbol': 'BTC/CLP', 'base': 'BTC', 'quote': 'CLP', 'brokerId': 9, 'broker': 'ChileBit' }, + }, + 'options': { + 'brokerId': '4', // https://blinktrade.com/docs/#brokers + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'BalanceReqID': this.nonce (), + }; + const response = await this.privatePostU2 (this.extend (request, params)); + const balances = this.safeValue (response['Responses'], this.options['brokerId']); + const result = { 'info': response }; + if (balances !== undefined) { + const currencyIds = Object.keys (this.currencies_by_id); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + // we only set the balance for the currency if that currency is present in response + // otherwise we will lose the info if the currency balance has been funded or traded or not + if (currencyId in balances) { + const account = this.account (); + let used = this.safeFloat (balances, currencyId + '_locked'); + if (used !== undefined) { + used *= 1e-8; + } + let total = this.safeFloat (balances, currencyId); + if (total !== undefined) { + total *= 1e-8; + } + account['used'] = used; + account['total'] = total; + result[code] = account; + } + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['quote'], + 'crypto_currency': market['base'], + }; + const response = await this.publicGetCurrencyOrderbook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['quote'], + 'crypto_currency': market['base'], + }; + const ticker = await this.publicGetCurrencyTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const lowercaseQuote = market['quote'].toLowerCase (); + const quoteVolume = 'vol_' + lowercaseQuote; + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, quoteVolume), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (trade, 'side'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['quote'], + 'crypto_currency': market['base'], + }; + const response = await this.publicGetCurrencyTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const market = this.market (symbol); + const orderSide = (side === 'buy') ? '1' : '2'; + const request = { + 'ClOrdID': this.nonce (), + 'Symbol': market['id'], + 'Side': orderSide, + 'OrdType': '2', + 'Price': price, + 'OrderQty': amount, + 'BrokerID': market['brokerId'], + }; + const response = await this.privatePostD (this.extend (request, params)); + const indexed = this.indexBy (response['Responses'], 'MsgType'); + const execution = indexed['8']; + return { + 'info': response, + 'id': execution['OrderID'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.privatePostF (this.extend ({ + 'ClOrdID': id, + }, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const request = this.extend ({ 'MsgType': path }, query); + body = this.json (request); + headers = { + 'APIKey': this.apiKey, + 'Nonce': nonce, + 'Signature': this.hmac (this.encode (nonce), this.encode (this.secret)), + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('Status' in response) { + if (response['Status'] !== 200) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],87:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { TICK_SIZE } = require ('./base/functions/number'); +const { ExchangeError, InvalidOrder, BadRequest, InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class ftx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'ftx', + 'name': 'FTX', + 'countries': [ 'HK' ], + 'rateLimit': 100, + 'certified': true, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg', + 'www': 'https://ftx.com', + 'api': 'https://ftx.com', + 'doc': 'https://github.com/ftexchange/ftx', + 'fees': 'https://ftexchange.zendesk.com/hc/en-us/articles/360024479432-Fees', + 'referral': 'https://ftx.com/#a=1623029', + }, + 'has': { + 'cancelAllOrders': true, + 'fetchClosedOrders': false, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBook': true, + 'fetchOrders': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTrades': true, + 'fetchTradingFees': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'timeframes': { + '15s': '15', + '1m': '60', + '5m': '300', + '15m': '900', + '1h': '3600', + '4h': '14400', + '1d': '86400', + }, + 'api': { + 'public': { + 'get': [ + 'coins', + 'markets', + 'markets/{market_name}', + 'markets/{market_name}/orderbook', // ?depth={depth} + 'markets/{market_name}/trades', // ?limit={limit}&start_time={start_time}&end_time={end_time} + 'markets/{market_name}/candles', // ?resolution={resolution}&limit={limit}&start_time={start_time}&end_time={end_time} + 'futures', + 'futures/{future_name}', + 'futures/{future_name}/stats', + 'funding_rates', + 'lt/tokens', + 'lt/{token_name}', + ], + }, + 'private': { + 'get': [ + 'account', + 'positions', + 'wallet/coins', + 'wallet/balances', + 'wallet/deposit_address/{coin}', + 'wallet/deposits', + 'wallet/withdrawals', + 'orders', // ?market={market} + 'orders/history', // ?market={market} + 'orders/{order_id}', + 'orders/by_client_id/{client_order_id}', + 'conditional_orders', // ?market={market} + 'conditional_orders/history', // ?market={market} + 'fills', // ?market={market} + 'funding_payments', + 'lt/balances', + 'lt/creations', + 'lt/redemptions', + 'subaccounts', + 'subaccounts/{nickname}/balances', + ], + 'post': [ + 'account/leverage', + 'wallet/withdrawals', + 'orders', + 'conditional_orders', + 'lt/{token_name}/create', + 'lt/{token_name}/redeem', + 'subaccounts', + 'subaccounts/update_name', + 'subaccounts/transfer', + ], + 'delete': [ + 'orders/{order_id}', + 'orders/by_client_id/{client_order_id}', + 'orders', + 'conditional_orders/{order_id}', + 'subaccounts', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.02 / 100, + 'taker': 0.07 / 100, + 'tiers': { + 'taker': [ + [0, 0.07 / 100], + [1000000, 0.06 / 100], + [5000000, 0.055 / 100], + [10000000, 0.05 / 100], + [15000000, 0.045 / 100], + [35000000, 0.04 / 100], + ], + 'maker': [ + [0, 0.02 / 100], + [1000000, 0.02 / 100], + [5000000, 0.015 / 100], + [10000000, 0.015 / 100], + [15000000, 0.01 / 100], + [35000000, 0.01 / 100], + ], + }, + }, + 'funding': { + 'withdraw': {}, + }, + }, + 'exceptions': { + 'exact': { + 'Not enough balances': InsufficientFunds, // {"error":"Not enough balances","success":false} + 'InvalidPrice': InvalidOrder, // {"error":"Invalid price","success":false} + 'Size too small': InvalidOrder, // {"error":"Size too small","success":false} + 'Missing parameter price': InvalidOrder, // {"error":"Missing parameter price","success":false} + 'Order not found': OrderNotFound, // {"error":"Order not found","success":false} + }, + 'broad': { + 'Invalid parameter': BadRequest, // {"error":"Invalid parameter start_time","success":false} + 'The requested URL was not found on the server': BadRequest, + 'No such coin': BadRequest, + 'No such market': BadRequest, + 'An unexpected error occurred': ExchangeError, // {"error":"An unexpected error occurred, please try again later (58BC21C795).","success":false} + }, + }, + 'precisionMode': TICK_SIZE, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCoins (params); + const currencies = this.safeValue (response, 'result', []); + // + // { + // "success":true, + // "result": [ + // {"id":"BTC","name":"Bitcoin"}, + // {"id":"ETH","name":"Ethereum"}, + // {"id":"ETHMOON","name":"10X Long Ethereum Token","underlying":"ETH"}, + // {"id":"EOSBULL","name":"3X Long EOS Token","underlying":"EOS"}, + // ], + // } + // + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'id'); + const code = this.safeCurrencyCode (id); + const name = this.safeString (currency, 'name'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': name, + 'active': undefined, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'withdraw': { 'min': undefined, 'max': undefined }, + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + // + // { + // 'success': true, + // "result": [ + // { + // "ask":170.37, + // "baseCurrency":null, + // "bid":170.31, + // "change1h":-0.019001554672655036, + // "change24h":-0.024841165359738997, + // "changeBod":-0.03816406029469881, + // "enabled":true, + // "last":170.37, + // "name":"ETH-PERP", + // "price":170.37, + // "priceIncrement":0.01, + // "quoteCurrency":null, + // "quoteVolume24h":7742164.59889, + // "sizeIncrement":0.001, + // "type":"future", + // "underlying":"ETH", + // "volumeUsd24h":7742164.59889 + // }, + // { + // "ask":170.44, + // "baseCurrency":"ETH", + // "bid":170.41, + // "change1h":-0.018485459257126403, + // "change24h":-0.023825887743413515, + // "changeBod":-0.037605872388481086, + // "enabled":true, + // "last":172.72, + // "name":"ETH/USD", + // "price":170.44, + // "priceIncrement":0.01, + // "quoteCurrency":"USD", + // "quoteVolume24h":382802.0252, + // "sizeIncrement":0.001, + // "type":"spot", + // "underlying":null, + // "volumeUsd24h":382802.0252 + // }, + // ], + // } + // + const result = []; + const markets = this.safeValue (response, 'result', []); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString2 (market, 'baseCurrency', 'underlying'); + const quoteId = this.safeString (market, 'quoteCurrency', 'USD'); + const type = this.safeString (market, 'type'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + // check if a market is a spot or future market + const symbol = (type === 'future') ? this.safeString (market, 'name') : (base + '/' + quote); + const active = this.safeValue (market, 'enabled'); + const sizeIncrement = this.safeFloat (market, 'sizeIncrement'); + const priceIncrement = this.safeFloat (market, 'priceIncrement'); + const precision = { + 'amount': sizeIncrement, + 'price': priceIncrement, + }; + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'type': type, + 'future': (type === 'future'), + 'spot': (type === 'spot'), + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': sizeIncrement, + 'max': undefined, + }, + 'price': { + 'min': priceIncrement, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }; + result.push (entry); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "ask":171.29, + // "baseCurrency":null, // base currency for spot markets + // "bid":171.24, + // "change1h":-0.0012244897959183673, + // "change24h":-0.031603346901854366, + // "changeBod":-0.03297013492914808, + // "enabled":true, + // "last":171.44, + // "name":"ETH-PERP", + // "price":171.29, + // "priceIncrement":0.01, + // "quoteCurrency":null, // quote currency for spot markets + // "quoteVolume24h":8570651.12113, + // "sizeIncrement":0.001, + // "type":"future", + // "underlying":"ETH", // null for spot markets + // "volumeUsd24h":8570651.12113, + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'name'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const type = this.safeString (ticker, 'type'); + if (type === 'future') { + symbol = marketId; + } else { + const base = this.safeCurrencyCode (this.safeString (ticker, 'baseCurrency')); + const quote = this.safeCurrencyCode (this.safeString (ticker, 'quoteCurrency')); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const timestamp = this.milliseconds (); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'change24h'), + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume24h'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + }; + const response = await this.publicGetMarketsMarketName (this.extend (request, params)); + // + // { + // "success":true, + // "result":{ + // "ask":171.29, + // "baseCurrency":null, // base currency for spot markets + // "bid":171.24, + // "change1h":-0.0012244897959183673, + // "change24h":-0.031603346901854366, + // "changeBod":-0.03297013492914808, + // "enabled":true, + // "last":171.44, + // "name":"ETH-PERP", + // "price":171.29, + // "priceIncrement":0.01, + // "quoteCurrency":null, // quote currency for spot markets + // "quoteVolume24h":8570651.12113, + // "sizeIncrement":0.001, + // "type":"future", + // "underlying":"ETH", // null for spot markets + // "volumeUsd24h":8570651.12113, + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseTicker (result, market); + } + + parseTickers (tickers, symbols = undefined) { + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarkets (params); + // + // { + // 'success': true, + // "result": [ + // { + // "ask":170.44, + // "baseCurrency":"ETH", + // "bid":170.41, + // "change1h":-0.018485459257126403, + // "change24h":-0.023825887743413515, + // "changeBod":-0.037605872388481086, + // "enabled":true, + // "last":172.72, + // "name":"ETH/USD", + // "price":170.44, + // "priceIncrement":0.01, + // "quoteCurrency":"USD", + // "quoteVolume24h":382802.0252, + // "sizeIncrement":0.001, + // "type":"spot", + // "underlying":null, + // "volumeUsd24h":382802.0252 + // }, + // ], + // } + // + const tickers = this.safeValue (response, 'result', []); + return this.parseTickers (tickers, symbols); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + }; + if (limit !== undefined) { + request['depth'] = limit; // max 100, default 20 + } + const response = await this.publicGetMarketsMarketNameOrderbook (this.extend (request, params)); + // + // { + // "success":true, + // "result":{ + // "asks":[ + // [171.95,279.865], + // [171.98,102.42], + // [171.99,124.11], + // ], + // "bids":[ + // [171.93,69.749], + // [171.9,288.325], + // [171.88,87.47], + // ], + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseOrderBook (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "close":177.23, + // "high":177.45, + // "low":177.2, + // "open":177.43, + // "startTime":"2019-10-17T13:27:00+00:00", + // "time":1571318820000.0, + // "volume":0.0 + // } + // + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + 'resolution': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + const response = await this.publicGetMarketsMarketNameCandles (this.extend (request, params)); + // + // { + // "success": true, + // "result":[ + // { + // "close":177.23, + // "high":177.45, + // "low":177.2, + // "open":177.43, + // "startTime":"2019-10-17T13:27:00+00:00", + // "time":1571318820000.0, + // "volume":0.0 + // }, + // { + // "close":177.26, + // "high":177.33, + // "low":177.23, + // "open":177.23, + // "startTime":"2019-10-17T13:28:00+00:00", + // "time":1571318880000.0, + // "volume":0.0 + // }, + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseOHLCVs (result, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "id":1715826, + // "liquidation":false, + // "price":171.62, + // "side":"buy", + // "size":2.095, + // "time":"2019-10-18T12:59:54.288166+00:00" + // } + // + // fetchMyTrades (private) + // + // { + // "fee": 20.1374935, + // "feeRate": 0.0005, + // "future": "EOS-0329", + // "id": 11215, + // "liquidity": "taker", + // "market": "EOS-0329", + // "baseCurrency": null, + // "quoteCurrency": null, + // "orderId": 8436981, + // "price": 4.201, + // "side": "buy", + // "size": 9587, + // "time": "2019-03-27T19:15:10.204619+00:00", + // "type": "order" + // } + // + const id = this.safeString (trade, 'id'); + const takerOrMaker = this.safeString (trade, 'liquidity'); + const marketId = this.safeString (trade, 'market'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const base = this.safeCurrencyCode (this.safeString (trade, 'baseCurrency')); + const quote = this.safeCurrencyCode (this.safeString (trade, 'quoteCurrency')); + if ((base !== undefined) && (quote !== undefined)) { + symbol = base + '/' + quote; + } else { + symbol = marketId; + } + } + } + const timestamp = this.parse8601 (this.safeString (trade, 'time')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const side = this.safeString (trade, 'side'); + let cost = undefined; + if (price !== undefined && amount !== undefined) { + cost = price * amount; + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'rate': this.safeFloat (trade, 'feeRate'), + }; + } + const orderId = this.safeString (trade, 'orderId'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + }; + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + // start_time doesn't work without end_time + request['end_time'] = this.seconds (); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetMarketsMarketNameTrades (this.extend (request, params)); + // + // { + // "success":true, + // "result":[ + // { + // "id":1715826, + // "liquidation":false, + // "price":171.62, + // "side":"buy", + // "size":2.095, + // "time":"2019-10-18T12:59:54.288166+00:00" + // }, + // { + // "id":1715763, + // "liquidation":false, + // "price":171.89, + // "side":"sell", + // "size":1.477, + // "time":"2019-10-18T12:58:38.443734+00:00" + // }, + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccount (params); + // + // { + // "success": true, + // "result": { + // "backstopProvider": true, + // "collateral": 3568181.02691129, + // "freeCollateral": 1786071.456884368, + // "initialMarginRequirement": 0.12222384240257728, + // "liquidating": false, + // "maintenanceMarginRequirement": 0.07177992558058484, + // "makerFee": 0.0002, + // "marginFraction": 0.5588433331419503, + // "openMarginFraction": 0.2447194090423075, + // "takerFee": 0.0005, + // "totalAccountValue": 3568180.98341129, + // "totalPositionSize": 6384939.6992, + // "username": "user@domain.com", + // "positions": [ + // { + // "cost": -31.7906, + // "entryPrice": 138.22, + // "future": "ETH-PERP", + // "initialMarginRequirement": 0.1, + // "longOrderSize": 1744.55, + // "maintenanceMarginRequirement": 0.04, + // "netSize": -0.23, + // "openSize": 1744.32, + // "realizedPnl": 3.39441714, + // "shortOrderSize": 1732.09, + // "side": "sell", + // "size": 0.23, + // "unrealizedPnl": 0, + // }, + // ], + // }, + // } + // + const result = this.safeValue (response, 'result', {}); + return { + 'info': response, + 'maker': this.safeFloat (result, 'makerFee'), + 'taker': this.safeFloat (result, 'takerFee'), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletBalances (params); + // + // { + // "success": true, + // "result": [ + // { + // "coin": "USDTBEAR", + // "free": 2320.2, + // "total": 2340.2 + // }, + // ], + // } + // + const result = { + 'info': response, + }; + const balances = this.safeValue (response, 'result', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const code = this.safeCurrencyCode (this.safeString (balance, 'coin')); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'free'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'new': 'open', + 'open': 'open', + 'closed': 'closed', // filled or canceled + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder, fetchOrders, fetchOpenOrders, createOrder ("limit", "market") + // + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 0, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "remainingSize": 31431, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null, + // } + // + // createOrder (conditional, "stop", "trailingStop", or "takeProfit") + // + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "triggerPrice": 0.306525, + // "orderId": null, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "stop", + // "orderPrice": null, + // "error": null, + // "triggeredAt": null, + // "reduceOnly": false + // } + // + const id = this.safeString (order, 'id'); + const timestamp = this.parse8601 (this.safeString (order, 'createdAt')); + const filled = this.safeFloat (order, 'filledSize'); + const remaining = this.safeFloat (order, 'remainingSize'); + let symbol = undefined; + const marketId = this.safeString (order, 'market'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const side = this.safeString (order, 'side'); + const type = this.safeString (order, 'type'); + const amount = this.safeFloat (order, 'size'); + let cost = undefined; + if (filled !== undefined && amount !== undefined) { + cost = filled * amount; + } + const price = this.safeFloat2 (order, 'price', 'triggerPrice'); + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'triggeredAt')); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'side': side, // "buy" or "sell" + // 'price': 0.306525, // send null for market orders + 'type': type, // "limit", "market", "stop", "trailingStop", or "takeProfit" + 'size': parseFloat (this.amountToPrecision (symbol, amount)), + // 'reduceOnly': false, // optional, default is false + // 'ioc': false, // optional, default is false, limit or market orders only + // 'postOnly': false, // optional, default is false, limit or market orders only + // 'clientId': 'abcdef0123456789', // string, optional, client order id, limit or market orders only + }; + let priceToPrecision = undefined; + if (price !== undefined) { + priceToPrecision = parseFloat (this.priceToPrecision (symbol, price)); + } + let method = 'privatePostConditionalOrders'; + if (type === 'limit') { + method = 'privatePostOrders'; + request['price'] = priceToPrecision; + } else if (type === 'market') { + method = 'privatePostOrders'; + request['price'] = null; + } else if ((type === 'stop') || (type === 'takeProfit')) { + request['triggerPrice'] = priceToPrecision; + // request['orderPrice'] = number; // optional, order type is limit if this is specified, otherwise market + } else if (type === 'trailingStop') { + request['trailValue'] = priceToPrecision; // negative for "sell", positive for "buy" + } else { + throw new InvalidOrder (this.id + ' createOrder () does not support order type ' + type + ', only limit, market, stop, trailingStop, or takeProfit orders are supported'); + } + const response = await this[method] (this.extend (request, params)); + // + // orders + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 0, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "remainingSize": 31431, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null, + // } + // ] + // } + // + // conditional orders + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "triggerPrice": 0.306525, + // "orderId": null, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "stop", + // "orderPrice": null, + // "error": null, + // "triggeredAt": null, + // "reduceOnly": false + // } + // ] + // } + // + // + const result = this.safeValue (response, 'result', []); + return this.parseOrder (result, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privateDeleteOrdersOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "result": "Order queued for cancelation" + // } + // + const result = this.safeValue (response, 'result', {}); + return result; + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'market': market['id'], // optional + 'conditionalOrdersOnly': false, // cancel conditional orders only + 'limitOrdersOnly': false, // cancel existing limit orders (non-conditional orders) only + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateDeleteOrders (this.extend (request, params)); + const result = this.safeValue (response, 'result', {}); + // + // { + // "success": true, + // "result": "Orders queued for cancelation" + // } + // + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateGetOrdersOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "result": { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 10, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "avgFillPrice": 0.306526, + // "remainingSize": 31421, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseOrder (result); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 10, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "avgFillPrice": 0.306526, + // "remainingSize": 31421, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null + // } + // ] + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseOrders (result, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 100 + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + const response = await this.privateGetOrdersHistory (this.extend (request, params)); + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 10, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "avgFillPrice": 0.306526, + // "remainingSize": 31421, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null + // } + // ] + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseOrders (result, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + const response = await this.privateGetFills (this.extend (request, params)); + // + // { + // "success": true, + // "result": [ + // { + // "fee": 20.1374935, + // "feeRate": 0.0005, + // "future": "EOS-0329", + // "id": 11215, + // "liquidity": "taker", + // "market": "EOS-0329", + // "baseCurrency": null, + // "quoteCurrency": null, + // "orderId": 8436981, + // "price": 4.201, + // "side": "buy", + // "size": 9587, + // "time": "2019-03-27T19:15:10.204619+00:00", + // "type": "order" + // } + // ] + // } + // + const trades = this.safeValue (response, 'result', []); + return this.parseTrades (trades, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + 'size': amount, + 'address': address, + // 'password': 'string', // optional withdrawal password if it is required for your account + // 'code': '192837', // optional 2fa code if it is required for your account + }; + if (tag !== undefined) { + request['tag'] = tag; + } + const response = await this.privatePostWalletWithdrawals (this.extend (request, params)); + // + // { + // "success": true, + // "result": { + // "coin": "USDTBEAR", + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null", + // "fee": 0, + // "id": 1, + // "size": "20.2", + // "status": "requested", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "null" + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseTransaction (result, currency); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + }; + const response = await this.privateGetWalletDepositAddressCoin (this.extend (request, params)); + // + // { + // "success": true, + // "result": { + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null" + // } + // } + // + const result = this.safeValue (response, 'result', {}); + const address = this.safeString (result, 'address'); + const tag = this.safeString (result, 'tag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + // what are other statuses here? + 'confirmed': 'ok', // deposits + 'complete': 'ok', // withdrawals + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction) { + // + // fetchDeposits + // + // { + // "coin": "TUSD", + // "confirmations": 64, + // "confirmedTime": "2019-03-05T09:56:55.728933+00:00", + // "fee": 0, + // "id": 1, + // "sentTime": "2019-03-05T09:56:55.735929+00:00", + // "size": "99.0", + // "status": "confirmed", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // + // fetchWithdrawals + // + // { + // "coin": "TUSD", + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null", + // "fee": 0, + // "id": 1, + // "size": "99.0", + // "status": "complete", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // + const code = this.safeCurrencyCode (this.safeString (transaction, 'coin')); + const id = this.safeInteger (transaction, 'id'); + const amount = this.safeFloat (transaction, 'size'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const timestamp = this.parse8601 (this.safeString (transaction, 'time')); + const txid = this.safeString (transaction, 'txid'); + const address = this.safeString (transaction, 'address'); + const tag = this.safeString (transaction, 'tag'); + const fee = this.safeFloat (transaction, 'fee'); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': undefined, + 'addressTo': address, + 'tagFrom': undefined, + 'tag': tag, + 'tagTo': undefined, + 'type': undefined, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': { + 'currency': code, + 'cost': fee, + 'rate': undefined, + }, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletDeposits (params); + // + // { + // "success": true, + // "result": { + // "coin": "TUSD", + // "confirmations": 64, + // "confirmedTime": "2019-03-05T09:56:55.728933+00:00", + // "fee": 0, + // "id": 1, + // "sentTime": "2019-03-05T09:56:55.735929+00:00", + // "size": "99.0", + // "status": "confirmed", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTransactions (result); + } + + async fetchWithdrawals (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletWithdrawals (params); + // + // { + // "success": true, + // "result": { + // "coin": "TUSD", + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null", + // "fee": 0, + // "id": 1, + // "size": "99.0", + // "status": "complete", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTransactions (result); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/api/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + if (method !== 'POST') { + if (Object.keys (query).length) { + const suffix = '?' + this.urlencode (query); + url += suffix; + request += suffix; + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.milliseconds ().toString (); + let auth = timestamp + method + request; + headers = { + 'FTX-KEY': this.apiKey, + 'FTX-TS': timestamp, + }; + if (method === 'POST') { + body = this.json (query); + auth += body; + headers['Content-Type'] = 'application/json'; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256'); + headers['FTX-SIGN'] = signature; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to the default error handler + } + // + // {"error":"Invalid parameter start_time","success":false} + // {"error":"Not enough balances","success":false} + // + const success = this.safeValue (response, 'success'); + if (!success) { + const feedback = this.id + ' ' + body; + const error = this.safeString (response, 'error'); + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],88:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class fybse extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'fybse', + 'name': 'FYB-SE', + 'countries': [ 'SE' ], // Sweden + 'has': { + 'CORS': false, + }, + 'rateLimit': 1500, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766512-31019772-5edb-11e7-8241-2e675e6797f1.jpg', + 'api': 'https://www.fybse.se/api/SEK', + 'www': 'https://www.fybse.se', + 'doc': 'https://fyb.docs.apiary.io', + }, + 'api': { + 'public': { + 'get': [ + 'ticker', + 'tickerdetailed', + 'orderbook', + 'trades', + ], + }, + 'private': { + 'post': [ + 'test', + 'getaccinfo', + 'getpendingorders', + 'getorderhistory', + 'cancelpendingorder', + 'placeorder', + 'withdraw', + ], + }, + }, + 'markets': { + 'BTC/SEK': { 'id': 'SEK', 'symbol': 'BTC/SEK', 'base': 'BTC', 'quote': 'SEK' }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetaccinfo (params); + const btc = this.safeFloat (response, 'btcBal'); + const symbol = this.symbols[0]; + const quote = this.markets[symbol]['quote']; + const lowercase = quote.toLowerCase () + 'Bal'; + const fiat = this.safeFloat (response, lowercase); + const crypto = this.account (); + crypto['total'] = btc; + const result = { 'BTC': crypto }; + result[quote] = this.account (); + result[quote]['total'] = fiat; + result['info'] = response; + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetOrderbook (params); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const ticker = await this.publicGetTickerdetailed (params); + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + const volume = this.safeFloat (ticker, 'vol'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': volume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTrades (params); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'qty': amount, + 'price': price, + 'type': side[0].toUpperCase (), + }; + const response = await this.privatePostPlaceorder (this.extend (request, params)); + return { + 'info': response, + 'id': response['pending_oid'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderNo': id, + }; + return await this.privatePostCancelpendingorder (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + url += '.json'; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'timestamp': nonce }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'key': this.apiKey, + 'sig': this.hmac (this.encode (body), this.encode (this.secret), 'sha1'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (api === 'private') { + if ('error' in response) { + if (response['error']) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],89:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ExchangeError, ArgumentsRequired, InvalidAddress, OrderNotFound, NotSupported, DDoSProtection, InsufficientFunds, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class gateio extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'gateio', + 'name': 'Gate.io', + 'countries': [ 'CN' ], + 'version': '2', + 'rateLimit': 1000, + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchTickers': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': true, + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'fetchClosedOrders': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrderTrades': true, + 'fetchOrders': true, + 'fetchOrder': true, + 'fetchMyTrades': true, + }, + 'timeframes': { + '1m': 60, + '5m': 300, + '10m': 600, + '15m': 900, + '30m': 1800, + '1h': 3600, + '2h': 7200, + '4h': 14400, + '6h': 21600, + '12h': 43200, + '1d': 86400, + '1w': 604800, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg', + 'api': { + 'public': 'https://data.gate.io/api', + 'private': 'https://data.gate.io/api', + }, + 'www': 'https://gate.io/', + 'doc': 'https://gate.io/api2', + 'fees': [ + 'https://gate.io/fee', + 'https://support.gate.io/hc/en-us/articles/115003577673', + ], + 'referral': 'https://www.gate.io/signup/2436035', + }, + 'api': { + 'public': { + 'get': [ + 'candlestick2/{id}', + 'pairs', + 'marketinfo', + 'marketlist', + 'tickers', + 'ticker/{id}', + 'orderBook/{id}', + 'trade/{id}', + 'tradeHistory/{id}', + 'tradeHistory/{id}/{tid}', + ], + }, + 'private': { + 'post': [ + 'balances', + 'depositAddress', + 'newAddress', + 'depositsWithdrawals', + 'buy', + 'sell', + 'cancelOrder', + 'cancelAllOrders', + 'getOrder', + 'openOrders', + 'tradeHistory', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.002, + 'taker': 0.002, + }, + }, + 'exceptions': { + 'exact': { + '4': DDoSProtection, + '5': AuthenticationError, // { result: "false", code: 5, message: "Error: invalid key or sign, please re-generate it from your account" } + '6': AuthenticationError, // { result: 'false', code: 6, message: 'Error: invalid data ' } + '7': NotSupported, + '8': NotSupported, + '9': NotSupported, + '15': DDoSProtection, + '16': OrderNotFound, + '17': OrderNotFound, + '20': InvalidOrder, + '21': InsufficientFunds, + }, + // https://gate.io/api2#errCode + 'errorCodeNames': { + '1': 'Invalid request', + '2': 'Invalid version', + '3': 'Invalid request', + '4': 'Too many attempts', + '5': 'Invalid sign', + '6': 'Invalid sign', + '7': 'Currency is not supported', + '8': 'Currency is not supported', + '9': 'Currency is not supported', + '10': 'Verified failed', + '11': 'Obtaining address failed', + '12': 'Empty params', + '13': 'Internal error, please report to administrator', + '14': 'Invalid user', + '15': 'Cancel order too fast, please wait 1 min and try again', + '16': 'Invalid order id or order is already closed', + '17': 'Invalid orderid', + '18': 'Invalid amount', + '19': 'Not permitted or trade is disabled', + '20': 'Your order size is too small', + '21': 'You don\'t have enough fund', + }, + }, + 'options': { + 'fetchTradesMethod': 'public_get_tradehistory_id', // 'public_get_tradehistory_id_tid' + 'limits': { + 'cost': { + 'min': { + 'BTC': 0.0001, + 'ETH': 0.001, + 'USDT': 1, + }, + }, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketinfo (params); + const markets = this.safeValue (response, 'pairs'); + if (!markets) { + throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response'); + } + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const keys = Object.keys (market); + const id = this.safeString (keys, 0); + const details = market[id]; + // all of their symbols are separated with an underscore + // but not boe_eth_eth (BOE_ETH/ETH) which has two underscores + // https://github.com/ccxt/ccxt/issues/4894 + const parts = id.split ('_'); + const numParts = parts.length; + let baseId = parts[0]; + let quoteId = parts[1]; + if (numParts > 2) { + baseId = parts[0] + '_' + parts[1]; + quoteId = parts[2]; + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': details['decimal_places'], + }; + const amountLimits = { + 'min': details['min_amount'], + 'max': undefined, + }; + const priceLimits = { + 'min': Math.pow (10, -details['decimal_places']), + 'max': undefined, + }; + const defaultCost = amountLimits['min'] * priceLimits['min']; + const minCost = this.safeFloat (this.options['limits']['cost']['min'], quote, defaultCost); + const costLimits = { + 'min': minCost, + 'max': undefined, + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const active = true; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'maker': details['fee'] / 100, + 'taker': details['fee'] / 100, + 'precision': precision, + 'limits': limits, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalances (params); + const result = { 'info': response }; + let available = this.safeValue (response, 'available', {}); + if (Array.isArray (available)) { + available = {}; + } + const locked = this.safeValue (response, 'locked', {}); + const currencyIds = Object.keys (available); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (available, currencyId); + account['used'] = this.safeFloat (locked, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetOrderBookId (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // they return [ Timestamp, Volume, Close, High, Low, Open ] + return [ + parseInt (ohlcv[0]), // t + parseFloat (ohlcv[5]), // o + parseFloat (ohlcv[3]), // h + parseFloat (ohlcv[4]), // l + parseFloat (ohlcv[2]), // c + parseFloat (ohlcv[1]), // v + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + 'group_sec': this.timeframes[timeframe], + }; + // max limit = 1001 + if (limit !== undefined) { + const periodDurationInSeconds = this.parseTimeframe (timeframe); + const hours = parseInt ((periodDurationInSeconds * limit) / 3600); + request['range_hour'] = Math.max (0, hours - 1); + } + const response = await this.publicGetCandlestick2Id (this.extend (request, params)); + // + // { + // "elapsed": "15ms", + // "result": "true", + // "data": [ + // [ "1553930820000", "1.005299", "4081.05", "4086.18", "4081.05", "4086.18" ], + // [ "1553930880000", "0.110923277", "4095.2", "4095.23", "4091.15", "4091.15" ], + // ... + // [ "1553934420000", "0", "4089.42", "4089.42", "4089.42", "4089.42" ], + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOHLCVs (data, market, timeframe, since, limit); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'percentChange'); + let open = undefined; + let change = undefined; + let average = undefined; + if ((last !== undefined) && (percentage !== undefined)) { + const relativeChange = percentage / 100; + open = last / this.sum (1, relativeChange); + change = last - open; + average = this.sum (last, open) / 2; + } + open = this.safeFloat (ticker, 'open', open); + change = this.safeFloat (ticker, 'change', change); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat2 (ticker, 'high24hr', 'high'), + 'low': this.safeFloat2 (ticker, 'low24hr', 'low'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'quoteVolume'), // gateio has them reversed + 'quoteVolume': this.safeFloat (ticker, 'baseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const [ baseId, quoteId ] = id.split ('_'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + let market = undefined; + if (symbol in this.markets) { + market = this.markets[symbol]; + } + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + } + result[symbol] = this.parseTicker (response[id], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const ticker = await this.publicGetTickerId (this.extend ({ + 'id': market['id'], + }, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + let timestamp = this.safeTimestamp2 (trade, 'timestamp', 'time_unix'); + timestamp = this.safeTimestamp (trade, 'time', timestamp); + const id = this.safeString2 (trade, 'tradeID', 'id'); + // take either of orderid or orderId + const orderId = this.safeString2 (trade, 'orderid', 'orderNumber'); + const price = this.safeFloat2 (trade, 'rate', 'price'); + const amount = this.safeFloat (trade, 'amount'); + const type = this.safeString (trade, 'type'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': type, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const method = this.safeString (this.options, 'fetchTradesMethod', 'public_get_tradehistory_id'); + const response = await this[method] (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.privatePostOpenOrders (params); + return this.parseOrders (response['orders'], undefined, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderNumber': id, + 'currencyPair': this.marketId (symbol), + }; + const response = await this.privatePostGetOrder (this.extend (request, params)); + return this.parseOrder (response['order']); + } + + parseOrderStatus (status) { + const statuses = { + 'cancelled': 'canceled', + // 'closed': 'closed', // these two statuses aren't actually needed + // 'open': 'open', // as they are mapped one-to-one + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // {'amount': '0.00000000', + // 'currencyPair': 'xlm_usdt', + // 'fee': '0.0113766632239302 USDT', + // 'feeCurrency': 'USDT', + // 'feePercentage': 0.18, + // 'feeValue': '0.0113766632239302', + // 'filledAmount': '30.14004987', + // 'filledRate': 0.2097, + // 'initialAmount': '30.14004987', + // 'initialRate': '0.2097', + // 'left': 0, + // 'orderNumber': '998307286', + // 'rate': '0.2097', + // 'status': 'closed', + // 'timestamp': 1531158583, + // 'type': 'sell'}, + // + const id = this.safeString2 (order, 'orderNumber', 'id'); + let symbol = undefined; + const marketId = this.safeString (order, 'currencyPair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp2 (order, 'timestamp', 'ctime'); + const lastTradeTimestamp = this.safeTimestamp (order, 'mtime'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let side = this.safeString (order, 'type'); + // handling for order.update messages + if (side === '1') { + side = 'sell'; + } else if (side === '2') { + side = 'buy'; + } + const price = this.safeFloat2 (order, 'initialRate', 'price'); + const average = this.safeFloat (order, 'filledRate'); + const amount = this.safeFloat2 (order, 'initialAmount', 'amount'); + const filled = this.safeFloat (order, 'filledAmount'); + // In the order status response, this field has a different name. + const remaining = this.safeFloat2 (order, 'leftAmount', 'left'); + const feeCost = this.safeFloat (order, 'feeValue'); + const feeCurrencyId = this.safeString (order, 'feeCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + let feeRate = this.safeFloat (order, 'feePercentage'); + if (feeRate !== undefined) { + feeRate = feeRate / 100; + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'average': average, + 'trades': undefined, + 'fee': { + 'cost': feeCost, + 'currency': feeCurrencyCode, + 'rate': feeRate, + }, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const method = 'privatePost' + this.capitalize (side); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'rate': price, + 'amount': amount, + }; + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (this.extend ({ + 'status': 'open', + 'type': side, + 'initialAmount': amount, + }, response), market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires symbol argument'); + } + await this.loadMarkets (); + const request = { + 'orderNumber': id, + 'currencyPair': this.marketId (symbol), + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async queryDepositAddress (method, code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + method = 'privatePost' + method + 'Address'; + const request = { + 'currency': currency['id'], + }; + const response = await this[method] (this.extend (request, params)); + let address = this.safeString (response, 'addr'); + let tag = undefined; + if ((address !== undefined) && (address.indexOf ('address') >= 0)) { + throw new InvalidAddress (this.id + ' queryDepositAddress ' + address); + } + if (code === 'XRP') { + const parts = address.split (' '); + address = parts[0]; + tag = parts[1]; + } + return { + 'currency': currency, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async createDepositAddress (code, params = {}) { + return await this.queryDepositAddress ('New', code, params); + } + + async fetchDepositAddress (code, params = {}) { + return await this.queryDepositAddress ('Deposit', code, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privatePostOpenOrders (params); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'orderNumber': id, + }; + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires symbol param'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, // Address must exist in you AddressBook in security settings + }; + if (tag !== undefined) { + request['address'] += ' ' + tag; + } + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const prefix = (api === 'private') ? (api + '/') : ''; + let url = this.urls['api'][api] + this.version + '/1/' + prefix + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = { 'nonce': nonce }; + body = this.urlencode (this.extend (request, query)); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Key': this.apiKey, + 'Sign': signature, + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchTransactionsByType (type = undefined, code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['start'] = since; + } + const response = await this.privatePostDepositsWithdrawals (this.extend (request, params)); + let transactions = undefined; + if (type === undefined) { + const deposits = this.safeValue (response, 'deposits', []); + const withdrawals = this.safeValue (response, 'withdraws', []); + transactions = this.arrayConcat (deposits, withdrawals); + } else { + transactions = this.safeValue (response, type, []); + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + return this.parseTransactions (transactions, currency, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType (undefined, code, since, limit, params); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('deposits', code, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('withdraws', code, since, limit, params); + } + + parseTransaction (transaction, currency = undefined) { + // + // deposit + // + // { + // 'id': 'd16520849', + // 'currency': 'NEO', + // 'address': False, + // 'amount': '1', + // 'txid': '01acf6b8ce4d24a....', + // 'timestamp': '1553125968', + // 'status': 'DONE', + // 'type': 'deposit' + // } + // + // withdrawal + // + // { + // 'id': 'w5864259', + // 'currency': 'ETH', + // 'address': '0x72632f462....', + // 'amount': '0.4947', + // 'txid': '0x111167d120f736....', + // 'timestamp': '1553123688', + // 'status': 'DONE', + // 'type': 'withdrawal' + // } + // + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const id = this.safeString (transaction, 'id'); + const txid = this.safeString (transaction, 'txid'); + const amount = this.safeFloat (transaction, 'amount'); + let address = this.safeString (transaction, 'address'); + if (address === 'false') { + address = undefined; + } + const timestamp = this.safeTimestamp (transaction, 'timestamp'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const type = this.parseTransactionType (id[0]); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'PEND': 'pending', + 'REQUEST': 'pending', + 'DMOVE': 'pending', + 'CANCEL': 'failed', + 'DONE': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransactionType (type) { + const types = { + 'd': 'deposit', + 'w': 'withdrawal', + }; + return this.safeString (types, type, type); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + const resultString = this.safeString (response, 'result', ''); + if (resultString !== 'false') { + return; + } + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'message', body); + if (errorCode !== undefined) { + const feedback = this.safeString (this.exceptions['errorCodeNames'], errorCode, message); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],90:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, BadRequest, OrderNotFound, InvalidOrder, InvalidNonce, InsufficientFunds, AuthenticationError, PermissionDenied, NotSupported, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class gemini extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'gemini', + 'name': 'Gemini', + 'countries': [ 'US' ], + 'rateLimit': 1500, // 200 for private API + 'version': 'v1', + 'has': { + 'fetchDepositAddress': false, + 'createDepositAddress': true, + 'CORS': false, + 'fetchBidsAsks': false, + 'fetchTickers': false, + 'fetchMyTrades': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': false, + 'createMarketOrder': false, + 'withdraw': true, + 'fetchTransactions': true, + 'fetchWithdrawals': false, + 'fetchDeposits': false, + 'fetchOHLCV': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27816857-ce7be644-6096-11e7-82d6-3c257263229c.jpg', + 'api': { + 'public': 'https://api.gemini.com', + 'private': 'https://api.gemini.com', + 'web': 'https://docs.gemini.com', + }, + 'www': 'https://gemini.com/', + 'doc': [ + 'https://docs.gemini.com/rest-api', + 'https://docs.sandbox.gemini.com', + ], + 'test': 'https://api.sandbox.gemini.com', + 'fees': [ + 'https://gemini.com/api-fee-schedule', + 'https://gemini.com/trading-fees', + 'https://gemini.com/transfer-fees', + ], + }, + 'api': { + 'web': { + 'get': [ + 'rest-api', + ], + }, + 'public': { + 'get': [ + 'v1/symbols', + 'v1/pubticker/{symbol}', + 'v1/book/{symbol}', + 'v1/trades/{symbol}', + 'v1/auction/{symbol}', + 'v1/auction/{symbol}/history', + 'v2/candles/{symbol}/{timeframe}', + 'v2/ticker/{symbol}', + ], + }, + 'private': { + 'post': [ + 'v1/order/new', + 'v1/order/cancel', + 'v1/order/cancel/session', + 'v1/order/cancel/all', + 'v1/order/status', + 'v1/orders', + 'v1/mytrades', + 'v1/notionalvolume', + 'v1/tradevolume', + 'v1/transfers', + 'v1/balances', + 'v1/deposit/{currency}/newAddress', + 'v1/withdraw/{currency}', + 'v1/heartbeat', + 'v1/transfers', + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.0035, + 'maker': 0.001, + }, + }, + 'httpExceptions': { + '400': BadRequest, // Auction not open or paused, ineligible timing, market not open, or the request was malformed, in the case of a private API request, missing or malformed Gemini private API authentication headers + '403': PermissionDenied, // The API key is missing the role necessary to access this private API endpoint + '404': OrderNotFound, // Unknown API entry point or Order not found + '406': InsufficientFunds, // Insufficient Funds + '429': RateLimitExceeded, // Rate Limiting was applied + '500': ExchangeError, // The server encountered an error + '502': ExchangeError, // Technical issues are preventing the request from being satisfied + '503': OnMaintenance, // The exchange is down for maintenance + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1hr', + '6h': '6hr', + '1d': '1day', + }, + 'exceptions': { + 'exact': { + 'AuctionNotOpen': BadRequest, // Failed to place an auction-only order because there is no current auction open for this symbol + 'ClientOrderIdTooLong': BadRequest, // The Client Order ID must be under 100 characters + 'ClientOrderIdMustBeString': BadRequest, // The Client Order ID must be a string + 'ConflictingOptions': BadRequest, // New orders using a combination of order execution options are not supported + 'EndpointMismatch': BadRequest, // The request was submitted to an endpoint different than the one in the payload + 'EndpointNotFound': BadRequest, // No endpoint was specified + 'IneligibleTiming': BadRequest, // Failed to place an auction order for the current auction on this symbol because the timing is not eligible, new orders may only be placed before the auction begins. + 'InsufficientFunds': InsufficientFunds, // The order was rejected because of insufficient funds + 'InvalidJson': BadRequest, // The JSON provided is invalid + 'InvalidNonce': InvalidNonce, // The nonce was not greater than the previously used nonce, or was not present + 'InvalidOrderType': InvalidOrder, // An unknown order type was provided + 'InvalidPrice': InvalidOrder, // For new orders, the price was invalid + 'InvalidQuantity': InvalidOrder, // A negative or otherwise invalid quantity was specified + 'InvalidSide': InvalidOrder, // For new orders, and invalid side was specified + 'InvalidSignature': AuthenticationError, // The signature did not match the expected signature + 'InvalidSymbol': BadRequest, // An invalid symbol was specified + 'InvalidTimestampInPayload': BadRequest, // The JSON payload contained a timestamp parameter with an unsupported value. + 'Maintenance': OnMaintenance, // The system is down for maintenance + 'MarketNotOpen': InvalidOrder, // The order was rejected because the market is not accepting new orders + 'MissingApikeyHeader': AuthenticationError, // The X-GEMINI-APIKEY header was missing + 'MissingOrderField': InvalidOrder, // A required order_id field was not specified + 'MissingRole': AuthenticationError, // The API key used to access this endpoint does not have the required role assigned to it + 'MissingPayloadHeader': AuthenticationError, // The X-GEMINI-PAYLOAD header was missing + 'MissingSignatureHeader': AuthenticationError, // The X-GEMINI-SIGNATURE header was missing + 'NoSSL': AuthenticationError, // You must use HTTPS to access the API + 'OptionsMustBeArray': BadRequest, // The options parameter must be an array. + 'OrderNotFound': OrderNotFound, // The order specified was not found + 'RateLimit': RateLimitExceeded, // Requests were made too frequently. See Rate Limits below. + 'System': ExchangeError, // We are experiencing technical issues + 'UnsupportedOption': BadRequest, // This order execution option is not supported. + }, + 'broad': { + 'The Gemini Exchange is currently undergoing maintenance.': OnMaintenance, // The Gemini Exchange is currently undergoing maintenance. Please check https://status.gemini.com/ for more information. + }, + }, + 'options': { + 'fetchMarketsMethod': 'fetch_markets_from_web', + }, + }); + } + + async fetchMarkets (params = {}) { + const method = this.safeValue (this.options, 'fetchMarketsMethod', 'fetch_markets_from_api'); + return await this[method] (params); + } + + async fetchMarketsFromWeb (symbols = undefined, params = {}) { + const response = await this.webGetRestApi (params); + const sections = response.split ('

Symbols and minimums

'); + const numSections = sections.length; + const error = this.id + ' the ' + this.name + ' API doc HTML markup has changed, breaking the parser of order limits and precision info for ' + this.name + ' markets.'; + if (numSections !== 2) { + throw new NotSupported (error); + } + const tables = sections[1].split ('tbody>'); + const numTables = tables.length; + if (numTables < 2) { + throw new NotSupported (error); + } + // tables[1] = tables[1].replace ("\n", ''); // eslint-disable-line quotes + const rows = tables[1].split ("\n"); // eslint-disable-line quotes + const numRows = rows.length; + if (numRows < 2) { + throw new NotSupported (error); + } + const result = []; + // skip the first element (empty string) + for (let i = 1; i < numRows; i++) { + const row = rows[i]; + const cells = row.split ("\n"); // eslint-disable-line quotes + const numCells = cells.length; + if (numCells < 7) { + throw new NotSupported (error); + } + // + // [ + // 'btcusd', + // 'USD', // quote + // 'BTC', // base + // '0.00001 BTC (1e-5)', // min amount + // '0.00000001 BTC (1e-8)', // amount min tick size + // '0.01 USD', // price min tick size + // '\n' + // ] + // + let id = cells[0].replace ('', ''); + id = id.replace ('', ''); + id = id.replace ('', ''); + let baseId = cells[2].replace ('', ''); + let quoteId = cells[1].replace ('', ''); + const minAmountAsString = cells[3].replace ('', ''); + const amountTickSizeAsString = cells[4].replace ('', ''); + const priceTickSizeAsString = cells[5].replace ('', ''); + const minAmount = minAmountAsString.split (' '); + const amountPrecision = amountTickSizeAsString.split (' '); + const pricePrecision = priceTickSizeAsString.split (' '); + baseId = baseId.toLowerCase (); + quoteId = quoteId.toLowerCase (); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.precisionFromString (amountPrecision[0]), + 'price': this.precisionFromString (pricePrecision[0]), + }; + const active = undefined; + result.push ({ + 'id': id, + 'info': row, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': parseFloat (minAmount[0]), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchMarketsFromAPI (params = {}) { + const response = await this.publicGetV1Symbols (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const id = response[i]; + const market = id; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': undefined, + 'price': undefined, + }; + result.push ({ + 'id': id, + 'info': market, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit_bids'] = limit; + request['limit_asks'] = limit; + } + const response = await this.publicGetV1BookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.publicGetV1PubtickerSymbol (this.extend (request, params)); + const timestamp = this.safeInteger (ticker['volume'], 'timestamp'); + const baseCurrency = market['base']; // unified structures are guaranteed to have unified fields + const quoteCurrency = market['quote']; // so we don't need safe-methods for unified structures + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker['volume'], baseCurrency), + 'quoteVolume': this.safeFloat (ticker['volume'], quoteCurrency), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'timestampms'); + const id = this.safeString (trade, 'tid'); + const orderId = this.safeString (trade, 'order_id'); + const feeCurrencyId = this.safeString (trade, 'fee_currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + const fee = { + 'cost': this.safeFloat (trade, 'fee_amount'), + 'currency': feeCurrencyCode, + }; + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const type = undefined; + const side = this.safeStringLower (trade, 'type'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'order': orderId, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'cost': cost, + 'amount': amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetV1TradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostV1Balances (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'amount'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + const timestamp = this.safeInteger (order, 'timestampms'); + const amount = this.safeFloat (order, 'original_amount'); + const remaining = this.safeFloat (order, 'remaining_amount'); + const filled = this.safeFloat (order, 'executed_amount'); + let status = 'closed'; + if (order['is_live']) { + status = 'open'; + } + if (order['is_cancelled']) { + status = 'canceled'; + } + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avg_execution_price'); + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = filled * average; + } + } + let type = this.safeString (order, 'type'); + if (type === 'exchange limit') { + type = 'limit'; + } else if (type === 'market buy' || type === 'market sell') { + type = 'market'; + } else { + type = order['type']; + } + const fee = undefined; + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'order_id'); + const side = this.safeStringLower (order, 'side'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privatePostV1OrderStatus (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostV1Orders (params); + let orders = this.parseOrders (response, undefined, since, limit); + if (symbol !== undefined) { + const market = this.market (symbol); // throws on non-existent symbol + orders = this.filterBySymbol (orders, market['symbol']); + } + return orders; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const nonce = this.nonce (); + const request = { + 'client_order_id': nonce.toString (), + 'symbol': this.marketId (symbol), + 'amount': amount.toString (), + 'price': price.toString (), + 'side': side, + 'type': 'exchange limit', // gemini allows limit orders only + }; + const response = await this.privatePostV1OrderNew (this.extend (request, params)); + return { + 'info': response, + 'id': response['order_id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + return await this.privatePostV1OrderCancel (this.extend (request, params)); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit_trades'] = limit; + } + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.privatePostV1Mytrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + }; + const response = await this.privatePostV1WithdrawCurrency (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response, 'txHash'), + }; + } + + nonce () { + return this.milliseconds (); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit_transfers'] = limit; + } + if (since !== undefined) { + request['timestamp'] = since; + } + const response = await this.privatePostV1Transfers (this.extend (request, params)); + return this.parseTransactions (response); + } + + parseTransaction (transaction, currency = undefined) { + const timestamp = this.safeInteger (transaction, 'timestampms'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const address = this.safeString (transaction, 'destination'); + const type = this.safeStringLower (transaction, 'type'); + let status = 'pending'; + // When deposits show as Advanced or Complete they are available for trading. + if (transaction['status']) { + status = 'ok'; + } + let fee = undefined; + const feeAmount = this.safeFloat (transaction, 'feeAmount'); + if (feeAmount !== undefined) { + fee = { + 'cost': feeAmount, + 'currency': code, + }; + } + return { + 'info': transaction, + 'id': this.safeString (transaction, 'eid'), + 'txid': this.safeString (transaction, 'txHash'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': undefined, // or is it defined? + 'type': type, // direction of the transaction, ('deposit' | 'withdraw') + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = this.extend ({ + 'request': url, + 'nonce': nonce, + }, query); + let payload = this.json (request); + payload = this.stringToBase64 (this.encode (payload)); + const signature = this.hmac (payload, this.encode (this.secret), 'sha384'); + headers = { + 'Content-Type': 'text/plain', + 'X-GEMINI-APIKEY': this.apiKey, + 'X-GEMINI-PAYLOAD': this.decode (payload), + 'X-GEMINI-SIGNATURE': signature, + }; + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + url = this.urls['api'][api] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + if (typeof body === 'string') { + const feedback = this.id + ' ' + body; + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + } + return; // fallback to default error handler + } + // + // { + // "result": "error", + // "reason": "BadNonce", + // "message": "Out-of-sequence nonce <1234> precedes previously used nonce <2345>" + // } + // + const result = this.safeString (response, 'result'); + if (result === 'error') { + const reason = this.safeString (response, 'reason'); + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions['exact'], reason, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostV1DepositCurrencyNewAddress (this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'timeframe': this.timeframes[timeframe], + 'symbol': market['id'], + }; + const response = await this.publicGetV2CandlesSymbolTimeframe (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],91:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, InsufficientFunds, OrderNotFound, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class hitbtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'hitbtc', + 'name': 'HitBTC', + 'countries': [ 'HK' ], + 'rateLimit': 1500, + 'version': '1', + 'has': { + 'CORS': false, + 'fetchTrades': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrderTrades': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg', + 'api': 'https://api.hitbtc.com', + 'www': 'https://hitbtc.com', + 'referral': 'https://hitbtc.com/?ref_id=5a5d39a65d466', + 'doc': 'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv1.md', + 'fees': [ + 'https://hitbtc.com/fees-and-limits', + 'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits', + ], + }, + 'api': { + 'public': { + 'get': [ + '{symbol}/orderbook', + '{symbol}/ticker', + '{symbol}/trades', + '{symbol}/trades/recent', + 'symbols', + 'ticker', + 'time', + ], + }, + 'trading': { + 'get': [ + 'balance', + 'orders/active', + 'orders/recent', + 'order', + 'trades/by/order', + 'trades', + ], + 'post': [ + 'new_order', + 'cancel_order', + 'cancel_orders', + ], + }, + 'payment': { + 'get': [ + 'balance', + 'address/{currency}', + 'transactions', + 'transactions/{transaction}', + ], + 'post': [ + 'transfer_to_trading', + 'transfer_to_main', + 'address/{currency}', + 'payout', + ], + }, + }, + // hardcoded fees are deprecated and should only be used when there's no other way to get fee info + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.07 / 100, + 'taker': 0.07 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'BCC': 0.0018, + 'ETH': 0.00215, + 'BCH': 0.0018, + 'USDT': 100, + 'DASH': 0.03, + 'BTG': 0.0005, + 'LTC': 0.003, + 'ZEC': 0.0001, + 'XMR': 0.09, + '1ST': 0.84, + 'ADX': 5.7, + 'AE': 6.7, + 'AEON': 0.01006, + 'AIR': 565, + 'AMP': 9, + 'ANT': 6.7, + 'ARDR': 1, + 'ARN': 18.5, + 'ART': 26, + 'ATB': 0.0004, + 'ATL': 27, + 'ATM': 504, + 'ATS': 860, + 'AVT': 1.9, + 'BAS': 113, + 'BCN': 0.1, + 'DAO.Casino': 124, // id = 'BET' + 'BKB': 46, + 'BMC': 32, + 'BMT': 100, + 'BNT': 2.57, + 'BQX': 4.7, + 'BTM': 40, + 'BTX': 0.04, + 'BUS': 0.004, + 'CCT': 115, + 'CDT': 100, + 'CDX': 30, + 'CFI': 61, + 'CLD': 0.88, + 'CND': 574, + 'CNX': 0.04, + 'COSS': 65, + 'CSNO': 16, + 'CTR': 15, + 'CTX': 146, + 'CVC': 8.46, + 'DBIX': 0.0168, + 'DCN': 120000, + 'DCT': 0.02, + 'DDF': 342, + 'DENT': 6240, + 'DGB': 0.4, + 'DGD': 0.01, + 'DICE': 0.32, + 'DLT': 0.26, + 'DNT': 0.21, + 'DOGE': 2, + 'DOV': 34, + 'DRPU': 24, + 'DRT': 240, + 'DSH': 0.017, + 'EBET': 84, + 'EBTC': 20, + 'EBTCOLD': 6.6, + 'ECAT': 14, + 'EDG': 2, + 'EDO': 2.9, + 'ELE': 0.00172, + 'ELM': 0.004, + 'EMC': 0.03, + 'EMGO': 14, + 'ENJ': 163, + 'EOS': 1.5, + 'ERO': 34, + 'ETBS': 15, + 'ETC': 0.002, + 'ETP': 0.004, + 'EVX': 5.4, + 'EXN': 456, + 'FRD': 65, + 'FUEL': 123.00105, + 'FUN': 202.9598309, + 'FYN': 1.849, + 'FYP': 66.13, + 'GNO': 0.0034, + 'GUP': 4, + 'GVT': 1.2, + 'HAC': 144, + 'HDG': 7, + 'HGT': 1082, + 'HPC': 0.4, + 'HVN': 120, + 'ICN': 0.55, + 'ICO': 34, + 'ICOS': 0.35, + 'IND': 76, + 'INDI': 5913, + 'ITS': 15.0012, + 'IXT': 11, + 'KBR': 143, + 'KICK': 112, + 'LA': 41, + 'LAT': 1.44, + 'LIFE': 13000, + 'LRC': 27, + 'LSK': 0.3, + 'LUN': 0.34, + 'MAID': 5, + 'MANA': 143, + 'MCAP': 5.44, + 'MIPS': 43, + 'MNE': 1.33, + 'MSP': 121, + 'MTH': 92, + 'MYB': 3.9, + 'NDC': 165, + 'NEBL': 0.04, + 'NET': 3.96, + 'NTO': 998, + 'NXC': 13.39, + 'NXT': 3, + 'OAX': 15, + 'ODN': 0.004, + 'OMG': 2, + 'OPT': 335, + 'ORME': 2.8, + 'OTN': 0.57, + 'PAY': 3.1, + 'PIX': 96, + 'PLBT': 0.33, + 'PLR': 114, + 'PLU': 0.87, + 'POE': 784, + 'POLL': 3.5, + 'PPT': 2, + 'PRE': 32, + 'PRG': 39, + 'PRO': 41, + 'PRS': 60, + 'PTOY': 0.5, + 'QAU': 63, + 'QCN': 0.03, + 'QTUM': 0.04, + 'QVT': 64, + 'REP': 0.02, + 'RKC': 15, + 'RVT': 14, + 'SAN': 2.24, + 'SBD': 0.03, + 'SCL': 2.6, + 'SISA': 1640, + 'SKIN': 407, + 'SMART': 0.4, + 'SMS': 0.0375, + 'SNC': 36, + 'SNGLS': 4, + 'SNM': 48, + 'SNT': 233, + 'STEEM': 0.01, + 'STRAT': 0.01, + 'STU': 14, + 'STX': 11, + 'SUB': 17, + 'SUR': 3, + 'SWT': 0.51, + 'TAAS': 0.91, + 'TBT': 2.37, + 'TFL': 15, + 'TIME': 0.03, + 'TIX': 7.1, + 'TKN': 1, + 'TKR': 84, + 'TNT': 90, + 'TRST': 1.6, + 'TRX': 1395, + 'UET': 480, + 'UGT': 15, + 'VEN': 14, + 'VERI': 0.037, + 'VIB': 50, + 'VIBE': 145, + 'VOISE': 618, + 'WEALTH': 0.0168, + 'WINGS': 2.4, + 'WTC': 0.75, + 'XAUR': 3.23, + 'XDN': 0.01, + 'XEM': 15, + 'XUC': 0.9, + 'YOYOW': 140, + 'ZAP': 24, + 'ZRX': 23, + 'ZSC': 191, + }, + 'deposit': { + 'BTC': 0.0006, + 'ETH': 0.003, + 'BCH': 0, + 'USDT': 0, + 'BTG': 0, + 'LTC': 0, + 'ZEC': 0, + 'XMR': 0, + '1ST': 0, + 'ADX': 0, + 'AE': 0, + 'AEON': 0, + 'AIR': 0, + 'AMP': 0, + 'ANT': 0, + 'ARDR': 0, + 'ARN': 0, + 'ART': 0, + 'ATB': 0, + 'ATL': 0, + 'ATM': 0, + 'ATS': 0, + 'AVT': 0, + 'BAS': 0, + 'BCN': 0, + 'DAO.Casino': 0, // id = 'BET' + 'BKB': 0, + 'BMC': 0, + 'BMT': 0, + 'BNT': 0, + 'BQX': 0, + 'BTM': 0, + 'BTX': 0, + 'BUS': 0, + 'CCT': 0, + 'CDT': 0, + 'CDX': 0, + 'CFI': 0, + 'CLD': 0, + 'CND': 0, + 'CNX': 0, + 'COSS': 0, + 'CSNO': 0, + 'CTR': 0, + 'CTX': 0, + 'CVC': 0, + 'DBIX': 0, + 'DCN': 0, + 'DCT': 0, + 'DDF': 0, + 'DENT': 0, + 'DGB': 0, + 'DGD': 0, + 'DICE': 0, + 'DLT': 0, + 'DNT': 0, + 'DOGE': 0, + 'DOV': 0, + 'DRPU': 0, + 'DRT': 0, + 'DSH': 0, + 'EBET': 0, + 'EBTC': 0, + 'EBTCOLD': 0, + 'ECAT': 0, + 'EDG': 0, + 'EDO': 0, + 'ELE': 0, + 'ELM': 0, + 'EMC': 0, + 'EMGO': 0, + 'ENJ': 0, + 'EOS': 0, + 'ERO': 0, + 'ETBS': 0, + 'ETC': 0, + 'ETP': 0, + 'EVX': 0, + 'EXN': 0, + 'FRD': 0, + 'FUEL': 0, + 'FUN': 0, + 'FYN': 0, + 'FYP': 0, + 'GNO': 0, + 'GUP': 0, + 'GVT': 0, + 'HAC': 0, + 'HDG': 0, + 'HGT': 0, + 'HPC': 0, + 'HVN': 0, + 'ICN': 0, + 'ICO': 0, + 'ICOS': 0, + 'IND': 0, + 'INDI': 0, + 'ITS': 0, + 'IXT': 0, + 'KBR': 0, + 'KICK': 0, + 'LA': 0, + 'LAT': 0, + 'LIFE': 0, + 'LRC': 0, + 'LSK': 0, + 'LUN': 0, + 'MAID': 0, + 'MANA': 0, + 'MCAP': 0, + 'MIPS': 0, + 'MNE': 0, + 'MSP': 0, + 'MTH': 0, + 'MYB': 0, + 'NDC': 0, + 'NEBL': 0, + 'NET': 0, + 'NTO': 0, + 'NXC': 0, + 'NXT': 0, + 'OAX': 0, + 'ODN': 0, + 'OMG': 0, + 'OPT': 0, + 'ORME': 0, + 'OTN': 0, + 'PAY': 0, + 'PIX': 0, + 'PLBT': 0, + 'PLR': 0, + 'PLU': 0, + 'POE': 0, + 'POLL': 0, + 'PPT': 0, + 'PRE': 0, + 'PRG': 0, + 'PRO': 0, + 'PRS': 0, + 'PTOY': 0, + 'QAU': 0, + 'QCN': 0, + 'QTUM': 0, + 'QVT': 0, + 'REP': 0, + 'RKC': 0, + 'RVT': 0, + 'SAN': 0, + 'SBD': 0, + 'SCL': 0, + 'SISA': 0, + 'SKIN': 0, + 'SMART': 0, + 'SMS': 0, + 'SNC': 0, + 'SNGLS': 0, + 'SNM': 0, + 'SNT': 0, + 'STEEM': 0, + 'STRAT': 0, + 'STU': 0, + 'STX': 0, + 'SUB': 0, + 'SUR': 0, + 'SWT': 0, + 'TAAS': 0, + 'TBT': 0, + 'TFL': 0, + 'TIME': 0, + 'TIX': 0, + 'TKN': 0, + 'TKR': 0, + 'TNT': 0, + 'TRST': 0, + 'TRX': 0, + 'UET': 0, + 'UGT': 0, + 'VEN': 0, + 'VERI': 0, + 'VIB': 0, + 'VIBE': 0, + 'VOISE': 0, + 'WEALTH': 0, + 'WINGS': 0, + 'WTC': 0, + 'XAUR': 0, + 'XDN': 0, + 'XEM': 0, + 'XUC': 0, + 'YOYOW': 0, + 'ZAP': 0, + 'ZRX': 0, + 'ZSC': 0, + }, + }, + }, + 'commonCurrencies': { + 'BET': 'DAO.Casino', + 'CAT': 'BitClave', + 'CPT': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 + 'DRK': 'DASH', + 'EMGO': 'MGO', + 'GET': 'Themis', + 'HSR': 'HC', + 'LNC': 'LinkerCoin', + 'UNC': 'Unigame', + 'USD': 'USDT', + 'XBT': 'BTC', + }, + 'exceptions': { + 'exact': { + '2001': BadSymbol, // {"error":{"code":2001,"message":"Symbol not found","description":"Try get /api/2/public/symbol, to get list of all available symbols."}} + }, + 'broad': { + }, + }, + 'options': { + 'defaultTimeInForce': 'FOK', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetSymbols (params); + const markets = this.safeValue (response, 'symbols'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'commodity'); + const quoteId = this.safeString (market, 'currency'); + const lot = this.safeFloat (market, 'lot'); + const step = this.safeFloat (market, 'step'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'lot': lot, + 'step': step, + 'active': true, + 'maker': this.safeFloat (market, 'provideLiquidityRate'), + 'taker': this.safeFloat (market, 'takeLiquidityRate'), + 'precision': { + 'amount': this.precisionFromString (market['lot']), + 'price': this.precisionFromString (market['step']), + }, + 'limits': { + 'amount': { + 'min': lot, + 'max': undefined, + }, + 'price': { + 'min': step, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + let method = this.safeString (params, 'type', 'trading'); + method += 'GetBalance'; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + const balances = this.safeValue (response, 'balance', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat2 (balance, 'cash', 'balance'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const orderbook = await this.publicGetSymbolOrderbook (this.extend ({ + 'symbol': this.marketId (symbol), + }, params)); + return this.parseOrderBook (orderbook); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'volume_quote'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTicker (params); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetSymbolTicker (this.extend (request, params)); + if ('message' in response) { + throw new ExchangeError (this.id + ' ' + response['message']); + } + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + if (Array.isArray (trade)) { + return this.parsePublicTrade (trade, market); + } + return this.parseOrderTrade (trade, market); + } + + parsePublicTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = undefined; + const tradeLength = trade.length; + if (tradeLength > 4) { + side = trade[4]; + } + const price = parseFloat (trade[1]); + const amount = parseFloat (trade[2]); + const cost = price * amount; + return { + 'info': trade, + 'id': trade[0].toString (), + 'timestamp': trade[3], + 'datetime': this.iso8601 (trade[3]), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + parseOrderTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let amount = this.safeFloat (trade, 'execQuantity'); + if (market) { + amount *= market['lot']; + } + const price = this.safeFloat (trade, 'execPrice'); + const cost = price * amount; + const fee = { + 'cost': this.safeFloat (trade, 'fee'), + 'currency': undefined, + 'rate': undefined, + }; + const timestamp = this.safeInteger (trade, 'timestamp'); + const id = this.safeString (trade, 'tradeId'); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const orderId = this.safeString (trade, 'clientOrderId'); + const side = this.safeString (trade, 'side'); + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + // 'from': 0, + // 'till': 100, + // 'by': 'ts', // or by trade_id + // 'sort': 'desc', // or asc + // 'start_index': 0, + // 'max_results': 1000, + // 'format_item': 'object', + // 'format_price': 'number', + // 'format_amount': 'number', + // 'format_tid': 'string', + // 'format_timestamp': 'millisecond', + // 'format_wrap': false, + // 'side': 'true', + }; + if (since !== undefined) { + request['by'] = 'ts'; + request['from'] = since; + } + if (limit !== undefined) { + request['max_results'] = limit; + } + const response = await this.publicGetSymbolTrades (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // check if amount can be evenly divided into lots + // they want integer quantity in lot units + const quantity = parseFloat (amount) / market['lot']; + const wholeLots = Math.round (quantity); + const difference = quantity - wholeLots; + if (Math.abs (difference) > market['step']) { + throw new ExchangeError (this.id + ' order amount should be evenly divisible by lot unit size of ' + market['lot'].toString ()); + } + const clientOrderId = this.milliseconds (); + const request = { + 'clientOrderId': clientOrderId.toString (), + 'symbol': market['id'], + 'side': side, + 'quantity': wholeLots.toString (), // quantity in integer lot units + 'type': type, + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } else { + request['timeInForce'] = this.options['defaultTimeInForce']; + } + const response = await this.tradingPostNewOrder (this.extend (request, params)); + const order = this.parseOrder (response['ExecutionReport'], market); + if (order['status'] === 'rejected') { + throw new InvalidOrder (this.id + ' order was rejected by the exchange ' + this.json (order)); + } + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + return await this.tradingPostCancelOrder (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'new': 'open', + 'partiallyFilled': 'open', + 'filled': 'closed', + 'canceled': 'canceled', + 'rejected': 'rejected', + 'expired': 'expired', + }; + return this.safeString (statuses, status); + } + + parseOrder (order, market = undefined) { + let timestamp = this.safeInteger (order, 'lastTimestamp'); + if (timestamp === undefined) { + timestamp = this.safeInteger (order, 'timestamp'); + } + let symbol = undefined; + if (market === undefined) { + market = this.markets_by_id[order['symbol']]; + } + const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); + let price = this.safeFloat (order, 'orderPrice'); + price = this.safeFloat (order, 'price', price); + price = this.safeFloat (order, 'avgPrice', price); + let amount = this.safeFloat (order, 'orderQuantity'); + amount = this.safeFloat (order, 'quantity', amount); + let remaining = this.safeFloat (order, 'quantityLeaves'); + remaining = this.safeFloat (order, 'leavesQuantity', remaining); + let filled = undefined; + let cost = undefined; + const amountDefined = (amount !== undefined); + const remainingDefined = (remaining !== undefined); + if (market !== undefined) { + symbol = market['symbol']; + if (amountDefined) { + amount *= market['lot']; + } + if (remainingDefined) { + remaining *= market['lot']; + } + } else { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (amountDefined) { + if (remainingDefined) { + filled = amount - remaining; + if (price !== undefined) { + cost = price * filled; + } + } + } + const feeCost = this.safeFloat (order, 'fee'); + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': undefined, + }; + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const id = this.safeString (order, 'clientOrderId'); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.tradingGetOrder (this.extend (request, params)); + if (response['orders'][0]) { + return this.parseOrder (response['orders'][0]); + } + throw new OrderNotFound (this.id + ' fetchOrder() error: ' + this.response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const statuses = [ 'new', 'partiallyFiiled' ]; + let market = undefined; + const request = { + 'sort': 'desc', + 'statuses': statuses.join (','), + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbols'] = market['id']; + } + const response = await this.tradingGetOrdersActive (this.extend (request, params)); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const statuses = [ 'filled', 'canceled', 'rejected', 'expired' ]; + const request = { + 'sort': 'desc', + 'statuses': statuses.join (','), + 'max_results': 1000, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbols'] = market['id']; + } + const response = await this.tradingGetOrdersRecent (this.extend (request, params)); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.tradingGetTradesByOrder (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency_code': currency['id'], + 'amount': amount, + 'address': address, + }; + if (tag !== undefined) { + request['extra_id'] = tag; + } + const response = await this.paymentPostPayout (this.extend (request, params)); + return { + 'info': response, + 'id': response['transaction'], + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + 'api' + '/' + this.version + '/' + api + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const payload = { 'nonce': nonce, 'apikey': this.apiKey }; + query = this.extend (payload, query); + if (method === 'GET') { + url += '?' + this.urlencode (query); + } else { + url += '?' + this.urlencode (payload); + } + let auth = url; + if (method === 'POST') { + if (Object.keys (query).length) { + body = this.urlencode (query); + auth += body; + } + } + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Signature': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512').toLowerCase (), + }; + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('code' in response) { + if ('ExecutionReport' in response) { + if (response['ExecutionReport']['orderRejectReason'] === 'orderExceedsLimit') { + throw new InsufficientFunds (this.id + ' ' + this.json (response)); + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // fallback to default error handler + } + const error = this.safeValue (response, 'error'); + if (error) { + const code = this.safeValue (error, 'code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown error + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],92:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const hitbtc = require ('./hitbtc'); +const { BadSymbol, PermissionDenied, ExchangeError, ExchangeNotAvailable, OrderNotFound, InsufficientFunds, InvalidOrder, RequestTimeout, AuthenticationError } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); +// --------------------------------------------------------------------------- + +module.exports = class hitbtc2 extends hitbtc { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'hitbtc2', + 'name': 'HitBTC', + 'countries': [ 'HK' ], + 'rateLimit': 1500, + 'version': '2', + 'has': { + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'CORS': true, + 'editOrder': true, + 'fetchCurrencies': true, + 'fetchOHLCV': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchOrderTrades': false, // not implemented yet + 'fetchDeposits': false, + 'fetchWithdrawals': false, + 'fetchTransactions': true, + 'fetchTradingFee': true, + }, + 'timeframes': { + '1m': 'M1', + '3m': 'M3', + '5m': 'M5', + '15m': 'M15', + '30m': 'M30', // default + '1h': 'H1', + '4h': 'H4', + '1d': 'D1', + '1w': 'D7', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg', + 'api': 'https://api.hitbtc.com', + 'www': 'https://hitbtc.com', + 'referral': 'https://hitbtc.com/?ref_id=5a5d39a65d466', + 'doc': [ + 'https://api.hitbtc.com', + 'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv2.md', + ], + 'fees': [ + 'https://hitbtc.com/fees-and-limits', + 'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits', + ], + }, + 'api': { + 'public': { + 'get': [ + 'symbol', // Available Currency Symbols + 'symbol/{symbol}', // Get symbol info + 'currency', // Available Currencies + 'currency/{currency}', // Get currency info + 'ticker', // Ticker list for all symbols + 'ticker/{symbol}', // Ticker for symbol + 'trades/{symbol}', // Trades + 'orderbook/{symbol}', // Orderbook + 'candles/{symbol}', // Candles + ], + }, + 'private': { + 'get': [ + 'order', // List your current open orders + 'order/{clientOrderId}', // Get a single order by clientOrderId + 'trading/balance', // Get trading balance + 'trading/fee/all', // Get trading fee rate + 'trading/fee/{symbol}', // Get trading fee rate + 'history/trades', // Get historical trades + 'history/order', // Get historical orders + 'history/order/{id}/trades', // Get historical trades by specified order + 'account/balance', // Get main acccount balance + 'account/transactions', // Get account transactions + 'account/transactions/{id}', // Get account transaction by id + 'account/crypto/address/{currency}', // Get deposit crypro address + ], + 'post': [ + 'order', // Create new order + 'account/crypto/withdraw', // Withdraw crypro + 'account/crypto/address/{currency}', // Create new deposit crypro address + 'account/transfer', // Transfer amount to trading + ], + 'put': [ + 'order/{clientOrderId}', // Create new order + 'account/crypto/withdraw/{id}', // Commit withdraw crypro + ], + 'delete': [ + 'order', // Cancel all open orders + 'order/{clientOrderId}', // Cancel order + 'account/crypto/withdraw/{id}', // Rollback withdraw crypro + ], + 'patch': [ + 'order/{clientOrderId}', // Cancel Replace order + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'BCC': 0.0018, + 'ETH': 0.00958, + 'BCH': 0.0018, + 'USDT': 100, + 'DASH': 0.03, + 'BTG': 0.0005, + 'XRP': 0.509, + 'LTC': 0.003, + 'ZEC': 0.0001, + 'XMR': 0.09, + '1ST': 0.84, + 'ADX': 5.7, + 'AE': 6.7, + 'AEON': 0.01006, + 'AIR': 565, + 'AMM': 14, + 'AMP': 342, + 'ANT': 6.7, + 'ARDR': 1, + 'ARN': 18.5, + 'ART': 26, + 'ATB': 0.0004, + 'ATL': 27, + 'ATM': 504, + 'ATS': 860, + 'AVT': 1.9, + 'BAS': 113, + 'BCN': 0.1, + 'BET': 124, + 'BKB': 46, + 'BMC': 32, + 'BMT': 100, + 'BNT': 2.57, + 'BQX': 4.7, + 'BTCA': 351.21, + 'BTM': 40, + 'BTX': 0.04, + 'BUS': 0.004, + 'CAPP': 97, + 'CCT': 6, + 'CDT': 100, + 'CDX': 30, + 'CFI': 61, + 'CL': 13.85, + 'CLD': 0.88, + 'CND': 574, + 'CNX': 0.04, + 'COSS': 65, + 'CPAY': 5.487, + 'CSNO': 16, + 'CTR': 15, + 'CTX': 146, + 'CVC': 8.46, + 'DATA': 12.949, + 'DBIX': 0.0168, + 'DCN': 1280, + 'DCT': 0.02, + 'DDF': 342, + 'DENT': 1000, + 'DGB': 0.4, + 'DGD': 0.01, + 'DICE': 0.32, + 'DLT': 0.26, + 'DNT': 0.21, + 'DOGE': 2, + 'DOV': 34, + 'DRPU': 24, + 'DRT': 240, + 'DSH': 0.017, + 'EBET': 84, + 'EBTC': 20, + 'EBTCOLD': 6.6, + 'ECAT': 14, + 'EDG': 2, + 'EDO': 2.9, + 'EKO': 1136.36, + 'ELE': 0.00172, + 'ELM': 0.004, + 'EMC': 0.03, + 'MGO': 14, + 'ENJ': 163, + 'EOS': 1.5, + 'ERO': 34, + 'ETBS': 15, + 'ETC': 0.002, + 'ETP': 0.004, + 'EVX': 5.4, + 'EXN': 456, + 'FCN': 0.000005, + 'FRD': 65, + 'FUEL': 123.00105, + 'FUN': 202.9598309, + 'FYN': 1.849, + 'FYP': 66.13, + 'GAME': 0.004, + 'GNO': 0.0034, + 'GUP': 4, + 'GVT': 1.2, + 'HSR': 0.04, + 'HAC': 144, + 'HDG': 7, + 'HGT': 1082, + 'HPC': 0.4, + 'HVN': 120, + 'ICN': 0.55, + 'ICO': 34, + 'ICOS': 0.35, + 'IND': 76, + 'INDI': 790, + 'ITS': 15.0012, + 'IXT': 11, + 'KBR': 143, + 'KICK': 112, + 'KMD': 4, + 'LA': 41, + 'LEND': 388, + 'LAT': 1.44, + 'LIFE': 13000, + 'LRC': 27, + 'LSK': 0.3, + 'LOC': 11.076, + 'LUN': 0.34, + 'MAID': 5, + 'MANA': 143, + 'MCAP': 5.44, + 'MIPS': 43, + 'MNE': 1.33, + 'MSP': 121, + 'MCO': 0.357, + 'MTH': 92, + 'MYB': 3.9, + 'NDC': 165, + 'NEBL': 0.04, + 'NET': 3.96, + 'NTO': 998, + 'NGC': 2.368, + 'NXC': 13.39, + 'NXT': 3, + 'OAX': 15, + 'ODN': 0.004, + 'OMG': 2, + 'OPT': 335, + 'ORME': 2.8, + 'OTN': 0.57, + 'PAY': 3.1, + 'PIX': 96, + 'PLBT': 0.33, + 'PLR': 114, + 'PLU': 0.87, + 'POE': 784, + 'POLL': 3.5, + 'PPT': 2, + 'PRE': 32, + 'PRG': 39, + 'PRO': 41, + 'PRS': 60, + 'PTOY': 0.5, + 'QAU': 63, + 'QCN': 0.03, + 'QTUM': 0.04, + 'QVT': 64, + 'REP': 0.02, + 'RKC': 15, + 'RLC': 1.21, + 'RVT': 14, + 'SC': 30, + 'SAN': 2.24, + 'SBD': 0.03, + 'SCL': 2.6, + 'SISA': 1640, + 'SKIN': 407, + 'SWFTC': 352.94, + 'SMART': 0.4, + 'SMS': 0.0375, + 'SNC': 36, + 'SNGLS': 4, + 'SNM': 48, + 'SNT': 233, + 'STAR': 0.144, + 'STORM': 153.19, + 'STEEM': 0.01, + 'STRAT': 0.01, + 'SPF': 14.4, + 'STU': 14, + 'STX': 11, + 'SUB': 17, + 'SUR': 3, + 'SWT': 0.51, + 'TAAS': 0.91, + 'TBT': 2.37, + 'TFL': 15, + 'TIME': 0.03, + 'TIX': 7.1, + 'TKN': 1, + 'TGT': 173, + 'TKR': 84, + 'TNT': 90, + 'TRST': 1.6, + 'TRX': 270, + 'UET': 480, + 'UGT': 15, + 'UTT': 3, + 'VEN': 14, + 'VERI': 0.037, + 'VIB': 50, + 'VIBE': 145, + 'VOISE': 618, + 'WEALTH': 0.0168, + 'WINGS': 2.4, + 'WTC': 0.75, + 'WRC': 48, + 'XAUR': 3.23, + 'XDN': 0.01, + 'XEM': 15, + 'XUC': 0.9, + 'YOYOW': 140, + 'ZAP': 24, + 'ZRX': 23, + 'ZSC': 191, + }, + 'deposit': { + 'BTC': 0, + 'ETH': 0, + 'BCH': 0, + 'USDT': 0, + 'BTG': 0, + 'LTC': 0, + 'ZEC': 0, + 'XMR': 0, + '1ST': 0, + 'ADX': 0, + 'AE': 0, + 'AEON': 0, + 'AIR': 0, + 'AMP': 0, + 'ANT': 0, + 'ARDR': 0, + 'ARN': 0, + 'ART': 0, + 'ATB': 0, + 'ATL': 0, + 'ATM': 0, + 'ATS': 0, + 'AVT': 0, + 'BAS': 0, + 'BCN': 0, + 'BET': 0, + 'BKB': 0, + 'BMC': 0, + 'BMT': 0, + 'BNT': 0, + 'BQX': 0, + 'BTM': 0, + 'BTX': 0, + 'BUS': 0, + 'CCT': 0, + 'CDT': 0, + 'CDX': 0, + 'CFI': 0, + 'CLD': 0, + 'CND': 0, + 'CNX': 0, + 'COSS': 0, + 'CSNO': 0, + 'CTR': 0, + 'CTX': 0, + 'CVC': 0, + 'DBIX': 0, + 'DCN': 0, + 'DCT': 0, + 'DDF': 0, + 'DENT': 0, + 'DGB': 0, + 'DGD': 0, + 'DICE': 0, + 'DLT': 0, + 'DNT': 0, + 'DOGE': 0, + 'DOV': 0, + 'DRPU': 0, + 'DRT': 0, + 'DSH': 0, + 'EBET': 0, + 'EBTC': 0, + 'EBTCOLD': 0, + 'ECAT': 0, + 'EDG': 0, + 'EDO': 0, + 'ELE': 0, + 'ELM': 0, + 'EMC': 0, + 'EMGO': 0, + 'ENJ': 0, + 'EOS': 0, + 'ERO': 0, + 'ETBS': 0, + 'ETC': 0, + 'ETP': 0, + 'EVX': 0, + 'EXN': 0, + 'FRD': 0, + 'FUEL': 0, + 'FUN': 0, + 'FYN': 0, + 'FYP': 0, + 'GNO': 0, + 'GUP': 0, + 'GVT': 0, + 'HAC': 0, + 'HDG': 0, + 'HGT': 0, + 'HPC': 0, + 'HVN': 0, + 'ICN': 0, + 'ICO': 0, + 'ICOS': 0, + 'IND': 0, + 'INDI': 0, + 'ITS': 0, + 'IXT': 0, + 'KBR': 0, + 'KICK': 0, + 'LA': 0, + 'LAT': 0, + 'LIFE': 0, + 'LRC': 0, + 'LSK': 0, + 'LUN': 0, + 'MAID': 0, + 'MANA': 0, + 'MCAP': 0, + 'MIPS': 0, + 'MNE': 0, + 'MSP': 0, + 'MTH': 0, + 'MYB': 0, + 'NDC': 0, + 'NEBL': 0, + 'NET': 0, + 'NTO': 0, + 'NXC': 0, + 'NXT': 0, + 'OAX': 0, + 'ODN': 0, + 'OMG': 0, + 'OPT': 0, + 'ORME': 0, + 'OTN': 0, + 'PAY': 0, + 'PIX': 0, + 'PLBT': 0, + 'PLR': 0, + 'PLU': 0, + 'POE': 0, + 'POLL': 0, + 'PPT': 0, + 'PRE': 0, + 'PRG': 0, + 'PRO': 0, + 'PRS': 0, + 'PTOY': 0, + 'QAU': 0, + 'QCN': 0, + 'QTUM': 0, + 'QVT': 0, + 'REP': 0, + 'RKC': 0, + 'RVT': 0, + 'SAN': 0, + 'SBD': 0, + 'SCL': 0, + 'SISA': 0, + 'SKIN': 0, + 'SMART': 0, + 'SMS': 0, + 'SNC': 0, + 'SNGLS': 0, + 'SNM': 0, + 'SNT': 0, + 'STEEM': 0, + 'STRAT': 0, + 'STU': 0, + 'STX': 0, + 'SUB': 0, + 'SUR': 0, + 'SWT': 0, + 'TAAS': 0, + 'TBT': 0, + 'TFL': 0, + 'TIME': 0, + 'TIX': 0, + 'TKN': 0, + 'TKR': 0, + 'TNT': 0, + 'TRST': 0, + 'TRX': 0, + 'UET': 0, + 'UGT': 0, + 'VEN': 0, + 'VERI': 0, + 'VIB': 0, + 'VIBE': 0, + 'VOISE': 0, + 'WEALTH': 0, + 'WINGS': 0, + 'WTC': 0, + 'XAUR': 0, + 'XDN': 0, + 'XEM': 0, + 'XUC': 0, + 'YOYOW': 0, + 'ZAP': 0, + 'ZRX': 0, + 'ZSC': 0, + }, + }, + }, + 'options': { + 'defaultTimeInForce': 'FOK', + }, + 'exceptions': { + '504': RequestTimeout, // {"error":{"code":504,"message":"Gateway Timeout"}} + '1002': AuthenticationError, // {"error":{"code":1002,"message":"Authorization failed","description":""}} + '1003': PermissionDenied, // "Action is forbidden for this API key" + '2010': InvalidOrder, // "Quantity not a valid number" + '2001': BadSymbol, // "Symbol not found" + '2011': InvalidOrder, // "Quantity too low" + '2020': InvalidOrder, // "Price not a valid number" + '20002': OrderNotFound, // canceling non-existent order + '20001': InsufficientFunds, + }, + }); + } + + feeToPrecision (symbol, fee) { + return this.decimalToPrecision (fee, TRUNCATE, 8, DECIMAL_PLACES); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetSymbol (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const lot = this.safeFloat (market, 'quantityIncrement'); + const step = this.safeFloat (market, 'tickSize'); + const precision = { + 'price': this.precisionFromString (market['tickSize']), + // FIXME: for lots > 1 the following line returns 0 + // 'amount': this.precisionFromString (market['quantityIncrement']), + 'amount': -1 * parseInt (Math.log10 (lot)), + }; + const taker = this.safeFloat (market, 'takeLiquidityRate'); + const maker = this.safeFloat (market, 'provideLiquidityRate'); + result.push (this.extend (this.fees['trading'], { + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'taker': taker, + 'maker': maker, + 'precision': precision, + 'limits': { + 'amount': { + 'min': lot, + 'max': undefined, + }, + 'price': { + 'min': step, + 'max': undefined, + }, + 'cost': { + 'min': lot * step, + 'max': undefined, + }, + }, + })); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrency (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'id'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const precision = 8; // default precision, todo: fix "magic constants" + const code = this.safeCurrencyCode (id); + const payin = this.safeValue (currency, 'payinEnabled'); + const payout = this.safeValue (currency, 'payoutEnabled'); + const transfer = this.safeValue (currency, 'transferEnabled'); + let active = payin && payout && transfer; + if ('disabled' in currency) { + if (currency['disabled']) { + active = false; + } + } + let type = 'fiat'; + if (('crypto' in currency) && currency['crypto']) { + type = 'crypto'; + } + const name = this.safeString (currency, 'fullName'); + result[code] = { + 'id': id, + 'code': code, + 'type': type, + 'payin': payin, + 'payout': payout, + 'transfer': transfer, + 'info': currency, + 'name': name, + 'active': active, + 'fee': this.safeFloat (currency, 'payoutFee'), // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchTradingFee (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.extend ({ + 'symbol': market['id'], + }, this.omit (params, 'symbol')); + const response = await this.privateGetTradingFeeSymbol (request); + // + // { + // takeLiquidityRate: '0.001', + // provideLiquidityRate: '-0.0001' + // } + // + return { + 'info': response, + 'maker': this.safeFloat (response, 'provideLiquidityRate'), + 'taker': this.safeFloat (response, 'takeLiquidityRate'), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const type = this.safeString (params, 'type', 'trading'); + const method = 'privateGet' + this.capitalize (type) + 'Balance'; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (ohlcv['timestamp']); + return [ + timestamp, + parseFloat (ohlcv['open']), + parseFloat (ohlcv['max']), + parseFloat (ohlcv['min']), + parseFloat (ohlcv['close']), + parseFloat (ohlcv['volume']), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'period': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetCandlesSymbol (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 100, 0 = unlimited + } + const response = await this.publicGetOrderbookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'size'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.parse8601 (ticker['timestamp']); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const baseVolume = this.safeFloat (ticker, 'volume'); + const quoteVolume = this.safeFloat (ticker, 'volumeQuote'); + const open = this.safeFloat (ticker, 'open'); + const last = this.safeFloat (ticker, 'last'); + let change = undefined; + let percentage = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + if (open > 0) { + percentage = change / open * 100; + } + } + let vwap = undefined; + if (quoteVolume !== undefined) { + if (baseVolume !== undefined) { + if (baseVolume > 0) { + vwap = quoteVolume / baseVolume; + } + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = response[i]; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } else { + result[marketId] = this.parseTicker (ticker); + } + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTickerSymbol (this.extend (request, params)); + if ('message' in response) { + throw new ExchangeError (this.id + ' ' + response['message']); + } + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + // + // createMarketOrder + // + // { fee: "0.0004644", + // id: 386394956, + // price: "0.4644", + // quantity: "1", + // timestamp: "2018-10-25T16:41:44.780Z" } + // + // fetchTrades ... + // + // fetchMyTrades ... + // + const timestamp = this.parse8601 (trade['timestamp']); + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrency = market ? market['quote'] : undefined; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const orderId = this.safeString (trade, 'clientOrderId'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + const cost = price * amount; + const side = this.safeString (trade, 'side'); + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + const response = await this.privateGetAccountTransactions (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // id: 'd53ee9df-89bf-4d09-886e-849f8be64647', + // index: 1044718371, + // type: 'payout', + // status: 'success', + // currency: 'ETH', + // amount: '4.522683200000000000000000', + // createdAt: '2018-06-07T00:43:32.426Z', + // updatedAt: '2018-06-07T00:45:36.447Z', + // hash: '0x973e5683dfdf80a1fb1e0b96e19085b6489221d2ddf864daa46903c5ec283a0f', + // address: '0xC5a59b21948C1d230c8C54f05590000Eb3e1252c', + // fee: '0.00958', + // }, + // { + // id: 'e6c63331-467e-4922-9edc-019e75d20ba3', + // index: 1044714672, + // type: 'exchangeToBank', + // status: 'success', + // currency: 'ETH', + // amount: '4.532263200000000000', + // createdAt: '2018-06-07T00:42:39.543Z', + // updatedAt: '2018-06-07T00:42:39.683Z', + // }, + // { + // id: '3b052faa-bf97-4636-a95c-3b5260015a10', + // index: 1009280164, + // type: 'bankToExchange', + // status: 'success', + // currency: 'CAS', + // amount: '104797.875800000000000000', + // createdAt: '2018-05-19T02:34:36.750Z', + // updatedAt: '2018-05-19T02:34:36.857Z', + // }, + // { + // id: 'd525249f-7498-4c81-ba7b-b6ae2037dc08', + // index: 1009279948, + // type: 'payin', + // status: 'success', + // currency: 'CAS', + // amount: '104797.875800000000000000', + // createdAt: '2018-05-19T02:30:16.698Z', + // updatedAt: '2018-05-19T02:34:28.159Z', + // hash: '0xa6530e1231de409cf1f282196ed66533b103eac1df2aa4a7739d56b02c5f0388', + // address: '0xd53ed559a6d963af7cb3f3fcd0e7ca499054db8b', + // } + // + // { + // "id": "4f351f4f-a8ee-4984-a468-189ed590ddbd", + // "index": 3112719565, + // "type": "withdraw", + // "status": "success", + // "currency": "BCHOLD", + // "amount": "0.02423133", + // "createdAt": "2019-07-16T16:52:04.494Z", + // "updatedAt": "2019-07-16T16:54:07.753Z" + // } + const id = this.safeString (transaction, 'id'); + const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt')); + const updated = this.parse8601 (this.safeString (transaction, 'updatedAt')); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const amount = this.safeFloat (transaction, 'amount'); + const address = this.safeString (transaction, 'address'); + const txid = this.safeString (transaction, 'hash'); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': code, + }; + } + const type = this.parseTransactionType (this.safeString (transaction, 'type')); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'pending': 'pending', + 'failed': 'failed', + 'success': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransactionType (type) { + const types = { + 'payin': 'deposit', + 'payout': 'withdrawal', + 'withdraw': 'withdrawal', + }; + return this.safeString (types, type, type); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['sort'] = 'ASC'; + request['from'] = this.iso8601 (since); + } + const response = await this.publicGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + // their max accepted length is 32 characters + const uuid = this.uuid (); + const parts = uuid.split ('-'); + let clientOrderId = parts.join (''); + clientOrderId = clientOrderId.slice (0, 32); + amount = parseFloat (amount); + const request = { + 'clientOrderId': clientOrderId, + 'symbol': market['id'], + 'side': side, + 'quantity': this.amountToPrecision (symbol, amount), + 'type': type, + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } else { + request['timeInForce'] = this.options['defaultTimeInForce']; + } + const response = await this.privatePostOrder (this.extend (request, params)); + const order = this.parseOrder (response); + if (order['status'] === 'rejected') { + throw new InvalidOrder (this.id + ' order was rejected by the exchange ' + this.json (order)); + } + const id = order['id']; + this.orders[id] = order; + return order; + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + // their max accepted length is 32 characters + const uuid = this.uuid (); + const parts = uuid.split ('-'); + let requestClientId = parts.join (''); + requestClientId = requestClientId.slice (0, 32); + const request = { + 'clientOrderId': id, + 'requestClientId': requestClientId, + }; + if (amount !== undefined) { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + if (price !== undefined) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePatchOrderClientOrderId (this.extend (request, params)); + const order = this.parseOrder (response); + this.orders[order['id']] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.privateDeleteOrderClientOrderId (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOrderStatus (status) { + const statuses = { + 'new': 'open', + 'suspended': 'open', + 'partiallyFilled': 'open', + 'filled': 'closed', + 'canceled': 'canceled', + 'expired': 'failed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createMarketOrder + // + // { clientOrderId: "fe36aa5e190149bf9985fb673bbb2ea0", + // createdAt: "2018-10-25T16:41:44.780Z", + // cumQuantity: "1", + // id: "66799540063", + // quantity: "1", + // side: "sell", + // status: "filled", + // symbol: "XRPUSDT", + // timeInForce: "FOK", + // tradesReport: [ { fee: "0.0004644", + // id: 386394956, + // price: "0.4644", + // quantity: "1", + // timestamp: "2018-10-25T16:41:44.780Z" } ], + // type: "market", + // updatedAt: "2018-10-25T16:41:44.780Z" } + // + const created = this.parse8601 (this.safeString (order, 'createdAt')); + const updated = this.parse8601 (this.safeString (order, 'updatedAt')); + const marketId = this.safeString (order, 'symbol'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['id']; + } + } + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'cumQuantity'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const id = this.safeString (order, 'clientOrderId'); + let price = this.safeFloat (order, 'price'); + if (price === undefined) { + if (id in this.orders) { + price = this.orders[id]['price']; + } + } + let remaining = undefined; + let cost = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + if (price !== undefined) { + cost = filled * price; + } + } + } + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + let trades = this.safeValue (order, 'tradesReport'); + let fee = undefined; + let average = undefined; + if (trades !== undefined) { + trades = this.parseTrades (trades, market); + let feeCost = undefined; + const numTrades = trades.length; + let tradesCost = 0; + for (let i = 0; i < numTrades; i++) { + if (feeCost === undefined) { + feeCost = 0; + } + tradesCost = this.sum (tradesCost, trades[i]['cost']); + const tradeFee = this.safeValue (trades[i], 'fee', {}); + const tradeFeeCost = this.safeFloat (tradeFee, 'cost'); + if (tradeFeeCost !== undefined) { + feeCost = this.sum (feeCost, tradeFeeCost); + } + } + cost = tradesCost; + if ((filled !== undefined) && (filled > 0)) { + average = cost / filled; + if (type === 'market') { + if (price === undefined) { + price = average; + } + } + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': market['quote'], + }; + } + } + return { + 'id': id, + 'timestamp': created, + 'datetime': this.iso8601 (created), + 'lastTradeTimestamp': updated, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + 'trades': trades, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.privateGetHistoryOrder (this.extend (request, params)); + const numOrders = response.length; + if (numOrders > 0) { + return this.parseOrder (response[0]); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + + async fetchOpenOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.privateGetOrderClientOrderId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privateGetOrder (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + const response = await this.privateGetHistoryOrder (this.extend (request, params)); + const parsedOrders = this.parseOrders (response, market); + const orders = []; + for (let i = 0; i < parsedOrders.length; i++) { + const order = parsedOrders[i]; + const status = order['status']; + if ((status === 'closed') || (status === 'canceled')) { + orders.push (order); + } + } + return this.filterBySinceLimit (orders, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'symbol': 'BTC/USD', // optional + // 'sort': 'DESC', // or 'ASC' + // 'by': 'timestamp', // or 'id' String timestamp by default, or id + // 'from': 'Datetime or Number', // ISO 8601 + // 'till': 'Datetime or Number', + // 'limit': 100, + // 'offset': 0, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetHistoryTrades (this.extend (request, params)); + // + // [ + // { + // "id": 9535486, + // "clientOrderId": "f8dbaab336d44d5ba3ff578098a68454", + // "orderId": 816088377, + // "symbol": "ETHBTC", + // "side": "sell", + // "quantity": "0.061", + // "price": "0.045487", + // "fee": "0.000002775", + // "timestamp": "2017-05-17T12:32:57.848Z" + // }, + // { + // "id": 9535437, + // "clientOrderId": "27b9bfc068b44194b1f453c7af511ed6", + // "orderId": 816088021, + // "symbol": "ETHBTC", + // "side": "buy", + // "quantity": "0.038", + // "price": "0.046000", + // "fee": "-0.000000174", + // "timestamp": "2017-05-17T12:30:57.848Z" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + // The id needed here is the exchange's id, and not the clientOrderID, + // which is the id that is stored in the unified order id + // To get the exchange's id you need to grab it from order['info']['id'] + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'id': id, + }; + const response = await this.privateGetHistoryOrderIdTrades (this.extend (request, params)); + const numOrders = response.length; + if (numOrders > 0) { + return this.parseTrades (response, market, since, limit); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found, ' + this.id + '.fetchOrderTrades() requires an exchange-specific order id, you need to grab it from order["info"]["id"]'); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostAccountCryptoAddressCurrency (this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + const tag = this.safeString (response, 'paymentId'); + return { + 'currency': currency, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetAccountCryptoAddressCurrency (this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + const tag = this.safeString (response, 'paymentId'); + return { + 'currency': currency['code'], + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + }; + if (tag) { + request['paymentId'] = tag; + } + const response = await this.privatePostAccountCryptoWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/api/' + this.version + '/'; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + url += api + '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + url += this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (Object.keys (query).length) { + body = this.json (query); + } + const payload = this.encode (this.apiKey + ':' + this.secret); + const auth = this.stringToBase64 (payload); + headers = { + 'Authorization': 'Basic ' + this.decode (auth), + 'Content-Type': 'application/json', + }; + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code >= 400) { + const feedback = this.id + ' ' + body; + // {"code":504,"message":"Gateway Timeout","description":""} + if ((code === 503) || (code === 504)) { + throw new ExchangeNotAvailable (feedback); + } + // fallback to default error handler on rate limit errors + // {"code":429,"message":"Too many requests","description":"Too many requests"} + if (code === 429) { + return; + } + // {"error":{"code":20002,"message":"Order not found","description":""}} + if (body[0] === '{') { + if ('error' in response) { + const errorCode = this.safeString (response['error'], 'code'); + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + const message = this.safeString (response['error'], 'message'); + if (message === 'Duplicate clientOrderId') { + throw new InvalidOrder (feedback); + } + } + } + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/errors":9,"./base/functions/number":15,"./hitbtc":91}],93:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ExchangeError, ExchangeNotAvailable, InvalidOrder, OrderNotFound, InsufficientFunds, ArgumentsRequired, BadSymbol, BadRequest, RequestTimeout } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class huobipro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'huobipro', + 'name': 'Huobi Pro', + 'countries': [ 'CN' ], + 'rateLimit': 2000, + 'userAgent': this.userAgents['chrome39'], + 'version': 'v1', + 'accounts': undefined, + 'accountsById': undefined, + 'hostname': 'api.huobi.pro', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchDepositAddress': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchTradingLimits': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchCurrencies': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '60min', + '4h': '4hour', + '1d': '1day', + '1w': '1week', + '1M': '1mon', + '1y': '1year', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766569-15aa7b9a-5edd-11e7-9e7f-44791f4ee49c.jpg', + 'api': { + 'market': 'https://{hostname}', + 'public': 'https://{hostname}', + 'private': 'https://{hostname}', + 'zendesk': 'https://huobiglobal.zendesk.com/hc/en-us/articles', + }, + 'www': 'https://www.huobi.pro', + 'referral': 'https://www.huobi.co/en-us/topic/invited/?invite_code=rwrd3', + 'doc': 'https://huobiapi.github.io/docs/spot/v1/cn/', + 'fees': 'https://www.huobi.pro/about/fee/', + }, + 'api': { + 'zendesk': { + 'get': [ + '360000400491-Trade-Limits', + ], + }, + 'market': { + 'get': [ + 'history/kline', // 获�K线数� + 'detail/merged', // 获���行情(Ticker) + 'depth', // 获� Market Depth 数� + 'trade', // 获� Trade Detail 数� + 'history/trade', // 批�获�最近的交易记录 + 'detail', // 获� Market Detail 24�时�交�数� + 'tickers', + ], + }, + 'public': { + 'get': [ + 'common/symbols', // 查询系统支�的所有交易对 + 'common/currencys', // 查询系统支�的所有�� + 'common/timestamp', // 查询系统当�时间 + 'common/exchange', // order limits + 'settings/currencys', // ?language=en-US + ], + }, + 'private': { + // todo add v2 endpoints + // 'GET /v2/account/withdraw/quota + // 'GET /v2/reference/currencies + // 'GET /v2/account/deposit/address + 'get': [ + 'account/accounts', // 查询当�用户的所有账户(�account-id) + 'account/accounts/{id}/balance', // 查询指定账户的余� + 'account/accounts/{sub-uid}', + 'account/history', + 'cross-margin/loan-info', + 'fee/fee-rate/get', + 'order/openOrders', + 'order/orders', + 'order/orders/{id}', // 查询�个订�详情 + 'order/orders/{id}/matchresults', // 查询�个订�的�交明细 + 'order/orders/getClientOrder', + 'order/history', // 查询当�委托�历�委托 + 'order/matchresults', // 查询当��交�历��交 + 'dw/withdraw-virtual/addresses', // 查询虚拟��现地� + 'query/deposit-withdraw', + 'margin/loan-orders', // 借贷订� + 'margin/accounts/balance', // 借贷账户详情 + 'points/actions', + 'points/orders', + 'subuser/aggregate-balance', + 'stable-coin/exchange_rate', + 'stable-coin/quote', + ], + // todo add v2 endpoints + // POST /v2/sub-user/management + 'post': [ + 'futures/transfer', + 'order/batch-orders', + 'order/orders/place', // 创建并执行一个新订� (一步下�, 推�使用) + 'order/orders/submitCancelClientOrder', + 'order/orders/batchCancelOpenOrders', + 'order/orders', // 创建一个新的订�请求 (仅创建订�,�执行下�) + 'order/orders/{id}/place', // 执行一个订� (仅执行已创建的订�) + 'order/orders/{id}/submitcancel', // 申请撤销一个订�请求 + 'order/orders/batchcancel', // 批�撤销订� + 'dw/balance/transfer', // 资产划转 + 'dw/withdraw/api/create', // 申请�现虚拟� + 'dw/withdraw-virtual/create', // 申请�现虚拟� + 'dw/withdraw-virtual/{id}/place', // 确认申请虚拟��现 + 'dw/withdraw-virtual/{id}/cancel', // 申请�消�现虚拟� + 'dw/transfer-in/margin', // 现货账户划入至借贷账户 + 'dw/transfer-out/margin', // 借贷账户划出至现货账户 + 'margin/orders', // 申请借贷 + 'margin/orders/{id}/repay', // 归还借贷 + 'stable-coin/exchange', + 'subuser/transfer', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.002, + 'taker': 0.002, + }, + }, + 'exceptions': { + 'exact': { + // err-code + 'timeout': RequestTimeout, // {"ts":1571653730865,"status":"error","err-code":"timeout","err-msg":"Request Timeout"} + 'gateway-internal-error': ExchangeNotAvailable, // {"status":"error","err-code":"gateway-internal-error","err-msg":"Failed to load data. Try again later.","data":null} + 'account-frozen-balance-insufficient-error': InsufficientFunds, // {"status":"error","err-code":"account-frozen-balance-insufficient-error","err-msg":"trade account balance is not enough, left: `0.0027`","data":null} + 'invalid-amount': InvalidOrder, // eg "Paramemter `amount` is invalid." + 'order-limitorder-amount-min-error': InvalidOrder, // limit order amount error, min: `0.001` + 'order-marketorder-amount-min-error': InvalidOrder, // market order amount error, min: `0.01` + 'order-limitorder-price-min-error': InvalidOrder, // limit order price error + 'order-limitorder-price-max-error': InvalidOrder, // limit order price error + 'order-orderstate-error': OrderNotFound, // canceling an already canceled order + 'order-queryorder-invalid': OrderNotFound, // querying a non-existent order + 'order-update-error': ExchangeNotAvailable, // undocumented error + 'api-signature-check-failed': AuthenticationError, + 'api-signature-not-valid': AuthenticationError, // {"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Incorrect Access key [Access key错误]","data":null} + 'base-record-invalid': OrderNotFound, // https://github.com/ccxt/ccxt/issues/5750 + // err-msg + 'invalid symbol': BadSymbol, // {"ts":1568813334794,"status":"error","err-code":"invalid-parameter","err-msg":"invalid symbol"} + 'invalid-parameter': BadRequest, // {"ts":1576210479343,"status":"error","err-code":"invalid-parameter","err-msg":"symbol trade not open now"} + 'base-symbol-trade-disabled': BadSymbol, // {"status":"error","err-code":"base-symbol-trade-disabled","err-msg":"Trading is disabled for this symbol","data":null} + }, + }, + 'options': { + // https://github.com/ccxt/ccxt/issues/5376 + 'fetchOrdersByStatesMethod': 'private_get_order_orders', // 'private_get_order_history' // https://github.com/ccxt/ccxt/pull/5392 + 'fetchOpenOrdersMethod': 'fetch_open_orders_v1', // 'fetch_open_orders_v2' // https://github.com/ccxt/ccxt/issues/5388 + 'createMarketBuyOrderRequiresPrice': true, + 'fetchMarketsMethod': 'publicGetCommonSymbols', + 'fetchBalanceMethod': 'privateGetAccountAccountsIdBalance', + 'createOrderMethod': 'privatePostOrderOrdersPlace', + 'language': 'en-US', + }, + 'commonCurrencies': { + // https://github.com/ccxt/ccxt/issues/6081 + // https://github.com/ccxt/ccxt/issues/3365 + // https://github.com/ccxt/ccxt/issues/2873 + 'GET': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol) + 'HOT': 'Hydro Protocol', // conflict with HOT (Holo) https://github.com/ccxt/ccxt/issues/4929 + }, + }); + } + + async fetchTradingLimits (symbols = undefined, params = {}) { + // this method should not be called directly, use loadTradingLimits () instead + // by default it will try load withdrawal fees of all currencies (with separate requests) + // however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const result = {}; + for (let i = 0; i < symbols.length; i++) { + const symbol = symbols[i]; + result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params); + } + return result; + } + + async fetchTradingLimitsById (id, params = {}) { + const request = { + 'symbol': id, + }; + const response = await this.publicGetCommonExchange (this.extend (request, params)); + // + // { status: "ok", + // data: { symbol: "aidocbtc", + // 'buy-limit-must-less-than': 1.1, + // 'sell-limit-must-greater-than': 0.9, + // 'limit-order-must-greater-than': 1, + // 'limit-order-must-less-than': 5000000, + // 'market-buy-order-must-greater-than': 0.0001, + // 'market-buy-order-must-less-than': 100, + // 'market-sell-order-must-greater-than': 1, + // 'market-sell-order-must-less-than': 500000, + // 'circuit-break-when-greater-than': 10000, + // 'circuit-break-when-less-than': 10, + // 'market-sell-order-rate-must-less-than': 0.1, + // 'market-buy-order-rate-must-less-than': 0.1 } } + // + return this.parseTradingLimits (this.safeValue (response, 'data', {})); + } + + parseTradingLimits (limits, symbol = undefined, params = {}) { + // + // { symbol: "aidocbtc", + // 'buy-limit-must-less-than': 1.1, + // 'sell-limit-must-greater-than': 0.9, + // 'limit-order-must-greater-than': 1, + // 'limit-order-must-less-than': 5000000, + // 'market-buy-order-must-greater-than': 0.0001, + // 'market-buy-order-must-less-than': 100, + // 'market-sell-order-must-greater-than': 1, + // 'market-sell-order-must-less-than': 500000, + // 'circuit-break-when-greater-than': 10000, + // 'circuit-break-when-less-than': 10, + // 'market-sell-order-rate-must-less-than': 0.1, + // 'market-buy-order-rate-must-less-than': 0.1 } + // + return { + 'info': limits, + 'limits': { + 'amount': { + 'min': this.safeFloat (limits, 'limit-order-must-greater-than'), + 'max': this.safeFloat (limits, 'limit-order-must-less-than'), + }, + }, + }; + } + + async fetchMarkets (params = {}) { + const method = this.options['fetchMarketsMethod']; + const response = await this[method] (params); + const markets = this.safeValue (response, 'data'); + const numMarkets = markets.length; + if (numMarkets < 1) { + throw new ExchangeError (this.id + ' publicGetCommonSymbols returned empty response: ' + this.json (markets)); + } + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = this.safeString (market, 'base-currency'); + const quoteId = this.safeString (market, 'quote-currency'); + const id = baseId + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': market['amount-precision'], + 'price': market['price-precision'], + }; + const maker = (base === 'OMG') ? 0 : 0.2 / 100; + const taker = (base === 'OMG') ? 0 : 0.2 / 100; + const minAmount = this.safeFloat (market, 'min-order-amt', Math.pow (10, -precision['amount'])); + const maxAmount = this.safeFloat (market, 'max-order-amt'); + const minCost = this.safeFloat (market, 'min-order-value', 0); + const state = this.safeString (market, 'state'); + const active = (state === 'online'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'taker': taker, + 'maker': maker, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': maxAmount, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (ticker, 'ts'); + let bid = undefined; + let ask = undefined; + let bidVolume = undefined; + let askVolume = undefined; + if ('bid' in ticker) { + if (Array.isArray (ticker['bid'])) { + bid = this.safeFloat (ticker['bid'], 0); + bidVolume = this.safeFloat (ticker['bid'], 1); + } + } + if ('ask' in ticker) { + if (Array.isArray (ticker['ask'])) { + ask = this.safeFloat (ticker['ask'], 0); + askVolume = this.safeFloat (ticker['ask'], 1); + } + } + const open = this.safeFloat (ticker, 'open'); + const close = this.safeFloat (ticker, 'close'); + let change = undefined; + let percentage = undefined; + let average = undefined; + if ((open !== undefined) && (close !== undefined)) { + change = close - open; + average = this.sum (open, close) / 2; + if ((close !== undefined) && (close > 0)) { + percentage = (change / open) * 100; + } + } + const baseVolume = this.safeFloat (ticker, 'amount'); + const quoteVolume = this.safeFloat (ticker, 'vol'); + let vwap = undefined; + if (baseVolume !== undefined && quoteVolume !== undefined && baseVolume > 0) { + vwap = quoteVolume / baseVolume; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': bid, + 'bidVolume': bidVolume, + 'ask': ask, + 'askVolume': askVolume, + 'vwap': vwap, + 'open': open, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'type': 'step0', + }; + const response = await this.marketGetDepth (this.extend (request, params)); + if ('tick' in response) { + if (!response['tick']) { + throw new ExchangeError (this.id + ' fetchOrderBook() returned empty response: ' + this.json (response)); + } + const orderbook = this.safeValue (response, 'tick'); + const result = this.parseOrderBook (orderbook, orderbook['ts']); + result['nonce'] = orderbook['version']; + return result; + } + throw new ExchangeError (this.id + ' fetchOrderBook() returned unrecognized response: ' + this.json (response)); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetDetailMerged (this.extend (request, params)); + return this.parseTicker (response['tick'], market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.marketGetTickers (params); + const tickers = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (response, 'ts'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const marketId = this.safeString (tickers[i], 'symbol'); + const market = this.safeValue (this.markets_by_id, marketId); + let symbol = marketId; + if (market !== undefined) { + symbol = market['symbol']; + const ticker = this.parseTicker (tickers[i], market); + ticker['timestamp'] = timestamp; + ticker['datetime'] = this.iso8601 (timestamp); + result[symbol] = ticker; + } + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger2 (trade, 'ts', 'created-at'); + const order = this.safeString (trade, 'order-id'); + let side = this.safeString (trade, 'direction'); + let type = this.safeString (trade, 'type'); + if (type !== undefined) { + const typeParts = type.split ('-'); + side = typeParts[0]; + type = typeParts[1]; + } + const takerOrMaker = this.safeString (trade, 'role'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'filled-amount', 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let fee = undefined; + let feeCost = this.safeFloat (trade, 'filled-fees'); + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = (side === 'buy') ? market['base'] : market['quote']; + } + const filledPoints = this.safeFloat (trade, 'filled-points'); + if (filledPoints !== undefined) { + if ((feeCost === undefined) || (feeCost === 0.0)) { + feeCost = filledPoints; + feeCurrency = this.safeCurrencyCode (this.safeString (trade, 'fee-deduct-currency')); + } + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + const id = this.safeString (trade, 'id'); + return { + 'id': id, + 'info': trade, + 'order': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['size'] = limit; // 1-100 orders, default is 100 + } + if (since !== undefined) { + request['start-date'] = this.ymd (since); // maximum query window size is 2 days, query window shift should be within past 120 days + } + const response = await this.privateGetOrderMatchresults (this.extend (request, params)); + const trades = this.parseTrades (response['data'], market, since, limit); + return trades; + } + + async fetchTrades (symbol, since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.marketGetHistoryTrade (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + let result = []; + for (let i = 0; i < data.length; i++) { + const trades = this.safeValue (data[i], 'data', []); + for (let j = 0; j < trades.length; j++) { + const trade = this.parseTrade (trades[j], market); + result.push (trade); + } + } + result = this.sortBy (result, 'timestamp'); + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'id'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'amount'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'period': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.marketGetHistoryKline (this.extend (request, params)); + return this.parseOHLCVs (response['data'], market, timeframe, since, limit); + } + + async fetchAccounts (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountAccounts (params); + return response['data']; + } + + async fetchCurrencies (params = {}) { + const request = { + 'language': this.options['language'], + }; + const response = await this.publicGetSettingsCurrencys (this.extend (request, params)); + const currencies = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + // + // { name: "ctxc", + // 'display-name': "CTXC", + // 'withdraw-precision': 8, + // 'currency-type': "eth", + // 'currency-partition': "pro", + // 'support-sites': null, + // 'otc-enable': 0, + // 'deposit-min-amount': "2", + // 'withdraw-min-amount': "4", + // 'show-precision': "8", + // weight: "2988", + // visible: true, + // 'deposit-desc': "Please don’t deposit any other digital assets except CTXC t…", + // 'withdraw-desc': "Minimum withdrawal amount: 4 CTXC. !>_ 0)) { + average = cost / filled; + } + } + const feeCost = this.safeFloat2 (order, 'filled-fees', 'field-fees'); // typo in their API, filled fees + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = (side === 'sell') ? market['quote'] : market['base']; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const market = this.market (symbol); + const request = { + 'account-id': this.accounts[0]['id'], + 'symbol': market['id'], + 'type': side + '-' + type, + }; + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " market buy order requires price argument to calculate cost (total amount of quote currency to spend for buying, amount * price). To switch off this warning exception and specify cost in the amount argument, set .options['createMarketBuyOrderRequiresPrice'] = false. Make sure you know what you're doing."); + } else { + // despite that cost = amount * price is in quote currency and should have quote precision + // the exchange API requires the cost supplied in 'amount' to be of base precision + // more about it here: https://github.com/ccxt/ccxt/pull/4395 + // we use priceToPrecision instead of amountToPrecision here + // because in this case the amount is in the quote currency + request['amount'] = this.costToPrecision (symbol, parseFloat (amount) * parseFloat (price)); + } + } else { + request['amount'] = this.costToPrecision (symbol, amount); + } + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + if (type === 'limit' || type === 'ioc' || type === 'limit-maker') { + request['price'] = this.priceToPrecision (symbol, price); + } + const method = this.options['createOrderMethod']; + const response = await this[method] (this.extend (request, params)); + const timestamp = this.milliseconds (); + const id = this.safeString (response, 'data'); + return { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const response = await this.privatePostOrderOrdersIdSubmitcancel ({ 'id': id }); + // + // let response = { + // 'status': 'ok', + // 'data': '10138899000', + // }; + // + return this.extend (this.parseOrder (response), { + 'id': id, + 'status': 'canceled', + }); + } + + currencyToPrecision (currency, fee) { + return this.decimalToPrecision (fee, 0, this.currencies[currency]['precision']); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let key = 'quote'; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.currencyToPrecision (market[key], cost)), + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currency (code); + const request = { + 'address': address, // only supports existing addresses in your withdraw address list + 'amount': amount, + 'currency': currency['id'].toLowerCase (), + }; + if (tag !== undefined) { + request['addr-tag'] = tag; // only for XRP? + } + const response = await this.privatePostDwWithdrawApiCreate (this.extend (request, params)); + const id = this.safeString (response, 'data'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/'; + if (api === 'market') { + url += api; + } else if ((api === 'public') || (api === 'private')) { + url += this.version; + } + url += '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.ymdhms (this.milliseconds (), 'T'); + let request = { + 'SignatureMethod': 'HmacSHA256', + 'SignatureVersion': '2', + 'AccessKeyId': this.apiKey, + 'Timestamp': timestamp, + }; + if (method !== 'POST') { + request = this.extend (request, query); + } + request = this.keysort (request); + let auth = this.urlencode (request); + // unfortunately, PHP demands double quotes for the escaped newline symbol + // eslint-disable-next-line quotes + const payload = [ method, this.hostname, url, auth ].join ("\n"); + const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64'); + auth += '&' + this.urlencode ({ 'Signature': signature }); + url += '?' + auth; + if (method === 'POST') { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } else { + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }) + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('status' in response) { + // + // {"status":"error","err-code":"order-limitorder-amount-min-error","err-msg":"limit order amount error, min: `0.001`","data":null} + // + const status = this.safeString (response, 'status'); + if (status === 'error') { + const code = this.safeString (response, 'err-code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + const message = this.safeString (response, 'err-msg'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (feedback); + } + } + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + if (limit === undefined || limit > 100) { + limit = 100; + } + await this.loadMarkets (); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const request = { + 'type': 'deposit', + 'from': 0, // From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from + }; + if (currency !== undefined) { + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; // max 100 + } + const response = await this.privateGetQueryDepositWithdraw (this.extend (request, params)); + // return response + return this.parseTransactions (response['data'], currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + if (limit === undefined || limit > 100) { + limit = 100; + } + await this.loadMarkets (); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const request = { + 'type': 'withdraw', + 'from': 0, // From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from + }; + if (currency !== undefined) { + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; // max 100 + } + const response = await this.privateGetQueryDepositWithdraw (this.extend (request, params)); + // return response + return this.parseTransactions (response['data'], currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // 'id': 8211029, + // 'type': 'deposit', + // 'currency': 'eth', + // 'chain': 'eth', + // 'tx-hash': 'bd315....', + // 'amount': 0.81162421, + // 'address': '4b8b....', + // 'address-tag': '', + // 'fee': 0, + // 'state': 'safe', + // 'created-at': 1542180380965, + // 'updated-at': 1542180788077 + // } + // + // fetchWithdrawals + // + // { + // 'id': 6908275, + // 'type': 'withdraw', + // 'currency': 'btc', + // 'chain': 'btc', + // 'tx-hash': 'c1a1a....', + // 'amount': 0.80257005, + // 'address': '1QR....', + // 'address-tag': '', + // 'fee': 0.0005, + // 'state': 'confirmed', + // 'created-at': 1552107295685, + // 'updated-at': 1552108032859 + // } + // + const timestamp = this.safeInteger (transaction, 'created-at'); + const updated = this.safeInteger (transaction, 'updated-at'); + const code = this.safeCurrencyCode (this.safeString (transaction, 'currency')); + let type = this.safeString (transaction, 'type'); + if (type === 'withdraw') { + type = 'withdrawal'; + } + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const tag = this.safeString (transaction, 'address-tag'); + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + feeCost = Math.abs (feeCost); + } + return { + 'info': transaction, + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'tx-hash'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'address'), + 'tag': tag, + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': { + 'currency': code, + 'cost': feeCost, + 'rate': undefined, + }, + }; + } + + parseTransactionStatus (status) { + const statuses = { + // deposit statuses + 'unknown': 'failed', + 'confirming': 'pending', + 'confirmed': 'ok', + 'safe': 'ok', + 'orphan': 'failed', + // withdrawal statuses + 'submitted': 'pending', + 'canceled': 'canceled', + 'reexamine': 'pending', + 'reject': 'failed', + 'pass': 'pending', + 'wallet-reject': 'failed', + // 'confirmed': 'ok', // present in deposit statuses + 'confirm-error': 'failed', + 'repealed': 'failed', + 'wallet-transfer': 'pending', + 'pre-transfer': 'pending', + }; + return this.safeString (statuses, status, status); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],94:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const huobipro = require ('./huobipro.js'); + +// --------------------------------------------------------------------------- + +module.exports = class huobiru extends huobipro { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'huobiru', + 'name': 'Huobi Russia', + 'countries': [ 'RU' ], + 'hostname': 'www.huobi.com.ru', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/52978816-e8552e00-33e3-11e9-98ed-845acfece834.jpg', + 'api': { + 'market': 'https://www.huobi.com.ru/api', + 'public': 'https://www.huobi.com.ru/api', + 'private': 'https://www.huobi.com.ru/api', + 'zendesk': 'https://huobiglobal.zendesk.com/hc/en-us/articles', + }, + 'www': 'https://www.huobi.com.ru/ru-ru', + 'referral': 'https://www.huobi.com.ru/invite?invite_code=esc74', + 'doc': 'https://github.com/cloudapidoc/API_Docs_en', + 'fees': 'https://www.huobi.com.ru/ru-ru/about/fee/', + }, + }); + } +}; + +},{"./huobipro.js":93}],95:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class ice3x extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'ice3x', + 'name': 'ICE3X', + 'countries': [ 'ZA' ], // South Africa + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38012176-11616c32-3269-11e8-9f05-e65cf885bb15.jpg', + 'api': 'https://ice3x.com/api', + 'www': [ + 'https://ice3x.com', + 'https://ice3x.co.za', + ], + 'doc': 'https://ice3x.co.za/ice-cubed-bitcoin-exchange-api-documentation-1-june-2017', + 'fees': [ + 'https://help.ice3.com/support/solutions/articles/11000033293-trading-fees', + 'https://help.ice3.com/support/solutions/articles/11000033288-fees-explained', + 'https://help.ice3.com/support/solutions/articles/11000008131-what-are-your-fiat-deposit-and-withdrawal-fees-', + 'https://help.ice3.com/support/solutions/articles/11000033289-deposit-fees', + ], + 'referral': 'https://ice3x.com?ref=14341802', + }, + 'api': { + 'public': { + 'get': [ + 'currency/list', + 'currency/info', + 'pair/list', + 'pair/info', + 'stats/marketdepthfull', + 'stats/marketdepthbtcav', + 'stats/marketdepth', + 'orderbook/info', + 'trade/list', + 'trade/info', + ], + }, + 'private': { + 'post': [ + 'balance/list', + 'balance/info', + 'order/new', + 'order/cancel', + 'order/list', + 'order/info', + 'trade/list', + 'trade/info', + 'transaction/list', + 'transaction/info', + 'invoice/list', + 'invoice/info', + 'invoice/pdf', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.005, + 'taker': 0.005, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencyList (params); + const currencies = response['response']['entities']; + const precision = this.precision['amount']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'currency_id'); + const currencyId = this.safeString (currency, 'iso'); + const code = this.safeCurrencyCode (currencyId); + result[code] = { + 'id': id, + 'code': code, + 'name': currency['name'], + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + if (this.currencies_by_id === undefined) { + this.currencies = await this.fetchCurrencies (); + this.currencies_by_id = this.indexBy (this.currencies, 'id'); + } + const response = await this.publicGetPairList (params); + const markets = this.safeValue (response['response'], 'entities'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'pair_id'); + const baseId = this.safeString (market, 'currency_id_from'); + const quoteId = this.safeString (market, 'currency_id_to'); + const baseCurrency = this.currencies_by_id[baseId]; + const quoteCurrency = this.currencies_by_id[quoteId]; + const base = baseCurrency['code']; + const quote = quoteCurrency['code']; + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + const symbol = market['symbol']; + const last = this.safeFloat (ticker, 'last_price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'max_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'min_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'vol'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + }; + const response = await this.publicGetStatsMarketdepthfull (this.extend (request, params)); + const ticker = this.safeValue (response['response'], 'entity'); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetStatsMarketdepthfull (params); + const tickers = this.safeValue (response['response'], 'entities'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const marketId = this.safeString (ticker, 'pair_id'); + const market = this.safeValue (this.marketsById, marketId); + if (market !== undefined) { + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_id': this.marketId (symbol), + }; + if (limit !== undefined) { + const type = this.safeString (params, 'type'); + if ((type !== 'ask') && (type !== 'bid')) { + // eslint-disable-next-line quotes + throw new ExchangeError (this.id + " fetchOrderBook requires an exchange-specific extra 'type' param ('bid' or 'ask') when used with a limit"); + } else { + request['items_per_page'] = limit; + } + } + const response = await this.publicGetOrderbookInfo (this.extend (request, params)); + const orderbook = this.safeValue (response['response'], 'entities'); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'created'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': market['quote'], + }; + } + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, 'trade_id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + }; + const response = await this.publicGetTradeList (this.extend (request, params)); + const trades = this.safeValue (response['response'], 'entities'); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalanceList (params); + const result = { 'info': response }; + const balances = this.safeValue (response['response'], 'entities', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + // currency ids are numeric strings + const currencyId = this.safeString (balance, 'currency_id'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + const pairId = this.safeInteger (order, 'pair_id'); + let symbol = undefined; + if (pairId && !market && (pairId in this.marketsById)) { + market = this.marketsById[pairId]; + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'created'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + let status = this.safeInteger (order, 'active'); + let remaining = this.safeFloat (order, 'remaining'); + let filled = undefined; + if (status === 1) { + status = 'open'; + } else { + status = 'closed'; + remaining = 0; + filled = amount; + } + let fee = undefined; + const feeCost = this.safeFloat (order, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + }; + if (market !== undefined) { + fee['currency'] = market['quote']; + } + } + return { + 'id': this.safeString (order, 'order_id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': this.safeStrin (order, 'type'), + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + 'type': side, + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostOrderNew (this.extend (request, params)); + const order = this.parseOrder ({ + 'order_id': response['response']['entity']['order_id'], + 'created': this.seconds (), + 'active': 1, + 'type': side, + 'price': price, + 'volume': amount, + 'remaining': amount, + 'info': response, + }, market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order_id': id, + }; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order _id': id, + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + const order = this.safeValue (response['response'], 'entity'); + return this.parseOrder (order); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrderList (params); + const orders = this.safeValue (response['response'], 'entities'); + return this.parseOrders (orders, undefined, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + }; + if (limit !== undefined) { + request['items_per_page'] = limit; + } + if (since !== undefined) { + request['date_from'] = parseInt (since / 1000); + } + const response = await this.privatePostTradeList (this.extend (request, params)); + const trades = this.safeValue (response['response'], 'entities'); + return this.parseTrades (trades, market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency_id': currency['id'], + }; + const response = await this.privatePostBalanceInfo (this.extend (request, params)); + const balance = this.safeValue (response['response'], 'entity'); + const address = this.safeString (balance, 'address'); + const status = address ? 'ok' : 'none'; + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'status': status, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'nonce': this.nonce (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const errors = this.safeValue (response, 'errors'); + const data = this.safeValue (response, 'response'); + if (errors || !data) { + const authErrorKeys = [ 'Key', 'user_id', 'Sign' ]; + for (let i = 0; i < authErrorKeys.length; i++) { + const errorKey = authErrorKeys[i]; + const errorMessage = this.safeString (errors, errorKey); + if (!errorMessage) { + continue; + } + if (errorKey === 'user_id' && errorMessage.indexOf ('authorization') < 0) { + continue; + } + throw new AuthenticationError (errorMessage); + } + throw new ExchangeError (this.json (errors)); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],96:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class idex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'idex', + 'name': 'IDEX', + 'countries': [ 'US' ], + 'rateLimit': 1500, + 'certified': true, + 'requiresWeb3': true, + 'has': { + 'fetchOrderBook': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchMarkets': true, + 'fetchBalance': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchOpenOrders': true, + 'fetchTransactions': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchOHLCV': false, + }, + 'timeframes': { + '1m': 'M1', + '3m': 'M3', + '5m': 'M5', + '15m': 'M15', + '30m': 'M30', // default + '1h': 'H1', + '4h': 'H4', + '1d': 'D1', + '1w': 'D7', + '1M': '1M', + }, + 'urls': { + 'test': 'https://api.idex.market', + 'logo': 'https://user-images.githubusercontent.com/1294454/63693236-3415e380-c81c-11e9-8600-ba1634f1407d.jpg', + 'api': 'https://api.idex.market', + 'www': 'https://idex.market', + 'doc': [ + 'https://docs.idex.market/', + ], + }, + 'api': { + 'public': { + 'post': [ + 'returnTicker', + 'returnCurrenciesWithPairs', // undocumented + 'returnCurrencies', + 'return24Volume', + 'returnBalances', + 'returnCompleteBalances', // shows amount in orders as well as total + 'returnDepositsWithdrawals', + 'returnOpenOrders', + 'returnOrderBook', + 'returnOrderStatus', + 'returnOrderTrades', + 'returnTradeHistory', + 'returnTradeHistoryMeta', // not documented + 'returnContractAddress', + 'returnNextNonce', + ], + }, + 'private': { + 'post': [ + 'order', + 'cancel', + 'trade', + 'withdraw', + ], + }, + }, + 'options': { + 'contractAddress': undefined, // 0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208 + 'orderNonce': undefined, + }, + 'exceptions': { + 'Invalid order signature. Please try again.': AuthenticationError, + 'You have insufficient funds to match this order. If you believe this is a mistake please refresh and try again.': InsufficientFunds, + 'Order no longer available.': InvalidOrder, + }, + 'requiredCredentials': { + 'walletAddress': true, + 'privateKey': true, + 'apiKey': false, + 'secret': false, + }, + 'commonCurrencies': { + 'ONE': 'Menlo One', + 'FT': 'Fabric Token', + }, + }); + } + + async fetchMarkets (params = {}) { + // idex does not have an endpoint for markets + // instead we generate the markets from the endpoint for currencies + const request = { + 'includeDelisted': true, + }; + const markets = await this.publicPostReturnCurrenciesWithPairs (this.extend (request, params)); + const currenciesById = {}; + const currencies = markets['tokens']; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + currenciesById[currency['symbol']] = currency; + } + const result = []; + const limits = { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + const quotes = markets['pairs']; + const keys = Object.keys (quotes); + for (let i = 0; i < keys.length; i++) { + const quoteId = keys[i]; + const bases = quotes[quoteId]; + const quote = this.safeCurrencyCode (quoteId); + const quoteCurrency = currenciesById[quoteId]; + for (let j = 0; j < bases.length; j++) { + const baseId = bases[j]; + const id = quoteId + '_' + baseId; + const base = this.safeCurrencyCode (baseId); + const symbol = base + '/' + quote; + const baseCurrency = currenciesById[baseId]; + const baseAddress = baseCurrency['address']; + const quoteAddress = quoteCurrency['address']; + const precision = { + 'price': this.safeInteger (quoteCurrency, 'decimals'), + 'amount': this.safeInteger (baseCurrency, 'decimals'), + }; + result.push ({ + 'symbol': symbol, + 'precision': precision, + 'base': base, + 'quote': quote, + 'baseId': baseAddress, + 'quoteId': quoteAddress, + 'limits': limits, + 'id': id, + 'info': baseCurrency, + 'tierBased': false, + }); + } + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // last: '0.0016550916', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.0016743368', + // highestBid: '0.001163726270773897', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' + // } + // + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const baseVolume = this.safeFloat (ticker, 'baseVolume'); + const quoteVolume = this.safeFloat (ticker, 'quoteVolume'); + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'percentChange'); + return { + 'symbol': symbol, + 'timestamp': undefined, + 'datetime': undefined, + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': quoteVolume, + 'quoteVolume': baseVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicPostReturnTicker (params); + // { ETH_BOUNCY: + // { last: '0.000000004000088005', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.00000000599885995', + // highestBid: '0.000000001400500103', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' }, + // ETH_NBAI: + // { last: '0.0000032', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.000004000199999502', + // highestBid: '0.0000016002', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' }, } + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = undefined; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + market = { 'symbol': symbol }; + } + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicPostReturnTicker (this.extend (request, params)); + // { last: '0.0016550916', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.0016743368', + // highestBid: '0.001163726270773897', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' } + return this.parseTicker (response, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const id = market['quote'] + '_' + market['base']; + const request = { + 'market': id, + 'count': 100, // the default will only return one trade + }; + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.publicPostReturnOrderBook (this.extend (request, params)); + // + // { + // "asks": [ + // { + // "price": "0.001675282799999", + // "amount": "206.163978911921061732", + // "total": "0.345382967850497906", + // "orderHash": "0xfdf12c124a6a7fa4a8e1866b324da888c8e1b3ad209f5050d3a23df3397a5cb7", + // "params": { + // "tokenBuy": "0x0000000000000000000000000000000000000000", + // "buySymbol": "ETH", + // "buyPrecision": 18, + // "amountBuy": "345382967850497906", + // "tokenSell": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", + // "sellSymbol": "ABT", + // "sellPrecision": 18, + // "amountSell": "206163978911921061732", + // "expires": 10000, + // "nonce": 13489307413, + // "user": "0x9e8ef79316a4a79bbf55a5f9c16b3e068fff65c6" + // } + // } + // ], + // "bids": [ + // { + // "price": "0.001161865193232242", + // "amount": "854.393661648355", + // "total": "0.992690256787469029", + // "orderHash": "0x2f2baaf982085e4096f9e23e376214885fa74b2939497968e92222716fc2c86d", + // "params": { + // "tokenBuy": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", + // "buySymbol": "ABT", + // "buyPrecision": 18, + // "amountBuy": "854393661648355000000", + // "tokenSell": "0x0000000000000000000000000000000000000000", + // "sellSymbol": "ETH", + // "sellPrecision": 18, + // "amountSell": "992690256787469029", + // "expires": 10000, + // "nonce": 18155189676, + // "user": "0xb631284dd7b74a846af5b37766ceb1f85d53eca4" + // } + // } + // ] + // } + // + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseBidAsk (bidAsk, priceKey = 0, amountKey = 1) { + const price = this.safeFloat (bidAsk, priceKey); + const amount = this.safeFloat (bidAsk, amountKey); + const info = bidAsk; + return [price, amount, info]; + } + + async fetchBalance (params = {}) { + const request = { + 'address': this.walletAddress, + }; + const response = await this.publicPostReturnCompleteBalances (this.extend (request, params)); + // + // { + // ETH: { available: '0.0167', onOrders: '0.1533' } + // } + // + const result = { + 'info': response, + }; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const currency = keys[i]; + const balance = response[currency]; + const code = this.safeCurrencyCode (currency); + result[code] = { + 'free': this.safeFloat (balance, 'available'), + 'used': this.safeFloat (balance, 'onOrders'), + }; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + this.checkRequiredDependencies (); + await this.loadMarkets (); + const market = this.market (symbol); + if (type === 'limit') { + const expires = 100000; + const contractAddress = await this.getContractAddress (); + let tokenBuy = undefined; + let tokenSell = undefined; + let amountBuy = undefined; + let amountSell = undefined; + const quoteAmount = parseFloat (price) * parseFloat (amount); + if (side === 'buy') { + tokenBuy = market['baseId']; + tokenSell = market['quoteId']; + amountBuy = this.toWei (amount, 'ether', market['precision']['amount']); + amountSell = this.toWei (quoteAmount, 'ether', 18); + } else { + tokenBuy = market['quoteId']; + tokenSell = market['baseId']; + amountBuy = this.toWei (quoteAmount, 'ether', 18); + amountSell = this.toWei (amount, 'ether', market['precision']['amount']); + } + const nonce = await this.getNonce (); + const orderToHash = { + 'contractAddress': contractAddress, + 'tokenBuy': tokenBuy, + 'amountBuy': amountBuy, + 'tokenSell': tokenSell, + 'amountSell': amountSell, + 'expires': expires, + 'nonce': nonce, + 'address': this.walletAddress, + }; + const orderHash = this.getIdexCreateOrderHash (orderToHash); + const signature = this.signMessage (orderHash, this.privateKey); + const request = { + 'tokenBuy': tokenBuy, + 'amountBuy': amountBuy, + 'tokenSell': tokenSell, + 'amountSell': amountSell, + 'address': this.walletAddress, + 'nonce': nonce, + 'expires': expires, + }; + const response = await this.privatePostOrder (this.extend (request, signature)); // this.extend (request, params) will cause invalid signature + // { orderNumber: 1562323021, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // timestamp: 1564041428, + // price: '0.00073', + // amount: '210', + // total: '0.1533', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' } } + return this.parseOrder (response, market); + } else if (type === 'market') { + if (!('orderHash' in params)) { + throw new ArgumentsRequired (this.id + ' market order requires an order structure such as that in fetchOrderBook()[\'bids\'][0][2], fetchOrder()[\'info\'], or fetchOpenOrders()[0][\'info\']'); + } + // { price: '0.000132247803328924', + // amount: '19980', + // total: '2.6423111105119', + // orderHash: + // '0x5fb3452b3d13fc013585b51c91c43a0fbe4298c211243763c49437848c274749', + // params: + // { tokenBuy: '0x0000000000000000000000000000000000000000', + // buySymbol: 'ETH', + // buyPrecision: 18, + // amountBuy: '2642311110511900000', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // sellSymbol: 'IDEX', + // sellPrecision: 18, + // amountSell: '19980000000000000000000', + // expires: 10000, + // nonce: 1564656561510, + // user: '0xc3f8304270e49b8e8197bfcfd8567b83d9e4479b' } } + const orderToSign = { + 'orderHash': params['orderHash'], + 'amount': params['params']['amountBuy'], + 'address': params['params']['user'], + 'nonce': params['params']['nonce'], + }; + const orderHash = this.getIdexMarketOrderHash (orderToSign); + const signature = this.signMessage (orderHash, this.privateKey); + const signedOrder = this.extend (orderToSign, signature); + signedOrder['address'] = this.walletAddress; + signedOrder['nonce'] = await this.getNonce (); + // [ { + // "amount": "0.07", + // "date": "2017-10-13 16:25:36", + // "total": "0.49", + // "market": "ETH_DVIP", + // "type": "buy", + // "price": "7", + // "orderHash": "0xcfe4018c59e50e0e1964c979e6213ce5eb8c751cbc98a44251eb48a0985adc52", + // "uuid": "250d51a0-b033-11e7-9984-a9ab79bb8f35" + // } ] + const response = await this.privatePostTrade (signedOrder); + return this.parseOrders (response, market); + } + } + + async getNonce () { + if (this.options['orderNonce'] === undefined) { + const response = await this.publicPostReturnNextNonce ({ + 'address': this.walletAddress, + }); + return this.safeInteger (response, 'nonce'); + } else { + const result = this.options['orderNonce']; + this.options['orderNonce'] = this.sum (this.options['orderNonce'], 1); + return result; + } + } + + async getContractAddress () { + if (this.options['contractAddress'] !== undefined) { + return this.options['contractAddress']; + } + const response = await this.publicPostReturnContractAddress (); + this.options['contractAddress'] = this.safeString (response, 'address'); + return this.options['contractAddress']; + } + + async cancelOrder (orderId, symbol = undefined, params = {}) { + const nonce = await this.getNonce (); + const orderToHash = { + 'orderHash': orderId, + 'nonce': nonce, + }; + const orderHash = this.getIdexCancelOrderHash (orderToHash); + const signature = this.signMessage (orderHash, this.privateKey); + const request = { + 'orderHash': orderId, + 'address': this.walletAddress, + 'nonce': nonce, + }; + const response = await this.privatePostCancel (this.extend (request, signature)); + // { success: 1 } + if ('success' in response) { + return { + 'info': response, + }; + } else { + throw new ExchangeError (this.id + ' cancel order failed ' + this.json (response)); + } + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'address': this.walletAddress, + }; + if (since !== undefined) { + request['start'] = parseInt (Math.floor (since / 1000)); + } + const response = await this.publicPostReturnDepositsWithdrawals (this.extend (request, params)); + // { deposits: + // [ { currency: 'ETH', + // amount: '0.05', + // timestamp: 1563953513, + // transactionHash: + // '0xd6eefd81c7efc9beeb35b924d6db3c93a78bf7eac082ba87e107ad4e94bccdcf', + // depositNumber: 1586430 }, + // { currency: 'ETH', + // amount: '0.12', + // timestamp: 1564040359, + // transactionHash: + // '0x2ecbb3ab72b6f79fc7a9058c39dce28f913152748c1507d13ab1759e965da3ca', + // depositNumber: 1587341 } ], + // withdrawals: + // [ { currency: 'ETH', + // amount: '0.149', + // timestamp: 1564060001, + // transactionHash: + // '0xab555fc301779dd92fd41ccd143b1d72776ae7b5acfc59ca44a1d376f68fda15', + // withdrawalNumber: 1444070, + // status: 'COMPLETE' } ] } + const deposits = this.parseTransactions (response['deposits'], currency, since, limit); + const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); + return this.arrayConcat (deposits, withdrawals); + } + + parseTransaction (item, currency = undefined) { + // { currency: 'ETH', + // amount: '0.05', + // timestamp: 1563953513, + // transactionHash: + // '0xd6eefd81c7efc9beeb35b924d6db3c93a78bf7eac082ba87e107ad4e94bccdcf', + // depositNumber: 1586430 } + const amount = this.safeFloat (item, 'amount'); + const timestamp = this.safeTimestamp (item, 'timestamp'); + const txhash = this.safeString (item, 'transactionHash'); + let id = undefined; + let type = undefined; + let status = undefined; + let addressFrom = undefined; + let addressTo = undefined; + if ('depositNumber' in item) { + id = this.safeString (item, 'depositNumber'); + type = 'deposit'; + addressFrom = this.walletAddress; + addressTo = this.options['contractAddress']; + } else if ('withdrawalNumber' in item) { + id = this.safeString (item, 'withdrawalNumber'); + type = 'withdrawal'; + status = this.parseTransactionStatus (this.safeString (item, 'status')); + addressFrom = this.options['contractAddress']; + addressTo = this.walletAddress; + } + const code = this.safeCurrencyCode (this.safeString (item, 'currency')); + return { + 'info': item, + 'id': id, + 'txid': txhash, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'currency': code, + 'amount': amount, + 'status': status, + 'type': type, + 'updated': undefined, + 'comment': undefined, + 'addressFrom': addressFrom, + 'tagFrom': undefined, + 'addressTo': addressTo, + 'tagTo': undefined, + 'fee': { + 'currency': code, + 'cost': undefined, + 'rate': undefined, + }, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'COMPLETE': 'ok', + }; + return this.safeString (statuses, status); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (this.walletAddress === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a walletAddress'); + } + await this.loadMarkets (); + const request = { + 'address': this.walletAddress, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.publicPostReturnOpenOrders (this.extend (request, params)); + // [ { timestamp: 1564041428, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // orderNumber: 1562323021, + // market: 'ETH_LIT', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buySymbol: 'LIT', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellSymbol: 'ETH', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, + // price: '0.00073', + // amount: '210', + // status: 'open', + // total: '0.1533' } ] + return this.parseOrders (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'orderHash': id, + }; + const response = await this.publicPostReturnOrderStatus (this.extend (request, params)); + // { filled: '0', + // initialAmount: '210', + // timestamp: 1564041428, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // orderNumber: 1562323021, + // market: 'ETH_LIT', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buySymbol: 'LIT', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellSymbol: 'ETH', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, + // price: '0.00073', + // amount: '210', + // status: 'open', + // total: '0.1533' } + return this.parseOrder (response, market); + } + + parseOrder (order, market = undefined) { + // { filled: '0', + // initialAmount: '210', + // timestamp: 1564041428, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // orderNumber: 1562323021, + // market: 'ETH_LIT', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buySymbol: 'LIT', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellSymbol: 'ETH', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, + // price: '0.00073', + // amount: '210', + // status: 'open', + // total: '0.1533' } + const timestamp = this.safeTimestamp (order, 'timestamp'); + const side = this.safeString (order, 'type'); + let symbol = undefined; + let amount = undefined; + let remaining = undefined; + if ('initialAmount' in order) { + amount = this.safeFloat (order, 'initialAmount'); + remaining = this.safeFloat (order, 'amount'); + } else { + amount = this.safeFloat (order, 'amount'); + } + const filled = this.safeFloat (order, 'filled'); + const cost = this.safeFloat (order, 'total'); + const price = this.safeFloat (order, 'price'); + if ('market' in order) { + const marketId = order['market']; + symbol = this.markets_by_id[marketId]['symbol']; + } else if ((side !== undefined) && ('params' in order)) { + const params = order['params']; + const buy = this.safeCurrencyCode (this.safeString (params, 'tokenBuy')); + const sell = this.safeCurrencyCode (this.safeString (params, 'tokenSell')); + if (buy !== undefined && sell !== undefined) { + symbol = (side === 'buy') ? (buy + '/' + sell) : (sell + '/' + buy); + } + } + if (symbol === undefined && market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'orderHash'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'side': side, + 'amount': amount, + 'price': price, + 'type': 'limit', + 'filled': filled, + 'remaining': remaining, + 'cost': cost, + 'status': status, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'open': 'open', + }; + return this.safeString (statuses, status, status); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (this.walletAddress === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a walletAddress'); + } + await this.loadMarkets (); + const request = { + 'address': this.walletAddress, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + if (limit !== undefined) { + request['start'] = parseInt (Math.floor (limit)); + } + const response = await this.publicPostReturnTradeHistory (this.extend (request, params)); + // { ETH_IDEX: + // [ { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } ] } + // + // if a symbol is specified in the request: + // + // [ { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } ] + if (Array.isArray (response)) { + return this.parseTrades (response, market, since, limit); + } else { + let result = []; + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const trades = response[marketId]; + const parsed = this.parseTrades (trades, market, since, limit); + result = this.arrayConcat (result, parsed); + } + return result; + } + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['start'] = parseInt (Math.floor (limit)); + } + const response = await this.publicPostReturnTradeHistory (this.extend (request, params)); + // [ { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } ] + return this.parseTrades (response, market, since, limit); + } + + parseTrade (trade, market = undefined) { + // { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } + const side = this.safeString (trade, 'type'); + let feeCurrency = undefined; + let symbol = undefined; + const maker = this.safeString (trade, 'maker'); + let takerOrMaker = undefined; + if (maker !== undefined && this.walletAddress !== undefined) { + if (maker.toLowerCase () === this.walletAddress.toLowerCase ()) { + takerOrMaker = 'maker'; + } else { + takerOrMaker = 'taker'; + } + } + const buy = this.safeCurrencyCode (this.safeString (trade, 'tokenBuy')); + const sell = this.safeCurrencyCode (this.safeString (trade, 'tokenSell')); + // get ready to be mind-boggled + let feeSide = undefined; + if (buy !== undefined && sell !== undefined) { + if (side === 'buy') { + feeSide = 'buyerFee'; + if (takerOrMaker === 'maker') { + symbol = buy + '/' + sell; + feeCurrency = buy; + } else { + symbol = sell + '/' + buy; + feeCurrency = sell; + } + } else { + feeSide = 'sellerFee'; + if (takerOrMaker === 'maker') { + symbol = sell + '/' + buy; + feeCurrency = buy; + } else { + symbol = buy + '/' + sell; + feeCurrency = sell; + } + } + } + if (symbol === undefined && market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const id = this.safeString (trade, 'tid'); + const amount = this.safeFloat (trade, 'amount'); + const price = this.safeFloat (trade, 'price'); + const cost = this.safeFloat (trade, 'total'); + let feeCost = this.safeFloat (trade, feeSide); + if (feeCost < 0) { + const gasFee = this.safeFloat (trade, 'gasFee'); + feeCost = this.sum (gasFee, feeCost); + } + const fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + if (feeCost !== undefined && amount !== undefined) { + const feeCurrencyAmount = (feeCurrency === 'ETH') ? cost : amount; + fee['rate'] = feeCost / feeCurrencyAmount; + } + const orderId = this.safeString (trade, 'orderHash'); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'order': orderId, + 'symbol': symbol, + 'type': 'limit', + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkRequiredDependencies (); + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const tokenAddress = currency['id']; + const nonce = await this.getNonce (); + amount = this.toWei (amount, 'ether', currency['precision']); + const requestToHash = { + 'contractAddress': await this.getContractAddress (), + 'token': tokenAddress, + 'amount': amount, + 'address': address, + 'nonce': nonce, + }; + const hash = this.getIdexWithdrawHash (requestToHash); + const signature = this.signMessage (hash, this.privateKey); + const request = { + 'address': address, + 'amount': amount, + 'token': tokenAddress, + 'nonce': nonce, + }; + const response = await this.privatePostWithdraw (this.extend (request, signature)); + // { amount: '0' } + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + body = this.json (params); // all methods are POST + const url = this.urls['api'] + '/' + path; + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + if (api === 'private') { + this.checkRequiredCredentials (); + headers['API-Key'] = this.apiKey; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + getIdexCreateOrderHash (order) { + return this.soliditySha3 ([ + order['contractAddress'], // address + order['tokenBuy'], // address + order['amountBuy'], // uint256 + order['tokenSell'], // address + order['amountSell'], // uint256 + order['expires'], // uint256 + order['nonce'], // uint256 + order['address'], // address + ]); + } + + getIdexCancelOrderHash (order) { + return this.soliditySha3 ([ + order['orderHash'], // address + order['nonce'], // uint256 + ]); + } + + getIdexMarketOrderHash (order) { + return this.soliditySha3 ([ + order['orderHash'], // address + order['amount'], // uint256 + order['address'], // address + order['nonce'], // uint256 + ]); + } + + getIdexWithdrawHash (request) { + return this.soliditySha3 ([ + request['contractAddress'], // address + request['token'], // uint256 + request['amount'], // uint256 + request['address'], // address + request['nonce'], // uint256 + ]); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if ('error' in response) { + if (response['error'] in this.exceptions) { + throw new this.exceptions[response['error']] (this.id + ' ' + response['error']); + } + throw new ExchangeError (this.id + ' ' + body); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],97:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class independentreserve extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'independentreserve', + 'name': 'Independent Reserve', + 'countries': [ 'AU', 'NZ' ], // Australia, New Zealand + 'rateLimit': 1000, + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30521662-cf3f477c-9bcb-11e7-89bc-d1ac85012eda.jpg', + 'api': { + 'public': 'https://api.independentreserve.com/Public', + 'private': 'https://api.independentreserve.com/Private', + }, + 'www': 'https://www.independentreserve.com', + 'doc': 'https://www.independentreserve.com/API', + }, + 'api': { + 'public': { + 'get': [ + 'GetValidPrimaryCurrencyCodes', + 'GetValidSecondaryCurrencyCodes', + 'GetValidLimitOrderTypes', + 'GetValidMarketOrderTypes', + 'GetValidOrderTypes', + 'GetValidTransactionTypes', + 'GetMarketSummary', + 'GetOrderBook', + 'GetAllOrders', + 'GetTradeHistorySummary', + 'GetRecentTrades', + 'GetFxRates', + ], + }, + 'private': { + 'post': [ + 'PlaceLimitOrder', + 'PlaceMarketOrder', + 'CancelOrder', + 'GetOpenOrders', + 'GetClosedOrders', + 'GetClosedFilledOrders', + 'GetOrderDetails', + 'GetAccounts', + 'GetTransactions', + 'GetDigitalCurrencyDepositAddress', + 'GetDigitalCurrencyDepositAddresses', + 'SynchDigitalCurrencyDepositAddressWithBlockchain', + 'WithdrawDigitalCurrency', + 'RequestFiatWithdrawal', + 'GetTrades', + 'GetBrokerageFees', + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.5 / 100, + 'maker': 0.5 / 100, + 'percentage': true, + 'tierBased': false, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const baseCurrencies = await this.publicGetGetValidPrimaryCurrencyCodes (params); + const quoteCurrencies = await this.publicGetGetValidSecondaryCurrencyCodes (params); + const result = []; + for (let i = 0; i < baseCurrencies.length; i++) { + const baseId = baseCurrencies[i]; + const base = this.safeCurrencyCode (baseId); + for (let j = 0; j < quoteCurrencies.length; j++) { + const quoteId = quoteCurrencies[j]; + const quote = this.safeCurrencyCode (quoteId); + const id = baseId + '/' + quoteId; + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': id, + }); + } + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privatePostGetAccounts (params); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'CurrencyCode'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'AvailableBalance'); + account['total'] = this.safeFloat (balance, 'TotalBalance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + }; + const response = await this.publicGetGetOrderBook (this.extend (request, params)); + const timestamp = this.parse8601 (this.safeString (response, 'CreatedTimestampUtc')); + return this.parseOrderBook (response, timestamp, 'BuyOrders', 'SellOrders', 'Price', 'Volume'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.parse8601 (this.safeString (ticker, 'CreatedTimestampUtc')); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'LastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'DayHighestPrice'), + 'low': this.safeFloat (ticker, 'DayLowestPrice'), + 'bid': this.safeFloat (ticker, 'CurrentHighestBidPrice'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'CurrentLowestOfferPrice'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'DayAvgPrice'), + 'baseVolume': this.safeFloat (ticker, 'DayVolumeXbtInSecondaryCurrrency'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + }; + const response = await this.publicGetGetMarketSummary (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + const baseId = this.safeString (order, 'PrimaryCurrencyCode'); + const quoteId = this.safeString (order, 'PrimaryCurrencyCode'); + let base = undefined; + let quote = undefined; + if ((baseId !== undefined) && (quoteId !== undefined)) { + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + quote = market['quote']; + } + let orderType = this.safeValue (order, 'Type'); + if (orderType.indexOf ('Market') >= 0) { + orderType = 'market'; + } else if (orderType.indexOf ('Limit') >= 0) { + orderType = 'limit'; + } + let side = undefined; + if (orderType.indexOf ('Bid') >= 0) { + side = 'buy'; + } else if (orderType.indexOf ('Offer') >= 0) { + side = 'sell'; + } + const timestamp = this.parse8601 (this.safeString (order, 'CreatedTimestampUtc')); + let amount = this.safeFloat (order, 'VolumeOrdered'); + if (amount === undefined) { + amount = this.safeFloat (order, 'Volume'); + } + const filled = this.safeFloat (order, 'VolumeFilled'); + let remaining = undefined; + const feeRate = this.safeFloat (order, 'FeePercent'); + let feeCost = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + if (feeRate !== undefined) { + feeCost = feeRate * filled; + } + } + } + const fee = { + 'rate': feeRate, + 'cost': feeCost, + 'currency': base, + }; + const id = this.safeString (order, 'OrderGuid'); + const status = this.parseOrderStatus (this.safeString (order, 'Status')); + const cost = this.safeFloat (order, 'Value'); + const average = this.safeFloat (order, 'AvgPrice'); + const price = this.safeFloat (order, 'Price', average); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'Open': 'open', + 'PartiallyFilled': 'open', + 'Filled': 'closed', + 'PartiallyFilledAndCancelled': 'canceled', + 'Cancelled': 'canceled', + 'PartiallyFilledAndExpired': 'canceled', + 'Expired': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetOrderDetails (this.extend ({ + 'orderGuid': id, + }, params)); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseOrder (response, market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const pageIndex = this.safeInteger (params, 'pageIndex', 1); + if (limit === undefined) { + limit = 50; + } + const request = this.ordered ({ + 'pageIndex': pageIndex, + 'pageSize': limit, + }); + const response = await this.privatePostGetTrades (this.extend (request, params)); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseTrades (response['Data'], market, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (trade['TradeTimestampUtc']); + const id = this.safeString (trade, 'TradeGuid'); + const orderId = this.safeString (trade, 'OrderGuid'); + const price = this.safeFloat2 (trade, 'Price', 'SecondaryCurrencyTradePrice'); + const amount = this.safeFloat2 (trade, 'VolumeTraded', 'PrimaryCurrencyAmount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeString (trade, 'OrderType'); + if (side !== undefined) { + if (side.indexOf ('Bid') >= 0) { + side = 'buy'; + } else if (side.indexOf ('Offer') >= 0) { + side = 'sell'; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + 'numberOfRecentTradesToRetrieve': 50, // max = 50 + }; + const response = await this.publicGetGetRecentTrades (this.extend (request, params)); + return this.parseTrades (response['Trades'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const capitalizedOrderType = this.capitalize (type); + const method = 'privatePostPlace' + capitalizedOrderType + 'Order'; + let orderType = capitalizedOrderType; + orderType += (side === 'sell') ? 'Offer' : 'Bid'; + const request = this.ordered ({ + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + 'orderType': orderType, + }); + if (type === 'limit') { + request['price'] = price; + } + request['volume'] = amount; + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['OrderGuid'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderGuid': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const auth = [ + url, + 'apiKey=' + this.apiKey, + 'nonce=' + nonce.toString (), + ]; + const keys = Object.keys (params); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = params[key].toString (); + auth.push (key + '=' + value); + } + const message = auth.join (','); + const signature = this.hmac (this.encode (message), this.encode (this.secret)); + const query = this.ordered ({}); + query['apiKey'] = this.apiKey; + query['nonce'] = nonce; + query['signature'] = signature.toUpperCase (); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + query[key] = params[key]; + } + body = this.json (query); + headers = { 'Content-Type': 'application/json' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7}],98:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InsufficientFunds, InvalidOrder, OrderNotFound, AuthenticationError, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class indodax extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'indodax', + 'name': 'INDODAX', + 'countries': [ 'ID' ], // Indonesia + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchTickers': false, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchClosedOrders': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': false, + 'fetchCurrencies': false, + 'withdraw': true, + }, + 'version': '1.8', // as of 9 April 2018 + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/37443283-2fddd0e4-281c-11e8-9741-b4f1419001b5.jpg', + 'api': { + 'public': 'https://indodax.com/api', + 'private': 'https://indodax.com/tapi', + }, + 'www': 'https://www.indodax.com', + 'doc': 'https://indodax.com/downloads/BITCOINCOID-API-DOCUMENTATION.pdf', + 'referral': 'https://indodax.com/ref/testbitcoincoid/1', + }, + 'api': { + 'public': { + 'get': [ + '{pair}/ticker', + '{pair}/trades', + '{pair}/depth', + ], + }, + 'private': { + 'post': [ + 'getInfo', + 'transHistory', + 'trade', + 'tradeHistory', + 'getOrder', + 'openOrders', + 'cancelOrder', + 'orderHistory', + 'withdrawCoin', + ], + }, + }, + 'markets': { + // HARDCODING IS DEPRECATED + // but they don't have a corresponding endpoint in their API + // IDR markets + 'BTC/IDR': { 'id': 'btc_idr', 'symbol': 'BTC/IDR', 'base': 'BTC', 'quote': 'IDR', 'baseId': 'btc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.0001, 'max': undefined }}}, + 'TEN/IDR': { 'id': 'ten_idr', 'symbol': 'TEN/IDR', 'base': 'TEN', 'quote': 'IDR', 'baseId': 'ten', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'ABYSS/IDR': { 'id': 'abyss_idr', 'symbol': 'ABYSS/IDR', 'base': 'ABYSS', 'quote': 'IDR', 'baseId': 'abyss', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'ACT/IDR': { 'id': 'act_idr', 'symbol': 'ACT/IDR', 'base': 'ACT', 'quote': 'IDR', 'baseId': 'act', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'ADA/IDR': { 'id': 'ada_idr', 'symbol': 'ADA/IDR', 'base': 'ADA', 'quote': 'IDR', 'baseId': 'ada', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'AOA/IDR': { 'id': 'aoa_idr', 'symbol': 'AOA/IDR', 'base': 'AOA', 'quote': 'IDR', 'baseId': 'aoa', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'ATOM/IDR': { 'id': 'atom_idr', 'symbol': 'ATOM/IDR', 'base': 'ATOM', 'quote': 'IDR', 'baseId': 'atom', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'BAT/IDR': { 'id': 'bat_idr', 'symbol': 'BAT/IDR', 'base': 'BAT', 'quote': 'IDR', 'baseId': 'bat', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'BCD/IDR': { 'id': 'bcd_idr', 'symbol': 'BCD/IDR', 'base': 'BCD', 'quote': 'IDR', 'baseId': 'bcd', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'BCH/IDR': { 'id': 'bchabc_idr', 'symbol': 'BCH/IDR', 'base': 'BCH', 'quote': 'IDR', 'baseId': 'bchabc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'BSV/IDR': { 'id': 'bchsv_idr', 'symbol': 'BSV/IDR', 'base': 'BSV', 'quote': 'IDR', 'baseId': 'bchsv', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'BNB/IDR': { 'id': 'bnb_idr', 'symbol': 'BNB/IDR', 'base': 'BNB', 'quote': 'IDR', 'baseId': 'bnb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'BTG/IDR': { 'id': 'btg_idr', 'symbol': 'BTG/IDR', 'base': 'BTG', 'quote': 'IDR', 'baseId': 'btg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'BTS/IDR': { 'id': 'bts_idr', 'symbol': 'BTS/IDR', 'base': 'BTS', 'quote': 'IDR', 'baseId': 'bts', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'BTT/IDR': { 'id': 'btt_idr', 'symbol': 'BTT/IDR', 'base': 'BTT', 'quote': 'IDR', 'baseId': 'btt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1000, 'max': undefined }}}, + 'COAL/IDR': { 'id': 'coal_idr', 'symbol': 'COAL/IDR', 'base': 'COAL', 'quote': 'IDR', 'baseId': 'coal', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 50, 'max': undefined }}}, + 'CRO/IDR': { 'id': 'cro_idr', 'symbol': 'CRO/IDR', 'base': 'CRO', 'quote': 'IDR', 'baseId': 'cro', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DASH/IDR': { 'id': 'drk_idr', 'symbol': 'DASH/IDR', 'base': 'DASH', 'quote': 'IDR', 'baseId': 'drk', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DAX/IDR': { 'id': 'dax_idr', 'symbol': 'DAX/IDR', 'base': 'DAX', 'quote': 'IDR', 'baseId': 'dax', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DOGE/IDR': { 'id': 'doge_idr', 'symbol': 'DOGE/IDR', 'base': 'DOGE', 'quote': 'IDR', 'baseId': 'doge', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1000, 'max': undefined }}}, + 'ETH/IDR': { 'id': 'eth_idr', 'symbol': 'ETH/IDR', 'base': 'ETH', 'quote': 'IDR', 'baseId': 'eth', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'EOS/IDR': { 'id': 'eos_idr', 'symbol': 'EOS/IDR', 'base': 'EOS', 'quote': 'IDR', 'baseId': 'eos', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'ETC/IDR': { 'id': 'etc_idr', 'symbol': 'ETC/IDR', 'base': 'ETC', 'quote': 'IDR', 'baseId': 'etc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'GARD/IDR': { 'id': 'gard_idr', 'symbol': 'GARD/IDR', 'base': 'GARD', 'quote': 'IDR', 'baseId': 'gard', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'GSC/IDR': { 'id': 'gsc_idr', 'symbol': 'GSC/IDR', 'base': 'GSC', 'quote': 'IDR', 'baseId': 'gsc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'GXC/IDR': { 'id': 'gxs_idr', 'symbol': 'GXC/IDR', 'base': 'GXC', 'quote': 'IDR', 'baseId': 'gxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'HPB/IDR': { 'id': 'hpb_idr', 'symbol': 'HPB/IDR', 'base': 'HPB', 'quote': 'IDR', 'baseId': 'hpb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'IGNIS/IDR': { 'id': 'ignis_idr', 'symbol': 'IGNIS/IDR', 'base': 'IGNIS', 'quote': 'IDR', 'baseId': 'ignis', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'INX/IDR': { 'id': 'inx_idr', 'symbol': 'INX/IDR', 'base': 'INX', 'quote': 'IDR', 'baseId': 'inx', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'IOTA/IDR': { 'id': 'iota_idr', 'symbol': 'IOTA/IDR', 'base': 'IOTA', 'quote': 'IDR', 'baseId': 'iota', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'LINK/IDR': { 'id': 'link_idr', 'symbol': 'LINK/IDR', 'base': 'LINK', 'quote': 'IDR', 'baseId': 'link', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'LTC/IDR': { 'id': 'ltc_idr', 'symbol': 'LTC/IDR', 'base': 'LTC', 'quote': 'IDR', 'baseId': 'ltc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'MBL/IDR': { 'id': 'mbl_idr', 'symbol': 'MBL/IDR', 'base': 'MBL', 'quote': 'IDR', 'baseId': 'mbl', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'NEO/IDR': { 'id': 'neo_idr', 'symbol': 'NEO/IDR', 'base': 'NEO', 'quote': 'IDR', 'baseId': 'neo', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'NPXS/IDR': { 'id': 'npxs_idr', 'symbol': 'NPXS/IDR', 'base': 'NPXS', 'quote': 'IDR', 'baseId': 'npxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'NXT/IDR': { 'id': 'nxt_idr', 'symbol': 'NXT/IDR', 'base': 'NXT', 'quote': 'IDR', 'baseId': 'nxt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'OKB/IDR': { 'id': 'okb_idr', 'symbol': 'OKB/IDR', 'base': 'OKB', 'quote': 'IDR', 'baseId': 'okb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.5, 'max': undefined }}}, + 'ONT/IDR': { 'id': 'ont_idr', 'symbol': 'ONT/IDR', 'base': 'ONT', 'quote': 'IDR', 'baseId': 'ont', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'PXG/IDR': { 'id': 'pxg_idr', 'symbol': 'PXG/IDR', 'base': 'PXG', 'quote': 'IDR', 'baseId': 'pxg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'QTUM/IDR': { 'id': 'qtum_idr', 'symbol': 'QTUM/IDR', 'base': 'QTUM', 'quote': 'IDR', 'baseId': 'qtum', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'RVN/IDR': { 'id': 'rvn_idr', 'symbol': 'RVN/IDR', 'base': 'RVN', 'quote': 'IDR', 'baseId': 'rvn', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'SSP/IDR': { 'id': 'ssp_idr', 'symbol': 'SSP/IDR', 'base': 'SSP', 'quote': 'IDR', 'baseId': 'ssp', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'SUMO/IDR': { 'id': 'sumo_idr', 'symbol': 'SUMO/IDR', 'base': 'SUMO', 'quote': 'IDR', 'baseId': 'sumo', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + // 'STQ/IDR': { 'id': 'stq_idr', 'symbol': 'STQ/IDR', 'base': 'STQ', 'quote': 'IDR', 'baseId': 'stq', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'TRX/IDR': { 'id': 'trx_idr', 'symbol': 'TRX/IDR', 'base': 'TRX', 'quote': 'IDR', 'baseId': 'trx', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'USDC/IDR': { 'id': 'usdc_idr', 'symbol': 'USDC/IDR', 'base': 'USDC', 'quote': 'IDR', 'baseId': 'usdc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'USDT/IDR': { 'id': 'usdt_idr', 'symbol': 'USDT/IDR', 'base': 'USDT', 'quote': 'IDR', 'baseId': 'usdt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'VEX/IDR': { 'id': 'vex_idr', 'symbol': 'VEX/IDR', 'base': 'VEX', 'quote': 'IDR', 'baseId': 'vex', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'VIDY/IDR': { 'id': 'vidy_idr', 'symbol': 'VIDY/IDR', 'base': 'VIDY', 'quote': 'IDR', 'baseId': 'vidy', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 100, 'max': undefined }}}, + 'WAVES/IDR': { 'id': 'waves_idr', 'symbol': 'WAVES/IDR', 'base': 'WAVES', 'quote': 'IDR', 'baseId': 'waves', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'XEM/IDR': { 'id': 'nem_idr', 'symbol': 'XEM/IDR', 'base': 'XEM', 'quote': 'IDR', 'baseId': 'nem', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'XLM/IDR': { 'id': 'str_idr', 'symbol': 'XLM/IDR', 'base': 'XLM', 'quote': 'IDR', 'baseId': 'str', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 20, 'max': undefined }}}, + 'XDCE/IDR': { 'id': 'xdce_idr', 'symbol': 'XDCE/IDR', 'base': 'XDCE', 'quote': 'IDR', 'baseId': 'xdce', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}}, + 'XMR/IDR': { 'id': 'xmr_idr', 'symbol': 'XMR/IDR', 'base': 'XMR', 'quote': 'IDR', 'baseId': 'xmr', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'XRP/IDR': { 'id': 'xrp_idr', 'symbol': 'XRP/IDR', 'base': 'XRP', 'quote': 'IDR', 'baseId': 'xrp', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}}, + 'XZC/IDR': { 'id': 'xzc_idr', 'symbol': 'XZC/IDR', 'base': 'XZC', 'quote': 'IDR', 'baseId': 'xzc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'VSYS/IDR': { 'id': 'vsys_idr', 'symbol': 'VSYS/IDR', 'base': 'VSYS', 'quote': 'IDR', 'baseId': 'vsys', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'ZEC/IDR': { 'id': 'zec_idr', 'symbol': 'ZEC/IDR', 'base': 'ZEC', 'quote': 'IDR', 'baseId': 'zec', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + // BTC markets + 'BTS/BTC': { 'id': 'bts_btc', 'symbol': 'BTS/BTC', 'base': 'BTS', 'quote': 'BTC', 'baseId': 'bts', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DASH/BTC': { 'id': 'drk_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'drk', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 6 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DOGE/BTC': { 'id': 'doge_btc', 'symbol': 'DOGE/BTC', 'base': 'DOGE', 'quote': 'BTC', 'baseId': 'doge', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 5 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 6 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'NXT/BTC': { 'id': 'nxt_btc', 'symbol': 'NXT/BTC', 'base': 'NXT', 'quote': 'BTC', 'baseId': 'nxt', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'SUMO/BTC': { 'id': 'sumo_btc', 'symbol': 'SUMO/BTC', 'base': 'SUMO', 'quote': 'BTC', 'baseId': 'sumo', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'TEN/BTC': { 'id': 'ten_btc', 'symbol': 'TEN/BTC', 'base': 'TEN', 'quote': 'BTC', 'baseId': 'ten', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'XEM/BTC': { 'id': 'nem_btc', 'symbol': 'XEM/BTC', 'base': 'XEM', 'quote': 'BTC', 'baseId': 'nem', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'XLM/BTC': { 'id': 'str_btc', 'symbol': 'XLM/BTC', 'base': 'XLM', 'quote': 'BTC', 'baseId': 'str', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'XRP/BTC': { 'id': 'xrp_btc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0, + 'taker': 0.003, + }, + }, + 'exceptions': { + 'exact': { + 'invalid_pair': BadSymbol, // {"error":"invalid_pair","error_description":"Invalid Pair"} + 'Insufficient balance.': InsufficientFunds, + 'invalid order.': OrderNotFound, + 'Invalid credentials. API not found or session has expired.': AuthenticationError, + 'Invalid credentials. Bad sign.': AuthenticationError, + }, + 'broad': { + 'Minimum price': InvalidOrder, + 'Minimum order': InvalidOrder, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfo (params); + const balances = this.safeValue (response, 'return', {}); + const free = this.safeValue (balances, 'balance', {}); + const used = this.safeValue (balances, 'balance_hold', {}); + const result = { 'info': response }; + const currencyIds = Object.keys (free); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['used'] = this.safeFloat (used, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const orderbook = await this.publicGetPairDepth (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'buy', 'sell'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTicker (this.extend (request, params)); + // + // { + // "ticker": { + // "high":"0.01951", + // "low":"0.01877", + // "vol_eth":"39.38839319", + // "vol_btc":"0.75320886", + // "last":"0.01896", + // "buy":"0.01896", + // "sell":"0.019", + // "server_time":1565248908 + // } + // } + // + const ticker = response['ticker']; + const timestamp = this.safeTimestamp (ticker, 'server_time'); + const baseVolume = 'vol_' + market['baseId'].toLowerCase (); + const quoteVolume = 'vol_' + market['quoteId'].toLowerCase (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, baseVolume), + 'quoteVolume': this.safeFloat (ticker, quoteVolume), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + let side = undefined; + if ('type' in order) { + side = order['type']; + } + let status = this.safeString (order, 'status', 'open'); + if (status === 'filled') { + status = 'closed'; + } else if (status === 'cancelled') { + status = 'canceled'; + } + let symbol = undefined; + let cost = undefined; + const price = this.safeFloat (order, 'price'); + let amount = undefined; + let remaining = undefined; + let filled = undefined; + if (market !== undefined) { + symbol = market['symbol']; + let quoteId = market['quoteId']; + let baseId = market['baseId']; + if ((market['quoteId'] === 'idr') && ('order_rp' in order)) { + quoteId = 'rp'; + } + if ((market['baseId'] === 'idr') && ('remain_rp' in order)) { + baseId = 'rp'; + } + cost = this.safeFloat (order, 'order_' + quoteId); + if (cost) { + amount = cost / price; + const remainingCost = this.safeFloat (order, 'remain_' + quoteId); + if (remainingCost !== undefined) { + remaining = remainingCost / price; + filled = amount - remaining; + } + } else { + amount = this.safeFloat (order, 'order_' + baseId); + cost = price * amount; + remaining = this.safeFloat (order, 'remain_' + baseId); + filled = amount - remaining; + } + } + let average = undefined; + if (filled) { + average = cost / filled; + } + const timestamp = this.safeInteger (order, 'submit_time'); + const fee = undefined; + const id = this.safeString (order, 'order_id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrder requires a symbol'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'order_id': id, + }; + const response = await this.privatePostGetOrder (this.extend (request, params)); + const orders = response['return']; + const order = this.parseOrder (this.extend ({ 'id': id }, orders['order']), market); + return this.extend ({ 'info': response }, order); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostOpenOrders (this.extend (request, params)); + const rawOrders = response['return']['orders']; + // { success: 1, return: { orders: null }} if no orders + if (!rawOrders) { + return []; + } + // { success: 1, return: { orders: [ ... objects ] }} for orders fetched by symbol + if (symbol !== undefined) { + return this.parseOrders (rawOrders, market, since, limit); + } + // { success: 1, return: { orders: { marketid: [ ... objects ] }}} if all orders are fetched + const marketIds = Object.keys (rawOrders); + let exchangeOrders = []; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const marketOrders = rawOrders[marketId]; + market = this.markets_by_id[marketId]; + const parsedOrders = this.parseOrders (marketOrders, market, since, limit); + exchangeOrders = this.arrayConcat (exchangeOrders, parsedOrders); + } + return exchangeOrders; + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrders requires a symbol'); + } + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostOrderHistory (this.extend (request, params)); + let orders = this.parseOrders (response['return']['orders'], market, since, limit); + orders = this.filterBy (orders, 'status', 'closed'); + if (symbol !== undefined) { + return this.filterBySymbol (orders, symbol); + } + return orders; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'price': price, + }; + const currency = market['baseId']; + if (side === 'buy') { + request[market['quoteId']] = amount * price; + } else { + request[market['baseId']] = amount; + } + request[currency] = amount; + const result = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': result, + 'id': result['return']['order_id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + const side = this.safeValue (params, 'side'); + if (side === undefined) { + throw new ExchangeError (this.id + ' cancelOrder requires an extra "side" param'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'pair': market['id'], + 'type': side, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // Custom string you need to provide to identify each withdrawal. + // Will be passed to callback URL (assigned via website to the API key) + // so your system can identify the request and confirm it. + // Alphanumeric, max length 255. + const requestId = this.milliseconds (); + // Alternatively: + // let requestId = this.uuid (); + const request = { + 'currency': currency['id'], + 'withdraw_amount': amount, + 'withdraw_address': address, + 'request_id': requestId.toString (), + }; + if (tag) { + request['withdraw_memo'] = tag; + } + const response = await this.privatePostWithdrawCoin (this.extend (request, params)); + // + // { + // "success": 1, + // "status": "approved", + // "withdraw_currency": "xrp", + // "withdraw_address": "rwWr7KUZ3ZFwzgaDGjKBysADByzxvohQ3C", + // "withdraw_amount": "10000.00000000", + // "fee": "2.00000000", + // "amount_after_fee": "9998.00000000", + // "submit_time": "1509469200", + // "withdraw_id": "xrp-12345", + // "txid": "", + // "withdraw_memo": "123123" + // } + // + let id = undefined; + if (('txid' in response) && (response['txid'].length > 0)) { + id = response['txid']; + } + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api === 'public') { + url += '/' + this.implodeParams (path, params); + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'method': path, + 'nonce': this.nonce (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // { success: 0, error: "invalid order." } + // or + // [{ data, ... }, { ... }, ... ] + if (Array.isArray (response)) { + return; // public endpoints may return []-arrays + } + const error = this.safeValue (response, 'error', ''); + if (!('success' in response) && error === '') { + return; // no 'success' property on public responses + } + if (this.safeInteger (response, 'success', 0) === 1) { + // { success: 1, return: { orders: [] }} + if (!('return' in response)) { + throw new ExchangeError (this.id + ': malformed response: ' + this.json (response)); + } else { + return; + } + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown message + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],99:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class itbit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'itbit', + 'name': 'itBit', + 'countries': [ 'US' ], + 'rateLimit': 2000, + 'version': 'v1', + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27822159-66153620-60ad-11e7-89e7-005f6d7f3de0.jpg', + 'api': 'https://api.itbit.com', + 'www': 'https://www.itbit.com', + 'doc': [ + 'https://api.itbit.com/docs', + 'https://www.itbit.com/api', + ], + }, + 'api': { + 'public': { + 'get': [ + 'markets/{symbol}/ticker', + 'markets/{symbol}/order_book', + 'markets/{symbol}/trades', + ], + }, + 'private': { + 'get': [ + 'wallets', + 'wallets/{walletId}', + 'wallets/{walletId}/balances/{currencyCode}', + 'wallets/{walletId}/funding_history', + 'wallets/{walletId}/trades', + 'wallets/{walletId}/orders', + 'wallets/{walletId}/orders/{id}', + ], + 'post': [ + 'wallet_transfers', + 'wallets', + 'wallets/{walletId}/cryptocurrency_deposits', + 'wallets/{walletId}/cryptocurrency_withdrawals', + 'wallets/{walletId}/orders', + 'wire_withdrawal', + ], + 'delete': [ + 'wallets/{walletId}/orders/{id}', + ], + }, + }, + 'markets': { + 'BTC/USD': { 'id': 'XBTUSD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD' }, + 'BTC/SGD': { 'id': 'XBTSGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD' }, + 'BTC/EUR': { 'id': 'XBTEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR' }, + 'ETH/USD': { 'id': 'ETHUSD', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD' }, + 'ETH/EUR': { 'id': 'ETHEUR', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR' }, + 'ETH/SGD': { 'id': 'ETHSGD', 'symbol': 'ETH/SGD', 'base': 'ETH', 'quote': 'SGD' }, + }, + 'fees': { + 'trading': { + 'maker': -0.03 / 100, + 'taker': 0.35 / 100, + }, + }, + 'commonCurrencies': { + 'XBT': 'BTC', + }, + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const orderbook = await this.publicGetMarketsSymbolOrderBook (this.extend (request, params)); + return this.parseOrderBook (orderbook); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const ticker = await this.publicGetMarketsSymbolTicker (this.extend (request, params)); + const serverTimeUTC = this.safeString (ticker, 'serverTimeUTC'); + if (!serverTimeUTC) { + throw new ExchangeError (this.id + ' fetchTicker returned a bad response: ' + this.json (ticker)); + } + const timestamp = this.parse8601 (serverTimeUTC); + const vwap = this.safeFloat (ticker, 'vwap24h'); + const baseVolume = this.safeFloat (ticker, 'volume24h'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high24h'), + 'low': this.safeFloat (ticker, 'low24h'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'openToday'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // timestamp: "2015-05-22T17:45:34.7570000Z", + // matchNumber: "5CR1JEUBBM8J", + // price: "351.45000000", + // amount: "0.00010000" + // } + // + // fetchMyTrades (private) + // + // { + // "orderId": "248ffda4-83a0-4033-a5bb-8929d523f59f", + // "timestamp": "2015-05-11T14:48:01.9870000Z", + // "instrument": "XBTUSD", + // "direction": "buy", // buy or sell + // "currency1": "XBT", // base currency + // "currency1Amount": "0.00010000", // order amount in base currency + // "currency2": "USD", // quote currency + // "currency2Amount": "0.0250530000000000", // order cost in quote currency + // "rate": "250.53000000", + // "commissionPaid": "0.00000000", // net trade fee paid after using any available rebate balance + // "commissionCurrency": "USD", + // "rebatesApplied": "-0.000125265", // negative values represent amount of rebate balance used for trades removing liquidity from order book; positive values represent amount of rebate balance earned from trades adding liquidity to order book + // "rebateCurrency": "USD", + // "executionId": "23132" + // } + // + const id = this.safeString2 (trade, 'executionId', 'matchNumber'); + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const side = this.safeString (trade, 'direction'); + const orderId = this.safeString (trade, 'orderId'); + let feeCost = this.safeFloat (trade, 'commissionPaid'); + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + let rebatesApplied = this.safeFloat (trade, 'rebatesApplied'); + if (rebatesApplied !== undefined) { + rebatesApplied = -rebatesApplied; + } + const rebateCurrencyId = this.safeString (trade, 'rebateCurrency'); + const rebateCurrency = this.safeCurrencyCode (rebateCurrencyId); + const price = this.safeFloat2 (trade, 'price', 'rate'); + const amount = this.safeFloat2 (trade, 'currency1Amount', 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + const marketId = this.safeString (trade, 'instrument'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (trade, 'currency1'); + const quoteId = this.safeString (trade, 'currency2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const result = { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + if (feeCost !== undefined) { + if (rebatesApplied !== undefined) { + if (feeCurrency === rebateCurrency) { + feeCost = this.sum (feeCost, rebatesApplied); + result['fee'] = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } else { + result['fees'] = [ + { + 'cost': feeCost, + 'currency': feeCurrency, + }, + { + 'cost': rebatesApplied, + 'currency': rebateCurrency, + }, + ]; + } + } else { + result['fee'] = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + } + if (!('fee' in result)) { + if (!('fees' in result)) { + result['fee'] = undefined; + } + } + return result; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const walletId = this.safeString (params, 'walletId'); + if (walletId === undefined) { + throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter'); + } + const request = { + 'walletId': walletId, + }; + if (limit !== undefined) { + request['perPage'] = limit; // default 50, max 50 + } + const response = await this.privateGetWalletsWalletIdFundingHistory (this.extend (request, params)); + // { bankName: 'USBC (usd)', + // withdrawalId: 94740, + // holdingPeriodCompletionDate: '2018-04-16T07:57:05.9606869', + // time: '2018-04-16T07:57:05.9600000', + // currency: 'USD', + // transactionType: 'Withdrawal', + // amount: '2186.72000000', + // walletName: 'Wallet', + // status: 'completed' }, + // + // { "time": "2018-01-02T19:52:22.4176503", + // "amount": "0.50000000", + // "status": "completed", + // "txnHash": "1b6fff67ed83cb9e9a38ca4976981fc047322bc088430508fe764a127d3ace95", + // "currency": "XBT", + // "walletName": "Wallet", + // "transactionType": "Deposit", + // "destinationAddress": "3AAWTH9et4e8o51YKp9qPpmujrNXKwHWNX"} + const items = response['fundingHistory']; + const result = []; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const time = this.safeString (item, 'time'); + const timestamp = this.parse8601 (time); + const currency = this.safeString (item, 'currency'); + const destinationAddress = this.safeString (item, 'destinationAddress'); + const txnHash = this.safeString (item, 'txnHash'); + const transactionType = this.safeStringLower (item, 'transactionType'); + const transactionStatus = this.safeString (item, 'status'); + const status = this.parseTransferStatus (transactionStatus); + result.push ({ + 'id': this.safeString (item, 'withdrawalId'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'currency': this.safeCurrencyCode (currency), + 'address': destinationAddress, + 'tag': undefined, + 'txid': txnHash, + 'type': transactionType, + 'status': status, + 'amount': this.safeFloat (item, 'amount'), + 'fee': undefined, + 'info': item, + }); + } + return result; + } + + parseTransferStatus (status) { + const options = { + 'cancelled': 'canceled', + 'completed': 'ok', + }; + return this.safeString (options, status, 'pending'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const walletId = this.safeString (params, 'walletId'); + if (walletId === undefined) { + throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter'); + } + const request = { + 'walletId': walletId, + }; + if (since !== undefined) { + request['rangeStart'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['perPage'] = limit; // default 50, max 50 + } + const response = await this.privateGetWalletsWalletIdTrades (this.extend (request, params)); + // + // { + // "totalNumberOfRecords": "2", + // "currentPageNumber": "1", + // "latestExecutionId": "332", // most recent execution at time of response + // "recordsPerPage": "50", + // "tradingHistory": [ + // { + // "orderId": "248ffda4-83a0-4033-a5bb-8929d523f59f", + // "timestamp": "2015-05-11T14:48:01.9870000Z", + // "instrument": "XBTUSD", + // "direction": "buy", // buy or sell + // "currency1": "XBT", // base currency + // "currency1Amount": "0.00010000", // order amount in base currency + // "currency2": "USD", // quote currency + // "currency2Amount": "0.0250530000000000", // order cost in quote currency + // "rate": "250.53000000", + // "commissionPaid": "0.00000000", // net trade fee paid after using any available rebate balance + // "commissionCurrency": "USD", + // "rebatesApplied": "-0.000125265", // negative values represent amount of rebate balance used for trades removing liquidity from order book; positive values represent amount of rebate balance earned from trades adding liquidity to order book + // "rebateCurrency": "USD", + // "executionId": "23132" + // }, + // ], + // } + // + const trades = this.safeValue (response, 'tradingHistory', []); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseTrades (trades, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetMarketsSymbolTrades (this.extend (request, params)); + // + // { + // count: 3, + // recentTrades: [ + // { + // timestamp: "2015-05-22T17:45:34.7570000Z", + // matchNumber: "5CR1JEUBBM8J", + // price: "351.45000000", + // amount: "0.00010000" + // }, + // ] + // } + // + const trades = this.safeValue (response, 'recentTrades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.fetchWallets (params); + const balances = response[0]['balances']; + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableBalance'); + account['total'] = this.safeFloat (balance, 'totalBalance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchWallets (params = {}) { + await this.loadMarkets (); + if (!this.uid) { + throw new AuthenticationError (this.id + ' fetchWallets requires uid API credential'); + } + const request = { + 'userId': this.uid, + }; + return await this.privateGetWallets (this.extend (request, params)); + } + + async fetchWallet (walletId, params = {}) { + await this.loadMarkets (); + const request = { + 'walletId': walletId, + }; + return await this.privateGetWalletsWalletId (this.extend (request, params)); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'open', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'filled', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' fetchOrders requires a walletId parameter'); + } + const walletId = params['walletId']; + const request = { + 'walletId': walletId, + }; + const response = await this.privateGetWalletsWalletIdOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'submitted': 'open', // order pending book entry + 'open': 'open', + 'filled': 'closed', + 'cancelled': 'canceled', + 'rejected': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const side = order['side']; + const type = order['type']; + const symbol = this.markets_by_id[order['instrument']]['symbol']; + const timestamp = this.parse8601 (order['createdTime']); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'amountFilled'); + const remaining = amount - filled; + const fee = undefined; + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'volumeWeightedAveragePrice'); + const cost = filled * average; + return { + 'id': order['id'], + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': this.parseOrderStatus (this.safeString (order, 'status')), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + // 'trades': this.parseTrades (order['trades'], market), + }; + } + + nonce () { + return this.milliseconds (); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' createOrder requires a walletId parameter'); + } + amount = amount.toString (); + price = price.toString (); + const market = this.market (symbol); + const request = { + 'side': side, + 'type': type, + 'currency': market['id'].replace (market['quote'], ''), + 'amount': amount, + 'display': amount, + 'price': price, + 'instrument': market['id'], + }; + const response = await this.privatePostWalletsWalletIdOrders (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' fetchOrder requires a walletId parameter'); + } + const request = { + 'id': id, + }; + const response = await this.privateGetWalletsWalletIdOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' cancelOrder requires a walletId parameter'); + } + const request = { + 'id': id, + }; + return await this.privateDeleteWalletsWalletIdOrdersId (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET' && Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + if (method === 'POST' && Object.keys (query).length) { + body = this.json (query); + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const timestamp = nonce; + const authBody = (method === 'POST') ? body : ''; + const auth = [ method, url, authBody, nonce, timestamp ]; + const message = nonce + this.json (auth).replace ('\\/', '/'); + const hash = this.hash (this.encode (message), 'sha256', 'binary'); + const binaryUrl = this.stringToBinary (this.encode (url)); + const binhash = this.binaryConcat (binaryUrl, hash); + const signature = this.hmac (binhash, this.encode (this.secret), 'sha512', 'base64'); + headers = { + 'Authorization': this.apiKey + ':' + this.decode (signature), + 'Content-Type': 'application/json', + 'X-Auth-Timestamp': timestamp, + 'X-Auth-Nonce': nonce, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('code' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],100:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, InvalidOrder, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class kkex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kkex', + 'name': 'KKEX', + 'countries': [ 'CN', 'US', 'JP' ], + 'version': 'v2', + 'has': { + 'CORS': false, + 'fetchBalance': true, + 'fetchTickers': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'createMarketOrder': true, + 'fetchOrder': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '4h': '4hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + '1M': '1month', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/47401462-2e59f800-d74a-11e8-814f-e4ae17b4968a.jpg', + 'api': { + 'public': 'https://kkex.com/api/v1', + 'private': 'https://kkex.com/api/v2', + 'v1': 'https://kkex.com/api/v1', + }, + 'www': 'https://kkex.com', + 'doc': 'https://kkex.com/api_wiki/cn/', + 'fees': 'https://intercom.help/kkex/fee', + }, + 'api': { + 'public': { + 'get': [ + 'exchange_rate', + 'products', + 'assets', + 'tickers', + 'ticker', + 'depth', + 'trades', + 'kline', + ], + }, + 'private': { + 'post': [ + 'profile', + 'trade', + 'batch_trade', + 'cancel_order', + 'cancel_all_orders', + 'order_history', + 'userinfo', + 'order_info', + 'orders_info', + ], + }, + 'v1': { + 'post': [ + 'process_strategy', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.002, + 'maker': 0.002, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'options': { + 'lastNonceTimestamp': 0, + }, + }); + } + + async fetchMarkets (params = {}) { + let tickers = await this.publicGetTickers (params); + tickers = tickers['tickers']; + let products = await this.publicGetProducts (params); + products = products['products']; + const markets = []; + for (let k = 0; k < tickers.length; k++) { + const keys = Object.keys (tickers[k]); + markets.push (keys[0]); + } + const result = []; + for (let i = 0; i < markets.length; i++) { + const id = markets[i]; + const market = markets[i]; + let baseId = ''; + let quoteId = ''; + let precision = {}; + let limits = {}; + for (let j = 0; j < products.length; j++) { + const p = products[j]; + if (p['mark_asset'] + p['base_asset'] === market) { + quoteId = p['base_asset']; + baseId = p['mark_asset']; + const price_scale_str = p['price_scale'].toString (); + const scale = price_scale_str.length - 1; + precision = { + 'price': scale, + 'amount': scale, + }; + limits = { + 'amount': { + 'min': Math.max (this.safeFloat (p, 'min_bid_size'), this.safeFloat (p, 'min_ask_size')), + 'max': Math.min (this.safeFloat (p, 'max_bid_size'), this.safeFloat (p, 'max_ask_size')), + }, + 'price': { + 'min': this.safeFloat (p, 'min_price'), + 'max': this.safeFloat (p, 'max_price'), + }, + }; + limits['cost'] = { + 'min': this.safeFloat (p, 'min_bid_amount'), + 'max': this.safeFloat (p, 'max_bid_amount'), + }; + } + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.markets[symbol]; + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.extend (response['ticker'], this.omit (response, 'ticker')); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + // + // { date: 1540350657, + // tickers: [ { ENUBTC: { sell: "0.00000256", + // buy: "0.00000253", + // last: "0.00000253", + // vol: "138686.828804", + // high: "0.00000278", + // low: "0.00000253", + // open: "0.0000027" } }, + // { ENUEOS: { sell: "0.00335", + // buy: "0.002702", + // last: "0.0034", + // vol: "15084.9", + // high: "0.0034", + // low: "0.003189", + // open: "0.003189" } } ], + // result: true } + // + const tickers = this.safeValue (response, 'tickers'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ids = Object.keys (tickers[i]); + const id = ids[0]; + const market = this.safeValue (this.markets_by_id, id); + if (market !== undefined) { + const symbol = market['symbol']; + const ticker = this.extend (tickers[i][id], this.omit (response, 'tickers')); + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'date_ms'); + const datetime = this.iso8601 (timestamp); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': datetime, + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserinfo (params); + const balances = this.safeValue (response, 'info'); + const result = { 'info': response }; + const funds = this.safeValue (balances, 'funds'); + const free = this.safeValue (funds, 'free', {}); + const freezed = this.safeValue (funds, 'freezed', {}); + const currencyIds = Object.keys (free); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['used'] = this.safeFloat (freezed, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (!symbol) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'symbol': market['id'], + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + if (response['result']) { + return this.parseOrder (response['order'], market); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + parseInt (ohlcv[0]), + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'type': this.timeframes[timeframe], + }; + if (since !== undefined) { + // since = this.milliseconds () - this.parseTimeframe (timeframe) * limit * 1000; + request['since'] = parseInt (since / 1000); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetKline (this.extend (request, params)); + // + // [ + // [ + // "1521072000000", + // "0.000002", + // "0.00003", + // "0.000002", + // "0.00003", + // "3.106889" + // ], + // [ + // "1517356800000", + // "0.1", + // "0.1", + // "0.00000013", + // "0.000001", + // "542832.83114" + // ] + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + '-1': 'canceled', + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeString (order, 'side'); + if (side === undefined) { + side = this.safeString (order, 'type'); + } + const timestamp = this.safeInteger (order, 'create_date'); + const id = this.safeString2 (order, 'order_id', 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'deal_amount'); + let average = this.safeFloat (order, 'avg_price'); + average = this.safeFloat (order, 'price_avg', average); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (average !== undefined) { + cost = average * filled; + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'average': average, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'type': side, + }; + if (type === 'market') { + // for market buy it requires the amount of quote currency to spend + if (side === 'buy') { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + request['amount'] = this.costToPrecision (symbol, parseFloat (amount) * parseFloat (price)); + } + } + request['price'] = this.costToPrecision (symbol, amount); + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + request['type'] += '_' + type; + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostTrade (this.extend (request, params)); + const id = this.safeString (response, 'order_id'); + return { + 'info': response, + 'id': id, + 'datetime': undefined, + 'timestamp': undefined, + 'lastTradeTimestamp': undefined, + 'status': 'open', + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'symbol': market['id'], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['page_length'] = limit; // 20 by default + } + const response = await this.privatePostOrderHistory (this.extend (request, params)); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 0, + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 1, + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'public') { + url += '?' + this.urlencode (params); + headers = { 'Content-Type': 'application/json' }; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + let signature = this.extend ({ + 'nonce': nonce, + 'api_key': this.apiKey, + }, params); + signature = this.urlencode (this.keysort (signature)); + signature += '&secret_key=' + this.secret; + signature = this.hash (this.encode (signature), 'md5'); + signature = signature.toUpperCase (); + body = this.extend ({ + 'api_key': this.apiKey, + 'sign': signature, + 'nonce': nonce, + }, params); + body = this.urlencode (body); + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],101:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeNotAvailable, ArgumentsRequired, PermissionDenied, AuthenticationError, ExchangeError, OrderNotFound, DDoSProtection, InvalidNonce, InsufficientFunds, CancelPending, InvalidOrder, InvalidAddress, NotSupported } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class kraken extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kraken', + 'name': 'Kraken', + 'countries': [ 'US' ], + 'version': '0', + 'rateLimit': 3000, + 'certified': true, + 'has': { + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'CORS': false, + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchWithdrawals': true, + 'fetchDeposits': true, + 'withdraw': true, + 'fetchLedgerEntry': true, + 'fetchLedger': true, + }, + 'marketsByAltname': {}, + 'timeframes': { + '1m': 1, + '5m': 5, + '15m': 15, + '30m': 30, + '1h': 60, + '4h': 240, + '1d': 1440, + '1w': 10080, + '2w': 21600, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766599-22709304-5ede-11e7-9de1-9f33732e1509.jpg', + 'api': { + 'public': 'https://api.kraken.com', + 'private': 'https://api.kraken.com', + 'zendesk': 'https://support.kraken.com/hc/en-us/articles', + }, + 'www': 'https://www.kraken.com', + 'doc': 'https://www.kraken.com/features/api', + 'fees': 'https://www.kraken.com/en-us/features/fee-schedule', + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.26 / 100, + 'maker': 0.16 / 100, + 'tiers': { + 'taker': [ + [0, 0.0026], + [50000, 0.0024], + [100000, 0.0022], + [250000, 0.0020], + [500000, 0.0018], + [1000000, 0.0016], + [2500000, 0.0014], + [5000000, 0.0012], + [10000000, 0.0001], + ], + 'maker': [ + [0, 0.0016], + [50000, 0.0014], + [100000, 0.0012], + [250000, 0.0010], + [500000, 0.0008], + [1000000, 0.0006], + [2500000, 0.0004], + [5000000, 0.0002], + [10000000, 0.0], + ], + }, + }, + // this is a bad way of hardcoding fees that change on daily basis + // hardcoding is now considered obsolete, we will remove all of it eventually + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'ETH': 0.005, + 'XRP': 0.02, + 'XLM': 0.00002, + 'LTC': 0.02, + 'DOGE': 2, + 'ZEC': 0.00010, + 'ICN': 0.02, + 'REP': 0.01, + 'ETC': 0.005, + 'MLN': 0.003, + 'XMR': 0.05, + 'DASH': 0.005, + 'GNO': 0.01, + 'EOS': 0.5, + 'BCH': 0.001, + 'XTZ': 0.05, + 'USD': 5, // if domestic wire + 'EUR': 5, // if domestic wire + 'CAD': 10, // CAD EFT Withdrawal + 'JPY': 300, // if domestic wire + }, + 'deposit': { + 'BTC': 0, + 'ETH': 0, + 'XRP': 0, + 'XLM': 0, + 'LTC': 0, + 'DOGE': 0, + 'ZEC': 0, + 'ICN': 0, + 'REP': 0, + 'ETC': 0, + 'MLN': 0, + 'XMR': 0, + 'DASH': 0, + 'GNO': 0, + 'EOS': 0, + 'BCH': 0, + 'XTZ': 0.05, + 'USD': 5, // if domestic wire + 'EUR': 0, // free deposit if EUR SEPA Deposit + 'CAD': 5, // if domestic wire + 'JPY': 0, // Domestic Deposit (Free, ¥5,000 deposit minimum) + }, + }, + }, + 'api': { + 'zendesk': { + 'get': [ + // we should really refrain from putting fixed fee numbers and stop hardcoding + // we will be using their web APIs to scrape all numbers from these articles + '205893708-What-is-the-minimum-order-size-', + '201396777-What-are-the-deposit-fees-', + '201893608-What-are-the-withdrawal-fees-', + ], + }, + 'public': { + 'get': [ + 'Assets', + 'AssetPairs', + 'Depth', + 'OHLC', + 'Spread', + 'Ticker', + 'Time', + 'Trades', + ], + }, + 'private': { + 'post': [ + 'AddOrder', + 'AddExport', + 'Balance', + 'CancelOrder', + 'ClosedOrders', + 'DepositAddresses', + 'DepositMethods', + 'DepositStatus', + 'ExportStatus', + 'GetWebSocketsToken', + 'Ledgers', + 'OpenOrders', + 'OpenPositions', + 'QueryLedgers', + 'QueryOrders', + 'QueryTrades', + 'RetrieveExport', + 'RemoveExport', + 'TradeBalance', + 'TradesHistory', + 'TradeVolume', + 'Withdraw', + 'WithdrawCancel', + 'WithdrawInfo', + 'WithdrawStatus', + ], + }, + }, + 'commonCurrencies': { + 'XBT': 'BTC', + 'XDG': 'DOGE', + }, + 'options': { + 'cacheDepositMethodsOnFetchDepositAddress': true, // will issue up to two calls in fetchDepositAddress + 'depositMethods': {}, + 'delistedMarketsById': {}, + // cannot withdraw/deposit these + 'inactiveCurrencies': [ 'CAD', 'USD', 'JPY', 'GBP' ], + }, + 'exceptions': { + 'EQuery:Invalid asset pair': BadSymbol, // {"error":["EQuery:Invalid asset pair"]} + 'EAPI:Invalid key': AuthenticationError, + 'EFunding:Unknown withdraw key': ExchangeError, + 'EFunding:Invalid amount': InsufficientFunds, + 'EService:Unavailable': ExchangeNotAvailable, + 'EDatabase:Internal error': ExchangeNotAvailable, + 'EService:Busy': ExchangeNotAvailable, + 'EQuery:Unknown asset': ExchangeError, + 'EAPI:Rate limit exceeded': DDoSProtection, + 'EOrder:Rate limit exceeded': DDoSProtection, + 'EGeneral:Internal error': ExchangeNotAvailable, + 'EGeneral:Temporary lockout': DDoSProtection, + 'EGeneral:Permission denied': PermissionDenied, + }, + }); + } + + costToPrecision (symbol, cost) { + return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); + } + + feeToPrecision (symbol, fee) { + return this.decimalToPrecision (fee, TRUNCATE, this.markets[symbol]['precision']['amount'], DECIMAL_PLACES); + } + + async fetchMinOrderAmounts () { + const html = await this.zendeskGet205893708WhatIsTheMinimumOrderSize (); + const parts = html.split (''); + const numParts = parts.length; + if (numParts < 3) { + throw new NotSupported (this.id + ' fetchMinOrderAmounts HTML page markup has changed: https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size-'); + } + const result = {}; + // skip the part before the header and the header itself + for (let i = 2; i < parts.length; i++) { + const part = parts[i]; + const chunks = part.split (''); + const amountAndCode = chunks[0]; + if (amountAndCode !== 'To Be Announced') { + const pieces = amountAndCode.split (' '); + const numPieces = pieces.length; + if (numPieces === 2) { + const amount = parseFloat (pieces[0]); + const code = this.safeCurrencyCode (pieces[1]); + result[code] = amount; + } + } + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAssetPairs (params); + const limits = await this.fetchMinOrderAmounts (); + const keys = Object.keys (response['result']); + let result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = response['result'][id]; + const baseId = market['base']; + const quoteId = market['quote']; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const darkpool = id.indexOf ('.d') >= 0; + const symbol = darkpool ? market['altname'] : (base + '/' + quote); + let maker = undefined; + if ('fees_maker' in market) { + maker = parseFloat (market['fees_maker'][0][1]) / 100; + } + const precision = { + 'amount': market['lot_decimals'], + 'price': market['pair_decimals'], + }; + let minAmount = Math.pow (10, -precision['amount']); + if (base in limits) { + minAmount = limits[base]; + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'darkpool': darkpool, + 'info': market, + 'altname': market['altname'], + 'maker': maker, + 'taker': parseFloat (market['fees'][0][1]) / 100, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + }); + } + result = this.appendInactiveMarkets (result); + this.marketsByAltname = this.indexBy (result, 'altname'); + return result; + } + + safeCurrencyCode (currencyId, currency = undefined) { + if (currencyId.length > 3) { + if ((currencyId.indexOf ('X') === 0) || (currencyId.indexOf ('Z') === 0)) { + currencyId = currencyId.slice (1); + } + } + return super.safeCurrencyCode (currencyId, currency); + } + + appendInactiveMarkets (result) { + // result should be an array to append to + const precision = { 'amount': 8, 'price': 8 }; + const costLimits = { 'min': 0, 'max': undefined }; + const priceLimits = { 'min': Math.pow (10, -precision['price']), 'max': undefined }; + const amountLimits = { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']) }; + const limits = { 'amount': amountLimits, 'price': priceLimits, 'cost': costLimits }; + const defaults = { + 'darkpool': false, + 'info': undefined, + 'maker': undefined, + 'taker': undefined, + 'active': false, + 'precision': precision, + 'limits': limits, + }; + const markets = [ + // { 'id': 'XXLMZEUR', 'symbol': 'XLM/EUR', 'base': 'XLM', 'quote': 'EUR', 'altname': 'XLMEUR' }, + ]; + for (let i = 0; i < markets.length; i++) { + result.push (this.extend (defaults, markets[i])); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetAssets (params); + // + // { + // "error": [], + // "result": { + // "ADA": { "aclass": "currency", "altname": "ADA", "decimals": 8, "display_decimals": 6 }, + // "BCH": { "aclass": "currency", "altname": "BCH", "decimals": 10, "display_decimals": 5 }, + // ... + // }, + // } + // + const currencies = this.safeValue (response, 'result'); + const ids = Object.keys (currencies); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = currencies[id]; + // todo: will need to rethink the fees + // see: https://support.kraken.com/hc/en-us/articles/201893608-What-are-the-withdrawal-fees- + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (this.safeString (currency, 'altname')); + const precision = this.safeInteger (currency, 'decimals'); + // assumes all currencies are active except those listed above + const active = !this.inArray (code, this.options['inactiveCurrencies']); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + this.checkRequiredCredentials (); + const response = await this.privatePostTradeVolume (params); + const tradedVolume = this.safeFloat (response['result'], 'volume'); + const tiers = this.fees['trading']['tiers']; + let taker = tiers['taker'][1]; + let maker = tiers['maker'][1]; + for (let i = 0; i < tiers['taker'].length; i++) { + if (tradedVolume >= tiers['taker'][i][0]) { + taker = tiers['taker'][i][1]; + } + } + for (let i = 0; i < tiers['maker'].length; i++) { + if (tradedVolume >= tiers['maker'][i][0]) { + maker = tiers['maker'][i][1]; + } + } + return { + 'info': response, + 'maker': maker, + 'taker': taker, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (market['darkpool']) { + throw new ExchangeError (this.id + ' does not provide an order book for darkpool symbol ' + symbol); + } + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; // 100 + } + const response = await this.publicGetDepth (this.extend (request, params)); + const orderbook = response['result'][market['id']]; + return this.parseOrderBook (orderbook); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const baseVolume = parseFloat (ticker['v'][1]); + const vwap = parseFloat (ticker['p'][1]); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = parseFloat (ticker['c'][0]); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': parseFloat (ticker['h'][1]), + 'low': parseFloat (ticker['l'][1]), + 'bid': parseFloat (ticker['b'][0]), + 'bidVolume': undefined, + 'ask': parseFloat (ticker['a'][0]), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'o'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + symbols = (symbols === undefined) ? this.symbols : symbols; + const marketIds = []; + for (let i = 0; i < this.symbols.length; i++) { + const symbol = this.symbols[i]; + const market = this.markets[symbol]; + if (market['active'] && !market['darkpool']) { + marketIds.push (market['id']); + } + } + const request = { + 'pair': marketIds.join (','), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const tickers = response['result']; + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + if (this.inArray (symbol, symbols)) { + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const darkpool = symbol.indexOf ('.d') >= 0; + if (darkpool) { + throw new ExchangeError (this.id + ' does not provide a ticker for darkpool symbol ' + symbol); + } + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = response['result'][market['id']]; + return this.parseTicker (ticker, market); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[6]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'interval': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['since'] = parseInt ((since - 1) / 1000); + } + const response = await this.publicGetOHLC (this.extend (request, params)); + const ohlcvs = response['result'][market['id']]; + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + parseLedgerEntryType (type) { + const types = { + 'trade': 'trade', + 'withdrawal': 'transaction', + 'deposit': 'transaction', + 'transfer': 'transfer', + 'margin': 'margin', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // { + // 'LTFK7F-N2CUX-PNY4SX': { + // refid: "TSJTGT-DT7WN-GPPQMJ", + // time: 1520102320.555, + // type: "trade", + // aclass: "currency", + // asset: "XETH", + // amount: "0.1087194600", + // fee: "0.0000000000", + // balance: "0.2855851000" + // }, + // ... + // } + // + const id = this.safeString (item, 'id'); + let direction = undefined; + const account = undefined; + const referenceId = this.safeString (item, 'refid'); + const referenceAccount = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const code = this.safeCurrencyCode (this.safeString (item, 'asset'), currency); + let amount = this.safeFloat (item, 'amount'); + if (amount < 0) { + direction = 'out'; + amount = Math.abs (amount); + } else { + direction = 'in'; + } + const time = this.safeFloat (item, 'time'); + let timestamp = undefined; + if (time !== undefined) { + timestamp = parseInt (time * 1000); + } + const fee = { + 'cost': this.safeFloat (item, 'fee'), + 'currency': code, + }; + const before = undefined; + const after = this.safeFloat (item, 'balance'); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'direction': direction, + 'account': account, + 'referenceId': referenceId, + 'referenceAccount': referenceAccount, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, + 'after': after, + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + // https://www.kraken.com/features/api#get-ledgers-info + await this.loadMarkets (); + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostLedgers (this.extend (request, params)); + // { error: [], + // result: { ledger: { 'LPUAIB-TS774-UKHP7X': { refid: "A2B4HBV-L4MDIE-JU4N3N", + // time: 1520103488.314, + // type: "withdrawal", + // aclass: "currency", + // asset: "XETH", + // amount: "-0.2805800000", + // fee: "0.0050000000", + // balance: "0.0000051000" }, + const result = this.safeValue (response, 'result', {}); + const ledger = this.safeValue (result, 'ledger', {}); + const keys = Object.keys (ledger); + const items = []; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = ledger[key]; + value['id'] = key; + items.push (value); + } + return this.parseLedger (items, currency, since, limit); + } + + async fetchLedgerEntriesByIds (ids, code = undefined, params = {}) { + // https://www.kraken.com/features/api#query-ledgers + await this.loadMarkets (); + ids = ids.join (','); + const request = this.extend ({ + 'id': ids, + }, params); + const response = await this.privatePostQueryLedgers (request); + // { error: [], + // result: { 'LPUAIB-TS774-UKHP7X': { refid: "A2B4HBV-L4MDIE-JU4N3N", + // time: 1520103488.314, + // type: "withdrawal", + // aclass: "currency", + // asset: "XETH", + // amount: "-0.2805800000", + // fee: "0.0050000000", + // balance: "0.0000051000" } } } + const result = response['result']; + const keys = Object.keys (result); + const items = []; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = result[key]; + value['id'] = key; + items.push (value); + } + return this.parseLedger (items); + } + + async fetchLedgerEntry (id, code = undefined, params = {}) { + const items = await this.fetchLedgerEntriesByIds ([ id ], code, params); + return items[0]; + } + + parseTrade (trade, market = undefined) { + let timestamp = undefined; + let side = undefined; + let type = undefined; + let price = undefined; + let amount = undefined; + let id = undefined; + let order = undefined; + let fee = undefined; + const marketId = this.safeString (trade, 'pair'); + const foundMarket = this.findMarketByAltnameOrId (marketId); + let symbol = undefined; + if (foundMarket !== undefined) { + market = foundMarket; + } else if (marketId !== undefined) { + // delisted market ids go here + market = this.getDelistedMarketById (marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + if (Array.isArray (trade)) { + timestamp = parseInt (trade[2] * 1000); + side = (trade[3] === 's') ? 'sell' : 'buy'; + type = (trade[4] === 'l') ? 'limit' : 'market'; + price = parseFloat (trade[0]); + amount = parseFloat (trade[1]); + const tradeLength = trade.length; + if (tradeLength > 6) { + id = trade[6]; // artificially added as per #1794 + } + } else if ('ordertxid' in trade) { + order = trade['ordertxid']; + id = this.safeString2 (trade, 'id', 'postxid'); + timestamp = this.safeTimestamp (trade, 'time'); + side = trade['type']; + type = trade['ordertype']; + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'vol'); + if ('fee' in trade) { + let currency = undefined; + if (market) { + currency = market['quote']; + } + fee = { + 'cost': this.safeFloat (trade, 'fee'), + 'currency': currency, + }; + } + } + return { + 'id': id, + 'order': order, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': price * amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const id = market['id']; + const request = { + 'pair': id, + }; + // https://support.kraken.com/hc/en-us/articles/218198197-How-to-pull-all-trade-data-using-the-Kraken-REST-API + // https://github.com/ccxt/ccxt/issues/5677 + if (since !== undefined) { + // php does not format it properly + // therefore we use string concatenation here + request['since'] = since * 1e6; + request['since'] = since.toString () + '000000'; // expected to be in nanoseconds + } + // https://github.com/ccxt/ccxt/issues/5698 + if (limit !== undefined && limit !== 1000) { + const fetchTradesWarning = this.safeValue (this.options, 'fetchTradesWarning', true); + if (fetchTradesWarning) { + throw new ExchangeError (this.id + ' fetchTrades() cannot serve ' + limit.toString () + " trades without breaking the pagination, see https://github.com/ccxt/ccxt/issues/5698 for more details. Set exchange.options['fetchTradesWarning'] to acknowledge this warning and silence it."); + } + } + const response = await this.publicGetTrades (this.extend (request, params)); + // + // { + // "error": [], + // "result": { + // "XETHXXBT": [ + // ["0.032310","4.28169434",1541390792.763,"s","l",""] + // ], + // "last": "1541439421200678657" + // } + // } + // + const result = response['result']; + const trades = result[id]; + // trades is a sorted array: last (most recent trade) goes last + const length = trades.length; + if (length <= 0) { + return []; + } + const lastTrade = trades[length - 1]; + const lastTradeId = this.safeString (result, 'last'); + lastTrade.push (lastTradeId); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + const response = await this.privatePostBalance (params); + const balances = this.safeValue (response, 'result', {}); + const result = { 'info': balances }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balances, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'ordertype': type, + 'volume': this.amountToPrecision (symbol, amount), + }; + const priceIsDefined = (price !== undefined); + const marketOrder = (type === 'market'); + const limitOrder = (type === 'limit'); + const shouldIncludePrice = limitOrder || (!marketOrder && priceIsDefined); + if (shouldIncludePrice) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostAddOrder (this.extend (request, params)); + let id = this.safeValue (response['result'], 'txid'); + if (id !== undefined) { + if (Array.isArray (id)) { + const length = id.length; + id = (length > 1) ? id : id[0]; + } + } + return { + 'id': id, + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'average': undefined, + 'filled': undefined, + 'remaining': undefined, + 'status': undefined, + 'fee': undefined, + 'trades': undefined, + }; + } + + findMarketByAltnameOrId (id) { + if (id in this.marketsByAltname) { + return this.marketsByAltname[id]; + } else if (id in this.markets_by_id) { + return this.markets_by_id[id]; + } + return undefined; + } + + getDelistedMarketById (id) { + if (id === undefined) { + return id; + } + let market = this.safeValue (this.options['delistedMarketsById'], id); + if (market !== undefined) { + return market; + } + const baseIdStart = 0; + let baseIdEnd = 3; + let quoteIdStart = 3; + let quoteIdEnd = 6; + if (id.length === 8) { + baseIdEnd = 4; + quoteIdStart = 4; + quoteIdEnd = 8; + } else if (id.length === 7) { + baseIdEnd = 4; + quoteIdStart = 4; + quoteIdEnd = 7; + } + const baseId = id.slice (baseIdStart, baseIdEnd); + const quoteId = id.slice (quoteIdStart, quoteIdEnd); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + market = { + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + }; + this.options['delistedMarketsById'][id] = market; + return market; + } + + parseOrderStatus (status) { + const statuses = { + 'pending': 'open', // order pending book entry + 'open': 'open', + 'closed': 'closed', + 'canceled': 'canceled', + 'expired': 'expired', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const description = this.safeValue (order, 'descr', {}); + const side = this.safeString (description, 'type'); + const type = this.safeString (description, 'ordertype'); + const marketId = this.safeString (description, 'pair'); + const foundMarket = this.findMarketByAltnameOrId (marketId); + let symbol = undefined; + if (foundMarket !== undefined) { + market = foundMarket; + } else if (marketId !== undefined) { + // delisted market ids go here + market = this.getDelistedMarketById (marketId); + } + const timestamp = this.safeTimestamp (order, 'opentm'); + const amount = this.safeFloat (order, 'vol'); + const filled = this.safeFloat (order, 'vol_exec'); + const remaining = amount - filled; + let fee = undefined; + const cost = this.safeFloat (order, 'cost'); + let price = this.safeFloat (description, 'price'); + if ((price === undefined) || (price === 0)) { + price = this.safeFloat (description, 'price2'); + } + if ((price === undefined) || (price === 0)) { + price = this.safeFloat (order, 'price', price); + } + const average = this.safeFloat (order, 'price'); + if (market !== undefined) { + symbol = market['symbol']; + if ('fee' in order) { + const flags = order['oflags']; + const feeCost = this.safeFloat (order, 'fee'); + fee = { + 'cost': feeCost, + 'rate': undefined, + }; + if (flags.indexOf ('fciq') >= 0) { + fee['currency'] = market['quote']; + } else if (flags.indexOf ('fcib') >= 0) { + fee['currency'] = market['base']; + } + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString (order, 'id'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'average': average, + 'remaining': remaining, + 'fee': fee, + // 'trades': this.parseTrades (order['trades'], market), + }; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostQueryOrders (this.extend ({ + 'trades': true, // whether or not to include trades in output (optional, default false) + 'txid': id, // do not comma separate a list of ids - use fetchOrdersByIds instead + // 'userref': 'optional', // restrict results to given user reference id (optional) + }, params)); + const orders = response['result']; + const order = this.parseOrder (this.extend ({ 'id': id }, orders[id])); + return this.extend ({ 'info': response }, order); + } + + async fetchOrdersByIds (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostQueryOrders (this.extend ({ + 'trades': true, // whether or not to include trades in output (optional, default false) + 'txid': ids.join (','), // comma delimited list of transaction ids to query info about (20 maximum) + }, params)); + const result = this.safeValue (response, 'result', {}); + const orders = []; + const orderIds = Object.keys (result); + for (let i = 0; i < orderIds.length; i++) { + const id = orderIds[i]; + const item = result[id]; + const order = this.parseOrder (this.extend ({ 'id': id }, item)); + orders.push (order); + } + return orders; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'type': 'all', // any position, closed position, closing position, no position + // 'trades': false, // whether or not to include trades related to position in output + // 'start': 1234567890, // starting unix timestamp or trade tx id of results (exclusive) + // 'end': 1234567890, // ending unix timestamp or trade tx id of results (inclusive) + // 'ofs' = result offset + }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostTradesHistory (this.extend (request, params)); + // + // { + // "error": [], + // "result": { + // "trades": { + // "GJ3NYQ-XJRTF-THZABF": { + // "ordertxid": "TKH2SE-ZIF5E-CFI7LT", + // "postxid": "OEN3VX-M7IF5-JNBJAM", + // "pair": "XICNXETH", + // "time": 1527213229.4491, + // "type": "sell", + // "ordertype": "limit", + // "price": "0.001612", + // "cost": "0.025792", + // "fee": "0.000026", + // "vol": "16.00000000", + // "margin": "0.000000", + // "misc": "" + // }, + // ... + // }, + // "count": 9760, + // }, + // } + // + const trades = response['result']['trades']; + const ids = Object.keys (trades); + for (let i = 0; i < ids.length; i++) { + trades[ids[i]]['id'] = ids[i]; + } + const result = this.parseTrades (trades, undefined, since, limit); + if (symbol === undefined) { + return result; + } + return this.filterBySymbol (result, symbol); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let response = undefined; + try { + response = await this.privatePostCancelOrder (this.extend ({ + 'txid': id, + }, params)); + } catch (e) { + if (this.last_http_response) { + if (this.last_http_response.indexOf ('EOrder:Unknown order') >= 0) { + throw new OrderNotFound (this.id + ' cancelOrder() error ' + this.last_http_response); + } + } + throw e; + } + return response; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostOpenOrders (this.extend (request, params)); + const orders = this.parseOrders (response['result']['open'], undefined, since, limit); + if (symbol === undefined) { + return orders; + } + return this.filterBySymbol (orders, symbol); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostClosedOrders (this.extend (request, params)); + const orders = this.parseOrders (response['result']['closed'], undefined, since, limit); + if (symbol === undefined) { + return orders; + } + return this.filterBySymbol (orders, symbol); + } + + async fetchDepositMethods (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privatePostDepositMethods (this.extend (request, params)); + return response['result']; + } + + parseTransactionStatus (status) { + // IFEX transaction states + const statuses = { + 'Initial': 'pending', + 'Pending': 'pending', + 'Success': 'ok', + 'Settled': 'pending', + 'Failure': 'failed', + 'Partial': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { method: "Ether (Hex)", + // aclass: "currency", + // asset: "XETH", + // refid: "Q2CANKL-LBFVEE-U4Y2WQ", + // txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…", + // info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290", + // amount: "7.9999257900", + // fee: "0.0000000000", + // time: 1529223212, + // status: "Success" } + // + // fetchWithdrawals + // + // { method: "Ether", + // aclass: "currency", + // asset: "XETH", + // refid: "A2BF34S-O7LBNQ-UE4Y4O", + // txid: "0x288b83c6b0904d8400ef44e1c9e2187b5c8f7ea3d838222d53f701a15b5c274d", + // info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0", + // amount: "9.9950000000", + // fee: "0.0050000000", + // time: 1530481750, + // status: "Success" } + // + const id = this.safeString (transaction, 'refid'); + const txid = this.safeString (transaction, 'txid'); + const timestamp = this.safeTimestamp (transaction, 'time'); + const currencyId = this.safeString (transaction, 'asset'); + const code = this.safeCurrencyCode (currencyId, currency); + const address = this.safeString (transaction, 'info'); + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const type = this.safeString (transaction, 'type'); // injected from the outside + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost === undefined) { + if (type === 'deposit') { + feeCost = 0; + } + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + parseTransactionsByType (type, transactions, code = undefined, since = undefined, limit = undefined) { + const result = []; + for (let i = 0; i < transactions.length; i++) { + const transaction = this.parseTransaction (this.extend ({ + 'type': type, + }, transactions[i])); + result.push (transaction); + } + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://www.kraken.com/en-us/help/api#deposit-status + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privatePostDepositStatus (this.extend (request, params)); + // + // { error: [], + // result: [ { method: "Ether (Hex)", + // aclass: "currency", + // asset: "XETH", + // refid: "Q2CANKL-LBFVEE-U4Y2WQ", + // txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…", + // info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290", + // amount: "7.9999257900", + // fee: "0.0000000000", + // time: 1529223212, + // status: "Success" } ] } + // + return this.parseTransactionsByType ('deposit', response['result'], code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://www.kraken.com/en-us/help/api#withdraw-status + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privatePostWithdrawStatus (this.extend (request, params)); + // + // { error: [], + // result: [ { method: "Ether", + // aclass: "currency", + // asset: "XETH", + // refid: "A2BF34S-O7LBNQ-UE4Y4O", + // txid: "0x298c83c7b0904d8400ef43e1c9e2287b518f7ea3d838822d53f704a1565c274d", + // info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0", + // amount: "9.9950000000", + // fee: "0.0050000000", + // time: 1530481750, + // status: "Success" } ] } + // + return this.parseTransactionsByType ('withdrawal', response['result'], code, since, limit); + } + + async createDepositAddress (code, params = {}) { + const request = { + 'new': 'true', + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + // eslint-disable-next-line quotes + let method = this.safeString (params, 'method'); + if (method === undefined) { + if (this.options['cacheDepositMethodsOnFetchDepositAddress']) { + // cache depositMethods + if (!(code in this.options['depositMethods'])) { + this.options['depositMethods'][code] = await this.fetchDepositMethods (code); + } + method = this.options['depositMethods'][code][0]['method']; + } else { + throw new ExchangeError (this.id + ' fetchDepositAddress() requires an extra `method` parameter. Use fetchDepositMethods ("' + code + '") to get a list of available deposit methods or enable the exchange property .options["cacheDepositMethodsOnFetchDepositAddress"] = true'); + } + } + const request = { + 'asset': currency['id'], + 'method': method, + }; + const response = await this.privatePostDepositAddresses (this.extend (request, params)); // overwrite methods + const result = response['result']; + const numResults = result.length; + if (numResults < 1) { + throw new InvalidAddress (this.id + ' privatePostDepositAddresses() returned no addresses'); + } + const address = this.safeString (result[0], 'address'); + const tag = this.safeString2 (result[0], 'tag', 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + if ('key' in params) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + 'amount': amount, + // 'address': address, // they don't allow withdrawals to direct addresses + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': response['result'], + }; + } + throw new ExchangeError (this.id + " withdraw requires a 'key' parameter (withdrawal key name, as set up on your account)"); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + this.version + '/' + api + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, params)); + const auth = this.encode (nonce + body); + const hash = this.hash (auth, 'sha256', 'binary'); + const binary = this.stringToBinary (this.encode (url)); + const binhash = this.binaryConcat (binary, hash); + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (binhash, secret, 'sha512', 'base64'); + headers = { + 'API-Key': this.apiKey, + 'API-Sign': this.decode (signature), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } else { + url = '/' + path; + } + url = this.urls['api'][api] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + nonce () { + return this.milliseconds (); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code === 520) { + throw new ExchangeNotAvailable (this.id + ' ' + code.toString () + ' ' + reason); + } + // todo: rewrite this for "broad" exceptions matching + if (body.indexOf ('Invalid order') >= 0) { + throw new InvalidOrder (this.id + ' ' + body); + } + if (body.indexOf ('Invalid nonce') >= 0) { + throw new InvalidNonce (this.id + ' ' + body); + } + if (body.indexOf ('Insufficient funds') >= 0) { + throw new InsufficientFunds (this.id + ' ' + body); + } + if (body.indexOf ('Cancel pending') >= 0) { + throw new CancelPending (this.id + ' ' + body); + } + if (body.indexOf ('Invalid arguments:volume') >= 0) { + throw new InvalidOrder (this.id + ' ' + body); + } + if (body[0] === '{') { + if (typeof response !== 'string') { + if ('error' in response) { + const numErrors = response['error'].length; + if (numErrors) { + const message = this.id + ' ' + body; + for (let i = 0; i < response['error'].length; i++) { + const error = response['error'][i]; + this.throwExactlyMatchedException (this.exceptions, error, message); + } + throw new ExchangeError (message); + } + } + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],102:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, AccountSuspended, InvalidNonce, NotSupported, BadRequest, AuthenticationError, BadSymbol, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class kucoin extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kucoin', + 'name': 'KuCoin', + 'countries': [ 'SC' ], + 'rateLimit': 334, + 'version': 'v2', + 'certified': false, + 'comment': 'Platform 2.0', + 'has': { + 'fetchTime': true, + 'fetchMarkets': true, + 'fetchCurrencies': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchOrderBook': true, + 'fetchOrder': true, + 'fetchClosedOrders': true, + 'fetchOpenOrders': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchBalance': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchAccounts': true, + 'fetchFundingFee': true, + 'fetchOHLCV': true, + 'fetchLedger': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/57369448-3cc3aa80-7196-11e9-883e-5ebeb35e4f57.jpg', + 'referral': 'https://www.kucoin.com/?rcode=E5wkqe', + 'api': { + 'public': 'https://openapi-v2.kucoin.com', + 'private': 'https://openapi-v2.kucoin.com', + }, + 'test': { + 'public': 'https://openapi-sandbox.kucoin.com', + 'private': 'https://openapi-sandbox.kucoin.com', + }, + 'www': 'https://www.kucoin.com', + 'doc': [ + 'https://docs.kucoin.com', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'api': { + 'public': { + 'get': [ + 'timestamp', + 'symbols', + 'market/allTickers', + 'market/orderbook/level{level}', + 'market/histories', + 'market/candles', + 'market/stats', + 'currencies', + 'currencies/{currency}', + ], + 'post': [ + 'bullet-public', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/{accountId}', + 'accounts/{accountId}/ledgers', + 'accounts/{accountId}/holds', + 'deposit-addresses', + 'deposits', + 'hist-deposits', + 'hist-orders', + 'hist-withdrawals', + 'withdrawals', + 'withdrawals/quotas', + 'orders', + 'orders/{orderId}', + 'fills', + 'limit/fills', + ], + 'post': [ + 'accounts', + 'accounts/inner-transfer', + 'deposit-addresses', + 'withdrawals', + 'orders', + 'bullet-private', + ], + 'delete': [ + 'withdrawals/{withdrawalId}', + 'orders/{orderId}', + ], + }, + }, + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '8h': '8hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + }, + 'exceptions': { + 'exact': { + 'order not exist': OrderNotFound, + 'order not exist.': OrderNotFound, // duplicated error temporarily + 'order_not_exist': OrderNotFound, // {"code":"order_not_exist","msg":"order_not_exist"} ¯\_(ツ)_/¯ + 'order_not_exist_or_not_allow_to_cancel': InvalidOrder, // {"code":"400100","msg":"order_not_exist_or_not_allow_to_cancel"} + 'Order size below the minimum requirement.': InvalidOrder, // {"code":"400100","msg":"Order size below the minimum requirement."} + 'The withdrawal amount is below the minimum requirement.': ExchangeError, // {"code":"400100","msg":"The withdrawal amount is below the minimum requirement."} + '400': BadRequest, + '401': AuthenticationError, + '403': NotSupported, + '404': NotSupported, + '405': NotSupported, + '429': RateLimitExceeded, + '500': ExchangeError, + '503': ExchangeNotAvailable, + '200004': InsufficientFunds, + '230003': InsufficientFunds, // {"code":"230003","msg":"Balance insufficient!"} + '260100': InsufficientFunds, // {"code":"260100","msg":"account.noBalance"} + '300000': InvalidOrder, + '400000': BadSymbol, + '400001': AuthenticationError, + '400002': InvalidNonce, + '400003': AuthenticationError, + '400004': AuthenticationError, + '400005': AuthenticationError, + '400006': AuthenticationError, + '400007': AuthenticationError, + '400008': NotSupported, + '400100': BadRequest, + '411100': AccountSuspended, + '415000': BadRequest, // {"code":"415000","msg":"Unsupported Media Type"} + '500000': ExchangeError, + }, + 'broad': { + 'Exceeded the access frequency': RateLimitExceeded, + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'commonCurrencies': { + 'HOT': 'HOTNOW', + 'EDGE': 'DADI', // https://github.com/ccxt/ccxt/issues/5756 + }, + 'options': { + 'version': 'v1', + 'symbolSeparator': '-', + 'fetchMyTradesMethod': 'private_get_fills', + 'fetchBalance': { + 'type': 'trade', // or 'main' + }, + }, + }); + } + + nonce () { + return this.milliseconds (); + } + + async loadTimeDifference () { + const response = await this.publicGetTimestamp (); + const after = this.milliseconds (); + const kucoinTime = this.safeInteger (response, 'data'); + this.options['timeDifference'] = parseInt (after - kucoinTime); + return this.options['timeDifference']; + } + + async fetchTime (params = {}) { + const response = await this.publicGetTimestamp (params); + // + // { + // "code":"200000", + // "msg":"success", + // "data":1546837113087 + // } + // + return this.safeInteger (response, 'data'); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetSymbols (params); + // + // { quoteCurrency: 'BTC', + // symbol: 'KCS-BTC', + // quoteMaxSize: '9999999', + // quoteIncrement: '0.000001', + // baseMinSize: '0.01', + // quoteMinSize: '0.00001', + // enableTrading: true, + // priceIncrement: '0.00000001', + // name: 'KCS-BTC', + // baseIncrement: '0.01', + // baseMaxSize: '9999999', + // baseCurrency: 'KCS' } + // + const data = response['data']; + const result = []; + for (let i = 0; i < data.length; i++) { + const market = data[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = this.safeValue (market, 'enableTrading'); + const baseMaxSize = this.safeFloat (market, 'baseMaxSize'); + const baseMinSize = this.safeFloat (market, 'baseMinSize'); + const quoteMaxSize = this.safeFloat (market, 'quoteMaxSize'); + const quoteMinSize = this.safeFloat (market, 'quoteMinSize'); + // const quoteIncrement = this.safeFloat (market, 'quoteIncrement'); + const precision = { + 'amount': this.precisionFromString (this.safeString (market, 'baseIncrement')), + 'price': this.precisionFromString (this.safeString (market, 'priceIncrement')), + }; + const limits = { + 'amount': { + 'min': baseMinSize, + 'max': baseMaxSize, + }, + 'price': { + 'min': this.safeFloat (market, 'priceIncrement'), + 'max': quoteMaxSize / baseMinSize, + }, + 'cost': { + 'min': quoteMinSize, + 'max': quoteMaxSize, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // precision: 10, + // name: 'KCS', + // fullName: 'KCS shares', + // currency: 'KCS' + // } + // + const responseData = response['data']; + const result = {}; + for (let i = 0; i < responseData.length; i++) { + const entry = responseData[i]; + const id = this.safeString (entry, 'currency'); + const name = this.safeString (entry, 'fullName'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (entry, 'precision'); + result[code] = { + 'id': id, + 'name': name, + 'code': code, + 'precision': precision, + 'info': entry, + }; + } + return result; + } + + async fetchAccounts (params = {}) { + const response = await this.privateGetAccounts (params); + // + // { code: "200000", + // data: [ { balance: "0.00009788", + // available: "0.00009788", + // holds: "0", + // currency: "BTC", + // id: "5c6a4fd399a1d81c4f9cc4d0", + // type: "trade" }, + // ..., + // { balance: "0.00000001", + // available: "0.00000001", + // holds: "0", + // currency: "ETH", + // id: "5c6a49ec99a1d819392e8e9f", + // type: "trade" } ] } + // + const data = this.safeValue (response, 'data'); + const result = []; + for (let i = 0; i < data.length; i++) { + const account = data[i]; + const accountId = this.safeString (account, 'id'); + const currencyId = this.safeString (account, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const type = this.safeString (account, 'type'); // main or trade + result.push ({ + 'id': accountId, + 'type': type, + 'currency': code, + 'info': account, + }); + } + return result; + } + + async fetchFundingFee (code, params = {}) { + const currencyId = this.currencyId (code); + const request = { + 'currency': currencyId, + }; + const response = await this.privateGetWithdrawalsQuotas (this.extend (request, params)); + const data = response['data']; + const withdrawFees = {}; + withdrawFees[code] = this.safeFloat (data, 'withdrawMinFee'); + return { + 'info': response, + 'withdraw': withdrawFees, + 'deposit': {}, + }; + } + + parseTicker (ticker, market = undefined) { + // + // { + // symbol: "ETH-BTC", + // high: "0.019518", + // vol: "7997.82836194", + // last: "0.019329", + // low: "0.019", + // buy: "0.019329", + // sell: "0.01933", + // changePrice: "-0.000139", + // time: 1580553706304, + // averagePrice: "0.01926386", + // changeRate: "-0.0071", + // volValue: "154.40791568183474" + // } + // + // { + // "trading": true, + // "symbol": "KCS-BTC", + // "buy": 0.00011, + // "sell": 0.00012, + // "sort": 100, + // "volValue": 3.13851792584, //total + // "baseCurrency": "KCS", + // "market": "BTC", + // "quoteCurrency": "BTC", + // "symbolCode": "KCS-BTC", + // "datetime": 1548388122031, + // "high": 0.00013, + // "vol": 27514.34842, + // "low": 0.0001, + // "changePrice": -1.0e-5, + // "changeRate": -0.0769, + // "lastTradedPrice": 0.00012, + // "board": 0, + // "mark": 0 + // } + // + let percentage = this.safeFloat (ticker, 'changeRate'); + if (percentage !== undefined) { + percentage = percentage * 100; + } + const last = this.safeFloat2 (ticker, 'last', 'lastTradedPrice'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const timestamp = this.safeInteger2 (ticker, 'time', 'datetime'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'changePrice'), + 'percentage': percentage, + 'average': this.safeFloat (ticker, 'averagePrice'), + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'volValue'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketAllTickers (params); + // + // { + // "code": "200000", + // "data": { + // "date": 1550661940645, + // "ticker": [ + // 'buy': '0.00001168', + // 'changePrice': '-0.00000018', + // 'changeRate': '-0.0151', + // 'datetime': 1550661146316, + // 'high': '0.0000123', + // 'last': '0.00001169', + // 'low': '0.00001159', + // 'sell': '0.00001182', + // 'symbol': 'LOOM-BTC', + // 'vol': '44399.5669' + // }, + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + const tickers = this.safeValue (data, 'ticker', []); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = this.safeString (ticker, 'symbol'); + if (symbol !== undefined) { + result[symbol] = ticker; + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetMarketStats (this.extend (request, params)); + // + // { + // "code": "200000", + // "data": { + // 'buy': '0.00001168', + // 'changePrice': '-0.00000018', + // 'changeRate': '-0.0151', + // 'datetime': 1550661146316, + // 'high': '0.0000123', + // 'last': '0.00001169', + // 'low': '0.00001159', + // 'sell': '0.00001182', + // 'symbol': 'LOOM-BTC', + // 'vol': '44399.5669' + // }, + // } + // + return this.parseTicker (response['data'], market); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // [ + // "1545904980", // Start time of the candle cycle + // "0.058", // opening price + // "0.049", // closing price + // "0.058", // highest price + // "0.049", // lowest price + // "0.018", // base volume + // "0.000945", // quote volume + // ] + // + return [ + parseInt (ohlcv[0]) * 1000, + parseFloat (ohlcv[1]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketId = market['id']; + const request = { + 'symbol': marketId, + 'type': this.timeframes[timeframe], + }; + const duration = this.parseTimeframe (timeframe) * 1000; + let endAt = this.milliseconds (); // required param + if (since !== undefined) { + request['startAt'] = parseInt (Math.floor (since / 1000)); + if (limit === undefined) { + // https://docs.kucoin.com/#get-klines + // https://docs.kucoin.com/#details + // For each query, the system would return at most 1500 pieces of data. + // To obtain more data, please page the data by time. + limit = this.safeInteger (this.options, 'fetchOHLCVLimit', 1500); + } + endAt = this.sum (since, limit * duration); + } else if (limit !== undefined) { + since = endAt - limit * duration; + request['startAt'] = parseInt (Math.floor (since / 1000)); + } + request['endAt'] = parseInt (Math.floor (endAt / 1000)); + const response = await this.publicGetMarketCandles (this.extend (request, params)); + const responseData = this.safeValue (response, 'data', []); + return this.parseOHLCVs (responseData, market, timeframe, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currencyId = this.currencyId (code); + const request = { 'currency': currencyId }; + const response = await this.privatePostDepositAddresses (this.extend (request, params)); + // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} + // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} + const data = this.safeValue (response, 'data', {}); + let address = this.safeString (data, 'address'); + // BCH/BSV is returned with a "bitcoincash:" prefix, which we cut off here and only keep the address + if (address !== undefined) { + address = address.replace ('bitcoincash:', ''); + } + const tag = this.safeString (data, 'memo'); + this.checkAddress (address); + return { + 'info': response, + 'currency': code, + 'address': address, + 'tag': tag, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currencyId = this.currencyId (code); + const request = { 'currency': currencyId }; + const response = await this.privateGetDepositAddresses (this.extend (request, params)); + // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} + // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} + const data = this.safeValue (response, 'data', {}); + let address = this.safeString (data, 'address'); + // BCH/BSV is returned with a "bitcoincash:" prefix, which we cut off here and only keep the address + if (address !== undefined) { + address = address.replace ('bitcoincash:', ''); + } + const tag = this.safeString (data, 'memo'); + this.checkAddress (address); + return { + 'info': response, + 'currency': code, + 'address': address, + 'tag': tag, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + let level = '2'; + if (limit !== undefined) { + if ((limit !== 20) && (limit !== 100)) { + throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100'); + } + level += '_' + limit.toString (); + } + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = this.extend ({ 'symbol': marketId, 'level': level }, params); + const response = await this.publicGetMarketOrderbookLevelLevel (request); + // + // { sequence: '1547731421688', + // asks: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ], + // bids: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ], } + // + const data = response['data']; + const timestamp = this.safeInteger (data, 'time'); + // level can be a string such as 2_20 or 2_100 + const levelString = this.safeString (request, 'level'); + const levelParts = levelString.split ('_'); + const offset = parseInt (levelParts[0]); + const orderbook = this.parseOrderBook (data, timestamp, 'bids', 'asks', offset - 2, offset - 1); + orderbook['nonce'] = this.safeInteger (data, 'sequence'); + return orderbook; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + // required param, cannot be used twice + const clientOid = this.uuid (); + const request = { + 'clientOid': clientOid, + 'side': side, + 'symbol': marketId, + 'type': type, + }; + if (type !== 'market') { + request['price'] = this.priceToPrecision (symbol, price); + request['size'] = this.amountToPrecision (symbol, amount); + } else { + if (this.safeValue (params, 'quoteAmount')) { + // used to create market order by quote amount - https://github.com/ccxt/ccxt/issues/4876 + request['funds'] = this.amountToPrecision (symbol, amount); + } else { + request['size'] = this.amountToPrecision (symbol, amount); + } + } + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "orderId": "5bd6e9286d99522a52e458de" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const timestamp = this.milliseconds (); + const order = { + 'id': this.safeString (data, 'orderId'), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': undefined, + 'filled': undefined, + 'remaining': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + 'status': 'open', + 'clientOid': clientOid, + 'info': data, + }; + if (!this.safeValue (params, 'quoteAmount')) { + order['amount'] = amount; + } + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { 'orderId': id }; + const response = await this.privateDeleteOrdersOrderId (this.extend (request, params)); + return response; + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': status, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startAt'] = since; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "currentPage": 1, + // "pageSize": 1, + // "totalNum": 153408, + // "totalPage": 153408, + // "items": [ + // { + // "id": "5c35c02703aa673ceec2a168", //orderid + // "symbol": "BTC-USDT", //symbol + // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order + // "type": "limit", // order type,e.g. limit,markrt,stop_limit. + // "side": "buy", // transaction direction,include buy and sell + // "price": "10", // order price + // "size": "2", // order quantity + // "funds": "0", // order funds + // "dealFunds": "0.166", // deal funds + // "dealSize": "2", // deal quantity + // "fee": "0", // fee + // "feeCurrency": "USDT", // charge fee currency + // "stp": "", // self trade prevention,include CN,CO,DC,CB + // "stop": "", // stop type + // "stopTriggered": false, // stop order is triggered + // "stopPrice": "0", // stop price + // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK + // "postOnly": false, // postOnly + // "hidden": false, // hidden order + // "iceberg": false, // iceberg order + // "visibleSize": "0", // display quantity for iceberg order + // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT + // "channel": "IOS", // order source + // "clientOid": "", // user-entered order unique mark + // "remark": "", // remark + // "tags": "", // tag order source + // "isActive": false, // status before unfilled or uncancelled + // "cancelExist": false, // order cancellation transaction record + // "createdAt": 1547026471000 // time + // }, + // ] + // } + // } + const responseData = this.safeValue (response, 'data', {}); + const orders = this.safeValue (responseData, 'items', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('done', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('active', symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privateGetOrdersOrderId (this.extend (request, params)); + const responseData = response['data']; + return this.parseOrder (responseData, market); + } + + parseOrder (order, market = undefined) { + // + // fetchOpenOrders, fetchClosedOrders + // + // { + // "id": "5c35c02703aa673ceec2a168", //orderid + // "symbol": "BTC-USDT", //symbol + // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order + // "type": "limit", // order type,e.g. limit,markrt,stop_limit. + // "side": "buy", // transaction direction,include buy and sell + // "price": "10", // order price + // "size": "2", // order quantity + // "funds": "0", // order funds + // "dealFunds": "0.166", // deal funds + // "dealSize": "2", // deal quantity + // "fee": "0", // fee + // "feeCurrency": "USDT", // charge fee currency + // "stp": "", // self trade prevention,include CN,CO,DC,CB + // "stop": "", // stop type + // "stopTriggered": false, // stop order is triggered + // "stopPrice": "0", // stop price + // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK + // "postOnly": false, // postOnly + // "hidden": false, // hidden order + // "iceberg": false, // iceberg order + // "visibleSize": "0", // display quantity for iceberg order + // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT + // "channel": "IOS", // order source + // "clientOid": "", // user-entered order unique mark + // "remark": "", // remark + // "tags": "", // tag order source + // "isActive": false, // status before unfilled or uncancelled + // "cancelExist": false, // order cancellation transaction record + // "createdAt": 1547026471000 // time + // } + // + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + market = this.safeValue (this.markets_by_id, marketId); + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const orderId = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + const timestamp = this.safeInteger (order, 'createdAt'); + const datetime = this.iso8601 (timestamp); + let price = this.safeFloat (order, 'price'); + const side = this.safeString (order, 'side'); + const feeCurrencyId = this.safeString (order, 'feeCurrency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + const feeCost = this.safeFloat (order, 'fee'); + const amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat (order, 'dealSize'); + const cost = this.safeFloat (order, 'dealFunds'); + const remaining = amount - filled; + // bool + let status = order['isActive'] ? 'open' : 'closed'; + status = order['cancelExist'] ? 'canceled' : status; + const fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + } + } + return { + 'id': orderId, + 'symbol': symbol, + 'type': type, + 'side': side, + 'amount': amount, + 'price': price, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'timestamp': timestamp, + 'datetime': datetime, + 'fee': fee, + 'status': status, + 'info': order, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + const method = this.options['fetchMyTradesMethod']; + let parseResponseData = false; + if (method === 'private_get_fills') { + // does not return trades earlier than 2019-02-18T00:00:00Z + if (since !== undefined) { + // only returns trades up to one week after the since param + request['startAt'] = since; + } + } else if (method === 'private_get_limit_fills') { + // does not return trades earlier than 2019-02-18T00:00:00Z + // takes no params + // only returns first 1000 trades (not only "in the last 24 hours" as stated in the docs) + parseResponseData = true; + } else if (method === 'private_get_hist_orders') { + // despite that this endpoint is called `HistOrders` + // it returns historical trades instead of orders + // returns trades earlier than 2019-02-18T00:00:00Z only + if (since !== undefined) { + request['startAt'] = parseInt (since / 1000); + } + } else { + throw new ExchangeError (this.id + ' invalid fetchClosedOrder method'); + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "currentPage": 1, + // "pageSize": 50, + // "totalNum": 1, + // "totalPage": 1, + // "items": [ + // { + // "symbol":"BTC-USDT", // symbol + // "tradeId":"5c35c02709e4f67d5266954e", // trade id + // "orderId":"5c35c02703aa673ceec2a168", // order id + // "counterOrderId":"5c1ab46003aa676e487fa8e3", // counter order id + // "side":"buy", // transaction direction,include buy and sell + // "liquidity":"taker", // include taker and maker + // "forceTaker":true, // forced to become taker + // "price":"0.083", // order price + // "size":"0.8424304", // order quantity + // "funds":"0.0699217232", // order funds + // "fee":"0", // fee + // "feeRate":"0", // fee rate + // "feeCurrency":"USDT", // charge fee currency + // "stop":"", // stop type + // "type":"limit", // order type, e.g. limit, market, stop_limit. + // "createdAt":1547026472000 // time + // }, + // //------------------------------------------------------ + // // v1 (historical) trade response structure + // { + // "symbol": "SNOV-ETH", + // "dealPrice": "0.0000246", + // "dealValue": "0.018942", + // "amount": "770", + // "fee": "0.00001137", + // "side": "sell", + // "createdAt": 1540080199 + // "id":"5c4d389e4c8c60413f78e2e5", + // } + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + let trades = undefined; + if (parseResponseData) { + trades = data; + } else { + trades = this.safeValue (data, 'items', []); + } + return this.parseTrades (trades, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startAt'] = Math.floor (since / 1000); + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + const response = await this.publicGetMarketHistories (this.extend (request, params)); + // + // { + // "code": "200000", + // "data": [ + // { + // "sequence": "1548764654235", + // "side": "sell", + // "size":"0.6841354", + // "price":"0.03202", + // "time":1548848575203567174 + // } + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "sequence": "1548764654235", + // "side": "sell", + // "size":"0.6841354", + // "price":"0.03202", + // "time":1548848575203567174 + // } + // + // { + // sequence: '1568787654360', + // symbol: 'BTC-USDT', + // side: 'buy', + // size: '0.00536577', + // price: '9345', + // takerOrderId: '5e356c4a9f1a790008f8d921', + // time: '1580559434436443257', + // type: 'match', + // makerOrderId: '5e356bffedf0010008fa5d7f', + // tradeId: '5e356c4aeefabd62c62a1ece' + // } + // + // fetchMyTrades (private) v2 + // + // { + // "symbol":"BTC-USDT", + // "tradeId":"5c35c02709e4f67d5266954e", + // "orderId":"5c35c02703aa673ceec2a168", + // "counterOrderId":"5c1ab46003aa676e487fa8e3", + // "side":"buy", + // "liquidity":"taker", + // "forceTaker":true, + // "price":"0.083", + // "size":"0.8424304", + // "funds":"0.0699217232", + // "fee":"0", + // "feeRate":"0", + // "feeCurrency":"USDT", + // "stop":"", + // "type":"limit", + // "createdAt":1547026472000 + // } + // + // fetchMyTrades v2 alternative format since 2019-05-21 https://github.com/ccxt/ccxt/pull/5162 + // + // { + // symbol: "OPEN-BTC", + // forceTaker: false, + // orderId: "5ce36420054b4663b1fff2c9", + // fee: "0", + // feeCurrency: "", + // type: "", + // feeRate: "0", + // createdAt: 1558417615000, + // size: "12.8206", + // stop: "", + // price: "0", + // funds: "0", + // tradeId: "5ce390cf6e0db23b861c6e80" + // } + // + // fetchMyTrades (private) v1 (historical) + // + // { + // "symbol": "SNOV-ETH", + // "dealPrice": "0.0000246", + // "dealValue": "0.018942", + // "amount": "770", + // "fee": "0.00001137", + // "side": "sell", + // "createdAt": 1540080199 + // "id":"5c4d389e4c8c60413f78e2e5", + // } + // + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const id = this.safeString2 (trade, 'tradeId', 'id'); + const orderId = this.safeString (trade, 'orderId'); + const takerOrMaker = this.safeString (trade, 'liquidity'); + const amount = this.safeFloat2 (trade, 'size', 'amount'); + let timestamp = this.safeInteger (trade, 'time'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp / 1000000); + } else { + timestamp = this.safeInteger (trade, 'createdAt'); + // if it's a historical v1 trade, the exchange returns timestamp in seconds + if (('dealValue' in trade) && (timestamp !== undefined)) { + timestamp = timestamp * 1000; + } + } + const price = this.safeFloat2 (trade, 'price', 'dealPrice'); + const side = this.safeString (trade, 'side'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'feeCurrency'); + let feeCurrency = this.safeCurrencyCode (feeCurrencyId); + if (feeCurrency === undefined) { + if (market !== undefined) { + feeCurrency = (side === 'sell') ? market['quote'] : market['base']; + } + } + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': this.safeFloat (trade, 'feeRate'), + }; + } + const type = this.safeString (trade, 'type'); + let cost = this.safeFloat2 (trade, 'funds', 'dealValue'); + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + } + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currencyId (code); + const request = { + 'currency': currency, + 'address': address, + 'amount': amount, + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const response = await this.privatePostWithdrawals (this.extend (request, params)); + // + // https://github.com/ccxt/ccxt/issues/5558 + // + // { + // "code": 200000, + // "data": { + // "withdrawalId": "abcdefghijklmnopqrstuvwxyz" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return { + 'id': this.safeString (data, 'withdrawalId'), + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'SUCCESS': 'ok', + 'PROCESSING': 'ok', + 'FAILURE': 'failed', + }; + return this.safeString (statuses, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989", + // "memo": "5c247c8a03aa677cea2a251d", + // "amount": 1, + // "fee": 0.0001, + // "currency": "KCS", + // "isInner": false, + // "walletTxId": "5bbb57386d99522d9f954c5a@test004", + // "status": "SUCCESS", + // "createdAt": 1544178843000, + // "updatedAt": 1544178891000 + // } + // + // fetchWithdrawals + // + // { + // "id": "5c2dc64e03aa675aa263f1ac", + // "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9", + // "memo": "", + // "currency": "ETH", + // "amount": 1.0000000, + // "fee": 0.0100000, + // "walletTxId": "3e2414d82acce78d38be7fe9", + // "isInner": false, + // "status": "FAILURE", + // "createdAt": 1546503758000, + // "updatedAt": 1546504603000 + // } + // + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let address = this.safeString (transaction, 'address'); + const amount = this.safeFloat (transaction, 'amount'); + let txid = this.safeString (transaction, 'walletTxId'); + if (txid !== undefined) { + const txidParts = txid.split ('@'); + const numTxidParts = txidParts.length; + if (numTxidParts > 1) { + if (address === undefined) { + if (txidParts[1].length > 1) { + address = txidParts[1]; + } + } + } + txid = txidParts[0]; + } + let type = (txid === undefined) ? 'withdrawal' : 'deposit'; + const rawStatus = this.safeString (transaction, 'status'); + const status = this.parseTransactionStatus (rawStatus); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + let rate = undefined; + if (amount !== undefined) { + rate = feeCost / amount; + } + fee = { + 'cost': feeCost, + 'rate': rate, + 'currency': code, + }; + } + const tag = this.safeString (transaction, 'memo'); + let timestamp = this.safeInteger2 (transaction, 'createdAt', 'createAt'); + const id = this.safeString (transaction, 'id'); + let updated = this.safeInteger (transaction, 'updatedAt'); + const isV1 = !('createdAt' in transaction); + // if it's a v1 structure + if (isV1) { + type = ('address' in transaction) ? 'withdrawal' : 'deposit'; + if (timestamp !== undefined) { + timestamp = timestamp * 1000; + } + if (updated !== undefined) { + updated = updated * 1000; + } + } + return { + 'id': id, + 'address': address, + 'tag': tag, + 'currency': code, + 'amount': amount, + 'txid': txid, + 'type': type, + 'status': status, + 'fee': fee, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'updated': updated, + 'info': transaction, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + let method = 'privateGetDeposits'; + if (since !== undefined) { + // if since is earlier than 2019-02-18T00:00:00Z + if (since < 1550448000000) { + request['startAt'] = parseInt (since / 1000); + method = 'privateGetHistDeposits'; + } else { + request['startAt'] = since; + } + } + const response = await this[method] (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "currentPage": 1, + // "pageSize": 5, + // "totalNum": 2, + // "totalPage": 1, + // "items": [ + // //-------------------------------------------------- + // // version 2 deposit response structure + // { + // "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989", + // "memo": "5c247c8a03aa677cea2a251d", + // "amount": 1, + // "fee": 0.0001, + // "currency": "KCS", + // "isInner": false, + // "walletTxId": "5bbb57386d99522d9f954c5a@test004", + // "status": "SUCCESS", + // "createdAt": 1544178843000, + // "updatedAt": 1544178891000 + // }, + // //-------------------------------------------------- + // // version 1 (historical) deposit response structure + // { + // "currency": "BTC", + // "createAt": 1528536998, + // "amount": "0.03266638", + // "walletTxId": "55c643bc2c68d6f17266383ac1be9e454038864b929ae7cee0bc408cc5c869e8@12ffGWmMMD1zA1WbFm7Ho3JZ1w6NYXjpFk@234", + // "isInner": false, + // "status": "SUCCESS", + // } + // ] + // } + // } + // + const responseData = response['data']['items']; + return this.parseTransactions (responseData, currency, since, limit, { 'type': 'deposit' }); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + let method = 'privateGetWithdrawals'; + if (since !== undefined) { + // if since is earlier than 2019-02-18T00:00:00Z + if (since < 1550448000000) { + request['startAt'] = parseInt (since / 1000); + method = 'privateGetHistWithdrawals'; + } else { + request['startAt'] = since; + } + } + const response = await this[method] (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "currentPage": 1, + // "pageSize": 5, + // "totalNum": 2, + // "totalPage": 1, + // "items": [ + // //-------------------------------------------------- + // // version 2 withdrawal response structure + // { + // "id": "5c2dc64e03aa675aa263f1ac", + // "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9", + // "memo": "", + // "currency": "ETH", + // "amount": 1.0000000, + // "fee": 0.0100000, + // "walletTxId": "3e2414d82acce78d38be7fe9", + // "isInner": false, + // "status": "FAILURE", + // "createdAt": 1546503758000, + // "updatedAt": 1546504603000 + // }, + // //-------------------------------------------------- + // // version 1 (historical) withdrawal response structure + // { + // "currency": "BTC", + // "createAt": 1526723468, + // "amount": "0.534", + // "address": "33xW37ZSW4tQvg443Pc7NLCAs167Yc2XUV", + // "walletTxId": "aeacea864c020acf58e51606169240e96774838dcd4f7ce48acf38e3651323f4", + // "isInner": false, + // "status": "SUCCESS" + // } + // ] + // } + // } + // + const responseData = response['data']['items']; + return this.parseTransactions (responseData, currency, since, limit, { 'type': 'withdrawal' }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + let type = undefined; + const request = {}; + if ('type' in params) { + type = params['type']; + if (type !== undefined) { + request['type'] = type; + } + params = this.omit (params, 'type'); + } else { + const options = this.safeValue (this.options, 'fetchBalance', {}); + type = this.safeString (options, 'type', 'trade'); + } + const response = await this.privateGetAccounts (this.extend (request, params)); + // + // { + // "code":"200000", + // "data":[ + // {"balance":"0.00009788","available":"0.00009788","holds":"0","currency":"BTC","id":"5c6a4fd399a1d81c4f9cc4d0","type":"trade"}, + // {"balance":"3.41060034","available":"3.41060034","holds":"0","currency":"SOUL","id":"5c6a4d5d99a1d8182d37046d","type":"trade"}, + // {"balance":"0.01562641","available":"0.01562641","holds":"0","currency":"NEO","id":"5c6a4f1199a1d8165a99edb1","type":"trade"}, + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const result = { 'info': response }; + for (let i = 0; i < data.length; i++) { + const balance = data[i]; + const balanceType = this.safeString (balance, 'type'); + if (balanceType === type) { + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'holds'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchLedger requires a code param'); + } + await this.loadMarkets (); + await this.loadAccounts (); + const currency = this.currency (code); + let accountId = this.safeString (params, 'accountId'); + if (accountId === undefined) { + for (let i = 0; i < this.accounts.length; i++) { + const account = this.accounts[i]; + if (account['currency'] === code && account['type'] === 'main') { + accountId = account['id']; + break; + } + } + } + if (accountId === undefined) { + throw new ExchangeError (this.id + ' ' + code + 'main account is not loaded in loadAccounts'); + } + const request = { + 'accountId': accountId, + }; + if (since !== undefined) { + request['startAt'] = Math.floor (since / 1000); + } + const response = await this.privateGetAccountsAccountIdLedgers (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // totalNum: 1, + // totalPage: 1, + // pageSize: 50, + // currentPage: 1, + // items: [ + // { + // createdAt: 1561897880000, + // amount: '0.0111123', + // bizType: 'Exchange', + // balance: '0.13224427', + // fee: '0.0000111', + // context: '{"symbol":"KCS-ETH","orderId":"5d18ab98c788c6426188296f","tradeId":"5d18ab9818996813f539a806"}', + // currency: 'ETH', + // direction: 'out' + // } + // ] + // } + // } + // + const items = response['data']['items']; + return this.parseLedger (items, currency, since, limit); + } + + parseLedgerEntry (item, currency = undefined) { + // + // trade + // + // { + // createdAt: 1561897880000, + // amount: '0.0111123', + // bizType: 'Exchange', + // balance: '0.13224427', + // fee: '0.0000111', + // context: '{"symbol":"KCS-ETH","orderId":"5d18ab98c788c6426188296f","tradeId":"5d18ab9818996813f539a806"}', + // currency: 'ETH', + // direction: 'out' + // } + // + // withdrawal + // + // { + // createdAt: 1561900264000, + // amount: '0.14333217', + // bizType: 'Withdrawal', + // balance: '0', + // fee: '0.01', + // context: '{"orderId":"5d18b4e687111437cf1c48b9","txId":"0x1d136ee065c5c4c5caa293faa90d43e213c953d7cdd575c89ed0b54eb87228b8"}', + // currency: 'ETH', + // direction: 'out' + // } + // + const currencyId = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const fee = { + 'cost': this.safeFloat (item, 'fee'), + 'code': code, + }; + const amount = this.safeFloat (item, 'amount'); + const after = this.safeFloat (item, 'balance'); + const direction = this.safeString (item, 'direction'); + let before = undefined; + if (after !== undefined && amount !== undefined) { + const difference = (direction === 'out') ? amount : -amount; + before = this.sum (after, difference); + } + const timestamp = this.safeInteger (item, 'createdAt'); + const type = this.parseLedgerEntryType (this.safeString (item, 'bizType')); + const contextString = this.safeString (item, 'context'); + let id = undefined; + let referenceId = undefined; + if (this.isJsonEncodedObject (contextString)) { + const context = this.parseJson (contextString); + id = this.safeString (context, 'orderId'); + if (type === 'trade') { + referenceId = this.safeString (context, 'tradeId'); + } else if (type === 'transaction') { + referenceId = this.safeString (context, 'txId'); + } + } + return { + 'id': id, + 'currency': code, + 'account': undefined, + 'referenceAccount': undefined, + 'referenceId': referenceId, + 'status': undefined, + 'amount': amount, + 'before': before, + 'after': after, + 'fee': fee, + 'direction': direction, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'type': type, + 'info': item, + }; + } + + parseLedgerEntryType (type) { + const types = { + 'Exchange': 'trade', + 'Withdrawal': 'transaction', + 'Deposit': 'transaction', + 'Transfer': 'transfer', + }; + return this.safeString (types, type, type); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + // + // the v2 URL is https://openapi-v2.kucoin.com/api/v1/endpoint + // † ↑ + // + const version = this.safeString (params, 'version', this.options['version']); + params = this.omit (params, 'version'); + let endpoint = '/api/' + version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let endpart = ''; + headers = (headers !== undefined) ? headers : {}; + if (Object.keys (query).length) { + if (method !== 'GET') { + body = this.json (query); + endpart = body; + headers['Content-Type'] = 'application/json'; + } else { + endpoint += '?' + this.urlencode (query); + } + } + const url = this.urls['api'][api] + endpoint; + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.nonce ().toString (); + headers = this.extend ({ + 'KC-API-KEY': this.apiKey, + 'KC-API-TIMESTAMP': timestamp, + 'KC-API-PASSPHRASE': this.password, + }, headers); + const payload = timestamp + method + endpoint + endpart; + const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64'); + headers['KC-API-SIGN'] = this.decode (signature); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + this.throwBroadlyMatchedException (this.exceptions['broad'], body, body); + return; + } + // + // bad + // { "code": "400100", "msg": "validation.createOrder.clientOidIsRequired" } + // good + // { code: '200000', data: { ... }} + // + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'msg'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, message); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, message); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],103:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const acx = require ('./acx.js'); +const { ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class kuna extends acx { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kuna', + 'name': 'Kuna', + 'countries': [ 'UA' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchOHLCV': false, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'withdraw': false, + }, + 'urls': { + 'referral': 'https://kuna.io?r=kunaid-gvfihe8az7o4', + 'logo': 'https://user-images.githubusercontent.com/1294454/31697638-912824fa-b3c1-11e7-8c36-cf9606eb94ac.jpg', + 'api': 'https://kuna.io', + 'www': 'https://kuna.io', + 'doc': 'https://kuna.io/documents/api', + 'fees': 'https://kuna.io/documents/api', + }, + 'fees': { + 'trading': { + 'taker': 0.25 / 100, + 'maker': 0.25 / 100, + }, + 'funding': { + 'withdraw': { + 'UAH': '1%', + 'BTC': 0.001, + 'BCH': 0.001, + 'ETH': 0.01, + 'WAVES': 0.01, + 'GOL': 0.0, + 'GBG': 0.0, + // 'RMC': 0.001 BTC + // 'ARN': 0.01 ETH + // 'R': 0.01 ETH + // 'EVR': 0.01 ETH + }, + 'deposit': { + // 'UAH': (amount) => amount * 0.001 + 5 + }, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const quotes = [ 'btc', 'eth', 'eurs', 'rub', 'uah', 'usd', 'usdt' ]; + const pricePrecisions = { + 'UAH': 0, + }; + const markets = []; + const response = await this.publicGetTickers (params); + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + for (let j = 0; j < quotes.length; j++) { + const quoteId = quotes[j]; + const index = id.indexOf (quoteId); + const slice = id.slice (index); + if ((index > 0) && (slice === quoteId)) { + const baseId = id.replace (quoteId, ''); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 6, + 'price': this.safeInteger (pricePrecisions, quote, 6), + }; + markets.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + break; + } + } + } + return markets; + } + + async fetchL3OrderBook (symbol, limit = undefined, params = {}) { + return await this.fetchOrderBook (symbol, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privateGetOrders (this.extend (request, params)); + // todo emulation of fetchClosedOrders, fetchOrders, fetchOrder + // with order cache + fetchOpenOrders + // as in BTC-e, Liqui, Yobit, DSX, Tidex, WEX + return this.parseOrders (response, market, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + let side = this.safeString2 (trade, 'side', 'trend'); + if (side !== undefined) { + const sideMap = { + 'ask': 'sell', + 'bid': 'buy', + }; + side = this.safeString (sideMap, side); + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + const cost = this.safeFloat (trade, 'funds'); + const orderId = this.safeString (trade, 'order_id'); + const id = this.safeString (trade, 'id'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': orderId, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privateGetTradesMy (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } +}; + +},{"./acx.js":4,"./base/errors":9}],104:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class lakebtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'lakebtc', + 'name': 'LakeBTC', + 'countries': [ 'US' ], + 'version': 'api_v2', + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchTickers': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28074120-72b7c38a-6660-11e7-92d9-d9027502281d.jpg', + 'api': 'https://api.lakebtc.com', + 'www': 'https://www.lakebtc.com', + 'doc': [ + 'https://www.lakebtc.com/s/api_v2', + 'https://www.lakebtc.com/s/api', + ], + }, + 'api': { + 'public': { + 'get': [ + 'bcorderbook', + 'bctrades', + 'ticker', + ], + }, + 'private': { + 'post': [ + 'buyOrder', + 'cancelOrders', + 'getAccountInfo', + 'getExternalAccounts', + 'getOrders', + 'getTrades', + 'openOrders', + 'sellOrder', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.15 / 100, + 'taker': 0.2 / 100, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTicker (params); + const result = []; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = response[id]; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = baseId.toUpperCase (); + const quote = quoteId.toUpperCase (); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const balances = this.safeValue (response, 'balance', {}); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balances, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetBcorderbook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + let symbol = ids[i]; + const ticker = response[symbol]; + let market = undefined; + if (symbol in this.markets_by_id) { + market = this.markets_by_id[symbol]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tickers = await this.publicGetTicker (params); + return this.parseTicker (tickers[market['id']], market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetBctrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const method = 'privatePost' + this.capitalize (side) + 'Order'; + const market = this.market (symbol); + const order = { + 'params': [ price, amount, market['id'] ], + }; + const response = await this[method] (this.extend (order, params)); + return { + 'info': response, + 'id': this.safeString (response, 'id'), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'params': [ id ], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + nonce () { + return this.microseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version; + if (api === 'public') { + url += '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + let queryParams = ''; + if ('params' in params) { + const paramsList = params['params']; + queryParams = paramsList.join (','); + } + const query = this.urlencode ({ + 'tonce': nonce, + 'accesskey': this.apiKey, + 'requestmethod': method.toLowerCase (), + 'id': nonce, + 'method': path, + 'params': queryParams, + }); + body = this.json ({ + 'method': path, + 'params': queryParams, + 'id': nonce, + }); + const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha1'); + const auth = this.encode (this.apiKey + ':' + signature); + headers = { + 'Json-Rpc-Tonce': nonce.toString (), + 'Authorization': 'Basic ' + this.decode (this.stringToBase64 (auth)), + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],105:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InvalidNonce, OrderNotFound, InvalidOrder, DDoSProtection, BadRequest, AuthenticationError } = require ('./base/errors'); +const { ROUND } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class latoken extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'latoken', + 'name': 'Latoken', + 'countries': [ 'KY' ], // Cayman Islands + 'version': 'v1', + 'rateLimit': 2000, + 'certified': false, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + 'publicAPI': true, + 'privateAPI': true, + 'cancelOrder': true, + 'cancelAllOrders': true, + 'createMarketOrder': false, + 'createOrder': true, + 'fetchBalance': true, + 'fetchCanceledOrders': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + 'fetchOrder': false, + 'fetchOrdersByStatus': true, + 'fetchOrderBook': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/61511972-24c39f00-aa01-11e9-9f7c-471f1d6e5214.jpg', + 'api': 'https://api.latoken.com', + 'www': 'https://latoken.com', + 'doc': [ + 'https://api.latoken.com', + ], + }, + 'api': { + 'public': { + 'get': [ + 'ExchangeInfo/time', + 'ExchangeInfo/limits', + 'ExchangeInfo/pairs', + 'ExchangeInfo/pairs/{currency}', + 'ExchangeInfo/pair', + 'ExchangeInfo/currencies', + 'ExchangeInfo/currencies/{symbol}', + 'MarketData/tickers', + 'MarketData/ticker/{symbol}', + 'MarketData/orderBook/{symbol}', + 'MarketData/orderBook/{symbol}/{limit}', + 'MarketData/trades/{symbol}', + 'MarketData/trades/{symbol}/{limit}', + ], + }, + 'private': { + 'get': [ + 'Account/balances', + 'Account/balances/{currency}', + 'Order/status', + 'Order/active', + 'Order/get_order', + 'Order/trades', + ], + 'post': [ + 'Order/new', + 'Order/test-order', + 'Order/cancel', + 'Order/cancel_all', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + }, + 'commonCurrencies': { + 'TSL': 'Treasure SL', + }, + 'options': { + 'createOrderMethod': 'private_post_order_new', // private_post_order_test_order + }, + 'exceptions': { + 'exact': { + 'Signature or ApiKey is not valid': AuthenticationError, + 'Request is out of time': InvalidNonce, + 'Symbol must be specified': BadRequest, + }, + 'broad': { + 'Request limit reached': DDoSProtection, + 'Pair': BadRequest, + 'Price needs to be greater than': InvalidOrder, + 'Amount needs to be greater than': InvalidOrder, + 'The Symbol field is required': InvalidOrder, + 'OrderType is not valid': InvalidOrder, + 'Side is not valid': InvalidOrder, + 'Cancelable order whit': OrderNotFound, + 'Order': OrderNotFound, + }, + }, + }); + } + + nonce () { + return this.milliseconds (); + } + + async fetchTime (params = {}) { + const response = await this.publicGetExchangeInfoTime (params); + // + // { + // "time": "2019-04-18T9:00:00.0Z", + // "unixTimeSeconds": 1555578000, + // "unixTimeMiliseconds": 1555578000000 + // } + // + return this.safeInteger (response, 'unixTimeMiliseconds'); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeInfoPairs (params); + // + // [ + // { + // "pairId": 502, + // "symbol": "LAETH", + // "baseCurrency": "LA", + // "quotedCurrency": "ETH", + // "makerFee": 0.01, + // "takerFee": 0.01, + // "pricePrecision": 8, + // "amountPrecision": 8, + // "minQty": 0.1 + // } + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + // the exchange shows them inverted + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quotedCurrency'); + const numericId = this.safeInteger (market, 'pairId'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': this.safeInteger (market, 'pricePrecision'), + 'amount': this.safeInteger (market, 'amountPrecision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minQty'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'numericId': numericId, + 'info': market, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, // assuming true + 'precision': precision, + 'limits': limits, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetExchangeInfoCurrencies (params); + // + // [ + // { + // "currencyId": 102, + // "symbol": "LA", + // "name": "Latoken", + // "precission": 8, + // "type": "ERC20", + // "fee": 0.1 + // } + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const numericId = this.safeInteger (currency, 'currencyId'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'precission'); + const fee = this.safeFloat (currency, 'fee'); + const active = undefined; + result[code] = { + 'id': id, + 'numericId': numericId, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let precision = market['precision']['price']; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + precision = market['precision']['amount']; + } + cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (cost), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountBalances (params); + // + // [ + // { + // "currencyId": 102, + // "symbol": "LA", + // "name": "Latoken", + // "amount": 1054.66, + // "available": 900.66, + // "frozen": 154, + // "pending": 0 + // } + // ] + // + const result = { + 'info': response, + }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'symbol'); + const code = this.safeCurrencyCode (currencyId); + const frozen = this.safeFloat (balance, 'frozen'); + const pending = this.safeFloat (balance, 'pending'); + const used = this.sum (frozen, pending); + const account = { + 'free': this.safeFloat (balance, 'available'), + 'used': used, + 'total': this.safeFloat (balance, 'amount'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'limit': 10, + }; + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 100 + } + const response = await this.publicGetMarketDataOrderBookSymbolLimit (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "spread": 0.07, + // "asks": [ + // { "price": 136.3, "quantity": 7.024 } + // ], + // "bids": [ + // { "price": 136.2, "quantity": 6.554 } + // ] + // } + // + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'quantity'); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "pairId":"63b41092-f3f6-4ea4-9e7c-4525ed250dad", + // "symbol":"ETHBTC", + // "volume":11317.037494474000000000, + // "open":0.020033000000000000, + // "low":0.019791000000000000, + // "high":0.020375000000000000, + // "close":0.019923000000000000, + // "priceChange":-0.1500 + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const open = this.safeFloat (ticker, 'open'); + const close = this.safeFloat (ticker, 'close'); + let change = undefined; + if (open !== undefined && close !== undefined) { + change = close - open; + } + const percentage = this.safeFloat (ticker, 'priceChange'); + const timestamp = this.nonce (); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'low': this.safeFloat (ticker, 'low'), + 'high': this.safeFloat (ticker, 'high'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetMarketDataTickerSymbol (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "volume": 1023314.3202, + // "open": 134.82, + // "low": 133.95, + // "high": 136.22, + // "close": 135.12, + // "priceChange": 0.22 + // } + // + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketDataTickers (params); + // + // [ + // { + // "pairId": 502, + // "symbol": "LAETH", + // "volume": 1023314.3202, + // "open": 134.82, + // "low": 133.95, + // "high": 136.22, + // "close": 135.12, + // "priceChange": 0.22 + // } + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + if (symbols === undefined || this.inArray (symbol, symbols)) { + result[symbol] = ticker; + } + } + return result; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // side: 'buy', + // price: 0.33634, + // amount: 0.01, + // timestamp: 1564240008000 // milliseconds + // } + // + // fetchMyTrades (private) + // + // { + // id: '1564223032.892829.3.tg15', + // orderId: '1564223032.671436.707548@1379:1', + // commission: 0, + // side: 'buy', + // price: 0.32874, + // amount: 0.607, + // timestamp: 1564223033 // seconds + // } + // + const type = undefined; + let timestamp = this.safeInteger2 (trade, 'timestamp', 'time'); + if (timestamp !== undefined) { + // 03 Jan 2009 - first block + if (timestamp < 1230940800000) { + timestamp *= 1000; + } + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const side = this.safeString (trade, 'side'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'orderId'); + const feeCost = this.safeFloat (trade, 'commission'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': undefined, + }; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': type, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 50, max 100 + } + const response = await this.publicGetMarketDataTradesSymbol (this.extend (request, params)); + // + // { + // "pairId":370, + // "symbol":"ETHBTC", + // "tradeCount":51, + // "trades": [ + // { + // side: 'buy', + // price: 0.33634, + // amount: 0.01, + // timestamp: 1564240008000 // milliseconds + // } + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.privateGetOrderTrades (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "tradeCount": 1, + // "trades": [ + // { + // id: '1564223032.892829.3.tg15', + // orderId: '1564223032.671436.707548@1379:1', + // commission: 0, + // side: 'buy', + // price: 0.32874, + // amount: 0.607, + // timestamp: 1564223033 // seconds + // } + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'active': 'open', + 'partiallyFilled': 'open', + 'filled': 'closed', + 'cancelled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "orderId":"1563460093.134037.704945@0370:2", + // "cliOrdId":"", + // "pairId":370, + // "symbol":"ETHBTC", + // "side":"sell", + // "orderType":"limit", + // "price":1.0, + // "amount":1.0 + // } + // + // cancelOrder, fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders + // + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // + const id = this.safeString (order, 'orderId'); + const timestamp = this.safeTimestamp (order, 'timeCreated'); + const marketId = this.safeString (order, 'symbol'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (order, 'side'); + const type = this.safeString (order, 'orderType'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'executedAmount'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); + let cost = undefined; + if (filled !== undefined) { + if (price !== undefined) { + cost = filled * price; + } + } + const timeFilled = this.safeTimestamp (order, 'timeFilled'); + let lastTradeTimestamp = undefined; + if ((timeFilled !== undefined) && (timeFilled > 0)) { + lastTradeTimestamp = timeFilled; + } + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'average': undefined, + 'remaining': remaining, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersWithMethod ('private_get_order_active', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByStatus ('filled', symbol, since, limit, params); + } + + async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByStatus ('cancelled', symbol, since, limit, params); + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': status, + }; + return this.fetchOrdersWithMethod ('private_get_order_status', symbol, since, limit, this.extend (request, params)); + } + + async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrdersWithMethod requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 100 + } + const response = await this[method] (this.extend (request, params)); + // + // [ + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privateGetOrderGetOrder (this.extend (request, params)); + // + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // + return this.parseOrder (response); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'symbol': this.marketId (symbol), + 'side': side, + 'price': this.priceToPrecision (symbol, price), + 'amount': this.amountToPrecision (symbol, amount), + 'orderType': type, + }; + const method = this.safeString (this.options, 'createOrderMethod', 'private_post_order_new'); + const response = await this[method] (this.extend (request, params)); + // + // { + // "orderId":"1563460093.134037.704945@0370:2", + // "cliOrdId":"", + // "pairId":370, + // "symbol":"ETHBTC", + // "side":"sell", + // "orderType":"limit", + // "price":1.0, + // "amount":1.0 + // } + // + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + // + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // + return this.parseOrder (response); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); + } + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'symbol': marketId, + }; + const response = await this.privatePostOrderCancelAll (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "cancelledOrders": [ + // "1555492358.126073.126767@0502:2" + // ] + // } + // + const result = []; + const canceledOrders = this.safeValue (response, 'cancelledOrders', []); + for (let i = 0; i < canceledOrders.length; i++) { + const order = this.parseOrder ({ + 'symbol': marketId, + 'orderId': canceledOrders[i], + 'orderStatus': 'canceled', + }); + result.push (order); + } + return result; + } + + sign (path, api = 'public', method = 'GET', params = undefined, headers = undefined, body = undefined) { + let request = '/api/' + this.version + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + const nonce = this.nonce (); + query = this.extend ({ + 'timestamp': nonce, + }, query); + } + const urlencodedQuery = this.urlencode (query); + if (Object.keys (query).length) { + request += '?' + urlencodedQuery; + } + if (api === 'private') { + this.checkRequiredCredentials (); + const signature = this.hmac (this.encode (request), this.encode (this.secret)); + headers = { + 'X-LA-KEY': this.apiKey, + 'X-LA-SIGNATURE': signature, + }; + if (method === 'POST') { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + body = urlencodedQuery; + } + } + const url = this.urls['api'] + request; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; + } + // + // { "message": "Request limit reached!", "details": "Request limit reached. Maximum allowed: 1 per 1s. Please try again in 1 second(s)." } + // { "error": { "message": "Pair 370 is not found","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Signature or ApiKey is not valid","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Request is out of time", "errorType": "RequestError", "statusCode":400 }} + // { "error": { "message": "Price needs to be greater than 0","errorType":"ValidationError","statusCode":400 }} + // { "error": { "message": "Side is not valid, Price needs to be greater than 0, Amount needs to be greater than 0, The Symbol field is required., OrderType is not valid","errorType":"ValidationError","statusCode":400 }} + // { "error": { "message": "Cancelable order whit ID 1563460289.571254.704945@0370:1 not found","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Symbol must be specified","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Order 1563460289.571254.704945@0370:1 is not found","errorType":"RequestError","statusCode":400 }} + // + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + const error = this.safeValue (response, 'error', {}); + const errorMessage = this.safeString (error, 'message'); + if (errorMessage !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], errorMessage, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], errorMessage, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],106:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, DDoSProtection, AuthenticationError, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class lbank extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'lbank', + 'name': 'LBank', + 'countries': [ 'CN' ], + 'version': 'v1', + 'has': { + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': false, // status 0 API doesn't work + 'fetchClosedOrders': true, + }, + 'timeframes': { + '1m': 'minute1', + '5m': 'minute5', + '15m': 'minute15', + '30m': 'minute30', + '1h': 'hour1', + '2h': 'hour2', + '4h': 'hour4', + '6h': 'hour6', + '8h': 'hour8', + '12h': 'hour12', + '1d': 'day1', + '1w': 'week1', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg', + 'api': 'https://api.lbank.info', + 'www': 'https://www.lbank.info', + 'doc': 'https://github.com/LBank-exchange/lbank-official-api-docs', + 'fees': 'https://lbankinfo.zendesk.com/hc/en-gb/articles/360012072873-Trading-Fees', + 'referral': 'https://www.lbex.io/invite?icode=7QCY', + }, + 'api': { + 'public': { + 'get': [ + 'currencyPairs', + 'ticker', + 'depth', + 'trades', + 'kline', + 'accuracy', + ], + }, + 'private': { + 'post': [ + 'user_info', + 'create_order', + 'cancel_order', + 'orders_info', + 'orders_info_history', + 'withdraw', + 'withdrawCancel', + 'withdraws', + 'withdrawConfigs', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': undefined, + 'ZEC': 0.01, + 'ETH': 0.01, + 'ETC': 0.01, + // 'QTUM': amount => Math.max (0.01, amount * (0.1 / 100)), + 'VEN': 10.0, + 'BCH': 0.0002, + 'SC': 50.0, + 'BTM': 20.0, + 'NAS': 1.0, + 'EOS': 1.0, + 'XWC': 5.0, + 'BTS': 1.0, + 'INK': 10.0, + 'BOT': 3.0, + 'YOYOW': 15.0, + 'TGC': 10.0, + 'NEO': 0.0, + 'CMT': 20.0, + 'SEER': 2000.0, + 'FIL': undefined, + 'BTG': undefined, + }, + }, + }, + 'commonCurrencies': { + 'VET_ERC20': 'VEN', + }, + 'options': { + 'cacheSecretAsPem': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAccuracy (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = market['symbol']; + const parts = id.split ('_'); + let baseId = undefined; + let quoteId = undefined; + const numParts = parts.length; + // lbank will return symbols like "vet_erc20_usdt" + if (numParts > 2) { + baseId = parts[0] + '_' + parts[1]; + quoteId = parts[2]; + } else { + baseId = parts[0]; + quoteId = parts[1]; + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'quantityAccuracy'), + 'price': this.safeInteger (market, 'priceAccuracy'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': id, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + const market = this.marketsById[marketId]; + symbol = market['symbol']; + } else { + const parts = marketId.split ('_'); + let baseId = undefined; + let quoteId = undefined; + const numParts = parts.length; + // lbank will return symbols like "vet_erc20_usdt" + if (numParts > 2) { + baseId = parts[0] + '_' + parts[1]; + quoteId = parts[2]; + } else { + baseId = parts[0]; + quoteId = parts[1]; + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const timestamp = this.safeInteger (ticker, 'timestamp'); + const info = ticker; + ticker = info['ticker']; + const last = this.safeFloat (ticker, 'latest'); + const percentage = this.safeFloat (ticker, 'change'); + let open = undefined; + if (percentage !== undefined) { + const relativeChange = this.sum (1, percentage / 100); + if (relativeChange > 0) { + open = last / this.sum (1, relativeChange); + } + } + let change = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'turnover'), + 'info': info, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': 'all', + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchOrderBook (symbol, limit = 60, params = {}) { + await this.loadMarkets (); + let size = 60; + if (limit !== undefined) { + size = Math.min (limit, size); + } + const request = { + 'symbol': this.marketId (symbol), + 'size': size, + }; + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'date_ms'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + return { + 'id': id, + 'info': this.safeValue (trade, 'info', trade), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'size': 100, + }; + if (since !== undefined) { + request['time'] = parseInt (since); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (since === undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a `since` argument'); + } + if (limit === undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a `limit` argument'); + } + const request = { + 'symbol': market['id'], + 'type': this.timeframes[timeframe], + 'size': limit, + 'time': parseInt (since / 1000), + }; + const response = await this.publicGetKline (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserInfo (params); + // + // { + // "result":"true", + // "info":{ + // "freeze":{ + // "iog":"0.00000000", + // "ssc":"0.00000000", + // "eon":"0.00000000", + // }, + // "asset":{ + // "iog":"0.00000000", + // "ssc":"0.00000000", + // "eon":"0.00000000", + // }, + // "free":{ + // "iog":"0.00000000", + // "ssc":"0.00000000", + // "eon":"0.00000000", + // }, + // } + // } + // + const result = { 'info': response }; + const info = this.safeValue (response, 'info', {}); + const free = this.safeValue (info, 'free', {}); + const freeze = this.safeValue (info, 'freeze', {}); + const asset = this.safeValue (info, 'asset', {}); + const currencyIds = Object.keys (free); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['used'] = this.safeFloat (freeze, currencyId); + account['total'] = this.safeFloat (asset, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + '-1': 'cancelled', // cancelled + '0': 'open', // not traded + '1': 'open', // partial deal + '2': 'closed', // complete deal + '4': 'closed', // disposal processing + }; + return this.safeString (statuses, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + const responseMarket = this.safeValue (this.marketsById, order['symbol']); + if (responseMarket !== undefined) { + symbol = responseMarket['symbol']; + } else if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'create_time'); + // Limit Order Request Returns: Order Price + // Market Order Returns: cny amount of market order + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount', 0.0); + const filled = this.safeFloat (order, 'deal_amount', 0.0); + const av_price = this.safeFloat (order, 'avg_price'); + let cost = undefined; + if (av_price !== undefined) { + cost = filled * av_price; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString (order, 'order_id'); + const type = this.safeString (order, 'order_type'); + const side = this.safeString (order, 'type'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': this.safeValue (order, 'info', order), + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let order = { + 'symbol': market['id'], + 'type': side, + 'amount': amount, + }; + if (type === 'market') { + order['type'] += '_market'; + } else { + order['price'] = price; + } + const response = await this.privatePostCreateOrder (this.extend (order, params)); + order = this.omit (order, 'type'); + order['order_id'] = response['order_id']; + order['type'] = side; + order['order_type'] = type; + order['create_time'] = this.milliseconds (); + order['info'] = response; + order = this.parseOrder (order, market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'order_id': id, + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + // Id can be a list of ids delimited by a comma + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'order_id': id, + }; + const response = await this.privatePostOrdersInfo (this.extend (request, params)); + const orders = this.parseOrders (response['orders'], market); + const numOrders = orders.length; + if (numOrders === 1) { + return orders[0]; + } else { + return orders; + } + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'current_page': 1, + 'page_length': limit, + }; + const response = await this.privatePostOrdersInfoHistory (this.extend (request, params)); + return this.parseOrders (response['orders'], undefined, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + const closed = this.filterBy (orders, 'status', 'closed'); + const canceled = this.filterBy (orders, 'status', 'cancelled'); // cancelled orders may be partially filled + const allOrders = this.arrayConcat (closed, canceled); + return this.filterBySymbolSinceLimit (allOrders, symbol, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + // mark and fee are optional params, mark is a note and must be less than 255 characters + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'assetCode': currency['id'], + 'amount': amount, + 'account': address, + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const response = this.privatePostWithdraw (this.extend (request, params)); + return { + 'id': response['id'], + 'info': response, + }; + } + + convertSecretToPem (secret) { + const lineLength = 64; + const secretLength = secret.length - 0; + let numLines = parseInt (secretLength / lineLength); + numLines = this.sum (numLines, 1); + let pem = "-----BEGIN PRIVATE KEY-----\n"; // eslint-disable-line + for (let i = 0; i < numLines; i++) { + const start = i * lineLength; + const end = this.sum (start, lineLength); + pem += this.secret.slice (start, end) + "\n"; // eslint-disable-line + } + return pem + '-----END PRIVATE KEY-----'; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + // Every endpoint ends with ".do" + url += '.do'; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const query = this.keysort (this.extend ({ + 'api_key': this.apiKey, + }, params)); + const queryString = this.rawencode (query); + const message = this.hash (this.encode (queryString)).toUpperCase (); + const cacheSecretAsPem = this.safeValue (this.options, 'cacheSecretAsPem', true); + let pem = undefined; + if (cacheSecretAsPem) { + pem = this.safeValue (this.options, 'pem'); + if (pem === undefined) { + pem = this.convertSecretToPem (this.secret); + this.options['pem'] = pem; + } + } else { + pem = this.convertSecretToPem (this.secret); + } + const sign = this.binaryToBase64 (this.rsa (message, this.encode (pem), 'RS256')); + query['sign'] = sign; + body = this.urlencode (query); + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const success = this.safeString (response, 'result'); + if (success === 'false') { + const errorCode = this.safeString (response, 'error_code'); + const message = this.safeString ({ + '10000': 'Internal error', + '10001': 'The required parameters can not be empty', + '10002': 'verification failed', + '10003': 'Illegal parameters', + '10004': 'User requests are too frequent', + '10005': 'Key does not exist', + '10006': 'user does not exist', + '10007': 'Invalid signature', + '10008': 'This currency pair is not supported', + '10009': 'Limit orders can not be missing orders and the number of orders', + '10010': 'Order price or order quantity must be greater than 0', + '10011': 'Market orders can not be missing the amount of the order', + '10012': 'market sell orders can not be missing orders', + '10013': 'is less than the minimum trading position 0.001', + '10014': 'Account number is not enough', + '10015': 'The order type is wrong', + '10016': 'Account balance is not enough', + '10017': 'Abnormal server', + '10018': 'order inquiry can not be more than 50 less than one', + '10019': 'withdrawal orders can not be more than 3 less than one', + '10020': 'less than the minimum amount of the transaction limit of 0.001', + '10022': 'Insufficient key authority', + }, errorCode, this.json (response)); + const ErrorClass = this.safeValue ({ + '10002': AuthenticationError, + '10004': DDoSProtection, + '10005': AuthenticationError, + '10006': AuthenticationError, + '10007': AuthenticationError, + '10009': InvalidOrder, + '10010': InvalidOrder, + '10011': InvalidOrder, + '10012': InvalidOrder, + '10013': InvalidOrder, + '10014': InvalidOrder, + '10015': InvalidOrder, + '10016': InvalidOrder, + '10022': AuthenticationError, + }, errorCode, ExchangeError); + throw new ErrorClass (message); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],107:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InvalidNonce, OrderNotFound, InvalidOrder, InsufficientFunds, AuthenticationError, DDoSProtection, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class liquid extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'liquid', + 'name': 'Liquid', + 'countries': [ 'JP', 'CN', 'TW' ], + 'version': '2', + 'rateLimit': 1000, + 'has': { + 'CORS': false, + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/45798859-1a872600-bcb4-11e8-8746-69291ce87b04.jpg', + 'api': 'https://api.liquid.com', + 'www': 'https://www.liquid.com', + 'doc': [ + 'https://developers.liquid.com', + ], + 'fees': 'https://help.liquid.com/getting-started-with-liquid/the-platform/fee-structure', + 'referral': 'https://www.liquid.com?affiliate=SbzC62lt30976', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'products', + 'products/{id}', + 'products/{id}/price_levels', + 'executions', + 'ir_ladders/{currency}', + 'fees', // add fetchFees, fetchTradingFees, fetchFundingFees + ], + }, + 'private': { + 'get': [ + 'accounts/balance', + 'accounts/main_asset', + 'accounts/{id}', + 'accounts/{currency}/reserved_balance_details', + 'crypto_accounts', // add fetchAccounts + 'crypto_withdrawals', // add fetchWithdrawals + 'executions/me', + 'fiat_accounts', // add fetchAccounts + 'fund_infos', // add fetchDeposits + 'loan_bids', + 'loans', + 'orders', + 'orders/{id}', + 'orders/{id}/trades', // add fetchOrderTrades + 'trades', + 'trades/{id}/loans', + 'trading_accounts', + 'trading_accounts/{id}', + 'transactions', + 'withdrawals', // add fetchWithdrawals + ], + 'post': [ + 'crypto_withdrawals', + 'fund_infos', + 'fiat_accounts', + 'loan_bids', + 'orders', + 'withdrawals', + ], + 'put': [ + 'crypto_withdrawal/{id}/cancel', + 'loan_bids/{id}/close', + 'loans/{id}', + 'orders/{id}', // add editOrder + 'orders/{id}/cancel', + 'trades/{id}', + 'trades/{id}/adjust_margin', + 'trades/{id}/close', + 'trades/close_all', + 'trading_accounts/{id}', + 'withdrawals/{id}/cancel', + ], + }, + }, + 'exceptions': { + 'API rate limit exceeded. Please retry after 300s': DDoSProtection, + 'API Authentication failed': AuthenticationError, + 'Nonce is too small': InvalidNonce, + 'Order not found': OrderNotFound, + 'Can not update partially filled order': InvalidOrder, + 'Can not update non-live order': OrderNotFound, + 'not_enough_free_balance': InsufficientFunds, + 'must_be_positive': InvalidOrder, + 'less_than_order_size': InvalidOrder, + }, + 'commonCurrencies': { + 'WIN': 'WCOIN', + 'HOT': 'HOT Token', + }, + 'options': { + 'cancelOrderException': true, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // [ + // { + // currency_type: 'fiat', + // currency: 'USD', + // symbol: '$', + // assets_precision: 2, + // quoting_precision: 5, + // minimum_withdrawal: '15.0', + // withdrawal_fee: 5, + // minimum_fee: null, + // minimum_order_quantity: null, + // display_precision: 2, + // depositable: true, + // withdrawable: true, + // discount_fee: 0.5, + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'currency'); + const code = this.safeCurrencyCode (id); + const active = currency['depositable'] && currency['withdrawable']; + const amountPrecision = this.safeInteger (currency, 'display_precision'); + const pricePrecision = this.safeInteger (currency, 'quoting_precision'); + const precision = Math.max (amountPrecision, pricePrecision); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': this.safeFloat (currency, 'withdrawal_fee'), + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -amountPrecision), + 'max': Math.pow (10, amountPrecision), + }, + 'price': { + 'min': Math.pow (10, -pricePrecision), + 'max': Math.pow (10, pricePrecision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimum_withdrawal'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetProducts (); + // + // [ + // { + // id: '7', + // product_type: 'CurrencyPair', + // code: 'CASH', + // name: ' CASH Trading', + // market_ask: 8865.79147, + // market_bid: 8853.95988, + // indicator: 1, + // currency: 'SGD', + // currency_pair_code: 'BTCSGD', + // symbol: 'S$', + // btc_minimum_withdraw: null, + // fiat_minimum_withdraw: null, + // pusher_channel: 'product_cash_btcsgd_7', + // taker_fee: 0, + // maker_fee: 0, + // low_market_bid: '8803.25579', + // high_market_ask: '8905.0', + // volume_24h: '15.85443468', + // last_price_24h: '8807.54625', + // last_traded_price: '8857.77206', + // last_traded_quantity: '0.00590974', + // quoted_currency: 'SGD', + // base_currency: 'BTC', + // disabled: false, + // }, + // ] + // + const currencies = await this.fetchCurrencies (); + const currenciesByCode = this.indexBy (currencies, 'code'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = market['id'].toString (); + const baseId = market['base_currency']; + const quoteId = market['quoted_currency']; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const maker = this.safeFloat (market, 'maker_fee'); + const taker = this.safeFloat (market, 'taker_fee'); + const active = !market['disabled']; + const baseCurrency = this.safeValue (currenciesByCode, base); + const quoteCurrency = this.safeValue (currenciesByCode, quote); + const precision = { + 'amount': 8, + 'price': 8, + }; + let minAmount = undefined; + if (baseCurrency !== undefined) { + minAmount = this.safeFloat (baseCurrency['info'], 'minimum_order_quantity'); + // precision['amount'] = this.safeInteger (baseCurrency['info'], 'quoting_precision'); + } + let minPrice = undefined; + if (quoteCurrency !== undefined) { + precision['price'] = this.safeInteger (quoteCurrency['info'], 'quoting_precision'); + minPrice = Math.pow (10, -precision['price']); + } + let minCost = undefined; + if (minPrice !== undefined) { + if (minAmount !== undefined) { + minCost = minPrice * minAmount; + } + } + const limits = { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'maker': maker, + 'taker': taker, + 'limits': limits, + 'precision': precision, + 'active': active, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountsBalance (params); + // + // [ + // {"currency":"USD","balance":"0.0"}, + // {"currency":"BTC","balance":"0.0"}, + // {"currency":"ETH","balance":"0.1651354"} + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetProductsIdPriceLevels (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'buy_price_levels', 'sell_price_levels'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let last = undefined; + if ('last_traded_price' in ticker) { + if (ticker['last_traded_price']) { + const length = ticker['last_traded_price'].length; + if (length > 0) { + last = this.safeFloat (ticker, 'last_traded_price'); + } + } + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (ticker, 'base_currency'); + const quoteId = this.safeString (ticker, 'quoted_currency'); + if (symbol in this.markets) { + market = this.markets[symbol]; + } else { + symbol = this.safeCurrencyCode (baseId) + '/' + this.safeCurrencyCode (quoteId); + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let change = undefined; + let percentage = undefined; + let average = undefined; + const open = this.safeFloat (ticker, 'last_price_24h'); + if (open !== undefined && last !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + if (open > 0) { + percentage = change / open * 100; + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high_market_ask'), + 'low': this.safeFloat (ticker, 'low_market_bid'), + 'bid': this.safeFloat (ticker, 'market_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'market_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'volume_24h'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetProducts (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetProductsId (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market) { + // { id: 12345, + // quantity: "6.789", + // price: "98765.4321", + // taker_side: "sell", + // created_at: 1512345678, + // my_side: "buy" } + const timestamp = this.safeTimestamp (trade, 'created_at'); + const orderId = this.safeString (trade, 'order_id'); + // 'taker_side' gets filled for both fetchTrades and fetchMyTrades + const takerSide = this.safeString (trade, 'taker_side'); + // 'my_side' gets filled for fetchMyTrades only and may differ from 'taker_side' + const mySide = this.safeString (trade, 'my_side'); + const side = (mySide !== undefined) ? mySide : takerSide; + let takerOrMaker = undefined; + if (mySide !== undefined) { + takerOrMaker = (takerSide === mySide) ? 'taker' : 'maker'; + } + let cost = undefined; + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_id': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + // timestamp should be in seconds, whereas we use milliseconds in since and everywhere + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.publicGetExecutions (this.extend (request, params)); + const result = (since !== undefined) ? response : response['models']; + return this.parseTrades (result, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the `with_details` param is undocumented - it adds the order_id to the results + const request = { + 'product_id': market['id'], + 'with_details': true, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetExecutionsMe (this.extend (request, params)); + return this.parseTrades (response['models'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_type': type, + 'product_id': this.marketId (symbol), + 'side': side, + 'quantity': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "id": 2157474, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.0", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "live", + // "leverage_level": 1, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0" + // } + // + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePutOrdersIdCancel (this.extend (request, params)); + const order = this.parseOrder (response); + if (order['status'] === 'closed') { + if (this.options['cancelOrderException']) { + throw new OrderNotFound (this.id + ' order closed already: ' + this.json (response)); + } + } + return order; + } + + async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (price === undefined) { + throw new ArgumentsRequired (this.id + ' editOrder requires the price argument'); + } + const request = { + 'order': { + 'quantity': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + }, + 'id': id, + }; + const response = await this.privatePutOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOrderStatus (status) { + const statuses = { + 'live': 'open', + 'filled': 'closed', + 'cancelled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "id": 2157474, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.0", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "live", + // "leverage_level": 1, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0" + // } + // + // fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders + // + // { + // "id": 2157479, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.01", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "filled", + // "leverage_level": 2, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0", + // "executions": [ + // { + // "id": 4566133, + // "quantity": "0.01", + // "price": "500.0", + // "taker_side": "buy", + // "my_side": "sell", + // "created_at": 1465396785 + // } + // ] + // } + // + const orderId = this.safeString (order, 'id'); + const timestamp = this.safeTimestamp (order, 'created_at'); + const marketId = this.safeString (order, 'product_id'); + market = this.safeValue (this.markets_by_id, marketId); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const amount = this.safeFloat (order, 'quantity'); + let filled = this.safeFloat (order, 'filled_quantity'); + const price = this.safeFloat (order, 'price'); + let symbol = undefined; + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + const type = this.safeString (order, 'order_type'); + let tradeCost = 0; + let tradeFilled = 0; + let average = this.safeFloat (order, 'average_price'); + const trades = this.parseTrades (this.safeValue (order, 'executions', []), market, undefined, undefined, { + 'order': orderId, + 'type': type, + }); + const numTrades = trades.length; + for (let i = 0; i < numTrades; i++) { + // php copies values upon assignment, but not references them + // todo rewrite this (shortly) + const trade = trades[i]; + trade['order'] = orderId; + trade['type'] = type; + tradeFilled = this.sum (tradeFilled, trade['amount']); + tradeCost = this.sum (tradeCost, trade['cost']); + } + let cost = undefined; + let lastTradeTimestamp = undefined; + if (numTrades > 0) { + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + if (!average && (tradeFilled > 0)) { + average = tradeCost / tradeFilled; + } + if (cost === undefined) { + cost = tradeCost; + } + if (filled === undefined) { + filled = tradeFilled; + } + } + let remaining = undefined; + if (amount !== undefined && filled !== undefined) { + remaining = amount - filled; + } + const side = this.safeString (order, 'side'); + return { + 'id': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': type, + 'status': status, + 'symbol': symbol, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'cost': cost, + 'remaining': remaining, + 'average': average, + 'trades': trades, + 'fee': { + 'currency': feeCurrency, + 'cost': this.safeFloat (order, 'order_fee'), + }, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + // 'funding_currency': market['quoteId'], // filter orders based on "funding" currency (quote currency) + // 'product_id': market['id'], + // 'status': 'live', // 'filled', 'cancelled' + // 'trading_type': 'spot', // 'margin', 'cfd' + 'with_details': 1, // return full order details including executions + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "models": [ + // { + // "id": 2157474, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.0", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "live", + // "leverage_level": 1, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0", + // "executions": [], // optional + // } + // ], + // "current_page": 1, + // "total_pages": 1 + // } + // + const orders = this.safeValue (response, 'models', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'status': 'live' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'status': 'filled' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + // 'auth_code': '', // optional 2fa code + 'currency': currency['id'], + 'address': address, + 'amount': this.currencyToPrecision (code, amount), + // 'payment_id': tag, // for XRP only + // 'memo_type': 'text', // 'text', 'id' or 'hash', for XLM only + // 'memo_value': tag, // for XLM only + }; + if (tag !== undefined) { + if (code === 'XRP') { + request['payment_id'] = tag; + } else if (code === 'XLM') { + request['memo_type'] = 'text'; // overrideable via params + request['memo_value'] = tag; + } else { + throw new NotSupported (this.id + ' withdraw() only supports a tag along the address for XRP or XLM'); + } + } + const response = await this.privatePostCryptoWithdrawals (this.extend (request, params)); + // + // { + // "id": 1353, + // "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", + // "amount": 1.0, + // "state": "pending", + // "currency": "BTC", + // "withdrawal_fee": 0.0, + // "created_at": 1568016450, + // "updated_at": 1568016450, + // "payment_id": null + // } + // + return this.parseTransaction (response, currency); + } + + parseTransactionStatus (status) { + const statuses = { + 'pending': 'pending', + 'cancelled': 'canceled', + 'approved': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // withdraw + // + // { + // "id": 1353, + // "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", + // "amount": 1.0, + // "state": "pending", + // "currency": "BTC", + // "withdrawal_fee": 0.0, + // "created_at": 1568016450, + // "updated_at": 1568016450, + // "payment_id": null + // } + // + // fetchDeposits, fetchWithdrawals + // + // ... + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + const tag = this.safeString2 (transaction, 'payment_id', 'memo_value'); + const txid = undefined; + const currencyId = this.safeString (transaction, 'asset'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeTimestamp (transaction, 'created_at'); + const updated = this.safeTimestamp (transaction, 'updated_at'); + const type = 'withdrawal'; + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const amount = this.safeFloat (transaction, 'amount'); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + headers = { + 'X-Quoine-API-Version': this.version, + 'Content-Type': 'application/json', + }; + if (api === 'private') { + this.checkRequiredCredentials (); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (Object.keys (query).length) { + body = this.json (query); + } + const nonce = this.nonce (); + const request = { + 'path': url, + 'nonce': nonce, + 'token_id': this.apiKey, + 'iat': Math.floor (nonce / 1000), // issued at + }; + headers['X-Quoine-Auth'] = this.jwt (request, this.encode (this.secret)); + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code >= 200 && code < 300) { + return; + } + if (code === 401) { + // expected non-json response + this.throwExactlyMatchedException (this.exceptions, body, body); + return; + } + if (code === 429) { + throw new DDoSProtection (this.id + ' ' + body); + } + if (response === undefined) { + return; + } + const feedback = this.id + ' ' + body; + const message = this.safeString (response, 'message'); + const errors = this.safeValue (response, 'errors'); + if (message !== undefined) { + // + // { "message": "Order not found" } + // + this.throwExactlyMatchedException (this.exceptions, message, feedback); + } else if (errors !== undefined) { + // + // { "errors": { "user": ["not_enough_free_balance"] }} + // { "errors": { "quantity": ["less_than_order_size"] }} + // { "errors": { "order": ["Can not update partially filled order"] }} + // + const types = Object.keys (errors); + for (let i = 0; i < types.length; i++) { + const type = types[i]; + const errorMessages = errors[type]; + for (let j = 0; j < errorMessages.length; j++) { + const message = errorMessages[j]; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + } + } + } else { + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],108:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, NotSupported, InvalidOrder, OrderNotFound, ExchangeNotAvailable, RateLimitExceeded, InsufficientFunds } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class livecoin extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'livecoin', + 'name': 'LiveCoin', + 'countries': [ 'US', 'UK', 'RU' ], + 'rateLimit': 1000, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'CORS': false, + 'fetchTickers': true, + 'fetchCurrencies': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchOrders': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27980768-f22fc424-638a-11e7-89c9-6010a54ff9be.jpg', + 'api': 'https://api.livecoin.net', + 'www': 'https://www.livecoin.net', + 'doc': 'https://www.livecoin.net/api?lang=en', + 'referral': 'https://livecoin.net/?from=Livecoin-CQ1hfx44', + }, + 'api': { + 'public': { + 'get': [ + 'exchange/all/order_book', + 'exchange/last_trades', + 'exchange/maxbid_minask', + 'exchange/order_book', + 'exchange/restrictions', + 'exchange/ticker', // omit params to get all tickers at once + 'info/coinInfo', + ], + }, + 'private': { + 'get': [ + 'exchange/client_orders', + 'exchange/order', + 'exchange/trades', + 'exchange/commission', + 'exchange/commissionCommonInfo', + 'payment/balances', + 'payment/balance', + 'payment/get/address', + 'payment/history/size', + 'payment/history/transactions', + ], + 'post': [ + 'exchange/buylimit', + 'exchange/buymarket', + 'exchange/cancellimit', + 'exchange/selllimit', + 'exchange/sellmarket', + 'payment/out/capitalist', + 'payment/out/card', + 'payment/out/coin', + 'payment/out/okpay', + 'payment/out/payeer', + 'payment/out/perfectmoney', + 'payment/voucher/amount', + 'payment/voucher/make', + 'payment/voucher/redeem', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.18 / 100, + 'taker': 0.18 / 100, + }, + }, + 'commonCurrencies': { + 'BTCH': 'Bithash', + 'CPC': 'Capricoin', + 'CPT': 'Cryptos', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 + 'EDR': 'E-Dinar Coin', // conflicts with EDR for Endor Protocol and EDRCoin + 'eETT': 'EETT', + 'FirstBlood': '1ST', + 'FORTYTWO': '42', + 'LEO': 'LeoCoin', + 'ORE': 'Orectic', + 'PLN': 'Plutaneum', // conflict with Polish Zloty + 'RUR': 'RUB', + 'SCT': 'SpaceCoin', + 'TPI': 'ThaneCoin', + 'wETT': 'WETT', + 'XBT': 'Bricktox', + }, + 'exceptions': { + 'exact': { + '1': ExchangeError, + '10': AuthenticationError, + '100': ExchangeError, // invalid parameters + '101': AuthenticationError, + '102': AuthenticationError, + '103': InvalidOrder, // invalid currency + '104': InvalidOrder, // invalid amount + '105': InvalidOrder, // unable to block funds + '11': AuthenticationError, + '12': AuthenticationError, + '2': AuthenticationError, // "User not found" + '20': AuthenticationError, + '30': AuthenticationError, + '31': NotSupported, + '32': ExchangeError, + '429': RateLimitExceeded, + '503': ExchangeNotAvailable, + }, + 'broad': { + 'insufficient funds': InsufficientFunds, // https://github.com/ccxt/ccxt/issues/5749 + 'NOT FOUND': OrderNotFound, + 'Cannot find order': OrderNotFound, + 'Minimal amount is': InvalidOrder, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeTicker (params); + const restrictions = await this.publicGetExchangeRestrictions (); + const restrictionsById = this.indexBy (restrictions['restrictions'], 'currencyPair'); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + const [ baseId, quoteId ] = id.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const coinRestrictions = this.safeValue (restrictionsById, symbol); + const precision = { + 'price': 5, + 'amount': 8, + 'cost': 8, + }; + const limits = { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + }; + if (coinRestrictions) { + precision['price'] = this.safeInteger (coinRestrictions, 'priceScale', 5); + limits['amount']['min'] = this.safeFloat (coinRestrictions, 'minLimitQuantity', limits['amount']['min']); + } + limits['price'] = { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetInfoCoinInfo (params); + const currencies = this.safeValue (response, 'info'); + let result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'symbol'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (id); + const precision = 8; // default precision, todo: fix "magic constants" + const walletStatus = this.safeString (currency, 'walletStatus'); + const active = (walletStatus === 'normal'); + const name = this.safeString (currency, 'name'); + const fee = this.safeFloat (currency, 'withdrawFee'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': name, + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minOrderAmount'), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': this.safeFloat (currency, 'minOrderAmount'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawAmount'), + 'max': Math.pow (10, precision), + }, + 'deposit': { + 'min': this.safeFloat (currency, 'minDepositAmount'), + 'max': undefined, + }, + }, + }; + } + result = this.appendFiatCurrencies (result); + return result; + } + + appendFiatCurrencies (result) { + const precision = 8; + const defaults = { + 'info': undefined, + 'active': true, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'withdraw': { 'min': undefined, 'max': undefined }, + 'deposit': { 'min': undefined, 'max': undefined }, + 'amount': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + }, + }; + const currencies = [ + { 'id': 'USD', 'code': 'USD', 'name': 'US Dollar' }, + { 'id': 'EUR', 'code': 'EUR', 'name': 'Euro' }, + // { 'id': 'RUR', 'code': 'RUB', 'name': 'Russian ruble' }, + ]; + currencies.push ({ + 'id': 'RUR', + 'code': this.safeCurrencyCode ('RUR'), + 'name': 'Russian ruble', + }); + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const code = currency['code']; + result[code] = this.extend (defaults, currency); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetPaymentBalances (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + let account = undefined; + if (code in result) { + account = result[code]; + } else { + account = this.account (); + } + if (balance['type'] === 'total') { + account['total'] = this.safeFloat (balance, 'value'); + } + if (balance['type'] === 'available') { + account['free'] = this.safeFloat (balance, 'value'); + } + if (balance['type'] === 'trade') { + account['used'] = this.safeFloat (balance, 'value'); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetExchangeCommissionCommonInfo (params); + const commission = this.safeFloat (response, 'commission'); + return { + 'info': response, + 'maker': commission, + 'taker': commission, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + 'groupByPrice': 'false', + }; + if (limit !== undefined) { + request['depth'] = limit; // 100 + } + const response = await this.publicGetExchangeOrderBook (this.extend (request, params)); + const timestamp = this.safeInteger (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'best_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'best_ask'), + 'askVolume': undefined, + 'vwap': this.safeFloat (ticker, 'vwap'), + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetExchangeTicker (params); + const tickers = this.indexBy (response, 'symbol'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + const ticker = await this.publicGetExchangeTicker (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "time": 1409935047, + // "id": 99451, + // "price": 350, + // "quantity": 2.85714285, + // "type": "BUY" + // } + // + // fetchMyTrades (private) + // + // { + // "datetime": 1435844369, + // "id": 30651619, + // "type": "sell", + // "symbol": "BTC/EUR", + // "price": 230, + // "quantity": 0.1, + // "commission": 0, + // "clientorderid": 1472837650 + // } + const timestamp = this.safeTimestamp2 (trade, 'time', 'datetime'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrency = market ? market['quote'] : undefined; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + const orderId = this.safeString (trade, 'clientorderid'); + const id = this.safeString (trade, 'id'); + const side = this.safeStringLower (trade, 'type'); + const amount = this.safeFloat (trade, 'quantity'); + const price = this.safeFloat (trade, 'price'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + // orderDesc': 'true', // or 'false', if true then new orders will be first, otherwise old orders will be first. + // 'offset': 0, // page offset, position of the first item on the page + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetExchangeTrades (this.extend (request, params)); + // + // [ + // { + // "datetime": 1435844369, + // "id": 30651619, + // "type": "sell", + // "symbol": "BTC/EUR", + // "price": 230, + // "quantity": 0.1, + // "commission": 0, + // "clientorderid": 1472837650 + // }, + // { + // "datetime": 1435844356, + // "id": 30651618, + // "type": "sell", + // "symbol": "BTC/EUR", + // "price": 230, + // "quantity": 0.2, + // "commission": 0.092, + // "clientorderid": 1472837651 + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + const response = await this.publicGetExchangeLastTrades (this.extend (request, params)); + // + // [ + // { + // "time": 1409935047, + // "id": 99451, + // "price": 350, + // "quantity": 2.85714285, + // "type": "BUY" + // }, + // { + // "time": 1409934792, + // "id": 99450, + // "price": 350, + // "quantity": 0.57142857, + // "type": "SELL" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privateGetExchangeOrder (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOrderStatus (status) { + const statuses = { + 'OPEN': 'open', + 'PARTIALLY_FILLED': 'open', + 'EXECUTED': 'closed', + 'CANCELLED': 'canceled', + 'PARTIALLY_FILLED_AND_CANCELLED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let timestamp = undefined; + if ('lastModificationTime' in order) { + timestamp = this.safeString (order, 'lastModificationTime'); + if (timestamp !== undefined) { + if (timestamp.indexOf ('T') >= 0) { + timestamp = this.parse8601 (timestamp); + } else { + timestamp = this.safeInteger (order, 'lastModificationTime'); + } + } + } + // TODO currently not supported by livecoin + // let trades = this.parseTrades (order['trades'], market, since, limit); + const trades = undefined; + const status = this.parseOrderStatus (this.safeString2 (order, 'status', 'orderStatus')); + let symbol = undefined; + if (market === undefined) { + let marketId = this.safeString (order, 'currencyPair'); + marketId = this.safeString (order, 'symbol', marketId); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let type = this.safeStringLower (order, 'type'); + let side = undefined; + if (type !== undefined) { + const orderType = type.split ('_'); + type = orderType[0]; + side = orderType[1]; + } + const price = this.safeFloat (order, 'price'); + // of the next two lines the latter overrides the former, if present in the order structure + let remaining = this.safeFloat (order, 'remainingQuantity'); + remaining = this.safeFloat (order, 'remaining_quantity', remaining); + const amount = this.safeFloat (order, 'quantity', remaining); + let filled = undefined; + if (remaining !== undefined) { + filled = amount - remaining; + } + let cost = undefined; + if (filled !== undefined && price !== undefined) { + cost = filled * price; + } + const feeRate = this.safeFloat (order, 'commission_rate'); + let feeCost = undefined; + if (cost !== undefined && feeRate !== undefined) { + feeCost = cost * feeRate; + } + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + return { + 'info': order, + 'id': order['id'], + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }, + }; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['currencyPair'] = market['id']; + } + if (since !== undefined) { + request['issuedFrom'] = parseInt (since); + } + if (limit !== undefined) { + request['endRow'] = limit - 1; + } + const response = await this.privateGetExchangeClientOrders (this.extend (request, params)); + const result = []; + let rawOrders = []; + if (response['data']) { + rawOrders = response['data']; + } + for (let i = 0; i < rawOrders.length; i++) { + const order = rawOrders[i]; + result.push (this.parseOrder (order, market)); + } + return this.sortBy (result, 'timestamp'); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openClosed': 'OPEN', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openClosed': 'CLOSED', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostExchange' + this.capitalize (side) + type; + const market = this.market (symbol); + const request = { + 'quantity': this.amountToPrecision (symbol, amount), + 'currencyPair': market['id'], + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this[method] (this.extend (request, params)); + const result = { + 'info': response, + 'id': response['orderId'].toString (), + }; + const success = this.safeValue (response, 'success'); + if (success) { + result['status'] = 'open'; + } + return result; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'orderId': id, + 'currencyPair': market['id'], + }; + const response = await this.privatePostExchangeCancellimit (this.extend (request, params)); + const message = this.safeString (response, 'message', this.json (response)); + if ('success' in response) { + if (!response['success']) { + throw new InvalidOrder (message); + } else if ('cancelled' in response) { + if (response['cancelled']) { + return { + 'status': 'canceled', + 'info': response, + }; + } else { + throw new OrderNotFound (message); + } + } + } + throw new ExchangeError (this.id + ' cancelOrder() failed: ' + this.json (response)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + // Sometimes the response with be { key: null } for all keys. + // An example is if you attempt to withdraw more than is allowed when withdrawal fees are considered. + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + let wallet = address; + if (tag !== undefined) { + wallet += '::' + tag; + } + const request = { + 'amount': this.decimalToPrecision (amount, TRUNCATE, currency['precision'], DECIMAL_PLACES), + 'currency': currency['id'], + 'wallet': wallet, + }; + const response = await this.privatePostPaymentOutCoin (this.extend (request, params)); + const id = this.safeInteger (response, 'id'); + if (id === undefined) { + throw new InsufficientFunds (this.id + ' insufficient funds to cover requested withdrawal amount post fees ' + this.json (response)); + } + return { + 'info': response, + 'id': id, + }; + } + + parseTransaction (transaction, currency = undefined) { + // { + // "id": "c853093d5aa06df1c92d79c2...", (tx on deposits, address on withdrawals) + // "type": "DEPOSIT", + // "date": 1553186482676, + // "amount": 712.61266, + // "fee": 0, + // "fixedCurrency": "XVG", + // "taxCurrency": "XVG", + // "variableAmount": null, + // "variableCurrency": null, + // "external": "Coin", + // "login": "USERNAME", + // "externalKey": "....87diPBy......3hTtuwUT78Yi", (address on deposits, tx on withdrawals) + // "documentId": 1110662453 + // }, + let txid = undefined; + let address = undefined; + const id = this.safeString (transaction, 'documentId'); + const amount = this.safeFloat (transaction, 'amount'); + const timestamp = this.safeInteger (transaction, 'date'); + const type = this.safeStringLower (transaction, 'type'); + const currencyId = this.safeString (transaction, 'fixedCurrency'); + const feeCost = this.safeFloat (transaction, 'fee'); + const code = this.safeCurrencyCode (currencyId, currency); + if (type === 'withdrawal') { + txid = this.safeString (transaction, 'externalKey'); + address = this.safeString (transaction, 'id'); + } else if (type === 'deposit') { + address = this.safeString (transaction, 'externalKey'); + txid = this.safeString (transaction, 'id'); + } + let status = undefined; + if (type === 'deposit') { + status = 'ok'; // Deposits is not registered until they are in account. Withdrawals are left as undefined, not entirely sure about theyre status. + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const endtime = 2505600000; // 29 days - exchange has maximum 30 days. + const now = this.milliseconds (); + const request = { + 'types': 'DEPOSIT', + 'end': now, + 'start': (since !== undefined) ? parseInt (since) : now - endtime, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + const response = await this.privateGetPaymentHistoryTransactions (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const endtime = 2505600000; // 29 days - exchange has maximum 30 days. + const now = this.milliseconds (); + const request = { + 'types': 'WITHDRAWAL', + 'end': now, + 'start': (since !== undefined) ? parseInt (since) : now - endtime, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + if (since !== undefined) { + request['start'] = since; + } + const response = await this.privateGetPaymentHistoryTransactions (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + async fetchDepositAddress (currency, params = {}) { + const request = { + 'currency': currency, + }; + const response = await this.privateGetPaymentGetAddress (this.extend (request, params)); + let address = this.safeString (response, 'wallet'); + let tag = undefined; + if (address.indexOf (':') >= 0) { + const parts = address.split (':'); + address = parts[0]; + tag = parts[2]; + } + this.checkAddress (address); + return { + 'currency': currency, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + path; + const query = this.urlencode (this.keysort (params)); + if (method === 'GET') { + if (Object.keys (params).length) { + url += '?' + query; + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + if (method === 'POST') { + body = query; + } + const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha256'); + headers = { + 'Api-Key': this.apiKey, + 'Sign': signature.toUpperCase (), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if (code >= 300) { + const feedback = this.id + ' ' + body; + const errorCode = this.safeString (response, 'errorCode'); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + throw new ExchangeError (feedback); + } + // returns status code 200 even if success === false + const success = this.safeValue (response, 'success', true); + if (!success) { + const feedback = this.id + ' ' + body; + const message = this.safeString2 (response, 'message', 'exception'); + if (message !== undefined) { + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + throw new ExchangeError (feedback); + } + } +}; + + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],109:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class luno extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'luno', + 'name': 'luno', + 'countries': [ 'GB', 'SG', 'ZA' ], + 'rateLimit': 1000, + 'version': '1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + }, + 'urls': { + 'referral': 'https://www.luno.com/invite/44893A', + 'logo': 'https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg', + 'api': 'https://api.mybitx.com/api', + 'www': 'https://www.luno.com', + 'doc': [ + 'https://www.luno.com/en/api', + 'https://npmjs.org/package/bitx', + 'https://github.com/bausmeier/node-bitx', + ], + }, + 'api': { + 'public': { + 'get': [ + 'orderbook', + 'orderbook_top', + 'ticker', + 'tickers', + 'trades', + ], + }, + 'private': { + 'get': [ + 'accounts/{id}/pending', + 'accounts/{id}/transactions', + 'balance', + 'fee_info', + 'funding_address', + 'listorders', + 'listtrades', + 'orders/{id}', + 'quotes/{id}', + 'withdrawals', + 'withdrawals/{id}', + ], + 'post': [ + 'accounts', + 'postorder', + 'marketorder', + 'stoporder', + 'funding_address', + 'withdrawals', + 'send', + 'quotes', + 'oauth2/grant', + ], + 'put': [ + 'quotes/{id}', + ], + 'delete': [ + 'quotes/{id}', + 'withdrawals/{id}', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTickers (params); + const result = []; + for (let i = 0; i < response['tickers'].length; i++) { + const market = response['tickers'][i]; + const id = market['pair']; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const wallets = this.safeValue (response, 'balance', []); + const result = { 'info': response }; + for (let i = 0; i < wallets.length; i++) { + const wallet = wallets[i]; + const currencyId = this.safeString (wallet, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const reserved = this.safeFloat (wallet, 'reserved'); + const unconfirmed = this.safeFloat (wallet, 'unconfirmed'); + const balance = this.safeFloat (wallet, 'balance'); + const account = this.account (); + account['used'] = this.sum (reserved, unconfirmed); + account['total'] = this.sum (balance, unconfirmed); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + let method = 'publicGetOrderbook'; + if (limit !== undefined) { + if (limit <= 100) { + method += 'Top'; // get just the top of the orderbook when limit is low + } + } + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this[method] (this.extend (request, params)); + const timestamp = this.safeInteger (response, 'timestamp'); + return this.parseOrderBook (response, timestamp, 'bids', 'asks', 'price', 'volume'); + } + + parseOrder (order, market = undefined) { + const timestamp = this.safeInteger (order, 'creation_timestamp'); + const status = (order['state'] === 'PENDING') ? 'open' : 'closed'; + const side = (order['type'] === 'ASK') ? 'sell' : 'buy'; + const marketId = this.safeString (order, 'pair'); + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (order, 'limit_price'); + const amount = this.safeFloat (order, 'limit_volume'); + const quoteFee = this.safeFloat (order, 'fee_counter'); + const baseFee = this.safeFloat (order, 'fee_base'); + const filled = this.safeFloat (order, 'base'); + const cost = this.safeFloat (order, 'counter'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + const fee = { 'currency': undefined }; + if (quoteFee) { + fee['cost'] = quoteFee; + if (market !== undefined) { + fee['currency'] = market['quote']; + } + } else { + fee['cost'] = baseFee; + if (market !== undefined) { + fee['currency'] = market['base']; + } + } + const id = this.safeString (order, 'order_id'); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'cost': cost, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrdersByState (state = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (state !== undefined) { + request['state'] = state; + } + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privateGetListorders (this.extend (request, params)); + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState (undefined, symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('PENDING', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('COMPLETE', symbol, since, limit, params); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last_trade'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'rolling_24_hour_volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const tickers = this.indexBy (response['tickers'], 'pair'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market) { + // For public trade data (is_buy === True) indicates 'buy' side but for private trade data + // is_buy indicates maker or taker. The value of "type" (ASK/BID) indicate sell/buy side. + // Private trade data includes ID field which public trade data does not. + const orderId = this.safeString (trade, 'order_id'); + let takerOrMaker = undefined; + let side = undefined; + if (orderId !== undefined) { + side = (trade['type'] === 'ASK') ? 'sell' : 'buy'; + if (side === 'sell' && trade['is_buy']) { + takerOrMaker = 'maker'; + } else if (side === 'buy' && !trade['is_buy']) { + takerOrMaker = 'maker'; + } else { + takerOrMaker = 'taker'; + } + } else { + side = trade['is_buy'] ? 'buy' : 'sell'; + } + const feeBase = this.safeFloat (trade, 'fee_base'); + const feeCounter = this.safeFloat (trade, 'fee_counter'); + let feeCurrency = undefined; + let feeCost = undefined; + if (feeBase !== undefined) { + if (feeBase !== 0.0) { + feeCurrency = market['base']; + feeCost = feeBase; + } + } else if (feeCounter !== undefined) { + if (feeCounter !== 0.0) { + feeCurrency = market['quote']; + feeCost = feeCounter; + } + } + const timestamp = this.safeInteger (trade, 'timestamp'); + return { + 'info': trade, + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': this.safeFloat (trade, 'price'), + 'amount': this.safeFloat (trade, 'volume'), + // Does not include potential fee costs + 'cost': this.safeFloat (trade, 'counter'), + 'fee': { + 'cost': feeCost, + 'currency': feeCurrency, + }, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (since !== undefined) { + request['since'] = since; + } + const response = await this.publicGetTrades (this.extend (request, params)); + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetListtrades (this.extend (request, params)); + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetFeeInfo (params); + return { + 'info': response, + 'maker': this.safeFloat (response, 'maker_fee'), + 'taker': this.safeFloat (response, 'taker_fee'), + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let method = 'privatePost'; + const request = { + 'pair': this.marketId (symbol), + }; + if (type === 'market') { + method += 'Marketorder'; + request['type'] = side.toUpperCase (); + if (side === 'buy') { + request['counter_volume'] = amount; + } else { + request['base_volume'] = amount; + } + } else { + method += 'Postorder'; + request['volume'] = amount; + request['price'] = price; + request['type'] = (side === 'buy') ? 'BID' : 'ASK'; + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['order_id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + return await this.privatePostStoporder (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + if (api === 'private') { + this.checkRequiredCredentials (); + let auth = this.encode (this.apiKey + ':' + this.secret); + auth = this.stringToBase64 (auth); + headers = { 'Authorization': 'Basic ' + this.decode (auth) }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],110:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class lykke extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'lykke', + 'name': 'Lykke', + 'countries': [ 'CH' ], + 'version': 'v1', + 'rateLimit': 200, + 'has': { + 'CORS': false, + 'fetchOHLCV': false, + 'fetchTrades': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/34487620-3139a7b0-efe6-11e7-90f5-e520cef74451.jpg', + 'api': { + 'mobile': 'https://public-api.lykke.com/api', + 'public': 'https://hft-api.lykke.com/api', + 'private': 'https://hft-api.lykke.com/api', + }, + 'test': { + 'mobile': 'https://public-api.lykke.com/api', + 'public': 'https://hft-service-dev.lykkex.net/api', + 'private': 'https://hft-service-dev.lykkex.net/api', + }, + 'www': 'https://www.lykke.com', + 'doc': [ + 'https://hft-api.lykke.com/swagger/ui/', + 'https://www.lykke.com/lykke_api', + ], + 'fees': 'https://www.lykke.com/trading-conditions', + }, + 'api': { + 'mobile': { + 'get': [ + 'Market/{market}', + 'Trades/{AssetPairId}', + ], + }, + 'public': { + 'get': [ + 'AssetPairs', + 'AssetPairs/{id}', + 'IsAlive', + 'OrderBooks', + 'OrderBooks/{AssetPairId}', + ], + }, + 'private': { + 'get': [ + 'Orders', + 'Orders/{id}', + 'Wallets', + ], + 'post': [ + 'Orders/limit', + 'Orders/market', + 'Orders/{id}/Cancel', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0, // as of 7 Feb 2018, see https://github.com/ccxt/ccxt/issues/1863 + 'taker': 0.0, // https://www.lykke.com/cp/wallet-fees-and-limits + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + }, + 'deposit': { + 'BTC': 0, + }, + }, + }, + 'commonCurrencies': { + 'XPD': 'Lykke XPD', + }, + }); + } + + parseTrade (trade, market) { + // + // public fetchTrades + // + // { + // "id": "d5983ab8-e9ec-48c9-bdd0-1b18f8e80a71", + // "assetPairId": "BTCUSD", + // "dateTime": "2019-05-15T06:52:02.147Z", + // "volume": 0.00019681, + // "index": 0, + // "price": 8023.333, + // "action": "Buy" + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'AssetPairId'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + const timestamp = this.parse8601 (this.safeString (trade, 'dateTime')); + const side = this.safeStringLower (trade, 'action'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + const cost = price * amount; + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 100; + } + const request = { + 'AssetPairId': market['id'], + 'skip': 0, + 'take': limit, + }; + const response = await this.mobileGetTradesAssetPairId (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWallets (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'AssetId'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'Balance'); + account['used'] = this.safeFloat (balance, 'Reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostOrdersIdCancel ({ 'id': id }); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const query = { + 'AssetPairId': market['id'], + 'OrderAction': this.capitalize (side), + 'Volume': amount, + }; + if (type === 'market') { + query['Asset'] = (side === 'buy') ? market['base'] : market['quote']; + } else if (type === 'limit') { + query['Price'] = price; + } + const method = 'privatePostOrders' + this.capitalize (type); + const result = await this[method] (this.extend (query, params)); + return { + 'id': undefined, + 'info': result, + }; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetAssetPairs (); + // + // [ { Id: "AEBTC", + // Name: "AE/BTC", + // Accuracy: 6, + // InvertedAccuracy: 8, + // BaseAssetId: "6f75280b-a005-4016-a3d8-03dc644e8912", + // QuotingAssetId: "BTC", + // MinVolume: 0.4, + // MinInvertedVolume: 0.0001 }, + // { Id: "AEETH", + // Name: "AE/ETH", + // Accuracy: 6, + // InvertedAccuracy: 8, + // BaseAssetId: "6f75280b-a005-4016-a3d8-03dc644e8912", + // QuotingAssetId: "ETH", + // MinVolume: 0.4, + // MinInvertedVolume: 0.001 } ] + // + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'Id'); + const name = this.safeString (market, 'Name'); + const [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'Accuracy'), + 'price': this.safeInteger (market, 'InvertedAccuracy'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'active': true, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const close = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume24H'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const ticker = await this.mobileGetMarketMarket (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseOrderStatus (status) { + const statuses = { + 'Open': 'open', + 'Pending': 'open', + 'InOrderBook': 'open', + 'Processing': 'open', + 'Matched': 'closed', + 'Cancelled': 'canceled', + 'Rejected': 'rejected', + 'Replaced': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeString (order, 'Status')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'AssetPairId'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market) { + symbol = market['symbol']; + } + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'LastMatchTime')); + let timestamp = undefined; + if (('Registered' in order) && (order['Registered'])) { + timestamp = this.parse8601 (order['Registered']); + } else if (('CreatedAt' in order) && (order['CreatedAt'])) { + timestamp = this.parse8601 (order['CreatedAt']); + } + const price = this.safeFloat (order, 'Price'); + const amount = this.safeFloat (order, 'Volume'); + const remaining = this.safeFloat (order, 'RemainingVolume'); + const filled = amount - remaining; + const cost = filled * price; + const id = this.safeString (order, 'Id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'price': price, + 'cost': cost, + 'average': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetOrders (params); + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'InOrderBook', + }; + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, undefined, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'Matched', + }; + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetOrderBooksAssetPairId (this.extend ({ + 'AssetPairId': this.marketId (symbol), + }, params)); + const orderbook = { + 'timestamp': undefined, + 'bids': [], + 'asks': [], + }; + let timestamp = undefined; + for (let i = 0; i < response.length; i++) { + const side = response[i]; + if (side['IsBuy']) { + orderbook['bids'] = this.arrayConcat (orderbook['bids'], side['Prices']); + } else { + orderbook['asks'] = this.arrayConcat (orderbook['asks'], side['Prices']); + } + const sideTimestamp = this.parse8601 (side['Timestamp']); + timestamp = (timestamp === undefined) ? sideTimestamp : Math.max (timestamp, sideTimestamp); + } + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'Price', 'Volume'); + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1) { + const price = this.safeFloat (bidask, priceKey); + let amount = this.safeFloat (bidask, amountKey); + if (amount < 0) { + amount = -amount; + } + return [ price, amount ]; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'mobile') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'private') { + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + this.checkRequiredCredentials (); + headers = { + 'api-key': this.apiKey, + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }; + if (method === 'POST') { + if (Object.keys (params).length) { + body = this.json (params); + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7}],111:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class mercado extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'mercado', + 'name': 'Mercado Bitcoin', + 'countries': [ 'BR' ], // Brazil + 'rateLimit': 1000, + 'version': 'v3', + 'has': { + 'CORS': true, + 'createMarketOrder': true, + 'fetchOrder': true, + 'withdraw': true, + 'fetchOHLCV': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchTicker': true, + 'fetchTickers': false, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '6h': '6h', + '12h': '12h', + '1d': '1d', + '3d': '3d', + '1w': '1w', + '2w': '2w', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27837060-e7c58714-60ea-11e7-9192-f05e86adb83f.jpg', + 'api': { + 'public': 'https://www.mercadobitcoin.net/api', + 'private': 'https://www.mercadobitcoin.net/tapi', + 'v4Public': 'https://www.mercadobitcoin.com.br/v4', + }, + 'www': 'https://www.mercadobitcoin.com.br', + 'doc': [ + 'https://www.mercadobitcoin.com.br/api-doc', + 'https://www.mercadobitcoin.com.br/trade-api', + ], + }, + 'api': { + 'public': { + 'get': [ + '{coin}/orderbook/', // last slash critical + '{coin}/ticker/', + '{coin}/trades/', + '{coin}/trades/{from}/', + '{coin}/trades/{from}/{to}', + '{coin}/day-summary/{year}/{month}/{day}/', + ], + }, + 'private': { + 'post': [ + 'cancel_order', + 'get_account_info', + 'get_order', + 'get_withdrawal', + 'list_system_messages', + 'list_orders', + 'list_orderbook', + 'place_buy_order', + 'place_sell_order', + 'place_market_buy_order', + 'place_market_sell_order', + 'withdraw_coin', + ], + }, + 'v4Public': { + 'get': [ + '{coin}/candle/', + ], + }, + }, + 'markets': { + 'BTC/BRL': { 'id': 'BRLBTC', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Bitcoin' }, + 'LTC/BRL': { 'id': 'BRLLTC', 'symbol': 'LTC/BRL', 'base': 'LTC', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Litecoin' }, + 'BCH/BRL': { 'id': 'BRLBCH', 'symbol': 'BCH/BRL', 'base': 'BCH', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'BCash' }, + 'XRP/BRL': { 'id': 'BRLXRP', 'symbol': 'XRP/BRL', 'base': 'XRP', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Ripple' }, + 'ETH/BRL': { 'id': 'BRLETH', 'symbol': 'ETH/BRL', 'base': 'ETH', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Ethereum' }, + }, + 'fees': { + 'trading': { + 'maker': 0.3 / 100, + 'taker': 0.7 / 100, + }, + }, + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['base'], + }; + const response = await this.publicGetCoinOrderbook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['base'], + }; + const response = await this.publicGetCoinTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'ticker', {}); + const timestamp = this.safeTimestamp (ticker, 'date'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = 'publicGetCoinTrades'; + const request = { + 'coin': market['base'], + }; + if (since !== undefined) { + method += 'From'; + request['from'] = parseInt (since / 1000); + } + const to = this.safeInteger (params, 'to'); + if (to !== undefined) { + method += 'To'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const data = this.safeValue (response, 'response_data', {}); + const balances = this.safeValue (data, 'balance', {}); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + if (currencyId in balances) { + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'coin_pair': this.marketId (symbol), + }; + let method = this.capitalize (side) + 'Order'; + if (type === 'limit') { + method = 'privatePostPlace' + method; + request['limit_price'] = this.priceToPrecision (symbol, price); + request['quantity'] = this.amountToPrecision (symbol, amount); + } else { + method = 'privatePostPlaceMarket' + method; + if (side === 'buy') { + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount'); + } + request['cost'] = this.priceToPrecision (symbol, amount * price); + } else { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + } + const response = await this[method] (this.extend (request, params)); + // TODO: replace this with a call to parseOrder for unification + return { + 'info': response, + 'id': response['response_data']['order']['order_id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder () requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin_pair': market['id'], + 'order_id': id, + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + // + // { + // response_data: { + // order: { + // order_id: 2176769, + // coin_pair: 'BRLBCH', + // order_type: 2, + // status: 3, + // has_fills: false, + // quantity: '0.10000000', + // limit_price: '1996.15999', + // executed_quantity: '0.00000000', + // executed_price_avg: '0.00000', + // fee: '0.00000000', + // created_timestamp: '1536956488', + // updated_timestamp: '1536956499', + // operations: [] + // } + // }, + // status_code: 100, + // server_unix_timestamp: '1536956499' + // } + // + const responseData = this.safeValue (response, 'response_data', {}); + const order = this.safeValue (responseData, 'order', {}); + return this.parseOrder (order, market); + } + + parseOrderStatus (status) { + const statuses = { + '2': 'open', + '3': 'canceled', + '4': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "order_id": 4, + // "coin_pair": "BRLBTC", + // "order_type": 1, + // "status": 2, + // "has_fills": true, + // "quantity": "2.00000000", + // "limit_price": "900.00000", + // "executed_quantity": "1.00000000", + // "executed_price_avg": "900.00000", + // "fee": "0.00300000", + // "created_timestamp": "1453838494", + // "updated_timestamp": "1453838494", + // "operations": [ + // { + // "operation_id": 1, + // "quantity": "1.00000000", + // "price": "900.00000", + // "fee_rate": "0.30", + // "executed_timestamp": "1453838494", + // }, + // ], + // } + // + const id = this.safeString (order, 'order_id'); + let side = undefined; + if ('order_type' in order) { + side = (order['order_type'] === 1) ? 'buy' : 'sell'; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'coin_pair'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'created_timestamp'); + const fee = { + 'cost': this.safeFloat (order, 'fee'), + 'currency': market['quote'], + }; + const price = this.safeFloat (order, 'limit_price'); + // price = this.safeFloat (order, 'executed_price_avg', price); + const average = this.safeFloat (order, 'executed_price_avg'); + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'executed_quantity'); + const remaining = amount - filled; + const cost = filled * average; + const lastTradeTimestamp = this.safeTimestamp (order, 'updated_timestamp'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': undefined, // todo parse trades (operations) + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin_pair': market['id'], + 'order_id': parseInt (id), + }; + const response = await this.privatePostGetOrder (this.extend (request, params)); + const responseData = this.safeValue (response, 'response_data', {}); + const order = this.safeValue (responseData, 'order'); + return this.parseOrder (order, market); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + 'quantity': amount.toFixed (10), + 'address': address, + }; + if (code === 'BRL') { + const account_ref = ('account_ref' in params); + if (!account_ref) { + throw new ExchangeError (this.id + ' requires account_ref parameter to withdraw ' + code); + } + } else if (code !== 'LTC') { + const tx_fee = ('tx_fee' in params); + if (!tx_fee) { + throw new ExchangeError (this.id + ' requires tx_fee parameter to withdraw ' + code); + } + if (code === 'XRP') { + if (tag === undefined) { + if (!('destination_tag' in params)) { + throw new ExchangeError (this.id + ' requires a tag argument or destination_tag parameter to withdraw ' + code); + } + } else { + request['destination_tag'] = tag; + } + } + } + const response = await this.privatePostWithdrawCoin (this.extend (request, params)); + return { + 'info': response, + 'id': response['response_data']['withdrawal']['id'], + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'timestamp'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'precision': this.timeframes[timeframe], + 'coin': market['id'].toLowerCase (), + }; + if (limit !== undefined && since !== undefined) { + request['from'] = parseInt (since / 1000); + request['to'] = this.sum (request['from'], limit * this.parseTimeframe (timeframe)); + } else if (since !== undefined) { + request['from'] = parseInt (since / 1000); + request['to'] = this.sum (this.seconds (), 1); + } else if (limit !== undefined) { + request['to'] = this.seconds (); + request['from'] = request['to'] - (limit * this.parseTimeframe (timeframe)); + } + const response = await this.v4PublicGetCoinCandle (this.extend (request, params)); + const candles = this.safeValue (response, 'candles', []); + return this.parseOHLCVs (candles, market, timeframe, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders () requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin_pair': market['id'], + }; + const response = await this.privatePostListOrders (this.extend (request, params)); + const responseData = this.safeValue (response, 'response_data', {}); + const orders = this.safeValue (responseData, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/'; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public' || (api === 'v4Public')) { + url += this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + url += this.version + '/'; + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'tapi_method': path, + 'tapi_nonce': nonce, + }, params)); + const auth = '/tapi/' + this.version + '/' + '?' + body; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'TAPI-ID': this.apiKey, + 'TAPI-MAC': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error_message' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],112:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class mixcoins extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'mixcoins', + 'name': 'MixCoins', + 'countries': [ 'GB', 'HK' ], + 'rateLimit': 1500, + 'version': 'v1', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30237212-ed29303c-9535-11e7-8af8-fcd381cfa20c.jpg', + 'api': 'https://mixcoins.com/api', + 'www': 'https://mixcoins.com', + 'doc': 'https://mixcoins.com/help/api/', + }, + 'api': { + 'public': { + 'get': [ + 'ticker/', + 'trades/', + 'depth/', + ], + }, + 'private': { + 'post': [ + 'cancel', + 'info', + 'orders', + 'order', + 'transactions', + 'trade', + ], + }, + }, + 'markets': { + 'BTC/USDT': { 'id': 'btc_usdt', 'symbol': 'BTC/USDT', 'base': 'BTC', 'quote': 'USDT', 'baseId': 'btc', 'quoteId': 'usdt', 'maker': 0.0015, 'taker': 0.0025 }, + 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'maker': 0.001, 'taker': 0.0015 }, + 'BCH/BTC': { 'id': 'bch_btc', 'symbol': 'BCH/BTC', 'base': 'BCH', 'quote': 'BTC', 'baseId': 'bch', 'quoteId': 'btc', 'maker': 0.001, 'taker': 0.0015 }, + 'LSK/BTC': { 'id': 'lsk_btc', 'symbol': 'LSK/BTC', 'base': 'LSK', 'quote': 'BTC', 'baseId': 'lsk', 'quoteId': 'btc', 'maker': 0.0015, 'taker': 0.0025 }, + 'BCH/USDT': { 'id': 'bch_usdt', 'symbol': 'BCH/USDT', 'base': 'BCH', 'quote': 'USDT', 'baseId': 'bch', 'quoteId': 'usdt', 'maker': 0.001, 'taker': 0.0015 }, + 'ETH/USDT': { 'id': 'eth_usdt', 'symbol': 'ETH/USDT', 'base': 'ETH', 'quote': 'USDT', 'baseId': 'eth', 'quoteId': 'usdt', 'maker': 0.001, 'taker': 0.0015 }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostInfo (params); + const balances = this.safeValue (response['result'], 'wallet'); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'avail'); + account['used'] = this.safeFloat (balance, 'lock'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + }; + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response['result']); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'result'); + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response['result'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'op': side, + 'amount': amount, + }; + if (type === 'market') { + request['order_type'] = 1; + request['price'] = price; + } else { + request['order_type'] = 0; + } + const response = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': response, + 'id': response['result']['id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.secret, 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('status' in response) { + // + // todo add a unified standard handleErrors with this.exceptions in describe() + // + // {"status":503,"message":"Maintenancing, try again later","result":null} + // + if (response['status'] === 200) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],113:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, ArgumentsRequired, BadRequest, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class oceanex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'oceanex', + 'name': 'OceanEx', + 'countries': [ 'US' ], + 'version': 'v1', + 'rateLimit': 3000, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/58385970-794e2d80-8001-11e9-889c-0567cd79b78e.jpg', + 'api': 'https://api.oceanex.pro', + 'www': 'https://www.oceanex.pro.com', + 'doc': 'https://api.oceanex.pro/doc/v1', + 'referral': 'https://oceanex.pro/signup?referral=VE24QX', + }, + 'has': { + 'fetchMarkets': true, + 'fetchCurrencies': false, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchOrderBook': true, + 'fetchOrderBooks': true, + 'fetchTrades': true, + 'fetchTradingLimits': false, + 'fetchTradingFees': false, + 'fetchAllTradingFees': true, + 'fetchFundingFees': false, + 'fetchTime': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchBalance': true, + 'createMarketOrder': true, + 'createOrder': true, + 'cancelOrder': true, + 'cancelOrders': true, + 'cancelAllOrders': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '4h': '4h', + '12h': '12h', + '1d': '1d', + '1w': '1w', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'tickers/{pair}', + 'tickers_multi', + 'order_book', + 'order_book/multi', + 'fees/trading', + 'trades', + 'timestamp', + ], + }, + 'private': { + 'get': [ + 'key', + 'members/me', + 'orders', + 'orders/filter', + ], + 'post': [ + 'orders', + 'orders/multi', + 'order/delete', + 'order/delete/multi', + 'orders/clear', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + }, + 'exceptions': { + 'codes': { + '-1': BadRequest, + '-2': BadRequest, + '1001': BadRequest, + '1004': ArgumentsRequired, + '1006': AuthenticationError, + '1008': AuthenticationError, + '1010': AuthenticationError, + '1011': PermissionDenied, + '2001': AuthenticationError, + '2002': InvalidOrder, + '2004': OrderNotFound, + '9003': PermissionDenied, + }, + 'exact': { + 'market does not have a valid value': BadRequest, + 'side does not have a valid value': BadRequest, + 'Account::AccountError: Cannot lock funds': InsufficientFunds, + 'The account does not exist': AuthenticationError, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { 'show_details': true }; + const response = await this.publicGetMarkets (this.extend (request, params)); + const result = []; + const markets = this.safeValue (response, 'data'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeValue (market, 'id'); + const name = this.safeValue (market, 'name'); + let [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + baseId = baseId.toLowerCase (); + quoteId = quoteId.toLowerCase (); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'info': market, + 'precision': { + 'amount': this.safeValue (market, 'amount_precision'), + 'price': this.safeValue (market, 'price_precision'), + 'base': this.safeValue (market, 'ask_precision'), + 'quote': this.safeValue (market, 'bid_precision'), + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeValue (market, 'minimum_trading_amount'), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTickersPair (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": { + // "at":1559431729, + // "ticker": { + // "buy":"0.0065", + // "sell":"0.00677", + // "low":"0.00677", + // "high":"0.00677", + // "last":"0.00677", + // "vol":"2000.0" + // } + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseTicker (data, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const marketIds = this.marketIds (symbols); + const request = { 'markets': marketIds }; + const response = await this.publicGetTickersMulti (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": { + // "at":1559431729, + // "ticker": { + // "buy":"0.0065", + // "sell":"0.00677", + // "low":"0.00677", + // "high":"0.00677", + // "last":"0.00677", + // "vol":"2000.0" + // } + // } + // } + // + const data = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < data.length; i++) { + const ticker = data[i]; + const marketId = this.safeString (ticker, 'market'); + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + parseTicker (data, market = undefined) { + // + // { + // "at":1559431729, + // "ticker": { + // "buy":"0.0065", + // "sell":"0.00677", + // "low":"0.00677", + // "high":"0.00677", + // "last":"0.00677", + // "vol":"2000.0" + // } + // } + // + const ticker = this.safeValue (data, 'ticker', {}); + const timestamp = this.safeTimestamp (data, 'at'); + return { + 'symbol': market['symbol'], + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': this.safeFloat (ticker, 'last'), + 'last': this.safeFloat (ticker, 'last'), + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": { + // "timestamp":1559433057, + // "asks": [ + // ["100.0","20.0"], + // ["4.74","2000.0"], + // ["1.74","4000.0"], + // ], + // "bids":[ + // ["0.0065","5482873.4"], + // ["0.00649","4781956.2"], + // ["0.00648","2876006.8"], + // ], + // } + // } + // + const orderbook = this.safeValue (response, 'data', {}); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const marketIds = this.marketIds (symbols); + const request = { + 'markets': marketIds, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBookMulti (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": [ + // { + // "timestamp":1559433057, + // "market": "bagvet", + // "asks": [ + // ["100.0","20.0"], + // ["4.74","2000.0"], + // ["1.74","4000.0"], + // ], + // "bids":[ + // ["0.0065","5482873.4"], + // ["0.00649","4781956.2"], + // ["0.00648","2876006.8"], + // ], + // }, + // ..., + // ], + // } + // + const data = this.safeValue (response, 'data', []); + const result = {}; + for (let i = 0; i < data.length; i++) { + const orderbook = data[i]; + const marketId = this.safeString (orderbook, 'market'); + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + result[symbol] = this.parseOrderBook (orderbook, timestamp); + } + return result; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTrades (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + return this.parseTrades (data, market, since, limit); + } + + parseTrade (trade, market = undefined) { + let side = this.safeValue (trade, 'side'); + if (side === 'bid') { + side = 'buy'; + } else if (side === 'ask') { + side = 'sell'; + } + let symbol = undefined; + const marketId = this.safeValue (trade, 'market'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let timestamp = this.safeTimestamp (trade, 'created_on'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': this.safeString (trade, 'id'), + 'order': undefined, + 'type': 'limit', + 'takerOrMaker': undefined, + 'side': side, + 'price': this.safeFloat (trade, 'price'), + 'amount': this.safeFloat (trade, 'volume'), + 'cost': undefined, + 'fee': undefined, + }; + } + + async fetchTime (params = {}) { + const response = await this.publicGetTimestamp (params); + // + // {"code":0,"message":"Operation successful","data":1559433420} + // + return this.safeTimestamp (response, 'data'); + } + + async fetchAllTradingFees (params = {}) { + const response = await this.publicGetFeesTrading (params); + const data = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < data.length; i++) { + const group = data[i]; + const maker = this.safeValue (group, 'ask_fee', {}); + const taker = this.safeValue (group, 'bid_fee', {}); + const marketId = this.safeString (group, 'market'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + symbol = this.markets_by_id[marketId]['symbol']; + } + result[symbol] = { + 'info': group, + 'symbol': symbol, + 'maker': this.safeFloat (maker, 'value'), + 'taker': this.safeFloat (taker, 'value'), + }; + } + return result; + } + + async fetchKey (params = {}) { + const response = await this.privateGetKey (params); + return this.safeValue (response, 'data'); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetMembersMe (params); + const data = this.safeValue (response, 'data'); + const balances = this.safeValue (data, 'accounts'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeValue (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'side': side, + 'ord_type': type, + 'volume': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + return this.parseOrder (data, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + let ids = id; + if (!Array.isArray (id)) { + ids = [ id ]; + } + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { 'ids': ids }; + const response = await this.privateGetOrders (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + const dataLength = data.length; + if (data === undefined) { + throw new OrderNotFound (this.id + ' could not found matching order'); + } + if (Array.isArray (id)) { + return this.parseOrders (data, market); + } + if (dataLength === 0) { + throw new OrderNotFound (this.id + ' could not found matching order'); + } + return this.parseOrder (data[0], market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'states': [ 'wait' ], + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'states': [ 'done', 'cancel' ], + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const states = this.safeValue (params, 'states', [ 'wait', 'done', 'cancel' ]); + const query = this.omit (params, 'states'); + const request = { + 'market': market['id'], + 'states': states, + 'need_price': 'True', + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrdersFilter (this.extend (request, query)); + const data = this.safeValue (response, 'data', []); + let result = []; + for (let i = 0; i < data.length; i++) { + const orders = this.safeValue (data[i], 'orders', []); + const status = this.parseOrderStatus (this.safeValue (data[i], 'state')); + const parsedOrders = this.parseOrders (orders, market, since, limit, { 'status': status }); + result = this.arrayConcat (result, parsedOrders); + } + return result; + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeValue (order, 'state')); + const marketId = this.safeValue2 (order, 'market', 'market_id'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let timestamp = this.safeTimestamp (order, 'created_on'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (order, 'created_at')); + } + return { + 'info': order, + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': this.safeValue (order, 'ord_type'), + 'side': this.safeValue (order, 'side'), + 'price': this.safeFloat (order, 'price'), + 'average': this.safeFloat (order, 'avg_price'), + 'amount': this.safeFloat (order, 'volume'), + 'remaining': this.safeFloat (order, 'remaining_volume'), + 'filled': this.safeFloat (order, 'executed_volume'), + 'status': status, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'wait': 'open', + 'done': 'closed', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async createOrders (symbol, orders, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'orders': orders, + }; + // orders: [{"side":"buy", "volume":.2, "price":1001}, {"side":"sell", "volume":0.2, "price":1002}] + const response = await this.privatePostOrdersMulti (this.extend (request, params)); + const data = response['data']; + return this.parseOrders (data); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrderDelete (this.extend ({ 'id': id }, params)); + const data = this.safeValue (response, 'data'); + return this.parseOrder (data); + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrderDeleteMulti (this.extend ({ 'ids': ids }, params)); + const data = this.safeValue (response, 'data'); + return this.parseOrders (data); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrdersClear (params); + const data = this.safeValue (response, 'data'); + return this.parseOrders (data); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (path === 'tickers_multi' || path === 'order_book/multi') { + let request = '?'; + const markets = this.safeValue (params, 'markets'); + for (let i = 0; i < markets.length; i++) { + request += 'markets[]=' + markets[i] + '&'; + } + const limit = this.safeValue (params, 'limit'); + if (limit !== undefined) { + request += 'limit=' + limit; + } + url += request; + } else if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'private') { + this.checkRequiredCredentials (); + const request = { + 'uid': this.apiKey, + 'data': query, + }; + // to set the private key: + // const fs = require ('fs') + // exchange.secret = fs.readFileSync ('oceanex.pem', 'utf8') + const jwt_token = this.jwt (request, this.encode (this.secret), 'RS256'); + url += '?user_jwt=' + jwt_token; + } + headers = { 'Content-Type': 'application/json' }; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + // + // {"code":1011,"message":"This IP '5.228.233.138' is not allowed","data":{}} + // + if (response === undefined) { + return; + } + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + if ((errorCode !== undefined) && (errorCode !== '0')) { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['codes'], errorCode, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (response); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],114:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const okcoinusd = require ('./okcoinusd.js'); + +// --------------------------------------------------------------------------- + +module.exports = class okcoincny extends okcoinusd { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okcoincny', + 'name': 'OKCoin CNY', + 'countries': [ 'CN' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766792-8be9157a-5ee5-11e7-926c-6d69b8d3378d.jpg', + 'api': { + 'web': 'https://www.okcoin.cn', + 'public': 'https://www.okcoin.cn/api', + 'private': 'https://www.okcoin.cn/api', + }, + 'www': 'https://www.okcoin.cn', + 'doc': 'https://www.okcoin.cn/rest_getStarted.html', + 'referral': 'https://www.okcoin.cn', + }, + 'markets': { + 'BTC/CNY': { 'id': 'btc_cny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'LTC/CNY': { 'id': 'ltc_cny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'ETH/CNY': { 'id': 'eth_cny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'ETC/CNY': { 'id': 'etc_cny', 'symbol': 'ETC/CNY', 'base': 'ETC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'BCH/CNY': { 'id': 'bcc_cny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + }, + }); + } +}; + +},{"./okcoinusd.js":115}],115:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, ArgumentsRequired, DDoSProtection, InsufficientFunds, InvalidOrder, OrderNotFound, AuthenticationError, BadRequest } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class okcoinusd extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okcoinusd', + 'name': 'OKCoin USD', + 'countries': [ 'CN', 'US' ], + 'version': 'v1', + 'rateLimit': 1000, // up to 3000 requests per 5 minutes ≈ 600 requests per minute ≈ 10 requests per second ≈ 100 ms + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchTickers': true, + 'withdraw': true, + 'futures': false, + }, + 'extension': '.do', // appended to endpoint URL + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': '1day', + '3d': '3day', + '1w': '1week', + }, + 'api': { + 'v3': { + 'get': [ + 'futures/pc/market/futuresCoin', + ], + }, + 'web': { + 'get': [ + 'futures/pc/market/marketOverview', + 'spot/markets/index-tickers', + 'spot/markets/currencies', + 'spot/markets/products', + 'spot/markets/tickers', + 'spot/user-level', + ], + 'post': [ + 'futures/pc/market/futuresCoin', + ], + }, + 'public': { + 'get': [ + 'depth', + 'exchange_rate', + 'future_depth', + 'future_estimated_price', + 'future_hold_amount', + 'future_index', + 'future_kline', + 'future_price_limit', + 'future_ticker', + 'future_trades', + 'kline', + 'otcs', + 'ticker', + 'tickers', + 'trades', + ], + }, + 'private': { + 'post': [ + 'account_records', + 'batch_trade', + 'borrow_money', + 'borrow_order_info', + 'borrows_info', + 'cancel_borrow', + 'cancel_order', + 'cancel_otc_order', + 'cancel_withdraw', + 'funds_transfer', + 'future_batch_trade', + 'future_cancel', + 'future_devolve', + 'future_explosive', + 'future_order_info', + 'future_orders_info', + 'future_position', + 'future_position_4fix', + 'future_trade', + 'future_trades_history', + 'future_userinfo', + 'future_userinfo_4fix', + 'lend_depth', + 'order_fee', + 'order_history', + 'order_info', + 'orders_info', + 'otc_order_history', + 'otc_order_info', + 'repayment', + 'submit_otc_order', + 'trade', + 'trade_history', + 'trade_otc_order', + 'wallet_info', + 'withdraw', + 'withdraw_info', + 'unrepayments_info', + 'userinfo', + ], + }, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766791-89ffb502-5ee5-11e7-8a5b-c5950b68ac65.jpg', + 'api': { + 'v3': 'https://www.okcoin.com/v3', + 'web': 'https://www.okcoin.com/v2', + 'public': 'https://www.okcoin.com/api', + 'private': 'https://www.okcoin.com', + }, + 'www': 'https://www.okcoin.com', + 'doc': [ + 'https://www.okcoin.com/docs/en/', + 'https://www.npmjs.com/package/okcoin.com', + ], + 'referral': 'https://www.okcoin.com/account/register?flag=activity&channelId=600001513', + 'fees': 'https://support.okcoin.com/hc/en-us/articles/360015261532-OKCoin-Fee-Rates', + }, + // these are okcoin.com fees, okex fees are in okex.js + 'fees': { + 'trading': { + 'taker': 0.002, + 'maker': 0.001, + }, + }, + 'exceptions': { + // see https://github.com/okcoin-okex/API-docs-OKEx.com/blob/master/API-For-Spot-EN/Error%20Code%20For%20Spot.md + '10000': ExchangeError, // "Required field, can not be null" + '10001': DDoSProtection, // "Request frequency too high to exceed the limit allowed" + '10005': AuthenticationError, // "'SecretKey' does not exist" + '10006': AuthenticationError, // "'Api_key' does not exist" + '10007': AuthenticationError, // "Signature does not match" + '1002': InsufficientFunds, // "The transaction amount exceed the balance" + '1003': InvalidOrder, // "The transaction amount is less than the minimum requirement" + '1004': InvalidOrder, // "The transaction amount is less than 0" + '1013': InvalidOrder, // no contract type (PR-1101) + '1027': InvalidOrder, // createLimitBuyOrder(symbol, 0, 0): Incorrect parameter may exceeded limits + '1050': InvalidOrder, // returned when trying to cancel an order that was filled or canceled previously + '1217': InvalidOrder, // "Order was sent at ±5% of the current market price. Please resend" + '10014': InvalidOrder, // "Order price must be between 0 and 1,000,000" + '1009': OrderNotFound, // for spot markets, cancelling closed order + '1019': OrderNotFound, // order closed? ("Undo order failed") + '1051': OrderNotFound, // for spot markets, cancelling "just closed" order + '10009': OrderNotFound, // for spot markets, "Order does not exist" + '20015': OrderNotFound, // for future markets + '10008': BadRequest, // Illegal URL parameter + '1007': BadSymbol, // No trading market information + // todo: sort out below + // 10000 Required parameter is empty + // 10001 Request frequency too high to exceed the limit allowed + // 10002 Authentication failure + // 10002 System error + // 10003 This connection has requested other user data + // 10004 Request failed + // 10005 api_key or sign is invalid, 'SecretKey' does not exist + // 10006 'Api_key' does not exist + // 10007 Signature does not match + // 10008 Illegal parameter, Parameter erorr + // 10009 Order does not exist + // 10010 Insufficient funds + // 10011 Amount too low + // 10012 Only btc_usd ltc_usd supported + // 10013 Only support https request + // 10014 Order price must be between 0 and 1,000,000 + // 10015 Order price differs from current market price too much / Channel subscription temporally not available + // 10016 Insufficient coins balance + // 10017 API authorization error / WebSocket authorization error + // 10018 borrow amount less than lower limit [usd:100,btc:0.1,ltc:1] + // 10019 loan agreement not checked + // 1002 The transaction amount exceed the balance + // 10020 rate cannot exceed 1% + // 10021 rate cannot less than 0.01% + // 10023 fail to get latest ticker + // 10024 balance not sufficient + // 10025 quota is full, cannot borrow temporarily + // 10026 Loan (including reserved loan) and margin cannot be withdrawn + // 10027 Cannot withdraw within 24 hrs of authentication information modification + // 10028 Withdrawal amount exceeds daily limit + // 10029 Account has unpaid loan, please cancel/pay off the loan before withdraw + // 1003 The transaction amount is less than the minimum requirement + // 10031 Deposits can only be withdrawn after 6 confirmations + // 10032 Please enabled phone/google authenticator + // 10033 Fee higher than maximum network transaction fee + // 10034 Fee lower than minimum network transaction fee + // 10035 Insufficient BTC/LTC + // 10036 Withdrawal amount too low + // 10037 Trade password not set + // 1004 The transaction amount is less than 0 + // 10040 Withdrawal cancellation fails + // 10041 Withdrawal address not exsit or approved + // 10042 Admin password error + // 10043 Account equity error, withdrawal failure + // 10044 fail to cancel borrowing order + // 10047 this function is disabled for sub-account + // 10048 withdrawal information does not exist + // 10049 User can not have more than 50 unfilled small orders (amount<0.15BTC) + // 10050 can't cancel more than once + // 10051 order completed transaction + // 10052 not allowed to withdraw + // 10064 after a USD deposit, that portion of assets will not be withdrawable for the next 48 hours + // 1008 No latest market information + // 1009 No order + // 1010 Different user of the cancelled order and the original order + // 10100 User account frozen + // 10101 order type is wrong + // 10102 incorrect ID + // 10103 the private otc order's key incorrect + // 10106 API key domain not matched + // 1011 No documented user + // 1013 No order type + // 1014 No login + // 1015 No market depth information + // 1017 Date error + // 1018 Order failed + // 1019 Undo order failed + // 10216 Non-available API / non-public API + // 1024 Currency does not exist + // 1025 No chart type + // 1026 No base currency quantity + // 1027 Incorrect parameter may exceeded limits + // 1028 Reserved decimal failed + // 1029 Preparing + // 1030 Account has margin and futures, transactions can not be processed + // 1031 Insufficient Transferring Balance + // 1032 Transferring Not Allowed + // 1035 Password incorrect + // 1036 Google Verification code Invalid + // 1037 Google Verification code incorrect + // 1038 Google Verification replicated + // 1039 Message Verification Input exceed the limit + // 1040 Message Verification invalid + // 1041 Message Verification incorrect + // 1042 Wrong Google Verification Input exceed the limit + // 1043 Login password cannot be same as the trading password + // 1044 Old password incorrect + // 1045 2nd Verification Needed + // 1046 Please input old password + // 1048 Account Blocked + // 1050 Orders have been withdrawn or withdrawn + // 1051 Order completed + // 1201 Account Deleted at 00: 00 + // 1202 Account Not Exist + // 1203 Insufficient Balance + // 1204 Invalid currency + // 1205 Invalid Account + // 1206 Cash Withdrawal Blocked + // 1207 Transfer Not Support + // 1208 No designated account + // 1209 Invalid api + // 1216 Market order temporarily suspended. Please send limit order + // 1217 Order was sent at ±5% of the current market price. Please resend + // 1218 Place order failed. Please try again later + // 20001 User does not exist + // 20002 Account frozen + // 20003 Account frozen due to forced liquidation + // 20004 Contract account frozen + // 20005 User contract account does not exist + // 20006 Required field missing + // 20007 Illegal parameter + // 20008 Contract account balance is too low + // 20009 Contract status error + // 20010 Risk rate ratio does not exist + // 20011 Risk rate lower than 90%/80% before opening BTC position with 10x/20x leverage. or risk rate lower than 80%/60% before opening LTC position with 10x/20x leverage + // 20012 Risk rate lower than 90%/80% after opening BTC position with 10x/20x leverage. or risk rate lower than 80%/60% after opening LTC position with 10x/20x leverage + // 20013 Temporally no counter party price + // 20014 System error + // 20015 Order does not exist + // 20016 Close amount bigger than your open positions, liquidation quantity bigger than holding + // 20017 Not authorized/illegal operation/illegal order ID + // 20018 Order price cannot be more than 103-105% or less than 95-97% of the previous minute price + // 20019 IP restricted from accessing the resource + // 20020 Secret key does not exist + // 20021 Index information does not exist + // 20022 Wrong API interface (Cross margin mode shall call cross margin API, fixed margin mode shall call fixed margin API) + // 20023 Account in fixed-margin mode + // 20024 Signature does not match + // 20025 Leverage rate error + // 20026 API Permission Error + // 20027 no transaction record + // 20028 no such contract + // 20029 Amount is large than available funds + // 20030 Account still has debts + // 20038 Due to regulation, this function is not availavle in the country/region your currently reside in. + // 20049 Request frequency too high + // 20100 request time out + // 20101 the format of data is error + // 20102 invalid login + // 20103 event type error + // 20104 subscription type error + // 20107 JSON format error + // 20115 The quote is not match + // 20116 Param not match + // 21020 Contracts are being delivered, orders cannot be placed + // 21021 Contracts are being settled, contracts cannot be placed + }, + 'options': { + 'marketBuyPrice': false, + 'fetchOHLCVWarning': true, + 'contractTypes': { + '1': 'this_week', + '2': 'next_week', + '4': 'quarter', + }, + 'fetchTickersMethod': 'fetch_tickers_from_api', + }, + }); + } + + async fetchMarkets (params = {}) { + // TODO: they have a new fee schedule as of Feb 7 + // the new fees are progressive and depend on 30-day traded volume + // the following is the worst case + const result = []; + const spotResponse = await this.webGetSpotMarketsProducts (); + // + // { + // "code": 0, + // "data": [ + // { + // "baseCurrency":0, + // "brokerId":0, + // "callAuctionOrCallNoCancelAuction":false, + // "callNoCancelSwitchTime":{}, + // "collect":"0", + // "continuousSwitchTime":{}, + // "groupId":1, + // "isMarginOpen":true, + // "listDisplay":0, + // "marginRiskPreRatio":1.2, + // "marginRiskRatio":1.1, + // "marketFrom":118, + // "maxMarginLeverage":5, + // "maxPriceDigit":1, + // "maxSizeDigit":8, + // "mergeTypes":"0.1,1,10", + // "minTradeSize":0.00100000, + // "online":1, + // "productId":20, + // "quoteCurrency":7, + // "quoteIncrement":"0.1", + // "quotePrecision":2, + // "sort":30038, + // "symbol":"btc_usdt", + // "tradingMode":3 + // }, + // ] + // } + // + const spotMarkets = this.safeValue (spotResponse, 'data', []); + let markets = spotMarkets; + if (this.has['futures']) { + const futuresResponse = await this.v3GetFuturesPcMarketFuturesCoin ({ + 'currencyCode': 0, + }); + // + // { + // "msg":"success", + // "code":0, + // "detailMsg":"", + // "data": [ + // { + // "symbolId":0, + // "symbol":"f_usd_btc", + // "iceSingleAvgMinAmount":2, + // "minTradeSize":1, + // "iceSingleAvgMaxAmount":500, + // "contractDepthLevel":["0.01","0.2"], + // "dealAllMaxAmount":999, + // "maxSizeDigit":4, + // "contracts":[ + // { "marketFrom":34, "id":201905240000034, "type":1, "desc":"BTC0524" }, + // { "marketFrom":13, "id":201905310000013, "type":2, "desc":"BTC0531" }, + // { "marketFrom":12, "id":201906280000012, "type":4, "desc":"BTC0628" }, + // ], + // "maxPriceDigit":2, + // "nativeRate":1, + // "quote":"usd", + // "nativeCurrency":"usd", + // "nativeCurrencyMark":"$", + // "contractSymbol":0, + // "unitAmount":100.00, + // "symbolMark":"฿", + // "symbolDesc":"BTC" + // }, + // ] + // } + // + const data = this.safeValue (futuresResponse, 'data', {}); + const futuresMarkets = this.safeValue (data, 'usd', []); + markets = this.arrayConcat (spotMarkets, futuresMarkets); + } + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + let symbol = undefined; + let base = undefined; + let quote = undefined; + let baseId = undefined; + let quoteId = undefined; + let baseNumericId = undefined; + let quoteNumericId = undefined; + let lowercaseId = undefined; + let uppercaseBaseId = undefined; + const precision = { + 'amount': this.safeInteger (market, 'maxSizeDigit'), + 'price': this.safeInteger (market, 'maxPriceDigit'), + }; + const minAmount = this.safeFloat (market, 'minTradeSize'); + const minPrice = Math.pow (10, -precision['price']); + let contracts = this.safeValue (market, 'contracts'); + if (contracts === undefined) { + // spot markets + lowercaseId = id; + const parts = id.split ('_'); + baseId = parts[0]; + quoteId = parts[1]; + baseNumericId = this.safeInteger (market, 'baseCurrency'); + quoteNumericId = this.safeInteger (market, 'quoteCurrency'); + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + contracts = [{}]; + } else { + // futures markets + quoteId = this.safeStringLower2 (market, 'quote', 'denomination'); + uppercaseBaseId = this.safeStringLower (market, 'symbolDesc'); + baseId = uppercaseBaseId.toLowerCase (); + lowercaseId = baseId + '_' + quoteId; + base = this.safeCurrencyCode (uppercaseBaseId); + quote = this.safeCurrencyCode (quoteId); + } + for (let k = 0; k < contracts.length; k++) { + const contract = contracts[k]; + let type = this.safeString (contract, 'type', 'spot'); + let contractType = undefined; + let spot = true; + let future = false; + let active = true; + if (type === 'spot') { + symbol = base + '/' + quote; + active = market['online'] !== 0; + } else { + const contractId = this.safeString (contract, 'id'); + symbol = base + '-' + quote + '-' + contractId.slice (2, 8); + contractType = this.safeString (this.options['contractTypes'], type); + type = 'future'; + spot = false; + future = true; + } + const fees = this.safeValue2 (this.fees, type, 'trading', {}); + result.push (this.extend (fees, { + 'id': id, + 'lowercaseId': lowercaseId, + 'contractType': contractType, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'baseNumericId': baseNumericId, + 'quoteNumericId': quoteNumericId, + 'info': market, + 'type': type, + 'spot': spot, + 'future': future, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': minAmount * minPrice, + 'max': undefined, + }, + }, + })); + } + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, cost)), + }; + } + + async fetchTickersFromApi (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + const response = await this.publicGetTickers (this.extend (request, params)); + const tickers = response['tickers']; + const timestamp = this.safeTimestamp (response, 'date'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + let ticker = tickers[i]; + ticker = this.parseTicker (this.extend (tickers[i], { 'timestamp': timestamp })); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTickersFromWeb (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + const response = await this.webGetSpotMarketsTickers (this.extend (request, params)); + const tickers = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTickers (symbols = undefined, params = {}) { + const method = this.options['fetchTickersMethod']; + return await this[method] (symbols, params); + } + + async fetchOrderBook (symbol = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureDepth' : 'publicGetDepth'; + const request = this.createRequest (market, params); + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this[method] (request); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + // + // { buy: "48.777300", + // change: "-1.244500", + // changePercentage: "-2.47%", + // close: "49.064000", + // createdDate: 1531704852254, + // currencyId: 527, + // dayHigh: "51.012500", + // dayLow: "48.124200", + // high: "51.012500", + // inflows: "0", + // last: "49.064000", + // low: "48.124200", + // marketFrom: 627, + // name: { }, + // open: "50.308500", + // outflows: "0", + // productId: 527, + // sell: "49.064000", + // symbol: "zec_okb", + // volume: "1049.092535" } + // + const timestamp = this.safeInteger2 (ticker, 'timestamp', 'createdDate'); + let symbol = undefined; + if (market === undefined) { + if ('symbol' in ticker) { + const marketId = ticker['symbol']; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = ticker['symbol'].split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open'); + const change = this.safeFloat (ticker, 'change'); + const percentage = this.safeFloat (ticker, 'changePercentage'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat2 (ticker, 'vol', 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureTicker' : 'publicGetTicker'; + const request = this.createRequest (market, params); + const response = await this[method] (request); + let ticker = this.safeValue (response, 'ticker'); + if (ticker === undefined) { + throw new ExchangeError (this.id + ' fetchTicker returned an empty response: ' + this.json (response)); + } + const timestamp = this.safeTimestamp (response, 'date'); + if (timestamp !== undefined) { + ticker = this.extend (ticker, { 'timestamp': timestamp }); + } + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'date_ms'); + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureTrades' : 'publicGetTrades'; + const request = this.createRequest (market, params); + const response = await this[method] (request); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + const numElements = ohlcv.length; + const volumeIndex = (numElements > 6) ? 6 : 5; + return [ + ohlcv[0], // timestamp + parseFloat (ohlcv[1]), // Open + parseFloat (ohlcv[2]), // High + parseFloat (ohlcv[3]), // Low + parseFloat (ohlcv[4]), // Close + // parseFloat (ohlcv[5]), // quote volume + // parseFloat (ohlcv[6]), // base volume + parseFloat (ohlcv[volumeIndex]), // okex will return base volume in the 7th element for future markets + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureKline' : 'publicGetKline'; + const request = this.createRequest (market, { + 'type': this.timeframes[timeframe], + }); + if (since !== undefined) { + request['since'] = parseInt ((this.milliseconds () - 86400000) / 1000); // default last 24h + } + if (limit !== undefined) { + if (this.options['fetchOHLCVWarning']) { + throw new ExchangeError (this.id + ' fetchOHLCV counts "limit" candles backwards in chronological ascending order, therefore the "limit" argument for ' + this.id + ' is disabled. Set ' + this.id + '.options["fetchOHLCVWarning"] = false to suppress this warning message.'); + } + request['size'] = parseInt (limit); // max is 1440 candles + } + const response = await this[method] (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserinfo (params); + const info = this.safeValue (response, 'info', {}); + const balances = this.safeValue (info, 'funds', {}); + const result = { 'info': response }; + let ids = Object.keys (balances['free']); + let usedField = 'freezed'; + // wtf, okex? + // https://github.com/okcoin-okex/API-docs-OKEx.com/commit/01cf9dd57b1f984a8737ef76a037d4d3795d2ac7 + if (!(usedField in balances)) { + usedField = 'holds'; + } + const usedKeys = Object.keys (balances[usedField]); + ids = this.arrayConcat (ids, usedKeys); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + const account = this.account (); + account['free'] = this.safeFloat (balances['free'], id); + account['used'] = this.safeFloat (balances[usedField], id); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'privatePostFutureTrade' : 'privatePostTrade'; + const orderSide = (type === 'market') ? (side + '_market') : side; + const isMarketBuy = ((market['spot']) && (type === 'market') && (side === 'buy') && (!this.options['marketBuyPrice'])); + const orderPrice = isMarketBuy ? this.safeFloat (params, 'cost') : price; + const request = this.createRequest (market, { + 'type': orderSide, + }); + if (market['future']) { + request['match_price'] = (type === 'market') ? 1 : 0; // match best counter party price? 0 or 1, ignores price if 1 + request['lever_rate'] = 10; // leverage rate value: 10 or 20 (10 by default) + request['type'] = (side === 'buy') ? '1' : '2'; + } else if (type === 'market') { + if (side === 'buy') { + if (!orderPrice) { + if (this.options['marketBuyPrice']) { + // eslint-disable-next-line quotes + throw new ExchangeError (this.id + " market buy orders require a price argument (the amount you want to spend or the cost of the order) when this.options['marketBuyPrice'] is true."); + } else { + // eslint-disable-next-line quotes + throw new ExchangeError (this.id + " market buy orders require an additional cost parameter, cost = price * amount. If you want to pass the cost of the market order (the amount you want to spend) in the price argument (the default " + this.id + " behaviour), set this.options['marketBuyPrice'] = true. It will effectively suppress this warning exception as well."); + } + } else { + request['price'] = orderPrice; + } + } else { + request['amount'] = amount; + } + } + if (type !== 'market') { + request['price'] = orderPrice; + request['amount'] = amount; + } + params = this.omit (params, 'cost'); + const response = await this[method] (this.extend (request, params)); + const timestamp = this.milliseconds (); + return { + 'info': response, + 'id': this.safeString (response, 'order_id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'privatePostFutureCancel' : 'privatePostCancelOrder'; + const request = this.createRequest (market, { + 'order_id': id, + }); + const response = await this[method] (this.extend (request, params)); + return response; + } + + parseOrderStatus (status) { + const statuses = { + '-1': 'canceled', + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeValue (statuses, status, status); + } + + parseOrderSide (side) { + if (side === 1) { + return 'buy'; // open long position + } else if (side === 2) { + return 'sell'; // open short position + } else if (side === 3) { + return 'sell'; // liquidate long position + } else if (side === 4) { + return 'buy'; // liquidate short position + } + return side; + } + + parseOrder (order, market = undefined) { + let side = undefined; + let type = undefined; + if ('type' in order) { + if ((order['type'] === 'buy') || (order['type'] === 'sell')) { + side = order['type']; + type = 'limit'; + } else if (order['type'] === 'buy_market') { + side = 'buy'; + type = 'market'; + } else if (order['type'] === 'sell_market') { + side = 'sell'; + type = 'market'; + } else { + side = this.parseOrderSide (order['type']); + if (('contract_name' in order) || ('lever_rate' in order)) { + type = 'margin'; + } + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market) { + symbol = market['symbol']; + } + const createDateField = this.getCreateDateField (); + const timestamp = this.safeInteger (order, createDateField); + let amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'deal_amount'); + amount = Math.max (amount, filled); + let remaining = Math.max (0, amount - filled); + if (type === 'market') { + remaining = 0; + } + let average = this.safeFloat (order, 'avg_price'); + // https://github.com/ccxt/ccxt/issues/2452 + average = this.safeFloat (order, 'price_avg', average); + const cost = average * filled; + return { + 'info': order, + 'id': this.safeString (order, 'order_id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + getCreateDateField () { + // needed for derived exchanges + // allcoin typo create_data instead of create_date + return 'create_date'; + } + + getOrdersField () { + // needed for derived exchanges + // allcoin typo order instead of orders (expected based on their API docs) + return 'orders'; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'privatePostFutureOrderInfo' : 'privatePostOrderInfo'; + const request = this.createRequest (market, { + 'order_id': id, + // 'status': 0, // 0 for unfilled orders, 1 for filled orders + // 'current_page': 1, // current page number + // 'page_length': 200, // number of orders returned per page, maximum 200 + }); + const response = await this[method] (this.extend (request, params)); + const ordersField = this.getOrdersField (); + const numOrders = response[ordersField].length; + if (numOrders > 0) { + return this.parseOrder (response[ordersField][0]); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + let method = market['future'] ? 'privatePostFutureOrdersInfo' : 'privatePost'; + let request = this.createRequest (market); + const order_id_in_params = ('order_id' in params); + if (market['future']) { + if (!order_id_in_params) { + throw new ExchangeError (this.id + ' fetchOrders() requires order_id param for futures market ' + symbol + ' (a string of one or more order ids, comma-separated)'); + } + } else { + const status = ('type' in params) ? params['type'] : params['status']; + if (typeof status === 'undefined') { + const name = order_id_in_params ? 'type' : 'status'; + throw new ExchangeError (this.id + ' fetchOrders() requires ' + name + ' param for spot market ' + symbol + ' (0 - for unfilled orders, 1 - for filled/canceled orders)'); + } + if (order_id_in_params) { + method += 'OrdersInfo'; + request = this.extend (request, { + 'type': status, + 'order_id': params['order_id'], + }); + } else { + method += 'OrderHistory'; + request = this.extend (request, { + 'status': status, + 'current_page': 1, // current page number + 'page_length': 200, // number of orders returned per page, maximum 200 + }); + } + params = this.omit (params, [ 'type', 'status' ]); + } + const response = await this[method] (this.extend (request, params)); + const ordersField = this.getOrdersField (); + return this.parseOrders (response[ordersField], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 0, // 0 for unfilled orders, 1 for filled orders + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 1, // 0 for unfilled orders, 1 for filled orders + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // if (amount < 0.01) + // throw new ExchangeError (this.id + ' withdraw() requires amount > 0.01'); + // for some reason they require to supply a pair of currencies for withdrawing one currency + const currencyId = currency['id'] + '_usd'; + if (tag) { + address = address + ':' + tag; + } + const request = { + 'symbol': currencyId, + 'withdraw_address': address, + 'withdraw_amount': amount, + 'target': 'address', // or 'okcn', 'okcom', 'okex' + }; + let query = params; + if ('chargefee' in query) { + request['chargefee'] = query['chargefee']; + query = this.omit (query, 'chargefee'); + } else { + throw new ExchangeError (this.id + ' withdraw() requires a `chargefee` parameter'); + } + if (this.password) { + request['trade_pwd'] = this.password; + } else if ('password' in query) { + request['trade_pwd'] = query['password']; + query = this.omit (query, 'password'); + } else if ('trade_pwd' in query) { + request['trade_pwd'] = query['trade_pwd']; + query = this.omit (query, 'trade_pwd'); + } + const passwordInRequest = ('trade_pwd' in request); + if (!passwordInRequest) { + throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a password / trade_pwd parameter'); + } + const response = await this.privatePostWithdraw (this.extend (request, query)); + return { + 'info': response, + 'id': this.safeString (response, 'withdraw_id'), + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/'; + const isWebApi = (api === 'web') || (api === 'v3'); + if (!isWebApi) { + url += this.version + '/'; + } + url += path; + if (!isWebApi) { + url += this.extension; + } + if (api === 'private') { + this.checkRequiredCredentials (); + const query = this.keysort (this.extend ({ + 'api_key': this.apiKey, + }, params)); + // secret key must be at the end of query + const queryString = this.rawencode (query) + '&secret_key=' + this.secret; + query['sign'] = this.hash (this.encode (queryString)).toUpperCase (); + body = this.urlencode (query); + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + url = this.urls['api'][api] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + createRequest (market, params = {}) { + if (market['future']) { + return this.deepExtend ({ + 'symbol': market['lowercaseId'], + 'contract_type': market['contractType'], + }, params); + } + return this.deepExtend ({ + 'symbol': market['id'], + }, params); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('error_code' in response) { + const error = this.safeString (response, 'error_code'); + const message = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, message); + throw new ExchangeError (message); + } + if ('result' in response) { + if (!response['result']) { + throw new ExchangeError (this.id + ' ' + body); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],116:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const okcoinusd = require ('./okcoinusd.js'); + +// --------------------------------------------------------------------------- + +module.exports = class okex extends okcoinusd { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okex', + 'name': 'OKEX', + 'countries': [ 'CN', 'US' ], + 'has': { + 'CORS': false, + 'futures': true, + 'fetchTickers': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/32552768-0d6dd3c6-c4a6-11e7-90f8-c043b64756a7.jpg', + 'api': { + 'v3': 'https://www.okex.com/v3', + 'web': 'https://www.okex.com/v2', + 'public': 'https://www.okex.com/api', + 'private': 'https://www.okex.com/api', + }, + 'www': 'https://www.okex.com', + 'doc': [ + 'https://github.com/okcoin-okex/API-docs-OKEx.com', + 'https://www.okex.com/docs/en/', + ], + 'fees': 'https://www.okex.com/pages/products/fees.html', + 'referral': 'https://www.okex.com', + }, + 'fees': { + 'trading': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'spot': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'future': { + 'taker': 0.0005, + 'maker': 0.0002, + }, + 'swap': { + 'taker': 0.00075, + 'maker': 0.0002, + }, + }, + 'commonCurrencies': { + // OKEX refers to ERC20 version of Aeternity (AEToken) + 'AE': 'AET', // https://github.com/ccxt/ccxt/issues/4981 + 'HOT': 'Hydro Protocol', + 'HSR': 'HC', + 'MAG': 'Maggie', + 'YOYO': 'YOYOW', + 'WIN': 'WinToken', // https://github.com/ccxt/ccxt/issues/5701 + }, + }); + } +}; + +},{"./okcoinusd.js":115}],117:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, AccountSuspended, InvalidAddress, PermissionDenied, DDoSProtection, InsufficientFunds, InvalidNonce, CancelPending, InvalidOrder, OrderNotFound, AuthenticationError, RequestTimeout, NotSupported, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class okex3 extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okex3', + 'name': 'OKEX', + 'countries': [ 'CN', 'US' ], + 'version': 'v3', + 'rateLimit': 1000, // up to 3000 requests per 5 minutes ≈ 600 requests per minute ≈ 10 requests per second ≈ 100 ms + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': false, // see below + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTime': true, + 'fetchTransactions': false, + 'fetchMyTrades': false, // they don't have it + 'fetchDepositAddress': true, + 'fetchOrderTrades': true, + 'fetchTickers': true, + 'fetchLedger': true, + 'withdraw': true, + 'futures': true, + }, + 'timeframes': { + '1m': '60', + '3m': '180', + '5m': '300', + '15m': '900', + '30m': '1800', + '1h': '3600', + '2h': '7200', + '4h': '14400', + '6h': '21600', + '12h': '43200', + '1d': '86400', + '1w': '604800', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/32552768-0d6dd3c6-c4a6-11e7-90f8-c043b64756a7.jpg', + 'api': 'https://www.okex.com', + 'www': 'https://www.okex.com', + 'doc': 'https://www.okex.com/docs/en/', + 'fees': 'https://www.okex.com/pages/products/fees.html', + }, + 'api': { + 'general': { + 'get': [ + 'time', + ], + }, + 'account': { + 'get': [ + 'currencies', + 'wallet', + 'wallet/{currency}', + 'withdrawal/fee', + 'withdrawal/history', + 'withdrawal/history/{currency}', + 'ledger', + 'deposit/address', + 'deposit/history', + 'deposit/history/{currency}', + ], + 'post': [ + 'transfer', + 'withdrawal', + ], + }, + 'spot': { + 'get': [ + 'accounts', + 'accounts/{currency}', + 'accounts/{currency}/ledger', + 'orders', + 'orders_pending', + 'orders/{order_id}', + 'orders/{client_oid}', + 'fills', + 'algo', + // public + 'instruments', + 'instruments/{instrument_id}/book', + 'instruments/ticker', + 'instruments/{instrument_id}/ticker', + 'instruments/{instrument_id}/trades', + 'instruments/{instrument_id}/candles', + ], + 'post': [ + 'order_algo', + 'orders', + 'batch_orders', + 'cancel_orders/{order_id}', + 'cancel_orders/{client_oid}', + 'cancel_batch_algos', + 'cancel_batch_orders', + ], + }, + 'margin': { + 'get': [ + 'accounts', + 'accounts/{instrument_id}', + 'accounts/{instrument_id}/ledger', + 'accounts/availability', + 'accounts/{instrument_id}/availability', + 'accounts/borrowed', + 'accounts/{instrument_id}/borrowed', + 'orders', + 'orders/{order_id}', + 'orders/{client_oid}', + 'orders_pending', + 'fills', + ], + 'post': [ + 'accounts/borrow', + 'accounts/repayment', + 'orders', + 'batch_orders', + 'cancel_orders', + 'cancel_orders/{order_id}', + 'cancel_orders/{client_oid}', + 'cancel_batch_orders', + ], + }, + 'futures': { + 'get': [ + 'position', + '{instrument_id}/position', + 'accounts', + 'accounts/{currency}', + 'accounts/{currency}/leverage', + 'accounts/{currency}/ledger', + 'order_algo/{instrument_id}', + 'orders/{instrument_id}', + 'orders/{instrument_id}/{order_id}', + 'orders/{instrument_id}/{client_oid}', + 'fills', + // public + 'instruments', + 'instruments/{instrument_id}/book', + 'instruments/ticker', + 'instruments/{instrument_id}/ticker', + 'instruments/{instrument_id}/trades', + 'instruments/{instrument_id}/candles', + 'accounts/{instrument_id}/holds', + 'instruments/{instrument_id}/index', + 'rate', + 'instruments/{instrument_id}/estimated_price', + 'instruments/{instrument_id}/open_interest', + 'instruments/{instrument_id}/price_limit', + 'instruments/{instrument_id}/liquidation', + 'instruments/{instrument_id}/mark_price', + ], + 'post': [ + 'accounts/{currency}/leverage', + 'accounts/margin_mode', + 'order', + 'orders', + 'order_algo', + 'cancel_algos', + 'cancel_order/{instrument_id}/{order_id}', + 'cancel_order/{instrument_id}/{client_oid}', + 'cancel_batch_orders/{instrument_id}', + 'close_position', + 'cancel_all', + ], + }, + 'swap': { + 'get': [ + 'position', + '{instrument_id}/position', + 'accounts', + '{instrument_id}/accounts', + 'accounts/{instrument_id}/settings', + 'accounts/{instrument_id}/ledger', + 'accounts/{instrument_id}/holds', + 'order_algo/{instrument_id}', + 'orders/{instrument_id}', + 'orders/{instrument_id}/{order_id}', + 'orders/{instrument_id}/{client_oid}', + 'fills', + // public + 'instruments', + 'instruments/{instrument_id}/depth', + 'instruments/ticker', + 'instruments/{instrument_id}/ticker', + 'instruments/{instrument_id}/trades', + 'instruments/{instrument_id}/candles', + 'instruments/{instrument_id}/index', + 'rate', + 'instruments/{instrument_id}/open_interest', + 'instruments/{instrument_id}/price_limit', + 'instruments/{instrument_id}/liquidation', + 'instruments/{instrument_id}/funding_time', + 'instruments/{instrument_id}/mark_price', + 'instruments/{instrument_id}/historical_funding_rate', + ], + 'post': [ + 'accounts/{instrument_id}/leverage', + 'order', + 'order_algo', + 'orders', + 'cancel_algos', + 'cancel_order/{instrument_id}/{order_id}', + 'cancel_order/{instrument_id}/{client_oid}', + 'cancel_batch_orders/{instrument_id}', + ], + }, + // they have removed this part from public + 'ett': { + 'get': [ + 'accounts', + 'accounts/{currency}', + 'accounts/{currency}/ledger', + 'orders', // fetchOrder, fetchOrders + // public + 'constituents/{ett}', + 'define-price/{ett}', + ], + 'post': [ + 'orders', + 'orders/{order_id}', + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'spot': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'futures': { + 'taker': 0.0005, + 'maker': 0.0002, + }, + 'swap': { + 'taker': 0.00075, + 'maker': 0.00020, + }, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'exceptions': { + // http error codes + // 400 Bad Request — Invalid request format + // 401 Unauthorized — Invalid API Key + // 403 Forbidden — You do not have access to the requested resource + // 404 Not Found + // 500 Internal Server Error — We had a problem with our server + 'exact': { + '1': ExchangeError, // { "code": 1, "message": "System error" } + // undocumented + 'failure to get a peer from the ring-balancer': ExchangeNotAvailable, // { "message": "failure to get a peer from the ring-balancer" } + '4010': PermissionDenied, // { "code": 4010, "message": "For the security of your funds, withdrawals are not permitted within 24 hours after changing fund password / mobile number / Google Authenticator settings " } + // common + '30001': AuthenticationError, // { "code": 30001, "message": 'request header "OK_ACCESS_KEY" cannot be blank'} + '30002': AuthenticationError, // { "code": 30002, "message": 'request header "OK_ACCESS_SIGN" cannot be blank'} + '30003': AuthenticationError, // { "code": 30003, "message": 'request header "OK_ACCESS_TIMESTAMP" cannot be blank'} + '30004': AuthenticationError, // { "code": 30004, "message": 'request header "OK_ACCESS_PASSPHRASE" cannot be blank'} + '30005': InvalidNonce, // { "code": 30005, "message": "invalid OK_ACCESS_TIMESTAMP" } + '30006': AuthenticationError, // { "code": 30006, "message": "invalid OK_ACCESS_KEY" } + '30007': BadRequest, // { "code": 30007, "message": 'invalid Content_Type, please use "application/json" format'} + '30008': RequestTimeout, // { "code": 30008, "message": "timestamp request expired" } + '30009': ExchangeError, // { "code": 30009, "message": "system error" } + '30010': AuthenticationError, // { "code": 30010, "message": "API validation failed" } + '30011': PermissionDenied, // { "code": 30011, "message": "invalid IP" } + '30012': AuthenticationError, // { "code": 30012, "message": "invalid authorization" } + '30013': AuthenticationError, // { "code": 30013, "message": "invalid sign" } + '30014': DDoSProtection, // { "code": 30014, "message": "request too frequent" } + '30015': AuthenticationError, // { "code": 30015, "message": 'request header "OK_ACCESS_PASSPHRASE" incorrect'} + '30016': ExchangeError, // { "code": 30015, "message": "you are using v1 apiKey, please use v1 endpoint. If you would like to use v3 endpoint, please subscribe to v3 apiKey" } + '30017': ExchangeError, // { "code": 30017, "message": "apikey's broker id does not match" } + '30018': ExchangeError, // { "code": 30018, "message": "apikey's domain does not match" } + '30019': ExchangeNotAvailable, // { "code": 30019, "message": "Api is offline or unavailable" } + '30020': BadRequest, // { "code": 30020, "message": "body cannot be blank" } + '30021': BadRequest, // { "code": 30021, "message": "Json data format error" }, { "code": 30021, "message": "json data format error" } + '30022': PermissionDenied, // { "code": 30022, "message": "Api has been frozen" } + '30023': BadRequest, // { "code": 30023, "message": "{0} parameter cannot be blank" } + '30024': BadSymbol, // {"code":30024,"message":"\"instrument_id\" is an invalid parameter"} + '30025': BadRequest, // { "code": 30025, "message": "{0} parameter category error" } + '30026': DDoSProtection, // { "code": 30026, "message": "requested too frequent" } + '30027': AuthenticationError, // { "code": 30027, "message": "login failure" } + '30028': PermissionDenied, // { "code": 30028, "message": "unauthorized execution" } + '30029': AccountSuspended, // { "code": 30029, "message": "account suspended" } + '30030': ExchangeError, // { "code": 30030, "message": "endpoint request failed. Please try again" } + '30031': BadRequest, // { "code": 30031, "message": "token does not exist" } + '30032': BadSymbol, // { "code": 30032, "message": "pair does not exist" } + '30033': BadRequest, // { "code": 30033, "message": "exchange domain does not exist" } + '30034': ExchangeError, // { "code": 30034, "message": "exchange ID does not exist" } + '30035': ExchangeError, // { "code": 30035, "message": "trading is not supported in this website" } + '30036': ExchangeError, // { "code": 30036, "message": "no relevant data" } + '30038': AuthenticationError, // { "code": 30038, "message": "user does not exist" } + '30037': ExchangeNotAvailable, // { "code": 30037, "message": "endpoint is offline or unavailable" } + // futures + '32001': AccountSuspended, // { "code": 32001, "message": "futures account suspended" } + '32002': PermissionDenied, // { "code": 32002, "message": "futures account does not exist" } + '32003': CancelPending, // { "code": 32003, "message": "canceling, please wait" } + '32004': ExchangeError, // { "code": 32004, "message": "you have no unfilled orders" } + '32005': InvalidOrder, // { "code": 32005, "message": "max order quantity" } + '32006': InvalidOrder, // { "code": 32006, "message": "the order price or trigger price exceeds USD 1 million" } + '32007': InvalidOrder, // { "code": 32007, "message": "leverage level must be the same for orders on the same side of the contract" } + '32008': InvalidOrder, // { "code": 32008, "message": "Max. positions to open (cross margin)" } + '32009': InvalidOrder, // { "code": 32009, "message": "Max. positions to open (fixed margin)" } + '32010': ExchangeError, // { "code": 32010, "message": "leverage cannot be changed with open positions" } + '32011': ExchangeError, // { "code": 32011, "message": "futures status error" } + '32012': ExchangeError, // { "code": 32012, "message": "futures order update error" } + '32013': ExchangeError, // { "code": 32013, "message": "token type is blank" } + '32014': ExchangeError, // { "code": 32014, "message": "your number of contracts closing is larger than the number of contracts available" } + '32015': ExchangeError, // { "code": 32015, "message": "margin ratio is lower than 100% before opening positions" } + '32016': ExchangeError, // { "code": 32016, "message": "margin ratio is lower than 100% after opening position" } + '32017': ExchangeError, // { "code": 32017, "message": "no BBO" } + '32018': ExchangeError, // { "code": 32018, "message": "the order quantity is less than 1, please try again" } + '32019': ExchangeError, // { "code": 32019, "message": "the order price deviates from the price of the previous minute by more than 3%" } + '32020': ExchangeError, // { "code": 32020, "message": "the price is not in the range of the price limit" } + '32021': ExchangeError, // { "code": 32021, "message": "leverage error" } + '32022': ExchangeError, // { "code": 32022, "message": "this function is not supported in your country or region according to the regulations" } + '32023': ExchangeError, // { "code": 32023, "message": "this account has outstanding loan" } + '32024': ExchangeError, // { "code": 32024, "message": "order cannot be placed during delivery" } + '32025': ExchangeError, // { "code": 32025, "message": "order cannot be placed during settlement" } + '32026': ExchangeError, // { "code": 32026, "message": "your account is restricted from opening positions" } + '32029': ExchangeError, // { "code": 32029, "message": "order info does not exist" } + '32028': ExchangeError, // { "code": 32028, "message": "account is suspended and liquidated" } + '32027': ExchangeError, // { "code": 32027, "message": "cancelled over 20 orders" } + '32044': ExchangeError, // { "code": 32044, "message": "The margin ratio after submitting this order is lower than the minimum requirement ({0}) for your tier." } + // token and margin trading + '33001': PermissionDenied, // { "code": 33001, "message": "margin account for this pair is not enabled yet" } + '33002': AccountSuspended, // { "code": 33002, "message": "margin account for this pair is suspended" } + '33003': InsufficientFunds, // { "code": 33003, "message": "no loan balance" } + '33004': ExchangeError, // { "code": 33004, "message": "loan amount cannot be smaller than the minimum limit" } + '33005': ExchangeError, // { "code": 33005, "message": "repayment amount must exceed 0" } + '33006': ExchangeError, // { "code": 33006, "message": "loan order not found" } + '33007': ExchangeError, // { "code": 33007, "message": "status not found" } + '33008': ExchangeError, // { "code": 33008, "message": "loan amount cannot exceed the maximum limit" } + '33009': ExchangeError, // { "code": 33009, "message": "user ID is blank" } + '33010': ExchangeError, // { "code": 33010, "message": "you cannot cancel an order during session 2 of call auction" } + '33011': ExchangeError, // { "code": 33011, "message": "no new market data" } + '33012': ExchangeError, // { "code": 33012, "message": "order cancellation failed" } + '33013': InvalidOrder, // { "code": 33013, "message": "order placement failed" } + '33014': OrderNotFound, // { "code": 33014, "message": "order does not exist" } + '33015': InvalidOrder, // { "code": 33015, "message": "exceeded maximum limit" } + '33016': ExchangeError, // { "code": 33016, "message": "margin trading is not open for this token" } + '33017': InsufficientFunds, // { "code": 33017, "message": "insufficient balance" } + '33018': ExchangeError, // { "code": 33018, "message": "this parameter must be smaller than 1" } + '33020': ExchangeError, // { "code": 33020, "message": "request not supported" } + '33021': BadRequest, // { "code": 33021, "message": "token and the pair do not match" } + '33022': InvalidOrder, // { "code": 33022, "message": "pair and the order do not match" } + '33023': ExchangeError, // { "code": 33023, "message": "you can only place market orders during call auction" } + '33024': InvalidOrder, // { "code": 33024, "message": "trading amount too small" } + '33025': InvalidOrder, // { "code": 33025, "message": "base token amount is blank" } + '33026': ExchangeError, // { "code": 33026, "message": "transaction completed" } + '33027': InvalidOrder, // { "code": 33027, "message": "cancelled order or order cancelling" } + '33028': InvalidOrder, // { "code": 33028, "message": "the decimal places of the trading price exceeded the limit" } + '33029': InvalidOrder, // { "code": 33029, "message": "the decimal places of the trading size exceeded the limit" } + '33034': ExchangeError, // { "code": 33034, "message": "You can only place limit order after Call Auction has started" } + '33059': BadRequest, // { "code": 33059, "message": "client_oid or order_id is required" } + '33060': BadRequest, // { "code": 33060, "message": "Only fill in either parameter client_oid or order_id" } + // account + '34001': PermissionDenied, // { "code": 34001, "message": "withdrawal suspended" } + '34002': InvalidAddress, // { "code": 34002, "message": "please add a withdrawal address" } + '34003': ExchangeError, // { "code": 34003, "message": "sorry, this token cannot be withdrawn to xx at the moment" } + '34004': ExchangeError, // { "code": 34004, "message": "withdrawal fee is smaller than minimum limit" } + '34005': ExchangeError, // { "code": 34005, "message": "withdrawal fee exceeds the maximum limit" } + '34006': ExchangeError, // { "code": 34006, "message": "withdrawal amount is lower than the minimum limit" } + '34007': ExchangeError, // { "code": 34007, "message": "withdrawal amount exceeds the maximum limit" } + '34008': InsufficientFunds, // { "code": 34008, "message": "insufficient balance" } + '34009': ExchangeError, // { "code": 34009, "message": "your withdrawal amount exceeds the daily limit" } + '34010': ExchangeError, // { "code": 34010, "message": "transfer amount must be larger than 0" } + '34011': ExchangeError, // { "code": 34011, "message": "conditions not met" } + '34012': ExchangeError, // { "code": 34012, "message": "the minimum withdrawal amount for NEO is 1, and the amount must be an integer" } + '34013': ExchangeError, // { "code": 34013, "message": "please transfer" } + '34014': ExchangeError, // { "code": 34014, "message": "transfer limited" } + '34015': ExchangeError, // { "code": 34015, "message": "subaccount does not exist" } + '34016': PermissionDenied, // { "code": 34016, "message": "transfer suspended" } + '34017': AccountSuspended, // { "code": 34017, "message": "account suspended" } + '34018': AuthenticationError, // { "code": 34018, "message": "incorrect trades password" } + '34019': PermissionDenied, // { "code": 34019, "message": "please bind your email before withdrawal" } + '34020': PermissionDenied, // { "code": 34020, "message": "please bind your funds password before withdrawal" } + '34021': InvalidAddress, // { "code": 34021, "message": "Not verified address" } + '34022': ExchangeError, // { "code": 34022, "message": "Withdrawals are not available for sub accounts" } + '34023': PermissionDenied, // { "code": 34023, "message": "Please enable futures trading before transferring your funds" } + // swap + '35001': ExchangeError, // { "code": 35001, "message": "Contract does not exist" } + '35002': ExchangeError, // { "code": 35002, "message": "Contract settling" } + '35003': ExchangeError, // { "code": 35003, "message": "Contract paused" } + '35004': ExchangeError, // { "code": 35004, "message": "Contract pending settlement" } + '35005': AuthenticationError, // { "code": 35005, "message": "User does not exist" } + '35008': InvalidOrder, // { "code": 35008, "message": "Risk ratio too high" } + '35010': InvalidOrder, // { "code": 35010, "message": "Position closing too large" } + '35012': InvalidOrder, // { "code": 35012, "message": "Incorrect order size" } + '35014': InvalidOrder, // { "code": 35014, "message": "Order price is not within limit" } + '35015': InvalidOrder, // { "code": 35015, "message": "Invalid leverage level" } + '35017': ExchangeError, // { "code": 35017, "message": "Open orders exist" } + '35019': InvalidOrder, // { "code": 35019, "message": "Order size too large" } + '35020': InvalidOrder, // { "code": 35020, "message": "Order price too high" } + '35021': InvalidOrder, // { "code": 35021, "message": "Order size exceeded current tier limit" } + '35022': ExchangeError, // { "code": 35022, "message": "Contract status error" } + '35024': ExchangeError, // { "code": 35024, "message": "Contract not initialized" } + '35025': InsufficientFunds, // { "code": 35025, "message": "No account balance" } + '35026': ExchangeError, // { "code": 35026, "message": "Contract settings not initialized" } + '35029': OrderNotFound, // { "code": 35029, "message": "Order does not exist" } + '35030': InvalidOrder, // { "code": 35030, "message": "Order size too large" } + '35031': InvalidOrder, // { "code": 35031, "message": "Cancel order size too large" } + '35032': ExchangeError, // { "code": 35032, "message": "Invalid user status" } + '35039': ExchangeError, // { "code": 35039, "message": "Open order quantity exceeds limit" } + '35040': InvalidOrder, // {"error_message":"Invalid order type","result":"true","error_code":"35040","order_id":"-1"} + '35044': ExchangeError, // { "code": 35044, "message": "Invalid order status" } + '35046': InsufficientFunds, // { "code": 35046, "message": "Negative account balance" } + '35047': InsufficientFunds, // { "code": 35047, "message": "Insufficient account balance" } + '35048': ExchangeError, // { "code": 35048, "message": "User contract is frozen and liquidating" } + '35049': InvalidOrder, // { "code": 35049, "message": "Invalid order type" } + '35050': InvalidOrder, // { "code": 35050, "message": "Position settings are blank" } + '35052': InsufficientFunds, // { "code": 35052, "message": "Insufficient cross margin" } + '35053': ExchangeError, // { "code": 35053, "message": "Account risk too high" } + '35055': InsufficientFunds, // { "code": 35055, "message": "Insufficient account balance" } + '35057': ExchangeError, // { "code": 35057, "message": "No last traded price" } + '35058': ExchangeError, // { "code": 35058, "message": "No limit" } + '35059': BadRequest, // { "code": 35059, "message": "client_oid or order_id is required" } + '35060': BadRequest, // { "code": 35060, "message": "Only fill in either parameter client_oid or order_id" } + '35061': BadRequest, // { "code": 35061, "message": "Invalid instrument_id" } + '35062': InvalidOrder, // { "code": 35062, "message": "Invalid match_price" } + '35063': InvalidOrder, // { "code": 35063, "message": "Invalid order_size" } + '35064': InvalidOrder, // { "code": 35064, "message": "Invalid client_oid" } + }, + 'broad': { + }, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + 'fetchMarkets': [ 'spot', 'futures', 'swap' ], + 'defaultType': 'spot', // 'account', 'spot', 'margin', 'futures', 'swap' + 'auth': { + 'time': 'public', + 'currencies': 'private', + 'instruments': 'public', + 'rate': 'public', + 'constituents/{ett}': 'public', + 'define-price/{ett}': 'public', + }, + }, + 'commonCurrencies': { + // OKEX refers to ERC20 version of Aeternity (AEToken) + 'AE': 'AET', // https://github.com/ccxt/ccxt/issues/4981 + 'HOT': 'Hydro Protocol', + 'HSR': 'HC', + 'MAG': 'Maggie', + 'YOYO': 'YOYOW', + 'WIN': 'WinToken', // https://github.com/ccxt/ccxt/issues/5701 + }, + }); + } + + async fetchTime (params = {}) { + const response = await this.generalGetTime (params); + // + // { + // "iso": "2015-01-07T23:47:25.201Z", + // "epoch": 1420674445.201 + // } + // + return this.parse8601 (this.safeString (response, 'iso')); + } + + async fetchMarkets (params = {}) { + const types = this.safeValue (this.options, 'fetchMarkets'); + let result = []; + for (let i = 0; i < types.length; i++) { + const markets = await this.fetchMarketsByType (types[i], params); + result = this.arrayConcat (result, markets); + } + return result; + } + + parseMarkets (markets) { + const result = []; + for (let i = 0; i < markets.length; i++) { + result.push (this.parseMarket (markets[i])); + } + return result; + } + + parseMarket (market) { + // + // spot markets + // + // [ { base_currency: "EOS", + // instrument_id: "EOS-OKB", + // min_size: "0.01", + // product_id: "EOS-OKB", + // quote_currency: "OKB", + // size_increment: "0.000001", + // tick_size: "0.0001" }, + // + // ..., // the spot endpoint also returns ETT instruments + // + // { base_currency: "OK06ETT", + // base_increment: "0.00000001", + // base_min_size: "0.01", + // instrument_id: "OK06ETT-USDT", + // min_size: "0.01", + // product_id: "OK06ETT-USDT", + // quote_currency: "USDT", + // quote_increment: "0.0001", + // size_increment: "0.00000001", + // tick_size: "0.0001" } ] + // + // futures markets + // + // [ { instrument_id: "BTG-USD-190329", + // underlying_index: "BTG", + // quote_currency: "USD", + // tick_size: "0.01", + // contract_val: "10", + // listing: "2018-12-14", + // delivery: "2019-03-29", + // trade_increment: "1" } ] + // + // swap markets + // + // [ { instrument_id: "BTC-USD-SWAP", + // underlying_index: "BTC", + // quote_currency: "USD", + // coin: "BTC", + // contract_val: "100", + // listing: "2018-10-23T20:11:00.443Z", + // delivery: "2018-10-24T20:11:00.443Z", + // size_increment: "4", + // tick_size: "4" } ] + // + const id = this.safeString (market, 'instrument_id'); + let marketType = 'spot'; + let spot = true; + let future = false; + let swap = false; + let baseId = this.safeString (market, 'base_currency'); + const contractVal = this.safeFloat (market, 'contract_val'); + if (contractVal !== undefined) { + marketType = 'swap'; + spot = false; + swap = true; + baseId = this.safeString (market, 'coin'); + const futuresAlias = this.safeString (market, 'alias'); + if (futuresAlias !== undefined) { + swap = false; + future = true; + marketType = 'futures'; + baseId = this.safeString (market, 'underlying_index'); + } + } + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = spot ? (base + '/' + quote) : id; + let amountPrecision = this.safeString (market, 'size_increment'); + if (amountPrecision !== undefined) { + amountPrecision = this.precisionFromString (amountPrecision); + } + let pricePrecision = this.safeString (market, 'tick_size'); + if (pricePrecision !== undefined) { + pricePrecision = this.precisionFromString (pricePrecision); + } + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const minAmount = this.safeFloat2 (market, 'min_size', 'base_min_size'); + let minPrice = this.safeFloat (market, 'tick_size'); + if (precision['price'] !== undefined) { + minPrice = Math.pow (10, -precision['price']); + } + let minCost = undefined; + if (minAmount !== undefined && minPrice !== undefined) { + minCost = minAmount * minPrice; + } + const active = true; + const fees = this.safeValue2 (this.fees, marketType, 'trading', {}); + return this.extend (fees, { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'type': marketType, + 'spot': spot, + 'futures': future, + 'swap': swap, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + }); + } + + async fetchMarketsByType (type, params = {}) { + const method = type + 'GetInstruments'; + const response = await this[method] (params); + // + // spot markets + // + // [ { base_currency: "EOS", + // base_increment: "0.000001", + // base_min_size: "0.01", + // instrument_id: "EOS-OKB", + // min_size: "0.01", + // product_id: "EOS-OKB", + // quote_currency: "OKB", + // quote_increment: "0.0001", + // size_increment: "0.000001", + // tick_size: "0.0001" } ] + // + // futures markets + // + // [ { instrument_id: "BTG-USD-190329", + // underlying_index: "BTG", + // quote_currency: "USD", + // tick_size: "0.01", + // contract_val: "10", + // listing: "2018-12-14", + // delivery: "2019-03-29", + // trade_increment: "1" } ] + // + // swap markets + // + // [ { instrument_id: "BTC-USD-SWAP", + // underlying_index: "BTC", + // quote_currency: "USD", + // coin: "BTC", + // contract_val: "100", + // listing: "2018-10-23T20:11:00.443Z", + // delivery: "2018-10-24T20:11:00.443Z", + // size_increment: "4", + // tick_size: "4" } ] + // + return this.parseMarkets (response); + } + + async fetchCurrencies (params = {}) { + // has['fetchCurrencies'] is currently set to false + // despite that their docs say these endpoints are public: + // https://www.okex.com/api/account/v3/withdrawal/fee + // https://www.okex.com/api/account/v3/currencies + // it will still reply with { "code":30001, "message": "OK-ACCESS-KEY header is required" } + // if you attempt to access it without authentication + const response = await this.accountGetCurrencies (params); + // + // [ + // { + // name: '', + // currency: 'BTC', + // can_withdraw: '1', + // can_deposit: '1', + // min_withdrawal: '0.0100000000000000' + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'currency'); + const code = this.safeCurrencyCode (id); + const precision = 8; // default precision, todo: fix "magic constants" + const name = this.safeString (currency, 'name'); + const canDeposit = this.safeInteger (currency, 'can_deposit'); + const canWithdraw = this.safeInteger (currency, 'can_withdraw'); + const active = canDeposit && canWithdraw; + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': name, + 'active': active, + 'fee': undefined, // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'withdraw': { + 'min': this.safeFloat (currency, 'min_withdrawal'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = market['type'] + 'GetInstrumentsInstrumentId'; + method += (market['type'] === 'swap') ? 'Depth' : 'Book'; + const request = { + 'instrument_id': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; // max 200 + } + const response = await this[method] (this.extend (request, params)); + // + // { asks: [ ["0.02685268", "0.242571", "1"], + // ["0.02685493", "0.164085", "1"], + // ... + // ["0.02779", "1.039", "1"], + // ["0.027813", "0.0876", "1"] ], + // bids: [ ["0.02684052", "10.371849", "1"], + // ["0.02684051", "3.707", "4"], + // ... + // ["0.02634963", "0.132934", "1"], + // ["0.02634962", "0.264838", "2"] ], + // timestamp: "2018-12-17T20:24:16.159Z" } + // + const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + // + // { best_ask: "0.02665472", + // best_bid: "0.02665221", + // instrument_id: "ETH-BTC", + // product_id: "ETH-BTC", + // last: "0.02665472", + // ask: "0.02665472", // missing in the docs + // bid: "0.02665221", // not mentioned in the docs + // open_24h: "0.02645482", + // high_24h: "0.02714633", + // low_24h: "0.02614109", + // base_volume_24h: "572298.901923", + // timestamp: "2018-12-17T21:20:07.856Z", + // quote_volume_24h: "15094.86831261" } + // + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + let symbol = undefined; + const marketId = this.safeString (ticker, 'instrument_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else if (marketId !== undefined) { + const parts = marketId.split ('-'); + const numParts = parts.length; + if (numParts === 2) { + const [ baseId, quoteId ] = parts; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else { + symbol = marketId; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open_24h'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high_24h'), + 'low': this.safeFloat (ticker, 'low_24h'), + 'bid': this.safeFloat (ticker, 'best_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'best_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'base_volume_24h'), + 'quoteVolume': this.safeFloat (ticker, 'quote_volume_24h'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['type'] + 'GetInstrumentsInstrumentIdTicker'; + const request = { + 'instrument_id': market['id'], + }; + const response = await this[method] (this.extend (request, params)); + // + // { best_ask: "0.02665472", + // best_bid: "0.02665221", + // instrument_id: "ETH-BTC", + // product_id: "ETH-BTC", + // last: "0.02665472", + // ask: "0.02665472", + // bid: "0.02665221", + // open_24h: "0.02645482", + // high_24h: "0.02714633", + // low_24h: "0.02614109", + // base_volume_24h: "572298.901923", + // timestamp: "2018-12-17T21:20:07.856Z", + // quote_volume_24h: "15094.86831261" } + // + return this.parseTicker (response); + } + + async fetchTickersByType (type, symbols = undefined, params = {}) { + await this.loadMarkets (); + const method = type + 'GetInstrumentsTicker'; + const response = await this[method] (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTickers (symbols = undefined, params = {}) { + const defaultType = this.safeString2 (this.options, 'fetchTickers', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + return await this.fetchTickersByType (type, symbols, this.omit (params, 'type')); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // spot trades + // + // { + // time: "2018-12-17T23:31:08.268Z", + // timestamp: "2018-12-17T23:31:08.268Z", + // trade_id: "409687906", + // price: "0.02677805", + // size: "0.923467", + // side: "sell" + // } + // + // futures trades, swap trades + // + // { + // trade_id: "1989230840021013", + // side: "buy", + // price: "92.42", + // qty: "184", // missing in swap markets + // size: "5", // missing in futures markets + // timestamp: "2018-12-17T23:26:04.613Z" + // } + // + // fetchOrderTrades (private) + // + // spot trades, margin trades + // + // { + // "created_at":"2019-03-15T02:52:56.000Z", + // "exec_type":"T", // whether the order is taker or maker + // "fee":"0.00000082", + // "instrument_id":"BTC-USDT", + // "ledger_id":"3963052721", + // "liquidity":"T", // whether the order is taker or maker + // "order_id":"2482659399697408", + // "price":"3888.6", + // "product_id":"BTC-USDT", + // "side":"buy", + // "size":"0.00055306", + // "timestamp":"2019-03-15T02:52:56.000Z" + // }, + // + // futures trades, swap trades + // + // { + // "trade_id":"197429674631450625", + // "instrument_id":"EOS-USD-SWAP", + // "order_id":"6a-7-54d663a28-0", + // "price":"3.633", + // "order_qty":"1.0000", + // "fee":"-0.000551", + // "created_at":"2019-03-21T04:41:58.0Z", // missing in swap trades + // "timestamp":"2019-03-25T05:56:31.287Z", // missing in futures trades + // "exec_type":"M", // whether the order is taker or maker + // "side":"short", // "buy" in futures trades + // } + // + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString2 (trade, 'timestamp', 'created_at')); + const price = this.safeFloat (trade, 'price'); + let amount = this.safeFloat2 (trade, 'size', 'qty'); + amount = this.safeFloat (trade, 'order_qty', amount); + let takerOrMaker = this.safeString2 (trade, 'exec_type', 'liquidity'); + if (takerOrMaker === 'M') { + takerOrMaker = 'maker'; + } else if (takerOrMaker === 'T') { + takerOrMaker = 'taker'; + } + const side = this.safeString (trade, 'side'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + const feeCost = this.safeFloat (trade, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = (side === 'buy') ? market['base'] : market['quote']; + } + fee = { + // fee is either a positive number (invitation rebate) + // or a negative number (transaction fee deduction) + // therefore we need to invert the fee + // more about it https://github.com/ccxt/ccxt/issues/5909 + 'cost': -feeCost, + 'currency': feeCurrency, + }; + } + const orderId = this.safeString (trade, 'order_id'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': this.safeString2 (trade, 'trade_id', 'ledger_id'), + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['type'] + 'GetInstrumentsInstrumentIdTrades'; + if ((limit === undefined) || (limit > 100)) { + limit = 100; // maximum = default = 100 + } + const request = { + 'instrument_id': market['id'], + 'limit': limit, + // from: 'id', + // to: 'id', + }; + const response = await this[method] (this.extend (request, params)); + // + // spot markets + // + // [ + // { + // time: "2018-12-17T23:31:08.268Z", + // timestamp: "2018-12-17T23:31:08.268Z", + // trade_id: "409687906", + // price: "0.02677805", + // size: "0.923467", + // side: "sell" + // } + // ] + // + // futures markets, swap markets + // + // [ + // { + // trade_id: "1989230840021013", + // side: "buy", + // price: "92.42", + // qty: "184", // missing in swap markets + // size: "5", // missing in futures markets + // timestamp: "2018-12-17T23:26:04.613Z" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // spot markets + // + // { + // close: "0.02684545", + // high: "0.02685084", + // low: "0.02683312", + // open: "0.02683894", + // time: "2018-12-17T20:28:00.000Z", + // volume: "101.457222" + // } + // + // futures markets + // + // [ + // 1545072720000, + // 0.3159, + // 0.3161, + // 0.3144, + // 0.3149, + // 22886, + // 725179.26172331, + // ] + // + if (Array.isArray (ohlcv)) { + const numElements = ohlcv.length; + const volumeIndex = (numElements > 6) ? 6 : 5; + let timestamp = ohlcv[0]; + if (typeof timestamp === 'string') { + timestamp = this.parse8601 (timestamp); + } + return [ + timestamp, // timestamp + parseFloat (ohlcv[1]), // Open + parseFloat (ohlcv[2]), // High + parseFloat (ohlcv[3]), // Low + parseFloat (ohlcv[4]), // Close + // parseFloat (ohlcv[5]), // Quote Volume + // parseFloat (ohlcv[6]), // Base Volume + parseFloat (ohlcv[volumeIndex]), // Volume, okex will return base volume in the 7th element for future markets + ]; + } else { + return [ + this.parse8601 (this.safeString (ohlcv, 'time')), + this.safeFloat (ohlcv, 'open'), // Open + this.safeFloat (ohlcv, 'high'), // High + this.safeFloat (ohlcv, 'low'), // Low + this.safeFloat (ohlcv, 'close'), // Close + this.safeFloat (ohlcv, 'volume'), // Base Volume + ]; + } + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['type'] + 'GetInstrumentsInstrumentIdCandles'; + const request = { + 'instrument_id': market['id'], + 'granularity': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['start'] = this.iso8601 (since); + } + const response = await this[method] (this.extend (request, params)); + // + // spot markets + // + // [ { close: "0.02683401", + // high: "0.02683401", + // low: "0.02683401", + // open: "0.02683401", + // time: "2018-12-17T23:47:00.000Z", + // volume: "0" }, + // ... + // { close: "0.02684545", + // high: "0.02685084", + // low: "0.02683312", + // open: "0.02683894", + // time: "2018-12-17T20:28:00.000Z", + // volume: "101.457222" } ] + // + // futures + // + // [ [ 1545090660000, + // 0.3171, + // 0.3174, + // 0.3171, + // 0.3173, + // 1648, + // 51930.38579450868 ], + // ... + // [ 1545072720000, + // 0.3159, + // 0.3161, + // 0.3144, + // 0.3149, + // 22886, + // 725179.26172331 ] ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseAccountBalance (response) { + // + // account + // + // [ + // { + // balance: 0, + // available: 0, + // currency: "BTC", + // hold: 0 + // }, + // { + // balance: 0, + // available: 0, + // currency: "ETH", + // hold: 0 + // } + // ] + // + // spot + // + // [ + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "BTC", + // balance: "0.0000000497717339", + // available: "0.0000000497717339", + // holds: "0" + // }, + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "ICN", + // balance: "0.00000000925", + // available: "0.00000000925", + // holds: "0" + // } + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'hold'); + account['free'] = this.safeFloat (balance, 'available'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseMarginBalance (response) { + // + // [ + // { + // "currency:BTC": { + // "available":"0", + // "balance":"0", + // "borrowed":"0", + // "can_withdraw":"0", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "currency:USDT": { + // "available":"100", + // "balance":"100", + // "borrowed":"0", + // "can_withdraw":"100", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "instrument_id":"BTC-USDT", + // "liquidation_price":"0", + // "product_id":"BTC-USDT", + // "risk_rate":"" + // }, + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const marketId = this.safeString (balance, 'instrument_id'); + const market = this.safeValue (this.markets_by_id, marketId); + let symbol = undefined; + if (market === undefined) { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else { + symbol = market['symbol']; + } + const omittedBalance = this.omit (balance, [ + 'instrument_id', + 'liquidation_price', + 'product_id', + 'risk_rate', + 'margin_ratio', + 'maint_margin_ratio', + 'tiers', + ]); + const keys = Object.keys (omittedBalance); + const accounts = {}; + for (let k = 0; k < keys.length; k++) { + const key = keys[k]; + const marketBalance = balance[key]; + if (key.indexOf (':') >= 0) { + const parts = key.split (':'); + const currencyId = parts[1]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (marketBalance, 'balance'); + account['used'] = this.safeFloat (marketBalance, 'hold'); + account['free'] = this.safeFloat (marketBalance, 'available'); + accounts[code] = account; + } else { + throw new NotSupported (this.id + ' margin balance response format has changed!'); + } + } + result[symbol] = this.parseBalance (accounts); + } + return result; + } + + parseFuturesBalance (response) { + // + // { + // "info":{ + // "eos":{ + // "auto_margin":"0", + // "contracts": [ + // { + // "available_qty":"40.37069445", + // "fixed_balance":"0", + // "instrument_id":"EOS-USD-190329", + // "margin_for_unfilled":"0", + // "margin_frozen":"0", + // "realized_pnl":"0", + // "unrealized_pnl":"0" + // }, + // { + // "available_qty":"40.37069445", + // "fixed_balance":"14.54895721", + // "instrument_id":"EOS-USD-190628", + // "margin_for_unfilled":"0", + // "margin_frozen":"10.64042157", + // "realized_pnl":"-3.90853564", + // "unrealized_pnl":"-0.259" + // }, + // ], + // "equity":"50.75220665", + // "margin_mode":"fixed", + // "total_avail_balance":"40.37069445" + // }, + // } + // } + // + // their root field name is "info", so our info will contain their info + const result = { 'info': response }; + const info = this.safeValue (response, 'info', {}); + const ids = Object.keys (info); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + const balance = this.safeValue (info, id, {}); + const account = this.account (); + // it may be incorrect to use total, free and used for swap accounts + account['total'] = this.safeFloat (balance, 'equity'); + account['free'] = this.safeFloat (balance, 'total_avail_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseSwapBalance (response) { + // + // { + // "info": [ + // { + // "equity":"3.0139", + // "fixed_balance":"0.0000", + // "instrument_id":"EOS-USD-SWAP", + // "margin":"0.5523", + // "margin_frozen":"0.0000", + // "margin_mode":"crossed", + // "margin_ratio":"1.0913", + // "realized_pnl":"-0.0006", + // "timestamp":"2019-03-25T03:46:10.336Z", + // "total_avail_balance":"3.0000", + // "unrealized_pnl":"0.0145" + // } + // ] + // } + // + // their root field name is "info", so our info will contain their info + const result = { 'info': response }; + const info = this.safeValue (response, 'info', []); + for (let i = 0; i < info.length; i++) { + const balance = info[i]; + const marketId = this.safeString (balance, 'instrument_id'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + symbol = this.markets_by_id[marketId]['symbol']; + } + const account = this.account (); + // it may be incorrect to use total, free and used for swap accounts + account['total'] = this.safeFloat (balance, 'equity'); + account['free'] = this.safeFloat (balance, 'total_avail_balance'); + result[symbol] = account; + } + return this.parseBalance (result); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + if (type === undefined) { + throw new ArgumentsRequired (this.id + " fetchBalance requires a type parameter (one of 'account', 'spot', 'margin', 'futures', 'swap')"); + } + const suffix = (type === 'account') ? 'Wallet' : 'Accounts'; + const method = type + 'Get' + suffix; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + // + // account + // + // [ + // { + // balance: 0, + // available: 0, + // currency: "BTC", + // hold: 0 + // }, + // { + // balance: 0, + // available: 0, + // currency: "ETH", + // hold: 0 + // } + // ] + // + // spot + // + // [ + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "BTC", + // balance: "0.0000000497717339", + // available: "0.0000000497717339", + // holds: "0" + // }, + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "ICN", + // balance: "0.00000000925", + // available: "0.00000000925", + // holds: "0" + // } + // ] + // + // margin + // + // [ + // { + // "currency:BTC": { + // "available":"0", + // "balance":"0", + // "borrowed":"0", + // "can_withdraw":"0", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "currency:USDT": { + // "available":"100", + // "balance":"100", + // "borrowed":"0", + // "can_withdraw":"100", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "instrument_id":"BTC-USDT", + // "liquidation_price":"0", + // "product_id":"BTC-USDT", + // "risk_rate":"" + // }, + // ] + // + // futures + // + // { + // "info":{ + // "eos":{ + // "auto_margin":"0", + // "contracts": [ + // { + // "available_qty":"40.37069445", + // "fixed_balance":"0", + // "instrument_id":"EOS-USD-190329", + // "margin_for_unfilled":"0", + // "margin_frozen":"0", + // "realized_pnl":"0", + // "unrealized_pnl":"0" + // }, + // { + // "available_qty":"40.37069445", + // "fixed_balance":"14.54895721", + // "instrument_id":"EOS-USD-190628", + // "margin_for_unfilled":"0", + // "margin_frozen":"10.64042157", + // "realized_pnl":"-3.90853564", + // "unrealized_pnl":"-0.259" + // }, + // ], + // "equity":"50.75220665", + // "margin_mode":"fixed", + // "total_avail_balance":"40.37069445" + // }, + // } + // } + // + // swap + // + // { + // "info": [ + // { + // "equity":"3.0139", + // "fixed_balance":"0.0000", + // "instrument_id":"EOS-USD-SWAP", + // "margin":"0.5523", + // "margin_frozen":"0.0000", + // "margin_mode":"crossed", + // "margin_ratio":"1.0913", + // "realized_pnl":"-0.0006", + // "timestamp":"2019-03-25T03:46:10.336Z", + // "total_avail_balance":"3.0000", + // "unrealized_pnl":"0.0145" + // } + // ] + // } + // + if ((type === 'account') || (type === 'spot')) { + return this.parseAccountBalance (response); + } else if (type === 'margin') { + return this.parseMarginBalance (response); + } else if (type === 'futures') { + return this.parseFuturesBalance (response); + } else if (type === 'swap') { + return this.parseSwapBalance (response); + } + throw new NotSupported (this.id + " fetchBalance does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')"); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let request = { + 'instrument_id': market['id'], + // 'client_oid': 'abcdef1234567890', // [a-z0-9]{1,32} + // 'order_type': '0', // 0: Normal limit order (Unfilled and 0 represent normal limit order) 1: Post only 2: Fill Or Kill 3: Immediatel Or Cancel + }; + let method = undefined; + if (market['futures'] || market['swap']) { + const size = market['futures'] ? this.numberToString (amount) : this.amountToPrecision (symbol, amount); + request = this.extend (request, { + 'type': type, // 1:open long 2:open short 3:close long 4:close short for futures + 'size': size, + 'price': this.priceToPrecision (symbol, price), + // 'match_price': '0', // Order at best counter party price? (0:no 1:yes). The default is 0. If it is set as 1, the price parameter will be ignored. When posting orders at best bid price, order_type can only be 0 (regular order). + }); + if (market['futures']) { + request['leverage'] = '10'; // or '20' + } + method = market['type'] + 'PostOrder'; + } else { + const marginTrading = this.safeString (params, 'margin_trading', '1'); // 1 = spot, 2 = margin + request = this.extend (request, { + 'side': side, + 'type': type, // limit/market + 'margin_trading': marginTrading, // 1 = spot, 2 = margin + }); + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + request['size'] = this.amountToPrecision (symbol, amount); + } else if (type === 'market') { + // for market buy it requires the amount of quote currency to spend + if (side === 'buy') { + let notional = this.safeFloat (params, 'notional'); + const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true); + if (createMarketBuyOrderRequiresPrice) { + if (price !== undefined) { + if (notional === undefined) { + notional = amount * price; + } + } else if (notional === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'notional' extra parameter (the exchange-specific behaviour)"); + } + } + request['notional'] = this.costToPrecision (symbol, notional); + } else { + request['size'] = this.amountToPrecision (symbol, amount); + } + } + method = (marginTrading === '2') ? 'marginPostOrders' : 'spotPostOrders'; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "client_oid":"oktspot79", + // "error_code":"", + // "error_message":"", + // "order_id":"2510789768709120", + // "result":true + // } + // + const timestamp = this.milliseconds (); + const id = this.safeString (response, 'order_id'); + return { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const type = market['type']; + let method = type + 'PostCancelOrder'; + const request = { + 'instrument_id': market['id'], + }; + if (market['futures'] || market['swap']) { + method += 'InstrumentId'; + } else { + method += 's'; + } + const clientOid = this.safeString (params, 'client_oid'); + if (clientOid !== undefined) { + method += 'ClientOid'; + request['client_oid'] = clientOid; + } else { + method += 'OrderId'; + request['order_id'] = id; + } + const query = this.omit (params, 'type'); + const response = await this[method] (this.extend (request, query)); + const result = ('result' in response) ? response : this.safeValue (response, market['id'], {}); + // + // spot, margin + // + // { + // "btc-usdt": [ + // { + // "result":true, + // "client_oid":"a123", + // "order_id": "2510832677225473" + // } + // ] + // } + // + // futures, swap + // + // { + // "result": true, + // "client_oid": "oktfuture10", // missing if requested by order_id + // "order_id": "2517535534836736", + // "instrument_id": "EOS-USD-190628" + // } + // + return this.parseOrder (result, market); + } + + parseOrderStatus (status) { + const statuses = { + '-2': 'failed', + '-1': 'canceled', + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrderSide (side) { + const sides = { + '1': 'buy', // open long + '2': 'sell', // open short + '3': 'sell', // close long + '4': 'buy', // close short + }; + return this.safeString (sides, side, side); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "client_oid":"oktspot79", + // "error_code":"", + // "error_message":"", + // "order_id":"2510789768709120", + // "result":true + // } + // + // cancelOrder + // + // { + // "result": true, + // "client_oid": "oktfuture10", // missing if requested by order_id + // "order_id": "2517535534836736", + // // instrument_id is missing for spot/margin orders + // // available in futures and swap orders only + // "instrument_id": "EOS-USD-190628", + // } + // + // fetchOrder, fetchOrdersByState, fetchOpenOrders, fetchClosedOrders + // + // // spot and margin orders + // + // { + // "client_oid":"oktspot76", + // "created_at":"2019-03-18T07:26:49.000Z", + // "filled_notional":"3.9734", + // "filled_size":"0.001", // filled_qty in futures and swap orders + // "funds":"", // this is most likely the same as notional + // "instrument_id":"BTC-USDT", + // "notional":"", + // "order_id":"2500723297813504", + // "order_type":"0", + // "price":"4013", + // "product_id":"BTC-USDT", // missing in futures and swap orders + // "side":"buy", + // "size":"0.001", + // "status":"filled", + // "state": "2", + // "timestamp":"2019-03-18T07:26:49.000Z", + // "type":"limit" + // } + // + // // futures and swap orders + // + // { + // "instrument_id":"EOS-USD-190628", + // "size":"10", + // "timestamp":"2019-03-20T10:04:55.000Z", + // "filled_qty":"10", // filled_size in spot and margin orders + // "fee":"-0.00841043", + // "order_id":"2512669605501952", + // "price":"3.668", + // "price_avg":"3.567", // missing in spot and margin orders + // "status":"2", + // "state": "2", + // "type":"4", + // "contract_val":"10", + // "leverage":"10", // missing in swap, spot and margin orders + // "client_oid":"", + // "pnl":"1.09510794", // missing in swap, spo and margin orders + // "order_type":"0" + // } + // + const id = this.safeString (order, 'order_id'); + const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); + let side = this.safeString (order, 'side'); + let type = this.safeString (order, 'type'); + if ((side !== 'buy') && (side !== 'sell')) { + side = this.parseOrderSide (type); + } + if ((type !== 'limit') && (type !== 'market')) { + if ('pnl' in order) { + type = 'futures'; + } else { + type = 'swap'; + } + } + let symbol = undefined; + const marketId = this.safeString (order, 'instrument_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + if (market !== undefined) { + if (symbol === undefined) { + symbol = market['symbol']; + } + } + let amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat2 (order, 'filled_size', 'filled_qty'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + amount = Math.max (amount, filled); + remaining = Math.max (0, amount - filled); + } + } + if (type === 'market') { + remaining = 0; + } + let cost = this.safeFloat2 (order, 'filled_notional', 'funds'); + const price = this.safeFloat (order, 'price'); + let average = this.safeFloat (order, 'price_avg'); + if (cost === undefined) { + if (filled !== undefined && average !== undefined) { + cost = average * filled; + } + } else { + if ((average === undefined) && (filled !== undefined) && (filled > 0)) { + average = cost / filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const feeCost = this.safeFloat (order, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + const feeCurrency = undefined; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const defaultType = this.safeString2 (this.options, 'fetchOrder', 'defaultType', market['type']); + const type = this.safeString (params, 'type', defaultType); + if (type === undefined) { + throw new ArgumentsRequired (this.id + " fetchOrder requires a type parameter (one of 'spot', 'margin', 'futures', 'swap')."); + } + const instrumentId = (market['futures'] || market['swap']) ? 'InstrumentId' : ''; + let method = type + 'GetOrders' + instrumentId; + const request = { + 'instrument_id': market['id'], + // 'client_oid': 'abcdef12345', // optional, [a-z0-9]{1,32} + // 'order_id': id, + }; + const clientOid = this.safeString (params, 'client_oid'); + if (clientOid !== undefined) { + method += 'ClientOid'; + request['client_oid'] = clientOid; + } else { + method += 'OrderId'; + request['order_id'] = id; + } + const query = this.omit (params, 'type'); + const response = await this[method] (this.extend (request, query)); + // + // spot, margin + // + // { + // "client_oid":"oktspot70", + // "created_at":"2019-03-15T02:52:56.000Z", + // "filled_notional":"3.8886", + // "filled_size":"0.001", + // "funds":"", + // "instrument_id":"BTC-USDT", + // "notional":"", + // "order_id":"2482659399697408", + // "order_type":"0", + // "price":"3927.3", + // "product_id":"BTC-USDT", + // "side":"buy", + // "size":"0.001", + // "status":"filled", + // "state": "2", + // "timestamp":"2019-03-15T02:52:56.000Z", + // "type":"limit" + // } + // + // futures, swap + // + // { + // "instrument_id":"EOS-USD-190628", + // "size":"10", + // "timestamp":"2019-03-20T02:46:38.000Z", + // "filled_qty":"10", + // "fee":"-0.0080819", + // "order_id":"2510946213248000", + // "price":"3.712", + // "price_avg":"3.712", + // "status":"2", + // "state": "2", + // "type":"2", + // "contract_val":"10", + // "leverage":"10", + // "client_oid":"", // missing in swap orders + // "pnl":"0", // missing in swap orders + // "order_type":"0" + // } + // + return this.parseOrder (response); + } + + async fetchOrdersByState (state, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrdersByState requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const type = market['type']; + const request = { + 'instrument_id': market['id'], + // '-2': failed, + // '-1': cancelled, + // '0': open , + // '1': partially filled, + // '2': fully filled, + // '3': submitting, + // '4': cancelling, + // '6': incomplete(open+partially filled), + // '7': complete(cancelled+fully filled), + 'state': state, + }; + let method = type + 'GetOrders'; + if (market['futures'] || market['swap']) { + method += 'InstrumentId'; + } + const query = this.omit (params, 'type'); + const response = await this[method] (this.extend (request, query)); + // + // spot, margin + // + // [ + // // in fact, this documented API response does not correspond + // // to their actual API response for spot markets + // // OKEX v3 API returns a plain array of orders (see below) + // [ + // { + // "client_oid":"oktspot76", + // "created_at":"2019-03-18T07:26:49.000Z", + // "filled_notional":"3.9734", + // "filled_size":"0.001", + // "funds":"", + // "instrument_id":"BTC-USDT", + // "notional":"", + // "order_id":"2500723297813504", + // "order_type":"0", + // "price":"4013", + // "product_id":"BTC-USDT", + // "side":"buy", + // "size":"0.001", + // "status":"filled", + // "state": "2", + // "timestamp":"2019-03-18T07:26:49.000Z", + // "type":"limit" + // }, + // ], + // { + // "before":"2500723297813504", + // "after":"2500650881647616" + // } + // ] + // + // futures, swap + // + // { + // "result":true, // missing in swap orders + // "order_info": [ + // { + // "instrument_id":"EOS-USD-190628", + // "size":"10", + // "timestamp":"2019-03-20T10:04:55.000Z", + // "filled_qty":"10", + // "fee":"-0.00841043", + // "order_id":"2512669605501952", + // "price":"3.668", + // "price_avg":"3.567", + // "status":"2", + // "state": "2", + // "type":"4", + // "contract_val":"10", + // "leverage":"10", // missing in swap orders + // "client_oid":"", + // "pnl":"1.09510794", // missing in swap orders + // "order_type":"0" + // }, + // ] + // } + // + let orders = undefined; + if (market['type'] === 'swap' || market['type'] === 'futures') { + orders = this.safeValue (response, 'order_info', []); + } else { + orders = response; + const responseLength = response.length; + if (responseLength < 1) { + return []; + } + // in fact, this documented API response does not correspond + // to their actual API response for spot markets + // OKEX v3 API returns a plain array of orders + if (responseLength > 1) { + const before = this.safeValue (response[1], 'before'); + if (before !== undefined) { + orders = response[0]; + } + } + } + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // '-2': failed, + // '-1': cancelled, + // '0': open , + // '1': partially filled, + // '2': fully filled, + // '3': submitting, + // '4': cancelling, + // '6': incomplete(open+partially filled), + // '7': complete(cancelled+fully filled), + return await this.fetchOrdersByState ('6', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // '-2': failed, + // '-1': cancelled, + // '0': open , + // '1': partially filled, + // '2': fully filled, + // '3': submitting, + // '4': cancelling, + // '6': incomplete(open+partially filled), + // '7': complete(cancelled+fully filled), + return await this.fetchOrdersByState ('7', symbol, since, limit, params); + } + + parseDepositAddresses (addresses) { + const result = []; + for (let i = 0; i < addresses.length; i++) { + result.push (this.parseDepositAddress (addresses[i])); + } + return result; + } + + parseDepositAddress (depositAddress, currency = undefined) { + // + // { + // address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa', + // currency: 'eth' + // tag: 'abcde12345', // will be missing if the token does not require a deposit tag + // payment_id: 'abcde12345', // will not be returned if the token does not require a payment_id + // // can_deposit: 1, // 0 or 1, documented but missing + // // can_withdraw: 1, // 0 or 1, documented but missing + // } + // + const address = this.safeString (depositAddress, 'address'); + let tag = this.safeString2 (depositAddress, 'tag', 'payment_id'); + tag = this.safeString (depositAddress, 'memo', tag); + const currencyId = this.safeString (depositAddress, 'currency'); + const code = this.safeCurrencyCode (currencyId); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': depositAddress, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.accountGetDepositAddress (this.extend (request, params)); + // + // [ + // { + // address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa', + // currency: 'eth' + // } + // ] + // + const addresses = this.parseDepositAddresses (response); + const numAddresses = addresses.length; + if (numAddresses < 1) { + throw new InvalidAddress (this.id + ' fetchDepositAddress cannot return nonexistent addresses, you should create withdrawal addresses with the exchange website first'); + } + return addresses[0]; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (tag) { + address = address + ':' + tag; + } + const fee = this.safeString (params, 'fee'); + if (fee === undefined) { + throw new ExchangeError (this.id + " withdraw() requires a `fee` string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set '0'. Withdrawing to external digital asset address requires network transaction fee."); + } + const request = { + 'currency': currency['id'], + 'to_address': address, + 'destination': '4', // 2 = OKCoin International, 3 = OKEx 4 = others + 'amount': this.numberToString (amount), + 'fee': fee, // String. Network transaction fee ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set as 0. Withdrawal to external digital asset address requires network transaction fee. + }; + if ('password' in params) { + request['trade_pwd'] = params['password']; + } else if ('trade_pwd' in params) { + request['trade_pwd'] = params['trade_pwd']; + } else if (this.password) { + request['trade_pwd'] = this.password; + } + const query = this.omit (params, [ 'fee', 'password', 'trade_pwd' ]); + if (!('trade_pwd' in request)) { + throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a password / trade_pwd parameter'); + } + const response = await this.accountPostWithdrawal (this.extend (request, query)); + // + // { + // "amount":"0.1", + // "withdrawal_id":"67485", + // "currency":"btc", + // "result":true + // } + // + return { + 'info': response, + 'id': this.safeString (response, 'withdrawal_id'), + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'accountGetDepositHistory'; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['code'] = currency['code']; + method += 'Currency'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'accountGetWithdrawalHistory'; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['code'] = currency['code']; + method += 'Currency'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit, params); + } + + parseTransactionStatus (status) { + // + // deposit statuses + // + // { + // '0': 'waiting for confirmation', + // '1': 'confirmation account', + // '2': 'recharge success' + // } + // + // withdrawal statues + // + // { + // '-3': 'pending cancel', + // '-2': 'cancelled', + // '-1': 'failed', + // '0': 'pending', + // '1': 'sending', + // '2': 'sent', + // '3': 'email confirmation', + // '4': 'manual confirmation', + // '5': 'awaiting identity confirmation' + // } + // + const statuses = { + '-3': 'pending', + '-2': 'pending', + '-1': 'failed', + '0': 'pending', + '1': 'pending', + '2': 'ok', + '3': 'pending', + '4': 'pending', + '5': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // withdraw + // + // { + // "amount":"0.1", + // "withdrawal_id":"67485", + // "currency":"btc", + // "result":true + // } + // + // fetchWithdrawals + // + // { + // amount: "4.72100000", + // withdrawal_id: "1729116", + // fee: "0.01000000eth", + // txid: "0xf653125bbf090bcfe4b5e8e7b8f586a9d87aa7de94598702758c0802b…", + // currency: "ETH", + // from: "7147338839", + // to: "0x26a3CB49578F07000575405a57888681249c35Fd", + // timestamp: "2018-08-17T07:03:42.000Z", + // status: "2" + // } + // + // fetchDeposits + // + // { + // amount: "0.47847546", + // txid: "1723573_3_0_0_WALLET", + // currency: "BTC", + // to: "", + // timestamp: "2018-08-16T03:41:10.000Z", + // status: "2" + // } + // + let type = undefined; + let id = undefined; + let address = undefined; + const withdrawalId = this.safeString (transaction, 'withdrawal_id'); + const addressFrom = this.safeString (transaction, 'from'); + const addressTo = this.safeString (transaction, 'to'); + if (withdrawalId !== undefined) { + type = 'withdrawal'; + id = withdrawalId; + address = addressTo; + } else { + // the payment_id will appear on new deposits but appears to be removed from the response after 2 months + id = this.safeString (transaction, 'payment_id'); + type = 'deposit'; + address = addressTo; + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const txid = this.safeString (transaction, 'txid'); + const timestamp = this.parse8601 (this.safeString (transaction, 'timestamp')); + let feeCost = undefined; + if (type === 'deposit') { + feeCost = 0; + } else { + if (currencyId !== undefined) { + const feeWithCurrencyId = this.safeString (transaction, 'fee'); + if (feeWithCurrencyId !== undefined) { + // https://github.com/ccxt/ccxt/pull/5748 + const lowercaseCurrencyId = currencyId.toLowerCase (); + const feeWithoutCurrencyId = feeWithCurrencyId.replace (lowercaseCurrencyId, ''); + feeCost = parseFloat (feeWithoutCurrencyId); + } + } + } + // todo parse tags + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'addressFrom': addressFrom, + 'addressTo': addressTo, + 'address': address, + 'tagFrom': undefined, + 'tagTo': undefined, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + // okex actually returns ledger entries instead of fills here, so each fill in the order + // is represented by two trades with opposite buy/sell sides, not one :\ + // this aspect renders the 'fills' endpoint unusable for fetchOrderTrades + // until either OKEX fixes the API or we workaround this on our side somehow + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrderTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + if ((limit === undefined) || (limit > 100)) { + limit = 100; + } + const request = { + 'instrument_id': market['id'], + 'order_id': id, + // from: '1', // return the page after the specified page number + // to: '1', // return the page before the specified page number + 'limit': limit, // optional, number of results per request, default = maximum = 100 + }; + const defaultType = this.safeString2 (this.options, 'fetchMyTrades', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const method = type + 'GetFills'; + const response = await this[method] (this.extend (request, query)); + // + // spot trades, margin trades + // + // [ + // { + // "created_at":"2019-09-20T07:15:24.000Z", + // "exec_type":"T", + // "fee":"0", + // "instrument_id":"ETH-USDT", + // "ledger_id":"7173486113", + // "liquidity":"T", + // "order_id":"3553868136523776", + // "price":"217.59", + // "product_id":"ETH-USDT", + // "side":"sell", + // "size":"0.04619899", + // "timestamp":"2019-09-20T07:15:24.000Z" + // } + // ] + // + // futures trades, swap trades + // + // [ + // { + // "trade_id":"197429674631450625", + // "instrument_id":"EOS-USD-SWAP", + // "order_id":"6a-7-54d663a28-0", + // "price":"3.633", + // "order_qty":"1.0000", + // "fee":"-0.000551", + // "created_at":"2019-03-21T04:41:58.0Z", // missing in swap trades + // "timestamp":"2019-03-25T05:56:31.287Z", // missing in futures trades + // "exec_type":"M", // whether the order is taker or maker + // "side":"short", // "buy" in futures trades + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchLedger', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const suffix = (type === 'account') ? '' : 'Accounts'; + let argument = ''; + const request = { + // 'from': 'id', + // 'to': 'id', + }; + if (limit !== undefined) { + request['limit'] = limit; + } + let currency = undefined; + if ((type === 'spot') || (type === 'futures')) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + " fetchLedger requires a currency code argument for '" + type + "' markets"); + } + argument = 'Currency'; + currency = this.currency (code); + request['currency'] = currency['id']; + } else if ((type === 'margin') || (type === 'swap')) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + " fetchLedger requires a code argument (a market symbol) for '" + type + "' markets"); + } + argument = 'InstrumentId'; + const market = this.market (code); // we intentionally put a market inside here for the margin and swap ledgers + currency = this.currency (market['base']); + request['instrument_id'] = market['id']; + // + // if (type === 'margin') { + // // + // // 3. Borrow + // // 4. Repayment + // // 5. Interest + // // 7. Buy + // // 8. Sell + // // 9. From capital account + // // 10. From C2C + // // 11. From Futures + // // 12. From Spot + // // 13. From ETT + // // 14. To capital account + // // 15. To C2C + // // 16. To Spot + // // 17. To Futures + // // 18. To ETT + // // 19. Mandatory Repayment + // // 20. From Piggybank + // // 21. To Piggybank + // // 22. From Perpetual + // // 23. To Perpetual + // // 24. Liquidation Fee + // // 54. Clawback + // // 59. Airdrop Return. + // // + // request['type'] = 'number'; // All types will be returned if this filed is left blank + // } + // + } else if (type === 'account') { + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + // + // // + // // 1. deposit + // // 2. withdrawal + // // 13. cancel withdrawal + // // 18. into futures account + // // 19. out of futures account + // // 20. into sub account + // // 21. out of sub account + // // 28. claim + // // 29. into ETT account + // // 30. out of ETT account + // // 31. into C2C account + // // 32. out of C2C account + // // 33. into margin account + // // 34. out of margin account + // // 37. into spot account + // // 38. out of spot account + // // + // request['type'] = 'number'; + // + } else { + throw new NotSupported (this.id + " fetchLedger does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')"); + } + const method = type + 'Get' + suffix + argument + 'Ledger'; + const response = await this[method] (this.extend (request, query)); + // + // transfer funds transfer in/out + // trade funds moved as a result of a trade, spot and margin accounts only + // rebate fee rebate as per fee schedule, spot and margin accounts only + // match open long/open short/close long/close short (futures) or a change in the amount because of trades (swap) + // fee fee, futures only + // settlement settlement/clawback/settle long/settle short + // liquidation force close long/force close short/deliver close long/deliver close short + // funding funding fee, swap only + // margin a change in the amount after adjusting margin, swap only + // + // account + // + // [ + // { + // "amount":0.00051843, + // "balance":0.00100941, + // "currency":"BTC", + // "fee":0, + // "ledger_id":8987285, + // "timestamp":"2018-10-12T11:01:14.000Z", + // "typename":"Get from activity" + // } + // ] + // + // spot + // + // [ + // { + // "timestamp":"2019-03-18T07:08:25.000Z", + // "ledger_id":"3995334780", + // "created_at":"2019-03-18T07:08:25.000Z", + // "currency":"BTC", + // "amount":"0.0009985", + // "balance":"0.0029955", + // "type":"trade", + // "details":{ + // "instrument_id":"BTC-USDT", + // "order_id":"2500650881647616", + // "product_id":"BTC-USDT" + // } + // } + // ] + // + // margin + // + // [ + // [ + // { + // "created_at":"2019-03-20T03:45:05.000Z", + // "ledger_id":"78918186", + // "timestamp":"2019-03-20T03:45:05.000Z", + // "currency":"EOS", + // "amount":"0", // ? + // "balance":"0.59957711", + // "type":"transfer", + // "details":{ + // "instrument_id":"EOS-USDT", + // "order_id":"787057", + // "product_id":"EOS-USDT" + // } + // } + // ], + // { + // "before":"78965766", + // "after":"78918186" + // } + // ] + // + // futures + // + // [ + // { + // "ledger_id":"2508090544914461", + // "timestamp":"2019-03-19T14:40:24.000Z", + // "amount":"-0.00529521", + // "balance":"0", + // "currency":"EOS", + // "type":"fee", + // "details":{ + // "order_id":"2506982456445952", + // "instrument_id":"EOS-USD-190628" + // } + // } + // ] + // + // swap + // + // [ + // { + // "amount":"0.004742", + // "fee":"-0.000551", + // "type":"match", + // "instrument_id":"EOS-USD-SWAP", + // "ledger_id":"197429674941902848", + // "timestamp":"2019-03-25T05:56:31.286Z" + // }, + // ] + // + const isArray = Array.isArray (response[0]); + const isMargin = (type === 'margin'); + const entries = (isMargin && isArray) ? response[0] : response; + return this.parseLedger (entries, currency, since, limit); + } + + parseLedgerEntryType (type) { + const types = { + 'transfer': 'transfer', // // funds transfer in/out + 'trade': 'trade', // funds moved as a result of a trade, spot and margin accounts only + 'rebate': 'rebate', // fee rebate as per fee schedule, spot and margin accounts only + 'match': 'trade', // open long/open short/close long/close short (futures) or a change in the amount because of trades (swap) + 'fee': 'fee', // fee, futures only + 'settlement': 'trade', // settlement/clawback/settle long/settle short + 'liquidation': 'trade', // force close long/force close short/deliver close long/deliver close short + 'funding': 'fee', // funding fee, swap only + 'margin': 'margin', // a change in the amount after adjusting margin, swap only + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // + // account + // + // { + // "amount":0.00051843, + // "balance":0.00100941, + // "currency":"BTC", + // "fee":0, + // "ledger_id":8987285, + // "timestamp":"2018-10-12T11:01:14.000Z", + // "typename":"Get from activity" + // } + // + // spot + // + // { + // "timestamp":"2019-03-18T07:08:25.000Z", + // "ledger_id":"3995334780", + // "created_at":"2019-03-18T07:08:25.000Z", + // "currency":"BTC", + // "amount":"0.0009985", + // "balance":"0.0029955", + // "type":"trade", + // "details":{ + // "instrument_id":"BTC-USDT", + // "order_id":"2500650881647616", + // "product_id":"BTC-USDT" + // } + // } + // + // margin + // + // { + // "created_at":"2019-03-20T03:45:05.000Z", + // "ledger_id":"78918186", + // "timestamp":"2019-03-20T03:45:05.000Z", + // "currency":"EOS", + // "amount":"0", // ? + // "balance":"0.59957711", + // "type":"transfer", + // "details":{ + // "instrument_id":"EOS-USDT", + // "order_id":"787057", + // "product_id":"EOS-USDT" + // } + // } + // + // futures + // + // { + // "ledger_id":"2508090544914461", + // "timestamp":"2019-03-19T14:40:24.000Z", + // "amount":"-0.00529521", + // "balance":"0", + // "currency":"EOS", + // "type":"fee", + // "details":{ + // "order_id":"2506982456445952", + // "instrument_id":"EOS-USD-190628" + // } + // } + // + // swap + // + // { + // "amount":"0.004742", + // "fee":"-0.000551", + // "type":"match", + // "instrument_id":"EOS-USD-SWAP", + // "ledger_id":"197429674941902848", + // "timestamp":"2019-03-25T05:56:31.286Z" + // }, + // + const id = this.safeString (item, 'ledger_id'); + const account = undefined; + const details = this.safeValue (item, 'details', {}); + const referenceId = this.safeString (details, 'order_id'); + const referenceAccount = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const code = this.safeCurrencyCode (this.safeString (item, 'currency'), currency); + const amount = this.safeFloat (item, 'amount'); + const timestamp = this.parse8601 (this.safeString (item, 'timestamp')); + const fee = { + 'cost': this.safeFloat (item, 'fee'), + 'currency': code, + }; + const before = undefined; + const after = this.safeFloat (item, 'balance'); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'account': account, + 'referenceId': referenceId, + 'referenceAccount': referenceAccount, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, // balance before + 'after': after, // balance after + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const isArray = Array.isArray (params); + let request = '/api/' + api + '/' + this.version + '/'; + request += isArray ? path : this.implodeParams (path, params); + const query = isArray ? params : this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + const type = this.getPathAuthenticationType (path); + if (type === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (type === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.iso8601 (this.milliseconds ()); + headers = { + 'OK-ACCESS-KEY': this.apiKey, + 'OK-ACCESS-PASSPHRASE': this.password, + 'OK-ACCESS-TIMESTAMP': timestamp, + // 'OK-FROM': '', + // 'OK-TO': '', + // 'OK-LIMIT': '', + }; + let auth = timestamp + method + request; + if (method === 'GET') { + if (Object.keys (query).length) { + const urlencodedQuery = '?' + this.urlencode (query); + url += urlencodedQuery; + auth += urlencodedQuery; + } + } else { + if (isArray || Object.keys (query).length) { + body = this.json (query); + auth += body; + } + headers['Content-Type'] = 'application/json'; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); + headers['OK-ACCESS-SIGN'] = this.decode (signature); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + getPathAuthenticationType (path) { + const auth = this.safeValue (this.options, 'auth', {}); + const key = this.findBroadlyMatchedKey (auth, path); + return this.safeString (auth, key, 'private'); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + const feedback = this.id + ' ' + body; + if (code === 503) { + throw new ExchangeError (feedback); + } + if (!response) { + return; // fallback to default error handler + } + const message = this.safeString (response, 'message'); + const errorCode = this.safeString2 (response, 'code', 'error_code'); + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + if (message !== undefined) { + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],118:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class paymium extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'paymium', + 'name': 'Paymium', + 'countries': [ 'FR', 'EU' ], + 'rateLimit': 2000, + 'version': 'v1', + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27790564-a945a9d4-5ff9-11e7-9d2d-b635763f2f24.jpg', + 'api': 'https://paymium.com/api', + 'www': 'https://www.paymium.com', + 'fees': 'https://www.paymium.com/page/help/fees', + 'doc': [ + 'https://github.com/Paymium/api-documentation', + 'https://www.paymium.com/page/developers', + ], + }, + 'api': { + 'public': { + 'get': [ + 'countries', + 'data/{id}/ticker', + 'data/{id}/trades', + 'data/{id}/depth', + 'bitcoin_charts/{id}/trades', + 'bitcoin_charts/{id}/depth', + ], + }, + 'private': { + 'get': [ + 'merchant/get_payment/{UUID}', + 'user', + 'user/addresses', + 'user/addresses/{btc_address}', + 'user/orders', + 'user/orders/{UUID}', + 'user/price_alerts', + ], + 'post': [ + 'user/orders', + 'user/addresses', + 'user/payment_requests', + 'user/price_alerts', + 'merchant/create_payment', + ], + 'delete': [ + 'user/orders/{UUID}/cancel', + 'user/price_alerts/{id}', + ], + }, + }, + 'markets': { + 'BTC/EUR': { 'id': 'eur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur' }, + }, + 'fees': { + 'trading': { + 'maker': 0.002, + 'taker': 0.002, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetUser (params); + const result = { 'info': response }; + const currencies = Object.keys (this.currencies); + for (let i = 0; i < currencies.length; i++) { + const code = currencies[i]; + const currencyId = this.currencyId (code); + const free = 'balance_' + currencyId; + if (free in response) { + const account = this.account (); + const used = 'locked_' + currencyId; + account['free'] = this.safeFloat (response, free); + account['used'] = this.safeFloat (response, used); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetDataIdDepth (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + const request = { + 'id': this.marketId (symbol), + }; + const ticker = await this.publicGetDataIdTicker (this.extend (request, params)); + const timestamp = this.safeTimestamp (ticker, 'at'); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'variation'), + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'created_at_int'); + const id = this.safeString (trade, 'uuid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (trade, 'side'); + const price = this.safeFloat (trade, 'price'); + const amountField = 'traded_' + market['base'].toLowerCase (); + const amount = this.safeFloat (trade, amountField); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetDataIdTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'type': this.capitalize (type) + 'Order', + 'currency': this.marketId (symbol), + 'direction': side, + 'amount': amount, + }; + if (type !== 'market') { + request['price'] = price; + } + const response = await this.privatePostUserOrders (this.extend (request, params)); + return { + 'info': response, + 'id': response['uuid'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'UUID': id, + }; + return await this.privateDeleteUserOrdersUUIDCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = nonce + url; + if (method === 'POST') { + if (Object.keys (query).length) { + body = this.json (query); + auth += body; + } + } + headers = { + 'Api-Key': this.apiKey, + 'Api-Signature': this.hmac (this.encode (auth), this.encode (this.secret)), + 'Api-Nonce': nonce, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('errors' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],119:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, RequestTimeout, AuthenticationError, PermissionDenied, DDoSProtection, InsufficientFunds, OrderNotFound, OrderNotCached, InvalidOrder, AccountSuspended, CancelPending, InvalidNonce } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class poloniex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'poloniex', + 'name': 'Poloniex', + 'countries': [ 'US' ], + 'rateLimit': 1000, // up to 6 calls per second + 'certified': true, // 2019-06-07 + 'has': { + 'CORS': false, + 'createDepositAddress': true, + 'createMarketOrder': false, + 'editOrder': true, + 'fetchClosedOrders': 'emulated', + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrder': true, // true endpoint for a single open order + 'fetchOpenOrders': true, // true endpoint for open orders + 'fetchOrder': 'emulated', // no endpoint for a single open-or-closed order (just for an open order only) + 'fetchOrderBooks': true, + 'fetchOrders': 'emulated', // no endpoint for open-or-closed orders (just for open orders only) + 'fetchOrderStatus': 'emulated', // no endpoint for status of a single open-or-closed order (just for open orders only) + 'fetchOrderTrades': true, // true endpoint for trades of a single open or closed order + 'fetchTickers': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchTransactions': true, + 'fetchWithdrawals': true, + 'cancelAllOrders': true, + 'withdraw': true, + }, + 'timeframes': { + '5m': 300, + '15m': 900, + '30m': 1800, + '2h': 7200, + '4h': 14400, + '1d': 86400, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg', + 'api': { + 'public': 'https://poloniex.com/public', + 'private': 'https://poloniex.com/tradingApi', + }, + 'www': 'https://www.poloniex.com', + 'doc': 'https://docs.poloniex.com', + 'fees': 'https://poloniex.com/fees', + 'referral': 'https://www.poloniex.com/?utm_source=ccxt&utm_medium=web', + }, + 'api': { + 'public': { + 'get': [ + 'return24hVolume', + 'returnChartData', + 'returnCurrencies', + 'returnLoanOrders', + 'returnOrderBook', + 'returnTicker', + 'returnTradeHistory', + ], + }, + 'private': { + 'post': [ + 'buy', + 'cancelLoanOffer', + 'cancelOrder', + 'cancelAllOrders', + 'closeMarginPosition', + 'createLoanOffer', + 'generateNewAddress', + 'getMarginPosition', + 'marginBuy', + 'marginSell', + 'moveOrder', + 'returnActiveLoans', + 'returnAvailableAccountBalances', + 'returnBalances', + 'returnCompleteBalances', + 'returnDepositAddresses', + 'returnDepositsWithdrawals', + 'returnFeeInfo', + 'returnLendingHistory', + 'returnMarginAccountSummary', + 'returnOpenLoanOffers', + 'returnOpenOrders', + 'returnOrderTrades', + 'returnOrderStatus', + 'returnTradableBalances', + 'returnTradeHistory', + 'sell', + 'toggleAutoRenew', + 'transferBalance', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + // starting from Jan 8 2020 + 'maker': 0.0009, + 'taker': 0.0009, + }, + 'funding': {}, + }, + 'limits': { + 'amount': { + 'min': 0.000001, + 'max': 1000000000, + }, + 'price': { + 'min': 0.00000001, + 'max': 1000000000, + }, + 'cost': { + 'min': 0.00000000, + 'max': 1000000000, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'commonCurrencies': { + 'AIR': 'AirCoin', + 'APH': 'AphroditeCoin', + 'BCC': 'BTCtalkcoin', + 'BDG': 'Badgercoin', + 'BTM': 'Bitmark', + 'CON': 'Coino', + 'GOLD': 'GoldEagles', + 'GPUC': 'GPU', + 'HOT': 'Hotcoin', + 'ITC': 'Information Coin', + 'PLX': 'ParallaxCoin', + 'KEY': 'KEYCoin', + 'STR': 'XLM', + 'SOC': 'SOCC', + 'XAP': 'API Coin', + }, + 'options': { + 'limits': { + 'cost': { + 'min': { + 'BTC': 0.0001, + 'ETH': 0.0001, + 'XMR': 0.0001, + 'USDT': 1.0, + }, + }, + }, + }, + 'exceptions': { + 'exact': { + 'You may only place orders that reduce your position.': InvalidOrder, + 'Invalid order number, or you are not the person who placed the order.': OrderNotFound, + 'Permission denied': PermissionDenied, + 'Connection timed out. Please try again.': RequestTimeout, + 'Internal error. Please try again.': ExchangeNotAvailable, + 'Order not found, or you are not the person who placed it.': OrderNotFound, + 'Invalid API key/secret pair.': AuthenticationError, + 'Please do not make more than 8 API calls per second.': DDoSProtection, + 'Rate must be greater than zero.': InvalidOrder, // {"error":"Rate must be greater than zero."} + }, + 'broad': { + 'Total must be at least': InvalidOrder, // {"error":"Total must be at least 0.0001."} + 'This account is frozen.': AccountSuspended, + 'Not enough': InsufficientFunds, + 'Nonce must be greater': InvalidNonce, + 'You have already called cancelOrder or moveOrder on this order.': CancelPending, + 'Amount must be at least': InvalidOrder, // {"error":"Amount must be at least 0.000001."} + 'is either completed or does not exist': InvalidOrder, // {"error":"Order 587957810791 is either completed or does not exist."} + }, + }, + }); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, cost)), + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'date'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'quoteVolume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'period': this.timeframes[timeframe], + }; + if (since === undefined) { + request['end'] = this.seconds (); + if (limit === undefined) { + request['start'] = request['end'] - this.parseTimeframe ('1w'); // max range = 1 week + } else { + request['start'] = request['end'] - this.sum (limit) * this.parseTimeframe (timeframe); + } + } else { + request['start'] = parseInt (since / 1000); + if (limit !== undefined) { + const end = this.sum (request['start'], limit * this.parseTimeframe (timeframe)); + request['end'] = end; + } + } + const response = await this.publicGetReturnChartData (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetReturnTicker (params); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const limits = this.extend (this.limits, { + 'cost': { + 'min': this.safeValue (this.options['limits']['cost']['min'], quote), + }, + }); + const isFrozen = this.safeString (market, 'isFrozen'); + const active = (isFrozen !== '1'); + const numericId = this.safeInteger (market, 'id'); + result.push ({ + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': active, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'account': 'all', + }; + const response = await this.privatePostReturnCompleteBalances (this.extend (request, params)); + const result = { 'info': response }; + const currencyIds = Object.keys (response); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = this.safeValue (response, currencyId, {}); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'onOrders'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const fees = await this.privatePostReturnFeeInfo (params); + return { + 'info': fees, + 'maker': this.safeFloat (fees, 'makerFee'), + 'taker': this.safeFloat (fees, 'takerFee'), + 'withdraw': {}, + 'deposit': {}, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + }; + if (limit !== undefined) { + request['depth'] = limit; // 100 + } + const response = await this.publicGetReturnOrderBook (this.extend (request, params)); + const orderbook = this.parseOrderBook (response); + orderbook['nonce'] = this.safeInteger (response, 'seq'); + return orderbook; + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': 'all', + }; + if (limit !== undefined) { + request['depth'] = limit; // 100 + } + const response = await this.publicGetReturnOrderBook (this.extend (request, params)); + const marketIds = Object.keys (response); + const result = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = undefined; + if (marketId in this.markets_by_id) { + symbol = this.markets_by_id[marketId]['symbol']; + } else { + const [ quoteId, baseId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const orderbook = this.parseOrderBook (response[marketId]); + orderbook['nonce'] = this.safeInteger (response[marketId], 'seq'); + result[symbol] = orderbook; + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + let open = undefined; + let change = undefined; + let average = undefined; + const last = this.safeFloat (ticker, 'last'); + const relativeChange = this.safeFloat (ticker, 'percentChange'); + if (relativeChange !== -1) { + open = last / this.sum (1, relativeChange); + change = last - open; + average = this.sum (last, open) / 2; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high24hr'), + 'low': this.safeFloat (ticker, 'low24hr'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': relativeChange * 100, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'quoteVolume'), + 'quoteVolume': this.safeFloat (ticker, 'baseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetReturnTicker (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = undefined; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + market = { 'symbol': symbol }; + } + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetReturnCurrencies (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = response[id]; + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const precision = 8; // default precision, todo: fix "magic constants" + const code = this.safeCurrencyCode (id); + const active = (currency['delisted'] === 0) && !currency['disabled']; + const numericId = this.safeInteger (currency, 'id'); + result[code] = { + 'id': id, + 'numericId': numericId, + 'code': code, + 'info': currency, + 'name': currency['name'], + 'active': active, + 'fee': this.safeFloat (currency, 'txFee'), // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': currency['txFee'], + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetReturnTicker (params); + const ticker = response[market['id']]; + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchMyTrades (symbol defined, specific market) + // + // { + // globalTradeID: 394698946, + // tradeID: 45210255, + // date: '2018-10-23 17:28:55', + // type: 'sell', + // rate: '0.03114126', + // amount: '0.00018753', + // total: '0.00000583' + // } + // + // fetchMyTrades (symbol undefined, all markets) + // + // { + // globalTradeID: 394131412, + // tradeID: '5455033', + // date: '2018-10-16 18:05:17', + // rate: '0.06935244', + // amount: '1.40308443', + // total: '0.09730732', + // fee: '0.00100000', + // orderNumber: '104768235081', + // type: 'sell', + // category: 'exchange' + // } + // + // createOrder (taker trades) + // + // { + // 'amount': '200.00000000', + // 'date': '2019-12-15 16:04:10', + // 'rate': '0.00000355', + // 'total': '0.00071000', + // 'tradeID': '119871', + // 'type': 'buy', + // 'takerAdjustment': '200.00000000' + // } + // + const id = this.safeString2 (trade, 'globalTradeID', 'tradeID'); + const orderId = this.safeString (trade, 'orderNumber'); + const timestamp = this.parse8601 (this.safeString (trade, 'date')); + let symbol = undefined; + let base = undefined; + let quote = undefined; + if ((!market) && ('currencyPair' in trade)) { + const currencyPair = trade['currencyPair']; + if (currencyPair in this.markets_by_id) { + market = this.markets_by_id[currencyPair]; + } else { + const parts = currencyPair.split ('_'); + quote = parts[0]; + base = parts[1]; + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + quote = market['quote']; + } + const side = this.safeString (trade, 'type'); + let fee = undefined; + const price = this.safeFloat (trade, 'rate'); + const cost = this.safeFloat (trade, 'total'); + const amount = this.safeFloat (trade, 'amount'); + if ('fee' in trade) { + const rate = this.safeFloat (trade, 'fee'); + let feeCost = undefined; + let currency = undefined; + if (side === 'buy') { + currency = base; + feeCost = amount * rate; + } else { + currency = quote; + if (cost !== undefined) { + feeCost = cost * rate; + } + } + fee = { + 'type': undefined, + 'rate': rate, + 'cost': feeCost, + 'currency': currency, + }; + } + let takerOrMaker = undefined; + const takerAdjustment = this.safeFloat (trade, 'takerAdjustment'); + if (takerAdjustment !== undefined) { + takerOrMaker = 'taker'; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + request['end'] = this.seconds (); // last 50000 trades by default + } + const trades = await this.publicGetReturnTradeHistory (this.extend (request, params)); + return this.parseTrades (trades, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const pair = market ? market['id'] : 'all'; + const request = { 'currencyPair': pair }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + request['end'] = this.sum (this.seconds (), 1); // adding 1 is a fix for #3411 + } + // limit is disabled (does not really work as expected) + if (limit !== undefined) { + request['limit'] = parseInt (limit); + } + const response = await this.privatePostReturnTradeHistory (this.extend (request, params)); + // + // specific market (symbol defined) + // + // [ + // { + // globalTradeID: 394700861, + // tradeID: 45210354, + // date: '2018-10-23 18:01:58', + // type: 'buy', + // rate: '0.03117266', + // amount: '0.00000652', + // total: '0.00000020' + // }, + // { + // globalTradeID: 394698946, + // tradeID: 45210255, + // date: '2018-10-23 17:28:55', + // type: 'sell', + // rate: '0.03114126', + // amount: '0.00018753', + // total: '0.00000583' + // } + // ] + // + // all markets (symbol undefined) + // + // { + // BTC_BCH: [{ + // globalTradeID: 394131412, + // tradeID: '5455033', + // date: '2018-10-16 18:05:17', + // rate: '0.06935244', + // amount: '1.40308443', + // total: '0.09730732', + // fee: '0.00100000', + // orderNumber: '104768235081', + // type: 'sell', + // category: 'exchange' + // }, { + // globalTradeID: 394126818, + // tradeID: '5455007', + // date: '2018-10-16 16:55:34', + // rate: '0.06935244', + // amount: '0.00155709', + // total: '0.00010798', + // fee: '0.00200000', + // orderNumber: '104768179137', + // type: 'sell', + // category: 'exchange' + // }], + // } + // + let result = []; + if (market !== undefined) { + result = this.parseTrades (response, market); + } else { + if (response) { + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + const trades = this.parseTrades (response[id], market); + for (let j = 0; j < trades.length; j++) { + result.push (trades[j]); + } + } else { + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const trades = response[id]; + for (let j = 0; j < trades.length; j++) { + const market = { + 'symbol': symbol, + 'base': base, + 'quote': quote, + }; + result.push (this.parseTrade (trades[j], market)); + } + } + } + } + } + return this.filterBySinceLimit (result, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'Open': 'open', + 'Partially filled': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOpenOrder + // + // { + // status: 'Open', + // rate: '0.40000000', + // amount: '1.00000000', + // currencyPair: 'BTC_ETH', + // date: '2018-10-17 17:04:50', + // total: '0.40000000', + // type: 'buy', + // startingAmount: '1.00000', + // } + // + // fetchOpenOrders + // + // { + // orderNumber: '514514894224', + // type: 'buy', + // rate: '0.00001000', + // startingAmount: '100.00000000', + // amount: '100.00000000', + // total: '0.00100000', + // date: '2018-10-23 17:38:53', + // margin: 0, + // } + // + // createOrder + // + // { + // 'orderNumber': '9805453960', + // 'resultingTrades': [ + // { + // 'amount': '200.00000000', + // 'date': '2019-12-15 16:04:10', + // 'rate': '0.00000355', + // 'total': '0.00071000', + // 'tradeID': '119871', + // 'type': 'buy', + // 'takerAdjustment': '200.00000000', + // }, + // ], + // 'fee': '0.00000000', + // 'currencyPair': 'BTC_MANA', + // // --------------------------------------------------------- + // // the following fields are injected by createOrder + // 'timestamp': timestamp, + // 'status': 'open', + // 'type': type, + // 'side': side, + // 'price': price, + // 'amount': amount, + // } + // + let timestamp = this.safeInteger (order, 'timestamp'); + if (!timestamp) { + timestamp = this.parse8601 (order['date']); + } + let trades = undefined; + if ('resultingTrades' in order) { + trades = this.parseTrades (order['resultingTrades'], market); + } + let symbol = undefined; + const marketId = this.safeString (order, 'currencyPair'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat2 (order, 'price', 'rate'); + let remaining = this.safeFloat (order, 'amount'); + let amount = this.safeFloat (order, 'startingAmount'); + let filled = undefined; + let cost = 0; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + if (price !== undefined) { + cost = filled * price; + } + } + } else { + amount = remaining; + } + let status = this.parseOrderStatus (this.safeString (order, 'status')); + let average = undefined; + let lastTradeTimestamp = undefined; + if (filled === undefined) { + if (trades !== undefined) { + filled = 0; + cost = 0; + const tradesLength = trades.length; + if (tradesLength > 0) { + lastTradeTimestamp = trades[0]['timestamp']; + for (let i = 0; i < tradesLength; i++) { + const trade = trades[i]; + const tradeAmount = trade['amount']; + const tradePrice = trade['price']; + filled = this.sum (filled, tradeAmount); + cost = this.sum (cost, tradePrice * tradeAmount); + lastTradeTimestamp = Math.max (lastTradeTimestamp, trade['timestamp']); + } + } + remaining = Math.max (amount - filled, 0); + if (filled >= amount) { + status = 'closed'; + } + } + } + if ((filled !== undefined) && (cost !== undefined) && (filled > 0)) { + average = cost / filled; + } + let type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side', type); + if (type === side) { + type = undefined; + } + const id = this.safeString (order, 'orderNumber'); + let fee = undefined; + const feeCost = this.safeFloat (order, 'fee'); + if (feeCost !== undefined) { + let feeCurrencyCode = undefined; + if (market !== undefined) { + feeCurrencyCode = (side === 'buy') ? market['base'] : market['quote']; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': fee, + }; + } + + parseOpenOrders (orders, market, result) { + for (let i = 0; i < orders.length; i++) { + const order = orders[i]; + const extended = this.extend (order, { + 'status': 'open', + 'type': 'limit', + 'side': order['type'], + 'price': order['rate'], + }); + result.push (this.parseOrder (extended, market)); + } + return result; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const pair = market ? market['id'] : 'all'; + const request = { + 'currencyPair': pair, + }; + const response = await this.privatePostReturnOpenOrders (this.extend (request, params)); + let openOrders = []; + if (market !== undefined) { + openOrders = this.parseOpenOrders (response, market, openOrders); + } else { + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const orders = response[marketId]; + const m = this.markets_by_id[marketId]; + openOrders = this.parseOpenOrders (orders, m, openOrders); + } + } + for (let j = 0; j < openOrders.length; j++) { + this.orders[openOrders[j]['id']] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + const result = []; + for (let k = 0; k < cachedOrderIds.length; k++) { + const id = cachedOrderIds[k]; + if (id in openOrdersIndexedById) { + this.orders[id] = this.extend (this.orders[id], openOrdersIndexedById[id]); + } else { + let order = this.orders[id]; + if (order['status'] === 'open') { + order = this.extend (order, { + 'status': 'closed', + 'cost': undefined, + 'filled': order['amount'], + 'remaining': 0.0, + }); + if (order['cost'] === undefined) { + if (order['filled'] !== undefined) { + order['cost'] = order['filled'] * order['price']; + } + } + this.orders[id] = order; + } + } + const order = this.orders[id]; + if (market !== undefined) { + if (order['symbol'] === symbol) { + result.push (order); + } + } else { + result.push (order); + } + } + return this.filterBySinceLimit (result, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const since = this.safeValue (params, 'since'); + const limit = this.safeValue (params, 'limit'); + const request = this.omit (params, [ 'since', 'limit' ]); + const orders = await this.fetchOrders (symbol, since, limit, request); + for (let i = 0; i < orders.length; i++) { + if (orders[i]['id'] === id) { + return orders[i]; + } + } + throw new OrderNotCached (this.id + ' order id ' + id.toString () + ' is not in "open" state and not found in cache'); + } + + filterOrdersByStatus (orders, status) { + const result = []; + for (let i = 0; i < orders.length; i++) { + if (orders[i]['status'] === status) { + result.push (orders[i]); + } + } + return result; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterOrdersByStatus (orders, 'open'); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterOrdersByStatus (orders, 'closed'); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const method = 'privatePost' + this.capitalize (side); + const market = this.market (symbol); + amount = this.amountToPrecision (symbol, amount); + const request = { + 'currencyPair': market['id'], + 'rate': this.priceToPrecision (symbol, price), + 'amount': amount, + }; + // remember the timestamp before issuing the request + const timestamp = this.milliseconds (); + const response = await this[method] (this.extend (request, params)); + // + // { + // 'orderNumber': '9805453960', + // 'resultingTrades': [ + // { + // 'amount': '200.00000000', + // 'date': '2019-12-15 16:04:10', + // 'rate': '0.00000355', + // 'total': '0.00071000', + // 'tradeID': '119871', + // 'type': 'buy', + // 'takerAdjustment': '200.00000000', + // }, + // ], + // 'fee': '0.00000000', + // 'currencyPair': 'BTC_MANA', + // } + // + const order = this.parseOrder (this.extend ({ + 'timestamp': timestamp, + 'status': 'open', + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + }, response), market); + const id = order['id']; + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + price = parseFloat (price); + const request = { + 'orderNumber': id, + 'rate': this.priceToPrecision (symbol, price), + }; + if (amount !== undefined) { + request['amount'] = this.amountToPrecision (symbol, amount); + } + const response = await this.privatePostMoveOrder (this.extend (request, params)); + let result = undefined; + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + const newid = response['orderNumber']; + this.orders[newid] = this.extend (this.orders[id], { + 'id': newid, + 'price': price, + 'status': 'open', + }); + if (amount !== undefined) { + this.orders[newid]['amount'] = amount; + } + result = this.extend (this.orders[newid], { 'info': response }); + } else { + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + result = this.parseOrder (response, market); + this.orders[result['id']] = result; + } + return result; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let response = undefined; + try { + response = await this.privatePostCancelOrder (this.extend ({ + 'orderNumber': id, + }, params)); + } catch (e) { + if (e instanceof CancelPending) { + // A request to cancel the order has been sent already. + // If we then attempt to cancel the order the second time + // before the first request is processed the exchange will + // throw a CancelPending exception. Poloniex won't show the + // order in the list of active (open) orders and the cached + // order will be marked as 'closed' (see #1801 for details). + // To avoid that we proactively mark the order as 'canceled' + // here. If for some reason the order does not get canceled + // and still appears in the active list then the order cache + // will eventually get back in sync on a call to `fetchOrder`. + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + } + throw e; + } + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + async cancelAllOrders (symbol = undefined, params = {}) { + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['currencyPair'] = market['id']; + } + const response = await this.privatePostCancelAllOrders (this.extend (request, params)); + // + // { + // "success": 1, + // "message": "Orders canceled", + // "orderNumbers": [ + // 503749, + // 888321, + // 7315825, + // 7316824 + // ] + // } + // + const orderIds = this.safeValue (response, 'orderNumbers', []); + for (let i = 0; i < orderIds.length; i++) { + const id = orderIds[i].toString (); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + } + return response; + } + + async fetchOpenOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + id = id.toString (); + const response = await this.privatePostReturnOrderStatus (this.extend ({ + 'orderNumber': id, + }, params)); + // + // { + // success: 1, + // result: { + // '6071071': { + // status: 'Open', + // rate: '0.40000000', + // amount: '1.00000000', + // currencyPair: 'BTC_ETH', + // date: '2018-10-17 17:04:50', + // total: '0.40000000', + // type: 'buy', + // startingAmount: '1.00000', + // }, + // }, + // } + // + const result = this.safeValue (response['result'], id); + if (result === undefined) { + throw new OrderNotFound (this.id + ' order id ' + id + ' not found'); + } + const order = this.parseOrder (result); + order['id'] = id; + this.orders[id] = order; + return order; + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const orders = await this.fetchOpenOrders (symbol, undefined, undefined, params); + const indexed = this.indexBy (orders, 'id'); + return (id in indexed) ? 'open' : 'closed'; + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderNumber': id, + }; + const trades = await this.privatePostReturnOrderTrades (this.extend (request, params)); + return this.parseTrades (trades); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostGenerateNewAddress (this.extend (request, params)); + let address = undefined; + let tag = undefined; + if (response['success'] === 1) { + address = this.safeString (response, 'response'); + } + this.checkAddress (address); + const depositAddress = this.safeString (currency['info'], 'depositAddress'); + if (depositAddress !== undefined) { + tag = address; + address = depositAddress; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const response = await this.privatePostReturnDepositAddresses (params); + const currencyId = currency['id']; + let address = this.safeString (response, currencyId); + let tag = undefined; + this.checkAddress (address); + const depositAddress = this.safeString (currency['info'], 'depositAddress'); + if (depositAddress !== undefined) { + tag = address; + address = depositAddress; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + }; + if (tag) { + request['paymentId'] = tag; + } + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response, 'withdrawalNumber'), + }; + } + + async fetchTransactionsHelper (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const year = 31104000; // 60 * 60 * 24 * 30 * 12 = one year of history, why not + const now = this.seconds (); + const start = (since !== undefined) ? parseInt (since / 1000) : now - 10 * year; + const request = { + 'start': start, // UNIX timestamp, required + 'end': now, // UNIX timestamp, required + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostReturnDepositsWithdrawals (this.extend (request, params)); + // + // { deposits: [ { currency: "BTC", + // address: "1MEtiqJWru53FhhHrfJPPvd2tC3TPDVcmW", + // amount: "0.01063000", + // confirmations: 1, + // txid: "952b0e1888d6d491591facc0d37b5ebec540ac1efb241fdbc22bcc20d1822fb6", + // timestamp: 1507916888, + // status: "COMPLETE" }, + // { currency: "ETH", + // address: "0x20108ba20b65c04d82909e91df06618107460197", + // amount: "4.00000000", + // confirmations: 38, + // txid: "0x4be260073491fe63935e9e0da42bd71138fdeb803732f41501015a2d46eb479d", + // timestamp: 1525060430, + // status: "COMPLETE" } ], + // withdrawals: [ { withdrawalNumber: 8224394, + // currency: "EMC2", + // address: "EYEKyCrqTNmVCpdDV8w49XvSKRP9N3EUyF", + // amount: "63.10796020", + // fee: "0.01000000", + // timestamp: 1510819838, + // status: "COMPLETE: d37354f9d02cb24d98c8c4fc17aa42f475530b5727effdf668ee5a43ce667fd6", + // ipAddress: "5.220.220.200" }, + // { withdrawalNumber: 9290444, + // currency: "ETH", + // address: "0x191015ff2e75261d50433fbd05bd57e942336149", + // amount: "0.15500000", + // fee: "0.00500000", + // timestamp: 1514099289, + // status: "COMPLETE: 0x12d444493b4bca668992021fd9e54b5292b8e71d9927af1f076f554e4bea5b2d", + // ipAddress: "5.228.227.214" }, + // { withdrawalNumber: 11518260, + // currency: "BTC", + // address: "8JoDXAmE1GY2LRK8jD1gmAmgRPq54kXJ4t", + // amount: "0.20000000", + // fee: "0.00050000", + // timestamp: 1527918155, + // status: "COMPLETE: 1864f4ebb277d90b0b1ff53259b36b97fa1990edc7ad2be47c5e0ab41916b5ff", + // ipAddress: "211.8.195.26" } ] } + // + return response; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.fetchTransactionsHelper (code, since, limit, params); + for (let i = 0; i < response['deposits'].length; i++) { + response['deposits'][i]['type'] = 'deposit'; + } + for (let i = 0; i < response['withdrawals'].length; i++) { + response['withdrawals'][i]['type'] = 'withdrawal'; + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); + const deposits = this.parseTransactions (response['deposits'], currency, since, limit); + const transactions = this.arrayConcat (deposits, withdrawals); + return this.filterByCurrencySinceLimit (this.sortBy (transactions, 'timestamp'), code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.fetchTransactionsHelper (code, since, limit, params); + for (let i = 0; i < response['withdrawals'].length; i++) { + response['withdrawals'][i]['type'] = 'withdrawal'; + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); + return this.filterByCurrencySinceLimit (withdrawals, code, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.fetchTransactionsHelper (code, since, limit, params); + for (let i = 0; i < response['deposits'].length; i++) { + response['deposits'][i]['type'] = 'deposit'; + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const deposits = this.parseTransactions (response['deposits'], currency, since, limit); + return this.filterByCurrencySinceLimit (deposits, code, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'COMPLETE': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // deposits + // + // { + // "txid": "f49d489616911db44b740612d19464521179c76ebe9021af85b6de1e2f8d68cd", + // "type": "deposit", + // "amount": "49798.01987021", + // "status": "COMPLETE", + // "address": "DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT", + // "currency": "DOGE", + // "timestamp": 1524321838, + // "confirmations": 3371, + // "depositNumber": 134587098 + // } + // + // withdrawals + // + // { + // "fee": "0.00050000", + // "type": "withdrawal", + // "amount": "0.40234387", + // "status": "COMPLETE: fbabb2bf7d81c076f396f3441166d5f60f6cea5fdfe69e02adcc3b27af8c2746", + // "address": "1EdAqY4cqHoJGAgNfUFER7yZpg1Jc9DUa3", + // "currency": "BTC", + // "canCancel": 0, + // "ipAddress": "185.230.101.31", + // "paymentID": null, + // "timestamp": 1523834337, + // "canResendEmail": 0, + // "withdrawalNumber": 11162900 + // } + // + const timestamp = this.safeTimestamp (transaction, 'timestamp'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + let status = this.safeString (transaction, 'status', 'pending'); + let txid = this.safeString (transaction, 'txid'); + if (status !== undefined) { + const parts = status.split (': '); + const numParts = parts.length; + status = parts[0]; + if ((numParts > 1) && (txid === undefined)) { + txid = parts[1]; + } + status = this.parseTransactionStatus (status); + } + const type = this.safeString (transaction, 'type'); + const id = this.safeString2 (transaction, 'withdrawalNumber', 'depositNumber'); + let amount = this.safeFloat (transaction, 'amount'); + const address = this.safeString (transaction, 'address'); + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost === undefined) { + // according to https://poloniex.com/fees/ + feeCost = 0; // FIXME: remove hardcoded value that may change any time + } + if (type === 'withdrawal') { + // poloniex withdrawal amount includes the fee + amount = amount - feeCost; + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.extend ({ 'command': path }, params); + if (api === 'public') { + url += '?' + this.urlencode (query); + } else { + this.checkRequiredCredentials (); + query['nonce'] = this.nonce (); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // {"error":"Permission denied."} + if ('error' in response) { + const message = response['error']; + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],120:[function(require,module,exports){ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ROUND } = require ('./base/functions/number'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors'); + +module.exports = class rightbtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'rightbtc', + 'name': 'RightBTC', + 'countries': [ 'AE' ], + 'has': { + 'privateAPI': false, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': false, + 'fetchOrder': 'emulated', + 'fetchMyTrades': true, + }, + 'timeframes': { + '1m': 'min1', + '5m': 'min5', + '15m': 'min15', + '30m': 'min30', + '1h': 'hr1', + '1d': 'day1', + '1w': 'week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/42633917-7d20757e-85ea-11e8-9f53-fffe9fbb7695.jpg', + 'api': 'https://www.rightbtc.com/api', + 'www': 'https://www.rightbtc.com', + 'doc': [ + 'https://docs.rightbtc.com/api/', + ], + // eslint-disable-next-line no-useless-escape + // 'fees': 'https://www.rightbtc.com/\#\!/support/fee', + }, + 'api': { + 'public': { + 'get': [ + // 'getAssetsTradingPairs/zh', // 404 + 'trading_pairs', + 'ticker/{trading_pair}', + 'tickers', + 'depth/{trading_pair}', + 'depth/{trading_pair}/{count}', + 'trades/{trading_pair}', + 'trades/{trading_pair}/{count}', + 'candlestick/latest/{trading_pair}', + 'candlestick/{timeSymbol}/{trading_pair}', + 'candlestick/{timeSymbol}/{trading_pair}/{count}', + ], + }, + 'trader': { + 'get': [ + 'balance/{symbol}', + 'balances', + 'deposits/{asset}/{page}', + 'withdrawals/{asset}/{page}', + 'orderpage/{trading_pair}/{cursor}', + 'orders/{trading_pair}/{ids}', // ids are a slash-separated list of {id}/{id}/{id}/... + 'history/{trading_pair}/{ids}', + 'historys/{trading_pair}/{page}', + 'trading_pairs', + ], + 'post': [ + 'order', + ], + 'delete': [ + 'order/{trading_pair}/{ids}', + ], + }, + }, + // HARDCODING IS DEPRECATED, THE FEES BELOW SHOULD BE REWRITTEN + 'fees': { + 'trading': { + // min trading fees + // 0.0001 BTC + // 0.01 ETP + // 0.001 ETH + // 0.1 BITCNY + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + // 'BTM': n => 3 + n * (1 / 100), + // 'ZDC': n => 1 + n * (0.5 / 100), + // 'ZGC': n => 0.5 + n * (0.5 / 100), + // 'BTS': n => 1 + n * (1 / 100), + // 'DLT': n => 3 + n * (1 / 100), + // 'SNT': n => 10 + n * (1 / 100), + // 'XNC': n => 1 + n * (1 / 100), + // 'ICO': n => 3 + n * (1 / 100), + // 'CMC': n => 1 + n * (0.5 / 100), + // 'GXS': n => 0.2 + n * (1 / 100), + // 'OBITS': n => 0.3 + n * (1 / 100), + // 'ICS': n => 2 + n * (1 / 100), + // 'TIC': n => 2 + n * (1 / 100), + // 'IND': n => 20 + n * (1 / 100), + // 'MVC': n => 20 + n * (1 / 100), + // 'BitCNY': n => 0.1 + n * (1 / 100), + // 'MTX': n => 1 + n * (1 / 100), + 'ETP': 0.01, + 'BTC': 0.0005, + 'ETH': 0.005, + 'ETC': 0.01, + 'STORJ': 3, + 'LTC': 0.01, + 'ZEC': 0.001, + 'BCC': 0.001, + 'XRB': 0, + 'NXS': 0.1, + }, + }, + }, + 'commonCurrencies': { + 'XRB': 'NANO', + }, + 'exceptions': { + 'ERR_USERTOKEN_NOT_FOUND': AuthenticationError, + 'ERR_ASSET_NOT_EXISTS': ExchangeError, + 'ERR_ASSET_NOT_AVAILABLE': ExchangeError, + 'ERR_BALANCE_NOT_ENOUGH': InsufficientFunds, + 'ERR_CREATE_ORDER': InvalidOrder, + 'ERR_CANDLESTICK_DATA': ExchangeError, + }, + }); + } + + async fetchMarkets (params = {}) { + // let zh = await this.publicGetGetAssetsTradingPairsZh (); + const markets = await this.publicGetTradingPairs (params); + const marketIds = Object.keys (markets); + const result = []; + for (let i = 0; i < marketIds.length; i++) { + const id = marketIds[i]; + const market = markets[id]; + const baseId = this.safeString (market, 'bid_asset_symbol'); + const quoteId = this.safeString (market, 'ask_asset_symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'bid_asset_decimals'), + 'price': this.safeInteger (market, 'ask_asset_decimals'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['price']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + divideSafeFloat (x, key, divisor) { + const value = this.safeFloat (x, key); + if (value !== undefined) { + return value / divisor; + } + return value; + } + + parseTicker (ticker, market = undefined) { + const symbol = market['symbol']; + const timestamp = this.safeInteger (ticker, 'date'); + const last = this.divideSafeFloat (ticker, 'last', 1e8); + const high = this.divideSafeFloat (ticker, 'high', 1e8); + const low = this.divideSafeFloat (ticker, 'low', 1e8); + const bid = this.divideSafeFloat (ticker, 'buy', 1e8); + const ask = this.divideSafeFloat (ticker, 'sell', 1e8); + const baseVolume = this.divideSafeFloat (ticker, 'vol24h', 1e8); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': high, + 'low': low, + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + }; + const response = await this.publicGetTickerTradingPair (this.extend (request, params)); + const result = this.safeValue (response, 'result'); + if (result === undefined) { + throw new ExchangeError (this.id + ' fetchTicker returned an empty response for symbol ' + symbol); + } + return this.parseTicker (result, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const tickers = response['result']; + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const id = ticker['market']; + if (!(id in this.marketsById)) { + continue; + } + const market = this.marketsById[id]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'trading_pair': this.marketId (symbol), + }; + let method = 'publicGetDepthTradingPair'; + if (limit !== undefined) { + method += 'Count'; + request['count'] = limit; + } + const response = await this[method] (this.extend (request, params)); + const bidsasks = {}; + const types = ['bid', 'ask']; + for (let ti = 0; ti < types.length; ti++) { + const type = types[ti]; + bidsasks[type] = []; + for (let i = 0; i < response['result'][type].length; i++) { + const [ price, amount, total ] = response['result'][type][i]; + bidsasks[type].push ([ + price / 1e8, + amount / 1e8, + total / 1e8, + ]); + } + } + return this.parseOrderBook (bidsasks, undefined, 'bid', 'ask'); + } + + parseTrade (trade, market = undefined) { + // + // { + // "order_id": 118735, + // "trade_id": 7, + // "trading_pair": "BTCCNY", + // "side": "B", + // "quantity": 1000000000, + // "price": 900000000, + // "created_at": "2017-06-06T20:45:27.000Z" + // } + // + let timestamp = this.safeInteger (trade, 'date'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + } + let id = this.safeString (trade, 'tid'); + id = this.safeString (trade, 'trade_id', id); + const orderId = this.safeString (trade, 'order_id'); + const price = this.divideSafeFloat (trade, 'price', 1e8); + let amount = this.safeFloat (trade, 'amount'); + amount = this.safeFloat (trade, 'quantity', amount); + if (amount !== undefined) { + amount = amount / 1e8; + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'trading_pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = this.costToPrecision (symbol, price * amount); + cost = parseFloat (cost); + let side = this.safeStringLower (trade, 'side'); + if (side === 'b') { + side = 'buy'; + } else if (side === 's') { + side = 'sell'; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + }; + const response = await this.publicGetTradesTradingPair (this.extend (request, params)); + return this.parseTrades (response['result'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + parseInt (ohlcv[0]), + parseFloat (ohlcv[2]) / 1e8, + parseFloat (ohlcv[3]) / 1e8, + parseFloat (ohlcv[4]) / 1e8, + parseFloat (ohlcv[5]) / 1e8, + parseFloat (ohlcv[1]) / 1e8, + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'timeSymbol': this.timeframes[timeframe], + }; + const response = await this.publicGetCandlestickTimeSymbolTradingPair (this.extend (request, params)); + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.traderGetBalances (params); + // + // { + // "status": { + // "success": 1, + // "message": "GET_BALANCES" + // }, + // "result": [ + // { + // "asset": "ETP", + // "balance": "5000000000000", + // "frozen": "0", + // "state": "1" + // }, + // { + // "asset": "CNY", + // "balance": "10000000000000", + // "frozen": "240790000", + // "state": "1" + // } + // ] + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'result', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + // https://github.com/ccxt/ccxt/issues/3873 + account['free'] = this.divideSafeFloat (balance, 'balance', 1e8); + account['used'] = this.divideSafeFloat (balance, 'frozen', 1e8); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const order = { + 'trading_pair': market['id'], + // We need to use decimalToPrecision here, since + // 0.036*1e8 === 3599999.9999999995 + // which would get truncated to 3599999 after parseInt + // which would then be rejected by rightBtc because it's too precise + 'quantity': parseInt (this.decimalToPrecision (amount * 1e8, ROUND, 0, this.precisionMode)), + 'limit': parseInt (this.decimalToPrecision (price * 1e8, ROUND, 0, this.precisionMode)), + 'type': type.toUpperCase (), + 'side': side.toUpperCase (), + }; + const response = await this.traderPostOrder (this.extend (order, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'ids': id, + }; + const response = await this.traderDeleteOrderTradingPairIds (this.extend (request, params)); + return response; + } + + parseOrderStatus (status) { + const statuses = { + 'NEW': 'open', + 'TRADE': 'closed', // TRADE means filled or partially filled orders + 'CANCEL': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder / fetchOpenOrders + // + // { + // "id": 4180528, + // "quantity": 20000000, + // "rest": 20000000, + // "limit": 1000000, + // "price": null, + // "side": "BUY", + // "created": 1496005693738 + // } + // + // fetchOrders + // + // { + // "trading_pair": "ETPCNY", + // "status": "TRADE", + // "fee": 0.23, + // "min_fee": 10000000, + // "created_at": "2017-05-25T00:12:27.000Z", + // "cost": 1152468000000, + // "limit": 3600000000, + // "id": 11060, + // "quantity": 32013000000, + // "filled_quantity": 32013000000 + // } + // + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const marketId = this.safeString (order, 'trading_pair'); + if (market === undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let symbol = marketId; + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = this.safeInteger (order, 'created'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (order, 'created_at')); + } + if ('time' in order) { + timestamp = order['time']; + } else if ('transactTime' in order) { + timestamp = order['transactTime']; + } + let price = this.safeFloat2 (order, 'limit', 'price'); + if (price !== undefined) { + price = price / 1e8; + } + const amount = this.divideSafeFloat (order, 'quantity', 1e8); + let filled = this.divideSafeFloat (order, 'filled_quantity', 1e8); + let remaining = this.divideSafeFloat (order, 'rest', 1e8); + const cost = this.divideSafeFloat (order, 'cost', 1e8); + // lines 483-494 should be generalized into a base class method + if (amount !== undefined) { + if (remaining === undefined) { + if (filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + if (filled === undefined) { + if (remaining !== undefined) { + filled = Math.max (0, amount - remaining); + } + } + } + const type = 'limit'; + const side = this.safeStringLower (order, 'side'); + const feeCost = this.divideSafeFloat (order, 'min_fee', 1e8); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = market['quote']; + } + fee = { + 'rate': this.safeFloat (order, 'fee'), + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + const trades = undefined; + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'ids': id, + }; + const response = await this.traderGetOrdersTradingPairIds (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": "SUC_LIST_AVTICE_ORDERS" + // }, + // "result": [ + // { + // "id": 4180528, + // "quantity": 20000000, + // "rest": 20000000, + // "limit": 1000000, + // "price": null, + // "side": "BUY", + // "created": 1496005693738 + // } + // ] + // } + // + const orders = this.parseOrders (response['result'], market); + const ordersById = this.indexBy (orders, 'id'); + if (!(id in ordersById)) { + throw new OrderNotFound (this.id + ' fetchOrder could not find order ' + id.toString () + ' in open orders.'); + } + return ordersById[id]; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'cursor': 0, + }; + const response = await this.traderGetOrderpageTradingPairCursor (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": "SUC_LIST_AVTICE_ORDERS_PAGE" + // }, + // "result": { + // "cursor": "0", + // "orders": [ + // { + // "id": 4180528, + // "quantity": 20000000, + // "rest": 20000000, + // "limit": 1000000, + // "price": null, + // "side": "BUY", + // "created": 1496005693738 + // } + // ] + // } + // } + // + return this.parseOrders (response['result']['orders'], market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + let ids = this.safeString (params, 'ids'); + if ((symbol === undefined) || (ids === undefined)) { + throw new ArgumentsRequired (this.id + " fetchOrders requires a 'symbol' argument and an extra 'ids' parameter. The 'ids' should be an array or a string of one or more order ids separated with slashes."); // eslint-disable-line quotes + } + if (Array.isArray (ids)) { + ids = ids.join ('/'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'ids': ids, + }; + const response = await this.traderGetHistoryTradingPairIds (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": null + // }, + // "result": [ + // { + // "trading_pair": "ETPCNY", + // "status": "TRADE", + // "fee": 0.23, + // "min_fee": 10000000, + // "created_at": "2017-05-25T00:12:27.000Z", + // "cost": 1152468000000, + // "limit": 3600000000, + // "id": 11060, + // "quantity": 32013000000, + // "filled_quantity": 32013000000 + // } + // ] + // } + // + return this.parseOrders (response['result'], undefined, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'page': 0, + }; + const response = await this.traderGetHistorysTradingPairPage (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": null + // }, + // "result": [ + // { + // "order_id": 118735, + // "trade_id": 7, + // "trading_pair": "BTCCNY", + // "side": "B", + // "quantity": 1000000000, + // "price": 900000000, + // "created_at": "2017-06-06T20:45:27.000Z" + // }, + // { + // "order_id": 118734, + // "trade_id": 7, + // "trading_pair": "BTCCNY", + // "side": "S", + // "quantity": 1000000000, + // "price": 900000000, + // "created_at": "2017-06-06T20:45:27.000Z" + // } + // ] + // } + // + return this.parseTrades (response['result'], undefined, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + '/' + api + '/' + this.implodeParams (path, params); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + headers = { + 'apikey': this.apiKey, + 'signature': this.secret, + }; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + headers['Content-Type'] = 'application/json'; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const status = this.safeValue (response, 'status'); + if (status !== undefined) { + // + // {"status":{"success":0,"message":"ERR_USERTOKEN_NOT_FOUND"}} + // + const success = this.safeString (status, 'success'); + if (success !== '1') { + const message = this.safeString (status, 'message'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + throw new ExchangeError (feedback); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9,"./base/functions/number":15}],121:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class southxchange extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'southxchange', + 'name': 'SouthXchange', + 'countries': [ 'AR' ], // Argentina + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'createDepositAddress': true, + 'fetchOpenOrders': true, + 'fetchTickers': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27838912-4f94ec8a-60f6-11e7-9e5d-bbf9bd50a559.jpg', + 'api': 'https://www.southxchange.com/api', + 'www': 'https://www.southxchange.com', + 'doc': 'https://www.southxchange.com/Home/Api', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'price/{symbol}', + 'prices', + 'book/{symbol}', + 'trades/{symbol}', + ], + }, + 'private': { + 'post': [ + 'cancelMarketOrders', + 'cancelOrder', + 'getOrder', + 'generatenewaddress', + 'listOrders', + 'listBalances', + 'listTransactions', + 'placeOrder', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.3 / 100, + }, + }, + 'commonCurrencies': { + 'SMT': 'SmartNode', + 'MTC': 'Marinecoin', + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetMarkets (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = market[0]; + const quoteId = market[1]; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = baseId + '/' + quoteId; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostListBalances (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'Currency'); + const code = this.safeCurrencyCode (currencyId); + const deposited = this.safeFloat (balance, 'Deposited'); + const unconfirmed = this.safeFloat (balance, 'Unconfirmed'); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'Available'); + account['total'] = this.sum (deposited, unconfirmed); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetBookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'BuyOrders', 'SellOrders', 'Price', 'Amount'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'Last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'Bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'Ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'Variation24Hr'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume24Hr'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetPrices (params); + const tickers = this.indexBy (response, 'Market'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetPriceSymbol (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'At'); + const price = this.safeFloat (trade, 'Price'); + const amount = this.safeFloat (trade, 'Amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const side = this.safeString (trade, 'Type'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': undefined, + 'order': undefined, + 'type': undefined, + 'side': side, + 'price': price, + 'takerOrMaker': undefined, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + const status = 'open'; + const baseId = this.safeString (order, 'ListingCurrency'); + const quoteId = this.safeString (order, 'ReferenceCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const timestamp = undefined; + const price = this.safeFloat (order, 'LimitPrice'); + const amount = this.safeFloat (order, 'OriginalAmount'); + const remaining = this.safeFloat (order, 'Amount'); + let filled = undefined; + let cost = undefined; + if (amount !== undefined) { + cost = price * amount; + if (remaining !== undefined) { + filled = amount - remaining; + } + } + const type = 'limit'; + const side = this.safeStringLower (order, 'Type'); + const id = this.safeString (order, 'Code'); + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + return result; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privatePostListOrders (params); + return this.parseOrders (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'listingCurrency': market['base'], + 'referenceCurrency': market['quote'], + 'type': side, + 'amount': amount, + }; + if (type === 'limit') { + request['limitPrice'] = price; + } + const response = await this.privatePostPlaceOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response.toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderCode': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostGeneratenewaddress (this.extend (request, params)); + const parts = response.split ('|'); + const numParts = parts.length; + const address = parts[0]; + this.checkAddress (address); + let tag = undefined; + if (numParts > 1) { + tag = parts[1]; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'address': address, + 'amount': amount, + }; + if (tag !== undefined) { + request['address'] = address + '|' + tag; + } + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const url = this.urls['api'] + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + query = this.extend ({ + 'key': this.apiKey, + 'nonce': nonce, + }, query); + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + 'Hash': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7}],122:[function(require,module,exports){ +(function (module, exports) { + 'use strict'; + + // Utils + function assert (val, msg) { + if (!val) throw new Error(msg || 'Assertion failed'); + } + + // Could use `inherits` module, but don't want to move from single file + // architecture yet. + function inherits (ctor, superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; + } + + // BN + + function BN (number, base, endian) { + if (BN.isBN(number)) { + return number; + } + + this.negative = 0; + this.words = null; + this.length = 0; + + // Reduction context + this.red = null; + + if (number !== null) { + if (base === 'le' || base === 'be') { + endian = base; + base = 10; + } + + this._init(number || 0, base || 10, endian || 'be'); + } + } + if (typeof module === 'object') { + module.exports = BN; + } else { + exports.BN = BN; + } + + BN.BN = BN; + BN.wordSize = 26; + + var Buffer; + try { + Buffer = require('buffer').Buffer; + } catch (e) { + } + + BN.isBN = function isBN (num) { + if (num instanceof BN) { + return true; + } + + return num !== null && typeof num === 'object' && + num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); + }; + + BN.max = function max (left, right) { + if (left.cmp(right) > 0) return left; + return right; + }; + + BN.min = function min (left, right) { + if (left.cmp(right) < 0) return left; + return right; + }; + + BN.prototype._init = function init (number, base, endian) { + if (typeof number === 'number') { + return this._initNumber(number, base, endian); + } + + if (typeof number === 'object') { + return this._initArray(number, base, endian); + } + + if (base === 'hex') { + base = 16; + } + assert(base === (base | 0) && base >= 2 && base <= 36); + + number = number.toString().replace(/\s+/g, ''); + var start = 0; + if (number[0] === '-') { + start++; + } + + if (base === 16) { + this._parseHex(number, start); + } else { + this._parseBase(number, base, start); + } + + if (number[0] === '-') { + this.negative = 1; + } + + this._strip(); + + if (endian !== 'le') return; + + this._initArray(this.toArray(), base, endian); + }; + + BN.prototype._initNumber = function _initNumber (number, base, endian) { + if (number < 0) { + this.negative = 1; + number = -number; + } + if (number < 0x4000000) { + this.words = [number & 0x3ffffff]; + this.length = 1; + } else if (number < 0x10000000000000) { + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff + ]; + this.length = 2; + } else { + assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff, + 1 + ]; + this.length = 3; + } + + if (endian !== 'le') return; + + // Reverse the bytes + this._initArray(this.toArray(), base, endian); + }; + + BN.prototype._initArray = function _initArray (number, base, endian) { + // Perhaps a Uint8Array + assert(typeof number.length === 'number'); + if (number.length <= 0) { + this.words = [0]; + this.length = 1; + return this; + } + + this.length = Math.ceil(number.length / 3); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + var j, w; + var off = 0; + if (endian === 'be') { + for (i = number.length - 1, j = 0; i >= 0; i -= 3) { + w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } else if (endian === 'le') { + for (i = 0, j = 0; i < number.length; i += 3) { + w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } + return this._strip(); + }; + + function parseHex (str, start, end) { + var r = 0; + var len = Math.min(str.length, end); + var z = 0; + for (var i = start; i < len; i++) { + var c = str.charCodeAt(i) - 48; + + r <<= 4; + + var b; + + // 'a' - 'f' + if (c >= 49 && c <= 54) { + b = c - 49 + 0xa; + + // 'A' - 'F' + } else if (c >= 17 && c <= 22) { + b = c - 17 + 0xa; + + // '0' - '9' + } else { + b = c; + } + + r |= b; + z |= b; + } + + assert(!(z & 0xf0), 'Invalid character in ' + str); + return r; + } + + BN.prototype._parseHex = function _parseHex (number, start) { + // Create possibly bigger array to ensure that it fits the number + this.length = Math.ceil((number.length - start) / 6); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + var j, w; + // Scan 24-bit chunks and add them to the number + var off = 0; + for (i = number.length - 6, j = 0; i >= start; i -= 6) { + w = parseHex(number, i, i + 6); + this.words[j] |= (w << off) & 0x3ffffff; + // NOTE: `0x3fffff` is intentional here, 26bits max shift + 24bit hex limb + this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + if (i + 6 !== start) { + w = parseHex(number, start, i + 6); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; + } + this._strip(); + }; + + function parseBase (str, start, end, mul) { + var r = 0; + var b = 0; + var len = Math.min(str.length, end); + for (var i = start; i < len; i++) { + var c = str.charCodeAt(i) - 48; + + r *= mul; + + // 'a' + if (c >= 49) { + b = c - 49 + 0xa; + + // 'A' + } else if (c >= 17) { + b = c - 17 + 0xa; + + // '0' - '9' + } else { + b = c; + } + assert(c >= 0 && b < mul, 'Invalid character'); + r += b; + } + return r; + } + + BN.prototype._parseBase = function _parseBase (number, base, start) { + // Initialize as zero + this.words = [0]; + this.length = 1; + + // Find length of limb in base + for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { + limbLen++; + } + limbLen--; + limbPow = (limbPow / base) | 0; + + var total = number.length - start; + var mod = total % limbLen; + var end = Math.min(total, total - mod) + start; + + var word = 0; + for (var i = start; i < end; i += limbLen) { + word = parseBase(number, i, i + limbLen, base); + + this.imuln(limbPow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + + if (mod !== 0) { + var pow = 1; + word = parseBase(number, i, number.length, base); + + for (i = 0; i < mod; i++) { + pow *= base; + } + + this.imuln(pow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + }; + + BN.prototype.copy = function copy (dest) { + dest.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + dest.words[i] = this.words[i]; + } + dest.length = this.length; + dest.negative = this.negative; + dest.red = this.red; + }; + + BN.prototype._move = function _move (dest) { + dest.words = this.words; + dest.length = this.length; + dest.negative = this.negative; + dest.red = this.red; + }; + + BN.prototype.clone = function clone () { + var r = new BN(null); + this.copy(r); + return r; + }; + + BN.prototype._expand = function _expand (size) { + while (this.length < size) { + this.words[this.length++] = 0; + } + return this; + }; + + // Remove leading `0` from `this` + BN.prototype._strip = function strip () { + while (this.length > 1 && this.words[this.length - 1] === 0) { + this.length--; + } + return this._normSign(); + }; + + BN.prototype._normSign = function _normSign () { + // -0 = 0 + if (this.length === 1 && this.words[0] === 0) { + this.negative = 0; + } + return this; + }; + + // Check Symbol.for because not everywhere where Symbol defined + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility + if (typeof Symbol !== 'undefined' && typeof Symbol.for === 'function') { + BN.prototype[Symbol.for('nodejs.util.inspect.custom')] = inspect; + } else { + BN.prototype.inspect = inspect; + } + + function inspect () { + return (this.red ? ''; + } + + /* + + var zeros = []; + var groupSizes = []; + var groupBases = []; + + var s = ''; + var i = -1; + while (++i < BN.wordSize) { + zeros[i] = s; + s += '0'; + } + groupSizes[0] = 0; + groupSizes[1] = 0; + groupBases[0] = 0; + groupBases[1] = 0; + var base = 2 - 1; + while (++base < 36 + 1) { + var groupSize = 0; + var groupBase = 1; + while (groupBase < (1 << BN.wordSize) / base) { + groupBase *= base; + groupSize += 1; + } + groupSizes[base] = groupSize; + groupBases[base] = groupBase; + } + + */ + + var zeros = [ + '', + '0', + '00', + '000', + '0000', + '00000', + '000000', + '0000000', + '00000000', + '000000000', + '0000000000', + '00000000000', + '000000000000', + '0000000000000', + '00000000000000', + '000000000000000', + '0000000000000000', + '00000000000000000', + '000000000000000000', + '0000000000000000000', + '00000000000000000000', + '000000000000000000000', + '0000000000000000000000', + '00000000000000000000000', + '000000000000000000000000', + '0000000000000000000000000' + ]; + + var groupSizes = [ + 0, 0, + 25, 16, 12, 11, 10, 9, 8, + 8, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 5, 5, + 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5 + ]; + + var groupBases = [ + 0, 0, + 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, + 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, + 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, + 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, + 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 + ]; + + BN.prototype.toString = function toString (base, padding) { + base = base || 10; + padding = padding | 0 || 1; + + var out; + if (base === 16 || base === 'hex') { + out = ''; + var off = 0; + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = this.words[i]; + var word = (((w << off) | carry) & 0xffffff).toString(16); + carry = (w >>> (24 - off)) & 0xffffff; + if (carry !== 0 || i !== this.length - 1) { + out = zeros[6 - word.length] + word + out; + } else { + out = word + out; + } + off += 2; + if (off >= 26) { + off -= 26; + i--; + } + } + if (carry !== 0) { + out = carry.toString(16) + out; + } + while (out.length % padding !== 0) { + out = '0' + out; + } + if (this.negative !== 0) { + out = '-' + out; + } + return out; + } + + if (base === (base | 0) && base >= 2 && base <= 36) { + // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); + var groupSize = groupSizes[base]; + // var groupBase = Math.pow(base, groupSize); + var groupBase = groupBases[base]; + out = ''; + var c = this.clone(); + c.negative = 0; + while (!c.isZero()) { + var r = c.modrn(groupBase).toString(base); + c = c.idivn(groupBase); + + if (!c.isZero()) { + out = zeros[groupSize - r.length] + r + out; + } else { + out = r + out; + } + } + if (this.isZero()) { + out = '0' + out; + } + while (out.length % padding !== 0) { + out = '0' + out; + } + if (this.negative !== 0) { + out = '-' + out; + } + return out; + } + + assert(false, 'Base should be between 2 and 36'); + }; + + BN.prototype.toNumber = function toNumber () { + var ret = this.words[0]; + if (this.length === 2) { + ret += this.words[1] * 0x4000000; + } else if (this.length === 3 && this.words[2] === 0x01) { + // NOTE: at this stage it is known that the top bit is set + ret += 0x10000000000000 + (this.words[1] * 0x4000000); + } else if (this.length > 2) { + assert(false, 'Number can only safely store up to 53 bits'); + } + return (this.negative !== 0) ? -ret : ret; + }; + + BN.prototype.toJSON = function toJSON () { + return this.toString(16, 2); + }; + + if (Buffer) { + BN.prototype.toBuffer = function toBuffer (endian, length) { + return this.toArrayLike(Buffer, endian, length); + }; + } + + BN.prototype.toArray = function toArray (endian, length) { + return this.toArrayLike(Array, endian, length); + }; + + var allocate = function allocate (ArrayType, size) { + if (ArrayType.allocUnsafe) { + return ArrayType.allocUnsafe(size); + } + return new ArrayType(size); + }; + + BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { + this._strip(); + + var byteLength = this.byteLength(); + var reqLength = length || Math.max(1, byteLength); + assert(byteLength <= reqLength, 'byte array longer than desired length'); + assert(reqLength > 0, 'Requested array length <= 0'); + + var res = allocate(ArrayType, reqLength); + var postfix = endian === 'le' ? 'LE' : 'BE'; + this['_toArrayLike' + postfix](res, byteLength); + return res; + }; + + BN.prototype._toArrayLikeLE = function _toArrayLikeLE (res, byteLength) { + var position = 0; + var carry = 0; + + for (var i = 0, shift = 0; i < this.length; i++) { + var word = (this.words[i] << shift) | carry; + + res[position++] = word & 0xff; + if (position < res.length) { + res[position++] = (word >> 8) & 0xff; + } + if (position < res.length) { + res[position++] = (word >> 16) & 0xff; + } + + if (shift === 6) { + if (position < res.length) { + res[position++] = (word >> 24) & 0xff; + } + carry = 0; + shift = 0; + } else { + carry = word >>> 24; + shift += 2; + } + } + + if (position < res.length) { + res[position++] = carry; + + while (position < res.length) { + res[position++] = 0; + } + } + }; + + BN.prototype._toArrayLikeBE = function _toArrayLikeBE (res, byteLength) { + var position = res.length - 1; + var carry = 0; + + for (var i = 0, shift = 0; i < this.length; i++) { + var word = (this.words[i] << shift) | carry; + + res[position--] = word & 0xff; + if (position >= 0) { + res[position--] = (word >> 8) & 0xff; + } + if (position >= 0) { + res[position--] = (word >> 16) & 0xff; + } + + if (shift === 6) { + if (position >= 0) { + res[position--] = (word >> 24) & 0xff; + } + carry = 0; + shift = 0; + } else { + carry = word >>> 24; + shift += 2; + } + } + + if (position >= 0) { + res[position--] = carry; + + while (position >= 0) { + res[position--] = 0; + } + } + }; + + if (Math.clz32) { + BN.prototype._countBits = function _countBits (w) { + return 32 - Math.clz32(w); + }; + } else { + BN.prototype._countBits = function _countBits (w) { + var t = w; + var r = 0; + if (t >= 0x1000) { + r += 13; + t >>>= 13; + } + if (t >= 0x40) { + r += 7; + t >>>= 7; + } + if (t >= 0x8) { + r += 4; + t >>>= 4; + } + if (t >= 0x02) { + r += 2; + t >>>= 2; + } + return r + t; + }; + } + + BN.prototype._zeroBits = function _zeroBits (w) { + // Short-cut + if (w === 0) return 26; + + var t = w; + var r = 0; + if ((t & 0x1fff) === 0) { + r += 13; + t >>>= 13; + } + if ((t & 0x7f) === 0) { + r += 7; + t >>>= 7; + } + if ((t & 0xf) === 0) { + r += 4; + t >>>= 4; + } + if ((t & 0x3) === 0) { + r += 2; + t >>>= 2; + } + if ((t & 0x1) === 0) { + r++; + } + return r; + }; + + // Return number of used bits in a BN + BN.prototype.bitLength = function bitLength () { + var w = this.words[this.length - 1]; + var hi = this._countBits(w); + return (this.length - 1) * 26 + hi; + }; + + function toBitArray (num) { + var w = new Array(num.bitLength()); + + for (var bit = 0; bit < w.length; bit++) { + var off = (bit / 26) | 0; + var wbit = bit % 26; + + w[bit] = (num.words[off] >>> wbit) & 0x01; + } + + return w; + } + + // Number of trailing zero bits + BN.prototype.zeroBits = function zeroBits () { + if (this.isZero()) return 0; + + var r = 0; + for (var i = 0; i < this.length; i++) { + var b = this._zeroBits(this.words[i]); + r += b; + if (b !== 26) break; + } + return r; + }; + + BN.prototype.byteLength = function byteLength () { + return Math.ceil(this.bitLength() / 8); + }; + + BN.prototype.toTwos = function toTwos (width) { + if (this.negative !== 0) { + return this.abs().inotn(width).iaddn(1); + } + return this.clone(); + }; + + BN.prototype.fromTwos = function fromTwos (width) { + if (this.testn(width - 1)) { + return this.notn(width).iaddn(1).ineg(); + } + return this.clone(); + }; + + BN.prototype.isNeg = function isNeg () { + return this.negative !== 0; + }; + + // Return negative clone of `this` + BN.prototype.neg = function neg () { + return this.clone().ineg(); + }; + + BN.prototype.ineg = function ineg () { + if (!this.isZero()) { + this.negative ^= 1; + } + + return this; + }; + + // Or `num` with `this` in-place + BN.prototype.iuor = function iuor (num) { + while (this.length < num.length) { + this.words[this.length++] = 0; + } + + for (var i = 0; i < num.length; i++) { + this.words[i] = this.words[i] | num.words[i]; + } + + return this._strip(); + }; + + BN.prototype.ior = function ior (num) { + assert((this.negative | num.negative) === 0); + return this.iuor(num); + }; + + // Or `num` with `this` + BN.prototype.or = function or (num) { + if (this.length > num.length) return this.clone().ior(num); + return num.clone().ior(this); + }; + + BN.prototype.uor = function uor (num) { + if (this.length > num.length) return this.clone().iuor(num); + return num.clone().iuor(this); + }; + + // And `num` with `this` in-place + BN.prototype.iuand = function iuand (num) { + // b = min-length(num, this) + var b; + if (this.length > num.length) { + b = num; + } else { + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = this.words[i] & num.words[i]; + } + + this.length = b.length; + + return this._strip(); + }; + + BN.prototype.iand = function iand (num) { + assert((this.negative | num.negative) === 0); + return this.iuand(num); + }; + + // And `num` with `this` + BN.prototype.and = function and (num) { + if (this.length > num.length) return this.clone().iand(num); + return num.clone().iand(this); + }; + + BN.prototype.uand = function uand (num) { + if (this.length > num.length) return this.clone().iuand(num); + return num.clone().iuand(this); + }; + + // Xor `num` with `this` in-place + BN.prototype.iuxor = function iuxor (num) { + // a.length > b.length + var a; + var b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = a.words[i] ^ b.words[i]; + } + + if (this !== a) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = a.length; + + return this._strip(); + }; + + BN.prototype.ixor = function ixor (num) { + assert((this.negative | num.negative) === 0); + return this.iuxor(num); + }; + + // Xor `num` with `this` + BN.prototype.xor = function xor (num) { + if (this.length > num.length) return this.clone().ixor(num); + return num.clone().ixor(this); + }; + + BN.prototype.uxor = function uxor (num) { + if (this.length > num.length) return this.clone().iuxor(num); + return num.clone().iuxor(this); + }; + + // Not ``this`` with ``width`` bitwidth + BN.prototype.inotn = function inotn (width) { + assert(typeof width === 'number' && width >= 0); + + var bytesNeeded = Math.ceil(width / 26) | 0; + var bitsLeft = width % 26; + + // Extend the buffer with leading zeroes + this._expand(bytesNeeded); + + if (bitsLeft > 0) { + bytesNeeded--; + } + + // Handle complete words + for (var i = 0; i < bytesNeeded; i++) { + this.words[i] = ~this.words[i] & 0x3ffffff; + } + + // Handle the residue + if (bitsLeft > 0) { + this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); + } + + // And remove leading zeroes + return this._strip(); + }; + + BN.prototype.notn = function notn (width) { + return this.clone().inotn(width); + }; + + // Set `bit` of `this` + BN.prototype.setn = function setn (bit, val) { + assert(typeof bit === 'number' && bit >= 0); + + var off = (bit / 26) | 0; + var wbit = bit % 26; + + this._expand(off + 1); + + if (val) { + this.words[off] = this.words[off] | (1 << wbit); + } else { + this.words[off] = this.words[off] & ~(1 << wbit); + } + + return this._strip(); + }; + + // Add `num` to `this` in-place + BN.prototype.iadd = function iadd (num) { + var r; + + // negative + positive + if (this.negative !== 0 && num.negative === 0) { + this.negative = 0; + r = this.isub(num); + this.negative ^= 1; + return this._normSign(); + + // positive + negative + } else if (this.negative === 0 && num.negative !== 0) { + num.negative = 0; + r = this.isub(num); + num.negative = 1; + return r._normSign(); + } + + // a.length > b.length + var a, b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) + (b.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + + this.length = a.length; + if (carry !== 0) { + this.words[this.length] = carry; + this.length++; + // Copy the rest of the words + } else if (a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + return this; + }; + + // Add `num` to `this` + BN.prototype.add = function add (num) { + var res; + if (num.negative !== 0 && this.negative === 0) { + num.negative = 0; + res = this.sub(num); + num.negative ^= 1; + return res; + } else if (num.negative === 0 && this.negative !== 0) { + this.negative = 0; + res = num.sub(this); + this.negative = 1; + return res; + } + + if (this.length > num.length) return this.clone().iadd(num); + + return num.clone().iadd(this); + }; + + // Subtract `num` from `this` in-place + BN.prototype.isub = function isub (num) { + // this - (-num) = this + num + if (num.negative !== 0) { + num.negative = 0; + var r = this.iadd(num); + num.negative = 1; + return r._normSign(); + + // -this - num = -(this + num) + } else if (this.negative !== 0) { + this.negative = 0; + this.iadd(num); + this.negative = 1; + return this._normSign(); + } + + // At this point both numbers are positive + var cmp = this.cmp(num); + + // Optimization - zeroify + if (cmp === 0) { + this.negative = 0; + this.length = 1; + this.words[0] = 0; + return this; + } + + // a > b + var a, b; + if (cmp > 0) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) - (b.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + + // Copy rest of the words + if (carry === 0 && i < a.length && a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = Math.max(this.length, i); + + if (a !== this) { + this.negative = 1; + } + + return this._strip(); + }; + + // Subtract `num` from `this` + BN.prototype.sub = function sub (num) { + return this.clone().isub(num); + }; + + function smallMulTo (self, num, out) { + out.negative = num.negative ^ self.negative; + var len = (self.length + num.length) | 0; + out.length = len; + len = (len - 1) | 0; + + // Peel one iteration (compiler can't do it, because of code complexity) + var a = self.words[0] | 0; + var b = num.words[0] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + var carry = (r / 0x4000000) | 0; + out.words[0] = lo; + + for (var k = 1; k < len; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = carry >>> 26; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = (k - j) | 0; + a = self.words[i] | 0; + b = num.words[j] | 0; + r = a * b + rword; + ncarry += (r / 0x4000000) | 0; + rword = r & 0x3ffffff; + } + out.words[k] = rword | 0; + carry = ncarry | 0; + } + if (carry !== 0) { + out.words[k] = carry | 0; + } else { + out.length--; + } + + return out._strip(); + } + + // TODO(indutny): it may be reasonable to omit it for users who don't need + // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit + // multiplication (like elliptic secp256k1). + var comb10MulTo = function comb10MulTo (self, num, out) { + var a = self.words; + var b = num.words; + var o = out.words; + var c = 0; + var lo; + var mid; + var hi; + var a0 = a[0] | 0; + var al0 = a0 & 0x1fff; + var ah0 = a0 >>> 13; + var a1 = a[1] | 0; + var al1 = a1 & 0x1fff; + var ah1 = a1 >>> 13; + var a2 = a[2] | 0; + var al2 = a2 & 0x1fff; + var ah2 = a2 >>> 13; + var a3 = a[3] | 0; + var al3 = a3 & 0x1fff; + var ah3 = a3 >>> 13; + var a4 = a[4] | 0; + var al4 = a4 & 0x1fff; + var ah4 = a4 >>> 13; + var a5 = a[5] | 0; + var al5 = a5 & 0x1fff; + var ah5 = a5 >>> 13; + var a6 = a[6] | 0; + var al6 = a6 & 0x1fff; + var ah6 = a6 >>> 13; + var a7 = a[7] | 0; + var al7 = a7 & 0x1fff; + var ah7 = a7 >>> 13; + var a8 = a[8] | 0; + var al8 = a8 & 0x1fff; + var ah8 = a8 >>> 13; + var a9 = a[9] | 0; + var al9 = a9 & 0x1fff; + var ah9 = a9 >>> 13; + var b0 = b[0] | 0; + var bl0 = b0 & 0x1fff; + var bh0 = b0 >>> 13; + var b1 = b[1] | 0; + var bl1 = b1 & 0x1fff; + var bh1 = b1 >>> 13; + var b2 = b[2] | 0; + var bl2 = b2 & 0x1fff; + var bh2 = b2 >>> 13; + var b3 = b[3] | 0; + var bl3 = b3 & 0x1fff; + var bh3 = b3 >>> 13; + var b4 = b[4] | 0; + var bl4 = b4 & 0x1fff; + var bh4 = b4 >>> 13; + var b5 = b[5] | 0; + var bl5 = b5 & 0x1fff; + var bh5 = b5 >>> 13; + var b6 = b[6] | 0; + var bl6 = b6 & 0x1fff; + var bh6 = b6 >>> 13; + var b7 = b[7] | 0; + var bl7 = b7 & 0x1fff; + var bh7 = b7 >>> 13; + var b8 = b[8] | 0; + var bl8 = b8 & 0x1fff; + var bh8 = b8 >>> 13; + var b9 = b[9] | 0; + var bl9 = b9 & 0x1fff; + var bh9 = b9 >>> 13; + + out.negative = self.negative ^ num.negative; + out.length = 19; + /* k = 0 */ + lo = Math.imul(al0, bl0); + mid = Math.imul(al0, bh0); + mid = (mid + Math.imul(ah0, bl0)) | 0; + hi = Math.imul(ah0, bh0); + var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; + w0 &= 0x3ffffff; + /* k = 1 */ + lo = Math.imul(al1, bl0); + mid = Math.imul(al1, bh0); + mid = (mid + Math.imul(ah1, bl0)) | 0; + hi = Math.imul(ah1, bh0); + lo = (lo + Math.imul(al0, bl1)) | 0; + mid = (mid + Math.imul(al0, bh1)) | 0; + mid = (mid + Math.imul(ah0, bl1)) | 0; + hi = (hi + Math.imul(ah0, bh1)) | 0; + var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; + w1 &= 0x3ffffff; + /* k = 2 */ + lo = Math.imul(al2, bl0); + mid = Math.imul(al2, bh0); + mid = (mid + Math.imul(ah2, bl0)) | 0; + hi = Math.imul(ah2, bh0); + lo = (lo + Math.imul(al1, bl1)) | 0; + mid = (mid + Math.imul(al1, bh1)) | 0; + mid = (mid + Math.imul(ah1, bl1)) | 0; + hi = (hi + Math.imul(ah1, bh1)) | 0; + lo = (lo + Math.imul(al0, bl2)) | 0; + mid = (mid + Math.imul(al0, bh2)) | 0; + mid = (mid + Math.imul(ah0, bl2)) | 0; + hi = (hi + Math.imul(ah0, bh2)) | 0; + var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; + w2 &= 0x3ffffff; + /* k = 3 */ + lo = Math.imul(al3, bl0); + mid = Math.imul(al3, bh0); + mid = (mid + Math.imul(ah3, bl0)) | 0; + hi = Math.imul(ah3, bh0); + lo = (lo + Math.imul(al2, bl1)) | 0; + mid = (mid + Math.imul(al2, bh1)) | 0; + mid = (mid + Math.imul(ah2, bl1)) | 0; + hi = (hi + Math.imul(ah2, bh1)) | 0; + lo = (lo + Math.imul(al1, bl2)) | 0; + mid = (mid + Math.imul(al1, bh2)) | 0; + mid = (mid + Math.imul(ah1, bl2)) | 0; + hi = (hi + Math.imul(ah1, bh2)) | 0; + lo = (lo + Math.imul(al0, bl3)) | 0; + mid = (mid + Math.imul(al0, bh3)) | 0; + mid = (mid + Math.imul(ah0, bl3)) | 0; + hi = (hi + Math.imul(ah0, bh3)) | 0; + var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; + w3 &= 0x3ffffff; + /* k = 4 */ + lo = Math.imul(al4, bl0); + mid = Math.imul(al4, bh0); + mid = (mid + Math.imul(ah4, bl0)) | 0; + hi = Math.imul(ah4, bh0); + lo = (lo + Math.imul(al3, bl1)) | 0; + mid = (mid + Math.imul(al3, bh1)) | 0; + mid = (mid + Math.imul(ah3, bl1)) | 0; + hi = (hi + Math.imul(ah3, bh1)) | 0; + lo = (lo + Math.imul(al2, bl2)) | 0; + mid = (mid + Math.imul(al2, bh2)) | 0; + mid = (mid + Math.imul(ah2, bl2)) | 0; + hi = (hi + Math.imul(ah2, bh2)) | 0; + lo = (lo + Math.imul(al1, bl3)) | 0; + mid = (mid + Math.imul(al1, bh3)) | 0; + mid = (mid + Math.imul(ah1, bl3)) | 0; + hi = (hi + Math.imul(ah1, bh3)) | 0; + lo = (lo + Math.imul(al0, bl4)) | 0; + mid = (mid + Math.imul(al0, bh4)) | 0; + mid = (mid + Math.imul(ah0, bl4)) | 0; + hi = (hi + Math.imul(ah0, bh4)) | 0; + var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; + w4 &= 0x3ffffff; + /* k = 5 */ + lo = Math.imul(al5, bl0); + mid = Math.imul(al5, bh0); + mid = (mid + Math.imul(ah5, bl0)) | 0; + hi = Math.imul(ah5, bh0); + lo = (lo + Math.imul(al4, bl1)) | 0; + mid = (mid + Math.imul(al4, bh1)) | 0; + mid = (mid + Math.imul(ah4, bl1)) | 0; + hi = (hi + Math.imul(ah4, bh1)) | 0; + lo = (lo + Math.imul(al3, bl2)) | 0; + mid = (mid + Math.imul(al3, bh2)) | 0; + mid = (mid + Math.imul(ah3, bl2)) | 0; + hi = (hi + Math.imul(ah3, bh2)) | 0; + lo = (lo + Math.imul(al2, bl3)) | 0; + mid = (mid + Math.imul(al2, bh3)) | 0; + mid = (mid + Math.imul(ah2, bl3)) | 0; + hi = (hi + Math.imul(ah2, bh3)) | 0; + lo = (lo + Math.imul(al1, bl4)) | 0; + mid = (mid + Math.imul(al1, bh4)) | 0; + mid = (mid + Math.imul(ah1, bl4)) | 0; + hi = (hi + Math.imul(ah1, bh4)) | 0; + lo = (lo + Math.imul(al0, bl5)) | 0; + mid = (mid + Math.imul(al0, bh5)) | 0; + mid = (mid + Math.imul(ah0, bl5)) | 0; + hi = (hi + Math.imul(ah0, bh5)) | 0; + var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; + w5 &= 0x3ffffff; + /* k = 6 */ + lo = Math.imul(al6, bl0); + mid = Math.imul(al6, bh0); + mid = (mid + Math.imul(ah6, bl0)) | 0; + hi = Math.imul(ah6, bh0); + lo = (lo + Math.imul(al5, bl1)) | 0; + mid = (mid + Math.imul(al5, bh1)) | 0; + mid = (mid + Math.imul(ah5, bl1)) | 0; + hi = (hi + Math.imul(ah5, bh1)) | 0; + lo = (lo + Math.imul(al4, bl2)) | 0; + mid = (mid + Math.imul(al4, bh2)) | 0; + mid = (mid + Math.imul(ah4, bl2)) | 0; + hi = (hi + Math.imul(ah4, bh2)) | 0; + lo = (lo + Math.imul(al3, bl3)) | 0; + mid = (mid + Math.imul(al3, bh3)) | 0; + mid = (mid + Math.imul(ah3, bl3)) | 0; + hi = (hi + Math.imul(ah3, bh3)) | 0; + lo = (lo + Math.imul(al2, bl4)) | 0; + mid = (mid + Math.imul(al2, bh4)) | 0; + mid = (mid + Math.imul(ah2, bl4)) | 0; + hi = (hi + Math.imul(ah2, bh4)) | 0; + lo = (lo + Math.imul(al1, bl5)) | 0; + mid = (mid + Math.imul(al1, bh5)) | 0; + mid = (mid + Math.imul(ah1, bl5)) | 0; + hi = (hi + Math.imul(ah1, bh5)) | 0; + lo = (lo + Math.imul(al0, bl6)) | 0; + mid = (mid + Math.imul(al0, bh6)) | 0; + mid = (mid + Math.imul(ah0, bl6)) | 0; + hi = (hi + Math.imul(ah0, bh6)) | 0; + var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; + w6 &= 0x3ffffff; + /* k = 7 */ + lo = Math.imul(al7, bl0); + mid = Math.imul(al7, bh0); + mid = (mid + Math.imul(ah7, bl0)) | 0; + hi = Math.imul(ah7, bh0); + lo = (lo + Math.imul(al6, bl1)) | 0; + mid = (mid + Math.imul(al6, bh1)) | 0; + mid = (mid + Math.imul(ah6, bl1)) | 0; + hi = (hi + Math.imul(ah6, bh1)) | 0; + lo = (lo + Math.imul(al5, bl2)) | 0; + mid = (mid + Math.imul(al5, bh2)) | 0; + mid = (mid + Math.imul(ah5, bl2)) | 0; + hi = (hi + Math.imul(ah5, bh2)) | 0; + lo = (lo + Math.imul(al4, bl3)) | 0; + mid = (mid + Math.imul(al4, bh3)) | 0; + mid = (mid + Math.imul(ah4, bl3)) | 0; + hi = (hi + Math.imul(ah4, bh3)) | 0; + lo = (lo + Math.imul(al3, bl4)) | 0; + mid = (mid + Math.imul(al3, bh4)) | 0; + mid = (mid + Math.imul(ah3, bl4)) | 0; + hi = (hi + Math.imul(ah3, bh4)) | 0; + lo = (lo + Math.imul(al2, bl5)) | 0; + mid = (mid + Math.imul(al2, bh5)) | 0; + mid = (mid + Math.imul(ah2, bl5)) | 0; + hi = (hi + Math.imul(ah2, bh5)) | 0; + lo = (lo + Math.imul(al1, bl6)) | 0; + mid = (mid + Math.imul(al1, bh6)) | 0; + mid = (mid + Math.imul(ah1, bl6)) | 0; + hi = (hi + Math.imul(ah1, bh6)) | 0; + lo = (lo + Math.imul(al0, bl7)) | 0; + mid = (mid + Math.imul(al0, bh7)) | 0; + mid = (mid + Math.imul(ah0, bl7)) | 0; + hi = (hi + Math.imul(ah0, bh7)) | 0; + var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; + w7 &= 0x3ffffff; + /* k = 8 */ + lo = Math.imul(al8, bl0); + mid = Math.imul(al8, bh0); + mid = (mid + Math.imul(ah8, bl0)) | 0; + hi = Math.imul(ah8, bh0); + lo = (lo + Math.imul(al7, bl1)) | 0; + mid = (mid + Math.imul(al7, bh1)) | 0; + mid = (mid + Math.imul(ah7, bl1)) | 0; + hi = (hi + Math.imul(ah7, bh1)) | 0; + lo = (lo + Math.imul(al6, bl2)) | 0; + mid = (mid + Math.imul(al6, bh2)) | 0; + mid = (mid + Math.imul(ah6, bl2)) | 0; + hi = (hi + Math.imul(ah6, bh2)) | 0; + lo = (lo + Math.imul(al5, bl3)) | 0; + mid = (mid + Math.imul(al5, bh3)) | 0; + mid = (mid + Math.imul(ah5, bl3)) | 0; + hi = (hi + Math.imul(ah5, bh3)) | 0; + lo = (lo + Math.imul(al4, bl4)) | 0; + mid = (mid + Math.imul(al4, bh4)) | 0; + mid = (mid + Math.imul(ah4, bl4)) | 0; + hi = (hi + Math.imul(ah4, bh4)) | 0; + lo = (lo + Math.imul(al3, bl5)) | 0; + mid = (mid + Math.imul(al3, bh5)) | 0; + mid = (mid + Math.imul(ah3, bl5)) | 0; + hi = (hi + Math.imul(ah3, bh5)) | 0; + lo = (lo + Math.imul(al2, bl6)) | 0; + mid = (mid + Math.imul(al2, bh6)) | 0; + mid = (mid + Math.imul(ah2, bl6)) | 0; + hi = (hi + Math.imul(ah2, bh6)) | 0; + lo = (lo + Math.imul(al1, bl7)) | 0; + mid = (mid + Math.imul(al1, bh7)) | 0; + mid = (mid + Math.imul(ah1, bl7)) | 0; + hi = (hi + Math.imul(ah1, bh7)) | 0; + lo = (lo + Math.imul(al0, bl8)) | 0; + mid = (mid + Math.imul(al0, bh8)) | 0; + mid = (mid + Math.imul(ah0, bl8)) | 0; + hi = (hi + Math.imul(ah0, bh8)) | 0; + var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; + w8 &= 0x3ffffff; + /* k = 9 */ + lo = Math.imul(al9, bl0); + mid = Math.imul(al9, bh0); + mid = (mid + Math.imul(ah9, bl0)) | 0; + hi = Math.imul(ah9, bh0); + lo = (lo + Math.imul(al8, bl1)) | 0; + mid = (mid + Math.imul(al8, bh1)) | 0; + mid = (mid + Math.imul(ah8, bl1)) | 0; + hi = (hi + Math.imul(ah8, bh1)) | 0; + lo = (lo + Math.imul(al7, bl2)) | 0; + mid = (mid + Math.imul(al7, bh2)) | 0; + mid = (mid + Math.imul(ah7, bl2)) | 0; + hi = (hi + Math.imul(ah7, bh2)) | 0; + lo = (lo + Math.imul(al6, bl3)) | 0; + mid = (mid + Math.imul(al6, bh3)) | 0; + mid = (mid + Math.imul(ah6, bl3)) | 0; + hi = (hi + Math.imul(ah6, bh3)) | 0; + lo = (lo + Math.imul(al5, bl4)) | 0; + mid = (mid + Math.imul(al5, bh4)) | 0; + mid = (mid + Math.imul(ah5, bl4)) | 0; + hi = (hi + Math.imul(ah5, bh4)) | 0; + lo = (lo + Math.imul(al4, bl5)) | 0; + mid = (mid + Math.imul(al4, bh5)) | 0; + mid = (mid + Math.imul(ah4, bl5)) | 0; + hi = (hi + Math.imul(ah4, bh5)) | 0; + lo = (lo + Math.imul(al3, bl6)) | 0; + mid = (mid + Math.imul(al3, bh6)) | 0; + mid = (mid + Math.imul(ah3, bl6)) | 0; + hi = (hi + Math.imul(ah3, bh6)) | 0; + lo = (lo + Math.imul(al2, bl7)) | 0; + mid = (mid + Math.imul(al2, bh7)) | 0; + mid = (mid + Math.imul(ah2, bl7)) | 0; + hi = (hi + Math.imul(ah2, bh7)) | 0; + lo = (lo + Math.imul(al1, bl8)) | 0; + mid = (mid + Math.imul(al1, bh8)) | 0; + mid = (mid + Math.imul(ah1, bl8)) | 0; + hi = (hi + Math.imul(ah1, bh8)) | 0; + lo = (lo + Math.imul(al0, bl9)) | 0; + mid = (mid + Math.imul(al0, bh9)) | 0; + mid = (mid + Math.imul(ah0, bl9)) | 0; + hi = (hi + Math.imul(ah0, bh9)) | 0; + var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; + w9 &= 0x3ffffff; + /* k = 10 */ + lo = Math.imul(al9, bl1); + mid = Math.imul(al9, bh1); + mid = (mid + Math.imul(ah9, bl1)) | 0; + hi = Math.imul(ah9, bh1); + lo = (lo + Math.imul(al8, bl2)) | 0; + mid = (mid + Math.imul(al8, bh2)) | 0; + mid = (mid + Math.imul(ah8, bl2)) | 0; + hi = (hi + Math.imul(ah8, bh2)) | 0; + lo = (lo + Math.imul(al7, bl3)) | 0; + mid = (mid + Math.imul(al7, bh3)) | 0; + mid = (mid + Math.imul(ah7, bl3)) | 0; + hi = (hi + Math.imul(ah7, bh3)) | 0; + lo = (lo + Math.imul(al6, bl4)) | 0; + mid = (mid + Math.imul(al6, bh4)) | 0; + mid = (mid + Math.imul(ah6, bl4)) | 0; + hi = (hi + Math.imul(ah6, bh4)) | 0; + lo = (lo + Math.imul(al5, bl5)) | 0; + mid = (mid + Math.imul(al5, bh5)) | 0; + mid = (mid + Math.imul(ah5, bl5)) | 0; + hi = (hi + Math.imul(ah5, bh5)) | 0; + lo = (lo + Math.imul(al4, bl6)) | 0; + mid = (mid + Math.imul(al4, bh6)) | 0; + mid = (mid + Math.imul(ah4, bl6)) | 0; + hi = (hi + Math.imul(ah4, bh6)) | 0; + lo = (lo + Math.imul(al3, bl7)) | 0; + mid = (mid + Math.imul(al3, bh7)) | 0; + mid = (mid + Math.imul(ah3, bl7)) | 0; + hi = (hi + Math.imul(ah3, bh7)) | 0; + lo = (lo + Math.imul(al2, bl8)) | 0; + mid = (mid + Math.imul(al2, bh8)) | 0; + mid = (mid + Math.imul(ah2, bl8)) | 0; + hi = (hi + Math.imul(ah2, bh8)) | 0; + lo = (lo + Math.imul(al1, bl9)) | 0; + mid = (mid + Math.imul(al1, bh9)) | 0; + mid = (mid + Math.imul(ah1, bl9)) | 0; + hi = (hi + Math.imul(ah1, bh9)) | 0; + var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; + w10 &= 0x3ffffff; + /* k = 11 */ + lo = Math.imul(al9, bl2); + mid = Math.imul(al9, bh2); + mid = (mid + Math.imul(ah9, bl2)) | 0; + hi = Math.imul(ah9, bh2); + lo = (lo + Math.imul(al8, bl3)) | 0; + mid = (mid + Math.imul(al8, bh3)) | 0; + mid = (mid + Math.imul(ah8, bl3)) | 0; + hi = (hi + Math.imul(ah8, bh3)) | 0; + lo = (lo + Math.imul(al7, bl4)) | 0; + mid = (mid + Math.imul(al7, bh4)) | 0; + mid = (mid + Math.imul(ah7, bl4)) | 0; + hi = (hi + Math.imul(ah7, bh4)) | 0; + lo = (lo + Math.imul(al6, bl5)) | 0; + mid = (mid + Math.imul(al6, bh5)) | 0; + mid = (mid + Math.imul(ah6, bl5)) | 0; + hi = (hi + Math.imul(ah6, bh5)) | 0; + lo = (lo + Math.imul(al5, bl6)) | 0; + mid = (mid + Math.imul(al5, bh6)) | 0; + mid = (mid + Math.imul(ah5, bl6)) | 0; + hi = (hi + Math.imul(ah5, bh6)) | 0; + lo = (lo + Math.imul(al4, bl7)) | 0; + mid = (mid + Math.imul(al4, bh7)) | 0; + mid = (mid + Math.imul(ah4, bl7)) | 0; + hi = (hi + Math.imul(ah4, bh7)) | 0; + lo = (lo + Math.imul(al3, bl8)) | 0; + mid = (mid + Math.imul(al3, bh8)) | 0; + mid = (mid + Math.imul(ah3, bl8)) | 0; + hi = (hi + Math.imul(ah3, bh8)) | 0; + lo = (lo + Math.imul(al2, bl9)) | 0; + mid = (mid + Math.imul(al2, bh9)) | 0; + mid = (mid + Math.imul(ah2, bl9)) | 0; + hi = (hi + Math.imul(ah2, bh9)) | 0; + var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; + w11 &= 0x3ffffff; + /* k = 12 */ + lo = Math.imul(al9, bl3); + mid = Math.imul(al9, bh3); + mid = (mid + Math.imul(ah9, bl3)) | 0; + hi = Math.imul(ah9, bh3); + lo = (lo + Math.imul(al8, bl4)) | 0; + mid = (mid + Math.imul(al8, bh4)) | 0; + mid = (mid + Math.imul(ah8, bl4)) | 0; + hi = (hi + Math.imul(ah8, bh4)) | 0; + lo = (lo + Math.imul(al7, bl5)) | 0; + mid = (mid + Math.imul(al7, bh5)) | 0; + mid = (mid + Math.imul(ah7, bl5)) | 0; + hi = (hi + Math.imul(ah7, bh5)) | 0; + lo = (lo + Math.imul(al6, bl6)) | 0; + mid = (mid + Math.imul(al6, bh6)) | 0; + mid = (mid + Math.imul(ah6, bl6)) | 0; + hi = (hi + Math.imul(ah6, bh6)) | 0; + lo = (lo + Math.imul(al5, bl7)) | 0; + mid = (mid + Math.imul(al5, bh7)) | 0; + mid = (mid + Math.imul(ah5, bl7)) | 0; + hi = (hi + Math.imul(ah5, bh7)) | 0; + lo = (lo + Math.imul(al4, bl8)) | 0; + mid = (mid + Math.imul(al4, bh8)) | 0; + mid = (mid + Math.imul(ah4, bl8)) | 0; + hi = (hi + Math.imul(ah4, bh8)) | 0; + lo = (lo + Math.imul(al3, bl9)) | 0; + mid = (mid + Math.imul(al3, bh9)) | 0; + mid = (mid + Math.imul(ah3, bl9)) | 0; + hi = (hi + Math.imul(ah3, bh9)) | 0; + var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; + w12 &= 0x3ffffff; + /* k = 13 */ + lo = Math.imul(al9, bl4); + mid = Math.imul(al9, bh4); + mid = (mid + Math.imul(ah9, bl4)) | 0; + hi = Math.imul(ah9, bh4); + lo = (lo + Math.imul(al8, bl5)) | 0; + mid = (mid + Math.imul(al8, bh5)) | 0; + mid = (mid + Math.imul(ah8, bl5)) | 0; + hi = (hi + Math.imul(ah8, bh5)) | 0; + lo = (lo + Math.imul(al7, bl6)) | 0; + mid = (mid + Math.imul(al7, bh6)) | 0; + mid = (mid + Math.imul(ah7, bl6)) | 0; + hi = (hi + Math.imul(ah7, bh6)) | 0; + lo = (lo + Math.imul(al6, bl7)) | 0; + mid = (mid + Math.imul(al6, bh7)) | 0; + mid = (mid + Math.imul(ah6, bl7)) | 0; + hi = (hi + Math.imul(ah6, bh7)) | 0; + lo = (lo + Math.imul(al5, bl8)) | 0; + mid = (mid + Math.imul(al5, bh8)) | 0; + mid = (mid + Math.imul(ah5, bl8)) | 0; + hi = (hi + Math.imul(ah5, bh8)) | 0; + lo = (lo + Math.imul(al4, bl9)) | 0; + mid = (mid + Math.imul(al4, bh9)) | 0; + mid = (mid + Math.imul(ah4, bl9)) | 0; + hi = (hi + Math.imul(ah4, bh9)) | 0; + var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; + w13 &= 0x3ffffff; + /* k = 14 */ + lo = Math.imul(al9, bl5); + mid = Math.imul(al9, bh5); + mid = (mid + Math.imul(ah9, bl5)) | 0; + hi = Math.imul(ah9, bh5); + lo = (lo + Math.imul(al8, bl6)) | 0; + mid = (mid + Math.imul(al8, bh6)) | 0; + mid = (mid + Math.imul(ah8, bl6)) | 0; + hi = (hi + Math.imul(ah8, bh6)) | 0; + lo = (lo + Math.imul(al7, bl7)) | 0; + mid = (mid + Math.imul(al7, bh7)) | 0; + mid = (mid + Math.imul(ah7, bl7)) | 0; + hi = (hi + Math.imul(ah7, bh7)) | 0; + lo = (lo + Math.imul(al6, bl8)) | 0; + mid = (mid + Math.imul(al6, bh8)) | 0; + mid = (mid + Math.imul(ah6, bl8)) | 0; + hi = (hi + Math.imul(ah6, bh8)) | 0; + lo = (lo + Math.imul(al5, bl9)) | 0; + mid = (mid + Math.imul(al5, bh9)) | 0; + mid = (mid + Math.imul(ah5, bl9)) | 0; + hi = (hi + Math.imul(ah5, bh9)) | 0; + var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; + w14 &= 0x3ffffff; + /* k = 15 */ + lo = Math.imul(al9, bl6); + mid = Math.imul(al9, bh6); + mid = (mid + Math.imul(ah9, bl6)) | 0; + hi = Math.imul(ah9, bh6); + lo = (lo + Math.imul(al8, bl7)) | 0; + mid = (mid + Math.imul(al8, bh7)) | 0; + mid = (mid + Math.imul(ah8, bl7)) | 0; + hi = (hi + Math.imul(ah8, bh7)) | 0; + lo = (lo + Math.imul(al7, bl8)) | 0; + mid = (mid + Math.imul(al7, bh8)) | 0; + mid = (mid + Math.imul(ah7, bl8)) | 0; + hi = (hi + Math.imul(ah7, bh8)) | 0; + lo = (lo + Math.imul(al6, bl9)) | 0; + mid = (mid + Math.imul(al6, bh9)) | 0; + mid = (mid + Math.imul(ah6, bl9)) | 0; + hi = (hi + Math.imul(ah6, bh9)) | 0; + var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; + w15 &= 0x3ffffff; + /* k = 16 */ + lo = Math.imul(al9, bl7); + mid = Math.imul(al9, bh7); + mid = (mid + Math.imul(ah9, bl7)) | 0; + hi = Math.imul(ah9, bh7); + lo = (lo + Math.imul(al8, bl8)) | 0; + mid = (mid + Math.imul(al8, bh8)) | 0; + mid = (mid + Math.imul(ah8, bl8)) | 0; + hi = (hi + Math.imul(ah8, bh8)) | 0; + lo = (lo + Math.imul(al7, bl9)) | 0; + mid = (mid + Math.imul(al7, bh9)) | 0; + mid = (mid + Math.imul(ah7, bl9)) | 0; + hi = (hi + Math.imul(ah7, bh9)) | 0; + var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; + w16 &= 0x3ffffff; + /* k = 17 */ + lo = Math.imul(al9, bl8); + mid = Math.imul(al9, bh8); + mid = (mid + Math.imul(ah9, bl8)) | 0; + hi = Math.imul(ah9, bh8); + lo = (lo + Math.imul(al8, bl9)) | 0; + mid = (mid + Math.imul(al8, bh9)) | 0; + mid = (mid + Math.imul(ah8, bl9)) | 0; + hi = (hi + Math.imul(ah8, bh9)) | 0; + var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; + w17 &= 0x3ffffff; + /* k = 18 */ + lo = Math.imul(al9, bl9); + mid = Math.imul(al9, bh9); + mid = (mid + Math.imul(ah9, bl9)) | 0; + hi = Math.imul(ah9, bh9); + var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; + w18 &= 0x3ffffff; + o[0] = w0; + o[1] = w1; + o[2] = w2; + o[3] = w3; + o[4] = w4; + o[5] = w5; + o[6] = w6; + o[7] = w7; + o[8] = w8; + o[9] = w9; + o[10] = w10; + o[11] = w11; + o[12] = w12; + o[13] = w13; + o[14] = w14; + o[15] = w15; + o[16] = w16; + o[17] = w17; + o[18] = w18; + if (c !== 0) { + o[19] = c; + out.length++; + } + return out; + }; + + // Polyfill comb + if (!Math.imul) { + comb10MulTo = smallMulTo; + } + + function bigMulTo (self, num, out) { + out.negative = num.negative ^ self.negative; + out.length = self.length + num.length; + + var carry = 0; + var hncarry = 0; + for (var k = 0; k < out.length - 1; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = hncarry; + hncarry = 0; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = k - j; + var a = self.words[i] | 0; + var b = num.words[j] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; + lo = (lo + rword) | 0; + rword = lo & 0x3ffffff; + ncarry = (ncarry + (lo >>> 26)) | 0; + + hncarry += ncarry >>> 26; + ncarry &= 0x3ffffff; + } + out.words[k] = rword; + carry = ncarry; + ncarry = hncarry; + } + if (carry !== 0) { + out.words[k] = carry; + } else { + out.length--; + } + + return out._strip(); + } + + function jumboMulTo (self, num, out) { + // Temporary disable, see https://github.com/indutny/bn.js/issues/211 + // var fftm = new FFTM(); + // return fftm.mulp(self, num, out); + return bigMulTo(self, num, out); + } + + BN.prototype.mulTo = function mulTo (num, out) { + var res; + var len = this.length + num.length; + if (this.length === 10 && num.length === 10) { + res = comb10MulTo(this, num, out); + } else if (len < 63) { + res = smallMulTo(this, num, out); + } else if (len < 1024) { + res = bigMulTo(this, num, out); + } else { + res = jumboMulTo(this, num, out); + } + + return res; + }; + + // Cooley-Tukey algorithm for FFT + // slightly revisited to rely on looping instead of recursion + + function FFTM (x, y) { + this.x = x; + this.y = y; + } + + FFTM.prototype.makeRBT = function makeRBT (N) { + var t = new Array(N); + var l = BN.prototype._countBits(N) - 1; + for (var i = 0; i < N; i++) { + t[i] = this.revBin(i, l, N); + } + + return t; + }; + + // Returns binary-reversed representation of `x` + FFTM.prototype.revBin = function revBin (x, l, N) { + if (x === 0 || x === N - 1) return x; + + var rb = 0; + for (var i = 0; i < l; i++) { + rb |= (x & 1) << (l - i - 1); + x >>= 1; + } + + return rb; + }; + + // Performs "tweedling" phase, therefore 'emulating' + // behaviour of the recursive algorithm + FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { + for (var i = 0; i < N; i++) { + rtws[i] = rws[rbt[i]]; + itws[i] = iws[rbt[i]]; + } + }; + + FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { + this.permute(rbt, rws, iws, rtws, itws, N); + + for (var s = 1; s < N; s <<= 1) { + var l = s << 1; + + var rtwdf = Math.cos(2 * Math.PI / l); + var itwdf = Math.sin(2 * Math.PI / l); + + for (var p = 0; p < N; p += l) { + var rtwdf_ = rtwdf; + var itwdf_ = itwdf; + + for (var j = 0; j < s; j++) { + var re = rtws[p + j]; + var ie = itws[p + j]; + + var ro = rtws[p + j + s]; + var io = itws[p + j + s]; + + var rx = rtwdf_ * ro - itwdf_ * io; + + io = rtwdf_ * io + itwdf_ * ro; + ro = rx; + + rtws[p + j] = re + ro; + itws[p + j] = ie + io; + + rtws[p + j + s] = re - ro; + itws[p + j + s] = ie - io; + + /* jshint maxdepth : false */ + if (j !== l) { + rx = rtwdf * rtwdf_ - itwdf * itwdf_; + + itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; + rtwdf_ = rx; + } + } + } + } + }; + + FFTM.prototype.guessLen13b = function guessLen13b (n, m) { + var N = Math.max(m, n) | 1; + var odd = N & 1; + var i = 0; + for (N = N / 2 | 0; N; N = N >>> 1) { + i++; + } + + return 1 << i + 1 + odd; + }; + + FFTM.prototype.conjugate = function conjugate (rws, iws, N) { + if (N <= 1) return; + + for (var i = 0; i < N / 2; i++) { + var t = rws[i]; + + rws[i] = rws[N - i - 1]; + rws[N - i - 1] = t; + + t = iws[i]; + + iws[i] = -iws[N - i - 1]; + iws[N - i - 1] = -t; + } + }; + + FFTM.prototype.normalize13b = function normalize13b (ws, N) { + var carry = 0; + for (var i = 0; i < N / 2; i++) { + var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + + Math.round(ws[2 * i] / N) + + carry; + + ws[i] = w & 0x3ffffff; + + if (w < 0x4000000) { + carry = 0; + } else { + carry = w / 0x4000000 | 0; + } + } + + return ws; + }; + + FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { + var carry = 0; + for (var i = 0; i < len; i++) { + carry = carry + (ws[i] | 0); + + rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; + rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; + } + + // Pad with zeroes + for (i = 2 * len; i < N; ++i) { + rws[i] = 0; + } + + assert(carry === 0); + assert((carry & ~0x1fff) === 0); + }; + + FFTM.prototype.stub = function stub (N) { + var ph = new Array(N); + for (var i = 0; i < N; i++) { + ph[i] = 0; + } + + return ph; + }; + + FFTM.prototype.mulp = function mulp (x, y, out) { + var N = 2 * this.guessLen13b(x.length, y.length); + + var rbt = this.makeRBT(N); + + var _ = this.stub(N); + + var rws = new Array(N); + var rwst = new Array(N); + var iwst = new Array(N); + + var nrws = new Array(N); + var nrwst = new Array(N); + var niwst = new Array(N); + + var rmws = out.words; + rmws.length = N; + + this.convert13b(x.words, x.length, rws, N); + this.convert13b(y.words, y.length, nrws, N); + + this.transform(rws, _, rwst, iwst, N, rbt); + this.transform(nrws, _, nrwst, niwst, N, rbt); + + for (var i = 0; i < N; i++) { + var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; + iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; + rwst[i] = rx; + } + + this.conjugate(rwst, iwst, N); + this.transform(rwst, iwst, rmws, _, N, rbt); + this.conjugate(rmws, _, N); + this.normalize13b(rmws, N); + + out.negative = x.negative ^ y.negative; + out.length = x.length + y.length; + return out._strip(); + }; + + // Multiply `this` by `num` + BN.prototype.mul = function mul (num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return this.mulTo(num, out); + }; + + // Multiply employing FFT + BN.prototype.mulf = function mulf (num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return jumboMulTo(this, num, out); + }; + + // In-place Multiplication + BN.prototype.imul = function imul (num) { + return this.clone().mulTo(num, this); + }; + + BN.prototype.imuln = function imuln (num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(typeof num === 'number'); + assert(num < 0x4000000); + + // Carry + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = (this.words[i] | 0) * num; + var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); + carry >>= 26; + carry += (w / 0x4000000) | 0; + // NOTE: lo is 27bit maximum + carry += lo >>> 26; + this.words[i] = lo & 0x3ffffff; + } + + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + + return isNegNum ? this.ineg() : this; + }; + + BN.prototype.muln = function muln (num) { + return this.clone().imuln(num); + }; + + // `this` * `this` + BN.prototype.sqr = function sqr () { + return this.mul(this); + }; + + // `this` * `this` in-place + BN.prototype.isqr = function isqr () { + return this.imul(this.clone()); + }; + + // Math.pow(`this`, `num`) + BN.prototype.pow = function pow (num) { + var w = toBitArray(num); + if (w.length === 0) return new BN(1); + + // Skip leading zeroes + var res = this; + for (var i = 0; i < w.length; i++, res = res.sqr()) { + if (w[i] !== 0) break; + } + + if (++i < w.length) { + for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { + if (w[i] === 0) continue; + + res = res.mul(q); + } + } + + return res; + }; + + // Shift-left in-place + BN.prototype.iushln = function iushln (bits) { + assert(typeof bits === 'number' && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); + var i; + + if (r !== 0) { + var carry = 0; + + for (i = 0; i < this.length; i++) { + var newCarry = this.words[i] & carryMask; + var c = ((this.words[i] | 0) - newCarry) << r; + this.words[i] = c | carry; + carry = newCarry >>> (26 - r); + } + + if (carry) { + this.words[i] = carry; + this.length++; + } + } + + if (s !== 0) { + for (i = this.length - 1; i >= 0; i--) { + this.words[i + s] = this.words[i]; + } + + for (i = 0; i < s; i++) { + this.words[i] = 0; + } + + this.length += s; + } + + return this._strip(); + }; + + BN.prototype.ishln = function ishln (bits) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushln(bits); + }; + + // Shift-right in-place + // NOTE: `hint` is a lowest bit before trailing zeroes + // NOTE: if `extended` is present - it will be filled with destroyed bits + BN.prototype.iushrn = function iushrn (bits, hint, extended) { + assert(typeof bits === 'number' && bits >= 0); + var h; + if (hint) { + h = (hint - (hint % 26)) / 26; + } else { + h = 0; + } + + var r = bits % 26; + var s = Math.min((bits - r) / 26, this.length); + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + var maskedWords = extended; + + h -= s; + h = Math.max(0, h); + + // Extended mode, copy masked part + if (maskedWords) { + for (var i = 0; i < s; i++) { + maskedWords.words[i] = this.words[i]; + } + maskedWords.length = s; + } + + if (s === 0) { + // No-op, we should not move anything at all + } else if (this.length > s) { + this.length -= s; + for (i = 0; i < this.length; i++) { + this.words[i] = this.words[i + s]; + } + } else { + this.words[0] = 0; + this.length = 1; + } + + var carry = 0; + for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { + var word = this.words[i] | 0; + this.words[i] = (carry << (26 - r)) | (word >>> r); + carry = word & mask; + } + + // Push carried bits as a mask + if (maskedWords && carry !== 0) { + maskedWords.words[maskedWords.length++] = carry; + } + + if (this.length === 0) { + this.words[0] = 0; + this.length = 1; + } + + return this._strip(); + }; + + BN.prototype.ishrn = function ishrn (bits, hint, extended) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushrn(bits, hint, extended); + }; + + // Shift-left + BN.prototype.shln = function shln (bits) { + return this.clone().ishln(bits); + }; + + BN.prototype.ushln = function ushln (bits) { + return this.clone().iushln(bits); + }; + + // Shift-right + BN.prototype.shrn = function shrn (bits) { + return this.clone().ishrn(bits); + }; + + BN.prototype.ushrn = function ushrn (bits) { + return this.clone().iushrn(bits); + }; + + // Test if n bit is set + BN.prototype.testn = function testn (bit) { + assert(typeof bit === 'number' && bit >= 0); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) return false; + + // Check bit and return + var w = this.words[s]; + + return !!(w & q); + }; + + // Return only lowers bits of number (in-place) + BN.prototype.imaskn = function imaskn (bits) { + assert(typeof bits === 'number' && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + + assert(this.negative === 0, 'imaskn works only with positive numbers'); + + if (this.length <= s) { + return this; + } + + if (r !== 0) { + s++; + } + this.length = Math.min(s, this.length); + + if (r !== 0) { + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + this.words[this.length - 1] &= mask; + } + + return this._strip(); + }; + + // Return only lowers bits of number + BN.prototype.maskn = function maskn (bits) { + return this.clone().imaskn(bits); + }; + + // Add plain number `num` to `this` + BN.prototype.iaddn = function iaddn (num) { + assert(typeof num === 'number'); + assert(num < 0x4000000); + if (num < 0) return this.isubn(-num); + + // Possible sign change + if (this.negative !== 0) { + if (this.length === 1 && (this.words[0] | 0) <= num) { + this.words[0] = num - (this.words[0] | 0); + this.negative = 0; + return this; + } + + this.negative = 0; + this.isubn(num); + this.negative = 1; + return this; + } + + // Add without checks + return this._iaddn(num); + }; + + BN.prototype._iaddn = function _iaddn (num) { + this.words[0] += num; + + // Carry + for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { + this.words[i] -= 0x4000000; + if (i === this.length - 1) { + this.words[i + 1] = 1; + } else { + this.words[i + 1]++; + } + } + this.length = Math.max(this.length, i + 1); + + return this; + }; + + // Subtract plain number `num` from `this` + BN.prototype.isubn = function isubn (num) { + assert(typeof num === 'number'); + assert(num < 0x4000000); + if (num < 0) return this.iaddn(-num); + + if (this.negative !== 0) { + this.negative = 0; + this.iaddn(num); + this.negative = 1; + return this; + } + + this.words[0] -= num; + + if (this.length === 1 && this.words[0] < 0) { + this.words[0] = -this.words[0]; + this.negative = 1; + } else { + // Carry + for (var i = 0; i < this.length && this.words[i] < 0; i++) { + this.words[i] += 0x4000000; + this.words[i + 1] -= 1; + } + } + + return this._strip(); + }; + + BN.prototype.addn = function addn (num) { + return this.clone().iaddn(num); + }; + + BN.prototype.subn = function subn (num) { + return this.clone().isubn(num); + }; + + BN.prototype.iabs = function iabs () { + this.negative = 0; + + return this; + }; + + BN.prototype.abs = function abs () { + return this.clone().iabs(); + }; + + BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { + var len = num.length + shift; + var i; + + this._expand(len); + + var w; + var carry = 0; + for (i = 0; i < num.length; i++) { + w = (this.words[i + shift] | 0) + carry; + var right = (num.words[i] | 0) * mul; + w -= right & 0x3ffffff; + carry = (w >> 26) - ((right / 0x4000000) | 0); + this.words[i + shift] = w & 0x3ffffff; + } + for (; i < this.length - shift; i++) { + w = (this.words[i + shift] | 0) + carry; + carry = w >> 26; + this.words[i + shift] = w & 0x3ffffff; + } + + if (carry === 0) return this._strip(); + + // Subtraction overflow + assert(carry === -1); + carry = 0; + for (i = 0; i < this.length; i++) { + w = -(this.words[i] | 0) + carry; + carry = w >> 26; + this.words[i] = w & 0x3ffffff; + } + this.negative = 1; + + return this._strip(); + }; + + BN.prototype._wordDiv = function _wordDiv (num, mode) { + var shift = this.length - num.length; + + var a = this.clone(); + var b = num; + + // Normalize + var bhi = b.words[b.length - 1] | 0; + var bhiBits = this._countBits(bhi); + shift = 26 - bhiBits; + if (shift !== 0) { + b = b.ushln(shift); + a.iushln(shift); + bhi = b.words[b.length - 1] | 0; + } + + // Initialize quotient + var m = a.length - b.length; + var q; + + if (mode !== 'mod') { + q = new BN(null); + q.length = m + 1; + q.words = new Array(q.length); + for (var i = 0; i < q.length; i++) { + q.words[i] = 0; + } + } + + var diff = a.clone()._ishlnsubmul(b, 1, m); + if (diff.negative === 0) { + a = diff; + if (q) { + q.words[m] = 1; + } + } + + for (var j = m - 1; j >= 0; j--) { + var qj = (a.words[b.length + j] | 0) * 0x4000000 + + (a.words[b.length + j - 1] | 0); + + // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max + // (0x7ffffff) + qj = Math.min((qj / bhi) | 0, 0x3ffffff); + + a._ishlnsubmul(b, qj, j); + while (a.negative !== 0) { + qj--; + a.negative = 0; + a._ishlnsubmul(b, 1, j); + if (!a.isZero()) { + a.negative ^= 1; + } + } + if (q) { + q.words[j] = qj; + } + } + if (q) { + q._strip(); + } + a._strip(); + + // Denormalize + if (mode !== 'div' && shift !== 0) { + a.iushrn(shift); + } + + return { + div: q || null, + mod: a + }; + }; + + // NOTE: 1) `mode` can be set to `mod` to request mod only, + // to `div` to request div only, or be absent to + // request both div & mod + // 2) `positive` is true if unsigned mod is requested + BN.prototype.divmod = function divmod (num, mode, positive) { + assert(!num.isZero()); + + if (this.isZero()) { + return { + div: new BN(0), + mod: new BN(0) + }; + } + + var div, mod, res; + if (this.negative !== 0 && num.negative === 0) { + res = this.neg().divmod(num, mode); + + if (mode !== 'mod') { + div = res.div.neg(); + } + + if (mode !== 'div') { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.iadd(num); + } + } + + return { + div: div, + mod: mod + }; + } + + if (this.negative === 0 && num.negative !== 0) { + res = this.divmod(num.neg(), mode); + + if (mode !== 'mod') { + div = res.div.neg(); + } + + return { + div: div, + mod: res.mod + }; + } + + if ((this.negative & num.negative) !== 0) { + res = this.neg().divmod(num.neg(), mode); + + if (mode !== 'div') { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.isub(num); + } + } + + return { + div: res.div, + mod: mod + }; + } + + // Both numbers are positive at this point + + // Strip both numbers to approximate shift value + if (num.length > this.length || this.cmp(num) < 0) { + return { + div: new BN(0), + mod: this + }; + } + + // Very short reduction + if (num.length === 1) { + if (mode === 'div') { + return { + div: this.divn(num.words[0]), + mod: null + }; + } + + if (mode === 'mod') { + return { + div: null, + mod: new BN(this.modrn(num.words[0])) + }; + } + + return { + div: this.divn(num.words[0]), + mod: new BN(this.modrn(num.words[0])) + }; + } + + return this._wordDiv(num, mode); + }; + + // Find `this` / `num` + BN.prototype.div = function div (num) { + return this.divmod(num, 'div', false).div; + }; + + // Find `this` % `num` + BN.prototype.mod = function mod (num) { + return this.divmod(num, 'mod', false).mod; + }; + + BN.prototype.umod = function umod (num) { + return this.divmod(num, 'mod', true).mod; + }; + + // Find Round(`this` / `num`) + BN.prototype.divRound = function divRound (num) { + var dm = this.divmod(num); + + // Fast case - exact division + if (dm.mod.isZero()) return dm.div; + + var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; + + var half = num.ushrn(1); + var r2 = num.andln(1); + var cmp = mod.cmp(half); + + // Round down + if (cmp < 0 || (r2 === 1 && cmp === 0)) return dm.div; + + // Round up + return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); + }; + + BN.prototype.modrn = function modrn (num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(num <= 0x3ffffff); + var p = (1 << 26) % num; + + var acc = 0; + for (var i = this.length - 1; i >= 0; i--) { + acc = (p * acc + (this.words[i] | 0)) % num; + } + + return isNegNum ? -acc : acc; + }; + + // WARNING: DEPRECATED + BN.prototype.modn = function modn (num) { + return this.modrn(num); + }; + + // In-place division by number + BN.prototype.idivn = function idivn (num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(num <= 0x3ffffff); + + var carry = 0; + for (var i = this.length - 1; i >= 0; i--) { + var w = (this.words[i] | 0) + carry * 0x4000000; + this.words[i] = (w / num) | 0; + carry = w % num; + } + + this._strip(); + return isNegNum ? this.ineg() : this; + }; + + BN.prototype.divn = function divn (num) { + return this.clone().idivn(num); + }; + + BN.prototype.egcd = function egcd (p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var x = this; + var y = p.clone(); + + if (x.negative !== 0) { + x = x.umod(p); + } else { + x = x.clone(); + } + + // A * x + B * y = x + var A = new BN(1); + var B = new BN(0); + + // C * x + D * y = y + var C = new BN(0); + var D = new BN(1); + + var g = 0; + + while (x.isEven() && y.isEven()) { + x.iushrn(1); + y.iushrn(1); + ++g; + } + + var yp = y.clone(); + var xp = x.clone(); + + while (!x.isZero()) { + for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + x.iushrn(i); + while (i-- > 0) { + if (A.isOdd() || B.isOdd()) { + A.iadd(yp); + B.isub(xp); + } + + A.iushrn(1); + B.iushrn(1); + } + } + + for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + y.iushrn(j); + while (j-- > 0) { + if (C.isOdd() || D.isOdd()) { + C.iadd(yp); + D.isub(xp); + } + + C.iushrn(1); + D.iushrn(1); + } + } + + if (x.cmp(y) >= 0) { + x.isub(y); + A.isub(C); + B.isub(D); + } else { + y.isub(x); + C.isub(A); + D.isub(B); + } + } + + return { + a: C, + b: D, + gcd: y.iushln(g) + }; + }; + + // This is reduced incarnation of the binary EEA + // above, designated to invert members of the + // _prime_ fields F(p) at a maximal speed + BN.prototype._invmp = function _invmp (p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var a = this; + var b = p.clone(); + + if (a.negative !== 0) { + a = a.umod(p); + } else { + a = a.clone(); + } + + var x1 = new BN(1); + var x2 = new BN(0); + + var delta = b.clone(); + + while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { + for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + a.iushrn(i); + while (i-- > 0) { + if (x1.isOdd()) { + x1.iadd(delta); + } + + x1.iushrn(1); + } + } + + for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + b.iushrn(j); + while (j-- > 0) { + if (x2.isOdd()) { + x2.iadd(delta); + } + + x2.iushrn(1); + } + } + + if (a.cmp(b) >= 0) { + a.isub(b); + x1.isub(x2); + } else { + b.isub(a); + x2.isub(x1); + } + } + + var res; + if (a.cmpn(1) === 0) { + res = x1; + } else { + res = x2; + } + + if (res.cmpn(0) < 0) { + res.iadd(p); + } + + return res; + }; + + BN.prototype.gcd = function gcd (num) { + if (this.isZero()) return num.abs(); + if (num.isZero()) return this.abs(); + + var a = this.clone(); + var b = num.clone(); + a.negative = 0; + b.negative = 0; + + // Remove common factor of two + for (var shift = 0; a.isEven() && b.isEven(); shift++) { + a.iushrn(1); + b.iushrn(1); + } + + do { + while (a.isEven()) { + a.iushrn(1); + } + while (b.isEven()) { + b.iushrn(1); + } + + var r = a.cmp(b); + if (r < 0) { + // Swap `a` and `b` to make `a` always bigger than `b` + var t = a; + a = b; + b = t; + } else if (r === 0 || b.cmpn(1) === 0) { + break; + } + + a.isub(b); + } while (true); + + return b.iushln(shift); + }; + + // Invert number in the field F(num) + BN.prototype.invm = function invm (num) { + return this.egcd(num).a.umod(num); + }; + + BN.prototype.isEven = function isEven () { + return (this.words[0] & 1) === 0; + }; + + BN.prototype.isOdd = function isOdd () { + return (this.words[0] & 1) === 1; + }; + + // And first word and num + BN.prototype.andln = function andln (num) { + return this.words[0] & num; + }; + + // Increment at the bit position in-line + BN.prototype.bincn = function bincn (bit) { + assert(typeof bit === 'number'); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) { + this._expand(s + 1); + this.words[s] |= q; + return this; + } + + // Add bit and propagate, if needed + var carry = q; + for (var i = s; carry !== 0 && i < this.length; i++) { + var w = this.words[i] | 0; + w += carry; + carry = w >>> 26; + w &= 0x3ffffff; + this.words[i] = w; + } + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + return this; + }; + + BN.prototype.isZero = function isZero () { + return this.length === 1 && this.words[0] === 0; + }; + + BN.prototype.cmpn = function cmpn (num) { + var negative = num < 0; + + if (this.negative !== 0 && !negative) return -1; + if (this.negative === 0 && negative) return 1; + + this._strip(); + + var res; + if (this.length > 1) { + res = 1; + } else { + if (negative) { + num = -num; + } + + assert(num <= 0x3ffffff, 'Number is too big'); + + var w = this.words[0] | 0; + res = w === num ? 0 : w < num ? -1 : 1; + } + if (this.negative !== 0) return -res | 0; + return res; + }; + + // Compare two numbers and return: + // 1 - if `this` > `num` + // 0 - if `this` == `num` + // -1 - if `this` < `num` + BN.prototype.cmp = function cmp (num) { + if (this.negative !== 0 && num.negative === 0) return -1; + if (this.negative === 0 && num.negative !== 0) return 1; + + var res = this.ucmp(num); + if (this.negative !== 0) return -res | 0; + return res; + }; + + // Unsigned comparison + BN.prototype.ucmp = function ucmp (num) { + // At this point both numbers have the same sign + if (this.length > num.length) return 1; + if (this.length < num.length) return -1; + + var res = 0; + for (var i = this.length - 1; i >= 0; i--) { + var a = this.words[i] | 0; + var b = num.words[i] | 0; + + if (a === b) continue; + if (a < b) { + res = -1; + } else if (a > b) { + res = 1; + } + break; + } + return res; + }; + + BN.prototype.gtn = function gtn (num) { + return this.cmpn(num) === 1; + }; + + BN.prototype.gt = function gt (num) { + return this.cmp(num) === 1; + }; + + BN.prototype.gten = function gten (num) { + return this.cmpn(num) >= 0; + }; + + BN.prototype.gte = function gte (num) { + return this.cmp(num) >= 0; + }; + + BN.prototype.ltn = function ltn (num) { + return this.cmpn(num) === -1; + }; + + BN.prototype.lt = function lt (num) { + return this.cmp(num) === -1; + }; + + BN.prototype.lten = function lten (num) { + return this.cmpn(num) <= 0; + }; + + BN.prototype.lte = function lte (num) { + return this.cmp(num) <= 0; + }; + + BN.prototype.eqn = function eqn (num) { + return this.cmpn(num) === 0; + }; + + BN.prototype.eq = function eq (num) { + return this.cmp(num) === 0; + }; + + // + // A reduce context, could be using montgomery or something better, depending + // on the `m` itself. + // + BN.red = function red (num) { + return new Red(num); + }; + + BN.prototype.toRed = function toRed (ctx) { + assert(!this.red, 'Already a number in reduction context'); + assert(this.negative === 0, 'red works only with positives'); + return ctx.convertTo(this)._forceRed(ctx); + }; + + BN.prototype.fromRed = function fromRed () { + assert(this.red, 'fromRed works only with numbers in reduction context'); + return this.red.convertFrom(this); + }; + + BN.prototype._forceRed = function _forceRed (ctx) { + this.red = ctx; + return this; + }; + + BN.prototype.forceRed = function forceRed (ctx) { + assert(!this.red, 'Already a number in reduction context'); + return this._forceRed(ctx); + }; + + BN.prototype.redAdd = function redAdd (num) { + assert(this.red, 'redAdd works only with red numbers'); + return this.red.add(this, num); + }; + + BN.prototype.redIAdd = function redIAdd (num) { + assert(this.red, 'redIAdd works only with red numbers'); + return this.red.iadd(this, num); + }; + + BN.prototype.redSub = function redSub (num) { + assert(this.red, 'redSub works only with red numbers'); + return this.red.sub(this, num); + }; + + BN.prototype.redISub = function redISub (num) { + assert(this.red, 'redISub works only with red numbers'); + return this.red.isub(this, num); + }; + + BN.prototype.redShl = function redShl (num) { + assert(this.red, 'redShl works only with red numbers'); + return this.red.shl(this, num); + }; + + BN.prototype.redMul = function redMul (num) { + assert(this.red, 'redMul works only with red numbers'); + this.red._verify2(this, num); + return this.red.mul(this, num); + }; + + BN.prototype.redIMul = function redIMul (num) { + assert(this.red, 'redMul works only with red numbers'); + this.red._verify2(this, num); + return this.red.imul(this, num); + }; + + BN.prototype.redSqr = function redSqr () { + assert(this.red, 'redSqr works only with red numbers'); + this.red._verify1(this); + return this.red.sqr(this); + }; + + BN.prototype.redISqr = function redISqr () { + assert(this.red, 'redISqr works only with red numbers'); + this.red._verify1(this); + return this.red.isqr(this); + }; + + // Square root over p + BN.prototype.redSqrt = function redSqrt () { + assert(this.red, 'redSqrt works only with red numbers'); + this.red._verify1(this); + return this.red.sqrt(this); + }; + + BN.prototype.redInvm = function redInvm () { + assert(this.red, 'redInvm works only with red numbers'); + this.red._verify1(this); + return this.red.invm(this); + }; + + // Return negative clone of `this` % `red modulo` + BN.prototype.redNeg = function redNeg () { + assert(this.red, 'redNeg works only with red numbers'); + this.red._verify1(this); + return this.red.neg(this); + }; + + BN.prototype.redPow = function redPow (num) { + assert(this.red && !num.red, 'redPow(normalNum)'); + this.red._verify1(this); + return this.red.pow(this, num); + }; + + // Prime numbers with efficient reduction + var primes = { + k256: null, + p224: null, + p192: null, + p25519: null + }; + + // Pseudo-Mersenne prime + function MPrime (name, p) { + // P = 2 ^ N - K + this.name = name; + this.p = new BN(p, 16); + this.n = this.p.bitLength(); + this.k = new BN(1).iushln(this.n).isub(this.p); + + this.tmp = this._tmp(); + } + + MPrime.prototype._tmp = function _tmp () { + var tmp = new BN(null); + tmp.words = new Array(Math.ceil(this.n / 13)); + return tmp; + }; + + MPrime.prototype.ireduce = function ireduce (num) { + // Assumes that `num` is less than `P^2` + // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) + var r = num; + var rlen; + + do { + this.split(r, this.tmp); + r = this.imulK(r); + r = r.iadd(this.tmp); + rlen = r.bitLength(); + } while (rlen > this.n); + + var cmp = rlen < this.n ? -1 : r.ucmp(this.p); + if (cmp === 0) { + r.words[0] = 0; + r.length = 1; + } else if (cmp > 0) { + r.isub(this.p); + } else { + r._strip(); + } + + return r; + }; + + MPrime.prototype.split = function split (input, out) { + input.iushrn(this.n, 0, out); + }; + + MPrime.prototype.imulK = function imulK (num) { + return num.imul(this.k); + }; + + function K256 () { + MPrime.call( + this, + 'k256', + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); + } + inherits(K256, MPrime); + + K256.prototype.split = function split (input, output) { + // 256 = 9 * 26 + 22 + var mask = 0x3fffff; + + var outLen = Math.min(input.length, 9); + for (var i = 0; i < outLen; i++) { + output.words[i] = input.words[i]; + } + output.length = outLen; + + if (input.length <= 9) { + input.words[0] = 0; + input.length = 1; + return; + } + + // Shift by 9 limbs + var prev = input.words[9]; + output.words[output.length++] = prev & mask; + + for (i = 10; i < input.length; i++) { + var next = input.words[i] | 0; + input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); + prev = next; + } + prev >>>= 22; + input.words[i - 10] = prev; + if (prev === 0 && input.length > 10) { + input.length -= 10; + } else { + input.length -= 9; + } + }; + + K256.prototype.imulK = function imulK (num) { + // K = 0x1000003d1 = [ 0x40, 0x3d1 ] + num.words[num.length] = 0; + num.words[num.length + 1] = 0; + num.length += 2; + + // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 + var lo = 0; + for (var i = 0; i < num.length; i++) { + var w = num.words[i] | 0; + lo += w * 0x3d1; + num.words[i] = lo & 0x3ffffff; + lo = w * 0x40 + ((lo / 0x4000000) | 0); + } + + // Fast length reduction + if (num.words[num.length - 1] === 0) { + num.length--; + if (num.words[num.length - 1] === 0) { + num.length--; + } + } + return num; + }; + + function P224 () { + MPrime.call( + this, + 'p224', + 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); + } + inherits(P224, MPrime); + + function P192 () { + MPrime.call( + this, + 'p192', + 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); + } + inherits(P192, MPrime); + + function P25519 () { + // 2 ^ 255 - 19 + MPrime.call( + this, + '25519', + '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); + } + inherits(P25519, MPrime); + + P25519.prototype.imulK = function imulK (num) { + // K = 0x13 + var carry = 0; + for (var i = 0; i < num.length; i++) { + var hi = (num.words[i] | 0) * 0x13 + carry; + var lo = hi & 0x3ffffff; + hi >>>= 26; + + num.words[i] = lo; + carry = hi; + } + if (carry !== 0) { + num.words[num.length++] = carry; + } + return num; + }; + + // Exported mostly for testing purposes, use plain name instead + BN._prime = function prime (name) { + // Cached version of prime + if (primes[name]) return primes[name]; + + var prime; + if (name === 'k256') { + prime = new K256(); + } else if (name === 'p224') { + prime = new P224(); + } else if (name === 'p192') { + prime = new P192(); + } else if (name === 'p25519') { + prime = new P25519(); + } else { + throw new Error('Unknown prime ' + name); + } + primes[name] = prime; + + return prime; + }; + + // + // Base reduction engine + // + function Red (m) { + if (typeof m === 'string') { + var prime = BN._prime(m); + this.m = prime.p; + this.prime = prime; + } else { + assert(m.gtn(1), 'modulus must be greater than 1'); + this.m = m; + this.prime = null; + } + } + + Red.prototype._verify1 = function _verify1 (a) { + assert(a.negative === 0, 'red works only with positives'); + assert(a.red, 'red works only with red numbers'); + }; + + Red.prototype._verify2 = function _verify2 (a, b) { + assert((a.negative | b.negative) === 0, 'red works only with positives'); + assert(a.red && a.red === b.red, + 'red works only with red numbers'); + }; + + Red.prototype.imod = function imod (a) { + if (this.prime) return this.prime.ireduce(a)._forceRed(this); + + a.umod(this.m)._forceRed(this)._move(a); + return a; + }; + + Red.prototype.neg = function neg (a) { + if (a.isZero()) { + return a.clone(); + } + + return this.m.sub(a)._forceRed(this); + }; + + Red.prototype.add = function add (a, b) { + this._verify2(a, b); + + var res = a.add(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res._forceRed(this); + }; + + Red.prototype.iadd = function iadd (a, b) { + this._verify2(a, b); + + var res = a.iadd(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res; + }; + + Red.prototype.sub = function sub (a, b) { + this._verify2(a, b); + + var res = a.sub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res._forceRed(this); + }; + + Red.prototype.isub = function isub (a, b) { + this._verify2(a, b); + + var res = a.isub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res; + }; + + Red.prototype.shl = function shl (a, num) { + this._verify1(a); + return this.imod(a.ushln(num)); + }; + + Red.prototype.imul = function imul (a, b) { + this._verify2(a, b); + return this.imod(a.imul(b)); + }; + + Red.prototype.mul = function mul (a, b) { + this._verify2(a, b); + return this.imod(a.mul(b)); + }; + + Red.prototype.isqr = function isqr (a) { + return this.imul(a, a.clone()); + }; + + Red.prototype.sqr = function sqr (a) { + return this.mul(a, a); + }; + + Red.prototype.sqrt = function sqrt (a) { + if (a.isZero()) return a.clone(); + + var mod3 = this.m.andln(3); + assert(mod3 % 2 === 1); + + // Fast case + if (mod3 === 3) { + var pow = this.m.add(new BN(1)).iushrn(2); + return this.pow(a, pow); + } + + // Tonelli-Shanks algorithm (Totally unoptimized and slow) + // + // Find Q and S, that Q * 2 ^ S = (P - 1) + var q = this.m.subn(1); + var s = 0; + while (!q.isZero() && q.andln(1) === 0) { + s++; + q.iushrn(1); + } + assert(!q.isZero()); + + var one = new BN(1).toRed(this); + var nOne = one.redNeg(); + + // Find quadratic non-residue + // NOTE: Max is such because of generalized Riemann hypothesis. + var lpow = this.m.subn(1).iushrn(1); + var z = this.m.bitLength(); + z = new BN(2 * z * z).toRed(this); + + while (this.pow(z, lpow).cmp(nOne) !== 0) { + z.redIAdd(nOne); + } + + var c = this.pow(z, q); + var r = this.pow(a, q.addn(1).iushrn(1)); + var t = this.pow(a, q); + var m = s; + while (t.cmp(one) !== 0) { + var tmp = t; + for (var i = 0; tmp.cmp(one) !== 0; i++) { + tmp = tmp.redSqr(); + } + assert(i < m); + var b = this.pow(c, new BN(1).iushln(m - i - 1)); + + r = r.redMul(b); + c = b.redSqr(); + t = t.redMul(c); + m = i; + } + + return r; + }; + + Red.prototype.invm = function invm (a) { + var inv = a._invmp(this.m); + if (inv.negative !== 0) { + inv.negative = 0; + return this.imod(inv).redNeg(); + } else { + return this.imod(inv); + } + }; + + Red.prototype.pow = function pow (a, num) { + if (num.isZero()) return new BN(1).toRed(this); + if (num.cmpn(1) === 0) return a.clone(); + + var windowSize = 4; + var wnd = new Array(1 << windowSize); + wnd[0] = new BN(1).toRed(this); + wnd[1] = a; + for (var i = 2; i < wnd.length; i++) { + wnd[i] = this.mul(wnd[i - 1], a); + } + + var res = wnd[0]; + var current = 0; + var currentLen = 0; + var start = num.bitLength() % 26; + if (start === 0) { + start = 26; + } + + for (i = num.length - 1; i >= 0; i--) { + var word = num.words[i]; + for (var j = start - 1; j >= 0; j--) { + var bit = (word >> j) & 1; + if (res !== wnd[0]) { + res = this.sqr(res); + } + + if (bit === 0 && current === 0) { + currentLen = 0; + continue; + } + + current <<= 1; + current |= bit; + currentLen++; + if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; + + res = this.mul(res, wnd[current]); + currentLen = 0; + current = 0; + } + start = 26; + } + + return res; + }; + + Red.prototype.convertTo = function convertTo (num) { + var r = num.umod(this.m); + + return r === num ? r.clone() : r; + }; + + Red.prototype.convertFrom = function convertFrom (num) { + var res = num.clone(); + res.red = null; + return res; + }; + + // + // Montgomery method engine + // + + BN.mont = function mont (num) { + return new Mont(num); + }; + + function Mont (m) { + Red.call(this, m); + + this.shift = this.m.bitLength(); + if (this.shift % 26 !== 0) { + this.shift += 26 - (this.shift % 26); + } + + this.r = new BN(1).iushln(this.shift); + this.r2 = this.imod(this.r.sqr()); + this.rinv = this.r._invmp(this.m); + + this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); + this.minv = this.minv.umod(this.r); + this.minv = this.r.sub(this.minv); + } + inherits(Mont, Red); + + Mont.prototype.convertTo = function convertTo (num) { + return this.imod(num.ushln(this.shift)); + }; + + Mont.prototype.convertFrom = function convertFrom (num) { + var r = this.imod(num.mul(this.rinv)); + r.red = null; + return r; + }; + + Mont.prototype.imul = function imul (a, b) { + if (a.isZero() || b.isZero()) { + a.words[0] = 0; + a.length = 1; + return a; + } + + var t = a.imul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); + }; + + Mont.prototype.mul = function mul (a, b) { + if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); + + var t = a.mul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); + }; + + Mont.prototype.invm = function invm (a) { + // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R + var res = this.imod(a._invmp(this.m).mul(this.r2)); + return res._forceRed(this); + }; +})(typeof module === 'undefined' || module, this); + +},{"buffer":180}],123:[function(require,module,exports){ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(); + } + else if (typeof define === "function" && define.amd) { + // AMD + define([], factory); + } + else { + // Global (browser) + root.CryptoJS = factory(); + } +}(this, function () { + + /** + * CryptoJS core components. + */ + var CryptoJS = CryptoJS || (function (Math, undefined) { + /* + * Local polyfil of Object.create + */ + var create = Object.create || (function () { + function F() {}; + + return function (obj) { + var subtype; + + F.prototype = obj; + + subtype = new F(); + + F.prototype = null; + + return subtype; + }; + }()) + + /** + * CryptoJS namespace. + */ + var C = {}; + + /** + * Library namespace. + */ + var C_lib = C.lib = {}; + + /** + * Base object for prototypal inheritance. + */ + var Base = C_lib.Base = (function () { + + + return { + /** + * Creates a new object that inherits from this object. + * + * @param {Object} overrides Properties to copy into the new object. + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * field: 'value', + * + * method: function () { + * } + * }); + */ + extend: function (overrides) { + // Spawn + var subtype = create(this); + + // Augment + if (overrides) { + subtype.mixIn(overrides); + } + + // Create default initializer + if (!subtype.hasOwnProperty('init') || this.init === subtype.init) { + subtype.init = function () { + subtype.$super.init.apply(this, arguments); + }; + } + + // Initializer's prototype is the subtype object + subtype.init.prototype = subtype; + + // Reference supertype + subtype.$super = this; + + return subtype; + }, + + /** + * Extends this object and runs the init method. + * Arguments to create() will be passed to init(). + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var instance = MyType.create(); + */ + create: function () { + var instance = this.extend(); + instance.init.apply(instance, arguments); + + return instance; + }, + + /** + * Initializes a newly created object. + * Override this method to add some logic when your objects are created. + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * init: function () { + * // ... + * } + * }); + */ + init: function () { + }, + + /** + * Copies properties into this object. + * + * @param {Object} properties The properties to mix in. + * + * @example + * + * MyType.mixIn({ + * field: 'value' + * }); + */ + mixIn: function (properties) { + for (var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + + // IE won't copy toString using the loop above + if (properties.hasOwnProperty('toString')) { + this.toString = properties.toString; + } + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = instance.clone(); + */ + clone: function () { + return this.init.prototype.extend(this); + } + }; + }()); + + /** + * An array of 32-bit words. + * + * @property {Array} words The array of 32-bit words. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var WordArray = C_lib.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of 32-bit words. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.create(); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 4; + } + }, + + /** + * Converts this word array to a string. + * + * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex + * + * @return {string} The stringified word array. + * + * @example + * + * var string = wordArray + ''; + * var string = wordArray.toString(); + * var string = wordArray.toString(CryptoJS.enc.Utf8); + */ + toString: function (encoder) { + return (encoder || Hex).stringify(this); + }, + + /** + * Concatenates a word array to this word array. + * + * @param {WordArray} wordArray The word array to append. + * + * @return {WordArray} This word array. + * + * @example + * + * wordArray1.concat(wordArray2); + */ + concat: function (wordArray) { + // Shortcuts + var thisWords = this.words; + var thatWords = wordArray.words; + var thisSigBytes = this.sigBytes; + var thatSigBytes = wordArray.sigBytes; + + // Clamp excess bits + this.clamp(); + + // Concat + if (thisSigBytes % 4) { + // Copy one byte at a time + for (var i = 0; i < thatSigBytes; i++) { + var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); + } + } else { + // Copy one word at a time + for (var i = 0; i < thatSigBytes; i += 4) { + thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; + } + } + this.sigBytes += thatSigBytes; + + // Chainable + return this; + }, + + /** + * Removes insignificant bits. + * + * @example + * + * wordArray.clamp(); + */ + clamp: function () { + // Shortcuts + var words = this.words; + var sigBytes = this.sigBytes; + + // Clamp + words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); + words.length = Math.ceil(sigBytes / 4); + }, + + /** + * Creates a copy of this word array. + * + * @return {WordArray} The clone. + * + * @example + * + * var clone = wordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone.words = this.words.slice(0); + + return clone; + }, + + /** + * Creates a word array filled with random bytes. + * + * @param {number} nBytes The number of random bytes to generate. + * + * @return {WordArray} The random word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.random(16); + */ + random: function (nBytes) { + var words = []; + + var r = (function (m_w) { + var m_w = m_w; + var m_z = 0x3ade68b1; + var mask = 0xffffffff; + + return function () { + m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; + m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; + var result = ((m_z << 0x10) + m_w) & mask; + result /= 0x100000000; + result += 0.5; + return result * (Math.random() > .5 ? 1 : -1); + } + }); + + for (var i = 0, rcache; i < nBytes; i += 4) { + var _r = r((rcache || Math.random()) * 0x100000000); + + rcache = _r() * 0x3ade67b7; + words.push((_r() * 0x100000000) | 0); + } + + return new WordArray.init(words, nBytes); + } + }); + + /** + * Encoder namespace. + */ + var C_enc = C.enc = {}; + + /** + * Hex encoding strategy. + */ + var Hex = C_enc.Hex = { + /** + * Converts a word array to a hex string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The hex string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.enc.Hex.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var hexChars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + hexChars.push((bite >>> 4).toString(16)); + hexChars.push((bite & 0x0f).toString(16)); + } + + return hexChars.join(''); + }, + + /** + * Converts a hex string to a word array. + * + * @param {string} hexStr The hex string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Hex.parse(hexString); + */ + parse: function (hexStr) { + // Shortcut + var hexStrLength = hexStr.length; + + // Convert + var words = []; + for (var i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new WordArray.init(words, hexStrLength / 2); + } + }; + + /** + * Latin1 encoding strategy. + */ + var Latin1 = C_enc.Latin1 = { + /** + * Converts a word array to a Latin1 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Latin1 string. + * + * @static + * + * @example + * + * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var latin1Chars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + latin1Chars.push(String.fromCharCode(bite)); + } + + return latin1Chars.join(''); + }, + + /** + * Converts a Latin1 string to a word array. + * + * @param {string} latin1Str The Latin1 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); + */ + parse: function (latin1Str) { + // Shortcut + var latin1StrLength = latin1Str.length; + + // Convert + var words = []; + for (var i = 0; i < latin1StrLength; i++) { + words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); + } + + return new WordArray.init(words, latin1StrLength); + } + }; + + /** + * UTF-8 encoding strategy. + */ + var Utf8 = C_enc.Utf8 = { + /** + * Converts a word array to a UTF-8 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-8 string. + * + * @static + * + * @example + * + * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); + */ + stringify: function (wordArray) { + try { + return decodeURIComponent(escape(Latin1.stringify(wordArray))); + } catch (e) { + throw new Error('Malformed UTF-8 data'); + } + }, + + /** + * Converts a UTF-8 string to a word array. + * + * @param {string} utf8Str The UTF-8 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); + */ + parse: function (utf8Str) { + return Latin1.parse(unescape(encodeURIComponent(utf8Str))); + } + }; + + /** + * Abstract buffered block algorithm template. + * + * The property blockSize must be implemented in a concrete subtype. + * + * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 + */ + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ + /** + * Resets this block algorithm's data buffer to its initial state. + * + * @example + * + * bufferedBlockAlgorithm.reset(); + */ + reset: function () { + // Initial values + this._data = new WordArray.init(); + this._nDataBytes = 0; + }, + + /** + * Adds new data to this block algorithm's buffer. + * + * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. + * + * @example + * + * bufferedBlockAlgorithm._append('data'); + * bufferedBlockAlgorithm._append(wordArray); + */ + _append: function (data) { + // Convert string to WordArray, else assume WordArray already + if (typeof data == 'string') { + data = Utf8.parse(data); + } + + // Append + this._data.concat(data); + this._nDataBytes += data.sigBytes; + }, + + /** + * Processes available data blocks. + * + * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. + * + * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. + * + * @return {WordArray} The processed data. + * + * @example + * + * var processedData = bufferedBlockAlgorithm._process(); + * var processedData = bufferedBlockAlgorithm._process(!!'flush'); + */ + _process: function (doFlush) { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var dataSigBytes = data.sigBytes; + var blockSize = this.blockSize; + var blockSizeBytes = blockSize * 4; + + // Count blocks ready + var nBlocksReady = dataSigBytes / blockSizeBytes; + if (doFlush) { + // Round up to include partial blocks + nBlocksReady = Math.ceil(nBlocksReady); + } else { + // Round down to include only full blocks, + // less the number of blocks that must remain in the buffer + nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); + } + + // Count words ready + var nWordsReady = nBlocksReady * blockSize; + + // Count bytes ready + var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); + + // Process blocks + if (nWordsReady) { + for (var offset = 0; offset < nWordsReady; offset += blockSize) { + // Perform concrete-algorithm logic + this._doProcessBlock(dataWords, offset); + } + + // Remove processed words + var processedWords = dataWords.splice(0, nWordsReady); + data.sigBytes -= nBytesReady; + } + + // Return processed words + return new WordArray.init(processedWords, nBytesReady); + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = bufferedBlockAlgorithm.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone._data = this._data.clone(); + + return clone; + }, + + _minBufferSize: 0 + }); + + /** + * Abstract hasher template. + * + * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) + */ + var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + */ + cfg: Base.extend(), + + /** + * Initializes a newly created hasher. + * + * @param {Object} cfg (Optional) The configuration options to use for this hash computation. + * + * @example + * + * var hasher = CryptoJS.algo.SHA256.create(); + */ + init: function (cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Set initial values + this.reset(); + }, + + /** + * Resets this hasher to its initial state. + * + * @example + * + * hasher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-hasher logic + this._doReset(); + }, + + /** + * Updates this hasher with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {Hasher} This hasher. + * + * @example + * + * hasher.update('message'); + * hasher.update(wordArray); + */ + update: function (messageUpdate) { + // Append + this._append(messageUpdate); + + // Update the hash + this._process(); + + // Chainable + return this; + }, + + /** + * Finalizes the hash computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The hash. + * + * @example + * + * var hash = hasher.finalize(); + * var hash = hasher.finalize('message'); + * var hash = hasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Final message update + if (messageUpdate) { + this._append(messageUpdate); + } + + // Perform concrete-hasher logic + var hash = this._doFinalize(); + + return hash; + }, + + blockSize: 512/32, + + /** + * Creates a shortcut function to a hasher's object interface. + * + * @param {Hasher} hasher The hasher to create a helper for. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); + */ + _createHelper: function (hasher) { + return function (message, cfg) { + return new hasher.init(cfg).finalize(message); + }; + }, + + /** + * Creates a shortcut function to the HMAC's object interface. + * + * @param {Hasher} hasher The hasher to use in this HMAC helper. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); + */ + _createHmacHelper: function (hasher) { + return function (message, key) { + return new C_algo.HMAC.init(hasher, key).finalize(message); + }; + } + }); + + /** + * Algorithm namespace. + */ + var C_algo = C.algo = {}; + + return C; + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_enc = C.enc; + + /** + * Base64 encoding strategy. + */ + var Base64 = C_enc.Base64 = { + /** + * Converts a word array to a Base64 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Base64 string. + * + * @static + * + * @example + * + * var base64String = CryptoJS.enc.Base64.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + var map = this._map; + + // Clamp excess bits + wordArray.clamp(); + + // Convert + var base64Chars = []; + for (var i = 0; i < sigBytes; i += 3) { + var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; + var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; + + var triplet = (byte1 << 16) | (byte2 << 8) | byte3; + + for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { + base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); + } + } + + // Add padding + var paddingChar = map.charAt(64); + if (paddingChar) { + while (base64Chars.length % 4) { + base64Chars.push(paddingChar); + } + } + + return base64Chars.join(''); + }, + + /** + * Converts a Base64 string to a word array. + * + * @param {string} base64Str The Base64 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Base64.parse(base64String); + */ + parse: function (base64Str) { + // Shortcuts + var base64StrLength = base64Str.length; + var map = this._map; + var reverseMap = this._reverseMap; + + if (!reverseMap) { + reverseMap = this._reverseMap = []; + for (var j = 0; j < map.length; j++) { + reverseMap[map.charCodeAt(j)] = j; + } + } + + // Ignore padding + var paddingChar = map.charAt(64); + if (paddingChar) { + var paddingIndex = base64Str.indexOf(paddingChar); + if (paddingIndex !== -1) { + base64StrLength = paddingIndex; + } + } + + // Convert + return parseLoop(base64Str, base64StrLength, reverseMap); + + }, + + _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + }; + + function parseLoop(base64Str, base64StrLength, reverseMap) { + var words = []; + var nBytes = 0; + for (var i = 0; i < base64StrLength; i++) { + if (i % 4) { + var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2); + var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2); + words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); + nBytes++; + } + } + return WordArray.create(words, nBytes); + } + }()); + + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Constants table + var T = []; + + // Compute constants + (function () { + for (var i = 0; i < 64; i++) { + T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0; + } + }()); + + /** + * MD5 hash algorithm. + */ + var MD5 = C_algo.MD5 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0x67452301, 0xefcdab89, + 0x98badcfe, 0x10325476 + ]); + }, + + _doProcessBlock: function (M, offset) { + // Swap endian + for (var i = 0; i < 16; i++) { + // Shortcuts + var offset_i = offset + i; + var M_offset_i = M[offset_i]; + + M[offset_i] = ( + (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | + (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) + ); + } + + // Shortcuts + var H = this._hash.words; + + var M_offset_0 = M[offset + 0]; + var M_offset_1 = M[offset + 1]; + var M_offset_2 = M[offset + 2]; + var M_offset_3 = M[offset + 3]; + var M_offset_4 = M[offset + 4]; + var M_offset_5 = M[offset + 5]; + var M_offset_6 = M[offset + 6]; + var M_offset_7 = M[offset + 7]; + var M_offset_8 = M[offset + 8]; + var M_offset_9 = M[offset + 9]; + var M_offset_10 = M[offset + 10]; + var M_offset_11 = M[offset + 11]; + var M_offset_12 = M[offset + 12]; + var M_offset_13 = M[offset + 13]; + var M_offset_14 = M[offset + 14]; + var M_offset_15 = M[offset + 15]; + + // Working varialbes + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + + // Computation + a = FF(a, b, c, d, M_offset_0, 7, T[0]); + d = FF(d, a, b, c, M_offset_1, 12, T[1]); + c = FF(c, d, a, b, M_offset_2, 17, T[2]); + b = FF(b, c, d, a, M_offset_3, 22, T[3]); + a = FF(a, b, c, d, M_offset_4, 7, T[4]); + d = FF(d, a, b, c, M_offset_5, 12, T[5]); + c = FF(c, d, a, b, M_offset_6, 17, T[6]); + b = FF(b, c, d, a, M_offset_7, 22, T[7]); + a = FF(a, b, c, d, M_offset_8, 7, T[8]); + d = FF(d, a, b, c, M_offset_9, 12, T[9]); + c = FF(c, d, a, b, M_offset_10, 17, T[10]); + b = FF(b, c, d, a, M_offset_11, 22, T[11]); + a = FF(a, b, c, d, M_offset_12, 7, T[12]); + d = FF(d, a, b, c, M_offset_13, 12, T[13]); + c = FF(c, d, a, b, M_offset_14, 17, T[14]); + b = FF(b, c, d, a, M_offset_15, 22, T[15]); + + a = GG(a, b, c, d, M_offset_1, 5, T[16]); + d = GG(d, a, b, c, M_offset_6, 9, T[17]); + c = GG(c, d, a, b, M_offset_11, 14, T[18]); + b = GG(b, c, d, a, M_offset_0, 20, T[19]); + a = GG(a, b, c, d, M_offset_5, 5, T[20]); + d = GG(d, a, b, c, M_offset_10, 9, T[21]); + c = GG(c, d, a, b, M_offset_15, 14, T[22]); + b = GG(b, c, d, a, M_offset_4, 20, T[23]); + a = GG(a, b, c, d, M_offset_9, 5, T[24]); + d = GG(d, a, b, c, M_offset_14, 9, T[25]); + c = GG(c, d, a, b, M_offset_3, 14, T[26]); + b = GG(b, c, d, a, M_offset_8, 20, T[27]); + a = GG(a, b, c, d, M_offset_13, 5, T[28]); + d = GG(d, a, b, c, M_offset_2, 9, T[29]); + c = GG(c, d, a, b, M_offset_7, 14, T[30]); + b = GG(b, c, d, a, M_offset_12, 20, T[31]); + + a = HH(a, b, c, d, M_offset_5, 4, T[32]); + d = HH(d, a, b, c, M_offset_8, 11, T[33]); + c = HH(c, d, a, b, M_offset_11, 16, T[34]); + b = HH(b, c, d, a, M_offset_14, 23, T[35]); + a = HH(a, b, c, d, M_offset_1, 4, T[36]); + d = HH(d, a, b, c, M_offset_4, 11, T[37]); + c = HH(c, d, a, b, M_offset_7, 16, T[38]); + b = HH(b, c, d, a, M_offset_10, 23, T[39]); + a = HH(a, b, c, d, M_offset_13, 4, T[40]); + d = HH(d, a, b, c, M_offset_0, 11, T[41]); + c = HH(c, d, a, b, M_offset_3, 16, T[42]); + b = HH(b, c, d, a, M_offset_6, 23, T[43]); + a = HH(a, b, c, d, M_offset_9, 4, T[44]); + d = HH(d, a, b, c, M_offset_12, 11, T[45]); + c = HH(c, d, a, b, M_offset_15, 16, T[46]); + b = HH(b, c, d, a, M_offset_2, 23, T[47]); + + a = II(a, b, c, d, M_offset_0, 6, T[48]); + d = II(d, a, b, c, M_offset_7, 10, T[49]); + c = II(c, d, a, b, M_offset_14, 15, T[50]); + b = II(b, c, d, a, M_offset_5, 21, T[51]); + a = II(a, b, c, d, M_offset_12, 6, T[52]); + d = II(d, a, b, c, M_offset_3, 10, T[53]); + c = II(c, d, a, b, M_offset_10, 15, T[54]); + b = II(b, c, d, a, M_offset_1, 21, T[55]); + a = II(a, b, c, d, M_offset_8, 6, T[56]); + d = II(d, a, b, c, M_offset_15, 10, T[57]); + c = II(c, d, a, b, M_offset_6, 15, T[58]); + b = II(b, c, d, a, M_offset_13, 21, T[59]); + a = II(a, b, c, d, M_offset_4, 6, T[60]); + d = II(d, a, b, c, M_offset_11, 10, T[61]); + c = II(c, d, a, b, M_offset_2, 15, T[62]); + b = II(b, c, d, a, M_offset_9, 21, T[63]); + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + + var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000); + var nBitsTotalL = nBitsTotal; + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = ( + (((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) | + (((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00) + ); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( + (((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) | + (((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00) + ); + + data.sigBytes = (dataWords.length + 1) * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var hash = this._hash; + var H = hash.words; + + // Swap endian + for (var i = 0; i < 4; i++) { + // Shortcut + var H_i = H[i]; + + H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | + (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); + } + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + function FF(a, b, c, d, x, s, t) { + var n = a + ((b & c) | (~b & d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function GG(a, b, c, d, x, s, t) { + var n = a + ((b & d) | (c & ~d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function HH(a, b, c, d, x, s, t) { + var n = a + (b ^ c ^ d) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function II(a, b, c, d, x, s, t) { + var n = a + (c ^ (b | ~d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.MD5('message'); + * var hash = CryptoJS.MD5(wordArray); + */ + C.MD5 = Hasher._createHelper(MD5); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacMD5(message, key); + */ + C.HmacMD5 = Hasher._createHmacHelper(MD5); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Reusable object + var W = []; + + /** + * SHA-1 hash algorithm. + */ + var SHA1 = C_algo.SHA1 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0x67452301, 0xefcdab89, + 0x98badcfe, 0x10325476, + 0xc3d2e1f0 + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + + // Computation + for (var i = 0; i < 80; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; + W[i] = (n << 1) | (n >>> 31); + } + + var t = ((a << 5) | (a >>> 27)) + e + W[i]; + if (i < 20) { + t += ((b & c) | (~b & d)) + 0x5a827999; + } else if (i < 40) { + t += (b ^ c ^ d) + 0x6ed9eba1; + } else if (i < 60) { + t += ((b & c) | (b & d) | (c & d)) - 0x70e44324; + } else /* if (i < 80) */ { + t += (b ^ c ^ d) - 0x359d3e2a; + } + + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA1('message'); + * var hash = CryptoJS.SHA1(wordArray); + */ + C.SHA1 = Hasher._createHelper(SHA1); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA1(message, key); + */ + C.HmacSHA1 = Hasher._createHmacHelper(SHA1); + }()); + + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Initialization and round constants tables + var H = []; + var K = []; + + // Compute constants + (function () { + function isPrime(n) { + var sqrtN = Math.sqrt(n); + for (var factor = 2; factor <= sqrtN; factor++) { + if (!(n % factor)) { + return false; + } + } + + return true; + } + + function getFractionalBits(n) { + return ((n - (n | 0)) * 0x100000000) | 0; + } + + var n = 2; + var nPrime = 0; + while (nPrime < 64) { + if (isPrime(n)) { + if (nPrime < 8) { + H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2)); + } + K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3)); + + nPrime++; + } + + n++; + } + }()); + + // Reusable object + var W = []; + + /** + * SHA-256 hash algorithm. + */ + var SHA256 = C_algo.SHA256 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init(H.slice(0)); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + var f = H[5]; + var g = H[6]; + var h = H[7]; + + // Computation + for (var i = 0; i < 64; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var gamma0x = W[i - 15]; + var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ + ((gamma0x << 14) | (gamma0x >>> 18)) ^ + (gamma0x >>> 3); + + var gamma1x = W[i - 2]; + var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ + ((gamma1x << 13) | (gamma1x >>> 19)) ^ + (gamma1x >>> 10); + + W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; + } + + var ch = (e & f) ^ (~e & g); + var maj = (a & b) ^ (a & c) ^ (b & c); + + var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); + var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); + + var t1 = h + sigma1 + ch + K[i] + W[i]; + var t2 = sigma0 + maj; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + H[5] = (H[5] + f) | 0; + H[6] = (H[6] + g) | 0; + H[7] = (H[7] + h) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA256('message'); + * var hash = CryptoJS.SHA256(wordArray); + */ + C.SHA256 = Hasher._createHelper(SHA256); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA256(message, key); + */ + C.HmacSHA256 = Hasher._createHmacHelper(SHA256); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_enc = C.enc; + + /** + * UTF-16 BE encoding strategy. + */ + var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = { + /** + * Converts a word array to a UTF-16 BE string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-16 BE string. + * + * @static + * + * @example + * + * var utf16String = CryptoJS.enc.Utf16.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var utf16Chars = []; + for (var i = 0; i < sigBytes; i += 2) { + var codePoint = (words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff; + utf16Chars.push(String.fromCharCode(codePoint)); + } + + return utf16Chars.join(''); + }, + + /** + * Converts a UTF-16 BE string to a word array. + * + * @param {string} utf16Str The UTF-16 BE string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf16.parse(utf16String); + */ + parse: function (utf16Str) { + // Shortcut + var utf16StrLength = utf16Str.length; + + // Convert + var words = []; + for (var i = 0; i < utf16StrLength; i++) { + words[i >>> 1] |= utf16Str.charCodeAt(i) << (16 - (i % 2) * 16); + } + + return WordArray.create(words, utf16StrLength * 2); + } + }; + + /** + * UTF-16 LE encoding strategy. + */ + C_enc.Utf16LE = { + /** + * Converts a word array to a UTF-16 LE string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-16 LE string. + * + * @static + * + * @example + * + * var utf16Str = CryptoJS.enc.Utf16LE.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var utf16Chars = []; + for (var i = 0; i < sigBytes; i += 2) { + var codePoint = swapEndian((words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff); + utf16Chars.push(String.fromCharCode(codePoint)); + } + + return utf16Chars.join(''); + }, + + /** + * Converts a UTF-16 LE string to a word array. + * + * @param {string} utf16Str The UTF-16 LE string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf16LE.parse(utf16Str); + */ + parse: function (utf16Str) { + // Shortcut + var utf16StrLength = utf16Str.length; + + // Convert + var words = []; + for (var i = 0; i < utf16StrLength; i++) { + words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << (16 - (i % 2) * 16)); + } + + return WordArray.create(words, utf16StrLength * 2); + } + }; + + function swapEndian(word) { + return ((word << 8) & 0xff00ff00) | ((word >>> 8) & 0x00ff00ff); + } + }()); + + + (function () { + // Check if typed arrays are supported + if (typeof ArrayBuffer != 'function') { + return; + } + + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + + // Reference original init + var superInit = WordArray.init; + + // Augment WordArray.init to handle typed arrays + var subInit = WordArray.init = function (typedArray) { + // Convert buffers to uint8 + if (typedArray instanceof ArrayBuffer) { + typedArray = new Uint8Array(typedArray); + } + + // Convert other array views to uint8 + if ( + typedArray instanceof Int8Array || + (typeof Uint8ClampedArray !== "undefined" && typedArray instanceof Uint8ClampedArray) || + typedArray instanceof Int16Array || + typedArray instanceof Uint16Array || + typedArray instanceof Int32Array || + typedArray instanceof Uint32Array || + typedArray instanceof Float32Array || + typedArray instanceof Float64Array + ) { + typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); + } + + // Handle Uint8Array + if (typedArray instanceof Uint8Array) { + // Shortcut + var typedArrayByteLength = typedArray.byteLength; + + // Extract bytes + var words = []; + for (var i = 0; i < typedArrayByteLength; i++) { + words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8); + } + + // Initialize this word array + superInit.call(this, words, typedArrayByteLength); + } else { + // Else call normal init + superInit.apply(this, arguments); + } + }; + + subInit.prototype = WordArray; + }()); + + + /** @preserve + (c) 2012 by Cédric Mesnil. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Constants table + var _zl = WordArray.create([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]); + var _zr = WordArray.create([ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]); + var _sl = WordArray.create([ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ]); + var _sr = WordArray.create([ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ]); + + var _hl = WordArray.create([ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]); + var _hr = WordArray.create([ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]); + + /** + * RIPEMD160 hash algorithm. + */ + var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({ + _doReset: function () { + this._hash = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]); + }, + + _doProcessBlock: function (M, offset) { + + // Swap endian + for (var i = 0; i < 16; i++) { + // Shortcuts + var offset_i = offset + i; + var M_offset_i = M[offset_i]; + + // Swap + M[offset_i] = ( + (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | + (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) + ); + } + // Shortcut + var H = this._hash.words; + var hl = _hl.words; + var hr = _hr.words; + var zl = _zl.words; + var zr = _zr.words; + var sl = _sl.words; + var sr = _sr.words; + + // Working variables + var al, bl, cl, dl, el; + var ar, br, cr, dr, er; + + ar = al = H[0]; + br = bl = H[1]; + cr = cl = H[2]; + dr = dl = H[3]; + er = el = H[4]; + // Computation + var t; + for (var i = 0; i < 80; i += 1) { + t = (al + M[offset+zl[i]])|0; + if (i<16){ + t += f1(bl,cl,dl) + hl[0]; + } else if (i<32) { + t += f2(bl,cl,dl) + hl[1]; + } else if (i<48) { + t += f3(bl,cl,dl) + hl[2]; + } else if (i<64) { + t += f4(bl,cl,dl) + hl[3]; + } else {// if (i<80) { + t += f5(bl,cl,dl) + hl[4]; + } + t = t|0; + t = rotl(t,sl[i]); + t = (t+el)|0; + al = el; + el = dl; + dl = rotl(cl, 10); + cl = bl; + bl = t; + + t = (ar + M[offset+zr[i]])|0; + if (i<16){ + t += f5(br,cr,dr) + hr[0]; + } else if (i<32) { + t += f4(br,cr,dr) + hr[1]; + } else if (i<48) { + t += f3(br,cr,dr) + hr[2]; + } else if (i<64) { + t += f2(br,cr,dr) + hr[3]; + } else {// if (i<80) { + t += f1(br,cr,dr) + hr[4]; + } + t = t|0; + t = rotl(t,sr[i]) ; + t = (t+er)|0; + ar = er; + er = dr; + dr = rotl(cr, 10); + cr = br; + br = t; + } + // Intermediate hash value + t = (H[1] + cl + dr)|0; + H[1] = (H[2] + dl + er)|0; + H[2] = (H[3] + el + ar)|0; + H[3] = (H[4] + al + br)|0; + H[4] = (H[0] + bl + cr)|0; + H[0] = t; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( + (((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) | + (((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00) + ); + data.sigBytes = (dataWords.length + 1) * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var hash = this._hash; + var H = hash.words; + + // Swap endian + for (var i = 0; i < 5; i++) { + // Shortcut + var H_i = H[i]; + + // Swap + H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | + (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); + } + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + + function f1(x, y, z) { + return ((x) ^ (y) ^ (z)); + + } + + function f2(x, y, z) { + return (((x)&(y)) | ((~x)&(z))); + } + + function f3(x, y, z) { + return (((x) | (~(y))) ^ (z)); + } + + function f4(x, y, z) { + return (((x) & (z)) | ((y)&(~(z)))); + } + + function f5(x, y, z) { + return ((x) ^ ((y) |(~(z)))); + + } + + function rotl(x,n) { + return (x<>>(32-n)); + } + + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.RIPEMD160('message'); + * var hash = CryptoJS.RIPEMD160(wordArray); + */ + C.RIPEMD160 = Hasher._createHelper(RIPEMD160); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacRIPEMD160(message, key); + */ + C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var C_enc = C.enc; + var Utf8 = C_enc.Utf8; + var C_algo = C.algo; + + /** + * HMAC algorithm. + */ + var HMAC = C_algo.HMAC = Base.extend({ + /** + * Initializes a newly created HMAC. + * + * @param {Hasher} hasher The hash algorithm to use. + * @param {WordArray|string} key The secret key. + * + * @example + * + * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key); + */ + init: function (hasher, key) { + // Init hasher + hasher = this._hasher = new hasher.init(); + + // Convert string to WordArray, else assume WordArray already + if (typeof key == 'string') { + key = Utf8.parse(key); + } + + // Shortcuts + var hasherBlockSize = hasher.blockSize; + var hasherBlockSizeBytes = hasherBlockSize * 4; + + // Allow arbitrary length keys + if (key.sigBytes > hasherBlockSizeBytes) { + key = hasher.finalize(key); + } + + // Clamp excess bits + key.clamp(); + + // Clone key for inner and outer pads + var oKey = this._oKey = key.clone(); + var iKey = this._iKey = key.clone(); + + // Shortcuts + var oKeyWords = oKey.words; + var iKeyWords = iKey.words; + + // XOR keys with pad constants + for (var i = 0; i < hasherBlockSize; i++) { + oKeyWords[i] ^= 0x5c5c5c5c; + iKeyWords[i] ^= 0x36363636; + } + oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes; + + // Set initial values + this.reset(); + }, + + /** + * Resets this HMAC to its initial state. + * + * @example + * + * hmacHasher.reset(); + */ + reset: function () { + // Shortcut + var hasher = this._hasher; + + // Reset + hasher.reset(); + hasher.update(this._iKey); + }, + + /** + * Updates this HMAC with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {HMAC} This HMAC instance. + * + * @example + * + * hmacHasher.update('message'); + * hmacHasher.update(wordArray); + */ + update: function (messageUpdate) { + this._hasher.update(messageUpdate); + + // Chainable + return this; + }, + + /** + * Finalizes the HMAC computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The HMAC. + * + * @example + * + * var hmac = hmacHasher.finalize(); + * var hmac = hmacHasher.finalize('message'); + * var hmac = hmacHasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Shortcut + var hasher = this._hasher; + + // Compute HMAC + var innerHash = hasher.finalize(messageUpdate); + hasher.reset(); + var hmac = hasher.finalize(this._oKey.clone().concat(innerHash)); + + return hmac; + } + }); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var SHA1 = C_algo.SHA1; + var HMAC = C_algo.HMAC; + + /** + * Password-Based Key Derivation Function 2 algorithm. + */ + var PBKDF2 = C_algo.PBKDF2 = Base.extend({ + /** + * Configuration options. + * + * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) + * @property {Hasher} hasher The hasher to use. Default: SHA1 + * @property {number} iterations The number of iterations to perform. Default: 1 + */ + cfg: Base.extend({ + keySize: 128/32, + hasher: SHA1, + iterations: 1 + }), + + /** + * Initializes a newly created key derivation function. + * + * @param {Object} cfg (Optional) The configuration options to use for the derivation. + * + * @example + * + * var kdf = CryptoJS.algo.PBKDF2.create(); + * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 }); + * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 }); + */ + init: function (cfg) { + this.cfg = this.cfg.extend(cfg); + }, + + /** + * Computes the Password-Based Key Derivation Function 2. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * + * @return {WordArray} The derived key. + * + * @example + * + * var key = kdf.compute(password, salt); + */ + compute: function (password, salt) { + // Shortcut + var cfg = this.cfg; + + // Init HMAC + var hmac = HMAC.create(cfg.hasher, password); + + // Initial values + var derivedKey = WordArray.create(); + var blockIndex = WordArray.create([0x00000001]); + + // Shortcuts + var derivedKeyWords = derivedKey.words; + var blockIndexWords = blockIndex.words; + var keySize = cfg.keySize; + var iterations = cfg.iterations; + + // Generate key + while (derivedKeyWords.length < keySize) { + var block = hmac.update(salt).finalize(blockIndex); + hmac.reset(); + + // Shortcuts + var blockWords = block.words; + var blockWordsLength = blockWords.length; + + // Iterations + var intermediate = block; + for (var i = 1; i < iterations; i++) { + intermediate = hmac.finalize(intermediate); + hmac.reset(); + + // Shortcut + var intermediateWords = intermediate.words; + + // XOR intermediate with block + for (var j = 0; j < blockWordsLength; j++) { + blockWords[j] ^= intermediateWords[j]; + } + } + + derivedKey.concat(block); + blockIndexWords[0]++; + } + derivedKey.sigBytes = keySize * 4; + + return derivedKey; + } + }); + + /** + * Computes the Password-Based Key Derivation Function 2. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * @param {Object} cfg (Optional) The configuration options to use for this computation. + * + * @return {WordArray} The derived key. + * + * @static + * + * @example + * + * var key = CryptoJS.PBKDF2(password, salt); + * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 }); + * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 }); + */ + C.PBKDF2 = function (password, salt, cfg) { + return PBKDF2.create(cfg).compute(password, salt); + }; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var MD5 = C_algo.MD5; + + /** + * This key derivation function is meant to conform with EVP_BytesToKey. + * www.openssl.org/docs/crypto/EVP_BytesToKey.html + */ + var EvpKDF = C_algo.EvpKDF = Base.extend({ + /** + * Configuration options. + * + * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) + * @property {Hasher} hasher The hash algorithm to use. Default: MD5 + * @property {number} iterations The number of iterations to perform. Default: 1 + */ + cfg: Base.extend({ + keySize: 128/32, + hasher: MD5, + iterations: 1 + }), + + /** + * Initializes a newly created key derivation function. + * + * @param {Object} cfg (Optional) The configuration options to use for the derivation. + * + * @example + * + * var kdf = CryptoJS.algo.EvpKDF.create(); + * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 }); + * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 }); + */ + init: function (cfg) { + this.cfg = this.cfg.extend(cfg); + }, + + /** + * Derives a key from a password. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * + * @return {WordArray} The derived key. + * + * @example + * + * var key = kdf.compute(password, salt); + */ + compute: function (password, salt) { + // Shortcut + var cfg = this.cfg; + + // Init hasher + var hasher = cfg.hasher.create(); + + // Initial values + var derivedKey = WordArray.create(); + + // Shortcuts + var derivedKeyWords = derivedKey.words; + var keySize = cfg.keySize; + var iterations = cfg.iterations; + + // Generate key + while (derivedKeyWords.length < keySize) { + if (block) { + hasher.update(block); + } + var block = hasher.update(password).finalize(salt); + hasher.reset(); + + // Iterations + for (var i = 1; i < iterations; i++) { + block = hasher.finalize(block); + hasher.reset(); + } + + derivedKey.concat(block); + } + derivedKey.sigBytes = keySize * 4; + + return derivedKey; + } + }); + + /** + * Derives a key from a password. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * @param {Object} cfg (Optional) The configuration options to use for this computation. + * + * @return {WordArray} The derived key. + * + * @static + * + * @example + * + * var key = CryptoJS.EvpKDF(password, salt); + * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 }); + * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 }); + */ + C.EvpKDF = function (password, salt, cfg) { + return EvpKDF.create(cfg).compute(password, salt); + }; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var SHA256 = C_algo.SHA256; + + /** + * SHA-224 hash algorithm. + */ + var SHA224 = C_algo.SHA224 = SHA256.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 + ]); + }, + + _doFinalize: function () { + var hash = SHA256._doFinalize.call(this); + + hash.sigBytes -= 4; + + return hash; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA224('message'); + * var hash = CryptoJS.SHA224(wordArray); + */ + C.SHA224 = SHA256._createHelper(SHA224); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA224(message, key); + */ + C.HmacSHA224 = SHA256._createHmacHelper(SHA224); + }()); + + + (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var X32WordArray = C_lib.WordArray; + + /** + * x64 namespace. + */ + var C_x64 = C.x64 = {}; + + /** + * A 64-bit word. + */ + var X64Word = C_x64.Word = Base.extend({ + /** + * Initializes a newly created 64-bit word. + * + * @param {number} high The high 32 bits. + * @param {number} low The low 32 bits. + * + * @example + * + * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607); + */ + init: function (high, low) { + this.high = high; + this.low = low; + } + + /** + * Bitwise NOTs this word. + * + * @return {X64Word} A new x64-Word object after negating. + * + * @example + * + * var negated = x64Word.not(); + */ + // not: function () { + // var high = ~this.high; + // var low = ~this.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ANDs this word with the passed word. + * + * @param {X64Word} word The x64-Word to AND with this word. + * + * @return {X64Word} A new x64-Word object after ANDing. + * + * @example + * + * var anded = x64Word.and(anotherX64Word); + */ + // and: function (word) { + // var high = this.high & word.high; + // var low = this.low & word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to OR with this word. + * + * @return {X64Word} A new x64-Word object after ORing. + * + * @example + * + * var ored = x64Word.or(anotherX64Word); + */ + // or: function (word) { + // var high = this.high | word.high; + // var low = this.low | word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise XORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to XOR with this word. + * + * @return {X64Word} A new x64-Word object after XORing. + * + * @example + * + * var xored = x64Word.xor(anotherX64Word); + */ + // xor: function (word) { + // var high = this.high ^ word.high; + // var low = this.low ^ word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the left. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftL(25); + */ + // shiftL: function (n) { + // if (n < 32) { + // var high = (this.high << n) | (this.low >>> (32 - n)); + // var low = this.low << n; + // } else { + // var high = this.low << (n - 32); + // var low = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the right. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftR(7); + */ + // shiftR: function (n) { + // if (n < 32) { + // var low = (this.low >>> n) | (this.high << (32 - n)); + // var high = this.high >>> n; + // } else { + // var low = this.high >>> (n - 32); + // var high = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Rotates this word n bits to the left. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotL(25); + */ + // rotL: function (n) { + // return this.shiftL(n).or(this.shiftR(64 - n)); + // }, + + /** + * Rotates this word n bits to the right. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotR(7); + */ + // rotR: function (n) { + // return this.shiftR(n).or(this.shiftL(64 - n)); + // }, + + /** + * Adds this word with the passed word. + * + * @param {X64Word} word The x64-Word to add with this word. + * + * @return {X64Word} A new x64-Word object after adding. + * + * @example + * + * var added = x64Word.add(anotherX64Word); + */ + // add: function (word) { + // var low = (this.low + word.low) | 0; + // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0; + // var high = (this.high + word.high + carry) | 0; + + // return X64Word.create(high, low); + // } + }); + + /** + * An array of 64-bit words. + * + * @property {Array} words The array of CryptoJS.x64.Word objects. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var X64WordArray = C_x64.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.x64.WordArray.create(); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ]); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ], 10); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 8; + } + }, + + /** + * Converts this 64-bit word array to a 32-bit word array. + * + * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array. + * + * @example + * + * var x32WordArray = x64WordArray.toX32(); + */ + toX32: function () { + // Shortcuts + var x64Words = this.words; + var x64WordsLength = x64Words.length; + + // Convert + var x32Words = []; + for (var i = 0; i < x64WordsLength; i++) { + var x64Word = x64Words[i]; + x32Words.push(x64Word.high); + x32Words.push(x64Word.low); + } + + return X32WordArray.create(x32Words, this.sigBytes); + }, + + /** + * Creates a copy of this word array. + * + * @return {X64WordArray} The clone. + * + * @example + * + * var clone = x64WordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + + // Clone "words" array + var words = clone.words = this.words.slice(0); + + // Clone each X64Word object + var wordsLength = words.length; + for (var i = 0; i < wordsLength; i++) { + words[i] = words[i].clone(); + } + + return clone; + } + }); + }()); + + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var C_algo = C.algo; + + // Constants tables + var RHO_OFFSETS = []; + var PI_INDEXES = []; + var ROUND_CONSTANTS = []; + + // Compute Constants + (function () { + // Compute rho offset constants + var x = 1, y = 0; + for (var t = 0; t < 24; t++) { + RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64; + + var newX = y % 5; + var newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + + // Compute pi index constants + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5; + } + } + + // Compute round constants + var LFSR = 0x01; + for (var i = 0; i < 24; i++) { + var roundConstantMsw = 0; + var roundConstantLsw = 0; + + for (var j = 0; j < 7; j++) { + if (LFSR & 0x01) { + var bitPosition = (1 << j) - 1; + if (bitPosition < 32) { + roundConstantLsw ^= 1 << bitPosition; + } else /* if (bitPosition >= 32) */ { + roundConstantMsw ^= 1 << (bitPosition - 32); + } + } + + // Compute next LFSR + if (LFSR & 0x80) { + // Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1 + LFSR = (LFSR << 1) ^ 0x71; + } else { + LFSR <<= 1; + } + } + + ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw); + } + }()); + + // Reusable objects for temporary values + var T = []; + (function () { + for (var i = 0; i < 25; i++) { + T[i] = X64Word.create(); + } + }()); + + /** + * SHA-3 hash algorithm. + */ + var SHA3 = C_algo.SHA3 = Hasher.extend({ + /** + * Configuration options. + * + * @property {number} outputLength + * The desired number of bits in the output hash. + * Only values permitted are: 224, 256, 384, 512. + * Default: 512 + */ + cfg: Hasher.cfg.extend({ + outputLength: 512 + }), + + _doReset: function () { + var state = this._state = [] + for (var i = 0; i < 25; i++) { + state[i] = new X64Word.init(); + } + + this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32; + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var state = this._state; + var nBlockSizeLanes = this.blockSize / 2; + + // Absorb + for (var i = 0; i < nBlockSizeLanes; i++) { + // Shortcuts + var M2i = M[offset + 2 * i]; + var M2i1 = M[offset + 2 * i + 1]; + + // Swap endian + M2i = ( + (((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) | + (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00) + ); + M2i1 = ( + (((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) | + (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00) + ); + + // Absorb message into state + var lane = state[i]; + lane.high ^= M2i1; + lane.low ^= M2i; + } + + // Rounds + for (var round = 0; round < 24; round++) { + // Theta + for (var x = 0; x < 5; x++) { + // Mix column lanes + var tMsw = 0, tLsw = 0; + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + tMsw ^= lane.high; + tLsw ^= lane.low; + } + + // Temporary values + var Tx = T[x]; + Tx.high = tMsw; + Tx.low = tLsw; + } + for (var x = 0; x < 5; x++) { + // Shortcuts + var Tx4 = T[(x + 4) % 5]; + var Tx1 = T[(x + 1) % 5]; + var Tx1Msw = Tx1.high; + var Tx1Lsw = Tx1.low; + + // Mix surrounding columns + var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31)); + var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31)); + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + lane.high ^= tMsw; + lane.low ^= tLsw; + } + } + + // Rho Pi + for (var laneIndex = 1; laneIndex < 25; laneIndex++) { + // Shortcuts + var lane = state[laneIndex]; + var laneMsw = lane.high; + var laneLsw = lane.low; + var rhoOffset = RHO_OFFSETS[laneIndex]; + + // Rotate lanes + if (rhoOffset < 32) { + var tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset)); + var tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset)); + } else /* if (rhoOffset >= 32) */ { + var tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset)); + var tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset)); + } + + // Transpose lanes + var TPiLane = T[PI_INDEXES[laneIndex]]; + TPiLane.high = tMsw; + TPiLane.low = tLsw; + } + + // Rho pi at x = y = 0 + var T0 = T[0]; + var state0 = state[0]; + T0.high = state0.high; + T0.low = state0.low; + + // Chi + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + // Shortcuts + var laneIndex = x + 5 * y; + var lane = state[laneIndex]; + var TLane = T[laneIndex]; + var Tx1Lane = T[((x + 1) % 5) + 5 * y]; + var Tx2Lane = T[((x + 2) % 5) + 5 * y]; + + // Mix rows + lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high); + lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low); + } + } + + // Iota + var lane = state[0]; + var roundConstant = ROUND_CONSTANTS[round]; + lane.high ^= roundConstant.high; + lane.low ^= roundConstant.low;; + } + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + var blockSizeBits = this.blockSize * 32; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32); + dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var state = this._state; + var outputLengthBytes = this.cfg.outputLength / 8; + var outputLengthLanes = outputLengthBytes / 8; + + // Squeeze + var hashWords = []; + for (var i = 0; i < outputLengthLanes; i++) { + // Shortcuts + var lane = state[i]; + var laneMsw = lane.high; + var laneLsw = lane.low; + + // Swap endian + laneMsw = ( + (((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) | + (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00) + ); + laneLsw = ( + (((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) | + (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00) + ); + + // Squeeze state to retrieve hash + hashWords.push(laneLsw); + hashWords.push(laneMsw); + } + + // Return final computed hash + return new WordArray.init(hashWords, outputLengthBytes); + }, + + clone: function () { + var clone = Hasher.clone.call(this); + + var state = clone._state = this._state.slice(0); + for (var i = 0; i < 25; i++) { + state[i] = state[i].clone(); + } + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA3('message'); + * var hash = CryptoJS.SHA3(wordArray); + */ + C.SHA3 = Hasher._createHelper(SHA3); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA3(message, key); + */ + C.HmacSHA3 = Hasher._createHmacHelper(SHA3); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var X64WordArray = C_x64.WordArray; + var C_algo = C.algo; + + function X64Word_create() { + return X64Word.create.apply(X64Word, arguments); + } + + // Constants + var K = [ + X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd), + X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc), + X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019), + X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118), + X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe), + X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2), + X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1), + X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694), + X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3), + X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65), + X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483), + X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5), + X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210), + X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4), + X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725), + X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70), + X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926), + X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df), + X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8), + X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b), + X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001), + X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30), + X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910), + X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8), + X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53), + X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8), + X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb), + X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3), + X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60), + X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec), + X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9), + X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b), + X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207), + X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178), + X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6), + X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b), + X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493), + X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c), + X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a), + X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817) + ]; + + // Reusable objects + var W = []; + (function () { + for (var i = 0; i < 80; i++) { + W[i] = X64Word_create(); + } + }()); + + /** + * SHA-512 hash algorithm. + */ + var SHA512 = C_algo.SHA512 = Hasher.extend({ + _doReset: function () { + this._hash = new X64WordArray.init([ + new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b), + new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1), + new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f), + new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179) + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var H = this._hash.words; + + var H0 = H[0]; + var H1 = H[1]; + var H2 = H[2]; + var H3 = H[3]; + var H4 = H[4]; + var H5 = H[5]; + var H6 = H[6]; + var H7 = H[7]; + + var H0h = H0.high; + var H0l = H0.low; + var H1h = H1.high; + var H1l = H1.low; + var H2h = H2.high; + var H2l = H2.low; + var H3h = H3.high; + var H3l = H3.low; + var H4h = H4.high; + var H4l = H4.low; + var H5h = H5.high; + var H5l = H5.low; + var H6h = H6.high; + var H6l = H6.low; + var H7h = H7.high; + var H7l = H7.low; + + // Working variables + var ah = H0h; + var al = H0l; + var bh = H1h; + var bl = H1l; + var ch = H2h; + var cl = H2l; + var dh = H3h; + var dl = H3l; + var eh = H4h; + var el = H4l; + var fh = H5h; + var fl = H5l; + var gh = H6h; + var gl = H6l; + var hh = H7h; + var hl = H7l; + + // Rounds + for (var i = 0; i < 80; i++) { + // Shortcut + var Wi = W[i]; + + // Extend message + if (i < 16) { + var Wih = Wi.high = M[offset + i * 2] | 0; + var Wil = Wi.low = M[offset + i * 2 + 1] | 0; + } else { + // Gamma0 + var gamma0x = W[i - 15]; + var gamma0xh = gamma0x.high; + var gamma0xl = gamma0x.low; + var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7); + var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25)); + + // Gamma1 + var gamma1x = W[i - 2]; + var gamma1xh = gamma1x.high; + var gamma1xl = gamma1x.low; + var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6); + var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26)); + + // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] + var Wi7 = W[i - 7]; + var Wi7h = Wi7.high; + var Wi7l = Wi7.low; + + var Wi16 = W[i - 16]; + var Wi16h = Wi16.high; + var Wi16l = Wi16.low; + + var Wil = gamma0l + Wi7l; + var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0); + var Wil = Wil + gamma1l; + var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0); + var Wil = Wil + Wi16l; + var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0); + + Wi.high = Wih; + Wi.low = Wil; + } + + var chh = (eh & fh) ^ (~eh & gh); + var chl = (el & fl) ^ (~el & gl); + var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch); + var majl = (al & bl) ^ (al & cl) ^ (bl & cl); + + var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7)); + var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7)); + var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9)); + var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9)); + + // t1 = h + sigma1 + ch + K[i] + W[i] + var Ki = K[i]; + var Kih = Ki.high; + var Kil = Ki.low; + + var t1l = hl + sigma1l; + var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0); + var t1l = t1l + chl; + var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0); + var t1l = t1l + Kil; + var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0); + var t1l = t1l + Wil; + var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0); + + // t2 = sigma0 + maj + var t2l = sigma0l + majl; + var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0); + + // Update working variables + hh = gh; + hl = gl; + gh = fh; + gl = fl; + fh = eh; + fl = el; + el = (dl + t1l) | 0; + eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0; + dh = ch; + dl = cl; + ch = bh; + cl = bl; + bh = ah; + bl = al; + al = (t1l + t2l) | 0; + ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0; + } + + // Intermediate hash value + H0l = H0.low = (H0l + al); + H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0)); + H1l = H1.low = (H1l + bl); + H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0)); + H2l = H2.low = (H2l + cl); + H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0)); + H3l = H3.low = (H3l + dl); + H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0)); + H4l = H4.low = (H4l + el); + H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0)); + H5l = H5.low = (H5l + fl); + H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0)); + H6l = H6.low = (H6l + gl); + H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0)); + H7l = H7.low = (H7l + hl); + H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0)); + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Convert hash to 32-bit word array before returning + var hash = this._hash.toX32(); + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + }, + + blockSize: 1024/32 + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA512('message'); + * var hash = CryptoJS.SHA512(wordArray); + */ + C.SHA512 = Hasher._createHelper(SHA512); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA512(message, key); + */ + C.HmacSHA512 = Hasher._createHmacHelper(SHA512); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var X64WordArray = C_x64.WordArray; + var C_algo = C.algo; + var SHA512 = C_algo.SHA512; + + /** + * SHA-384 hash algorithm. + */ + var SHA384 = C_algo.SHA384 = SHA512.extend({ + _doReset: function () { + this._hash = new X64WordArray.init([ + new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507), + new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939), + new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511), + new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4) + ]); + }, + + _doFinalize: function () { + var hash = SHA512._doFinalize.call(this); + + hash.sigBytes -= 16; + + return hash; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA384('message'); + * var hash = CryptoJS.SHA384(wordArray); + */ + C.SHA384 = SHA512._createHelper(SHA384); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA384(message, key); + */ + C.HmacSHA384 = SHA512._createHmacHelper(SHA384); + }()); + + + /** + * Cipher core components. + */ + CryptoJS.lib.Cipher || (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm; + var C_enc = C.enc; + var Utf8 = C_enc.Utf8; + var Base64 = C_enc.Base64; + var C_algo = C.algo; + var EvpKDF = C_algo.EvpKDF; + + /** + * Abstract base cipher template. + * + * @property {number} keySize This cipher's key size. Default: 4 (128 bits) + * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits) + * @property {number} _ENC_XFORM_MODE A constant representing encryption mode. + * @property {number} _DEC_XFORM_MODE A constant representing decryption mode. + */ + var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + * + * @property {WordArray} iv The IV to use for this operation. + */ + cfg: Base.extend(), + + /** + * Creates this cipher in encryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray }); + */ + createEncryptor: function (key, cfg) { + return this.create(this._ENC_XFORM_MODE, key, cfg); + }, + + /** + * Creates this cipher in decryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray }); + */ + createDecryptor: function (key, cfg) { + return this.create(this._DEC_XFORM_MODE, key, cfg); + }, + + /** + * Initializes a newly created cipher. + * + * @param {number} xformMode Either the encryption or decryption transormation mode constant. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @example + * + * var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray }); + */ + init: function (xformMode, key, cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Store transform mode and key + this._xformMode = xformMode; + this._key = key; + + // Set initial values + this.reset(); + }, + + /** + * Resets this cipher to its initial state. + * + * @example + * + * cipher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-cipher logic + this._doReset(); + }, + + /** + * Adds data to be encrypted or decrypted. + * + * @param {WordArray|string} dataUpdate The data to encrypt or decrypt. + * + * @return {WordArray} The data after processing. + * + * @example + * + * var encrypted = cipher.process('data'); + * var encrypted = cipher.process(wordArray); + */ + process: function (dataUpdate) { + // Append + this._append(dataUpdate); + + // Process available blocks + return this._process(); + }, + + /** + * Finalizes the encryption or decryption process. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt. + * + * @return {WordArray} The data after final processing. + * + * @example + * + * var encrypted = cipher.finalize(); + * var encrypted = cipher.finalize('data'); + * var encrypted = cipher.finalize(wordArray); + */ + finalize: function (dataUpdate) { + // Final data update + if (dataUpdate) { + this._append(dataUpdate); + } + + // Perform concrete-cipher logic + var finalProcessedData = this._doFinalize(); + + return finalProcessedData; + }, + + keySize: 128/32, + + ivSize: 128/32, + + _ENC_XFORM_MODE: 1, + + _DEC_XFORM_MODE: 2, + + /** + * Creates shortcut functions to a cipher's object interface. + * + * @param {Cipher} cipher The cipher to create a helper for. + * + * @return {Object} An object with encrypt and decrypt shortcut functions. + * + * @static + * + * @example + * + * var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES); + */ + _createHelper: (function () { + function selectCipherStrategy(key) { + if (typeof key == 'string') { + return PasswordBasedCipher; + } else { + return SerializableCipher; + } + } + + return function (cipher) { + return { + encrypt: function (message, key, cfg) { + return selectCipherStrategy(key).encrypt(cipher, message, key, cfg); + }, + + decrypt: function (ciphertext, key, cfg) { + return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg); + } + }; + }; + }()) + }); + + /** + * Abstract base stream cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits) + */ + var StreamCipher = C_lib.StreamCipher = Cipher.extend({ + _doFinalize: function () { + // Process partial blocks + var finalProcessedBlocks = this._process(!!'flush'); + + return finalProcessedBlocks; + }, + + blockSize: 1 + }); + + /** + * Mode namespace. + */ + var C_mode = C.mode = {}; + + /** + * Abstract base block cipher mode template. + */ + var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({ + /** + * Creates this mode for encryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words); + */ + createEncryptor: function (cipher, iv) { + return this.Encryptor.create(cipher, iv); + }, + + /** + * Creates this mode for decryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words); + */ + createDecryptor: function (cipher, iv) { + return this.Decryptor.create(cipher, iv); + }, + + /** + * Initializes a newly created mode. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @example + * + * var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words); + */ + init: function (cipher, iv) { + this._cipher = cipher; + this._iv = iv; + } + }); + + /** + * Cipher Block Chaining mode. + */ + var CBC = C_mode.CBC = (function () { + /** + * Abstract base CBC mode. + */ + var CBC = BlockCipherMode.extend(); + + /** + * CBC encryptor. + */ + CBC.Encryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // XOR and encrypt + xorBlock.call(this, words, offset, blockSize); + cipher.encryptBlock(words, offset); + + // Remember this block to use with next block + this._prevBlock = words.slice(offset, offset + blockSize); + } + }); + + /** + * CBC decryptor. + */ + CBC.Decryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // Remember this block to use with next block + var thisBlock = words.slice(offset, offset + blockSize); + + // Decrypt and XOR + cipher.decryptBlock(words, offset); + xorBlock.call(this, words, offset, blockSize); + + // This block becomes the previous block + this._prevBlock = thisBlock; + } + }); + + function xorBlock(words, offset, blockSize) { + // Shortcut + var iv = this._iv; + + // Choose mixing block + if (iv) { + var block = iv; + + // Remove IV for subsequent blocks + this._iv = undefined; + } else { + var block = this._prevBlock; + } + + // XOR blocks + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= block[i]; + } + } + + return CBC; + }()); + + /** + * Padding namespace. + */ + var C_pad = C.pad = {}; + + /** + * PKCS #5/7 padding strategy. + */ + var Pkcs7 = C_pad.Pkcs7 = { + /** + * Pads data using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to pad. + * @param {number} blockSize The multiple that the data should be padded to. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.pad(wordArray, 4); + */ + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; + + // Create padding word + var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes; + + // Create padding + var paddingWords = []; + for (var i = 0; i < nPaddingBytes; i += 4) { + paddingWords.push(paddingWord); + } + var padding = WordArray.create(paddingWords, nPaddingBytes); + + // Add padding + data.concat(padding); + }, + + /** + * Unpads data that had been padded using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to unpad. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.unpad(wordArray); + */ + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + /** + * Abstract base block cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits) + */ + var BlockCipher = C_lib.BlockCipher = Cipher.extend({ + /** + * Configuration options. + * + * @property {Mode} mode The block mode to use. Default: CBC + * @property {Padding} padding The padding strategy to use. Default: Pkcs7 + */ + cfg: Cipher.cfg.extend({ + mode: CBC, + padding: Pkcs7 + }), + + reset: function () { + // Reset cipher + Cipher.reset.call(this); + + // Shortcuts + var cfg = this.cfg; + var iv = cfg.iv; + var mode = cfg.mode; + + // Reset block mode + if (this._xformMode == this._ENC_XFORM_MODE) { + var modeCreator = mode.createEncryptor; + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + var modeCreator = mode.createDecryptor; + // Keep at least one block in the buffer for unpadding + this._minBufferSize = 1; + } + + if (this._mode && this._mode.__creator == modeCreator) { + this._mode.init(this, iv && iv.words); + } else { + this._mode = modeCreator.call(mode, this, iv && iv.words); + this._mode.__creator = modeCreator; + } + }, + + _doProcessBlock: function (words, offset) { + this._mode.processBlock(words, offset); + }, + + _doFinalize: function () { + // Shortcut + var padding = this.cfg.padding; + + // Finalize + if (this._xformMode == this._ENC_XFORM_MODE) { + // Pad data + padding.pad(this._data, this.blockSize); + + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + + // Unpad data + padding.unpad(finalProcessedBlocks); + } + + return finalProcessedBlocks; + }, + + blockSize: 128/32 + }); + + /** + * A collection of cipher parameters. + * + * @property {WordArray} ciphertext The raw ciphertext. + * @property {WordArray} key The key to this ciphertext. + * @property {WordArray} iv The IV used in the ciphering operation. + * @property {WordArray} salt The salt used with a key derivation function. + * @property {Cipher} algorithm The cipher algorithm. + * @property {Mode} mode The block mode used in the ciphering operation. + * @property {Padding} padding The padding scheme used in the ciphering operation. + * @property {number} blockSize The block size of the cipher. + * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string. + */ + var CipherParams = C_lib.CipherParams = Base.extend({ + /** + * Initializes a newly created cipher params object. + * + * @param {Object} cipherParams An object with any of the possible cipher parameters. + * + * @example + * + * var cipherParams = CryptoJS.lib.CipherParams.create({ + * ciphertext: ciphertextWordArray, + * key: keyWordArray, + * iv: ivWordArray, + * salt: saltWordArray, + * algorithm: CryptoJS.algo.AES, + * mode: CryptoJS.mode.CBC, + * padding: CryptoJS.pad.PKCS7, + * blockSize: 4, + * formatter: CryptoJS.format.OpenSSL + * }); + */ + init: function (cipherParams) { + this.mixIn(cipherParams); + }, + + /** + * Converts this cipher params object to a string. + * + * @param {Format} formatter (Optional) The formatting strategy to use. + * + * @return {string} The stringified cipher params. + * + * @throws Error If neither the formatter nor the default formatter is set. + * + * @example + * + * var string = cipherParams + ''; + * var string = cipherParams.toString(); + * var string = cipherParams.toString(CryptoJS.format.OpenSSL); + */ + toString: function (formatter) { + return (formatter || this.formatter).stringify(this); + } + }); + + /** + * Format namespace. + */ + var C_format = C.format = {}; + + /** + * OpenSSL formatting strategy. + */ + var OpenSSLFormatter = C_format.OpenSSL = { + /** + * Converts a cipher params object to an OpenSSL-compatible string. + * + * @param {CipherParams} cipherParams The cipher params object. + * + * @return {string} The OpenSSL-compatible string. + * + * @static + * + * @example + * + * var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams); + */ + stringify: function (cipherParams) { + // Shortcuts + var ciphertext = cipherParams.ciphertext; + var salt = cipherParams.salt; + + // Format + if (salt) { + var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext); + } else { + var wordArray = ciphertext; + } + + return wordArray.toString(Base64); + }, + + /** + * Converts an OpenSSL-compatible string to a cipher params object. + * + * @param {string} openSSLStr The OpenSSL-compatible string. + * + * @return {CipherParams} The cipher params object. + * + * @static + * + * @example + * + * var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString); + */ + parse: function (openSSLStr) { + // Parse base64 + var ciphertext = Base64.parse(openSSLStr); + + // Shortcut + var ciphertextWords = ciphertext.words; + + // Test for salt + if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) { + // Extract salt + var salt = WordArray.create(ciphertextWords.slice(2, 4)); + + // Remove salt from ciphertext + ciphertextWords.splice(0, 4); + ciphertext.sigBytes -= 16; + } + + return CipherParams.create({ ciphertext: ciphertext, salt: salt }); + } + }; + + /** + * A cipher wrapper that returns ciphertext as a serializable cipher params object. + */ + var SerializableCipher = C_lib.SerializableCipher = Base.extend({ + /** + * Configuration options. + * + * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL + */ + cfg: Base.extend({ + format: OpenSSLFormatter + }), + + /** + * Encrypts a message. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv }); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Encrypt + var encryptor = cipher.createEncryptor(key, cfg); + var ciphertext = encryptor.finalize(message); + + // Shortcut + var cipherCfg = encryptor.cfg; + + // Create and return serializable cipher params + return CipherParams.create({ + ciphertext: ciphertext, + key: key, + iv: cipherCfg.iv, + algorithm: cipher, + mode: cipherCfg.mode, + padding: cipherCfg.padding, + blockSize: cipher.blockSize, + formatter: cfg.format + }); + }, + + /** + * Decrypts serialized ciphertext. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Decrypt + var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext); + + return plaintext; + }, + + /** + * Converts serialized ciphertext to CipherParams, + * else assumed CipherParams already and returns ciphertext unchanged. + * + * @param {CipherParams|string} ciphertext The ciphertext. + * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext. + * + * @return {CipherParams} The unserialized ciphertext. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format); + */ + _parse: function (ciphertext, format) { + if (typeof ciphertext == 'string') { + return format.parse(ciphertext, this); + } else { + return ciphertext; + } + } + }); + + /** + * Key derivation function namespace. + */ + var C_kdf = C.kdf = {}; + + /** + * OpenSSL key derivation function. + */ + var OpenSSLKdf = C_kdf.OpenSSL = { + /** + * Derives a key and IV from a password. + * + * @param {string} password The password to derive from. + * @param {number} keySize The size in words of the key to generate. + * @param {number} ivSize The size in words of the IV to generate. + * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly. + * + * @return {CipherParams} A cipher params object with the key, IV, and salt. + * + * @static + * + * @example + * + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32); + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt'); + */ + execute: function (password, keySize, ivSize, salt) { + // Generate random salt + if (!salt) { + salt = WordArray.random(64/8); + } + + // Derive key and IV + var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt); + + // Separate key and IV + var iv = WordArray.create(key.words.slice(keySize), ivSize * 4); + key.sigBytes = keySize * 4; + + // Return params + return CipherParams.create({ key: key, iv: iv, salt: salt }); + } + }; + + /** + * A serializable cipher wrapper that derives the key from a password, + * and returns ciphertext as a serializable cipher params object. + */ + var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({ + /** + * Configuration options. + * + * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL + */ + cfg: SerializableCipher.cfg.extend({ + kdf: OpenSSLKdf + }), + + /** + * Encrypts a message using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password'); + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Encrypt + var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg); + + // Mix in derived params + ciphertext.mixIn(derivedParams); + + return ciphertext; + }, + + /** + * Decrypts serialized ciphertext using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Decrypt + var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg); + + return plaintext; + } + }); + }()); + + + /** + * Cipher Feedback block mode. + */ + CryptoJS.mode.CFB = (function () { + var CFB = CryptoJS.lib.BlockCipherMode.extend(); + + CFB.Encryptor = CFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); + + // Remember this block to use with next block + this._prevBlock = words.slice(offset, offset + blockSize); + } + }); + + CFB.Decryptor = CFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // Remember this block to use with next block + var thisBlock = words.slice(offset, offset + blockSize); + + generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); + + // This block becomes the previous block + this._prevBlock = thisBlock; + } + }); + + function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) { + // Shortcut + var iv = this._iv; + + // Generate keystream + if (iv) { + var keystream = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } else { + var keystream = this._prevBlock; + } + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + + return CFB; + }()); + + + /** + * Electronic Codebook block mode. + */ + CryptoJS.mode.ECB = (function () { + var ECB = CryptoJS.lib.BlockCipherMode.extend(); + + ECB.Encryptor = ECB.extend({ + processBlock: function (words, offset) { + this._cipher.encryptBlock(words, offset); + } + }); + + ECB.Decryptor = ECB.extend({ + processBlock: function (words, offset) { + this._cipher.decryptBlock(words, offset); + } + }); + + return ECB; + }()); + + + /** + * ANSI X.923 padding strategy. + */ + CryptoJS.pad.AnsiX923 = { + pad: function (data, blockSize) { + // Shortcuts + var dataSigBytes = data.sigBytes; + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes; + + // Compute last byte position + var lastBytePos = dataSigBytes + nPaddingBytes - 1; + + // Pad + data.clamp(); + data.words[lastBytePos >>> 2] |= nPaddingBytes << (24 - (lastBytePos % 4) * 8); + data.sigBytes += nPaddingBytes; + }, + + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + + /** + * ISO 10126 padding strategy. + */ + CryptoJS.pad.Iso10126 = { + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; + + // Pad + data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)). + concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1)); + }, + + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + + /** + * ISO/IEC 9797-1 Padding Method 2. + */ + CryptoJS.pad.Iso97971 = { + pad: function (data, blockSize) { + // Add 0x80 byte + data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1)); + + // Zero pad the rest + CryptoJS.pad.ZeroPadding.pad(data, blockSize); + }, + + unpad: function (data) { + // Remove zero padding + CryptoJS.pad.ZeroPadding.unpad(data); + + // Remove one more byte -- the 0x80 byte + data.sigBytes--; + } + }; + + + /** + * Output Feedback block mode. + */ + CryptoJS.mode.OFB = (function () { + var OFB = CryptoJS.lib.BlockCipherMode.extend(); + + var Encryptor = OFB.Encryptor = OFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var keystream = this._keystream; + + // Generate keystream + if (iv) { + keystream = this._keystream = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + OFB.Decryptor = Encryptor; + + return OFB; + }()); + + + /** + * A noop padding strategy. + */ + CryptoJS.pad.NoPadding = { + pad: function () { + }, + + unpad: function () { + } + }; + + + (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var CipherParams = C_lib.CipherParams; + var C_enc = C.enc; + var Hex = C_enc.Hex; + var C_format = C.format; + + var HexFormatter = C_format.Hex = { + /** + * Converts the ciphertext of a cipher params object to a hexadecimally encoded string. + * + * @param {CipherParams} cipherParams The cipher params object. + * + * @return {string} The hexadecimally encoded string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.format.Hex.stringify(cipherParams); + */ + stringify: function (cipherParams) { + return cipherParams.ciphertext.toString(Hex); + }, + + /** + * Converts a hexadecimally encoded ciphertext string to a cipher params object. + * + * @param {string} input The hexadecimally encoded string. + * + * @return {CipherParams} The cipher params object. + * + * @static + * + * @example + * + * var cipherParams = CryptoJS.format.Hex.parse(hexString); + */ + parse: function (input) { + var ciphertext = Hex.parse(input); + return CipherParams.create({ ciphertext: ciphertext }); + } + }; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var BlockCipher = C_lib.BlockCipher; + var C_algo = C.algo; + + // Lookup tables + var SBOX = []; + var INV_SBOX = []; + var SUB_MIX_0 = []; + var SUB_MIX_1 = []; + var SUB_MIX_2 = []; + var SUB_MIX_3 = []; + var INV_SUB_MIX_0 = []; + var INV_SUB_MIX_1 = []; + var INV_SUB_MIX_2 = []; + var INV_SUB_MIX_3 = []; + + // Compute lookup tables + (function () { + // Compute double table + var d = []; + for (var i = 0; i < 256; i++) { + if (i < 128) { + d[i] = i << 1; + } else { + d[i] = (i << 1) ^ 0x11b; + } + } + + // Walk GF(2^8) + var x = 0; + var xi = 0; + for (var i = 0; i < 256; i++) { + // Compute sbox + var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); + sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; + SBOX[x] = sx; + INV_SBOX[sx] = x; + + // Compute multiplication + var x2 = d[x]; + var x4 = d[x2]; + var x8 = d[x4]; + + // Compute sub bytes, mix columns tables + var t = (d[sx] * 0x101) ^ (sx * 0x1010100); + SUB_MIX_0[x] = (t << 24) | (t >>> 8); + SUB_MIX_1[x] = (t << 16) | (t >>> 16); + SUB_MIX_2[x] = (t << 8) | (t >>> 24); + SUB_MIX_3[x] = t; + + // Compute inv sub bytes, inv mix columns tables + var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); + INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8); + INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16); + INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24); + INV_SUB_MIX_3[sx] = t; + + // Compute next counter + if (!x) { + x = xi = 1; + } else { + x = x2 ^ d[d[d[x8 ^ x2]]]; + xi ^= d[d[xi]]; + } + } + }()); + + // Precomputed Rcon lookup + var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; + + /** + * AES block cipher algorithm. + */ + var AES = C_algo.AES = BlockCipher.extend({ + _doReset: function () { + // Skip reset of nRounds has been set before and key did not change + if (this._nRounds && this._keyPriorReset === this._key) { + return; + } + + // Shortcuts + var key = this._keyPriorReset = this._key; + var keyWords = key.words; + var keySize = key.sigBytes / 4; + + // Compute number of rounds + var nRounds = this._nRounds = keySize + 6; + + // Compute number of key schedule rows + var ksRows = (nRounds + 1) * 4; + + // Compute key schedule + var keySchedule = this._keySchedule = []; + for (var ksRow = 0; ksRow < ksRows; ksRow++) { + if (ksRow < keySize) { + keySchedule[ksRow] = keyWords[ksRow]; + } else { + var t = keySchedule[ksRow - 1]; + + if (!(ksRow % keySize)) { + // Rot word + t = (t << 8) | (t >>> 24); + + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + + // Mix Rcon + t ^= RCON[(ksRow / keySize) | 0] << 24; + } else if (keySize > 6 && ksRow % keySize == 4) { + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + } + + keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t; + } + } + + // Compute inv key schedule + var invKeySchedule = this._invKeySchedule = []; + for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) { + var ksRow = ksRows - invKsRow; + + if (invKsRow % 4) { + var t = keySchedule[ksRow]; + } else { + var t = keySchedule[ksRow - 4]; + } + + if (invKsRow < 4 || ksRow <= 4) { + invKeySchedule[invKsRow] = t; + } else { + invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^ + INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]]; + } + } + }, + + encryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX); + }, + + decryptBlock: function (M, offset) { + // Swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + + this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX); + + // Inv swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + }, + + _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) { + // Shortcut + var nRounds = this._nRounds; + + // Get input, add round key + var s0 = M[offset] ^ keySchedule[0]; + var s1 = M[offset + 1] ^ keySchedule[1]; + var s2 = M[offset + 2] ^ keySchedule[2]; + var s3 = M[offset + 3] ^ keySchedule[3]; + + // Key schedule row counter + var ksRow = 4; + + // Rounds + for (var round = 1; round < nRounds; round++) { + // Shift rows, sub bytes, mix columns, add round key + var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++]; + var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++]; + var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++]; + var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++]; + + // Update state + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + // Shift rows, sub bytes, add round key + var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++]; + var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++]; + var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++]; + var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++]; + + // Set output + M[offset] = t0; + M[offset + 1] = t1; + M[offset + 2] = t2; + M[offset + 3] = t3; + }, + + keySize: 256/32 + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.AES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg); + */ + C.AES = BlockCipher._createHelper(AES); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var BlockCipher = C_lib.BlockCipher; + var C_algo = C.algo; + + // Permuted Choice 1 constants + var PC1 = [ + 57, 49, 41, 33, 25, 17, 9, 1, + 58, 50, 42, 34, 26, 18, 10, 2, + 59, 51, 43, 35, 27, 19, 11, 3, + 60, 52, 44, 36, 63, 55, 47, 39, + 31, 23, 15, 7, 62, 54, 46, 38, + 30, 22, 14, 6, 61, 53, 45, 37, + 29, 21, 13, 5, 28, 20, 12, 4 + ]; + + // Permuted Choice 2 constants + var PC2 = [ + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 + ]; + + // Cumulative bit shift constants + var BIT_SHIFTS = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28]; + + // SBOXes and round permutation constants + var SBOX_P = [ + { + 0x0: 0x808200, + 0x10000000: 0x8000, + 0x20000000: 0x808002, + 0x30000000: 0x2, + 0x40000000: 0x200, + 0x50000000: 0x808202, + 0x60000000: 0x800202, + 0x70000000: 0x800000, + 0x80000000: 0x202, + 0x90000000: 0x800200, + 0xa0000000: 0x8200, + 0xb0000000: 0x808000, + 0xc0000000: 0x8002, + 0xd0000000: 0x800002, + 0xe0000000: 0x0, + 0xf0000000: 0x8202, + 0x8000000: 0x0, + 0x18000000: 0x808202, + 0x28000000: 0x8202, + 0x38000000: 0x8000, + 0x48000000: 0x808200, + 0x58000000: 0x200, + 0x68000000: 0x808002, + 0x78000000: 0x2, + 0x88000000: 0x800200, + 0x98000000: 0x8200, + 0xa8000000: 0x808000, + 0xb8000000: 0x800202, + 0xc8000000: 0x800002, + 0xd8000000: 0x8002, + 0xe8000000: 0x202, + 0xf8000000: 0x800000, + 0x1: 0x8000, + 0x10000001: 0x2, + 0x20000001: 0x808200, + 0x30000001: 0x800000, + 0x40000001: 0x808002, + 0x50000001: 0x8200, + 0x60000001: 0x200, + 0x70000001: 0x800202, + 0x80000001: 0x808202, + 0x90000001: 0x808000, + 0xa0000001: 0x800002, + 0xb0000001: 0x8202, + 0xc0000001: 0x202, + 0xd0000001: 0x800200, + 0xe0000001: 0x8002, + 0xf0000001: 0x0, + 0x8000001: 0x808202, + 0x18000001: 0x808000, + 0x28000001: 0x800000, + 0x38000001: 0x200, + 0x48000001: 0x8000, + 0x58000001: 0x800002, + 0x68000001: 0x2, + 0x78000001: 0x8202, + 0x88000001: 0x8002, + 0x98000001: 0x800202, + 0xa8000001: 0x202, + 0xb8000001: 0x808200, + 0xc8000001: 0x800200, + 0xd8000001: 0x0, + 0xe8000001: 0x8200, + 0xf8000001: 0x808002 + }, + { + 0x0: 0x40084010, + 0x1000000: 0x4000, + 0x2000000: 0x80000, + 0x3000000: 0x40080010, + 0x4000000: 0x40000010, + 0x5000000: 0x40084000, + 0x6000000: 0x40004000, + 0x7000000: 0x10, + 0x8000000: 0x84000, + 0x9000000: 0x40004010, + 0xa000000: 0x40000000, + 0xb000000: 0x84010, + 0xc000000: 0x80010, + 0xd000000: 0x0, + 0xe000000: 0x4010, + 0xf000000: 0x40080000, + 0x800000: 0x40004000, + 0x1800000: 0x84010, + 0x2800000: 0x10, + 0x3800000: 0x40004010, + 0x4800000: 0x40084010, + 0x5800000: 0x40000000, + 0x6800000: 0x80000, + 0x7800000: 0x40080010, + 0x8800000: 0x80010, + 0x9800000: 0x0, + 0xa800000: 0x4000, + 0xb800000: 0x40080000, + 0xc800000: 0x40000010, + 0xd800000: 0x84000, + 0xe800000: 0x40084000, + 0xf800000: 0x4010, + 0x10000000: 0x0, + 0x11000000: 0x40080010, + 0x12000000: 0x40004010, + 0x13000000: 0x40084000, + 0x14000000: 0x40080000, + 0x15000000: 0x10, + 0x16000000: 0x84010, + 0x17000000: 0x4000, + 0x18000000: 0x4010, + 0x19000000: 0x80000, + 0x1a000000: 0x80010, + 0x1b000000: 0x40000010, + 0x1c000000: 0x84000, + 0x1d000000: 0x40004000, + 0x1e000000: 0x40000000, + 0x1f000000: 0x40084010, + 0x10800000: 0x84010, + 0x11800000: 0x80000, + 0x12800000: 0x40080000, + 0x13800000: 0x4000, + 0x14800000: 0x40004000, + 0x15800000: 0x40084010, + 0x16800000: 0x10, + 0x17800000: 0x40000000, + 0x18800000: 0x40084000, + 0x19800000: 0x40000010, + 0x1a800000: 0x40004010, + 0x1b800000: 0x80010, + 0x1c800000: 0x0, + 0x1d800000: 0x4010, + 0x1e800000: 0x40080010, + 0x1f800000: 0x84000 + }, + { + 0x0: 0x104, + 0x100000: 0x0, + 0x200000: 0x4000100, + 0x300000: 0x10104, + 0x400000: 0x10004, + 0x500000: 0x4000004, + 0x600000: 0x4010104, + 0x700000: 0x4010000, + 0x800000: 0x4000000, + 0x900000: 0x4010100, + 0xa00000: 0x10100, + 0xb00000: 0x4010004, + 0xc00000: 0x4000104, + 0xd00000: 0x10000, + 0xe00000: 0x4, + 0xf00000: 0x100, + 0x80000: 0x4010100, + 0x180000: 0x4010004, + 0x280000: 0x0, + 0x380000: 0x4000100, + 0x480000: 0x4000004, + 0x580000: 0x10000, + 0x680000: 0x10004, + 0x780000: 0x104, + 0x880000: 0x4, + 0x980000: 0x100, + 0xa80000: 0x4010000, + 0xb80000: 0x10104, + 0xc80000: 0x10100, + 0xd80000: 0x4000104, + 0xe80000: 0x4010104, + 0xf80000: 0x4000000, + 0x1000000: 0x4010100, + 0x1100000: 0x10004, + 0x1200000: 0x10000, + 0x1300000: 0x4000100, + 0x1400000: 0x100, + 0x1500000: 0x4010104, + 0x1600000: 0x4000004, + 0x1700000: 0x0, + 0x1800000: 0x4000104, + 0x1900000: 0x4000000, + 0x1a00000: 0x4, + 0x1b00000: 0x10100, + 0x1c00000: 0x4010000, + 0x1d00000: 0x104, + 0x1e00000: 0x10104, + 0x1f00000: 0x4010004, + 0x1080000: 0x4000000, + 0x1180000: 0x104, + 0x1280000: 0x4010100, + 0x1380000: 0x0, + 0x1480000: 0x10004, + 0x1580000: 0x4000100, + 0x1680000: 0x100, + 0x1780000: 0x4010004, + 0x1880000: 0x10000, + 0x1980000: 0x4010104, + 0x1a80000: 0x10104, + 0x1b80000: 0x4000004, + 0x1c80000: 0x4000104, + 0x1d80000: 0x4010000, + 0x1e80000: 0x4, + 0x1f80000: 0x10100 + }, + { + 0x0: 0x80401000, + 0x10000: 0x80001040, + 0x20000: 0x401040, + 0x30000: 0x80400000, + 0x40000: 0x0, + 0x50000: 0x401000, + 0x60000: 0x80000040, + 0x70000: 0x400040, + 0x80000: 0x80000000, + 0x90000: 0x400000, + 0xa0000: 0x40, + 0xb0000: 0x80001000, + 0xc0000: 0x80400040, + 0xd0000: 0x1040, + 0xe0000: 0x1000, + 0xf0000: 0x80401040, + 0x8000: 0x80001040, + 0x18000: 0x40, + 0x28000: 0x80400040, + 0x38000: 0x80001000, + 0x48000: 0x401000, + 0x58000: 0x80401040, + 0x68000: 0x0, + 0x78000: 0x80400000, + 0x88000: 0x1000, + 0x98000: 0x80401000, + 0xa8000: 0x400000, + 0xb8000: 0x1040, + 0xc8000: 0x80000000, + 0xd8000: 0x400040, + 0xe8000: 0x401040, + 0xf8000: 0x80000040, + 0x100000: 0x400040, + 0x110000: 0x401000, + 0x120000: 0x80000040, + 0x130000: 0x0, + 0x140000: 0x1040, + 0x150000: 0x80400040, + 0x160000: 0x80401000, + 0x170000: 0x80001040, + 0x180000: 0x80401040, + 0x190000: 0x80000000, + 0x1a0000: 0x80400000, + 0x1b0000: 0x401040, + 0x1c0000: 0x80001000, + 0x1d0000: 0x400000, + 0x1e0000: 0x40, + 0x1f0000: 0x1000, + 0x108000: 0x80400000, + 0x118000: 0x80401040, + 0x128000: 0x0, + 0x138000: 0x401000, + 0x148000: 0x400040, + 0x158000: 0x80000000, + 0x168000: 0x80001040, + 0x178000: 0x40, + 0x188000: 0x80000040, + 0x198000: 0x1000, + 0x1a8000: 0x80001000, + 0x1b8000: 0x80400040, + 0x1c8000: 0x1040, + 0x1d8000: 0x80401000, + 0x1e8000: 0x400000, + 0x1f8000: 0x401040 + }, + { + 0x0: 0x80, + 0x1000: 0x1040000, + 0x2000: 0x40000, + 0x3000: 0x20000000, + 0x4000: 0x20040080, + 0x5000: 0x1000080, + 0x6000: 0x21000080, + 0x7000: 0x40080, + 0x8000: 0x1000000, + 0x9000: 0x20040000, + 0xa000: 0x20000080, + 0xb000: 0x21040080, + 0xc000: 0x21040000, + 0xd000: 0x0, + 0xe000: 0x1040080, + 0xf000: 0x21000000, + 0x800: 0x1040080, + 0x1800: 0x21000080, + 0x2800: 0x80, + 0x3800: 0x1040000, + 0x4800: 0x40000, + 0x5800: 0x20040080, + 0x6800: 0x21040000, + 0x7800: 0x20000000, + 0x8800: 0x20040000, + 0x9800: 0x0, + 0xa800: 0x21040080, + 0xb800: 0x1000080, + 0xc800: 0x20000080, + 0xd800: 0x21000000, + 0xe800: 0x1000000, + 0xf800: 0x40080, + 0x10000: 0x40000, + 0x11000: 0x80, + 0x12000: 0x20000000, + 0x13000: 0x21000080, + 0x14000: 0x1000080, + 0x15000: 0x21040000, + 0x16000: 0x20040080, + 0x17000: 0x1000000, + 0x18000: 0x21040080, + 0x19000: 0x21000000, + 0x1a000: 0x1040000, + 0x1b000: 0x20040000, + 0x1c000: 0x40080, + 0x1d000: 0x20000080, + 0x1e000: 0x0, + 0x1f000: 0x1040080, + 0x10800: 0x21000080, + 0x11800: 0x1000000, + 0x12800: 0x1040000, + 0x13800: 0x20040080, + 0x14800: 0x20000000, + 0x15800: 0x1040080, + 0x16800: 0x80, + 0x17800: 0x21040000, + 0x18800: 0x40080, + 0x19800: 0x21040080, + 0x1a800: 0x0, + 0x1b800: 0x21000000, + 0x1c800: 0x1000080, + 0x1d800: 0x40000, + 0x1e800: 0x20040000, + 0x1f800: 0x20000080 + }, + { + 0x0: 0x10000008, + 0x100: 0x2000, + 0x200: 0x10200000, + 0x300: 0x10202008, + 0x400: 0x10002000, + 0x500: 0x200000, + 0x600: 0x200008, + 0x700: 0x10000000, + 0x800: 0x0, + 0x900: 0x10002008, + 0xa00: 0x202000, + 0xb00: 0x8, + 0xc00: 0x10200008, + 0xd00: 0x202008, + 0xe00: 0x2008, + 0xf00: 0x10202000, + 0x80: 0x10200000, + 0x180: 0x10202008, + 0x280: 0x8, + 0x380: 0x200000, + 0x480: 0x202008, + 0x580: 0x10000008, + 0x680: 0x10002000, + 0x780: 0x2008, + 0x880: 0x200008, + 0x980: 0x2000, + 0xa80: 0x10002008, + 0xb80: 0x10200008, + 0xc80: 0x0, + 0xd80: 0x10202000, + 0xe80: 0x202000, + 0xf80: 0x10000000, + 0x1000: 0x10002000, + 0x1100: 0x10200008, + 0x1200: 0x10202008, + 0x1300: 0x2008, + 0x1400: 0x200000, + 0x1500: 0x10000000, + 0x1600: 0x10000008, + 0x1700: 0x202000, + 0x1800: 0x202008, + 0x1900: 0x0, + 0x1a00: 0x8, + 0x1b00: 0x10200000, + 0x1c00: 0x2000, + 0x1d00: 0x10002008, + 0x1e00: 0x10202000, + 0x1f00: 0x200008, + 0x1080: 0x8, + 0x1180: 0x202000, + 0x1280: 0x200000, + 0x1380: 0x10000008, + 0x1480: 0x10002000, + 0x1580: 0x2008, + 0x1680: 0x10202008, + 0x1780: 0x10200000, + 0x1880: 0x10202000, + 0x1980: 0x10200008, + 0x1a80: 0x2000, + 0x1b80: 0x202008, + 0x1c80: 0x200008, + 0x1d80: 0x0, + 0x1e80: 0x10000000, + 0x1f80: 0x10002008 + }, + { + 0x0: 0x100000, + 0x10: 0x2000401, + 0x20: 0x400, + 0x30: 0x100401, + 0x40: 0x2100401, + 0x50: 0x0, + 0x60: 0x1, + 0x70: 0x2100001, + 0x80: 0x2000400, + 0x90: 0x100001, + 0xa0: 0x2000001, + 0xb0: 0x2100400, + 0xc0: 0x2100000, + 0xd0: 0x401, + 0xe0: 0x100400, + 0xf0: 0x2000000, + 0x8: 0x2100001, + 0x18: 0x0, + 0x28: 0x2000401, + 0x38: 0x2100400, + 0x48: 0x100000, + 0x58: 0x2000001, + 0x68: 0x2000000, + 0x78: 0x401, + 0x88: 0x100401, + 0x98: 0x2000400, + 0xa8: 0x2100000, + 0xb8: 0x100001, + 0xc8: 0x400, + 0xd8: 0x2100401, + 0xe8: 0x1, + 0xf8: 0x100400, + 0x100: 0x2000000, + 0x110: 0x100000, + 0x120: 0x2000401, + 0x130: 0x2100001, + 0x140: 0x100001, + 0x150: 0x2000400, + 0x160: 0x2100400, + 0x170: 0x100401, + 0x180: 0x401, + 0x190: 0x2100401, + 0x1a0: 0x100400, + 0x1b0: 0x1, + 0x1c0: 0x0, + 0x1d0: 0x2100000, + 0x1e0: 0x2000001, + 0x1f0: 0x400, + 0x108: 0x100400, + 0x118: 0x2000401, + 0x128: 0x2100001, + 0x138: 0x1, + 0x148: 0x2000000, + 0x158: 0x100000, + 0x168: 0x401, + 0x178: 0x2100400, + 0x188: 0x2000001, + 0x198: 0x2100000, + 0x1a8: 0x0, + 0x1b8: 0x2100401, + 0x1c8: 0x100401, + 0x1d8: 0x400, + 0x1e8: 0x2000400, + 0x1f8: 0x100001 + }, + { + 0x0: 0x8000820, + 0x1: 0x20000, + 0x2: 0x8000000, + 0x3: 0x20, + 0x4: 0x20020, + 0x5: 0x8020820, + 0x6: 0x8020800, + 0x7: 0x800, + 0x8: 0x8020000, + 0x9: 0x8000800, + 0xa: 0x20800, + 0xb: 0x8020020, + 0xc: 0x820, + 0xd: 0x0, + 0xe: 0x8000020, + 0xf: 0x20820, + 0x80000000: 0x800, + 0x80000001: 0x8020820, + 0x80000002: 0x8000820, + 0x80000003: 0x8000000, + 0x80000004: 0x8020000, + 0x80000005: 0x20800, + 0x80000006: 0x20820, + 0x80000007: 0x20, + 0x80000008: 0x8000020, + 0x80000009: 0x820, + 0x8000000a: 0x20020, + 0x8000000b: 0x8020800, + 0x8000000c: 0x0, + 0x8000000d: 0x8020020, + 0x8000000e: 0x8000800, + 0x8000000f: 0x20000, + 0x10: 0x20820, + 0x11: 0x8020800, + 0x12: 0x20, + 0x13: 0x800, + 0x14: 0x8000800, + 0x15: 0x8000020, + 0x16: 0x8020020, + 0x17: 0x20000, + 0x18: 0x0, + 0x19: 0x20020, + 0x1a: 0x8020000, + 0x1b: 0x8000820, + 0x1c: 0x8020820, + 0x1d: 0x20800, + 0x1e: 0x820, + 0x1f: 0x8000000, + 0x80000010: 0x20000, + 0x80000011: 0x800, + 0x80000012: 0x8020020, + 0x80000013: 0x20820, + 0x80000014: 0x20, + 0x80000015: 0x8020000, + 0x80000016: 0x8000000, + 0x80000017: 0x8000820, + 0x80000018: 0x8020820, + 0x80000019: 0x8000020, + 0x8000001a: 0x8000800, + 0x8000001b: 0x0, + 0x8000001c: 0x20800, + 0x8000001d: 0x820, + 0x8000001e: 0x20020, + 0x8000001f: 0x8020800 + } + ]; + + // Masks that select the SBOX input + var SBOX_MASK = [ + 0xf8000001, 0x1f800000, 0x01f80000, 0x001f8000, + 0x0001f800, 0x00001f80, 0x000001f8, 0x8000001f + ]; + + /** + * DES block cipher algorithm. + */ + var DES = C_algo.DES = BlockCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + + // Select 56 bits according to PC1 + var keyBits = []; + for (var i = 0; i < 56; i++) { + var keyBitPos = PC1[i] - 1; + keyBits[i] = (keyWords[keyBitPos >>> 5] >>> (31 - keyBitPos % 32)) & 1; + } + + // Assemble 16 subkeys + var subKeys = this._subKeys = []; + for (var nSubKey = 0; nSubKey < 16; nSubKey++) { + // Create subkey + var subKey = subKeys[nSubKey] = []; + + // Shortcut + var bitShift = BIT_SHIFTS[nSubKey]; + + // Select 48 bits according to PC2 + for (var i = 0; i < 24; i++) { + // Select from the left 28 key bits + subKey[(i / 6) | 0] |= keyBits[((PC2[i] - 1) + bitShift) % 28] << (31 - i % 6); + + // Select from the right 28 key bits + subKey[4 + ((i / 6) | 0)] |= keyBits[28 + (((PC2[i + 24] - 1) + bitShift) % 28)] << (31 - i % 6); + } + + // Since each subkey is applied to an expanded 32-bit input, + // the subkey can be broken into 8 values scaled to 32-bits, + // which allows the key to be used without expansion + subKey[0] = (subKey[0] << 1) | (subKey[0] >>> 31); + for (var i = 1; i < 7; i++) { + subKey[i] = subKey[i] >>> ((i - 1) * 4 + 3); + } + subKey[7] = (subKey[7] << 5) | (subKey[7] >>> 27); + } + + // Compute inverse subkeys + var invSubKeys = this._invSubKeys = []; + for (var i = 0; i < 16; i++) { + invSubKeys[i] = subKeys[15 - i]; + } + }, + + encryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._subKeys); + }, + + decryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._invSubKeys); + }, + + _doCryptBlock: function (M, offset, subKeys) { + // Get input + this._lBlock = M[offset]; + this._rBlock = M[offset + 1]; + + // Initial permutation + exchangeLR.call(this, 4, 0x0f0f0f0f); + exchangeLR.call(this, 16, 0x0000ffff); + exchangeRL.call(this, 2, 0x33333333); + exchangeRL.call(this, 8, 0x00ff00ff); + exchangeLR.call(this, 1, 0x55555555); + + // Rounds + for (var round = 0; round < 16; round++) { + // Shortcuts + var subKey = subKeys[round]; + var lBlock = this._lBlock; + var rBlock = this._rBlock; + + // Feistel function + var f = 0; + for (var i = 0; i < 8; i++) { + f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0]; + } + this._lBlock = rBlock; + this._rBlock = lBlock ^ f; + } + + // Undo swap from last round + var t = this._lBlock; + this._lBlock = this._rBlock; + this._rBlock = t; + + // Final permutation + exchangeLR.call(this, 1, 0x55555555); + exchangeRL.call(this, 8, 0x00ff00ff); + exchangeRL.call(this, 2, 0x33333333); + exchangeLR.call(this, 16, 0x0000ffff); + exchangeLR.call(this, 4, 0x0f0f0f0f); + + // Set output + M[offset] = this._lBlock; + M[offset + 1] = this._rBlock; + }, + + keySize: 64/32, + + ivSize: 64/32, + + blockSize: 64/32 + }); + + // Swap bits across the left and right words + function exchangeLR(offset, mask) { + var t = ((this._lBlock >>> offset) ^ this._rBlock) & mask; + this._rBlock ^= t; + this._lBlock ^= t << offset; + } + + function exchangeRL(offset, mask) { + var t = ((this._rBlock >>> offset) ^ this._lBlock) & mask; + this._lBlock ^= t; + this._rBlock ^= t << offset; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.DES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.DES.decrypt(ciphertext, key, cfg); + */ + C.DES = BlockCipher._createHelper(DES); + + /** + * Triple-DES block cipher algorithm. + */ + var TripleDES = C_algo.TripleDES = BlockCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + + // Create DES instances + this._des1 = DES.createEncryptor(WordArray.create(keyWords.slice(0, 2))); + this._des2 = DES.createEncryptor(WordArray.create(keyWords.slice(2, 4))); + this._des3 = DES.createEncryptor(WordArray.create(keyWords.slice(4, 6))); + }, + + encryptBlock: function (M, offset) { + this._des1.encryptBlock(M, offset); + this._des2.decryptBlock(M, offset); + this._des3.encryptBlock(M, offset); + }, + + decryptBlock: function (M, offset) { + this._des3.decryptBlock(M, offset); + this._des2.encryptBlock(M, offset); + this._des1.decryptBlock(M, offset); + }, + + keySize: 192/32, + + ivSize: 64/32, + + blockSize: 64/32 + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg); + */ + C.TripleDES = BlockCipher._createHelper(TripleDES); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + /** + * RC4 stream cipher algorithm. + */ + var RC4 = C_algo.RC4 = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + var keySigBytes = key.sigBytes; + + // Init sbox + var S = this._S = []; + for (var i = 0; i < 256; i++) { + S[i] = i; + } + + // Key setup + for (var i = 0, j = 0; i < 256; i++) { + var keyByteIndex = i % keySigBytes; + var keyByte = (keyWords[keyByteIndex >>> 2] >>> (24 - (keyByteIndex % 4) * 8)) & 0xff; + + j = (j + S[i] + keyByte) % 256; + + // Swap + var t = S[i]; + S[i] = S[j]; + S[j] = t; + } + + // Counters + this._i = this._j = 0; + }, + + _doProcessBlock: function (M, offset) { + M[offset] ^= generateKeystreamWord.call(this); + }, + + keySize: 256/32, + + ivSize: 0 + }); + + function generateKeystreamWord() { + // Shortcuts + var S = this._S; + var i = this._i; + var j = this._j; + + // Generate keystream word + var keystreamWord = 0; + for (var n = 0; n < 4; n++) { + i = (i + 1) % 256; + j = (j + S[i]) % 256; + + // Swap + var t = S[i]; + S[i] = S[j]; + S[j] = t; + + keystreamWord |= S[(S[i] + S[j]) % 256] << (24 - n * 8); + } + + // Update counters + this._i = i; + this._j = j; + + return keystreamWord; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg); + */ + C.RC4 = StreamCipher._createHelper(RC4); + + /** + * Modified RC4 stream cipher algorithm. + */ + var RC4Drop = C_algo.RC4Drop = RC4.extend({ + /** + * Configuration options. + * + * @property {number} drop The number of keystream words to drop. Default 192 + */ + cfg: RC4.cfg.extend({ + drop: 192 + }), + + _doReset: function () { + RC4._doReset.call(this); + + // Drop + for (var i = this.cfg.drop; i > 0; i--) { + generateKeystreamWord.call(this); + } + } + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg); + */ + C.RC4Drop = StreamCipher._createHelper(RC4Drop); + }()); + + + /** @preserve + * Counter block mode compatible with Dr Brian Gladman fileenc.c + * derived from CryptoJS.mode.CTR + * Jan Hruby jhruby.web@gmail.com + */ + CryptoJS.mode.CTRGladman = (function () { + var CTRGladman = CryptoJS.lib.BlockCipherMode.extend(); + + function incWord(word) + { + if (((word >> 24) & 0xff) === 0xff) { //overflow + var b1 = (word >> 16)&0xff; + var b2 = (word >> 8)&0xff; + var b3 = word & 0xff; + + if (b1 === 0xff) // overflow b1 + { + b1 = 0; + if (b2 === 0xff) + { + b2 = 0; + if (b3 === 0xff) + { + b3 = 0; + } + else + { + ++b3; + } + } + else + { + ++b2; + } + } + else + { + ++b1; + } + + word = 0; + word += (b1 << 16); + word += (b2 << 8); + word += b3; + } + else + { + word += (0x01 << 24); + } + return word; + } + + function incCounter(counter) + { + if ((counter[0] = incWord(counter[0])) === 0) + { + // encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8 + counter[1] = incWord(counter[1]); + } + return counter; + } + + var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var counter = this._counter; + + // Generate keystream + if (iv) { + counter = this._counter = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + + incCounter(counter); + + var keystream = counter.slice(0); + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + CTRGladman.Decryptor = Encryptor; + + return CTRGladman; + }()); + + + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + // Reusable objects + var S = []; + var C_ = []; + var G = []; + + /** + * Rabbit stream cipher algorithm + */ + var Rabbit = C_algo.Rabbit = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var K = this._key.words; + var iv = this.cfg.iv; + + // Swap endian + for (var i = 0; i < 4; i++) { + K[i] = (((K[i] << 8) | (K[i] >>> 24)) & 0x00ff00ff) | + (((K[i] << 24) | (K[i] >>> 8)) & 0xff00ff00); + } + + // Generate initial state values + var X = this._X = [ + K[0], (K[3] << 16) | (K[2] >>> 16), + K[1], (K[0] << 16) | (K[3] >>> 16), + K[2], (K[1] << 16) | (K[0] >>> 16), + K[3], (K[2] << 16) | (K[1] >>> 16) + ]; + + // Generate initial counter values + var C = this._C = [ + (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), + (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), + (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), + (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) + ]; + + // Carry bit + this._b = 0; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + + // Modify the counters + for (var i = 0; i < 8; i++) { + C[i] ^= X[(i + 4) & 7]; + } + + // IV setup + if (iv) { + // Shortcuts + var IV = iv.words; + var IV_0 = IV[0]; + var IV_1 = IV[1]; + + // Generate four subvectors + var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); + var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); + var i1 = (i0 >>> 16) | (i2 & 0xffff0000); + var i3 = (i2 << 16) | (i0 & 0x0000ffff); + + // Modify counter values + C[0] ^= i0; + C[1] ^= i1; + C[2] ^= i2; + C[3] ^= i3; + C[4] ^= i0; + C[5] ^= i1; + C[6] ^= i2; + C[7] ^= i3; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + } + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var X = this._X; + + // Iterate the system + nextState.call(this); + + // Generate four keystream words + S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); + S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); + S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); + S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); + + for (var i = 0; i < 4; i++) { + // Swap endian + S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | + (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); + + // Encrypt + M[offset + i] ^= S[i]; + } + }, + + blockSize: 128/32, + + ivSize: 64/32 + }); + + function nextState() { + // Shortcuts + var X = this._X; + var C = this._C; + + // Save old counter values + for (var i = 0; i < 8; i++) { + C_[i] = C[i]; + } + + // Calculate new counter values + C[0] = (C[0] + 0x4d34d34d + this._b) | 0; + C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; + C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; + C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; + C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; + C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; + C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; + C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; + this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; + + // Calculate the g-values + for (var i = 0; i < 8; i++) { + var gx = X[i] + C[i]; + + // Construct high and low argument for squaring + var ga = gx & 0xffff; + var gb = gx >>> 16; + + // Calculate high and low result of squaring + var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; + var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); + + // High XOR low + G[i] = gh ^ gl; + } + + // Calculate new state values + X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; + X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; + X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; + X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; + X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; + X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; + X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; + X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.Rabbit.encrypt(message, key, cfg); + * var plaintext = CryptoJS.Rabbit.decrypt(ciphertext, key, cfg); + */ + C.Rabbit = StreamCipher._createHelper(Rabbit); + }()); + + + /** + * Counter block mode. + */ + CryptoJS.mode.CTR = (function () { + var CTR = CryptoJS.lib.BlockCipherMode.extend(); + + var Encryptor = CTR.Encryptor = CTR.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var counter = this._counter; + + // Generate keystream + if (iv) { + counter = this._counter = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + var keystream = counter.slice(0); + cipher.encryptBlock(keystream, 0); + + // Increment counter + counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + CTR.Decryptor = Encryptor; + + return CTR; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + // Reusable objects + var S = []; + var C_ = []; + var G = []; + + /** + * Rabbit stream cipher algorithm. + * + * This is a legacy version that neglected to convert the key to little-endian. + * This error doesn't affect the cipher's security, + * but it does affect its compatibility with other implementations. + */ + var RabbitLegacy = C_algo.RabbitLegacy = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var K = this._key.words; + var iv = this.cfg.iv; + + // Generate initial state values + var X = this._X = [ + K[0], (K[3] << 16) | (K[2] >>> 16), + K[1], (K[0] << 16) | (K[3] >>> 16), + K[2], (K[1] << 16) | (K[0] >>> 16), + K[3], (K[2] << 16) | (K[1] >>> 16) + ]; + + // Generate initial counter values + var C = this._C = [ + (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), + (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), + (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), + (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) + ]; + + // Carry bit + this._b = 0; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + + // Modify the counters + for (var i = 0; i < 8; i++) { + C[i] ^= X[(i + 4) & 7]; + } + + // IV setup + if (iv) { + // Shortcuts + var IV = iv.words; + var IV_0 = IV[0]; + var IV_1 = IV[1]; + + // Generate four subvectors + var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); + var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); + var i1 = (i0 >>> 16) | (i2 & 0xffff0000); + var i3 = (i2 << 16) | (i0 & 0x0000ffff); + + // Modify counter values + C[0] ^= i0; + C[1] ^= i1; + C[2] ^= i2; + C[3] ^= i3; + C[4] ^= i0; + C[5] ^= i1; + C[6] ^= i2; + C[7] ^= i3; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + } + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var X = this._X; + + // Iterate the system + nextState.call(this); + + // Generate four keystream words + S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); + S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); + S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); + S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); + + for (var i = 0; i < 4; i++) { + // Swap endian + S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | + (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); + + // Encrypt + M[offset + i] ^= S[i]; + } + }, + + blockSize: 128/32, + + ivSize: 64/32 + }); + + function nextState() { + // Shortcuts + var X = this._X; + var C = this._C; + + // Save old counter values + for (var i = 0; i < 8; i++) { + C_[i] = C[i]; + } + + // Calculate new counter values + C[0] = (C[0] + 0x4d34d34d + this._b) | 0; + C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; + C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; + C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; + C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; + C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; + C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; + C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; + this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; + + // Calculate the g-values + for (var i = 0; i < 8; i++) { + var gx = X[i] + C[i]; + + // Construct high and low argument for squaring + var ga = gx & 0xffff; + var gb = gx >>> 16; + + // Calculate high and low result of squaring + var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; + var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); + + // High XOR low + G[i] = gh ^ gl; + } + + // Calculate new state values + X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; + X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; + X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; + X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; + X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; + X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; + X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; + X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RabbitLegacy.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RabbitLegacy.decrypt(ciphertext, key, cfg); + */ + C.RabbitLegacy = StreamCipher._createHelper(RabbitLegacy); + }()); + + + /** + * Zero padding strategy. + */ + CryptoJS.pad.ZeroPadding = { + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Pad + data.clamp(); + data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes); + }, + + unpad: function (data) { + // Shortcut + var dataWords = data.words; + + // Unpad + var i = data.sigBytes - 1; + while (!((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) { + i--; + } + data.sigBytes = i + 1; + } + }; + + + return CryptoJS; + +})); +},{}],124:[function(require,module,exports){ +'use strict'; + +var elliptic = exports; + +// hello ladies ;) +function inherits (ctor, superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; +} + +elliptic.inherits = inherits +elliptic.version = '6.5.0'; +elliptic.utils = require('./elliptic/utils'); +elliptic.curve = require('./elliptic/curve'); +elliptic.curves = require('./elliptic/curves'); + +// Protocols +elliptic.ec = require('./elliptic/ec'); + +},{"./elliptic/curve":127,"./elliptic/curves":130,"./elliptic/ec":131,"./elliptic/utils":135}],125:[function(require,module,exports){ +'use strict'; + +var BN = require('../../../../BN/bn'); +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var getNAF = utils.getNAF; +var getJSF = utils.getJSF; +var assert = utils.assert; + +function BaseCurve(type, conf) { + this.type = type; + this.p = new BN(conf.p, 16); + + // Use Montgomery, when there is no fast reduction for the prime + this.red = conf.prime ? BN.red(conf.prime) : BN.mont(this.p); + + // Useful for many curves + this.zero = new BN(0).toRed(this.red); + this.one = new BN(1).toRed(this.red); + this.two = new BN(2).toRed(this.red); + + // Curve configuration, optional + this.n = conf.n && new BN(conf.n, 16); + this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed); + + // Temporary arrays + this._wnafT1 = new Array(4); + this._wnafT2 = new Array(4); + this._wnafT3 = new Array(4); + this._wnafT4 = new Array(4); + + // Generalized Greg Maxwell's trick + var adjustCount = this.n && this.p.div(this.n); + if (!adjustCount || adjustCount.cmpn(100) > 0) { + this.redN = null; + } else { + this._maxwellTrick = true; + this.redN = this.n.toRed(this.red); + } +} +module.exports = BaseCurve; + +BaseCurve.prototype.point = function point() { + throw new Error('Not implemented'); +}; + +BaseCurve.prototype.validate = function validate() { + throw new Error('Not implemented'); +}; + +BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) { + assert(p.precomputed); + var doubles = p._getDoubles(); + + var naf = getNAF(k, 1); + var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1); + I /= 3; + + // Translate into more windowed form + var repr = []; + for (var j = 0; j < naf.length; j += doubles.step) { + var nafW = 0; + for (var k = j + doubles.step - 1; k >= j; k--) + nafW = (nafW << 1) + naf[k]; + repr.push(nafW); + } + + var a = this.jpoint(null, null, null); + var b = this.jpoint(null, null, null); + for (var i = I; i > 0; i--) { + for (var j = 0; j < repr.length; j++) { + var nafW = repr[j]; + if (nafW === i) + b = b.mixedAdd(doubles.points[j]); + else if (nafW === -i) + b = b.mixedAdd(doubles.points[j].neg()); + } + a = a.add(b); + } + return a.toP(); +}; + +BaseCurve.prototype._wnafMul = function _wnafMul(p, k) { + var w = 4; + + // Precompute window + var nafPoints = p._getNAFPoints(w); + w = nafPoints.wnd; + var wnd = nafPoints.points; + + // Get NAF form + var naf = getNAF(k, w); + + // Add `this`*(N+1) for every w-NAF index + var acc = this.jpoint(null, null, null); + for (var i = naf.length - 1; i >= 0; i--) { + // Count zeroes + for (var k = 0; i >= 0 && naf[i] === 0; i--) + k++; + if (i >= 0) + k++; + acc = acc.dblp(k); + + if (i < 0) + break; + var z = naf[i]; + assert(z !== 0); + if (p.type === 'affine') { + // J +- P + if (z > 0) + acc = acc.mixedAdd(wnd[(z - 1) >> 1]); + else + acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg()); + } else { + // J +- J + if (z > 0) + acc = acc.add(wnd[(z - 1) >> 1]); + else + acc = acc.add(wnd[(-z - 1) >> 1].neg()); + } + } + return p.type === 'affine' ? acc.toP() : acc; +}; + +BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW, + points, + coeffs, + len, + jacobianResult) { + var wndWidth = this._wnafT1; + var wnd = this._wnafT2; + var naf = this._wnafT3; + + // Fill all arrays + var max = 0; + for (var i = 0; i < len; i++) { + var p = points[i]; + var nafPoints = p._getNAFPoints(defW); + wndWidth[i] = nafPoints.wnd; + wnd[i] = nafPoints.points; + } + + // Comb small window NAFs + for (var i = len - 1; i >= 1; i -= 2) { + var a = i - 1; + var b = i; + if (wndWidth[a] !== 1 || wndWidth[b] !== 1) { + naf[a] = getNAF(coeffs[a], wndWidth[a]); + naf[b] = getNAF(coeffs[b], wndWidth[b]); + max = Math.max(naf[a].length, max); + max = Math.max(naf[b].length, max); + continue; + } + + var comb = [ + points[a], /* 1 */ + null, /* 3 */ + null, /* 5 */ + points[b] /* 7 */ + ]; + + // Try to avoid Projective points, if possible + if (points[a].y.cmp(points[b].y) === 0) { + comb[1] = points[a].add(points[b]); + comb[2] = points[a].toJ().mixedAdd(points[b].neg()); + } else if (points[a].y.cmp(points[b].y.redNeg()) === 0) { + comb[1] = points[a].toJ().mixedAdd(points[b]); + comb[2] = points[a].add(points[b].neg()); + } else { + comb[1] = points[a].toJ().mixedAdd(points[b]); + comb[2] = points[a].toJ().mixedAdd(points[b].neg()); + } + + var index = [ + -3, /* -1 -1 */ + -1, /* -1 0 */ + -5, /* -1 1 */ + -7, /* 0 -1 */ + 0, /* 0 0 */ + 7, /* 0 1 */ + 5, /* 1 -1 */ + 1, /* 1 0 */ + 3 /* 1 1 */ + ]; + + var jsf = getJSF(coeffs[a], coeffs[b]); + max = Math.max(jsf[0].length, max); + naf[a] = new Array(max); + naf[b] = new Array(max); + for (var j = 0; j < max; j++) { + var ja = jsf[0][j] | 0; + var jb = jsf[1][j] | 0; + + naf[a][j] = index[(ja + 1) * 3 + (jb + 1)]; + naf[b][j] = 0; + wnd[a] = comb; + } + } + + var acc = this.jpoint(null, null, null); + var tmp = this._wnafT4; + for (var i = max; i >= 0; i--) { + var k = 0; + + while (i >= 0) { + var zero = true; + for (var j = 0; j < len; j++) { + tmp[j] = naf[j][i] | 0; + if (tmp[j] !== 0) + zero = false; + } + if (!zero) + break; + k++; + i--; + } + if (i >= 0) + k++; + acc = acc.dblp(k); + if (i < 0) + break; + + for (var j = 0; j < len; j++) { + var z = tmp[j]; + var p; + if (z === 0) + continue; + else if (z > 0) + p = wnd[j][(z - 1) >> 1]; + else if (z < 0) + p = wnd[j][(-z - 1) >> 1].neg(); + + if (p.type === 'affine') + acc = acc.mixedAdd(p); + else + acc = acc.add(p); + } + } + // Zeroify references + for (var i = 0; i < len; i++) + wnd[i] = null; + + if (jacobianResult) + return acc; + else + return acc.toP(); +}; + +function BasePoint(curve, type) { + this.curve = curve; + this.type = type; + this.precomputed = null; +} +BaseCurve.BasePoint = BasePoint; + +BasePoint.prototype.eq = function eq(/*other*/) { + throw new Error('Not implemented'); +}; + +BasePoint.prototype.validate = function validate() { + return this.curve.validate(this); +}; + +BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) { + bytes = utils.toArray(bytes, enc); + + var len = this.p.byteLength(); + + // uncompressed, hybrid-odd, hybrid-even + if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) && + bytes.length - 1 === 2 * len) { + if (bytes[0] === 0x06) + assert(bytes[bytes.length - 1] % 2 === 0); + else if (bytes[0] === 0x07) + assert(bytes[bytes.length - 1] % 2 === 1); + + var res = this.point(bytes.slice(1, 1 + len), + bytes.slice(1 + len, 1 + 2 * len)); + + return res; + } else if ((bytes[0] === 0x02 || bytes[0] === 0x03) && + bytes.length - 1 === len) { + return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03); + } + throw new Error('Unknown point format'); +}; + +BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) { + return this.encode(enc, true); +}; + +BasePoint.prototype._encode = function _encode(compact) { + var len = this.curve.p.byteLength(); + var x = this.getX().toArray('be', len); + + if (compact) + return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x); + + return [ 0x04 ].concat(x, this.getY().toArray('be', len)) ; +}; + +BasePoint.prototype.encode = function encode(enc, compact) { + return utils.encode(this._encode(compact), enc); +}; + +BasePoint.prototype.precompute = function precompute(power) { + if (this.precomputed) + return this; + + var precomputed = { + doubles: null, + naf: null, + beta: null + }; + precomputed.naf = this._getNAFPoints(8); + precomputed.doubles = this._getDoubles(4, power); + precomputed.beta = this._getBeta(); + this.precomputed = precomputed; + + return this; +}; + +BasePoint.prototype._hasDoubles = function _hasDoubles(k) { + if (!this.precomputed) + return false; + + var doubles = this.precomputed.doubles; + if (!doubles) + return false; + + return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step); +}; + +BasePoint.prototype._getDoubles = function _getDoubles(step, power) { + if (this.precomputed && this.precomputed.doubles) + return this.precomputed.doubles; + + var doubles = [ this ]; + var acc = this; + for (var i = 0; i < power; i += step) { + for (var j = 0; j < step; j++) + acc = acc.dbl(); + doubles.push(acc); + } + return { + step: step, + points: doubles + }; +}; + +BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) { + if (this.precomputed && this.precomputed.naf) + return this.precomputed.naf; + + var res = [ this ]; + var max = (1 << wnd) - 1; + var dbl = max === 1 ? null : this.dbl(); + for (var i = 1; i < max; i++) + res[i] = res[i - 1].add(dbl); + return { + wnd: wnd, + points: res + }; +}; + +BasePoint.prototype._getBeta = function _getBeta() { + return null; +}; + +BasePoint.prototype.dblp = function dblp(k) { + var r = this; + for (var i = 0; i < k; i++) + r = r.dbl(); + return r; +}; + +},{"../../../../BN/bn":122,"../../elliptic":124}],126:[function(require,module,exports){ +'use strict'; + +var curve = require('../curve'); +var elliptic = require('../../elliptic'); +var BN = require('../../../../BN/bn'); +var inherits = elliptic.inherits; +var Base = curve.base; + +var assert = elliptic.utils.assert; + +function EdwardsCurve(conf) { + // NOTE: Important as we are creating point in Base.call() + this.twisted = (conf.a | 0) !== 1; + this.mOneA = this.twisted && (conf.a | 0) === -1; + this.extended = this.mOneA; + + Base.call(this, 'edwards', conf); + + this.a = new BN(conf.a, 16).umod(this.red.m); + this.a = this.a.toRed(this.red); + this.c = new BN(conf.c, 16).toRed(this.red); + this.c2 = this.c.redSqr(); + this.d = new BN(conf.d, 16).toRed(this.red); + this.dd = this.d.redAdd(this.d); + + assert(!this.twisted || this.c.fromRed().cmpn(1) === 0); + this.oneC = (conf.c | 0) === 1; +} +inherits(EdwardsCurve, Base); +module.exports = EdwardsCurve; + +EdwardsCurve.prototype._mulA = function _mulA(num) { + if (this.mOneA) + return num.redNeg(); + else + return this.a.redMul(num); +}; + +EdwardsCurve.prototype._mulC = function _mulC(num) { + if (this.oneC) + return num; + else + return this.c.redMul(num); +}; + +// Just for compatibility with Short curve +EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) { + return this.point(x, y, z, t); +}; + +EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) { + x = new BN(x, 16); + if (!x.red) + x = x.toRed(this.red); + + var x2 = x.redSqr(); + var rhs = this.c2.redSub(this.a.redMul(x2)); + var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); + + var y2 = rhs.redMul(lhs.redInvm()); + var y = y2.redSqrt(); + if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) + throw new Error('invalid point'); + + var isOdd = y.fromRed().isOdd(); + if (odd && !isOdd || !odd && isOdd) + y = y.redNeg(); + + return this.point(x, y); +}; + +EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) { + y = new BN(y, 16); + if (!y.red) + y = y.toRed(this.red); + + // x^2 = (y^2 - 1) / (d y^2 + 1) + var y2 = y.redSqr(); + var lhs = y2.redSub(this.one); + var rhs = y2.redMul(this.d).redAdd(this.one); + var x2 = lhs.redMul(rhs.redInvm()); + + if (x2.cmp(this.zero) === 0) { + if (odd) + throw new Error('invalid point'); + else + return this.point(this.zero, y); + } + + var x = x2.redSqrt(); + if (x.redSqr().redSub(x2).cmp(this.zero) !== 0) + throw new Error('invalid point'); + + if (x.isOdd() !== odd) + x = x.redNeg(); + + return this.point(x, y); +}; + +EdwardsCurve.prototype.validate = function validate(point) { + if (point.isInfinity()) + return true; + + // Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2) + point.normalize(); + + var x2 = point.x.redSqr(); + var y2 = point.y.redSqr(); + var lhs = x2.redMul(this.a).redAdd(y2); + var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); + + return lhs.cmp(rhs) === 0; +}; + +function Point(curve, x, y, z, t) { + Base.BasePoint.call(this, curve, 'projective'); + if (x === null && y === null && z === null) { + this.x = this.curve.zero; + this.y = this.curve.one; + this.z = this.curve.one; + this.t = this.curve.zero; + this.zOne = true; + } else { + this.x = new BN(x, 16); + this.y = new BN(y, 16); + this.z = z ? new BN(z, 16) : this.curve.one; + this.t = t && new BN(t, 16); + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.y.red) + this.y = this.y.toRed(this.curve.red); + if (!this.z.red) + this.z = this.z.toRed(this.curve.red); + if (this.t && !this.t.red) + this.t = this.t.toRed(this.curve.red); + this.zOne = this.z === this.curve.one; + + // Use extended coordinates + if (this.curve.extended && !this.t) { + this.t = this.x.redMul(this.y); + if (!this.zOne) + this.t = this.t.redMul(this.z.redInvm()); + } + } +} +inherits(Point, Base.BasePoint); + +EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) { + return Point.fromJSON(this, obj); +}; + +EdwardsCurve.prototype.point = function point(x, y, z, t) { + return new Point(this, x, y, z, t); +}; + +Point.fromJSON = function fromJSON(curve, obj) { + return new Point(curve, obj[0], obj[1], obj[2]); +}; + +Point.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +Point.prototype.isInfinity = function isInfinity() { + // XXX This code assumes that zero is always zero in red + return this.x.cmpn(0) === 0 && + this.y.cmp(this.z) === 0; +}; + +Point.prototype._extDbl = function _extDbl() { + // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + // #doubling-dbl-2008-hwcd + // 4M + 4S + + // A = X1^2 + var a = this.x.redSqr(); + // B = Y1^2 + var b = this.y.redSqr(); + // C = 2 * Z1^2 + var c = this.z.redSqr(); + c = c.redIAdd(c); + // D = a * A + var d = this.curve._mulA(a); + // E = (X1 + Y1)^2 - A - B + var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b); + // G = D + B + var g = d.redAdd(b); + // F = G - C + var f = g.redSub(c); + // H = D - B + var h = d.redSub(b); + // X3 = E * F + var nx = e.redMul(f); + // Y3 = G * H + var ny = g.redMul(h); + // T3 = E * H + var nt = e.redMul(h); + // Z3 = F * G + var nz = f.redMul(g); + return this.curve.point(nx, ny, nz, nt); +}; + +Point.prototype._projDbl = function _projDbl() { + // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html + // #doubling-dbl-2008-bbjlp + // #doubling-dbl-2007-bl + // and others + // Generally 3M + 4S or 2M + 4S + + // B = (X1 + Y1)^2 + var b = this.x.redAdd(this.y).redSqr(); + // C = X1^2 + var c = this.x.redSqr(); + // D = Y1^2 + var d = this.y.redSqr(); + + var nx; + var ny; + var nz; + if (this.curve.twisted) { + // E = a * C + var e = this.curve._mulA(c); + // F = E + D + var f = e.redAdd(d); + if (this.zOne) { + // X3 = (B - C - D) * (F - 2) + nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two)); + // Y3 = F * (E - D) + ny = f.redMul(e.redSub(d)); + // Z3 = F^2 - 2 * F + nz = f.redSqr().redSub(f).redSub(f); + } else { + // H = Z1^2 + var h = this.z.redSqr(); + // J = F - 2 * H + var j = f.redSub(h).redISub(h); + // X3 = (B-C-D)*J + nx = b.redSub(c).redISub(d).redMul(j); + // Y3 = F * (E - D) + ny = f.redMul(e.redSub(d)); + // Z3 = F * J + nz = f.redMul(j); + } + } else { + // E = C + D + var e = c.redAdd(d); + // H = (c * Z1)^2 + var h = this.curve._mulC(this.c.redMul(this.z)).redSqr(); + // J = E - 2 * H + var j = e.redSub(h).redSub(h); + // X3 = c * (B - E) * J + nx = this.curve._mulC(b.redISub(e)).redMul(j); + // Y3 = c * E * (C - D) + ny = this.curve._mulC(e).redMul(c.redISub(d)); + // Z3 = E * J + nz = e.redMul(j); + } + return this.curve.point(nx, ny, nz); +}; + +Point.prototype.dbl = function dbl() { + if (this.isInfinity()) + return this; + + // Double in extended coordinates + if (this.curve.extended) + return this._extDbl(); + else + return this._projDbl(); +}; + +Point.prototype._extAdd = function _extAdd(p) { + // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + // #addition-add-2008-hwcd-3 + // 8M + + // A = (Y1 - X1) * (Y2 - X2) + var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x)); + // B = (Y1 + X1) * (Y2 + X2) + var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x)); + // C = T1 * k * T2 + var c = this.t.redMul(this.curve.dd).redMul(p.t); + // D = Z1 * 2 * Z2 + var d = this.z.redMul(p.z.redAdd(p.z)); + // E = B - A + var e = b.redSub(a); + // F = D - C + var f = d.redSub(c); + // G = D + C + var g = d.redAdd(c); + // H = B + A + var h = b.redAdd(a); + // X3 = E * F + var nx = e.redMul(f); + // Y3 = G * H + var ny = g.redMul(h); + // T3 = E * H + var nt = e.redMul(h); + // Z3 = F * G + var nz = f.redMul(g); + return this.curve.point(nx, ny, nz, nt); +}; + +Point.prototype._projAdd = function _projAdd(p) { + // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html + // #addition-add-2008-bbjlp + // #addition-add-2007-bl + // 10M + 1S + + // A = Z1 * Z2 + var a = this.z.redMul(p.z); + // B = A^2 + var b = a.redSqr(); + // C = X1 * X2 + var c = this.x.redMul(p.x); + // D = Y1 * Y2 + var d = this.y.redMul(p.y); + // E = d * C * D + var e = this.curve.d.redMul(c).redMul(d); + // F = B - E + var f = b.redSub(e); + // G = B + E + var g = b.redAdd(e); + // X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) + var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d); + var nx = a.redMul(f).redMul(tmp); + var ny; + var nz; + if (this.curve.twisted) { + // Y3 = A * G * (D - a * C) + ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); + // Z3 = F * G + nz = f.redMul(g); + } else { + // Y3 = A * G * (D - C) + ny = a.redMul(g).redMul(d.redSub(c)); + // Z3 = c * F * G + nz = this.curve._mulC(f).redMul(g); + } + return this.curve.point(nx, ny, nz); +}; + +Point.prototype.add = function add(p) { + if (this.isInfinity()) + return p; + if (p.isInfinity()) + return this; + + if (this.curve.extended) + return this._extAdd(p); + else + return this._projAdd(p); +}; + +Point.prototype.mul = function mul(k) { + if (this._hasDoubles(k)) + return this.curve._fixedNafMul(this, k); + else + return this.curve._wnafMul(this, k); +}; + +Point.prototype.mulAdd = function mulAdd(k1, p, k2) { + return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false); +}; + +Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) { + return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true); +}; + +Point.prototype.normalize = function normalize() { + if (this.zOne) + return this; + + // Normalize coordinates + var zi = this.z.redInvm(); + this.x = this.x.redMul(zi); + this.y = this.y.redMul(zi); + if (this.t) + this.t = this.t.redMul(zi); + this.z = this.curve.one; + this.zOne = true; + return this; +}; + +Point.prototype.neg = function neg() { + return this.curve.point(this.x.redNeg(), + this.y, + this.z, + this.t && this.t.redNeg()); +}; + +Point.prototype.getX = function getX() { + this.normalize(); + return this.x.fromRed(); +}; + +Point.prototype.getY = function getY() { + this.normalize(); + return this.y.fromRed(); +}; + +Point.prototype.eq = function eq(other) { + return this === other || + this.getX().cmp(other.getX()) === 0 && + this.getY().cmp(other.getY()) === 0; +}; + +Point.prototype.eqXToP = function eqXToP(x) { + var rx = x.toRed(this.curve.red).redMul(this.z); + if (this.x.cmp(rx) === 0) + return true; + + var xc = x.clone(); + var t = this.curve.redN.redMul(this.z); + for (;;) { + xc.iadd(this.curve.n); + if (xc.cmp(this.curve.p) >= 0) + return false; + + rx.redIAdd(t); + if (this.x.cmp(rx) === 0) + return true; + } + return false; +}; + +// Compatibility with BaseCurve +Point.prototype.toP = Point.prototype.normalize; +Point.prototype.mixedAdd = Point.prototype.add; + +},{"../../../../BN/bn":122,"../../elliptic":124,"../curve":127}],127:[function(require,module,exports){ +'use strict'; + +var curve = exports; + +curve.base = require('./base'); +curve.short = require('./short'); +curve.mont = require('./mont'); +curve.edwards = require('./edwards'); + +},{"./base":125,"./edwards":126,"./mont":128,"./short":129}],128:[function(require,module,exports){ +'use strict'; + +var curve = require('../curve'); +var BN = require('../../../../BN/bn'); +var inherits = require('../../elliptic').inherits; +var Base = curve.base; + +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; + +function MontCurve(conf) { + Base.call(this, 'mont', conf); + + this.a = new BN(conf.a, 16).toRed(this.red); + this.b = new BN(conf.b, 16).toRed(this.red); + this.i4 = new BN(4).toRed(this.red).redInvm(); + this.two = new BN(2).toRed(this.red); + this.a24 = this.i4.redMul(this.a.redAdd(this.two)); +} +inherits(MontCurve, Base); +module.exports = MontCurve; + +MontCurve.prototype.validate = function validate(point) { + var x = point.normalize().x; + var x2 = x.redSqr(); + var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x); + var y = rhs.redSqrt(); + + return y.redSqr().cmp(rhs) === 0; +}; + +function Point(curve, x, z) { + Base.BasePoint.call(this, curve, 'projective'); + if (x === null && z === null) { + this.x = this.curve.one; + this.z = this.curve.zero; + } else { + this.x = new BN(x, 16); + this.z = new BN(z, 16); + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.z.red) + this.z = this.z.toRed(this.curve.red); + } +} +inherits(Point, Base.BasePoint); + +MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) { + return this.point(utils.toArray(bytes, enc), 1); +}; + +MontCurve.prototype.point = function point(x, z) { + return new Point(this, x, z); +}; + +MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) { + return Point.fromJSON(this, obj); +}; + +Point.prototype.precompute = function precompute() { + // No-op +}; + +Point.prototype._encode = function _encode() { + return this.getX().toArray('be', this.curve.p.byteLength()); +}; + +Point.fromJSON = function fromJSON(curve, obj) { + return new Point(curve, obj[0], obj[1] || curve.one); +}; + +Point.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +Point.prototype.isInfinity = function isInfinity() { + // XXX This code assumes that zero is always zero in red + return this.z.cmpn(0) === 0; +}; + +Point.prototype.dbl = function dbl() { + // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3 + // 2M + 2S + 4A + + // A = X1 + Z1 + var a = this.x.redAdd(this.z); + // AA = A^2 + var aa = a.redSqr(); + // B = X1 - Z1 + var b = this.x.redSub(this.z); + // BB = B^2 + var bb = b.redSqr(); + // C = AA - BB + var c = aa.redSub(bb); + // X3 = AA * BB + var nx = aa.redMul(bb); + // Z3 = C * (BB + A24 * C) + var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c))); + return this.curve.point(nx, nz); +}; + +Point.prototype.add = function add() { + throw new Error('Not supported on Montgomery curve'); +}; + +Point.prototype.diffAdd = function diffAdd(p, diff) { + // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3 + // 4M + 2S + 6A + + // A = X2 + Z2 + var a = this.x.redAdd(this.z); + // B = X2 - Z2 + var b = this.x.redSub(this.z); + // C = X3 + Z3 + var c = p.x.redAdd(p.z); + // D = X3 - Z3 + var d = p.x.redSub(p.z); + // DA = D * A + var da = d.redMul(a); + // CB = C * B + var cb = c.redMul(b); + // X5 = Z1 * (DA + CB)^2 + var nx = diff.z.redMul(da.redAdd(cb).redSqr()); + // Z5 = X1 * (DA - CB)^2 + var nz = diff.x.redMul(da.redISub(cb).redSqr()); + return this.curve.point(nx, nz); +}; + +Point.prototype.mul = function mul(k) { + var t = k.clone(); + var a = this; // (N / 2) * Q + Q + var b = this.curve.point(null, null); // (N / 2) * Q + var c = this; // Q + + for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1)) + bits.push(t.andln(1)); + + for (var i = bits.length - 1; i >= 0; i--) { + if (bits[i] === 0) { + // N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q + a = a.diffAdd(b, c); + // N * Q = 2 * ((N / 2) * Q + Q)) + b = b.dbl(); + } else { + // N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q) + b = a.diffAdd(b, c); + // N * Q + Q = 2 * ((N / 2) * Q + Q) + a = a.dbl(); + } + } + return b; +}; + +Point.prototype.mulAdd = function mulAdd() { + throw new Error('Not supported on Montgomery curve'); +}; + +Point.prototype.jumlAdd = function jumlAdd() { + throw new Error('Not supported on Montgomery curve'); +}; + +Point.prototype.eq = function eq(other) { + return this.getX().cmp(other.getX()) === 0; +}; + +Point.prototype.normalize = function normalize() { + this.x = this.x.redMul(this.z.redInvm()); + this.z = this.curve.one; + return this; +}; + +Point.prototype.getX = function getX() { + // Normalize coordinates + this.normalize(); + + return this.x.fromRed(); +}; + +},{"../../../../BN/bn":122,"../../elliptic":124,"../curve":127}],129:[function(require,module,exports){ +'use strict'; + +var curve = require('../curve'); +var elliptic = require('../../elliptic'); +var BN = require('../../../../BN/bn'); +var inherits = elliptic.inherits; +var Base = curve.base; + +var assert = elliptic.utils.assert; + +function ShortCurve(conf) { + Base.call(this, 'short', conf); + + this.a = new BN(conf.a, 16).toRed(this.red); + this.b = new BN(conf.b, 16).toRed(this.red); + this.tinv = this.two.redInvm(); + + this.zeroA = this.a.fromRed().cmpn(0) === 0; + this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0; + + // If the curve is endomorphic, precalculate beta and lambda + this.endo = this._getEndomorphism(conf); + this._endoWnafT1 = new Array(4); + this._endoWnafT2 = new Array(4); +} +inherits(ShortCurve, Base); +module.exports = ShortCurve; + +ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) { + // No efficient endomorphism + if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1) + return; + + // Compute beta and lambda, that lambda * P = (beta * Px; Py) + var beta; + var lambda; + if (conf.beta) { + beta = new BN(conf.beta, 16).toRed(this.red); + } else { + var betas = this._getEndoRoots(this.p); + // Choose the smallest beta + beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1]; + beta = beta.toRed(this.red); + } + if (conf.lambda) { + lambda = new BN(conf.lambda, 16); + } else { + // Choose the lambda that is matching selected beta + var lambdas = this._getEndoRoots(this.n); + if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) { + lambda = lambdas[0]; + } else { + lambda = lambdas[1]; + assert(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0); + } + } + + // Get basis vectors, used for balanced length-two representation + var basis; + if (conf.basis) { + basis = conf.basis.map(function(vec) { + return { + a: new BN(vec.a, 16), + b: new BN(vec.b, 16) + }; + }); + } else { + basis = this._getEndoBasis(lambda); + } + + return { + beta: beta, + lambda: lambda, + basis: basis + }; +}; + +ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) { + // Find roots of for x^2 + x + 1 in F + // Root = (-1 +- Sqrt(-3)) / 2 + // + var red = num === this.p ? this.red : BN.mont(num); + var tinv = new BN(2).toRed(red).redInvm(); + var ntinv = tinv.redNeg(); + + var s = new BN(3).toRed(red).redNeg().redSqrt().redMul(tinv); + + var l1 = ntinv.redAdd(s).fromRed(); + var l2 = ntinv.redSub(s).fromRed(); + return [ l1, l2 ]; +}; + +ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) { + // aprxSqrt >= sqrt(this.n) + var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2)); + + // 3.74 + // Run EGCD, until r(L + 1) < aprxSqrt + var u = lambda; + var v = this.n.clone(); + var x1 = new BN(1); + var y1 = new BN(0); + var x2 = new BN(0); + var y2 = new BN(1); + + // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n) + var a0; + var b0; + // First vector + var a1; + var b1; + // Second vector + var a2; + var b2; + + var prevR; + var i = 0; + var r; + var x; + while (u.cmpn(0) !== 0) { + var q = v.div(u); + r = v.sub(q.mul(u)); + x = x2.sub(q.mul(x1)); + var y = y2.sub(q.mul(y1)); + + if (!a1 && r.cmp(aprxSqrt) < 0) { + a0 = prevR.neg(); + b0 = x1; + a1 = r.neg(); + b1 = x; + } else if (a1 && ++i === 2) { + break; + } + prevR = r; + + v = u; + u = r; + x2 = x1; + x1 = x; + y2 = y1; + y1 = y; + } + a2 = r.neg(); + b2 = x; + + var len1 = a1.sqr().add(b1.sqr()); + var len2 = a2.sqr().add(b2.sqr()); + if (len2.cmp(len1) >= 0) { + a2 = a0; + b2 = b0; + } + + // Normalize signs + if (a1.negative) { + a1 = a1.neg(); + b1 = b1.neg(); + } + if (a2.negative) { + a2 = a2.neg(); + b2 = b2.neg(); + } + + return [ + { a: a1, b: b1 }, + { a: a2, b: b2 } + ]; +}; + +ShortCurve.prototype._endoSplit = function _endoSplit(k) { + var basis = this.endo.basis; + var v1 = basis[0]; + var v2 = basis[1]; + + var c1 = v2.b.mul(k).divRound(this.n); + var c2 = v1.b.neg().mul(k).divRound(this.n); + + var p1 = c1.mul(v1.a); + var p2 = c2.mul(v2.a); + var q1 = c1.mul(v1.b); + var q2 = c2.mul(v2.b); + + // Calculate answer + var k1 = k.sub(p1).sub(p2); + var k2 = q1.add(q2).neg(); + return { k1: k1, k2: k2 }; +}; + +ShortCurve.prototype.pointFromX = function pointFromX(x, odd) { + x = new BN(x, 16); + if (!x.red) + x = x.toRed(this.red); + + var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b); + var y = y2.redSqrt(); + if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) + throw new Error('invalid point'); + + // XXX Is there any way to tell if the number is odd without converting it + // to non-red form? + var isOdd = y.fromRed().isOdd(); + if (odd && !isOdd || !odd && isOdd) + y = y.redNeg(); + + return this.point(x, y); +}; + +ShortCurve.prototype.validate = function validate(point) { + if (point.inf) + return true; + + var x = point.x; + var y = point.y; + + var ax = this.a.redMul(x); + var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b); + return y.redSqr().redISub(rhs).cmpn(0) === 0; +}; + +ShortCurve.prototype._endoWnafMulAdd = + function _endoWnafMulAdd(points, coeffs, jacobianResult) { + var npoints = this._endoWnafT1; + var ncoeffs = this._endoWnafT2; + for (var i = 0; i < points.length; i++) { + var split = this._endoSplit(coeffs[i]); + var p = points[i]; + var beta = p._getBeta(); + + if (split.k1.negative) { + split.k1.ineg(); + p = p.neg(true); + } + if (split.k2.negative) { + split.k2.ineg(); + beta = beta.neg(true); + } + + npoints[i * 2] = p; + npoints[i * 2 + 1] = beta; + ncoeffs[i * 2] = split.k1; + ncoeffs[i * 2 + 1] = split.k2; + } + var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult); + + // Clean-up references to points and coefficients + for (var j = 0; j < i * 2; j++) { + npoints[j] = null; + ncoeffs[j] = null; + } + return res; +}; + +function Point(curve, x, y, isRed) { + Base.BasePoint.call(this, curve, 'affine'); + if (x === null && y === null) { + this.x = null; + this.y = null; + this.inf = true; + } else { + this.x = new BN(x, 16); + this.y = new BN(y, 16); + // Force redgomery representation when loading from JSON + if (isRed) { + this.x.forceRed(this.curve.red); + this.y.forceRed(this.curve.red); + } + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.y.red) + this.y = this.y.toRed(this.curve.red); + this.inf = false; + } +} +inherits(Point, Base.BasePoint); + +ShortCurve.prototype.point = function point(x, y, isRed) { + return new Point(this, x, y, isRed); +}; + +ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) { + return Point.fromJSON(this, obj, red); +}; + +Point.prototype._getBeta = function _getBeta() { + if (!this.curve.endo) + return; + + var pre = this.precomputed; + if (pre && pre.beta) + return pre.beta; + + var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y); + if (pre) { + var curve = this.curve; + var endoMul = function(p) { + return curve.point(p.x.redMul(curve.endo.beta), p.y); + }; + pre.beta = beta; + beta.precomputed = { + beta: null, + naf: pre.naf && { + wnd: pre.naf.wnd, + points: pre.naf.points.map(endoMul) + }, + doubles: pre.doubles && { + step: pre.doubles.step, + points: pre.doubles.points.map(endoMul) + } + }; + } + return beta; +}; + +Point.prototype.toJSON = function toJSON() { + if (!this.precomputed) + return [ this.x, this.y ]; + + return [ this.x, this.y, this.precomputed && { + doubles: this.precomputed.doubles && { + step: this.precomputed.doubles.step, + points: this.precomputed.doubles.points.slice(1) + }, + naf: this.precomputed.naf && { + wnd: this.precomputed.naf.wnd, + points: this.precomputed.naf.points.slice(1) + } + } ]; +}; + +Point.fromJSON = function fromJSON(curve, obj, red) { + if (typeof obj === 'string') + obj = JSON.parse(obj); + var res = curve.point(obj[0], obj[1], red); + if (!obj[2]) + return res; + + function obj2point(obj) { + return curve.point(obj[0], obj[1], red); + } + + var pre = obj[2]; + res.precomputed = { + beta: null, + doubles: pre.doubles && { + step: pre.doubles.step, + points: [ res ].concat(pre.doubles.points.map(obj2point)) + }, + naf: pre.naf && { + wnd: pre.naf.wnd, + points: [ res ].concat(pre.naf.points.map(obj2point)) + } + }; + return res; +}; + +Point.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +Point.prototype.isInfinity = function isInfinity() { + return this.inf; +}; + +Point.prototype.add = function add(p) { + // O + P = P + if (this.inf) + return p; + + // P + O = P + if (p.inf) + return this; + + // P + P = 2P + if (this.eq(p)) + return this.dbl(); + + // P + (-P) = O + if (this.neg().eq(p)) + return this.curve.point(null, null); + + // P + Q = O + if (this.x.cmp(p.x) === 0) + return this.curve.point(null, null); + + var c = this.y.redSub(p.y); + if (c.cmpn(0) !== 0) + c = c.redMul(this.x.redSub(p.x).redInvm()); + var nx = c.redSqr().redISub(this.x).redISub(p.x); + var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); + return this.curve.point(nx, ny); +}; + +Point.prototype.dbl = function dbl() { + if (this.inf) + return this; + + // 2P = O + var ys1 = this.y.redAdd(this.y); + if (ys1.cmpn(0) === 0) + return this.curve.point(null, null); + + var a = this.curve.a; + + var x2 = this.x.redSqr(); + var dyinv = ys1.redInvm(); + var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv); + + var nx = c.redSqr().redISub(this.x.redAdd(this.x)); + var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); + return this.curve.point(nx, ny); +}; + +Point.prototype.getX = function getX() { + return this.x.fromRed(); +}; + +Point.prototype.getY = function getY() { + return this.y.fromRed(); +}; + +Point.prototype.mul = function mul(k) { + k = new BN(k, 16); + + if (this._hasDoubles(k)) + return this.curve._fixedNafMul(this, k); + else if (this.curve.endo) + return this.curve._endoWnafMulAdd([ this ], [ k ]); + else + return this.curve._wnafMul(this, k); +}; + +Point.prototype.mulAdd = function mulAdd(k1, p2, k2) { + var points = [ this, p2 ]; + var coeffs = [ k1, k2 ]; + if (this.curve.endo) + return this.curve._endoWnafMulAdd(points, coeffs); + else + return this.curve._wnafMulAdd(1, points, coeffs, 2); +}; + +Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) { + var points = [ this, p2 ]; + var coeffs = [ k1, k2 ]; + if (this.curve.endo) + return this.curve._endoWnafMulAdd(points, coeffs, true); + else + return this.curve._wnafMulAdd(1, points, coeffs, 2, true); +}; + +Point.prototype.eq = function eq(p) { + return this === p || + this.inf === p.inf && + (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0); +}; + +Point.prototype.neg = function neg(_precompute) { + if (this.inf) + return this; + + var res = this.curve.point(this.x, this.y.redNeg()); + if (_precompute && this.precomputed) { + var pre = this.precomputed; + var negate = function(p) { + return p.neg(); + }; + res.precomputed = { + naf: pre.naf && { + wnd: pre.naf.wnd, + points: pre.naf.points.map(negate) + }, + doubles: pre.doubles && { + step: pre.doubles.step, + points: pre.doubles.points.map(negate) + } + }; + } + return res; +}; + +Point.prototype.toJ = function toJ() { + if (this.inf) + return this.curve.jpoint(null, null, null); + + var res = this.curve.jpoint(this.x, this.y, this.curve.one); + return res; +}; + +function JPoint(curve, x, y, z) { + Base.BasePoint.call(this, curve, 'jacobian'); + if (x === null && y === null && z === null) { + this.x = this.curve.one; + this.y = this.curve.one; + this.z = new BN(0); + } else { + this.x = new BN(x, 16); + this.y = new BN(y, 16); + this.z = new BN(z, 16); + } + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.y.red) + this.y = this.y.toRed(this.curve.red); + if (!this.z.red) + this.z = this.z.toRed(this.curve.red); + + this.zOne = this.z === this.curve.one; +} +inherits(JPoint, Base.BasePoint); + +ShortCurve.prototype.jpoint = function jpoint(x, y, z) { + return new JPoint(this, x, y, z); +}; + +JPoint.prototype.toP = function toP() { + if (this.isInfinity()) + return this.curve.point(null, null); + + var zinv = this.z.redInvm(); + var zinv2 = zinv.redSqr(); + var ax = this.x.redMul(zinv2); + var ay = this.y.redMul(zinv2).redMul(zinv); + + return this.curve.point(ax, ay); +}; + +JPoint.prototype.neg = function neg() { + return this.curve.jpoint(this.x, this.y.redNeg(), this.z); +}; + +JPoint.prototype.add = function add(p) { + // O + P = P + if (this.isInfinity()) + return p; + + // P + O = P + if (p.isInfinity()) + return this; + + // 12M + 4S + 7A + var pz2 = p.z.redSqr(); + var z2 = this.z.redSqr(); + var u1 = this.x.redMul(pz2); + var u2 = p.x.redMul(z2); + var s1 = this.y.redMul(pz2.redMul(p.z)); + var s2 = p.y.redMul(z2.redMul(this.z)); + + var h = u1.redSub(u2); + var r = s1.redSub(s2); + if (h.cmpn(0) === 0) { + if (r.cmpn(0) !== 0) + return this.curve.jpoint(null, null, null); + else + return this.dbl(); + } + + var h2 = h.redSqr(); + var h3 = h2.redMul(h); + var v = u1.redMul(h2); + + var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); + var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); + var nz = this.z.redMul(p.z).redMul(h); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.mixedAdd = function mixedAdd(p) { + // O + P = P + if (this.isInfinity()) + return p.toJ(); + + // P + O = P + if (p.isInfinity()) + return this; + + // 8M + 3S + 7A + var z2 = this.z.redSqr(); + var u1 = this.x; + var u2 = p.x.redMul(z2); + var s1 = this.y; + var s2 = p.y.redMul(z2).redMul(this.z); + + var h = u1.redSub(u2); + var r = s1.redSub(s2); + if (h.cmpn(0) === 0) { + if (r.cmpn(0) !== 0) + return this.curve.jpoint(null, null, null); + else + return this.dbl(); + } + + var h2 = h.redSqr(); + var h3 = h2.redMul(h); + var v = u1.redMul(h2); + + var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); + var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); + var nz = this.z.redMul(h); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.dblp = function dblp(pow) { + if (pow === 0) + return this; + if (this.isInfinity()) + return this; + if (!pow) + return this.dbl(); + + if (this.curve.zeroA || this.curve.threeA) { + var r = this; + for (var i = 0; i < pow; i++) + r = r.dbl(); + return r; + } + + // 1M + 2S + 1A + N * (4S + 5M + 8A) + // N = 1 => 6M + 6S + 9A + var a = this.curve.a; + var tinv = this.curve.tinv; + + var jx = this.x; + var jy = this.y; + var jz = this.z; + var jz4 = jz.redSqr().redSqr(); + + // Reuse results + var jyd = jy.redAdd(jy); + for (var i = 0; i < pow; i++) { + var jx2 = jx.redSqr(); + var jyd2 = jyd.redSqr(); + var jyd4 = jyd2.redSqr(); + var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); + + var t1 = jx.redMul(jyd2); + var nx = c.redSqr().redISub(t1.redAdd(t1)); + var t2 = t1.redISub(nx); + var dny = c.redMul(t2); + dny = dny.redIAdd(dny).redISub(jyd4); + var nz = jyd.redMul(jz); + if (i + 1 < pow) + jz4 = jz4.redMul(jyd4); + + jx = nx; + jz = nz; + jyd = dny; + } + + return this.curve.jpoint(jx, jyd.redMul(tinv), jz); +}; + +JPoint.prototype.dbl = function dbl() { + if (this.isInfinity()) + return this; + + if (this.curve.zeroA) + return this._zeroDbl(); + else if (this.curve.threeA) + return this._threeDbl(); + else + return this._dbl(); +}; + +JPoint.prototype._zeroDbl = function _zeroDbl() { + var nx; + var ny; + var nz; + // Z = 1 + if (this.zOne) { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html + // #doubling-mdbl-2007-bl + // 1M + 5S + 14A + + // XX = X1^2 + var xx = this.x.redSqr(); + // YY = Y1^2 + var yy = this.y.redSqr(); + // YYYY = YY^2 + var yyyy = yy.redSqr(); + // S = 2 * ((X1 + YY)^2 - XX - YYYY) + var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); + s = s.redIAdd(s); + // M = 3 * XX + a; a = 0 + var m = xx.redAdd(xx).redIAdd(xx); + // T = M ^ 2 - 2*S + var t = m.redSqr().redISub(s).redISub(s); + + // 8 * YYYY + var yyyy8 = yyyy.redIAdd(yyyy); + yyyy8 = yyyy8.redIAdd(yyyy8); + yyyy8 = yyyy8.redIAdd(yyyy8); + + // X3 = T + nx = t; + // Y3 = M * (S - T) - 8 * YYYY + ny = m.redMul(s.redISub(t)).redISub(yyyy8); + // Z3 = 2*Y1 + nz = this.y.redAdd(this.y); + } else { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html + // #doubling-dbl-2009-l + // 2M + 5S + 13A + + // A = X1^2 + var a = this.x.redSqr(); + // B = Y1^2 + var b = this.y.redSqr(); + // C = B^2 + var c = b.redSqr(); + // D = 2 * ((X1 + B)^2 - A - C) + var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c); + d = d.redIAdd(d); + // E = 3 * A + var e = a.redAdd(a).redIAdd(a); + // F = E^2 + var f = e.redSqr(); + + // 8 * C + var c8 = c.redIAdd(c); + c8 = c8.redIAdd(c8); + c8 = c8.redIAdd(c8); + + // X3 = F - 2 * D + nx = f.redISub(d).redISub(d); + // Y3 = E * (D - X3) - 8 * C + ny = e.redMul(d.redISub(nx)).redISub(c8); + // Z3 = 2 * Y1 * Z1 + nz = this.y.redMul(this.z); + nz = nz.redIAdd(nz); + } + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype._threeDbl = function _threeDbl() { + var nx; + var ny; + var nz; + // Z = 1 + if (this.zOne) { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html + // #doubling-mdbl-2007-bl + // 1M + 5S + 15A + + // XX = X1^2 + var xx = this.x.redSqr(); + // YY = Y1^2 + var yy = this.y.redSqr(); + // YYYY = YY^2 + var yyyy = yy.redSqr(); + // S = 2 * ((X1 + YY)^2 - XX - YYYY) + var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); + s = s.redIAdd(s); + // M = 3 * XX + a + var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a); + // T = M^2 - 2 * S + var t = m.redSqr().redISub(s).redISub(s); + // X3 = T + nx = t; + // Y3 = M * (S - T) - 8 * YYYY + var yyyy8 = yyyy.redIAdd(yyyy); + yyyy8 = yyyy8.redIAdd(yyyy8); + yyyy8 = yyyy8.redIAdd(yyyy8); + ny = m.redMul(s.redISub(t)).redISub(yyyy8); + // Z3 = 2 * Y1 + nz = this.y.redAdd(this.y); + } else { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b + // 3M + 5S + + // delta = Z1^2 + var delta = this.z.redSqr(); + // gamma = Y1^2 + var gamma = this.y.redSqr(); + // beta = X1 * gamma + var beta = this.x.redMul(gamma); + // alpha = 3 * (X1 - delta) * (X1 + delta) + var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta)); + alpha = alpha.redAdd(alpha).redIAdd(alpha); + // X3 = alpha^2 - 8 * beta + var beta4 = beta.redIAdd(beta); + beta4 = beta4.redIAdd(beta4); + var beta8 = beta4.redAdd(beta4); + nx = alpha.redSqr().redISub(beta8); + // Z3 = (Y1 + Z1)^2 - gamma - delta + nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta); + // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2 + var ggamma8 = gamma.redSqr(); + ggamma8 = ggamma8.redIAdd(ggamma8); + ggamma8 = ggamma8.redIAdd(ggamma8); + ggamma8 = ggamma8.redIAdd(ggamma8); + ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8); + } + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype._dbl = function _dbl() { + var a = this.curve.a; + + // 4M + 6S + 10A + var jx = this.x; + var jy = this.y; + var jz = this.z; + var jz4 = jz.redSqr().redSqr(); + + var jx2 = jx.redSqr(); + var jy2 = jy.redSqr(); + + var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); + + var jxd4 = jx.redAdd(jx); + jxd4 = jxd4.redIAdd(jxd4); + var t1 = jxd4.redMul(jy2); + var nx = c.redSqr().redISub(t1.redAdd(t1)); + var t2 = t1.redISub(nx); + + var jyd8 = jy2.redSqr(); + jyd8 = jyd8.redIAdd(jyd8); + jyd8 = jyd8.redIAdd(jyd8); + jyd8 = jyd8.redIAdd(jyd8); + var ny = c.redMul(t2).redISub(jyd8); + var nz = jy.redAdd(jy).redMul(jz); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.trpl = function trpl() { + if (!this.curve.zeroA) + return this.dbl().add(this); + + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl + // 5M + 10S + ... + + // XX = X1^2 + var xx = this.x.redSqr(); + // YY = Y1^2 + var yy = this.y.redSqr(); + // ZZ = Z1^2 + var zz = this.z.redSqr(); + // YYYY = YY^2 + var yyyy = yy.redSqr(); + // M = 3 * XX + a * ZZ2; a = 0 + var m = xx.redAdd(xx).redIAdd(xx); + // MM = M^2 + var mm = m.redSqr(); + // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM + var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); + e = e.redIAdd(e); + e = e.redAdd(e).redIAdd(e); + e = e.redISub(mm); + // EE = E^2 + var ee = e.redSqr(); + // T = 16*YYYY + var t = yyyy.redIAdd(yyyy); + t = t.redIAdd(t); + t = t.redIAdd(t); + t = t.redIAdd(t); + // U = (M + E)^2 - MM - EE - T + var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t); + // X3 = 4 * (X1 * EE - 4 * YY * U) + var yyu4 = yy.redMul(u); + yyu4 = yyu4.redIAdd(yyu4); + yyu4 = yyu4.redIAdd(yyu4); + var nx = this.x.redMul(ee).redISub(yyu4); + nx = nx.redIAdd(nx); + nx = nx.redIAdd(nx); + // Y3 = 8 * Y1 * (U * (T - U) - E * EE) + var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee))); + ny = ny.redIAdd(ny); + ny = ny.redIAdd(ny); + ny = ny.redIAdd(ny); + // Z3 = (Z1 + E)^2 - ZZ - EE + var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.mul = function mul(k, kbase) { + k = new BN(k, kbase); + + return this.curve._wnafMul(this, k); +}; + +JPoint.prototype.eq = function eq(p) { + if (p.type === 'affine') + return this.eq(p.toJ()); + + if (this === p) + return true; + + // x1 * z2^2 == x2 * z1^2 + var z2 = this.z.redSqr(); + var pz2 = p.z.redSqr(); + if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0) + return false; + + // y1 * z2^3 == y2 * z1^3 + var z3 = z2.redMul(this.z); + var pz3 = pz2.redMul(p.z); + return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0; +}; + +JPoint.prototype.eqXToP = function eqXToP(x) { + var zs = this.z.redSqr(); + var rx = x.toRed(this.curve.red).redMul(zs); + if (this.x.cmp(rx) === 0) + return true; + + var xc = x.clone(); + var t = this.curve.redN.redMul(zs); + for (;;) { + xc.iadd(this.curve.n); + if (xc.cmp(this.curve.p) >= 0) + return false; + + rx.redIAdd(t); + if (this.x.cmp(rx) === 0) + return true; + } + return false; +}; + +JPoint.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +JPoint.prototype.isInfinity = function isInfinity() { + // XXX This code assumes that zero is always zero in red + return this.z.cmpn(0) === 0; +}; + +},{"../../../../BN/bn":122,"../../elliptic":124,"../curve":127}],130:[function(require,module,exports){ +'use strict'; + +var curves = exports; + +var elliptic = require('../elliptic'); + +var assert = elliptic.utils.assert; + +function PresetCurve(options) { + if (options.type === 'short') + this.curve = new elliptic.curve.short(options); + else if (options.type === 'edwards') + this.curve = new elliptic.curve.edwards(options); + else + this.curve = new elliptic.curve.mont(options); + this.g = this.curve.g; + this.n = this.curve.n; + this.hash = options.hash; + + assert(this.g.validate(), 'Invalid curve'); + assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N != O'); +} +curves.PresetCurve = PresetCurve; + +function defineCurve(name, options) { + Object.defineProperty(curves, name, { + configurable: true, + enumerable: true, + get: function() { + var curve = new PresetCurve(options); + Object.defineProperty(curves, name, { + configurable: true, + enumerable: true, + value: curve + }); + return curve; + } + }); +} + +defineCurve('p192', { + type: 'short', + prime: 'p192', + p: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff', + a: 'ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc', + b: '64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1', + n: 'ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831', + hash: 'SHA256', + gRed: false, + g: [ + '188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012', + '07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811' + ] +}); + +defineCurve('p224', { + type: 'short', + prime: 'p224', + p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001', + a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe', + b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4', + n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d', + hash: 'SHA256', + gRed: false, + g: [ + 'b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21', + 'bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34' + ] +}); + +defineCurve('p256', { + type: 'short', + prime: null, + p: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff', + a: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc', + b: '5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b', + n: 'ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551', + hash: 'SHA256', + gRed: false, + g: [ + '6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296', + '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5' + ] +}); + +defineCurve('p384', { + type: 'short', + prime: null, + p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'fffffffe ffffffff 00000000 00000000 ffffffff', + a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'fffffffe ffffffff 00000000 00000000 fffffffc', + b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' + + '5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef', + n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' + + 'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973', + hash: 'SHA384', + gRed: false, + g: [ + 'aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 ' + + '5502f25d bf55296c 3a545e38 72760ab7', + '3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 ' + + '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f' + ] +}); + +defineCurve('p521', { + type: 'short', + prime: null, + p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff ffffffff', + a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff fffffffc', + b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' + + '99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' + + '3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00', + n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' + + 'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409', + hash: 'SHA512', + gRed: false, + g: [ + '000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 ' + + '053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 ' + + 'a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66', + '00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 ' + + '579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 ' + + '3fad0761 353c7086 a272c240 88be9476 9fd16650' + ] +}); + +defineCurve('curve25519', { + type: 'mont', + prime: 'p25519', + p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', + a: '76d06', + b: '1', + n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', + hash: 'SHA256', + gRed: false, + g: [ + '9' + ] +}); + +defineCurve('ed25519', { + type: 'edwards', + prime: 'p25519', + p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', + a: '-1', + c: '1', + // -121665 * (121666^(-1)) (mod P) + d: '52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3', + n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', + hash: 'SHA256', + gRed: false, + g: [ + '216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a', + + // 4/5 + '6666666666666666666666666666666666666666666666666666666666666658' + ] +}); + +var pre; +try { + pre = require('./precomputed/secp256k1'); +} catch (e) { + pre = undefined; +} + +defineCurve('secp256k1', { + type: 'short', + prime: 'k256', + p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f', + a: '0', + b: '7', + n: 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141', + h: '1', + hash: 'SHA256', + + // Precomputed endomorphism + beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', + lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', + basis: [ + { + a: '3086d221a7d46bcde86c90e49284eb15', + b: '-e4437ed6010e88286f547fa90abfe4c3' + }, + { + a: '114ca50f7a8e2f3f657c1108d9d44cfd8', + b: '3086d221a7d46bcde86c90e49284eb15' + } + ], + + gRed: false, + g: [ + '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', + '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', + pre + ] +}); + +},{"../elliptic":124,"./precomputed/secp256k1":134}],131:[function(require,module,exports){ +'use strict'; + +var BN = require('../../../../BN/bn'); +var HmacDRBG = require('../../hmac-drbg/hmac-drbg'); +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var assert = utils.assert; + +var KeyPair = require('./key'); +var Signature = require('./signature'); + +function EC(options) { + if (!(this instanceof EC)) + return new EC(options); + + // Shortcut `elliptic.ec(curve-name)` + if (typeof options === 'string') { + assert(elliptic.curves.hasOwnProperty(options), 'Unknown curve ' + options); + + options = elliptic.curves[options]; + } + + // Shortcut for `elliptic.ec(elliptic.curves.curveName)` + if (options instanceof elliptic.curves.PresetCurve) + options = { curve: options }; + + this.curve = options.curve.curve; + this.n = this.curve.n; + this.nh = this.n.ushrn(1); + this.g = this.curve.g; + + // Point on curve + this.g = options.curve.g; + this.g.precompute(options.curve.n.bitLength() + 1); + + // Hash for function for DRBG + this.hash = options.hash || options.curve.hash; +} +module.exports = EC; + +EC.prototype.keyPair = function keyPair(options) { + return new KeyPair(this, options); +}; + +EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) { + return KeyPair.fromPrivate(this, priv, enc); +}; + +EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) { + return KeyPair.fromPublic(this, pub, enc); +}; + +EC.prototype.genKeyPair = function genKeyPair(options) { + if (!options) + options = {}; + + // Instantiate Hmac_DRBG + var drbg = new HmacDRBG({ + hash: this.hash, + pers: options.pers, + persEnc: options.persEnc || 'utf8', + entropy: options.entropy || null, //elliptic.rand(this.hash.hmacStrength), + entropyEnc: options.entropy && options.entropyEnc || 'utf8', + nonce: this.n.toArray() + }); + + var bytes = this.n.byteLength(); + var ns2 = this.n.sub(new BN(2)); + do { + var priv = new BN(drbg.generate(bytes)); + if (priv.cmp(ns2) > 0) + continue; + + priv.iaddn(1); + return this.keyFromPrivate(priv); + } while (true); +}; + +EC.prototype._truncateToN = function truncateToN(msg, truncOnly) { + var delta = msg.byteLength() * 8 - this.n.bitLength(); + if (delta > 0) + msg = msg.ushrn(delta); + if (!truncOnly && msg.cmp(this.n) >= 0) + return msg.sub(this.n); + else + return msg; +}; + +EC.prototype.sign = function sign(msg, key, enc, options) { + if (typeof enc === 'object') { + options = enc; + enc = null; + } + if (!options) + options = {}; + + key = this.keyFromPrivate(key, enc); + msg = this._truncateToN(new BN(msg, 16)); + + // Zero-extend key to provide enough entropy + var bytes = this.n.byteLength(); + var bkey = key.getPrivate().toArray('be', bytes); + + // Zero-extend nonce to have the same byte size as N + var nonce = msg.toArray('be', bytes); + + // Instantiate Hmac_DRBG + var drbg = new HmacDRBG({ + hash: this.hash, + entropy: options['extraEntropy'] ? bkey.concat (options['extraEntropy']) : bkey, + nonce: nonce, + pers: options.pers, + persEnc: options.persEnc || 'utf8' + }); + + // Number of bytes to generate + var ns1 = this.n.sub(new BN(1)); + + for (var iter = 0; true; iter++) { + var k = options.k ? + options.k(iter) : + new BN(drbg.generate(this.n.byteLength())); + k = this._truncateToN(k, true); + if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) + continue; + + var kp = this.g.mul(k); + if (kp.isInfinity()) + continue; + + var kpX = kp.getX(); + var r = kpX.umod(this.n); + if (r.cmpn(0) === 0) + continue; + + var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg)); + s = s.umod(this.n); + if (s.cmpn(0) === 0) + continue; + + var recoveryParam = (kp.getY().isOdd() ? 1 : 0) | + (kpX.cmp(r) !== 0 ? 2 : 0); + + // Use complement of `s`, if it is > `n / 2` + if (options.canonical && s.cmp(this.nh) > 0) { + s = this.n.sub(s); + recoveryParam ^= 1; + } + + return new Signature({ r: r, s: s, recoveryParam: recoveryParam }); + } +}; + +EC.prototype.verify = function verify(msg, signature, key, enc) { + msg = this._truncateToN(new BN(msg, 16)); + key = this.keyFromPublic(key, enc); + signature = new Signature(signature, 'hex'); + + // Perform primitive values validation + var r = signature.r; + var s = signature.s; + if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0) + return false; + if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0) + return false; + + // Validate signature + var sinv = s.invm(this.n); + var u1 = sinv.mul(msg).umod(this.n); + var u2 = sinv.mul(r).umod(this.n); + + if (!this.curve._maxwellTrick) { + var p = this.g.mulAdd(u1, key.getPublic(), u2); + if (p.isInfinity()) + return false; + + return p.getX().umod(this.n).cmp(r) === 0; + } + + // NOTE: Greg Maxwell's trick, inspired by: + // https://git.io/vad3K + + var p = this.g.jmulAdd(u1, key.getPublic(), u2); + if (p.isInfinity()) + return false; + + // Compare `p.x` of Jacobian point with `r`, + // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the + // inverse of `p.z^2` + return p.eqXToP(r); +}; + +EC.prototype.recoverPubKey = function(msg, signature, j, enc) { + assert((3 & j) === j, 'The recovery param is more than two bits'); + signature = new Signature(signature, enc); + + var n = this.n; + var e = new BN(msg); + var r = signature.r; + var s = signature.s; + + // A set LSB signifies that the y-coordinate is odd + var isYOdd = j & 1; + var isSecondKey = j >> 1; + if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey) + throw new Error('Unable to find sencond key candinate'); + + // 1.1. Let x = r + jn. + if (isSecondKey) + r = this.curve.pointFromX(r.add(this.curve.n), isYOdd); + else + r = this.curve.pointFromX(r, isYOdd); + + var rInv = signature.r.invm(n); + var s1 = n.sub(e).mul(rInv).umod(n); + var s2 = s.mul(rInv).umod(n); + + // 1.6.1 Compute Q = r^-1 (sR - eG) + // Q = r^-1 (sR + -eG) + return this.g.mulAdd(s1, r, s2); +}; + +EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { + signature = new Signature(signature, enc); + if (signature.recoveryParam !== null) + return signature.recoveryParam; + + for (var i = 0; i < 4; i++) { + var Qprime; + try { + Qprime = this.recoverPubKey(e, signature, i); + } catch (e) { + continue; + } + + if (Qprime.eq(Q)) + return i; + } + throw new Error('Unable to find valid recovery factor'); +}; + +},{"../../../../BN/bn":122,"../../elliptic":124,"../../hmac-drbg/hmac-drbg":136,"./key":132,"./signature":133}],132:[function(require,module,exports){ +'use strict'; + +var BN = require('../../../../BN/bn'); +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var assert = utils.assert; + +function KeyPair(ec, options) { + this.ec = ec; + this.priv = null; + this.pub = null; + + // KeyPair(ec, { priv: ..., pub: ... }) + if (options.priv) + this._importPrivate(options.priv, options.privEnc); + if (options.pub) + this._importPublic(options.pub, options.pubEnc); +} +module.exports = KeyPair; + +KeyPair.fromPublic = function fromPublic(ec, pub, enc) { + if (pub instanceof KeyPair) + return pub; + + return new KeyPair(ec, { + pub: pub, + pubEnc: enc + }); +}; + +KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) { + if (priv instanceof KeyPair) + return priv; + + return new KeyPair(ec, { + priv: priv, + privEnc: enc + }); +}; + +KeyPair.prototype.validate = function validate() { + var pub = this.getPublic(); + + if (pub.isInfinity()) + return { result: false, reason: 'Invalid public key' }; + if (!pub.validate()) + return { result: false, reason: 'Public key is not a point' }; + if (!pub.mul(this.ec.curve.n).isInfinity()) + return { result: false, reason: 'Public key * N != O' }; + + return { result: true, reason: null }; +}; + +KeyPair.prototype.getPublic = function getPublic(compact, enc) { + // compact is optional argument + if (typeof compact === 'string') { + enc = compact; + compact = null; + } + + if (!this.pub) + this.pub = this.ec.g.mul(this.priv); + + if (!enc) + return this.pub; + + return this.pub.encode(enc, compact); +}; + +KeyPair.prototype.getPrivate = function getPrivate(enc) { + if (enc === 'hex') + return this.priv.toString(16, 2); + else + return this.priv; +}; + +KeyPair.prototype._importPrivate = function _importPrivate(key, enc) { + this.priv = new BN(key, enc || 16); + + // Ensure that the priv won't be bigger than n, otherwise we may fail + // in fixed multiplication method + this.priv = this.priv.umod(this.ec.curve.n); +}; + +KeyPair.prototype._importPublic = function _importPublic(key, enc) { + if (key.x || key.y) { + // Montgomery points only have an `x` coordinate. + // Weierstrass/Edwards points on the other hand have both `x` and + // `y` coordinates. + if (this.ec.curve.type === 'mont') { + assert(key.x, 'Need x coordinate'); + } else if (this.ec.curve.type === 'short' || + this.ec.curve.type === 'edwards') { + assert(key.x && key.y, 'Need both x and y coordinate'); + } + this.pub = this.ec.curve.point(key.x, key.y); + return; + } + this.pub = this.ec.curve.decodePoint(key, enc); +}; + +// ECDH +KeyPair.prototype.derive = function derive(pub) { + return pub.mul(this.priv).getX(); +}; + +// ECDSA +KeyPair.prototype.sign = function sign(msg, enc, options) { + return this.ec.sign(msg, this, enc, options); +}; + +KeyPair.prototype.verify = function verify(msg, signature) { + return this.ec.verify(msg, signature, this); +}; + +KeyPair.prototype.inspect = function inspect() { + return ''; +}; + +},{"../../../../BN/bn":122,"../../elliptic":124}],133:[function(require,module,exports){ +'use strict'; + +var BN = require('../../../../BN/bn'); + +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var assert = utils.assert; + +function Signature(options, enc) { + if (options instanceof Signature) + return options; + + //if (this._importDER(options, enc)) + // return; + + assert(options.r && options.s, 'Signature without r or s'); + this.r = new BN(options.r, 16); + this.s = new BN(options.s, 16); + if (options.recoveryParam === undefined) + this.recoveryParam = null; + else + this.recoveryParam = options.recoveryParam; +} +module.exports = Signature; + +},{"../../../../BN/bn":122,"../../elliptic":124}],134:[function(require,module,exports){ +module.exports = { + doubles: { + step: 4, + points: [ + [ + 'e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a', + 'f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821' + ], + [ + '8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508', + '11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf' + ], + [ + '175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739', + 'd3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695' + ], + [ + '363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640', + '4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9' + ], + [ + '8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c', + '4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36' + ], + [ + '723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda', + '96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f' + ], + [ + 'eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa', + '5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999' + ], + [ + '100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0', + 'cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09' + ], + [ + 'e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d', + '9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d' + ], + [ + 'feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d', + 'e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088' + ], + [ + 'da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1', + '9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d' + ], + [ + '53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0', + '5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8' + ], + [ + '8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047', + '10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a' + ], + [ + '385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862', + '283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453' + ], + [ + '6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7', + '7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160' + ], + [ + '3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd', + '56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0' + ], + [ + '85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83', + '7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6' + ], + [ + '948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a', + '53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589' + ], + [ + '6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8', + 'bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17' + ], + [ + 'e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d', + '4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda' + ], + [ + 'e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725', + '7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd' + ], + [ + '213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754', + '4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2' + ], + [ + '4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c', + '17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6' + ], + [ + 'fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6', + '6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f' + ], + [ + '76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39', + 'c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01' + ], + [ + 'c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891', + '893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3' + ], + [ + 'd895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b', + 'febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f' + ], + [ + 'b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03', + '2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7' + ], + [ + 'e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d', + 'eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78' + ], + [ + 'a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070', + '7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1' + ], + [ + '90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4', + 'e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150' + ], + [ + '8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da', + '662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82' + ], + [ + 'e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11', + '1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc' + ], + [ + '8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e', + 'efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b' + ], + [ + 'e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41', + '2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51' + ], + [ + 'b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef', + '67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45' + ], + [ + 'd68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8', + 'db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120' + ], + [ + '324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d', + '648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84' + ], + [ + '4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96', + '35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d' + ], + [ + '9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd', + 'ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d' + ], + [ + '6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5', + '9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8' + ], + [ + 'a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266', + '40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8' + ], + [ + '7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71', + '34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac' + ], + [ + '928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac', + 'c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f' + ], + [ + '85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751', + '1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962' + ], + [ + 'ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e', + '493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907' + ], + [ + '827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241', + 'c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec' + ], + [ + 'eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3', + 'be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d' + ], + [ + 'e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f', + '4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414' + ], + [ + '1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19', + 'aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd' + ], + [ + '146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be', + 'b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0' + ], + [ + 'fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9', + '6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811' + ], + [ + 'da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2', + '8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1' + ], + [ + 'a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13', + '7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c' + ], + [ + '174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c', + 'ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73' + ], + [ + '959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba', + '2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd' + ], + [ + 'd2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151', + 'e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405' + ], + [ + '64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073', + 'd99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589' + ], + [ + '8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458', + '38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e' + ], + [ + '13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b', + '69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27' + ], + [ + 'bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366', + 'd3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1' + ], + [ + '8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa', + '40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482' + ], + [ + '8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0', + '620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945' + ], + [ + 'dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787', + '7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573' + ], + [ + 'f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e', + 'ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82' + ] + ] + }, + naf: { + wnd: 7, + points: [ + [ + 'f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9', + '388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672' + ], + [ + '2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4', + 'd8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6' + ], + [ + '5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc', + '6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da' + ], + [ + 'acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe', + 'cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37' + ], + [ + '774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb', + 'd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b' + ], + [ + 'f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8', + 'ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81' + ], + [ + 'd7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e', + '581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58' + ], + [ + 'defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34', + '4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77' + ], + [ + '2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c', + '85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a' + ], + [ + '352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5', + '321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c' + ], + [ + '2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f', + '2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67' + ], + [ + '9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714', + '73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402' + ], + [ + 'daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729', + 'a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55' + ], + [ + 'c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db', + '2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482' + ], + [ + '6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4', + 'e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82' + ], + [ + '1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5', + 'b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396' + ], + [ + '605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479', + '2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49' + ], + [ + '62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d', + '80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf' + ], + [ + '80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f', + '1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a' + ], + [ + '7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb', + 'd0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7' + ], + [ + 'd528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9', + 'eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933' + ], + [ + '49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963', + '758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a' + ], + [ + '77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74', + '958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6' + ], + [ + 'f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530', + 'e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37' + ], + [ + '463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b', + '5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e' + ], + [ + 'f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247', + 'cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6' + ], + [ + 'caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1', + 'cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476' + ], + [ + '2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120', + '4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40' + ], + [ + '7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435', + '91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61' + ], + [ + '754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18', + '673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683' + ], + [ + 'e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8', + '59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5' + ], + [ + '186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb', + '3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b' + ], + [ + 'df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f', + '55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417' + ], + [ + '5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143', + 'efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868' + ], + [ + '290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba', + 'e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a' + ], + [ + 'af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45', + 'f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6' + ], + [ + '766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a', + '744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996' + ], + [ + '59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e', + 'c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e' + ], + [ + 'f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8', + 'e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d' + ], + [ + '7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c', + '30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2' + ], + [ + '948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519', + 'e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e' + ], + [ + '7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab', + '100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437' + ], + [ + '3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca', + 'ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311' + ], + [ + 'd3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf', + '8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4' + ], + [ + '1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610', + '68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575' + ], + [ + '733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4', + 'f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d' + ], + [ + '15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c', + 'd56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d' + ], + [ + 'a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940', + 'edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629' + ], + [ + 'e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980', + 'a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06' + ], + [ + '311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3', + '66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374' + ], + [ + '34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf', + '9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee' + ], + [ + 'f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63', + '4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1' + ], + [ + 'd7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448', + 'fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b' + ], + [ + '32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf', + '5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661' + ], + [ + '7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5', + '8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6' + ], + [ + 'ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6', + '8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e' + ], + [ + '16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5', + '5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d' + ], + [ + 'eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99', + 'f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc' + ], + [ + '78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51', + 'f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4' + ], + [ + '494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5', + '42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c' + ], + [ + 'a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5', + '204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b' + ], + [ + 'c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997', + '4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913' + ], + [ + '841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881', + '73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154' + ], + [ + '5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5', + '39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865' + ], + [ + '36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66', + 'd2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc' + ], + [ + '336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726', + 'ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224' + ], + [ + '8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede', + '6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e' + ], + [ + '1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94', + '60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6' + ], + [ + '85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31', + '3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511' + ], + [ + '29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51', + 'b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b' + ], + [ + 'a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252', + 'ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2' + ], + [ + '4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5', + 'cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c' + ], + [ + 'd24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b', + '6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3' + ], + [ + 'ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4', + '322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d' + ], + [ + 'af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f', + '6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700' + ], + [ + 'e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889', + '2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4' + ], + [ + '591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246', + 'b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196' + ], + [ + '11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984', + '998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4' + ], + [ + '3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a', + 'b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257' + ], + [ + 'cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030', + 'bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13' + ], + [ + 'c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197', + '6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096' + ], + [ + 'c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593', + 'c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38' + ], + [ + 'a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef', + '21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f' + ], + [ + '347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38', + '60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448' + ], + [ + 'da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a', + '49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a' + ], + [ + 'c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111', + '5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4' + ], + [ + '4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502', + '7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437' + ], + [ + '3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea', + 'be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7' + ], + [ + 'cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26', + '8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d' + ], + [ + 'b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986', + '39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a' + ], + [ + 'd4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e', + '62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54' + ], + [ + '48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4', + '25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77' + ], + [ + 'dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda', + 'ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517' + ], + [ + '6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859', + 'cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10' + ], + [ + 'e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f', + 'f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125' + ], + [ + 'eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c', + '6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e' + ], + [ + '13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942', + 'fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1' + ], + [ + 'ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a', + '1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2' + ], + [ + 'b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80', + '5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423' + ], + [ + 'ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d', + '438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8' + ], + [ + '8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1', + 'cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758' + ], + [ + '52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63', + 'c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375' + ], + [ + 'e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352', + '6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d' + ], + [ + '7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193', + 'ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec' + ], + [ + '5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00', + '9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0' + ], + [ + '32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58', + 'ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c' + ], + [ + 'e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7', + 'd3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4' + ], + [ + '8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8', + 'c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f' + ], + [ + '4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e', + '67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649' + ], + [ + '3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d', + 'cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826' + ], + [ + '674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b', + '299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5' + ], + [ + 'd32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f', + 'f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87' + ], + [ + '30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6', + '462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b' + ], + [ + 'be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297', + '62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc' + ], + [ + '93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a', + '7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c' + ], + [ + 'b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c', + 'ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f' + ], + [ + 'd5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52', + '4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a' + ], + [ + 'd3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb', + 'bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46' + ], + [ + '463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065', + 'bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f' + ], + [ + '7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917', + '603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03' + ], + [ + '74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9', + 'cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08' + ], + [ + '30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3', + '553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8' + ], + [ + '9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57', + '712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373' + ], + [ + '176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66', + 'ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3' + ], + [ + '75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8', + '9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8' + ], + [ + '809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721', + '9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1' + ], + [ + '1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180', + '4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9' + ] + ] + } +}; + +},{}],135:[function(require,module,exports){ +'use strict'; + +var utils = exports; +var BN = require('../../../BN/bn'); + +utils.assert = function (condition, errorMessage) { + if (!condition) { + throw new Error(errorMessage) + } +}; + +// Represent num in a w-NAF form +function getNAF(num, w) { + var naf = []; + var ws = 1 << (w + 1); + var k = num.clone(); + while (k.cmpn(1) >= 0) { + var z; + if (k.isOdd()) { + var mod = k.andln(ws - 1); + if (mod > (ws >> 1) - 1) + z = (ws >> 1) - mod; + else + z = mod; + k.isubn(z); + } else { + z = 0; + } + naf.push(z); + + // Optimization, shift by word if possible + var shift = (k.cmpn(0) !== 0 && k.andln(ws - 1) === 0) ? (w + 1) : 1; + for (var i = 1; i < shift; i++) + naf.push(0); + k.iushrn(shift); + } + + return naf; +} +utils.getNAF = getNAF; + +// Represent k1, k2 in a Joint Sparse Form +function getJSF(k1, k2) { + var jsf = [ + [], + [] + ]; + + k1 = k1.clone(); + k2 = k2.clone(); + var d1 = 0; + var d2 = 0; + while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) { + + // First phase + var m14 = (k1.andln(3) + d1) & 3; + var m24 = (k2.andln(3) + d2) & 3; + if (m14 === 3) + m14 = -1; + if (m24 === 3) + m24 = -1; + var u1; + if ((m14 & 1) === 0) { + u1 = 0; + } else { + var m8 = (k1.andln(7) + d1) & 7; + if ((m8 === 3 || m8 === 5) && m24 === 2) + u1 = -m14; + else + u1 = m14; + } + jsf[0].push(u1); + + var u2; + if ((m24 & 1) === 0) { + u2 = 0; + } else { + var m8 = (k2.andln(7) + d2) & 7; + if ((m8 === 3 || m8 === 5) && m14 === 2) + u2 = -m24; + else + u2 = m24; + } + jsf[1].push(u2); + + // Second phase + if (2 * d1 === u1 + 1) + d1 = 1 - d1; + if (2 * d2 === u2 + 1) + d2 = 1 - d2; + k1.iushrn(1); + k2.iushrn(1); + } + + return jsf; +} +utils.getJSF = getJSF; + +function cachedProperty(obj, name, computer) { + var key = '_' + name; + obj.prototype[name] = function cachedProperty() { + return this[key] !== undefined ? this[key] : + this[key] = computer.call(this); + }; +} +utils.cachedProperty = cachedProperty; + +function parseBytes(bytes) { + return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') : + bytes; +} +utils.parseBytes = parseBytes; + +function intFromLE(bytes) { + return new BN(bytes, 'hex', 'le'); +} +utils.intFromLE = intFromLE; + +// used to convert `CryptoJS` wordArrays into `crypto` hex buffers +function wordToByteArray(word, length) { + var ba = [], + xFF = 0xFF; + if (length > 0) + ba.push(word >>> 24); + if (length > 1) + ba.push((word >>> 16) & xFF); + if (length > 2) + ba.push((word >>> 8) & xFF); + if (length > 3) + ba.push(word & xFF); + + return ba; +} + +function wordArrayToBuffer(wordArray) { + let length = undefined; + if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) { + length = wordArray.sigBytes; + wordArray = wordArray.words; + } else { + throw Error('Argument not a wordArray') + } + + const result = [] + let bytes = [] + let i = 0; + while (length > 0) { + bytes = wordToByteArray(wordArray[i], Math.min(4, length)); + length -= bytes.length; + result.push(bytes); + i++; + } + return [].concat.apply([], result) +} + +utils.wordArrayToBuffer = wordArrayToBuffer; + +},{"../../../BN/bn":122}],136:[function(require,module,exports){ +'use strict'; + +var CryptoJS = require('../../../crypto-js/crypto-js'); +var assert = require('../elliptic/utils').assert; +var utils = require ('../elliptic/utils') +var { byteArrayToWordArray } = require('../../../../base/functions/encode'); + +// some static stuff +const ONE = CryptoJS.enc.Utf8.parse ('\x01') +const ZERO = CryptoJS.enc.Utf8.parse ('\x00') + +function HmacDRBG(options) { + if (!(this instanceof HmacDRBG)) + return new HmacDRBG(options); + this.hash = options.hash + this.predResist = !!options.predResist; + + this.outLen = this.hash.slice (3, 6); // only support SHAXXX hashes + this.minEntropy = options.minEntropy || 192; + + this._reseed = null; + this.reseedInterval = null; + this.K = null; + this.V = null; + + var entropy = options.entropy + var nonce = options.nonce + var pers = [] + assert(entropy.length >= (this.minEntropy / 8), + 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); + this._init(entropy, nonce, pers); +} +module.exports = HmacDRBG; + +HmacDRBG.prototype._init = function init(entropy, nonce, pers) { + var seed = entropy.concat(nonce).concat(pers); + + this.K = new CryptoJS.lib.WordArray.init () + this.V = new CryptoJS.lib.WordArray.init () + const magicNumber = (1 << 24) + (1 << 16) + (1 << 8) + 1 + this.V.words = Array (this.outLen / 32).fill (magicNumber) + this.V.sigBytes = 32 + this._update(seed); + this._reseed = 1; + this.reseedInterval = 0x1000000000000; // 2^48 +}; + +HmacDRBG.prototype._hmac = function hmac() { + return new CryptoJS.lib.WordArray.init (); +}; + +HmacDRBG.prototype._update = function update(seed) { + var kmac = this._hmac () + kmac.concat (this.V) + kmac.concat (ZERO) + if (seed) + kmac.concat (byteArrayToWordArray (seed)); + this.K = CryptoJS['Hmac' + this.hash] (kmac, this.K) + this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) + if (!seed) + return; + + kmac = this._hmac () + kmac.concat (this.V) + kmac.concat (ONE) + kmac.concat (byteArrayToWordArray (seed)) + this.K = CryptoJS['Hmac' + this.hash] (kmac, this.K) + this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) +}; + +HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) { + // Optional entropy enc + if (typeof entropyEnc !== 'string') { + addEnc = add; + add = entropyEnc; + entropyEnc = null; + } + + //entropy = utils.toArray(entropy, entropyEnc); + //add = utils.toArray(add, addEnc); + + assert(entropy.length >= (this.minEntropy / 8), + 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); + + this._update(entropy.concat(add || [])); + this._reseed = 1; +}; + +HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) { + if (this._reseed > this.reseedInterval) + throw new Error ('Reseed is required'); + + // Optional encoding + if (typeof enc !== 'string') { + addEnc = add; + add = enc; + enc = null; + } + + // Optional additional data + if (add) { + add = CryptoJS.enc.Utf8.parse (add); + this._update (add); + } + + var res = new CryptoJS.lib.WordArray.init (); + while (res.sigBytes < len) { + this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) + res.concat (this.V); + } + this._update (add); + this._reseed++; + return utils.wordArrayToBuffer(res); +}; + +},{"../../../../base/functions/encode":12,"../../../crypto-js/crypto-js":123,"../elliptic/utils":135}],137:[function(require,module,exports){ +'use strict'; + +var fetch = require('../node-fetch/index'); + +function wrapFetchForNode(fetch) { + // Support schemaless URIs on the server for parity with the browser. + // https://github.com/matthew-andrews/isomorphic-fetch/pull/10 + const f = function (u, options) { + if (typeof u === 'string' && u.slice(0, 2) === '//') { + return fetch('https:' + u, options); + } + + return fetch(u, options); + }; + // Igor Kroitor 2019 Dec 30 + // expose http and https + // https://github.com/ccxt/ccxt/issues/6327 + f.http = fetch.http; + f.https = fetch.https; + return f; +} + +module.exports = function (context) { + // This modifies the global `node-fetch` object, which isn't great, since + // different callers to `fetch-ponyfill` which pass a different Promise + // implementation would each expect to have their implementation used. But, + // given the way `node-fetch` is implemented, this is the only way to make + // it work at all. + if (context && context.Promise) { + fetch.Promise = context.Promise; + } + + return { + fetch: wrapFetchForNode(fetch), + Headers: fetch.Headers, + Request: fetch.Request, + Response: fetch.Response + }; +}; + +},{"../node-fetch/index":138}],138:[function(require,module,exports){ +(function (global,Buffer){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js +// (MIT licensed) + +const BUFFER = Symbol('buffer'); +const TYPE = Symbol('type'); + +class Blob { + constructor() { + this[TYPE] = ''; + + const blobParts = arguments[0]; + const options = arguments[1]; + + const buffers = []; + + if (blobParts) { + const a = blobParts; + const length = Number(a.length); + for (let i = 0; i < length; i++) { + const element = a[i]; + let buffer; + if (element instanceof Buffer) { + buffer = element; + } else if (ArrayBuffer.isView(element)) { + buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); + } else if (element instanceof ArrayBuffer) { + buffer = Buffer.from(element); + } else if (element instanceof Blob) { + buffer = element[BUFFER]; + } else { + buffer = Buffer.from(typeof element === 'string' ? element : String(element)); + } + buffers.push(buffer); + } + } + + this[BUFFER] = Buffer.concat(buffers); + + let type = options && options.type !== undefined && String(options.type).toLowerCase(); + if (type && !/[^\u0020-\u007E]/.test(type)) { + this[TYPE] = type; + } + } + get size() { + return this[BUFFER].length; + } + get type() { + return this[TYPE]; + } + slice() { + const size = this.size; + + const start = arguments[0]; + const end = arguments[1]; + let relativeStart, relativeEnd; + if (start === undefined) { + relativeStart = 0; + } else if (start < 0) { + relativeStart = Math.max(size + start, 0); + } else { + relativeStart = Math.min(start, size); + } + if (end === undefined) { + relativeEnd = size; + } else if (end < 0) { + relativeEnd = Math.max(size + end, 0); + } else { + relativeEnd = Math.min(end, size); + } + const span = Math.max(relativeEnd - relativeStart, 0); + + const buffer = this[BUFFER]; + const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); + const blob = new Blob([], { type: arguments[2] }); + blob[BUFFER] = slicedBuffer; + return blob; + } +} + +Object.defineProperties(Blob.prototype, { + size: { enumerable: true }, + type: { enumerable: true }, + slice: { enumerable: true } +}); + +Object.defineProperty(Blob.prototype, Symbol.toStringTag, { + value: 'Blob', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * fetch-error.js + * + * FetchError interface for operational errors + */ + +/** + * Create FetchError instance + * + * @param String message Error message for human + * @param String type Error type for machine + * @param String systemError For Node.js system error + * @return FetchError + */ +function FetchError(message, type, systemError) { + Error.call(this, message); + + this.message = message; + this.type = type; + + // when err.type is `system`, err.code contains system error code + if (systemError) { + this.code = this.errno = systemError.code; + } + + // hide custom error implementation details from end-users + Error.captureStackTrace(this, this.constructor); +} + +FetchError.prototype = Object.create(Error.prototype); +FetchError.prototype.constructor = FetchError; +FetchError.prototype.name = 'FetchError'; + +/** + * body.js + * + * Body interface provides common methods for Request and Response + */ + +const Stream = require('stream'); + +var _require = require('stream'); + +const PassThrough = _require.PassThrough; + +/* +let convert; +try { + convert = require('encoding').convert; +} catch (e) {} +*/ + +const INTERNALS = Symbol('Body internals'); + +/** + * Body mixin + * + * Ref: https://fetch.spec.whatwg.org/#body + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +function Body(body) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$size = _ref.size; + + let size = _ref$size === undefined ? 0 : _ref$size; + var _ref$timeout = _ref.timeout; + let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; + + if (body == null) { + // body is undefined or null + body = null; + } else if (typeof body === 'string') { + // body is string + } else if (isURLSearchParams(body)) { + // body is a URLSearchParams + } else if (body instanceof Blob) { + // body is blob + } else if (Buffer.isBuffer(body)) { + // body is buffer + } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') { + // body is array buffer + } else if (body instanceof Stream) { + // body is stream + } else { + // none of the above + // coerce to string + body = String(body); + } + this[INTERNALS] = { + body, + disturbed: false, + error: null + }; + this.size = size; + this.timeout = timeout; + + if (body instanceof Stream) { + body.on('error', function (err) { + _this[INTERNALS].error = new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err); + }); + } +} + +Body.prototype = { + get body() { + return this[INTERNALS].body; + }, + + get bodyUsed() { + return this[INTERNALS].disturbed; + }, + + /** + * Decode response as ArrayBuffer + * + * @return Promise + */ + arrayBuffer() { + return consumeBody.call(this).then(function (buf) { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + }); + }, + + /** + * Return raw response as Blob + * + * @return Promise + */ + blob() { + let ct = this.headers && this.headers.get('content-type') || ''; + return consumeBody.call(this).then(function (buf) { + return Object.assign( + // Prevent copying + new Blob([], { + type: ct.toLowerCase() + }), { + [BUFFER]: buf + }); + }); + }, + + /** + * Decode response as json + * + * @return Promise + */ + json() { + var _this2 = this; + + return consumeBody.call(this).then(function (buffer) { + try { + return JSON.parse(buffer.toString()); + } catch (err) { + return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json')); + } + }); + }, + + /** + * Decode response as text + * + * @return Promise + */ + text() { + return consumeBody.call(this).then(function (buffer) { + return buffer.toString(); + }); + }, + + /** + * Decode response as buffer (non-spec api) + * + * @return Promise + */ + buffer() { + return consumeBody.call(this); + }, + + /** + * Decode response as text, while automatically detecting the encoding and + * trying to decode to UTF-8 (non-spec api) + * + * @return Promise + */ + textConverted() { + var _this3 = this; + + return consumeBody.call(this).then(function (buffer) { + return convertBody(buffer, _this3.headers); + }); + } + +}; + +// In browsers, all properties are enumerable. +Object.defineProperties(Body.prototype, { + body: { enumerable: true }, + bodyUsed: { enumerable: true }, + arrayBuffer: { enumerable: true }, + blob: { enumerable: true }, + json: { enumerable: true }, + text: { enumerable: true } +}); + +Body.mixIn = function (proto) { + for (const name of Object.getOwnPropertyNames(Body.prototype)) { + // istanbul ignore else: future proof + if (!(name in proto)) { + const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); + Object.defineProperty(proto, name, desc); + } + } +}; + +/** + * Consume and convert an entire Body to a Buffer. + * + * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body + * + * @return Promise + */ +function consumeBody() { + var _this4 = this; + + if (this[INTERNALS].disturbed) { + return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`)); + } + + this[INTERNALS].disturbed = true; + + if (this[INTERNALS].error) { + return Body.Promise.reject(this[INTERNALS].error); + } + + // body is null + if (this.body === null) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is string + if (typeof this.body === 'string') { + return Body.Promise.resolve(Buffer.from(this.body)); + } + + // body is blob + if (this.body instanceof Blob) { + return Body.Promise.resolve(this.body[BUFFER]); + } + + // body is buffer + if (Buffer.isBuffer(this.body)) { + return Body.Promise.resolve(this.body); + } + + // body is buffer + if (Object.prototype.toString.call(this.body) === '[object ArrayBuffer]') { + return Body.Promise.resolve(Buffer.from(this.body)); + } + + // istanbul ignore if: should never happen + if (!(this.body instanceof Stream)) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is stream + // get ready to actually consume the body + let accum = []; + let accumBytes = 0; + let abort = false; + + return new Body.Promise(function (resolve, reject) { + let resTimeout; + + // allow timeout on slow response body + if (_this4.timeout) { + resTimeout = setTimeout(function () { + abort = true; + reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout')); + }, _this4.timeout); + } + + // handle stream error, such as incorrect content-encoding + _this4.body.on('error', function (err) { + reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err)); + }); + + _this4.body.on('data', function (chunk) { + if (abort || chunk === null) { + return; + } + + if (_this4.size && accumBytes + chunk.length > _this4.size) { + abort = true; + reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size')); + return; + } + + accumBytes += chunk.length; + accum.push(chunk); + }); + + _this4.body.on('end', function () { + if (abort) { + return; + } + + clearTimeout(resTimeout); + + try { + resolve(Buffer.concat(accum)); + } catch (err) { + // handle streams that have accumulated too much data (issue #414) + reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + }); +} + +/** + * Detect buffer encoding and convert to target encoding + * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding + * + * @param Buffer buffer Incoming buffer + * @param String encoding Target encoding + * @return String + */ +function convertBody(buffer, headers) { + if (typeof convert !== 'function') { + throw new Error('The package `encoding` must be installed to use the textConverted() function'); + } + + const ct = headers.get('content-type'); + let charset = 'utf-8'; + let res, str; + + // header + if (ct) { + res = /charset=([^;]*)/i.exec(ct); + } + + // no charset in content type, peek at response body for at most 1024 bytes + str = buffer.slice(0, 1024).toString(); + + // html5 + if (!res && str) { + res = / 0 && arguments[0] !== undefined ? arguments[0] : undefined; + + this[MAP] = Object.create(null); + + if (init instanceof Headers) { + const rawHeaders = init.raw(); + const headerNames = Object.keys(rawHeaders); + + for (const headerName of headerNames) { + for (const value of rawHeaders[headerName]) { + this.append(headerName, value); + } + } + + return; + } + + // We don't worry about converting prop to ByteString here as append() + // will handle it. + if (init == null) { + // no op + } else if (typeof init === 'object') { + const method = init[Symbol.iterator]; + if (method != null) { + if (typeof method !== 'function') { + throw new TypeError('Header pairs must be iterable'); + } + + // sequence> + // Note: per spec we have to first exhaust the lists then process them + const pairs = []; + for (const pair of init) { + if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { + throw new TypeError('Each header pair must be iterable'); + } + pairs.push(Array.from(pair)); + } + + for (const pair of pairs) { + if (pair.length !== 2) { + throw new TypeError('Each header pair must be a name/value tuple'); + } + this.append(pair[0], pair[1]); + } + } else { + // record + for (const key of Object.keys(init)) { + const value = init[key]; + this.append(key, value); + } + } + } else { + throw new TypeError('Provided initializer must be an object'); + } + } + + /** + * Return combined header value given name + * + * @param String name Header name + * @return Mixed + */ + get(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key === undefined) { + return null; + } + + return this[MAP][key].join(', '); + } + + /** + * Iterate over all headers + * + * @param Function callback Executed for each item with parameters (value, name, thisArg) + * @param Boolean thisArg `this` context for callback function + * @return Void + */ + forEach(callback) { + let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; + + let pairs = getHeaders(this); + let i = 0; + while (i < pairs.length) { + var _pairs$i = pairs[i]; + const name = _pairs$i[0], + value = _pairs$i[1]; + + callback.call(thisArg, value, name, this); + pairs = getHeaders(this); + i++; + } + } + + /** + * Overwrite header values given name + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + set(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + this[MAP][key !== undefined ? key : name] = [value]; + } + + /** + * Append a value onto existing header + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + append(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + if (key !== undefined) { + this[MAP][key].push(value); + } else { + this[MAP][name] = [value]; + } + } + + /** + * Check for header name existence + * + * @param String name Header name + * @return Boolean + */ + has(name) { + name = `${name}`; + validateName(name); + return find(this[MAP], name) !== undefined; + } + + /** + * Delete all header values given name + * + * @param String name Header name + * @return Void + */ + delete(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key !== undefined) { + delete this[MAP][key]; + } + } + + /** + * Return raw headers (non-spec api) + * + * @return Object + */ + raw() { + return this[MAP]; + } + + /** + * Get an iterator on keys. + * + * @return Iterator + */ + keys() { + return createHeadersIterator(this, 'key'); + } + + /** + * Get an iterator on values. + * + * @return Iterator + */ + values() { + return createHeadersIterator(this, 'value'); + } + + /** + * Get an iterator on entries. + * + * This is the default iterator of the Headers object. + * + * @return Iterator + */ + [Symbol.iterator]() { + return createHeadersIterator(this, 'key+value'); + } +} +Headers.prototype.entries = Headers.prototype[Symbol.iterator]; + +Object.defineProperty(Headers.prototype, Symbol.toStringTag, { + value: 'Headers', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Headers.prototype, { + get: { enumerable: true }, + forEach: { enumerable: true }, + set: { enumerable: true }, + append: { enumerable: true }, + has: { enumerable: true }, + delete: { enumerable: true }, + keys: { enumerable: true }, + values: { enumerable: true }, + entries: { enumerable: true } +}); + +function getHeaders(headers) { + let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value'; + + const keys = Object.keys(headers[MAP]).sort(); + return keys.map(kind === 'key' ? function (k) { + return k.toLowerCase(); + } : kind === 'value' ? function (k) { + return headers[MAP][k].join(', '); + } : function (k) { + return [k.toLowerCase(), headers[MAP][k].join(', ')]; + }); +} + +const INTERNAL = Symbol('internal'); + +function createHeadersIterator(target, kind) { + const iterator = Object.create(HeadersIteratorPrototype); + iterator[INTERNAL] = { + target, + kind, + index: 0 + }; + return iterator; +} + +const HeadersIteratorPrototype = Object.setPrototypeOf({ + next() { + // istanbul ignore if + if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { + throw new TypeError('Value of `this` is not a HeadersIterator'); + } + + var _INTERNAL = this[INTERNAL]; + const target = _INTERNAL.target, + kind = _INTERNAL.kind, + index = _INTERNAL.index; + + const values = getHeaders(target, kind); + const len = values.length; + if (index >= len) { + return { + value: undefined, + done: true + }; + } + + this[INTERNAL].index = index + 1; + + return { + value: values[index], + done: false + }; + } +}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); + +Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { + value: 'HeadersIterator', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * Export the Headers object in a form that Node.js can consume. + * + * @param Headers headers + * @return Object + */ +function exportNodeCompatibleHeaders(headers) { + const obj = Object.assign({ __proto__: null }, headers[MAP]); + + // http.request() only supports string as Host header. This hack makes + // specifying custom Host header possible. + const hostHeaderKey = find(headers[MAP], 'Host'); + if (hostHeaderKey !== undefined) { + obj[hostHeaderKey] = obj[hostHeaderKey][0]; + } + + return obj; +} + +/** + * Create a Headers object from an object of headers, ignoring those that do + * not conform to HTTP grammar productions. + * + * @param Object obj Object of headers + * @return Headers + */ +function createHeadersLenient(obj) { + const headers = new Headers(); + for (const name of Object.keys(obj)) { + if (invalidTokenRegex.test(name)) { + continue; + } + if (Array.isArray(obj[name])) { + for (const val of obj[name]) { + if (invalidHeaderCharRegex.test(val)) { + continue; + } + if (headers[MAP][name] === undefined) { + headers[MAP][name] = [val]; + } else { + headers[MAP][name].push(val); + } + } + } else if (!invalidHeaderCharRegex.test(obj[name])) { + headers[MAP][name] = [obj[name]]; + } + } + return headers; +} + +/** + * response.js + * + * Response class provides content decoding + */ + +var _require$1 = require('http'); + +const STATUS_CODES = _require$1.STATUS_CODES; + + +const INTERNALS$1 = Symbol('Response internals'); + +/** + * Response class + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +class Response { + constructor() { + let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + Body.call(this, body, opts); + + const status = opts.status || 200; + + this[INTERNALS$1] = { + url: opts.url, + status, + statusText: opts.statusText || STATUS_CODES[status], + headers: new Headers(opts.headers) + }; + } + + get url() { + return this[INTERNALS$1].url; + } + + get status() { + return this[INTERNALS$1].status; + } + + /** + * Convenience property representing if the request ended normally + */ + get ok() { + return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300; + } + + get statusText() { + return this[INTERNALS$1].statusText; + } + + get headers() { + return this[INTERNALS$1].headers; + } + + /** + * Clone this response + * + * @return Response + */ + clone() { + return new Response(clone(this), { + url: this.url, + status: this.status, + statusText: this.statusText, + headers: this.headers, + ok: this.ok + }); + } +} + +Body.mixIn(Response.prototype); + +Object.defineProperties(Response.prototype, { + url: { enumerable: true }, + status: { enumerable: true }, + ok: { enumerable: true }, + statusText: { enumerable: true }, + headers: { enumerable: true }, + clone: { enumerable: true } +}); + +Object.defineProperty(Response.prototype, Symbol.toStringTag, { + value: 'Response', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * request.js + * + * Request class contains server only options + * + * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. + */ + +var _require$2 = require('url'); + +const format_url = _require$2.format; +const parse_url = _require$2.parse; + + +const INTERNALS$2 = Symbol('Request internals'); + +/** + * Check if a value is an instance of Request. + * + * @param Mixed input + * @return Boolean + */ +function isRequest(input) { + return typeof input === 'object' && typeof input[INTERNALS$2] === 'object'; +} + +/** + * Request class + * + * @param Mixed input Url or Request instance + * @param Object init Custom options + * @return Void + */ +class Request { + constructor(input) { + let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + let parsedURL; + + // normalize input + if (!isRequest(input)) { + if (input && input.href) { + // in order to support Node.js' Url objects; though WHATWG's URL objects + // will fall into this branch also (since their `toString()` will return + // `href` property anyway) + parsedURL = parse_url(input.href); + } else { + // coerce input to a string before attempting to parse + parsedURL = parse_url(`${input}`); + } + input = {}; + } else { + parsedURL = parse_url(input.url); + } + + let method = init.method || input.method || 'GET'; + method = method.toUpperCase(); + + if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) { + throw new TypeError('Request with GET/HEAD method cannot have body'); + } + + let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null; + + Body.call(this, inputBody, { + timeout: init.timeout || input.timeout || 0, + size: init.size || input.size || 0 + }); + + const headers = new Headers(init.headers || input.headers || {}); + + if (init.body != null) { + const contentType = extractContentType(this); + if (contentType !== null && !headers.has('Content-Type')) { + headers.append('Content-Type', contentType); + } + } + + this[INTERNALS$2] = { + method, + redirect: init.redirect || input.redirect || 'follow', + headers, + parsedURL + }; + + // node-fetch-only options + this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20; + this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true; + this.counter = init.counter || input.counter || 0; + this.agent = init.agent || input.agent; + } + + get method() { + return this[INTERNALS$2].method; + } + + get url() { + return format_url(this[INTERNALS$2].parsedURL); + } + + get headers() { + return this[INTERNALS$2].headers; + } + + get redirect() { + return this[INTERNALS$2].redirect; + } + + /** + * Clone this request + * + * @return Request + */ + clone() { + return new Request(this); + } +} + +Body.mixIn(Request.prototype); + +Object.defineProperty(Request.prototype, Symbol.toStringTag, { + value: 'Request', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Request.prototype, { + method: { enumerable: true }, + url: { enumerable: true }, + headers: { enumerable: true }, + redirect: { enumerable: true }, + clone: { enumerable: true } +}); + +/** + * Convert a Request to Node.js http request options. + * + * @param Request A Request instance + * @return Object The options object to be passed to http.request + */ +function getNodeRequestOptions(request) { + const parsedURL = request[INTERNALS$2].parsedURL; + const headers = new Headers(request[INTERNALS$2].headers); + + // fetch step 1.3 + if (!headers.has('Accept')) { + headers.set('Accept', '*/*'); + } + + // Basic fetch + if (!parsedURL.protocol || !parsedURL.hostname) { + throw new TypeError('Only absolute URLs are supported'); + } + + if (!/^https?:$/.test(parsedURL.protocol)) { + throw new TypeError('Only HTTP(S) protocols are supported'); + } + + // HTTP-network-or-cache fetch steps 2.4-2.7 + let contentLengthValue = null; + if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { + contentLengthValue = '0'; + } + if (request.body != null) { + const totalBytes = getTotalBytes(request); + if (typeof totalBytes === 'number') { + contentLengthValue = String(totalBytes); + } + } + if (contentLengthValue) { + headers.set('Content-Length', contentLengthValue); + } + + // HTTP-network-or-cache fetch step 2.11 + if (!headers.has('User-Agent')) { + headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)'); + } + + // HTTP-network-or-cache fetch step 2.15 + if (request.compress) { + headers.set('Accept-Encoding', 'gzip,deflate'); + } + if (!headers.has('Connection') && !request.agent) { + headers.set('Connection', 'close'); + } + + // HTTP-network fetch step 4.2 + // chunked encoding is handled by Node.js + + return Object.assign({}, parsedURL, { + method: request.method, + headers: exportNodeCompatibleHeaders(headers), + agent: request.agent + }); +} + +/** + * index.js + * + * a request API compatible with window.fetch + * + * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. + */ + +const http = require('http'); +const https = require('https'); + +var _require$3 = require('stream'); + +const PassThrough$1 = _require$3.PassThrough; + +var _require2 = require('url'); + +const resolve_url = _require2.resolve; + +const zlib = require('zlib'); + +/** + * Fetch function + * + * @param Mixed url Absolute url or Request instance + * @param Object opts Fetch options + * @return Promise + */ +function fetch(url, opts) { + + // allow custom promise + if (!fetch.Promise) { + throw new Error('native promise missing, set fetch.Promise to your favorite alternative'); + } + + Body.Promise = fetch.Promise; + + // wrap http.request into fetch + return new fetch.Promise(function (resolve, reject) { + // build request object + const request = new Request(url, opts); + const options = getNodeRequestOptions(request); + + const send = (options.protocol === 'https:' ? https : http).request; + + // send request + const req = send(options); + let reqTimeout; + + function finalize() { + req.abort(); + clearTimeout(reqTimeout); + } + + if (request.timeout) { + req.once('socket', function (socket) { + reqTimeout = setTimeout(function () { + reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); + finalize(); + }, request.timeout); + }); + } + + req.on('error', function (err) { + reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); + finalize(); + }); + + req.on('response', function (res) { + clearTimeout(reqTimeout); + + const headers = createHeadersLenient(res.headers); + + // HTTP fetch step 5 + if (fetch.isRedirect(res.statusCode)) { + // HTTP fetch step 5.2 + const location = headers.get('Location'); + + // HTTP fetch step 5.3 + const locationURL = location === null ? null : resolve_url(request.url, location); + + // HTTP fetch step 5.5 + switch (request.redirect) { + case 'error': + reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); + finalize(); + return; + case 'manual': + // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL. + if (locationURL !== null) { + headers.set('Location', locationURL); + } + break; + case 'follow': + // HTTP-redirect fetch step 2 + if (locationURL === null) { + break; + } + + // HTTP-redirect fetch step 5 + if (request.counter >= request.follow) { + reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 6 (counter increment) + // Create a new Request object. + const requestOpts = { + headers: new Headers(request.headers), + follow: request.follow, + counter: request.counter + 1, + agent: request.agent, + compress: request.compress, + method: request.method, + body: request.body + }; + + // HTTP-redirect fetch step 9 + if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { + reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 11 + if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { + requestOpts.method = 'GET'; + requestOpts.body = undefined; + requestOpts.headers.delete('content-length'); + } + + // HTTP-redirect fetch step 15 + resolve(fetch(new Request(locationURL, requestOpts))); + finalize(); + return; + } + } + + // prepare response + let body = res.pipe(new PassThrough$1()); + const response_options = { + url: request.url, + status: res.statusCode, + statusText: res.statusMessage, + headers: headers, + size: request.size, + timeout: request.timeout + }; + + // HTTP-network fetch step 12.1.1.3 + const codings = headers.get('Content-Encoding'); + + // HTTP-network fetch step 12.1.1.4: handle content codings + + // in following scenarios we ignore compression support + // 1. compression support is disabled + // 2. HEAD request + // 3. no Content-Encoding header + // 4. no content response (204) + // 5. content not modified response (304) + if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { + resolve(new Response(body, response_options)); + return; + } + + // For Node v6+ + // Be less strict when decoding compressed responses, since sometimes + // servers send slightly invalid responses that are still accepted + // by common browsers. + // Always using Z_SYNC_FLUSH is what cURL does. + const zlibOptions = { + flush: zlib.Z_SYNC_FLUSH, + finishFlush: zlib.Z_SYNC_FLUSH + }; + + // for gzip + if (codings == 'gzip' || codings == 'x-gzip') { + body = body.pipe(zlib.createGunzip(zlibOptions)); + resolve(new Response(body, response_options)); + return; + } + + // for deflate + if (codings == 'deflate' || codings == 'x-deflate') { + // handle the infamous raw deflate response from old servers + // a hack for old IIS and Apache servers + const raw = res.pipe(new PassThrough$1()); + raw.once('data', function (chunk) { + // see http://stackoverflow.com/questions/37519828 + if ((chunk[0] & 0x0F) === 0x08) { + body = body.pipe(zlib.createInflate()); + } else { + body = body.pipe(zlib.createInflateRaw()); + } + resolve(new Response(body, response_options)); + }); + return; + } + + // otherwise, use response as-is + resolve(new Response(body, response_options)); + }); + + writeToStream(req, request); + }); +} + +/** + * Redirect code matching + * + * @param Number code Status code + * @return Boolean + */ +fetch.isRedirect = function (code) { + return code === 301 || code === 302 || code === 303 || code === 307 || code === 308; +}; + +// Needed for TypeScript. +fetch.default = fetch; + +// expose Promise +fetch.Promise = global.Promise; + +// Igor Kroitor 2019 Dec 30 +// expose http and https +// https://github.com/ccxt/ccxt/issues/6327 +fetch.http = http +fetch.https = https + +module.exports = exports = fetch; +exports.Headers = Headers; +exports.Request = Request; +exports.Response = Response; +exports.FetchError = FetchError; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) +},{"buffer":180,"http":222,"https":185,"stream":221,"url":228,"zlib":179}],139:[function(require,module,exports){ +(function (Buffer){ +/*! + * RSA library for Node.js + * + * Author: rzcoder + * License MIT + */ + +var rsa = require('./libs/rsa.js'); +var _ = require('./utils')._; +var schemes = require('./schemes/schemes.js'); +var formats = require('./formats/formats.js'); + +module.exports = (function () { + var SUPPORTED_HASH_ALGORITHMS = { + browser: ['md5', 'ripemd160', 'sha1', 'sha256', 'sha512'] + }; + + var DEFAULT_ENCRYPTION_SCHEME = 'pkcs1'; + var DEFAULT_SIGNING_SCHEME = 'pkcs1'; + + var EXPORT_FORMAT_ALIASES = { + 'private': 'pkcs1-private-pem', + 'private-der': 'pkcs1-private-der', + 'public': 'pkcs8-public-pem', + 'public-der': 'pkcs8-public-der', + }; + + /** + * @param key {string|buffer|object} Key in PEM format, or data for generate key {b: bits, e: exponent} + * @constructor + */ + function NodeRSA(key, format, options) { + if (!(this instanceof NodeRSA)) { + return new NodeRSA(key, format, options); + } + + if (_.isObject(format)) { + options = format; + format = undefined; + } + + this.$options = { + signingScheme: DEFAULT_SIGNING_SCHEME, + signingSchemeOptions: { + hash: 'sha256', + saltLength: null + }, + encryptionScheme: DEFAULT_ENCRYPTION_SCHEME, + encryptionSchemeOptions: { + hash: 'sha1', + label: null + }, + environment: 'browser', + rsaUtils: this + }; + this.keyPair = new rsa.Key(); + this.$cache = {}; + + if (Buffer.isBuffer(key) || _.isString(key)) { + this.importKey(key, format); + } else if (_.isObject(key)) { + this.generateKeyPair(key.b, key.e); + } + + this.setOptions(options); + } + + /** + * Set and validate options for key instance + * @param options + */ + NodeRSA.prototype.setOptions = function (options) { + options = options || {}; + if (options.environment) { + this.$options.environment = options.environment; + } + + if (options.signingScheme) { + if (_.isString(options.signingScheme)) { + var signingScheme = options.signingScheme.toLowerCase().split('-'); + if (signingScheme.length == 1) { + if (SUPPORTED_HASH_ALGORITHMS.node.indexOf(signingScheme[0]) > -1) { + this.$options.signingSchemeOptions = { + hash: signingScheme[0] + }; + this.$options.signingScheme = DEFAULT_SIGNING_SCHEME; + } else { + this.$options.signingScheme = signingScheme[0]; + this.$options.signingSchemeOptions = { + hash: null + }; + } + } else { + this.$options.signingSchemeOptions = { + hash: signingScheme[1] + }; + this.$options.signingScheme = signingScheme[0]; + } + } else if (_.isObject(options.signingScheme)) { + this.$options.signingScheme = options.signingScheme.scheme || DEFAULT_SIGNING_SCHEME; + this.$options.signingSchemeOptions = _.omit(options.signingScheme, 'scheme'); + } + + if (!schemes.isSignature(this.$options.signingScheme)) { + throw Error('Unsupported signing scheme'); + } + + if (this.$options.signingSchemeOptions.hash && + SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.signingSchemeOptions.hash) === -1) { + throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); + } + } + + if (options.encryptionScheme) { + if (_.isString(options.encryptionScheme)) { + this.$options.encryptionScheme = options.encryptionScheme.toLowerCase(); + this.$options.encryptionSchemeOptions = {}; + } else if (_.isObject(options.encryptionScheme)) { + this.$options.encryptionScheme = options.encryptionScheme.scheme || DEFAULT_ENCRYPTION_SCHEME; + this.$options.encryptionSchemeOptions = _.omit(options.encryptionScheme, 'scheme'); + } + + if (!schemes.isEncryption(this.$options.encryptionScheme)) { + throw Error('Unsupported encryption scheme'); + } + + if (this.$options.encryptionSchemeOptions.hash && + SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.encryptionSchemeOptions.hash) === -1) { + throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); + } + } + + this.keyPair.setOptions(this.$options); + }; + + /** + * Importing key + * @param keyData {string|buffer|Object} + * @param format {string} + */ + NodeRSA.prototype.importKey = function (keyData, format) { + if (!keyData) { + throw Error("Empty key given"); + } + + if (format) { + format = EXPORT_FORMAT_ALIASES[format] || format; + } + + if (!formats.detectAndImport(this.keyPair, keyData, format) && format === undefined) { + throw Error("Key format must be specified"); + } + + this.$cache = {}; + + return this; + }; + + /** + * Check if key pair contains private key + */ + NodeRSA.prototype.isPrivate = function () { + return this.keyPair.isPrivate(); + }; + + /** + * Check if key pair contains public key + * @param [strict] {boolean} - public key only, return false if have private exponent + */ + NodeRSA.prototype.isPublic = function (strict) { + return this.keyPair.isPublic(strict); + }; + + /** + * Check if key pair doesn't contains any data + */ + NodeRSA.prototype.isEmpty = function (strict) { + return !(this.keyPair.n || this.keyPair.e || this.keyPair.d); + }; + + /** + * Signing data + * + * @param buffer {string|number|object|array|Buffer} - data for signing. Object and array will convert to JSON string. + * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. + * @param source_encoding {string} - optional. Encoding for given string. Default utf8. + * @returns {string|Buffer} + */ + NodeRSA.prototype.sign = function (buffer, encoding, source_encoding) { + if (!this.isPrivate()) { + throw Error("This is not private key"); + } + + var res = this.keyPair.sign(this.$getDataForEncrypt(buffer, source_encoding)); + + if (encoding && encoding != 'buffer') { + res = res.toString(encoding); + } + + return res; + }; + + /** + * Preparing given data for encrypting/signing. Just make new/return Buffer object. + * + * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. + * @param encoding {string} - optional. Encoding for given string. Default utf8. + * @returns {Buffer} + */ + NodeRSA.prototype.$getDataForEncrypt = function (buffer, encoding) { + if (_.isString(buffer) || _.isNumber(buffer)) { + return Buffer.from('' + buffer, encoding || 'utf8'); + } else if (Buffer.isBuffer(buffer)) { + return buffer; + } else if (_.isObject(buffer)) { + return Buffer.from(JSON.stringify(buffer)); + } else { + throw Error("Unexpected data type"); + } + }; + + return NodeRSA; +})(); + +}).call(this,require("buffer").Buffer) +},{"./formats/formats.js":146,"./libs/rsa.js":150,"./schemes/schemes.js":152,"./utils":153,"buffer":180}],140:[function(require,module,exports){ +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + + newInvalidAsn1Error: function (msg) { + var e = new Error(); + e.name = 'InvalidAsn1Error'; + e.message = msg || ''; + return e; + } + +}; + +},{}],141:[function(require,module,exports){ +// Copyright 2011 Mark Cavage All rights reserved. + +var errors = require('./errors'); +var types = require('./types'); + +var Reader = require('./reader'); + +// --- Exports + +module.exports = { + Reader: Reader, +}; + +for (var t in types) { + if (types.hasOwnProperty(t)) + module.exports[t] = types[t]; +} +for (var e in errors) { + if (errors.hasOwnProperty(e)) + module.exports[e] = errors[e]; +} + +},{"./errors":140,"./reader":142,"./types":143}],142:[function(require,module,exports){ +(function (Buffer){ +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = require('assert'); +// var Buffer = require('safer-buffer').Buffer; + +var ASN1 = require('./types'); +var errors = require('./errors'); + + +// --- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + + + +// --- API + +function Reader(data) { + if (!data || !Buffer.isBuffer(data)) + throw new TypeError('data must be a node Buffer'); + + this._buf = data; + this._size = data.length; + + // These hold the "current" state + this._len = 0; + this._offset = 0; +} + +Object.defineProperty(Reader.prototype, 'length', { + enumerable: true, + get: function () { return (this._len); } +}); + +Object.defineProperty(Reader.prototype, 'offset', { + enumerable: true, + get: function () { return (this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'remain', { + get: function () { return (this._size - this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'buffer', { + get: function () { return (this._buf.slice(this._offset)); } +}); + + +/** + * Reads a single byte and advances offset; you can pass in `true` to make this + * a "peek" operation (i.e., get the byte, but don't advance the offset). + * + * @param {Boolean} peek true means don't move offset. + * @return {Number} the next byte, null if not enough data. + */ +Reader.prototype.readByte = function (peek) { + if (this._size - this._offset < 1) + return null; + + var b = this._buf[this._offset] & 0xff; + + if (!peek) + this._offset += 1; + + return b; +}; + + +Reader.prototype.peek = function () { + return this.readByte(true); +}; + + +/** + * Reads a (potentially) variable length off the BER buffer. This call is + * not really meant to be called directly, as callers have to manipulate + * the internal buffer afterwards. + * + * As a result of this call, you can call `Reader.length`, until the + * next thing called that does a readLength. + * + * @return {Number} the amount of offset to advance the buffer. + * @throws {InvalidAsn1Error} on bad ASN.1 + */ +Reader.prototype.readLength = function (offset) { + if (offset === undefined) + offset = this._offset; + + if (offset >= this._size) + return null; + + var lenB = this._buf[offset++] & 0xff; + if (lenB === null) + return null; + + if ((lenB & 0x80) === 0x80) { + lenB &= 0x7f; + + if (lenB === 0) + throw newInvalidAsn1Error('Indefinite length not supported'); + + if (lenB > 4) + throw newInvalidAsn1Error('encoding too long'); + + if (this._size - offset < lenB) + return null; + + this._len = 0; + for (var i = 0; i < lenB; i++) + this._len = (this._len << 8) + (this._buf[offset++] & 0xff); + + } else { + // Wasn't a variable length + this._len = lenB; + } + + return offset; +}; + + +/** + * Parses the next sequence in this BER buffer. + * + * To get the length of the sequence, call `Reader.length`. + * + * @return {Number} the sequence's tag. + */ +Reader.prototype.readSequence = function (tag) { + var seq = this.peek(); + if (seq === null) + return null; + if (tag !== undefined && tag !== seq) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + seq.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + this._offset = o; + return seq; +}; + + +Reader.prototype.readInt = function () { + return this._readTag(ASN1.Integer); +}; + + +Reader.prototype.readBoolean = function () { + return (this._readTag(ASN1.Boolean) === 0 ? false : true); +}; + + +Reader.prototype.readEnumeration = function () { + return this._readTag(ASN1.Enumeration); +}; + + +Reader.prototype.readString = function (tag, retbuf) { + if (!tag) + tag = ASN1.OctetString; + + var b = this.peek(); + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + + if (o === null) + return null; + + if (this.length > this._size - o) + return null; + + this._offset = o; + + if (this.length === 0) + return retbuf ? Buffer.alloc(0) : ''; + + var str = this._buf.slice(this._offset, this._offset + this.length); + this._offset += this.length; + + return retbuf ? str : str.toString('utf8'); +}; + +Reader.prototype.readOID = function (tag) { + if (!tag) + tag = ASN1.OID; + + var b = this.readString(tag, true); + if (b === null) + return null; + + var values = []; + var value = 0; + + for (var i = 0; i < b.length; i++) { + var byte = b[i] & 0xff; + + value <<= 7; + value += byte & 0x7f; + if ((byte & 0x80) === 0) { + values.push(value); + value = 0; + } + } + + value = values.shift(); + values.unshift(value % 40); + values.unshift((value / 40) >> 0); + + return values.join('.'); +}; + + +Reader.prototype._readTag = function (tag) { + assert.ok(tag !== undefined); + + var b = this.peek(); + + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + if (this.length > 4) + throw newInvalidAsn1Error('Integer too long: ' + this.length); + + if (this.length > this._size - o) + return null; + this._offset = o; + + var fb = this._buf[this._offset]; + var value = 0; + + for (var i = 0; i < this.length; i++) { + value <<= 8; + value |= (this._buf[this._offset++] & 0xff); + } + + if ((fb & 0x80) === 0x80 && i !== 4) + value -= (1 << (i * 8)); + + return value >> 0; +}; + + + +// --- Exported API + +module.exports = Reader; + +}).call(this,require("buffer").Buffer) +},{"./errors":140,"./types":143,"assert":175,"buffer":180}],143:[function(require,module,exports){ +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + EOC: 0, + Boolean: 1, + Integer: 2, + BitString: 3, + OctetString: 4, + Null: 5, + OID: 6, + ObjectDescriptor: 7, + External: 8, + Real: 9, // float + Enumeration: 10, + PDV: 11, + Utf8String: 12, + RelativeOID: 13, + Sequence: 16, + Set: 17, + NumericString: 18, + PrintableString: 19, + T61String: 20, + VideotexString: 21, + IA5String: 22, + UTCTime: 23, + GeneralizedTime: 24, + GraphicString: 25, + VisibleString: 26, + GeneralString: 28, + UniversalString: 29, + CharacterString: 30, + BMPString: 31, + Constructor: 32, + Context: 128 +}; + +},{}],144:[function(require,module,exports){ +// Copyright 2011 Mark Cavage All rights reserved. + +// If you have no idea what ASN.1 or BER is, see this: +// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc + +var Ber = require('./ber/index'); + + + +// --- Exported API + +module.exports = { + + Ber: Ber, + + BerReader: Ber.Reader, +}; + +},{"./ber/index":141}],145:[function(require,module,exports){ +var _ = require('../utils')._; +var utils = require('../utils'); + +module.exports = { + privateExport: function (key, options) { + return { + n: key.n.toBuffer(), + e: key.e, + d: key.d.toBuffer(), + p: key.p.toBuffer(), + q: key.q.toBuffer(), + dmp1: key.dmp1.toBuffer(), + dmq1: key.dmq1.toBuffer(), + coeff: key.coeff.toBuffer() + }; + }, + + privateImport: function (key, data, options) { + if (data.n && data.e && data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { + key.setPrivate( + data.n, + data.e, + data.d, + data.p, + data.q, + data.dmp1, + data.dmq1, + data.coeff + ); + } else { + throw Error("Invalid key data"); + } + }, + + publicExport: function (key, options) { + return { + n: key.n.toBuffer(), + e: key.e + }; + }, + + publicImport: function (key, data, options) { + if (data.n && data.e) { + key.setPublic( + data.n, + data.e + ); + } else { + throw Error("Invalid key data"); + } + }, + + /** + * Trying autodetect and import key + * @param key + * @param data + */ + autoImport: function (key, data) { + if (data.n && data.e) { + if (data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { + module.exports.privateImport(key, data); + return true; + } else { + module.exports.publicImport(key, data); + return true; + } + } + + return false; + } +}; + +},{"../utils":153}],146:[function(require,module,exports){ +var _ = require('../utils')._; + +module.exports = { + pkcs1: require('./pkcs1'), + pkcs8: require('./pkcs8'), + components: require('./components'), + + detectAndImport: function (key, data, format) { + if (format === undefined) { + for (var scheme in module.exports) { + if (typeof module.exports[scheme].autoImport === 'function' && module.exports[scheme].autoImport(key, data)) { + return true; + } + } + } else if (format) { + var fmt = formatParse(format); + + if (module.exports[fmt.scheme]) { + if (fmt.keyType === 'private') { + module.exports[fmt.scheme].privateImport(key, data, fmt.keyOpt); + } else { + module.exports[fmt.scheme].publicImport(key, data, fmt.keyOpt); + } + } else { + throw Error('Unsupported key format'); + } + } + + return false; + }, +}; + +},{"../utils":153,"./components":145,"./pkcs1":147,"./pkcs8":148}],147:[function(require,module,exports){ +(function (Buffer){ +var ber = require('../asn1/index').Ber; +var _ = require('../utils')._; +var utils = require('../utils'); + +const PRIVATE_OPENING_BOUNDARY = '-----BEGIN RSA PRIVATE KEY-----'; +const PRIVATE_CLOSING_BOUNDARY = '-----END RSA PRIVATE KEY-----'; + +const PUBLIC_OPENING_BOUNDARY = '-----BEGIN RSA PUBLIC KEY-----'; +const PUBLIC_CLOSING_BOUNDARY = '-----END RSA PUBLIC KEY-----'; + +module.exports = { + privateExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var d = key.d.toBuffer(); + var p = key.p.toBuffer(); + var q = key.q.toBuffer(); + var dmp1 = key.dmp1.toBuffer(); + var dmq1 = key.dmq1.toBuffer(); + var coeff = key.coeff.toBuffer(); + + var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic + var writer = new ber.Writer({size: length}); + + writer.startSequence(); + writer.writeInt(0); + writer.writeBuffer(n, 2); + writer.writeInt(key.e); + writer.writeBuffer(d, 2); + writer.writeBuffer(p, 2); + writer.writeBuffer(q, 2); + writer.writeBuffer(dmp1, 2); + writer.writeBuffer(dmq1, 2); + writer.writeBuffer(coeff, 2); + writer.endSequence(); + + if (options.type === 'der') { + return writer.buffer; + } else { + return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; + } + }, + + privateImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } else { + throw Error('Unsupported key format'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var reader = new ber.Reader(buffer); + reader.readSequence(); + reader.readString(2, true); // just zero + key.setPrivate( + reader.readString(2, true), // modulus + reader.readString(2, true), // publicExponent + reader.readString(2, true), // privateExponent + reader.readString(2, true), // prime1 + reader.readString(2, true), // prime2 + reader.readString(2, true), // exponent1 -- d mod (p1) + reader.readString(2, true), // exponent2 -- d mod (q-1) + reader.readString(2, true) // coefficient -- (inverse of q) mod p + ); + }, + + publicExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var length = n.length + 512; // magic + + var bodyWriter = new ber.Writer({size: length}); + bodyWriter.startSequence(); + bodyWriter.writeBuffer(n, 2); + bodyWriter.writeInt(key.e); + bodyWriter.endSequence(); + + if (options.type === 'der') { + return bodyWriter.buffer; + } else { + return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; + } + }, + + publicImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var body = new ber.Reader(buffer); + body.readSequence(); + key.setPublic( + body.readString(0x02, true), // modulus + body.readString(0x02, true) // publicExponent + ); + }, + + /** + * Trying autodetect and import key + * @param key + * @param data + */ + autoImport: function (key, data) { + // [\S\s]* matches zero or more of any character + if (/^[\S\s]*-----BEGIN RSA PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PRIVATE KEY-----[\S\s]*$/g.test(data)) { + module.exports.privateImport(key, data); + return true; + } + + if (/^[\S\s]*-----BEGIN RSA PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PUBLIC KEY-----[\S\s]*$/g.test(data)) { + module.exports.publicImport(key, data); + return true; + } + + return false; + } +}; + +}).call(this,require("buffer").Buffer) +},{"../asn1/index":144,"../utils":153,"buffer":180}],148:[function(require,module,exports){ +(function (Buffer){ +var ber = require('../asn1/index').Ber; +var _ = require('../utils')._; +var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1'; +var utils = require('../utils'); + +const PRIVATE_OPENING_BOUNDARY = '-----BEGIN PRIVATE KEY-----'; +const PRIVATE_CLOSING_BOUNDARY = '-----END PRIVATE KEY-----'; + +const PUBLIC_OPENING_BOUNDARY = '-----BEGIN PUBLIC KEY-----'; +const PUBLIC_CLOSING_BOUNDARY = '-----END PUBLIC KEY-----'; + +module.exports = { + privateExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var d = key.d.toBuffer(); + var p = key.p.toBuffer(); + var q = key.q.toBuffer(); + var dmp1 = key.dmp1.toBuffer(); + var dmq1 = key.dmq1.toBuffer(); + var coeff = key.coeff.toBuffer(); + + var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic + var bodyWriter = new ber.Writer({size: length}); + + bodyWriter.startSequence(); + bodyWriter.writeInt(0); + bodyWriter.writeBuffer(n, 2); + bodyWriter.writeInt(key.e); + bodyWriter.writeBuffer(d, 2); + bodyWriter.writeBuffer(p, 2); + bodyWriter.writeBuffer(q, 2); + bodyWriter.writeBuffer(dmp1, 2); + bodyWriter.writeBuffer(dmq1, 2); + bodyWriter.writeBuffer(coeff, 2); + bodyWriter.endSequence(); + + var writer = new ber.Writer({size: length}); + writer.startSequence(); + writer.writeInt(0); + writer.startSequence(); + writer.writeOID(PUBLIC_RSA_OID); + writer.writeNull(); + writer.endSequence(); + writer.writeBuffer(bodyWriter.buffer, 4); + writer.endSequence(); + + if (options.type === 'der') { + return writer.buffer; + } else { + return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; + } + }, + + privateImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) + .replace('-----END PRIVATE KEY-----', '') + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } else { + throw Error('Unsupported key format'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var reader = new ber.Reader(buffer); + reader.readSequence(); + reader.readInt(0); + var header = new ber.Reader(reader.readString(0x30, true)); + + if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { + throw Error('Invalid Public key format'); + } + + var body = new ber.Reader(reader.readString(0x04, true)); + body.readSequence(); + body.readString(2, true); // just zero + key.setPrivate( + body.readString(2, true), // modulus + body.readString(2, true), // publicExponent + body.readString(2, true), // privateExponent + body.readString(2, true), // prime1 + body.readString(2, true), // prime2 + body.readString(2, true), // exponent1 -- d mod (p1) + body.readString(2, true), // exponent2 -- d mod (q-1) + body.readString(2, true) // coefficient -- (inverse of q) mod p + ); + }, + + publicExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var length = n.length + 512; // magic + + var bodyWriter = new ber.Writer({size: length}); + bodyWriter.writeByte(0); + bodyWriter.startSequence(); + bodyWriter.writeBuffer(n, 2); + bodyWriter.writeInt(key.e); + bodyWriter.endSequence(); + + var writer = new ber.Writer({size: length}); + writer.startSequence(); + writer.startSequence(); + writer.writeOID(PUBLIC_RSA_OID); + writer.writeNull(); + writer.endSequence(); + writer.writeBuffer(bodyWriter.buffer, 3); + writer.endSequence(); + + if (options.type === 'der') { + return writer.buffer; + } else { + return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; + } + }, + + publicImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var reader = new ber.Reader(buffer); + reader.readSequence(); + var header = new ber.Reader(reader.readString(0x30, true)); + + if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { + throw Error('Invalid Public key format'); + } + + var body = new ber.Reader(reader.readString(0x03, true)); + body.readByte(); + body.readSequence(); + key.setPublic( + body.readString(0x02, true), // modulus + body.readString(0x02, true) // publicExponent + ); + }, + + /** + * Trying autodetect and import key + * @param key + * @param data + */ + autoImport: function (key, data) { + if (/^[\S\s]*-----BEGIN PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PRIVATE KEY-----[\S\s]*$/g.test(data)) { + module.exports.privateImport(key, data); + return true; + } + + if (/^[\S\s]*-----BEGIN PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PUBLIC KEY-----[\S\s]*$/g.test(data)) { + module.exports.publicImport(key, data); + return true; + } + + return false; + } +}; + +}).call(this,require("buffer").Buffer) +},{"../asn1/index":144,"../utils":153,"buffer":180}],149:[function(require,module,exports){ +(function (Buffer){ +/* + * Basic JavaScript BN library - subset useful for RSA encryption. + * + * Copyright (c) 2003-2005 Tom Wu + * ~BSD license~ + * + * Added Node.js Buffers support + * 2014 rzcoder + * + * Deleted some stuff to save bytes + * frosty00 + */ + +var _ = require('../utils')._; + +// Bits per digit +var dbits; + +// (public) Constructor +function BigInteger(a, b) { + if (a != null) { + if ("number" == typeof a) { + this.fromNumber(a, b); + } else if (Buffer.isBuffer(a)) { + this.fromBuffer(a); + } else if (b == null && "string" != typeof a) { + this.fromByteArray(a); + } else { + this.fromString(a, b); + } + } +} + +// return new, unset BigInteger +function nbi() { + return new BigInteger(null); +} + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i, x, w, j, c, n) { + var xl = x & 0x3fff, xh = x >> 14; + while (--n >= 0) { + var l = this[i] & 0x3fff; + var h = this[i++] >> 14; + var m = xh * l + h * xl; + l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; + c = (l >> 28) + (m >> 14) + xh * h; + w[j++] = l & 0xfffffff; + } + return c; +} + +// We need to select the fastest one that works in this environment. +//if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) { +// BigInteger.prototype.am = am2; +// dbits = 30; +//} else if (j_lm && (navigator.appName != "Netscape")) { +// BigInteger.prototype.am = am1; +// dbits = 26; +//} else { // Mozilla/Netscape seems to prefer am3 +// BigInteger.prototype.am = am3; +// dbits = 28; +//} + +// For node.js, we pick am3 with max dbits to 28. +BigInteger.prototype.am = am3; +dbits = 28; + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1 << dbits) - 1); +BigInteger.prototype.DV = (1 << dbits); + +var BI_FP = 52; +BigInteger.prototype.FV = Math.pow(2, BI_FP); +BigInteger.prototype.F1 = BI_FP - dbits; +BigInteger.prototype.F2 = 2 * dbits - BI_FP; + +// Digit conversions +var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; +var BI_RC = new Array(); +var rr, vv; +rr = "0".charCodeAt(0); +for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; +rr = "a".charCodeAt(0); +for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; +rr = "A".charCodeAt(0); +for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; + +function int2char(n) { + return BI_RM.charAt(n); +} +function intAt(s, i) { + var c = BI_RC[s.charCodeAt(i)]; + return (c == null) ? -1 : c; +} + +// (protected) copy this to r +function bnpCopyTo(r) { + for (var i = this.t - 1; i >= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x < 0) ? -1 : 0; + if (x > 0) this[0] = x; + else if (x < -1) this[0] = x + DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { + var r = nbi(); + r.fromInt(i); + return r; +} + +// (protected) set from string and radix +function bnpFromString(data, radix, unsigned) { + var k; + switch (radix) { + case 2: + k = 1; + break; + case 4: + k = 2; + break; + case 8: + k = 3; + break; + case 16: + k = 4; + break; + case 32: + k = 5; + break; + case 256: + k = 8; + break; + default: + this.fromRadix(data, radix); + return; + } + + this.t = 0; + this.s = 0; + + var i = data.length; + var mi = false; + var sh = 0; + + while (--i >= 0) { + var x = (k == 8) ? data[i] & 0xff : intAt(data, i); + if (x < 0) { + if (data.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if (sh === 0) + this[this.t++] = x; + else if (sh + k > this.DB) { + this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh; + this[this.t++] = (x >> (this.DB - sh)); + } + else + this[this.t - 1] |= x << sh; + sh += k; + if (sh >= this.DB) sh -= this.DB; + } + if ((!unsigned) && k == 8 && (data[0] & 0x80) != 0) { + this.s = -1; + if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; + } + this.clamp(); + if (mi) BigInteger.ZERO.subTo(this, this); +} + +function bnpFromByteArray(a, unsigned) { + this.fromString(a, 256, unsigned) +} + +function bnpFromBuffer(a) { + this.fromString(a, 256, true) +} + +// (protected) clamp off excess high words +function bnpClamp() { + var c = this.s & this.DM; + while (this.t > 0 && this[this.t - 1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if (this.s < 0) return "-" + this.negate().toString(b); + var k; + if (b == 16) k = 4; + else if (b == 8) k = 3; + else if (b == 2) k = 1; + else if (b == 32) k = 5; + else if (b == 4) k = 2; + else return this.toRadix(b); + var km = (1 << k) - 1, d, m = false, r = "", i = this.t; + var p = this.DB - (i * this.DB) % k; + if (i-- > 0) { + if (p < this.DB && (d = this[i] >> p) > 0) { + m = true; + r = int2char(d); + } + while (i >= 0) { + if (p < k) { + d = (this[i] & ((1 << p) - 1)) << (k - p); + d |= this[--i] >> (p += this.DB - k); + } + else { + d = (this[i] >> (p -= k)) & km; + if (p <= 0) { + p += this.DB; + --i; + } + } + if (d > 0) m = true; + if (m) r += int2char(d); + } + } + return m ? r : "0"; +} + +// (public) -this +function bnNegate() { + var r = nbi(); + BigInteger.ZERO.subTo(this, r); + return r; +} + +// (public) |this| +function bnAbs() { + return (this.s < 0) ? this.negate() : this; +} + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s - a.s; + if (r != 0) return r; + var i = this.t; + r = i - a.t; + if (r != 0) return (this.s < 0) ? -r : r; + while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if ((t = x >>> 16) != 0) { + x = t; + r += 16; + } + if ((t = x >> 8) != 0) { + x = t; + r += 8; + } + if ((t = x >> 4) != 0) { + x = t; + r += 4; + } + if ((t = x >> 2) != 0) { + x = t; + r += 2; + } + if ((t = x >> 1) != 0) { + x = t; + r += 1; + } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if (this.t <= 0) return 0; + return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n, r) { + var i; + for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i]; + for (i = n - 1; i >= 0; --i) r[i] = 0; + r.t = this.t + n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n, r) { + for (var i = n; i < this.t; ++i) r[i - n] = this[i]; + r.t = Math.max(this.t - n, 0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n, r) { + var bs = n % this.DB; + var cbs = this.DB - bs; + var bm = (1 << cbs) - 1; + var ds = Math.floor(n / this.DB), c = (this.s << bs) & this.DM, i; + for (i = this.t - 1; i >= 0; --i) { + r[i + ds + 1] = (this[i] >> cbs) | c; + c = (this[i] & bm) << bs; + } + for (i = ds - 1; i >= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t + ds + 1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n, r) { + r.s = this.s; + var ds = Math.floor(n / this.DB); + if (ds >= this.t) { + r.t = 0; + return; + } + var bs = n % this.DB; + var cbs = this.DB - bs; + var bm = (1 << bs) - 1; + r[0] = this[ds] >> bs; + for (var i = ds + 1; i < this.t; ++i) { + r[i - ds - 1] |= (this[i] & bm) << cbs; + r[i - ds] = this[i] >> bs; + } + if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs; + r.t = this.t - ds; + r.clamp(); +} + +// (protected) r = this - a +function bnpSubTo(a, r) { + var i = 0, c = 0, m = Math.min(a.t, this.t); + while (i < m) { + c += this[i] - a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + if (a.t < this.t) { + c -= a.s; + while (i < this.t) { + c += this[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while (i < a.t) { + c -= a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c < -1) r[i++] = this.DV + c; + else if (c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a, r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i + y.t; + while (--i >= 0) r[i] = 0; + for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); + r.s = 0; + r.clamp(); + if (this.s != a.s) BigInteger.ZERO.subTo(r, r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2 * x.t; + while (--i >= 0) r[i] = 0; + for (i = 0; i < x.t - 1; ++i) { + var c = x.am(i, x[i], r, 2 * i, 0, 1); + if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { + r[i + x.t] -= x.DV; + r[i + x.t + 1] = 1; + } + } + if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m, q, r) { + var pm = m.abs(); + if (pm.t <= 0) return; + var pt = this.abs(); + if (pt.t < pm.t) { + if (q != null) q.fromInt(0); + if (r != null) this.copyTo(r); + return; + } + if (r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus + if (nsh > 0) { + pm.lShiftTo(nsh, y); + pt.lShiftTo(nsh, r); + } + else { + pm.copyTo(y); + pt.copyTo(r); + } + var ys = y.t; + var y0 = y[ys - 1]; + if (y0 === 0) return; + var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0); + var d1 = this.FV / yt, d2 = (1 << this.F1) / yt, e = 1 << this.F2; + var i = r.t, j = i - ys, t = (q == null) ? nbi() : q; + y.dlShiftTo(j, t); + if (r.compareTo(t) >= 0) { + r[r.t++] = 1; + r.subTo(t, r); + } + BigInteger.ONE.dlShiftTo(ys, t); + t.subTo(y, y); // "negative" y so we can replace sub with am later + while (y.t < ys) y[y.t++] = 0; + while (--j >= 0) { + // Estimate quotient digit + var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); + if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out + y.dlShiftTo(j, t); + r.subTo(t, r); + while (r[i] < --qd) r.subTo(t, r); + } + } + if (q != null) { + r.drShiftTo(ys, q); + if (ts != ms) BigInteger.ZERO.subTo(q, q); + } + r.t = ys; + r.clamp(); + if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder + if (ts < 0) BigInteger.ZERO.subTo(r, r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a, null, r); + if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { + this.m = m; +} +function cConvert(x) { + if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { + return x; +} +function cReduce(x) { + x.divRemTo(this.m, null, x); +} +function cMulTo(x, y, r) { + x.multiplyTo(y, r); + this.reduce(r); +} +function cSqrTo(x, r) { + x.squareTo(r); + this.reduce(r); +} + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if (this.t < 1) return 0; + var x = this[0]; + if ((x & 1) === 0) return 0; + var y = x & 3; // y == 1/x mod 2^2 + y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 + y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 + y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y > 0) ? this.DV - y : -y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp & 0x7fff; + this.mph = this.mp >> 15; + this.um = (1 << (m.DB - 15)) - 1; + this.mt2 = 2 * m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t, r); + r.divRemTo(this.m, null, r); + if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while (x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for (var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i] & 0x7fff; + var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; + // use am to combine the multiply-shift-add into one call + j = i + this.m.t; + x[j] += this.m.am(0, u0, x, i, 0, this.m.t); + // propagate carry + while (x[j] >= x.DV) { + x[j] -= x.DV; + x[++j]++; + } + } + x.clamp(); + x.drShiftTo(this.m.t, x); + if (x.compareTo(this.m) >= 0) x.subTo(this.m, x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x, r) { + x.squareTo(r); + this.reduce(r); +} + +// r = "xy/R mod m"; x,y != r +function montMulTo(x, y, r) { + x.multiplyTo(y, r); + this.reduce(r); +} + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { + return ((this.t > 0) ? (this[0] & 1) : this.s) === 0; +} + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e, z) { + if (e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1; + g.copyTo(r); + while (--i >= 0) { + z.sqrTo(r, r2); + if ((e & (1 << i)) > 0) z.mulTo(r2, g, r); + else { + var t = r; + r = r2; + r2 = t; + } + } + return z.revert(r); +} + +// Copyright (c) 2005-2009 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero +// Version 1.2: square() API, isProbablePrime fix + +//(public) +function bnClone() { + var r = nbi(); + this.copyTo(r); + return r; +} + +//(public) return value as integer +function bnIntValue() { + if (this.s < 0) { + if (this.t == 1) return this[0] - this.DV; + else if (this.t === 0) return -1; + } + else if (this.t == 1) return this[0]; + else if (this.t === 0) return 0; +// assumes 16 < DB < 32 + return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0]; +} + +//(protected) return x s.t. r^x < DV +function bnpChunkSize(r) { + return Math.floor(Math.LN2 * this.DB / Math.log(r)); +} + +//(public) 0 if this === 0, 1 if this > 0 +function bnSigNum() { + if (this.s < 0) return -1; + else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +//(protected) convert to radix string +function bnpToRadix(b) { + if (b == null) b = 10; + if (this.signum() === 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b, cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d, y, z); + while (y.signum() > 0) { + r = (a + z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d, y, z); + } + return z.intValue().toString(b) + r; +} + +//(protected) convert from radix string +function bnpFromRadix(s, b) { + this.fromInt(0); + if (b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b, cs), mi = false, j = 0, w = 0; + for (var i = 0; i < s.length; ++i) { + var x = intAt(s, i); + if (x < 0) { + if (s.charAt(i) == "-" && this.signum() === 0) mi = true; + continue; + } + w = b * w + x; + if (++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w, 0); + j = 0; + w = 0; + } + } + if (j > 0) { + this.dMultiply(Math.pow(b, j)); + this.dAddOffset(w, 0); + } + if (mi) BigInteger.ZERO.subTo(this, this); +} + +//(public) convert to bigendian byte array +function bnToByteArray() { + var i = this.t, r = new Array(); + r[0] = this.s; + var p = this.DB - (i * this.DB) % 8, d, k = 0; + if (i-- > 0) { + if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) + r[k++] = d | (this.s << (this.DB - p)); + while (i >= 0) { + if (p < 8) { + d = (this[i] & ((1 << p) - 1)) << (8 - p); + d |= this[--i] >> (p += this.DB - 8); + } + else { + d = (this[i] >> (p -= 8)) & 0xff; + if (p <= 0) { + p += this.DB; + --i; + } + } + if ((d & 0x80) != 0) d |= -256; + if (k === 0 && (this.s & 0x80) != (d & 0x80)) ++k; + if (k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +/** + * return Buffer object + * @param trim {boolean} slice buffer if first element == 0 + * @returns {Buffer} + */ +function bnToBuffer(trimOrSize) { + var res = Buffer.from(this.toByteArray()); + if (trimOrSize === true && res[0] === 0) { + res = res.slice(1); + } else if (_.isNumber(trimOrSize)) { + if (res.length > trimOrSize) { + for (var i = 0; i < res.length - trimOrSize; i++) { + if (res[i] !== 0) { + return null; + } + } + return res.slice(res.length - trimOrSize); + } else if (res.length < trimOrSize) { + var padded = Buffer.alloc(trimOrSize); + padded.fill(0, 0, trimOrSize - res.length); + res.copy(padded, trimOrSize - res.length); + return padded; + } + } + return res; +} + +function bnEquals(a) { + return (this.compareTo(a) == 0); +} +function bnMin(a) { + return (this.compareTo(a) < 0) ? this : a; +} +function bnMax(a) { + return (this.compareTo(a) > 0) ? this : a; +} + +//(protected) r = this op a (bitwise) +function bnpBitwiseTo(a, op, r) { + var i, f, m = Math.min(a.t, this.t); + for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]); + if (a.t < this.t) { + f = a.s & this.DM; + for (i = m; i < this.t; ++i) r[i] = op(this[i], f); + r.t = this.t; + } + else { + f = this.s & this.DM; + for (i = m; i < a.t; ++i) r[i] = op(f, a[i]); + r.t = a.t; + } + r.s = op(this.s, a.s); + r.clamp(); +} + +//(public) this & a +function op_and(x, y) { + return x & y; +} +function bnAnd(a) { + var r = nbi(); + this.bitwiseTo(a, op_and, r); + return r; +} + +//(public) this | a +function op_or(x, y) { + return x | y; +} +function bnOr(a) { + var r = nbi(); + this.bitwiseTo(a, op_or, r); + return r; +} + +//(public) this ^ a +function op_xor(x, y) { + return x ^ y; +} +function bnXor(a) { + var r = nbi(); + this.bitwiseTo(a, op_xor, r); + return r; +} + +//(public) this & ~a +function op_andnot(x, y) { + return x & ~y; +} + +//(public) ~this +function bnNot() { + var r = nbi(); + for (var i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +//return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if (x === 0) return -1; + var r = 0; + if ((x & 0xffff) === 0) { + x >>= 16; + r += 16; + } + if ((x & 0xff) === 0) { + x >>= 8; + r += 8; + } + if ((x & 0xf) === 0) { + x >>= 4; + r += 4; + } + if ((x & 3) === 0) { + x >>= 2; + r += 2; + } + if ((x & 1) === 0) ++r; + return r; +} + +//(public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for (var i = 0; i < this.t; ++i) + if (this[i] != 0) return i * this.DB + lbit(this[i]); + if (this.s < 0) return this.t * this.DB; + return -1; +} + +//return number of 1 bits in x +function cbit(x) { + var r = 0; + while (x != 0) { + x &= x - 1; + ++r; + } + return r; +} + +//(protected) r = this + a +function bnpAddTo(a, r) { + var i = 0, c = 0, m = Math.min(a.t, this.t); + while (i < m) { + c += this[i] + a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + if (a.t < this.t) { + c += a.s; + while (i < this.t) { + c += this[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while (i < a.t) { + c += a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c > 0) r[i++] = c; + else if (c < -1) r[i++] = this.DV + c; + r.t = i; + r.clamp(); +} + +//(public) this + a +function bnAdd(a) { + var r = nbi(); + this.addTo(a, r); + return r; +} + +//(public) this - a +function bnSubtract(a) { + var r = nbi(); + this.subTo(a, r); + return r; +} + +//(public) this * a +function bnMultiply(a) { + var r = nbi(); + this.multiplyTo(a, r); + return r; +} + +// (public) this^2 +function bnSquare() { + var r = nbi(); + this.squareTo(r); + return r; +} + +//(public) this / a +function bnDivide(a) { + var r = nbi(); + this.divRemTo(a, r, null); + return r; +} + +//(public) this % a +function bnRemainder(a) { + var r = nbi(); + this.divRemTo(a, null, r); + return r; +} + +//(protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0, n - 1, this, 0, 0, this.t); + ++this.t; + this.clamp(); +} + +//(protected) this += n << w words, this >= 0 +function bnpDAddOffset(n, w) { + if (n === 0) return; + while (this.t <= w) this[this.t++] = 0; + this[w] += n; + while (this[w] >= this.DV) { + this[w] -= this.DV; + if (++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +//A "null" reducer +function NullExp() { +} +function nNop(x) { + return x; +} +function nMulTo(x, y, r) { + x.multiplyTo(y, r); +} +function nSqrTo(x, r) { + x.squareTo(r); +} + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +//(public) this^e +function bnPow(e) { + return this.exp(e, new NullExp()); +} + +//(protected) r = lower n words of "this * a", a.t <= n +//"this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a, n, r) { + var i = Math.min(this.t + a.t, n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while (i > 0) r[--i] = 0; + var j; + for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t); + for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i); + r.clamp(); +} + +//(protected) r = "this * a" without lower n words, n > 0 +//"this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a, n, r) { + --n; + var i = r.t = this.t + a.t - n; + r.s = 0; // assumes a,this >= 0 + while (--i >= 0) r[i] = 0; + for (i = Math.max(n - this.t, 0); i < a.t; ++i) + r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); + r.clamp(); + r.drShiftTo(1, r); +} + +//Barrett modular reduction +function Barrett(m) { +// setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2 * m.t, this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m); + else if (x.compareTo(this.m) < 0) return x; + else { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; + } +} + +function barrettRevert(x) { + return x; +} + +//x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t - 1, this.r2); + if (x.t > this.m.t + 1) { + x.t = this.m.t + 1; + x.clamp(); + } + this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); + this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); + while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1); + x.subTo(this.r2, x); + while (x.compareTo(this.m) >= 0) x.subTo(this.m, x); +} + +//r = x^2 mod m; x != r +function barrettSqrTo(x, r) { + x.squareTo(r); + this.reduce(r); +} + +//r = x*y mod m; x,y != r +function barrettMulTo(x, y, r) { + x.multiplyTo(y, r); + this.reduce(r); +} + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +//(public) this^e % m (HAC 14.85) +function bnModPow(e, m) { + var i = e.bitLength(), k, r = nbv(1), z; + if (i <= 0) return r; + else if (i < 18) k = 1; + else if (i < 48) k = 3; + else if (i < 144) k = 4; + else if (i < 768) k = 5; + else k = 6; + if (i < 8) + z = new Classic(m); + else if (m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + +// precomputation + var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1; + g[1] = z.convert(this); + if (k > 1) { + var g2 = nbi(); + z.sqrTo(g[1], g2); + while (n <= km) { + g[n] = nbi(); + z.mulTo(g2, g[n - 2], g[n]); + n += 2; + } + } + + var j = e.t - 1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j]) - 1; + while (j >= 0) { + if (i >= k1) w = (e[j] >> (i - k1)) & km; + else { + w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); + if (j > 0) w |= e[j - 1] >> (this.DB + i - k1); + } + + n = k; + while ((w & 1) === 0) { + w >>= 1; + --n; + } + if ((i -= n) < 0) { + i += this.DB; + --j; + } + if (is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while (n > 1) { + z.sqrTo(r, r2); + z.sqrTo(r2, r); + n -= 2; + } + if (n > 0) z.sqrTo(r, r2); else { + t = r; + r = r2; + r2 = t; + } + z.mulTo(r2, g[w], r); + } + + while (j >= 0 && (e[j] & (1 << i)) === 0) { + z.sqrTo(r, r2); + t = r; + r = r2; + r2 = t; + if (--i < 0) { + i = this.DB - 1; + --j; + } + } + } + return z.revert(r); +} + +//(public) gcd(this,a) (HAC 14.54) +function bnGCD(a) { + var x = (this.s < 0) ? this.negate() : this.clone(); + var y = (a.s < 0) ? a.negate() : a.clone(); + if (x.compareTo(y) < 0) { + var t = x; + x = y; + y = t; + } + var i = x.getLowestSetBit(), g = y.getLowestSetBit(); + if (g < 0) return x; + if (i < g) g = i; + if (g > 0) { + x.rShiftTo(g, x); + y.rShiftTo(g, y); + } + while (x.signum() > 0) { + if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x); + if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y); + if (x.compareTo(y) >= 0) { + x.subTo(y, x); + x.rShiftTo(1, x); + } + else { + y.subTo(x, y); + y.rShiftTo(1, y); + } + } + if (g > 0) y.lShiftTo(g, y); + return y; +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.fromByteArray = bnpFromByteArray; +BigInteger.prototype.fromBuffer = bnpFromBuffer; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; + +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.toBuffer = bnToBuffer; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.int2char = int2char; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); + +// JSBN-specific extension +BigInteger.prototype.square = bnSquare; + +//BigInteger interfaces not implemented in jsbn: + +//BigInteger(int signum, byte[] magnitude) +//double doubleValue() +//float floatValue() +//int hashCode() +//long longValue() +//static BigInteger valueOf(long val) + +module.exports = BigInteger; + +}).call(this,require("buffer").Buffer) +},{"../utils":153,"buffer":180}],150:[function(require,module,exports){ +/* + * RSA Encryption / Decryption with PKCS1 v2 Padding. + * + * ~MIT License~ + * Contributors: + * Tom Wu, rzcoder, frosty00 +*/ + +var _ = require('../utils')._; +var BigInteger = require('./jsbn.js'); +var utils = require('../utils.js'); +var schemes = require('../schemes/schemes.js'); + +//exports.BigInteger = BigInteger; +module.exports.Key = (function () { + /** + * RSA key constructor + * + * n - modulus + * e - publicExponent + * d - privateExponent + * p - prime1 + * q - prime2 + * dmp1 - exponent1 -- d mod (p1) + * dmq1 - exponent2 -- d mod (q-1) + * coeff - coefficient -- (inverse of q) mod p + */ + function RSAKey() { + this.n = null; + this.e = 0; + this.d = null; + this.p = null; + this.q = null; + this.dmp1 = null; + this.dmq1 = null; + this.coeff = null; + } + + RSAKey.prototype.setOptions = function (options) { + var signingSchemeProvider = schemes[options.signingScheme]; + var encryptionSchemeProvider = schemes[options.encryptionScheme]; + + if (signingSchemeProvider === encryptionSchemeProvider) { + this.signingScheme = this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); + } else { + this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); + this.signingScheme = signingSchemeProvider.makeScheme(this, options); + } + + }; + /** + * Set the private key fields N, e, d and CRT params from buffers + * + * @param N + * @param E + * @param D + * @param P + * @param Q + * @param DP + * @param DQ + * @param C + */ + RSAKey.prototype.setPrivate = function (N, E, D, P, Q, DP, DQ, C) { + if (N && E && D && N.length > 0 && (_.isNumber(E) || E.length > 0) && D.length > 0) { + this.n = new BigInteger(N); + this.e = _.isNumber(E) ? E : utils.get32IntFromBuffer(E, 0); + this.d = new BigInteger(D); + + if (P && Q && DP && DQ && C) { + this.p = new BigInteger(P); + this.q = new BigInteger(Q); + this.dmp1 = new BigInteger(DP); + this.dmq1 = new BigInteger(DQ); + this.coeff = new BigInteger(C); + } else { + // TODO: re-calculate any missing CRT params + } + this.$$recalculateCache(); + } else { + throw Error("Invalid RSA private key"); + } + }; + + /** + * private + * Perform raw private operation on "x": return x^d (mod n) + * + * @param x + * @returns {*} + */ + RSAKey.prototype.$doPrivate = function (x) { + if (this.p || this.q) { + return x.modPow(this.d, this.n); + } + + // TODO: re-calculate any missing CRT params + var xp = x.mod(this.p).modPow(this.dmp1, this.p); + var xq = x.mod(this.q).modPow(this.dmq1, this.q); + + while (xp.compareTo(xq) < 0) { + xp = xp.add(this.p); + } + return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); + }; + + RSAKey.prototype.sign = function (buffer) { + return this.signingScheme.sign.apply(this.signingScheme, arguments); + }; + + /** + * Check if key pair contains private key + */ + RSAKey.prototype.isPrivate = function () { + return this.n && this.e && this.d || false; + }; + + Object.defineProperty(RSAKey.prototype, 'keySize', { + get: function () { + return this.cache.keyBitLength; + } + }); + + Object.defineProperty(RSAKey.prototype, 'encryptedDataLength', { + get: function () { + return this.cache.keyByteLength; + } + }); + + Object.defineProperty(RSAKey.prototype, 'maxMessageLength', { + get: function () { + return this.encryptionScheme.maxMessageLength(); + } + }); + + /** + * Caching key data + */ + RSAKey.prototype.$$recalculateCache = function () { + this.cache = this.cache || {}; + // Bit & byte length + this.cache.keyBitLength = this.n.bitLength(); + this.cache.keyByteLength = (this.cache.keyBitLength + 6) >> 3; + }; + + return RSAKey; +})(); + + +},{"../schemes/schemes.js":152,"../utils":153,"../utils.js":153,"./jsbn.js":149}],151:[function(require,module,exports){ +(function (Buffer){ +/** + * PKCS1 padding and signature scheme + */ + +var BigInteger = require('../libs/jsbn'); +var CryptoJS = require('../../crypto-js/crypto-js'); +var constants = { RSA_NO_PADDING: 3 } +require('constants'); +var SIGN_INFO_HEAD = { + md2: Buffer.from('3020300c06082a864886f70d020205000410', 'hex'), + md5: Buffer.from('3020300c06082a864886f70d020505000410', 'hex'), + sha1: Buffer.from('3021300906052b0e03021a05000414', 'hex'), + sha224: Buffer.from('302d300d06096086480165030402040500041c', 'hex'), + sha256: Buffer.from('3031300d060960864801650304020105000420', 'hex'), + sha384: Buffer.from('3041300d060960864801650304020205000430', 'hex'), + sha512: Buffer.from('3051300d060960864801650304020305000440', 'hex'), + ripemd160: Buffer.from('3021300906052b2403020105000414', 'hex'), + rmd160: Buffer.from('3021300906052b2403020105000414', 'hex') +}; + +var SIGN_ALG_TO_HASH_ALIASES = { + 'ripemd160': 'rmd160' +}; + +var DEFAULT_HASH_FUNCTION = 'sha256'; + +module.exports = { + isEncryption: true, + isSignature: true +}; + +module.exports.makeScheme = function (key, options) { + function Scheme(key, options) { + this.key = key; + this.options = options; + } + + Scheme.prototype.maxMessageLength = function () { + if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { + return this.key.encryptedDataLength; + } + return this.key.encryptedDataLength - 11; + }; + + /** + * Unpad input Buffer and, if valid, return the Buffer object + * alg: PKCS#1 (type 2, random) + * @param buffer + * @returns {Buffer} + */ + Scheme.prototype.encUnPad = function (buffer, options) { + options = options || {}; + var i = 0; + + if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { + //RSA_NO_PADDING treated like JAVA left pad with zero character + var unPad; + if (typeof buffer.lastIndexOf == "function") { //patch for old node version + unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length); + } else { + unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length); + } + return unPad; + } + + if (buffer.length < 4) { + return null; + } + + /* Type 1: zeros padding for private key decrypt */ + if (options.type === 1) { + if (buffer[0] !== 0 && buffer[1] !== 1) { + return null; + } + i = 3; + while (buffer[i] !== 0) { + if (buffer[i] != 0xFF || ++i >= buffer.length) { + return null; + } + } + } else { + /* random padding for public key decrypt */ + if (buffer[0] !== 0 && buffer[1] !== 2) { + return null; + } + i = 3; + while (buffer[i] !== 0) { + if (++i >= buffer.length) { + return null; + } + } + } + return buffer.slice(i + 1, buffer.length); + }; + + Scheme.prototype.sign = function (buffer) { + var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION; + if (this.options.environment === 'browser') { + hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm; + var hasher = CryptoJS[hashAlgorithm.toUpperCase()] (buffer.toString()); + var asBuffer = wordArrayToBuffer(hasher) + var paddedHash = this.pkcs1pad(asBuffer, hashAlgorithm); + var res = this.key.$doPrivate(new BigInteger(paddedHash)).toBuffer(this.key.encryptedDataLength); + return res; + } else { + throw new Error ('CCXT only supports browser mode :P'); + } + }; + + /** + * PKCS#1 pad input buffer to max data length + * @param hashBuf + * @param hashAlgorithm + * @returns {*} + */ + Scheme.prototype.pkcs1pad = function (hashBuf, hashAlgorithm) { + var digest = SIGN_INFO_HEAD[hashAlgorithm]; + if (!digest) { + throw Error('Unsupported hash algorithm'); + } + + var data = Buffer.concat([digest, hashBuf]); + + if (data.length + 10 > this.key.encryptedDataLength) { + throw Error('Key is too short for signing algorithm (' + hashAlgorithm + ')'); + } + + var filled = Buffer.alloc(this.key.encryptedDataLength - data.length - 1); + filled.fill(0xff, 0, filled.length - 1); + filled[0] = 1; + filled[filled.length - 1] = 0; + + var res = Buffer.concat([filled, data]); + + return res; + }; + + return new Scheme(key, options); +}; + +// used to convert `CryptoJS` wordArrays into `crypto` hex buffers +function wordToByteArray(word, length) { + var ba = [], + xFF = 0xFF; + if (length > 0) + ba.push(word >>> 24); + if (length > 1) + ba.push((word >>> 16) & xFF); + if (length > 2) + ba.push((word >>> 8) & xFF); + if (length > 3) + ba.push(word & xFF); + + return ba; +} + +function wordArrayToBuffer(wordArray) { + let length = undefined; + if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) { + length = wordArray.sigBytes; + wordArray = wordArray.words; + } else { + throw Error('Argument not a wordArray') + } + + const result = [] + let bytes = [] + let i = 0; + while (length > 0) { + bytes = wordToByteArray(wordArray[i], Math.min(4, length)); + length -= bytes.length; + result.push(bytes); + i++; + } + return new Buffer.from([].concat.apply([], result), 'hex'); +} + +}).call(this,require("buffer").Buffer) +},{"../../crypto-js/crypto-js":123,"../libs/jsbn":149,"buffer":180,"constants":182}],152:[function(require,module,exports){ +module.exports = { + pkcs1: require('./pkcs1'), + + /** + * Check if scheme has padding methods + * @param scheme {string} + * @returns {Boolean} + */ + isEncryption: function (scheme) { + return module.exports[scheme] && module.exports[scheme].isEncryption; + }, + + /** + * Check if scheme has sign/verify methods + * @param scheme {string} + * @returns {Boolean} + */ + isSignature: function (scheme) { + return module.exports[scheme] && module.exports[scheme].isSignature; + } +}; + +},{"./pkcs1":151}],153:[function(require,module,exports){ +/* + * Utils functions + * + */ + +/** + * Break string str each maxLen symbols + * @param str + * @param maxLen + * @returns {string} + */ +module.exports.linebrk = function (str, maxLen) { + var res = ''; + var i = 0; + while (i + maxLen < str.length) { + res += str.substring(i, i + maxLen) + "\n"; + i += maxLen; + } + return res + str.substring(i, str.length); +}; + +/** + * Trying get a 32-bit unsigned integer from the partial buffer + * @param buffer + * @param offset + * @returns {Number} + */ +module.exports.get32IntFromBuffer = function (buffer, offset) { + offset = offset || 0; + var size = 0; + if ((size = buffer.length - offset) > 0) { + if (size >= 4) { + return buffer.readUInt32BE(offset); + } else { + var res = 0; + for (var i = offset + size, d = 0; i > offset; i--, d += 2) { + res += buffer[i - 1] * Math.pow(16, d); + } + return res; + } + } else { + return NaN; + } +}; + +module.exports._ = { + isObject: function (value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + }, + + isString: function (value) { + return typeof value == 'string' || value instanceof String; + }, + + isNumber: function (value) { + return typeof value == 'number' || !isNaN(parseFloat(value)) && isFinite(value); + }, + + /** + * Returns copy of `obj` without `removeProp` field. + * @param obj + * @param removeProp + * @returns Object + */ + omit: function (obj, removeProp) { + var newObj = {}; + for (var prop in obj) { + if (!obj.hasOwnProperty(prop) || prop === removeProp) { + continue; + } + newObj[prop] = obj[prop]; + } + + return newObj; + } +}; + +/** + * Strips everything around the opening and closing lines, including the lines + * themselves. + */ +module.exports.trimSurroundingText = function (data, opening, closing) { + var trimStartIndex = 0; + var trimEndIndex = data.length; + + var openingBoundaryIndex = data.indexOf(opening); + if (openingBoundaryIndex >= 0) { + trimStartIndex = openingBoundaryIndex + opening.length; + } + + var closingBoundaryIndex = data.indexOf(closing, openingBoundaryIndex); + if (closingBoundaryIndex >= 0) { + trimEndIndex = closingBoundaryIndex; + } + + return data.substring(trimStartIndex, trimEndIndex); +} + +},{}],154:[function(require,module,exports){ +'use strict'; + +var replace = String.prototype.replace; +var percentTwenties = /%20/g; + +module.exports = { + 'default': 'RFC3986', + formatters: { + RFC1738: function (value) { + return replace.call(value, percentTwenties, '+'); + }, + RFC3986: function (value) { + return value; + } + }, + RFC1738: 'RFC1738', + RFC3986: 'RFC3986' +}; + +},{}],155:[function(require,module,exports){ +'use strict'; + +var stringify = require('./stringify'); +var parse = require('./parse'); +var formats = require('./formats'); + +module.exports = { + formats: formats, + parse: parse, + stringify: stringify +}; + +},{"./formats":154,"./parse":156,"./stringify":157}],156:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); + +var has = Object.prototype.hasOwnProperty; + +var defaults = { + allowDots: false, + allowPrototypes: false, + arrayLimit: 20, + charset: 'utf-8', + charsetSentinel: false, + comma: false, + decoder: utils.decode, + delimiter: '&', + depth: 5, + ignoreQueryPrefix: false, + interpretNumericEntities: false, + parameterLimit: 1000, + parseArrays: true, + plainObjects: false, + strictNullHandling: false +}; + +var interpretNumericEntities = function (str) { + return str.replace(/&#(\d+);/g, function ($0, numberStr) { + return String.fromCharCode(parseInt(numberStr, 10)); + }); +}; + +// This is what browsers will submit when the ✓ character occurs in an +// application/x-www-form-urlencoded body and the encoding of the page containing +// the form is iso-8859-1, or when the submitted form has an accept-charset +// attribute of iso-8859-1. Presumably also with other charsets that do not contain +// the ✓ character, such as us-ascii. +var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓') + +// These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded. +var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓') + +var parseValues = function parseQueryStringValues(str, options) { + var obj = {}; + var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str; + var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit; + var parts = cleanStr.split(options.delimiter, limit); + var skipIndex = -1; // Keep track of where the utf8 sentinel was found + var i; + + var charset = options.charset; + if (options.charsetSentinel) { + for (i = 0; i < parts.length; ++i) { + if (parts[i].indexOf('utf8=') === 0) { + if (parts[i] === charsetSentinel) { + charset = 'utf-8'; + } else if (parts[i] === isoSentinel) { + charset = 'iso-8859-1'; + } + skipIndex = i; + i = parts.length; // The eslint settings do not allow break; + } + } + } + + for (i = 0; i < parts.length; ++i) { + if (i === skipIndex) { + continue; + } + var part = parts[i]; + + var bracketEqualsPos = part.indexOf(']='); + var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1; + + var key, val; + if (pos === -1) { + key = options.decoder(part, defaults.decoder, charset); + val = options.strictNullHandling ? null : ''; + } else { + key = options.decoder(part.slice(0, pos), defaults.decoder, charset); + val = options.decoder(part.slice(pos + 1), defaults.decoder, charset); + } + + if (val && options.interpretNumericEntities && charset === 'iso-8859-1') { + val = interpretNumericEntities(val); + } + + if (val && options.comma && val.indexOf(',') > -1) { + val = val.split(','); + } + + if (has.call(obj, key)) { + obj[key] = utils.combine(obj[key], val); + } else { + obj[key] = val; + } + } + + return obj; +}; + +var parseObject = function (chain, val, options) { + var leaf = val; + + for (var i = chain.length - 1; i >= 0; --i) { + var obj; + var root = chain[i]; + + if (root === '[]' && options.parseArrays) { + obj = [].concat(leaf); + } else { + obj = options.plainObjects ? Object.create(null) : {}; + var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root; + var index = parseInt(cleanRoot, 10); + if (!options.parseArrays && cleanRoot === '') { + obj = { 0: leaf }; + } else if ( + !isNaN(index) + && root !== cleanRoot + && String(index) === cleanRoot + && index >= 0 + && (options.parseArrays && index <= options.arrayLimit) + ) { + obj = []; + obj[index] = leaf; + } else { + obj[cleanRoot] = leaf; + } + } + + leaf = obj; + } + + return leaf; +}; + +var parseKeys = function parseQueryStringKeys(givenKey, val, options) { + if (!givenKey) { + return; + } + + // Transform dot notation to bracket notation + var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey; + + // The regex chunks + + var brackets = /(\[[^[\]]*])/; + var child = /(\[[^[\]]*])/g; + + // Get the parent + + var segment = brackets.exec(key); + var parent = segment ? key.slice(0, segment.index) : key; + + // Stash the parent if it exists + + var keys = []; + if (parent) { + // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties + if (!options.plainObjects && has.call(Object.prototype, parent)) { + if (!options.allowPrototypes) { + return; + } + } + + keys.push(parent); + } + + // Loop through children appending to the array until we hit depth + + var i = 0; + while ((segment = child.exec(key)) !== null && i < options.depth) { + i += 1; + if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) { + if (!options.allowPrototypes) { + return; + } + } + keys.push(segment[1]); + } + + // If there's a remainder, just add whatever is left + + if (segment) { + keys.push('[' + key.slice(segment.index) + ']'); + } + + return parseObject(keys, val, options); +}; + +var normalizeParseOptions = function normalizeParseOptions(opts) { + if (!opts) { + return defaults; + } + + if (opts.decoder !== null && opts.decoder !== undefined && typeof opts.decoder !== 'function') { + throw new TypeError('Decoder has to be a function.'); + } + + if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { + throw new Error('The charset option must be either utf-8, iso-8859-1, or undefined'); + } + var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset; + + return { + allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots, + allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes, + arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit, + charset: charset, + charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, + comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma, + decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder, + delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter, + depth: typeof opts.depth === 'number' ? opts.depth : defaults.depth, + ignoreQueryPrefix: opts.ignoreQueryPrefix === true, + interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities, + parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit, + parseArrays: opts.parseArrays !== false, + plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects, + strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling + }; +}; + +module.exports = function (str, opts) { + var options = normalizeParseOptions(opts); + + if (str === '' || str === null || typeof str === 'undefined') { + return options.plainObjects ? Object.create(null) : {}; + } + + var tempObj = typeof str === 'string' ? parseValues(str, options) : str; + var obj = options.plainObjects ? Object.create(null) : {}; + + // Iterate over the keys and setup the new object + + var keys = Object.keys(tempObj); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var newObj = parseKeys(key, tempObj[key], options); + obj = utils.merge(obj, newObj, options); + } + + return utils.compact(obj); +}; + +},{"./utils":158}],157:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); +var formats = require('./formats'); +var has = Object.prototype.hasOwnProperty; + +var arrayPrefixGenerators = { + brackets: function brackets(prefix) { // eslint-disable-line func-name-matching + return prefix + '[]'; + }, + comma: 'comma', + indices: function indices(prefix, key) { // eslint-disable-line func-name-matching + return prefix + '[' + key + ']'; + }, + repeat: function repeat(prefix) { // eslint-disable-line func-name-matching + return prefix; + } +}; + +var isArray = Array.isArray; +var push = Array.prototype.push; +var pushToArray = function (arr, valueOrArray) { + push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]); +}; + +var toISO = Date.prototype.toISOString; + +var defaults = { + addQueryPrefix: false, + allowDots: false, + charset: 'utf-8', + charsetSentinel: false, + delimiter: '&', + encode: true, + encoder: utils.encode, + encodeValuesOnly: false, + formatter: formats.formatters[formats['default']], + // deprecated + indices: false, + serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching + return toISO.call(date); + }, + skipNulls: false, + strictNullHandling: false +}; + +var stringify = function stringify( // eslint-disable-line func-name-matching + object, + prefix, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly, + charset +) { + var obj = object; + if (typeof filter === 'function') { + obj = filter(prefix, obj); + } else if (obj instanceof Date) { + obj = serializeDate(obj); + } else if (generateArrayPrefix === 'comma' && isArray(obj)) { + obj = obj.join(','); + } + + if (obj === null) { + if (strictNullHandling) { + return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix; + } + + obj = ''; + } + + if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) { + if (encoder) { + var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset); + return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))]; + } + return [formatter(prefix) + '=' + formatter(String(obj))]; + } + + var values = []; + + if (typeof obj === 'undefined') { + return values; + } + + var objKeys; + if (isArray(filter)) { + objKeys = filter; + } else { + var keys = Object.keys(obj); + objKeys = sort ? keys.sort(sort) : keys; + } + + for (var i = 0; i < objKeys.length; ++i) { + var key = objKeys[i]; + + if (skipNulls && obj[key] === null) { + continue; + } + + if (isArray(obj)) { + pushToArray(values, stringify( + obj[key], + typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly, + charset + )); + } else { + pushToArray(values, stringify( + obj[key], + prefix + (allowDots ? '.' + key : '[' + key + ']'), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly, + charset + )); + } + } + + return values; +}; + +var normalizeStringifyOptions = function normalizeStringifyOptions(opts) { + if (!opts) { + return defaults; + } + + if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') { + throw new TypeError('Encoder has to be a function.'); + } + + var charset = opts.charset || defaults.charset; + if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { + throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'); + } + + var format = formats['default']; + if (typeof opts.format !== 'undefined') { + if (!has.call(formats.formatters, opts.format)) { + throw new TypeError('Unknown format option provided.'); + } + format = opts.format; + } + var formatter = formats.formatters[format]; + + var filter = defaults.filter; + if (typeof opts.filter === 'function' || isArray(opts.filter)) { + filter = opts.filter; + } + + return { + addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix, + allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots, + charset: charset, + charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, + delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter, + encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode, + encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder, + encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly, + filter: filter, + formatter: formatter, + serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate, + skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls, + sort: typeof opts.sort === 'function' ? opts.sort : null, + strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling + }; +}; + +module.exports = function (object, opts) { + var obj = object; + var options = normalizeStringifyOptions(opts); + + var objKeys; + var filter; + + if (typeof options.filter === 'function') { + filter = options.filter; + obj = filter('', obj); + } else if (isArray(options.filter)) { + filter = options.filter; + objKeys = filter; + } + + var keys = []; + + if (typeof obj !== 'object' || obj === null) { + return ''; + } + + var arrayFormat; + if (opts && opts.arrayFormat in arrayPrefixGenerators) { + arrayFormat = opts.arrayFormat; + } else if (opts && 'indices' in opts) { + arrayFormat = opts.indices ? 'indices' : 'repeat'; + } else { + arrayFormat = 'indices'; + } + + var generateArrayPrefix = arrayPrefixGenerators[arrayFormat]; + + if (!objKeys) { + objKeys = Object.keys(obj); + } + + if (options.sort) { + objKeys.sort(options.sort); + } + + for (var i = 0; i < objKeys.length; ++i) { + var key = objKeys[i]; + + if (options.skipNulls && obj[key] === null) { + continue; + } + pushToArray(keys, stringify( + obj[key], + key, + generateArrayPrefix, + options.strictNullHandling, + options.skipNulls, + options.encode ? options.encoder : null, + options.filter, + options.sort, + options.allowDots, + options.serializeDate, + options.formatter, + options.encodeValuesOnly, + options.charset + )); + } + + var joined = keys.join(options.delimiter); + var prefix = options.addQueryPrefix === true ? '?' : ''; + + if (options.charsetSentinel) { + if (options.charset === 'iso-8859-1') { + // encodeURIComponent('✓'), the "numeric entity" representation of a checkmark + prefix += 'utf8=%26%2310003%3B&'; + } else { + // encodeURIComponent('✓') + prefix += 'utf8=%E2%9C%93&'; + } + } + + return joined.length > 0 ? prefix + joined : ''; +}; + +},{"./formats":154,"./utils":158}],158:[function(require,module,exports){ +'use strict'; + +var has = Object.prototype.hasOwnProperty; +var isArray = Array.isArray; + +var hexTable = (function () { + var array = []; + for (var i = 0; i < 256; ++i) { + array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); + } + + return array; +}()); + +var compactQueue = function compactQueue(queue) { + while (queue.length > 1) { + var item = queue.pop(); + var obj = item.obj[item.prop]; + + if (isArray(obj)) { + var compacted = []; + + for (var j = 0; j < obj.length; ++j) { + if (typeof obj[j] !== 'undefined') { + compacted.push(obj[j]); + } + } + + item.obj[item.prop] = compacted; + } + } +}; + +var arrayToObject = function arrayToObject(source, options) { + var obj = options && options.plainObjects ? Object.create(null) : {}; + for (var i = 0; i < source.length; ++i) { + if (typeof source[i] !== 'undefined') { + obj[i] = source[i]; + } + } + + return obj; +}; + +var merge = function merge(target, source, options) { + if (!source) { + return target; + } + + if (typeof source !== 'object') { + if (isArray(target)) { + target.push(source); + } else if (target && typeof target === 'object') { + if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) { + target[source] = true; + } + } else { + return [target, source]; + } + + return target; + } + + if (!target || typeof target !== 'object') { + return [target].concat(source); + } + + var mergeTarget = target; + if (isArray(target) && !isArray(source)) { + mergeTarget = arrayToObject(target, options); + } + + if (isArray(target) && isArray(source)) { + source.forEach(function (item, i) { + if (has.call(target, i)) { + var targetItem = target[i]; + if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { + target[i] = merge(targetItem, item, options); + } else { + target.push(item); + } + } else { + target[i] = item; + } + }); + return target; + } + + return Object.keys(source).reduce(function (acc, key) { + var value = source[key]; + + if (has.call(acc, key)) { + acc[key] = merge(acc[key], value, options); + } else { + acc[key] = value; + } + return acc; + }, mergeTarget); +}; + +var assign = function assignSingleSource(target, source) { + return Object.keys(source).reduce(function (acc, key) { + acc[key] = source[key]; + return acc; + }, target); +}; + +var decode = function (str, decoder, charset) { + var strWithoutPlus = str.replace(/\+/g, ' '); + if (charset === 'iso-8859-1') { + // unescape never throws, no try...catch needed: + return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape); + } + // utf-8 + try { + return decodeURIComponent(strWithoutPlus); + } catch (e) { + return strWithoutPlus; + } +}; + +var encode = function encode(str, defaultEncoder, charset) { + // This code was originally written by Brian White (mscdex) for the io.js core querystring library. + // It has been adapted here for stricter adherence to RFC 3986 + if (str.length === 0) { + return str; + } + + var string = typeof str === 'string' ? str : String(str); + + if (charset === 'iso-8859-1') { + return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) { + return '%26%23' + parseInt($0.slice(2), 16) + '%3B'; + }); + } + + var out = ''; + for (var i = 0; i < string.length; ++i) { + var c = string.charCodeAt(i); + + if ( + c === 0x2D // - + || c === 0x2E // . + || c === 0x5F // _ + || c === 0x7E // ~ + || (c >= 0x30 && c <= 0x39) // 0-9 + || (c >= 0x41 && c <= 0x5A) // a-z + || (c >= 0x61 && c <= 0x7A) // A-Z + ) { + out += string.charAt(i); + continue; + } + + if (c < 0x80) { + out = out + hexTable[c]; + continue; + } + + if (c < 0x800) { + out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + if (c < 0xD800 || c >= 0xE000) { + out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + i += 1; + c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); + out += hexTable[0xF0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3F)] + + hexTable[0x80 | ((c >> 6) & 0x3F)] + + hexTable[0x80 | (c & 0x3F)]; + } + + return out; +}; + +var compact = function compact(value) { + var queue = [{ obj: { o: value }, prop: 'o' }]; + var refs = []; + + for (var i = 0; i < queue.length; ++i) { + var item = queue[i]; + var obj = item.obj[item.prop]; + + var keys = Object.keys(obj); + for (var j = 0; j < keys.length; ++j) { + var key = keys[j]; + var val = obj[key]; + if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { + queue.push({ obj: obj, prop: key }); + refs.push(val); + } + } + } + + compactQueue(queue); + + return value; +}; + +var isRegExp = function isRegExp(obj) { + return Object.prototype.toString.call(obj) === '[object RegExp]'; +}; + +var isBuffer = function isBuffer(obj) { + if (!obj || typeof obj !== 'object') { + return false; + } + + return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); +}; + +var combine = function combine(a, b) { + return [].concat(a, b); +}; + +module.exports = { + arrayToObject: arrayToObject, + assign: assign, + combine: combine, + compact: compact, + decode: decode, + encode: encode, + isBuffer: isBuffer, + isRegExp: isRegExp, + merge: merge +}; + +},{}],159:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, AuthenticationError, ExchangeError, InsufficientFunds, OrderNotFound, PermissionDenied, BadRequest, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class stex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'stex', + 'name': 'STEX', // formerly known as stocks.exchange + 'countries': [ 'EE' ], // Estonia + 'rateLimit': 500, // https://help.stex.com/en/articles/2815043-api-3-rate-limits + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': false, + 'createMarketOrder': false, // limit orders only + 'fetchCurrencies': true, + 'fetchMarkets': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchOrderBook': true, + 'fetchOHLCV': true, + 'fetchBalance': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchMyTrades': true, + 'fetchOrderTrades': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'withdraw': true, + 'fetchFundingFees': true, + }, + 'version': 'v3', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/69680782-03fd0b80-10bd-11ea-909e-7f603500e9cc.jpg', + 'api': 'https://api3.stex.com', + 'www': 'https://www.stex.com', + 'doc': [ + 'https://help.stex.com/en/collections/1593608-api-v3-documentation', + ], + 'fees': 'https://app.stex.com/en/pairs-specification', + 'referral': 'https://app.stex.com?ref=36416021', + }, + 'requiredCredentials': { + 'apiKey': false, + 'secret': false, + 'token': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '30m': '30', + '1h': '60', + '4h': '240', + '12h': '720', + '1d': '1D', // default + }, + 'api': { + 'public': { + 'get': [ + 'currencies', // Available Currencies + 'currencies/{currencyId}', // Get currency info + 'markets', // Available markets + 'pairs-groups', // Available currency pairs groups (as displayed at stex trading page) + 'currency_pairs/list/{code}', // Available currency pairs + 'currency_pairs/group/{currencyPairGroupId}', // Available currency pairs for a given group + 'currency_pairs/{currencyPairId}', // Get currency pair information + 'ticker', // Tickers list for all currency pairs + 'ticker/{currencyPairId}', // Ticker for currency pair + 'trades/{currencyPairId}', // Trades for given currency pair + 'orderbook/{currencyPairId}', // Orderbook for given currency pair + 'chart/{currencyPairId}/{candlesType}', // A list of candles for given currency pair + 'deposit-statuses', // Available Deposit Statuses + 'deposit-statuses/{statusId}', // Get deposit status info + 'withdrawal-statuses', // Available Withdrawal Statuses + 'withdrawal-statuses/{statusId}', // Get status info + 'ping', // Test API is working and get server time + 'mobile-versions', // Shows the official mobile applications data + ], + }, + 'trading': { + 'get': [ + 'fees/{currencyPairId}', // Returns the user's fees for a given currency pair + 'orders', // List your currently open orders + 'orders/{currencyPairId}', // List your currently open orders for given currency pair + 'order/{orderId}', // Get a single order + ], + 'post': [ + 'orders/{currencyPairId}', // Create new order and put it to the orders processing queue + ], + 'delete': [ + 'orders', // Delete all active orders + 'orders/{currencyPairId}', // Delete active orders for given currency pair + 'order/{orderId}', // Cancel order + ], + }, + 'reports': { + 'get': [ + 'orders', // Get past orders + 'orders/{orderId}', // Get specified order details + 'trades/{currencyPairId}', // Get a list of user trades according to request parameters + 'background/{listMode}', // Get reports list for category + 'background/{id}', // Get some report info + 'background/download/{id}', // Get file by id + ], + 'post': [ + 'background/create', // Create new report + ], + 'delete': [ + 'background/{id}', // Remove report by id + ], + }, + 'profile': { + 'get': [ + 'info', // Account information + 'wallets', // Get a list of user wallets + 'wallets/{walletId}', // Single wallet information + 'wallets/address/{walletId}', // Get deposit address for given wallet + 'deposits', // Get a list of deposits made by user + 'deposits/{id}', // Get deposit by id + 'withdrawals', // Get a list of withdrawals made by user + 'withdrawals/{id}', // Get withdrawal by id + 'notifications', // Get notifications + 'favorite/currency_pairs', // Get favorite currency pairs + 'token-scopes', // Get current token scopes + ], + 'post': [ + 'wallets/burn/{walletId}', // Burns the given wallet + 'wallets/{currencyId}', // Create a wallet for given currency + 'wallets/address/{walletId}', // Create new deposit address + 'withdraw', // Create withdrawal request + 'referral/program', // Create referral program + 'referral/insert/{code}', // Insert referral code + 'referral/bonus_transfer/{currencyId}', // Transfer referral bonuses balance to main balance for given currency + ], + 'put': [ + 'profile/favorite/currency_pairs/set', // Set favorite currency pairs + ], + 'delete': [ + 'profile/withdraw/{withdrawalId}', // Cancel unconfirmed withdrawal + ], + }, + 'verification': { + 'get': [ + 'verification/countries', // Countries list, beta + 'verification/stex', // Get information about your KYC, beta + ], + 'post': [ + 'verification/stex', // Update information regarding of your KYC verification, beta + ], + }, + 'settings': { + 'get': [ + 'notifications/{event}', // User event notification settings + 'notifications', // User events notification settings + ], + 'put': [ + 'notifications', // Set notification settings + 'notifications/set', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.002, + 'maker': 0.002, + }, + }, + 'options': { + 'parseOrderToPrecision': false, + }, + 'exceptions': { + 'exact': { + // {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}} + // {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}} + 'Wrong parameters': BadRequest, + 'Unauthenticated.': AuthenticationError, // {"message":"Unauthenticated."} + 'Server Error': ExchangeError, // { "message": "Server Error" } + 'This feature is only enabled for users verifies by Cryptonomica': PermissionDenied, // {"success":false,"message":"This feature is only enabled for users verifies by Cryptonomica"} + 'Too Many Attempts.': DDoSProtection, // { "message": "Too Many Attempts." } + }, + 'broad': { + 'Not enough': InsufficientFunds, // {"success":false,"message":"Not enough ETH"} + }, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // "success":true, + // "data":[ + // { + // "id":1, + // "code":"BTC", + // "name":"Bitcoin", + // "active":true, + // "delisted":false, + // "precision":8, + // "minimum_tx_confirmations":1, + // "minimum_withdrawal_amount":"0.00200000", + // "minimum_deposit_amount":"0.00000000", + // "deposit_fee_currency_id":1, + // "deposit_fee_currency_code":"BTC", + // "deposit_fee_const":"0.00000000", + // "deposit_fee_percent":"0.00000000", + // "withdrawal_fee_currency_id":1, + // "withdrawal_fee_currency_code":"BTC", + // "withdrawal_fee_const":"0.00100000", + // "withdrawal_fee_percent":"0.00000000", + // "block_explorer_url":"https:\/\/blockchain.info\/tx\/", + // "protocol_specific_settings":null + // }, + // ] + // } + // + const result = {}; + const currencies = this.safeValue (response, 'data', []); + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'id'); + const numericId = this.safeInteger (currency, 'id'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (this.safeString (currency, 'code')); + const precision = this.safeInteger (currency, 'precision'); + const fee = this.safeFloat (currency, 'withdrawal_fee_const'); // todo: redesign + const active = this.safeValue (currency, 'active', true); + result[code] = { + 'id': id, + 'numericId': numericId, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': this.safeString (currency, 'name'), + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { 'min': Math.pow (10, -precision), 'max': undefined }, + 'price': { 'min': Math.pow (10, -precision), 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'deposit': { + 'min': this.safeFloat (currency, 'minimum_deposit_amount'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimum_withdrawal_amount'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const request = { + 'code': 'ALL', + }; + const response = await this.publicGetCurrencyPairsListCode (this.extend (request, params)); + // + // { + // "success":true, + // "data":[ + // { + // "id":935, + // "currency_id":662, + // "currency_code":"ABET", + // "currency_name":"Altbet", + // "market_currency_id":1, + // "market_code":"BTC", + // "market_name":"Bitcoin", + // "min_order_amount":"0.00000010", + // "min_buy_price":"0.00000001", + // "min_sell_price":"0.00000001", + // "buy_fee_percent":"0.20000000", + // "sell_fee_percent":"0.20000000", + // "active":true, + // "delisted":false, + // "pair_message":"", + // "currency_precision":8, + // "market_precision":8, + // "symbol":"ABET_BTC", + // "group_name":"BTC", + // "group_id":1 + // } + // ] + // } + // + const result = []; + const markets = this.safeValue (response, 'data', []); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const numericId = this.safeInteger (market, 'id'); + const baseId = this.safeString (market, 'currency_id'); + const quoteId = this.safeString (market, 'market_currency_id'); + const baseNumericId = this.safeInteger (market, 'currency_id'); + const quoteNumericId = this.safeInteger (market, 'market_currency_id'); + const base = this.safeCurrencyCode (this.safeString (market, 'currency_code')); + const quote = this.safeCurrencyCode (this.safeString (market, 'market_code')); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'currency_precision'), + 'price': this.safeInteger (market, 'market_precision'), + }; + const active = this.safeValue (market, 'active'); + const minBuyPrice = this.safeFloat (market, 'min_buy_price'); + const minSellPrice = this.safeFloat (market, 'min_sell_price'); + const minPrice = Math.max (minBuyPrice, minSellPrice); + const buyFee = this.safeFloat (market, 'buy_fee_percent') / 100; + const sellFee = this.safeFloat (market, 'sell_fee_percent') / 100; + const fee = Math.max (buyFee, sellFee); + result.push ({ + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'baseNumericId': baseNumericId, + 'quoteNumericId': quoteNumericId, + 'info': market, + 'active': active, + 'maker': fee, + 'taker': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'min_order_amount'), + 'max': undefined, + }, + 'price': { 'min': minPrice, 'max': undefined }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + }; + const response = await this.publicGetTickerCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 2, + // "amount_multiplier": 1, + // "currency_code": "ETH", + // "market_code": "BTC", + // "currency_name": "Ethereum", + // "market_name": "Bitcoin", + // "symbol": "ETH_BTC", + // "group_name": "BTC", + // "group_id": 1, + // "ask": "0.02069998", + // "bid": "0.02028622", + // "last": "0.02049224", + // "open": "0.02059605", + // "low": "0.01977744", + // "high": "0.02097005", + // "volume": "480.43248971", + // "volumeQuote": "23491.29826130", + // "count": "7384", + // "fiatsRate": { + // "USD": 7230.86, + // "EUR": 6590.79, + // "UAH": 173402, + // "AUD": 10595.51, + // "IDR": 101568085, + // "CNY": 50752, + // "KRW": 8452295, + // "JPY": 784607, + // "VND": 167315119, + // "INR": 517596, + // "GBP": 5607.25, + // "CAD": 9602.63, + // "BRL": 30472, + // "RUB": 460718 + // }, + // "timestamp": 1574698235601 + // } + // } + // + const ticker = this.safeValue (response, 'data', {}); + return this.parseTicker (ticker, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + }; + if (limit !== undefined) { + request['limit_bids'] = limit; // returns all if set to 0, default 100 + request['limit_asks'] = limit; // returns all if set to 0, default 100 + } + const response = await this.publicGetOrderbookCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "ask": [ + // { "currency_pair_id": 2, "amount": "2.17865373", "price": "0.02062917", "amount2": "0.04494382", "count": 1, "cumulative_amount": 2.17865373 }, + // { "currency_pair_id": 2, "amount": "2.27521743", "price": "0.02062918", "amount2": "0.04693587", "count": 1, "cumulative_amount": 4.45387116 }, + // { "currency_pair_id": 2, "amount": "1.26980049", "price": "0.02063170", "amount2": "0.02619814", "count": 1, "cumulative_amount": 5.72367165 }, + // ], + // "bid": [ + // { "currency_pair_id": 2, "amount": "0.00978005", "price": "0.02057000", "amount2": "0.00020118", "count": 1, "cumulative_amount": 0.00978005 }, + // { "currency_pair_id": 2, "amount": "0.00500000", "price": "0.02056000", "amount2": "0.00010280", "count": 1, "cumulative_amount": 0.01478005 }, + // { "currency_pair_id": 2, "amount": "0.77679882", "price": "0.02054001", "amount2": "0.01595546", "count": 1, "cumulative_amount": 0.79157887 }, + // ], + // "ask_total_amount": 2555.749174609999, + // "bid_total_amount": 29.180037330000005 + // } + // } + // + const orderbook = this.safeValue (response, 'data', {}); + return this.parseOrderBook (orderbook, undefined, 'bid', 'ask', 'price', 'amount'); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "id": 2, + // "amount_multiplier": 1, + // "currency_code": "ETH", + // "market_code": "BTC", + // "currency_name": "Ethereum", + // "market_name": "Bitcoin", + // "symbol": "ETH_BTC", + // "group_name": "BTC", + // "group_id": 1, + // "ask": "0.02069998", + // "bid": "0.02028622", + // "last": "0.02049224", + // "open": "0.02059605", + // "low": "0.01977744", + // "high": "0.02097005", + // "volume": "480.43248971", + // "volumeQuote": "23491.29826130", + // "count": "7384", + // "fiatsRate": { + // "USD": 7230.86, + // "EUR": 6590.79, + // "UAH": 173402, + // "AUD": 10595.51, + // "IDR": 101568085, + // "CNY": 50752, + // "KRW": 8452295, + // "JPY": 784607, + // "VND": 167315119, + // "INR": 517596, + // "GBP": 5607.25, + // "CAD": 9602.63, + // "BRL": 30472, + // "RUB": 460718 + // }, + // "timestamp": 1574698235601 + // } + // + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + let marketId = this.safeString (ticker, 'id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + marketId = this.safeString (ticker, 'symbol'); + if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open'); + let change = undefined; + let percentage = undefined; + if (last !== undefined) { + if (open !== undefined) { + change = last - open; + percentage = ((100 / open) * last) - 100; + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, // previous day close + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volumeQuote'), + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + parseTickers (tickers, symbols = undefined) { + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + // + // { + // "success":true, + // "data":[ + // { + // "id":262, + // "amount_multiplier":1, + // "currency_code":"ARDR", + // "market_code":"BTC", + // "currency_name":"ARDOR", + // "market_name":"Bitcoin", + // "symbol":"ARDR_BTC", + // "group_name":"BTC", + // "group_id":1, + // "ask":"0.00000630", + // "bid":"0.00000613", + // "last":"0.00000617", + // "open":"0.00000620", + // "low":"0.00000614", + // "high":"0.00000630", + // "volume":"30.37795305", + // "volumeQuote":"4911487.01996544", + // "count":"710", + // "fiatsRate":{ + // "USD":7230.86, + // "EUR":6590.79, + // "UAH":173402, + // "AUD":10744.52, + // "IDR":101568085, + // "CNY":50752, + // "KRW":8452295, + // "JPY":784607, + // "VND":167315119, + // "INR":517596, + // "GBP":5607.25, + // "CAD":9602.63, + // "BRL":30472, + // "RUB":467358 + // }, + // "timestamp":1574698617304, + // "group_position":1 + // }, + // ] + // } + // + const tickers = this.safeValue (response, 'data', []); + return this.parseTickers (tickers, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + // + // { + // "time": 1566086400000, + // "close": 0.01895, + // "open": 0.01812427, + // "high": 0.0191588, + // "low": 0.01807001, + // "volume": 2588.597813750006 + // } + // + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + 'candlesType': this.timeframes[timeframe], // default 1d + // 'timeStart': 1574709092, // unix timestamp in seconds, required + // 'timeEnd': 1574709092, // unix timestamp in seconds, required + // 'limit': 100, // default 100, optional + // 'offset' 100, // optional, pagination within timerange + }; + if (limit === undefined) { + limit = 100; + } else { + request['limit'] = limit; + } + const duration = this.parseTimeframe (timeframe); + const timerange = limit * duration; + if (since === undefined) { + request['timeEnd'] = this.seconds (); + request['timeStart'] = request['timeEnd'] - timerange; + } else { + request['timeStart'] = parseInt (since / 1000); + request['timeEnd'] = this.sum (request['timeStart'], timerange); + } + const response = await this.publicGetChartCurrencyPairIdCandlesType (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "time": 1566086400000, + // "close": 0.01895, + // "open": 0.01812427, + // "high": 0.0191588, + // "low": 0.01807001, + // "volume": 2588.597813750006 + // }, + // ] + // } + // + const ohlcvs = this.safeValue (response, 'data', []); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // public fetchTrades + // + // { + // "id": 35989317, + // "price": "0.02033813", + // "amount": "3.60000000", + // "type": "BUY", + // "timestamp": "1574713503" + // } + // + // private fetchMyTrades, fetchClosedOrder, fetchOrderTrades + // + // { + // "id": 658745, + // "buy_order_id": 6587453, + // "sell_order_id": 6587459, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // + const id = this.safeString (trade, 'id'); + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if ((price !== undefined) && (amount !== undefined)) { + cost = price * amount; + } + let symbol = undefined; + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const side = this.safeStringLower2 (trade, 'type', 'trade_type'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': undefined, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + // 'sort': 'ASC', // ASC or DESC, default DESC + // 'from': 1574709092, // unix timestamp, optional + // 'till': 1574709092, // unix timestamp, optional + // 'limit': 100, // default 100, optional + // 'offset': 100, // optional + }; + if (limit !== undefined) { + request['limit'] = limit; // currently limited to 100 or fewer + } + if (since !== undefined) { + request['sort'] = 'ASC'; // needed to make the from param work + request['from'] = parseInt (since / 1000); + } + const response = await this.publicGetTradesCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 35989317, + // "price": "0.02033813", + // "amount": "3.60000000", + // "type": "BUY", + // "timestamp": "1574713503" + // }, + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + // await this.loadAccounts (); + const response = await this.profileGetWallets (params); + // + // { + // "success": true, + // "data": [ + // { + // "id": null, + // "currency_id": 665, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "currency_code": "ORM", + // "currency_name": "Orium", + // "currency_type_id": 5, + // "balance": "0", + // "frozen_balance": "0", + // "bonus_balance": "0", + // "total_balance": "0", + // "protocol_specific_settings": null, + // "rates": { "BTC": "0.00000000020", "USD": "0.00000147" }, + // }, + // { + // "id": null, + // "currency_id": 272, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "currency_code": "USDT", + // "currency_name": "TetherUSD", + // "currency_type_id": 23, + // "balance": "0", + // "frozen_balance": "0", + // "bonus_balance": "0", + // "total_balance": "0", + // "protocol_specific_settings": [ + // { "protocol_name": "OMNI", "protocol_id": 10, "active": true, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 10, "withdrawal_fee_percent": 0, "block_explorer_url": "https://omniexplorer.info/search/" }, + // { "protocol_name": "ERC20", "protocol_id": 5, "active": true, "withdrawal_fee_const": 1.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://etherscan.io/tx/" }, + // { "protocol_name": "TRON", "protocol_id": 24, "active": true, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 0.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://tronscan.org/#/transaction/" } + // ], + // "rates": { "BTC": "0.00013893", "USD": "1" }, + // }, + // ] + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const code = this.safeCurrencyCode (this.safeString (balance, 'currency_id')); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'frozen_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'PROCESSING': 'open', + 'PENDING': 'open', + 'PARTIAL': 'open', + 'FINISHED': 'closed', + 'CANCELLED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder, fetchOpenOrders, fetchClosedOrders, cancelOrder, fetchOrder, fetchClosedOrder + // + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", // missing in fetchClosedOrder + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // // fetchClosedOrder only + // "trades": [ + // { + // "id": 658745, + // "buy_order_id": 658745, + // "sell_order_id": 828680665, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // ], + // // fetchClosedOrder only + // "fees": [ + // { + // "id": 1234567, + // "currency_id": 1, + // "amount": 0.00025, + // "timestamp": "1548149238" + // } + // ] + // } + // + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + let marketId = this.safeString (order, 'currency_pair_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + marketId = this.safeString (order, 'currency_pair_name'); + if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'timestamp'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'initial_amount'); + const filled = this.safeFloat (order, 'processed_amount'); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + if (this.options['parseOrderToPrecision']) { + remaining = parseFloat (this.amountToPrecision (symbol, remaining)); + } + remaining = Math.max (remaining, 0.0); + } + if (price !== undefined) { + if (cost === undefined) { + cost = price * filled; + } + } + } + let type = this.safeString (order, 'original_type'); + if ((type === 'BUY') || (type === 'SELL')) { + type = undefined; + } + const side = this.safeStringLower (order, 'type'); + const rawTrades = this.safeValue (order, 'trades'); + let trades = undefined; + if (rawTrades !== undefined) { + trades = this.parseTrades (rawTrades, market, undefined, undefined, { + 'symbol': symbol, + 'order': id, + }); + } + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'trades': trades, + }; + const fees = this.safeValue (order, 'fees'); + if (fees === undefined) { + result['fee'] = undefined; + } else { + const numFees = fees.length; + if (numFees > 0) { + result['fees'] = []; + for (let i = 0; i < fees.length; i++) { + const feeCost = this.safeFloat (fees[i], 'amount'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (fees[i], 'currency_id'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + result['fees'].push ({ + 'cost': feeCost, + 'currency': feeCurrencyCode, + }); + } + } + } else { + result['fee'] = undefined; + } + } + return result; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' createOrder allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + if (type === 'limit') { + type = side; + } + const request = { + 'currencyPairId': market['id'], + 'type': type.toUpperCase (), // 'BUY', 'SELL', 'STOP_LIMIT_BUY', 'STOP_LIMIT_SELL' + 'amount': parseFloat (this.amountToPrecision (symbol, amount)), // required + 'price': parseFloat (this.priceToPrecision (symbol, price)), // required + // 'trigger_price': 123.45 // required for STOP_LIMIT_BUY or STOP_LIMIT_SELL + }; + const response = await this.tradingPostOrdersCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.tradingGetOrderOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseOrder (data, market); + } + + async fetchClosedOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.reportsGetOrdersOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 5478965, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.00013800", + // "initial_amount": "1.00000000", + // "type": "BUY", + // "created": "2019-01-22 09:27:17", + // "timestamp": 1548149237, + // "status": "FINISHED", + // "trades": [ + // { + // "id": 658745, + // "buy_order_id": 6587453, + // "sell_order_id": 6587459, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // ], + // "fees": [ + // { + // "id": 1234567, + // "currency_id": 1, + // "amount": 0.00025, + // "timestamp": "1548149238" + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseOrder (data, market); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + const order = await this.fetchClosedOrder (id, symbol, params); + return order['trades']; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let method = 'tradingGetOrders'; + const request = { + // 'limit': 100, // default 100 + // 'offset': 100, + }; + if (symbol !== undefined) { + method = 'tradingGetOrdersCurrencyPairId'; + market = this.market (symbol); + request['currencyPairId'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.tradingDeleteOrderOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "put_into_processing_queue": [ + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // ], + // "not_put_into_processing_queue": [ + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // ], + // "message": "string" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const acceptedOrders = this.safeValue (data, 'put_into_processing_queue', []); + const rejectedOrders = this.safeValue (data, 'not_put_into_processing_queue', []); + const numAcceptedOrders = acceptedOrders.length; + const numRejectedOrders = rejectedOrders.length; + if (numAcceptedOrders < 1) { + if (numRejectedOrders < 1) { + throw new OrderNotFound (this.id + ' cancelOrder received an empty response: ' + this.json (response)); + } else { + return this.parseOrder (rejectedOrders[0]); + } + } else { + if (numRejectedOrders < 1) { + return this.parseOrder (acceptedOrders[0]); + } else { + throw new OrderNotFound (this.id + ' cancelOrder received an empty response: ' + this.json (response)); + } + } + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'tradingDeleteOrders'; + if (symbol !== undefined) { + const market = this.market (symbol); + request['currencyPairId'] = market['id']; + method = 'tradingDeleteOrdersCurrencyPairId'; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "success":true, + // "data":{ + // "put_into_processing_queue":[], + // "not_put_into_processing_queue":[], + // "message":"Orders operations are handled in processing queue, therefore cancelling is not immediate." + // } + // } + // + return response; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + // 'timeStart': '2019-11-26T19:54:55.901Z', // datetime in iso format + // 'timeEnd': '2019-11-26T19:54:55.901Z', // datetime in iso format + // 'limit': 100, // default 100 + // 'offset': 100, + }; + if (since !== undefined) { + request['timeStart'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.reportsGetTradesCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 658745, + // "buy_order_id": 6587453, + // "sell_order_id": 6587459, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyId': currency['id'], + // Default value is the value that represents legacy protocol. + // In case of USDT it is 10 as Tether OMNI was the default previously. + // The list of protocols can be obtained from the /public/currencies/{currencyId} + // 'protocol_id': 10, + }; + const response = await this.profilePostWalletsCurrencyId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 45875, + // "currency_id": 1, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "code": "BTC", + // "balance": "0.198752", + // "frozen_balance": "1.5784", + // "bonus_balance": "0.000", + // "deposit_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // }, + // "multi_deposit_addresses": [ + // { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // ], + // "withdrawal_additional_field_name": "Payment ID (optional)", + // "rates": { "BTC": 0.000001 }, + // "protocol_specific_settings": [ + // { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "active": true, + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_const": 0.002, + // "withdrawal_fee_percent": 0, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const depositAddress = this.safeValue (data, 'deposit_address', {}); + const address = this.safeString (depositAddress, 'address'); + const tag = this.safeString (depositAddress, 'additional_address_parameter'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const balance = await this.fetchBalance (); + const wallets = this.safeValue (balance['info'], 'data', []); + const walletsByCurrencyId = this.indexBy (wallets, 'currency_id'); + const currency = this.currency (code); + const wallet = this.safeValue (walletsByCurrencyId, currency['id']); + if (wallet === undefined) { + throw new ExchangeError (this.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first'); + } + const walletId = this.safeInteger (wallet, 'id'); + if (walletId === undefined) { + throw new ExchangeError (this.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first'); + } + const request = { + 'walletId': walletId, + }; + const response = await this.profileGetWalletsWalletId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 45875, + // "currency_id": 1, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "code": "BTC", + // "balance": "0.198752", + // "frozen_balance": "1.5784", + // "bonus_balance": "0.000", + // "deposit_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // }, + // "multi_deposit_addresses": [ + // { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // ], + // "withdrawal_additional_field_name": "Payment ID (optional)", + // "rates": { "BTC": 0.000001 }, + // "protocol_specific_settings": [ + // { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "active": true, + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_const": 0.002, + // "withdrawal_fee_percent": 0, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', []); + const depositAddress = this.safeValue (data, 'deposit_address', {}); + const address = this.safeString (depositAddress, 'address'); + const tag = this.safeString (depositAddress, 'additional_address_parameter'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + api + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + headers = { + 'Authorization': 'Bearer ' + this.token, + }; + if (method === 'GET' || method === 'DELETE') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + if (Object.keys (query).length) { + headers['Content-Type'] = 'application/json'; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + parseTransactionStatus (status) { + const statuses = { + 'processing': 'pending', + 'checking by system': 'pending', + 'hodl': 'pending', + 'amount too low': 'failed', + 'not confirmed': 'pending', + 'cancelled by User': 'canceled', + 'approved': 'pending', + 'finished': 'ok', + 'withdrawal error': 'failed', + 'deposit error': 'failed', + 'cancelled by admin': 'canceled', + 'awaiting': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "id": 123654789, + // "currency_id": 1, + // "currency_code": "BTC", + // "deposit_fee_currency_id": 1, + // "deposit_fee_currency_code": "BTC", + // "amount": 0.25, + // "fee": 0.00025, + // "txid": "qwertyuhgfdsasdfgh", + // "protocol_id": 0, + // "deposit_status_id": 1, + // "status": "PROCESSING", + // "status_color": "#BC3D51", + // "created_at": "2018-11-28 12:32:08", + // "timestamp": "1543409389", + // "confirmations": "1 of 2" + // } + // + // fetchWithdrawals + // + // { + // "id": 65899, + // "amount": "0.00600000", + // "currency_id": 1, + // "currency_code": "BTC", + // "fee": "0.00400000", + // "fee_currency_id": 1, + // "fee_currency_code": "BTC", + // "withdrawal_status_id": 1, + // "status": "Not Confirmed", + // "status_color": "#BC3D51", + // "created_at": "2019-01-21 09:36:05", + // "created_ts": "1548063365", + // "updated_at": "2019-01-21 09:36:05", + // "updated_ts": "1548063365", + // "txid": null, + // "protocol_id": 0, + // "withdrawal_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // } + // + const id = this.safeString (transaction, 'id'); + const withdrawalAddress = this.safeValue (transaction, 'withdrawal_address', {}); + const address = this.safeString (withdrawalAddress, 'address'); + const tag = this.safeString (withdrawalAddress, 'additional_address_parameter'); + const currencyId = this.safeString (transaction, 'currency_id'); + let code = undefined; + if (currencyId in this.currencies_by_id) { + currency = this.currencies_by_id[currencyId]; + } else { + code = this.commonCurrencyCode (this.safeString (transaction, 'currency_code')); + } + if ((code === undefined) && (currency !== undefined)) { + code = currency['code']; + } + const type = ('deposit_status_id' in transaction) ? 'deposit' : 'withdrawal'; + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeStringLower (transaction, 'status')); + const timestamp = this.safeTimestamp2 (transaction, 'timestamp', 'created_ts'); + const updated = this.safeTimestamp (transaction, 'updated_ts'); + const txid = this.safeString (transaction, 'txid'); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (transaction, 'fee_currency_id', 'deposit_fee_currency_id'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': address, + 'addressTo': address, + 'tagFrom': undefined, + 'tag': tag, + 'tagTo': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currencyId'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['timeStart'] = since; + } + const response = await this.profileGetDeposits (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 123654789, + // "currency_id": 1, + // "currency_code": "BTC", + // "deposit_fee_currency_id": 1, + // "deposit_fee_currency_code": "BTC", + // "amount": 0.25, + // "fee": 0.00025, + // "txid": "qwertyuhgfdsasdfgh", + // "protocol_id": 0, + // "deposit_status_id": 1, + // "status": "PROCESSING", + // "status_color": "#BC3D51", + // "created_at": "2018-11-28 12:32:08", + // "timestamp": "1543409389", + // "confirmations": "1 of 2", + // "protocol_specific_settings": { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // } + // ] + // } + // + const deposits = this.safeValue (response, 'data', []); + return this.parseTransactions (deposits, code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currencyId'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['timeStart'] = since; + } + const response = await this.profileGetWithdrawals (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 65899, + // "amount": "0.00600000", + // "currency_id": 1, + // "currency_code": "BTC", + // "fee": "0.00400000", + // "fee_currency_id": 1, + // "fee_currency_code": "BTC", + // "withdrawal_status_id": 1, + // "status": "Not Confirmed", + // "status_color": "#BC3D51", + // "created_at": "2019-01-21 09:36:05", + // "created_ts": "1548063365", + // "updated_at": "2019-01-21 09:36:05", + // "updated_ts": "1548063365", + // "txid": null, + // "protocol_id": 0, + // "withdrawal_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // }, + // "protocol_specific_settings": { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // } + // ] + // } + // + const withdrawals = this.safeValue (response, 'data', []); + return this.parseTransactions (withdrawals, code, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency_id': currency['id'], + 'amount': parseFloat (this.currencyToPrecision (code, amount)), + 'address': address, + // 'protocol_id': 10, // optional, to be used with multicurrency wallets like USDT + // 'additional_address_parameter': tag, // optional + }; + if (tag !== undefined) { + request['additional_address_parameter'] = tag; + } + const response = await this.profilePostWithdraw (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 65899, + // "amount": "0.00600000", + // "currency_id": 1, + // "currency_code": "BTC", + // "fee": "0.00400000", + // "fee_currency_id": 1, + // "fee_currency_code": "BTC", + // "withdrawal_status_id": 1, + // "status": "Not Confirmed", + // "status_color": "#BC3D51", + // "created_at": "2019-01-21 09:36:05", + // "created_ts": "1548063365", + // "updated_at": "2019-01-21 09:36:05", + // "updated_ts": "1548063365", + // "txid": null, + // "protocol_id": 0, + // "withdrawal_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseTransaction (data, currency); + } + + async fetchFundingFees (codes = undefined, params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // "success": true, + // "data": [ + // { + // "id": 1, + // "code": "BTC", + // "name": "Bitcoin", + // "active": true, + // "delisted": false, + // "precision": 8, + // "minimum_tx_confirmations": 24, + // "minimum_withdrawal_amount": "0.009", + // "minimum_deposit_amount": "0.000003", + // "deposit_fee_currency_id": 1, + // "deposit_fee_currency_code": "ETH", + // "deposit_fee_const": "0.00001", + // "deposit_fee_percent": "0", + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_currency_code": "ETH", + // "withdrawal_fee_const": "0.0015", + // "withdrawal_fee_percent": "0", + // "withdrawal_limit": "string", + // "block_explorer_url": "https://blockchain.info/tx/", + // "protocol_specific_settings": [ + // { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "active": true, + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_const": 0.002, + // "withdrawal_fee_percent": 0, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // ] + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const withdrawFees = {}; + const depositFees = {}; + for (let i = 0; i < data.length; i++) { + const id = this.safeString (data[i], 'id'); + const code = this.safeCurrencyCode (id); + withdrawFees[code] = this.safeFloat (data[i], 'withdrawal_fee_const'); + depositFees[code] = this.safeFloat (data[i], 'deposit_fee_const'); + } + return { + 'withdraw': withdrawFees, + 'deposit': depositFees, + 'info': response, + }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}} + // {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}} + // {"success":false,"message":"Not enough ETH"} + // + const success = this.safeValue (response, 'success', false); + if (!success) { + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],160:[function(require,module,exports){ +'use strict'; + +// ---------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InvalidNonce, AuthenticationError, AccountSuspended, InsufficientFunds, ExchangeError, ArgumentsRequired, NotSupported } = require ('./base/errors'); + +// ---------------------------------------------------------------------------- + +module.exports = class stronghold extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'stronghold', + 'name': 'Stronghold', + 'country': [ 'US' ], + 'rateLimit': 1000, + 'version': 'v1', + 'comment': 'This comment is optional', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/52160042-98c1f300-26be-11e9-90dd-da8473944c83.jpg', + 'api': { + 'public': 'https://api.stronghold.co', + 'private': 'https://api.stronghold.co', + }, + 'www': 'https://stronghold.co', + 'doc': [ + 'https://docs.stronghold.co', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'has': { + 'fetchMarkets': true, + 'fetchCurrencies': true, + 'fetchOrderBook': true, + 'fetchOpenOrders': true, + 'fetchTime': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': false, + 'createDepositAddress': true, + 'withdraw': true, + 'fetchTicker': false, + 'fetchTickers': false, + 'fetchAccounts': true, + 'fetchTransactions': true, + }, + 'api': { + 'public': { + 'get': [ + 'utilities/time', + 'utilities/uuid', + 'venues', + 'venues/{venueId}/assets', + 'venues/{venueId}/markets', + 'venues/{venueId}/markets/{marketId}/orderbook', + 'venues/{venueId}/markets/{marketId}/trades', + ], + 'post': [ + 'venues/{venueId}/assets', + 'iam/credentials', + 'identities', + ], + 'patch': [ + 'identities', + ], + 'put': [ + 'iam/credentials/{credentialId}', + ], + 'delete': [ + 'iam/credentials/{credentialId}', + ], + }, + 'private': { + 'get': [ + 'venues', + 'venues/{venueId}/accounts', + 'venues/{venueId}/accounts/{accountId}', + 'venues/{venueId}/accounts/{accountId}/payments/{paymentId}', + 'venues/{venueId}/accounts/{accountId}/orders', + 'venues/{venueId}/accounts/{accountId}/trades', + 'venues/{venueId}/accounts/{accountId}/transactions', + ], + 'post': [ + 'venues/{venueId}/accounts', + 'venues/{venueId}/accounts/{accountId}/orders', + 'venues/{venueId}/accounts/{accountId}/deposit', + 'venues/{venueId}/accounts/{accountId}/withdrawal', + 'venues/{venueId}/accounts/{accountId}/payments', + 'venues/{venueId}/accounts/{accountId}/payments/{paymentId}/stop', + 'venues/{venueId}/custody/accounts/{accountId}/operations/{operationId}/signatures', + 'venues/{venueId}/anchor/withdrawal', + 'venues/{venueId}/testing/friendbot', + ], + 'delete': [ + 'venues/{venueId}/accounts/{accountId}/orders/{orderId}', + ], + }, + }, + 'options': { + 'accountId': undefined, + 'venueId': 'trade-public', + 'venues': { + 'trade': 'trade-public', + 'sandbox': 'sandbox-public', + }, + 'paymentMethods': { + 'ETH': 'ethereum', + 'BTC': 'bitcoin', + 'XLM': 'stellar', + 'XRP': 'ripple', + 'LTC': 'litecoin', + 'SHX': 'stellar', + }, + }, + 'exceptions': { + 'CREDENTIAL_MISSING': AuthenticationError, + 'CREDENTIAL_INVALID': AuthenticationError, + 'CREDENTIAL_REVOKED': AccountSuspended, + 'CREDENTIAL_NO_IDENTITY': AuthenticationError, + 'PASSPHRASE_INVALID': AuthenticationError, + 'SIGNATURE_INVALID': AuthenticationError, + 'TIME_INVALID': InvalidNonce, + 'BYPASS_INVALID': AuthenticationError, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + }, + }); + } + + async getActiveAccount () { + if (this.options['accountId'] !== undefined) { + return this.options['accountId']; + } + await this.loadAccounts (); + const numAccounts = this.accounts.length; + if (numAccounts > 0) { + return this.accounts[0]['id']; + } + throw new ExchangeError (this.id + ' requires an accountId.'); + } + + async fetchAccounts (params = {}) { + const request = { + 'venueId': this.options['venueId'], + }; + const response = await this.privateGetVenuesVenueIdAccounts (this.extend (request, params)); + // + // [ { id: '34080200-b25a-483d-a734-255d30ba324d', + // venueSpecificId: '' } ... ] + // + return response['result']; + } + + async fetchTime (params = {}) { + const response = await this.publicGetUtilitiesTime (params); + // + // { + // "requestId": "6de8f506-ad9d-4d0d-94f3-ec4d55dfcdb9", + // "timestamp": 1536436649207281, + // "success": true, + // "statusCode": 200, + // "result": { + // "timestamp": "2018-09-08T19:57:29.207282Z" + // } + // } + // + return this.parse8601 (this.safeString (response['result'], 'timestamp')); + } + + async fetchMarkets (params = {}) { + const request = { + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdMarkets (this.extend (request, params)); + const data = response['result']; + // + // [ + // { + // id: 'SHXUSD', + // baseAssetId: 'SHX/stronghold.co', + // counterAssetId: 'USD/stronghold.co', + // minimumOrderSize: '1.0000000', + // minimumOrderIncrement: '1.0000000', + // minimumPriceIncrement: '0.00010000', + // displayDecimalsPrice: 4, + // displayDecimalsAmount: 0 + // }, + // ... + // ] + // + const result = {}; + for (let i = 0; i < data.length; i++) { + const entry = data[i]; + const marketId = entry['id']; + const baseId = this.safeString (entry, 'baseAssetId'); + const quoteId = this.safeString (entry, 'counterAssetId'); + const baseAssetId = baseId.split ('/')[0]; + const quoteAssetId = quoteId.split ('/')[0]; + const base = this.safeCurrencyCode (baseAssetId); + const quote = this.safeCurrencyCode (quoteAssetId); + const symbol = base + '/' + quote; + const limits = { + 'amount': { + 'min': this.safeFloat (entry, 'minimumOrderSize'), + 'max': undefined, + }, + }; + const precision = { + 'price': this.safeInteger (entry, 'displayDecimalsPrice'), + 'amount': this.safeInteger (entry, 'displayDecimalsAmount'), + }; + result[symbol] = { + 'symbol': symbol, + 'id': marketId, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'info': entry, + 'limits': limits, + }; + } + return result; + } + + async fetchCurrencies (params = {}) { + const request = { + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdAssets (this.extend (request, params)); + // + // [ + // { + // id: 'XLM/native', + // alias: '', + // code: 'XLM', + // name: '', + // displayDecimalsFull: 7, + // displayDecimalsSignificant: 2, + // }, + // ... + // ] + // + const data = response['result']; + const result = {}; + const limits = { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }; + for (let i = 0; i < data.length; i++) { + const entry = data[i]; + const assetId = this.safeString (entry, 'id'); + const currencyId = this.safeString (entry, 'code'); + const code = this.safeCurrencyCode (currencyId); + const precision = this.safeInteger (entry, 'displayDecimalsFull'); + result[code] = { + 'code': code, + 'id': assetId, + 'precision': precision, + 'info': entry, + 'active': undefined, + 'name': undefined, + 'limits': limits, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'marketId': marketId, + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdMarketsMarketIdOrderbook (this.extend (request, params)); + // + // { + // marketId: 'ETHBTC', + // bids: [ + // [ '0.031500', '7.385000' ], + // ..., + // ], + // asks: [ + // [ '0.031500', '7.385000' ], + // ..., + // ], + // } + // + const data = response['result']; + const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); + return this.parseOrderBook (data, timestamp); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdMarketsMarketIdTrades (this.extend (request, params)); + // + // { + // "requestId": "4d343700-b53f-4975-afcc-732ae9d3c828", + // "timestamp": "2018-11-08T19:22:11.399543Z", + // "success": true, + // "statusCode": 200, + // "result": { + // "marketId": "", + // "trades": [ + // [ "0.9", "3.10", "sell", "2018-11-08T19:22:11.399547Z" ], + // ... + // ], + // } + // } + // + return this.parseTrades (response['result']['trades'], market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // [ '0.03177000', '0.0643501', 'sell', '2019-01-27T23:02:04Z' ] + // + // fetchMyTrades (private) + // + // { + // id: '9cdb109c-d035-47e2-81f8-a0c802c9c5f9', + // orderId: 'a38d8bcb-9ff5-4c52-81a0-a40196a66462', + // marketId: 'XLMUSD', + // side: 'sell', + // size: '1.0000000', + // price: '0.10440600', + // settled: true, + // maker: false, + // executedAt: '2019-02-01T18:44:21Z' + // } + // + let id = undefined; + let takerOrMaker = undefined; + let price = undefined; + let amount = undefined; + let cost = undefined; + let side = undefined; + let timestamp = undefined; + let orderId = undefined; + if (Array.isArray (trade)) { + price = parseFloat (trade[0]); + amount = parseFloat (trade[1]); + side = trade[2]; + timestamp = this.parse8601 (trade[3]); + } else { + id = this.safeString (trade, 'id'); + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'size'); + side = this.safeString (trade, 'side'); + timestamp = this.parse8601 (this.safeString (trade, 'executedAt')); + orderId = this.safeString (trade, 'orderId'); + const marketId = this.safeString (trade, 'marketId'); + market = this.safeValue (this.markets_by_id, marketId); + const isMaker = this.safeValue (trade, 'maker'); + takerOrMaker = isMaker ? 'maker' : 'taker'; + } + if (amount !== undefined && price !== undefined) { + cost = amount * price; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': orderId, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': { + 'cost': undefined, + 'currency': undefined, + 'rate': undefined, + }, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " fetchTransactions requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountIdTransactions (request); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + return this.parseTransactions (response['result'], currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'queued': 'pending', + 'settling': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // { + // "id": "6408e003-0f14-4457-9340-ba608992ad5c", + // "status": "queued", + // "direction": "outgoing", + // "amount": "98.95000000", + // "assetId": "XLM/native", + // "sourceAccount": { + // "id": "774fa8ef-600b-4636-b9ed-cd6d23421915", + // "venueSpecificId": "GC5FIBIQZTQRMJE34GYF5EKH77GEQ3OHFX3NIP5OKDIZFA6VERLZSHY6" + // }, + // "destinationAccount": { + // "id": "f72b9fb5-9607-4dd3-b31f-6ded21337056", + // "venueSpecificId": "GAOWV6CYBE7DEWSWPODXLMI5YB75VXXZJX5OYVQ2YLZH2TVA3TMMSNYW" + // } + // } + const id = this.safeString (transaction, 'id'); + const assetId = this.safeString (transaction, 'assetId'); + let code = undefined; + if (assetId !== undefined) { + const currencyId = assetId.split ('/')[0]; + code = this.safeCurrencyCode (currencyId); + } else { + if (currency !== undefined) { + code = currency['code']; + } + } + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const feeCost = this.safeFloat (transaction, 'feeAmount'); + let feeRate = undefined; + if (feeCost !== undefined) { + feeRate = feeCost / amount; + } + const direction = this.safeString (transaction, 'direction'); + const datetime = this.safeString (transaction, 'requestedAt'); + const timestamp = this.parse8601 (datetime); + const updated = this.parse8601 (this.safeString (transaction, 'updatedAt')); + const type = (direction === 'outgoing' || direction === 'withdrawal') ? 'withdrawal' : 'deposit'; + const fee = { + 'cost': feeCost, + 'rate': feeRate, + }; + return { + 'id': id, + 'info': transaction, + 'currency': code, + 'amount': amount, + 'status': status, + 'fee': fee, + 'tag': undefined, + 'type': type, + 'updated': updated, + 'address': undefined, + 'txid': undefined, + 'timestamp': timestamp, + 'datetime': datetime, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'marketID': market['id'], + 'type': type, + 'side': side, + 'size': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " createOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privatePostVenuesVenueIdAccountsAccountIdOrders (request); + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'orderId': id, + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateDeleteVenuesVenueIdAccountsAccountIdOrdersOrderId (request); + return this.parseOrder (response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountIdOrders (request); + return this.parseOrders (response['result'], market, since, limit); + } + + parseOrder (order, market = undefined) { + // { id: '178596', + // marketId: 'XLMUSD', + // side: 'buy', + // size: '1.0000000', + // sizeFilled: '0', + // price: '0.10000000', + // placedAt: '2019-02-01T19:47:52Z' } + const marketId = this.safeString (order, 'marketId'); + if (marketId !== undefined) { + market = this.safeValue (this.marketsById, marketId); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'id'); + const datetime = this.safeString (order, 'placedAt'); + const amount = this.safeFloat (order, 'size'); + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'sizeFilled'); + let cost = undefined; + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'info': order, + 'symbol': symbol, + 'datetime': datetime, + 'timestamp': this.parse8601 (datetime), + 'side': this.safeString (order, 'side'), + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'price': price, + 'cost': cost, + 'trades': [], + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'type': undefined, + 'average': undefined, + }; + } + + nonce () { + return this.seconds (); + } + + setSandboxMode (enabled) { + if (enabled) { + this.options['venueId'] = this.options['venues']['sandbox']; + } else { + this.options['venueId'] = this.options['venues']['trade']; + } + } + + async fetchBalance (params = {}) { + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!('accountId' in request)) { + throw new ArgumentsRequired (this.id + " fetchBalance requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountId (request); + const balances = this.safeValue (response['result'], 'balances'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const assetId = this.safeString (balance, 'assetId'); + if (assetId !== undefined) { + const currencyId = assetId.split ('/')[0]; + const code = this.safeCurrencyCode (currencyId); + const account = {}; + account['total'] = this.safeFloat (balance, 'amount'); + account['free'] = this.safeFloat (balance, 'availableForTrade'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " fetchMyTrades requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountIdTrades (request); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseTrades (response['result'], market, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const paymentMethod = this.safeString (this.options['paymentMethods'], code); + if (paymentMethod === undefined) { + throw new NotSupported (this.id + ' createDepositAddress requires code to be BTC, ETH, or XLM'); + } + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'assetId': this.currencyId (code), + 'paymentMethod': paymentMethod, + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " createDepositAddress requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privatePostVenuesVenueIdAccountsAccountIdDeposit (request); + // + // { + // assetId: 'BTC/stronghold.co', + // paymentMethod: 'bitcoin', + // paymentMethodInstructions: { + // deposit_address: 'mzMT9Cfw8JXVWK7rMonrpGfY9tt57ytHt4', + // reference: 'sometimes-exists', + // }, + // direction: 'deposit', + // } + // + const data = response['result']['paymentMethodInstructions']; + const address = data['deposit_address']; + const tag = this.safeString (data, 'reference'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const paymentMethod = this.safeString (this.options['paymentMethods'], code); + if (paymentMethod === undefined) { + throw new NotSupported (this.id + ' withdraw requires code to be BTC, ETH, or XLM'); + } + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'assetId': this.currencyId (code), + 'amount': amount, + 'paymentMethod': paymentMethod, + 'paymentMethodDetails': { + 'withdrawal_address': address, + }, + }, params); + if (tag !== undefined) { + request['paymentMethodDetails']['reference'] = tag; + } + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " withdraw requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privatePostVenuesVenueIdAccountsAccountIdWithdrawal (request); + // + // { + // "id": "5be48892-1b6e-4431-a3cf-34b38811e82c", + // "assetId": "BTC/stronghold.co", + // "amount": "10", + // "feeAmount": "0.01", + // "paymentMethod": "bitcoin", + // "paymentMethodDetails": { + // "withdrawal_address": "1vHysJeXYV6nqhroBaGi52QWFarbJ1dmQ" + // }, + // "direction": "withdrawal", + // "status": "pending" + // } + // + const data = response['result']; + return { + 'id': this.safeString (data, 'id'), + 'info': response, + }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // fallback to base error handler by default + } + // + // { + // requestId: '3e7d17ab-b316-4721-b5aa-f7e6497eeab9', + // timestamp: '2019-01-31T21:59:06.696855Z', + // success: true, + // statusCode: 200, + // result: [] + // } + // + const errorCode = this.safeString (response, 'errorCode'); + if (errorCode in this.exceptions) { + const Exception = this.exceptions[errorCode]; + throw new Exception (this.id + ' ' + body); + } + const success = this.safeValue (response, 'success'); + if (!success) { + throw new ExchangeError (this.id + ' ' + body); + } + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + request; + if (Object.keys (query).length) { + if (method === 'GET') { + url += '?' + this.urlencode (query); + } else { + body = this.json (query); + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.nonce ().toString (); + let payload = timestamp + method + request; + if (body !== undefined) { + payload += body; + } + const secret = this.base64ToBinary (this.secret); + headers = { + 'SH-CRED-ID': this.apiKey, + 'SH-CRED-SIG': this.hmac (this.encode (payload), secret, 'sha256', 'base64'), + 'SH-CRED-TIME': timestamp, + 'SH-CRED-PASS': this.password, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],161:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const foxbit = require ('./foxbit.js'); + +// --------------------------------------------------------------------------- + +module.exports = class surbitcoin extends foxbit { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'surbitcoin', + 'name': 'SurBitcoin', + 'countries': [ 'VE' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991511-f0a50194-6481-11e7-99b5-8f02932424cc.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://surbitcoin.com', + 'doc': 'https://blinktrade.com/docs', + }, + 'options': { + 'brokerId': '1', // https://blinktrade.com/docs/#brokers + }, + }); + } +}; + +},{"./foxbit.js":86}],162:[function(require,module,exports){ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, AuthenticationError, InvalidOrder, OrderNotFound, NotSupported, OrderNotFillable, InvalidAddress, InsufficientFunds } = require ('./base/errors'); + +module.exports = class theocean extends Exchange { + describe () { + this.checkRequiredDependencies (); + return this.deepExtend (super.describe (), { + 'id': 'theocean', + 'name': 'The Ocean', + 'countries': [ 'US' ], + 'rateLimit': 3000, + 'version': 'v1', + 'requiresWeb3': true, + 'timeframes': { + '5m': '300', + '15m': '900', + '1h': '3600', + '6h': '21600', + '1d': '86400', + }, + 'has': { + 'cancelAllOrders': true, + 'CORS': false, // ? + 'fetchClosedOrders': true, + 'fetchOHLCV': false, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchTickers': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/43103756-d56613ce-8ed7-11e8-924e-68f9d4bcacab.jpg', + 'api': 'https://api.theocean.trade', + 'www': 'https://theocean.trade', + 'doc': 'https://docs.theocean.trade', + 'fees': 'https://theocean.trade/fees', + }, + 'api': { + 'public': { + 'get': [ + 'fee_components', + 'token_pairs', + 'ticker', + 'tickers', + 'candlesticks', + 'candlesticks/intervals', + 'trade_history', + 'order_book', + 'order/{orderHash}', + 'version', + ], + }, + 'private': { + 'get': [ + 'balance', + 'available_balance', + 'order_history', + 'order/unsigned', + 'order/unsigned/market', + ], + 'post': [ + 'order', + ], + 'delete': [ + 'order/{orderHash}', + 'order', + ], + }, + }, + 'exceptions': { + 'exact': { + 'Order not found': OrderNotFound, // {"message":"Order not found","errors":...} + }, + 'broad': { + "Price can't exceed 8 digits in precision.": InvalidOrder, // {"message":"Price can't exceed 8 digits in precision.","type":"paramPrice"} + 'Order cannot be canceled': InvalidOrder, // {"message":"Order cannot be canceled","type":"General error"} + 'Greater than available wallet balance.': InsufficientFunds, + 'Fillable amount under minimum': InvalidOrder, // {"message":"Fillable amount under minimum WETH trade size.","type":"paramQuoteTokenAmount"} + 'Fillable amount over maximum': InvalidOrder, // {"message":"Fillable amount over maximum TUSD trade size.","type":"paramQuoteTokenAmount"} + "Schema validation failed for 'params'": BadRequest, // // {"message":"Schema validation failed for 'params'"} + 'Service Temporarily Unavailable': ExchangeNotAvailable, + }, + }, + 'options': { + 'decimals': {}, + 'fetchOrderMethod': 'fetch_order_from_history', + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetTokenPairs (params); + // + // [ + // "baseToken": { + // "symbol": "ZRX", + // "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570", + // "name": "0x Protocol Token", + // "decimals": "18", + // "minAmount": "10000000000000000000", + // "maxAmount": "10000000000000000000000", + // "precision": "-8" + // }, + // "quoteToken": { + // "symbol": "ETH", + // "address": "0xd0a1e359811322d97991e03f863a0c30c2cf029c", + // "name": "Ether Token", + // "decimals": "18", + // "minAmount": "20000000000000000", + // "maxAmount": "20000000000000000000", + // "precision": "-8" + // } + // ] + // + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseToken = this.safeValue (market, 'baseToken', {}); + const quoteToken = this.safeValue (market, 'quoteToken', {}); + const baseId = this.safeString (baseToken, 'address'); + const quoteId = this.safeString (quoteToken, 'address'); + const base = this.safeCurrencyCode (this.safeString (baseToken, 'symbol')); + const quote = this.safeCurrencyCode (this.safeString (quoteToken, 'symbol')); + const symbol = base + '/' + quote; + const id = baseId + '/' + quoteId; + const baseDecimals = this.safeInteger (baseToken, 'decimals'); + const quoteDecimals = this.safeInteger (quoteToken, 'decimals'); + this.options['decimals'][base] = baseDecimals; + this.options['decimals'][quote] = quoteDecimals; + const precision = { + 'amount': -parseInt (baseToken['precision']), + 'price': -parseInt (quoteToken['precision']), + }; + const amountLimits = { + 'min': this.fromWei (this.safeString (baseToken, 'minAmount'), 'ether', baseDecimals), + 'max': this.fromWei (this.safeString (baseToken, 'maxAmount'), 'ether', baseDecimals), + }; + const priceLimits = { + 'min': undefined, + 'max': undefined, + }; + const costLimits = { + 'min': this.fromWei (this.safeString (quoteToken, 'minAmount'), 'ether', quoteDecimals), + 'max': this.fromWei (this.safeString (quoteToken, 'maxAmount'), 'ether', quoteDecimals), + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const active = true; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + const baseDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); + return [ + this.safeTimestamp (ohlcv, 'startTime'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.fromWei (this.safeString (ohlcv, 'baseVolume'), 'ether', baseDecimals), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + 'interval': this.timeframes[timeframe], + }; + if (since === undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a since argument'); + } + since = parseInt (since); + request['startTime'] = since; + const response = await this.publicGetCandlesticks (this.extend (request, params)); + // + // [ + // { + // "high": "100.52", + // "low": "97.23", + // "open": "98.45", + // "close": "99.23", + // "baseVolume": "2400000000000000000000", + // "quoteVolume": "1200000000000000000000", + // "startTime": "1512929323784" + // }, + // { + // "high": "100.52", + // "low": "97.23", + // "open": "98.45", + // "close": "99.23", + // "volume": "2400000000000000000000", + // "startTime": "1512929198980" + // } + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalanceByCode (code, params = {}) { + if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { + throw new InvalidAddress (this.id + ' fetchBalanceByCode() requires the .walletAddress to be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377"'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'walletAddress': this.walletAddress.toLowerCase (), + 'tokenAddress': currency['id'], + }; + const response = await this.privateGetBalance (this.extend (request, params)); + // + // {"available":"0","committed":"0","total":"0"} + // + const decimals = this.safeInteger (this.options['decimals'], code, 18); + const free = this.fromWei (this.safeString (response, 'available'), 'ether', decimals); + const used = this.fromWei (this.safeString (response, 'committed'), 'ether', decimals); + const total = this.fromWei (this.safeString (response, 'total'), 'ether', decimals); + return { + 'free': free, + 'used': used, + 'total': total, + }; + } + + async fetchBalance (params = {}) { + if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { + throw new InvalidAddress (this.id + ' fetchBalance() requires the .walletAddress to be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377"'); + } + let codes = this.safeValue (this.options, 'fetchBalanceCurrencies'); + if (codes === undefined) { + codes = this.safeValue (params, 'codes'); + } + if ((codes === undefined) || (!Array.isArray (codes))) { + throw new ExchangeError (this.id + ' fetchBalance() requires a `codes` parameter (an array of currency codes)'); + } + await this.loadMarkets (); + const result = {}; + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + result[code] = await this.fetchBalanceByCode (code); + } + return this.parseBalance (result); + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1, market = undefined) { + if (market === undefined) { + throw new ArgumentsRequired (this.id + ' parseBidAsk requires a market argument'); + } + const price = parseFloat (bidask[priceKey]); + const amountDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); + const amount = this.fromWei (bidask[amountKey], 'ether', amountDecimals); + return [ price, amount ]; + } + + parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1, market = undefined) { + const result = { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + const sides = [ bidsKey, asksKey ]; + for (let i = 0; i < sides.length; i++) { + const side = sides[i]; + const orders = []; + const bidasks = this.safeValue (orderbook, side); + for (let k = 0; k < bidasks.length; k++) { + orders.push (this.parseBidAsk (bidasks[k], priceKey, amountKey, market)); + } + result[side] = orders; + } + result[bidsKey] = this.sortBy (result[bidsKey], 0, true); + result[asksKey] = this.sortBy (result[asksKey], 0); + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + }; + if (limit !== undefined) { + request['depth'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { + // "bids": [ + // { orderHash: '0xe2b7f80198edb561cc66cd85cb8e5f420073cf1e5143193d8add8774bd8236c4', + // price: '30', + // availableAmount: '500000000000000000', + // creationTimestamp: '1547193525', + // expirationTimestampInSec: '1549789124' + // } + // ], + // "asks": [ + // { orderHash: '0xe2b7f80198edb561cc66cd85cb8e5f420073cf1e5143193d8add8774bd8236c4', + // price: '30', + // availableAmount: '500000000000000000', + // creationTimestamp: '1547193525', + // expirationTimestampInSec: '1549789124' + // } + // ] + // } + // + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'availableAmount', market); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "bid": "0.00050915", + // "ask": "0.00054134", + // "last": "0.00052718", + // "volume": "3000000000000000000", + // "timestamp": "1512929327792" + // } + // + const timestamp = parseInt (this.safeInteger (ticker, 'timestamp') / 1000); + let symbol = undefined; + let base = undefined; + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + } + const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); + const baseVolume = this.fromWei (this.safeString (ticker, 'volume'), 'ether', baseDecimals); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'priceChange'), + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTickers (params); + // + // [{ + // "baseTokenAddress": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d", + // "quoteTokenAddress": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a", + // "ticker": { + // "bid": "0.00050915", + // "ask": "0.00054134", + // "last": "0.00052718", + // "volume": "3000000000000000000", + // "timestamp": "1512929327792" + // } + // }] + // + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const baseId = this.safeString (ticker, 'baseTokenAddress'); + const quoteId = this.safeString (ticker, 'quoteTokenAddress'); + const marketId = baseId + '/' + quoteId; + let market = undefined; + let symbol = marketId; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker['ticker'], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades + // + // { + // "id": "37212", + // "transactionHash": "0x5e6e75e1aa681b51b034296f62ac19be7460411a2ad94042dd8ba637e13eac0c", + // "amount": "300000000000000000", + // "price": "0.00052718", + // ------- they also have a "confirmed" status here ↓ ----------------- + // "status": "filled", // filled | settled | failed + // "lastUpdated": "1520265048996" + // } + // + // parseOrder trades (timeline "actions", "fills") + // + // { action: "confirmed", + // amount: "1000000000000000000", + // intentID: "MARKET_INTENT:90jjw2s7gj90jjw2s7gkjjw2s7gl", + // txHash: "0x043488fdc3f995bf9e632a32424e41ed126de90f8cb340a1ff006c2a74ca8336", + // blockNumber: "8094822", + // timestamp: "1532261686" } + // + let timestamp = this.safeInteger (trade, 'lastUpdated'); + if (timestamp !== undefined) { + timestamp /= 1000; + } + const price = this.safeFloat (trade, 'price'); + const id = this.safeString (trade, 'id'); + const side = this.safeString (trade, 'side'); + let symbol = undefined; + let base = undefined; + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + } + const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); + const amount = this.fromWei (this.safeString (trade, 'amount'), 'ether', baseDecimals); + let cost = undefined; + if (amount !== undefined && price !== undefined) { + cost = amount * price; + } + const takerOrMaker = 'taker'; + return { + 'id': id, + 'order': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + }; + const response = await this.publicGetTradeHistory (this.extend (request, params)); + // + // [ + // { + // "id": "37212", + // "transactionHash": "0x5e6e75e1aa681b51b034296f62ac19be7460411a2ad94042dd8ba637e13eac0c", + // "amount": "300000000000000000", + // "price": "0.00052718", + // "status": "filled", // filled | settled | failed + // "lastUpdated": "1520265048996" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + const errorMessage = this.id + ' createOrder() requires `exchange.walletAddress` and `exchange.privateKey`. The .walletAddress should be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377". The .privateKey for that wallet should be a "0x"-prefixed hexstring like "0xe4f40d465efa94c98aec1a51f574329344c772c1bce33be07fa20a56795fdd09".'; + if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { + throw new InvalidAddress (errorMessage); + } + if (!this.privateKey || (this.privateKey.indexOf ('0x') !== 0)) { + throw new InvalidAddress (errorMessage); + } + const orderParams = await this.fetchOrderParamsToSign (symbol, type, side, amount, price, params); + const unsignedOrder = orderParams['unsignedZeroExOrder']; + if (unsignedOrder === undefined) { + throw new OrderNotFillable (this.id + ' ' + type + ' order to ' + side + ' ' + symbol + ' is not fillable at the moment'); + } + const signedOrder = await this.signZeroExOrderV2 (unsignedOrder, this.privateKey); + const id = this.safeString (signedOrder, 'orderHash'); + await this.postSignedOrder (signedOrder, orderParams, params); + const order = await this.fetchOrder (id); + order['type'] = type; + return order; + } + + async fetchOrderParamsToSign (symbol, type, side, amount, price = undefined, params = {}) { + if (side !== 'buy' && side !== 'sell') { + throw new ExchangeError (side + ' is not valid side param. Use \'buy\' or \'sell\''); + } + if (type !== 'market' && type !== 'limit') { + throw new ExchangeError (type + ' is not valid type param. Use \'market\' or \'limit\''); + } + if (type === 'limit' && price === undefined) { + throw new ExchangeError ('Price is not provided for limit order'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const baseDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); + const request = { + 'walletAddress': this.walletAddress.toLowerCase (), // Your Wallet Address + 'baseTokenAddress': market['baseId'], // Base token address + 'quoteTokenAddress': market['quoteId'], // Quote token address + 'side': side, // "buy" or "sell" + 'amount': this.toWei (this.amountToPrecision (symbol, amount), 'ether', baseDecimals), // Base token amount in wei + }; + let method = undefined; + if (type === 'limit') { + method = 'privateGetOrderUnsigned'; + request['price'] = this.priceToPrecision (symbol, price); + } else if (type === 'market') { + method = 'privateGetOrderUnsignedMarket'; + } else { + throw new ExchangeError ('Unsupported order type: ' + type); + } + const response = await this[method] (this.extend (request, params)); + return response; + } + + async postSignedOrder (signedOrder, requestParams, params = {}) { + let request = requestParams; + request['signedZeroExOrder'] = signedOrder; + request = this.omit (request, 'unsignedZeroExOrder'); + const response = await this.privatePostOrder (this.extend (request, params)); + return response; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderHash': id, + }; + const response = await this.privateDeleteOrderOrderHash (this.extend (request, params)); + // + // { + // "canceledOrder": { + // "orderHash": "0x3d6b287c1dc79262d2391ae2ca9d050fdbbab2c8b3180e4a46f9f321a7f1d7a9", + // "amount": "100000000000" + // } + // } + // + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.extend (this.parseOrder (response['canceledOrder'], market), { + 'status': 'canceled', + }); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + const response = await this.privateDeleteOrder (params); + // + // [{ + // "canceledOrder": { + // "orderHash": "0x3d6b287c1dc79262d2391ae2ca9d050fdbbab2c8b3180e4a46f9f321a7f1d7a9", + // "amount": "100000000000" + // } + // }] + // + return response; + } + + parseOrder (order, market = undefined) { + const zeroExOrder = this.safeValue (order, 'zeroExOrder'); + let id = this.safeString (order, 'orderHash'); + if ((id === undefined) && (zeroExOrder !== undefined)) { + id = this.safeString (zeroExOrder, 'orderHash'); + } + const side = this.safeString (order, 'side'); + const type = this.safeString (order, 'type'); // injected from outside + let timestamp = this.safeInteger (order, 'creationTimestamp'); + if (timestamp !== 'undefined') { + timestamp = parseInt (timestamp / 1000); + } + let symbol = undefined; + const baseId = this.safeString (order, 'baseTokenAddress'); + const quoteId = this.safeString (order, 'quoteTokenAddress'); + let marketId = undefined; + if (baseId !== undefined && quoteId !== undefined) { + marketId = baseId + '/' + quoteId; + } + market = this.safeValue (this.markets_by_id, marketId, market); + let base = undefined; + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + } + const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); + const price = this.safeFloat (order, 'price'); + const filledAmount = this.fromWei (this.safeString (order, 'filledAmount'), 'ether', baseDecimals); + const settledAmount = this.fromWei (this.safeString (order, 'settledAmount'), 'ether', baseDecimals); + const confirmedAmount = this.fromWei (this.safeString (order, 'confirmedAmount'), 'ether', baseDecimals); + const failedAmount = this.fromWei (this.safeString (order, 'failedAmount'), 'ether', baseDecimals); + const deadAmount = this.fromWei (this.safeString (order, 'deadAmount'), 'ether', baseDecimals); + const prunedAmount = this.fromWei (this.safeString (order, 'prunedAmount'), 'ether', baseDecimals); + const amount = this.fromWei (this.safeString (order, 'initialAmount'), 'ether', baseDecimals); + const filled = this.sum (filledAmount, settledAmount, confirmedAmount); + let remaining = undefined; + let lastTradeTimestamp = undefined; + const timeline = this.safeValue (order, 'timeline'); + let trades = undefined; + let status = undefined; + if (timeline !== undefined) { + const numEvents = timeline.length; + if (numEvents > 0) { + const timelineEventsGroupedByAction = this.groupBy (timeline, 'action'); + if ('error' in timelineEventsGroupedByAction) { + status = 'failed'; + } + if ('filled' in timelineEventsGroupedByAction) { + const fillEvents = this.safeValue (timelineEventsGroupedByAction, 'filled'); + const numFillEvents = fillEvents.length; + lastTradeTimestamp = this.safeInteger (fillEvents[numFillEvents - 1], 'timestamp'); + lastTradeTimestamp = (lastTradeTimestamp !== undefined) ? lastTradeTimestamp : lastTradeTimestamp; + trades = []; + for (let i = 0; i < numFillEvents; i++) { + const trade = this.parseTrade (this.extend (fillEvents[i], { + 'price': price, + }), market); + trades.push (this.extend (trade, { + 'order': id, + 'type': type, + 'side': side, + })); + } + } + } + } + let cost = undefined; + if (filled !== undefined) { + if (remaining === undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + } + if (price !== undefined) { + cost = filled * price; + } + } + let fee = undefined; + const feeCost = this.safeString (order, 'feeAmount'); + if (feeCost !== undefined) { + const feeOption = this.safeString (order, 'feeOption'); + let feeCurrency = undefined; + if (feeOption === 'feeInNative') { + if (market !== undefined) { + feeCurrency = market['base']; + } + } else if (feeOption === 'feeInZRX') { + feeCurrency = 'ZRX'; + } else { + throw new NotSupported (this.id + ' encountered an unsupported order fee option: ' + feeOption); + } + const feeDecimals = this.safeInteger (this.options['decimals'], feeCurrency, 18); + fee = { + 'cost': this.fromWei (feeCost, 'ether', feeDecimals), + 'currency': feeCurrency, + }; + } + const amountPrecision = market ? market['precision']['amount'] : 8; + if (remaining !== undefined) { + if (status === undefined) { + status = 'open'; + const rest = remaining - failedAmount - deadAmount - prunedAmount; + if (rest < Math.pow (10, -amountPrecision)) { + status = (filled < amount) ? 'canceled' : 'closed'; + } + } + } + const result = { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + return result; + } + + async fetchOpenOrder (id, symbol = undefined, params = {}) { + const method = this.options['fetchOrderMethod']; + return await this[method] (id, symbol, this.extend ({ + 'openAmount': 1, + }, params)); + } + + async fetchClosedOrder (id, symbol = undefined, params = {}) { + const method = this.options['fetchOrderMethod']; + return await this[method] (id, symbol, this.extend (params)); + } + + async fetchOrderFromHistory (id, symbol = undefined, params = {}) { + const request = { + 'orderHash': id, + }; + const orders = await this.fetchOrders (symbol, undefined, undefined, this.extend (request, params)); + const ordersById = this.indexBy (orders, 'id'); + if (id in ordersById) { + return ordersById[id]; + } + throw new OrderNotFound (this.id + ' could not find order ' + id + ' in order history'); + } + + async fetchOrderById (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderHash': id, + }; + const response = await this.publicGetOrderOrderHash (this.extend (request, params)); + // { + // baseTokenAddress: '0xb18845c260f680d5b9d84649638813e342e4f8c9', + // quoteTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + // side: 'sell', + // price: '30', + // feeTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + // amount: '500000000000000000', + // created: '1547194003', + // expires: '1549786003', + // zeroExOrder: { + // salt: '71810414258284992779348693906799008280152689028521273772736250669496045815907', + // maker: '0xfa1a3371bcbfcf3deaa8a6f67784bfbe5b886d7f', + // taker: '0x77b18613579d49f252bd237ef113884eb37a7090', + // makerFee: '0', + // takerFee: '0', + // orderHash: '0x368540323af55868dd9ce6ac248e6a91d9b7595252ca061c4ada7612b09af1cf', + // feeRecipient: '0x88a64b5e882e5ad851bea5e7a3c8ba7c523fecbe', + // makerTokenAmount: '500000000000000000', + // takerTokenAmount: '14845250714350000000', + // makerTokenAddress: '0xb18845c260f680d5b9d84649638813e342e4f8c9', + // takerTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + // exchangeContractAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', + // expirationUnixTimestampSec: '1549789602' + // }, + // feeAmount: '154749285650000000', + // feeOption: 'feeInNative', + // cancelAfter: '1549786003' + // } + return this.parseOrder (response); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const request = { + 'orderHash': id, + }; + const orders = await this.fetchOrders (symbol, undefined, undefined, this.extend (request, params)); + const numOrders = orders.length; + if (numOrders !== 1) { + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + return orders[0]; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['baseTokenAddress'] = market['baseId']; + request['quoteTokenAddress'] = market['quoteId']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrderHistory (this.extend (request, params)); + // + // [ + // { + // "orderHash": "0x94629386298dee69ae63cd3e414336ae153b3f02cffb9ffc53ad71e166615618", + // "baseTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d", + // "quoteTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990", + // "side": "buy", + // "openAmount": "10000000000000000000", + // "filledAmount": "0", + // "reservedAmount": "0", + // "settledAmount": "0", + // "confirmedAmount": "0", + // "deadAmount": "0", + // "price": "0.00050915", + // "timeline": [ + // { + // "action": "placed", + // "amount": "10000000000000000000", + // "timestamp": "1512929327792" + // } + // ] + // } + // ] + // + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openAmount': 1, // returns open orders with remaining openAmount >= 1 + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openAmount': 0, // returns closed orders with remaining openAmount === 0 + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.seconds ().toString (); + let prehash = this.apiKey + timestamp + method; + if (method === 'POST') { + body = this.json (query); + prehash += body; + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + prehash += this.json ({}); + } + const signature = this.hmac (this.encode (prehash), this.encode (this.secret), 'sha256', 'base64'); + headers = { + 'TOX-ACCESS-KEY': this.apiKey, + 'TOX-ACCESS-SIGN': signature, + 'TOX-ACCESS-TIMESTAMP': timestamp, + 'Content-Type': 'application/json', + }; + } else if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // code 401 and plain body 'Authentication failed' (with single quotes) + // this error is sent if you do not submit a proper Content-Type + if (body === "'Authentication failed'") { + throw new AuthenticationError (this.id + ' ' + body); + } + const message = this.safeString (response, 'message'); + if (message !== undefined) { + // + // {"message":"Schema validation failed for 'query'","errors":[{"name":"required","argument":"startTime","message":"requires property \"startTime\"","instance":{"baseTokenAddress":"0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570","quoteTokenAddress":"0xd0a1e359811322d97991e03f863a0c30c2cf029c","interval":"300"},"property":"instance"}]} + // {"message":"Logic validation failed for 'query'","errors":[{"message":"startTime should be between 0 and current date","type":"startTime"}]} + // {"message":"Order not found","errors":[]} + // {"message":"Orderbook exhausted for intent MARKET_INTENT:8yjjzd8b0e8yjjzd8b0fjjzd8b0g"} + // {"message":"Intent validation failed.","errors":[{"message":"Greater than available wallet balance.","type":"walletBaseTokenAmount"}]} + // {"message":"Schema validation failed for 'body'","errors":[{"name":"anyOf","argument":["[subschema 0]","[subschema 1]","[subschema 2]"],"message":"is not any of [subschema 0],[subschema 1],[subschema 2]","instance":{"signedTargetOrder":{"error":{"message":"Unsigned target order validation failed.","errors":[{"message":"Greater than available wallet balance.","type":"walletBaseTokenAmount"}]},"maker":"0x1709c02cd7327d391a39a7671af8a91a1ef8a47b","orderHash":"0xda007ea8b5eca71ac96fe4072f7c1209bb151d898a9cc89bbeaa594f0491ee49","ecSignature":{"v":27,"r":"0xb23ce6c4a7b5d51d77e2d00f6d1d472a3b2e72d5b2be1510cfeb122f9366b79e","s":"0x07d274e6d7a00b65fc3026c2f9019215b1e47a5ac4d1f05e03f90550d27109be"}}},"property":"instance"}]} + // {"message":"Schema validation failed for 'params'","errors":[{"name":"pattern","argument":"^0x[0-9a-fA-F]{64}$","message":"does not match pattern \"^0x[0-9a-fA-F]{64}$\"","instance":"1","property":"instance.orderHash"}]} + // + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],163:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, BadRequest, OrderNotFound, InvalidAddress } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class therock extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'therock', + 'name': 'TheRockTrading', + 'countries': [ 'MT' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchMyTrades': true, + 'fetchLedger': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': 'emulated', + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg', + 'api': 'https://api.therocktrading.com', + 'www': 'https://therocktrading.com', + 'doc': [ + 'https://api.therocktrading.com/doc/v1/index.html', + 'https://api.therocktrading.com/doc/', + ], + }, + 'api': { + 'public': { + 'get': [ + 'funds', + 'funds/{id}/orderbook', + 'funds/{id}/ticker', + 'funds/{id}/trades', + 'funds/tickers', + ], + }, + 'private': { + 'get': [ + 'balances', + 'balances/{id}', + 'discounts', + 'discounts/{id}', + 'funds', + 'funds/{id}', + 'funds/{id}/trades', + 'funds/{fund_id}/orders', + 'funds/{fund_id}/orders/{id}', + 'funds/{fund_id}/position_balances', + 'funds/{fund_id}/positions', + 'funds/{fund_id}/positions/{id}', + 'transactions', + 'transactions/{id}', + 'withdraw_limits/{id}', + 'withdraw_limits', + ], + 'post': [ + 'atms/withdraw', + 'funds/{fund_id}/orders', + ], + 'delete': [ + 'funds/{fund_id}/orders/{id}', + 'funds/{fund_id}/orders/remove_all', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.0005, + 'BCH': 0.0005, + 'PPC': 0.02, + 'ETH': 0.001, + 'ZEC': 0.001, + 'LTC': 0.002, + 'EUR': 2.5, // worst-case scenario: https://therocktrading.com/en/pages/fees + }, + 'deposit': { + 'BTC': 0, + 'BCH': 0, + 'PPC': 0, + 'ETH': 0, + 'ZEC': 0, + 'LTC': 0, + 'EUR': 0, + }, + }, + }, + 'exceptions': { + 'exact': { + 'Request already running': BadRequest, + 'cannot specify multiple address types': BadRequest, + 'Currency is not included in the list': BadRequest, + 'Record not found': OrderNotFound, + }, + 'broad': { + 'before must be greater than after param': BadRequest, + 'must be shorter than 60 days': BadRequest, + 'must be a multiple of (period param) in minutes': BadRequest, + 'Address allocation limit reached for currency': InvalidAddress, + 'is not a valid value for param currency': BadRequest, + ' is invalid': InvalidAddress, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetFunds (params); + // + // { funds: [ { id: "BTCEUR", + // description: "Trade Bitcoin with Euro", + // type: "currency", + // base_currency: "EUR", + // trade_currency: "BTC", + // buy_fee: 0.2, + // sell_fee: 0.2, + // minimum_price_offer: 0.01, + // minimum_quantity_offer: 0.0005, + // base_currency_decimals: 2, + // trade_currency_decimals: 4, + // leverages: [] }, + // { id: "LTCEUR", + // description: "Trade Litecoin with Euro", + // type: "currency", + // base_currency: "EUR", + // trade_currency: "LTC", + // buy_fee: 0.2, + // sell_fee: 0.2, + // minimum_price_offer: 0.01, + // minimum_quantity_offer: 0.01, + // base_currency_decimals: 2, + // trade_currency_decimals: 2, + // leverages: [] } ] } + // + const markets = this.safeValue (response, 'funds'); + const result = []; + if (markets === undefined) { + throw new ExchangeError (this.id + ' fetchMarkets got an unexpected response'); + } else { + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'trade_currency'); + const quoteId = this.safeString (market, 'base_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const buy_fee = this.safeFloat (market, 'buy_fee'); + const sell_fee = this.safeFloat (market, 'sell_fee'); + let taker = Math.max (buy_fee, sell_fee); + taker = taker / 100; + const maker = taker; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': true, + 'maker': maker, + 'taker': taker, + 'precision': { + 'amount': this.safeInteger (market, 'trade_currency_decimals'), + 'price': this.safeInteger (market, 'base_currency_decimals'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minimum_quantity_offer'), + 'max': undefined, + }, + 'price': { + 'min': this.safeFloat (market, 'minimum_price_offer'), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalances (params); + const balances = this.safeValue (response, 'balances', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'trading_balance'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const orderbook = await this.publicGetFundsIdOrderbook (this.extend (request, params)); + const timestamp = this.parse8601 (this.safeString (orderbook, 'date')); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.parse8601 (ticker['date']); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': this.safeFloat (ticker, 'close'), // previous day close, if any + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume_traded'), + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetFundsTickers (params); + const tickers = this.indexBy (response['tickers'], 'fund_id'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const ticker = await this.publicGetFundsIdTicker (this.extend ({ + 'id': market['id'], + }, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades, fetchOrder trades + // + // { id: 4493548, + // fund_id: "ETHBTC", + // amount: 0.203, + // price: 0.02783576, + // side: "buy", + // dark: false, + // date: "2018-11-30T08:19:18.236Z" } + // + // fetchMyTrades + // + // { id: 237338, + // fund_id: "BTCEUR", + // amount: 0.348, + // price: 348, + // side: "sell", + // dark: false, + // order_id: 14920648, + // date: "2015-06-03T00:49:49.000Z", + // transactions: [ { id: 2770768, + // date: "2015-06-03T00:49:49.000Z", + // type: "sold_currency_to_fund", + // price: 121.1, + // currency: "EUR" }, + // { id: 2770769, + // date: "2015-06-03T00:49:49.000Z", + // type: "released_currency_to_fund", + // price: 0.348, + // currency: "BTC" }, + // { id: 2770772, + // date: "2015-06-03T00:49:49.000Z", + // type: "paid_commission", + // price: 0.06, + // currency: "EUR", + // trade_id: 440492 } ] } + // + if (!market) { + market = this.markets_by_id[trade['fund_id']]; + } + const timestamp = this.parse8601 (this.safeString (trade, 'date')); + const id = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'order_id'); + const side = this.safeString (trade, 'side'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let fee = undefined; + let feeCost = undefined; + const transactions = this.safeValue (trade, 'transactions', []); + const transactionsByType = this.groupBy (transactions, 'type'); + const feeTransactions = this.safeValue (transactionsByType, 'paid_commission', []); + for (let i = 0; i < feeTransactions.length; i++) { + if (feeCost === undefined) { + feeCost = 0; + } + feeCost = this.sum (feeCost, this.safeFloat (feeTransactions[i], 'price')); + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': market['quote'], + }; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + parseLedgerEntryDirection (direction) { + const directions = { + 'affiliate_earnings': 'in', + 'atm_payment': 'in', + 'bought_currency_from_fund': 'out', + 'bought_shares': 'out', + 'paid_commission': 'out', + 'paypal_payment': 'in', + 'pos_payment': 'in', + 'released_currency_to_fund': 'out', + 'rollover_commission': 'out', + 'sold_currency_to_fund': 'in', + 'sold_shares': 'in', + 'transfer_received': 'in', + 'transfer_sent': 'out', + 'withdraw': 'out', + // commented types will be shown as-is + // 'acquired_currency_from_fund': '', + // 'acquired_insurance': '', + // 'dividend_distributed_to_holders': '', + // 'dividend_from_shares': '', + // 'exposed_position': '', + // 'insurances_reimbursement': '', + // 'lent_currency': '', + // 'linden_lab_assessment': '', + // 'position_transfer_received': '', + // 'return_lent_currency': '', + // 'returned_lent_currency': '', + // 'the_rock_assessment': '', + }; + return this.safeString (directions, direction, direction); + } + + parseLedgerEntryType (type) { + const types = { + 'affiliate_earnings': 'referral', + 'atm_payment': 'transaction', + 'bought_currency_from_fund': 'trade', + 'bought_shares': 'trade', + 'paid_commission': 'fee', + 'paypal_payment': 'transaction', + 'pos_payment': 'transaction', + 'released_currency_to_fund': 'trade', + 'rollover_commission': 'fee', + 'sold_currency_to_fund': 'trade', + 'sold_shares': 'trade', + 'transfer_received': 'transfer', + 'transfer_sent': 'transfer', + 'withdraw': 'transaction', + // commented types will be shown as-is + // 'acquired_currency_from_fund': '', + // 'acquired_insurance': '', + // 'dividend_distributed_to_holders': '', + // 'dividend_from_shares': '', + // 'exposed_position': '', + // 'insurances_reimbursement': '', + // 'lent_currency': '', + // 'linden_lab_assessment': '', + // 'position_transfer_received': '', + // 'return_lent_currency': '', + // 'returned_lent_currency': '', + // 'the_rock_assessment': '', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // withdrawal + // + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // } + // + // deposit + // + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // } + // + // trade fee + // + // { + // "id": 21311221, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "paid_commission", + // "price": 0.0001, + // "fund_id": "BTCEUR", + // "order_id": 12832371, + // "trade_id": 12923212, + // "currency": "BTC", + // "transfer_detail": null + // } + // + const id = this.safeString (item, 'id'); + let referenceId = undefined; + let type = this.safeString (item, 'type'); + const direction = this.parseLedgerEntryDirection (type); + type = this.parseLedgerEntryType (type); + if (type === 'trade' || type === 'fee') { + referenceId = this.safeString (item, 'trade_id'); + } + const currencyId = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const amount = this.safeFloat (item, 'price'); + const timestamp = this.parse8601 (this.safeString (item, 'date')); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'direction': direction, + 'account': undefined, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': undefined, + 'after': undefined, + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page': 1, + // 'fund_id': 'ETHBTC', // filter by fund symbol + // 'currency': 'BTC', // filter by currency + // 'after': '2015-02-06T08:47:26Z', // filter after a certain timestamp + // 'before': '2015-02-06T08:47:26Z', + // 'type': 'withdraw', + // 'order_id': '12832371', // filter by a specific order ID + // 'trade_id': '12923212', // filter by a specific trade ID + // 'transfer_method': 'bitcoin', // wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin + // 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // filter by a specific recipient (e.g. Bitcoin address, IBAN) + // 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', // filter by a specific transfer ID (e.g. Bitcoin TX hash) + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.privateGetTransactions (this.extend (request, params)); + // + // { + // "transactions": [ + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // }, + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // }, + // { + // "id": 21311221, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "paid_commission", + // "price": 0.0001, + // "fund_id": "BTCEUR", + // "order_id": 12832371, + // "trade_id": 12923212, + // "currency": "BTC", + // "transfer_detail": null + // } + // ], + // "meta": { + // "total_count": 1221, + // "first": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "previous": null, + // "current": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "next": { "page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2" }, + // "last": { "page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221" } + // } + // } + // + const transactions = this.safeValue (response, 'transactions', []); + return this.parseLedger (transactions, currency, since, limit); + } + + parseTransactionType (type) { + const types = { + 'withdraw': 'withdrawal', + 'atm_payment': 'deposit', + }; + return this.safeString (types, type, type); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchWithdrawals + // + // // fiat + // + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // } + // + // { + // "id": 12564223, + // "date": "2017-08-07T08:13:50.023Z", + // "note": "GB7IDL401573388", + // "type": "withdraw", + // "price": 4345.93, + // "fund_id": null, + // "currency": "EUR", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "id": "EXECUTEDBUTUNCHECKED", + // "method": "wire_transfer", + // "recipient": "GB7IDL401573388", + // "confirmations": 0 + // } + // } + // + // // crypto + // + // { + // id: 20914695, + // date: '2018-02-24T07:13:23.002Z', + // type: 'withdraw', + // price: 2.70883607, + // currency: 'BCH', + // fund_id: null, + // order_id: null, + // trade_id: null, + // note: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', + // transfer_detail: { + // method: 'bitcoin_cash', + // id: '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', + // recipient: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', + // confirmations: 0 + // } + // } + // + // + // fetchDeposits + // + // // fiat + // + // { + // id: 16176632, + // date: '2017-11-20T21:00:13.355Z', + // type: 'atm_payment', + // price: 5000, + // currency: 'EUR', + // fund_id: null, + // order_id: null, + // trade_id: null, + // note: 'Mistral deposit', + // transfer_detail: { + // method: 'wire_transfer', + // id: '972JQ49337DX769T', + // recipient: null, + // confirmations: 0 + // } + // } + // + // // crypto + // + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // } + // + const id = this.safeString (transaction, 'id'); + const type = this.parseTransactionType (this.safeString (transaction, 'type')); + const detail = this.safeValue (transaction, 'transfer_detail', {}); + const method = this.safeString (detail, 'method'); + let txid = undefined; + let address = undefined; + if (method !== undefined) { + if (method !== 'wire_transfer') { + txid = this.safeString (detail, 'id'); + address = this.safeString (detail, 'recipient'); + } + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const amount = this.safeFloat (transaction, 'price'); + const timestamp = this.parse8601 (this.safeString (transaction, 'date')); + const status = 'ok'; + // todo parse tags + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'addressFrom': undefined, + 'addressTo': address, + 'address': address, + 'tagFrom': undefined, + 'tagTo': undefined, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'withdraw', + }; + return await this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'atm_payment', + }; + return await this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page': 1, + // 'fund_id': 'ETHBTC', // filter by fund symbol + // 'currency': 'BTC', // filter by currency + // 'after': '2015-02-06T08:47:26Z', // filter after a certain timestamp + // 'before': '2015-02-06T08:47:26Z', + // 'type': 'withdraw', + // 'order_id': '12832371', // filter by a specific order ID + // 'trade_id': '12923212', // filter by a specific trade ID + // 'transfer_method': 'bitcoin', // wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin + // 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // filter by a specific recipient (e.g. Bitcoin address, IBAN) + // 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', // filter by a specific transfer ID (e.g. Bitcoin TX hash) + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + params = this.extend (request, params); + const response = await this.privateGetTransactions (params); + // + // { + // "transactions": [ + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // }, + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // }, + // { + // "id": 21311221, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "paid_commission", + // "price": 0.0001, + // "fund_id": "BTCEUR", + // "order_id": 12832371, + // "trade_id": 12923212, + // "currency": "BTC", + // "transfer_detail": null + // } + // ], + // "meta": { + // "total_count": 1221, + // "first": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "previous": null, + // "current": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "next": { "page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2" }, + // "last": { "page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221" } + // } + // } + // + const transactions = this.safeValue (response, 'transactions', []); + const transactionTypes = [ 'withdraw', 'atm_payment' ]; + const depositsAndWithdrawals = this.filterByArray (transactions, 'type', transactionTypes, false); + return this.parseTransactions (depositsAndWithdrawals, currency, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'active': 'open', + 'executed': 'closed', + 'deleted': 'canceled', + // don't know what this status means + // 'conditional': '?', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "id": 4325578, + // "fund_id":"BTCEUR", + // "side":"buy", + // "type":"limit", + // "status":"executed", + // "price":0.0102, + // "amount": 50.0, + // "amount_unfilled": 0.0, + // "conditional_type": null, + // "conditional_price": null, + // "date":"2015-06-03T00:49:48.000Z", + // "close_on": nil, + // "leverage": 1.0, + // "position_id": null, + // "trades": [ + // { + // "id":237338, + // "fund_id":"BTCEUR", + // "amount":50, + // "price":0.0102, + // "side":"buy", + // "dark":false, + // "date":"2015-06-03T00:49:49.000Z" + // } + // ] + // } + // + const id = this.safeString (order, 'id'); + let symbol = undefined; + const marketId = this.safeString (order, 'fund_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.parse8601 (this.safeString (order, 'date')); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + const amount = this.safeFloat (order, 'amount'); + const remaining = this.safeFloat (order, 'amount_unfilled'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + const price = this.safeFloat (order, 'price'); + let trades = this.safeValue (order, 'trades'); + let cost = undefined; + let average = undefined; + let lastTradeTimestamp = undefined; + if (trades !== undefined) { + const numTrades = trades.length; + if (numTrades > 0) { + trades = this.parseTrades (trades, market, undefined, undefined, { + 'orderId': id, + }); + // todo: determine the cost and the average price from trades + cost = 0; + filled = 0; + for (let i = 0; i < numTrades; i++) { + const trade = trades[i]; + cost = this.sum (cost, trade['cost']); + filled = this.sum (filled, trade['amount']); + } + if (filled > 0) { + average = cost / filled; + } + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + } else { + cost = 0; + } + } + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'average': average, + 'remaining': remaining, + 'fee': undefined, + 'trades': trades, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'active', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'executed', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'fund_id': market['id'], + // 'after': '2015-02-06T08:47:26Z', + // 'before': '2015-02-06T08:47:26Z' + // 'status': 'active', // 'executed', 'conditional' + // 'side': 'buy', // 'sell' + // 'position_id': 123, // filter orders by margin position id + }; + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.privateGetFundsFundIdOrders (this.extend (request, params)); + // + // { + // orders: [ + // { + // id: 299333648, + // fund_id: 'BTCEUR', + // side: 'sell', + // type: 'limit', + // status: 'executed', + // price: 5821, + // amount: 0.1, + // amount_unfilled: 0, + // conditional_type: null, + // conditional_price: null, + // date: '2018-06-18T17:38:16.129Z', + // close_on: null, + // dark: false, + // leverage: 1, + // position_id: 0 + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'fund_id': market['id'], + }; + const response = await this.privatePostFundsFundIdOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + price = 0; + } + const request = { + 'fund_id': this.marketId (symbol), + 'side': side, + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostFundsFundIdOrders (this.extend (request, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + 'fund_id': this.marketId (symbol), + }; + const response = await this.privateDeleteFundsFundIdOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + if (limit !== undefined) { + request['per_page'] = limit; // default 25 max 200 + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.privateGetFundsIdTrades (this.extend (request, params)); + // + // { trades: [ { id: 237338, + // fund_id: "BTCEUR", + // amount: 0.348, + // price: 348, + // side: "sell", + // dark: false, + // order_id: 14920648, + // date: "2015-06-03T00:49:49.000Z", + // transactions: [ { id: 2770768, + // date: "2015-06-03T00:49:49.000Z", + // type: "sold_currency_to_fund", + // price: 121.1, + // currency: "EUR" }, + // { id: 2770769, + // date: "2015-06-03T00:49:49.000Z", + // type: "released_currency_to_fund", + // price: 0.348, + // currency: "BTC" }, + // { id: 2770772, + // date: "2015-06-03T00:49:49.000Z", + // type: "paid_commission", + // price: 0.06, + // currency: "EUR", + // trade_id: 440492 } ] } ], + // meta: { total_count: 31, + // first: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" }, + // previous: null, + // current: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" }, + // next: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" }, + // last: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" } } } + // + return this.parseTrades (response['trades'], market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + if (limit !== undefined) { + request['per_page'] = limit; // default 25 max 200 + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.publicGetFundsIdTrades (this.extend (request, params)); + // + // { trades: [ { id: 4493548, + // fund_id: "ETHBTC", + // amount: 0.203, + // price: 0.02783576, + // side: "buy", + // dark: false, + // date: "2018-11-30T08:19:18.236Z" }, + // { id: 4492926, + // fund_id: "ETHBTC", + // amount: 0.04, + // price: 0.02767034, + // side: "buy", + // dark: false, + // date: "2018-11-30T07:03:03.897Z" } ], + // meta: { total_count: null, + // first: { page: 1, + // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" }, + // previous: null, + // current: { page: 1, + // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" }, + // next: { page: 2, + // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=2" }, + // last: null } } + // + return this.parseTrades (response['trades'], market, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + headers = (headers === undefined) ? {} : headers; + if (api === 'private') { + this.checkRequiredCredentials (); + if (Object.keys (query).length) { + if (method === 'POST') { + body = this.json (query); + headers['Content-Type'] = 'application/json'; + } else { + const queryString = this.rawencode (query); + if (queryString.length) { + url += '?' + queryString; + } + } + } + const nonce = this.nonce ().toString (); + const auth = nonce + url; + headers['X-TRT-KEY'] = this.apiKey; + headers['X-TRT-NONCE'] = nonce; + headers['X-TRT-SIGN'] = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); + } else if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.rawencode (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // { + // "errors": + // [ + // { "message": ":currency is not a valid value for param currency","code": "11","meta": { "key":"currency","value":":currency"} }, + // { "message": "Address allocation limit reached for currency :currency.","code": "13" }, + // { "message": "Request already running", "code": "50"}, + // { "message": "cannot specify multiple address types", "code": "12" }, + // { "message": ":address_type is invalid", "code": "12" } + // ] + // } + // + const errors = this.safeValue (response, 'errors', []); + const numErrors = errors.length; + if (numErrors > 0) { + const feedback = this.id + ' ' + body; + // here we throw the first error we can identify + for (let i = 0; i < numErrors; i++) { + const error = errors[i]; + const message = this.safeString (error, 'message'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],164:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class tidebit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'tidebit', + 'name': 'TideBit', + 'countries': [ 'HK' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'fetchDepositAddress': true, + 'CORS': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '2h': '120', + '4h': '240', + '12h': '720', + '1d': '1440', + '3d': '4320', + '1w': '10080', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/39034921-e3acf016-4480-11e8-9945-a6086a1082fe.jpg', + 'api': 'https://www.tidebit.com', + 'www': 'https://www.tidebit.com', + 'doc': [ + 'https://www.tidebit.com/documents/api/guide', + 'https://www.tidebit.com/swagger/#/default', + ], + 'referral': 'http://bit.ly/2IX0LrM', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'tickers', + 'tickers/{market}', + 'timestamp', + 'trades', + 'trades/{market}', + 'order_book', + 'order', + 'k_with_pending_trades', + 'k', + 'depth', + ], + 'post': [], + }, + 'private': { + 'get': [ + 'addresses/{address}', + 'deposits/history', + 'deposits/get_deposit', + 'deposits/deposit_address', + 'historys/orders', + 'historys/vouchers', + 'historys/accounts', + 'historys/snapshots', + 'linkage/get_status', + 'members/me', + 'order', + 'orders', + 'partners/orders/{id}/trades', + 'referral_commissions/get_undeposited', + 'referral_commissions/get_graph_data', + 'trades/my', + 'withdraws/bind_account_list', + 'withdraws/get_withdraw_account', + 'withdraws/fetch_bind_info', + ], + 'post': [ + 'deposits/deposit_cash', + 'favorite_markets/update', + 'order/delete', + 'orders', + 'orders/multi', + 'orders/clear', + 'referral_commissions/deposit', + 'withdraws/apply', + 'withdraws/bind_bank', + 'withdraws/bind_address', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.3 / 100, + 'taker': 0.3 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': true, + 'withdraw': {}, // There is only 1% fee on withdrawals to your bank account. + }, + }, + 'exceptions': { + '2002': InsufficientFunds, + '2003': OrderNotFound, + }, + }); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetDepositAddress (this.extend (request, params)); + if ('success' in response) { + if (response['success']) { + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'addressTag'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + } + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'id'); + const symbol = this.safeString (market, 'name'); + const [ baseId, quoteId ] = symbol.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetMembersMe (params); + const balances = this.safeValue (response, 'accounts'); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 300 + } + request['market'] = market['id']; + const response = await this.publicGetDepth (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'at'); + ticker = this.safeValue (ticker, 'ticker', {}); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'ask': this.safeFloat (ticker, 'sell'), + 'bidVolume': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'change': undefined, + 'percentage': undefined, + 'previousClose': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTickers (params); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + let symbol = id; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTickersMarket (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + const cost = this.safeFloat (trade, 'funds'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 30; // default is 30 + } + const request = { + 'market': market['id'], + 'period': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } else { + request['timestamp'] = 1800000; + } + const response = await this.publicGetK (this.extend (request, params)); + if (response === 'null') { + return []; + } + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'done': 'closed', + 'wait': 'open', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const marketId = order['market']; + symbol = this.markets_by_id[marketId]['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'ord_type'); + const side = this.safeString (order, 'side'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + const filled = this.safeFloat (order, 'executed_volume'); + const remaining = this.safeFloat (order, 'remaining_volume'); + let cost = undefined; + if (price !== undefined) { + if (filled !== undefined) { + cost = price * filled; + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'cost': cost, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'side': side, + 'volume': amount.toString (), + 'ord_type': type, + }; + if (type === 'limit') { + request['price'] = price.toString (); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const market = this.markets_by_id[response['market']]; + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const result = await this.privatePostOrderDelete (this.extend (request, params)); + const order = this.parseOrder (result); + const status = this.safeString (order, 'status'); + if (status === 'closed' || status === 'canceled') { + throw new OrderNotFound (this.id + ' ' + this.json (order)); + } + return order; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const id = this.safeString (params, 'id'); + if (id === undefined) { + throw new ExchangeError (this.id + ' withdraw() requires an extra `id` param (withdraw account id according to withdraws/bind_account_list endpoint'); + } + const request = { + 'id': id, + 'currency_type': 'coin', // or 'cash' + 'currency': currency['id'], + 'body': amount, + // 'address': address, // they don't allow withdrawing to direct addresses? + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const result = await this.privatePostWithdrawsApply (this.extend (request, params)); + return { + 'info': result, + 'id': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + encodeParams (params) { + return this.urlencode (this.keysort (params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = '/' + 'api/' + this.version + '/' + this.implodeParams (path, params) + '.json'; + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const sortedByKey = this.keysort (this.extend ({ + 'access_key': this.apiKey, + 'tonce': nonce, + }, params)); + const query = this.urlencode (sortedByKey); + const payload = method + '|' + request + '|' + query; + const signature = this.hmac (this.encode (payload), this.encode (this.secret)); + const suffix = query + '&signature=' + signature; + if (method === 'GET') { + url += '?' + suffix; + } else { + body = suffix; + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 400) || (response === undefined)) { + const feedback = this.id + ' ' + body; + if (response === undefined) { + throw new ExchangeError (feedback); + } + const error = this.safeValue (response, 'error', {}); + const errorCode = this.safeString (error, 'code'); + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + // fallback to default error handler + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],165:[function(require,module,exports){ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +module.exports = class tidex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'tidex', + 'name': 'Tidex', + 'countries': [ 'UK' ], + 'rateLimit': 2000, + 'version': '3', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchOrderBooks': true, + 'fetchOrder': true, + 'fetchOrders': 'emulated', + 'fetchOpenOrders': true, + 'fetchClosedOrders': 'emulated', + 'fetchTickers': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchCurrencies': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30781780-03149dc4-a12e-11e7-82bb-313b269d24d4.jpg', + 'api': { + 'web': 'https://gate.tidex.com/api', + 'public': 'https://api.tidex.com/api/3', + 'private': 'https://api.tidex.com/tapi', + }, + 'www': 'https://tidex.com', + 'doc': 'https://tidex.com/exchange/public-api', + 'referral': 'https://tidex.com/exchange/?ref=57f5638d9cd7', + 'fees': [ + 'https://tidex.com/exchange/assets-spec', + 'https://tidex.com/exchange/pairs-spec', + ], + }, + 'api': { + 'web': { + 'get': [ + 'currency', + 'pairs', + 'tickers', + 'orders', + 'ordershistory', + 'trade-data', + 'trade-data/{id}', + ], + }, + 'public': { + 'get': [ + 'info', + 'ticker/{pair}', + 'depth/{pair}', + 'trades/{pair}', + ], + }, + 'private': { + 'post': [ + 'getInfoExt', + 'getInfo', + 'Trade', + 'ActiveOrders', + 'OrderInfo', + 'CancelOrder', + 'TradeHistory', + 'CoinDepositAddress', + 'WithdrawCoin', + 'CreateCoupon', + 'RedeemCoupon', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.1 / 100, + 'maker': 0.1 / 100, + }, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + 'EMGO': 'MGO', + 'MGO': 'WMGO', + }, + 'exceptions': { + 'exact': { + '803': InvalidOrder, // "Count could not be less than 0.001." (selling below minAmount) + '804': InvalidOrder, // "Count could not be more than 10000." (buying above maxAmount) + '805': InvalidOrder, // "price could not be less than X." (minPrice violation on buy & sell) + '806': InvalidOrder, // "price could not be more than X." (maxPrice violation on buy & sell) + '807': InvalidOrder, // "cost could not be less than X." (minCost violation on buy & sell) + '831': InsufficientFunds, // "Not enougth X to create buy order." (buying with balance.quote < order.cost) + '832': InsufficientFunds, // "Not enougth X to create sell order." (selling with balance.base < order.amount) + '833': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) + }, + 'broad': { + 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} + 'invalid api key': AuthenticationError, + 'invalid sign': AuthenticationError, + 'api key dont have trade permission': AuthenticationError, + 'invalid parameter': InvalidOrder, + 'invalid order': InvalidOrder, + 'Requests too often': DDoSProtection, + 'not available': ExchangeNotAvailable, + 'data unavailable': ExchangeNotAvailable, + 'external service unavailable': ExchangeNotAvailable, + }, + }, + 'options': { + 'fetchTickersMaxLength': 2048, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.webGetCurrency (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const precision = currency['amountPoint']; + const code = this.safeCurrencyCode (id); + let active = currency['visible'] === true; + const canWithdraw = currency['withdrawEnable'] === true; + const canDeposit = currency['depositEnable'] === true; + if (!canWithdraw || !canDeposit) { + active = false; + } + const name = this.safeString (currency, 'name'); + result[code] = { + 'id': id, + 'code': code, + 'name': name, + 'active': active, + 'precision': precision, + 'funding': { + 'withdraw': { + 'active': canWithdraw, + 'fee': currency['withdrawFee'], + }, + 'deposit': { + 'active': canDeposit, + 'fee': 0.0, + }, + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': currency['withdrawMinAmout'], + 'max': undefined, + }, + 'deposit': { + 'min': currency['depositMinAmount'], + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInfo (params); + const markets = response['pairs']; + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'decimal_places'), + 'price': this.safeInteger (market, 'decimal_places'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }, + 'price': { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }, + 'cost': { + 'min': this.safeFloat (market, 'min_total'), + }, + }; + const hidden = this.safeInteger (market, 'hidden'); + const active = (hidden === 0); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'taker': market['fee'] / 100, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfoExt (params); + const balances = this.safeValue (response, 'return'); + const result = { 'info': balances }; + const funds = this.safeValue (balances, 'funds', {}); + const currencyIds = Object.keys (funds); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (funds, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'value'); + account['used'] = this.safeFloat (balance, 'inOrders'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const market_id_in_reponse = (market['id'] in response); + if (!market_id_in_reponse) { + throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); + } + const orderbook = response[market['id']]; + return this.parseOrderBook (orderbook); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join ('-'); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const result = {}; + ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + if (id in this.markets_by_id) { + symbol = this.markets_by_id[id]['symbol']; + } + result[symbol] = this.parseOrderBook (response[id]); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { high: 0.03497582, + // low: 0.03248474, + // avg: 0.03373028, + // vol: 120.11485715062999, + // vol_cur: 3572.24914074, + // last: 0.0337611, + // buy: 0.0337442, + // sell: 0.03377798, + // updated: 1537522009 } + // + let timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + if (!market['active']) { + timestamp = undefined; + } + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': this.safeFloat (ticker, 'vol_cur'), + 'quoteVolume': this.safeFloat (ticker, 'vol'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = this.ids; + if (symbols === undefined) { + const numIds = ids.length; + ids = ids.join ('-'); + // max URL length is 2048 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); + throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + const response = await this.publicGetTickerPair (this.extend (request, params)); + const result = {}; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (response[id], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let side = this.safeString (trade, 'type'); + if (side === 'ask') { + side = 'sell'; + } else if (side === 'bid') { + side = 'buy'; + } + const price = this.safeFloat2 (trade, 'rate', 'price'); + const id = this.safeString2 (trade, 'trade_id', 'tid'); + const orderId = this.safeString (trade, 'order_id'); + if ('pair' in trade) { + const marketId = this.safeString (trade, 'pair'); + market = this.safeValue (this.markets_by_id, marketId, market); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const amount = this.safeFloat (trade, 'amount'); + const type = 'limit'; // all trades are still limit trades + let takerOrMaker = undefined; + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const isYourOrder = this.safeValue (trade, 'is_your_order'); + if (isYourOrder !== undefined) { + takerOrMaker = 'taker'; + if (isYourOrder) { + takerOrMaker = 'maker'; + } + if (fee === undefined) { + fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); + } + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTradesPair (this.extend (request, params)); + if (Array.isArray (response)) { + const numElements = response.length; + if (numElements === 0) { + return []; + } + } + return this.parseTrades (response[market['id']], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'amount': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + }; + price = parseFloat (price); + amount = parseFloat (amount); + const response = await this.privatePostTrade (this.extend (request, params)); + let id = undefined; + let status = 'open'; + let filled = 0.0; + let remaining = amount; + if ('return' in response) { + id = this.safeString (response['return'], 'order_id'); + if (id === '0') { + id = this.safeString (response['return'], 'init_order_id'); + status = 'closed'; + } + filled = this.safeFloat (response['return'], 'received', 0.0); + remaining = this.safeFloat (response['return'], 'remains', amount); + } + const timestamp = this.milliseconds (); + const order = { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * filled, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'fee': undefined, + // 'trades': this.parseTrades (order['trades'], market), + }; + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'closed', + '2': 'canceled', + '3': 'canceled', // or partially-filled and still open? https://github.com/ccxt/ccxt/issues/1594 + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.safeTimestamp (order, 'timestamp_created'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let remaining = undefined; + let amount = undefined; + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if ('start_amount' in order) { + amount = this.safeFloat (order, 'start_amount'); + remaining = this.safeFloat (order, 'amount'); + } else { + remaining = this.safeFloat (order, 'amount'); + if (id in this.orders) { + amount = this.orders[id]['amount']; + } + } + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + cost = price * filled; + } + } + const fee = undefined; + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'type': 'limit', + 'side': order['type'], + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + }; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + id = id.toString (); + const newOrder = this.parseOrder (this.extend ({ 'id': id }, response['return'][id])); + const oldOrder = (id in this.orders) ? this.orders[id] : {}; + this.orders[id] = this.extend (oldOrder, newOrder); + return this.orders[id]; + } + + updateCachedOrders (openOrders, symbol) { + // update local cache with open orders + // this will add unseen orders and overwrite existing ones + for (let j = 0; j < openOrders.length; j++) { + const id = openOrders[j]['id']; + this.orders[id] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + for (let k = 0; k < cachedOrderIds.length; k++) { + // match each cached order to an order in the open orders array + // possible reasons why a cached order may be missing in the open orders array: + // - order was closed or canceled -> update cache + // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip + const cachedOrderId = cachedOrderIds[k]; + let cachedOrder = this.orders[cachedOrderId]; + if (!(cachedOrderId in openOrdersIndexedById)) { + // cached order is not in open orders array + // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order + if (symbol !== undefined && symbol !== cachedOrder['symbol']) { + continue; + } + // cached order is absent from the list of open orders -> mark the cached order as closed + if (cachedOrder['status'] === 'open') { + cachedOrder = this.extend (cachedOrder, { + 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") + 'cost': undefined, + 'filled': cachedOrder['amount'], + 'remaining': 0.0, + }); + if (cachedOrder['cost'] === undefined) { + if (cachedOrder['filled'] !== undefined) { + cachedOrder['cost'] = cachedOrder['filled'] * cachedOrder['price']; + } + } + this.orders[cachedOrderId] = cachedOrder; + } + } + } + return this.toArray (this.orders); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if ('fetchOrdersRequiresSymbol' in this.options) { + if (this.options['fetchOrdersRequiresSymbol']) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a `symbol` argument'); + } + } + } + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostActiveOrders (this.extend (request, params)); + // it can only return 'open' orders (i.e. no way to fetch 'closed' orders) + const orders = this.safeValue (response, 'return', []); + const openOrders = this.parseOrders (orders, market); + const allOrders = this.updateCachedOrders (openOrders, symbol); + const result = this.filterBySymbol (allOrders, symbol); + return this.filterBySinceLimit (result, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'open'); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + // some derived classes use camelcase notation for request fields + const request = { + // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) + // 'count': 1000, // the number of trades for display numerical, default = 1000 + // 'from_id': trade ID, from which the display starts numerical 0 + // 'end_id': trade ID on which the display ends numerical ∞ + // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) + // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) + // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) + // 'pair': 'eth_btc', // default = all markets + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['count'] = parseInt (limit); + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + const trades = this.safeValue (response, 'return', []); + return this.parseTrades (trades, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coinName': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + }; + // no docs on the tag, yet... + if (tag !== undefined) { + throw new ExchangeError (this.id + ' withdraw() does not support the tag argument yet due to a lack of docs on withdrawing with tag/memo on behalf of the exchange.'); + } + const response = await this.privatePostWithdrawCoin (this.extend (request, params)); + return { + 'info': response, + 'id': response['return']['tId'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + 'method': path, + }, query)); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': signature, + }; + } else if (api === 'public') { + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + url += '/' + this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + if (Object.keys (query).length) { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // 1 - The exchange only returns the integer 'success' key from their private API + // + // { "success": 1, ... } httpCode === 200 + // { "success": 0, ... } httpCode === 200 + // + // 2 - However, derived exchanges can return non-integers + // + // It can be a numeric string + // { "sucesss": "1", ... } + // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) + // + // Or just a string + // { "success": "true", ... } + // { "success": "false", ... }, httpCode >= 200 + // + // Or a boolean + // { "success": true, ... } + // { "success": false, ... }, httpCode >= 200 + // + // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types + // + // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui + // + // To cover points 1, 2, 3 and 4 combined this handler should work like this: + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],166:[function(require,module,exports){ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, PermissionDenied, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, RateLimitExceeded, NotSupported, BadRequest, AuthenticationError } = require ('./base/errors'); + +module.exports = class timex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'timex', + 'name': 'TimeX', + 'countries': [ 'AU' ], + 'version': 'v1', + 'rateLimit': 1500, + 'has': { + 'CORS': false, + 'cancelOrders': true, + 'editOrder': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchTradingFee': true, // maker fee only + }, + 'timeframes': { + '1m': 'I1', + '5m': 'I5', + '15m': 'I15', + '30m': 'I30', + '1h': 'H1', + '2h': 'H2', + '4h': 'H4', + '6h': 'H6', + '12h': 'H12', + '1d': 'D1', + '1w': 'W1', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/70423869-6839ab00-1a7f-11ea-8f94-13ae72c31115.jpg', + 'api': 'https://plasma-relay-backend.timex.io', + 'www': 'https://timex.io', + 'doc': 'https://docs.timex.io', + }, + 'api': { + 'custody': { + 'get': [ + 'credentials', // Get api key for address + 'credentials/h/{hash}', // Get api key by hash + 'credentials/k/{key}', // Get api key by key + 'credentials/me/address', // Get api key by hash + 'deposit-addresses', // Get deposit addresses list + 'deposit-addresses/h/{hash}', // Get deposit address by hash + ], + }, + 'history': { + 'get': [ + 'orders', // Gets historical orders + 'orders/details', // Gets order details + 'orders/export/csv', // Export orders to csv + 'trades', // Gets historical trades + 'trades/export/csv', // Export trades to csv + ], + }, + 'currencies': { + 'get': [ + 'a/{address}', // Gets currency by address + 'i/{id}', // Gets currency by id + 's/{symbol}', // Gets currency by symbol + ], + 'post': [ + 'perform', // Creates new currency + 'prepare', // Prepare creates new currency + 'remove/perform', // Removes currency by symbol + 's/{symbol}/remove/prepare', // Prepare remove currency by symbol + 's/{symbol}/update/perform', // Prepare update currency by symbol + 's/{symbol}/update/prepare', // Prepare update currency by symbol + ], + }, + 'markets': { + 'get': [ + 'i/{id}', // Gets market by id + 's/{symbol}', // Gets market by symbol + ], + 'post': [ + 'perform', // Creates new market + 'prepare', // Prepare creates new market + 'remove/perform', // Removes market by symbol + 's/{symbol}/remove/prepare', // Prepare remove market by symbol + 's/{symbol}/update/perform', // Prepare update market by symbol + 's/{symbol}/update/prepare', // Prepare update market by symbol + ], + }, + 'public': { + 'get': [ + 'candles', // Gets candles + 'currencies', // Gets all the currencies + 'markets', // Gets all the markets + 'orderbook', // Gets orderbook + 'orderbook/raw', // Gets raw orderbook + 'orderbook/v2', // Gets orderbook v2 + 'tickers', // Gets all the tickers + 'trades', // Gets trades + ], + }, + 'statistics': { + 'get': [ + 'address', // calculateAddressStatistics + ], + }, + 'trading': { + 'get': [ + 'balances', // Get trading balances for all (or selected) currencies + 'fees', // Get trading fee rates for all (or selected) markets + 'orders', // Gets open orders + ], + 'post': [ + 'orders', // Create new order + 'orders/json', // Create orders + ], + 'put': [ + 'orders', // Cancel or update orders + 'orders/json', // Update orders + ], + 'delete': [ + 'orders', // Delete orders + 'orders/json', // Delete orders + ], + }, + 'tradingview': { + 'get': [ + 'config', // Gets config + 'history', // Gets history + 'symbol_info', // Gets symbol info + 'time', // Gets time + ], + }, + }, + 'exceptions': { + 'exact': { + '0': ExchangeError, + '1': NotSupported, + '4000': BadRequest, + '4001': BadRequest, + '4002': InsufficientFunds, + '4003': AuthenticationError, + '4004': AuthenticationError, + '4005': BadRequest, + '4006': BadRequest, + '4007': BadRequest, + '4300': PermissionDenied, + '4100': AuthenticationError, + '4400': OrderNotFound, + '5001': InvalidOrder, + '5002': ExchangeError, + '400': BadRequest, + '401': AuthenticationError, + '403': PermissionDenied, + '404': OrderNotFound, + '429': RateLimitExceeded, + '500': ExchangeError, + '503': ExchangeNotAvailable, + }, + 'broad': { + 'Insufficient': InsufficientFunds, + }, + }, + 'options': { + 'fetchTickers': { + 'period': '1d', + }, + 'fetchTrades': { + 'sort': 'timestamp,asc', + }, + 'fetchMyTrades': { + 'sort': 'timestamp,asc', + }, + 'fetchOpenOrders': { + 'sort': 'createdAt,asc', + }, + 'fetchClosedOrders': { + 'sort': 'createdAt,asc', + }, + 'defaultSort': 'timestamp,asc', + 'defaultSortOrders': 'createdAt,asc', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + // + // [ + // { + // "symbol": "ETHBTC", + // "name": "ETH/BTC", + // "baseCurrency": "ETH", + // "baseTokenAddress": "0x45932db54b38af1f5a57136302eeba66a5975c15", + // "quoteCurrency": "BTC", + // "quoteTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "feeCurrency": "BTC", + // "feeTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "quantityIncrement": "0.0000001", + // "takerFee": "0.005", + // "makerFee": "0.0025", + // "tickSize": "0.00000001", + // "baseMinSize": "0.0001", + // "quoteMinSize": "0.00001", + // "locked": false + // } + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + result.push (this.parseMarket (response[i])); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // [ + // { + // "symbol": "BTC", + // "name": "Bitcoin", + // "address": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggb3BhY2l0eT0iMC41IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMwIDUzQzQyLjcwMjUgNTMgNTMgNDIuNzAyNSA1MyAzMEM1MyAxNy4yOTc1IDQyLjcwMjUgNyAzMCA3QzE3LjI5NzUgNyA3IDE3LjI5NzUgNyAzMEM3IDQyLjcwMjUgMTcuMjk3NSA1MyAzMCA1M1pNMzAgNTVDNDMuODA3MSA1NSA1NSA0My44MDcxIDU1IDMwQzU1IDE2LjE5MjkgNDMuODA3MSA1IDMwIDVDMTYuMTkyOSA1IDUgMTYuMTkyOSA1IDMwQzUgNDMuODA3MSAxNi4xOTI5IDU1IDMwIDU1WiIvPgo8cGF0aCBkPSJNNDAuOTQyNSAyNi42NTg1QzQxLjQwMDMgMjMuNjExMyAzOS4wNzA1IDIxLjk3MzIgMzUuODg0OCAyMC44ODA0TDM2LjkxODIgMTYuNzUyNkwzNC4zOTUxIDE2LjEyNjRMMzMuMzg5IDIwLjE0NTVDMzIuNzI1OCAxOS45ODA5IDMyLjA0NDUgMTkuODI1NiAzMS4zNjc1IDE5LjY3MTdMMzIuMzgwOCAxNS42MjYyTDI5Ljg1OTEgMTVMMjguODI1IDE5LjEyNjRDMjguMjc2IDE5LjAwMTkgMjcuNzM3IDE4Ljg3ODggMjcuMjEzOSAxOC43NDkzTDI3LjIxNjggMTguNzM2NEwyMy43MzcyIDE3Ljg3MTJMMjMuMDY2IDIwLjU1NDhDMjMuMDY2IDIwLjU1NDggMjQuOTM4IDIwLjk4MjEgMjQuODk4NSAyMS4wMDg1QzI1LjkyMDQgMjEuMjYyNiAyNi4xMDUgMjEuOTM2IDI2LjA3NDEgMjIuNDY5OUwyNC44OTcgMjcuMTcyNEMyNC45Njc1IDI3LjE5MDMgMjUuMDU4NyAyNy4yMTYgMjUuMTU5MyAyNy4yNTYxQzI1LjA3NTMgMjcuMjM1NCAyNC45ODU0IDI3LjIxMjQgMjQuODkyNyAyNy4xOTAzTDIzLjI0MjggMzMuNzc3OEMyMy4xMTc3IDM0LjA4NjkgMjIuODAwOCAzNC41NTA2IDIyLjA4NjUgMzQuMzc0NkMyMi4xMTE3IDM0LjQxMTEgMjAuMjUyNiAzMy45MTg3IDIwLjI1MjYgMzMuOTE4N0wxOSAzNi43OTQ5TDIyLjI4MzQgMzcuNjFDMjIuODk0MiAzNy43NjI0IDIzLjQ5MjggMzcuOTIyIDI0LjA4MjEgMzguMDcyM0wyMy4wMzggNDIuMjQ3NEwyNS41NTgyIDQyLjg3MzZMMjYuNTkyMyAzOC43NDI5QzI3LjI4MDcgMzguOTI5IDI3Ljk0OSAzOS4xMDA3IDI4LjYwMyAzOS4yNjI0TDI3LjU3MjUgNDMuMzczOEwzMC4wOTU2IDQ0TDMxLjEzOTcgMzkuODMyOEMzNS40NDIyIDQwLjY0MzYgMzguNjc3NCA0MC4zMTY2IDQwLjAzOTIgMzYuNDQxNEM0MS4xMzY1IDMzLjMyMTIgMzkuOTg0NiAzMS41MjEzIDM3LjcyMDkgMzAuMzQ3N0MzOS4zNjk0IDI5Ljk2OTEgNDAuNjExMiAyOC44ODkyIDQwLjk0MjUgMjYuNjU4NVYyNi42NTg1Wk0zNS4xNzc3IDM0LjcwODhDMzQuMzk4IDM3LjgyOSAyOS4xMjI2IDM2LjE0MjIgMjcuNDEyMiAzNS43MTkzTDI4Ljc5NzcgMzAuMTg4MUMzMC41MDgxIDMwLjYxMzIgMzUuOTkyNiAzMS40NTQ4IDM1LjE3NzcgMzQuNzA4OFpNMzUuOTU4MSAyNi42MTM0QzM1LjI0NjcgMjkuNDUxNyAzMC44NTU5IDI4LjAwOTcgMjkuNDMxNiAyNy42NTYxTDMwLjY4NzcgMjIuNjM5NUMzMi4xMTIgMjIuOTkzIDM2LjY5OSAyMy42NTI4IDM1Ljk1ODEgMjYuNjEzNFoiLz4KPC9zdmc+Cg==", + // "background": "transparent", + // "fiatSymbol": "BTC", + // "decimals": 8, + // "tradeDecimals": 20, + // "displayDecimals": 4, + // "crypto": true, + // "depositEnabled": true, + // "withdrawalEnabled": true, + // "transferEnabled": true, + // "buyEnabled": false, + // "purchaseEnabled": false, + // "redeemEnabled": false, + // "active": true, + // "withdrawalFee": "50000000000000000", + // "purchaseCommissions": [] + // }, + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + result.push (this.parseCurrency (currency)); + } + return this.indexBy (result, 'code'); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const period = this.safeString (this.options['fetchTickers'], 'period', '1d'); + const request = { + 'period': this.timeframes[period], // I1, I5, I15, I30, H1, H2, H4, H6, H12, D1, W1 + }; + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ + // { + // "ask": 0.017, + // "bid": 0.016, + // "high": 0.019, + // "last": 0.017, + // "low": 0.015, + // "market": "TIME/ETH", + // "open": 0.016, + // "period": "H1", + // "timestamp": "2018-12-14T20:50:36.134Z", + // "volume": 4.57, + // "volumeQuote": 0.07312 + // } + // ] + // + return this.parseTickers (response, symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const period = this.safeString (this.options['fetchTickers'], 'period', '1d'); + const request = { + 'market': market['id'], + 'period': this.timeframes[period], // I1, I5, I15, I30, H1, H2, H4, H6, H12, D1, W1 + }; + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ + // { + // "ask": 0.017, + // "bid": 0.016, + // "high": 0.019, + // "last": 0.017, + // "low": 0.015, + // "market": "TIME/ETH", + // "open": 0.016, + // "period": "H1", + // "timestamp": "2018-12-14T20:50:36.134Z", + // "volume": 4.57, + // "volumeQuote": 0.07312 + // } + // ] + // + const ticker = this.safeValue (response, 0); + return this.parseTicker (ticker, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderbookV2 (this.extend (request, params)); + // + // { + // "timestamp":"2019-12-05T00:21:09.538", + // "bid":[ + // { + // "index":"2", + // "price":"0.02024007", + // "baseTokenAmount":"0.0096894", + // "baseTokenCumulativeAmount":"0.0096894", + // "quoteTokenAmount":"0.000196114134258", + // "quoteTokenCumulativeAmount":"0.000196114134258" + // }, + // "ask":[ + // { + // "index":"-3", + // "price":"0.02024012", + // "baseTokenAmount":"0.005", + // "baseTokenCumulativeAmount":"0.005", + // "quoteTokenAmount":"0.0001012006", + // "quoteTokenCumulativeAmount":"0.0001012006" + // }, + // ] + // } + // + const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); + return this.parseOrderBook (response, timestamp, 'bid', 'ask', 'price', 'baseTokenAmount'); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const options = this.safeValue (this.options, 'fetchTrades', {}); + const defaultSort = this.safeValue (options, 'sort', 'timestamp,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'address': 'string', // trade’s member account (?) + // 'cursor': 1234, // int64 (?) + // 'from': this.iso8601 (since), + 'market': market['id'], + // 'page': 0, // results page you want to retrieve 0 .. N + // 'size': limit, // number of records per page, 100 by default + 'sort': sort, // array[string], sorting criteria in the format "property,asc" or "property,desc", default is ascending + // 'till': this.iso8601 (this.milliseconds ()), + }; + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['size'] = limit; // default is 100 + } + const response = await this.publicGetTrades (this.extend (request, query)); + // + // [ + // { + // "id":1, + // "timestamp":"2019-06-25T17:01:50.309", + // "direction":"BUY", + // "price":"0.027", + // "quantity":"0.001" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'period': this.timeframes[timeframe], + }; + // if since and limit are not specified + const duration = this.parseTimeframe (timeframe); + if (since !== undefined) { + request['from'] = this.iso8601 (since); + if (limit !== undefined) { + request['till'] = this.iso8601 (this.sum (since, this.sum (limit, 1) * duration * 1000)); + } + } else if (limit !== undefined) { + const now = this.milliseconds (); + request['till'] = this.iso8601 (now); + request['from'] = this.iso8601 (now - limit * duration * 1000 - 1); + } else { + request['till'] = this.iso8601 (this.milliseconds ()); + } + const response = await this.publicGetCandles (this.extend (request, params)); + // + // [ + // { + // "timestamp":"2019-12-04T23:00:00", + // "open":"0.02024009", + // "high":"0.02024009", + // "low":"0.02024009", + // "close":"0.02024009", + // "volume":"0.00008096036", + // "volumeQuote":"0.004", + // }, + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.tradingGetBalances (params); + // + // [ + // {"currency":"BTC","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"AUDT","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"ETH","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"TIME","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"USDT","totalBalance":"0","lockedBalance":"0"} + // ] + // + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'totalBalance'); + account['used'] = this.safeFloat (balance, 'lockedBalance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'quantity': this.amountToPrecision (symbol, amount), + 'side': side.toUpperCase (), + // 'clientOrderId': '123', + // 'expireIn': 1575523308, // in seconds + // 'expireTime': 1575523308, // unix timestamp + }; + let query = params; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + const defaultExpireIn = this.safeInteger (this.options, 'expireIn'); + const expireTime = this.safeValue (params, 'expireTime'); + const expireIn = this.safeValue (params, 'expireIn', defaultExpireIn); + if (expireTime !== undefined) { + request['expireTime'] = expireTime; + } else if (expireIn !== undefined) { + request['expireIn'] = expireIn; + } else { + throw new InvalidOrder (this.id + ' createOrder method requires a expireTime or expireIn param for a ' + type + ' order, you can also set the expireIn exchange-wide option'); + } + query = this.omit (params, [ 'expireTime', 'expireIn' ]); + } else { + request['price'] = 0; + } + const response = await this.tradingPostOrders (this.extend (request, query)); + // + // { + // "orders": [ + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + const order = this.safeValue (orders, 0, {}); + return this.parseOrder (order, market); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + }; + if (amount !== undefined) { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + if (price !== undefined) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.tradingPutOrders (this.extend (request, params)); + // + // { + // "changedOrders": [ + // { + // "newOrder": { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // }, + // "oldId": "string", + // }, + // ], + // "unchangedOrders": [ "string" ], + // } + // + if ('unchangedOrders' in response) { + const orderIds = this.safeValue (response, 'unchangedOrders', []); + const orderId = this.safeString (orderIds, 0); + return { + 'id': orderId, + 'info': response, + }; + } + const orders = this.safeValue (response, 'changedOrders', []); + const firstOrder = this.safeValue (orders, 0, {}); + const order = this.safeValue (firstOrder, 'newOrder', {}); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.cancelOrders ([ id ], symbol, params); + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': ids, + }; + const response = await this.tradingDeleteOrders (this.extend (request, params)); + // + // { + // "changedOrders": [ + // { + // "newOrder": { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // }, + // "oldId": "string", + // }, + // ], + // "unchangedOrders": [ "string" ], + // } + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderHash': id, + }; + const response = await this.historyGetOrdersDetails (request); + // + // { + // "order": { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // }, + // "trades": [ + // { + // "fee": "0.3", + // "id": 100, + // "makerOrTaker": "MAKER", + // "makerOrderId": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "takerOrderId": "string", + // "timestamp": "2019-12-05T07:48:26.310Z" + // } + // ] + // } + // + const order = this.safeValue (response, 'order', {}); + const trades = this.safeValue (response, 'trades', []); + return this.parseOrder (this.extend (order, { 'trades': trades })); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const options = this.safeValue (this.options, 'fetchOpenOrders', {}); + const defaultSort = this.safeValue (options, 'sort', 'createdAt,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'clientOrderId': '123', // order’s client id list for filter + // page: 0, // results page you want to retrieve (0 .. N) + 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.tradingGetOrders (this.extend (request, query)); + // + // { + // "orders": [ + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const options = this.safeValue (this.options, 'fetchClosedOrders', {}); + const defaultSort = this.safeValue (options, 'sort', 'createdAt,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'clientOrderId': '123', // order’s client id list for filter + // page: 0, // results page you want to retrieve (0 .. N) + 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported + 'side': 'BUY', // or 'SELL' + // 'till': this.iso8601 (this.milliseconds ()), + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.historyGetOrders (this.extend (request, query)); + // + // { + // "orders": [ + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const options = this.safeValue (this.options, 'fetchMyTrades', {}); + const defaultSort = this.safeValue (options, 'sort', 'timestamp,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'cursorId': 123, // int64 (?) + // 'from': this.iso8601 (since), + // 'makerOrderId': '1234', // maker order hash + // 'owner': '...', // owner address (?) + // 'page': 0, // results page you want to retrieve (0 .. N) + // 'side': 'BUY', // or 'SELL' + // 'size': limit, + 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported + // 'symbol': market['id'], + // 'takerOrderId': '1234', + // 'till': this.iso8601 (this.milliseconds ()), + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.historyGetTrades (this.extend (request, query)); + // + // { + // "trades": [ + // { + // "fee": "0.3", + // "id": 100, + // "makerOrTaker": "MAKER", + // "makerOrderId": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "takerOrderId": "string", + // "timestamp": "2019-12-08T04:54:11.171Z" + // } + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchTradingFee (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'markets': market['id'], + }; + const response = await this.tradingGetFees (this.extend (request, params)); + // + // [ + // { + // "fee": 0.0075, + // "market": "ETHBTC" + // } + // ] + // + const result = this.safeValue (response, 0, {}); + return { + 'info': response, + 'maker': this.safeFloat (result, 'fee'), + 'taker': undefined, + }; + } + + parseMarket (market) { + // + // { + // "symbol": "ETHBTC", + // "name": "ETH/BTC", + // "baseCurrency": "ETH", + // "baseTokenAddress": "0x45932db54b38af1f5a57136302eeba66a5975c15", + // "quoteCurrency": "BTC", + // "quoteTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "feeCurrency": "BTC", + // "feeTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "quantityIncrement": "0.0000001", + // "takerFee": "0.005", + // "makerFee": "0.0025", + // "tickSize": "0.00000001", + // "baseMinSize": "0.0001", + // "quoteMinSize": "0.00001", + // "locked": false + // } + // + const locked = this.safeValue (market, 'locked'); + const active = !locked; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.precisionFromString (this.safeString (market, 'quantityIncrement')), + 'price': this.precisionFromString (this.safeString (market, 'tickSize')), + }; + const amountIncrement = this.safeFloat (market, 'quantityIncrement'); + const minBase = this.safeFloat (market, 'baseMinSize'); + const minAmount = Math.max (amountIncrement, minBase); + const priceIncrement = this.safeFloat (market, 'tickSize'); + const minCost = this.safeFloat (market, 'quoteMinSize'); + const limits = { + 'amount': { 'min': minAmount, 'max': undefined }, + 'price': { 'min': priceIncrement, 'max': undefined }, + 'cost': { 'min': Math.max (minCost, minAmount * priceIncrement), 'max': undefined }, + }; + const taker = this.safeFloat (market, 'takerFee'); + const maker = this.safeFloat (market, 'makerFee'); + return { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'type': 'spot', + 'active': active, + 'precision': precision, + 'limits': limits, + 'taker': taker, + 'maker': maker, + 'info': market, + }; + } + + parseCurrency (currency) { + // + // { + // "symbol": "BTC", + // "name": "Bitcoin", + // "address": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR...mc+Cg==", + // "background": "transparent", + // "fiatSymbol": "BTC", + // "decimals": 8, + // "tradeDecimals": 20, + // "displayDecimals": 4, + // "crypto": true, + // "depositEnabled": true, + // "withdrawalEnabled": true, + // "transferEnabled": true, + // "buyEnabled": false, + // "purchaseEnabled": false, + // "redeemEnabled": false, + // "active": true, + // "withdrawalFee": "50000000000000000", + // "purchaseCommissions": [] + // }, + // + const id = this.safeString (currency, 'symbol'); + const code = this.safeCurrencyCode (id); + const name = this.safeString (currency, 'name'); + const precision = this.safeInteger (currency, 'decimals'); + const active = this.safeValue (currency, 'active'); + // const fee = this.safeFloat (currency, 'withdrawalFee'); + const feeString = this.safeString (currency, 'withdrawalFee'); + const feeStringLen = feeString.length; + const tradeDecimals = this.safeInteger (currency, 'tradeDecimals'); + let fee = undefined; + const dotIndex = feeStringLen - tradeDecimals; + if (dotIndex > 0) { + const whole = feeString.slice (0, dotIndex); + const fraction = feeString.slice (-dotIndex); + fee = parseFloat (whole + '.' + fraction); + } else { + let fraction = '.'; + for (let i = 0; i < -dotIndex; i++) { + fraction += '0'; + } + fee = parseFloat (fraction + feeString); + } + return { + 'id': code, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': name, + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'withdraw': { 'min': fee, 'max': undefined }, + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + }; + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "ask": 0.017, + // "bid": 0.016, + // "high": 0.019, + // "last": 0.017, + // "low": 0.015, + // "market": "TIME/ETH", + // "open": 0.016, + // "period": "H1", + // "timestamp": "2018-12-14T20:50:36.134Z", + // "volume": 4.57, + // "volumeQuote": 0.07312 + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'market'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open'); + let change = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + } + let percentage = undefined; + if (change !== undefined && open) { + percentage = (change / open) * 100; + } + return { + 'symbol': symbol, + 'info': ticker, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'volumeQuote'), + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "id":1, + // "timestamp":"2019-06-25T17:01:50.309", + // "direction":"BUY", + // "price":"0.027", + // "quantity":"0.001" + // } + // + // fetchMyTrades, fetchOrder (private) + // + // { + // "fee": "0.3", + // "id": 100, + // "makerOrTaker": "MAKER", + // "makerOrderId": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "takerOrderId": "string", + // "timestamp": "2019-12-08T04:54:11.171Z" + // } + // + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + const id = this.safeString (trade, 'id'); + const side = this.safeStringLower2 (trade, 'direction', 'side'); + const takerOrMaker = this.safeStringLower (trade, 'makerOrTaker'); + let orderId = undefined; + if (takerOrMaker !== undefined) { + orderId = this.safeString (trade, takerOrMaker + 'OrderId'); + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrency = (market === undefined) ? undefined : market['quote']; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + let cost = undefined; + if ((price !== undefined) && (amount !== undefined)) { + cost = this.costToPrecision (symbol, amount * price); + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': fee, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "timestamp":"2019-12-04T23:00:00", + // "open":"0.02024009", + // "high":"0.02024009", + // "low":"0.02024009", + // "close":"0.02024009", + // "volume":"0.00008096036", + // "volumeQuote":"0.004", + // } + // + return [ + this.parse8601 (this.safeString (ohlcv, 'timestamp')), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volumeQuote'), + ]; + } + + parseOrder (order, market = undefined) { + // + // fetchOrder, createOrder, cancelOrder, cancelOrders, fetchOpenOrders, fetchClosedOrders + // + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // "trades": [], // injected from the outside + // } + // + const id = this.safeString (order, 'id'); + const type = this.safeStringLower (order, 'type'); + const side = this.safeStringLower (order, 'side'); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'createdAt')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'filledQuantity'); + const canceledQuantity = this.safeFloat (order, 'cancelledQuantity'); + let remaining = undefined; + let status = undefined; + if ((amount !== undefined) && (filled !== undefined)) { + remaining = Math.max (amount - filled, 0.0); + if (filled >= amount) { + status = 'closed'; + } else if ((canceledQuantity !== undefined) && (canceledQuantity > 0)) { + status = 'canceled'; + } else { + status = 'open'; + } + } + const cost = parseFloat (this.costToPrecision (symbol, price * filled)); + const fee = undefined; + let lastTradeTimestamp = undefined; + let trades = undefined; + const rawTrades = this.safeValue (order, 'trades'); + if (rawTrades !== undefined) { + trades = this.parseTrades (rawTrades, market, undefined, undefined, { + 'order': id, + }); + } + if (trades !== undefined) { + const numTrades = trades.length; + if (numTrades > 0) { + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + api + '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencodeWithArrayRepeat (params); + } + if (api !== 'public') { + this.checkRequiredCredentials (); + const auth = this.stringToBase64 (this.encode (this.apiKey + ':' + this.secret)); + const secret = 'Basic ' + this.decode (auth); + headers = { 'authorization': secret }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (statusCode >= 400) { + // + // {"error":{"timestamp":"05.12.2019T05:25:43.584+0000","status":"BAD_REQUEST","message":"Insufficient ETH balance. Required: 1, actual: 0.","code":4001}} + // {"error":{"timestamp":"05.12.2019T04:03:25.419+0000","status":"FORBIDDEN","message":"Access denied","code":4300}} + // + const feedback = this.id + ' ' + responseBody; + let error = this.safeValue (response, 'error'); + if (error === undefined) { + error = response; + } + const code = this.safeString2 (error, 'code', 'status'); + const message = this.safeString2 (error, 'message', 'debugMessage'); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],167:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class upbit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'upbit', + 'name': 'Upbit', + 'countries': [ 'KR' ], + 'version': 'v1', + 'rateLimit': 1000, + 'certified': true, + // new metainfo interface + 'has': { + 'CORS': true, + 'createDepositAddress': true, + 'createMarketOrder': true, + 'fetchDepositAddress': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrderBooks': true, + 'fetchOpenOrders': true, + 'fetchOrders': false, + 'fetchTickers': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + }, + 'timeframes': { + '1m': 'minutes', + '3m': 'minutes', + '5m': 'minutes', + '15m': 'minutes', + '30m': 'minutes', + '1h': 'minutes', + '4h': 'minutes', + '1d': 'days', + '1w': 'weeks', + '1M': 'months', + }, + 'hostname': 'api.upbit.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg', + 'api': 'https://{hostname}', + 'www': 'https://upbit.com', + 'doc': 'https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C', + 'fees': 'https://upbit.com/service_center/guide', + }, + 'api': { + 'public': { + 'get': [ + 'market/all', + 'candles/{timeframe}', + 'candles/{timeframe}/{unit}', + 'candles/minutes/{unit}', + 'candles/minutes/1', + 'candles/minutes/3', + 'candles/minutes/5', + 'candles/minutes/15', + 'candles/minutes/30', + 'candles/minutes/60', + 'candles/minutes/240', + 'candles/days', + 'candles/weeks', + 'candles/months', + 'trades/ticks', + 'ticker', + 'orderbook', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'orders/chance', + 'order', + 'orders', + 'withdraws', + 'withdraw', + 'withdraws/chance', + 'deposits', + 'deposit', + 'deposits/coin_addresses', + 'deposits/coin_address', + ], + 'post': [ + 'orders', + 'withdraws/coin', + 'withdraws/krw', + 'deposits/generate_coin_address', + ], + 'delete': [ + 'order', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0025, + 'taker': 0.0025, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'exceptions': { + 'exact': { + 'This key has expired.': AuthenticationError, + 'Missing request parameter error. Check the required parameters!': BadRequest, + 'side is missing, side does not have a valid value': InvalidOrder, + }, + 'broad': { + 'thirdparty_agreement_required': PermissionDenied, + 'out_of_scope': PermissionDenied, + 'order_not_found': OrderNotFound, + 'insufficient_funds': InsufficientFunds, + 'invalid_access_key': AuthenticationError, + 'jwt_verification': AuthenticationError, + 'create_ask_error': ExchangeError, + 'create_bid_error': ExchangeError, + 'volume_too_large': InvalidOrder, + 'invalid_funds': InvalidOrder, + }, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + 'fetchTickersMaxLength': 4096, // 2048, + 'fetchOrderBooksMaxLength': 4096, // 2048, + 'symbolSeparator': '-', + 'tradingFeesByQuoteCurrency': { + 'KRW': 0.0005, + }, + }, + }); + } + + async fetchCurrency (code, params = {}) { + // this method is for retrieving funding fees and limits per currency + // it requires private access and API keys properly set up + await this.loadMarkets (); + const currency = this.currency (code); + return await this.fetchCurrencyById (currency['id'], params); + } + + async fetchCurrencyById (id, params = {}) { + // this method is for retrieving funding fees and limits per currency + // it requires private access and API keys properly set up + const request = { + 'currency': id, + }; + const response = await this.privateGetWithdrawsChance (this.extend (request, params)); + // + // { + // "member_level": { + // "security_level": 3, + // "fee_level": 0, + // "email_verified": true, + // "identity_auth_verified": true, + // "bank_account_verified": true, + // "kakao_pay_auth_verified": false, + // "locked": false, + // "wallet_locked": false + // }, + // "currency": { + // "code": "BTC", + // "withdraw_fee": "0.0005", + // "is_coin": true, + // "wallet_state": "working", + // "wallet_support": [ "deposit", "withdraw" ] + // }, + // "account": { + // "currency": "BTC", + // "balance": "10.0", + // "locked": "0.0", + // "avg_krw_buy_price": "8042000", + // "modified": false + // }, + // "withdraw_limit": { + // "currency": "BTC", + // "minimum": null, + // "onetime": null, + // "daily": "10.0", + // "remaining_daily": "10.0", + // "remaining_daily_krw": "0.0", + // "fixed": null, + // "can_withdraw": true + // } + // } + // + const memberInfo = this.safeValue (response, 'member_level', {}); + const currencyInfo = this.safeValue (response, 'currency', {}); + const withdrawLimits = this.safeValue (response, 'withdraw_limit', {}); + const canWithdraw = this.safeValue (withdrawLimits, 'can_withdraw'); + const walletState = this.safeString (currencyInfo, 'wallet_state'); + const walletLocked = this.safeValue (memberInfo, 'wallet_locked'); + const locked = this.safeValue (memberInfo, 'locked'); + let active = true; + if ((canWithdraw !== undefined) && canWithdraw) { + active = false; + } else if (walletState !== 'working') { + active = false; + } else if ((walletLocked !== undefined) && walletLocked) { + active = false; + } else if ((locked !== undefined) && locked) { + active = false; + } + const maxOnetimeWithdrawal = this.safeFloat (withdrawLimits, 'onetime'); + const maxDailyWithdrawal = this.safeFloat (withdrawLimits, 'daily', maxOnetimeWithdrawal); + const remainingDailyWithdrawal = this.safeFloat (withdrawLimits, 'remaining_daily', maxDailyWithdrawal); + let maxWithdrawLimit = undefined; + if (remainingDailyWithdrawal > 0) { + maxWithdrawLimit = remainingDailyWithdrawal; + } else { + maxWithdrawLimit = maxDailyWithdrawal; + } + const precision = undefined; + const currencyId = this.safeString (currencyInfo, 'code'); + const code = this.safeCurrencyCode (currencyId); + return { + 'info': response, + 'id': currencyId, + 'code': code, + 'name': code, + 'active': active, + 'fee': this.safeFloat (currencyInfo, 'withdraw_fee'), + 'precision': precision, + 'limits': { + 'withdraw': { + 'min': this.safeFloat (withdrawLimits, 'minimum'), + 'max': maxWithdrawLimit, + }, + }, + }; + } + + async fetchMarket (symbol, params = {}) { + // this method is for retrieving trading fees and limits per market + // it requires private access and API keys properly set up + await this.loadMarkets (); + const market = this.market (symbol); + return await this.fetchMarketById (market['id'], params); + } + + async fetchMarketById (id, params = {}) { + // this method is for retrieving trading fees and limits per market + // it requires private access and API keys properly set up + const request = { + 'market': id, + }; + const response = await this.privateGetOrdersChance (this.extend (request, params)); + // + // { bid_fee: "0.0005", + // ask_fee: "0.0005", + // market: { id: "KRW-BTC", + // name: "BTC/KRW", + // order_types: ["limit"], + // order_sides: ["ask", "bid"], + // bid: { currency: "KRW", + // price_unit: null, + // min_total: 1000 }, + // ask: { currency: "BTC", + // price_unit: null, + // min_total: 1000 }, + // max_total: "1000000000.0", + // state: "active" }, + // bid_account: { currency: "KRW", + // balance: "0.0", + // locked: "0.0", + // avg_krw_buy_price: "0", + // modified: false }, + // ask_account: { currency: "BTC", + // balance: "0.00780836", + // locked: "0.0", + // avg_krw_buy_price: "6465564.67", + // modified: false } } + // + const marketInfo = this.safeValue (response, 'market'); + const bid = this.safeValue (marketInfo, 'bid'); + const ask = this.safeValue (marketInfo, 'ask'); + const marketId = this.safeString (marketInfo, 'id'); + const baseId = this.safeString (ask, 'currency'); + const quoteId = this.safeString (bid, 'currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + const state = this.safeString (marketInfo, 'state'); + const active = (state === 'active'); + const bidFee = this.safeFloat (response, 'bid_fee'); + const askFee = this.safeFloat (response, 'ask_fee'); + const fee = Math.max (bidFee, askFee); + return { + 'info': response, + 'id': marketId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'maker': fee, + 'taker': fee, + 'limits': { + 'amount': { + 'min': this.safeFloat (ask, 'min_total'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (bid, 'min_total'), + 'max': this.safeFloat (marketInfo, 'max_total'), + }, + }, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketAll (params); + // + // [ { market: "KRW-BTC", + // korean_name: "비트코ì�¸", + // english_name: "Bitcoin" }, + // { market: "KRW-DASH", + // korean_name: "대시", + // english_name: "Dash" }, + // { market: "KRW-ETH", + // korean_name: "ì�´ë�”리움", + // english_name: "Ethereum" }, + // { market: "BTC-ETH", + // korean_name: "ì�´ë�”리움", + // english_name: "Ethereum" }, + // ..., + // { market: "BTC-BSV", + // korean_name: "비트코ì�¸ì—�스브ì�´", + // english_name: "Bitcoin SV" } ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'market'); + const [ quoteId, baseId ] = id.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + const active = true; + const makerFee = this.safeFloat (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['maker']); + const takerFee = this.safeFloat (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['taker']); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'maker': makerFee, + 'taker': takerFee, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + // + // [ { currency: "BTC", + // balance: "0.005", + // locked: "0.0", + // avg_krw_buy_price: "7446000", + // modified: false }, + // { currency: "ETH", + // balance: "0.1", + // locked: "0.0", + // avg_krw_buy_price: "250000", + // modified: false } ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + getSymbolFromMarketId (marketId, market = undefined) { + if (marketId === undefined) { + return undefined; + } + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + return market['symbol']; + } + const [ baseId, quoteId ] = marketId.split (this.options['symbolSeparator']); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + return base + '/' + quote; + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join (','); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchOrderBooksMaxLength']) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols (' + ids.length.toString () + ' characters) exceeding max URL length (' + this.options['fetchOrderBooksMaxLength'].toString () + ' characters), you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join (','); + } + const request = { + 'markets': ids, + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // timestamp: 1542899030043, + // total_ask_size: 109.57065201, + // total_bid_size: 125.74430631, + // orderbook_units: [ { ask_price: 0.02926679, + // bid_price: 0.02919904, + // ask_size: 4.20293961, + // bid_size: 11.65043576 }, + // ..., + // { ask_price: 0.02938209, + // bid_price: 0.0291231, + // ask_size: 0.05135782, + // bid_size: 13.5595 } ] }, + // { market: "KRW-BTC", + // timestamp: 1542899034662, + // total_ask_size: 12.89790974, + // total_bid_size: 4.88395783, + // orderbook_units: [ { ask_price: 5164000, + // bid_price: 5162000, + // ask_size: 2.57606495, + // bid_size: 0.214 }, + // ..., + // { ask_price: 5176000, + // bid_price: 5152000, + // ask_size: 2.752, + // bid_size: 0.4650305 } ] } ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const orderbook = response[i]; + const symbol = this.getSymbolFromMarketId (this.safeString (orderbook, 'market')); + const timestamp = this.safeInteger (orderbook, 'timestamp'); + result[symbol] = { + 'bids': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'bid_price', 'bid_size'), 0, true), + 'asks': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'ask_price', 'ask_size'), 0), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + const orderbooks = await this.fetchOrderBooks ([ symbol ], limit, params); + return this.safeValue (orderbooks, symbol); + } + + parseTicker (ticker, market = undefined) { + // + // { market: "BTC-ETH", + // trade_date: "20181122", + // trade_time: "104543", + // trade_date_kst: "20181122", + // trade_time_kst: "194543", + // trade_timestamp: 1542883543097, + // opening_price: 0.02976455, + // high_price: 0.02992577, + // low_price: 0.02934283, + // trade_price: 0.02947773, + // prev_closing_price: 0.02966, + // change: "FALL", + // change_price: 0.00018227, + // change_rate: 0.0061453136, + // signed_change_price: -0.00018227, + // signed_change_rate: -0.0061453136, + // trade_volume: 1.00000005, + // acc_trade_price: 100.95825586, + // acc_trade_price_24h: 289.58650166, + // acc_trade_volume: 3409.85311036, + // acc_trade_volume_24h: 9754.40510513, + // highest_52_week_price: 0.12345678, + // highest_52_week_date: "2018-02-01", + // lowest_52_week_price: 0.023936, + // lowest_52_week_date: "2017-12-08", + // timestamp: 1542883543813 } + // + const timestamp = this.safeInteger (ticker, 'trade_timestamp'); + const symbol = this.getSymbolFromMarketId (this.safeString (ticker, 'market'), market); + const previous = this.safeFloat (ticker, 'prev_closing_price'); + const last = this.safeFloat (ticker, 'trade_price'); + const change = this.safeFloat (ticker, 'signed_change_price'); + const percentage = this.safeFloat (ticker, 'signed_change_rate'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high_price'), + 'low': this.safeFloat (ticker, 'low_price'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'opening_price'), + 'close': last, + 'last': last, + 'previousClose': previous, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'acc_trade_volume_24h'), + 'quoteVolume': this.safeFloat (ticker, 'acc_trade_price_24h'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join (','); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join (','); + } + const request = { + 'markets': ids, + }; + const response = await this.publicGetTicker (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // trade_date: "20181122", + // trade_time: "104543", + // trade_date_kst: "20181122", + // trade_time_kst: "194543", + // trade_timestamp: 1542883543097, + // opening_price: 0.02976455, + // high_price: 0.02992577, + // low_price: 0.02934283, + // trade_price: 0.02947773, + // prev_closing_price: 0.02966, + // change: "FALL", + // change_price: 0.00018227, + // change_rate: 0.0061453136, + // signed_change_price: -0.00018227, + // signed_change_rate: -0.0061453136, + // trade_volume: 1.00000005, + // acc_trade_price: 100.95825586, + // acc_trade_price_24h: 289.58650166, + // acc_trade_volume: 3409.85311036, + // acc_trade_volume_24h: 9754.40510513, + // highest_52_week_price: 0.12345678, + // highest_52_week_date: "2018-02-01", + // lowest_52_week_price: 0.023936, + // lowest_52_week_date: "2017-12-08", + // timestamp: 1542883543813 } ] + // + const result = {}; + for (let t = 0; t < response.length; t++) { + const ticker = this.parseTicker (response[t]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return this.safeValue (tickers, symbol); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades + // + // { market: "BTC-ETH", + // trade_date_utc: "2018-11-22", + // trade_time_utc: "13:55:24", + // timestamp: 1542894924397, + // trade_price: 0.02914289, + // trade_volume: 0.20074397, + // prev_closing_price: 0.02966, + // change_price: -0.00051711, + // ask_bid: "ASK", + // sequential_id: 15428949259430000 } + // + // fetchOrder trades + // + // { + // "market": "KRW-BTC", + // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", + // "price": "101000.0", + // "volume": "0.77368323", + // "funds": "78142.00623", + // "ask_fee": "117.213009345", + // "bid_fee": "117.213009345", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid", + // } + // + const id = this.safeString2 (trade, 'sequential_id', 'uuid'); + const orderId = undefined; + let timestamp = this.safeInteger (trade, 'timestamp'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + } + let side = undefined; + const askOrBid = this.safeStringLower2 (trade, 'ask_bid', 'side'); + if (askOrBid === 'ask') { + side = 'sell'; + } else if (askOrBid === 'bid') { + side = 'buy'; + } + let cost = this.safeFloat (trade, 'funds'); + const price = this.safeFloat2 (trade, 'trade_price', 'price'); + const amount = this.safeFloat2 (trade, 'trade_volume', 'volume'); + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + } + const marketId = this.safeString (trade, 'market'); + market = this.safeValue (this.markets_by_id, marketId); + let fee = undefined; + let feeCurrency = undefined; + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const feeCost = this.safeString (trade, askOrBid + '_fee'); + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + } + return { + 'id': id, + 'info': trade, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 200; + } + const request = { + 'market': market['id'], + 'count': limit, + }; + const response = await this.publicGetTradesTicks (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // trade_date_utc: "2018-11-22", + // trade_time_utc: "13:55:24", + // timestamp: 1542894924397, + // trade_price: 0.02914289, + // trade_volume: 0.20074397, + // prev_closing_price: 0.02966, + // change_price: -0.00051711, + // ask_bid: "ASK", + // sequential_id: 15428949259430000 }, + // { market: "BTC-ETH", + // trade_date_utc: "2018-11-22", + // trade_time_utc: "13:03:10", + // timestamp: 1542891790123, + // trade_price: 0.02917, + // trade_volume: 7.392, + // prev_closing_price: 0.02966, + // change_price: -0.00049, + // ask_bid: "ASK", + // sequential_id: 15428917910540000 } ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + // + // { market: "BTC-ETH", + // candle_date_time_utc: "2018-11-22T13:47:00", + // candle_date_time_kst: "2018-11-22T22:47:00", + // opening_price: 0.02915963, + // high_price: 0.02915963, + // low_price: 0.02915448, + // trade_price: 0.02915448, + // timestamp: 1542894473674, + // candle_acc_trade_price: 0.0981629437535248, + // candle_acc_trade_volume: 3.36693173, + // unit: 1 }, + // + return [ + this.parse8601 (this.safeString (ohlcv, 'candle_date_time_utc')), + this.safeFloat (ohlcv, 'opening_price'), + this.safeFloat (ohlcv, 'high_price'), + this.safeFloat (ohlcv, 'low_price'), + this.safeFloat (ohlcv, 'trade_price'), + this.safeFloat (ohlcv, 'candle_acc_trade_volume'), // base volume + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const timeframePeriod = this.parseTimeframe (timeframe); + const timeframeValue = this.timeframes[timeframe]; + if (limit === undefined) { + limit = 200; + } + const request = { + 'market': market['id'], + 'timeframe': timeframeValue, + 'count': limit, + }; + let method = 'publicGetCandlesTimeframe'; + if (timeframeValue === 'minutes') { + const numMinutes = Math.round (timeframePeriod / 60); + request['unit'] = numMinutes; + method += 'Unit'; + } + if (since !== undefined) { + // convert `since` to `to` value + request['to'] = this.iso8601 (this.sum (since, timeframePeriod * limit * 1000)); + } + const response = await this[method] (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // candle_date_time_utc: "2018-11-22T13:47:00", + // candle_date_time_kst: "2018-11-22T22:47:00", + // opening_price: 0.02915963, + // high_price: 0.02915963, + // low_price: 0.02915448, + // trade_price: 0.02915448, + // timestamp: 1542894473674, + // candle_acc_trade_price: 0.0981629437535248, + // candle_acc_trade_volume: 3.36693173, + // unit: 1 }, + // { market: "BTC-ETH", + // candle_date_time_utc: "2018-11-22T10:06:00", + // candle_date_time_kst: "2018-11-22T19:06:00", + // opening_price: 0.0294, + // high_price: 0.02940882, + // low_price: 0.02934283, + // trade_price: 0.02937354, + // timestamp: 1542881219276, + // candle_acc_trade_price: 0.0762597110943884, + // candle_acc_trade_volume: 2.5949617, + // unit: 1 } ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + // for market buy it requires the amount of quote currency to spend + if (side === 'buy') { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + amount = amount * price; + } + } + } + } + let orderSide = undefined; + if (side === 'buy') { + orderSide = 'bid'; + } else if (side === 'sell') { + orderSide = 'ask'; + } else { + throw new InvalidOrder (this.id + ' createOrder allows buy or sell side only!'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'side': orderSide, + }; + if (type === 'limit') { + request['volume'] = this.amountToPrecision (symbol, amount); + request['price'] = this.priceToPrecision (symbol, price); + request['ord_type'] = type; + } else if (type === 'market') { + if (side === 'buy') { + request['ord_type'] = 'price'; + request['price'] = this.priceToPrecision (symbol, amount); + } else if (side === 'sell') { + request['ord_type'] = type; + request['volume'] = this.amountToPrecision (symbol, amount); + } + } + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // 'uuid': 'cdd92199-2897-4e14-9448-f923320408ad', + // 'side': 'bid', + // 'ord_type': 'limit', + // 'price': '100.0', + // 'avg_price': '0.0', + // 'state': 'wait', + // 'market': 'KRW-BTC', + // 'created_at': '2018-04-10T15:42:23+09:00', + // 'volume': '0.01', + // 'remaining_volume': '0.01', + // 'reserved_fee': '0.0015', + // 'remaining_fee': '0.0015', + // 'paid_fee': '0.0', + // 'locked': '1.0015', + // 'executed_volume': '0.0', + // 'trades_count': 0 + // } + // + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'uuid': id, + }; + const response = await this.privateDeleteOrder (this.extend (request, params)); + // + // { + // "uuid": "cdd92199-2897-4e14-9448-f923320408ad", + // "side": "bid", + // "ord_type": "limit", + // "price": "100.0", + // "state": "wait", + // "market": "KRW-BTC", + // "created_at": "2018-04-10T15:42:23+09:00", + // "volume": "0.01", + // "remaining_volume": "0.01", + // "reserved_fee": "0.0015", + // "remaining_fee": "0.0015", + // "paid_fee": "0.0", + // "locked": "1.0015", + // "executed_volume": "0.0", + // "trades_count": 0 + // } + // + return this.parseOrder (response); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page': 1, + // 'order_by': 'asc', // 'desc' + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + const response = await this.privateGetDeposits (this.extend (request, params)); + // + // [ + // { + // "type": "deposit", + // "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830", + // "currency": "KRW", + // "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a", + // "state": "ACCEPTED", + // "created_at": "2017-12-08T15:38:02+09:00", + // "done_at": "2017-12-08T15:38:02+09:00", + // "amount": "100000.0", + // "fee": "0.0" + // }, + // ..., + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'state': 'submitting', // 'submitted', 'almost_accepted', 'rejected', 'accepted', 'processing', 'done', 'canceled' + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + const response = await this.privateGetWithdraws (this.extend (request, params)); + // + // [ + // { + // "type": "withdraw", + // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", + // "currency": "BTC", + // "txid": null, + // "state": "processing", + // "created_at": "2018-04-13T11:24:01+09:00", + // "done_at": null, + // "amount": "0.01", + // "fee": "0.0", + // "krw_amount": "80420.0" + // }, + // ..., + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'ACCEPTED': 'ok', // deposits + // withdrawals: + 'submitting': 'pending', // 처리 중 + 'submitted': 'pending', // 처리 완료 + 'almost_accepted': 'pending', // 출금대기중 + 'rejected': 'failed', // ê±°ë¶€ + 'accepted': 'pending', // 승ì�¸ë�¨ + 'processing': 'pending', // 처리 중 + 'done': 'ok', // 완료 + 'canceled': 'canceled', // 취소ë�¨ + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "type": "deposit", + // "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830", + // "currency": "KRW", + // "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a", + // "state": "ACCEPTED", + // "created_at": "2017-12-08T15:38:02+09:00", + // "done_at": "2017-12-08T15:38:02+09:00", + // "amount": "100000.0", + // "fee": "0.0" + // } + // + // fetchWithdrawals + // + // { + // "type": "withdraw", + // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", + // "currency": "BTC", + // "txid": "cd81e9b45df8da29f936836e58c907a106057e454a45767a7b06fcb19b966bba", + // "state": "processing", + // "created_at": "2018-04-13T11:24:01+09:00", + // "done_at": null, + // "amount": "0.01", + // "fee": "0.0", + // "krw_amount": "80420.0" + // } + // + const id = this.safeString (transaction, 'uuid'); + const amount = this.safeFloat (transaction, 'amount'); + const address = undefined; // not present in the data structure received from the exchange + const tag = undefined; // not present in the data structure received from the exchange + const txid = this.safeString (transaction, 'txid'); + const updated = this.parse8601 (this.safeString (transaction, 'done_at')); + const timestamp = this.parse8601 (this.safeString (transaction, 'created_at', updated)); + let type = this.safeString (transaction, 'type'); + if (type === 'withdraw') { + type = 'withdrawal'; + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const feeCost = this.safeFloat (transaction, 'fee'); + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': tag, + 'status': status, + 'type': type, + 'updated': updated, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'wait': 'open', + 'done': 'closed', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", + // "side": "bid", + // "ord_type": "limit", + // "price": "17417000.0", + // "state": "done", + // "market": "KRW-BTC", + // "created_at": "2018-04-05T14:09:14+09:00", + // "volume": "1.0", + // "remaining_volume": "0.0", + // "reserved_fee": "26125.5", + // "remaining_fee": "25974.0", + // "paid_fee": "151.5", + // "locked": "17341974.0", + // "executed_volume": "1.0", + // "trades_count": 2, + // "trades": [ + // { + // "market": "KRW-BTC", + // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", + // "price": "101000.0", + // "volume": "0.77368323", + // "funds": "78142.00623", + // "ask_fee": "117.213009345", + // "bid_fee": "117.213009345", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid", + // }, + // { + // "market": "KRW-BTC", + // "uuid": "f73da467-c42f-407d-92fa-e10d86450a20", + // "price": "101000.0", + // "volume": "0.22631677", + // "funds": "22857.99377", + // "ask_fee": "34.286990655", // missing in market orders + // "bid_fee": "34.286990655", // missing in market orders + // "created_at": "2018-04-05T14:09:15+09:00", // missing in market orders + // "side": "bid", + // }, + // ], + // } + // + const id = this.safeString (order, 'uuid'); + let side = this.safeString (order, 'side'); + if (side === 'bid') { + side = 'buy'; + } else { + side = 'sell'; + } + let type = this.safeString (order, 'ord_type'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let lastTradeTimestamp = undefined; + let price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + const remaining = this.safeFloat (order, 'remaining_volume'); + const filled = this.safeFloat (order, 'executed_volume'); + let cost = undefined; + if (type === 'price') { + type = 'market'; + cost = price; + price = undefined; + } + let average = undefined; + let fee = undefined; + let feeCost = this.safeFloat (order, 'paid_fee'); + let feeCurrency = undefined; + const marketId = this.safeString (order, 'market'); + market = this.safeValue (this.markets_by_id, marketId); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + let trades = this.safeValue (order, 'trades', []); + trades = this.parseTrades (trades, market, undefined, undefined, { 'order': id }); + const numTrades = trades.length; + if (numTrades > 0) { + // the timestamp in fetchOrder trades is missing + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + let getFeesFromTrades = false; + if (feeCost === undefined) { + getFeesFromTrades = true; + feeCost = 0; + } + cost = 0; + for (let i = 0; i < numTrades; i++) { + const trade = trades[i]; + cost = this.sum (cost, trade['cost']); + if (getFeesFromTrades) { + const tradeFee = this.safeValue (trades[i], 'fee', {}); + const tradeFeeCost = this.safeFloat (tradeFee, 'cost'); + if (tradeFeeCost !== undefined) { + feeCost = this.sum (feeCost, tradeFeeCost); + } + } + } + average = cost / filled; + } + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + } + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + return result; + } + + async fetchOrdersByState (state, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'market': this.marketId (symbol), + 'state': state, + // 'page': 1, + // 'order_by': 'asc', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // [ + // { + // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", + // "side": "bid", + // "ord_type": "limit", + // "price": "17417000.0", + // "state": "done", + // "market": "KRW-BTC", + // "created_at": "2018-04-05T14:09:14+09:00", + // "volume": "1.0", + // "remaining_volume": "0.0", + // "reserved_fee": "26125.5", + // "remaining_fee": "25974.0", + // "paid_fee": "151.5", + // "locked": "17341974.0", + // "executed_volume": "1.0", + // "trades_count":2 + // }, + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('wait', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('done', symbol, since, limit, params); + } + + async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('cancel', symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'uuid': id, + }; + const response = await this.privateGetOrder (this.extend (request, params)); + // + // { + // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", + // "side": "bid", + // "ord_type": "limit", + // "price": "17417000.0", + // "state": "done", + // "market": "KRW-BTC", + // "created_at": "2018-04-05T14:09:14+09:00", + // "volume": "1.0", + // "remaining_volume": "0.0", + // "reserved_fee": "26125.5", + // "remaining_fee": "25974.0", + // "paid_fee": "151.5", + // "locked": "17341974.0", + // "executed_volume": "1.0", + // "trades_count": 2, + // "trades": [ + // { + // "market": "KRW-BTC", + // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", + // "price": "101000.0", + // "volume": "0.77368323", + // "funds": "78142.00623", + // "ask_fee": "117.213009345", + // "bid_fee": "117.213009345", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid" + // }, + // { + // "market": "KRW-BTC", + // "uuid": "f73da467-c42f-407d-92fa-e10d86450a20", + // "price": "101000.0", + // "volume": "0.22631677", + // "funds": "22857.99377", + // "ask_fee": "34.286990655", + // "bid_fee": "34.286990655", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid" + // } + // ] + // } + // + return this.parseOrder (response); + } + + parseDepositAddresses (addresses) { + const result = []; + for (let i = 0; i < addresses.length; i++) { + result.push (this.parseDepositAddress (addresses[i])); + } + return result; + } + + async fetchDepositAddresses (codes = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetDepositsCoinAddresses (params); + // + // [ + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // }, + // { + // "currency": "ETH", + // "deposit_address": "0x0d73e0a482b8cf568976d2e8688f4a899d29301c", + // "secondary_address": null + // }, + // { + // "currency": "XRP", + // "deposit_address": "rN9qNpgnBaZwqCg8CvUZRPqCcPPY7wfWep", + // "secondary_address": "3057887915" + // } + // ] + // + return this.parseDepositAddresses (response); + } + + parseDepositAddress (depositAddress, currency = undefined) { + // + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // } + // + const address = this.safeString (depositAddress, 'deposit_address'); + const tag = this.safeString (depositAddress, 'secondary_address'); + const currencyId = this.safeString (depositAddress, 'currency'); + const code = this.safeCurrencyCode (currencyId); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': depositAddress, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const response = await this.privateGetDepositsCoinAddress (this.extend ({ + 'currency': currency['id'], + }, params)); + // + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // } + // + return this.parseDepositAddress (response); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + // + // https://docs.upbit.com/v1.0/reference#%EC%9E%85%EA%B8%88-%EC%A3%BC%EC%86%8C-%EC%83%9D%EC%84%B1-%EC%9A%94%EC%B2%AD + // can be any of the two responses: + // + // { + // "success" : true, + // "message" : "Creating BTC deposit address." + // } + // + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // } + // + const message = this.safeString (response, 'message'); + if (message !== undefined) { + return { + 'currency': code, + 'address': undefined, + 'tag': undefined, + 'info': response, + }; + } + return this.parseDepositAddress (response); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'amount': amount, + }; + let method = 'privatePostWithdraws'; + if (code !== 'KRW') { + method += 'Coin'; + request['currency'] = currency['id']; + request['address'] = address; + if (tag !== undefined) { + request['secondary_address'] = tag; + } + } else { + method += 'Krw'; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "type": "withdraw", + // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", + // "currency": "BTC", + // "txid": "ebe6937b-130e-4066-8ac6-4b0e67f28adc", + // "state": "processing", + // "created_at": "2018-04-13T11:24:01+09:00", + // "done_at": null, + // "amount": "0.01", + // "fee": "0.0", + // "krw_amount": "80420.0" + // } + // + return this.parseTransaction (response); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'], { + 'hostname': this.hostname, + }); + url += '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method !== 'POST') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = { + 'access_key': this.apiKey, + 'nonce': nonce, + }; + if (Object.keys (query).length) { + const auth = this.urlencode (query); + const hash = this.hash (this.encode (auth), 'sha512'); + request['query_hash'] = hash; + request['query_hash_alg'] = 'SHA512'; + } + const jwt = this.jwt (request, this.encode (this.secret)); + headers = { + 'Authorization': 'Bearer ' + jwt, + }; + if ((method !== 'GET') && (method !== 'DELETE')) { + body = this.json (params); + headers['Content-Type'] = 'application/json'; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // { 'error': { 'message': "Missing request parameter error. Check the required parameters!", 'name': 400 } }, + // { 'error': { 'message': "side is missing, side does not have a valid value", 'name': "validation_error" } }, + // { 'error': { 'message': "ê°œì�¸ì •ë³´ ì œ 3ìž� 제공 ë�™ì�˜ê°€ 필요합니다.", 'name': "thirdparty_agreement_required" } }, + // { 'error': { 'message': "권한ì�´ 부족합니다.", 'name': "out_of_scope" } }, + // { 'error': { 'message': "주문ì�„ 찾지 못했습니다.", 'name': "order_not_found" } }, + // { 'error': { 'message': "주문가능한 금액(ETH)ì�´ 부족합니다.", 'name': "insufficient_funds_ask" } }, + // { 'error': { 'message': "주문가능한 금액(BTC)ì�´ 부족합니다.", 'name': "insufficient_funds_bid" } }, + // { 'error': { 'message': "잘못ë�œ 엑세스 키입니다.", 'name': "invalid_access_key" } }, + // { 'error': { 'message': "Jwt 토í�° ê²€ì¦�ì—� 실패했습니다.", 'name': "jwt_verification" } } + // + const error = this.safeValue (response, 'error'); + if (error !== undefined) { + const message = this.safeString (error, 'message'); + const name = this.safeString (error, 'name'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], name, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], name, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],168:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class vaultoro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'vaultoro', + 'name': 'Vaultoro', + 'countries': [ 'CH' ], + 'rateLimit': 1000, + 'version': '1', + 'has': { + 'CORS': true, + 'fetchMarkets': true, + 'fetchOrderBook': true, + 'fetchBalance': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchTrades': true, + 'fetchTicker': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766880-f205e870-5ee9-11e7-8fe2-0d5b15880752.jpg', + 'api': 'https://api.vaultoro.com', + 'www': 'https://www.vaultoro.com', + 'doc': 'https://api.vaultoro.com', + }, + 'commonCurrencies': { + 'GLD': 'Gold', + }, + 'api': { + 'public': { + 'get': [ + 'bidandask', + 'buyorders', + 'latest', + 'latesttrades', + 'markets', + 'orderbook', + 'sellorders', + 'transactions/day', + 'transactions/hour', + 'transactions/month', + ], + }, + 'private': { + 'get': [ + 'balance', + 'mytrades', + 'orders', + ], + 'post': [ + 'buy/{symbol}/{type}', + 'cancel/{id}', + 'sell/{symbol}/{type}', + 'withdraw', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const result = []; + const response = await this.publicGetMarkets (params); + const market = this.safeValue (response, 'data'); + const baseId = this.safeString (market, 'MarketCurrency'); + const quoteId = this.safeString (market, 'BaseCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = this.safeString (market, 'MarketName'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const balances = this.safeValue (response, 'data'); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'cash'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetOrderbook (params); + const orderbook = { + 'bids': response['data'][0]['b'], + 'asks': response['data'][1]['s'], + }; + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'Gold_Price', 'Gold_Amount'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'Time')); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'Gold_Price'); + const amount = this.safeFloat (trade, 'Gold_Amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTransactionsDay (params); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side) + 'SymbolType'; + const request = { + 'symbol': market['quoteId'].toLowerCase (), + 'type': type, + 'gld': amount, + 'price': price || 1, + }; + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['data']['Order_ID'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelId (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/'; + if (api === 'public') { + url += path; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + url += this.version + '/' + this.implodeParams (path, params); + const query = this.extend ({ + 'nonce': nonce, + 'apikey': this.apiKey, + }, this.omit (params, this.extractParams (path))); + url += '?' + this.urlencode (query); + headers = { + 'Content-Type': 'application/json', + 'X-Signature': this.hmac (this.encode (url), this.encode (this.secret)), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7}],169:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const foxbit = require ('./foxbit.js'); + +// --------------------------------------------------------------------------- + +module.exports = class vbtc extends foxbit { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'vbtc', + 'name': 'VBTC', + 'countries': [ 'VN' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991481-1f53d1d8-6481-11e7-884e-21d17e7939db.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://vbtc.exchange', + 'doc': 'https://blinktrade.com/docs', + }, + 'options': { + 'brokerId': '3', // https://blinktrade.com/docs/#brokers + }, + }); + } +}; + +},{"./foxbit.js":86}],170:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, DDoSProtection, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class whitebit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'whitebit', + 'name': 'WhiteBit', + 'version': 'v2', + 'countries': [ 'EE' ], + 'rateLimit': 500, + 'has': { + 'cancelOrder': false, + 'CORS': false, + 'createDepositAddress': false, + 'createLimitOrder': false, + 'createMarketOrder': false, + 'createOrder': false, + 'deposit': false, + 'editOrder': false, + 'fetchBalance': false, + 'fetchBidsAsks': false, + 'fetchCurrencies': true, + 'fetchMarkets': true, + 'fetchOHLCV': true, + 'fetchOrderBook': true, + 'fetchStatus': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTrades': true, + 'privateAPI': false, + 'publicAPI': true, + }, + 'timeframes': { + '1m': '1m', + '3m': '3m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '4h': '4h', + '6h': '6h', + '8h': '8h', + '12h': '12h', + '1d': '1d', + '3d': '3d', + '1w': '1w', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/66732963-8eb7dd00-ee66-11e9-849b-10d9282bb9e0.jpg', + 'api': { + 'web': 'https://whitebit.com/', + 'publicV2': 'https://whitebit.com/api/v2/public', + 'publicV1': 'https://whitebit.com/api/v1/public', + }, + 'www': 'https://www.whitebit.com', + 'doc': 'https://documenter.getpostman.com/view/7473075/SVSPomwS?version=latest#intro', + 'fees': 'https://whitebit.com/fee-schedule', + 'referral': 'https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963', + }, + 'api': { + 'web': { + 'get': [ + 'v1/healthcheck', + ], + }, + 'publicV1': { + 'get': [ + 'markets', + 'tickers', + 'ticker', + 'symbols', + 'depth/result', + 'history', + 'kline', + ], + }, + 'publicV2': { + 'get': [ + 'markets', + 'ticker', + 'assets', + 'fee', + 'depth/{market}', + 'trades/{market}', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + }, + 'options': { + 'fetchTradesMethod': 'fetchTradesV1', + }, + 'exceptions': { + 'exact': { + }, + 'broad': { + 'Market is not available': BadSymbol, // {"success":false,"message":{"market":["Market is not available"]},"result":[]} + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicV2GetMarkets (params); + // + // { + // "success":true, + // "message":"", + // "result":[ + // { + // "name":"BTC_USD", + // "moneyPrec":"2", + // "stock":"BTC", + // "money":"USD", + // "stockPrec":"6", + // "feePrec":"4", + // "minAmount":"0.001", + // "tradesEnabled":true, + // "minTotal":"0.001" + // } + // ] + // } + // + const markets = this.safeValue (response, 'result'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'stock'); + const quoteId = this.safeString (market, 'money'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = this.safeValue (market, 'tradesEnabled'); + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': { + 'amount': this.safeInteger (market, 'stockPrec'), + 'price': this.safeInteger (market, 'moneyPrec'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (market, 'minTotal'), + 'max': undefined, + }, + }, + }; + result.push (entry); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicV2GetAssets (params); + // + // { + // "success":true, + // "message":"", + // "result":{ + // "BTC":{ + // "id":"4f37bc79-f612-4a63-9a81-d37f7f9ff622", + // "lastUpdateTimestamp":"2019-10-12T04:40:05.000Z", + // "name":"Bitcoin", + // "canWithdraw":true, + // "canDeposit":true, + // "minWithdrawal":"0.001", + // "maxWithdrawal":"0", + // "makerFee":"0.1", + // "takerFee":"0.1" + // } + // } + // } + // + const currencies = this.safeValue (response, 'result'); + const ids = Object.keys (currencies); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = currencies[id]; + // breaks down in Python due to utf8 encoding issues on the exchange side + // const name = this.safeString (currency, 'name'); + const canDeposit = this.safeValue (currency, 'canDeposit', true); + const canWithdraw = this.safeValue (currency, 'canWithdraw', true); + const active = canDeposit && canWithdraw; + const code = this.safeCurrencyCode (id); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, // the original payload + 'name': undefined, // see the comment above + 'active': active, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawal'), + 'max': this.safeFloat (currency, 'maxWithdrawal'), + }, + }, + }; + } + return result; + } + + async fetchTradingFees (params = {}) { + const response = await this.publicV2GetFee (params); + const fees = this.safeValue (response, 'result'); + return { + 'maker': this.safeFloat (fees, 'makerFee'), + 'taker': this.safeFloat (fees, 'takerFee'), + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicV1GetTicker (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result": { + // "bid":"0.021979", + // "ask":"0.021996", + // "open":"0.02182", + // "high":"0.022039", + // "low":"0.02161", + // "last":"0.021987", + // "volume":"2810.267", + // "deal":"61.383565474", + // "change":"0.76", + // }, + // } + // + const ticker = this.safeValue (response, 'result', {}); + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + // + // fetchTicker + // + // { + // "bid":"0.021979", + // "ask":"0.021996", + // "open":"0.02182", + // "high":"0.022039", + // "low":"0.02161", + // "last":"0.021987", + // "volume":"2810.267", + // "deal":"61.383565474", + // "change":"0.76", + // } + // + // fetchTickers v1 + // + // { + // "at":1571022144, + // "ticker": { + // "bid":"0.022024", + // "ask":"0.022042", + // "low":"0.02161", + // "high":"0.022062", + // "last":"0.022036", + // "vol":"2813.503", + // "deal":"61.457279261", + // "change":"0.95" + // } + // } + // + const timestamp = this.safeTimestamp (ticker, 'at', this.milliseconds ()); + ticker = this.safeValue (ticker, 'ticker', ticker); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'change'); + let change = undefined; + if (percentage !== undefined) { + change = this.numberToString (percentage * 0.01); + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'deal'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicV1GetTickers (params); + // + // { + // "success":true, + // "message":"", + // "result": { + // "ETH_BTC": { + // "at":1571022144, + // "ticker": { + // "bid":"0.022024", + // "ask":"0.022042", + // "low":"0.02161", + // "high":"0.022062", + // "last":"0.022036", + // "vol":"2813.503", + // "deal":"61.457279261", + // "change":"0.95" + // } + // }, + // }, + // } + // + const data = this.safeValue (response, 'result'); + const marketIds = Object.keys (data); + const result = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let market = undefined; + let symbol = marketId; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = this.parseTicker (data[marketId], market); + result[symbol] = this.extend (ticker, { 'symbol': symbol }); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 50, maximum = 100 + } + const response = await this.publicV2GetDepthMarket (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result":{ + // "lastUpdateTimestamp":"2019-10-14T03:15:47.000Z", + // "asks":[ + // ["0.02204","2.03"], + // ["0.022041","2.492"], + // ["0.022042","2.254"], + // ], + // "bids":[ + // ["0.022018","2.327"], + // ["0.022017","1.336"], + // ["0.022015","2.089"], + // ], + // } + // } + // + const result = this.safeValue (response, 'result', {}); + const timestamp = this.parse8601 (this.safeString (result, 'lastUpdateTimestamp')); + return this.parseOrderBook (result, timestamp); + } + + async fetchTradesV1 (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'lastId': 1, // todo add since + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 50, maximum = 10000 + } + const response = await this.publicV1GetHistory (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result":[ + // { + // "id":11887426, + // "type":"buy", + // "time":1571023057.413769, + // "amount":"0.171", + // "price":"0.022052" + // } + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchTradesV2 (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 50, maximum = 10000 + } + const response = await this.publicV2GetTradesMarket (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result": [ + // { + // "tradeId":11903347, + // "price":"0.022044", + // "volume":"0.029", + // "time":"2019-10-14T06:30:57.000Z", + // "isBuyerMaker":false + // }, + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + const method = this.safeString (this.options, 'fetchTradesMethod', 'fetchTradesV2'); + return await this[method] (symbol, since, limit, params); + } + + parseTrade (trade, market = undefined) { + // + // fetchTradesV1 + // + // { + // "id":11887426, + // "type":"buy", + // "time":1571023057.413769, + // "amount":"0.171", + // "price":"0.022052" + // } + // + // fetchTradesV2 + // + // { + // "tradeId":11903347, + // "price":"0.022044", + // "volume":"0.029", + // "time":"2019-10-14T06:30:57.000Z", + // "isBuyerMaker":false + // } + // + let timestamp = this.safeValue (trade, 'time'); + if (typeof timestamp === 'string') { + timestamp = this.parse8601 (timestamp); + } else { + timestamp = parseInt (timestamp * 1000); + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'amount', 'volume'); + const id = this.safeString2 (trade, 'id', 'tradeId'); + let side = this.safeString (trade, 'type'); + if (side === undefined) { + const isBuyerMaker = this.safeValue (trade, 'isBuyerMaker'); + side = isBuyerMaker ? 'buy' : 'sell'; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + if (amount !== undefined && price !== undefined) { + cost = amount * price; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': undefined, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'interval': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + if (limit !== undefined) { + request['limit'] = limit; // default == max == 500 + } + const response = await this.publicV1GetKline (this.extend (request, params)); + const result = this.safeValue (response, 'result'); + return this.parseOHLCVs (result, market, timeframe, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, // timestamp + parseFloat (ohlcv[1]), // open + parseFloat (ohlcv[3]), // high + parseFloat (ohlcv[4]), // low + parseFloat (ohlcv[2]), // close + parseFloat (ohlcv[5]), // volume + ]; + } + + async fetchStatus (params = {}) { + const response = await this.webGetV1Healthcheck (); + const status = this.safeInteger (response, 'status'); + let formattedStatus = 'ok'; + if (status === 503) { + formattedStatus = 'maintenance'; + } + this.status = this.extend (this.status, { + 'status': formattedStatus, + 'updated': this.milliseconds (), + }); + return this.status; + } + + sign (path, api = 'publicV1', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 418) || (code === 429)) { + throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); + } + if (code === 404) { + throw new ExchangeError (this.id + ' ' + code.toString () + ' endpoint not found'); + } + if (response !== undefined) { + const success = this.safeValue (response, 'success'); + if (!success) { + const feedback = this.id + ' ' + body; + const message = this.safeValue (response, 'message'); + if (typeof message === 'string') { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + } + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + throw new ExchangeError (feedback); + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],171:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, NotSupported, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class xbtce extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'xbtce', + 'name': 'xBTCe', + 'countries': [ 'RU' ], + 'rateLimit': 2000, // responses are cached every 2 seconds + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'createMarketOrder': false, + 'fetchOHLCV': false, + }, + 'urls': { + 'referral': 'https://xbtce.com/?agent=XX97BTCXXXG687021000B', + 'logo': 'https://user-images.githubusercontent.com/1294454/28059414-e235970c-662c-11e7-8c3a-08e31f78684b.jpg', + 'api': 'https://cryptottlivewebapi.xbtce.net:8443/api', + 'www': 'https://www.xbtce.com', + 'doc': [ + 'https://www.xbtce.com/tradeapi', + 'https://support.xbtce.info/Knowledgebase/Article/View/52/25/xbtce-exchange-api', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'currency', + 'currency/{filter}', + 'level2', + 'level2/{filter}', + 'quotehistory/{symbol}/{periodicity}/bars/ask', + 'quotehistory/{symbol}/{periodicity}/bars/bid', + 'quotehistory/{symbol}/level2', + 'quotehistory/{symbol}/ticks', + 'symbol', + 'symbol/{filter}', + 'tick', + 'tick/{filter}', + 'ticker', + 'ticker/{filter}', + 'tradesession', + ], + }, + 'private': { + 'get': [ + 'tradeserverinfo', + 'tradesession', + 'currency', + 'currency/{filter}', + 'level2', + 'level2/{filter}', + 'symbol', + 'symbol/{filter}', + 'tick', + 'tick/{filter}', + 'account', + 'asset', + 'asset/{id}', + 'position', + 'position/{id}', + 'trade', + 'trade/{id}', + 'quotehistory/{symbol}/{periodicity}/bars/ask', + 'quotehistory/{symbol}/{periodicity}/bars/ask/info', + 'quotehistory/{symbol}/{periodicity}/bars/bid', + 'quotehistory/{symbol}/{periodicity}/bars/bid/info', + 'quotehistory/{symbol}/level2', + 'quotehistory/{symbol}/level2/info', + 'quotehistory/{symbol}/periodicities', + 'quotehistory/{symbol}/ticks', + 'quotehistory/{symbol}/ticks/info', + 'quotehistory/cache/{symbol}/{periodicity}/bars/ask', + 'quotehistory/cache/{symbol}/{periodicity}/bars/bid', + 'quotehistory/cache/{symbol}/level2', + 'quotehistory/cache/{symbol}/ticks', + 'quotehistory/symbols', + 'quotehistory/version', + ], + 'post': [ + 'trade', + 'tradehistory', + ], + 'put': [ + 'trade', + ], + 'delete': [ + 'trade', + ], + }, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.privateGetSymbol (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'Symbol'); + const baseId = this.safeString (market, 'MarginCurrency'); + const quoteId = this.safeString (market, 'ProfitCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + let symbol = base + '/' + quote; + symbol = market['IsTradeAllowed'] ? symbol : id; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetAsset (params); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'Currency'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'FreeAmount'), + 'used': this.safeFloat (balance, 'LockedAmount'), + 'total': this.safeFloat (balance, 'Amount'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'filter': market['id'], + }; + const response = await this.privateGetLevel2Filter (this.extend (request, params)); + const orderbook = response[0]; + const timestamp = this.safeInteger (orderbook, 'Timestamp'); + return this.parseOrderBook (orderbook, timestamp, 'Bids', 'Asks', 'Price', 'Volume'); + } + + parseTicker (ticker, market = undefined) { + let timestamp = 0; + let last = undefined; + if ('LastBuyTimestamp' in ticker) { + if (timestamp < ticker['LastBuyTimestamp']) { + timestamp = ticker['LastBuyTimestamp']; + last = ticker['LastBuyPrice']; + } + } + if ('LastSellTimestamp' in ticker) { + if (timestamp < ticker['LastSellTimestamp']) { + timestamp = ticker['LastSellTimestamp']; + last = ticker['LastSellPrice']; + } + } + if (!timestamp) { + timestamp = this.milliseconds (); + } + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': ticker['DailyBestBuyPrice'], + 'low': ticker['DailyBestSellPrice'], + 'bid': ticker['BestBid'], + 'bidVolume': undefined, + 'ask': ticker['BestAsk'], + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': ticker['DailyTradedTotalVolume'], + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const tickers = this.indexBy (response, 'Symbol'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + let symbol = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'filter': market['id'], + }; + const response = await this.publicGetTickerFilter (this.extend (request, params)); + const length = response.length; + if (length < 1) { + throw new ExchangeError (this.id + ' fetchTicker returned empty response, xBTCe public API error'); + } + const tickers = this.indexBy (response, 'Symbol'); + const ticker = tickers[market['id']]; + return this.parseTicker (ticker, market); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // no method for trades? + return await this.privateGetTrade (params); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv['Timestamp'], + ohlcv['Open'], + ohlcv['High'], + ohlcv['Low'], + ohlcv['Close'], + ohlcv['Volume'], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + // let minutes = parseInt (timeframe / 60); // 1 minute by default + // let periodicity = minutes.toString (); + // await this.loadMarkets (); + // let market = this.market (symbol); + // if (since === undefined) + // since = this.seconds () - 86400 * 7; // last day by defulat + // if (limit === undefined) + // limit = 1000; // default + // let response = await this.privateGetQuotehistorySymbolPeriodicityBarsBid (this.extend ({ + // 'symbol': market['id'], + // 'periodicity': periodicity, + // 'timestamp': since, + // 'count': limit, + // }, params)); + // return this.parseOHLCVs (response['Bars'], market, timeframe, since, limit); + throw new NotSupported (this.id + ' fetchOHLCV is disabled by the exchange'); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'pair': this.marketId (symbol), + 'type': side, + 'amount': amount, + 'rate': price, + }; + const response = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': response, + 'id': response['Id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'Type': 'Cancel', + 'Id': id, + }; + return await this.privateDeleteTrade (this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (!this.apiKey) { + throw new AuthenticationError (this.id + ' requires apiKey for all requests, their public API is always busy'); + } + if (!this.uid) { + throw new AuthenticationError (this.id + ' requires uid property for authentication and trading, their public API is always busy'); + } + let url = this.urls['api'] + '/' + this.version; + if (api === 'public') { + url += '/' + api; + } + url += '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + headers = { 'Accept-Encoding': 'gzip, deflate' }; + const nonce = this.nonce ().toString (); + if (method === 'POST') { + if (Object.keys (query).length) { + headers['Content-Type'] = 'application/json'; + body = this.json (query); + } else { + url += '?' + this.urlencode (query); + } + } + let auth = nonce + this.uid + this.apiKey + method + url; + if (body) { + auth += body; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); + const credentials = this.uid + ':' + this.apiKey + ':' + nonce + ':' + this.decode (signature); + headers['Authorization'] = 'HMAC ' + credentials; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],172:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InvalidNonce, InsufficientFunds, OrderNotFound, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class yobit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'yobit', + 'name': 'YoBit', + 'countries': [ 'RU' ], + 'rateLimit': 3000, // responses are cached every 2 seconds + 'version': '3', + 'has': { + 'CORS': false, + 'createDepositAddress': true, + 'createMarketOrder': false, + 'fetchClosedOrders': 'emulated', + 'fetchDepositAddress': true, + 'fetchDeposits': false, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBooks': true, + 'fetchOrders': 'emulated', + 'fetchTickers': true, + 'fetchTransactions': false, + 'fetchWithdrawals': false, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766910-cdcbfdae-5eea-11e7-9859-03fea873272d.jpg', + 'api': { + 'public': 'https://yobit.net/api', + 'private': 'https://yobit.net/tapi', + }, + 'www': 'https://www.yobit.net', + 'doc': 'https://www.yobit.net/en/api/', + 'fees': 'https://www.yobit.net/en/fees/', + }, + 'api': { + 'public': { + 'get': [ + 'depth/{pair}', + 'info', + 'ticker/{pair}', + 'trades/{pair}', + ], + }, + 'private': { + 'post': [ + 'ActiveOrders', + 'CancelOrder', + 'GetDepositAddress', + 'getInfo', + 'OrderInfo', + 'Trade', + 'TradeHistory', + 'WithdrawCoinsToAddress', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.002, + 'taker': 0.002, + }, + 'funding': { + 'withdraw': {}, + }, + }, + 'commonCurrencies': { + 'AIR': 'AirCoin', + 'ANI': 'ANICoin', + 'ANT': 'AntsCoin', // what is this, a coin for ants? + 'ATMCHA': 'ATM', + 'ASN': 'Ascension', + 'AST': 'Astral', + 'ATM': 'Autumncoin', + 'BCC': 'BCH', + 'BCS': 'BitcoinStake', + 'BLN': 'Bulleon', + 'BOT': 'BOTcoin', + 'BON': 'BONES', + 'BPC': 'BitcoinPremium', + 'BTS': 'Bitshares2', + 'CAT': 'BitClave', + 'CMT': 'CometCoin', + 'COV': 'Coven Coin', + 'COVX': 'COV', + 'CPC': 'Capricoin', + 'CS': 'CryptoSpots', + 'DCT': 'Discount', + 'DFT': 'DraftCoin', + 'DGD': 'DarkGoldCoin', + 'DIRT': 'DIRTY', + 'DROP': 'FaucetCoin', + 'DSH': 'DASH', + 'EKO': 'EkoCoin', + 'ENTER': 'ENTRC', + 'EPC': 'ExperienceCoin', + 'ESC': 'EdwardSnowden', + 'EUROPE': 'EUROP', + 'EXT': 'LifeExtension', + 'FUNK': 'FUNKCoin', + 'GCC': 'GlobalCryptocurrency', + 'GEN': 'Genstake', + 'GENE': 'Genesiscoin', + 'GOLD': 'GoldMint', + 'GOT': 'Giotto Coin', + 'HTML5': 'HTML', + 'HYPERX': 'HYPER', + 'ICN': 'iCoin', + 'INSANE': 'INSN', + 'JNT': 'JointCoin', + 'JPC': 'JupiterCoin', + 'KNC': 'KingN Coin', + 'LBTCX': 'LiteBitcoin', + 'LIZI': 'LiZi', + 'LOC': 'LocoCoin', + 'LOCX': 'LOC', + 'LUNYR': 'LUN', + 'LUN': 'LunarCoin', // they just change the ticker if it is already taken + 'MDT': 'Midnight', + 'NAV': 'NavajoCoin', + 'NBT': 'NiceBytes', + 'OMG': 'OMGame', + 'PAC': '$PAC', + 'PLAY': 'PlayCoin', + 'PIVX': 'Darknet', + 'PRS': 'PRE', + 'PUTIN': 'PUT', + 'STK': 'StakeCoin', + 'SUB': 'Subscriptio', + 'PAY': 'EPAY', + 'PLC': 'Platin Coin', + 'RCN': 'RCoin', + 'REP': 'Republicoin', + 'RUR': 'RUB', + 'TTC': 'TittieCoin', + 'XIN': 'XINCoin', + }, + 'options': { + // 'fetchTickersMaxLength': 2048, + 'fetchOrdersRequiresSymbol': true, + 'fetchTickersMaxLength': 512, + }, + 'exceptions': { + 'exact': { + '803': InvalidOrder, // "Count could not be less than 0.001." (selling below minAmount) + '804': InvalidOrder, // "Count could not be more than 10000." (buying above maxAmount) + '805': InvalidOrder, // "price could not be less than X." (minPrice violation on buy & sell) + '806': InvalidOrder, // "price could not be more than X." (maxPrice violation on buy & sell) + '807': InvalidOrder, // "cost could not be less than X." (minCost violation on buy & sell) + '831': InsufficientFunds, // "Not enougth X to create buy order." (buying with balance.quote < order.cost) + '832': InsufficientFunds, // "Not enougth X to create sell order." (selling with balance.base < order.amount) + '833': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) + }, + 'broad': { + 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} + 'invalid api key': AuthenticationError, + 'invalid sign': AuthenticationError, + 'api key dont have trade permission': AuthenticationError, + 'invalid parameter': InvalidOrder, + 'invalid order': InvalidOrder, + 'Requests too often': DDoSProtection, + 'not available': ExchangeNotAvailable, + 'data unavailable': ExchangeNotAvailable, + 'external service unavailable': ExchangeNotAvailable, + 'Total transaction amount': ExchangeError, // { "success": 0, "error": "Total transaction amount is less than minimal total: 0.00010000"} + 'Insufficient funds': InsufficientFunds, + 'invalid key': AuthenticationError, + 'invalid nonce': InvalidNonce, // {"success":0,"error":"invalid nonce (has already been used)"}' + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfo (params); + // + // { + // "success":1, + // "return":{ + // "funds":{ + // "ltc":22, + // "nvc":423.998, + // "ppc":10, + // }, + // "funds_incl_orders":{ + // "ltc":32, + // "nvc":523.998, + // "ppc":20, + // }, + // "rights":{ + // "info":1, + // "trade":0, + // "withdraw":0 + // }, + // "transaction_count":0, + // "open_orders":1, + // "server_time":1418654530 + // } + // } + // + const balances = this.safeValue (response, 'return', {}); + const result = { 'info': response }; + const free = this.safeValue (balances, 'funds', {}); + const total = this.safeValue (balances, 'funds_incl_orders', {}); + const currencyIds = Object.keys (this.extend (free, total)); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['total'] = this.safeFloat (total, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInfo (params); + const markets = this.safeValue (response, 'pairs'); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ baseId, quoteId ] = id.split ('_'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'decimal_places'), + 'price': this.safeInteger (market, 'decimal_places'), + }; + const amountLimits = { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }; + const priceLimits = { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }; + const costLimits = { + 'min': this.safeFloat (market, 'min_total'), + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const hidden = this.safeInteger (market, 'hidden'); + const active = (hidden === 0); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'taker': market['fee'] / 100, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const market_id_in_reponse = (market['id'] in response); + if (!market_id_in_reponse) { + throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); + } + const orderbook = response[market['id']]; + return this.parseOrderBook (orderbook); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join ('-'); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + // 'ignore_invalid': true, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const result = {}; + ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + if (id in this.markets_by_id) { + const market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseOrderBook (response[id]); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { high: 0.03497582, + // low: 0.03248474, + // avg: 0.03373028, + // vol: 120.11485715062999, + // vol_cur: 3572.24914074, + // last: 0.0337611, + // buy: 0.0337442, + // sell: 0.03377798, + // updated: 1537522009 } + // + const timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': this.safeFloat (ticker, 'vol_cur'), + 'quoteVolume': this.safeFloat (ticker, 'vol'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = this.ids; + if (symbols === undefined) { + const numIds = ids.length; + ids = ids.join ('-'); + const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); + // max URL length is 2048 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + const tickers = await this.publicGetTickerPair (this.extend (request, params)); + const result = {}; + const keys = Object.keys (tickers); + for (let k = 0; k < keys.length; k++) { + const id = keys[k]; + const ticker = tickers[id]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let side = this.safeString (trade, 'type'); + if (side === 'ask') { + side = 'sell'; + } else if (side === 'bid') { + side = 'buy'; + } + const price = this.safeFloat2 (trade, 'rate', 'price'); + const id = this.safeString2 (trade, 'trade_id', 'tid'); + const order = this.safeString (trade, 'order_id'); + if ('pair' in trade) { + const marketId = this.safeString (trade, 'pair'); + market = this.safeValue (this.markets_by_id, marketId, market); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const amount = this.safeFloat (trade, 'amount'); + const type = 'limit'; // all trades are still limit trades + let takerOrMaker = undefined; + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const isYourOrder = this.safeValue (trade, 'is_your_order'); + if (isYourOrder !== undefined) { + takerOrMaker = 'taker'; + if (isYourOrder) { + takerOrMaker = 'maker'; + } + if (fee === undefined) { + fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); + } + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'order': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTradesPair (this.extend (request, params)); + if (Array.isArray (response)) { + const numElements = response.length; + if (numElements === 0) { + return []; + } + } + return this.parseTrades (response[market['id']], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'amount': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + }; + price = parseFloat (price); + amount = parseFloat (amount); + const response = await this.privatePostTrade (this.extend (request, params)); + let id = undefined; + let status = 'open'; + let filled = 0.0; + let remaining = amount; + if ('return' in response) { + id = this.safeString (response['return'], 'order_id'); + if (id === '0') { + id = this.safeString (response['return'], 'init_order_id'); + status = 'closed'; + } + filled = this.safeFloat (response['return'], 'received', 0.0); + remaining = this.safeFloat (response['return'], 'remains', amount); + } + const timestamp = this.milliseconds (); + const order = { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * filled, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'fee': undefined, + // 'trades': this.parseTrades (order['trades'], market), + }; + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'closed', + '2': 'canceled', + '3': 'open', // or partially-filled and canceled? https://github.com/ccxt/ccxt/issues/1594 + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.safeTimestamp (order, 'timestamp_created'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const remaining = this.safeFloat (order, 'amount'); + let amount = undefined; + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if ('start_amount' in order) { + amount = this.safeFloat (order, 'start_amount'); + } else { + if (id in this.orders) { + amount = this.orders[id]['amount']; + } + } + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + cost = price * filled; + } + } + const fee = undefined; + const type = 'limit'; + const side = this.safeString (order, 'type'); + const result = { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + }; + return result; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + id = id.toString (); + const newOrder = this.parseOrder (this.extend ({ 'id': id }, response['return'][id])); + const oldOrder = (id in this.orders) ? this.orders[id] : {}; + this.orders[id] = this.extend (oldOrder, newOrder); + return this.orders[id]; + } + + updateCachedOrders (openOrders, symbol) { + // update local cache with open orders + // this will add unseen orders and overwrite existing ones + for (let j = 0; j < openOrders.length; j++) { + const id = openOrders[j]['id']; + this.orders[id] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + for (let k = 0; k < cachedOrderIds.length; k++) { + // match each cached order to an order in the open orders array + // possible reasons why a cached order may be missing in the open orders array: + // - order was closed or canceled -> update cache + // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip + const cachedOrderId = cachedOrderIds[k]; + let cachedOrder = this.orders[cachedOrderId]; + if (!(cachedOrderId in openOrdersIndexedById)) { + // cached order is not in open orders array + // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order + if (symbol !== undefined && symbol !== cachedOrder['symbol']) { + continue; + } + // cached order is absent from the list of open orders -> mark the cached order as closed + if (cachedOrder['status'] === 'open') { + cachedOrder = this.extend (cachedOrder, { + 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") + 'cost': undefined, + 'filled': cachedOrder['amount'], + 'remaining': 0.0, + }); + if (cachedOrder['cost'] === undefined) { + if (cachedOrder['filled'] !== undefined) { + cachedOrder['cost'] = cachedOrder['filled'] * cachedOrder['price']; + } + } + this.orders[cachedOrderId] = cachedOrder; + } + } + } + return this.toArray (this.orders); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const request = {}; + const market = undefined; + if (symbol !== undefined) { + const market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostActiveOrders (this.extend (request, params)); + // can only return 'open' orders (i.e. no way to fetch 'closed' orders) + let openOrders = []; + if ('return' in response) { + openOrders = this.parseOrders (response['return'], market); + } + const allOrders = this.updateCachedOrders (openOrders, symbol); + const result = this.filterBySymbol (allOrders, symbol); + return this.filterBySinceLimit (result, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'open'); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // some derived classes use camelcase notation for request fields + const request = { + // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) + // 'count': 1000, // the number of trades for display numerical, default = 1000 + // 'from_id': trade ID, from which the display starts numerical 0 + // 'end_id': trade ID on which the display ends numerical ∞ + // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) + // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) + // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) + 'pair': market['id'], + }; + if (limit !== undefined) { + request['count'] = parseInt (limit); + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + const trades = this.safeValue (response, 'return', {}); + const ids = Object.keys (trades); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const trade = this.parseTrade (this.extend (trades[id], { + 'trade_id': id, + }), market); + result.push (trade); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async createDepositAddress (code, params = {}) { + const request = { + 'need_new': 1, + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response['info'], + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coinName': currency['id'], + 'need_new': 0, + }; + const response = await this.privatePostGetDepositAddress (this.extend (request, params)); + const address = this.safeString (response['return'], 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coinName': currency['id'], + 'amount': amount, + 'address': address, + }; + // no docs on the tag, yet... + if (tag !== undefined) { + throw new ExchangeError (this.id + ' withdraw() does not support the tag argument yet due to a lack of docs on withdrawing with tag/memo on behalf of the exchange.'); + } + const response = await this.privatePostWithdrawCoinsToAddress (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + 'method': path, + }, query)); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': signature, + }; + } else if (api === 'public') { + url += '/' + this.version + '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + url += '/' + this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + if (Object.keys (query).length) { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // 1 - Liqui only returns the integer 'success' key from their private API + // + // { "success": 1, ... } httpCode === 200 + // { "success": 0, ... } httpCode === 200 + // + // 2 - However, exchanges derived from Liqui, can return non-integers + // + // It can be a numeric string + // { "sucesss": "1", ... } + // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) + // + // Or just a string + // { "success": "true", ... } + // { "success": "false", ... }, httpCode >= 200 + // + // Or a boolean + // { "success": true, ... } + // { "success": false, ... }, httpCode >= 200 + // + // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types + // + // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui + // + // To cover points 1, 2, 3 and 4 combined this handler should work like this: + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],173:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class zaif extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'zaif', + 'name': 'Zaif', + 'countries': [ 'JP' ], + 'rateLimit': 2000, + 'version': '1', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766927-39ca2ada-5eeb-11e7-972f-1b4199518ca6.jpg', + 'api': 'https://api.zaif.jp', + 'www': 'https://zaif.jp', + 'doc': [ + 'https://techbureau-api-document.readthedocs.io/ja/latest/index.html', + 'https://corp.zaif.jp/api-docs', + 'https://corp.zaif.jp/api-docs/api_links', + 'https://www.npmjs.com/package/zaif.jp', + 'https://github.com/you21979/node-zaif', + ], + 'fees': 'https://zaif.jp/fee?lang=en', + }, + 'fees': { + 'trading': { + 'percentage': true, + 'taker': 0.1 / 100, + 'maker': 0, + }, + }, + 'api': { + 'public': { + 'get': [ + 'depth/{pair}', + 'currencies/{pair}', + 'currencies/all', + 'currency_pairs/{pair}', + 'currency_pairs/all', + 'last_price/{pair}', + 'ticker/{pair}', + 'trades/{pair}', + ], + }, + 'private': { + 'post': [ + 'active_orders', + 'cancel_order', + 'deposit_history', + 'get_id_info', + 'get_info', + 'get_info2', + 'get_personal_info', + 'trade', + 'trade_history', + 'withdraw', + 'withdraw_history', + ], + }, + 'ecapi': { + 'post': [ + 'createInvoice', + 'getInvoice', + 'getInvoiceIdsByOrderNumber', + 'cancelInvoice', + ], + }, + 'tlapi': { + 'post': [ + 'get_positions', + 'position_history', + 'active_positions', + 'create_position', + 'change_position', + 'cancel_position', + ], + }, + 'fapi': { + 'get': [ + 'groups/{group_id}', + 'last_price/{group_id}/{pair}', + 'ticker/{group_id}/{pair}', + 'trades/{group_id}/{pair}', + 'depth/{group_id}/{pair}', + ], + }, + }, + 'options': { + // zaif schedule defines several market-specific fees + 'fees': { + 'BTC/JPY': { 'maker': 0, 'taker': 0 }, + 'BCH/JPY': { 'maker': 0, 'taker': 0.3 / 100 }, + 'BCH/BTC': { 'maker': 0, 'taker': 0.3 / 100 }, + 'PEPECASH/JPY': { 'maker': 0, 'taker': 0.01 / 100 }, + 'PEPECASH/BT': { 'maker': 0, 'taker': 0.01 / 100 }, + }, + }, + 'exceptions': { + 'exact': { + 'unsupported currency_pair': BadRequest, // {"error": "unsupported currency_pair"} + }, + 'broad': { + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetCurrencyPairsAll (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'currency_pair'); + const name = this.safeString (market, 'name'); + const [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': -Math.log10 (market['item_unit_step']), + 'price': market['aux_unit_point'], + }; + const fees = this.safeValue (this.options['fees'], symbol, this.fees['trading']); + const taker = fees['taker']; + const maker = fees['maker']; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, // can trade or not + 'precision': precision, + 'taker': taker, + 'maker': maker, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'item_unit_min'), + 'max': undefined, + }, + 'price': { + 'min': this.safeFloat (market, 'aux_unit_min'), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfo (params); + const balances = this.safeValue (response, 'return', {}); + const result = { 'info': response }; + const funds = this.safeValue (balances, 'funds', {}); + const currencyIds = Object.keys (funds); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (funds, currencyId); + const account = { + 'free': balance, + 'used': 0.0, + 'total': balance, + }; + if ('deposit' in balances) { + if (currencyId in balances['deposit']) { + account['total'] = this.safeFloat (balances['deposit'], currencyId); + account['used'] = account['total'] - account['free']; + } + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this.publicGetDepthPair (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const ticker = await this.publicGetTickerPair (this.extend (request, params)); + const timestamp = this.milliseconds (); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + let side = this.safeString (trade, 'trade_type'); + side = (side === 'bid') ? 'buy' : 'sell'; + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString2 (trade, 'id', 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + if (market === undefined) { + const marketId = this.safeString (trade, 'currency_pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + let response = await this.publicGetTradesPair (this.extend (request, params)); + const numTrades = response.length; + if (numTrades === 1) { + const firstTrade = response[0]; + if (!Object.keys (firstTrade).length) { + response = []; + } + } + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'currency_pair': this.marketId (symbol), + 'action': (side === 'buy') ? 'bid' : 'ask', + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': response, + 'id': response['return']['order_id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order_id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + parseOrder (order, market = undefined) { + let side = this.safeString (order, 'action'); + side = (side === 'bid') ? 'buy' : 'sell'; + const timestamp = this.safeTimestamp (order, 'timestamp'); + if (!market) { + const marketId = this.safeString (order, 'currency_pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const id = this.safeString (order, 'id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': 'open', + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + // 'is_token': false, + // 'is_token_both': false, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['currency_pair'] = market['id']; + } + const response = await this.privatePostActiveOrders (this.extend (request, params)); + return this.parseOrders (response['return'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + // 'from': 0, + // 'count': 1000, + // 'from_id': 0, + // 'end_id': 1000, + // 'order': 'DESC', + // 'since': 1503821051, + // 'end': 1503821051, + // 'is_token': false, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['currency_pair'] = market['id']; + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseOrders (response['return'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (code === 'JPY') { + throw new ExchangeError (this.id + ' withdraw() does not allow ' + code + ' withdrawals'); + } + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + // 'message': 'Hi!', // XEM and others + // 'opt_fee': 0.003, // BTC and MONA only + }; + if (tag !== undefined) { + request['message'] = tag; + } + const result = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': result, + 'id': result['return']['txid'], + 'fee': result['return']['fee'], + }; + } + + nonce () { + const nonce = parseFloat (this.milliseconds () / 1000); + return nonce.toFixed (8); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/'; + if (api === 'public') { + url += 'api/' + this.version + '/' + this.implodeParams (path, params); + } else if (api === 'fapi') { + url += 'fapi/' + this.version + '/' + this.implodeParams (path, params); + } else { + this.checkRequiredCredentials (); + if (api === 'ecapi') { + url += 'ecapi'; + } else if (api === 'tlapi') { + url += 'tlapi'; + } else { + url += 'tapi'; + } + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'method': path, + 'nonce': nonce, + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // + // {"error": "unsupported currency_pair"} + // + const feedback = this.id + ' ' + body; + const error = this.safeString (response, 'error'); + if (error !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown message + } + const success = this.safeValue (response, 'success', true); + if (!success) { + throw new ExchangeError (feedback); + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],174:[function(require,module,exports){ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadRequest, ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, OrderNotFound, ExchangeNotAvailable, DDoSProtection, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class zb extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'zb', + 'name': 'ZB', + 'countries': [ 'CN' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchDepositAddress': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchOHLCV': true, + 'fetchTickers': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': '1day', + '3d': '3day', + '1w': '1week', + }, + 'exceptions': { + // '1000': 'Successful operation', + '1001': ExchangeError, // 'General error message', + '1002': ExchangeError, // 'Internal error', + '1003': AuthenticationError, // 'Verification does not pass', + '1004': AuthenticationError, // 'Funding security password lock', + '1005': AuthenticationError, // 'Funds security password is incorrect, please confirm and re-enter.', + '1006': AuthenticationError, // 'Real-name certification pending approval or audit does not pass', + '1009': ExchangeNotAvailable, // 'This interface is under maintenance', + '2001': InsufficientFunds, // 'Insufficient CNY Balance', + '2002': InsufficientFunds, // 'Insufficient BTC Balance', + '2003': InsufficientFunds, // 'Insufficient LTC Balance', + '2005': InsufficientFunds, // 'Insufficient ETH Balance', + '2006': InsufficientFunds, // 'Insufficient ETC Balance', + '2007': InsufficientFunds, // 'Insufficient BTS Balance', + '2009': InsufficientFunds, // 'Account balance is not enough', + '3001': OrderNotFound, // 'Pending orders not found', + '3002': InvalidOrder, // 'Invalid price', + '3003': InvalidOrder, // 'Invalid amount', + '3004': AuthenticationError, // 'User does not exist', + '3005': BadRequest, // 'Invalid parameter', + '3006': AuthenticationError, // 'Invalid IP or inconsistent with the bound IP', + '3007': AuthenticationError, // 'The request time has expired', + '3008': OrderNotFound, // 'Transaction records not found', + '4001': ExchangeNotAvailable, // 'API interface is locked or not enabled', + '4002': DDoSProtection, // 'Request too often', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg', + 'api': { + 'public': 'http://api.zb.cn/data', // no https for public API + 'private': 'https://trade.zb.cn/api', + }, + 'www': 'https://www.zb.com', + 'doc': 'https://www.zb.com/i/developer', + 'fees': 'https://www.zb.com/i/rate', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'ticker', + 'allTicker', + 'depth', + 'trades', + 'kline', + ], + }, + 'private': { + 'get': [ + // spot API + 'order', + 'cancelOrder', + 'getOrder', + 'getOrders', + 'getOrdersNew', + 'getOrdersIgnoreTradeType', + 'getUnfinishedOrdersIgnoreTradeType', + 'getAccountInfo', + 'getUserAddress', + 'getWithdrawAddress', + 'getWithdrawRecord', + 'getChargeRecord', + 'getCnyWithdrawRecord', + 'getCnyChargeRecord', + 'withdraw', + // leverage API + 'getLeverAssetsInfo', + 'getLeverBills', + 'transferInLever', + 'transferOutLever', + 'loan', + 'cancelLoan', + 'getLoans', + 'getLoanRecords', + 'borrow', + 'repay', + 'getRepayments', + ], + }, + }, + 'fees': { + 'funding': { + 'withdraw': { + 'BTC': 0.0001, + 'BCH': 0.0006, + 'LTC': 0.005, + 'ETH': 0.01, + 'ETC': 0.01, + 'BTS': 3, + 'EOS': 1, + 'QTUM': 0.01, + 'HSR': 0.001, + 'XRP': 0.1, + 'USDT': '0.1%', + 'QCASH': 5, + 'DASH': 0.002, + 'BCD': 0, + 'UBTC': 0, + 'SBTC': 0, + 'INK': 20, + 'TV': 0.1, + 'BTH': 0, + 'BCX': 0, + 'LBTC': 0, + 'CHAT': 20, + 'bitCNY': 20, + 'HLC': 20, + 'BTP': 0, + 'BCW': 0, + }, + }, + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + }, + 'commonCurrencies': { + 'ENT': 'ENTCash', + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetMarkets (params); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'amountScale'), + 'price': this.safeInteger (market, 'priceScale'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetGetAccountInfo (params); + // todo: use this somehow + // let permissions = response['result']['base']; + const balances = this.safeValue (response['result'], 'coins'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + // { enName: "BTC", + // freez: "0.00000000", + // unitDecimal: 8, // always 8 + // cnName: "BTC", + // isCanRecharge: true, // TODO: should use this + // unitTag: "฿", + // isCanWithdraw: true, // TODO: should use this + // available: "0.00000000", + // key: "btc" } + const account = this.account (); + const currencyId = this.safeString (balance, 'key'); + const code = this.safeCurrencyCode (currencyId); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'freez'); + result[code] = account; + } + return this.parseBalance (result); + } + + getMarketFieldName () { + return 'market'; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetGetUserAddress (this.extend (request, params)); + let address = response['message']['datas']['key']; + let tag = undefined; + if (address.indexOf ('_') >= 0) { + const parts = address.split ('_'); + address = parts[0]; // WARNING: MAY BE tag_address INSTEAD OF address_tag FOR SOME CURRENCIES!! + tag = parts[1]; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketFieldName = this.getMarketFieldName (); + const request = {}; + request[marketFieldName] = market['id']; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetAllTicker (params); + const result = {}; + const anotherMarketsById = {}; + const marketIds = Object.keys (this.marketsById); + for (let i = 0; i < marketIds.length; i++) { + const tickerId = marketIds[i].replace ('_', ''); + anotherMarketsById[tickerId] = this.marketsById[marketIds[i]]; + } + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const market = anotherMarketsById[ids[i]]; + result[market['symbol']] = this.parseTicker (response[ids[i]], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketFieldName = this.getMarketFieldName (); + const request = {}; + request[marketFieldName] = market['id']; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = response['ticker']; + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 1000; + } + const request = { + 'market': market['id'], + 'type': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + request['since'] = since; + } + const response = await this.publicGetKline (this.extend (request, params)); + const data = this.safeValue (response, 'data', []); + return this.parseOHLCVs (data, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let side = this.safeString (trade, 'trade_type'); + side = (side === 'bid') ? 'buy' : 'sell'; + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketFieldName = this.getMarketFieldName (); + const request = {}; + request[marketFieldName] = market['id']; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new InvalidOrder (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const request = { + 'price': this.priceToPrecision (symbol, price), + 'amount': this.amountToPrecision (symbol, amount), + 'tradeType': (side === 'buy') ? '1' : '0', + 'currency': this.marketId (symbol), + }; + const response = await this.privateGetOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id.toString (), + 'currency': this.marketId (symbol), + }; + return await this.privateGetCancelOrder (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const request = { + 'id': id.toString (), + 'currency': this.marketId (symbol), + }; + const response = await this.privateGetGetOrder (this.extend (request, params)); + // + // { + // 'total_amount': 0.01, + // 'id': '20180910244276459', + // 'price': 180.0, + // 'trade_date': 1536576744960, + // 'status': 2, + // 'trade_money': '1.96742', + // 'trade_amount': 0.01, + // 'type': 0, + // 'currency': 'eth_usdt' + // } + // + return this.parseOrder (response, undefined); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = 50, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + 'fetchOrders requires a symbol parameter'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'pageIndex': 1, // default pageIndex is 1 + 'pageSize': limit, // default pageSize is 50 + }; + let method = 'privateGetGetOrdersIgnoreTradeType'; + // tradeType 交易类型1/0[buy/sell] + if ('tradeType' in params) { + method = 'privateGetGetOrdersNew'; + } + let response = undefined; + try { + response = await this[method] (this.extend (request, params)); + } catch (e) { + if (e instanceof OrderNotFound) { + return []; + } + throw e; + } + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = 10, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + 'fetchOpenOrders requires a symbol parameter'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'pageIndex': 1, // default pageIndex is 1 + 'pageSize': limit, // default pageSize is 10 + }; + let method = 'privateGetGetUnfinishedOrdersIgnoreTradeType'; + // tradeType 交易类型1/0[buy/sell] + if ('tradeType' in params) { + method = 'privateGetGetOrdersNew'; + } + let response = undefined; + try { + response = await this[method] (this.extend (request, params)); + } catch (e) { + if (e instanceof OrderNotFound) { + return []; + } + throw e; + } + return this.parseOrders (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder + // + // { + // 'total_amount': 0.01, + // 'id': '20180910244276459', + // 'price': 180.0, + // 'trade_date': 1536576744960, + // 'status': 2, + // 'trade_money': '1.96742', + // 'trade_amount': 0.01, + // 'type': 0, + // 'currency': 'eth_usdt' + // } + // + let side = this.safeInteger (order, 'type'); + side = (side === 1) ? 'buy' : 'sell'; + const type = 'limit'; // market order is not availalbe in ZB + let timestamp = undefined; + const createDateField = this.getCreateDateField (); + if (createDateField in order) { + timestamp = order[createDateField]; + } + let symbol = undefined; + const marketId = this.safeString (order, 'currency'); + if (marketId in this.markets_by_id) { + // get symbol from currency + market = this.marketsById[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'trade_amount'); + const amount = this.safeFloat (order, 'total_amount'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const cost = this.safeFloat (order, 'trade_money'); + let average = undefined; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + if ((cost !== undefined) && (filled !== undefined) && (filled > 0)) { + average = cost / filled; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'canceled', + '2': 'closed', + '3': 'open', // partial + }; + return this.safeString (statuses, status, status); + } + + getCreateDateField () { + return 'trade_date'; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api === 'public') { + url += '/' + this.version + '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + let query = this.keysort (this.extend ({ + 'method': path, + 'accesskey': this.apiKey, + }, params)); + const nonce = this.nonce (); + query = this.keysort (query); + const auth = this.rawencode (query); + const secret = this.hash (this.encode (this.secret), 'sha1'); + const signature = this.hmac (this.encode (auth), this.encode (secret), 'md5'); + const suffix = 'sign=' + signature + '&reqTime=' + nonce.toString (); + url += '/' + path + '?' + auth + '&' + suffix; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if (body[0] === '{') { + const feedback = this.id + ' ' + body; + if ('code' in response) { + const code = this.safeString (response, 'code'); + this.throwExactlyMatchedException (this.exceptions, code, feedback); + if (code !== '1000') { + throw new ExchangeError (feedback); + } + } + // special case for {"result":false,"message":"æœ�务端忙碌"} (a "Busy Server" reply) + const result = this.safeValue (response, 'result'); + if (result !== undefined) { + if (!result) { + const message = this.safeString (response, 'message'); + if (message === 'æœ�务端忙碌') { + throw new ExchangeNotAvailable (feedback); + } else { + throw new ExchangeError (feedback); + } + } + } + } + } +}; + +},{"./base/Exchange":7,"./base/errors":9}],175:[function(require,module,exports){ +(function (global){ +'use strict'; + +// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js +// original notice: + +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +function compare(a, b) { + if (a === b) { + return 0; + } + + var x = a.length; + var y = b.length; + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + + if (x < y) { + return -1; + } + if (y < x) { + return 1; + } + return 0; +} +function isBuffer(b) { + if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { + return global.Buffer.isBuffer(b); + } + return !!(b != null && b._isBuffer); +} + +// based on node assert, original notice: + +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +var util = require('util/'); +var hasOwn = Object.prototype.hasOwnProperty; +var pSlice = Array.prototype.slice; +var functionsHaveNames = (function () { + return function foo() {}.name === 'foo'; +}()); +function pToString (obj) { + return Object.prototype.toString.call(obj); +} +function isView(arrbuf) { + if (isBuffer(arrbuf)) { + return false; + } + if (typeof global.ArrayBuffer !== 'function') { + return false; + } + if (typeof ArrayBuffer.isView === 'function') { + return ArrayBuffer.isView(arrbuf); + } + if (!arrbuf) { + return false; + } + if (arrbuf instanceof DataView) { + return true; + } + if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { + return true; + } + return false; +} +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +var regex = /\s*function\s+([^\(\s]*)\s*/; +// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js +function getName(func) { + if (!util.isFunction(func)) { + return; + } + if (functionsHaveNames) { + return func.name; + } + var str = func.toString(); + var match = str.match(regex); + return match && match[1]; +} +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = getName(stackStartFunction); + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function truncate(s, n) { + if (typeof s === 'string') { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} +function inspect(something) { + if (functionsHaveNames || !util.isFunction(something)) { + return util.inspect(something); + } + var rawname = getName(something); + var name = rawname ? ': ' + rawname : ''; + return '[Function' + name + ']'; +} +function getMessage(self) { + return truncate(inspect(self.actual), 128) + ' ' + + self.operator + ' ' + + truncate(inspect(self.expected), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); + } +}; + +function _deepEqual(actual, expected, strict, memos) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + } else if (isBuffer(actual) && isBuffer(expected)) { + return compare(actual, expected) === 0; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if ((actual === null || typeof actual !== 'object') && + (expected === null || typeof expected !== 'object')) { + return strict ? actual === expected : actual == expected; + + // If both values are instances of typed arrays, wrap their underlying + // ArrayBuffers in a Buffer each to increase performance + // This optimization requires the arrays to have the same type as checked by + // Object.prototype.toString (aka pToString). Never perform binary + // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their + // bit patterns are not identical. + } else if (isView(actual) && isView(expected) && + pToString(actual) === pToString(expected) && + !(actual instanceof Float32Array || + actual instanceof Float64Array)) { + return compare(new Uint8Array(actual.buffer), + new Uint8Array(expected.buffer)) === 0; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else if (isBuffer(actual) !== isBuffer(expected)) { + return false; + } else { + memos = memos || {actual: [], expected: []}; + + var actualIndex = memos.actual.indexOf(actual); + if (actualIndex !== -1) { + if (actualIndex === memos.expected.indexOf(expected)) { + return true; + } + } + + memos.actual.push(actual); + memos.expected.push(expected); + + return objEquiv(actual, expected, strict, memos); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b, strict, actualVisitedObjects) { + if (a === null || a === undefined || b === null || b === undefined) + return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) + return a === b; + if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) + return false; + var aIsArgs = isArguments(a); + var bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b, strict); + } + var ka = objectKeys(a); + var kb = objectKeys(b); + var key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length !== kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] !== kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +assert.notDeepStrictEqual = notDeepStrictEqual; +function notDeepStrictEqual(actual, expected, message) { + if (_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); + } +} + + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } + + try { + if (actual instanceof expected) { + return true; + } + } catch (e) { + // Ignore. The instanceof check doesn't work for arrow functions. + } + + if (Error.isPrototypeOf(expected)) { + return false; + } + + return expected.call({}, actual) === true; +} + +function _tryBlock(block) { + var error; + try { + block(); + } catch (e) { + error = e; + } + return error; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (typeof block !== 'function') { + throw new TypeError('"block" argument must be a function'); + } + + if (typeof expected === 'string') { + message = expected; + expected = null; + } + + actual = _tryBlock(block); + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + var userProvidedMessage = typeof message === 'string'; + var isUnwantedException = !shouldThrow && util.isError(actual); + var isUnexpectedException = !shouldThrow && actual && !expected; + + if ((isUnwantedException && + userProvidedMessage && + expectedException(actual, expected)) || + isUnexpectedException) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws(true, block, error, message); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws(false, block, error, message); +}; + +assert.ifError = function(err) { if (err) throw err; }; + +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"util/":233}],176:[function(require,module,exports){ +'use strict' + +exports.byteLength = byteLength +exports.toByteArray = toByteArray +exports.fromByteArray = fromByteArray + +var lookup = [] +var revLookup = [] +var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array + +var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i] + revLookup[code.charCodeAt(i)] = i +} + +revLookup['-'.charCodeAt(0)] = 62 +revLookup['_'.charCodeAt(0)] = 63 + +function placeHoldersCount (b64) { + var len = b64.length + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 +} + +function byteLength (b64) { + // base64 is 4/3 + up to two characters of the original data + return (b64.length * 3 / 4) - placeHoldersCount(b64) +} + +function toByteArray (b64) { + var i, l, tmp, placeHolders, arr + var len = b64.length + placeHolders = placeHoldersCount(b64) + + arr = new Arr((len * 3 / 4) - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? len - 4 : len + + var L = 0 + + for (i = 0; i < l; i += 4) { + tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] + arr[L++] = (tmp >> 16) & 0xFF + arr[L++] = (tmp >> 8) & 0xFF + arr[L++] = tmp & 0xFF + } + + if (placeHolders === 2) { + tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[L++] = tmp & 0xFF + } else if (placeHolders === 1) { + tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[L++] = (tmp >> 8) & 0xFF + arr[L++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var output = '' + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + output += lookup[tmp >> 2] + output += lookup[(tmp << 4) & 0x3F] + output += '==' + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) + output += lookup[tmp >> 10] + output += lookup[(tmp >> 4) & 0x3F] + output += lookup[(tmp << 2) & 0x3F] + output += '=' + } + + parts.push(output) + + return parts.join('') +} + +},{}],177:[function(require,module,exports){ + +},{}],178:[function(require,module,exports){ +(function (process,Buffer){ +'use strict'; +/* eslint camelcase: "off" */ + +var assert = require('assert'); + +var Zstream = require('pako/lib/zlib/zstream'); +var zlib_deflate = require('pako/lib/zlib/deflate.js'); +var zlib_inflate = require('pako/lib/zlib/inflate.js'); +var constants = require('pako/lib/zlib/constants'); + +for (var key in constants) { + exports[key] = constants[key]; +} + +// zlib modes +exports.NONE = 0; +exports.DEFLATE = 1; +exports.INFLATE = 2; +exports.GZIP = 3; +exports.GUNZIP = 4; +exports.DEFLATERAW = 5; +exports.INFLATERAW = 6; +exports.UNZIP = 7; + +var GZIP_HEADER_ID1 = 0x1f; +var GZIP_HEADER_ID2 = 0x8b; + +/** + * Emulate Node's zlib C++ layer for use by the JS layer in index.js + */ +function Zlib(mode) { + if (typeof mode !== 'number' || mode < exports.DEFLATE || mode > exports.UNZIP) { + throw new TypeError('Bad argument'); + } + + this.dictionary = null; + this.err = 0; + this.flush = 0; + this.init_done = false; + this.level = 0; + this.memLevel = 0; + this.mode = mode; + this.strategy = 0; + this.windowBits = 0; + this.write_in_progress = false; + this.pending_close = false; + this.gzip_id_bytes_read = 0; +} + +Zlib.prototype.close = function () { + if (this.write_in_progress) { + this.pending_close = true; + return; + } + + this.pending_close = false; + + assert(this.init_done, 'close before init'); + assert(this.mode <= exports.UNZIP); + + if (this.mode === exports.DEFLATE || this.mode === exports.GZIP || this.mode === exports.DEFLATERAW) { + zlib_deflate.deflateEnd(this.strm); + } else if (this.mode === exports.INFLATE || this.mode === exports.GUNZIP || this.mode === exports.INFLATERAW || this.mode === exports.UNZIP) { + zlib_inflate.inflateEnd(this.strm); + } + + this.mode = exports.NONE; + + this.dictionary = null; +}; + +Zlib.prototype.write = function (flush, input, in_off, in_len, out, out_off, out_len) { + return this._write(true, flush, input, in_off, in_len, out, out_off, out_len); +}; + +Zlib.prototype.writeSync = function (flush, input, in_off, in_len, out, out_off, out_len) { + return this._write(false, flush, input, in_off, in_len, out, out_off, out_len); +}; + +Zlib.prototype._write = function (async, flush, input, in_off, in_len, out, out_off, out_len) { + assert.equal(arguments.length, 8); + + assert(this.init_done, 'write before init'); + assert(this.mode !== exports.NONE, 'already finalized'); + assert.equal(false, this.write_in_progress, 'write already in progress'); + assert.equal(false, this.pending_close, 'close is pending'); + + this.write_in_progress = true; + + assert.equal(false, flush === undefined, 'must provide flush value'); + + this.write_in_progress = true; + + if (flush !== exports.Z_NO_FLUSH && flush !== exports.Z_PARTIAL_FLUSH && flush !== exports.Z_SYNC_FLUSH && flush !== exports.Z_FULL_FLUSH && flush !== exports.Z_FINISH && flush !== exports.Z_BLOCK) { + throw new Error('Invalid flush value'); + } + + if (input == null) { + input = Buffer.alloc(0); + in_len = 0; + in_off = 0; + } + + this.strm.avail_in = in_len; + this.strm.input = input; + this.strm.next_in = in_off; + this.strm.avail_out = out_len; + this.strm.output = out; + this.strm.next_out = out_off; + this.flush = flush; + + if (!async) { + // sync version + this._process(); + + if (this._checkError()) { + return this._afterSync(); + } + return; + } + + // async version + var self = this; + process.nextTick(function () { + self._process(); + self._after(); + }); + + return this; +}; + +Zlib.prototype._afterSync = function () { + var avail_out = this.strm.avail_out; + var avail_in = this.strm.avail_in; + + this.write_in_progress = false; + + return [avail_in, avail_out]; +}; + +Zlib.prototype._process = function () { + var next_expected_header_byte = null; + + // If the avail_out is left at 0, then it means that it ran out + // of room. If there was avail_out left over, then it means + // that all of the input was consumed. + switch (this.mode) { + case exports.DEFLATE: + case exports.GZIP: + case exports.DEFLATERAW: + this.err = zlib_deflate.deflate(this.strm, this.flush); + break; + case exports.UNZIP: + if (this.strm.avail_in > 0) { + next_expected_header_byte = this.strm.next_in; + } + + switch (this.gzip_id_bytes_read) { + case 0: + if (next_expected_header_byte === null) { + break; + } + + if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID1) { + this.gzip_id_bytes_read = 1; + next_expected_header_byte++; + + if (this.strm.avail_in === 1) { + // The only available byte was already read. + break; + } + } else { + this.mode = exports.INFLATE; + break; + } + + // fallthrough + case 1: + if (next_expected_header_byte === null) { + break; + } + + if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID2) { + this.gzip_id_bytes_read = 2; + this.mode = exports.GUNZIP; + } else { + // There is no actual difference between INFLATE and INFLATERAW + // (after initialization). + this.mode = exports.INFLATE; + } + + break; + default: + throw new Error('invalid number of gzip magic number bytes read'); + } + + // fallthrough + case exports.INFLATE: + case exports.GUNZIP: + case exports.INFLATERAW: + this.err = zlib_inflate.inflate(this.strm, this.flush + + // If data was encoded with dictionary + );if (this.err === exports.Z_NEED_DICT && this.dictionary) { + // Load it + this.err = zlib_inflate.inflateSetDictionary(this.strm, this.dictionary); + if (this.err === exports.Z_OK) { + // And try to decode again + this.err = zlib_inflate.inflate(this.strm, this.flush); + } else if (this.err === exports.Z_DATA_ERROR) { + // Both inflateSetDictionary() and inflate() return Z_DATA_ERROR. + // Make it possible for After() to tell a bad dictionary from bad + // input. + this.err = exports.Z_NEED_DICT; + } + } + while (this.strm.avail_in > 0 && this.mode === exports.GUNZIP && this.err === exports.Z_STREAM_END && this.strm.next_in[0] !== 0x00) { + // Bytes remain in input buffer. Perhaps this is another compressed + // member in the same archive, or just trailing garbage. + // Trailing zero bytes are okay, though, since they are frequently + // used for padding. + + this.reset(); + this.err = zlib_inflate.inflate(this.strm, this.flush); + } + break; + default: + throw new Error('Unknown mode ' + this.mode); + } +}; + +Zlib.prototype._checkError = function () { + // Acceptable error states depend on the type of zlib stream. + switch (this.err) { + case exports.Z_OK: + case exports.Z_BUF_ERROR: + if (this.strm.avail_out !== 0 && this.flush === exports.Z_FINISH) { + this._error('unexpected end of file'); + return false; + } + break; + case exports.Z_STREAM_END: + // normal statuses, not fatal + break; + case exports.Z_NEED_DICT: + if (this.dictionary == null) { + this._error('Missing dictionary'); + } else { + this._error('Bad dictionary'); + } + return false; + default: + // something else. + this._error('Zlib error'); + return false; + } + + return true; +}; + +Zlib.prototype._after = function () { + if (!this._checkError()) { + return; + } + + var avail_out = this.strm.avail_out; + var avail_in = this.strm.avail_in; + + this.write_in_progress = false; + + // call the write() cb + this.callback(avail_in, avail_out); + + if (this.pending_close) { + this.close(); + } +}; + +Zlib.prototype._error = function (message) { + if (this.strm.msg) { + message = this.strm.msg; + } + this.onerror(message, this.err + + // no hope of rescue. + );this.write_in_progress = false; + if (this.pending_close) { + this.close(); + } +}; + +Zlib.prototype.init = function (windowBits, level, memLevel, strategy, dictionary) { + assert(arguments.length === 4 || arguments.length === 5, 'init(windowBits, level, memLevel, strategy, [dictionary])'); + + assert(windowBits >= 8 && windowBits <= 15, 'invalid windowBits'); + assert(level >= -1 && level <= 9, 'invalid compression level'); + + assert(memLevel >= 1 && memLevel <= 9, 'invalid memlevel'); + + assert(strategy === exports.Z_FILTERED || strategy === exports.Z_HUFFMAN_ONLY || strategy === exports.Z_RLE || strategy === exports.Z_FIXED || strategy === exports.Z_DEFAULT_STRATEGY, 'invalid strategy'); + + this._init(level, windowBits, memLevel, strategy, dictionary); + this._setDictionary(); +}; + +Zlib.prototype.params = function () { + throw new Error('deflateParams Not supported'); +}; + +Zlib.prototype.reset = function () { + this._reset(); + this._setDictionary(); +}; + +Zlib.prototype._init = function (level, windowBits, memLevel, strategy, dictionary) { + this.level = level; + this.windowBits = windowBits; + this.memLevel = memLevel; + this.strategy = strategy; + + this.flush = exports.Z_NO_FLUSH; + + this.err = exports.Z_OK; + + if (this.mode === exports.GZIP || this.mode === exports.GUNZIP) { + this.windowBits += 16; + } + + if (this.mode === exports.UNZIP) { + this.windowBits += 32; + } + + if (this.mode === exports.DEFLATERAW || this.mode === exports.INFLATERAW) { + this.windowBits = -1 * this.windowBits; + } + + this.strm = new Zstream(); + + switch (this.mode) { + case exports.DEFLATE: + case exports.GZIP: + case exports.DEFLATERAW: + this.err = zlib_deflate.deflateInit2(this.strm, this.level, exports.Z_DEFLATED, this.windowBits, this.memLevel, this.strategy); + break; + case exports.INFLATE: + case exports.GUNZIP: + case exports.INFLATERAW: + case exports.UNZIP: + this.err = zlib_inflate.inflateInit2(this.strm, this.windowBits); + break; + default: + throw new Error('Unknown mode ' + this.mode); + } + + if (this.err !== exports.Z_OK) { + this._error('Init error'); + } + + this.dictionary = dictionary; + + this.write_in_progress = false; + this.init_done = true; +}; + +Zlib.prototype._setDictionary = function () { + if (this.dictionary == null) { + return; + } + + this.err = exports.Z_OK; + + switch (this.mode) { + case exports.DEFLATE: + case exports.DEFLATERAW: + this.err = zlib_deflate.deflateSetDictionary(this.strm, this.dictionary); + break; + default: + break; + } + + if (this.err !== exports.Z_OK) { + this._error('Failed to set dictionary'); + } +}; + +Zlib.prototype._reset = function () { + this.err = exports.Z_OK; + + switch (this.mode) { + case exports.DEFLATE: + case exports.DEFLATERAW: + case exports.GZIP: + this.err = zlib_deflate.deflateReset(this.strm); + break; + case exports.INFLATE: + case exports.INFLATERAW: + case exports.GUNZIP: + this.err = zlib_inflate.inflateReset(this.strm); + break; + default: + break; + } + + if (this.err !== exports.Z_OK) { + this._error('Failed to reset stream'); + } +}; + +exports.Zlib = Zlib; +}).call(this,require('_process'),require("buffer").Buffer) +},{"_process":202,"assert":175,"buffer":180,"pako/lib/zlib/constants":192,"pako/lib/zlib/deflate.js":194,"pako/lib/zlib/inflate.js":196,"pako/lib/zlib/zstream":200}],179:[function(require,module,exports){ +(function (process){ +'use strict'; + +var Buffer = require('buffer').Buffer; +var Transform = require('stream').Transform; +var binding = require('./binding'); +var util = require('util'); +var assert = require('assert').ok; +var kMaxLength = require('buffer').kMaxLength; +var kRangeErrorMessage = 'Cannot create final Buffer. It would be larger ' + 'than 0x' + kMaxLength.toString(16) + ' bytes'; + +// zlib doesn't provide these, so kludge them in following the same +// const naming scheme zlib uses. +binding.Z_MIN_WINDOWBITS = 8; +binding.Z_MAX_WINDOWBITS = 15; +binding.Z_DEFAULT_WINDOWBITS = 15; + +// fewer than 64 bytes per chunk is stupid. +// technically it could work with as few as 8, but even 64 bytes +// is absurdly low. Usually a MB or more is best. +binding.Z_MIN_CHUNK = 64; +binding.Z_MAX_CHUNK = Infinity; +binding.Z_DEFAULT_CHUNK = 16 * 1024; + +binding.Z_MIN_MEMLEVEL = 1; +binding.Z_MAX_MEMLEVEL = 9; +binding.Z_DEFAULT_MEMLEVEL = 8; + +binding.Z_MIN_LEVEL = -1; +binding.Z_MAX_LEVEL = 9; +binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION; + +// expose all the zlib constants +var bkeys = Object.keys(binding); +for (var bk = 0; bk < bkeys.length; bk++) { + var bkey = bkeys[bk]; + if (bkey.match(/^Z/)) { + Object.defineProperty(exports, bkey, { + enumerable: true, value: binding[bkey], writable: false + }); + } +} + +// translation table for return codes. +var codes = { + Z_OK: binding.Z_OK, + Z_STREAM_END: binding.Z_STREAM_END, + Z_NEED_DICT: binding.Z_NEED_DICT, + Z_ERRNO: binding.Z_ERRNO, + Z_STREAM_ERROR: binding.Z_STREAM_ERROR, + Z_DATA_ERROR: binding.Z_DATA_ERROR, + Z_MEM_ERROR: binding.Z_MEM_ERROR, + Z_BUF_ERROR: binding.Z_BUF_ERROR, + Z_VERSION_ERROR: binding.Z_VERSION_ERROR +}; + +var ckeys = Object.keys(codes); +for (var ck = 0; ck < ckeys.length; ck++) { + var ckey = ckeys[ck]; + codes[codes[ckey]] = ckey; +} + +Object.defineProperty(exports, 'codes', { + enumerable: true, value: Object.freeze(codes), writable: false +}); + +exports.Deflate = Deflate; +exports.Inflate = Inflate; +exports.Gzip = Gzip; +exports.Gunzip = Gunzip; +exports.DeflateRaw = DeflateRaw; +exports.InflateRaw = InflateRaw; +exports.Unzip = Unzip; + +exports.createDeflate = function (o) { + return new Deflate(o); +}; + +exports.createInflate = function (o) { + return new Inflate(o); +}; + +exports.createDeflateRaw = function (o) { + return new DeflateRaw(o); +}; + +exports.createInflateRaw = function (o) { + return new InflateRaw(o); +}; + +exports.createGzip = function (o) { + return new Gzip(o); +}; + +exports.createGunzip = function (o) { + return new Gunzip(o); +}; + +exports.createUnzip = function (o) { + return new Unzip(o); +}; + +// Convenience methods. +// compress/decompress a string or buffer in one step. +exports.deflate = function (buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } + return zlibBuffer(new Deflate(opts), buffer, callback); +}; + +exports.deflateSync = function (buffer, opts) { + return zlibBufferSync(new Deflate(opts), buffer); +}; + +exports.gzip = function (buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } + return zlibBuffer(new Gzip(opts), buffer, callback); +}; + +exports.gzipSync = function (buffer, opts) { + return zlibBufferSync(new Gzip(opts), buffer); +}; + +exports.deflateRaw = function (buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } + return zlibBuffer(new DeflateRaw(opts), buffer, callback); +}; + +exports.deflateRawSync = function (buffer, opts) { + return zlibBufferSync(new DeflateRaw(opts), buffer); +}; + +exports.unzip = function (buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } + return zlibBuffer(new Unzip(opts), buffer, callback); +}; + +exports.unzipSync = function (buffer, opts) { + return zlibBufferSync(new Unzip(opts), buffer); +}; + +exports.inflate = function (buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } + return zlibBuffer(new Inflate(opts), buffer, callback); +}; + +exports.inflateSync = function (buffer, opts) { + return zlibBufferSync(new Inflate(opts), buffer); +}; + +exports.gunzip = function (buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } + return zlibBuffer(new Gunzip(opts), buffer, callback); +}; + +exports.gunzipSync = function (buffer, opts) { + return zlibBufferSync(new Gunzip(opts), buffer); +}; + +exports.inflateRaw = function (buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; + } + return zlibBuffer(new InflateRaw(opts), buffer, callback); +}; + +exports.inflateRawSync = function (buffer, opts) { + return zlibBufferSync(new InflateRaw(opts), buffer); +}; + +function zlibBuffer(engine, buffer, callback) { + var buffers = []; + var nread = 0; + + engine.on('error', onError); + engine.on('end', onEnd); + + engine.end(buffer); + flow(); + + function flow() { + var chunk; + while (null !== (chunk = engine.read())) { + buffers.push(chunk); + nread += chunk.length; + } + engine.once('readable', flow); + } + + function onError(err) { + engine.removeListener('end', onEnd); + engine.removeListener('readable', flow); + callback(err); + } + + function onEnd() { + var buf; + var err = null; + + if (nread >= kMaxLength) { + err = new RangeError(kRangeErrorMessage); + } else { + buf = Buffer.concat(buffers, nread); + } + + buffers = []; + engine.close(); + callback(err, buf); + } +} + +function zlibBufferSync(engine, buffer) { + if (typeof buffer === 'string') buffer = Buffer.from(buffer); + + if (!Buffer.isBuffer(buffer)) throw new TypeError('Not a string or buffer'); + + var flushFlag = engine._finishFlushFlag; + + return engine._processChunk(buffer, flushFlag); +} + +// generic zlib +// minimal 2-byte header +function Deflate(opts) { + if (!(this instanceof Deflate)) return new Deflate(opts); + Zlib.call(this, opts, binding.DEFLATE); +} + +function Inflate(opts) { + if (!(this instanceof Inflate)) return new Inflate(opts); + Zlib.call(this, opts, binding.INFLATE); +} + +// gzip - bigger header, same deflate compression +function Gzip(opts) { + if (!(this instanceof Gzip)) return new Gzip(opts); + Zlib.call(this, opts, binding.GZIP); +} + +function Gunzip(opts) { + if (!(this instanceof Gunzip)) return new Gunzip(opts); + Zlib.call(this, opts, binding.GUNZIP); +} + +// raw - no header +function DeflateRaw(opts) { + if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); + Zlib.call(this, opts, binding.DEFLATERAW); +} + +function InflateRaw(opts) { + if (!(this instanceof InflateRaw)) return new InflateRaw(opts); + Zlib.call(this, opts, binding.INFLATERAW); +} + +// auto-detect header. +function Unzip(opts) { + if (!(this instanceof Unzip)) return new Unzip(opts); + Zlib.call(this, opts, binding.UNZIP); +} + +function isValidFlushFlag(flag) { + return flag === binding.Z_NO_FLUSH || flag === binding.Z_PARTIAL_FLUSH || flag === binding.Z_SYNC_FLUSH || flag === binding.Z_FULL_FLUSH || flag === binding.Z_FINISH || flag === binding.Z_BLOCK; +} + +// the Zlib class they all inherit from +// This thing manages the queue of requests, and returns +// true or false if there is anything in the queue when +// you call the .write() method. + +function Zlib(opts, mode) { + var _this = this; + + this._opts = opts = opts || {}; + this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK; + + Transform.call(this, opts); + + if (opts.flush && !isValidFlushFlag(opts.flush)) { + throw new Error('Invalid flush flag: ' + opts.flush); + } + if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) { + throw new Error('Invalid flush flag: ' + opts.finishFlush); + } + + this._flushFlag = opts.flush || binding.Z_NO_FLUSH; + this._finishFlushFlag = typeof opts.finishFlush !== 'undefined' ? opts.finishFlush : binding.Z_FINISH; + + if (opts.chunkSize) { + if (opts.chunkSize < exports.Z_MIN_CHUNK || opts.chunkSize > exports.Z_MAX_CHUNK) { + throw new Error('Invalid chunk size: ' + opts.chunkSize); + } + } + + if (opts.windowBits) { + if (opts.windowBits < exports.Z_MIN_WINDOWBITS || opts.windowBits > exports.Z_MAX_WINDOWBITS) { + throw new Error('Invalid windowBits: ' + opts.windowBits); + } + } + + if (opts.level) { + if (opts.level < exports.Z_MIN_LEVEL || opts.level > exports.Z_MAX_LEVEL) { + throw new Error('Invalid compression level: ' + opts.level); + } + } + + if (opts.memLevel) { + if (opts.memLevel < exports.Z_MIN_MEMLEVEL || opts.memLevel > exports.Z_MAX_MEMLEVEL) { + throw new Error('Invalid memLevel: ' + opts.memLevel); + } + } + + if (opts.strategy) { + if (opts.strategy != exports.Z_FILTERED && opts.strategy != exports.Z_HUFFMAN_ONLY && opts.strategy != exports.Z_RLE && opts.strategy != exports.Z_FIXED && opts.strategy != exports.Z_DEFAULT_STRATEGY) { + throw new Error('Invalid strategy: ' + opts.strategy); + } + } + + if (opts.dictionary) { + if (!Buffer.isBuffer(opts.dictionary)) { + throw new Error('Invalid dictionary: it should be a Buffer instance'); + } + } + + this._handle = new binding.Zlib(mode); + + var self = this; + this._hadError = false; + this._handle.onerror = function (message, errno) { + // there is no way to cleanly recover. + // continuing only obscures problems. + _close(self); + self._hadError = true; + + var error = new Error(message); + error.errno = errno; + error.code = exports.codes[errno]; + self.emit('error', error); + }; + + var level = exports.Z_DEFAULT_COMPRESSION; + if (typeof opts.level === 'number') level = opts.level; + + var strategy = exports.Z_DEFAULT_STRATEGY; + if (typeof opts.strategy === 'number') strategy = opts.strategy; + + this._handle.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, level, opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, strategy, opts.dictionary); + + this._buffer = Buffer.allocUnsafe(this._chunkSize); + this._offset = 0; + this._level = level; + this._strategy = strategy; + + this.once('end', this.close); + + Object.defineProperty(this, '_closed', { + get: function () { + return !_this._handle; + }, + configurable: true, + enumerable: true + }); +} + +util.inherits(Zlib, Transform); + +Zlib.prototype.params = function (level, strategy, callback) { + if (level < exports.Z_MIN_LEVEL || level > exports.Z_MAX_LEVEL) { + throw new RangeError('Invalid compression level: ' + level); + } + if (strategy != exports.Z_FILTERED && strategy != exports.Z_HUFFMAN_ONLY && strategy != exports.Z_RLE && strategy != exports.Z_FIXED && strategy != exports.Z_DEFAULT_STRATEGY) { + throw new TypeError('Invalid strategy: ' + strategy); + } + + if (this._level !== level || this._strategy !== strategy) { + var self = this; + this.flush(binding.Z_SYNC_FLUSH, function () { + assert(self._handle, 'zlib binding closed'); + self._handle.params(level, strategy); + if (!self._hadError) { + self._level = level; + self._strategy = strategy; + if (callback) callback(); + } + }); + } else { + process.nextTick(callback); + } +}; + +Zlib.prototype.reset = function () { + assert(this._handle, 'zlib binding closed'); + return this._handle.reset(); +}; + +// This is the _flush function called by the transform class, +// internally, when the last chunk has been written. +Zlib.prototype._flush = function (callback) { + this._transform(Buffer.alloc(0), '', callback); +}; + +Zlib.prototype.flush = function (kind, callback) { + var _this2 = this; + + var ws = this._writableState; + + if (typeof kind === 'function' || kind === undefined && !callback) { + callback = kind; + kind = binding.Z_FULL_FLUSH; + } + + if (ws.ended) { + if (callback) process.nextTick(callback); + } else if (ws.ending) { + if (callback) this.once('end', callback); + } else if (ws.needDrain) { + if (callback) { + this.once('drain', function () { + return _this2.flush(kind, callback); + }); + } + } else { + this._flushFlag = kind; + this.write(Buffer.alloc(0), '', callback); + } +}; + +Zlib.prototype.close = function (callback) { + _close(this, callback); + process.nextTick(emitCloseNT, this); +}; + +function _close(engine, callback) { + if (callback) process.nextTick(callback); + + // Caller may invoke .close after a zlib error (which will null _handle). + if (!engine._handle) return; + + engine._handle.close(); + engine._handle = null; +} + +function emitCloseNT(self) { + self.emit('close'); +} + +Zlib.prototype._transform = function (chunk, encoding, cb) { + var flushFlag; + var ws = this._writableState; + var ending = ws.ending || ws.ended; + var last = ending && (!chunk || ws.length === chunk.length); + + if (chunk !== null && !Buffer.isBuffer(chunk)) return cb(new Error('invalid input')); + + if (!this._handle) return cb(new Error('zlib binding closed')); + + // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag + // (or whatever flag was provided using opts.finishFlush). + // If it's explicitly flushing at some other time, then we use + // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression + // goodness. + if (last) flushFlag = this._finishFlushFlag;else { + flushFlag = this._flushFlag; + // once we've flushed the last of the queue, stop flushing and + // go back to the normal behavior. + if (chunk.length >= ws.length) { + this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH; + } + } + + this._processChunk(chunk, flushFlag, cb); +}; + +Zlib.prototype._processChunk = function (chunk, flushFlag, cb) { + var availInBefore = chunk && chunk.length; + var availOutBefore = this._chunkSize - this._offset; + var inOff = 0; + + var self = this; + + var async = typeof cb === 'function'; + + if (!async) { + var buffers = []; + var nread = 0; + + var error; + this.on('error', function (er) { + error = er; + }); + + assert(this._handle, 'zlib binding closed'); + do { + var res = this._handle.writeSync(flushFlag, chunk, // in + inOff, // in_off + availInBefore, // in_len + this._buffer, // out + this._offset, //out_off + availOutBefore); // out_len + } while (!this._hadError && callback(res[0], res[1])); + + if (this._hadError) { + throw error; + } + + if (nread >= kMaxLength) { + _close(this); + throw new RangeError(kRangeErrorMessage); + } + + var buf = Buffer.concat(buffers, nread); + _close(this); + + return buf; + } + + assert(this._handle, 'zlib binding closed'); + var req = this._handle.write(flushFlag, chunk, // in + inOff, // in_off + availInBefore, // in_len + this._buffer, // out + this._offset, //out_off + availOutBefore); // out_len + + req.buffer = chunk; + req.callback = callback; + + function callback(availInAfter, availOutAfter) { + // When the callback is used in an async write, the callback's + // context is the `req` object that was created. The req object + // is === this._handle, and that's why it's important to null + // out the values after they are done being used. `this._handle` + // can stay in memory longer than the callback and buffer are needed. + if (this) { + this.buffer = null; + this.callback = null; + } + + if (self._hadError) return; + + var have = availOutBefore - availOutAfter; + assert(have >= 0, 'have should not go down'); + + if (have > 0) { + var out = self._buffer.slice(self._offset, self._offset + have); + self._offset += have; + // serve some output to the consumer. + if (async) { + self.push(out); + } else { + buffers.push(out); + nread += out.length; + } + } + + // exhausted the output buffer, or used all the input create a new one. + if (availOutAfter === 0 || self._offset >= self._chunkSize) { + availOutBefore = self._chunkSize; + self._offset = 0; + self._buffer = Buffer.allocUnsafe(self._chunkSize); + } + + if (availOutAfter === 0) { + // Not actually done. Need to reprocess. + // Also, update the availInBefore to the availInAfter value, + // so that if we have to hit it a third (fourth, etc.) time, + // it'll have the correct byte counts. + inOff += availInBefore - availInAfter; + availInBefore = availInAfter; + + if (!async) return true; + + var newReq = self._handle.write(flushFlag, chunk, inOff, availInBefore, self._buffer, self._offset, self._chunkSize); + newReq.callback = callback; // this same function + newReq.buffer = chunk; + return; + } + + if (!async) return false; + + // finished with the chunk. + cb(); + } +}; + +util.inherits(Deflate, Zlib); +util.inherits(Inflate, Zlib); +util.inherits(Gzip, Zlib); +util.inherits(Gunzip, Zlib); +util.inherits(DeflateRaw, Zlib); +util.inherits(InflateRaw, Zlib); +util.inherits(Unzip, Zlib); +}).call(this,require('_process')) +},{"./binding":178,"_process":202,"assert":175,"buffer":180,"stream":221,"util":233}],180:[function(require,module,exports){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + +'use strict' + +var base64 = require('base64-js') +var ieee754 = require('ieee754') + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 + +var K_MAX_LENGTH = 0x7fffffff +exports.kMaxLength = K_MAX_LENGTH + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Print warning and recommend using `buffer` v4.x which has an Object + * implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * We report that the browser does not support typed arrays if the are not subclassable + * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` + * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support + * for __proto__ and has a buggy typed array implementation. + */ +Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() + +if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && + typeof console.error === 'function') { + console.error( + 'This browser lacks typed array (Uint8Array) support which is required by ' + + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' + ) +} + +function typedArraySupport () { + // Can typed array instances can be augmented? + try { + var arr = new Uint8Array(1) + arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }} + return arr.foo() === 42 + } catch (e) { + return false + } +} + +function createBuffer (length) { + if (length > K_MAX_LENGTH) { + throw new RangeError('Invalid typed array length') + } + // Return an augmented `Uint8Array` instance + var buf = new Uint8Array(length) + buf.__proto__ = Buffer.prototype + return buf +} + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + +function Buffer (arg, encodingOrOffset, length) { + // Common case. + if (typeof arg === 'number') { + if (typeof encodingOrOffset === 'string') { + throw new Error( + 'If encoding is specified then the first argument must be a string' + ) + } + return allocUnsafe(arg) + } + return from(arg, encodingOrOffset, length) +} + +// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 +if (typeof Symbol !== 'undefined' && Symbol.species && + Buffer[Symbol.species] === Buffer) { + Object.defineProperty(Buffer, Symbol.species, { + value: null, + configurable: true, + enumerable: false, + writable: false + }) +} + +Buffer.poolSize = 8192 // not used by this implementation + +function from (value, encodingOrOffset, length) { + if (typeof value === 'number') { + throw new TypeError('"value" argument must not be a number') + } + + if (isArrayBuffer(value)) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof value === 'string') { + return fromString(value, encodingOrOffset) + } + + return fromObject(value) +} + +/** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ +Buffer.from = function (value, encodingOrOffset, length) { + return from(value, encodingOrOffset, length) +} + +// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: +// https://github.com/feross/buffer/pull/148 +Buffer.prototype.__proto__ = Uint8Array.prototype +Buffer.__proto__ = Uint8Array + +function assertSize (size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be a number') + } else if (size < 0) { + throw new RangeError('"size" argument must not be negative') + } +} + +function alloc (size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpretted as a start offset. + return typeof encoding === 'string' + ? createBuffer(size).fill(fill, encoding) + : createBuffer(size).fill(fill) + } + return createBuffer(size) +} + +/** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ +Buffer.alloc = function (size, fill, encoding) { + return alloc(size, fill, encoding) +} + +function allocUnsafe (size) { + assertSize(size) + return createBuffer(size < 0 ? 0 : checked(size) | 0) +} + +/** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ +Buffer.allocUnsafe = function (size) { + return allocUnsafe(size) +} +/** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ +Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(size) +} + +function fromString (string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('"encoding" must be a valid string encoding') + } + + var length = byteLength(string, encoding) | 0 + var buf = createBuffer(length) + + var actual = buf.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + buf = buf.slice(0, actual) + } + + return buf +} + +function fromArrayLike (array) { + var length = array.length < 0 ? 0 : checked(array.length) | 0 + var buf = createBuffer(length) + for (var i = 0; i < length; i += 1) { + buf[i] = array[i] & 255 + } + return buf +} + +function fromArrayBuffer (array, byteOffset, length) { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('\'offset\' is out of bounds') + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('\'length\' is out of bounds') + } + + var buf + if (byteOffset === undefined && length === undefined) { + buf = new Uint8Array(array) + } else if (length === undefined) { + buf = new Uint8Array(array, byteOffset) + } else { + buf = new Uint8Array(array, byteOffset, length) + } + + // Return an augmented `Uint8Array` instance + buf.__proto__ = Buffer.prototype + return buf +} + +function fromObject (obj) { + if (Buffer.isBuffer(obj)) { + var len = checked(obj.length) | 0 + var buf = createBuffer(len) + + if (buf.length === 0) { + return buf + } + + obj.copy(buf, 0, 0, len) + return buf + } + + if (obj) { + if (isArrayBufferView(obj) || 'length' in obj) { + if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { + return createBuffer(0) + } + return fromArrayLike(obj) + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + return fromArrayLike(obj.data) + } + } + + throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') +} + +function checked (length) { + // Note: cannot use `length < K_MAX_LENGTH` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= K_MAX_LENGTH) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) +} + +Buffer.isBuffer = function isBuffer (b) { + return b != null && b._isBuffer === true +} + +Buffer.compare = function compare (a, b) { + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError('Arguments must be Buffers') + } + + if (a === b) return 0 + + var x = a.length + var y = b.length + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + + if (list.length === 0) { + return Buffer.alloc(0) + } + + var i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; ++i) { + length += list[i].length + } + } + + var buffer = Buffer.allocUnsafe(length) + var pos = 0 + for (i = 0; i < list.length; ++i) { + var buf = list[i] + if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + buf.copy(buffer, pos) + pos += buf.length + } + return buffer +} + +function byteLength (string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length + } + if (isArrayBufferView(string) || isArrayBuffer(string)) { + return string.byteLength + } + if (typeof string !== 'string') { + string = '' + string + } + + var len = string.length + if (len === 0) return 0 + + // Use a for loop to avoid recursion + var loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len + case 'utf8': + case 'utf-8': + case undefined: + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) return utf8ToBytes(string).length // assume utf8 + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString (encoding, start, end) { + var loweredCase = false + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coersion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } + + if (!encoding) encoding = 'utf8' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) +// to detect a Buffer instance. It's not possible to use `instanceof Buffer` +// reliably in a browserify context because there could be multiple different +// copies of the 'buffer' package in use. This method works even for Buffer +// instances that were created from another copy of the `buffer` package. +// See: https://github.com/feross/buffer/issues/154 +Buffer.prototype._isBuffer = true + +function swap (b, n, m) { + var i = b[n] + b[n] = b[m] + b[m] = i +} + +Buffer.prototype.swap16 = function swap16 () { + var len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (var i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this +} + +Buffer.prototype.swap32 = function swap32 () { + var len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (var i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this +} + +Buffer.prototype.swap64 = function swap64 () { + var len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (var i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this +} + +Buffer.prototype.toString = function toString () { + var length = this.length + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + var str = '' + var max = exports.INSPECT_MAX_BYTES + if (this.length > 0) { + str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') + if (this.length > max) str += ' ... ' + } + return '' +} + +Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { + if (!Buffer.isBuffer(target)) { + throw new TypeError('Argument must be a Buffer') + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 + + var x = thisEnd - thisStart + var y = end - start + var len = Math.min(x, y) + + var thisCopy = this.slice(thisStart, thisEnd) + var targetCopy = target.slice(start, end) + + for (var i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding) + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) + } + + throw new TypeError('val must be string, number or Buffer') +} + +function arrayIndexOf (arr, val, byteOffset, encoding, dir) { + var indexSize = 1 + var arrLength = arr.length + var valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 + } + } + + function read (buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + var i + if (dir) { + var foundIndex = -1 + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize + } else { + if (foundIndex !== -1) i -= i - foundIndex + foundIndex = -1 + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + var found = true + for (var j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } + } + + return -1 +} + +Buffer.prototype.includes = function includes (val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) +} + +Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + // must be an even number of digits + var strLen = string.length + if (strLen % 2 !== 0) throw new TypeError('Invalid hex string') + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; ++i) { + var parsed = parseInt(string.substr(i * 2, 2), 16) + if (numberIsNaN(parsed)) return i + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function latin1Write (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0 + if (isFinite(length)) { + length = length >>> 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + } else { + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) + } + + var remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + var loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + return asciiWrite(this, string, offset, length) + + case 'latin1': + case 'binary': + return latin1Write(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function latin1Slice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; ++i) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + var newBuf = this.subarray(start, end) + // Return an augmented `Uint8Array` instance + newBuf.__proto__ = Buffer.prototype + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + var val = this[offset + --byteLength] + var mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var i = byteLength + var mul = 1 + var val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') +} + +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var mul = 1 + var i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var i = byteLength - 1 + var mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = 0 + var mul = 1 + var sub = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = byteLength - 1 + var mul = 1 + var sub = 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + var len = end - start + var i + + if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (i = len - 1; i >= 0; --i) { + target[i + targetStart] = this[i + start] + } + } else if (len < 1000) { + // ascending copy from start + for (i = 0; i < len; ++i) { + target[i + targetStart] = this[i + start] + } + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, start + len), + targetStart + ) + } + + return len +} + +// Usage: +// buffer.fill(number[, offset[, end]]) +// buffer.fill(buffer[, offset[, end]]) +// buffer.fill(string[, offset[, end]][, encoding]) +Buffer.prototype.fill = function fill (val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (val.length === 1) { + var code = val.charCodeAt(0) + if (code < 256) { + val = code + } + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + } else if (typeof val === 'number') { + val = val & 255 + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } + + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 + + if (!val) val = 0 + + var i + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val + } + } else { + var bytes = Buffer.isBuffer(val) + ? val + : new Buffer(val, encoding) + var len = bytes.length + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] + } + } + + return this +} + +// HELPER FUNCTIONS +// ================ + +var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (string, units) { + units = units || Infinity + var codePoint + var length = string.length + var leadSurrogate = null + var bytes = [] + + for (var i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; ++i) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +// ArrayBuffers from another context (i.e. an iframe) do not pass the `instanceof` check +// but they should be treated as valid. See: https://github.com/feross/buffer/issues/166 +function isArrayBuffer (obj) { + return obj instanceof ArrayBuffer || + (obj != null && obj.constructor != null && obj.constructor.name === 'ArrayBuffer' && + typeof obj.byteLength === 'number') +} + +// Node 0.10 supports `ArrayBuffer` but lacks `ArrayBuffer.isView` +function isArrayBufferView (obj) { + return (typeof ArrayBuffer.isView === 'function') && ArrayBuffer.isView(obj) +} + +function numberIsNaN (obj) { + return obj !== obj // eslint-disable-line no-self-compare +} + +},{"base64-js":176,"ieee754":186}],181:[function(require,module,exports){ +module.exports = { + "100": "Continue", + "101": "Switching Protocols", + "102": "Processing", + "200": "OK", + "201": "Created", + "202": "Accepted", + "203": "Non-Authoritative Information", + "204": "No Content", + "205": "Reset Content", + "206": "Partial Content", + "207": "Multi-Status", + "208": "Already Reported", + "226": "IM Used", + "300": "Multiple Choices", + "301": "Moved Permanently", + "302": "Found", + "303": "See Other", + "304": "Not Modified", + "305": "Use Proxy", + "307": "Temporary Redirect", + "308": "Permanent Redirect", + "400": "Bad Request", + "401": "Unauthorized", + "402": "Payment Required", + "403": "Forbidden", + "404": "Not Found", + "405": "Method Not Allowed", + "406": "Not Acceptable", + "407": "Proxy Authentication Required", + "408": "Request Timeout", + "409": "Conflict", + "410": "Gone", + "411": "Length Required", + "412": "Precondition Failed", + "413": "Payload Too Large", + "414": "URI Too Long", + "415": "Unsupported Media Type", + "416": "Range Not Satisfiable", + "417": "Expectation Failed", + "418": "I'm a teapot", + "421": "Misdirected Request", + "422": "Unprocessable Entity", + "423": "Locked", + "424": "Failed Dependency", + "425": "Unordered Collection", + "426": "Upgrade Required", + "428": "Precondition Required", + "429": "Too Many Requests", + "431": "Request Header Fields Too Large", + "451": "Unavailable For Legal Reasons", + "500": "Internal Server Error", + "501": "Not Implemented", + "502": "Bad Gateway", + "503": "Service Unavailable", + "504": "Gateway Timeout", + "505": "HTTP Version Not Supported", + "506": "Variant Also Negotiates", + "507": "Insufficient Storage", + "508": "Loop Detected", + "509": "Bandwidth Limit Exceeded", + "510": "Not Extended", + "511": "Network Authentication Required" +} + +},{}],182:[function(require,module,exports){ +module.exports={ + "O_RDONLY": 0, + "O_WRONLY": 1, + "O_RDWR": 2, + "S_IFMT": 61440, + "S_IFREG": 32768, + "S_IFDIR": 16384, + "S_IFCHR": 8192, + "S_IFBLK": 24576, + "S_IFIFO": 4096, + "S_IFLNK": 40960, + "S_IFSOCK": 49152, + "O_CREAT": 512, + "O_EXCL": 2048, + "O_NOCTTY": 131072, + "O_TRUNC": 1024, + "O_APPEND": 8, + "O_DIRECTORY": 1048576, + "O_NOFOLLOW": 256, + "O_SYNC": 128, + "O_SYMLINK": 2097152, + "O_NONBLOCK": 4, + "S_IRWXU": 448, + "S_IRUSR": 256, + "S_IWUSR": 128, + "S_IXUSR": 64, + "S_IRWXG": 56, + "S_IRGRP": 32, + "S_IWGRP": 16, + "S_IXGRP": 8, + "S_IRWXO": 7, + "S_IROTH": 4, + "S_IWOTH": 2, + "S_IXOTH": 1, + "E2BIG": 7, + "EACCES": 13, + "EADDRINUSE": 48, + "EADDRNOTAVAIL": 49, + "EAFNOSUPPORT": 47, + "EAGAIN": 35, + "EALREADY": 37, + "EBADF": 9, + "EBADMSG": 94, + "EBUSY": 16, + "ECANCELED": 89, + "ECHILD": 10, + "ECONNABORTED": 53, + "ECONNREFUSED": 61, + "ECONNRESET": 54, + "EDEADLK": 11, + "EDESTADDRREQ": 39, + "EDOM": 33, + "EDQUOT": 69, + "EEXIST": 17, + "EFAULT": 14, + "EFBIG": 27, + "EHOSTUNREACH": 65, + "EIDRM": 90, + "EILSEQ": 92, + "EINPROGRESS": 36, + "EINTR": 4, + "EINVAL": 22, + "EIO": 5, + "EISCONN": 56, + "EISDIR": 21, + "ELOOP": 62, + "EMFILE": 24, + "EMLINK": 31, + "EMSGSIZE": 40, + "EMULTIHOP": 95, + "ENAMETOOLONG": 63, + "ENETDOWN": 50, + "ENETRESET": 52, + "ENETUNREACH": 51, + "ENFILE": 23, + "ENOBUFS": 55, + "ENODATA": 96, + "ENODEV": 19, + "ENOENT": 2, + "ENOEXEC": 8, + "ENOLCK": 77, + "ENOLINK": 97, + "ENOMEM": 12, + "ENOMSG": 91, + "ENOPROTOOPT": 42, + "ENOSPC": 28, + "ENOSR": 98, + "ENOSTR": 99, + "ENOSYS": 78, + "ENOTCONN": 57, + "ENOTDIR": 20, + "ENOTEMPTY": 66, + "ENOTSOCK": 38, + "ENOTSUP": 45, + "ENOTTY": 25, + "ENXIO": 6, + "EOPNOTSUPP": 102, + "EOVERFLOW": 84, + "EPERM": 1, + "EPIPE": 32, + "EPROTO": 100, + "EPROTONOSUPPORT": 43, + "EPROTOTYPE": 41, + "ERANGE": 34, + "EROFS": 30, + "ESPIPE": 29, + "ESRCH": 3, + "ESTALE": 70, + "ETIME": 101, + "ETIMEDOUT": 60, + "ETXTBSY": 26, + "EWOULDBLOCK": 35, + "EXDEV": 18, + "SIGHUP": 1, + "SIGINT": 2, + "SIGQUIT": 3, + "SIGILL": 4, + "SIGTRAP": 5, + "SIGABRT": 6, + "SIGIOT": 6, + "SIGBUS": 10, + "SIGFPE": 8, + "SIGKILL": 9, + "SIGUSR1": 30, + "SIGSEGV": 11, + "SIGUSR2": 31, + "SIGPIPE": 13, + "SIGALRM": 14, + "SIGTERM": 15, + "SIGCHLD": 20, + "SIGCONT": 19, + "SIGSTOP": 17, + "SIGTSTP": 18, + "SIGTTIN": 21, + "SIGTTOU": 22, + "SIGURG": 16, + "SIGXCPU": 24, + "SIGXFSZ": 25, + "SIGVTALRM": 26, + "SIGPROF": 27, + "SIGWINCH": 28, + "SIGIO": 23, + "SIGSYS": 12, + "SSL_OP_ALL": 2147486719, + "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION": 262144, + "SSL_OP_CIPHER_SERVER_PREFERENCE": 4194304, + "SSL_OP_CISCO_ANYCONNECT": 32768, + "SSL_OP_COOKIE_EXCHANGE": 8192, + "SSL_OP_CRYPTOPRO_TLSEXT_BUG": 2147483648, + "SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS": 2048, + "SSL_OP_EPHEMERAL_RSA": 0, + "SSL_OP_LEGACY_SERVER_CONNECT": 4, + "SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER": 32, + "SSL_OP_MICROSOFT_SESS_ID_BUG": 1, + "SSL_OP_MSIE_SSLV2_RSA_PADDING": 0, + "SSL_OP_NETSCAPE_CA_DN_BUG": 536870912, + "SSL_OP_NETSCAPE_CHALLENGE_BUG": 2, + "SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG": 1073741824, + "SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG": 8, + "SSL_OP_NO_COMPRESSION": 131072, + "SSL_OP_NO_QUERY_MTU": 4096, + "SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION": 65536, + "SSL_OP_NO_SSLv2": 16777216, + "SSL_OP_NO_SSLv3": 33554432, + "SSL_OP_NO_TICKET": 16384, + "SSL_OP_NO_TLSv1": 67108864, + "SSL_OP_NO_TLSv1_1": 268435456, + "SSL_OP_NO_TLSv1_2": 134217728, + "SSL_OP_PKCS1_CHECK_1": 0, + "SSL_OP_PKCS1_CHECK_2": 0, + "SSL_OP_SINGLE_DH_USE": 1048576, + "SSL_OP_SINGLE_ECDH_USE": 524288, + "SSL_OP_SSLEAY_080_CLIENT_DH_BUG": 128, + "SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG": 0, + "SSL_OP_TLS_BLOCK_PADDING_BUG": 512, + "SSL_OP_TLS_D5_BUG": 256, + "SSL_OP_TLS_ROLLBACK_BUG": 8388608, + "ENGINE_METHOD_DSA": 2, + "ENGINE_METHOD_DH": 4, + "ENGINE_METHOD_RAND": 8, + "ENGINE_METHOD_ECDH": 16, + "ENGINE_METHOD_ECDSA": 32, + "ENGINE_METHOD_CIPHERS": 64, + "ENGINE_METHOD_DIGESTS": 128, + "ENGINE_METHOD_STORE": 256, + "ENGINE_METHOD_PKEY_METHS": 512, + "ENGINE_METHOD_PKEY_ASN1_METHS": 1024, + "ENGINE_METHOD_ALL": 65535, + "ENGINE_METHOD_NONE": 0, + "DH_CHECK_P_NOT_SAFE_PRIME": 2, + "DH_CHECK_P_NOT_PRIME": 1, + "DH_UNABLE_TO_CHECK_GENERATOR": 4, + "DH_NOT_SUITABLE_GENERATOR": 8, + "NPN_ENABLED": 1, + "RSA_PKCS1_PADDING": 1, + "RSA_SSLV23_PADDING": 2, + "RSA_NO_PADDING": 3, + "RSA_PKCS1_OAEP_PADDING": 4, + "RSA_X931_PADDING": 5, + "RSA_PKCS1_PSS_PADDING": 6, + "POINT_CONVERSION_COMPRESSED": 2, + "POINT_CONVERSION_UNCOMPRESSED": 4, + "POINT_CONVERSION_HYBRID": 6, + "F_OK": 0, + "R_OK": 4, + "W_OK": 2, + "X_OK": 1, + "UV_UDP_REUSEADDR": 4 +} + +},{}],183:[function(require,module,exports){ +(function (Buffer){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. + +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); + } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = Buffer.isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +}).call(this,{"isBuffer":require("../../is-buffer/index.js")}) +},{"../../is-buffer/index.js":188}],184:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } else { + // At least give some kind of context to the user + var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); + err.context = er; + throw err; + } + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); + } + } else if (isObject(handler)) { + args = Array.prototype.slice.call(arguments, 1); + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else if (listeners) { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.prototype.listenerCount = function(type) { + if (this._events) { + var evlistener = this._events[type]; + + if (isFunction(evlistener)) + return 1; + else if (evlistener) + return evlistener.length; + } + return 0; +}; + +EventEmitter.listenerCount = function(emitter, type) { + return emitter.listenerCount(type); +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} + +},{}],185:[function(require,module,exports){ +var http = require('http') +var url = require('url') + +var https = module.exports + +for (var key in http) { + if (http.hasOwnProperty(key)) https[key] = http[key] +} + +https.request = function (params, cb) { + params = validateParams(params) + return http.request.call(this, params, cb) +} + +https.get = function (params, cb) { + params = validateParams(params) + return http.get.call(this, params, cb) +} + +function validateParams (params) { + if (typeof params === 'string') { + params = url.parse(params) + } + if (!params.protocol) { + params.protocol = 'https:' + } + if (params.protocol !== 'https:') { + throw new Error('Protocol "' + params.protocol + '" not supported. Expected "https:"') + } + return params +} + +},{"http":222,"url":228}],186:[function(require,module,exports){ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],187:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],188:[function(require,module,exports){ +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +// The _isBuffer check is for Safari 5-7 support, because it's missing +// Object.prototype.constructor. Remove this eventually +module.exports = function (obj) { + return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +} + +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} + +// For Node v0.10 support. Remove this eventually. +function isSlowBuffer (obj) { + return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) +} + +},{}],189:[function(require,module,exports){ +var toString = {}.toString; + +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; + +},{}],190:[function(require,module,exports){ +'use strict'; + + +var TYPED_OK = (typeof Uint8Array !== 'undefined') && + (typeof Uint16Array !== 'undefined') && + (typeof Int32Array !== 'undefined'); + +function _has(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +exports.assign = function (obj /*from1, from2, from3, ...*/) { + var sources = Array.prototype.slice.call(arguments, 1); + while (sources.length) { + var source = sources.shift(); + if (!source) { continue; } + + if (typeof source !== 'object') { + throw new TypeError(source + 'must be non-object'); + } + + for (var p in source) { + if (_has(source, p)) { + obj[p] = source[p]; + } + } + } + + return obj; +}; + + +// reduce buffer size, avoiding mem copy +exports.shrinkBuf = function (buf, size) { + if (buf.length === size) { return buf; } + if (buf.subarray) { return buf.subarray(0, size); } + buf.length = size; + return buf; +}; + + +var fnTyped = { + arraySet: function (dest, src, src_offs, len, dest_offs) { + if (src.subarray && dest.subarray) { + dest.set(src.subarray(src_offs, src_offs + len), dest_offs); + return; + } + // Fallback to ordinary array + for (var i = 0; i < len; i++) { + dest[dest_offs + i] = src[src_offs + i]; + } + }, + // Join array of chunks to single array. + flattenChunks: function (chunks) { + var i, l, len, pos, chunk, result; + + // calculate data length + len = 0; + for (i = 0, l = chunks.length; i < l; i++) { + len += chunks[i].length; + } + + // join chunks + result = new Uint8Array(len); + pos = 0; + for (i = 0, l = chunks.length; i < l; i++) { + chunk = chunks[i]; + result.set(chunk, pos); + pos += chunk.length; + } + + return result; + } +}; + +var fnUntyped = { + arraySet: function (dest, src, src_offs, len, dest_offs) { + for (var i = 0; i < len; i++) { + dest[dest_offs + i] = src[src_offs + i]; + } + }, + // Join array of chunks to single array. + flattenChunks: function (chunks) { + return [].concat.apply([], chunks); + } +}; + + +// Enable/Disable typed arrays use, for testing +// +exports.setTyped = function (on) { + if (on) { + exports.Buf8 = Uint8Array; + exports.Buf16 = Uint16Array; + exports.Buf32 = Int32Array; + exports.assign(exports, fnTyped); + } else { + exports.Buf8 = Array; + exports.Buf16 = Array; + exports.Buf32 = Array; + exports.assign(exports, fnUntyped); + } +}; + +exports.setTyped(TYPED_OK); + +},{}],191:[function(require,module,exports){ +'use strict'; + +// Note: adler32 takes 12% for level 0 and 2% for level 6. +// It isn't worth it to make additional optimizations as in original. +// Small size is preferable. + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +function adler32(adler, buf, len, pos) { + var s1 = (adler & 0xffff) |0, + s2 = ((adler >>> 16) & 0xffff) |0, + n = 0; + + while (len !== 0) { + // Set limit ~ twice less than 5552, to keep + // s2 in 31-bits, because we force signed ints. + // in other case %= will fail. + n = len > 2000 ? 2000 : len; + len -= n; + + do { + s1 = (s1 + buf[pos++]) |0; + s2 = (s2 + s1) |0; + } while (--n); + + s1 %= 65521; + s2 %= 65521; + } + + return (s1 | (s2 << 16)) |0; +} + + +module.exports = adler32; + +},{}],192:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +module.exports = { + + /* Allowed flush values; see deflate() and inflate() below for details */ + Z_NO_FLUSH: 0, + Z_PARTIAL_FLUSH: 1, + Z_SYNC_FLUSH: 2, + Z_FULL_FLUSH: 3, + Z_FINISH: 4, + Z_BLOCK: 5, + Z_TREES: 6, + + /* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + Z_OK: 0, + Z_STREAM_END: 1, + Z_NEED_DICT: 2, + Z_ERRNO: -1, + Z_STREAM_ERROR: -2, + Z_DATA_ERROR: -3, + //Z_MEM_ERROR: -4, + Z_BUF_ERROR: -5, + //Z_VERSION_ERROR: -6, + + /* compression levels */ + Z_NO_COMPRESSION: 0, + Z_BEST_SPEED: 1, + Z_BEST_COMPRESSION: 9, + Z_DEFAULT_COMPRESSION: -1, + + + Z_FILTERED: 1, + Z_HUFFMAN_ONLY: 2, + Z_RLE: 3, + Z_FIXED: 4, + Z_DEFAULT_STRATEGY: 0, + + /* Possible values of the data_type field (though see inflate()) */ + Z_BINARY: 0, + Z_TEXT: 1, + //Z_ASCII: 1, // = Z_TEXT (deprecated) + Z_UNKNOWN: 2, + + /* The deflate compression method */ + Z_DEFLATED: 8 + //Z_NULL: null // Use -1 or null inline, depending on var type +}; + +},{}],193:[function(require,module,exports){ +'use strict'; + +// Note: we can't get significant speed boost here. +// So write code to minimize size - no pregenerated tables +// and array tools dependencies. + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +// Use ordinary array, since untyped makes no boost here +function makeTable() { + var c, table = []; + + for (var n = 0; n < 256; n++) { + c = n; + for (var k = 0; k < 8; k++) { + c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; +} + +// Create table on load. Just 255 signed longs. Not a problem. +var crcTable = makeTable(); + + +function crc32(crc, buf, len, pos) { + var t = crcTable, + end = pos + len; + + crc ^= -1; + + for (var i = pos; i < end; i++) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + + +module.exports = crc32; + +},{}],194:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +var utils = require('../utils/common'); +var trees = require('./trees'); +var adler32 = require('./adler32'); +var crc32 = require('./crc32'); +var msg = require('./messages'); + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + + +/* Allowed flush values; see deflate() and inflate() below for details */ +var Z_NO_FLUSH = 0; +var Z_PARTIAL_FLUSH = 1; +//var Z_SYNC_FLUSH = 2; +var Z_FULL_FLUSH = 3; +var Z_FINISH = 4; +var Z_BLOCK = 5; +//var Z_TREES = 6; + + +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ +var Z_OK = 0; +var Z_STREAM_END = 1; +//var Z_NEED_DICT = 2; +//var Z_ERRNO = -1; +var Z_STREAM_ERROR = -2; +var Z_DATA_ERROR = -3; +//var Z_MEM_ERROR = -4; +var Z_BUF_ERROR = -5; +//var Z_VERSION_ERROR = -6; + + +/* compression levels */ +//var Z_NO_COMPRESSION = 0; +//var Z_BEST_SPEED = 1; +//var Z_BEST_COMPRESSION = 9; +var Z_DEFAULT_COMPRESSION = -1; + + +var Z_FILTERED = 1; +var Z_HUFFMAN_ONLY = 2; +var Z_RLE = 3; +var Z_FIXED = 4; +var Z_DEFAULT_STRATEGY = 0; + +/* Possible values of the data_type field (though see inflate()) */ +//var Z_BINARY = 0; +//var Z_TEXT = 1; +//var Z_ASCII = 1; // = Z_TEXT +var Z_UNKNOWN = 2; + + +/* The deflate compression method */ +var Z_DEFLATED = 8; + +/*============================================================================*/ + + +var MAX_MEM_LEVEL = 9; +/* Maximum value for memLevel in deflateInit2 */ +var MAX_WBITS = 15; +/* 32K LZ77 window */ +var DEF_MEM_LEVEL = 8; + + +var LENGTH_CODES = 29; +/* number of length codes, not counting the special END_BLOCK code */ +var LITERALS = 256; +/* number of literal bytes 0..255 */ +var L_CODES = LITERALS + 1 + LENGTH_CODES; +/* number of Literal or Length codes, including the END_BLOCK code */ +var D_CODES = 30; +/* number of distance codes */ +var BL_CODES = 19; +/* number of codes used to transfer the bit lengths */ +var HEAP_SIZE = 2 * L_CODES + 1; +/* maximum heap size */ +var MAX_BITS = 15; +/* All codes must not exceed MAX_BITS bits */ + +var MIN_MATCH = 3; +var MAX_MATCH = 258; +var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + +var PRESET_DICT = 0x20; + +var INIT_STATE = 42; +var EXTRA_STATE = 69; +var NAME_STATE = 73; +var COMMENT_STATE = 91; +var HCRC_STATE = 103; +var BUSY_STATE = 113; +var FINISH_STATE = 666; + +var BS_NEED_MORE = 1; /* block not completed, need more input or more output */ +var BS_BLOCK_DONE = 2; /* block flush performed */ +var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ +var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ + +var OS_CODE = 0x03; // Unix :) . Don't detect, use this default. + +function err(strm, errorCode) { + strm.msg = msg[errorCode]; + return errorCode; +} + +function rank(f) { + return ((f) << 1) - ((f) > 4 ? 9 : 0); +} + +function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } + + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->output buffer and copying into it. + * (See also read_buf()). + */ +function flush_pending(strm) { + var s = strm.state; + + //_tr_flush_bits(s); + var len = s.pending; + if (len > strm.avail_out) { + len = strm.avail_out; + } + if (len === 0) { return; } + + utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out); + strm.next_out += len; + s.pending_out += len; + strm.total_out += len; + strm.avail_out -= len; + s.pending -= len; + if (s.pending === 0) { + s.pending_out = 0; + } +} + + +function flush_block_only(s, last) { + trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); + s.block_start = s.strstart; + flush_pending(s.strm); +} + + +function put_byte(s, b) { + s.pending_buf[s.pending++] = b; +} + + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +function putShortMSB(s, b) { +// put_byte(s, (Byte)(b >> 8)); +// put_byte(s, (Byte)(b & 0xff)); + s.pending_buf[s.pending++] = (b >>> 8) & 0xff; + s.pending_buf[s.pending++] = b & 0xff; +} + + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->input buffer and copying from it. + * (See also flush_pending()). + */ +function read_buf(strm, buf, start, size) { + var len = strm.avail_in; + + if (len > size) { len = size; } + if (len === 0) { return 0; } + + strm.avail_in -= len; + + // zmemcpy(buf, strm->next_in, len); + utils.arraySet(buf, strm.input, strm.next_in, len, start); + if (strm.state.wrap === 1) { + strm.adler = adler32(strm.adler, buf, len, start); + } + + else if (strm.state.wrap === 2) { + strm.adler = crc32(strm.adler, buf, len, start); + } + + strm.next_in += len; + strm.total_in += len; + + return len; +} + + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +function longest_match(s, cur_match) { + var chain_length = s.max_chain_length; /* max hash chain length */ + var scan = s.strstart; /* current string */ + var match; /* matched string */ + var len; /* length of current match */ + var best_len = s.prev_length; /* best match length so far */ + var nice_match = s.nice_match; /* stop if match long enough */ + var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? + s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; + + var _win = s.window; // shortcut + + var wmask = s.w_mask; + var prev = s.prev; + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + var strend = s.strstart + MAX_MATCH; + var scan_end1 = _win[scan + best_len - 1]; + var scan_end = _win[scan + best_len]; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s.prev_length >= s.good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (nice_match > s.lookahead) { nice_match = s.lookahead; } + + // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + // Assert(cur_match < s->strstart, "no future"); + match = cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ + + if (_win[match + best_len] !== scan_end || + _win[match + best_len - 1] !== scan_end1 || + _win[match] !== _win[scan] || + _win[++match] !== _win[scan + 1]) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2; + match++; + // Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + /*jshint noempty:false*/ + } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + scan < strend); + + // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) { + s.match_start = cur_match; + best_len = len; + if (len >= nice_match) { + break; + } + scan_end1 = _win[scan + best_len - 1]; + scan_end = _win[scan + best_len]; + } + } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); + + if (best_len <= s.lookahead) { + return best_len; + } + return s.lookahead; +} + + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +function fill_window(s) { + var _w_size = s.w_size; + var p, n, m, more, str; + + //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = s.window_size - s.lookahead - s.strstart; + + // JS ints have 32 bit, block below not needed + /* Deal with !@#$% 64K limit: */ + //if (sizeof(int) <= 2) { + // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + // more = wsize; + // + // } else if (more == (unsigned)(-1)) { + // /* Very unlikely, but possible on 16 bit machine if + // * strstart == 0 && lookahead == 1 (input done a byte at time) + // */ + // more--; + // } + //} + + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { + + utils.arraySet(s.window, s.window, _w_size, _w_size, 0); + s.match_start -= _w_size; + s.strstart -= _w_size; + /* we now have strstart >= MAX_DIST */ + s.block_start -= _w_size; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + + n = s.hash_size; + p = n; + do { + m = s.head[--p]; + s.head[p] = (m >= _w_size ? m - _w_size : 0); + } while (--n); + + n = _w_size; + p = n; + do { + m = s.prev[--p]; + s.prev[p] = (m >= _w_size ? m - _w_size : 0); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); + + more += _w_size; + } + if (s.strm.avail_in === 0) { + break; + } + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + //Assert(more >= 2, "more < 2"); + n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); + s.lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s.lookahead + s.insert >= MIN_MATCH) { + str = s.strstart - s.insert; + s.ins_h = s.window[str]; + + /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask; +//#if MIN_MATCH != 3 +// Call update_hash() MIN_MATCH-3 more times +//#endif + while (s.insert) { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = str; + str++; + s.insert--; + if (s.lookahead + s.insert < MIN_MATCH) { + break; + } + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ +// if (s.high_water < s.window_size) { +// var curr = s.strstart + s.lookahead; +// var init = 0; +// +// if (s.high_water < curr) { +// /* Previous high water mark below current data -- zero WIN_INIT +// * bytes or up to end of window, whichever is less. +// */ +// init = s.window_size - curr; +// if (init > WIN_INIT) +// init = WIN_INIT; +// zmemzero(s->window + curr, (unsigned)init); +// s->high_water = curr + init; +// } +// else if (s->high_water < (ulg)curr + WIN_INIT) { +// /* High water mark at or above current data, but below current data +// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up +// * to end of window, whichever is less. +// */ +// init = (ulg)curr + WIN_INIT - s->high_water; +// if (init > s->window_size - s->high_water) +// init = s->window_size - s->high_water; +// zmemzero(s->window + s->high_water, (unsigned)init); +// s->high_water += init; +// } +// } +// +// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, +// "not enough room for search"); +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +function deflate_stored(s, flush) { + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + var max_block_size = 0xffff; + + if (max_block_size > s.pending_buf_size - 5) { + max_block_size = s.pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s.lookahead <= 1) { + + //Assert(s->strstart < s->w_size+MAX_DIST(s) || + // s->block_start >= (long)s->w_size, "slide too late"); +// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || +// s.block_start >= s.w_size)) { +// throw new Error("slide too late"); +// } + + fill_window(s); + if (s.lookahead === 0 && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + + if (s.lookahead === 0) { + break; + } + /* flush the current block */ + } + //Assert(s->block_start >= 0L, "block gone"); +// if (s.block_start < 0) throw new Error("block gone"); + + s.strstart += s.lookahead; + s.lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + var max_start = s.block_start + max_block_size; + + if (s.strstart === 0 || s.strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s.lookahead = s.strstart - max_start; + s.strstart = max_start; + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + + + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + + s.insert = 0; + + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + + if (s.strstart > s.block_start) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_NEED_MORE; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +function deflate_fast(s, flush) { + var hash_head; /* head of the hash chain */ + var bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) { + break; /* flush the current block */ + } + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0/*NIL*/; + if (s.lookahead >= MIN_MATCH) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + } + if (s.match_length >= MIN_MATCH) { + // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only + + /*** _tr_tally_dist(s, s.strstart - s.match_start, + s.match_length - MIN_MATCH, bflush); ***/ + bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); + + s.lookahead -= s.match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { + s.match_length--; /* string at strstart already in table */ + do { + s.strstart++; + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s.match_length !== 0); + s.strstart++; + } else + { + s.strstart += s.match_length; + s.match_length = 0; + s.ins_h = s.window[s.strstart]; + /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask; + +//#if MIN_MATCH != 3 +// Call UPDATE_HASH() MIN_MATCH-3 more times +//#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s.window[s.strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = trees._tr_tally(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1); + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +function deflate_slow(s, flush) { + var hash_head; /* head of hash chain */ + var bflush; /* set if current block must be flushed */ + + var max_insert; + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) { break; } /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0/*NIL*/; + if (s.lookahead >= MIN_MATCH) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + */ + s.prev_length = s.match_length; + s.prev_match = s.match_start; + s.match_length = MIN_MATCH - 1; + + if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && + s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + + if (s.match_length <= 5 && + (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s.match_length = MIN_MATCH - 1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { + max_insert = s.strstart + s.lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + //check_match(s, s.strstart-1, s.prev_match, s.prev_length); + + /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, + s.prev_length - MIN_MATCH, bflush);***/ + bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s.lookahead -= s.prev_length - 1; + s.prev_length -= 2; + do { + if (++s.strstart <= max_insert) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + } while (--s.prev_length !== 0); + s.match_available = 0; + s.match_length = MIN_MATCH - 1; + s.strstart++; + + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + } else if (s.match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); + + if (bflush) { + /*** FLUSH_BLOCK_ONLY(s, 0) ***/ + flush_block_only(s, false); + /***/ + } + s.strstart++; + s.lookahead--; + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s.match_available = 1; + s.strstart++; + s.lookahead--; + } + } + //Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s.match_available) { + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]); + + s.match_available = 0; + } + s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_BLOCK_DONE; +} + + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +function deflate_rle(s, flush) { + var bflush; /* set if current block must be flushed */ + var prev; /* byte at distance one to match */ + var scan, strend; /* scan goes up to strend for length of run */ + + var _win = s.window; + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s.lookahead <= MAX_MATCH) { + fill_window(s); + if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) { break; } /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s.match_length = 0; + if (s.lookahead >= MIN_MATCH && s.strstart > 0) { + scan = s.strstart - 1; + prev = _win[scan]; + if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { + strend = s.strstart + MAX_MATCH; + do { + /*jshint noempty:false*/ + } while (prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + scan < strend); + s.match_length = MAX_MATCH - (strend - scan); + if (s.match_length > s.lookahead) { + s.match_length = s.lookahead; + } + } + //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s.match_length >= MIN_MATCH) { + //check_match(s, s.strstart, s.strstart - 1, s.match_length); + + /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ + bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH); + + s.lookahead -= s.match_length; + s.strstart += s.match_length; + s.match_length = 0; + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = trees._tr_tally(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +function deflate_huff(s, flush) { + var bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s.lookahead === 0) { + fill_window(s); + if (s.lookahead === 0) { + if (flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s.match_length = 0; + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = trees._tr_tally(s, 0, s.window[s.strstart]); + s.lookahead--; + s.strstart++; + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; +} + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +function Config(good_length, max_lazy, nice_length, max_chain, func) { + this.good_length = good_length; + this.max_lazy = max_lazy; + this.nice_length = nice_length; + this.max_chain = max_chain; + this.func = func; +} + +var configuration_table; + +configuration_table = [ + /* good lazy nice chain */ + new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ + new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ + new Config(4, 5, 16, 8, deflate_fast), /* 2 */ + new Config(4, 6, 32, 32, deflate_fast), /* 3 */ + + new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ + new Config(8, 16, 32, 32, deflate_slow), /* 5 */ + new Config(8, 16, 128, 128, deflate_slow), /* 6 */ + new Config(8, 32, 128, 256, deflate_slow), /* 7 */ + new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ + new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ +]; + + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +function lm_init(s) { + s.window_size = 2 * s.w_size; + + /*** CLEAR_HASH(s); ***/ + zero(s.head); // Fill with NIL (= 0); + + /* Set the default configuration parameters: + */ + s.max_lazy_match = configuration_table[s.level].max_lazy; + s.good_match = configuration_table[s.level].good_length; + s.nice_match = configuration_table[s.level].nice_length; + s.max_chain_length = configuration_table[s.level].max_chain; + + s.strstart = 0; + s.block_start = 0; + s.lookahead = 0; + s.insert = 0; + s.match_length = s.prev_length = MIN_MATCH - 1; + s.match_available = 0; + s.ins_h = 0; +} + + +function DeflateState() { + this.strm = null; /* pointer back to this zlib stream */ + this.status = 0; /* as the name implies */ + this.pending_buf = null; /* output still pending */ + this.pending_buf_size = 0; /* size of pending_buf */ + this.pending_out = 0; /* next pending byte to output to the stream */ + this.pending = 0; /* nb of bytes in the pending buffer */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.gzhead = null; /* gzip header information to write */ + this.gzindex = 0; /* where in extra, name, or comment */ + this.method = Z_DEFLATED; /* can only be DEFLATED */ + this.last_flush = -1; /* value of flush param for previous deflate call */ + + this.w_size = 0; /* LZ77 window size (32K by default) */ + this.w_bits = 0; /* log2(w_size) (8..16) */ + this.w_mask = 0; /* w_size - 1 */ + + this.window = null; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. + */ + + this.window_size = 0; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + this.prev = null; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + this.head = null; /* Heads of the hash chains or NIL. */ + + this.ins_h = 0; /* hash index of string to be inserted */ + this.hash_size = 0; /* number of elements in hash table */ + this.hash_bits = 0; /* log2(hash_size) */ + this.hash_mask = 0; /* hash_size-1 */ + + this.hash_shift = 0; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + this.block_start = 0; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + this.match_length = 0; /* length of best match */ + this.prev_match = 0; /* previous match */ + this.match_available = 0; /* set if previous match exists */ + this.strstart = 0; /* start of string to insert */ + this.match_start = 0; /* start of matching string */ + this.lookahead = 0; /* number of valid bytes ahead in window */ + + this.prev_length = 0; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + this.max_chain_length = 0; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + this.max_lazy_match = 0; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ + // That's alias to max_lazy_match, don't use directly + //this.max_insert_length = 0; + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + this.level = 0; /* compression level (1..9) */ + this.strategy = 0; /* favor or force Huffman coding*/ + + this.good_match = 0; + /* Use a faster search when the previous match is longer than this */ + + this.nice_match = 0; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + + /* Didn't use ct_data typedef below to suppress compiler warning */ + + // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + // Use flat array of DOUBLE size, with interleaved fata, + // because JS does not support effective + this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2); + this.dyn_dtree = new utils.Buf16((2 * D_CODES + 1) * 2); + this.bl_tree = new utils.Buf16((2 * BL_CODES + 1) * 2); + zero(this.dyn_ltree); + zero(this.dyn_dtree); + zero(this.bl_tree); + + this.l_desc = null; /* desc. for literal tree */ + this.d_desc = null; /* desc. for distance tree */ + this.bl_desc = null; /* desc. for bit length tree */ + + //ush bl_count[MAX_BITS+1]; + this.bl_count = new utils.Buf16(MAX_BITS + 1); + /* number of codes at each bit length for an optimal tree */ + + //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + this.heap = new utils.Buf16(2 * L_CODES + 1); /* heap used to build the Huffman trees */ + zero(this.heap); + + this.heap_len = 0; /* number of elements in the heap */ + this.heap_max = 0; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; + zero(this.depth); + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + this.l_buf = 0; /* buffer index for literals or lengths */ + + this.lit_bufsize = 0; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + this.last_lit = 0; /* running index in l_buf */ + + this.d_buf = 0; + /* Buffer index for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + this.opt_len = 0; /* bit length of current block with optimal trees */ + this.static_len = 0; /* bit length of current block with static trees */ + this.matches = 0; /* number of string matches in current block */ + this.insert = 0; /* bytes at end of window left to insert */ + + + this.bi_buf = 0; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + this.bi_valid = 0; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + // Used for window memory init. We safely ignore it for JS. That makes + // sense only for pointers and memory check tools. + //this.high_water = 0; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ +} + + +function deflateResetKeep(strm) { + var s; + + if (!strm || !strm.state) { + return err(strm, Z_STREAM_ERROR); + } + + strm.total_in = strm.total_out = 0; + strm.data_type = Z_UNKNOWN; + + s = strm.state; + s.pending = 0; + s.pending_out = 0; + + if (s.wrap < 0) { + s.wrap = -s.wrap; + /* was made negative by deflate(..., Z_FINISH); */ + } + s.status = (s.wrap ? INIT_STATE : BUSY_STATE); + strm.adler = (s.wrap === 2) ? + 0 // crc32(0, Z_NULL, 0) + : + 1; // adler32(0, Z_NULL, 0) + s.last_flush = Z_NO_FLUSH; + trees._tr_init(s); + return Z_OK; +} + + +function deflateReset(strm) { + var ret = deflateResetKeep(strm); + if (ret === Z_OK) { + lm_init(strm.state); + } + return ret; +} + + +function deflateSetHeader(strm, head) { + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; } + strm.state.gzhead = head; + return Z_OK; +} + + +function deflateInit2(strm, level, method, windowBits, memLevel, strategy) { + if (!strm) { // === Z_NULL + return Z_STREAM_ERROR; + } + var wrap = 1; + + if (level === Z_DEFAULT_COMPRESSION) { + level = 6; + } + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } + + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } + + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return err(strm, Z_STREAM_ERROR); + } + + + if (windowBits === 8) { + windowBits = 9; + } + /* until 256-byte window bug fixed */ + + var s = new DeflateState(); + + strm.state = s; + s.strm = strm; + + s.wrap = wrap; + s.gzhead = null; + s.w_bits = windowBits; + s.w_size = 1 << s.w_bits; + s.w_mask = s.w_size - 1; + + s.hash_bits = memLevel + 7; + s.hash_size = 1 << s.hash_bits; + s.hash_mask = s.hash_size - 1; + s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); + + s.window = new utils.Buf8(s.w_size * 2); + s.head = new utils.Buf16(s.hash_size); + s.prev = new utils.Buf16(s.w_size); + + // Don't need mem init magic for JS. + //s.high_water = 0; /* nothing written to s->window yet */ + + s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + s.pending_buf_size = s.lit_bufsize * 4; + + //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + //s->pending_buf = (uchf *) overlay; + s.pending_buf = new utils.Buf8(s.pending_buf_size); + + // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) + //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s.d_buf = 1 * s.lit_bufsize; + + //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + s.l_buf = (1 + 2) * s.lit_bufsize; + + s.level = level; + s.strategy = strategy; + s.method = method; + + return deflateReset(strm); +} + +function deflateInit(strm, level) { + return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); +} + + +function deflate(strm, flush) { + var old_flush, s; + var beg, val; // for gzip header write only + + if (!strm || !strm.state || + flush > Z_BLOCK || flush < 0) { + return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; + } + + s = strm.state; + + if (!strm.output || + (!strm.input && strm.avail_in !== 0) || + (s.status === FINISH_STATE && flush !== Z_FINISH)) { + return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR); + } + + s.strm = strm; /* just in case */ + old_flush = s.last_flush; + s.last_flush = flush; + + /* Write the header */ + if (s.status === INIT_STATE) { + + if (s.wrap === 2) { // GZIP header + strm.adler = 0; //crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (!s.gzhead) { // s->gzhead == Z_NULL + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s.level === 9 ? 2 : + (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s.status = BUSY_STATE; + } + else { + put_byte(s, (s.gzhead.text ? 1 : 0) + + (s.gzhead.hcrc ? 2 : 0) + + (!s.gzhead.extra ? 0 : 4) + + (!s.gzhead.name ? 0 : 8) + + (!s.gzhead.comment ? 0 : 16) + ); + put_byte(s, s.gzhead.time & 0xff); + put_byte(s, (s.gzhead.time >> 8) & 0xff); + put_byte(s, (s.gzhead.time >> 16) & 0xff); + put_byte(s, (s.gzhead.time >> 24) & 0xff); + put_byte(s, s.level === 9 ? 2 : + (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? + 4 : 0)); + put_byte(s, s.gzhead.os & 0xff); + if (s.gzhead.extra && s.gzhead.extra.length) { + put_byte(s, s.gzhead.extra.length & 0xff); + put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); + } + if (s.gzhead.hcrc) { + strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0); + } + s.gzindex = 0; + s.status = EXTRA_STATE; + } + } + else // DEFLATE header + { + var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; + var level_flags = -1; + + if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { + level_flags = 0; + } else if (s.level < 6) { + level_flags = 1; + } else if (s.level === 6) { + level_flags = 2; + } else { + level_flags = 3; + } + header |= (level_flags << 6); + if (s.strstart !== 0) { header |= PRESET_DICT; } + header += 31 - (header % 31); + + s.status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s.strstart !== 0) { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + strm.adler = 1; // adler32(0L, Z_NULL, 0); + } + } + +//#ifdef GZIP + if (s.status === EXTRA_STATE) { + if (s.gzhead.extra/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + + while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + break; + } + } + put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); + s.gzindex++; + } + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (s.gzindex === s.gzhead.extra.length) { + s.gzindex = 0; + s.status = NAME_STATE; + } + } + else { + s.status = NAME_STATE; + } + } + if (s.status === NAME_STATE) { + if (s.gzhead.name/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.name.length) { + val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val !== 0); + + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val === 0) { + s.gzindex = 0; + s.status = COMMENT_STATE; + } + } + else { + s.status = COMMENT_STATE; + } + } + if (s.status === COMMENT_STATE) { + if (s.gzhead.comment/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.comment.length) { + val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val !== 0); + + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val === 0) { + s.status = HCRC_STATE; + } + } + else { + s.status = HCRC_STATE; + } + } + if (s.status === HCRC_STATE) { + if (s.gzhead.hcrc) { + if (s.pending + 2 > s.pending_buf_size) { + flush_pending(strm); + } + if (s.pending + 2 <= s.pending_buf_size) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + strm.adler = 0; //crc32(0L, Z_NULL, 0); + s.status = BUSY_STATE; + } + } + else { + s.status = BUSY_STATE; + } + } +//#endif + + /* Flush as much pending output as possible */ + if (s.pending !== 0) { + flush_pending(strm); + if (strm.avail_out === 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s.last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && + flush !== Z_FINISH) { + return err(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s.status === FINISH_STATE && strm.avail_in !== 0) { + return err(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm.avail_in !== 0 || s.lookahead !== 0 || + (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { + var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : + (s.strategy === Z_RLE ? deflate_rle(s, flush) : + configuration_table[s.level].func(s, flush)); + + if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { + s.status = FINISH_STATE; + } + if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { + if (strm.avail_out === 0) { + s.last_flush = -1; + /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate === BS_BLOCK_DONE) { + if (flush === Z_PARTIAL_FLUSH) { + trees._tr_align(s); + } + else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + + trees._tr_stored_block(s, 0, 0, false); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush === Z_FULL_FLUSH) { + /*** CLEAR_HASH(s); ***/ /* forget history */ + zero(s.head); // Fill with NIL (= 0); + + if (s.lookahead === 0) { + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + } + } + flush_pending(strm); + if (strm.avail_out === 0) { + s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + //Assert(strm->avail_out > 0, "bug2"); + //if (strm.avail_out <= 0) { throw new Error("bug2");} + + if (flush !== Z_FINISH) { return Z_OK; } + if (s.wrap <= 0) { return Z_STREAM_END; } + + /* Write the trailer */ + if (s.wrap === 2) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + put_byte(s, (strm.adler >> 16) & 0xff); + put_byte(s, (strm.adler >> 24) & 0xff); + put_byte(s, strm.total_in & 0xff); + put_byte(s, (strm.total_in >> 8) & 0xff); + put_byte(s, (strm.total_in >> 16) & 0xff); + put_byte(s, (strm.total_in >> 24) & 0xff); + } + else + { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s.wrap > 0) { s.wrap = -s.wrap; } + /* write the trailer only once! */ + return s.pending !== 0 ? Z_OK : Z_STREAM_END; +} + +function deflateEnd(strm) { + var status; + + if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { + return Z_STREAM_ERROR; + } + + status = strm.state.status; + if (status !== INIT_STATE && + status !== EXTRA_STATE && + status !== NAME_STATE && + status !== COMMENT_STATE && + status !== HCRC_STATE && + status !== BUSY_STATE && + status !== FINISH_STATE + ) { + return err(strm, Z_STREAM_ERROR); + } + + strm.state = null; + + return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; +} + + +/* ========================================================================= + * Initializes the compression dictionary from the given byte + * sequence without producing any compressed output. + */ +function deflateSetDictionary(strm, dictionary) { + var dictLength = dictionary.length; + + var s; + var str, n; + var wrap; + var avail; + var next; + var input; + var tmpDict; + + if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { + return Z_STREAM_ERROR; + } + + s = strm.state; + wrap = s.wrap; + + if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { + return Z_STREAM_ERROR; + } + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap === 1) { + /* adler32(strm->adler, dictionary, dictLength); */ + strm.adler = adler32(strm.adler, dictionary, dictLength, 0); + } + + s.wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s.w_size) { + if (wrap === 0) { /* already empty otherwise */ + /*** CLEAR_HASH(s); ***/ + zero(s.head); // Fill with NIL (= 0); + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + /* use the tail */ + // dictionary = dictionary.slice(dictLength - s.w_size); + tmpDict = new utils.Buf8(s.w_size); + utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0); + dictionary = tmpDict; + dictLength = s.w_size; + } + /* insert dictionary into window and hash */ + avail = strm.avail_in; + next = strm.next_in; + input = strm.input; + strm.avail_in = dictLength; + strm.next_in = 0; + strm.input = dictionary; + fill_window(s); + while (s.lookahead >= MIN_MATCH) { + str = s.strstart; + n = s.lookahead - (MIN_MATCH - 1); + do { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + + s.head[s.ins_h] = str; + str++; + } while (--n); + s.strstart = str; + s.lookahead = MIN_MATCH - 1; + fill_window(s); + } + s.strstart += s.lookahead; + s.block_start = s.strstart; + s.insert = s.lookahead; + s.lookahead = 0; + s.match_length = s.prev_length = MIN_MATCH - 1; + s.match_available = 0; + strm.next_in = next; + strm.input = input; + strm.avail_in = avail; + s.wrap = wrap; + return Z_OK; +} + + +exports.deflateInit = deflateInit; +exports.deflateInit2 = deflateInit2; +exports.deflateReset = deflateReset; +exports.deflateResetKeep = deflateResetKeep; +exports.deflateSetHeader = deflateSetHeader; +exports.deflate = deflate; +exports.deflateEnd = deflateEnd; +exports.deflateSetDictionary = deflateSetDictionary; +exports.deflateInfo = 'pako deflate (from Nodeca project)'; + +/* Not implemented +exports.deflateBound = deflateBound; +exports.deflateCopy = deflateCopy; +exports.deflateParams = deflateParams; +exports.deflatePending = deflatePending; +exports.deflatePrime = deflatePrime; +exports.deflateTune = deflateTune; +*/ + +},{"../utils/common":190,"./adler32":191,"./crc32":193,"./messages":198,"./trees":199}],195:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +// See state defs from inflate.js +var BAD = 30; /* got a data error -- remain here until reset */ +var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state.mode === LEN + strm.avail_in >= 6 + strm.avail_out >= 258 + start >= strm.avail_out + state.bits < 8 + + On return, state.mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm.avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm.avail_out >= 258 for each loop to avoid checking for + output space. + */ +module.exports = function inflate_fast(strm, start) { + var state; + var _in; /* local strm.input */ + var last; /* have enough input while in < last */ + var _out; /* local strm.output */ + var beg; /* inflate()'s initial strm.output */ + var end; /* while out < end, enough space available */ +//#ifdef INFLATE_STRICT + var dmax; /* maximum distance from zlib header */ +//#endif + var wsize; /* window size or zero if not using window */ + var whave; /* valid bytes in the window */ + var wnext; /* window write index */ + // Use `s_window` instead `window`, avoid conflict with instrumentation tools + var s_window; /* allocated sliding window, if wsize != 0 */ + var hold; /* local strm.hold */ + var bits; /* local strm.bits */ + var lcode; /* local strm.lencode */ + var dcode; /* local strm.distcode */ + var lmask; /* mask for first level of length codes */ + var dmask; /* mask for first level of distance codes */ + var here; /* retrieved table entry */ + var op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + var len; /* match length, unused bytes */ + var dist; /* match distance */ + var from; /* where to copy match from */ + var from_source; + + + var input, output; // JS specific, because we have no pointers + + /* copy state to local variables */ + state = strm.state; + //here = state.here; + _in = strm.next_in; + input = strm.input; + last = _in + (strm.avail_in - 5); + _out = strm.next_out; + output = strm.output; + beg = _out - (start - strm.avail_out); + end = _out + (strm.avail_out - 257); +//#ifdef INFLATE_STRICT + dmax = state.dmax; +//#endif + wsize = state.wsize; + whave = state.whave; + wnext = state.wnext; + s_window = state.window; + hold = state.hold; + bits = state.bits; + lcode = state.lencode; + dcode = state.distcode; + lmask = (1 << state.lenbits) - 1; + dmask = (1 << state.distbits) - 1; + + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + top: + do { + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + + here = lcode[hold & lmask]; + + dolen: + for (;;) { // Goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + if (op === 0) { /* literal */ + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + output[_out++] = here & 0xffff/*here.val*/; + } + else if (op & 16) { /* length base */ + len = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + len += hold & ((1 << op) - 1); + hold >>>= op; + bits -= op; + } + //Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = dcode[hold & dmask]; + + dodist: + for (;;) { // goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + + if (op & 16) { /* distance base */ + dist = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + } + dist += hold & ((1 << op) - 1); +//#ifdef INFLATE_STRICT + if (dist > dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } +//#endif + hold >>>= op; + bits -= op; + //Tracevv((stderr, "inflate: distance %u\n", dist)); + op = _out - beg; /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } + +// (!) This block is disabled in zlib defaults, +// don't enable it for binary compatibility +//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR +// if (len <= op - whave) { +// do { +// output[_out++] = 0; +// } while (--len); +// continue top; +// } +// len -= op - whave; +// do { +// output[_out++] = 0; +// } while (--op > whave); +// if (op === 0) { +// from = _out - dist; +// do { +// output[_out++] = output[from++]; +// } while (--len); +// continue top; +// } +//#endif + } + from = 0; // window index + from_source = s_window; + if (wnext === 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = 0; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + while (len > 2) { + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + len -= 3; + } + if (len) { + output[_out++] = from_source[from++]; + if (len > 1) { + output[_out++] = from_source[from++]; + } + } + } + else { + from = _out - dist; /* copy direct from output */ + do { /* minimum length is three */ + output[_out++] = output[from++]; + output[_out++] = output[from++]; + output[_out++] = output[from++]; + len -= 3; + } while (len > 2); + if (len) { + output[_out++] = output[from++]; + if (len > 1) { + output[_out++] = output[from++]; + } + } + } + } + else if ((op & 64) === 0) { /* 2nd level distance code */ + here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dodist; + } + else { + strm.msg = 'invalid distance code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } + else if ((op & 64) === 0) { /* 2nd level length code */ + here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dolen; + } + else if (op & 32) { /* end-of-block */ + //Tracevv((stderr, "inflate: end of block\n")); + state.mode = TYPE; + break top; + } + else { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } while (_in < last && _out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + _in -= len; + bits -= len << 3; + hold &= (1 << bits) - 1; + + /* update state and return */ + strm.next_in = _in; + strm.next_out = _out; + strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); + strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); + state.hold = hold; + state.bits = bits; + return; +}; + +},{}],196:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +var utils = require('../utils/common'); +var adler32 = require('./adler32'); +var crc32 = require('./crc32'); +var inflate_fast = require('./inffast'); +var inflate_table = require('./inftrees'); + +var CODES = 0; +var LENS = 1; +var DISTS = 2; + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + + +/* Allowed flush values; see deflate() and inflate() below for details */ +//var Z_NO_FLUSH = 0; +//var Z_PARTIAL_FLUSH = 1; +//var Z_SYNC_FLUSH = 2; +//var Z_FULL_FLUSH = 3; +var Z_FINISH = 4; +var Z_BLOCK = 5; +var Z_TREES = 6; + + +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ +var Z_OK = 0; +var Z_STREAM_END = 1; +var Z_NEED_DICT = 2; +//var Z_ERRNO = -1; +var Z_STREAM_ERROR = -2; +var Z_DATA_ERROR = -3; +var Z_MEM_ERROR = -4; +var Z_BUF_ERROR = -5; +//var Z_VERSION_ERROR = -6; + +/* The deflate compression method */ +var Z_DEFLATED = 8; + + +/* STATES ====================================================================*/ +/* ===========================================================================*/ + + +var HEAD = 1; /* i: waiting for magic header */ +var FLAGS = 2; /* i: waiting for method and flags (gzip) */ +var TIME = 3; /* i: waiting for modification time (gzip) */ +var OS = 4; /* i: waiting for extra flags and operating system (gzip) */ +var EXLEN = 5; /* i: waiting for extra length (gzip) */ +var EXTRA = 6; /* i: waiting for extra bytes (gzip) */ +var NAME = 7; /* i: waiting for end of file name (gzip) */ +var COMMENT = 8; /* i: waiting for end of comment (gzip) */ +var HCRC = 9; /* i: waiting for header crc (gzip) */ +var DICTID = 10; /* i: waiting for dictionary check value */ +var DICT = 11; /* waiting for inflateSetDictionary() call */ +var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ +var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ +var STORED = 14; /* i: waiting for stored size (length and complement) */ +var COPY_ = 15; /* i/o: same as COPY below, but only first time in */ +var COPY = 16; /* i/o: waiting for input or output to copy stored block */ +var TABLE = 17; /* i: waiting for dynamic block table lengths */ +var LENLENS = 18; /* i: waiting for code length code lengths */ +var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ +var LEN_ = 20; /* i: same as LEN below, but only first time in */ +var LEN = 21; /* i: waiting for length/lit/eob code */ +var LENEXT = 22; /* i: waiting for length extra bits */ +var DIST = 23; /* i: waiting for distance code */ +var DISTEXT = 24; /* i: waiting for distance extra bits */ +var MATCH = 25; /* o: waiting for output space to copy string */ +var LIT = 26; /* o: waiting for output space to write literal */ +var CHECK = 27; /* i: waiting for 32-bit check value */ +var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ +var DONE = 29; /* finished check, done -- remain here until reset */ +var BAD = 30; /* got a data error -- remain here until reset */ +var MEM = 31; /* got an inflate() memory error -- remain here until reset */ +var SYNC = 32; /* looking for synchronization bytes to restart inflate() */ + +/* ===========================================================================*/ + + + +var ENOUGH_LENS = 852; +var ENOUGH_DISTS = 592; +//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + +var MAX_WBITS = 15; +/* 32K LZ77 window */ +var DEF_WBITS = MAX_WBITS; + + +function zswap32(q) { + return (((q >>> 24) & 0xff) + + ((q >>> 8) & 0xff00) + + ((q & 0xff00) << 8) + + ((q & 0xff) << 24)); +} + + +function InflateState() { + this.mode = 0; /* current inflate mode */ + this.last = false; /* true if processing last block */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.havedict = false; /* true if dictionary provided */ + this.flags = 0; /* gzip header method and flags (0 if zlib) */ + this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ + this.check = 0; /* protected copy of check value */ + this.total = 0; /* protected copy of output count */ + // TODO: may be {} + this.head = null; /* where to save gzip header information */ + + /* sliding window */ + this.wbits = 0; /* log base 2 of requested window size */ + this.wsize = 0; /* window size or zero if not using window */ + this.whave = 0; /* valid bytes in the window */ + this.wnext = 0; /* window write index */ + this.window = null; /* allocated sliding window, if needed */ + + /* bit accumulator */ + this.hold = 0; /* input bit accumulator */ + this.bits = 0; /* number of bits in "in" */ + + /* for string and stored block copying */ + this.length = 0; /* literal or length of data to copy */ + this.offset = 0; /* distance back to copy string from */ + + /* for table and code decoding */ + this.extra = 0; /* extra bits needed */ + + /* fixed and dynamic code tables */ + this.lencode = null; /* starting table for length/literal codes */ + this.distcode = null; /* starting table for distance codes */ + this.lenbits = 0; /* index bits for lencode */ + this.distbits = 0; /* index bits for distcode */ + + /* dynamic table building */ + this.ncode = 0; /* number of code length code lengths */ + this.nlen = 0; /* number of length code lengths */ + this.ndist = 0; /* number of distance code lengths */ + this.have = 0; /* number of code lengths in lens[] */ + this.next = null; /* next available space in codes[] */ + + this.lens = new utils.Buf16(320); /* temporary storage for code lengths */ + this.work = new utils.Buf16(288); /* work area for code table building */ + + /* + because we don't have pointers in js, we use lencode and distcode directly + as buffers so we don't need codes + */ + //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */ + this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ + this.distdyn = null; /* dynamic table for distance codes (JS specific) */ + this.sane = 0; /* if false, allow invalid distance too far */ + this.back = 0; /* bits back of last unprocessed length/lit */ + this.was = 0; /* initial length of match */ +} + +function inflateResetKeep(strm) { + var state; + + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + strm.total_in = strm.total_out = state.total = 0; + strm.msg = ''; /*Z_NULL*/ + if (state.wrap) { /* to support ill-conceived Java test suite */ + strm.adler = state.wrap & 1; + } + state.mode = HEAD; + state.last = 0; + state.havedict = 0; + state.dmax = 32768; + state.head = null/*Z_NULL*/; + state.hold = 0; + state.bits = 0; + //state.lencode = state.distcode = state.next = state.codes; + state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS); + state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS); + + state.sane = 1; + state.back = -1; + //Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +function inflateReset(strm) { + var state; + + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + state.wsize = 0; + state.whave = 0; + state.wnext = 0; + return inflateResetKeep(strm); + +} + +function inflateReset2(strm, windowBits) { + var wrap; + var state; + + /* get the state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; + if (windowBits < 48) { + windowBits &= 15; + } + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) { + return Z_STREAM_ERROR; + } + if (state.window !== null && state.wbits !== windowBits) { + state.window = null; + } + + /* update state and reset the rest of it */ + state.wrap = wrap; + state.wbits = windowBits; + return inflateReset(strm); +} + +function inflateInit2(strm, windowBits) { + var ret; + var state; + + if (!strm) { return Z_STREAM_ERROR; } + //strm.msg = Z_NULL; /* in case we return an error */ + + state = new InflateState(); + + //if (state === Z_NULL) return Z_MEM_ERROR; + //Tracev((stderr, "inflate: allocated\n")); + strm.state = state; + state.window = null/*Z_NULL*/; + ret = inflateReset2(strm, windowBits); + if (ret !== Z_OK) { + strm.state = null/*Z_NULL*/; + } + return ret; +} + +function inflateInit(strm) { + return inflateInit2(strm, DEF_WBITS); +} + + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +var virgin = true; + +var lenfix, distfix; // We have no pointers in JS, so keep tables separate + +function fixedtables(state) { + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + var sym; + + lenfix = new utils.Buf32(512); + distfix = new utils.Buf32(32); + + /* literal/length table */ + sym = 0; + while (sym < 144) { state.lens[sym++] = 8; } + while (sym < 256) { state.lens[sym++] = 9; } + while (sym < 280) { state.lens[sym++] = 7; } + while (sym < 288) { state.lens[sym++] = 8; } + + inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); + + /* distance table */ + sym = 0; + while (sym < 32) { state.lens[sym++] = 5; } + + inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); + + /* do this just once */ + virgin = false; + } + + state.lencode = lenfix; + state.lenbits = 9; + state.distcode = distfix; + state.distbits = 5; +} + + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +function updatewindow(strm, src, end, copy) { + var dist; + var state = strm.state; + + /* if it hasn't been done already, allocate space for the window */ + if (state.window === null) { + state.wsize = 1 << state.wbits; + state.wnext = 0; + state.whave = 0; + + state.window = new utils.Buf8(state.wsize); + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state.wsize) { + utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0); + state.wnext = 0; + state.whave = state.wsize; + } + else { + dist = state.wsize - state.wnext; + if (dist > copy) { + dist = copy; + } + //zmemcpy(state->window + state->wnext, end - copy, dist); + utils.arraySet(state.window, src, end - copy, dist, state.wnext); + copy -= dist; + if (copy) { + //zmemcpy(state->window, end - copy, copy); + utils.arraySet(state.window, src, end - copy, copy, 0); + state.wnext = copy; + state.whave = state.wsize; + } + else { + state.wnext += dist; + if (state.wnext === state.wsize) { state.wnext = 0; } + if (state.whave < state.wsize) { state.whave += dist; } + } + } + return 0; +} + +function inflate(strm, flush) { + var state; + var input, output; // input/output buffers + var next; /* next input INDEX */ + var put; /* next output INDEX */ + var have, left; /* available input and output */ + var hold; /* bit buffer */ + var bits; /* bits in bit buffer */ + var _in, _out; /* save starting available input and output */ + var copy; /* number of stored or match bytes to copy */ + var from; /* where to copy match bytes from */ + var from_source; + var here = 0; /* current decoding table entry */ + var here_bits, here_op, here_val; // paked "here" denormalized (JS specific) + //var last; /* parent table entry */ + var last_bits, last_op, last_val; // paked "last" denormalized (JS specific) + var len; /* length to copy for repeats, bits to drop */ + var ret; /* return code */ + var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */ + var opts; + + var n; // temporary var for NEED_BITS + + var order = /* permutation of code lengths */ + [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + + + if (!strm || !strm.state || !strm.output || + (!strm.input && strm.avail_in !== 0)) { + return Z_STREAM_ERROR; + } + + state = strm.state; + if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ + + + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + _in = have; + _out = left; + ret = Z_OK; + + inf_leave: // goto emulation + for (;;) { + switch (state.mode) { + case HEAD: + if (state.wrap === 0) { + state.mode = TYPEDO; + break; + } + //=== NEEDBITS(16); + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ + state.check = 0/*crc32(0L, Z_NULL, 0)*/; + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = FLAGS; + break; + } + state.flags = 0; /* expect zlib header */ + if (state.head) { + state.head.done = false; + } + if (!(state.wrap & 1) || /* check if zlib header allowed */ + (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { + strm.msg = 'incorrect header check'; + state.mode = BAD; + break; + } + if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + break; + } + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + len = (hold & 0x0f)/*BITS(4)*/ + 8; + if (state.wbits === 0) { + state.wbits = len; + } + else if (len > state.wbits) { + strm.msg = 'invalid window size'; + state.mode = BAD; + break; + } + state.dmax = 1 << len; + //Tracev((stderr, "inflate: zlib header ok\n")); + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = hold & 0x200 ? DICTID : TYPE; + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + break; + case FLAGS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.flags = hold; + if ((state.flags & 0xff) !== Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + break; + } + if (state.flags & 0xe000) { + strm.msg = 'unknown header flags set'; + state.mode = BAD; + break; + } + if (state.head) { + state.head.text = ((hold >> 8) & 1); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = TIME; + /* falls through */ + case TIME: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.time = hold; + } + if (state.flags & 0x0200) { + //=== CRC4(state.check, hold) + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + hbuf[2] = (hold >>> 16) & 0xff; + hbuf[3] = (hold >>> 24) & 0xff; + state.check = crc32(state.check, hbuf, 4, 0); + //=== + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = OS; + /* falls through */ + case OS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.xflags = (hold & 0xff); + state.head.os = (hold >> 8); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = EXLEN; + /* falls through */ + case EXLEN: + if (state.flags & 0x0400) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length = hold; + if (state.head) { + state.head.extra_len = hold; + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + else if (state.head) { + state.head.extra = null/*Z_NULL*/; + } + state.mode = EXTRA; + /* falls through */ + case EXTRA: + if (state.flags & 0x0400) { + copy = state.length; + if (copy > have) { copy = have; } + if (copy) { + if (state.head) { + len = state.head.extra_len - state.length; + if (!state.head.extra) { + // Use untyped array for more convenient processing later + state.head.extra = new Array(state.head.extra_len); + } + utils.arraySet( + state.head.extra, + input, + next, + // extra field is limited to 65536 bytes + // - no need for additional size check + copy, + /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ + len + ); + //zmemcpy(state.head.extra + len, next, + // len + copy > state.head.extra_max ? + // state.head.extra_max - len : copy); + } + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + state.length -= copy; + } + if (state.length) { break inf_leave; } + } + state.length = 0; + state.mode = NAME; + /* falls through */ + case NAME: + if (state.flags & 0x0800) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + // TODO: 2 or 1 bytes? + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.name_max*/)) { + state.head.name += String.fromCharCode(len); + } + } while (len && copy < have); + + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.name = null; + } + state.length = 0; + state.mode = COMMENT; + /* falls through */ + case COMMENT: + if (state.flags & 0x1000) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.comm_max*/)) { + state.head.comment += String.fromCharCode(len); + } + } while (len && copy < have); + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.comment = null; + } + state.mode = HCRC; + /* falls through */ + case HCRC: + if (state.flags & 0x0200) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.check & 0xffff)) { + strm.msg = 'header crc mismatch'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + if (state.head) { + state.head.hcrc = ((state.flags >> 9) & 1); + state.head.done = true; + } + strm.adler = state.check = 0; + state.mode = TYPE; + break; + case DICTID: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + strm.adler = state.check = zswap32(hold); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = DICT; + /* falls through */ + case DICT: + if (state.havedict === 0) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + return Z_NEED_DICT; + } + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = TYPE; + /* falls through */ + case TYPE: + if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } + /* falls through */ + case TYPEDO: + if (state.last) { + //--- BYTEBITS() ---// + hold >>>= bits & 7; + bits -= bits & 7; + //---// + state.mode = CHECK; + break; + } + //=== NEEDBITS(3); */ + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.last = (hold & 0x01)/*BITS(1)*/; + //--- DROPBITS(1) ---// + hold >>>= 1; + bits -= 1; + //---// + + switch ((hold & 0x03)/*BITS(2)*/) { + case 0: /* stored block */ + //Tracev((stderr, "inflate: stored block%s\n", + // state.last ? " (last)" : "")); + state.mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + //Tracev((stderr, "inflate: fixed codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = LEN_; /* decode codes */ + if (flush === Z_TREES) { + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break inf_leave; + } + break; + case 2: /* dynamic block */ + //Tracev((stderr, "inflate: dynamic codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = TABLE; + break; + case 3: + strm.msg = 'invalid block type'; + state.mode = BAD; + } + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break; + case STORED: + //--- BYTEBITS() ---// /* go to byte boundary */ + hold >>>= bits & 7; + bits -= bits & 7; + //---// + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { + strm.msg = 'invalid stored block lengths'; + state.mode = BAD; + break; + } + state.length = hold & 0xffff; + //Tracev((stderr, "inflate: stored length %u\n", + // state.length)); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = COPY_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case COPY_: + state.mode = COPY; + /* falls through */ + case COPY: + copy = state.length; + if (copy) { + if (copy > have) { copy = have; } + if (copy > left) { copy = left; } + if (copy === 0) { break inf_leave; } + //--- zmemcpy(put, next, copy); --- + utils.arraySet(output, input, next, copy, put); + //---// + have -= copy; + next += copy; + left -= copy; + put += copy; + state.length -= copy; + break; + } + //Tracev((stderr, "inflate: stored end\n")); + state.mode = TYPE; + break; + case TABLE: + //=== NEEDBITS(14); */ + while (bits < 14) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// +//#ifndef PKZIP_BUG_WORKAROUND + if (state.nlen > 286 || state.ndist > 30) { + strm.msg = 'too many length or distance symbols'; + state.mode = BAD; + break; + } +//#endif + //Tracev((stderr, "inflate: table sizes ok\n")); + state.have = 0; + state.mode = LENLENS; + /* falls through */ + case LENLENS: + while (state.have < state.ncode) { + //=== NEEDBITS(3); + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + while (state.have < 19) { + state.lens[order[state.have++]] = 0; + } + // We have separate tables & no pointers. 2 commented lines below not needed. + //state.next = state.codes; + //state.lencode = state.next; + // Switch to use dynamic table + state.lencode = state.lendyn; + state.lenbits = 7; + + opts = { bits: state.lenbits }; + ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); + state.lenbits = opts.bits; + + if (ret) { + strm.msg = 'invalid code lengths set'; + state.mode = BAD; + break; + } + //Tracev((stderr, "inflate: code lengths ok\n")); + state.have = 0; + state.mode = CODELENS; + /* falls through */ + case CODELENS: + while (state.have < state.nlen + state.ndist) { + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_val < 16) { + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.lens[state.have++] = here_val; + } + else { + if (here_val === 16) { + //=== NEEDBITS(here.bits + 2); + n = here_bits + 2; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + if (state.have === 0) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; + } + len = state.lens[state.have - 1]; + copy = 3 + (hold & 0x03);//BITS(2); + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + } + else if (here_val === 17) { + //=== NEEDBITS(here.bits + 3); + n = here_bits + 3; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 3 + (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + else { + //=== NEEDBITS(here.bits + 7); + n = here_bits + 7; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 11 + (hold & 0x7f);//BITS(7); + //--- DROPBITS(7) ---// + hold >>>= 7; + bits -= 7; + //---// + } + if (state.have + copy > state.nlen + state.ndist) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; + } + while (copy--) { + state.lens[state.have++] = len; + } + } + } + + /* handle error breaks in while */ + if (state.mode === BAD) { break; } + + /* check for end-of-block code (better have one) */ + if (state.lens[256] === 0) { + strm.msg = 'invalid code -- missing end-of-block'; + state.mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state.lenbits = 9; + + opts = { bits: state.lenbits }; + ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.lenbits = opts.bits; + // state.lencode = state.next; + + if (ret) { + strm.msg = 'invalid literal/lengths set'; + state.mode = BAD; + break; + } + + state.distbits = 6; + //state.distcode.copy(state.codes); + // Switch to use dynamic table + state.distcode = state.distdyn; + opts = { bits: state.distbits }; + ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.distbits = opts.bits; + // state.distcode = state.next; + + if (ret) { + strm.msg = 'invalid distances set'; + state.mode = BAD; + break; + } + //Tracev((stderr, 'inflate: codes ok\n')); + state.mode = LEN_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case LEN_: + state.mode = LEN; + /* falls through */ + case LEN: + if (have >= 6 && left >= 258) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + inflate_fast(strm, _out); + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + if (state.mode === TYPE) { + state.back = -1; + } + break; + } + state.back = 0; + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if (here_bits <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_op && (here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.lencode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + state.length = here_val; + if (here_op === 0) { + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + state.mode = LIT; + break; + } + if (here_op & 32) { + //Tracevv((stderr, "inflate: end of block\n")); + state.back = -1; + state.mode = TYPE; + break; + } + if (here_op & 64) { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + break; + } + state.extra = here_op & 15; + state.mode = LENEXT; + /* falls through */ + case LENEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //Tracevv((stderr, "inflate: length %u\n", state.length)); + state.was = state.length; + state.mode = DIST; + /* falls through */ + case DIST: + for (;;) { + here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if ((here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.distcode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + if (here_op & 64) { + strm.msg = 'invalid distance code'; + state.mode = BAD; + break; + } + state.offset = here_val; + state.extra = (here_op) & 15; + state.mode = DISTEXT; + /* falls through */ + case DISTEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } +//#ifdef INFLATE_STRICT + if (state.offset > state.dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break; + } +//#endif + //Tracevv((stderr, "inflate: distance %u\n", state.offset)); + state.mode = MATCH; + /* falls through */ + case MATCH: + if (left === 0) { break inf_leave; } + copy = _out - left; + if (state.offset > copy) { /* copy from window */ + copy = state.offset - copy; + if (copy > state.whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break; + } +// (!) This block is disabled in zlib defaults, +// don't enable it for binary compatibility +//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR +// Trace((stderr, "inflate.c too far\n")); +// copy -= state.whave; +// if (copy > state.length) { copy = state.length; } +// if (copy > left) { copy = left; } +// left -= copy; +// state.length -= copy; +// do { +// output[put++] = 0; +// } while (--copy); +// if (state.length === 0) { state.mode = LEN; } +// break; +//#endif + } + if (copy > state.wnext) { + copy -= state.wnext; + from = state.wsize - copy; + } + else { + from = state.wnext - copy; + } + if (copy > state.length) { copy = state.length; } + from_source = state.window; + } + else { /* copy from output */ + from_source = output; + from = put - state.offset; + copy = state.length; + } + if (copy > left) { copy = left; } + left -= copy; + state.length -= copy; + do { + output[put++] = from_source[from++]; + } while (--copy); + if (state.length === 0) { state.mode = LEN; } + break; + case LIT: + if (left === 0) { break inf_leave; } + output[put++] = state.length; + left--; + state.mode = LEN; + break; + case CHECK: + if (state.wrap) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + // Use '|' instead of '+' to make sure that result is signed + hold |= input[next++] << bits; + bits += 8; + } + //===// + _out -= left; + strm.total_out += _out; + state.total += _out; + if (_out) { + strm.adler = state.check = + /*UPDATE(state.check, put - _out, _out);*/ + (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out)); + + } + _out = left; + // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too + if ((state.flags ? hold : zswap32(hold)) !== state.check) { + strm.msg = 'incorrect data check'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: check matches trailer\n")); + } + state.mode = LENGTH; + /* falls through */ + case LENGTH: + if (state.wrap && state.flags) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.total & 0xffffffff)) { + strm.msg = 'incorrect length check'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: length matches trailer\n")); + } + state.mode = DONE; + /* falls through */ + case DONE: + ret = Z_STREAM_END; + break inf_leave; + case BAD: + ret = Z_DATA_ERROR; + break inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + /* falls through */ + default: + return Z_STREAM_ERROR; + } + } + + // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + + if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && + (state.mode < CHECK || flush !== Z_FINISH))) { + if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { + state.mode = MEM; + return Z_MEM_ERROR; + } + } + _in -= strm.avail_in; + _out -= strm.avail_out; + strm.total_in += _in; + strm.total_out += _out; + state.total += _out; + if (state.wrap && _out) { + strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ + (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out)); + } + strm.data_type = state.bits + (state.last ? 64 : 0) + + (state.mode === TYPE ? 128 : 0) + + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); + if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { + ret = Z_BUF_ERROR; + } + return ret; +} + +function inflateEnd(strm) { + + if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { + return Z_STREAM_ERROR; + } + + var state = strm.state; + if (state.window) { + state.window = null; + } + strm.state = null; + return Z_OK; +} + +function inflateGetHeader(strm, head) { + var state; + + /* check state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; } + + /* save header structure */ + state.head = head; + head.done = false; + return Z_OK; +} + +function inflateSetDictionary(strm, dictionary) { + var dictLength = dictionary.length; + + var state; + var dictid; + var ret; + + /* check state */ + if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; } + state = strm.state; + + if (state.wrap !== 0 && state.mode !== DICT) { + return Z_STREAM_ERROR; + } + + /* check for correct dictionary identifier */ + if (state.mode === DICT) { + dictid = 1; /* adler32(0, null, 0)*/ + /* dictid = adler32(dictid, dictionary, dictLength); */ + dictid = adler32(dictid, dictionary, dictLength, 0); + if (dictid !== state.check) { + return Z_DATA_ERROR; + } + } + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary, dictLength, dictLength); + if (ret) { + state.mode = MEM; + return Z_MEM_ERROR; + } + state.havedict = 1; + // Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +exports.inflateReset = inflateReset; +exports.inflateReset2 = inflateReset2; +exports.inflateResetKeep = inflateResetKeep; +exports.inflateInit = inflateInit; +exports.inflateInit2 = inflateInit2; +exports.inflate = inflate; +exports.inflateEnd = inflateEnd; +exports.inflateGetHeader = inflateGetHeader; +exports.inflateSetDictionary = inflateSetDictionary; +exports.inflateInfo = 'pako inflate (from Nodeca project)'; + +/* Not implemented +exports.inflateCopy = inflateCopy; +exports.inflateGetDictionary = inflateGetDictionary; +exports.inflateMark = inflateMark; +exports.inflatePrime = inflatePrime; +exports.inflateSync = inflateSync; +exports.inflateSyncPoint = inflateSyncPoint; +exports.inflateUndermine = inflateUndermine; +*/ + +},{"../utils/common":190,"./adler32":191,"./crc32":193,"./inffast":195,"./inftrees":197}],197:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +var utils = require('../utils/common'); + +var MAXBITS = 15; +var ENOUGH_LENS = 852; +var ENOUGH_DISTS = 592; +//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + +var CODES = 0; +var LENS = 1; +var DISTS = 2; + +var lbase = [ /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +]; + +var lext = [ /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 +]; + +var dbase = [ /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0 +]; + +var dext = [ /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64 +]; + +module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) +{ + var bits = opts.bits; + //here = opts.here; /* table entry for duplication */ + + var len = 0; /* a code's length in bits */ + var sym = 0; /* index of code symbols */ + var min = 0, max = 0; /* minimum and maximum code lengths */ + var root = 0; /* number of index bits for root table */ + var curr = 0; /* number of index bits for current table */ + var drop = 0; /* code bits to drop for sub-table */ + var left = 0; /* number of prefix codes available */ + var used = 0; /* code entries in table used */ + var huff = 0; /* Huffman code */ + var incr; /* for incrementing code, index */ + var fill; /* index for replicating entries */ + var low; /* low bits for current root entry */ + var mask; /* mask for low root bits */ + var next; /* next available space in table */ + var base = null; /* base value table to use */ + var base_index = 0; +// var shoextra; /* extra bits table to use */ + var end; /* use base and extra for symbol > end */ + var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ + var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ + var extra = null; + var extra_index = 0; + + var here_bits, here_op, here_val; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) { + count[len] = 0; + } + for (sym = 0; sym < codes; sym++) { + count[lens[lens_index + sym]]++; + } + + /* bound code lengths, force root to be within code lengths */ + root = bits; + for (max = MAXBITS; max >= 1; max--) { + if (count[max] !== 0) { break; } + } + if (root > max) { + root = max; + } + if (max === 0) { /* no symbols to code at all */ + //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ + //table.bits[opts.table_index] = 1; //here.bits = (var char)1; + //table.val[opts.table_index++] = 0; //here.val = (var short)0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + + //table.op[opts.table_index] = 64; + //table.bits[opts.table_index] = 1; + //table.val[opts.table_index++] = 0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + opts.bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) { + if (count[min] !== 0) { break; } + } + if (root < min) { + root = min; + } + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return -1; + } /* over-subscribed */ + } + if (left > 0 && (type === CODES || max !== 1)) { + return -1; /* incomplete set */ + } + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) { + offs[len + 1] = offs[len] + count[len]; + } + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) { + if (lens[lens_index + sym] !== 0) { + work[offs[lens[lens_index + sym]]++] = sym; + } + } + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + // poor man optimization - use if-else instead of switch, + // to avoid deopts in old v8 + if (type === CODES) { + base = extra = work; /* dummy value--not used */ + end = 19; + + } else if (type === LENS) { + base = lbase; + base_index -= 257; + extra = lext; + extra_index -= 257; + end = 256; + + } else { /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize opts for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = table_index; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = -1; /* trigger new sub-table when len > root */ + used = 1 << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here_bits = len - drop; + if (work[sym] < end) { + here_op = 0; + here_val = work[sym]; + } + else if (work[sym] > end) { + here_op = extra[extra_index + work[sym]]; + here_val = base[base_index + work[sym]]; + } + else { + here_op = 32 + 64; /* end of block */ + here_val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1 << (len - drop); + fill = 1 << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; + } while (fill !== 0); + + /* backwards increment the len-bit code huff */ + incr = 1 << (len - 1); + while (huff & incr) { + incr >>= 1; + } + if (incr !== 0) { + huff &= incr - 1; + huff += incr; + } else { + huff = 0; + } + + /* go to next symbol, update count, len */ + sym++; + if (--count[len] === 0) { + if (len === max) { break; } + len = lens[lens_index + work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) !== low) { + /* if first time, transition to sub-tables */ + if (drop === 0) { + drop = root; + } + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = 1 << curr; + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) { break; } + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1 << curr; + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + /* point entry in root table to sub-table */ + low = huff & mask; + /*table.op[low] = curr; + table.bits[low] = root; + table.val[low] = next - opts.table_index;*/ + table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff !== 0) { + //table.op[next + huff] = 64; /* invalid code marker */ + //table.bits[next + huff] = len - drop; + //table.val[next + huff] = 0; + table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; + } + + /* set return parameters */ + //opts.table_index += used; + opts.bits = root; + return 0; +}; + +},{"../utils/common":190}],198:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +module.exports = { + 2: 'need dictionary', /* Z_NEED_DICT 2 */ + 1: 'stream end', /* Z_STREAM_END 1 */ + 0: '', /* Z_OK 0 */ + '-1': 'file error', /* Z_ERRNO (-1) */ + '-2': 'stream error', /* Z_STREAM_ERROR (-2) */ + '-3': 'data error', /* Z_DATA_ERROR (-3) */ + '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */ + '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ + '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ +}; + +},{}],199:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +var utils = require('../utils/common'); + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + + +//var Z_FILTERED = 1; +//var Z_HUFFMAN_ONLY = 2; +//var Z_RLE = 3; +var Z_FIXED = 4; +//var Z_DEFAULT_STRATEGY = 0; + +/* Possible values of the data_type field (though see inflate()) */ +var Z_BINARY = 0; +var Z_TEXT = 1; +//var Z_ASCII = 1; // = Z_TEXT +var Z_UNKNOWN = 2; + +/*============================================================================*/ + + +function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } } + +// From zutil.h + +var STORED_BLOCK = 0; +var STATIC_TREES = 1; +var DYN_TREES = 2; +/* The three kinds of block type */ + +var MIN_MATCH = 3; +var MAX_MATCH = 258; +/* The minimum and maximum match lengths */ + +// From deflate.h +/* =========================================================================== + * Internal compression state. + */ + +var LENGTH_CODES = 29; +/* number of length codes, not counting the special END_BLOCK code */ + +var LITERALS = 256; +/* number of literal bytes 0..255 */ + +var L_CODES = LITERALS + 1 + LENGTH_CODES; +/* number of Literal or Length codes, including the END_BLOCK code */ + +var D_CODES = 30; +/* number of distance codes */ + +var BL_CODES = 19; +/* number of codes used to transfer the bit lengths */ + +var HEAP_SIZE = 2 * L_CODES + 1; +/* maximum heap size */ + +var MAX_BITS = 15; +/* All codes must not exceed MAX_BITS bits */ + +var Buf_size = 16; +/* size of bit buffer in bi_buf */ + + +/* =========================================================================== + * Constants + */ + +var MAX_BL_BITS = 7; +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +var END_BLOCK = 256; +/* end of block literal code */ + +var REP_3_6 = 16; +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +var REPZ_3_10 = 17; +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +var REPZ_11_138 = 18; +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* eslint-disable comma-spacing,array-bracket-spacing */ +var extra_lbits = /* extra bits for each length code */ + [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]; + +var extra_dbits = /* extra bits for each distance code */ + [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]; + +var extra_blbits = /* extra bits for each bit length code */ + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]; + +var bl_order = + [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]; +/* eslint-enable comma-spacing,array-bracket-spacing */ + +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +// We pre-fill arrays with 0 to avoid uninitialized gaps + +var DIST_CODE_LEN = 512; /* see definition of array dist_code below */ + +// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 +var static_ltree = new Array((L_CODES + 2) * 2); +zero(static_ltree); +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +var static_dtree = new Array(D_CODES * 2); +zero(static_dtree); +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +var _dist_code = new Array(DIST_CODE_LEN); +zero(_dist_code); +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +var _length_code = new Array(MAX_MATCH - MIN_MATCH + 1); +zero(_length_code); +/* length code for each normalized match length (0 == MIN_MATCH) */ + +var base_length = new Array(LENGTH_CODES); +zero(base_length); +/* First normalized length for each code (0 = MIN_MATCH) */ + +var base_dist = new Array(D_CODES); +zero(base_dist); +/* First normalized distance for each code (0 = distance of 1) */ + + +function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { + + this.static_tree = static_tree; /* static tree or NULL */ + this.extra_bits = extra_bits; /* extra bits for each code or NULL */ + this.extra_base = extra_base; /* base index for extra_bits */ + this.elems = elems; /* max number of elements in the tree */ + this.max_length = max_length; /* max bit length for the codes */ + + // show if `static_tree` has data or dummy - needed for monomorphic objects + this.has_stree = static_tree && static_tree.length; +} + + +var static_l_desc; +var static_d_desc; +var static_bl_desc; + + +function TreeDesc(dyn_tree, stat_desc) { + this.dyn_tree = dyn_tree; /* the dynamic tree */ + this.max_code = 0; /* largest code with non zero frequency */ + this.stat_desc = stat_desc; /* the corresponding static tree */ +} + + + +function d_code(dist) { + return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; +} + + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +function put_short(s, w) { +// put_byte(s, (uch)((w) & 0xff)); +// put_byte(s, (uch)((ush)(w) >> 8)); + s.pending_buf[s.pending++] = (w) & 0xff; + s.pending_buf[s.pending++] = (w >>> 8) & 0xff; +} + + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +function send_bits(s, value, length) { + if (s.bi_valid > (Buf_size - length)) { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + put_short(s, s.bi_buf); + s.bi_buf = value >> (Buf_size - s.bi_valid); + s.bi_valid += length - Buf_size; + } else { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + s.bi_valid += length; + } +} + + +function send_code(s, c, tree) { + send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/); +} + + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +function bi_reverse(code, len) { + var res = 0; + do { + res |= code & 1; + code >>>= 1; + res <<= 1; + } while (--len > 0); + return res >>> 1; +} + + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +function bi_flush(s) { + if (s.bi_valid === 16) { + put_short(s, s.bi_buf); + s.bi_buf = 0; + s.bi_valid = 0; + + } else if (s.bi_valid >= 8) { + s.pending_buf[s.pending++] = s.bi_buf & 0xff; + s.bi_buf >>= 8; + s.bi_valid -= 8; + } +} + + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +function gen_bitlen(s, desc) +// deflate_state *s; +// tree_desc *desc; /* the tree descriptor */ +{ + var tree = desc.dyn_tree; + var max_code = desc.max_code; + var stree = desc.stat_desc.static_tree; + var has_stree = desc.stat_desc.has_stree; + var extra = desc.stat_desc.extra_bits; + var base = desc.stat_desc.extra_base; + var max_length = desc.stat_desc.max_length; + var h; /* heap index */ + var n, m; /* iterate over the tree elements */ + var bits; /* bit length */ + var xbits; /* extra bits */ + var f; /* frequency */ + var overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) { + s.bl_count[bits] = 0; + } + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n * 2 + 1]/*.Len*/ = bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) { continue; } /* not a leaf node */ + + s.bl_count[bits]++; + xbits = 0; + if (n >= base) { + xbits = extra[n - base]; + } + f = tree[n * 2]/*.Freq*/; + s.opt_len += f * (bits + xbits); + if (has_stree) { + s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits); + } + } + if (overflow === 0) { return; } + + // Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length - 1; + while (s.bl_count[bits] === 0) { bits--; } + s.bl_count[bits]--; /* move one leaf down the tree */ + s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ + s.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits !== 0; bits--) { + n = s.bl_count[bits]; + while (n !== 0) { + m = s.heap[--h]; + if (m > max_code) { continue; } + if (tree[m * 2 + 1]/*.Len*/ !== bits) { + // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/; + tree[m * 2 + 1]/*.Len*/ = bits; + } + n--; + } + } +} + + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +function gen_codes(tree, max_code, bl_count) +// ct_data *tree; /* the tree to decorate */ +// int max_code; /* largest code with non zero frequency */ +// ushf *bl_count; /* number of codes at each bit length */ +{ + var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */ + var code = 0; /* running code value */ + var bits; /* bit index */ + var n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits - 1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + //Assert (code + bl_count[MAX_BITS]-1 == (1< length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES - 1; code++) { + base_length[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + _length_code[length++] = code; + } + } + //Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + _length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + _dist_code[dist++] = code; + } + } + //Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for (; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + _dist_code[256 + dist++] = code; + } + } + //Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) { + bl_count[bits] = 0; + } + + n = 0; + while (n <= 143) { + static_ltree[n * 2 + 1]/*.Len*/ = 8; + n++; + bl_count[8]++; + } + while (n <= 255) { + static_ltree[n * 2 + 1]/*.Len*/ = 9; + n++; + bl_count[9]++; + } + while (n <= 279) { + static_ltree[n * 2 + 1]/*.Len*/ = 7; + n++; + bl_count[7]++; + } + while (n <= 287) { + static_ltree[n * 2 + 1]/*.Len*/ = 8; + n++; + bl_count[8]++; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(static_ltree, L_CODES + 1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n * 2 + 1]/*.Len*/ = 5; + static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); + } + + // Now data ready and we can init static trees + static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); + static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + //static_init_done = true; +} + + +/* =========================================================================== + * Initialize a new block. + */ +function init_block(s) { + var n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } + + s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; + s.opt_len = s.static_len = 0; + s.last_lit = s.matches = 0; +} + + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +function bi_windup(s) +{ + if (s.bi_valid > 8) { + put_short(s, s.bi_buf); + } else if (s.bi_valid > 0) { + //put_byte(s, (Byte)s->bi_buf); + s.pending_buf[s.pending++] = s.bi_buf; + } + s.bi_buf = 0; + s.bi_valid = 0; +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +function copy_block(s, buf, len, header) +//DeflateState *s; +//charf *buf; /* the input data */ +//unsigned len; /* its length */ +//int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, len); + put_short(s, ~len); + } +// while (len--) { +// put_byte(s, *buf++); +// } + utils.arraySet(s.pending_buf, s.window, buf, len, s.pending); + s.pending += len; +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +function smaller(tree, n, m, depth) { + var _n2 = n * 2; + var _m2 = m * 2; + return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || + (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); +} + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +function pqdownheap(s, tree, k) +// deflate_state *s; +// ct_data *tree; /* the tree to restore */ +// int k; /* node to move down */ +{ + var v = s.heap[k]; + var j = k << 1; /* left son of k */ + while (j <= s.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s.heap_len && + smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s.heap[j], s.depth)) { break; } + + /* Exchange v with the smallest son */ + s.heap[k] = s.heap[j]; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s.heap[k] = v; +} + + +// inlined manually +// var SMALLEST = 1; + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +function compress_block(s, ltree, dtree) +// deflate_state *s; +// const ct_data *ltree; /* literal tree */ +// const ct_data *dtree; /* distance tree */ +{ + var dist; /* distance of matched string */ + var lc; /* match length or unmatched char (if dist == 0) */ + var lx = 0; /* running index in l_buf */ + var code; /* the code to send */ + var extra; /* number of extra bits to send */ + + if (s.last_lit !== 0) { + do { + dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]); + lc = s.pending_buf[s.l_buf + lx]; + lx++; + + if (dist === 0) { + send_code(s, lc, ltree); /* send a literal byte */ + //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code + LITERALS + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra !== 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + //Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra !== 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + // "pendingBuf overflow"); + + } while (lx < s.last_lit); + } + + send_code(s, END_BLOCK, ltree); +} + + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +function build_tree(s, desc) +// deflate_state *s; +// tree_desc *desc; /* the tree descriptor */ +{ + var tree = desc.dyn_tree; + var stree = desc.stat_desc.static_tree; + var has_stree = desc.stat_desc.has_stree; + var elems = desc.stat_desc.elems; + var n, m; /* iterate over heap elements */ + var max_code = -1; /* largest code with non zero frequency */ + var node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n * 2]/*.Freq*/ !== 0) { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + + } else { + tree[n * 2 + 1]/*.Len*/ = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s.heap_len < 2) { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2]/*.Freq*/ = 1; + s.depth[node] = 0; + s.opt_len--; + + if (has_stree) { + s.static_len -= stree[node * 2 + 1]/*.Len*/; + } + /* node is 0 or 1 so it does not have extra bits */ + } + desc.max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + //pqremove(s, tree, n); /* n = node of least frequency */ + /*** pqremove ***/ + n = s.heap[1/*SMALLEST*/]; + s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; + pqdownheap(s, tree, 1/*SMALLEST*/); + /***/ + + m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ + + s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ + s.heap[--s.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; + s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; + tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node; + + /* and insert the new node in the heap */ + s.heap[1/*SMALLEST*/] = node++; + pqdownheap(s, tree, 1/*SMALLEST*/); + + } while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes(tree, max_code, s.bl_count); +} + + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +function scan_tree(s, tree, max_code) +// deflate_state *s; +// ct_data *tree; /* the tree to be scanned */ +// int max_code; /* and its largest code of non zero frequency */ +{ + var n; /* iterates over all tree elements */ + var prevlen = -1; /* last emitted length */ + var curlen; /* length of current code */ + + var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ + + var count = 0; /* repeat count of the current code */ + var max_count = 7; /* max repeat count */ + var min_count = 4; /* min repeat count */ + + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } + tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; + + if (++count < max_count && curlen === nextlen) { + continue; + + } else if (count < min_count) { + s.bl_tree[curlen * 2]/*.Freq*/ += count; + + } else if (curlen !== 0) { + + if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } + s.bl_tree[REP_3_6 * 2]/*.Freq*/++; + + } else if (count <= 10) { + s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++; + + } else { + s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++; + } + + count = 0; + prevlen = curlen; + + if (nextlen === 0) { + max_count = 138; + min_count = 3; + + } else if (curlen === nextlen) { + max_count = 6; + min_count = 3; + + } else { + max_count = 7; + min_count = 4; + } + } +} + + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +function send_tree(s, tree, max_code) +// deflate_state *s; +// ct_data *tree; /* the tree to be scanned */ +// int max_code; /* and its largest code of non zero frequency */ +{ + var n; /* iterates over all tree elements */ + var prevlen = -1; /* last emitted length */ + var curlen; /* length of current code */ + + var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ + + var count = 0; /* repeat count of the current code */ + var max_count = 7; /* max repeat count */ + var min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; + + if (++count < max_count && curlen === nextlen) { + continue; + + } else if (count < min_count) { + do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); + + } else if (curlen !== 0) { + if (curlen !== prevlen) { + send_code(s, curlen, s.bl_tree); + count--; + } + //Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s.bl_tree); + send_bits(s, count - 3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s.bl_tree); + send_bits(s, count - 3, 3); + + } else { + send_code(s, REPZ_11_138, s.bl_tree); + send_bits(s, count - 11, 7); + } + + count = 0; + prevlen = curlen; + if (nextlen === 0) { + max_count = 138; + min_count = 3; + + } else if (curlen === nextlen) { + max_count = 6; + min_count = 3; + + } else { + max_count = 7; + min_count = 4; + } + } +} + + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +function build_bl_tree(s) { + var max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, s.dyn_ltree, s.l_desc.max_code); + scan_tree(s, s.dyn_dtree, s.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, s.bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { + break; + } + } + /* Update opt_len to include the bit length tree and counts */ + s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + // s->opt_len, s->static_len)); + + return max_blindex; +} + + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +function send_all_trees(s, lcodes, dcodes, blcodes) +// deflate_state *s; +// int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + var rank; /* index in bl_order */ + + //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + // "too many codes"); + //Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes - 1, 5); + send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3); + } + //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ + //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ + //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +function detect_data_type(s) { + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + var black_mask = 0xf3ffc07f; + var n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>>= 1) { + if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { + return Z_BINARY; + } + } + + /* Check for textual ("white-listed") bytes. */ + if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || + s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { + return Z_TEXT; + } + for (n = 32; n < LITERALS; n++) { + if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { + return Z_TEXT; + } + } + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + + +var static_init_done = false; + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +function _tr_init(s) +{ + + if (!static_init_done) { + tr_static_init(); + static_init_done = true; + } + + s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); + s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); + s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); + + s.bi_buf = 0; + s.bi_valid = 0; + + /* Initialize the first block of the first file: */ + init_block(s); +} + + +/* =========================================================================== + * Send a stored block + */ +function _tr_stored_block(s, buf, stored_len, last) +//DeflateState *s; +//charf *buf; /* input block */ +//ulg stored_len; /* length of input block */ +//int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ + copy_block(s, buf, stored_len, true); /* with header */ +} + + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ +function _tr_align(s) { + send_bits(s, STATIC_TREES << 1, 3); + send_code(s, END_BLOCK, static_ltree); + bi_flush(s); +} + + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +function _tr_flush_block(s, buf, stored_len, last) +//DeflateState *s; +//charf *buf; /* input block, or NULL if too old */ +//ulg stored_len; /* length of input block */ +//int last; /* one if this is the last block for a file */ +{ + var opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + var max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s.level > 0) { + + /* Check if the file is binary or text */ + if (s.strm.data_type === Z_UNKNOWN) { + s.strm.data_type = detect_data_type(s); + } + + /* Construct the literal and distance trees */ + build_tree(s, s.l_desc); + // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + + build_tree(s, s.d_desc); + // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s.opt_len + 3 + 7) >>> 3; + static_lenb = (s.static_len + 3 + 7) >>> 3; + + // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + // s->last_lit)); + + if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } + + } else { + // Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + + if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { + /* 4: two words for the lengths */ + + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + + } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { + + send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); + compress_block(s, static_ltree, static_dtree); + + } else { + send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); + send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); + compress_block(s, s.dyn_ltree, s.dyn_dtree); + } + // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); + } + // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + // s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +function _tr_tally(s, dist, lc) +// deflate_state *s; +// unsigned dist; /* distance of matched string */ +// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + //var out_length, in_length, dcode; + + s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; + s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; + + s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; + s.last_lit++; + + if (dist === 0) { + /* lc is the unmatched char */ + s.dyn_ltree[lc * 2]/*.Freq*/++; + } else { + s.matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + //Assert((ush)dist < (ush)MAX_DIST(s) && + // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++; + s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; + } + +// (!) This block is disabled in zlib defaults, +// don't enable it for binary compatibility + +//#ifdef TRUNCATE_BLOCK +// /* Try to guess if it is profitable to stop the current block here */ +// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { +// /* Compute an upper bound for the compressed length */ +// out_length = s.last_lit*8; +// in_length = s.strstart - s.block_start; +// +// for (dcode = 0; dcode < D_CODES; dcode++) { +// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); +// } +// out_length >>>= 3; +// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", +// // s->last_lit, in_length, out_length, +// // 100L - out_length*100L/in_length)); +// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { +// return true; +// } +// } +//#endif + + return (s.last_lit === s.lit_bufsize - 1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +exports._tr_init = _tr_init; +exports._tr_stored_block = _tr_stored_block; +exports._tr_flush_block = _tr_flush_block; +exports._tr_tally = _tr_tally; +exports._tr_align = _tr_align; + +},{"../utils/common":190}],200:[function(require,module,exports){ +'use strict'; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +function ZStream() { + /* next input byte */ + this.input = null; // JS specific, because we have no pointers + this.next_in = 0; + /* number of bytes available at input */ + this.avail_in = 0; + /* total number of input bytes read so far */ + this.total_in = 0; + /* next output byte should be put there */ + this.output = null; // JS specific, because we have no pointers + this.next_out = 0; + /* remaining free space at output */ + this.avail_out = 0; + /* total number of bytes output so far */ + this.total_out = 0; + /* last error message, NULL if no error */ + this.msg = ''/*Z_NULL*/; + /* not visible by applications */ + this.state = null; + /* best guess about the data type: binary or text */ + this.data_type = 2/*Z_UNKNOWN*/; + /* adler32 value of the uncompressed data */ + this.adler = 0; +} + +module.exports = ZStream; + +},{}],201:[function(require,module,exports){ +(function (process){ +'use strict'; + +if (!process.version || + process.version.indexOf('v0.') === 0 || + process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { + module.exports = nextTick; +} else { + module.exports = process.nextTick; +} + +function nextTick(fn, arg1, arg2, arg3) { + if (typeof fn !== 'function') { + throw new TypeError('"callback" argument must be a function'); + } + var len = arguments.length; + var args, i; + switch (len) { + case 0: + case 1: + return process.nextTick(fn); + case 2: + return process.nextTick(function afterTickOne() { + fn.call(null, arg1); + }); + case 3: + return process.nextTick(function afterTickTwo() { + fn.call(null, arg1, arg2); + }); + case 4: + return process.nextTick(function afterTickThree() { + fn.call(null, arg1, arg2, arg3); + }); + default: + args = new Array(len - 1); + i = 0; + while (i < args.length) { + args[i++] = arguments[i]; + } + return process.nextTick(function afterTick() { + fn.apply(null, args); + }); + } +} + +}).call(this,require('_process')) +},{"_process":202}],202:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],203:[function(require,module,exports){ +(function (global){ +/*! https://mths.be/punycode v1.4.1 by @mathias */ +;(function(root) { + + /** Detect free variables */ + var freeExports = typeof exports == 'object' && exports && + !exports.nodeType && exports; + var freeModule = typeof module == 'object' && module && + !module.nodeType && module; + var freeGlobal = typeof global == 'object' && global; + if ( + freeGlobal.global === freeGlobal || + freeGlobal.window === freeGlobal || + freeGlobal.self === freeGlobal + ) { + root = freeGlobal; + } + + /** + * The `punycode` object. + * @name punycode + * @type Object + */ + var punycode, + + /** Highest positive signed 32-bit float value */ + maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 + + /** Bootstring parameters */ + base = 36, + tMin = 1, + tMax = 26, + skew = 38, + damp = 700, + initialBias = 72, + initialN = 128, // 0x80 + delimiter = '-', // '\x2D' + + /** Regular expressions */ + regexPunycode = /^xn--/, + regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars + regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators + + /** Error messages */ + errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' + }, + + /** Convenience shortcuts */ + baseMinusTMin = base - tMin, + floor = Math.floor, + stringFromCharCode = String.fromCharCode, + + /** Temporary variable */ + key; + + /*--------------------------------------------------------------------------*/ + + /** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ + function error(type) { + throw new RangeError(errors[type]); + } + + /** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ + function map(array, fn) { + var length = array.length; + var result = []; + while (length--) { + result[length] = fn(array[length]); + } + return result; + } + + /** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ + function mapDomain(string, fn) { + var parts = string.split('@'); + var result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; + } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + var labels = string.split('.'); + var encoded = map(labels, fn).join('.'); + return result + encoded; + } + + /** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ + function ucs2decode(string) { + var output = [], + counter = 0, + length = string.length, + value, + extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + /** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ + function ucs2encode(array) { + return map(array, function(value) { + var output = ''; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + return output; + }).join(''); + } + + /** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ + function basicToDigit(codePoint) { + if (codePoint - 48 < 10) { + return codePoint - 22; + } + if (codePoint - 65 < 26) { + return codePoint - 65; + } + if (codePoint - 97 < 26) { + return codePoint - 97; + } + return base; + } + + /** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ + function digitToBasic(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + } + + /** + * Bias adaptation function as per section 3.4 of RFC 3492. + * https://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ + function adapt(delta, numPoints, firstTime) { + var k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + } + + /** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ + function decode(input) { + // Don't use UCS-2 + var output = [], + inputLength = input.length, + out, + i = 0, + n = initialN, + bias = initialBias, + basic, + j, + index, + oldi, + w, + k, + digit, + t, + /** Cached calculation results */ + baseMinusT; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + for (oldi = i, w = 1, k = base; /* no condition */; k += base) { + + if (index >= inputLength) { + error('invalid-input'); + } + + digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } + + i += digit * w; + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (digit < t) { + break; + } + + baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error('overflow'); + } + + w *= baseMinusT; + + } + + out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output + output.splice(i++, 0, n); + + } + + return ucs2encode(output); + } + + /** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ + function encode(input) { + var n, + delta, + handledCPCount, + basicLength, + bias, + j, + m, + q, + k, + t, + currentValue, + output = [], + /** `inputLength` will hold the number of code points in `input`. */ + inputLength, + /** Cached calculation results */ + handledCPCountPlusOne, + baseMinusT, + qMinusT; + + // Convert the input in UCS-2 to Unicode + input = ucs2decode(input); + + // Cache the length + inputLength = input.length; + + // Initialize the state + n = initialN; + delta = 0; + bias = initialBias; + + // Handle the basic code points + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); + } + } + + handledCPCount = basicLength = output.length; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string - if it is not empty - with a delimiter + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + + // All non-basic code points < n have been handled already. Find the next + // larger one: + for (m = maxInt, j = 0; j < inputLength; ++j) { + currentValue = input[j]; + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow + handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (j = 0; j < inputLength; ++j) { + currentValue = input[j]; + + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } + + if (currentValue == n) { + // Represent delta as a generalized variable-length integer + for (q = delta, k = base; /* no condition */; k += base) { + t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + qMinusT = q - t; + baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); + } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + + } + return output.join(''); + } + + /** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ + function toUnicode(input) { + return mapDomain(input, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); + } + + /** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ + function toASCII(input) { + return mapDomain(input, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); + } + + /*--------------------------------------------------------------------------*/ + + /** Define the public API */ + punycode = { + /** + * A string representing the current Punycode.js version number. + * @memberOf punycode + * @type String + */ + 'version': '1.4.1', + /** + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see + * @memberOf punycode + * @type Object + */ + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode + }; + + /** Expose `punycode` */ + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define('punycode', function() { + return punycode; + }); + } else if (freeExports && freeModule) { + if (module.exports == freeExports) { + // in Node.js, io.js, or RingoJS v0.8.0+ + freeModule.exports = punycode; + } else { + // in Narwhal or RingoJS v0.7.0- + for (key in punycode) { + punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); + } + } + } else { + // in Rhino or a web browser + root.punycode = punycode; + } + +}(this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],204:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// If obj.hasOwnProperty has been overridden, then calling +// obj.hasOwnProperty(prop) will break. +// See: https://github.com/joyent/node/issues/1707 +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +module.exports = function(qs, sep, eq, options) { + sep = sep || '&'; + eq = eq || '='; + var obj = {}; + + if (typeof qs !== 'string' || qs.length === 0) { + return obj; + } + + var regexp = /\+/g; + qs = qs.split(sep); + + var maxKeys = 1000; + if (options && typeof options.maxKeys === 'number') { + maxKeys = options.maxKeys; + } + + var len = qs.length; + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0 && len > maxKeys) { + len = maxKeys; + } + + for (var i = 0; i < len; ++i) { + var x = qs[i].replace(regexp, '%20'), + idx = x.indexOf(eq), + kstr, vstr, k, v; + + if (idx >= 0) { + kstr = x.substr(0, idx); + vstr = x.substr(idx + 1); + } else { + kstr = x; + vstr = ''; + } + + k = decodeURIComponent(kstr); + v = decodeURIComponent(vstr); + + if (!hasOwnProperty(obj, k)) { + obj[k] = v; + } else if (isArray(obj[k])) { + obj[k].push(v); + } else { + obj[k] = [obj[k], v]; + } + } + + return obj; +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; + +},{}],205:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +var stringifyPrimitive = function(v) { + switch (typeof v) { + case 'string': + return v; + + case 'boolean': + return v ? 'true' : 'false'; + + case 'number': + return isFinite(v) ? v : ''; + + default: + return ''; + } +}; + +module.exports = function(obj, sep, eq, name) { + sep = sep || '&'; + eq = eq || '='; + if (obj === null) { + obj = undefined; + } + + if (typeof obj === 'object') { + return map(objectKeys(obj), function(k) { + var ks = encodeURIComponent(stringifyPrimitive(k)) + eq; + if (isArray(obj[k])) { + return map(obj[k], function(v) { + return ks + encodeURIComponent(stringifyPrimitive(v)); + }).join(sep); + } else { + return ks + encodeURIComponent(stringifyPrimitive(obj[k])); + } + }).join(sep); + + } + + if (!name) return ''; + return encodeURIComponent(stringifyPrimitive(name)) + eq + + encodeURIComponent(stringifyPrimitive(obj)); +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; + +function map (xs, f) { + if (xs.map) return xs.map(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + res.push(f(xs[i], i)); + } + return res; +} + +var objectKeys = Object.keys || function (obj) { + var res = []; + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key); + } + return res; +}; + +},{}],206:[function(require,module,exports){ +'use strict'; + +exports.decode = exports.parse = require('./decode'); +exports.encode = exports.stringify = require('./encode'); + +},{"./decode":204,"./encode":205}],207:[function(require,module,exports){ +module.exports = require('./lib/_stream_duplex.js'); + +},{"./lib/_stream_duplex.js":208}],208:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. + +'use strict'; + +/**/ + +var processNextTick = require('process-nextick-args'); +/**/ + +/**/ +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +module.exports = Duplex; + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var Readable = require('./_stream_readable'); +var Writable = require('./_stream_writable'); + +util.inherits(Duplex, Readable); + +var keys = objectKeys(Writable.prototype); +for (var v = 0; v < keys.length; v++) { + var method = keys[v]; + if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; +} + +function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); + + Readable.call(this, options); + Writable.call(this, options); + + if (options && options.readable === false) this.readable = false; + + if (options && options.writable === false) this.writable = false; + + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; + + this.once('end', onend); +} + +// the no-half-open enforcer +function onend() { + // if we allow half-open state, or if the writable side ended, + // then we're ok. + if (this.allowHalfOpen || this._writableState.ended) return; + + // no more data can be written. + // But allow more writes to happen in this tick. + processNextTick(onEndNT, this); +} + +function onEndNT(self) { + self.end(); +} + +Object.defineProperty(Duplex.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined || this._writableState === undefined) { + return false; + } + return this._readableState.destroyed && this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (this._readableState === undefined || this._writableState === undefined) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + this._writableState.destroyed = value; + } +}); + +Duplex.prototype._destroy = function (err, cb) { + this.push(null); + this.end(); + + processNextTick(cb, err); +}; + +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} +},{"./_stream_readable":210,"./_stream_writable":212,"core-util-is":183,"inherits":187,"process-nextick-args":201}],209:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. + +'use strict'; + +module.exports = PassThrough; + +var Transform = require('./_stream_transform'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); + + Transform.call(this, options); +} + +PassThrough.prototype._transform = function (chunk, encoding, cb) { + cb(null, chunk); +}; +},{"./_stream_transform":211,"core-util-is":183,"inherits":187}],210:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +/**/ + +var processNextTick = require('process-nextick-args'); +/**/ + +module.exports = Readable; + +/**/ +var isArray = require('isarray'); +/**/ + +/**/ +var Duplex; +/**/ + +Readable.ReadableState = ReadableState; + +/**/ +var EE = require('events').EventEmitter; + +var EElistenerCount = function (emitter, type) { + return emitter.listeners(type).length; +}; +/**/ + +/**/ +var Stream = require('./internal/streams/stream'); +/**/ + +// TODO(bmeurer): Change this back to const once hole checks are +// properly optimized away early in Ignition+TurboFan. +/**/ +var Buffer = require('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} +/**/ + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +/**/ +var debugUtil = require('util'); +var debug = void 0; +if (debugUtil && debugUtil.debuglog) { + debug = debugUtil.debuglog('stream'); +} else { + debug = function () {}; +} +/**/ + +var BufferList = require('./internal/streams/BufferList'); +var destroyImpl = require('./internal/streams/destroy'); +var StringDecoder; + +util.inherits(Readable, Stream); + +var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + +function prependListener(emitter, event, fn) { + // Sadly this is not cacheable as some libraries bundle their own + // event emitter implementation with them. + if (typeof emitter.prependListener === 'function') { + return emitter.prependListener(event, fn); + } else { + // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; + } +} + +function ReadableState(options, stream) { + Duplex = Duplex || require('./_stream_duplex'); + + options = options || {}; + + // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + this.objectMode = !!options.objectMode; + + if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode; + + // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + var hwm = options.highWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; + this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; + + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); + + // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift() + this.buffer = new BufferList(); + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; + + // a flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + this.sync = true; + + // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + this.resumeScheduled = false; + + // has it been destroyed + this.destroyed = false; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; + + // if true, a maybeReadMore has been scheduled + this.readingMore = false; + + this.decoder = null; + this.encoding = null; + if (options.encoding) { + if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + Duplex = Duplex || require('./_stream_duplex'); + + if (!(this instanceof Readable)) return new Readable(options); + + this._readableState = new ReadableState(options, this); + + // legacy + this.readable = true; + + if (options) { + if (typeof options.read === 'function') this._read = options.read; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + } + + Stream.call(this); +} + +Object.defineProperty(Readable.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined) { + return false; + } + return this._readableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + } +}); + +Readable.prototype.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; +Readable.prototype._destroy = function (err, cb) { + this.push(null); + cb(err); +}; + +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function (chunk, encoding) { + var state = this._readableState; + var skipChunkCheck; + + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + skipChunkCheck = true; + } + } else { + skipChunkCheck = true; + } + + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); +}; + +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function (chunk) { + return readableAddChunk(this, chunk, null, true, false); +}; + +function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { + var state = stream._readableState; + if (chunk === null) { + state.reading = false; + onEofChunk(stream, state); + } else { + var er; + if (!skipChunkCheck) er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (addToFront) { + if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); + } else if (state.ended) { + stream.emit('error', new Error('stream.push() after EOF')); + } else { + state.reading = false; + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); + } else { + addChunk(stream, state, chunk, false); + } + } + } else if (!addToFront) { + state.reading = false; + } + } + + return needMoreData(state); +} + +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); + + if (state.needReadable) emitReadable(stream); + } + maybeReadMore(stream, state); +} + +function chunkInvalid(state, chunk) { + var er; + if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} + +// if it's past the high water mark, we can push in some more. +// Also, if we have no data yet, we can stand some +// more bytes. This is to work around cases where hwm=0, +// such as the repl. Also, if the push() triggered a +// readable event, and the user called read(largeNumber) such that +// needReadable was set, then we ought to push more, so that another +// 'readable' event will be triggered. +function needMoreData(state) { + return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); +} + +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; +}; + +// backwards compatibility. +Readable.prototype.setEncoding = function (enc) { + if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; + this._readableState.decoder = new StringDecoder(enc); + this._readableState.encoding = enc; + return this; +}; + +// Don't raise the hwm > 8MB +var MAX_HWM = 0x800000; +function computeNewHighWaterMark(n) { + if (n >= MAX_HWM) { + n = MAX_HWM; + } else { + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n++; + } + return n; +} + +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function howMuchToRead(n, state) { + if (n <= 0 || state.length === 0 && state.ended) return 0; + if (state.objectMode) return 1; + if (n !== n) { + // Only flow one buffer at a time + if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; + } + // If we're asking for more than the current hwm, then raise the hwm. + if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); + if (n <= state.length) return n; + // Don't have enough + if (!state.ended) { + state.needReadable = true; + return 0; + } + return state.length; +} + +// you can override either this method, or the async _read(n) below. +Readable.prototype.read = function (n) { + debug('read', n); + n = parseInt(n, 10); + var state = this._readableState; + var nOrig = n; + + if (n !== 0) state.emittedReadable = false; + + // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); + + // if we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) endReadable(this); + return null; + } + + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); + + // if we currently have less than the highWaterMark, then also read some + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } + + // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } else if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; + // if the length is currently zero, then we *need* a readable event. + if (state.length === 0) state.needReadable = true; + // call internal read method + this._read(state.highWaterMark); + state.sync = false; + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (!state.reading) n = howMuchToRead(nOrig, state); + } + + var ret; + if (n > 0) ret = fromList(n, state);else ret = null; + + if (ret === null) { + state.needReadable = true; + n = 0; + } else { + state.length -= n; + } + + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) state.needReadable = true; + + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended) endReadable(this); + } + + if (ret !== null) this.emit('data', ret); + + return ret; +}; + +function onEofChunk(stream, state) { + if (state.ended) return; + if (state.decoder) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + state.ended = true; + + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); +} + +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + var state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + if (state.sync) processNextTick(emitReadable_, stream);else emitReadable_(stream); + } +} + +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); +} + +// at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + processNextTick(maybeReadMore_, stream, state); + } +} + +function maybeReadMore_(stream, state) { + var len = state.length; + while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) { + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) + // didn't get any data, stop spinning. + break;else len = state.length; + } + state.readingMore = false; +} + +// abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function (n) { + this.emit('error', new Error('_read() is not implemented')); +}; + +Readable.prototype.pipe = function (dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + case 1: + state.pipes = [state.pipes, dest]; + break; + default: + state.pipes.push(dest); + break; + } + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + + var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; + + var endFn = doEnd ? onend : unpipe; + if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn); + + dest.on('unpipe', onunpipe); + function onunpipe(readable, unpipeInfo) { + debug('onunpipe'); + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } + } + } + + function onend() { + debug('onend'); + dest.end(); + } + + // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + + var cleanedUp = false; + function cleanup() { + debug('cleanup'); + // cleanup event handlers once the pipe is broken + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', unpipe); + src.removeListener('data', ondata); + + cleanedUp = true; + + // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); + } + + // If the user pushes more data while we're writing to dest then we'll end up + // in ondata again. However, we only want to increase awaitDrain once because + // dest will only emit one 'drain' event for the multiple writes. + // => Introduce a guard on increasing awaitDrain. + var increasedAwaitDrain = false; + src.on('data', ondata); + function ondata(chunk) { + debug('ondata'); + increasedAwaitDrain = false; + var ret = dest.write(chunk); + if (false === ret && !increasedAwaitDrain) { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { + debug('false write response, pause', src._readableState.awaitDrain); + src._readableState.awaitDrain++; + increasedAwaitDrain = true; + } + src.pause(); + } + } + + // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er); + } + + // Make sure our error handler is attached before userland ones. + prependListener(dest, 'error', onerror); + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + dest.once('close', onclose); + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } + + // tell the dest that it's being piped to + dest.emit('pipe', src); + + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function () { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) state.awaitDrain--; + if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + +Readable.prototype.unpipe = function (dest) { + var state = this._readableState; + var unpipeInfo = { hasUnpiped: false }; + + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) return this; + + // just one destination. most common case. + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) return this; + + if (!dest) dest = state.pipes; + + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) dest.emit('unpipe', this, unpipeInfo); + return this; + } + + // slow case. multiple pipe destinations. + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) { + dests[i].emit('unpipe', this, unpipeInfo); + }return this; + } + + // try to find the right one. + var index = indexOf(state.pipes, dest); + if (index === -1) return this; + + state.pipes.splice(index, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) state.pipes = state.pipes[0]; + + dest.emit('unpipe', this, unpipeInfo); + + return this; +}; + +// set up data events if they are asked for +// Ensure readable listeners eventually get something +Readable.prototype.on = function (ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + + if (ev === 'data') { + // Start flowing on next tick if stream isn't explicitly paused + if (this._readableState.flowing !== false) this.resume(); + } else if (ev === 'readable') { + var state = this._readableState; + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; + state.emittedReadable = false; + if (!state.reading) { + processNextTick(nReadingNextTick, this); + } else if (state.length) { + emitReadable(this); + } + } + } + + return res; +}; +Readable.prototype.addListener = Readable.prototype.on; + +function nReadingNextTick(self) { + debug('readable nexttick read 0'); + self.read(0); +} + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function () { + var state = this._readableState; + if (!state.flowing) { + debug('resume'); + state.flowing = true; + resume(this, state); + } + return this; +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + processNextTick(resume_, stream, state); + } +} + +function resume_(stream, state) { + if (!state.reading) { + debug('resume read 0'); + stream.read(0); + } + + state.resumeScheduled = false; + state.awaitDrain = 0; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} + +Readable.prototype.pause = function () { + debug('call pause flowing=%j', this._readableState.flowing); + if (false !== this._readableState.flowing) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } + return this; +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + while (state.flowing && stream.read() !== null) {} +} + +// wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function (stream) { + var state = this._readableState; + var paused = false; + + var self = this; + stream.on('end', function () { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) self.push(chunk); + } + + self.push(null); + }); + + stream.on('data', function (chunk) { + debug('wrapped data'); + if (state.decoder) chunk = state.decoder.write(chunk); + + // don't skip over falsy values in objectMode + if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; + + var ret = self.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } + }); + + // proxy all the other methods. + // important when wrapping filters and duplexes. + for (var i in stream) { + if (this[i] === undefined && typeof stream[i] === 'function') { + this[i] = function (method) { + return function () { + return stream[method].apply(stream, arguments); + }; + }(i); + } + } + + // proxy certain important events. + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], self.emit.bind(self, kProxyEvents[n])); + } + + // when we try to consume some more bytes, simply unpause the + // underlying stream. + self._read = function (n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); + } + }; + + return self; +}; + +// exposed for testing purposes only. +Readable._fromList = fromList; + +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromList(n, state) { + // nothing buffered + if (state.length === 0) return null; + + var ret; + if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { + // read it all, truncate the list + if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length); + state.buffer.clear(); + } else { + // read part of list + ret = fromListPartial(n, state.buffer, state.decoder); + } + + return ret; +} + +// Extracts only enough buffered data to satisfy the amount requested. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromListPartial(n, list, hasStrings) { + var ret; + if (n < list.head.data.length) { + // slice is the same for buffers and strings + ret = list.head.data.slice(0, n); + list.head.data = list.head.data.slice(n); + } else if (n === list.head.data.length) { + // first chunk is a perfect match + ret = list.shift(); + } else { + // result spans more than one buffer + ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); + } + return ret; +} + +// Copies a specified amount of characters from the list of buffered data +// chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBufferString(n, list) { + var p = list.head; + var c = 1; + var ret = p.data; + n -= ret.length; + while (p = p.next) { + var str = p.data; + var nb = n > str.length ? str.length : n; + if (nb === str.length) ret += str;else ret += str.slice(0, n); + n -= nb; + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = str.slice(nb); + } + break; + } + ++c; + } + list.length -= c; + return ret; +} + +// Copies a specified amount of bytes from the list of buffered data chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBuffer(n, list) { + var ret = Buffer.allocUnsafe(n); + var p = list.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + while (p = p.next) { + var buf = p.data; + var nb = n > buf.length ? buf.length : n; + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = buf.slice(nb); + } + break; + } + ++c; + } + list.length -= c; + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); + + if (!state.endEmitted) { + state.ended = true; + processNextTick(endReadableNT, state, stream); + } +} + +function endReadableNT(state, stream) { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + } +} + +function forEach(xs, f) { + for (var i = 0, l = xs.length; i < l; i++) { + f(xs[i], i); + } +} + +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + return -1; +} +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./_stream_duplex":208,"./internal/streams/BufferList":213,"./internal/streams/destroy":214,"./internal/streams/stream":215,"_process":202,"core-util-is":183,"events":184,"inherits":187,"isarray":189,"process-nextick-args":201,"safe-buffer":220,"string_decoder/":226,"util":177}],211:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. + +'use strict'; + +module.exports = Transform; + +var Duplex = require('./_stream_duplex'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(Transform, Duplex); + +function TransformState(stream) { + this.afterTransform = function (er, data) { + return afterTransform(stream, er, data); + }; + + this.needTransform = false; + this.transforming = false; + this.writecb = null; + this.writechunk = null; + this.writeencoding = null; +} + +function afterTransform(stream, er, data) { + var ts = stream._transformState; + ts.transforming = false; + + var cb = ts.writecb; + + if (!cb) { + return stream.emit('error', new Error('write callback called multiple times')); + } + + ts.writechunk = null; + ts.writecb = null; + + if (data !== null && data !== undefined) stream.push(data); + + cb(er); + + var rs = stream._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + stream._read(rs.highWaterMark); + } +} + +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); + + Duplex.call(this, options); + + this._transformState = new TransformState(this); + + var stream = this; + + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; + + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; + + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; + + if (typeof options.flush === 'function') this._flush = options.flush; + } + + // When the writable side finishes, then flush out anything remaining. + this.once('prefinish', function () { + if (typeof this._flush === 'function') this._flush(function (er, data) { + done(stream, er, data); + });else done(stream); + }); +} + +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; + +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function (chunk, encoding, cb) { + throw new Error('_transform() is not implemented'); +}; + +Transform.prototype._write = function (chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); + } +}; + +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function (n) { + var ts = this._transformState; + + if (ts.writechunk !== null && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + +Transform.prototype._destroy = function (err, cb) { + var _this = this; + + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + _this.emit('close'); + }); +}; + +function done(stream, er, data) { + if (er) return stream.emit('error', er); + + if (data !== null && data !== undefined) stream.push(data); + + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + var ws = stream._writableState; + var ts = stream._transformState; + + if (ws.length) throw new Error('Calling transform done when ws.length != 0'); + + if (ts.transforming) throw new Error('Calling transform done when still transforming'); + + return stream.push(null); +} +},{"./_stream_duplex":208,"core-util-is":183,"inherits":187}],212:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. + +'use strict'; + +/**/ + +var processNextTick = require('process-nextick-args'); +/**/ + +module.exports = Writable; + +/* */ +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} + +// It seems a linked list but it is not +// there will be only 2 of these for each stream +function CorkedRequest(state) { + var _this = this; + + this.next = null; + this.entry = null; + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ + +/**/ +var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick; +/**/ + +/**/ +var Duplex; +/**/ + +Writable.WritableState = WritableState; + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +/**/ +var internalUtil = { + deprecate: require('util-deprecate') +}; +/**/ + +/**/ +var Stream = require('./internal/streams/stream'); +/**/ + +/**/ +var Buffer = require('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} +/**/ + +var destroyImpl = require('./internal/streams/destroy'); + +util.inherits(Writable, Stream); + +function nop() {} + +function WritableState(options, stream) { + Duplex = Duplex || require('./_stream_duplex'); + + options = options || {}; + + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; + + if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode; + + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; + this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm; + + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); + + // if _final has been called + this.finalCalled = false; + + // drain event flag. + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; + + // has it been destroyed + this.destroyed = false; + + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // a flag to see when we're in the middle of a write. + this.writing = false; + + // when true all writes will be buffered until .uncork() call + this.corked = 0; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function (er) { + onwrite(stream, er); + }; + + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; + + // the amount that is being written when _write is called. + this.writelen = 0; + + this.bufferedRequest = null; + this.lastBufferedRequest = null; + + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; + + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; + + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; + + // count buffered requests + this.bufferedRequestCount = 0; + + // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + this.corkedRequestsFree = new CorkedRequest(this); +} + +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; + } + return out; +}; + +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function () { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); + +// Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. +var realHasInstance; +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function (object) { + if (realHasInstance.call(this, object)) return true; + + return object && object._writableState instanceof WritableState; + } + }); +} else { + realHasInstance = function (object) { + return object instanceof this; + }; +} + +function Writable(options) { + Duplex = Duplex || require('./_stream_duplex'); + + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { + return new Writable(options); + } + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + if (options) { + if (typeof options.write === 'function') this._write = options.write; + + if (typeof options.writev === 'function') this._writev = options.writev; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + + if (typeof options.final === 'function') this._final = options.final; + } + + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function () { + this.emit('error', new Error('Cannot pipe, not readable')); +}; + +function writeAfterEnd(stream, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + processNextTick(cb, er); +} + +// Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. +function validChunk(stream, state, chunk, cb) { + var valid = true; + var er = false; + + if (chunk === null) { + er = new TypeError('May not write null values to stream'); + } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + if (er) { + stream.emit('error', er); + processNextTick(cb, er); + valid = false; + } + return valid; +} + +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + var isBuf = _isUint8Array(chunk) && !state.objectMode; + + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; + + if (typeof cb !== 'function') cb = nop; + + if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } + + return ret; +}; + +Writable.prototype.cork = function () { + var state = this._writableState; + + state.corked++; +}; + +Writable.prototype.uncork = function () { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); + } +}; + +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); + } + return chunk; +} + +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; + } + } + var len = state.objectMode ? 1 : chunk.length; + + state.length += len; + + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; + + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); + } + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; + + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + processNextTick(cb, er); + // this can emit finish, and it will always happen + // after error + processNextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + // this can emit finish, but finish must + // always follow error + finishMaybe(stream, state); + } +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + + onwriteStateUpdate(state); + + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state); + + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } + + if (sync) { + /**/ + asyncWrite(afterWrite, stream, state, finished, cb); + /**/ + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} + +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} + +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; + + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; + + var count = 0; + var allBuffers = true; + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; + } + buffer.allBuffers = allBuffers; + + doWrite(stream, state, true, state.length, buffer, '', holder.finish); + + // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + state.pendingcb++; + state.lastBufferedRequest = null; + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); + } + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + break; + } + } + + if (entry === null) state.lastBufferedRequest = null; + } + + state.bufferedRequestCount = 0; + state.bufferedRequest = entry; + state.bufferProcessing = false; +} + +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new Error('_write() is not implemented')); +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; + + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); + } + + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) endWritable(this, state, cb); +}; + +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + if (err) { + stream.emit('error', err); + } + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function') { + state.pendingcb++; + state.finalCalled = true; + processNextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} + +function finishMaybe(stream, state) { + var need = needFinish(state); + if (need) { + prefinish(stream, state); + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + } + } + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) processNextTick(cb);else stream.once('finish', cb); + } + state.ended = true; + stream.writable = false; +} + +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; + } + if (state.corkedRequestsFree) { + state.corkedRequestsFree.next = corkReq; + } else { + state.corkedRequestsFree = corkReq; + } +} + +Object.defineProperty(Writable.prototype, 'destroyed', { + get: function () { + if (this._writableState === undefined) { + return false; + } + return this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._writableState.destroyed = value; + } +}); + +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; +Writable.prototype._destroy = function (err, cb) { + this.end(); + cb(err); +}; +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./_stream_duplex":208,"./internal/streams/destroy":214,"./internal/streams/stream":215,"_process":202,"core-util-is":183,"inherits":187,"process-nextick-args":201,"safe-buffer":220,"util-deprecate":230}],213:[function(require,module,exports){ +'use strict'; + +/**/ + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Buffer = require('safe-buffer').Buffer; +/**/ + +function copyBuffer(src, target, offset) { + src.copy(target, offset); +} + +module.exports = function () { + function BufferList() { + _classCallCheck(this, BufferList); + + this.head = null; + this.tail = null; + this.length = 0; + } + + BufferList.prototype.push = function push(v) { + var entry = { data: v, next: null }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + }; + + BufferList.prototype.unshift = function unshift(v) { + var entry = { data: v, next: this.head }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + }; + + BufferList.prototype.shift = function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + }; + + BufferList.prototype.clear = function clear() { + this.head = this.tail = null; + this.length = 0; + }; + + BufferList.prototype.join = function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) { + ret += s + p.data; + }return ret; + }; + + BufferList.prototype.concat = function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + if (this.length === 1) return this.head.data; + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; + } + return ret; + }; + + return BufferList; +}(); +},{"safe-buffer":220}],214:[function(require,module,exports){ +'use strict'; + +/**/ + +var processNextTick = require('process-nextick-args'); +/**/ + +// undocumented cb() API, needed for core, not for public API +function destroy(err, cb) { + var _this = this; + + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; + + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { + processNextTick(emitErrorNT, this, err); + } + return; + } + + // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + + if (this._readableState) { + this._readableState.destroyed = true; + } + + // if this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + processNextTick(emitErrorNT, _this, err); + if (_this._writableState) { + _this._writableState.errorEmitted = true; + } + } else if (cb) { + cb(err); + } + }); +} + +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } + + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; + } +} + +function emitErrorNT(self, err) { + self.emit('error', err); +} + +module.exports = { + destroy: destroy, + undestroy: undestroy +}; +},{"process-nextick-args":201}],215:[function(require,module,exports){ +module.exports = require('events').EventEmitter; + +},{"events":184}],216:[function(require,module,exports){ +module.exports = require('./readable').PassThrough + +},{"./readable":217}],217:[function(require,module,exports){ +exports = module.exports = require('./lib/_stream_readable.js'); +exports.Stream = exports; +exports.Readable = exports; +exports.Writable = require('./lib/_stream_writable.js'); +exports.Duplex = require('./lib/_stream_duplex.js'); +exports.Transform = require('./lib/_stream_transform.js'); +exports.PassThrough = require('./lib/_stream_passthrough.js'); + +},{"./lib/_stream_duplex.js":208,"./lib/_stream_passthrough.js":209,"./lib/_stream_readable.js":210,"./lib/_stream_transform.js":211,"./lib/_stream_writable.js":212}],218:[function(require,module,exports){ +module.exports = require('./readable').Transform + +},{"./readable":217}],219:[function(require,module,exports){ +module.exports = require('./lib/_stream_writable.js'); + +},{"./lib/_stream_writable.js":212}],220:[function(require,module,exports){ +/* eslint-disable node/no-deprecated-api */ +var buffer = require('buffer') +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} + +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} + +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf +} + +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} + +},{"buffer":180}],221:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = Stream; + +var EE = require('events').EventEmitter; +var inherits = require('inherits'); + +inherits(Stream, EE); +Stream.Readable = require('readable-stream/readable.js'); +Stream.Writable = require('readable-stream/writable.js'); +Stream.Duplex = require('readable-stream/duplex.js'); +Stream.Transform = require('readable-stream/transform.js'); +Stream.PassThrough = require('readable-stream/passthrough.js'); + +// Backwards-compat with node 0.4.x +Stream.Stream = Stream; + + + +// old-style streams. Note that the pipe method (the only relevant +// part of this class) is overridden in the Readable class. + +function Stream() { + EE.call(this); +} + +Stream.prototype.pipe = function(dest, options) { + var source = this; + + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } + } + } + + source.on('data', ondata); + + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + + dest.on('drain', ondrain); + + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on('end', onend); + source.on('close', onclose); + } + + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + + dest.end(); + } + + + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + + if (typeof dest.destroy === 'function') dest.destroy(); + } + + // don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EE.listenerCount(this, 'error') === 0) { + throw er; // Unhandled stream error in pipe. + } + } + + source.on('error', onerror); + dest.on('error', onerror); + + // remove all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); + + source.removeListener('end', onend); + source.removeListener('close', onclose); + + source.removeListener('error', onerror); + dest.removeListener('error', onerror); + + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); + + dest.removeListener('close', cleanup); + } + + source.on('end', cleanup); + source.on('close', cleanup); + + dest.on('close', cleanup); + + dest.emit('pipe', source); + + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; + +},{"events":184,"inherits":187,"readable-stream/duplex.js":207,"readable-stream/passthrough.js":216,"readable-stream/readable.js":217,"readable-stream/transform.js":218,"readable-stream/writable.js":219}],222:[function(require,module,exports){ +(function (global){ +var ClientRequest = require('./lib/request') +var extend = require('xtend') +var statusCodes = require('builtin-status-codes') +var url = require('url') + +var http = exports + +http.request = function (opts, cb) { + if (typeof opts === 'string') + opts = url.parse(opts) + else + opts = extend(opts) + + // Normally, the page is loaded from http or https, so not specifying a protocol + // will result in a (valid) protocol-relative url. However, this won't work if + // the protocol is something else, like 'file:' + var defaultProtocol = global.location.protocol.search(/^https?:$/) === -1 ? 'http:' : '' + + var protocol = opts.protocol || defaultProtocol + var host = opts.hostname || opts.host + var port = opts.port + var path = opts.path || '/' + + // Necessary for IPv6 addresses + if (host && host.indexOf(':') !== -1) + host = '[' + host + ']' + + // This may be a relative url. The browser should always be able to interpret it correctly. + opts.url = (host ? (protocol + '//' + host) : '') + (port ? ':' + port : '') + path + opts.method = (opts.method || 'GET').toUpperCase() + opts.headers = opts.headers || {} + + // Also valid opts.auth, opts.mode + + var req = new ClientRequest(opts) + if (cb) + req.on('response', cb) + return req +} + +http.get = function get (opts, cb) { + var req = http.request(opts, cb) + req.end() + return req +} + +http.Agent = function () {} +http.Agent.defaultMaxSockets = 4 + +http.STATUS_CODES = statusCodes + +http.METHODS = [ + 'CHECKOUT', + 'CONNECT', + 'COPY', + 'DELETE', + 'GET', + 'HEAD', + 'LOCK', + 'M-SEARCH', + 'MERGE', + 'MKACTIVITY', + 'MKCOL', + 'MOVE', + 'NOTIFY', + 'OPTIONS', + 'PATCH', + 'POST', + 'PROPFIND', + 'PROPPATCH', + 'PURGE', + 'PUT', + 'REPORT', + 'SEARCH', + 'SUBSCRIBE', + 'TRACE', + 'UNLOCK', + 'UNSUBSCRIBE' +] +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./lib/request":224,"builtin-status-codes":181,"url":228,"xtend":234}],223:[function(require,module,exports){ +(function (global){ +exports.fetch = isFunction(global.fetch) && isFunction(global.ReadableStream) + +exports.blobConstructor = false +try { + new Blob([new ArrayBuffer(1)]) + exports.blobConstructor = true +} catch (e) {} + +// The xhr request to example.com may violate some restrictive CSP configurations, +// so if we're running in a browser that supports `fetch`, avoid calling getXHR() +// and assume support for certain features below. +var xhr +function getXHR () { + // Cache the xhr value + if (xhr !== undefined) return xhr + + if (global.XMLHttpRequest) { + xhr = new global.XMLHttpRequest() + // If XDomainRequest is available (ie only, where xhr might not work + // cross domain), use the page location. Otherwise use example.com + // Note: this doesn't actually make an http request. + try { + xhr.open('GET', global.XDomainRequest ? '/' : 'https://example.com') + } catch(e) { + xhr = null + } + } else { + // Service workers don't have XHR + xhr = null + } + return xhr +} + +function checkTypeSupport (type) { + var xhr = getXHR() + if (!xhr) return false + try { + xhr.responseType = type + return xhr.responseType === type + } catch (e) {} + return false +} + +// For some strange reason, Safari 7.0 reports typeof global.ArrayBuffer === 'object'. +// Safari 7.1 appears to have fixed this bug. +var haveArrayBuffer = typeof global.ArrayBuffer !== 'undefined' +var haveSlice = haveArrayBuffer && isFunction(global.ArrayBuffer.prototype.slice) + +// If fetch is supported, then arraybuffer will be supported too. Skip calling +// checkTypeSupport(), since that calls getXHR(). +exports.arraybuffer = exports.fetch || (haveArrayBuffer && checkTypeSupport('arraybuffer')) + +// These next two tests unavoidably show warnings in Chrome. Since fetch will always +// be used if it's available, just return false for these to avoid the warnings. +exports.msstream = !exports.fetch && haveSlice && checkTypeSupport('ms-stream') +exports.mozchunkedarraybuffer = !exports.fetch && haveArrayBuffer && + checkTypeSupport('moz-chunked-arraybuffer') + +// If fetch is supported, then overrideMimeType will be supported too. Skip calling +// getXHR(). +exports.overrideMimeType = exports.fetch || (getXHR() ? isFunction(getXHR().overrideMimeType) : false) + +exports.vbArray = isFunction(global.VBArray) + +function isFunction (value) { + return typeof value === 'function' +} + +xhr = null // Help gc + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],224:[function(require,module,exports){ +(function (process,global,Buffer){ +var capability = require('./capability') +var inherits = require('inherits') +var response = require('./response') +var stream = require('readable-stream') +var toArrayBuffer = require('to-arraybuffer') + +var IncomingMessage = response.IncomingMessage +var rStates = response.readyStates + +function decideMode (preferBinary, useFetch) { + if (capability.fetch && useFetch) { + return 'fetch' + } else if (capability.mozchunkedarraybuffer) { + return 'moz-chunked-arraybuffer' + } else if (capability.msstream) { + return 'ms-stream' + } else if (capability.arraybuffer && preferBinary) { + return 'arraybuffer' + } else if (capability.vbArray && preferBinary) { + return 'text:vbarray' + } else { + return 'text' + } +} + +var ClientRequest = module.exports = function (opts) { + var self = this + stream.Writable.call(self) + + self._opts = opts + self._body = [] + self._headers = {} + if (opts.auth) + self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64')) + Object.keys(opts.headers).forEach(function (name) { + self.setHeader(name, opts.headers[name]) + }) + + var preferBinary + var useFetch = true + if (opts.mode === 'disable-fetch' || 'timeout' in opts) { + // If the use of XHR should be preferred and includes preserving the 'content-type' header. + // Force XHR to be used since the Fetch API does not yet support timeouts. + useFetch = false + preferBinary = true + } else if (opts.mode === 'prefer-streaming') { + // If streaming is a high priority but binary compatibility and + // the accuracy of the 'content-type' header aren't + preferBinary = false + } else if (opts.mode === 'allow-wrong-content-type') { + // If streaming is more important than preserving the 'content-type' header + preferBinary = !capability.overrideMimeType + } else if (!opts.mode || opts.mode === 'default' || opts.mode === 'prefer-fast') { + // Use binary if text streaming may corrupt data or the content-type header, or for speed + preferBinary = true + } else { + throw new Error('Invalid value for opts.mode') + } + self._mode = decideMode(preferBinary, useFetch) + + self.on('finish', function () { + self._onFinish() + }) +} + +inherits(ClientRequest, stream.Writable) + +ClientRequest.prototype.setHeader = function (name, value) { + var self = this + var lowerName = name.toLowerCase() + // This check is not necessary, but it prevents warnings from browsers about setting unsafe + // headers. To be honest I'm not entirely sure hiding these warnings is a good thing, but + // http-browserify did it, so I will too. + if (unsafeHeaders.indexOf(lowerName) !== -1) + return + + self._headers[lowerName] = { + name: name, + value: value + } +} + +ClientRequest.prototype.getHeader = function (name) { + var header = this._headers[name.toLowerCase()] + if (header) + return header.value + return null +} + +ClientRequest.prototype.removeHeader = function (name) { + var self = this + delete self._headers[name.toLowerCase()] +} + +ClientRequest.prototype._onFinish = function () { + var self = this + + if (self._destroyed) + return + var opts = self._opts + + var headersObj = self._headers + var body = null + if (opts.method !== 'GET' && opts.method !== 'HEAD') { + if (capability.blobConstructor) { + body = new global.Blob(self._body.map(function (buffer) { + return toArrayBuffer(buffer) + }), { + type: (headersObj['content-type'] || {}).value || '' + }) + } else { + // get utf8 string + body = Buffer.concat(self._body).toString() + } + } + + // create flattened list of headers + var headersList = [] + Object.keys(headersObj).forEach(function (keyName) { + var name = headersObj[keyName].name + var value = headersObj[keyName].value + if (Array.isArray(value)) { + value.forEach(function (v) { + headersList.push([name, v]) + }) + } else { + headersList.push([name, value]) + } + }) + + if (self._mode === 'fetch') { + global.fetch(self._opts.url, { + method: self._opts.method, + headers: headersList, + body: body || undefined, + mode: 'cors', + credentials: opts.withCredentials ? 'include' : 'same-origin' + }).then(function (response) { + self._fetchResponse = response + self._connect() + }, function (reason) { + self.emit('error', reason) + }) + } else { + var xhr = self._xhr = new global.XMLHttpRequest() + try { + xhr.open(self._opts.method, self._opts.url, true) + } catch (err) { + process.nextTick(function () { + self.emit('error', err) + }) + return + } + + // Can't set responseType on really old browsers + if ('responseType' in xhr) + xhr.responseType = self._mode.split(':')[0] + + if ('withCredentials' in xhr) + xhr.withCredentials = !!opts.withCredentials + + if (self._mode === 'text' && 'overrideMimeType' in xhr) + xhr.overrideMimeType('text/plain; charset=x-user-defined') + + if ('timeout' in opts) { + xhr.timeout = opts.timeout + xhr.ontimeout = function () { + self.emit('timeout') + } + } + + headersList.forEach(function (header) { + xhr.setRequestHeader(header[0], header[1]) + }) + + self._response = null + xhr.onreadystatechange = function () { + switch (xhr.readyState) { + case rStates.LOADING: + case rStates.DONE: + self._onXHRProgress() + break + } + } + // Necessary for streaming in Firefox, since xhr.response is ONLY defined + // in onprogress, not in onreadystatechange with xhr.readyState = 3 + if (self._mode === 'moz-chunked-arraybuffer') { + xhr.onprogress = function () { + self._onXHRProgress() + } + } + + xhr.onerror = function () { + if (self._destroyed) + return + self.emit('error', new Error('XHR error')) + } + + try { + xhr.send(body) + } catch (err) { + process.nextTick(function () { + self.emit('error', err) + }) + return + } + } +} + +/** + * Checks if xhr.status is readable and non-zero, indicating no error. + * Even though the spec says it should be available in readyState 3, + * accessing it throws an exception in IE8 + */ +function statusValid (xhr) { + try { + var status = xhr.status + return (status !== null && status !== 0) + } catch (e) { + return false + } +} + +ClientRequest.prototype._onXHRProgress = function () { + var self = this + + if (!statusValid(self._xhr) || self._destroyed) + return + + if (!self._response) + self._connect() + + self._response._onXHRProgress() +} + +ClientRequest.prototype._connect = function () { + var self = this + + if (self._destroyed) + return + + self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode) + self._response.on('error', function(err) { + self.emit('error', err) + }) + + self.emit('response', self._response) +} + +ClientRequest.prototype._write = function (chunk, encoding, cb) { + var self = this + + self._body.push(chunk) + cb() +} + +ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function () { + var self = this + self._destroyed = true + if (self._response) + self._response._destroyed = true + if (self._xhr) + self._xhr.abort() + // Currently, there isn't a way to truly abort a fetch. + // If you like bikeshedding, see https://github.com/whatwg/fetch/issues/27 +} + +ClientRequest.prototype.end = function (data, encoding, cb) { + var self = this + if (typeof data === 'function') { + cb = data + data = undefined + } + + stream.Writable.prototype.end.call(self, data, encoding, cb) +} + +ClientRequest.prototype.flushHeaders = function () {} +ClientRequest.prototype.setTimeout = function () {} +ClientRequest.prototype.setNoDelay = function () {} +ClientRequest.prototype.setSocketKeepAlive = function () {} + +// Taken from http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method +var unsafeHeaders = [ + 'accept-charset', + 'accept-encoding', + 'access-control-request-headers', + 'access-control-request-method', + 'connection', + 'content-length', + 'cookie', + 'cookie2', + 'date', + 'dnt', + 'expect', + 'host', + 'keep-alive', + 'origin', + 'referer', + 'te', + 'trailer', + 'transfer-encoding', + 'upgrade', + 'user-agent', + 'via' +] + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) +},{"./capability":223,"./response":225,"_process":202,"buffer":180,"inherits":187,"readable-stream":217,"to-arraybuffer":227}],225:[function(require,module,exports){ +(function (process,global,Buffer){ +var capability = require('./capability') +var inherits = require('inherits') +var stream = require('readable-stream') + +var rStates = exports.readyStates = { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 +} + +var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode) { + var self = this + stream.Readable.call(self) + + self._mode = mode + self.headers = {} + self.rawHeaders = [] + self.trailers = {} + self.rawTrailers = [] + + // Fake the 'close' event, but only once 'end' fires + self.on('end', function () { + // The nextTick is necessary to prevent the 'request' module from causing an infinite loop + process.nextTick(function () { + self.emit('close') + }) + }) + + if (mode === 'fetch') { + self._fetchResponse = response + + self.url = response.url + self.statusCode = response.status + self.statusMessage = response.statusText + + response.headers.forEach(function(header, key){ + self.headers[key.toLowerCase()] = header + self.rawHeaders.push(key, header) + }) + + + // TODO: this doesn't respect backpressure. Once WritableStream is available, this can be fixed + var reader = response.body.getReader() + function read () { + reader.read().then(function (result) { + if (self._destroyed) + return + if (result.done) { + self.push(null) + return + } + self.push(new Buffer(result.value)) + read() + }).catch(function(err) { + self.emit('error', err) + }) + } + read() + + } else { + self._xhr = xhr + self._pos = 0 + + self.url = xhr.responseURL + self.statusCode = xhr.status + self.statusMessage = xhr.statusText + var headers = xhr.getAllResponseHeaders().split(/\r?\n/) + headers.forEach(function (header) { + var matches = header.match(/^([^:]+):\s*(.*)/) + if (matches) { + var key = matches[1].toLowerCase() + if (key === 'set-cookie') { + if (self.headers[key] === undefined) { + self.headers[key] = [] + } + self.headers[key].push(matches[2]) + } else if (self.headers[key] !== undefined) { + self.headers[key] += ', ' + matches[2] + } else { + self.headers[key] = matches[2] + } + self.rawHeaders.push(matches[1], matches[2]) + } + }) + + self._charset = 'x-user-defined' + if (!capability.overrideMimeType) { + var mimeType = self.rawHeaders['mime-type'] + if (mimeType) { + var charsetMatch = mimeType.match(/;\s*charset=([^;])(;|$)/) + if (charsetMatch) { + self._charset = charsetMatch[1].toLowerCase() + } + } + if (!self._charset) + self._charset = 'utf-8' // best guess + } + } +} + +inherits(IncomingMessage, stream.Readable) + +IncomingMessage.prototype._read = function () {} + +IncomingMessage.prototype._onXHRProgress = function () { + var self = this + + var xhr = self._xhr + + var response = null + switch (self._mode) { + case 'text:vbarray': // For IE9 + if (xhr.readyState !== rStates.DONE) + break + try { + // This fails in IE8 + response = new global.VBArray(xhr.responseBody).toArray() + } catch (e) {} + if (response !== null) { + self.push(new Buffer(response)) + break + } + // Falls through in IE8 + case 'text': + try { // This will fail when readyState = 3 in IE9. Switch mode and wait for readyState = 4 + response = xhr.responseText + } catch (e) { + self._mode = 'text:vbarray' + break + } + if (response.length > self._pos) { + var newData = response.substr(self._pos) + if (self._charset === 'x-user-defined') { + var buffer = new Buffer(newData.length) + for (var i = 0; i < newData.length; i++) + buffer[i] = newData.charCodeAt(i) & 0xff + + self.push(buffer) + } else { + self.push(newData, self._charset) + } + self._pos = response.length + } + break + case 'arraybuffer': + if (xhr.readyState !== rStates.DONE || !xhr.response) + break + response = xhr.response + self.push(new Buffer(new Uint8Array(response))) + break + case 'moz-chunked-arraybuffer': // take whole + response = xhr.response + if (xhr.readyState !== rStates.LOADING || !response) + break + self.push(new Buffer(new Uint8Array(response))) + break + case 'ms-stream': + response = xhr.response + if (xhr.readyState !== rStates.LOADING) + break + var reader = new global.MSStreamReader() + reader.onprogress = function () { + if (reader.result.byteLength > self._pos) { + self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos)))) + self._pos = reader.result.byteLength + } + } + reader.onload = function () { + self.push(null) + } + // reader.onerror = ??? // TODO: this + reader.readAsArrayBuffer(response) + break + } + + // The ms-stream case handles end separately in reader.onload() + if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') { + self.push(null) + } +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) +},{"./capability":223,"_process":202,"buffer":180,"inherits":187,"readable-stream":217}],226:[function(require,module,exports){ +'use strict'; + +var Buffer = require('safe-buffer').Buffer; + +var isEncoding = Buffer.isEncoding || function (encoding) { + encoding = '' + encoding; + switch (encoding && encoding.toLowerCase()) { + case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': + return true; + default: + return false; + } +}; + +function _normalizeEncoding(enc) { + if (!enc) return 'utf8'; + var retried; + while (true) { + switch (enc) { + case 'utf8': + case 'utf-8': + return 'utf8'; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return 'utf16le'; + case 'latin1': + case 'binary': + return 'latin1'; + case 'base64': + case 'ascii': + case 'hex': + return enc; + default: + if (retried) return; // undefined + enc = ('' + enc).toLowerCase(); + retried = true; + } + } +}; + +// Do not cache `Buffer.isEncoding` when checking encoding names as some +// modules monkey-patch it to support additional encodings +function normalizeEncoding(enc) { + var nenc = _normalizeEncoding(enc); + if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); + return nenc || enc; +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. +exports.StringDecoder = StringDecoder; +function StringDecoder(encoding) { + this.encoding = normalizeEncoding(encoding); + var nb; + switch (this.encoding) { + case 'utf16le': + this.text = utf16Text; + this.end = utf16End; + nb = 4; + break; + case 'utf8': + this.fillLast = utf8FillLast; + nb = 4; + break; + case 'base64': + this.text = base64Text; + this.end = base64End; + nb = 3; + break; + default: + this.write = simpleWrite; + this.end = simpleEnd; + return; + } + this.lastNeed = 0; + this.lastTotal = 0; + this.lastChar = Buffer.allocUnsafe(nb); +} + +StringDecoder.prototype.write = function (buf) { + if (buf.length === 0) return ''; + var r; + var i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ''; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ''; +}; + +StringDecoder.prototype.end = utf8End; + +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; + +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer +StringDecoder.prototype.fillLast = function (buf) { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +}; + +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. +function utf8CheckByte(byte) { + if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; + return -1; +} + +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. +function utf8CheckIncomplete(self, buf, i) { + var j = buf.length - 1; + if (j < i) return 0; + var nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0;else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// UTF-8 replacement characters ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'.repeat(p); + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'.repeat(p + 1); + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'.repeat(p + 2); + } + } + } +} + +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + var p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a +// partial character, the character's bytes are buffered until the required +// number of bytes are available. +function utf8Text(buf, i) { + var total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString('utf8', i); + this.lastTotal = total; + var end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString('utf8', i, end); +} + +// For UTF-8, a replacement character for each buffered byte of a (partial) +// character needs to be added to the output. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'.repeat(this.lastTotal - this.lastNeed); + return r; +} + +// UTF-16LE typically needs two bytes per character, but even if we have an even +// number of bytes available, we need to check if we end on a leading/high +// surrogate. In that case, we need to wait for the next two bytes in order to +// decode the last character properly. +function utf16Text(buf, i) { + if ((buf.length - i) % 2 === 0) { + var r = buf.toString('utf16le', i); + if (r) { + var c = r.charCodeAt(r.length - 1); + if (c >= 0xD800 && c <= 0xDBFF) { + this.lastNeed = 2; + this.lastTotal = 4; + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + return r.slice(0, -1); + } + } + return r; + } + this.lastNeed = 1; + this.lastTotal = 2; + this.lastChar[0] = buf[buf.length - 1]; + return buf.toString('utf16le', i, buf.length - 1); +} + +// For UTF-16LE we do not explicitly append special replacement characters if we +// end on a partial character, we simply let v8 handle that. +function utf16End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) { + var end = this.lastTotal - this.lastNeed; + return r + this.lastChar.toString('utf16le', 0, end); + } + return r; +} + +function base64Text(buf, i) { + var n = (buf.length - i) % 3; + if (n === 0) return buf.toString('base64', i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString('base64', i, buf.length - n); +} + +function base64End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + return r; +} + +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); +} + +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; +} +},{"safe-buffer":220}],227:[function(require,module,exports){ +var Buffer = require('buffer').Buffer + +module.exports = function (buf) { + // If the buffer is backed by a Uint8Array, a faster version will work + if (buf instanceof Uint8Array) { + // If the buffer isn't a subarray, return the underlying ArrayBuffer + if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) { + return buf.buffer + } else if (typeof buf.buffer.slice === 'function') { + // Otherwise we need to get a proper copy + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) + } + } + + if (Buffer.isBuffer(buf)) { + // This is the slow version that will work with any Buffer + // implementation (even in old browsers) + var arrayCopy = new Uint8Array(buf.length) + var len = buf.length + for (var i = 0; i < len; i++) { + arrayCopy[i] = buf[i] + } + return arrayCopy.buffer + } else { + throw new Error('Argument must be a Buffer') + } +} + +},{"buffer":180}],228:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +var punycode = require('punycode'); +var util = require('./util'); + +exports.parse = urlParse; +exports.resolve = urlResolve; +exports.resolveObject = urlResolveObject; +exports.format = urlFormat; + +exports.Url = Url; + +function Url() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.host = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.query = null; + this.pathname = null; + this.path = null; + this.href = null; +} + +// Reference: RFC 3986, RFC 1808, RFC 2396 + +// define these here so at least they only have to be +// compiled once on the first module load. +var protocolPattern = /^([a-z0-9.+-]+:)/i, + portPattern = /:[0-9]*$/, + + // Special case for a simple path URL + simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, + + // RFC 2396: characters reserved for delimiting URLs. + // We actually just auto-escape these. + delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], + + // RFC 2396: characters not allowed for various reasons. + unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), + + // Allowed by RFCs, but cause of XSS attacks. Always escape these. + autoEscape = ['\''].concat(unwise), + // Characters that are never ever allowed in a hostname. + // Note that any invalid chars are also handled, but these + // are the ones that are *expected* to be seen, so we fast-path + // them. + nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape), + hostEndingChars = ['/', '?', '#'], + hostnameMaxLen = 255, + hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/, + hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, + // protocols that can allow "unsafe" and "unwise" chars. + unsafeProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that never have a hostname. + hostlessProtocol = { + 'javascript': true, + 'javascript:': true + }, + // protocols that always contain a // bit. + slashedProtocol = { + 'http': true, + 'https': true, + 'ftp': true, + 'gopher': true, + 'file': true, + 'http:': true, + 'https:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true + }, + querystring = require('querystring'); + +function urlParse(url, parseQueryString, slashesDenoteHost) { + if (url && util.isObject(url) && url instanceof Url) return url; + + var u = new Url; + u.parse(url, parseQueryString, slashesDenoteHost); + return u; +} + +Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { + if (!util.isString(url)) { + throw new TypeError("Parameter 'url' must be a string, not " + typeof url); + } + + // Copy chrome, IE, opera backslash-handling behavior. + // Back slashes before the query string get converted to forward slashes + // See: https://code.google.com/p/chromium/issues/detail?id=25916 + var queryIndex = url.indexOf('?'), + splitter = + (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#', + uSplit = url.split(splitter), + slashRegex = /\\/g; + uSplit[0] = uSplit[0].replace(slashRegex, '/'); + url = uSplit.join(splitter); + + var rest = url; + + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); + + if (!slashesDenoteHost && url.split('#').length === 1) { + // Try fast path regexp + var simplePath = simplePathPattern.exec(rest); + if (simplePath) { + this.path = rest; + this.href = rest; + this.pathname = simplePath[1]; + if (simplePath[2]) { + this.search = simplePath[2]; + if (parseQueryString) { + this.query = querystring.parse(this.search.substr(1)); + } else { + this.query = this.search.substr(1); + } + } else if (parseQueryString) { + this.search = ''; + this.query = {}; + } + return this; + } + } + + var proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + var lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.substr(proto.length); + } + + // figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { + var slashes = rest.substr(0, 2) === '//'; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + this.slashes = true; + } + } + + if (!hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto]))) { + + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the last @ sign, unless some host-ending character + // comes *before* the @-sign. + // URLs are obnoxious. + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:c path:/?@c + + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. + + // find the first instance of any hostEndingChars + var hostEnd = -1; + for (var i = 0; i < hostEndingChars.length; i++) { + var hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + + // at this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + var auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf('@'); + } else { + // atSign must be in auth portion. + // http://a@b/c@d => host:b auth:a path:/c@d + atSign = rest.lastIndexOf('@', hostEnd); + } + + // Now we have a portion which is definitely the auth. + // Pull that off. + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = decodeURIComponent(auth); + } + + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (var i = 0; i < nonHostChars.length; i++) { + var hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) + hostEnd = hec; + } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) + hostEnd = rest.length; + + this.host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); + + // pull out port. + this.parseHost(); + + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + this.hostname = this.hostname || ''; + + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + var ipv6Hostname = this.hostname[0] === '[' && + this.hostname[this.hostname.length - 1] === ']'; + + // validate a little. + if (!ipv6Hostname) { + var hostparts = this.hostname.split(/\./); + for (var i = 0, l = hostparts.length; i < l; i++) { + var part = hostparts[i]; + if (!part) continue; + if (!part.match(hostnamePartPattern)) { + var newpart = ''; + for (var j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + // we replace non-ASCII char with a temporary placeholder + // we need this to make sure size of hostname is not + // broken by replacing non-ASCII by nothing + newpart += 'x'; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + var validParts = hostparts.slice(0, i); + var notHost = hostparts.slice(i + 1); + var bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = '/' + notHost.join('.') + rest; + } + this.hostname = validParts.join('.'); + break; + } + } + } + } + + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } else { + // hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); + } + + if (!ipv6Hostname) { + // IDNA Support: Returns a punycoded representation of "domain". + // It only converts parts of the domain name that + // have non-ASCII characters, i.e. it doesn't matter if + // you call it with a domain that already is ASCII-only. + this.hostname = punycode.toASCII(this.hostname); + } + + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; + this.host = h + p; + this.href += this.host; + + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + if (rest[0] !== '/') { + rest = '/' + rest; + } + } + } + + // now rest is set to the post-host stuff. + // chop off any delim chars. + if (!unsafeProtocol[lowerProto]) { + + // First, make 100% sure that any "autoEscape" chars get + // escaped, even if encodeURIComponent doesn't think they + // need to be. + for (var i = 0, l = autoEscape.length; i < l; i++) { + var ae = autoEscape[i]; + if (rest.indexOf(ae) === -1) + continue; + var esc = encodeURIComponent(ae); + if (esc === ae) { + esc = escape(ae); + } + rest = rest.split(ae).join(esc); + } + } + + + // chop off from the tail first. + var hash = rest.indexOf('#'); + if (hash !== -1) { + // got a fragment string. + this.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + var qm = rest.indexOf('?'); + if (qm !== -1) { + this.search = rest.substr(qm); + this.query = rest.substr(qm + 1); + if (parseQueryString) { + this.query = querystring.parse(this.query); + } + rest = rest.slice(0, qm); + } else if (parseQueryString) { + // no query string, but parseQueryString still requested + this.search = ''; + this.query = {}; + } + if (rest) this.pathname = rest; + if (slashedProtocol[lowerProto] && + this.hostname && !this.pathname) { + this.pathname = '/'; + } + + //to support http.request + if (this.pathname || this.search) { + var p = this.pathname || ''; + var s = this.search || ''; + this.path = p + s; + } + + // finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; +}; + +// format a parsed object into a url string +function urlFormat(obj) { + // ensure it's an object, and not a string url. + // If it's an obj, this is a no-op. + // this way, you can call url_format() on strings + // to clean up potentially wonky urls. + if (util.isString(obj)) obj = urlParse(obj); + if (!(obj instanceof Url)) return Url.prototype.format.call(obj); + return obj.format(); +} + +Url.prototype.format = function() { + var auth = this.auth || ''; + if (auth) { + auth = encodeURIComponent(auth); + auth = auth.replace(/%3A/i, ':'); + auth += '@'; + } + + var protocol = this.protocol || '', + pathname = this.pathname || '', + hash = this.hash || '', + host = false, + query = ''; + + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + (this.hostname.indexOf(':') === -1 ? + this.hostname : + '[' + this.hostname + ']'); + if (this.port) { + host += ':' + this.port; + } + } + + if (this.query && + util.isObject(this.query) && + Object.keys(this.query).length) { + query = querystring.stringify(this.query); + } + + var search = this.search || (query && ('?' + query)) || ''; + + if (protocol && protocol.substr(-1) !== ':') protocol += ':'; + + // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + // unless they had them to begin with. + if (this.slashes || + (!protocol || slashedProtocol[protocol]) && host !== false) { + host = '//' + (host || ''); + if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname; + } else if (!host) { + host = ''; + } + + if (hash && hash.charAt(0) !== '#') hash = '#' + hash; + if (search && search.charAt(0) !== '?') search = '?' + search; + + pathname = pathname.replace(/[?#]/g, function(match) { + return encodeURIComponent(match); + }); + search = search.replace('#', '%23'); + + return protocol + host + pathname + search + hash; +}; + +function urlResolve(source, relative) { + return urlParse(source, false, true).resolve(relative); +} + +Url.prototype.resolve = function(relative) { + return this.resolveObject(urlParse(relative, false, true)).format(); +}; + +function urlResolveObject(source, relative) { + if (!source) return relative; + return urlParse(source, false, true).resolveObject(relative); +} + +Url.prototype.resolveObject = function(relative) { + if (util.isString(relative)) { + var rel = new Url(); + rel.parse(relative, false, true); + relative = rel; + } + + var result = new Url(); + var tkeys = Object.keys(this); + for (var tk = 0; tk < tkeys.length; tk++) { + var tkey = tkeys[tk]; + result[tkey] = this[tkey]; + } + + // hash is always overridden, no matter what. + // even href="" will remove it. + result.hash = relative.hash; + + // if the relative url is empty, then there's nothing left to do here. + if (relative.href === '') { + result.href = result.format(); + return result; + } + + // hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + // take everything except the protocol from relative + var rkeys = Object.keys(relative); + for (var rk = 0; rk < rkeys.length; rk++) { + var rkey = rkeys[rk]; + if (rkey !== 'protocol') + result[rkey] = relative[rkey]; + } + + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[result.protocol] && + result.hostname && !result.pathname) { + result.path = result.pathname = '/'; + } + + result.href = result.format(); + return result; + } + + if (relative.protocol && relative.protocol !== result.protocol) { + // if it's a known url protocol, then changing + // the protocol does weird things + // first, if it's not file:, then we MUST have a host, + // and if there was a path + // to begin with, then we MUST have a path. + // if it is file:, then the host is dropped, + // because that's known to be hostless. + // anything else is assumed to be absolute. + if (!slashedProtocol[relative.protocol]) { + var keys = Object.keys(relative); + for (var v = 0; v < keys.length; v++) { + var k = keys[v]; + result[k] = relative[k]; + } + result.href = result.format(); + return result; + } + + result.protocol = relative.protocol; + if (!relative.host && !hostlessProtocol[relative.protocol]) { + var relPath = (relative.pathname || '').split('/'); + while (relPath.length && !(relative.host = relPath.shift())); + if (!relative.host) relative.host = ''; + if (!relative.hostname) relative.hostname = ''; + if (relPath[0] !== '') relPath.unshift(''); + if (relPath.length < 2) relPath.unshift(''); + result.pathname = relPath.join('/'); + } else { + result.pathname = relative.pathname; + } + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ''; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // to support http.request + if (result.pathname || result.search) { + var p = result.pathname || ''; + var s = result.search || ''; + result.path = p + s; + } + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + } + + var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'), + isRelAbs = ( + relative.host || + relative.pathname && relative.pathname.charAt(0) === '/' + ), + mustEndAbs = (isRelAbs || isSourceAbs || + (result.host && relative.pathname)), + removeAllDots = mustEndAbs, + srcPath = result.pathname && result.pathname.split('/') || [], + relPath = relative.pathname && relative.pathname.split('/') || [], + psychotic = result.protocol && !slashedProtocol[result.protocol]; + + // if the url is a non-slashed url, then relative + // links like ../.. should be able + // to crawl up to the hostname, as well. This is strange. + // result.protocol has already been set by now. + // Later on, put the first path part into the host field. + if (psychotic) { + result.hostname = ''; + result.port = null; + if (result.host) { + if (srcPath[0] === '') srcPath[0] = result.host; + else srcPath.unshift(result.host); + } + result.host = ''; + if (relative.protocol) { + relative.hostname = null; + relative.port = null; + if (relative.host) { + if (relPath[0] === '') relPath[0] = relative.host; + else relPath.unshift(relative.host); + } + relative.host = null; + } + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); + } + + if (isRelAbs) { + // it's absolute. + result.host = (relative.host || relative.host === '') ? + relative.host : result.host; + result.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : result.hostname; + result.search = relative.search; + result.query = relative.query; + srcPath = relPath; + // fall through to the dot-handling below. + } else if (relPath.length) { + // it's relative + // throw away the existing file, and take the new path instead. + if (!srcPath) srcPath = []; + srcPath.pop(); + srcPath = srcPath.concat(relPath); + result.search = relative.search; + result.query = relative.query; + } else if (!util.isNullOrUndefined(relative.search)) { + // just pull out the search. + // like href='?foo'. + // Put this after the other two cases because it simplifies the booleans + if (psychotic) { + result.hostname = result.host = srcPath.shift(); + //occationaly the auth can get stuck only in host + //this especially happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + result.search = relative.search; + result.query = relative.query; + //to support http.request + if (!util.isNull(result.pathname) || !util.isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.href = result.format(); + return result; + } + + if (!srcPath.length) { + // no path at all. easy. + // we've already handled the other stuff above. + result.pathname = null; + //to support http.request + if (result.search) { + result.path = '/' + result.search; + } else { + result.path = null; + } + result.href = result.format(); + return result; + } + + // if a url ENDs in . or .., then it must get a trailing slash. + // however, if it ends in anything else non-slashy, + // then it must NOT get a trailing slash. + var last = srcPath.slice(-1)[0]; + var hasTrailingSlash = ( + (result.host || relative.host || srcPath.length > 1) && + (last === '.' || last === '..') || last === ''); + + // strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = srcPath.length; i >= 0; i--) { + last = srcPath[i]; + if (last === '.') { + srcPath.splice(i, 1); + } else if (last === '..') { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + for (; up--; up) { + srcPath.unshift('..'); + } + } + + if (mustEndAbs && srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { + srcPath.unshift(''); + } + + if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) { + srcPath.push(''); + } + + var isAbsolute = srcPath[0] === '' || + (srcPath[0] && srcPath[0].charAt(0) === '/'); + + // put the host back + if (psychotic) { + result.hostname = result.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; + //occationaly the auth can get stuck only in host + //this especially happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = result.host && result.host.indexOf('@') > 0 ? + result.host.split('@') : false; + if (authInHost) { + result.auth = authInHost.shift(); + result.host = result.hostname = authInHost.shift(); + } + } + + mustEndAbs = mustEndAbs || (result.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(''); + } + + if (!srcPath.length) { + result.pathname = null; + result.path = null; + } else { + result.pathname = srcPath.join('/'); + } + + //to support request.http + if (!util.isNull(result.pathname) || !util.isNull(result.search)) { + result.path = (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; +}; + +Url.prototype.parseHost = function() { + var host = this.host; + var port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ':') { + this.port = port.substr(1); + } + host = host.substr(0, host.length - port.length); + } + if (host) this.hostname = host; +}; + +},{"./util":229,"punycode":203,"querystring":206}],229:[function(require,module,exports){ +'use strict'; + +module.exports = { + isString: function(arg) { + return typeof(arg) === 'string'; + }, + isObject: function(arg) { + return typeof(arg) === 'object' && arg !== null; + }, + isNull: function(arg) { + return arg === null; + }, + isNullOrUndefined: function(arg) { + return arg == null; + } +}; + +},{}],230:[function(require,module,exports){ +(function (global){ + +/** + * Module exports. + */ + +module.exports = deprecate; + +/** + * Mark that a method should not be used. + * Returns a modified function which warns once by default. + * + * If `localStorage.noDeprecation = true` is set, then it is a no-op. + * + * If `localStorage.throwDeprecation = true` is set, then deprecated functions + * will throw an Error when invoked. + * + * If `localStorage.traceDeprecation = true` is set, then deprecated functions + * will invoke `console.trace()` instead of `console.error()`. + * + * @param {Function} fn - the function to deprecate + * @param {String} msg - the string to print to the console when `fn` is invoked + * @returns {Function} a new "deprecated" version of `fn` + * @api public + */ + +function deprecate (fn, msg) { + if (config('noDeprecation')) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (config('throwDeprecation')) { + throw new Error(msg); + } else if (config('traceDeprecation')) { + console.trace(msg); + } else { + console.warn(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +} + +/** + * Checks `localStorage` for boolean values for the given `name`. + * + * @param {String} name + * @returns {Boolean} + * @api private + */ + +function config (name) { + // accessing global.localStorage can trigger a DOMException in sandboxed iframes + try { + if (!global.localStorage) return false; + } catch (_) { + return false; + } + var val = global.localStorage[name]; + if (null == val) return false; + return String(val).toLowerCase() === 'true'; +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],231:[function(require,module,exports){ +arguments[4][187][0].apply(exports,arguments) +},{"dup":187}],232:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],233:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":232,"_process":202,"inherits":231}],234:[function(require,module,exports){ +module.exports = extend + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function extend() { + var target = {} + + for (var i = 0; i < arguments.length; i++) { + var source = arguments[i] + + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target +} + +},{}]},{},[1]); diff --git a/TaskServer/node_modules/ccxt/js/_1btcxe.js b/TaskServer/node_modules/ccxt/js/_1btcxe.js new file mode 100644 index 000000000..f18280a68 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/_1btcxe.js @@ -0,0 +1,298 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class _1btcxe extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': '_1btcxe', + 'name': '1BTCXE', + 'countries': [ 'PA' ], // Panama + 'comment': 'Crypto Capital API', + 'has': { + 'CORS': true, + 'withdraw': true, + }, + 'timeframes': { + '1d': '1year', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766049-2b294408-5ecc-11e7-85cc-adaff013dc1a.jpg', + 'api': 'https://1btcxe.com/api', + 'www': 'https://1btcxe.com', + 'doc': 'https://1btcxe.com/api-docs.php', + }, + 'api': { + 'public': { + 'get': [ + 'stats', + 'historical-prices', + 'order-book', + 'transactions', + ], + }, + 'private': { + 'post': [ + 'balances-and-info', + 'open-orders', + 'user-transactions', + 'btc-deposit-address/get', + 'btc-deposit-address/new', + 'deposits/get', + 'withdrawals/get', + 'orders/new', + 'orders/edit', + 'orders/cancel', + 'orders/status', + 'withdrawals/new', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + return [ + { 'id': 'USD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'BTC', 'quoteId': 'USD' }, + { 'id': 'EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR' }, + { 'id': 'CNY', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'baseId': 'BTC', 'quoteId': 'CNY' }, + { 'id': 'RUB', 'symbol': 'BTC/RUB', 'base': 'BTC', 'quote': 'RUB', 'baseId': 'BTC', 'quoteId': 'RUB' }, + { 'id': 'CHF', 'symbol': 'BTC/CHF', 'base': 'BTC', 'quote': 'CHF', 'baseId': 'BTC', 'quoteId': 'CHF' }, + { 'id': 'JPY', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'BTC', 'quoteId': 'JPY' }, + { 'id': 'GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'BTC', 'quoteId': 'GBP' }, + { 'id': 'CAD', 'symbol': 'BTC/CAD', 'base': 'BTC', 'quote': 'CAD', 'baseId': 'BTC', 'quoteId': 'CAD' }, + { 'id': 'AUD', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'BTC', 'quoteId': 'AUD' }, + { 'id': 'AED', 'symbol': 'BTC/AED', 'base': 'BTC', 'quote': 'AED', 'baseId': 'BTC', 'quoteId': 'AED' }, + { 'id': 'BGN', 'symbol': 'BTC/BGN', 'base': 'BTC', 'quote': 'BGN', 'baseId': 'BTC', 'quoteId': 'BGN' }, + { 'id': 'CZK', 'symbol': 'BTC/CZK', 'base': 'BTC', 'quote': 'CZK', 'baseId': 'BTC', 'quoteId': 'CZK' }, + { 'id': 'DKK', 'symbol': 'BTC/DKK', 'base': 'BTC', 'quote': 'DKK', 'baseId': 'BTC', 'quoteId': 'DKK' }, + { 'id': 'HKD', 'symbol': 'BTC/HKD', 'base': 'BTC', 'quote': 'HKD', 'baseId': 'BTC', 'quoteId': 'HKD' }, + { 'id': 'HRK', 'symbol': 'BTC/HRK', 'base': 'BTC', 'quote': 'HRK', 'baseId': 'BTC', 'quoteId': 'HRK' }, + { 'id': 'HUF', 'symbol': 'BTC/HUF', 'base': 'BTC', 'quote': 'HUF', 'baseId': 'BTC', 'quoteId': 'HUF' }, + { 'id': 'ILS', 'symbol': 'BTC/ILS', 'base': 'BTC', 'quote': 'ILS', 'baseId': 'BTC', 'quoteId': 'ILS' }, + { 'id': 'INR', 'symbol': 'BTC/INR', 'base': 'BTC', 'quote': 'INR', 'baseId': 'BTC', 'quoteId': 'INR' }, + { 'id': 'MUR', 'symbol': 'BTC/MUR', 'base': 'BTC', 'quote': 'MUR', 'baseId': 'BTC', 'quoteId': 'MUR' }, + { 'id': 'MXN', 'symbol': 'BTC/MXN', 'base': 'BTC', 'quote': 'MXN', 'baseId': 'BTC', 'quoteId': 'MXN' }, + { 'id': 'NOK', 'symbol': 'BTC/NOK', 'base': 'BTC', 'quote': 'NOK', 'baseId': 'BTC', 'quoteId': 'NOK' }, + { 'id': 'NZD', 'symbol': 'BTC/NZD', 'base': 'BTC', 'quote': 'NZD', 'baseId': 'BTC', 'quoteId': 'NZD' }, + { 'id': 'PLN', 'symbol': 'BTC/PLN', 'base': 'BTC', 'quote': 'PLN', 'baseId': 'BTC', 'quoteId': 'PLN' }, + { 'id': 'RON', 'symbol': 'BTC/RON', 'base': 'BTC', 'quote': 'RON', 'baseId': 'BTC', 'quoteId': 'RON' }, + { 'id': 'SEK', 'symbol': 'BTC/SEK', 'base': 'BTC', 'quote': 'SEK', 'baseId': 'BTC', 'quoteId': 'SEK' }, + { 'id': 'SGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD', 'baseId': 'BTC', 'quoteId': 'SGD' }, + { 'id': 'THB', 'symbol': 'BTC/THB', 'base': 'BTC', 'quote': 'THB', 'baseId': 'BTC', 'quoteId': 'THB' }, + { 'id': 'TRY', 'symbol': 'BTC/TRY', 'base': 'BTC', 'quote': 'TRY', 'baseId': 'BTC', 'quoteId': 'TRY' }, + { 'id': 'ZAR', 'symbol': 'BTC/ZAR', 'base': 'BTC', 'quote': 'ZAR', 'baseId': 'BTC', 'quoteId': 'ZAR' }, + ]; + } + + async fetchBalance (params = {}) { + const response = await this.privatePostBalancesAndInfo (params); + const balance = response['balances-and-info']; + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const account = this.account (); + account['free'] = this.safeFloat (balance['available'], currencyId); + account['used'] = this.safeFloat (balance['on_hold'], currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + const request = { + 'currency': this.marketId (symbol), + }; + const response = await this.publicGetOrderBook (this.extend (request, params)); + return this.parseOrderBook (response['order-book'], undefined, 'bid', 'ask', 'price', 'order_amount'); + } + + async fetchTicker (symbol, params = {}) { + const request = { + 'currency': this.marketId (symbol), + }; + const response = await this.publicGetStats (this.extend (request, params)); + const ticker = this.safeValue (response, 'stats', {}); + const last = this.safeFloat (ticker, 'last_price'); + return { + 'symbol': symbol, + 'timestamp': undefined, + 'datetime': undefined, + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'daily_change'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'total_btc_traded'), + 'info': ticker, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + return [ + this.parse8601 (ohlcv['date'] + ' 00:00:00'), + undefined, + undefined, + undefined, + this.safeFloat (ohlcv, 'price'), + undefined, + ]; + } + + async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { + const market = this.market (symbol); + const response = await this.publicGetHistoricalPrices (this.extend ({ + 'currency': market['id'], + 'timeframe': this.timeframes[timeframe], + }, params)); + const ohlcvs = this.toArray (this.omit (response['historical-prices'], 'request_currency')); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const id = this.safeString (trade, 'id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = undefined; + const side = this.safeString (trade, 'maker_type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTransactions (this.extend (request, params)); + const trades = this.toArray (this.omit (response['transactions'], 'request_currency')); + return this.parseTrades (trades, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + const request = { + 'side': side, + 'type': type, + 'currency': this.marketId (symbol), + 'amount': amount, + }; + if (type === 'limit') { + request['limit_price'] = price; + } + const result = await this.privatePostOrdersNew (this.extend (request, params)); + return { + 'info': result, + 'id': result, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostOrdersCancel (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + }; + const response = await this.privatePostWithdrawalsNew (this.extend (request, params)); + return { + 'info': response, + 'id': response['result']['uuid'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (this.id === 'cryptocapital') { + throw new ExchangeError (this.id + ' is an abstract base API for _1btcxe'); + } + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const query = this.extend ({ + 'api_key': this.apiKey, + 'nonce': this.nonce (), + }, params); + const request = this.json (query); + query['signature'] = this.hmac (this.encode (request), this.encode (this.secret)); + body = this.json (query); + headers = { 'Content-Type': 'application/json' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response === 'string') { + if (response.indexOf ('Maintenance') >= 0) { + throw new ExchangeNotAvailable (this.id + ' on maintenance'); + } + } + if ('errors' in response) { + let errors = []; + for (let e = 0; e < response['errors'].length; e++) { + const error = response['errors'][e]; + errors.push (error['code'] + ': ' + error['message']); + } + errors = errors.join (' '); + throw new ExchangeError (this.id + ' ' + errors); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/acx.js b/TaskServer/node_modules/ccxt/js/acx.js new file mode 100644 index 000000000..798d473da --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/acx.js @@ -0,0 +1,465 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class acx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'acx', + 'name': 'ACX', + 'countries': [ 'AU' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'withdraw': true, + 'fetchOrder': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '2h': '120', + '4h': '240', + '12h': '720', + '1d': '1440', + '3d': '4320', + '1w': '10080', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg', + 'extension': '.json', + 'api': 'https://acx.io/api', + 'www': 'https://acx.io', + 'doc': 'https://acx.io/documents/api_v2', + }, + 'api': { + 'public': { + 'get': [ + 'depth', // Get depth or specified market Both asks and bids are sorted from highest price to lowest. + 'k_with_pending_trades', // Get K data with pending trades, which are the trades not included in K data yet, because there's delay between trade generated and processed by K data generator + 'k', // Get OHLC(k line) of specific market + 'markets', // Get all available markets + 'order_book', // Get the order book of specified market + 'order_book/{market}', + 'tickers', // Get ticker of all markets + 'tickers/{market}', // Get ticker of specific market + 'timestamp', // Get server current time, in seconds since Unix epoch + 'trades', // Get recent trades on market, each trade is included only once Trades are sorted in reverse creation order. + 'trades/{market}', + ], + }, + 'private': { + 'get': [ + 'members/me', // Get your profile and accounts info + 'deposits', // Get your deposits history + 'deposit', // Get details of specific deposit + 'deposit_address', // Where to deposit The address field could be empty when a new address is generating (e.g. for bitcoin), you should try again later in that case. + 'orders', // Get your orders, results is paginated + 'order', // Get information of specified order + 'trades/my', // Get your executed trades Trades are sorted in reverse creation order. + 'withdraws', // Get your cryptocurrency withdraws + 'withdraw', // Get your cryptocurrency withdraw + ], + 'post': [ + 'orders', // Create a Sell/Buy order + 'orders/multi', // Create multiple sell/buy orders + 'orders/clear', // Cancel all my orders + 'order/delete', // Cancel an order + 'withdraw', // Create a withdraw + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': true, + 'withdraw': {}, // There is only 1% fee on withdrawals to your bank account. + }, + }, + 'exceptions': { + '2002': InsufficientFunds, + '2003': OrderNotFound, + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetMarkets (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = market['id']; + const symbol = market['name']; + let baseId = this.safeString (market, 'base_unit'); + let quoteId = this.safeString (market, 'quote_unit'); + if ((baseId === undefined) || (quoteId === undefined)) { + const ids = symbol.split ('/'); + baseId = ids[0].toLowerCase (); + quoteId = ids[1].toLowerCase (); + } + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + // todo: find out their undocumented precision and limits + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetMembersMe (params); + const balances = this.safeValue (response, 'accounts'); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 300 + } + const orderbook = await this.publicGetDepth (this.extend (request, params)); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'at'); + ticker = ticker['ticker']; + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + let symbol = id; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + let base = id.slice (0, 3); + let quote = id.slice (3, 6); + base = base.toUpperCase (); + quote = quote.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + symbol = base + '/' + quote; + } + result[symbol] = this.parseTicker (response[id], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTickersMarket (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': this.safeFloat (trade, 'price'), + 'amount': this.safeFloat (trade, 'volume'), + 'cost': this.safeFloat (trade, 'funds'), + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 500; // default is 30 + } + const request = { + 'market': market['id'], + 'period': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.publicGetK (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'done': 'closed', + 'wait': 'open', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const marketId = this.safeString (order, 'market'); + symbol = this.markets_by_id[marketId]['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + const id = this.safeString (order, 'id'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'amount': this.safeFloat (order, 'volume'), + 'filled': this.safeFloat (order, 'executed_volume'), + 'remaining': this.safeFloat (order, 'remaining_volume'), + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': parseInt (id), + }; + const response = await this.privateGetOrder (this.extend (request, params)); + return this.parseOrder (response); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'side': side, + 'volume': amount.toString (), + 'ord_type': type, + }; + if (type === 'limit') { + request['price'] = price.toString (); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const marketId = this.safeValue (response, 'market'); + const market = this.safeValue (this.markets_by_id, marketId); + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePostOrderDelete (this.extend (request, params)); + const order = this.parseOrder (response); + const status = order['status']; + if (status === 'closed' || status === 'canceled') { + throw new OrderNotFound (this.id + ' ' + this.json (order)); + } + return order; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // they have XRP but no docs on memo/tag + const request = { + 'currency': currency['id'], + 'sum': amount, + 'address': address, + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + // withdrawal response is undocumented + return { + 'info': response, + 'id': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + encodeParams (params) { + if ('orders' in params) { + const orders = params['orders']; + let query = this.urlencode (this.keysort (this.omit (params, 'orders'))); + for (let i = 0; i < orders.length; i++) { + const order = orders[i]; + const keys = Object.keys (order); + for (let k = 0; k < keys.length; k++) { + const key = keys[k]; + const value = order[key]; + query += '&orders%5B%5D%5B' + key + '%5D=' + value.toString (); + } + } + return query; + } + return this.urlencode (this.keysort (params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/api/' + this.version + '/' + this.implodeParams (path, params); + if ('extension' in this.urls) { + request += this.urls['extension']; + } + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const query = this.encodeParams (this.extend ({ + 'access_key': this.apiKey, + 'tonce': nonce, + }, params)); + const auth = method + '|' + request + '|' + query; + const signed = this.hmac (this.encode (auth), this.encode (this.secret)); + const suffix = query + '&signature=' + signed; + if (method === 'GET') { + url += '?' + suffix; + } else { + body = suffix; + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code === 400) { + const error = this.safeValue (response, 'error'); + const errorCode = this.safeString (error, 'code'); + const feedback = this.id + ' ' + this.json (response); + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + // fallback to default error handler + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/adara.js b/TaskServer/node_modules/ccxt/js/adara.js new file mode 100644 index 000000000..96da8e0ea --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/adara.js @@ -0,0 +1,1254 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class adara extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'adara', + 'name': 'Adara', + 'countries': [ 'MT' ], + 'version': 'v1', + 'rateLimit': 1000, + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': true, + 'fetchCurrencies': true, + 'fetchOrderBooks': false, + 'createMarketOrder': false, + 'fetchDepositAddress': false, + 'fetchClosedOrders': true, + 'fetchMyTrades': false, + 'fetchOHLCV': false, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchTickers': true, + 'withdraw': false, + 'fetchDeposits': false, + 'fetchWithdrawals': false, + 'fetchTransactions': false, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'token': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/49189583-0466a780-f380-11e8-9248-57a631aad2d6.jpg', + 'api': 'https://api.adara.io', + 'www': 'https://adara.io', + 'doc': 'https://api.adara.io/v1', + 'fees': 'https://adara.io/fees', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'limits', + 'market', + 'marketDepth', + 'marketInfo', + 'orderBook', + 'quote/', + 'quote/{id}', + 'symbols', + 'trade', + ], + 'post': [ + 'confirmContactEmail', + 'restorePassword', + 'user', // sign up + ], + }, + 'private': { + 'get': [ + 'balance', + 'order', + 'order/{id}', + 'currencyBalance', + 'apiKey', // the list of apiKeys + 'user/{id}', + ], + 'post': [ + 'order', + 'recovery', + 'user', + 'apiKey', // sign in and optionally create an apiKey + 'contact', + ], + 'patch': [ + 'order/{id}', + 'user/{id}', // change password + 'customer', // update user info + ], + 'delete': [ + 'apiKey', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.001, + 'taker': 0.001, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'exceptions': { + 'exact': { + 'Insufficient funds': InsufficientFunds, + 'Amount is too small': InvalidOrder, + 'operation has invalid value': InvalidOrder, + "closed order can't be changed": InvalidOrder, + 'Order is not found': OrderNotFound, + 'AUTH': AuthenticationError, + 'You are not authorized': AuthenticationError, + 'Bad Request': BadRequest, + '500': ExchangeError, + }, + 'broad': {}, + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { + 'include': 'from,to', + }; + const response = await this.publicGetSymbols (this.extend (request, params)); + const included = this.safeValue (response, 'included', []); + const includedByType = this.groupBy (included, 'type'); + const currencies = this.safeValue (includedByType, 'currency', []); + const currenciesById = this.indexBy (currencies, 'id'); + // + // { meta: { total: 61 }, + // data: [ { id: "XRPUSD", + // type: "symbol", + // attributes: { allowTrade: false, + // createdAt: "2018-10-23T09:31:06.830Z", + // digits: 5, + // fullName: "XRPUSD", + // makerFee: "0.0250", + // name: "XRPUSD", + // takerFee: "0.0250", + // updatedAt: "2018-10-23T09:31:06.830Z" }, + // relationships: { from: { data: { id: "XRP", type: "currency" } }, + // to: { data: { id: "USD", type: "currency" } } } }, + // { id: "XRPETH", + // type: "symbol", + // attributes: { allowTrade: true, + // createdAt: "2018-10-09T22:34:28.268Z", + // digits: 8, + // fullName: "XRPETH", + // makerFee: "0.0025", + // name: "XRPETH", + // takerFee: "0.0025", + // updatedAt: "2018-10-09T22:34:28.268Z" }, + // relationships: { from: { data: { id: "XRP", type: "currency" } }, + // to: { data: { id: "ETH", type: "currency" } } } } ], + // included: [ { id: "XRP", + // type: "currency", + // attributes: { accuracy: 4, + // active: true, + // allowDeposit: true, + // allowTrade: false, + // allowWallet: true, + // allowWithdraw: true, + // name: "Ripple", + // shortName: "XRP", + // transactionUriTemplate: "https://www.ripplescan.com/transactions/:txId", + // walletUriTemplate: "https://www.ripplescan.com/accounts/:address", + // withdrawFee: "0.20000000", + // withdrawMinAmount: "22.00000000" }, + // relationships: { } }, + // { id: "ETH", + // type: "currency", + // attributes: { accuracy: 8, + // active: true, + // allowDeposit: true, + // allowTrade: true, + // allowWallet: true, + // allowWithdraw: true, + // name: "Ethereum", + // shortName: "ETH", + // transactionUriTemplate: "https://etherscan.io/tx/:txId", + // walletUriTemplate: "https://etherscan.io/address/:address", + // withdrawFee: "0.00800000", + // withdrawMinAmount: "0.02000000" }, + // relationships: { } }, + // { id: "USD", + // type: "currency", + // attributes: { accuracy: 6, + // active: true, + // allowDeposit: false, + // allowTrade: true, + // allowWallet: false, + // allowWithdraw: false, + // name: "USD", + // shortName: "USD", + // transactionUriTemplate: null, + // walletUriTemplate: null, + // withdrawFee: "0.00000000", + // withdrawMinAmount: "0.00000000" }, + // relationships: { } } ] } + // + const result = []; + const markets = response['data']; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const attributes = this.safeValue (market, 'attributes', {}); + const relationships = this.safeValue (market, 'relationships', {}); + const fromRelationship = this.safeValue (relationships, 'from', {}); + const toRelationship = this.safeValue (relationships, 'to', {}); + const fromRelationshipData = this.safeValue (fromRelationship, 'data', {}); + const toRelationshipData = this.safeValue (toRelationship, 'data', {}); + const baseId = this.safeString (fromRelationshipData, 'id'); + const quoteId = this.safeString (toRelationshipData, 'id'); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + const baseCurrency = this.safeValue (currenciesById, baseId, {}); + const baseCurrencyAttributes = this.safeValue (baseCurrency, 'attributes', {}); + const symbol = base + '/' + quote; + const amountPrecision = this.safeInteger (baseCurrencyAttributes, 'accuracy', 8); + const pricePrecision = this.safeInteger (attributes, 'digits', 8); + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const active = this.safeValue (attributes, 'allowTrade'); + const maker = this.safeFloat (attributes, 'makerFee'); + const taker = this.safeFloat (attributes, 'takerFee'); + result.push ({ + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'maker': maker, + 'taker': taker, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { meta: { total: 22 }, + // data: [ { id: "USD", + // type: "currency", + // attributes: { accuracy: 6, + // active: true, + // allowDeposit: false, + // allowTrade: true, + // allowWallet: false, + // allowWithdraw: false, + // name: "USD", + // shortName: "USD", + // transactionUriTemplate: null, + // walletUriTemplate: null, + // withdrawFee: "0.00000000", + // withdrawMinAmount: "0.00000000" }, + // relationships: { } }, + // { id: "BTC", + // type: "currency", + // attributes: { accuracy: 8, + // active: true, + // allowDeposit: true, + // allowTrade: true, + // allowWallet: true, + // allowWithdraw: true, + // name: "Bitcoin", + // shortName: "BTC", + // transactionUriTemplate: "https://blockexplorer.com/tx/:txId", + // walletUriTemplate: "https://blockexplorer.com/address/:address", + // withdrawFee: "0.00050000", + // withdrawMinAmount: "0.00200000" }, + // relationships: { } } ], + // included: [] } + // + const currencies = response['data']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'id'); + const attributes = this.safeValue (currency, 'attributes', {}); + const code = this.commonCurrencyCode (id); + const precision = this.safeInteger (attributes, 'accuracy'); + const fee = this.safeFloat (attributes, 'withdrawFee'); + const active = this.safeValue (attributes, 'active'); + const allowDeposit = this.safeValue (attributes, 'allowDeposit'); + const allowWithdraw = this.safeValue (attributes, 'allowWithdraw'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': this.safeString (attributes, 'name'), + 'active': (active && allowDeposit && allowWithdraw), + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (attributes, 'withdrawMinAmount'), + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + // + // { data: [ { type: "balance", + // id: "U4f0f0940-39bf-45a8-90bc-12d2899db4f1_BALANCE_FOR_ETH", + // attributes: { totalBalance: 10000, + // onOrders: 0, + // normalizedTotalBalance: 310, + // normalizedOnOrders: 0, + // percentage: 3.004116443856034, + // serializedAt: 1543324487949 }, + // relationships: { currency: { data: { type: "currency", id: "ETH" } }, + // normalizedCurrency: { data: { type: "currency", id: "BTC" } } } } ], + // included: [ { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543324487948 }, + // relationships: { } }, + // { type: "currency", + // id: "ETH", + // attributes: { name: "Ethereum", + // shortName: "ETH", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543324487948 } } ] } + // + const result = { 'info': response }; + const data = this.safeValue (response, 'data'); + if (data !== undefined) { + for (let i = 0; i < data.length; i++) { + const balance = data[i]; + const attributes = this.safeValue (balance, 'attributes', {}); + const relationships = this.safeValue (balance, 'relationships', {}); + const currencyRelationship = this.safeValue (relationships, 'currency', {}); + const currencyRelationshipData = this.safeValue (currencyRelationship, 'data'); + const currencyId = this.safeString (currencyRelationshipData, 'id'); + const code = this.commonCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (attributes, 'totalBalance'); + account['used'] = this.safeFloat (attributes, 'onOrders'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + getSymbolFromMarketId (marketId, market = undefined) { + if (marketId === undefined) { + return undefined; + } + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + return market['symbol']; + } + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + return base + '/' + quote; + } + + parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 'price', amountKey = 'amount') { + const bids = []; + const asks = []; + const numBidAsks = orderbook.length; + if (numBidAsks > 0) { + timestamp = this.safeInteger (orderbook[0]['attributes'], 'serializedAt'); + } + for (let i = 0; i < orderbook.length; i++) { + const bidask = orderbook[i]; + const attributes = this.safeValue (bidask, 'attributes', {}); + const currenTimestamp = this.safeInteger (attributes, 'serializedAt'); + timestamp = Math.max (timestamp, currenTimestamp); + const id = this.safeString (bidask, 'id'); + if (id.indexOf ('OBID') >= 0) { + bids.push (this.parseBidAsk (bidask['attributes'], priceKey, amountKey)); + } else if (id.indexOf ('OSID') >= 0) { + asks.push (this.parseBidAsk (bidask['attributes'], priceKey, amountKey)); + } else { + throw new ExchangeError (this.id + ' parseOrderBook encountered an unrecognized bidask format: ' + this.json (bidask)); + } + } + return { + 'bids': this.sortBy (bids, 0, true), + 'asks': this.sortBy (asks, 0), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const filters = 'filters[symbol]'; + const request = {}; + request[filters] = market['id']; + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { data: [ { type: "orderBook", + // id: "OBID0SLTCETHS0", + // attributes: { price: 1, + // amount: 4, + // serializedAt: 1543116143473 } }, + // { type: "orderBook", + // id: "OSID3SLTCETHS0", + // attributes: { price: 12, + // amount: 12, + // serializedAt: 1543116143474 } } ] } + // + return this.parseOrderBook (response['data'], undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseTicker (ticker, market = undefined) { + // + // { type: "quote", + // id: "XRPETH", + // attributes: { currentPrice: 1, + // low: 1, + // high: 1, + // baseVolume: 0, + // quoteVolume: 0, + // change: 0, + // percentChange: 0, + // serializedAt: 1543109275996 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } + // + const symbol = this.getSymbolFromMarketId (this.safeString (ticker, 'id'), market); + const attributes = this.safeValue (ticker, 'attributes', {}); + const timestamp = this.safeInteger (attributes, 'serializedAt'); + const last = this.safeFloat (attributes, 'currentPrice'); + const change = this.safeFloat (attributes, 'change'); + let open = undefined; + if (change !== undefined) { + if (last !== undefined) { + open = last - change; + } + } + const percentage = this.safeFloat (attributes, 'percentChange'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (attributes, 'high'), + 'low': this.safeFloat (attributes, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetQuote (params); + const data = this.safeValue (response, 'data', []); + // + // { data: [ { type: "quote", + // id: "XRPETH", + // attributes: { currentPrice: 1, + // low: 1, + // high: 1, + // baseVolume: 0, + // quoteVolume: 0, + // change: 0, + // percentChange: 0, + // serializedAt: 1543109275996 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } ], + // included: [ { type: "currency", + // id: "XRP", + // attributes: { name: "Ripple", + // shortName: "XRP", + // active: true, + // accuracy: 4, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543109275996 }, + // relationships: { } }, + // { type: "symbol", + // id: "XRPETH", + // attributes: { fullName: "XRPETH", + // digits: 8, + // allowTrade: true, + // serializedAt: 1543109275996 }, + // relationships: { from: { data: { type: "currency", id: "XRP" } }, + // to: { data: { type: "currency", id: "ETH" } } } } ] } + // + const result = {}; + for (let t = 0; t < data.length; t++) { + const ticker = this.parseTicker (data[t]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetQuoteId (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "ETH", + // attributes: { name: "Ethereum", + // shortName: "ETH", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543111444033 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543111444033 } }, + // { type: "symbol", + // id: "ETHBTC", + // attributes: { fullName: "ETHBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543111444033 }, + // relationships: { from: { data: { type: "currency", id: "ETH" } }, + // to: { data: { type: "currency", id: "BTC" } } } } ], + // data: { type: "quote", + // id: "ETHBTC", + // attributes: { currentPrice: 34, + // low: 34, + // high: 34, + // baseVolume: 0, + // quoteVolume: 0, + // change: 0, + // percentChange: 0, + // serializedAt: 1543111444033 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } } (fetchTicker @ adara.js:546) + // + return this.parseTicker (response['data']); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades + // + // + // { type: "trade", + // id: "1542988964359136846136847", + // attributes: { price: 34, + // amount: 4, + // total: 136, + // operation: "buy", + // createdAt: "2018-11-23T16:02:44.359Z", + // serializedAt: 1543112364995 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } ], + // + const id = this.safeString (trade, 'id', 'uuid'); + const attributes = this.safeValue (trade, 'attributes', {}); + const relationships = this.safeValue (trade, 'relationships', {}); + const symbolRelationship = this.safeValue (relationships, 'symbol', {}); + const symbolRelationshipData = this.safeValue (symbolRelationship, 'data', {}); + const marketId = this.safeString (symbolRelationshipData, 'id'); + market = this.safeValue (this.markets_by_id, marketId, market); + let symbol = undefined; + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else if (marketId !== undefined) { + const baseIdLength = marketId.length - 3; + const baseId = marketId.slice (0, baseIdLength); + const quoteId = marketId.slice (baseIdLength); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const orderId = undefined; + const timestamp = this.parse8601 (this.safeString (attributes, 'createdAt')); + const side = this.safeString (attributes, 'operation'); + const price = this.safeFloat (attributes, 'price'); + const amount = this.safeFloat (attributes, 'amount'); + let cost = this.safeFloat (attributes, 'total'); + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + } + const feeCost = this.safeFloat (attributes, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'id': id, + 'info': trade, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + // 'id': market['id'], + }; + const response = await this.publicGetTrade (this.extend (request, params)); + // + // { data: [ { type: "trade", + // id: "1542988964359136846136847", + // attributes: { price: 34, + // amount: 4, + // total: 136, + // operation: "buy", + // createdAt: "2018-11-23T16:02:44.359Z", + // serializedAt: 1543112364995 }, + // relationships: { symbol: { data: { type: "symbol", id: "ETHBTC" } } } } ], + // included: [ { type: "currency", + // id: "ETH", + // attributes: { name: "Ethereum", + // shortName: "ETH", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543112364995 }, + // relationships: { } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // ... + // serializedAt: 1543112364995 }, + // relationships: { } }, + // { type: "symbol", + // id: "ETHBTC", + // attributes: { fullName: "ETHBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543112364995 }, + // relationships: { from: { data: { type: "currency", id: "ETH" } }, + // to: { data: { type: "currency", id: "BTC" } } } } } + // + return this.parseTrades (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'data': { + 'type': 'order', + 'attributes': { + 'amount': parseFloat (this.amountToPrecision (symbol, amount)), + 'operation': side, + 'orderType': type, + 'price': parseFloat (this.priceToPrecision (symbol, price)), + }, + 'relationships': { + 'symbol': { + 'data': { + 'id': market['id'], + 'type': 'symbol', + }, + }, + }, + }, + 'included': [ + { + 'id': market['id'], + 'type': 'symbol', + }, + ], + }; + const response = await this.privatePostOrder (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543434477449 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543434477449 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543434477449 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } } ], + // data: { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543434477449, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 220, + // price: 0.000035, + // averagePrice: 0, + // fee: 0, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: null, + // status: "open", + // filled: 0, + // flags: [] }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } } } } } + // + return this.parseOrder (response['data']); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + 'data': { + 'attributes': { + 'status': 'canceled', + }, + }, + }; + const response = await this.privatePatchOrderId (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543437874742 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543437874742 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543437874742 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } } ], + // data: { type: "order", + // id: "34794", + // attributes: { serializedAt: 1543437874742, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 110, + // price: 0.000034, + // averagePrice: 0, + // fee: 0, + // timeOpen: "2018-11-28T20:42:35.486Z", + // timeClose: null, + // status: "canceled", + // filled: 0, + // flags: [] }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } } } } } + // + return this.parseOrder (response['data']); + } + + parseOrderStatus (status) { + const statuses = { + 'open': 'open', + 'closed': 'closed', + 'canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543435013349, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 220, + // price: 0.000035, + // averagePrice: 0.000035, + // fee: 0.0001925, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: "2018-11-28T19:47:57.452Z", + // status: "closed", + // filled: 220, + // flags: null }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } } } } + // + const id = this.safeString (order, 'id'); + const attributes = this.safeValue (order, 'attributes', {}); + const relationships = this.safeValue (order, 'relationships', {}); + const symbolRelationship = this.safeValue (relationships, 'symbol', {}); + const symbolRelationshipData = this.safeValue (symbolRelationship, 'data', {}); + const tradesRelationship = this.safeValue (relationships, 'trades', {}); + const tradesRelationshipData = this.safeValue (tradesRelationship, 'data'); + const marketId = this.safeString (symbolRelationshipData, 'id'); + market = this.safeValue (this.markets_by_id, marketId, market); + let feeCurrency = undefined; + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else if (marketId !== undefined) { + const baseIdLength = marketId.length - 3; + const baseId = marketId.slice (0, baseIdLength); + const quoteId = marketId.slice (baseIdLength); + const base = this.commonCurrencyCode (baseId); + const quote = this.commonCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const timestamp = this.parse8601 (this.safeString (attributes, 'timeOpen')); + const side = this.safeString (attributes, 'operation'); + const type = this.safeString (attributes, 'orderType'); + const status = this.parseOrderStatus (this.safeString (attributes, 'status')); + const lastTradeTimestamp = this.parse8601 (this.safeString (attributes, 'timeClose')); + const price = this.safeFloat (attributes, 'price'); + const amount = this.safeFloat (attributes, 'amount'); + const filled = this.safeFloat (attributes, 'filled'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + let cost = undefined; + const average = this.safeFloat (attributes, 'averagePrice'); + if (cost === undefined) { + if ((average !== undefined) && (filled !== undefined)) { + cost = parseFloat (this.costToPrecision (symbol, average * filled)); + } + } + let fee = undefined; + const feeCost = this.safeFloat (attributes, 'fee'); + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + } + let trades = undefined; + if (tradesRelationshipData !== undefined) { + const numTrades = tradesRelationshipData.length; + if (numTrades > 0) { + trades = this.parseTrades (tradesRelationshipData, market); + } + } + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + return result; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'include': 'trades', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + const filters = 'filters[symbol]'; + request[filters] = market['id']; + } + const response = await this.privateGetOrder (this.extend (request, params)); + // + // { data: [ { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543436770259, + // operation: "buy", + // orderType: "limit", + // amount: 220, + // price: 0.000035, + // averagePrice: 0.000035, + // fee: 0.0001925, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: "2018-11-28T19:47:57.452Z", + // status: "closed", + // filled: 220, + // flags: null }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } }, + // trades: { data: [{ type: "trade", id: "34789_34793" }] } } } ], + // included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543436770259 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543436770259 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543436770259 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } }, + // { type: "trade", + // id: "34789_34793", + // attributes: { fee: 0.0001925, + // price: 0.000035, + // amount: 220, + // total: 0.0077, + // operation: "buy", + // createdAt: "2018-11-28T19:47:57.451Z" } } ], + // meta: { total: 1 } } + // + return this.parseOrdersResponse (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const filters = 'filters[status][]'; + const request = {}; + request[filters] = 'open'; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const filters = 'filters[status][]'; + const request = {}; + request[filters] = 'closed'; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + parseOrdersResponse (response, market = undefined, since = undefined, limit = undefined) { + const included = this.safeValue (response, 'included', []); + const includedByType = this.groupBy (included, 'type'); + const unparsedTrades = this.safeValue (includedByType, 'trade', []); + const trades = this.parseTrades (unparsedTrades, market); + const tradesById = this.indexBy (trades, 'id'); + const orders = this.parseOrders (this.safeValue (response, 'data', []), market, since, limit); + const result = []; + for (let i = 0; i < orders.length; i++) { + const order = orders[i]; + const orderTrades = []; + const orderFee = this.safeValue (order, 'fee', {}); + const orderFeeCurrency = this.safeString (orderFee, 'currency'); + if (order['trades'] !== undefined) { + for (let j = 0; j < order['trades'].length; j++) { + const orderTrade = order['trades'][j]; + const orderTradeId = orderTrade['id']; + if (orderTradeId in tradesById) { + orderTrades.push (this.deepExtend (tradesById[orderTradeId], { + 'order': order['id'], + 'type': order['type'], + 'symbol': order['symbol'], + 'fee': { + 'currency': orderFeeCurrency, + }, + })); + } + } + } + const numOrderTrades = orderTrades.length; + if (numOrderTrades > 0) { + order['trades'] = orderTrades; + } + result.push (order); + } + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + 'include': 'trades', + }; + const response = await this.privateGetOrderId (this.extend (request, params)); + // + // { included: [ { type: "currency", + // id: "XLM", + // attributes: { name: "Stellar", + // shortName: "XLM", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: false, + // serializedAt: 1543436451996 } }, + // { type: "currency", + // id: "BTC", + // attributes: { name: "Bitcoin", + // shortName: "BTC", + // active: true, + // accuracy: 8, + // allowDeposit: true, + // allowWithdraw: true, + // allowWallet: true, + // allowTrade: true, + // serializedAt: 1543436451996 } }, + // { type: "symbol", + // id: "XLMBTC", + // attributes: { fullName: "XLMBTC", + // digits: 6, + // allowTrade: true, + // serializedAt: 1543436451996 }, + // relationships: { from: { data: { type: "currency", id: "XLM" } }, + // to: { data: { type: "currency", id: "BTC" } } } }, + // { type: "trade", + // id: "34789_34793", + // attributes: { fee: 0.0001925, + // price: 0.000035, + // amount: 220, + // total: 0.0077, + // operation: "buy", + // createdAt: "2018-11-28T19:47:57.451Z" } } ], + // data: { type: "order", + // id: "34793", + // attributes: { serializedAt: 1543436451996, + // operation: "buy", + // orderType: "limit", + // clientId: "4733ea40-7d5c-4ddc-aec5-eb41baf90555", + // amount: 220, + // price: 0.000035, + // averagePrice: 0.000035, + // fee: 0.0001925, + // timeOpen: "2018-11-28T19:47:57.435Z", + // timeClose: "2018-11-28T19:47:57.452Z", + // status: "closed", + // filled: 220, + // flags: null }, + // relationships: { symbol: { data: { type: "symbol", id: "XLMBTC" } }, + // trades: { data: [{ type: "trade", id: "34789_34793" }] } } } } + // + const data = this.safeValue (response, 'data'); + response['data'] = []; + response['data'].push (data); + const orders = this.parseOrdersResponse (response); + const ordersById = this.indexBy (orders, 'id'); + if (id in ordersById) { + return ordersById[id]; + } + throw new OrderNotFound (this.id + ' fetchOrder could not find order id ' + id.toString ()); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let payload = '/' + this.implodeParams (path, params); + let url = this.urls['api'] + '/' + this.version + payload; + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + if (api === 'private') { + const nonce = this.nonce (); + let expiredAt = this.sum (nonce, this.safeInteger (this.options, 'expiredAt', 10000)); + expiredAt = expiredAt.toString (); + if ((method === 'POST') || (method === 'PATCH')) { + body = this.json (query); + payload = body; + } + if (this.token) { + headers = { + 'Cookie': 'token=' + this.token, + }; + } else { + this.checkRequiredCredentials (); + const auth = method + payload + 'expiredAt=' + expiredAt; + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512', 'base64'); + headers = { + 'X-ADX-EXPIRE': expiredAt, + 'X-ADX-APIKEY': this.apiKey, + 'X-ADX-SIGNATURE': signature, + }; + } + if (method !== 'GET') { + headers['Content-Type'] = 'application/json'; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const errors = this.safeValue (response, 'errors', []); + const numErrors = errors.length; + if (numErrors > 0) { + const error = errors[0]; + const code = this.safeString (error, 'code'); + const status = this.safeString (error, 'status'); + const title = this.safeString (error, 'title'); + const detail = this.safeString (error, 'detail'); + const feedback = this.id + ' ' + this.json (response); + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], status, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], title, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], detail, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/anxpro.js b/TaskServer/node_modules/ccxt/js/anxpro.js new file mode 100644 index 000000000..d27fb71aa --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/anxpro.js @@ -0,0 +1,1216 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, BadRequest, OrderNotFound, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class anxpro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'anxpro', + 'name': 'ANXPro', + 'countries': [ 'JP', 'SG', 'HK', 'NZ' ], + 'rateLimit': 1500, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + 'fetchCurrencies': true, + 'fetchOHLCV': false, + 'fetchTrades': false, + 'fetchOpenOrders': true, + 'fetchDepositAddress': true, + 'fetchTransactions': true, + 'fetchMyTrades': true, + 'createDepositAddress': false, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27765983-fd8595da-5ec9-11e7-82e3-adb3ab8c2612.jpg', + 'api': { + 'public': 'https://anxpro.com/api/2', + 'private': 'https://anxpro.com/api/2', + 'v3public': 'https://anxpro.com/api/3', + 'v3private': 'https://anxpro.com/api/3', + }, + 'www': 'https://anxpro.com', + 'doc': [ + 'https://anxv2.docs.apiary.io', + 'https://anxv3.docs.apiary.io', + 'https://anxpro.com/pages/api', + ], + }, + 'api': { + 'v3public': { + 'get': [ + 'currencyStatic', + ], + }, + 'v3private': { + 'post': [ + 'register/register', + 'register/verifyRegistration', + 'register/resendVerification', + 'register/autoRegister', + 'account', + 'subaccount/new', + 'transaction/list', + 'order/list', + 'trade/list', + 'send', + 'receive', + 'receive/create', + 'batch/new', + 'batch/add', + 'batch/list', + 'batch/info', + 'batch/closeForSend', + 'order/new', + 'order/info', + 'order/cancel', + 'retail/quote', + 'retail/trade', + 'validateAddress', + 'address/check', + 'alert/create', + 'alert/delete', + 'alert/list', + 'kyc/personal', + 'kyc/document', + 'kyc/status', + 'kyc/verifyCode', + 'news/list', + 'press/list', + 'announcements/list', + 'apiDoc/list', + ], + }, + 'public': { + 'get': [ + '{currency_pair}/money/ticker', + '{currency_pair}/money/depth/full', + '{currency_pair}/money/trade/fetch', // disabled by ANXPro + ], + }, + 'private': { + 'post': [ + '{currency_pair}/money/order/add', + '{currency_pair}/money/order/cancel', + '{currency_pair}/money/order/quote', + '{currency_pair}/money/order/result', + '{currency_pair}/money/orders', + 'money/{currency}/address', + 'money/{currency}/send_simple', + 'money/info', + 'money/trade/list', + 'money/wallet/history', + ], + }, + }, + 'httpExceptions': { + '403': AuthenticationError, + }, + 'exceptions': { + 'exact': { + // v2 + 'Insufficient Funds': InsufficientFunds, + 'Trade value too small': InvalidOrder, + 'The currency pair is not supported': BadRequest, + 'Order amount is too low': InvalidOrder, + 'Order amount is too high': InvalidOrder, + 'order rate is too low': InvalidOrder, + 'order rate is too high': InvalidOrder, + 'Too many open orders': InvalidOrder, + 'Unexpected error': ExchangeError, + 'Order Engine is offline': ExchangeNotAvailable, + 'No executed order with that identifer found': OrderNotFound, + 'Unknown server error, please contact support.': ExchangeError, + 'Not available': ExchangeNotAvailable, // { "status": "Not available" } + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + }, + 'options': { + 'fetchMyTradesMethod': 'private_post_money_trade_list', // or 'v3private_post_trade_list' + }, + }); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + // todo: migrate this to fetchLedger + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['from'] = since; + } + if (limit !== undefined) { + request['max'] = limit; + } + const currency = (code === undefined) ? undefined : this.currency (code); + if (currency !== undefined) { + request['ccy'] = currency['id']; + } + const response = await this.v3privatePostTransactionList (this.extend (request, params)); + // + // { + // transactions: [ + // { + // transactionClass: 'COIN', + // uuid: '7896857b-2ed6-4c62-ba4c-619837438d9c', + // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', + // amount: -17865.72689976, + // fee: 1, + // balanceBefore: 17865.72689976, + // balanceAfter: 17865.72689976, + // ccy: 'XRP', + // transactionState: 'PROCESSED', + // transactionType: 'WITHDRAWAL', + // received: '1551357946000', + // processed: '1551357946000', + // timestampMillis: '1557441435932', + // displayTitle: 'Coin Withdrawal', + // displayDescription: 'Withdraw to: rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345', + // coinAddress: 'rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345', + // coinTransactionId: '68444611753E9D8F5C33DCBBF43F01391070F79CAFCF7625397D1CEFA519064A', + // subAccount: [ + // Object + // ] + // }, + // { + // transactionClass: 'FILL', + // uuid: 'a5ae54de-c14a-4ef8-842d-56000c9dc7ab', + // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', + // amount: 0.09006364, + // fee: 0.00018013, + // balanceBefore: 0.3190001, + // balanceAfter: 0.40888361, + // ccy: 'BTC', + // transactionState: 'PROCESSED', + // transactionType: 'FILL_CREDIT', + // received: '1551357057000', + // processed: '1551357057000', + // timestampMillis: '1557441435956', + // displayTitle: 'Order Fill', + // displayDescription: 'Buy BTC @ 3008.53930 EUR/BTC' + // } + // ], + // count: ..., + // timestamp: '1557441435971', + // resultCode: 'OK' + // } + // + const transactions = this.safeValue (response, 'transactions', []); + const grouped = this.groupBy (transactions, 'transactionType', []); + const depositsAndWithdrawals = this.arrayConcat (this.safeValue (grouped, 'DEPOSIT', []), this.safeValue (grouped, 'WITHDRAWAL', [])); + return this.parseTransactions (depositsAndWithdrawals, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // withdrawal + // + // { + // transactionClass: 'COIN', + // uuid: 'bff91938-4dad-4c48-9db6-468324ce96c1', + // userUuid: '82027ee9-cb59-4f29-80d6-f7e793f39ad4', + // amount: -0.40888361, + // fee: 0.002, + // balanceBefore: 0.40888361, + // balanceAfter: 0.40888361, + // ccy: 'BTC', + // transactionState: 'PROCESSED', + // transactionType: 'WITHDRAWAL', + // received: '1551357156000', + // processed: '1551357156000', + // timestampMillis: '1557441846213', + // displayTitle: 'Coin Withdrawal', + // displayDescription: 'Withdraw to: 1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', + // coinAddress: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', + // coinTransactionId: + // 'ab80abcb62bf6261ebc827c73dd59a4ce15d740b6ba734af6542f43b6485b923', + // subAccount: { + // uuid: '652e1add-0d0b-462c-a03c-d6197c825c1a', + // name: 'DEFAULT' + // } + // } + // + // deposit + // + // { + // "transactionClass": "COIN", + // "uuid": "eb65576f-c1a8-423c-8e2f-fa50109b2eab", + // "userUuid": "82027ee9-cb59-4f29-80d6-f7e793f39ad4", + // "amount": 3.99287184, + // "fee": 0, + // "balanceBefore": 8.39666034, + // "balanceAfter": 12.38953218, + // "ccy": "ETH", + // "transactionState": "PROCESSED", + // "transactionType": "DEPOSIT", + // "received": "1529420056000", + // "processed": "1529420766000", + // "timestampMillis": "1557442743854", + // "displayTitle": "Coin Deposit", + // "displayDescription": "Deposit to: 0xf123aa44fadea913a7da99cc2ee202db684ce0e3", + // "coinTransactionId": "0x33a3e5ea7c034dc5324a88aa313962df0a5d571ab4bcc3cb00b876b1bdfc54f7", + // "coinConfirmations": 51, + // "coinConfirmationsRequired": 45, + // "subAccount": {"uuid": "aba1de05-c7c6-49d7-84ab-a6aca0e827b6", "name": "DEFAULT"} + // } + // + const timestamp = this.safeInteger (transaction, 'received'); + const updated = this.safeInteger (transaction, 'processed'); + const transactionType = this.safeString (transaction, 'transactionType'); + let type = undefined; + let amount = this.safeFloat (transaction, 'amount'); + let address = this.safeString (transaction, 'coinAddress'); + let tag = undefined; + if (transactionType === 'WITHDRAWAL') { + type = 'withdrawal'; + amount = -amount; + if (address) { + // xrp: "coinAddress": "rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg?dt=3750180345", + if (address.indexOf ('?dt=') >= 0) { + const parts = address.split ('?dt='); + address = parts[0]; + tag = parts[1]; + } + } + } else if (transactionType === 'DEPOSIT') { + if (!address) { + const displayDescription = this.safeString (transaction, 'displayDescription'); + const addressText = displayDescription.replace ('Deposit to: ', ''); + if (addressText.length > 0) { + // eth: "displayDescription": "Deposit to: 0xf123aa44fadea913a7da99cc2ee202db684ce0e3", + // xrp: "displayDescription": "Deposit to: rUjxty1WWLwX1evhKf3C2XNZDMcXEZ9ToJ?dt=504562345", + if (addressText.indexOf ('?dt=') >= 0) { + const parts = addressText.split ('?dt='); + address = parts[0]; + tag = parts[1]; + } else { + address = addressText; + } + } + } + type = 'deposit'; + } + const currencyId = this.safeString (transaction, 'ccy'); + const code = this.safeCurrencyCode (currencyId); + const transactionState = this.safeString (transaction, 'transactionState'); + const status = this.parseTransactionStatus (transactionState); + const feeCost = this.safeFloat (transaction, 'fee'); + const netAmount = amount - feeCost; + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'id': this.safeString (transaction, 'uuid'), + 'currency': code, + 'amount': netAmount, + 'address': address, + 'tag': tag, + 'status': status, + 'type': type, + 'updated': updated, + 'txid': this.safeString (transaction, 'coinTransactionId'), + 'fee': { + 'cost': feeCost, + 'currency': code, + }, + 'info': transaction, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'PROCESSED': 'ok', + 'REVERSED': 'canceled', + 'CANCELLED_INSUFFICIENT_FUNDS': 'canceled', + 'CANCELLED_LIMIT_BREACH': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // + // v2 + // + // { + // result: 'success', + // data: [ + // { + // tradeId: 'c2ed821d-717a-4b7e-beb0-a9ba60e8f5a0', + // orderId: '5a65ae21-c7a8-4009-b3af-306c2ad21a02', + // timestamp: '1551357057000', + // tradedCurrencyFillAmount: '0.09006364', + // settlementCurrencyFillAmount: '270.96', + // settlementCurrencyFillAmountUnrounded: '270.96000000', + // price: '3008.53930', + // ccyPair: 'BTCEUR', + // side: 'BUY' // missing in v3 + // }, + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD', + // side: 'BUY' // missing in v3 + // }, + // ] + // } + // + // v3 + // + // { + // trades: [ + // { + // tradeId: 'c2ed821d-717a-4b7e-beb0-a9ba60e8f5a0', + // orderId: '5a65ae21-c7a8-4009-b3af-306c2ad21a02', + // timestamp: '1551357057000', + // tradedCurrencyFillAmount: '0.09006364', + // settlementCurrencyFillAmount: '270.96', + // settlementCurrencyFillAmountUnrounded: '270.96000000', + // price: '3008.53930', + // ccyPair: 'BTCEUR' + // }, + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD' + // }, + // ], + // count: 3, + // timestamp: '1557438456732', + // resultCode: 'OK' + // } + // + const request = {}; + if (limit !== undefined) { + request['max'] = limit; + } + const method = this.safeString (this.options, 'fetchMyTradesMethod', 'private_post_money_trade_list'); + const response = await this[method] (this.extend (request, params)); + const trades = this.safeValue2 (response, 'trades', 'data', []); + const market = (symbol === undefined) ? undefined : this.market (symbol); + return this.parseTrades (trades, market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // v2 + // + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD', + // side: 'BUY', // missing in v3 + // } + // + // v3 + // + // { + // tradeId: 'fc0d3a9d-8b0b-4dff-b2e9-edd160785210', + // orderId: '8161ae6e-251a-4eed-a56f-d3d6555730c1', + // timestamp: '1551357033000', + // tradedCurrencyFillAmount: '0.06521746', + // settlementCurrencyFillAmount: '224.09', + // settlementCurrencyFillAmountUnrounded: '224.09000000', + // price: '3436.04305', + // ccyPair: 'BTCUSD' + // } + // + const id = this.safeString (trade, 'tradeId'); + const orderId = this.safeString (trade, 'orderId'); + const timestamp = this.safeInteger (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'tradedCurrencyFillAmount'); + const cost = this.safeFloat (trade, 'settlementCurrencyFillAmount'); + const side = this.safeStringLower (trade, 'side'); + let symbol = undefined; + const marketId = this.safeString (trade, 'ccyPair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + return { + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + 'info': trade, + }; + } + + async fetchCurrencies (params = {}) { + const response = await this.v3publicGetCurrencyStatic (params); + // + // { + // "currencyStatic": { + // "currencies": { + // "HKD": { + // "decimals": 2, + // "minOrderSize": 1.00000000, + // "maxOrderSize": 10000000000.00000000, + // "displayDenominator": 1, + // "summaryDecimals": 0, + // "displayUnit": "HKD", + // "symbol": "$", + // "type": "FIAT", + // "engineSettings": { + // "depositsEnabled": false, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 1.00000000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 36000.00000000, + // "maxMarketOrderSize": 36000.00000000, + // "assetDivisibility": 0 + // }, + // "ETH": { + // "decimals": 8, + // "minOrderSize": 0.00010000, + // "maxOrderSize": 1000000000.00000000, + // "type": "CRYPTO", + // "confirmationThresholds": [ + // { "confosRequired": 30, "threshold": 0.50000000 }, + // { "confosRequired": 45, "threshold": 10.00000000 }, + // { "confosRequired": 70 } + // ], + // "networkFee": 0.00500000, + // "engineSettings": { + // "depositsEnabled": true, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 0.00010000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 10000000000.00000000, + // "maxMarketOrderSize": 1000000000.00000000, + // "digitalCurrencyType": "ETHEREUM", + // "assetDivisibility": 0, + // "assetIcon": "/images/currencies/crypto/ETH.svg" + // }, + // }, + // "currencyPairs": { + // "ETHUSD": { + // "priceDecimals": 5, + // "engineSettings": { + // "tradingEnabled": true, + // "displayEnabled": true, + // "cancelOnly": true, + // "verifyRequired": false, + // "restrictedBuy": false, + // "restrictedSell": false + // }, + // "minOrderRate": 10.00000000, + // "maxOrderRate": 10000.00000000, + // "displayPriceDecimals": 5, + // "tradedCcy": "ETH", + // "settlementCcy": "USD", + // "preferredMarket": "ANX", + // "chartEnabled": true, + // "simpleTradeEnabled": false + // }, + // }, + // }, + // "timestamp": "1549840691039", + // "resultCode": "OK" + // } + // + const currencyStatic = this.safeValue (response, 'currencyStatic', {}); + const currencies = this.safeValue (currencyStatic, 'currencies', {}); + const result = {}; + const ids = Object.keys (currencies); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = currencies[id]; + const code = this.safeCurrencyCode (id); + const engineSettings = this.safeValue (currency, 'engineSettings'); + const depositsEnabled = this.safeValue (engineSettings, 'depositsEnabled'); + const withdrawalsEnabled = this.safeValue (engineSettings, 'withdrawalsEnabled'); + const displayEnabled = this.safeValue (engineSettings, 'displayEnabled'); + const active = depositsEnabled && withdrawalsEnabled && displayEnabled; + const precision = this.safeInteger (currency, 'decimals'); + const fee = this.safeFloat (currency, 'networkFee'); + const type = this.safeStringLower (currency, 'type'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'type': type, + 'active': active, + 'precision': precision, + 'fee': fee, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minOrderSize'), + 'max': this.safeFloat (currency, 'maxOrderSize'), + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (currency, 'minOrderValue'), + 'max': this.safeFloat (currency, 'maxOrderValue'), + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.v3publicGetCurrencyStatic (params); + // + // { + // "currencyStatic": { + // "currencies": { + // "HKD": { + // "decimals": 2, + // "minOrderSize": 1.00000000, + // "maxOrderSize": 10000000000.00000000, + // "displayDenominator": 1, + // "summaryDecimals": 0, + // "displayUnit": "HKD", + // "symbol": "$", + // "type": "FIAT", + // "engineSettings": { + // "depositsEnabled": false, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 1.00000000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 36000.00000000, + // "maxMarketOrderSize": 36000.00000000, + // "assetDivisibility": 0 + // }, + // "ETH": { + // "decimals": 8, + // "minOrderSize": 0.00010000, + // "maxOrderSize": 1000000000.00000000, + // "type": "CRYPTO", + // "confirmationThresholds": [ + // { "confosRequired": 30, "threshold": 0.50000000 }, + // { "confosRequired": 45, "threshold": 10.00000000 }, + // { "confosRequired": 70 } + // ], + // "networkFee": 0.00500000, + // "engineSettings": { + // "depositsEnabled": true, + // "withdrawalsEnabled": true, + // "displayEnabled": true, + // "mobileAccessEnabled": true + // }, + // "minOrderValue": 0.00010000, + // "maxOrderValue": 10000000000.00000000, + // "maxMarketOrderValue": 10000000000.00000000, + // "maxMarketOrderSize": 1000000000.00000000, + // "digitalCurrencyType": "ETHEREUM", + // "assetDivisibility": 0, + // "assetIcon": "/images/currencies/crypto/ETH.svg" + // }, + // }, + // "currencyPairs": { + // "ETHUSD": { + // "priceDecimals": 5, + // "engineSettings": { + // "tradingEnabled": true, + // "displayEnabled": true, + // "cancelOnly": true, + // "verifyRequired": false, + // "restrictedBuy": false, + // "restrictedSell": false + // }, + // "minOrderRate": 10.00000000, + // "maxOrderRate": 10000.00000000, + // "displayPriceDecimals": 5, + // "tradedCcy": "ETH", + // "settlementCcy": "USD", + // "preferredMarket": "ANX", + // "chartEnabled": true, + // "simpleTradeEnabled": false + // }, + // }, + // }, + // "timestamp": "1549840691039", + // "resultCode": "OK" + // } + // + const currencyStatic = this.safeValue (response, 'currencyStatic', {}); + const currencies = this.safeValue (currencyStatic, 'currencies', {}); + const currencyPairs = this.safeValue (currencyStatic, 'currencyPairs', {}); + const result = []; + const ids = Object.keys (currencyPairs); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = currencyPairs[id]; + // + // "ETHUSD": { + // "priceDecimals": 5, + // "engineSettings": { + // "tradingEnabled": true, + // "displayEnabled": true, + // "cancelOnly": true, + // "verifyRequired": false, + // "restrictedBuy": false, + // "restrictedSell": false + // }, + // "minOrderRate": 10.00000000, + // "maxOrderRate": 10000.00000000, + // "displayPriceDecimals": 5, + // "tradedCcy": "ETH", + // "settlementCcy": "USD", + // "preferredMarket": "ANX", + // "chartEnabled": true, + // "simpleTradeEnabled": false + // }, + // + const baseId = this.safeString (market, 'tradedCcy'); + const quoteId = this.safeString (market, 'settlementCcy'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const baseCurrency = this.safeValue (currencies, baseId, {}); + const quoteCurrency = this.safeValue (currencies, quoteId, {}); + const precision = { + 'price': this.safeInteger (market, 'priceDecimals'), + 'amount': this.safeInteger (baseCurrency, 'decimals'), + }; + const engineSettings = this.safeValue (market, 'engineSettings'); + const displayEnabled = this.safeValue (engineSettings, 'displayEnabled'); + const tradingEnabled = this.safeValue (engineSettings, 'tradingEnabled'); + const active = displayEnabled && tradingEnabled; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'active': active, + 'limits': { + 'price': { + 'min': this.safeFloat (market, 'minOrderRate'), + 'max': this.safeFloat (market, 'maxOrderRate'), + }, + 'amount': { + 'min': this.safeFloat (baseCurrency, 'minOrderSize'), + 'max': this.safeFloat (baseCurrency, 'maxOrderSize'), + }, + 'cost': { + 'min': this.safeFloat (quoteCurrency, 'minOrderValue'), + 'max': this.safeFloat (quoteCurrency, 'maxOrderValue'), + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostMoneyInfo (params); + const balance = this.safeValue (response, 'data', {}); + const wallets = this.safeValue (balance, 'Wallets', {}); + const currencyIds = Object.keys (wallets); + const result = { 'info': balance }; + for (let c = 0; c < currencyIds.length; c++) { + const currencyId = currencyIds[c]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + const wallet = this.safeValue (wallets, currencyId); + account['free'] = this.safeFloat (wallet['Available_Balance'], 'value'); + account['total'] = this.safeFloat (wallet['Balance'], 'value'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currency_pair': this.marketId (symbol), + }; + const response = await this.publicGetCurrencyPairMoneyDepthFull (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data', {}); + const timestamp = this.safeIntegerProduct (orderbook, 'dataUpdateTime', 0.001); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'currency_pair': this.marketId (symbol), + }; + const response = await this.publicGetCurrencyPairMoneyTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'data', {}); + const timestamp = this.safeIntegerProduct (ticker, 'dataUpdateTime', 0.001); + const bid = this.safeFloat (ticker['buy'], 'value'); + const ask = this.safeFloat (ticker['sell'], 'value'); + const baseVolume = this.safeFloat (ticker['vol'], 'value'); + const last = this.safeFloat (ticker['last'], 'value'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker['high'], 'value'), + 'low': this.safeFloat (ticker['low'], 'value'), + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker['avg'], 'value'), + 'baseVolume': baseVolume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' switched off the trades endpoint, see their docs at https://docs.anxv2.apiary.io'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['max'] = limit; + } + const response = await this.v3privatePostOrderList (this.extend (request, params)); + const orders = this.safeValue (response, 'orders', []); + const market = (symbol === undefined) ? undefined : this.market (symbol); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency_pair': market['id'], + }; + // ANXPro will return all symbol pairs regardless of what is specified in request + const response = await this.privatePostCurrencyPairMoneyOrders (this.extend (request, params)); + // + // { + // "result": "success", + // "data": [ + // { + // "oid": "e74305c7-c424-4fbc-a8a2-b41d8329deb0", + // "currency": "HKD", + // "item": "BTC", + // "type": "offer", + // "amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "effective_amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "price": { + // "currency": "HKD", + // "display": "412.34567 HKD", + // "display_short": "412.35 HKD", + // "value": "412.34567", + // "value_int": "41234567" + // }, + // "status": "open", + // "date": 1393411075000, + // "priority": 1393411075000000, + // "actions": [] + // }, + // ... + // ] + // } + // + return this.parseOrders (this.safeValue (response, 'data', {}), market, since, limit); + } + + parseOrder (order, market = undefined) { + if ('orderId' in order) { + return this.parseOrderV3 (order, market); + } else { + return this.parseOrderV2 (order, market); + } + } + + parseOrderStatus (status) { + const statuses = { + 'ACTIVE': 'open', + 'FULL_FILL': 'closed', + 'CANCEL': 'canceled', + 'USER_CANCEL_PARTIAL': 'canceled', + 'PARTIAL_FILL': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrderV3 (order, market = undefined) { + // + // v3 + // + // { + // orderType: 'LIMIT', + // tradedCurrency: 'XRP', + // settlementCurrency: 'BTC', + // tradedCurrencyAmount: '400.00000000', + // buyTradedCurrency: true, + // limitPriceInSettlementCurrency: '0.00007129', + // timestamp: '1522547850000', + // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', + // tradedCurrencyAmountOutstanding: '0.00000000', + // orderStatus: 'FULL_FILL', + // executedAverageRate: '0.00007127', + // trades: [ + // { + // tradeId: 'fe16b796-df57-41a2-b6d9-3489f189749e', + // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', + // timestamp: '1522547850000', + // tradedCurrencyFillAmount: '107.91298639', + // settlementCurrencyFillAmount: '0.00768772', + // settlementCurrencyFillAmountUnrounded: '0.00768772', + // price: '0.00007124', + // ccyPair: 'XRPBTC' + // }, + // { + // tradeId: 'e2962f67-c094-4243-8b88-0cdc70a1b1c7', + // orderId: '62a8be4d-73c6-4469-90cd-28b4726effe0', + // timestamp: '1522547851000', + // tradedCurrencyFillAmount: '292.08701361', + // settlementCurrencyFillAmount: '0.02082288', + // settlementCurrencyFillAmountUnrounded: '0.02082288', + // price: '0.00007129', + // ccyPair: 'XRPBTC' + // } + // ] + // } + // + const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); + const base = this.safeCurrencyCode (this.safeString (order, 'tradedCurrency')); + const quote = this.safeCurrencyCode (this.safeString (order, 'settlementCurrency')); + const symbol = base + '/' + quote; + const buyTradedCurrency = this.safeString (order, 'buyTradedCurrency'); + const side = (buyTradedCurrency === 'true') ? 'buy' : 'sell'; + const timestamp = this.safeInteger (order, 'timestamp'); + let lastTradeTimestamp = undefined; + const trades = []; + let filled = 0; + const type = this.safeStringLower (order, 'orderType'); + for (let i = 0; i < order['trades'].length; i++) { + const trade = order['trades'][i]; + const tradeTimestamp = this.safeInteger (trade, 'timestamp'); + if (!lastTradeTimestamp || lastTradeTimestamp < tradeTimestamp) { + lastTradeTimestamp = tradeTimestamp; + } + const parsedTrade = this.extend (this.parseTrade (trade), { 'side': side, 'type': type }); + trades.push (parsedTrade); + filled = this.sum (filled, parsedTrade['amount']); + } + const price = this.safeFloat (order, 'limitPriceInSettlementCurrency'); + const executedAverageRate = this.safeFloat (order, 'executedAverageRate'); + const remaining = (type === 'market') ? 0 : this.safeFloat (order, 'tradedCurrencyAmountOutstanding'); + let amount = this.safeFloat (order, 'tradedCurrencyAmount'); + if (!amount) { + const settlementCurrencyAmount = this.safeFloat (order, 'settlementCurrencyAmount'); + amount = settlementCurrencyAmount / executedAverageRate; + } + const cost = executedAverageRate * filled; + return { + 'id': this.safeString (order, 'orderId'), + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': undefined, + 'trades': trades, + 'info': order, + }; + } + + parseOrderV2 (order, market = undefined) { + // + // v2 + // + // { + // "oid": "e74305c7-c424-4fbc-a8a2-b41d8329deb0", + // "currency": "HKD", + // "item": "BTC", + // "type": "offer", <-- bid/offer + // "amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "effective_amount": { + // "currency": "BTC", + // "display": "10.00000000 BTC", + // "display_short": "10.00 BTC", + // "value": "10.00000000", + // "value_int": "1000000000" + // }, + // "price": { + // "currency": "HKD", + // "display": "412.34567 HKD", + // "display_short": "412.35 HKD", + // "value": "412.34567", + // "value_int": "41234567" + // }, + // "status": "open", + // "date": 1393411075000, + // "priority": 1393411075000000, + // "actions": [] + // } + // + const id = this.safeString (order, 'oid'); + const status = this.safeString (order, 'status'); + const timestamp = this.safeInteger (order, 'date'); + const baseId = this.safeString (order, 'item'); + const quoteId = this.safeString (order, 'currency'); + const marketId = baseId + '/' + quoteId; + market = this.safeValue (this.markets_by_id, marketId); + let symbol = undefined; + if (typeof market !== 'undefined') { + symbol = market['symbol']; + } + const amount_info = this.safeValue (order, 'amount', {}); + const effective_info = this.safeValue (order, 'effective_amount', {}); + const price_info = this.safeValue (order, 'price', {}); + const remaining = this.safeFloat (effective_info, 'value'); + const amount = this.safeFloat (amount_info, 'volume'); + const price = this.safeFloat (price_info, 'value'); + let filled = undefined; + let cost = undefined; + if (typeof amount !== 'undefined') { + if (typeof remaining !== 'undefined') { + filled = amount - remaining; + cost = price * filled; + } + } + const orderType = 'limit'; + let side = this.safeString (order, 'type'); + if (side === 'offer') { + side = 'sell'; + } else { + side = 'buy'; + } + const fee = undefined; + const trades = undefined; // todo parse trades + const lastTradeTimestamp = undefined; + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const amountMultiplier = Math.pow (10, market['precision']['amount']); + const request = { + 'currency_pair': market['id'], + 'amount_int': parseInt (amount * amountMultiplier), // 10^8 + }; + if (type === 'limit') { + const priceMultiplier = Math.pow (10, market['precision']['price']); + request['price_int'] = parseInt (price * priceMultiplier); // 10^5 or 10^8 + } + request['type'] = (side === 'buy') ? 'bid' : 'ask'; + const response = await this.privatePostCurrencyPairMoneyOrderAdd (this.extend (request, params)); + return { + 'info': response, + 'id': response['data'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostCurrencyPairMoneyOrderCancel ({ 'oid': id }); + } + + getAmountMultiplier (code) { + const multipliers = { + 'BTC': 100000000, + 'LTC': 100000000, + 'STR': 100000000, + 'XRP': 100000000, + 'DOGE': 100000000, + }; + const defaultValue = 100; + return this.safeInteger (multipliers, code, defaultValue); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const multiplier = this.getAmountMultiplier (code); + const request = { + 'currency': currency, + 'amount_int': parseInt (amount * multiplier), + 'address': address, + }; + if (tag !== undefined) { + request['destinationTag'] = tag; + } + const response = await this.privatePostMoneyCurrencySendSimple (this.extend (request, params)); + return { + 'info': response, + 'id': response['data']['transactionId'], + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostMoneyCurrencyAddress (this.extend (request, params)); + const data = this.safeValue (response, 'data', {}); + const address = this.safeString (data, 'addr'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + '/' + request; + if (api === 'public' || api === 'v3public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + let auth = undefined; + let contentType = undefined; + if (api === 'v3private') { + body = this.json (this.extend ({ 'tonce': nonce * 1000 }, query)); + const path = url.replace ('https://anxpro.com/', ''); + auth = path + '\0' + body; + contentType = 'application/json'; + } else { + body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); + // eslint-disable-next-line quotes + auth = request + "\0" + body; + contentType = 'application/x-www-form-urlencoded'; + } + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); + headers = { + 'Content-Type': contentType, + 'Rest-Key': this.apiKey, + 'Rest-Sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined || response === '') { + return; + } + const result = this.safeString (response, 'result'); + const code = this.safeString (response, 'resultCode'); + const status = this.safeString (response, 'status'); + if (((result !== undefined) && (result !== 'success')) || ((code !== undefined) && (code !== 'OK')) || (status !== undefined)) { + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], status, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/base/.eslintrc.js b/TaskServer/node_modules/ccxt/js/base/.eslintrc.js new file mode 100644 index 000000000..c441debac --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/.eslintrc.js @@ -0,0 +1,43 @@ +'use strict'; + +module.exports = { + "env": { + "node": true, + "es6": true, + "browser": true, + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "script", + }, + "rules": { + "semi": "off", + "no-unused-vars": ["off"], + "quotes": ["off", "single"], + "quote-props": "off", + "func-call-spacing": ["error", "always"], + "one-var": "off", + "indent": ["warn", 4, { "ignoredNodes": ["ObjectPattern"] }], + "comma-style": "off", + "no-multi-spaces": "off", + "comma-dangle": "off", + "spaced-comment": "off", + "camelcase": "off", + "padded-blocks": "off", + "multiline-comment-style": "off", + "curly": "off", + "rest-spread-spacing": "off", + "no-multiple-empty-lines": "off", + "no-undef-init": "off", + "no-useless-return": "off", + "no-console": "off", + "operator-linebreak": "off", + "key-spacing": "off", + "brace-style": "off", + "padding-line-between-statements": ["off", + {"blankLine": "never", "prev":"*", "next": "*" }, + {"blankLine": "always", "prev":"directive", "next": "*" }, + {"blankLine": "always", "prev":"*", "next": "cjs-export" }, + ], + }, +} diff --git a/TaskServer/node_modules/ccxt/js/base/Exchange.js b/TaskServer/node_modules/ccxt/js/base/Exchange.js new file mode 100644 index 000000000..c7511a1ec --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/Exchange.js @@ -0,0 +1,1504 @@ +"use strict"; + +// ---------------------------------------------------------------------------- + +const functions = require ('./functions') + +const { + isNode + , keys + , values + , deepExtend + , extend + , clone + , flatten + , unique + , indexBy + , sortBy + , groupBy + , aggregate + , uuid + , unCamelCase + , precisionFromString + , throttle + , capitalize + , now + , timeout + , TimedOut + , buildOHLCVC + , decimalToPrecision + , defaultFetch +} = functions + +const { + ExchangeError + , BadSymbol + , InvalidAddress + , NotSupported + , AuthenticationError + , DDoSProtection + , RequestTimeout + , ExchangeNotAvailable + , RateLimitExceeded } = require ('./errors') + +const { TRUNCATE, ROUND, DECIMAL_PLACES } = functions.precisionConstants + +const BN = require ('../static_dependencies/BN/bn') + +// ---------------------------------------------------------------------------- +// web3 / 0x imports + +let Web3 = undefined + , ethAbi = undefined + , ethUtil = undefined + , BigNumber = undefined + +try { + const requireFunction = require; + Web3 = requireFunction ('web3') // eslint-disable-line global-require + ethAbi = requireFunction ('ethereumjs-abi') // eslint-disable-line global-require + ethUtil = requireFunction ('ethereumjs-util') // eslint-disable-line global-require + BigNumber = requireFunction ('bignumber.js') // eslint-disable-line global-require + // we prefer bignumber.js over BN.js + // BN = requireFunction ('bn.js') // eslint-disable-line global-require +} catch (e) { + // nothing +} + +// ---------------------------------------------------------------------------- + +module.exports = class Exchange { + + describe () { + return { + 'id': undefined, + 'name': undefined, + 'countries': undefined, + 'enableRateLimit': false, + 'rateLimit': 2000, // milliseconds = seconds * 1000 + 'certified': false, + 'has': { + 'cancelAllOrders': false, + 'cancelOrder': true, + 'cancelOrders': false, + 'CORS': false, + 'createDepositAddress': false, + 'createLimitOrder': true, + 'createMarketOrder': true, + 'createOrder': true, + 'deposit': false, + 'editOrder': 'emulated', + 'fetchBalance': true, + 'fetchBidsAsks': false, + 'fetchClosedOrders': false, + 'fetchCurrencies': false, + 'fetchDepositAddress': false, + 'fetchDeposits': false, + 'fetchFundingFees': false, + 'fetchL2OrderBook': true, + 'fetchLedger': false, + 'fetchMarkets': true, + 'fetchMyTrades': false, + 'fetchOHLCV': 'emulated', + 'fetchOpenOrders': false, + 'fetchOrder': false, + 'fetchOrderBook': true, + 'fetchOrderBooks': false, + 'fetchOrders': false, + 'fetchOrderTrades': false, + 'fetchStatus': 'emulated', + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchTime': false, + 'fetchTrades': true, + 'fetchTradingFee': false, + 'fetchTradingFees': false, + 'fetchTradingLimits': false, + 'fetchTransactions': false, + 'fetchWithdrawals': false, + 'privateAPI': true, + 'publicAPI': true, + 'withdraw': false, + }, + 'urls': { + 'logo': undefined, + 'api': undefined, + 'www': undefined, + 'doc': undefined, + 'fees': undefined, + }, + 'api': undefined, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': false, + 'login': false, + 'password': false, + 'twofa': false, // 2-factor authentication (one-time password key) + 'privateKey': false, // a "0x"-prefixed hexstring private key for a wallet + 'walletAddress': false, // the wallet address "0x"-prefixed hexstring + 'token': false, // reserved for HTTP auth in some cases + }, + 'markets': undefined, // to be filled manually or by fetchMarkets + 'currencies': {}, // to be filled manually or by fetchMarkets + 'timeframes': undefined, // redefine if the exchange has.fetchOHLCV + 'fees': { + 'trading': { + 'tierBased': undefined, + 'percentage': undefined, + 'taker': undefined, + 'maker': undefined, + }, + 'funding': { + 'tierBased': undefined, + 'percentage': undefined, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'status': { + 'status': 'ok', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'exceptions': undefined, + 'httpExceptions': { + '422': ExchangeError, + '418': DDoSProtection, + '429': RateLimitExceeded, + '404': ExchangeNotAvailable, + '409': ExchangeNotAvailable, + '500': ExchangeNotAvailable, + '501': ExchangeNotAvailable, + '502': ExchangeNotAvailable, + '520': ExchangeNotAvailable, + '521': ExchangeNotAvailable, + '522': ExchangeNotAvailable, + '525': ExchangeNotAvailable, + '526': ExchangeNotAvailable, + '400': ExchangeNotAvailable, + '403': ExchangeNotAvailable, + '405': ExchangeNotAvailable, + '503': ExchangeNotAvailable, + '530': ExchangeNotAvailable, + '408': RequestTimeout, + '504': RequestTimeout, + '401': AuthenticationError, + '511': AuthenticationError, + }, + // some exchanges report only 'free' on `fetchBlance` call (i.e. report no 'used' funds) + // in this case ccxt will try to infer 'used' funds from open order cache, which might be stale + // still, some exchanges report number of open orders together with balance + // if you set the following flag to 'true' ccxt will leave 'used' funds undefined in case of discrepancy + 'dontGetUsedBalanceFromStaleCache': false, + 'commonCurrencies': { // gets extended/overwritten in subclasses + 'XBT': 'BTC', + 'BCC': 'BCH', + 'DRK': 'DASH', + 'BCHABC': 'BCH', + 'BCHSV': 'BSV', + }, + 'precisionMode': DECIMAL_PLACES, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + } // return + } // describe () + + constructor (userConfig = {}) { + Object.assign (this, functions) + // if (isNode) { + // this.nodeVersion = process.version.match (/\d+\.\d+\.\d+/)[0] + // this.userAgent = { + // 'User-Agent': 'ccxt/' + Exchange.ccxtVersion + + // ' (+https://github.com/ccxt/ccxt)' + + // ' Node.js/' + this.nodeVersion + ' (JavaScript)' + // } + // } + + this.options = {} // exchange-specific options, if any + + // fetch implementation options (JS only) + this.fetchOptions = { + // keepalive: true, // does not work in Chrome, https://github.com/ccxt/ccxt/issues/6368 + } + + this.userAgents = { + 'chrome': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', + 'chrome39': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36', + } + + this.headers = {} + + // prepended to URL, like https://proxy.com/https://exchange.com/api... + this.proxy = '' + this.origin = '*' // CORS origin + + this.minFundingAddressLength = 1 // used in checkAddress + this.substituteCommonCurrencyCodes = true // reserved + + // do not delete this line, it is needed for users to be able to define their own fetchImplementation + this.fetchImplementation = defaultFetch + + this.timeout = 10000 // milliseconds + this.verbose = false + this.debug = false + this.userAgent = undefined + this.twofa = undefined // two-factor authentication (2FA) + + this.apiKey = undefined + this.secret = undefined + this.uid = undefined + this.login = undefined + this.password = undefined + this.privateKey = undefined // a "0x"-prefixed hexstring private key for a wallet + this.walletAddress = undefined // a wallet address "0x"-prefixed hexstring + this.token = undefined // reserved for HTTP auth in some cases + + this.balance = {} + this.orderbooks = {} + this.tickers = {} + this.orders = {} + this.trades = {} + this.transactions = {} + this.ohlcvs = {} + + this.requiresWeb3 = false + this.precision = {} + + this.enableLastJsonResponse = true + this.enableLastHttpResponse = true + this.enableLastResponseHeaders = true + this.last_http_response = undefined + this.last_json_response = undefined + this.last_response_headers = undefined + + this.arrayConcat = (a, b) => a.concat (b) + + const unCamelCaseProperties = (obj = this) => { + if (obj !== null) { + for (const k of Object.getOwnPropertyNames (obj)) { + this[unCamelCase (k)] = this[k] + } + unCamelCaseProperties (Object.getPrototypeOf (obj)) + } + } + unCamelCaseProperties () + + // merge configs + const config = deepExtend (this.describe (), userConfig) + + // merge to this + for (const [property, value] of Object.entries (config)) + this[property] = deepExtend (this[property], value) + + if (!this.httpAgent) { + this.httpAgent = defaultFetch.http ? new defaultFetch.http.Agent ({ 'keepAlive': true }) : undefined + } + + if (!this.httpsAgent) { + this.httpsAgent = defaultFetch.https ? new defaultFetch.https.Agent ({ 'keepAlive': true }) : undefined + } + + // generate old metainfo interface + for (const k in this.has) { + this['has' + capitalize (k)] = !!this.has[k] // converts 'emulated' to true + } + + if (this.api) + this.defineRestApi (this.api, 'request') + + this.initRestRateLimiter () + + if (this.markets) + this.setMarkets (this.markets) + + if (this.requiresWeb3 && !this.web3 && Web3) { + const provider = (this.web3ProviderURL) ? new Web3.providers.HttpProvider (this.web3ProviderURL) : new Web3.providers.HttpProvider () + this.web3 = new Web3 (Web3.givenProvider || provider) + } + } + + defaults () { + return { /* override me */ } + } + + nonce () { + return this.seconds () + } + + encodeURIComponent (...args) { + return encodeURIComponent (...args) + } + + checkRequiredCredentials (error = true) { + const keys = Object.keys (this.requiredCredentials) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (this.requiredCredentials[key] && !this[key]) { + if (error) { + throw new AuthenticationError (this.id + ' requires `' + key + '` credential') + } else { + return error + } + } + } + return true + } + + checkAddress (address) { + + if (address === undefined) + throw new InvalidAddress (this.id + ' address is undefined') + + // check the address is not the same letter like 'aaaaa' nor too short nor has a space + if ((unique (address).length === 1) || address.length < this.minFundingAddressLength || address.includes (' ')) + throw new InvalidAddress (this.id + ' address is invalid or has less than ' + this.minFundingAddressLength.toString () + ' characters: "' + this.json (address) + '"') + + return address + } + + initRestRateLimiter () { + + if (this.rateLimit === undefined) + throw new Error (this.id + '.rateLimit property is not configured') + + this.tokenBucket = this.extend ({ + refillRate: 1 / this.rateLimit, + delay: 1, + capacity: 1, + defaultCost: 1, + maxCapacity: 1000, + }, this.tokenBucket) + + this.throttle = throttle (this.tokenBucket) + + this.executeRestRequest = (url, method = 'GET', headers = undefined, body = undefined) => { + + // fetchImplementation cannot be called on this. in browsers: + // TypeError Failed to execute 'fetch' on 'Window': Illegal invocation + const fetchImplementation = this.fetchImplementation + + const params = { method, headers, body, timeout: this.timeout } + + if (this.agent) { + this.agent.keepAlive = true + params['agent'] = this.agent + } else if (this.httpAgent && url.indexOf ('http://') === 0) { + params['agent'] = this.httpAgent + } else if (this.httpsAgent && url.indexOf ('https://') === 0) { + params['agent'] = this.httpsAgent + } + + const promise = + fetchImplementation (url, this.extend (params, this.fetchOptions)) + .catch ((e) => { + if (isNode) + throw new ExchangeNotAvailable ([ this.id, method, url, e.type, e.message ].join (' ')) + throw e // rethrow all unknown errors + }) + .then (response => this.handleRestResponse (response, url, method, headers, body)) + + return timeout (this.timeout, promise).catch ((e) => { + if (e instanceof TimedOut) + throw new RequestTimeout (this.id + ' ' + method + ' ' + url + ' request timed out (' + this.timeout + ' ms)') + throw e + }) + } + } + + setSandboxMode (enabled) { + if (!!enabled) { // eslint-disable-line no-extra-boolean-cast + if ('test' in this.urls) { + if (typeof this.urls['api'] === 'string') { + this.urls['api_backup'] = this.urls['api'] + this.urls['api'] = this.urls['test'] + } else { + this.urls['api_backup'] = clone (this.urls['api']) + this.urls['api'] = clone (this.urls['test']) + } + } else { + throw new NotSupported (this.id + ' does not have a sandbox URL') + } + } else if ('api_backup' in this.urls) { + if (typeof this.urls['api'] === 'string') { + this.urls['api'] = this.urls['api_backup'] + } else { + this.urls['api'] = clone (this.urls['api_backup']) + } + } + } + + defineRestApi (api, methodName, options = {}) { + + for (const type of Object.keys (api)) { + for (const httpMethod of Object.keys (api[type])) { + + let paths = api[type][httpMethod] + for (let i = 0; i < paths.length; i++) { + let path = paths[i].trim () + let splitPath = path.split (/[^a-zA-Z0-9]/) + + let uppercaseMethod = httpMethod.toUpperCase () + let lowercaseMethod = httpMethod.toLowerCase () + let camelcaseMethod = this.capitalize (lowercaseMethod) + let camelcaseSuffix = splitPath.map (this.capitalize).join ('') + let underscoreSuffix = splitPath.map (x => x.trim ().toLowerCase ()).filter (x => x.length > 0).join ('_') + + let camelcase = type + camelcaseMethod + this.capitalize (camelcaseSuffix) + let underscore = type + '_' + lowercaseMethod + '_' + underscoreSuffix + + if ('suffixes' in options) { + if ('camelcase' in options['suffixes']) + camelcase += options['suffixes']['camelcase'] + if ('underscore' in options.suffixes) + underscore += options['suffixes']['underscore'] + } + + if ('underscore_suffix' in options) + underscore += options.underscoreSuffix; + if ('camelcase_suffix' in options) + camelcase += options.camelcaseSuffix; + + let partial = async params => this[methodName] (path, type, uppercaseMethod, params || {}) + + this[camelcase] = partial + this[underscore] = partial + } + } + } + } + + fetch (url, method = 'GET', headers = undefined, body = undefined) { + + if (isNode && this.userAgent) { + if (typeof this.userAgent === 'string') + headers = extend ({ 'User-Agent': this.userAgent }, headers) + else if ((typeof this.userAgent === 'object') && ('User-Agent' in this.userAgent)) + headers = extend (this.userAgent, headers) + } + + if (typeof this.proxy === 'function') { + + url = this.proxy (url) + if (isNode) + headers = extend ({ 'Origin': this.origin }, headers) + + } else if (typeof this.proxy === 'string') { + + if (this.proxy.length) + if (isNode) + headers = extend ({ 'Origin': this.origin }, headers) + + url = this.proxy + url + } + + headers = extend (this.headers, headers) + + if (this.verbose) + console.log ("fetch:\n", this.id, method, url, "\nRequest:\n", headers, "\n", body, "\n") + + return this.executeRestRequest (url, method, headers, body) + } + + async fetch2 (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + + if (this.enableRateLimit) + await this.throttle () + + const request = this.sign (path, type, method, params, headers, body) + return this.fetch (request.url, request.method, request.headers, request.body) + } + + request (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + return this.fetch2 (path, type, method, params, headers, body) + } + + parseJson (jsonString) { + try { + if (this.isJsonEncodedObject (jsonString)) { + return JSON.parse (jsonString) + } + } catch (e) { + // SyntaxError + return undefined + } + } + + throwExactlyMatchedException (exact, string, message) { + if (string in exact) { + throw new exact[string] (message) + } + } + + throwBroadlyMatchedException (broad, string, message) { + const broadKey = this.findBroadlyMatchedKey (broad, string) + if (broadKey !== undefined) { + throw new broad[broadKey] (message) + } + } + + // a helper for matching error strings exactly vs broadly + findBroadlyMatchedKey (broad, string) { + const keys = Object.keys (broad) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (string.indexOf (key) >= 0) { + return key + } + } + return undefined + } + + handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { + // override me + } + + defaultErrorHandler (code, reason, url, method, headers, body, response) { + if ((code >= 200) && (code <= 299)) { + return + } + let details = body + const codeAsString = code.toString () + let ErrorClass = undefined + if (codeAsString in this.httpExceptions) { + ErrorClass = this.httpExceptions[codeAsString] + } + if (response === undefined) { + const maintenance = body.match (/offline|busy|retry|wait|unavailable|maintain|maintenance|maintenancing/i) + const ddosProtection = body.match (/cloudflare|incapsula|overload|ddos/i) + if (maintenance) { + ErrorClass = ExchangeNotAvailable + details += ' offline, on maintenance, or unreachable from this location at the moment' + } + if (ddosProtection) { + ErrorClass = DDoSProtection + } + } + if (ErrorClass === ExchangeNotAvailable) { + details += ' (possible reasons: ' + [ + 'invalid API keys', + 'bad or old nonce', + 'exchange is down or offline', + 'on maintenance', + 'DDoS protection', + 'rate-limiting', + ].join (', ') + ')' + } + if (ErrorClass !== undefined) { + throw new ErrorClass ([ this.id, method, url, code, reason, details ].join (' ')) + } + } + + getResponseHeaders (response) { + const result = {} + response.headers.forEach ((value, key) => { + key = key.split ('-').map (word => capitalize (word)).join ('-') + result[key] = value + }) + return result + } + + handleRestResponse (response, url, method = 'GET', requestHeaders = undefined, requestBody = undefined) { + + return response.text ().then ((responseBody) => { + + const json = this.parseJson (responseBody) + + const responseHeaders = this.getResponseHeaders (response) + + if (this.enableLastResponseHeaders) { + this.last_response_headers = responseHeaders + } + + if (this.enableLastHttpResponse) { + this.last_http_response = responseBody // FIXME: for those classes that haven't switched to handleErrors yet + } + + if (this.enableLastJsonResponse) { + this.last_json_response = json // FIXME: for those classes that haven't switched to handleErrors yet + } + + if (this.verbose) + console.log ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponse:\n", responseHeaders, "\n", responseBody, "\n") + + this.handleErrors (response.status, response.statusText, url, method, responseHeaders, responseBody, json, requestHeaders, requestBody) + this.defaultErrorHandler (response.status, response.statusText, url, method, responseHeaders, responseBody, json) + + return json || responseBody + }) + } + + setMarkets (markets, currencies = undefined) { + const values = Object.values (markets).map (market => deepExtend ({ + 'limits': this.limits, + 'precision': this.precision, + }, this.fees['trading'], market)) + this.markets = indexBy (values, 'symbol') + this.marketsById = indexBy (markets, 'id') + this.markets_by_id = this.marketsById + this.symbols = Object.keys (this.markets).sort () + this.ids = Object.keys (this.markets_by_id).sort () + if (currencies) { + this.currencies = deepExtend (currencies, this.currencies) + } else { + const baseCurrencies = + values.filter (market => 'base' in market) + .map (market => ({ + id: market.baseId || market.base, + numericId: (market.baseNumericId !== undefined) ? market.baseNumericId : undefined, + code: market.base, + precision: market.precision ? (market.precision.base || market.precision.amount) : 8, + })) + const quoteCurrencies = + values.filter (market => 'quote' in market) + .map (market => ({ + id: market.quoteId || market.quote, + numericId: (market.quoteNumericId !== undefined) ? market.quoteNumericId : undefined, + code: market.quote, + precision: market.precision ? (market.precision.quote || market.precision.price) : 8, + })) + const allCurrencies = baseCurrencies.concat (quoteCurrencies) + const groupedCurrencies = groupBy (allCurrencies, 'code') + const currencies = Object.keys (groupedCurrencies).map (code => + groupedCurrencies[code].reduce ((previous, current) => + ((previous.precision > current.precision) ? previous : current), groupedCurrencies[code][0])) + const sortedCurrencies = sortBy (flatten (currencies), 'code') + this.currencies = deepExtend (indexBy (sortedCurrencies, 'code'), this.currencies) + } + this.currencies_by_id = indexBy (this.currencies, 'id') + return this.markets + } + + async loadMarkets (reload = false, params = {}) { + if (!reload && this.markets) { + if (!this.markets_by_id) { + return this.setMarkets (this.markets) + } + return this.markets + } + let currencies = undefined + if (this.has.fetchCurrencies) { + currencies = await this.fetchCurrencies () + } + const markets = await this.fetchMarkets (params) + return this.setMarkets (markets, currencies) + } + + async loadAccounts (reload = false, params = {}) { + if (reload) { + this.accounts = await this.fetchAccounts (params) + } else { + if (this.accounts) { + return this.accounts + } else { + this.accounts = await this.fetchAccounts (params) + } + } + this.accountsById = this.indexBy (this.accounts, 'id') + return this.accounts + } + + fetchBidsAsks (symbols = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchBidsAsks not supported yet') + } + + async fetchOHLCVC (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) { + if (!this.has['fetchTrades']) + throw new NotSupported (this.id + ' fetchOHLCV() not supported yet') + await this.loadMarkets () + const trades = await this.fetchTrades (symbol, since, limits, params) + const ohlcvc = buildOHLCVC (trades, timeframe, since, limits) + return ohlcvc + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) { + if (!this.has['fetchTrades']) + throw new NotSupported (this.id + ' fetchOHLCV() not supported yet') + await this.loadMarkets () + const trades = await this.fetchTrades (symbol, since, limits, params) + const ohlcvc = buildOHLCVC (trades, timeframe, since, limits) + return ohlcvc.map (c => c.slice (0, -1)) + } + + parseTradingViewOHLCV (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + const result = this.convertTradingViewToOHLCV (ohlcvs); + return this.parseOHLCVs (result, market, timeframe, since, limit); + } + + convertTradingViewToOHLCV (ohlcvs) { + const result = []; + for (let i = 0; i < ohlcvs['t'].length; i++) { + result.push ([ + ohlcvs['t'][i] * 1000, + ohlcvs['o'][i], + ohlcvs['h'][i], + ohlcvs['l'][i], + ohlcvs['c'][i], + ohlcvs['v'][i], + ]); + } + return result; + } + + convertOHLCVToTradingView (ohlcvs) { + const result = { + 't': [], + 'o': [], + 'h': [], + 'l': [], + 'c': [], + 'v': [], + }; + for (let i = 0; i < ohlcvs.length; i++) { + result['t'].push (parseInt (ohlcvs[i][0] / 1000)); + result['o'].push (ohlcvs[i][1]); + result['h'].push (ohlcvs[i][2]); + result['l'].push (ohlcvs[i][3]); + result['c'].push (ohlcvs[i][4]); + result['v'].push (ohlcvs[i][5]); + } + return result; + } + + fetchTicker (symbol, params = {}) { + throw new NotSupported (this.id + ' fetchTicker not supported yet') + } + + fetchTickers (symbols = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchTickers not supported yet') + } + + purgeCachedOrders (before) { + const orders = Object + .values (this.orders) + .filter (order => + (order.status === 'open') || + (order.timestamp >= before)) + this.orders = indexBy (orders, 'id') + return this.orders + } + + fetchOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrder not supported yet'); + } + + fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrders not supported yet'); + } + + fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOpenOrders not supported yet'); + } + + fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchClosedOrders not supported yet'); + } + + fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchMyTrades not supported yet'); + } + + fetchTransactions (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchTransactions not supported yet'); + } + + fetchDeposits (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchDeposits not supported yet'); + } + + fetchWithdrawals (symbol = undefined, since = undefined, limit = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchWithdrawals not supported yet'); + } + + fetchCurrencies (params = {}) { + // markets are returned as a list + // currencies are returned as a dict + // this is for historical reasons + // and may be changed for consistency later + return new Promise ((resolve, reject) => resolve (this.currencies)); + } + + fetchMarkets (params = {}) { + // markets are returned as a list + // currencies are returned as a dict + // this is for historical reasons + // and may be changed for consistency later + return new Promise ((resolve, reject) => resolve (Object.values (this.markets))) + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + const order = await this.fetchOrder (id, symbol, params); + return order['status']; + } + + account () { + return { + 'free': undefined, + 'used': undefined, + 'total': undefined, + } + } + + commonCurrencyCode (currency) { + if (!this.substituteCommonCurrencyCodes) + return currency + return this.safeString (this.commonCurrencies, currency, currency) + } + + currencyId (commonCode) { + + if (this.currencies === undefined) { + throw new ExchangeError (this.id + ' currencies not loaded') + } + + if (commonCode in this.currencies) { + return this.currencies[commonCode]['id']; + } + + const currencyIds = {} + const distinct = Object.keys (this.commonCurrencies) + for (let i = 0; i < distinct.length; i++) { + const k = distinct[i] + currencyIds[this.commonCurrencies[k]] = k + } + + return this.safeString (currencyIds, commonCode, commonCode) + } + + currency (code) { + + if (this.currencies === undefined) + throw new ExchangeError (this.id + ' currencies not loaded') + + if ((typeof code === 'string') && (code in this.currencies)) + return this.currencies[code] + + throw new ExchangeError (this.id + ' does not have currency code ' + code) + } + + market (symbol) { + + if (this.markets === undefined) + throw new ExchangeError (this.id + ' markets not loaded') + + if ((typeof symbol === 'string') && (symbol in this.markets)) + return this.markets[symbol] + + throw new BadSymbol (this.id + ' does not have market symbol ' + symbol) + } + + marketId (symbol) { + const market = this.market (symbol) + return (market !== undefined ? market['id'] : symbol) + } + + marketIds (symbols) { + return symbols.map (symbol => this.marketId (symbol)); + } + + symbol (symbol) { + return this.market (symbol).symbol || symbol + } + + url (path, params = {}) { + let result = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)) + if (Object.keys (query).length) + result += '?' + this.urlencode (query) + return result + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1) { + const price = parseFloat (bidask[priceKey]) + const amount = parseFloat (bidask[amountKey]) + return [ price, amount ] + } + + parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) { + return Object.values (bidasks || []).map (bidask => this.parseBidAsk (bidask, priceKey, amountKey)) + } + + async fetchL2OrderBook (symbol, limit = undefined, params = {}) { + const orderbook = await this.fetchOrderBook (symbol, limit, params) + return extend (orderbook, { + 'bids': sortBy (aggregate (orderbook.bids), 0, true), + 'asks': sortBy (aggregate (orderbook.asks), 0), + }) + } + + parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1) { + return { + 'bids': sortBy ((bidsKey in orderbook) ? this.parseBidsAsks (orderbook[bidsKey], priceKey, amountKey) : [], 0, true), + 'asks': sortBy ((asksKey in orderbook) ? this.parseBidsAsks (orderbook[asksKey], priceKey, amountKey) : [], 0), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + } + } + + parseBalance (balance) { + + const currencies = Object.keys (this.omit (balance, [ 'info', 'free', 'used', 'total' ])); + + balance['free'] = {} + balance['used'] = {} + balance['total'] = {} + + for (const currency of currencies) { + + if (balance[currency].total === undefined) { + if (balance[currency].free !== undefined && balance[currency].used !== undefined) { + balance[currency].total = this.sum (balance[currency].free, balance[currency].used) + } + } + if (balance[currency].free === undefined) { + if (balance[currency].total !== undefined && balance[currency].used !== undefined) { + balance[currency].free = this.sum (balance[currency].total, -balance[currency].used) + } + } + if (balance[currency].used === undefined) { + if (balance[currency].total !== undefined && balance[currency].free !== undefined) { + balance[currency].used = this.sum (balance[currency].total, -balance[currency].free) + } + } + + balance.free[currency] = balance[currency].free + balance.used[currency] = balance[currency].used + balance.total[currency] = balance[currency].total + } + + return balance + } + + async fetchPartialBalance (part, params = {}) { + const balance = await this.fetchBalance (params) + return balance[part] + } + + fetchFreeBalance (params = {}) { + return this.fetchPartialBalance ('free', params) + } + + fetchUsedBalance (params = {}) { + return this.fetchPartialBalance ('used', params) + } + + fetchTotalBalance (params = {}) { + return this.fetchPartialBalance ('total', params) + } + + async fetchStatus (params = {}) { + if (this.has['fetchTime']) { + const time = await this.fetchTime(params) + return this.status = this.extend(this.status, { + 'updated': time, + }) + } + return this.status + } + + async fetchTradingFees (params = {}) { + throw new NotSupported (this.id + ' fetchTradingFees not supported yet') + } + + async fetchTradingFee (symbol, params = {}) { + if (!this.has['fetchTradingFees']) { + throw new NotSupported (this.id + ' fetchTradingFee not supported yet') + } + return await this.fetchTradingFees (params) + } + + async loadTradingLimits (symbols = undefined, reload = false, params = {}) { + if (this.has['fetchTradingLimits']) { + if (reload || !('limitsLoaded' in this.options)) { + const response = await this.fetchTradingLimits (symbols); + for (let i = 0; i < symbols.length; i++) { + const symbol = symbols[i]; + this.markets[symbol] = this.deepExtend (this.markets[symbol], response[symbol]); + } + this.options['limitsLoaded'] = this.milliseconds (); + } + } + return this.markets; + } + + filterBySinceLimit (array, since = undefined, limit = undefined, key = 'timestamp') { + if (since !== undefined && since !== null) + array = array.filter (entry => entry[key] >= since) + if (limit !== undefined && limit !== null) + array = array.slice (0, limit) + return array + } + + filterByValueSinceLimit (array, field, value = undefined, since = undefined, limit = undefined) { + + const valueIsDefined = value !== undefined && value !== null + const sinceIsDefined = since !== undefined && since !== null + + // single-pass filter for both symbol and since + if (valueIsDefined || sinceIsDefined) + array = Object.values (array).filter (entry => + ((valueIsDefined ? (entry[field] === value) : true) && + (sinceIsDefined ? (entry.timestamp >= since) : true))) + + if (limit !== undefined && limit !== null) + array = Object.values (array).slice (0, limit) + + return array + } + + filterBySymbolSinceLimit (array, symbol = undefined, since = undefined, limit = undefined) { + return this.filterByValueSinceLimit (array, 'symbol', symbol, since, limit) + } + + filterByCurrencySinceLimit (array, code = undefined, since = undefined, limit = undefined) { + return this.filterByValueSinceLimit (array, 'currency', code, since, limit) + } + + filterByArray (objects, key, values = undefined, indexed = true) { + + objects = Object.values (objects) + + // return all of them if no values were passed + if (values === undefined || values === null) + return indexed ? indexBy (objects, key) : objects + + const result = [] + for (let i = 0; i < objects.length; i++) { + if (values.includes (objects[i][key])) + result.push (objects[i]) + } + + return indexed ? indexBy (result, key) : result + } + + parseTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (trades)) { + // throw new ExchangeError (this.id + ' parseTrades expected an array in the trades argument, but got ' + typeof trades); + // } + let result = Object.values (trades || []).map (trade => this.extend (this.parseTrade (trade, market), params)) + result = sortBy (result, 'timestamp') + const symbol = (market !== undefined) ? market['symbol'] : undefined + return this.filterBySymbolSinceLimit (result, symbol, since, limit) + } + + parseTransactions (transactions, currency = undefined, since = undefined, limit = undefined, params = {}) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (transactions)) { + // throw new ExchangeError (this.id + ' parseTransactions expected an array in the transactions argument, but got ' + typeof transactions); + // } + let result = Object.values (transactions || []).map (transaction => this.extend (this.parseTransaction (transaction, currency), params)) + result = this.sortBy (result, 'timestamp'); + const code = (currency !== undefined) ? currency['code'] : undefined; + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + parseLedger (data, currency = undefined, since = undefined, limit = undefined, params = {}) { + let result = []; + const array = Object.values (data || []); + for (let i = 0; i < array.length; i++) { + const itemOrItems = this.parseLedgerEntry (array[i], currency); + if (Array.isArray (itemOrItems)) { + for (let j = 0; j < itemOrItems.length; j++) { + result.push (this.extend (itemOrItems[j], params)); + } + } else { + result.push (this.extend (itemOrItems, params)); + } + } + result = this.sortBy (result, 'timestamp'); + const code = (currency !== undefined) ? currency['code'] : undefined; + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (orders)) { + // throw new ExchangeError (this.id + ' parseOrders expected an array in the orders argument, but got ' + typeof orders); + // } + let result = Object.values (orders).map (order => this.extend (this.parseOrder (order, market), params)) + result = sortBy (result, 'timestamp') + const symbol = (market !== undefined) ? market['symbol'] : undefined + return this.filterBySymbolSinceLimit (result, symbol, since, limit) + } + + safeCurrencyCode (currencyId, currency = undefined) { + let code = undefined + if (currencyId !== undefined) { + if (this.currencies_by_id !== undefined && currencyId in this.currencies_by_id) { + code = this.currencies_by_id[currencyId]['code'] + } else { + code = this.commonCurrencyCode (currencyId.toUpperCase ()) + } + } + if (code === undefined && currency !== undefined) { + code = currency['code'] + } + return code + } + + filterBySymbol (array, symbol = undefined) { + return ((symbol !== undefined) ? array.filter (entry => entry.symbol === symbol) : array) + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return Array.isArray (ohlcv) ? ohlcv.slice (0, 6) : ohlcv + } + + parseOHLCVs (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // this code is commented out temporarily to catch for exchange-specific errors + // if (!this.isArray (ohlcvs)) { + // throw new ExchangeError (this.id + ' parseOHLCVs expected an array in the ohlcvs argument, but got ' + typeof ohlcvs); + // } + ohlcvs = Object.values (ohlcvs || []) + const result = [] + for (let i = 0; i < ohlcvs.length; i++) { + if (limit && (result.length >= limit)) { + break; + } + const ohlcv = this.parseOHLCV (ohlcvs[i], market, timeframe, since, limit) + if (since && (ohlcv[0] < since)) { + continue + } + result.push (ohlcv) + } + return this.sortBy (result, 0) + } + + editLimitBuyOrder (id, symbol, ...args) { + return this.editLimitOrder (id, symbol, 'buy', ...args) + } + + editLimitSellOrder (id, symbol, ...args) { + return this.editLimitOrder (id, symbol, 'sell', ...args) + } + + editLimitOrder (id, symbol, ...args) { + return this.editOrder (id, symbol, 'limit', ...args) + } + + async editOrder (id, symbol, ...args) { + if (!this.enableRateLimit) + throw new ExchangeError (this.id + ' editOrder() requires enableRateLimit = true') + await this.cancelOrder (id, symbol); + return this.createOrder (symbol, ...args) + } + + createLimitOrder (symbol, ...args) { + return this.createOrder (symbol, 'limit', ...args) + } + + createMarketOrder (symbol, ...args) { + return this.createOrder (symbol, 'market', ...args) + } + + createLimitBuyOrder (symbol, ...args) { + return this.createOrder (symbol, 'limit', 'buy', ...args) + } + + createLimitSellOrder (symbol, ...args) { + return this.createOrder (symbol, 'limit', 'sell', ...args) + } + + createMarketBuyOrder (symbol, amount, params = {}) { + return this.createOrder (symbol, 'market', 'buy', amount, undefined, params) + } + + createMarketSellOrder (symbol, amount, params = {}) { + return this.createOrder (symbol, 'market', 'sell', amount, undefined, params) + } + + costToPrecision (symbol, cost) { + return decimalToPrecision (cost, ROUND, this.markets[symbol].precision.price, this.precisionMode) + } + + priceToPrecision (symbol, price) { + return decimalToPrecision (price, ROUND, this.markets[symbol].precision.price, this.precisionMode) + } + + amountToPrecision (symbol, amount) { + return decimalToPrecision (amount, TRUNCATE, this.markets[symbol].precision.amount, this.precisionMode) + } + + feeToPrecision (symbol, fee) { + return decimalToPrecision (fee, ROUND, this.markets[symbol].precision.price, this.precisionMode) + } + + currencyToPrecision (currency, fee) { + return decimalToPrecision (fee, ROUND, this.currencies[currency]['precision'], this.precisionMode); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol] + const rate = market[takerOrMaker] + const cost = parseFloat (this.costToPrecision (symbol, amount * price)) + return { + 'type': takerOrMaker, + 'currency': market['quote'], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), + } + } + + // ------------------------------------------------------------------------ + // web3 / 0x methods + static hasWeb3 () { + return Web3 && ethUtil && ethAbi && BigNumber + } + + checkRequiredDependencies () { + if (!Exchange.hasWeb3 ()) { + throw new ExchangeError ("Required dependencies missing: \nnpm i web3 ethereumjs-util ethereumjs-abi bignumber.js --no-save"); + } + } + + ethDecimals (unit = 'ether') { + const units = { + 'wei': 0, // 1 + 'kwei': 3, // 1000 + 'babbage': 3, // 1000 + 'femtoether': 3, // 1000 + 'mwei': 6, // 1000000 + 'lovelace': 6, // 1000000 + 'picoether': 6, // 1000000 + 'gwei': 9, // 1000000000 + 'shannon': 9, // 1000000000 + 'nanoether': 9, // 1000000000 + 'nano': 9, // 1000000000 + 'szabo': 12, // 1000000000000 + 'microether': 12, // 1000000000000 + 'micro': 12, // 1000000000000 + 'finney': 15, // 1000000000000000 + 'milliether': 15, // 1000000000000000 + 'milli': 15, // 1000000000000000 + 'ether': 18, // 1000000000000000000 + 'kether': 21, // 1000000000000000000000 + 'grand': 21, // 1000000000000000000000 + 'mether': 24, // 1000000000000000000000000 + 'gether': 27, // 1000000000000000000000000000 + 'tether': 30, // 1000000000000000000000000000000 + } + return this.safeValue (units, unit) + } + + ethUnit (decimals = 18) { + const units = { + 0: 'wei', // 1000000000000000000 + 3: 'kwei', // 1000000000000000 + 6: 'mwei', // 1000000000000 + 9: 'gwei', // 1000000000 + 12: 'szabo', // 1000000 + 15: 'finney', // 1000 + 18: 'ether', // 1 + 21: 'kether', // 0.001 + 24: 'mether', // 0.000001 + 27: 'gether', // 0.000000001 + 30: 'tether', // 0.000000000001 + } + return this.safeValue (units, decimals) + } + + fromWei (amount, unit = 'ether', decimals = 18) { + if (amount === undefined) { + return amount + } + if (decimals !== 18) { + amount = new BigNumber (amount).times (new BigNumber (10 ** (18 - decimals))).toFixed () + } else { + amount = new BigNumber (amount).toFixed () + } + return parseFloat (this.web3.utils.fromWei (amount, unit)) + } + + toWei (amount, unit = 'ether', decimals = 18) { + if (amount === undefined) { + return amount + } + if (decimals !== 18) { + amount = new BigNumber (this.numberToString (amount)).div (new BigNumber (10 ** (18 - decimals))).toFixed () + } else { + amount = this.numberToString (amount) + } + return this.web3.utils.toWei (amount, unit) + } + + soliditySha3 (array) { + const values = this.solidityValues (array); + const types = this.solidityTypes (values); + return '0x' + ethAbi.soliditySHA3 (types, values).toString ('hex') + } + + solidityTypes (array) { + return array.map (value => (this.web3.utils.isAddress (value) ? 'address' : 'uint256')) + } + + solidityValues (array) { + return array.map (value => (this.web3.utils.isAddress (value) ? value : (new BigNumber (value).toFixed ()))) + } + + getZeroExOrderHash (order) { + return this.soliditySha3 ([ + order['exchangeContractAddress'], // address + order['maker'], // address + order['taker'], // address + order['makerTokenAddress'], // address + order['takerTokenAddress'], // address + order['feeRecipient'], // address + order['makerTokenAmount'], // uint256 + order['takerTokenAmount'], // uint256 + order['makerFee'], // uint256 + order['takerFee'], // uint256 + order['expirationUnixTimestampSec'], // uint256 + order['salt'], // uint256 + ]); + } + + getZeroExOrderHashV2 (order) { + // https://github.com/0xProject/0x-monorepo/blob/development/python-packages/order_utils/src/zero_ex/order_utils/__init__.py + const addressPadding = '000000000000000000000000'; + const header = '1901'; + const domainStructHeader = '91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766f0f24618f4c4be1e62e026fb039a20ef96f4495294817d1027ffaa6d1f70e61ead7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5'; + const orderSchemaHash = '770501f88a26ede5c04a20ef877969e961eb11fc13b78aaf414b633da0d4f86f'; + + const domainStructHash = ethAbi.soliditySHA3 ( + [ + 'bytes', + 'bytes', + 'address' + ], + [ + Buffer.from (domainStructHeader, 'hex'), + Buffer.from (addressPadding, 'hex'), + order['exchangeAddress'] + ] + ); + const orderStructHash = ethAbi.soliditySHA3 ( + [ + 'bytes', + 'bytes', + 'address', + 'bytes', + 'address', + 'bytes', + 'address', + 'bytes', + 'address', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'string', + 'string' + ], + [ + Buffer.from (orderSchemaHash, 'hex'), + Buffer.from (addressPadding, 'hex'), + order['makerAddress'], + Buffer.from (addressPadding, 'hex'), + order['takerAddress'], + Buffer.from (addressPadding, 'hex'), + order['feeRecipientAddress'], + Buffer.from (addressPadding, 'hex'), + order['senderAddress'], + order['makerAssetAmount'], + order['takerAssetAmount'], + order['makerFee'], + order['takerFee'], + order['expirationTimeSeconds'], + order['salt'], + ethUtil.keccak (Buffer.from (order['makerAssetData'].slice (2), 'hex')), + ethUtil.keccak (Buffer.from (order['takerAssetData'].slice (2), 'hex')), + ] + ); + return '0x' + ethUtil.keccak (Buffer.concat ([ + Buffer.from (header, 'hex'), + domainStructHash, + orderStructHash + ])).toString ('hex'); + } + + signZeroExOrder (order, privateKey) { + const orderHash = this.getZeroExOrderHash (order); + const signature = this.signMessage (orderHash, privateKey); + return this.extend (order, { + 'orderHash': orderHash, + 'ecSignature': signature, // todo fix v if needed + }) + } + + signZeroExOrderV2 (order, privateKey) { + const orderHash = this.getZeroExOrderHashV2 (order); + const signature = this.signMessage (orderHash, privateKey); + return this.extend (order, { + 'orderHash': orderHash, + 'signature': this.convertECSignatureToSignatureHex (signature), + }) + } + + convertECSignatureToSignatureHex (signature) { + // https://github.com/0xProject/0x-monorepo/blob/development/packages/order-utils/src/signature_utils.ts + let v = signature.v; + if (v !== 27 && v !== 28) { + v = v + 27; + } + return '0x' + v.toString (16) + signature['r'].slice (-64) + signature['s'].slice (-64) + '03' + } + + static remove0xPrefix (hexData) { + if (hexData.slice (0, 2) === '0x') { + return hexData.slice (2) + } else { + return hexData + } + } + + hashMessage (message) { + // takes a hex encoded message + const binaryMessage = this.base16ToBinary (Exchange.remove0xPrefix (message)) + const prefix = this.stringToBinary ('\x19Ethereum Signed Message:\n' + binaryMessage.sigBytes) + return '0x' + this.hash (this.binaryConcat (prefix, binaryMessage), 'keccak', 'hex') + } + + signHash (hash, privateKey) { + const signature = this.ecdsa (hash.slice (-64), privateKey.slice (-64), 'secp256k1', undefined) + return { + 'r': '0x' + signature['r'], + 's': '0x' + signature['s'], + 'v': 27 + signature['v'], + } + } + + signMessage (message, privateKey) { + return this.signHash (this.hashMessage (message), privateKey.slice (-64)) + } + + oath () { + if (typeof this.twofa !== 'undefined') { + return this.totp (this.twofa) + } else { + throw new ExchangeError (this.id + ' this.twofa has not been set') + } + } + + // the following functions take and return numbers represented as strings + // this is useful for arbitrary precision maths that floats lack + integerDivide (a, b) { + return new BN (a).div (new BN (b)) + } + + integerModulo (a, b) { + return new BN (a).mod (new BN (b)) + } + + integerPow (a, b) { + return new BN (a).pow (new BN (b)) + } +} + diff --git a/TaskServer/node_modules/ccxt/js/base/errorHierarchy.js b/TaskServer/node_modules/ccxt/js/base/errorHierarchy.js new file mode 100644 index 000000000..5f30b1796 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/errorHierarchy.js @@ -0,0 +1,44 @@ +'use strict'; + +const errorHierarchy = { + 'BaseError': { + 'ExchangeError': { + 'AuthenticationError': { + 'PermissionDenied': {}, + 'AccountSuspended': {}, + }, + 'ArgumentsRequired': {}, + 'BadRequest': { + 'BadSymbol': {}, + }, + 'BadResponse': { + 'NullResponse': {}, + }, + 'InsufficientFunds': {}, + 'InvalidAddress': { + 'AddressPending': {}, + }, + 'InvalidOrder': { + 'OrderNotFound': {}, + 'OrderNotCached': {}, + 'CancelPending': {}, + 'OrderImmediatelyFillable': {}, + 'OrderNotFillable': {}, + 'DuplicateOrderId': {}, + }, + 'NotSupported': {}, + }, + 'NetworkError': { + 'DDoSProtection': { + 'RateLimitExceeded': {}, + }, + 'ExchangeNotAvailable': { + 'OnMaintenance': {}, + }, + 'InvalidNonce': {}, + 'RequestTimeout': {}, + }, + }, +}; + +module.exports = errorHierarchy; diff --git a/TaskServer/node_modules/ccxt/js/base/errors.js b/TaskServer/node_modules/ccxt/js/base/errors.js new file mode 100644 index 000000000..25a28580a --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/errors.js @@ -0,0 +1,51 @@ +'use strict'; + +const errorHierarchy = require ('./errorHierarchy.js') + +/* ------------------------------------------------------------------------ */ + +function subclass (BaseClass, classes, namespace = {}) { + + for (const [className, subclasses] of Object.entries (classes)) { + + const Class = Object.assign (namespace, { + + /* By creating a named property, we trick compiler to assign our class constructor function a name. + Otherwise, all our error constructors would be shown as [Function: Error] in the debugger! And + the super-useful `e.constructor.name` magic wouldn't work — we then would have no chance to + obtain a error type string from an error instance programmatically! */ + + [className]: class extends BaseClass { + + constructor (message) { + + super (message) + + /* A workaround to make `instanceof` work on custom Error classes in transpiled ES5. + See my blog post for the explanation of this hack: + + https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801 */ + + this.constructor = Class + this.__proto__ = Class.prototype + this.name = className + this.message = message + } + } + + })[className] + + subclass (Class, subclasses, namespace) + } + + return namespace +} + +/* ------------------------------------------------------------------------ */ + +module.exports = subclass ( + // Root class + Error, + // Derived class hierarchy + errorHierarchy +) diff --git a/TaskServer/node_modules/ccxt/js/base/functions.js b/TaskServer/node_modules/ccxt/js/base/functions.js new file mode 100644 index 000000000..d03d5feb2 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions.js @@ -0,0 +1,28 @@ +'use strict'; + +/* ------------------------------------------------------------------------ */ + +const { unCamelCase } = require ('./functions/string') + +const unCamelCasePropertyNames = x => { + for (const k in x) x[unCamelCase (k)] = x[k] // camel_case_method = camelCaseMethod + return x +} + +/* ------------------------------------------------------------------------ */ + +module.exports = unCamelCasePropertyNames (Object.assign ({} + + , require ('./functions/platform') + , require ('./functions/generic') + , require ('./functions/string') + , require ('./functions/type') + , require ('./functions/number') + , require ('./functions/encode') + , require ('./functions/crypto') + , require ('./functions/time') + , require ('./functions/throttle') + , require ('./functions/misc') +)) + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/crypto.js b/TaskServer/node_modules/ccxt/js/base/functions/crypto.js new file mode 100644 index 000000000..269bd37fb --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/crypto.js @@ -0,0 +1,147 @@ +'use strict' + +/* ------------------------------------------------------------------------ */ + +const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js') +const { capitalize } = require ('./string') +const { stringToBase64, utf16ToBase64, urlencodeBase64 } = require ('./encode') +const NodeRSA = require ('./../../static_dependencies/node-rsa/NodeRSA') +const { numberToLE } = require ('./encode') +const EC = require ('./../../static_dependencies/elliptic/lib/elliptic').ec +const { ArgumentsRequired } = require ('./../errors') +const BN = require ('../../static_dependencies/BN/bn.js') + +/* ------------------------------------------------------------------------ */ + +const hash = (request, hash = 'md5', digest = 'hex') => { + const options = {} + if (hash === 'keccak') { + hash = 'SHA3' + options['outputLength'] = 256 + } + const result = CryptoJS[hash.toUpperCase ()] (request, options) + return (digest === 'binary') ? result : result.toString (CryptoJS.enc[capitalize (digest)]) +} + +/* ............................................. */ + +const hmac = (request, secret, hash = 'sha256', digest = 'hex') => { + const result = CryptoJS['Hmac' + hash.toUpperCase ()] (request, secret) + if (digest) { + const encoding = (digest === 'binary') ? 'Latin1' : capitalize (digest) + return result.toString (CryptoJS.enc[capitalize (encoding)]) + } + return result +} + +/* ............................................. */ + +function rsa (request, secret, alg = 'RS256') { + const algos = { + 'RS256': 'pkcs1-sha256', + 'RS512': 'pkcs1-sha512', + } + if (!(alg in algos)) { + throw new ExchangeError (alg + ' is not a supported rsa signing algorithm.') + } + const algorithm = algos[alg] + let key = new NodeRSA (secret, { + 'environment': 'browser', + 'signingScheme': algorithm, + }) + return key.sign (request, 'base64', 'binary') +} + + +/** + * @return {string} + */ +function jwt (request, secret, alg = 'HS256') { + const algos = { + 'HS256': 'sha256', + 'HS384': 'sha384', + 'HS512': 'sha512', + }; + const encodedHeader = urlencodeBase64 (stringToBase64 (JSON.stringify ({ 'alg': alg, 'typ': 'JWT' }))) + const encodedData = urlencodeBase64 (stringToBase64 (JSON.stringify (request))) + const token = [ encodedHeader, encodedData ].join ('.') + const algoType = alg.slice (0, 2); + const algorithm = algos[alg] + let signature = undefined + if (algoType === 'HS') { + signature = urlencodeBase64 (hmac (token, secret, algorithm, 'base64')) + } else if (algoType === 'RS') { + signature = urlencodeBase64 (rsa (token, secret, alg)) + } + return [ token, signature ].join ('.') +} + +function ecdsa (request, secret, algorithm = 'p256', hashFunction = undefined, fixedLength = false) { + let digest = request + if (hashFunction !== undefined) { + digest = hash (request, hashFunction, 'hex') + } + const curve = new EC (algorithm) + let signature = curve.sign (digest, secret, 'hex', { 'canonical': true }) + let counter = new BN ('0') + const minimum_size = new BN ('1').shln (8 * 31).sub (new BN ('1')) + while (fixedLength && (signature.r.gt (curve.nh) || signature.r.lte (minimum_size) || signature.s.lte (minimum_size))) { + signature = curve.sign (digest, secret, 'hex', { 'canonical': true, 'extraEntropy': counter.toArray ('le', 32)}) + counter = counter.add (new BN ('1')) + } + return { + 'r': signature.r.toString (16).padStart (64, '0'), + 's': signature.s.toString (16).padStart (64, '0'), + 'v': signature.recoveryParam, + } +} + +/* ------------------------------------------------------------------------ */ + +const totp = (secret) => { + + const dec2hex = s => ((s < 15.5 ? '0' : '') + Math.round (s).toString (16)) + , hex2dec = s => parseInt (s, 16) + , leftpad = (s, p) => (p + s).slice (-p.length) // both s and p are short strings + + const base32tohex = (base32) => { + let base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' + let bits = '' + let hex = '' + for (let i = 0; i < base32.length; i++) { + let val = base32chars.indexOf (base32.charAt (i).toUpperCase ()) + bits += leftpad (val.toString (2), '00000') + } + for (let i = 0; i + 4 <= bits.length; i += 4) { + let chunk = bits.substr (i, 4) + hex = hex + parseInt (chunk, 2).toString (16) + } + return hex + } + + const getOTP = (secret) => { + secret = secret.replace (' ', '') // support 2fa-secrets with spaces like "4TDV WOGO" → "4TDVWOGO" + let epoch = Math.round (new Date ().getTime () / 1000.0) + let time = leftpad (dec2hex (Math.floor (epoch / 30)), '0000000000000000') + let hmacRes = hmac (CryptoJS.enc.Hex.parse (time), CryptoJS.enc.Hex.parse (base32tohex (secret)), 'sha1', 'hex') + let offset = hex2dec (hmacRes.substring (hmacRes.length - 1)) + let otp = (hex2dec (hmacRes.substr (offset * 2, 8)) & hex2dec ('7fffffff')) + '' + otp = (otp).substr (otp.length - 6, 6) + return otp + } + + return getOTP (secret) +} + +/* ------------------------------------------------------------------------ */ + +module.exports = { + hash, + hmac, + jwt, + totp, + rsa, + ecdsa, +} + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/encode.js b/TaskServer/node_modules/ccxt/js/base/functions/encode.js new file mode 100644 index 000000000..843fdb0ad --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/encode.js @@ -0,0 +1,71 @@ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js') +const qs = require ('../../static_dependencies/qs/index') +const BN = require ('../../static_dependencies/BN/bn') + +/* ------------------------------------------------------------------------ */ + +module.exports = + + { json: (data, params = undefined) => JSON.stringify (data) + , unjson: JSON.parse + + , isJsonEncodedObject: object => ( + (typeof object === 'string') && + (object.length >= 2) && + ((object[0] === '{') || (object[0] === '[')) + ) + + , stringToBinary (str) { + const arr = new Uint8Array (str.length) + for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt (i); } + return CryptoJS.lib.WordArray.create (arr) + } + + , stringToBase64: string => CryptoJS.enc.Latin1.parse (string).toString (CryptoJS.enc.Base64) + , utf16ToBase64: string => CryptoJS.enc.Utf16 .parse (string).toString (CryptoJS.enc.Base64) + , base64ToBinary: string => CryptoJS.enc.Base64.parse (string) + , base64ToString: string => CryptoJS.enc.Base64.parse (string).toString (CryptoJS.enc.Utf8) + , binaryToBase64: binary => binary.toString (CryptoJS.enc.Base64) + , base16ToBinary: string => CryptoJS.enc.Hex.parse (string) + , binaryToBase16: binary => binary.toString (CryptoJS.enc.Hex) + , binaryConcat: (...args) => args.reduce ((a, b) => a.concat (b)) + , binaryConcatArray: (arr) => arr.reduce ((a, b) => a.concat (b)) + + , urlencode: object => qs.stringify (object) + , urlencodeWithArrayRepeat: object => qs.stringify (object, { arrayFormat: 'repeat' }) + , rawencode: object => qs.stringify (object, { encode: false }) + , encode: x => x + , decode: x => x + + // Url-safe-base64 without equals signs, with + replaced by - and slashes replaced by underscores + + , urlencodeBase64: base64string => base64string.replace (/[=]+$/, '') + .replace (/\+/g, '-') + .replace (/\//g, '_') + + , numberToLE: (n, padding) => { + const hexArray = new BN (n).toArray ('le', padding) + return byteArrayToWordArray (hexArray) + } + + , numberToBE: (n, padding) => { + const hexArray = new BN (n).toArray ('be', padding) + return byteArrayToWordArray (hexArray) + } +} + +function byteArrayToWordArray(ba) { + const wa = [] + for (let i = 0; i < ba.length; i++) { + wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i) + } + return CryptoJS.lib.WordArray.create (wa, ba.length) +} + +module.exports['byteArrayToWordArray'] = byteArrayToWordArray + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/generic.js b/TaskServer/node_modules/ccxt/js/base/functions/generic.js new file mode 100644 index 000000000..3cb881a5f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/generic.js @@ -0,0 +1,241 @@ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const { isObject, isNumber, isDictionary, isArray } = require ('./type') + +/* ------------------------------------------------------------------------ */ + +const keys = Object.keys + + , values = x => !isArray (x) // don't copy arrays if they're already arrays! + ? Object.values (x) + : x + + , index = x => new Set (values (x)) + + , extend = (...args) => Object.assign ({}, ...args) // NB: side-effect free + + , clone = x => isArray (x) + ? Array.from (x) // clones arrays + : extend (x) // clones objects + +/* ------------------------------------------------------------------------ */ + +module.exports = + + { keys + , values + , extend + , clone + , index + , ordered: x => x // a stub to keep assoc keys in order (in JS it does nothing, it's mostly for Python) + , unique: x => Array.from (index (x)) + + /* ............................................. */ + + , inArray (needle, haystack) { + + return haystack.includes (needle) + } + + , toArray (object) { + + return Object.values (object) + } + + , isEmpty (object) { + if (!object) + return true; + return (Array.isArray (object) ? object : Object.keys (object)).length < 1; + } + +/* ............................................. */ + + , keysort (x, out = {}) { + + for (const k of keys (x).sort ()) + out[k] = x[k] + + return out + } + +/* ............................................. */ + + /* + Accepts a map/array of objects and a key name to be used as an index: + array = [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value2', anotherKey: 'anotherValue2' }, + { someKey: 'value3', anotherKey: 'anotherValue3' }, + ] + key = 'someKey' + + Returns a map: + { + value1: { someKey: 'value1', anotherKey: 'anotherValue1' }, + value2: { someKey: 'value2', anotherKey: 'anotherValue2' }, + value3: { someKey: 'value3', anotherKey: 'anotherValue3' }, + } + */ + + , indexBy (x, k, out = {}) { + + for (const v of values (x)) + if (k in v) + out[v[k]] = v + + return out + } + +/* ............................................. */ + + /* + Accepts a map/array of objects and a key name to be used as a grouping parameter: + array = [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value1', anotherKey: 'anotherValue2' }, + { someKey: 'value3', anotherKey: 'anotherValue3' }, + ] + key = 'someKey' + + Returns a map: + { + value1: [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value1', anotherKey: 'anotherValue2' }, + ] + value3: [ + { someKey: 'value3', anotherKey: 'anotherValue3' } + ], + } + */ + + , groupBy (x, k, out = {}) { + + for (const v of values (x)) { + if (k in v) { + const p = v[k] + out[p] = out[p] || [] + out[p].push (v) + } + } + return out + } + +/* ............................................. */ + + /* + Accepts a map/array of objects, a key name and a key value to be used as a filter: + array = [ + { someKey: 'value1', anotherKey: 'anotherValue1' }, + { someKey: 'value2', anotherKey: 'anotherValue2' }, + { someKey: 'value3', anotherKey: 'anotherValue3' }, + ] + key = 'someKey' + value = 'value1' + + Returns an array: + [ + value1: { someKey: 'value1', anotherKey: 'anotherValue1' }, + ] + */ + + , filterBy (x, k, value = undefined, out = []) { + + for (const v of values (x)) + if (v[k] === value) + out.push (v) + + return out + } + +/* ............................................. */ + + , sortBy: (array, // NB: MUTATES ARRAY! + key, + descending = false, + direction = descending ? -1 : 1) => array.sort ((a, b) => + ((a[key] < b[key]) ? -direction : + ((a[key] > b[key]) ? direction : 0))) + +/* ............................................. */ + + , flatten: function flatten (x, out = []) { + + for (const v of x) { + if (isArray (v)) flatten (v, out) + else out.push (v) + } + + return out + } + +/* ............................................. */ + + , pluck: (x, k) => values (x) + .filter (v => k in v) + .map (v => v[k]) + +/* ............................................. */ + + , omit (x, ...args) { + + if (!Array.isArray (x)) { + + const out = clone (x) + + for (const k of args) { + + if (isArray (k)) { // omit (x, ['a', 'b']) + + for (const kk of k) { + delete out[kk] + } + } + + else delete out[k] // omit (x, 'a', 'b') + } + + return out + } + + return x + } + +/* ............................................. */ + + , sum (...xs) { + + const ns = xs.filter (isNumber) // leave only numbers + + return (ns.length > 0) + ? ns.reduce ((a, b) => a + b, 0) + : undefined + } + +/* ............................................. */ + + , deepExtend: function deepExtend (...xs) { + + let out = undefined + + for (const x of xs) { + + if (isDictionary (x)) { + + if (!isObject (out)) + out = {} + + for (const k in x) + out[k] = deepExtend (out[k], x[k]) + + } else out = x + } + + return out + } + +/* ------------------------------------------------------------------------ */ + +} diff --git a/TaskServer/node_modules/ccxt/js/base/functions/misc.js b/TaskServer/node_modules/ccxt/js/base/functions/misc.js new file mode 100644 index 000000000..72570df00 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/misc.js @@ -0,0 +1,131 @@ +'use strict'; + +const { ROUND_UP, ROUND_DOWN } = require ('./number') +const { NotSupported } = require ('./../errors') + +//------------------------------------------------------------------------- +// converts timeframe to seconds +const parseTimeframe = (timeframe) => { + + const amount = timeframe.slice (0, -1) + const unit = timeframe.slice (-1) + let scale = undefined; + + if (unit === 'y') { + scale = 60 * 60 * 24 * 365 + } else if (unit === 'M') { + scale = 60 * 60 * 24 * 30 + } else if (unit === 'w') { + scale = 60 * 60 * 24 * 7 + } else if (unit === 'd') { + scale = 60 * 60 * 24 + } else if (unit === 'h') { + scale = 60 * 60 + } else if (unit === 'm') { + scale = 60 + } else if (unit === 's') { + scale = 1 + } else { + throw new NotSupported ('timeframe unit ' + unit + ' is not supported') + } + + return amount * scale +} + +const roundTimeframe = (timeframe, timestamp, direction = ROUND_DOWN) => { + const ms = parseTimeframe (timeframe) * 1000 + // Get offset based on timeframe in milliseconds + const offset = timestamp % ms + return timestamp - offset + ((direction === ROUND_UP) ? ms : 0); +} + +// given a sorted arrays of trades (recent last) and a timeframe builds an array of OHLCV candles +const buildOHLCVC = (trades, timeframe = '1m', since = -Infinity, limit = Infinity) => { + const ms = parseTimeframe (timeframe) * 1000; + const ohlcvs = []; + const [ timestamp, /* open */, high, low, close, volume, count ] = [ 0, 1, 2, 3, 4, 5, 6 ]; + const oldest = Math.min (trades.length - 1, limit); + + for (let i = 0; i <= oldest; i++) { + const trade = trades[i]; + if (trade.timestamp < since) + continue; + const openingTime = Math.floor (trade.timestamp / ms) * ms; // shift to the edge of m/h/d (but not M) + const candle = ohlcvs.length - 1; + + if (candle === -1 || openingTime >= ohlcvs[candle][timestamp] + ms) { + // moved to a new timeframe -> create a new candle from opening trade + ohlcvs.push ([ + openingTime, // timestamp + trade.price, // O + trade.price, // H + trade.price, // L + trade.price, // C + trade.amount, // V + 1, // count + ]); + } else { + // still processing the same timeframe -> update opening trade + ohlcvs[candle][high] = Math.max (ohlcvs[candle][high], trade.price); + ohlcvs[candle][low] = Math.min (ohlcvs[candle][low], trade.price); + ohlcvs[candle][close] = trade.price; + ohlcvs[candle][volume] += trade.amount; + ohlcvs[candle][count]++; + } // if + } // for + return ohlcvs; +} + +const extractParams = (string) => { + const re = /{([\w-]+)}/g + const matches = [] + let match = re.exec (string) + while (match) { + matches.push (match[1]) + match = re.exec (string) + } + return matches +} + +const implodeParams = (string, params) => { + if (!Array.isArray (params)) { + const keys = Object.keys (params) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (!Array.isArray (params[key])) { + string = string.replace ('{' + key + '}', params[key]) + } + } + } + return string +} + +/* ------------------------------------------------------------------------ */ + +module.exports = { + + aggregate (bidasks) { + + const result = {} + + for (let i = 0; i < bidasks.length; i++) { + const [ price, volume ] = bidasks[i]; + if (volume > 0) { + result[price] = (result[price] || 0) + volume + } + } + + return Object.keys (result).map (price => [parseFloat (price), parseFloat (result[price])]) + }, + + parseTimeframe, + roundTimeframe, + buildOHLCVC, + ROUND_UP, + ROUND_DOWN, + + implodeParams, + extractParams +} + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/number.js b/TaskServer/node_modules/ccxt/js/base/functions/number.js new file mode 100644 index 000000000..5f6ce75e4 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/number.js @@ -0,0 +1,306 @@ +'use strict' + +/* ------------------------------------------------------------------------ + + NB: initially, I used objects for options passing: + + decimalToPrecision ('123.456', { digits: 2, round: true, afterPoint: true }) + + ...but it turns out it's hard to port that across different languages and it is also + probably has a performance penalty -- while it's a performance critical code! So + I switched to using named constants instead, as it is actually more readable and + succinct, and surely doesn't come with any inherent performance downside: + + decimalToPrecision ('123.456', ROUND, 2, DECIMAL_PLACES) */ + +const ROUND = 0 // rounding mode + , TRUNCATE = 1 + , ROUND_UP = 2 + , ROUND_DOWN = 3 + +const DECIMAL_PLACES = 0 // digits counting mode + , SIGNIFICANT_DIGITS = 1 + , TICK_SIZE = 2 + +const NO_PADDING = 0 // zero-padding mode + , PAD_WITH_ZERO = 1 + +const precisionConstants = { + ROUND, + TRUNCATE, + ROUND_UP, + ROUND_DOWN, + DECIMAL_PLACES, + SIGNIFICANT_DIGITS, + TICK_SIZE, + NO_PADDING, + PAD_WITH_ZERO, +} + +/* ------------------------------------------------------------------------ */ + +// See https://stackoverflow.com/questions/1685680/how-to-avoid-scientific-notation-for-large-numbers-in-javascript for discussion + +function numberToString (x) { // avoids scientific notation for too large and too small numbers + + if (typeof x === 'string') return x + + if (Math.abs (x) < 1.0) { + const s = x.toString () + const e = parseInt (s.split ('e-')[1]) + const neg = (s[0] === '-') + if (e) { + x *= Math.pow (10, e - 1) + x = (neg ? '-' : '') + '0.' + (new Array (e)).join ('0') + x.toString ().substring (neg ? 3 : 2) + } + } else { + let e = parseInt (x.toString ().split ('+')[1]) + if (e > 20) { + e -= 20 + x /= Math.pow (10, e) + x += (new Array (e + 1)).join ('0') + } + } + return x.toString () +} + +//----------------------------------------------------------------------------- +// expects non-scientific notation + +const truncate_regExpCache = [] + , truncate_to_string = (num, precision = 0) => { + num = numberToString (num) + if (precision > 0) { + const re = truncate_regExpCache[precision] || (truncate_regExpCache[precision] = new RegExp ("([-]*\\d+\\.\\d{" + precision + "})(\\d)")) + const [ , result] = num.toString ().match (re) || [null, num] + return result.toString () + } + return parseInt (num).toString () + } + , truncate = (num, precision = 0) => parseFloat (truncate_to_string (num, precision)) + +function precisionFromString (string) { + const split = string.replace (/0+$/g, '').split ('.') + return (split.length > 1) ? (split[1].length) : 0 +} + +/* ------------------------------------------------------------------------ */ + +const decimalToPrecision = (x, roundingMode + , numPrecisionDigits + , countingMode = DECIMAL_PLACES + , paddingMode = NO_PADDING) => { + + if (numPrecisionDigits < 0) { + if (countingMode === TICK_SIZE) { + throw new Error (`TICK_SIZE cant be used with negative numPrecisionDigits`) + } + const toNearest = Math.pow (10, -numPrecisionDigits) + if (roundingMode === ROUND) { + return (toNearest * decimalToPrecision (x / toNearest, roundingMode, 0, countingMode, paddingMode)).toString () + } + if (roundingMode === TRUNCATE) { + return (x - (x % toNearest)).toString () + } + } + +/* handle tick size */ + if (countingMode === TICK_SIZE) { + const missing = x % numPrecisionDigits + const reminder = x / numPrecisionDigits + if (reminder !== Math.floor (reminder)) { + if (roundingMode === ROUND) { + if (x > 0) { + if (missing >= numPrecisionDigits / 2) { + x = x - missing + numPrecisionDigits + } else { + x = x - missing + } + } else { + if (missing >= numPrecisionDigits / 2) { + x = Number (x) - missing + } else { + x = Number (x) - missing - numPrecisionDigits + } + } + } else if (roundingMode === TRUNCATE) { + x = x - missing + } + } + const precisionDigitsString = decimalToPrecision (numPrecisionDigits, ROUND, 100, DECIMAL_PLACES, NO_PADDING) + const newNumPrecisionDigits = precisionFromString (precisionDigitsString) + return decimalToPrecision (x, ROUND, newNumPrecisionDigits, DECIMAL_PLACES, paddingMode); + } + +/* Convert to a string (if needed), skip leading minus sign (if any) */ + + const str = numberToString (x) + , isNegative = str[0] === '-' + , strStart = isNegative ? 1 : 0 + , strEnd = str.length + +/* Find the dot position in the source buffer */ + + for (var strDot = 0; strDot < strEnd; strDot++) + if (str[strDot] === '.') + break + + const hasDot = strDot < str.length + +/* Char code constants */ + + const MINUS = 45 + , DOT = 46 + , ZERO = 48 + , ONE = (ZERO + 1) + , FIVE = (ZERO + 5) + , NINE = (ZERO + 9) + +/* For -123.4567 the `chars` array will hold 01234567 (leading zero is reserved for rounding cases when 099 → 100) */ + + const chars = new Uint8Array ((strEnd - strStart) + (hasDot ? 0 : 1)) + chars[0] = ZERO + +/* Validate & copy digits, determine certain locations in the resulting buffer */ + + let afterDot = chars.length + , digitsStart = -1 // significant digits + , digitsEnd = -1 + + for (var i = 1, j = strStart; j < strEnd; j++, i++) { + + const c = str.charCodeAt (j) + + if (c === DOT) { + afterDot = i-- + + } else if ((c < ZERO) || (c > NINE)) { + throw new Error (`${str}: invalid number (contains an illegal character '${str[i - 1]}')`) + + } else { + chars[i] = c + if ((c !== ZERO) && (digitsStart < 0)) digitsStart = i + } + } + + if (digitsStart < 0) digitsStart = 1 + +/* Determine the range to cut */ + + let precisionStart = (countingMode === DECIMAL_PLACES) ? afterDot // 0.(0)001234567 + : digitsStart // 0.00(1)234567 + , precisionEnd = precisionStart + + numPrecisionDigits + +/* Reset the last significant digit index, as it will change during the rounding/truncation. */ + + digitsEnd = -1 + +/* Perform rounding/truncation per digit, from digitsEnd to digitsStart, by using the following + algorithm (rounding 999 → 1000, as an example): + + step = i=3 i=2 i=1 i=0 + + chars = 0999 0999 0900 1000 + memo = ---0 --1- -1-- 0--- */ + + let allZeros = true; + let signNeeded = isNegative; + + for (let i = chars.length - 1, memo = 0; i >= 0; i--) { + + let c = chars[i] + + if (i !== 0) { + c += memo + + if (i >= (precisionStart + numPrecisionDigits)) { + + const ceil = (roundingMode === ROUND) && + (c >= FIVE) && + !((c === FIVE) && memo) // prevents rounding of 1.45 to 2 + + c = ceil ? (NINE + 1) : ZERO + } + if (c > NINE) { c = ZERO; memo = 1; } + else memo = 0 + + } else if (memo) c = ONE // leading extra digit (0900 → 1000) + + chars[i] = c + + if (c !== ZERO) { + allZeros = false + digitsStart = i + digitsEnd = (digitsEnd < 0) ? (i + 1) : digitsEnd + } + } + +/* Update the precision range, as `digitsStart` may have changed... & the need for a negative sign if it is only 0 */ + + if (countingMode === SIGNIFICANT_DIGITS) { + precisionStart = digitsStart + precisionEnd = precisionStart + numPrecisionDigits + } + + if (allZeros) { + signNeeded = false + } + +/* Determine the input character range */ + + const readStart = ((digitsStart >= afterDot) || allZeros) ? (afterDot - 1) : digitsStart // 0.000(1)234 ----> (0).0001234 + , readEnd = (digitsEnd < afterDot) ? (afterDot ) : digitsEnd // 12(3)000 ----> 123000( ) + +/* Compute various sub-ranges */ + + const nSign = (signNeeded ? 1 : 0) // (-)123.456 + , nBeforeDot = (nSign + (afterDot - readStart)) // (-123).456 + , nAfterDot = Math.max (readEnd - afterDot, 0) // -123.(456) + , actualLength = (readEnd - readStart) // -(123.456) + , desiredLength = (paddingMode === NO_PADDING) + ? (actualLength) // -(123.456) + : (precisionEnd - readStart) // -(123.456 ) + + , pad = Math.max (desiredLength - actualLength, 0) // -123.456( ) + , padStart = (nBeforeDot + 1 + nAfterDot) // -123.456( ) + , padEnd = (padStart + pad) // -123.456 ( ) + , isInteger = (nAfterDot + pad) === 0 // -123 + +/* Fill the output buffer with characters */ + + const out = new Uint8Array (nBeforeDot + (isInteger ? 0 : 1) + nAfterDot + pad) + // ------------------------------------------------------------------------------------------ // --------------------- + if (signNeeded) out[0] = MINUS // - minus sign + for (i = nSign, j = readStart; i < nBeforeDot; i++, j++) out[i] = chars[j] // 123 before dot + if (!isInteger) out[nBeforeDot] = DOT // . dot + for (i = nBeforeDot + 1, j = afterDot; i < padStart; i++, j++) out[i] = chars[j] // 456 after dot + for (i = padStart; i < padEnd; i++) out[i] = ZERO // 000 padding + +/* Build a string from the output buffer */ + + return String.fromCharCode (...out) +} + +/* ------------------------------------------------------------------------ */ + +module.exports = { + + numberToString, + precisionFromString, + decimalToPrecision, + truncate_to_string, + truncate, + precisionConstants, + ROUND, + TRUNCATE, + ROUND_UP, + ROUND_DOWN, + DECIMAL_PLACES, + SIGNIFICANT_DIGITS, + TICK_SIZE, + NO_PADDING, + PAD_WITH_ZERO, +} + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/orders.js b/TaskServer/node_modules/ccxt/js/base/functions/orders.js new file mode 100644 index 000000000..b988e8a75 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/orders.js @@ -0,0 +1,59 @@ +'use strict'; + +const updateCachedOrders = function (openOrders, symbol) { +} + +const updateOrders = function (oldOrders, newOrders, closeOpenOrders = false) { + + for (let i = 0; i < newOrders.length; i++) { + let newOrder = newOrders[i]; + } + + // update local cache with open orders + for (let j = 0; j < openOrders.length; j++) { + const id = openOrders[j]['id']; + this.orders[id] = openOrders[j]; + } + let openOrdersIndexedById = this.indexBy (openOrders, 'id'); + let cachedOrderIds = Object.keys (this.orders); + let result = []; + for (let k = 0; k < cachedOrderIds.length; k++) { + // match each cached order to an order in the open orders array + // possible reasons why a cached order may be missing in the open orders array: + // - order was closed or canceled -> update cache + // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip + let id = cachedOrderIds[k]; + let order = this.orders[id]; + result.push (order); + if (!(id in openOrdersIndexedById)) { + // cached order is not in open orders array + // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order + if (symbol !== undefined && symbol !== order['symbol']) + continue; + // order is cached but not present in the list of open orders -> mark the cached order as closed + if (order['status'] === 'open') { + order = this.extend (order, { + 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") + 'cost': undefined, + 'filled': order['amount'], + 'remaining': 0.0, + }); + if (order['cost'] === undefined) { + if (order['filled'] !== undefined) + order['cost'] = order['filled'] * order['price']; + } + this.orders[id] = order; + } + } + } + return result; + + console.log (this.id, orders, openOrders, closedOrders, canceledOrders) + process.exit () +} +// ----------------------------------------------------------------------------- + +module.exports = { + + updateOrders, +} diff --git a/TaskServer/node_modules/ccxt/js/base/functions/platform.js b/TaskServer/node_modules/ccxt/js/base/functions/platform.js new file mode 100644 index 000000000..de3b1874a --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/platform.js @@ -0,0 +1,38 @@ +"use strict"; + +// ---------------------------------------------------------------------------- +// There's been a lot of messing with this code... +// The problem is to satisfy the following requirements: +// - properly detect isNode == true on server side and isNode == false in the browser (on client side) +// - make sure create-react-app, react-starter-kit and other react frameworks work +// - make sure it does not break the browserified version (when linked into a html from a cdn) +// - make sure it does not break the webpacking and babel-transpiled scripts +// - make sure it works in Electron +// - make sure it works with Angular.js +// - make sure it does not break other possible usage scenarios + +const isBrowser = typeof window !== 'undefined' + +const isElectron = typeof process !== 'undefined' && + typeof process.versions !== 'undefined' && + typeof process.versions.electron !== 'undefined' + +const isWebWorker = typeof WorkerGlobalScope !== 'undefined' && (self instanceof WorkerGlobalScope) + +const isWindows = typeof process !== 'undefined' && process.platform === "win32" + +const isNode = !(isBrowser || isWebWorker) + +const defaultFetch = typeof (fetch) === "undefined" ? require ('../../static_dependencies/fetch-ponyfill/fetch-node') ().fetch : fetch + +// ---------------------------------------------------------------------------- + +module.exports = { + + isBrowser, + isElectron, + isWebWorker, + isNode, + isWindows, + defaultFetch, +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/base/functions/string.js b/TaskServer/node_modules/ccxt/js/base/functions/string.js new file mode 100644 index 000000000..cabc63b48 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/string.js @@ -0,0 +1,21 @@ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const uuid = a => a ? (a ^ Math.random () * 16 >> a / 4).toString (16) + : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid) + +module.exports = + + { uuid + + // hasFetchOHLCV → has_fetch_ohlcv; parseHTTPResponse → parse_http_response + , unCamelCase: s => s.match (/^[A-Z0-9_]+$/) ? s : (s.replace (/[a-z0-9][A-Z]/g, x => x[0] + '_' + x[1]).replace(/[A-Z0-9][A-Z0-9][a-z]/g, x => x[0] + '_' + x[1] + x[2]).toLowerCase ()) + + , capitalize: s => s.length + ? (s.charAt (0).toUpperCase () + s.slice (1)) + : s + , strip: s => s.replace(/^\s+|\s+$/g, '') + } + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/throttle.js b/TaskServer/node_modules/ccxt/js/base/functions/throttle.js new file mode 100644 index 000000000..a6a15f0ab --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/throttle.js @@ -0,0 +1,64 @@ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const { sleep + , now } = require ('./time') + +/* ------------------------------------------------------------------------ */ + +module.exports = { + + throttle: function throttle (cfg) { + + let lastTimestamp = now () + , numTokens = (cfg.numTokens !== undefined) ? cfg.numTokens : cfg.capacity + , running = false + , counter = 0 + + const queue = [] + + return Object.assign ((cost) => { + + if (queue.length > cfg.maxCapacity) + throw new Error ('Backlog is over max capacity of ' + cfg.maxCapacity) + + return new Promise (async (resolve, reject) => { + + try { + queue.push ({ cost, resolve, reject }) + + if (!running) { + running = true + while (queue.length > 0) { + const hasEnoughTokens = cfg.capacity ? (numTokens > 0) : (numTokens >= 0) + if (hasEnoughTokens) { + if (queue.length > 0) { + let { cost, resolve, reject } = queue[0] + cost = (cost || cfg.defaultCost) + if (numTokens >= Math.min (cost, cfg.capacity)) { + numTokens -= cost + queue.shift () + resolve () + } + } + } + const t = now () + , elapsed = t - lastTimestamp + lastTimestamp = t + numTokens = Math.min (cfg.capacity, numTokens + elapsed * cfg.refillRate) + await sleep (cfg.delay) + } + running = false + } + + } catch (e) { + reject (e) + } + }) + + }, cfg, { configure: newCfg => throttle (Object.assign ({}, cfg, newCfg)) }) + } +} + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/time.js b/TaskServer/node_modules/ccxt/js/base/functions/time.js new file mode 100644 index 000000000..83d646d17 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/time.js @@ -0,0 +1,185 @@ +'use strict'; + +/* ------------------------------------------------------------------------ */ + +const now = Date.now // TODO: figure out how to utilize performance.now () properly – it's not as easy as it does not return a unix timestamp... +const microseconds = () => now () * 1000 // TODO: utilize performance.now for that purpose +const milliseconds = now +const seconds = () => Math.floor (now () / 1000) + +/* ------------------------------------------------------------------------ */ + +const setTimeout_original = setTimeout +const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = now () + ms) => { + +/* The built-in setTimeout function can fire its callback earlier than specified, so we + need to ensure that it does not happen: sleep recursively until `targetTime` is reached... */ + + let clearInnerTimeout = () => {} + let active = true + + const id = setTimeout (() => { + active = true + const rest = targetTime - now () + if (rest > 0) { + clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime) // try sleep more + } else { + done () + } + }, ms) + + return function clear () { + if (active) { + active = false // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion) + clearTimeout (id) + } + clearInnerTimeout () + } +} + +/* ------------------------------------------------------------------------ */ + +class TimedOut extends Error { + + constructor () { + const message = 'timed out' + super (message) + this.constructor = TimedOut + this.__proto__ = TimedOut.prototype + this.message = message + } +} + +/* ------------------------------------------------------------------------ */ + +const iso8601 = (timestamp) => { + let _timestampNumber = undefined; + if (typeof timestamp === 'number') { + _timestampNumber = Math.floor (timestamp); + } else { + _timestampNumber = parseInt (timestamp, 10); + } + + // undefined, null and lots of nasty non-numeric values yield NaN + if (Number.isNaN (_timestampNumber) || _timestampNumber < 0) { + return undefined; + } + + // last line of defence + try { + return new Date (_timestampNumber).toISOString (); + } catch (e) { + return undefined; + } +} + +const parse8601 = (x) => { + if (typeof x !== 'string' || !x) { + return undefined; + } + + if (x.match (/^[0-9]+$/)) { + // a valid number in a string, not a date. + return undefined; + } + + if (x.indexOf ('-') < 0 || x.indexOf (':') < 0) { // no date can be without a dash and a colon + return undefined; + } + + // last line of defence + try { + const candidate = Date.parse (((x.indexOf ('+') >= 0) || (x.slice (-1) === 'Z')) ? x : (x + 'Z').replace (/\s(\d\d):/, 'T$1:')); + if (Number.isNaN (candidate)) { + return undefined; + } + return candidate; + } catch (e) { + return undefined; + } +} + +const parseDate = (x) => { + if (typeof x !== 'string' || !x) { + return undefined; + } + + if (x.indexOf ('GMT') >= 0) { + try { + return Date.parse (x); + } catch (e) { + return undefined; + } + } + + return parse8601 (x); +} + +const mdy = (timestamp, infix = '-') => { + infix = infix || '' + const date = new Date (timestamp) + const Y = date.getUTCFullYear ().toString () + let m = date.getUTCMonth () + 1 + let d = date.getUTCDate () + m = m < 10 ? ('0' + m) : m.toString () + d = d < 10 ? ('0' + d) : d.toString () + return m + infix + d + infix + Y +} + +const ymd = (timestamp, infix = '-') => { + infix = infix || '' + const date = new Date (timestamp) + const Y = date.getUTCFullYear ().toString () + let m = date.getUTCMonth () + 1 + let d = date.getUTCDate () + m = m < 10 ? ('0' + m) : m.toString () + d = d < 10 ? ('0' + d) : d.toString () + return Y + infix + m + infix + d +} + +const ymdhms = (timestamp, infix = ' ') => { + const date = new Date (timestamp) + const Y = date.getUTCFullYear () + let m = date.getUTCMonth () + 1 + let d = date.getUTCDate () + let H = date.getUTCHours () + let M = date.getUTCMinutes () + let S = date.getUTCSeconds () + m = m < 10 ? ('0' + m) : m + d = d < 10 ? ('0' + d) : d + H = H < 10 ? ('0' + H) : H + M = M < 10 ? ('0' + M) : M + S = S < 10 ? ('0' + S) : S + return Y + '-' + m + '-' + d + infix + H + ':' + M + ':' + S +} + +module.exports = + + { + now + , microseconds + , milliseconds + , seconds + , iso8601 + , parse8601 + , parseDate + , mdy + , ymd + , ymdhms + , setTimeout_safe + , sleep: ms => new Promise (resolve => setTimeout_safe (resolve, ms)) + , TimedOut + , timeout: async (ms, promise) => { + + let clear = () => {} + const expires = new Promise (resolve => (clear = setTimeout_safe (resolve, ms))) + + try { + return await Promise.race ([promise, expires.then (() => { throw new TimedOut () })]) + } finally { + clear () // fixes https://github.com/ccxt/ccxt/issues/749 + } + } +} + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/base/functions/type.js b/TaskServer/node_modules/ccxt/js/base/functions/type.js new file mode 100644 index 000000000..c65a098db --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/base/functions/type.js @@ -0,0 +1,63 @@ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const isNumber = Number.isFinite + , isArray = Array.isArray + , hasProps = o => ((o !== undefined) && (o !== null)) + , isString = s => (typeof s === 'string') + , isObject = o => ((o !== null) && (typeof o === 'object')) + , isDictionary = o => (isObject (o) && !isArray (o)) + , isStringCoercible = x => ((hasProps (x) && x.toString) || isNumber (x)) + +/* ............................................. */ + +const prop = (o, k) => (isObject (o) ? o[k] : undefined) + , prop2 = (o, k1, k2) => (!isObject (o) ? undefined : (((k1 in o) && (o[k1] !== null)) ? o[k1] : o[k2])) + +/* ............................................. */ + +const asFloat = x => ((isNumber (x) || isString (x)) ? parseFloat (x) : NaN) + , asInteger = x => ((isNumber (x) || isString (x)) ? parseInt (x, 10) : NaN) + +/* ............................................. */ + +module.exports = { + + isNumber + , isArray + , isObject + , isString + , isStringCoercible + , isDictionary + + , hasProps + , prop + + , asFloat + , asInteger + + , safeFloat: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? n : $default) + , safeInteger: (o, k, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? n : $default) + , safeIntegerProduct: (o, k, $factor, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? parseInt (n * $factor) : $default) + , safeTimestamp: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? parseInt (n * 1000) : $default) + , safeValue: (o, k, $default, x = prop (o, k)) => (hasProps (x) ? x : $default) + , safeString: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x) : $default) + , safeStringLower: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default) + , safeStringUpper: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default) + + // not using safeFloats with an array argument as we're trying to save some cycles here + // we're not using safeFloat3 either because those cases are too rare to deserve their own optimization + + , safeFloat2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default) + , safeInteger2: (o, k1, k2, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default) + , safeIntegerProduct2: (o, k1, k2, $factor, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * $factor) : $default) + , safeTimestamp2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * 1000) : $default) + , safeValue2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (hasProps (x) ? x : $default) + , safeString2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x) : $default) + , safeStringLower2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default) + , safeStringUpper2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default) + +} + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/bcex.js b/TaskServer/node_modules/ccxt/js/bcex.js new file mode 100644 index 000000000..afd15bc10 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bcex.js @@ -0,0 +1,686 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bcex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bcex', + 'name': 'BCEX', + 'countries': [ 'CN', 'CA' ], + 'version': '1', + 'has': { + 'fetchBalance': true, + 'fetchMarkets': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchTrades': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchClosedOrders': 'emulated', + 'fetchOpenOrders': true, + 'fetchTradingLimits': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/43362240-21c26622-92ee-11e8-9464-5801ec526d77.jpg', + 'api': 'https://www.bcex.top', + 'www': 'https://www.bcex.top', + 'doc': 'https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface', + 'fees': 'https://bcex.udesk.cn/hc/articles/57085', + 'referral': 'https://www.bcex.top/register?invite_code=758978&lang=en', + }, + 'status': { + 'status': 'error', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'api': { + 'public': { + 'get': [ + 'Api_Market/getPriceList', // tickers + 'Api_Order/ticker', // last ohlcv candle (ticker) + 'Api_Order/depth', // orderbook + 'Api_Market/getCoinTrade', // ticker + 'Api_Order/marketOrder', // trades... + ], + 'post': [ + 'Api_Market/getPriceList', // tickers + 'Api_Order/ticker', // last ohlcv candle (ticker) + 'Api_Order/depth', // orderbook + 'Api_Market/getCoinTrade', // ticker + 'Api_Order/marketOrder', // trades... + ], + }, + 'private': { + 'post': [ + 'Api_Order/cancel', + 'Api_Order/coinTrust', // limit order + 'Api_Order/orderList', // open / all orders (my trades?) + 'Api_Order/orderInfo', + 'Api_Order/tradeList', // open / all orders + 'Api_Order/trustList', // ? + 'Api_User/userBalance', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'buy': 0.0, + 'sell': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'ckusd': 0.0, + 'other': 0.05 / 100, + }, + 'deposit': {}, + }, + }, + 'exceptions': { + '该��存在,�法�作': ExchangeError, // { code: 1, msg: "该��存在,�法�作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well) + '公钥��法': AuthenticationError, // { code: 1, msg: '公钥��法' } - wrong public key + '您的�用余��足': InsufficientFunds, // { code: 1, msg: '您的�用余��足' } - your available balance is insufficient + '您的btc�足': InsufficientFunds, // { code: 1, msg: '您的btc�足' } - your btc is insufficient + '�数�法': InvalidOrder, // {'code': 1, 'msg': '�数�法'} - 'Parameter illegal' + '订�信��存在': OrderNotFound, // {'code': 1, 'msg': '订�信��存在'} - 'Order information does not exist' + }, + 'options': { + 'limits': { + // hardcoding is deprecated, using these predefined values is not recommended, use loadTradingLimits instead + 'AFC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}}, + 'AFC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}}, + 'AFT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}}, + 'AICC/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}}, + 'AIDOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}}, + 'AISI/ETH': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 500 }}}, + 'AIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}}, + 'ANS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 500 }}}, + 'ANS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}}, + 'ARC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 60, 'max': 600000 }}}, + 'AXF/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 1000000 }}}, + 'BASH/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 3000000 }}}, + 'BATT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 60, 'max': 1500000 }}}, + 'BCD/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.3, 'max': 7000 }}}, + 'BHPC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 70000 }}}, + 'BHPC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 60000 }}}, + 'BOPO/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 2000000 }}}, + 'BOPO/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 10000000 }}}, + 'BTC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 10 }}}, + 'BTC/CNET': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0005, 'max': 5 }}}, + 'BTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0002, 'max': 4 }}}, + 'BTE/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}}, + 'BU/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}}, + 'CIC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3000, 'max': 30000000 }}}, + 'CIT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}}, + 'CIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}}, + 'CMT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 2500000 }}}, + 'CNET/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 12, 'max': 120000 }}}, + 'CNMC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 50000 }}}, + 'CTC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 550000 }}}, + 'CZR/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 12, 'max': 500000 }}}, + 'DCON/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 8, 'max': 300000 }}}, + 'DCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 40000 }}}, + 'DCT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 2, 'max': 2000 }}}, + 'DOGE/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 3000, 'max': 14000000 }}}, + 'DOGE/CKUSD': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 500, 'max': 2000000 }}}, + 'DRCT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 16, 'max': 190000 }}}, + 'ELA/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 500 }}}, + 'ELF/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 100000 }}}, + 'ELF/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.01, 'max': 100000 }}}, + 'EOS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}}, + 'EOS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 30000 }}}, + 'EOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.18, 'max': 1800 }}}, + 'ETC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}}, + 'ETC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}}, + 'ETF/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 150000 }}}, + 'ETH/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.015, 'max': 100 }}}, + 'ETH/CKUSD': { 'precision': { 'amount': 4, 'price': 4 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}}, + 'ETH/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}}, + 'FCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}}, + 'FCT/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}}, + 'GAME/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 1, 'max': 10000 }}}, + 'GOOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}}, + 'GP/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 600, 'max': 6000000 }}}, + 'HSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 1000, 'max': 20000000 }}}, + 'IFISH/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 300, 'max': 8000000 }}}, + 'IIC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 4000000 }}}, + 'IMOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}}, + 'JC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}}, + 'LBTC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 3000 }}}, + 'LEC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 500, 'max': 5000000 }}}, + 'LKY/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 70000 }}}, + 'LKY/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, + 'LMC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}}, + 'LSK/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.3, 'max': 3000 }}}, + 'LTC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}}, + 'LTC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}}, + 'LTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 450 }}}, + 'MC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 10000, 'max': 100000000 }}}, + 'MCC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 30, 'max': 350000 }}}, + 'MOC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 25, 'max': 600000 }}}, + 'MRYC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}}, + 'MT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 6000000 }}}, + 'MXI/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 5000, 'max': 60000000 }}}, + 'NAI/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, + 'NAS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 15000 }}}, + 'NAS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}}, + 'NEWOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 65, 'max': 700000 }}}, + 'NKN/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 3, 'max': 350000 }}}, + 'NTK/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 30000 }}}, + 'ONT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2000 }}}, + 'ONT/ETH': { 'precision': { 'amount': 3, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 1000 }}}, + 'PNT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 80, 'max': 800000 }}}, + 'PST/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}}, + 'PTT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 450, 'max': 10000000 }}}, + 'QTUM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.4, 'max': 2800 }}}, + 'QTUM/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}}, + 'RATING/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}}, + 'RHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}}, + 'SDA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 500000 }}}, + 'SDD/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}}, + 'SHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 2500000 }}}, + 'SHE/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 5000000 }}}, + 'SMC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}}, + 'SOP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 1000000 }}}, + 'TAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 35, 'max': 800000 }}}, + 'TIP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 200000 }}}, + 'TKT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 40, 'max': 400000 }}}, + 'TLC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}}, + 'TNC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 110000 }}}, + 'TUB/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 8000000 }}}, + 'UC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 3000000 }}}, + 'UDB/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 2000, 'max': 40000000 }}}, + 'UIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}}, + 'VAAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 250000 }}}, + 'VPN/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}}, + 'VSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 30, 'max': 650000 }}}, + 'WAVES/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.15, 'max': 1500 }}}, + 'WDNA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 250000 }}}, + 'WIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3, 'max': 30000 }}}, + 'XAS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 25000 }}}, + 'XLM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 300000 }}}, + 'XLM/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1, 'max': 300000 }}}, + 'XLM/USDT': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}}, + 'XRP/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 24, 'max': 100000 }}}, + 'XRP/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}}, + 'YBCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 200000 }}}, + 'YBCT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 200000 }}}, + 'YBY/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 25000, 'max': 250000000 }}}, + 'ZEC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}}, + 'ZEC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}}, + }, + }, + }); + } + + async fetchTradingLimits (symbols = undefined, params = {}) { + // this method should not be called directly, use loadTradingLimits () instead + // by default it will try load withdrawal fees of all currencies (with separate requests, sequentially) + // however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const result = {}; + for (let i = 0; i < symbols.length; i++) { + const symbol = symbols[i]; + result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params); + } + return result; + } + + async fetchTradingLimitsById (id, params = {}) { + const request = { + 'symbol': id, + }; + const response = await this.publicPostApiOrderTicker (this.extend (request, params)); + // + // { code: 0, + // msg: "获�牌价信��功", + // data: { high: 0.03721392, + // low: 0.03335362, + // buy: "0.03525757", + // sell: "0.03531160", + // last: 0.0352634, + // vol: "184742.4176", + // min_trade: "0.01500000", + // max_trade: "100.00000000", + // number_float: "4", + // price_float: "8" } } } + // + return this.parseTradingLimits (this.safeValue (response, 'data', {})); + } + + parseTradingLimits (limits, symbol = undefined, params = {}) { + // + // { high: 0.03721392, + // low: 0.03335362, + // buy: "0.03525757", + // sell: "0.03531160", + // last: 0.0352634, + // vol: "184742.4176", + // min_trade: "0.01500000", + // max_trade: "100.00000000", + // number_float: "4", + // price_float: "8" } + // + return { + 'info': limits, + 'precision': { + 'amount': this.safeInteger (limits, 'number_float'), + 'price': this.safeInteger (limits, 'price_float'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (limits, 'min_trade'), + 'max': this.safeFloat (limits, 'max_trade'), + }, + }, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetApiMarketGetPriceList (params); + const result = []; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const currentMarketId = keys[i]; + const currentMarkets = response[currentMarketId]; + for (let j = 0; j < currentMarkets.length; j++) { + const market = currentMarkets[j]; + const baseId = this.safeString (market, 'coin_from'); + const quoteId = this.safeString (market, 'coin_to'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const id = baseId + '2' + quoteId; + const symbol = base + '/' + quote; + const active = true; + const defaults = this.safeValue (this.options['limits'], symbol, {}); + result.push (this.extend ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + // overrided by defaults from this.options['limits'] + 'precision': { + 'amount': undefined, + 'price': undefined, + }, + // overrided by defaults from this.options['limits'] + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + 'info': market, + }, defaults)); + } + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp2 (trade, 'date', 'created'); + const id = this.safeString (trade, 'tid'); + const orderId = this.safeString (trade, 'order_id'); + const amount = this.safeFloat2 (trade, 'number', 'amount'); + const price = this.safeFloat (trade, 'price'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let side = this.safeString (trade, 'side'); + if (side === 'sale') { + side = 'sell'; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'order': orderId, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const market = this.market (symbol); + const response = await this.publicPostApiOrderMarketOrder (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostApiUserUserBalance (params); + const data = this.safeValue (response, 'data'); + let keys = Object.keys (data); + const result = { }; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const amount = this.safeFloat (data, key); + const parts = key.split ('_'); + const currencyId = parts[0]; + const lockOrOver = parts[1]; + const code = this.safeCurrencyCode (currencyId); + if (!(code in result)) { + result[code] = this.account (); + } + if (lockOrOver === 'lock') { + result[code]['used'] = parseFloat (amount); + } else { + result[code]['free'] = parseFloat (amount); + } + } + keys = Object.keys (result); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const total = this.sum (result[key]['used'], result[key]['free']); + result[key]['total'] = total; + } + result['info'] = data; + return this.parseBalance (result); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.markets[symbol]; + const request = { + 'part': market['quoteId'], + 'coin': market['baseId'], + }; + const response = await this.publicPostApiMarketGetCoinTrade (this.extend (request, params)); + const timestamp = this.milliseconds (); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (response, 'max'), + 'low': this.safeFloat (response, 'min'), + 'bid': this.safeFloat (response, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (response, 'sale'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': this.safeFloat (response, 'price'), + 'last': this.safeFloat (response, 'price'), + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (response, 'change_24h'), + 'average': undefined, + 'baseVolume': this.safeFloat (response, 'volume_24h'), + 'quoteVolume': undefined, + 'info': response, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'symbol': marketId, + }; + const response = await this.publicPostApiOrderDepth (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + const timestamp = this.safeTimestamp (data, 'date'); + return this.parseOrderBook (data, timestamp); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.privatePostApiOrderOrderList (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', // partially filled + '2': 'closed', + '3': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a `symbol` argument'); + } + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'trust_id': id, + }; + const response = await this.privatePostApiOrderOrderInfo (this.extend (request, params)); + const order = this.safeValue (response, 'data'); + const timestamp = this.safeTimestamp (order, 'created'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let side = this.safeString (order, 'flag'); + if (side === 'sale') { + side = 'sell'; + } + // Can't use parseOrder because the data format is different btw endpoint for fetchOrder and fetchOrders + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'cost': undefined, + 'average': this.safeFloat (order, 'avg_price'), + 'amount': this.safeFloat (order, 'number'), + 'filled': this.safeFloat (order, 'numberdeal'), + 'remaining': this.safeFloat (order, 'numberover'), + 'status': status, + 'fee': undefined, + }; + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const timestamp = this.safeTimestamp (order, 'datetime'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = undefined; + let side = this.safeString (order, 'type'); + if (side === 'sale') { + side = 'sell'; + } + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avg_price'); + const amount = this.safeFloat (order, 'amount'); + const remaining = this.safeFloat (order, 'amount_outstanding'); + const filled = amount - remaining; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const cost = filled * price; + const fee = undefined; + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + return result; + } + + async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'type': type, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privatePostApiOrderTradeList (this.extend (request, params)); + if ('data' in response) { + return this.parseOrders (response['data'], market, since, limit); + } + return []; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByType ('open', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByType ('all', symbol, since, limit, params); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'type': side, + 'price': this.priceToPrecision (symbol, price), + 'number': this.amountToPrecision (symbol, amount), + }; + const response = await this.privatePostApiOrderCoinTrust (this.extend (request, params)); + const data = this.safeValue (response, 'data', {}); + const id = this.safeString (data, 'order_id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument'); + } + await this.loadMarkets (); + const request = {}; + if (symbol !== undefined) { + request['symbol'] = this.marketId (symbol); + } + if (id !== undefined) { + request['order_id'] = id; + } + return await this.privatePostApiOrderCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + let payload = this.urlencode ({ 'api_key': this.apiKey }); + if (Object.keys (query).length) { + payload += '&' + this.urlencode (this.keysort (query)); + } + const auth = payload + '&secret_key=' + this.secret; + const signature = this.hash (this.encode (auth)); + body = payload + '&sign=' + signature; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const errorCode = this.safeValue (response, 'code'); + if (errorCode !== undefined) { + if (errorCode !== 0) { + // + // { code: 1, msg: "该��存在,�法�作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well) + // { code: 1, msg: '公钥��法' } - wrong public key + // { code: 1, msg: '价格输入有误,请检查你的数值精度' } - 'The price input is incorrect, please check your numerical accuracy' + // { code: 1, msg: '�笔最�交易数��能�于0.00100000,请您�新挂�'} - + // 'The minimum number of single transactions cannot be less than 0.00100000. Please re-post the order' + // + const message = this.safeString (response, 'msg'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + if (message.indexOf ('请您�新挂�') >= 0) { // minimum limit + throw new InvalidOrder (feedback); + } else { + throw new ExchangeError (feedback); + } + } + } + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[side]; + const cost = parseFloat (this.costToPrecision (symbol, amount * price)); + return { + 'type': takerOrMaker, + 'currency': market['quote'], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), + }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bequant.js b/TaskServer/node_modules/ccxt/js/bequant.js new file mode 100644 index 000000000..78d3cda1d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bequant.js @@ -0,0 +1,28 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const hitbtc2 = require ('./hitbtc2'); +// --------------------------------------------------------------------------- + +module.exports = class bequant extends hitbtc2 { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bequant', + 'name': 'Bequant', + 'countries': [ 'MT' ], // Malta + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg', + 'api': 'https://api.bequant.io', + 'www': 'https://bequant.io', + 'doc': [ + 'https://api.bequant.io/', + ], + 'fees': [ + 'https://bequant.io/fees-and-limits', + ], + 'referral': 'https://bequant.io', + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bibox.js b/TaskServer/node_modules/ccxt/js/bibox.js new file mode 100644 index 000000000..4865c70e4 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bibox.js @@ -0,0 +1,963 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AccountSuspended, ArgumentsRequired, AuthenticationError, DDoSProtection, ExchangeNotAvailable, InvalidOrder, OrderNotFound, PermissionDenied, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bibox extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bibox', + 'name': 'Bibox', + 'countries': [ 'CN', 'US', 'KR' ], + 'version': 'v1', + 'has': { + 'CORS': false, + 'publicAPI': false, + 'fetchBalance': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchFundingFees': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'createMarketOrder': false, // or they will return https://github.com/ccxt/ccxt/issues/2338 + 'withdraw': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': 'day', + '1w': 'week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/34902611-2be8bf1a-f830-11e7-91a2-11b2f292e750.jpg', + 'api': 'https://api.bibox.com', + 'www': 'https://www.bibox.com', + 'doc': [ + 'https://biboxcom.github.io/en/', + ], + 'fees': 'https://bibox.zendesk.com/hc/en-us/articles/360002336133', + 'referral': 'https://www.bibox.com/signPage?id=11114745&lang=en', + }, + 'api': { + 'public': { + 'post': [ + // TODO: rework for full endpoint/cmd paths here + 'mdata', + ], + 'get': [ + 'mdata', + ], + }, + 'private': { + 'post': [ + 'user', + 'orderpending', + 'transfer', + ], + }, + 'v2private': { + 'post': [ + 'assets/transfer/spot', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'exceptions': { + '2011': AccountSuspended, // Account is locked + '2015': AuthenticationError, // Google authenticator is wrong + '2021': InsufficientFunds, // Insufficient balance available for withdrawal + '2027': InsufficientFunds, // Insufficient balance available (for trade) + '2033': OrderNotFound, // operation failed! Orders have been completed or revoked + '2067': InvalidOrder, // Does not support market orders + '2068': InvalidOrder, // The number of orders can not be less than + '2085': InvalidOrder, // Order quantity is too small + '3012': AuthenticationError, // invalid apiKey + '3024': PermissionDenied, // wrong apikey permissions + '3025': AuthenticationError, // signature failed + '4000': ExchangeNotAvailable, // current network is unstable + '4003': DDoSProtection, // server busy please try again later + }, + 'commonCurrencies': { + 'KEY': 'Bihu', + 'MTC': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025 + 'PAI': 'PCHAIN', + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { + 'cmd': 'marketAll', + }; + const response = await this.publicGetMdata (this.extend (request, params)); + // + // { + // "result": [ + // { + // "is_hide":0, + // "high_cny":"1.9478", + // "amount":"272.41", + // "coin_symbol":"BIX", + // "last":"0.00002487", + // "currency_symbol":"BTC", + // "change":"+0.00000073", + // "low_cny":"1.7408", + // "base_last_cny":"1.84538041", + // "area_id":7, + // "percent":"+3.02%", + // "last_cny":"1.8454", + // "high":"0.00002625", + // "low":"0.00002346", + // "pair_type":0, + // "last_usd":"0.2686", + // "vol24H":"10940613", + // "id":1, + // "high_usd":"0.2835", + // "low_usd":"0.2534" + // } + // ], + // "cmd":"marketAll", + // "ver":"1.1" + // } + // + const markets = this.safeValue (response, 'result'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const numericId = this.safeInteger (market, 'id'); + const baseId = this.safeString (market, 'coin_symbol'); + const quoteId = this.safeString (market, 'currency_symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = baseId + '_' + quoteId; + const precision = { + 'amount': 4, + 'price': 8, + }; + result.push ({ + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // we don't set values that are not defined by the exchange + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const baseId = this.safeString (ticker, 'coin_symbol'); + const quoteId = this.safeString (ticker, 'currency_symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const last = this.safeFloat (ticker, 'last'); + const change = this.safeFloat (ticker, 'change'); + const baseVolume = this.safeFloat2 (ticker, 'vol', 'vol24H'); + let open = undefined; + if ((last !== undefined) && (change !== undefined)) { + open = last - change; + } + let percentage = this.safeString (ticker, 'percent'); + if (percentage !== undefined) { + percentage = percentage.replace ('%', ''); + percentage = parseFloat (percentage); + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': this.safeFloat (ticker, 'amount'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'ticker', + 'pair': market['id'], + }; + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseTicker (response['result'], market); + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + const ticker = this.parseTicker (rawTickers[i]); + if ((symbols === undefined) || (this.inArray (ticker['symbol'], symbols))) { + tickers.push (ticker); + } + } + return tickers; + } + + async fetchTickers (symbols = undefined, params = {}) { + const request = { + 'cmd': 'marketAll', + }; + const response = await this.publicGetMdata (this.extend (request, params)); + const tickers = this.parseTickers (response['result'], symbols); + return this.indexBy (tickers, 'symbol'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger2 (trade, 'time', 'createdAt'); + let side = this.safeInteger2 (trade, 'side', 'order_side'); + side = (side === 1) ? 'buy' : 'sell'; + let symbol = undefined; + if (market === undefined) { + let marketId = this.safeString (trade, 'pair'); + if (marketId === undefined) { + const baseId = this.safeString (trade, 'coin_symbol'); + const quoteId = this.safeString (trade, 'currency_symbol'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + marketId = baseId + '_' + quoteId; + } + } + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + let feeCurrency = this.safeString (trade, 'fee_symbol'); + if (feeCurrency !== undefined) { + if (feeCurrency in this.currencies_by_id) { + feeCurrency = this.currencies_by_id[feeCurrency]['code']; + } else { + feeCurrency = this.safeCurrencyCode (feeCurrency); + } + } + const feeRate = undefined; // todo: deduce from market if market is defined + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined && amount !== undefined) { + cost = price * amount; + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }; + } + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'id': id, + 'order': undefined, // Bibox does not have it (documented) yet + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': 'limit', + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'deals', + 'pair': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; // default = 200 + } + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseTrades (response['result'], market, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'depth', + 'pair': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; // default = 200 + } + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseOrderBook (response['result'], this.safeFloat (response['result'], 'update_time'), 'bids', 'asks', 'price', 'volume'); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv['time'], + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'vol'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'kline', + 'pair': market['id'], + 'period': this.timeframes[timeframe], + 'size': limit, + }; + const response = await this.publicGetMdata (this.extend (request, params)); + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + + async fetchCurrencies (params = {}) { + if (!this.apiKey || !this.secret) { + throw new AuthenticationError (this.id + " fetchCurrencies is an authenticated endpoint, therefore it requires 'apiKey' and 'secret' credentials. If you don't need currency details, set exchange.has['fetchCurrencies'] = false before calling its methods."); + } + const request = { + 'cmd': 'transfer/coinList', + 'body': {}, + }; + const response = await this.privatePostTransfer (this.extend (request, params)); + const currencies = this.safeValue (response, 'result'); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'symbol'); + const name = currency['name']; // contains hieroglyphs causing python ASCII bug + const code = this.safeCurrencyCode (id); + const precision = 8; + const deposit = this.safeValue (currency, 'enable_deposit'); + const withdraw = this.safeValue (currency, 'enable_withdraw'); + const active = (deposit && withdraw); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': name, + 'active': active, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const type = this.safeString (params, 'type', 'assets'); + params = this.omit (params, 'type'); + const request = { + 'cmd': 'transfer/' + type, // assets, mainAssets + 'body': this.extend ({ + 'select': 1, // return full info + }, params), + }; + const response = await this.privatePostTransfer (request); + const balances = this.safeValue (response, 'result'); + const result = { 'info': balances }; + let indexed = undefined; + if ('assets_list' in balances) { + indexed = this.indexBy (balances['assets_list'], 'coin_symbol'); + } else { + indexed = balances; + } + const keys = Object.keys (indexed); + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + let code = id.toUpperCase (); + if (code.indexOf ('TOTAL_') >= 0) { + code = code.slice (6); + } + if (code in this.currencies_by_id) { + code = this.currencies_by_id[code]['code']; + } + const account = this.account (); + let balance = indexed[id]; + if (typeof balance === 'string') { + balance = parseFloat (balance); + account['free'] = balance; + account['used'] = 0.0; + account['total'] = balance; + } else { + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'freeze'); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = { + 'page': 1, + }; + if (code !== undefined) { + currency = this.currency (code); + request['symbol'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; + } else { + request['size'] = 100; + } + const response = await this.privatePostTransfer ({ + 'cmd': 'transfer/transferInList', + 'body': this.extend (request, params), + }); + const deposits = this.safeValue (response['result'], 'items', []); + for (let i = 0; i < deposits.length; i++) { + deposits[i]['type'] = 'deposit'; + } + return this.parseTransactions (deposits, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = { + 'page': 1, + }; + if (code !== undefined) { + currency = this.currency (code); + request['symbol'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; + } else { + request['size'] = 100; + } + const response = await this.privatePostTransfer ({ + 'cmd': 'transfer/transferOutList', + 'body': this.extend (request, params), + }); + const withdrawals = this.safeValue (response['result'], 'items', []); + for (let i = 0; i < withdrawals.length; i++) { + withdrawals[i]['type'] = 'withdrawal'; + } + return this.parseTransactions (withdrawals, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // 'id': 1023291, + // 'coin_symbol': 'ETH', + // 'to_address': '0x7263....', + // 'amount': '0.49170000', + // 'confirmCount': '16', + // 'createdAt': 1553123867000, + // 'status': 2 + // } + // + // fetchWithdrawals + // + // { + // 'id': 521844, + // 'coin_symbol': 'ETH', + // 'to_address': '0xfd4e....', + // 'addr_remark': '', + // 'amount': '0.39452750', + // 'fee': '0.00600000', + // 'createdAt': 1553226906000, + // 'memo': '', + // 'status': 3 + // } + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'to_address'); + const currencyId = this.safeString (transaction, 'coin_symbol'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeString (transaction, 'createdAt'); + let tag = this.safeString (transaction, 'addr_remark'); + const type = this.safeString (transaction, 'type'); + const status = this.parseTransactionStatusByType (this.safeString (transaction, 'status'), type); + const amount = this.safeFloat (transaction, 'amount'); + let feeCost = this.safeFloat (transaction, 'fee'); + if (type === 'deposit') { + feeCost = 0; + tag = undefined; + } + const fee = { + 'cost': feeCost, + 'currency': code, + }; + return { + 'info': transaction, + 'id': id, + 'txid': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + parseTransactionStatusByType (status, type = undefined) { + const statuses = { + 'deposit': { + '1': 'pending', + '2': 'ok', + }, + 'withdrawal': { + '0': 'pending', + '3': 'ok', + }, + }; + return this.safeString (this.safeValue (statuses, type, {}), status, status); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const orderType = (type === 'limit') ? 2 : 1; + const orderSide = (side === 'buy') ? 1 : 2; + const request = { + 'cmd': 'orderpending/trade', + 'body': this.extend ({ + 'pair': market['id'], + 'account_type': 0, + 'order_type': orderType, + 'order_side': orderSide, + 'pay_bix': 0, + 'amount': amount, + 'price': price, + }, params), + }; + const response = await this.privatePostOrderpending (request); + return { + 'info': response, + 'id': this.safeString (response, 'result'), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'cmd': 'orderpending/cancelTrade', + 'body': this.extend ({ + 'orders_id': id, + }, params), + }; + const response = await this.privatePostOrderpending (request); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'cmd': 'orderpending/order', + 'body': this.extend ({ + 'id': id.toString (), + 'account_type': 0, // 0 = spot account + }, params), + }; + const response = await this.privatePostOrderpending (request); + const order = this.safeValue (response, 'result'); + if (this.isEmpty (order)) { + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + return this.parseOrder (order); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market === undefined) { + let marketId = undefined; + const baseId = this.safeString (order, 'coin_symbol'); + const quoteId = this.safeString (order, 'currency_symbol'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + marketId = baseId + '_' + quoteId; + } + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const type = (order['order_type'] === 1) ? 'market' : 'limit'; + const timestamp = order['createdAt']; + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'deal_price'); + const filled = this.safeFloat (order, 'deal_amount'); + const amount = this.safeFloat (order, 'amount'); + let cost = this.safeFloat2 (order, 'deal_money', 'money'); + let remaining = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (cost === undefined) { + cost = price * filled; + } + } + const side = (order['order_side'] === 1) ? 'buy' : 'sell'; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString (order, 'id'); + const feeCost = this.safeFloat (order, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': undefined, + }; + } + cost = cost ? cost : (parseFloat (price) * filled); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + parseOrderStatus (status) { + const statuses = { + // original comments from bibox: + '1': 'open', // pending + '2': 'open', // part completed + '3': 'closed', // completed + '4': 'canceled', // part canceled + '5': 'canceled', // canceled + '6': 'canceled', // canceling + }; + return this.safeString (statuses, status, status); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let pair = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + pair = market['id']; + } + const size = limit ? limit : 200; + const request = { + 'cmd': 'orderpending/orderPendingList', + 'body': this.extend ({ + 'pair': pair, + 'account_type': 0, // 0 - regular, 1 - margin + 'page': 1, + 'size': size, + }, params), + }; + const response = await this.privatePostOrderpending (request); + const orders = this.safeValue (response['result'], 'items', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = 200, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cmd': 'orderpending/pendingHistoryList', + 'body': this.extend ({ + 'pair': market['id'], + 'account_type': 0, // 0 - regular, 1 - margin + 'page': 1, + 'size': limit, + }, params), + }; + const response = await this.privatePostOrderpending (request); + const orders = this.safeValue (response['result'], 'items', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const size = limit ? limit : 200; + const request = { + 'cmd': 'orderpending/orderHistoryList', + 'body': this.extend ({ + 'pair': market['id'], + 'account_type': 0, // 0 - regular, 1 - margin + 'page': 1, + 'size': size, + 'coin_symbol': market['baseId'], + 'currency_symbol': market['quoteId'], + }, params), + }; + const response = await this.privatePostOrderpending (request); + const trades = this.safeValue (response['result'], 'items', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'cmd': 'transfer/transferIn', + 'body': this.extend ({ + 'coin_symbol': currency['id'], + }, params), + }; + const response = await this.privatePostTransfer (request); + // + // { + // "result":"3Jx6RZ9TNMsAoy9NUzBwZf68QBppDruSKW","cmd":"transfer/transferIn" + // } + // + // { + // "result":"{\"account\":\"PERSONALLY OMITTED\",\"memo\":\"PERSONALLY OMITTED\"}","cmd":"transfer/transferIn" + // } + // + const result = this.safeString (response, 'result'); + let address = result; + let tag = undefined; + if (this.isJsonEncodedObject (result)) { + const parsed = JSON.parse (result); + address = this.safeString (parsed, 'account'); + tag = this.safeString (parsed, 'memo'); + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (this.password === undefined) { + if (!('trade_pwd' in params)) { + throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a trade_pwd parameter'); + } + } + if (!('totp_code' in params)) { + throw new ExchangeError (this.id + ' withdraw() requires a totp_code parameter for 2FA authentication'); + } + const request = { + 'trade_pwd': this.password, + 'coin_symbol': currency['id'], + 'amount': amount, + 'addr': address, + }; + if (tag !== undefined) { + request['address_remark'] = tag; + } + const response = await this.privatePostTransfer ({ + 'cmd': 'transfer/transferOut', + 'body': this.extend (request, params), + }); + return { + 'info': response, + 'id': undefined, + }; + } + + async fetchFundingFees (codes = undefined, params = {}) { + // by default it will try load withdrawal fees of all currencies (with separate requests) + // however if you define codes = [ 'ETH', 'BTC' ] in args it will only load those + await this.loadMarkets (); + const withdrawFees = {}; + const info = {}; + if (codes === undefined) { + codes = Object.keys (this.currencies); + } + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const request = { + 'cmd': 'transfer/coinConfig', + 'body': this.extend ({ + 'coin_symbol': currency['id'], + }, params), + }; + const response = await this.privatePostTransfer (request); + info[code] = response; + withdrawFees[code] = this.safeFloat (response['result'], 'withdraw_fee'); + } + return { + 'info': info, + 'withdraw': withdrawFees, + 'deposit': {}, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + const cmds = this.json ([ params ]); + if (api === 'public') { + if (method !== 'GET') { + body = { 'cmds': cmds }; + } else if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v2private') { + this.checkRequiredCredentials (); + url = this.urls['api'] + '/v2/' + path; + const json_params = this.json (params); + body = { + 'body': json_params, + 'apikey': this.apiKey, + 'sign': this.hmac (this.encode (json_params), this.encode (this.secret), 'md5'), + }; + } else { + this.checkRequiredCredentials (); + body = { + 'cmds': cmds, + 'apikey': this.apiKey, + 'sign': this.hmac (this.encode (cmds), this.encode (this.secret), 'md5'), + }; + } + if (body !== undefined) { + body = this.json (body, { 'convertArraysToObjects': true }); + } + headers = { 'Content-Type': 'application/json' }; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if ('error' in response) { + if ('code' in response['error']) { + const code = this.safeString (response['error'], 'code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } + throw new ExchangeError (this.id + ' ' + body); + } + if (!('result' in response)) { + throw new ExchangeError (this.id + ' ' + body); + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (method === 'GET') { + return response; + } else { + return response['result'][0]; + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bigone.js b/TaskServer/node_modules/ccxt/js/bigone.js new file mode 100644 index 000000000..b8093a1c7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bigone.js @@ -0,0 +1,1255 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, OrderNotFound, InsufficientFunds, PermissionDenied, BadRequest, RateLimitExceeded, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bigone extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bigone', + 'name': 'BigONE', + 'countries': [ 'CN' ], + 'version': 'v3', + 'rateLimit': 1200, // 500 request per 10 minutes + 'has': { + 'cancelAllOrders': true, + 'createMarketOrder': false, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchTickers': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': 'min1', + '5m': 'min5', + '15m': 'min15', + '30m': 'min30', + '1h': 'hour1', + '3h': 'hour3', + '4h': 'hour4', + '6h': 'hour6', + '12h': 'hour12', + '1d': 'day1', + '1w': 'week1', + '1M': 'month1', + }, + 'hostname': 'big.one', // set to 'b1.run' for China mainland + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/69354403-1d532180-0c91-11ea-88ed-44c06cefdf87.jpg', + 'api': { + 'public': 'https://{hostname}/api/v3', + 'private': 'https://{hostname}/api/v3/viewer', + }, + 'www': 'https://big.one', + 'doc': 'https://open.big.one/docs/api.html', + 'fees': 'https://bigone.zendesk.com/hc/en-us/articles/115001933374-BigONE-Fee-Policy', + 'referral': 'https://b1.run/users/new?code=D3LLBVFT', + }, + 'api': { + 'public': { + 'get': [ + 'ping', + 'asset_pairs', + 'asset_pairs/{asset_pair_name}/depth', + 'asset_pairs/{asset_pair_name}/trades', + 'asset_pairs/{asset_pair_name}/ticker', + 'asset_pairs/{asset_pair_name}/candles', + 'asset_pairs/tickers', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'assets/{asset_symbol}/address', + 'orders', + 'orders/{id}', + 'orders/multi', + 'trades', + 'withdrawals', + 'deposits', + ], + 'post': [ + 'orders', + 'orders/{id}/cancel', + 'orders/cancel', + 'withdrawals', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + // HARDCODING IS DEPRECATED THE FEES BELOW ARE TO BE REMOVED SOON + 'withdraw': { + 'BTC': 0.001, + 'ETH': 0.005, + 'EOS': 0.01, + 'ZEC': 0.003, + 'LTC': 0.01, + 'QTUM': 0.01, + // 'INK': 0.01 QTUM, + // 'BOT': 0.01 QTUM, + 'ETC': 0.01, + 'GAS': 0.0, + 'BTS': 1.0, + 'GXS': 0.1, + 'BITCNY': 19.0, + }, + }, + }, + 'exceptions': { + 'exact': { + '10001': BadRequest, // syntax error + '10005': ExchangeError, // internal error + "Amount's scale must greater than AssetPair's base scale": InvalidOrder, + '10007': BadRequest, // parameter error, {"code":10007,"message":"Amount's scale must greater than AssetPair's base scale"} + '10011': ExchangeError, // system error + '10013': OrderNotFound, // {"code":10013,"message":"Resource not found"} + '10014': InsufficientFunds, // {"code":10014,"message":"Insufficient funds"} + '10403': PermissionDenied, // permission denied + '10429': RateLimitExceeded, // too many requests + '40004': AuthenticationError, // {"code":40004,"message":"invalid jwt"} + '40103': AuthenticationError, // invalid otp code + '40104': AuthenticationError, // invalid asset pin code + '40301': PermissionDenied, // {"code":40301,"message":"Permission denied withdrawal create"} + '40302': ExchangeError, // already requested + '40601': ExchangeError, // resource is locked + '40602': ExchangeError, // resource is depleted + '40603': InsufficientFunds, // insufficient resource + '40120': InvalidOrder, // Order is in trading + '40121': InvalidOrder, // Order is already cancelled or filled + }, + 'broad': { + }, + }, + 'commonCurrencies': { + 'ONE': 'BigONE Token', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAssetPairs (params); + // + // { + // "code":0, + // "data":[ + // { + // "id":"01e48809-b42f-4a38-96b1-c4c547365db1", + // "name":"PCX-BTC", + // "quote_scale":7, + // "quote_asset":{ + // "id":"0df9c3c3-255a-46d7-ab82-dedae169fba9", + // "symbol":"BTC", + // "name":"Bitcoin", + // }, + // "base_asset":{ + // "id":"405484f7-4b03-4378-a9c1-2bd718ecab51", + // "symbol":"PCX", + // "name":"ChainX", + // }, + // "base_scale":3, + // "min_quote_value":"0.0001", + // }, + // ] + // } + // + const markets = this.safeValue (response, 'data', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const uuid = this.safeString (market, 'id'); + const baseAsset = this.safeValue (market, 'base_asset', {}); + const quoteAsset = this.safeValue (market, 'quote_asset', {}); + const baseId = this.safeString (baseAsset, 'symbol'); + const quoteId = this.safeString (quoteAsset, 'symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'base_scale'), + 'price': this.safeInteger (market, 'quote_scale'), + }; + const minCost = this.safeInteger (market, 'min_quote_value'); + const entry = { + 'id': id, + 'uuid': uuid, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }; + result.push (entry); + } + return result; + } + + async loadMarkets (reload = false, params = {}) { + const markets = await super.loadMarkets (reload, params); + let marketsByUuid = this.safeValue (this.options, 'marketsByUuid'); + if ((marketsByUuid === undefined) || reload) { + marketsByUuid = {}; + for (let i = 0; i < this.symbols.length; i++) { + const symbol = this.symbols[i]; + const market = this.markets[symbol]; + const uuid = this.safeString (market, 'uuid'); + marketsByUuid[uuid] = market; + } + this.options['marketsByUuid'] = marketsByUuid; + } + return markets; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "asset_pair_name":"ETH-BTC", + // "bid":{"price":"0.021593","order_count":1,"quantity":"0.20936"}, + // "ask":{"price":"0.021613","order_count":1,"quantity":"2.87064"}, + // "open":"0.021795", + // "high":"0.021795", + // "low":"0.021471", + // "close":"0.021613", + // "volume":"117078.90431", + // "daily_change":"-0.000182" + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'asset_pair_name'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const close = this.safeFloat (ticker, 'close'); + const bid = this.safeValue (ticker, 'bid', {}); + const ask = this.safeValue (ticker, 'ask', {}); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (bid, 'price'), + 'bidVolume': this.safeFloat (bid, 'quantity'), + 'ask': this.safeFloat (ask, 'price'), + 'askVolume': this.safeFloat (ask, 'quantity'), + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'daily_change'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + const response = await this.publicGetAssetPairsAssetPairNameTicker (this.extend (request, params)); + // + // { + // "code":0, + // "data":{ + // "asset_pair_name":"ETH-BTC", + // "bid":{"price":"0.021593","order_count":1,"quantity":"0.20936"}, + // "ask":{"price":"0.021613","order_count":1,"quantity":"2.87064"}, + // "open":"0.021795", + // "high":"0.021795", + // "low":"0.021471", + // "close":"0.021613", + // "volume":"117078.90431", + // "daily_change":"-0.000182" + // } + // } + // + const ticker = this.safeValue (response, 'data', {}); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['pair_names'] = ids.join (','); + } + const response = await this.publicGetAssetPairsTickers (this.extend (request, params)); + // + // { + // "code":0, + // "data":[ + // { + // "asset_pair_name":"PCX-BTC", + // "bid":{"price":"0.000234","order_count":1,"quantity":"0.518"}, + // "ask":{"price":"0.0002348","order_count":1,"quantity":"2.348"}, + // "open":"0.0002343", + // "high":"0.0002348", + // "low":"0.0002162", + // "close":"0.0002348", + // "volume":"12887.016", + // "daily_change":"0.0000005" + // }, + // { + // "asset_pair_name":"GXC-USDT", + // "bid":{"price":"0.5054","order_count":1,"quantity":"40.53"}, + // "ask":{"price":"0.5055","order_count":1,"quantity":"38.53"}, + // "open":"0.5262", + // "high":"0.5323", + // "low":"0.5055", + // "close":"0.5055", + // "volume":"603963.05", + // "daily_change":"-0.0207" + // } + // ] + // } + // + const tickers = this.safeValue (response, 'data', []); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 50, max 200 + } + const response = await this.publicGetAssetPairsAssetPairNameDepth (this.extend (request, params)); + // + // { + // "code":0, + // "data": { + // "asset_pair_name": "EOS-BTC", + // "bids": [ + // { "price": "42", "order_count": 4, "quantity": "23.33363711" } + // ], + // "asks": [ + // { "price": "45", "order_count": 2, "quantity": "4193.3283464" } + // ] + // } + // } + // + const orderbook = this.safeValue (response, 'data', {}); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'quantity'); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "id": 38199941, + // "price": "3378.67", + // "amount": "0.019812", + // "taker_side": "ASK", + // "created_at": "2019-01-29T06:05:56Z" + // } + // + // fetchMyTrades (private) + // + // { + // "id": 10854280, + // "asset_pair_name": "XIN-USDT", + // "price": "70", + // "amount": "1", + // "taker_side": "ASK", + // "maker_order_id": 58284908, + // "taker_order_id": 58284909, + // "maker_fee": "0.0008", + // "taker_fee": "0.07", + // "side": "SELF_TRADING", + // "inserted_at": "2019-04-16T12:00:01Z" + // }, + // + // { + // "id": 10854263, + // "asset_pair_name": "XIN-USDT", + // "price": "75.7", + // "amount": "12.743149", + // "taker_side": "BID", + // "maker_order_id": null, + // "taker_order_id": 58284888, + // "maker_fee": null, + // "taker_fee": "0.0025486298", + // "side": "BID", + // "inserted_at": "2019-04-15T06:20:57Z" + // } + // + const timestamp = this.parse8601 (this.safeString2 (trade, 'created_at', 'inserted_at')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const marketId = this.safeString (trade, 'asset_pair_name'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = this.costToPrecision (symbol, price * amount); + } + } + let side = this.safeString (trade, 'side'); + const takerSide = this.safeString (trade, 'taker_side'); + let takerOrMaker = undefined; + if ((takerSide !== undefined) && (side !== undefined) && (side !== 'SELF_TRADING')) { + takerOrMaker = (takerSide === side) ? 'taker' : 'maker'; + } + if (side === undefined) { + // taker side is not related to buy/sell side + // the following code is probably a mistake + side = (takerSide === 'ASK') ? 'sell' : 'buy'; + } else { + if (side === 'BID') { + side = 'buy'; + } else if (side === 'ASK') { + side = 'sell'; + } + } + const makerOrderId = this.safeString (trade, 'maker_order_id'); + const takerOrderId = this.safeString (trade, 'taker_order_id'); + let orderId = undefined; + if (makerOrderId !== undefined) { + if (takerOrderId !== undefined) { + orderId = [ makerOrderId, takerOrderId ]; + } else { + orderId = makerOrderId; + } + } else if (takerOrderId !== undefined) { + orderId = takerOrderId; + } + const id = this.safeString (trade, 'id'); + const result = { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': parseFloat (cost), + 'info': trade, + }; + let makerCurrencyCode = undefined; + let takerCurrencyCode = undefined; + if ((market !== undefined) && (takerOrMaker !== undefined)) { + if (side === 'buy') { + if (takerOrMaker === 'maker') { + makerCurrencyCode = market['base']; + takerCurrencyCode = market['quote']; + } else { + makerCurrencyCode = market['quote']; + takerCurrencyCode = market['base']; + } + } else { + if (takerOrMaker === 'maker') { + makerCurrencyCode = market['quote']; + takerCurrencyCode = market['base']; + } else { + makerCurrencyCode = market['base']; + takerCurrencyCode = market['quote']; + } + } + } else if (side === 'SELF_TRADING') { + if (takerSide === 'BID') { + makerCurrencyCode = market['quote']; + takerCurrencyCode = market['base']; + } else if (takerSide === 'ASK') { + makerCurrencyCode = market['base']; + takerCurrencyCode = market['quote']; + } + } + const makerFeeCost = this.safeFloat (trade, 'maker_fee'); + const takerFeeCost = this.safeFloat (trade, 'taker_fee'); + if (makerFeeCost !== undefined) { + if (takerFeeCost !== undefined) { + result['fees'] = [ + { 'cost': makerFeeCost, 'currency': makerCurrencyCode }, + { 'cost': takerFeeCost, 'currency': takerCurrencyCode }, + ]; + } else { + result['fee'] = { 'cost': makerFeeCost, 'currency': makerCurrencyCode }; + } + } else if (takerFeeCost !== undefined) { + result['fee'] = { 'cost': takerFeeCost, 'currency': takerCurrencyCode }; + } else { + result['fee'] = undefined; + } + return result; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + const response = await this.publicGetAssetPairsAssetPairNameTrades (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "id": 38199941, + // "price": "3378.67", + // "amount": "0.019812", + // "taker_side": "ASK", + // "created_at": "2019-01-29T06:05:56Z" + // }, + // { + // "id": 38199934, + // "price": "3376.14", + // "amount": "0.019384", + // "taker_side": "ASK", + // "created_at": "2019-01-29T06:05:40Z" + // } + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // close: '0.021562', + // high: '0.021563', + // low: '0.02156', + // open: '0.021563', + // time: '2019-11-21T07:54:00Z', + // volume: '59.84376' + // } + // + return [ + this.parse8601 (this.safeString (ohlcv, 'time')), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 100; // default 100, max 500 + } + const request = { + 'asset_pair_name': market['id'], + 'period': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + // const start = parseInt (since / 1000); + const end = this.sum (since, limit * this.parseTimeframe (timeframe) * 1000); + request['time'] = this.iso8601 (end); + } + const response = await this.publicGetAssetPairsAssetPairNameCandles (this.extend (request, params)); + // + // { + // code: 0, + // data: [ + // { + // close: '0.021656', + // high: '0.021658', + // low: '0.021652', + // open: '0.021652', + // time: '2019-11-21T09:30:00Z', + // volume: '53.08664' + // }, + // { + // close: '0.021652', + // high: '0.021656', + // low: '0.021652', + // open: '0.021656', + // time: '2019-11-21T09:29:00Z', + // volume: '88.39861' + // }, + // ] + // } + // + const ohlcvs = this.safeValue (response, 'data', []); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + // + // { + // "code":0, + // "data":[ + // {"asset_symbol":"NKC","balance":"0","locked_balance":"0"}, + // {"asset_symbol":"UBTC","balance":"0","locked_balance":"0"}, + // {"asset_symbol":"READ","balance":"0","locked_balance":"0"}, + // ], + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const symbol = this.safeString (balance, 'asset_symbol'); + const code = this.safeCurrencyCode (symbol); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + // + // { + // "id": 10, + // "asset_pair_name": "EOS-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "FILLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z", + // } + // + const id = this.safeString (order, 'id'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'asset_pair_name'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'filled_amount'); + let remaining = undefined; + if (amount !== undefined && filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let side = this.safeString (order, 'side'); + if (side === 'BID') { + side = 'buy'; + } else { + side = 'sell'; + } + let cost = undefined; + if (filled !== undefined) { + if (price !== undefined) { + cost = filled * price; + } + } + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'updated_at')); + const average = this.safeFloat (order, 'avg_deal_price'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + side = (side === 'buy') ? 'BID' : 'ASK'; + const request = { + 'asset_pair_name': market['id'], // asset pair name BTC-USDT, required + 'side': side, // order side one of "ASK"/"BID", required + 'amount': this.amountToPrecision (symbol, amount), // order amount, string, required + 'price': this.priceToPrecision (symbol, price), // order price, string, required + }; + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "id": 10, + // "asset_pair_name": "EOS-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "FILLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z" + // } + // + const order = this.safeValue (response, 'data'); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { 'id': id }; + const response = await this.privatePostOrdersIdCancel (this.extend (request, params)); + // { + // "id": 10, + // "asset_pair_name": "EOS-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "CANCELLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z" + // } + const order = this.safeValue (response, 'data'); + return this.parseOrder (order); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + }; + const response = await this.privatePostOrdersCancel (this.extend (request, params)); + // + // { + // "code":0, + // "data": { + // "cancelled":[ + // 58272370, + // 58272377 + // ], + // "failed": [] + // } + // } + // + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { 'id': id }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + const order = this.safeValue (response, 'data', {}); + return this.parseOrder (order); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + // 'page_token': 'dxzef', // request page after this page token + // 'side': 'ASK', // 'ASK' or 'BID', optional + // 'state': 'FILLED', // 'CANCELLED', 'FILLED', 'PENDING' + // 'limit' 20, // default 20, max 200 + }; + if (limit !== undefined) { + request['limit'] = limit; // default 20, max 200 + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "code":0, + // "data": [ + // { + // "id": 10, + // "asset_pair_name": "ETH-BTC", + // "price": "10.00", + // "amount": "10.00", + // "filled_amount": "9.0", + // "avg_deal_price": "12.0", + // "side": "ASK", + // "state": "FILLED", + // "created_at":"2019-01-29T06:05:56Z", + // "updated_at":"2019-01-29T06:05:56Z", + // }, + // ], + // "page_token":"dxzef", + // } + // + const orders = this.safeValue (response, 'data', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument'); + } + const market = this.market (symbol); + const request = { + 'asset_pair_name': market['id'], + // 'page_token': 'dxzef', // request page after this page token + }; + if (limit !== undefined) { + request['limit'] = limit; // default 20, max 200 + } + const response = await this.privateGetTrades (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "id": 10854280, + // "asset_pair_name": "XIN-USDT", + // "price": "70", + // "amount": "1", + // "taker_side": "ASK", + // "maker_order_id": 58284908, + // "taker_order_id": 58284909, + // "maker_fee": "0.0008", + // "taker_fee": "0.07", + // "side": "SELF_TRADING", + // "inserted_at": "2019-04-16T12:00:01Z" + // }, + // { + // "id": 10854263, + // "asset_pair_name": "XIN-USDT", + // "price": "75.7", + // "amount": "12.743149", + // "taker_side": "BID", + // "maker_order_id": null, + // "taker_order_id": 58284888, + // "maker_fee": null, + // "taker_fee": "0.0025486298", + // "side": "BID", + // "inserted_at": "2019-04-15T06:20:57Z" + // } + // ], + // "page_token":"dxfv" + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit, params); + } + + parseOrderStatus (status) { + const statuses = { + 'PENDING': 'open', + 'FILLED': 'closed', + 'CANCELLED': 'canceled', + }; + return this.safeString (statuses, status); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'PENDING', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'FILLED', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + nonce () { + return this.microseconds () * 1000; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + const baseUrl = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname }); + let url = baseUrl + '/' + this.implodeParams (path, params); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const request = { + 'type': 'OpenAPIV2', + 'sub': this.apiKey, + 'nonce': nonce, + // 'recv_window': '30', // default 30 + }; + const jwt = this.jwt (request, this.encode (this.secret)); + headers = { + 'Authorization': 'Bearer ' + jwt, + }; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (method === 'POST') { + headers['Content-Type'] = 'application/json'; + body = this.json (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset_symbol': currency['id'], + }; + const response = await this.privateGetAssetsAssetSymbolAddress (this.extend (request, params)); + // + // the actual response format is not the same as the documented one + // the data key contains an array in the actual response + // + // { + // "code":0, + // "message":"", + // "data":[ + // { + // "id":5521878, + // "chain":"Bitcoin", + // "value":"1GbmyKoikhpiQVZ1C9sbF17mTyvBjeobVe", + // "memo":"" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const dataLength = data.length; + if (dataLength < 1) { + throw new ExchangeError (this.id + 'fetchDepositAddress() returned empty address response'); + } + const firstElement = data[0]; + const address = this.safeString (firstElement, 'value'); + const tag = this.safeString (firstElement, 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + // what are other statuses here? + 'WITHHOLD': 'ok', // deposits + 'UNCONFIRMED': 'pending', + 'CONFIRMED': 'ok', // withdrawals + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "amount": "25.0", + // "asset_symbol": "BTS" + // "confirms": 100, + // "id": 5, + // "inserted_at": "2018-02-16T11:39:58.000Z", + // "is_internal": false, + // "kind": "default", + // "memo": "", + // "state": "WITHHOLD", + // "txid": "72e03037d144dae3d32b68b5045462b1049a0755", + // "updated_at": "2018-11-09T10:20:09.000Z", + // } + // + // fetchWithdrawals + // + // { + // "amount": "5", + // "asset_symbol": "ETH", + // "completed_at": "2018-03-15T16:13:45.610463Z", + // "customer_id": "10", + // "id": 10, + // "inserted_at": "2018-03-15T16:13:45.610463Z", + // "is_internal": true, + // "note": "2018-03-15T16:13:45.610463Z", + // "state": "CONFIRMED", + // "target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7" + // "txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0", + // } + // + const currencyId = this.safeString (transaction, 'asset_symbol'); + const code = this.safeCurrencyCode (currencyId); + const id = this.safeInteger (transaction, 'id'); + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const timestamp = this.parse8601 (this.safeString (transaction, 'inserted_at')); + const updated = this.parse8601 (this.safeString2 (transaction, 'updated_at', 'completed_at')); + const txid = this.safeString (transaction, 'txid'); + const address = this.safeString (transaction, 'target_address'); + const tag = this.safeString (transaction, 'memo'); + const type = ('customer_id' in transaction) ? 'deposit' : 'withdrawal'; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': undefined, + 'addressTo': address, + 'tagFrom': undefined, + 'tag': tag, + 'tagTo': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': undefined, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page_token': 'dxzef', // request page after this page token + // 'limit': 50, // optional, default 50 + // 'kind': 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining + // 'asset_symbol': 'BTC', // optional + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['asset_symbol'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 50 + } + const response = await this.privateGetDeposits (this.extend (request, params)); + // + // { + // "code": 0, + // "page_token": "NQ==", + // "data": [ + // { + // "id": 5, + // "amount": "25.0", + // "confirms": 100, + // "txid": "72e03037d144dae3d32b68b5045462b1049a0755", + // "is_internal": false, + // "inserted_at": "2018-02-16T11:39:58.000Z", + // "updated_at": "2018-11-09T10:20:09.000Z", + // "kind": "default", + // "memo": "", + // "state": "WITHHOLD", + // "asset_symbol": "BTS" + // } + // ] + // } + // + const deposits = this.safeValue (response, 'data', []); + return this.parseTransactions (deposits, code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page_token': 'dxzef', // request page after this page token + // 'limit': 50, // optional, default 50 + // 'kind': 'string', // optional - air_drop, big_holder_dividend, default, eosc_to_eos, internal, equally_airdrop, referral_mining, one_holder_dividend, single_customer, snapshotted_airdrop, trade_mining + // 'asset_symbol': 'BTC', // optional + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['asset_symbol'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 50 + } + const response = await this.privateGetWithdrawals (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "id": 10, + // "customer_id": "10", + // "asset_symbol": "ETH", + // "amount": "5", + // "state": "CONFIRMED", + // "note": "2018-03-15T16:13:45.610463Z", + // "txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0", + // "completed_at": "2018-03-15T16:13:45.610463Z", + // "inserted_at": "2018-03-15T16:13:45.610463Z", + // "is_internal": true, + // "target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7" + // } + // ], + // "page_token":"dxvf" + // } + // + const withdrawals = this.safeValue (response, 'data', []); + return this.parseTransactions (withdrawals, code, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'symbol': currency['id'], + 'target_address': address, + 'amount': this.currencyToPrecision (code, amount), + }; + if (tag !== undefined) { + request['memo'] = tag; + } + // requires write permission on the wallet + const response = await this.privatePostWithdrawals (this.extend (request, params)); + // + // { + // "code":0, + // "message":"", + // "data":[ + // { + // "id":1, + // "customer_id":7, + // "asset_uuid":"50293b12-5be8-4f5b-b31d-d43cdd5ccc29", + // "amount":"100", + // "recipient":null, + // "state":"PENDING", + // "is_internal":true, + // "note":"asdsadsad", + // "kind":"on_chain", + // "txid":"asdasdasdsadsadsad", + // "confirms":5, + // "inserted_at":null, + // "updated_at":null, + // "completed_at":null, + // "commision":null, + // "explain":"" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const dataLength = data.length; + if (dataLength < 1) { + throw new ExchangeError (this.id + ' withdraw() returned an empty response'); + } + const transaction = data[0]; + return this.parseTransaction (transaction, currency); + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"code":10013,"message":"Resource not found"} + // {"code":40004,"message":"invalid jwt"} + // + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + if (code !== '0') { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/binance.js b/TaskServer/node_modules/ccxt/js/binance.js new file mode 100644 index 000000000..4ba1bec15 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/binance.js @@ -0,0 +1,1858 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress } = require ('./base/errors'); +const { ROUND } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class binance extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'binance', + 'name': 'Binance', + 'countries': [ 'JP', 'MT' ], // Japan, Malta + 'rateLimit': 500, + 'certified': true, + // new metainfo interface + 'has': { + 'fetchDepositAddress': true, + 'CORS': false, + 'fetchBidsAsks': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchOHLCV': true, + 'fetchMyTrades': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': 'emulated', + 'withdraw': true, + 'fetchFundingFees': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + }, + 'timeframes': { + '1m': '1m', + '3m': '3m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '4h': '4h', + '6h': '6h', + '8h': '8h', + '12h': '12h', + '1d': '1d', + '3d': '3d', + '1w': '1w', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg', + 'api': { + 'web': 'https://www.binance.com', + 'wapi': 'https://api.binance.com/wapi/v3', + 'sapi': 'https://api.binance.com/sapi/v1', + 'fapiPublic': 'https://fapi.binance.com/fapi/v1', + 'fapiPrivate': 'https://fapi.binance.com/fapi/v1', + 'public': 'https://api.binance.com/api/v3', + 'private': 'https://api.binance.com/api/v3', + 'v3': 'https://api.binance.com/api/v3', + 'v1': 'https://api.binance.com/api/v1', + }, + 'www': 'https://www.binance.com', + 'referral': 'https://www.binance.com/?ref=10205187', + 'doc': [ + 'https://binance-docs.github.io/apidocs/spot/en', + ], + 'api_management': 'https://www.binance.com/en/usercenter/settings/api-management', + 'fees': 'https://www.binance.com/en/fee/schedule', + }, + 'api': { + 'web': { + 'get': [ + 'exchange/public/product', + 'assetWithdraw/getAllAsset.html', + ], + }, + // the API structure below will need 3-layer apidefs + 'sapi': { + 'get': [ + 'accountSnapshot', + // these endpoints require this.apiKey + 'margin/asset', + 'margin/pair', + 'margin/allAssets', + 'margin/allPairs', + 'margin/priceIndex', + // these endpoints require this.apiKey + this.secret + 'asset/assetDividend', + 'margin/loan', + 'margin/repay', + 'margin/account', + 'margin/transfer', + 'margin/interestHistory', + 'margin/forceLiquidationRec', + 'margin/order', + 'margin/openOrders', + 'margin/allOrders', + 'margin/myTrades', + 'margin/maxBorrowable', + 'margin/maxTransferable', + 'futures/transfer', + // https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi + 'capital/config/getall', // get networks for withdrawing USDT ERC20 vs USDT Omni + 'capital/deposit/address', + 'capital/deposit/hisrec', + 'capital/deposit/subAddress', + 'capital/deposit/subHisrec', + 'capital/withdraw/history', + 'sub-account/futures/account', + 'sub-account/futures/accountSummary', + 'sub-account/futures/positionRisk', + 'sub-account/margin/account', + 'sub-account/margin/accountSummary', + 'sub-account/status', + ], + 'post': [ + 'asset/dust', + 'account/disableFastWithdrawSwitch', + 'account/enableFastWithdrawSwitch', + 'capital/withdraw/apply', + 'margin/transfer', + 'margin/loan', + 'margin/repay', + 'margin/order', + 'sub-account/margin/enable', + 'sub-account/margin/enable', + 'sub-account/futures/enable', + 'userDataStream', + 'futures/transfer', + ], + 'put': [ + 'userDataStream', + ], + 'delete': [ + 'margin/order', + 'userDataStream', + ], + }, + 'wapi': { + 'post': [ + 'withdraw', + 'sub-account/transfer', + ], + 'get': [ + 'depositHistory', + 'withdrawHistory', + 'depositAddress', + 'accountStatus', + 'systemStatus', + 'apiTradingStatus', + 'userAssetDribbletLog', + 'tradeFee', + 'assetDetail', + 'sub-account/list', + 'sub-account/transfer/history', + 'sub-account/assets', + ], + }, + 'fapiPublic': { + 'get': [ + 'ping', + 'time', + 'exchangeInfo', + 'depth', + 'trades', + 'historicalTrades', + 'aggTrades', + 'klines', + 'premiumIndex', + 'ticker/24hr', + 'ticker/price', + 'ticker/bookTicker', + ], + 'put': [ 'listenKey' ], + 'post': [ 'listenKey' ], + 'delete': [ 'listenKey' ], + }, + 'fapiPrivate': { + 'get': [ + 'allOrders', + 'openOrders', + 'order', + 'account', + 'balance', + 'positionMargin/history', + 'positionRisk', + 'userTrades', + 'income', + ], + 'post': [ + 'positionMargin', + 'marginType', + 'order', + 'leverage', + ], + 'delete': [ + 'order', + 'allOpenOrders', + ], + }, + 'v3': { + 'get': [ + 'ticker/price', + 'ticker/bookTicker', + ], + }, + 'public': { + 'get': [ + 'ping', + 'time', + 'depth', + 'trades', + 'aggTrades', + 'historicalTrades', + 'klines', + 'ticker/24hr', + 'ticker/price', + 'ticker/bookTicker', + 'exchangeInfo', + ], + 'put': [ 'userDataStream' ], + 'post': [ 'userDataStream' ], + 'delete': [ 'userDataStream' ], + }, + 'private': { + 'get': [ + 'allOrderList', // oco + 'openOrderList', // oco + 'orderList', // oco + 'order', + 'openOrders', + 'allOrders', + 'account', + 'myTrades', + ], + 'post': [ + 'order/oco', + 'order', + 'order/test', + ], + 'delete': [ + 'orderList', // oco + 'order', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + }, + 'commonCurrencies': { + 'BCC': 'BCC', // kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848 + 'YOYO': 'YOYOW', + }, + // exchange-specific options + 'options': { + 'fetchTradesMethod': 'publicGetAggTrades', + 'fetchTickersMethod': 'publicGetTicker24hr', + 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel + 'defaultLimitOrderType': 'limit', // or 'limit_maker' + 'defaultType': 'spot', // 'spot', 'future' + 'hasAlreadyAuthenticatedSuccessfully': false, + 'warnOnFetchOpenOrdersWithoutSymbol': true, + 'recvWindow': 5 * 1000, // 5 sec, binance default + 'timeDifference': 0, // the difference between system clock and Binance clock + 'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation + 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision + 'newOrderRespType': { + 'market': 'FULL', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills + 'limit': 'RESULT', // we change it from 'ACK' by default to 'RESULT' + }, + }, + 'exceptions': { + 'API key does not exist': AuthenticationError, + 'Order would trigger immediately.': InvalidOrder, + 'Account has insufficient balance for requested action.': InsufficientFunds, + 'Rest API trading is not enabled.': ExchangeNotAvailable, + '-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."} + '-1013': InvalidOrder, // createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL + '-1021': InvalidNonce, // 'your time is ahead of server' + '-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."} + '-1100': InvalidOrder, // createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price' + '-1104': ExchangeError, // Not all sent parameters were read, read 8 parameters but was sent 9 + '-1128': ExchangeError, // {"code":-1128,"msg":"Combination of optional parameters invalid."} + '-2010': ExchangeError, // generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc... + '-2011': OrderNotFound, // cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER' + '-2013': OrderNotFound, // fetchOrder (1, 'BTC/USDT') -> 'Order does not exist' + '-2014': AuthenticationError, // { "code":-2014, "msg": "API-key format invalid." } + '-2015': AuthenticationError, // "Invalid API-key, IP, or permissions for action." + }, + }); + } + + nonce () { + return this.milliseconds () - this.options['timeDifference']; + } + + async fetchTime (params = {}) { + const type = this.safeString2 (this.options, 'fetchTime', 'defaultType', 'spot'); + const method = (type === 'spot') ? 'publicGetTime' : 'fapiPublicGetTime'; + const response = await this[method] (params); + return this.safeFloat (response, 'serverTime'); + } + + async loadTimeDifference () { + const serverTime = await this.fetchTime (); + const after = this.milliseconds (); + this.options['timeDifference'] = parseInt (after - serverTime); + return this.options['timeDifference']; + } + + async fetchMarkets (params = {}) { + const defaultType = this.safeString2 (this.options, 'fetchMarkets', 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const method = (type === 'spot') ? 'publicGetExchangeInfo' : 'fapiPublicGetExchangeInfo'; + const response = await this[method] (query); + // + // spot + // + // { + // "timezone":"UTC", + // "serverTime":1575416692969, + // "rateLimits":[ + // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, + // {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":100}, + // {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":200000} + // ], + // "exchangeFilters":[], + // "symbols":[ + // { + // "symbol":"ETHBTC", + // "status":"TRADING", + // "baseAsset":"ETH", + // "baseAssetPrecision":8, + // "quoteAsset":"BTC", + // "quotePrecision":8, + // "baseCommissionPrecision":8, + // "quoteCommissionPrecision":8, + // "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"], + // "icebergAllowed":true, + // "ocoAllowed":true, + // "quoteOrderQtyMarketAllowed":true, + // "isSpotTradingAllowed":true, + // "isMarginTradingAllowed":true, + // "filters":[ + // {"filterType":"PRICE_FILTER","minPrice":"0.00000100","maxPrice":"100000.00000000","tickSize":"0.00000100"}, + // {"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5}, + // {"filterType":"LOT_SIZE","minQty":"0.00100000","maxQty":"100000.00000000","stepSize":"0.00100000"}, + // {"filterType":"MIN_NOTIONAL","minNotional":"0.00010000","applyToMarket":true,"avgPriceMins":5}, + // {"filterType":"ICEBERG_PARTS","limit":10}, + // {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"63100.00000000","stepSize":"0.00000000"}, + // {"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5} + // ] + // }, + // ], + // } + // + // futures (fapi) + // + // { + // "timezone":"UTC", + // "serverTime":1575417244353, + // "rateLimits":[ + // {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200}, + // {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200} + // ], + // "exchangeFilters":[], + // "symbols":[ + // { + // "symbol":"BTCUSDT", + // "status":"TRADING", + // "maintMarginPercent":"2.5000", + // "requiredMarginPercent":"5.0000", + // "baseAsset":"BTC", + // "quoteAsset":"USDT", + // "pricePrecision":2, + // "quantityPrecision":3, + // "baseAssetPrecision":8, + // "quotePrecision":8, + // "filters":[ + // {"minPrice":"0.01","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.01"}, + // {"stepSize":"0.001","filterType":"LOT_SIZE","maxQty":"1000","minQty":"0.001"}, + // {"stepSize":"0.001","filterType":"MARKET_LOT_SIZE","maxQty":"1000","minQty":"0.001"}, + // {"limit":200,"filterType":"MAX_NUM_ORDERS"}, + // {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"} + // ], + // "orderTypes":["LIMIT","MARKET","STOP"], + // "timeInForce":["GTC","IOC","FOK","GTX"] + // } + // ] + // } + // + if (this.options['adjustForTimeDifference']) { + await this.loadTimeDifference (); + } + const markets = this.safeValue (response, 'symbols'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const future = ('maintMarginPercent' in market); + const spot = !future; + const marketType = spot ? 'spot' : 'future'; + const id = this.safeString (market, 'symbol'); + const baseId = market['baseAsset']; + const quoteId = market['quoteAsset']; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const filters = this.indexBy (market['filters'], 'filterType'); + const precision = { + 'base': market['baseAssetPrecision'], + 'quote': market['quotePrecision'], + 'amount': market['baseAssetPrecision'], + 'price': market['quotePrecision'], + }; + const status = this.safeString (market, 'status'); + const active = (status === 'TRADING'); + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'type': marketType, + 'spot': spot, + 'future': future, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': -1 * Math.log10 (precision['amount']), + 'max': undefined, + }, + }, + }; + if ('PRICE_FILTER' in filters) { + const filter = filters['PRICE_FILTER']; + // PRICE_FILTER reports zero values for maxPrice + // since they updated filter types in November 2018 + // https://github.com/ccxt/ccxt/issues/4286 + // therefore limits['price']['max'] doesn't have any meaningful value except undefined + entry['limits']['price'] = { + 'min': this.safeFloat (filter, 'minPrice'), + 'max': undefined, + }; + const maxPrice = this.safeFloat (filter, 'maxPrice'); + if ((maxPrice !== undefined) && (maxPrice > 0)) { + entry['limits']['price']['max'] = maxPrice; + } + entry['precision']['price'] = this.precisionFromString (filter['tickSize']); + } + if ('LOT_SIZE' in filters) { + const filter = this.safeValue (filters, 'LOT_SIZE', {}); + const stepSize = this.safeString (filter, 'stepSize'); + entry['precision']['amount'] = this.precisionFromString (stepSize); + entry['limits']['amount'] = { + 'min': this.safeFloat (filter, 'minQty'), + 'max': this.safeFloat (filter, 'maxQty'), + }; + } + if ('MIN_NOTIONAL' in filters) { + entry['limits']['cost']['min'] = this.safeFloat (filters['MIN_NOTIONAL'], 'minNotional'); + } + result.push (entry); + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let precision = market['precision']['price']; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + precision = market['precision']['amount']; + } + cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (cost), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + const method = (type === 'spot') ? 'privateGetAccount' : 'fapiPrivateGetAccount'; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + // + // spot + // + // { + // makerCommission: 10, + // takerCommission: 10, + // buyerCommission: 0, + // sellerCommission: 0, + // canTrade: true, + // canWithdraw: true, + // canDeposit: true, + // updateTime: 1575357359602, + // accountType: "MARGIN", + // balances: [ + // { asset: "BTC", free: "0.00219821", locked: "0.00000000" }, + // ] + // } + // + // futures (fapi) + // + // { + // "feeTier":0, + // "canTrade":true, + // "canDeposit":true, + // "canWithdraw":true, + // "updateTime":0, + // "totalInitialMargin":"0.00000000", + // "totalMaintMargin":"0.00000000", + // "totalWalletBalance":"4.54000000", + // "totalUnrealizedProfit":"0.00000000", + // "totalMarginBalance":"4.54000000", + // "totalPositionInitialMargin":"0.00000000", + // "totalOpenOrderInitialMargin":"0.00000000", + // "maxWithdrawAmount":"4.54000000", + // "assets":[ + // { + // "asset":"USDT", + // "walletBalance":"4.54000000", + // "unrealizedProfit":"0.00000000", + // "marginBalance":"4.54000000", + // "maintMargin":"0.00000000", + // "initialMargin":"0.00000000", + // "positionInitialMargin":"0.00000000", + // "openOrderInitialMargin":"0.00000000", + // "maxWithdrawAmount":"4.54000000" + // } + // ], + // "positions":[ + // { + // "symbol":"BTCUSDT", + // "initialMargin":"0.00000", + // "maintMargin":"0.00000", + // "unrealizedProfit":"0.00000000", + // "positionInitialMargin":"0.00000", + // "openOrderInitialMargin":"0.00000" + // } + // ] + // } + // + const result = { 'info': response }; + if (type === 'spot') { + const balances = this.safeValue (response, 'balances', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'free'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + } else { + const balances = this.safeValue (response, 'assets', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'initialMargin'); + account['total'] = this.safeFloat (balance, 'marginBalance'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 5000, see https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book + } + const method = market['spot'] ? 'publicGetDepth' : 'fapiPublicGetDepth'; + const response = await this[method] (this.extend (request, params)); + const orderbook = this.parseOrderBook (response); + orderbook['nonce'] = this.safeInteger (response, 'lastUpdateId'); + return orderbook; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'closeTime'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highPrice'), + 'low': this.safeFloat (ticker, 'lowPrice'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': this.safeFloat (ticker, 'bidQty'), + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': this.safeFloat (ticker, 'askQty'), + 'vwap': this.safeFloat (ticker, 'weightedAvgPrice'), + 'open': this.safeFloat (ticker, 'openPrice'), + 'close': last, + 'last': last, + 'previousClose': this.safeFloat (ticker, 'prevClosePrice'), // previous day close + 'change': this.safeFloat (ticker, 'priceChange'), + 'percentage': this.safeFloat (ticker, 'priceChangePercent'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchStatus (params = {}) { + const response = await this.wapiGetSystemStatus (); + let status = this.safeValue (response, 'status'); + if (status !== undefined) { + status = (status === 0) ? 'ok' : 'maintenance'; + this.status = this.extend (this.status, { + 'status': status, + 'updated': this.milliseconds (), + }); + } + return this.status; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const method = market['spot'] ? 'publicGetTicker24hr' : 'fapiPublicGetTicker24hr'; + const response = await this[method] (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchBidsAsks (symbols = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchOpenOrders', 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const method = (type === 'spot') ? 'publicGetTickerBookTicker' : 'fapiPublicGetTickerBookTicker'; + const response = await this[method] (query); + return this.parseTickers (response, symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const method = this.options['fetchTickersMethod']; + const response = await this[method] (params); + return this.parseTickers (response, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0], + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'interval': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default == max == 500 + } + const method = market['spot'] ? 'publicGetKlines' : 'fapiPublicGetKlines'; + const response = await this[method] (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + if ('isDustTrade' in trade) { + return this.parseDustTrade (trade, market); + } + // + // aggregate trades + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list + // + // { + // "a": 26129, // Aggregate tradeId + // "p": "0.01633102", // Price + // "q": "4.70443515", // Quantity + // "f": 27781, // First tradeId + // "l": 27781, // Last tradeId + // "T": 1498793709153, // Timestamp + // "m": true, // Was the buyer the maker? + // "M": true // Was the trade the best price match? + // } + // + // recent public trades and old public trades + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#recent-trades-list + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#old-trade-lookup-market_data + // + // { + // "id": 28457, + // "price": "4.00000100", + // "qty": "12.00000000", + // "time": 1499865549590, + // "isBuyerMaker": true, + // "isBestMatch": true + // } + // + // private trades + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#account-trade-list-user_data + // + // { + // "symbol": "BNBBTC", + // "id": 28457, + // "orderId": 100234, + // "price": "4.00000100", + // "qty": "12.00000000", + // "commission": "10.10000000", + // "commissionAsset": "BNB", + // "time": 1499865549590, + // "isBuyer": true, + // "isMaker": false, + // "isBestMatch": true + // } + // + // futures trades + // https://binance-docs.github.io/apidocs/futures/en/#account-trade-list-user_data + // + // { + // "accountId": 20, + // "buyer": False, + // "commission": "-0.07819010", + // "commissionAsset": "USDT", + // "counterPartyId": 653, + // "id": 698759, + // "maker": False, + // "orderId": 25851813, + // "price": "7819.01", + // "qty": "0.002", + // "quoteQty": "0.01563", + // "realizedPnl": "-0.91539999", + // "side": "SELL", + // "symbol": "BTCUSDT", + // "time": 1569514978020 + // } + // + const timestamp = this.safeInteger2 (trade, 'T', 'time'); + const price = this.safeFloat2 (trade, 'p', 'price'); + const amount = this.safeFloat2 (trade, 'q', 'qty'); + const id = this.safeString2 (trade, 'a', 'id'); + let side = undefined; + const orderId = this.safeString (trade, 'orderId'); + if ('m' in trade) { + side = trade['m'] ? 'sell' : 'buy'; // this is reversed intentionally + } else if ('isBuyerMaker' in trade) { + side = trade['isBuyerMaker'] ? 'sell' : 'buy'; + } else if ('side' in trade) { + side = this.safeStringLower (trade, 'side'); + } else { + if ('isBuyer' in trade) { + side = trade['isBuyer'] ? 'buy' : 'sell'; // this is a true side + } + } + let fee = undefined; + if ('commission' in trade) { + fee = { + 'cost': this.safeFloat (trade, 'commission'), + 'currency': this.safeCurrencyCode (this.safeString (trade, 'commissionAsset')), + }; + } + let takerOrMaker = undefined; + if ('isMaker' in trade) { + takerOrMaker = trade['isMaker'] ? 'maker' : 'taker'; + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': price * amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + // 'fromId': 123, // ID to get aggregate trades from INCLUSIVE. + // 'startTime': 456, // Timestamp in ms to get aggregate trades from INCLUSIVE. + // 'endTime': 789, // Timestamp in ms to get aggregate trades until INCLUSIVE. + // 'limit': 500, // default = 500, maximum = 1000 + }; + if (this.options['fetchTradesMethod'] === 'publicGetAggTrades') { + if (since !== undefined) { + request['startTime'] = since; + // https://github.com/ccxt/ccxt/issues/6400 + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list + request['endTime'] = this.sum (since, 3600000); + } + } + if (limit !== undefined) { + request['limit'] = limit; // default = 500, maximum = 1000 + } + // + // Caveats: + // - default limit (500) applies only if no other parameters set, trades up + // to the maximum limit may be returned to satisfy other parameters + // - if both limit and time window is set and time window contains more + // trades than the limit then the last trades from the window are returned + // - 'tradeId' accepted and returned by this method is "aggregate" trade id + // which is different from actual trade id + // - setting both fromId and time window results in error + const method = this.safeValue (this.options, 'fetchTradesMethod', 'publicGetTrades'); + const response = await this[method] (this.extend (request, params)); + // + // aggregate trades + // + // [ + // { + // "a": 26129, // Aggregate tradeId + // "p": "0.01633102", // Price + // "q": "4.70443515", // Quantity + // "f": 27781, // First tradeId + // "l": 27781, // Last tradeId + // "T": 1498793709153, // Timestamp + // "m": true, // Was the buyer the maker? + // "M": true // Was the trade the best price match? + // } + // ] + // + // recent public trades and historical public trades + // + // [ + // { + // "id": 28457, + // "price": "4.00000100", + // "qty": "12.00000000", + // "time": 1499865549590, + // "isBuyerMaker": true, + // "isBestMatch": true + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'NEW': 'open', + 'PARTIALLY_FILLED': 'open', + 'FILLED': 'closed', + 'CANCELED': 'canceled', + 'PENDING_CANCEL': 'canceling', // currently unused + 'REJECTED': 'rejected', + 'EXPIRED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = undefined; + if ('time' in order) { + timestamp = this.safeInteger (order, 'time'); + } else if ('transactTime' in order) { + timestamp = this.safeInteger (order, 'transactTime'); + } + let price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'origQty'); + const filled = this.safeFloat (order, 'executedQty'); + let remaining = undefined; + // - Spot/Margin market: cummulativeQuoteQty + // - Futures market: cumQuote. + // Note this is not the actual cost, since Binance futures uses leverage to calculate margins. + let cost = this.safeFloat2 (order, 'cummulativeQuoteQty', 'cumQuote'); + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + if (this.options['parseOrderToPrecision']) { + remaining = parseFloat (this.amountToPrecision (symbol, remaining)); + } + remaining = Math.max (remaining, 0.0); + } + if (price !== undefined) { + if (cost === undefined) { + cost = price * filled; + } + } + } + const id = this.safeString (order, 'orderId'); + let type = this.safeStringLower (order, 'type'); + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + if (this.options['parseOrderToPrecision']) { + price = parseFloat (this.priceToPrecision (symbol, price)); + } + } + } + } + } else if (type === 'limit_maker') { + type = 'limit'; + } + const side = this.safeStringLower (order, 'side'); + let fee = undefined; + let trades = undefined; + const fills = this.safeValue (order, 'fills'); + if (fills !== undefined) { + trades = this.parseTrades (fills, market); + const numTrades = trades.length; + if (numTrades > 0) { + cost = trades[0]['cost']; + fee = { + 'cost': trades[0]['fee']['cost'], + 'currency': trades[0]['fee']['currency'], + }; + for (let i = 1; i < trades.length; i++) { + cost = this.sum (cost, trades[i]['cost']); + fee['cost'] = this.sum (fee['cost'], trades[i]['fee']['cost']); + } + } + } + let average = undefined; + if (cost !== undefined) { + if (filled) { + average = cost / filled; + if (this.options['parseOrderToPrecision']) { + average = parseFloat (this.priceToPrecision (symbol, average)); + } + } + if (this.options['parseOrderToPrecision']) { + cost = parseFloat (this.costToPrecision (symbol, cost)); + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the next 5 lines are added to support for testing orders + let method = market['spot'] ? 'privatePostOrder' : 'fapiPrivatePostOrder'; + if (market['spot']) { + const test = this.safeValue (params, 'test', false); + if (test) { + method += 'Test'; + params = this.omit (params, 'test'); + } + } + const uppercaseType = type.toUpperCase (); + const validOrderTypes = this.safeValue (market['info'], 'orderTypes'); + if (!this.inArray (uppercaseType, validOrderTypes)) { + throw new InvalidOrder (this.id + ' ' + type + ' is not a valid order type in ' + market['type'] + ' market ' + symbol); + } + const request = { + 'symbol': market['id'], + 'type': uppercaseType, + 'side': side.toUpperCase (), + }; + if (uppercaseType === 'MARKET') { + const quoteOrderQty = this.safeFloat (params, 'quoteOrderQty'); + if (quoteOrderQty !== undefined) { + request['quoteOrderQty'] = this.costToPrecision (symbol, quoteOrderQty); + } else if (price !== undefined) { + request['quoteOrderQty'] = this.costToPrecision (symbol, amount * price); + } else { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + } else { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + if (market['spot']) { + request['newOrderRespType'] = this.safeValue (this.options['newOrderRespType'], type, 'RESULT'); // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills + } + let timeInForceIsRequired = false; + let priceIsRequired = false; + let stopPriceIsRequired = false; + if (uppercaseType === 'LIMIT') { + priceIsRequired = true; + timeInForceIsRequired = true; + } else if ((uppercaseType === 'STOP_LOSS') || (uppercaseType === 'TAKE_PROFIT')) { + stopPriceIsRequired = true; + } else if ((uppercaseType === 'STOP_LOSS_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) { + stopPriceIsRequired = true; + priceIsRequired = true; + timeInForceIsRequired = true; + } else if (uppercaseType === 'LIMIT_MAKER') { + priceIsRequired = true; + } else if (uppercaseType === 'STOP') { + stopPriceIsRequired = true; + priceIsRequired = true; + } + if (priceIsRequired) { + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order'); + } + request['price'] = this.priceToPrecision (symbol, price); + } + if (timeInForceIsRequired) { + request['timeInForce'] = this.options['defaultTimeInForce']; // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel + } + if (stopPriceIsRequired) { + const stopPrice = this.safeFloat (params, 'stopPrice'); + if (stopPrice === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order'); + } else { + params = this.omit (params, 'stopPrice'); + request['stopPrice'] = this.priceToPrecision (symbol, stopPrice); + } + } + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['spot'] ? 'privateGetOrder' : 'fapiPrivateGetOrder'; + const request = { + 'symbol': market['id'], + }; + const origClientOrderId = this.safeValue (params, 'origClientOrderId'); + if (origClientOrderId !== undefined) { + request['origClientOrderId'] = origClientOrderId; + } else { + request['orderId'] = parseInt (id); + } + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const method = market['spot'] ? 'privateGetAllOrders' : 'fapiPrivateGetAllOrders'; + const response = await this[method] (this.extend (request, params)); + // + // Spot: + // [ + // { + // "symbol": "LTCBTC", + // "orderId": 1, + // "clientOrderId": "myOrder1", + // "price": "0.1", + // "origQty": "1.0", + // "executedQty": "0.0", + // "cummulativeQuoteQty": "0.0", + // "status": "NEW", + // "timeInForce": "GTC", + // "type": "LIMIT", + // "side": "BUY", + // "stopPrice": "0.0", + // "icebergQty": "0.0", + // "time": 1499827319559, + // "updateTime": 1499827319559, + // "isWorking": true + // } + // ] + // + // Futures: + // [ + // { + // "symbol": "BTCUSDT", + // "orderId": 1, + // "clientOrderId": "myOrder1", + // "price": "0.1", + // "origQty": "1.0", + // "executedQty": "1.0", + // "cumQuote": "10.0", + // "status": "NEW", + // "timeInForce": "GTC", + // "type": "LIMIT", + // "side": "BUY", + // "stopPrice": "0.0", + // "updateTime": 1499827319559 + // } + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let query = undefined; + let type = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + type = market['type']; + query = params; + } else if (this.options['warnOnFetchOpenOrdersWithoutSymbol']) { + const symbols = this.symbols; + const numSymbols = symbols.length; + const fetchOpenOrdersRateLimit = parseInt (numSymbols / 2); + throw new ExchangeError (this.id + ' fetchOpenOrders WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + fetchOpenOrdersRateLimit.toString () + ' seconds. Do not call this method frequently to avoid ban. Set ' + this.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = false to suppress this warning message.'); + } else { + const defaultType = this.safeString2 (this.options, 'fetchOpenOrders', 'defaultType', 'spot'); + type = this.safeString (params, 'type', defaultType); + query = this.omit (params, 'type'); + } + const method = (type === 'spot') ? 'privateGetOpenOrders' : 'fapiPrivateGetOpenOrders'; + const response = await this[method] (this.extend (request, query)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'orderId': parseInt (id), + // 'origClientOrderId': id, + }; + const method = market['spot'] ? 'privateDeleteOrder' : 'fapiPrivateDeleteOrder'; + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['spot'] ? 'privateGetMyTrades' : 'fapiPrivateGetUserTrades'; + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this[method] (this.extend (request, params)); + // + // spot trade + // [ + // { + // "symbol": "BNBBTC", + // "id": 28457, + // "orderId": 100234, + // "price": "4.00000100", + // "qty": "12.00000000", + // "commission": "10.10000000", + // "commissionAsset": "BNB", + // "time": 1499865549590, + // "isBuyer": true, + // "isMaker": false, + // "isBestMatch": true, + // } + // ] + // + // futures trade + // + // [ + // { + // "accountId": 20, + // "buyer": False, + // "commission": "-0.07819010", + // "commissionAsset": "USDT", + // "counterPartyId": 653, + // "id": 698759, + // "maker": False, + // "orderId": 25851813, + // "price": "7819.01", + // "qty": "0.002", + // "quoteQty": "0.01563", + // "realizedPnl": "-0.91539999", + // "side": "SELL", + // "symbol": "BTCUSDT", + // "time": 1569514978020 + // } + // ] + return this.parseTrades (response, market, since, limit); + } + + async fetchMyDustTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // + // Binance provides an opportunity to trade insignificant (i.e. non-tradable and non-withdrawable) + // token leftovers (of any asset) into `BNB` coin which in turn can be used to pay trading fees with it. + // The corresponding trades history is called the `Dust Log` and can be requested via the following end-point: + // https://github.com/binance-exchange/binance-official-api-docs/blob/master/wapi-api.md#dustlog-user_data + // + await this.loadMarkets (); + const response = await this.wapiGetUserAssetDribbletLog (params); + // { success: true, + // results: { total: 1, + // rows: [ { transfered_total: "1.06468458", + // service_charge_total: "0.02172826", + // tran_id: 2701371634, + // logs: [ { tranId: 2701371634, + // serviceChargeAmount: "0.00012819", + // uid: "35103861", + // amount: "0.8012", + // operateTime: "2018-10-07 17:56:07", + // transferedAmount: "0.00628141", + // fromAsset: "ADA" } ], + // operate_time: "2018-10-07 17:56:06" } ] } } + const results = this.safeValue (response, 'results', {}); + const rows = this.safeValue (results, 'rows', []); + const data = []; + for (let i = 0; i < rows.length; i++) { + const logs = rows[i]['logs']; + for (let j = 0; j < logs.length; j++) { + logs[j]['isDustTrade'] = true; + data.push (logs[j]); + } + } + const trades = this.parseTrades (data, undefined, since, limit); + return this.filterBySinceLimit (trades, since, limit); + } + + parseDustTrade (trade, market = undefined) { + // { tranId: 2701371634, + // serviceChargeAmount: "0.00012819", + // uid: "35103861", + // amount: "0.8012", + // operateTime: "2018-10-07 17:56:07", + // transferedAmount: "0.00628141", + // fromAsset: "ADA" }, + const orderId = this.safeString (trade, 'tranId'); + const timestamp = this.parse8601 (this.safeString (trade, 'operateTime')); + const tradedCurrency = this.safeCurrencyCode (this.safeString (trade, 'fromAsset')); + const earnedCurrency = this.currency ('BNB')['code']; + const applicantSymbol = earnedCurrency + '/' + tradedCurrency; + let tradedCurrencyIsQuote = false; + if (applicantSymbol in this.markets) { + tradedCurrencyIsQuote = true; + } + // + // Warning + // Binance dust trade `fee` is already excluded from the `BNB` earning reported in the `Dust Log`. + // So the parser should either set the `fee.cost` to `0` or add it on top of the earned + // BNB `amount` (or `cost` depending on the trade `side`). The second of the above options + // is much more illustrative and therefore preferable. + // + const fee = { + 'currency': earnedCurrency, + 'cost': this.safeFloat (trade, 'serviceChargeAmount'), + }; + let symbol = undefined; + let amount = undefined; + let cost = undefined; + let side = undefined; + if (tradedCurrencyIsQuote) { + symbol = applicantSymbol; + amount = this.sum (this.safeFloat (trade, 'transferedAmount'), fee['cost']); + cost = this.safeFloat (trade, 'amount'); + side = 'buy'; + } else { + symbol = tradedCurrency + '/' + earnedCurrency; + amount = this.safeFloat (trade, 'amount'); + cost = this.sum (this.safeFloat (trade, 'transferedAmount'), fee['cost']); + side = 'sell'; + } + let price = undefined; + if (cost !== undefined) { + if (amount) { + price = cost / amount; + } + } + const id = undefined; + const type = undefined; + const takerOrMaker = undefined; + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'amount': amount, + 'price': price, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + const response = await this.wapiGetDepositHistory (this.extend (request, params)); + // + // { success: true, + // depositList: [ { insertTime: 1517425007000, + // amount: 0.3, + // address: "0x0123456789abcdef", + // addressTag: "", + // txId: "0x0123456789abcdef", + // asset: "ETH", + // status: 1 } ] } + // + return this.parseTransactions (response['depositList'], currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + const response = await this.wapiGetWithdrawHistory (this.extend (request, params)); + // + // { withdrawList: [ { amount: 14, + // address: "0x0123456789abcdef...", + // successTime: 1514489710000, + // transactionFee: 0.01, + // addressTag: "", + // txId: "0x0123456789abcdef...", + // id: "0123456789abcdef...", + // asset: "ETH", + // applyTime: 1514488724000, + // status: 6 }, + // { amount: 7600, + // address: "0x0123456789abcdef...", + // successTime: 1515323226000, + // transactionFee: 0.01, + // addressTag: "", + // txId: "0x0123456789abcdef...", + // id: "0123456789abcdef...", + // asset: "ICN", + // applyTime: 1515322539000, + // status: 6 } ], + // success: true } + // + return this.parseTransactions (response['withdrawList'], currency, since, limit); + } + + parseTransactionStatusByType (status, type = undefined) { + if (type === undefined) { + return status; + } + const statuses = { + 'deposit': { + '0': 'pending', + '1': 'ok', + }, + 'withdrawal': { + '0': 'pending', // Email Sent + '1': 'canceled', // Cancelled (different from 1 = ok in deposits) + '2': 'pending', // Awaiting Approval + '3': 'failed', // Rejected + '4': 'pending', // Processing + '5': 'failed', // Failure + '6': 'ok', // Completed + }, + }; + return (status in statuses[type]) ? statuses[type][status] : status; + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // { insertTime: 1517425007000, + // amount: 0.3, + // address: "0x0123456789abcdef", + // addressTag: "", + // txId: "0x0123456789abcdef", + // asset: "ETH", + // status: 1 } + // + // fetchWithdrawals + // + // { amount: 14, + // address: "0x0123456789abcdef...", + // successTime: 1514489710000, + // transactionFee: 0.01, + // addressTag: "", + // txId: "0x0123456789abcdef...", + // id: "0123456789abcdef...", + // asset: "ETH", + // applyTime: 1514488724000, + // status: 6 } + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + let tag = this.safeString (transaction, 'addressTag'); // set but unused + if (tag !== undefined) { + if (tag.length < 1) { + tag = undefined; + } + } + const txid = this.safeValue (transaction, 'txId'); + const currencyId = this.safeString (transaction, 'asset'); + const code = this.safeCurrencyCode (currencyId, currency); + let timestamp = undefined; + const insertTime = this.safeInteger (transaction, 'insertTime'); + const applyTime = this.safeInteger (transaction, 'applyTime'); + let type = this.safeString (transaction, 'type'); + if (type === undefined) { + if ((insertTime !== undefined) && (applyTime === undefined)) { + type = 'deposit'; + timestamp = insertTime; + } else if ((insertTime === undefined) && (applyTime !== undefined)) { + type = 'withdrawal'; + timestamp = applyTime; + } + } + const status = this.parseTransactionStatusByType (this.safeString (transaction, 'status'), type); + const amount = this.safeFloat (transaction, 'amount'); + const feeCost = this.safeFloat (transaction, 'transactionFee'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { 'currency': code, 'cost': feeCost }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.wapiGetDepositAddress (this.extend (request, params)); + const success = this.safeValue (response, 'success'); + if ((success === undefined) || !success) { + throw new InvalidAddress (this.id + ' fetchDepositAddress returned an empty response – create the deposit address in the user settings first.'); + } + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'addressTag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async fetchFundingFees (codes = undefined, params = {}) { + const response = await this.wapiGetAssetDetail (params); + // + // { + // "success": true, + // "assetDetail": { + // "CTR": { + // "minWithdrawAmount": "70.00000000", //min withdraw amount + // "depositStatus": false,//deposit status + // "withdrawFee": 35, // withdraw fee + // "withdrawStatus": true, //withdraw status + // "depositTip": "Delisted, Deposit Suspended" //reason + // }, + // "SKY": { + // "minWithdrawAmount": "0.02000000", + // "depositStatus": true, + // "withdrawFee": 0.01, + // "withdrawStatus": true + // } + // } + // } + // + const detail = this.safeValue (response, 'assetDetail', {}); + const ids = Object.keys (detail); + const withdrawFees = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + withdrawFees[code] = this.safeFloat (detail[id], 'withdrawFee'); + } + return { + 'withdraw': withdrawFees, + 'deposit': {}, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // name is optional, can be overrided via params + const name = address.slice (0, 20); + const request = { + 'asset': currency['id'], + 'address': address, + 'amount': parseFloat (amount), + 'name': name, // name is optional, can be overrided via params + // https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi + // issue sapiGetCapitalConfigGetall () to get networks for withdrawing USDT ERC20 vs USDT Omni + // 'network': 'ETH', // 'BTC', 'TRX', etc, optional + }; + if (tag !== undefined) { + request['addressTag'] = tag; + } + const response = await this.wapiPostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response, 'id'), + }; + } + + parseTradingFee (fee, market = undefined) { + // + // { + // "symbol": "ADABNB", + // "maker": 0.9000, + // "taker": 1.0000 + // } + // + const marketId = this.safeString (fee, 'symbol'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + const market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + return { + 'info': fee, + 'symbol': symbol, + 'maker': this.safeFloat (fee, 'maker'), + 'taker': this.safeFloat (fee, 'taker'), + }; + } + + async fetchTradingFee (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.wapiGetTradeFee (this.extend (request, params)); + // + // { + // "tradeFee": [ + // { + // "symbol": "ADABNB", + // "maker": 0.9000, + // "taker": 1.0000 + // } + // ], + // "success": true + // } + // + const tradeFee = this.safeValue (response, 'tradeFee', []); + const first = this.safeValue (tradeFee, 0, {}); + return this.parseTradingFee (first); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.wapiGetTradeFee (params); + // + // { + // "tradeFee": [ + // { + // "symbol": "ADABNB", + // "maker": 0.9000, + // "taker": 1.0000 + // } + // ], + // "success": true + // } + // + const tradeFee = this.safeValue (response, 'tradeFee', []); + const result = {}; + for (let i = 0; i < tradeFee.length; i++) { + const fee = this.parseTradingFee (tradeFee[i]); + const symbol = fee['symbol']; + result[symbol] = fee; + } + return result; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + url += '/' + path; + if (api === 'wapi') { + url += '.html'; + } + const userDataStream = ((path === 'userDataStream') || (path === 'listenKey')); + if (path === 'historicalTrades') { + if (this.apiKey) { + headers = { + 'X-MBX-APIKEY': this.apiKey, + }; + } else { + throw new AuthenticationError (this.id + ' historicalTrades endpoint requires `apiKey` credential'); + } + } else if (userDataStream) { + if (this.apiKey) { + // v1 special case for userDataStream + body = this.urlencode (params); + headers = { + 'X-MBX-APIKEY': this.apiKey, + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } else { + throw new AuthenticationError (this.id + ' userDataStream endpoint requires `apiKey` credential'); + } + } + if ((api === 'private') || (api === 'sapi') || (api === 'wapi' && path !== 'systemStatus') || (api === 'fapiPrivate')) { + this.checkRequiredCredentials (); + let query = undefined; + if ((api === 'sapi') && (path === 'asset/dust')) { + query = this.urlencodeWithArrayRepeat (this.extend ({ + 'timestamp': this.nonce (), + 'recvWindow': this.options['recvWindow'], + }, params)); + } else { + query = this.urlencode (this.extend ({ + 'timestamp': this.nonce (), + 'recvWindow': this.options['recvWindow'], + }, params)); + } + const signature = this.hmac (this.encode (query), this.encode (this.secret)); + query += '&' + 'signature=' + signature; + headers = { + 'X-MBX-APIKEY': this.apiKey, + }; + if ((method === 'GET') || (method === 'DELETE') || (api === 'wapi')) { + url += '?' + query; + } else { + body = query; + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + } else { + // userDataStream endpoints are public, but POST, PUT, DELETE + // therefore they don't accept URL query arguments + // https://github.com/ccxt/ccxt/issues/5224 + if (!userDataStream) { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 418) || (code === 429)) { + throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); + } + // error response in a form: { "code": -1013, "msg": "Invalid quantity." } + // following block cointains legacy checks against message patterns in "msg" property + // will switch "code" checks eventually, when we know all of them + if (code >= 400) { + if (body.indexOf ('Price * QTY is zero or less') >= 0) { + throw new InvalidOrder (this.id + ' order cost = amount * price is zero or less ' + body); + } + if (body.indexOf ('LOT_SIZE') >= 0) { + throw new InvalidOrder (this.id + ' order amount should be evenly divisible by lot size ' + body); + } + if (body.indexOf ('PRICE_FILTER') >= 0) { + throw new InvalidOrder (this.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid float value in general, use this.priceToPrecision (symbol, amount) ' + body); + } + } + if (body.length > 0) { + if (body[0] === '{') { + // check success value for wapi endpoints + // response in format {'msg': 'The coin does not exist.', 'success': true/false} + const success = this.safeValue (response, 'success', true); + if (!success) { + const message = this.safeString (response, 'msg'); + let parsedMessage = undefined; + if (message !== undefined) { + try { + parsedMessage = JSON.parse (message); + } catch (e) { + // do nothing + parsedMessage = undefined; + } + if (parsedMessage !== undefined) { + response = parsedMessage; + } + } + } + const message = this.safeString (response, 'msg'); + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions, message, this.id + ' ' + message); + } + // checks against error codes + const error = this.safeString (response, 'code'); + if (error !== undefined) { + // a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} + // despite that their message is very confusing, it is raised by Binance + // on a temporary ban (the API key is valid, but disabled for a while) + if ((error === '-2015') && this.options['hasAlreadyAuthenticatedSuccessfully']) { + throw new DDoSProtection (this.id + ' temporary banned: ' + body); + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + throw new ExchangeError (feedback); + } + if (!success) { + throw new ExchangeError (this.id + ' ' + body); + } + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + // a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} + if ((api === 'private') || (api === 'wapi')) { + this.options['hasAlreadyAuthenticatedSuccessfully'] = true; + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/binanceje.js b/TaskServer/node_modules/ccxt/js/binanceje.js new file mode 100644 index 000000000..dc7e0026a --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/binanceje.js @@ -0,0 +1,52 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const binance = require ('./binance.js'); + +// --------------------------------------------------------------------------- + +module.exports = class binanceje extends binance { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'binanceje', + 'name': 'Binance Jersey', + 'countries': [ 'JE' ], // Jersey + 'certified': false, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/54874009-d526eb00-4df3-11e9-928c-ce6a2b914cd1.jpg', + 'api': { + 'web': 'https://www.binance.je', + 'wapi': 'https://api.binance.je/wapi/v3', + 'public': 'https://api.binance.je/api/v1', + 'private': 'https://api.binance.je/api/v3', + 'v3': 'https://api.binance.je/api/v3', + 'v1': 'https://api.binance.je/api/v1', + }, + 'www': 'https://www.binance.je', + 'referral': 'https://www.binance.je/?ref=35047921', + 'doc': 'https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md', + 'fees': 'https://www.binance.je/fees.html', + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.0005, + 'maker': 0.0005, + }, + // should be deleted, these are outdated and inaccurate + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.0005, + 'ETH': 0.01, + }, + 'deposit': {}, + }, + }, + }); + } +}; + diff --git a/TaskServer/node_modules/ccxt/js/binanceus.js b/TaskServer/node_modules/ccxt/js/binanceus.js new file mode 100644 index 000000000..611306672 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/binanceus.js @@ -0,0 +1,42 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const binance = require ('./binance.js'); + +// --------------------------------------------------------------------------- + +module.exports = class binanceus extends binance { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'binanceus', + 'name': 'Binance US', + 'countries': [ 'US' ], // US + 'certified': false, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/65177307-217b7c80-da5f-11e9-876e-0b748ba0a358.jpg', + 'api': { + 'web': 'https://www.binance.us', + 'wapi': 'https://api.binance.us/wapi/v3', + 'public': 'https://api.binance.us/api/v1', + 'private': 'https://api.binance.us/api/v3', + 'v3': 'https://api.binance.us/api/v3', + 'v1': 'https://api.binance.us/api/v1', + }, + 'www': 'https://www.binance.us', + 'referral': 'https://www.binance.us/?ref=35005074', + 'doc': 'https://github.com/binance-us/binance-official-api-docs', + 'fees': 'https://www.binance.us/en/fee/schedule', + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.0, // 0.1% trading fee, zero fees for all trading pairs before November 1 + 'maker': 0.0, // 0.1% trading fee, zero fees for all trading pairs before November 1 + }, + }, + }); + } +}; + diff --git a/TaskServer/node_modules/ccxt/js/bit2c.js b/TaskServer/node_modules/ccxt/js/bit2c.js new file mode 100644 index 000000000..42b1907aa --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bit2c.js @@ -0,0 +1,418 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bit2c extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bit2c', + 'name': 'Bit2C', + 'countries': [ 'IL' ], // Israel + 'rateLimit': 3000, + 'has': { + 'CORS': false, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766119-3593220e-5ece-11e7-8b3a-5a041f6bcc3f.jpg', + 'api': 'https://bit2c.co.il', + 'www': 'https://www.bit2c.co.il', + 'referral': 'https://bit2c.co.il/Aff/63bfed10-e359-420c-ab5a-ad368dab0baf', + 'doc': [ + 'https://www.bit2c.co.il/home/api', + 'https://github.com/OferE/bit2c', + ], + }, + 'api': { + 'public': { + 'get': [ + 'Exchanges/{pair}/Ticker', + 'Exchanges/{pair}/orderbook', + 'Exchanges/{pair}/trades', + 'Exchanges/{pair}/lasttrades', + ], + }, + 'private': { + 'post': [ + 'Merchant/CreateCheckout', + 'Order/AddCoinFundsRequest', + 'Order/AddFund', + 'Order/AddOrder', + 'Order/AddOrderMarketPriceBuy', + 'Order/AddOrderMarketPriceSell', + 'Order/CancelOrder', + 'Order/AddCoinFundsRequest', + 'Order/AddStopOrder', + 'Payment/GetMyId', + 'Payment/Send', + 'Payment/Pay', + ], + 'get': [ + 'Account/Balance', + 'Account/Balance/v2', + 'Order/MyOrders', + 'Order/GetById', + 'Order/AccountHistory', + 'Order/OrderHistory', + ], + }, + }, + 'markets': { + 'BTC/NIS': { 'id': 'BtcNis', 'symbol': 'BTC/NIS', 'base': 'BTC', 'quote': 'NIS', 'baseId': 'Btc', 'quoteId': 'Nis' }, + 'ETH/NIS': { 'id': 'EthNis', 'symbol': 'ETH/NIS', 'base': 'ETH', 'quote': 'NIS', 'baseId': 'Eth', 'quoteId': 'Nis' }, + 'BCH/NIS': { 'id': 'BchabcNis', 'symbol': 'BCH/NIS', 'base': 'BCH', 'quote': 'NIS', 'baseId': 'Bchabc', 'quoteId': 'Nis' }, + 'LTC/NIS': { 'id': 'LtcNis', 'symbol': 'LTC/NIS', 'base': 'LTC', 'quote': 'NIS', 'baseId': 'Ltc', 'quoteId': 'Nis' }, + 'ETC/NIS': { 'id': 'EtcNis', 'symbol': 'ETC/NIS', 'base': 'ETC', 'quote': 'NIS', 'baseId': 'Etc', 'quoteId': 'Nis' }, + 'BTG/NIS': { 'id': 'BtgNis', 'symbol': 'BTG/NIS', 'base': 'BTG', 'quote': 'NIS', 'baseId': 'Btg', 'quoteId': 'Nis' }, + 'BSV/NIS': { 'id': 'BchsvNis', 'symbol': 'BSV/NIS', 'base': 'BSV', 'quote': 'NIS', 'baseId': 'Bchsv', 'quoteId': 'Nis' }, + 'GRIN/NIS': { 'id': 'GrinNis', 'symbol': 'GRIN/NIS', 'base': 'GRIN', 'quote': 'NIS', 'baseId': 'Grin', 'quoteId': 'Nis' }, + }, + 'fees': { + 'trading': { + 'maker': 0.5 / 100, + 'taker': 0.5 / 100, + }, + }, + 'options': { + 'fetchTradesMethod': 'public_get_exchanges_pair_lasttrades', + }, + 'exceptions': { + // { "error" : "Please provide valid APIkey" } + // { "error" : "Please provide valid nonce in Request UInt64.TryParse failed for nonce :" } + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balance = await this.privateGetAccountBalanceV2 (params); + // + // { + // "AVAILABLE_NIS": 0.0, + // "NIS": 0.0, + // "LOCKED_NIS": 0.0, + // "AVAILABLE_BTC": 0.0, + // "BTC": 0.0, + // "LOCKED_BTC": 0.0, + // "AVAILABLE_ETH": 0.0, + // "ETH": 0.0, + // "LOCKED_ETH": 0.0, + // "AVAILABLE_BCHSV": 0.0, + // "BCHSV": 0.0, + // "LOCKED_BCHSV": 0.0, + // "AVAILABLE_BCHABC": 0.0, + // "BCHABC": 0.0, + // "LOCKED_BCHABC": 0.0, + // "AVAILABLE_LTC": 0.0, + // "LTC": 0.0, + // "LOCKED_LTC": 0.0, + // "AVAILABLE_ETC": 0.0, + // "ETC": 0.0, + // "LOCKED_ETC": 0.0, + // "AVAILABLE_BTG": 0.0, + // "BTG": 0.0, + // "LOCKED_BTG": 0.0, + // "AVAILABLE_GRIN": 0.0, + // "GRIN": 0.0, + // "LOCKED_GRIN": 0.0, + // "Fees": { + // "BtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "EthNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "BchabcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "LtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "EtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "BtgNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "LtcBtc": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "BchsvNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }, + // "GrinNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 } + // } + // } + // + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const account = this.account (); + const currencyId = this.currencyId (code); + const uppercase = currencyId.toUpperCase (); + if (uppercase in balance) { + account['free'] = this.safeFloat (balance, 'AVAILABLE_' + uppercase); + account['total'] = this.safeFloat (balance, uppercase); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const orderbook = await this.publicGetExchangesPairOrderbook (this.extend (request, params)); + return this.parseOrderBook (orderbook); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const ticker = await this.publicGetExchangesPairTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const averagePrice = this.safeFloat (ticker, 'av'); + const baseVolume = this.safeFloat (ticker, 'a'); + let quoteVolume = undefined; + if (baseVolume !== undefined && averagePrice !== undefined) { + quoteVolume = baseVolume * averagePrice; + } + const last = this.safeFloat (ticker, 'll'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'h'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'l'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': averagePrice, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = this.options['fetchTradesMethod']; + const request = { + 'pair': market['id'], + }; + const response = await this[method] (this.extend (request, params)); + if (typeof response === 'string') { + throw new ExchangeError (response); + } + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let method = 'privatePostOrderAddOrder'; + const request = { + 'Amount': amount, + 'Pair': this.marketId (symbol), + }; + if (type === 'market') { + method += 'MarketPrice' + this.capitalize (side); + } else { + request['Price'] = price; + request['Total'] = amount * price; + request['IsBid'] = (side === 'buy'); + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['NewOrder']['id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostOrderCancelOrder (this.extend (request, params)); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.privateGetOrderMyOrders (this.extend (request, params)); + const orders = this.safeValue (response, market['id'], {}); + const asks = this.safeValue (orders, 'ask', []); + const bids = this.safeValue (orders, 'bid', []); + return this.parseOrders (this.arrayConcat (asks, bids), market, since, limit); + } + + parseOrder (order, market = undefined) { + const timestamp = this.safeInteger (order, 'created'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeValue (order, 'type'); + if (side === 0) { + side = 'buy'; + } else if (side === 1) { + side = 'sell'; + } + const id = this.safeString (order, 'id'); + const status = this.safeString (order, 'status'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': cost, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (limit !== undefined) { + request['take'] = limit; + } + request['take'] = limit; + if (since !== undefined) { + request['toTime'] = this.ymd (this.milliseconds (), '.'); + request['fromTime'] = this.ymd (since, '.'); + } + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privateGetOrderOrderHistory (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseTrade (trade, market = undefined) { + let timestamp = undefined; + let id = undefined; + let price = undefined; + let amount = undefined; + let orderId = undefined; + let feeCost = undefined; + let side = undefined; + const reference = this.safeString (trade, 'reference'); + if (reference !== undefined) { + timestamp = this.safeTimestamp (trade, 'ticks'); + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'firstAmount'); + const reference_parts = reference.split ('|'); // reference contains 'pair|orderId|tradeId' + if (market === undefined) { + const marketId = this.safeString (trade, 'pair'); + if (marketId in this.markets_by_id[marketId]) { + market = this.markets_by_id[marketId]; + } else if (reference_parts[0] in this.markets_by_id) { + market = this.markets_by_id[reference_parts[0]]; + } + } + orderId = reference_parts[1]; + id = reference_parts[2]; + side = this.safeInteger (trade, 'action'); + if (side === 0) { + side = 'buy'; + } else if (side === 1) { + side = 'sell'; + } + feeCost = this.safeFloat (trade, 'feeAmount'); + } else { + timestamp = this.safeTimestamp (trade, 'date'); + id = this.safeString (trade, 'tid'); + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'amount'); + side = this.safeValue (trade, 'isBid'); + if (side !== undefined) { + if (side) { + side = 'buy'; + } else { + side = 'sell'; + } + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': price * amount, + 'fee': { + 'cost': feeCost, + 'currency': 'NIS', + 'rate': undefined, + }, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + if (api === 'public') { + url += '.json'; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const query = this.extend ({ + 'nonce': nonce, + }, params); + const auth = this.urlencode (query); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + auth; + } + } else { + body = auth; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512', 'base64'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'key': this.apiKey, + 'sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; + diff --git a/TaskServer/node_modules/ccxt/js/bitbank.js b/TaskServer/node_modules/ccxt/js/bitbank.js new file mode 100644 index 000000000..cc8dda8d7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitbank.js @@ -0,0 +1,566 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, InvalidNonce, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitbank extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitbank', + 'name': 'bitbank', + 'countries': [ 'JP' ], + 'version': 'v1', + 'has': { + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '4h': '4hour', + '8h': '8hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/37808081-b87f2d9c-2e59-11e8-894d-c1900b7584fe.jpg', + 'api': { + 'public': 'https://public.bitbank.cc', + 'private': 'https://api.bitbank.cc', + }, + 'www': 'https://bitbank.cc/', + 'doc': 'https://docs.bitbank.cc/', + 'fees': 'https://bitbank.cc/docs/fees/', + }, + 'api': { + 'public': { + 'get': [ + '{pair}/ticker', + '{pair}/depth', + '{pair}/transactions', + '{pair}/transactions/{yyyymmdd}', + '{pair}/candlestick/{candletype}/{yyyymmdd}', + ], + }, + 'private': { + 'get': [ + 'user/assets', + 'user/spot/order', + 'user/spot/active_orders', + 'user/spot/trade_history', + 'user/withdrawal_account', + ], + 'post': [ + 'user/spot/order', + 'user/spot/cancel_order', + 'user/spot/cancel_orders', + 'user/spot/orders_info', + 'user/request_withdrawal', + ], + }, + }, + 'markets': { + 'BCH/BTC': { 'id': 'bcc_btc', 'symbol': 'BCH/BTC', 'base': 'BCH', 'quote': 'BTC', 'baseId': 'bcc', 'quoteId': 'btc' }, + 'BCH/JPY': { 'id': 'bcc_jpy', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bcc', 'quoteId': 'jpy' }, + 'MONA/BTC': { 'id': 'mona_btc', 'symbol': 'MONA/BTC', 'base': 'MONA', 'quote': 'BTC', 'baseId': 'mona', 'quoteId': 'btc' }, + 'MONA/JPY': { 'id': 'mona_jpy', 'symbol': 'MONA/JPY', 'base': 'MONA', 'quote': 'JPY', 'baseId': 'mona', 'quoteId': 'jpy' }, + 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' }, + 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, + 'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' }, + 'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' }, + }, + 'fees': { + 'trading': { + 'maker': -0.02 / 100, + 'taker': 0.12 / 100, + }, + 'funding': { + 'withdraw': { + // 'JPY': (amount > 30000) ? 756 : 540, + 'BTC': 0.001, + 'LTC': 0.001, + 'XRP': 0.15, + 'ETH': 0.0005, + 'MONA': 0.001, + 'BCC': 0.001, + }, + }, + }, + 'precision': { + 'price': 8, + 'amount': 8, + }, + 'exceptions': { + '20001': AuthenticationError, + '20002': AuthenticationError, + '20003': AuthenticationError, + '20005': AuthenticationError, + '20004': InvalidNonce, + '40020': InvalidOrder, + '40021': InvalidOrder, + '40025': ExchangeError, + '40013': OrderNotFound, + '40014': OrderNotFound, + '50008': PermissionDenied, + '50009': OrderNotFound, + '50010': OrderNotFound, + '60001': InsufficientFunds, + '60005': InvalidOrder, + }, + }); + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTicker (this.extend (request, params)); + return this.parseTicker (response['data'], market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this.publicGetPairDepth (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data', {}); + const timestamp = this.safeInteger (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'executed_at'); + let symbol = undefined; + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const id = this.safeString2 (trade, 'transaction_id', 'trade_id'); + const takerOrMaker = this.safeString (trade, 'maker_taker'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee_amount_quote'); + if (feeCost !== undefined) { + fee = { + 'currency': market['quote'], + 'cost': feeCurrency, + }; + } + const orderId = this.safeString (trade, 'order_id'); + const type = this.safeString (trade, 'type'); + const side = this.safeString (trade, 'side'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTransactions (this.extend (request, params)); + return this.parseTrades (response['data']['transactions'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + ohlcv[5], + parseFloat (ohlcv[0]), + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let date = this.milliseconds (); + date = this.ymd (date); + date = date.split ('-'); + const request = { + 'pair': market['id'], + 'candletype': this.timeframes[timeframe], + 'yyyymmdd': date.join (''), + }; + const response = await this.publicGetPairCandlestickCandletypeYyyymmdd (this.extend (request, params)); + const ohlcv = this.safeValue (response['data']['candlestick'][0], 'ohlcv'); + return this.parseOHLCVs (ohlcv, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetUserAssets (params); + const result = { 'info': response }; + const balances = response['data']['assets']; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'free_amount'), + 'used': this.safeFloat (balance, 'locked_amount'), + 'total': this.safeFloat (balance, 'onhand_amount'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'UNFILLED': 'open', + 'PARTIALLY_FILLED': 'open', + 'FULLY_FILLED': 'closed', + 'CANCELED_UNFILLED': 'canceled', + 'CANCELED_PARTIALLY_FILLED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'order_id'); + const marketId = this.safeString (order, 'pair'); + let symbol = undefined; + if (marketId && !market && (marketId in this.marketsById)) { + market = this.marketsById[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'ordered_at'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'start_amount'); + const filled = this.safeFloat (order, 'executed_amount'); + const remaining = this.safeFloat (order, 'remaining_amount'); + const average = this.safeFloat (order, 'average_price'); + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = filled * average; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const type = this.safeStringLower (order, 'type'); + const side = this.safeStringLower (order, 'side'); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder requires a price argument for both market and limit orders'); + } + const request = { + 'pair': market['id'], + 'amount': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + 'side': side, + 'type': type, + }; + const response = await this.privatePostUserSpotOrder (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'pair': market['id'], + }; + const response = await this.privatePostUserSpotCancelOrder (this.extend (request, params)); + return response['data']; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'pair': market['id'], + }; + const response = await this.privateGetUserSpotOrder (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privateGetUserSpotActiveOrders (this.extend (request, params)); + return this.parseOrders (response['data']['orders'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = {}; + if (market !== undefined) { + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['count'] = limit; + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privateGetUserSpotTradeHistory (this.extend (request, params)); + return this.parseTrades (response['data']['trades'], market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privateGetUserWithdrawalAccount (this.extend (request, params)); + // Not sure about this if there could be more than one account... + const accounts = response['data']['accounts']; + const address = this.safeString (accounts[0], 'address'); + return { + 'currency': currency, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + if (!('uuid' in params)) { + throw new ExchangeError (this.id + ' uuid is required for withdrawal'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + 'amount': amount, + }; + const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params)); + const txid = this.safeString (response['data'], 'txid'); + return { + 'info': response, + 'id': txid, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + '/'; + if (api === 'public') { + url += this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = nonce; + url += this.version + '/' + this.implodeParams (path, params); + if (method === 'POST') { + body = this.json (query); + auth += body; + } else { + auth += '/' + this.version + '/' + path; + if (Object.keys (query).length) { + query = this.urlencode (query); + url += '?' + query; + auth += '?' + query; + } + } + headers = { + 'Content-Type': 'application/json', + 'ACCESS-KEY': this.apiKey, + 'ACCESS-NONCE': nonce, + 'ACCESS-SIGNATURE': this.hmac (this.encode (auth), this.encode (this.secret)), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const success = this.safeInteger (response, 'success'); + const data = this.safeValue (response, 'data'); + if (!success || !data) { + const errorMessages = { + '10000': 'URL does not exist', + '10001': 'A system error occurred. Please contact support', + '10002': 'Invalid JSON format. Please check the contents of transmission', + '10003': 'A system error occurred. Please contact support', + '10005': 'A timeout error occurred. Please wait for a while and try again', + '20001': 'API authentication failed', + '20002': 'Illegal API key', + '20003': 'API key does not exist', + '20004': 'API Nonce does not exist', + '20005': 'API signature does not exist', + '20011': 'Two-step verification failed', + '20014': 'SMS authentication failed', + '30001': 'Please specify the order quantity', + '30006': 'Please specify the order ID', + '30007': 'Please specify the order ID array', + '30009': 'Please specify the stock', + '30012': 'Please specify the order price', + '30013': 'Trade Please specify either', + '30015': 'Please specify the order type', + '30016': 'Please specify asset name', + '30019': 'Please specify uuid', + '30039': 'Please specify the amount to be withdrawn', + '40001': 'The order quantity is invalid', + '40006': 'Count value is invalid', + '40007': 'End time is invalid', + '40008': 'end_id Value is invalid', + '40009': 'The from_id value is invalid', + '40013': 'The order ID is invalid', + '40014': 'The order ID array is invalid', + '40015': 'Too many specified orders', + '40017': 'Incorrect issue name', + '40020': 'The order price is invalid', + '40021': 'The trading classification is invalid', + '40022': 'Start date is invalid', + '40024': 'The order type is invalid', + '40025': 'Incorrect asset name', + '40028': 'uuid is invalid', + '40048': 'The amount of withdrawal is illegal', + '50003': 'Currently, this account is in a state where you can not perform the operation you specified. Please contact support', + '50004': 'Currently, this account is temporarily registered. Please try again after registering your account', + '50005': 'Currently, this account is locked. Please contact support', + '50006': 'Currently, this account is locked. Please contact support', + '50008': 'User identification has not been completed', + '50009': 'Your order does not exist', + '50010': 'Can not cancel specified order', + '50011': 'API not found', + '60001': 'The number of possessions is insufficient', + '60002': 'It exceeds the quantity upper limit of the tender buying order', + '60003': 'The specified quantity exceeds the limit', + '60004': 'The specified quantity is below the threshold', + '60005': 'The specified price is above the limit', + '60006': 'The specified price is below the lower limit', + '70001': 'A system error occurred. Please contact support', + '70002': 'A system error occurred. Please contact support', + '70003': 'A system error occurred. Please contact support', + '70004': 'We are unable to accept orders as the transaction is currently suspended', + '70005': 'Order can not be accepted because purchase order is currently suspended', + '70006': 'We can not accept orders because we are currently unsubscribed ', + '70009': 'We are currently temporarily restricting orders to be carried out. Please use the limit order.', + '70010': 'We are temporarily raising the minimum order quantity as the system load is now rising.', + }; + const errorClasses = this.exceptions; + const code = this.safeString (data, 'code'); + const message = this.safeString (errorMessages, code, 'Error'); + const ErrorClass = this.safeValue (errorClasses, code); + if (ErrorClass !== undefined) { + throw new ErrorClass (message); + } else { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitbay.js b/TaskServer/node_modules/ccxt/js/bitbay.js new file mode 100644 index 000000000..ef6373182 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitbay.js @@ -0,0 +1,1145 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitbay extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitbay', + 'name': 'BitBay', + 'countries': [ 'MT', 'EU' ], // Malta + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'withdraw': true, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + }, + 'urls': { + 'referral': 'https://auth.bitbay.net/ref/jHlbB4mIkdS1', + 'logo': 'https://user-images.githubusercontent.com/1294454/27766132-978a7bd8-5ece-11e7-9540-bc96d1e9bbb8.jpg', + 'www': 'https://bitbay.net', + 'api': { + 'public': 'https://bitbay.net/API/Public', + 'private': 'https://bitbay.net/API/Trading/tradingApi.php', + 'v1_01Public': 'https://api.bitbay.net/rest', + 'v1_01Private': 'https://api.bitbay.net/rest', + }, + 'doc': [ + 'https://bitbay.net/public-api', + 'https://bitbay.net/en/private-api', + 'https://bitbay.net/account/tab-api', + 'https://github.com/BitBayNet/API', + 'https://docs.bitbay.net/v1.0.1-en/reference', + ], + 'fees': 'https://bitbay.net/en/fees', + }, + 'api': { + 'public': { + 'get': [ + '{id}/all', + '{id}/market', + '{id}/orderbook', + '{id}/ticker', + '{id}/trades', + ], + }, + 'private': { + 'post': [ + 'info', + 'trade', + 'cancel', + 'orderbook', + 'orders', + 'transfer', + 'withdraw', + 'history', + 'transactions', + ], + }, + 'v1_01Public': { + 'get': [ + 'trading/ticker', + 'trading/ticker/{symbol}', + 'trading/stats', + 'trading/orderbook/{symbol}', + 'trading/transactions/{symbol}', + 'trading/candle/history/{symbol}/{resolution}', + ], + }, + 'v1_01Private': { + 'get': [ + 'payments/withdrawal/{detailId}', + 'payments/deposit/{detailId}', + 'trading/offer', + 'trading/config/{symbol}', + 'trading/history/transactions', + 'balances/BITBAY/history', + 'balances/BITBAY/balance', + 'fiat_cantor/rate/{baseId}/{quoteId}', + 'fiat_cantor/history', + ], + 'post': [ + 'trading/offer/{symbol}', + 'trading/config/{symbol}', + 'balances/BITBAY/balance', + 'balances/BITBAY/balance/transfer/{source}/{destination}', + 'fiat_cantor/exchange', + ], + 'delete': [ + 'trading/offer/{symbol}/{id}/{side}/{price}', + ], + 'put': [ + 'balances/BITBAY/balance/{id}', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.30 / 100, + 'taker': 0.43 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.0009, + 'LTC': 0.005, + 'ETH': 0.00126, + 'LSK': 0.2, + 'BCH': 0.0006, + 'GAME': 0.005, + 'DASH': 0.001, + 'BTG': 0.0008, + 'PLN': 4, + 'EUR': 1.5, + }, + }, + }, + 'exceptions': { + '400': ExchangeError, // At least one parameter wasn't set + '401': InvalidOrder, // Invalid order type + '402': InvalidOrder, // No orders with specified currencies + '403': InvalidOrder, // Invalid payment currency name + '404': InvalidOrder, // Error. Wrong transaction type + '405': InvalidOrder, // Order with this id doesn't exist + '406': InsufficientFunds, // No enough money or crypto + // code 407 not specified are not specified in their docs + '408': InvalidOrder, // Invalid currency name + '501': AuthenticationError, // Invalid public key + '502': AuthenticationError, // Invalid sign + '503': InvalidNonce, // Invalid moment parameter. Request time doesn't match current server time + '504': ExchangeError, // Invalid method + '505': AuthenticationError, // Key has no permission for this action + '506': AuthenticationError, // Account locked. Please contact with customer service + // codes 507 and 508 are not specified in their docs + '509': ExchangeError, // The BIC/SWIFT is required for this currency + '510': ExchangeError, // Invalid market name + 'FUNDS_NOT_SUFFICIENT': InsufficientFunds, + 'OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS': InvalidOrder, + 'OFFER_NOT_FOUND': OrderNotFound, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.v1_01PublicGetTradingTicker (params); + // + // { + // status: 'Ok', + // items: { + // 'BSV-USD': { + // market: { + // code: 'BSV-USD', + // first: { currency: 'BSV', minOffer: '0.00035', scale: 8 }, + // second: { currency: 'USD', minOffer: '5', scale: 2 } + // }, + // time: '1557569762154', + // highestBid: '52.31', + // lowestAsk: '62.99', + // rate: '63', + // previousRate: '51.21', + // }, + // }, + // } + // + const result = []; + const items = this.safeValue (response, 'items'); + const keys = Object.keys (items); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const item = items[key]; + const market = this.safeValue (item, 'market', {}); + const first = this.safeValue (market, 'first', {}); + const second = this.safeValue (market, 'second', {}); + const baseId = this.safeString (first, 'currency'); + const quoteId = this.safeString (second, 'currency'); + const id = baseId + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (first, 'scale'), + 'price': this.safeInteger (second, 'scale'), + }; + // todo: check that the limits have ben interpreted correctly + // todo: parse the fees page + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'active': undefined, + 'fee': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (first, 'minOffer'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (second, 'minOffer'), + 'max': undefined, + }, + }, + 'info': item, + }); + } + return result; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + const response = await this.v1_01PrivateGetTradingOffer (this.extend (request, params)); + const items = this.safeValue (response, 'items', []); + return this.parseOrders (items, undefined, since, limit, { 'status': 'open' }); + } + + parseOrder (order, market = undefined) { + // + // { + // market: 'ETH-EUR', + // offerType: 'Sell', + // id: '93d3657b-d616-11e9-9248-0242ac110005', + // currentAmount: '0.04', + // lockedAmount: '0.04', + // rate: '280', + // startAmount: '0.04', + // time: '1568372806924', + // postOnly: false, + // hidden: false, + // mode: 'limit', + // receivedAmount: '0.0', + // firstBalanceId: '5b816c3e-437c-4e43-9bef-47814ae7ebfc', + // secondBalanceId: 'ab43023b-4079-414c-b340-056e3430a3af' + // } + // + const marketId = this.safeString (order, 'market'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const timestamp = this.safeInteger (order, 'time'); + const amount = this.safeFloat (order, 'startAmount'); + const remaining = this.safeFloat (order, 'currentAmount'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = Math.max (0, amount - remaining); + } + } + return { + 'id': this.safeString (order, 'id'), + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': this.safeString (order, 'mode'), + 'side': this.safeStringLower (order, 'offerType'), + 'price': this.safeFloat (order, 'rate'), + 'amount': amount, + 'cost': undefined, + 'filled': filled, + 'remaining': remaining, + 'average': undefined, + 'fee': undefined, + 'trades': undefined, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbol) { + const markets = [ this.marketId (symbol) ]; + request['markets'] = markets; + } + const query = { 'query': this.json (this.extend (request, params)) }; + const response = await this.v1_01PrivateGetTradingHistoryTransactions (query); + // + // { + // status: 'Ok', + // totalRows: '67', + // items: [ + // { + // id: 'b54659a0-51b5-42a0-80eb-2ac5357ccee2', + // market: 'BTC-EUR', + // time: '1541697096247', + // amount: '0.00003', + // rate: '4341.44', + // initializedBy: 'Sell', + // wasTaker: false, + // userAction: 'Buy', + // offerId: 'bd19804a-6f89-4a69-adb8-eb078900d006', + // commissionValue: null + // }, + // ] + // } + // + const items = this.safeValue (response, 'items'); + const result = this.parseTrades (items, undefined, since, limit); + if (symbol === undefined) { + return result; + } + return this.filterBySymbol (result, symbol); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.v1_01PrivateGetBalancesBITBAYBalance (params); + const balances = this.safeValue (response, 'balances'); + if (balances === undefined) { + throw new ExchangeError (this.id + ' empty balance response ' + this.json (response)); + } + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'lockedFunds'); + account['free'] = this.safeFloat (balance, 'availableFunds'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const orderbook = await this.publicGetIdOrderbook (this.extend (request, params)); + return this.parseOrderBook (orderbook); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const ticker = await this.publicGetIdTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const baseVolume = this.safeFloat (ticker, 'volume'); + const vwap = this.safeFloat (ticker, 'vwap'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'average'), + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + const balanceCurrencies = []; + if (code !== undefined) { + const currency = this.currency (code); + balanceCurrencies.push (currency['id']); + } + let request = { + 'balanceCurrencies': balanceCurrencies, + }; + if (since !== undefined) { + request['fromTime'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + request = this.extend (request, params); + const response = await this.v1_01PrivateGetBalancesBITBAYHistory ({ 'query': this.json (request) }); + const items = response['items']; + return this.parseLedger (items, undefined, since, limit); + } + + parseLedgerEntry (item, currency = undefined) { + // + // FUNDS_MIGRATION + // { + // "historyId": "84ea7a29-7da5-4de5-b0c0-871e83cad765", + // "balance": { + // "id": "821ec166-cb88-4521-916c-f4eb44db98df", + // "currency": "LTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "LTC" + // }, + // "detailId": null, + // "time": 1506128252968, + // "type": "FUNDS_MIGRATION", + // "value": 0.0009957, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 0.0009957, "available": 0.0009957, "locked": 0 }, + // "change": { "total": 0.0009957, "available": 0.0009957, "locked": 0 } + // } + // + // CREATE_BALANCE + // { + // "historyId": "d0fabd8d-9107-4b5e-b9a6-3cab8af70d49", + // "balance": { + // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", + // "currency": "BTG", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTG" + // }, + // "detailId": null, + // "time": 1508895244751, + // "type": "CREATE_BALANCE", + // "value": 0, + // "fundsBefore": { "total": null, "available": null, "locked": null }, + // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, + // "change": { "total": 0, "available": 0, "locked": 0 } + // } + // + // BITCOIN_GOLD_FORK + // { + // "historyId": "2b4d52d3-611c-473d-b92c-8a8d87a24e41", + // "balance": { + // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", + // "currency": "BTG", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTG" + // }, + // "detailId": null, + // "time": 1508895244778, + // "type": "BITCOIN_GOLD_FORK", + // "value": 0.00453512, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, + // "change": { "total": 0.00453512, "available": 0.00453512, "locked": 0 } + // } + // + // ADD_FUNDS + // { + // "historyId": "3158236d-dae5-4a5d-81af-c1fa4af340fb", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "8e83a960-e737-4380-b8bb-259d6e236faa", + // "time": 1520631178816, + // "type": "ADD_FUNDS", + // "value": 0.628405, + // "fundsBefore": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, + // "fundsAfter": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, + // "change": { "total": 0.628405, "available": 0.628405, "locked": 0 } + // } + // + // TRANSACTION_PRE_LOCKING + // { + // "historyId": "e7d19e0f-03b3-46a8-bc72-dde72cc24ead", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": null, + // "time": 1520706403868, + // "type": "TRANSACTION_PRE_LOCKING", + // "value": -0.1, + // "fundsBefore": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, + // "fundsAfter": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, + // "change": { "total": 0, "available": -0.1, "locked": 0.1 } + // } + // + // TRANSACTION_POST_OUTCOME + // { + // "historyId": "c4010825-231d-4a9c-8e46-37cde1f7b63c", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "bf2876bc-b545-4503-96c8-ef4de8233876", + // "time": 1520706404032, + // "type": "TRANSACTION_POST_OUTCOME", + // "value": -0.01771415, + // "fundsBefore": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, + // "fundsAfter": { "total": 0.61522597, "available": 0.53294012, "locked": 0.08228585 }, + // "change": { "total": -0.01771415, "available": 0, "locked": -0.01771415 } + // } + // + // TRANSACTION_POST_INCOME + // { + // "historyId": "7f18b7af-b676-4125-84fd-042e683046f6", + // "balance": { + // "id": "ab43023b-4079-414c-b340-056e3430a3af", + // "currency": "EUR", + // "type": "FIAT", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "EUR" + // }, + // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", + // "time": 1520706404035, + // "type": "TRANSACTION_POST_INCOME", + // "value": 628.78, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 628.78, "available": 628.78, "locked": 0 }, + // "change": { "total": 628.78, "available": 628.78, "locked": 0 } + // } + // + // TRANSACTION_COMMISSION_OUTCOME + // { + // "historyId": "843177fa-61bc-4cbf-8be5-b029d856c93b", + // "balance": { + // "id": "ab43023b-4079-414c-b340-056e3430a3af", + // "currency": "EUR", + // "type": "FIAT", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "EUR" + // }, + // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", + // "time": 1520706404050, + // "type": "TRANSACTION_COMMISSION_OUTCOME", + // "value": -2.71, + // "fundsBefore": { "total": 766.06, "available": 766.06, "locked": 0 }, + // "fundsAfter": { "total": 763.35,"available": 763.35, "locked": 0 }, + // "change": { "total": -2.71, "available": -2.71, "locked": 0 } + // } + // + // TRANSACTION_OFFER_COMPLETED_RETURN + // { + // "historyId": "cac69b04-c518-4dc5-9d86-e76e91f2e1d2", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": null, + // "time": 1520714886425, + // "type": "TRANSACTION_OFFER_COMPLETED_RETURN", + // "value": 0.00000196, + // "fundsBefore": { "total": 0.00941208, "available": 0.00941012, "locked": 0.00000196 }, + // "fundsAfter": { "total": 0.00941208, "available": 0.00941208, "locked": 0 }, + // "change": { "total": 0, "available": 0.00000196, "locked": -0.00000196 } + // } + // + // WITHDRAWAL_LOCK_FUNDS + // { + // "historyId": "03de2271-66ab-4960-a786-87ab9551fc14", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", + // "time": 1522245654481, + // "type": "WITHDRAWAL_LOCK_FUNDS", + // "value": -0.8, + // "fundsBefore": { "total": 0.8, "available": 0.8, "locked": 0 }, + // "fundsAfter": { "total": 0.8, "available": 0, "locked": 0.8 }, + // "change": { "total": 0, "available": -0.8, "locked": 0.8 } + // } + // + // WITHDRAWAL_SUBTRACT_FUNDS + // { + // "historyId": "b0308c89-5288-438d-a306-c6448b1a266d", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", + // "time": 1522246526186, + // "type": "WITHDRAWAL_SUBTRACT_FUNDS", + // "value": -0.8, + // "fundsBefore": { "total": 0.8, "available": 0, "locked": 0.8 }, + // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, + // "change": { "total": -0.8, "available": 0, "locked": -0.8 } + // } + // + // TRANSACTION_OFFER_ABORTED_RETURN + // { + // "historyId": "b1a3c075-d403-4e05-8f32-40512cdd88c0", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": null, + // "time": 1522512298662, + // "type": "TRANSACTION_OFFER_ABORTED_RETURN", + // "value": 0.0564931, + // "fundsBefore": { "total": 0.44951311, "available": 0.39302001, "locked": 0.0564931 }, + // "fundsAfter": { "total": 0.44951311, "available": 0.44951311, "locked": 0 }, + // "change": { "total": 0, "available": 0.0564931, "locked": -0.0564931 } + // } + // + // WITHDRAWAL_UNLOCK_FUNDS + // { + // "historyId": "0ed569a2-c330-482e-bb89-4cb553fb5b11", + // "balance": { + // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", + // "currency": "BTC", + // "type": "CRYPTO", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "BTC" + // }, + // "detailId": "0c7be256-c336-4111-bee7-4eb22e339700", + // "time": 1527866360785, + // "type": "WITHDRAWAL_UNLOCK_FUNDS", + // "value": 0.05045, + // "fundsBefore": { "total": 0.86001578, "available": 0.80956578, "locked": 0.05045 }, + // "fundsAfter": { "total": 0.86001578, "available": 0.86001578, "locked": 0 }, + // "change": { "total": 0, "available": 0.05045, "locked": -0.05045 } + // } + // + // TRANSACTION_COMMISSION_RETURN + // { + // "historyId": "07c89c27-46f1-4d7a-8518-b73798bf168a", + // "balance": { + // "id": "ab43023b-4079-414c-b340-056e3430a3af", + // "currency": "EUR", + // "type": "FIAT", + // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", + // "name": "EUR" + // }, + // "detailId": null, + // "time": 1528304043063, + // "type": "TRANSACTION_COMMISSION_RETURN", + // "value": 0.6, + // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, + // "fundsAfter": { "total": 0.6, "available": 0.6, "locked": 0 }, + // "change": { "total": 0.6, "available": 0.6, "locked": 0 } + // } + // + const timestamp = this.safeInteger (item, 'time'); + const balance = this.safeValue (item, 'balance', {}); + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const change = this.safeValue (item, 'change', {}); + let amount = this.safeFloat (change, 'total'); + let direction = 'in'; + if (amount < 0) { + direction = 'out'; + amount = -amount; + } + const id = this.safeString (item, 'historyId'); + // there are 2 undocumented api calls: (v1_01PrivateGetPaymentsDepositDetailId and v1_01PrivateGetPaymentsWithdrawalDetailId) + // that can be used to enrich the transfers with txid, address etc (you need to use info.detailId as a parameter) + const referenceId = this.safeString (item, 'detailId'); + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const fundsBefore = this.safeValue (item, 'fundsBefore', {}); + const before = this.safeFloat (fundsBefore, 'total'); + const fundsAfter = this.safeValue (item, 'fundsAfter', {}); + const after = this.safeFloat (fundsAfter, 'total'); + return { + 'info': item, + 'id': id, + 'direction': direction, + 'account': undefined, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, + 'after': after, + 'status': 'ok', + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + parseLedgerEntryType (type) { + const types = { + 'ADD_FUNDS': 'transaction', + 'BITCOIN_GOLD_FORK': 'transaction', + 'CREATE_BALANCE': 'transaction', + 'FUNDS_MIGRATION': 'transaction', + 'WITHDRAWAL_LOCK_FUNDS': 'transaction', + 'WITHDRAWAL_SUBTRACT_FUNDS': 'transaction', + 'WITHDRAWAL_UNLOCK_FUNDS': 'transaction', + 'TRANSACTION_COMMISSION_OUTCOME': 'fee', + 'TRANSACTION_COMMISSION_RETURN': 'fee', + 'TRANSACTION_OFFER_ABORTED_RETURN': 'trade', + 'TRANSACTION_OFFER_COMPLETED_RETURN': 'trade', + 'TRANSACTION_POST_INCOME': 'trade', + 'TRANSACTION_POST_OUTCOME': 'trade', + 'TRANSACTION_PRE_LOCKING': 'trade', + }; + return this.safeString (types, type, type); + } + + parseTrade (trade, market = undefined) { + // + // createOrder trades + // + // { + // "rate": "0.02195928", + // "amount": "0.00167952" + // } + // + // fetchMyTrades (private) + // + // { + // amount: "0.29285199", + // commissionValue: "0.00125927", + // id: "11c8203a-a267-11e9-b698-0242ac110007", + // initializedBy: "Buy", + // market: "ETH-EUR", + // offerId: "11c82038-a267-11e9-b698-0242ac110007", + // rate: "277", + // time: "1562689917517", + // userAction: "Buy", + // wasTaker: true, + // } + // + // fetchTrades (public) + // + // { + // id: 'df00b0da-e5e0-11e9-8c19-0242ac11000a', + // t: '1570108958831', + // a: '0.04776653', + // r: '0.02145854', + // ty: 'Sell' + // } + // + const timestamp = this.safeInteger2 (trade, 'time', 't'); + const userAction = this.safeString (trade, 'userAction'); + const side = (userAction === 'Buy') ? 'buy' : 'sell'; + const wasTaker = this.safeValue (trade, 'wasTaker'); + let takerOrMaker = undefined; + if (wasTaker !== undefined) { + takerOrMaker = wasTaker ? 'taker' : 'maker'; + } + const price = this.safeFloat2 (trade, 'rate', 'r'); + const amount = this.safeFloat2 (trade, 'amount', 'a'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const feeCost = this.safeFloat (trade, 'commissionValue'); + const marketId = this.safeString (trade, 'market'); + let base = undefined; + let quote = undefined; + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + base = market['base']; + quote = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + if (symbol === undefined) { + symbol = market['symbol']; + } + if (base === undefined) { + base = market['base']; + } + } + let fee = undefined; + if (feeCost !== undefined) { + const feeCcy = (side === 'buy') ? base : quote; + fee = { + 'currency': feeCcy, + 'cost': feeCost, + }; + } + const order = this.safeString (trade, 'offerId'); + // todo: check this logic + let type = undefined; + if (order !== undefined) { + type = order ? 'limit' : 'market'; + } + return { + 'id': this.safeString (trade, 'id'), + 'order': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tradingSymbol = market['baseId'] + '-' + market['quoteId']; + const request = { + 'symbol': tradingSymbol, + }; + if (since !== undefined) { + request['fromTime'] = since - 1; // result does not include exactly `since` time therefore decrease by 1 + } + if (limit !== undefined) { + request['limit'] = limit; // default - 10, max - 300 + } + const response = await this.v1_01PublicGetTradingTransactionsSymbol (this.extend (request, params)); + const items = this.safeValue (response, 'items'); + return this.parseTrades (items, symbol, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tradingSymbol = market['baseId'] + '-' + market['quoteId']; + const request = { + 'symbol': tradingSymbol, + 'offerType': side, + 'amount': amount, + 'mode': type, + }; + if (type === 'limit') { + request['rate'] = price; + } + const response = await this.v1_01PrivatePostTradingOfferSymbol (this.extend (request, params)); + // + // unfilled (open order) + // + // { + // status: 'Ok', + // completed: false, // can deduce status from here + // offerId: 'ce9cc72e-d61c-11e9-9248-0242ac110005', + // transactions: [], // can deduce order info from here + // } + // + // filled (closed order) + // + // { + // "status": "Ok", + // "offerId": "942a4a3e-e922-11e9-8c19-0242ac11000a", + // "completed": true, + // "transactions": [ + // { + // "rate": "0.02195928", + // "amount": "0.00167952" + // }, + // { + // "rate": "0.02195928", + // "amount": "0.00167952" + // }, + // { + // "rate": "0.02196207", + // "amount": "0.27704177" + // } + // ] + // } + // + // partially-filled (open order) + // + // { + // "status": "Ok", + // "offerId": "d0ebefab-f4d7-11e9-8c19-0242ac11000a", + // "completed": false, + // "transactions": [ + // { + // "rate": "0.02106404", + // "amount": "0.0019625" + // }, + // { + // "rate": "0.02106404", + // "amount": "0.0019625" + // }, + // { + // "rate": "0.02105901", + // "amount": "0.00975256" + // } + // ] + // } + // + const timestamp = this.milliseconds (); // the real timestamp is missing in the response + const id = this.safeString (response, 'offerId'); + const completed = this.safeValue (response, 'completed', false); + const status = completed ? 'closed' : 'open'; + let filled = 0; + let cost = undefined; + const transactions = this.safeValue (response, 'transactions'); + let trades = undefined; + if (transactions !== undefined) { + trades = this.parseTrades (transactions, market, undefined, undefined, { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'side': side, + 'type': type, + 'orderId': id, + }); + cost = 0; + for (let i = 0; i < trades.length; i++) { + filled = this.sum (filled, trades[i]['amount']); + cost = this.sum (cost, trades[i]['cost']); + } + } + const remaining = amount - filled; + return { + 'id': id, + 'info': response, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': parseFloat (price), + 'amount': parseFloat (amount), + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'average': undefined, + 'fee': undefined, + 'trades': trades, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const side = this.safeString (params, 'side'); + if (side === undefined) { + throw new ExchangeError (this.id + ' cancelOrder() requires a `side` parameter ("buy" or "sell")'); + } + const price = this.safeValue (params, 'price'); + if (price === undefined) { + throw new ExchangeError (this.id + ' cancelOrder() requires a `price` parameter (float or string)'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const tradingSymbol = market['baseId'] + '-' + market['quoteId']; + const request = { + 'symbol': tradingSymbol, + 'id': id, + 'side': side, + 'price': price, + }; + // { status: 'Fail', errors: [ 'NOT_RECOGNIZED_OFFER_TYPE' ] } -- if required params are missing + // { status: 'Ok', errors: [] } + return this.v1_01PrivateDeleteTradingOfferSymbolIdSidePrice (this.extend (request, params)); + } + + isFiat (currency) { + const fiatCurrencies = { + 'USD': true, + 'EUR': true, + 'PLN': true, + }; + return this.safeValue (fiatCurrencies, currency, false); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + let method = undefined; + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'quantity': amount, + }; + if (this.isFiat (code)) { + method = 'privatePostWithdraw'; + // request['account'] = params['account']; // they demand an account number + // request['express'] = params['express']; // whatever it means, they don't explain + // request['bic'] = ''; + } else { + method = 'privatePostTransfer'; + if (tag !== undefined) { + address += '?dt=' + tag.toString (); + } + request['address'] = address; + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api === 'public') { + const query = this.omit (params, this.extractParams (path)); + url += '/' + this.implodeParams (path, params) + '.json'; + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'v1_01Public') { + const query = this.omit (params, this.extractParams (path)); + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'v1_01Private') { + this.checkRequiredCredentials (); + const query = this.omit (params, this.extractParams (path)); + url += '/' + this.implodeParams (path, params); + const nonce = this.milliseconds ().toString (); + let payload = undefined; + if (method !== 'POST') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + payload = this.apiKey + nonce; + } else if (body === undefined) { + body = this.json (query); + payload = this.apiKey + nonce + body; + } + headers = { + 'Request-Timestamp': nonce, + 'Operation-Id': this.uuid (), + 'API-Key': this.apiKey, + 'API-Hash': this.hmac (this.encode (payload), this.encode (this.secret), 'sha512'), + 'Content-Type': 'application/json', + }; + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'method': path, + 'moment': this.nonce (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'API-Key': this.apiKey, + 'API-Hash': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('code' in response) { + // + // bitbay returns the integer 'success': 1 key from their private API + // or an integer 'code' value from 0 to 510 and an error message + // + // { 'success': 1, ... } + // { 'code': 502, 'message': 'Invalid sign' } + // { 'code': 0, 'message': 'offer funds not exceeding minimums' } + // + // 400 At least one parameter wasn't set + // 401 Invalid order type + // 402 No orders with specified currencies + // 403 Invalid payment currency name + // 404 Error. Wrong transaction type + // 405 Order with this id doesn't exist + // 406 No enough money or crypto + // 408 Invalid currency name + // 501 Invalid public key + // 502 Invalid sign + // 503 Invalid moment parameter. Request time doesn't match current server time + // 504 Invalid method + // 505 Key has no permission for this action + // 506 Account locked. Please contact with customer service + // 509 The BIC/SWIFT is required for this currency + // 510 Invalid market name + // + const code = this.safeString (response, 'code'); // always an integer + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } else if ('status' in response) { + // + // {"status":"Fail","errors":["OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS"]} + // + const status = this.safeString (response, 'status'); + if (status === 'Fail') { + const errors = this.safeValue (response, 'errors'); + const feedback = this.id + ' ' + body; + for (let i = 0; i < errors.length; i++) { + const error = errors[i]; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + } + throw new ExchangeError (feedback); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitfinex.js b/TaskServer/node_modules/ccxt/js/bitfinex.js new file mode 100644 index 000000000..3ff8ac32f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitfinex.js @@ -0,0 +1,1178 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { NotSupported, RateLimitExceeded, AuthenticationError, PermissionDenied, ArgumentsRequired, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, InvalidNonce } = require ('./base/errors'); +const { SIGNIFICANT_DIGITS } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bitfinex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitfinex', + 'name': 'Bitfinex', + 'countries': [ 'VG' ], + 'version': 'v1', + 'rateLimit': 1500, + 'certified': true, + // new metainfo interface + 'has': { + 'CORS': false, + 'cancelAllOrders': true, + 'createDepositAddress': true, + 'deposit': true, + 'fetchClosedOrders': true, + 'fetchDepositAddress': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchFundingFees': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchTransactions': true, + 'fetchDeposits': false, + 'fetchWithdrawals': false, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '3h': '3h', + '6h': '6h', + '12h': '12h', + '1d': '1D', + '1w': '7D', + '2w': '14D', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg', + 'api': { + 'v2': 'https://api-pub.bitfinex.com', // https://github.com/ccxt/ccxt/issues/5109 + 'public': 'https://api.bitfinex.com', + 'private': 'https://api.bitfinex.com', + }, + 'www': 'https://www.bitfinex.com', + 'referral': 'https://www.bitfinex.com/?refcode=P61eYxFL', + 'doc': [ + 'https://docs.bitfinex.com/v1/docs', + 'https://github.com/bitfinexcom/bitfinex-api-node', + ], + }, + 'api': { + // v2 symbol ids require a 't' prefix + // just the public part of it (use bitfinex2 for everything else) + 'v2': { + 'get': [ + 'platform/status', + 'tickers', + 'ticker/{symbol}', + 'trades/{symbol}/hist', + 'book/{symbol}/{precision}', + 'book/{symbol}/P0', + 'book/{symbol}/P1', + 'book/{symbol}/P2', + 'book/{symbol}/P3', + 'book/{symbol}/R0', + 'stats1/{key}:{size}:{symbol}:{side}/{section}', + 'stats1/{key}:{size}:{symbol}/{section}', + 'stats1/{key}:{size}:{symbol}:long/last', + 'stats1/{key}:{size}:{symbol}:long/hist', + 'stats1/{key}:{size}:{symbol}:short/last', + 'stats1/{key}:{size}:{symbol}:short/hist', + 'candles/trade:{timeframe}:{symbol}/{section}', + 'candles/trade:{timeframe}:{symbol}/last', + 'candles/trade:{timeframe}:{symbol}/hist', + ], + }, + 'public': { + 'get': [ + 'book/{symbol}', + // 'candles/{symbol}', + 'lendbook/{currency}', + 'lends/{currency}', + 'pubticker/{symbol}', + 'stats/{symbol}', + 'symbols', + 'symbols_details', + 'tickers', + 'trades/{symbol}', + ], + }, + 'private': { + 'post': [ + 'account_fees', + 'account_infos', + 'balances', + 'basket_manage', + 'credits', + 'deposit/new', + 'funding/close', + 'history', + 'history/movements', + 'key_info', + 'margin_infos', + 'mytrades', + 'mytrades_funding', + 'offer/cancel', + 'offer/new', + 'offer/status', + 'offers', + 'offers/hist', + 'order/cancel', + 'order/cancel/all', + 'order/cancel/multi', + 'order/cancel/replace', + 'order/new', + 'order/new/multi', + 'order/status', + 'orders', + 'orders/hist', + 'position/claim', + 'position/close', + 'positions', + 'summary', + 'taken_funds', + 'total_taken_funds', + 'transfer', + 'unused_taken_funds', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + 'tiers': { + 'taker': [ + [0, 0.2 / 100], + [500000, 0.2 / 100], + [1000000, 0.2 / 100], + [2500000, 0.2 / 100], + [5000000, 0.2 / 100], + [7500000, 0.2 / 100], + [10000000, 0.18 / 100], + [15000000, 0.16 / 100], + [20000000, 0.14 / 100], + [25000000, 0.12 / 100], + [30000000, 0.1 / 100], + ], + 'maker': [ + [0, 0.1 / 100], + [500000, 0.08 / 100], + [1000000, 0.06 / 100], + [2500000, 0.04 / 100], + [5000000, 0.02 / 100], + [7500000, 0], + [10000000, 0], + [15000000, 0], + [20000000, 0], + [25000000, 0], + [30000000, 0], + ], + }, + }, + 'funding': { + 'tierBased': false, // true for tier-based/progressive + 'percentage': false, // fixed commission + // Actually deposit fees are free for larger deposits (> $1000 USD equivalent) + // these values below are deprecated, we should not hardcode fees and limits anymore + // to be reimplemented with bitfinex funding fees from their API or web endpoints + 'deposit': { + 'BTC': 0.0004, + 'IOTA': 0.5, + 'ETH': 0.0027, + 'BCH': 0.0001, + 'LTC': 0.001, + 'EOS': 0.24279, + 'XMR': 0.04, + 'SAN': 0.99269, + 'DASH': 0.01, + 'ETC': 0.01, + 'XRP': 0.02, + 'YYW': 16.915, + 'NEO': 0, + 'ZEC': 0.001, + 'BTG': 0, + 'OMG': 0.14026, + 'DATA': 20.773, + 'QASH': 1.9858, + 'ETP': 0.01, + 'QTUM': 0.01, + 'EDO': 0.95001, + 'AVT': 1.3045, + 'USDT': 0, + 'TRX': 28.184, + 'ZRX': 1.9947, + 'RCN': 10.793, + 'TNB': 31.915, + 'SNT': 14.976, + 'RLC': 1.414, + 'GNT': 5.8952, + 'SPK': 10.893, + 'REP': 0.041168, + 'BAT': 6.1546, + 'ELF': 1.8753, + 'FUN': 32.336, + 'SNG': 18.622, + 'AID': 8.08, + 'MNA': 16.617, + 'NEC': 1.6504, + 'XTZ': 0.2, + }, + 'withdraw': { + 'BTC': 0.0004, + 'IOTA': 0.5, + 'ETH': 0.0027, + 'BCH': 0.0001, + 'LTC': 0.001, + 'EOS': 0.24279, + 'XMR': 0.04, + 'SAN': 0.99269, + 'DASH': 0.01, + 'ETC': 0.01, + 'XRP': 0.02, + 'YYW': 16.915, + 'NEO': 0, + 'ZEC': 0.001, + 'BTG': 0, + 'OMG': 0.14026, + 'DATA': 20.773, + 'QASH': 1.9858, + 'ETP': 0.01, + 'QTUM': 0.01, + 'EDO': 0.95001, + 'AVT': 1.3045, + 'USDT': 20, + 'TRX': 28.184, + 'ZRX': 1.9947, + 'RCN': 10.793, + 'TNB': 31.915, + 'SNT': 14.976, + 'RLC': 1.414, + 'GNT': 5.8952, + 'SPK': 10.893, + 'REP': 0.041168, + 'BAT': 6.1546, + 'ELF': 1.8753, + 'FUN': 32.336, + 'SNG': 18.622, + 'AID': 8.08, + 'MNA': 16.617, + 'NEC': 1.6504, + 'XTZ': 0.2, + }, + }, + }, + 'commonCurrencies': { + 'ABS': 'ABYSS', + 'AIO': 'AION', + 'ALG': 'ALGO', // https://github.com/ccxt/ccxt/issues/6034 + 'AMP': 'AMPL', + 'ATM': 'ATMI', + 'ATO': 'ATOM', // https://github.com/ccxt/ccxt/issues/5118 + 'BAB': 'BCH', + 'CTX': 'CTXC', + 'DAD': 'DADI', + 'DAT': 'DATA', + 'DSH': 'DASH', + 'DRK': 'DRK', + 'GSD': 'GUSD', + 'HOT': 'Hydro Protocol', + 'IOS': 'IOST', + 'IOT': 'IOTA', + 'IQX': 'IQ', + 'MIT': 'MITH', + 'MNA': 'MANA', + 'NCA': 'NCASH', + 'ORS': 'ORS Group', // conflict with Origin Sport #3230 + 'POY': 'POLY', + 'QSH': 'QASH', + 'QTM': 'QTUM', + 'SEE': 'SEER', + 'SNG': 'SNGLS', + 'SPK': 'SPANK', + 'STJ': 'STORJ', + 'TSD': 'TUSD', + 'YYW': 'YOYOW', + 'UDC': 'USDC', + 'UST': 'USDT', + 'UTN': 'UTNP', + 'VSY': 'VSYS', + 'XCH': 'XCHF', + }, + 'exceptions': { + 'exact': { + 'temporarily_unavailable': ExchangeNotAvailable, // Sorry, the service is temporarily unavailable. See https://www.bitfinex.com/ for more info. + 'Order could not be cancelled.': OrderNotFound, // non-existent order + 'No such order found.': OrderNotFound, // ? + 'Order price must be positive.': InvalidOrder, // on price <= 0 + 'Could not find a key matching the given X-BFX-APIKEY.': AuthenticationError, + 'Key price should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (price) + 'Key amount should be a decimal number, e.g. "123.456"': InvalidOrder, // on isNaN (amount) + 'ERR_RATE_LIMIT': RateLimitExceeded, + 'Ratelimit': RateLimitExceeded, + 'Nonce is too small.': InvalidNonce, + 'No summary found.': ExchangeError, // fetchTradingFees (summary) endpoint can give this vague error message + 'Cannot evaluate your available balance, please try again': ExchangeNotAvailable, + }, + 'broad': { + 'This API key does not have permission': PermissionDenied, // authenticated but not authorized + 'not enough exchange balance for ': InsufficientFunds, // when buying cost is greater than the available quote currency + 'minimum size for ': InvalidOrder, // when amount below limits.amount.min + 'Invalid order': InvalidOrder, // ? + 'The available balance is only': InsufficientFunds, // {"status":"error","message":"Cannot withdraw 1.0027 ETH from your exchange wallet. The available balance is only 0.0 ETH. If you have limit orders, open positions, unused or active margin funding, this will decrease your available balance. To increase it, you can cancel limit orders or reduce/close your positions.","withdrawal_id":0,"fees":"0.0027"} + }, + }, + 'precisionMode': SIGNIFICANT_DIGITS, + 'options': { + 'currencyNames': { + 'AGI': 'agi', + 'AID': 'aid', + 'AIO': 'aio', + 'ANT': 'ant', + 'AVT': 'aventus', // #1811 + 'BAT': 'bat', + // https://github.com/ccxt/ccxt/issues/5833 + 'BCH': 'bab', // undocumented + // 'BCH': 'bcash', // undocumented + 'BCI': 'bci', + 'BFT': 'bft', + 'BTC': 'bitcoin', + 'BTG': 'bgold', + 'CFI': 'cfi', + 'DAI': 'dai', + 'DADI': 'dad', + 'DASH': 'dash', + 'DATA': 'datacoin', + 'DTH': 'dth', + 'EDO': 'eidoo', // #1811 + 'ELF': 'elf', + 'EOS': 'eos', + 'ETC': 'ethereumc', + 'ETH': 'ethereum', + 'ETP': 'metaverse', + 'FUN': 'fun', + 'GNT': 'golem', + 'IOST': 'ios', + 'IOTA': 'iota', + // https://github.com/ccxt/ccxt/issues/5833 + 'LEO': 'let', // ETH chain + // 'LEO': 'les', // EOS chain + 'LRC': 'lrc', + 'LTC': 'litecoin', + 'LYM': 'lym', + 'MANA': 'mna', + 'MIT': 'mit', + 'MKR': 'mkr', + 'MTN': 'mtn', + 'NEO': 'neo', + 'ODE': 'ode', + 'OMG': 'omisego', + 'OMNI': 'mastercoin', + 'QASH': 'qash', + 'QTUM': 'qtum', // #1811 + 'RCN': 'rcn', + 'RDN': 'rdn', + 'REP': 'rep', + 'REQ': 'req', + 'RLC': 'rlc', + 'SAN': 'santiment', + 'SNGLS': 'sng', + 'SNT': 'status', + 'SPANK': 'spk', + 'STORJ': 'stj', + 'TNB': 'tnb', + 'TRX': 'trx', + 'USD': 'wire', + 'USDC': 'udc', // https://github.com/ccxt/ccxt/issues/5833 + 'UTK': 'utk', + 'USDT': 'tetheruso', // Tether on Omni + // 'USDT': 'tetheruse', // Tether on ERC20 + // 'USDT': 'tetherusl', // Tether on Liquid + // 'USDT': 'tetherusx', // Tether on Tron + // 'USDT': 'tetheruss', // Tether on EOS + 'VEE': 'vee', + 'WAX': 'wax', + 'XLM': 'xlm', + 'XMR': 'monero', + 'XRP': 'ripple', + 'XVG': 'xvg', + 'YOYOW': 'yoyow', + 'ZEC': 'zcash', + 'ZRX': 'zrx', + 'XTZ': 'tezos', + }, + 'orderTypes': { + 'limit': 'exchange limit', + 'market': 'exchange market', + }, + }, + }); + } + + async fetchFundingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostAccountFees (params); + const fees = response['withdraw']; + const withdraw = {}; + const ids = Object.keys (fees); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + withdraw[code] = this.safeFloat (fees, id); + } + return { + 'info': response, + 'withdraw': withdraw, + 'deposit': withdraw, // only for deposits of less than $1000 + }; + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostSummary (params); + // + // { + // time: '2019-02-20T15:50:19.152000Z', + // trade_vol_30d: [ + // { + // curr: 'Total (USD)', + // vol: 0, + // vol_maker: 0, + // vol_BFX: 0, + // vol_BFX_maker: 0, + // vol_ETHFX: 0, + // vol_ETHFX_maker: 0 + // } + // ], + // fees_funding_30d: {}, + // fees_funding_total_30d: 0, + // fees_trading_30d: {}, + // fees_trading_total_30d: 0, + // maker_fee: 0.001, + // taker_fee: 0.002 + // } + // + return { + 'info': response, + 'maker': this.safeFloat (response, 'maker_fee'), + 'taker': this.safeFloat (response, 'taker_fee'), + }; + } + + async fetchMarkets (params = {}) { + const ids = await this.publicGetSymbols (); + const details = await this.publicGetSymbolsDetails (); + const result = []; + for (let i = 0; i < details.length; i++) { + const market = details[i]; + let id = this.safeString (market, 'pair'); + if (!this.inArray (id, ids)) { + continue; + } + id = id.toUpperCase (); + let baseId = undefined; + let quoteId = undefined; + if (id.indexOf (':') >= 0) { + const parts = id.split (':'); + baseId = parts[0]; + quoteId = parts[1]; + } else { + baseId = id.slice (0, 3); + quoteId = id.slice (3, 6); + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': market['price_precision'], + 'amount': undefined, + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_order_size'), + 'max': this.safeFloat (market, 'maximum_order_size'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + }; + limits['cost'] = { + 'min': limits['amount']['min'] * limits['price']['min'], + 'max': undefined, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + amountToPrecision (symbol, amount) { + return this.numberToString (amount); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let key = 'quote'; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + const code = market[key]; + const currency = this.safeValue (this.currencies, code); + if (currency !== undefined) { + const precision = this.safeInteger (currency, 'precision'); + if (precision !== undefined) { + cost = parseFloat (this.currencyToPrecision (code, cost)); + } + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balanceType = this.safeString (params, 'type', 'exchange'); + const query = this.omit (params, 'type'); + const response = await this.privatePostBalances (query); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + if (balance['type'] === balanceType) { + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + // bitfinex had BCH previously, now it's BAB, but the old + // BCH symbol is kept for backward-compatibility + // we need a workaround here so that the old BCH balance + // would not override the new BAB balance (BAB is unified to BCH) + // https://github.com/ccxt/ccxt/issues/4989 + if (!(code in result)) { + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'amount'); + result[code] = account; + } + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit_bids'] = limit; + request['limit_asks'] = limit; + } + const response = await this.publicGetBookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.publicGetPubtickerSymbol (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + let timestamp = this.safeFloat (ticker, 'timestamp'); + if (timestamp !== undefined) { + timestamp *= 1000; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else if ('pair' in ticker) { + const marketId = this.safeString (ticker, 'pair'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const baseId = marketId.slice (0, 3); + const quoteId = marketId.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + const last = this.safeFloat (ticker, 'last_price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'mid'), + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market) { + const id = this.safeString (trade, 'tid'); + let timestamp = this.safeFloat (trade, 'timestamp'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp) * 1000; + } + const type = undefined; + const side = this.safeStringLower (trade, 'type'); + const orderId = this.safeString (trade, 'order_id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let fee = undefined; + if ('fee_amount' in trade) { + const feeCost = -this.safeFloat (trade, 'fee_amount'); + const feeCurrencyId = this.safeString (trade, 'fee_currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': type, + 'order': orderId, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'limit_trades': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.publicGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit_trades'] = limit; + } + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.privatePostMytrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'side': side, + 'amount': this.amountToPrecision (symbol, amount), + 'type': this.safeString (this.options['orderTypes'], type, type), + 'ocoorder': false, + 'buy_price_oco': 0, + 'sell_price_oco': 0, + }; + if (type === 'market') { + request['price'] = this.nonce ().toString (); + } else { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrderNew (this.extend (request, params)); + return this.parseOrder (response); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const order = { + 'order_id': id, + }; + if (price !== undefined) { + order['price'] = this.priceToPrecision (symbol, price); + } + if (amount !== undefined) { + order['amount'] = this.numberToString (amount); + } + if (symbol !== undefined) { + order['symbol'] = this.marketId (symbol); + } + if (side !== undefined) { + order['side'] = side; + } + if (type !== undefined) { + order['type'] = this.safeString (this.options['orderTypes'], type, type); + } + const response = await this.privatePostOrderCancelReplace (this.extend (order, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + return await this.privatePostOrderCancelAll (params); + } + + parseOrder (order, market = undefined) { + const side = this.safeString (order, 'side'); + const open = this.safeValue (order, 'is_live'); + const canceled = this.safeValue (order, 'is_cancelled'); + let status = undefined; + if (open) { + status = 'open'; + } else if (canceled) { + status = 'canceled'; + } else { + status = 'closed'; + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeStringUpper (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let orderType = order['type']; + const exchange = orderType.indexOf ('exchange ') >= 0; + if (exchange) { + const parts = order['type'].split (' '); + orderType = parts[1]; + } + let timestamp = this.safeFloat (order, 'timestamp'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp) * 1000; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'average': this.safeFloat (order, 'avg_execution_price'), + 'amount': this.safeFloat (order, 'original_amount'), + 'remaining': this.safeFloat (order, 'remaining_amount'), + 'filled': this.safeFloat (order, 'executed_amount'), + 'status': status, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (symbol !== undefined) { + if (!(symbol in this.markets)) { + throw new ExchangeError (this.id + ' has no symbol ' + symbol); + } + } + const response = await this.privatePostOrders (params); + let orders = this.parseOrders (response, undefined, since, limit); + if (symbol !== undefined) { + orders = this.filterBy (orders, 'symbol', symbol); + } + return orders; + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostOrdersHist (this.extend (request, params)); + let orders = this.parseOrders (response, undefined, since, limit); + if (symbol !== undefined) { + orders = this.filterBy (orders, 'symbol', symbol); + } + orders = this.filterByArray (orders, 'status', [ 'closed', 'canceled' ], false); + return orders; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0], + ohlcv[1], + ohlcv[3], + ohlcv[4], + ohlcv[2], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const market = this.market (symbol); + const v2id = 't' + market['id']; + const request = { + 'symbol': v2id, + 'timeframe': this.timeframes[timeframe], + 'sort': 1, + 'limit': limit, + }; + if (since !== undefined) { + request['start'] = since; + } + const response = await this.v2GetCandlesTradeTimeframeSymbolHist (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + getCurrencyName (code) { + if (code in this.options['currencyNames']) { + return this.options['currencyNames'][code]; + } + throw new NotSupported (this.id + ' ' + code + ' not supported for withdrawal'); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const request = { + 'renew': 1, + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'info': response['info'], + 'currency': code, + 'address': address, + 'tag': undefined, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const name = this.getCurrencyName (code); + const request = { + 'method': name, + 'wallet_name': 'exchange', + 'renew': 0, // a value of 1 will generate a new address + }; + const response = await this.privatePostDepositNew (this.extend (request, params)); + let address = this.safeValue (response, 'address'); + let tag = undefined; + if ('address_pool' in response) { + tag = address; + address = response['address_pool']; + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency `code` argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostHistoryMovements (this.extend (request, params)); + // + // [ + // { + // "id":581183, + // "txid": 123456, + // "currency":"BTC", + // "method":"BITCOIN", + // "type":"WITHDRAWAL", + // "amount":".01", + // "description":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ, offchain transfer ", + // "address":"3QXYWgRGX2BPYBpUDBssGbeWEa5zq6snBZ", + // "status":"COMPLETED", + // "timestamp":"1443833327.0", + // "timestamp_created": "1443833327.1", + // "fee": 0.1, + // } + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // crypto + // + // { + // "id": 12042490, + // "fee": "-0.02", + // "txid": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D", + // "type": "DEPOSIT", + // "amount": "2099.849999", + // "method": "RIPPLE", + // "status": "COMPLETED", + // "address": "2505189261", + // "currency": "XRP", + // "timestamp": "1551730524.0", + // "description": "EA5B5A66000B66855865EFF2494D7C8D1921FCBE996482157EBD749F2C85E13D", + // "timestamp_created": "1551730523.0" + // } + // + // fiat + // + // { + // "id": 12725095, + // "fee": "-60.0", + // "txid": null, + // "type": "WITHDRAWAL", + // "amount": "9943.0", + // "method": "WIRE", + // "status": "SENDING", + // "address": null, + // "currency": "EUR", + // "timestamp": "1561802484.0", + // "description": "Name: bob, AccountAddress: some address, Account: someaccountno, Bank: bank address, SWIFT: foo, Country: UK, Details of Payment: withdrawal name, Intermediary Bank Name: , Intermediary Bank Address: , Intermediary Bank City: , Intermediary Bank Country: , Intermediary Bank Account: , Intermediary Bank SWIFT: , Fee: -60.0", + // "timestamp_created": "1561716066.0" + // } + // + let timestamp = this.safeFloat (transaction, 'timestamp_created'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp * 1000); + } + let updated = this.safeFloat (transaction, 'timestamp'); + if (updated !== undefined) { + updated = parseInt (updated * 1000); + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeStringLower (transaction, 'type'); // DEPOSIT or WITHDRAWAL + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + feeCost = Math.abs (feeCost); + } + return { + 'info': transaction, + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'txid'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'address'), // todo: this is actually the tag for XRP transfers (the address is missing) + 'tag': undefined, // refix it properly for the tag from description + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': { + 'currency': code, + 'cost': feeCost, + 'rate': undefined, + }, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'SENDING': 'pending', + 'CANCELED': 'canceled', + 'ZEROCONFIRMED': 'failed', // ZEROCONFIRMED happens e.g. in a double spend attempt (I had one in my movements!) + 'COMPLETED': 'ok', + }; + return this.safeString (statuses, status, status); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + const name = this.getCurrencyName (code); + const request = { + 'withdraw_type': name, + 'walletselected': 'exchange', + 'amount': amount.toString (), + 'address': address, + }; + if (tag !== undefined) { + request['payment_id'] = tag; + } + const responses = await this.privatePostWithdraw (this.extend (request, params)); + const response = responses[0]; + const id = this.safeString (response, 'withdrawal_id'); + const message = this.safeString (response, 'message'); + const errorMessage = this.findBroadlyMatchedKey (this.exceptions['broad'], message); + if (id === 0) { + if (errorMessage !== undefined) { + const ExceptionClass = this.exceptions['broad'][errorMessage]; + throw new ExceptionClass (this.id + ' ' + message); + } + throw new ExchangeError (this.id + ' withdraw returned an id of zero: ' + this.json (response)); + } + return { + 'info': response, + 'id': id, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.implodeParams (path, params); + if (api === 'v2') { + request = '/' + api + request; + } else { + request = '/' + this.version + request; + } + let query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + request; + if ((api === 'public') || (path.indexOf ('/hist') >= 0)) { + if (Object.keys (query).length) { + const suffix = '?' + this.urlencode (query); + url += suffix; + request += suffix; + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + query = this.extend ({ + 'nonce': nonce.toString (), + 'request': request, + }, query); + body = this.json (query); + query = this.encode (body); + const payload = this.stringToBase64 (query); + const secret = this.encode (this.secret); + const signature = this.hmac (payload, secret, 'sha384'); + headers = { + 'X-BFX-APIKEY': this.apiKey, + 'X-BFX-PAYLOAD': this.decode (payload), + 'X-BFX-SIGNATURE': signature, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code >= 400) { + if (body[0] === '{') { + const feedback = this.id + ' ' + body; + const message = this.safeString2 (response, 'message', 'error'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitfinex2.js b/TaskServer/node_modules/ccxt/js/bitfinex2.js new file mode 100644 index 000000000..520ef0b8f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitfinex2.js @@ -0,0 +1,649 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const bitfinex = require ('./bitfinex.js'); +const { ExchangeError, NotSupported, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitfinex2 extends bitfinex { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitfinex2', + 'name': 'Bitfinex', + 'countries': [ 'VG' ], + 'version': 'v2', + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': true, + 'createLimitOrder': false, + 'createMarketOrder': false, + 'createOrder': false, + 'deposit': false, + 'editOrder': false, + 'fetchDepositAddress': false, + 'fetchClosedOrders': false, + 'fetchFundingFees': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': false, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchTradingFee': false, + 'fetchTradingFees': false, + 'fetchTransactions': false, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '3h': '3h', + '6h': '6h', + '12h': '12h', + '1d': '1D', + '1w': '7D', + '2w': '14D', + '1M': '1M', + }, + 'rateLimit': 1500, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg', + 'api': { + 'v1': 'https://api.bitfinex.com', + 'public': 'https://api-pub.bitfinex.com', + 'private': 'https://api.bitfinex.com', + }, + 'www': 'https://www.bitfinex.com', + 'doc': [ + 'https://docs.bitfinex.com/v2/docs/', + 'https://github.com/bitfinexcom/bitfinex-api-node', + ], + 'fees': 'https://www.bitfinex.com/fees', + }, + 'api': { + 'v1': { + 'get': [ + 'symbols', + 'symbols_details', + ], + }, + 'public': { + 'get': [ + 'conf/pub:map:currency:label', + 'platform/status', + 'tickers', + 'ticker/{symbol}', + 'trades/{symbol}/hist', + 'book/{symbol}/{precision}', + 'book/{symbol}/P0', + 'book/{symbol}/P1', + 'book/{symbol}/P2', + 'book/{symbol}/P3', + 'book/{symbol}/R0', + 'stats1/{key}:{size}:{symbol}:{side}/{section}', + 'stats1/{key}:{size}:{symbol}/{section}', + 'stats1/{key}:{size}:{symbol}:long/last', + 'stats1/{key}:{size}:{symbol}:long/hist', + 'stats1/{key}:{size}:{symbol}:short/last', + 'stats1/{key}:{size}:{symbol}:short/hist', + 'candles/trade:{timeframe}:{symbol}/{section}', + 'candles/trade:{timeframe}:{symbol}/last', + 'candles/trade:{timeframe}:{symbol}/hist', + ], + 'post': [ + 'calc/trade/avg', + 'calc/fx', + ], + }, + 'private': { + 'post': [ + 'auth/r/wallets', + 'auth/r/orders/{symbol}', + 'auth/r/orders/{symbol}/new', + 'auth/r/orders/{symbol}/hist', + 'auth/r/order/{symbol}:{id}/trades', + 'auth/w/order/submit', + 'auth/r/trades/hist', + 'auth/r/trades/{symbol}/hist', + 'auth/r/positions', + 'auth/r/positions/hist', + 'auth/r/positions/audit', + 'auth/r/funding/offers/{symbol}', + 'auth/r/funding/offers/{symbol}/hist', + 'auth/r/funding/loans/{symbol}', + 'auth/r/funding/loans/{symbol}/hist', + 'auth/r/funding/credits/{symbol}', + 'auth/r/funding/credits/{symbol}/hist', + 'auth/r/funding/trades/{symbol}/hist', + 'auth/r/info/margin/{key}', + 'auth/r/info/funding/{key}', + 'auth/r/ledgers/hist', + 'auth/r/movements/hist', + 'auth/r/movements/{currency}/hist', + 'auth/r/stats/perf:{timeframe}/hist', + 'auth/r/alerts', + 'auth/w/alert/set', + 'auth/w/alert/{type}:{symbol}:{price}/del', + 'auth/calc/order/avail', + 'auth/r/ledgers/{symbol}/hist', + 'auth/r/settings', + 'auth/w/settings/set', + 'auth/w/settings/del', + 'auth/r/info/user', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.0004, + 'BCH': 0.0001, + 'ETH': 0.00135, + 'EOS': 0.0, + 'LTC': 0.001, + 'OMG': 0.15097, + 'IOT': 0.0, + 'NEO': 0.0, + 'ETC': 0.01, + 'XRP': 0.02, + 'ETP': 0.01, + 'ZEC': 0.001, + 'BTG': 0.0, + 'DASH': 0.01, + 'XMR': 0.0001, + 'QTM': 0.01, + 'EDO': 0.23687, + 'DAT': 9.8858, + 'AVT': 1.1251, + 'SAN': 0.35977, + 'USDT': 5.0, + 'SPK': 16.971, + 'BAT': 1.1209, + 'GNT': 2.8789, + 'SNT': 9.0848, + 'QASH': 1.726, + 'YYW': 7.9464, + }, + }, + }, + 'options': { + 'precision': 'R0', // P0, P1, P2, P3, P4, R0 + 'orderTypes': { + 'MARKET': undefined, + 'EXCHANGE MARKET': 'market', + 'LIMIT': undefined, + 'EXCHANGE LIMIT': 'limit', + 'STOP': undefined, + 'EXCHANGE STOP': 'stopOrLoss', + 'TRAILING STOP': undefined, + 'EXCHANGE TRAILING STOP': undefined, + 'FOK': undefined, + 'EXCHANGE FOK': 'limit FOK', + 'STOP LIMIT': undefined, + 'EXCHANGE STOP LIMIT': 'limit stop', + 'IOC': undefined, + 'EXCHANGE IOC': 'limit ioc', + }, + 'fiat': { + 'USD': 'USD', + 'EUR': 'EUR', + 'JPY': 'JPY', + 'GBP': 'GBP', + }, + }, + }); + } + + isFiat (code) { + return (code in this.options['fiat']); + } + + getCurrencyId (code) { + return 'f' + code; + } + + async fetchMarkets (params = {}) { + const response = await this.v1GetSymbolsDetails (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + let id = this.safeString (market, 'pair'); + id = id.toUpperCase (); + let baseId = undefined; + let quoteId = undefined; + if (id.indexOf (':') >= 0) { + const parts = id.split (':'); + baseId = parts[0]; + quoteId = parts[1]; + } else { + baseId = id.slice (0, 3); + quoteId = id.slice (3, 6); + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + id = 't' + id; + baseId = this.getCurrencyId (baseId); + quoteId = this.getCurrencyId (quoteId); + const precision = { + 'price': this.safeInteger (market, 'price_precision'), + 'amount': this.safeInteger (market, 'price_precision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_order_size'), + 'max': this.safeFloat (market, 'maximum_order_size'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + }; + limits['cost'] = { + 'min': limits['amount']['min'] * limits['price']['min'], + 'max': undefined, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + 'swap': false, + 'spot': false, + 'futures': false, + }); + } + return result; + } + + async fetchBalance (params = {}) { + // this api call does not return the 'used' amount - use the v1 version instead (which also returns zero balances) + await this.loadMarkets (); + const response = await this.privatePostAuthRWallets (params); + const balanceType = this.safeString (params, 'type', 'exchange'); + const result = { 'info': response }; + for (let b = 0; b < response.length; b++) { + const balance = response[b]; + const accountType = balance[0]; + let currency = balance[1]; + const total = balance[2]; + const available = balance[4]; + if (accountType === balanceType) { + if (currency[0] === 't') { + currency = currency.slice (1); + } + const code = this.safeCurrencyCode (currency); + const account = this.account (); + // do not fill in zeroes and missing values in the parser + // rewrite and unify the following to use the unified parseBalance + account['total'] = total; + if (!available) { + if (available === 0) { + account['free'] = 0; + account['used'] = total; + } else { + account['free'] = total; + } + } else { + account['free'] = available; + account['used'] = account['total'] - account['free']; + } + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const precision = this.safeValue (this.options, 'precision', 'R0'); + const request = { + 'symbol': this.marketId (symbol), + 'precision': precision, + }; + if (limit !== undefined) { + request['len'] = limit; // 25 or 100 + } + const fullRequest = this.extend (request, params); + const orderbook = await this.publicGetBookSymbolPrecision (fullRequest); + const timestamp = this.milliseconds (); + const result = { + 'bids': [], + 'asks': [], + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + const priceIndex = (fullRequest['precision'] === 'R0') ? 1 : 0; + for (let i = 0; i < orderbook.length; i++) { + const order = orderbook[i]; + const price = order[priceIndex]; + const amount = Math.abs (order[2]); + const side = (order[2] > 0) ? 'bids' : 'asks'; + result[side].push ([ price, amount ]); + } + result['bids'] = this.sortBy (result['bids'], 0, true); + result['asks'] = this.sortBy (result['asks'], 0); + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const length = ticker.length; + const last = ticker[length - 4]; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': ticker[length - 2], + 'low': ticker[length - 1], + 'bid': ticker[length - 10], + 'bidVolume': undefined, + 'ask': ticker[length - 8], + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': ticker[length - 6], + 'percentage': ticker[length - 5] * 100, + 'average': undefined, + 'baseVolume': ticker[length - 3], + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['symbols'] = ids.join (','); + } else { + request['symbols'] = 'ALL'; + } + const tickers = await this.publicGetTickers (this.extend (request, params)); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const id = ticker[0]; + if (id in this.markets_by_id) { + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.publicGetTickerSymbol (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // [ + // ID, + // MTS, // timestamp + // AMOUNT, + // PRICE + // ] + // + // fetchMyTrades (private) + // + // [ + // ID, + // PAIR, + // MTS_CREATE, + // ORDER_ID, + // EXEC_AMOUNT, + // EXEC_PRICE, + // ORDER_TYPE, + // ORDER_PRICE, + // MAKER, + // FEE, + // FEE_CURRENCY, + // ... + // ] + // + const tradeLength = trade.length; + const isPrivate = (tradeLength > 5); + const id = trade[0].toString (); + const amountIndex = isPrivate ? 4 : 2; + let amount = trade[amountIndex]; + let cost = undefined; + const priceIndex = isPrivate ? 5 : 3; + const price = trade[priceIndex]; + let side = undefined; + let orderId = undefined; + let takerOrMaker = undefined; + let type = undefined; + let fee = undefined; + let symbol = undefined; + const timestampIndex = isPrivate ? 2 : 1; + const timestamp = trade[timestampIndex]; + if (isPrivate) { + const marketId = trade[1]; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + orderId = trade[3].toString (); + takerOrMaker = (trade[8] === 1) ? 'maker' : 'taker'; + const feeCost = trade[9]; + const feeCurrency = this.safeCurrencyCode (trade[10]); + if (feeCost !== undefined) { + fee = { + 'cost': Math.abs (feeCost), + 'currency': feeCurrency, + }; + } + const orderType = trade[6]; + type = this.safeString (this.options['orderTypes'], orderType); + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + if (amount !== undefined) { + side = (amount < 0) ? 'sell' : 'buy'; + amount = Math.abs (amount); + if (cost === undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'side': side, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let sort = '-1'; + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['start'] = since; + sort = '1'; + } + if (limit !== undefined) { + request['limit'] = limit; // default 120, max 5000 + } + request['sort'] = sort; + const response = await this.publicGetTradesSymbolHist (this.extend (request, params)); + // + // [ + // [ + // ID, + // MTS, // timestamp + // AMOUNT, + // PRICE + // ] + // ] + // + const trades = this.sortBy (response, 1); + return this.parseTrades (trades, market, undefined, limit); + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 100, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 100; // default 100, max 5000 + } + if (since === undefined) { + since = this.milliseconds () - this.parseTimeframe (timeframe) * limit * 1000; + } + const request = { + 'symbol': market['id'], + 'timeframe': this.timeframes[timeframe], + 'sort': 1, + 'start': since, + 'limit': limit, + }; + const response = await this.publicGetCandlesTradeTimeframeSymbolHist (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + throw new NotSupported (this.id + ' createOrder not implemented yet'); + } + + cancelOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' cancelOrder not implemented yet'); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrder not implemented yet'); + } + + async fetchDepositAddress (currency, params = {}) { + throw new NotSupported (this.id + ' fetchDepositAddress() not implemented yet.'); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + throw new NotSupported (this.id + ' withdraw not implemented yet'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + 'end': this.milliseconds (), + }; + if (since !== undefined) { + request['start'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default 25, max 1000 + } + let method = 'privatePostAuthRTradesHist'; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + method = 'privatePostAuthRTradesSymbolHist'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'v1') { + request = api + request; + } else { + request = this.version + request; + } + let url = this.urls['api'][api] + '/' + request; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + body = this.json (query); + const auth = '/api/' + request + nonce + body; + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha384'); + headers = { + 'bfx-nonce': nonce, + 'bfx-apikey': this.apiKey, + 'bfx-signature': signature, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (response) { + if ('message' in response) { + if (response['message'].indexOf ('not enough exchange balance') >= 0) { + throw new InsufficientFunds (this.id + ' ' + this.json (response)); + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } else if (response === '') { + throw new ExchangeError (this.id + ' returned empty response'); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitflyer.js b/TaskServer/node_modules/ccxt/js/bitflyer.js new file mode 100644 index 000000000..6d740fd3d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitflyer.js @@ -0,0 +1,487 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitflyer extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitflyer', + 'name': 'bitFlyer', + 'countries': [ 'JP' ], + 'version': 'v1', + 'rateLimit': 1000, // their nonce-timestamp is in seconds... + 'has': { + 'CORS': false, + 'withdraw': true, + 'fetchMyTrades': true, + 'fetchOrders': true, + 'fetchOrder': 'emulated', + 'fetchOpenOrders': 'emulated', + 'fetchClosedOrders': 'emulated', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28051642-56154182-660e-11e7-9b0d-6042d1e6edd8.jpg', + 'api': 'https://api.bitflyer.jp', + 'www': 'https://bitflyer.jp', + 'doc': 'https://lightning.bitflyer.com/docs?lang=en', + }, + 'api': { + 'public': { + 'get': [ + 'getmarkets/usa', // new (wip) + 'getmarkets/eu', // new (wip) + 'getmarkets', // or 'markets' + 'getboard', // ... + 'getticker', + 'getexecutions', + 'gethealth', + 'getboardstate', + 'getchats', + ], + }, + 'private': { + 'get': [ + 'getpermissions', + 'getbalance', + 'getbalancehistory', + 'getcollateral', + 'getcollateralhistory', + 'getcollateralaccounts', + 'getaddresses', + 'getcoinins', + 'getcoinouts', + 'getbankaccounts', + 'getdeposits', + 'getwithdrawals', + 'getchildorders', + 'getparentorders', + 'getparentorder', + 'getexecutions', + 'getpositions', + 'gettradingcommission', + ], + 'post': [ + 'sendcoin', + 'withdraw', + 'sendchildorder', + 'cancelchildorder', + 'sendparentorder', + 'cancelparentorder', + 'cancelallchildorders', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'BTC/JPY': { + 'maker': 0.15 / 100, + 'taker': 0.15 / 100, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const jp_markets = await this.publicGetGetmarkets (params); + const us_markets = await this.publicGetGetmarketsUsa (params); + const eu_markets = await this.publicGetGetmarketsEu (params); + let markets = this.arrayConcat (jp_markets, us_markets); + markets = this.arrayConcat (markets, eu_markets); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'product_code'); + const currencies = id.split ('_'); + let baseId = undefined; + let quoteId = undefined; + let base = undefined; + let quote = undefined; + const numCurrencies = currencies.length; + if (numCurrencies === 1) { + baseId = id.slice (0, 3); + quoteId = id.slice (3, 6); + } else if (numCurrencies === 2) { + baseId = currencies[0]; + quoteId = currencies[1]; + } else { + baseId = currencies[1]; + quoteId = currencies[2]; + } + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + const symbol = (numCurrencies === 2) ? (base + '/' + quote) : id; + const fees = this.safeValue (this.fees, symbol, this.fees['trading']); + let maker = this.safeValue (fees, 'maker', this.fees['trading']['maker']); + let taker = this.safeValue (fees, 'taker', this.fees['trading']['taker']); + let spot = true; + let future = false; + let type = 'spot'; + if (('alias' in market) || (currencies[0] === 'FX')) { + type = 'future'; + future = true; + spot = false; + maker = 0.0; + taker = 0.0; + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'maker': maker, + 'taker': taker, + 'type': type, + 'spot': spot, + 'future': future, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetGetbalance (params); + // + // [ + // { + // "currency_code": "JPY", + // "amount": 1024078, + // "available": 508000 + // }, + // { + // "currency_code": "BTC", + // "amount": 10.24, + // "available": 4.12 + // }, + // { + // "currency_code": "ETH", + // "amount": 20.48, + // "available": 16.38 + // } + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'amount'); + account['free'] = this.safeFloat (balance, 'available'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + }; + const orderbook = await this.publicGetGetboard (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'size'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + }; + const ticker = await this.publicGetGetticker (this.extend (request, params)); + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + const last = this.safeFloat (ticker, 'ltp'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'best_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'best_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume_by_product'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + let side = this.safeStringLower (trade, 'side'); + if (side !== undefined) { + if (side.length < 1) { + side = undefined; + } + } + let order = undefined; + if (side !== undefined) { + const id = side + '_child_order_acceptance_id'; + if (id in trade) { + order = trade[id]; + } + } + if (order === undefined) { + order = this.safeString (trade, 'child_order_acceptance_id'); + } + const timestamp = this.parse8601 (this.safeString (trade, 'exec_date')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const id = this.safeString (trade, 'id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': order, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_code': market['id'], + }; + const response = await this.publicGetGetexecutions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + 'child_order_type': type.toUpperCase (), + 'side': side.toUpperCase (), + 'price': price, + 'size': amount, + }; + const result = await this.privatePostSendchildorder (this.extend (request, params)); + // { "status": - 200, "error_message": "Insufficient funds", "data": null } + const id = this.safeString (result, 'child_order_acceptance_id'); + return { + 'info': result, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder() requires a `symbol` argument'); + } + await this.loadMarkets (); + const request = { + 'product_code': this.marketId (symbol), + 'child_order_acceptance_id': id, + }; + return await this.privatePostCancelchildorder (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'ACTIVE': 'open', + 'COMPLETED': 'closed', + 'CANCELED': 'canceled', + 'EXPIRED': 'canceled', + 'REJECTED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const timestamp = this.parse8601 (this.safeString (order, 'child_order_date')); + const amount = this.safeFloat (order, 'size'); + const remaining = this.safeFloat (order, 'outstanding_size'); + const filled = this.safeFloat (order, 'executed_size'); + const price = this.safeFloat (order, 'price'); + const cost = price * filled; + const status = this.parseOrderStatus (this.safeString (order, 'child_order_state')); + const type = this.safeStringLower (order, 'child_order_type'); + const side = this.safeStringLower (order, 'side'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'product_code'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCost = this.safeFloat (order, 'total_commission'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': undefined, + 'rate': undefined, + }; + } + const id = this.safeString (order, 'child_order_acceptance_id'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_code': market['id'], + 'count': limit, + }; + const response = await this.privateGetGetchildorders (this.extend (request, params)); + let orders = this.parseOrders (response, market, since, limit); + if (symbol !== undefined) { + orders = this.filterBy (orders, 'symbol', symbol); + } + return orders; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { + const request = { + 'child_order_state': 'ACTIVE', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = 100, params = {}) { + const request = { + 'child_order_state': 'COMPLETED', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a `symbol` argument'); + } + const orders = await this.fetchOrders (symbol); + const ordersById = this.indexBy (orders, 'id'); + if (id in ordersById) { + return ordersById[id]; + } + throw new OrderNotFound (this.id + ' No order found with id ' + id); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_code': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privateGetGetexecutions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + if (code !== 'JPY' && code !== 'USD' && code !== 'EUR') { + throw new ExchangeError (this.id + ' allows withdrawing JPY, USD, EUR only, ' + code + ' is not supported'); + } + const currency = this.currency (code); + const request = { + 'currency_code': currency['id'], + 'amount': amount, + // 'bank_account_id': 1234, + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + const id = this.safeString (response, 'message_id'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.version + '/'; + if (api === 'private') { + request += 'me/'; + } + request += path; + if (method === 'GET') { + if (Object.keys (params).length) { + request += '?' + this.urlencode (params); + } + } + const url = this.urls['api'] + request; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = [ nonce, method, request ].join (''); + if (Object.keys (params).length) { + if (method !== 'GET') { + body = this.json (params); + auth += body; + } + } + headers = { + 'ACCESS-KEY': this.apiKey, + 'ACCESS-TIMESTAMP': nonce, + 'ACCESS-SIGN': this.hmac (this.encode (auth), this.encode (this.secret)), + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitforex.js b/TaskServer/node_modules/ccxt/js/bitforex.js new file mode 100644 index 000000000..a3c1724d7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitforex.js @@ -0,0 +1,594 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, OrderNotFound, InsufficientFunds, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitforex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitforex', + 'name': 'Bitforex', + 'countries': [ 'CN' ], + 'version': 'v1', + 'has': { + 'fetchBalance': true, + 'fetchMarkets': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchMyTrades': false, + 'fetchTrades': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOHLCV': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + '1M': '1month', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/44310033-69e9e600-a3d8-11e8-873d-54d74d1bc4e4.jpg', + 'api': 'https://api.bitforex.com', + 'www': 'https://www.bitforex.com', + 'doc': 'https://github.com/githubdev2020/API_Doc_en/wiki', + 'fees': 'https://help.bitforex.com/en_us/?cat=13', + 'referral': 'https://www.bitforex.com/en/invitationRegister?inviterId=1867438', + }, + 'api': { + 'public': { + 'get': [ + 'api/v1/market/symbols', + 'api/v1/market/ticker', + 'api/v1/market/depth', + 'api/v1/market/trades', + 'api/v1/market/kline', + ], + }, + 'private': { + 'post': [ + 'api/v1/fund/mainAccount', + 'api/v1/fund/allAccount', + 'api/v1/trade/placeOrder', + 'api/v1/trade/placeMultiOrder', + 'api/v1/trade/cancelOrder', + 'api/v1/trade/orderInfo', + 'api/v1/trade/orderInfos', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': true, + 'deposit': {}, + 'withdraw': { + 'BTC': 0.0005, + 'ETH': 0.01, + 'BCH': 0.0001, + 'LTC': 0.001, + 'ETC': 0.005, + 'USDT': 5, + 'CMCT': 30, + 'AION': 3, + 'LVT': 0, + 'DATA': 40, + 'RHP': 50, + 'NEO': 0, + 'AIDOC': 10, + 'BQT': 2, + 'R': 2, + 'DPY': 0.8, + 'GTC': 40, + 'AGI': 30, + 'DENT': 100, + 'SAN': 1, + 'SPANK': 8, + 'AID': 5, + 'OMG': 0.1, + 'BFT': 5, + 'SHOW': 150, + 'TRX': 20, + 'ABYSS': 10, + 'THM': 25, + 'ZIL': 20, + 'PPT': 0.2, + 'WTC': 0.4, + 'LRC': 7, + 'BNT': 1, + 'CTXC': 1, + 'MITH': 20, + 'TRUE': 4, + 'LYM': 10, + 'VEE': 100, + 'AUTO': 200, + 'REN': 50, + 'TIO': 2.5, + 'NGC': 1.5, + 'PST': 10, + 'CRE': 200, + 'IPC': 5, + 'PTT': 1000, + 'XMCT': 20, + 'ATMI': 40, + 'TERN': 40, + 'XLM': 0.01, + 'ODE': 15, + 'FTM': 100, + 'RTE': 100, + 'DCC': 100, + 'IMT': 500, + 'GOT': 3, + 'EGT': 500, + 'DACC': 1000, + 'UBEX': 500, + 'ABL': 100, + 'OLT': 100, + 'DAV': 40, + 'THRT': 10, + 'RMESH': 3, + 'UPP': 20, + 'SDT': 0, + 'SHR': 10, + 'MTV': 3, + 'ESS': 100, + 'MET': 3, + 'TTC': 20, + 'LXT': 10, + 'XCLP': 100, + 'LUK': 100, + 'UBC': 100, + 'DTX': 10, + 'BEAT': 20, + 'DEED': 2, + 'BGX': 3000, + 'PRL': 20, + 'ELY': 50, + 'CARD': 300, + 'SQR': 15, + 'VRA': 400, + 'BWX': 3500, + 'MAS': 75, + 'FLP': 0.6, + 'UNC': 300, + 'CRNC': 15, + 'MFG': 70, + 'ZXC': 70, + 'TRT': 30, + 'ZIX': 35, + 'XRA': 10, + 'AMO': 1600, + 'IPG': 3, + 'uDoo': 50, + 'URB': 30, + 'ARCONA': 3, + 'CRAD': 5, + 'NOBS': 1000, + 'ADF': 2, + 'ELF': 5, + 'LX': 20, + 'PATH': 15, + 'SILK': 120, + 'SKYFT': 50, + 'EDN': 50, + 'ADE': 50, + 'EDR': 10, + 'TIME': 0.25, + 'SPRK': 20, + 'QTUM': 0.01, + 'BF': 5, + 'ZPR': 100, + 'HYB': 10, + 'CAN': 30, + 'CEL': 10, + 'ATS': 50, + 'KCASH': 1, + 'ACT': 0.01, + 'MT': 300, + 'DXT': 30, + 'WAB': 4000, + 'HYDRO': 400, + 'LQD': 5, + 'OPTC': 200, + 'EQUAD': 80, + 'LATX': 50, + 'LEDU': 100, + 'RIT': 70, + 'ACDC': 500, + 'FSN': 2, + }, + }, + }, + 'exceptions': { + '4004': OrderNotFound, + '1013': AuthenticationError, + '1016': AuthenticationError, + '3002': InsufficientFunds, + '10204': DDoSProtection, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetApiV1MarketSymbols (params); + const data = response['data']; + const result = []; + for (let i = 0; i < data.length; i++) { + const market = data[i]; + const id = this.safeString (market, 'symbol'); + const symbolParts = id.split ('-'); + const baseId = symbolParts[2]; + const quoteId = symbolParts[1]; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = true; + const precision = { + 'amount': this.safeInteger (market, 'amountPrecision'), + 'price': this.safeInteger (market, 'pricePrecision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minOrderAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'time'); + const id = this.safeString (trade, 'tid'); + const orderId = undefined; + const amount = this.safeFloat (trade, 'amount'); + const price = this.safeFloat (trade, 'price'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const sideId = this.safeInteger (trade, 'direction'); + const side = this.parseSide (sideId); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'order': orderId, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['size'] = limit; + } + const market = this.market (symbol); + const response = await this.publicGetApiV1MarketTrades (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostApiV1FundAllAccount (params); + const data = response['data']; + const result = { 'info': response }; + for (let i = 0; i < data.length; i++) { + const balance = data[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'frozen'); + account['free'] = this.safeFloat (balance, 'active'); + account['total'] = this.safeFloat (balance, 'fix'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.markets[symbol]; + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetApiV1MarketTicker (this.extend (request, params)); + const data = response['data']; + const timestamp = this.safeInteger (data, 'date'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (data, 'high'), + 'low': this.safeFloat (data, 'low'), + 'bid': this.safeFloat (data, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (data, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': this.safeFloat (data, 'last'), + 'last': this.safeFloat (data, 'last'), + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (data, 'vol'), + 'quoteVolume': undefined, + 'info': response, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'vol'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'ktype': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['size'] = limit; // default 1, max 600 + } + const response = await this.publicGetApiV1MarketKline (this.extend (request, params)); + const ohlcvs = this.safeValue (response, 'data', []); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'symbol': marketId, + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetApiV1MarketDepth (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (response, 'time'); + return this.parseOrderBook (data, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'canceled', + '4': 'canceled', + }; + return (status in statuses) ? statuses[status] : status; + } + + parseSide (sideId) { + if (sideId === 1) { + return 'buy'; + } else if (sideId === 2) { + return 'sell'; + } else { + return undefined; + } + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'orderId'); + const timestamp = this.safeFloat (order, 'createTime'); + const lastTradeTimestamp = this.safeFloat (order, 'lastTime'); + const symbol = market['symbol']; + const sideId = this.safeInteger (order, 'tradeType'); + const side = this.parseSide (sideId); + const type = undefined; + const price = this.safeFloat (order, 'orderPrice'); + const average = this.safeFloat (order, 'avgPrice'); + const amount = this.safeFloat (order, 'orderAmount'); + const filled = this.safeFloat (order, 'dealAmount'); + const remaining = amount - filled; + const status = this.parseOrderStatus (this.safeString (order, 'orderState')); + const cost = filled * price; + const feeSide = (side === 'buy') ? 'base' : 'quote'; + const feeCurrency = market[feeSide]; + const fee = { + 'cost': this.safeFloat (order, 'tradeFee'), + 'currency': feeCurrency, + }; + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': this.marketId (symbol), + 'orderId': id, + }; + const response = await this.privatePostApiV1TradeOrderInfo (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + return order; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': this.marketId (symbol), + 'state': 0, + }; + const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': this.marketId (symbol), + 'state': 1, + }; + const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let sideId = undefined; + if (side === 'buy') { + sideId = 1; + } else if (side === 'sell') { + sideId = 2; + } + const request = { + 'symbol': this.marketId (symbol), + 'price': price, + 'amount': amount, + 'tradeType': sideId, + }; + const response = await this.privatePostApiV1TradePlaceOrder (this.extend (request, params)); + const data = response['data']; + return { + 'info': response, + 'id': this.safeString (data, 'orderId'), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + if (symbol !== undefined) { + request['symbol'] = this.marketId (symbol); + } + const results = await this.privatePostApiV1TradeCancelOrder (this.extend (request, params)); + const success = results['success']; + const returnVal = { 'info': results, 'success': success }; + return returnVal; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + let payload = this.urlencode ({ 'accessKey': this.apiKey }); + query['nonce'] = this.milliseconds (); + if (Object.keys (query).length) { + payload += '&' + this.urlencode (this.keysort (query)); + } + // let message = '/' + 'api/' + this.version + '/' + path + '?' + payload; + const message = '/' + path + '?' + payload; + const signature = this.hmac (this.encode (message), this.encode (this.secret)); + body = payload + '&signData=' + signature; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (typeof body !== 'string') { + return; // fallback to default error handler + } + if ((body[0] === '{') || (body[0] === '[')) { + const feedback = this.id + ' ' + body; + const success = this.safeValue (response, 'success'); + if (success !== undefined) { + if (!success) { + const code = this.safeString (response, 'code'); + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bithumb.js b/TaskServer/node_modules/ccxt/js/bithumb.js new file mode 100644 index 000000000..7a37629b4 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bithumb.js @@ -0,0 +1,449 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, AuthenticationError, BadRequest, PermissionDenied, InvalidAddress } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bithumb extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bithumb', + 'name': 'Bithumb', + 'countries': [ 'KR' ], // South Korea + 'rateLimit': 500, + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30597177-ea800172-9d5e-11e7-804c-b9d4fa9b56b0.jpg', + 'api': { + 'public': 'https://api.bithumb.com/public', + 'private': 'https://api.bithumb.com', + }, + 'www': 'https://www.bithumb.com', + 'doc': 'https://apidocs.bithumb.com', + 'fees': 'https://en.bithumb.com/customer_support/info_fee', + }, + 'api': { + 'public': { + 'get': [ + 'ticker/{currency}', + 'ticker/all', + 'orderbook/{currency}', + 'orderbook/all', + 'transaction_history/{currency}', + 'transaction_history/all', + ], + }, + 'private': { + 'post': [ + 'info/account', + 'info/balance', + 'info/wallet_address', + 'info/ticker', + 'info/orders', + 'info/user_transactions', + 'trade/place', + 'info/order_detail', + 'trade/cancel', + 'trade/btc_withdrawal', + 'trade/krw_deposit', + 'trade/krw_withdrawal', + 'trade/market_buy', + 'trade/market_sell', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.25 / 100, + 'taker': 0.25 / 100, + }, + }, + 'exceptions': { + 'Bad Request(SSL)': BadRequest, + 'Bad Request(Bad Method)': BadRequest, + 'Bad Request.(Auth Data)': AuthenticationError, // { "status": "5100", "message": "Bad Request.(Auth Data)" } + 'Not Member': AuthenticationError, + 'Invalid Apikey': AuthenticationError, // {"status":"5300","message":"Invalid Apikey"} + 'Method Not Allowed.(Access IP)': PermissionDenied, + 'Method Not Allowed.(BTC Adress)': InvalidAddress, + 'Method Not Allowed.(Access)': PermissionDenied, + 'Database Fail': ExchangeNotAvailable, + 'Invalid Parameter': BadRequest, + '5600': ExchangeError, + 'Unknown Error': ExchangeError, + 'After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions': ExchangeError, // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"} + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTickerAll (params); + const data = this.safeValue (response, 'data'); + const currencyIds = Object.keys (data); + const result = []; + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + if (currencyId === 'date') { + continue; + } + const market = data[currencyId]; + const base = currencyId; + const quote = 'KRW'; + const symbol = currencyId + '/' + quote; + let active = true; + if (Array.isArray (market)) { + const numElements = market.length; + if (numElements === 0) { + active = false; + } + } + result.push ({ + 'id': currencyId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'info': market, + 'active': active, + 'precision': { + 'amount': undefined, + 'price': undefined, + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'currency': 'ALL', + }; + const response = await this.privatePostInfoBalance (this.extend (request, params)); + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const account = this.account (); + const currency = this.currency (code); + const lowerCurrencyId = this.safeStringLower (currency, 'id'); + account['total'] = this.safeFloat (balances, 'total_' + lowerCurrencyId); + account['used'] = this.safeFloat (balances, 'in_use_' + lowerCurrencyId); + account['free'] = this.safeFloat (balances, 'available_' + lowerCurrencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['base'], + }; + if (limit !== undefined) { + request['count'] = limit; // max = 50 + } + const response = await this.publicGetOrderbookCurrency (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'quantity'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const open = this.safeFloat (ticker, 'opening_price'); + const close = this.safeFloat (ticker, 'closing_price'); + let change = undefined; + let percentage = undefined; + let average = undefined; + if ((close !== undefined) && (open !== undefined)) { + change = close - open; + if (open > 0) { + percentage = change / open * 100; + } + average = this.sum (open, close) / 2; + } + const baseVolume = this.safeFloat (ticker, 'units_traded_24H'); + const quoteVolume = this.safeFloat (ticker, 'acc_trade_value_24H'); + let vwap = undefined; + if (quoteVolume !== undefined && baseVolume !== undefined) { + vwap = quoteVolume / baseVolume; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'max_price'), + 'low': this.safeFloat (ticker, 'min_price'), + 'bid': this.safeFloat (ticker, 'buy_price'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell_price'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': open, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickerAll (params); + const result = {}; + const timestamp = this.safeInteger (response['data'], 'date'); + const tickers = this.omit (response['data'], 'date'); + const ids = Object.keys (tickers); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + const ticker = tickers[id]; + const isArray = Array.isArray (ticker); + if (!isArray) { + ticker['date'] = timestamp; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['base'], + }; + const response = await this.publicGetTickerCurrency (this.extend (request, params)); + return this.parseTicker (response['data'], market); + } + + parseTrade (trade, market = undefined) { + // a workaround for their bug in date format, hours are not 0-padded + const parts = trade['transaction_date'].split (' '); + const transaction_date = parts[0]; + let transaction_time = parts[1]; + if (transaction_time.length < 8) { + transaction_time = '0' + transaction_time; + } + let timestamp = this.parse8601 (transaction_date + ' ' + transaction_time); + timestamp -= 9 * 3600000; // they report UTC + 9 hours (server in Korean timezone) + const type = undefined; + let side = this.safeString (trade, 'type'); + side = (side === 'ask') ? 'sell' : 'buy'; + const id = this.safeString (trade, 'cont_no'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'units_traded'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['base'], + }; + if (limit === undefined) { + request['count'] = limit; // default 20, max 100 + } + const response = await this.publicGetTransactionHistoryCurrency (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let request = undefined; + let method = 'privatePostTrade'; + if (type === 'limit') { + request = { + 'order_currency': market['id'], + 'Payment_currency': market['quote'], + 'units': amount, + 'price': price, + 'type': (side === 'buy') ? 'bid' : 'ask', + }; + method += 'Place'; + } else if (type === 'market') { + request = { + 'currency': market['id'], + 'units': amount, + }; + method += 'Market' + this.capitalize (side); + } + const response = await this[method] (this.extend (request, params)); + const id = this.safeString (response, 'order_id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const side_in_params = ('side' in params); + if (!side_in_params) { + throw new ExchangeError (this.id + ' cancelOrder requires a `side` parameter (sell or buy) and a `currency` parameter'); + } + const currency = this.safeString (params, 'currency'); + if (currency === undefined) { + throw new ExchangeError (this.id + ' cancelOrder requires a `currency` parameter (a currency id)'); + } + const side = (params['side'] === 'buy') ? 'bid' : 'ask'; + params = this.omit (params, [ 'side', 'currency' ]); + const request = { + 'order_id': id, + 'type': side, + 'currency': currency, + }; + return await this.privatePostTradeCancel (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'units': amount, + 'address': address, + 'currency': currency['id'], + }; + if (currency === 'XRP' || currency === 'XMR') { + const destination = this.safeString (params, 'destination'); + if ((tag === undefined) && (destination === undefined)) { + throw new ExchangeError (this.id + ' ' + code + ' withdraw() requires a tag argument or an extra destination param'); + } else if (tag !== undefined) { + request['destination'] = tag; + } + } + const response = await this.privatePostTradeBtcWithdrawal (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const endpoint = '/' + this.implodeParams (path, params); + let url = this.urls['api'][api] + endpoint; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'endpoint': endpoint, + }, query)); + const nonce = this.nonce ().toString (); + const auth = endpoint + "\0" + body + "\0" + nonce; // eslint-disable-line quotes + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); + const signature64 = this.decode (this.stringToBase64 (this.encode (signature))); + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Api-Key': this.apiKey, + 'Api-Sign': signature64.toString (), + 'Api-Nonce': nonce, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('status' in response) { + // + // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"} + // + const status = this.safeString (response, 'status'); + const message = this.safeString (response, 'message'); + if (status !== undefined) { + if (status === '0000') { + return; // no error + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, status, feedback); + this.throwExactlyMatchedException (this.exceptions, message, feedback); + throw new ExchangeError (feedback); + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('status' in response) { + if (response['status'] === '0000') { + return response; + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitkk.js b/TaskServer/node_modules/ccxt/js/bitkk.js new file mode 100644 index 000000000..055657b41 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitkk.js @@ -0,0 +1,26 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const zb = require ('./zb.js'); + +// --------------------------------------------------------------------------- + +module.exports = class bitkk extends zb { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitkk', + 'name': 'bitkk', + 'comment': 'a Chinese ZB clone', + 'urls': { + 'api': { + 'public': 'http://api.bitkk.com/data', // no https for public API + 'private': 'https://trade.bitkk.com/api', + }, + 'www': 'https://www.bitkk.com', + 'doc': 'https://www.bitkk.com/i/developer', + 'fees': 'https://www.bitkk.com/i/rate', + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitlish.js b/TaskServer/node_modules/ccxt/js/bitlish.js new file mode 100644 index 000000000..e45e529fc --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitlish.js @@ -0,0 +1,378 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitlish extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitlish', + 'name': 'Bitlish', + 'countries': [ 'GB', 'EU', 'RU' ], + 'rateLimit': 1500, + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'withdraw': true, + }, + 'timeframes': { + '1h': 3600, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766275-dcfc6c30-5ed3-11e7-839d-00a846385d0b.jpg', + 'api': 'https://bitlish.com/api', + 'www': 'https://bitlish.com', + 'doc': 'https://bitlish.com/api', + 'fees': 'https://bitlish.com/fees', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.3 / 100, // anonymous 0.3%, verified 0.2% + 'maker': 0.2 / 100, // anonymous 0.2%, verified 0.1% + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'LTC': 0.001, + 'DOGE': 0.001, + 'ETH': 0.001, + 'XMR': 0, + 'ZEC': 0.001, + 'DASH': 0.0001, + 'EUR': 50, + }, + 'deposit': { + 'BTC': 0, + 'LTC': 0, + 'DOGE': 0, + 'ETH': 0, + 'XMR': 0, + 'ZEC': 0, + 'DASH': 0, + 'EUR': 0, + }, + }, + }, + 'api': { + 'public': { + 'get': [ + 'instruments', + 'ohlcv', + 'pairs', + 'tickers', + 'trades_depth', + 'trades_history', + ], + 'post': [ + 'instruments', + 'ohlcv', + 'pairs', + 'tickers', + 'trades_depth', + 'trades_history', + ], + }, + 'private': { + 'post': [ + 'accounts_operations', + 'balance', + 'cancel_trade', + 'cancel_trades_by_ids', + 'cancel_all_trades', + 'create_bcode', + 'create_template_wallet', + 'create_trade', + 'deposit', + 'list_accounts_operations_from_ts', + 'list_active_trades', + 'list_bcodes', + 'list_my_matches_from_ts', + 'list_my_trades', + 'list_my_trads_from_ts', + 'list_payment_methods', + 'list_payments', + 'redeem_code', + 'resign', + 'signin', + 'signout', + 'trade_details', + 'trade_options', + 'withdraw', + 'withdraw_by_id', + ], + }, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + 'XDG': 'DOGE', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetPairs (params); + const result = []; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = response[key]; + const id = this.safeString (market, 'id'); + const name = this.safeString (market, 'name'); + const [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'first'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'prc') * 100, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'sum'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTickers (params); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.safeValue (this.markets_by_id, id); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTickers (params); + const marketId = market['id']; + return this.parseTicker (response[marketId], market); + } + + async fetchOHLCV (symbol, timeframe = '1h', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // let market = this.market (symbol); + const now = this.seconds (); + let start = now - 86400 * 30; // last 30 days + if (since !== undefined) { + start = parseInt (since / 1000); + } + const interval = [ start.toString (), undefined ]; + const request = { + 'time_range': interval, + }; + return await this.publicPostOhlcv (this.extend (request, params)); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_id': this.marketId (symbol), + }; + const response = await this.publicGetTradesDepth (this.extend (request, params)); + let timestamp = undefined; + const last = this.safeInteger (response, 'last'); + if (last !== undefined) { + timestamp = parseInt (last / 1000); + } + return this.parseOrderBook (response, timestamp, 'bid', 'ask', 'price', 'volume'); + } + + parseTrade (trade, market = undefined) { + const side = (trade['dir'] === 'bid') ? 'buy' : 'sell'; + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = this.safeInteger (trade, 'created'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp / 1000); + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTradesHistory (this.extend ({ + 'pair_id': market['id'], + }, params)); + return this.parseTrades (response['list'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const currencyIds = Object.keys (response); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + const balance = this.safeValue (response, currencyId, {}); + account['free'] = this.safeFloat (balance, 'funds'); + account['used'] = this.safeFloat (balance, 'holded'); + result[code] = account; + } + return this.parseBalance (result); + } + + async signIn (params = {}) { + const request = { + 'login': this.login, + 'passwd': this.password, + }; + return await this.privatePostSignin (this.extend (request, params)); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_id': this.marketId (symbol), + 'dir': (side === 'buy') ? 'bid' : 'ask', + 'amount': amount, + }; + if (type === 'limit') { + request['price'] = price; + } + const response = await this.privatePostCreateTrade (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelTrade (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + if (code !== 'BTC') { + // they did not document other types... + throw new NotSupported (this.id + ' currently supports BTC withdrawals only, until they document other currencies...'); + } + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'account': address, + 'payment_method': 'bitcoin', // they did not document other types... + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': response['message_id'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (method === 'GET') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + body = this.json (params); + headers = { 'Content-Type': 'application/json' }; + } + } else { + this.checkRequiredCredentials (); + body = this.json (this.extend ({ 'token': this.apiKey }, params)); + headers = { 'Content-Type': 'application/json' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitmart.js b/TaskServer/node_modules/ccxt/js/bitmart.js new file mode 100644 index 000000000..3773ece77 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitmart.js @@ -0,0 +1,923 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ArgumentsRequired, ExchangeError, InvalidOrder, BadRequest, OrderNotFound, DDoSProtection, BadSymbol } = require ('./base/errors'); +// --------------------------------------------------------------------------- + +module.exports = class bitmart extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitmart', + 'name': 'BitMart', + 'countries': [ 'US', 'CN', 'HK', 'KR' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': true, + 'fetchMarkets': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchCurrencies': true, + 'fetchOrderBook': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchBalance': true, + 'createOrder': true, + 'cancelOrder': true, + 'cancelAllOrders': true, + 'fetchOrders': false, + 'fetchOrderTrades': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchCanceledOrders': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg', + 'api': 'https://openapi.bitmart.com', + 'www': 'https://www.bitmart.com/', + 'doc': 'https://github.com/bitmartexchange/bitmart-official-api-docs', + 'referral': 'http://www.bitmart.com/?r=rQCFLh', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'token': { + 'post': [ + 'authentication', + ], + }, + 'public': { + 'get': [ + 'currencies', + 'ping', + 'steps', + 'symbols', + 'symbols_details', + 'symbols/{symbol}/kline', + 'symbols/{symbol}/orders', + 'symbols/{symbol}/trades', + 'ticker', + 'time', + ], + }, + 'private': { + 'get': [ + 'orders', + 'orders/{id}', + 'trades', + 'wallet', + ], + 'post': [ + 'orders', + ], + 'delete': [ + 'orders', + 'orders/{id}', + ], + }, + }, + 'timeframes': { + '1m': 1, + '3m': 3, + '5m': 5, + '15m': 15, + '30m': 30, + '45m': 45, + '1h': 60, + '2h': 120, + '3h': 180, + '4h': 240, + '1d': 1440, + '1w': 10080, + '1M': 43200, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.002, + 'maker': 0.001, + 'tiers': { + 'taker': [ + [0, 0.20 / 100], + [10, 0.18 / 100], + [50, 0.16 / 100], + [250, 0.14 / 100], + [1000, 0.12 / 100], + [5000, 0.10 / 100], + [25000, 0.08 / 100], + [50000, 0.06 / 100], + ], + 'maker': [ + [0, 0.1 / 100], + [10, 0.09 / 100], + [50, 0.08 / 100], + [250, 0.07 / 100], + [1000, 0.06 / 100], + [5000, 0.05 / 100], + [25000, 0.04 / 100], + [50000, 0.03 / 100], + ], + }, + }, + }, + 'exceptions': { + 'exact': { + 'Place order error': InvalidOrder, // {"message":"Place order error"} + 'Not found': OrderNotFound, // {"message":"Not found"} + 'Visit too often, please try again later': DDoSProtection, // {"code":-30,"msg":"Visit too often, please try again later","subMsg":"","data":{}} + 'Unknown symbol': BadSymbol, // {"message":"Unknown symbol"} + }, + 'broad': { + 'Invalid limit. limit must be in the range': InvalidOrder, + 'Maximum price is': InvalidOrder, // {"message":"Maximum price is 0.112695"} + // {"message":"Required Integer parameter 'status' is not present"} + // {"message":"Required String parameter 'symbol' is not present"} + // {"message":"Required Integer parameter 'offset' is not present"} + // {"message":"Required Integer parameter 'limit' is not present"} + // {"message":"Required Long parameter 'from' is not present"} + // {"message":"Required Long parameter 'to' is not present"} + 'is not present': BadRequest, + }, + }, + 'commonCurrencies': { + 'ONE': 'Menlo One', + }, + }); + } + + async fetchTime (params = {}) { + const response = await this.publicGetTime (params); + // + // { + // "server_time": 1527777538000 + // } + // + return this.safeInteger (response, 'server_time'); + } + + async signIn (params = {}) { + const message = this.apiKey + ':' + this.secret + ':' + this.uid; + const data = { + 'grant_type': 'client_credentials', + 'client_id': this.apiKey, + 'client_secret': this.hmac (this.encode (message), this.encode (this.secret), 'sha256'), + }; + const response = await this.tokenPostAuthentication (this.extend (data, params)); + const accessToken = this.safeString (response, 'access_token'); + if (!accessToken) { + throw new AuthenticationError (this.id + ' signIn() failed to authenticate. Access token missing from response.'); + } + const expiresIn = this.safeInteger (response, 'expires_in'); + this.options['expires'] = this.sum (this.nonce (), expiresIn * 1000); + this.options['accessToken'] = accessToken; + return response; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetSymbolsDetails (params); + // + // [ + // { + // "id":"1SG_BTC", + // "base_currency":"1SG", + // "quote_currency":"BTC", + // "quote_increment":"0.1", + // "base_min_size":"0.1000000000", + // "base_max_size":"10000000.0000000000", + // "price_min_precision":4, + // "price_max_precision":6, + // "expiration":"NA" + // } + // ] + // + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + // + // https://github.com/bitmartexchange/bitmart-official-api-docs/blob/master/rest/public/symbols_details.md#response-details + // from the above API doc: + // quote_increment Minimum order price as well as the price increment + // price_min_precision Minimum price precision (digit) used to query price and kline + // price_max_precision Maximum price precision (digit) used to query price and kline + // + // the docs are wrong: https://github.com/ccxt/ccxt/issues/5612 + // + const quoteIncrement = this.safeString (market, 'quote_increment'); + const amountPrecision = this.precisionFromString (quoteIncrement); + const pricePrecision = this.safeInteger (market, 'price_max_precision'); + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'base_min_size'), + 'max': this.safeFloat (market, 'base_max_size'), + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + const marketId = this.safeString (ticker, 'symbol_id'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const last = this.safeFloat (ticker, 'current_price'); + const percentage = this.safeFloat (ticker, 'fluctuation'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highest_price'), + 'low': this.safeFloat (ticker, 'lowest_price'), + 'bid': this.safeFloat (ticker, 'bid_1'), + 'bidVolume': this.safeFloat (ticker, 'bid_1_amount'), + 'ask': this.safeFloat (ticker, 'ask_1'), + 'askVolume': this.safeFloat (ticker, 'ask_1_amount'), + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': percentage * 100, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'base_volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + // + // { + // "volume":"97487.38", + // "ask_1":"0.00148668", + // "base_volume":"144.59", + // "lowest_price":"0.00144362", + // "bid_1":"0.00148017", + // "highest_price":"0.00151000", + // "ask_1_amount":"92.03", + // "current_price":"0.00148230", + // "fluctuation":"+0.0227", + // "symbol_id":"XRP_ETH", + // "url":"https://www.bitmart.com/trade?symbol=XRP_ETH", + // "bid_1_amount":"134.78" + // } + // + return this.parseTicker (response); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTicker (params); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchCurrencies (params = {}) { + const currencies = await this.publicGetCurrencies (params); + // + // [ + // { + // "name":"CNY1", + // "withdraw_enabled":false, + // "id":"CNY1", + // "deposit_enabled":false + // } + // ] + // + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const currencyId = this.safeString (currency, 'id'); + const code = this.safeCurrencyCode (currencyId); + const name = this.safeString (currency, 'name'); + const withdrawEnabled = this.safeValue (currency, 'withdraw_enabled'); + const depositEnabled = this.safeValue (currency, 'deposit_enabled'); + const active = withdrawEnabled && depositEnabled; + result[code] = { + 'id': currencyId, + 'code': code, + 'name': name, + 'info': currency, // the original payload + 'active': active, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'withdraw': { 'min': undefined, 'max': undefined }, + }, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + // 'precision': 4, // optional price precision / depth level whose range is defined in symbol details + }; + const response = await this.publicGetSymbolsSymbolOrders (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'buys', 'sells', 'price', 'amount'); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "amount":"2.29275119", + // "price":"0.021858", + // "count":"104.8930", + // "order_time":1563997286061, + // "type":"sell" + // } + // + // fetchMyTrades (private) + // + // { + // active: true, + // amount: '0.2000', + // entrustType: 1, + // entrust_id: 979648824, + // fees: '0.0000085532', + // price: '0.021383', + // symbol: 'ETH_BTC', + // timestamp: 1574343514000, + // trade_id: 329418828 + // }, + // + const id = this.safeString (trade, 'trade_id'); + const timestamp = this.safeInteger2 (trade, 'timestamp', 'order_time'); + const type = undefined; + let side = this.safeStringLower (trade, 'type'); + if ((side === undefined) && ('entrustType' in trade)) { + side = trade['entrustType'] ? 'sell' : 'buy'; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const orderId = this.safeInteger (trade, 'entrust_id'); + const marketId = this.safeString (trade, 'symbol'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const feeCost = this.safeFloat (trade, 'fees'); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrencyCode = undefined; + if (market !== undefined) { + feeCurrencyCode = (side === 'buy') ? market['base'] : market['quote']; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': undefined, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetSymbolsSymbolTrades (this.extend (request, params)); + // + // [ + // { + // "amount":"2.29275119", + // "price":"0.021858", + // "count":"104.8930", + // "order_time":1563997286061, + // "type":"sell" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // limit is required, must be in the range (0, 50) + const maxLimit = 50; + limit = (limit === undefined) ? maxLimit : Math.min (limit, maxLimit); + const request = { + 'symbol': market['id'], + 'offset': 0, // current page, starts from 0 + 'limit': limit, // required + }; + const response = await this.privateGetTrades (this.extend (request, params)); + // + // { + // "total_trades": 216, + // "total_pages": 22, + // "current_page": 0, + // "trades": [ + // { + // "symbol": "BMX_ETH", + // "amount": "1.0", + // "fees": "0.0005000000", + // "trade_id": 2734956, + // "price": "0.00013737", + // "active": true, + // "entrust_id": 5576623, + // "timestamp": 1545292334000 + // }, + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'entrust_id': id, + }; + return await this.fetchMyTrades (symbol, since, limit, this.extend (request, params)); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeInteger (ohlcv, 'timestamp'), + this.safeFloat (ohlcv, 'open_price'), + this.safeFloat (ohlcv, 'highest_price'), + this.safeFloat (ohlcv, 'lowest_price'), + this.safeFloat (ohlcv, 'current_price'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + if (since === undefined && limit === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOHLCV requires either a `since` argument or a `limit` argument (or both)'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const periodInSeconds = this.parseTimeframe (timeframe); + const duration = periodInSeconds * limit * 1000; + let to = this.milliseconds (); + if (since === undefined) { + since = to - duration; + } else { + to = this.sum (since, duration); + } + const request = { + 'symbol': market['id'], + 'from': since, // start time of k-line data (in milliseconds, required) + 'to': to, // end time of k-line data (in milliseconds, required) + 'step': this.timeframes[timeframe], // steps of sampling (in minutes, default 1 minute, optional) + }; + const response = await this.publicGetSymbolsSymbolKline (this.extend (request, params)); + // + // [ + // { + // "timestamp":1525761000000, + // "open_price":"0.010130", + // "highest_price":"0.010130", + // "lowest_price":"0.010130", + // "current_price":"0.010130", + // "volume":"0.000000" + // } + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetWallet (params); + // + // [ + // { + // "name":"Bitcoin", + // "available":"0.0000000000", + // "frozen":"0.0000000000", + // "id":"BTC" + // } + // ] + // + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'id'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'frozen'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "entrust_id":1223181 + // } + // + // cancelOrder + // + // {} + // + // fetchOrder, fetchOrdersByStatus, fetchOpenOrders, fetchClosedOrders + // + // { + // "entrust_id":1223181, + // "symbol":"BMX_ETH", + // "timestamp":1528060666000, + // "side":"buy", + // "price":"1.000000", + // "fees":"0.1", + // "original_amount":"1", + // "executed_amount":"1", + // "remaining_amount":"0", + // "status":3 + // } + // + const id = this.safeString (order, 'entrust_id'); + const timestamp = this.milliseconds (); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'original_amount'); + let cost = undefined; + let filled = this.safeFloat (order, 'executed_amount'); + let remaining = this.safeFloat (order, 'remaining_amount'); + if (amount !== undefined) { + if (remaining !== undefined) { + if (filled === undefined) { + filled = amount - remaining; + } + } + if (filled !== undefined) { + if (remaining === undefined) { + remaining = amount - filled; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } + } + } + const side = this.safeString (order, 'side'); + const type = undefined; + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'all', + '1': 'open', + '2': 'open', + '3': 'closed', + '4': 'canceled', + '5': 'open', + '6': 'closed', + }; + return this.safeString (statuses, status, status); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'side': side.toLowerCase (), + 'amount': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "entrust_id":1223181 + // } + // + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const intId = parseInt (id); + const request = { + 'id': intId, + 'entrust_id': intId, + }; + const response = await this.privateDeleteOrdersId (this.extend (request, params)); + // + // responds with an empty object {} + // + return this.parseOrder (response); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); + } + const side = this.safeString (params, 'side'); + if (side === undefined) { + throw new ArgumentsRequired (this.id + " cancelAllOrders requires a `side` parameter ('buy' or 'sell')"); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'side': side, // 'buy' or 'sell' + }; + const response = await this.privateDeleteOrders (this.extend (request, params)); + // + // responds with an empty object {} + // + return response; + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrdersByStatus requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 500; // default 500, max 1000 + } + const request = { + 'symbol': market['id'], + 'status': status, + 'offset': 0, // current page, starts from 0 + 'limit': limit, + }; + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "orders":[ + // { + // "entrust_id":1223181, + // "symbol":"BMX_ETH", + // "timestamp":1528060666000, + // "side":"buy", + // "price":"1.000000", + // "fees":"0.1", + // "original_amount":"1", + // "executed_amount":"1", + // "remaining_amount":"0", + // "status":3 + // } + // ], + // "total_pages":1, + // "total_orders":1, + // "current_page":0, + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // 5 = pending & partially filled orders + return await this.fetchOrdersByStatus (5, symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // 3 = closed orders + return await this.fetchOrdersByStatus (3, symbol, since, limit, params); + } + + async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // 4 = canceled orders + return await this.fetchOrdersByStatus (4, symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + // + // { + // "entrust_id":1223181, + // "symbol":"BMX_ETH", + // "timestamp":1528060666000, + // "side":"buy", + // "price":"1.000000", + // "fees":"0.1", + // "original_amount":"1", + // "executed_amount":"1", + // "remaining_amount":"0", + // "status":3 + // } + // + return this.parseOrder (response); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'token') { + this.checkRequiredCredentials (); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } else { + const nonce = this.nonce (); + this.checkRequiredCredentials (); + const token = this.safeString (this.options, 'accessToken'); + if (token === undefined) { + throw new AuthenticationError (this.id + ' ' + path + ' endpoint requires an accessToken option or a prior call to signIn() method'); + } + const expires = this.safeInteger (this.options, 'expires'); + if (expires !== undefined) { + if (nonce >= expires) { + throw new AuthenticationError (this.id + ' accessToken expired, supply a new accessToken or call the signIn() method'); + } + } + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + headers = { + 'Content-Type': 'application/json', + 'X-BM-TIMESTAMP': nonce.toString (), + 'X-BM-AUTHORIZATION': 'Bearer ' + token, + }; + if (method !== 'GET') { + query = this.keysort (query); + body = this.json (query); + const message = this.urlencode (query); + headers['X-BM-SIGNATURE'] = this.hmac (this.encode (message), this.encode (this.secret), 'sha256'); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // + // {"message":"Maximum price is 0.112695"} + // {"message":"Required Integer parameter 'status' is not present"} + // {"message":"Required String parameter 'symbol' is not present"} + // {"message":"Required Integer parameter 'offset' is not present"} + // {"message":"Required Integer parameter 'limit' is not present"} + // {"message":"Required Long parameter 'from' is not present"} + // {"message":"Required Long parameter 'to' is not present"} + // {"message":"Invalid status. status=6 not support any more, please use 3:deal_success orders, 4:cancelled orders"} + // {"message":"Not found"} + // {"message":"Place order error"} + // + const feedback = this.id + ' ' + body; + const message = this.safeString2 (response, 'message', 'msg'); + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitmax.js b/TaskServer/node_modules/ccxt/js/bitmax.js new file mode 100644 index 000000000..ec9754c07 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitmax.js @@ -0,0 +1,1181 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, AuthenticationError, ExchangeError, InsufficientFunds, InvalidOrder, BadSymbol } = require ('./base/errors'); +const { ROUND } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bitmax extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitmax', + 'name': 'BitMax', + 'countries': [ 'CN' ], // China + 'rateLimit': 500, + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': false, + 'fetchAccounts': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchMyTrades': false, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchOrderTrades': true, + 'fetchClosedOrders': true, + 'fetchTransactions': false, + 'fetchCurrencies': true, + 'cancelAllOrders': true, + 'fetchDepositAddress': true, + }, + 'timeframes': { + '1m': '1', + '3m': '3', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '2h': '120', + '4h': '240', + '6h': '360', + '12h': '720', + '1d': '1d', + '1w': '1w', + '1M': '1m', + }, + 'version': 'v1', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/66820319-19710880-ef49-11e9-8fbe-16be62a11992.jpg', + 'api': 'https://bitmax.io', + 'test': 'https://bitmax-test.io/api', + 'www': 'https://bitmax.io', + 'doc': [ + 'https://github.com/bitmax-exchange/api-doc/blob/master/bitmax-api-doc-v1.2.md', + ], + 'fees': 'https://bitmax.io/#/feeRate/tradeRate', + 'referral': 'https://bitmax.io/#/register?inviteCode=EL6BXBQM', + }, + 'api': { + 'public': { + 'get': [ + 'assets', + 'depth', + 'fees', + 'quote', + 'depth', + 'trades', + 'products', + 'ticker/24hr', + 'barhist', + 'barhist/info', + 'margin/ref-price', + ], + }, + 'private': { + 'get': [ + 'deposit', + 'user/info', + 'balance', + 'order/batch', + 'order/open', + 'order', + 'order/history', + 'order/{coid}', + 'order/fills/{coid}', + 'transaction', + 'margin/balance', + 'margin/order/open', + 'margin/order', + ], + 'post': [ + 'margin/order', + 'order', + 'order/batch', + ], + 'delete': [ + 'margin/order', + 'order', + 'order/all', + 'order/batch', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + }, + 'options': { + 'accountGroup': undefined, + 'parseOrderToPrecision': false, + }, + 'exceptions': { + 'exact': { + '2100': AuthenticationError, // {"code":2100,"message":"ApiKeyFailure"} + '5002': BadSymbol, // {"code":5002,"message":"Invalid Symbol"} + '6010': InsufficientFunds, // {'code': 6010, 'message': 'Not enough balance.'} + '60060': InvalidOrder, // { 'code': 60060, 'message': 'The order is already filled or canceled.' } + '600503': InvalidOrder, // {"code":600503,"message":"Notional is too small."} + }, + 'broad': {}, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetAssets (params); + // + // [ + // { + // "assetCode" : "LTO", + // "assetName" : "LTO", + // "precisionScale" : 9, + // "nativeScale" : 3, + // "withdrawalFee" : 5.0, + // "minWithdrawalAmt" : 10.0, + // "status" : "Normal" + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'assetCode'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'precisionScale'); + const fee = this.safeFloat (currency, 'withdrawalFee'); // todo: redesign + const status = this.safeString (currency, 'status'); + const active = (status === 'Normal'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': this.safeString (currency, 'assetName'), + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawalAmt'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetProducts (params); + // + // [ + // { + // "symbol" : "BCH/USDT", + // "domain" : "USDS", + // "baseAsset" : "BCH", + // "quoteAsset" : "USDT", + // "priceScale" : 2, + // "qtyScale" : 3, + // "notionalScale" : 9, + // "minQty" : "0.000000001", + // "maxQty" : "1000000000", + // "minNotional" : "5", + // "maxNotional" : "200000", + // "status" : "Normal", + // "miningStatus" : "", + // "marginTradable" : true, + // "commissionType" : "Quote", + // "commissionReserveRate" : 0.0010000000 + // }, + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseAsset'); + const quoteId = this.safeString (market, 'quoteAsset'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'qtyScale'), + 'price': this.safeInteger (market, 'notionalScale'), + }; + const status = this.safeString (market, 'status'); + const active = (status === 'Normal'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minQty'), + 'max': this.safeFloat (market, 'maxQty'), + }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { + 'min': this.safeFloat (market, 'minNotional'), + 'max': this.safeFloat (market, 'maxNotional'), + }, + }, + }); + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let precision = market['precision']['price']; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + precision = market['precision']['amount']; + } + cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (cost), + }; + } + + async fetchAccounts (params = {}) { + let accountGroup = this.safeString (this.options, 'accountGroup'); + let response = undefined; + if (accountGroup === undefined) { + response = await this.privateGetUserInfo (params); + // + // { + // "accountGroup": 5 + // } + // + accountGroup = this.safeString (response, 'accountGroup'); + } + return [ + { + 'id': accountGroup, + 'type': undefined, + 'currency': undefined, + 'info': response, + }, + ]; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const response = await this.privateGetBalance (params); + // + // { + // "code": 0, + // "status": "success", // this field will be deprecated soon + // "email": "foo@bar.com", // this field will be deprecated soon + // "data": [ + // { + // "assetCode": "TSC", + // "assetName": "Ethereum", + // "totalAmount": "20.03", // total balance amount + // "availableAmount": "20.03", // balance amount available to trade + // "inOrderAmount": "0.000", // in order amount + // "btcValue": "70.81" // the current BTC value of the balance, may be missing + // }, + // ] + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const code = this.safeCurrencyCode (this.safeString (balance, 'assetCode')); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableAmount'); + account['used'] = this.safeFloat (balance, 'inOrderAmount'); + account['total'] = this.safeFloat (balance, 'totalAmount'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['n'] = limit; // default = maximum = 100 + } + const response = await this.publicGetDepth (this.extend (request, params)); + // + // { + // "m":"depth", + // "ts":1570866464777, + // "seqnum":5124140078, + // "s":"ETH/USDT", + // "asks":[ + // ["183.57","5.92"], + // ["183.6","10.185"] + // ], + // "bids":[ + // ["183.54","0.16"], + // ["183.53","10.8"], + // ] + // } + // + const timestamp = this.safeInteger (response, 'ts'); + const result = this.parseOrderBook (response, timestamp); + result['nonce'] = this.safeInteger (response, 'seqnum'); + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "symbol":"BCH/USDT", + // "interval":"1d", + // "barStartTime":1570866600000, + // "openPrice":"225.16", + // "closePrice":"224.05", + // "highPrice":"226.08", + // "lowPrice":"218.92", + // "volume":"8607.036" + // } + // + const timestamp = this.safeInteger (ticker, 'barStartTime'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'closePrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highPrice'), + 'low': this.safeFloat (ticker, 'lowPrice'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'openPrice'), + 'close': last, + 'last': last, + 'previousClose': undefined, // previous day close + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTicker24hr (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker24hr (params); + return this.parseTickers (response, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "m":"bar", + // "s":"ETH/BTC", + // "ba":"ETH", + // "qa":"BTC", + // "i":"1", + // "t":1570867020000, + // "o":"0.022023", + // "c":"0.022018", + // "h":"0.022023", + // "l":"0.022018", + // "v":"2.510", + // } + // + return [ + this.safeInteger (ohlcv, 't'), + this.safeFloat (ohlcv, 'o'), + this.safeFloat (ohlcv, 'h'), + this.safeFloat (ohlcv, 'l'), + this.safeFloat (ohlcv, 'c'), + this.safeFloat (ohlcv, 'v'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'interval': this.timeframes[timeframe], + }; + // if since and limit are not specified + // the exchange will return just 1 last candle by default + const duration = this.parseTimeframe (timeframe); + if (since !== undefined) { + request['from'] = since; + if (limit !== undefined) { + request['to'] = this.sum (since, limit * duration * 1000, 1); + } + } else if (limit !== undefined) { + request['to'] = this.milliseconds (); + request['from'] = request['to'] - limit * duration * 1000 - 1; + } + const response = await this.publicGetBarhist (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // public fetchTrades + // + // { + // "p": "13.75", // price + // "q": "6.68", // quantity + // "t": 1528988084944, // timestamp + // "bm": False, // if true, the buyer is the market maker, we only use this field to "define the side" of a public trade + // } + // + // private fetchOrderTrades + // + // { + // "ap": "0.029062965", // average filled price + // "bb": "36851.981", // base asset total balance + // "bc": "0", // if possitive, this is the BTMX commission charged by reverse mining, if negative, this is the mining output of the current fill. + // "bpb": "36851.981", // base asset pending balance + // "btmxBal": "0.0", // optional, the BTMX balance of the current account. This field is only available when bc is non-zero. + // "cat": "CASH", // account category: CASH/MARGIN + // "coid": "41g6wtPRFrJXgg6YxjqI6Qoog139Dmoi", // client order id, (needed to cancel order) + // "ei": "NULL_VAL", // execution instruction + // "errorCode": "NULL_VAL", // if the order is rejected, this field explains why + // "execId": "12562285", // for each user, this is a strictly increasing long integer (represented as string) + // "f": "78.074", // filled quantity, this is the aggregated quantity executed by all past fills + // "fa": "BTC", // fee asset + // "fee": "0.000693608", // fee + // 'lp': "0.029064", // last price, the price executed by the last fill + // "l": "11.932", // last quantity, the quantity executed by the last fill + // "m": "order", // message type + // "orderType": "Limit", // Limit, Market, StopLimit, StopMarket + // "p": "0.029066", // limit price, only available for limit and stop limit orders + // "q": "100.000", // order quantity + // "qb": "98878.642957097", // quote asset total balance + // "qpb": "98877.967247508", // quote asset pending balance + // "s": "ETH/BTC", // symbol + // "side": "Buy", // side + // "status": "PartiallyFilled", // order status + // "t": 1561131458389, // timestamp + // } + // + const timestamp = this.safeInteger (trade, 't'); + const price = this.safeFloat (trade, 'p'); + const amount = this.safeFloat (trade, 'q'); + let cost = undefined; + if ((price !== undefined) && (amount !== undefined)) { + cost = price * amount; + } + const buyerIsMaker = this.safeValue (trade, 'bm'); + let symbol = undefined; + const marketId = this.safeString (trade, 's'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = market.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fa'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const orderId = this.safeString (trade, 'coid'); + let side = this.safeStringLower (trade, 'side'); + if ((side === undefined) && (buyerIsMaker !== undefined)) { + side = buyerIsMaker ? 'buy' : 'sell'; + } + const type = this.safeStringLower (trade, 'orderType'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': undefined, + 'order': orderId, + 'type': type, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['n'] = limit; // currently limited to 100 or fewer + } + const response = await this.publicGetTrades (this.extend (request, params)); + // + // { + // "m": "marketTrades", // message type + // "s": "ETH/BTC", // symbol + // "trades": [ + // { + // "p": "13.75", // price + // "q": "6.68", // quantity + // "t": 1528988084944, // timestamp + // "bm": False, // if true, the buyer is the market maker + // }, + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'PendingNew': 'open', + 'New': 'open', + 'PartiallyFilled': 'open', + 'Filled': 'closed', + 'Canceled': 'canceled', + 'Rejected': 'rejected', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "coid": "xxx...xxx", + // "action": "new", + // "success": true // success = true means the order has been submitted to the matching engine. + // } + // + // fetchOrder, fetchOpenOrders, fetchClosedOrders + // + // { + // "accountCategory": "CASH", + // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", + // "avgPrice": "0.000000000", + // "baseAsset": "ETH", + // "btmxCommission": "0.000000000", + // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order + // "errorCode": "NULL_VAL", + // "execId": "12452288", + // "execInst": "NULL_VAL", + // "fee": "0.000000000", // cumulative fee paid for this order + // "feeAsset": "", // the asset + // "filledQty": "0.000000000", // filled quantity + // "notional": "0.000000000", + // "orderPrice": "0.310000000", // only available for limit and stop limit orders + // "orderQty": "1.000000000", + // "orderType": "StopLimit", + // "quoteAsset": "BTC", + // "side": "Buy", + // "status": "PendingNew", + // "stopPrice": "0.300000000", // only available for stop market and stop limit orders + // "symbol": "ETH/BTC", + // "time": 1566091628227, // The last execution time of the order + // "sendingTime": 1566091503547, // The sending time of the order + // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" + // } + // + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const marketId = this.safeString (order, 'symbol'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'sendingTime'); + let price = this.safeFloat (order, 'orderPrice'); + const amount = this.safeFloat (order, 'orderQty'); + const filled = this.safeFloat (order, 'filledQty'); + let remaining = undefined; + let cost = this.safeFloat (order, 'cummulativeQuoteQty'); + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + if (this.options['parseOrderToPrecision']) { + remaining = parseFloat (this.amountToPrecision (symbol, remaining)); + } + remaining = Math.max (remaining, 0.0); + } + if (price !== undefined) { + if (cost === undefined) { + cost = price * filled; + } + } + } + const id = this.safeString (order, 'coid'); + let type = this.safeString (order, 'orderType'); + if (type !== undefined) { + type = type.toLowerCase (); + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + } + } + } + const side = this.safeStringLower (order, 'side'); + const fee = { + 'cost': this.safeFloat (order, 'fee'), + 'currency': this.safeString (order, 'feeAsset'), + }; + const average = this.safeFloat (order, 'avgPrice'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': undefined, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const market = this.market (symbol); + const request = { + 'coid': this.coid (), // a unique identifier of length 32 + // 'time': this.milliseconds (), // milliseconds since UNIX epoch in UTC, this is filled in the private section of the sign() method below + 'symbol': market['id'], + // 'orderPrice': this.priceToPrecision (symbol, price), // optional, limit price of the order. This field is required for limit orders and stop limit orders + // 'stopPrice': '15.7', // optional, stopPrice of the order. This field is required for stop_market orders and stop limit orders + 'orderQty': this.amountToPrecision (symbol, amount), + 'orderType': type, // order type, you shall specify one of the following: "limit", "market", "stop_market", "stop_limit" + 'side': side, // "buy" or "sell" + // 'postOnly': true, // optional, if true, the order will either be posted to the limit order book or be cancelled, i.e. the order cannot take liquidity, default is false + // 'timeInForce': 'GTC', // optional, supports "GTC" good-till-canceled and "IOC" immediate-or-cancel + }; + if ((type === 'limit') || (type === 'stop_limit')) { + request['orderPrice'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "email": "foo@bar.com", // this field will be deprecated soon + // "status": "success", // this field will be deprecated soon + // "data": { + // "coid": "xxx...xxx", + // "action": "new", + // "success": true, // success = true means the order has been submitted to the matching engine + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'coid': id, + }; + const response = await this.privateGetOrderCoid (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // "data": { + // "accountCategory": "CASH", + // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", + // "avgPrice": "0.000000000", + // "baseAsset": "ETH", + // "btmxCommission": "0.000000000", + // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order + // "errorCode": "NULL_VAL", + // "execId": "12452288", + // "execInst": "NULL_VAL", + // "fee": "0.000000000", // cumulative fee paid for this order + // "feeAsset": "", // the asset + // "filledQty": "0.000000000", // filled quantity + // "notional": "0.000000000", + // "orderPrice": "0.310000000", // only available for limit and stop limit orders + // "orderQty": "1.000000000", + // "orderType": "StopLimit", + // "quoteAsset": "BTC", + // "side": "Buy", + // "status": "PendingNew", + // "stopPrice": "0.300000000", // only available for stop market and stop limit orders + // "symbol": "ETH/BTC", + // "time": 1566091628227, // The last execution time of the order + // "sendingTime": 1566091503547, // The sending time of the order + // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'coid': id, + }; + const response = await this.privateGetOrderFillsCoid (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // "data": [ + // { + // "ap": "0.029062965", // average filled price + // "bb": "36851.981", // base asset total balance + // "bc": "0", // if possitive, this is the BTMX commission charged by reverse mining, if negative, this is the mining output of the current fill. + // "bpb": "36851.981", // base asset pending balance + // "btmxBal": "0.0", // optional, the BTMX balance of the current account. This field is only available when bc is non-zero. + // "cat": "CASH", // account category: CASH/MARGIN + // "coid": "41g6wtPRFrJXgg6YxjqI6Qoog139Dmoi", // client order id, (needed to cancel order) + // "ei": "NULL_VAL", // execution instruction + // "errorCode": "NULL_VAL", // if the order is rejected, this field explains why + // "execId": "12562285", // for each user, this is a strictly increasing long integer (represented as string) + // "f": "78.074", // filled quantity, this is the aggregated quantity executed by all past fills + // "fa": "BTC", // fee asset + // "fee": "0.000693608", // fee + // 'lp': "0.029064", // last price, the price executed by the last fill + // "l": "11.932", // last quantity, the quantity executed by the last fill + // "m": "order", // message type + // "orderType": "Limit", // Limit, Market, StopLimit, StopMarket + // "p": "0.029066", // limit price, only available for limit and stop limit orders + // "q": "100.000", // order quantity + // "qb": "98878.642957097", // quote asset total balance + // "qpb": "98877.967247508", // quote asset pending balance + // "s": "ETH/BTC", // symbol + // "side": "Buy", // side + // "status": "PartiallyFilled", // order status + // "t": 1561131458389, // timestamp + // }, + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseTrades (data, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + const request = { + // 'side': 'buy', // or 'sell', optional + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privateGetOrderOpen (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // "data": [ + // { + // "accountCategory": "CASH", + // "accountId": "cshKAhmTHQNUKhR1pQyrDOdotE3Tsnz4", + // "avgPrice": "0.000000000", + // "baseAsset": "ETH", + // "btmxCommission": "0.000000000", + // "coid": "41g6wtPRFrJXgg6Y7vpIkcCyWhgcK0cF", // the unique identifier, you will need, this value to cancel this order + // "errorCode": "NULL_VAL", + // "execId": "12452288", + // "execInst": "NULL_VAL", + // "fee": "0.000000000", // cumulative fee paid for this order + // "feeAsset": "", // the asset + // "filledQty": "0.000000000", // filled quantity + // "notional": "0.000000000", + // "orderPrice": "0.310000000", // only available for limit and stop limit orders + // "orderQty": "1.000000000", + // "orderType": "StopLimit", + // "quoteAsset": "BTC", + // "side": "Buy", + // "status": "PendingNew", + // "stopPrice": "0.300000000", // only available for stop market and stop limit orders + // "symbol": "ETH/BTC", + // "time": 1566091628227, // The last execution time of the order + // "sendingTime": 1566091503547, // The sending time of the order + // "userId": "supEQeSJQllKkxYSgLOoVk7hJAX59WSz" + // }, + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let market = undefined; + const request = { + // 'symbol': 'ETH/BTC', // optional + // 'category': 'CASH', // optional, string + // 'orderType': 'Market', // optional, string + // 'page': 1, // optional, integer type, starts at 1 + // 'pageSize': 100, // optional, integer type + // 'side': 'buy', // or 'sell', optional, case insensitive. + // 'startTime': 1566091628227, // optional, integer milliseconds since UNIX epoch representing the start of the range + // 'endTime': 1566091628227, // optional, integer milliseconds since UNIX epoch representing the end of the range + // 'status': 'Filled', // optional, can only be one of "Filled", "Canceled", "Rejected" + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + if (limit !== undefined) { + request['n'] = limit; // default 15, max 50 + } + const response = await this.privateGetOrderHistory (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // 'data': { + // 'page': 1, + // 'pageSize': 20, + // 'limit': 500, + // 'hasNext': False, + // 'data': [ + // { + // 'time': 1566091429000, // The last execution time of the order (This timestamp is in second level resolution) + // 'coid': 'QgQIMJhPFrYfUf60ZTihmseTqhzzwOCx', + // 'execId': '331', + // 'symbol': 'BTMX/USDT', + // 'orderType': 'Market', + // 'baseAsset': 'BTMX', + // 'quoteAsset': 'USDT', + // 'side': 'Buy', + // 'stopPrice': '0.000000000', // only meaningful for stop market and stop limit orders + // 'orderPrice': '0.123000000', // only meaningful for limit and stop limit orders + // 'orderQty': '9229.409000000', + // 'filledQty': '9229.409000000', + // 'avgPrice': '0.095500000', + // 'fee': '0.352563424', + // 'feeAsset': 'USDT', + // 'btmxCommission': '0.000000000', + // 'status': 'Filled', + // 'notional': '881.408559500', + // 'userId': '5DNEppWy33SayHjFQpgQUTjwNMSjEhD3', + // 'accountId': 'ACPHERRWRIA3VQADMEAB2ZTLYAXNM3PJ', + // 'accountCategory': 'CASH', + // 'errorCode': 'NULL_VAL', + // 'execInst': 'NULL_VAL', + // "sendingTime": 1566091382736, // The sending time of the order + // }, + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const orders = this.safeValue (data, 'data', []); + return this.parseOrders (orders, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + await this.loadAccounts (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'coid': this.coid (), + 'origCoid': id, + // 'time': this.milliseconds (), // this is filled in the private section of the sign() method below + }; + const response = await this.privateDeleteOrder (this.extend (request, params)); + // + // { + // 'code': 0, + // 'status': 'success', // this field will be deprecated soon + // 'email': 'foo@bar.com', // this field will be deprecated soon + // 'data': { + // 'action': 'cancel', + // 'coid': 'gaSRTi3o3Yo4PaXpVK0NSLP47vmJuLea', + // 'success': True, + // } + // } + // + const order = this.safeValue (response, 'data', {}); + return this.parseOrder (order); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const request = { + // 'side': 'buy', // optional string field (case-insensitive), either "buy" or "sell" + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; // optional + } + const response = await this.privateDeleteOrderAll (this.extend (request, params)); + // + // ? + // + return response; + } + + coid () { + const uuid = this.uuid (); + const parts = uuid.split ('-'); + const clientOrderId = parts.join (''); + const coid = clientOrderId.slice (0, 32); + return coid; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const currency = this.currency (code); + const request = { + 'requestId': this.coid (), + // 'time': this.milliseconds (), // this is filled in the private section of the sign() method below + 'assetCode': currency['id'], + }; + // note: it is highly recommended to use V2 version of this route, + // especially for assets with multiple block chains such as USDT. + const response = await this.privateGetDeposit (this.extend (request, params)); + // + // v1 + // + // { + // "data": { + // "address": "0x26a3CB49578F07000575405a57888681249c35Fd" + // }, + // "email": "igor.kroitor@gmial.com", + // "status": "success", + // } + // + // v2 + // + // { + // "code": 0, + // "data": [ + // { + // "asset": "XRP", + // "blockChain": "Ripple", + // "addressData": { + // "address": "rpinhtY4p35bPmVXPbfWRUtZ1w1K1gYShB", + // "destTag": "54301" + // } + // } + // ], + // "email": "xxx@xxx.com", + // "status": "success" // the request has been submitted to the server + // } + // + let addressData = this.safeValue (response, 'data'); + if (Array.isArray (addressData)) { + const firstElement = this.safeValue (addressData, 0, {}); + addressData = this.safeValue (firstElement, 'addressData', {}); + } + const address = this.safeString (addressData, 'address'); + const tag = this.safeString (addressData, 'destTag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/api/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + let accountGroup = this.safeString (this.options, 'accountGroup'); + if (accountGroup === undefined) { + if (this.accounts !== undefined) { + accountGroup = this.accounts[0]['id']; + } + } + if (accountGroup !== undefined) { + url = '/' + accountGroup + url; + } + const coid = this.safeString (query, 'coid'); + query['time'] = this.milliseconds ().toString (); + let auth = query['time'] + '+' + path.replace ('/{coid}', ''); // fix sign error + headers = { + 'x-auth-key': this.apiKey, + 'x-auth-timestamp': query['time'], + 'Content-Type': 'application/json', + }; + if (coid !== undefined) { + auth += '+' + coid; + headers['x-auth-coid'] = coid; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); + headers['x-auth-signature'] = signature; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + } + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"code":2100,"message":"ApiKeyFailure"} + // {'code': 6010, 'message': 'Not enough balance.'} + // {'code': 60060, 'message': 'The order is already filled or canceled.'} + // + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + const error = (code !== undefined) && (code !== '0'); + if (error || (message !== undefined)) { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitmex.js b/TaskServer/node_modules/ccxt/js/bitmex.js new file mode 100644 index 000000000..43aed90cd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitmex.js @@ -0,0 +1,1317 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { TICK_SIZE } = require ('./base/functions/number'); +const { AuthenticationError, BadRequest, DDoSProtection, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitmex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitmex', + 'name': 'BitMEX', + 'countries': [ 'SC' ], // Seychelles + 'version': 'v1', + 'userAgent': undefined, + 'rateLimit': 2000, + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'withdraw': true, + 'editOrder': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchLedger': true, + 'fetchTransactions': 'emulated', + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '1h': '1h', + '1d': '1d', + }, + 'urls': { + 'test': { + 'public': 'https://testnet.bitmex.com', + 'private': 'https://testnet.bitmex.com', + }, + 'logo': 'https://user-images.githubusercontent.com/1294454/27766319-f653c6e6-5ed4-11e7-933d-f0bc3699ae8f.jpg', + 'api': { + 'public': 'https://www.bitmex.com', + 'private': 'https://www.bitmex.com', + }, + 'www': 'https://www.bitmex.com', + 'doc': [ + 'https://www.bitmex.com/app/apiOverview', + 'https://github.com/BitMEX/api-connectors/tree/master/official-http', + ], + 'fees': 'https://www.bitmex.com/app/fees', + 'referral': 'https://www.bitmex.com/register/rm3C16', + }, + 'api': { + 'public': { + 'get': [ + 'announcement', + 'announcement/urgent', + 'funding', + 'instrument', + 'instrument/active', + 'instrument/activeAndIndices', + 'instrument/activeIntervals', + 'instrument/compositeIndex', + 'instrument/indices', + 'insurance', + 'leaderboard', + 'liquidation', + 'orderBook', + 'orderBook/L2', + 'quote', + 'quote/bucketed', + 'schema', + 'schema/websocketHelp', + 'settlement', + 'stats', + 'stats/history', + 'trade', + 'trade/bucketed', + ], + }, + 'private': { + 'get': [ + 'apiKey', + 'chat', + 'chat/channels', + 'chat/connected', + 'execution', + 'execution/tradeHistory', + 'notification', + 'order', + 'position', + 'user', + 'user/affiliateStatus', + 'user/checkReferralCode', + 'user/commission', + 'user/depositAddress', + 'user/margin', + 'user/minWithdrawalFee', + 'user/wallet', + 'user/walletHistory', + 'user/walletSummary', + ], + 'post': [ + 'apiKey', + 'apiKey/disable', + 'apiKey/enable', + 'chat', + 'order', + 'order/bulk', + 'order/cancelAllAfter', + 'order/closePosition', + 'position/isolate', + 'position/leverage', + 'position/riskLimit', + 'position/transferMargin', + 'user/cancelWithdrawal', + 'user/confirmEmail', + 'user/confirmEnableTFA', + 'user/confirmWithdrawal', + 'user/disableTFA', + 'user/logout', + 'user/logoutAll', + 'user/preferences', + 'user/requestEnableTFA', + 'user/requestWithdrawal', + ], + 'put': [ + 'order', + 'order/bulk', + 'user', + ], + 'delete': [ + 'apiKey', + 'order', + 'order/all', + ], + }, + }, + 'exceptions': { + 'exact': { + 'Invalid API Key.': AuthenticationError, + 'This key is disabled.': PermissionDenied, + 'Access Denied': PermissionDenied, + 'Duplicate clOrdID': InvalidOrder, + 'orderQty is invalid': InvalidOrder, + 'Invalid price': InvalidOrder, + 'Invalid stopPx for ordType': InvalidOrder, + }, + 'broad': { + 'Signature not valid': AuthenticationError, + 'overloaded': ExchangeNotAvailable, + 'Account has insufficient Available Balance': InsufficientFunds, + 'Service unavailable': ExchangeNotAvailable, // {"error":{"message":"Service unavailable","name":"HTTPError"}} + }, + }, + 'precisionMode': TICK_SIZE, + 'options': { + // https://blog.bitmex.com/api_announcement/deprecation-of-api-nonce-header/ + // https://github.com/ccxt/ccxt/issues/4789 + 'api-expires': 5, // in seconds + 'fetchOHLCVOpenTimestamp': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInstrumentActiveAndIndices (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const active = (market['state'] !== 'Unlisted'); + const id = market['symbol']; + const baseId = market['underlying']; + const quoteId = market['quoteCurrency']; + const basequote = baseId + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const swap = (id === basequote); + // 'positionCurrency' may be empty ("", as Bitmex currently returns for ETHUSD) + // so let's take the quote currency first and then adjust if needed + const positionId = this.safeString2 (market, 'positionCurrency', 'quoteCurrency'); + let type = undefined; + let future = false; + let prediction = false; + const position = this.safeCurrencyCode (positionId); + let symbol = id; + if (swap) { + type = 'swap'; + symbol = base + '/' + quote; + } else if (id.indexOf ('B_') >= 0) { + prediction = true; + type = 'prediction'; + } else { + future = true; + type = 'future'; + } + const precision = { + 'amount': undefined, + 'price': undefined, + }; + const lotSize = this.safeFloat (market, 'lotSize'); + const tickSize = this.safeFloat (market, 'tickSize'); + if (lotSize !== undefined) { + precision['amount'] = lotSize; + } + if (tickSize !== undefined) { + precision['price'] = tickSize; + } + const limits = { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': tickSize, + 'max': this.safeFloat (market, 'maxPrice'), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + const limitField = (position === quote) ? 'cost' : 'amount'; + limits[limitField] = { + 'min': lotSize, + 'max': this.safeFloat (market, 'maxOrderQty'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'taker': this.safeFloat (market, 'takerFee'), + 'maker': this.safeFloat (market, 'makerFee'), + 'type': type, + 'spot': false, + 'swap': swap, + 'future': future, + 'prediction': prediction, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'currency': 'all', + }; + const response = await this.privateGetUserMargin (this.extend (request, params)); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableMargin'); + account['total'] = this.safeFloat (balance, 'marginBalance'); + if (code === 'BTC') { + account['free'] = account['free'] * 0.00000001; + account['total'] = account['total'] * 0.00000001; + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['depth'] = limit; + } + const response = await this.publicGetOrderBookL2 (this.extend (request, params)); + const result = { + 'bids': [], + 'asks': [], + 'timestamp': undefined, + 'datetime': undefined, + 'nonce': undefined, + }; + for (let i = 0; i < response.length; i++) { + const order = response[i]; + const side = (order['side'] === 'Sell') ? 'asks' : 'bids'; + const amount = this.safeFloat (order, 'size'); + const price = this.safeFloat (order, 'price'); + // https://github.com/ccxt/ccxt/issues/4926 + // https://github.com/ccxt/ccxt/issues/4927 + // the exchange sometimes returns null price in the orderbook + if (price !== undefined) { + result[side].push ([ price, amount ]); + } + } + result['bids'] = this.sortBy (result['bids'], 0, true); + result['asks'] = this.sortBy (result['asks'], 0); + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const filter = { + 'filter': { + 'orderID': id, + }, + }; + const response = await this.fetchOrders (symbol, undefined, undefined, this.deepExtend (filter, params)); + const numResults = response.length; + if (numResults === 1) { + return response[0]; + } + throw new OrderNotFound (this.id + ': The order ' + id + ' not found.'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startTime'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['count'] = limit; + } + request = this.deepExtend (request, params); + // why the hassle? urlencode in python is kinda broken for nested dicts. + // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" + // Bitmex doesn't like that. Hence resorting to this hack. + if ('filter' in request) { + request['filter'] = this.json (request['filter']); + } + const response = await this.privateGetOrder (request); + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'filter': { + 'open': true, + }, + }; + return await this.fetchOrders (symbol, since, limit, this.deepExtend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // Bitmex barfs if you set 'open': false in the filter... + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startTime'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['count'] = limit; + } + request = this.deepExtend (request, params); + // why the hassle? urlencode in python is kinda broken for nested dicts. + // E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}" + // Bitmex doesn't like that. Hence resorting to this hack. + if ('filter' in request) { + request['filter'] = this.json (request['filter']); + } + const response = await this.privateGetExecutionTradeHistory (request); + // + // [ + // { + // "execID": "string", + // "orderID": "string", + // "clOrdID": "string", + // "clOrdLinkID": "string", + // "account": 0, + // "symbol": "string", + // "side": "string", + // "lastQty": 0, + // "lastPx": 0, + // "underlyingLastPx": 0, + // "lastMkt": "string", + // "lastLiquidityInd": "string", + // "simpleOrderQty": 0, + // "orderQty": 0, + // "price": 0, + // "displayQty": 0, + // "stopPx": 0, + // "pegOffsetValue": 0, + // "pegPriceType": "string", + // "currency": "string", + // "settlCurrency": "string", + // "execType": "string", + // "ordType": "string", + // "timeInForce": "string", + // "execInst": "string", + // "contingencyType": "string", + // "exDestination": "string", + // "ordStatus": "string", + // "triggered": "string", + // "workingIndicator": true, + // "ordRejReason": "string", + // "simpleLeavesQty": 0, + // "leavesQty": 0, + // "simpleCumQty": 0, + // "cumQty": 0, + // "avgPx": 0, + // "commission": 0, + // "tradePublishIndicator": "string", + // "multiLegReportingType": "string", + // "text": "string", + // "trdMatchID": "string", + // "execCost": 0, + // "execComm": 0, + // "homeNotional": 0, + // "foreignNotional": 0, + // "transactTime": "2019-03-05T12:47:02.762Z", + // "timestamp": "2019-03-05T12:47:02.762Z" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + parseLedgerEntryType (type) { + const types = { + 'Withdrawal': 'transaction', + 'RealisedPNL': 'margin', + 'UnrealisedPNL': 'margin', + 'Deposit': 'transaction', + 'Transfer': 'transfer', + 'AffiliatePayout': 'referral', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // { + // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", + // account: 24321, + // currency: "XBt", + // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... + // amount: -1000000, + // fee: 300000, + // transactStatus: "Completed", // "Canceled", ... + // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", + // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", + // text: "", + // transactTime: "2017-03-21T20:05:14.388Z", + // walletBalance: 0, // balance after + // marginBalance: null, + // timestamp: "2017-03-22T13:09:23.514Z" + // } + // + // ButMEX returns the unrealized pnl from the wallet history endpoint. + // The unrealized pnl transaction has an empty timestamp. + // It is not related to historical pnl it has status set to "Pending". + // Therefore it's not a part of the history at all. + // https://github.com/ccxt/ccxt/issues/6047 + // + // { + // "transactID":"00000000-0000-0000-0000-000000000000", + // "account":121210, + // "currency":"XBt", + // "transactType":"UnrealisedPNL", + // "amount":-5508, + // "fee":0, + // "transactStatus":"Pending", + // "address":"XBTUSD", + // "tx":"", + // "text":"", + // "transactTime":null, # �---------------------------- null + // "walletBalance":139198767, + // "marginBalance":139193259, + // "timestamp":null # �---------------------------- null + // } + // + const id = this.safeString (item, 'transactID'); + const account = this.safeString (item, 'account'); + const referenceId = this.safeString (item, 'tx'); + const referenceAccount = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'transactType')); + const currencyId = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let amount = this.safeFloat (item, 'amount'); + if (amount !== undefined) { + amount = amount * 1e-8; + } + let timestamp = this.parse8601 (this.safeString (item, 'transactTime')); + if (timestamp === undefined) { + // https://github.com/ccxt/ccxt/issues/6047 + // set the timestamp to zero, 1970 Jan 1 00:00:00 + // for unrealized pnl and other transactions without a timestamp + timestamp = 0; // see comments above + } + let feeCost = this.safeFloat (item, 'fee', 0); + if (feeCost !== undefined) { + feeCost = feeCost * 1e-8; + } + const fee = { + 'cost': feeCost, + 'currency': code, + }; + let after = this.safeFloat (item, 'walletBalance'); + if (after !== undefined) { + after = after * 1e-8; + } + const before = this.sum (after, -amount); + let direction = undefined; + if (amount < 0) { + direction = 'out'; + amount = Math.abs (amount); + } else { + direction = 'in'; + } + const status = this.parseTransactionStatus (this.safeString (item, 'transactStatus')); + return { + 'id': id, + 'info': item, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'direction': direction, + 'account': account, + 'referenceId': referenceId, + 'referenceAccount': referenceAccount, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, + 'after': after, + 'status': status, + 'fee': fee, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const request = { + // 'start': 123, + }; + // + // if (since !== undefined) { + // // date-based pagination not supported + // } + // + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privateGetUserWalletHistory (this.extend (request, params)); + // + // [ + // { + // transactID: "69573da3-7744-5467-3207-89fd6efe7a47", + // account: 24321, + // currency: "XBt", + // transactType: "Withdrawal", // "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ... + // amount: -1000000, + // fee: 300000, + // transactStatus: "Completed", // "Canceled", ... + // address: "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9", + // tx: "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA", + // text: "", + // transactTime: "2017-03-21T20:05:14.388Z", + // walletBalance: 0, // balance after + // marginBalance: null, + // timestamp: "2017-03-22T13:09:23.514Z" + // } + // ] + // + return this.parseLedger (response, currency, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'start': 123, + }; + // + // if (since !== undefined) { + // // date-based pagination not supported + // } + // + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privateGetUserWalletHistory (this.extend (request, params)); + const transactions = this.filterByArray (response, 'transactType', [ 'Withdrawal', 'Deposit' ], false); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + return this.parseTransactions (transactions, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'Canceled': 'canceled', + 'Completed': 'ok', + 'Pending': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // 'transactID': 'ffe699c2-95ee-4c13-91f9-0faf41daec25', + // 'account': 123456, + // 'currency': 'XBt', + // 'transactType': 'Withdrawal', + // 'amount': -100100000, + // 'fee': 100000, + // 'transactStatus': 'Completed', + // 'address': '385cR5DM96n1HvBDMzLHPYcw89fZAXULJP', + // 'tx': '3BMEXabcdefghijklmnopqrstuvwxyz123', + // 'text': '', + // 'transactTime': '2019-01-02T01:00:00.000Z', + // 'walletBalance': 99900000, + // 'marginBalance': None, + // 'timestamp': '2019-01-02T13:00:00.000Z' + // } + // + const id = this.safeString (transaction, 'transactID'); + // For deposits, transactTime == timestamp + // For withdrawals, transactTime is submission, timestamp is processed + const transactTime = this.parse8601 (this.safeString (transaction, 'transactTime')); + const timestamp = this.parse8601 (this.safeString (transaction, 'timestamp')); + const type = this.safeStringLower (transaction, 'transactType'); + // Deposits have no from address or to address, withdrawals have both + let address = undefined; + let addressFrom = undefined; + let addressTo = undefined; + if (type === 'withdrawal') { + address = this.safeString (transaction, 'address'); + addressFrom = this.safeString (transaction, 'tx'); + addressTo = address; + } + let amount = this.safeInteger (transaction, 'amount'); + if (amount !== undefined) { + amount = Math.abs (amount) * 1e-8; + } + let feeCost = this.safeInteger (transaction, 'fee'); + if (feeCost !== undefined) { + feeCost = feeCost * 1e-8; + } + const fee = { + 'cost': feeCost, + 'currency': 'BTC', + }; + let status = this.safeString (transaction, 'transactStatus'); + if (status !== undefined) { + status = this.parseTransactionStatus (status); + } + return { + 'info': transaction, + 'id': id, + 'txid': undefined, + 'timestamp': transactTime, + 'datetime': this.iso8601 (transactTime), + 'addressFrom': addressFrom, + 'address': address, + 'addressTo': addressTo, + 'tagFrom': undefined, + 'tag': undefined, + 'tagTo': undefined, + 'type': type, + 'amount': amount, + // BTC is the only currency on Bitmex + 'currency': 'BTC', + 'status': status, + 'updated': timestamp, + 'comment': undefined, + 'fee': fee, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (!market['active']) { + throw new ExchangeError (this.id + ': symbol ' + symbol + ' is delisted'); + } + const tickers = await this.fetchTickers ([ symbol ], params); + const ticker = this.safeValue (tickers, symbol); + if (ticker === undefined) { + throw new ExchangeError (this.id + ' ticker symbol ' + symbol + ' not found'); + } + return ticker; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetInstrumentActiveAndIndices (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = this.safeString (ticker, 'symbol'); + if (symbol !== undefined) { + result[symbol] = ticker; + } + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { symbol: "ETHH19", + // rootSymbol: "ETH", + // state: "Open", + // typ: "FFCCSX", + // listing: "2018-12-17T04:00:00.000Z", + // front: "2019-02-22T12:00:00.000Z", + // expiry: "2019-03-29T12:00:00.000Z", + // settle: "2019-03-29T12:00:00.000Z", + // relistInterval: null, + // inverseLeg: "", + // sellLeg: "", + // buyLeg: "", + // optionStrikePcnt: null, + // optionStrikeRound: null, + // optionStrikePrice: null, + // optionMultiplier: null, + // positionCurrency: "ETH", + // underlying: "ETH", + // quoteCurrency: "XBT", + // underlyingSymbol: "ETHXBT=", + // reference: "BMEX", + // referenceSymbol: ".BETHXBT30M", + // calcInterval: null, + // publishInterval: null, + // publishTime: null, + // maxOrderQty: 100000000, + // maxPrice: 10, + // lotSize: 1, + // tickSize: 0.00001, + // multiplier: 100000000, + // settlCurrency: "XBt", + // underlyingToPositionMultiplier: 1, + // underlyingToSettleMultiplier: null, + // quoteToSettleMultiplier: 100000000, + // isQuanto: false, + // isInverse: false, + // initMargin: 0.02, + // maintMargin: 0.01, + // riskLimit: 5000000000, + // riskStep: 5000000000, + // limit: null, + // capped: false, + // taxed: true, + // deleverage: true, + // makerFee: -0.0005, + // takerFee: 0.0025, + // settlementFee: 0, + // insuranceFee: 0, + // fundingBaseSymbol: "", + // fundingQuoteSymbol: "", + // fundingPremiumSymbol: "", + // fundingTimestamp: null, + // fundingInterval: null, + // fundingRate: null, + // indicativeFundingRate: null, + // rebalanceTimestamp: null, + // rebalanceInterval: null, + // openingTimestamp: "2019-02-13T08:00:00.000Z", + // closingTimestamp: "2019-02-13T09:00:00.000Z", + // sessionInterval: "2000-01-01T01:00:00.000Z", + // prevClosePrice: 0.03347, + // limitDownPrice: null, + // limitUpPrice: null, + // bankruptLimitDownPrice: null, + // bankruptLimitUpPrice: null, + // prevTotalVolume: 1386531, + // totalVolume: 1387062, + // volume: 531, + // volume24h: 17118, + // prevTotalTurnover: 4741294246000, + // totalTurnover: 4743103466000, + // turnover: 1809220000, + // turnover24h: 57919845000, + // homeNotional24h: 17118, + // foreignNotional24h: 579.19845, + // prevPrice24h: 0.03349, + // vwap: 0.03383564, + // highPrice: 0.03458, + // lowPrice: 0.03329, + // lastPrice: 0.03406, + // lastPriceProtected: 0.03406, + // lastTickDirection: "ZeroMinusTick", + // lastChangePcnt: 0.017, + // bidPrice: 0.03406, + // midPrice: 0.034065, + // askPrice: 0.03407, + // impactBidPrice: 0.03406, + // impactMidPrice: 0.034065, + // impactAskPrice: 0.03407, + // hasLiquidity: true, + // openInterest: 83679, + // openValue: 285010674000, + // fairMethod: "ImpactMidPrice", + // fairBasisRate: 0, + // fairBasis: 0, + // fairPrice: 0.03406, + // markMethod: "FairPrice", + // markPrice: 0.03406, + // indicativeTaxRate: 0, + // indicativeSettlePrice: 0.03406, + // optionUnderlyingPrice: null, + // settledPrice: null, + // timestamp: "2019-02-13T08:40:30.000Z", + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + const open = this.safeFloat (ticker, 'prevPrice24h'); + const last = this.safeFloat (ticker, 'lastPrice'); + let change = undefined; + let percentage = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + if (open > 0) { + percentage = change / open * 100; + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highPrice'), + 'low': this.safeFloat (ticker, 'lowPrice'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': undefined, + 'vwap': this.safeFloat (ticker, 'vwap'), + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': this.sum (open, last) / 2, + 'baseVolume': this.safeFloat (ticker, 'homeNotional24h'), + 'quoteVolume': this.safeFloat (ticker, 'foreignNotional24h'), + 'info': ticker, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (this.safeString (ohlcv, 'timestamp')); + return [ + timestamp, + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // send JSON key/value pairs, such as {"key": "value"} + // filter by individual fields and do advanced queries on timestamps + // let filter = { 'key': 'value' }; + // send a bare series (e.g. XBU) to nearest expiring contract in that series + // you can also send a timeframe, e.g. XBU:monthly + // timeframes: daily, weekly, monthly, quarterly, and biquarterly + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'binSize': this.timeframes[timeframe], + 'partial': true, // true == include yet-incomplete current bins + // 'filter': filter, // filter by individual fields and do advanced queries + // 'columns': [], // will return all columns if omitted + // 'start': 0, // starting point for results (wtf?) + // 'reverse': false, // true == newest first + // 'endTime': '', // ending date filter for results + }; + if (limit !== undefined) { + request['count'] = limit; // default 100, max 500 + } + const duration = this.parseTimeframe (timeframe) * 1000; + const fetchOHLCVOpenTimestamp = this.safeValue (this.options, 'fetchOHLCVOpenTimestamp', true); + // if since is not set, they will return candles starting from 2017-01-01 + if (since !== undefined) { + let timestamp = since; + if (fetchOHLCVOpenTimestamp) { + timestamp = this.sum (timestamp, duration); + } + const ymdhms = this.ymdhms (timestamp); + request['startTime'] = ymdhms; // starting date filter for results + } + const response = await this.publicGetTradeBucketed (this.extend (request, params)); + const result = this.parseOHLCVs (response, market, timeframe, since, limit); + if (fetchOHLCVOpenTimestamp) { + // bitmex returns the candle's close timestamp - https://github.com/ccxt/ccxt/issues/4446 + // we can emulate the open timestamp by shifting all the timestamps one place + // so the previous close becomes the current open, and we drop the first candle + for (let i = 0; i < result.length; i++) { + result[i][0] = result[i][0] - duration; + } + } + return result; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // timestamp: '2018-08-28T00:00:02.735Z', + // symbol: 'XBTUSD', + // side: 'Buy', + // size: 2000, + // price: 6906.5, + // tickDirection: 'PlusTick', + // trdMatchID: 'b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8', + // grossValue: 28958000, + // homeNotional: 0.28958, + // foreignNotional: 2000 + // } + // + // fetchMyTrades (private) + // + // { + // "execID": "string", + // "orderID": "string", + // "clOrdID": "string", + // "clOrdLinkID": "string", + // "account": 0, + // "symbol": "string", + // "side": "string", + // "lastQty": 0, + // "lastPx": 0, + // "underlyingLastPx": 0, + // "lastMkt": "string", + // "lastLiquidityInd": "string", + // "simpleOrderQty": 0, + // "orderQty": 0, + // "price": 0, + // "displayQty": 0, + // "stopPx": 0, + // "pegOffsetValue": 0, + // "pegPriceType": "string", + // "currency": "string", + // "settlCurrency": "string", + // "execType": "string", + // "ordType": "string", + // "timeInForce": "string", + // "execInst": "string", + // "contingencyType": "string", + // "exDestination": "string", + // "ordStatus": "string", + // "triggered": "string", + // "workingIndicator": true, + // "ordRejReason": "string", + // "simpleLeavesQty": 0, + // "leavesQty": 0, + // "simpleCumQty": 0, + // "cumQty": 0, + // "avgPx": 0, + // "commission": 0, + // "tradePublishIndicator": "string", + // "multiLegReportingType": "string", + // "text": "string", + // "trdMatchID": "string", + // "execCost": 0, + // "execComm": 0, + // "homeNotional": 0, + // "foreignNotional": 0, + // "transactTime": "2019-03-05T12:47:02.762Z", + // "timestamp": "2019-03-05T12:47:02.762Z" + // } + // + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'size', 'lastQty'); + const id = this.safeString (trade, 'trdMatchID'); + const order = this.safeString (trade, 'orderID'); + const side = this.safeStringLower (trade, 'side'); + // price * amount doesn't work for all symbols (e.g. XBT, ETH) + let cost = this.safeFloat (trade, 'execCost'); + if (cost !== undefined) { + cost = Math.abs (cost) / 100000000; + } + let fee = undefined; + if ('execComm' in trade) { + let feeCost = this.safeFloat (trade, 'execComm'); + feeCost = feeCost / 100000000; + const currencyId = this.safeString (trade, 'settlCurrency'); + const feeCurrency = this.safeCurrencyCode (currencyId); + const feeRate = this.safeFloat (trade, 'commission'); + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }; + } + let takerOrMaker = undefined; + if (fee !== undefined) { + takerOrMaker = (fee['cost'] < 0) ? 'maker' : 'taker'; + } + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + const type = this.safeStringLower (trade, 'ordType'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': order, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'fee': fee, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'New': 'open', + 'PartiallyFilled': 'open', + 'Filled': 'closed', + 'DoneForDay': 'open', + 'Canceled': 'canceled', + 'PendingCancel': 'open', + 'PendingNew': 'open', + 'Rejected': 'rejected', + 'Expired': 'expired', + 'Stopped': 'open', + 'Untriggered': 'open', + 'Triggered': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeString (order, 'ordStatus')); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + } + const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'transactTime')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'orderQty'); + const filled = this.safeFloat (order, 'cumQty', 0.0); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = Math.max (amount - filled, 0.0); + } + } + const average = this.safeFloat (order, 'avgPx'); + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = average * filled; + } else if (price !== undefined) { + cost = price * filled; + } + } + const id = this.safeString (order, 'orderID'); + const type = this.safeStringLower (order, 'ordType'); + const side = this.safeStringLower (order, 'side'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startTime'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.publicGetTrade (this.extend (request, params)); + // + // [ + // { + // timestamp: '2018-08-28T00:00:02.735Z', + // symbol: 'XBTUSD', + // side: 'Buy', + // size: 2000, + // price: 6906.5, + // tickDirection: 'PlusTick', + // trdMatchID: 'b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8', + // grossValue: 28958000, + // homeNotional: 0.28958, + // foreignNotional: 2000 + // }, + // { + // timestamp: '2018-08-28T00:00:03.778Z', + // symbol: 'XBTUSD', + // side: 'Sell', + // size: 1000, + // price: 6906, + // tickDirection: 'MinusTick', + // trdMatchID: '0d4f1682-5270-a800-569b-4a0eb92db97c', + // grossValue: 14480000, + // homeNotional: 0.1448, + // foreignNotional: 1000 + // }, + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'side': this.capitalize (side), + 'orderQty': amount, + 'ordType': this.capitalize (type), + }; + if (price !== undefined) { + request['price'] = price; + } + const response = await this.privatePostOrder (this.extend (request, params)); + const order = this.parseOrder (response); + const id = this.safeString (order, 'id'); + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderID': id, + }; + if (amount !== undefined) { + request['orderQty'] = amount; + } + if (price !== undefined) { + request['price'] = price; + } + const response = await this.privatePutOrder (this.extend (request, params)); + const order = this.parseOrder (response); + this.orders[order['id']] = order; + return this.extend ({ 'info': response }, order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateDeleteOrder (this.extend ({ 'orderID': id }, params)); + let order = response[0]; + const error = this.safeString (order, 'error'); + if (error !== undefined) { + if (error.indexOf ('Unable to cancel order due to existing state') >= 0) { + throw new OrderNotFound (this.id + ' cancelOrder() failed: ' + error); + } + } + order = this.parseOrder (order); + this.orders[order['id']] = order; + return this.extend ({ 'info': response }, order); + } + + isFiat (currency) { + if (currency === 'EUR') { + return true; + } + if (currency === 'PLN') { + return true; + } + return false; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + // let currency = this.currency (code); + if (code !== 'BTC') { + throw new ExchangeError (this.id + ' supoprts BTC withdrawals only, other currencies coming soon...'); + } + const request = { + 'currency': 'XBt', // temporarily + 'amount': amount, + 'address': address, + // 'otpToken': '123456', // requires if two-factor auth (OTP) is enabled + // 'fee': 0.001, // bitcoin network fee + }; + const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params)); + return { + 'info': response, + 'id': response['transactID'], + }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code === 429) { + throw new DDoSProtection (this.id + ' ' + body); + } + if (code >= 400) { + const error = this.safeValue (response, 'error', {}); + const message = this.safeString (error, 'message'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + if (code === 400) { + throw new BadRequest (feedback); + } + throw new ExchangeError (feedback); // unknown message + } + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let query = '/api/' + this.version + '/' + path; + if (method === 'GET') { + if (Object.keys (params).length) { + query += '?' + this.urlencode (params); + } + } else { + const format = this.safeString (params, '_format'); + if (format !== undefined) { + query += '?' + this.urlencode ({ '_format': format }); + params = this.omit (params, '_format'); + } + } + const url = this.urls['api'][api] + query; + if (this.apiKey && this.secret) { + let auth = method + query; + let expires = this.safeInteger (this.options, 'api-expires'); + headers = { + 'Content-Type': 'application/json', + 'api-key': this.apiKey, + }; + expires = this.sum (this.seconds (), expires); + expires = expires.toString (); + auth += expires; + headers['api-expires'] = expires; + if (method === 'POST' || method === 'PUT' || method === 'DELETE') { + if (Object.keys (params).length) { + body = this.json (params); + auth += body; + } + } + headers['api-signature'] = this.hmac (this.encode (auth), this.encode (this.secret)); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitso.js b/TaskServer/node_modules/ccxt/js/bitso.js new file mode 100644 index 000000000..0ebc5ca39 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitso.js @@ -0,0 +1,562 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InvalidNonce, AuthenticationError, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitso extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitso', + 'name': 'Bitso', + 'countries': [ 'MX' ], // Mexico + 'rateLimit': 2000, // 30 requests per minute + 'version': 'v3', + 'has': { + 'CORS': true, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766335-715ce7aa-5ed5-11e7-88a8-173a27bb30fe.jpg', + 'api': 'https://api.bitso.com', + 'www': 'https://bitso.com', + 'doc': 'https://bitso.com/api_info', + 'fees': 'https://bitso.com/fees?l=es', + 'referral': 'https://bitso.com/?ref=itej', + }, + 'options': { + 'precision': { + 'XRP': 6, + 'MXN': 2, + 'TUSD': 2, + }, + 'defaultPrecision': 8, + }, + 'api': { + 'public': { + 'get': [ + 'available_books', + 'ticker', + 'order_book', + 'trades', + ], + }, + 'private': { + 'get': [ + 'account_status', + 'balance', + 'fees', + 'fundings', + 'fundings/{fid}', + 'funding_destination', + 'kyc_documents', + 'ledger', + 'ledger/trades', + 'ledger/fees', + 'ledger/fundings', + 'ledger/withdrawals', + 'mx_bank_codes', + 'open_orders', + 'order_trades/{oid}', + 'orders/{oid}', + 'user_trades', + 'user_trades/{tid}', + 'withdrawals/', + 'withdrawals/{wid}', + ], + 'post': [ + 'bitcoin_withdrawal', + 'debit_card_withdrawal', + 'ether_withdrawal', + 'ripple_withdrawal', + 'bcash_withdrawal', + 'litecoin_withdrawal', + 'orders', + 'phone_number', + 'phone_verification', + 'phone_withdrawal', + 'spei_withdrawal', + 'ripple_withdrawal', + 'bcash_withdrawal', + 'litecoin_withdrawal', + ], + 'delete': [ + 'orders/{oid}', + 'orders/all', + ], + }, + }, + 'exceptions': { + '0201': AuthenticationError, // Invalid Nonce or Invalid Credentials + '104': InvalidNonce, // Cannot perform request - nonce must be higher than 1520307203724237 + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAvailableBooks (params); + const markets = this.safeValue (response, 'payload'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'book'); + const [ baseId, quoteId ] = id.split ('_'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_amount'), + 'max': this.safeFloat (market, 'maximum_amount'), + }, + 'price': { + 'min': this.safeFloat (market, 'minimum_price'), + 'max': this.safeFloat (market, 'maximum_price'), + }, + 'cost': { + 'min': this.safeFloat (market, 'minimum_value'), + 'max': this.safeFloat (market, 'maximum_value'), + }, + }; + const precision = { + 'amount': this.safeInteger (this.options['precision'], base, this.options['defaultPrecision']), + 'price': this.safeInteger (this.options['precision'], quote, this.options['defaultPrecision']), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'limits': limits, + 'precision': precision, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const balances = this.safeValue (response['payload'], 'balances'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'available'), + 'used': this.safeFloat (balance, 'locked'), + 'total': this.safeFloat (balance, 'total'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'book': this.marketId (symbol), + }; + const response = await this.publicGetOrderBook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'payload'); + const timestamp = this.parse8601 (this.safeString (orderbook, 'updated_at')); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'book': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'payload'); + const timestamp = this.parse8601 (this.safeString (ticker, 'created_at')); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'book'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString2 (trade, 'side', 'maker_side'); + let amount = this.safeFloat2 (trade, 'amount', 'major'); + if (amount !== undefined) { + amount = Math.abs (amount); + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fees_amount'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fees_currency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + let cost = this.safeFloat (trade, 'minor'); + if (cost !== undefined) { + cost = Math.abs (cost); + } + const price = this.safeFloat (trade, 'price'); + const orderId = this.safeString (trade, 'oid'); + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'book': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response['payload'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = 25, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the don't support fetching trades starting from a date yet + // use the `marker` extra param for that + // this is not a typo, the variable name is 'marker' (don't confuse with 'market') + const markerInParams = ('marker' in params); + // warn the user with an exception if the user wants to filter + // starting from since timestamp, but does not set the trade id with an extra 'marker' param + if ((since !== undefined) && !markerInParams) { + throw new ExchangeError (this.id + ' fetchMyTrades does not support fetching trades starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id'); + } + // convert it to an integer unconditionally + if (markerInParams) { + params = this.extend (params, { + 'marker': parseInt (params['marker']), + }); + } + const request = { + 'book': market['id'], + 'limit': limit, // default = 25, max = 100 + // 'sort': 'desc', // default = desc + // 'marker': id, // integer id to start from + }; + const response = await this.privateGetUserTrades (this.extend (request, params)); + return this.parseTrades (response['payload'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'book': this.marketId (symbol), + 'side': side, + 'type': type, + 'major': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const id = this.safeString (response['payload'], 'oid'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'oid': id, + }; + return await this.privateDeleteOrdersOid (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'partial-fill': 'open', // this is a common substitution in ccxt + 'completed': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'oid'); + const side = this.safeString (order, 'side'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'book'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const orderType = this.safeString (order, 'type'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'original_amount'); + const remaining = this.safeFloat (order, 'unfilled_amount'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = 25, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the don't support fetching trades starting from a date yet + // use the `marker` extra param for that + // this is not a typo, the variable name is 'marker' (don't confuse with 'market') + const markerInParams = ('marker' in params); + // warn the user with an exception if the user wants to filter + // starting from since timestamp, but does not set the trade id with an extra 'marker' param + if ((since !== undefined) && !markerInParams) { + throw new ExchangeError (this.id + ' fetchOpenOrders does not support fetching orders starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id'); + } + // convert it to an integer unconditionally + if (markerInParams) { + params = this.extend (params, { + 'marker': parseInt (params['marker']), + }); + } + const request = { + 'book': market['id'], + 'limit': limit, // default = 25, max = 100 + // 'sort': 'desc', // default = desc + // 'marker': id, // integer id to start from + }; + const response = await this.privateGetOpenOrders (this.extend (request, params)); + const orders = this.parseOrders (response['payload'], market, since, limit); + return orders; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetOrdersOid ({ + 'oid': id, + }); + const payload = this.safeValue (response, 'payload'); + if (Array.isArray (payload)) { + const numOrders = response['payload'].length; + if (numOrders === 1) { + return this.parseOrder (payload[0]); + } + } + throw new OrderNotFound (this.id + ': The order ' + id + ' not found.'); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'oid': id, + }; + const response = await this.privateGetOrderTradesOid (this.extend (request, params)); + return this.parseTrades (response['payload'], market); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'fund_currency': currency['id'], + }; + const response = await this.privateGetFundingDestination (this.extend (request, params)); + let address = this.safeString (response['payload'], 'account_identifier'); + let tag = undefined; + if (code === 'XRP') { + const parts = address.split ('?dt='); + address = parts[0]; + tag = parts[1]; + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const methods = { + 'BTC': 'Bitcoin', + 'ETH': 'Ether', + 'XRP': 'Ripple', + 'BCH': 'Bcash', + 'LTC': 'Litecoin', + }; + const method = (code in methods) ? methods[code] : undefined; + if (method === undefined) { + throw new ExchangeError (this.id + ' not valid withdraw coin: ' + code); + } + const request = { + 'amount': amount, + 'address': address, + 'destination_tag': tag, + }; + const classMethod = 'privatePost' + method + 'Withdrawal'; + const response = await this[classMethod] (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response['payload'], 'wid'), + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let endpoint = '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + endpoint += '?' + this.urlencode (query); + } + } + const url = this.urls['api'] + endpoint; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let request = [ nonce, method, endpoint ].join (''); + if (method !== 'GET') { + if (Object.keys (query).length) { + body = this.json (query); + request += body; + } + } + const signature = this.hmac (this.encode (request), this.encode (this.secret)); + const auth = this.apiKey + ':' + nonce + ':' + signature; + headers = { + 'Authorization': 'Bitso ' + auth, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // {"success":false,"error":{"code":104,"message":"Cannot perform request - nonce must be higher than 1520307203724237"}} + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const feedback = this.id + ' ' + this.json (response); + const error = this.safeValue (response, 'error'); + if (error === undefined) { + throw new ExchangeError (feedback); + } + const code = this.safeString (error, 'code'); + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('success' in response) { + if (response['success']) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitstamp.js b/TaskServer/node_modules/ccxt/js/bitstamp.js new file mode 100644 index 000000000..e2d0c6d5e --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitstamp.js @@ -0,0 +1,1144 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ExchangeError, NotSupported, PermissionDenied, InvalidNonce, OrderNotFound, InsufficientFunds, InvalidAddress, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitstamp extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitstamp', + 'name': 'Bitstamp', + 'countries': [ 'GB' ], + 'rateLimit': 1000, + 'version': 'v2', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': true, + 'fetchDepositAddress': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchTransactions': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg', + 'api': { + 'public': 'https://www.bitstamp.net/api', + 'private': 'https://www.bitstamp.net/api', + }, + 'www': 'https://www.bitstamp.net', + 'doc': 'https://www.bitstamp.net/api', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'order_book/{pair}/', + 'ticker_hour/{pair}/', + 'ticker/{pair}/', + 'transactions/{pair}/', + 'trading-pairs-info/', + ], + }, + 'private': { + 'post': [ + 'balance/', + 'balance/{pair}/', + 'bch_withdrawal/', + 'bch_address/', + 'user_transactions/', + 'user_transactions/{pair}/', + 'open_orders/all/', + 'open_orders/{pair}/', + 'order_status/', + 'cancel_order/', + 'buy/{pair}/', + 'buy/market/{pair}/', + 'buy/instant/{pair}/', + 'sell/{pair}/', + 'sell/market/{pair}/', + 'sell/instant/{pair}/', + 'ltc_withdrawal/', + 'ltc_address/', + 'eth_withdrawal/', + 'eth_address/', + 'xrp_withdrawal/', + 'xrp_address/', + 'transfer-to-main/', + 'transfer-from-main/', + 'withdrawal-requests/', + 'withdrawal/open/', + 'withdrawal/status/', + 'withdrawal/cancel/', + 'liquidation_address/new/', + 'liquidation_address/info/', + ], + }, + 'v1': { + 'post': [ + 'bitcoin_deposit_address/', + 'unconfirmed_btc/', + 'bitcoin_withdrawal/', + 'ripple_withdrawal/', + 'ripple_address/', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.5 / 100, + 'maker': 0.5 / 100, + 'tiers': { + 'taker': [ + [0, 0.5 / 100], + [20000, 0.25 / 100], + [100000, 0.24 / 100], + [200000, 0.22 / 100], + [400000, 0.20 / 100], + [600000, 0.15 / 100], + [1000000, 0.14 / 100], + [2000000, 0.13 / 100], + [4000000, 0.12 / 100], + [20000000, 0.11 / 100], + [20000001, 0.10 / 100], + ], + 'maker': [ + [0, 0.5 / 100], + [20000, 0.25 / 100], + [100000, 0.24 / 100], + [200000, 0.22 / 100], + [400000, 0.20 / 100], + [600000, 0.15 / 100], + [1000000, 0.14 / 100], + [2000000, 0.13 / 100], + [4000000, 0.12 / 100], + [20000000, 0.11 / 100], + [20000001, 0.10 / 100], + ], + }, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0, + 'BCH': 0, + 'LTC': 0, + 'ETH': 0, + 'XRP': 0, + 'USD': 25, + 'EUR': 0.90, + }, + 'deposit': { + 'BTC': 0, + 'BCH': 0, + 'LTC': 0, + 'ETH': 0, + 'XRP': 0, + 'USD': 25, + 'EUR': 0, + }, + }, + }, + 'exceptions': { + 'exact': { + 'No permission found': PermissionDenied, + 'API key not found': AuthenticationError, + 'IP address not allowed': PermissionDenied, + 'Invalid nonce': InvalidNonce, + 'Invalid signature': AuthenticationError, + 'Authentication failed': AuthenticationError, + 'Missing key, signature and nonce parameters': AuthenticationError, + 'Your account is frozen': PermissionDenied, + 'Please update your profile with your FATCA information, before using API.': PermissionDenied, + 'Order not found': OrderNotFound, + 'Price is more than 20% below market price.': InvalidOrder, + }, + 'broad': { + 'Minimum order size is': InvalidOrder, // Minimum order size is 5.0 EUR. + 'Check your account balance for details.': InsufficientFunds, // You have only 0.00100000 BTC available. Check your account balance for details. + 'Ensure this value has at least': InvalidAddress, // Ensure this value has at least 25 characters (it has 4). + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTradingPairsInfo (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const name = this.safeString (market, 'name'); + let [ base, quote ] = name.split ('/'); + const baseId = base.toLowerCase (); + const quoteId = quote.toLowerCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const symbolId = baseId + '_' + quoteId; + const id = this.safeString (market, 'url_symbol'); + const precision = { + 'amount': market['base_decimals'], + 'price': market['counter_decimals'], + }; + const parts = market['minimum_order'].split (' '); + const cost = parts[0]; + // let [ cost, currency ] = market['minimum_order'].split (' '); + const active = (market['trading'] === 'Enabled'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'symbolId': symbolId, + 'info': market, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': parseFloat (cost), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this.publicGetOrderBookPair (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const ticker = await this.publicGetTickerPair (this.extend (request, params)); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + getCurrencyIdFromTransaction (transaction) { + // + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "datetime": XXX, + // "usd": 0.0, + // "btc": 0.0, + // "eth": "0.05000000", + // "type": "0", + // "id": XXX, + // "eur": 0.0 + // } + // + const currencyId = this.safeStringLower (transaction, 'currency'); + if (currencyId !== undefined) { + return currencyId; + } + transaction = this.omit (transaction, [ + 'fee', + 'price', + 'datetime', + 'type', + 'status', + 'id', + ]); + const ids = Object.keys (transaction); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + if (id.indexOf ('_') < 0) { + const value = this.safeFloat (transaction, id); + if ((value !== undefined) && (value !== 0)) { + return id; + } + } + } + return undefined; + } + + getMarketFromTrade (trade) { + trade = this.omit (trade, [ + 'fee', + 'price', + 'datetime', + 'tid', + 'type', + 'order_id', + 'side', + ]); + const currencyIds = Object.keys (trade); + const numCurrencyIds = currencyIds.length; + if (numCurrencyIds > 2) { + throw new ExchangeError (this.id + ' getMarketFromTrade too many keys: ' + this.json (currencyIds) + ' in the trade: ' + this.json (trade)); + } + if (numCurrencyIds === 2) { + let marketId = currencyIds[0] + currencyIds[1]; + if (marketId in this.markets_by_id) { + return this.markets_by_id[marketId]; + } + marketId = currencyIds[1] + currencyIds[0]; + if (marketId in this.markets_by_id) { + return this.markets_by_id[marketId]; + } + } + return undefined; + } + + getMarketFromTrades (trades) { + const tradesBySymbol = this.indexBy (trades, 'symbol'); + const symbols = Object.keys (tradesBySymbol); + const numSymbols = symbols.length; + if (numSymbols === 1) { + return this.markets[symbols[0]]; + } + return undefined; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // date: '1551814435', + // tid: '83581898', + // price: '0.03532850', + // type: '1', + // amount: '0.85945907' + // }, + // + // fetchMyTrades, trades returned within fetchOrder (private) + // + // { + // "usd": "6.0134400000000000", + // "price": "4008.96000000", + // "datetime": "2019-03-28 23:07:37.233599", + // "fee": "0.02", + // "btc": "0.00150000", + // "tid": 84452058, + // "type": 2 + // } + // + // from fetchOrder: + // { fee: '0.000019', + // price: '0.00015803', + // datetime: '2018-01-07 10:45:34.132551', + // btc: '0.0079015000000000', + // tid: 42777395, + // type: 2, //(0 - deposit; 1 - withdrawal; 2 - market trade) NOT buy/sell + // xrp: '50.00000000' } + const id = this.safeString2 (trade, 'id', 'tid'); + let symbol = undefined; + let side = undefined; + let price = this.safeFloat (trade, 'price'); + let amount = this.safeFloat (trade, 'amount'); + const orderId = this.safeString (trade, 'order_id'); + const type = undefined; + let cost = this.safeFloat (trade, 'cost'); + if (market === undefined) { + const keys = Object.keys (trade); + for (let i = 0; i < keys.length; i++) { + if (keys[i].indexOf ('_') >= 0) { + const marketId = keys[i].replace ('_', ''); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + } + // if the market is still not defined + // try to deduce it from used keys + if (market === undefined) { + market = this.getMarketFromTrade (trade); + } + } + const feeCost = this.safeFloat (trade, 'fee'); + let feeCurrency = undefined; + if (market !== undefined) { + price = this.safeFloat (trade, market['symbolId'], price); + amount = this.safeFloat (trade, market['baseId'], amount); + cost = this.safeFloat (trade, market['quoteId'], cost); + feeCurrency = market['quote']; + symbol = market['symbol']; + } + let timestamp = this.safeString2 (trade, 'date', 'datetime'); + if (timestamp !== undefined) { + if (timestamp.indexOf (' ') >= 0) { + // iso8601 + timestamp = this.parse8601 (timestamp); + } else { + // string unix epoch in seconds + timestamp = parseInt (timestamp); + timestamp = timestamp * 1000; + } + } + // if it is a private trade + if ('id' in trade) { + if (amount !== undefined) { + if (amount < 0) { + side = 'sell'; + amount = -amount; + } else { + side = 'buy'; + } + } + } else { + side = this.safeString (trade, 'type'); + if (side === '1') { + side = 'sell'; + } else if (side === '0') { + side = 'buy'; + } + } + if (cost === undefined) { + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + } + if (cost !== undefined) { + cost = Math.abs (cost); + } + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'time': 'hour', + }; + const response = await this.publicGetTransactionsPair (this.extend (request, params)); + // + // [ + // { + // date: '1551814435', + // tid: '83581898', + // price: '0.03532850', + // type: '1', + // amount: '0.85945907' + // }, + // { + // date: '1551814434', + // tid: '83581896', + // price: '0.03532851', + // type: '1', + // amount: '11.34130961' + // }, + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balance = await this.privatePostBalance (params); + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const account = this.account (); + account['free'] = this.safeFloat (balance, currencyId + '_available'); + account['used'] = this.safeFloat (balance, currencyId + '_reserved'); + account['total'] = this.safeFloat (balance, currencyId + '_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = 'privatePost' + this.capitalize (side); + const request = { + 'pair': market['id'], + 'amount': this.amountToPrecision (symbol, amount), + }; + if (type === 'market') { + method += 'Market'; + } else { + request['price'] = this.priceToPrecision (symbol, price); + } + method += 'Pair'; + const response = await this[method] (this.extend (request, params)); + const order = this.parseOrder (response, market); + return this.extend (order, { + 'type': type, + }); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'In Queue': 'open', + 'Open': 'open', + 'Finished': 'closed', + 'Canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrderStatus (this.safeString (response, 'status')); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { 'id': id }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + // + // { + // "status": "Finished", + // "id": 3047704374, + // "transactions": [ + // { + // "usd": "6.0134400000000000", + // "price": "4008.96000000", + // "datetime": "2019-03-28 23:07:37.233599", + // "fee": "0.02", + // "btc": "0.00150000", + // "tid": 84452058, + // "type": 2 + // } + // ] + // } + return this.parseOrder (response, market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'privatePostUserTransactions'; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + method += 'Pair'; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this[method] (this.extend (request, params)); + const result = this.filterBy (response, 'type', '2'); + return this.parseTrades (result, market, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostUserTransactions (this.extend (request, params)); + // + // [ + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "id": 1234567894, + // "usd": 0, + // "btc": 0, + // "datetime": "2018-09-08 09:00:31", + // "type": "1", + // "xrp": "-20.00000000", + // "eur": 0, + // }, + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "id": 1134567891, + // "usd": 0, + // "btc": 0, + // "datetime": "2018-09-07 18:47:52", + // "type": "0", + // "xrp": "20.00000000", + // "eur": 0, + // }, + // ] + // + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const transactions = this.filterByArray (response, 'type', [ '0', '1' ], false); + return this.parseTransactions (transactions, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['timedelta'] = this.milliseconds () - since; + } + const response = await this.privatePostWithdrawalRequests (this.extend (request, params)); + // + // [ + // { + // status: 2, + // datetime: '2018-10-17 10:58:13', + // currency: 'BTC', + // amount: '0.29669259', + // address: 'aaaaa', + // type: 1, + // id: 111111, + // transaction_id: 'xxxx', + // }, + // { + // status: 2, + // datetime: '2018-10-17 10:55:17', + // currency: 'ETH', + // amount: '1.11010664', + // address: 'aaaa', + // type: 16, + // id: 222222, + // transaction_id: 'xxxxx', + // }, + // ] + // + return this.parseTransactions (response, undefined, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchTransactions + // + // { + // "fee": "0.00000000", + // "btc_usd": "0.00", + // "id": 1234567894, + // "usd": 0, + // "btc": 0, + // "datetime": "2018-09-08 09:00:31", + // "type": "1", + // "xrp": "-20.00000000", + // "eur": 0, + // } + // + // fetchWithdrawals + // + // { + // status: 2, + // datetime: '2018-10-17 10:58:13', + // currency: 'BTC', + // amount: '0.29669259', + // address: 'aaaaa', + // type: 1, + // id: 111111, + // transaction_id: 'xxxx', + // } + // + // { + // "id": 3386432, + // "type": 14, + // "amount": "863.21332500", + // "status": 2, + // "address": "rE1sdh25BJQ3qFwngiTBwaq3zPGGYcrjp1?dt=1455", + // "currency": "XRP", + // "datetime": "2018-01-05 15:27:55", + // "transaction_id": "001743B03B0C79BA166A064AC0142917B050347B4CB23BA2AB4B91B3C5608F4C" + // } + // + const timestamp = this.parse8601 (this.safeString (transaction, 'datetime')); + const id = this.safeString (transaction, 'id'); + const currencyId = this.getCurrencyIdFromTransaction (transaction); + const code = this.safeCurrencyCode (currencyId, currency); + const feeCost = this.safeFloat (transaction, 'fee'); + let feeCurrency = undefined; + let amount = undefined; + if ('amount' in transaction) { + amount = this.safeFloat (transaction, 'amount'); + } else if (currency !== undefined) { + amount = this.safeFloat (transaction, currency['id'], amount); + feeCurrency = currency['code']; + } else if ((code !== undefined) && (currencyId !== undefined)) { + amount = this.safeFloat (transaction, currencyId, amount); + feeCurrency = code; + } + if (amount !== undefined) { + // withdrawals have a negative amount + amount = Math.abs (amount); + } + let status = 'ok'; + if ('status' in transaction) { + status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + } + let type = undefined; + if ('type' in transaction) { + // from fetchTransactions + const rawType = this.safeString (transaction, 'type'); + if (rawType === '0') { + type = 'deposit'; + } else if (rawType === '1') { + type = 'withdrawal'; + } + } else { + // from fetchWithdrawals + type = 'withdrawal'; + } + const txid = this.safeString (transaction, 'transaction_id'); + let tag = undefined; + let address = this.safeString (transaction, 'address'); + if (address !== undefined) { + // dt (destination tag) is embedded into the address field + const addressParts = address.split ('?dt='); + const numParts = addressParts.length; + if (numParts > 1) { + address = addressParts[0]; + tag = addressParts[1]; + } + } + const addressFrom = undefined; + const addressTo = address; + const tagFrom = undefined; + const tagTo = tag; + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + 'rate': undefined, + }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': addressFrom, + 'addressTo': addressTo, + 'address': address, + 'tagFrom': tagFrom, + 'tagTo': tagTo, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + parseTransactionStatus (status) { + // withdrawals: + // 0 (open), 1 (in process), 2 (finished), 3 (canceled) or 4 (failed). + const statuses = { + '0': 'pending', // Open + '1': 'pending', // In process + '2': 'ok', // Finished + '3': 'canceled', // Canceled + '4': 'failed', // Failed + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // from fetch order: + // { status: 'Finished', + // id: 731693945, + // transactions: + // [ { fee: '0.000019', + // price: '0.00015803', + // datetime: '2018-01-07 10:45:34.132551', + // btc: '0.0079015000000000', + // tid: 42777395, + // type: 2, + // xrp: '50.00000000' } ] } + // + // partially filled order: + // { "id": 468646390, + // "status": "Canceled", + // "transactions": [{ + // "eth": "0.23000000", + // "fee": "0.09", + // "tid": 25810126, + // "usd": "69.8947000000000000", + // "type": 2, + // "price": "303.89000000", + // "datetime": "2017-11-11 07:22:20.710567" + // }]} + // + // from create order response: + // { + // price: '0.00008012', + // currency_pair: 'XRP/BTC', + // datetime: '2019-01-31 21:23:36', + // amount: '15.00000000', + // type: '0', + // id: '2814205012' + // } + // + const id = this.safeString (order, 'id'); + let side = this.safeString (order, 'type'); + if (side !== undefined) { + side = (side === '1') ? 'sell' : 'buy'; + } + // there is no timestamp from fetchOrder + const timestamp = this.parse8601 (this.safeString (order, 'datetime')); + let lastTradeTimestamp = undefined; + let symbol = undefined; + let marketId = this.safeStringLower (order, 'currency_pair'); + if (marketId !== undefined) { + marketId = marketId.replace ('/', ''); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + } + let amount = this.safeFloat (order, 'amount'); + let filled = 0.0; + const trades = []; + const transactions = this.safeValue (order, 'transactions', []); + let feeCost = undefined; + let cost = undefined; + const numTransactions = transactions.length; + if (numTransactions > 0) { + feeCost = 0.0; + for (let i = 0; i < numTransactions; i++) { + const trade = this.parseTrade (this.extend ({ + 'order_id': id, + 'side': side, + }, transactions[i]), market); + filled = this.sum (filled, trade['amount']); + feeCost = this.sum (feeCost, trade['fee']['cost']); + if (cost === undefined) { + cost = 0.0; + } + cost = this.sum (cost, trade['cost']); + trades.push (trade); + } + lastTradeTimestamp = trades[numTransactions - 1]['timestamp']; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + if ((status === 'closed') && (amount === undefined)) { + amount = filled; + } + let remaining = undefined; + if (amount !== undefined) { + remaining = amount - filled; + } + let price = this.safeFloat (order, 'price'); + if (market === undefined) { + market = this.getMarketFromTrades (trades); + } + let feeCurrency = undefined; + if (market !== undefined) { + if (symbol === undefined) { + symbol = market['symbol']; + } + feeCurrency = market['quote']; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } else if (price === undefined) { + if (filled > 0) { + price = cost / filled; + } + } + let fee = undefined; + if (feeCost !== undefined) { + if (feeCurrency !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': fee, + 'info': order, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + let market = undefined; + await this.loadMarkets (); + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privatePostOpenOrdersAll (params); + // [ + // { + // price: '0.00008012', + // currency_pair: 'XRP/BTC', + // datetime: '2019-01-31 21:23:36', + // amount: '15.00000000', + // type: '0', + // id: '2814205012', + // } + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const order = this.parseOrder (response[i], market); + result.push (this.extend (order, { + 'status': 'open', + 'type': 'limit', + })); + } + if (symbol === undefined) { + return this.filterBySinceLimit (result, since, limit); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + getCurrencyName (code) { + if (code === 'BTC') { + return 'bitcoin'; + } + return code.toLowerCase (); + } + + isFiat (code) { + if (code === 'USD') { + return true; + } + if (code === 'EUR') { + return true; + } + return false; + } + + async fetchDepositAddress (code, params = {}) { + if (this.isFiat (code)) { + throw new NotSupported (this.id + ' fiat fetchDepositAddress() for ' + code + ' is not implemented yet'); + } + const name = this.getCurrencyName (code); + const v1 = (code === 'BTC'); + let method = v1 ? 'v1' : 'private'; // v1 or v2 + method += 'Post' + this.capitalize (name); + method += v1 ? 'Deposit' : ''; + method += 'Address'; + const response = await this[method] (params); + const address = v1 ? response : this.safeString (response, 'address'); + const tag = v1 ? undefined : this.safeString (response, 'destination_tag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + if (this.isFiat (code)) { + throw new NotSupported (this.id + ' fiat withdraw() for ' + code + ' is not implemented yet'); + } + const name = this.getCurrencyName (code); + const request = { + 'amount': amount, + 'address': address, + }; + const v1 = (code === 'BTC'); + let method = v1 ? 'v1' : 'private'; // v1 or v2 + method += 'Post' + this.capitalize (name) + 'Withdrawal'; + if (code === 'XRP') { + if (tag !== undefined) { + request['destination_tag'] = tag; + } + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/'; + if (api !== 'v1') { + url += this.version + '/'; + } + url += this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret))); + query = this.extend ({ + 'key': this.apiKey, + 'signature': signature.toUpperCase (), + 'nonce': nonce, + }, query); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // + // {"error": "No permission found"} // fetchDepositAddress returns this on apiKeys that don't have the permission required + // {"status": "error", "reason": {"__all__": ["Minimum order size is 5.0 EUR."]}} + // reuse of a nonce gives: { status: 'error', reason: 'Invalid nonce', code: 'API0004' } + const status = this.safeString (response, 'status'); + const error = this.safeValue (response, 'error'); + if ((status === 'error') || (error !== undefined)) { + let errors = []; + if (typeof error === 'string') { + errors.push (error); + } else if (error !== undefined) { + const keys = Object.keys (error); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = this.safeValue (error, key); + if (Array.isArray (value)) { + errors = this.arrayConcat (errors, value); + } else { + errors.push (value); + } + } + } + const reason = this.safeValue (response, 'reason', {}); + if (typeof reason === 'string') { + errors.push (reason); + } else { + const all = this.safeValue (reason, '__all__', []); + for (let i = 0; i < all.length; i++) { + errors.push (all[i]); + } + } + const code = this.safeString (response, 'code'); + if (code === 'API0005') { + throw new AuthenticationError (this.id + ' invalid signature, use the uid for the main account if you have subaccounts'); + } + const feedback = this.id + ' ' + body; + for (let i = 0; i < errors.length; i++) { + const value = errors[i]; + this.throwExactlyMatchedException (this.exceptions['exact'], value, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], value, feedback); + } + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitstamp1.js b/TaskServer/node_modules/ccxt/js/bitstamp1.js new file mode 100644 index 000000000..8019a2435 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitstamp1.js @@ -0,0 +1,291 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitstamp1 extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitstamp1', + 'name': 'Bitstamp', + 'countries': [ 'GB' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg', + 'api': 'https://www.bitstamp.net/api', + 'www': 'https://www.bitstamp.net', + 'doc': 'https://www.bitstamp.net/api', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'ticker', + 'ticker_hour', + 'order_book', + 'transactions', + 'eur_usd', + ], + }, + 'private': { + 'post': [ + 'balance', + 'user_transactions', + 'open_orders', + 'order_status', + 'cancel_order', + 'cancel_all_orders', + 'buy', + 'sell', + 'bitcoin_deposit_address', + 'unconfirmed_btc', + 'ripple_withdrawal', + 'ripple_address', + 'withdrawal_requests', + 'bitcoin_withdrawal', + ], + }, + }, + 'markets': { + 'BTC/USD': { 'id': 'btcusd', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'btc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'BTC/EUR': { 'id': 'btceur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'EUR/USD': { 'id': 'eurusd', 'symbol': 'EUR/USD', 'base': 'EUR', 'quote': 'USD', 'baseId': 'eur', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'XRP/USD': { 'id': 'xrpusd', 'symbol': 'XRP/USD', 'base': 'XRP', 'quote': 'USD', 'baseId': 'xrp', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'XRP/EUR': { 'id': 'xrpeur', 'symbol': 'XRP/EUR', 'base': 'XRP', 'quote': 'EUR', 'baseId': 'xrp', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'XRP/BTC': { 'id': 'xrpbtc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/USD': { 'id': 'ltcusd', 'symbol': 'LTC/USD', 'base': 'LTC', 'quote': 'USD', 'baseId': 'ltc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/EUR': { 'id': 'ltceur', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'ltc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, + 'ETH/USD': { 'id': 'ethusd', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD', 'baseId': 'eth', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 }, + 'ETH/EUR': { 'id': 'etheur', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR', 'baseId': 'eth', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 }, + 'ETH/BTC': { 'id': 'ethbtc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 }, + }, + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + if (symbol !== 'BTC/USD') { + throw new ExchangeError (this.id + ' ' + this.version + " fetchOrderBook doesn't support " + symbol + ', use it for BTC/USD only'); + } + await this.loadMarkets (); + const orderbook = await this.publicGetOrderBook (params); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + async fetchTicker (symbol, params = {}) { + if (symbol !== 'BTC/USD') { + throw new ExchangeError (this.id + ' ' + this.version + " fetchTicker doesn't support " + symbol + ', use it for BTC/USD only'); + } + await this.loadMarkets (); + const ticker = await this.publicGetTicker (params); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp2 (trade, 'date', 'datetime'); + const side = (trade['type'] === 0) ? 'buy' : 'sell'; + const orderId = this.safeString (trade, 'order_id'); + if ('currency_pair' in trade) { + if (trade['currency_pair'] in this.markets_by_id) { + market = this.markets_by_id[trade['currency_pair']]; + } + } + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + if (symbol !== 'BTC/USD') { + throw new BadSymbol (this.id + ' ' + this.version + " fetchTrades doesn't support " + symbol + ', use it for BTC/USD only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'time': 'minute', + }; + const response = await this.publicGetTransactions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + const balance = await this.privatePostBalance (params); + const result = { 'info': balance }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const account = this.account (); + account['free'] = this.safeFloat (balance, currencyId + '_available'); + account['used'] = this.safeFloat (balance, currencyId + '_reserved'); + account['total'] = this.safeFloat (balance, currencyId + '_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' ' + this.version + ' accepts limit orders only'); + } + if (symbol !== 'BTC/USD') { + throw new ExchangeError (this.id + ' v1 supports BTC/USD orders only'); + } + await this.loadMarkets (); + const method = 'privatePost' + this.capitalize (side); + const request = { + 'amount': amount, + 'price': price, + }; + const response = await this[method] (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostCancelOrder ({ 'id': id }); + } + + parseOrderStatus (status) { + const statuses = { + 'In Queue': 'open', + 'Open': 'open', + 'Finished': 'closed', + 'Canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrderStatus (response); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const pair = market ? market['id'] : 'all'; + const request = { + 'id': pair, + }; + const response = await this.privatePostOpenOrdersId (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' fetchOrder is not implemented yet'); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret))); + query = this.extend ({ + 'key': this.apiKey, + 'signature': signature.toUpperCase (), + 'nonce': nonce, + }, query); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('status' in response) { + if (response['status'] === 'error') { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bittrex.js b/TaskServer/node_modules/ccxt/js/bittrex.js new file mode 100644 index 000000000..19a8b2961 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bittrex.js @@ -0,0 +1,1446 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, ExchangeNotAvailable, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, DDoSProtection, PermissionDenied, AddressPending, OnMaintenance } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bittrex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bittrex', + 'name': 'Bittrex', + 'countries': [ 'US' ], + 'version': 'v1.1', + 'rateLimit': 1500, + 'certified': true, + // new metainfo interface + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchDepositAddress': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchMyTrades': 'emulated', + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchTickers': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + }, + 'timeframes': { + '1m': 'oneMin', + '5m': 'fiveMin', + '30m': 'thirtyMin', + '1h': 'hour', + '1d': 'day', + }, + 'hostname': 'bittrex.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766352-cf0b3c26-5ed5-11e7-82b7-f3826b7a97d8.jpg', + 'api': { + 'public': 'https://{hostname}/api', + 'account': 'https://{hostname}/api', + 'market': 'https://{hostname}/api', + 'v2': 'https://{hostname}/api/v2.0/pub', + 'v3': 'https://api.bittrex.com/v3', + 'v3public': 'https://api.bittrex.com/v3', + }, + 'www': 'https://bittrex.com', + 'doc': [ + 'https://bittrex.github.io/api/', + 'https://bittrex.github.io/api/v3', + 'https://www.npmjs.com/package/bittrex-node', + ], + 'fees': [ + 'https://bittrex.zendesk.com/hc/en-us/articles/115003684371-BITTREX-SERVICE-FEES-AND-WITHDRAWAL-LIMITATIONS', + 'https://bittrex.zendesk.com/hc/en-us/articles/115000199651-What-fees-does-Bittrex-charge-', + ], + }, + 'api': { + 'v3': { + 'get': [ + 'account', + 'addresses', + 'addresses/{currencySymbol}', + 'balances', + 'balances/{currencySymbol}', + 'currencies', + 'currencies/{symbol}', + 'deposits/open', + 'deposits/closed', + 'deposits/ByTxId/{txId}', + 'deposits/{depositId}', + 'orders/closed', + 'orders/open', + 'orders/{orderId}', + 'ping', + 'subaccounts/{subaccountId}', + 'subaccounts', + 'withdrawals/open', + 'withdrawals/closed', + 'withdrawals/ByTxId/{txId}', + 'withdrawals/{withdrawalId}', + ], + 'post': [ + 'addresses', + 'orders', + 'subaccounts', + 'withdrawals', + ], + 'delete': [ + 'orders/{orderId}', + 'withdrawals/{withdrawalId}', + ], + }, + 'v3public': { + 'get': [ + 'markets', + 'markets/summaries', + 'markets/{marketSymbol}', + 'markets/{marketSymbol}/summary', + 'markets/{marketSymbol}/orderbook', + 'markets/{marketSymbol}/trades', + 'markets/{marketSymbol}/ticker', + 'markets/{marketSymbol}/candles', + ], + }, + 'v2': { + 'get': [ + 'currencies/GetBTCPrice', + 'currencies/GetWalletHealth', + 'general/GetLatestAlert', + 'market/GetTicks', + 'market/GetLatestTick', + 'Markets/GetMarketSummaries', + 'market/GetLatestTick', + ], + }, + 'public': { + 'get': [ + 'currencies', + 'markethistory', + 'markets', + 'marketsummaries', + 'marketsummary', + 'orderbook', + 'ticker', + ], + }, + 'account': { + 'get': [ + 'balance', + 'balances', + 'depositaddress', + 'deposithistory', + 'order', + 'orders', + 'orderhistory', + 'withdrawalhistory', + 'withdraw', + ], + }, + 'market': { + 'get': [ + 'buylimit', + 'buymarket', + 'cancel', + 'openorders', + 'selllimit', + 'sellmarket', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0025, + 'taker': 0.0025, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.0005, + 'LTC': 0.01, + 'DOGE': 2, + 'VTC': 0.02, + 'PPC': 0.02, + 'FTC': 0.2, + 'RDD': 2, + 'NXT': 2, + 'DASH': 0.05, + 'POT': 0.002, + 'BLK': 0.02, + 'EMC2': 0.2, + 'XMY': 0.2, + 'GLD': 0.0002, + 'SLR': 0.2, + 'GRS': 0.2, + }, + 'deposit': { + 'BTC': 0, + 'LTC': 0, + 'DOGE': 0, + 'VTC': 0, + 'PPC': 0, + 'FTC': 0, + 'RDD': 0, + 'NXT': 0, + 'DASH': 0, + 'POT': 0, + 'BLK': 0, + 'EMC2': 0, + 'XMY': 0, + 'GLD': 0, + 'SLR': 0, + 'GRS': 0, + }, + }, + }, + 'exceptions': { + 'exact': { + // 'Call to Cancel was throttled. Try again in 60 seconds.': DDoSProtection, + // 'Call to GetBalances was throttled. Try again in 60 seconds.': DDoSProtection, + 'APISIGN_NOT_PROVIDED': AuthenticationError, + 'INVALID_SIGNATURE': AuthenticationError, + 'INVALID_CURRENCY': ExchangeError, + 'INVALID_PERMISSION': AuthenticationError, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + 'QUANTITY_NOT_PROVIDED': InvalidOrder, + 'MIN_TRADE_REQUIREMENT_NOT_MET': InvalidOrder, + 'ORDER_NOT_OPEN': OrderNotFound, + 'INVALID_ORDER': InvalidOrder, + 'UUID_INVALID': OrderNotFound, + 'RATE_NOT_PROVIDED': InvalidOrder, // createLimitBuyOrder ('ETH/BTC', 1, 0) + 'INVALID_MARKET': BadSymbol, // {"success":false,"message":"INVALID_MARKET","result":null,"explanation":null} + 'WHITELIST_VIOLATION_IP': PermissionDenied, + 'DUST_TRADE_DISALLOWED_MIN_VALUE': InvalidOrder, + 'RESTRICTED_MARKET': BadSymbol, + 'We are down for scheduled maintenance, but we\u2019ll be back up shortly.': OnMaintenance, // {"success":false,"message":"We are down for scheduled maintenance, but we\u2019ll be back up shortly.","result":null,"explanation":null} + }, + 'broad': { + 'throttled': DDoSProtection, + 'problem': ExchangeNotAvailable, + }, + }, + 'options': { + 'parseOrderStatus': false, + 'hasAlreadyAuthenticatedSuccessfully': false, // a workaround for APIKEY_INVALID + 'symbolSeparator': '-', + // With certain currencies, like + // AEON, BTS, GXS, NXT, SBD, STEEM, STR, XEM, XLM, XMR, XRP + // an additional tag / memo / payment id is usually required by exchanges. + // With Bittrex some currencies imply the "base address + tag" logic. + // The base address for depositing is stored on this.currencies[code] + // The base address identifies the exchange as the recipient + // while the tag identifies the user account within the exchange + // and the tag is retrieved with fetchDepositAddress. + 'tag': { + 'NXT': true, // NXT, BURST + 'CRYPTO_NOTE_PAYMENTID': true, // AEON, XMR + 'BITSHAREX': true, // BTS + 'RIPPLE': true, // XRP + 'NEM': true, // XEM + 'STELLAR': true, // XLM + 'STEEM': true, // SBD, GOLOS + // https://github.com/ccxt/ccxt/issues/4794 + // 'LISK': true, // LSK + }, + 'subaccountId': undefined, + // see the implementation of fetchClosedOrdersV3 below + 'fetchClosedOrdersMethod': 'fetch_closed_orders_v3', + 'fetchClosedOrdersFilterBySince': true, + }, + 'commonCurrencies': { + 'BITS': 'SWIFT', + 'CPC': 'Capricoin', + }, + }); + } + + costToPrecision (symbol, cost) { + return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); + } + + feeToPrecision (symbol, fee) { + return this.decimalToPrecision (fee, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); + } + + async fetchMarkets (params = {}) { + const response = await this.v3publicGetMarkets (params); + // + // [ + // { + // "symbol":"LTC-BTC", + // "baseCurrencySymbol":"LTC", + // "quoteCurrencySymbol":"BTC", + // "minTradeSize":"0.01686767", + // "precision":8, + // "status":"ONLINE", // "OFFLINE" + // "createdAt":"2014-02-13T00:00:00Z" + // }, + // { + // "symbol":"VDX-USDT", + // "baseCurrencySymbol":"VDX", + // "quoteCurrencySymbol":"USDT", + // "minTradeSize":"300.00000000", + // "precision":8, + // "status":"ONLINE", // "OFFLINE" + // "createdAt":"2019-05-23T00:41:21.843Z", + // "notice":"USDT has swapped to an ERC20-based token as of August 5, 2019." + // } + // ] + // + const result = []; + // const markets = this.safeValue (response, 'result'); + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const baseId = this.safeString (market, 'baseCurrencySymbol'); + const quoteId = this.safeString (market, 'quoteCurrencySymbol'); + // bittrex v2 uses inverted pairs, v3 uses regular pairs + // we use v3 for fetchMarkets and v2 throughout the rest of this implementation + // therefore we swap the base �→ quote here to be v2-compatible + // https://github.com/ccxt/ccxt/issues/5634 + // const id = this.safeString (market, 'symbol'); + const id = quoteId + this.options['symbolSeparator'] + baseId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const pricePrecision = this.safeInteger (market, 'precision', 8); + const precision = { + 'amount': 8, + 'price': pricePrecision, + }; + const status = this.safeString (market, 'status'); + const active = (status === 'ONLINE'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minTradeSize'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.accountGetBalances (params); + const balances = this.safeValue (response, 'result'); + const result = { 'info': balances }; + const indexed = this.indexBy (balances, 'Currency'); + const currencyIds = Object.keys (indexed); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + const balance = indexed[currencyId]; + account['free'] = this.safeFloat (balance, 'Available'); + account['total'] = this.safeFloat (balance, 'Balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'type': 'both', + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + let orderbook = response['result']; + if ('type' in params) { + if (params['type'] === 'buy') { + orderbook = { + 'buy': response['result'], + 'sell': [], + }; + } else if (params['type'] === 'sell') { + orderbook = { + 'buy': [], + 'sell': response['result'], + }; + } + } + return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity'); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // "success": true, + // "message": "", + // "result": [ + // { + // "Currency": "BTC", + // "CurrencyLong":"Bitcoin", + // "MinConfirmation":2, + // "TxFee":0.00050000, + // "IsActive":true, + // "IsRestricted":false, + // "CoinType":"BITCOIN", + // "BaseAddress":"1N52wHoVR79PMDishab2XmRHsbekCdGquK", + // "Notice":null + // }, + // ..., + // ] + // } + // + const currencies = this.safeValue (response, 'result', []); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'Currency'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (id); + const precision = 8; // default precision, todo: fix "magic constants" + const address = this.safeValue (currency, 'BaseAddress'); + const fee = this.safeFloat (currency, 'TxFee'); // todo: redesign + result[code] = { + 'id': id, + 'code': code, + 'address': address, + 'info': currency, + 'type': currency['CoinType'], + 'name': currency['CurrencyLong'], + 'active': currency['IsActive'], + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': fee, + 'max': undefined, + }, + }, + }; + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "MarketName":"BTC-ETH", + // "High":0.02127099, + // "Low":0.02035064, + // "Volume":10288.40271571, + // "Last":0.02070510, + // "BaseVolume":214.64663206, + // "TimeStamp":"2019-09-18T21:03:59.897", + // "Bid":0.02070509, + // "Ask":0.02070510, + // "OpenBuyOrders":1228, + // "OpenSellOrders":5899, + // "PrevDay":0.02082823, + // "Created":"2015-08-14T09:02:24.817" + // } + // + const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp')); + let symbol = undefined; + const marketId = this.safeString (ticker, 'MarketName'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + symbol = this.parseSymbol (marketId); + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const previous = this.safeFloat (ticker, 'PrevDay'); + const last = this.safeFloat (ticker, 'Last'); + let change = undefined; + let percentage = undefined; + if (last !== undefined) { + if (previous !== undefined) { + change = last - previous; + if (previous > 0) { + percentage = (change / previous) * 100; + } + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'High'), + 'low': this.safeFloat (ticker, 'Low'), + 'bid': this.safeFloat (ticker, 'Bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'Ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': previous, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume'), + 'quoteVolume': this.safeFloat (ticker, 'BaseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketsummaries (params); + const result = this.safeValue (response, 'result'); + const tickers = []; + for (let i = 0; i < result.length; i++) { + const ticker = this.parseTicker (result[i]); + tickers.push (ticker); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketsummary (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result":[ + // { + // "MarketName":"BTC-ETH", + // "High":0.02127099, + // "Low":0.02035064, + // "Volume":10288.40271571, + // "Last":0.02070510, + // "BaseVolume":214.64663206, + // "TimeStamp":"2019-09-18T21:03:59.897", + // "Bid":0.02070509, + // "Ask":0.02070510, + // "OpenBuyOrders":1228, + // "OpenSellOrders":5899, + // "PrevDay":0.02082823, + // "Created":"2015-08-14T09:02:24.817" + // } + // ] + // } + // + const ticker = response['result'][0]; + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (trade['TimeStamp'] + '+00:00'); + let side = undefined; + if (trade['OrderType'] === 'BUY') { + side = 'buy'; + } else if (trade['OrderType'] === 'SELL') { + side = 'sell'; + } + const id = this.safeString2 (trade, 'Id', 'ID'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + const price = this.safeFloat (trade, 'Price'); + const amount = this.safeFloat (trade, 'Quantity'); + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': 'limit', + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarkethistory (this.extend (request, params)); + if ('result' in response) { + if (response['result'] !== undefined) { + return this.parseTrades (response['result'], market, since, limit); + } + } + throw new ExchangeError (this.id + ' fetchTrades() returned undefined response'); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (ohlcv['T'] + '+00:00'); + return [ + timestamp, + ohlcv['O'], + ohlcv['H'], + ohlcv['L'], + ohlcv['C'], + ohlcv['V'], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'tickInterval': this.timeframes[timeframe], + 'marketName': market['id'], + }; + const response = await this.v2GetMarketGetTicks (this.extend (request, params)); + if ('result' in response) { + if (response['result']) { + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + } + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.marketGetOpenorders (this.extend (request, params)); + const result = this.safeValue (response, 'result', []); + const orders = this.parseOrders (result, market, since, limit); + return this.filterBySymbol (orders, symbol); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'marketGet' + this.capitalize (side) + type; + const request = { + 'market': market['id'], + 'quantity': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + }; + // if (type == 'limit') + // order['rate'] = this.priceToPrecision (symbol, price); + const response = await this[method] (this.extend (request, params)); + const orderIdField = this.getOrderIdField (); + const orderId = this.safeString (response['result'], orderIdField); + return { + 'info': response, + 'id': orderId, + 'symbol': symbol, + 'type': type, + 'side': side, + 'status': 'open', + }; + } + + getOrderIdField () { + return 'uuid'; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const orderIdField = this.getOrderIdField (); + const request = {}; + request[orderIdField] = id; + const response = await this.marketGetCancel (this.extend (request, params)); + // + // { + // "success": true, + // "message": "''", + // "result": { + // "uuid": "614c34e4-8d71-11e3-94b5-425861b86ab6" + // } + // } + // + return this.extend (this.parseOrder (response), { + 'status': 'canceled', + }); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://support.bittrex.com/hc/en-us/articles/115003723911 + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + const response = await this.accountGetDeposithistory (this.extend (request, params)); + // + // { success: true, + // message: "", + // result: [ { Id: 22578097, + // Amount: 0.3, + // Currency: "ETH", + // Confirmations: 15, + // LastUpdated: "2018-06-10T07:12:10.57", + // TxId: "0xf50b5ba2ca5438b58f93516eaa523eaf35b4420ca0f24061003df1be7…", + // CryptoAddress: "0xb25f281fa51f1635abd4a60b0870a62d2a7fa404" } ] } + // + // we cannot filter by `since` timestamp, as it isn't set by Bittrex + // see https://github.com/ccxt/ccxt/issues/4067 + // return this.parseTransactions (response['result'], currency, since, limit); + return this.parseTransactions (response['result'], currency, undefined, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://support.bittrex.com/hc/en-us/articles/115003723911 + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + const response = await this.accountGetWithdrawalhistory (this.extend (request, params)); + // + // { + // "success" : true, + // "message" : "", + // "result" : [{ + // "PaymentUuid" : "b32c7a5c-90c6-4c6e-835c-e16df12708b1", + // "Currency" : "BTC", + // "Amount" : 17.00000000, + // "Address" : "1DfaaFBdbB5nrHj87x3NHS4onvw1GPNyAu", + // "Opened" : "2014-07-09T04:24:47.217", + // "Authorized" : true, + // "PendingPayment" : false, + // "TxCost" : 0.00020000, + // "TxId" : null, + // "Canceled" : true, + // "InvalidAddress" : false + // }, { + // "PaymentUuid" : "d193da98-788c-4188-a8f9-8ec2c33fdfcf", + // "Currency" : "XC", + // "Amount" : 7513.75121715, + // "Address" : "TcnSMgAd7EonF2Dgc4c9K14L12RBaW5S5J", + // "Opened" : "2014-07-08T23:13:31.83", + // "Authorized" : true, + // "PendingPayment" : false, + // "TxCost" : 0.00002000, + // "TxId" : "d8a575c2a71c7e56d02ab8e26bb1ef0a2f6cf2094f6ca2116476a569c1e84f6e", + // "Canceled" : false, + // "InvalidAddress" : false + // } + // ] + // } + // + return this.parseTransactions (response['result'], currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // Id: 72578097, + // Amount: 0.3, + // Currency: "ETH", + // Confirmations: 15, + // LastUpdated: "2018-06-17T07:12:14.57", + // TxId: "0xb31b5ba2ca5438b58f93516eaa523eaf35b4420ca0f24061003df1be7…", + // CryptoAddress: "0x2d5f281fa51f1635abd4a60b0870a62d2a7fa404" + // } + // + // fetchWithdrawals + // + // { + // "PaymentUuid" : "e293da98-788c-4188-a8f9-8ec2c33fdfcf", + // "Currency" : "XC", + // "Amount" : 7513.75121715, + // "Address" : "EVnSMgAd7EonF2Dgc4c9K14L12RBaW5S5J", + // "Opened" : "2014-07-08T23:13:31.83", + // "Authorized" : true, + // "PendingPayment" : false, + // "TxCost" : 0.00002000, + // "TxId" : "b4a575c2a71c7e56d02ab8e26bb1ef0a2f6cf2094f6ca2116476a569c1e84f6e", + // "Canceled" : false, + // "InvalidAddress" : false + // } + // + const id = this.safeString2 (transaction, 'Id', 'PaymentUuid'); + const amount = this.safeFloat (transaction, 'Amount'); + const address = this.safeString2 (transaction, 'CryptoAddress', 'Address'); + const txid = this.safeString (transaction, 'TxId'); + const updated = this.parse8601 (this.safeString (transaction, 'LastUpdated')); + const opened = this.parse8601 (this.safeString (transaction, 'Opened')); + const timestamp = opened ? opened : updated; + const type = (opened === undefined) ? 'deposit' : 'withdrawal'; + const currencyId = this.safeString (transaction, 'Currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let status = 'pending'; + if (type === 'deposit') { + // + // deposits numConfirmations never reach the minConfirmations number + // we set all of them to 'ok', otherwise they'd all be 'pending' + // + // const numConfirmations = this.safeInteger (transaction, 'Confirmations', 0); + // const minConfirmations = this.safeInteger (currency['info'], 'MinConfirmation'); + // if (numConfirmations >= minConfirmations) { + // status = 'ok'; + // } + // + status = 'ok'; + } else { + const authorized = this.safeValue (transaction, 'Authorized', false); + const pendingPayment = this.safeValue (transaction, 'PendingPayment', false); + const canceled = this.safeValue (transaction, 'Canceled', false); + const invalidAddress = this.safeValue (transaction, 'InvalidAddress', false); + if (invalidAddress) { + status = 'failed'; + } else if (canceled) { + status = 'canceled'; + } else if (pendingPayment) { + status = 'pending'; + } else if (authorized && (txid !== undefined)) { + status = 'ok'; + } + } + let feeCost = this.safeFloat (transaction, 'TxCost'); + if (feeCost === undefined) { + if (type === 'deposit') { + // according to https://support.bittrex.com/hc/en-us/articles/115000199651-What-fees-does-Bittrex-charge- + feeCost = 0; // FIXME: remove hardcoded value that may change any time + } + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': updated, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + parseSymbol (id) { + const [ quoteId, baseId ] = id.split (this.options['symbolSeparator']); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + return base + '/' + quote; + } + + parseOrder (order, market = undefined) { + if ('marketSymbol' in order) { + return this.parseOrderV3 (order, market); + } else { + return this.parseOrderV2 (order, market); + } + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + if (this.options['fetchClosedOrdersFilterBySince']) { + return super.parseOrders (orders, market, since, limit, params); + } else { + return super.parseOrders (orders, market, undefined, limit, params); + } + } + + parseOrderStatus (status) { + const statuses = { + 'CLOSED': 'closed', + 'OPEN': 'open', + 'CANCELLED': 'canceled', + 'CANCELED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrderV3 (order, market = undefined) { + // + // { + // id: '1be35109-b763-44ce-b6ea-05b6b0735c0c', + // marketSymbol: 'LTC-ETH', + // direction: 'BUY', + // type: 'LIMIT', + // quantity: '0.50000000', + // limit: '0.17846699', + // timeInForce: 'GOOD_TIL_CANCELLED', + // fillQuantity: '0.50000000', + // commission: '0.00022286', + // proceeds: '0.08914915', + // status: 'CLOSED', + // createdAt: '2018-06-23T13:14:28.613Z', + // updatedAt: '2018-06-23T13:14:30.19Z', + // closedAt: '2018-06-23T13:14:30.19Z' + // } + // + const marketSymbol = this.safeString (order, 'marketSymbol'); + let symbol = undefined; + let feeCurrency = undefined; + if (marketSymbol !== undefined) { + const [ baseId, quoteId ] = marketSymbol.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const direction = this.safeStringLower (order, 'direction'); + const createdAt = this.safeString (order, 'createdAt'); + const updatedAt = this.safeString (order, 'updatedAt'); + const closedAt = this.safeString (order, 'closedAt'); + let lastTradeTimestamp = undefined; + if (closedAt !== undefined) { + lastTradeTimestamp = this.parse8601 (closedAt); + } else if (updatedAt) { + lastTradeTimestamp = this.parse8601 (updatedAt); + } + const timestamp = this.parse8601 (createdAt); + const type = this.safeStringLower (order, 'type'); + const quantity = this.safeFloat (order, 'quantity'); + const limit = this.safeFloat (order, 'limit'); + const fillQuantity = this.safeFloat (order, 'fillQuantity'); + const commission = this.safeFloat (order, 'commission'); + const proceeds = this.safeFloat (order, 'proceeds'); + const status = this.safeStringLower (order, 'status'); + let average = undefined; + let remaining = undefined; + if (fillQuantity !== undefined) { + if (proceeds !== undefined) { + if (fillQuantity > 0) { + average = proceeds / fillQuantity; + } else if (proceeds === 0) { + average = 0; + } + } + if (quantity !== undefined) { + remaining = quantity - fillQuantity; + } + } + return { + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': direction, + 'price': limit, + 'cost': proceeds, + 'average': average, + 'amount': quantity, + 'filled': fillQuantity, + 'remaining': remaining, + 'status': status, + 'fee': { + 'cost': commission, + 'currency': feeCurrency, + }, + 'info': order, + }; + } + + parseOrderV2 (order, market = undefined) { + // + // { + // "Uuid": "string (uuid)", + // "OrderUuid": "8925d746-bc9f-4684-b1aa-e507467aaa99", + // "Exchange": "BTC-LTC", + // "OrderType": "string", + // "Quantity": 100000, + // "QuantityRemaining": 100000, + // "Limit": 1e-8, + // "CommissionPaid": 0, + // "Price": 0, + // "PricePerUnit": null, + // "Opened": "2014-07-09T03:55:48.583", + // "Closed": null, + // "CancelInitiated": "boolean", + // "ImmediateOrCancel": "boolean", + // "IsConditional": "boolean" + // } + // + let side = this.safeString2 (order, 'OrderType', 'Type'); + const isBuyOrder = (side === 'LIMIT_BUY') || (side === 'BUY'); + const isSellOrder = (side === 'LIMIT_SELL') || (side === 'SELL'); + if (isBuyOrder) { + side = 'buy'; + } + if (isSellOrder) { + side = 'sell'; + } + // We parse different fields in a very specific order. + // Order might well be closed and then canceled. + let status = undefined; + if (('Opened' in order) && order['Opened']) { + status = 'open'; + } + if (('Closed' in order) && order['Closed']) { + status = 'closed'; + } + if (('CancelInitiated' in order) && order['CancelInitiated']) { + status = 'canceled'; + } + if (('Status' in order) && this.options['parseOrderStatus']) { + status = this.parseOrderStatus (this.safeString (order, 'Status')); + } + let symbol = undefined; + if ('Exchange' in order) { + const marketId = this.safeString (order, 'Exchange'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = this.parseSymbol (marketId); + } + } + } else { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let timestamp = undefined; + const opened = this.safeString (order, 'Opened'); + if (opened !== undefined) { + timestamp = this.parse8601 (opened + '+00:00'); + } + const created = this.safeString (order, 'Created'); + if (created !== undefined) { + timestamp = this.parse8601 (created + '+00:00'); + } + let lastTradeTimestamp = undefined; + const lastTimestamp = this.safeString (order, 'TimeStamp'); + if (lastTimestamp !== undefined) { + lastTradeTimestamp = this.parse8601 (lastTimestamp + '+00:00'); + } + const closed = this.safeString (order, 'Closed'); + if (closed !== undefined) { + lastTradeTimestamp = this.parse8601 (closed + '+00:00'); + } + if (timestamp === undefined) { + timestamp = lastTradeTimestamp; + } + let fee = undefined; + const feeCost = this.safeFloat2 (order, 'Commission', 'CommissionPaid'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + }; + if (market !== undefined) { + fee['currency'] = market['quote']; + } else if (symbol !== undefined) { + const currencyIds = symbol.split ('/'); + const quoteCurrencyId = currencyIds[1]; + fee['currency'] = this.safeCurrencyCode (quoteCurrencyId); + } + } + let price = this.safeFloat (order, 'Limit'); + let cost = this.safeFloat (order, 'Price'); + const amount = this.safeFloat (order, 'Quantity'); + const remaining = this.safeFloat (order, 'QuantityRemaining'); + let filled = undefined; + if (amount !== undefined && remaining !== undefined) { + filled = amount - remaining; + if ((status === 'closed') && (remaining > 0)) { + status = 'canceled'; + } + } + if (!cost) { + if (price && filled) { + cost = price * filled; + } + } + if (!price) { + if (cost && filled) { + price = cost / filled; + } + } + const average = this.safeFloat (order, 'PricePerUnit'); + const id = this.safeString2 (order, 'OrderUuid', 'OrderId'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let response = undefined; + try { + const orderIdField = this.getOrderIdField (); + const request = {}; + request[orderIdField] = id; + response = await this.accountGetOrder (this.extend (request, params)); + } catch (e) { + if (this.last_json_response) { + const message = this.safeString (this.last_json_response, 'message'); + if (message === 'UUID_INVALID') { + throw new OrderNotFound (this.id + ' fetchOrder() error: ' + this.last_http_response); + } + } + throw e; + } + if (!response['result']) { + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + return this.parseOrder (response['result']); + } + + orderToTrade (order) { + // this entire method should be moved to the base class + const timestamp = this.safeInteger2 (order, 'lastTradeTimestamp', 'timestamp'); + return { + 'id': this.safeString (order, 'id'), + 'side': this.safeString (order, 'side'), + 'order': this.safeString (order, 'id'), + 'type': this.safeString (order, 'type'), + 'price': this.safeFloat (order, 'average'), + 'amount': this.safeFloat (order, 'filled'), + 'cost': this.safeFloat (order, 'cost'), + 'symbol': this.safeString (order, 'symbol'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': this.safeValue (order, 'fee'), + 'info': order, + }; + } + + ordersToTrades (orders) { + // this entire method should be moved to the base class + const result = []; + for (let i = 0; i < orders.length; i++) { + result.push (this.orderToTrade (orders[i])); + } + return result; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchClosedOrders (symbol, since, limit, params); + return this.ordersToTrades (orders); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const method = this.safeString (this.options, 'fetchClosedOrdersMethod', 'fetch_closed_orders_v3'); + return await this[method] (symbol, since, limit, params); + } + + async fetchClosedOrdersV2 (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.accountGetOrderhistory (this.extend (request, params)); + const result = this.safeValue (response, 'result', []); + const orders = this.parseOrders (result, market, since, limit); + if (symbol !== undefined) { + return this.filterBySymbol (orders, symbol); + } + return orders; + } + + async fetchClosedOrdersV3 (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['pageSize'] = limit; + } + if (since !== undefined) { + request['startDate'] = this.ymdhms (since, 'T') + 'Z'; + } + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + // because of this line we will have to rethink the entire v3 + // in other words, markets define all the rest of the API + // and v3 market ids are reversed in comparison to v2 + // v3 has to be a completely separate implementation + // otherwise we will have to shuffle symbols and currencies everywhere + // which is prone to errors, as was shown here + // https://github.com/ccxt/ccxt/pull/5219#issuecomment-499646209 + request['marketSymbol'] = market['base'] + '-' + market['quote']; + } + const response = await this.v3GetOrdersClosed (this.extend (request, params)); + const orders = this.parseOrders (response, market, since, limit); + if (symbol !== undefined) { + return this.filterBySymbol (orders, symbol); + } + return orders; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.accountGetDepositaddress (this.extend (request, params)); + // + // { "success": false, "message": "ADDRESS_GENERATING", "result": null } + // + // { success: true, + // message: "", + // result: { Currency: "INCNT", + // Address: "3PHvQt9bK21f7eVQVdJzrNPcsMzXabEA5Ha" } } } + // + let address = this.safeString (response['result'], 'Address'); + const message = this.safeString (response, 'message'); + if (!address || message === 'ADDRESS_GENERATING') { + throw new AddressPending (this.id + ' the address for ' + code + ' is being generated (pending, not ready yet, retry again later)'); + } + let tag = undefined; + if (currency['type'] in this.options['tag']) { + tag = address; + address = currency['address']; + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'quantity': amount, + 'address': address, + }; + if (tag !== undefined) { + request['paymentid'] = tag; + } + const response = await this.accountGetWithdraw (this.extend (request, params)); + const result = this.safeValue (response, 'result', {}); + const id = this.safeString (result, 'uuid'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }) + '/'; + if (api !== 'v2' && api !== 'v3' && api !== 'v3public') { + url += this.version + '/'; + } + if (api === 'public') { + url += api + '/' + method.toLowerCase () + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v3public') { + url += path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v2') { + url += path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'v3') { + url += path; + if (Object.keys (params).length) { + url += '?' + this.rawencode (params); + } + const contentHash = this.hash (this.encode (''), 'sha512', 'hex'); + const timestamp = this.milliseconds ().toString (); + let auth = timestamp + url + method + contentHash; + const subaccountId = this.safeValue (this.options, 'subaccountId'); + if (subaccountId !== undefined) { + auth += subaccountId; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); + headers = { + 'Api-Key': this.apiKey, + 'Api-Timestamp': timestamp, + 'Api-Content-Hash': contentHash, + 'Api-Signature': signature, + }; + if (subaccountId !== undefined) { + headers['Api-Subaccount-Id'] = subaccountId; + } + } else { + this.checkRequiredCredentials (); + url += api + '/'; + if (((api === 'account') && (path !== 'withdraw')) || (path === 'openorders')) { + url += method.toLowerCase (); + } + const request = { + 'apikey': this.apiKey, + }; + const disableNonce = this.safeValue (this.options, 'disableNonce'); + if ((disableNonce === undefined) || !disableNonce) { + request['nonce'] = this.nonce (); + } + url += path + '?' + this.urlencode (this.extend (request, params)); + const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512'); + headers = { 'apisign': signature }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // { success: false, message: "message" } + // + if (body[0] === '{') { + let success = this.safeValue (response, 'success'); + if (success === undefined) { + // throw new ExchangeError (this.id + ' malformed response ' + this.json (response)); + return; + } + if (typeof success === 'string') { + // bleutrade uses string instead of boolean + success = (success === 'true') ? true : false; + } + if (!success) { + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + if (message === 'APIKEY_INVALID') { + if (this.options['hasAlreadyAuthenticatedSuccessfully']) { + throw new DDoSProtection (feedback); + } else { + throw new AuthenticationError (feedback); + } + } + // https://github.com/ccxt/ccxt/issues/4932 + // the following two lines are now redundant, see line 171 in describe() + // + // if (message === 'DUST_TRADE_DISALLOWED_MIN_VALUE_50K_SAT') + // throw new InvalidOrder (this.id + ' order cost should be over 50k satoshi ' + this.json (response)); + // + if (message === 'INVALID_ORDER') { + // Bittrex will return an ambiguous INVALID_ORDER message + // upon canceling already-canceled and closed orders + // therefore this special case for cancelOrder + // let url = 'https://bittrex.com/api/v1.1/market/cancel?apikey=API_KEY&uuid=ORDER_UUID' + const cancel = 'cancel'; + const indexOfCancel = url.indexOf (cancel); + if (indexOfCancel >= 0) { + const urlParts = url.split ('?'); + const numParts = urlParts.length; + if (numParts > 1) { + const query = urlParts[1]; + const params = query.split ('&'); + const numParams = params.length; + let orderId = undefined; + for (let i = 0; i < numParams; i++) { + const param = params[i]; + const keyValue = param.split ('='); + if (keyValue[0] === 'uuid') { + orderId = keyValue[1]; + break; + } + } + if (orderId !== undefined) { + throw new OrderNotFound (this.id + ' cancelOrder ' + orderId + ' ' + this.json (response)); + } else { + throw new OrderNotFound (this.id + ' cancelOrder ' + this.json (response)); + } + } + } + } + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + if (message !== undefined) { + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + throw new ExchangeError (feedback); + } + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + // a workaround for APIKEY_INVALID + if ((api === 'account') || (api === 'market')) { + this.options['hasAlreadyAuthenticatedSuccessfully'] = true; + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bitz.js b/TaskServer/node_modules/ccxt/js/bitz.js new file mode 100644 index 000000000..7b6da7c50 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bitz.js @@ -0,0 +1,1211 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, PermissionDenied, InvalidOrder, AuthenticationError, InsufficientFunds, OrderNotFound, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bitz extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bitz', + 'name': 'Bit-Z', + 'countries': [ 'HK' ], + 'rateLimit': 2000, + 'version': 'v2', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrders': true, + 'fetchOrder': true, + 'createMarketOrder': false, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '60min', + '4h': '4hour', + '1d': '1day', + '5d': '5day', + '1w': '1week', + '1M': '1mon', + }, + 'hostname': 'apiv2.bitz.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/35862606-4f554f14-0b5d-11e8-957d-35058c504b6f.jpg', + 'api': { + 'market': 'https://{hostname}', + 'trade': 'https://{hostname}', + 'assets': 'https://{hostname}', + }, + 'www': 'https://www.bitz.com', + 'doc': 'https://apidoc.bitz.com/en/', + 'fees': 'https://www.bitz.com/fee?type=1', + 'referral': 'https://u.bitz.com/register?invite_code=1429193', + }, + 'api': { + 'market': { + 'get': [ + 'ticker', + 'depth', + 'order', // trades + 'tickerall', + 'kline', + 'symbolList', + 'currencyRate', + 'currencyCoinRate', + 'coinRate', + ], + }, + 'trade': { + 'post': [ + 'addEntrustSheet', + 'cancelEntrustSheet', + 'cancelAllEntrustSheet', + 'getUserHistoryEntrustSheet', // closed orders + 'getUserNowEntrustSheet', // open orders + 'getEntrustSheetInfo', // order + 'depositOrWithdraw', // transactions + ], + }, + 'assets': { + 'post': [ + 'getUserAssets', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.002, + 'taker': 0.002, + }, + 'funding': { + 'withdraw': { + 'BTC': '0.5%', + 'DKKT': '0.5%', + 'ETH': 0.01, + 'USDT': '0.5%', + 'LTC': '0.5%', + 'FCT': '0.5%', + 'LSK': '0.5%', + 'HXI': '0.8%', + 'ZEC': '0.5%', + 'DOGE': '0.5%', + 'MZC': '0.5%', + 'ETC': '0.5%', + 'GXS': '0.5%', + 'XPM': '0.5%', + 'PPC': '0.5%', + 'BLK': '0.5%', + 'XAS': '0.5%', + 'HSR': '0.5%', + 'NULS': 5.0, + 'VOISE': 350.0, + 'PAY': 1.5, + 'EOS': 0.6, + 'YBCT': 35.0, + 'OMG': 0.3, + 'OTN': 0.4, + 'BTX': '0.5%', + 'QTUM': '0.5%', + 'DASH': '0.5%', + 'GAME': '0.5%', + 'BCH': '0.5%', + 'GNT': 9.0, + 'SSS': 1500.0, + 'ARK': '0.5%', + 'PART': '0.5%', + 'LEO': '0.5%', + 'DGB': '0.5%', + 'ZSC': 130.0, + 'VIU': 350.0, + 'BTG': '0.5%', + 'ARN': 10.0, + 'VTC': '0.5%', + 'BCD': '0.5%', + 'TRX': 200.0, + 'HWC': '0.5%', + 'UNIT': '0.5%', + 'OXY': '0.5%', + 'MCO': 0.3500, + 'SBTC': '0.5%', + 'BCX': '0.5%', + 'ETF': '0.5%', + 'PYLNT': 0.4000, + 'XRB': '0.5%', + 'ETP': '0.5%', + }, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'options': { + 'fetchOHLCVVolume': true, + 'fetchOHLCVWarning': true, + 'lastNonceTimestamp': 0, + }, + 'commonCurrencies': { + // https://github.com/ccxt/ccxt/issues/3881 + // https://support.bit-z.pro/hc/en-us/articles/360007500654-BOX-BOX-Token- + 'BOX': 'BOX Token', + 'XRB': 'NANO', + 'PXC': 'Pixiecoin', + 'VTC': 'VoteCoin', + 'TTC': 'TimesChain', + }, + 'exceptions': { + // '200': Success + '-102': ExchangeError, // Invalid parameter + '-103': AuthenticationError, // Verification failed + '-104': ExchangeNotAvailable, // Network Error-1 + '-105': AuthenticationError, // Invalid api signature + '-106': ExchangeNotAvailable, // Network Error-2 + '-109': AuthenticationError, // Invalid scretKey + '-110': DDoSProtection, // The number of access requests exceeded + '-111': PermissionDenied, // Current IP is not in the range of trusted IP + '-112': ExchangeNotAvailable, // Service is under maintenance + '-100015': AuthenticationError, // Trade password error + '-100044': ExchangeError, // Fail to request data + '-100101': ExchangeError, // Invalid symbol + '-100201': ExchangeError, // Invalid symbol + '-100301': ExchangeError, // Invalid symbol + '-100401': ExchangeError, // Invalid symbol + '-100302': ExchangeError, // Type of K-line error + '-100303': ExchangeError, // Size of K-line error + '-200003': AuthenticationError, // Please set trade password + '-200005': PermissionDenied, // This account can not trade + '-200025': ExchangeNotAvailable, // Temporary trading halt + '-200027': InvalidOrder, // Price Error + '-200028': InvalidOrder, // Amount must be greater than 0 + '-200029': InvalidOrder, // Number must be between %s and %d + '-200030': InvalidOrder, // Over price range + '-200031': InsufficientFunds, // Insufficient assets + '-200032': ExchangeError, // System error. Please contact customer service + '-200033': ExchangeError, // Fail to trade + '-200034': OrderNotFound, // The order does not exist + '-200035': OrderNotFound, // Cancellation error, order filled + '-200037': InvalidOrder, // Trade direction error + '-200038': ExchangeError, // Trading Market Error + '-200055': OrderNotFound, // Order record does not exist + '-300069': AuthenticationError, // api_key is illegal + '-300101': ExchangeError, // Transaction type error + '-300102': InvalidOrder, // Price or number cannot be less than 0 + '-300103': AuthenticationError, // Trade password error + '-301001': ExchangeNotAvailable, // Network Error-3 + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.marketGetSymbolList (params); + // + // { status: 200, + // msg: "", + // data: { ltc_btc: { id: "1", + // name: "ltc_btc", + // coinFrom: "ltc", + // coinTo: "btc", + // numberFloat: "4", + // priceFloat: "8", + // status: "1", + // minTrade: "0.010", + // maxTrade: "500000000.000" }, + // qtum_usdt: { id: "196", + // name: "qtum_usdt", + // coinFrom: "qtum", + // coinTo: "usdt", + // numberFloat: "4", + // priceFloat: "2", + // status: "1", + // minTrade: "0.100", + // maxTrade: "500000000.000" }, }, + // time: 1535969146, + // microtime: "0.66955600 1535969146", + // source: "api" } + // + const markets = this.safeValue (response, 'data'); + const ids = Object.keys (markets); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = markets[id]; + const numericId = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'coinFrom'); + const quoteId = this.safeString (market, 'coinTo'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'numberFloat'), + 'price': this.safeInteger (market, 'priceFloat'), + }; + result.push ({ + 'info': market, + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minTrade'), + 'max': this.safeFloat (market, 'maxTrade'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.assetsPostGetUserAssets (params); + // + // { + // status: 200, + // msg: "", + // data: { + // cny: 0, + // usd: 0, + // btc_total: 0, + // info: [{ + // "name": "zpr", + // "num": "37.49067275", + // "over": "37.49067275", + // "lock": "0.00000000", + // "btc": "0.00000000", + // "usd": "0.00000000", + // "cny": "0.00000000", + // }], + // }, + // time: 1535983966, + // microtime: "0.70400500 1535983966", + // source: "api", + // } + // + const balances = this.safeValue (response['data'], 'info'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'name'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'lock'); + account['total'] = this.safeFloat (balance, 'num'); + account['free'] = this.safeFloat (balance, 'over'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseTicker (ticker, market = undefined) { + // + // { symbol: "eth_btc", + // quoteVolume: "3905.72", + // volume: "97058.21", + // priceChange: "-1.72", + // priceChange24h: "-1.65", + // askPrice: "0.03971272", + // askQty: "0.0663", + // bidPrice: "0.03961469", + // bidQty: "19.5451", + // open: "0.04036769", + // high: "0.04062988", + // low: "0.03956123", + // now: "0.03970100", + // firstId: 115567767, + // lastId: 115795316, + // dealCount: 14078, + // numberPrecision: 4, + // pricePrecision: 8, + // cny: "1959.05", + // usd: "287.10", + // krw: "318655.82" } + // + const timestamp = undefined; + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'now'); + const open = this.safeFloat (ticker, 'open'); + let change = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': this.safeFloat (ticker, 'bidQty'), + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': this.safeFloat (ticker, 'askQty'), + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': this.safeFloat (ticker, 'priceChange24h'), + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + parseMicrotime (microtime) { + if (microtime === undefined) { + return microtime; + } + const parts = microtime.split (' '); + const milliseconds = parseFloat (parts[0]); + const seconds = parseInt (parts[1]); + const total = this.sum (seconds, milliseconds); + return parseInt (total * 1000); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetTicker (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { symbol: "eth_btc", + // quoteVolume: "3905.72", + // volume: "97058.21", + // priceChange: "-1.72", + // priceChange24h: "-1.65", + // askPrice: "0.03971272", + // askQty: "0.0663", + // bidPrice: "0.03961469", + // bidQty: "19.5451", + // open: "0.04036769", + // high: "0.04062988", + // low: "0.03956123", + // now: "0.03970100", + // firstId: 115567767, + // lastId: 115795316, + // dealCount: 14078, + // numberPrecision: 4, + // pricePrecision: 8, + // cny: "1959.05", + // usd: "287.10", + // krw: "318655.82" }, + // time: 1535970397, + // microtime: "0.76341900 1535970397", + // source: "api" } + // + const ticker = this.parseTicker (response['data'], market); + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + return this.extend (ticker, { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + }); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['symbols'] = ids.join (','); + } + const response = await this.marketGetTickerall (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { ela_btc: { symbol: "ela_btc", + // quoteVolume: "0.00", + // volume: "3.28", + // priceChange: "0.00", + // priceChange24h: "0.00", + // askPrice: "0.00147984", + // askQty: "5.4580", + // bidPrice: "0.00120230", + // bidQty: "12.5384", + // open: "0.00149078", + // high: "0.00149078", + // low: "0.00149078", + // now: "0.00149078", + // firstId: 115581219, + // lastId: 115581219, + // dealCount: 1, + // numberPrecision: 4, + // pricePrecision: 8, + // cny: "73.66", + // usd: "10.79", + // krw: "11995.03" } }, + // time: 1535971578, + // microtime: "0.39854200 1535971578", + // source: "api" } + // + const tickers = this.safeValue (response, 'data'); + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + const result = {}; + const ids = Object.keys (tickers); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let ticker = tickers[id]; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + } + ticker = this.parseTicker (tickers[id], market); + let symbol = ticker['symbol']; + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol !== undefined) { + result[symbol] = this.extend (ticker, { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + }); + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.marketGetDepth (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { asks: [ ["10.00000000", "0.4426", "4.4260"], + // ["1.00000000", "0.8339", "0.8339"], + // ["0.91700000", "0.0500", "0.0458"], + // ["0.20000000", "0.1000", "0.0200"], + // ["0.03987120", "16.1262", "0.6429"], + // ["0.03986120", "9.7523", "0.3887"] ], + // bids: [ ["0.03976145", "0.0359", "0.0014"], + // ["0.03973401", "20.9493", "0.8323"], + // ["0.03967970", "0.0328", "0.0013"], + // ["0.00000002", "10000.0000", "0.0002"], + // ["0.00000001", "231840.7500", "0.0023"] ], + // coinPair: "eth_btc" }, + // time: 1535974778, + // microtime: "0.04017400 1535974778", + // source: "api" } + // + const orderbook = this.safeValue (response, 'data'); + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { id: 115807453, + // t: "19:36:24", + // T: 1535974584, + // p: "0.03983296", + // n: "0.1000", + // s: "buy" }, + // + const id = this.safeString (trade, 'id'); + const timestamp = this.safeTimestamp (trade, 'T'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'p'); + const amount = this.safeFloat (trade, 'n'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = this.priceToPrecision (symbol, amount * price); + } + } + const side = this.safeString (trade, 's'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetOrder (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: [ { id: 115807453, + // t: "19:36:24", + // T: 1535974584, + // p: "0.03983296", + // n: "0.1000", + // s: "buy" }, + // { id: 115806811, + // t: "19:33:19", + // T: 1535974399, + // p: "0.03981135", + // n: "9.4612", + // s: "sell" } ], + // time: 1535974583, + // microtime: "0.57118100 1535974583", + // source: "api" } + // + return this.parseTrades (response['data'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { time: "1535973420000", + // open: "0.03975084", + // high: "0.03975084", + // low: "0.03967700", + // close: "0.03967700", + // volume: "12.4733", + // datetime: "2018-09-03 19:17:00" } + // + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const duration = this.parseTimeframe (timeframe) * 1000; + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'resolution': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['size'] = Math.min (limit, 300); // 1-300 + if (since !== undefined) { + request['to'] = this.sum (since, limit * duration * 1000); + } + } else { + if (since !== undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a limit argument if the since argument is specified'); + } + } + const response = await this.marketGetKline (this.extend (request, params)); + // + // { status: 200, + // msg: "", + // data: { bars: [ { time: "1535973420000", + // open: "0.03975084", + // high: "0.03975084", + // low: "0.03967700", + // close: "0.03967700", + // volume: "12.4733", + // datetime: "2018-09-03 19:17:00" }, + // { time: "1535955480000", + // open: "0.04009900", + // high: "0.04016745", + // low: "0.04009900", + // close: "0.04012074", + // volume: "74.4803", + // datetime: "2018-09-03 14:18:00" } ], + // resolution: "1min", + // symbol: "eth_btc", + // from: "1535973420000", + // to: "1535955480000", + // size: 300 }, + // time: 1535973435, + // microtime: "0.56462100 1535973435", + // source: "api" } + // + const bars = this.safeValue (response['data'], 'bars', undefined); + if (bars === undefined) { + return []; + } + return this.parseOHLCVs (bars, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', // partially filled + '2': 'closed', // filled + '3': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "id": "693248739", // order id + // "uId": "2074056", // uid + // "price": "100", // price + // "number": "10", // number + // "numberOver": "10", // undealed + // "flag": "sale", // flag + // "status": "0", // unfilled + // "coinFrom": "vtc", + // "coinTo": "dkkt", + // "numberDeal": "0" // dealed + // } + // + const id = this.safeString (order, 'id'); + let symbol = undefined; + if (market === undefined) { + const baseId = this.safeString (order, 'coinFrom'); + const quoteId = this.safeString (order, 'coinTo'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const marketId = baseId + '_' + quoteId; + if (marketId in this.markets_by_id) { + market = this.safeValue (this.markets_by_id, marketId); + } else { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeString (order, 'flag'); + if (side !== undefined) { + side = (side === 'sale') ? 'sell' : 'buy'; + } + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'number'); + const remaining = this.safeFloat (order, 'numberOver'); + const filled = this.safeFloat (order, 'numberDeal'); + let timestamp = this.safeInteger (order, 'timestamp'); + if (timestamp === undefined) { + timestamp = this.safeTimestamp (order, 'created'); + } + let cost = this.safeFloat (order, 'orderTotalPrice'); + if (price !== undefined) { + if (filled !== undefined) { + cost = filled * price; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' createOrder allows limit orders only'); + } + const market = this.market (symbol); + const orderType = (side === 'buy') ? '1' : '2'; + if (!this.password) { + throw new ExchangeError (this.id + ' createOrder() requires you to set exchange.password = "YOUR_TRADING_PASSWORD" (a trade password is NOT THE SAME as your login password)'); + } + const request = { + 'symbol': market['id'], + 'type': orderType, + 'price': this.priceToPrecision (symbol, price), + 'number': this.amountToPrecision (symbol, amount), + 'tradePwd': this.password, + }; + const response = await this.tradePostAddEntrustSheet (this.extend (request, params)); + // + // { + // "status": 200, + // "msg": "", + // "data": { + // "id": "693248739", // order id + // "uId": "2074056", // uid + // "price": "100", // price + // "number": "10", // number + // "numberOver": "10", // undealed + // "flag": "sale", // flag + // "status": "0", // unfilled + // "coinFrom": "vtc", + // "coinTo": "dkkt", + // "numberDeal": "0" // dealed + // }, + // "time": "1533035297", + // "microtime": "0.41892000 1533035297", + // "source": "api", + // } + // + const timestamp = this.parseMicrotime (this.safeString (response, 'microtime')); + const order = this.extend ({ + 'timestamp': timestamp, + }, response['data']); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'entrustSheetId': id, + }; + const response = await this.tradePostCancelEntrustSheet (this.extend (request, params)); + // + // { + // "status":200, + // "msg":"", + // "data":{ + // "updateAssetsData":{ + // "coin":"bz", + // "over":"1000.00000000", + // "lock":"-1000.00000000" + // }, + // "assetsInfo":{ + // "coin":"bz", + // "over":"9999.99999999", + // "lock":"9999.99999999" + // } + // }, + // "time":"1535464383", + // "microtime":"0.91558000 1535464383", + // "source":"api" + // } + // + return response; + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'ids': ids.join (','), + }; + const response = await this.tradePostCancelEntrustSheet (this.extend (request, params)); + // + // { + // "status":200, + // "msg":"", + // "data":{ + // "744173808":{ + // "updateAssetsData":{ + // "coin":"bz", + // "over":"100.00000000", + // "lock":"-100.00000000" + // }, + // "assetsInfo":{ + // "coin":"bz", + // "over":"899.99999999", + // "lock":"19099.99999999" + // } + // }, + // "744173809":{ + // "updateAssetsData":{ + // "coin":"bz", + // "over":"100.00000000", + // "lock":"-100.00000000" + // }, + // "assetsInfo":{ + // "coin":"bz", + // "over":"999.99999999", + // "lock":"18999.99999999" + // } + // } + // }, + // "time":"1535525649", + // "microtime":"0.05009400 1535525649", + // "source":"api" + // } + // + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'entrustSheetId': id, + }; + const response = await this.tradePostGetEntrustSheetInfo (this.extend (request, params)); + // + // { + // "status":200, + // "msg":"", + // "data":{ + // "id":"708279852", + // "uId":"2074056", + // "price":"100.00000000", + // "number":"10.0000", + // "total":"0.00000000", + // "numberOver":"10.0000", + // "numberDeal":"0.0000", + // "flag":"sale", + // "status":"0", //0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled + // "coinFrom":"bz", + // "coinTo":"usdt", + // "orderTotalPrice":"0", + // "created":"1533279876" + // }, + // "time":"1533280294", + // "microtime":"0.36859200 1533280294", + // "source":"api" + // } + // + return this.parseOrder (response['data']); + } + + async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coinFrom': market['baseId'], + 'coinTo': market['quoteId'], + // 'type': 1, // optional integer, 1 = buy, 2 = sell + // 'page': 1, // optional integer + // 'pageSize': 100, // optional integer, max 100 + // 'startTime': 1510235730, // optional integer timestamp in seconds + // 'endTime': 1510235730, // optional integer timestamp in seconds + }; + if (limit !== undefined) { + request['page'] = 1; + request['pageSize'] = limit; + } + if (since !== undefined) { + request['startTime'] = parseInt (since / 1000); + // request['endTime'] = parseInt (since / 1000); + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "status": 200, + // "msg": "", + // "data": { + // "data": [ + // { + // "id": "693248739", + // "uid": "2074056", + // "price": "100.00000000", + // "number": "10.0000", + // "total": "0.00000000", + // "numberOver": "0.0000", + // "numberDeal": "0.0000", + // "flag": "sale", + // "status": "3", // 0:unfilled, 1:partial deal, 2:all transactions, 3:already cancelled + // "isNew": "N", + // "coinFrom": "vtc", + // "coinTo": "dkkt", + // "created": "1533035300", + // }, + // { + // "id": "723086996", + // "uid": "2074056", + // "price": "100.00000000", + // "number": "10.0000", + // "total": "0.00000000", + // "numberOver": "0.0000", + // "numberDeal": "0.0000", + // "flag": "sale", + // "status": "3", + // "isNew": "N", + // "coinFrom": "bz", + // "coinTo": "usdt", + // "created": "1533523568", + // }, + // ], + // "pageInfo": { + // "limit": "10", + // "offest": "0", + // "current_page": "1", + // "page_size": "10", + // "total_count": "17", + // "page_count": "2", + // } + // }, + // "time": "1533279329", + // "microtime": "0.15305300 1533279329", + // "source": "api" + // } + // + const orders = this.safeValue (response['data'], 'data', []); + return this.parseOrders (orders, undefined, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersWithMethod ('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersWithMethod ('tradePostGetUserNowEntrustSheet', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersWithMethod ('tradePostGetUserHistoryEntrustSheet', symbol, since, limit, params); + } + + parseTransactionStatus (status) { + const statuses = { + '1': 'pending', + '2': 'pending', + '3': 'pending', + '4': 'ok', + '5': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // "id": '96275', + // "uid": '2109073', + // "wallet": '0xf4c4141c0127bc37b1d0c409a091920eba13ada7', + // "txid": '0xb7adfa52aa566f9ac112e3c01f77bd91179b19eab12092a9a5a8b33d5086e31d', + // "confirm": '12', + // "number": '0.50000000', + // "status": 4, + // "updated": '1534944168605', + // "addressUrl": 'https://etherscan.io/address/', + // "txidUrl": 'https://etherscan.io/tx/', + // "description": 'Ethereum', + // "coin": 'eth', + // "memo": '' + // } + // + // { + // "id":"397574", + // "uid":"2033056", + // "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k", + // "txid":"", + // "confirm":"0", + // "number":"1000.00000000", + // "status":1, + // "updated":"0", + // "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=", + // "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=", + // "description":"Tether", + // "coin":"usdt", + // "memo":"" + // } + // + // { + // "id":"153606", + // "uid":"2033056", + // "wallet":"1AG1gZvQAYu3WBvgg7p4BMMghQD2gE693k", + // "txid":"aa2b179f84cd6dedafd41845e0fbf7f01e14c0d71ea3140d03d6f5a9ccd93199", + // "confirm":"0", + // "number":"761.11110000", + // "status":4, + // "updated":"1536726133579", + // "addressUrl":"http://omniexplorer.info/lookupadd.aspx?address=", + // "txidUrl":"http://omniexplorer.info/lookuptx.aspx?txid=", + // "description":"Tether", + // "coin":"usdt", + // "memo":"" + // } + // + let timestamp = this.safeInteger (transaction, 'updated'); + if (timestamp === 0) { + timestamp = undefined; + } + const currencyId = this.safeString (transaction, 'coin'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeStringLower (transaction, 'type'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + return { + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'txid'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'wallet'), + 'tag': this.safeString (transaction, 'memo'), + 'type': type, + 'amount': this.safeFloat (transaction, 'number'), + 'currency': code, + 'status': status, + 'updated': timestamp, + 'fee': undefined, + 'info': transaction, + }; + } + + parseTransactionsByType (type, transactions, code = undefined, since = undefined, limit = undefined) { + const result = []; + for (let i = 0; i < transactions.length; i++) { + const transaction = this.parseTransaction (this.extend ({ + 'type': type, + }, transactions[i])); + result.push (transaction); + } + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + parseTransactionType (type) { + const types = { + 'deposit': 1, + 'withdrawal': 2, + }; + return this.safeInteger (types, type, type); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsForType ('deposit', code, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsForType ('withdrawal', code, since, limit, params); + } + + async fetchTransactionsForType (type, code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency `code` argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + 'type': this.parseTransactionType (type), + }; + if (since !== undefined) { + request['startTime'] = parseInt (since / 1000).toString (); + } + if (limit !== undefined) { + request['page'] = 1; + request['pageSize'] = limit; + } + const response = await this.tradePostDepositOrWithdraw (this.extend (request, params)); + const transactions = this.safeValue (response['data'], 'data', []); + return this.parseTransactionsByType (type, transactions, code, since, limit); + } + + nonce () { + const currentTimestamp = this.seconds (); + if (currentTimestamp > this.options['lastNonceTimestamp']) { + this.options['lastNonceTimestamp'] = currentTimestamp; + this.options['lastNonce'] = 100000; + } + this.options['lastNonce'] = this.sum (this.options['lastNonce'], 1); + return this.options['lastNonce']; + } + + sign (path, api = 'market', method = 'GET', params = {}, headers = undefined, body = undefined) { + const baseUrl = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname }); + let url = baseUrl + '/' + this.capitalize (api) + '/' + path; + let query = undefined; + if (api === 'market') { + query = this.urlencode (params); + if (query.length) { + url += '?' + query; + } + } else { + this.checkRequiredCredentials (); + body = this.rawencode (this.keysort (this.extend ({ + 'apiKey': this.apiKey, + 'timeStamp': this.seconds (), + 'nonce': this.nonce (), + }, params))); + body += '&sign=' + this.hash (this.encode (body + this.secret)); + headers = { 'Content-type': 'application/x-www-form-urlencoded' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const status = this.safeString (response, 'status'); + if (status !== undefined) { + const feedback = this.id + ' ' + body; + // + // {"status":-107,"msg":"","data":"","time":1535968848,"microtime":"0.89092200 1535968848","source":"api"} + // + if (status === '200') { + // + // {"status":200,"msg":"","data":-200031,"time":1535999806,"microtime":"0.85476800 1535999806","source":"api"} + // + const code = this.safeInteger (response, 'data'); + if (code !== undefined) { + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); + } else { + return; // no error + } + } + this.throwExactlyMatchedException (this.exceptions, status, feedback); + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bl3p.js b/TaskServer/node_modules/ccxt/js/bl3p.js new file mode 100644 index 000000000..2cc0fce07 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bl3p.js @@ -0,0 +1,233 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class bl3p extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bl3p', + 'name': 'BL3P', + 'countries': [ 'NL', 'EU' ], // Netherlands, EU + 'rateLimit': 1000, + 'version': '1', + 'comment': 'An exchange market by BitonicNL', + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg', + 'api': 'https://api.bl3p.eu', + 'www': [ + 'https://bl3p.eu', + 'https://bitonic.nl', + ], + 'doc': [ + 'https://github.com/BitonicNL/bl3p-api/tree/master/docs', + 'https://bl3p.eu/api', + 'https://bitonic.nl/en/api', + ], + }, + 'api': { + 'public': { + 'get': [ + '{market}/ticker', + '{market}/orderbook', + '{market}/trades', + ], + }, + 'private': { + 'post': [ + '{market}/money/depth/full', + '{market}/money/order/add', + '{market}/money/order/cancel', + '{market}/money/order/result', + '{market}/money/orders', + '{market}/money/orders/history', + '{market}/money/trades/fetch', + 'GENMKT/money/info', + 'GENMKT/money/deposit_address', + 'GENMKT/money/new_deposit_address', + 'GENMKT/money/wallet/history', + 'GENMKT/money/withdraw', + ], + }, + }, + 'markets': { + 'BTC/EUR': { 'id': 'BTCEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 }, + 'LTC/EUR': { 'id': 'LTCEUR', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'LTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGENMKTMoneyInfo (params); + const data = this.safeValue (response, 'data', {}); + const wallets = this.safeValue (data, 'wallets'); + const result = { 'info': data }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const wallet = this.safeValue (wallets, currencyId, {}); + const available = this.safeValue (wallet, 'available', {}); + const balance = this.safeValue (wallet, 'balance', {}); + const account = this.account (); + account['free'] = this.safeFloat (available, 'value'); + account['total'] = this.safeFloat (balance, 'value'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1) { + return [ + bidask[priceKey] / 100000.0, + bidask[amountKey] / 100000000.0, + ]; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketOrderbook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data'); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price_int', 'amount_int'); + } + + async fetchTicker (symbol, params = {}) { + const request = { + 'market': this.marketId (symbol), + }; + const ticker = await this.publicGetMarketTicker (this.extend (request, params)); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker['volume'], '24h'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const id = this.safeString (trade, 'trade_id'); + const timestamp = this.safeInteger (trade, 'date'); + let price = this.safeFloat (trade, 'price_int'); + if (price !== undefined) { + price /= 100000.0; + } + let amount = this.safeFloat (trade, 'amount_int'); + if (amount !== undefined) { + amount /= 100000000.0; + } + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + const market = this.market (symbol); + const response = await this.publicGetMarketTrades (this.extend ({ + 'market': market['id'], + }, params)); + const result = this.parseTrades (response['data']['trades'], market, since, limit); + return result; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + const market = this.market (symbol); + const order = { + 'market': market['id'], + 'amount_int': parseInt (amount * 100000000), + 'fee_currency': market['quote'], + 'type': (side === 'buy') ? 'bid' : 'ask', + }; + if (type === 'limit') { + order['price_int'] = parseInt (price * 100000.0); + } + const response = await this.privatePostMarketMoneyOrderAdd (this.extend (order, params)); + const orderId = this.safeString (response['data'], 'order_id'); + return { + 'info': response, + 'id': orderId, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order_id': id, + }; + return await this.privatePostMarketMoneyOrderCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = this.implodeParams (path, params); + let url = this.urls['api'] + '/' + this.version + '/' + request; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); + const secret = this.base64ToBinary (this.secret); + // eslint-disable-next-line quotes + const auth = request + "\0" + body; + const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Rest-Key': this.apiKey, + 'Rest-Sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bleutrade.js b/TaskServer/node_modules/ccxt/js/bleutrade.js new file mode 100644 index 000000000..a75351e38 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bleutrade.js @@ -0,0 +1,781 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const bittrex = require ('./bittrex.js'); +const { ExchangeError, AuthenticationError, InvalidOrder, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bleutrade extends bittrex { + describe () { + const timeframes = { + '15m': '15m', + '20m': '20m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '3h': '3h', + '4h': '4h', + '6h': '6h', + '8h': '8h', + '12h': '12h', + '1d': '1d', + }; + const result = this.deepExtend (super.describe (), { + 'id': 'bleutrade', + 'name': 'Bleutrade', + 'countries': [ 'BR' ], // Brazil + 'rateLimit': 1000, + 'version': 'v2', + 'certified': false, + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'fetchOrders': true, + 'fetchClosedOrders': true, + 'fetchOrderTrades': true, + 'fetchLedger': true, + }, + 'timeframes': timeframes, + 'hostname': 'bleutrade.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30303000-b602dbe6-976d-11e7-956d-36c5049c01e7.jpg', + 'api': { + 'public': 'https://{hostname}/api/v2', + 'account': 'https://{hostname}/api/v2', + 'market': 'https://{hostname}/api/v2', + 'v3Private': 'https://{hostname}/api/v3/private', + 'v3Public': 'https://{hostname}/api/v3/public', + }, + 'www': 'https://bleutrade.com', + 'doc': [ + 'https://app.swaggerhub.com/apis-docs/bleu/white-label/3.0.0', + ], + 'fees': 'https://bleutrade.com/help/fees_and_deadlines', + }, + 'api': { + 'account': { + 'get': [ + 'balance', + 'balances', + 'depositaddress', + 'deposithistory', + 'order', + 'orders', + 'orderhistory', + 'withdrawhistory', + 'withdraw', + ], + }, + 'public': { + 'get': [ + 'candles', + 'currencies', + 'markethistory', + 'markets', + 'marketsummaries', + 'marketsummary', + 'orderbook', + 'ticker', + ], + }, + 'v3Public': { + 'get': [ + 'assets', + 'markets', + 'ticker', + 'marketsummary', + 'marketsummaries', + 'orderbook', + 'markethistory', + 'candles', + ], + }, + 'v3Private': { + 'get': [ + 'getbalance', + 'getbalances', + 'buylimit', + 'selllimit', + 'buylimitami', + 'selllimitami', + 'buystoplimit', + 'sellstoplimit', + 'ordercancel', + 'getopenorders', + 'getdeposithistory', + 'getdepositaddress', + 'getmytransactions', + 'withdraw', + 'directtransfer', + 'getwithdrawhistory', + 'getlimits', + ], + }, + }, + 'fees': { + 'funding': { + 'withdraw': { + 'ADC': 0.1, + 'BTA': 0.1, + 'BITB': 0.1, + 'BTC': 0.001, + 'BCC': 0.001, + 'BTCD': 0.001, + 'BTG': 0.001, + 'BLK': 0.1, + 'CDN': 0.1, + 'CLAM': 0.01, + 'DASH': 0.001, + 'DCR': 0.05, + 'DGC': 0.1, + 'DP': 0.1, + 'DPC': 0.1, + 'DOGE': 10.0, + 'EFL': 0.1, + 'ETH': 0.01, + 'EXP': 0.1, + 'FJC': 0.1, + 'BSTY': 0.001, + 'GB': 0.1, + 'NLG': 0.1, + 'HTML': 1.0, + 'LTC': 0.001, + 'MONA': 0.01, + 'MOON': 1.0, + 'NMC': 0.015, + 'NEOS': 0.1, + 'NVC': 0.05, + 'OK': 0.1, + 'PPC': 0.1, + 'POT': 0.1, + 'XPM': 0.001, + 'QTUM': 0.1, + 'RDD': 0.1, + 'SLR': 0.1, + 'START': 0.1, + 'SLG': 0.1, + 'TROLL': 0.1, + 'UNO': 0.01, + 'VRC': 0.1, + 'VTC': 0.1, + 'XVP': 0.1, + 'WDC': 0.001, + 'ZET': 0.1, + }, + }, + }, + 'commonCurrencies': { + 'EPC': 'Epacoin', + }, + 'exceptions': { + 'exact': { + 'Insufficient funds!': InsufficientFunds, + 'Invalid Order ID': InvalidOrder, + 'Invalid apikey or apisecret': AuthenticationError, + }, + }, + 'options': { + // price precision by quote currency code + 'pricePrecisionByCode': { + 'USD': 3, + }, + 'parseOrderStatus': true, + 'disableNonce': false, + 'symbolSeparator': '_', + }, + }); + // bittrex inheritance override + result['timeframes'] = timeframes; + return result; + } + + async fetchMarkets (params = {}) { + // https://github.com/ccxt/ccxt/issues/5668 + const response = await this.publicGetMarkets (params); + const result = []; + const markets = this.safeValue (response, 'result'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'MarketName'); + const baseId = this.safeString (market, 'MarketCurrency'); + const quoteId = this.safeString (market, 'BaseCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + let pricePrecision = 8; + if (quote in this.options['pricePrecisionByCode']) { + pricePrecision = this.options['pricePrecisionByCode'][quote]; + } + const precision = { + 'amount': 8, + 'price': pricePrecision, + }; + // bittrex uses boolean values, bleutrade uses strings + let active = this.safeValue (market, 'IsActive', false); + if ((active !== 'false') && active) { + active = true; + } else { + active = false; + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'MinTradeSize'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseOrderStatus (status) { + const statuses = { + 'OK': 'closed', + 'OPEN': 'open', + 'CANCELED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // Possible params + // orderstatus (ALL, OK, OPEN, CANCELED) + // ordertype (ALL, BUY, SELL) + // depth (optional, default is 500, max is 20000) + await this.loadMarkets (); + let market = undefined; + let marketId = 'ALL'; + if (symbol !== undefined) { + market = this.market (symbol); + marketId = market['id']; + } + const request = { + 'market': marketId, + 'orderstatus': 'ALL', + }; + const response = await this.accountGetOrders (this.extend (request, params)); + return this.parseOrders (response['result'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (response, 'status', 'closed'); + } + + getOrderIdField () { + return 'orderid'; + } + + parseSymbol (id) { + let [ base, quote ] = id.split (this.options['symbolSeparator']); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + return base + '/' + quote; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'type': 'ALL', + }; + if (limit !== undefined) { + request['depth'] = limit; // 50 + } + const response = await this.publicGetOrderbook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'result'); + if (!orderbook) { + throw new ExchangeError (this.id + ' publicGetOrderbook() returneded no result ' + this.json (response)); + } + return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity'); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + // Currently we can't set the makerOrTaker field, but if the user knows the order side then it can be + // determined (if the side of the trade is different to the side of the order, then the trade is maker). + // Similarly, the correct 'side' for the trade is that of the order. + // The trade fee can be set by the user, it is always 0.25% and is taken in the quote currency. + await this.loadMarkets (); + const request = { + 'orderid': id, + }; + const response = await this.accountGetOrderhistory (this.extend (request, params)); + return this.parseTrades (response['result'], undefined, since, limit, { + 'order': id, + }); + } + + async fetchTransactionsByType (type, code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const method = (type === 'deposit') ? 'accountGetDeposithistory' : 'accountGetWithdrawhistory'; + const response = await this[method] (params); + const result = this.parseTransactions (response['result']); + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('deposit', code, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('withdrawal', code, since, limit, params); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (ohlcv['TimeStamp'] + '+00:00'); + return [ + timestamp, + this.safeFloat (ohlcv, 'Open'), + this.safeFloat (ohlcv, 'High'), + this.safeFloat (ohlcv, 'Low'), + this.safeFloat (ohlcv, 'Close'), + this.safeFloat (ohlcv, 'Volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'period': this.timeframes[timeframe], + 'market': market['id'], + 'count': limit, + }; + const response = await this.publicGetCandles (this.extend (request, params)); + if ('result' in response) { + if (response['result']) { + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + } + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (trade['TimeStamp'] + '+00:00'); + let side = undefined; + if (trade['OrderType'] === 'BUY') { + side = 'buy'; + } else if (trade['OrderType'] === 'SELL') { + side = 'sell'; + } + const id = this.safeString2 (trade, 'TradeID', 'ID'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + const price = this.safeFloat (trade, 'Price'); + const amount = this.safeFloat (trade, 'Quantity'); + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + parseLedgerEntryType (type) { + // deposits don't seem to appear in here + const types = { + 'TRADE': 'trade', + 'WITHDRAW': 'transaction', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // trade (both sides) + // + // { + // ID: 109660527, + // TimeStamp: '2018-11-14 15:12:57.140776', + // Asset: 'ETH', + // AssetName: 'Ethereum', + // Amount: 0.01, + // Type: 'TRADE', + // Description: 'Trade +, order id 133111123', + // Comments: '', + // CoinSymbol: 'ETH', + // CoinName: 'Ethereum' + // } + // + // { + // ID: 109660526, + // TimeStamp: '2018-11-14 15:12:57.140776', + // Asset: 'BTC', + // AssetName: 'Bitcoin', + // Amount: -0.00031776, + // Type: 'TRADE', + // Description: 'Trade -, order id 133111123, fee -0.00000079', + // Comments: '', + // CoinSymbol: 'BTC', + // CoinName: 'Bitcoin' + // } + // + // withdrawal + // + // { + // ID: 104672316, + // TimeStamp: '2018-05-03 08:18:19.031831', + // Asset: 'DOGE', + // AssetName: 'Dogecoin', + // Amount: -61893.87864686, + // Type: 'WITHDRAW', + // Description: 'Withdraw: 61883.87864686 to address DD8tgehNNyYB2iqVazi2W1paaztgcWXtF6; fee 10.00000000', + // Comments: '', + // CoinSymbol: 'DOGE', + // CoinName: 'Dogecoin' + // } + // + const code = this.safeCurrencyCode (this.safeString (item, 'CoinSymbol'), currency); + const description = this.safeString (item, 'Description'); + const type = this.parseLedgerEntryType (this.safeString (item, 'Type')); + let referenceId = undefined; + let fee = undefined; + const delimiter = (type === 'trade') ? ', ' : '; '; + const parts = description.split (delimiter); + for (let i = 0; i < parts.length; i++) { + let part = parts[i]; + if (part.indexOf ('fee') === 0) { + part = part.replace ('fee ', ''); + let feeCost = parseFloat (part); + if (feeCost < 0) { + feeCost = -feeCost; + } + fee = { + 'cost': feeCost, + 'currency': code, + }; + } else if (part.indexOf ('order id') === 0) { + referenceId = part.replace ('order id', ''); + } + // + // does not belong to Ledger, related to parseTransaction + // + // if (part.indexOf ('Withdraw') === 0) { + // const details = part.split (' to address '); + // if (details.length > 1) { + // address = details[1]; + // } + // + } + const timestamp = this.parse8601 (this.safeString (item, 'TimeStamp')); + let amount = this.safeFloat (item, 'Amount'); + let direction = undefined; + if (amount !== undefined) { + direction = 'in'; + if (amount < 0) { + direction = 'out'; + amount = -amount; + } + } + const id = this.safeString (item, 'ID'); + return { + 'id': id, + 'info': item, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'direction': direction, + 'account': undefined, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': undefined, + 'after': undefined, + 'status': 'ok', + 'fee': fee, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + // + // if (code === undefined) { + // throw new ExchangeError (this.id + ' fetchClosedOrders requires a `symbol` argument'); + // } + // + await this.loadMarkets (); + const request = {}; + // + // if (code !== undefined) { + // const currency = this.market (code); + // request['asset'] = currency['id']; + // } + // + const response = await this.v3PrivateGetGetmytransactions (this.extend (request, params)); + return this.parseLedger (response['result'], code, since, limit); + } + + parseOrder (order, market = undefined) { + // + // fetchOrders + // + // { + // OrderId: '107220258', + // Exchange: 'LTC_BTC', + // Type: 'SELL', + // Quantity: '2.13040000', + // QuantityRemaining: '0.00000000', + // Price: '0.01332672', + // Status: 'OK', + // Created: '2018-06-30 04:55:50', + // QuantityBaseTraded: '0.02839125', + // Comments: '' + // } + // + let side = this.safeString2 (order, 'OrderType', 'Type'); + const isBuyOrder = (side === 'LIMIT_BUY') || (side === 'BUY'); + const isSellOrder = (side === 'LIMIT_SELL') || (side === 'SELL'); + if (isBuyOrder) { + side = 'buy'; + } + if (isSellOrder) { + side = 'sell'; + } + // We parse different fields in a very specific order. + // Order might well be closed and then canceled. + let status = undefined; + if (('Opened' in order) && order['Opened']) { + status = 'open'; + } + if (('Closed' in order) && order['Closed']) { + status = 'closed'; + } + if (('CancelInitiated' in order) && order['CancelInitiated']) { + status = 'canceled'; + } + if (('Status' in order) && this.options['parseOrderStatus']) { + status = this.parseOrderStatus (this.safeString (order, 'Status')); + } + let symbol = undefined; + const marketId = this.safeString (order, 'Exchange'); + if (marketId === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } else { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = this.parseSymbol (marketId); + } + } + let timestamp = undefined; + if ('Opened' in order) { + timestamp = this.parse8601 (order['Opened'] + '+00:00'); + } + if ('Created' in order) { + timestamp = this.parse8601 (order['Created'] + '+00:00'); + } + let lastTradeTimestamp = undefined; + if (('TimeStamp' in order) && (order['TimeStamp'] !== undefined)) { + lastTradeTimestamp = this.parse8601 (order['TimeStamp'] + '+00:00'); + } + if (('Closed' in order) && (order['Closed'] !== undefined)) { + lastTradeTimestamp = this.parse8601 (order['Closed'] + '+00:00'); + } + if (timestamp === undefined) { + timestamp = lastTradeTimestamp; + } + let fee = undefined; + let commission = undefined; + if ('Commission' in order) { + commission = 'Commission'; + } else if ('CommissionPaid' in order) { + commission = 'CommissionPaid'; + } + if (commission) { + fee = { + 'cost': this.safeFloat (order, commission), + }; + if (market !== undefined) { + fee['currency'] = market['quote']; + } else if (symbol !== undefined) { + const currencyIds = symbol.split ('/'); + const quoteCurrencyId = currencyIds[1]; + fee['currency'] = this.safeCurrencyCode (quoteCurrencyId); + } + } + let price = this.safeFloat (order, 'Price'); + let cost = undefined; + const amount = this.safeFloat (order, 'Quantity'); + const remaining = this.safeFloat (order, 'QuantityRemaining'); + let filled = undefined; + if (amount !== undefined && remaining !== undefined) { + filled = amount - remaining; + } + if (!cost) { + if (price && filled) { + cost = price * filled; + } + } + if (!price) { + if (cost && filled) { + price = cost / filled; + } + } + const average = this.safeFloat (order, 'PricePerUnit'); + const id = this.safeString2 (order, 'OrderUuid', 'OrderId'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + parseTransaction (transaction, currency = undefined) { + // + // deposit: + // + // { + // Id: '96974373', + // Coin: 'DOGE', + // Amount: '12.05752192', + // TimeStamp: '2017-09-29 08:10:09', + // Label: 'DQqSjjhzCm3ozT4vAevMUHgv4vsi9LBkoE', + // } + // + // withdrawal: + // + // { + // Id: '98009125', + // Coin: 'DOGE', + // Amount: '-483858.64312050', + // TimeStamp: '2017-11-22 22:29:05', + // Label: '483848.64312050;DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT;10.00000000', + // TransactionId: '8563105276cf798385fee7e5a563c620fea639ab132b089ea880d4d1f4309432', + // } + // + // { + // "Id": "95820181", + // "Coin": "BTC", + // "Amount": "-0.71300000", + // "TimeStamp": "2017-07-19 17:14:24", + // "Label": "0.71200000;PER9VM2txt4BTdfyWgvv3GziECRdVEPN63;0.00100000", + // "TransactionId": "CANCELED" + // } + // + const id = this.safeString (transaction, 'Id'); + let amount = this.safeFloat (transaction, 'Amount'); + let type = 'deposit'; + if (amount < 0) { + amount = Math.abs (amount); + type = 'withdrawal'; + } + const currencyId = this.safeString (transaction, 'Coin'); + const code = this.safeCurrencyCode (currencyId, currency); + const label = this.safeString (transaction, 'Label'); + const timestamp = this.parse8601 (this.safeString (transaction, 'TimeStamp')); + let txid = this.safeString (transaction, 'TransactionId'); + let address = undefined; + let feeCost = undefined; + const labelParts = label.split (';'); + if (labelParts.length === 3) { + amount = parseFloat (labelParts[0]); + address = labelParts[1]; + feeCost = parseFloat (labelParts[2]); + } else { + address = label; + } + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'currency': code, + 'cost': feeCost, + }; + } + let status = 'ok'; + if (txid === 'CANCELED') { + txid = undefined; + status = 'canceled'; + } + return { + 'info': transaction, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }) + '/'; + if (api === 'v3Private' || api === 'account') { + this.checkRequiredCredentials (); + if (api === 'account') { + url += api + '/'; + } + if (((api === 'account') && (path !== 'withdraw')) || (path === 'openorders')) { + url += method.toLowerCase (); + } + const request = { + 'apikey': this.apiKey, + }; + request['nonce'] = this.nonce (); + url += path + '?' + this.urlencode (this.extend (request, params)); + const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512'); + headers = { 'apisign': signature }; + } else { + url += api + '/' + method.toLowerCase () + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/braziliex.js b/TaskServer/node_modules/ccxt/js/braziliex.js new file mode 100644 index 000000000..e3ef518ea --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/braziliex.js @@ -0,0 +1,616 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InvalidOrder, AuthenticationError, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class braziliex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'braziliex', + 'name': 'Braziliex', + 'countries': [ 'BR' ], + 'rateLimit': 1000, + 'has': { + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/34703593-c4498674-f504-11e7-8d14-ff8e44fb78c1.jpg', + 'api': 'https://braziliex.com/api/v1', + 'www': 'https://braziliex.com/', + 'doc': 'https://braziliex.com/exchange/api.php', + 'fees': 'https://braziliex.com/exchange/fees.php', + 'referral': 'https://braziliex.com/?ref=5FE61AB6F6D67DA885BC98BA27223465', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'ticker', + 'ticker/{market}', + 'orderbook/{market}', + 'tradehistory/{market}', + ], + }, + 'private': { + 'post': [ + 'balance', + 'complete_balance', + 'open_orders', + 'trade_history', + 'deposit_address', + 'sell', + 'buy', + 'cancel_order', + 'order_status', + ], + }, + }, + 'commonCurrencies': { + 'EPC': 'Epacoin', + 'ABC': 'Anti Bureaucracy Coin', + }, + 'fees': { + 'trading': { + 'maker': 0.005, + 'taker': 0.005, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'options': { + 'fetchCurrencies': { + 'expires': 1000, // 1 second + }, + }, + }); + } + + async fetchCurrenciesFromCache (params = {}) { + // this method is now redundant + // currencies are now fetched before markets + const options = this.safeValue (this.options, 'fetchCurrencies', {}); + const timestamp = this.safeInteger (options, 'timestamp'); + const expires = this.safeInteger (options, 'expires', 1000); + const now = this.milliseconds (); + if ((timestamp === undefined) || ((now - timestamp) > expires)) { + const response = await this.publicGetCurrencies (params); + this.options['fetchCurrencies'] = this.extend (options, { + 'response': response, + 'timestamp': now, + }); + } + return this.safeValue (this.options['fetchCurrencies'], 'response'); + } + + async fetchCurrencies (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + // + // { + // brl: { + // name: "Real", + // withdrawal_txFee: 0.0075, + // txWithdrawalFee: 9, + // MinWithdrawal: 30, + // minConf: 1, + // minDeposit: 0, + // txDepositFee: 0, + // txDepositPercentageFee: 0, + // minAmountTradeFIAT: 5, + // minAmountTradeBTC: 0.0001, + // minAmountTradeUSDT: 0.0001, + // decimal: 8, + // decimal_withdrawal: 8, + // active: 1, + // dev_active: 1, + // under_maintenance: 0, + // order: "010", + // is_withdrawal_active: 1, + // is_deposit_active: 1, + // is_token_erc20: 0, + // is_fiat: 1, + // gateway: 0, + // }, + // btc: { + // name: "Bitcoin", + // txWithdrawalMinFee: 0.000125, + // txWithdrawalFee: 0.00015625, + // MinWithdrawal: 0.0005, + // minConf: 1, + // minDeposit: 0, + // txDepositFee: 0, + // txDepositPercentageFee: 0, + // minAmountTradeFIAT: 5, + // minAmountTradeBTC: 0.0001, + // minAmountTradeUSDT: 0.0001, + // decimal: 8, + // decimal_withdrawal: 8, + // active: 1, + // dev_active: 1, + // under_maintenance: 0, + // order: "011", + // is_withdrawal_active: 1, + // is_deposit_active: 1, + // is_token_erc20: 0, + // is_fiat: 0, + // gateway: 1, + // } + // } + // + this.options['currencies'] = { + 'timestamp': this.milliseconds (), + 'response': response, + }; + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = response[id]; + const precision = this.safeInteger (currency, 'decimal'); + const code = this.safeCurrencyCode (id); + let active = this.safeInteger (currency, 'active') === 1; + const maintenance = this.safeInteger (currency, 'under_maintenance'); + if (maintenance !== 0) { + active = false; + } + const canWithdraw = this.safeInteger (currency, 'is_withdrawal_active') === 1; + const canDeposit = this.safeInteger (currency, 'is_deposit_active') === 1; + if (!canWithdraw || !canDeposit) { + active = false; + } + result[code] = { + 'id': id, + 'code': code, + 'name': currency['name'], + 'active': active, + 'precision': precision, + 'funding': { + 'withdraw': { + 'active': canWithdraw, + 'fee': this.safeFloat (currency, 'txWithdrawalFee'), + }, + 'deposit': { + 'active': canDeposit, + 'fee': this.safeFloat (currency, 'txDepositFee'), + }, + }, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'MinWithdrawal'), + 'max': Math.pow (10, precision), + }, + 'deposit': { + 'min': this.safeFloat (currency, 'minDeposit'), + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const currencies = await this.fetchCurrenciesFromCache (params); + const response = await this.publicGetTicker (); + // + // { + // btc_brl: { + // active: 1, + // market: 'btc_brl', + // last: 14648, + // percentChange: -0.95, + // baseVolume24: 27.856, + // quoteVolume24: 409328.039, + // baseVolume: 27.856, + // quoteVolume: 409328.039, + // highestBid24: 14790, + // lowestAsk24: 14450.01, + // highestBid: 14450.37, + // lowestAsk: 14699.98 + // }, + // ... + // } + // + const ids = Object.keys (response); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = response[id]; + const [ baseId, quoteId ] = id.split ('_'); + const uppercaseBaseId = baseId.toUpperCase (); + const uppercaseQuoteId = quoteId.toUpperCase (); + const base = this.safeCurrencyCode (uppercaseBaseId); + const quote = this.safeCurrencyCode (uppercaseQuoteId); + const symbol = base + '/' + quote; + const baseCurrency = this.safeValue (currencies, baseId, {}); + const quoteCurrency = this.safeValue (currencies, quoteId, {}); + const quoteIsFiat = this.safeInteger (quoteCurrency, 'is_fiat', 0); + let minCost = undefined; + if (quoteIsFiat) { + minCost = this.safeFloat (baseCurrency, 'minAmountTradeFIAT'); + } else { + minCost = this.safeFloat (baseCurrency, 'minAmountTrade' + uppercaseQuoteId); + } + const isActive = this.safeInteger (market, 'active'); + const active = (isActive === 1); + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'highestBid24'), + 'low': this.safeFloat (ticker, 'lowestAsk24'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'percentChange'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume24'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume24'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTickerMarket (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const marketId = ids[i]; + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (response[marketId], market); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + }; + const response = await this.publicGetOrderbookMarket (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString2 (trade, 'date_exec', 'date')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const cost = this.safeFloat (trade, 'total'); + const orderId = this.safeString (trade, 'order_number'); + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, '_id'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTradehistoryMarket (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privatePostCompleteBalance (params); + const result = { 'info': balances }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = balances[currencyId]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + // + // { + // "order_number":"58ee441d05f8233fadabfb07", + // "type":"buy", + // "market":"ltc_btc", + // "price":"0.01000000", + // "amount":"0.00200000", + // "total":"0.00002000", + // "progress":"1.0000", + // "date":"2017-03-12 15:13:33" + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'market'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = this.safeInteger (order, 'timestamp'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (order, 'date')); + } + const price = this.safeFloat (order, 'price'); + const cost = this.safeFloat (order, 'total', 0.0); + const amount = this.safeFloat (order, 'amount'); + const filledPercentage = this.safeFloat (order, 'progress'); + const filled = amount * filledPercentage; + const remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); + let info = order; + if ('info' in info) { + info = order['info']; + } + const id = this.safeString (order, 'order_number'); + const fee = this.safeValue (order, 'fee'); // propagated from createOrder + const status = (filledPercentage === 1.0) ? 'closed' : 'open'; + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': order['type'], + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': info, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side); + const request = { + 'market': market['id'], + // 'price': this.priceToPrecision (symbol, price), + // 'amount': this.amountToPrecision (symbol, amount), + 'price': price, + 'amount': amount, + }; + const response = await this[method] (this.extend (request, params)); + const success = this.safeInteger (response, 'success'); + if (success !== 1) { + throw new InvalidOrder (this.id + ' ' + this.json (response)); + } + let parts = response['message'].split (' / '); + parts = parts.slice (1); + const feeParts = parts[5].split (' '); + const order = this.parseOrder ({ + 'timestamp': this.milliseconds (), + 'order_number': response['order_number'], + 'type': parts[0].toLowerCase (), + 'market': parts[0].toLowerCase (), + 'amount': parts[2].split (' ')[1], + 'price': parts[3].split (' ')[1], + 'total': parts[4].split (' ')[1], + 'fee': { + 'cost': parseFloat (feeParts[1]), + 'currency': feeParts[2], + }, + 'progress': '0.0', + 'info': response, + }, market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_number': id, + 'market': market['id'], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_number': id, + 'market': market['id'], + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privatePostOpenOrders (this.extend (request, params)); + return this.parseOrders (response['order_open'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseTrades (response['trade_history'], market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostDepositAddress (this.extend (request, params)); + const address = this.safeString (response, 'deposit_address'); + this.checkAddress (address); + const tag = this.safeString (response, 'payment_id'); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + api; + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + query = this.extend ({ + 'command': path, + 'nonce': this.nonce (), + }, query); + body = this.urlencode (query); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Content-type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.decode (signature), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ((typeof response === 'string') && (response.length < 1)) { + throw new ExchangeError (this.id + ' returned empty response'); + } + if ('success' in response) { + const success = this.safeInteger (response, 'success'); + if (success === 0) { + const message = this.safeString (response, 'message'); + if (message === 'Invalid APIKey') { + throw new AuthenticationError (message); + } + throw new ExchangeError (message); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/btcalpha.js b/TaskServer/node_modules/ccxt/js/btcalpha.js new file mode 100644 index 000000000..990ca8b69 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/btcalpha.js @@ -0,0 +1,471 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, DDoSProtection, InvalidOrder, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcalpha extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcalpha', + 'name': 'BTC-Alpha', + 'countries': [ 'US' ], + 'version': 'v1', + 'has': { + 'fetchTicker': false, + 'fetchOHLCV': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '4h': '240', + '1d': 'D', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/42625213-dabaa5da-85cf-11e8-8f99-aa8f8f7699f0.jpg', + 'api': 'https://btc-alpha.com/api', + 'www': 'https://btc-alpha.com', + 'doc': 'https://btc-alpha.github.io/api-docs', + 'fees': 'https://btc-alpha.com/fees/', + 'referral': 'https://btc-alpha.com/?r=123788', + }, + 'api': { + 'public': { + 'get': [ + 'currencies/', + 'pairs/', + 'orderbook/{pair_name}/', + 'exchanges/', + 'charts/{pair}/{type}/chart/', + ], + }, + 'private': { + 'get': [ + 'wallets/', + 'orders/own/', + 'order/{id}/', + 'exchanges/own/', + 'deposits/', + 'withdraws/', + ], + 'post': [ + 'order/', + 'order-cancel/', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.00135, + 'LTC': 0.0035, + 'XMR': 0.018, + 'ZEC': 0.002, + 'ETH': 0.01, + 'ETC': 0.01, + 'SIB': 1.5, + 'CCRB': 4, + 'PZM': 0.05, + 'ITI': 0.05, + 'DCY': 5, + 'R': 5, + 'ATB': 0.05, + 'BRIA': 0.05, + 'KZC': 0.05, + 'HWC': 1, + 'SPA': 1, + 'SMS': 0.001, + 'REC': 0.01, + 'SUP': 1, + 'BQ': 100, + 'GDS': 0.1, + 'EVN': 300, + 'TRKC': 0.01, + 'UNI': 1, + 'STN': 1, + 'BCH': undefined, + 'QBIC': 0.5, + }, + }, + }, + 'commonCurrencies': { + 'CBC': 'Cashbery', + }, + 'exceptions': { + 'exact': {}, + 'broad': { + 'Out of balance': InsufficientFunds, // {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"} + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetPairs (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'currency1'); + const quoteId = this.safeString (market, 'currency2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': this.safeInteger (market, 'price_precision'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minimum_order_size'), + 'max': this.safeFloat (market, 'maximum_order_size'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_name': this.marketId (symbol), + }; + if (limit) { + request['limit_sell'] = limit; + request['limit_buy'] = limit; + } + const response = await this.publicGetOrderbookPairName (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'buy', 'sell', 'price', 'amount'); + } + + parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) { + const result = []; + for (let i = 0; i < bidasks.length; i++) { + const bidask = bidasks[i]; + if (bidask) { + result.push (this.parseBidAsk (bidask, priceKey, amountKey)); + } + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market === undefined) { + market = this.safeValue (this.marketsById, trade['pair']); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const id = this.safeString2 (trade, 'id', 'tid'); + const side = this.safeString2 (trade, 'my_side', 'side'); + const orderId = this.safeString (trade, 'o_id'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const trades = await this.publicGetExchanges (this.extend (request, params)); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.publicGetChartsPairTypeChart (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWallets (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'reserve'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + '1': 'open', + '2': 'canceled', + '3': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market === undefined) { + market = this.safeValue (this.marketsById, order['pair']); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'date'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString2 (order, 'oid', 'id'); + let trades = this.safeValue (order, 'trades', []); + trades = this.parseTrades (trades, market); + const side = this.safeString2 (order, 'my_side', 'type'); + let filled = undefined; + const numTrades = trades.length; + if (numTrades > 0) { + filled = 0.0; + for (let i = 0; i < numTrades; i++) { + filled = this.sum (filled, trades[i]['amount']); + } + } + let remaining = undefined; + if ((amount !== undefined) && (amount > 0) && (filled !== undefined)) { + remaining = Math.max (0, amount - filled); + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'amount': amount, + 'price': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostOrder (this.extend (request, params)); + if (!response['success']) { + throw new InvalidOrder (this.id + ' ' + this.json (response)); + } + const order = this.parseOrder (response, market); + amount = (order['amount'] > 0) ? order['amount'] : amount; + return this.extend (order, { + 'amount': amount, + }); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order': id, + }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const order = await this.privateGetOrderId (this.extend (request, params)); + return this.parseOrder (order); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const orders = await this.privateGetOrdersOwn (this.extend (request, params)); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': '1', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': '3', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbol !== undefined) { + const market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const trades = await this.privateGetExchangesOwn (this.extend (request, params)); + return this.parseTrades (trades, undefined, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.urlencode (this.keysort (this.omit (params, this.extractParams (path)))); + let url = this.urls['api'] + '/'; + if (path !== 'charts/{pair}/{type}/chart/') { + url += 'v1/'; + } + url += this.implodeParams (path, params); + headers = { 'Accept': 'application/json' }; + if (api === 'public') { + if (query.length) { + url += '?' + query; + } + } else { + this.checkRequiredCredentials (); + let payload = this.apiKey; + if (method === 'POST') { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + body = query; + payload += body; + } else if (query.length) { + url += '?' + query; + } + headers['X-KEY'] = this.apiKey; + headers['X-SIGN'] = this.hmac (this.encode (payload), this.encode (this.secret)); + headers['X-NONCE'] = this.nonce ().toString (); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"} + // + const error = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + if (error !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + } + if (code === 401 || code === 403) { + throw new AuthenticationError (feedback); + } else if (code === 429) { + throw new DDoSProtection (feedback); + } + if (code < 400) { + return; + } + throw new ExchangeError (feedback); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/btcbox.js b/TaskServer/node_modules/ccxt/js/btcbox.js new file mode 100644 index 000000000..e9fa45401 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/btcbox.js @@ -0,0 +1,406 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InsufficientFunds, InvalidOrder, AuthenticationError, PermissionDenied, InvalidNonce, OrderNotFound, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcbox extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcbox', + 'name': 'BtcBox', + 'countries': [ 'JP' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchTickers': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/31275803-4df755a8-aaa1-11e7-9abb-11ec2fad9f2d.jpg', + 'api': 'https://www.btcbox.co.jp/api', + 'www': 'https://www.btcbox.co.jp/', + 'doc': 'https://www.btcbox.co.jp/help/asm', + 'fees': 'https://support.btcbox.co.jp/hc/en-us/articles/360001235694-Fees-introduction', + }, + 'api': { + 'public': { + 'get': [ + 'depth', + 'orders', + 'ticker', + ], + }, + 'private': { + 'post': [ + 'balance', + 'trade_add', + 'trade_cancel', + 'trade_list', + 'trade_view', + 'wallet', + ], + }, + }, + 'markets': { + 'BTC/JPY': { 'id': 'btc', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy', 'taker': 0.05 / 100, 'maker': 0.05 / 100 }, + 'ETH/JPY': { 'id': 'eth', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, + 'LTC/JPY': { 'id': 'ltc', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, + 'BCH/JPY': { 'id': 'bch', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bch', 'quoteId': 'jpy', 'taker': 0.10 / 100, 'maker': 0.10 / 100 }, + }, + 'exceptions': { + '104': AuthenticationError, + '105': PermissionDenied, + '106': InvalidNonce, + '107': InvalidOrder, // price should be an integer + '200': InsufficientFunds, + '201': InvalidOrder, // amount too small + '202': InvalidOrder, // price should be [0 : 1000000] + '203': OrderNotFound, + '401': OrderNotFound, // cancel canceled, closed or non-existent order + '402': DDoSProtection, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const currencyId = currency['id']; + const free = currencyId + '_balance'; + if (free in response) { + const account = this.account (); + const used = currencyId + '_lock'; + account['free'] = this.safeFloat (response, free); + account['used'] = this.safeFloat (response, used); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + const numSymbols = this.symbols.length; + if (numSymbols > 1) { + request['coin'] = market['baseId']; + } + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + const numSymbols = this.symbols.length; + if (numSymbols > 1) { + request['coin'] = market['baseId']; + } + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const type = undefined; + const side = this.safeString (trade, 'type'); + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + const numSymbols = this.symbols.length; + if (numSymbols > 1) { + request['coin'] = market['baseId']; + } + const response = await this.publicGetOrders (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'amount': amount, + 'price': price, + 'type': side, + 'coin': market['baseId'], + }; + const response = await this.privatePostTradeAdd (this.extend (request, params)); + // + // { + // "result":true, + // "id":"11" + // } + // + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // a special case for btcbox – default symbol is BTC/JPY + if (symbol === undefined) { + symbol = 'BTC/JPY'; + } + const market = this.market (symbol); + const request = { + 'id': id, + 'coin': market['baseId'], + }; + const response = await this.privatePostTradeCancel (this.extend (request, params)); + // + // {"result":true, "id":"11"} + // + return this.parseOrder (response, market); + } + + parseOrderStatus (status) { + const statuses = { + // TODO: complete list + 'part': 'open', // partially or not at all executed + 'all': 'closed', // fully executed + 'cancelled': 'canceled', + 'closed': 'closed', // never encountered, seems to be bug in the doc + 'no': 'closed', // not clarified in the docs... + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // {"id":11,"datetime":"2014-10-21 10:47:20","type":"sell","price":42000,"amount_original":1.2,"amount_outstanding":1.2,"status":"closed","trades":[]} + // + const id = this.safeString (order, 'id'); + const datetimeString = this.safeString (order, 'datetime'); + let timestamp = undefined; + if (datetimeString !== undefined) { + timestamp = this.parse8601 (order['datetime'] + '+09:00'); // Tokyo time + } + const amount = this.safeFloat (order, 'amount_original'); + const remaining = this.safeFloat (order, 'amount_outstanding'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + const price = this.safeFloat (order, 'price'); + let cost = undefined; + if (price !== undefined) { + if (filled !== undefined) { + cost = filled * price; + } + } + // status is set by fetchOrder method only + let status = this.parseOrderStatus (this.safeString (order, 'status')); + // fetchOrders do not return status, use heuristic + if (status === undefined) { + if (remaining !== undefined && remaining === 0) { + status = 'closed'; + } + } + const trades = undefined; // todo: this.parseTrades (order['trades']); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (order, 'type'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'side': side, + 'type': undefined, + 'status': status, + 'symbol': symbol, + 'price': price, + 'cost': cost, + 'trades': trades, + 'fee': undefined, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // a special case for btcbox – default symbol is BTC/JPY + if (symbol === undefined) { + symbol = 'BTC/JPY'; + } + const market = this.market (symbol); + const request = this.extend ({ + 'id': id, + 'coin': market['baseId'], + }, params); + const response = await this.privatePostTradeView (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // a special case for btcbox – default symbol is BTC/JPY + if (symbol === undefined) { + symbol = 'BTC/JPY'; + } + const market = this.market (symbol); + const request = { + 'type': type, // 'open' or 'all' + 'coin': market['baseId'], + }; + const response = await this.privatePostTradeList (this.extend (request, params)); + const orders = this.parseOrders (response, market, since, limit); + // status (open/closed/canceled) is undefined + // btcbox does not return status, but we know it's 'open' as we queried for open orders + if (type === 'open') { + for (let i = 0; i < orders.length; i++) { + orders[i]['status'] = 'open'; + } + } + return orders; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('all', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('open', symbol, since, limit, params); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const query = this.extend ({ + 'key': this.apiKey, + 'nonce': nonce, + }, params); + const request = this.urlencode (query); + const secret = this.hash (this.encode (this.secret)); + query['signature'] = this.hmac (this.encode (request), this.encode (secret)); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // resort to defaultErrorHandler + } + // typical error response: {"result":false,"code":"401"} + if (httpCode >= 400) { + return; // resort to defaultErrorHandler + } + const result = this.safeValue (response, 'result'); + if (result === undefined || result === true) { + return; // either public API (no error codes expected) or success + } + const code = this.safeValue (response, 'code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, code, feedback); + throw new ExchangeError (feedback); // unknown message + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response === 'string') { + // sometimes the exchange returns whitespace prepended to json + response = this.strip (response); + if (!this.isJsonEncodedObject (response)) { + throw new ExchangeError (this.id + ' ' + response); + } + response = JSON.parse (response); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/btcchina.js b/TaskServer/node_modules/ccxt/js/btcchina.js new file mode 100644 index 000000000..b68b2e7b2 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/btcchina.js @@ -0,0 +1,368 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class btcchina extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcchina', + 'name': 'BTCChina', + 'countries': [ 'CN' ], + 'rateLimit': 1500, + 'version': 'v1', + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766368-465b3286-5ed6-11e7-9a11-0f6467e1d82b.jpg', + 'api': { + 'plus': 'https://plus-api.btcchina.com/market', + 'public': 'https://data.btcchina.com/data', + 'private': 'https://api.btcchina.com/api_trade_v1.php', + }, + 'www': 'https://www.btcchina.com', + 'doc': 'https://www.btcchina.com/apidocs', + }, + 'api': { + 'plus': { + 'get': [ + 'orderbook', + 'ticker', + 'trade', + ], + }, + 'public': { + 'get': [ + 'historydata', + 'orderbook', + 'ticker', + 'trades', + ], + }, + 'private': { + 'post': [ + 'BuyIcebergOrder', + 'BuyOrder', + 'BuyOrder2', + 'BuyStopOrder', + 'CancelIcebergOrder', + 'CancelOrder', + 'CancelStopOrder', + 'GetAccountInfo', + 'getArchivedOrder', + 'getArchivedOrders', + 'GetDeposits', + 'GetIcebergOrder', + 'GetIcebergOrders', + 'GetMarketDepth', + 'GetMarketDepth2', + 'GetOrder', + 'GetOrders', + 'GetStopOrder', + 'GetStopOrders', + 'GetTransactions', + 'GetWithdrawal', + 'GetWithdrawals', + 'RequestWithdrawal', + 'SellIcebergOrder', + 'SellOrder', + 'SellOrder2', + 'SellStopOrder', + ], + }, + }, + 'markets': { + 'BTC/CNY': { 'id': 'btccny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'api': 'public', 'plus': false }, + 'LTC/CNY': { 'id': 'ltccny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY', 'api': 'public', 'plus': false }, + 'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'api': 'public', 'plus': false }, + 'BCH/CNY': { 'id': 'bcccny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY', 'api': 'plus', 'plus': true }, + 'ETH/CNY': { 'id': 'ethcny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY', 'api': 'plus', 'plus': true }, + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { + 'market': 'all', + }; + const markets = await this.publicGetTicker (this.extend (request, params)); + const result = []; + const keys = Object.keys (markets); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = markets[key]; + const parts = key.split ('_'); + const id = parts[1]; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const balances = this.safeValue (response, 'result'); + const result = { 'info': balances }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const account = this.account (); + const currencyId = currency['id']; + if (currencyId in balances['balance']) { + account['total'] = parseFloat (balances['balance'][currencyId]['amount']); + } + if (currencyId in balances['frozen']) { + account['used'] = parseFloat (balances['frozen'][currencyId]['amount']); + } + result[code] = account; + } + return this.parseBalance (result); + } + + createMarketRequest (market) { + const request = {}; + const field = (market['plus']) ? 'symbol' : 'market'; + request[field] = market['id']; + return request; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['api'] + 'GetOrderbook'; + const request = this.createMarketRequest (market); + const response = await this[method] (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'date'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market) { + const timestamp = this.safeTimestamp (ticker, 'date'); + const last = this.safeFloat (ticker, 'last'); + return { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'ask': this.safeFloat (ticker, 'sell'), + 'vwap': this.safeFloat (ticker, 'vwap'), + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTickerPlus (ticker, market) { + const timestamp = this.safeInteger (ticker, 'Timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'High'), + 'low': this.safeFloat (ticker, 'Low'), + 'bid': this.safeFloat (ticker, 'BidPrice'), + 'ask': this.safeFloat (ticker, 'AskPrice'), + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'Open'), + 'last': this.safeFloat (ticker, 'Last'), + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume24H'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['api'] + 'GetTicker'; + const request = this.createMarketRequest (market); + const response = await this[method] (this.extend (request, params)); + const ticker = this.safeValue (response, 'ticker'); + if (market['plus']) { + return this.parseTickerPlus (ticker, market); + } + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'date'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': undefined, + 'side': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + } + + parseTradePlus (trade, market) { + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + const side = this.safeStringLower (trade, 'side'); + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + } + + parseTradesPlus (trades, market = undefined) { + const result = []; + for (let i = 0; i < trades.length; i++) { + result.push (this.parseTradePlus (trades[i], market)); + } + return result; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = market['api'] + 'GetTrade'; + const request = this.createMarketRequest (market); + if (market['plus']) { + const now = this.milliseconds (); + request['start_time'] = now - 86400000; + request['end_time'] = now; + } else { + method += 's'; // trades vs trade + } + const response = await this[method] (this.extend (request, params)); + if (market['plus']) { + return this.parseTradesPlus (response['trades'], market); + } + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side) + 'Order2'; + const request = {}; + const id = market['id'].toUpperCase (); + if (type === 'market') { + request['params'] = [ undefined, amount, id ]; + } else { + request['params'] = [ price, amount, id ]; + } + const response = await this[method] (this.extend (request, params)); + const orderId = this.safeString (response, 'id'); + return { + 'info': response, + 'id': orderId, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = params['market']; // TODO fixme + const request = { + 'params': [ id, market ], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + nonce () { + return this.microseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'private') { + this.checkRequiredCredentials (); + let p = []; + if ('params' in params) { + p = params['params']; + } + const nonce = this.nonce (); + const request = { + 'method': path, + 'id': nonce, + 'params': p, + }; + p = p.join (','); + body = this.json (request); + const query = [ + 'tonce=' + nonce, + 'accesskey=' + this.apiKey, + 'requestmethod=' + method.toLowerCase (), + 'id=' + nonce, + 'method=' + path, + 'params=' + p, + ].join ('&'); + const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha1'); + const auth = this.encode (this.apiKey + ':' + signature); + headers = { + 'Authorization': 'Basic ' + this.stringToBase64 (auth), + 'Json-Rpc-Tonce': nonce, + }; + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/btcmarkets.js b/TaskServer/node_modules/ccxt/js/btcmarkets.js new file mode 100644 index 000000000..4211000c7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/btcmarkets.js @@ -0,0 +1,754 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, OrderNotFound, ArgumentsRequired, InvalidOrder, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcmarkets extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcmarkets', + 'name': 'BTC Markets', + 'countries': [ 'AU' ], // Australia + 'rateLimit': 1000, // market data cached for 1 second (trades cached for 2 seconds) + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchClosedOrders': 'emulated', + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'cancelOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/29142911-0e1acfc2-7d5c-11e7-98c4-07d9532b29d7.jpg', + 'api': { + 'public': 'https://api.btcmarkets.net', + 'private': 'https://api.btcmarkets.net', + 'web': 'https://btcmarkets.net/data', + }, + 'www': 'https://btcmarkets.net', + 'doc': 'https://github.com/BTCMarkets/API', + }, + 'api': { + 'public': { + 'get': [ + 'market/{id}/tick', + 'market/{id}/orderbook', + 'market/{id}/trades', + 'v2/market/{id}/tickByTime/{timeframe}', + 'v2/market/{id}/trades', + 'v2/market/active', + ], + }, + 'private': { + 'get': [ + 'account/balance', + 'account/{id}/tradingfee', + 'fundtransfer/history', + 'v2/order/open', + 'v2/order/open/{id}', + 'v2/order/history/{instrument}/{currency}/', + 'v2/order/trade/history/{id}', + 'v2/transaction/history/{currency}', + ], + 'post': [ + 'fundtransfer/withdrawCrypto', + 'fundtransfer/withdrawEFT', + 'order/create', + 'order/cancel', + 'order/history', + 'order/open', + 'order/trade/history', + 'order/createBatch', // they promise it's coming soon... + 'order/detail', + ], + }, + 'web': { + 'get': [ + 'market/BTCMarkets/{id}/tickByTime', + ], + }, + }, + 'timeframes': { + '1m': 'minute', + '1h': 'hour', + '1d': 'day', + }, + 'exceptions': { + '3': InvalidOrder, + '6': DDoSProtection, + }, + 'fees': { + 'percentage': true, + 'tierBased': true, + 'maker': -0.05 / 100, + 'taker': 0.20 / 100, + }, + 'options': { + 'fees': { + 'AUD': { + 'maker': 0.85 / 100, + 'taker': 0.85 / 100, + }, + }, + }, + }); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['since'] = since; + } + const response = await this.privateGetFundtransferHistory (this.extend (request, params)); + const transactions = response['fundTransfers']; + return this.parseTransactions (transactions, undefined, since, limit); + } + + parseTransactionStatus (status) { + // todo: find more statuses + const statuses = { + 'Complete': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (item, currency = undefined) { + // + // { + // status: 'Complete', + // fundTransferId: 1904311906, + // description: 'ETH withdraw from [me@email.com] to Address: 0xF123aa44FadEa913a7da99cc2eE202Db684Ce0e3 amount: 8.28965701 fee: 0.00000000', + // creationTime: 1529418358525, + // currency: 'ETH', + // amount: 828965701, + // fee: 0, + // transferType: 'WITHDRAW', + // errorMessage: null, + // lastUpdate: 1529418376754, + // cryptoPaymentDetail: { + // address: '0xF123aa44FadEa913a7da99cc2eE202Db684Ce0e3', + // txId: '0x8fe483b6f9523559b9ebffb29624f98e86227d2660d4a1fd4785d45e51c662c2' + // } + // } + // + // { + // status: 'Complete', + // fundTransferId: 494077500, + // description: 'BITCOIN Deposit, B 0.1000', + // creationTime: 1501077601015, + // currency: 'BTC', + // amount: 10000000, + // fee: 0, + // transferType: 'DEPOSIT', + // errorMessage: null, + // lastUpdate: 1501077601133, + // cryptoPaymentDetail: null + // } + // + // { + // "fee": 0, + // "amount": 56, + // "status": "Complete", + // "currency": "BCHABC", + // "lastUpdate": 1542339164044, + // "description": "BitcoinCashABC Deposit, P 0.00000056", + // "creationTime": 1542339164003, + // "errorMessage": null, + // "transferType": "DEPOSIT", + // "fundTransferId": 2527326972, + // "cryptoPaymentDetail": null + // } + // + const timestamp = this.safeInteger (item, 'creationTime'); + const lastUpdate = this.safeInteger (item, 'lastUpdate'); + const transferType = this.safeString (item, 'transferType'); + const cryptoPaymentDetail = this.safeValue (item, 'cryptoPaymentDetail', {}); + const address = this.safeString (cryptoPaymentDetail, 'address'); + const txid = this.safeString (cryptoPaymentDetail, 'txId'); + let type = undefined; + if (transferType === 'DEPOSIT') { + type = 'deposit'; + } else if (transferType === 'WITHDRAW') { + type = 'withdrawal'; + } else { + type = transferType; + } + const fee = this.safeFloat (item, 'fee'); + const status = this.parseTransactionStatus (this.safeString (item, 'status')); + const ccy = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (ccy); + // todo: this logic is duplicated below + let amount = this.safeFloat (item, 'amount'); + if (amount !== undefined) { + amount = amount * 1e-8; + } + return { + 'id': this.safeString (item, 'fundTransferId'), + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': lastUpdate, + 'fee': { + 'currency': code, + 'cost': fee, + }, + 'info': item, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetV2MarketActive (params); + const result = []; + const markets = this.safeValue (response, 'markets'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = this.safeString (market, 'instrument'); + const quoteId = this.safeString (market, 'currency'); + const id = baseId + '/' + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const fees = this.safeValue (this.safeValue (this.options, 'fees', {}), quote, this.fees); + let pricePrecision = 2; + let amountPrecision = 4; + const minAmount = 0.001; // where does it come from? + let minPrice = undefined; + if (quote === 'AUD') { + if ((base === 'XRP') || (base === 'OMG')) { + pricePrecision = 4; + } + amountPrecision = -Math.log10 (minAmount); + minPrice = Math.pow (10, -pricePrecision); + } + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const limits = { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'maker': fees['maker'], + 'taker': fees['taker'], + 'limits': limits, + 'precision': precision, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetAccountBalance (params); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const multiplier = 100000000; + let total = this.safeFloat (balance, 'balance'); + if (total !== undefined) { + total /= multiplier; + } + let used = this.safeFloat (balance, 'pendingFunds'); + if (used !== undefined) { + used /= multiplier; + } + const account = this.account (); + account['used'] = used; + account['total'] = total; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "timestamp":1572307200000, + // "open":1962218, + // "high":1974850, + // "low":1962208, + // "close":1974850, + // "volume":305211315, + // } + // + const multiplier = 100000000; // for price and volume + const keys = [ 'open', 'high', 'low', 'close', 'volume' ]; + const result = [ + this.safeInteger (ohlcv, 'timestamp'), + ]; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + let value = this.safeFloat (ohlcv, key); + if (value !== undefined) { + value = value / multiplier; + } + result.push (value); + } + return result; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.load_markets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + 'timeframe': this.timeframes[timeframe], + // set to true to see candles more recent than the timestamp in the + // since parameter, if a since parameter is used, default is false + 'indexForward': true, + // set to true to see the earliest candles first in the list of + // returned candles in chronological order, default is false + 'sortForward': true, + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default is 3000 + } + const response = await this.publicGetV2MarketIdTickByTimeTimeframe (this.extend (request, params)); + // + // { + // "success":true, + // "paging":{ + // "newer":"/v2/market/ETH/BTC/tickByTime/day?indexForward=true&since=1572307200000", + // "older":"/v2/market/ETH/BTC/tickByTime/day?since=1457827200000" + // }, + // "ticks":[ + // {"timestamp":1572307200000,"open":1962218,"high":1974850,"low":1962208,"close":1974850,"volume":305211315}, + // {"timestamp":1572220800000,"open":1924700,"high":1951276,"low":1909328,"close":1951276,"volume":1086067595}, + // {"timestamp":1572134400000,"open":1962155,"high":1962734,"low":1900905,"close":1930243,"volume":790141098}, + // ], + // } + // + const ticks = this.safeValue (response, 'ticks', []); + return this.parseOHLCVs (ticks, market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetMarketIdOrderbook (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'bestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume24h'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetMarketIdTick (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + // 'since': 59868345231, + 'id': market['id'], + }; + const response = await this.publicGetMarketIdTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const multiplier = 100000000; // for price and volume + const orderSide = (side === 'buy') ? 'Bid' : 'Ask'; + const request = this.ordered ({ + 'currency': market['quote'], + }); + request['currency'] = market['quote']; + request['instrument'] = market['base']; + request['price'] = parseInt (price * multiplier); + request['volume'] = parseInt (amount * multiplier); + request['orderSide'] = orderSide; + request['ordertype'] = this.capitalize (type); + request['clientRequestId'] = this.nonce ().toString (); + const response = await this.privatePostOrderCreate (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + for (let i = 0; i < ids.length; i++) { + ids[i] = parseInt (ids[i]); + } + const request = { + 'orderIds': ids, + }; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.cancelOrders ([ id ]); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let currency = undefined; + let cost = undefined; + if (market['quote'] === 'AUD') { + currency = market['quote']; + cost = parseFloat (this.costToPrecision (symbol, amount * price)); + } else { + currency = market['base']; + cost = parseFloat (this.amountToPrecision (symbol, amount)); + } + return { + 'type': takerOrMaker, + 'currency': currency, + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)), + }; + } + + parseMyTrade (trade, market) { + const multiplier = 100000000; + const timestamp = this.safeInteger (trade, 'creationTime'); + let side = this.safeFloat (trade, 'side'); + side = (side === 'Bid') ? 'buy' : 'sell'; + // BTCMarkets always charge in AUD for AUD-related transactions. + let feeCurrencyCode = undefined; + let symbol = undefined; + if (market !== undefined) { + feeCurrencyCode = (market['quote'] === 'AUD') ? market['quote'] : market['base']; + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + let price = this.safeFloat (trade, 'price'); + if (price !== undefined) { + price /= multiplier; + } + let amount = this.safeFloat (trade, 'volume'); + if (amount !== undefined) { + amount /= multiplier; + } + let feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + feeCost /= multiplier; + } + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const orderId = this.safeString (trade, 'orderId'); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'order': orderId, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': { + 'currency': feeCurrencyCode, + 'cost': feeCost, + }, + }; + } + + parseMyTrades (trades, market = undefined, since = undefined, limit = undefined) { + const result = []; + for (let i = 0; i < trades.length; i++) { + const trade = this.parseMyTrade (trades[i], market); + result.push (trade); + } + return result; + } + + parseOrder (order, market = undefined) { + const multiplier = 100000000; + const side = (order['orderSide'] === 'Bid') ? 'buy' : 'sell'; + const type = (order['ordertype'] === 'Limit') ? 'limit' : 'market'; + const timestamp = this.safeInteger (order, 'creationTime'); + if (market === undefined) { + market = this.market (order['instrument'] + '/' + order['currency']); + } + let status = 'open'; + if (order['status'] === 'Failed' || order['status'] === 'Cancelled' || order['status'] === 'Partially Cancelled' || order['status'] === 'Error') { + status = 'canceled'; + } else if (order['status'] === 'Fully Matched' || order['status'] === 'Partially Matched') { + status = 'closed'; + } + const price = this.safeFloat (order, 'price') / multiplier; + const amount = this.safeFloat (order, 'volume') / multiplier; + const remaining = this.safeFloat (order, 'openVolume', 0.0) / multiplier; + const filled = amount - remaining; + const trades = this.parseMyTrades (order['trades'], market); + const numTrades = trades.length; + let cost = filled * price; + let average = undefined; + let lastTradeTimestamp = undefined; + if (numTrades > 0) { + cost = 0; + for (let i = 0; i < numTrades; i++) { + const trade = trades[i]; + cost = this.sum (cost, trade['cost']); + } + if (filled > 0) { + average = cost / filled; + } + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': market['symbol'], + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'average': average, + 'status': status, + 'trades': trades, + 'fee': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const ids = [ parseInt (id) ]; + const request = { + 'orderIds': ids, + }; + const response = await this.privatePostOrderDetail (this.extend (request, params)); + const numOrders = response['orders'].length; + if (numOrders < 1) { + throw new OrderNotFound (this.id + ' No matching order found: ' + id); + } + const order = response['orders'][0]; + return this.parseOrder (order); + } + + createPaginatedRequest (market, since = undefined, limit = undefined) { + limit = (limit === undefined) ? 100 : limit; + since = (since === undefined) ? 0 : since; + const request = this.ordered ({ + 'currency': market['quoteId'], + 'instrument': market['baseId'], + 'limit': limit, + 'since': since, + }); + return request; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ': fetchOrders requires a `symbol` argument.'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.createPaginatedRequest (market, since, limit); + const response = await this.privatePostOrderHistory (this.extend (request, params)); + return this.parseOrders (response['orders'], market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ': fetchOpenOrders requires a `symbol` argument.'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.createPaginatedRequest (market, since, limit); + const response = await this.privatePostOrderOpen (this.extend (request, params)); + return this.parseOrders (response['orders'], market); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ': fetchMyTrades requires a `symbol` argument.'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.createPaginatedRequest (market, since, limit); + const response = await this.privatePostOrderTradeHistory (this.extend (request, params)); + return this.parseMyTrades (response['trades'], market); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const uri = '/' + this.implodeParams (path, params); + let url = this.urls['api'][api] + uri; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = undefined; + headers = { + 'apikey': this.apiKey, + 'timestamp': nonce, + }; + if (method === 'POST') { + headers['Content-Type'] = 'application/json'; + auth = uri + "\n" + nonce + "\n"; // eslint-disable-line quotes + body = this.json (params); + auth += body; + } else { + const query = this.keysort (this.omit (params, this.extractParams (path))); + let queryString = ''; + if (Object.keys (query).length) { + queryString = this.urlencode (query); + url += '?' + queryString; + queryString += "\n"; // eslint-disable-line quotes + } + auth = uri + "\n" + queryString + nonce + "\n"; // eslint-disable-line quotes + } + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64'); + headers['signature'] = this.decode (signature); + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + if (!response['success']) { + const error = this.safeString (response, 'errorCode'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + throw new ExchangeError (feedback); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/btctradeim.js b/TaskServer/node_modules/ccxt/js/btctradeim.js new file mode 100644 index 000000000..28bc60075 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/btctradeim.js @@ -0,0 +1,71 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const coinegg = require ('./coinegg.js'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btctradeim extends coinegg { + describe () { + const result = this.deepExtend (super.describe (), { + 'id': 'btctradeim', + 'name': 'BtcTrade.im', + 'countries': [ 'HK' ], + 'urls': { + 'referral': 'https://m.baobi.com/invite?inv=1765b2', + 'logo': 'https://user-images.githubusercontent.com/1294454/36770531-c2142444-1c5b-11e8-91e2-a4d90dc85fe8.jpg', + 'api': { + 'web': 'https://api.btctrade.im/coin', + 'rest': 'https://api.btctrade.im/api/v1', + }, + 'www': 'https://www.btctrade.im', + 'doc': 'https://www.btctrade.im/help.api.html', + 'fees': 'https://www.btctrade.im/spend.price.html', + }, + 'status': { + 'status': 'error', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.001, + }, + }, + }, + // see the fix below + // 'options': { + // 'quoteIds': [ 'btc', 'eth', 'usc' ], + // }, + }); + // a fix for PHP array_merge not overwriting "lists" (integer-indexed arrays) + // https://github.com/ccxt/ccxt/issues/3343 + result['options']['quoteIds'] = [ 'btc', 'eth', 'usc' ]; + return result; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (api === 'web') { + return response; + } + const data = this.safeValue (response, 'data'); + if (data) { + const code = this.safeString (response, 'code'); + if (code !== '0') { + const message = this.safeString (response, 'msg', 'Error'); + throw new ExchangeError (message); + } + return data; + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/btctradeua.js b/TaskServer/node_modules/ccxt/js/btctradeua.js new file mode 100644 index 000000000..c232d1bb0 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/btctradeua.js @@ -0,0 +1,380 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btctradeua extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btctradeua', + 'name': 'BTC Trade UA', + 'countries': [ 'UA' ], // Ukraine, + 'rateLimit': 3000, + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchOpenOrders': true, + }, + 'urls': { + 'referral': 'https://btc-trade.com.ua/registration/22689', + 'logo': 'https://user-images.githubusercontent.com/1294454/27941483-79fc7350-62d9-11e7-9f61-ac47f28fcd96.jpg', + 'api': 'https://btc-trade.com.ua/api', + 'www': 'https://btc-trade.com.ua', + 'doc': 'https://docs.google.com/document/d/1ocYA0yMy_RXd561sfG3qEPZ80kyll36HUxvCRe5GbhE/edit', + }, + 'api': { + 'public': { + 'get': [ + 'deals/{symbol}', + 'trades/sell/{symbol}', + 'trades/buy/{symbol}', + 'japan_stat/high/{symbol}', + ], + }, + 'private': { + 'post': [ + 'auth', + 'ask/{symbol}', + 'balance', + 'bid/{symbol}', + 'buy/{symbol}', + 'my_orders/{symbol}', + 'order/status/{id}', + 'remove/order/{id}', + 'sell/{symbol}', + ], + }, + }, + 'markets': { + 'BCH/UAH': { 'id': 'bch_uah', 'symbol': 'BCH/UAH', 'base': 'BCH', 'quote': 'UAH', 'baseId': 'bch', 'quoteId': 'uah' }, + 'BTC/UAH': { 'id': 'btc_uah', 'symbol': 'BTC/UAH', 'base': 'BTC', 'quote': 'UAH', 'baseId': 'btc', 'quoteId': 'uah', 'precision': { 'price': 1 }, 'limits': { 'amount': { 'min': 0.0000000001 }}}, + 'DASH/BTC': { 'id': 'dash_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'dash', 'quoteId': 'btc' }, + 'DASH/UAH': { 'id': 'dash_uah', 'symbol': 'DASH/UAH', 'base': 'DASH', 'quote': 'UAH', 'baseId': 'dash', 'quoteId': 'uah' }, + 'DOGE/BTC': { 'id': 'doge_btc', 'symbol': 'DOGE/BTC', 'base': 'DOGE', 'quote': 'BTC', 'baseId': 'doge', 'quoteId': 'btc' }, + 'DOGE/UAH': { 'id': 'doge_uah', 'symbol': 'DOGE/UAH', 'base': 'DOGE', 'quote': 'UAH', 'baseId': 'doge', 'quoteId': 'uah' }, + 'ETH/UAH': { 'id': 'eth_uah', 'symbol': 'ETH/UAH', 'base': 'ETH', 'quote': 'UAH', 'baseId': 'eth', 'quoteId': 'uah' }, + 'ITI/UAH': { 'id': 'iti_uah', 'symbol': 'ITI/UAH', 'base': 'ITI', 'quote': 'UAH', 'baseId': 'iti', 'quoteId': 'uah' }, + 'KRB/UAH': { 'id': 'krb_uah', 'symbol': 'KRB/UAH', 'base': 'KRB', 'quote': 'UAH', 'baseId': 'krb', 'quoteId': 'uah' }, + 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, + 'LTC/UAH': { 'id': 'ltc_uah', 'symbol': 'LTC/UAH', 'base': 'LTC', 'quote': 'UAH', 'baseId': 'ltc', 'quoteId': 'uah' }, + 'NVC/BTC': { 'id': 'nvc_btc', 'symbol': 'NVC/BTC', 'base': 'NVC', 'quote': 'BTC', 'baseId': 'nvc', 'quoteId': 'btc' }, + 'NVC/UAH': { 'id': 'nvc_uah', 'symbol': 'NVC/UAH', 'base': 'NVC', 'quote': 'UAH', 'baseId': 'nvc', 'quoteId': 'uah' }, + 'PPC/BTC': { 'id': 'ppc_btc', 'symbol': 'PPC/BTC', 'base': 'PPC', 'quote': 'BTC', 'baseId': 'ppc', 'quoteId': 'btc' }, + 'SIB/UAH': { 'id': 'sib_uah', 'symbol': 'SIB/UAH', 'base': 'SIB', 'quote': 'UAH', 'baseId': 'sib', 'quoteId': 'uah' }, + 'XMR/UAH': { 'id': 'xmr_uah', 'symbol': 'XMR/UAH', 'base': 'XMR', 'quote': 'UAH', 'baseId': 'xmr', 'quoteId': 'uah' }, + 'ZEC/UAH': { 'id': 'zec_uah', 'symbol': 'ZEC/UAH', 'base': 'ZEC', 'quote': 'UAH', 'baseId': 'zec', 'quoteId': 'uah' }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.0006, + 'LTC': 0.01, + 'NVC': 0.01, + 'DOGE': 10, + }, + }, + }, + }); + } + + async signIn (params = {}) { + return await this.privatePostAuth (params); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'accounts'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const bids = await this.publicGetTradesBuySymbol (this.extend (request, params)); + const asks = await this.publicGetTradesSellSymbol (this.extend (request, params)); + const orderbook = { + 'bids': [], + 'asks': [], + }; + if (bids) { + if ('list' in bids) { + orderbook['bids'] = bids['list']; + } + } + if (asks) { + if ('list' in asks) { + orderbook['asks'] = asks['list']; + } + } + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'currency_trade'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetJapanStatHighSymbol (this.extend (request, params)); + const ticker = this.safeValue (response, 'trades'); + const timestamp = this.milliseconds (); + const result = { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': undefined, + 'last': undefined, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': undefined, + 'info': ticker, + }; + const tickerLength = ticker.length; + if (tickerLength > 0) { + const start = Math.max (tickerLength - 48, 0); + for (let i = start; i < ticker.length; i++) { + const candle = ticker[i]; + if (result['open'] === undefined) { + result['open'] = candle[1]; + } + if ((result['high'] === undefined) || (result['high'] < candle[2])) { + result['high'] = candle[2]; + } + if ((result['low'] === undefined) || (result['low'] > candle[3])) { + result['low'] = candle[3]; + } + if (result['baseVolume'] === undefined) { + result['baseVolume'] = -candle[5]; + } else { + result['baseVolume'] -= candle[5]; + } + } + const last = tickerLength - 1; + result['last'] = ticker[last][4]; + result['close'] = result['last']; + result['baseVolume'] = -1 * result['baseVolume']; + } + return result; + } + + convertCyrillicMonthNameToString (cyrillic) { + const months = { + '�нвар�': '01', + 'феврал�': '02', + 'марта': '03', + 'апрел�': '04', + 'ма�': '05', + 'июн�': '06', + 'июл�': '07', + 'авгу�та': '08', + '�ент�бр�': '09', + 'окт�бр�': '10', + 'но�бр�': '11', + 'декабр�': '12', + }; + return this.safeString (months, cyrillic); + } + + parseCyrillicDatetime (cyrillic) { + const parts = cyrillic.split (' '); + let day = parts[0]; + const month = this.convertCyrillicMonthNameToString (parts[1]); + if (!month) { + throw new ExchangeError (this.id + ' parseTrade() undefined month name: ' + cyrillic); + } + const year = parts[2]; + let hms = parts[4]; + const hmsLength = hms.length; + if (hmsLength === 7) { + hms = '0' + hms; + } + if (day.length === 1) { + day = '0' + day; + } + const ymd = [ year, month, day ].join ('-'); + const ymdhms = ymd + 'T' + hms; + const timestamp = this.parse8601 (ymdhms); + // server reports local time, adjust to UTC + let md = [ month, day ].join (''); + md = parseInt (md); + // a special case for DST + // subtract 2 hours during winter + if (md < 325 || md > 1028) { + return timestamp - 7200000; + } + // subtract 3 hours during summer + return timestamp - 10800000; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parseCyrillicDatetime (this.safeString (trade, 'pub_date')); + const id = this.safeString (trade, 'id'); + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amnt_trade'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetDealsSymbol (this.extend (request, params)); + // they report each trade twice (once for both of the two sides of the fill) + // deduplicate trades for that reason + const trades = []; + for (let i = 0; i < response.length; i++) { + if (response[i]['id'] % 2) { + trades.push (response[i]); + } + } + return this.parseTrades (trades, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side) + 'Id'; + const request = { + 'count': amount, + 'currency1': market['quoteId'], + 'currency': market['baseId'], + 'price': price, + }; + return this[method] (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostRemoveOrderId (this.extend (request, params)); + } + + parseOrder (order, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, // until they fix their timestamp + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': 'open', + 'symbol': symbol, + 'type': undefined, + 'side': this.safeString (order, 'type'), + 'price': this.safeFloat (order, 'price'), + 'amount': this.safeFloat (order, 'amnt_trade'), + 'filled': 0, + 'remaining': this.safeFloat (order, 'amnt_trade'), + 'trades': undefined, + 'info': order, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.privatePostMyOrdersSymbol (this.extend (request, params)); + const orders = this.safeValue (response, 'your_open_orders'); + return this.parseOrders (orders, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += this.implodeParams (path, query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'out_order_id': nonce, + 'nonce': nonce, + }, query)); + const auth = body + this.secret; + headers = { + 'public-key': this.apiKey, + 'api-sign': this.hash (this.encode (auth), 'sha256'), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/btcturk.js b/TaskServer/node_modules/ccxt/js/btcturk.js new file mode 100644 index 000000000..ecdf87034 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/btcturk.js @@ -0,0 +1,321 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class btcturk extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'btcturk', + 'name': 'BTCTurk', + 'countries': [ 'TR' ], // Turkey + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + }, + 'timeframes': { + '1d': '1d', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27992709-18e15646-64a3-11e7-9fa2-b0950ec7712f.jpg', + 'api': 'https://www.btcturk.com/api', + 'www': 'https://www.btcturk.com', + 'doc': 'https://github.com/BTCTrader/broker-api-docs', + }, + 'api': { + 'public': { + 'get': [ + 'ohlcdata', // ?last=COUNT + 'orderbook', + 'ticker', + 'trades', // ?last=COUNT (max 50) + ], + }, + 'private': { + 'get': [ + 'balance', + 'openOrders', + 'userTransactions', // ?offset=0&limit=25&sort=asc + ], + 'post': [ + 'exchange', + 'cancelOrder', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.002 * 1.18, + 'taker': 0.003 * 1.18, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTicker (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'pair'); + let baseId = id.slice (0, 3); + let quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + baseId = baseId.toLowerCase (); + quoteId = quoteId.toLowerCase (); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + const active = true; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const result = { 'info': response }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currencies[code]; + const free = currency['id'] + '_available'; + const total = currency['id'] + '_balance'; + const used = currency['id'] + '_reserved'; + if (free in response) { + const account = this.account (); + account['free'] = this.safeFloat (response, free); + account['total'] = this.safeFloat (response, total); + account['used'] = this.safeFloat (response, used); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pairSymbol': market['id'], + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'average'), + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTicker (params); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const marketId = this.safeString (ticker, 'pair'); + let symbol = marketId; + let market = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[symbol]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tickers = await this.fetchTickers (params); + return this.safeValue2 (tickers, market['id'], symbol); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // let maxCount = 50; + const request = { + 'pairSymbol': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (this.safeString (ohlcv, 'Time')); + return [ + timestamp, + this.safeFloat (ohlcv, 'Open'), + this.safeFloat (ohlcv, 'High'), + this.safeFloat (ohlcv, 'Low'), + this.safeFloat (ohlcv, 'Close'), + this.safeFloat (ohlcv, 'Volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + if (limit !== undefined) { + request['last'] = limit; + } + const response = await this.publicGetOhlcdata (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'PairSymbol': this.marketId (symbol), + 'OrderType': (side === 'buy') ? 0 : 1, + 'OrderMethod': (type === 'market') ? 1 : 0, + }; + if (type === 'market') { + if (!('Total' in params)) { + throw new ExchangeError (this.id + ' createOrder requires the "Total" extra parameter for market orders (amount and price are both ignored)'); + } + } else { + request['Price'] = price; + request['Amount'] = amount; + } + const response = await this.privatePostExchange (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (this.id === 'btctrader') { + throw new ExchangeError (this.id + ' is an abstract base API for BTCExchange, BTCTurk'); + } + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + body = this.urlencode (params); + const secret = this.base64ToBinary (this.secret); + const auth = this.apiKey + nonce; + headers = { + 'X-PCK': this.apiKey, + 'X-Stamp': nonce, + 'X-Signature': this.stringToBase64 (this.hmac (this.encode (auth), secret, 'sha256', 'binary')), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/buda.js b/TaskServer/node_modules/ccxt/js/buda.js new file mode 100644 index 000000000..fff477ca0 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/buda.js @@ -0,0 +1,786 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AddressPending, AuthenticationError, ExchangeError, NotSupported, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class buda extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'buda', + 'name': 'Buda', + 'countries': [ 'AR', 'CL', 'CO', 'PE' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': false, + 'createDepositAddress': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': true, + 'fetchMyTrades': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchTrades': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/47380619-8a029200-d706-11e8-91e0-8a391fe48de3.jpg', + 'api': 'https://www.buda.com/api', + 'www': 'https://www.buda.com', + 'doc': 'https://api.buda.com', + 'fees': 'https://www.buda.com/comisiones', + }, + 'status': { + 'status': 'error', + 'updated': undefined, + 'eta': undefined, + 'url': undefined, + }, + 'api': { + 'public': { + 'get': [ + 'pairs', + 'markets', + 'currencies', + 'markets/{market}', + 'markets/{market}/ticker', + 'markets/{market}/volume', + 'markets/{market}/order_book', + 'markets/{market}/trades', + 'currencies/{currency}/fees/deposit', + 'currencies/{currency}/fees/withdrawal', + 'tv/history', + ], + 'post': [ + 'markets/{market}/quotations', + ], + }, + 'private': { + 'get': [ + 'balances', + 'balances/{currency}', + 'currencies/{currency}/balances', + 'orders', + 'orders/{id}', + 'markets/{market}/orders', + 'deposits', + 'currencies/{currency}/deposits', + 'withdrawals', + 'currencies/{currency}/withdrawals', + 'currencies/{currency}/receive_addresses', + 'currencies/{currency}/receive_addresses/{id}', + ], + 'post': [ + 'markets/{market}/orders', + 'currencies/{currency}/deposits', + 'currencies/{currency}/withdrawals', + 'currencies/{currency}/simulated_withdrawals', + 'currencies/{currency}/receive_addresses', + ], + 'put': [ + 'orders/{id}', + ], + }, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '30m': '30', + '1h': '60', + '2h': '120', + '1d': 'D', + '1w': 'W', + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.008, // 0.8% + 'maker': 0.004, // 0.4% + 'tiers': { + 'taker': [ + [0, 0.008], // 0.8% + [2000, 0.007], // 0.7% + [20000, 0.006], // 0.6% + [100000, 0.005], // 0.5% + [500000, 0.004], // 0.4% + [2500000, 0.003], // 0.3% + [12500000, 0.002], // 0.2% + ], + 'maker': [ + [0, 0.004], // 0.4% + [2000, 0.0035], // 0.35% + [20000, 0.003], // 0.3% + [100000, 0.0025], // 0.25% + [500000, 0.002], // 0.2% + [2500000, 0.0015], // 0.15% + [12500000, 0.001], // 0.1% + ], + }, + }, + }, + 'exceptions': { + 'not_authorized': AuthenticationError, // { message: 'Invalid credentials', code: 'not_authorized' } + 'forbidden': PermissionDenied, // { message: 'You dont have access to this resource', code: 'forbidden' } + 'invalid_record': ExchangeError, // { message: 'Validation Failed', code: 'invalid_record', errors: [] } + 'not_found': ExchangeError, // { message: 'Not found', code: 'not_found' } + 'parameter_missing': ExchangeError, // { message: 'Parameter missing', code: 'parameter_missing' } + 'bad_parameter': ExchangeError, // { message: 'Bad Parameter format', code: 'bad_parameter' } + }, + }); + } + + async fetchCurrencyInfo (currency, currencies = undefined) { + if (!currencies) { + const response = await this.publicGetCurrencies (); + currencies = this.safeValue (response, 'currencies'); + } + for (let i = 0; i < currencies.length; i++) { + const currencyInfo = currencies[i]; + if (currencyInfo['id'] === currency) { + return currencyInfo; + } + } + return undefined; + } + + async fetchMarkets (params = {}) { + const marketsResponse = await this.publicGetMarkets (params); + const markets = this.safeValue (marketsResponse, 'markets'); + const currenciesResponse = await this.publicGetCurrencies (); + const currencies = this.safeValue (currenciesResponse, 'currencies'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const baseInfo = await this.fetchCurrencyInfo (baseId, currencies); + const quoteInfo = await this.fetchCurrencyInfo (quoteId, currencies); + const symbol = base + '/' + quote; + const precision = { + 'amount': baseInfo['input_decimals'], + 'price': quoteInfo['input_decimals'], + }; + const limits = { + 'amount': { + 'min': parseFloat (market['minimum_order_amount'][0]), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }; + limits['cost'] = { + 'min': limits['amount']['min'] * limits['price']['min'], + 'max': undefined, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (); + const currencies = response['currencies']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + if (!currency['managed']) { + continue; + } + const id = this.safeString (currency, 'id'); + const code = this.safeCurrencyCode (id); + const precision = this.safeFloat (currency, 'input_decimals'); + const minimum = Math.pow (10, -precision); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': undefined, + 'active': true, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minimum, + 'max': undefined, + }, + 'price': { + 'min': minimum, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'deposit': { + 'min': parseFloat (currency['deposit_minimum'][0]), + 'max': undefined, + }, + 'withdraw': { + 'min': parseFloat (currency['withdrawal_minimum'][0]), + }, + }, + }; + } + return result; + } + + async fetchFundingFees (codes = undefined, params = {}) { + // by default it will try load withdrawal fees of all currencies (with separate requests) + // however if you define codes = [ 'ETH', 'BTC' ] in args it will only load those + await this.loadMarkets (); + const withdrawFees = {}; + const depositFees = {}; + const info = {}; + if (codes === undefined) { + codes = Object.keys (this.currencies); + } + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currency = this.currency (code); + const request = { 'currency': currency['id'] }; + const withdrawResponse = await this.publicGetCurrenciesCurrencyFeesWithdrawal (request); + const depositResponse = await this.publicGetCurrenciesCurrencyFeesDeposit (request); + withdrawFees[code] = this.parseFundingFee (withdrawResponse['fee']); + depositFees[code] = this.parseFundingFee (depositResponse['fee']); + info[code] = { + 'withdraw': withdrawResponse, + 'deposit': depositResponse, + }; + } + return { + 'withdraw': withdrawFees, + 'deposit': depositFees, + 'info': info, + }; + } + + parseFundingFee (fee, type = undefined) { + if (type === undefined) { + type = fee['name']; + } + if (type === 'withdrawal') { + type = 'withdraw'; + } + return { + 'type': type, + 'currency': fee['base'][1], + 'rate': fee['percent'], + 'cost': parseFloat (fee['base'][0]), + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketsMarketTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'ticker'); + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = parseFloat (ticker['last_price'][0]); + const percentage = parseFloat (ticker['price_variation_24h']); + const open = parseFloat (this.priceToPrecision (symbol, last / (percentage + 1))); + const change = last - open; + const average = this.sum (last, open) / 2; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': parseFloat (ticker['max_bid'][0]), + 'bidVolume': undefined, + 'ask': parseFloat (ticker['min_ask'][0]), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': open, + 'change': change, + 'percentage': percentage * 100, + 'average': average, + 'baseVolume': parseFloat (ticker['volume'][0]), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + // the since argument works backwards – returns trades up to the specified timestamp + // therefore not implemented here + // the method is still available for users to be able to traverse backwards in time + // by using the timestamp from the first received trade upon each iteration + if (limit !== undefined) { + request['limit'] = limit; // 50 max + } + const response = await this.publicGetMarketsMarketTrades (this.extend (request, params)); + // + // { trades: { market_id: "ETH-BTC", + // timestamp: null, + // last_timestamp: "1536901277302", + // entries: [ [ "1540077456791", "0.0063767", "0.03", "sell", 479842 ], + // [ "1539916642772", "0.01888263", "0.03019563", "sell", 479438 ], + // [ "1539834081787", "0.023718648", "0.031001", "sell", 479069 ], + // ... ] + // + return this.parseTrades (response['trades']['entries'], market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // [ "1540077456791", "0.0063767", "0.03", "sell", 479842 ] + // + let timestamp = undefined; + let side = undefined; + const type = undefined; + let price = undefined; + let amount = undefined; + let id = undefined; + const order = undefined; + const fee = undefined; + let symbol = undefined; + let cost = undefined; + if (market) { + symbol = market['symbol']; + } + if (Array.isArray (trade)) { + timestamp = parseInt (trade[0]); + price = parseFloat (trade[1]); + amount = parseFloat (trade[2]); + cost = price * amount; + side = trade[3]; + id = trade[4].toString (); + } + return { + 'id': id, + 'order': order, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketsMarketOrderBook (this.extend (request, params)); + const orderbook = this.safeValue (response, 'order_book'); + return this.parseOrderBook (orderbook); + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (since === undefined) { + since = this.milliseconds () - 86400000; + } + const request = { + 'symbol': market['id'], + 'resolution': this.timeframes[timeframe], + 'from': since / 1000, + 'to': this.seconds (), + }; + const response = await this.publicGetTvHistory (this.extend (request, params)); + return this.parseTradingViewOHLCV (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalances (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'balances'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'id'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = parseFloat (balance['available_amount'][0]); + account['total'] = parseFloat (balance['amount'][0]); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': parseInt (id), + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + const order = this.safeValue (response, 'order'); + return this.parseOrder (order); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'market': market['id'], + 'per': limit, + }; + const response = await this.privateGetMarketsMarketOrders (this.extend (request, params)); + const orders = this.safeValue (response, 'orders'); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'pending', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'state': 'traded', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + side = (side === 'buy') ? 'Bid' : 'Ask'; + const request = { + 'market': this.marketId (symbol), + 'price_type': type, + 'type': side, + 'amount': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['limit'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostMarketsMarketOrders (this.extend (request, params)); + const order = this.safeValue (response, 'order'); + return this.parseOrder (order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': parseInt (id), + 'state': 'canceling', + }; + const response = await this.privatePutOrdersId (this.extend (request, params)); + const order = this.safeValue (response, 'order'); + return this.parseOrder (order); + } + + parseOrderStatus (status) { + const statuses = { + 'traded': 'closed', + 'received': 'open', + 'canceling': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + let symbol = undefined; + if (market === undefined) { + const marketId = order['market_id']; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const type = this.safeString (order, 'price_type'); + const side = this.safeStringLower (order, 'type'); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const amount = parseFloat (order['original_amount'][0]); + const remaining = parseFloat (order['amount'][0]); + const filled = parseFloat (order['traded_amount'][0]); + const cost = parseFloat (order['total_exchanged'][0]); + let price = this.safeFloat (order, 'limit'); + if (price !== undefined) { + price = parseFloat (price[0]); + } + if (cost > 0 && filled > 0) { + price = this.priceToPrecision (symbol, cost / filled); + } + const fee = { + 'cost': parseFloat (order['paid_fee'][0]), + 'currency': order['paid_fee'][1], + }; + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + isFiat (code) { + const fiats = { + 'ARS': true, + 'CLP': true, + 'COP': true, + 'PEN': true, + }; + return this.safeValue (fiats, code, false); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + if (this.isFiat (code)) { + throw new NotSupported (this.id + ' fetchDepositAddress() for fiat ' + code + ' is not supported'); + } + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetCurrenciesCurrencyReceiveAddresses (this.extend (request, params)); + const receiveAddresses = this.safeValue (response, 'receive_addresses'); + const addressPool = []; + for (let i = 1; i < receiveAddresses.length; i++) { + const receiveAddress = receiveAddresses[i]; + if (receiveAddress['ready']) { + const address = receiveAddress['address']; + this.checkAddress (address); + addressPool.push (address); + } + } + const addressPoolLength = addressPool.length; + if (addressPoolLength < 1) { + throw new AddressPending (this.id + ': there are no addresses ready for receiving ' + code + ', retry again later)'); + } + const address = addressPool[0]; + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': receiveAddresses, + }; + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + if (this.isFiat (code)) { + throw new NotSupported (this.id + ': fiat fetchDepositAddress() for ' + code + ' is not supported'); + } + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostCurrenciesCurrencyReceiveAddresses (this.extend (request, params)); + const address = this.safeString (response['receive_address'], 'address'); // the creation is async and returns a null address, returns only the id + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'rejected': 'failed', + 'confirmed': 'ok', + 'anulled': 'canceled', + 'retained': 'canceled', + 'pending_confirmation': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + const id = this.safeString (transaction, 'id'); + const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const amount = parseFloat (transaction['amount'][0]); + const fee = parseFloat (transaction['fee'][0]); + const feeCurrency = transaction['fee'][1]; + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const type = ('deposit_data' in transaction) ? 'deposit' : 'withdrawal'; + const data = this.safeValue (transaction, type + '_data', {}); + const address = this.safeValue (data, 'target_address'); + const txid = this.safeString (data, 'tx_hash'); + const updated = this.parse8601 (this.safeString (data, 'updated_at')); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': { + 'cost': fee, + 'rate': feeCurrency, + }, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ': fetchDeposits() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'per': limit, + }; + const response = await this.privateGetCurrenciesCurrencyDeposits (this.extend (request, params)); + const deposits = this.safeValue (response, 'deposits'); + return this.parseTransactions (deposits, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ': fetchDeposits() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'per': limit, + }; + const response = await this.privateGetCurrenciesCurrencyWithdrawals (this.extend (request, params)); + const withdrawals = this.safeValue (response, 'withdrawals'); + return this.parseTransactions (withdrawals, currency, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'withdrawal_data': { + 'target_address': address, + }, + }; + const response = await this.privatePostCurrenciesCurrencyWithdrawals (this.extend (request, params)); + const withdrawal = this.safeValue (response, 'withdrawal'); + return this.parseTransaction (withdrawal); + } + + nonce () { + return this.microseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (Object.keys (query).length) { + if (method === 'GET') { + request += '?' + this.urlencode (query); + } else { + body = this.json (query); + } + } + const url = this.urls['api'] + '/' + this.version + '/' + request; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const components = [ method, '/api/' + this.version + '/' + request ]; + if (body) { + const base64Body = this.stringToBase64 (this.encode (body)); + components.push (this.decode (base64Body)); + } + components.push (nonce); + const message = components.join (' '); + const signature = this.hmac (this.encode (message), this.encode (this.secret), 'sha384'); + headers = { + 'X-SBTC-APIKEY': this.apiKey, + 'X-SBTC-SIGNATURE': signature, + 'X-SBTC-NONCE': nonce, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if (code >= 400) { + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'message', body); + const feedback = this.id + ' ' + message; + if (errorCode !== undefined) { + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bw.js b/TaskServer/node_modules/ccxt/js/bw.js new file mode 100644 index 000000000..fcc3b1629 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bw.js @@ -0,0 +1,1187 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, AuthenticationError, ArgumentsRequired, ExchangeNotAvailable } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class bw extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bw', + 'name': 'BW', + 'countries': [ 'CN' ], + 'rateLimit': 1500, + 'version': 'v1', + 'has': { + 'cancelAllOrders': false, + 'cancelOrder': true, + 'cancelOrders': false, + 'CORS': false, + 'createDepositAddress': false, + 'createLimitOrder': true, + 'createMarketOrder': false, + 'createOrder': true, + 'deposit': false, + 'editOrder': false, + 'fetchBalance': true, + 'fetchBidsAsks': false, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': false, + 'fetchL2OrderBook': false, + 'fetchLedger': false, + 'fetchMarkets': true, + 'fetchMyTrades': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBook': true, + 'fetchOrderBooks': false, + 'fetchOrders': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTrades': true, + 'fetchTradingFee': false, + 'fetchTradingFees': false, + 'fetchTradingLimits': false, + 'fetchTransactions': false, + 'fetchWithdrawals': true, + 'privateAPI': false, + 'publicAPI': false, + 'withdraw': false, + }, + 'timeframes': { + '1m': '1M', + '5m': '5M', + '15m': '15M', + '30m': '30M', + '1h': '1H', + '1w': '1W', + }, + 'hostname': 'bw.com', // set to 'bw.io' for China mainland + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/69436317-31128c80-0d52-11ea-91d1-eb7bb5818812.jpg', + 'api': 'https://www.{hostname}', + 'www': 'https://www.bw.com', + 'doc': 'https://github.com/bw-exchange/api_docs_en/wiki', + 'fees': 'https://www.bw.com/feesRate', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.2 / 100, + 'maker': 0.2 / 100, + 'tiers': { + 'taker': [ + [ 0, 0.2 / 100 ], + ], + 'maker': [ + [ 0, 0.2 / 100 ], + ], + }, + }, + 'funding': { + }, + }, + 'exceptions': { + 'exact': { + '999': AuthenticationError, + '1000': ExchangeNotAvailable, // {"datas":null,"resMsg":{"message":"getKlines error:data not exitsts\uff0cplease wait ,dataType=4002_KLINE_1M","method":null,"code":"1000"}} + '5017': BadSymbol, // {"datas":null,"resMsg":{"message":"market not exist","method":null,"code":"5017"}} + }, + }, + 'api': { + 'public': { + 'get': [ + 'api/data/v1/klines', + 'api/data/v1/ticker', + 'api/data/v1/tickers', + 'api/data/v1/trades', + 'api/data/v1/entrusts', + 'exchange/config/controller/website/marketcontroller/getByWebId', + 'exchange/config/controller/website/currencycontroller/getCurrencyList', + ], + }, + 'private': { + 'get': [ + 'exchange/entrust/controller/website/EntrustController/getEntrustById', + 'exchange/entrust/controller/website/EntrustController/getUserEntrustRecordFromCacheWithPage', + 'exchange/entrust/controller/website/EntrustController/getUserEntrustList', + 'exchange/fund/controller/website/fundwebsitecontroller/getwithdrawaddress', + 'exchange/fund/controller/website/fundwebsitecontroller/getpayoutcoinrecord', + 'exchange/entrust/controller/website/EntrustController/getUserEntrustList', + // the docs say that the following URLs are HTTP POST + // in the docs header and HTTP GET in the docs body + // the docs contradict themselves, a typo most likely + // the actual HTTP method is POST for this endpoint + // 'exchange/fund/controller/website/fundcontroller/getPayinAddress', + // 'exchange/fund/controller/website/fundcontroller/getPayinCoinRecord', + ], + 'post': [ + 'exchange/fund/controller/website/fundcontroller/getPayinAddress', // see the comment above + 'exchange/fund/controller/website/fundcontroller/getPayinCoinRecord', // see the comment above + 'exchange/fund/controller/website/fundcontroller/findbypage', + 'exchange/entrust/controller/website/EntrustController/addEntrust', + 'exchange/entrust/controller/website/EntrustController/cancelEntrust', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeConfigControllerWebsiteMarketcontrollerGetByWebId (params); + // + // { + // "datas": [ + // { + // "orderNum":null, + // "leverEnable":true, + // "leverMultiple":10, + // "marketId":"291", + // "webId":"102", + // "serverId":"entrust_bw_23", + // "name":"eos_usdt", + // "leverType":"2", + // "buyerCurrencyId":"11", + // "sellerCurrencyId":"7", + // "amountDecimal":4, + // "priceDecimal":3, + // "minAmount":"0.0100000000", + // "state":1, + // "openTime":1572537600000, + // "defaultFee":"0.00200000", + // "createUid":null, + // "createTime":0, + // "modifyUid":null, + // "modifyTime":1574160113735, + // "combineMarketId":"", + // "isCombine":0, + // "isMining":0 + // } + // ], + // "resMsg": { "message":"success !", "method":null, "code":"1" } + // } + // + const markets = this.safeValue (response, 'datas', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'marketId'); + const numericId = parseInt (id); + const name = this.safeStringUpper (market, 'name'); + let [ base, quote ] = name.split ('_'); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const baseId = this.safeString (market, 'sellerCurrencyId'); + const quoteId = this.safeString (market, 'buyerCurrencyId'); + const baseNumericId = parseInt (baseId); + const quoteNumericId = parseInt (quoteId); + const symbol = base + '/' + quote; + const state = this.safeInteger (market, 'state'); + const active = (state === 1); + const fee = this.safeFloat (market, 'defaultFee'); + result.push ({ + 'id': id, + 'active': active, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'baseNumericId': baseNumericId, + 'quoteNumericId': quoteNumericId, + 'maker': fee, + 'taker': fee, + 'info': market, + 'precision': { + 'amount': this.safeInteger (market, 'amountDecimal'), + 'price': this.safeInteger (market, 'priceDecimal'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minAmount'), + 'max': undefined, + }, + 'price': { + 'min': 0, + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetExchangeConfigControllerWebsiteCurrencycontrollerGetCurrencyList (params); + // + // { + // "datas":[ + // { + // "currencyId":"456", + // "name":"pan", + // "alias":"pan", + // "logo":"pan.svg", + // "description":"pan", + // "descriptionEnglish":"pan", + // "defaultDecimal":2, + // "createUid":null, + // "createTime":1574068133762, + // "modifyUid":null, + // "modifyTime":0, + // "state":1, + // "mark":"pan", + // "totalNumber":"0", + // "publishNumber":"0", + // "marketValue":"0", + // "isLegalCoin":0, + // "needBlockUrl":1, + // "blockChainUrl":"https://etherscan.io/tx/", + // "tradeSearchUrl":null, + // "tokenCoinsId":0, + // "isMining":"0", + // "arithmetic":null, + // "founder":"bw_nxwal", + // "teamAddress":null, + // "remark":null, + // "tokenName":"ethw2", + // "isMemo":0, + // "websiteCurrencyId":"7rhqoHLohkG", + // "drawFlag":0, + // "rechargeFlag":1, + // "drawFee":"0.03000000", + // "onceDrawLimit":100, + // "dailyDrawLimit":500, + // "timesFreetrial":"0", + // "hourFreetrial":"0", + // "dayFreetrial":"0", + // "minFee":"0", + // "inConfigTimes":7, + // "outConfigTimes":7, + // "minCash":"0.06000000", + // "limitAmount":"0", + // "zbExist":false, + // "zone":1 + // }, + // ], + // "resMsg": { "message":"success !", "method":null, "code":"1" } + // } + // + const currencies = this.safeValue (response, 'datas', []); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'currencyId'); + const code = this.safeCurrencyCode (this.safeStringUpper (currency, 'name')); + const state = this.safeInteger (currency, 'state'); + const active = state === 1; + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': this.safeFloat (currency, 'drawFee'), + 'precision': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'limitAmount', 0), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': parseFloat (this.safeInteger (currency, 'onceDrawLimit')), + }, + }, + }; + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // [ + // "281", // market id + // "9754.4", // last + // "9968.8", // high + // "9631.5", // low + // "47865.6432", // base volume + // "-2.28", // change + // // closing price for last 6 hours + // "[[1, 9750.1], [2, 9737.1], [3, 9727.5], [4, 9722], [5, 9722.1], [6, 9754.4]]", + // "9752.12", // bid + // "9756.69", // ask + // "469849357.2364" // quote volume + // ] + // + let symbol = undefined; + const marketId = this.safeString (ticker, 0); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const close = parseFloat (this.safeValue (ticker, 1)); + const bid = this.safeValue (ticker, 'bid', {}); + const ask = this.safeValue (ticker, 'ask', {}); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': parseFloat (this.safeValue (ticker, 2)), + 'low': parseFloat (this.safeValue (ticker, 3)), + 'bid': parseFloat (this.safeValue (ticker, 7)), + 'bidVolume': this.safeFloat (bid, 'quantity'), + 'ask': parseFloat (this.safeValue (ticker, 8)), + 'askVolume': this.safeFloat (ask, 'quantity'), + 'vwap': undefined, + 'open': undefined, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': parseFloat (this.safeValue (ticker, 5)), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': parseFloat (this.safeValue (ticker, 4)), + 'quoteVolume': parseFloat (this.safeValue (ticker, 9)), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + const response = await this.publicGetApiDataV1Ticker (this.extend (request, params)); + // + // { + // "datas": [ + // "281", + // "7601.99", + // "8126.5", + // "7474.68", + // "47004.8708", + // "-6.18", + // "[[1, 7800.34], [2, 7626.41], [3, 7609.97], [4, 7569.04], [5, 7577.93], [6, 7601.99]]", + // "7600.24", + // "7603.69", + // "371968300.0119", + // ], + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + const ticker = this.safeValue (response, 'datas', []); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetApiDataV1Tickers (params); + // + // { + // "datas": [ + // [ + // "4051", + // "0.00194", + // "0.00863", + // "0.0012", + // "1519020", + // "-38.22", + // "[[1, 0.0023], [2, 0.00198], [3, 0.00199], [4, 0.00195], [5, 0.00199], [6, 0.00194]]", + // "0.00123", + // "0.0045", + // "4466.8104", + // ], + // ], + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const datas = this.safeValue (response, 'datas', []); + const result = {}; + for (let i = 0; i < datas.length; i++) { + const ticker = this.parseTicker (datas[i]); + const symbol = ticker['symbol']; + if ((symbols === undefined) || this.inArray (symbol, symbols)) { + result[symbol] = ticker; + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + if (limit !== undefined) { + request['dataSize'] = limit; + } + const response = await this.publicGetApiDataV1Entrusts (this.extend (request, params)); + // + // { + // "datas": { + // "asks": [ + // [ "9740.43", "0.0083" ], + // ], + // "bids": [ + // [ "9734.33", "0.0133" ], + // ], + // "timestamp": "1569303520", + // }, + // "resMsg": { + // "message": "success !", + // "method": null, + // "code": "1", + // }, + // } + // + const orderbook = this.safeValue (response, 'datas', []); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // [ + // "T", // trade + // "281", // market id + // "1569303302", // timestamp + // "BTC_USDT", // market name + // "ask", // side + // "9745.08", // price + // "0.0026" // amount + // ] + // + // fetchMyTrades (private) + // + // ... + // + const timestamp = this.safeTimestamp (trade, 2); + const price = this.safeFloat (trade, 5); + const amount = this.safeFloat (trade, 6); + const marketId = this.safeString (trade, 1); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const marketName = this.safeString (trade, 3); + const [ baseId, quoteId ] = marketName.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = this.costToPrecision (symbol, price * amount); + } + } + const sideString = this.safeString (trade, 4); + const side = (sideString === 'ask') ? 'sell' : 'buy'; + return { + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': parseFloat (cost), + 'fee': undefined, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + if (limit !== undefined) { + request['dataSize'] = limit; // max 20 + } + const response = await this.publicGetApiDataV1Trades (this.extend (request, params)); + // + // { + // "datas": [ + // [ + // "T", // trade + // "281", // market id + // "1569303302", // timestamp + // "BTC_USDT", // market name + // "ask", // side + // "9745.08", // price + // "0.0026" // amount + // ], + // ], + // "resMsg": { "code": "1", "method": null, "message": "success !" }, + // } + // + const trades = this.safeValue (response, 'datas', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 3), + this.safeFloat (ohlcv, 4), + this.safeFloat (ohlcv, 5), + this.safeFloat (ohlcv, 6), + this.safeFloat (ohlcv, 7), + this.safeFloat (ohlcv, 8), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'type': this.timeframes[timeframe], + 'dataSize': 500, + }; + if (limit !== undefined) { + request['dataSize'] = limit; + } + const response = await this.publicGetApiDataV1Klines (this.extend (request, params)); + const data = this.safeValue (response, 'datas', []); + const ohlcvs = this.parseOHLCVs (data, market, timeframe, since, limit); + return this.sortBy (ohlcvs, 0); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerFindbypage (params); + // + // { + // "datas": { + // "totalRow": 6, + // "pageSize": 99, + // "list": [ + // { + // "amount": "0.000090000000000000", // The current number of tokens available + // "currencyTypeId": 2, // Token ID + // "freeze": "0.009900000000000000", // Current token freezing quantity + // }, + // ], + // "pageNum": 1, + // }, + // "resMsg": { "code": "1", "message": "success !" } + // } + // + const data = this.safeValue (response, 'datas', {}); + const balances = this.safeValue (data, 'list', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeInteger (balance, 'currencyTypeId'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'amount'); + account['used'] = this.safeFloat (balance, 'freeze'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (price === undefined) { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'amount': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + 'type': (side === 'buy') ? 1 : 0, + 'rangeType': 0, // limit order + 'marketId': market['id'], + }; + const response = await this.privatePostExchangeEntrustControllerWebsiteEntrustControllerAddEntrust (this.extend (request, params)); + // + // { + // "datas": { + // "entrustId": "E6581105708337483776", + // }, + // "resMsg": { + // "message": "success !", + // "method": null, + // "code": "1" + // } + // } + // + const data = this.safeValue (response, 'datas'); + const id = this.safeString (data, 'entrustId'); + return { + 'id': id, + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'average': undefined, + 'filled': undefined, + 'remaining': undefined, + 'status': 'open', + 'fee': undefined, + 'trades': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + '-3': 'canceled', + '-2': 'canceled', + '-1': 'canceled', + '0': 'open', + '1': 'canceled', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder, fetchOpenOrders, fetchClosedOrders + // + // { + // "entrustId": "E6581108027628212224", // Order id + // "price": "1450", // price + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "amount": "14.05", // Order quantity + // "totalMoney": "20372.50", // Total order amount + // "completeAmount": "0", // Quantity sold + // "completeTotalMoney": "0", // Total dealt amount + // "type": 1, // 0 = sell, 1 = buy, -1 = cancel + // "entrustType": 0, // 0 = ordinary current price commission, 1 = lever commission + // "status": 0, // + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "availabelAmount": "14.05" // Outstanding quantity, typo in the docs or in the API, availabel vs available + // } + // + const marketId = this.safeString (order, 'marketId'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + const timestamp = this.safeInteger (order, 'createTime'); + let side = this.safeString (order, 'type'); + if (side === '0') { + side = 'sell'; + } else if (side === '1') { + side = 'buy'; + } + const amount = this.safeFloat (order, 'amount'); + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'completeAmount'); + let remaining = this.safeFloat2 (order, 'availabelAmount', 'availableAmount'); // typo in the docs or in the API, availabel vs available + let cost = this.safeFloat (order, 'totalMoney'); + if (filled !== undefined) { + if (amount !== undefined) { + if (remaining === undefined) { + remaining = amount - filled; + } + } + if (cost === undefined) { + if (price !== undefined) { + cost = filled * cost; + } + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + return { + 'info': order, + 'id': this.safeString (order, 'entrustId'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': this.safeString (market, 'symbol'), + 'type': 'limit', + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'entrustId': id, + }; + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetEntrustById (this.extend (request, params)); + // + // { + // "datas": { + // "entrustId": "E6581108027628212224", // Order id + // "price": "1450", // price + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "amount": "14.05", // Order quantity + // "totalMoney": "20372.50", // Total order amount + // "completeAmount": "0", // Quantity sold + // "completeTotalMoney": "0", // Total dealt amount + // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel + // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission + // "status": 0, // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "availabelAmount": "14.05" // Outstanding quantity + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + const order = this.safeValue (response, 'datas', {}); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'entrustId': id, + }; + const response = await this.privatePostExchangeEntrustControllerWebsiteEntrustControllerCancelEntrust (this.extend (request, params)); + // + // { + // "datas": null, + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + return { + 'info': response, + 'id': id, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageIndex': 0, // also works without it, most likely a typo in the docs + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default limit is 20 + } + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustRecordFromCacheWithPage (this.extend (request, params)); + // + // { + // "datas": { + // "pageNum": 1, + // "pageSize": 2, + // "totalPage": 20, + // "totalRow": 40, + // "entrustList": [ + // { + // "amount": "14.050000000000000000", // Order quantity + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "totalMoney": "20372.500000000000000000", // Total order amount + // "entrustId": "E6581108027628212224", // Order id + // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel + // "completeAmount": "0", // Quantity sold + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "price": "1450.000000000", // price + // "completeTotalMoney": "0", // Quantity sold + // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission + // "status": 0 // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt + // }, + // ], + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const orders = this.safeValue (data, 'entrustList', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default limit is 20 + } + if (since !== undefined) { + request['startDateTime'] = since; + } + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustList (this.extend (request, params)); + const data = this.safeValue (response, 'datas', {}); + const orders = this.safeValue (data, 'entrustList', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageIndex': 0, // also works without it, most likely a typo in the docs + // 'type': 0, // 0 = sell, 1 = buy, -1 = cancel + // 'status': -1, // -1 = insufficient funds, failed orders, 0 = pending orders, 1 = canceled, 2 = closed, 3 = partial + // 'startDateTime': since, + // 'endDateTime': this.milliseconds (), + }; + if (since !== undefined) { + request['startDateTime'] = since; + } + if (limit !== undefined) { + request['pageSize'] = limit; // default limit is 20 + } + const response = await this.privateGetExchangeEntrustControllerWebsiteEntrustControllerGetUserEntrustList (this.extend (request, params)); + // + // { + // "datas": { + // "pageNum": 1, + // "pageSize": 2, + // "totalPage": 20, + // "totalRow": 40, + // "entrustList": [ + // { + // "amount": "14.050000000000000000", // Order quantity + // "rangeType": 0, // Commission type 0: limit price commission 1: interval commission + // "totalMoney": "20372.500000000000000000", // Total order amount + // "entrustId": "E6581108027628212224", // Order id + // "type": 1, // Trade direction, 0: sell, 1: buy, -1: cancel + // "completeAmount": "0", // Quantity sold + // "marketId": "318", // The market id + // "createTime": 1569058424861, // Create time + // "price": "1450.000000000", // price + // "completeTotalMoney": "0", // Quantity sold + // "entrustType": 0, // Commission type, 0: ordinary current price commission, 1: lever commission + // "status": 0 // Order status,-3:fund Freeze exception,Order status to be confirmed -2: fund freeze failure, order failure, -1: insufficient funds, order failure, 0: pending order, 1: cancelled, 2: dealt, 3: partially dealt + // }, + // ], + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const orders = this.safeValue (data, 'entrustList', []); + return this.parseOrders (orders, market, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'], { 'hostname': this.hostname }) + '/' + path; + if (method === 'GET') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + body = this.json (params); + } + if (api === 'private') { + const ms = this.milliseconds ().toString (); + let content = ''; + if (method === 'GET') { + const sortedParams = this.keysort (params); + const keys = Object.keys (sortedParams); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + content += key + sortedParams[key].toString (); + } + } else { + content = body; + } + const signature = this.apiKey + ms + content + this.secret; + const hash = this.hash (this.encode (signature), 'md5'); + if (!headers) { + headers = {}; + } + headers['Apiid'] = this.apiKey; + headers['Timestamp'] = ms; + headers['Sign'] = hash; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyTypeName': currency['name'], + }; + const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerGetPayinAddress (this.extend (request, params)); + // + // { + // "datas": { + // "isMemo": true, // 是�为memo 格�,false:�,true :是 + // "address": "bweosdeposit_787928102918558272", // 充�地� + // "memo": "787928102918558272", // ��memo + // "account": "bweosdeposit" // ��账户 + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" } + // } + // + const data = this.safeValue (response, 'datas', {}); + const address = this.safeString (data, 'address'); + const tag = this.safeString (data, 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + '-1': 'canceled', // or auditing failed + '0': 'pending', + '1': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "depositId": "D6574268549744189441", // Deposit ID + // "amount": "54.753589700000000000", // Deposit amount + // "txId": "INNER_SYSTEM_TRANSFER_1198941", // Trading ID + // "confirmTimes": 0, // Confirmation number + // "depositAddress": "bweosdeposit_787928102918558272", // Deposit address + // "createTime": "2019-09-02 20:36:08.0", // Deposit time + // "status": 1, // Deposit status, 0: not received, 1: received + // "currencyTypeId": 7, // Token ID + // } + // + // fetchWithdrawals + // + // { + // "withdrawalId": "W6527498439872634880", // Withdrawal ID + // "fees": "0.500000000000000000", // Withdrawal fee + // "withdrawalAddress": "okbtothemoon_941657", // Withdrawal address + // "currencyId": "7", // Token ID + // "amount": "10.000000000000000000", // Withdrawal amount + // "state": 1, // Status, 1: normal, -1: delete + // "verifyStatus": 1, // Audit status, 0: to be audited, 1: auditing passed, -1: auditing failed + // "createTime": 1556276903656, // WIthdrawal time + // "actuallyAmount": "9.500000000000000000", // Actual amount received + // } + // + const id = this.safeString (transaction, 'depositId', 'withdrawalId'); + const address = this.safeString2 (transaction, 'depositAddress', 'withdrawalAddress'); + const currencyId = this.safeString2 (transaction, 'currencyId', 'currencyTypeId'); + let code = undefined; + if (currencyId in this.currencies_by_id) { + currency = this.currencies_by_id[currencyId]; + } + if ((code === undefined) && (currency !== undefined)) { + code = currency['code']; + } + const type = ('depositId' in transaction) ? 'deposit' : 'withdrawal'; + const amount = this.safeFloat2 (transaction, 'actuallyAmount', 'amount'); + const status = this.parseTransactionStatus (this.safeString2 (transaction, 'verifyStatus', 'state')); + const timestamp = this.safeInteger (transaction, 'createTime'); + const txid = this.safeString (transaction, 'txId'); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fees'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': code, + }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': address, + 'addressTo': undefined, + 'tagFrom': undefined, + 'tag': undefined, + 'tagTo': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyTypeName': currency['name'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageNum': 0, // also works without it, most likely a typo in the docs + // 'sort': 1, // 1 = asc, 0 = desc + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default 50 + } + const response = await this.privatePostExchangeFundControllerWebsiteFundcontrollerGetPayinCoinRecord (this.extend (request, params)); + // + // { + // "datas": { + // "totalRow":2, + // "totalPage": 1, + // "pageSize": 2, + // "pageNum": 1, + // "list": [ + // { + // "depositId": "D6574268549744189441", // Deposit ID + // "amount": "54.753589700000000000", // Deposit amount + // "txId": "INNER_SYSTEM_TRANSFER_1198941", // Trading ID + // "confirmTimes": 0, // Confirmation number + // "depositAddress": "bweosdeposit_787928102918558272", // Deposit address + // "createTime": "2019-09-02 20:36:08.0", // Deposit time + // "status": 1, // Deposit status, 0: not received, 1: received + // "currencyTypeId": 7, // Token ID + // }, + // ] + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const deposits = this.safeValue (data, 'list', []); + return this.parseTransactions (deposits, code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyId': currency['id'], + // 'pageSize': limit, // documented as required, but it works without it + // 'pageIndex': 0, // also works without it, most likely a typo in the docs + // 'tab': 'all', // all, wait (submitted, not audited), success (auditing passed), fail (auditing failed), cancel (canceled by user) + }; + if (limit !== undefined) { + request['pageSize'] = limit; // default 50 + } + const response = await this.privateGetExchangeFundControllerWebsiteFundwebsitecontrollerGetpayoutcoinrecord (this.extend (request, params)); + // + // { + // "datas": { + // "totalRow": 1, + // "totalPage": 1, + // "pageSize": 2, + // "pageNum": 1, + // "list": [ + // { + // "withdrawalId": "W6527498439872634880", // Withdrawal ID + // "fees": "0.500000000000000000", // Withdrawal fee + // "withdrawalAddress": "okbtothemoon_941657", // Withdrawal address + // "currencyId": "7", // Token ID + // "amount": "10.000000000000000000", // Withdrawal amount + // "state": 1, // Status, 1: normal, -1: delete + // "verifyStatus": 1, // Audit status, 0: to be audited, 1: auditing passed, -1: auditing failed + // "createTime": 1556276903656, // WIthdrawal time + // "actuallyAmount": "9.500000000000000000", // Actual amount received + // }, + // ], + // }, + // "resMsg": { "message": "success !", "method": null, "code": "1" }, + // } + // + const data = this.safeValue (response, 'datas', {}); + const withdrawals = this.safeValue (data, 'list', []); + return this.parseTransactions (withdrawals, code, since, limit); + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // default error handler + } + const resMsg = this.safeValue (response, 'resMsg'); + const errorCode = this.safeString (resMsg, 'code'); + if (errorCode !== '1') { + const feedback = this.id + ' ' + this.json (response); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + throw new ExchangeError (feedback); // unknown error + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/bytetrade.js b/TaskServer/node_modules/ccxt/js/bytetrade.js new file mode 100644 index 000000000..925504cf1 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/bytetrade.js @@ -0,0 +1,1367 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, BadRequest, AuthenticationError, DDoSProtection, BadResponse } = require ('./base/errors'); +const { TRUNCATE, NO_PADDING, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class bytetrade extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'bytetrade', + 'name': 'ByteTrade', + 'countries': ['HK'], + 'rateLimit': 500, + 'requiresWeb3': true, + 'certified': true, + // new metainfo interface + 'has': { + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'CORS': false, + 'fetchBidsAsks': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchMyTrades': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '4h': '4h', + '1d': '1d', + '5d': '5d', + '1w': '1w', + '1M': '1M', + }, + 'urls': { + 'test': 'https://api-v2-test.bytetrade.com', + 'logo': 'https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg', + 'api': 'https://api-v2.bytetrade.com', + 'www': 'https://www.bytetrade.com', + 'doc': 'https://github.com/Bytetrade/bytetrade-official-api-docs/wiki', + }, + 'api': { + 'market': { + 'get': [ + 'klines', // Kline of a symbol + 'depth', // Market Depth of a symbol + 'trades', // Trade records of a symbol + 'tickers', + ], + }, + 'public': { + 'get': [ + 'symbols', // Reference information of trading instrument, including base currency, quote precision, etc. + 'currencies', // The list of currencies available + 'balance', // Get the balance of an account + 'orders/open', // Get the open orders of an account + 'orders/closed', // Get the closed orders of an account + 'orders/all', // Get the open and closed orders of an account + 'orders', // Get the details of an order of an account + 'orders/trades', // Get detail match results + 'depositaddress', // Get deposit address + 'withdrawals', // Get withdrawals info + 'deposits', // Get deposit info + 'transfers', // Get transfer info + ], + 'post': [ + 'transaction/createorder', // Post create order transaction to blockchain + 'transaction/cancelorder', // Post cancel order transaction to blockchain + 'transaction/withdraw', // Post withdraw transaction to blockchain + 'transaction/transfer', // Post transfer transaction to blockchain + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.0008, + 'maker': 0.0008, + }, + }, + 'commonCurrencies': { + '48': 'Blocktonic', + }, + 'exceptions': { + 'vertify error': AuthenticationError, // typo on the exchange side, 'vertify' + 'verify error': AuthenticationError, // private key signature is incorrect + 'transaction already in network': BadRequest, // same transaction submited + 'invalid argument': BadRequest, + }, + }); + } + + async fetchCurrencies (params = {}) { + const currencies = await this.publicGetCurrencies (params); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'code'); + let code = undefined; + if (id in this.commonCurrencies) { + code = this.commonCurrencies[id]; + } else { + code = this.safeString (currency, 'name'); + } + const name = this.safeString (currency, 'fullname'); + // in bytetrade.com DEX, request https://api-v2.bytetrade.com/currencies will return currencies, + // the api doc is https://github.com/Bytetrade/bytetrade-official-api-docs/wiki/rest-api#get-currencies-get-currencys-supported-in-bytetradecom + // we can see the coin name is none-unique in the result, the coin which code is 18 is the CyberMiles ERC20, and the coin which code is 35 is the CyberMiles main chain, but their name is same. + // that is because bytetrade is a DEX, supports people create coin with the same name, but the id(code) of coin is unique, so we should use the id or name and id as the identity of coin. + // For coin name and symbol is same with CCXT, I use name@id as the key of commonCurrencies dict. + // [{ + // "name": "CMT", // currency name, non-unique + // "code": "18", // currency id, unique + // "type": "crypto", + // "fullname": "CyberMiles", + // "active": true, + // "chainType": "ethereum", + // "basePrecision": 18, + // "transferPrecision": 10, + // "externalPrecision": 18, + // "chainContractAddress": "0xf85feea2fdd81d51177f6b8f35f0e6734ce45f5f", + // "limits": { + // "deposit": { + // "min": "0", + // "max": "-1" + // }, + // "withdraw": { + // "min": "0", + // "max": "-1" + // } + // } + // }, + // { + // "name": "CMT", + // "code": "35", + // "type": "crypto", + // "fullname": "CyberMiles", + // "active": true, + // "chainType": "cmt", + // "basePrecision": 18, + // "transferPrecision": 10, + // "externalPrecision": 18, + // "chainContractAddress": "0x0000000000000000000000000000000000000000", + // "limits": { + // "deposit": { + // "min": "1", + // "max": "-1" + // }, + // "withdraw": { + // "min": "10", + // "max": "-1" + // } + // } + // } + // ] + const active = this.safeValue (currency, 'active'); + const limits = this.safeValue (currency, 'limits'); + const deposit = this.safeValue (limits, 'deposit'); + const amountPrecision = this.safeInteger (currency, 'basePrecision'); + let maxDeposit = this.safeFloat (deposit, 'max'); + if (maxDeposit === -1.0) { + maxDeposit = undefined; + } + const withdraw = this.safeValue (limits, 'withdraw'); + let maxWithdraw = this.safeFloat (withdraw, 'max'); + if (maxWithdraw === -1.0) { + maxWithdraw = undefined; + } + result[code] = { + 'id': id, + 'code': code, + 'name': name, + 'active': active, + 'precision': { + 'amount': amountPrecision, + 'price': undefined, + }, + 'fee': undefined, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'deposit': { + 'min': this.safeFloat (deposit, 'min'), + 'max': maxDeposit, + }, + 'withdraw': { + 'min': this.safeFloat (withdraw, 'min'), + 'max': maxWithdraw, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetSymbols (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + let base = this.safeString (market, 'baseName'); + let quote = this.safeString (market, 'quoteName'); + const baseId = this.safeString (market, 'base'); + const quoteId = this.safeString (market, 'quote'); + if (baseId in this.commonCurrencies) { + base = this.commonCurrencies[baseId]; + } + if (quoteId in this.commonCurrencies) { + quote = this.commonCurrencies[quoteId]; + } + const symbol = base + '/' + quote; + const limits = this.safeValue (market, 'limits', {}); + const amount = this.safeValue (limits, 'amount', {}); + const price = this.safeValue (limits, 'price', {}); + const precision = this.safeValue (market, 'precision', {}); + const active = this.safeString (market, 'active'); + const normalBase = base.split ('@')[0]; + const normalQuote = quote.split ('@')[0]; + const normalSymbol = normalBase + '/' + normalQuote; + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': { + 'amount': this.safeInteger (precision, 'amount'), + 'price': this.safeInteger (precision, 'price'), + }, + 'normalSymbol': normalSymbol, + 'limits': { + 'amount': { + 'min': this.safeFloat (amount, 'min'), + 'max': this.safeFloat (amount, 'max'), + }, + 'price': { + 'min': this.safeFloat (price, 'min'), + 'max': this.safeFloat (price, 'max'), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + result.push (entry); + } + return result; + } + + async fetchBalance (params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired (this.id + ' fetchDeposits requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const request = { + 'userid': this.apiKey, + }; + const balances = await this.publicGetBalance (this.extend (request, params)); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'code'); + const code = this.safeCurrencyCode (currencyId, undefined); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'free'); + account['used'] = this.safeFloat (balance, 'used'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = maximum = 100 + } + const response = await this.marketGetDepth (this.extend (request, params)); + const timestamp = this.safeValue (response, 'timestamp'); + const orderbook = this.parseOrderBook (response, timestamp); + return orderbook; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'timestamp'); + // + // [ + // { + // "symbol":"68719476706", + // "name":"ETH/BTC", + // "base":"2", + // "quote":"32", + // "timestamp":1575905991933, + // "datetime":"2019-12-09T15:39:51.933Z", + // "high":"0", + // "low":"0", + // "open":"0", + // "close":"0", + // "last":"0", + // "change":"0", + // "percentage":"0", + // "baseVolume":"0", + // "quoteVolume":"0" + // } + // ] + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (ticker, 'base'); + const quoteId = this.safeString (ticker, 'quote'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': this.safeFloat (ticker, 'weightedAvgPrice'), + 'open': this.safeFloat (ticker, 'open'), + 'close': this.safeFloat (ticker, 'close'), + 'last': this.safeFloat (ticker, 'last'), + 'previousClose': undefined, // previous day close + 'change': this.safeFloat (ticker, 'change'), + 'percentage': this.safeFloat (ticker, 'percentage'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetTickers (this.extend (request, params)); + // + // [ + // { + // "symbol":"68719476706", + // "name":"ETH/BTC", + // "base":"2", + // "quote":"32", + // "timestamp":1575905991933, + // "datetime":"2019-12-09T15:39:51.933Z", + // "high":"0", + // "low":"0", + // "open":"0", + // "close":"0", + // "last":"0", + // "change":"0", + // "percentage":"0", + // "baseVolume":"0", + // "quoteVolume":"0" + // } + // ] + // + if (Array.isArray (response)) { + const ticker = this.safeValue (response, 0); + if (ticker === undefined) { + throw new BadResponse (this.id + ' fetchTicker() returned an empty response'); + } + return this.parseTicker (ticker, market); + } + return this.parseTicker (response, market); + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + async fetchBidsAsks (symbols = undefined, params = {}) { + await this.loadMarkets (); + const rawTickers = await this.marketGetDepth (params); + return this.parseTickers (rawTickers, symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const rawTickers = await this.marketGetTickers (params); + return this.parseTickers (rawTickers, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0], + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'timeframe': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.marketGetKlines (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const cost = this.safeFloat (trade, 'cost'); + const id = this.safeString (trade, 'id'); + const type = this.safeString (trade, 'type'); + const takerOrMaker = this.safeString (trade, 'takerOrMaker'); + const side = this.safeString (trade, 'side'); + const datetime = this.safeString (trade, 'datetime'); + const order = this.safeString (trade, 'order'); + const fee = this.safeValue (trade, 'fee'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'symbol'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': datetime, + 'symbol': symbol, + 'id': id, + 'order': order, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; // default = 100, maximum = 500 + } + const response = await this.marketGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + const status = this.safeString (order, 'status'); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (order, 'base'); + const quoteId = this.safeString (order, 'quote'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'timestamp'); + const datetime = this.safeString (order, 'datetime'); + const lastTradeTimestamp = this.safeInteger (order, 'lastTradeTimestamp'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'filled'); + const remaining = this.safeFloat (order, 'remaining'); + const cost = this.safeFloat (order, 'cost'); + const average = this.safeFloat (order, 'average'); + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + const fee = this.safeValue (order, 'fee'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': datetime, + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + this.checkRequiredDependencies (); + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('createOrder requires this.apiKey or userid in params'); + } + await this.loadMarkets (); + const market = this.market (symbol); + let sideNum = undefined; + let typeNum = undefined; + if (side === 'sell') { + sideNum = 1; + } else { + sideNum = 2; + } + if (type === 'limit') { + typeNum = 1; + } else { + typeNum = 2; + } + const normalSymbol = market['normalSymbol']; + const baseId = market['baseId']; + const baseCurrency = this.currency (market['base']); + const amountTruncated = this.amountToPrecision (symbol, amount); + const amountChain = this.toWei (amountTruncated, 'ether', baseCurrency['precision']['amount']); + const quoteId = market['quoteId']; + const quoteCurrency = this.currency (market['quote']); + const priceRounded = this.priceToPrecision (symbol, price); + const priceChain = this.toWei (priceRounded, 'ether', quoteCurrency['precision']['amount']); + const now = this.milliseconds (); + const expiration = this.milliseconds (); + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const chainName = 'Sagittarius'; + const defaultFee = this.safeString (this.options, 'fee', '300000000000000'); + const fee = this.safeString (params, 'fee', defaultFee); + const eightBytes = this.integerPow ('2', '64'); + const allByteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (32, 1), + this.numberToLE (0, 8), + this.numberToLE (fee, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (sideNum, 1), + this.numberToLE (typeNum, 1), + this.numberToLE (normalSymbol.length, 1), + this.stringToBinary (this.encode (normalSymbol)), + this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (this.integerDivide (priceChain, eightBytes), 8), + this.numberToLE (this.integerModulo (priceChain, eightBytes), 8), + this.numberToLE (0, 2), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (0, 2), + this.numberToLE (parseInt (quoteId), 4), + this.numberToLE (parseInt (baseId), 4), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const txByteStringArray = [ + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (32, 1), + this.numberToLE (0, 8), + this.numberToLE (fee, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (sideNum, 1), + this.numberToLE (typeNum, 1), + this.numberToLE (normalSymbol.length, 1), + this.stringToBinary (this.encode (normalSymbol)), + this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (this.integerDivide (priceChain, eightBytes), 8), + this.numberToLE (this.integerModulo (priceChain, eightBytes), 8), + this.numberToLE (0, 2), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (0, 2), + this.numberToLE (parseInt (quoteId), 4), + this.numberToLE (parseInt (baseId), 4), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const txbytestring = this.binaryConcatArray (txByteStringArray); + const txidhash = this.hash (txbytestring, 'sha256', 'hex'); + const txid = txidhash.slice (0, 40); + const orderidByteStringArray = [ + this.numberToLE (txid.length, 1), + this.stringToBinary (this.encode (txid)), + this.numberToBE (0, 4), + ]; + const orderidbytestring = this.binaryConcatArray (orderidByteStringArray); + const orderidhash = this.hash (orderidbytestring, 'sha256', 'hex'); + const orderid = orderidhash.slice (0, 40); + const bytestring = this.binaryConcatArray (allByteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + const operation = { + 'now': datetime, + 'expiration': expirationDatetime, + 'fee': fee, + 'creator': this.apiKey, + 'side': sideNum, + 'order_type': typeNum, + 'market_name': normalSymbol, + 'amount': amountChain, + 'price': priceChain, + 'use_btt_as_fee': false, + 'money_id': parseInt (quoteId), + 'stock_id': parseInt (baseId), + }; + const fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + 32, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + const request = { + 'trObj': this.json (fatty), + }; + const response = await this.publicPostTransactionCreateorder (request); + const timestamp = this.milliseconds (); + const statusCode = this.safe_string (response, 'code'); + const status = (statusCode === '0') ? 'open' : 'failed'; + return { + 'info': response, + 'id': orderid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': undefined, + 'type': undefined, + 'side': undefined, + 'price': undefined, + 'amount': undefined, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchOrder requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const request = { + 'userid': this.apiKey, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.markets[symbol]; + request['symbol'] = market['id']; + } + request['id'] = id; + const response = await this.publicGetOrders (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchOpenOrders requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const request = { + 'userid': this.apiKey, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersOpen (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchClosedOrders requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + let market = undefined; + const request = { + 'userid': this.apiKey, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersClosed (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchOrders requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + let market = undefined; + const request = { + 'userid': this.apiKey, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersAll (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('cancelOrder requires hasAlreadyAuthenticatedSuccessfully'); + } + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const baseId = market['baseId']; + const quoteId = market['quoteId']; + const normalSymbol = market['normalSymbol']; + const feeAmount = '300000000000000'; + const now = this.milliseconds (); + const expiration = 0; + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const chainName = 'Sagittarius'; + const byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (expiration, 4), + this.numberToLE (1, 1), + this.numberToLE (33, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (normalSymbol.length, 1), + this.stringToBinary (this.encode (normalSymbol)), + this.base16ToBinary (id), + this.numberToLE (parseInt (quoteId), 4), + this.numberToLE (parseInt (baseId), 4), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const bytestring = this.binaryConcatArray (byteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + const operation = { + 'fee': feeAmount, + 'creator': this.apiKey, + 'order_id': id, + 'market_name': normalSymbol, + 'money_id': parseInt (quoteId), + 'stock_id': parseInt (baseId), + }; + const fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + 33, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + const request = { + 'trObj': this.json (fatty), + }; + const response = await this.publicPostTransactionCancelorder (request); + const timestamp = this.milliseconds (); + const statusCode = this.safe_string (response, 'code'); + const status = (statusCode === '0') ? 'canceled' : 'failed'; + return { + 'info': response, + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': undefined, + 'type': undefined, + 'side': undefined, + 'price': undefined, + 'amount': undefined, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async transfer (code, amount, address, params = {}) { + this.checkRequiredDependencies (); + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('transfer requires this.apiKey'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING); + const amountChain = this.toWei (amountTruncate, 'ether', currency['precision']['amount']); + const assetType = parseInt (currency['id']); + const now = this.milliseconds (); + const expiration = now; + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const feeAmount = '300000000000000'; + const chainName = 'Sagittarius'; + const eightBytes = this.integerPow ('2', '64'); + const byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (0, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (address.length, 1), + this.stringToBinary (this.encode (address)), + this.numberToLE (assetType, 4), + this.numberToLE (this.integerDivide (amountChain, eightBytes), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + const bytestring = this.binaryConcatArray (byteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + const operation = { + 'fee': '300000000000000', + 'from': this.apiKey, + 'to': address, + 'asset_type': parseInt (currency['id']), + 'amount': amountChain.toString (), + }; + const fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + 0, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + const request = { + 'trObj': this.json (fatty), + }; + const response = await this.publicPostTransactionTransfer (request); + const timestamp = this.milliseconds (); + const statusCode = this.safe_string (response, 'code'); + let status = ''; + if (statusCode === '0') { + status = 'submit success'; + } else { + status = 'submit fail'; + } + return { + 'info': response, + 'id': '', + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': undefined, + 'type': undefined, + 'side': undefined, + 'price': undefined, + 'amount': undefined, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'fee': undefined, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchMyTrades requires this.apiKey or userid argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'userid': this.apiKey, + }; + if (symbol !== undefined) { + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrdersTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchDeposits requires this.apiKey or userid argument'); + } + let currency = undefined; + const request = { + 'userid': this.apiKey, + }; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetDeposits (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchWithdrawals requires this.apiKey or userid argument'); + } + let currency = undefined; + const request = { + 'userid': this.apiKey, + }; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetWithdrawals (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'DEPOSIT_FAILED': 'failed', + 'FEE_SEND_FAILED': 'failed', + 'FEE_FAILED': 'failed', + 'PAY_SEND_FAILED': 'failed', + 'PAY_FAILED': 'failed', + 'BTT_FAILED': 'failed', + 'WITHDDRAW_FAILED': 'failed', + 'USER_FAILED': 'failed', + 'FEE_EXECUED': 'pending', + 'PAY_EXECUED': 'pending', + 'WITHDDRAW_EXECUTED': 'pending', + 'USER_EXECUED': 'pending', + 'BTT_SUCCED': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + let tag = this.safeString (transaction, 'tag'); + if (tag !== undefined) { + if (tag.length < 1) { + tag = undefined; + } + } + const txid = this.safeValue (transaction, 'txid'); + const currencyId = this.safeString (transaction, 'code'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeInteger (transaction, 'timestamp'); + const datetime = this.safeString (transaction, 'datetime'); + const type = this.safeString (transaction, 'type'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const amount = this.safeFloat (transaction, 'amount'); + const feeInfo = this.safeValue (transaction, 'fee'); + const feeCost = this.safeFloat (feeInfo, 'cost'); + const feeCurrencyId = this.safeString (feeInfo, 'code'); + const feeCode = this.safeCurrencyCode (feeCurrencyId, currency); + const fee = { + 'cost': feeCost, + 'currency': feeCode, + }; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': datetime, + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + if (!('userid' in params) && (this.apiKey === undefined)) { + throw new ArgumentsRequired ('fetchDepositAddress requires this.apiKey or userid argument'); + } + const currency = this.currency (code); + const request = { + 'userid': this.apiKey, + 'code': currency['id'], + }; + const response = await this.publicGetDepositaddress (request); + const address = this.safeString (response[0], 'address'); + const tag = this.safeString (response[0], 'tag'); + const chainType = this.safeString (response[0], 'chainType'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'chainType': chainType, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkRequiredDependencies (); + this.checkAddress (address); + await this.loadMarkets (); + if (this.apiKey === undefined) { + throw new ArgumentsRequired ('withdraw requires this.apiKey'); + } + const addressResponse = await this.fetchDepositAddress (code); + const chainTypeString = this.safeString (addressResponse, 'chainType'); + const chainId = this.safeString (addressResponse['info'][0], 'chainId'); + let middleAddress = ''; + if (chainTypeString === 'eos') { + middleAddress = address; + } else { + middleAddress = this.safeString (addressResponse, 'address'); + } + let operationId = 18; + if (chainTypeString !== 'ethereum' && chainTypeString !== 'etc' && chainTypeString !== 'eos' && chainTypeString !== 'cmt' && chainTypeString !== 'naka') { + operationId = 26; + } + const now = this.milliseconds (); + const expiration = 0; + let datetime = this.iso8601 (now); + datetime = datetime.split ('.')[0]; + let expirationDatetime = this.iso8601 (expiration); + expirationDatetime = expirationDatetime.split ('.')[0]; + const chainName = 'Sagittarius'; + const feeAmount = '300000000000000'; + const currency = this.currency (code); + const coinId = currency['id']; + const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING); + const amountChain = this.toWei (amountTruncate, 'ether', currency['info']['externalPrecision']); + const eightBytes = this.integerPow ('2', '64'); + let assetFee = 0; + let byteStringArray = []; + if (operationId === 26) { + assetFee = currency['info']['fee']; + byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (operationId, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (address.length, 1), + this.stringToBinary (this.encode (address)), + this.numberToLE (parseInt (coinId), 4), + this.numberToLE (Math.floor (parseInt (parseFloat (this.integerDivide (amountChain, eightBytes)))), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (1, 1), + this.numberToLE (this.integerDivide (assetFee, eightBytes), 8), + this.numberToLE (this.integerModulo (assetFee, eightBytes), 8), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + } else { + byteStringArray = [ + this.numberToBE (1, 32), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (Math.floor (expiration / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (operationId, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), // string for 32 bit php + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (Math.floor (now / 1000), 4), + this.numberToLE (1, 1), + this.numberToLE (4, 1), + this.numberToLE (0, 8), + this.numberToLE (feeAmount, 8), + this.numberToLE (this.apiKey.length, 1), + this.stringToBinary (this.encode (this.apiKey)), + this.numberToLE (middleAddress.length, 1), + this.stringToBinary (this.encode (middleAddress)), + this.numberToLE (parseInt (coinId), 4), + this.numberToLE (Math.floor (parseInt (parseFloat (this.integerDivide (amountChain, eightBytes)))), 8), + this.numberToLE (this.integerModulo (amountChain, eightBytes), 8), + this.numberToLE (0, 1), + this.numberToLE (1, 1), + this.numberToLE (chainName.length, 1), + this.stringToBinary (this.encode (chainName)), + this.numberToLE (0, 1), + ]; + } + const bytestring = this.binaryConcatArray (byteStringArray); + const hash = this.hash (bytestring, 'sha256', 'hex'); + const signature = this.ecdsa (hash, this.secret, 'secp256k1', undefined, true); + const recoveryParam = this.decode (this.binaryToBase16 (this.numberToLE (this.sum (signature['v'], 31), 1))); + const mySignature = recoveryParam + signature['r'] + signature['s']; + let fatty = undefined; + let request = undefined; + let operation = undefined; + const chainContractAddress = this.safeString (currency['info'], 'chainContractAddress'); + if (operationId === 26) { + operation = { + 'fee': feeAmount, + 'from': this.apiKey, + 'to_external_address': address, + 'asset_type': parseInt (coinId), + 'amount': amountChain, + 'asset_fee': assetFee, + }; + fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + operationId, + operation, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + request = { + 'chainType': chainId, + 'trObj': this.json (fatty), + 'chainContractAddresss': chainContractAddress, + }; + } else { + operation = { + 'fee': feeAmount, + 'from': this.apiKey, + 'to_external_address': middleAddress, + 'asset_type': parseInt (coinId), + 'amount': amountChain, + 'asset_fee': assetFee, + }; + const middle = { + 'fee': feeAmount, + 'proposaler': this.apiKey, + 'expiration_time': datetime, + 'proposed_ops': [{ + 'op': [4, operation], + }], + }; + fatty = { + 'timestamp': datetime, + 'expiration': expirationDatetime, + 'operations': [ + [ + operationId, + middle, + ], + ], + 'validate_type': 0, + 'dapp': 'Sagittarius', + 'signatures': [ + mySignature, + ], + }; + if (chainTypeString === 'eos') { + request = { + 'chainType': chainId, + 'toExternalAddress': 'noneed', + 'trObj': this.json (fatty), + 'chainContractAddresss': chainContractAddress, + }; + } else { + request = { + 'chainType': chainId, + 'toExternalAddress': address, + 'trObj': this.json (fatty), + 'chainContractAddresss': chainContractAddress, + }; + } + } + const response = await this.publicPostTransactionWithdraw (request); + return { + 'info': response, + 'id': this.safeString (response, 'id'), + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api']; + url += '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code === 503) { + throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); + } + if (response === undefined) { + return; // fallback to default error handler + } + if ('code' in response) { + const status = this.safeString (response, 'code'); + if (status === '1') { + const message = this.safeString (response, 'msg'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + throw new ExchangeError (feedback); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/cex.js b/TaskServer/node_modules/ccxt/js/cex.js new file mode 100644 index 000000000..076f2c312 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/cex.js @@ -0,0 +1,1176 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, NullResponse, InvalidOrder, NotSupported, InsufficientFunds, InvalidNonce, OrderNotFound, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class cex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'cex', + 'name': 'CEX.IO', + 'countries': [ 'GB', 'EU', 'CY', 'RU' ], + 'rateLimit': 1500, + 'has': { + 'CORS': true, + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchDepositAddress': true, + 'fetchOrders': true, + }, + 'timeframes': { + '1m': '1m', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg', + 'api': 'https://cex.io/api', + 'www': 'https://cex.io', + 'doc': 'https://cex.io/cex-api', + 'fees': [ + 'https://cex.io/fee-schedule', + 'https://cex.io/limits-commissions', + ], + 'referral': 'https://cex.io/r/0/up105393824/0/', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'currency_profile', + 'currency_limits/', + 'last_price/{pair}/', + 'last_prices/{currencies}/', + 'ohlcv/hd/{yyyymmdd}/{pair}', + 'order_book/{pair}/', + 'ticker/{pair}/', + 'tickers/{currencies}/', + 'trade_history/{pair}/', + ], + 'post': [ + 'convert/{pair}', + 'price_stats/{pair}', + ], + }, + 'private': { + 'post': [ + 'active_orders_status/', + 'archived_orders/{pair}/', + 'balance/', + 'cancel_order/', + 'cancel_orders/{pair}/', + 'cancel_replace_order/{pair}/', + 'close_position/{pair}/', + 'get_address/', + 'get_myfee/', + 'get_order/', + 'get_order_tx/', + 'open_orders/{pair}/', + 'open_orders/', + 'open_position/{pair}/', + 'open_positions/{pair}/', + 'place_order/{pair}/', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.16 / 100, + 'taker': 0.25 / 100, + }, + 'funding': { + 'withdraw': { + // 'USD': undefined, + // 'EUR': undefined, + // 'RUB': undefined, + // 'GBP': undefined, + 'BTC': 0.001, + 'ETH': 0.01, + 'BCH': 0.001, + 'DASH': 0.01, + 'BTG': 0.001, + 'ZEC': 0.001, + 'XRP': 0.02, + }, + 'deposit': { + // 'USD': amount => amount * 0.035 + 0.25, + // 'EUR': amount => amount * 0.035 + 0.24, + // 'RUB': amount => amount * 0.05 + 15.57, + // 'GBP': amount => amount * 0.035 + 0.2, + 'BTC': 0.0, + 'ETH': 0.0, + 'BCH': 0.0, + 'DASH': 0.0, + 'BTG': 0.0, + 'ZEC': 0.0, + 'XRP': 0.0, + 'XLM': 0.0, + }, + }, + }, + 'exceptions': { + 'exact': {}, + 'broad': { + 'Insufficient funds': InsufficientFunds, + 'Nonce must be incremented': InvalidNonce, + 'Invalid Order': InvalidOrder, + 'Order not found': OrderNotFound, + 'Rate limit exceeded': RateLimitExceeded, + }, + }, + 'options': { + 'fetchOHLCVWarning': true, + 'createMarketBuyOrderRequiresPrice': true, + 'order': { + 'status': { + 'c': 'canceled', + 'd': 'closed', + 'cd': 'closed', + 'a': 'open', + }, + }, + }, + }); + } + + async fetchCurrenciesFromCache (params = {}) { + // this method is now redundant + // currencies are now fetched before markets + const options = this.safeValue (this.options, 'fetchCurrencies', {}); + const timestamp = this.safeInteger (options, 'timestamp'); + const expires = this.safeInteger (options, 'expires', 1000); + const now = this.milliseconds (); + if ((timestamp === undefined) || ((now - timestamp) > expires)) { + const response = await this.publicGetCurrencyProfile (params); + this.options['fetchCurrencies'] = this.extend (options, { + 'response': response, + 'timestamp': now, + }); + } + return this.safeValue (this.options['fetchCurrencies'], 'response'); + } + + async fetchCurrencies (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + this.options['currencies'] = { + 'timestamp': this.milliseconds (), + 'response': response, + }; + // + // { + // "e":"currency_profile", + // "ok":"ok", + // "data":{ + // "symbols":[ + // { + // "code":"GHS", + // "contract":true, + // "commodity":true, + // "fiat":false, + // "description":"CEX.IO doesn't provide cloud mining services anymore.", + // "precision":8, + // "scale":0, + // "minimumCurrencyAmount":"0.00000001", + // "minimalWithdrawalAmount":-1 + // }, + // { + // "code":"BTC", + // "contract":false, + // "commodity":false, + // "fiat":false, + // "description":"", + // "precision":8, + // "scale":0, + // "minimumCurrencyAmount":"0.00000001", + // "minimalWithdrawalAmount":0.002 + // }, + // { + // "code":"ETH", + // "contract":false, + // "commodity":false, + // "fiat":false, + // "description":"", + // "precision":8, + // "scale":2, + // "minimumCurrencyAmount":"0.00000100", + // "minimalWithdrawalAmount":0.01 + // } + // ], + // "pairs":[ + // { + // "symbol1":"BTC", + // "symbol2":"USD", + // "pricePrecision":1, + // "priceScale":"/1000000", + // "minLotSize":0.002, + // "minLotSizeS2":20 + // }, + // { + // "symbol1":"ETH", + // "symbol2":"USD", + // "pricePrecision":2, + // "priceScale":"/10000", + // "minLotSize":0.1, + // "minLotSizeS2":20 + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', []); + const currencies = this.safeValue (data, 'symbols', []); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'code'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'precision'); + const active = true; + result[code] = { + 'id': id, + 'code': code, + 'name': id, + 'active': active, + 'precision': precision, + 'fee': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minimumCurrencyAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimalWithdrawalAmount'), + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const currenciesResponse = await this.fetchCurrenciesFromCache (params); + const currenciesData = this.safeValue (currenciesResponse, 'data', {}); + const currencies = this.safeValue (currenciesData, 'symbols', []); + const currenciesById = this.indexBy (currencies, 'code'); + const pairs = this.safeValue (currenciesData, 'pairs', []); + const response = await this.publicGetCurrencyLimits (params); + // + // { + // "e":"currency_limits", + // "ok":"ok", + // "data": { + // "pairs":[ + // { + // "symbol1":"BTC", + // "symbol2":"USD", + // "minLotSize":0.002, + // "minLotSizeS2":20, + // "maxLotSize":30, + // "minPrice":"1500", + // "maxPrice":"35000" + // }, + // { + // "symbol1":"BCH", + // "symbol2":"EUR", + // "minLotSize":0.1, + // "minLotSizeS2":20, + // "maxLotSize":null, + // "minPrice":"25", + // "maxPrice":"8192" + // } + // ] + // } + // } + // + const result = []; + const markets = this.safeValue (response['data'], 'pairs'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = this.safeString (market, 'symbol1'); + const quoteId = this.safeString (market, 'symbol2'); + const id = baseId + '/' + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const baseCurrency = this.safeValue (currenciesById, baseId, {}); + const quoteCurrency = this.safeValue (currenciesById, quoteId, {}); + let pricePrecision = this.safeInteger (quoteCurrency, 'precision', 8); + for (let j = 0; j < pairs.length; j++) { + const pair = pairs[j]; + if ((pair['symbol1'] === baseId) && (pair['symbol2'] === quoteId)) { + // we might need to account for `priceScale` here + pricePrecision = this.safeInteger (pair, 'pricePrecision', pricePrecision); + } + } + const baseCcyPrecision = this.safeInteger (baseCurrency, 'precision', 8); + const baseCcyScale = this.safeInteger (baseCurrency, 'scale', 0); + const amountPrecision = baseCcyPrecision - baseCcyScale; + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + result.push ({ + 'id': id, + 'info': market, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minLotSize'), + 'max': this.safeFloat (market, 'maxLotSize'), + }, + 'price': { + 'min': this.safeFloat (market, 'minPrice'), + 'max': this.safeFloat (market, 'maxPrice'), + }, + 'cost': { + 'min': this.safeFloat (market, 'minLotSizeS2'), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const ommited = [ 'username', 'timestamp' ]; + const balances = this.omit (response, ommited); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + // https://github.com/ccxt/ccxt/issues/5484 + account['used'] = this.safeFloat (balance, 'orders', 0.0); + const code = this.safeCurrencyCode (currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + if (limit !== undefined) { + request['depth'] = limit; + } + const response = await this.publicGetOrderBookPair (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (since === undefined) { + since = this.milliseconds () - 86400000; // yesterday + } else { + if (this.options['fetchOHLCVWarning']) { + throw new ExchangeError (this.id + " fetchOHLCV warning: CEX can return historical candles for a certain date only, this might produce an empty or null reply. Set exchange.options['fetchOHLCVWarning'] = false or add ({ 'options': { 'fetchOHLCVWarning': false }}) to constructor params to suppress this warning message."); + } + } + let ymd = this.ymd (since); + ymd = ymd.split ('-'); + ymd = ymd.join (''); + const request = { + 'pair': market['id'], + 'yyyymmdd': ymd, + }; + try { + const response = await this.publicGetOhlcvHdYyyymmddPair (this.extend (request, params)); + const key = 'data' + this.timeframes[timeframe]; + const ohlcvs = JSON.parse (response[key]); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } catch (e) { + if (e instanceof NullResponse) { + return []; + } + } + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const volume = this.safeFloat (ticker, 'volume'); + const high = this.safeFloat (ticker, 'high'); + const low = this.safeFloat (ticker, 'low'); + const bid = this.safeFloat (ticker, 'bid'); + const ask = this.safeFloat (ticker, 'ask'); + const last = this.safeFloat (ticker, 'last'); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': high, + 'low': low, + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': volume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const currencies = Object.keys (this.currencies); + const request = { + 'currencies': currencies.join ('/'), + }; + const response = await this.publicGetTickersCurrencies (this.extend (request, params)); + const tickers = response['data']; + const result = {}; + for (let t = 0; t < tickers.length; t++) { + const ticker = tickers[t]; + const symbol = ticker['pair'].replace (':', '/'); + const market = this.markets[symbol]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const ticker = await this.publicGetTickerPair (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTradeHistoryPair (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + // for market buy it requires the amount of quote currency to spend + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + amount = amount * price; + } + } + } + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + 'type': side, + 'amount': amount, + }; + if (type === 'limit') { + request['price'] = price; + } else { + request['order_type'] = type; + } + const response = await this.privatePostPlaceOrderPair (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + parseOrder (order, market = undefined) { + // Depending on the call, 'time' can be a unix int, unix string or ISO string + // Yes, really + let timestamp = this.safeValue (order, 'time'); + if (typeof timestamp === 'string' && timestamp.indexOf ('T') >= 0) { + // ISO8601 string + timestamp = this.parse8601 (timestamp); + } else { + // either integer or string integer + timestamp = parseInt (timestamp); + } + let symbol = undefined; + if (market === undefined) { + const baseId = this.safeString (order, 'symbol1'); + const quoteId = this.safeString (order, 'symbol2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + if (symbol in this.markets) { + market = this.market (symbol); + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'price'); + let amount = this.safeFloat (order, 'amount'); + // sell orders can have a negative amount + // https://github.com/ccxt/ccxt/issues/5338 + if (amount !== undefined) { + amount = Math.abs (amount); + } + const remaining = this.safeFloat2 (order, 'pending', 'remains'); + const filled = amount - remaining; + let fee = undefined; + let cost = undefined; + if (market !== undefined) { + symbol = market['symbol']; + cost = this.safeFloat (order, 'ta:' + market['quote']); + if (cost === undefined) { + cost = this.safeFloat (order, 'tta:' + market['quote']); + } + const baseFee = 'fa:' + market['base']; + const baseTakerFee = 'tfa:' + market['base']; + const quoteFee = 'fa:' + market['quote']; + const quoteTakerFee = 'tfa:' + market['quote']; + let feeRate = this.safeFloat (order, 'tradingFeeMaker'); + if (!feeRate) { + feeRate = this.safeFloat (order, 'tradingFeeTaker', feeRate); + } + if (feeRate) { + feeRate /= 100.0; // convert to mathematically-correct percentage coefficients: 1.0 = 100% + } + if ((baseFee in order) || (baseTakerFee in order)) { + const baseFeeCost = this.safeFloat2 (order, baseFee, baseTakerFee); + fee = { + 'currency': market['base'], + 'rate': feeRate, + 'cost': baseFeeCost, + }; + } else if ((quoteFee in order) || (quoteTakerFee in order)) { + const quoteFeeCost = this.safeFloat2 (order, quoteFee, quoteTakerFee); + fee = { + 'currency': market['quote'], + 'rate': feeRate, + 'cost': quoteFeeCost, + }; + } + } + if (!cost) { + cost = price * filled; + } + const side = order['type']; + let trades = undefined; + const orderId = order['id']; + if ('vtx' in order) { + trades = []; + for (let i = 0; i < order['vtx'].length; i++) { + const item = order['vtx'][i]; + const tradeSide = this.safeString (item, 'type'); + if (tradeSide === 'cancel') { + // looks like this might represent the cancelled part of an order + // { id: '4426729543', + // type: 'cancel', + // time: '2017-09-22T00:24:30.476Z', + // user: 'up106404164', + // c: 'user:up106404164:a:BCH', + // d: 'order:4426728375:a:BCH', + // a: '0.09935956', + // amount: '0.09935956', + // balance: '0.42580261', + // symbol: 'BCH', + // order: '4426728375', + // buy: null, + // sell: null, + // pair: null, + // pos: null, + // cs: '0.42580261', + // ds: 0 } + continue; + } + const tradePrice = this.safeFloat (item, 'price'); + if (tradePrice === undefined) { + // this represents the order + // { + // "a": "0.47000000", + // "c": "user:up106404164:a:EUR", + // "d": "order:6065499239:a:EUR", + // "cs": "1432.93", + // "ds": "476.72", + // "id": "6065499249", + // "buy": null, + // "pos": null, + // "pair": null, + // "sell": null, + // "time": "2018-04-22T13:07:22.152Z", + // "type": "buy", + // "user": "up106404164", + // "order": "6065499239", + // "amount": "-715.97000000", + // "symbol": "EUR", + // "balance": "1432.93000000" } + continue; + } + // todo: deal with these + if (tradeSide === 'costsNothing') { + continue; + } + // -- + // if (side !== tradeSide) + // throw new Error (JSON.stringify (order, null, 2)); + // if (orderId !== item['order']) + // throw new Error (JSON.stringify (order, null, 2)); + // -- + // partial buy trade + // { + // "a": "0.01589885", + // "c": "user:up106404164:a:BTC", + // "d": "order:6065499239:a:BTC", + // "cs": "0.36300000", + // "ds": 0, + // "id": "6067991213", + // "buy": "6065499239", + // "pos": null, + // "pair": null, + // "sell": "6067991206", + // "time": "2018-04-22T23:09:11.773Z", + // "type": "buy", + // "user": "up106404164", + // "order": "6065499239", + // "price": 7146.5, + // "amount": "0.01589885", + // "symbol": "BTC", + // "balance": "0.36300000", + // "symbol2": "EUR", + // "fee_amount": "0.19" } + // -- + // trade with zero amount, but non-zero fee + // { + // "a": "0.00000000", + // "c": "user:up106404164:a:EUR", + // "d": "order:5840654423:a:EUR", + // "cs": 559744, + // "ds": 0, + // "id": "5840654429", + // "buy": "5807238573", + // "pos": null, + // "pair": null, + // "sell": "5840654423", + // "time": "2018-03-15T03:20:14.010Z", + // "type": "sell", + // "user": "up106404164", + // "order": "5840654423", + // "price": 730, + // "amount": "0.00000000", + // "symbol": "EUR", + // "balance": "5597.44000000", + // "symbol2": "BCH", + // "fee_amount": "0.01" } + // -- + // trade which should have an amount of exactly 0.002BTC + // { + // "a": "16.70000000", + // "c": "user:up106404164:a:GBP", + // "d": "order:9927386681:a:GBP", + // "cs": "86.90", + // "ds": 0, + // "id": "9927401610", + // "buy": "9927401601", + // "pos": null, + // "pair": null, + // "sell": "9927386681", + // "time": "2019-08-21T15:25:37.777Z", + // "type": "sell", + // "user": "up106404164", + // "order": "9927386681", + // "price": 8365, + // "amount": "16.70000000", + // "office": "UK", + // "symbol": "GBP", + // "balance": "86.90000000", + // "symbol2": "BTC", + // "fee_amount": "0.03" + // } + const tradeTimestamp = this.parse8601 (this.safeString (item, 'time')); + const tradeAmount = this.safeFloat (item, 'amount'); + const feeCost = this.safeFloat (item, 'fee_amount'); + let absTradeAmount = (tradeAmount < 0) ? -tradeAmount : tradeAmount; + let tradeCost = undefined; + if (tradeSide === 'sell') { + tradeCost = absTradeAmount; + absTradeAmount = this.sum (feeCost, tradeCost) / tradePrice; + } else { + tradeCost = absTradeAmount * tradePrice; + } + trades.push ({ + 'id': this.safeString (item, 'id'), + 'timestamp': tradeTimestamp, + 'datetime': this.iso8601 (tradeTimestamp), + 'order': orderId, + 'symbol': symbol, + 'price': tradePrice, + 'amount': absTradeAmount, + 'cost': tradeCost, + 'side': tradeSide, + 'fee': { + 'cost': feeCost, + 'currency': market['quote'], + }, + 'info': item, + }); + } + } + return { + 'id': orderId, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': (price === undefined) ? 'market' : 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': fee, + 'info': order, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'privatePostOpenOrders'; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + method += 'Pair'; + } + const orders = await this[method] (this.extend (request, params)); + for (let i = 0; i < orders.length; i++) { + orders[i] = this.extend (orders[i], { 'status': 'open' }); + } + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostArchivedOrdersPair'; + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a symbol argument'); + } + const market = this.market (symbol); + const request = { 'pair': market['id'] }; + const response = await this[method] (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id.toString (), + }; + const response = await this.privatePostGetOrderTx (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'limit': limit, + 'pair': market['id'], + 'dateFrom': since, + }; + const response = await this.privatePostArchivedOrdersPair (this.extend (request, params)); + const results = []; + for (let i = 0; i < response.length; i++) { + // cancelled (unfilled): + // { id: '4005785516', + // type: 'sell', + // time: '2017-07-18T19:08:34.223Z', + // lastTxTime: '2017-07-18T19:08:34.396Z', + // lastTx: '4005785522', + // pos: null, + // status: 'c', + // symbol1: 'ETH', + // symbol2: 'GBP', + // amount: '0.20000000', + // price: '200.5625', + // remains: '0.20000000', + // 'a:ETH:cds': '0.20000000', + // tradingFeeMaker: '0', + // tradingFeeTaker: '0.16', + // tradingFeeUserVolumeAmount: '10155061217', + // orderId: '4005785516' } + // -- + // cancelled (partially filled buy): + // { id: '4084911657', + // type: 'buy', + // time: '2017-08-05T03:18:39.596Z', + // lastTxTime: '2019-03-19T17:37:46.404Z', + // lastTx: '8459265833', + // pos: null, + // status: 'cd', + // symbol1: 'BTC', + // symbol2: 'GBP', + // amount: '0.05000000', + // price: '2241.4692', + // tfacf: '1', + // remains: '0.03910535', + // 'tfa:GBP': '0.04', + // 'tta:GBP': '24.39', + // 'a:BTC:cds': '0.01089465', + // 'a:GBP:cds': '112.26', + // 'f:GBP:cds': '0.04', + // tradingFeeMaker: '0', + // tradingFeeTaker: '0.16', + // tradingFeeUserVolumeAmount: '13336396963', + // orderId: '4084911657' } + // -- + // cancelled (partially filled sell): + // { id: '4426728375', + // type: 'sell', + // time: '2017-09-22T00:24:20.126Z', + // lastTxTime: '2017-09-22T00:24:30.476Z', + // lastTx: '4426729543', + // pos: null, + // status: 'cd', + // symbol1: 'BCH', + // symbol2: 'BTC', + // amount: '0.10000000', + // price: '0.11757182', + // tfacf: '1', + // remains: '0.09935956', + // 'tfa:BTC': '0.00000014', + // 'tta:BTC': '0.00007537', + // 'a:BCH:cds': '0.10000000', + // 'a:BTC:cds': '0.00007537', + // 'f:BTC:cds': '0.00000014', + // tradingFeeMaker: '0', + // tradingFeeTaker: '0.18', + // tradingFeeUserVolumeAmount: '3466715450', + // orderId: '4426728375' } + // -- + // filled: + // { id: '5342275378', + // type: 'sell', + // time: '2018-01-04T00:28:12.992Z', + // lastTxTime: '2018-01-04T00:28:12.992Z', + // lastTx: '5342275393', + // pos: null, + // status: 'd', + // symbol1: 'BCH', + // symbol2: 'BTC', + // amount: '0.10000000', + // kind: 'api', + // price: '0.17', + // remains: '0.00000000', + // 'tfa:BTC': '0.00003902', + // 'tta:BTC': '0.01699999', + // 'a:BCH:cds': '0.10000000', + // 'a:BTC:cds': '0.01699999', + // 'f:BTC:cds': '0.00003902', + // tradingFeeMaker: '0.15', + // tradingFeeTaker: '0.23', + // tradingFeeUserVolumeAmount: '1525951128', + // orderId: '5342275378' } + // -- + // market order (buy): + // { "id": "6281946200", + // "pos": null, + // "time": "2018-05-23T11:55:43.467Z", + // "type": "buy", + // "amount": "0.00000000", + // "lastTx": "6281946210", + // "status": "d", + // "amount2": "20.00", + // "orderId": "6281946200", + // "remains": "0.00000000", + // "symbol1": "ETH", + // "symbol2": "EUR", + // "tfa:EUR": "0.05", + // "tta:EUR": "19.94", + // "a:ETH:cds": "0.03764100", + // "a:EUR:cds": "20.00", + // "f:EUR:cds": "0.05", + // "lastTxTime": "2018-05-23T11:55:43.467Z", + // "tradingFeeTaker": "0.25", + // "tradingFeeUserVolumeAmount": "55998097" } + // -- + // market order (sell): + // { "id": "6282200948", + // "pos": null, + // "time": "2018-05-23T12:42:58.315Z", + // "type": "sell", + // "amount": "-0.05000000", + // "lastTx": "6282200958", + // "status": "d", + // "orderId": "6282200948", + // "remains": "0.00000000", + // "symbol1": "ETH", + // "symbol2": "EUR", + // "tfa:EUR": "0.07", + // "tta:EUR": "26.49", + // "a:ETH:cds": "0.05000000", + // "a:EUR:cds": "26.49", + // "f:EUR:cds": "0.07", + // "lastTxTime": "2018-05-23T12:42:58.315Z", + // "tradingFeeTaker": "0.25", + // "tradingFeeUserVolumeAmount": "56294576" } + const order = response[i]; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const baseId = this.safeString (order, 'symbol1'); + const quoteId = this.safeString (order, 'symbol2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const side = this.safeString (order, 'type'); + const baseAmount = this.safeFloat (order, 'a:' + baseId + ':cds'); + const quoteAmount = this.safeFloat (order, 'a:' + quoteId + ':cds'); + const fee = this.safeFloat (order, 'f:' + quoteId + ':cds'); + const amount = this.safeFloat (order, 'amount'); + const price = this.safeFloat (order, 'price'); + const remaining = this.safeFloat (order, 'remains'); + const filled = amount - remaining; + let orderAmount = undefined; + let cost = undefined; + let average = undefined; + let type = undefined; + if (!price) { + type = 'market'; + orderAmount = baseAmount; + cost = quoteAmount; + average = orderAmount / cost; + } else { + const ta = this.safeFloat (order, 'ta:' + quoteId, 0); + const tta = this.safeFloat (order, 'tta:' + quoteId, 0); + const fa = this.safeFloat (order, 'fa:' + quoteId, 0); + const tfa = this.safeFloat (order, 'tfa:' + quoteId, 0); + if (side === 'sell') { + cost = this.sum (this.sum (ta, tta), this.sum (fa, tfa)); + } else { + cost = this.sum (ta, tta) - this.sum (fa, tfa); + } + type = 'limit'; + orderAmount = amount; + average = cost / filled; + } + const time = this.safeString (order, 'time'); + const lastTxTime = this.safeString (order, 'lastTxTime'); + const timestamp = this.parse8601 (time); + results.push ({ + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastUpdated': this.parse8601 (lastTxTime), + 'status': status, + 'symbol': symbol, + 'side': side, + 'price': price, + 'amount': orderAmount, + 'average': average, + 'type': type, + 'filled': filled, + 'cost': cost, + 'remaining': remaining, + 'fee': { + 'cost': fee, + 'currency': quote, + }, + 'info': order, + }); + } + return results; + } + + parseOrderStatus (status) { + return this.safeString (this.options['order']['status'], status, status); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + if (amount === undefined) { + throw new ArgumentsRequired (this.id + ' editOrder requires a amount argument'); + } + if (price === undefined) { + throw new ArgumentsRequired (this.id + ' editOrder requires a price argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // see: https://cex.io/rest-api#/definitions/CancelReplaceOrderRequest + const request = { + 'pair': market['id'], + 'type': side, + 'amount': amount, + 'price': price, + 'order_id': id, + }; + const response = await this.privatePostCancelReplaceOrderPair (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchDepositAddress (code, params = {}) { + if (code === 'XRP' || code === 'XLM') { + // https://github.com/ccxt/ccxt/pull/2327#issuecomment-375204856 + throw new NotSupported (this.id + ' fetchDepositAddress does not support XRP and XLM addresses yet (awaiting docs from CEX.io)'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostGetAddress (this.extend (request, params)); + const address = this.safeString (response, 'data'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.json (this.extend ({ + 'key': this.apiKey, + 'signature': signature.toUpperCase (), + 'nonce': nonce, + }, query)); + headers = { + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (Array.isArray (response)) { + return response; // public endpoints may return []-arrays + } + if (response === undefined) { + throw new NullResponse (this.id + ' returned ' + this.json (response)); + } + if (response === true || response === 'true') { + return; + } + if ('e' in response) { + if ('ok' in response) { + if (response['ok'] === 'ok') { + return; + } + } + } + if ('error' in response) { + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/chilebit.js b/TaskServer/node_modules/ccxt/js/chilebit.js new file mode 100644 index 000000000..4aeeb9ec3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/chilebit.js @@ -0,0 +1,32 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const foxbit = require ('./foxbit.js'); + +// --------------------------------------------------------------------------- + +module.exports = class chilebit extends foxbit { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'chilebit', + 'name': 'ChileBit', + 'countries': [ 'CL' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991414-1298f0d8-647f-11e7-9c40-d56409266336.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://chilebit.net', + 'doc': 'https://blinktrade.com/docs', + }, + 'options': { + 'brokerId': '9', // https://blinktrade.com/docs/#brokers + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/cobinhood.js b/TaskServer/node_modules/ccxt/js/cobinhood.js new file mode 100644 index 000000000..4a1ac33a6 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/cobinhood.js @@ -0,0 +1,861 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InvalidAddress, InsufficientFunds, InvalidNonce, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class cobinhood extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'cobinhood', + 'name': 'COBINHOOD', + 'countries': [ 'TW' ], + 'rateLimit': 1000 / 10, + 'version': 'v1', + 'has': { + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrderTrades': true, + 'fetchOrder': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'withdraw': true, + 'fetchMyTrades': true, + 'editOrder': true, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + }, + 'timeframes': { + // the first two don't seem to work at all + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '3h': '3h', + '6h': '6h', + '12h': '12h', + '1d': '1D', + '1w': '7D', + '2w': '14D', + '1M': '1M', + }, + 'urls': { + 'referral': 'https://cobinhood.com?referrerId=a9d57842-99bb-4d7c-b668-0479a15a458b', + 'logo': 'https://user-images.githubusercontent.com/1294454/35755576-dee02e5c-0878-11e8-989f-1595d80ba47f.jpg', + 'api': 'https://api.cobinhood.com', + 'www': 'https://cobinhood.com', + 'doc': 'https://cobinhood.github.io/api-public', + }, + 'api': { + 'system': { + 'get': [ + 'info', + 'time', + 'messages', + 'messages/{message_id}', + ], + }, + 'admin': { + 'get': [ + 'system/messages', + 'system/messages/{message_id}', + ], + 'post': [ + 'system/messages', + ], + 'patch': [ + 'system/messages/{message_id}', + ], + 'delete': [ + 'system/messages/{message_id}', + ], + }, + 'public': { + 'get': [ + 'market/fundingbook/precisions/{currency_id}', + 'market/fundingbooks/{currency_id}', + 'market/tickers', + 'market/currencies', + 'market/quote_currencies', + 'market/trading_pairs', + 'market/orderbook/precisions/{trading_pair_id}', + 'market/orderbooks/{trading_pair_id}', + 'market/stats', + 'market/tickers', // fetchTickers + 'market/tickers/{trading_pair_id}', + 'market/trades/{trading_pair_id}', + 'market/trades_history/{trading_pair_id}', + 'market/trading_pairs', + 'chart/candles/{trading_pair_id}', + 'system/time', + ], + }, + 'private': { + 'get': [ + 'funding/auto_offerings', + 'funding/auto_offerings/{currency_id}', + 'funding/funding_history', + 'funding/fundings', + 'funding/loans', + 'funding/loans/{loan_id}', + 'trading/orders/{order_id}', + 'trading/orders/{order_id}/trades', + 'trading/orders', + 'trading/order_history', + 'trading/positions', + 'trading/positions/{trading_pair_id}', + 'trading/positions/{trading_pair_id}/claimable_size', + 'trading/trades', + 'trading/trades/{trade_id}', + 'trading/volume', + 'wallet/balances', + 'wallet/ledger', + 'wallet/limits/withdrawal', + 'wallet/generic_deposits', + 'wallet/generic_deposits/{generic_deposit_id}', + 'wallet/generic_withdrawals', + 'wallet/generic_withdrawals/{generic_withdrawal_id}', + // older endpoints + 'wallet/deposit_addresses', + 'wallet/deposit_addresses/iota', + 'wallet/withdrawal_addresses', + 'wallet/withdrawal_frozen', + 'wallet/withdrawals/{withdrawal_id}', + 'wallet/withdrawals', + 'wallet/deposits/{deposit_id}', + 'wallet/deposits', + ], + 'patch': [ + 'trading/positions/{trading_pair_id}', + ], + 'post': [ + 'funding/auto_offerings', + 'funding/fundings', + 'trading/check_order', + 'trading/orders', + // older endpoints + 'wallet/deposit_addresses', + 'wallet/transfer', + 'wallet/withdrawal_addresses', + 'wallet/withdrawals', + 'wallet/withdrawals/fee', + ], + 'put': [ + 'funding/fundings/{funding_id}', + 'trading/orders/{order_id}', + ], + 'delete': [ + 'funding/auto_offerings/{currency_id}', + 'funding/fundings/{funding_id}', + 'funding/loans/{loan_id}', + 'trading/orders/{order_id}', + 'trading/positions/{trading_pair_id}', + 'wallet/generic_withdrawals/{generic_withdrawal_id}', + 'wallet/withdrawal_addresses/{wallet_id}', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.0, + 'taker': 0.0, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'exceptions': { + 'insufficient_balance': InsufficientFunds, + 'invalid_order_size': InvalidOrder, + 'invalid_nonce': InvalidNonce, + 'unauthorized_scope': PermissionDenied, + 'invalid_address': InvalidAddress, + 'parameter_error': OrderNotFound, + }, + 'commonCurrencies': { + 'SMT': 'SocialMedia.Market', + 'MTN': 'Motion Token', + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetMarketCurrencies (params); + const currencies = response['result']['currencies']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'currency'); + const name = this.safeString (currency, 'name'); + const code = this.safeCurrencyCode (id); + const minUnit = this.safeFloat (currency, 'min_unit'); + result[code] = { + 'id': id, + 'code': code, + 'name': name, + 'active': true, + 'fiat': false, + 'precision': this.precisionFromString (currency['min_unit']), + 'limits': { + 'amount': { + 'min': minUnit, + 'max': undefined, + }, + 'price': { + 'min': minUnit, + 'max': undefined, + }, + 'deposit': { + 'min': minUnit, + 'max': undefined, + }, + 'withdraw': { + 'min': minUnit, + 'max': undefined, + }, + }, + 'funding': { + 'withdraw': { + 'fee': this.safeFloat (currency, 'withdrawal_fee'), + }, + 'deposit': { + 'fee': this.safeFloat (currency, 'deposit_fee'), + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketTradingPairs (params); + const markets = this.safeValue (response['result'], 'trading_pairs'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const [ baseId, quoteId ] = id.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': this.precisionFromString (market['quote_increment']), + }; + const active = this.safeValue (market, 'is_active', true); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'base_min_size'), + 'max': this.safeFloat (market, 'base_max_size'), + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'trading_pair_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last_trade_price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, '24h_high'), + 'low': this.safeFloat (ticker, '24h_low'), + 'bid': this.safeFloat (ticker, 'highest_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowest_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'percentChanged24hr'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, '24h_volume'), + 'quoteVolume': this.safeFloat (ticker, 'quote_volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair_id': market['id'], + }; + const response = await this.publicGetMarketTickersTradingPairId (this.extend (request, params)); + const ticker = this.safeValue (response['result'], 'ticker'); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketTickers (params); + const tickers = this.safeValue (response['result'], 'tickers'); + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.indexBy (result, 'symbol'); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'trading_pair_id': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit'] = limit; // 100 + } + const response = await this.publicGetMarketOrderbooksTradingPairId (this.extend (request, params)); + return this.parseOrderBook (response['result']['orderbook'], undefined, 'bids', 'asks', 0, 2); + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + // you can't determine your side from maker/taker side and vice versa + // you can't determine if your order/trade was a maker or a taker based + // on just the side of your order/trade + // https://github.com/ccxt/ccxt/issues/4300 + // let side = (trade['maker_side'] === 'bid') ? 'sell' : 'buy'; + const side = undefined; + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair_id': market['id'], + 'limit': limit, // default 20, but that seems too little + }; + const response = await this.publicGetMarketTradesTradingPairId (this.extend (request, params)); + const trades = this.safeValue (response['result'], 'trades'); + return this.parseTrades (trades, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + // they say that timestamps are Unix Timestamps in seconds, but in fact those are milliseconds + ohlcv['timestamp'], + parseFloat (ohlcv['open']), + parseFloat (ohlcv['high']), + parseFloat (ohlcv['low']), + parseFloat (ohlcv['close']), + parseFloat (ohlcv['volume']), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // + // they say in their docs that end_time defaults to current server time + // but if you don't specify it, their range limits does not allow you to query anything + // + // they also say that start_time defaults to 0, + // but most calls fail if you do not specify any of end_time + // + // to make things worse, their docs say it should be a Unix Timestamp + // but with seconds it fails, so we set milliseconds (somehow it works that way) + // + const endTime = this.milliseconds (); + const request = { + 'trading_pair_id': market['id'], + 'timeframe': this.timeframes[timeframe], + 'end_time': endTime, + }; + if (since !== undefined) { + request['start_time'] = since; + } + const response = await this.publicGetChartCandlesTradingPairId (this.extend (request, params)); + const ohlcv = this.safeValue (response['result'], 'candles'); + return this.parseOHLCVs (ohlcv, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletBalances (params); + const result = { 'info': response }; + const balances = this.safeValue (response['result'], 'balances'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'on_order'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'filled': 'closed', + 'rejected': 'closed', + 'partially_filled': 'open', + 'pending_cancellation': 'open', + 'pending_modification': 'open', + 'open': 'open', + 'new': 'open', + 'queued': 'open', + 'cancelled': 'canceled', + 'triggered': 'triggered', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // 'completed_at': None, + // 'eq_price': '0', + // 'filled': '0', + // 'id': '88426800-beae-4407-b4a1-f65cef693542', + // 'price': '0.00000507', + // 'side': 'bid', + // 'size': '3503.6489', + // 'source': 'exchange', + // 'state': 'open', + // 'timestamp': 1535258403597, + // 'trading_pair_id': 'ACT-BTC', + // 'type': 'limit', + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString2 (order, 'trading_pair', 'trading_pair_id'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'timestamp'); + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'eq_price'); + const amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat (order, 'filled'); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined && average !== undefined) { + cost = average * filled; + } else if (average !== undefined) { + cost = average * amount; + } + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let side = this.safeString (order, 'side'); + if (side === 'bid') { + side = 'buy'; + } else if (side === 'ask') { + side = 'sell'; + } + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': this.safeString (order, 'type'), // market, limit, stop, stop_limit, trailing_stop, fill_or_kill + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + side = (side === 'sell') ? 'ask' : 'bid'; + const request = { + 'trading_pair_id': market['id'], + 'type': type, // market, limit, stop, stop_limit + 'side': side, + 'size': this.amountToPrecision (symbol, amount), + }; + if (type !== 'market') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostTradingOrders (this.extend (request, params)); + const order = this.parseOrder (response['result']['order'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async editOrder (id, symbol, type, side, amount, price, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + 'price': this.priceToPrecision (symbol, price), + 'size': this.amountToPrecision (symbol, amount), + }; + const response = await this.privatePutTradingOrdersOrderId (this.extend (request, params)); + return this.parseOrder (this.extend (response, { + 'id': id, + })); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateDeleteTradingOrdersOrderId (this.extend (request, params)); + return this.parseOrder (this.extend (response, { + 'id': id, + })); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id.toString (), + }; + const response = await this.privateGetTradingOrdersOrderId (this.extend (request, params)); + return this.parseOrder (response['result']['order']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const result = await this.privateGetTradingOrders (params); + const orders = this.parseOrders (result['result']['orders'], undefined, since, limit); + if (symbol !== undefined) { + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + return this.filterBySinceLimit (orders, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['trading_pair_id'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 50, max 100 + } + const response = await this.privateGetTradingOrderHistory (this.extend (request, params)); + const orders = this.parseOrders (response['result']['orders'], market, since, limit); + if (symbol !== undefined) { + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + return this.filterBySinceLimit (orders, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateGetTradingOrdersOrderIdTrades (this.extend (request, params)); + const market = (symbol === undefined) ? undefined : this.market (symbol); + return this.parseTrades (response['result']['trades'], market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = {}; + if (symbol !== undefined) { + request['trading_pair_id'] = market['id']; + } + const response = await this.privateGetTradingTrades (this.extend (request, params)); + return this.parseTrades (response['result']['trades'], market, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + // 'ledger_type' is required, see: https://cobinhood.github.io/api-public/#create-new-deposit-address + const ledgerType = this.safeString (params, 'ledger_type', 'exchange'); + const request = { + 'currency': currency['id'], + 'ledger_type': ledgerType, + }; + const response = await this.privatePostWalletDepositAddresses (this.extend (request, params)); + const address = this.safeString (response['result']['deposit_address'], 'address'); + const tag = this.safeString (response['result']['deposit_address'], 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetWalletDepositAddresses (this.extend (request, params)); + // + // { success: true, + // result: { deposit_addresses: [ { address: "abcdefg", + // blockchain_id: "eosio", + // created_at: 1536768050235, + // currency: "EOS", + // memo: "12345678", + // type: "exchange" } ] } } + // + const addresses = this.safeValue (response['result'], 'deposit_addresses', []); + let address = undefined; + let tag = undefined; + if (addresses.length > 0) { + address = this.safeString (addresses[0], 'address'); + tag = this.safeString2 (addresses[0], 'memo', 'tag'); + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const response = await this.privatePostWalletWithdrawals (this.extend (request, params)); + return { + 'id': undefined, + 'info': response, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ' fetchDeposits() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetWalletDeposits (this.extend (request, params)); + return this.parseTransactions (response['result']['deposits'], currency); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (code === undefined) { + throw new ExchangeError (this.id + ' fetchWithdrawals() requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetWalletWithdrawals (this.extend (request, params)); + return this.parseTransactions (response['result']['withdrawals'], currency); + } + + parseTransactionStatus (status) { + const statuses = { + 'tx_pending_two_factor_auth': 'pending', + 'tx_pending_email_auth': 'pending', + 'tx_pending_approval': 'pending', + 'tx_approved': 'pending', + 'tx_processing': 'pending', + 'tx_pending': 'pending', + 'tx_sent': 'pending', + 'tx_cancelled': 'canceled', + 'tx_timeout': 'failed', + 'tx_invalid': 'failed', + 'tx_rejected': 'failed', + 'tx_confirmed': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + const timestamp = this.safeInteger (transaction, 'created_at'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let id = undefined; + const withdrawalId = this.safeString (transaction, 'withdrawal_id'); + const depositId = this.safeString (transaction, 'deposit_id'); + let type = undefined; + let address = undefined; + if (withdrawalId !== undefined) { + type = 'withdrawal'; + id = withdrawalId; + address = this.safeString (transaction, 'to_address'); + } else if (depositId !== undefined) { + type = 'deposit'; + id = depositId; + address = this.safeString (transaction, 'from_address'); + } + const additionalInfo = this.safeValue (transaction, 'additional_info', {}); + const tag = this.safeString (additionalInfo, 'memo'); + return { + 'info': transaction, + 'id': id, + 'txid': this.safeString (transaction, 'txhash'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, // refix it properly + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': this.parseTransactionStatus (transaction['status']), + 'updated': undefined, + 'fee': { + 'cost': this.safeFloat (transaction, 'fee'), + 'rate': undefined, + }, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + headers = {}; + if (api === 'private') { + this.checkRequiredCredentials (); + // headers['device_id'] = this.apiKey; + headers['nonce'] = this.nonce ().toString (); + headers['Authorization'] = this.apiKey; + } + if (method === 'GET') { + query = this.urlencode (query); + if (query.length) { + url += '?' + query; + } + } else { + headers['Content-type'] = 'application/json; charset=UTF-8'; + body = this.json (query); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code < 400 || code >= 600) { + return; + } + if (body[0] !== '{') { + throw new ExchangeError (this.id + ' ' + body); + } + const feedback = this.id + ' ' + this.json (response); + const errorCode = this.safeValue (response['error'], 'error_code'); + if (method === 'DELETE' || method === 'GET') { + if (errorCode === 'parameter_error') { + if (url.indexOf ('trading/orders/') >= 0) { + // Cobinhood returns vague "parameter_error" on fetchOrder() and cancelOrder() calls + // for invalid order IDs as well as orders that are not "open" + throw new InvalidOrder (feedback); + } + } + } + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + + nonce () { + return this.milliseconds (); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinbase.js b/TaskServer/node_modules/ccxt/js/coinbase.js new file mode 100644 index 000000000..86699a90b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinbase.js @@ -0,0 +1,1185 @@ +'use strict'; + +// ---------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, RateLimitExceeded } = require ('./base/errors'); + +// ---------------------------------------------------------------------------- + +module.exports = class coinbase extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinbase', + 'name': 'Coinbase', + 'countries': [ 'US' ], + 'rateLimit': 400, // 10k calls per hour + 'version': 'v2', + 'userAgent': this.userAgents['chrome'], + 'headers': { + 'CB-VERSION': '2018-05-30', + }, + 'has': { + 'CORS': true, + 'cancelOrder': false, + 'createDepositAddress': true, + 'createOrder': false, + 'deposit': false, + 'fetchBalance': true, + 'fetchClosedOrders': false, + 'fetchCurrencies': true, + 'fetchDepositAddress': false, + 'fetchMarkets': true, + 'fetchMyTrades': false, + 'fetchOHLCV': false, + 'fetchOpenOrders': false, + 'fetchOrder': false, + 'fetchOrderBook': false, + 'fetchL2OrderBook': false, + 'fetchLedger': true, + 'fetchOrders': false, + 'fetchTicker': true, + 'fetchTickers': false, + 'fetchTime': true, + 'fetchBidsAsks': false, + 'fetchTrades': false, + 'withdraw': false, + 'fetchTransactions': false, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchMySells': true, + 'fetchMyBuys': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/40811661-b6eceae2-653a-11e8-829e-10bfadb078cf.jpg', + 'api': 'https://api.coinbase.com', + 'www': 'https://www.coinbase.com', + 'doc': 'https://developers.coinbase.com/api/v2', + 'fees': 'https://support.coinbase.com/customer/portal/articles/2109597-buy-sell-bank-transfer-fees', + 'referral': 'https://www.coinbase.com/join/58cbe25a355148797479dbd2', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'time', + 'exchange-rates', + 'users/{user_id}', + 'prices/{symbol}/buy', + 'prices/{symbol}/sell', + 'prices/{symbol}/spot', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/{account_id}', + 'accounts/{account_id}/addresses', + 'accounts/{account_id}/addresses/{address_id}', + 'accounts/{account_id}/addresses/{address_id}/transactions', + 'accounts/{account_id}/transactions', + 'accounts/{account_id}/transactions/{transaction_id}', + 'accounts/{account_id}/buys', + 'accounts/{account_id}/buys/{buy_id}', + 'accounts/{account_id}/sells', + 'accounts/{account_id}/sells/{sell_id}', + 'accounts/{account_id}/deposits', + 'accounts/{account_id}/deposits/{deposit_id}', + 'accounts/{account_id}/withdrawals', + 'accounts/{account_id}/withdrawals/{withdrawal_id}', + 'payment-methods', + 'payment-methods/{payment_method_id}', + 'user', + 'user/auth', + ], + 'post': [ + 'accounts', + 'accounts/{account_id}/primary', + 'accounts/{account_id}/addresses', + 'accounts/{account_id}/transactions', + 'accounts/{account_id}/transactions/{transaction_id}/complete', + 'accounts/{account_id}/transactions/{transaction_id}/resend', + 'accounts/{account_id}/buys', + 'accounts/{account_id}/buys/{buy_id}/commit', + 'accounts/{account_id}/sells', + 'accounts/{account_id}/sells/{sell_id}/commit', + 'accounts/{account_id}/deposists', + 'accounts/{account_id}/deposists/{deposit_id}/commit', + 'accounts/{account_id}/withdrawals', + 'accounts/{account_id}/withdrawals/{withdrawal_id}/commit', + ], + 'put': [ + 'accounts/{account_id}', + 'user', + ], + 'delete': [ + 'accounts/{id}', + 'accounts/{account_id}/transactions/{transaction_id}', + ], + }, + }, + 'exceptions': { + 'two_factor_required': AuthenticationError, // 402 When sending money over 2fa limit + 'param_required': ExchangeError, // 400 Missing parameter + 'validation_error': ExchangeError, // 400 Unable to validate POST/PUT + 'invalid_request': ExchangeError, // 400 Invalid request + 'personal_details_required': AuthenticationError, // 400 User’s personal detail required to complete this request + 'identity_verification_required': AuthenticationError, // 400 Identity verification is required to complete this request + 'jumio_verification_required': AuthenticationError, // 400 Document verification is required to complete this request + 'jumio_face_match_verification_required': AuthenticationError, // 400 Document verification including face match is required to complete this request + 'unverified_email': AuthenticationError, // 400 User has not verified their email + 'authentication_error': AuthenticationError, // 401 Invalid auth (generic) + 'invalid_token': AuthenticationError, // 401 Invalid Oauth token + 'revoked_token': AuthenticationError, // 401 Revoked Oauth token + 'expired_token': AuthenticationError, // 401 Expired Oauth token + 'invalid_scope': AuthenticationError, // 403 User hasn’t authenticated necessary scope + 'not_found': ExchangeError, // 404 Resource not found + 'rate_limit_exceeded': RateLimitExceeded, // 429 Rate limit exceeded + 'internal_server_error': ExchangeError, // 500 Internal server error + }, + 'options': { + 'fetchCurrencies': { + 'expires': 5000, + }, + 'accounts': [ + 'wallet', + 'fiat', + // 'vault', + ], + }, + }); + } + + async fetchTime (params = {}) { + const response = await this.publicGetTime (params); + const data = this.safeValue (response, 'data', {}); + return this.parse8601 (this.safeString (data, 'iso')); + } + + async fetchAccounts (params = {}) { + const response = await this.privateGetAccounts (params); + // + // { + // "id": "XLM", + // "name": "XLM Wallet", + // "primary": false, + // "type": "wallet", + // "currency": { + // "code": "XLM", + // "name": "Stellar Lumens", + // "color": "#000000", + // "sort_index": 127, + // "exponent": 7, + // "type": "crypto", + // "address_regex": "^G[A-Z2-7]{55}$", + // "asset_id": "13b83335-5ede-595b-821e-5bcdfa80560f", + // "destination_tag_name": "XLM Memo ID", + // "destination_tag_regex": "^[ -~]{1,28}$" + // }, + // "balance": { + // "amount": "0.0000000", + // "currency": "XLM" + // }, + // "created_at": null, + // "updated_at": null, + // "resource": "account", + // "resource_path": "/v2/accounts/XLM", + // "allow_deposits": true, + // "allow_withdrawals": true + // } + // + const data = this.safeValue (response, 'data', []); + const result = []; + for (let i = 0; i < data.length; i++) { + const account = data[i]; + const currency = this.safeValue (account, 'currency', {}); + const currencyId = this.safeString (currency, 'code'); + const code = this.safeCurrencyCode (currencyId); + result.push ({ + 'id': this.safeString (account, 'id'), + 'type': this.safeString (account, 'type'), + 'code': code, + 'info': account, + }); + } + return result; + } + + async createDepositAddress (code, params = {}) { + let accountId = this.safeString (params, 'account_id'); + params = this.omit (params, 'account_id'); + if (accountId === undefined) { + await this.loadAccounts (); + for (let i = 0; i < this.accounts.length; i++) { + const account = this.accounts[i]; + if (account['code'] === code && account['type'] === 'wallet') { + accountId = account['id']; + break; + } + } + } + if (accountId === undefined) { + throw new ExchangeError (this.id + ' createDepositAddress could not find the account with matching currency code, specify an `account_id` extra param'); + } + const request = { + 'account_id': accountId, + }; + const response = await this.privatePostAccountsAccountIdAddresses (this.extend (request, params)); + // + // { + // "data": { + // "id": "05b1ebbf-9438-5dd4-b297-2ddedc98d0e4", + // "address": "coinbasebase", + // "address_info": { + // "address": "coinbasebase", + // "destination_tag": "287594668" + // }, + // "name": null, + // "created_at": "2019-07-01T14:39:29Z", + // "updated_at": "2019-07-01T14:39:29Z", + // "network": "eosio", + // "uri_scheme": "eosio", + // "resource": "address", + // "resource_path": "/v2/accounts/14cfc769-e852-52f3-b831-711c104d194c/addresses/05b1ebbf-9438-5dd4-b297-2ddedc98d0e4", + // "warnings": [ + // { + // "title": "Only send EOS (EOS) to this address", + // "details": "Sending any other cryptocurrency will result in permanent loss.", + // "image_url": "https://dynamic-assets.coinbase.com/deaca3d47b10ed4a91a872e9618706eec34081127762d88f2476ac8e99ada4b48525a9565cf2206d18c04053f278f693434af4d4629ca084a9d01b7a286a7e26/asset_icons/1f8489bb280fb0a0fd643c1161312ba49655040e9aaaced5f9ad3eeaf868eadc.png" + // }, + // { + // "title": "Both an address and EOS memo are required to receive EOS", + // "details": "If you send funds without an EOS memo or with an incorrect EOS memo, your funds cannot be credited to your account.", + // "image_url": "https://www.coinbase.com/assets/receive-warning-2f3269d83547a7748fb39d6e0c1c393aee26669bfea6b9f12718094a1abff155.png" + // } + // ], + // "warning_title": "Only send EOS (EOS) to this address", + // "warning_details": "Sending any other cryptocurrency will result in permanent loss.", + // "destination_tag": "287594668", + // "deposit_uri": "eosio:coinbasebase?dt=287594668", + // "callback_url": null + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const tag = this.safeString (data, 'destination_tag'); + const address = this.safeString (data, 'address'); + return { + 'currency': code, + 'tag': tag, + 'address': address, + 'info': response, + }; + } + + async fetchMySells (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // they don't have an endpoint for all historical trades + const request = await this.prepareAccountRequest (limit, params); + await this.loadMarkets (); + const query = this.omit (params, [ 'account_id', 'accountId' ]); + const sells = await this.privateGetAccountsAccountIdSells (this.extend (request, query)); + return this.parseTrades (sells['data'], undefined, since, limit); + } + + async fetchMyBuys (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // they don't have an endpoint for all historical trades + const request = await this.prepareAccountRequest (limit, params); + await this.loadMarkets (); + const query = this.omit (params, [ 'account_id', 'accountId' ]); + const buys = await this.privateGetAccountsAccountIdBuys (this.extend (request, query)); + return this.parseTrades (buys['data'], undefined, since, limit); + } + + async fetchTransactionsWithMethod (method, code = undefined, since = undefined, limit = undefined, params = {}) { + const request = await this.prepareAccountRequestWithCurrencyCode (code, limit, params); + await this.loadMarkets (); + const query = this.omit (params, [ 'account_id', 'accountId' ]); + const response = await this[method] (this.extend (request, query)); + return this.parseTransactions (response['data'], undefined, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + // fiat only, for crypto transactions use fetchLedger + return await this.fetchTransactionsWithMethod ('privateGetAccountsAccountIdWithdrawals', code, since, limit, params); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + // fiat only, for crypto transactions use fetchLedger + return await this.fetchTransactionsWithMethod ('privateGetAccountsAccountIdDeposits', code, since, limit, params); + } + + parseTransactionStatus (status) { + const statuses = { + 'created': 'pending', + 'completed': 'ok', + 'canceled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, market = undefined) { + // + // fiat deposit + // + // { + // "id": "f34c19f3-b730-5e3d-9f72", + // "status": "completed", + // "payment_method": { + // "id": "a022b31d-f9c7-5043-98f2", + // "resource": "payment_method", + // "resource_path": "/v2/payment-methods/a022b31d-f9c7-5043-98f2" + // }, + // "transaction": { + // "id": "04ed4113-3732-5b0c-af86-b1d2146977d0", + // "resource": "transaction", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/04ed4113-3732-5b0c-af86" + // }, + // "user_reference": "2VTYTH", + // "created_at": "2017-02-09T07:01:18Z", + // "updated_at": "2017-02-09T07:01:26Z", + // "resource": "deposit", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/deposits/f34c19f3-b730-5e3d-9f72", + // "committed": true, + // "payout_at": "2017-02-12T07:01:17Z", + // "instant": false, + // "fee": { "amount": "0.00", "currency": "EUR" }, + // "amount": { "amount": "114.02", "currency": "EUR" }, + // "subtotal": { "amount": "114.02", "currency": "EUR" }, + // "hold_until": null, + // "hold_days": 0, + // "hold_business_days": 0, + // "next_step": null + // } + // + // fiat_withdrawal + // + // { + // "id": "cfcc3b4a-eeb6-5e8c-8058", + // "status": "completed", + // "payment_method": { + // "id": "8b94cfa4-f7fd-5a12-a76a", + // "resource": "payment_method", + // "resource_path": "/v2/payment-methods/8b94cfa4-f7fd-5a12-a76a" + // }, + // "transaction": { + // "id": "fcc2550b-5104-5f83-a444", + // "resource": "transaction", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/fcc2550b-5104-5f83-a444" + // }, + // "user_reference": "MEUGK", + // "created_at": "2018-07-26T08:55:12Z", + // "updated_at": "2018-07-26T08:58:18Z", + // "resource": "withdrawal", + // "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/withdrawals/cfcc3b4a-eeb6-5e8c-8058", + // "committed": true, + // "payout_at": "2018-07-31T08:55:12Z", + // "instant": false, + // "fee": { "amount": "0.15", "currency": "EUR" }, + // "amount": { "amount": "13130.69", "currency": "EUR" }, + // "subtotal": { "amount": "13130.84", "currency": "EUR" }, + // "idem": "e549dee5-63ed-4e79-8a96", + // "next_step": null + // } + // + const subtotalObject = this.safeValue (transaction, 'subtotal', {}); + const feeObject = this.safeValue (transaction, 'fee', {}); + const id = this.safeString (transaction, 'id'); + const timestamp = this.parse8601 (this.safeValue (transaction, 'created_at')); + const updated = this.parse8601 (this.safeValue (transaction, 'updated_at')); + const type = this.safeString (transaction, 'resource'); + const amount = this.safeFloat (subtotalObject, 'amount'); + const currencyId = this.safeString (subtotalObject, 'currency'); + const currency = this.safeCurrencyCode (currencyId); + const feeCost = this.safeFloat (feeObject, 'amount'); + const feeCurrencyId = this.safeString (feeObject, 'currency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + let status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + if (status === undefined) { + const committed = this.safeValue (transaction, 'committed'); + status = committed ? 'ok' : 'pending'; + } + return { + 'info': transaction, + 'id': id, + 'txid': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': undefined, + 'tag': undefined, + 'type': type, + 'amount': amount, + 'currency': currency, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + parseTrade (trade, market = undefined) { + // + // { + // "id": "67e0eaec-07d7-54c4-a72c-2e92826897df", + // "status": "completed", + // "payment_method": { + // "id": "83562370-3e5c-51db-87da-752af5ab9559", + // "resource": "payment_method", + // "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" + // }, + // "transaction": { + // "id": "441b9494-b3f0-5b98-b9b0-4d82c21c252a", + // "resource": "transaction", + // "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" + // }, + // "amount": { "amount": "1.00000000", "currency": "BTC" }, + // "total": { "amount": "10.25", "currency": "USD" }, + // "subtotal": { "amount": "10.10", "currency": "USD" }, + // "created_at": "2015-01-31T20:49:02Z", + // "updated_at": "2015-02-11T16:54:02-08:00", + // "resource": "buy", + // "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/67e0eaec-07d7-54c4-a72c-2e92826897df", + // "committed": true, + // "instant": false, + // "fee": { "amount": "0.15", "currency": "USD" }, + // "payout_at": "2015-02-18T16:54:00-08:00" + // } + // + let symbol = undefined; + const totalObject = this.safeValue (trade, 'total', {}); + const amountObject = this.safeValue (trade, 'amount', {}); + const subtotalObject = this.safeValue (trade, 'subtotal', {}); + const feeObject = this.safeValue (trade, 'fee', {}); + const id = this.safeString (trade, 'id'); + const timestamp = this.parse8601 (this.safeValue (trade, 'created_at')); + if (market === undefined) { + const baseId = this.safeString (totalObject, 'currency'); + const quoteId = this.safeString (amountObject, 'currency'); + if ((baseId !== undefined) && (quoteId !== undefined)) { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const orderId = undefined; + const side = this.safeString (trade, 'resource'); + const type = undefined; + const cost = this.safeFloat (subtotalObject, 'amount'); + const amount = this.safeFloat (amountObject, 'amount'); + let price = undefined; + if (cost !== undefined) { + if (amount !== undefined) { + price = cost / amount; + } + } + const feeCost = this.safeFloat (feeObject, 'amount'); + const feeCurrencyId = this.safeString (feeObject, 'currency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + const currencies = this.safeValue (response, 'currencies', {}); + const exchangeRates = this.safeValue (response, 'exchangeRates', {}); + const data = this.safeValue (currencies, 'data', []); + const dataById = this.indexBy (data, 'id'); + const rates = this.safeValue (this.safeValue (exchangeRates, 'data', {}), 'rates', {}); + const baseIds = Object.keys (rates); + const result = []; + for (let i = 0; i < baseIds.length; i++) { + const baseId = baseIds[i]; + const base = this.safeCurrencyCode (baseId); + const type = (baseId in dataById) ? 'fiat' : 'crypto'; + // https://github.com/ccxt/ccxt/issues/6066 + if (type === 'crypto') { + for (let j = 0; j < data.length; j++) { + const quoteCurrency = data[j]; + const quoteId = this.safeString (quoteCurrency, 'id'); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = baseId + '-' + quoteId; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': quoteCurrency, + 'precision': { + 'amount': undefined, + 'price': undefined, + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (quoteCurrency, 'min_size'), + 'max': undefined, + }, + }, + }); + } + } + } + return result; + } + + async fetchCurrenciesFromCache (params = {}) { + const options = this.safeValue (this.options, 'fetchCurrencies', {}); + const timestamp = this.safeInteger (options, 'timestamp'); + const expires = this.safeInteger (options, 'expires', 1000); + const now = this.milliseconds (); + if ((timestamp === undefined) || ((now - timestamp) > expires)) { + const currencies = await this.publicGetCurrencies (params); + const exchangeRates = await this.publicGetExchangeRates (params); + this.options['fetchCurrencies'] = this.extend (options, { + 'currencies': currencies, + 'exchangeRates': exchangeRates, + 'timestamp': now, + }); + } + return this.safeValue (this.options, 'fetchCurrencies', {}); + } + + async fetchCurrencies (params = {}) { + const response = await this.fetchCurrenciesFromCache (params); + const currencies = this.safeValue (response, 'currencies', {}); + // + // { + // "data":[ + // {"id":"AED","name":"United Arab Emirates Dirham","min_size":"0.01000000"}, + // {"id":"AFN","name":"Afghan Afghani","min_size":"0.01000000"}, + // {"id":"ALL","name":"Albanian Lek","min_size":"0.01000000"}, + // {"id":"AMD","name":"Armenian Dram","min_size":"0.01000000"}, + // {"id":"ANG","name":"Netherlands Antillean Gulden","min_size":"0.01000000"}, + // // ... + // ], + // } + // + const exchangeRates = this.safeValue (response, 'exchangeRates', {}); + // + // { + // "data":{ + // "currency":"USD", + // "rates":{ + // "AED":"3.67", + // "AFN":"78.21", + // "ALL":"110.42", + // "AMD":"474.18", + // "ANG":"1.75", + // // ... + // }, + // } + // } + // + const data = this.safeValue (currencies, 'data', []); + const dataById = this.indexBy (data, 'id'); + const rates = this.safeValue (this.safeValue (exchangeRates, 'data', {}), 'rates', {}); + const keys = Object.keys (rates); + const result = {}; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const type = (key in dataById) ? 'fiat' : 'crypto'; + const currency = this.safeValue (dataById, key, {}); + const id = this.safeString (currency, 'id', key); + const name = this.safeString (currency, 'name'); + const code = this.safeCurrencyCode (id); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, // the original payload + 'type': type, + 'name': name, + 'active': true, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'min_size'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const timestamp = this.seconds (); + const market = this.market (symbol); + const request = this.extend ({ + 'symbol': market['id'], + }, params); + const buy = await this.publicGetPricesSymbolBuy (request); + const sell = await this.publicGetPricesSymbolSell (request); + const spot = await this.publicGetPricesSymbolSpot (request); + const ask = this.safeFloat (buy['data'], 'amount'); + const bid = this.safeFloat (sell['data'], 'amount'); + const last = this.safeFloat (spot['data'], 'amount'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'bid': bid, + 'ask': ask, + 'last': last, + 'high': undefined, + 'low': undefined, + 'bidVolume': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': undefined, + 'info': { + 'buy': buy, + 'sell': sell, + 'spot': spot, + }, + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + const balances = this.safeValue (response, 'data'); + const accounts = this.safeValue (params, 'type', this.options['accounts']); + const result = { 'info': response }; + for (let b = 0; b < balances.length; b++) { + const balance = balances[b]; + if (this.inArray (balance['type'], accounts)) { + const currencyId = this.safeString (balance['balance'], 'currency'); + const code = this.safeCurrencyCode (currencyId); + const total = this.safeFloat (balance['balance'], 'amount'); + const free = total; + const used = undefined; + if (code in result) { + result[code]['free'] = this.sum (result[code]['free'], total); + result[code]['total'] = this.sum (result[code]['total'], total); + } else { + const account = { + 'free': free, + 'used': used, + 'total': total, + }; + result[code] = account; + } + } + } + return this.parseBalance (result); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = await this.prepareAccountRequestWithCurrencyCode (code, limit, params); + const query = this.omit (params, ['account_id', 'accountId']); + // for pagination use parameter 'starting_after' + // the value for the next page can be obtained from the result of the previous call in the 'pagination' field + // eg: instance.last_json_response.pagination.next_starting_after + const response = await this.privateGetAccountsAccountIdTransactions (this.extend (request, query)); + return this.parseLedger (response['data'], undefined, since, limit); + } + + parseLedgerEntryStatus (status) { + const types = { + 'completed': 'ok', + }; + return this.safeString (types, status, status); + } + + parseLedgerEntryType (type) { + const types = { + 'buy': 'trade', + 'sell': 'trade', + 'fiat_deposit': 'transaction', + 'fiat_withdrawal': 'transaction', + 'exchange_deposit': 'transaction', // fiat withdrawal (from coinbase to coinbasepro) + 'exchange_withdrawal': 'transaction', // fiat deposit (to coinbase from coinbasepro) + 'send': 'transaction', // crypto deposit OR withdrawal + 'pro_deposit': 'transaction', // crypto withdrawal (from coinbase to coinbasepro) + 'pro_withdrawal': 'transaction', // crypto deposit (to coinbase from coinbasepro) + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // crypto deposit transaction + // + // { + // id: '34e4816b-4c8c-5323-a01c-35a9fa26e490', + // type: 'send', + // status: 'completed', + // amount: { amount: '28.31976528', currency: 'BCH' }, + // native_amount: { amount: '2799.65', currency: 'GBP' }, + // description: null, + // created_at: '2019-02-28T12:35:20Z', + // updated_at: '2019-02-28T12:43:24Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/34e4816b-4c8c-5323-a01c-35a9fa26e490', + // instant_exchange: false, + // network: { + // status: 'confirmed', + // hash: '56222d865dae83774fccb2efbd9829cf08c75c94ce135bfe4276f3fb46d49701', + // transaction_url: 'https://bch.btc.com/56222d865dae83774fccb2efbd9829cf08c75c94ce135bfe4276f3fb46d49701' + // }, + // from: { resource: 'bitcoin_cash_network', currency: 'BCH' }, + // details: { title: 'Received Bitcoin Cash', subtitle: 'From Bitcoin Cash address' } + // } + // + // crypto withdrawal transaction + // + // { + // id: '459aad99-2c41-5698-ac71-b6b81a05196c', + // type: 'send', + // status: 'completed', + // amount: { amount: '-0.36775642', currency: 'BTC' }, + // native_amount: { amount: '-1111.65', currency: 'GBP' }, + // description: null, + // created_at: '2019-03-20T08:37:07Z', + // updated_at: '2019-03-20T08:49:33Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/459aad99-2c41-5698-ac71-b6b81a05196c', + // instant_exchange: false, + // network: { + // status: 'confirmed', + // hash: '2732bbcf35c69217c47b36dce64933d103895277fe25738ffb9284092701e05b', + // transaction_url: 'https://blockchain.info/tx/2732bbcf35c69217c47b36dce64933d103895277fe25738ffb9284092701e05b', + // transaction_fee: { amount: '0.00000000', currency: 'BTC' }, + // transaction_amount: { amount: '0.36775642', currency: 'BTC' }, + // confirmations: 15682 + // }, + // to: { + // resource: 'bitcoin_address', + // address: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX', + // currency: 'BTC', + // address_info: { address: '1AHnhqbvbYx3rnZx8uC7NbFZaTe4tafFHX' } + // }, + // idem: 'da0a2f14-a2af-4c5a-a37e-d4484caf582bsend', + // application: { + // id: '5756ab6e-836b-553b-8950-5e389451225d', + // resource: 'application', + // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' + // }, + // details: { title: 'Sent Bitcoin', subtitle: 'To Bitcoin address' } + // } + // + // withdrawal transaction from coinbase to coinbasepro + // + // { + // id: '5b1b9fb8-5007-5393-b923-02903b973fdc', + // type: 'pro_deposit', + // status: 'completed', + // amount: { amount: '-0.00001111', currency: 'BCH' }, + // native_amount: { amount: '0.00', currency: 'GBP' }, + // description: null, + // created_at: '2019-02-28T13:31:58Z', + // updated_at: '2019-02-28T13:31:58Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/5b1b9fb8-5007-5393-b923-02903b973fdc', + // instant_exchange: false, + // application: { + // id: '5756ab6e-836b-553b-8950-5e389451225d', + // resource: 'application', + // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' + // }, + // details: { title: 'Transferred Bitcoin Cash', subtitle: 'To Coinbase Pro' } + // } + // + // withdrawal transaction from coinbase to gdax + // + // { + // id: 'badb7313-a9d3-5c07-abd0-00f8b44199b1', + // type: 'exchange_deposit', + // status: 'completed', + // amount: { amount: '-0.43704149', currency: 'BCH' }, + // native_amount: { amount: '-51.90', currency: 'GBP' }, + // description: null, + // created_at: '2019-03-19T10:30:40Z', + // updated_at: '2019-03-19T10:30:40Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c01d7364-edd7-5f3a-bd1d-de53d4cbb25e/transactions/badb7313-a9d3-5c07-abd0-00f8b44199b1', + // instant_exchange: false, + // details: { title: 'Transferred Bitcoin Cash', subtitle: 'To GDAX' } + // } + // + // deposit transaction from gdax to coinbase + // + // { + // id: '9c4b642c-8688-58bf-8962-13cef64097de', + // type: 'exchange_withdrawal', + // status: 'completed', + // amount: { amount: '0.57729420', currency: 'BTC' }, + // native_amount: { amount: '4418.72', currency: 'GBP' }, + // description: null, + // created_at: '2018-02-17T11:33:33Z', + // updated_at: '2018-02-17T11:33:33Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/9c4b642c-8688-58bf-8962-13cef64097de', + // instant_exchange: false, + // details: { title: 'Transferred Bitcoin', subtitle: 'From GDAX' } + // } + // + // deposit transaction from coinbasepro to coinbase + // + // { + // id: '8d6dd0b9-3416-568a-889d-8f112fae9e81', + // type: 'pro_withdrawal', + // status: 'completed', + // amount: { amount: '0.40555386', currency: 'BTC' }, + // native_amount: { amount: '1140.27', currency: 'GBP' }, + // description: null, + // created_at: '2019-03-04T19:41:58Z', + // updated_at: '2019-03-04T19:41:58Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/8d6dd0b9-3416-568a-889d-8f112fae9e81', + // instant_exchange: false, + // application: { + // id: '5756ab6e-836b-553b-8950-5e389451225d', + // resource: 'application', + // resource_path: '/v2/applications/5756ab6e-836b-553b-8950-5e389451225d' + // }, + // details: { title: 'Transferred Bitcoin', subtitle: 'From Coinbase Pro' } + // } + // + // sell trade + // + // { + // id: 'a9409207-df64-585b-97ab-a50780d2149e', + // type: 'sell', + // status: 'completed', + // amount: { amount: '-9.09922880', currency: 'BTC' }, + // native_amount: { amount: '-7285.73', currency: 'GBP' }, + // description: null, + // created_at: '2017-03-27T15:38:34Z', + // updated_at: '2017-03-27T15:38:34Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/transactions/a9409207-df64-585b-97ab-a50780d2149e', + // instant_exchange: false, + // sell: { + // id: 'e3550b4d-8ae6-5de3-95fe-1fb01ba83051', + // resource: 'sell', + // resource_path: '/v2/accounts/c6afbd34-4bd0-501e-8616-4862c193cd84/sells/e3550b4d-8ae6-5de3-95fe-1fb01ba83051' + // }, + // details: { + // title: 'Sold Bitcoin', + // subtitle: 'Using EUR Wallet', + // payment_method_name: 'EUR Wallet' + // } + // } + // + // buy trade + // + // { + // id: '63eeed67-9396-5912-86e9-73c4f10fe147', + // type: 'buy', + // status: 'completed', + // amount: { amount: '2.39605772', currency: 'ETH' }, + // native_amount: { amount: '98.31', currency: 'GBP' }, + // description: null, + // created_at: '2017-03-27T09:07:56Z', + // updated_at: '2017-03-27T09:07:57Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/8902f85d-4a69-5d74-82fe-8e390201bda7/transactions/63eeed67-9396-5912-86e9-73c4f10fe147', + // instant_exchange: false, + // buy: { + // id: '20b25b36-76c6-5353-aa57-b06a29a39d82', + // resource: 'buy', + // resource_path: '/v2/accounts/8902f85d-4a69-5d74-82fe-8e390201bda7/buys/20b25b36-76c6-5353-aa57-b06a29a39d82' + // }, + // details: { + // title: 'Bought Ethereum', + // subtitle: 'Using EUR Wallet', + // payment_method_name: 'EUR Wallet' + // } + // } + // + // fiat deposit transaction + // + // { + // id: '04ed4113-3732-5b0c-af86-b1d2146977d0', + // type: 'fiat_deposit', + // status: 'completed', + // amount: { amount: '114.02', currency: 'EUR' }, + // native_amount: { amount: '97.23', currency: 'GBP' }, + // description: null, + // created_at: '2017-02-09T07:01:21Z', + // updated_at: '2017-02-09T07:01:22Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/04ed4113-3732-5b0c-af86-b1d2146977d0', + // instant_exchange: false, + // fiat_deposit: { + // id: 'f34c19f3-b730-5e3d-9f72-96520448677a', + // resource: 'fiat_deposit', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/deposits/f34c19f3-b730-5e3d-9f72-96520448677a' + // }, + // details: { + // title: 'Deposited funds', + // subtitle: 'From SEPA Transfer (GB47 BARC 20..., reference CBADVI)', + // payment_method_name: 'SEPA Transfer (GB47 BARC 20..., reference CBADVI)' + // } + // } + // + // fiat withdrawal transaction + // + // { + // id: '957d98e2-f80e-5e2f-a28e-02945aa93079', + // type: 'fiat_withdrawal', + // status: 'completed', + // amount: { amount: '-11000.00', currency: 'EUR' }, + // native_amount: { amount: '-9698.22', currency: 'GBP' }, + // description: null, + // created_at: '2017-12-06T13:19:19Z', + // updated_at: '2017-12-06T13:19:19Z', + // resource: 'transaction', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/957d98e2-f80e-5e2f-a28e-02945aa93079', + // instant_exchange: false, + // fiat_withdrawal: { + // id: 'f4bf1fd9-ab3b-5de7-906d-ed3e23f7a4e7', + // resource: 'fiat_withdrawal', + // resource_path: '/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/withdrawals/f4bf1fd9-ab3b-5de7-906d-ed3e23f7a4e7' + // }, + // details: { + // title: 'Withdrew funds', + // subtitle: 'To HSBC BANK PLC (GB74 MIDL...)', + // payment_method_name: 'HSBC BANK PLC (GB74 MIDL...)' + // } + // } + // + const amountInfo = this.safeValue (item, 'amount', {}); + let amount = this.safeFloat (amountInfo, 'amount'); + let direction = undefined; + if (amount < 0) { + direction = 'out'; + amount = -amount; + } else { + direction = 'in'; + } + const currencyId = this.safeString (amountInfo, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + // + // the address and txid do not belong to the unified ledger structure + // + // let address = undefined; + // if (item['to']) { + // address = this.safeString (item['to'], 'address'); + // } + // let txid = undefined; + // + let fee = undefined; + const networkInfo = this.safeValue (item, 'network', {}); + // txid = network['hash']; // txid does not belong to the unified ledger structure + const feeInfo = this.safeValue (networkInfo, 'transaction_fee'); + if (feeInfo !== undefined) { + const feeCurrencyId = this.safeString (feeInfo, 'currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId, currency); + const feeAmount = this.safeFloat (feeInfo, 'amount'); + fee = { + 'cost': feeAmount, + 'currency': feeCurrencyCode, + }; + } + const timestamp = this.parse8601 (this.safeValue (item, 'created_at')); + const id = this.safeString (item, 'id'); + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const status = this.parseLedgerEntryStatus (this.safeString (item, 'status')); + const path = this.safeString (item, 'resource_path'); + let accountId = undefined; + if (path !== undefined) { + const parts = path.split ('/'); + const numParts = parts.length; + if (numParts > 3) { + accountId = parts[3]; + } + } + return { + 'info': item, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'direction': direction, + 'account': accountId, + 'referenceId': undefined, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': undefined, + 'after': undefined, + 'status': status, + 'fee': fee, + }; + } + + async findAccountId (code) { + await this.loadMarkets (); + await this.loadAccounts (); + for (let i = 0; i < this.accounts.length; i++) { + const account = this.accounts[i]; + if (account['code'] === code) { + return account['id']; + } + } + return undefined; + } + + prepareAccountRequest (limit = undefined, params = {}) { + const accountId = this.safeString2 (params, 'account_id', 'accountId'); + if (accountId === undefined) { + throw new ArgumentsRequired (this.id + ' method requires an account_id (or accountId) parameter'); + } + const request = { + 'account_id': accountId, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + return request; + } + + async prepareAccountRequestWithCurrencyCode (code = undefined, limit = undefined, params = {}) { + let accountId = this.safeString2 (params, 'account_id', 'accountId'); + if (accountId === undefined) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' method requires an account_id (or accountId) parameter OR a currency code argument'); + } + accountId = await this.findAccountId (code); + if (accountId === undefined) { + throw new ExchangeError (this.id + ' could not find account id for ' + code); + } + } + const request = { + 'account_id': accountId, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + return request; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let fullPath = '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + fullPath += '?' + this.urlencode (query); + } + } + const url = this.urls['api'] + fullPath; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let payload = ''; + if (method !== 'GET') { + if (Object.keys (query).length) { + body = this.json (query); + payload = body; + } + } + const auth = nonce + method + fullPath + payload; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + headers = { + 'CB-ACCESS-KEY': this.apiKey, + 'CB-ACCESS-SIGN': signature, + 'CB-ACCESS-TIMESTAMP': nonce, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const feedback = this.id + ' ' + body; + // + // {"error": "invalid_request", "error_description": "The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed."} + // + // or + // + // { + // "errors": [ + // { + // "id": "not_found", + // "message": "Not found" + // } + // ] + // } + // + let errorCode = this.safeString (response, 'error'); + if (errorCode !== undefined) { + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + const errors = this.safeValue (response, 'errors'); + if (errors !== undefined) { + if (Array.isArray (errors)) { + const numErrors = errors.length; + if (numErrors > 0) { + errorCode = this.safeString (errors[0], 'id'); + if (errorCode !== undefined) { + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + } + } + } + const data = this.safeValue (response, 'data'); + if (data === undefined) { + throw new ExchangeError (this.id + ' failed due to a malformed response ' + this.json (response)); + } + } +}; + diff --git a/TaskServer/node_modules/ccxt/js/coinbaseprime.js b/TaskServer/node_modules/ccxt/js/coinbaseprime.js new file mode 100644 index 000000000..3cebe5a2c --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinbaseprime.js @@ -0,0 +1,30 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const coinbasepro = require ('./coinbasepro.js'); + +// --------------------------------------------------------------------------- + +module.exports = class coinbaseprime extends coinbasepro { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinbaseprime', + 'name': 'Coinbase Prime', + 'urls': { + 'test': { + 'public': 'https://api-public.sandbox.prime.coinbase.com', + 'private': 'https://api-public.sandbox.prime.coinbase.com', + }, + 'logo': 'https://user-images.githubusercontent.com/1294454/44539184-29f26e00-a70c-11e8-868f-e907fc236a7c.jpg', + 'api': { + 'public': 'https://api.prime.coinbase.com', + 'private': 'https://api.prime.coinbase.com', + }, + 'www': 'https://prime.coinbase.com', + 'doc': 'https://docs.prime.coinbase.com', + 'fees': 'https://support.prime.coinbase.com/customer/en/portal/articles/2945629-fees?b_id=17475', + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinbasepro.js b/TaskServer/node_modules/ccxt/js/coinbasepro.js new file mode 100644 index 000000000..744c62e58 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinbasepro.js @@ -0,0 +1,957 @@ +'use strict'; + +// ---------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InsufficientFunds, ArgumentsRequired, ExchangeError, InvalidOrder, InvalidAddress, AuthenticationError, NotSupported, OrderNotFound, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); + +// ---------------------------------------------------------------------------- + +module.exports = class coinbasepro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinbasepro', + 'name': 'Coinbase Pro', + 'countries': [ 'US' ], + 'rateLimit': 1000, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'cancelAllOrders': true, + 'CORS': true, + 'deposit': true, + 'fetchAccounts': true, + 'fetchClosedOrders': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderTrades': true, + 'fetchOrders': true, + 'fetchTime': true, + 'fetchTransactions': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': 60, + '5m': 300, + '15m': 900, + '1h': 3600, + '6h': 21600, + '1d': 86400, + }, + 'urls': { + 'test': { + 'public': 'https://api-public.sandbox.pro.coinbase.com', + 'private': 'https://api-public.sandbox.pro.coinbase.com', + }, + 'logo': 'https://user-images.githubusercontent.com/1294454/41764625-63b7ffde-760a-11e8-996d-a6328fa9347a.jpg', + 'api': { + 'public': 'https://api.pro.coinbase.com', + 'private': 'https://api.pro.coinbase.com', + }, + 'www': 'https://pro.coinbase.com/', + 'doc': 'https://docs.pro.coinbase.com', + 'fees': [ + 'https://docs.pro.coinbase.com/#fees', + 'https://support.pro.coinbase.com/customer/en/portal/articles/2945310-fees', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'products', + 'products/{id}/book', + 'products/{id}/candles', + 'products/{id}/stats', + 'products/{id}/ticker', + 'products/{id}/trades', + 'time', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/{id}', + 'accounts/{id}/holds', + 'accounts/{id}/ledger', + 'accounts/{id}/transfers', + 'coinbase-accounts', + 'coinbase-accounts/{id}/addresses', + 'fills', + 'funding', + 'fees', + 'orders', + 'orders/{id}', + 'otc/orders', + 'payment-methods', + 'position', + 'reports/{id}', + 'users/self/trailing-volume', + ], + 'post': [ + 'conversions', + 'deposits/coinbase-account', + 'deposits/payment-method', + 'coinbase-accounts/{id}/addresses', + 'funding/repay', + 'orders', + 'position/close', + 'profiles/margin-transfer', + 'reports', + 'withdrawals/coinbase', + 'withdrawals/crypto', + 'withdrawals/payment-method', + ], + 'delete': [ + 'orders', + 'orders/{id}', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, // complicated tier system per coin + 'percentage': true, + 'maker': 0.5 / 100, // highest fee of all tiers + 'taker': 0.5 / 100, // highest fee of all tiers + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BCH': 0, + 'BTC': 0, + 'LTC': 0, + 'ETH': 0, + 'EUR': 0.15, + 'USD': 25, + }, + 'deposit': { + 'BCH': 0, + 'BTC': 0, + 'LTC': 0, + 'ETH': 0, + 'EUR': 0.15, + 'USD': 10, + }, + }, + }, + 'exceptions': { + 'exact': { + 'Insufficient funds': InsufficientFunds, + 'NotFound': OrderNotFound, + 'Invalid API Key': AuthenticationError, + 'invalid signature': AuthenticationError, + 'Invalid Passphrase': AuthenticationError, + 'Invalid order id': InvalidOrder, + 'Private rate limit exceeded': RateLimitExceeded, + }, + 'broad': { + 'Order already done': OrderNotFound, + 'order not found': OrderNotFound, + 'price too small': InvalidOrder, + 'price too precise': InvalidOrder, + 'under maintenance': OnMaintenance, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetProducts (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const priceLimits = { + 'min': this.safeFloat (market, 'quote_increment'), + 'max': undefined, + }; + const precision = { + 'amount': this.precisionFromString (this.safeString (market, 'base_increment')), + 'price': this.precisionFromString (this.safeString (market, 'quote_increment')), + }; + const active = market['status'] === 'online'; + result.push (this.extend (this.fees['trading'], { + 'id': id, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'base_min_size'), + 'max': this.safeFloat (market, 'base_max_size'), + }, + 'price': priceLimits, + 'cost': { + 'min': this.safeFloat (market, 'min_market_funds'), + 'max': this.safeFloat (market, 'max_market_funds'), + }, + }, + 'active': active, + 'info': market, + })); + } + return result; + } + + async fetchAccounts (params = {}) { + const response = await this.privateGetAccounts (params); + // + // [ + // { + // id: '4aac9c60-cbda-4396-9da4-4aa71e95fba0', + // currency: 'BTC', + // balance: '0.0000000000000000', + // available: '0', + // hold: '0.0000000000000000', + // profile_id: 'b709263e-f42a-4c7d-949a-a95c83d065da' + // }, + // { + // id: 'f75fa69a-1ad1-4a80-bd61-ee7faa6135a3', + // currency: 'USDC', + // balance: '0.0000000000000000', + // available: '0', + // hold: '0.0000000000000000', + // profile_id: 'b709263e-f42a-4c7d-949a-a95c83d065da' + // }, + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const account = response[i]; + const accountId = this.safeString (account, 'id'); + const currencyId = this.safeString (account, 'currency'); + const code = this.safeCurrencyCode (currencyId); + result.push ({ + 'id': accountId, + 'type': undefined, + 'currency': code, + 'info': account, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'available'), + 'used': this.safeFloat (balance, 'hold'), + 'total': this.safeFloat (balance, 'balance'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + // level 1 - only the best bid and ask + // level 2 - top 50 bids and asks (aggregated) + // level 3 - full order book (non aggregated) + const request = { + 'id': this.marketId (symbol), + 'level': 2, // 1 best bidask, 2 aggregated, 3 full + }; + const response = await this.publicGetProductsIdBook (this.extend (request, params)); + // + // { + // "sequence":1924393896, + // "bids":[ + // ["0.01825","24.34811287",2], + // ["0.01824","72.5463",3], + // ["0.01823","424.54298049",6], + // ], + // "asks":[ + // ["0.01826","171.10414904",4], + // ["0.01827","22.60427028",1], + // ["0.01828","397.46018784",7], + // ] + // } + // + const orderbook = this.parseOrderBook (response); + orderbook['nonce'] = this.safeInteger (response, 'sequence'); + return orderbook; + } + + parseTicker (ticker, market = undefined) { + // + // publicGetProductsIdTicker + // + // { + // "trade_id":843439, + // "price":"0.997999", + // "size":"80.29769", + // "time":"2020-01-28T02:13:33.012523Z", + // "bid":"0.997094", + // "ask":"0.998", + // "volume":"1903188.03750000" + // } + // + // publicGetProductsIdStats + // + // { + // "open": "34.19000000", + // "high": "95.70000000", + // "low": "7.06000000", + // "volume": "2.41000000" + // } + // + const timestamp = this.parse8601 (this.safeValue (ticker, 'time')); + const bid = this.safeFloat (ticker, 'bid'); + const ask = this.safeFloat (ticker, 'ask'); + const last = this.safeFloat (ticker, 'price'); + const symbol = (market === undefined) ? undefined : market['symbol']; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + // publicGetProductsIdTicker or publicGetProductsIdStats + const method = this.safeString (this.options, 'fetchTickerMethod', 'publicGetProductsIdTicker'); + const response = await this[method] (this.extend (request, params)); + // + // publicGetProductsIdTicker + // + // { + // "trade_id":843439, + // "price":"0.997999", + // "size":"80.29769", + // "time":"2020-01-28T02:13:33.012523Z", + // "bid":"0.997094", + // "ask":"0.998", + // "volume":"1903188.03750000" + // } + // + // publicGetProductsIdStats + // + // { + // "open": "34.19000000", + // "high": "95.70000000", + // "low": "7.06000000", + // "volume": "2.41000000" + // } + // + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + // + // { + // type: 'match', + // trade_id: 82047307, + // maker_order_id: '0f358725-2134-435e-be11-753912a326e0', + // taker_order_id: '252b7002-87a3-425c-ac73-f5b9e23f3caf', + // side: 'sell', + // size: '0.00513192', + // price: '9314.78', + // product_id: 'BTC-USD', + // sequence: 12038915443, + // time: '2020-01-31T20:03:41.158814Z' + // } + // + const timestamp = this.parse8601 (this.safeString2 (trade, 'time', 'created_at')); + let symbol = undefined; + const marketId = this.safeString (trade, 'product_id'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + let feeRate = undefined; + let feeCurrency = undefined; + let takerOrMaker = undefined; + if (market !== undefined) { + feeCurrency = market['quote']; + if ('liquidity' in trade) { + takerOrMaker = (trade['liquidity'] === 'T') ? 'taker' : 'maker'; + feeRate = market[takerOrMaker]; + } + } + const feeCost = this.safeFloat2 (trade, 'fill_fees', 'fee'); + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }; + const type = undefined; + const id = this.safeString (trade, 'trade_id'); + let side = (trade['side'] === 'buy') ? 'sell' : 'buy'; + const orderId = this.safeString (trade, 'order_id'); + // Coinbase Pro returns inverted side to fetchMyTrades vs fetchTrades + if (orderId !== undefined) { + side = (trade['side'] === 'buy') ? 'buy' : 'sell'; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + return { + 'id': id, + 'order': orderId, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'fee': fee, + 'cost': price * amount, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // as of 2018-08-23 + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_id': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetFills (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], // fixes issue #2 + }; + const response = await this.publicGetProductsIdTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[3], + ohlcv[2], + ohlcv[1], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const granularity = this.timeframes[timeframe]; + const request = { + 'id': market['id'], + 'granularity': granularity, + }; + if (since !== undefined) { + request['start'] = this.iso8601 (since); + if (limit === undefined) { + // https://docs.pro.coinbase.com/#get-historic-rates + limit = 300; // max = 300 + } + request['end'] = this.iso8601 (this.sum ((limit - 1) * granularity * 1000, since)); + } + const response = await this.publicGetProductsIdCandles (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchTime (params = {}) { + const response = await this.publicGetTime (params); + return this.parse8601 (this.safeString (response, 'iso')); + } + + parseOrderStatus (status) { + const statuses = { + 'pending': 'open', + 'active': 'open', + 'open': 'open', + 'done': 'closed', + 'canceled': 'canceled', + 'canceling': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + let symbol = undefined; + const marketId = this.safeString (order, 'product_id'); + let quote = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'filled_size'); + const amount = this.safeFloat (order, 'size', filled); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const cost = this.safeFloat (order, 'executed_value'); + const feeCost = this.safeFloat (order, 'fill_fees'); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrencyCode = undefined; + if (market !== undefined) { + feeCurrencyCode = market['quote']; + } else if (quote !== undefined) { + feeCurrencyCode = quote; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + 'rate': undefined, + }; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'order_id': id, + }; + const response = await this.privateGetFills (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'all', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'done', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + // let oid = this.nonce ().toString (); + const request = { + 'product_id': this.marketId (symbol), + 'side': side, + 'size': this.amountToPrecision (symbol, amount), + 'type': type, + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.privateDeleteOrdersId ({ 'id': id }); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + return await this.privateDeleteOrders (params); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + const cost = amount * price; + const currency = market['quote']; + return { + 'type': takerOrMaker, + 'currency': currency, + 'rate': rate, + 'cost': parseFloat (this.currencyToPrecision (currency, rate * cost)), + }; + } + + async fetchPaymentMethods (params = {}) { + return await this.privateGetPaymentMethods (params); + } + + async deposit (code, amount, address, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + }; + let method = 'privatePostDeposits'; + if ('payment_method_id' in params) { + // deposit from a payment_method, like a bank account + method += 'PaymentMethod'; + } else if ('coinbase_account_id' in params) { + // deposit into Coinbase Pro account from a Coinbase account + method += 'CoinbaseAccount'; + } else { + // deposit methodotherwise we did not receive a supported deposit location + // relevant docs link for the Googlers + // https://docs.pro.coinbase.com/#deposits + throw new NotSupported (this.id + ' deposit() requires one of `coinbase_account_id` or `payment_method_id` extra params'); + } + const response = await this[method] (this.extend (request, params)); + if (!response) { + throw new ExchangeError (this.id + ' deposit() error: ' + this.json (response)); + } + return { + 'info': response, + 'id': response['id'], + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + }; + let method = 'privatePostWithdrawals'; + if ('payment_method_id' in params) { + method += 'PaymentMethod'; + } else if ('coinbase_account_id' in params) { + method += 'CoinbaseAccount'; + } else { + method += 'Crypto'; + request['crypto_address'] = address; + } + const response = await this[method] (this.extend (request, params)); + if (!response) { + throw new ExchangeError (this.id + ' withdraw() error: ' + this.json (response)); + } + return { + 'info': response, + 'id': response['id'], + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + let currency = undefined; + let id = this.safeString (params, 'id'); // account id + if (id === undefined) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a currency code argument if no account id specified in params'); + } + currency = this.currency (code); + const accountsByCurrencyCode = this.indexBy (this.accounts, 'currency'); + const account = this.safeValue (accountsByCurrencyCode, code); + if (account === undefined) { + throw new ExchangeError (this.id + ' fetchTransactions() could not find account id for ' + code); + } + id = account['id']; + } + const request = { + 'id': id, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetAccountsIdTransfers (this.extend (request, params)); + for (let i = 0; i < response.length; i++) { + response[i]['currency'] = code; + } + return this.parseTransactions (response, currency, since, limit); + } + + parseTransactionStatus (transaction) { + const canceled = this.safeValue (transaction, 'canceled_at'); + if (canceled) { + return 'canceled'; + } + const processed = this.safeValue (transaction, 'processed_at'); + const completed = this.safeValue (transaction, 'completed_at'); + if (completed) { + return 'ok'; + } else if (processed && !completed) { + return 'failed'; + } else { + return 'pending'; + } + } + + parseTransaction (transaction, currency = undefined) { + const details = this.safeValue (transaction, 'details', {}); + const id = this.safeString (transaction, 'id'); + const txid = this.safeString (details, 'crypto_transaction_hash'); + const timestamp = this.parse8601 (this.safeString (transaction, 'created_at')); + const updated = this.parse8601 (this.safeString (transaction, 'processed_at')); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const fee = undefined; + const status = this.parseTransactionStatus (transaction); + const amount = this.safeFloat (transaction, 'amount'); + let type = this.safeString (transaction, 'type'); + let address = this.safeString (details, 'crypto_address'); + const tag = this.safeString (details, 'destination_tag'); + address = this.safeString (transaction, 'crypto_address', address); + if (type === 'withdraw') { + type = 'withdrawal'; + address = this.safeString (details, 'sent_to_address', address); + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } + const url = this.urls['api'][api] + request; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let payload = ''; + if (method !== 'GET') { + if (Object.keys (query).length) { + body = this.json (query); + payload = body; + } + } + const what = nonce + method + request + payload; + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (this.encode (what), secret, 'sha256', 'base64'); + headers = { + 'CB-ACCESS-KEY': this.apiKey, + 'CB-ACCESS-SIGN': this.decode (signature), + 'CB-ACCESS-TIMESTAMP': nonce, + 'CB-ACCESS-PASSPHRASE': this.password, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + let accounts = this.safeValue (this.options, 'coinbaseAccounts'); + if (accounts === undefined) { + accounts = await this.privateGetCoinbaseAccounts (); + this.options['coinbaseAccounts'] = accounts; // cache it + this.options['coinbaseAccountsByCurrencyId'] = this.indexBy (accounts, 'currency'); + } + const currencyId = currency['id']; + const account = this.safeValue (this.options['coinbaseAccountsByCurrencyId'], currencyId); + if (account === undefined) { + // eslint-disable-next-line quotes + throw new InvalidAddress (this.id + " fetchDepositAddress() could not find currency code " + code + " with id = " + currencyId + " in this.options['coinbaseAccountsByCurrencyId']"); + } + const request = { + 'id': account['id'], + }; + const response = await this.privateGetCoinbaseAccountsIdAddresses (this.extend (request, params)); + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'destination_tag'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + let accounts = this.safeValue (this.options, 'coinbaseAccounts'); + if (accounts === undefined) { + accounts = await this.privateGetCoinbaseAccounts (); + this.options['coinbaseAccounts'] = accounts; // cache it + this.options['coinbaseAccountsByCurrencyId'] = this.indexBy (accounts, 'currency'); + } + const currencyId = currency['id']; + const account = this.safeValue (this.options['coinbaseAccountsByCurrencyId'], currencyId); + if (account === undefined) { + // eslint-disable-next-line quotes + throw new InvalidAddress (this.id + " fetchDepositAddress() could not find currency code " + code + " with id = " + currencyId + " in this.options['coinbaseAccountsByCurrencyId']"); + } + const request = { + 'id': account['id'], + }; + const response = await this.privatePostCoinbaseAccountsIdAddresses (this.extend (request, params)); + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'destination_tag'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 400) || (code === 404)) { + if (body[0] === '{') { + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + throw new ExchangeError (this.id + ' ' + body); + } + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response !== 'string') { + if ('message' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coincheck.js b/TaskServer/node_modules/ccxt/js/coincheck.js new file mode 100644 index 000000000..5b9bfdc8f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coincheck.js @@ -0,0 +1,430 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coincheck extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coincheck', + 'name': 'coincheck', + 'countries': [ 'JP', 'ID' ], + 'rateLimit': 1500, + 'has': { + 'CORS': false, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766464-3b5c3c74-5ed9-11e7-840e-31b32968e1da.jpg', + 'api': 'https://coincheck.com/api', + 'www': 'https://coincheck.com', + 'doc': 'https://coincheck.com/documents/exchange/api', + 'fees': [ + 'https://coincheck.com/exchange/fee', + 'https://coincheck.com/info/fee', + ], + }, + 'api': { + 'public': { + 'get': [ + 'exchange/orders/rate', + 'order_books', + 'rate/{pair}', + 'ticker', + 'trades', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/balance', + 'accounts/leverage_balance', + 'bank_accounts', + 'deposit_money', + 'exchange/orders/opens', + 'exchange/orders/transactions', + 'exchange/orders/transactions_pagination', + 'exchange/leverage/positions', + 'lending/borrows/matches', + 'send_money', + 'withdraws', + ], + 'post': [ + 'bank_accounts', + 'deposit_money/{id}/fast', + 'exchange/orders', + 'exchange/transfers/to_leverage', + 'exchange/transfers/from_leverage', + 'lending/borrows', + 'lending/borrows/{id}/repay', + 'send_money', + 'withdraws', + ], + 'delete': [ + 'bank_accounts/{id}', + 'exchange/orders/{id}', + 'withdraws/{id}', + ], + }, + }, + 'markets': { + 'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' }, // the only real pair + // 'ETH/JPY': { 'id': 'eth_jpy', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy' }, + // 'ETC/JPY': { 'id': 'etc_jpy', 'symbol': 'ETC/JPY', 'base': 'ETC', 'quote': 'JPY', 'baseId': 'etc', 'quoteId': 'jpy' }, + // 'DAO/JPY': { 'id': 'dao_jpy', 'symbol': 'DAO/JPY', 'base': 'DAO', 'quote': 'JPY', 'baseId': 'dao', 'quoteId': 'jpy' }, + // 'LSK/JPY': { 'id': 'lsk_jpy', 'symbol': 'LSK/JPY', 'base': 'LSK', 'quote': 'JPY', 'baseId': 'lsk', 'quoteId': 'jpy' }, + // 'FCT/JPY': { 'id': 'fct_jpy', 'symbol': 'FCT/JPY', 'base': 'FCT', 'quote': 'JPY', 'baseId': 'fct', 'quoteId': 'jpy' }, + // 'XMR/JPY': { 'id': 'xmr_jpy', 'symbol': 'XMR/JPY', 'base': 'XMR', 'quote': 'JPY', 'baseId': 'xmr', 'quoteId': 'jpy' }, + // 'REP/JPY': { 'id': 'rep_jpy', 'symbol': 'REP/JPY', 'base': 'REP', 'quote': 'JPY', 'baseId': 'rep', 'quoteId': 'jpy' }, + // 'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' }, + // 'ZEC/JPY': { 'id': 'zec_jpy', 'symbol': 'ZEC/JPY', 'base': 'ZEC', 'quote': 'JPY', 'baseId': 'zec', 'quoteId': 'jpy' }, + // 'XEM/JPY': { 'id': 'xem_jpy', 'symbol': 'XEM/JPY', 'base': 'XEM', 'quote': 'JPY', 'baseId': 'xem', 'quoteId': 'jpy' }, + // 'LTC/JPY': { 'id': 'ltc_jpy', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy' }, + // 'DASH/JPY': { 'id': 'dash_jpy', 'symbol': 'DASH/JPY', 'base': 'DASH', 'quote': 'JPY', 'baseId': 'dash', 'quoteId': 'jpy' }, + // 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' }, + // 'ETC/BTC': { 'id': 'etc_btc', 'symbol': 'ETC/BTC', 'base': 'ETC', 'quote': 'BTC', 'baseId': 'etc', 'quoteId': 'btc' }, + // 'LSK/BTC': { 'id': 'lsk_btc', 'symbol': 'LSK/BTC', 'base': 'LSK', 'quote': 'BTC', 'baseId': 'lsk', 'quoteId': 'btc' }, + // 'FCT/BTC': { 'id': 'fct_btc', 'symbol': 'FCT/BTC', 'base': 'FCT', 'quote': 'BTC', 'baseId': 'fct', 'quoteId': 'btc' }, + // 'XMR/BTC': { 'id': 'xmr_btc', 'symbol': 'XMR/BTC', 'base': 'XMR', 'quote': 'BTC', 'baseId': 'xmr', 'quoteId': 'btc' }, + // 'REP/BTC': { 'id': 'rep_btc', 'symbol': 'REP/BTC', 'base': 'REP', 'quote': 'BTC', 'baseId': 'rep', 'quoteId': 'btc' }, + // 'XRP/BTC': { 'id': 'xrp_btc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc' }, + // 'ZEC/BTC': { 'id': 'zec_btc', 'symbol': 'ZEC/BTC', 'base': 'ZEC', 'quote': 'BTC', 'baseId': 'zec', 'quoteId': 'btc' }, + // 'XEM/BTC': { 'id': 'xem_btc', 'symbol': 'XEM/BTC', 'base': 'XEM', 'quote': 'BTC', 'baseId': 'xem', 'quoteId': 'btc' }, + // 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' }, + // 'DASH/BTC': { 'id': 'dash_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'dash', 'quoteId': 'btc' }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0, + 'taker': 0, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetAccountsBalance (params); + const result = { 'info': balances }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currencyId = this.currencyId (code); + if (currencyId in balances) { + const account = this.account (); + const reserved = currencyId + '_reserved'; + account['free'] = this.safeFloat (balances, currencyId); + account['used'] = this.safeFloat (balances, reserved); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // Only BTC/JPY is meaningful + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privateGetExchangeOrdersOpens (params); + const rawOrders = this.safeValue (response, 'orders', []); + const parsedOrders = this.parseOrders (rawOrders, market, since, limit); + const result = []; + for (let i = 0; i < parsedOrders.length; i++) { + result.push (this.extend (parsedOrders[i], { 'status': 'open' })); + } + return result; + } + + parseOrder (order, market = undefined) { + // + // fetchOpenOrders + // + // { id: 202835, + // order_type: "buy", + // rate: 26890, + // pair: "btc_jpy", + // pending_amount: "0.5527", + // pending_market_buy_amount: null, + // stop_loss_rate: null, + // created_at: "2015-01-10T05:55:38.000Z" } + // + // todo: add formats for fetchOrder, fetchClosedOrders here + // + const id = this.safeString (order, 'id'); + const side = this.safeString (order, 'order_type'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const amount = this.safeFloat (order, 'pending_amount'); + const remaining = this.safeFloat (order, 'pending_amount'); + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if (remaining !== undefined) { + if (amount !== undefined) { + filled = Math.max (amount - remaining, 0); + if (price !== undefined) { + cost = filled * price; + } + } + } + const status = undefined; + const marketId = this.safeString (order, 'pair'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'side': side, + 'type': undefined, + 'status': status, + 'symbol': symbol, + 'price': price, + 'cost': cost, + 'fee': undefined, + 'info': order, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + if (symbol !== 'BTC/JPY') { + throw new BadSymbol (this.id + ' fetchOrderBook () supports BTC/JPY only'); + } + await this.loadMarkets (); + const response = await this.publicGetOrderBooks (params); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + if (symbol !== 'BTC/JPY') { + throw new BadSymbol (this.id + ' fetchTicker () supports BTC/JPY only'); + } + await this.loadMarkets (); + const ticker = await this.publicGetTicker (params); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'rate'); + const marketId = this.safeString (trade, 'pair'); + market = this.safeValue (this.markets_by_id, marketId, market); + let symbol = undefined; + let baseId = undefined; + let quoteId = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + baseId = market['baseId']; + quoteId = market['quoteId']; + symbol = market['symbol']; + } else { + const ids = marketId.split ('_'); + baseId = ids[0]; + quoteId = ids[1]; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let takerOrMaker = undefined; + let amount = undefined; + let cost = undefined; + let side = undefined; + let fee = undefined; + let orderId = undefined; + if ('liquidity' in trade) { + if (this.safeString (trade, 'liquidity') === 'T') { + takerOrMaker = 'taker'; + } else if (this.safeString (trade, 'liquidity') === 'M') { + takerOrMaker = 'maker'; + } + const funds = this.safeValue (trade, 'funds', {}); + amount = this.safeFloat (funds, baseId); + cost = this.safeFloat (funds, quoteId); + fee = { + 'currency': this.safeString (trade, 'fee_currency'), + 'cost': this.safeFloat (trade, 'fee'), + }; + side = this.safeString (trade, 'side'); + orderId = this.safeString (trade, 'order_id'); + } else { + amount = this.safeFloat (trade, 'amount'); + side = this.safeString (trade, 'order_type'); + } + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + } + return { + 'id': id, + 'info': trade, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': orderId, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.privateGetExchangeOrdersTransactions (this.extend ({}, params)); + const transactions = this.safeValue (response, 'transactions', []); + return this.parseTrades (transactions, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + if (symbol !== 'BTC/JPY') { + throw new BadSymbol (this.id + ' fetchTrades () supports BTC/JPY only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTrades (this.extend (request, params)); + const data = this.safeValue (response, 'data', []); + return this.parseTrades (data, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + if (type === 'market') { + const order_type = type + '_' + side; + request['order_type'] = order_type; + const prefix = (side === 'buy') ? (order_type + '_') : ''; + request[prefix + 'amount'] = amount; + } else { + request['order_type'] = side; + request['rate'] = price; + request['amount'] = amount; + } + const response = await this.privatePostExchangeOrders (this.extend (request, params)); + const id = this.safeString (response, 'id'); + return { + 'info': response, + 'id': id, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'id': id, + }; + return await this.privateDeleteExchangeOrdersId (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let queryString = ''; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (this.keysort (query)); + } + } else { + if (Object.keys (query).length) { + body = this.urlencode (this.keysort (query)); + queryString = body; + } + } + const auth = nonce + url + queryString; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'ACCESS-KEY': this.apiKey, + 'ACCESS-NONCE': nonce, + 'ACCESS-SIGNATURE': this.hmac (this.encode (auth), this.encode (this.secret)), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (api === 'public') { + return response; + } + if ('success' in response) { + if (response['success']) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinegg.js b/TaskServer/node_modules/ccxt/js/coinegg.js new file mode 100644 index 000000000..fd7a16cc9 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinegg.js @@ -0,0 +1,503 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, InvalidNonce, InsufficientFunds, InvalidOrder, OrderNotFound, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinegg extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinegg', + 'name': 'CoinEgg', + 'countries': [ 'CN', 'UK' ], + 'has': { + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': 'emulated', + 'fetchMyTrades': false, + 'fetchTickers': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/36770310-adfa764e-1c5a-11e8-8e09-449daac3d2fb.jpg', + 'api': { + 'web': 'https://trade.coinegg.com/web', + 'rest': 'https://api.coinegg.com/api/v1', + }, + 'www': 'https://www.coinegg.com', + 'doc': 'https://www.coinegg.com/explain.api.html', + 'fees': 'https://www.coinegg.com/fee.html', + 'referral': 'https://www.coinegg.com/user/register?invite=523218', + }, + 'api': { + 'web': { + 'get': [ + 'symbol/ticker?right_coin={quote}', + '{quote}/trends', + '{quote}/{base}/order', + '{quote}/{base}/trades', + '{quote}/{base}/depth.js', + ], + }, + 'public': { + 'get': [ + 'ticker/region/{quote}', + 'depth/region/{quote}', + 'orders/region/{quote}', + ], + }, + 'private': { + 'post': [ + 'balance', + 'trade_add/region/{quote}', + 'trade_cancel/region/{quote}', + 'trade_view/region/{quote}', + 'trade_list/region/{quote}', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.008, + 'BCH': 0.002, + 'LTC': 0.001, + 'ETH': 0.01, + 'ETC': 0.01, + 'NEO': 0, + 'QTUM': '1%', + 'XRP': '1%', + 'DOGE': '1%', + 'LSK': '1%', + 'XAS': '1%', + 'BTS': '1%', + 'GAME': '1%', + 'GOOC': '1%', + 'NXT': '1%', + 'IFC': '1%', + 'DNC': '1%', + 'BLK': '1%', + 'VRC': '1%', + 'XPM': '1%', + 'VTC': '1%', + 'TFC': '1%', + 'PLC': '1%', + 'EAC': '1%', + 'PPC': '1%', + 'FZ': '1%', + 'ZET': '1%', + 'RSS': '1%', + 'PGC': '1%', + 'SKT': '1%', + 'JBC': '1%', + 'RIO': '1%', + 'LKC': '1%', + 'ZCC': '1%', + 'MCC': '1%', + 'QEC': '1%', + 'MET': '1%', + 'YTC': '1%', + 'HLB': '1%', + 'MRYC': '1%', + 'MTC': '1%', + 'KTC': 0, + }, + }, + }, + 'exceptions': { + '103': AuthenticationError, + '104': AuthenticationError, + '105': AuthenticationError, + '106': InvalidNonce, + '200': InsufficientFunds, + '201': InvalidOrder, + '202': InvalidOrder, + '203': OrderNotFound, + '402': DDoSProtection, + }, + 'errorMessages': { + '100': 'Required parameters can not be empty', + '101': 'Illegal parameter', + '102': 'coin does not exist', + '103': 'Key does not exist', + '104': 'Signature does not match', + '105': 'Insufficient permissions', + '106': 'Request expired(nonce error)', + '200': 'Lack of balance', + '201': 'Too small for the number of trading', + '202': 'Price must be in 0 - 1000000', + '203': 'Order does not exist', + '204': 'Pending order amount must be above 0.001 BTC', + '205': 'Restrict pending order prices', + '206': 'Decimal place error', + '401': 'System error', + '402': 'Requests are too frequent', + '403': 'Non-open API', + '404': 'IP restriction does not request the resource', + '405': 'Currency transactions are temporarily closed', + }, + 'options': { + 'quoteIds': [ 'btc', 'eth', 'usc', 'usdt' ], + }, + 'commonCurrencies': { + 'JBC': 'JubaoCoin', + }, + }); + } + + async fetchMarkets (params = {}) { + const quoteIds = this.options['quoteIds']; + const result = []; + for (let b = 0; b < quoteIds.length; b++) { + const quoteId = quoteIds[b]; + const response = await this.webGetSymbolTickerRightCoinQuote ({ + 'quote': quoteId, + }); + const tickers = this.safeValue (response, 'data', []); + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const id = ticker['symbol']; + const baseId = id.split ('_')[0]; + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': ticker, + }); + } + } + return result; + } + + parseTicker (ticker, market = undefined) { + const symbol = market['symbol']; + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'change'); + let open = undefined; + let change = undefined; + let average = undefined; + if (percentage !== undefined) { + const relativeChange = percentage / 100; + open = last / this.sum (1, relativeChange); + change = last - open; + average = this.sum (last, open) / 2; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVol'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.publicGetTickerRegionQuote (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.publicGetDepthRegionQuote (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const symbol = market['symbol']; + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = this.costToPrecision (symbol, price * amount); + } + } + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.publicGetOrdersRegionQuote (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalance (params); + const result = { 'info': response }; + const data = this.safeValue (response, 'data', {}); + const balances = this.omit (data, 'uid'); + const keys = Object.keys (balances); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const [ currencyId, accountType ] = key.split ('_'); + const code = this.safeCurrencyCode (currencyId); + if (!(code in result)) { + result[code] = this.account (); + } + const type = (accountType === 'lock') ? 'used' : 'free'; + result[code][type] = this.safeFloat (balances, key); + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'datetime')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount_original'); + const remaining = this.safeFloat (order, 'amount_outstanding'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + let status = this.safeString (order, 'status'); + if (status === 'cancelled') { + status = 'canceled'; + } else { + status = remaining ? 'open' : 'closed'; + } + const info = this.safeValue (order, 'info', order); + const type = 'limit'; + const side = this.safeString (order, 'type'); + const id = this.safeString (order, 'id'); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': info, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + 'type': side, + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostTradeAddRegionQuote (this.extend (request, params)); + const id = this.safeString (response, 'id'); + const order = this.parseOrder ({ + 'id': id, + 'datetime': this.ymdhms (this.milliseconds ()), + 'amount_original': amount, + 'amount_outstanding': amount, + 'price': price, + 'type': side, + 'info': response, + }, market); + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + return await this.privatePostTradeCancelRegionQuote (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + const response = await this.privatePostTradeViewRegionQuote (this.extend (request, params)); + return this.parseOrder (response['data'], market); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['baseId'], + 'quote': market['quoteId'], + }; + if (since !== undefined) { + request['since'] = since / 1000; + } + const response = await this.privatePostTradeListRegionQuote (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'open', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let apiType = 'rest'; + if (api === 'web') { + apiType = api; + } + let url = this.urls['api'][apiType] + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public' || api === 'web') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + query = this.urlencode (this.extend ({ + 'key': this.apiKey, + 'nonce': this.nonce (), + }, query)); + const secret = this.hash (this.encode (this.secret)); + const signature = this.hmac (this.encode (query), this.encode (secret)); + query += '&' + 'signature=' + signature; + if (method === 'GET') { + url += '?' + query; + } else { + headers = { + 'Content-type': 'application/x-www-form-urlencoded', + }; + body = query; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // private endpoints return the following structure: + // {"result":true,"data":{...}} - success + // {"result":false,"code":"103"} - failure + // {"code":0,"msg":"Suceess","data":{"uid":"2716039","btc_balance":"0.00000000","btc_lock":"0.00000000","xrp_balance":"0.00000000","xrp_lock":"0.00000000"}} + const result = this.safeValue (response, 'result'); + if (result === undefined) { + // public endpoint � this comment left here by the contributor, in fact a missing result does not necessarily mean a public endpoint... + // we should just check the code and don't rely on the result at all here... + return; + } + if (result === true) { + // success + return; + } + const errorCode = this.safeString (response, 'code'); + const errorMessages = this.errorMessages; + const message = this.safeString (errorMessages, errorCode, 'Unknown Error'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (this.id + ' ' + message); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinex.js b/TaskServer/node_modules/ccxt/js/coinex.js new file mode 100644 index 000000000..9d13942c6 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinex.js @@ -0,0 +1,912 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InsufficientFunds, OrderNotFound, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinex', + 'name': 'CoinEx', + 'version': 'v1', + 'countries': [ 'CN' ], + 'rateLimit': 1000, + 'has': { + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + }, + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': '1day', + '3d': '3day', + '1w': '1week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38046312-0b450aac-32c8-11e8-99ab-bc6b136b6cc7.jpg', + 'api': 'https://api.coinex.com', + 'www': 'https://www.coinex.com', + 'doc': 'https://github.com/coinexcom/coinex_exchange_api/wiki', + 'fees': 'https://www.coinex.com/fees', + 'referral': 'https://www.coinex.com/register?refer_code=yw5fz', + }, + 'api': { + 'public': { + 'get': [ + 'common/currency/rate', + 'common/asset/config', + 'market/info', + 'market/list', + 'market/ticker', + 'market/ticker/all', + 'market/depth', + 'market/deals', + 'market/kline', + ], + }, + 'private': { + 'get': [ + 'balance/coin/deposit', + 'balance/coin/withdraw', + 'balance/info', + 'future/account', + 'future/config', + 'future/limitprice', + 'future/loan/history', + 'future/market', + 'margin/account', + 'margin/config', + 'margin/loan/history', + 'margin/market', + 'order', + 'order/deals', + 'order/finished', + 'order/finished/{id}', + 'order/pending', + 'order/status', + 'order/status/batch', + 'order/user/deals', + ], + 'post': [ + 'balance/coin/withdraw', + 'future/flat', + 'future/loan', + 'future/transfer', + 'margin/flat', + 'margin/loan', + 'margin/transfer', + 'order/batchlimit', + 'order/ioc', + 'order/limit', + 'order/market', + 'sub_account/transfer', + ], + 'delete': [ + 'balance/coin/withdraw', + 'order/pending/batch', + 'order/pending', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.001, + 'taker': 0.001, + }, + 'funding': { + 'withdraw': { + 'BCH': 0.0, + 'BTC': 0.001, + 'LTC': 0.001, + 'ETH': 0.001, + 'ZEC': 0.0001, + 'DASH': 0.0001, + }, + }, + }, + 'limits': { + 'amount': { + 'min': 0.001, + 'max': undefined, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketInfo (params); + // + // { + // "code": 0, + // "data": { + // "WAVESBTC": { + // "name": "WAVESBTC", + // "min_amount": "1", + // "maker_fee_rate": "0.001", + // "taker_fee_rate": "0.001", + // "pricing_name": "BTC", + // "pricing_decimal": 8, + // "trading_name": "WAVES", + // "trading_decimal": 8 + // } + // } + // } + // + const markets = this.safeValue (response, 'data', {}); + const result = []; + const keys = Object.keys (markets); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = markets[key]; + const id = this.safeString (market, 'name'); + const tradingName = this.safeString (market, 'trading_name'); + const baseId = tradingName; + const quoteId = this.safeString (market, 'pricing_name'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + let symbol = base + '/' + quote; + if (tradingName === id) { + symbol = id; + } + const precision = { + 'amount': this.safeInteger (market, 'trading_decimal'), + 'price': this.safeInteger (market, 'pricing_decimal'), + }; + const active = undefined; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'taker': this.safeFloat (market, 'taker_fee_rate'), + 'maker': this.safeFloat (market, 'maker_fee_rate'), + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + ticker = this.safeValue (ticker, 'ticker', {}); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat2 (ticker, 'vol', 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketTicker (this.extend (request, params)); + return this.parseTicker (response['data'], market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketTickerAll (params); + const data = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (data, 'date'); + const tickers = this.safeValue (data, 'ticker'); + const marketIds = Object.keys (tickers); + const result = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = marketId; + let market = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + const ticker = { + 'date': timestamp, + 'ticker': tickers[marketId], + }; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchOrderBook (symbol, limit = 20, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 20; // default + } + const request = { + 'market': this.marketId (symbol), + 'merge': '0.0000000001', + 'limit': limit.toString (), + }; + const response = await this.publicGetMarketDepth (this.extend (request, params)); + return this.parseOrderBook (response['data']); + } + + parseTrade (trade, market = undefined) { + // this method parses both public and private trades + let timestamp = this.safeTimestamp (trade, 'create_time'); + if (timestamp === undefined) { + timestamp = this.safeInteger (trade, 'date_ms'); + } + const tradeId = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'order_id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const marketId = this.safeString (trade, 'market'); + market = this.safeValue (this.markets_by_id, marketId, market); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = this.safeFloat (trade, 'deal_money'); + if (!cost) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fee_asset'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const takerOrMaker = this.safeString (trade, 'role'); + const side = this.safeString (trade, 'type'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': tradeId, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetMarketDeals (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + parseFloat (ohlcv[1]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'type': this.timeframes[timeframe], + }; + const response = await this.publicGetMarketKline (this.extend (request, params)); + return this.parseOHLCVs (response['data'], market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalanceInfo (params); + // + // { + // "code": 0, + // "data": { + // "BCH": { # BCH account + // "available": "13.60109", # Available BCH + // "frozen": "0.00000" # Frozen BCH + // }, + // "BTC": { # BTC account + // "available": "32590.16", # Available BTC + // "frozen": "7000.00" # Frozen BTC + // }, + // "ETH": { # ETH account + // "available": "5.06000", # Available ETH + // "frozen": "0.00000" # Frozen ETH + // } + // }, + // "message": "Ok" + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'frozen'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'not_deal': 'open', + 'part_deal': 'open', + 'done': 'closed', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder + // + // { + // "amount": "0.1", + // "asset_fee": "0.22736197736197736197", + // "avg_price": "196.85000000000000000000", + // "create_time": 1537270135, + // "deal_amount": "0.1", + // "deal_fee": "0", + // "deal_money": "19.685", + // "fee_asset": "CET", + // "fee_discount": "0.5", + // "id": 1788259447, + // "left": "0", + // "maker_fee_rate": "0", + // "market": "ETHUSDT", + // "order_type": "limit", + // "price": "170.00000000", + // "status": "done", + // "taker_fee_rate": "0.0005", + // "type": "sell", + // } + // + const timestamp = this.safeTimestamp (order, 'create_time'); + const price = this.safeFloat (order, 'price'); + const cost = this.safeFloat (order, 'deal_money'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'deal_amount'); + const average = this.safeFloat (order, 'avg_price'); + let symbol = undefined; + const marketId = this.safeString (order, 'market'); + market = this.safeValue (this.markets_by_id, marketId); + const feeCurrencyId = this.safeString (order, 'fee_asset'); + let feeCurrency = this.safeCurrencyCode (feeCurrencyId); + if (market !== undefined) { + symbol = market['symbol']; + if (feeCurrency === undefined) { + feeCurrency = market['quote']; + } + } + const remaining = this.safeFloat (order, 'left'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const type = this.safeString (order, 'order_type'); + const side = this.safeString (order, 'type'); + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': { + 'currency': feeCurrency, + 'cost': this.safeFloat (order, 'deal_fee'), + }, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostOrder' + this.capitalize (type); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'type': side, + }; + amount = parseFloat (amount); + // for market buy it requires the amount of quote currency to spend + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + price = parseFloat (price); + request['amount'] = this.costToPrecision (symbol, amount * price); + } + } else { + request['amount'] = this.costToPrecision (symbol, amount); + } + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + if ((type === 'limit') || (type === 'ioc')) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this[method] (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'market': market['id'], + }; + const response = await this.privateDeleteOrderPending (this.extend (request, params)); + return this.parseOrder (response['data'], market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'market': market['id'], + }; + const response = await this.privateGetOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "data": { + // "amount": "0.1", + // "asset_fee": "0.22736197736197736197", + // "avg_price": "196.85000000000000000000", + // "create_time": 1537270135, + // "deal_amount": "0.1", + // "deal_fee": "0", + // "deal_money": "19.685", + // "fee_asset": "CET", + // "fee_discount": "0.5", + // "id": 1788259447, + // "left": "0", + // "maker_fee_rate": "0", + // "market": "ETHUSDT", + // "order_type": "limit", + // "price": "170.00000000", + // "status": "done", + // "taker_fee_rate": "0.0005", + // "type": "sell", + // }, + // "message": "Ok" + // } + // + return this.parseOrder (response['data'], market); + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const request = { + 'page': 1, + 'limit': limit, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const method = 'privateGetOrder' + this.capitalize (status); + const response = await this[method] (this.extend (request, params)); + return this.parseOrders (response['data']['data'], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('pending', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('finished', symbol, since, limit, params); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const request = { + 'page': 1, + 'limit': limit, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateGetOrderUserDeals (this.extend (request, params)); + return this.parseTrades (response['data']['data'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (tag) { + address = address + ':' + tag; + } + const request = { + 'coin_type': currency['id'], + 'coin_address': address, // must be authorized, inter-user transfer by a registered mobile phone number or an email address is supported + 'actual_amount': parseFloat (amount), // the actual amount without fees, https://www.coinex.com/fees + 'transfer_method': '1', // '1' = normal onchain transfer, '2' = internal local transfer from one user to another + }; + const response = await this.privatePostBalanceCoinWithdraw (this.extend (request, params)); + // + // { + // "code": 0, + // "data": { + // "actual_amount": "1.00000000", + // "amount": "1.00000000", + // "coin_address": "1KAv3pazbTk2JnQ5xTo6fpKK7p1it2RzD4", + // "coin_type": "BCH", + // "coin_withdraw_id": 206, + // "confirmations": 0, + // "create_time": 1524228297, + // "status": "audit", + // "tx_fee": "0", + // "tx_id": "" + // }, + // "message": "Ok" + // } + // + const transaction = this.safeValue (response, 'data', {}); + return this.parseTransaction (transaction, currency); + } + + parseTransactionStatus (status) { + const statuses = { + 'audit': 'pending', + 'pass': 'pending', + 'processing': 'pending', + 'confirming': 'pending', + 'not_pass': 'failed', + 'cancel': 'canceled', + 'finish': 'ok', + 'fail': 'failed', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "actual_amount": "120.00000000", + // "actual_amount_display": "120", + // "add_explorer": "XXX", + // "amount": "120.00000000", + // "amount_display": "120", + // "coin_address": "XXXXXXXX", + // "coin_address_display": "XXXXXXXX", + // "coin_deposit_id": 1866, + // "coin_type": "USDT", + // "confirmations": 0, + // "create_time": 1539595701, + // "explorer": "", + // "remark": "", + // "status": "finish", + // "status_display": "finish", + // "transfer_method": "local", + // "tx_id": "", + // "tx_id_display": "XXXXXXXXXX" + // } + // + // fetchWithdrawals + // + // { + // "actual_amount": "0.10000000", + // "amount": "0.10000000", + // "coin_address": "15sr1VdyXQ6sVLqeJUJ1uPzLpmQtgUeBSB", + // "coin_type": "BCH", + // "coin_withdraw_id": 203, + // "confirmations": 11, + // "create_time": 1515806440, + // "status": "finish", + // "tx_fee": "0", + // "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6" + // } + // + const id = this.safeString2 (transaction, 'coin_withdraw_id', 'coin_deposit_id'); + const address = this.safeString (transaction, 'coin_address'); + let tag = this.safeString (transaction, 'remark'); // set but unused + if (tag !== undefined) { + if (tag.length < 1) { + tag = undefined; + } + } + let txid = this.safeValue (transaction, 'tx_id'); + if (txid !== undefined) { + if (txid.length < 1) { + txid = undefined; + } + } + const currencyId = this.safeString (transaction, 'coin_type'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeTimestamp (transaction, 'create_time'); + const type = ('coin_withdraw_id' in transaction) ? 'withdraw' : 'deposit'; + const status = this.parseTransactionStatus (this.safeString (transaction, 'status'), type); + const amount = this.safeFloat (transaction, 'amount'); + let feeCost = this.safeFloat (transaction, 'tx_fee'); + if (type === 'deposit') { + feeCost = 0; + } + const fee = { + 'cost': feeCost, + 'currency': code, + }; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin_type': currency['id'], + }; + if (limit !== undefined) { + request['Limit'] = limit; + } + const response = await this.privateGetBalanceCoinWithdraw (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "actual_amount": "1.00000000", + // "amount": "1.00000000", + // "coin_address": "1KAv3pazbTk2JnQ5xTo6fpKK7p1it2RzD4", + // "coin_type": "BCH", + // "coin_withdraw_id": 206, + // "confirmations": 0, + // "create_time": 1524228297, + // "status": "audit", + // "tx_fee": "0", + // "tx_id": "" + // }, + // { + // "actual_amount": "0.10000000", + // "amount": "0.10000000", + // "coin_address": "15sr1VdyXQ6sVLqeJUJ1uPzLpmQtgUeBSB", + // "coin_type": "BCH", + // "coin_withdraw_id": 203, + // "confirmations": 11, + // "create_time": 1515806440, + // "status": "finish", + // "tx_fee": "0", + // "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6" + // }, + // { + // "actual_amount": "0.00100000", + // "amount": "0.00100000", + // "coin_address": "1GVVx5UBddLKrckTprNi4VhHSymeQ8tsLF", + // "coin_type": "BCH", + // "coin_withdraw_id": 27, + // "confirmations": 0, + // "create_time": 1513933541, + // "status": "cancel", + // "tx_fee": "0", + // "tx_id": "" + // } + // ], + // "message": "Ok" + // } + // + return this.parseTransactions (response['data'], currency, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin_type': currency['id'], + }; + if (limit !== undefined) { + request['Limit'] = limit; + } + const response = await this.privateGetBalanceCoinDeposit (this.extend (request, params)); + // { + // "code": 0, + // "data": [ + // { + // "actual_amount": "4.65397682", + // "actual_amount_display": "4.65397682", + // "add_explorer": "https://etherscan.io/address/0x361XXXXXX", + // "amount": "4.65397682", + // "amount_display": "4.65397682", + // "coin_address": "0x36dabcdXXXXXX", + // "coin_address_display": "0x361X*****XXXXX", + // "coin_deposit_id": 966191, + // "coin_type": "ETH", + // "confirmations": 30, + // "create_time": 1531661445, + // "explorer": "https://etherscan.io/tx/0x361XXXXXX", + // "remark": "", + // "status": "finish", + // "status_display": "finish", + // "transfer_method": "onchain", + // "tx_id": "0x361XXXXXX", + // "tx_id_display": "0x361XXXXXX" + // } + // ], + // "message": "Ok" + // } + // + return this.parseTransactions (response['data'], currency, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + path = this.implodeParams (path, params); + let url = this.urls['api'] + '/' + this.version + '/' + path; + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + query = this.extend ({ + 'access_id': this.apiKey, + 'tonce': nonce.toString (), + }, query); + query = this.keysort (query); + const urlencoded = this.urlencode (query); + const signature = this.hash (this.encode (urlencoded + '&secret_key=' + this.secret)); + headers = { + 'Authorization': signature.toUpperCase (), + 'Content-Type': 'application/json', + }; + if ((method === 'GET') || (method === 'DELETE')) { + url += '?' + urlencoded; + } else { + body = this.json (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const code = this.safeString (response, 'code'); + const data = this.safeValue (response, 'data'); + const message = this.safeString (response, 'message'); + if ((code !== '0') || (data === undefined) || ((message !== 'Ok') && !data)) { + const responseCodes = { + '24': AuthenticationError, + '25': AuthenticationError, + '107': InsufficientFunds, + '600': OrderNotFound, + '601': InvalidOrder, + '602': InvalidOrder, + '606': InvalidOrder, + }; + const ErrorClass = this.safeValue (responseCodes, code, ExchangeError); + throw new ErrorClass (response['message']); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinfalcon.js b/TaskServer/node_modules/ccxt/js/coinfalcon.js new file mode 100644 index 000000000..c30b05e0a --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinfalcon.js @@ -0,0 +1,429 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, RateLimitExceeded, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinfalcon extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinfalcon', + 'name': 'CoinFalcon', + 'countries': [ 'GB' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'fetchTickers': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/41822275-ed982188-77f5-11e8-92bb-496bcd14ca52.jpg', + 'api': 'https://coinfalcon.com', + 'www': 'https://coinfalcon.com', + 'doc': 'https://docs.coinfalcon.com', + 'fees': 'https://coinfalcon.com/fees', + 'referral': 'https://coinfalcon.com/?ref=CFJSVGTUPASB', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'markets/{market}/orders', + 'markets/{market}/trades', + ], + }, + 'private': { + 'get': [ + 'user/accounts', + 'user/orders', + 'user/orders/{id}', + 'user/trades', + ], + 'post': [ + 'user/orders', + ], + 'delete': [ + 'user/orders/{id}', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'maker': 0.0, + 'taker': 0.002, // tiered fee starts at 0.2% + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + const markets = this.safeValue (response, 'data'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const [ baseId, quoteId ] = market['name'].split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'size_precision'), + 'price': this.safeInteger (market, 'price_precision'), + }; + result.push ({ + 'id': market['name'], + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + if (market === undefined) { + const marketId = this.safeString (ticker, 'name'); + market = this.safeValue (this.markets_by_id, marketId, market); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.milliseconds (); + const last = parseFloat (ticker['last_price']); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'change_in_24h'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const tickers = await this.fetchTickers (params); + return tickers[symbol]; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarkets (params); + const tickers = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'level': '3', + }; + const response = await this.publicGetMarketsMarketOrders (this.extend (request, params)); + return this.parseOrderBook (response['data'], undefined, 'bids', 'asks', 'price', 'size'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + const symbol = market['symbol']; + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const tradeId = this.safeString (trade, 'id'); + const side = this.safeString (trade, 'side'); + const orderId = this.safeString (trade, 'order_id'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyCode = this.safeString (trade, 'fee_currency_code'); + fee = { + 'cost': feeCost, + 'currency': this.safeCurrencyCode (feeCurrencyCode), + }; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': tradeId, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (since !== undefined) { + request['start_time'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetUserTrades (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (since !== undefined) { + request['since'] = this.iso8601 (since); + } + const response = await this.publicGetMarketsMarketTrades (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetUserAccounts (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'available_balance'), + 'used': this.safeFloat (balance, 'hold_balance'), + 'total': this.safeFloat (balance, 'balance'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'fulfilled': 'closed', + 'canceled': 'canceled', + 'pending': 'open', + 'open': 'open', + 'partially_filled': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + if (market === undefined) { + const marketId = this.safeString (order, 'market'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat (order, 'size_filled'); + let remaining = undefined; + let cost = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); + } + if (price !== undefined) { + cost = parseFloat (this.priceToPrecision (symbol, filled * price)); + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let type = this.safeString (order, 'operation_type'); + if (type !== undefined) { + type = type.split ('_'); + type = type[0]; + } + const side = this.safeString (order, 'order_type'); + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // price/size must be string + const request = { + 'market': market['id'], + 'size': this.amountToPrecision (symbol, amount), + 'order_type': side, + }; + if (type === 'limit') { + price = this.priceToPrecision (symbol, price); + request['price'] = price.toString (); + } + request['operation_type'] = type + '_order'; + const response = await this.privatePostUserOrders (this.extend (request, params)); + const order = this.parseOrder (response['data'], market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateDeleteUserOrdersId (this.extend (request, params)); + const market = this.market (symbol); + return this.parseOrder (response['data'], market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetUserOrdersId (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbol !== undefined) { + request['market'] = this.marketId (symbol); + } + if (since !== undefined) { + request['since_time'] = this.iso8601 (this.milliseconds ()); + } + // TODO: test status=all if it works for closed orders too + const response = await this.privateGetUserOrders (this.extend (request, params)); + return this.parseOrders (response['data']); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/api/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + if (method === 'GET') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + } + const seconds = this.seconds ().toString (); + let payload = [ seconds, method, request ].join ('|'); + if (body) { + payload += '|' + body; + } + const signature = this.hmac (this.encode (payload), this.encode (this.secret)); + headers = { + 'CF-API-KEY': this.apiKey, + 'CF-API-TIMESTAMP': seconds, + 'CF-API-SIGNATURE': signature, + 'Content-Type': 'application/json', + }; + } + const url = this.urls['api'] + request; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code < 400) { + return; + } + const ErrorClass = this.safeValue ({ + '401': AuthenticationError, + '429': RateLimitExceeded, + }, code, ExchangeError); + throw new ErrorClass (body); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinfloor.js b/TaskServer/node_modules/ccxt/js/coinfloor.js new file mode 100644 index 000000000..e21e3fb57 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinfloor.js @@ -0,0 +1,519 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InsufficientFunds, ExchangeError, InvalidNonce, InvalidOrder, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinfloor extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinfloor', + 'name': 'coinfloor', + 'rateLimit': 1000, + 'countries': [ 'UK' ], + 'has': { + 'CORS': false, + 'fetchOpenOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28246081-623fc164-6a1c-11e7-913f-bac0d5576c90.jpg', + 'api': 'https://webapi.coinfloor.co.uk/bist', + 'www': 'https://www.coinfloor.co.uk', + 'doc': [ + 'https://github.com/coinfloor/api', + 'https://www.coinfloor.co.uk/api', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + 'password': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + '{id}/ticker/', + '{id}/order_book/', + '{id}/transactions/', + ], + }, + 'private': { + 'post': [ + '{id}/balance/', + '{id}/user_transactions/', + '{id}/open_orders/', + '{symbol}/cancel_order/', + '{id}/buy/', + '{id}/sell/', + '{id}/buy_market/', + '{id}/sell_market/', + '{id}/estimate_sell_market/', + '{id}/estimate_buy_market/', + ], + }, + }, + 'markets': { + 'BTC/GBP': { 'id': 'XBT/GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'XBT', 'quoteId': 'GBP', 'precision': { 'price': 0, 'amount': 4 }}, + 'BTC/EUR': { 'id': 'XBT/EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'XBT', 'quoteId': 'EUR', 'precision': { 'price': 0, 'amount': 4 }}, + 'ETH/GBP': { 'id': 'ETH/GBP', 'symbol': 'ETH/GBP', 'base': 'ETH', 'quote': 'GBP', 'baseId': 'ETH', 'quoteId': 'GBP', 'precision': { 'price': 0, 'amount': 4 }}, + }, + 'exceptions': { + 'exact': { + 'You have insufficient funds.': InsufficientFunds, + 'Tonce is out of sequence.': InvalidNonce, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + let market = undefined; + let query = params; + const symbol = this.safeString (params, 'symbol'); + if (symbol !== undefined) { + market = this.market (params['symbol']); + query = this.omit (params, 'symbol'); + } + const marketId = this.safeString (params, 'id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market === undefined) { + throw new ArgumentsRequired (this.id + ' fetchBalance requires a symbol param'); + } + const request = { + 'id': market['id'], + }; + const response = await this.privatePostIdBalance (this.extend (request, query)); + const result = { + 'info': response, + }; + // base/quote used for keys e.g. "xbt_reserved" + const base = market['base']; + const quote = market['quote']; + const baseIdLower = this.safeStringLower (market, 'baseId'); + const quoteIdLower = this.safeStringLower (market, 'quoteId'); + result[base] = { + 'free': this.safeFloat (response, baseIdLower + '_available'), + 'used': this.safeFloat (response, baseIdLower + '_reserved'), + 'total': this.safeFloat (response, baseIdLower + '_balance'), + }; + result[quote] = { + 'free': this.safeFloat (response, quoteIdLower + '_available'), + 'used': this.safeFloat (response, quoteIdLower + '_reserved'), + 'total': this.safeFloat (response, quoteIdLower + '_balance'), + }; + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetIdOrderBook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + // rewrite to get the timestamp from HTTP headers + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetIdTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetIdTransactions (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + // code is actually a market symbol in this situation, not a currency code + await this.loadMarkets (); + let market = undefined; + if (code !== undefined) { + market = this.market (code); + if (market === undefined) { + throw new ArgumentsRequired (this.id + ' fetchTransactions requires a code argument (a market symbol)'); + } + } + const request = { + 'id': market['id'], + 'limit': limit, + }; + const response = await this.privatePostIdUserTransactions (this.extend (request, params)); + return this.parseLedger (response, undefined, since, undefined); + } + + parseLedgerEntryStatus (status) { + const types = { + 'completed': 'ok', + }; + return this.safeString (types, status, status); + } + + parseLedgerEntryType (type) { + const types = { + '0': 'transaction', // deposit + '1': 'transaction', // withdrawal + '2': 'trade', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // trade + // + // { + // "datetime": "2017-07-25 06:41:24", + // "id": 1500964884381265, + // "type": 2, + // "xbt": "0.1000", + // "xbt_eur": "2322.00", + // "eur": "-232.20", + // "fee": "0.00", + // "order_id": 84696745 + // } + // + // transaction (withdrawal) + // + // { + // "datetime": "2017-07-25 13:19:46", + // "id": 97669, + // "type": 1, + // "xbt": "-3.0000", + // "xbt_eur": null, + // "eur": "0", + // "fee": "0.0000", + // "order_id": null + // } + // + // transaction (deposit) + // + // { + // "datetime": "2017-07-27 16:44:55", + // "id": 98277, + // "type": 0, + // "xbt": "0", + // "xbt_eur": null, + // "eur": "4970.04", + // "fee": "0.00", + // "order_id": null + // } + // + const keys = Object.keys (item); + let baseId = undefined; + let quoteId = undefined; + let baseAmount = undefined; + let quoteAmount = undefined; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (key.indexOf ('_') > 0) { + const parts = key.split ('_'); + const numParts = parts.length; + if (numParts === 2) { + const tmpBaseAmount = this.safeFloat (item, parts[0]); + const tmpQuoteAmount = this.safeFloat (item, parts[1]); + if (tmpBaseAmount !== undefined && tmpQuoteAmount !== undefined) { + baseId = parts[0]; + quoteId = parts[1]; + baseAmount = tmpBaseAmount; + quoteAmount = tmpQuoteAmount; + } + } + } + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const referenceId = this.safeString (item, 'id'); + const timestamp = this.parse8601 (this.safeString (item, 'datetime')); + let fee = undefined; + const feeCost = this.safeFloat (item, 'fee'); + const result = { + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'amount': undefined, + 'direction': undefined, + 'currency': undefined, + 'type': type, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'before': undefined, + 'after': undefined, + 'status': 'ok', + 'fee': fee, + 'info': item, + }; + if (type === 'trade') { + // + // it's a trade so let's make multiple entries, we have several options: + // + // if fee is always in quote currency (the exchange uses this) + // https://github.com/coinfloor/API/blob/master/IMPL-GUIDE.md#how-fees-affect-trade-quantities + // + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': quote, + }; + } + return [ + this.extend (result, { 'currency': base, 'amount': Math.abs (baseAmount), 'direction': (baseAmount > 0) ? 'in' : 'out' }), + this.extend (result, { 'currency': quote, 'amount': Math.abs (quoteAmount), 'direction': (quoteAmount > 0) ? 'in' : 'out', 'fee': fee }), + ]; + // + // if fee is base or quote depending on buy/sell side + // + // const baseFee = (baseAmount > 0) ? { 'currency': base, 'cost': feeCost } : undefined; + // const quoteFee = (quoteAmount > 0) ? { 'currency': quote, 'cost': feeCost } : undefined; + // return [ + // this.extend (result, { 'currency': base, 'amount': baseAmount, 'direction': (baseAmount > 0) ? 'in' : 'out', 'fee': baseFee }), + // this.extend (result, { 'currency': quote, 'amount': quoteAmount, 'direction': (quoteAmount > 0) ? 'in' : 'out', 'fee': quoteFee }), + // ]; + // + // fee as the 3rd item + // + // return [ + // this.extend (result, { 'currency': base, 'amount': baseAmount, 'direction': (baseAmount > 0) ? 'in' : 'out' }), + // this.extend (result, { 'currency': quote, 'amount': quoteAmount, 'direction': (quoteAmount > 0) ? 'in' : 'out' }), + // this.extend (result, { 'currency': feeCurrency, 'amount': feeCost, 'direction': 'out', 'type': 'fee' }), + // ]; + // + } else { + // + // it's a regular transaction (deposit or withdrawal) + // + const amount = (baseAmount === 0) ? quoteAmount : baseAmount; + const code = (baseAmount === 0) ? quote : base; + const direction = (amount > 0) ? 'in' : 'out'; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': code, + }; + } + return this.extend (result, { + 'currency': code, + 'amount': Math.abs (amount), + 'direction': direction, + 'fee': fee, + }); + } + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + let method = 'privatePostId' + this.capitalize (side); + if (type === 'market') { + request['quantity'] = amount; + method += 'Market'; + } else { + request['price'] = price; + request['amount'] = amount; + } + return await this[method] (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'id': id, + }; + const response = await this.privatePostSymbolCancelOrder (request); + if (response === 'false') { + // unfortunately the exchange does not give much info in the response + throw new InvalidOrder (this.id + ' cancel was rejected'); + } + return response; + } + + parseOrder (order, market = undefined) { + const timestamp = this.parse8601 (this.safeString (order, 'datetime')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let side = undefined; + const status = this.safeString (order, 'status'); + if (order['type'] === 0) { + side = 'buy'; + } else if (order['type'] === 1) { + side = 'sell'; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol param'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.privatePostIdOpenOrders (this.extend (request, params)); + // { + // "amount": "1.0000", + // "datetime": "2019-07-12 13:28:16", + // "id": 233123443, + // "price": "1000.00", + // "type": 0 + // } + return this.parseOrders (response, market, since, limit, { 'status': 'open' }); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code < 400) { + return; + } + if (response === undefined) { + return; + } + const message = this.safeString (response, 'error_msg'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (feedback); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + // curl -k -u '[User ID]/[API key]:[Passphrase]' https://webapi.coinfloor.co.uk:8090/bist/XBT/GBP/balance/ + let url = this.urls['api'] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, query)); + const auth = this.uid + '/' + this.apiKey + ':' + this.password; + const signature = this.decode (this.stringToBase64 (this.encode (auth))); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ' + signature, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coingi.js b/TaskServer/node_modules/ccxt/js/coingi.js new file mode 100644 index 000000000..03197be93 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coingi.js @@ -0,0 +1,347 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coingi extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coingi', + 'name': 'Coingi', + 'rateLimit': 1000, + 'countries': [ 'PA', 'BG', 'CN', 'US' ], // Panama, Bulgaria, China, US + 'has': { + 'CORS': false, + 'fetchTickers': true, + }, + 'urls': { + 'referral': 'https://www.coingi.com/?r=XTPPMC', + 'logo': 'https://user-images.githubusercontent.com/1294454/28619707-5c9232a8-7212-11e7-86d6-98fe5d15cc6e.jpg', + 'api': { + 'www': 'https://coingi.com', + 'current': 'https://api.coingi.com', + 'user': 'https://api.coingi.com', + }, + 'www': 'https://coingi.com', + 'doc': 'https://coingi.docs.apiary.io', + }, + 'api': { + 'www': { + 'get': [ + '', + ], + }, + 'current': { + 'get': [ + 'order-book/{pair}/{askCount}/{bidCount}/{depth}', + 'transactions/{pair}/{maxCount}', + '24hour-rolling-aggregation', + ], + }, + 'user': { + 'post': [ + 'balance', + 'add-order', + 'cancel-order', + 'orders', + 'transactions', + 'create-crypto-withdrawal', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.2 / 100, + 'maker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'LTC': 0.01, + 'DOGE': 2, + 'PPC': 0.02, + 'VTC': 0.2, + 'NMC': 2, + 'DASH': 0.002, + 'USD': 10, + 'EUR': 10, + }, + 'deposit': { + 'BTC': 0, + 'LTC': 0, + 'DOGE': 0, + 'PPC': 0, + 'VTC': 0, + 'NMC': 0, + 'DASH': 0, + 'USD': 5, + 'EUR': 1, + }, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.wwwGet (params); + const parts = response.split ('do=currencyPairSelector-selectCurrencyPair" class="active">'); + const currencyParts = parts[1].split ('
'); + const result = []; + for (let i = 1; i < currencyParts.length; i++) { + const currencyPart = currencyParts[i]; + const idParts = currencyPart.split ('
'); + let id = idParts[0]; + id = id.replace ('/', '-'); + id = id.toLowerCase (); + const [ baseId, quoteId ] = id.split ('-'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': id, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const lowercaseCurrencies = []; + const currencies = Object.keys (this.currencies); + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + lowercaseCurrencies.push (currency.toLowerCase ()); + } + const request = { + 'currencies': lowercaseCurrencies.join (','), + }; + const response = await this.userPostBalance (this.extend (request, params)); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance['currency'], 'name'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + const blocked = this.safeFloat (balance, 'blocked'); + const inOrders = this.safeFloat (balance, 'inOrders'); + const withdrawing = this.safeFloat (balance, 'withdrawing'); + account['used'] = this.sum (blocked, inOrders, withdrawing); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = 512, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'depth': 32, // maximum number of depth range steps 1-32 + 'askCount': limit, // maximum returned number of asks 1-512 + 'bidCount': limit, // maximum returned number of bids 1-512 + }; + const orderbook = await this.currentGetOrderBookPairAskCountBidCountDepth (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'baseAmount'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': undefined, + 'last': undefined, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'counterVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.currentGet24hourRollingAggregation (params); + const result = {}; + for (let t = 0; t < response.length; t++) { + const ticker = response[t]; + const base = ticker['currencyPair']['base'].toUpperCase (); + const quote = ticker['currencyPair']['counter'].toUpperCase (); + const symbol = base + '/' + quote; + let market = undefined; + if (symbol in this.markets) { + market = this.markets[symbol]; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const tickers = await this.fetchTickers (undefined, params); + if (symbol in tickers) { + return tickers[symbol]; + } + throw new ExchangeError (this.id + ' return did not contain ' + symbol); + } + + parseTrade (trade, market = undefined) { + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const timestamp = this.safeInteger (trade, 'timestamp'); + const id = this.safeString (trade, 'id'); + const marketId = this.safeString (trade, 'currencyPair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, // type + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'maxCount': 128, + }; + const response = await this.currentGetTransactionsPairMaxCount (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + 'volume': amount, + 'price': price, + 'orderType': (side === 'buy') ? 0 : 1, + }; + const response = await this.userPostAddOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response['result'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + return await this.userPostCancelOrder (this.extend (request, params)); + } + + sign (path, api = 'current', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api !== 'www') { + url += '/' + api + '/' + this.implodeParams (path, params); + } + const query = this.omit (params, this.extractParams (path)); + if (api === 'current') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'user') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = this.extend ({ + 'token': this.apiKey, + 'nonce': nonce, + }, query); + const auth = nonce.toString () + '$' + this.apiKey; + request['signature'] = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.json (request); + headers = { + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'current', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (typeof response !== 'string') { + if ('errors' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinmarketcap.js b/TaskServer/node_modules/ccxt/js/coinmarketcap.js new file mode 100644 index 000000000..6e7f7b115 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinmarketcap.js @@ -0,0 +1,326 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinmarketcap extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinmarketcap', + 'name': 'CoinMarketCap', + 'rateLimit': 10000, + 'version': 'v1', + 'countries': [ 'US' ], + 'has': { + 'CORS': true, + 'privateAPI': false, + 'createOrder': false, + 'createMarketOrder': false, + 'createLimitOrder': false, + 'cancelOrder': false, + 'editOrder': false, + 'fetchBalance': false, + 'fetchOrderBook': false, + 'fetchL2OrderBook': false, + 'fetchOHLCV': false, + 'fetchTrades': false, + 'fetchTickers': true, + 'fetchCurrencies': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28244244-9be6312a-69ed-11e7-99c1-7c1797275265.jpg', + 'api': { + 'public': 'https://api.coinmarketcap.com', + 'files': 'https://files.coinmarketcap.com', + 'charts': 'https://graph.coinmarketcap.com', + }, + 'www': 'https://coinmarketcap.com', + 'doc': 'https://coinmarketcap.com/api', + }, + 'requiredCredentials': { + 'apiKey': false, + 'secret': false, + }, + 'api': { + 'files': { + 'get': [ + 'generated/stats/global.json', + ], + }, + 'graphs': { + 'get': [ + 'currencies/{name}/', + ], + }, + 'public': { + 'get': [ + 'ticker/', + 'ticker/{id}/', + 'global/', + ], + }, + }, + 'currencyCodes': [ + 'AUD', + 'BRL', + 'CAD', + 'CHF', + 'CNY', + 'EUR', + 'GBP', + 'HKD', + 'IDR', + 'INR', + 'JPY', + 'KRW', + 'MXN', + 'RUB', + 'USD', + 'BTC', + 'ETH', + 'LTC', + ], + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + throw new ExchangeError ('Fetching order books is not supported by the API of ' + this.id); + } + + currencyCode (base, name) { + const currencies = { + 'ACChain': 'ACChain', + 'AdCoin': 'AdCoin', + 'BatCoin': 'BatCoin', + 'Bitgem': 'Bitgem', + 'BlazeCoin': 'BlazeCoin', + 'BlockCAT': 'BlockCAT', + 'Blocktrade Token': 'Blocktrade Token', + 'Catcoin': 'Catcoin', + 'CanYaCoin': 'CanYaCoin', // conflict with CAN (Content and AD Network) + 'Comet': 'Comet', // conflict with CMT (CyberMiles) + 'CPChain': 'CPChain', + 'CrowdCoin': 'CrowdCoin', // conflict with CRC CryCash + 'Cryptaur': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 + 'Cubits': 'Cubits', // conflict with QBT (Qbao) + 'DAO.Casino': 'DAO.Casino', // conflict with BET (BetaCoin) + 'E-Dinar Coin': 'E-Dinar Coin', // conflict with EDR Endor Protocol and EDRCoin + 'EDRcoin': 'EDRcoin', // conflict with EDR Endor Protocol and E-Dinar Coin + 'ENTCash': 'ENTCash', // conflict with ENT (Eternity) + 'FairCoin': 'FairCoin', // conflict with FAIR (FairGame) https://github.com/ccxt/ccxt/pull/5865 + 'Fabric Token': 'Fabric Token', + // 'GET Protocol': 'GET Protocol', + 'Global Tour Coin': 'Global Tour Coin', // conflict with GTC (Game.com) + 'GuccioneCoin': 'GuccioneCoin', // conflict with GCC (Global Cryptocurrency) + 'HarmonyCoin': 'HarmonyCoin', // conflict with HMC (Hi Mutual Society) + 'Harvest Masternode Coin': 'Harvest Masternode Coin', // conflict with HC (HyperCash) + 'HOT Token': 'HOT Token', + 'Hydro Protocol': 'Hydro Protocol', // conflict with HOT (Holo) + 'Huncoin': 'Huncoin', // conflict with HNC (Helleniccoin) + 'iCoin': 'iCoin', + 'Infinity Economics': 'Infinity Economics', // conflict with XIN (Mixin) + 'KingN Coin': 'KingN Coin', // conflict with KNC (Kyber Network) + 'LiteBitcoin': 'LiteBitcoin', // conflict with LBTC (LightningBitcoin) + 'Maggie': 'Maggie', + 'MTC Mesh Network': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025 + 'IOTA': 'IOTA', // a special case, most exchanges list it as IOTA, therefore we change just the Coinmarketcap instead of changing them all + 'NetCoin': 'NetCoin', + 'PCHAIN': 'PCHAIN', // conflict with PAI (Project Pai) + 'Polcoin': 'Polcoin', + 'PutinCoin': 'PutinCoin', // conflict with PUT (Profile Utility Token) + 'Rcoin': 'Rcoin', // conflict with RCN (Ripio Credit Network) + // https://github.com/ccxt/ccxt/issues/6081 + // https://github.com/ccxt/ccxt/issues/3365 + // https://github.com/ccxt/ccxt/issues/2873 + 'Themis': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol) + 'Menlo One': 'Menlo One', // conflict with Harmony (ONE) + 'BigONE Token': 'BigONE Token', // conflict with Harmony (ONE) + }; + return this.safeValue (currencies, name, base); + } + + async fetchMarkets (params = {}) { + const request = { + 'limit': 0, + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const currencies = this.currencyCodes; + for (let j = 0; j < currencies.length; j++) { + const quote = currencies[j]; + const quoteId = quote.toLowerCase (); + const baseId = market['id']; + const base = this.currencyCode (market['symbol'], market['name']); + const symbol = base + '/' + quote; + const id = baseId + '/' + quoteId; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + } + return result; + } + + async fetchGlobal (currency = 'USD') { + await this.loadMarkets (); + const request = {}; + if (currency) { + request['convert'] = currency; + } + return await this.publicGetGlobal (request); + } + + parseTicker (ticker, market = undefined) { + let timestamp = this.safeTimestamp (ticker, 'last_updated'); + if (timestamp === undefined) { + timestamp = this.milliseconds (); + } + const change = this.safeFloat (ticker, 'percent_change_24h'); + let last = undefined; + let symbol = undefined; + let volume = undefined; + if (market !== undefined) { + symbol = market['symbol']; + const priceKey = 'price_' + market['quoteId']; + last = this.safeFloat (ticker, priceKey); + const volumeKey = '24h_volume_' + market['quoteId']; + volume = this.safeFloat (ticker, volumeKey); + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': change, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': volume, + 'info': ticker, + }; + } + + async fetchTickers (currency = 'USD', params = {}) { + await this.loadMarkets (); + const request = { + 'limit': 10000, + }; + if (currency) { + request['convert'] = currency; + } + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + for (let t = 0; t < response.length; t++) { + const ticker = response[t]; + const currencyId = currency.toLowerCase (); + const id = ticker['id'] + '/' + currencyId; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'convert': market['quote'], + 'id': market['baseId'], + }; + const response = await this.publicGetTickerId (this.extend (request, params)); + const ticker = response[0]; + return this.parseTicker (ticker, market); + } + + async fetchCurrencies (params = {}) { + const request = { + 'limit': 0, + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const name = this.safeString (currency, 'name'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const precision = 8; // default precision, todo: fix "magic constants" + const code = this.currencyCode (id, name); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': name, + 'active': true, + 'fee': undefined, // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + if (response['error']) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinmate.js b/TaskServer/node_modules/ccxt/js/coinmate.js new file mode 100644 index 000000000..3418dfb9d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinmate.js @@ -0,0 +1,518 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinmate extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinmate', + 'name': 'CoinMate', + 'countries': [ 'GB', 'CZ', 'EU' ], // UK, Czech Republic + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'fetchMyTrades': true, + 'fetchTransactions': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27811229-c1efb510-606c-11e7-9a36-84ba2ce412d8.jpg', + 'api': 'https://coinmate.io/api', + 'www': 'https://coinmate.io', + 'fees': 'https://coinmate.io/fees', + 'doc': [ + 'https://coinmate.docs.apiary.io', + 'https://coinmate.io/developers', + ], + 'referral': 'https://coinmate.io?referral=YTFkM1RsOWFObVpmY1ZjMGREQmpTRnBsWjJJNVp3PT0', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'orderBook', + 'ticker', + 'transactions', + 'tradingPairs', + ], + }, + 'private': { + 'post': [ + 'balances', + 'bitcoinCashWithdrawal', + 'bitcoinCashDepositAddresses', + 'bitcoinDepositAddresses', + 'bitcoinWithdrawal', + 'bitcoinWithdrawalFees', + 'buyInstant', + 'buyLimit', + 'cancelOrder', + 'cancelOrderWithInfo', + 'createVoucher', + 'dashDepositAddresses', + 'dashWithdrawal', + 'ethereumWithdrawal', + 'ethereumDepositAddresses', + 'litecoinWithdrawal', + 'litecoinDepositAddresses', + 'openOrders', + 'order', + 'orderHistory', + 'pusherAuth', + 'redeemVoucher', + 'replaceByBuyLimit', + 'replaceByBuyInstant', + 'replaceBySellLimit', + 'replaceBySellInstant', + 'rippleDepositAddresses', + 'rippleWithdrawal', + 'sellInstant', + 'sellLimit', + 'transactionHistory', + 'traderFees', + 'tradeHistory', + 'transfer', + 'transferHistory', + 'unconfirmedBitcoinDeposits', + 'unconfirmedBitcoinCashDeposits', + 'unconfirmedDashDeposits', + 'unconfirmedEthereumDeposits', + 'unconfirmedLitecoinDeposits', + 'unconfirmedRippleDeposits', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.05 / 100, + 'taker': 0.15 / 100, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTradingPairs (params); + // + // { + // "error":false, + // "errorMessage":null, + // "data": [ + // { + // "name":"BTC_EUR", + // "firstCurrency":"BTC", + // "secondCurrency":"EUR", + // "priceDecimals":2, + // "lotDecimals":8, + // "minAmount":0.0002, + // "tradesWebSocketChannelId":"trades-BTC_EUR", + // "orderBookWebSocketChannelId":"order_book-BTC_EUR", + // "tradeStatisticsWebSocketChannelId":"statistics-BTC_EUR" + // }, + // ] + // } + // + const data = this.safeValue (response, 'data'); + const result = []; + for (let i = 0; i < data.length; i++) { + const market = data[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'firstCurrency'); + const quoteId = this.safeString (market, 'secondCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': market, + 'precision': { + 'price': this.safeInteger (market, 'priceDecimals'), + 'amount': this.safeInteger (market, 'lotDecimals'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalances (params); + const balances = this.safeValue (response, 'data'); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (balances, currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'reserved'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + 'groupByPriceLimit': 'False', + }; + const response = await this.publicGetOrderBook (this.extend (request, params)); + const orderbook = response['data']; + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'data'); + const timestamp = this.safeTimestamp (ticker, 'timestamp'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'vwap': undefined, + 'askVolume': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'amount'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'limit': 1000, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['timestampFrom'] = since; + } + if (code !== undefined) { + request['currency'] = this.currencyId (code); + } + const response = await this.privatePostTransferHistory (this.extend (request, params)); + const items = response['data']; + return this.parseTransactions (items, undefined, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + // any other types ? + 'COMPLETED': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (item, currency = undefined) { + // + // deposits + // + // { + // transactionId: 1862815, + // timestamp: 1516803982388, + // amountCurrency: 'LTC', + // amount: 1, + // fee: 0, + // walletType: 'LTC', + // transferType: 'DEPOSIT', + // transferStatus: 'COMPLETED', + // txid: + // 'ccb9255dfa874e6c28f1a64179769164025329d65e5201849c2400abd6bce245', + // destination: 'LQrtSKA6LnhcwRrEuiborQJnjFF56xqsFn', + // destinationTag: null + // } + // + // withdrawals + // + // { + // transactionId: 2140966, + // timestamp: 1519314282976, + // amountCurrency: 'EUR', + // amount: 8421.7228, + // fee: 16.8772, + // walletType: 'BANK_WIRE', + // transferType: 'WITHDRAWAL', + // transferStatus: 'COMPLETED', + // txid: null, + // destination: null, + // destinationTag: null + // } + // + const timestamp = this.safeInteger (item, 'timestamp'); + const amount = this.safeFloat (item, 'amount'); + const fee = this.safeFloat (item, 'fee'); + const txid = this.safeString (item, 'txid'); + const address = this.safeString (item, 'destination'); + const tag = this.safeString (item, 'destinationTag'); + const currencyId = this.safeString (item, 'amountCurrency'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeStringLower (item, 'transferType'); + const status = this.parseTransactionStatus (this.safeString (item, 'transferStatus')); + const id = this.safeString (item, 'transactionId'); + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'currency': code, + 'amount': amount, + 'type': type, + 'txid': txid, + 'address': address, + 'tag': tag, + 'status': status, + 'fee': { + 'cost': fee, + 'currency': currency, + }, + 'info': item, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 1000; + } + const request = { + 'limit': limit, + }; + if (since !== undefined) { + request['timestampFrom'] = since; + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + const items = response['data']; + return this.parseTrades (items, undefined, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchMyTrades (private) + // + // { + // transactionId: 2671819, + // createdTimestamp: 1529649127605, + // currencyPair: 'LTC_BTC', + // type: 'BUY', + // orderType: 'LIMIT', + // orderId: 101810227, + // amount: 0.01, + // price: 0.01406, + // fee: 0, + // feeType: 'MAKER' + // } + // + // fetchTrades (public) + // + // { + // "timestamp":1561598833416, + // "transactionId":"4156303", + // "price":10950.41, + // "amount":0.004, + // "currencyPair":"BTC_EUR", + // "tradeType":"BUY" + // } + // + let symbol = undefined; + const marketId = this.safeString (trade, 'currencyPair'); + let quote = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id[marketId]) { + market = this.markets_by_id[marketId]; + quote = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const side = this.safeStringLower2 (trade, 'type', 'tradeType'); + const type = this.safeStringLower (trade, 'orderType'); + const orderId = this.safeString (trade, 'orderId'); + const id = this.safeString (trade, 'transactionId'); + const timestamp = this.safeInteger2 (trade, 'timestamp', 'createdTimestamp'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': quote, + }; + } + let takerOrMaker = this.safeString (trade, 'feeType'); + takerOrMaker = (takerOrMaker === 'MAKER') ? 'maker' : 'taker'; + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': orderId, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'minutesIntoHistory': 10, + }; + const response = await this.publicGetTransactions (this.extend (request, params)); + // + // { + // "error":false, + // "errorMessage":null, + // "data":[ + // { + // "timestamp":1561598833416, + // "transactionId":"4156303", + // "price":10950.41, + // "amount":0.004, + // "currencyPair":"BTC_EUR", + // "tradeType":"BUY" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseTrades (data, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let method = 'privatePost' + this.capitalize (side); + const request = { + 'currencyPair': this.marketId (symbol), + }; + if (type === 'market') { + if (side === 'buy') { + request['total'] = amount; // amount in fiat + } else { + request['amount'] = amount; // amount in fiat + } + method += 'Instant'; + } else { + request['amount'] = amount; // amount in crypto + request['price'] = price; + method += this.capitalize (type); + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['data'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostCancelOrder ({ 'orderId': id }); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const auth = nonce + this.uid + this.apiKey; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.urlencode (this.extend ({ + 'clientId': this.uid, + 'nonce': nonce, + 'publicKey': this.apiKey, + 'signature': signature.toUpperCase (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + if (response['error']) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinone.js b/TaskServer/node_modules/ccxt/js/coinone.js new file mode 100644 index 000000000..56bb65ce9 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinone.js @@ -0,0 +1,509 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, BadRequest, ExchangeError, ArgumentsRequired, InvalidOrder, OrderNotFound, OnMaintenance } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinone extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinone', + 'name': 'CoinOne', + 'countries': [ 'KR' ], // Korea + 'rateLimit': 667, + 'version': 'v2', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchTickers': true, + 'fetchOrder': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38003300-adc12fba-323f-11e8-8525-725f53c4a659.jpg', + 'api': 'https://api.coinone.co.kr', + 'www': 'https://coinone.co.kr', + 'doc': 'https://doc.coinone.co.kr', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + }, + 'api': { + 'public': { + 'get': [ + 'orderbook/', + 'trades/', + 'ticker/', + ], + }, + 'private': { + 'post': [ + 'account/btc_deposit_address/', + 'account/balance/', + 'account/daily_balance/', + 'account/user_info/', + 'account/virtual_account/', + 'order/cancel_all/', + 'order/cancel/', + 'order/limit_buy/', + 'order/limit_sell/', + 'order/complete_orders/', + 'order/limit_orders/', + 'order/order_info/', + 'transaction/auth_number/', + 'transaction/history/', + 'transaction/krw/history/', + 'transaction/btc/', + 'transaction/coin/', + ], + }, + }, + 'markets': { + 'BCH/KRW': { 'id': 'bch', 'symbol': 'BCH/KRW', 'base': 'BCH', 'quote': 'KRW', 'baseId': 'bch', 'quoteId': 'krw' }, + 'BTC/KRW': { 'id': 'btc', 'symbol': 'BTC/KRW', 'base': 'BTC', 'quote': 'KRW', 'baseId': 'btc', 'quoteId': 'krw' }, + 'BTG/KRW': { 'id': 'btg', 'symbol': 'BTG/KRW', 'base': 'BTG', 'quote': 'KRW', 'baseId': 'btg', 'quoteId': 'krw' }, + 'ETC/KRW': { 'id': 'etc', 'symbol': 'ETC/KRW', 'base': 'ETC', 'quote': 'KRW', 'baseId': 'etc', 'quoteId': 'krw' }, + 'ETH/KRW': { 'id': 'eth', 'symbol': 'ETH/KRW', 'base': 'ETH', 'quote': 'KRW', 'baseId': 'eth', 'quoteId': 'krw' }, + 'IOTA/KRW': { 'id': 'iota', 'symbol': 'IOTA/KRW', 'base': 'IOTA', 'quote': 'KRW', 'baseId': 'iota', 'quoteId': 'krw' }, + 'LTC/KRW': { 'id': 'ltc', 'symbol': 'LTC/KRW', 'base': 'LTC', 'quote': 'KRW', 'baseId': 'ltc', 'quoteId': 'krw' }, + 'OMG/KRW': { 'id': 'omg', 'symbol': 'OMG/KRW', 'base': 'OMG', 'quote': 'KRW', 'baseId': 'omg', 'quoteId': 'krw' }, + 'QTUM/KRW': { 'id': 'qtum', 'symbol': 'QTUM/KRW', 'base': 'QTUM', 'quote': 'KRW', 'baseId': 'qtum', 'quoteId': 'krw' }, + 'XRP/KRW': { 'id': 'xrp', 'symbol': 'XRP/KRW', 'base': 'XRP', 'quote': 'KRW', 'baseId': 'xrp', 'quoteId': 'krw' }, + 'EOS/KRW': { 'id': 'eos', 'symbol': 'EOS/KRW', 'base': 'EOS', 'quote': 'KRW', 'baseId': 'eos', 'quoteId': 'krw' }, + 'DATA/KRW': { 'id': 'data', 'symbol': 'DATA/KRW', 'base': 'DATA', 'quote': 'KRW', 'baseId': 'data', 'quoteId': 'krw' }, + 'ZIL/KRW': { 'id': 'zil', 'symbol': 'ZIL/KRW', 'base': 'ZIL', 'quote': 'KRW', 'baseId': 'zil', 'quoteId': 'krw' }, + 'KNC/KRW': { 'id': 'knc', 'symbol': 'KNC/KRW', 'base': 'KNC', 'quote': 'KRW', 'baseId': 'knc', 'quoteId': 'krw' }, + 'ZRX/KRW': { 'id': 'zrx', 'symbol': 'ZRX/KRW', 'base': 'ZRX', 'quote': 'KRW', 'baseId': 'zrx', 'quoteId': 'krw' }, + 'LUNA/KRW': { 'id': 'luna', 'symbol': 'LUNA/KRW', 'base': 'LUNA', 'quote': 'KRW', 'baseId': 'luna', 'quoteId': 'krw' }, + 'ATOM/KRW': { 'id': 'atom', 'symbol': 'ATOM/KRW', 'base': 'ATOM', 'quote': 'KRW', 'baseId': 'atom', 'quoteId': 'krw' }, + 'VNT/KRW': { 'id': 'vnt', 'symbol': 'VNT/KRW', 'base': 'VNT', 'quote': 'KRW', 'baseId': 'vnt', 'quoteId': 'krw' }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + 'tiers': { + 'taker': [ + [0, 0.001], + [100000000, 0.0009], + [1000000000, 0.0008], + [5000000000, 0.0007], + [10000000000, 0.0006], + [20000000000, 0.0005], + [30000000000, 0.0004], + [40000000000, 0.0003], + [50000000000, 0.0002], + ], + 'maker': [ + [0, 0.001], + [100000000, 0.0008], + [1000000000, 0.0006], + [5000000000, 0.0004], + [10000000000, 0.0002], + [20000000000, 0], + [30000000000, 0], + [40000000000, 0], + [50000000000, 0], + ], + }, + }, + }, + 'exceptions': { + '405': OnMaintenance, // {"errorCode":"405","status":"maintenance","result":"error"} + '104': OrderNotFound, + '108': BadSymbol, // {"errorCode":"108","errorMsg":"Unknown CryptoCurrency","result":"error"} + '107': BadRequest, // {"errorCode":"107","errorMsg":"Parameter error","result":"error"} + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostAccountBalance (params); + const result = { 'info': response }; + const balances = this.omit (response, [ + 'errorCode', + 'result', + 'normalWallets', + ]); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = balances[currencyId]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'avail'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'format': 'json', + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'qty'); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currency': 'all', + 'format': 'json', + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'format': 'json', + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + const previousClose = this.safeFloat (ticker, 'yesterday_last'); + let change = undefined; + if (last !== undefined && previousClose !== undefined) { + change = previousClose - last; + } + const symbol = (market !== undefined) ? market['symbol'] : undefined; + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'first'), + 'close': last, + 'last': last, + 'previousClose': previousClose, + 'change': change, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const symbol = (market !== undefined) ? market['symbol'] : undefined; + const is_ask = this.safeString (trade, 'is_ask'); + let side = undefined; + if (is_ask === '1') { + side = 'sell'; + } else if (is_ask === '0') { + side = 'buy'; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'qty'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'order': undefined, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'period': 'hour', + 'format': 'json', + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response['completeOrders'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const request = { + 'price': price, + 'currency': this.marketId (symbol), + 'qty': amount, + }; + const method = 'privatePostOrder' + this.capitalize (type) + this.capitalize (side); + const response = await this[method] (this.extend (request, params)); + let id = this.safeString (response, 'orderId'); + if (id !== undefined) { + id = id.toUpperCase (); + } + const timestamp = this.milliseconds (); + const cost = price * amount; + const order = { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': undefined, + 'amount': amount, + 'filled': undefined, + 'remaining': amount, + 'status': 'open', + 'fee': undefined, + }; + this.orders[id] = order; + return order; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let result = undefined; + let market = undefined; + if (symbol === undefined) { + if (id in this.orders) { + market = this.market (this.orders[id]['symbol']); + } else { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument for order ids missing in the .orders cache (the order was created with a different instance of this class or within a different run of this code).'); + } + } else { + market = this.market (symbol); + } + try { + const request = { + 'order_id': id, + 'currency': market['id'], + }; + const response = await this.privatePostOrderOrderInfo (this.extend (request, params)); + result = this.parseOrder (response); + this.orders[id] = result; + } catch (e) { + if (e instanceof OrderNotFound) { + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + result = this.orders[id]; + } else { + throw e; + } + } else { + throw e; + } + } + return result; + } + + parseOrderStatus (status) { + const statuses = { + 'live': 'open', + 'partially_filled': 'open', + 'filled': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const info = this.safeValue (order, 'info'); + const id = this.safeStringUpper (info, 'orderId'); + const timestamp = this.safeTimestamp (info, 'timestamp'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let cost = undefined; + let side = this.safeString (info, 'type'); + if (side.indexOf ('ask') >= 0) { + side = 'sell'; + } else { + side = 'buy'; + } + const price = this.safeFloat (info, 'price'); + const amount = this.safeFloat (info, 'qty'); + const remaining = this.safeFloat (info, 'remainQty'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + if (price !== undefined) { + cost = price * amount; + } + } + const currency = this.safeString (info, 'currency'); + const fee = { + 'currency': currency, + 'cost': this.safeFloat (info, 'fee'), + 'rate': this.safeFloat (info, 'feeRate'), + }; + let symbol = undefined; + if (market === undefined) { + const marketId = currency.toLowerCase (); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const order = this.safeValue (this.orders, id); + let amount = undefined; + let price = undefined; + let side = undefined; + if (order === undefined) { + if (symbol === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `symbol` argument is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + price = this.safeFloat (params, 'price'); + if (price === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `price` parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + amount = this.safeFloat (params, 'qty'); + if (amount === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `qty` (amount) parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + side = this.safeFloat (params, 'is_ask'); + if (side === undefined) { + // eslint-disable-next-line quotes + throw new InvalidOrder (this.id + " cancelOrder could not find the order id " + id + " in orders cache. The order was probably created with a different instance of this class earlier. The `is_ask` (side) parameter is missing. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder."); + } + } else { + price = order['price']; + amount = order['amount']; + side = (order['side'] === 'buy') ? 0 : 1; + symbol = order['symbol']; + } + const request = { + 'order_id': id, + 'price': price, + 'qty': amount, + 'is_ask': side, + 'currency': this.marketId (symbol), + }; + this.orders[id]['status'] = 'canceled'; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + '/'; + if (api === 'public') { + url += request; + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + url += this.version + '/' + request; + const nonce = this.nonce ().toString (); + const json = this.json (this.extend ({ + 'access_token': this.apiKey, + 'nonce': nonce, + }, params)); + const payload = this.stringToBase64 (this.encode (json)); + body = this.decode (payload); + const secret = this.secret.toUpperCase (); + const signature = this.hmac (payload, this.encode (secret), 'sha512'); + headers = { + 'content-type': 'application/json', + 'X-COINONE-PAYLOAD': payload, + 'X-COINONE-SIGNATURE': signature, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if ('result' in response) { + const result = response['result']; + if (result !== 'success') { + // + // { "errorCode": "405", "status": "maintenance", "result": "error"} + // + const errorCode = this.safeString (response, 'errorCode'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + throw new ExchangeError (feedback); + } + } else { + throw new ExchangeError (this.id + ' ' + body); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coinspot.js b/TaskServer/node_modules/ccxt/js/coinspot.js new file mode 100644 index 000000000..2055e5937 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coinspot.js @@ -0,0 +1,171 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coinspot extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coinspot', + 'name': 'CoinSpot', + 'countries': [ 'AU' ], // Australia + 'rateLimit': 1000, + 'has': { + 'CORS': false, + 'createMarketOrder': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28208429-3cacdf9a-6896-11e7-854e-4c79a772a30f.jpg', + 'api': { + 'public': 'https://www.coinspot.com.au/pubapi', + 'private': 'https://www.coinspot.com.au/api', + }, + 'www': 'https://www.coinspot.com.au', + 'doc': 'https://www.coinspot.com.au/api', + 'referral': 'https://www.coinspot.com.au/register?code=PJURCU', + }, + 'api': { + 'public': { + 'get': [ + 'latest', + ], + }, + 'private': { + 'post': [ + 'orders', + 'orders/history', + 'my/coin/deposit', + 'my/coin/send', + 'quote/buy', + 'quote/sell', + 'my/balances', + 'my/orders', + 'my/buy', + 'my/sell', + 'my/buy/cancel', + 'my/sell/cancel', + ], + }, + }, + 'markets': { + 'BTC/AUD': { 'id': 'btc', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'btc', 'quoteId': 'aud' }, + 'LTC/AUD': { 'id': 'ltc', 'symbol': 'LTC/AUD', 'base': 'LTC', 'quote': 'AUD', 'baseId': 'ltc', 'quoteId': 'aud' }, + 'DOGE/AUD': { 'id': 'doge', 'symbol': 'DOGE/AUD', 'base': 'DOGE', 'quote': 'AUD', 'baseId': 'doge', 'quoteId': 'aud' }, + }, + 'commonCurrencies': { + 'DRK': 'DASH', + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostMyBalances (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'balance', {}); + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balances, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cointype': market['id'], + }; + const orderbook = await this.privatePostOrders (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'buyorders', 'sellorders', 'rate', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetLatest (params); + let id = this.marketId (symbol); + id = id.toLowerCase (); + const ticker = response['prices'][id]; + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'cointype': market['id'], + }; + const response = await this.privatePostOrdersHistory (this.extend (request, params)); + const trades = this.safeValue (response, 'orders', []); + return this.parseTrades (trades, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostMy' + this.capitalize (side); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'cointype': this.marketId (symbol), + 'amount': amount, + 'rate': price, + }; + return await this[method] (this.extend (request, params)); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + throw new NotSupported (this.id + ' cancelOrder () is not fully implemented yet'); + // let method = 'privatePostMyBuy'; + // return await this[method] ({ 'id': id }); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (!this.apiKey) { + throw new AuthenticationError (this.id + ' requires apiKey for all requests'); + } + const url = this.urls['api'][api] + '/' + path; + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.json (this.extend ({ 'nonce': nonce }, params)); + headers = { + 'Content-Type': 'application/json', + 'key': this.apiKey, + 'sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coolcoin.js b/TaskServer/node_modules/ccxt/js/coolcoin.js new file mode 100644 index 000000000..fefef58c7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coolcoin.js @@ -0,0 +1,70 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const coinegg = require ('./coinegg.js'); + +// --------------------------------------------------------------------------- + +module.exports = class coolcoin extends coinegg { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coolcoin', + 'name': 'CoolCoin', + 'countries': [ 'HK' ], + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/36770529-be7b1a04-1c5b-11e8-9600-d11f1996b539.jpg', + 'api': { + 'web': 'https://www.coolcoin.com/coin', + 'rest': 'https://www.coolcoin.com/api/v1', + }, + 'www': 'https://www.coolcoin.com', + 'doc': 'https://www.coolcoin.com/help.api.html', + 'fees': 'https://www.coolcoin.com/spend.price.html', + 'referral': 'https://www.coolcoin.com/user/register?invite_code=bhaega', + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': 0.001, + 'BCH': 0.002, + 'ETH': 0.01, + 'ETC': 0.01, + 'LTC': 0.001, + 'TBC': '1%', + 'HSR': '1%', + 'NEO': '1%', + 'SDC': '1%', + 'EOS': '1%', + 'BTM': '1%', + 'XAS': '1%', + 'ACT': '1%', + 'SAK': '1%', + 'GCS': '1%', + 'HCC': '1%', + 'QTUM': '1%', + 'GEC': '1%', + 'TRX': '1%', + 'IFC': '1%', + 'PAY': '1%', + 'PGC': '1%', + 'KTC': '1%', + 'INT': '1%', + 'LSK': '0.5%', + 'SKT': '1%', + 'SSS': '1%', + 'BT1': '1%', + 'BT2': '1%', + }, + }, + }, + 'options': { + 'quoteIds': ['btc', 'usdt'], + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/coss.js b/TaskServer/node_modules/ccxt/js/coss.js new file mode 100644 index 000000000..1524d0d1f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/coss.js @@ -0,0 +1,942 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class coss extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'coss', + 'name': 'COSS', + 'countries': [ 'SG', 'NL' ], + 'rateLimit': 1000, + 'version': 'v1', + 'certified': false, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/50328158-22e53c00-0503-11e9-825c-c5cfd79bfa74.jpg', + 'api': { + 'trade': 'https://trade.coss.io/c/api/v1', + 'engine': 'https://engine.coss.io/api/v1', + 'public': 'https://trade.coss.io/c/api/v1', + 'web': 'https://trade.coss.io/c', // undocumented + 'exchange': 'https://exchange.coss.io/api', + }, + 'www': 'https://www.coss.io', + 'doc': 'https://api.coss.io/v1/spec', + 'referral': 'https://www.coss.io/c/reg?r=OWCMHQVW2Q', + }, + 'has': { + 'fetchTrades': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchMarkets': true, + 'fetchCurrencies': true, + 'fetchBalance': true, + 'fetchOrderBook': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOrderTrades': true, + 'fetchClosedOrders': true, + 'fetchOpenOrders': true, + 'fetchOHLCV': true, + 'createOrder': true, + 'cancelOrder': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '4h': '4h', + '6h': '6h', + '12h': '12h', + '1d': '1d', + '1w': '1w', + }, + 'api': { + 'exchange': { + 'get': [ + 'getmarketsummaries', + ], + }, + 'public': { + 'get': [ + 'market-price', + 'exchange-info', + ], + }, + 'web': { + 'get': [ + 'coins/getinfo/all', // undocumented + 'order/symbols', // undocumented + 'coins/get_base_list', // undocumented + ], + }, + 'engine': { + 'get': [ + 'dp', + 'ht', + 'cs', + ], + }, + 'trade': { + 'get': [ + 'ping', + 'time', + 'account/balances', + 'account/details', + ], + 'post': [ + 'order/add', + 'order/details', + 'order/list/open', + 'order/list/completed', + 'order/list/all', + 'order/trade-detail', + ], + 'delete': [ + 'order/cancel', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.0025, + 'maker': 0.0, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'commonCurrencies': { + 'COS': 'COSS', + 'COSS': 'COSS.io', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeInfo (params); + // + // { timezone: "UTC", + // server_time: 1545171487108, + // rate_limits: [ { type: "REQUESTS", + // interval: "MINUTE", + // limit: 1000 } ], + // base_currencies: [ { currency_code: "BTC", minimum_total_order: "0.0001" }, + // { currency_code: "USDT", minimum_total_order: "1" }, + // { currency_code: "EUR", minimum_total_order: "1" } ], + // coins: [ { currency_code: "ADI", + // name: "Aditus", + // minimum_order_amount: "0.00000001" }, + // ... + // { currency_code: "NPXSXEM", + // name: "PundiX-XEM", + // minimum_order_amount: "0.00000001" } ], + // symbols: [ { symbol: "ADI_BTC", + // amount_limit_decimal: 0, + // price_limit_decimal: 8, + // allow_trading: true }, + // ... + // { symbol: "ETH_GUSD", + // amount_limit_decimal: 5, + // price_limit_decimal: 3, + // allow_trading: true } ] } + // + const result = []; + const markets = this.safeValue (response, 'symbols', []); + const baseCurrencies = this.safeValue (response, 'base_currencies', []); + const baseCurrenciesByIds = this.indexBy (baseCurrencies, 'currency_code'); + const currencies = this.safeValue (response, 'coins', []); + const currenciesByIds = this.indexBy (currencies, 'currency_code'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const marketId = market['symbol']; + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'amount_limit_decimal'), + 'price': this.safeInteger (market, 'price_limit_decimal'), + }; + const active = this.safeValue (market, 'allow_trading', false); + const baseCurrency = this.safeValue (baseCurrenciesByIds, baseId, {}); + const minCost = this.safeFloat (baseCurrency, 'minimum_total_order'); + const currency = this.safeValue (currenciesByIds, baseId, {}); + const defaultMinAmount = Math.pow (10, -precision['amount']); + const minAmount = this.safeFloat (currency, 'minimum_order_amount', defaultMinAmount); + result.push ({ + 'symbol': symbol, + 'id': marketId, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.webGetCoinsGetinfoAll (params); + // + // [ { currency_code: "VET", + // name: "VeChain", + // buy_limit: 0, + // sell_limit: 0, + // usdt: 0, + // transaction_time_limit: 5, + // status: "trade", + // withdrawn_fee: "0.6", + // minimum_withdrawn_amount: "1.2", + // minimum_deposit_amount: "0.6", + // minimum_order_amount: "0.00000001", + // decimal_format: "0.########", + // token_type: null, // "erc", "eos", "stellar", "tron", "ripple"... + // buy_at: 0, + // sell_at: 0, + // min_rate: 0, + // max_rate: 0, + // allow_withdrawn: false, + // allow_deposit: false, + // explorer_website_mainnet_link: null, + // explorer_website_testnet_link: null, + // deposit_block_confirmation: "6", + // withdraw_block_confirmation: "0", + // icon_url: "https://s2.coinmarketcap.com/static/img/coins/32x32/3077.png", + // is_fiat: false, + // allow_sell: true, + // allow_buy: true }] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const currencyId = this.safeString (currency, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const name = this.safeString (currency, 'name'); + const allowBuy = this.safeValue (currency, 'allow_buy'); + const allowSell = this.safeValue (currency, 'allow_sell'); + const allowWithdrawals = this.safeValue (currency, 'allow_withdrawn'); + const allowDeposits = this.safeValue (currency, 'allow_deposit'); + const active = allowBuy && allowSell && allowWithdrawals && allowDeposits; + const fee = this.safeFloat (currency, 'withdrawn_fee'); + const type = this.safeString (currency, 'token_type'); + // + // decimal_format can be anything... + // + // 0.######## + // #.######## + // 0.## + // '' (empty string) + // 0.000000 + // null (undefined) + // 0.0000 + // 0.### + // + const decimalFormat = this.safeString (currency, 'decimal_format'); + let precision = 8; + if (decimalFormat !== undefined) { + const parts = decimalFormat.split ('.'); + const numParts = parts.length; // transpiler workaround for array lengths + if (numParts > 1) { + if (parts[1].length > 1) { + precision = parts[1].length; + } + } + } + result[code] = { + 'id': currencyId, + 'code': code, + 'info': currency, + 'name': name, + 'active': active, + 'fee': fee, + 'precision': precision, + 'type': type, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minimum_order_amount'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimum_withdrawn_amount'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.tradeGetAccountBalances (params); + // + // [ { currency_code: "ETH", + // address: "0x6820511d43111a941d3e187b9e36ec64af763bde", // deposit address + // total: "0.20399125", + // available: "0.20399125", + // in_order: "0", + // memo: null }, // tag, if any + // { currency_code: "ICX", + // address: "", + // total: "0", + // available: "0", + // in_order: "0", + // memo: null } ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const total = this.safeFloat (balance, 'total'); + const used = this.safeFloat (balance, 'in_order'); + const free = this.safeFloat (balance, 'available'); + result[code] = { + 'total': total, + 'used': used, + 'free': free, + }; + } + return this.parseBalance (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + parseInt (ohlcv[0]), // timestamp + parseFloat (ohlcv[1]), // Open + parseFloat (ohlcv[2]), // High + parseFloat (ohlcv[3]), // Low + parseFloat (ohlcv[4]), // Close + parseFloat (ohlcv[5]), // base Volume + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'tt': this.timeframes[timeframe], + }; + const response = await this.engineGetCs (this.extend (request, params)); + // + // { tt: "1m", + // symbol: "ETH_BTC", + // nextTime: 1545138960000, + // series: [ [ 1545138960000, + // "0.02705000", + // "0.02705000", + // "0.02705000", + // "0.02705000", + // "0.00000000" ], + // ... + // [ 1545168900000, + // "0.02684000", + // "0.02684000", + // "0.02684000", + // "0.02684000", + // "0.00000000" ] ], + // limit: 500 } + // + return this.parseOHLCVs (response['series'], market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { 'symbol': marketId }; + // limit argument is not supported on COSS's end + const response = await this.engineGetDp (this.extend (request, params)); + // + // { symbol: "COSS_ETH", + // asks: [ ["0.00065200", "214.15000000"], + // ["0.00065300", "645.45000000"], + // ... + // ["0.00076400", "380.00000000"], + // ["0.00076900", "25.00000000"] ], + // limit: 100, + // bids: [ ["0.00065100", "666.99000000"], + // ["0.00065000", "1171.93000000"], + // ... + // ["0.00037700", "3300.00000000"], + // ["0.00037600", "2010.82000000"] ], + // time: 1545180569354 } + // + const timestamp = this.safeInteger (response, 'time'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + // + // { MarketName: "COSS-ETH", + // High: 0.00066, + // Low: 0.000628, + // BaseVolume: 131.09652674, + // Last: 0.000636, + // TimeStamp: "2018-12-19T05:16:41.369Z", + // Volume: 206126.6143710692, + // Ask: "0.00063600", + // Bid: "0.00063400", + // PrevDay: 0.000636 } + // + const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp')); + let symbol = undefined; + let marketId = this.safeString (ticker, 'MarketName'); + if (marketId !== undefined) { + marketId = marketId.replace ('-', '_'); + } + market = this.safeValue (this.markets_by_id, marketId, market); + if (market === undefined) { + if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const previous = this.safeFloat (ticker, 'PrevDay'); + const last = this.safeFloat (ticker, 'Last'); + let change = undefined; + let percentage = undefined; + if (last !== undefined) { + if (previous !== undefined) { + change = last - previous; + if (previous > 0) { + percentage = (change / previous) * 100; + } + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'High'), + 'low': this.safeFloat (ticker, 'Low'), + 'bid': this.safeFloat (ticker, 'Bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'Ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': previous, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume'), + 'quoteVolume': this.safeFloat (ticker, 'BaseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.exchangeGetGetmarketsummaries (params); + // + // { success: true, + // message: "", + // result: [ { MarketName: "COSS-ETH", + // High: 0.00066, + // Low: 0.000628, + // BaseVolume: 131.09652674, + // Last: 0.000636, + // TimeStamp: "2018-12-19T05:16:41.369Z", + // Volume: 206126.6143710692, + // Ask: "0.00063600", + // Bid: "0.00063400", + // PrevDay: 0.000636 }, + // ... + // { MarketName: "XLM-BTC", + // High: 0.0000309, + // Low: 0.0000309, + // BaseVolume: 0, + // Last: 0.0000309, + // TimeStamp: "2018-12-19T02:00:02.145Z", + // Volume: 0, + // Ask: "0.00003300", + // Bid: "0.00003090", + // PrevDay: 0.0000309 } ], + // volumes: [ { CoinName: "ETH", Volume: 668.1928095999999 }, // these are overall exchange volumes + // { CoinName: "USD", Volume: 9942.58480324 }, + // { CoinName: "BTC", Volume: 43.749184570000004 }, + // { CoinName: "COSS", Volume: 909909.26644574 }, + // { CoinName: "EUR", Volume: 0 }, + // { CoinName: "TUSD", Volume: 2613.3395026999997 }, + // { CoinName: "USDT", Volume: 1017152.07416519 }, + // { CoinName: "GUSD", Volume: 1.80438 }, + // { CoinName: "XRP", Volume: 15.95508 }, + // { CoinName: "GBP", Volume: 0 }, + // { CoinName: "USDC", Volume: 0 } ], + // t: 1545196604371 } + // + const tickers = this.safeValue (response, 'result', []); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.engineGetHt (this.extend (request, params)); + // + // { symbol: "COSS_ETH", + // limit: 100, + // history: [ { id: 481321, + // price: "0.00065100", + // qty: "272.92000000", + // isBuyerMaker: false, + // time: 1545180845019 }, + // { id: 481322, + // price: "0.00065200", + // qty: "1.90000000", + // isBuyerMaker: true, + // time: 1545180847535 }, + // ... + // { id: 481420, + // price: "0.00065300", + // qty: "2.00000000", + // isBuyerMaker: true, + // time: 1545181167702 } ], + // time: 1545181171274 } + // + return this.parseTrades (response['history'], market, since, limit); + } + + parseTradeFee (fee) { + if (fee === undefined) { + return fee; + } + const parts = fee.split (' '); + const numParts = parts.length; + const cost = parts[0]; + let code = undefined; + if (numParts > 1) { + code = this.safeCurrencyCode (parts[1]); + } + return { + 'cost': cost, + 'currency': code, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { id: 481322, + // price: "0.00065200", + // qty: "1.90000000", + // isBuyerMaker: true, + // time: 1545180847535 } + // + // fetchOrderTrades (private) + // + // [ { hex_id: null, + // symbol: "COSS_ETH", + // order_id: "ad6f6b47-3def-4add-a5d5-2549a9df1593", + // order_side: "BUY", + // price: "0.00065900", + // quantity: "10", + // fee: "0.00700000 COSS", + // additional_fee: "0.00000461 ETH", + // total: "0.00659000 ETH", + // timestamp: 1545152356075 } ] + // + const id = this.safeString (trade, 'id'); + const timestamp = this.safeInteger (trade, 'time'); + const orderId = this.safeString (trade, 'order_id'); + const side = this.safeStringLower (trade, 'order_side'); + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + market = this.safeValue (this.markets_by_id, marketId, market); + if (market === undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } else if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'qty', 'quantity'); + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + const result = { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + const fee = this.parseTradeFee (this.safeString (trade, 'fee')); + if (fee !== undefined) { + const additionalFee = this.parseTradeFee (this.safeString (trade, 'additional_fee')); + if (additionalFee === undefined) { + result['fee'] = fee; + } else { + result['fees'] = [ + fee, + additionalFee, + ]; + } + } + return result; + } + + async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + // 'from_id': 'b2a2d379-f9b6-418b-9414-cbf8330b20d1', // string (uuid), fetchOrders (all orders) only + // 'page': 0, // different pagination in fetchOpenOrders and fetchClosedOrders + // 'limit': 50, // optional, max = default = 50 + 'symbol': market['id'], // required + }; + if (limit !== undefined) { + request['limit'] = limit; // max = default = 50 + } + const method = 'tradePostOrderList' + type; + const response = await this[method] (this.extend (request, params)); + // + // fetchOrders, fetchClosedOrders + // + // [ { hex_id: "5c192784330fe51149f556bb", + // order_id: "5e46e1b1-93d5-4656-9b43-a5635b08eae9", + // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", + // order_symbol: "COSS_ETH", + // order_side: "BUY", + // status: "filled", + // createTime: 1545152388019, + // type: "limit", + // timeMatching: 0, + // order_price: "0.00065900", + // order_size: "10", + // executed: "10", + // stop_price: "0.00000000", + // avg: "0.00065900", + // total: "0.00659000 ETH" } ] + // + // fetchOpenOrders + // + // { + // "total": 2, + // "list": [ + // { + // "order_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4ty", + // "account_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4a6", + // "order_symbol": "eth-btc", + // "order_side": "BUY", + // "status": "OPEN", + // "createTime": 1538114348750, + // "type": "limit", + // "order_price": "0.12345678", + // "order_size": "10.12345678", + // "executed": "0", + // "stop_price": "02.12345678", + // "avg": "1.12345678", + // "total": "2.12345678" + // } + // ] + // } + // + // the following code is to handle the above difference in response formats + let orders = undefined; + if (Array.isArray (response)) { + orders = response; + } else { + orders = this.safeValue (response, 'list', []); + } + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('All', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('Completed', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByType ('Open', symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.tradePostOrderDetails (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'order_id': id, + }; + const response = await this.tradePostOrderTradeDetail (this.extend (request, params)); + // + // [ { hex_id: null, + // symbol: "COSS_ETH", + // order_id: "ad6f6b47-3def-4add-a5d5-2549a9df1593", + // order_side: "BUY", + // price: "0.00065900", + // quantity: "10", + // fee: "0.00700000 COSS", + // additional_fee: "0.00000461 ETH", + // total: "0.00659000 ETH", + // timestamp: 1545152356075 } ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOrderStatus (status) { + if (status === undefined) { + return status; + } + const statuses = { + 'OPEN': 'open', + 'CANCELLED': 'canceled', + 'FILLED': 'closed', + 'PARTIAL_FILL': 'closed', + 'CANCELLING': 'open', + }; + return this.safeString (statuses, status.toUpperCase (), status); + } + + parseOrder (order, market = undefined) { + // + // { hex_id: "5c192784330fe51149f556bb", // missing in fetchOpenOrders + // order_id: "5e46e1b1-93d5-4656-9b43-a5635b08eae9", + // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", + // order_symbol: "COSS_ETH", // coss-eth in docs + // order_side: "BUY", + // status: "filled", + // createTime: 1545152388019, + // type: "limit", + // timeMatching: 0, // missing in fetchOpenOrders + // order_price: "0.00065900", + // order_size: "10", + // executed: "10", + // stop_price: "0.00000000", + // avg: "0.00065900", + // total: "0.00659000 ETH" } + // + const id = this.safeString (order, 'order_id'); + let symbol = undefined; + let marketId = this.safeString (order, 'order_symbol'); + if (marketId === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } else { + // a minor workaround for lowercase eth-btc symbols + marketId = marketId.toUpperCase (); + marketId = marketId.replace ('-', '_'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market === undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else { + symbol = market['symbol']; + } + } + const timestamp = this.safeInteger (order, 'createTime'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'order_price'); + const filled = this.safeFloat (order, 'executed'); + const type = this.safeString (order, 'type'); + const amount = this.safeFloat (order, 'order_size'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const average = this.safeFloat (order, 'avg'); + const side = this.safeStringLower (order, 'order_side'); + const cost = this.safeFloat (order, 'total'); + const fee = undefined; + const trades = undefined; + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_symbol': market['id'], + 'order_size': this.amountToPrecision (symbol, amount), + 'order_side': side.toUpperCase (), + 'type': type, + }; + if (price !== undefined) { + request['order_price'] = this.priceToPrecision (symbol, price); + } + const response = await this.tradePostOrderAdd (this.extend (request, params)); + // + // { + // "order_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4ty", + // "account_id": "9e5ae4dd-3369-401d-81f5-dff985e1c4a6", + // "order_symbol": "eth-btc", + // "order_side": "BUY", + // "status": "OPEN", + // "createTime": 1538114348750, + // "type": "limit", + // "order_price": "0.12345678", + // "order_size": "10.12345678", + // "executed": "0", + // "stop_price": "02.12345678", + // "avg": "1.12345678", + // "total": "2.12345678" + // } + // + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'order_symbol': market['id'], + }; + const response = await this.tradeDeleteOrderCancel (this.extend (request, params)); + // + // { order_symbol: "COSS_ETH", + // order_id: "30f2d698-39a0-4b9f-a3a6-a179542373bd", + // order_size: 0, + // account_id: "a0c20128-b9e0-484e-9bc8-b8bb86340e5b", + // timestamp: 1545202728814, + // recvWindow: null } + // + return this.parseOrder (response); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'trade') { + this.checkRequiredCredentials (); + const timestamp = this.nonce (); + const query = this.extend ({ + 'timestamp': timestamp, // required (int64) + // 'recvWindow': 10000, // optional (int32) + }, params); + let request = undefined; + if (method === 'GET') { + request = this.urlencode (query); + url += '?' + request; + } else { + request = this.json (query); + body = request; + } + headers = { + 'Signature': this.hmac (this.encode (request), this.encode (this.secret)), + 'Authorization': this.apiKey, + 'X-Requested-With': 'XMLHttpRequest', + }; + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/crex24.js b/TaskServer/node_modules/ccxt/js/crex24.js new file mode 100644 index 000000000..93f89a92a --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/crex24.js @@ -0,0 +1,1300 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest, InvalidNonce, RequestTimeout, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, AuthenticationError, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class crex24 extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'crex24', + 'name': 'CREX24', + 'countries': [ 'EE' ], // Estonia + 'rateLimit': 500, + 'version': 'v2', + // new metainfo interface + 'has': { + 'cancelAllOrders': true, + 'CORS': false, + 'editOrder': true, + 'fetchBidsAsks': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOrderTrades': true, + 'fetchTickers': true, + 'fetchTradingFee': false, // actually, true, but will be implemented later + 'fetchTradingFees': false, // actually, true, but will be implemented later + 'fetchTransactions': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1m', + '3m': '3m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '4h': '4h', + '1d': '1d', + '1w': '1w', + '1M': '1mo', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/47813922-6f12cc00-dd5d-11e8-97c6-70f957712d47.jpg', + 'api': 'https://api.crex24.com', + 'www': 'https://crex24.com', + 'referral': 'https://crex24.com/?refid=slxsjsjtil8xexl9hksr', + 'doc': 'https://docs.crex24.com/trade-api/v2', + 'fees': 'https://crex24.com/fees', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'instruments', + 'tickers', + 'recentTrades', + 'orderBook', + 'ohlcv', + ], + }, + 'trading': { + 'get': [ + 'orderStatus', + 'orderTrades', + 'activeOrders', + 'orderHistory', + 'tradeHistory', + 'tradeFee', + // this is in trading API according to their docs, but most likely a typo in their docs + 'moneyTransferStatus', + ], + 'post': [ + 'placeOrder', + 'modifyOrder', + 'cancelOrdersById', + 'cancelOrdersByInstrument', + 'cancelAllOrders', + ], + }, + 'account': { + 'get': [ + 'balance', + 'depositAddress', + 'moneyTransfers', + // this is in trading API according to their docs, but most likely a typo in their docs + 'moneyTransferStatus', + 'previewWithdrawal', + ], + 'post': [ + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.001, + 'maker': -0.0001, + }, + // should be deleted, these are outdated and inaccurate + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'commonCurrencies': { + 'YOYO': 'YOYOW', + 'BCC': 'BCH', + }, + // exchange-specific options + 'options': { + 'fetchOrdersMethod': 'tradingGetOrderHistory', // or 'tradingGetActiveOrders' + 'fetchClosedOrdersMethod': 'tradingGetOrderHistory', // or 'tradingGetActiveOrders' + 'fetchTickersMethod': 'publicGetTicker24hr', + 'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel + 'defaultLimitOrderType': 'limit', // or 'limit_maker' + 'hasAlreadyAuthenticatedSuccessfully': false, + 'warnOnFetchOpenOrdersWithoutSymbol': true, + 'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision + 'newOrderRespType': 'RESULT', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills + }, + 'exceptions': { + 'exact': { + "Parameter 'filter' contains invalid value.": BadRequest, // eslint-disable-quotes + "Mandatory parameter 'instrument' is missing.": BadRequest, // eslint-disable-quotes + "The value of parameter 'till' must be greater than or equal to the value of parameter 'from'.": BadRequest, // eslint-disable-quotes + 'Failed to verify request signature.': AuthenticationError, // eslint-disable-quotes + "Nonce error. Make sure that the value passed in the 'X-CREX24-API-NONCE' header is greater in each consecutive request than in the previous one for the corresponding API-Key provided in 'X-CREX24-API-KEY' header.": InvalidNonce, + 'Market orders are not supported by the instrument currently.': InvalidOrder, + "Parameter 'instrument' contains invalid value.": BadSymbol, + }, + 'broad': { + 'API Key': AuthenticationError, // "API Key '9edc48de-d5b0-4248-8e7e-f59ffcd1c7f1' doesn't exist." + 'Insufficient funds': InsufficientFunds, // "Insufficient funds: new order requires 10 ETH which is more than the available balance." + 'has been delisted.': BadSymbol, // {"errorDescription":"Instrument '$PAC-BTC' has been delisted."} + }, + }, + }); + } + + nonce () { + return this.milliseconds (); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInstruments (params); + // + // [ { symbol: "$PAC-BTC", + // baseCurrency: "$PAC", + // quoteCurrency: "BTC", + // feeCurrency: "BTC", + // tickSize: 1e-8, + // minPrice: 1e-8, + // minVolume: 1, + // supportedOrderTypes: ["limit"], + // state: "active" }, + // { symbol: "ZZC-USD", + // baseCurrency: "ZZC", + // quoteCurrency: "USD", + // feeCurrency: "USD", + // tickSize: 0.0001, + // minPrice: 0.0001, + // minVolume: 1, + // supportedOrderTypes: ["limit"], + // state: "active" } ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const tickSize = this.safeValue (market, 'tickSize'); + const minPrice = this.safeValue (market, 'minPrice'); + const minAmount = this.safeFloat (market, 'minVolume'); + const precision = { + 'amount': this.precisionFromString (this.numberToString (minAmount)), + 'price': this.precisionFromString (this.numberToString (tickSize)), + }; + const active = (market['state'] === 'active'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // [ { symbol: "$PAC", + // name: "PACCoin", + // isFiat: false, + // depositsAllowed: true, + // depositConfirmationCount: 8, + // minDeposit: 0, + // withdrawalsAllowed: true, + // withdrawalPrecision: 8, + // minWithdrawal: 4, + // maxWithdrawal: 1000000000, + // flatWithdrawalFee: 2, + // isDelisted: false }, + // { symbol: "ZZC", + // name: "Zozo", + // isFiat: false, + // depositsAllowed: false, + // depositConfirmationCount: 8, + // minDeposit: 0, + // withdrawalsAllowed: false, + // withdrawalPrecision: 8, + // minWithdrawal: 0.2, + // maxWithdrawal: 1000000000, + // flatWithdrawalFee: 0.1, + // isDelisted: false } ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'withdrawalPrecision'); + const address = this.safeValue (currency, 'BaseAddress'); + const active = (currency['depositsAllowed'] && currency['withdrawalsAllowed'] && !currency['isDelisted']); + const type = currency['isFiat'] ? 'fiat' : 'crypto'; + result[code] = { + 'id': id, + 'code': code, + 'address': address, + 'info': currency, + 'type': type, + 'name': this.safeString (currency, 'name'), + 'active': active, + 'fee': this.safeFloat (currency, 'flatWithdrawalFee'), // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'deposit': { + 'min': this.safeFloat (currency, 'minDeposit'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawal'), + 'max': this.safeFloat (currency, 'maxWithdrawal'), + }, + }, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + // 'currency': 'ETH', // comma-separated list of currency ids + // 'nonZeroOnly': 'false', // true by default + }; + const response = await this.accountGetBalance (this.extend (request, params)); + // + // [ + // { + // "currency": "ETH", + // "available": 0.0, + // "reserved": 0.0 + // } + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = maximum = 100 + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { buyLevels: [ { price: 0.03099, volume: 0.00610063 }, + // { price: 0.03097, volume: 1.33455158 }, + // { price: 0.03096, volume: 0.0830889 }, + // { price: 0.03095, volume: 0.0820356 }, + // { price: 0.03093, volume: 0.5499419 }, + // { price: 0.03092, volume: 0.23317494 }, + // { price: 0.03091, volume: 0.62105322 }, + // { price: 0.00620041, volume: 0.003 } ], + // sellLevels: [ { price: 0.03117, volume: 5.47492315 }, + // { price: 0.03118, volume: 1.97744139 }, + // { price: 0.03119, volume: 0.012 }, + // { price: 0.03121, volume: 0.741242 }, + // { price: 0.03122, volume: 0.96178089 }, + // { price: 0.03123, volume: 0.152326 }, + // { price: 0.03124, volume: 2.63462933 }, + // { price: 0.069, volume: 0.004 } ] } + // + return this.parseOrderBook (response, undefined, 'buyLevels', 'sellLevels', 'price', 'volume'); + } + + parseTicker (ticker, market = undefined) { + // + // { instrument: "ZZC-USD", + // last: 0.065, + // percentChange: 0, + // low: 0.065, + // high: 0.065, + // baseVolume: 0, + // quoteVolume: 0, + // volumeInBtc: 0, + // volumeInUsd: 0, + // ask: 0.5, + // bid: 0.0007, + // timestamp: "2018-10-31T09:21:25Z" } ] + // + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + let symbol = undefined; + const marketId = this.safeString (ticker, 'instrument'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } else if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, // previous day close + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'percentChange'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'baseVolume'), + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ { instrument: "$PAC-BTC", + // last: 3.3e-7, + // percentChange: 3.125, + // low: 2.7e-7, + // high: 3.3e-7, + // baseVolume: 191700.79823187, + // quoteVolume: 0.0587930939346704, + // volumeInBtc: 0.0587930939346704, + // volumeInUsd: 376.2006339435353, + // ask: 3.3e-7, + // bid: 3.1e-7, + // timestamp: "2018-10-31T09:21:25Z" } ] + // + const numTickers = response.length; + if (numTickers < 1) { + throw new ExchangeError (this.id + ' fetchTicker could not load quotes for symbol ' + symbol); + } + return this.parseTicker (response[0], market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (symbols !== undefined) { + const ids = this.marketIds (symbols); + request['instrument'] = ids.join (','); + } + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ { instrument: "$PAC-BTC", + // last: 3.3e-7, + // percentChange: 3.125, + // low: 2.7e-7, + // high: 3.3e-7, + // baseVolume: 191700.79823187, + // quoteVolume: 0.0587930939346704, + // volumeInBtc: 0.0587930939346704, + // volumeInUsd: 376.2006339435353, + // ask: 3.3e-7, + // bid: 3.1e-7, + // timestamp: "2018-10-31T09:21:25Z" }, + // { instrument: "ZZC-USD", + // last: 0.065, + // percentChange: 0, + // low: 0.065, + // high: 0.065, + // baseVolume: 0, + // quoteVolume: 0, + // volumeInBtc: 0, + // volumeInUsd: 0, + // ask: 0.5, + // bid: 0.0007, + // timestamp: "2018-10-31T09:21:25Z" } ] + // + return this.parseTickers (response, symbols); + } + + parseTickers (tickers, symbols = undefined) { + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.filterByArray (result, 'symbol', symbols); + } + + parseTrade (trade, market = undefined) { + // + // public fetchTrades + // + // { price: 0.03105, + // volume: 0.11, + // side: "sell", + // timestamp: "2018-10-31T04:19:35Z" } ] + // + // private fetchMyTrades + // + // { + // "id": 3005866, + // "orderId": 468533093, + // "timestamp": "2018-06-02T16:26:27Z", + // "instrument": "BCH-ETH", + // "side": "buy", + // "price": 1.78882, + // "volume": 0.027, + // "fee": 0.0000483, + // "feeCurrency": "ETH" + // } + // + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const id = this.safeString (trade, 'id'); + const side = this.safeString (trade, 'side'); + const orderId = this.safeString (trade, 'orderId'); + let symbol = undefined; + const marketId = this.safeString (trade, 'instrument'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } + let fee = undefined; + const feeCurrencyId = this.safeString (trade, 'feeCurrency'); + const feeCode = this.safeCurrencyCode (feeCurrencyId); + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCode, + }; + } + const takerOrMaker = undefined; + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // min 1, max 1000, default 100 + } + const response = await this.publicGetRecentTrades (this.extend (request, params)); + // + // [ { price: 0.03117, + // volume: 0.02597403, + // side: "buy", + // timestamp: "2018-10-31T09:37:46Z" }, + // { price: 0.03105, + // volume: 0.11, + // side: "sell", + // timestamp: "2018-10-31T04:19:35Z" } ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // { timestamp: '2019-09-21T10:36:00Z', + // open: 0.02152, + // high: 0.02156, + // low: 0.02152, + // close: 0.02156, + // volume: 0.01741259 } + const date = this.safeString (ohlcv, 'timestamp'); + const timestamp = this.parse8601 (date); + return [ + timestamp, + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'granularity': this.timeframes[timeframe], + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // Accepted values: 1 - 1000. If the parameter is not specified, the number of results is limited to 100 + } + const response = await this.publicGetOhlcv (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'submitting': 'open', // A newly created limit order has a status "submitting" until it has been processed. + // This status changes during the lifetime of an order and can have different values depending on the value of the parameter Time In Force. + 'unfilledActive': 'open', // order is active, no trades have been made + 'partiallyFilledActive': 'open', // part of the order has been filled, the other part is active + 'filled': 'closed', // order has been filled entirely + 'partiallyFilledCancelled': 'canceled', // part of the order has been filled, the other part has been cancelled either by the trader or by the system (see the value of cancellationReason of an Order for more details on the reason of cancellation) + 'unfilledCancelled': 'canceled', // order has been cancelled, no trades have taken place (see the value of cancellationReason of an Order for more details on the reason of cancellation) + }; + return (status in statuses) ? statuses[status] : status; + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "id": 469594855, + // "timestamp": "2018-06-08T16:59:44Z", + // "instrument": "BTS-BTC", + // "side": "buy", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 4.0, + // "price": 0.000025, + // "stopPrice": null, + // "remainingVolume": 4.0, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + const marketId = this.safeString (order, 'instrument'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); + let price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + const remaining = this.safeFloat (order, 'remainingVolume'); + let filled = undefined; + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'lastUpdate')); + let cost = undefined; + if (remaining !== undefined) { + if (amount !== undefined) { + filled = amount - remaining; + if (this.options['parseOrderToPrecision']) { + filled = parseFloat (this.amountToPrecision (symbol, filled)); + } + filled = Math.max (filled, 0.0); + if (price !== undefined) { + cost = price * filled; + } + } + } + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + } + } + const side = this.safeString (order, 'side'); + const fee = undefined; + const trades = undefined; + let average = undefined; + if (cost !== undefined) { + if (filled) { + average = cost / filled; + } + if (this.options['parseOrderToPrecision']) { + cost = parseFloat (this.costToPrecision (symbol, cost)); + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + 'volume': this.amountToPrecision (symbol, amount), + // The value must comply with the list of order types supported by the instrument (see the value of parameter supportedOrderTypes of the Instrument) + // If the parameter is not specified, the default value "limit" is used + // More about order types in the corresponding section of documentation + 'type': type, // 'limit', 'market', 'stopLimit', in fact as of 2018-10-31, only 'limit' orders are supported for all markets + 'side': side, // 'buy' or 'sell' + // "GTC" - Good-Til-Cancelled + // "IOC" - Immediate-Or-Cancel (currently not supported by the exchange API, reserved for future use) + // "FOK" - Fill-Or-Kill (currently not supported by the exchange API, reserved for future use) + // 'timeInForce': 'GTC', // IOC', 'FOK' + // 'strictValidation': false, // false - prices will be rounded to meet the requirement, true - execution of the method will be aborted and an error message will be returned + }; + let priceIsRequired = false; + let stopPriceIsRequired = false; + if (type === 'limit') { + priceIsRequired = true; + } else if (type === 'stopLimit') { + priceIsRequired = true; + stopPriceIsRequired = true; + } + if (priceIsRequired) { + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order'); + } + request['price'] = this.priceToPrecision (symbol, price); + } + if (stopPriceIsRequired) { + const stopPrice = this.safeFloat (params, 'stopPrice'); + if (stopPrice === undefined) { + throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order'); + } else { + request['stopPrice'] = this.priceToPrecision (symbol, stopPrice); + } + } + const response = await this.tradingPostPlaceOrder (this.extend (request, params)); + // + // { + // "id": 469594855, + // "timestamp": "2018-06-08T16:59:44Z", + // "instrument": "BTS-BTC", + // "side": "buy", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 4.0, + // "price": 0.000025, + // "stopPrice": null, + // "remainingVolume": 4.0, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // + return this.parseOrder (response, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.tradingGetOrderStatus (this.extend (request, params)); + // + // [ + // { + // "id": 466747915, + // "timestamp": "2018-05-26T06:43:49Z", + // "instrument": "UNI-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 5700.0, + // "price": 0.000005, + // "stopPrice": null, + // "remainingVolume": 1.948051948052, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // ] + // + const numOrders = response.length; + if (numOrders < 1) { + throw new OrderNotFound (this.id + ' fetchOrder could not fetch order id ' + id); + } + return this.parseOrder (response[0]); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['from'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (symbol !== undefined) { + const market = this.market (symbol); + request['instrument'] = market['id']; + } + const method = this.safeString (this.options, 'fetchOrdersMethod', 'tradingGetOrderHistory'); + const response = await this[method] (this.extend (request, params)); + // + // [ + // { + // "id": 468535711, + // "timestamp": "2018-06-02T16:42:40Z", + // "instrument": "BTC-EUR", + // "side": "sell", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 0.00770733, + // "price": 6724.9, + // "stopPrice": null, + // "remainingVolume": 0.00770733, + // "lastUpdate": "2018-06-02T16:42:40Z", + // "parentOrderId": null, + // "childOrderId": null + // } + // ] + // + return this.parseOrders (response); + } + + async fetchOrdersByIds (ids = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': ids.join (','), + }; + const response = await this.tradingGetOrderStatus (this.extend (request, params)); + // + // [ + // { + // "id": 466747915, + // "timestamp": "2018-05-26T06:43:49Z", + // "instrument": "UNI-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 5700.0, + // "price": 0.000005, + // "stopPrice": null, + // "remainingVolume": 1.948051948052, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // } + // ] + // + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['instrument'] = market['id']; + } + const response = await this.tradingGetActiveOrders (this.extend (request, params)); + // + // [ + // { + // "id": 466747915, + // "timestamp": "2018-05-26T06:43:49Z", + // "instrument": "UNI-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 5700.0, + // "price": 0.000005, + // "stopPrice": null, + // "remainingVolume": 1.948051948052, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // { + // "id": 466748077, + // "timestamp": "2018-05-26T06:45:29Z", + // "instrument": "PRJ-BTC", + // "side": "sell", + // "type": "limit", + // "status": "partiallyFilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 10000.0, + // "price": 0.0000007, + // "stopPrice": null, + // "remainingVolume": 9975.0, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // ... + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['instrument'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['limit'] = limit; // min 1, max 1000, default 100 + } + const method = this.safeString (this.options, 'fetchClosedOrdersMethod', 'tradingGetOrderHistory'); + const response = await this[method] (this.extend (request, params)); + // [ + // { + // "id": 468535711, + // "timestamp": "2018-06-02T16:42:40Z", + // "instrument": "BTC-EUR", + // "side": "sell", + // "type": "limit", + // "status": "submitting", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 0.00770733, + // "price": 6724.9, + // "stopPrice": null, + // "remainingVolume": 0.00770733, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // { + // "id": 468535707, + // "timestamp": "2018-06-02T16:42:37Z", + // "instrument": "BTG-BTC", + // "side": "buy", + // "type": "limit", + // "status": "unfilledActive", + // "cancellationReason": null, + // "timeInForce": "GTC", + // "volume": 0.0173737, + // "price": 0.00589027, + // "stopPrice": null, + // "remainingVolume": 0.0173737, + // "lastUpdate": null, + // "parentOrderId": null, + // "childOrderId": null + // }, + // ... + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'ids': [ + parseInt (id), + ], + }; + const response = await this.tradingPostCancelOrdersById (this.extend (request, params)); + // + // [ + // 465448358, + // 468364313 + // ] + // + return this.parseOrder (response); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + const response = await this.tradingPostCancelAllOrders (params); + // + // [ + // 465448358, + // 468364313 + // ] + // + return response; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['instrument'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['limit'] = limit; // min 1, max 1000, default 100 + } + const response = await this.tradingGetTradeHistory (this.extend (request, params)); + // + // [ + // { + // "id": 3005866, + // "orderId": 468533093, + // "timestamp": "2018-06-02T16:26:27Z", + // "instrument": "BCH-ETH", + // "side": "buy", + // "price": 1.78882, + // "volume": 0.027, + // "fee": 0.0000483, + // "feeCurrency": "ETH" + // }, + // { + // "id": 3005812, + // "orderId": 468515771, + // "timestamp": "2018-06-02T16:16:05Z", + // "instrument": "ETC-BTC", + // "side": "sell", + // "price": 0.00210958, + // "volume": 0.05994006, + // "fee": -0.000000063224, + // "feeCurrency": "BTC" + // }, + // ... + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['from'] = this.ymd (since, 'T'); + } + const response = await this.accountGetMoneyTransfers (this.extend (request, params)); + // + // [ + // { + // "id": 756446, + // "type": "deposit", + // "currency": "ETH", + // "address": "0x451d5a1b7519aa75164f440df78c74aac96023fe", + // "paymentId": null, + // "amount": 0.142, + // "fee": null, + // "txId": "0x2b49098749840a9482c4894be94f94864b498a1306b6874687a5640cc9871918", + // "createdAt": "2018-06-02T19:30:28Z", + // "processedAt": "2018-06-02T21:10:41Z", + // "confirmationsRequired": 12, + // "confirmationCount": 12, + // "status": "success", + // "errorDescription": null + // }, + // { + // "id": 754618, + // "type": "deposit", + // "currency": "BTC", + // "address": "1IgNfmERVcier4IhfGEfutkLfu4AcmeiUC", + // "paymentId": null, + // "amount": 0.09, + // "fee": null, + // "txId": "6876541687a9187e987c9187654f7198b9718af974641687b19a87987f91874f", + // "createdAt": "2018-06-02T16:19:44Z", + // "processedAt": "2018-06-02T16:20:50Z", + // "confirmationsRequired": 1, + // "confirmationCount": 1, + // "status": "success", + // "errorDescription": null + // }, + // ... + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'deposit', + }; + return this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'withdrawal', + }; + return this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + parseTransactionStatus (status) { + const statuses = { + 'pending': 'pending', // transfer is in progress + 'success': 'ok', // completed successfully + 'failed': 'failed', // aborted at some point (money will be credited back to the account of origin) + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // "id": 756446, + // "type": "deposit", + // "currency": "ETH", + // "address": "0x451d5a1b7519aa75164f440df78c74aac96023fe", + // "paymentId": null, + // "amount": 0.142, + // "fee": null, + // "txId": "0x2b49098749840a9482c4894be94f94864b498a1306b6874687a5640cc9871918", + // "createdAt": "2018-06-02T19:30:28Z", + // "processedAt": "2018-06-02T21:10:41Z", + // "confirmationsRequired": 12, + // "confirmationCount": 12, + // "status": "success", + // "errorDescription": null, + // } + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + const tag = this.safeString (transaction, 'paymentId'); + const txid = this.safeValue (transaction, 'txId'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const type = this.safeString (transaction, 'type'); + const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt')); + const updated = this.parse8601 (this.safeString (transaction, 'processedAt')); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const amount = this.safeFloat (transaction, 'amount'); + const feeCost = this.safeFloat (transaction, 'fee'); + const fee = { + 'cost': feeCost, + 'currency': code, + }; + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.accountGetDepositAddress (this.extend (request, params)); + // + // { + // "currency": "BTS", + // "address": "crex24", + // "paymentId": "0fg4da4186741579" + // } + // + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'paymentId'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'address': address, + 'amount': parseFloat (this.currencyToPrecision (code, amount)), + // sets whether the specified amount includes fee, can have either of the two values + // true - balance will be decreased by amount, whereas [amount - fee] will be transferred to the specified address + // false - amount will be deposited to the specified address, whereas the balance will be decreased by [amount + fee] + // 'includeFee': false, // the default value is false + }; + if (tag !== undefined) { + request['paymentId'] = tag; + } + const response = await this.accountPostWithdraw (this.extend (request, params)); + return this.parseTransaction (response); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/' + this.version + '/' + api + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET') { + if (Object.keys (query).length) { + request += '?' + this.urlencode (query); + } + } + const url = this.urls['api'] + request; + if ((api === 'trading') || (api === 'account')) { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const secret = this.base64ToBinary (this.secret); + let auth = request + nonce; + headers = { + 'X-CREX24-API-KEY': this.apiKey, + 'X-CREX24-API-NONCE': nonce, + }; + if (method === 'POST') { + headers['Content-Type'] = 'application/json'; + body = this.json (params); + auth += body; + } + const signature = this.stringToBase64 (this.hmac (this.encode (auth), secret, 'sha512', 'binary')); + headers['X-CREX24-API-SIGN'] = this.decode (signature); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!this.isJsonEncodedObject (body)) { + return; // fallback to default error handler + } + if ((code >= 200) && (code < 300)) { + return; // no error + } + const message = this.safeString (response, 'errorDescription'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + if (code === 400) { + throw new BadRequest (feedback); + } else if (code === 401) { + throw new AuthenticationError (feedback); + } else if (code === 403) { + throw new AuthenticationError (feedback); + } else if (code === 429) { + throw new DDoSProtection (feedback); + } else if (code === 500) { + throw new ExchangeError (feedback); + } else if (code === 503) { + throw new ExchangeNotAvailable (feedback); + } else if (code === 504) { + throw new RequestTimeout (feedback); + } + throw new ExchangeError (feedback); // unknown message + } +}; diff --git a/TaskServer/node_modules/ccxt/js/deribit.js b/TaskServer/node_modules/ccxt/js/deribit.js new file mode 100644 index 000000000..9027c5bf1 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/deribit.js @@ -0,0 +1,760 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { TICK_SIZE } = require ('./base/functions/number'); +const { AuthenticationError, ExchangeError, ArgumentsRequired, PermissionDenied, InvalidOrder, OrderNotFound, DDoSProtection, NotSupported, ExchangeNotAvailable, InsufficientFunds } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class deribit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'deribit', + 'name': 'Deribit', + 'countries': [ 'NL' ], // Netherlands + 'version': 'v1', + 'userAgent': undefined, + 'rateLimit': 2000, + 'has': { + 'CORS': true, + 'editOrder': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchTickers': false, + }, + 'urls': { + 'test': 'https://test.deribit.com', + 'logo': 'https://user-images.githubusercontent.com/1294454/41933112-9e2dd65a-798b-11e8-8440-5bab2959fcb8.jpg', + 'api': 'https://www.deribit.com', + 'www': 'https://www.deribit.com', + 'doc': [ + 'https://docs.deribit.com', + 'https://github.com/deribit', + ], + 'fees': 'https://www.deribit.com/pages/information/fees', + 'referral': 'https://www.deribit.com/reg-1189.4038', + }, + 'api': { + 'public': { + 'get': [ + 'ping', + 'test', + 'getinstruments', + 'index', + 'getcurrencies', + 'getorderbook', + 'getlasttrades', + 'getsummary', + 'stats', + 'getannouncments', + ], + }, + 'private': { + 'get': [ + 'account', + 'getopenorders', + 'positions', + 'orderhistory', + 'orderstate', + 'tradehistory', + 'newannouncements', + ], + 'post': [ + 'buy', + 'sell', + 'edit', + 'cancel', + 'cancelall', + ], + }, + }, + 'exceptions': { + // 0 or absent Success, No error + '9999': PermissionDenied, // "api_not_enabled" User didn't enable API for the Account + '10000': AuthenticationError, // "authorization_required" Authorization issue, invalid or absent signature etc + '10001': ExchangeError, // "error" Some general failure, no public information available + '10002': InvalidOrder, // "qty_too_low" Order quantity is too low + '10003': InvalidOrder, // "order_overlap" Rejection, order overlap is found and self-trading is not enabled + '10004': OrderNotFound, // "order_not_found" Attempt to operate with order that can't be found by specified id + '10005': InvalidOrder, // "price_too_low " Price is too low, defines current limit for the operation + '10006': InvalidOrder, // "price_too_low4idx " Price is too low for current index, defines current bottom limit for the operation + '10007': InvalidOrder, // "price_too_high " Price is too high, defines current up limit for the operation + '10008': InvalidOrder, // "price_too_high4idx " Price is too high for current index, defines current up limit for the operation + '10009': InsufficientFunds, // "not_enough_funds" Account has not enough funds for the operation + '10010': OrderNotFound, // "already_closed" Attempt of doing something with closed order + '10011': InvalidOrder, // "price_not_allowed" This price is not allowed for some reason + '10012': InvalidOrder, // "book_closed" Operation for instrument which order book had been closed + '10013': PermissionDenied, // "pme_max_total_open_orders " Total limit of open orders has been exceeded, it is applicable for PME users + '10014': PermissionDenied, // "pme_max_future_open_orders " Limit of count of futures' open orders has been exceeded, it is applicable for PME users + '10015': PermissionDenied, // "pme_max_option_open_orders " Limit of count of options' open orders has been exceeded, it is applicable for PME users + '10016': PermissionDenied, // "pme_max_future_open_orders_size " Limit of size for futures has been exceeded, it is applicable for PME users + '10017': PermissionDenied, // "pme_max_option_open_orders_size " Limit of size for options has been exceeded, it is applicable for PME users + '10019': PermissionDenied, // "locked_by_admin" Trading is temporary locked by admin + '10020': ExchangeError, // "invalid_or_unsupported_instrument" Instrument name is not valid + '10022': InvalidOrder, // "invalid_quantity" quantity was not recognized as a valid number + '10023': InvalidOrder, // "invalid_price" price was not recognized as a valid number + '10024': InvalidOrder, // "invalid_max_show" max_show parameter was not recognized as a valid number + '10025': InvalidOrder, // "invalid_order_id" Order id is missing or its format was not recognized as valid + '10026': InvalidOrder, // "price_precision_exceeded" Extra precision of the price is not supported + '10027': InvalidOrder, // "non_integer_contract_amount" Futures contract amount was not recognized as integer + '10028': DDoSProtection, // "too_many_requests" Allowed request rate has been exceeded + '10029': OrderNotFound, // "not_owner_of_order" Attempt to operate with not own order + '10030': ExchangeError, // "must_be_websocket_request" REST request where Websocket is expected + '10031': ExchangeError, // "invalid_args_for_instrument" Some of arguments are not recognized as valid + '10032': InvalidOrder, // "whole_cost_too_low" Total cost is too low + '10033': NotSupported, // "not_implemented" Method is not implemented yet + '10034': InvalidOrder, // "stop_price_too_high" Stop price is too high + '10035': InvalidOrder, // "stop_price_too_low" Stop price is too low + '11035': InvalidOrder, // "no_more_stops " Allowed amount of stop orders has been exceeded + '11036': InvalidOrder, // "invalid_stoppx_for_index_or_last" Invalid StopPx (too high or too low) as to current index or market + '11037': InvalidOrder, // "outdated_instrument_for_IV_order" Instrument already not available for trading + '11038': InvalidOrder, // "no_adv_for_futures" Advanced orders are not available for futures + '11039': InvalidOrder, // "no_adv_postonly" Advanced post-only orders are not supported yet + '11040': InvalidOrder, // "impv_not_in_range 0..499%" Implied volatility is out of allowed range + '11041': InvalidOrder, // "not_adv_order" Advanced order properties can't be set if the order is not advanced + '11042': PermissionDenied, // "permission_denied" Permission for the operation has been denied + '11044': OrderNotFound, // "not_open_order" Attempt to do open order operations with the not open order + '11045': ExchangeError, // "invalid_event" Event name has not been recognized + '11046': ExchangeError, // "outdated_instrument" At several minutes to instrument expiration, corresponding advanced implied volatility orders are not allowed + '11047': ExchangeError, // "unsupported_arg_combination" The specified combination of arguments is not supported + '11048': ExchangeError, // "not_on_this_server" The requested operation is not available on this server. + '11050': ExchangeError, // "invalid_request" Request has not been parsed properly + '11051': ExchangeNotAvailable, // "system_maintenance" System is under maintenance + '11030': ExchangeError, // "other_reject " Some rejects which are not considered as very often, more info may be specified in + '11031': ExchangeError, // "other_error " Some errors which are not considered as very often, more info may be specified in + }, + 'precisionMode': TICK_SIZE, + 'options': { + 'fetchTickerQuotes': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetGetinstruments (params); + const markets = this.safeValue (response, 'result'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'instrumentName'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const type = this.safeString (market, 'kind'); + const future = (type === 'future'); + const option = (type === 'option'); + const active = this.safeValue (market, 'isActive'); + const precision = { + 'amount': this.safeFloat (market, 'minTradeAmount'), + 'price': this.safeFloat (market, 'tickSize'), + }; + result.push ({ + 'id': id, + 'symbol': id, + 'base': base, + 'quote': quote, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minTradeAmount'), + 'max': undefined, + }, + 'price': { + 'min': this.safeFloat (market, 'tickSize'), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'type': type, + 'spot': false, + 'future': future, + 'option': option, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccount (params); + // + // { + // "usOut":1569048827943520, + // "usIn":1569048827943020, + // "usDiff":500, + // "testnet":false, + // "success":true, + // "result":{ + // "equity":2e-9, + // "maintenanceMargin":0.0, + // "initialMargin":0.0, + // "availableFunds":0.0, + // "balance":0.0, + // "marginBalance":0.0, + // "SUPL":0.0, + // "SRPL":0.0, + // "PNL":0.0, + // "optionsPNL":0.0, + // "optionsSUPL":0.0, + // "optionsSRPL":0.0, + // "optionsD":0.0, + // "optionsG":0.0, + // "optionsV":0.0, + // "optionsTh":0.0, + // "futuresPNL":0.0, + // "futuresSUPL":0.0, + // "futuresSRPL":0.0, + // "deltaTotal":0.0, + // "sessionFunding":0.0, + // "depositAddress":"13tUtNsJSZa1F5GeCmwBywVrymHpZispzw", + // "currency":"BTC" + // }, + // "message":"" + // } + // + const result = { + 'info': response, + }; + const balance = this.safeValue (response, 'result', {}); + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableFunds'); + account['used'] = this.safeFloat (balance, 'maintenanceMargin'); + account['total'] = this.safeFloat (balance, 'equity'); + result[code] = account; + return this.parseBalance (result); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetAccount (this.extend (request, params)); + const result = this.safeValue (response, 'result', {}); + const address = this.safeString (result, 'depositAddress'); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "currentFunding":0.0, + // "funding8h":0.0000017213784611422821, + // "instrumentName":"BTC-PERPETUAL", + // "openInterest":7600223, + // "openInterestAmount":76002230, + // "high":7665.5, + // "low":7450.0, + // "volume":12964792.0, + // "volumeUsd":129647920, + // "volumeBtc":17214.63595316, + // "last":7520.5, + // "bidPrice":7520.0, + // "askPrice":7520.5, + // "midPrice":7520.25, + // "estDelPrice":"", + // "markPrice":7521.0, + // "created":"2019-12-09 15:17:00 GMT" + // } + // + const timestamp = this.safeInteger (ticker, 'created'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'instrumentName'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bidPrice'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'askPrice'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.publicGetGetsummary (this.extend (request, params)); + // + // { + // "usOut":1575904620528163, + // "usIn":1575904620528129, + // "usDiff":34, + // "testnet":false, + // "success":true, + // "result": { + // "currentFunding":0.0, + // "funding8h":0.0000017213784611422821, + // "instrumentName":"BTC-PERPETUAL", + // "openInterest":7600223, + // "openInterestAmount":76002230, + // "high":7665.5, + // "low":7450.0, + // "volume":12964792.0, + // "volumeUsd":129647920, + // "volumeBtc":17214.63595316, + // "last":7520.5, + // "bidPrice":7520.0, + // "askPrice":7520.5, + // "midPrice":7520.25, + // "estDelPrice":"", + // "markPrice":7521.0, + // "created":"2019-12-09 15:17:00 GMT" + // }, + // "message":"" + // } + // + return this.parseTicker (response['result'], market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "tradeId":23197559, + // "instrument":"BTC-28JUN19", + // "timeStamp":1559643011379, + // "tradeSeq":1997200, + // "quantity":2, + // "amount":20.0, + // "price":8010.0, + // "direction":"sell", + // "tickDirection":2, + // "indexPrice":7969.01 + // } + // + // fetchMyTrades (private) + // + // { + // "quantity":54, + // "amount":540.0, + // "tradeId":23087297, + // "instrument":"BTC-PERPETUAL", + // "timeStamp":1559604178803, + // "tradeSeq":8265011, + // "price":8213.0, + // "side":"sell", + // "orderId":12373631800, + // "matchingId":0, + // "liquidity":"T", + // "fee":0.000049312, + // "feeCurrency":"BTC", + // "tickDirection":3, + // "indexPrice":8251.94, + // "selfTrade":false + // } + // + const id = this.safeString (trade, 'tradeId'); + const orderId = this.safeString (trade, 'orderId'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'timeStamp'); + const side = this.safeString2 (trade, 'side', 'direction'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'feeCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } else { + request['limit'] = 10000; + } + const response = await this.publicGetGetlasttrades (this.extend (request, params)); + // + // { + // "usOut":1559643108984527, + // "usIn":1559643108984470, + // "usDiff":57, + // "testnet":false, + // "success":true, + // "result": [ + // { + // "tradeId":23197559, + // "instrument":"BTC-28JUN19", + // "timeStamp":1559643011379, + // "tradeSeq":1997200, + // "quantity":2, + // "amount":20.0, + // "price":8010.0, + // "direction":"sell", + // "tickDirection":2, + // "indexPrice":7969.01 + // } + // ], + // "message":"" + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.publicGetGetorderbook (this.extend (request, params)); + const timestamp = this.safeInteger (response, 'usOut') / 1000; + const orderbook = this.parseOrderBook (response['result'], timestamp, 'bids', 'asks', 'price', 'quantity'); + return this.extend (orderbook, { + 'nonce': this.safeInteger (response, 'tstamp'), + }); + } + + parseOrderStatus (status) { + const statuses = { + 'open': 'open', + 'cancelled': 'canceled', + 'filled': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "orderId": 5258039, // ID of the order + // "type": "limit", // not documented, but present in the actual response + // "instrument": "BTC-26MAY17", // instrument name (market id) + // "direction": "sell", // order direction, "buy" or "sell" + // "price": 1860, // float, USD for futures, BTC for options + // "label": "", // label set by the owner, up to 32 chars + // "quantity": 10, // quantity, in contracts ($10 per contract for futures, ฿1 — for options) + // "filledQuantity": 3, // filled quantity, in contracts ($10 per contract for futures, ฿1 — for options) + // "avgPrice": 1860, // average fill price of the order + // "commission": -0.000001613, // in BTC units + // "created": 1494491899308, // creation timestamp + // "state": "open", // open, cancelled, etc + // "postOnly": false // true for post-only orders only + // open orders -------------------------------------------------------- + // "lastUpdate": 1494491988754, // timestamp of the last order state change (before this cancelorder of course) + // closed orders ------------------------------------------------------ + // "tstamp": 1494492913288, // timestamp of the last order state change, documented, but may be missing in the actual response + // "modified": 1494492913289, // timestamp of the last db write operation, e.g. trade that doesn't change order status, documented, but may missing in the actual response + // "adv": false // advanced type (false, or "usd" or "implv") + // "trades": [], // not documented, injected from the outside of the parseOrder method into the order + // } + // + const timestamp = this.safeInteger (order, 'created'); + const lastUpdate = this.safeInteger (order, 'lastUpdate'); + let lastTradeTimestamp = this.safeInteger2 (order, 'tstamp', 'modified'); + const id = this.safeString (order, 'orderId'); + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avgPrice'); + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'filledQuantity'); + if (lastTradeTimestamp === undefined) { + if (filled !== undefined) { + if (filled > 0) { + lastTradeTimestamp = lastUpdate; + } + } + } + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (price !== undefined) { + cost = price * filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const side = this.safeStringLower (order, 'direction'); + let feeCost = this.safeFloat (order, 'commission'); + if (feeCost !== undefined) { + feeCost = Math.abs (feeCost); + } + const fee = { + 'cost': feeCost, + 'currency': 'BTC', + }; + const type = this.safeString (order, 'type'); + const marketId = this.safeString (order, 'instrument'); + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': average, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': undefined, // todo: parse trades + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privateGetOrderstate (this.extend (request, params)); + const result = this.safeValue (response, 'result'); + if (result === undefined) { + throw new OrderNotFound (this.id + ' fetchOrder() ' + this.json (response)); + } + return this.parseOrder (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'instrument': this.marketId (symbol), + 'quantity': amount, + 'type': type, + // 'post_only': 'false' or 'true', https://github.com/ccxt/ccxt/issues/5159 + }; + if (price !== undefined) { + request['price'] = price; + } + const method = 'privatePost' + this.capitalize (side); + const response = await this[method] (this.extend (request, params)); + const order = this.safeValue (response['result'], 'order'); + if (order === undefined) { + return response; + } + return this.parseOrder (order); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + if (amount !== undefined) { + request['quantity'] = amount; + } + if (price !== undefined) { + request['price'] = price; + } + const response = await this.privatePostEdit (this.extend (request, params)); + return this.parseOrder (response['result']['order']); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privatePostCancel (this.extend (request, params)); + return this.parseOrder (response['result']['order']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.privateGetGetopenorders (this.extend (request, params)); + return this.parseOrders (response['result'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + const response = await this.privateGetOrderhistory (this.extend (request, params)); + return this.parseOrders (response['result'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'instrument': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; // default = 20 + } + const response = await this.privateGetTradehistory (this.extend (request, params)); + // + // { + // "usOut":1559611553394836, + // "usIn":1559611553394000, + // "usDiff":836, + // "testnet":false, + // "success":true, + // "result": [ + // { + // "quantity":54, + // "amount":540.0, + // "tradeId":23087297, + // "instrument":"BTC-PERPETUAL", + // "timeStamp":1559604178803, + // "tradeSeq":8265011, + // "price":8213.0, + // "side":"sell", + // "orderId":12373631800, + // "matchingId":0, + // "liquidity":"T", + // "fee":0.000049312, + // "feeCurrency":"BTC", + // "tickDirection":3, + // "indexPrice":8251.94, + // "selfTrade":false + // } + // ], + // "message":"", + // "has_more":true + // } + // + const trades = this.safeValue (response, 'result', []); + return this.parseTrades (trades, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = '/' + 'api/' + this.version + '/' + api + '/' + path; + let url = this.urls['api'] + query; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = '_=' + nonce + '&_ackey=' + this.apiKey + '&_acsec=' + this.secret + '&_action=' + query; + if (Object.keys (params).length) { + params = this.keysort (params); + auth += '&' + this.urlencode (params); + } + const hash = this.hash (this.encode (auth), 'sha256', 'base64'); + const signature = this.apiKey + '.' + nonce + '.' + this.decode (hash); + headers = { + 'x-deribit-sig': signature, + }; + if (method !== 'GET') { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + body = this.urlencode (params); + } else if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // fallback to default error handler + } + // + // {"usOut":1535877098645376,"usIn":1535877098643364,"usDiff":2012,"testnet":false,"success":false,"message":"order_not_found","error":10004} + // + const error = this.safeString (response, 'error'); + if ((error !== undefined) && (error !== '0')) { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/digifinex.js b/TaskServer/node_modules/ccxt/js/digifinex.js new file mode 100644 index 000000000..ab6133e23 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/digifinex.js @@ -0,0 +1,1164 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AccountSuspended, BadRequest, BadResponse, NetworkError, DDoSProtection, AuthenticationError, PermissionDenied, ArgumentsRequired, ExchangeError, InsufficientFunds, InvalidOrder, InvalidNonce, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class digifinex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'digifinex', + 'name': 'DigiFinex', + 'countries': [ 'SG' ], + 'version': 'v3', + 'rateLimit': 900, // 300 for posts + 'has': { + 'cancelOrders': true, + 'fetchOrders': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchMyTrades': true, + 'fetchLedger': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '4h': '240', + '12h': '720', + '1d': '1D', + '1w': '1W', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/62184319-304e8880-b366-11e9-99fe-8011d6929195.jpg', + 'api': 'https://openapi.digifinex.vip', + 'www': 'https://www.digifinex.vip', + 'doc': [ + 'https://docs.digifinex.vip', + ], + 'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328482-Fee-Structure-on-DigiFinex', + 'referral': 'https://www.digifinex.vip/en-ww/from/DhOzBg/3798****5114', + }, + 'api': { + 'v2': { + 'get': [ + 'ticker', + ], + }, + 'public': { + 'get': [ + '{market}/symbols', + 'kline', + 'margin/currencies', + 'margin/symbols', + 'markets', // undocumented + 'order_book', + 'ping', + 'spot/symbols', + 'time', + 'trades', + ], + }, + 'private': { + 'get': [ + '{market}/financelog', + '{market}/mytrades', + '{market}/order', + '{market}/order/current', + '{market}/order/history', + 'margin/assets', + 'margin/financelog', + 'margin/mytrades', + 'margin/order', + 'margin/order/current', + 'margin/order/history', + 'margin/positions', + 'otc/financelog', + 'spot/assets', + 'spot/financelog', + 'spot/mytrades', + 'spot/order', + 'spot/order/current', + 'spot/order/history', + ], + 'post': [ + '{market}/order/cancel', + '{market}/order/new', + 'margin/order/cancel', + 'margin/order/new', + 'margin/position/close', + 'spot/order/cancel', + 'spot/order/new', + 'transfer', + ], + }, + }, + 'exceptions': { + 'exact': { + '10001': [ BadRequest, "Wrong request method, please check it's a GET ot POST request" ], + '10002': [ AuthenticationError, 'Invalid ApiKey' ], + '10003': [ AuthenticationError, "Sign doesn't match" ], + '10004': [ BadRequest, 'Illegal request parameters' ], + '10005': [ DDoSProtection, 'Request frequency exceeds the limit' ], + '10006': [ PermissionDenied, 'Unauthorized to execute this request' ], + '10007': [ PermissionDenied, 'IP address Unauthorized' ], + '10008': [ InvalidNonce, 'Timestamp for this request is invalid, timestamp must within 1 minute' ], + '10009': [ NetworkError, 'Unexist endpoint, please check endpoint URL' ], + '10011': [ AccountSuspended, 'ApiKey expired. Please go to client side to re-create an ApiKey' ], + '20001': [ PermissionDenied, 'Trade is not open for this trading pair' ], + '20002': [ PermissionDenied, 'Trade of this trading pair is suspended' ], + '20003': [ InvalidOrder, 'Invalid price or amount' ], + '20007': [ InvalidOrder, 'Price precision error' ], + '20008': [ InvalidOrder, 'Amount precision error' ], + '20009': [ InvalidOrder, 'Amount is less than the minimum requirement' ], + '20010': [ InvalidOrder, 'Cash Amount is less than the minimum requirement' ], + '20011': [ InsufficientFunds, 'Insufficient balance' ], + '20012': [ BadRequest, 'Invalid trade type, valid value: buy/sell)' ], + '20013': [ InvalidOrder, 'No order info found' ], + '20014': [ BadRequest, 'Invalid date, Valid format: 2018-07-25)' ], + '20015': [ BadRequest, 'Date exceeds the limit' ], + '20018': [ PermissionDenied, 'Your trading rights have been banned by the system' ], + '20019': [ BadRequest, 'Wrong trading pair symbol. Correct format:"usdt_btc". Quote asset is in the front' ], + '20020': [ DDoSProtection, "You have violated the API operation trading rules and temporarily forbid trading. At present, we have certain restrictions on the user's transaction rate and withdrawal rate." ], + '50000': [ ExchangeError, 'Exception error' ], + }, + 'broad': { + }, + }, + 'options': { + 'defaultType': 'spot', + 'types': [ 'spot', 'margin', 'otc' ], + }, + }); + } + + async fetchMarketsByType (type, params = {}) { + const method = 'publicGet' + this.capitalize (type) + 'Symbols'; + const response = await this[method] (params); + // + // { + // "symbol_list": [ + // { + // "order_types":["LIMIT","MARKET"], + // "quote_asset":"USDT", + // "minimum_value":2, + // "amount_precision":4, + // "status":"TRADING", + // "minimum_amount":0.001, + // "symbol":"LTC_USDT", + // "margin_rate":0.3, + // "zone":"MAIN", + // "base_asset":"LTC", + // "price_precision":2 + // }, + // ], + // "code":0 + // } + // + const markets = this.safeValue (response, 'symbols_list', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'base_asset'); + const quoteId = this.safeString (market, 'quote_asset'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'amount_precision'), + 'price': this.safeInteger (market, 'price_precision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minimum_amount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (market, 'minimum_value'), + 'max': undefined, + }, + }; + // + // The status is documented in the exchange API docs as follows: + // TRADING, HALT (delisted), BREAK (trading paused) + // https://docs.digifinex.vip/en-ww/v3/#/public/spot/symbols + // However, all spot markets actually have status === 'HALT' + // despite that they appear to be active on the exchange website. + // Apparently, we can't trust this status. + // const status = this.safeString (market, 'status'); + // const active = (status === 'TRADING'); + // + const active = undefined; + const spot = (type === 'spot'); + const margin = (type === 'margin'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'type': type, + 'spot': spot, + 'margin': margin, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + // + // { + // "data": [ + // { + // "volume_precision":4, + // "price_precision":2, + // "market":"btc_usdt", + // "min_amount":2, + // "min_volume":0.0001 + // }, + // ], + // "date":1564507456, + // "code":0 + // } + // + const markets = this.safeValue (response, 'data', []); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'market'); + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'volume_precision'), + 'price': this.safeInteger (market, 'price_precision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'min_volume'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': undefined, + }, + }; + const active = undefined; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const type = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const method = 'privateGet' + this.capitalize (type) + 'Assets'; + const response = await this[method] (params); + // + // { + // "code": 0, + // "list": [ + // { + // "currency": "BTC", + // "free": 4723846.89208129, + // "total": 0 + // } + // ] + // } + const balances = this.safeValue (response, 'list', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['used'] = this.safeFloat (balance, 'frozen'); + account['free'] = this.safeFloat (balance, 'free'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 150 + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { + // "bids": [ + // [9605.77,0.0016], + // [9605.46,0.0003], + // [9602.04,0.0127], + // ], + // "asks": [ + // [9627.22,0.025803], + // [9627.12,0.168543], + // [9626.52,0.0011529], + // ], + // "date":1564509499, + // "code":0 + // } + // + const timestamp = this.safeTimestamp (response, 'date'); + return this.parseOrderBook (response, timestamp); + } + + async fetchTickers (symbols = undefined, params = {}) { + const apiKey = this.safeValue (params, 'apiKey', this.apiKey); + if (!apiKey) { + throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter'); + } + await this.loadMarkets (); + const request = { + 'apiKey': apiKey, + }; + const response = await this.v2GetTicker (this.extend (request, params)); + // + // { + // "ticker":{ + // "btc_eth":{ + // "last":0.021957, + // "base_vol":2249.3521732227, + // "change":-0.6, + // "vol":102443.5111, + // "sell":0.021978, + // "low":0.021791, + // "buy":0.021946, + // "high":0.022266 + // } + // }, + // "date":1564518452, + // "code":0 + // } + // + const result = {}; + const tickers = this.safeValue (response, 'ticker', {}); + const date = this.safeInteger (response, 'date'); + const reversedMarketIds = Object.keys (tickers); + for (let i = 0; i < reversedMarketIds.length; i++) { + const reversedMarketId = reversedMarketIds[i]; + const ticker = this.extend ({ + 'date': date, + }, tickers[reversedMarketId]); + const [ quoteId, baseId ] = reversedMarketId.split ('_'); + const marketId = baseId + '_' + quoteId; + let market = undefined; + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const apiKey = this.safeValue (params, 'apiKey', this.apiKey); + if (!apiKey) { + throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // reversed base/quote in v2 + const marketId = market['quoteId'] + '_' + market['baseId']; + const request = { + 'symbol': marketId, + 'apiKey': apiKey, + }; + const response = await this.v2GetTicker (this.extend (request, params)); + // + // { + // "ticker":{ + // "btc_eth":{ + // "last":0.021957, + // "base_vol":2249.3521732227, + // "change":-0.6, + // "vol":102443.5111, + // "sell":0.021978, + // "low":0.021791, + // "buy":0.021946, + // "high":0.022266 + // } + // }, + // "date":1564518452, + // "code":0 + // } + // + const date = this.safeInteger (response, 'date'); + const ticker = this.safeValue (response, 'ticker', {}); + let result = this.safeValue (ticker, marketId, {}); + result = this.extend ({ 'date': date }, result); + return this.parseTicker (result, market); + } + + parseTicker (ticker, market = undefined) { + // + // fetchTicker, fetchTickers + // + // { + // "last":0.021957, + // "base_vol":2249.3521732227, + // "change":-0.6, + // "vol":102443.5111, + // "sell":0.021978, + // "low":0.021791, + // "buy":0.021946, + // "high":0.022266, + // "date"1564518452, // injected from fetchTicker/fetchTickers + // } + // + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (ticker, 'date'); + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'change'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'base_vol'), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "date":1564520003, + // "id":1596149203, + // "amount":0.7073, + // "type":"buy", + // "price":0.02193, + // } + // + // fetchMyTrades (private) + // + // { + // "symbol": "BTC_USDT", + // "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966", + // "id": 28457, + // "price": 0.1, + // "amount": 0, + // "fee": 0.096, + // "fee_currency": "USDT", + // "timestamp": 1499865549, + // "side": "buy", + // "is_maker": true + // } + // + const id = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'order_id'); + const timestamp = this.safeTimestamp2 (trade, 'date', 'timestamp'); + const side = this.safeString2 (trade, 'type', 'side'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[market]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const takerOrMaker = this.safeValue (trade, 'is_maker'); + const feeCost = this.safeFloat (trade, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'fee_currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': orderId, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 500 + } + const response = await this.publicGetTrades (this.extend (request, params)); + // + // { + // "data":[ + // { + // "date":1564520003, + // "id":1596149203, + // "amount":0.7073, + // "type":"buy", + // "price":0.02193, + // }, + // { + // "date":1564520002, + // "id":1596149165, + // "amount":0.3232, + // "type":"sell", + // "price":0.021927, + // }, + // ], + // "code": 0, + // "date": 1564520003, + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseTrades (data, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, // timestamp + ohlcv[5], // open + ohlcv[3], // high + ohlcv[4], // low + ohlcv[2], // close + ohlcv[1], // volume + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'period': this.timeframes[timeframe], + // 'start_time': 1564520003, // starting timestamp, 200 candles before end_time by default + // 'end_time': 1564520003, // ending timestamp, current timestamp by default + }; + if (since !== undefined) { + const startTime = parseInt (since / 1000); + request['start_time'] = startTime; + if (limit !== undefined) { + const duration = this.parseTimeframe (timeframe); + request['end_time'] = this.sum (startTime, limit * duration); + } + } else if (limit !== undefined) { + const endTime = this.seconds (); + const duration = this.parseTimeframe (timeframe); + request['startTime'] = this.sum (endTime, -limit * duration); + } + const response = await this.publicGetKline (this.extend (request, params)); + // + // { + // "code":0, + // "data":[ + // [1556712900,2205.899,0.029967,0.02997,0.029871,0.029927], + // [1556713800,1912.9174,0.029992,0.030014,0.029955,0.02996], + // [1556714700,1556.4795,0.029974,0.030019,0.029969,0.02999], + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOHLCVs (data, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const request = { + 'market': orderType, + 'symbol': market['id'], + 'amount': this.amountToPrecision (symbol, amount), + // 'post_only': 0, // 0 by default, if set to 1 the order will be canceled if it can be executed immediately, making sure there will be no market taking + }; + let suffix = ''; + if (type === 'market') { + suffix = '_market'; + } else { + request['price'] = this.priceToPrecision (symbol, price); + } + request['type'] = side + suffix; + const response = await this.privatePostMarketOrderNew (this.extend (request, params)); + // + // { + // "code": 0, + // "order_id": "198361cecdc65f9c8c9bb2fa68faec40" + // } + // + const result = this.parseOrder (response, market); + return this.extend (result, { + 'symbol': symbol, + 'side': side, + 'type': type, + 'amount': amount, + 'price': price, + }); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const request = { + 'market': orderType, + 'order_id': id, + }; + const response = await this.privatePostMarketOrderCancel (this.extend (request, params)); + // + // { + // "code": 0, + // "success": [ + // "198361cecdc65f9c8c9bb2fa68faec40", + // "3fb0d98e51c18954f10d439a9cf57de0" + // ], + // "error": [ + // "78a7104e3c65cc0c5a212a53e76d0205" + // ] + // } + // + const canceledOrders = this.safeValue (response, 'success', []); + const numCanceledOrders = canceledOrders.length; + if (numCanceledOrders !== 1) { + throw new OrderNotFound (this.id + ' cancelOrder ' + id + ' not found'); + } + return response; + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + const request = { + 'market': orderType, + 'order_id': ids.join (','), + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "success": [ + // "198361cecdc65f9c8c9bb2fa68faec40", + // "3fb0d98e51c18954f10d439a9cf57de0" + // ], + // "error": [ + // "78a7104e3c65cc0c5a212a53e76d0205" + // ] + // } + // + const canceledOrders = this.safeValue (response, 'success', []); + const numCanceledOrders = canceledOrders.length; + if (numCanceledOrders < 1) { + throw new OrderNotFound (this.id + ' cancelOrders error'); + } + return response; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'open', // partially filled + '2': 'closed', + '3': 'canceled', + '4': 'canceled', // partially filled and canceled + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "code": 0, + // "order_id": "198361cecdc65f9c8c9bb2fa68faec40" + // } + // + // fetchOrder, fetchOpenOrders, fetchOrders + // + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // + const id = this.safeString (order, 'order_id'); + const timestamp = this.safeTimestamp (order, 'created_date'); + const lastTradeTimestamp = this.safeTimestamp (order, 'finished_date'); + let side = this.safeString (order, 'type'); + let type = undefined; + if (side !== undefined) { + const parts = side.split ('_'); + const numParts = parts.length; + if (numParts > 1) { + side = parts[0]; + type = parts[1]; + } else { + type = 'limit'; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + if (market === undefined) { + const exchange = order['symbol'].toUpperCase (); + if (exchange in this.markets_by_id) { + market = this.markets_by_id[exchange]; + } + } + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'executed_amount'); + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avg_price'); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = filled * average; + } + if (amount !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'cost': cost, + 'average': average, + 'status': status, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + const request = { + 'market': orderType, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privateGetMarketOrderCurrent (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + const request = { + 'market': orderType, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); // default 3 days from now, max 30 days + } + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 100 + } + const response = await this.privateGetMarketOrderHistory (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'market': orderType, + 'order_id': id, + }; + const response = await this.privateGetMarketOrder (this.extend (request, params)); + // + // { + // "code": 0, + // "data": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "dd3164b333a4afa9d5730bb87f6db8b3", + // "created_date": 1562303547, + // "finished_date": 0, + // "price": 0.1, + // "amount": 1, + // "cash_amount": 1, + // "executed_amount": 0, + // "avg_price": 0, + // "status": 1, + // "type": "buy", + // "kind": "margin" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + let market = undefined; + const request = { + 'market': orderType, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); // default 3 days from now, max 30 days + } + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 100 + } + const response = await this.privateGetMarketMytrades (this.extend (request, params)); + // + // { + // "code": 0, + // "list": [ + // { + // "symbol": "BTC_USDT", + // "order_id": "6707cbdcda0edfaa7f4ab509e4cbf966", + // "id": 28457, + // "price": 0.1, + // "amount": 0, + // "fee": 0.096, + // "fee_currency": "USDT", + // "timestamp": 1499865549, + // "side": "buy", + // "is_maker": true + // } + // ] + // } + // + const data = this.safeValue (response, 'list', []); + return this.parseTrades (data, market, since, limit); + } + + parseLedgerEntryType (type) { + const types = {}; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // { + // "currency_mark": "BTC", + // "type": 100234, + // "num": 28457, + // "balance": 0.1, + // "time": 1546272000 + // } + // + const id = this.safeString (item, 'num'); + const account = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const code = this.safeCurrencyCode (this.safeString (item, 'currency_mark'), currency); + const timestamp = this.safeTimestamp (item, 'time'); + const before = undefined; + const after = this.safeFloat (item, 'balance'); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'direction': undefined, + 'account': account, + 'referenceId': undefined, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': undefined, + 'before': before, + 'after': after, + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + const defaultType = this.safeString (this.options, 'defaultType', 'spot'); + const orderType = this.safeString (params, 'type', defaultType); + params = this.omit (params, 'type'); + await this.loadMarkets (); + const request = { + 'market': orderType, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency_mark'] = currency['id']; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 1000 + } + const response = await this.privateGetMarketFinancelog (this.extend (request, params)); + // + // { + // "code": 0, + // "data": { + // "total": 521, + // "finance": [ + // { + // "currency_mark": "BTC", + // "type": 100234, + // "num": 28457, + // "balance": 0.1, + // "time": 1546272000 + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const items = this.safeValue (data, 'finance', []); + return this.parseLedger (items, currency, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const version = (api === 'v2') ? api : this.version; + let url = this.urls['api'] + '/' + version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + const urlencoded = this.urlencode (this.keysort (query)); + if (api === 'private') { + const nonce = this.nonce ().toString (); + const auth = urlencoded; + // the signature is not time-limited :\ + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + if (method === 'GET') { + if (urlencoded) { + url += '?' + urlencoded; + } + } else if (method === 'POST') { + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + if (urlencoded) { + body = urlencoded; + } + } + headers = { + 'ACCESS-KEY': this.apiKey, + 'ACCESS-SIGN': signature, + 'ACCESS-TIMESTAMP': nonce, + }; + } else { + if (urlencoded) { + url += '?' + urlencoded; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + dateUTC8 (timestampMS) { + const timedelta = this.safeValue (this.options, 'timedelta', 8 * 60 * 60 * 1000); // eight hours + return this.ymd (timestampMS + timedelta); + } + + handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { + if (!response) { + return; // fall back to default error handler + } + const code = this.safeString (response, 'code'); + if (code === '0') { + return; // no error + } + const feedback = this.id + ' ' + responseBody; + if (code === undefined) { + throw new BadResponse (feedback); + } + const unknownError = [ ExchangeError, feedback ]; + const [ ExceptionClass, message ] = this.safeValue (this.exceptions['exact'], code, unknownError); + throw new ExceptionClass (message); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/dsx.js b/TaskServer/node_modules/ccxt/js/dsx.js new file mode 100644 index 000000000..b37bcf6c3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/dsx.js @@ -0,0 +1,1265 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InvalidNonce, BadRequest, InsufficientFunds, PermissionDenied, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class dsx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'dsx', + 'name': 'DSX', + 'countries': [ 'UK' ], + 'rateLimit': 1500, + 'version': 'v3', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': false, + 'fetchOrderBooks': false, + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'fetchTransactions': true, + 'fetchTickers': true, + 'fetchMyTrades': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27990275-1413158a-645a-11e7-931c-94717f7510e3.jpg', + 'api': { + 'public': 'https://dsx.uk/mapi', // market data + 'private': 'https://dsx.uk/tapi', // trading + 'dwapi': 'https://dsx.uk/dwapi', // deposit/withdraw + }, + 'www': 'https://dsx.uk', + 'doc': [ + 'https://dsx.uk/developers/publicApi', + ], + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.15 / 100, + 'taker': 0.25 / 100, + }, + }, + 'timeframes': { + '1m': 'm', + '1h': 'h', + '1d': 'd', + }, + 'api': { + // market data (public) + 'public': { + 'get': [ + 'barsFromMoment/{pair}/{period}/{start}', + 'depth/{pair}', + 'info', + 'lastBars/{pair}/{period}/{amount}', // period is 'm', 'h' or 'd' + 'periodBars/{pair}/{period}/{start}/{end}', + 'ticker/{pair}', + 'trades/{pair}', + ], + }, + // trading (private) + 'private': { + 'post': [ + 'info/account', + 'history/transactions', + 'history/trades', + 'history/orders', + 'orders', + 'order/cancel', + // 'order/cancel/all', + 'order/status', + 'order/new', + 'volume', + 'fees', // trading fee schedule + ], + }, + // deposit / withdraw (private) + 'dwapi': { + 'post': [ + 'deposit/cryptoaddress', + 'withdraw/crypto', + 'withdraw/fiat', + 'withdraw/submit', + // 'withdraw/cancel', + 'transaction/status', // see 'history/transactions' in private tapi above + ], + }, + }, + 'exceptions': { + 'exact': { + 'Sign is invalid': AuthenticationError, // {"success":0,"error":"Sign is invalid"} + 'Order was rejected. Incorrect price.': InvalidOrder, // {"success":0,"error":"Order was rejected. Incorrect price."} + "Order was rejected. You don't have enough money.": InsufficientFunds, // {"success":0,"error":"Order was rejected. You don't have enough money."} + 'This method is blocked for your pair of keys': PermissionDenied, // {"success":0,"error":"This method is blocked for your pair of keys"} + }, + 'broad': { + 'INVALID_PARAMETER': BadRequest, + 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} + 'invalid api key': AuthenticationError, + 'invalid sign': AuthenticationError, + 'api key dont have trade permission': AuthenticationError, + 'invalid parameter': InvalidOrder, + 'invalid order': InvalidOrder, + 'Requests too often': DDoSProtection, + 'not available': ExchangeNotAvailable, + 'data unavailable': ExchangeNotAvailable, + 'external service unavailable': ExchangeNotAvailable, + 'nonce is invalid': InvalidNonce, // {"success":0,"error":"Parameter: nonce is invalid"} + }, + }, + 'options': { + 'fetchTickersMaxLength': 250, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInfo (params); + // + // { + // "server_time": 1522057909, + // "pairs": { + // "ethusd": { + // "decimal_places": 5, + // "min_price": 100, + // "max_price": 1500, + // "min_amount": 0.01, + // "hidden": 0, + // "fee": 0, + // "amount_decimal_places": 4, + // "quoted_currency": "USD", + // "base_currency": "ETH" + // } + // } + // } + // + const markets = this.safeValue (response, 'pairs'); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quoted_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'decimal_places'), + 'price': this.safeInteger (market, 'decimal_places'), + }; + const amountLimits = { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }; + const priceLimits = { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }; + const costLimits = { + 'min': this.safeFloat (market, 'min_total'), + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const hidden = this.safeInteger (market, 'hidden'); + const active = (hidden === 0); + // see parseMarket below + // https://github.com/ccxt/ccxt/pull/5786 + const otherId = base.toLowerCase () + quote.toLowerCase (); + result.push ({ + 'id': id, + 'otherId': otherId, // https://github.com/ccxt/ccxt/pull/5786 + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostInfoAccount (); + // + // { + // "success" : 1, + // "return" : { + // "funds" : { + // "BTC" : { + // "total" : 0, + // "available" : 0 + // }, + // "USD" : { + // "total" : 0, + // "available" : 0 + // }, + // "USDT" : { + // "total" : 0, + // "available" : 0 + // } + // }, + // "rights" : { + // "info" : 1, + // "trade" : 1 + // }, + // "transactionCount" : 0, + // "openOrders" : 0, + // "serverTime" : 1537451465 + // } + // } + // + const balances = this.safeValue (response, 'return'); + const result = { 'info': response }; + const funds = this.safeValue (balances, 'funds'); + const currencyIds = Object.keys (funds); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (funds, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseTicker (ticker, market = undefined) { + // + // { high: 0.03492, + // low: 0.03245, + // avg: 29.46133, + // vol: 500.8661, + // vol_cur: 17.000797104, + // last: 0.03364, + // buy: 0.03362, + // sell: 0.03381, + // updated: 1537521993, + // pair: "ethbtc" } + // + const timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'pair'); + market = this.parseMarket (marketId); + if (market !== undefined) { + symbol = market['symbol']; + } + // dsx average is inverted, liqui average is not + let average = this.safeFloat (ticker, 'avg'); + if (average !== undefined) { + if (average > 0) { + average = 1 / average; + } + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'vol_cur'), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "amount" : 0.0128, + // "price" : 6483.99000, + // "timestamp" : 1540334614, + // "tid" : 35684364, + // "type" : "ask" + // } + // + // fetchMyTrades (private) + // + // { + // "number": "36635882", // <-- this is present if the trade has come from the '/order/status' call + // "id": "36635882", // <-- this may have been artifically added by the parseTrades method + // "pair": "btcusd", + // "type": "buy", + // "volume": 0.0595, + // "rate": 9750, + // "orderId": 77149299, + // "timestamp": 1519612317, + // "commission": 0.00020825, + // "commissionCurrency": "btc" + // } + // + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let side = this.safeString (trade, 'type'); + if (side === 'ask') { + side = 'sell'; + } else if (side === 'bid') { + side = 'buy'; + } + const price = this.safeFloat2 (trade, 'rate', 'price'); + const id = this.safeString2 (trade, 'number', 'id'); + const orderId = this.safeString (trade, 'orderId'); + const marketId = this.safeString (trade, 'pair'); + market = this.parseMarket (marketId); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const amount = this.safeFloat2 (trade, 'amount', 'volume'); + const type = 'limit'; // all trades are still limit trades + let takerOrMaker = undefined; + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const isYourOrder = this.safeValue (trade, 'is_your_order'); + if (isYourOrder !== undefined) { + takerOrMaker = 'taker'; + if (isYourOrder) { + takerOrMaker = 'maker'; + } + if (fee === undefined) { + fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); + } + } + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + parseTrades (trades, market = undefined, since = undefined, limit = undefined, params = {}) { + let result = []; + if (Array.isArray (trades)) { + for (let i = 0; i < trades.length; i++) { + result.push (this.parseTrade (trades[i], market)); + } + } else { + const ids = Object.keys (trades); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const trade = this.parseTrade (trades[id], market); + result.push (this.extend (trade, { 'id': id }, params)); + } + } + result = this.sortBy (result, 'timestamp'); + const symbol = (market !== undefined) ? market['symbol'] : undefined; + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const market_id_in_reponse = (market['id'] in response); + if (!market_id_in_reponse) { + throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); + } + const orderbook = response[market['id']]; + return this.parseOrderBook (orderbook); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join ('-'); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const result = {}; + ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + if (id in this.markets_by_id) { + const market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseOrderBook (response[id]); + } + return result; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = this.ids; + if (symbols === undefined) { + const numIds = ids.length; + ids = ids.join ('-'); + const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); + // max URL length is 2048 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + const tickers = await this.publicGetTickerPair (this.extend (request, params)); + // + // { + // "bchbtc" : { + // "high" : 0.02989, + // "low" : 0.02736, + // "avg" : 33.90585, + // "vol" : 0.65982205, + // "vol_cur" : 0.0194604180960, + // "last" : 0.03000, + // "buy" : 0.02980, + // "sell" : 0.03001, + // "updated" : 1568104614, + // "pair" : "bchbtc" + // }, + // "ethbtc" : { + // "high" : 0.01772, + // "low" : 0.01742, + // "avg" : 56.89082, + // "vol" : 229.247115044, + // "vol_cur" : 4.02959737298943, + // "last" : 0.01769, + // "buy" : 0.01768, + // "sell" : 0.01776, + // "updated" : 1568104614, + // "pair" : "ethbtc" + // } + // } + // + const result = {}; + const keys = Object.keys (tickers); + for (let k = 0; k < keys.length; k++) { + const id = keys[k]; + const ticker = tickers[id]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTradesPair (this.extend (request, params)); + if (Array.isArray (response)) { + const numElements = response.length; + if (numElements === 0) { + return []; + } + } + return this.parseTrades (response[market['id']], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "high" : 0.01955, + // "open" : 0.01955, + // "low" : 0.01955, + // "close" : 0.01955, + // "amount" : 2.5, + // "timestamp" : 1565155740000 + // } + // + return [ + this.safeInteger (ohlcv, 'timestamp'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'amount'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'period': this.timeframes[timeframe], + }; + let method = 'publicGetLastBarsPairPeriodAmount'; + if (since === undefined) { + if (limit === undefined) { + limit = 100; // required, max 2000 + } + request['amount'] = limit; + } else { + method = 'publicGetPeriodBarsPairPeriodStartEnd'; + // in their docs they expect milliseconds + // but it returns empty arrays with milliseconds + // however, it does work properly with seconds + request['start'] = parseInt (since / 1000); + if (limit === undefined) { + request['end'] = this.seconds (); + } else { + const duration = this.parseTimeframe (timeframe) * 1000; + const end = this.sum (since, duration * limit); + request['end'] = parseInt (end / 1000); + } + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "ethbtc": [ + // { + // "high" : 0.01955, + // "open" : 0.01955, + // "low" : 0.01955, + // "close" : 0.01955, + // "amount" : 2.5, + // "timestamp" : 1565155740000 + // }, + // { + // "high" : 0.01967, + // "open" : 0.01967, + // "low" : 0.01967, + // "close" : 0.01967, + // "amount" : 0, + // "timestamp" : 1565155680000 + // } + // ] + // } + // + const candles = this.safeValue (response, market['id'], []); + return this.parseOHLCVs (candles, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (type === 'market' && price === undefined) { + throw new ArgumentsRequired (this.id + ' createOrder requires a price argument even for market orders, that is the worst price that you agree to fill your order for'); + } + const request = { + 'pair': market['id'], + 'type': side, + 'volume': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + 'orderType': type, + }; + price = parseFloat (price); + amount = parseFloat (amount); + const response = await this.privatePostOrderNew (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "received": 0, + // "remains": 10, + // "funds": { + // "BTC": { + // "total": 100, + // "available": 95 + // }, + // "USD": { + // "total": 10000, + // "available": 9995 + // }, + // "EUR": { + // "total": 1000, + // "available": 995 + // }, + // "LTC": { + // "total": 1000, + // "available": 995 + // } + // }, + // "orderId": 0, // https://github.com/ccxt/ccxt/issues/3677 + // } + // } + // + let status = 'open'; + let filled = 0.0; + let remaining = amount; + const responseReturn = this.safeValue (response, 'return'); + let id = this.safeString2 (responseReturn, 'orderId', 'order_id'); + if (id === '0') { + id = this.safeString (responseReturn, 'initOrderId', 'init_order_id'); + status = 'closed'; + } + filled = this.safeFloat (responseReturn, 'received', 0.0); + remaining = this.safeFloat (responseReturn, 'remains', amount); + const timestamp = this.milliseconds (); + return { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * filled, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'fee': undefined, + // 'trades': this.parseTrades (order['trades'], market), + }; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', // Active + '1': 'closed', // Filled + '2': 'canceled', // Killed + '3': 'canceling', // Killing + '7': 'canceled', // Rejected + }; + return this.safeString (statuses, status, status); + } + + parseMarket (id) { + if (id in this.markets_by_id) { + return this.markets_by_id[id]; + } else { + // the following is a fix for + // https://github.com/ccxt/ccxt/pull/5786 + // https://github.com/ccxt/ccxt/issues/5770 + let markets_by_other_id = this.safeValue (this.options, 'markets_by_other_id'); + if (markets_by_other_id === undefined) { + this.options['markets_by_other_id'] = this.indexBy (this.markets, 'otherId'); + markets_by_other_id = this.options['markets_by_other_id']; + } + if (id in markets_by_other_id) { + return markets_by_other_id[id]; + } + } + return undefined; + } + + parseOrder (order, market = undefined) { + // + // fetchOrder + // + // { + // "number": 36635882, + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit", + // "deals": [ + // { + // "pair": "btcusd", + // "type": "buy", + // "amount": 1, + // "rate": 1000.0, + // "orderId": 1, + // "timestamp": 1496672724, + // "commission": 0.001, + // "commissionCurrency": "btc" + // } + // ] + // } + // + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.safeTimestamp (order, 'timestampCreated'); + const marketId = this.safeString (order, 'pair'); + market = this.parseMarket (marketId); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const remaining = this.safeFloat (order, 'remainingVolume'); + const amount = this.safeFloat (order, 'volume'); + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + cost = price * filled; + } + } + const orderType = this.safeString (order, 'orderType'); + const side = this.safeString (order, 'type'); + let fee = undefined; + const deals = this.safeValue (order, 'deals', []); + const numDeals = deals.length; + let trades = undefined; + let lastTradeTimestamp = undefined; + if (numDeals > 0) { + trades = this.parseTrades (deals); + let feeCost = undefined; + let feeCurrency = undefined; + for (let i = 0; i < trades.length; i++) { + const trade = trades[i]; + if (feeCost === undefined) { + feeCost = 0; + } + feeCost = this.sum (feeCost, trade['fee']['cost']); + feeCurrency = trade['fee']['currency']; + lastTradeTimestamp = trade['timestamp']; + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + } + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': parseInt (id), + }; + const response = await this.privatePostOrderStatus (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit", + // "deals": [ + // { + // "pair": "btcusd", + // "type": "buy", + // "amount": 1, + // "rate": 1000.0, + // "orderId": 1, + // "timestamp": 1496672724, + // "commission": 0.001, + // "commissionCurrency": "btc" + // } + // ] + // } + // } + // + return this.parseOrder (this.extend ({ + 'id': id, + }, response['return'])); + } + + parseOrdersById (orders, symbol = undefined, since = undefined, limit = undefined) { + const ids = Object.keys (orders); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.parseOrder (this.extend ({ + 'id': id.toString (), + }, orders[id])); + result.push (order); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'count': 10, // Decimal, The maximum number of orders to return + // 'fromId': 123, // Decimal, ID of the first order of the selection + // 'endId': 321, // Decimal, ID of the last order of the selection + // 'order': 'ASC', // String, Order in which orders shown. Possible values are "ASC" — from first to last, "DESC" — from last to first. + }; + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "0": { + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit" + // } + // } + // } + // + return this.parseOrdersById (this.safeValue (response, 'return', {}), symbol, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'count': 10, // Decimal, The maximum number of orders to return + // 'fromId': 123, // Decimal, ID of the first order of the selection + // 'endId': 321, // Decimal, ID of the last order of the selection + // 'order': 'ASC', // String, Order in which orders shown. Possible values are "ASC" — from first to last, "DESC" — from last to first. + }; + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privatePostHistoryOrders (this.extend (request, params)); + // + // { + // "success": 1, + // "return": { + // "0": { + // "pair": "btcusd", + // "type": "buy", + // "remainingVolume": 10, + // "volume": 10, + // "rate": 1000.0, + // "timestampCreated": 1496670, + // "status": 0, + // "orderType": "limit" + // } + // } + // } + // + return this.parseOrdersById (this.safeValue (response, 'return', {}), symbol, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + // some derived classes use camelcase notation for request fields + const request = { + // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) + // 'count': 1000, // the number of trades for display numerical, default = 1000 + // 'from_id': trade ID, from which the display starts numerical 0 + // 'end_id': trade ID on which the display ends numerical ∞ + // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) + // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) + // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) + // 'pair': 'eth_btc', // default = all markets + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['count'] = parseInt (limit); + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostHistoryTrades (this.extend (request, params)); + let trades = []; + if ('return' in response) { + trades = response['return']; + } + return this.parseTrades (trades, market, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.privatePostHistoryTransactions (this.extend (request, params)); + // + // { + // "success": 1, + // "return": [ + // { + // "id": 1, + // "timestamp": 11, + // "type": "Withdraw", + // "amount": 1, + // "currency": "btc", + // "confirmationsCount": 6, + // "address": "address", + // "status": 2, + // "commission": 0.0001 + // } + // ] + // } + // + const transactions = this.safeValue (response, 'return', []); + return this.parseTransactions (transactions, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + '1': 'failed', + '2': 'ok', + '3': 'pending', + '4': 'failed', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // "id": 1, + // "timestamp": 11, // 11 in their docs ( + // "type": "Withdraw", + // "amount": 1, + // "currency": "btc", + // "confirmationsCount": 6, + // "address": "address", + // "status": 2, + // "commission": 0.0001 + // } + // + const timestamp = this.safeTimestamp (transaction, 'timestamp'); + let type = this.safeString (transaction, 'type'); + if (type !== undefined) { + if (type === 'Incoming') { + type = 'deposit'; + } else if (type === 'Withdraw') { + type = 'withdrawal'; + } + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + return { + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'txid'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'address'), + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'fee': { + 'currency': code, + 'cost': this.safeFloat (transaction, 'commission'), + 'rate': undefined, + }, + 'info': transaction, + }; + } + + async createDepositAddress (code, params = {}) { + const request = { + 'new': 1, + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + return response; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.dwapiPostDepositCryptoaddress (this.extend (request, params)); + const result = this.safeValue (response, 'return', {}); + const address = this.safeString (result, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, // not documented in DSX API + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const commission = this.safeValue (params, 'commission'); + if (commission === undefined) { + throw new ArgumentsRequired (this.id + ' withdraw() requires a `commission` (withdrawal fee) parameter (string)'); + } + params = this.omit (params, commission); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + 'commission': commission, + }; + if (tag !== undefined) { + request['address'] += ':' + tag; + } + const response = await this.dwapiPostWithdrawCrypto (this.extend (request, params)); + // + // [ + // { + // "success": 1, + // "return": { + // "transactionId": 2863073 + // } + // } + // ] + // + const data = this.safeValue (response, 'return', {}); + const id = this.safeString (data, 'transactionId'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.omit (params, this.extractParams (path)); + if (api === 'private' || api === 'dwapi') { + url += '/' + this.version + '/' + this.implodeParams (path, params); + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + }, query)); + const signature = this.decode (this.hmac (this.encode (body), this.encode (this.secret), 'sha512', 'base64')); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': signature, + }; + } else if (api === 'public') { + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + url += '/' + this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + if (Object.keys (query).length) { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // 1 - Liqui only returns the integer 'success' key from their private API + // + // { "success": 1, ... } httpCode === 200 + // { "success": 0, ... } httpCode === 200 + // + // 2 - However, exchanges derived from Liqui, can return non-integers + // + // It can be a numeric string + // { "sucesss": "1", ... } + // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) + // + // Or just a string + // { "success": "true", ... } + // { "success": "false", ... }, httpCode >= 200 + // + // Or a boolean + // { "success": true, ... } + // { "success": false, ... }, httpCode >= 200 + // + // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types + // + // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui + // + // To cover points 1, 2, 3 and 4 combined this handler should work like this: + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/exmo.js b/TaskServer/node_modules/ccxt/js/exmo.js new file mode 100644 index 000000000..c354a0393 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/exmo.js @@ -0,0 +1,1413 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, NotSupported, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class exmo extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'exmo', + 'name': 'EXMO', + 'countries': [ 'ES', 'RU' ], // Spain, Russia + 'rateLimit': 350, // once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchClosedOrders': 'emulated', + 'fetchDepositAddress': true, + 'fetchOpenOrders': true, + 'fetchOrder': 'emulated', + 'fetchOrders': 'emulated', + 'fetchOrderTrades': true, + 'fetchOrderBooks': true, + 'fetchMyTrades': true, + 'fetchTickers': true, + 'withdraw': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchFundingFees': true, + 'fetchCurrencies': true, + 'fetchTransactions': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg', + 'api': { + 'public': 'https://api.exmo.com', + 'private': 'https://api.exmo.com', + 'web': 'https://exmo.me', + }, + 'www': 'https://exmo.me', + 'referral': 'https://exmo.me/?ref=131685', + 'doc': [ + 'https://exmo.me/en/api_doc?ref=131685', + 'https://github.com/exmo-dev/exmo_api_lib/tree/master/nodejs', + ], + 'fees': 'https://exmo.com/en/docs/fees', + }, + 'api': { + 'web': { + 'get': [ + 'ctrl/feesAndLimits', + 'en/docs/fees', + ], + }, + 'public': { + 'get': [ + 'currency', + 'order_book', + 'pair_settings', + 'ticker', + 'trades', + ], + }, + 'private': { + 'post': [ + 'user_info', + 'order_create', + 'order_cancel', + 'user_open_orders', + 'user_trades', + 'user_cancelled_orders', + 'order_trades', + 'required_amount', + 'deposit_address', + 'withdraw_crypt', + 'withdraw_get_txid', + 'excode_create', + 'excode_load', + 'wallet_history', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, // fixed funding fees for crypto, see fetchFundingFees below + }, + }, + 'options': { + 'useWebapiForFetchingFees': false, // TODO: figure why Exmo bans us when we try to fetch() their web urls + 'feesAndLimits': { + 'success': 1, + 'ctlr': 'feesAndLimits', + 'error': '', + 'data': { + 'limits': [ + { 'pair': 'BTC/USD', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'BTC/EUR', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'BTC/RUB', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '2000000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'BTC/UAH', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '1500000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'BTC/PLN', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.001', 'max_p': '90000', 'min_a': '1', 'max_a': '2000000' }, + { 'pair': 'BTC/TRY', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '800000', 'min_a': '40', 'max_a': '6000000' }, + { 'pair': 'ROOBEE/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.0001', 'max_a': '10' }, + { 'pair': 'ROOBEE/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'USDC/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'USDC/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '1000' }, + { 'pair': 'USDC/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'USDC/USDT', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'ETZ/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '10' }, + { 'pair': 'ETZ/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ETZ/USDT', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '1000' }, + { 'pair': 'PTI/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' }, + { 'pair': 'PTI/USDT', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' }, + { 'pair': 'PTI/EOS', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '5000', 'min_a': '0.01', 'max_a': '20000' }, + { 'pair': 'ATMCASH/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' }, + { 'pair': 'TRX/UAH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'ETH/TRY', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.1', 'max_p': '80000', 'min_a': '10', 'max_a': '6000000' }, + { 'pair': 'XRP/TRY', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '6000000' }, + { 'pair': 'XLM/TRY', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '6000000' }, + { 'pair': 'MNC/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'MNC/ETH', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '1000' }, + { 'pair': 'MNC/USD', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' }, + { 'pair': 'DAI/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'DAI/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '5000' }, + { 'pair': 'DAI/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'DAI/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '0.5', 'max_a': '30000000' }, + { 'pair': 'MKR/BTC', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'MKR/DAI', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.5', 'max_p': '500000', 'min_a': '0.005', 'max_a': '500000' }, + { 'pair': 'QTUM/BTC', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'QTUM/ETH', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'QTUM/USD', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '10000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'HB/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'SMART/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'SMART/USD', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'SMART/EUR', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'SMART/RUB', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'XEM/BTC', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00015', 'max_a': '100' }, + { 'pair': 'XEM/USD', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'XEM/EUR', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'GUSD/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' }, + { 'pair': 'GUSD/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'GUSD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'LSK/BTC', 'min_q': '1', 'max_q': '200000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' }, + { 'pair': 'LSK/USD', 'min_q': '1', 'max_q': '200000', 'min_p': '0.1', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'LSK/RUB', 'min_q': '1', 'max_q': '200000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '50000000' }, + { 'pair': 'NEO/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'NEO/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'NEO/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1500000', 'min_a': '50', 'max_a': '50000000' }, + { 'pair': 'ADA/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ADA/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'ADA/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'ZRX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ZRX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'GNT/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'GNT/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'TRX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'TRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'TRX/RUB', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'GAS/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'GAS/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'INK/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'INK/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'INK/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'MNX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'MNX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'MNX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'OMG/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'OMG/ETH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'OMG/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'XLM/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'XLM/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'XLM/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'EOS/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'EOS/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'BTG/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'BTG/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'DXT/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'DXT/USD', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'BTCZ/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.1', 'max_a': '100' }, + { 'pair': 'BCH/BTC', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '5', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'BCH/USD', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '30000', 'min_a': '0.0001', 'max_a': '500000' }, + { 'pair': 'BCH/RUB', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '2000000', 'min_a': '0.0001', 'max_a': '50000000' }, + { 'pair': 'BCH/ETH', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.0000001', 'max_p': '200', 'min_a': '0.0001', 'max_a': '5000' }, + { 'pair': 'DASH/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'DASH/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'DASH/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' }, + { 'pair': 'ETH/BTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ETH/LTC', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '100000' }, + { 'pair': 'ETH/USD', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'ETH/EUR', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '10000', 'min_a': '1', 'max_a': '500000' }, + { 'pair': 'ETH/RUB', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '1', 'max_a': '50000000' }, + { 'pair': 'ETH/UAH', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '1000000', 'min_a': '90', 'max_a': '15000000' }, + { 'pair': 'ETH/PLN', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.001', 'max_p': '8000', 'min_a': '1', 'max_a': '2000000' }, + { 'pair': 'ETC/BTC', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '0.5', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ETC/USD', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'ETC/RUB', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '50000000' }, + { 'pair': 'LTC/BTC', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'LTC/USD', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'LTC/EUR', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'LTC/RUB', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' }, + { 'pair': 'ZEC/BTC', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'ZEC/USD', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'ZEC/EUR', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'ZEC/RUB', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' }, + { 'pair': 'XRP/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'XRP/USD', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, + { 'pair': 'XRP/RUB', 'min_q': '1', 'max_q': '100000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '50000000' }, + { 'pair': 'XMR/BTC', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'XMR/USD', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'XMR/EUR', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'BTC/USDT', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.01', 'max_p': '30000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'ETH/USDT', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'USDT/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'USDT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'USD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' }, + { 'pair': 'DOGE/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'WAVES/BTC', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' }, + { 'pair': 'WAVES/RUB', 'min_q': '0.5', 'max_q': '10000', 'min_p': '1', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' }, + { 'pair': 'KICK/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'KICK/ETH', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' }, + { 'pair': 'KICK/USDT', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' }, + { 'pair': 'EOS/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'BCH/EUR', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'XRP/EUR', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, + { 'pair': 'XRP/UAH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '15000000' }, + { 'pair': 'XEM/UAH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'BCH/USDT', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'DASH/USDT', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' }, + { 'pair': 'BCH/UAH', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.1', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'XRP/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' }, + { 'pair': 'USDT/UAH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '1', 'max_p': '3000', 'min_a': '2', 'max_a': '15000000' }, + { 'pair': 'USDT/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'ZRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' }, + { 'pair': 'BTG/ETH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.01', 'max_a': '5000' }, + { 'pair': 'WAVES/USD', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.001', 'max_p': '3500', 'min_a': '0.5', 'max_a': '500000' }, + { 'pair': 'DOGE/USD', 'min_q': '100', 'max_q': '1000000000', 'min_p': '0.0000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' }, + { 'pair': 'XRP/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' }, + { 'pair': 'DASH/UAH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '200000', 'min_a': '10', 'max_a': '15000000' }, + { 'pair': 'XMR/ETH', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' }, + { 'pair': 'WAVES/ETH', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.00001', 'max_p': '30', 'min_a': '0.0035', 'max_a': '3500' }, + { 'pair': 'DCR/BTC', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' }, + { 'pair': 'DCR/RUB', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '3000000' }, + { 'pair': 'DCR/UAH', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.25', 'max_a': '1000000' }, + { 'pair': 'ZAG/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'EXM/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0000001', 'max_a': '100' }, + { 'pair': 'VLX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'BTT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' }, + { 'pair': 'BTT/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' }, + { 'pair': 'BTT/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' }, + ], + 'fees': [ + { + 'group': 'crypto', + 'title': 'Криптовалюта', + 'items': [ + { 'prov': 'EXM', 'dep': '0%', 'wd': '1 EXM' }, + { 'prov': 'BTC', 'dep': '0%', 'wd': '0.0005 BTC' }, + { 'prov': 'LTC', 'dep': '0%', 'wd': '0.01 LTC' }, + { 'prov': 'DOGE', 'dep': '0%', 'wd': '1 DOGE' }, + { 'prov': 'DASH', 'dep': '0%', 'wd': '0.01 DASH' }, + { 'prov': 'ETH', 'dep': '0%', 'wd': '0.01 ETH' }, + { 'prov': 'WAVES', 'dep': '0%', 'wd': '0.001 WAVES' }, + { 'prov': 'ZEC', 'dep': '0%', 'wd': '0.001 ZEC' }, + { 'prov': 'USDT', 'dep': '0%', 'wd': '5 USDT' }, + { 'prov': 'XMR', 'dep': '0%', 'wd': '0.05 XMR' }, + { 'prov': 'XRP', 'dep': '0%', 'wd': '0.02 XRP' }, + { 'prov': 'KICK', 'dep': '0%', 'wd': '50 KICK' }, + { 'prov': 'ETC', 'dep': '0%', 'wd': '0.01 ETC' }, + { 'prov': 'BCH', 'dep': '0%', 'wd': '0.001 BCH' }, + { 'prov': 'BTG', 'dep': '0%', 'wd': '0.001 BTG' }, + { 'prov': 'EOS', 'dep': '0%', 'wd': '0.05 EOS' }, + { 'prov': 'BTCZ', 'dep': '0%', 'wd': '5 BTCZ' }, + { 'prov': 'DXT', 'dep': '20 DXT', 'wd': '20 DXT' }, + { 'prov': 'XLM', 'dep': '0%', 'wd': '0.01 XLM' }, + { 'prov': 'MNX', 'dep': '0%', 'wd': '0.01 MNX' }, + { 'prov': 'OMG', 'dep': '0.1 OMG', 'wd': '0.5 OMG' }, + { 'prov': 'TRX', 'dep': '0%', 'wd': '1 TRX' }, + { 'prov': 'ADA', 'dep': '0%', 'wd': '1 ADA' }, + { 'prov': 'INK', 'dep': '10 INK', 'wd': '50 INK' }, + { 'prov': 'NEO', 'dep': '0%', 'wd': '0%' }, + { 'prov': 'GAS', 'dep': '0%', 'wd': '0%' }, + { 'prov': 'ZRX', 'dep': '0%', 'wd': '1 ZRX' }, + { 'prov': 'GNT', 'dep': '0%', 'wd': '1 GNT' }, + { 'prov': 'GUSD', 'dep': '0%', 'wd': '0.5 GUSD' }, + { 'prov': 'LSK', 'dep': '0%', 'wd': '0.1 LSK' }, + { 'prov': 'XEM', 'dep': '0%', 'wd': '5 XEM' }, + { 'prov': 'SMART', 'dep': '0%', 'wd': '0.5 SMART' }, + { 'prov': 'QTUM', 'dep': '0%', 'wd': '0.01 QTUM' }, + { 'prov': 'HB', 'dep': '0%', 'wd': '10 HB' }, + { 'prov': 'DAI', 'dep': '0%', 'wd': '1 DAI' }, + { 'prov': 'MKR', 'dep': '0%', 'wd': '0.005 MKR' }, + { 'prov': 'MNC', 'dep': '0%', 'wd': '15 MNC' }, + { 'prov': 'PTI', 'dep': '0%', 'wd': '10 PTI' }, + { 'prov': 'ATMCASH', 'dep': '0%', 'wd': '5 ATMCASH' }, + { 'prov': 'ETZ', 'dep': '0%', 'wd': '1 ETZ' }, + { 'prov': 'USDC', 'dep': '0%', 'wd': '0.5 USDC' }, + { 'prov': 'ROOBEE', 'dep': '0%', 'wd': '200 ROOBEE' }, + { 'prov': 'DCR', 'dep': '0%', 'wd': '0.01 DCR' }, + { 'prov': 'ZAG', 'dep': '0%', 'wd': '0%' }, + { 'prov': 'BTT', 'dep': '0%', 'wd': '100 BTT' }, + { 'prov': 'VLX', 'dep': '0%', 'wd': '1 VLX' }, + ], + }, + { + 'group': 'usd', + 'title': 'USD', + 'items': [ + { 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + { 'prov': 'Perfect Money', 'dep': '-', 'wd': '0.5%' }, + { 'prov': 'AdvCash', 'dep': '3.45%', 'wd': '2.95%' }, + { 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' }, + { 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' }, + { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' }, + ], + }, + { + 'group': 'eur', + 'title': 'EUR', + 'items': [ + { 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' }, + { 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' }, + { 'prov': 'AdvCash', 'dep': '0.45%', 'wd': '-' }, + { 'prov': 'Wire Transfer', 'dep': '6.95 EUR', 'wd': '-' }, + { 'prov': 'SEPA', 'dep': '6.95 EUR', 'wd': '-' }, + { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' }, + { 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'rub', + 'title': 'RUB', + 'items': [ + { 'prov': 'Yandex Money', 'dep': '2.95%', 'wd': '1.95%' }, + { 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' }, + { 'prov': 'AdvCash', 'dep': '3.95%', 'wd': '0.95%' }, + { 'prov': 'Qiwi', 'dep': '3.95%', 'wd': '2.95%' }, + { 'prov': 'Visa/MasterCard', 'dep': '3.95%', 'wd': '4.95% + 60 RUB' }, + { 'prov': 'WebMoney', 'dep': '-', 'wd': '3.5%' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'pln', + 'title': 'PLN', + 'items': [ + { 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' }, + { 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'try', + 'title': 'TRY', + 'items': [ + { 'prov': 'QR ile yatırma', 'dep': '5.95%', 'wd': '-' }, + { 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + ], + }, + { + 'group': 'uah', + 'title': 'UAH', + 'items': [ + { 'prov': 'Terminal', 'dep': '2.6%', 'wd': '-' }, + { 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' }, + { 'prov': 'AdvCash', 'dep': '-', 'wd': '2.95%' }, + { 'prov': 'Visa/MasterCard', 'dep': '2.6%', 'wd': '3.95% + 20 UAH' }, + { 'prov': 'Enfins', 'dep': '0%', 'wd': '1.95%' }, + ], + }, + ], + }, + }, + }, + 'exceptions': { + 'exact': { + '40005': AuthenticationError, // Authorization error, incorrect signature + '40009': InvalidNonce, // + '40015': ExchangeError, // API function do not exist + '40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"} + '40017': AuthenticationError, // Wrong API Key + '50052': InsufficientFunds, + '50054': InsufficientFunds, + '50304': OrderNotFound, // "Order was not found '123456789'" (fetching order trades for an order that does not have trades yet) + '50173': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) + '50319': InvalidOrder, // Price by order is less than permissible minimum for this pair + '50321': InvalidOrder, // Price by order is more than permissible maximum for this pair + }, + 'broad': { + 'API rate limit exceeded': RateLimitExceeded, // {"result":false,"error":"API rate limit exceeded for 99.33.55.224. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400} + }, + }, + }); + } + + async fetchTradingFees (params = {}) { + if (this.options['useWebapiForFetchingFees']) { + const response = await this.webGetEnDocsFees (params); + let parts = response.split (''); + let numParts = parts.length; + if (numParts !== 2) { + throw new NotSupported (this.id + ' fetchTradingFees format has changed'); + } + const rest = parts[1]; + parts = rest.split (''); + numParts = parts.length; + if (numParts < 2) { + throw new NotSupported (this.id + ' fetchTradingFees format has changed'); + } + const fee = parseFloat (parts[0].replace ('%', '')) * 0.01; + const taker = fee; + const maker = fee; + return { + // 'info': response, + 'maker': maker, + 'taker': taker, + }; + } else { + return { + 'maker': this.fees['trading']['maker'], + 'taker': this.fees['trading']['taker'], + }; + } + } + + parseFixedFloatValue (input) { + if ((input === undefined) || (input === '-')) { + return undefined; + } + const isPercentage = (input.indexOf ('%') >= 0); + const parts = input.split (' '); + const value = parts[0].replace ('%', ''); + const result = parseFloat (value); + if ((result > 0) && isPercentage) { + throw new ExchangeError (this.id + ' parseFixedFloatValue detected an unsupported non-zero percentage-based fee ' + input); + } + return result; + } + + async fetchFundingFees (params = {}) { + let response = undefined; + if (this.options['useWebapiForFetchingFees']) { + response = await this.webGetCtrlFeesAndLimits (params); + } else { + response = this.options['feesAndLimits']; + } + // the code below assumes all non-zero crypto fees are fixed (for now) + const withdraw = {}; + const deposit = {}; + const groups = this.safeValue (response['data'], 'fees'); + const groupsByGroup = this.indexBy (groups, 'group'); + const items = groupsByGroup['crypto']['items']; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const code = this.safeCurrencyCode (this.safeString (item, 'prov')); + const withdrawalFee = this.safeString (item, 'wd'); + const depositFee = this.safeString (item, 'dep'); + if (withdrawalFee !== undefined) { + if (withdrawalFee.length > 0) { + withdraw[code] = this.parseFixedFloatValue (withdrawalFee); + } + } + if (depositFee !== undefined) { + if (depositFee.length > 0) { + deposit[code] = this.parseFixedFloatValue (depositFee); + } + } + } + // sets fiat fees to undefined + const fiatGroups = this.toArray (this.omit (groupsByGroup, 'crypto')); + for (let i = 0; i < fiatGroups.length; i++) { + const code = this.safeCurrencyCode (this.safeString (fiatGroups[i], 'title')); + withdraw[code] = undefined; + deposit[code] = undefined; + } + const result = { + 'info': response, + 'withdraw': withdraw, + 'deposit': deposit, + }; + // cache them for later use + this.options['fundingFees'] = result; + return result; + } + + async fetchCurrencies (params = {}) { + const fees = await this.fetchFundingFees (params); + // todo redesign the 'fee' property in currencies + const ids = Object.keys (fees['withdraw']); + const limitsByMarketId = this.indexBy (fees['info']['data']['limits'], 'pair'); + const marketIds = Object.keys (limitsByMarketId); + const minAmounts = {}; + const minPrices = {}; + const minCosts = {}; + const maxAmounts = {}; + const maxPrices = {}; + const maxCosts = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const limit = limitsByMarketId[marketId]; + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const maxAmount = this.safeFloat (limit, 'max_q'); + const maxPrice = this.safeFloat (limit, 'max_p'); + const maxCost = this.safeFloat (limit, 'max_a'); + const minAmount = this.safeFloat (limit, 'min_q'); + const minPrice = this.safeFloat (limit, 'min_p'); + const minCost = this.safeFloat (limit, 'min_a'); + minAmounts[base] = Math.min (this.safeFloat (minAmounts, base, minAmount), minAmount); + maxAmounts[base] = Math.max (this.safeFloat (maxAmounts, base, maxAmount), maxAmount); + minPrices[quote] = Math.min (this.safeFloat (minPrices, quote, minPrice), minPrice); + minCosts[quote] = Math.min (this.safeFloat (minCosts, quote, minCost), minCost); + maxPrices[quote] = Math.max (this.safeFloat (maxPrices, quote, maxPrice), maxPrice); + maxCosts[quote] = Math.max (this.safeFloat (maxCosts, quote, maxCost), maxCost); + } + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + const fee = this.safeValue (fees['withdraw'], code); + const active = true; + result[code] = { + 'id': id, + 'code': code, + 'name': code, + 'active': active, + 'fee': fee, + 'precision': 8, + 'limits': { + 'amount': { + 'min': this.safeFloat (minAmounts, code), + 'max': this.safeFloat (maxAmounts, code), + }, + 'price': { + 'min': this.safeFloat (minPrices, code), + 'max': this.safeFloat (maxPrices, code), + }, + 'cost': { + 'min': this.safeFloat (minCosts, code), + 'max': this.safeFloat (maxCosts, code), + }, + }, + 'info': id, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const fees = await this.fetchTradingFees (); + const response = await this.publicGetPairSettings (params); + const keys = Object.keys (response); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = response[id]; + const symbol = id.replace ('_', '/'); + const [ baseId, quoteId ] = symbol.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'taker': fees['taker'], + 'maker': fees['maker'], + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'min_quantity'), + 'max': this.safeFloat (market, 'max_quantity'), + }, + 'price': { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }, + 'cost': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserInfo (params); + const result = { 'info': response }; + const codes = Object.keys (this.currencies); + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + const currencyId = this.currencyId (code); + const account = this.account (); + if (currencyId in response['balances']) { + account['free'] = this.safeFloat (response['balances'], currencyId); + } + if (currencyId in response['reserved']) { + account['used'] = this.safeFloat (response['reserved'], currencyId); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + const result = this.safeValue (response, market['id']); + return this.parseOrderBook (result, undefined, 'bid', 'ask'); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join (','); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join (','); + } + const request = { + 'pair': ids, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + const result = {}; + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = marketId; + if (marketId in this.markets_by_id) { + const market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + result[symbol] = this.parseOrderBook (response[marketId], undefined, 'bid', 'ask'); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last_trade'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy_price'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell_price'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'vol_curr'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const market = this.market (symbol); + return this.parseTicker (response[market['id']], market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let fee = undefined; + let symbol = undefined; + const id = this.safeString (trade, 'trade_id'); + const orderId = this.safeString (trade, 'order_id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + const cost = this.safeFloat (trade, 'amount'); + const side = this.safeString (trade, 'type'); + const type = undefined; + if (market !== undefined) { + symbol = market['symbol']; + if (market['taker'] !== market['maker']) { + throw new ExchangeError (this.id + ' parseTrade can not deduce proper fee costs, taker and maker fees now differ'); + } + if ((side === 'buy') && (amount !== undefined)) { + fee = { + 'currency': market['base'], + 'cost': amount * market['taker'], + 'rate': market['taker'], + }; + } else if ((side === 'sell') && (cost !== undefined)) { + fee = { + 'currency': market['quote'], + 'cost': cost * market['taker'], + 'rate': market['taker'], + }; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + const data = this.safeValue (response, market['id'], []); + return this.parseTrades (data, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // a symbol is required but it can be a single string, or a non-empty array + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument (a single symbol or an array)'); + } + await this.loadMarkets (); + let pair = undefined; + let market = undefined; + if (Array.isArray (symbol)) { + const numSymbols = symbol.length; + if (numSymbols < 1) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a non-empty symbol array'); + } + const marketIds = this.marketIds (symbol); + pair = marketIds.join (','); + } else { + market = this.market (symbol); + pair = market['id']; + } + const request = { + 'pair': pair, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostUserTrades (this.extend (request, params)); + let result = []; + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const items = response[marketId]; + const trades = this.parseTrades (items, market, since, limit, { + 'symbol': symbol, + }); + result = this.arrayConcat (result, trades); + } + return this.filterBySinceLimit (result, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const prefix = (type === 'market') ? (type + '_') : ''; + const market = this.market (symbol); + if ((type === 'market') && (price === undefined)) { + price = 0; + } + const request = { + 'pair': market['id'], + 'quantity': this.amountToPrecision (symbol, amount), + 'type': prefix + side, + 'price': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostOrderCreate (this.extend (request, params)); + const id = this.safeString (response, 'order_id'); + const timestamp = this.milliseconds (); + amount = parseFloat (amount); + price = parseFloat (price); + const status = 'open'; + const order = { + 'id': id, + 'info': response, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * amount, + 'amount': amount, + 'remaining': amount, + 'filled': 0.0, + 'fee': undefined, + 'trades': undefined, + }; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { 'order_id': id }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + try { + const request = { + 'order_id': id.toString (), + }; + const response = await this.privatePostOrderTrades (this.extend (request, params)); + // + // { + // "type": "buy", + // "in_currency": "BTC", + // "in_amount": "1", + // "out_currency": "USD", + // "out_amount": "100", + // "trades": [ + // { + // "trade_id": 3, + // "date": 1435488248, + // "type": "buy", + // "pair": "BTC_USD", + // "order_id": 12345, + // "quantity": 1, + // "price": 100, + // "amount": 100 + // } + // ] + // } + // + const order = this.parseOrder (response); + return this.extend (order, { + 'id': id.toString (), + }); + } catch (e) { + if (e instanceof OrderNotFound) { + if (id in this.orders) { + return this.orders[id]; + } + } + } + throw new OrderNotFound (this.id + ' fetchOrder order id ' + id.toString () + ' not found in cache.'); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'order_id': id.toString (), + }; + const response = await this.privatePostOrderTrades (this.extend (request, params)); + const trades = this.safeValue (response, 'trades'); + return this.parseTrades (trades, market, since, limit); + } + + updateCachedOrders (openOrders, symbol) { + // update local cache with open orders + for (let j = 0; j < openOrders.length; j++) { + const id = openOrders[j]['id']; + this.orders[id] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + for (let k = 0; k < cachedOrderIds.length; k++) { + // match each cached order to an order in the open orders array + // possible reasons why a cached order may be missing in the open orders array: + // - order was closed or canceled -> update cache + // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip + const id = cachedOrderIds[k]; + let order = this.orders[id]; + if (!(id in openOrdersIndexedById)) { + // cached order is not in open orders array + // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order + if (symbol !== undefined && symbol !== order['symbol']) { + continue; + } + // order is cached but not present in the list of open orders -> mark the cached order as closed + if (order['status'] === 'open') { + order = this.extend (order, { + 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") + 'cost': undefined, + 'filled': order['amount'], + 'remaining': 0.0, + }); + if (order['cost'] === undefined) { + if (order['filled'] !== undefined) { + order['cost'] = order['filled'] * order['price']; + } + } + this.orders[id] = order; + } + } + } + return this.toArray (this.orders); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserOpenOrders (params); + const marketIds = Object.keys (response); + let orders = []; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let market = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + const parsedOrders = this.parseOrders (response[marketId], market); + orders = this.arrayConcat (orders, parsedOrders); + } + this.updateCachedOrders (orders, symbol); + return this.filterBySymbolSinceLimit (this.toArray (this.orders), symbol, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.fetchOrders (symbol, since, limit, params); + const orders = this.filterBy (this.orders, 'status', 'open'); + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.fetchOrders (symbol, since, limit, params); + const orders = this.filterBy (this.orders, 'status', 'closed'); + return this.filterBySymbolSinceLimit (orders, symbol, since, limit); + } + + parseOrder (order, market = undefined) { + // + // fetchOrders, fetchOpenOrders, fetchClosedOrders + // + // { + // "order_id": "14", + // "created": "1435517311", + // "type": "buy", + // "pair": "BTC_USD", + // "price": "100", + // "quantity": "1", + // "amount": "100" + // } + // + // fetchOrder + // + // { + // "type": "buy", + // "in_currency": "BTC", + // "in_amount": "1", + // "out_currency": "USD", + // "out_amount": "100", + // "trades": [ + // { + // "trade_id": 3, + // "date": 1435488248, + // "type": "buy", + // "pair": "BTC_USD", + // "order_id": 12345, + // "quantity": 1, + // "price": 100, + // "amount": 100 + // } + // ] + // } + // + let id = this.safeString (order, 'order_id'); + let timestamp = this.safeTimestamp (order, 'created'); + let symbol = undefined; + const side = this.safeString (order, 'type'); + if (market === undefined) { + let marketId = undefined; + if ('pair' in order) { + marketId = order['pair']; + } else if (('in_currency' in order) && ('out_currency' in order)) { + if (side === 'buy') { + marketId = order['in_currency'] + '_' + order['out_currency']; + } else { + marketId = order['out_currency'] + '_' + order['in_currency']; + } + } + if ((marketId !== undefined) && (marketId in this.markets_by_id)) { + market = this.markets_by_id[marketId]; + } + } + let amount = this.safeFloat (order, 'quantity'); + if (amount === undefined) { + const amountField = (side === 'buy') ? 'in_amount' : 'out_amount'; + amount = this.safeFloat (order, amountField); + } + let price = this.safeFloat (order, 'price'); + let cost = this.safeFloat (order, 'amount'); + let filled = 0.0; + const trades = []; + const transactions = this.safeValue (order, 'trades', []); + let feeCost = undefined; + let lastTradeTimestamp = undefined; + let average = undefined; + const numTransactions = transactions.length; + if (numTransactions > 0) { + feeCost = 0; + for (let i = 0; i < numTransactions; i++) { + const trade = this.parseTrade (transactions[i], market); + if (id === undefined) { + id = trade['order']; + } + if (timestamp === undefined) { + timestamp = trade['timestamp']; + } + if (timestamp > trade['timestamp']) { + timestamp = trade['timestamp']; + } + filled = this.sum (filled, trade['amount']); + feeCost = this.sum (feeCost, trade['fee']['cost']); + trades.push (trade); + } + lastTradeTimestamp = trades[numTransactions - 1]['timestamp']; + } + let remaining = undefined; + if (amount !== undefined) { + remaining = amount - filled; + } + let status = this.safeString (order, 'status'); // in case we need to redefine it for canceled orders + if (filled >= amount) { + status = 'closed'; + } else { + status = 'open'; + } + if (market === undefined) { + market = this.getMarketFromTrades (trades); + } + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } else { + if (filled > 0) { + if (average === undefined) { + average = cost / filled; + } + if (price === undefined) { + price = cost / filled; + } + } + } + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'average': average, + 'trades': trades, + 'fee': fee, + 'info': order, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostDepositAddress (params); + const depositAddress = this.safeString (response, code); + let address = undefined; + let tag = undefined; + if (depositAddress) { + const addressAndTag = depositAddress.split (','); + address = addressAndTag[0]; + const numParts = addressAndTag.length; + if (numParts > 1) { + tag = addressAndTag[1]; + } + } + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + getMarketFromTrades (trades) { + const tradesBySymbol = this.indexBy (trades, 'pair'); + const symbols = Object.keys (tradesBySymbol); + const numSymbols = symbols.length; + if (numSymbols === 1) { + return this.markets[symbols[0]]; + } + return undefined; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + let key = 'quote'; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, cost)), + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'amount': amount, + 'currency': currency['id'], + 'address': address, + }; + if (tag !== undefined) { + request['invoice'] = tag; + } + const response = await this.privatePostWithdrawCrypt (this.extend (request, params)); + return { + 'info': response, + 'id': response['task_id'], + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'transferred': 'ok', + 'paid': 'ok', + 'pending': 'pending', + 'processing': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchTransactions + // + // { + // "dt": 1461841192, + // "type": "deposit", + // "curr": "RUB", + // "status": "processing", + // "provider": "Qiwi (LA) [12345]", + // "amount": "1", + // "account": "", + // "txid": "ec46f784ad976fd7f7539089d1a129fe46...", + // } + // + const timestamp = this.safeTimestamp (transaction, 'dt'); + let amount = this.safeFloat (transaction, 'amount'); + if (amount !== undefined) { + amount = Math.abs (amount); + } + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const txid = this.safeString (transaction, 'txid'); + const type = this.safeString (transaction, 'type'); + const currencyId = this.safeString (transaction, 'curr'); + const code = this.safeCurrencyCode (currencyId, currency); + let address = this.safeString (transaction, 'account'); + if (address !== undefined) { + const parts = address.split (':'); + const numParts = parts.length; + if (numParts === 2) { + address = parts[1].replace (' ', ''); + } + } + let fee = undefined; + // fixed funding fees only (for now) + if (!this.fees['funding']['percentage']) { + const key = (type === 'withdrawal') ? 'withdraw' : 'deposit'; + let feeCost = this.safeFloat (this.options['fundingFees'][key], code); + // users don't pay for cashbacks, no fees for that + const provider = this.safeString (transaction, 'provider'); + if (provider === 'cashback') { + feeCost = 0; + } + if (feeCost !== undefined) { + // withdrawal amount includes the fee + if (type === 'withdrawal') { + amount = amount - feeCost; + } + fee = { + 'cost': feeCost, + 'currency': code, + 'rate': undefined, + }; + } + } + return { + 'info': transaction, + 'id': undefined, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, // refix it properly + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['date'] = parseInt (since / 1000); + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const response = await this.privatePostWalletHistory (this.extend (request, params)); + // + // { + // "result": true, + // "error": "", + // "begin": "1493942400", + // "end": "1494028800", + // "history": [ + // { + // "dt": 1461841192, + // "type": "deposit", + // "curr": "RUB", + // "status": "processing", + // "provider": "Qiwi (LA) [12345]", + // "amount": "1", + // "account": "", + // "txid": "ec46f784ad976fd7f7539089d1a129fe46...", + // }, + // { + // "dt": 1463414785, + // "type": "withdrawal", + // "curr": "USD", + // "status": "paid", + // "provider": "EXCODE", + // "amount": "-1", + // "account": "EX-CODE_19371_USDda...", + // "txid": "", + // }, + // ], + // } + // + return this.parseTransactions (response['history'], currency, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/'; + if (api !== 'web') { + url += this.version + '/'; + } + url += path; + if ((api === 'public') || (api === 'web')) { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + nonce () { + return this.milliseconds (); + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('result' in response) { + // + // {"result":false,"error":"Error 50052: Insufficient funds"} + // + let success = this.safeValue (response, 'result', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + let code = undefined; + const message = this.safeString (response, 'error'); + const errorParts = message.split (':'); + const numParts = errorParts.length; + if (numParts > 1) { + const errorSubParts = errorParts[0].split (' '); + const numSubParts = errorSubParts.length; + code = (numSubParts > 1) ? errorSubParts[1] : errorSubParts[0]; + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/exx.js b/TaskServer/node_modules/ccxt/js/exx.js new file mode 100644 index 000000000..0fd106329 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/exx.js @@ -0,0 +1,435 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, ExchangeNotAvailable } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class exx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'exx', + 'name': 'EXX', + 'countries': [ 'CN' ], + 'rateLimit': 1000 / 10, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchOpenOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/37770292-fbf613d0-2de4-11e8-9f79-f2dc451b8ccb.jpg', + 'api': { + 'public': 'https://api.exx.com/data/v1', + 'private': 'https://trade.exx.com/api', + }, + 'www': 'https://www.exx.com/', + 'doc': 'https://www.exx.com/help/restApi', + 'fees': 'https://www.exx.com/help/rate', + 'referral': 'https://www.exx.com/r/fde4260159e53ab8a58cc9186d35501f?recommQd=1', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'tickers', + 'ticker', + 'depth', + 'trades', + ], + }, + 'private': { + 'get': [ + 'order', + 'cancel', + 'getOrder', + 'getOpenOrders', + 'getBalance', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BCC': 0.0003, + 'BCD': 0.0, + 'BOT': 10.0, + 'BTC': 0.001, + 'BTG': 0.0, + 'BTM': 25.0, + 'BTS': 3.0, + 'EOS': 1.0, + 'ETC': 0.01, + 'ETH': 0.01, + 'ETP': 0.012, + 'HPY': 0.0, + 'HSR': 0.001, + 'INK': 20.0, + 'LTC': 0.005, + 'MCO': 0.6, + 'MONA': 0.01, + 'QASH': 5.0, + 'QCASH': 5.0, + 'QTUM': 0.01, + 'USDT': 5.0, + }, + }, + }, + 'commonCurrencies': { + 'TV': 'TIV', // Ti-Value + }, + 'exceptions': { + '103': AuthenticationError, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + const ids = Object.keys (response); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = response[id]; + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = market['isOpen'] === true; + const precision = { + 'amount': parseInt (market['amountScale']), + 'price': parseInt (market['priceScale']), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const symbol = market['symbol']; + const timestamp = this.safeInteger (ticker, 'date'); + ticker = ticker['ticker']; + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'riseRate'), + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const result = {}; + const timestamp = this.milliseconds (); + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + if (!(id in this.marketsById)) { + continue; + } + const market = this.marketsById[id]; + const symbol = market['symbol']; + const ticker = { + 'date': timestamp, + 'ticker': response[id], + }; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currency': this.marketId (symbol), + }; + const response = await this.publicGetDepth (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, 'tid'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetGetBalance (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'funds'); + const currencies = Object.keys (balances); + for (let i = 0; i < currencies.length; i++) { + const currencyId = currencies[i]; + const balance = balances[currencyId]; + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'balance'), + 'used': this.safeFloat (balance, 'freeze'), + 'total': this.safeFloat (balance, 'total'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + const symbol = market['symbol']; + const timestamp = parseInt (order['trade_date']); + const price = this.safeFloat (order, 'price'); + const cost = this.safeFloat (order, 'trade_money'); + const amount = this.safeFloat (order, 'total_amount'); + const filled = this.safeFloat (order, 'trade_amount', 0.0); + const remaining = parseFloat (this.amountToPrecision (symbol, amount - filled)); + let status = this.safeInteger (order, 'status'); + if (status === 1) { + status = 'canceled'; + } else if (status === 2) { + status = 'closed'; + } else { + status = 'open'; + } + let fee = undefined; + if ('fees' in order) { + fee = { + 'cost': this.safeFloat (order, 'fees'), + 'currency': market['quote'], + }; + } + return { + 'id': this.safeString (order, 'id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': order['type'], + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'type': side, + 'price': price, + 'amount': amount, + }; + const response = await this.privateGetOrder (this.extend (request, params)); + const id = this.safeString (response, 'id'); + const order = this.parseOrder ({ + 'id': id, + 'trade_date': this.milliseconds (), + 'total_amount': amount, + 'price': price, + 'type': side, + 'info': response, + }, market); + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'currency': market['id'], + }; + const response = await this.privateGetCancel (this.extend (request, params)); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'currency': market['id'], + }; + const response = await this.privateGetGetOrder (this.extend (request, params)); + return this.parseOrder (response, market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + }; + const response = await this.privateGetGetOpenOrders (this.extend (request, params)); + if (!Array.isArray (response)) { + return []; + } + return this.parseOrders (response, market, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const query = this.urlencode (this.keysort (this.extend ({ + 'accesskey': this.apiKey, + 'nonce': this.nonce (), + }, params))); + const signed = this.hmac (this.encode (query), this.encode (this.secret), 'sha512'); + url += '?' + query + '&signature=' + signed; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"result":false,"message":"�务端忙碌"} + // ... and other formats + // + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + if (code === '100') { + return; + } + if (code !== undefined) { + this.throwExactlyMatchedException (this.exceptions, code, feedback); + if (code === '308') { + // this is returned by the exchange when there are no open orders + // {"code":308,"message":"Not Found Transaction Record"} + return; + } else { + throw new ExchangeError (feedback); + } + } + const result = this.safeValue (response, 'result'); + if (result !== undefined) { + if (!result) { + if (message === '�务端忙碌') { + throw new ExchangeNotAvailable (feedback); + } else { + throw new ExchangeError (feedback); + } + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/fcoin.js b/TaskServer/node_modules/ccxt/js/fcoin.js new file mode 100644 index 000000000..692eca143 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/fcoin.js @@ -0,0 +1,715 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, ExchangeNotAvailable, ArgumentsRequired, InsufficientFunds, InvalidOrder, RateLimitExceeded, InvalidNonce, AuthenticationError, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class fcoin extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'fcoin', + 'name': 'FCoin', + 'countries': [ 'CN' ], + 'rateLimit': 2000, + 'userAgent': this.userAgents['chrome39'], + 'version': 'v2', + 'accounts': undefined, + 'accountsById': undefined, + 'hostname': 'fcoin.com', + 'has': { + 'CORS': false, + 'fetchDepositAddress': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOrderBook': true, + 'fetchOrderBooks': false, + 'fetchTradingLimits': false, + 'withdraw': false, + 'fetchCurrencies': false, + }, + 'timeframes': { + '1m': 'M1', + '3m': 'M3', + '5m': 'M5', + '15m': 'M15', + '30m': 'M30', + '1h': 'H1', + '1d': 'D1', + '1w': 'W1', + '1M': 'MN', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/42244210-c8c42e1e-7f1c-11e8-8710-a5fb63b165c4.jpg', + 'api': { + 'public': 'https://api.{hostname}', + 'private': 'https://api.{hostname}', + 'market': 'https://api.{hostname}', + 'openapi': 'https://www.{hostname}', + }, + 'www': 'https://www.fcoin.com', + 'referral': 'https://www.fcoin.com/i/Z5P7V', + 'doc': 'https://developer.fcoin.com', + 'fees': 'https://fcoinjp.zendesk.com/hc/en-us/articles/360018727371', + }, + 'api': { + 'openapi': { + 'get': [ + 'symbols', + ], + }, + 'market': { + 'get': [ + 'ticker/{symbol}', + 'depth/{level}/{symbol}', + 'trades/{symbol}', + 'candles/{timeframe}/{symbol}', + ], + }, + 'public': { + 'get': [ + 'symbols', + 'currencies', + 'server-time', + ], + }, + 'private': { + 'get': [ + 'accounts/balance', + 'assets/accounts/balance', + 'broker/otc/suborders', + 'broker/otc/suborders/{id}', + 'broker/otc/suborders/{id}/payments', + 'broker/otc/users', + 'broker/otc/users/me/balances', + 'broker/otc/users/me/balance', + 'broker/leveraged_accounts/account', + 'broker/leveraged_accounts', + 'orders', + 'orders/{order_id}', + 'orders/{order_id}/match-results', // check order result + ], + 'post': [ + 'assets/accounts/assets-to-spot', + 'accounts/spot-to-assets', + 'broker/otc/assets/transfer/in', + 'broker/otc/assets/transfer/out', + 'broker/otc/suborders', + 'broker/otc/suborders/{id}/pay_confirm', + 'broker/otc/suborders/{id}/cancel', + 'broker/leveraged/assets/transfer/in', + 'broker/leveraged/assets/transfer/out', + 'orders', + 'orders/{order_id}/submit-cancel', // cancel order + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': -0.0002, + 'taker': 0.0003, + }, + }, + 'limits': { + 'amount': { 'min': 0.01, 'max': 100000 }, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + 'fetchMarketsMethod': 'fetch_markets_from_open_api', // or 'fetch_markets_from_api' + 'limits': { + 'BTM/USDT': { 'amount': { 'min': 0.1, 'max': 10000000 }}, + 'ETC/USDT': { 'amount': { 'min': 0.001, 'max': 400000 }}, + 'ETH/USDT': { 'amount': { 'min': 0.001, 'max': 10000 }}, + 'LTC/USDT': { 'amount': { 'min': 0.001, 'max': 40000 }}, + 'BCH/USDT': { 'amount': { 'min': 0.001, 'max': 5000 }}, + 'BTC/USDT': { 'amount': { 'min': 0.001, 'max': 1000 }}, + 'ICX/ETH': { 'amount': { 'min': 0.01, 'max': 3000000 }}, + 'OMG/ETH': { 'amount': { 'min': 0.01, 'max': 500000 }}, + 'FT/USDT': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'ZIL/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'ZIP/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'FT/BTC': { 'amount': { 'min': 1, 'max': 10000000 }}, + 'FT/ETH': { 'amount': { 'min': 1, 'max': 10000000 }}, + }, + }, + 'exceptions': { + '400': NotSupported, // Bad Request + '401': AuthenticationError, + '405': NotSupported, + '429': RateLimitExceeded, // Too Many Requests, exceed api request limit + '1002': ExchangeNotAvailable, // System busy + '1016': InsufficientFunds, + '2136': AuthenticationError, // The API key is expired + '3008': InvalidOrder, + '6004': InvalidNonce, + '6005': AuthenticationError, // Illegal API Signature + '40003': BadSymbol, + }, + 'commonCurrencies': { + 'DAG': 'DAGX', + 'PAI': 'PCHAIN', + 'MT': 'Mariana Token', + }, + }); + } + + async fetchMarkets (params = {}) { + const method = this.safeString (this.options, 'fetchMarketsMethod', 'fetch_markets_from_open_api'); + return await this[method] (params); + } + + async fetchMarketsFromOpenAPI (params = {}) { + // https://github.com/ccxt/ccxt/issues/5648 + const response = await this.openapiGetSymbols (params); + // + // { + // "status":"ok", + // "data":{ + // "categories":[ "fone::coinforce", ... ], + // "symbols":{ + // "mdaeth":{ + // "price_decimal":8, + // "amount_decimal":2, + // "base_currency":"mda", + // "quote_currency":"eth", + // "symbol":"mdaeth", + // "category":"fone::bitangel", + // "leveraged_multiple":null, + // "tradeable":false, + // "market_order_enabled":false, + // "limit_amount_min":"1", + // "limit_amount_max":"10000000", + // "main_tag":"", + // "daily_open_at":"", + // "daily_close_at":"" + // }, + // } + // "category_ref":{ + // "fone::coinforce":[ "btcusdt", ... ], + // } + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const markets = this.safeValue (data, 'symbols', {}); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const market = markets[key]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': this.safeInteger (market, 'price_decimal'), + 'amount': this.safeInteger (market, 'amount_decimal'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'limit_amount_min'), + 'max': this.safeFloat (market, 'limit_amount_max'), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + const active = this.safeValue (market, 'tradeable', false); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchMarketsFromAPI (params = {}) { + const response = await this.publicGetSymbols (params); + // + // { + // "status":0, + // "data":[ + // { + // "name":"dapusdt", + // "base_currency":"dap", + // "quote_currency":"usdt", + // "price_decimal":6, + // "amount_decimal":2, + // "tradable":true + // }, + // ] + // } + // + const result = []; + const markets = this.safeValue (response, 'data'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'base_currency'); + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': market['price_decimal'], + 'amount': market['amount_decimal'], + }; + let limits = { + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + }; + const active = this.safeValue (market, 'tradable', false); + if (symbol in this.options['limits']) { + limits = this.extend (this.options['limits'][symbol], limits); + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountsBalance (params); + const result = { 'info': response }; + const balances = this.safeValue (response, 'data'); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'frozen'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseBidsAsks (orders, priceKey = 0, amountKey = 1) { + const result = []; + const length = orders.length; + const halfLength = parseInt (length / 2); + // += 2 in the for loop below won't transpile + for (let i = 0; i < halfLength; i++) { + const index = i * 2; + const priceField = this.sum (index, priceKey); + const amountField = this.sum (index, amountKey); + result.push ([ + this.safeFloat (orders, priceField), + this.safeFloat (orders, amountField), + ]); + } + return result; + } + + async fetchOrderBook (symbol = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit !== undefined) { + if ((limit === 20) || (limit === 150)) { + limit = 'L' + limit.toString (); + } else { + throw new ExchangeError (this.id + ' fetchOrderBook supports limit of 20 or 150. Other values are not accepted'); + } + } else { + limit = 'L20'; + } + const request = { + 'symbol': this.marketId (symbol), + 'level': limit, // L20, L150 + }; + const response = await this.marketGetDepthLevelSymbol (this.extend (request, params)); + const orderbook = this.safeValue (response, 'data'); + return this.parseOrderBook (orderbook, orderbook['ts'], 'bids', 'asks', 0, 1); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.marketGetTickerSymbol (this.extend (request, params)); + return this.parseTicker (ticker['data'], market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = undefined; + let symbol = undefined; + if (market === undefined) { + const tickerType = this.safeString (ticker, 'type'); + if (tickerType !== undefined) { + const parts = tickerType.split ('.'); + const id = parts[1]; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + } + } + } + const values = ticker['ticker']; + const last = this.safeFloat (values, 0); + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (values, 7), + 'low': this.safeFloat (values, 8), + 'bid': this.safeFloat (values, 2), + 'bidVolume': this.safeFloat (values, 3), + 'ask': this.safeFloat (values, 4), + 'askVolume': this.safeFloat (values, 5), + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (values, 9), + 'quoteVolume': this.safeFloat (values, 10), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'ts'); + const side = this.safeStringLower (trade, 'side'); + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + const fee = undefined; + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'limit': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.marketGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + 'side': side, + 'type': type, + }; + // for market buy it requires the amount of quote currency to spend + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + request['amount'] = this.costToPrecision (symbol, amount * price); + } + } else { + request['amount'] = this.costToPrecision (symbol, amount); + } + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + if ((type === 'limit') || (type === 'ioc') || (type === 'fok')) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + return { + 'info': response, + 'id': response['data'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privatePostOrdersOrderIdSubmitCancel (this.extend (request, params)); + const order = this.parseOrder (response); + return this.extend (order, { + 'id': id, + 'status': 'canceled', + }); + } + + parseOrderStatus (status) { + const statuses = { + 'submitted': 'open', + 'canceled': 'canceled', + 'partial_filled': 'open', + 'partial_canceled': 'canceled', + 'filled': 'closed', + 'pending_cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const side = this.safeString (order, 'side'); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + const orderType = this.safeString (order, 'type'); + const timestamp = this.safeInteger (order, 'created_at'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'filled_amount'); + let remaining = undefined; + let price = this.safeFloat (order, 'price'); + let cost = this.safeFloat (order, 'executed_value'); + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (cost === undefined) { + if (price !== undefined) { + cost = price * filled; + } + } else if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + let feeCurrency = undefined; + let feeCost = undefined; + const feeRebate = this.safeFloat (order, 'fees_income'); + if ((feeRebate !== undefined) && (feeRebate > 0)) { + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = (side === 'buy') ? market['quote'] : market['base']; + } + feeCost = -feeRebate; + } else { + feeCost = this.safeFloat (order, 'fill_fees'); + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = (side === 'buy') ? market['base'] : market['quote']; + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'average': undefined, + 'status': status, + 'fee': { + 'cost': feeCost, + 'currency': feeCurrency, + }, + 'trades': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateGetOrdersOrderId (this.extend (request, params)); + return this.parseOrder (response['data']); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'states': 'submitted,partial_filled' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'states': 'partial_canceled,filled' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders() requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'states': 'submitted,partial_filled,partial_canceled,filled,canceled', + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response['data'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'id'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'base_vol'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 20; // default is 20 + } + const request = { + 'symbol': market['id'], + 'timeframe': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + const sinceInSeconds = parseInt (since / 1000); + const timerange = limit * this.parseTimeframe (timeframe); + request['before'] = this.sum (sinceInSeconds, timerange) - 1; + } + const response = await this.marketGetCandlesTimeframeSymbol (this.extend (request, params)); + return this.parseOHLCVs (response['data'], market, timeframe, since, limit); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/'; + const openAPI = (api === 'openapi'); + const privateAPI = (api === 'private'); + request += openAPI ? (api + '/') : ''; + request += this.version + '/'; + request += (privateAPI || openAPI) ? '' : (api + '/'); + request += this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + let url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }); + url += request; + if (privateAPI) { + this.checkRequiredCredentials (); + const timestamp = this.nonce ().toString (); + query = this.keysort (query); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.rawencode (query); + } + } + // HTTP_METHOD + HTTP_REQUEST_URI + TIMESTAMP + POST_BODY + let auth = method + url + timestamp; + if (method === 'POST') { + if (Object.keys (query).length) { + body = this.json (query); + auth += this.urlencode (query); + } + } + const payload = this.stringToBase64 (this.encode (auth)); + let signature = this.hmac (payload, this.encode (this.secret), 'sha1', 'binary'); + signature = this.decode (this.stringToBase64 (signature)); + headers = { + 'FC-ACCESS-KEY': this.apiKey, + 'FC-ACCESS-SIGNATURE': signature, + 'FC-ACCESS-TIMESTAMP': timestamp, + 'Content-Type': 'application/json', + }; + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const status = this.safeString (response, 'status'); + if (status !== '0' && status !== 'ok') { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, status, feedback); + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/fcoinjp.js b/TaskServer/node_modules/ccxt/js/fcoinjp.js new file mode 100644 index 000000000..9ecf75d43 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/fcoinjp.js @@ -0,0 +1,24 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const fcoin = require ('./fcoin.js'); + +// --------------------------------------------------------------------------- + +module.exports = class fcoinjp extends fcoin { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'fcoinjp', + 'name': 'FCoinJP', + 'countries': [ 'JP' ], + 'hostname': 'fcoinjp.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/54219174-08b66b00-4500-11e9-862d-f522d0fe08c6.jpg', + 'fees': 'https://fcoinjp.zendesk.com/hc/en-us/articles/360018727371', + 'www': 'https://www.fcoinjp.com', + 'referral': undefined, + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/flowbtc.js b/TaskServer/node_modules/ccxt/js/flowbtc.js new file mode 100644 index 000000000..0658415ca --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/flowbtc.js @@ -0,0 +1,274 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class flowbtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'flowbtc', + 'name': 'flowBTC', + 'countries': [ 'BR' ], // Brazil + 'version': 'v1', + 'rateLimit': 1000, + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28162465-cd815d4c-67cf-11e7-8e57-438bea0523a2.jpg', + 'api': 'https://publicapi.flowbtc.com.br', + 'www': 'https://www.flowbtc.com.br', + 'doc': 'https://www.flowbtc.com.br/api.html', + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'post': [ + 'GetTicker', + 'GetTrades', + 'GetTradesByDate', + 'GetOrderBook', + 'GetProductPairs', + 'GetProducts', + ], + }, + 'private': { + 'post': [ + 'CreateAccount', + 'GetUserInfo', + 'SetUserInfo', + 'GetAccountInfo', + 'GetAccountTrades', + 'GetDepositAddresses', + 'Withdraw', + 'CreateOrder', + 'ModifyOrder', + 'CancelOrder', + 'CancelAllOrders', + 'GetAccountOpenOrders', + 'GetOrderFee', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0025, + 'taker': 0.005, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicPostGetProductPairs (params); + const markets = this.safeValue (response, 'productPairs'); + const result = {}; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'product1Label'); + const quoteId = this.safeString (market, 'product2Label'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const precision = { + 'amount': this.safeInteger (market, 'product1DecimalPlaces'), + 'price': this.safeInteger (market, 'product2DecimalPlaces'), + }; + const symbol = base + '/' + quote; + result[symbol] = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }; + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const balances = this.safeValue (response, 'currencies'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = balance['name']; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['total'] = this.safeFloat (balance, 'hold'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'productPair': market['id'], + }; + const response = await this.publicPostGetOrderBook (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'px', 'qty'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'productPair': market['id'], + }; + const ticker = await this.publicPostGetTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume24hr'), + 'quoteVolume': this.safeFloat (ticker, 'volume24hrProduct2'), + 'info': ticker, + }; + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'unixtime'); + const side = (trade['incomingOrderSide'] === 0) ? 'buy' : 'sell'; + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'px'); + const amount = this.safeFloat (trade, 'qty'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'id': id, + 'order': undefined, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'ins': market['id'], + 'startIndex': -1, + }; + const response = await this.publicPostGetTrades (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const orderType = (type === 'market') ? 1 : 0; + const request = { + 'ins': this.marketId (symbol), + 'side': side, + 'orderType': orderType, + 'qty': amount, + 'px': this.priceToPrecision (symbol, price), + }; + const response = await this.privatePostCreateOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response['serverOrderId'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + if ('ins' in params) { + const request = { + 'serverOrderId': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + throw new ExchangeError (this.id + ' requires `ins` symbol parameter for cancelling an order'); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + body = this.json (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const auth = nonce.toString () + this.uid + this.apiKey; + const signature = this.hmac (this.encode (auth), this.encode (this.secret)); + body = this.json (this.extend ({ + 'apiKey': this.apiKey, + 'apiNonce': nonce, + 'apiSig': signature.toUpperCase (), + }, params)); + headers = { + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('isAccepted' in response) { + if (response['isAccepted']) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/foxbit.js b/TaskServer/node_modules/ccxt/js/foxbit.js new file mode 100644 index 000000000..452fd9cdd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/foxbit.js @@ -0,0 +1,257 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class foxbit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'foxbit', + 'name': 'FoxBit', + 'countries': [ 'BR' ], + 'has': { + 'CORS': false, + 'createMarketOrder': false, + }, + 'rateLimit': 1000, + 'version': 'v1', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991413-11b40d42-647f-11e7-91ee-78ced874dd09.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://foxbit.com.br/exchange', + 'doc': 'https://foxbit.com.br/api/', + }, + 'comment': 'Blinktrade API', + 'api': { + 'public': { + 'get': [ + '{currency}/ticker', // ?crypto_currency=BTC + '{currency}/orderbook', // ?crypto_currency=BTC + '{currency}/trades', // ?crypto_currency=BTC&since=&limit= + ], + }, + 'private': { + 'post': [ + 'D', // order + 'F', // cancel order + 'U2', // balance + 'U4', // my orders + 'U6', // withdraw + 'U18', // deposit + 'U24', // confirm withdrawal + 'U26', // list withdrawals + 'U30', // list deposits + 'U34', // ledger + 'U70', // cancel withdrawal + ], + }, + }, + 'markets': { + 'BTC/VEF': { 'id': 'BTCVEF', 'symbol': 'BTC/VEF', 'base': 'BTC', 'quote': 'VEF', 'brokerId': 1, 'broker': 'SurBitcoin' }, + 'BTC/VND': { 'id': 'BTCVND', 'symbol': 'BTC/VND', 'base': 'BTC', 'quote': 'VND', 'brokerId': 3, 'broker': 'VBTC' }, + 'BTC/BRL': { 'id': 'BTCBRL', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'brokerId': 4, 'broker': 'FoxBit' }, + 'BTC/PKR': { 'id': 'BTCPKR', 'symbol': 'BTC/PKR', 'base': 'BTC', 'quote': 'PKR', 'brokerId': 8, 'broker': 'UrduBit' }, + 'BTC/CLP': { 'id': 'BTCCLP', 'symbol': 'BTC/CLP', 'base': 'BTC', 'quote': 'CLP', 'brokerId': 9, 'broker': 'ChileBit' }, + }, + 'options': { + 'brokerId': '4', // https://blinktrade.com/docs/#brokers + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'BalanceReqID': this.nonce (), + }; + const response = await this.privatePostU2 (this.extend (request, params)); + const balances = this.safeValue (response['Responses'], this.options['brokerId']); + const result = { 'info': response }; + if (balances !== undefined) { + const currencyIds = Object.keys (this.currencies_by_id); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + // we only set the balance for the currency if that currency is present in response + // otherwise we will lose the info if the currency balance has been funded or traded or not + if (currencyId in balances) { + const account = this.account (); + let used = this.safeFloat (balances, currencyId + '_locked'); + if (used !== undefined) { + used *= 1e-8; + } + let total = this.safeFloat (balances, currencyId); + if (total !== undefined) { + total *= 1e-8; + } + account['used'] = used; + account['total'] = total; + result[code] = account; + } + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['quote'], + 'crypto_currency': market['base'], + }; + const response = await this.publicGetCurrencyOrderbook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['quote'], + 'crypto_currency': market['base'], + }; + const ticker = await this.publicGetCurrencyTicker (this.extend (request, params)); + const timestamp = this.milliseconds (); + const lowercaseQuote = market['quote'].toLowerCase (); + const quoteVolume = 'vol_' + lowercaseQuote; + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, quoteVolume), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (trade, 'side'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['quote'], + 'crypto_currency': market['base'], + }; + const response = await this.publicGetCurrencyTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const market = this.market (symbol); + const orderSide = (side === 'buy') ? '1' : '2'; + const request = { + 'ClOrdID': this.nonce (), + 'Symbol': market['id'], + 'Side': orderSide, + 'OrdType': '2', + 'Price': price, + 'OrderQty': amount, + 'BrokerID': market['brokerId'], + }; + const response = await this.privatePostD (this.extend (request, params)); + const indexed = this.indexBy (response['Responses'], 'MsgType'); + const execution = indexed['8']; + return { + 'info': response, + 'id': execution['OrderID'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.privatePostF (this.extend ({ + 'ClOrdID': id, + }, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const request = this.extend ({ 'MsgType': path }, query); + body = this.json (request); + headers = { + 'APIKey': this.apiKey, + 'Nonce': nonce, + 'Signature': this.hmac (this.encode (nonce), this.encode (this.secret)), + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('Status' in response) { + if (response['Status'] !== 200) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/ftx.js b/TaskServer/node_modules/ccxt/js/ftx.js new file mode 100644 index 000000000..0af5d417e --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/ftx.js @@ -0,0 +1,1356 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { TICK_SIZE } = require ('./base/functions/number'); +const { ExchangeError, InvalidOrder, BadRequest, InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class ftx extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'ftx', + 'name': 'FTX', + 'countries': [ 'HK' ], + 'rateLimit': 100, + 'certified': true, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg', + 'www': 'https://ftx.com', + 'api': 'https://ftx.com', + 'doc': 'https://github.com/ftexchange/ftx', + 'fees': 'https://ftexchange.zendesk.com/hc/en-us/articles/360024479432-Fees', + 'referral': 'https://ftx.com/#a=1623029', + }, + 'has': { + 'cancelAllOrders': true, + 'fetchClosedOrders': false, + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchFundingFees': false, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBook': true, + 'fetchOrders': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTrades': true, + 'fetchTradingFees': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'timeframes': { + '15s': '15', + '1m': '60', + '5m': '300', + '15m': '900', + '1h': '3600', + '4h': '14400', + '1d': '86400', + }, + 'api': { + 'public': { + 'get': [ + 'coins', + 'markets', + 'markets/{market_name}', + 'markets/{market_name}/orderbook', // ?depth={depth} + 'markets/{market_name}/trades', // ?limit={limit}&start_time={start_time}&end_time={end_time} + 'markets/{market_name}/candles', // ?resolution={resolution}&limit={limit}&start_time={start_time}&end_time={end_time} + 'futures', + 'futures/{future_name}', + 'futures/{future_name}/stats', + 'funding_rates', + 'lt/tokens', + 'lt/{token_name}', + ], + }, + 'private': { + 'get': [ + 'account', + 'positions', + 'wallet/coins', + 'wallet/balances', + 'wallet/deposit_address/{coin}', + 'wallet/deposits', + 'wallet/withdrawals', + 'orders', // ?market={market} + 'orders/history', // ?market={market} + 'orders/{order_id}', + 'orders/by_client_id/{client_order_id}', + 'conditional_orders', // ?market={market} + 'conditional_orders/history', // ?market={market} + 'fills', // ?market={market} + 'funding_payments', + 'lt/balances', + 'lt/creations', + 'lt/redemptions', + 'subaccounts', + 'subaccounts/{nickname}/balances', + ], + 'post': [ + 'account/leverage', + 'wallet/withdrawals', + 'orders', + 'conditional_orders', + 'lt/{token_name}/create', + 'lt/{token_name}/redeem', + 'subaccounts', + 'subaccounts/update_name', + 'subaccounts/transfer', + ], + 'delete': [ + 'orders/{order_id}', + 'orders/by_client_id/{client_order_id}', + 'orders', + 'conditional_orders/{order_id}', + 'subaccounts', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.02 / 100, + 'taker': 0.07 / 100, + 'tiers': { + 'taker': [ + [0, 0.07 / 100], + [1000000, 0.06 / 100], + [5000000, 0.055 / 100], + [10000000, 0.05 / 100], + [15000000, 0.045 / 100], + [35000000, 0.04 / 100], + ], + 'maker': [ + [0, 0.02 / 100], + [1000000, 0.02 / 100], + [5000000, 0.015 / 100], + [10000000, 0.015 / 100], + [15000000, 0.01 / 100], + [35000000, 0.01 / 100], + ], + }, + }, + 'funding': { + 'withdraw': {}, + }, + }, + 'exceptions': { + 'exact': { + 'Not enough balances': InsufficientFunds, // {"error":"Not enough balances","success":false} + 'InvalidPrice': InvalidOrder, // {"error":"Invalid price","success":false} + 'Size too small': InvalidOrder, // {"error":"Size too small","success":false} + 'Missing parameter price': InvalidOrder, // {"error":"Missing parameter price","success":false} + 'Order not found': OrderNotFound, // {"error":"Order not found","success":false} + }, + 'broad': { + 'Invalid parameter': BadRequest, // {"error":"Invalid parameter start_time","success":false} + 'The requested URL was not found on the server': BadRequest, + 'No such coin': BadRequest, + 'No such market': BadRequest, + 'An unexpected error occurred': ExchangeError, // {"error":"An unexpected error occurred, please try again later (58BC21C795).","success":false} + }, + }, + 'precisionMode': TICK_SIZE, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCoins (params); + const currencies = this.safeValue (response, 'result', []); + // + // { + // "success":true, + // "result": [ + // {"id":"BTC","name":"Bitcoin"}, + // {"id":"ETH","name":"Ethereum"}, + // {"id":"ETHMOON","name":"10X Long Ethereum Token","underlying":"ETH"}, + // {"id":"EOSBULL","name":"3X Long EOS Token","underlying":"EOS"}, + // ], + // } + // + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'id'); + const code = this.safeCurrencyCode (id); + const name = this.safeString (currency, 'name'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': name, + 'active': undefined, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'withdraw': { 'min': undefined, 'max': undefined }, + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + // + // { + // 'success': true, + // "result": [ + // { + // "ask":170.37, + // "baseCurrency":null, + // "bid":170.31, + // "change1h":-0.019001554672655036, + // "change24h":-0.024841165359738997, + // "changeBod":-0.03816406029469881, + // "enabled":true, + // "last":170.37, + // "name":"ETH-PERP", + // "price":170.37, + // "priceIncrement":0.01, + // "quoteCurrency":null, + // "quoteVolume24h":7742164.59889, + // "sizeIncrement":0.001, + // "type":"future", + // "underlying":"ETH", + // "volumeUsd24h":7742164.59889 + // }, + // { + // "ask":170.44, + // "baseCurrency":"ETH", + // "bid":170.41, + // "change1h":-0.018485459257126403, + // "change24h":-0.023825887743413515, + // "changeBod":-0.037605872388481086, + // "enabled":true, + // "last":172.72, + // "name":"ETH/USD", + // "price":170.44, + // "priceIncrement":0.01, + // "quoteCurrency":"USD", + // "quoteVolume24h":382802.0252, + // "sizeIncrement":0.001, + // "type":"spot", + // "underlying":null, + // "volumeUsd24h":382802.0252 + // }, + // ], + // } + // + const result = []; + const markets = this.safeValue (response, 'result', []); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString2 (market, 'baseCurrency', 'underlying'); + const quoteId = this.safeString (market, 'quoteCurrency', 'USD'); + const type = this.safeString (market, 'type'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + // check if a market is a spot or future market + const symbol = (type === 'future') ? this.safeString (market, 'name') : (base + '/' + quote); + const active = this.safeValue (market, 'enabled'); + const sizeIncrement = this.safeFloat (market, 'sizeIncrement'); + const priceIncrement = this.safeFloat (market, 'priceIncrement'); + const precision = { + 'amount': sizeIncrement, + 'price': priceIncrement, + }; + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'type': type, + 'future': (type === 'future'), + 'spot': (type === 'spot'), + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': sizeIncrement, + 'max': undefined, + }, + 'price': { + 'min': priceIncrement, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }; + result.push (entry); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // "ask":171.29, + // "baseCurrency":null, // base currency for spot markets + // "bid":171.24, + // "change1h":-0.0012244897959183673, + // "change24h":-0.031603346901854366, + // "changeBod":-0.03297013492914808, + // "enabled":true, + // "last":171.44, + // "name":"ETH-PERP", + // "price":171.29, + // "priceIncrement":0.01, + // "quoteCurrency":null, // quote currency for spot markets + // "quoteVolume24h":8570651.12113, + // "sizeIncrement":0.001, + // "type":"future", + // "underlying":"ETH", // null for spot markets + // "volumeUsd24h":8570651.12113, + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'name'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const type = this.safeString (ticker, 'type'); + if (type === 'future') { + symbol = marketId; + } else { + const base = this.safeCurrencyCode (this.safeString (ticker, 'baseCurrency')); + const quote = this.safeCurrencyCode (this.safeString (ticker, 'quoteCurrency')); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const timestamp = this.milliseconds (); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'change24h'), + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'quoteVolume24h'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + }; + const response = await this.publicGetMarketsMarketName (this.extend (request, params)); + // + // { + // "success":true, + // "result":{ + // "ask":171.29, + // "baseCurrency":null, // base currency for spot markets + // "bid":171.24, + // "change1h":-0.0012244897959183673, + // "change24h":-0.031603346901854366, + // "changeBod":-0.03297013492914808, + // "enabled":true, + // "last":171.44, + // "name":"ETH-PERP", + // "price":171.29, + // "priceIncrement":0.01, + // "quoteCurrency":null, // quote currency for spot markets + // "quoteVolume24h":8570651.12113, + // "sizeIncrement":0.001, + // "type":"future", + // "underlying":"ETH", // null for spot markets + // "volumeUsd24h":8570651.12113, + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseTicker (result, market); + } + + parseTickers (tickers, symbols = undefined) { + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarkets (params); + // + // { + // 'success': true, + // "result": [ + // { + // "ask":170.44, + // "baseCurrency":"ETH", + // "bid":170.41, + // "change1h":-0.018485459257126403, + // "change24h":-0.023825887743413515, + // "changeBod":-0.037605872388481086, + // "enabled":true, + // "last":172.72, + // "name":"ETH/USD", + // "price":170.44, + // "priceIncrement":0.01, + // "quoteCurrency":"USD", + // "quoteVolume24h":382802.0252, + // "sizeIncrement":0.001, + // "type":"spot", + // "underlying":null, + // "volumeUsd24h":382802.0252 + // }, + // ], + // } + // + const tickers = this.safeValue (response, 'result', []); + return this.parseTickers (tickers, symbols); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + }; + if (limit !== undefined) { + request['depth'] = limit; // max 100, default 20 + } + const response = await this.publicGetMarketsMarketNameOrderbook (this.extend (request, params)); + // + // { + // "success":true, + // "result":{ + // "asks":[ + // [171.95,279.865], + // [171.98,102.42], + // [171.99,124.11], + // ], + // "bids":[ + // [171.93,69.749], + // [171.9,288.325], + // [171.88,87.47], + // ], + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseOrderBook (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "close":177.23, + // "high":177.45, + // "low":177.2, + // "open":177.43, + // "startTime":"2019-10-17T13:27:00+00:00", + // "time":1571318820000.0, + // "volume":0.0 + // } + // + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + 'resolution': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + const response = await this.publicGetMarketsMarketNameCandles (this.extend (request, params)); + // + // { + // "success": true, + // "result":[ + // { + // "close":177.23, + // "high":177.45, + // "low":177.2, + // "open":177.43, + // "startTime":"2019-10-17T13:27:00+00:00", + // "time":1571318820000.0, + // "volume":0.0 + // }, + // { + // "close":177.26, + // "high":177.33, + // "low":177.23, + // "open":177.23, + // "startTime":"2019-10-17T13:28:00+00:00", + // "time":1571318880000.0, + // "volume":0.0 + // }, + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseOHLCVs (result, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "id":1715826, + // "liquidation":false, + // "price":171.62, + // "side":"buy", + // "size":2.095, + // "time":"2019-10-18T12:59:54.288166+00:00" + // } + // + // fetchMyTrades (private) + // + // { + // "fee": 20.1374935, + // "feeRate": 0.0005, + // "future": "EOS-0329", + // "id": 11215, + // "liquidity": "taker", + // "market": "EOS-0329", + // "baseCurrency": null, + // "quoteCurrency": null, + // "orderId": 8436981, + // "price": 4.201, + // "side": "buy", + // "size": 9587, + // "time": "2019-03-27T19:15:10.204619+00:00", + // "type": "order" + // } + // + const id = this.safeString (trade, 'id'); + const takerOrMaker = this.safeString (trade, 'liquidity'); + const marketId = this.safeString (trade, 'market'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const base = this.safeCurrencyCode (this.safeString (trade, 'baseCurrency')); + const quote = this.safeCurrencyCode (this.safeString (trade, 'quoteCurrency')); + if ((base !== undefined) && (quote !== undefined)) { + symbol = base + '/' + quote; + } else { + symbol = marketId; + } + } + } + const timestamp = this.parse8601 (this.safeString (trade, 'time')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'size'); + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const side = this.safeString (trade, 'side'); + let cost = undefined; + if (price !== undefined && amount !== undefined) { + cost = price * amount; + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'rate': this.safeFloat (trade, 'feeRate'), + }; + } + const orderId = this.safeString (trade, 'orderId'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market_name': market['id'], + }; + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + // start_time doesn't work without end_time + request['end_time'] = this.seconds (); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetMarketsMarketNameTrades (this.extend (request, params)); + // + // { + // "success":true, + // "result":[ + // { + // "id":1715826, + // "liquidation":false, + // "price":171.62, + // "side":"buy", + // "size":2.095, + // "time":"2019-10-18T12:59:54.288166+00:00" + // }, + // { + // "id":1715763, + // "liquidation":false, + // "price":171.89, + // "side":"sell", + // "size":1.477, + // "time":"2019-10-18T12:58:38.443734+00:00" + // }, + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccount (params); + // + // { + // "success": true, + // "result": { + // "backstopProvider": true, + // "collateral": 3568181.02691129, + // "freeCollateral": 1786071.456884368, + // "initialMarginRequirement": 0.12222384240257728, + // "liquidating": false, + // "maintenanceMarginRequirement": 0.07177992558058484, + // "makerFee": 0.0002, + // "marginFraction": 0.5588433331419503, + // "openMarginFraction": 0.2447194090423075, + // "takerFee": 0.0005, + // "totalAccountValue": 3568180.98341129, + // "totalPositionSize": 6384939.6992, + // "username": "user@domain.com", + // "positions": [ + // { + // "cost": -31.7906, + // "entryPrice": 138.22, + // "future": "ETH-PERP", + // "initialMarginRequirement": 0.1, + // "longOrderSize": 1744.55, + // "maintenanceMarginRequirement": 0.04, + // "netSize": -0.23, + // "openSize": 1744.32, + // "realizedPnl": 3.39441714, + // "shortOrderSize": 1732.09, + // "side": "sell", + // "size": 0.23, + // "unrealizedPnl": 0, + // }, + // ], + // }, + // } + // + const result = this.safeValue (response, 'result', {}); + return { + 'info': response, + 'maker': this.safeFloat (result, 'makerFee'), + 'taker': this.safeFloat (result, 'takerFee'), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletBalances (params); + // + // { + // "success": true, + // "result": [ + // { + // "coin": "USDTBEAR", + // "free": 2320.2, + // "total": 2340.2 + // }, + // ], + // } + // + const result = { + 'info': response, + }; + const balances = this.safeValue (response, 'result', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const code = this.safeCurrencyCode (this.safeString (balance, 'coin')); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'free'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'new': 'open', + 'open': 'open', + 'closed': 'closed', // filled or canceled + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder, fetchOrders, fetchOpenOrders, createOrder ("limit", "market") + // + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 0, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "remainingSize": 31431, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null, + // } + // + // createOrder (conditional, "stop", "trailingStop", or "takeProfit") + // + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "triggerPrice": 0.306525, + // "orderId": null, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "stop", + // "orderPrice": null, + // "error": null, + // "triggeredAt": null, + // "reduceOnly": false + // } + // + const id = this.safeString (order, 'id'); + const timestamp = this.parse8601 (this.safeString (order, 'createdAt')); + const filled = this.safeFloat (order, 'filledSize'); + const remaining = this.safeFloat (order, 'remainingSize'); + let symbol = undefined; + const marketId = this.safeString (order, 'market'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const side = this.safeString (order, 'side'); + const type = this.safeString (order, 'type'); + const amount = this.safeFloat (order, 'size'); + let cost = undefined; + if (filled !== undefined && amount !== undefined) { + cost = filled * amount; + } + const price = this.safeFloat2 (order, 'price', 'triggerPrice'); + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'triggeredAt')); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + 'trades': undefined, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'side': side, // "buy" or "sell" + // 'price': 0.306525, // send null for market orders + 'type': type, // "limit", "market", "stop", "trailingStop", or "takeProfit" + 'size': parseFloat (this.amountToPrecision (symbol, amount)), + // 'reduceOnly': false, // optional, default is false + // 'ioc': false, // optional, default is false, limit or market orders only + // 'postOnly': false, // optional, default is false, limit or market orders only + // 'clientId': 'abcdef0123456789', // string, optional, client order id, limit or market orders only + }; + let priceToPrecision = undefined; + if (price !== undefined) { + priceToPrecision = parseFloat (this.priceToPrecision (symbol, price)); + } + let method = 'privatePostConditionalOrders'; + if (type === 'limit') { + method = 'privatePostOrders'; + request['price'] = priceToPrecision; + } else if (type === 'market') { + method = 'privatePostOrders'; + request['price'] = null; + } else if ((type === 'stop') || (type === 'takeProfit')) { + request['triggerPrice'] = priceToPrecision; + // request['orderPrice'] = number; // optional, order type is limit if this is specified, otherwise market + } else if (type === 'trailingStop') { + request['trailValue'] = priceToPrecision; // negative for "sell", positive for "buy" + } else { + throw new InvalidOrder (this.id + ' createOrder () does not support order type ' + type + ', only limit, market, stop, trailingStop, or takeProfit orders are supported'); + } + const response = await this[method] (this.extend (request, params)); + // + // orders + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 0, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "remainingSize": 31431, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null, + // } + // ] + // } + // + // conditional orders + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "triggerPrice": 0.306525, + // "orderId": null, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "stop", + // "orderPrice": null, + // "error": null, + // "triggeredAt": null, + // "reduceOnly": false + // } + // ] + // } + // + // + const result = this.safeValue (response, 'result', []); + return this.parseOrder (result, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privateDeleteOrdersOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "result": "Order queued for cancelation" + // } + // + const result = this.safeValue (response, 'result', {}); + return result; + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'market': market['id'], // optional + 'conditionalOrdersOnly': false, // cancel conditional orders only + 'limitOrdersOnly': false, // cancel existing limit orders (non-conditional orders) only + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateDeleteOrders (this.extend (request, params)); + const result = this.safeValue (response, 'result', {}); + // + // { + // "success": true, + // "result": "Orders queued for cancelation" + // } + // + return result; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privateGetOrdersOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "result": { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 10, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "avgFillPrice": 0.306526, + // "remainingSize": 31421, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseOrder (result); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 10, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "avgFillPrice": 0.306526, + // "remainingSize": 31421, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null + // } + // ] + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseOrders (result, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default 100, max 100 + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + const response = await this.privateGetOrdersHistory (this.extend (request, params)); + // + // { + // "success": true, + // "result": [ + // { + // "createdAt": "2019-03-05T09:56:55.728933+00:00", + // "filledSize": 10, + // "future": "XRP-PERP", + // "id": 9596912, + // "market": "XRP-PERP", + // "price": 0.306525, + // "avgFillPrice": 0.306526, + // "remainingSize": 31421, + // "side": "sell", + // "size": 31431, + // "status": "open", + // "type": "limit", + // "reduceOnly": false, + // "ioc": false, + // "postOnly": false, + // "clientId": null + // } + // ] + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseOrders (result, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['start_time'] = parseInt (since / 1000); + } + const response = await this.privateGetFills (this.extend (request, params)); + // + // { + // "success": true, + // "result": [ + // { + // "fee": 20.1374935, + // "feeRate": 0.0005, + // "future": "EOS-0329", + // "id": 11215, + // "liquidity": "taker", + // "market": "EOS-0329", + // "baseCurrency": null, + // "quoteCurrency": null, + // "orderId": 8436981, + // "price": 4.201, + // "side": "buy", + // "size": 9587, + // "time": "2019-03-27T19:15:10.204619+00:00", + // "type": "order" + // } + // ] + // } + // + const trades = this.safeValue (response, 'result', []); + return this.parseTrades (trades, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + 'size': amount, + 'address': address, + // 'password': 'string', // optional withdrawal password if it is required for your account + // 'code': '192837', // optional 2fa code if it is required for your account + }; + if (tag !== undefined) { + request['tag'] = tag; + } + const response = await this.privatePostWalletWithdrawals (this.extend (request, params)); + // + // { + // "success": true, + // "result": { + // "coin": "USDTBEAR", + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null", + // "fee": 0, + // "id": 1, + // "size": "20.2", + // "status": "requested", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "null" + // } + // } + // + const result = this.safeValue (response, 'result', {}); + return this.parseTransaction (result, currency); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + }; + const response = await this.privateGetWalletDepositAddressCoin (this.extend (request, params)); + // + // { + // "success": true, + // "result": { + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null" + // } + // } + // + const result = this.safeValue (response, 'result', {}); + const address = this.safeString (result, 'address'); + const tag = this.safeString (result, 'tag'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + // what are other statuses here? + 'confirmed': 'ok', // deposits + 'complete': 'ok', // withdrawals + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction) { + // + // fetchDeposits + // + // { + // "coin": "TUSD", + // "confirmations": 64, + // "confirmedTime": "2019-03-05T09:56:55.728933+00:00", + // "fee": 0, + // "id": 1, + // "sentTime": "2019-03-05T09:56:55.735929+00:00", + // "size": "99.0", + // "status": "confirmed", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // + // fetchWithdrawals + // + // { + // "coin": "TUSD", + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null", + // "fee": 0, + // "id": 1, + // "size": "99.0", + // "status": "complete", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // + const code = this.safeCurrencyCode (this.safeString (transaction, 'coin')); + const id = this.safeInteger (transaction, 'id'); + const amount = this.safeFloat (transaction, 'size'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const timestamp = this.parse8601 (this.safeString (transaction, 'time')); + const txid = this.safeString (transaction, 'txid'); + const address = this.safeString (transaction, 'address'); + const tag = this.safeString (transaction, 'tag'); + const fee = this.safeFloat (transaction, 'fee'); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': undefined, + 'addressTo': address, + 'tagFrom': undefined, + 'tag': tag, + 'tagTo': undefined, + 'type': undefined, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': { + 'currency': code, + 'cost': fee, + 'rate': undefined, + }, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletDeposits (params); + // + // { + // "success": true, + // "result": { + // "coin": "TUSD", + // "confirmations": 64, + // "confirmedTime": "2019-03-05T09:56:55.728933+00:00", + // "fee": 0, + // "id": 1, + // "sentTime": "2019-03-05T09:56:55.735929+00:00", + // "size": "99.0", + // "status": "confirmed", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTransactions (result); + } + + async fetchWithdrawals (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWalletWithdrawals (params); + // + // { + // "success": true, + // "result": { + // "coin": "TUSD", + // "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE", + // "tag": "null", + // "fee": 0, + // "id": 1, + // "size": "99.0", + // "status": "complete", + // "time": "2019-03-05T09:56:55.728933+00:00", + // "txid": "0x8078356ae4b06a036d64747546c274af19581f1c78c510b60505798a7ffcaf1" + // } + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTransactions (result); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let request = '/api/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + if (method !== 'POST') { + if (Object.keys (query).length) { + const suffix = '?' + this.urlencode (query); + url += suffix; + request += suffix; + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.milliseconds ().toString (); + let auth = timestamp + method + request; + headers = { + 'FTX-KEY': this.apiKey, + 'FTX-TS': timestamp, + }; + if (method === 'POST') { + body = this.json (query); + auth += body; + headers['Content-Type'] = 'application/json'; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256'); + headers['FTX-SIGN'] = signature; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to the default error handler + } + // + // {"error":"Invalid parameter start_time","success":false} + // {"error":"Not enough balances","success":false} + // + const success = this.safeValue (response, 'success'); + if (!success) { + const feedback = this.id + ' ' + body; + const error = this.safeString (response, 'error'); + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/fybse.js b/TaskServer/node_modules/ccxt/js/fybse.js new file mode 100644 index 000000000..86ff50a73 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/fybse.js @@ -0,0 +1,195 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class fybse extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'fybse', + 'name': 'FYB-SE', + 'countries': [ 'SE' ], // Sweden + 'has': { + 'CORS': false, + }, + 'rateLimit': 1500, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766512-31019772-5edb-11e7-8241-2e675e6797f1.jpg', + 'api': 'https://www.fybse.se/api/SEK', + 'www': 'https://www.fybse.se', + 'doc': 'https://fyb.docs.apiary.io', + }, + 'api': { + 'public': { + 'get': [ + 'ticker', + 'tickerdetailed', + 'orderbook', + 'trades', + ], + }, + 'private': { + 'post': [ + 'test', + 'getaccinfo', + 'getpendingorders', + 'getorderhistory', + 'cancelpendingorder', + 'placeorder', + 'withdraw', + ], + }, + }, + 'markets': { + 'BTC/SEK': { 'id': 'SEK', 'symbol': 'BTC/SEK', 'base': 'BTC', 'quote': 'SEK' }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetaccinfo (params); + const btc = this.safeFloat (response, 'btcBal'); + const symbol = this.symbols[0]; + const quote = this.markets[symbol]['quote']; + const lowercase = quote.toLowerCase () + 'Bal'; + const fiat = this.safeFloat (response, lowercase); + const crypto = this.account (); + crypto['total'] = btc; + const result = { 'BTC': crypto }; + result[quote] = this.account (); + result[quote]['total'] = fiat; + result['info'] = response; + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetOrderbook (params); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const ticker = await this.publicGetTickerdetailed (params); + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + const volume = this.safeFloat (ticker, 'vol'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': volume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTrades (params); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'qty': amount, + 'price': price, + 'type': side[0].toUpperCase (), + }; + const response = await this.privatePostPlaceorder (this.extend (request, params)); + return { + 'info': response, + 'id': response['pending_oid'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderNo': id, + }; + return await this.privatePostCancelpendingorder (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + path; + if (api === 'public') { + url += '.json'; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ 'timestamp': nonce }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'key': this.apiKey, + 'sig': this.hmac (this.encode (body), this.encode (this.secret), 'sha1'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if (api === 'private') { + if ('error' in response) { + if (response['error']) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + } + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/gateio.js b/TaskServer/node_modules/ccxt/js/gateio.js new file mode 100644 index 000000000..e28bd238e --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/gateio.js @@ -0,0 +1,791 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ExchangeError, ArgumentsRequired, InvalidAddress, OrderNotFound, NotSupported, DDoSProtection, InsufficientFunds, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class gateio extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'gateio', + 'name': 'Gate.io', + 'countries': [ 'CN' ], + 'version': '2', + 'rateLimit': 1000, + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchTickers': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': true, + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'fetchClosedOrders': false, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrderTrades': true, + 'fetchOrders': true, + 'fetchOrder': true, + 'fetchMyTrades': true, + }, + 'timeframes': { + '1m': 60, + '5m': 300, + '10m': 600, + '15m': 900, + '30m': 1800, + '1h': 3600, + '2h': 7200, + '4h': 14400, + '6h': 21600, + '12h': 43200, + '1d': 86400, + '1w': 604800, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg', + 'api': { + 'public': 'https://data.gate.io/api', + 'private': 'https://data.gate.io/api', + }, + 'www': 'https://gate.io/', + 'doc': 'https://gate.io/api2', + 'fees': [ + 'https://gate.io/fee', + 'https://support.gate.io/hc/en-us/articles/115003577673', + ], + 'referral': 'https://www.gate.io/signup/2436035', + }, + 'api': { + 'public': { + 'get': [ + 'candlestick2/{id}', + 'pairs', + 'marketinfo', + 'marketlist', + 'tickers', + 'ticker/{id}', + 'orderBook/{id}', + 'trade/{id}', + 'tradeHistory/{id}', + 'tradeHistory/{id}/{tid}', + ], + }, + 'private': { + 'post': [ + 'balances', + 'depositAddress', + 'newAddress', + 'depositsWithdrawals', + 'buy', + 'sell', + 'cancelOrder', + 'cancelAllOrders', + 'getOrder', + 'openOrders', + 'tradeHistory', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'maker': 0.002, + 'taker': 0.002, + }, + }, + 'exceptions': { + 'exact': { + '4': DDoSProtection, + '5': AuthenticationError, // { result: "false", code: 5, message: "Error: invalid key or sign, please re-generate it from your account" } + '6': AuthenticationError, // { result: 'false', code: 6, message: 'Error: invalid data ' } + '7': NotSupported, + '8': NotSupported, + '9': NotSupported, + '15': DDoSProtection, + '16': OrderNotFound, + '17': OrderNotFound, + '20': InvalidOrder, + '21': InsufficientFunds, + }, + // https://gate.io/api2#errCode + 'errorCodeNames': { + '1': 'Invalid request', + '2': 'Invalid version', + '3': 'Invalid request', + '4': 'Too many attempts', + '5': 'Invalid sign', + '6': 'Invalid sign', + '7': 'Currency is not supported', + '8': 'Currency is not supported', + '9': 'Currency is not supported', + '10': 'Verified failed', + '11': 'Obtaining address failed', + '12': 'Empty params', + '13': 'Internal error, please report to administrator', + '14': 'Invalid user', + '15': 'Cancel order too fast, please wait 1 min and try again', + '16': 'Invalid order id or order is already closed', + '17': 'Invalid orderid', + '18': 'Invalid amount', + '19': 'Not permitted or trade is disabled', + '20': 'Your order size is too small', + '21': 'You don\'t have enough fund', + }, + }, + 'options': { + 'fetchTradesMethod': 'public_get_tradehistory_id', // 'public_get_tradehistory_id_tid' + 'limits': { + 'cost': { + 'min': { + 'BTC': 0.0001, + 'ETH': 0.001, + 'USDT': 1, + }, + }, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketinfo (params); + const markets = this.safeValue (response, 'pairs'); + if (!markets) { + throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response'); + } + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const keys = Object.keys (market); + const id = this.safeString (keys, 0); + const details = market[id]; + // all of their symbols are separated with an underscore + // but not boe_eth_eth (BOE_ETH/ETH) which has two underscores + // https://github.com/ccxt/ccxt/issues/4894 + const parts = id.split ('_'); + const numParts = parts.length; + let baseId = parts[0]; + let quoteId = parts[1]; + if (numParts > 2) { + baseId = parts[0] + '_' + parts[1]; + quoteId = parts[2]; + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': details['decimal_places'], + }; + const amountLimits = { + 'min': details['min_amount'], + 'max': undefined, + }; + const priceLimits = { + 'min': Math.pow (10, -details['decimal_places']), + 'max': undefined, + }; + const defaultCost = amountLimits['min'] * priceLimits['min']; + const minCost = this.safeFloat (this.options['limits']['cost']['min'], quote, defaultCost); + const costLimits = { + 'min': minCost, + 'max': undefined, + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const active = true; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'maker': details['fee'] / 100, + 'taker': details['fee'] / 100, + 'precision': precision, + 'limits': limits, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalances (params); + const result = { 'info': response }; + let available = this.safeValue (response, 'available', {}); + if (Array.isArray (available)) { + available = {}; + } + const locked = this.safeValue (response, 'locked', {}); + const currencyIds = Object.keys (available); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (available, currencyId); + account['used'] = this.safeFloat (locked, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetOrderBookId (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // they return [ Timestamp, Volume, Close, High, Low, Open ] + return [ + parseInt (ohlcv[0]), // t + parseFloat (ohlcv[5]), // o + parseFloat (ohlcv[3]), // h + parseFloat (ohlcv[4]), // l + parseFloat (ohlcv[2]), // c + parseFloat (ohlcv[1]), // v + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + 'group_sec': this.timeframes[timeframe], + }; + // max limit = 1001 + if (limit !== undefined) { + const periodDurationInSeconds = this.parseTimeframe (timeframe); + const hours = parseInt ((periodDurationInSeconds * limit) / 3600); + request['range_hour'] = Math.max (0, hours - 1); + } + const response = await this.publicGetCandlestick2Id (this.extend (request, params)); + // + // { + // "elapsed": "15ms", + // "result": "true", + // "data": [ + // [ "1553930820000", "1.005299", "4081.05", "4086.18", "4081.05", "4086.18" ], + // [ "1553930880000", "0.110923277", "4095.2", "4095.23", "4091.15", "4091.15" ], + // ... + // [ "1553934420000", "0", "4089.42", "4089.42", "4089.42", "4089.42" ], + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOHLCVs (data, market, timeframe, since, limit); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'percentChange'); + let open = undefined; + let change = undefined; + let average = undefined; + if ((last !== undefined) && (percentage !== undefined)) { + const relativeChange = percentage / 100; + open = last / this.sum (1, relativeChange); + change = last - open; + average = this.sum (last, open) / 2; + } + open = this.safeFloat (ticker, 'open', open); + change = this.safeFloat (ticker, 'change', change); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat2 (ticker, 'high24hr', 'high'), + 'low': this.safeFloat2 (ticker, 'low24hr', 'low'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'quoteVolume'), // gateio has them reversed + 'quoteVolume': this.safeFloat (ticker, 'baseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const result = {}; + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const [ baseId, quoteId ] = id.split ('_'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + let market = undefined; + if (symbol in this.markets) { + market = this.markets[symbol]; + } + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + } + result[symbol] = this.parseTicker (response[id], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const ticker = await this.publicGetTickerId (this.extend ({ + 'id': market['id'], + }, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + let timestamp = this.safeTimestamp2 (trade, 'timestamp', 'time_unix'); + timestamp = this.safeTimestamp (trade, 'time', timestamp); + const id = this.safeString2 (trade, 'tradeID', 'id'); + // take either of orderid or orderId + const orderId = this.safeString2 (trade, 'orderid', 'orderNumber'); + const price = this.safeFloat2 (trade, 'rate', 'price'); + const amount = this.safeFloat (trade, 'amount'); + const type = this.safeString (trade, 'type'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': type, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const method = this.safeString (this.options, 'fetchTradesMethod', 'public_get_tradehistory_id'); + const response = await this[method] (this.extend (request, params)); + return this.parseTrades (response['data'], market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.privatePostOpenOrders (params); + return this.parseOrders (response['orders'], undefined, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderNumber': id, + 'currencyPair': this.marketId (symbol), + }; + const response = await this.privatePostGetOrder (this.extend (request, params)); + return this.parseOrder (response['order']); + } + + parseOrderStatus (status) { + const statuses = { + 'cancelled': 'canceled', + // 'closed': 'closed', // these two statuses aren't actually needed + // 'open': 'open', // as they are mapped one-to-one + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // {'amount': '0.00000000', + // 'currencyPair': 'xlm_usdt', + // 'fee': '0.0113766632239302 USDT', + // 'feeCurrency': 'USDT', + // 'feePercentage': 0.18, + // 'feeValue': '0.0113766632239302', + // 'filledAmount': '30.14004987', + // 'filledRate': 0.2097, + // 'initialAmount': '30.14004987', + // 'initialRate': '0.2097', + // 'left': 0, + // 'orderNumber': '998307286', + // 'rate': '0.2097', + // 'status': 'closed', + // 'timestamp': 1531158583, + // 'type': 'sell'}, + // + const id = this.safeString2 (order, 'orderNumber', 'id'); + let symbol = undefined; + const marketId = this.safeString (order, 'currencyPair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp2 (order, 'timestamp', 'ctime'); + const lastTradeTimestamp = this.safeTimestamp (order, 'mtime'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let side = this.safeString (order, 'type'); + // handling for order.update messages + if (side === '1') { + side = 'sell'; + } else if (side === '2') { + side = 'buy'; + } + const price = this.safeFloat2 (order, 'initialRate', 'price'); + const average = this.safeFloat (order, 'filledRate'); + const amount = this.safeFloat2 (order, 'initialAmount', 'amount'); + const filled = this.safeFloat (order, 'filledAmount'); + // In the order status response, this field has a different name. + const remaining = this.safeFloat2 (order, 'leftAmount', 'left'); + const feeCost = this.safeFloat (order, 'feeValue'); + const feeCurrencyId = this.safeString (order, 'feeCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + let feeRate = this.safeFloat (order, 'feePercentage'); + if (feeRate !== undefined) { + feeRate = feeRate / 100; + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'average': average, + 'trades': undefined, + 'fee': { + 'cost': feeCost, + 'currency': feeCurrencyCode, + 'rate': feeRate, + }, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const method = 'privatePost' + this.capitalize (side); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'rate': price, + 'amount': amount, + }; + const response = await this[method] (this.extend (request, params)); + return this.parseOrder (this.extend ({ + 'status': 'open', + 'type': side, + 'initialAmount': amount, + }, response), market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires symbol argument'); + } + await this.loadMarkets (); + const request = { + 'orderNumber': id, + 'currencyPair': this.marketId (symbol), + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async queryDepositAddress (method, code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + method = 'privatePost' + method + 'Address'; + const request = { + 'currency': currency['id'], + }; + const response = await this[method] (this.extend (request, params)); + let address = this.safeString (response, 'addr'); + let tag = undefined; + if ((address !== undefined) && (address.indexOf ('address') >= 0)) { + throw new InvalidAddress (this.id + ' queryDepositAddress ' + address); + } + if (code === 'XRP') { + const parts = address.split (' '); + address = parts[0]; + tag = parts[1]; + } + return { + 'currency': currency, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async createDepositAddress (code, params = {}) { + return await this.queryDepositAddress ('New', code, params); + } + + async fetchDepositAddress (code, params = {}) { + return await this.queryDepositAddress ('Deposit', code, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privatePostOpenOrders (params); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'orderNumber': id, + }; + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires symbol param'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, // Address must exist in you AddressBook in security settings + }; + if (tag !== undefined) { + request['address'] += ' ' + tag; + } + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const prefix = (api === 'private') ? (api + '/') : ''; + let url = this.urls['api'][api] + this.version + '/1/' + prefix + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = { 'nonce': nonce }; + body = this.urlencode (this.extend (request, query)); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Key': this.apiKey, + 'Sign': signature, + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async fetchTransactionsByType (type = undefined, code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['start'] = since; + } + const response = await this.privatePostDepositsWithdrawals (this.extend (request, params)); + let transactions = undefined; + if (type === undefined) { + const deposits = this.safeValue (response, 'deposits', []); + const withdrawals = this.safeValue (response, 'withdraws', []); + transactions = this.arrayConcat (deposits, withdrawals); + } else { + transactions = this.safeValue (response, type, []); + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + return this.parseTransactions (transactions, currency, since, limit); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType (undefined, code, since, limit, params); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('deposits', code, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchTransactionsByType ('withdraws', code, since, limit, params); + } + + parseTransaction (transaction, currency = undefined) { + // + // deposit + // + // { + // 'id': 'd16520849', + // 'currency': 'NEO', + // 'address': False, + // 'amount': '1', + // 'txid': '01acf6b8ce4d24a....', + // 'timestamp': '1553125968', + // 'status': 'DONE', + // 'type': 'deposit' + // } + // + // withdrawal + // + // { + // 'id': 'w5864259', + // 'currency': 'ETH', + // 'address': '0x72632f462....', + // 'amount': '0.4947', + // 'txid': '0x111167d120f736....', + // 'timestamp': '1553123688', + // 'status': 'DONE', + // 'type': 'withdrawal' + // } + // + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const id = this.safeString (transaction, 'id'); + const txid = this.safeString (transaction, 'txid'); + const amount = this.safeFloat (transaction, 'amount'); + let address = this.safeString (transaction, 'address'); + if (address === 'false') { + address = undefined; + } + const timestamp = this.safeTimestamp (transaction, 'timestamp'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const type = this.parseTransactionType (id[0]); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'PEND': 'pending', + 'REQUEST': 'pending', + 'DMOVE': 'pending', + 'CANCEL': 'failed', + 'DONE': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransactionType (type) { + const types = { + 'd': 'deposit', + 'w': 'withdrawal', + }; + return this.safeString (types, type, type); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + const resultString = this.safeString (response, 'result', ''); + if (resultString !== 'false') { + return; + } + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'message', body); + if (errorCode !== undefined) { + const feedback = this.safeString (this.exceptions['errorCodeNames'], errorCode, message); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/gemini.js b/TaskServer/node_modules/ccxt/js/gemini.js new file mode 100644 index 000000000..71ad81453 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/gemini.js @@ -0,0 +1,693 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, BadRequest, OrderNotFound, InvalidOrder, InvalidNonce, InsufficientFunds, AuthenticationError, PermissionDenied, NotSupported, OnMaintenance, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class gemini extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'gemini', + 'name': 'Gemini', + 'countries': [ 'US' ], + 'rateLimit': 1500, // 200 for private API + 'version': 'v1', + 'has': { + 'fetchDepositAddress': false, + 'createDepositAddress': true, + 'CORS': false, + 'fetchBidsAsks': false, + 'fetchTickers': false, + 'fetchMyTrades': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': false, + 'createMarketOrder': false, + 'withdraw': true, + 'fetchTransactions': true, + 'fetchWithdrawals': false, + 'fetchDeposits': false, + 'fetchOHLCV': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27816857-ce7be644-6096-11e7-82d6-3c257263229c.jpg', + 'api': { + 'public': 'https://api.gemini.com', + 'private': 'https://api.gemini.com', + 'web': 'https://docs.gemini.com', + }, + 'www': 'https://gemini.com/', + 'doc': [ + 'https://docs.gemini.com/rest-api', + 'https://docs.sandbox.gemini.com', + ], + 'test': 'https://api.sandbox.gemini.com', + 'fees': [ + 'https://gemini.com/api-fee-schedule', + 'https://gemini.com/trading-fees', + 'https://gemini.com/transfer-fees', + ], + }, + 'api': { + 'web': { + 'get': [ + 'rest-api', + ], + }, + 'public': { + 'get': [ + 'v1/symbols', + 'v1/pubticker/{symbol}', + 'v1/book/{symbol}', + 'v1/trades/{symbol}', + 'v1/auction/{symbol}', + 'v1/auction/{symbol}/history', + 'v2/candles/{symbol}/{timeframe}', + 'v2/ticker/{symbol}', + ], + }, + 'private': { + 'post': [ + 'v1/order/new', + 'v1/order/cancel', + 'v1/order/cancel/session', + 'v1/order/cancel/all', + 'v1/order/status', + 'v1/orders', + 'v1/mytrades', + 'v1/notionalvolume', + 'v1/tradevolume', + 'v1/transfers', + 'v1/balances', + 'v1/deposit/{currency}/newAddress', + 'v1/withdraw/{currency}', + 'v1/heartbeat', + 'v1/transfers', + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.0035, + 'maker': 0.001, + }, + }, + 'httpExceptions': { + '400': BadRequest, // Auction not open or paused, ineligible timing, market not open, or the request was malformed, in the case of a private API request, missing or malformed Gemini private API authentication headers + '403': PermissionDenied, // The API key is missing the role necessary to access this private API endpoint + '404': OrderNotFound, // Unknown API entry point or Order not found + '406': InsufficientFunds, // Insufficient Funds + '429': RateLimitExceeded, // Rate Limiting was applied + '500': ExchangeError, // The server encountered an error + '502': ExchangeError, // Technical issues are preventing the request from being satisfied + '503': OnMaintenance, // The exchange is down for maintenance + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1hr', + '6h': '6hr', + '1d': '1day', + }, + 'exceptions': { + 'exact': { + 'AuctionNotOpen': BadRequest, // Failed to place an auction-only order because there is no current auction open for this symbol + 'ClientOrderIdTooLong': BadRequest, // The Client Order ID must be under 100 characters + 'ClientOrderIdMustBeString': BadRequest, // The Client Order ID must be a string + 'ConflictingOptions': BadRequest, // New orders using a combination of order execution options are not supported + 'EndpointMismatch': BadRequest, // The request was submitted to an endpoint different than the one in the payload + 'EndpointNotFound': BadRequest, // No endpoint was specified + 'IneligibleTiming': BadRequest, // Failed to place an auction order for the current auction on this symbol because the timing is not eligible, new orders may only be placed before the auction begins. + 'InsufficientFunds': InsufficientFunds, // The order was rejected because of insufficient funds + 'InvalidJson': BadRequest, // The JSON provided is invalid + 'InvalidNonce': InvalidNonce, // The nonce was not greater than the previously used nonce, or was not present + 'InvalidOrderType': InvalidOrder, // An unknown order type was provided + 'InvalidPrice': InvalidOrder, // For new orders, the price was invalid + 'InvalidQuantity': InvalidOrder, // A negative or otherwise invalid quantity was specified + 'InvalidSide': InvalidOrder, // For new orders, and invalid side was specified + 'InvalidSignature': AuthenticationError, // The signature did not match the expected signature + 'InvalidSymbol': BadRequest, // An invalid symbol was specified + 'InvalidTimestampInPayload': BadRequest, // The JSON payload contained a timestamp parameter with an unsupported value. + 'Maintenance': OnMaintenance, // The system is down for maintenance + 'MarketNotOpen': InvalidOrder, // The order was rejected because the market is not accepting new orders + 'MissingApikeyHeader': AuthenticationError, // The X-GEMINI-APIKEY header was missing + 'MissingOrderField': InvalidOrder, // A required order_id field was not specified + 'MissingRole': AuthenticationError, // The API key used to access this endpoint does not have the required role assigned to it + 'MissingPayloadHeader': AuthenticationError, // The X-GEMINI-PAYLOAD header was missing + 'MissingSignatureHeader': AuthenticationError, // The X-GEMINI-SIGNATURE header was missing + 'NoSSL': AuthenticationError, // You must use HTTPS to access the API + 'OptionsMustBeArray': BadRequest, // The options parameter must be an array. + 'OrderNotFound': OrderNotFound, // The order specified was not found + 'RateLimit': RateLimitExceeded, // Requests were made too frequently. See Rate Limits below. + 'System': ExchangeError, // We are experiencing technical issues + 'UnsupportedOption': BadRequest, // This order execution option is not supported. + }, + 'broad': { + 'The Gemini Exchange is currently undergoing maintenance.': OnMaintenance, // The Gemini Exchange is currently undergoing maintenance. Please check https://status.gemini.com/ for more information. + }, + }, + 'options': { + 'fetchMarketsMethod': 'fetch_markets_from_web', + }, + }); + } + + async fetchMarkets (params = {}) { + const method = this.safeValue (this.options, 'fetchMarketsMethod', 'fetch_markets_from_api'); + return await this[method] (params); + } + + async fetchMarketsFromWeb (symbols = undefined, params = {}) { + const response = await this.webGetRestApi (params); + const sections = response.split ('

Symbols and minimums

'); + const numSections = sections.length; + const error = this.id + ' the ' + this.name + ' API doc HTML markup has changed, breaking the parser of order limits and precision info for ' + this.name + ' markets.'; + if (numSections !== 2) { + throw new NotSupported (error); + } + const tables = sections[1].split ('tbody>'); + const numTables = tables.length; + if (numTables < 2) { + throw new NotSupported (error); + } + // tables[1] = tables[1].replace ("\n", ''); // eslint-disable-line quotes + const rows = tables[1].split ("\n"); // eslint-disable-line quotes + const numRows = rows.length; + if (numRows < 2) { + throw new NotSupported (error); + } + const result = []; + // skip the first element (empty string) + for (let i = 1; i < numRows; i++) { + const row = rows[i]; + const cells = row.split ("\n"); // eslint-disable-line quotes + const numCells = cells.length; + if (numCells < 7) { + throw new NotSupported (error); + } + // + // [ + // 'btcusd', + // 'USD', // quote + // 'BTC', // base + // '0.00001 BTC (1e-5)', // min amount + // '0.00000001 BTC (1e-8)', // amount min tick size + // '0.01 USD', // price min tick size + // '\n' + // ] + // + let id = cells[0].replace ('', ''); + id = id.replace ('', ''); + id = id.replace ('', ''); + let baseId = cells[2].replace ('', ''); + let quoteId = cells[1].replace ('', ''); + const minAmountAsString = cells[3].replace ('', ''); + const amountTickSizeAsString = cells[4].replace ('', ''); + const priceTickSizeAsString = cells[5].replace ('', ''); + const minAmount = minAmountAsString.split (' '); + const amountPrecision = amountTickSizeAsString.split (' '); + const pricePrecision = priceTickSizeAsString.split (' '); + baseId = baseId.toLowerCase (); + quoteId = quoteId.toLowerCase (); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.precisionFromString (amountPrecision[0]), + 'price': this.precisionFromString (pricePrecision[0]), + }; + const active = undefined; + result.push ({ + 'id': id, + 'info': row, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': parseFloat (minAmount[0]), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchMarketsFromAPI (params = {}) { + const response = await this.publicGetV1Symbols (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const id = response[i]; + const market = id; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': undefined, + 'price': undefined, + }; + result.push ({ + 'id': id, + 'info': market, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit_bids'] = limit; + request['limit_asks'] = limit; + } + const response = await this.publicGetV1BookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const ticker = await this.publicGetV1PubtickerSymbol (this.extend (request, params)); + const timestamp = this.safeInteger (ticker['volume'], 'timestamp'); + const baseCurrency = market['base']; // unified structures are guaranteed to have unified fields + const quoteCurrency = market['quote']; // so we don't need safe-methods for unified structures + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker['volume'], baseCurrency), + 'quoteVolume': this.safeFloat (ticker['volume'], quoteCurrency), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'timestampms'); + const id = this.safeString (trade, 'tid'); + const orderId = this.safeString (trade, 'order_id'); + const feeCurrencyId = this.safeString (trade, 'fee_currency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + const fee = { + 'cost': this.safeFloat (trade, 'fee_amount'), + 'currency': feeCurrencyCode, + }; + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const type = undefined; + const side = this.safeStringLower (trade, 'type'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'order': orderId, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'cost': cost, + 'amount': amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetV1TradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostV1Balances (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'amount'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + const timestamp = this.safeInteger (order, 'timestampms'); + const amount = this.safeFloat (order, 'original_amount'); + const remaining = this.safeFloat (order, 'remaining_amount'); + const filled = this.safeFloat (order, 'executed_amount'); + let status = 'closed'; + if (order['is_live']) { + status = 'open'; + } + if (order['is_cancelled']) { + status = 'canceled'; + } + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'avg_execution_price'); + let cost = undefined; + if (filled !== undefined) { + if (average !== undefined) { + cost = filled * average; + } + } + let type = this.safeString (order, 'type'); + if (type === 'exchange limit') { + type = 'limit'; + } else if (type === 'market buy' || type === 'market sell') { + type = 'market'; + } else { + type = order['type']; + } + const fee = undefined; + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'order_id'); + const side = this.safeStringLower (order, 'side'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + const response = await this.privatePostV1OrderStatus (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostV1Orders (params); + let orders = this.parseOrders (response, undefined, since, limit); + if (symbol !== undefined) { + const market = this.market (symbol); // throws on non-existent symbol + orders = this.filterBySymbol (orders, market['symbol']); + } + return orders; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const nonce = this.nonce (); + const request = { + 'client_order_id': nonce.toString (), + 'symbol': this.marketId (symbol), + 'amount': amount.toString (), + 'price': price.toString (), + 'side': side, + 'type': 'exchange limit', // gemini allows limit orders only + }; + const response = await this.privatePostV1OrderNew (this.extend (request, params)); + return { + 'info': response, + 'id': response['order_id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + return await this.privatePostV1OrderCancel (this.extend (request, params)); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit_trades'] = limit; + } + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.privatePostV1Mytrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + }; + const response = await this.privatePostV1WithdrawCurrency (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response, 'txHash'), + }; + } + + nonce () { + return this.milliseconds (); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (limit !== undefined) { + request['limit_transfers'] = limit; + } + if (since !== undefined) { + request['timestamp'] = since; + } + const response = await this.privatePostV1Transfers (this.extend (request, params)); + return this.parseTransactions (response); + } + + parseTransaction (transaction, currency = undefined) { + const timestamp = this.safeInteger (transaction, 'timestampms'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const address = this.safeString (transaction, 'destination'); + const type = this.safeStringLower (transaction, 'type'); + let status = 'pending'; + // When deposits show as Advanced or Complete they are available for trading. + if (transaction['status']) { + status = 'ok'; + } + let fee = undefined; + const feeAmount = this.safeFloat (transaction, 'feeAmount'); + if (feeAmount !== undefined) { + fee = { + 'cost': feeAmount, + 'currency': code, + }; + } + return { + 'info': transaction, + 'id': this.safeString (transaction, 'eid'), + 'txid': this.safeString (transaction, 'txHash'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': undefined, // or is it defined? + 'type': type, // direction of the transaction, ('deposit' | 'withdraw') + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'updated': undefined, + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = this.extend ({ + 'request': url, + 'nonce': nonce, + }, query); + let payload = this.json (request); + payload = this.stringToBase64 (this.encode (payload)); + const signature = this.hmac (payload, this.encode (this.secret), 'sha384'); + headers = { + 'Content-Type': 'text/plain', + 'X-GEMINI-APIKEY': this.apiKey, + 'X-GEMINI-PAYLOAD': this.decode (payload), + 'X-GEMINI-SIGNATURE': signature, + }; + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + url = this.urls['api'][api] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + if (typeof body === 'string') { + const feedback = this.id + ' ' + body; + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + } + return; // fallback to default error handler + } + // + // { + // "result": "error", + // "reason": "BadNonce", + // "message": "Out-of-sequence nonce <1234> precedes previously used nonce <2345>" + // } + // + const result = this.safeString (response, 'result'); + if (result === 'error') { + const reason = this.safeString (response, 'reason'); + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + message; + this.throwExactlyMatchedException (this.exceptions['exact'], reason, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostV1DepositCurrencyNewAddress (this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'timeframe': this.timeframes[timeframe], + 'symbol': market['id'], + }; + const response = await this.publicGetV2CandlesSymbolTimeframe (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/hitbtc.js b/TaskServer/node_modules/ccxt/js/hitbtc.js new file mode 100644 index 000000000..5ee42b767 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/hitbtc.js @@ -0,0 +1,1035 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, InsufficientFunds, OrderNotFound, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class hitbtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'hitbtc', + 'name': 'HitBTC', + 'countries': [ 'HK' ], + 'rateLimit': 1500, + 'version': '1', + 'has': { + 'CORS': false, + 'fetchTrades': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrderTrades': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg', + 'api': 'https://api.hitbtc.com', + 'www': 'https://hitbtc.com', + 'referral': 'https://hitbtc.com/?ref_id=5a5d39a65d466', + 'doc': 'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv1.md', + 'fees': [ + 'https://hitbtc.com/fees-and-limits', + 'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits', + ], + }, + 'api': { + 'public': { + 'get': [ + '{symbol}/orderbook', + '{symbol}/ticker', + '{symbol}/trades', + '{symbol}/trades/recent', + 'symbols', + 'ticker', + 'time', + ], + }, + 'trading': { + 'get': [ + 'balance', + 'orders/active', + 'orders/recent', + 'order', + 'trades/by/order', + 'trades', + ], + 'post': [ + 'new_order', + 'cancel_order', + 'cancel_orders', + ], + }, + 'payment': { + 'get': [ + 'balance', + 'address/{currency}', + 'transactions', + 'transactions/{transaction}', + ], + 'post': [ + 'transfer_to_trading', + 'transfer_to_main', + 'address/{currency}', + 'payout', + ], + }, + }, + // hardcoded fees are deprecated and should only be used when there's no other way to get fee info + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.07 / 100, + 'taker': 0.07 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'BCC': 0.0018, + 'ETH': 0.00215, + 'BCH': 0.0018, + 'USDT': 100, + 'DASH': 0.03, + 'BTG': 0.0005, + 'LTC': 0.003, + 'ZEC': 0.0001, + 'XMR': 0.09, + '1ST': 0.84, + 'ADX': 5.7, + 'AE': 6.7, + 'AEON': 0.01006, + 'AIR': 565, + 'AMP': 9, + 'ANT': 6.7, + 'ARDR': 1, + 'ARN': 18.5, + 'ART': 26, + 'ATB': 0.0004, + 'ATL': 27, + 'ATM': 504, + 'ATS': 860, + 'AVT': 1.9, + 'BAS': 113, + 'BCN': 0.1, + 'DAO.Casino': 124, // id = 'BET' + 'BKB': 46, + 'BMC': 32, + 'BMT': 100, + 'BNT': 2.57, + 'BQX': 4.7, + 'BTM': 40, + 'BTX': 0.04, + 'BUS': 0.004, + 'CCT': 115, + 'CDT': 100, + 'CDX': 30, + 'CFI': 61, + 'CLD': 0.88, + 'CND': 574, + 'CNX': 0.04, + 'COSS': 65, + 'CSNO': 16, + 'CTR': 15, + 'CTX': 146, + 'CVC': 8.46, + 'DBIX': 0.0168, + 'DCN': 120000, + 'DCT': 0.02, + 'DDF': 342, + 'DENT': 6240, + 'DGB': 0.4, + 'DGD': 0.01, + 'DICE': 0.32, + 'DLT': 0.26, + 'DNT': 0.21, + 'DOGE': 2, + 'DOV': 34, + 'DRPU': 24, + 'DRT': 240, + 'DSH': 0.017, + 'EBET': 84, + 'EBTC': 20, + 'EBTCOLD': 6.6, + 'ECAT': 14, + 'EDG': 2, + 'EDO': 2.9, + 'ELE': 0.00172, + 'ELM': 0.004, + 'EMC': 0.03, + 'EMGO': 14, + 'ENJ': 163, + 'EOS': 1.5, + 'ERO': 34, + 'ETBS': 15, + 'ETC': 0.002, + 'ETP': 0.004, + 'EVX': 5.4, + 'EXN': 456, + 'FRD': 65, + 'FUEL': 123.00105, + 'FUN': 202.9598309, + 'FYN': 1.849, + 'FYP': 66.13, + 'GNO': 0.0034, + 'GUP': 4, + 'GVT': 1.2, + 'HAC': 144, + 'HDG': 7, + 'HGT': 1082, + 'HPC': 0.4, + 'HVN': 120, + 'ICN': 0.55, + 'ICO': 34, + 'ICOS': 0.35, + 'IND': 76, + 'INDI': 5913, + 'ITS': 15.0012, + 'IXT': 11, + 'KBR': 143, + 'KICK': 112, + 'LA': 41, + 'LAT': 1.44, + 'LIFE': 13000, + 'LRC': 27, + 'LSK': 0.3, + 'LUN': 0.34, + 'MAID': 5, + 'MANA': 143, + 'MCAP': 5.44, + 'MIPS': 43, + 'MNE': 1.33, + 'MSP': 121, + 'MTH': 92, + 'MYB': 3.9, + 'NDC': 165, + 'NEBL': 0.04, + 'NET': 3.96, + 'NTO': 998, + 'NXC': 13.39, + 'NXT': 3, + 'OAX': 15, + 'ODN': 0.004, + 'OMG': 2, + 'OPT': 335, + 'ORME': 2.8, + 'OTN': 0.57, + 'PAY': 3.1, + 'PIX': 96, + 'PLBT': 0.33, + 'PLR': 114, + 'PLU': 0.87, + 'POE': 784, + 'POLL': 3.5, + 'PPT': 2, + 'PRE': 32, + 'PRG': 39, + 'PRO': 41, + 'PRS': 60, + 'PTOY': 0.5, + 'QAU': 63, + 'QCN': 0.03, + 'QTUM': 0.04, + 'QVT': 64, + 'REP': 0.02, + 'RKC': 15, + 'RVT': 14, + 'SAN': 2.24, + 'SBD': 0.03, + 'SCL': 2.6, + 'SISA': 1640, + 'SKIN': 407, + 'SMART': 0.4, + 'SMS': 0.0375, + 'SNC': 36, + 'SNGLS': 4, + 'SNM': 48, + 'SNT': 233, + 'STEEM': 0.01, + 'STRAT': 0.01, + 'STU': 14, + 'STX': 11, + 'SUB': 17, + 'SUR': 3, + 'SWT': 0.51, + 'TAAS': 0.91, + 'TBT': 2.37, + 'TFL': 15, + 'TIME': 0.03, + 'TIX': 7.1, + 'TKN': 1, + 'TKR': 84, + 'TNT': 90, + 'TRST': 1.6, + 'TRX': 1395, + 'UET': 480, + 'UGT': 15, + 'VEN': 14, + 'VERI': 0.037, + 'VIB': 50, + 'VIBE': 145, + 'VOISE': 618, + 'WEALTH': 0.0168, + 'WINGS': 2.4, + 'WTC': 0.75, + 'XAUR': 3.23, + 'XDN': 0.01, + 'XEM': 15, + 'XUC': 0.9, + 'YOYOW': 140, + 'ZAP': 24, + 'ZRX': 23, + 'ZSC': 191, + }, + 'deposit': { + 'BTC': 0.0006, + 'ETH': 0.003, + 'BCH': 0, + 'USDT': 0, + 'BTG': 0, + 'LTC': 0, + 'ZEC': 0, + 'XMR': 0, + '1ST': 0, + 'ADX': 0, + 'AE': 0, + 'AEON': 0, + 'AIR': 0, + 'AMP': 0, + 'ANT': 0, + 'ARDR': 0, + 'ARN': 0, + 'ART': 0, + 'ATB': 0, + 'ATL': 0, + 'ATM': 0, + 'ATS': 0, + 'AVT': 0, + 'BAS': 0, + 'BCN': 0, + 'DAO.Casino': 0, // id = 'BET' + 'BKB': 0, + 'BMC': 0, + 'BMT': 0, + 'BNT': 0, + 'BQX': 0, + 'BTM': 0, + 'BTX': 0, + 'BUS': 0, + 'CCT': 0, + 'CDT': 0, + 'CDX': 0, + 'CFI': 0, + 'CLD': 0, + 'CND': 0, + 'CNX': 0, + 'COSS': 0, + 'CSNO': 0, + 'CTR': 0, + 'CTX': 0, + 'CVC': 0, + 'DBIX': 0, + 'DCN': 0, + 'DCT': 0, + 'DDF': 0, + 'DENT': 0, + 'DGB': 0, + 'DGD': 0, + 'DICE': 0, + 'DLT': 0, + 'DNT': 0, + 'DOGE': 0, + 'DOV': 0, + 'DRPU': 0, + 'DRT': 0, + 'DSH': 0, + 'EBET': 0, + 'EBTC': 0, + 'EBTCOLD': 0, + 'ECAT': 0, + 'EDG': 0, + 'EDO': 0, + 'ELE': 0, + 'ELM': 0, + 'EMC': 0, + 'EMGO': 0, + 'ENJ': 0, + 'EOS': 0, + 'ERO': 0, + 'ETBS': 0, + 'ETC': 0, + 'ETP': 0, + 'EVX': 0, + 'EXN': 0, + 'FRD': 0, + 'FUEL': 0, + 'FUN': 0, + 'FYN': 0, + 'FYP': 0, + 'GNO': 0, + 'GUP': 0, + 'GVT': 0, + 'HAC': 0, + 'HDG': 0, + 'HGT': 0, + 'HPC': 0, + 'HVN': 0, + 'ICN': 0, + 'ICO': 0, + 'ICOS': 0, + 'IND': 0, + 'INDI': 0, + 'ITS': 0, + 'IXT': 0, + 'KBR': 0, + 'KICK': 0, + 'LA': 0, + 'LAT': 0, + 'LIFE': 0, + 'LRC': 0, + 'LSK': 0, + 'LUN': 0, + 'MAID': 0, + 'MANA': 0, + 'MCAP': 0, + 'MIPS': 0, + 'MNE': 0, + 'MSP': 0, + 'MTH': 0, + 'MYB': 0, + 'NDC': 0, + 'NEBL': 0, + 'NET': 0, + 'NTO': 0, + 'NXC': 0, + 'NXT': 0, + 'OAX': 0, + 'ODN': 0, + 'OMG': 0, + 'OPT': 0, + 'ORME': 0, + 'OTN': 0, + 'PAY': 0, + 'PIX': 0, + 'PLBT': 0, + 'PLR': 0, + 'PLU': 0, + 'POE': 0, + 'POLL': 0, + 'PPT': 0, + 'PRE': 0, + 'PRG': 0, + 'PRO': 0, + 'PRS': 0, + 'PTOY': 0, + 'QAU': 0, + 'QCN': 0, + 'QTUM': 0, + 'QVT': 0, + 'REP': 0, + 'RKC': 0, + 'RVT': 0, + 'SAN': 0, + 'SBD': 0, + 'SCL': 0, + 'SISA': 0, + 'SKIN': 0, + 'SMART': 0, + 'SMS': 0, + 'SNC': 0, + 'SNGLS': 0, + 'SNM': 0, + 'SNT': 0, + 'STEEM': 0, + 'STRAT': 0, + 'STU': 0, + 'STX': 0, + 'SUB': 0, + 'SUR': 0, + 'SWT': 0, + 'TAAS': 0, + 'TBT': 0, + 'TFL': 0, + 'TIME': 0, + 'TIX': 0, + 'TKN': 0, + 'TKR': 0, + 'TNT': 0, + 'TRST': 0, + 'TRX': 0, + 'UET': 0, + 'UGT': 0, + 'VEN': 0, + 'VERI': 0, + 'VIB': 0, + 'VIBE': 0, + 'VOISE': 0, + 'WEALTH': 0, + 'WINGS': 0, + 'WTC': 0, + 'XAUR': 0, + 'XDN': 0, + 'XEM': 0, + 'XUC': 0, + 'YOYOW': 0, + 'ZAP': 0, + 'ZRX': 0, + 'ZSC': 0, + }, + }, + }, + 'commonCurrencies': { + 'BET': 'DAO.Casino', + 'CAT': 'BitClave', + 'CPT': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 + 'DRK': 'DASH', + 'EMGO': 'MGO', + 'GET': 'Themis', + 'HSR': 'HC', + 'LNC': 'LinkerCoin', + 'UNC': 'Unigame', + 'USD': 'USDT', + 'XBT': 'BTC', + }, + 'exceptions': { + 'exact': { + '2001': BadSymbol, // {"error":{"code":2001,"message":"Symbol not found","description":"Try get /api/2/public/symbol, to get list of all available symbols."}} + }, + 'broad': { + }, + }, + 'options': { + 'defaultTimeInForce': 'FOK', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetSymbols (params); + const markets = this.safeValue (response, 'symbols'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'commodity'); + const quoteId = this.safeString (market, 'currency'); + const lot = this.safeFloat (market, 'lot'); + const step = this.safeFloat (market, 'step'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'lot': lot, + 'step': step, + 'active': true, + 'maker': this.safeFloat (market, 'provideLiquidityRate'), + 'taker': this.safeFloat (market, 'takeLiquidityRate'), + 'precision': { + 'amount': this.precisionFromString (market['lot']), + 'price': this.precisionFromString (market['step']), + }, + 'limits': { + 'amount': { + 'min': lot, + 'max': undefined, + }, + 'price': { + 'min': step, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + let method = this.safeString (params, 'type', 'trading'); + method += 'GetBalance'; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + const balances = this.safeValue (response, 'balance', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat2 (balance, 'cash', 'balance'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const orderbook = await this.publicGetSymbolOrderbook (this.extend ({ + 'symbol': this.marketId (symbol), + }, params)); + return this.parseOrderBook (orderbook); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'volume_quote'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTicker (params); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetSymbolTicker (this.extend (request, params)); + if ('message' in response) { + throw new ExchangeError (this.id + ' ' + response['message']); + } + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + if (Array.isArray (trade)) { + return this.parsePublicTrade (trade, market); + } + return this.parseOrderTrade (trade, market); + } + + parsePublicTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = undefined; + const tradeLength = trade.length; + if (tradeLength > 4) { + side = trade[4]; + } + const price = parseFloat (trade[1]); + const amount = parseFloat (trade[2]); + const cost = price * amount; + return { + 'info': trade, + 'id': trade[0].toString (), + 'timestamp': trade[3], + 'datetime': this.iso8601 (trade[3]), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + parseOrderTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let amount = this.safeFloat (trade, 'execQuantity'); + if (market) { + amount *= market['lot']; + } + const price = this.safeFloat (trade, 'execPrice'); + const cost = price * amount; + const fee = { + 'cost': this.safeFloat (trade, 'fee'), + 'currency': undefined, + 'rate': undefined, + }; + const timestamp = this.safeInteger (trade, 'timestamp'); + const id = this.safeString (trade, 'tradeId'); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const orderId = this.safeString (trade, 'clientOrderId'); + const side = this.safeString (trade, 'side'); + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + // 'from': 0, + // 'till': 100, + // 'by': 'ts', // or by trade_id + // 'sort': 'desc', // or asc + // 'start_index': 0, + // 'max_results': 1000, + // 'format_item': 'object', + // 'format_price': 'number', + // 'format_amount': 'number', + // 'format_tid': 'string', + // 'format_timestamp': 'millisecond', + // 'format_wrap': false, + // 'side': 'true', + }; + if (since !== undefined) { + request['by'] = 'ts'; + request['from'] = since; + } + if (limit !== undefined) { + request['max_results'] = limit; + } + const response = await this.publicGetSymbolTrades (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // check if amount can be evenly divided into lots + // they want integer quantity in lot units + const quantity = parseFloat (amount) / market['lot']; + const wholeLots = Math.round (quantity); + const difference = quantity - wholeLots; + if (Math.abs (difference) > market['step']) { + throw new ExchangeError (this.id + ' order amount should be evenly divisible by lot unit size of ' + market['lot'].toString ()); + } + const clientOrderId = this.milliseconds (); + const request = { + 'clientOrderId': clientOrderId.toString (), + 'symbol': market['id'], + 'side': side, + 'quantity': wholeLots.toString (), // quantity in integer lot units + 'type': type, + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } else { + request['timeInForce'] = this.options['defaultTimeInForce']; + } + const response = await this.tradingPostNewOrder (this.extend (request, params)); + const order = this.parseOrder (response['ExecutionReport'], market); + if (order['status'] === 'rejected') { + throw new InvalidOrder (this.id + ' order was rejected by the exchange ' + this.json (order)); + } + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + return await this.tradingPostCancelOrder (this.extend (request, params)); + } + + parseOrderStatus (status) { + const statuses = { + 'new': 'open', + 'partiallyFilled': 'open', + 'filled': 'closed', + 'canceled': 'canceled', + 'rejected': 'rejected', + 'expired': 'expired', + }; + return this.safeString (statuses, status); + } + + parseOrder (order, market = undefined) { + let timestamp = this.safeInteger (order, 'lastTimestamp'); + if (timestamp === undefined) { + timestamp = this.safeInteger (order, 'timestamp'); + } + let symbol = undefined; + if (market === undefined) { + market = this.markets_by_id[order['symbol']]; + } + const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); + let price = this.safeFloat (order, 'orderPrice'); + price = this.safeFloat (order, 'price', price); + price = this.safeFloat (order, 'avgPrice', price); + let amount = this.safeFloat (order, 'orderQuantity'); + amount = this.safeFloat (order, 'quantity', amount); + let remaining = this.safeFloat (order, 'quantityLeaves'); + remaining = this.safeFloat (order, 'leavesQuantity', remaining); + let filled = undefined; + let cost = undefined; + const amountDefined = (amount !== undefined); + const remainingDefined = (remaining !== undefined); + if (market !== undefined) { + symbol = market['symbol']; + if (amountDefined) { + amount *= market['lot']; + } + if (remainingDefined) { + remaining *= market['lot']; + } + } else { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (amountDefined) { + if (remainingDefined) { + filled = amount - remaining; + if (price !== undefined) { + cost = price * filled; + } + } + } + const feeCost = this.safeFloat (order, 'fee'); + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + const fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': undefined, + }; + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const id = this.safeString (order, 'clientOrderId'); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.tradingGetOrder (this.extend (request, params)); + if (response['orders'][0]) { + return this.parseOrder (response['orders'][0]); + } + throw new OrderNotFound (this.id + ' fetchOrder() error: ' + this.response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const statuses = [ 'new', 'partiallyFiiled' ]; + let market = undefined; + const request = { + 'sort': 'desc', + 'statuses': statuses.join (','), + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbols'] = market['id']; + } + const response = await this.tradingGetOrdersActive (this.extend (request, params)); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const statuses = [ 'filled', 'canceled', 'rejected', 'expired' ]; + const request = { + 'sort': 'desc', + 'statuses': statuses.join (','), + 'max_results': 1000, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbols'] = market['id']; + } + const response = await this.tradingGetOrdersRecent (this.extend (request, params)); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.tradingGetTradesByOrder (this.extend (request, params)); + return this.parseTrades (response['trades'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency_code': currency['id'], + 'amount': amount, + 'address': address, + }; + if (tag !== undefined) { + request['extra_id'] = tag; + } + const response = await this.paymentPostPayout (this.extend (request, params)); + return { + 'info': response, + 'id': response['transaction'], + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + 'api' + '/' + this.version + '/' + api + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const payload = { 'nonce': nonce, 'apikey': this.apiKey }; + query = this.extend (payload, query); + if (method === 'GET') { + url += '?' + this.urlencode (query); + } else { + url += '?' + this.urlencode (payload); + } + let auth = url; + if (method === 'POST') { + if (Object.keys (query).length) { + body = this.urlencode (query); + auth += body; + } + } + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Signature': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512').toLowerCase (), + }; + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('code' in response) { + if ('ExecutionReport' in response) { + if (response['ExecutionReport']['orderRejectReason'] === 'orderExceedsLimit') { + throw new InsufficientFunds (this.id + ' ' + this.json (response)); + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // fallback to default error handler + } + const error = this.safeValue (response, 'error'); + if (error) { + const code = this.safeValue (error, 'code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown error + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/hitbtc2.js b/TaskServer/node_modules/ccxt/js/hitbtc2.js new file mode 100644 index 000000000..502024ec2 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/hitbtc2.js @@ -0,0 +1,1503 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const hitbtc = require ('./hitbtc'); +const { BadSymbol, PermissionDenied, ExchangeError, ExchangeNotAvailable, OrderNotFound, InsufficientFunds, InvalidOrder, RequestTimeout, AuthenticationError } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); +// --------------------------------------------------------------------------- + +module.exports = class hitbtc2 extends hitbtc { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'hitbtc2', + 'name': 'HitBTC', + 'countries': [ 'HK' ], + 'rateLimit': 1500, + 'version': '2', + 'has': { + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'CORS': true, + 'editOrder': true, + 'fetchCurrencies': true, + 'fetchOHLCV': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchOrderTrades': false, // not implemented yet + 'fetchDeposits': false, + 'fetchWithdrawals': false, + 'fetchTransactions': true, + 'fetchTradingFee': true, + }, + 'timeframes': { + '1m': 'M1', + '3m': 'M3', + '5m': 'M5', + '15m': 'M15', + '30m': 'M30', // default + '1h': 'H1', + '4h': 'H4', + '1d': 'D1', + '1w': 'D7', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg', + 'api': 'https://api.hitbtc.com', + 'www': 'https://hitbtc.com', + 'referral': 'https://hitbtc.com/?ref_id=5a5d39a65d466', + 'doc': [ + 'https://api.hitbtc.com', + 'https://github.com/hitbtc-com/hitbtc-api/blob/master/APIv2.md', + ], + 'fees': [ + 'https://hitbtc.com/fees-and-limits', + 'https://support.hitbtc.com/hc/en-us/articles/115005148605-Fees-and-limits', + ], + }, + 'api': { + 'public': { + 'get': [ + 'symbol', // Available Currency Symbols + 'symbol/{symbol}', // Get symbol info + 'currency', // Available Currencies + 'currency/{currency}', // Get currency info + 'ticker', // Ticker list for all symbols + 'ticker/{symbol}', // Ticker for symbol + 'trades/{symbol}', // Trades + 'orderbook/{symbol}', // Orderbook + 'candles/{symbol}', // Candles + ], + }, + 'private': { + 'get': [ + 'order', // List your current open orders + 'order/{clientOrderId}', // Get a single order by clientOrderId + 'trading/balance', // Get trading balance + 'trading/fee/all', // Get trading fee rate + 'trading/fee/{symbol}', // Get trading fee rate + 'history/trades', // Get historical trades + 'history/order', // Get historical orders + 'history/order/{id}/trades', // Get historical trades by specified order + 'account/balance', // Get main acccount balance + 'account/transactions', // Get account transactions + 'account/transactions/{id}', // Get account transaction by id + 'account/crypto/address/{currency}', // Get deposit crypro address + ], + 'post': [ + 'order', // Create new order + 'account/crypto/withdraw', // Withdraw crypro + 'account/crypto/address/{currency}', // Create new deposit crypro address + 'account/transfer', // Transfer amount to trading + ], + 'put': [ + 'order/{clientOrderId}', // Create new order + 'account/crypto/withdraw/{id}', // Commit withdraw crypro + ], + 'delete': [ + 'order', // Cancel all open orders + 'order/{clientOrderId}', // Cancel order + 'account/crypto/withdraw/{id}', // Rollback withdraw crypro + ], + 'patch': [ + 'order/{clientOrderId}', // Cancel Replace order + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'BCC': 0.0018, + 'ETH': 0.00958, + 'BCH': 0.0018, + 'USDT': 100, + 'DASH': 0.03, + 'BTG': 0.0005, + 'XRP': 0.509, + 'LTC': 0.003, + 'ZEC': 0.0001, + 'XMR': 0.09, + '1ST': 0.84, + 'ADX': 5.7, + 'AE': 6.7, + 'AEON': 0.01006, + 'AIR': 565, + 'AMM': 14, + 'AMP': 342, + 'ANT': 6.7, + 'ARDR': 1, + 'ARN': 18.5, + 'ART': 26, + 'ATB': 0.0004, + 'ATL': 27, + 'ATM': 504, + 'ATS': 860, + 'AVT': 1.9, + 'BAS': 113, + 'BCN': 0.1, + 'BET': 124, + 'BKB': 46, + 'BMC': 32, + 'BMT': 100, + 'BNT': 2.57, + 'BQX': 4.7, + 'BTCA': 351.21, + 'BTM': 40, + 'BTX': 0.04, + 'BUS': 0.004, + 'CAPP': 97, + 'CCT': 6, + 'CDT': 100, + 'CDX': 30, + 'CFI': 61, + 'CL': 13.85, + 'CLD': 0.88, + 'CND': 574, + 'CNX': 0.04, + 'COSS': 65, + 'CPAY': 5.487, + 'CSNO': 16, + 'CTR': 15, + 'CTX': 146, + 'CVC': 8.46, + 'DATA': 12.949, + 'DBIX': 0.0168, + 'DCN': 1280, + 'DCT': 0.02, + 'DDF': 342, + 'DENT': 1000, + 'DGB': 0.4, + 'DGD': 0.01, + 'DICE': 0.32, + 'DLT': 0.26, + 'DNT': 0.21, + 'DOGE': 2, + 'DOV': 34, + 'DRPU': 24, + 'DRT': 240, + 'DSH': 0.017, + 'EBET': 84, + 'EBTC': 20, + 'EBTCOLD': 6.6, + 'ECAT': 14, + 'EDG': 2, + 'EDO': 2.9, + 'EKO': 1136.36, + 'ELE': 0.00172, + 'ELM': 0.004, + 'EMC': 0.03, + 'MGO': 14, + 'ENJ': 163, + 'EOS': 1.5, + 'ERO': 34, + 'ETBS': 15, + 'ETC': 0.002, + 'ETP': 0.004, + 'EVX': 5.4, + 'EXN': 456, + 'FCN': 0.000005, + 'FRD': 65, + 'FUEL': 123.00105, + 'FUN': 202.9598309, + 'FYN': 1.849, + 'FYP': 66.13, + 'GAME': 0.004, + 'GNO': 0.0034, + 'GUP': 4, + 'GVT': 1.2, + 'HSR': 0.04, + 'HAC': 144, + 'HDG': 7, + 'HGT': 1082, + 'HPC': 0.4, + 'HVN': 120, + 'ICN': 0.55, + 'ICO': 34, + 'ICOS': 0.35, + 'IND': 76, + 'INDI': 790, + 'ITS': 15.0012, + 'IXT': 11, + 'KBR': 143, + 'KICK': 112, + 'KMD': 4, + 'LA': 41, + 'LEND': 388, + 'LAT': 1.44, + 'LIFE': 13000, + 'LRC': 27, + 'LSK': 0.3, + 'LOC': 11.076, + 'LUN': 0.34, + 'MAID': 5, + 'MANA': 143, + 'MCAP': 5.44, + 'MIPS': 43, + 'MNE': 1.33, + 'MSP': 121, + 'MCO': 0.357, + 'MTH': 92, + 'MYB': 3.9, + 'NDC': 165, + 'NEBL': 0.04, + 'NET': 3.96, + 'NTO': 998, + 'NGC': 2.368, + 'NXC': 13.39, + 'NXT': 3, + 'OAX': 15, + 'ODN': 0.004, + 'OMG': 2, + 'OPT': 335, + 'ORME': 2.8, + 'OTN': 0.57, + 'PAY': 3.1, + 'PIX': 96, + 'PLBT': 0.33, + 'PLR': 114, + 'PLU': 0.87, + 'POE': 784, + 'POLL': 3.5, + 'PPT': 2, + 'PRE': 32, + 'PRG': 39, + 'PRO': 41, + 'PRS': 60, + 'PTOY': 0.5, + 'QAU': 63, + 'QCN': 0.03, + 'QTUM': 0.04, + 'QVT': 64, + 'REP': 0.02, + 'RKC': 15, + 'RLC': 1.21, + 'RVT': 14, + 'SC': 30, + 'SAN': 2.24, + 'SBD': 0.03, + 'SCL': 2.6, + 'SISA': 1640, + 'SKIN': 407, + 'SWFTC': 352.94, + 'SMART': 0.4, + 'SMS': 0.0375, + 'SNC': 36, + 'SNGLS': 4, + 'SNM': 48, + 'SNT': 233, + 'STAR': 0.144, + 'STORM': 153.19, + 'STEEM': 0.01, + 'STRAT': 0.01, + 'SPF': 14.4, + 'STU': 14, + 'STX': 11, + 'SUB': 17, + 'SUR': 3, + 'SWT': 0.51, + 'TAAS': 0.91, + 'TBT': 2.37, + 'TFL': 15, + 'TIME': 0.03, + 'TIX': 7.1, + 'TKN': 1, + 'TGT': 173, + 'TKR': 84, + 'TNT': 90, + 'TRST': 1.6, + 'TRX': 270, + 'UET': 480, + 'UGT': 15, + 'UTT': 3, + 'VEN': 14, + 'VERI': 0.037, + 'VIB': 50, + 'VIBE': 145, + 'VOISE': 618, + 'WEALTH': 0.0168, + 'WINGS': 2.4, + 'WTC': 0.75, + 'WRC': 48, + 'XAUR': 3.23, + 'XDN': 0.01, + 'XEM': 15, + 'XUC': 0.9, + 'YOYOW': 140, + 'ZAP': 24, + 'ZRX': 23, + 'ZSC': 191, + }, + 'deposit': { + 'BTC': 0, + 'ETH': 0, + 'BCH': 0, + 'USDT': 0, + 'BTG': 0, + 'LTC': 0, + 'ZEC': 0, + 'XMR': 0, + '1ST': 0, + 'ADX': 0, + 'AE': 0, + 'AEON': 0, + 'AIR': 0, + 'AMP': 0, + 'ANT': 0, + 'ARDR': 0, + 'ARN': 0, + 'ART': 0, + 'ATB': 0, + 'ATL': 0, + 'ATM': 0, + 'ATS': 0, + 'AVT': 0, + 'BAS': 0, + 'BCN': 0, + 'BET': 0, + 'BKB': 0, + 'BMC': 0, + 'BMT': 0, + 'BNT': 0, + 'BQX': 0, + 'BTM': 0, + 'BTX': 0, + 'BUS': 0, + 'CCT': 0, + 'CDT': 0, + 'CDX': 0, + 'CFI': 0, + 'CLD': 0, + 'CND': 0, + 'CNX': 0, + 'COSS': 0, + 'CSNO': 0, + 'CTR': 0, + 'CTX': 0, + 'CVC': 0, + 'DBIX': 0, + 'DCN': 0, + 'DCT': 0, + 'DDF': 0, + 'DENT': 0, + 'DGB': 0, + 'DGD': 0, + 'DICE': 0, + 'DLT': 0, + 'DNT': 0, + 'DOGE': 0, + 'DOV': 0, + 'DRPU': 0, + 'DRT': 0, + 'DSH': 0, + 'EBET': 0, + 'EBTC': 0, + 'EBTCOLD': 0, + 'ECAT': 0, + 'EDG': 0, + 'EDO': 0, + 'ELE': 0, + 'ELM': 0, + 'EMC': 0, + 'EMGO': 0, + 'ENJ': 0, + 'EOS': 0, + 'ERO': 0, + 'ETBS': 0, + 'ETC': 0, + 'ETP': 0, + 'EVX': 0, + 'EXN': 0, + 'FRD': 0, + 'FUEL': 0, + 'FUN': 0, + 'FYN': 0, + 'FYP': 0, + 'GNO': 0, + 'GUP': 0, + 'GVT': 0, + 'HAC': 0, + 'HDG': 0, + 'HGT': 0, + 'HPC': 0, + 'HVN': 0, + 'ICN': 0, + 'ICO': 0, + 'ICOS': 0, + 'IND': 0, + 'INDI': 0, + 'ITS': 0, + 'IXT': 0, + 'KBR': 0, + 'KICK': 0, + 'LA': 0, + 'LAT': 0, + 'LIFE': 0, + 'LRC': 0, + 'LSK': 0, + 'LUN': 0, + 'MAID': 0, + 'MANA': 0, + 'MCAP': 0, + 'MIPS': 0, + 'MNE': 0, + 'MSP': 0, + 'MTH': 0, + 'MYB': 0, + 'NDC': 0, + 'NEBL': 0, + 'NET': 0, + 'NTO': 0, + 'NXC': 0, + 'NXT': 0, + 'OAX': 0, + 'ODN': 0, + 'OMG': 0, + 'OPT': 0, + 'ORME': 0, + 'OTN': 0, + 'PAY': 0, + 'PIX': 0, + 'PLBT': 0, + 'PLR': 0, + 'PLU': 0, + 'POE': 0, + 'POLL': 0, + 'PPT': 0, + 'PRE': 0, + 'PRG': 0, + 'PRO': 0, + 'PRS': 0, + 'PTOY': 0, + 'QAU': 0, + 'QCN': 0, + 'QTUM': 0, + 'QVT': 0, + 'REP': 0, + 'RKC': 0, + 'RVT': 0, + 'SAN': 0, + 'SBD': 0, + 'SCL': 0, + 'SISA': 0, + 'SKIN': 0, + 'SMART': 0, + 'SMS': 0, + 'SNC': 0, + 'SNGLS': 0, + 'SNM': 0, + 'SNT': 0, + 'STEEM': 0, + 'STRAT': 0, + 'STU': 0, + 'STX': 0, + 'SUB': 0, + 'SUR': 0, + 'SWT': 0, + 'TAAS': 0, + 'TBT': 0, + 'TFL': 0, + 'TIME': 0, + 'TIX': 0, + 'TKN': 0, + 'TKR': 0, + 'TNT': 0, + 'TRST': 0, + 'TRX': 0, + 'UET': 0, + 'UGT': 0, + 'VEN': 0, + 'VERI': 0, + 'VIB': 0, + 'VIBE': 0, + 'VOISE': 0, + 'WEALTH': 0, + 'WINGS': 0, + 'WTC': 0, + 'XAUR': 0, + 'XDN': 0, + 'XEM': 0, + 'XUC': 0, + 'YOYOW': 0, + 'ZAP': 0, + 'ZRX': 0, + 'ZSC': 0, + }, + }, + }, + 'options': { + 'defaultTimeInForce': 'FOK', + }, + 'exceptions': { + '504': RequestTimeout, // {"error":{"code":504,"message":"Gateway Timeout"}} + '1002': AuthenticationError, // {"error":{"code":1002,"message":"Authorization failed","description":""}} + '1003': PermissionDenied, // "Action is forbidden for this API key" + '2010': InvalidOrder, // "Quantity not a valid number" + '2001': BadSymbol, // "Symbol not found" + '2011': InvalidOrder, // "Quantity too low" + '2020': InvalidOrder, // "Price not a valid number" + '20002': OrderNotFound, // canceling non-existent order + '20001': InsufficientFunds, + }, + }); + } + + feeToPrecision (symbol, fee) { + return this.decimalToPrecision (fee, TRUNCATE, 8, DECIMAL_PLACES); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetSymbol (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const lot = this.safeFloat (market, 'quantityIncrement'); + const step = this.safeFloat (market, 'tickSize'); + const precision = { + 'price': this.precisionFromString (market['tickSize']), + // FIXME: for lots > 1 the following line returns 0 + // 'amount': this.precisionFromString (market['quantityIncrement']), + 'amount': -1 * parseInt (Math.log10 (lot)), + }; + const taker = this.safeFloat (market, 'takeLiquidityRate'); + const maker = this.safeFloat (market, 'provideLiquidityRate'); + result.push (this.extend (this.fees['trading'], { + 'info': market, + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'taker': taker, + 'maker': maker, + 'precision': precision, + 'limits': { + 'amount': { + 'min': lot, + 'max': undefined, + }, + 'price': { + 'min': step, + 'max': undefined, + }, + 'cost': { + 'min': lot * step, + 'max': undefined, + }, + }, + })); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrency (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'id'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const precision = 8; // default precision, todo: fix "magic constants" + const code = this.safeCurrencyCode (id); + const payin = this.safeValue (currency, 'payinEnabled'); + const payout = this.safeValue (currency, 'payoutEnabled'); + const transfer = this.safeValue (currency, 'transferEnabled'); + let active = payin && payout && transfer; + if ('disabled' in currency) { + if (currency['disabled']) { + active = false; + } + } + let type = 'fiat'; + if (('crypto' in currency) && currency['crypto']) { + type = 'crypto'; + } + const name = this.safeString (currency, 'fullName'); + result[code] = { + 'id': id, + 'code': code, + 'type': type, + 'payin': payin, + 'payout': payout, + 'transfer': transfer, + 'info': currency, + 'name': name, + 'active': active, + 'fee': this.safeFloat (currency, 'payoutFee'), // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchTradingFee (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.extend ({ + 'symbol': market['id'], + }, this.omit (params, 'symbol')); + const response = await this.privateGetTradingFeeSymbol (request); + // + // { + // takeLiquidityRate: '0.001', + // provideLiquidityRate: '-0.0001' + // } + // + return { + 'info': response, + 'maker': this.safeFloat (response, 'provideLiquidityRate'), + 'taker': this.safeFloat (response, 'takeLiquidityRate'), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const type = this.safeString (params, 'type', 'trading'); + const method = 'privateGet' + this.capitalize (type) + 'Balance'; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + const timestamp = this.parse8601 (ohlcv['timestamp']); + return [ + timestamp, + parseFloat (ohlcv['open']), + parseFloat (ohlcv['max']), + parseFloat (ohlcv['min']), + parseFloat (ohlcv['close']), + parseFloat (ohlcv['volume']), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'period': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetCandlesSymbol (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 100, 0 = unlimited + } + const response = await this.publicGetOrderbookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bid', 'ask', 'price', 'size'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.parse8601 (ticker['timestamp']); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const baseVolume = this.safeFloat (ticker, 'volume'); + const quoteVolume = this.safeFloat (ticker, 'volumeQuote'); + const open = this.safeFloat (ticker, 'open'); + const last = this.safeFloat (ticker, 'last'); + let change = undefined; + let percentage = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + if (open > 0) { + percentage = change / open * 100; + } + } + let vwap = undefined; + if (quoteVolume !== undefined) { + if (baseVolume !== undefined) { + if (baseVolume > 0) { + vwap = quoteVolume / baseVolume; + } + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = response[i]; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } else { + result[marketId] = this.parseTicker (ticker); + } + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTickerSymbol (this.extend (request, params)); + if ('message' in response) { + throw new ExchangeError (this.id + ' ' + response['message']); + } + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + // + // createMarketOrder + // + // { fee: "0.0004644", + // id: 386394956, + // price: "0.4644", + // quantity: "1", + // timestamp: "2018-10-25T16:41:44.780Z" } + // + // fetchTrades ... + // + // fetchMyTrades ... + // + const timestamp = this.parse8601 (trade['timestamp']); + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrency = market ? market['quote'] : undefined; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const orderId = this.safeString (trade, 'clientOrderId'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + const cost = price * amount; + const side = this.safeString (trade, 'side'); + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['startTime'] = since; + } + const response = await this.privateGetAccountTransactions (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // { + // id: 'd53ee9df-89bf-4d09-886e-849f8be64647', + // index: 1044718371, + // type: 'payout', + // status: 'success', + // currency: 'ETH', + // amount: '4.522683200000000000000000', + // createdAt: '2018-06-07T00:43:32.426Z', + // updatedAt: '2018-06-07T00:45:36.447Z', + // hash: '0x973e5683dfdf80a1fb1e0b96e19085b6489221d2ddf864daa46903c5ec283a0f', + // address: '0xC5a59b21948C1d230c8C54f05590000Eb3e1252c', + // fee: '0.00958', + // }, + // { + // id: 'e6c63331-467e-4922-9edc-019e75d20ba3', + // index: 1044714672, + // type: 'exchangeToBank', + // status: 'success', + // currency: 'ETH', + // amount: '4.532263200000000000', + // createdAt: '2018-06-07T00:42:39.543Z', + // updatedAt: '2018-06-07T00:42:39.683Z', + // }, + // { + // id: '3b052faa-bf97-4636-a95c-3b5260015a10', + // index: 1009280164, + // type: 'bankToExchange', + // status: 'success', + // currency: 'CAS', + // amount: '104797.875800000000000000', + // createdAt: '2018-05-19T02:34:36.750Z', + // updatedAt: '2018-05-19T02:34:36.857Z', + // }, + // { + // id: 'd525249f-7498-4c81-ba7b-b6ae2037dc08', + // index: 1009279948, + // type: 'payin', + // status: 'success', + // currency: 'CAS', + // amount: '104797.875800000000000000', + // createdAt: '2018-05-19T02:30:16.698Z', + // updatedAt: '2018-05-19T02:34:28.159Z', + // hash: '0xa6530e1231de409cf1f282196ed66533b103eac1df2aa4a7739d56b02c5f0388', + // address: '0xd53ed559a6d963af7cb3f3fcd0e7ca499054db8b', + // } + // + // { + // "id": "4f351f4f-a8ee-4984-a468-189ed590ddbd", + // "index": 3112719565, + // "type": "withdraw", + // "status": "success", + // "currency": "BCHOLD", + // "amount": "0.02423133", + // "createdAt": "2019-07-16T16:52:04.494Z", + // "updatedAt": "2019-07-16T16:54:07.753Z" + // } + const id = this.safeString (transaction, 'id'); + const timestamp = this.parse8601 (this.safeString (transaction, 'createdAt')); + const updated = this.parse8601 (this.safeString (transaction, 'updatedAt')); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const amount = this.safeFloat (transaction, 'amount'); + const address = this.safeString (transaction, 'address'); + const txid = this.safeString (transaction, 'hash'); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': code, + }; + } + const type = this.parseTransactionType (this.safeString (transaction, 'type')); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': undefined, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'pending': 'pending', + 'failed': 'failed', + 'success': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransactionType (type) { + const types = { + 'payin': 'deposit', + 'payout': 'withdrawal', + 'withdraw': 'withdrawal', + }; + return this.safeString (types, type, type); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['sort'] = 'ASC'; + request['from'] = this.iso8601 (since); + } + const response = await this.publicGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + // their max accepted length is 32 characters + const uuid = this.uuid (); + const parts = uuid.split ('-'); + let clientOrderId = parts.join (''); + clientOrderId = clientOrderId.slice (0, 32); + amount = parseFloat (amount); + const request = { + 'clientOrderId': clientOrderId, + 'symbol': market['id'], + 'side': side, + 'quantity': this.amountToPrecision (symbol, amount), + 'type': type, + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } else { + request['timeInForce'] = this.options['defaultTimeInForce']; + } + const response = await this.privatePostOrder (this.extend (request, params)); + const order = this.parseOrder (response); + if (order['status'] === 'rejected') { + throw new InvalidOrder (this.id + ' order was rejected by the exchange ' + this.json (order)); + } + const id = order['id']; + this.orders[id] = order; + return order; + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + // their max accepted length is 32 characters + const uuid = this.uuid (); + const parts = uuid.split ('-'); + let requestClientId = parts.join (''); + requestClientId = requestClientId.slice (0, 32); + const request = { + 'clientOrderId': id, + 'requestClientId': requestClientId, + }; + if (amount !== undefined) { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + if (price !== undefined) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePatchOrderClientOrderId (this.extend (request, params)); + const order = this.parseOrder (response); + this.orders[order['id']] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.privateDeleteOrderClientOrderId (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOrderStatus (status) { + const statuses = { + 'new': 'open', + 'suspended': 'open', + 'partiallyFilled': 'open', + 'filled': 'closed', + 'canceled': 'canceled', + 'expired': 'failed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createMarketOrder + // + // { clientOrderId: "fe36aa5e190149bf9985fb673bbb2ea0", + // createdAt: "2018-10-25T16:41:44.780Z", + // cumQuantity: "1", + // id: "66799540063", + // quantity: "1", + // side: "sell", + // status: "filled", + // symbol: "XRPUSDT", + // timeInForce: "FOK", + // tradesReport: [ { fee: "0.0004644", + // id: 386394956, + // price: "0.4644", + // quantity: "1", + // timestamp: "2018-10-25T16:41:44.780Z" } ], + // type: "market", + // updatedAt: "2018-10-25T16:41:44.780Z" } + // + const created = this.parse8601 (this.safeString (order, 'createdAt')); + const updated = this.parse8601 (this.safeString (order, 'updatedAt')); + const marketId = this.safeString (order, 'symbol'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['id']; + } + } + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'cumQuantity'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const id = this.safeString (order, 'clientOrderId'); + let price = this.safeFloat (order, 'price'); + if (price === undefined) { + if (id in this.orders) { + price = this.orders[id]['price']; + } + } + let remaining = undefined; + let cost = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + if (price !== undefined) { + cost = filled * price; + } + } + } + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + let trades = this.safeValue (order, 'tradesReport'); + let fee = undefined; + let average = undefined; + if (trades !== undefined) { + trades = this.parseTrades (trades, market); + let feeCost = undefined; + const numTrades = trades.length; + let tradesCost = 0; + for (let i = 0; i < numTrades; i++) { + if (feeCost === undefined) { + feeCost = 0; + } + tradesCost = this.sum (tradesCost, trades[i]['cost']); + const tradeFee = this.safeValue (trades[i], 'fee', {}); + const tradeFeeCost = this.safeFloat (tradeFee, 'cost'); + if (tradeFeeCost !== undefined) { + feeCost = this.sum (feeCost, tradeFeeCost); + } + } + cost = tradesCost; + if ((filled !== undefined) && (filled > 0)) { + average = cost / filled; + if (type === 'market') { + if (price === undefined) { + price = average; + } + } + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': market['quote'], + }; + } + } + return { + 'id': id, + 'timestamp': created, + 'datetime': this.iso8601 (created), + 'lastTradeTimestamp': updated, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + 'trades': trades, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.privateGetHistoryOrder (this.extend (request, params)); + const numOrders = response.length; + if (numOrders > 0) { + return this.parseOrder (response[0]); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + + async fetchOpenOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + // we use clientOrderId as the order id with HitBTC intentionally + // because most of their endpoints will require clientOrderId + // explained here: https://github.com/ccxt/ccxt/issues/5674 + const request = { + 'clientOrderId': id, + }; + const response = await this.privateGetOrderClientOrderId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + const response = await this.privateGetOrder (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + const response = await this.privateGetHistoryOrder (this.extend (request, params)); + const parsedOrders = this.parseOrders (response, market); + const orders = []; + for (let i = 0; i < parsedOrders.length; i++) { + const order = parsedOrders[i]; + const status = order['status']; + if ((status === 'closed') || (status === 'canceled')) { + orders.push (order); + } + } + return this.filterBySinceLimit (orders, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'symbol': 'BTC/USD', // optional + // 'sort': 'DESC', // or 'ASC' + // 'by': 'timestamp', // or 'id' String timestamp by default, or id + // 'from': 'Datetime or Number', // ISO 8601 + // 'till': 'Datetime or Number', + // 'limit': 100, + // 'offset': 0, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetHistoryTrades (this.extend (request, params)); + // + // [ + // { + // "id": 9535486, + // "clientOrderId": "f8dbaab336d44d5ba3ff578098a68454", + // "orderId": 816088377, + // "symbol": "ETHBTC", + // "side": "sell", + // "quantity": "0.061", + // "price": "0.045487", + // "fee": "0.000002775", + // "timestamp": "2017-05-17T12:32:57.848Z" + // }, + // { + // "id": 9535437, + // "clientOrderId": "27b9bfc068b44194b1f453c7af511ed6", + // "orderId": 816088021, + // "symbol": "ETHBTC", + // "side": "buy", + // "quantity": "0.038", + // "price": "0.046000", + // "fee": "-0.000000174", + // "timestamp": "2017-05-17T12:30:57.848Z" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + // The id needed here is the exchange's id, and not the clientOrderID, + // which is the id that is stored in the unified order id + // To get the exchange's id you need to grab it from order['info']['id'] + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'id': id, + }; + const response = await this.privateGetHistoryOrderIdTrades (this.extend (request, params)); + const numOrders = response.length; + if (numOrders > 0) { + return this.parseTrades (response, market, since, limit); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found, ' + this.id + '.fetchOrderTrades() requires an exchange-specific order id, you need to grab it from order["info"]["id"]'); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostAccountCryptoAddressCurrency (this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + const tag = this.safeString (response, 'paymentId'); + return { + 'currency': currency, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetAccountCryptoAddressCurrency (this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + const tag = this.safeString (response, 'paymentId'); + return { + 'currency': currency['code'], + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + }; + if (tag) { + request['paymentId'] = tag; + } + const response = await this.privatePostAccountCryptoWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/api/' + this.version + '/'; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + url += api + '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + url += this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (Object.keys (query).length) { + body = this.json (query); + } + const payload = this.encode (this.apiKey + ':' + this.secret); + const auth = this.stringToBase64 (payload); + headers = { + 'Authorization': 'Basic ' + this.decode (auth), + 'Content-Type': 'application/json', + }; + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (code >= 400) { + const feedback = this.id + ' ' + body; + // {"code":504,"message":"Gateway Timeout","description":""} + if ((code === 503) || (code === 504)) { + throw new ExchangeNotAvailable (feedback); + } + // fallback to default error handler on rate limit errors + // {"code":429,"message":"Too many requests","description":"Too many requests"} + if (code === 429) { + return; + } + // {"error":{"code":20002,"message":"Order not found","description":""}} + if (body[0] === '{') { + if ('error' in response) { + const errorCode = this.safeString (response['error'], 'code'); + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + const message = this.safeString (response['error'], 'message'); + if (message === 'Duplicate clientOrderId') { + throw new InvalidOrder (feedback); + } + } + } + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/huobipro.js b/TaskServer/node_modules/ccxt/js/huobipro.js new file mode 100644 index 000000000..23c6fe99b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/huobipro.js @@ -0,0 +1,1233 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { AuthenticationError, ExchangeError, ExchangeNotAvailable, InvalidOrder, OrderNotFound, InsufficientFunds, ArgumentsRequired, BadSymbol, BadRequest, RequestTimeout } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class huobipro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'huobipro', + 'name': 'Huobi Pro', + 'countries': [ 'CN' ], + 'rateLimit': 2000, + 'userAgent': this.userAgents['chrome39'], + 'version': 'v1', + 'accounts': undefined, + 'accountsById': undefined, + 'hostname': 'api.huobi.pro', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchDepositAddress': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchTradingLimits': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchCurrencies': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '60min', + '4h': '4hour', + '1d': '1day', + '1w': '1week', + '1M': '1mon', + '1y': '1year', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766569-15aa7b9a-5edd-11e7-9e7f-44791f4ee49c.jpg', + 'api': { + 'market': 'https://{hostname}', + 'public': 'https://{hostname}', + 'private': 'https://{hostname}', + 'zendesk': 'https://huobiglobal.zendesk.com/hc/en-us/articles', + }, + 'www': 'https://www.huobi.pro', + 'referral': 'https://www.huobi.co/en-us/topic/invited/?invite_code=rwrd3', + 'doc': 'https://huobiapi.github.io/docs/spot/v1/cn/', + 'fees': 'https://www.huobi.pro/about/fee/', + }, + 'api': { + 'zendesk': { + 'get': [ + '360000400491-Trade-Limits', + ], + }, + 'market': { + 'get': [ + 'history/kline', // 获�K线数� + 'detail/merged', // 获���行情(Ticker) + 'depth', // 获� Market Depth 数� + 'trade', // 获� Trade Detail 数� + 'history/trade', // 批�获�最近的交易记录 + 'detail', // 获� Market Detail 24�时�交�数� + 'tickers', + ], + }, + 'public': { + 'get': [ + 'common/symbols', // 查询系统支�的所有交易对 + 'common/currencys', // 查询系统支�的所有�� + 'common/timestamp', // 查询系统当�时间 + 'common/exchange', // order limits + 'settings/currencys', // ?language=en-US + ], + }, + 'private': { + // todo add v2 endpoints + // 'GET /v2/account/withdraw/quota + // 'GET /v2/reference/currencies + // 'GET /v2/account/deposit/address + 'get': [ + 'account/accounts', // 查询当�用户的所有账户(�account-id) + 'account/accounts/{id}/balance', // 查询指定账户的余� + 'account/accounts/{sub-uid}', + 'account/history', + 'cross-margin/loan-info', + 'fee/fee-rate/get', + 'order/openOrders', + 'order/orders', + 'order/orders/{id}', // 查询�个订�详情 + 'order/orders/{id}/matchresults', // 查询�个订�的�交明细 + 'order/orders/getClientOrder', + 'order/history', // 查询当�委托�历�委托 + 'order/matchresults', // 查询当��交�历��交 + 'dw/withdraw-virtual/addresses', // 查询虚拟��现地� + 'query/deposit-withdraw', + 'margin/loan-orders', // 借贷订� + 'margin/accounts/balance', // 借贷账户详情 + 'points/actions', + 'points/orders', + 'subuser/aggregate-balance', + 'stable-coin/exchange_rate', + 'stable-coin/quote', + ], + // todo add v2 endpoints + // POST /v2/sub-user/management + 'post': [ + 'futures/transfer', + 'order/batch-orders', + 'order/orders/place', // 创建并执行一个新订� (一步下�, 推�使用) + 'order/orders/submitCancelClientOrder', + 'order/orders/batchCancelOpenOrders', + 'order/orders', // 创建一个新的订�请求 (仅创建订�,�执行下�) + 'order/orders/{id}/place', // 执行一个订� (仅执行已创建的订�) + 'order/orders/{id}/submitcancel', // 申请撤销一个订�请求 + 'order/orders/batchcancel', // 批�撤销订� + 'dw/balance/transfer', // 资产划转 + 'dw/withdraw/api/create', // 申请�现虚拟� + 'dw/withdraw-virtual/create', // 申请�现虚拟� + 'dw/withdraw-virtual/{id}/place', // 确认申请虚拟��现 + 'dw/withdraw-virtual/{id}/cancel', // 申请�消�现虚拟� + 'dw/transfer-in/margin', // 现货账户划入至借贷账户 + 'dw/transfer-out/margin', // 借贷账户划出至现货账户 + 'margin/orders', // 申请借贷 + 'margin/orders/{id}/repay', // 归还借贷 + 'stable-coin/exchange', + 'subuser/transfer', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.002, + 'taker': 0.002, + }, + }, + 'exceptions': { + 'exact': { + // err-code + 'timeout': RequestTimeout, // {"ts":1571653730865,"status":"error","err-code":"timeout","err-msg":"Request Timeout"} + 'gateway-internal-error': ExchangeNotAvailable, // {"status":"error","err-code":"gateway-internal-error","err-msg":"Failed to load data. Try again later.","data":null} + 'account-frozen-balance-insufficient-error': InsufficientFunds, // {"status":"error","err-code":"account-frozen-balance-insufficient-error","err-msg":"trade account balance is not enough, left: `0.0027`","data":null} + 'invalid-amount': InvalidOrder, // eg "Paramemter `amount` is invalid." + 'order-limitorder-amount-min-error': InvalidOrder, // limit order amount error, min: `0.001` + 'order-marketorder-amount-min-error': InvalidOrder, // market order amount error, min: `0.01` + 'order-limitorder-price-min-error': InvalidOrder, // limit order price error + 'order-limitorder-price-max-error': InvalidOrder, // limit order price error + 'order-orderstate-error': OrderNotFound, // canceling an already canceled order + 'order-queryorder-invalid': OrderNotFound, // querying a non-existent order + 'order-update-error': ExchangeNotAvailable, // undocumented error + 'api-signature-check-failed': AuthenticationError, + 'api-signature-not-valid': AuthenticationError, // {"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Incorrect Access key [Access key错误]","data":null} + 'base-record-invalid': OrderNotFound, // https://github.com/ccxt/ccxt/issues/5750 + // err-msg + 'invalid symbol': BadSymbol, // {"ts":1568813334794,"status":"error","err-code":"invalid-parameter","err-msg":"invalid symbol"} + 'invalid-parameter': BadRequest, // {"ts":1576210479343,"status":"error","err-code":"invalid-parameter","err-msg":"symbol trade not open now"} + 'base-symbol-trade-disabled': BadSymbol, // {"status":"error","err-code":"base-symbol-trade-disabled","err-msg":"Trading is disabled for this symbol","data":null} + }, + }, + 'options': { + // https://github.com/ccxt/ccxt/issues/5376 + 'fetchOrdersByStatesMethod': 'private_get_order_orders', // 'private_get_order_history' // https://github.com/ccxt/ccxt/pull/5392 + 'fetchOpenOrdersMethod': 'fetch_open_orders_v1', // 'fetch_open_orders_v2' // https://github.com/ccxt/ccxt/issues/5388 + 'createMarketBuyOrderRequiresPrice': true, + 'fetchMarketsMethod': 'publicGetCommonSymbols', + 'fetchBalanceMethod': 'privateGetAccountAccountsIdBalance', + 'createOrderMethod': 'privatePostOrderOrdersPlace', + 'language': 'en-US', + }, + 'commonCurrencies': { + // https://github.com/ccxt/ccxt/issues/6081 + // https://github.com/ccxt/ccxt/issues/3365 + // https://github.com/ccxt/ccxt/issues/2873 + 'GET': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol) + 'HOT': 'Hydro Protocol', // conflict with HOT (Holo) https://github.com/ccxt/ccxt/issues/4929 + }, + }); + } + + async fetchTradingLimits (symbols = undefined, params = {}) { + // this method should not be called directly, use loadTradingLimits () instead + // by default it will try load withdrawal fees of all currencies (with separate requests) + // however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const result = {}; + for (let i = 0; i < symbols.length; i++) { + const symbol = symbols[i]; + result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params); + } + return result; + } + + async fetchTradingLimitsById (id, params = {}) { + const request = { + 'symbol': id, + }; + const response = await this.publicGetCommonExchange (this.extend (request, params)); + // + // { status: "ok", + // data: { symbol: "aidocbtc", + // 'buy-limit-must-less-than': 1.1, + // 'sell-limit-must-greater-than': 0.9, + // 'limit-order-must-greater-than': 1, + // 'limit-order-must-less-than': 5000000, + // 'market-buy-order-must-greater-than': 0.0001, + // 'market-buy-order-must-less-than': 100, + // 'market-sell-order-must-greater-than': 1, + // 'market-sell-order-must-less-than': 500000, + // 'circuit-break-when-greater-than': 10000, + // 'circuit-break-when-less-than': 10, + // 'market-sell-order-rate-must-less-than': 0.1, + // 'market-buy-order-rate-must-less-than': 0.1 } } + // + return this.parseTradingLimits (this.safeValue (response, 'data', {})); + } + + parseTradingLimits (limits, symbol = undefined, params = {}) { + // + // { symbol: "aidocbtc", + // 'buy-limit-must-less-than': 1.1, + // 'sell-limit-must-greater-than': 0.9, + // 'limit-order-must-greater-than': 1, + // 'limit-order-must-less-than': 5000000, + // 'market-buy-order-must-greater-than': 0.0001, + // 'market-buy-order-must-less-than': 100, + // 'market-sell-order-must-greater-than': 1, + // 'market-sell-order-must-less-than': 500000, + // 'circuit-break-when-greater-than': 10000, + // 'circuit-break-when-less-than': 10, + // 'market-sell-order-rate-must-less-than': 0.1, + // 'market-buy-order-rate-must-less-than': 0.1 } + // + return { + 'info': limits, + 'limits': { + 'amount': { + 'min': this.safeFloat (limits, 'limit-order-must-greater-than'), + 'max': this.safeFloat (limits, 'limit-order-must-less-than'), + }, + }, + }; + } + + async fetchMarkets (params = {}) { + const method = this.options['fetchMarketsMethod']; + const response = await this[method] (params); + const markets = this.safeValue (response, 'data'); + const numMarkets = markets.length; + if (numMarkets < 1) { + throw new ExchangeError (this.id + ' publicGetCommonSymbols returned empty response: ' + this.json (markets)); + } + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = this.safeString (market, 'base-currency'); + const quoteId = this.safeString (market, 'quote-currency'); + const id = baseId + quoteId; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': market['amount-precision'], + 'price': market['price-precision'], + }; + const maker = (base === 'OMG') ? 0 : 0.2 / 100; + const taker = (base === 'OMG') ? 0 : 0.2 / 100; + const minAmount = this.safeFloat (market, 'min-order-amt', Math.pow (10, -precision['amount'])); + const maxAmount = this.safeFloat (market, 'max-order-amt'); + const minCost = this.safeFloat (market, 'min-order-value', 0); + const state = this.safeString (market, 'state'); + const active = (state === 'online'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'taker': taker, + 'maker': maker, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': maxAmount, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (ticker, 'ts'); + let bid = undefined; + let ask = undefined; + let bidVolume = undefined; + let askVolume = undefined; + if ('bid' in ticker) { + if (Array.isArray (ticker['bid'])) { + bid = this.safeFloat (ticker['bid'], 0); + bidVolume = this.safeFloat (ticker['bid'], 1); + } + } + if ('ask' in ticker) { + if (Array.isArray (ticker['ask'])) { + ask = this.safeFloat (ticker['ask'], 0); + askVolume = this.safeFloat (ticker['ask'], 1); + } + } + const open = this.safeFloat (ticker, 'open'); + const close = this.safeFloat (ticker, 'close'); + let change = undefined; + let percentage = undefined; + let average = undefined; + if ((open !== undefined) && (close !== undefined)) { + change = close - open; + average = this.sum (open, close) / 2; + if ((close !== undefined) && (close > 0)) { + percentage = (change / open) * 100; + } + } + const baseVolume = this.safeFloat (ticker, 'amount'); + const quoteVolume = this.safeFloat (ticker, 'vol'); + let vwap = undefined; + if (baseVolume !== undefined && quoteVolume !== undefined && baseVolume > 0) { + vwap = quoteVolume / baseVolume; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': bid, + 'bidVolume': bidVolume, + 'ask': ask, + 'askVolume': askVolume, + 'vwap': vwap, + 'open': open, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'type': 'step0', + }; + const response = await this.marketGetDepth (this.extend (request, params)); + if ('tick' in response) { + if (!response['tick']) { + throw new ExchangeError (this.id + ' fetchOrderBook() returned empty response: ' + this.json (response)); + } + const orderbook = this.safeValue (response, 'tick'); + const result = this.parseOrderBook (orderbook, orderbook['ts']); + result['nonce'] = orderbook['version']; + return result; + } + throw new ExchangeError (this.id + ' fetchOrderBook() returned unrecognized response: ' + this.json (response)); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.marketGetDetailMerged (this.extend (request, params)); + return this.parseTicker (response['tick'], market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.marketGetTickers (params); + const tickers = this.safeValue (response, 'data'); + const timestamp = this.safeInteger (response, 'ts'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const marketId = this.safeString (tickers[i], 'symbol'); + const market = this.safeValue (this.markets_by_id, marketId); + let symbol = marketId; + if (market !== undefined) { + symbol = market['symbol']; + const ticker = this.parseTicker (tickers[i], market); + ticker['timestamp'] = timestamp; + ticker['datetime'] = this.iso8601 (timestamp); + result[symbol] = ticker; + } + } + return result; + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger2 (trade, 'ts', 'created-at'); + const order = this.safeString (trade, 'order-id'); + let side = this.safeString (trade, 'direction'); + let type = this.safeString (trade, 'type'); + if (type !== undefined) { + const typeParts = type.split ('-'); + side = typeParts[0]; + type = typeParts[1]; + } + const takerOrMaker = this.safeString (trade, 'role'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'filled-amount', 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let fee = undefined; + let feeCost = this.safeFloat (trade, 'filled-fees'); + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = (side === 'buy') ? market['base'] : market['quote']; + } + const filledPoints = this.safeFloat (trade, 'filled-points'); + if (filledPoints !== undefined) { + if ((feeCost === undefined) || (feeCost === 0.0)) { + feeCost = filledPoints; + feeCurrency = this.safeCurrencyCode (this.safeString (trade, 'fee-deduct-currency')); + } + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + const id = this.safeString (trade, 'id'); + return { + 'id': id, + 'info': trade, + 'order': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['size'] = limit; // 1-100 orders, default is 100 + } + if (since !== undefined) { + request['start-date'] = this.ymd (since); // maximum query window size is 2 days, query window shift should be within past 120 days + } + const response = await this.privateGetOrderMatchresults (this.extend (request, params)); + const trades = this.parseTrades (response['data'], market, since, limit); + return trades; + } + + async fetchTrades (symbol, since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.marketGetHistoryTrade (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + let result = []; + for (let i = 0; i < data.length; i++) { + const trades = this.safeValue (data[i], 'data', []); + for (let j = 0; j < trades.length; j++) { + const trade = this.parseTrade (trades[j], market); + result.push (trade); + } + } + result = this.sortBy (result, 'timestamp'); + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'id'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'amount'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'period': this.timeframes[timeframe], + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.marketGetHistoryKline (this.extend (request, params)); + return this.parseOHLCVs (response['data'], market, timeframe, since, limit); + } + + async fetchAccounts (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountAccounts (params); + return response['data']; + } + + async fetchCurrencies (params = {}) { + const request = { + 'language': this.options['language'], + }; + const response = await this.publicGetSettingsCurrencys (this.extend (request, params)); + const currencies = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + // + // { name: "ctxc", + // 'display-name': "CTXC", + // 'withdraw-precision': 8, + // 'currency-type': "eth", + // 'currency-partition': "pro", + // 'support-sites': null, + // 'otc-enable': 0, + // 'deposit-min-amount': "2", + // 'withdraw-min-amount': "4", + // 'show-precision': "8", + // weight: "2988", + // visible: true, + // 'deposit-desc': "Please don’t deposit any other digital assets except CTXC t…", + // 'withdraw-desc': "Minimum withdrawal amount: 4 CTXC. !>_ 0)) { + average = cost / filled; + } + } + const feeCost = this.safeFloat2 (order, 'filled-fees', 'field-fees'); // typo in their API, filled fees + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = (side === 'sell') ? market['quote'] : market['base']; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + await this.loadAccounts (); + const market = this.market (symbol); + const request = { + 'account-id': this.accounts[0]['id'], + 'symbol': market['id'], + 'type': side + '-' + type, + }; + if ((type === 'market') && (side === 'buy')) { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " market buy order requires price argument to calculate cost (total amount of quote currency to spend for buying, amount * price). To switch off this warning exception and specify cost in the amount argument, set .options['createMarketBuyOrderRequiresPrice'] = false. Make sure you know what you're doing."); + } else { + // despite that cost = amount * price is in quote currency and should have quote precision + // the exchange API requires the cost supplied in 'amount' to be of base precision + // more about it here: https://github.com/ccxt/ccxt/pull/4395 + // we use priceToPrecision instead of amountToPrecision here + // because in this case the amount is in the quote currency + request['amount'] = this.costToPrecision (symbol, parseFloat (amount) * parseFloat (price)); + } + } else { + request['amount'] = this.costToPrecision (symbol, amount); + } + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + if (type === 'limit' || type === 'ioc' || type === 'limit-maker') { + request['price'] = this.priceToPrecision (symbol, price); + } + const method = this.options['createOrderMethod']; + const response = await this[method] (this.extend (request, params)); + const timestamp = this.milliseconds (); + const id = this.safeString (response, 'data'); + return { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const response = await this.privatePostOrderOrdersIdSubmitcancel ({ 'id': id }); + // + // let response = { + // 'status': 'ok', + // 'data': '10138899000', + // }; + // + return this.extend (this.parseOrder (response), { + 'id': id, + 'status': 'canceled', + }); + } + + currencyToPrecision (currency, fee) { + return this.decimalToPrecision (fee, 0, this.currencies[currency]['precision']); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let key = 'quote'; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.currencyToPrecision (market[key], cost)), + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currency (code); + const request = { + 'address': address, // only supports existing addresses in your withdraw address list + 'amount': amount, + 'currency': currency['id'].toLowerCase (), + }; + if (tag !== undefined) { + request['addr-tag'] = tag; // only for XRP? + } + const response = await this.privatePostDwWithdrawApiCreate (this.extend (request, params)); + const id = this.safeString (response, 'data'); + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/'; + if (api === 'market') { + url += api; + } else if ((api === 'public') || (api === 'private')) { + url += this.version; + } + url += '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.ymdhms (this.milliseconds (), 'T'); + let request = { + 'SignatureMethod': 'HmacSHA256', + 'SignatureVersion': '2', + 'AccessKeyId': this.apiKey, + 'Timestamp': timestamp, + }; + if (method !== 'POST') { + request = this.extend (request, query); + } + request = this.keysort (request); + let auth = this.urlencode (request); + // unfortunately, PHP demands double quotes for the escaped newline symbol + // eslint-disable-next-line quotes + const payload = [ method, this.hostname, url, auth ].join ("\n"); + const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64'); + auth += '&' + this.urlencode ({ 'Signature': signature }); + url += '?' + auth; + if (method === 'POST') { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } else { + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + url = this.implodeParams (this.urls['api'][api], { + 'hostname': this.hostname, + }) + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('status' in response) { + // + // {"status":"error","err-code":"order-limitorder-amount-min-error","err-msg":"limit order amount error, min: `0.001`","data":null} + // + const status = this.safeString (response, 'status'); + if (status === 'error') { + const code = this.safeString (response, 'err-code'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + const message = this.safeString (response, 'err-msg'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (feedback); + } + } + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + if (limit === undefined || limit > 100) { + limit = 100; + } + await this.loadMarkets (); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const request = { + 'type': 'deposit', + 'from': 0, // From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from + }; + if (currency !== undefined) { + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; // max 100 + } + const response = await this.privateGetQueryDepositWithdraw (this.extend (request, params)); + // return response + return this.parseTransactions (response['data'], currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + if (limit === undefined || limit > 100) { + limit = 100; + } + await this.loadMarkets (); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const request = { + 'type': 'withdraw', + 'from': 0, // From 'id' ... if you want to get results after a particular transaction id, pass the id in params.from + }; + if (currency !== undefined) { + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['size'] = limit; // max 100 + } + const response = await this.privateGetQueryDepositWithdraw (this.extend (request, params)); + // return response + return this.parseTransactions (response['data'], currency, since, limit); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // 'id': 8211029, + // 'type': 'deposit', + // 'currency': 'eth', + // 'chain': 'eth', + // 'tx-hash': 'bd315....', + // 'amount': 0.81162421, + // 'address': '4b8b....', + // 'address-tag': '', + // 'fee': 0, + // 'state': 'safe', + // 'created-at': 1542180380965, + // 'updated-at': 1542180788077 + // } + // + // fetchWithdrawals + // + // { + // 'id': 6908275, + // 'type': 'withdraw', + // 'currency': 'btc', + // 'chain': 'btc', + // 'tx-hash': 'c1a1a....', + // 'amount': 0.80257005, + // 'address': '1QR....', + // 'address-tag': '', + // 'fee': 0.0005, + // 'state': 'confirmed', + // 'created-at': 1552107295685, + // 'updated-at': 1552108032859 + // } + // + const timestamp = this.safeInteger (transaction, 'created-at'); + const updated = this.safeInteger (transaction, 'updated-at'); + const code = this.safeCurrencyCode (this.safeString (transaction, 'currency')); + let type = this.safeString (transaction, 'type'); + if (type === 'withdraw') { + type = 'withdrawal'; + } + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const tag = this.safeString (transaction, 'address-tag'); + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + feeCost = Math.abs (feeCost); + } + return { + 'info': transaction, + 'id': this.safeString (transaction, 'id'), + 'txid': this.safeString (transaction, 'tx-hash'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': this.safeString (transaction, 'address'), + 'tag': tag, + 'type': type, + 'amount': this.safeFloat (transaction, 'amount'), + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': { + 'currency': code, + 'cost': feeCost, + 'rate': undefined, + }, + }; + } + + parseTransactionStatus (status) { + const statuses = { + // deposit statuses + 'unknown': 'failed', + 'confirming': 'pending', + 'confirmed': 'ok', + 'safe': 'ok', + 'orphan': 'failed', + // withdrawal statuses + 'submitted': 'pending', + 'canceled': 'canceled', + 'reexamine': 'pending', + 'reject': 'failed', + 'pass': 'pending', + 'wallet-reject': 'failed', + // 'confirmed': 'ok', // present in deposit statuses + 'confirm-error': 'failed', + 'repealed': 'failed', + 'wallet-transfer': 'pending', + 'pre-transfer': 'pending', + }; + return this.safeString (statuses, status, status); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/huobiru.js b/TaskServer/node_modules/ccxt/js/huobiru.js new file mode 100644 index 000000000..039a8ce78 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/huobiru.js @@ -0,0 +1,31 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const huobipro = require ('./huobipro.js'); + +// --------------------------------------------------------------------------- + +module.exports = class huobiru extends huobipro { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'huobiru', + 'name': 'Huobi Russia', + 'countries': [ 'RU' ], + 'hostname': 'www.huobi.com.ru', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/52978816-e8552e00-33e3-11e9-98ed-845acfece834.jpg', + 'api': { + 'market': 'https://www.huobi.com.ru/api', + 'public': 'https://www.huobi.com.ru/api', + 'private': 'https://www.huobi.com.ru/api', + 'zendesk': 'https://huobiglobal.zendesk.com/hc/en-us/articles', + }, + 'www': 'https://www.huobi.com.ru/ru-ru', + 'referral': 'https://www.huobi.com.ru/invite?invite_code=esc74', + 'doc': 'https://github.com/cloudapidoc/API_Docs_en', + 'fees': 'https://www.huobi.com.ru/ru-ru/about/fee/', + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/ice3x.js b/TaskServer/node_modules/ccxt/js/ice3x.js new file mode 100644 index 000000000..6405d358b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/ice3x.js @@ -0,0 +1,477 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class ice3x extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'ice3x', + 'name': 'ICE3X', + 'countries': [ 'ZA' ], // South Africa + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38012176-11616c32-3269-11e8-9f05-e65cf885bb15.jpg', + 'api': 'https://ice3x.com/api', + 'www': [ + 'https://ice3x.com', + 'https://ice3x.co.za', + ], + 'doc': 'https://ice3x.co.za/ice-cubed-bitcoin-exchange-api-documentation-1-june-2017', + 'fees': [ + 'https://help.ice3.com/support/solutions/articles/11000033293-trading-fees', + 'https://help.ice3.com/support/solutions/articles/11000033288-fees-explained', + 'https://help.ice3.com/support/solutions/articles/11000008131-what-are-your-fiat-deposit-and-withdrawal-fees-', + 'https://help.ice3.com/support/solutions/articles/11000033289-deposit-fees', + ], + 'referral': 'https://ice3x.com?ref=14341802', + }, + 'api': { + 'public': { + 'get': [ + 'currency/list', + 'currency/info', + 'pair/list', + 'pair/info', + 'stats/marketdepthfull', + 'stats/marketdepthbtcav', + 'stats/marketdepth', + 'orderbook/info', + 'trade/list', + 'trade/info', + ], + }, + 'private': { + 'post': [ + 'balance/list', + 'balance/info', + 'order/new', + 'order/cancel', + 'order/list', + 'order/info', + 'trade/list', + 'trade/info', + 'transaction/list', + 'transaction/info', + 'invoice/list', + 'invoice/info', + 'invoice/pdf', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.005, + 'taker': 0.005, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencyList (params); + const currencies = response['response']['entities']; + const precision = this.precision['amount']; + const result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'currency_id'); + const currencyId = this.safeString (currency, 'iso'); + const code = this.safeCurrencyCode (currencyId); + result[code] = { + 'id': id, + 'code': code, + 'name': currency['name'], + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + if (this.currencies_by_id === undefined) { + this.currencies = await this.fetchCurrencies (); + this.currencies_by_id = this.indexBy (this.currencies, 'id'); + } + const response = await this.publicGetPairList (params); + const markets = this.safeValue (response['response'], 'entities'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'pair_id'); + const baseId = this.safeString (market, 'currency_id_from'); + const quoteId = this.safeString (market, 'currency_id_to'); + const baseCurrency = this.currencies_by_id[baseId]; + const quoteCurrency = this.currencies_by_id[quoteId]; + const base = baseCurrency['code']; + const quote = quoteCurrency['code']; + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + const symbol = market['symbol']; + const last = this.safeFloat (ticker, 'last_price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'max'), + 'low': this.safeFloat (ticker, 'min'), + 'bid': this.safeFloat (ticker, 'max_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'min_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'vol'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + }; + const response = await this.publicGetStatsMarketdepthfull (this.extend (request, params)); + const ticker = this.safeValue (response['response'], 'entity'); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetStatsMarketdepthfull (params); + const tickers = this.safeValue (response['response'], 'entities'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const marketId = this.safeString (ticker, 'pair_id'); + const market = this.safeValue (this.marketsById, marketId); + if (market !== undefined) { + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair_id': this.marketId (symbol), + }; + if (limit !== undefined) { + const type = this.safeString (params, 'type'); + if ((type !== 'ask') && (type !== 'bid')) { + // eslint-disable-next-line quotes + throw new ExchangeError (this.id + " fetchOrderBook requires an exchange-specific extra 'type' param ('bid' or 'ask') when used with a limit"); + } else { + request['items_per_page'] = limit; + } + } + const response = await this.publicGetOrderbookInfo (this.extend (request, params)); + const orderbook = this.safeValue (response['response'], 'entities'); + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'created'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': market['quote'], + }; + } + const type = 'limit'; + const side = this.safeString (trade, 'type'); + const id = this.safeString (trade, 'trade_id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + }; + const response = await this.publicGetTradeList (this.extend (request, params)); + const trades = this.safeValue (response['response'], 'entities'); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostBalanceList (params); + const result = { 'info': response }; + const balances = this.safeValue (response['response'], 'entities', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + // currency ids are numeric strings + const currencyId = this.safeString (balance, 'currency_id'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrder (order, market = undefined) { + const pairId = this.safeInteger (order, 'pair_id'); + let symbol = undefined; + if (pairId && !market && (pairId in this.marketsById)) { + market = this.marketsById[pairId]; + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'created'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + let status = this.safeInteger (order, 'active'); + let remaining = this.safeFloat (order, 'remaining'); + let filled = undefined; + if (status === 1) { + status = 'open'; + } else { + status = 'closed'; + remaining = 0; + filled = amount; + } + let fee = undefined; + const feeCost = this.safeFloat (order, 'fee'); + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + }; + if (market !== undefined) { + fee['currency'] = market['quote']; + } + } + return { + 'id': this.safeString (order, 'order_id'), + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': 'limit', + 'side': this.safeStrin (order, 'type'), + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + 'type': side, + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostOrderNew (this.extend (request, params)); + const order = this.parseOrder ({ + 'order_id': response['response']['entity']['order_id'], + 'created': this.seconds (), + 'active': 1, + 'type': side, + 'price': price, + 'volume': amount, + 'remaining': amount, + 'info': response, + }, market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order_id': id, + }; + return await this.privatePostOrderCancel (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order _id': id, + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + const order = this.safeValue (response['response'], 'entity'); + return this.parseOrder (order); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrderList (params); + const orders = this.safeValue (response['response'], 'entities'); + return this.parseOrders (orders, undefined, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair_id': market['id'], + }; + if (limit !== undefined) { + request['items_per_page'] = limit; + } + if (since !== undefined) { + request['date_from'] = parseInt (since / 1000); + } + const response = await this.privatePostTradeList (this.extend (request, params)); + const trades = this.safeValue (response['response'], 'entities'); + return this.parseTrades (trades, market, since, limit); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency_id': currency['id'], + }; + const response = await this.privatePostBalanceInfo (this.extend (request, params)); + const balance = this.safeValue (response['response'], 'entity'); + const address = this.safeString (balance, 'address'); + const status = address ? 'ok' : 'none'; + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'status': status, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'nonce': this.nonce (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const errors = this.safeValue (response, 'errors'); + const data = this.safeValue (response, 'response'); + if (errors || !data) { + const authErrorKeys = [ 'Key', 'user_id', 'Sign' ]; + for (let i = 0; i < authErrorKeys.length; i++) { + const errorKey = authErrorKeys[i]; + const errorMessage = this.safeString (errors, errorKey); + if (!errorMessage) { + continue; + } + if (errorKey === 'user_id' && errorMessage.indexOf ('authorization') < 0) { + continue; + } + throw new AuthenticationError (errorMessage); + } + throw new ExchangeError (this.json (errors)); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/idex.js b/TaskServer/node_modules/ccxt/js/idex.js new file mode 100644 index 000000000..50000d5eb --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/idex.js @@ -0,0 +1,1079 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class idex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'idex', + 'name': 'IDEX', + 'countries': [ 'US' ], + 'rateLimit': 1500, + 'certified': true, + 'requiresWeb3': true, + 'has': { + 'fetchOrderBook': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchMarkets': true, + 'fetchBalance': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchOpenOrders': true, + 'fetchTransactions': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchOHLCV': false, + }, + 'timeframes': { + '1m': 'M1', + '3m': 'M3', + '5m': 'M5', + '15m': 'M15', + '30m': 'M30', // default + '1h': 'H1', + '4h': 'H4', + '1d': 'D1', + '1w': 'D7', + '1M': '1M', + }, + 'urls': { + 'test': 'https://api.idex.market', + 'logo': 'https://user-images.githubusercontent.com/1294454/63693236-3415e380-c81c-11e9-8600-ba1634f1407d.jpg', + 'api': 'https://api.idex.market', + 'www': 'https://idex.market', + 'doc': [ + 'https://docs.idex.market/', + ], + }, + 'api': { + 'public': { + 'post': [ + 'returnTicker', + 'returnCurrenciesWithPairs', // undocumented + 'returnCurrencies', + 'return24Volume', + 'returnBalances', + 'returnCompleteBalances', // shows amount in orders as well as total + 'returnDepositsWithdrawals', + 'returnOpenOrders', + 'returnOrderBook', + 'returnOrderStatus', + 'returnOrderTrades', + 'returnTradeHistory', + 'returnTradeHistoryMeta', // not documented + 'returnContractAddress', + 'returnNextNonce', + ], + }, + 'private': { + 'post': [ + 'order', + 'cancel', + 'trade', + 'withdraw', + ], + }, + }, + 'options': { + 'contractAddress': undefined, // 0x2a0c0DBEcC7E4D658f48E01e3fA353F44050c208 + 'orderNonce': undefined, + }, + 'exceptions': { + 'Invalid order signature. Please try again.': AuthenticationError, + 'You have insufficient funds to match this order. If you believe this is a mistake please refresh and try again.': InsufficientFunds, + 'Order no longer available.': InvalidOrder, + }, + 'requiredCredentials': { + 'walletAddress': true, + 'privateKey': true, + 'apiKey': false, + 'secret': false, + }, + 'commonCurrencies': { + 'ONE': 'Menlo One', + 'FT': 'Fabric Token', + }, + }); + } + + async fetchMarkets (params = {}) { + // idex does not have an endpoint for markets + // instead we generate the markets from the endpoint for currencies + const request = { + 'includeDelisted': true, + }; + const markets = await this.publicPostReturnCurrenciesWithPairs (this.extend (request, params)); + const currenciesById = {}; + const currencies = markets['tokens']; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + currenciesById[currency['symbol']] = currency; + } + const result = []; + const limits = { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + const quotes = markets['pairs']; + const keys = Object.keys (quotes); + for (let i = 0; i < keys.length; i++) { + const quoteId = keys[i]; + const bases = quotes[quoteId]; + const quote = this.safeCurrencyCode (quoteId); + const quoteCurrency = currenciesById[quoteId]; + for (let j = 0; j < bases.length; j++) { + const baseId = bases[j]; + const id = quoteId + '_' + baseId; + const base = this.safeCurrencyCode (baseId); + const symbol = base + '/' + quote; + const baseCurrency = currenciesById[baseId]; + const baseAddress = baseCurrency['address']; + const quoteAddress = quoteCurrency['address']; + const precision = { + 'price': this.safeInteger (quoteCurrency, 'decimals'), + 'amount': this.safeInteger (baseCurrency, 'decimals'), + }; + result.push ({ + 'symbol': symbol, + 'precision': precision, + 'base': base, + 'quote': quote, + 'baseId': baseAddress, + 'quoteId': quoteAddress, + 'limits': limits, + 'id': id, + 'info': baseCurrency, + 'tierBased': false, + }); + } + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { + // last: '0.0016550916', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.0016743368', + // highestBid: '0.001163726270773897', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' + // } + // + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const baseVolume = this.safeFloat (ticker, 'baseVolume'); + const quoteVolume = this.safeFloat (ticker, 'quoteVolume'); + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'percentChange'); + return { + 'symbol': symbol, + 'timestamp': undefined, + 'datetime': undefined, + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': quoteVolume, + 'quoteVolume': baseVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicPostReturnTicker (params); + // { ETH_BOUNCY: + // { last: '0.000000004000088005', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.00000000599885995', + // highestBid: '0.000000001400500103', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' }, + // ETH_NBAI: + // { last: '0.0000032', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.000004000199999502', + // highestBid: '0.0000016002', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' }, } + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = undefined; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + market = { 'symbol': symbol }; + } + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicPostReturnTicker (this.extend (request, params)); + // { last: '0.0016550916', + // high: 'N/A', + // low: 'N/A', + // lowestAsk: '0.0016743368', + // highestBid: '0.001163726270773897', + // percentChange: '0', + // baseVolume: '0', + // quoteVolume: '0' } + return this.parseTicker (response, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const id = market['quote'] + '_' + market['base']; + const request = { + 'market': id, + 'count': 100, // the default will only return one trade + }; + if (limit !== undefined) { + request['count'] = limit; + } + const response = await this.publicPostReturnOrderBook (this.extend (request, params)); + // + // { + // "asks": [ + // { + // "price": "0.001675282799999", + // "amount": "206.163978911921061732", + // "total": "0.345382967850497906", + // "orderHash": "0xfdf12c124a6a7fa4a8e1866b324da888c8e1b3ad209f5050d3a23df3397a5cb7", + // "params": { + // "tokenBuy": "0x0000000000000000000000000000000000000000", + // "buySymbol": "ETH", + // "buyPrecision": 18, + // "amountBuy": "345382967850497906", + // "tokenSell": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", + // "sellSymbol": "ABT", + // "sellPrecision": 18, + // "amountSell": "206163978911921061732", + // "expires": 10000, + // "nonce": 13489307413, + // "user": "0x9e8ef79316a4a79bbf55a5f9c16b3e068fff65c6" + // } + // } + // ], + // "bids": [ + // { + // "price": "0.001161865193232242", + // "amount": "854.393661648355", + // "total": "0.992690256787469029", + // "orderHash": "0x2f2baaf982085e4096f9e23e376214885fa74b2939497968e92222716fc2c86d", + // "params": { + // "tokenBuy": "0xb98d4c97425d9908e66e53a6fdf673acca0be986", + // "buySymbol": "ABT", + // "buyPrecision": 18, + // "amountBuy": "854393661648355000000", + // "tokenSell": "0x0000000000000000000000000000000000000000", + // "sellSymbol": "ETH", + // "sellPrecision": 18, + // "amountSell": "992690256787469029", + // "expires": 10000, + // "nonce": 18155189676, + // "user": "0xb631284dd7b74a846af5b37766ceb1f85d53eca4" + // } + // } + // ] + // } + // + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + parseBidAsk (bidAsk, priceKey = 0, amountKey = 1) { + const price = this.safeFloat (bidAsk, priceKey); + const amount = this.safeFloat (bidAsk, amountKey); + const info = bidAsk; + return [price, amount, info]; + } + + async fetchBalance (params = {}) { + const request = { + 'address': this.walletAddress, + }; + const response = await this.publicPostReturnCompleteBalances (this.extend (request, params)); + // + // { + // ETH: { available: '0.0167', onOrders: '0.1533' } + // } + // + const result = { + 'info': response, + }; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const currency = keys[i]; + const balance = response[currency]; + const code = this.safeCurrencyCode (currency); + result[code] = { + 'free': this.safeFloat (balance, 'available'), + 'used': this.safeFloat (balance, 'onOrders'), + }; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + this.checkRequiredDependencies (); + await this.loadMarkets (); + const market = this.market (symbol); + if (type === 'limit') { + const expires = 100000; + const contractAddress = await this.getContractAddress (); + let tokenBuy = undefined; + let tokenSell = undefined; + let amountBuy = undefined; + let amountSell = undefined; + const quoteAmount = parseFloat (price) * parseFloat (amount); + if (side === 'buy') { + tokenBuy = market['baseId']; + tokenSell = market['quoteId']; + amountBuy = this.toWei (amount, 'ether', market['precision']['amount']); + amountSell = this.toWei (quoteAmount, 'ether', 18); + } else { + tokenBuy = market['quoteId']; + tokenSell = market['baseId']; + amountBuy = this.toWei (quoteAmount, 'ether', 18); + amountSell = this.toWei (amount, 'ether', market['precision']['amount']); + } + const nonce = await this.getNonce (); + const orderToHash = { + 'contractAddress': contractAddress, + 'tokenBuy': tokenBuy, + 'amountBuy': amountBuy, + 'tokenSell': tokenSell, + 'amountSell': amountSell, + 'expires': expires, + 'nonce': nonce, + 'address': this.walletAddress, + }; + const orderHash = this.getIdexCreateOrderHash (orderToHash); + const signature = this.signMessage (orderHash, this.privateKey); + const request = { + 'tokenBuy': tokenBuy, + 'amountBuy': amountBuy, + 'tokenSell': tokenSell, + 'amountSell': amountSell, + 'address': this.walletAddress, + 'nonce': nonce, + 'expires': expires, + }; + const response = await this.privatePostOrder (this.extend (request, signature)); // this.extend (request, params) will cause invalid signature + // { orderNumber: 1562323021, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // timestamp: 1564041428, + // price: '0.00073', + // amount: '210', + // total: '0.1533', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' } } + return this.parseOrder (response, market); + } else if (type === 'market') { + if (!('orderHash' in params)) { + throw new ArgumentsRequired (this.id + ' market order requires an order structure such as that in fetchOrderBook()[\'bids\'][0][2], fetchOrder()[\'info\'], or fetchOpenOrders()[0][\'info\']'); + } + // { price: '0.000132247803328924', + // amount: '19980', + // total: '2.6423111105119', + // orderHash: + // '0x5fb3452b3d13fc013585b51c91c43a0fbe4298c211243763c49437848c274749', + // params: + // { tokenBuy: '0x0000000000000000000000000000000000000000', + // buySymbol: 'ETH', + // buyPrecision: 18, + // amountBuy: '2642311110511900000', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // sellSymbol: 'IDEX', + // sellPrecision: 18, + // amountSell: '19980000000000000000000', + // expires: 10000, + // nonce: 1564656561510, + // user: '0xc3f8304270e49b8e8197bfcfd8567b83d9e4479b' } } + const orderToSign = { + 'orderHash': params['orderHash'], + 'amount': params['params']['amountBuy'], + 'address': params['params']['user'], + 'nonce': params['params']['nonce'], + }; + const orderHash = this.getIdexMarketOrderHash (orderToSign); + const signature = this.signMessage (orderHash, this.privateKey); + const signedOrder = this.extend (orderToSign, signature); + signedOrder['address'] = this.walletAddress; + signedOrder['nonce'] = await this.getNonce (); + // [ { + // "amount": "0.07", + // "date": "2017-10-13 16:25:36", + // "total": "0.49", + // "market": "ETH_DVIP", + // "type": "buy", + // "price": "7", + // "orderHash": "0xcfe4018c59e50e0e1964c979e6213ce5eb8c751cbc98a44251eb48a0985adc52", + // "uuid": "250d51a0-b033-11e7-9984-a9ab79bb8f35" + // } ] + const response = await this.privatePostTrade (signedOrder); + return this.parseOrders (response, market); + } + } + + async getNonce () { + if (this.options['orderNonce'] === undefined) { + const response = await this.publicPostReturnNextNonce ({ + 'address': this.walletAddress, + }); + return this.safeInteger (response, 'nonce'); + } else { + const result = this.options['orderNonce']; + this.options['orderNonce'] = this.sum (this.options['orderNonce'], 1); + return result; + } + } + + async getContractAddress () { + if (this.options['contractAddress'] !== undefined) { + return this.options['contractAddress']; + } + const response = await this.publicPostReturnContractAddress (); + this.options['contractAddress'] = this.safeString (response, 'address'); + return this.options['contractAddress']; + } + + async cancelOrder (orderId, symbol = undefined, params = {}) { + const nonce = await this.getNonce (); + const orderToHash = { + 'orderHash': orderId, + 'nonce': nonce, + }; + const orderHash = this.getIdexCancelOrderHash (orderToHash); + const signature = this.signMessage (orderHash, this.privateKey); + const request = { + 'orderHash': orderId, + 'address': this.walletAddress, + 'nonce': nonce, + }; + const response = await this.privatePostCancel (this.extend (request, signature)); + // { success: 1 } + if ('success' in response) { + return { + 'info': response, + }; + } else { + throw new ExchangeError (this.id + ' cancel order failed ' + this.json (response)); + } + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'address': this.walletAddress, + }; + if (since !== undefined) { + request['start'] = parseInt (Math.floor (since / 1000)); + } + const response = await this.publicPostReturnDepositsWithdrawals (this.extend (request, params)); + // { deposits: + // [ { currency: 'ETH', + // amount: '0.05', + // timestamp: 1563953513, + // transactionHash: + // '0xd6eefd81c7efc9beeb35b924d6db3c93a78bf7eac082ba87e107ad4e94bccdcf', + // depositNumber: 1586430 }, + // { currency: 'ETH', + // amount: '0.12', + // timestamp: 1564040359, + // transactionHash: + // '0x2ecbb3ab72b6f79fc7a9058c39dce28f913152748c1507d13ab1759e965da3ca', + // depositNumber: 1587341 } ], + // withdrawals: + // [ { currency: 'ETH', + // amount: '0.149', + // timestamp: 1564060001, + // transactionHash: + // '0xab555fc301779dd92fd41ccd143b1d72776ae7b5acfc59ca44a1d376f68fda15', + // withdrawalNumber: 1444070, + // status: 'COMPLETE' } ] } + const deposits = this.parseTransactions (response['deposits'], currency, since, limit); + const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); + return this.arrayConcat (deposits, withdrawals); + } + + parseTransaction (item, currency = undefined) { + // { currency: 'ETH', + // amount: '0.05', + // timestamp: 1563953513, + // transactionHash: + // '0xd6eefd81c7efc9beeb35b924d6db3c93a78bf7eac082ba87e107ad4e94bccdcf', + // depositNumber: 1586430 } + const amount = this.safeFloat (item, 'amount'); + const timestamp = this.safeTimestamp (item, 'timestamp'); + const txhash = this.safeString (item, 'transactionHash'); + let id = undefined; + let type = undefined; + let status = undefined; + let addressFrom = undefined; + let addressTo = undefined; + if ('depositNumber' in item) { + id = this.safeString (item, 'depositNumber'); + type = 'deposit'; + addressFrom = this.walletAddress; + addressTo = this.options['contractAddress']; + } else if ('withdrawalNumber' in item) { + id = this.safeString (item, 'withdrawalNumber'); + type = 'withdrawal'; + status = this.parseTransactionStatus (this.safeString (item, 'status')); + addressFrom = this.options['contractAddress']; + addressTo = this.walletAddress; + } + const code = this.safeCurrencyCode (this.safeString (item, 'currency')); + return { + 'info': item, + 'id': id, + 'txid': txhash, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'currency': code, + 'amount': amount, + 'status': status, + 'type': type, + 'updated': undefined, + 'comment': undefined, + 'addressFrom': addressFrom, + 'tagFrom': undefined, + 'addressTo': addressTo, + 'tagTo': undefined, + 'fee': { + 'currency': code, + 'cost': undefined, + 'rate': undefined, + }, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'COMPLETE': 'ok', + }; + return this.safeString (statuses, status); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (this.walletAddress === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a walletAddress'); + } + await this.loadMarkets (); + const request = { + 'address': this.walletAddress, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.publicPostReturnOpenOrders (this.extend (request, params)); + // [ { timestamp: 1564041428, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // orderNumber: 1562323021, + // market: 'ETH_LIT', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buySymbol: 'LIT', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellSymbol: 'ETH', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, + // price: '0.00073', + // amount: '210', + // status: 'open', + // total: '0.1533' } ] + return this.parseOrders (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { + 'orderHash': id, + }; + const response = await this.publicPostReturnOrderStatus (this.extend (request, params)); + // { filled: '0', + // initialAmount: '210', + // timestamp: 1564041428, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // orderNumber: 1562323021, + // market: 'ETH_LIT', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buySymbol: 'LIT', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellSymbol: 'ETH', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, + // price: '0.00073', + // amount: '210', + // status: 'open', + // total: '0.1533' } + return this.parseOrder (response, market); + } + + parseOrder (order, market = undefined) { + // { filled: '0', + // initialAmount: '210', + // timestamp: 1564041428, + // orderHash: + // '0x31c42154a8421425a18d076df400d9ec1ef64d5251285384a71ba3c0ab31beb4', + // orderNumber: 1562323021, + // market: 'ETH_LIT', + // type: 'buy', + // params: + // { tokenBuy: '0x763fa6806e1acf68130d2d0f0df754c93cc546b2', + // buySymbol: 'LIT', + // buyPrecision: 18, + // amountBuy: '210000000000000000000', + // tokenSell: '0x0000000000000000000000000000000000000000', + // sellSymbol: 'ETH', + // sellPrecision: 18, + // amountSell: '153300000000000000', + // expires: 100000, + // nonce: 1, + // user: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1' }, + // price: '0.00073', + // amount: '210', + // status: 'open', + // total: '0.1533' } + const timestamp = this.safeTimestamp (order, 'timestamp'); + const side = this.safeString (order, 'type'); + let symbol = undefined; + let amount = undefined; + let remaining = undefined; + if ('initialAmount' in order) { + amount = this.safeFloat (order, 'initialAmount'); + remaining = this.safeFloat (order, 'amount'); + } else { + amount = this.safeFloat (order, 'amount'); + } + const filled = this.safeFloat (order, 'filled'); + const cost = this.safeFloat (order, 'total'); + const price = this.safeFloat (order, 'price'); + if ('market' in order) { + const marketId = order['market']; + symbol = this.markets_by_id[marketId]['symbol']; + } else if ((side !== undefined) && ('params' in order)) { + const params = order['params']; + const buy = this.safeCurrencyCode (this.safeString (params, 'tokenBuy')); + const sell = this.safeCurrencyCode (this.safeString (params, 'tokenSell')); + if (buy !== undefined && sell !== undefined) { + symbol = (side === 'buy') ? (buy + '/' + sell) : (sell + '/' + buy); + } + } + if (symbol === undefined && market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'orderHash'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'side': side, + 'amount': amount, + 'price': price, + 'type': 'limit', + 'filled': filled, + 'remaining': remaining, + 'cost': cost, + 'status': status, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'open': 'open', + }; + return this.safeString (statuses, status, status); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (this.walletAddress === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a walletAddress'); + } + await this.loadMarkets (); + const request = { + 'address': this.walletAddress, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + if (limit !== undefined) { + request['start'] = parseInt (Math.floor (limit)); + } + const response = await this.publicPostReturnTradeHistory (this.extend (request, params)); + // { ETH_IDEX: + // [ { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } ] } + // + // if a symbol is specified in the request: + // + // [ { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } ] + if (Array.isArray (response)) { + return this.parseTrades (response, market, since, limit); + } else { + let result = []; + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const trades = response[marketId]; + const parsed = this.parseTrades (trades, market, since, limit); + result = this.arrayConcat (result, parsed); + } + return result; + } + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['start'] = parseInt (Math.floor (limit)); + } + const response = await this.publicPostReturnTradeHistory (this.extend (request, params)); + // [ { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } ] + return this.parseTrades (response, market, since, limit); + } + + parseTrade (trade, market = undefined) { + // { type: 'buy', + // date: '2019-07-25 11:24:41', + // amount: '347.833140025692348611', + // total: '0.050998794333719943', + // uuid: 'cbdff960-aece-11e9-b566-c5d69c3be671', + // tid: 4320867, + // timestamp: 1564053881, + // price: '0.000146618560640751', + // taker: '0x0ab991497116f7f5532a4c2f4f7b1784488628e1', + // maker: '0x1a961bc2e0d619d101f5f92a6be752132d7606e6', + // orderHash: + // '0xbec6485613a15be619c04c1425e8e821ebae42b88fa95ac4dfe8ba2beb363ee4', + // transactionHash: + // '0xf094e07b329ac8046e8f34db358415863c41daa36765c05516f4cf4f5b403ad1', + // tokenBuy: '0x0000000000000000000000000000000000000000', + // buyerFee: '0.695666280051384697', + // gasFee: '28.986780264563232993', + // sellerFee: '0.00005099879433372', + // tokenSell: '0xb705268213d593b8fd88d3fdeff93aff5cbdcfae', + // usdValue: '11.336926687304238214' } + const side = this.safeString (trade, 'type'); + let feeCurrency = undefined; + let symbol = undefined; + const maker = this.safeString (trade, 'maker'); + let takerOrMaker = undefined; + if (maker !== undefined && this.walletAddress !== undefined) { + if (maker.toLowerCase () === this.walletAddress.toLowerCase ()) { + takerOrMaker = 'maker'; + } else { + takerOrMaker = 'taker'; + } + } + const buy = this.safeCurrencyCode (this.safeString (trade, 'tokenBuy')); + const sell = this.safeCurrencyCode (this.safeString (trade, 'tokenSell')); + // get ready to be mind-boggled + let feeSide = undefined; + if (buy !== undefined && sell !== undefined) { + if (side === 'buy') { + feeSide = 'buyerFee'; + if (takerOrMaker === 'maker') { + symbol = buy + '/' + sell; + feeCurrency = buy; + } else { + symbol = sell + '/' + buy; + feeCurrency = sell; + } + } else { + feeSide = 'sellerFee'; + if (takerOrMaker === 'maker') { + symbol = sell + '/' + buy; + feeCurrency = buy; + } else { + symbol = buy + '/' + sell; + feeCurrency = sell; + } + } + } + if (symbol === undefined && market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const id = this.safeString (trade, 'tid'); + const amount = this.safeFloat (trade, 'amount'); + const price = this.safeFloat (trade, 'price'); + const cost = this.safeFloat (trade, 'total'); + let feeCost = this.safeFloat (trade, feeSide); + if (feeCost < 0) { + const gasFee = this.safeFloat (trade, 'gasFee'); + feeCost = this.sum (gasFee, feeCost); + } + const fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + if (feeCost !== undefined && amount !== undefined) { + const feeCurrencyAmount = (feeCurrency === 'ETH') ? cost : amount; + fee['rate'] = feeCost / feeCurrencyAmount; + } + const orderId = this.safeString (trade, 'orderHash'); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'order': orderId, + 'symbol': symbol, + 'type': 'limit', + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkRequiredDependencies (); + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const tokenAddress = currency['id']; + const nonce = await this.getNonce (); + amount = this.toWei (amount, 'ether', currency['precision']); + const requestToHash = { + 'contractAddress': await this.getContractAddress (), + 'token': tokenAddress, + 'amount': amount, + 'address': address, + 'nonce': nonce, + }; + const hash = this.getIdexWithdrawHash (requestToHash); + const signature = this.signMessage (hash, this.privateKey); + const request = { + 'address': address, + 'amount': amount, + 'token': tokenAddress, + 'nonce': nonce, + }; + const response = await this.privatePostWithdraw (this.extend (request, signature)); + // { amount: '0' } + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + body = this.json (params); // all methods are POST + const url = this.urls['api'] + '/' + path; + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }; + if (api === 'private') { + this.checkRequiredCredentials (); + headers['API-Key'] = this.apiKey; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + getIdexCreateOrderHash (order) { + return this.soliditySha3 ([ + order['contractAddress'], // address + order['tokenBuy'], // address + order['amountBuy'], // uint256 + order['tokenSell'], // address + order['amountSell'], // uint256 + order['expires'], // uint256 + order['nonce'], // uint256 + order['address'], // address + ]); + } + + getIdexCancelOrderHash (order) { + return this.soliditySha3 ([ + order['orderHash'], // address + order['nonce'], // uint256 + ]); + } + + getIdexMarketOrderHash (order) { + return this.soliditySha3 ([ + order['orderHash'], // address + order['amount'], // uint256 + order['address'], // address + order['nonce'], // uint256 + ]); + } + + getIdexWithdrawHash (request) { + return this.soliditySha3 ([ + request['contractAddress'], // address + request['token'], // uint256 + request['amount'], // uint256 + request['address'], // address + request['nonce'], // uint256 + ]); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if ('error' in response) { + if (response['error'] in this.exceptions) { + throw new this.exceptions[response['error']] (this.id + ' ' + response['error']); + } + throw new ExchangeError (this.id + ' ' + body); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/independentreserve.js b/TaskServer/node_modules/ccxt/js/independentreserve.js new file mode 100644 index 000000000..98a30e08e --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/independentreserve.js @@ -0,0 +1,409 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class independentreserve extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'independentreserve', + 'name': 'Independent Reserve', + 'countries': [ 'AU', 'NZ' ], // Australia, New Zealand + 'rateLimit': 1000, + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30521662-cf3f477c-9bcb-11e7-89bc-d1ac85012eda.jpg', + 'api': { + 'public': 'https://api.independentreserve.com/Public', + 'private': 'https://api.independentreserve.com/Private', + }, + 'www': 'https://www.independentreserve.com', + 'doc': 'https://www.independentreserve.com/API', + }, + 'api': { + 'public': { + 'get': [ + 'GetValidPrimaryCurrencyCodes', + 'GetValidSecondaryCurrencyCodes', + 'GetValidLimitOrderTypes', + 'GetValidMarketOrderTypes', + 'GetValidOrderTypes', + 'GetValidTransactionTypes', + 'GetMarketSummary', + 'GetOrderBook', + 'GetAllOrders', + 'GetTradeHistorySummary', + 'GetRecentTrades', + 'GetFxRates', + ], + }, + 'private': { + 'post': [ + 'PlaceLimitOrder', + 'PlaceMarketOrder', + 'CancelOrder', + 'GetOpenOrders', + 'GetClosedOrders', + 'GetClosedFilledOrders', + 'GetOrderDetails', + 'GetAccounts', + 'GetTransactions', + 'GetDigitalCurrencyDepositAddress', + 'GetDigitalCurrencyDepositAddresses', + 'SynchDigitalCurrencyDepositAddressWithBlockchain', + 'WithdrawDigitalCurrency', + 'RequestFiatWithdrawal', + 'GetTrades', + 'GetBrokerageFees', + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.5 / 100, + 'maker': 0.5 / 100, + 'percentage': true, + 'tierBased': false, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const baseCurrencies = await this.publicGetGetValidPrimaryCurrencyCodes (params); + const quoteCurrencies = await this.publicGetGetValidSecondaryCurrencyCodes (params); + const result = []; + for (let i = 0; i < baseCurrencies.length; i++) { + const baseId = baseCurrencies[i]; + const base = this.safeCurrencyCode (baseId); + for (let j = 0; j < quoteCurrencies.length; j++) { + const quoteId = quoteCurrencies[j]; + const quote = this.safeCurrencyCode (quoteId); + const id = baseId + '/' + quoteId; + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': id, + }); + } + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privatePostGetAccounts (params); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'CurrencyCode'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'AvailableBalance'); + account['total'] = this.safeFloat (balance, 'TotalBalance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + }; + const response = await this.publicGetGetOrderBook (this.extend (request, params)); + const timestamp = this.parse8601 (this.safeString (response, 'CreatedTimestampUtc')); + return this.parseOrderBook (response, timestamp, 'BuyOrders', 'SellOrders', 'Price', 'Volume'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.parse8601 (this.safeString (ticker, 'CreatedTimestampUtc')); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'LastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'DayHighestPrice'), + 'low': this.safeFloat (ticker, 'DayLowestPrice'), + 'bid': this.safeFloat (ticker, 'CurrentHighestBidPrice'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'CurrentLowestOfferPrice'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'DayAvgPrice'), + 'baseVolume': this.safeFloat (ticker, 'DayVolumeXbtInSecondaryCurrrency'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + }; + const response = await this.publicGetGetMarketSummary (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + const baseId = this.safeString (order, 'PrimaryCurrencyCode'); + const quoteId = this.safeString (order, 'PrimaryCurrencyCode'); + let base = undefined; + let quote = undefined; + if ((baseId !== undefined) && (quoteId !== undefined)) { + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + quote = market['quote']; + } + let orderType = this.safeValue (order, 'Type'); + if (orderType.indexOf ('Market') >= 0) { + orderType = 'market'; + } else if (orderType.indexOf ('Limit') >= 0) { + orderType = 'limit'; + } + let side = undefined; + if (orderType.indexOf ('Bid') >= 0) { + side = 'buy'; + } else if (orderType.indexOf ('Offer') >= 0) { + side = 'sell'; + } + const timestamp = this.parse8601 (this.safeString (order, 'CreatedTimestampUtc')); + let amount = this.safeFloat (order, 'VolumeOrdered'); + if (amount === undefined) { + amount = this.safeFloat (order, 'Volume'); + } + const filled = this.safeFloat (order, 'VolumeFilled'); + let remaining = undefined; + const feeRate = this.safeFloat (order, 'FeePercent'); + let feeCost = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + if (feeRate !== undefined) { + feeCost = feeRate * filled; + } + } + } + const fee = { + 'rate': feeRate, + 'cost': feeCost, + 'currency': base, + }; + const id = this.safeString (order, 'OrderGuid'); + const status = this.parseOrderStatus (this.safeString (order, 'Status')); + const cost = this.safeFloat (order, 'Value'); + const average = this.safeFloat (order, 'AvgPrice'); + const price = this.safeFloat (order, 'Price', average); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': orderType, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'Open': 'open', + 'PartiallyFilled': 'open', + 'Filled': 'closed', + 'PartiallyFilledAndCancelled': 'canceled', + 'Cancelled': 'canceled', + 'PartiallyFilledAndExpired': 'canceled', + 'Expired': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetOrderDetails (this.extend ({ + 'orderGuid': id, + }, params)); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseOrder (response, market); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = 50, params = {}) { + await this.loadMarkets (); + const pageIndex = this.safeInteger (params, 'pageIndex', 1); + if (limit === undefined) { + limit = 50; + } + const request = this.ordered ({ + 'pageIndex': pageIndex, + 'pageSize': limit, + }); + const response = await this.privatePostGetTrades (this.extend (request, params)); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseTrades (response['Data'], market, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (trade['TradeTimestampUtc']); + const id = this.safeString (trade, 'TradeGuid'); + const orderId = this.safeString (trade, 'OrderGuid'); + const price = this.safeFloat2 (trade, 'Price', 'SecondaryCurrencyTradePrice'); + const amount = this.safeFloat2 (trade, 'VolumeTraded', 'PrimaryCurrencyAmount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeString (trade, 'OrderType'); + if (side !== undefined) { + if (side.indexOf ('Bid') >= 0) { + side = 'buy'; + } else if (side.indexOf ('Offer') >= 0) { + side = 'sell'; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + 'numberOfRecentTradesToRetrieve': 50, // max = 50 + }; + const response = await this.publicGetGetRecentTrades (this.extend (request, params)); + return this.parseTrades (response['Trades'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const capitalizedOrderType = this.capitalize (type); + const method = 'privatePostPlace' + capitalizedOrderType + 'Order'; + let orderType = capitalizedOrderType; + orderType += (side === 'sell') ? 'Offer' : 'Bid'; + const request = this.ordered ({ + 'primaryCurrencyCode': market['baseId'], + 'secondaryCurrencyCode': market['quoteId'], + 'orderType': orderType, + }); + if (type === 'limit') { + request['price'] = price; + } + request['volume'] = amount; + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['OrderGuid'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderGuid': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const auth = [ + url, + 'apiKey=' + this.apiKey, + 'nonce=' + nonce.toString (), + ]; + const keys = Object.keys (params); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = params[key].toString (); + auth.push (key + '=' + value); + } + const message = auth.join (','); + const signature = this.hmac (this.encode (message), this.encode (this.secret)); + const query = this.ordered ({}); + query['apiKey'] = this.apiKey; + query['nonce'] = nonce; + query['signature'] = signature.toUpperCase (); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + query[key] = params[key]; + } + body = this.json (query); + headers = { 'Content-Type': 'application/json' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/indodax.js b/TaskServer/node_modules/ccxt/js/indodax.js new file mode 100644 index 000000000..6d54ec676 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/indodax.js @@ -0,0 +1,553 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InsufficientFunds, InvalidOrder, OrderNotFound, AuthenticationError, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class indodax extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'indodax', + 'name': 'INDODAX', + 'countries': [ 'ID' ], // Indonesia + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchTickers': false, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchClosedOrders': true, + 'fetchOpenOrders': true, + 'fetchMyTrades': false, + 'fetchCurrencies': false, + 'withdraw': true, + }, + 'version': '1.8', // as of 9 April 2018 + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/37443283-2fddd0e4-281c-11e8-9741-b4f1419001b5.jpg', + 'api': { + 'public': 'https://indodax.com/api', + 'private': 'https://indodax.com/tapi', + }, + 'www': 'https://www.indodax.com', + 'doc': 'https://indodax.com/downloads/BITCOINCOID-API-DOCUMENTATION.pdf', + 'referral': 'https://indodax.com/ref/testbitcoincoid/1', + }, + 'api': { + 'public': { + 'get': [ + '{pair}/ticker', + '{pair}/trades', + '{pair}/depth', + ], + }, + 'private': { + 'post': [ + 'getInfo', + 'transHistory', + 'trade', + 'tradeHistory', + 'getOrder', + 'openOrders', + 'cancelOrder', + 'orderHistory', + 'withdrawCoin', + ], + }, + }, + 'markets': { + // HARDCODING IS DEPRECATED + // but they don't have a corresponding endpoint in their API + // IDR markets + 'BTC/IDR': { 'id': 'btc_idr', 'symbol': 'BTC/IDR', 'base': 'BTC', 'quote': 'IDR', 'baseId': 'btc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.0001, 'max': undefined }}}, + 'TEN/IDR': { 'id': 'ten_idr', 'symbol': 'TEN/IDR', 'base': 'TEN', 'quote': 'IDR', 'baseId': 'ten', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'ABYSS/IDR': { 'id': 'abyss_idr', 'symbol': 'ABYSS/IDR', 'base': 'ABYSS', 'quote': 'IDR', 'baseId': 'abyss', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'ACT/IDR': { 'id': 'act_idr', 'symbol': 'ACT/IDR', 'base': 'ACT', 'quote': 'IDR', 'baseId': 'act', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'ADA/IDR': { 'id': 'ada_idr', 'symbol': 'ADA/IDR', 'base': 'ADA', 'quote': 'IDR', 'baseId': 'ada', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'AOA/IDR': { 'id': 'aoa_idr', 'symbol': 'AOA/IDR', 'base': 'AOA', 'quote': 'IDR', 'baseId': 'aoa', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'ATOM/IDR': { 'id': 'atom_idr', 'symbol': 'ATOM/IDR', 'base': 'ATOM', 'quote': 'IDR', 'baseId': 'atom', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'BAT/IDR': { 'id': 'bat_idr', 'symbol': 'BAT/IDR', 'base': 'BAT', 'quote': 'IDR', 'baseId': 'bat', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'BCD/IDR': { 'id': 'bcd_idr', 'symbol': 'BCD/IDR', 'base': 'BCD', 'quote': 'IDR', 'baseId': 'bcd', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'BCH/IDR': { 'id': 'bchabc_idr', 'symbol': 'BCH/IDR', 'base': 'BCH', 'quote': 'IDR', 'baseId': 'bchabc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'BSV/IDR': { 'id': 'bchsv_idr', 'symbol': 'BSV/IDR', 'base': 'BSV', 'quote': 'IDR', 'baseId': 'bchsv', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'BNB/IDR': { 'id': 'bnb_idr', 'symbol': 'BNB/IDR', 'base': 'BNB', 'quote': 'IDR', 'baseId': 'bnb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'BTG/IDR': { 'id': 'btg_idr', 'symbol': 'BTG/IDR', 'base': 'BTG', 'quote': 'IDR', 'baseId': 'btg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'BTS/IDR': { 'id': 'bts_idr', 'symbol': 'BTS/IDR', 'base': 'BTS', 'quote': 'IDR', 'baseId': 'bts', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'BTT/IDR': { 'id': 'btt_idr', 'symbol': 'BTT/IDR', 'base': 'BTT', 'quote': 'IDR', 'baseId': 'btt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1000, 'max': undefined }}}, + 'COAL/IDR': { 'id': 'coal_idr', 'symbol': 'COAL/IDR', 'base': 'COAL', 'quote': 'IDR', 'baseId': 'coal', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 50, 'max': undefined }}}, + 'CRO/IDR': { 'id': 'cro_idr', 'symbol': 'CRO/IDR', 'base': 'CRO', 'quote': 'IDR', 'baseId': 'cro', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DASH/IDR': { 'id': 'drk_idr', 'symbol': 'DASH/IDR', 'base': 'DASH', 'quote': 'IDR', 'baseId': 'drk', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DAX/IDR': { 'id': 'dax_idr', 'symbol': 'DAX/IDR', 'base': 'DAX', 'quote': 'IDR', 'baseId': 'dax', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DOGE/IDR': { 'id': 'doge_idr', 'symbol': 'DOGE/IDR', 'base': 'DOGE', 'quote': 'IDR', 'baseId': 'doge', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1000, 'max': undefined }}}, + 'ETH/IDR': { 'id': 'eth_idr', 'symbol': 'ETH/IDR', 'base': 'ETH', 'quote': 'IDR', 'baseId': 'eth', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'EOS/IDR': { 'id': 'eos_idr', 'symbol': 'EOS/IDR', 'base': 'EOS', 'quote': 'IDR', 'baseId': 'eos', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'ETC/IDR': { 'id': 'etc_idr', 'symbol': 'ETC/IDR', 'base': 'ETC', 'quote': 'IDR', 'baseId': 'etc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'GARD/IDR': { 'id': 'gard_idr', 'symbol': 'GARD/IDR', 'base': 'GARD', 'quote': 'IDR', 'baseId': 'gard', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'GSC/IDR': { 'id': 'gsc_idr', 'symbol': 'GSC/IDR', 'base': 'GSC', 'quote': 'IDR', 'baseId': 'gsc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'GXC/IDR': { 'id': 'gxs_idr', 'symbol': 'GXC/IDR', 'base': 'GXC', 'quote': 'IDR', 'baseId': 'gxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'HPB/IDR': { 'id': 'hpb_idr', 'symbol': 'HPB/IDR', 'base': 'HPB', 'quote': 'IDR', 'baseId': 'hpb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'IGNIS/IDR': { 'id': 'ignis_idr', 'symbol': 'IGNIS/IDR', 'base': 'IGNIS', 'quote': 'IDR', 'baseId': 'ignis', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'INX/IDR': { 'id': 'inx_idr', 'symbol': 'INX/IDR', 'base': 'INX', 'quote': 'IDR', 'baseId': 'inx', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'IOTA/IDR': { 'id': 'iota_idr', 'symbol': 'IOTA/IDR', 'base': 'IOTA', 'quote': 'IDR', 'baseId': 'iota', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'LINK/IDR': { 'id': 'link_idr', 'symbol': 'LINK/IDR', 'base': 'LINK', 'quote': 'IDR', 'baseId': 'link', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'LTC/IDR': { 'id': 'ltc_idr', 'symbol': 'LTC/IDR', 'base': 'LTC', 'quote': 'IDR', 'baseId': 'ltc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'MBL/IDR': { 'id': 'mbl_idr', 'symbol': 'MBL/IDR', 'base': 'MBL', 'quote': 'IDR', 'baseId': 'mbl', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'NEO/IDR': { 'id': 'neo_idr', 'symbol': 'NEO/IDR', 'base': 'NEO', 'quote': 'IDR', 'baseId': 'neo', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'NPXS/IDR': { 'id': 'npxs_idr', 'symbol': 'NPXS/IDR', 'base': 'NPXS', 'quote': 'IDR', 'baseId': 'npxs', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'NXT/IDR': { 'id': 'nxt_idr', 'symbol': 'NXT/IDR', 'base': 'NXT', 'quote': 'IDR', 'baseId': 'nxt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'OKB/IDR': { 'id': 'okb_idr', 'symbol': 'OKB/IDR', 'base': 'OKB', 'quote': 'IDR', 'baseId': 'okb', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.5, 'max': undefined }}}, + 'ONT/IDR': { 'id': 'ont_idr', 'symbol': 'ONT/IDR', 'base': 'ONT', 'quote': 'IDR', 'baseId': 'ont', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'PXG/IDR': { 'id': 'pxg_idr', 'symbol': 'PXG/IDR', 'base': 'PXG', 'quote': 'IDR', 'baseId': 'pxg', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'QTUM/IDR': { 'id': 'qtum_idr', 'symbol': 'QTUM/IDR', 'base': 'QTUM', 'quote': 'IDR', 'baseId': 'qtum', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'RVN/IDR': { 'id': 'rvn_idr', 'symbol': 'RVN/IDR', 'base': 'RVN', 'quote': 'IDR', 'baseId': 'rvn', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'SSP/IDR': { 'id': 'ssp_idr', 'symbol': 'SSP/IDR', 'base': 'SSP', 'quote': 'IDR', 'baseId': 'ssp', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + 'SUMO/IDR': { 'id': 'sumo_idr', 'symbol': 'SUMO/IDR', 'base': 'SUMO', 'quote': 'IDR', 'baseId': 'sumo', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 5, 'max': undefined }}}, + // 'STQ/IDR': { 'id': 'stq_idr', 'symbol': 'STQ/IDR', 'base': 'STQ', 'quote': 'IDR', 'baseId': 'stq', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'TRX/IDR': { 'id': 'trx_idr', 'symbol': 'TRX/IDR', 'base': 'TRX', 'quote': 'IDR', 'baseId': 'trx', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'USDC/IDR': { 'id': 'usdc_idr', 'symbol': 'USDC/IDR', 'base': 'USDC', 'quote': 'IDR', 'baseId': 'usdc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'USDT/IDR': { 'id': 'usdt_idr', 'symbol': 'USDT/IDR', 'base': 'USDT', 'quote': 'IDR', 'baseId': 'usdt', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'VEX/IDR': { 'id': 'vex_idr', 'symbol': 'VEX/IDR', 'base': 'VEX', 'quote': 'IDR', 'baseId': 'vex', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': undefined, 'max': undefined }}}, + 'VIDY/IDR': { 'id': 'vidy_idr', 'symbol': 'VIDY/IDR', 'base': 'VIDY', 'quote': 'IDR', 'baseId': 'vidy', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 100, 'max': undefined }}}, + 'WAVES/IDR': { 'id': 'waves_idr', 'symbol': 'WAVES/IDR', 'base': 'WAVES', 'quote': 'IDR', 'baseId': 'waves', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'XEM/IDR': { 'id': 'nem_idr', 'symbol': 'XEM/IDR', 'base': 'XEM', 'quote': 'IDR', 'baseId': 'nem', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'XLM/IDR': { 'id': 'str_idr', 'symbol': 'XLM/IDR', 'base': 'XLM', 'quote': 'IDR', 'baseId': 'str', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 20, 'max': undefined }}}, + 'XDCE/IDR': { 'id': 'xdce_idr', 'symbol': 'XDCE/IDR', 'base': 'XDCE', 'quote': 'IDR', 'baseId': 'xdce', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}}, + 'XMR/IDR': { 'id': 'xmr_idr', 'symbol': 'XMR/IDR', 'base': 'XMR', 'quote': 'IDR', 'baseId': 'xmr', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'XRP/IDR': { 'id': 'xrp_idr', 'symbol': 'XRP/IDR', 'base': 'XRP', 'quote': 'IDR', 'baseId': 'xrp', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 10, 'max': undefined }}}, + 'XZC/IDR': { 'id': 'xzc_idr', 'symbol': 'XZC/IDR', 'base': 'XZC', 'quote': 'IDR', 'baseId': 'xzc', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'VSYS/IDR': { 'id': 'vsys_idr', 'symbol': 'VSYS/IDR', 'base': 'VSYS', 'quote': 'IDR', 'baseId': 'vsys', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.1, 'max': undefined }}}, + 'ZEC/IDR': { 'id': 'zec_idr', 'symbol': 'ZEC/IDR', 'base': 'ZEC', 'quote': 'IDR', 'baseId': 'zec', 'quoteId': 'idr', 'precision': { 'amount': 8, 'price': 0 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + // BTC markets + 'BTS/BTC': { 'id': 'bts_btc', 'symbol': 'BTS/BTC', 'base': 'BTS', 'quote': 'BTC', 'baseId': 'bts', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DASH/BTC': { 'id': 'drk_btc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC', 'baseId': 'drk', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 6 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'DOGE/BTC': { 'id': 'doge_btc', 'symbol': 'DOGE/BTC', 'base': 'DOGE', 'quote': 'BTC', 'baseId': 'doge', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 5 }, 'limits': { 'amount': { 'min': 0.001, 'max': undefined }}}, + 'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 6 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'NXT/BTC': { 'id': 'nxt_btc', 'symbol': 'NXT/BTC', 'base': 'NXT', 'quote': 'BTC', 'baseId': 'nxt', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'SUMO/BTC': { 'id': 'sumo_btc', 'symbol': 'SUMO/BTC', 'base': 'SUMO', 'quote': 'BTC', 'baseId': 'sumo', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'TEN/BTC': { 'id': 'ten_btc', 'symbol': 'TEN/BTC', 'base': 'TEN', 'quote': 'BTC', 'baseId': 'ten', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'XEM/BTC': { 'id': 'nem_btc', 'symbol': 'XEM/BTC', 'base': 'XEM', 'quote': 'BTC', 'baseId': 'nem', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 1, 'max': undefined }}}, + 'XLM/BTC': { 'id': 'str_btc', 'symbol': 'XLM/BTC', 'base': 'XLM', 'quote': 'BTC', 'baseId': 'str', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + 'XRP/BTC': { 'id': 'xrp_btc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc', 'precision': { 'amount': 8, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': undefined }}}, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0, + 'taker': 0.003, + }, + }, + 'exceptions': { + 'exact': { + 'invalid_pair': BadSymbol, // {"error":"invalid_pair","error_description":"Invalid Pair"} + 'Insufficient balance.': InsufficientFunds, + 'invalid order.': OrderNotFound, + 'Invalid credentials. API not found or session has expired.': AuthenticationError, + 'Invalid credentials. Bad sign.': AuthenticationError, + }, + 'broad': { + 'Minimum price': InvalidOrder, + 'Minimum order': InvalidOrder, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfo (params); + const balances = this.safeValue (response, 'return', {}); + const free = this.safeValue (balances, 'balance', {}); + const used = this.safeValue (balances, 'balance_hold', {}); + const result = { 'info': response }; + const currencyIds = Object.keys (free); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['used'] = this.safeFloat (used, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const orderbook = await this.publicGetPairDepth (this.extend (request, params)); + return this.parseOrderBook (orderbook, undefined, 'buy', 'sell'); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTicker (this.extend (request, params)); + // + // { + // "ticker": { + // "high":"0.01951", + // "low":"0.01877", + // "vol_eth":"39.38839319", + // "vol_btc":"0.75320886", + // "last":"0.01896", + // "buy":"0.01896", + // "sell":"0.019", + // "server_time":1565248908 + // } + // } + // + const ticker = response['ticker']; + const timestamp = this.safeTimestamp (ticker, 'server_time'); + const baseVolume = 'vol_' + market['baseId'].toLowerCase (); + const quoteVolume = 'vol_' + market['quoteId'].toLowerCase (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, baseVolume), + 'quoteVolume': this.safeFloat (ticker, quoteVolume), + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetPairTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + let side = undefined; + if ('type' in order) { + side = order['type']; + } + let status = this.safeString (order, 'status', 'open'); + if (status === 'filled') { + status = 'closed'; + } else if (status === 'cancelled') { + status = 'canceled'; + } + let symbol = undefined; + let cost = undefined; + const price = this.safeFloat (order, 'price'); + let amount = undefined; + let remaining = undefined; + let filled = undefined; + if (market !== undefined) { + symbol = market['symbol']; + let quoteId = market['quoteId']; + let baseId = market['baseId']; + if ((market['quoteId'] === 'idr') && ('order_rp' in order)) { + quoteId = 'rp'; + } + if ((market['baseId'] === 'idr') && ('remain_rp' in order)) { + baseId = 'rp'; + } + cost = this.safeFloat (order, 'order_' + quoteId); + if (cost) { + amount = cost / price; + const remainingCost = this.safeFloat (order, 'remain_' + quoteId); + if (remainingCost !== undefined) { + remaining = remainingCost / price; + filled = amount - remaining; + } + } else { + amount = this.safeFloat (order, 'order_' + baseId); + cost = price * amount; + remaining = this.safeFloat (order, 'remain_' + baseId); + filled = amount - remaining; + } + } + let average = undefined; + if (filled) { + average = cost / filled; + } + const timestamp = this.safeInteger (order, 'submit_time'); + const fee = undefined; + const id = this.safeString (order, 'order_id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrder requires a symbol'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'order_id': id, + }; + const response = await this.privatePostGetOrder (this.extend (request, params)); + const orders = response['return']; + const order = this.parseOrder (this.extend ({ 'id': id }, orders['order']), market); + return this.extend ({ 'info': response }, order); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostOpenOrders (this.extend (request, params)); + const rawOrders = response['return']['orders']; + // { success: 1, return: { orders: null }} if no orders + if (!rawOrders) { + return []; + } + // { success: 1, return: { orders: [ ... objects ] }} for orders fetched by symbol + if (symbol !== undefined) { + return this.parseOrders (rawOrders, market, since, limit); + } + // { success: 1, return: { orders: { marketid: [ ... objects ] }}} if all orders are fetched + const marketIds = Object.keys (rawOrders); + let exchangeOrders = []; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const marketOrders = rawOrders[marketId]; + market = this.markets_by_id[marketId]; + const parsedOrders = this.parseOrders (marketOrders, market, since, limit); + exchangeOrders = this.arrayConcat (exchangeOrders, parsedOrders); + } + return exchangeOrders; + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrders requires a symbol'); + } + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostOrderHistory (this.extend (request, params)); + let orders = this.parseOrders (response['return']['orders'], market, since, limit); + orders = this.filterBy (orders, 'status', 'closed'); + if (symbol !== undefined) { + return this.filterBySymbol (orders, symbol); + } + return orders; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'price': price, + }; + const currency = market['baseId']; + if (side === 'buy') { + request[market['quoteId']] = amount * price; + } else { + request[market['baseId']] = amount; + } + request[currency] = amount; + const result = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': result, + 'id': result['return']['order_id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + const side = this.safeValue (params, 'side'); + if (side === undefined) { + throw new ExchangeError (this.id + ' cancelOrder requires an extra "side" param'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'pair': market['id'], + 'type': side, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // Custom string you need to provide to identify each withdrawal. + // Will be passed to callback URL (assigned via website to the API key) + // so your system can identify the request and confirm it. + // Alphanumeric, max length 255. + const requestId = this.milliseconds (); + // Alternatively: + // let requestId = this.uuid (); + const request = { + 'currency': currency['id'], + 'withdraw_amount': amount, + 'withdraw_address': address, + 'request_id': requestId.toString (), + }; + if (tag) { + request['withdraw_memo'] = tag; + } + const response = await this.privatePostWithdrawCoin (this.extend (request, params)); + // + // { + // "success": 1, + // "status": "approved", + // "withdraw_currency": "xrp", + // "withdraw_address": "rwWr7KUZ3ZFwzgaDGjKBysADByzxvohQ3C", + // "withdraw_amount": "10000.00000000", + // "fee": "2.00000000", + // "amount_after_fee": "9998.00000000", + // "submit_time": "1509469200", + // "withdraw_id": "xrp-12345", + // "txid": "", + // "withdraw_memo": "123123" + // } + // + let id = undefined; + if (('txid' in response) && (response['txid'].length > 0)) { + id = response['txid']; + } + return { + 'info': response, + 'id': id, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api === 'public') { + url += '/' + this.implodeParams (path, params); + } else { + this.checkRequiredCredentials (); + body = this.urlencode (this.extend ({ + 'method': path, + 'nonce': this.nonce (), + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // { success: 0, error: "invalid order." } + // or + // [{ data, ... }, { ... }, ... ] + if (Array.isArray (response)) { + return; // public endpoints may return []-arrays + } + const error = this.safeValue (response, 'error', ''); + if (!('success' in response) && error === '') { + return; // no 'success' property on public responses + } + if (this.safeInteger (response, 'success', 0) === 1) { + // { success: 1, return: { orders: [] }} + if (!('return' in response)) { + throw new ExchangeError (this.id + ': malformed response: ' + this.json (response)); + } else { + return; + } + } + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown message + } +}; diff --git a/TaskServer/node_modules/ccxt/js/itbit.js b/TaskServer/node_modules/ccxt/js/itbit.js new file mode 100644 index 000000000..192fa145d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/itbit.js @@ -0,0 +1,600 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class itbit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'itbit', + 'name': 'itBit', + 'countries': [ 'US' ], + 'rateLimit': 2000, + 'version': 'v1', + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchMyTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27822159-66153620-60ad-11e7-89e7-005f6d7f3de0.jpg', + 'api': 'https://api.itbit.com', + 'www': 'https://www.itbit.com', + 'doc': [ + 'https://api.itbit.com/docs', + 'https://www.itbit.com/api', + ], + }, + 'api': { + 'public': { + 'get': [ + 'markets/{symbol}/ticker', + 'markets/{symbol}/order_book', + 'markets/{symbol}/trades', + ], + }, + 'private': { + 'get': [ + 'wallets', + 'wallets/{walletId}', + 'wallets/{walletId}/balances/{currencyCode}', + 'wallets/{walletId}/funding_history', + 'wallets/{walletId}/trades', + 'wallets/{walletId}/orders', + 'wallets/{walletId}/orders/{id}', + ], + 'post': [ + 'wallet_transfers', + 'wallets', + 'wallets/{walletId}/cryptocurrency_deposits', + 'wallets/{walletId}/cryptocurrency_withdrawals', + 'wallets/{walletId}/orders', + 'wire_withdrawal', + ], + 'delete': [ + 'wallets/{walletId}/orders/{id}', + ], + }, + }, + 'markets': { + 'BTC/USD': { 'id': 'XBTUSD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD' }, + 'BTC/SGD': { 'id': 'XBTSGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD' }, + 'BTC/EUR': { 'id': 'XBTEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR' }, + 'ETH/USD': { 'id': 'ETHUSD', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD' }, + 'ETH/EUR': { 'id': 'ETHEUR', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR' }, + 'ETH/SGD': { 'id': 'ETHSGD', 'symbol': 'ETH/SGD', 'base': 'ETH', 'quote': 'SGD' }, + }, + 'fees': { + 'trading': { + 'maker': -0.03 / 100, + 'taker': 0.35 / 100, + }, + }, + 'commonCurrencies': { + 'XBT': 'BTC', + }, + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const orderbook = await this.publicGetMarketsSymbolOrderBook (this.extend (request, params)); + return this.parseOrderBook (orderbook); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const ticker = await this.publicGetMarketsSymbolTicker (this.extend (request, params)); + const serverTimeUTC = this.safeString (ticker, 'serverTimeUTC'); + if (!serverTimeUTC) { + throw new ExchangeError (this.id + ' fetchTicker returned a bad response: ' + this.json (ticker)); + } + const timestamp = this.parse8601 (serverTimeUTC); + const vwap = this.safeFloat (ticker, 'vwap24h'); + const baseVolume = this.safeFloat (ticker, 'volume24h'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high24h'), + 'low': this.safeFloat (ticker, 'low24h'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'openToday'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // timestamp: "2015-05-22T17:45:34.7570000Z", + // matchNumber: "5CR1JEUBBM8J", + // price: "351.45000000", + // amount: "0.00010000" + // } + // + // fetchMyTrades (private) + // + // { + // "orderId": "248ffda4-83a0-4033-a5bb-8929d523f59f", + // "timestamp": "2015-05-11T14:48:01.9870000Z", + // "instrument": "XBTUSD", + // "direction": "buy", // buy or sell + // "currency1": "XBT", // base currency + // "currency1Amount": "0.00010000", // order amount in base currency + // "currency2": "USD", // quote currency + // "currency2Amount": "0.0250530000000000", // order cost in quote currency + // "rate": "250.53000000", + // "commissionPaid": "0.00000000", // net trade fee paid after using any available rebate balance + // "commissionCurrency": "USD", + // "rebatesApplied": "-0.000125265", // negative values represent amount of rebate balance used for trades removing liquidity from order book; positive values represent amount of rebate balance earned from trades adding liquidity to order book + // "rebateCurrency": "USD", + // "executionId": "23132" + // } + // + const id = this.safeString2 (trade, 'executionId', 'matchNumber'); + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const side = this.safeString (trade, 'direction'); + const orderId = this.safeString (trade, 'orderId'); + let feeCost = this.safeFloat (trade, 'commissionPaid'); + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + let rebatesApplied = this.safeFloat (trade, 'rebatesApplied'); + if (rebatesApplied !== undefined) { + rebatesApplied = -rebatesApplied; + } + const rebateCurrencyId = this.safeString (trade, 'rebateCurrency'); + const rebateCurrency = this.safeCurrencyCode (rebateCurrencyId); + const price = this.safeFloat2 (trade, 'price', 'rate'); + const amount = this.safeFloat2 (trade, 'currency1Amount', 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + const marketId = this.safeString (trade, 'instrument'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (trade, 'currency1'); + const quoteId = this.safeString (trade, 'currency2'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const result = { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + }; + if (feeCost !== undefined) { + if (rebatesApplied !== undefined) { + if (feeCurrency === rebateCurrency) { + feeCost = this.sum (feeCost, rebatesApplied); + result['fee'] = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } else { + result['fees'] = [ + { + 'cost': feeCost, + 'currency': feeCurrency, + }, + { + 'cost': rebatesApplied, + 'currency': rebateCurrency, + }, + ]; + } + } else { + result['fee'] = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + } + if (!('fee' in result)) { + if (!('fees' in result)) { + result['fee'] = undefined; + } + } + return result; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const walletId = this.safeString (params, 'walletId'); + if (walletId === undefined) { + throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter'); + } + const request = { + 'walletId': walletId, + }; + if (limit !== undefined) { + request['perPage'] = limit; // default 50, max 50 + } + const response = await this.privateGetWalletsWalletIdFundingHistory (this.extend (request, params)); + // { bankName: 'USBC (usd)', + // withdrawalId: 94740, + // holdingPeriodCompletionDate: '2018-04-16T07:57:05.9606869', + // time: '2018-04-16T07:57:05.9600000', + // currency: 'USD', + // transactionType: 'Withdrawal', + // amount: '2186.72000000', + // walletName: 'Wallet', + // status: 'completed' }, + // + // { "time": "2018-01-02T19:52:22.4176503", + // "amount": "0.50000000", + // "status": "completed", + // "txnHash": "1b6fff67ed83cb9e9a38ca4976981fc047322bc088430508fe764a127d3ace95", + // "currency": "XBT", + // "walletName": "Wallet", + // "transactionType": "Deposit", + // "destinationAddress": "3AAWTH9et4e8o51YKp9qPpmujrNXKwHWNX"} + const items = response['fundingHistory']; + const result = []; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const time = this.safeString (item, 'time'); + const timestamp = this.parse8601 (time); + const currency = this.safeString (item, 'currency'); + const destinationAddress = this.safeString (item, 'destinationAddress'); + const txnHash = this.safeString (item, 'txnHash'); + const transactionType = this.safeStringLower (item, 'transactionType'); + const transactionStatus = this.safeString (item, 'status'); + const status = this.parseTransferStatus (transactionStatus); + result.push ({ + 'id': this.safeString (item, 'withdrawalId'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'currency': this.safeCurrencyCode (currency), + 'address': destinationAddress, + 'tag': undefined, + 'txid': txnHash, + 'type': transactionType, + 'status': status, + 'amount': this.safeFloat (item, 'amount'), + 'fee': undefined, + 'info': item, + }); + } + return result; + } + + parseTransferStatus (status) { + const options = { + 'cancelled': 'canceled', + 'completed': 'ok', + }; + return this.safeString (options, status, 'pending'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const walletId = this.safeString (params, 'walletId'); + if (walletId === undefined) { + throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter'); + } + const request = { + 'walletId': walletId, + }; + if (since !== undefined) { + request['rangeStart'] = this.ymdhms (since, 'T'); + } + if (limit !== undefined) { + request['perPage'] = limit; // default 50, max 50 + } + const response = await this.privateGetWalletsWalletIdTrades (this.extend (request, params)); + // + // { + // "totalNumberOfRecords": "2", + // "currentPageNumber": "1", + // "latestExecutionId": "332", // most recent execution at time of response + // "recordsPerPage": "50", + // "tradingHistory": [ + // { + // "orderId": "248ffda4-83a0-4033-a5bb-8929d523f59f", + // "timestamp": "2015-05-11T14:48:01.9870000Z", + // "instrument": "XBTUSD", + // "direction": "buy", // buy or sell + // "currency1": "XBT", // base currency + // "currency1Amount": "0.00010000", // order amount in base currency + // "currency2": "USD", // quote currency + // "currency2Amount": "0.0250530000000000", // order cost in quote currency + // "rate": "250.53000000", + // "commissionPaid": "0.00000000", // net trade fee paid after using any available rebate balance + // "commissionCurrency": "USD", + // "rebatesApplied": "-0.000125265", // negative values represent amount of rebate balance used for trades removing liquidity from order book; positive values represent amount of rebate balance earned from trades adding liquidity to order book + // "rebateCurrency": "USD", + // "executionId": "23132" + // }, + // ], + // } + // + const trades = this.safeValue (response, 'tradingHistory', []); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseTrades (trades, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetMarketsSymbolTrades (this.extend (request, params)); + // + // { + // count: 3, + // recentTrades: [ + // { + // timestamp: "2015-05-22T17:45:34.7570000Z", + // matchNumber: "5CR1JEUBBM8J", + // price: "351.45000000", + // amount: "0.00010000" + // }, + // ] + // } + // + const trades = this.safeValue (response, 'recentTrades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.fetchWallets (params); + const balances = response[0]['balances']; + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'availableBalance'); + account['total'] = this.safeFloat (balance, 'totalBalance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchWallets (params = {}) { + await this.loadMarkets (); + if (!this.uid) { + throw new AuthenticationError (this.id + ' fetchWallets requires uid API credential'); + } + const request = { + 'userId': this.uid, + }; + return await this.privateGetWallets (this.extend (request, params)); + } + + async fetchWallet (walletId, params = {}) { + await this.loadMarkets (); + const request = { + 'walletId': walletId, + }; + return await this.privateGetWalletsWalletId (this.extend (request, params)); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'open', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'filled', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' fetchOrders requires a walletId parameter'); + } + const walletId = params['walletId']; + const request = { + 'walletId': walletId, + }; + const response = await this.privateGetWalletsWalletIdOrders (this.extend (request, params)); + return this.parseOrders (response, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'submitted': 'open', // order pending book entry + 'open': 'open', + 'filled': 'closed', + 'cancelled': 'canceled', + 'rejected': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const side = order['side']; + const type = order['type']; + const symbol = this.markets_by_id[order['instrument']]['symbol']; + const timestamp = this.parse8601 (order['createdTime']); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'amountFilled'); + const remaining = amount - filled; + const fee = undefined; + const price = this.safeFloat (order, 'price'); + const average = this.safeFloat (order, 'volumeWeightedAveragePrice'); + const cost = filled * average; + return { + 'id': order['id'], + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': this.parseOrderStatus (this.safeString (order, 'status')), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': fee, + // 'trades': this.parseTrades (order['trades'], market), + }; + } + + nonce () { + return this.milliseconds (); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' createOrder requires a walletId parameter'); + } + amount = amount.toString (); + price = price.toString (); + const market = this.market (symbol); + const request = { + 'side': side, + 'type': type, + 'currency': market['id'].replace (market['quote'], ''), + 'amount': amount, + 'display': amount, + 'price': price, + 'instrument': market['id'], + }; + const response = await this.privatePostWalletsWalletIdOrders (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' fetchOrder requires a walletId parameter'); + } + const request = { + 'id': id, + }; + const response = await this.privateGetWalletsWalletIdOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const walletIdInParams = ('walletId' in params); + if (!walletIdInParams) { + throw new ExchangeError (this.id + ' cancelOrder requires a walletId parameter'); + } + const request = { + 'id': id, + }; + return await this.privateDeleteWalletsWalletIdOrdersId (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method === 'GET' && Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + if (method === 'POST' && Object.keys (query).length) { + body = this.json (query); + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const timestamp = nonce; + const authBody = (method === 'POST') ? body : ''; + const auth = [ method, url, authBody, nonce, timestamp ]; + const message = nonce + this.json (auth).replace ('\\/', '/'); + const hash = this.hash (this.encode (message), 'sha256', 'binary'); + const binaryUrl = this.stringToBinary (this.encode (url)); + const binhash = this.binaryConcat (binaryUrl, hash); + const signature = this.hmac (binhash, this.encode (this.secret), 'sha512', 'base64'); + headers = { + 'Authorization': this.apiKey + ':' + this.decode (signature), + 'Content-Type': 'application/json', + 'X-Auth-Timestamp': timestamp, + 'X-Auth-Nonce': nonce, + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('code' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/kkex.js b/TaskServer/node_modules/ccxt/js/kkex.js new file mode 100644 index 000000000..58b799e95 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/kkex.js @@ -0,0 +1,566 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, InvalidOrder, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class kkex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kkex', + 'name': 'KKEX', + 'countries': [ 'CN', 'US', 'JP' ], + 'version': 'v2', + 'has': { + 'CORS': false, + 'fetchBalance': true, + 'fetchTickers': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'createMarketOrder': true, + 'fetchOrder': true, + }, + 'timeframes': { + '1m': '1min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '4h': '4hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + '1M': '1month', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/47401462-2e59f800-d74a-11e8-814f-e4ae17b4968a.jpg', + 'api': { + 'public': 'https://kkex.com/api/v1', + 'private': 'https://kkex.com/api/v2', + 'v1': 'https://kkex.com/api/v1', + }, + 'www': 'https://kkex.com', + 'doc': 'https://kkex.com/api_wiki/cn/', + 'fees': 'https://intercom.help/kkex/fee', + }, + 'api': { + 'public': { + 'get': [ + 'exchange_rate', + 'products', + 'assets', + 'tickers', + 'ticker', + 'depth', + 'trades', + 'kline', + ], + }, + 'private': { + 'post': [ + 'profile', + 'trade', + 'batch_trade', + 'cancel_order', + 'cancel_all_orders', + 'order_history', + 'userinfo', + 'order_info', + 'orders_info', + ], + }, + 'v1': { + 'post': [ + 'process_strategy', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.002, + 'maker': 0.002, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'options': { + 'lastNonceTimestamp': 0, + }, + }); + } + + async fetchMarkets (params = {}) { + let tickers = await this.publicGetTickers (params); + tickers = tickers['tickers']; + let products = await this.publicGetProducts (params); + products = products['products']; + const markets = []; + for (let k = 0; k < tickers.length; k++) { + const keys = Object.keys (tickers[k]); + markets.push (keys[0]); + } + const result = []; + for (let i = 0; i < markets.length; i++) { + const id = markets[i]; + const market = markets[i]; + let baseId = ''; + let quoteId = ''; + let precision = {}; + let limits = {}; + for (let j = 0; j < products.length; j++) { + const p = products[j]; + if (p['mark_asset'] + p['base_asset'] === market) { + quoteId = p['base_asset']; + baseId = p['mark_asset']; + const price_scale_str = p['price_scale'].toString (); + const scale = price_scale_str.length - 1; + precision = { + 'price': scale, + 'amount': scale, + }; + limits = { + 'amount': { + 'min': Math.max (this.safeFloat (p, 'min_bid_size'), this.safeFloat (p, 'min_ask_size')), + 'max': Math.min (this.safeFloat (p, 'max_bid_size'), this.safeFloat (p, 'max_ask_size')), + }, + 'price': { + 'min': this.safeFloat (p, 'min_price'), + 'max': this.safeFloat (p, 'max_price'), + }, + }; + limits['cost'] = { + 'min': this.safeFloat (p, 'min_bid_amount'), + 'max': this.safeFloat (p, 'max_bid_amount'), + }; + } + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.markets[symbol]; + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.extend (response['ticker'], this.omit (response, 'ticker')); + return this.parseTicker (ticker, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + // + // { date: 1540350657, + // tickers: [ { ENUBTC: { sell: "0.00000256", + // buy: "0.00000253", + // last: "0.00000253", + // vol: "138686.828804", + // high: "0.00000278", + // low: "0.00000253", + // open: "0.0000027" } }, + // { ENUEOS: { sell: "0.00335", + // buy: "0.002702", + // last: "0.0034", + // vol: "15084.9", + // high: "0.0034", + // low: "0.003189", + // open: "0.003189" } } ], + // result: true } + // + const tickers = this.safeValue (response, 'tickers'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ids = Object.keys (tickers[i]); + const id = ids[0]; + const market = this.safeValue (this.markets_by_id, id); + if (market !== undefined) { + const symbol = market['symbol']; + const ticker = this.extend (tickers[i][id], this.omit (response, 'tickers')); + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeInteger (trade, 'date_ms'); + const datetime = this.iso8601 (timestamp); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': datetime, + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserinfo (params); + const balances = this.safeValue (response, 'info'); + const result = { 'info': response }; + const funds = this.safeValue (balances, 'funds'); + const free = this.safeValue (funds, 'free', {}); + const freezed = this.safeValue (funds, 'freezed', {}); + const currencyIds = Object.keys (free); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['used'] = this.safeFloat (freezed, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (!symbol) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'symbol': market['id'], + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + if (response['result']) { + return this.parseOrder (response['order'], market); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + parseInt (ohlcv[0]), + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'type': this.timeframes[timeframe], + }; + if (since !== undefined) { + // since = this.milliseconds () - this.parseTimeframe (timeframe) * limit * 1000; + request['since'] = parseInt (since / 1000); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetKline (this.extend (request, params)); + // + // [ + // [ + // "1521072000000", + // "0.000002", + // "0.00003", + // "0.000002", + // "0.00003", + // "3.106889" + // ], + // [ + // "1517356800000", + // "0.1", + // "0.1", + // "0.00000013", + // "0.000001", + // "542832.83114" + // ] + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + '-1': 'canceled', + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let side = this.safeString (order, 'side'); + if (side === undefined) { + side = this.safeString (order, 'type'); + } + const timestamp = this.safeInteger (order, 'create_date'); + const id = this.safeString2 (order, 'order_id', 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'deal_amount'); + let average = this.safeFloat (order, 'avg_price'); + average = this.safeFloat (order, 'price_avg', average); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + if (average !== undefined) { + cost = average * filled; + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'average': average, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'type': side, + }; + if (type === 'market') { + // for market buy it requires the amount of quote currency to spend + if (side === 'buy') { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + request['amount'] = this.costToPrecision (symbol, parseFloat (amount) * parseFloat (price)); + } + } + request['price'] = this.costToPrecision (symbol, amount); + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + } + request['type'] += '_' + type; + } else { + request['amount'] = this.amountToPrecision (symbol, amount); + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostTrade (this.extend (request, params)); + const id = this.safeString (response, 'order_id'); + return { + 'info': response, + 'id': id, + 'datetime': undefined, + 'timestamp': undefined, + 'lastTradeTimestamp': undefined, + 'status': 'open', + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': undefined, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'order_id': id, + 'symbol': market['id'], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['page_length'] = limit; // 20 by default + } + const response = await this.privatePostOrderHistory (this.extend (request, params)); + return this.parseOrders (response['orders'], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 0, + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 1, + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + path; + if (api === 'public') { + url += '?' + this.urlencode (params); + headers = { 'Content-Type': 'application/json' }; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + let signature = this.extend ({ + 'nonce': nonce, + 'api_key': this.apiKey, + }, params); + signature = this.urlencode (this.keysort (signature)); + signature += '&secret_key=' + this.secret; + signature = this.hash (this.encode (signature), 'md5'); + signature = signature.toUpperCase (); + body = this.extend ({ + 'api_key': this.apiKey, + 'sign': signature, + 'nonce': nonce, + }, params); + body = this.urlencode (body); + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/kraken.js b/TaskServer/node_modules/ccxt/js/kraken.js new file mode 100644 index 000000000..51505f672 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/kraken.js @@ -0,0 +1,1431 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeNotAvailable, ArgumentsRequired, PermissionDenied, AuthenticationError, ExchangeError, OrderNotFound, DDoSProtection, InvalidNonce, InsufficientFunds, CancelPending, InvalidOrder, InvalidAddress, NotSupported } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class kraken extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kraken', + 'name': 'Kraken', + 'countries': [ 'US' ], + 'version': '0', + 'rateLimit': 3000, + 'certified': true, + 'has': { + 'createDepositAddress': true, + 'fetchDepositAddress': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'CORS': false, + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchWithdrawals': true, + 'fetchDeposits': true, + 'withdraw': true, + 'fetchLedgerEntry': true, + 'fetchLedger': true, + }, + 'marketsByAltname': {}, + 'timeframes': { + '1m': 1, + '5m': 5, + '15m': 15, + '30m': 30, + '1h': 60, + '4h': 240, + '1d': 1440, + '1w': 10080, + '2w': 21600, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766599-22709304-5ede-11e7-9de1-9f33732e1509.jpg', + 'api': { + 'public': 'https://api.kraken.com', + 'private': 'https://api.kraken.com', + 'zendesk': 'https://support.kraken.com/hc/en-us/articles', + }, + 'www': 'https://www.kraken.com', + 'doc': 'https://www.kraken.com/features/api', + 'fees': 'https://www.kraken.com/en-us/features/fee-schedule', + }, + 'fees': { + 'trading': { + 'tierBased': true, + 'percentage': true, + 'taker': 0.26 / 100, + 'maker': 0.16 / 100, + 'tiers': { + 'taker': [ + [0, 0.0026], + [50000, 0.0024], + [100000, 0.0022], + [250000, 0.0020], + [500000, 0.0018], + [1000000, 0.0016], + [2500000, 0.0014], + [5000000, 0.0012], + [10000000, 0.0001], + ], + 'maker': [ + [0, 0.0016], + [50000, 0.0014], + [100000, 0.0012], + [250000, 0.0010], + [500000, 0.0008], + [1000000, 0.0006], + [2500000, 0.0004], + [5000000, 0.0002], + [10000000, 0.0], + ], + }, + }, + // this is a bad way of hardcoding fees that change on daily basis + // hardcoding is now considered obsolete, we will remove all of it eventually + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + 'ETH': 0.005, + 'XRP': 0.02, + 'XLM': 0.00002, + 'LTC': 0.02, + 'DOGE': 2, + 'ZEC': 0.00010, + 'ICN': 0.02, + 'REP': 0.01, + 'ETC': 0.005, + 'MLN': 0.003, + 'XMR': 0.05, + 'DASH': 0.005, + 'GNO': 0.01, + 'EOS': 0.5, + 'BCH': 0.001, + 'XTZ': 0.05, + 'USD': 5, // if domestic wire + 'EUR': 5, // if domestic wire + 'CAD': 10, // CAD EFT Withdrawal + 'JPY': 300, // if domestic wire + }, + 'deposit': { + 'BTC': 0, + 'ETH': 0, + 'XRP': 0, + 'XLM': 0, + 'LTC': 0, + 'DOGE': 0, + 'ZEC': 0, + 'ICN': 0, + 'REP': 0, + 'ETC': 0, + 'MLN': 0, + 'XMR': 0, + 'DASH': 0, + 'GNO': 0, + 'EOS': 0, + 'BCH': 0, + 'XTZ': 0.05, + 'USD': 5, // if domestic wire + 'EUR': 0, // free deposit if EUR SEPA Deposit + 'CAD': 5, // if domestic wire + 'JPY': 0, // Domestic Deposit (Free, ¥5,000 deposit minimum) + }, + }, + }, + 'api': { + 'zendesk': { + 'get': [ + // we should really refrain from putting fixed fee numbers and stop hardcoding + // we will be using their web APIs to scrape all numbers from these articles + '205893708-What-is-the-minimum-order-size-', + '201396777-What-are-the-deposit-fees-', + '201893608-What-are-the-withdrawal-fees-', + ], + }, + 'public': { + 'get': [ + 'Assets', + 'AssetPairs', + 'Depth', + 'OHLC', + 'Spread', + 'Ticker', + 'Time', + 'Trades', + ], + }, + 'private': { + 'post': [ + 'AddOrder', + 'AddExport', + 'Balance', + 'CancelOrder', + 'ClosedOrders', + 'DepositAddresses', + 'DepositMethods', + 'DepositStatus', + 'ExportStatus', + 'GetWebSocketsToken', + 'Ledgers', + 'OpenOrders', + 'OpenPositions', + 'QueryLedgers', + 'QueryOrders', + 'QueryTrades', + 'RetrieveExport', + 'RemoveExport', + 'TradeBalance', + 'TradesHistory', + 'TradeVolume', + 'Withdraw', + 'WithdrawCancel', + 'WithdrawInfo', + 'WithdrawStatus', + ], + }, + }, + 'commonCurrencies': { + 'XBT': 'BTC', + 'XDG': 'DOGE', + }, + 'options': { + 'cacheDepositMethodsOnFetchDepositAddress': true, // will issue up to two calls in fetchDepositAddress + 'depositMethods': {}, + 'delistedMarketsById': {}, + // cannot withdraw/deposit these + 'inactiveCurrencies': [ 'CAD', 'USD', 'JPY', 'GBP' ], + }, + 'exceptions': { + 'EQuery:Invalid asset pair': BadSymbol, // {"error":["EQuery:Invalid asset pair"]} + 'EAPI:Invalid key': AuthenticationError, + 'EFunding:Unknown withdraw key': ExchangeError, + 'EFunding:Invalid amount': InsufficientFunds, + 'EService:Unavailable': ExchangeNotAvailable, + 'EDatabase:Internal error': ExchangeNotAvailable, + 'EService:Busy': ExchangeNotAvailable, + 'EQuery:Unknown asset': ExchangeError, + 'EAPI:Rate limit exceeded': DDoSProtection, + 'EOrder:Rate limit exceeded': DDoSProtection, + 'EGeneral:Internal error': ExchangeNotAvailable, + 'EGeneral:Temporary lockout': DDoSProtection, + 'EGeneral:Permission denied': PermissionDenied, + }, + }); + } + + costToPrecision (symbol, cost) { + return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['price'], DECIMAL_PLACES); + } + + feeToPrecision (symbol, fee) { + return this.decimalToPrecision (fee, TRUNCATE, this.markets[symbol]['precision']['amount'], DECIMAL_PLACES); + } + + async fetchMinOrderAmounts () { + const html = await this.zendeskGet205893708WhatIsTheMinimumOrderSize (); + const parts = html.split (''); + const numParts = parts.length; + if (numParts < 3) { + throw new NotSupported (this.id + ' fetchMinOrderAmounts HTML page markup has changed: https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size-'); + } + const result = {}; + // skip the part before the header and the header itself + for (let i = 2; i < parts.length; i++) { + const part = parts[i]; + const chunks = part.split (''); + const amountAndCode = chunks[0]; + if (amountAndCode !== 'To Be Announced') { + const pieces = amountAndCode.split (' '); + const numPieces = pieces.length; + if (numPieces === 2) { + const amount = parseFloat (pieces[0]); + const code = this.safeCurrencyCode (pieces[1]); + result[code] = amount; + } + } + } + return result; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAssetPairs (params); + const limits = await this.fetchMinOrderAmounts (); + const keys = Object.keys (response['result']); + let result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = response['result'][id]; + const baseId = market['base']; + const quoteId = market['quote']; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const darkpool = id.indexOf ('.d') >= 0; + const symbol = darkpool ? market['altname'] : (base + '/' + quote); + let maker = undefined; + if ('fees_maker' in market) { + maker = parseFloat (market['fees_maker'][0][1]) / 100; + } + const precision = { + 'amount': market['lot_decimals'], + 'price': market['pair_decimals'], + }; + let minAmount = Math.pow (10, -precision['amount']); + if (base in limits) { + minAmount = limits[base]; + } + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'darkpool': darkpool, + 'info': market, + 'altname': market['altname'], + 'maker': maker, + 'taker': parseFloat (market['fees'][0][1]) / 100, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + }); + } + result = this.appendInactiveMarkets (result); + this.marketsByAltname = this.indexBy (result, 'altname'); + return result; + } + + safeCurrencyCode (currencyId, currency = undefined) { + if (currencyId.length > 3) { + if ((currencyId.indexOf ('X') === 0) || (currencyId.indexOf ('Z') === 0)) { + currencyId = currencyId.slice (1); + } + } + return super.safeCurrencyCode (currencyId, currency); + } + + appendInactiveMarkets (result) { + // result should be an array to append to + const precision = { 'amount': 8, 'price': 8 }; + const costLimits = { 'min': 0, 'max': undefined }; + const priceLimits = { 'min': Math.pow (10, -precision['price']), 'max': undefined }; + const amountLimits = { 'min': Math.pow (10, -precision['amount']), 'max': Math.pow (10, precision['amount']) }; + const limits = { 'amount': amountLimits, 'price': priceLimits, 'cost': costLimits }; + const defaults = { + 'darkpool': false, + 'info': undefined, + 'maker': undefined, + 'taker': undefined, + 'active': false, + 'precision': precision, + 'limits': limits, + }; + const markets = [ + // { 'id': 'XXLMZEUR', 'symbol': 'XLM/EUR', 'base': 'XLM', 'quote': 'EUR', 'altname': 'XLMEUR' }, + ]; + for (let i = 0; i < markets.length; i++) { + result.push (this.extend (defaults, markets[i])); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetAssets (params); + // + // { + // "error": [], + // "result": { + // "ADA": { "aclass": "currency", "altname": "ADA", "decimals": 8, "display_decimals": 6 }, + // "BCH": { "aclass": "currency", "altname": "BCH", "decimals": 10, "display_decimals": 5 }, + // ... + // }, + // } + // + const currencies = this.safeValue (response, 'result'); + const ids = Object.keys (currencies); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = currencies[id]; + // todo: will need to rethink the fees + // see: https://support.kraken.com/hc/en-us/articles/201893608-What-are-the-withdrawal-fees- + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (this.safeString (currency, 'altname')); + const precision = this.safeInteger (currency, 'decimals'); + // assumes all currencies are active except those listed above + const active = !this.inArray (code, this.options['inactiveCurrencies']); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + this.checkRequiredCredentials (); + const response = await this.privatePostTradeVolume (params); + const tradedVolume = this.safeFloat (response['result'], 'volume'); + const tiers = this.fees['trading']['tiers']; + let taker = tiers['taker'][1]; + let maker = tiers['maker'][1]; + for (let i = 0; i < tiers['taker'].length; i++) { + if (tradedVolume >= tiers['taker'][i][0]) { + taker = tiers['taker'][i][1]; + } + } + for (let i = 0; i < tiers['maker'].length; i++) { + if (tradedVolume >= tiers['maker'][i][0]) { + maker = tiers['maker'][i][1]; + } + } + return { + 'info': response, + 'maker': maker, + 'taker': taker, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (market['darkpool']) { + throw new ExchangeError (this.id + ' does not provide an order book for darkpool symbol ' + symbol); + } + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['count'] = limit; // 100 + } + const response = await this.publicGetDepth (this.extend (request, params)); + const orderbook = response['result'][market['id']]; + return this.parseOrderBook (orderbook); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const baseVolume = parseFloat (ticker['v'][1]); + const vwap = parseFloat (ticker['p'][1]); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = parseFloat (ticker['c'][0]); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': parseFloat (ticker['h'][1]), + 'low': parseFloat (ticker['l'][1]), + 'bid': parseFloat (ticker['b'][0]), + 'bidVolume': undefined, + 'ask': parseFloat (ticker['a'][0]), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'o'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + symbols = (symbols === undefined) ? this.symbols : symbols; + const marketIds = []; + for (let i = 0; i < this.symbols.length; i++) { + const symbol = this.symbols[i]; + const market = this.markets[symbol]; + if (market['active'] && !market['darkpool']) { + marketIds.push (market['id']); + } + } + const request = { + 'pair': marketIds.join (','), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const tickers = response['result']; + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + if (this.inArray (symbol, symbols)) { + result[symbol] = this.parseTicker (ticker, market); + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const darkpool = symbol.indexOf ('.d') >= 0; + if (darkpool) { + throw new ExchangeError (this.id + ' does not provide a ticker for darkpool symbol ' + symbol); + } + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = response['result'][market['id']]; + return this.parseTicker (ticker, market); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + parseFloat (ohlcv[1]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[6]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'interval': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['since'] = parseInt ((since - 1) / 1000); + } + const response = await this.publicGetOHLC (this.extend (request, params)); + const ohlcvs = response['result'][market['id']]; + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + parseLedgerEntryType (type) { + const types = { + 'trade': 'trade', + 'withdrawal': 'transaction', + 'deposit': 'transaction', + 'transfer': 'transfer', + 'margin': 'margin', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // { + // 'LTFK7F-N2CUX-PNY4SX': { + // refid: "TSJTGT-DT7WN-GPPQMJ", + // time: 1520102320.555, + // type: "trade", + // aclass: "currency", + // asset: "XETH", + // amount: "0.1087194600", + // fee: "0.0000000000", + // balance: "0.2855851000" + // }, + // ... + // } + // + const id = this.safeString (item, 'id'); + let direction = undefined; + const account = undefined; + const referenceId = this.safeString (item, 'refid'); + const referenceAccount = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const code = this.safeCurrencyCode (this.safeString (item, 'asset'), currency); + let amount = this.safeFloat (item, 'amount'); + if (amount < 0) { + direction = 'out'; + amount = Math.abs (amount); + } else { + direction = 'in'; + } + const time = this.safeFloat (item, 'time'); + let timestamp = undefined; + if (time !== undefined) { + timestamp = parseInt (time * 1000); + } + const fee = { + 'cost': this.safeFloat (item, 'fee'), + 'currency': code, + }; + const before = undefined; + const after = this.safeFloat (item, 'balance'); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'direction': direction, + 'account': account, + 'referenceId': referenceId, + 'referenceAccount': referenceAccount, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, + 'after': after, + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + // https://www.kraken.com/features/api#get-ledgers-info + await this.loadMarkets (); + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['asset'] = currency['id']; + } + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostLedgers (this.extend (request, params)); + // { error: [], + // result: { ledger: { 'LPUAIB-TS774-UKHP7X': { refid: "A2B4HBV-L4MDIE-JU4N3N", + // time: 1520103488.314, + // type: "withdrawal", + // aclass: "currency", + // asset: "XETH", + // amount: "-0.2805800000", + // fee: "0.0050000000", + // balance: "0.0000051000" }, + const result = this.safeValue (response, 'result', {}); + const ledger = this.safeValue (result, 'ledger', {}); + const keys = Object.keys (ledger); + const items = []; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = ledger[key]; + value['id'] = key; + items.push (value); + } + return this.parseLedger (items, currency, since, limit); + } + + async fetchLedgerEntriesByIds (ids, code = undefined, params = {}) { + // https://www.kraken.com/features/api#query-ledgers + await this.loadMarkets (); + ids = ids.join (','); + const request = this.extend ({ + 'id': ids, + }, params); + const response = await this.privatePostQueryLedgers (request); + // { error: [], + // result: { 'LPUAIB-TS774-UKHP7X': { refid: "A2B4HBV-L4MDIE-JU4N3N", + // time: 1520103488.314, + // type: "withdrawal", + // aclass: "currency", + // asset: "XETH", + // amount: "-0.2805800000", + // fee: "0.0050000000", + // balance: "0.0000051000" } } } + const result = response['result']; + const keys = Object.keys (result); + const items = []; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = result[key]; + value['id'] = key; + items.push (value); + } + return this.parseLedger (items); + } + + async fetchLedgerEntry (id, code = undefined, params = {}) { + const items = await this.fetchLedgerEntriesByIds ([ id ], code, params); + return items[0]; + } + + parseTrade (trade, market = undefined) { + let timestamp = undefined; + let side = undefined; + let type = undefined; + let price = undefined; + let amount = undefined; + let id = undefined; + let order = undefined; + let fee = undefined; + const marketId = this.safeString (trade, 'pair'); + const foundMarket = this.findMarketByAltnameOrId (marketId); + let symbol = undefined; + if (foundMarket !== undefined) { + market = foundMarket; + } else if (marketId !== undefined) { + // delisted market ids go here + market = this.getDelistedMarketById (marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + if (Array.isArray (trade)) { + timestamp = parseInt (trade[2] * 1000); + side = (trade[3] === 's') ? 'sell' : 'buy'; + type = (trade[4] === 'l') ? 'limit' : 'market'; + price = parseFloat (trade[0]); + amount = parseFloat (trade[1]); + const tradeLength = trade.length; + if (tradeLength > 6) { + id = trade[6]; // artificially added as per #1794 + } + } else if ('ordertxid' in trade) { + order = trade['ordertxid']; + id = this.safeString2 (trade, 'id', 'postxid'); + timestamp = this.safeTimestamp (trade, 'time'); + side = trade['type']; + type = trade['ordertype']; + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'vol'); + if ('fee' in trade) { + let currency = undefined; + if (market) { + currency = market['quote']; + } + fee = { + 'cost': this.safeFloat (trade, 'fee'), + 'currency': currency, + }; + } + } + return { + 'id': id, + 'order': order, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': price * amount, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const id = market['id']; + const request = { + 'pair': id, + }; + // https://support.kraken.com/hc/en-us/articles/218198197-How-to-pull-all-trade-data-using-the-Kraken-REST-API + // https://github.com/ccxt/ccxt/issues/5677 + if (since !== undefined) { + // php does not format it properly + // therefore we use string concatenation here + request['since'] = since * 1e6; + request['since'] = since.toString () + '000000'; // expected to be in nanoseconds + } + // https://github.com/ccxt/ccxt/issues/5698 + if (limit !== undefined && limit !== 1000) { + const fetchTradesWarning = this.safeValue (this.options, 'fetchTradesWarning', true); + if (fetchTradesWarning) { + throw new ExchangeError (this.id + ' fetchTrades() cannot serve ' + limit.toString () + " trades without breaking the pagination, see https://github.com/ccxt/ccxt/issues/5698 for more details. Set exchange.options['fetchTradesWarning'] to acknowledge this warning and silence it."); + } + } + const response = await this.publicGetTrades (this.extend (request, params)); + // + // { + // "error": [], + // "result": { + // "XETHXXBT": [ + // ["0.032310","4.28169434",1541390792.763,"s","l",""] + // ], + // "last": "1541439421200678657" + // } + // } + // + const result = response['result']; + const trades = result[id]; + // trades is a sorted array: last (most recent trade) goes last + const length = trades.length; + if (length <= 0) { + return []; + } + const lastTrade = trades[length - 1]; + const lastTradeId = this.safeString (result, 'last'); + lastTrade.push (lastTradeId); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + const response = await this.privatePostBalance (params); + const balances = this.safeValue (response, 'result', {}); + const result = { 'info': balances }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balances, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'ordertype': type, + 'volume': this.amountToPrecision (symbol, amount), + }; + const priceIsDefined = (price !== undefined); + const marketOrder = (type === 'market'); + const limitOrder = (type === 'limit'); + const shouldIncludePrice = limitOrder || (!marketOrder && priceIsDefined); + if (shouldIncludePrice) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostAddOrder (this.extend (request, params)); + let id = this.safeValue (response['result'], 'txid'); + if (id !== undefined) { + if (Array.isArray (id)) { + const length = id.length; + id = (length > 1) ? id : id[0]; + } + } + return { + 'id': id, + 'info': response, + 'timestamp': undefined, + 'datetime': undefined, + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': undefined, + 'average': undefined, + 'filled': undefined, + 'remaining': undefined, + 'status': undefined, + 'fee': undefined, + 'trades': undefined, + }; + } + + findMarketByAltnameOrId (id) { + if (id in this.marketsByAltname) { + return this.marketsByAltname[id]; + } else if (id in this.markets_by_id) { + return this.markets_by_id[id]; + } + return undefined; + } + + getDelistedMarketById (id) { + if (id === undefined) { + return id; + } + let market = this.safeValue (this.options['delistedMarketsById'], id); + if (market !== undefined) { + return market; + } + const baseIdStart = 0; + let baseIdEnd = 3; + let quoteIdStart = 3; + let quoteIdEnd = 6; + if (id.length === 8) { + baseIdEnd = 4; + quoteIdStart = 4; + quoteIdEnd = 8; + } else if (id.length === 7) { + baseIdEnd = 4; + quoteIdStart = 4; + quoteIdEnd = 7; + } + const baseId = id.slice (baseIdStart, baseIdEnd); + const quoteId = id.slice (quoteIdStart, quoteIdEnd); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + market = { + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + }; + this.options['delistedMarketsById'][id] = market; + return market; + } + + parseOrderStatus (status) { + const statuses = { + 'pending': 'open', // order pending book entry + 'open': 'open', + 'closed': 'closed', + 'canceled': 'canceled', + 'expired': 'expired', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const description = this.safeValue (order, 'descr', {}); + const side = this.safeString (description, 'type'); + const type = this.safeString (description, 'ordertype'); + const marketId = this.safeString (description, 'pair'); + const foundMarket = this.findMarketByAltnameOrId (marketId); + let symbol = undefined; + if (foundMarket !== undefined) { + market = foundMarket; + } else if (marketId !== undefined) { + // delisted market ids go here + market = this.getDelistedMarketById (marketId); + } + const timestamp = this.safeTimestamp (order, 'opentm'); + const amount = this.safeFloat (order, 'vol'); + const filled = this.safeFloat (order, 'vol_exec'); + const remaining = amount - filled; + let fee = undefined; + const cost = this.safeFloat (order, 'cost'); + let price = this.safeFloat (description, 'price'); + if ((price === undefined) || (price === 0)) { + price = this.safeFloat (description, 'price2'); + } + if ((price === undefined) || (price === 0)) { + price = this.safeFloat (order, 'price', price); + } + const average = this.safeFloat (order, 'price'); + if (market !== undefined) { + symbol = market['symbol']; + if ('fee' in order) { + const flags = order['oflags']; + const feeCost = this.safeFloat (order, 'fee'); + fee = { + 'cost': feeCost, + 'rate': undefined, + }; + if (flags.indexOf ('fciq') >= 0) { + fee['currency'] = market['quote']; + } else if (flags.indexOf ('fcib') >= 0) { + fee['currency'] = market['base']; + } + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString (order, 'id'); + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'average': average, + 'remaining': remaining, + 'fee': fee, + // 'trades': this.parseTrades (order['trades'], market), + }; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostQueryOrders (this.extend ({ + 'trades': true, // whether or not to include trades in output (optional, default false) + 'txid': id, // do not comma separate a list of ids - use fetchOrdersByIds instead + // 'userref': 'optional', // restrict results to given user reference id (optional) + }, params)); + const orders = response['result']; + const order = this.parseOrder (this.extend ({ 'id': id }, orders[id])); + return this.extend ({ 'info': response }, order); + } + + async fetchOrdersByIds (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostQueryOrders (this.extend ({ + 'trades': true, // whether or not to include trades in output (optional, default false) + 'txid': ids.join (','), // comma delimited list of transaction ids to query info about (20 maximum) + }, params)); + const result = this.safeValue (response, 'result', {}); + const orders = []; + const orderIds = Object.keys (result); + for (let i = 0; i < orderIds.length; i++) { + const id = orderIds[i]; + const item = result[id]; + const order = this.parseOrder (this.extend ({ 'id': id }, item)); + orders.push (order); + } + return orders; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'type': 'all', // any position, closed position, closing position, no position + // 'trades': false, // whether or not to include trades related to position in output + // 'start': 1234567890, // starting unix timestamp or trade tx id of results (exclusive) + // 'end': 1234567890, // ending unix timestamp or trade tx id of results (inclusive) + // 'ofs' = result offset + }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostTradesHistory (this.extend (request, params)); + // + // { + // "error": [], + // "result": { + // "trades": { + // "GJ3NYQ-XJRTF-THZABF": { + // "ordertxid": "TKH2SE-ZIF5E-CFI7LT", + // "postxid": "OEN3VX-M7IF5-JNBJAM", + // "pair": "XICNXETH", + // "time": 1527213229.4491, + // "type": "sell", + // "ordertype": "limit", + // "price": "0.001612", + // "cost": "0.025792", + // "fee": "0.000026", + // "vol": "16.00000000", + // "margin": "0.000000", + // "misc": "" + // }, + // ... + // }, + // "count": 9760, + // }, + // } + // + const trades = response['result']['trades']; + const ids = Object.keys (trades); + for (let i = 0; i < ids.length; i++) { + trades[ids[i]]['id'] = ids[i]; + } + const result = this.parseTrades (trades, undefined, since, limit); + if (symbol === undefined) { + return result; + } + return this.filterBySymbol (result, symbol); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let response = undefined; + try { + response = await this.privatePostCancelOrder (this.extend ({ + 'txid': id, + }, params)); + } catch (e) { + if (this.last_http_response) { + if (this.last_http_response.indexOf ('EOrder:Unknown order') >= 0) { + throw new OrderNotFound (this.id + ' cancelOrder() error ' + this.last_http_response); + } + } + throw e; + } + return response; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostOpenOrders (this.extend (request, params)); + const orders = this.parseOrders (response['result']['open'], undefined, since, limit); + if (symbol === undefined) { + return orders; + } + return this.filterBySymbol (orders, symbol); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + const response = await this.privatePostClosedOrders (this.extend (request, params)); + const orders = this.parseOrders (response['result']['closed'], undefined, since, limit); + if (symbol === undefined) { + return orders; + } + return this.filterBySymbol (orders, symbol); + } + + async fetchDepositMethods (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privatePostDepositMethods (this.extend (request, params)); + return response['result']; + } + + parseTransactionStatus (status) { + // IFEX transaction states + const statuses = { + 'Initial': 'pending', + 'Pending': 'pending', + 'Success': 'ok', + 'Settled': 'pending', + 'Failure': 'failed', + 'Partial': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { method: "Ether (Hex)", + // aclass: "currency", + // asset: "XETH", + // refid: "Q2CANKL-LBFVEE-U4Y2WQ", + // txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…", + // info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290", + // amount: "7.9999257900", + // fee: "0.0000000000", + // time: 1529223212, + // status: "Success" } + // + // fetchWithdrawals + // + // { method: "Ether", + // aclass: "currency", + // asset: "XETH", + // refid: "A2BF34S-O7LBNQ-UE4Y4O", + // txid: "0x288b83c6b0904d8400ef44e1c9e2187b5c8f7ea3d838222d53f701a15b5c274d", + // info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0", + // amount: "9.9950000000", + // fee: "0.0050000000", + // time: 1530481750, + // status: "Success" } + // + const id = this.safeString (transaction, 'refid'); + const txid = this.safeString (transaction, 'txid'); + const timestamp = this.safeTimestamp (transaction, 'time'); + const currencyId = this.safeString (transaction, 'asset'); + const code = this.safeCurrencyCode (currencyId, currency); + const address = this.safeString (transaction, 'info'); + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const type = this.safeString (transaction, 'type'); // injected from the outside + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost === undefined) { + if (type === 'deposit') { + feeCost = 0; + } + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + parseTransactionsByType (type, transactions, code = undefined, since = undefined, limit = undefined) { + const result = []; + for (let i = 0; i < transactions.length; i++) { + const transaction = this.parseTransaction (this.extend ({ + 'type': type, + }, transactions[i])); + result.push (transaction); + } + return this.filterByCurrencySinceLimit (result, code, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://www.kraken.com/en-us/help/api#deposit-status + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privatePostDepositStatus (this.extend (request, params)); + // + // { error: [], + // result: [ { method: "Ether (Hex)", + // aclass: "currency", + // asset: "XETH", + // refid: "Q2CANKL-LBFVEE-U4Y2WQ", + // txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…", + // info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290", + // amount: "7.9999257900", + // fee: "0.0000000000", + // time: 1529223212, + // status: "Success" } ] } + // + return this.parseTransactionsByType ('deposit', response['result'], code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // https://www.kraken.com/en-us/help/api#withdraw-status + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument'); + } + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + }; + const response = await this.privatePostWithdrawStatus (this.extend (request, params)); + // + // { error: [], + // result: [ { method: "Ether", + // aclass: "currency", + // asset: "XETH", + // refid: "A2BF34S-O7LBNQ-UE4Y4O", + // txid: "0x298c83c7b0904d8400ef43e1c9e2287b518f7ea3d838822d53f704a1565c274d", + // info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0", + // amount: "9.9950000000", + // fee: "0.0050000000", + // time: 1530481750, + // status: "Success" } ] } + // + return this.parseTransactionsByType ('withdrawal', response['result'], code, since, limit); + } + + async createDepositAddress (code, params = {}) { + const request = { + 'new': 'true', + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + // eslint-disable-next-line quotes + let method = this.safeString (params, 'method'); + if (method === undefined) { + if (this.options['cacheDepositMethodsOnFetchDepositAddress']) { + // cache depositMethods + if (!(code in this.options['depositMethods'])) { + this.options['depositMethods'][code] = await this.fetchDepositMethods (code); + } + method = this.options['depositMethods'][code][0]['method']; + } else { + throw new ExchangeError (this.id + ' fetchDepositAddress() requires an extra `method` parameter. Use fetchDepositMethods ("' + code + '") to get a list of available deposit methods or enable the exchange property .options["cacheDepositMethodsOnFetchDepositAddress"] = true'); + } + } + const request = { + 'asset': currency['id'], + 'method': method, + }; + const response = await this.privatePostDepositAddresses (this.extend (request, params)); // overwrite methods + const result = response['result']; + const numResults = result.length; + if (numResults < 1) { + throw new InvalidAddress (this.id + ' privatePostDepositAddresses() returned no addresses'); + } + const address = this.safeString (result[0], 'address'); + const tag = this.safeString2 (result[0], 'tag', 'memo'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + if ('key' in params) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'asset': currency['id'], + 'amount': amount, + // 'address': address, // they don't allow withdrawals to direct addresses + }; + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': response['result'], + }; + } + throw new ExchangeError (this.id + " withdraw requires a 'key' parameter (withdrawal key name, as set up on your account)"); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + this.version + '/' + api + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + body = this.urlencode (this.extend ({ 'nonce': nonce }, params)); + const auth = this.encode (nonce + body); + const hash = this.hash (auth, 'sha256', 'binary'); + const binary = this.stringToBinary (this.encode (url)); + const binhash = this.binaryConcat (binary, hash); + const secret = this.base64ToBinary (this.secret); + const signature = this.hmac (binhash, secret, 'sha512', 'base64'); + headers = { + 'API-Key': this.apiKey, + 'API-Sign': this.decode (signature), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } else { + url = '/' + path; + } + url = this.urls['api'][api] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + nonce () { + return this.milliseconds (); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code === 520) { + throw new ExchangeNotAvailable (this.id + ' ' + code.toString () + ' ' + reason); + } + // todo: rewrite this for "broad" exceptions matching + if (body.indexOf ('Invalid order') >= 0) { + throw new InvalidOrder (this.id + ' ' + body); + } + if (body.indexOf ('Invalid nonce') >= 0) { + throw new InvalidNonce (this.id + ' ' + body); + } + if (body.indexOf ('Insufficient funds') >= 0) { + throw new InsufficientFunds (this.id + ' ' + body); + } + if (body.indexOf ('Cancel pending') >= 0) { + throw new CancelPending (this.id + ' ' + body); + } + if (body.indexOf ('Invalid arguments:volume') >= 0) { + throw new InvalidOrder (this.id + ' ' + body); + } + if (body[0] === '{') { + if (typeof response !== 'string') { + if ('error' in response) { + const numErrors = response['error'].length; + if (numErrors) { + const message = this.id + ' ' + body; + for (let i = 0; i < response['error'].length; i++) { + const error = response['error'][i]; + this.throwExactlyMatchedException (this.exceptions, error, message); + } + throw new ExchangeError (message); + } + } + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/kucoin.js b/TaskServer/node_modules/ccxt/js/kucoin.js new file mode 100644 index 000000000..8d2e8a33c --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/kucoin.js @@ -0,0 +1,1662 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, AccountSuspended, InvalidNonce, NotSupported, BadRequest, AuthenticationError, BadSymbol, RateLimitExceeded } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class kucoin extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kucoin', + 'name': 'KuCoin', + 'countries': [ 'SC' ], + 'rateLimit': 334, + 'version': 'v2', + 'certified': false, + 'comment': 'Platform 2.0', + 'has': { + 'fetchTime': true, + 'fetchMarkets': true, + 'fetchCurrencies': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchOrderBook': true, + 'fetchOrder': true, + 'fetchClosedOrders': true, + 'fetchOpenOrders': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchBalance': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchAccounts': true, + 'fetchFundingFee': true, + 'fetchOHLCV': true, + 'fetchLedger': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/57369448-3cc3aa80-7196-11e9-883e-5ebeb35e4f57.jpg', + 'referral': 'https://www.kucoin.com/?rcode=E5wkqe', + 'api': { + 'public': 'https://openapi-v2.kucoin.com', + 'private': 'https://openapi-v2.kucoin.com', + }, + 'test': { + 'public': 'https://openapi-sandbox.kucoin.com', + 'private': 'https://openapi-sandbox.kucoin.com', + }, + 'www': 'https://www.kucoin.com', + 'doc': [ + 'https://docs.kucoin.com', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'api': { + 'public': { + 'get': [ + 'timestamp', + 'symbols', + 'market/allTickers', + 'market/orderbook/level{level}', + 'market/histories', + 'market/candles', + 'market/stats', + 'currencies', + 'currencies/{currency}', + ], + 'post': [ + 'bullet-public', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'accounts/{accountId}', + 'accounts/{accountId}/ledgers', + 'accounts/{accountId}/holds', + 'deposit-addresses', + 'deposits', + 'hist-deposits', + 'hist-orders', + 'hist-withdrawals', + 'withdrawals', + 'withdrawals/quotas', + 'orders', + 'orders/{orderId}', + 'fills', + 'limit/fills', + ], + 'post': [ + 'accounts', + 'accounts/inner-transfer', + 'deposit-addresses', + 'withdrawals', + 'orders', + 'bullet-private', + ], + 'delete': [ + 'withdrawals/{withdrawalId}', + 'orders/{orderId}', + ], + }, + }, + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '8h': '8hour', + '12h': '12hour', + '1d': '1day', + '1w': '1week', + }, + 'exceptions': { + 'exact': { + 'order not exist': OrderNotFound, + 'order not exist.': OrderNotFound, // duplicated error temporarily + 'order_not_exist': OrderNotFound, // {"code":"order_not_exist","msg":"order_not_exist"} ¯\_(ツ)_/¯ + 'order_not_exist_or_not_allow_to_cancel': InvalidOrder, // {"code":"400100","msg":"order_not_exist_or_not_allow_to_cancel"} + 'Order size below the minimum requirement.': InvalidOrder, // {"code":"400100","msg":"Order size below the minimum requirement."} + 'The withdrawal amount is below the minimum requirement.': ExchangeError, // {"code":"400100","msg":"The withdrawal amount is below the minimum requirement."} + '400': BadRequest, + '401': AuthenticationError, + '403': NotSupported, + '404': NotSupported, + '405': NotSupported, + '429': RateLimitExceeded, + '500': ExchangeError, + '503': ExchangeNotAvailable, + '200004': InsufficientFunds, + '230003': InsufficientFunds, // {"code":"230003","msg":"Balance insufficient!"} + '260100': InsufficientFunds, // {"code":"260100","msg":"account.noBalance"} + '300000': InvalidOrder, + '400000': BadSymbol, + '400001': AuthenticationError, + '400002': InvalidNonce, + '400003': AuthenticationError, + '400004': AuthenticationError, + '400005': AuthenticationError, + '400006': AuthenticationError, + '400007': AuthenticationError, + '400008': NotSupported, + '400100': BadRequest, + '411100': AccountSuspended, + '415000': BadRequest, // {"code":"415000","msg":"Unsupported Media Type"} + '500000': ExchangeError, + }, + 'broad': { + 'Exceeded the access frequency': RateLimitExceeded, + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'commonCurrencies': { + 'HOT': 'HOTNOW', + 'EDGE': 'DADI', // https://github.com/ccxt/ccxt/issues/5756 + }, + 'options': { + 'version': 'v1', + 'symbolSeparator': '-', + 'fetchMyTradesMethod': 'private_get_fills', + 'fetchBalance': { + 'type': 'trade', // or 'main' + }, + }, + }); + } + + nonce () { + return this.milliseconds (); + } + + async loadTimeDifference () { + const response = await this.publicGetTimestamp (); + const after = this.milliseconds (); + const kucoinTime = this.safeInteger (response, 'data'); + this.options['timeDifference'] = parseInt (after - kucoinTime); + return this.options['timeDifference']; + } + + async fetchTime (params = {}) { + const response = await this.publicGetTimestamp (params); + // + // { + // "code":"200000", + // "msg":"success", + // "data":1546837113087 + // } + // + return this.safeInteger (response, 'data'); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetSymbols (params); + // + // { quoteCurrency: 'BTC', + // symbol: 'KCS-BTC', + // quoteMaxSize: '9999999', + // quoteIncrement: '0.000001', + // baseMinSize: '0.01', + // quoteMinSize: '0.00001', + // enableTrading: true, + // priceIncrement: '0.00000001', + // name: 'KCS-BTC', + // baseIncrement: '0.01', + // baseMaxSize: '9999999', + // baseCurrency: 'KCS' } + // + const data = response['data']; + const result = []; + for (let i = 0; i < data.length; i++) { + const market = data[i]; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = this.safeValue (market, 'enableTrading'); + const baseMaxSize = this.safeFloat (market, 'baseMaxSize'); + const baseMinSize = this.safeFloat (market, 'baseMinSize'); + const quoteMaxSize = this.safeFloat (market, 'quoteMaxSize'); + const quoteMinSize = this.safeFloat (market, 'quoteMinSize'); + // const quoteIncrement = this.safeFloat (market, 'quoteIncrement'); + const precision = { + 'amount': this.precisionFromString (this.safeString (market, 'baseIncrement')), + 'price': this.precisionFromString (this.safeString (market, 'priceIncrement')), + }; + const limits = { + 'amount': { + 'min': baseMinSize, + 'max': baseMaxSize, + }, + 'price': { + 'min': this.safeFloat (market, 'priceIncrement'), + 'max': quoteMaxSize / baseMinSize, + }, + 'cost': { + 'min': quoteMinSize, + 'max': quoteMaxSize, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // precision: 10, + // name: 'KCS', + // fullName: 'KCS shares', + // currency: 'KCS' + // } + // + const responseData = response['data']; + const result = {}; + for (let i = 0; i < responseData.length; i++) { + const entry = responseData[i]; + const id = this.safeString (entry, 'currency'); + const name = this.safeString (entry, 'fullName'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (entry, 'precision'); + result[code] = { + 'id': id, + 'name': name, + 'code': code, + 'precision': precision, + 'info': entry, + }; + } + return result; + } + + async fetchAccounts (params = {}) { + const response = await this.privateGetAccounts (params); + // + // { code: "200000", + // data: [ { balance: "0.00009788", + // available: "0.00009788", + // holds: "0", + // currency: "BTC", + // id: "5c6a4fd399a1d81c4f9cc4d0", + // type: "trade" }, + // ..., + // { balance: "0.00000001", + // available: "0.00000001", + // holds: "0", + // currency: "ETH", + // id: "5c6a49ec99a1d819392e8e9f", + // type: "trade" } ] } + // + const data = this.safeValue (response, 'data'); + const result = []; + for (let i = 0; i < data.length; i++) { + const account = data[i]; + const accountId = this.safeString (account, 'id'); + const currencyId = this.safeString (account, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const type = this.safeString (account, 'type'); // main or trade + result.push ({ + 'id': accountId, + 'type': type, + 'currency': code, + 'info': account, + }); + } + return result; + } + + async fetchFundingFee (code, params = {}) { + const currencyId = this.currencyId (code); + const request = { + 'currency': currencyId, + }; + const response = await this.privateGetWithdrawalsQuotas (this.extend (request, params)); + const data = response['data']; + const withdrawFees = {}; + withdrawFees[code] = this.safeFloat (data, 'withdrawMinFee'); + return { + 'info': response, + 'withdraw': withdrawFees, + 'deposit': {}, + }; + } + + parseTicker (ticker, market = undefined) { + // + // { + // symbol: "ETH-BTC", + // high: "0.019518", + // vol: "7997.82836194", + // last: "0.019329", + // low: "0.019", + // buy: "0.019329", + // sell: "0.01933", + // changePrice: "-0.000139", + // time: 1580553706304, + // averagePrice: "0.01926386", + // changeRate: "-0.0071", + // volValue: "154.40791568183474" + // } + // + // { + // "trading": true, + // "symbol": "KCS-BTC", + // "buy": 0.00011, + // "sell": 0.00012, + // "sort": 100, + // "volValue": 3.13851792584, //total + // "baseCurrency": "KCS", + // "market": "BTC", + // "quoteCurrency": "BTC", + // "symbolCode": "KCS-BTC", + // "datetime": 1548388122031, + // "high": 0.00013, + // "vol": 27514.34842, + // "low": 0.0001, + // "changePrice": -1.0e-5, + // "changeRate": -0.0769, + // "lastTradedPrice": 0.00012, + // "board": 0, + // "mark": 0 + // } + // + let percentage = this.safeFloat (ticker, 'changeRate'); + if (percentage !== undefined) { + percentage = percentage * 100; + } + const last = this.safeFloat2 (ticker, 'last', 'lastTradedPrice'); + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const timestamp = this.safeInteger2 (ticker, 'time', 'datetime'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': this.safeFloat (ticker, 'changePrice'), + 'percentage': percentage, + 'average': this.safeFloat (ticker, 'averagePrice'), + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'volValue'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketAllTickers (params); + // + // { + // "code": "200000", + // "data": { + // "date": 1550661940645, + // "ticker": [ + // 'buy': '0.00001168', + // 'changePrice': '-0.00000018', + // 'changeRate': '-0.0151', + // 'datetime': 1550661146316, + // 'high': '0.0000123', + // 'last': '0.00001169', + // 'low': '0.00001159', + // 'sell': '0.00001182', + // 'symbol': 'LOOM-BTC', + // 'vol': '44399.5669' + // }, + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + const tickers = this.safeValue (data, 'ticker', []); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = this.safeString (ticker, 'symbol'); + if (symbol !== undefined) { + result[symbol] = ticker; + } + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetMarketStats (this.extend (request, params)); + // + // { + // "code": "200000", + // "data": { + // 'buy': '0.00001168', + // 'changePrice': '-0.00000018', + // 'changeRate': '-0.0151', + // 'datetime': 1550661146316, + // 'high': '0.0000123', + // 'last': '0.00001169', + // 'low': '0.00001159', + // 'sell': '0.00001182', + // 'symbol': 'LOOM-BTC', + // 'vol': '44399.5669' + // }, + // } + // + return this.parseTicker (response['data'], market); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // [ + // "1545904980", // Start time of the candle cycle + // "0.058", // opening price + // "0.049", // closing price + // "0.058", // highest price + // "0.049", // lowest price + // "0.018", // base volume + // "0.000945", // quote volume + // ] + // + return [ + parseInt (ohlcv[0]) * 1000, + parseFloat (ohlcv[1]), + parseFloat (ohlcv[3]), + parseFloat (ohlcv[4]), + parseFloat (ohlcv[2]), + parseFloat (ohlcv[5]), + ]; + } + + async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketId = market['id']; + const request = { + 'symbol': marketId, + 'type': this.timeframes[timeframe], + }; + const duration = this.parseTimeframe (timeframe) * 1000; + let endAt = this.milliseconds (); // required param + if (since !== undefined) { + request['startAt'] = parseInt (Math.floor (since / 1000)); + if (limit === undefined) { + // https://docs.kucoin.com/#get-klines + // https://docs.kucoin.com/#details + // For each query, the system would return at most 1500 pieces of data. + // To obtain more data, please page the data by time. + limit = this.safeInteger (this.options, 'fetchOHLCVLimit', 1500); + } + endAt = this.sum (since, limit * duration); + } else if (limit !== undefined) { + since = endAt - limit * duration; + request['startAt'] = parseInt (Math.floor (since / 1000)); + } + request['endAt'] = parseInt (Math.floor (endAt / 1000)); + const response = await this.publicGetMarketCandles (this.extend (request, params)); + const responseData = this.safeValue (response, 'data', []); + return this.parseOHLCVs (responseData, market, timeframe, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currencyId = this.currencyId (code); + const request = { 'currency': currencyId }; + const response = await this.privatePostDepositAddresses (this.extend (request, params)); + // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} + // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} + const data = this.safeValue (response, 'data', {}); + let address = this.safeString (data, 'address'); + // BCH/BSV is returned with a "bitcoincash:" prefix, which we cut off here and only keep the address + if (address !== undefined) { + address = address.replace ('bitcoincash:', ''); + } + const tag = this.safeString (data, 'memo'); + this.checkAddress (address); + return { + 'info': response, + 'currency': code, + 'address': address, + 'tag': tag, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currencyId = this.currencyId (code); + const request = { 'currency': currencyId }; + const response = await this.privateGetDepositAddresses (this.extend (request, params)); + // BCH {"code":"200000","data":{"address":"bitcoincash:qza3m4nj9rx7l9r0cdadfqxts6f92shvhvr5ls4q7z","memo":""}} + // BTC {"code":"200000","data":{"address":"36SjucKqQpQSvsak9A7h6qzFjrVXpRNZhE","memo":""}} + const data = this.safeValue (response, 'data', {}); + let address = this.safeString (data, 'address'); + // BCH/BSV is returned with a "bitcoincash:" prefix, which we cut off here and only keep the address + if (address !== undefined) { + address = address.replace ('bitcoincash:', ''); + } + const tag = this.safeString (data, 'memo'); + this.checkAddress (address); + return { + 'info': response, + 'currency': code, + 'address': address, + 'tag': tag, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + let level = '2'; + if (limit !== undefined) { + if ((limit !== 20) && (limit !== 100)) { + throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100'); + } + level += '_' + limit.toString (); + } + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = this.extend ({ 'symbol': marketId, 'level': level }, params); + const response = await this.publicGetMarketOrderbookLevelLevel (request); + // + // { sequence: '1547731421688', + // asks: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ], + // bids: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ], } + // + const data = response['data']; + const timestamp = this.safeInteger (data, 'time'); + // level can be a string such as 2_20 or 2_100 + const levelString = this.safeString (request, 'level'); + const levelParts = levelString.split ('_'); + const offset = parseInt (levelParts[0]); + const orderbook = this.parseOrderBook (data, timestamp, 'bids', 'asks', offset - 2, offset - 1); + orderbook['nonce'] = this.safeInteger (data, 'sequence'); + return orderbook; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + // required param, cannot be used twice + const clientOid = this.uuid (); + const request = { + 'clientOid': clientOid, + 'side': side, + 'symbol': marketId, + 'type': type, + }; + if (type !== 'market') { + request['price'] = this.priceToPrecision (symbol, price); + request['size'] = this.amountToPrecision (symbol, amount); + } else { + if (this.safeValue (params, 'quoteAmount')) { + // used to create market order by quote amount - https://github.com/ccxt/ccxt/issues/4876 + request['funds'] = this.amountToPrecision (symbol, amount); + } else { + request['size'] = this.amountToPrecision (symbol, amount); + } + } + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "orderId": "5bd6e9286d99522a52e458de" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const timestamp = this.milliseconds (); + const order = { + 'id': this.safeString (data, 'orderId'), + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': undefined, + 'filled': undefined, + 'remaining': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + 'status': 'open', + 'clientOid': clientOid, + 'info': data, + }; + if (!this.safeValue (params, 'quoteAmount')) { + order['amount'] = amount; + } + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { 'orderId': id }; + const response = await this.privateDeleteOrdersOrderId (this.extend (request, params)); + return response; + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': status, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['startAt'] = since; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "currentPage": 1, + // "pageSize": 1, + // "totalNum": 153408, + // "totalPage": 153408, + // "items": [ + // { + // "id": "5c35c02703aa673ceec2a168", //orderid + // "symbol": "BTC-USDT", //symbol + // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order + // "type": "limit", // order type,e.g. limit,markrt,stop_limit. + // "side": "buy", // transaction direction,include buy and sell + // "price": "10", // order price + // "size": "2", // order quantity + // "funds": "0", // order funds + // "dealFunds": "0.166", // deal funds + // "dealSize": "2", // deal quantity + // "fee": "0", // fee + // "feeCurrency": "USDT", // charge fee currency + // "stp": "", // self trade prevention,include CN,CO,DC,CB + // "stop": "", // stop type + // "stopTriggered": false, // stop order is triggered + // "stopPrice": "0", // stop price + // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK + // "postOnly": false, // postOnly + // "hidden": false, // hidden order + // "iceberg": false, // iceberg order + // "visibleSize": "0", // display quantity for iceberg order + // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT + // "channel": "IOS", // order source + // "clientOid": "", // user-entered order unique mark + // "remark": "", // remark + // "tags": "", // tag order source + // "isActive": false, // status before unfilled or uncancelled + // "cancelExist": false, // order cancellation transaction record + // "createdAt": 1547026471000 // time + // }, + // ] + // } + // } + const responseData = this.safeValue (response, 'data', {}); + const orders = this.safeValue (responseData, 'items', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('done', symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByStatus ('active', symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privateGetOrdersOrderId (this.extend (request, params)); + const responseData = response['data']; + return this.parseOrder (responseData, market); + } + + parseOrder (order, market = undefined) { + // + // fetchOpenOrders, fetchClosedOrders + // + // { + // "id": "5c35c02703aa673ceec2a168", //orderid + // "symbol": "BTC-USDT", //symbol + // "opType": "DEAL", // operation type,deal is pending order,cancel is cancel order + // "type": "limit", // order type,e.g. limit,markrt,stop_limit. + // "side": "buy", // transaction direction,include buy and sell + // "price": "10", // order price + // "size": "2", // order quantity + // "funds": "0", // order funds + // "dealFunds": "0.166", // deal funds + // "dealSize": "2", // deal quantity + // "fee": "0", // fee + // "feeCurrency": "USDT", // charge fee currency + // "stp": "", // self trade prevention,include CN,CO,DC,CB + // "stop": "", // stop type + // "stopTriggered": false, // stop order is triggered + // "stopPrice": "0", // stop price + // "timeInForce": "GTC", // time InForce,include GTC,GTT,IOC,FOK + // "postOnly": false, // postOnly + // "hidden": false, // hidden order + // "iceberg": false, // iceberg order + // "visibleSize": "0", // display quantity for iceberg order + // "cancelAfter": 0, // cancel orders time,requires timeInForce to be GTT + // "channel": "IOS", // order source + // "clientOid": "", // user-entered order unique mark + // "remark": "", // remark + // "tags": "", // tag order source + // "isActive": false, // status before unfilled or uncancelled + // "cancelExist": false, // order cancellation transaction record + // "createdAt": 1547026471000 // time + // } + // + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + market = this.safeValue (this.markets_by_id, marketId); + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const orderId = this.safeString (order, 'id'); + const type = this.safeString (order, 'type'); + const timestamp = this.safeInteger (order, 'createdAt'); + const datetime = this.iso8601 (timestamp); + let price = this.safeFloat (order, 'price'); + const side = this.safeString (order, 'side'); + const feeCurrencyId = this.safeString (order, 'feeCurrency'); + const feeCurrency = this.safeCurrencyCode (feeCurrencyId); + const feeCost = this.safeFloat (order, 'fee'); + const amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat (order, 'dealSize'); + const cost = this.safeFloat (order, 'dealFunds'); + const remaining = amount - filled; + // bool + let status = order['isActive'] ? 'open' : 'closed'; + status = order['cancelExist'] ? 'canceled' : status; + const fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + if (type === 'market') { + if (price === 0.0) { + if ((cost !== undefined) && (filled !== undefined)) { + if ((cost > 0) && (filled > 0)) { + price = cost / filled; + } + } + } + } + return { + 'id': orderId, + 'symbol': symbol, + 'type': type, + 'side': side, + 'amount': amount, + 'price': price, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'timestamp': timestamp, + 'datetime': datetime, + 'fee': fee, + 'status': status, + 'info': order, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + const method = this.options['fetchMyTradesMethod']; + let parseResponseData = false; + if (method === 'private_get_fills') { + // does not return trades earlier than 2019-02-18T00:00:00Z + if (since !== undefined) { + // only returns trades up to one week after the since param + request['startAt'] = since; + } + } else if (method === 'private_get_limit_fills') { + // does not return trades earlier than 2019-02-18T00:00:00Z + // takes no params + // only returns first 1000 trades (not only "in the last 24 hours" as stated in the docs) + parseResponseData = true; + } else if (method === 'private_get_hist_orders') { + // despite that this endpoint is called `HistOrders` + // it returns historical trades instead of orders + // returns trades earlier than 2019-02-18T00:00:00Z only + if (since !== undefined) { + request['startAt'] = parseInt (since / 1000); + } + } else { + throw new ExchangeError (this.id + ' invalid fetchClosedOrder method'); + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "currentPage": 1, + // "pageSize": 50, + // "totalNum": 1, + // "totalPage": 1, + // "items": [ + // { + // "symbol":"BTC-USDT", // symbol + // "tradeId":"5c35c02709e4f67d5266954e", // trade id + // "orderId":"5c35c02703aa673ceec2a168", // order id + // "counterOrderId":"5c1ab46003aa676e487fa8e3", // counter order id + // "side":"buy", // transaction direction,include buy and sell + // "liquidity":"taker", // include taker and maker + // "forceTaker":true, // forced to become taker + // "price":"0.083", // order price + // "size":"0.8424304", // order quantity + // "funds":"0.0699217232", // order funds + // "fee":"0", // fee + // "feeRate":"0", // fee rate + // "feeCurrency":"USDT", // charge fee currency + // "stop":"", // stop type + // "type":"limit", // order type, e.g. limit, market, stop_limit. + // "createdAt":1547026472000 // time + // }, + // //------------------------------------------------------ + // // v1 (historical) trade response structure + // { + // "symbol": "SNOV-ETH", + // "dealPrice": "0.0000246", + // "dealValue": "0.018942", + // "amount": "770", + // "fee": "0.00001137", + // "side": "sell", + // "createdAt": 1540080199 + // "id":"5c4d389e4c8c60413f78e2e5", + // } + // ] + // } + // + const data = this.safeValue (response, 'data', {}); + let trades = undefined; + if (parseResponseData) { + trades = data; + } else { + trades = this.safeValue (data, 'items', []); + } + return this.parseTrades (trades, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (since !== undefined) { + request['startAt'] = Math.floor (since / 1000); + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + const response = await this.publicGetMarketHistories (this.extend (request, params)); + // + // { + // "code": "200000", + // "data": [ + // { + // "sequence": "1548764654235", + // "side": "sell", + // "size":"0.6841354", + // "price":"0.03202", + // "time":1548848575203567174 + // } + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "sequence": "1548764654235", + // "side": "sell", + // "size":"0.6841354", + // "price":"0.03202", + // "time":1548848575203567174 + // } + // + // { + // sequence: '1568787654360', + // symbol: 'BTC-USDT', + // side: 'buy', + // size: '0.00536577', + // price: '9345', + // takerOrderId: '5e356c4a9f1a790008f8d921', + // time: '1580559434436443257', + // type: 'match', + // makerOrderId: '5e356bffedf0010008fa5d7f', + // tradeId: '5e356c4aeefabd62c62a1ece' + // } + // + // fetchMyTrades (private) v2 + // + // { + // "symbol":"BTC-USDT", + // "tradeId":"5c35c02709e4f67d5266954e", + // "orderId":"5c35c02703aa673ceec2a168", + // "counterOrderId":"5c1ab46003aa676e487fa8e3", + // "side":"buy", + // "liquidity":"taker", + // "forceTaker":true, + // "price":"0.083", + // "size":"0.8424304", + // "funds":"0.0699217232", + // "fee":"0", + // "feeRate":"0", + // "feeCurrency":"USDT", + // "stop":"", + // "type":"limit", + // "createdAt":1547026472000 + // } + // + // fetchMyTrades v2 alternative format since 2019-05-21 https://github.com/ccxt/ccxt/pull/5162 + // + // { + // symbol: "OPEN-BTC", + // forceTaker: false, + // orderId: "5ce36420054b4663b1fff2c9", + // fee: "0", + // feeCurrency: "", + // type: "", + // feeRate: "0", + // createdAt: 1558417615000, + // size: "12.8206", + // stop: "", + // price: "0", + // funds: "0", + // tradeId: "5ce390cf6e0db23b861c6e80" + // } + // + // fetchMyTrades (private) v1 (historical) + // + // { + // "symbol": "SNOV-ETH", + // "dealPrice": "0.0000246", + // "dealValue": "0.018942", + // "amount": "770", + // "fee": "0.00001137", + // "side": "sell", + // "createdAt": 1540080199 + // "id":"5c4d389e4c8c60413f78e2e5", + // } + // + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + const id = this.safeString2 (trade, 'tradeId', 'id'); + const orderId = this.safeString (trade, 'orderId'); + const takerOrMaker = this.safeString (trade, 'liquidity'); + const amount = this.safeFloat2 (trade, 'size', 'amount'); + let timestamp = this.safeInteger (trade, 'time'); + if (timestamp !== undefined) { + timestamp = parseInt (timestamp / 1000000); + } else { + timestamp = this.safeInteger (trade, 'createdAt'); + // if it's a historical v1 trade, the exchange returns timestamp in seconds + if (('dealValue' in trade) && (timestamp !== undefined)) { + timestamp = timestamp * 1000; + } + } + const price = this.safeFloat2 (trade, 'price', 'dealPrice'); + const side = this.safeString (trade, 'side'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'feeCurrency'); + let feeCurrency = this.safeCurrencyCode (feeCurrencyId); + if (feeCurrency === undefined) { + if (market !== undefined) { + feeCurrency = (side === 'sell') ? market['quote'] : market['base']; + } + } + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': this.safeFloat (trade, 'feeRate'), + }; + } + const type = this.safeString (trade, 'type'); + let cost = this.safeFloat2 (trade, 'funds', 'dealValue'); + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + } + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + this.checkAddress (address); + const currency = this.currencyId (code); + const request = { + 'currency': currency, + 'address': address, + 'amount': amount, + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const response = await this.privatePostWithdrawals (this.extend (request, params)); + // + // https://github.com/ccxt/ccxt/issues/5558 + // + // { + // "code": 200000, + // "data": { + // "withdrawalId": "abcdefghijklmnopqrstuvwxyz" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return { + 'id': this.safeString (data, 'withdrawalId'), + 'info': response, + }; + } + + parseTransactionStatus (status) { + const statuses = { + 'SUCCESS': 'ok', + 'PROCESSING': 'ok', + 'FAILURE': 'failed', + }; + return this.safeString (statuses, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989", + // "memo": "5c247c8a03aa677cea2a251d", + // "amount": 1, + // "fee": 0.0001, + // "currency": "KCS", + // "isInner": false, + // "walletTxId": "5bbb57386d99522d9f954c5a@test004", + // "status": "SUCCESS", + // "createdAt": 1544178843000, + // "updatedAt": 1544178891000 + // } + // + // fetchWithdrawals + // + // { + // "id": "5c2dc64e03aa675aa263f1ac", + // "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9", + // "memo": "", + // "currency": "ETH", + // "amount": 1.0000000, + // "fee": 0.0100000, + // "walletTxId": "3e2414d82acce78d38be7fe9", + // "isInner": false, + // "status": "FAILURE", + // "createdAt": 1546503758000, + // "updatedAt": 1546504603000 + // } + // + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + let address = this.safeString (transaction, 'address'); + const amount = this.safeFloat (transaction, 'amount'); + let txid = this.safeString (transaction, 'walletTxId'); + if (txid !== undefined) { + const txidParts = txid.split ('@'); + const numTxidParts = txidParts.length; + if (numTxidParts > 1) { + if (address === undefined) { + if (txidParts[1].length > 1) { + address = txidParts[1]; + } + } + } + txid = txidParts[0]; + } + let type = (txid === undefined) ? 'withdrawal' : 'deposit'; + const rawStatus = this.safeString (transaction, 'status'); + const status = this.parseTransactionStatus (rawStatus); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + let rate = undefined; + if (amount !== undefined) { + rate = feeCost / amount; + } + fee = { + 'cost': feeCost, + 'rate': rate, + 'currency': code, + }; + } + const tag = this.safeString (transaction, 'memo'); + let timestamp = this.safeInteger2 (transaction, 'createdAt', 'createAt'); + const id = this.safeString (transaction, 'id'); + let updated = this.safeInteger (transaction, 'updatedAt'); + const isV1 = !('createdAt' in transaction); + // if it's a v1 structure + if (isV1) { + type = ('address' in transaction) ? 'withdrawal' : 'deposit'; + if (timestamp !== undefined) { + timestamp = timestamp * 1000; + } + if (updated !== undefined) { + updated = updated * 1000; + } + } + return { + 'id': id, + 'address': address, + 'tag': tag, + 'currency': code, + 'amount': amount, + 'txid': txid, + 'type': type, + 'status': status, + 'fee': fee, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'updated': updated, + 'info': transaction, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + let method = 'privateGetDeposits'; + if (since !== undefined) { + // if since is earlier than 2019-02-18T00:00:00Z + if (since < 1550448000000) { + request['startAt'] = parseInt (since / 1000); + method = 'privateGetHistDeposits'; + } else { + request['startAt'] = since; + } + } + const response = await this[method] (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "currentPage": 1, + // "pageSize": 5, + // "totalNum": 2, + // "totalPage": 1, + // "items": [ + // //-------------------------------------------------- + // // version 2 deposit response structure + // { + // "address": "0x5f047b29041bcfdbf0e4478cdfa753a336ba6989", + // "memo": "5c247c8a03aa677cea2a251d", + // "amount": 1, + // "fee": 0.0001, + // "currency": "KCS", + // "isInner": false, + // "walletTxId": "5bbb57386d99522d9f954c5a@test004", + // "status": "SUCCESS", + // "createdAt": 1544178843000, + // "updatedAt": 1544178891000 + // }, + // //-------------------------------------------------- + // // version 1 (historical) deposit response structure + // { + // "currency": "BTC", + // "createAt": 1528536998, + // "amount": "0.03266638", + // "walletTxId": "55c643bc2c68d6f17266383ac1be9e454038864b929ae7cee0bc408cc5c869e8@12ffGWmMMD1zA1WbFm7Ho3JZ1w6NYXjpFk@234", + // "isInner": false, + // "status": "SUCCESS", + // } + // ] + // } + // } + // + const responseData = response['data']['items']; + return this.parseTransactions (responseData, currency, since, limit, { 'type': 'deposit' }); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['pageSize'] = limit; + } + let method = 'privateGetWithdrawals'; + if (since !== undefined) { + // if since is earlier than 2019-02-18T00:00:00Z + if (since < 1550448000000) { + request['startAt'] = parseInt (since / 1000); + method = 'privateGetHistWithdrawals'; + } else { + request['startAt'] = since; + } + } + const response = await this[method] (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // "currentPage": 1, + // "pageSize": 5, + // "totalNum": 2, + // "totalPage": 1, + // "items": [ + // //-------------------------------------------------- + // // version 2 withdrawal response structure + // { + // "id": "5c2dc64e03aa675aa263f1ac", + // "address": "0x5bedb060b8eb8d823e2414d82acce78d38be7fe9", + // "memo": "", + // "currency": "ETH", + // "amount": 1.0000000, + // "fee": 0.0100000, + // "walletTxId": "3e2414d82acce78d38be7fe9", + // "isInner": false, + // "status": "FAILURE", + // "createdAt": 1546503758000, + // "updatedAt": 1546504603000 + // }, + // //-------------------------------------------------- + // // version 1 (historical) withdrawal response structure + // { + // "currency": "BTC", + // "createAt": 1526723468, + // "amount": "0.534", + // "address": "33xW37ZSW4tQvg443Pc7NLCAs167Yc2XUV", + // "walletTxId": "aeacea864c020acf58e51606169240e96774838dcd4f7ce48acf38e3651323f4", + // "isInner": false, + // "status": "SUCCESS" + // } + // ] + // } + // } + // + const responseData = response['data']['items']; + return this.parseTransactions (responseData, currency, since, limit, { 'type': 'withdrawal' }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + let type = undefined; + const request = {}; + if ('type' in params) { + type = params['type']; + if (type !== undefined) { + request['type'] = type; + } + params = this.omit (params, 'type'); + } else { + const options = this.safeValue (this.options, 'fetchBalance', {}); + type = this.safeString (options, 'type', 'trade'); + } + const response = await this.privateGetAccounts (this.extend (request, params)); + // + // { + // "code":"200000", + // "data":[ + // {"balance":"0.00009788","available":"0.00009788","holds":"0","currency":"BTC","id":"5c6a4fd399a1d81c4f9cc4d0","type":"trade"}, + // {"balance":"3.41060034","available":"3.41060034","holds":"0","currency":"SOUL","id":"5c6a4d5d99a1d8182d37046d","type":"trade"}, + // {"balance":"0.01562641","available":"0.01562641","holds":"0","currency":"NEO","id":"5c6a4f1199a1d8165a99edb1","type":"trade"}, + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const result = { 'info': response }; + for (let i = 0; i < data.length; i++) { + const balance = data[i]; + const balanceType = this.safeString (balance, 'type'); + if (balanceType === type) { + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'holds'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + ' fetchLedger requires a code param'); + } + await this.loadMarkets (); + await this.loadAccounts (); + const currency = this.currency (code); + let accountId = this.safeString (params, 'accountId'); + if (accountId === undefined) { + for (let i = 0; i < this.accounts.length; i++) { + const account = this.accounts[i]; + if (account['currency'] === code && account['type'] === 'main') { + accountId = account['id']; + break; + } + } + } + if (accountId === undefined) { + throw new ExchangeError (this.id + ' ' + code + 'main account is not loaded in loadAccounts'); + } + const request = { + 'accountId': accountId, + }; + if (since !== undefined) { + request['startAt'] = Math.floor (since / 1000); + } + const response = await this.privateGetAccountsAccountIdLedgers (this.extend (request, params)); + // + // { + // code: '200000', + // data: { + // totalNum: 1, + // totalPage: 1, + // pageSize: 50, + // currentPage: 1, + // items: [ + // { + // createdAt: 1561897880000, + // amount: '0.0111123', + // bizType: 'Exchange', + // balance: '0.13224427', + // fee: '0.0000111', + // context: '{"symbol":"KCS-ETH","orderId":"5d18ab98c788c6426188296f","tradeId":"5d18ab9818996813f539a806"}', + // currency: 'ETH', + // direction: 'out' + // } + // ] + // } + // } + // + const items = response['data']['items']; + return this.parseLedger (items, currency, since, limit); + } + + parseLedgerEntry (item, currency = undefined) { + // + // trade + // + // { + // createdAt: 1561897880000, + // amount: '0.0111123', + // bizType: 'Exchange', + // balance: '0.13224427', + // fee: '0.0000111', + // context: '{"symbol":"KCS-ETH","orderId":"5d18ab98c788c6426188296f","tradeId":"5d18ab9818996813f539a806"}', + // currency: 'ETH', + // direction: 'out' + // } + // + // withdrawal + // + // { + // createdAt: 1561900264000, + // amount: '0.14333217', + // bizType: 'Withdrawal', + // balance: '0', + // fee: '0.01', + // context: '{"orderId":"5d18b4e687111437cf1c48b9","txId":"0x1d136ee065c5c4c5caa293faa90d43e213c953d7cdd575c89ed0b54eb87228b8"}', + // currency: 'ETH', + // direction: 'out' + // } + // + const currencyId = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (currencyId, currency); + const fee = { + 'cost': this.safeFloat (item, 'fee'), + 'code': code, + }; + const amount = this.safeFloat (item, 'amount'); + const after = this.safeFloat (item, 'balance'); + const direction = this.safeString (item, 'direction'); + let before = undefined; + if (after !== undefined && amount !== undefined) { + const difference = (direction === 'out') ? amount : -amount; + before = this.sum (after, difference); + } + const timestamp = this.safeInteger (item, 'createdAt'); + const type = this.parseLedgerEntryType (this.safeString (item, 'bizType')); + const contextString = this.safeString (item, 'context'); + let id = undefined; + let referenceId = undefined; + if (this.isJsonEncodedObject (contextString)) { + const context = this.parseJson (contextString); + id = this.safeString (context, 'orderId'); + if (type === 'trade') { + referenceId = this.safeString (context, 'tradeId'); + } else if (type === 'transaction') { + referenceId = this.safeString (context, 'txId'); + } + } + return { + 'id': id, + 'currency': code, + 'account': undefined, + 'referenceAccount': undefined, + 'referenceId': referenceId, + 'status': undefined, + 'amount': amount, + 'before': before, + 'after': after, + 'fee': fee, + 'direction': direction, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'type': type, + 'info': item, + }; + } + + parseLedgerEntryType (type) { + const types = { + 'Exchange': 'trade', + 'Withdrawal': 'transaction', + 'Deposit': 'transaction', + 'Transfer': 'transfer', + }; + return this.safeString (types, type, type); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + // + // the v2 URL is https://openapi-v2.kucoin.com/api/v1/endpoint + // † ↑ + // + const version = this.safeString (params, 'version', this.options['version']); + params = this.omit (params, 'version'); + let endpoint = '/api/' + version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let endpart = ''; + headers = (headers !== undefined) ? headers : {}; + if (Object.keys (query).length) { + if (method !== 'GET') { + body = this.json (query); + endpart = body; + headers['Content-Type'] = 'application/json'; + } else { + endpoint += '?' + this.urlencode (query); + } + } + const url = this.urls['api'][api] + endpoint; + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.nonce ().toString (); + headers = this.extend ({ + 'KC-API-KEY': this.apiKey, + 'KC-API-TIMESTAMP': timestamp, + 'KC-API-PASSPHRASE': this.password, + }, headers); + const payload = timestamp + method + endpoint + endpart; + const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64'); + headers['KC-API-SIGN'] = this.decode (signature); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + this.throwBroadlyMatchedException (this.exceptions['broad'], body, body); + return; + } + // + // bad + // { "code": "400100", "msg": "validation.createOrder.clientOidIsRequired" } + // good + // { code: '200000', data: { ... }} + // + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'msg'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, message); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, message); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/kuna.js b/TaskServer/node_modules/ccxt/js/kuna.js new file mode 100644 index 000000000..63e4225c2 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/kuna.js @@ -0,0 +1,192 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const acx = require ('./acx.js'); +const { ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class kuna extends acx { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'kuna', + 'name': 'Kuna', + 'countries': [ 'UA' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchOHLCV': false, + 'fetchOpenOrders': true, + 'fetchMyTrades': true, + 'withdraw': false, + }, + 'urls': { + 'referral': 'https://kuna.io?r=kunaid-gvfihe8az7o4', + 'logo': 'https://user-images.githubusercontent.com/1294454/31697638-912824fa-b3c1-11e7-8c36-cf9606eb94ac.jpg', + 'api': 'https://kuna.io', + 'www': 'https://kuna.io', + 'doc': 'https://kuna.io/documents/api', + 'fees': 'https://kuna.io/documents/api', + }, + 'fees': { + 'trading': { + 'taker': 0.25 / 100, + 'maker': 0.25 / 100, + }, + 'funding': { + 'withdraw': { + 'UAH': '1%', + 'BTC': 0.001, + 'BCH': 0.001, + 'ETH': 0.01, + 'WAVES': 0.01, + 'GOL': 0.0, + 'GBG': 0.0, + // 'RMC': 0.001 BTC + // 'ARN': 0.01 ETH + // 'R': 0.01 ETH + // 'EVR': 0.01 ETH + }, + 'deposit': { + // 'UAH': (amount) => amount * 0.001 + 5 + }, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const quotes = [ 'btc', 'eth', 'eurs', 'rub', 'uah', 'usd', 'usdt' ]; + const pricePrecisions = { + 'UAH': 0, + }; + const markets = []; + const response = await this.publicGetTickers (params); + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + for (let j = 0; j < quotes.length; j++) { + const quoteId = quotes[j]; + const index = id.indexOf (quoteId); + const slice = id.slice (index); + if ((index > 0) && (slice === quoteId)) { + const baseId = id.replace (quoteId, ''); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 6, + 'price': this.safeInteger (pricePrecisions, quote, 6), + }; + markets.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + break; + } + } + } + return markets; + } + + async fetchL3OrderBook (symbol, limit = undefined, params = {}) { + return await this.fetchOrderBook (symbol, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privateGetOrders (this.extend (request, params)); + // todo emulation of fetchClosedOrders, fetchOrders, fetchOrder + // with order cache + fetchOpenOrders + // as in BTC-e, Liqui, Yobit, DSX, Tidex, WEX + return this.parseOrders (response, market, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + let side = this.safeString2 (trade, 'side', 'trend'); + if (side !== undefined) { + const sideMap = { + 'ask': 'sell', + 'bid': 'buy', + }; + side = this.safeString (sideMap, side); + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + const cost = this.safeFloat (trade, 'funds'); + const orderId = this.safeString (trade, 'order_id'); + const id = this.safeString (trade, 'id'); + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': orderId, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.privateGetTradesMy (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/lakebtc.js b/TaskServer/node_modules/ccxt/js/lakebtc.js new file mode 100644 index 000000000..522584224 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/lakebtc.js @@ -0,0 +1,284 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class lakebtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'lakebtc', + 'name': 'LakeBTC', + 'countries': [ 'US' ], + 'version': 'api_v2', + 'has': { + 'CORS': true, + 'createMarketOrder': false, + 'fetchTickers': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/28074120-72b7c38a-6660-11e7-92d9-d9027502281d.jpg', + 'api': 'https://api.lakebtc.com', + 'www': 'https://www.lakebtc.com', + 'doc': [ + 'https://www.lakebtc.com/s/api_v2', + 'https://www.lakebtc.com/s/api', + ], + }, + 'api': { + 'public': { + 'get': [ + 'bcorderbook', + 'bctrades', + 'ticker', + ], + }, + 'private': { + 'post': [ + 'buyOrder', + 'cancelOrders', + 'getAccountInfo', + 'getExternalAccounts', + 'getOrders', + 'getTrades', + 'openOrders', + 'sellOrder', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.15 / 100, + 'taker': 0.2 / 100, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTicker (params); + const result = []; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = response[id]; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = baseId.toUpperCase (); + const quote = quoteId.toUpperCase (); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const balances = this.safeValue (response, 'balance', {}); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balances, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetBcorderbook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + let symbol = ids[i]; + const ticker = response[symbol]; + let market = undefined; + if (symbol in this.markets_by_id) { + market = this.markets_by_id[symbol]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const tickers = await this.publicGetTicker (params); + return this.parseTicker (tickers[market['id']], market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetBctrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const method = 'privatePost' + this.capitalize (side) + 'Order'; + const market = this.market (symbol); + const order = { + 'params': [ price, amount, market['id'] ], + }; + const response = await this[method] (this.extend (order, params)); + return { + 'info': response, + 'id': this.safeString (response, 'id'), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'params': [ id ], + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + nonce () { + return this.microseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version; + if (api === 'public') { + url += '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + let queryParams = ''; + if ('params' in params) { + const paramsList = params['params']; + queryParams = paramsList.join (','); + } + const query = this.urlencode ({ + 'tonce': nonce, + 'accesskey': this.apiKey, + 'requestmethod': method.toLowerCase (), + 'id': nonce, + 'method': path, + 'params': queryParams, + }); + body = this.json ({ + 'method': path, + 'params': queryParams, + 'id': nonce, + }); + const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha1'); + const auth = this.encode (this.apiKey + ':' + signature); + headers = { + 'Json-Rpc-Tonce': nonce.toString (), + 'Authorization': 'Basic ' + this.decode (this.stringToBase64 (auth)), + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/latoken.js b/TaskServer/node_modules/ccxt/js/latoken.js new file mode 100644 index 000000000..055ec98a7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/latoken.js @@ -0,0 +1,895 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InvalidNonce, OrderNotFound, InvalidOrder, DDoSProtection, BadRequest, AuthenticationError } = require ('./base/errors'); +const { ROUND } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class latoken extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'latoken', + 'name': 'Latoken', + 'countries': [ 'KY' ], // Cayman Islands + 'version': 'v1', + 'rateLimit': 2000, + 'certified': false, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + 'publicAPI': true, + 'privateAPI': true, + 'cancelOrder': true, + 'cancelAllOrders': true, + 'createMarketOrder': false, + 'createOrder': true, + 'fetchBalance': true, + 'fetchCanceledOrders': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + 'fetchOrder': false, + 'fetchOrdersByStatus': true, + 'fetchOrderBook': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTime': true, + 'fetchTrades': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/61511972-24c39f00-aa01-11e9-9f7c-471f1d6e5214.jpg', + 'api': 'https://api.latoken.com', + 'www': 'https://latoken.com', + 'doc': [ + 'https://api.latoken.com', + ], + }, + 'api': { + 'public': { + 'get': [ + 'ExchangeInfo/time', + 'ExchangeInfo/limits', + 'ExchangeInfo/pairs', + 'ExchangeInfo/pairs/{currency}', + 'ExchangeInfo/pair', + 'ExchangeInfo/currencies', + 'ExchangeInfo/currencies/{symbol}', + 'MarketData/tickers', + 'MarketData/ticker/{symbol}', + 'MarketData/orderBook/{symbol}', + 'MarketData/orderBook/{symbol}/{limit}', + 'MarketData/trades/{symbol}', + 'MarketData/trades/{symbol}/{limit}', + ], + }, + 'private': { + 'get': [ + 'Account/balances', + 'Account/balances/{currency}', + 'Order/status', + 'Order/active', + 'Order/get_order', + 'Order/trades', + ], + 'post': [ + 'Order/new', + 'Order/test-order', + 'Order/cancel', + 'Order/cancel_all', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + }, + 'commonCurrencies': { + 'TSL': 'Treasure SL', + }, + 'options': { + 'createOrderMethod': 'private_post_order_new', // private_post_order_test_order + }, + 'exceptions': { + 'exact': { + 'Signature or ApiKey is not valid': AuthenticationError, + 'Request is out of time': InvalidNonce, + 'Symbol must be specified': BadRequest, + }, + 'broad': { + 'Request limit reached': DDoSProtection, + 'Pair': BadRequest, + 'Price needs to be greater than': InvalidOrder, + 'Amount needs to be greater than': InvalidOrder, + 'The Symbol field is required': InvalidOrder, + 'OrderType is not valid': InvalidOrder, + 'Side is not valid': InvalidOrder, + 'Cancelable order whit': OrderNotFound, + 'Order': OrderNotFound, + }, + }, + }); + } + + nonce () { + return this.milliseconds (); + } + + async fetchTime (params = {}) { + const response = await this.publicGetExchangeInfoTime (params); + // + // { + // "time": "2019-04-18T9:00:00.0Z", + // "unixTimeSeconds": 1555578000, + // "unixTimeMiliseconds": 1555578000000 + // } + // + return this.safeInteger (response, 'unixTimeMiliseconds'); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeInfoPairs (params); + // + // [ + // { + // "pairId": 502, + // "symbol": "LAETH", + // "baseCurrency": "LA", + // "quotedCurrency": "ETH", + // "makerFee": 0.01, + // "takerFee": 0.01, + // "pricePrecision": 8, + // "amountPrecision": 8, + // "minQty": 0.1 + // } + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + // the exchange shows them inverted + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quotedCurrency'); + const numericId = this.safeInteger (market, 'pairId'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'price': this.safeInteger (market, 'pricePrecision'), + 'amount': this.safeInteger (market, 'amountPrecision'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'minQty'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'numericId': numericId, + 'info': market, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, // assuming true + 'precision': precision, + 'limits': limits, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetExchangeInfoCurrencies (params); + // + // [ + // { + // "currencyId": 102, + // "symbol": "LA", + // "name": "Latoken", + // "precission": 8, + // "type": "ERC20", + // "fee": 0.1 + // } + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const numericId = this.safeInteger (currency, 'currencyId'); + const code = this.safeCurrencyCode (id); + const precision = this.safeInteger (currency, 'precission'); + const fee = this.safeFloat (currency, 'fee'); + const active = undefined; + result[code] = { + 'id': id, + 'numericId': numericId, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }, + }; + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = amount * rate; + let precision = market['precision']['price']; + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + precision = market['precision']['amount']; + } + cost = this.decimalToPrecision (cost, ROUND, precision, this.precisionMode); + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (cost), + }; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountBalances (params); + // + // [ + // { + // "currencyId": 102, + // "symbol": "LA", + // "name": "Latoken", + // "amount": 1054.66, + // "available": 900.66, + // "frozen": 154, + // "pending": 0 + // } + // ] + // + const result = { + 'info': response, + }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'symbol'); + const code = this.safeCurrencyCode (currencyId); + const frozen = this.safeFloat (balance, 'frozen'); + const pending = this.safeFloat (balance, 'pending'); + const used = this.sum (frozen, pending); + const account = { + 'free': this.safeFloat (balance, 'available'), + 'used': used, + 'total': this.safeFloat (balance, 'amount'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'limit': 10, + }; + if (limit !== undefined) { + request['limit'] = limit; // default 10, max 100 + } + const response = await this.publicGetMarketDataOrderBookSymbolLimit (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "spread": 0.07, + // "asks": [ + // { "price": 136.3, "quantity": 7.024 } + // ], + // "bids": [ + // { "price": 136.2, "quantity": 6.554 } + // ] + // } + // + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'quantity'); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "pairId":"63b41092-f3f6-4ea4-9e7c-4525ed250dad", + // "symbol":"ETHBTC", + // "volume":11317.037494474000000000, + // "open":0.020033000000000000, + // "low":0.019791000000000000, + // "high":0.020375000000000000, + // "close":0.019923000000000000, + // "priceChange":-0.1500 + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const open = this.safeFloat (ticker, 'open'); + const close = this.safeFloat (ticker, 'close'); + let change = undefined; + if (open !== undefined && close !== undefined) { + change = close - open; + } + const percentage = this.safeFloat (ticker, 'priceChange'); + const timestamp = this.nonce (); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'low': this.safeFloat (ticker, 'low'), + 'high': this.safeFloat (ticker, 'high'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetMarketDataTickerSymbol (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "volume": 1023314.3202, + // "open": 134.82, + // "low": 133.95, + // "high": 136.22, + // "close": 135.12, + // "priceChange": 0.22 + // } + // + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetMarketDataTickers (params); + // + // [ + // { + // "pairId": 502, + // "symbol": "LAETH", + // "volume": 1023314.3202, + // "open": 134.82, + // "low": 133.95, + // "high": 136.22, + // "close": 135.12, + // "priceChange": 0.22 + // } + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + if (symbols === undefined || this.inArray (symbol, symbols)) { + result[symbol] = ticker; + } + } + return result; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // side: 'buy', + // price: 0.33634, + // amount: 0.01, + // timestamp: 1564240008000 // milliseconds + // } + // + // fetchMyTrades (private) + // + // { + // id: '1564223032.892829.3.tg15', + // orderId: '1564223032.671436.707548@1379:1', + // commission: 0, + // side: 'buy', + // price: 0.32874, + // amount: 0.607, + // timestamp: 1564223033 // seconds + // } + // + const type = undefined; + let timestamp = this.safeInteger2 (trade, 'timestamp', 'time'); + if (timestamp !== undefined) { + // 03 Jan 2009 - first block + if (timestamp < 1230940800000) { + timestamp *= 1000; + } + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + const side = this.safeString (trade, 'side'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'orderId'); + const feeCost = this.safeFloat (trade, 'commission'); + let fee = undefined; + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': undefined, + }; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': orderId, + 'type': type, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 50, max 100 + } + const response = await this.publicGetMarketDataTradesSymbol (this.extend (request, params)); + // + // { + // "pairId":370, + // "symbol":"ETHBTC", + // "tradeCount":51, + // "trades": [ + // { + // side: 'buy', + // price: 0.33634, + // amount: 0.01, + // timestamp: 1564240008000 // milliseconds + // } + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.privateGetOrderTrades (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "tradeCount": 1, + // "trades": [ + // { + // id: '1564223032.892829.3.tg15', + // orderId: '1564223032.671436.707548@1379:1', + // commission: 0, + // side: 'buy', + // price: 0.32874, + // amount: 0.607, + // timestamp: 1564223033 // seconds + // } + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'active': 'open', + 'partiallyFilled': 'open', + 'filled': 'closed', + 'cancelled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "orderId":"1563460093.134037.704945@0370:2", + // "cliOrdId":"", + // "pairId":370, + // "symbol":"ETHBTC", + // "side":"sell", + // "orderType":"limit", + // "price":1.0, + // "amount":1.0 + // } + // + // cancelOrder, fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders + // + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // + const id = this.safeString (order, 'orderId'); + const timestamp = this.safeTimestamp (order, 'timeCreated'); + const marketId = this.safeString (order, 'symbol'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (order, 'side'); + const type = this.safeString (order, 'orderType'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'executedAmount'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'orderStatus')); + let cost = undefined; + if (filled !== undefined) { + if (price !== undefined) { + cost = filled * price; + } + } + const timeFilled = this.safeTimestamp (order, 'timeFilled'); + let lastTradeTimestamp = undefined; + if ((timeFilled !== undefined) && (timeFilled > 0)) { + lastTradeTimestamp = timeFilled; + } + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'average': undefined, + 'remaining': remaining, + 'fee': undefined, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersWithMethod ('private_get_order_active', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByStatus ('filled', symbol, since, limit, params); + } + + async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return this.fetchOrdersByStatus ('cancelled', symbol, since, limit, params); + } + + async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': status, + }; + return this.fetchOrdersWithMethod ('private_get_order_status', symbol, since, limit, this.extend (request, params)); + } + + async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrdersWithMethod requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default 100 + } + const response = await this[method] (this.extend (request, params)); + // + // [ + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privateGetOrderGetOrder (this.extend (request, params)); + // + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // + return this.parseOrder (response); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'symbol': this.marketId (symbol), + 'side': side, + 'price': this.priceToPrecision (symbol, price), + 'amount': this.amountToPrecision (symbol, amount), + 'orderType': type, + }; + const method = this.safeString (this.options, 'createOrderMethod', 'private_post_order_new'); + const response = await this[method] (this.extend (request, params)); + // + // { + // "orderId":"1563460093.134037.704945@0370:2", + // "cliOrdId":"", + // "pairId":370, + // "symbol":"ETHBTC", + // "side":"sell", + // "orderType":"limit", + // "price":1.0, + // "amount":1.0 + // } + // + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privatePostOrderCancel (this.extend (request, params)); + // + // { + // "orderId": "1555492358.126073.126767@0502:2", + // "cliOrdId": "myNewOrder", + // "pairId": 502, + // "symbol": "LAETH", + // "side": "buy", + // "orderType": "limit", + // "price": 136.2, + // "amount": 0.57, + // "orderStatus": "partiallyFilled", + // "executedAmount": 0.27, + // "reaminingAmount": 0.3, + // "timeCreated": 155551580736, + // "timeFilled": 0 + // } + // + return this.parseOrder (response); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument'); + } + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'symbol': marketId, + }; + const response = await this.privatePostOrderCancelAll (this.extend (request, params)); + // + // { + // "pairId": 502, + // "symbol": "LAETH", + // "cancelledOrders": [ + // "1555492358.126073.126767@0502:2" + // ] + // } + // + const result = []; + const canceledOrders = this.safeValue (response, 'cancelledOrders', []); + for (let i = 0; i < canceledOrders.length; i++) { + const order = this.parseOrder ({ + 'symbol': marketId, + 'orderId': canceledOrders[i], + 'orderStatus': 'canceled', + }); + result.push (order); + } + return result; + } + + sign (path, api = 'public', method = 'GET', params = undefined, headers = undefined, body = undefined) { + let request = '/api/' + this.version + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + const nonce = this.nonce (); + query = this.extend ({ + 'timestamp': nonce, + }, query); + } + const urlencodedQuery = this.urlencode (query); + if (Object.keys (query).length) { + request += '?' + urlencodedQuery; + } + if (api === 'private') { + this.checkRequiredCredentials (); + const signature = this.hmac (this.encode (request), this.encode (this.secret)); + headers = { + 'X-LA-KEY': this.apiKey, + 'X-LA-SIGNATURE': signature, + }; + if (method === 'POST') { + headers['Content-Type'] = 'application/x-www-form-urlencoded'; + body = urlencodedQuery; + } + } + const url = this.urls['api'] + request; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; + } + // + // { "message": "Request limit reached!", "details": "Request limit reached. Maximum allowed: 1 per 1s. Please try again in 1 second(s)." } + // { "error": { "message": "Pair 370 is not found","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Signature or ApiKey is not valid","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Request is out of time", "errorType": "RequestError", "statusCode":400 }} + // { "error": { "message": "Price needs to be greater than 0","errorType":"ValidationError","statusCode":400 }} + // { "error": { "message": "Side is not valid, Price needs to be greater than 0, Amount needs to be greater than 0, The Symbol field is required., OrderType is not valid","errorType":"ValidationError","statusCode":400 }} + // { "error": { "message": "Cancelable order whit ID 1563460289.571254.704945@0370:1 not found","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Symbol must be specified","errorType":"RequestError","statusCode":400 }} + // { "error": { "message": "Order 1563460289.571254.704945@0370:1 is not found","errorType":"RequestError","statusCode":400 }} + // + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + const error = this.safeValue (response, 'error', {}); + const errorMessage = this.safeString (error, 'message'); + if (errorMessage !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], errorMessage, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], errorMessage, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/lbank.js b/TaskServer/node_modules/ccxt/js/lbank.js new file mode 100644 index 000000000..c63e53959 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/lbank.js @@ -0,0 +1,656 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, DDoSProtection, AuthenticationError, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class lbank extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'lbank', + 'name': 'LBank', + 'countries': [ 'CN' ], + 'version': 'v1', + 'has': { + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': false, // status 0 API doesn't work + 'fetchClosedOrders': true, + }, + 'timeframes': { + '1m': 'minute1', + '5m': 'minute5', + '15m': 'minute15', + '30m': 'minute30', + '1h': 'hour1', + '2h': 'hour2', + '4h': 'hour4', + '6h': 'hour6', + '8h': 'hour8', + '12h': 'hour12', + '1d': 'day1', + '1w': 'week1', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg', + 'api': 'https://api.lbank.info', + 'www': 'https://www.lbank.info', + 'doc': 'https://github.com/LBank-exchange/lbank-official-api-docs', + 'fees': 'https://lbankinfo.zendesk.com/hc/en-gb/articles/360012072873-Trading-Fees', + 'referral': 'https://www.lbex.io/invite?icode=7QCY', + }, + 'api': { + 'public': { + 'get': [ + 'currencyPairs', + 'ticker', + 'depth', + 'trades', + 'kline', + 'accuracy', + ], + }, + 'private': { + 'post': [ + 'user_info', + 'create_order', + 'cancel_order', + 'orders_info', + 'orders_info_history', + 'withdraw', + 'withdrawCancel', + 'withdraws', + 'withdrawConfigs', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + 'funding': { + 'withdraw': { + 'BTC': undefined, + 'ZEC': 0.01, + 'ETH': 0.01, + 'ETC': 0.01, + // 'QTUM': amount => Math.max (0.01, amount * (0.1 / 100)), + 'VEN': 10.0, + 'BCH': 0.0002, + 'SC': 50.0, + 'BTM': 20.0, + 'NAS': 1.0, + 'EOS': 1.0, + 'XWC': 5.0, + 'BTS': 1.0, + 'INK': 10.0, + 'BOT': 3.0, + 'YOYOW': 15.0, + 'TGC': 10.0, + 'NEO': 0.0, + 'CMT': 20.0, + 'SEER': 2000.0, + 'FIL': undefined, + 'BTG': undefined, + }, + }, + }, + 'commonCurrencies': { + 'VET_ERC20': 'VEN', + }, + 'options': { + 'cacheSecretAsPem': true, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetAccuracy (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = market['symbol']; + const parts = id.split ('_'); + let baseId = undefined; + let quoteId = undefined; + const numParts = parts.length; + // lbank will return symbols like "vet_erc20_usdt" + if (numParts > 2) { + baseId = parts[0] + '_' + parts[1]; + quoteId = parts[2]; + } else { + baseId = parts[0]; + quoteId = parts[1]; + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'quantityAccuracy'), + 'price': this.safeInteger (market, 'priceAccuracy'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': id, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'symbol'); + if (marketId in this.markets_by_id) { + const market = this.marketsById[marketId]; + symbol = market['symbol']; + } else { + const parts = marketId.split ('_'); + let baseId = undefined; + let quoteId = undefined; + const numParts = parts.length; + // lbank will return symbols like "vet_erc20_usdt" + if (numParts > 2) { + baseId = parts[0] + '_' + parts[1]; + quoteId = parts[2]; + } else { + baseId = parts[0]; + quoteId = parts[1]; + } + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + const timestamp = this.safeInteger (ticker, 'timestamp'); + const info = ticker; + ticker = info['ticker']; + const last = this.safeFloat (ticker, 'latest'); + const percentage = this.safeFloat (ticker, 'change'); + let open = undefined; + if (percentage !== undefined) { + const relativeChange = this.sum (1, percentage / 100); + if (relativeChange > 0) { + open = last / this.sum (1, relativeChange); + } + } + let change = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + } + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': this.safeFloat (ticker, 'turnover'), + 'info': info, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': 'all', + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchOrderBook (symbol, limit = 60, params = {}) { + await this.loadMarkets (); + let size = 60; + if (limit !== undefined) { + size = Math.min (limit, size); + } + const request = { + 'symbol': this.marketId (symbol), + 'size': size, + }; + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'date_ms'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = parseFloat (this.costToPrecision (symbol, price * amount)); + } + } + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + return { + 'id': id, + 'info': this.safeValue (trade, 'info', trade), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'size': 100, + }; + if (since !== undefined) { + request['time'] = parseInt (since); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = 1000, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (since === undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a `since` argument'); + } + if (limit === undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a `limit` argument'); + } + const request = { + 'symbol': market['id'], + 'type': this.timeframes[timeframe], + 'size': limit, + 'time': parseInt (since / 1000), + }; + const response = await this.publicGetKline (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserInfo (params); + // + // { + // "result":"true", + // "info":{ + // "freeze":{ + // "iog":"0.00000000", + // "ssc":"0.00000000", + // "eon":"0.00000000", + // }, + // "asset":{ + // "iog":"0.00000000", + // "ssc":"0.00000000", + // "eon":"0.00000000", + // }, + // "free":{ + // "iog":"0.00000000", + // "ssc":"0.00000000", + // "eon":"0.00000000", + // }, + // } + // } + // + const result = { 'info': response }; + const info = this.safeValue (response, 'info', {}); + const free = this.safeValue (info, 'free', {}); + const freeze = this.safeValue (info, 'freeze', {}); + const asset = this.safeValue (info, 'asset', {}); + const currencyIds = Object.keys (free); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['used'] = this.safeFloat (freeze, currencyId); + account['total'] = this.safeFloat (asset, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + '-1': 'cancelled', // cancelled + '0': 'open', // not traded + '1': 'open', // partial deal + '2': 'closed', // complete deal + '4': 'closed', // disposal processing + }; + return this.safeString (statuses, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + const responseMarket = this.safeValue (this.marketsById, order['symbol']); + if (responseMarket !== undefined) { + symbol = responseMarket['symbol']; + } else if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (order, 'create_time'); + // Limit Order Request Returns: Order Price + // Market Order Returns: cny amount of market order + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount', 0.0); + const filled = this.safeFloat (order, 'deal_amount', 0.0); + const av_price = this.safeFloat (order, 'avg_price'); + let cost = undefined; + if (av_price !== undefined) { + cost = filled * av_price; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const id = this.safeString (order, 'order_id'); + const type = this.safeString (order, 'order_type'); + const side = this.safeString (order, 'type'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': undefined, + 'fee': undefined, + 'info': this.safeValue (order, 'info', order), + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let order = { + 'symbol': market['id'], + 'type': side, + 'amount': amount, + }; + if (type === 'market') { + order['type'] += '_market'; + } else { + order['price'] = price; + } + const response = await this.privatePostCreateOrder (this.extend (order, params)); + order = this.omit (order, 'type'); + order['order_id'] = response['order_id']; + order['type'] = side; + order['order_type'] = type; + order['create_time'] = this.milliseconds (); + order['info'] = response; + order = this.parseOrder (order, market); + const id = order['id']; + this.orders[id] = order; + return order; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'order_id': id, + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + // Id can be a list of ids delimited by a comma + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'order_id': id, + }; + const response = await this.privatePostOrdersInfo (this.extend (request, params)); + const orders = this.parseOrders (response['orders'], market); + const numOrders = orders.length; + if (numOrders === 1) { + return orders[0]; + } else { + return orders; + } + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (limit === undefined) { + limit = 100; + } + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'current_page': 1, + 'page_length': limit, + }; + const response = await this.privatePostOrdersInfoHistory (this.extend (request, params)); + return this.parseOrders (response['orders'], undefined, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + const closed = this.filterBy (orders, 'status', 'closed'); + const canceled = this.filterBy (orders, 'status', 'cancelled'); // cancelled orders may be partially filled + const allOrders = this.arrayConcat (closed, canceled); + return this.filterBySymbolSinceLimit (allOrders, symbol, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + // mark and fee are optional params, mark is a note and must be less than 255 characters + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'assetCode': currency['id'], + 'amount': amount, + 'account': address, + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const response = this.privatePostWithdraw (this.extend (request, params)); + return { + 'id': response['id'], + 'info': response, + }; + } + + convertSecretToPem (secret) { + const lineLength = 64; + const secretLength = secret.length - 0; + let numLines = parseInt (secretLength / lineLength); + numLines = this.sum (numLines, 1); + let pem = "-----BEGIN PRIVATE KEY-----\n"; // eslint-disable-line + for (let i = 0; i < numLines; i++) { + const start = i * lineLength; + const end = this.sum (start, lineLength); + pem += this.secret.slice (start, end) + "\n"; // eslint-disable-line + } + return pem + '-----END PRIVATE KEY-----'; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + // Every endpoint ends with ".do" + url += '.do'; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const query = this.keysort (this.extend ({ + 'api_key': this.apiKey, + }, params)); + const queryString = this.rawencode (query); + const message = this.hash (this.encode (queryString)).toUpperCase (); + const cacheSecretAsPem = this.safeValue (this.options, 'cacheSecretAsPem', true); + let pem = undefined; + if (cacheSecretAsPem) { + pem = this.safeValue (this.options, 'pem'); + if (pem === undefined) { + pem = this.convertSecretToPem (this.secret); + this.options['pem'] = pem; + } + } else { + pem = this.convertSecretToPem (this.secret); + } + const sign = this.binaryToBase64 (this.rsa (message, this.encode (pem), 'RS256')); + query['sign'] = sign; + body = this.urlencode (query); + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + const success = this.safeString (response, 'result'); + if (success === 'false') { + const errorCode = this.safeString (response, 'error_code'); + const message = this.safeString ({ + '10000': 'Internal error', + '10001': 'The required parameters can not be empty', + '10002': 'verification failed', + '10003': 'Illegal parameters', + '10004': 'User requests are too frequent', + '10005': 'Key does not exist', + '10006': 'user does not exist', + '10007': 'Invalid signature', + '10008': 'This currency pair is not supported', + '10009': 'Limit orders can not be missing orders and the number of orders', + '10010': 'Order price or order quantity must be greater than 0', + '10011': 'Market orders can not be missing the amount of the order', + '10012': 'market sell orders can not be missing orders', + '10013': 'is less than the minimum trading position 0.001', + '10014': 'Account number is not enough', + '10015': 'The order type is wrong', + '10016': 'Account balance is not enough', + '10017': 'Abnormal server', + '10018': 'order inquiry can not be more than 50 less than one', + '10019': 'withdrawal orders can not be more than 3 less than one', + '10020': 'less than the minimum amount of the transaction limit of 0.001', + '10022': 'Insufficient key authority', + }, errorCode, this.json (response)); + const ErrorClass = this.safeValue ({ + '10002': AuthenticationError, + '10004': DDoSProtection, + '10005': AuthenticationError, + '10006': AuthenticationError, + '10007': AuthenticationError, + '10009': InvalidOrder, + '10010': InvalidOrder, + '10011': InvalidOrder, + '10012': InvalidOrder, + '10013': InvalidOrder, + '10014': InvalidOrder, + '10015': InvalidOrder, + '10016': InvalidOrder, + '10022': AuthenticationError, + }, errorCode, ExchangeError); + throw new ErrorClass (message); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/liquid.js b/TaskServer/node_modules/ccxt/js/liquid.js new file mode 100644 index 000000000..1b7a4b66d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/liquid.js @@ -0,0 +1,929 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InvalidNonce, OrderNotFound, InvalidOrder, InsufficientFunds, AuthenticationError, DDoSProtection, NotSupported } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class liquid extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'liquid', + 'name': 'Liquid', + 'countries': [ 'JP', 'CN', 'TW' ], + 'version': '2', + 'rateLimit': 1000, + 'has': { + 'CORS': false, + 'fetchCurrencies': true, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/45798859-1a872600-bcb4-11e8-8746-69291ce87b04.jpg', + 'api': 'https://api.liquid.com', + 'www': 'https://www.liquid.com', + 'doc': [ + 'https://developers.liquid.com', + ], + 'fees': 'https://help.liquid.com/getting-started-with-liquid/the-platform/fee-structure', + 'referral': 'https://www.liquid.com?affiliate=SbzC62lt30976', + }, + 'api': { + 'public': { + 'get': [ + 'currencies', + 'products', + 'products/{id}', + 'products/{id}/price_levels', + 'executions', + 'ir_ladders/{currency}', + 'fees', // add fetchFees, fetchTradingFees, fetchFundingFees + ], + }, + 'private': { + 'get': [ + 'accounts/balance', + 'accounts/main_asset', + 'accounts/{id}', + 'accounts/{currency}/reserved_balance_details', + 'crypto_accounts', // add fetchAccounts + 'crypto_withdrawals', // add fetchWithdrawals + 'executions/me', + 'fiat_accounts', // add fetchAccounts + 'fund_infos', // add fetchDeposits + 'loan_bids', + 'loans', + 'orders', + 'orders/{id}', + 'orders/{id}/trades', // add fetchOrderTrades + 'trades', + 'trades/{id}/loans', + 'trading_accounts', + 'trading_accounts/{id}', + 'transactions', + 'withdrawals', // add fetchWithdrawals + ], + 'post': [ + 'crypto_withdrawals', + 'fund_infos', + 'fiat_accounts', + 'loan_bids', + 'orders', + 'withdrawals', + ], + 'put': [ + 'crypto_withdrawal/{id}/cancel', + 'loan_bids/{id}/close', + 'loans/{id}', + 'orders/{id}', // add editOrder + 'orders/{id}/cancel', + 'trades/{id}', + 'trades/{id}/adjust_margin', + 'trades/{id}/close', + 'trades/close_all', + 'trading_accounts/{id}', + 'withdrawals/{id}/cancel', + ], + }, + }, + 'exceptions': { + 'API rate limit exceeded. Please retry after 300s': DDoSProtection, + 'API Authentication failed': AuthenticationError, + 'Nonce is too small': InvalidNonce, + 'Order not found': OrderNotFound, + 'Can not update partially filled order': InvalidOrder, + 'Can not update non-live order': OrderNotFound, + 'not_enough_free_balance': InsufficientFunds, + 'must_be_positive': InvalidOrder, + 'less_than_order_size': InvalidOrder, + }, + 'commonCurrencies': { + 'WIN': 'WCOIN', + 'HOT': 'HOT Token', + }, + 'options': { + 'cancelOrderException': true, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // [ + // { + // currency_type: 'fiat', + // currency: 'USD', + // symbol: '$', + // assets_precision: 2, + // quoting_precision: 5, + // minimum_withdrawal: '15.0', + // withdrawal_fee: 5, + // minimum_fee: null, + // minimum_order_quantity: null, + // display_precision: 2, + // depositable: true, + // withdrawable: true, + // discount_fee: 0.5, + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'currency'); + const code = this.safeCurrencyCode (id); + const active = currency['depositable'] && currency['withdrawable']; + const amountPrecision = this.safeInteger (currency, 'display_precision'); + const pricePrecision = this.safeInteger (currency, 'quoting_precision'); + const precision = Math.max (amountPrecision, pricePrecision); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': code, + 'active': active, + 'fee': this.safeFloat (currency, 'withdrawal_fee'), + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -amountPrecision), + 'max': Math.pow (10, amountPrecision), + }, + 'price': { + 'min': Math.pow (10, -pricePrecision), + 'max': Math.pow (10, pricePrecision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimum_withdrawal'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetProducts (); + // + // [ + // { + // id: '7', + // product_type: 'CurrencyPair', + // code: 'CASH', + // name: ' CASH Trading', + // market_ask: 8865.79147, + // market_bid: 8853.95988, + // indicator: 1, + // currency: 'SGD', + // currency_pair_code: 'BTCSGD', + // symbol: 'S$', + // btc_minimum_withdraw: null, + // fiat_minimum_withdraw: null, + // pusher_channel: 'product_cash_btcsgd_7', + // taker_fee: 0, + // maker_fee: 0, + // low_market_bid: '8803.25579', + // high_market_ask: '8905.0', + // volume_24h: '15.85443468', + // last_price_24h: '8807.54625', + // last_traded_price: '8857.77206', + // last_traded_quantity: '0.00590974', + // quoted_currency: 'SGD', + // base_currency: 'BTC', + // disabled: false, + // }, + // ] + // + const currencies = await this.fetchCurrencies (); + const currenciesByCode = this.indexBy (currencies, 'code'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = market['id'].toString (); + const baseId = market['base_currency']; + const quoteId = market['quoted_currency']; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const maker = this.safeFloat (market, 'maker_fee'); + const taker = this.safeFloat (market, 'taker_fee'); + const active = !market['disabled']; + const baseCurrency = this.safeValue (currenciesByCode, base); + const quoteCurrency = this.safeValue (currenciesByCode, quote); + const precision = { + 'amount': 8, + 'price': 8, + }; + let minAmount = undefined; + if (baseCurrency !== undefined) { + minAmount = this.safeFloat (baseCurrency['info'], 'minimum_order_quantity'); + // precision['amount'] = this.safeInteger (baseCurrency['info'], 'quoting_precision'); + } + let minPrice = undefined; + if (quoteCurrency !== undefined) { + precision['price'] = this.safeInteger (quoteCurrency['info'], 'quoting_precision'); + minPrice = Math.pow (10, -precision['price']); + } + let minCost = undefined; + if (minPrice !== undefined) { + if (minAmount !== undefined) { + minCost = minPrice * minAmount; + } + } + const limits = { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'maker': maker, + 'taker': taker, + 'limits': limits, + 'precision': precision, + 'active': active, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccountsBalance (params); + // + // [ + // {"currency":"USD","balance":"0.0"}, + // {"currency":"BTC","balance":"0.0"}, + // {"currency":"ETH","balance":"0.1651354"} + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetProductsIdPriceLevels (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'buy_price_levels', 'sell_price_levels'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let last = undefined; + if ('last_traded_price' in ticker) { + if (ticker['last_traded_price']) { + const length = ticker['last_traded_price'].length; + if (length > 0) { + last = this.safeFloat (ticker, 'last_traded_price'); + } + } + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (ticker, 'id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const baseId = this.safeString (ticker, 'base_currency'); + const quoteId = this.safeString (ticker, 'quoted_currency'); + if (symbol in this.markets) { + market = this.markets[symbol]; + } else { + symbol = this.safeCurrencyCode (baseId) + '/' + this.safeCurrencyCode (quoteId); + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let change = undefined; + let percentage = undefined; + let average = undefined; + const open = this.safeFloat (ticker, 'last_price_24h'); + if (open !== undefined && last !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + if (open > 0) { + percentage = change / open * 100; + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high_market_ask'), + 'low': this.safeFloat (ticker, 'low_market_bid'), + 'bid': this.safeFloat (ticker, 'market_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'market_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'volume_24h'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetProducts (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetProductsId (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market) { + // { id: 12345, + // quantity: "6.789", + // price: "98765.4321", + // taker_side: "sell", + // created_at: 1512345678, + // my_side: "buy" } + const timestamp = this.safeTimestamp (trade, 'created_at'); + const orderId = this.safeString (trade, 'order_id'); + // 'taker_side' gets filled for both fetchTrades and fetchMyTrades + const takerSide = this.safeString (trade, 'taker_side'); + // 'my_side' gets filled for fetchMyTrades only and may differ from 'taker_side' + const mySide = this.safeString (trade, 'my_side'); + const side = (mySide !== undefined) ? mySide : takerSide; + let takerOrMaker = undefined; + if (mySide !== undefined) { + takerOrMaker = (takerSide === mySide) ? 'taker' : 'maker'; + } + let cost = undefined; + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const id = this.safeString (trade, 'id'); + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'product_id': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + // timestamp should be in seconds, whereas we use milliseconds in since and everywhere + request['timestamp'] = parseInt (since / 1000); + } + const response = await this.publicGetExecutions (this.extend (request, params)); + const result = (since !== undefined) ? response : response['models']; + return this.parseTrades (result, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + // the `with_details` param is undocumented - it adds the order_id to the results + const request = { + 'product_id': market['id'], + 'with_details': true, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetExecutionsMe (this.extend (request, params)); + return this.parseTrades (response['models'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_type': type, + 'product_id': this.marketId (symbol), + 'side': side, + 'quantity': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // "id": 2157474, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.0", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "live", + // "leverage_level": 1, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0" + // } + // + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privatePutOrdersIdCancel (this.extend (request, params)); + const order = this.parseOrder (response); + if (order['status'] === 'closed') { + if (this.options['cancelOrderException']) { + throw new OrderNotFound (this.id + ' order closed already: ' + this.json (response)); + } + } + return order; + } + + async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (price === undefined) { + throw new ArgumentsRequired (this.id + ' editOrder requires the price argument'); + } + const request = { + 'order': { + 'quantity': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + }, + 'id': id, + }; + const response = await this.privatePutOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOrderStatus (status) { + const statuses = { + 'live': 'open', + 'filled': 'closed', + 'cancelled': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "id": 2157474, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.0", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "live", + // "leverage_level": 1, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0" + // } + // + // fetchOrder, fetchOrders, fetchOpenOrders, fetchClosedOrders + // + // { + // "id": 2157479, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.01", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "filled", + // "leverage_level": 2, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0", + // "executions": [ + // { + // "id": 4566133, + // "quantity": "0.01", + // "price": "500.0", + // "taker_side": "buy", + // "my_side": "sell", + // "created_at": 1465396785 + // } + // ] + // } + // + const orderId = this.safeString (order, 'id'); + const timestamp = this.safeTimestamp (order, 'created_at'); + const marketId = this.safeString (order, 'product_id'); + market = this.safeValue (this.markets_by_id, marketId); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const amount = this.safeFloat (order, 'quantity'); + let filled = this.safeFloat (order, 'filled_quantity'); + const price = this.safeFloat (order, 'price'); + let symbol = undefined; + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + const type = this.safeString (order, 'order_type'); + let tradeCost = 0; + let tradeFilled = 0; + let average = this.safeFloat (order, 'average_price'); + const trades = this.parseTrades (this.safeValue (order, 'executions', []), market, undefined, undefined, { + 'order': orderId, + 'type': type, + }); + const numTrades = trades.length; + for (let i = 0; i < numTrades; i++) { + // php copies values upon assignment, but not references them + // todo rewrite this (shortly) + const trade = trades[i]; + trade['order'] = orderId; + trade['type'] = type; + tradeFilled = this.sum (tradeFilled, trade['amount']); + tradeCost = this.sum (tradeCost, trade['cost']); + } + let cost = undefined; + let lastTradeTimestamp = undefined; + if (numTrades > 0) { + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + if (!average && (tradeFilled > 0)) { + average = tradeCost / tradeFilled; + } + if (cost === undefined) { + cost = tradeCost; + } + if (filled === undefined) { + filled = tradeFilled; + } + } + let remaining = undefined; + if (amount !== undefined && filled !== undefined) { + remaining = amount - filled; + } + const side = this.safeString (order, 'side'); + return { + 'id': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': type, + 'status': status, + 'symbol': symbol, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'cost': cost, + 'remaining': remaining, + 'average': average, + 'trades': trades, + 'fee': { + 'currency': feeCurrency, + 'cost': this.safeFloat (order, 'order_fee'), + }, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + // 'funding_currency': market['quoteId'], // filter orders based on "funding" currency (quote currency) + // 'product_id': market['id'], + // 'status': 'live', // 'filled', 'cancelled' + // 'trading_type': 'spot', // 'margin', 'cfd' + 'with_details': 1, // return full order details including executions + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['product_id'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // { + // "models": [ + // { + // "id": 2157474, + // "order_type": "limit", + // "quantity": "0.01", + // "disc_quantity": "0.0", + // "iceberg_total_quantity": "0.0", + // "side": "sell", + // "filled_quantity": "0.0", + // "price": "500.0", + // "created_at": 1462123639, + // "updated_at": 1462123639, + // "status": "live", + // "leverage_level": 1, + // "source_exchange": "QUOINE", + // "product_id": 1, + // "product_code": "CASH", + // "funding_currency": "USD", + // "currency_pair_code": "BTCUSD", + // "order_fee": "0.0", + // "executions": [], // optional + // } + // ], + // "current_page": 1, + // "total_pages": 1 + // } + // + const orders = this.safeValue (response, 'models', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'status': 'live' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { 'status': 'filled' }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + // 'auth_code': '', // optional 2fa code + 'currency': currency['id'], + 'address': address, + 'amount': this.currencyToPrecision (code, amount), + // 'payment_id': tag, // for XRP only + // 'memo_type': 'text', // 'text', 'id' or 'hash', for XLM only + // 'memo_value': tag, // for XLM only + }; + if (tag !== undefined) { + if (code === 'XRP') { + request['payment_id'] = tag; + } else if (code === 'XLM') { + request['memo_type'] = 'text'; // overrideable via params + request['memo_value'] = tag; + } else { + throw new NotSupported (this.id + ' withdraw() only supports a tag along the address for XRP or XLM'); + } + } + const response = await this.privatePostCryptoWithdrawals (this.extend (request, params)); + // + // { + // "id": 1353, + // "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", + // "amount": 1.0, + // "state": "pending", + // "currency": "BTC", + // "withdrawal_fee": 0.0, + // "created_at": 1568016450, + // "updated_at": 1568016450, + // "payment_id": null + // } + // + return this.parseTransaction (response, currency); + } + + parseTransactionStatus (status) { + const statuses = { + 'pending': 'pending', + 'cancelled': 'canceled', + 'approved': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // withdraw + // + // { + // "id": 1353, + // "address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", + // "amount": 1.0, + // "state": "pending", + // "currency": "BTC", + // "withdrawal_fee": 0.0, + // "created_at": 1568016450, + // "updated_at": 1568016450, + // "payment_id": null + // } + // + // fetchDeposits, fetchWithdrawals + // + // ... + // + const id = this.safeString (transaction, 'id'); + const address = this.safeString (transaction, 'address'); + const tag = this.safeString2 (transaction, 'payment_id', 'memo_value'); + const txid = undefined; + const currencyId = this.safeString (transaction, 'asset'); + const code = this.safeCurrencyCode (currencyId, currency); + const timestamp = this.safeTimestamp (transaction, 'created_at'); + const updated = this.safeTimestamp (transaction, 'updated_at'); + const type = 'withdrawal'; + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const amount = this.safeFloat (transaction, 'amount'); + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'address': address, + 'tag': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + headers = { + 'X-Quoine-API-Version': this.version, + 'Content-Type': 'application/json', + }; + if (api === 'private') { + this.checkRequiredCredentials (); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (Object.keys (query).length) { + body = this.json (query); + } + const nonce = this.nonce (); + const request = { + 'path': url, + 'nonce': nonce, + 'token_id': this.apiKey, + 'iat': Math.floor (nonce / 1000), // issued at + }; + headers['X-Quoine-Auth'] = this.jwt (request, this.encode (this.secret)); + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + url = this.urls['api'] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (code >= 200 && code < 300) { + return; + } + if (code === 401) { + // expected non-json response + this.throwExactlyMatchedException (this.exceptions, body, body); + return; + } + if (code === 429) { + throw new DDoSProtection (this.id + ' ' + body); + } + if (response === undefined) { + return; + } + const feedback = this.id + ' ' + body; + const message = this.safeString (response, 'message'); + const errors = this.safeValue (response, 'errors'); + if (message !== undefined) { + // + // { "message": "Order not found" } + // + this.throwExactlyMatchedException (this.exceptions, message, feedback); + } else if (errors !== undefined) { + // + // { "errors": { "user": ["not_enough_free_balance"] }} + // { "errors": { "quantity": ["less_than_order_size"] }} + // { "errors": { "order": ["Can not update partially filled order"] }} + // + const types = Object.keys (errors); + for (let i = 0; i < types.length; i++) { + const type = types[i]; + const errorMessages = errors[type]; + for (let j = 0; j < errorMessages.length; j++) { + const message = errorMessages[j]; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + } + } + } else { + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/livecoin.js b/TaskServer/node_modules/ccxt/js/livecoin.js new file mode 100644 index 000000000..a08144a31 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/livecoin.js @@ -0,0 +1,910 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, NotSupported, InvalidOrder, OrderNotFound, ExchangeNotAvailable, RateLimitExceeded, InsufficientFunds } = require ('./base/errors'); +const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number'); + +// --------------------------------------------------------------------------- + +module.exports = class livecoin extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'livecoin', + 'name': 'LiveCoin', + 'countries': [ 'US', 'UK', 'RU' ], + 'rateLimit': 1000, + 'userAgent': this.userAgents['chrome'], + 'has': { + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'CORS': false, + 'fetchTickers': true, + 'fetchCurrencies': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchOrders': true, + 'fetchOrder': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchWithdrawals': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27980768-f22fc424-638a-11e7-89c9-6010a54ff9be.jpg', + 'api': 'https://api.livecoin.net', + 'www': 'https://www.livecoin.net', + 'doc': 'https://www.livecoin.net/api?lang=en', + 'referral': 'https://livecoin.net/?from=Livecoin-CQ1hfx44', + }, + 'api': { + 'public': { + 'get': [ + 'exchange/all/order_book', + 'exchange/last_trades', + 'exchange/maxbid_minask', + 'exchange/order_book', + 'exchange/restrictions', + 'exchange/ticker', // omit params to get all tickers at once + 'info/coinInfo', + ], + }, + 'private': { + 'get': [ + 'exchange/client_orders', + 'exchange/order', + 'exchange/trades', + 'exchange/commission', + 'exchange/commissionCommonInfo', + 'payment/balances', + 'payment/balance', + 'payment/get/address', + 'payment/history/size', + 'payment/history/transactions', + ], + 'post': [ + 'exchange/buylimit', + 'exchange/buymarket', + 'exchange/cancellimit', + 'exchange/selllimit', + 'exchange/sellmarket', + 'payment/out/capitalist', + 'payment/out/card', + 'payment/out/coin', + 'payment/out/okpay', + 'payment/out/payeer', + 'payment/out/perfectmoney', + 'payment/voucher/amount', + 'payment/voucher/make', + 'payment/voucher/redeem', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.18 / 100, + 'taker': 0.18 / 100, + }, + }, + 'commonCurrencies': { + 'BTCH': 'Bithash', + 'CPC': 'Capricoin', + 'CPT': 'Cryptos', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081 + 'EDR': 'E-Dinar Coin', // conflicts with EDR for Endor Protocol and EDRCoin + 'eETT': 'EETT', + 'FirstBlood': '1ST', + 'FORTYTWO': '42', + 'LEO': 'LeoCoin', + 'ORE': 'Orectic', + 'PLN': 'Plutaneum', // conflict with Polish Zloty + 'RUR': 'RUB', + 'SCT': 'SpaceCoin', + 'TPI': 'ThaneCoin', + 'wETT': 'WETT', + 'XBT': 'Bricktox', + }, + 'exceptions': { + 'exact': { + '1': ExchangeError, + '10': AuthenticationError, + '100': ExchangeError, // invalid parameters + '101': AuthenticationError, + '102': AuthenticationError, + '103': InvalidOrder, // invalid currency + '104': InvalidOrder, // invalid amount + '105': InvalidOrder, // unable to block funds + '11': AuthenticationError, + '12': AuthenticationError, + '2': AuthenticationError, // "User not found" + '20': AuthenticationError, + '30': AuthenticationError, + '31': NotSupported, + '32': ExchangeError, + '429': RateLimitExceeded, + '503': ExchangeNotAvailable, + }, + 'broad': { + 'insufficient funds': InsufficientFunds, // https://github.com/ccxt/ccxt/issues/5749 + 'NOT FOUND': OrderNotFound, + 'Cannot find order': OrderNotFound, + 'Minimal amount is': InvalidOrder, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetExchangeTicker (params); + const restrictions = await this.publicGetExchangeRestrictions (); + const restrictionsById = this.indexBy (restrictions['restrictions'], 'currencyPair'); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'symbol'); + const [ baseId, quoteId ] = id.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const coinRestrictions = this.safeValue (restrictionsById, symbol); + const precision = { + 'price': 5, + 'amount': 8, + 'cost': 8, + }; + const limits = { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + }; + if (coinRestrictions) { + precision['price'] = this.safeInteger (coinRestrictions, 'priceScale', 5); + limits['amount']['min'] = this.safeFloat (coinRestrictions, 'minLimitQuantity', limits['amount']['min']); + } + limits['price'] = { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetInfoCoinInfo (params); + const currencies = this.safeValue (response, 'info'); + let result = {}; + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'symbol'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (id); + const precision = 8; // default precision, todo: fix "magic constants" + const walletStatus = this.safeString (currency, 'walletStatus'); + const active = (walletStatus === 'normal'); + const name = this.safeString (currency, 'name'); + const fee = this.safeFloat (currency, 'withdrawFee'); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'name': name, + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (currency, 'minOrderAmount'), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': this.safeFloat (currency, 'minOrderAmount'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawAmount'), + 'max': Math.pow (10, precision), + }, + 'deposit': { + 'min': this.safeFloat (currency, 'minDepositAmount'), + 'max': undefined, + }, + }, + }; + } + result = this.appendFiatCurrencies (result); + return result; + } + + appendFiatCurrencies (result) { + const precision = 8; + const defaults = { + 'info': undefined, + 'active': true, + 'fee': undefined, + 'precision': precision, + 'limits': { + 'withdraw': { 'min': undefined, 'max': undefined }, + 'deposit': { 'min': undefined, 'max': undefined }, + 'amount': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + }, + }; + const currencies = [ + { 'id': 'USD', 'code': 'USD', 'name': 'US Dollar' }, + { 'id': 'EUR', 'code': 'EUR', 'name': 'Euro' }, + // { 'id': 'RUR', 'code': 'RUB', 'name': 'Russian ruble' }, + ]; + currencies.push ({ + 'id': 'RUR', + 'code': this.safeCurrencyCode ('RUR'), + 'name': 'Russian ruble', + }); + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const code = currency['code']; + result[code] = this.extend (defaults, currency); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetPaymentBalances (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + let account = undefined; + if (code in result) { + account = result[code]; + } else { + account = this.account (); + } + if (balance['type'] === 'total') { + account['total'] = this.safeFloat (balance, 'value'); + } + if (balance['type'] === 'available') { + account['free'] = this.safeFloat (balance, 'value'); + } + if (balance['type'] === 'trade') { + account['used'] = this.safeFloat (balance, 'value'); + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetExchangeCommissionCommonInfo (params); + const commission = this.safeFloat (response, 'commission'); + return { + 'info': response, + 'maker': commission, + 'taker': commission, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + 'groupByPrice': 'false', + }; + if (limit !== undefined) { + request['depth'] = limit; // 100 + } + const response = await this.publicGetExchangeOrderBook (this.extend (request, params)); + const timestamp = this.safeInteger (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'best_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'best_ask'), + 'askVolume': undefined, + 'vwap': this.safeFloat (ticker, 'vwap'), + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetExchangeTicker (params); + const tickers = this.indexBy (response, 'symbol'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + const ticker = await this.publicGetExchangeTicker (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "time": 1409935047, + // "id": 99451, + // "price": 350, + // "quantity": 2.85714285, + // "type": "BUY" + // } + // + // fetchMyTrades (private) + // + // { + // "datetime": 1435844369, + // "id": 30651619, + // "type": "sell", + // "symbol": "BTC/EUR", + // "price": 230, + // "quantity": 0.1, + // "commission": 0, + // "clientorderid": 1472837650 + // } + const timestamp = this.safeTimestamp2 (trade, 'time', 'datetime'); + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrency = market ? market['quote'] : undefined; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + const orderId = this.safeString (trade, 'clientorderid'); + const id = this.safeString (trade, 'id'); + const side = this.safeStringLower (trade, 'type'); + const amount = this.safeFloat (trade, 'quantity'); + const price = this.safeFloat (trade, 'price'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + // orderDesc': 'true', // or 'false', if true then new orders will be first, otherwise old orders will be first. + // 'offset': 0, // page offset, position of the first item on the page + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetExchangeTrades (this.extend (request, params)); + // + // [ + // { + // "datetime": 1435844369, + // "id": 30651619, + // "type": "sell", + // "symbol": "BTC/EUR", + // "price": 230, + // "quantity": 0.1, + // "commission": 0, + // "clientorderid": 1472837650 + // }, + // { + // "datetime": 1435844356, + // "id": 30651618, + // "type": "sell", + // "symbol": "BTC/EUR", + // "price": 230, + // "quantity": 0.2, + // "commission": 0.092, + // "clientorderid": 1472837651 + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + const response = await this.publicGetExchangeLastTrades (this.extend (request, params)); + // + // [ + // { + // "time": 1409935047, + // "id": 99451, + // "price": 350, + // "quantity": 2.85714285, + // "type": "BUY" + // }, + // { + // "time": 1409934792, + // "id": 99450, + // "price": 350, + // "quantity": 0.57142857, + // "type": "SELL" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.privateGetExchangeOrder (this.extend (request, params)); + return this.parseOrder (response); + } + + parseOrderStatus (status) { + const statuses = { + 'OPEN': 'open', + 'PARTIALLY_FILLED': 'open', + 'EXECUTED': 'closed', + 'CANCELLED': 'canceled', + 'PARTIALLY_FILLED_AND_CANCELLED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let timestamp = undefined; + if ('lastModificationTime' in order) { + timestamp = this.safeString (order, 'lastModificationTime'); + if (timestamp !== undefined) { + if (timestamp.indexOf ('T') >= 0) { + timestamp = this.parse8601 (timestamp); + } else { + timestamp = this.safeInteger (order, 'lastModificationTime'); + } + } + } + // TODO currently not supported by livecoin + // let trades = this.parseTrades (order['trades'], market, since, limit); + const trades = undefined; + const status = this.parseOrderStatus (this.safeString2 (order, 'status', 'orderStatus')); + let symbol = undefined; + if (market === undefined) { + let marketId = this.safeString (order, 'currencyPair'); + marketId = this.safeString (order, 'symbol', marketId); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let type = this.safeStringLower (order, 'type'); + let side = undefined; + if (type !== undefined) { + const orderType = type.split ('_'); + type = orderType[0]; + side = orderType[1]; + } + const price = this.safeFloat (order, 'price'); + // of the next two lines the latter overrides the former, if present in the order structure + let remaining = this.safeFloat (order, 'remainingQuantity'); + remaining = this.safeFloat (order, 'remaining_quantity', remaining); + const amount = this.safeFloat (order, 'quantity', remaining); + let filled = undefined; + if (remaining !== undefined) { + filled = amount - remaining; + } + let cost = undefined; + if (filled !== undefined && price !== undefined) { + cost = filled * price; + } + const feeRate = this.safeFloat (order, 'commission_rate'); + let feeCost = undefined; + if (cost !== undefined && feeRate !== undefined) { + feeCost = cost * feeRate; + } + let feeCurrency = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } + return { + 'info': order, + 'id': order['id'], + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': { + 'cost': feeCost, + 'currency': feeCurrency, + 'rate': feeRate, + }, + }; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = {}; + if (symbol !== undefined) { + market = this.market (symbol); + request['currencyPair'] = market['id']; + } + if (since !== undefined) { + request['issuedFrom'] = parseInt (since); + } + if (limit !== undefined) { + request['endRow'] = limit - 1; + } + const response = await this.privateGetExchangeClientOrders (this.extend (request, params)); + const result = []; + let rawOrders = []; + if (response['data']) { + rawOrders = response['data']; + } + for (let i = 0; i < rawOrders.length; i++) { + const order = rawOrders[i]; + result.push (this.parseOrder (order, market)); + } + return this.sortBy (result, 'timestamp'); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openClosed': 'OPEN', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openClosed': 'CLOSED', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const method = 'privatePostExchange' + this.capitalize (side) + type; + const market = this.market (symbol); + const request = { + 'quantity': this.amountToPrecision (symbol, amount), + 'currencyPair': market['id'], + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this[method] (this.extend (request, params)); + const result = { + 'info': response, + 'id': response['orderId'].toString (), + }; + const success = this.safeValue (response, 'success'); + if (success) { + result['status'] = 'open'; + } + return result; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'orderId': id, + 'currencyPair': market['id'], + }; + const response = await this.privatePostExchangeCancellimit (this.extend (request, params)); + const message = this.safeString (response, 'message', this.json (response)); + if ('success' in response) { + if (!response['success']) { + throw new InvalidOrder (message); + } else if ('cancelled' in response) { + if (response['cancelled']) { + return { + 'status': 'canceled', + 'info': response, + }; + } else { + throw new OrderNotFound (message); + } + } + } + throw new ExchangeError (this.id + ' cancelOrder() failed: ' + this.json (response)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + // Sometimes the response with be { key: null } for all keys. + // An example is if you attempt to withdraw more than is allowed when withdrawal fees are considered. + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + let wallet = address; + if (tag !== undefined) { + wallet += '::' + tag; + } + const request = { + 'amount': this.decimalToPrecision (amount, TRUNCATE, currency['precision'], DECIMAL_PLACES), + 'currency': currency['id'], + 'wallet': wallet, + }; + const response = await this.privatePostPaymentOutCoin (this.extend (request, params)); + const id = this.safeInteger (response, 'id'); + if (id === undefined) { + throw new InsufficientFunds (this.id + ' insufficient funds to cover requested withdrawal amount post fees ' + this.json (response)); + } + return { + 'info': response, + 'id': id, + }; + } + + parseTransaction (transaction, currency = undefined) { + // { + // "id": "c853093d5aa06df1c92d79c2...", (tx on deposits, address on withdrawals) + // "type": "DEPOSIT", + // "date": 1553186482676, + // "amount": 712.61266, + // "fee": 0, + // "fixedCurrency": "XVG", + // "taxCurrency": "XVG", + // "variableAmount": null, + // "variableCurrency": null, + // "external": "Coin", + // "login": "USERNAME", + // "externalKey": "....87diPBy......3hTtuwUT78Yi", (address on deposits, tx on withdrawals) + // "documentId": 1110662453 + // }, + let txid = undefined; + let address = undefined; + const id = this.safeString (transaction, 'documentId'); + const amount = this.safeFloat (transaction, 'amount'); + const timestamp = this.safeInteger (transaction, 'date'); + const type = this.safeStringLower (transaction, 'type'); + const currencyId = this.safeString (transaction, 'fixedCurrency'); + const feeCost = this.safeFloat (transaction, 'fee'); + const code = this.safeCurrencyCode (currencyId, currency); + if (type === 'withdrawal') { + txid = this.safeString (transaction, 'externalKey'); + address = this.safeString (transaction, 'id'); + } else if (type === 'deposit') { + address = this.safeString (transaction, 'externalKey'); + txid = this.safeString (transaction, 'id'); + } + let status = undefined; + if (type === 'deposit') { + status = 'ok'; // Deposits is not registered until they are in account. Withdrawals are left as undefined, not entirely sure about theyre status. + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const endtime = 2505600000; // 29 days - exchange has maximum 30 days. + const now = this.milliseconds (); + const request = { + 'types': 'DEPOSIT', + 'end': now, + 'start': (since !== undefined) ? parseInt (since) : now - endtime, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + const response = await this.privateGetPaymentHistoryTransactions (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const endtime = 2505600000; // 29 days - exchange has maximum 30 days. + const now = this.milliseconds (); + const request = { + 'types': 'WITHDRAWAL', + 'end': now, + 'start': (since !== undefined) ? parseInt (since) : now - endtime, + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + if (since !== undefined) { + request['start'] = since; + } + const response = await this.privateGetPaymentHistoryTransactions (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit); + } + + async fetchDepositAddress (currency, params = {}) { + const request = { + 'currency': currency, + }; + const response = await this.privateGetPaymentGetAddress (this.extend (request, params)); + let address = this.safeString (response, 'wallet'); + let tag = undefined; + if (address.indexOf (':') >= 0) { + const parts = address.split (':'); + address = parts[0]; + tag = parts[2]; + } + this.checkAddress (address); + return { + 'currency': currency, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + path; + const query = this.urlencode (this.keysort (params)); + if (method === 'GET') { + if (Object.keys (params).length) { + url += '?' + query; + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + if (method === 'POST') { + body = query; + } + const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha256'); + headers = { + 'Api-Key': this.apiKey, + 'Sign': signature.toUpperCase (), + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if (code >= 300) { + const feedback = this.id + ' ' + body; + const errorCode = this.safeString (response, 'errorCode'); + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + throw new ExchangeError (feedback); + } + // returns status code 200 even if success === false + const success = this.safeValue (response, 'success', true); + if (!success) { + const feedback = this.id + ' ' + body; + const message = this.safeString2 (response, 'message', 'exception'); + if (message !== undefined) { + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + throw new ExchangeError (feedback); + } + } +}; + diff --git a/TaskServer/node_modules/ccxt/js/luno.js b/TaskServer/node_modules/ccxt/js/luno.js new file mode 100644 index 000000000..aa6986fdd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/luno.js @@ -0,0 +1,455 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class luno extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'luno', + 'name': 'luno', + 'countries': [ 'GB', 'SG', 'ZA' ], + 'rateLimit': 1000, + 'version': '1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + }, + 'urls': { + 'referral': 'https://www.luno.com/invite/44893A', + 'logo': 'https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg', + 'api': 'https://api.mybitx.com/api', + 'www': 'https://www.luno.com', + 'doc': [ + 'https://www.luno.com/en/api', + 'https://npmjs.org/package/bitx', + 'https://github.com/bausmeier/node-bitx', + ], + }, + 'api': { + 'public': { + 'get': [ + 'orderbook', + 'orderbook_top', + 'ticker', + 'tickers', + 'trades', + ], + }, + 'private': { + 'get': [ + 'accounts/{id}/pending', + 'accounts/{id}/transactions', + 'balance', + 'fee_info', + 'funding_address', + 'listorders', + 'listtrades', + 'orders/{id}', + 'quotes/{id}', + 'withdrawals', + 'withdrawals/{id}', + ], + 'post': [ + 'accounts', + 'postorder', + 'marketorder', + 'stoporder', + 'funding_address', + 'withdrawals', + 'send', + 'quotes', + 'oauth2/grant', + ], + 'put': [ + 'quotes/{id}', + ], + 'delete': [ + 'quotes/{id}', + 'withdrawals/{id}', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetTickers (params); + const result = []; + for (let i = 0; i < response['tickers'].length; i++) { + const market = response['tickers'][i]; + const id = market['pair']; + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const wallets = this.safeValue (response, 'balance', []); + const result = { 'info': response }; + for (let i = 0; i < wallets.length; i++) { + const wallet = wallets[i]; + const currencyId = this.safeString (wallet, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const reserved = this.safeFloat (wallet, 'reserved'); + const unconfirmed = this.safeFloat (wallet, 'unconfirmed'); + const balance = this.safeFloat (wallet, 'balance'); + const account = this.account (); + account['used'] = this.sum (reserved, unconfirmed); + account['total'] = this.sum (balance, unconfirmed); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + let method = 'publicGetOrderbook'; + if (limit !== undefined) { + if (limit <= 100) { + method += 'Top'; // get just the top of the orderbook when limit is low + } + } + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this[method] (this.extend (request, params)); + const timestamp = this.safeInteger (response, 'timestamp'); + return this.parseOrderBook (response, timestamp, 'bids', 'asks', 'price', 'volume'); + } + + parseOrder (order, market = undefined) { + const timestamp = this.safeInteger (order, 'creation_timestamp'); + const status = (order['state'] === 'PENDING') ? 'open' : 'closed'; + const side = (order['type'] === 'ASK') ? 'sell' : 'buy'; + const marketId = this.safeString (order, 'pair'); + let symbol = undefined; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (order, 'limit_price'); + const amount = this.safeFloat (order, 'limit_volume'); + const quoteFee = this.safeFloat (order, 'fee_counter'); + const baseFee = this.safeFloat (order, 'fee_base'); + const filled = this.safeFloat (order, 'base'); + const cost = this.safeFloat (order, 'counter'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + const fee = { 'currency': undefined }; + if (quoteFee) { + fee['cost'] = quoteFee; + if (market !== undefined) { + fee['currency'] = market['quote']; + } + } else { + fee['cost'] = baseFee; + if (market !== undefined) { + fee['currency'] = market['base']; + } + } + const id = this.safeString (order, 'order_id'); + return { + 'id': id, + 'datetime': this.iso8601 (timestamp), + 'timestamp': timestamp, + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'cost': cost, + 'remaining': remaining, + 'trades': undefined, + 'fee': fee, + 'info': order, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrdersByState (state = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (state !== undefined) { + request['state'] = state; + } + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privateGetListorders (this.extend (request, params)); + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState (undefined, symbol, since, limit, params); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('PENDING', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('COMPLETE', symbol, since, limit, params); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last_trade'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'rolling_24_hour_volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const tickers = this.indexBy (response['tickers'], 'pair'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market) { + // For public trade data (is_buy === True) indicates 'buy' side but for private trade data + // is_buy indicates maker or taker. The value of "type" (ASK/BID) indicate sell/buy side. + // Private trade data includes ID field which public trade data does not. + const orderId = this.safeString (trade, 'order_id'); + let takerOrMaker = undefined; + let side = undefined; + if (orderId !== undefined) { + side = (trade['type'] === 'ASK') ? 'sell' : 'buy'; + if (side === 'sell' && trade['is_buy']) { + takerOrMaker = 'maker'; + } else if (side === 'buy' && !trade['is_buy']) { + takerOrMaker = 'maker'; + } else { + takerOrMaker = 'taker'; + } + } else { + side = trade['is_buy'] ? 'buy' : 'sell'; + } + const feeBase = this.safeFloat (trade, 'fee_base'); + const feeCounter = this.safeFloat (trade, 'fee_counter'); + let feeCurrency = undefined; + let feeCost = undefined; + if (feeBase !== undefined) { + if (feeBase !== 0.0) { + feeCurrency = market['base']; + feeCost = feeBase; + } + } else if (feeCounter !== undefined) { + if (feeCounter !== 0.0) { + feeCurrency = market['quote']; + feeCost = feeCounter; + } + } + const timestamp = this.safeInteger (trade, 'timestamp'); + return { + 'info': trade, + 'id': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': market['symbol'], + 'order': orderId, + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': this.safeFloat (trade, 'price'), + 'amount': this.safeFloat (trade, 'volume'), + // Does not include potential fee costs + 'cost': this.safeFloat (trade, 'counter'), + 'fee': { + 'cost': feeCost, + 'currency': feeCurrency, + }, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (since !== undefined) { + request['since'] = since; + } + const response = await this.publicGetTrades (this.extend (request, params)); + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (since !== undefined) { + request['since'] = since; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetListtrades (this.extend (request, params)); + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetFeeInfo (params); + return { + 'info': response, + 'maker': this.safeFloat (response, 'maker_fee'), + 'taker': this.safeFloat (response, 'taker_fee'), + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + let method = 'privatePost'; + const request = { + 'pair': this.marketId (symbol), + }; + if (type === 'market') { + method += 'Marketorder'; + request['type'] = side.toUpperCase (); + if (side === 'buy') { + request['counter_volume'] = amount; + } else { + request['base_volume'] = amount; + } + } else { + method += 'Postorder'; + request['volume'] = amount; + request['price'] = price; + request['type'] = (side === 'buy') ? 'BID' : 'ASK'; + } + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['order_id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': id, + }; + return await this.privatePostStoporder (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + if (api === 'private') { + this.checkRequiredCredentials (); + let auth = this.encode (this.apiKey + ':' + this.secret); + auth = this.stringToBase64 (auth); + headers = { 'Authorization': 'Basic ' + this.decode (auth) }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/lykke.js b/TaskServer/node_modules/ccxt/js/lykke.js new file mode 100644 index 000000000..a18711500 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/lykke.js @@ -0,0 +1,458 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class lykke extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'lykke', + 'name': 'Lykke', + 'countries': [ 'CH' ], + 'version': 'v1', + 'rateLimit': 200, + 'has': { + 'CORS': false, + 'fetchOHLCV': false, + 'fetchTrades': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/34487620-3139a7b0-efe6-11e7-90f5-e520cef74451.jpg', + 'api': { + 'mobile': 'https://public-api.lykke.com/api', + 'public': 'https://hft-api.lykke.com/api', + 'private': 'https://hft-api.lykke.com/api', + }, + 'test': { + 'mobile': 'https://public-api.lykke.com/api', + 'public': 'https://hft-service-dev.lykkex.net/api', + 'private': 'https://hft-service-dev.lykkex.net/api', + }, + 'www': 'https://www.lykke.com', + 'doc': [ + 'https://hft-api.lykke.com/swagger/ui/', + 'https://www.lykke.com/lykke_api', + ], + 'fees': 'https://www.lykke.com/trading-conditions', + }, + 'api': { + 'mobile': { + 'get': [ + 'Market/{market}', + 'Trades/{AssetPairId}', + ], + }, + 'public': { + 'get': [ + 'AssetPairs', + 'AssetPairs/{id}', + 'IsAlive', + 'OrderBooks', + 'OrderBooks/{AssetPairId}', + ], + }, + 'private': { + 'get': [ + 'Orders', + 'Orders/{id}', + 'Wallets', + ], + 'post': [ + 'Orders/limit', + 'Orders/market', + 'Orders/{id}/Cancel', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0, // as of 7 Feb 2018, see https://github.com/ccxt/ccxt/issues/1863 + 'taker': 0.0, // https://www.lykke.com/cp/wallet-fees-and-limits + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.001, + }, + 'deposit': { + 'BTC': 0, + }, + }, + }, + 'commonCurrencies': { + 'XPD': 'Lykke XPD', + }, + }); + } + + parseTrade (trade, market) { + // + // public fetchTrades + // + // { + // "id": "d5983ab8-e9ec-48c9-bdd0-1b18f8e80a71", + // "assetPairId": "BTCUSD", + // "dateTime": "2019-05-15T06:52:02.147Z", + // "volume": 0.00019681, + // "index": 0, + // "price": 8023.333, + // "action": "Buy" + // } + // + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'AssetPairId'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + const timestamp = this.parse8601 (this.safeString (trade, 'dateTime')); + const side = this.safeStringLower (trade, 'action'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + const cost = price * amount; + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 100; + } + const request = { + 'AssetPairId': market['id'], + 'skip': 0, + 'take': limit, + }; + const response = await this.mobileGetTradesAssetPairId (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetWallets (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'AssetId'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'Balance'); + account['used'] = this.safeFloat (balance, 'Reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + return await this.privatePostOrdersIdCancel ({ 'id': id }); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const query = { + 'AssetPairId': market['id'], + 'OrderAction': this.capitalize (side), + 'Volume': amount, + }; + if (type === 'market') { + query['Asset'] = (side === 'buy') ? market['base'] : market['quote']; + } else if (type === 'limit') { + query['Price'] = price; + } + const method = 'privatePostOrders' + this.capitalize (type); + const result = await this[method] (this.extend (query, params)); + return { + 'id': undefined, + 'info': result, + }; + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetAssetPairs (); + // + // [ { Id: "AEBTC", + // Name: "AE/BTC", + // Accuracy: 6, + // InvertedAccuracy: 8, + // BaseAssetId: "6f75280b-a005-4016-a3d8-03dc644e8912", + // QuotingAssetId: "BTC", + // MinVolume: 0.4, + // MinInvertedVolume: 0.0001 }, + // { Id: "AEETH", + // Name: "AE/ETH", + // Accuracy: 6, + // InvertedAccuracy: 8, + // BaseAssetId: "6f75280b-a005-4016-a3d8-03dc644e8912", + // QuotingAssetId: "ETH", + // MinVolume: 0.4, + // MinInvertedVolume: 0.001 } ] + // + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'Id'); + const name = this.safeString (market, 'Name'); + const [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'Accuracy'), + 'price': this.safeInteger (market, 'InvertedAccuracy'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'active': true, + 'info': market, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['amount']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const close = this.safeFloat (ticker, 'lastPrice'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': close, + 'last': close, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': undefined, + 'quoteVolume': this.safeFloat (ticker, 'volume24H'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const ticker = await this.mobileGetMarketMarket (this.extend (request, params)); + return this.parseTicker (ticker, market); + } + + parseOrderStatus (status) { + const statuses = { + 'Open': 'open', + 'Pending': 'open', + 'InOrderBook': 'open', + 'Processing': 'open', + 'Matched': 'closed', + 'Cancelled': 'canceled', + 'Rejected': 'rejected', + 'Replaced': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeString (order, 'Status')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'AssetPairId'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market) { + symbol = market['symbol']; + } + const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'LastMatchTime')); + let timestamp = undefined; + if (('Registered' in order) && (order['Registered'])) { + timestamp = this.parse8601 (order['Registered']); + } else if (('CreatedAt' in order) && (order['CreatedAt'])) { + timestamp = this.parse8601 (order['CreatedAt']); + } + const price = this.safeFloat (order, 'Price'); + const amount = this.safeFloat (order, 'Volume'); + const remaining = this.safeFloat (order, 'RemainingVolume'); + const filled = amount - remaining; + const cost = filled * price; + const id = this.safeString (order, 'Id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'price': price, + 'cost': cost, + 'average': undefined, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const response = await this.privateGetOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetOrders (params); + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'InOrderBook', + }; + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, undefined, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'status': 'Matched', + }; + const response = await this.privateGetOrders (this.extend (request, params)); + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetOrderBooksAssetPairId (this.extend ({ + 'AssetPairId': this.marketId (symbol), + }, params)); + const orderbook = { + 'timestamp': undefined, + 'bids': [], + 'asks': [], + }; + let timestamp = undefined; + for (let i = 0; i < response.length; i++) { + const side = response[i]; + if (side['IsBuy']) { + orderbook['bids'] = this.arrayConcat (orderbook['bids'], side['Prices']); + } else { + orderbook['asks'] = this.arrayConcat (orderbook['asks'], side['Prices']); + } + const sideTimestamp = this.parse8601 (side['Timestamp']); + timestamp = (timestamp === undefined) ? sideTimestamp : Math.max (timestamp, sideTimestamp); + } + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'Price', 'Volume'); + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1) { + const price = this.safeFloat (bidask, priceKey); + let amount = this.safeFloat (bidask, amountKey); + if (amount < 0) { + amount = -amount; + } + return [ price, amount ]; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'mobile') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'private') { + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + this.checkRequiredCredentials (); + headers = { + 'api-key': this.apiKey, + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }; + if (method === 'POST') { + if (Object.keys (params).length) { + body = this.json (params); + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/mercado.js b/TaskServer/node_modules/ccxt/js/mercado.js new file mode 100644 index 000000000..ed02fd5b2 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/mercado.js @@ -0,0 +1,506 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class mercado extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'mercado', + 'name': 'Mercado Bitcoin', + 'countries': [ 'BR' ], // Brazil + 'rateLimit': 1000, + 'version': 'v3', + 'has': { + 'CORS': true, + 'createMarketOrder': true, + 'fetchOrder': true, + 'withdraw': true, + 'fetchOHLCV': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchTicker': true, + 'fetchTickers': false, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '6h': '6h', + '12h': '12h', + '1d': '1d', + '3d': '3d', + '1w': '1w', + '2w': '2w', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27837060-e7c58714-60ea-11e7-9192-f05e86adb83f.jpg', + 'api': { + 'public': 'https://www.mercadobitcoin.net/api', + 'private': 'https://www.mercadobitcoin.net/tapi', + 'v4Public': 'https://www.mercadobitcoin.com.br/v4', + }, + 'www': 'https://www.mercadobitcoin.com.br', + 'doc': [ + 'https://www.mercadobitcoin.com.br/api-doc', + 'https://www.mercadobitcoin.com.br/trade-api', + ], + }, + 'api': { + 'public': { + 'get': [ + '{coin}/orderbook/', // last slash critical + '{coin}/ticker/', + '{coin}/trades/', + '{coin}/trades/{from}/', + '{coin}/trades/{from}/{to}', + '{coin}/day-summary/{year}/{month}/{day}/', + ], + }, + 'private': { + 'post': [ + 'cancel_order', + 'get_account_info', + 'get_order', + 'get_withdrawal', + 'list_system_messages', + 'list_orders', + 'list_orderbook', + 'place_buy_order', + 'place_sell_order', + 'place_market_buy_order', + 'place_market_sell_order', + 'withdraw_coin', + ], + }, + 'v4Public': { + 'get': [ + '{coin}/candle/', + ], + }, + }, + 'markets': { + 'BTC/BRL': { 'id': 'BRLBTC', 'symbol': 'BTC/BRL', 'base': 'BTC', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Bitcoin' }, + 'LTC/BRL': { 'id': 'BRLLTC', 'symbol': 'LTC/BRL', 'base': 'LTC', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Litecoin' }, + 'BCH/BRL': { 'id': 'BRLBCH', 'symbol': 'BCH/BRL', 'base': 'BCH', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'BCash' }, + 'XRP/BRL': { 'id': 'BRLXRP', 'symbol': 'XRP/BRL', 'base': 'XRP', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Ripple' }, + 'ETH/BRL': { 'id': 'BRLETH', 'symbol': 'ETH/BRL', 'base': 'ETH', 'quote': 'BRL', 'precision': { 'amount': 8, 'price': 5 }, 'suffix': 'Ethereum' }, + }, + 'fees': { + 'trading': { + 'maker': 0.3 / 100, + 'taker': 0.7 / 100, + }, + }, + }); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['base'], + }; + const response = await this.publicGetCoinOrderbook (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin': market['base'], + }; + const response = await this.publicGetCoinTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'ticker', {}); + const timestamp = this.safeTimestamp (ticker, 'date'); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = 'publicGetCoinTrades'; + const request = { + 'coin': market['base'], + }; + if (since !== undefined) { + method += 'From'; + request['from'] = parseInt (since / 1000); + } + const to = this.safeInteger (params, 'to'); + if (to !== undefined) { + method += 'To'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetAccountInfo (params); + const data = this.safeValue (response, 'response_data', {}); + const balances = this.safeValue (data, 'balance', {}); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + if (currencyId in balances) { + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['total'] = this.safeFloat (balance, 'total'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'coin_pair': this.marketId (symbol), + }; + let method = this.capitalize (side) + 'Order'; + if (type === 'limit') { + method = 'privatePostPlace' + method; + request['limit_price'] = this.priceToPrecision (symbol, price); + request['quantity'] = this.amountToPrecision (symbol, amount); + } else { + method = 'privatePostPlaceMarket' + method; + if (side === 'buy') { + if (price === undefined) { + throw new InvalidOrder (this.id + ' createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount'); + } + request['cost'] = this.priceToPrecision (symbol, amount * price); + } else { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + } + const response = await this[method] (this.extend (request, params)); + // TODO: replace this with a call to parseOrder for unification + return { + 'info': response, + 'id': response['response_data']['order']['order_id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder () requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin_pair': market['id'], + 'order_id': id, + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + // + // { + // response_data: { + // order: { + // order_id: 2176769, + // coin_pair: 'BRLBCH', + // order_type: 2, + // status: 3, + // has_fills: false, + // quantity: '0.10000000', + // limit_price: '1996.15999', + // executed_quantity: '0.00000000', + // executed_price_avg: '0.00000', + // fee: '0.00000000', + // created_timestamp: '1536956488', + // updated_timestamp: '1536956499', + // operations: [] + // } + // }, + // status_code: 100, + // server_unix_timestamp: '1536956499' + // } + // + const responseData = this.safeValue (response, 'response_data', {}); + const order = this.safeValue (responseData, 'order', {}); + return this.parseOrder (order, market); + } + + parseOrderStatus (status) { + const statuses = { + '2': 'open', + '3': 'canceled', + '4': 'closed', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "order_id": 4, + // "coin_pair": "BRLBTC", + // "order_type": 1, + // "status": 2, + // "has_fills": true, + // "quantity": "2.00000000", + // "limit_price": "900.00000", + // "executed_quantity": "1.00000000", + // "executed_price_avg": "900.00000", + // "fee": "0.00300000", + // "created_timestamp": "1453838494", + // "updated_timestamp": "1453838494", + // "operations": [ + // { + // "operation_id": 1, + // "quantity": "1.00000000", + // "price": "900.00000", + // "fee_rate": "0.30", + // "executed_timestamp": "1453838494", + // }, + // ], + // } + // + const id = this.safeString (order, 'order_id'); + let side = undefined; + if ('order_type' in order) { + side = (order['order_type'] === 1) ? 'buy' : 'sell'; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'coin_pair'); + market = this.safeValue (this.markets_by_id, marketId); + } + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'created_timestamp'); + const fee = { + 'cost': this.safeFloat (order, 'fee'), + 'currency': market['quote'], + }; + const price = this.safeFloat (order, 'limit_price'); + // price = this.safeFloat (order, 'executed_price_avg', price); + const average = this.safeFloat (order, 'executed_price_avg'); + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'executed_quantity'); + const remaining = amount - filled; + const cost = filled * average; + const lastTradeTimestamp = this.safeTimestamp (order, 'updated_timestamp'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': undefined, // todo parse trades (operations) + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder () requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin_pair': market['id'], + 'order_id': parseInt (id), + }; + const response = await this.privatePostGetOrder (this.extend (request, params)); + const responseData = this.safeValue (response, 'response_data', {}); + const order = this.safeValue (responseData, 'order'); + return this.parseOrder (order, market); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coin': currency['id'], + 'quantity': amount.toFixed (10), + 'address': address, + }; + if (code === 'BRL') { + const account_ref = ('account_ref' in params); + if (!account_ref) { + throw new ExchangeError (this.id + ' requires account_ref parameter to withdraw ' + code); + } + } else if (code !== 'LTC') { + const tx_fee = ('tx_fee' in params); + if (!tx_fee) { + throw new ExchangeError (this.id + ' requires tx_fee parameter to withdraw ' + code); + } + if (code === 'XRP') { + if (tag === undefined) { + if (!('destination_tag' in params)) { + throw new ExchangeError (this.id + ' requires a tag argument or destination_tag parameter to withdraw ' + code); + } + } else { + request['destination_tag'] = tag; + } + } + } + const response = await this.privatePostWithdrawCoin (this.extend (request, params)); + return { + 'info': response, + 'id': response['response_data']['withdrawal']['id'], + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'timestamp'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'precision': this.timeframes[timeframe], + 'coin': market['id'].toLowerCase (), + }; + if (limit !== undefined && since !== undefined) { + request['from'] = parseInt (since / 1000); + request['to'] = this.sum (request['from'], limit * this.parseTimeframe (timeframe)); + } else if (since !== undefined) { + request['from'] = parseInt (since / 1000); + request['to'] = this.sum (this.seconds (), 1); + } else if (limit !== undefined) { + request['to'] = this.seconds (); + request['from'] = request['to'] - (limit * this.parseTimeframe (timeframe)); + } + const response = await this.v4PublicGetCoinCandle (this.extend (request, params)); + const candles = this.safeValue (response, 'candles', []); + return this.parseOHLCVs (candles, market, timeframe, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders () requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'coin_pair': market['id'], + }; + const response = await this.privatePostListOrders (this.extend (request, params)); + const responseData = this.safeValue (response, 'response_data', {}); + const orders = this.safeValue (responseData, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api] + '/'; + const query = this.omit (params, this.extractParams (path)); + if (api === 'public' || (api === 'v4Public')) { + url += this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + url += this.version + '/'; + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'tapi_method': path, + 'tapi_nonce': nonce, + }, params)); + const auth = '/tapi/' + this.version + '/' + '?' + body; + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'TAPI-ID': this.apiKey, + 'TAPI-MAC': this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('error_message' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/mixcoins.js b/TaskServer/node_modules/ccxt/js/mixcoins.js new file mode 100644 index 000000000..4bd5bc7e5 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/mixcoins.js @@ -0,0 +1,223 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class mixcoins extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'mixcoins', + 'name': 'MixCoins', + 'countries': [ 'GB', 'HK' ], + 'rateLimit': 1500, + 'version': 'v1', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30237212-ed29303c-9535-11e7-8af8-fcd381cfa20c.jpg', + 'api': 'https://mixcoins.com/api', + 'www': 'https://mixcoins.com', + 'doc': 'https://mixcoins.com/help/api/', + }, + 'api': { + 'public': { + 'get': [ + 'ticker/', + 'trades/', + 'depth/', + ], + }, + 'private': { + 'post': [ + 'cancel', + 'info', + 'orders', + 'order', + 'transactions', + 'trade', + ], + }, + }, + 'markets': { + 'BTC/USDT': { 'id': 'btc_usdt', 'symbol': 'BTC/USDT', 'base': 'BTC', 'quote': 'USDT', 'baseId': 'btc', 'quoteId': 'usdt', 'maker': 0.0015, 'taker': 0.0025 }, + 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'maker': 0.001, 'taker': 0.0015 }, + 'BCH/BTC': { 'id': 'bch_btc', 'symbol': 'BCH/BTC', 'base': 'BCH', 'quote': 'BTC', 'baseId': 'bch', 'quoteId': 'btc', 'maker': 0.001, 'taker': 0.0015 }, + 'LSK/BTC': { 'id': 'lsk_btc', 'symbol': 'LSK/BTC', 'base': 'LSK', 'quote': 'BTC', 'baseId': 'lsk', 'quoteId': 'btc', 'maker': 0.0015, 'taker': 0.0025 }, + 'BCH/USDT': { 'id': 'bch_usdt', 'symbol': 'BCH/USDT', 'base': 'BCH', 'quote': 'USDT', 'baseId': 'bch', 'quoteId': 'usdt', 'maker': 0.001, 'taker': 0.0015 }, + 'ETH/USDT': { 'id': 'eth_usdt', 'symbol': 'ETH/USDT', 'base': 'ETH', 'quote': 'USDT', 'baseId': 'eth', 'quoteId': 'usdt', 'maker': 0.001, 'taker': 0.0015 }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostInfo (params); + const balances = this.safeValue (response['result'], 'wallet'); + const result = { 'info': response }; + const currencyIds = Object.keys (balances); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (balances, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'avail'); + account['used'] = this.safeFloat (balance, 'lock'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + }; + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response['result']); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + }; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = this.safeValue (response, 'result'); + const timestamp = this.milliseconds (); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response['result'], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'op': side, + 'amount': amount, + }; + if (type === 'market') { + request['order_type'] = 1; + request['price'] = price; + } else { + request['order_type'] = 0; + } + const response = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': response, + 'id': response['result']['id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + path; + if (api === 'public') { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.secret, 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('status' in response) { + // + // todo add a unified standard handleErrors with this.exceptions in describe() + // + // {"status":503,"message":"Maintenancing, try again later","result":null} + // + if (response['status'] === 200) { + return response; + } + } + throw new ExchangeError (this.id + ' ' + this.json (response)); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/oceanex.js b/TaskServer/node_modules/ccxt/js/oceanex.js new file mode 100644 index 000000000..12e7fa96b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/oceanex.js @@ -0,0 +1,690 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, AuthenticationError, ArgumentsRequired, BadRequest, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class oceanex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'oceanex', + 'name': 'OceanEx', + 'countries': [ 'US' ], + 'version': 'v1', + 'rateLimit': 3000, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/58385970-794e2d80-8001-11e9-889c-0567cd79b78e.jpg', + 'api': 'https://api.oceanex.pro', + 'www': 'https://www.oceanex.pro.com', + 'doc': 'https://api.oceanex.pro/doc/v1', + 'referral': 'https://oceanex.pro/signup?referral=VE24QX', + }, + 'has': { + 'fetchMarkets': true, + 'fetchCurrencies': false, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchOrderBook': true, + 'fetchOrderBooks': true, + 'fetchTrades': true, + 'fetchTradingLimits': false, + 'fetchTradingFees': false, + 'fetchAllTradingFees': true, + 'fetchFundingFees': false, + 'fetchTime': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchBalance': true, + 'createMarketOrder': true, + 'createOrder': true, + 'cancelOrder': true, + 'cancelOrders': true, + 'cancelAllOrders': true, + }, + 'timeframes': { + '1m': '1m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '4h': '4h', + '12h': '12h', + '1d': '1d', + '1w': '1w', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'tickers/{pair}', + 'tickers_multi', + 'order_book', + 'order_book/multi', + 'fees/trading', + 'trades', + 'timestamp', + ], + }, + 'private': { + 'get': [ + 'key', + 'members/me', + 'orders', + 'orders/filter', + ], + 'post': [ + 'orders', + 'orders/multi', + 'order/delete', + 'order/delete/multi', + 'orders/clear', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.1 / 100, + }, + }, + 'exceptions': { + 'codes': { + '-1': BadRequest, + '-2': BadRequest, + '1001': BadRequest, + '1004': ArgumentsRequired, + '1006': AuthenticationError, + '1008': AuthenticationError, + '1010': AuthenticationError, + '1011': PermissionDenied, + '2001': AuthenticationError, + '2002': InvalidOrder, + '2004': OrderNotFound, + '9003': PermissionDenied, + }, + 'exact': { + 'market does not have a valid value': BadRequest, + 'side does not have a valid value': BadRequest, + 'Account::AccountError: Cannot lock funds': InsufficientFunds, + 'The account does not exist': AuthenticationError, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const request = { 'show_details': true }; + const response = await this.publicGetMarkets (this.extend (request, params)); + const result = []; + const markets = this.safeValue (response, 'data'); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeValue (market, 'id'); + const name = this.safeValue (market, 'name'); + let [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + baseId = baseId.toLowerCase (); + quoteId = quoteId.toLowerCase (); + const symbol = base + '/' + quote; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'info': market, + 'precision': { + 'amount': this.safeValue (market, 'amount_precision'), + 'price': this.safeValue (market, 'price_precision'), + 'base': this.safeValue (market, 'ask_precision'), + 'quote': this.safeValue (market, 'bid_precision'), + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeValue (market, 'minimum_trading_amount'), + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + const response = await this.publicGetTickersPair (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": { + // "at":1559431729, + // "ticker": { + // "buy":"0.0065", + // "sell":"0.00677", + // "low":"0.00677", + // "high":"0.00677", + // "last":"0.00677", + // "vol":"2000.0" + // } + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseTicker (data, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const marketIds = this.marketIds (symbols); + const request = { 'markets': marketIds }; + const response = await this.publicGetTickersMulti (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": { + // "at":1559431729, + // "ticker": { + // "buy":"0.0065", + // "sell":"0.00677", + // "low":"0.00677", + // "high":"0.00677", + // "last":"0.00677", + // "vol":"2000.0" + // } + // } + // } + // + const data = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < data.length; i++) { + const ticker = data[i]; + const marketId = this.safeString (ticker, 'market'); + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + parseTicker (data, market = undefined) { + // + // { + // "at":1559431729, + // "ticker": { + // "buy":"0.0065", + // "sell":"0.00677", + // "low":"0.00677", + // "high":"0.00677", + // "last":"0.00677", + // "vol":"2000.0" + // } + // } + // + const ticker = this.safeValue (data, 'ticker', {}); + const timestamp = this.safeTimestamp (data, 'at'); + return { + 'symbol': market['symbol'], + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': this.safeFloat (ticker, 'last'), + 'last': this.safeFloat (ticker, 'last'), + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": { + // "timestamp":1559433057, + // "asks": [ + // ["100.0","20.0"], + // ["4.74","2000.0"], + // ["1.74","4000.0"], + // ], + // "bids":[ + // ["0.0065","5482873.4"], + // ["0.00649","4781956.2"], + // ["0.00648","2876006.8"], + // ], + // } + // } + // + const orderbook = this.safeValue (response, 'data', {}); + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + return this.parseOrderBook (orderbook, timestamp); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + if (symbols === undefined) { + symbols = this.symbols; + } + const marketIds = this.marketIds (symbols); + const request = { + 'markets': marketIds, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderBookMulti (this.extend (request, params)); + // + // { + // "code":0, + // "message":"Operation successful", + // "data": [ + // { + // "timestamp":1559433057, + // "market": "bagvet", + // "asks": [ + // ["100.0","20.0"], + // ["4.74","2000.0"], + // ["1.74","4000.0"], + // ], + // "bids":[ + // ["0.0065","5482873.4"], + // ["0.00649","4781956.2"], + // ["0.00648","2876006.8"], + // ], + // }, + // ..., + // ], + // } + // + const data = this.safeValue (response, 'data', []); + const result = {}; + for (let i = 0; i < data.length; i++) { + const orderbook = data[i]; + const marketId = this.safeString (orderbook, 'market'); + const market = this.markets_by_id[marketId]; + const symbol = market['symbol']; + const timestamp = this.safeTimestamp (orderbook, 'timestamp'); + result[symbol] = this.parseOrderBook (orderbook, timestamp); + } + return result; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTrades (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + return this.parseTrades (data, market, since, limit); + } + + parseTrade (trade, market = undefined) { + let side = this.safeValue (trade, 'side'); + if (side === 'bid') { + side = 'buy'; + } else if (side === 'ask') { + side = 'sell'; + } + let symbol = undefined; + const marketId = this.safeValue (trade, 'market'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let timestamp = this.safeTimestamp (trade, 'created_on'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': this.safeString (trade, 'id'), + 'order': undefined, + 'type': 'limit', + 'takerOrMaker': undefined, + 'side': side, + 'price': this.safeFloat (trade, 'price'), + 'amount': this.safeFloat (trade, 'volume'), + 'cost': undefined, + 'fee': undefined, + }; + } + + async fetchTime (params = {}) { + const response = await this.publicGetTimestamp (params); + // + // {"code":0,"message":"Operation successful","data":1559433420} + // + return this.safeTimestamp (response, 'data'); + } + + async fetchAllTradingFees (params = {}) { + const response = await this.publicGetFeesTrading (params); + const data = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < data.length; i++) { + const group = data[i]; + const maker = this.safeValue (group, 'ask_fee', {}); + const taker = this.safeValue (group, 'bid_fee', {}); + const marketId = this.safeString (group, 'market'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + symbol = this.markets_by_id[marketId]['symbol']; + } + result[symbol] = { + 'info': group, + 'symbol': symbol, + 'maker': this.safeFloat (maker, 'value'), + 'taker': this.safeFloat (taker, 'value'), + }; + } + return result; + } + + async fetchKey (params = {}) { + const response = await this.privateGetKey (params); + return this.safeValue (response, 'data'); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetMembersMe (params); + const data = this.safeValue (response, 'data'); + const balances = this.safeValue (data, 'accounts'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeValue (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'side': side, + 'ord_type': type, + 'volume': this.amountToPrecision (symbol, amount), + }; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + return this.parseOrder (data, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + let ids = id; + if (!Array.isArray (id)) { + ids = [ id ]; + } + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = { 'ids': ids }; + const response = await this.privateGetOrders (this.extend (request, params)); + const data = this.safeValue (response, 'data'); + const dataLength = data.length; + if (data === undefined) { + throw new OrderNotFound (this.id + ' could not found matching order'); + } + if (Array.isArray (id)) { + return this.parseOrders (data, market); + } + if (dataLength === 0) { + throw new OrderNotFound (this.id + ' could not found matching order'); + } + return this.parseOrder (data[0], market); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'states': [ 'wait' ], + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'states': [ 'done', 'cancel' ], + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const states = this.safeValue (params, 'states', [ 'wait', 'done', 'cancel' ]); + const query = this.omit (params, 'states'); + const request = { + 'market': market['id'], + 'states': states, + 'need_price': 'True', + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrdersFilter (this.extend (request, query)); + const data = this.safeValue (response, 'data', []); + let result = []; + for (let i = 0; i < data.length; i++) { + const orders = this.safeValue (data[i], 'orders', []); + const status = this.parseOrderStatus (this.safeValue (data[i], 'state')); + const parsedOrders = this.parseOrders (orders, market, since, limit, { 'status': status }); + result = this.arrayConcat (result, parsedOrders); + } + return result; + } + + parseOrder (order, market = undefined) { + const status = this.parseOrderStatus (this.safeValue (order, 'state')); + const marketId = this.safeValue2 (order, 'market', 'market_id'); + let symbol = undefined; + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + } + if (symbol === undefined) { + if (market !== undefined) { + symbol = market['symbol']; + } + } + let timestamp = this.safeTimestamp (order, 'created_on'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (order, 'created_at')); + } + return { + 'info': order, + 'id': this.safeString (order, 'id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': this.safeValue (order, 'ord_type'), + 'side': this.safeValue (order, 'side'), + 'price': this.safeFloat (order, 'price'), + 'average': this.safeFloat (order, 'avg_price'), + 'amount': this.safeFloat (order, 'volume'), + 'remaining': this.safeFloat (order, 'remaining_volume'), + 'filled': this.safeFloat (order, 'executed_volume'), + 'status': status, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'wait': 'open', + 'done': 'closed', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + async createOrders (symbol, orders, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'orders': orders, + }; + // orders: [{"side":"buy", "volume":.2, "price":1001}, {"side":"sell", "volume":0.2, "price":1002}] + const response = await this.privatePostOrdersMulti (this.extend (request, params)); + const data = response['data']; + return this.parseOrders (data); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrderDelete (this.extend ({ 'id': id }, params)); + const data = this.safeValue (response, 'data'); + return this.parseOrder (data); + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrderDeleteMulti (this.extend ({ 'ids': ids }, params)); + const data = this.safeValue (response, 'data'); + return this.parseOrders (data); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privatePostOrdersClear (params); + const data = this.safeValue (response, 'data'); + return this.parseOrders (data); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (path === 'tickers_multi' || path === 'order_book/multi') { + let request = '?'; + const markets = this.safeValue (params, 'markets'); + for (let i = 0; i < markets.length; i++) { + request += 'markets[]=' + markets[i] + '&'; + } + const limit = this.safeValue (params, 'limit'); + if (limit !== undefined) { + request += 'limit=' + limit; + } + url += request; + } else if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (api === 'private') { + this.checkRequiredCredentials (); + const request = { + 'uid': this.apiKey, + 'data': query, + }; + // to set the private key: + // const fs = require ('fs') + // exchange.secret = fs.readFileSync ('oceanex.pem', 'utf8') + const jwt_token = this.jwt (request, this.encode (this.secret), 'RS256'); + url += '?user_jwt=' + jwt_token; + } + headers = { 'Content-Type': 'application/json' }; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + // + // {"code":1011,"message":"This IP '5.228.233.138' is not allowed","data":{}} + // + if (response === undefined) { + return; + } + const errorCode = this.safeString (response, 'code'); + const message = this.safeString (response, 'message'); + if ((errorCode !== undefined) && (errorCode !== '0')) { + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['codes'], errorCode, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (response); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/okcoincny.js b/TaskServer/node_modules/ccxt/js/okcoincny.js new file mode 100644 index 000000000..04c306ff4 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/okcoincny.js @@ -0,0 +1,38 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const okcoinusd = require ('./okcoinusd.js'); + +// --------------------------------------------------------------------------- + +module.exports = class okcoincny extends okcoinusd { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okcoincny', + 'name': 'OKCoin CNY', + 'countries': [ 'CN' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766792-8be9157a-5ee5-11e7-926c-6d69b8d3378d.jpg', + 'api': { + 'web': 'https://www.okcoin.cn', + 'public': 'https://www.okcoin.cn/api', + 'private': 'https://www.okcoin.cn/api', + }, + 'www': 'https://www.okcoin.cn', + 'doc': 'https://www.okcoin.cn/rest_getStarted.html', + 'referral': 'https://www.okcoin.cn', + }, + 'markets': { + 'BTC/CNY': { 'id': 'btc_cny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'LTC/CNY': { 'id': 'ltc_cny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'ETH/CNY': { 'id': 'eth_cny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'ETC/CNY': { 'id': 'etc_cny', 'symbol': 'ETC/CNY', 'base': 'ETC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + 'BCH/CNY': { 'id': 'bcc_cny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false }, + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/okcoinusd.js b/TaskServer/node_modules/ccxt/js/okcoinusd.js new file mode 100644 index 000000000..6e9e2cba7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/okcoinusd.js @@ -0,0 +1,1116 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadSymbol, ExchangeError, ArgumentsRequired, DDoSProtection, InsufficientFunds, InvalidOrder, OrderNotFound, AuthenticationError, BadRequest } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class okcoinusd extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okcoinusd', + 'name': 'OKCoin USD', + 'countries': [ 'CN', 'US' ], + 'version': 'v1', + 'rateLimit': 1000, // up to 3000 requests per 5 minutes ≈ 600 requests per minute ≈ 10 requests per second ≈ 100 ms + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchTickers': true, + 'withdraw': true, + 'futures': false, + }, + 'extension': '.do', // appended to endpoint URL + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': '1day', + '3d': '3day', + '1w': '1week', + }, + 'api': { + 'v3': { + 'get': [ + 'futures/pc/market/futuresCoin', + ], + }, + 'web': { + 'get': [ + 'futures/pc/market/marketOverview', + 'spot/markets/index-tickers', + 'spot/markets/currencies', + 'spot/markets/products', + 'spot/markets/tickers', + 'spot/user-level', + ], + 'post': [ + 'futures/pc/market/futuresCoin', + ], + }, + 'public': { + 'get': [ + 'depth', + 'exchange_rate', + 'future_depth', + 'future_estimated_price', + 'future_hold_amount', + 'future_index', + 'future_kline', + 'future_price_limit', + 'future_ticker', + 'future_trades', + 'kline', + 'otcs', + 'ticker', + 'tickers', + 'trades', + ], + }, + 'private': { + 'post': [ + 'account_records', + 'batch_trade', + 'borrow_money', + 'borrow_order_info', + 'borrows_info', + 'cancel_borrow', + 'cancel_order', + 'cancel_otc_order', + 'cancel_withdraw', + 'funds_transfer', + 'future_batch_trade', + 'future_cancel', + 'future_devolve', + 'future_explosive', + 'future_order_info', + 'future_orders_info', + 'future_position', + 'future_position_4fix', + 'future_trade', + 'future_trades_history', + 'future_userinfo', + 'future_userinfo_4fix', + 'lend_depth', + 'order_fee', + 'order_history', + 'order_info', + 'orders_info', + 'otc_order_history', + 'otc_order_info', + 'repayment', + 'submit_otc_order', + 'trade', + 'trade_history', + 'trade_otc_order', + 'wallet_info', + 'withdraw', + 'withdraw_info', + 'unrepayments_info', + 'userinfo', + ], + }, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766791-89ffb502-5ee5-11e7-8a5b-c5950b68ac65.jpg', + 'api': { + 'v3': 'https://www.okcoin.com/v3', + 'web': 'https://www.okcoin.com/v2', + 'public': 'https://www.okcoin.com/api', + 'private': 'https://www.okcoin.com', + }, + 'www': 'https://www.okcoin.com', + 'doc': [ + 'https://www.okcoin.com/docs/en/', + 'https://www.npmjs.com/package/okcoin.com', + ], + 'referral': 'https://www.okcoin.com/account/register?flag=activity&channelId=600001513', + 'fees': 'https://support.okcoin.com/hc/en-us/articles/360015261532-OKCoin-Fee-Rates', + }, + // these are okcoin.com fees, okex fees are in okex.js + 'fees': { + 'trading': { + 'taker': 0.002, + 'maker': 0.001, + }, + }, + 'exceptions': { + // see https://github.com/okcoin-okex/API-docs-OKEx.com/blob/master/API-For-Spot-EN/Error%20Code%20For%20Spot.md + '10000': ExchangeError, // "Required field, can not be null" + '10001': DDoSProtection, // "Request frequency too high to exceed the limit allowed" + '10005': AuthenticationError, // "'SecretKey' does not exist" + '10006': AuthenticationError, // "'Api_key' does not exist" + '10007': AuthenticationError, // "Signature does not match" + '1002': InsufficientFunds, // "The transaction amount exceed the balance" + '1003': InvalidOrder, // "The transaction amount is less than the minimum requirement" + '1004': InvalidOrder, // "The transaction amount is less than 0" + '1013': InvalidOrder, // no contract type (PR-1101) + '1027': InvalidOrder, // createLimitBuyOrder(symbol, 0, 0): Incorrect parameter may exceeded limits + '1050': InvalidOrder, // returned when trying to cancel an order that was filled or canceled previously + '1217': InvalidOrder, // "Order was sent at ±5% of the current market price. Please resend" + '10014': InvalidOrder, // "Order price must be between 0 and 1,000,000" + '1009': OrderNotFound, // for spot markets, cancelling closed order + '1019': OrderNotFound, // order closed? ("Undo order failed") + '1051': OrderNotFound, // for spot markets, cancelling "just closed" order + '10009': OrderNotFound, // for spot markets, "Order does not exist" + '20015': OrderNotFound, // for future markets + '10008': BadRequest, // Illegal URL parameter + '1007': BadSymbol, // No trading market information + // todo: sort out below + // 10000 Required parameter is empty + // 10001 Request frequency too high to exceed the limit allowed + // 10002 Authentication failure + // 10002 System error + // 10003 This connection has requested other user data + // 10004 Request failed + // 10005 api_key or sign is invalid, 'SecretKey' does not exist + // 10006 'Api_key' does not exist + // 10007 Signature does not match + // 10008 Illegal parameter, Parameter erorr + // 10009 Order does not exist + // 10010 Insufficient funds + // 10011 Amount too low + // 10012 Only btc_usd ltc_usd supported + // 10013 Only support https request + // 10014 Order price must be between 0 and 1,000,000 + // 10015 Order price differs from current market price too much / Channel subscription temporally not available + // 10016 Insufficient coins balance + // 10017 API authorization error / WebSocket authorization error + // 10018 borrow amount less than lower limit [usd:100,btc:0.1,ltc:1] + // 10019 loan agreement not checked + // 1002 The transaction amount exceed the balance + // 10020 rate cannot exceed 1% + // 10021 rate cannot less than 0.01% + // 10023 fail to get latest ticker + // 10024 balance not sufficient + // 10025 quota is full, cannot borrow temporarily + // 10026 Loan (including reserved loan) and margin cannot be withdrawn + // 10027 Cannot withdraw within 24 hrs of authentication information modification + // 10028 Withdrawal amount exceeds daily limit + // 10029 Account has unpaid loan, please cancel/pay off the loan before withdraw + // 1003 The transaction amount is less than the minimum requirement + // 10031 Deposits can only be withdrawn after 6 confirmations + // 10032 Please enabled phone/google authenticator + // 10033 Fee higher than maximum network transaction fee + // 10034 Fee lower than minimum network transaction fee + // 10035 Insufficient BTC/LTC + // 10036 Withdrawal amount too low + // 10037 Trade password not set + // 1004 The transaction amount is less than 0 + // 10040 Withdrawal cancellation fails + // 10041 Withdrawal address not exsit or approved + // 10042 Admin password error + // 10043 Account equity error, withdrawal failure + // 10044 fail to cancel borrowing order + // 10047 this function is disabled for sub-account + // 10048 withdrawal information does not exist + // 10049 User can not have more than 50 unfilled small orders (amount<0.15BTC) + // 10050 can't cancel more than once + // 10051 order completed transaction + // 10052 not allowed to withdraw + // 10064 after a USD deposit, that portion of assets will not be withdrawable for the next 48 hours + // 1008 No latest market information + // 1009 No order + // 1010 Different user of the cancelled order and the original order + // 10100 User account frozen + // 10101 order type is wrong + // 10102 incorrect ID + // 10103 the private otc order's key incorrect + // 10106 API key domain not matched + // 1011 No documented user + // 1013 No order type + // 1014 No login + // 1015 No market depth information + // 1017 Date error + // 1018 Order failed + // 1019 Undo order failed + // 10216 Non-available API / non-public API + // 1024 Currency does not exist + // 1025 No chart type + // 1026 No base currency quantity + // 1027 Incorrect parameter may exceeded limits + // 1028 Reserved decimal failed + // 1029 Preparing + // 1030 Account has margin and futures, transactions can not be processed + // 1031 Insufficient Transferring Balance + // 1032 Transferring Not Allowed + // 1035 Password incorrect + // 1036 Google Verification code Invalid + // 1037 Google Verification code incorrect + // 1038 Google Verification replicated + // 1039 Message Verification Input exceed the limit + // 1040 Message Verification invalid + // 1041 Message Verification incorrect + // 1042 Wrong Google Verification Input exceed the limit + // 1043 Login password cannot be same as the trading password + // 1044 Old password incorrect + // 1045 2nd Verification Needed + // 1046 Please input old password + // 1048 Account Blocked + // 1050 Orders have been withdrawn or withdrawn + // 1051 Order completed + // 1201 Account Deleted at 00: 00 + // 1202 Account Not Exist + // 1203 Insufficient Balance + // 1204 Invalid currency + // 1205 Invalid Account + // 1206 Cash Withdrawal Blocked + // 1207 Transfer Not Support + // 1208 No designated account + // 1209 Invalid api + // 1216 Market order temporarily suspended. Please send limit order + // 1217 Order was sent at ±5% of the current market price. Please resend + // 1218 Place order failed. Please try again later + // 20001 User does not exist + // 20002 Account frozen + // 20003 Account frozen due to forced liquidation + // 20004 Contract account frozen + // 20005 User contract account does not exist + // 20006 Required field missing + // 20007 Illegal parameter + // 20008 Contract account balance is too low + // 20009 Contract status error + // 20010 Risk rate ratio does not exist + // 20011 Risk rate lower than 90%/80% before opening BTC position with 10x/20x leverage. or risk rate lower than 80%/60% before opening LTC position with 10x/20x leverage + // 20012 Risk rate lower than 90%/80% after opening BTC position with 10x/20x leverage. or risk rate lower than 80%/60% after opening LTC position with 10x/20x leverage + // 20013 Temporally no counter party price + // 20014 System error + // 20015 Order does not exist + // 20016 Close amount bigger than your open positions, liquidation quantity bigger than holding + // 20017 Not authorized/illegal operation/illegal order ID + // 20018 Order price cannot be more than 103-105% or less than 95-97% of the previous minute price + // 20019 IP restricted from accessing the resource + // 20020 Secret key does not exist + // 20021 Index information does not exist + // 20022 Wrong API interface (Cross margin mode shall call cross margin API, fixed margin mode shall call fixed margin API) + // 20023 Account in fixed-margin mode + // 20024 Signature does not match + // 20025 Leverage rate error + // 20026 API Permission Error + // 20027 no transaction record + // 20028 no such contract + // 20029 Amount is large than available funds + // 20030 Account still has debts + // 20038 Due to regulation, this function is not availavle in the country/region your currently reside in. + // 20049 Request frequency too high + // 20100 request time out + // 20101 the format of data is error + // 20102 invalid login + // 20103 event type error + // 20104 subscription type error + // 20107 JSON format error + // 20115 The quote is not match + // 20116 Param not match + // 21020 Contracts are being delivered, orders cannot be placed + // 21021 Contracts are being settled, contracts cannot be placed + }, + 'options': { + 'marketBuyPrice': false, + 'fetchOHLCVWarning': true, + 'contractTypes': { + '1': 'this_week', + '2': 'next_week', + '4': 'quarter', + }, + 'fetchTickersMethod': 'fetch_tickers_from_api', + }, + }); + } + + async fetchMarkets (params = {}) { + // TODO: they have a new fee schedule as of Feb 7 + // the new fees are progressive and depend on 30-day traded volume + // the following is the worst case + const result = []; + const spotResponse = await this.webGetSpotMarketsProducts (); + // + // { + // "code": 0, + // "data": [ + // { + // "baseCurrency":0, + // "brokerId":0, + // "callAuctionOrCallNoCancelAuction":false, + // "callNoCancelSwitchTime":{}, + // "collect":"0", + // "continuousSwitchTime":{}, + // "groupId":1, + // "isMarginOpen":true, + // "listDisplay":0, + // "marginRiskPreRatio":1.2, + // "marginRiskRatio":1.1, + // "marketFrom":118, + // "maxMarginLeverage":5, + // "maxPriceDigit":1, + // "maxSizeDigit":8, + // "mergeTypes":"0.1,1,10", + // "minTradeSize":0.00100000, + // "online":1, + // "productId":20, + // "quoteCurrency":7, + // "quoteIncrement":"0.1", + // "quotePrecision":2, + // "sort":30038, + // "symbol":"btc_usdt", + // "tradingMode":3 + // }, + // ] + // } + // + const spotMarkets = this.safeValue (spotResponse, 'data', []); + let markets = spotMarkets; + if (this.has['futures']) { + const futuresResponse = await this.v3GetFuturesPcMarketFuturesCoin ({ + 'currencyCode': 0, + }); + // + // { + // "msg":"success", + // "code":0, + // "detailMsg":"", + // "data": [ + // { + // "symbolId":0, + // "symbol":"f_usd_btc", + // "iceSingleAvgMinAmount":2, + // "minTradeSize":1, + // "iceSingleAvgMaxAmount":500, + // "contractDepthLevel":["0.01","0.2"], + // "dealAllMaxAmount":999, + // "maxSizeDigit":4, + // "contracts":[ + // { "marketFrom":34, "id":201905240000034, "type":1, "desc":"BTC0524" }, + // { "marketFrom":13, "id":201905310000013, "type":2, "desc":"BTC0531" }, + // { "marketFrom":12, "id":201906280000012, "type":4, "desc":"BTC0628" }, + // ], + // "maxPriceDigit":2, + // "nativeRate":1, + // "quote":"usd", + // "nativeCurrency":"usd", + // "nativeCurrencyMark":"$", + // "contractSymbol":0, + // "unitAmount":100.00, + // "symbolMark":"฿", + // "symbolDesc":"BTC" + // }, + // ] + // } + // + const data = this.safeValue (futuresResponse, 'data', {}); + const futuresMarkets = this.safeValue (data, 'usd', []); + markets = this.arrayConcat (spotMarkets, futuresMarkets); + } + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'symbol'); + let symbol = undefined; + let base = undefined; + let quote = undefined; + let baseId = undefined; + let quoteId = undefined; + let baseNumericId = undefined; + let quoteNumericId = undefined; + let lowercaseId = undefined; + let uppercaseBaseId = undefined; + const precision = { + 'amount': this.safeInteger (market, 'maxSizeDigit'), + 'price': this.safeInteger (market, 'maxPriceDigit'), + }; + const minAmount = this.safeFloat (market, 'minTradeSize'); + const minPrice = Math.pow (10, -precision['price']); + let contracts = this.safeValue (market, 'contracts'); + if (contracts === undefined) { + // spot markets + lowercaseId = id; + const parts = id.split ('_'); + baseId = parts[0]; + quoteId = parts[1]; + baseNumericId = this.safeInteger (market, 'baseCurrency'); + quoteNumericId = this.safeInteger (market, 'quoteCurrency'); + base = this.safeCurrencyCode (baseId); + quote = this.safeCurrencyCode (quoteId); + contracts = [{}]; + } else { + // futures markets + quoteId = this.safeStringLower2 (market, 'quote', 'denomination'); + uppercaseBaseId = this.safeStringLower (market, 'symbolDesc'); + baseId = uppercaseBaseId.toLowerCase (); + lowercaseId = baseId + '_' + quoteId; + base = this.safeCurrencyCode (uppercaseBaseId); + quote = this.safeCurrencyCode (quoteId); + } + for (let k = 0; k < contracts.length; k++) { + const contract = contracts[k]; + let type = this.safeString (contract, 'type', 'spot'); + let contractType = undefined; + let spot = true; + let future = false; + let active = true; + if (type === 'spot') { + symbol = base + '/' + quote; + active = market['online'] !== 0; + } else { + const contractId = this.safeString (contract, 'id'); + symbol = base + '-' + quote + '-' + contractId.slice (2, 8); + contractType = this.safeString (this.options['contractTypes'], type); + type = 'future'; + spot = false; + future = true; + } + const fees = this.safeValue2 (this.fees, type, 'trading', {}); + result.push (this.extend (fees, { + 'id': id, + 'lowercaseId': lowercaseId, + 'contractType': contractType, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'baseNumericId': baseNumericId, + 'quoteNumericId': quoteNumericId, + 'info': market, + 'type': type, + 'spot': spot, + 'future': future, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': minAmount * minPrice, + 'max': undefined, + }, + }, + })); + } + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, cost)), + }; + } + + async fetchTickersFromApi (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + const response = await this.publicGetTickers (this.extend (request, params)); + const tickers = response['tickers']; + const timestamp = this.safeTimestamp (response, 'date'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + let ticker = tickers[i]; + ticker = this.parseTicker (this.extend (tickers[i], { 'timestamp': timestamp })); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTickersFromWeb (symbols = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + const response = await this.webGetSpotMarketsTickers (this.extend (request, params)); + const tickers = this.safeValue (response, 'data'); + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = this.parseTicker (tickers[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTickers (symbols = undefined, params = {}) { + const method = this.options['fetchTickersMethod']; + return await this[method] (symbols, params); + } + + async fetchOrderBook (symbol = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureDepth' : 'publicGetDepth'; + const request = this.createRequest (market, params); + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this[method] (request); + return this.parseOrderBook (response); + } + + parseTicker (ticker, market = undefined) { + // + // { buy: "48.777300", + // change: "-1.244500", + // changePercentage: "-2.47%", + // close: "49.064000", + // createdDate: 1531704852254, + // currencyId: 527, + // dayHigh: "51.012500", + // dayLow: "48.124200", + // high: "51.012500", + // inflows: "0", + // last: "49.064000", + // low: "48.124200", + // marketFrom: 627, + // name: { }, + // open: "50.308500", + // outflows: "0", + // productId: 527, + // sell: "49.064000", + // symbol: "zec_okb", + // volume: "1049.092535" } + // + const timestamp = this.safeInteger2 (ticker, 'timestamp', 'createdDate'); + let symbol = undefined; + if (market === undefined) { + if ('symbol' in ticker) { + const marketId = ticker['symbol']; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + const [ baseId, quoteId ] = ticker['symbol'].split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open'); + const change = this.safeFloat (ticker, 'change'); + const percentage = this.safeFloat (ticker, 'changePercentage'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat2 (ticker, 'vol', 'volume'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureTicker' : 'publicGetTicker'; + const request = this.createRequest (market, params); + const response = await this[method] (request); + let ticker = this.safeValue (response, 'ticker'); + if (ticker === undefined) { + throw new ExchangeError (this.id + ' fetchTicker returned an empty response: ' + this.json (response)); + } + const timestamp = this.safeTimestamp (response, 'date'); + if (timestamp !== undefined) { + ticker = this.extend (ticker, { 'timestamp': timestamp }); + } + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const timestamp = this.safeInteger (trade, 'date_ms'); + const id = this.safeString (trade, 'tid'); + const type = undefined; + const side = this.safeString (trade, 'type'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': type, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureTrades' : 'publicGetTrades'; + const request = this.createRequest (market, params); + const response = await this[method] (request); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + const numElements = ohlcv.length; + const volumeIndex = (numElements > 6) ? 6 : 5; + return [ + ohlcv[0], // timestamp + parseFloat (ohlcv[1]), // Open + parseFloat (ohlcv[2]), // High + parseFloat (ohlcv[3]), // Low + parseFloat (ohlcv[4]), // Close + // parseFloat (ohlcv[5]), // quote volume + // parseFloat (ohlcv[6]), // base volume + parseFloat (ohlcv[volumeIndex]), // okex will return base volume in the 7th element for future markets + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'publicGetFutureKline' : 'publicGetKline'; + const request = this.createRequest (market, { + 'type': this.timeframes[timeframe], + }); + if (since !== undefined) { + request['since'] = parseInt ((this.milliseconds () - 86400000) / 1000); // default last 24h + } + if (limit !== undefined) { + if (this.options['fetchOHLCVWarning']) { + throw new ExchangeError (this.id + ' fetchOHLCV counts "limit" candles backwards in chronological ascending order, therefore the "limit" argument for ' + this.id + ' is disabled. Set ' + this.id + '.options["fetchOHLCVWarning"] = false to suppress this warning message.'); + } + request['size'] = parseInt (limit); // max is 1440 candles + } + const response = await this[method] (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostUserinfo (params); + const info = this.safeValue (response, 'info', {}); + const balances = this.safeValue (info, 'funds', {}); + const result = { 'info': response }; + let ids = Object.keys (balances['free']); + let usedField = 'freezed'; + // wtf, okex? + // https://github.com/okcoin-okex/API-docs-OKEx.com/commit/01cf9dd57b1f984a8737ef76a037d4d3795d2ac7 + if (!(usedField in balances)) { + usedField = 'holds'; + } + const usedKeys = Object.keys (balances[usedField]); + ids = this.arrayConcat (ids, usedKeys); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + const account = this.account (); + account['free'] = this.safeFloat (balances['free'], id); + account['used'] = this.safeFloat (balances[usedField], id); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'privatePostFutureTrade' : 'privatePostTrade'; + const orderSide = (type === 'market') ? (side + '_market') : side; + const isMarketBuy = ((market['spot']) && (type === 'market') && (side === 'buy') && (!this.options['marketBuyPrice'])); + const orderPrice = isMarketBuy ? this.safeFloat (params, 'cost') : price; + const request = this.createRequest (market, { + 'type': orderSide, + }); + if (market['future']) { + request['match_price'] = (type === 'market') ? 1 : 0; // match best counter party price? 0 or 1, ignores price if 1 + request['lever_rate'] = 10; // leverage rate value: 10 or 20 (10 by default) + request['type'] = (side === 'buy') ? '1' : '2'; + } else if (type === 'market') { + if (side === 'buy') { + if (!orderPrice) { + if (this.options['marketBuyPrice']) { + // eslint-disable-next-line quotes + throw new ExchangeError (this.id + " market buy orders require a price argument (the amount you want to spend or the cost of the order) when this.options['marketBuyPrice'] is true."); + } else { + // eslint-disable-next-line quotes + throw new ExchangeError (this.id + " market buy orders require an additional cost parameter, cost = price * amount. If you want to pass the cost of the market order (the amount you want to spend) in the price argument (the default " + this.id + " behaviour), set this.options['marketBuyPrice'] = true. It will effectively suppress this warning exception as well."); + } + } else { + request['price'] = orderPrice; + } + } else { + request['amount'] = amount; + } + } + if (type !== 'market') { + request['price'] = orderPrice; + request['amount'] = amount; + } + params = this.omit (params, 'cost'); + const response = await this[method] (this.extend (request, params)); + const timestamp = this.milliseconds (); + return { + 'info': response, + 'id': this.safeString (response, 'order_id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'privatePostFutureCancel' : 'privatePostCancelOrder'; + const request = this.createRequest (market, { + 'order_id': id, + }); + const response = await this[method] (this.extend (request, params)); + return response; + } + + parseOrderStatus (status) { + const statuses = { + '-1': 'canceled', + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeValue (statuses, status, status); + } + + parseOrderSide (side) { + if (side === 1) { + return 'buy'; // open long position + } else if (side === 2) { + return 'sell'; // open short position + } else if (side === 3) { + return 'sell'; // liquidate long position + } else if (side === 4) { + return 'buy'; // liquidate short position + } + return side; + } + + parseOrder (order, market = undefined) { + let side = undefined; + let type = undefined; + if ('type' in order) { + if ((order['type'] === 'buy') || (order['type'] === 'sell')) { + side = order['type']; + type = 'limit'; + } else if (order['type'] === 'buy_market') { + side = 'buy'; + type = 'market'; + } else if (order['type'] === 'sell_market') { + side = 'sell'; + type = 'market'; + } else { + side = this.parseOrderSide (order['type']); + if (('contract_name' in order) || ('lever_rate' in order)) { + type = 'margin'; + } + } + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market) { + symbol = market['symbol']; + } + const createDateField = this.getCreateDateField (); + const timestamp = this.safeInteger (order, createDateField); + let amount = this.safeFloat (order, 'amount'); + const filled = this.safeFloat (order, 'deal_amount'); + amount = Math.max (amount, filled); + let remaining = Math.max (0, amount - filled); + if (type === 'market') { + remaining = 0; + } + let average = this.safeFloat (order, 'avg_price'); + // https://github.com/ccxt/ccxt/issues/2452 + average = this.safeFloat (order, 'price_avg', average); + const cost = average * filled; + return { + 'info': order, + 'id': this.safeString (order, 'order_id'), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': this.safeFloat (order, 'price'), + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + getCreateDateField () { + // needed for derived exchanges + // allcoin typo create_data instead of create_date + return 'create_date'; + } + + getOrdersField () { + // needed for derived exchanges + // allcoin typo order instead of orders (expected based on their API docs) + return 'orders'; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['future'] ? 'privatePostFutureOrderInfo' : 'privatePostOrderInfo'; + const request = this.createRequest (market, { + 'order_id': id, + // 'status': 0, // 0 for unfilled orders, 1 for filled orders + // 'current_page': 1, // current page number + // 'page_length': 200, // number of orders returned per page, maximum 200 + }); + const response = await this[method] (this.extend (request, params)); + const ordersField = this.getOrdersField (); + const numOrders = response[ordersField].length; + if (numOrders > 0) { + return this.parseOrder (response[ordersField][0]); + } + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + let method = market['future'] ? 'privatePostFutureOrdersInfo' : 'privatePost'; + let request = this.createRequest (market); + const order_id_in_params = ('order_id' in params); + if (market['future']) { + if (!order_id_in_params) { + throw new ExchangeError (this.id + ' fetchOrders() requires order_id param for futures market ' + symbol + ' (a string of one or more order ids, comma-separated)'); + } + } else { + const status = ('type' in params) ? params['type'] : params['status']; + if (typeof status === 'undefined') { + const name = order_id_in_params ? 'type' : 'status'; + throw new ExchangeError (this.id + ' fetchOrders() requires ' + name + ' param for spot market ' + symbol + ' (0 - for unfilled orders, 1 - for filled/canceled orders)'); + } + if (order_id_in_params) { + method += 'OrdersInfo'; + request = this.extend (request, { + 'type': status, + 'order_id': params['order_id'], + }); + } else { + method += 'OrderHistory'; + request = this.extend (request, { + 'status': status, + 'current_page': 1, // current page number + 'page_length': 200, // number of orders returned per page, maximum 200 + }); + } + params = this.omit (params, [ 'type', 'status' ]); + } + const response = await this[method] (this.extend (request, params)); + const ordersField = this.getOrdersField (); + return this.parseOrders (response[ordersField], market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 0, // 0 for unfilled orders, 1 for filled orders + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 1, // 0 for unfilled orders, 1 for filled orders + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + // if (amount < 0.01) + // throw new ExchangeError (this.id + ' withdraw() requires amount > 0.01'); + // for some reason they require to supply a pair of currencies for withdrawing one currency + const currencyId = currency['id'] + '_usd'; + if (tag) { + address = address + ':' + tag; + } + const request = { + 'symbol': currencyId, + 'withdraw_address': address, + 'withdraw_amount': amount, + 'target': 'address', // or 'okcn', 'okcom', 'okex' + }; + let query = params; + if ('chargefee' in query) { + request['chargefee'] = query['chargefee']; + query = this.omit (query, 'chargefee'); + } else { + throw new ExchangeError (this.id + ' withdraw() requires a `chargefee` parameter'); + } + if (this.password) { + request['trade_pwd'] = this.password; + } else if ('password' in query) { + request['trade_pwd'] = query['password']; + query = this.omit (query, 'password'); + } else if ('trade_pwd' in query) { + request['trade_pwd'] = query['trade_pwd']; + query = this.omit (query, 'trade_pwd'); + } + const passwordInRequest = ('trade_pwd' in request); + if (!passwordInRequest) { + throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a password / trade_pwd parameter'); + } + const response = await this.privatePostWithdraw (this.extend (request, query)); + return { + 'info': response, + 'id': this.safeString (response, 'withdraw_id'), + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = '/'; + const isWebApi = (api === 'web') || (api === 'v3'); + if (!isWebApi) { + url += this.version + '/'; + } + url += path; + if (!isWebApi) { + url += this.extension; + } + if (api === 'private') { + this.checkRequiredCredentials (); + const query = this.keysort (this.extend ({ + 'api_key': this.apiKey, + }, params)); + // secret key must be at the end of query + const queryString = this.rawencode (query) + '&secret_key=' + this.secret; + query['sign'] = this.hash (this.encode (queryString)).toUpperCase (); + body = this.urlencode (query); + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } else { + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } + url = this.urls['api'][api] + url; + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + createRequest (market, params = {}) { + if (market['future']) { + return this.deepExtend ({ + 'symbol': market['lowercaseId'], + 'contract_type': market['contractType'], + }, params); + } + return this.deepExtend ({ + 'symbol': market['id'], + }, params); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('error_code' in response) { + const error = this.safeString (response, 'error_code'); + const message = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, error, message); + throw new ExchangeError (message); + } + if ('result' in response) { + if (!response['result']) { + throw new ExchangeError (this.id + ' ' + body); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/okex.js b/TaskServer/node_modules/ccxt/js/okex.js new file mode 100644 index 000000000..d13a78f98 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/okex.js @@ -0,0 +1,65 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const okcoinusd = require ('./okcoinusd.js'); + +// --------------------------------------------------------------------------- + +module.exports = class okex extends okcoinusd { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okex', + 'name': 'OKEX', + 'countries': [ 'CN', 'US' ], + 'has': { + 'CORS': false, + 'futures': true, + 'fetchTickers': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/32552768-0d6dd3c6-c4a6-11e7-90f8-c043b64756a7.jpg', + 'api': { + 'v3': 'https://www.okex.com/v3', + 'web': 'https://www.okex.com/v2', + 'public': 'https://www.okex.com/api', + 'private': 'https://www.okex.com/api', + }, + 'www': 'https://www.okex.com', + 'doc': [ + 'https://github.com/okcoin-okex/API-docs-OKEx.com', + 'https://www.okex.com/docs/en/', + ], + 'fees': 'https://www.okex.com/pages/products/fees.html', + 'referral': 'https://www.okex.com', + }, + 'fees': { + 'trading': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'spot': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'future': { + 'taker': 0.0005, + 'maker': 0.0002, + }, + 'swap': { + 'taker': 0.00075, + 'maker': 0.0002, + }, + }, + 'commonCurrencies': { + // OKEX refers to ERC20 version of Aeternity (AEToken) + 'AE': 'AET', // https://github.com/ccxt/ccxt/issues/4981 + 'HOT': 'Hydro Protocol', + 'HSR': 'HC', + 'MAG': 'Maggie', + 'YOYO': 'YOYOW', + 'WIN': 'WinToken', // https://github.com/ccxt/ccxt/issues/5701 + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/okex3.js b/TaskServer/node_modules/ccxt/js/okex3.js new file mode 100644 index 000000000..22c83cce3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/okex3.js @@ -0,0 +1,2722 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, AccountSuspended, InvalidAddress, PermissionDenied, DDoSProtection, InsufficientFunds, InvalidNonce, CancelPending, InvalidOrder, OrderNotFound, AuthenticationError, RequestTimeout, NotSupported, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class okex3 extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'okex3', + 'name': 'OKEX', + 'countries': [ 'CN', 'US' ], + 'version': 'v3', + 'rateLimit': 1000, // up to 3000 requests per 5 minutes ≈ 600 requests per minute ≈ 10 requests per second ≈ 100 ms + 'has': { + 'CORS': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrders': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': false, // see below + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTime': true, + 'fetchTransactions': false, + 'fetchMyTrades': false, // they don't have it + 'fetchDepositAddress': true, + 'fetchOrderTrades': true, + 'fetchTickers': true, + 'fetchLedger': true, + 'withdraw': true, + 'futures': true, + }, + 'timeframes': { + '1m': '60', + '3m': '180', + '5m': '300', + '15m': '900', + '30m': '1800', + '1h': '3600', + '2h': '7200', + '4h': '14400', + '6h': '21600', + '12h': '43200', + '1d': '86400', + '1w': '604800', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/32552768-0d6dd3c6-c4a6-11e7-90f8-c043b64756a7.jpg', + 'api': 'https://www.okex.com', + 'www': 'https://www.okex.com', + 'doc': 'https://www.okex.com/docs/en/', + 'fees': 'https://www.okex.com/pages/products/fees.html', + }, + 'api': { + 'general': { + 'get': [ + 'time', + ], + }, + 'account': { + 'get': [ + 'currencies', + 'wallet', + 'wallet/{currency}', + 'withdrawal/fee', + 'withdrawal/history', + 'withdrawal/history/{currency}', + 'ledger', + 'deposit/address', + 'deposit/history', + 'deposit/history/{currency}', + ], + 'post': [ + 'transfer', + 'withdrawal', + ], + }, + 'spot': { + 'get': [ + 'accounts', + 'accounts/{currency}', + 'accounts/{currency}/ledger', + 'orders', + 'orders_pending', + 'orders/{order_id}', + 'orders/{client_oid}', + 'fills', + 'algo', + // public + 'instruments', + 'instruments/{instrument_id}/book', + 'instruments/ticker', + 'instruments/{instrument_id}/ticker', + 'instruments/{instrument_id}/trades', + 'instruments/{instrument_id}/candles', + ], + 'post': [ + 'order_algo', + 'orders', + 'batch_orders', + 'cancel_orders/{order_id}', + 'cancel_orders/{client_oid}', + 'cancel_batch_algos', + 'cancel_batch_orders', + ], + }, + 'margin': { + 'get': [ + 'accounts', + 'accounts/{instrument_id}', + 'accounts/{instrument_id}/ledger', + 'accounts/availability', + 'accounts/{instrument_id}/availability', + 'accounts/borrowed', + 'accounts/{instrument_id}/borrowed', + 'orders', + 'orders/{order_id}', + 'orders/{client_oid}', + 'orders_pending', + 'fills', + ], + 'post': [ + 'accounts/borrow', + 'accounts/repayment', + 'orders', + 'batch_orders', + 'cancel_orders', + 'cancel_orders/{order_id}', + 'cancel_orders/{client_oid}', + 'cancel_batch_orders', + ], + }, + 'futures': { + 'get': [ + 'position', + '{instrument_id}/position', + 'accounts', + 'accounts/{currency}', + 'accounts/{currency}/leverage', + 'accounts/{currency}/ledger', + 'order_algo/{instrument_id}', + 'orders/{instrument_id}', + 'orders/{instrument_id}/{order_id}', + 'orders/{instrument_id}/{client_oid}', + 'fills', + // public + 'instruments', + 'instruments/{instrument_id}/book', + 'instruments/ticker', + 'instruments/{instrument_id}/ticker', + 'instruments/{instrument_id}/trades', + 'instruments/{instrument_id}/candles', + 'accounts/{instrument_id}/holds', + 'instruments/{instrument_id}/index', + 'rate', + 'instruments/{instrument_id}/estimated_price', + 'instruments/{instrument_id}/open_interest', + 'instruments/{instrument_id}/price_limit', + 'instruments/{instrument_id}/liquidation', + 'instruments/{instrument_id}/mark_price', + ], + 'post': [ + 'accounts/{currency}/leverage', + 'accounts/margin_mode', + 'order', + 'orders', + 'order_algo', + 'cancel_algos', + 'cancel_order/{instrument_id}/{order_id}', + 'cancel_order/{instrument_id}/{client_oid}', + 'cancel_batch_orders/{instrument_id}', + 'close_position', + 'cancel_all', + ], + }, + 'swap': { + 'get': [ + 'position', + '{instrument_id}/position', + 'accounts', + '{instrument_id}/accounts', + 'accounts/{instrument_id}/settings', + 'accounts/{instrument_id}/ledger', + 'accounts/{instrument_id}/holds', + 'order_algo/{instrument_id}', + 'orders/{instrument_id}', + 'orders/{instrument_id}/{order_id}', + 'orders/{instrument_id}/{client_oid}', + 'fills', + // public + 'instruments', + 'instruments/{instrument_id}/depth', + 'instruments/ticker', + 'instruments/{instrument_id}/ticker', + 'instruments/{instrument_id}/trades', + 'instruments/{instrument_id}/candles', + 'instruments/{instrument_id}/index', + 'rate', + 'instruments/{instrument_id}/open_interest', + 'instruments/{instrument_id}/price_limit', + 'instruments/{instrument_id}/liquidation', + 'instruments/{instrument_id}/funding_time', + 'instruments/{instrument_id}/mark_price', + 'instruments/{instrument_id}/historical_funding_rate', + ], + 'post': [ + 'accounts/{instrument_id}/leverage', + 'order', + 'order_algo', + 'orders', + 'cancel_algos', + 'cancel_order/{instrument_id}/{order_id}', + 'cancel_order/{instrument_id}/{client_oid}', + 'cancel_batch_orders/{instrument_id}', + ], + }, + // they have removed this part from public + 'ett': { + 'get': [ + 'accounts', + 'accounts/{currency}', + 'accounts/{currency}/ledger', + 'orders', // fetchOrder, fetchOrders + // public + 'constituents/{ett}', + 'define-price/{ett}', + ], + 'post': [ + 'orders', + 'orders/{order_id}', + ], + }, + }, + 'fees': { + 'trading': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'spot': { + 'taker': 0.0015, + 'maker': 0.0010, + }, + 'futures': { + 'taker': 0.0005, + 'maker': 0.0002, + }, + 'swap': { + 'taker': 0.00075, + 'maker': 0.00020, + }, + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'exceptions': { + // http error codes + // 400 Bad Request — Invalid request format + // 401 Unauthorized — Invalid API Key + // 403 Forbidden — You do not have access to the requested resource + // 404 Not Found + // 500 Internal Server Error — We had a problem with our server + 'exact': { + '1': ExchangeError, // { "code": 1, "message": "System error" } + // undocumented + 'failure to get a peer from the ring-balancer': ExchangeNotAvailable, // { "message": "failure to get a peer from the ring-balancer" } + '4010': PermissionDenied, // { "code": 4010, "message": "For the security of your funds, withdrawals are not permitted within 24 hours after changing fund password / mobile number / Google Authenticator settings " } + // common + '30001': AuthenticationError, // { "code": 30001, "message": 'request header "OK_ACCESS_KEY" cannot be blank'} + '30002': AuthenticationError, // { "code": 30002, "message": 'request header "OK_ACCESS_SIGN" cannot be blank'} + '30003': AuthenticationError, // { "code": 30003, "message": 'request header "OK_ACCESS_TIMESTAMP" cannot be blank'} + '30004': AuthenticationError, // { "code": 30004, "message": 'request header "OK_ACCESS_PASSPHRASE" cannot be blank'} + '30005': InvalidNonce, // { "code": 30005, "message": "invalid OK_ACCESS_TIMESTAMP" } + '30006': AuthenticationError, // { "code": 30006, "message": "invalid OK_ACCESS_KEY" } + '30007': BadRequest, // { "code": 30007, "message": 'invalid Content_Type, please use "application/json" format'} + '30008': RequestTimeout, // { "code": 30008, "message": "timestamp request expired" } + '30009': ExchangeError, // { "code": 30009, "message": "system error" } + '30010': AuthenticationError, // { "code": 30010, "message": "API validation failed" } + '30011': PermissionDenied, // { "code": 30011, "message": "invalid IP" } + '30012': AuthenticationError, // { "code": 30012, "message": "invalid authorization" } + '30013': AuthenticationError, // { "code": 30013, "message": "invalid sign" } + '30014': DDoSProtection, // { "code": 30014, "message": "request too frequent" } + '30015': AuthenticationError, // { "code": 30015, "message": 'request header "OK_ACCESS_PASSPHRASE" incorrect'} + '30016': ExchangeError, // { "code": 30015, "message": "you are using v1 apiKey, please use v1 endpoint. If you would like to use v3 endpoint, please subscribe to v3 apiKey" } + '30017': ExchangeError, // { "code": 30017, "message": "apikey's broker id does not match" } + '30018': ExchangeError, // { "code": 30018, "message": "apikey's domain does not match" } + '30019': ExchangeNotAvailable, // { "code": 30019, "message": "Api is offline or unavailable" } + '30020': BadRequest, // { "code": 30020, "message": "body cannot be blank" } + '30021': BadRequest, // { "code": 30021, "message": "Json data format error" }, { "code": 30021, "message": "json data format error" } + '30022': PermissionDenied, // { "code": 30022, "message": "Api has been frozen" } + '30023': BadRequest, // { "code": 30023, "message": "{0} parameter cannot be blank" } + '30024': BadSymbol, // {"code":30024,"message":"\"instrument_id\" is an invalid parameter"} + '30025': BadRequest, // { "code": 30025, "message": "{0} parameter category error" } + '30026': DDoSProtection, // { "code": 30026, "message": "requested too frequent" } + '30027': AuthenticationError, // { "code": 30027, "message": "login failure" } + '30028': PermissionDenied, // { "code": 30028, "message": "unauthorized execution" } + '30029': AccountSuspended, // { "code": 30029, "message": "account suspended" } + '30030': ExchangeError, // { "code": 30030, "message": "endpoint request failed. Please try again" } + '30031': BadRequest, // { "code": 30031, "message": "token does not exist" } + '30032': BadSymbol, // { "code": 30032, "message": "pair does not exist" } + '30033': BadRequest, // { "code": 30033, "message": "exchange domain does not exist" } + '30034': ExchangeError, // { "code": 30034, "message": "exchange ID does not exist" } + '30035': ExchangeError, // { "code": 30035, "message": "trading is not supported in this website" } + '30036': ExchangeError, // { "code": 30036, "message": "no relevant data" } + '30038': AuthenticationError, // { "code": 30038, "message": "user does not exist" } + '30037': ExchangeNotAvailable, // { "code": 30037, "message": "endpoint is offline or unavailable" } + // futures + '32001': AccountSuspended, // { "code": 32001, "message": "futures account suspended" } + '32002': PermissionDenied, // { "code": 32002, "message": "futures account does not exist" } + '32003': CancelPending, // { "code": 32003, "message": "canceling, please wait" } + '32004': ExchangeError, // { "code": 32004, "message": "you have no unfilled orders" } + '32005': InvalidOrder, // { "code": 32005, "message": "max order quantity" } + '32006': InvalidOrder, // { "code": 32006, "message": "the order price or trigger price exceeds USD 1 million" } + '32007': InvalidOrder, // { "code": 32007, "message": "leverage level must be the same for orders on the same side of the contract" } + '32008': InvalidOrder, // { "code": 32008, "message": "Max. positions to open (cross margin)" } + '32009': InvalidOrder, // { "code": 32009, "message": "Max. positions to open (fixed margin)" } + '32010': ExchangeError, // { "code": 32010, "message": "leverage cannot be changed with open positions" } + '32011': ExchangeError, // { "code": 32011, "message": "futures status error" } + '32012': ExchangeError, // { "code": 32012, "message": "futures order update error" } + '32013': ExchangeError, // { "code": 32013, "message": "token type is blank" } + '32014': ExchangeError, // { "code": 32014, "message": "your number of contracts closing is larger than the number of contracts available" } + '32015': ExchangeError, // { "code": 32015, "message": "margin ratio is lower than 100% before opening positions" } + '32016': ExchangeError, // { "code": 32016, "message": "margin ratio is lower than 100% after opening position" } + '32017': ExchangeError, // { "code": 32017, "message": "no BBO" } + '32018': ExchangeError, // { "code": 32018, "message": "the order quantity is less than 1, please try again" } + '32019': ExchangeError, // { "code": 32019, "message": "the order price deviates from the price of the previous minute by more than 3%" } + '32020': ExchangeError, // { "code": 32020, "message": "the price is not in the range of the price limit" } + '32021': ExchangeError, // { "code": 32021, "message": "leverage error" } + '32022': ExchangeError, // { "code": 32022, "message": "this function is not supported in your country or region according to the regulations" } + '32023': ExchangeError, // { "code": 32023, "message": "this account has outstanding loan" } + '32024': ExchangeError, // { "code": 32024, "message": "order cannot be placed during delivery" } + '32025': ExchangeError, // { "code": 32025, "message": "order cannot be placed during settlement" } + '32026': ExchangeError, // { "code": 32026, "message": "your account is restricted from opening positions" } + '32029': ExchangeError, // { "code": 32029, "message": "order info does not exist" } + '32028': ExchangeError, // { "code": 32028, "message": "account is suspended and liquidated" } + '32027': ExchangeError, // { "code": 32027, "message": "cancelled over 20 orders" } + '32044': ExchangeError, // { "code": 32044, "message": "The margin ratio after submitting this order is lower than the minimum requirement ({0}) for your tier." } + // token and margin trading + '33001': PermissionDenied, // { "code": 33001, "message": "margin account for this pair is not enabled yet" } + '33002': AccountSuspended, // { "code": 33002, "message": "margin account for this pair is suspended" } + '33003': InsufficientFunds, // { "code": 33003, "message": "no loan balance" } + '33004': ExchangeError, // { "code": 33004, "message": "loan amount cannot be smaller than the minimum limit" } + '33005': ExchangeError, // { "code": 33005, "message": "repayment amount must exceed 0" } + '33006': ExchangeError, // { "code": 33006, "message": "loan order not found" } + '33007': ExchangeError, // { "code": 33007, "message": "status not found" } + '33008': ExchangeError, // { "code": 33008, "message": "loan amount cannot exceed the maximum limit" } + '33009': ExchangeError, // { "code": 33009, "message": "user ID is blank" } + '33010': ExchangeError, // { "code": 33010, "message": "you cannot cancel an order during session 2 of call auction" } + '33011': ExchangeError, // { "code": 33011, "message": "no new market data" } + '33012': ExchangeError, // { "code": 33012, "message": "order cancellation failed" } + '33013': InvalidOrder, // { "code": 33013, "message": "order placement failed" } + '33014': OrderNotFound, // { "code": 33014, "message": "order does not exist" } + '33015': InvalidOrder, // { "code": 33015, "message": "exceeded maximum limit" } + '33016': ExchangeError, // { "code": 33016, "message": "margin trading is not open for this token" } + '33017': InsufficientFunds, // { "code": 33017, "message": "insufficient balance" } + '33018': ExchangeError, // { "code": 33018, "message": "this parameter must be smaller than 1" } + '33020': ExchangeError, // { "code": 33020, "message": "request not supported" } + '33021': BadRequest, // { "code": 33021, "message": "token and the pair do not match" } + '33022': InvalidOrder, // { "code": 33022, "message": "pair and the order do not match" } + '33023': ExchangeError, // { "code": 33023, "message": "you can only place market orders during call auction" } + '33024': InvalidOrder, // { "code": 33024, "message": "trading amount too small" } + '33025': InvalidOrder, // { "code": 33025, "message": "base token amount is blank" } + '33026': ExchangeError, // { "code": 33026, "message": "transaction completed" } + '33027': InvalidOrder, // { "code": 33027, "message": "cancelled order or order cancelling" } + '33028': InvalidOrder, // { "code": 33028, "message": "the decimal places of the trading price exceeded the limit" } + '33029': InvalidOrder, // { "code": 33029, "message": "the decimal places of the trading size exceeded the limit" } + '33034': ExchangeError, // { "code": 33034, "message": "You can only place limit order after Call Auction has started" } + '33059': BadRequest, // { "code": 33059, "message": "client_oid or order_id is required" } + '33060': BadRequest, // { "code": 33060, "message": "Only fill in either parameter client_oid or order_id" } + // account + '34001': PermissionDenied, // { "code": 34001, "message": "withdrawal suspended" } + '34002': InvalidAddress, // { "code": 34002, "message": "please add a withdrawal address" } + '34003': ExchangeError, // { "code": 34003, "message": "sorry, this token cannot be withdrawn to xx at the moment" } + '34004': ExchangeError, // { "code": 34004, "message": "withdrawal fee is smaller than minimum limit" } + '34005': ExchangeError, // { "code": 34005, "message": "withdrawal fee exceeds the maximum limit" } + '34006': ExchangeError, // { "code": 34006, "message": "withdrawal amount is lower than the minimum limit" } + '34007': ExchangeError, // { "code": 34007, "message": "withdrawal amount exceeds the maximum limit" } + '34008': InsufficientFunds, // { "code": 34008, "message": "insufficient balance" } + '34009': ExchangeError, // { "code": 34009, "message": "your withdrawal amount exceeds the daily limit" } + '34010': ExchangeError, // { "code": 34010, "message": "transfer amount must be larger than 0" } + '34011': ExchangeError, // { "code": 34011, "message": "conditions not met" } + '34012': ExchangeError, // { "code": 34012, "message": "the minimum withdrawal amount for NEO is 1, and the amount must be an integer" } + '34013': ExchangeError, // { "code": 34013, "message": "please transfer" } + '34014': ExchangeError, // { "code": 34014, "message": "transfer limited" } + '34015': ExchangeError, // { "code": 34015, "message": "subaccount does not exist" } + '34016': PermissionDenied, // { "code": 34016, "message": "transfer suspended" } + '34017': AccountSuspended, // { "code": 34017, "message": "account suspended" } + '34018': AuthenticationError, // { "code": 34018, "message": "incorrect trades password" } + '34019': PermissionDenied, // { "code": 34019, "message": "please bind your email before withdrawal" } + '34020': PermissionDenied, // { "code": 34020, "message": "please bind your funds password before withdrawal" } + '34021': InvalidAddress, // { "code": 34021, "message": "Not verified address" } + '34022': ExchangeError, // { "code": 34022, "message": "Withdrawals are not available for sub accounts" } + '34023': PermissionDenied, // { "code": 34023, "message": "Please enable futures trading before transferring your funds" } + // swap + '35001': ExchangeError, // { "code": 35001, "message": "Contract does not exist" } + '35002': ExchangeError, // { "code": 35002, "message": "Contract settling" } + '35003': ExchangeError, // { "code": 35003, "message": "Contract paused" } + '35004': ExchangeError, // { "code": 35004, "message": "Contract pending settlement" } + '35005': AuthenticationError, // { "code": 35005, "message": "User does not exist" } + '35008': InvalidOrder, // { "code": 35008, "message": "Risk ratio too high" } + '35010': InvalidOrder, // { "code": 35010, "message": "Position closing too large" } + '35012': InvalidOrder, // { "code": 35012, "message": "Incorrect order size" } + '35014': InvalidOrder, // { "code": 35014, "message": "Order price is not within limit" } + '35015': InvalidOrder, // { "code": 35015, "message": "Invalid leverage level" } + '35017': ExchangeError, // { "code": 35017, "message": "Open orders exist" } + '35019': InvalidOrder, // { "code": 35019, "message": "Order size too large" } + '35020': InvalidOrder, // { "code": 35020, "message": "Order price too high" } + '35021': InvalidOrder, // { "code": 35021, "message": "Order size exceeded current tier limit" } + '35022': ExchangeError, // { "code": 35022, "message": "Contract status error" } + '35024': ExchangeError, // { "code": 35024, "message": "Contract not initialized" } + '35025': InsufficientFunds, // { "code": 35025, "message": "No account balance" } + '35026': ExchangeError, // { "code": 35026, "message": "Contract settings not initialized" } + '35029': OrderNotFound, // { "code": 35029, "message": "Order does not exist" } + '35030': InvalidOrder, // { "code": 35030, "message": "Order size too large" } + '35031': InvalidOrder, // { "code": 35031, "message": "Cancel order size too large" } + '35032': ExchangeError, // { "code": 35032, "message": "Invalid user status" } + '35039': ExchangeError, // { "code": 35039, "message": "Open order quantity exceeds limit" } + '35040': InvalidOrder, // {"error_message":"Invalid order type","result":"true","error_code":"35040","order_id":"-1"} + '35044': ExchangeError, // { "code": 35044, "message": "Invalid order status" } + '35046': InsufficientFunds, // { "code": 35046, "message": "Negative account balance" } + '35047': InsufficientFunds, // { "code": 35047, "message": "Insufficient account balance" } + '35048': ExchangeError, // { "code": 35048, "message": "User contract is frozen and liquidating" } + '35049': InvalidOrder, // { "code": 35049, "message": "Invalid order type" } + '35050': InvalidOrder, // { "code": 35050, "message": "Position settings are blank" } + '35052': InsufficientFunds, // { "code": 35052, "message": "Insufficient cross margin" } + '35053': ExchangeError, // { "code": 35053, "message": "Account risk too high" } + '35055': InsufficientFunds, // { "code": 35055, "message": "Insufficient account balance" } + '35057': ExchangeError, // { "code": 35057, "message": "No last traded price" } + '35058': ExchangeError, // { "code": 35058, "message": "No limit" } + '35059': BadRequest, // { "code": 35059, "message": "client_oid or order_id is required" } + '35060': BadRequest, // { "code": 35060, "message": "Only fill in either parameter client_oid or order_id" } + '35061': BadRequest, // { "code": 35061, "message": "Invalid instrument_id" } + '35062': InvalidOrder, // { "code": 35062, "message": "Invalid match_price" } + '35063': InvalidOrder, // { "code": 35063, "message": "Invalid order_size" } + '35064': InvalidOrder, // { "code": 35064, "message": "Invalid client_oid" } + }, + 'broad': { + }, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + 'fetchMarkets': [ 'spot', 'futures', 'swap' ], + 'defaultType': 'spot', // 'account', 'spot', 'margin', 'futures', 'swap' + 'auth': { + 'time': 'public', + 'currencies': 'private', + 'instruments': 'public', + 'rate': 'public', + 'constituents/{ett}': 'public', + 'define-price/{ett}': 'public', + }, + }, + 'commonCurrencies': { + // OKEX refers to ERC20 version of Aeternity (AEToken) + 'AE': 'AET', // https://github.com/ccxt/ccxt/issues/4981 + 'HOT': 'Hydro Protocol', + 'HSR': 'HC', + 'MAG': 'Maggie', + 'YOYO': 'YOYOW', + 'WIN': 'WinToken', // https://github.com/ccxt/ccxt/issues/5701 + }, + }); + } + + async fetchTime (params = {}) { + const response = await this.generalGetTime (params); + // + // { + // "iso": "2015-01-07T23:47:25.201Z", + // "epoch": 1420674445.201 + // } + // + return this.parse8601 (this.safeString (response, 'iso')); + } + + async fetchMarkets (params = {}) { + const types = this.safeValue (this.options, 'fetchMarkets'); + let result = []; + for (let i = 0; i < types.length; i++) { + const markets = await this.fetchMarketsByType (types[i], params); + result = this.arrayConcat (result, markets); + } + return result; + } + + parseMarkets (markets) { + const result = []; + for (let i = 0; i < markets.length; i++) { + result.push (this.parseMarket (markets[i])); + } + return result; + } + + parseMarket (market) { + // + // spot markets + // + // [ { base_currency: "EOS", + // instrument_id: "EOS-OKB", + // min_size: "0.01", + // product_id: "EOS-OKB", + // quote_currency: "OKB", + // size_increment: "0.000001", + // tick_size: "0.0001" }, + // + // ..., // the spot endpoint also returns ETT instruments + // + // { base_currency: "OK06ETT", + // base_increment: "0.00000001", + // base_min_size: "0.01", + // instrument_id: "OK06ETT-USDT", + // min_size: "0.01", + // product_id: "OK06ETT-USDT", + // quote_currency: "USDT", + // quote_increment: "0.0001", + // size_increment: "0.00000001", + // tick_size: "0.0001" } ] + // + // futures markets + // + // [ { instrument_id: "BTG-USD-190329", + // underlying_index: "BTG", + // quote_currency: "USD", + // tick_size: "0.01", + // contract_val: "10", + // listing: "2018-12-14", + // delivery: "2019-03-29", + // trade_increment: "1" } ] + // + // swap markets + // + // [ { instrument_id: "BTC-USD-SWAP", + // underlying_index: "BTC", + // quote_currency: "USD", + // coin: "BTC", + // contract_val: "100", + // listing: "2018-10-23T20:11:00.443Z", + // delivery: "2018-10-24T20:11:00.443Z", + // size_increment: "4", + // tick_size: "4" } ] + // + const id = this.safeString (market, 'instrument_id'); + let marketType = 'spot'; + let spot = true; + let future = false; + let swap = false; + let baseId = this.safeString (market, 'base_currency'); + const contractVal = this.safeFloat (market, 'contract_val'); + if (contractVal !== undefined) { + marketType = 'swap'; + spot = false; + swap = true; + baseId = this.safeString (market, 'coin'); + const futuresAlias = this.safeString (market, 'alias'); + if (futuresAlias !== undefined) { + swap = false; + future = true; + marketType = 'futures'; + baseId = this.safeString (market, 'underlying_index'); + } + } + const quoteId = this.safeString (market, 'quote_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = spot ? (base + '/' + quote) : id; + let amountPrecision = this.safeString (market, 'size_increment'); + if (amountPrecision !== undefined) { + amountPrecision = this.precisionFromString (amountPrecision); + } + let pricePrecision = this.safeString (market, 'tick_size'); + if (pricePrecision !== undefined) { + pricePrecision = this.precisionFromString (pricePrecision); + } + const precision = { + 'amount': amountPrecision, + 'price': pricePrecision, + }; + const minAmount = this.safeFloat2 (market, 'min_size', 'base_min_size'); + let minPrice = this.safeFloat (market, 'tick_size'); + if (precision['price'] !== undefined) { + minPrice = Math.pow (10, -precision['price']); + } + let minCost = undefined; + if (minAmount !== undefined && minPrice !== undefined) { + minCost = minAmount * minPrice; + } + const active = true; + const fees = this.safeValue2 (this.fees, marketType, 'trading', {}); + return this.extend (fees, { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'type': marketType, + 'spot': spot, + 'futures': future, + 'swap': swap, + 'active': active, + 'precision': precision, + 'limits': { + 'amount': { + 'min': minAmount, + 'max': undefined, + }, + 'price': { + 'min': minPrice, + 'max': undefined, + }, + 'cost': { + 'min': minCost, + 'max': undefined, + }, + }, + }); + } + + async fetchMarketsByType (type, params = {}) { + const method = type + 'GetInstruments'; + const response = await this[method] (params); + // + // spot markets + // + // [ { base_currency: "EOS", + // base_increment: "0.000001", + // base_min_size: "0.01", + // instrument_id: "EOS-OKB", + // min_size: "0.01", + // product_id: "EOS-OKB", + // quote_currency: "OKB", + // quote_increment: "0.0001", + // size_increment: "0.000001", + // tick_size: "0.0001" } ] + // + // futures markets + // + // [ { instrument_id: "BTG-USD-190329", + // underlying_index: "BTG", + // quote_currency: "USD", + // tick_size: "0.01", + // contract_val: "10", + // listing: "2018-12-14", + // delivery: "2019-03-29", + // trade_increment: "1" } ] + // + // swap markets + // + // [ { instrument_id: "BTC-USD-SWAP", + // underlying_index: "BTC", + // quote_currency: "USD", + // coin: "BTC", + // contract_val: "100", + // listing: "2018-10-23T20:11:00.443Z", + // delivery: "2018-10-24T20:11:00.443Z", + // size_increment: "4", + // tick_size: "4" } ] + // + return this.parseMarkets (response); + } + + async fetchCurrencies (params = {}) { + // has['fetchCurrencies'] is currently set to false + // despite that their docs say these endpoints are public: + // https://www.okex.com/api/account/v3/withdrawal/fee + // https://www.okex.com/api/account/v3/currencies + // it will still reply with { "code":30001, "message": "OK-ACCESS-KEY header is required" } + // if you attempt to access it without authentication + const response = await this.accountGetCurrencies (params); + // + // [ + // { + // name: '', + // currency: 'BTC', + // can_withdraw: '1', + // can_deposit: '1', + // min_withdrawal: '0.0100000000000000' + // }, + // ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'currency'); + const code = this.safeCurrencyCode (id); + const precision = 8; // default precision, todo: fix "magic constants" + const name = this.safeString (currency, 'name'); + const canDeposit = this.safeInteger (currency, 'can_deposit'); + const canWithdraw = this.safeInteger (currency, 'can_withdraw'); + const active = canDeposit && canWithdraw; + result[code] = { + 'id': id, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': name, + 'active': active, + 'fee': undefined, // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'withdraw': { + 'min': this.safeFloat (currency, 'min_withdrawal'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let method = market['type'] + 'GetInstrumentsInstrumentId'; + method += (market['type'] === 'swap') ? 'Depth' : 'Book'; + const request = { + 'instrument_id': market['id'], + }; + if (limit !== undefined) { + request['size'] = limit; // max 200 + } + const response = await this[method] (this.extend (request, params)); + // + // { asks: [ ["0.02685268", "0.242571", "1"], + // ["0.02685493", "0.164085", "1"], + // ... + // ["0.02779", "1.039", "1"], + // ["0.027813", "0.0876", "1"] ], + // bids: [ ["0.02684052", "10.371849", "1"], + // ["0.02684051", "3.707", "4"], + // ... + // ["0.02634963", "0.132934", "1"], + // ["0.02634962", "0.264838", "2"] ], + // timestamp: "2018-12-17T20:24:16.159Z" } + // + const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + // + // { best_ask: "0.02665472", + // best_bid: "0.02665221", + // instrument_id: "ETH-BTC", + // product_id: "ETH-BTC", + // last: "0.02665472", + // ask: "0.02665472", // missing in the docs + // bid: "0.02665221", // not mentioned in the docs + // open_24h: "0.02645482", + // high_24h: "0.02714633", + // low_24h: "0.02614109", + // base_volume_24h: "572298.901923", + // timestamp: "2018-12-17T21:20:07.856Z", + // quote_volume_24h: "15094.86831261" } + // + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + let symbol = undefined; + const marketId = this.safeString (ticker, 'instrument_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else if (marketId !== undefined) { + const parts = marketId.split ('-'); + const numParts = parts.length; + if (numParts === 2) { + const [ baseId, quoteId ] = parts; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else { + symbol = marketId; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open_24h'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high_24h'), + 'low': this.safeFloat (ticker, 'low_24h'), + 'bid': this.safeFloat (ticker, 'best_bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'best_ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'base_volume_24h'), + 'quoteVolume': this.safeFloat (ticker, 'quote_volume_24h'), + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['type'] + 'GetInstrumentsInstrumentIdTicker'; + const request = { + 'instrument_id': market['id'], + }; + const response = await this[method] (this.extend (request, params)); + // + // { best_ask: "0.02665472", + // best_bid: "0.02665221", + // instrument_id: "ETH-BTC", + // product_id: "ETH-BTC", + // last: "0.02665472", + // ask: "0.02665472", + // bid: "0.02665221", + // open_24h: "0.02645482", + // high_24h: "0.02714633", + // low_24h: "0.02614109", + // base_volume_24h: "572298.901923", + // timestamp: "2018-12-17T21:20:07.856Z", + // quote_volume_24h: "15094.86831261" } + // + return this.parseTicker (response); + } + + async fetchTickersByType (type, symbols = undefined, params = {}) { + await this.loadMarkets (); + const method = type + 'GetInstrumentsTicker'; + const response = await this[method] (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const ticker = this.parseTicker (response[i]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTickers (symbols = undefined, params = {}) { + const defaultType = this.safeString2 (this.options, 'fetchTickers', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + return await this.fetchTickersByType (type, symbols, this.omit (params, 'type')); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // spot trades + // + // { + // time: "2018-12-17T23:31:08.268Z", + // timestamp: "2018-12-17T23:31:08.268Z", + // trade_id: "409687906", + // price: "0.02677805", + // size: "0.923467", + // side: "sell" + // } + // + // futures trades, swap trades + // + // { + // trade_id: "1989230840021013", + // side: "buy", + // price: "92.42", + // qty: "184", // missing in swap markets + // size: "5", // missing in futures markets + // timestamp: "2018-12-17T23:26:04.613Z" + // } + // + // fetchOrderTrades (private) + // + // spot trades, margin trades + // + // { + // "created_at":"2019-03-15T02:52:56.000Z", + // "exec_type":"T", // whether the order is taker or maker + // "fee":"0.00000082", + // "instrument_id":"BTC-USDT", + // "ledger_id":"3963052721", + // "liquidity":"T", // whether the order is taker or maker + // "order_id":"2482659399697408", + // "price":"3888.6", + // "product_id":"BTC-USDT", + // "side":"buy", + // "size":"0.00055306", + // "timestamp":"2019-03-15T02:52:56.000Z" + // }, + // + // futures trades, swap trades + // + // { + // "trade_id":"197429674631450625", + // "instrument_id":"EOS-USD-SWAP", + // "order_id":"6a-7-54d663a28-0", + // "price":"3.633", + // "order_qty":"1.0000", + // "fee":"-0.000551", + // "created_at":"2019-03-21T04:41:58.0Z", // missing in swap trades + // "timestamp":"2019-03-25T05:56:31.287Z", // missing in futures trades + // "exec_type":"M", // whether the order is taker or maker + // "side":"short", // "buy" in futures trades + // } + // + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString2 (trade, 'timestamp', 'created_at')); + const price = this.safeFloat (trade, 'price'); + let amount = this.safeFloat2 (trade, 'size', 'qty'); + amount = this.safeFloat (trade, 'order_qty', amount); + let takerOrMaker = this.safeString2 (trade, 'exec_type', 'liquidity'); + if (takerOrMaker === 'M') { + takerOrMaker = 'maker'; + } else if (takerOrMaker === 'T') { + takerOrMaker = 'taker'; + } + const side = this.safeString (trade, 'side'); + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + const feeCost = this.safeFloat (trade, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = (side === 'buy') ? market['base'] : market['quote']; + } + fee = { + // fee is either a positive number (invitation rebate) + // or a negative number (transaction fee deduction) + // therefore we need to invert the fee + // more about it https://github.com/ccxt/ccxt/issues/5909 + 'cost': -feeCost, + 'currency': feeCurrency, + }; + } + const orderId = this.safeString (trade, 'order_id'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': this.safeString2 (trade, 'trade_id', 'ledger_id'), + 'order': orderId, + 'type': undefined, + 'takerOrMaker': takerOrMaker, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['type'] + 'GetInstrumentsInstrumentIdTrades'; + if ((limit === undefined) || (limit > 100)) { + limit = 100; // maximum = default = 100 + } + const request = { + 'instrument_id': market['id'], + 'limit': limit, + // from: 'id', + // to: 'id', + }; + const response = await this[method] (this.extend (request, params)); + // + // spot markets + // + // [ + // { + // time: "2018-12-17T23:31:08.268Z", + // timestamp: "2018-12-17T23:31:08.268Z", + // trade_id: "409687906", + // price: "0.02677805", + // size: "0.923467", + // side: "sell" + // } + // ] + // + // futures markets, swap markets + // + // [ + // { + // trade_id: "1989230840021013", + // side: "buy", + // price: "92.42", + // qty: "184", // missing in swap markets + // size: "5", // missing in futures markets + // timestamp: "2018-12-17T23:26:04.613Z" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // spot markets + // + // { + // close: "0.02684545", + // high: "0.02685084", + // low: "0.02683312", + // open: "0.02683894", + // time: "2018-12-17T20:28:00.000Z", + // volume: "101.457222" + // } + // + // futures markets + // + // [ + // 1545072720000, + // 0.3159, + // 0.3161, + // 0.3144, + // 0.3149, + // 22886, + // 725179.26172331, + // ] + // + if (Array.isArray (ohlcv)) { + const numElements = ohlcv.length; + const volumeIndex = (numElements > 6) ? 6 : 5; + let timestamp = ohlcv[0]; + if (typeof timestamp === 'string') { + timestamp = this.parse8601 (timestamp); + } + return [ + timestamp, // timestamp + parseFloat (ohlcv[1]), // Open + parseFloat (ohlcv[2]), // High + parseFloat (ohlcv[3]), // Low + parseFloat (ohlcv[4]), // Close + // parseFloat (ohlcv[5]), // Quote Volume + // parseFloat (ohlcv[6]), // Base Volume + parseFloat (ohlcv[volumeIndex]), // Volume, okex will return base volume in the 7th element for future markets + ]; + } else { + return [ + this.parse8601 (this.safeString (ohlcv, 'time')), + this.safeFloat (ohlcv, 'open'), // Open + this.safeFloat (ohlcv, 'high'), // High + this.safeFloat (ohlcv, 'low'), // Low + this.safeFloat (ohlcv, 'close'), // Close + this.safeFloat (ohlcv, 'volume'), // Base Volume + ]; + } + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = market['type'] + 'GetInstrumentsInstrumentIdCandles'; + const request = { + 'instrument_id': market['id'], + 'granularity': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['start'] = this.iso8601 (since); + } + const response = await this[method] (this.extend (request, params)); + // + // spot markets + // + // [ { close: "0.02683401", + // high: "0.02683401", + // low: "0.02683401", + // open: "0.02683401", + // time: "2018-12-17T23:47:00.000Z", + // volume: "0" }, + // ... + // { close: "0.02684545", + // high: "0.02685084", + // low: "0.02683312", + // open: "0.02683894", + // time: "2018-12-17T20:28:00.000Z", + // volume: "101.457222" } ] + // + // futures + // + // [ [ 1545090660000, + // 0.3171, + // 0.3174, + // 0.3171, + // 0.3173, + // 1648, + // 51930.38579450868 ], + // ... + // [ 1545072720000, + // 0.3159, + // 0.3161, + // 0.3144, + // 0.3149, + // 22886, + // 725179.26172331 ] ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseAccountBalance (response) { + // + // account + // + // [ + // { + // balance: 0, + // available: 0, + // currency: "BTC", + // hold: 0 + // }, + // { + // balance: 0, + // available: 0, + // currency: "ETH", + // hold: 0 + // } + // ] + // + // spot + // + // [ + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "BTC", + // balance: "0.0000000497717339", + // available: "0.0000000497717339", + // holds: "0" + // }, + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "ICN", + // balance: "0.00000000925", + // available: "0.00000000925", + // holds: "0" + // } + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'hold'); + account['free'] = this.safeFloat (balance, 'available'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseMarginBalance (response) { + // + // [ + // { + // "currency:BTC": { + // "available":"0", + // "balance":"0", + // "borrowed":"0", + // "can_withdraw":"0", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "currency:USDT": { + // "available":"100", + // "balance":"100", + // "borrowed":"0", + // "can_withdraw":"100", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "instrument_id":"BTC-USDT", + // "liquidation_price":"0", + // "product_id":"BTC-USDT", + // "risk_rate":"" + // }, + // ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const marketId = this.safeString (balance, 'instrument_id'); + const market = this.safeValue (this.markets_by_id, marketId); + let symbol = undefined; + if (market === undefined) { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } else { + symbol = market['symbol']; + } + const omittedBalance = this.omit (balance, [ + 'instrument_id', + 'liquidation_price', + 'product_id', + 'risk_rate', + 'margin_ratio', + 'maint_margin_ratio', + 'tiers', + ]); + const keys = Object.keys (omittedBalance); + const accounts = {}; + for (let k = 0; k < keys.length; k++) { + const key = keys[k]; + const marketBalance = balance[key]; + if (key.indexOf (':') >= 0) { + const parts = key.split (':'); + const currencyId = parts[1]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (marketBalance, 'balance'); + account['used'] = this.safeFloat (marketBalance, 'hold'); + account['free'] = this.safeFloat (marketBalance, 'available'); + accounts[code] = account; + } else { + throw new NotSupported (this.id + ' margin balance response format has changed!'); + } + } + result[symbol] = this.parseBalance (accounts); + } + return result; + } + + parseFuturesBalance (response) { + // + // { + // "info":{ + // "eos":{ + // "auto_margin":"0", + // "contracts": [ + // { + // "available_qty":"40.37069445", + // "fixed_balance":"0", + // "instrument_id":"EOS-USD-190329", + // "margin_for_unfilled":"0", + // "margin_frozen":"0", + // "realized_pnl":"0", + // "unrealized_pnl":"0" + // }, + // { + // "available_qty":"40.37069445", + // "fixed_balance":"14.54895721", + // "instrument_id":"EOS-USD-190628", + // "margin_for_unfilled":"0", + // "margin_frozen":"10.64042157", + // "realized_pnl":"-3.90853564", + // "unrealized_pnl":"-0.259" + // }, + // ], + // "equity":"50.75220665", + // "margin_mode":"fixed", + // "total_avail_balance":"40.37069445" + // }, + // } + // } + // + // their root field name is "info", so our info will contain their info + const result = { 'info': response }; + const info = this.safeValue (response, 'info', {}); + const ids = Object.keys (info); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const code = this.safeCurrencyCode (id); + const balance = this.safeValue (info, id, {}); + const account = this.account (); + // it may be incorrect to use total, free and used for swap accounts + account['total'] = this.safeFloat (balance, 'equity'); + account['free'] = this.safeFloat (balance, 'total_avail_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseSwapBalance (response) { + // + // { + // "info": [ + // { + // "equity":"3.0139", + // "fixed_balance":"0.0000", + // "instrument_id":"EOS-USD-SWAP", + // "margin":"0.5523", + // "margin_frozen":"0.0000", + // "margin_mode":"crossed", + // "margin_ratio":"1.0913", + // "realized_pnl":"-0.0006", + // "timestamp":"2019-03-25T03:46:10.336Z", + // "total_avail_balance":"3.0000", + // "unrealized_pnl":"0.0145" + // } + // ] + // } + // + // their root field name is "info", so our info will contain their info + const result = { 'info': response }; + const info = this.safeValue (response, 'info', []); + for (let i = 0; i < info.length; i++) { + const balance = info[i]; + const marketId = this.safeString (balance, 'instrument_id'); + let symbol = marketId; + if (marketId in this.markets_by_id) { + symbol = this.markets_by_id[marketId]['symbol']; + } + const account = this.account (); + // it may be incorrect to use total, free and used for swap accounts + account['total'] = this.safeFloat (balance, 'equity'); + account['free'] = this.safeFloat (balance, 'total_avail_balance'); + result[symbol] = account; + } + return this.parseBalance (result); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + if (type === undefined) { + throw new ArgumentsRequired (this.id + " fetchBalance requires a type parameter (one of 'account', 'spot', 'margin', 'futures', 'swap')"); + } + const suffix = (type === 'account') ? 'Wallet' : 'Accounts'; + const method = type + 'Get' + suffix; + const query = this.omit (params, 'type'); + const response = await this[method] (query); + // + // account + // + // [ + // { + // balance: 0, + // available: 0, + // currency: "BTC", + // hold: 0 + // }, + // { + // balance: 0, + // available: 0, + // currency: "ETH", + // hold: 0 + // } + // ] + // + // spot + // + // [ + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "BTC", + // balance: "0.0000000497717339", + // available: "0.0000000497717339", + // holds: "0" + // }, + // { + // frozen: "0", + // hold: "0", + // id: "2149632", + // currency: "ICN", + // balance: "0.00000000925", + // available: "0.00000000925", + // holds: "0" + // } + // ] + // + // margin + // + // [ + // { + // "currency:BTC": { + // "available":"0", + // "balance":"0", + // "borrowed":"0", + // "can_withdraw":"0", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "currency:USDT": { + // "available":"100", + // "balance":"100", + // "borrowed":"0", + // "can_withdraw":"100", + // "frozen":"0", + // "hold":"0", + // "holds":"0", + // "lending_fee":"0" + // }, + // "instrument_id":"BTC-USDT", + // "liquidation_price":"0", + // "product_id":"BTC-USDT", + // "risk_rate":"" + // }, + // ] + // + // futures + // + // { + // "info":{ + // "eos":{ + // "auto_margin":"0", + // "contracts": [ + // { + // "available_qty":"40.37069445", + // "fixed_balance":"0", + // "instrument_id":"EOS-USD-190329", + // "margin_for_unfilled":"0", + // "margin_frozen":"0", + // "realized_pnl":"0", + // "unrealized_pnl":"0" + // }, + // { + // "available_qty":"40.37069445", + // "fixed_balance":"14.54895721", + // "instrument_id":"EOS-USD-190628", + // "margin_for_unfilled":"0", + // "margin_frozen":"10.64042157", + // "realized_pnl":"-3.90853564", + // "unrealized_pnl":"-0.259" + // }, + // ], + // "equity":"50.75220665", + // "margin_mode":"fixed", + // "total_avail_balance":"40.37069445" + // }, + // } + // } + // + // swap + // + // { + // "info": [ + // { + // "equity":"3.0139", + // "fixed_balance":"0.0000", + // "instrument_id":"EOS-USD-SWAP", + // "margin":"0.5523", + // "margin_frozen":"0.0000", + // "margin_mode":"crossed", + // "margin_ratio":"1.0913", + // "realized_pnl":"-0.0006", + // "timestamp":"2019-03-25T03:46:10.336Z", + // "total_avail_balance":"3.0000", + // "unrealized_pnl":"0.0145" + // } + // ] + // } + // + if ((type === 'account') || (type === 'spot')) { + return this.parseAccountBalance (response); + } else if (type === 'margin') { + return this.parseMarginBalance (response); + } else if (type === 'futures') { + return this.parseFuturesBalance (response); + } else if (type === 'swap') { + return this.parseSwapBalance (response); + } + throw new NotSupported (this.id + " fetchBalance does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')"); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + let request = { + 'instrument_id': market['id'], + // 'client_oid': 'abcdef1234567890', // [a-z0-9]{1,32} + // 'order_type': '0', // 0: Normal limit order (Unfilled and 0 represent normal limit order) 1: Post only 2: Fill Or Kill 3: Immediatel Or Cancel + }; + let method = undefined; + if (market['futures'] || market['swap']) { + const size = market['futures'] ? this.numberToString (amount) : this.amountToPrecision (symbol, amount); + request = this.extend (request, { + 'type': type, // 1:open long 2:open short 3:close long 4:close short for futures + 'size': size, + 'price': this.priceToPrecision (symbol, price), + // 'match_price': '0', // Order at best counter party price? (0:no 1:yes). The default is 0. If it is set as 1, the price parameter will be ignored. When posting orders at best bid price, order_type can only be 0 (regular order). + }); + if (market['futures']) { + request['leverage'] = '10'; // or '20' + } + method = market['type'] + 'PostOrder'; + } else { + const marginTrading = this.safeString (params, 'margin_trading', '1'); // 1 = spot, 2 = margin + request = this.extend (request, { + 'side': side, + 'type': type, // limit/market + 'margin_trading': marginTrading, // 1 = spot, 2 = margin + }); + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + request['size'] = this.amountToPrecision (symbol, amount); + } else if (type === 'market') { + // for market buy it requires the amount of quote currency to spend + if (side === 'buy') { + let notional = this.safeFloat (params, 'notional'); + const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true); + if (createMarketBuyOrderRequiresPrice) { + if (price !== undefined) { + if (notional === undefined) { + notional = amount * price; + } + } else if (notional === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'notional' extra parameter (the exchange-specific behaviour)"); + } + } + request['notional'] = this.costToPrecision (symbol, notional); + } else { + request['size'] = this.amountToPrecision (symbol, amount); + } + } + method = (marginTrading === '2') ? 'marginPostOrders' : 'spotPostOrders'; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "client_oid":"oktspot79", + // "error_code":"", + // "error_message":"", + // "order_id":"2510789768709120", + // "result":true + // } + // + const timestamp = this.milliseconds (); + const id = this.safeString (response, 'order_id'); + return { + 'info': response, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'cost': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const type = market['type']; + let method = type + 'PostCancelOrder'; + const request = { + 'instrument_id': market['id'], + }; + if (market['futures'] || market['swap']) { + method += 'InstrumentId'; + } else { + method += 's'; + } + const clientOid = this.safeString (params, 'client_oid'); + if (clientOid !== undefined) { + method += 'ClientOid'; + request['client_oid'] = clientOid; + } else { + method += 'OrderId'; + request['order_id'] = id; + } + const query = this.omit (params, 'type'); + const response = await this[method] (this.extend (request, query)); + const result = ('result' in response) ? response : this.safeValue (response, market['id'], {}); + // + // spot, margin + // + // { + // "btc-usdt": [ + // { + // "result":true, + // "client_oid":"a123", + // "order_id": "2510832677225473" + // } + // ] + // } + // + // futures, swap + // + // { + // "result": true, + // "client_oid": "oktfuture10", // missing if requested by order_id + // "order_id": "2517535534836736", + // "instrument_id": "EOS-USD-190628" + // } + // + return this.parseOrder (result, market); + } + + parseOrderStatus (status) { + const statuses = { + '-2': 'failed', + '-1': 'canceled', + '0': 'open', + '1': 'open', + '2': 'closed', + '3': 'open', + '4': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrderSide (side) { + const sides = { + '1': 'buy', // open long + '2': 'sell', // open short + '3': 'sell', // close long + '4': 'buy', // close short + }; + return this.safeString (sides, side, side); + } + + parseOrder (order, market = undefined) { + // + // createOrder + // + // { + // "client_oid":"oktspot79", + // "error_code":"", + // "error_message":"", + // "order_id":"2510789768709120", + // "result":true + // } + // + // cancelOrder + // + // { + // "result": true, + // "client_oid": "oktfuture10", // missing if requested by order_id + // "order_id": "2517535534836736", + // // instrument_id is missing for spot/margin orders + // // available in futures and swap orders only + // "instrument_id": "EOS-USD-190628", + // } + // + // fetchOrder, fetchOrdersByState, fetchOpenOrders, fetchClosedOrders + // + // // spot and margin orders + // + // { + // "client_oid":"oktspot76", + // "created_at":"2019-03-18T07:26:49.000Z", + // "filled_notional":"3.9734", + // "filled_size":"0.001", // filled_qty in futures and swap orders + // "funds":"", // this is most likely the same as notional + // "instrument_id":"BTC-USDT", + // "notional":"", + // "order_id":"2500723297813504", + // "order_type":"0", + // "price":"4013", + // "product_id":"BTC-USDT", // missing in futures and swap orders + // "side":"buy", + // "size":"0.001", + // "status":"filled", + // "state": "2", + // "timestamp":"2019-03-18T07:26:49.000Z", + // "type":"limit" + // } + // + // // futures and swap orders + // + // { + // "instrument_id":"EOS-USD-190628", + // "size":"10", + // "timestamp":"2019-03-20T10:04:55.000Z", + // "filled_qty":"10", // filled_size in spot and margin orders + // "fee":"-0.00841043", + // "order_id":"2512669605501952", + // "price":"3.668", + // "price_avg":"3.567", // missing in spot and margin orders + // "status":"2", + // "state": "2", + // "type":"4", + // "contract_val":"10", + // "leverage":"10", // missing in swap, spot and margin orders + // "client_oid":"", + // "pnl":"1.09510794", // missing in swap, spo and margin orders + // "order_type":"0" + // } + // + const id = this.safeString (order, 'order_id'); + const timestamp = this.parse8601 (this.safeString (order, 'timestamp')); + let side = this.safeString (order, 'side'); + let type = this.safeString (order, 'type'); + if ((side !== 'buy') && (side !== 'sell')) { + side = this.parseOrderSide (type); + } + if ((type !== 'limit') && (type !== 'market')) { + if ('pnl' in order) { + type = 'futures'; + } else { + type = 'swap'; + } + } + let symbol = undefined; + const marketId = this.safeString (order, 'instrument_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + symbol = marketId; + } + if (market !== undefined) { + if (symbol === undefined) { + symbol = market['symbol']; + } + } + let amount = this.safeFloat (order, 'size'); + const filled = this.safeFloat2 (order, 'filled_size', 'filled_qty'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + amount = Math.max (amount, filled); + remaining = Math.max (0, amount - filled); + } + } + if (type === 'market') { + remaining = 0; + } + let cost = this.safeFloat2 (order, 'filled_notional', 'funds'); + const price = this.safeFloat (order, 'price'); + let average = this.safeFloat (order, 'price_avg'); + if (cost === undefined) { + if (filled !== undefined && average !== undefined) { + cost = average * filled; + } + } else { + if ((average === undefined) && (filled !== undefined) && (filled > 0)) { + average = cost / filled; + } + } + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const feeCost = this.safeFloat (order, 'fee'); + let fee = undefined; + if (feeCost !== undefined) { + const feeCurrency = undefined; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const defaultType = this.safeString2 (this.options, 'fetchOrder', 'defaultType', market['type']); + const type = this.safeString (params, 'type', defaultType); + if (type === undefined) { + throw new ArgumentsRequired (this.id + " fetchOrder requires a type parameter (one of 'spot', 'margin', 'futures', 'swap')."); + } + const instrumentId = (market['futures'] || market['swap']) ? 'InstrumentId' : ''; + let method = type + 'GetOrders' + instrumentId; + const request = { + 'instrument_id': market['id'], + // 'client_oid': 'abcdef12345', // optional, [a-z0-9]{1,32} + // 'order_id': id, + }; + const clientOid = this.safeString (params, 'client_oid'); + if (clientOid !== undefined) { + method += 'ClientOid'; + request['client_oid'] = clientOid; + } else { + method += 'OrderId'; + request['order_id'] = id; + } + const query = this.omit (params, 'type'); + const response = await this[method] (this.extend (request, query)); + // + // spot, margin + // + // { + // "client_oid":"oktspot70", + // "created_at":"2019-03-15T02:52:56.000Z", + // "filled_notional":"3.8886", + // "filled_size":"0.001", + // "funds":"", + // "instrument_id":"BTC-USDT", + // "notional":"", + // "order_id":"2482659399697408", + // "order_type":"0", + // "price":"3927.3", + // "product_id":"BTC-USDT", + // "side":"buy", + // "size":"0.001", + // "status":"filled", + // "state": "2", + // "timestamp":"2019-03-15T02:52:56.000Z", + // "type":"limit" + // } + // + // futures, swap + // + // { + // "instrument_id":"EOS-USD-190628", + // "size":"10", + // "timestamp":"2019-03-20T02:46:38.000Z", + // "filled_qty":"10", + // "fee":"-0.0080819", + // "order_id":"2510946213248000", + // "price":"3.712", + // "price_avg":"3.712", + // "status":"2", + // "state": "2", + // "type":"2", + // "contract_val":"10", + // "leverage":"10", + // "client_oid":"", // missing in swap orders + // "pnl":"0", // missing in swap orders + // "order_type":"0" + // } + // + return this.parseOrder (response); + } + + async fetchOrdersByState (state, symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrdersByState requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const type = market['type']; + const request = { + 'instrument_id': market['id'], + // '-2': failed, + // '-1': cancelled, + // '0': open , + // '1': partially filled, + // '2': fully filled, + // '3': submitting, + // '4': cancelling, + // '6': incomplete(open+partially filled), + // '7': complete(cancelled+fully filled), + 'state': state, + }; + let method = type + 'GetOrders'; + if (market['futures'] || market['swap']) { + method += 'InstrumentId'; + } + const query = this.omit (params, 'type'); + const response = await this[method] (this.extend (request, query)); + // + // spot, margin + // + // [ + // // in fact, this documented API response does not correspond + // // to their actual API response for spot markets + // // OKEX v3 API returns a plain array of orders (see below) + // [ + // { + // "client_oid":"oktspot76", + // "created_at":"2019-03-18T07:26:49.000Z", + // "filled_notional":"3.9734", + // "filled_size":"0.001", + // "funds":"", + // "instrument_id":"BTC-USDT", + // "notional":"", + // "order_id":"2500723297813504", + // "order_type":"0", + // "price":"4013", + // "product_id":"BTC-USDT", + // "side":"buy", + // "size":"0.001", + // "status":"filled", + // "state": "2", + // "timestamp":"2019-03-18T07:26:49.000Z", + // "type":"limit" + // }, + // ], + // { + // "before":"2500723297813504", + // "after":"2500650881647616" + // } + // ] + // + // futures, swap + // + // { + // "result":true, // missing in swap orders + // "order_info": [ + // { + // "instrument_id":"EOS-USD-190628", + // "size":"10", + // "timestamp":"2019-03-20T10:04:55.000Z", + // "filled_qty":"10", + // "fee":"-0.00841043", + // "order_id":"2512669605501952", + // "price":"3.668", + // "price_avg":"3.567", + // "status":"2", + // "state": "2", + // "type":"4", + // "contract_val":"10", + // "leverage":"10", // missing in swap orders + // "client_oid":"", + // "pnl":"1.09510794", // missing in swap orders + // "order_type":"0" + // }, + // ] + // } + // + let orders = undefined; + if (market['type'] === 'swap' || market['type'] === 'futures') { + orders = this.safeValue (response, 'order_info', []); + } else { + orders = response; + const responseLength = response.length; + if (responseLength < 1) { + return []; + } + // in fact, this documented API response does not correspond + // to their actual API response for spot markets + // OKEX v3 API returns a plain array of orders + if (responseLength > 1) { + const before = this.safeValue (response[1], 'before'); + if (before !== undefined) { + orders = response[0]; + } + } + } + return this.parseOrders (orders, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // '-2': failed, + // '-1': cancelled, + // '0': open , + // '1': partially filled, + // '2': fully filled, + // '3': submitting, + // '4': cancelling, + // '6': incomplete(open+partially filled), + // '7': complete(cancelled+fully filled), + return await this.fetchOrdersByState ('6', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + // '-2': failed, + // '-1': cancelled, + // '0': open , + // '1': partially filled, + // '2': fully filled, + // '3': submitting, + // '4': cancelling, + // '6': incomplete(open+partially filled), + // '7': complete(cancelled+fully filled), + return await this.fetchOrdersByState ('7', symbol, since, limit, params); + } + + parseDepositAddresses (addresses) { + const result = []; + for (let i = 0; i < addresses.length; i++) { + result.push (this.parseDepositAddress (addresses[i])); + } + return result; + } + + parseDepositAddress (depositAddress, currency = undefined) { + // + // { + // address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa', + // currency: 'eth' + // tag: 'abcde12345', // will be missing if the token does not require a deposit tag + // payment_id: 'abcde12345', // will not be returned if the token does not require a payment_id + // // can_deposit: 1, // 0 or 1, documented but missing + // // can_withdraw: 1, // 0 or 1, documented but missing + // } + // + const address = this.safeString (depositAddress, 'address'); + let tag = this.safeString2 (depositAddress, 'tag', 'payment_id'); + tag = this.safeString (depositAddress, 'memo', tag); + const currencyId = this.safeString (depositAddress, 'currency'); + const code = this.safeCurrencyCode (currencyId); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': depositAddress, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.accountGetDepositAddress (this.extend (request, params)); + // + // [ + // { + // address: '0x696abb81974a8793352cbd33aadcf78eda3cfdfa', + // currency: 'eth' + // } + // ] + // + const addresses = this.parseDepositAddresses (response); + const numAddresses = addresses.length; + if (numAddresses < 1) { + throw new InvalidAddress (this.id + ' fetchDepositAddress cannot return nonexistent addresses, you should create withdrawal addresses with the exchange website first'); + } + return addresses[0]; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (tag) { + address = address + ':' + tag; + } + const fee = this.safeString (params, 'fee'); + if (fee === undefined) { + throw new ExchangeError (this.id + " withdraw() requires a `fee` string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set '0'. Withdrawing to external digital asset address requires network transaction fee."); + } + const request = { + 'currency': currency['id'], + 'to_address': address, + 'destination': '4', // 2 = OKCoin International, 3 = OKEx 4 = others + 'amount': this.numberToString (amount), + 'fee': fee, // String. Network transaction fee ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set as 0. Withdrawal to external digital asset address requires network transaction fee. + }; + if ('password' in params) { + request['trade_pwd'] = params['password']; + } else if ('trade_pwd' in params) { + request['trade_pwd'] = params['trade_pwd']; + } else if (this.password) { + request['trade_pwd'] = this.password; + } + const query = this.omit (params, [ 'fee', 'password', 'trade_pwd' ]); + if (!('trade_pwd' in request)) { + throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a password / trade_pwd parameter'); + } + const response = await this.accountPostWithdrawal (this.extend (request, query)); + // + // { + // "amount":"0.1", + // "withdrawal_id":"67485", + // "currency":"btc", + // "result":true + // } + // + return { + 'info': response, + 'id': this.safeString (response, 'withdrawal_id'), + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'accountGetDepositHistory'; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['code'] = currency['code']; + method += 'Currency'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit, params); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'accountGetWithdrawalHistory'; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['code'] = currency['code']; + method += 'Currency'; + } + const response = await this[method] (this.extend (request, params)); + return this.parseTransactions (response, currency, since, limit, params); + } + + parseTransactionStatus (status) { + // + // deposit statuses + // + // { + // '0': 'waiting for confirmation', + // '1': 'confirmation account', + // '2': 'recharge success' + // } + // + // withdrawal statues + // + // { + // '-3': 'pending cancel', + // '-2': 'cancelled', + // '-1': 'failed', + // '0': 'pending', + // '1': 'sending', + // '2': 'sent', + // '3': 'email confirmation', + // '4': 'manual confirmation', + // '5': 'awaiting identity confirmation' + // } + // + const statuses = { + '-3': 'pending', + '-2': 'pending', + '-1': 'failed', + '0': 'pending', + '1': 'pending', + '2': 'ok', + '3': 'pending', + '4': 'pending', + '5': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // withdraw + // + // { + // "amount":"0.1", + // "withdrawal_id":"67485", + // "currency":"btc", + // "result":true + // } + // + // fetchWithdrawals + // + // { + // amount: "4.72100000", + // withdrawal_id: "1729116", + // fee: "0.01000000eth", + // txid: "0xf653125bbf090bcfe4b5e8e7b8f586a9d87aa7de94598702758c0802b…", + // currency: "ETH", + // from: "7147338839", + // to: "0x26a3CB49578F07000575405a57888681249c35Fd", + // timestamp: "2018-08-17T07:03:42.000Z", + // status: "2" + // } + // + // fetchDeposits + // + // { + // amount: "0.47847546", + // txid: "1723573_3_0_0_WALLET", + // currency: "BTC", + // to: "", + // timestamp: "2018-08-16T03:41:10.000Z", + // status: "2" + // } + // + let type = undefined; + let id = undefined; + let address = undefined; + const withdrawalId = this.safeString (transaction, 'withdrawal_id'); + const addressFrom = this.safeString (transaction, 'from'); + const addressTo = this.safeString (transaction, 'to'); + if (withdrawalId !== undefined) { + type = 'withdrawal'; + id = withdrawalId; + address = addressTo; + } else { + // the payment_id will appear on new deposits but appears to be removed from the response after 2 months + id = this.safeString (transaction, 'payment_id'); + type = 'deposit'; + address = addressTo; + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const txid = this.safeString (transaction, 'txid'); + const timestamp = this.parse8601 (this.safeString (transaction, 'timestamp')); + let feeCost = undefined; + if (type === 'deposit') { + feeCost = 0; + } else { + if (currencyId !== undefined) { + const feeWithCurrencyId = this.safeString (transaction, 'fee'); + if (feeWithCurrencyId !== undefined) { + // https://github.com/ccxt/ccxt/pull/5748 + const lowercaseCurrencyId = currencyId.toLowerCase (); + const feeWithoutCurrencyId = feeWithCurrencyId.replace (lowercaseCurrencyId, ''); + feeCost = parseFloat (feeWithoutCurrencyId); + } + } + } + // todo parse tags + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'addressFrom': addressFrom, + 'addressTo': addressTo, + 'address': address, + 'tagFrom': undefined, + 'tagTo': undefined, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + // okex actually returns ledger entries instead of fills here, so each fill in the order + // is represented by two trades with opposite buy/sell sides, not one :\ + // this aspect renders the 'fills' endpoint unusable for fetchOrderTrades + // until either OKEX fixes the API or we workaround this on our side somehow + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrderTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + if ((limit === undefined) || (limit > 100)) { + limit = 100; + } + const request = { + 'instrument_id': market['id'], + 'order_id': id, + // from: '1', // return the page after the specified page number + // to: '1', // return the page before the specified page number + 'limit': limit, // optional, number of results per request, default = maximum = 100 + }; + const defaultType = this.safeString2 (this.options, 'fetchMyTrades', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const method = type + 'GetFills'; + const response = await this[method] (this.extend (request, query)); + // + // spot trades, margin trades + // + // [ + // { + // "created_at":"2019-09-20T07:15:24.000Z", + // "exec_type":"T", + // "fee":"0", + // "instrument_id":"ETH-USDT", + // "ledger_id":"7173486113", + // "liquidity":"T", + // "order_id":"3553868136523776", + // "price":"217.59", + // "product_id":"ETH-USDT", + // "side":"sell", + // "size":"0.04619899", + // "timestamp":"2019-09-20T07:15:24.000Z" + // } + // ] + // + // futures trades, swap trades + // + // [ + // { + // "trade_id":"197429674631450625", + // "instrument_id":"EOS-USD-SWAP", + // "order_id":"6a-7-54d663a28-0", + // "price":"3.633", + // "order_qty":"1.0000", + // "fee":"-0.000551", + // "created_at":"2019-03-21T04:41:58.0Z", // missing in swap trades + // "timestamp":"2019-03-25T05:56:31.287Z", // missing in futures trades + // "exec_type":"M", // whether the order is taker or maker + // "side":"short", // "buy" in futures trades + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const defaultType = this.safeString2 (this.options, 'fetchLedger', 'defaultType'); + const type = this.safeString (params, 'type', defaultType); + const query = this.omit (params, 'type'); + const suffix = (type === 'account') ? '' : 'Accounts'; + let argument = ''; + const request = { + // 'from': 'id', + // 'to': 'id', + }; + if (limit !== undefined) { + request['limit'] = limit; + } + let currency = undefined; + if ((type === 'spot') || (type === 'futures')) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + " fetchLedger requires a currency code argument for '" + type + "' markets"); + } + argument = 'Currency'; + currency = this.currency (code); + request['currency'] = currency['id']; + } else if ((type === 'margin') || (type === 'swap')) { + if (code === undefined) { + throw new ArgumentsRequired (this.id + " fetchLedger requires a code argument (a market symbol) for '" + type + "' markets"); + } + argument = 'InstrumentId'; + const market = this.market (code); // we intentionally put a market inside here for the margin and swap ledgers + currency = this.currency (market['base']); + request['instrument_id'] = market['id']; + // + // if (type === 'margin') { + // // + // // 3. Borrow + // // 4. Repayment + // // 5. Interest + // // 7. Buy + // // 8. Sell + // // 9. From capital account + // // 10. From C2C + // // 11. From Futures + // // 12. From Spot + // // 13. From ETT + // // 14. To capital account + // // 15. To C2C + // // 16. To Spot + // // 17. To Futures + // // 18. To ETT + // // 19. Mandatory Repayment + // // 20. From Piggybank + // // 21. To Piggybank + // // 22. From Perpetual + // // 23. To Perpetual + // // 24. Liquidation Fee + // // 54. Clawback + // // 59. Airdrop Return. + // // + // request['type'] = 'number'; // All types will be returned if this filed is left blank + // } + // + } else if (type === 'account') { + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + // + // // + // // 1. deposit + // // 2. withdrawal + // // 13. cancel withdrawal + // // 18. into futures account + // // 19. out of futures account + // // 20. into sub account + // // 21. out of sub account + // // 28. claim + // // 29. into ETT account + // // 30. out of ETT account + // // 31. into C2C account + // // 32. out of C2C account + // // 33. into margin account + // // 34. out of margin account + // // 37. into spot account + // // 38. out of spot account + // // + // request['type'] = 'number'; + // + } else { + throw new NotSupported (this.id + " fetchLedger does not support the '" + type + "' type (the type must be one of 'account', 'spot', 'margin', 'futures', 'swap')"); + } + const method = type + 'Get' + suffix + argument + 'Ledger'; + const response = await this[method] (this.extend (request, query)); + // + // transfer funds transfer in/out + // trade funds moved as a result of a trade, spot and margin accounts only + // rebate fee rebate as per fee schedule, spot and margin accounts only + // match open long/open short/close long/close short (futures) or a change in the amount because of trades (swap) + // fee fee, futures only + // settlement settlement/clawback/settle long/settle short + // liquidation force close long/force close short/deliver close long/deliver close short + // funding funding fee, swap only + // margin a change in the amount after adjusting margin, swap only + // + // account + // + // [ + // { + // "amount":0.00051843, + // "balance":0.00100941, + // "currency":"BTC", + // "fee":0, + // "ledger_id":8987285, + // "timestamp":"2018-10-12T11:01:14.000Z", + // "typename":"Get from activity" + // } + // ] + // + // spot + // + // [ + // { + // "timestamp":"2019-03-18T07:08:25.000Z", + // "ledger_id":"3995334780", + // "created_at":"2019-03-18T07:08:25.000Z", + // "currency":"BTC", + // "amount":"0.0009985", + // "balance":"0.0029955", + // "type":"trade", + // "details":{ + // "instrument_id":"BTC-USDT", + // "order_id":"2500650881647616", + // "product_id":"BTC-USDT" + // } + // } + // ] + // + // margin + // + // [ + // [ + // { + // "created_at":"2019-03-20T03:45:05.000Z", + // "ledger_id":"78918186", + // "timestamp":"2019-03-20T03:45:05.000Z", + // "currency":"EOS", + // "amount":"0", // ? + // "balance":"0.59957711", + // "type":"transfer", + // "details":{ + // "instrument_id":"EOS-USDT", + // "order_id":"787057", + // "product_id":"EOS-USDT" + // } + // } + // ], + // { + // "before":"78965766", + // "after":"78918186" + // } + // ] + // + // futures + // + // [ + // { + // "ledger_id":"2508090544914461", + // "timestamp":"2019-03-19T14:40:24.000Z", + // "amount":"-0.00529521", + // "balance":"0", + // "currency":"EOS", + // "type":"fee", + // "details":{ + // "order_id":"2506982456445952", + // "instrument_id":"EOS-USD-190628" + // } + // } + // ] + // + // swap + // + // [ + // { + // "amount":"0.004742", + // "fee":"-0.000551", + // "type":"match", + // "instrument_id":"EOS-USD-SWAP", + // "ledger_id":"197429674941902848", + // "timestamp":"2019-03-25T05:56:31.286Z" + // }, + // ] + // + const isArray = Array.isArray (response[0]); + const isMargin = (type === 'margin'); + const entries = (isMargin && isArray) ? response[0] : response; + return this.parseLedger (entries, currency, since, limit); + } + + parseLedgerEntryType (type) { + const types = { + 'transfer': 'transfer', // // funds transfer in/out + 'trade': 'trade', // funds moved as a result of a trade, spot and margin accounts only + 'rebate': 'rebate', // fee rebate as per fee schedule, spot and margin accounts only + 'match': 'trade', // open long/open short/close long/close short (futures) or a change in the amount because of trades (swap) + 'fee': 'fee', // fee, futures only + 'settlement': 'trade', // settlement/clawback/settle long/settle short + 'liquidation': 'trade', // force close long/force close short/deliver close long/deliver close short + 'funding': 'fee', // funding fee, swap only + 'margin': 'margin', // a change in the amount after adjusting margin, swap only + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // + // account + // + // { + // "amount":0.00051843, + // "balance":0.00100941, + // "currency":"BTC", + // "fee":0, + // "ledger_id":8987285, + // "timestamp":"2018-10-12T11:01:14.000Z", + // "typename":"Get from activity" + // } + // + // spot + // + // { + // "timestamp":"2019-03-18T07:08:25.000Z", + // "ledger_id":"3995334780", + // "created_at":"2019-03-18T07:08:25.000Z", + // "currency":"BTC", + // "amount":"0.0009985", + // "balance":"0.0029955", + // "type":"trade", + // "details":{ + // "instrument_id":"BTC-USDT", + // "order_id":"2500650881647616", + // "product_id":"BTC-USDT" + // } + // } + // + // margin + // + // { + // "created_at":"2019-03-20T03:45:05.000Z", + // "ledger_id":"78918186", + // "timestamp":"2019-03-20T03:45:05.000Z", + // "currency":"EOS", + // "amount":"0", // ? + // "balance":"0.59957711", + // "type":"transfer", + // "details":{ + // "instrument_id":"EOS-USDT", + // "order_id":"787057", + // "product_id":"EOS-USDT" + // } + // } + // + // futures + // + // { + // "ledger_id":"2508090544914461", + // "timestamp":"2019-03-19T14:40:24.000Z", + // "amount":"-0.00529521", + // "balance":"0", + // "currency":"EOS", + // "type":"fee", + // "details":{ + // "order_id":"2506982456445952", + // "instrument_id":"EOS-USD-190628" + // } + // } + // + // swap + // + // { + // "amount":"0.004742", + // "fee":"-0.000551", + // "type":"match", + // "instrument_id":"EOS-USD-SWAP", + // "ledger_id":"197429674941902848", + // "timestamp":"2019-03-25T05:56:31.286Z" + // }, + // + const id = this.safeString (item, 'ledger_id'); + const account = undefined; + const details = this.safeValue (item, 'details', {}); + const referenceId = this.safeString (details, 'order_id'); + const referenceAccount = undefined; + const type = this.parseLedgerEntryType (this.safeString (item, 'type')); + const code = this.safeCurrencyCode (this.safeString (item, 'currency'), currency); + const amount = this.safeFloat (item, 'amount'); + const timestamp = this.parse8601 (this.safeString (item, 'timestamp')); + const fee = { + 'cost': this.safeFloat (item, 'fee'), + 'currency': code, + }; + const before = undefined; + const after = this.safeFloat (item, 'balance'); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'account': account, + 'referenceId': referenceId, + 'referenceAccount': referenceAccount, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': before, // balance before + 'after': after, // balance after + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': fee, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const isArray = Array.isArray (params); + let request = '/api/' + api + '/' + this.version + '/'; + request += isArray ? path : this.implodeParams (path, params); + const query = isArray ? params : this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + const type = this.getPathAuthenticationType (path); + if (type === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else if (type === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.iso8601 (this.milliseconds ()); + headers = { + 'OK-ACCESS-KEY': this.apiKey, + 'OK-ACCESS-PASSPHRASE': this.password, + 'OK-ACCESS-TIMESTAMP': timestamp, + // 'OK-FROM': '', + // 'OK-TO': '', + // 'OK-LIMIT': '', + }; + let auth = timestamp + method + request; + if (method === 'GET') { + if (Object.keys (query).length) { + const urlencodedQuery = '?' + this.urlencode (query); + url += urlencodedQuery; + auth += urlencodedQuery; + } + } else { + if (isArray || Object.keys (query).length) { + body = this.json (query); + auth += body; + } + headers['Content-Type'] = 'application/json'; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); + headers['OK-ACCESS-SIGN'] = this.decode (signature); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + getPathAuthenticationType (path) { + const auth = this.safeValue (this.options, 'auth', {}); + const key = this.findBroadlyMatchedKey (auth, path); + return this.safeString (auth, key, 'private'); + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + const feedback = this.id + ' ' + body; + if (code === 503) { + throw new ExchangeError (feedback); + } + if (!response) { + return; // fallback to default error handler + } + const message = this.safeString (response, 'message'); + const errorCode = this.safeString2 (response, 'code', 'error_code'); + if (message !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback); + if (message !== undefined) { + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/paymium.js b/TaskServer/node_modules/ccxt/js/paymium.js new file mode 100644 index 000000000..16e50e717 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/paymium.js @@ -0,0 +1,246 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class paymium extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'paymium', + 'name': 'Paymium', + 'countries': [ 'FR', 'EU' ], + 'rateLimit': 2000, + 'version': 'v1', + 'has': { + 'CORS': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27790564-a945a9d4-5ff9-11e7-9d2d-b635763f2f24.jpg', + 'api': 'https://paymium.com/api', + 'www': 'https://www.paymium.com', + 'fees': 'https://www.paymium.com/page/help/fees', + 'doc': [ + 'https://github.com/Paymium/api-documentation', + 'https://www.paymium.com/page/developers', + ], + }, + 'api': { + 'public': { + 'get': [ + 'countries', + 'data/{id}/ticker', + 'data/{id}/trades', + 'data/{id}/depth', + 'bitcoin_charts/{id}/trades', + 'bitcoin_charts/{id}/depth', + ], + }, + 'private': { + 'get': [ + 'merchant/get_payment/{UUID}', + 'user', + 'user/addresses', + 'user/addresses/{btc_address}', + 'user/orders', + 'user/orders/{UUID}', + 'user/price_alerts', + ], + 'post': [ + 'user/orders', + 'user/addresses', + 'user/payment_requests', + 'user/price_alerts', + 'merchant/create_payment', + ], + 'delete': [ + 'user/orders/{UUID}/cancel', + 'user/price_alerts/{id}', + ], + }, + }, + 'markets': { + 'BTC/EUR': { 'id': 'eur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur' }, + }, + 'fees': { + 'trading': { + 'maker': 0.002, + 'taker': 0.002, + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetUser (params); + const result = { 'info': response }; + const currencies = Object.keys (this.currencies); + for (let i = 0; i < currencies.length; i++) { + const code = currencies[i]; + const currencyId = this.currencyId (code); + const free = 'balance_' + currencyId; + if (free in response) { + const account = this.account (); + const used = 'locked_' + currencyId; + account['free'] = this.safeFloat (response, free); + account['used'] = this.safeFloat (response, used); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const response = await this.publicGetDataIdDepth (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount'); + } + + async fetchTicker (symbol, params = {}) { + const request = { + 'id': this.marketId (symbol), + }; + const ticker = await this.publicGetDataIdTicker (this.extend (request, params)); + const timestamp = this.safeTimestamp (ticker, 'at'); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'price'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'variation'), + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'created_at_int'); + const id = this.safeString (trade, 'uuid'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const side = this.safeString (trade, 'side'); + const price = this.safeFloat (trade, 'price'); + const amountField = 'traded_' + market['base'].toLowerCase (); + const amount = this.safeFloat (trade, amountField); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + return { + 'info': trade, + 'id': id, + 'order': undefined, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + const response = await this.publicGetDataIdTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'type': this.capitalize (type) + 'Order', + 'currency': this.marketId (symbol), + 'direction': side, + 'amount': amount, + }; + if (type !== 'market') { + request['price'] = price; + } + const response = await this.privatePostUserOrders (this.extend (request, params)); + return { + 'info': response, + 'id': response['uuid'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'UUID': id, + }; + return await this.privateDeleteUserOrdersUUIDCancel (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + let auth = nonce + url; + if (method === 'POST') { + if (Object.keys (query).length) { + body = this.json (query); + auth += body; + } + } + headers = { + 'Api-Key': this.apiKey, + 'Api-Signature': this.hmac (this.encode (auth), this.encode (this.secret)), + 'Api-Nonce': nonce, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const response = await this.fetch2 (path, api, method, params, headers, body); + if ('errors' in response) { + throw new ExchangeError (this.id + ' ' + this.json (response)); + } + return response; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/poloniex.js b/TaskServer/node_modules/ccxt/js/poloniex.js new file mode 100644 index 000000000..2e4588459 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/poloniex.js @@ -0,0 +1,1451 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, RequestTimeout, AuthenticationError, PermissionDenied, DDoSProtection, InsufficientFunds, OrderNotFound, OrderNotCached, InvalidOrder, AccountSuspended, CancelPending, InvalidNonce } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class poloniex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'poloniex', + 'name': 'Poloniex', + 'countries': [ 'US' ], + 'rateLimit': 1000, // up to 6 calls per second + 'certified': true, // 2019-06-07 + 'has': { + 'CORS': false, + 'createDepositAddress': true, + 'createMarketOrder': false, + 'editOrder': true, + 'fetchClosedOrders': 'emulated', + 'fetchCurrencies': true, + 'fetchDepositAddress': true, + 'fetchDeposits': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrder': true, // true endpoint for a single open order + 'fetchOpenOrders': true, // true endpoint for open orders + 'fetchOrder': 'emulated', // no endpoint for a single open-or-closed order (just for an open order only) + 'fetchOrderBooks': true, + 'fetchOrders': 'emulated', // no endpoint for open-or-closed orders (just for open orders only) + 'fetchOrderStatus': 'emulated', // no endpoint for status of a single open-or-closed order (just for open orders only) + 'fetchOrderTrades': true, // true endpoint for trades of a single open or closed order + 'fetchTickers': true, + 'fetchTradingFee': true, + 'fetchTradingFees': true, + 'fetchTransactions': true, + 'fetchWithdrawals': true, + 'cancelAllOrders': true, + 'withdraw': true, + }, + 'timeframes': { + '5m': 300, + '15m': 900, + '30m': 1800, + '2h': 7200, + '4h': 14400, + '1d': 86400, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg', + 'api': { + 'public': 'https://poloniex.com/public', + 'private': 'https://poloniex.com/tradingApi', + }, + 'www': 'https://www.poloniex.com', + 'doc': 'https://docs.poloniex.com', + 'fees': 'https://poloniex.com/fees', + 'referral': 'https://www.poloniex.com/?utm_source=ccxt&utm_medium=web', + }, + 'api': { + 'public': { + 'get': [ + 'return24hVolume', + 'returnChartData', + 'returnCurrencies', + 'returnLoanOrders', + 'returnOrderBook', + 'returnTicker', + 'returnTradeHistory', + ], + }, + 'private': { + 'post': [ + 'buy', + 'cancelLoanOffer', + 'cancelOrder', + 'cancelAllOrders', + 'closeMarginPosition', + 'createLoanOffer', + 'generateNewAddress', + 'getMarginPosition', + 'marginBuy', + 'marginSell', + 'moveOrder', + 'returnActiveLoans', + 'returnAvailableAccountBalances', + 'returnBalances', + 'returnCompleteBalances', + 'returnDepositAddresses', + 'returnDepositsWithdrawals', + 'returnFeeInfo', + 'returnLendingHistory', + 'returnMarginAccountSummary', + 'returnOpenLoanOffers', + 'returnOpenOrders', + 'returnOrderTrades', + 'returnOrderStatus', + 'returnTradableBalances', + 'returnTradeHistory', + 'sell', + 'toggleAutoRenew', + 'transferBalance', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + // starting from Jan 8 2020 + 'maker': 0.0009, + 'taker': 0.0009, + }, + 'funding': {}, + }, + 'limits': { + 'amount': { + 'min': 0.000001, + 'max': 1000000000, + }, + 'price': { + 'min': 0.00000001, + 'max': 1000000000, + }, + 'cost': { + 'min': 0.00000000, + 'max': 1000000000, + }, + }, + 'precision': { + 'amount': 8, + 'price': 8, + }, + 'commonCurrencies': { + 'AIR': 'AirCoin', + 'APH': 'AphroditeCoin', + 'BCC': 'BTCtalkcoin', + 'BDG': 'Badgercoin', + 'BTM': 'Bitmark', + 'CON': 'Coino', + 'GOLD': 'GoldEagles', + 'GPUC': 'GPU', + 'HOT': 'Hotcoin', + 'ITC': 'Information Coin', + 'PLX': 'ParallaxCoin', + 'KEY': 'KEYCoin', + 'STR': 'XLM', + 'SOC': 'SOCC', + 'XAP': 'API Coin', + }, + 'options': { + 'limits': { + 'cost': { + 'min': { + 'BTC': 0.0001, + 'ETH': 0.0001, + 'XMR': 0.0001, + 'USDT': 1.0, + }, + }, + }, + }, + 'exceptions': { + 'exact': { + 'You may only place orders that reduce your position.': InvalidOrder, + 'Invalid order number, or you are not the person who placed the order.': OrderNotFound, + 'Permission denied': PermissionDenied, + 'Connection timed out. Please try again.': RequestTimeout, + 'Internal error. Please try again.': ExchangeNotAvailable, + 'Order not found, or you are not the person who placed it.': OrderNotFound, + 'Invalid API key/secret pair.': AuthenticationError, + 'Please do not make more than 8 API calls per second.': DDoSProtection, + 'Rate must be greater than zero.': InvalidOrder, // {"error":"Rate must be greater than zero."} + }, + 'broad': { + 'Total must be at least': InvalidOrder, // {"error":"Total must be at least 0.0001."} + 'This account is frozen.': AccountSuspended, + 'Not enough': InsufficientFunds, + 'Nonce must be greater': InvalidNonce, + 'You have already called cancelOrder or moveOrder on this order.': CancelPending, + 'Amount must be at least': InvalidOrder, // {"error":"Amount must be at least 0.000001."} + 'is either completed or does not exist': InvalidOrder, // {"error":"Order 587957810791 is either completed or does not exist."} + }, + }, + }); + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': parseFloat (this.feeToPrecision (symbol, cost)), + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + this.safeTimestamp (ohlcv, 'date'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'quoteVolume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + 'period': this.timeframes[timeframe], + }; + if (since === undefined) { + request['end'] = this.seconds (); + if (limit === undefined) { + request['start'] = request['end'] - this.parseTimeframe ('1w'); // max range = 1 week + } else { + request['start'] = request['end'] - this.sum (limit) * this.parseTimeframe (timeframe); + } + } else { + request['start'] = parseInt (since / 1000); + if (limit !== undefined) { + const end = this.sum (request['start'], limit * this.parseTimeframe (timeframe)); + request['end'] = end; + } + } + const response = await this.publicGetReturnChartData (this.extend (request, params)); + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetReturnTicker (params); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const limits = this.extend (this.limits, { + 'cost': { + 'min': this.safeValue (this.options['limits']['cost']['min'], quote), + }, + }); + const isFrozen = this.safeString (market, 'isFrozen'); + const active = (isFrozen !== '1'); + const numericId = this.safeInteger (market, 'id'); + result.push ({ + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': active, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const request = { + 'account': 'all', + }; + const response = await this.privatePostReturnCompleteBalances (this.extend (request, params)); + const result = { 'info': response }; + const currencyIds = Object.keys (response); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const balance = this.safeValue (response, currencyId, {}); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'onOrders'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchTradingFees (params = {}) { + await this.loadMarkets (); + const fees = await this.privatePostReturnFeeInfo (params); + return { + 'info': fees, + 'maker': this.safeFloat (fees, 'makerFee'), + 'taker': this.safeFloat (fees, 'takerFee'), + 'withdraw': {}, + 'deposit': {}, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': this.marketId (symbol), + }; + if (limit !== undefined) { + request['depth'] = limit; // 100 + } + const response = await this.publicGetReturnOrderBook (this.extend (request, params)); + const orderbook = this.parseOrderBook (response); + orderbook['nonce'] = this.safeInteger (response, 'seq'); + return orderbook; + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'currencyPair': 'all', + }; + if (limit !== undefined) { + request['depth'] = limit; // 100 + } + const response = await this.publicGetReturnOrderBook (this.extend (request, params)); + const marketIds = Object.keys (response); + const result = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let symbol = undefined; + if (marketId in this.markets_by_id) { + symbol = this.markets_by_id[marketId]['symbol']; + } else { + const [ quoteId, baseId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const orderbook = this.parseOrderBook (response[marketId]); + orderbook['nonce'] = this.safeInteger (response[marketId], 'seq'); + result[symbol] = orderbook; + } + return result; + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + let open = undefined; + let change = undefined; + let average = undefined; + const last = this.safeFloat (ticker, 'last'); + const relativeChange = this.safeFloat (ticker, 'percentChange'); + if (relativeChange !== -1) { + open = last / this.sum (1, relativeChange); + change = last - open; + average = this.sum (last, open) / 2; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high24hr'), + 'low': this.safeFloat (ticker, 'low24hr'), + 'bid': this.safeFloat (ticker, 'highestBid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'lowestAsk'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': relativeChange * 100, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'quoteVolume'), + 'quoteVolume': this.safeFloat (ticker, 'baseVolume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetReturnTicker (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = undefined; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + market = { 'symbol': symbol }; + } + const ticker = response[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetReturnCurrencies (params); + const ids = Object.keys (response); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = response[id]; + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const precision = 8; // default precision, todo: fix "magic constants" + const code = this.safeCurrencyCode (id); + const active = (currency['delisted'] === 0) && !currency['disabled']; + const numericId = this.safeInteger (currency, 'id'); + result[code] = { + 'id': id, + 'numericId': numericId, + 'code': code, + 'info': currency, + 'name': currency['name'], + 'active': active, + 'fee': this.safeFloat (currency, 'txFee'), // todo: redesign + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': currency['txFee'], + 'max': Math.pow (10, precision), + }, + }, + }; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetReturnTicker (params); + const ticker = response[market['id']]; + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchMyTrades (symbol defined, specific market) + // + // { + // globalTradeID: 394698946, + // tradeID: 45210255, + // date: '2018-10-23 17:28:55', + // type: 'sell', + // rate: '0.03114126', + // amount: '0.00018753', + // total: '0.00000583' + // } + // + // fetchMyTrades (symbol undefined, all markets) + // + // { + // globalTradeID: 394131412, + // tradeID: '5455033', + // date: '2018-10-16 18:05:17', + // rate: '0.06935244', + // amount: '1.40308443', + // total: '0.09730732', + // fee: '0.00100000', + // orderNumber: '104768235081', + // type: 'sell', + // category: 'exchange' + // } + // + // createOrder (taker trades) + // + // { + // 'amount': '200.00000000', + // 'date': '2019-12-15 16:04:10', + // 'rate': '0.00000355', + // 'total': '0.00071000', + // 'tradeID': '119871', + // 'type': 'buy', + // 'takerAdjustment': '200.00000000' + // } + // + const id = this.safeString2 (trade, 'globalTradeID', 'tradeID'); + const orderId = this.safeString (trade, 'orderNumber'); + const timestamp = this.parse8601 (this.safeString (trade, 'date')); + let symbol = undefined; + let base = undefined; + let quote = undefined; + if ((!market) && ('currencyPair' in trade)) { + const currencyPair = trade['currencyPair']; + if (currencyPair in this.markets_by_id) { + market = this.markets_by_id[currencyPair]; + } else { + const parts = currencyPair.split ('_'); + quote = parts[0]; + base = parts[1]; + symbol = base + '/' + quote; + } + } + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + quote = market['quote']; + } + const side = this.safeString (trade, 'type'); + let fee = undefined; + const price = this.safeFloat (trade, 'rate'); + const cost = this.safeFloat (trade, 'total'); + const amount = this.safeFloat (trade, 'amount'); + if ('fee' in trade) { + const rate = this.safeFloat (trade, 'fee'); + let feeCost = undefined; + let currency = undefined; + if (side === 'buy') { + currency = base; + feeCost = amount * rate; + } else { + currency = quote; + if (cost !== undefined) { + feeCost = cost * rate; + } + } + fee = { + 'type': undefined, + 'rate': rate, + 'cost': feeCost, + 'currency': currency, + }; + } + let takerOrMaker = undefined; + const takerAdjustment = this.safeFloat (trade, 'takerAdjustment'); + if (takerAdjustment !== undefined) { + takerOrMaker = 'taker'; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPair': market['id'], + }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + request['end'] = this.seconds (); // last 50000 trades by default + } + const trades = await this.publicGetReturnTradeHistory (this.extend (request, params)); + return this.parseTrades (trades, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const pair = market ? market['id'] : 'all'; + const request = { 'currencyPair': pair }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + request['end'] = this.sum (this.seconds (), 1); // adding 1 is a fix for #3411 + } + // limit is disabled (does not really work as expected) + if (limit !== undefined) { + request['limit'] = parseInt (limit); + } + const response = await this.privatePostReturnTradeHistory (this.extend (request, params)); + // + // specific market (symbol defined) + // + // [ + // { + // globalTradeID: 394700861, + // tradeID: 45210354, + // date: '2018-10-23 18:01:58', + // type: 'buy', + // rate: '0.03117266', + // amount: '0.00000652', + // total: '0.00000020' + // }, + // { + // globalTradeID: 394698946, + // tradeID: 45210255, + // date: '2018-10-23 17:28:55', + // type: 'sell', + // rate: '0.03114126', + // amount: '0.00018753', + // total: '0.00000583' + // } + // ] + // + // all markets (symbol undefined) + // + // { + // BTC_BCH: [{ + // globalTradeID: 394131412, + // tradeID: '5455033', + // date: '2018-10-16 18:05:17', + // rate: '0.06935244', + // amount: '1.40308443', + // total: '0.09730732', + // fee: '0.00100000', + // orderNumber: '104768235081', + // type: 'sell', + // category: 'exchange' + // }, { + // globalTradeID: 394126818, + // tradeID: '5455007', + // date: '2018-10-16 16:55:34', + // rate: '0.06935244', + // amount: '0.00155709', + // total: '0.00010798', + // fee: '0.00200000', + // orderNumber: '104768179137', + // type: 'sell', + // category: 'exchange' + // }], + // } + // + let result = []; + if (market !== undefined) { + result = this.parseTrades (response, market); + } else { + if (response) { + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + const trades = this.parseTrades (response[id], market); + for (let j = 0; j < trades.length; j++) { + result.push (trades[j]); + } + } else { + const [ quoteId, baseId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const trades = response[id]; + for (let j = 0; j < trades.length; j++) { + const market = { + 'symbol': symbol, + 'base': base, + 'quote': quote, + }; + result.push (this.parseTrade (trades[j], market)); + } + } + } + } + } + return this.filterBySinceLimit (result, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'Open': 'open', + 'Partially filled': 'open', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOpenOrder + // + // { + // status: 'Open', + // rate: '0.40000000', + // amount: '1.00000000', + // currencyPair: 'BTC_ETH', + // date: '2018-10-17 17:04:50', + // total: '0.40000000', + // type: 'buy', + // startingAmount: '1.00000', + // } + // + // fetchOpenOrders + // + // { + // orderNumber: '514514894224', + // type: 'buy', + // rate: '0.00001000', + // startingAmount: '100.00000000', + // amount: '100.00000000', + // total: '0.00100000', + // date: '2018-10-23 17:38:53', + // margin: 0, + // } + // + // createOrder + // + // { + // 'orderNumber': '9805453960', + // 'resultingTrades': [ + // { + // 'amount': '200.00000000', + // 'date': '2019-12-15 16:04:10', + // 'rate': '0.00000355', + // 'total': '0.00071000', + // 'tradeID': '119871', + // 'type': 'buy', + // 'takerAdjustment': '200.00000000', + // }, + // ], + // 'fee': '0.00000000', + // 'currencyPair': 'BTC_MANA', + // // --------------------------------------------------------- + // // the following fields are injected by createOrder + // 'timestamp': timestamp, + // 'status': 'open', + // 'type': type, + // 'side': side, + // 'price': price, + // 'amount': amount, + // } + // + let timestamp = this.safeInteger (order, 'timestamp'); + if (!timestamp) { + timestamp = this.parse8601 (order['date']); + } + let trades = undefined; + if ('resultingTrades' in order) { + trades = this.parseTrades (order['resultingTrades'], market); + } + let symbol = undefined; + const marketId = this.safeString (order, 'currencyPair'); + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat2 (order, 'price', 'rate'); + let remaining = this.safeFloat (order, 'amount'); + let amount = this.safeFloat (order, 'startingAmount'); + let filled = undefined; + let cost = 0; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + if (price !== undefined) { + cost = filled * price; + } + } + } else { + amount = remaining; + } + let status = this.parseOrderStatus (this.safeString (order, 'status')); + let average = undefined; + let lastTradeTimestamp = undefined; + if (filled === undefined) { + if (trades !== undefined) { + filled = 0; + cost = 0; + const tradesLength = trades.length; + if (tradesLength > 0) { + lastTradeTimestamp = trades[0]['timestamp']; + for (let i = 0; i < tradesLength; i++) { + const trade = trades[i]; + const tradeAmount = trade['amount']; + const tradePrice = trade['price']; + filled = this.sum (filled, tradeAmount); + cost = this.sum (cost, tradePrice * tradeAmount); + lastTradeTimestamp = Math.max (lastTradeTimestamp, trade['timestamp']); + } + } + remaining = Math.max (amount - filled, 0); + if (filled >= amount) { + status = 'closed'; + } + } + } + if ((filled !== undefined) && (cost !== undefined) && (filled > 0)) { + average = cost / filled; + } + let type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side', type); + if (type === side) { + type = undefined; + } + const id = this.safeString (order, 'orderNumber'); + let fee = undefined; + const feeCost = this.safeFloat (order, 'fee'); + if (feeCost !== undefined) { + let feeCurrencyCode = undefined; + if (market !== undefined) { + feeCurrencyCode = (side === 'buy') ? market['base'] : market['quote']; + } + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'trades': trades, + 'fee': fee, + }; + } + + parseOpenOrders (orders, market, result) { + for (let i = 0; i < orders.length; i++) { + const order = orders[i]; + const extended = this.extend (order, { + 'status': 'open', + 'type': 'limit', + 'side': order['type'], + 'price': order['rate'], + }); + result.push (this.parseOrder (extended, market)); + } + return result; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const pair = market ? market['id'] : 'all'; + const request = { + 'currencyPair': pair, + }; + const response = await this.privatePostReturnOpenOrders (this.extend (request, params)); + let openOrders = []; + if (market !== undefined) { + openOrders = this.parseOpenOrders (response, market, openOrders); + } else { + const marketIds = Object.keys (response); + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const orders = response[marketId]; + const m = this.markets_by_id[marketId]; + openOrders = this.parseOpenOrders (orders, m, openOrders); + } + } + for (let j = 0; j < openOrders.length; j++) { + this.orders[openOrders[j]['id']] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + const result = []; + for (let k = 0; k < cachedOrderIds.length; k++) { + const id = cachedOrderIds[k]; + if (id in openOrdersIndexedById) { + this.orders[id] = this.extend (this.orders[id], openOrdersIndexedById[id]); + } else { + let order = this.orders[id]; + if (order['status'] === 'open') { + order = this.extend (order, { + 'status': 'closed', + 'cost': undefined, + 'filled': order['amount'], + 'remaining': 0.0, + }); + if (order['cost'] === undefined) { + if (order['filled'] !== undefined) { + order['cost'] = order['filled'] * order['price']; + } + } + this.orders[id] = order; + } + } + const order = this.orders[id]; + if (market !== undefined) { + if (order['symbol'] === symbol) { + result.push (order); + } + } else { + result.push (order); + } + } + return this.filterBySinceLimit (result, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const since = this.safeValue (params, 'since'); + const limit = this.safeValue (params, 'limit'); + const request = this.omit (params, [ 'since', 'limit' ]); + const orders = await this.fetchOrders (symbol, since, limit, request); + for (let i = 0; i < orders.length; i++) { + if (orders[i]['id'] === id) { + return orders[i]; + } + } + throw new OrderNotCached (this.id + ' order id ' + id.toString () + ' is not in "open" state and not found in cache'); + } + + filterOrdersByStatus (orders, status) { + const result = []; + for (let i = 0; i < orders.length; i++) { + if (orders[i]['status'] === status) { + result.push (orders[i]); + } + } + return result; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterOrdersByStatus (orders, 'open'); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterOrdersByStatus (orders, 'closed'); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const method = 'privatePost' + this.capitalize (side); + const market = this.market (symbol); + amount = this.amountToPrecision (symbol, amount); + const request = { + 'currencyPair': market['id'], + 'rate': this.priceToPrecision (symbol, price), + 'amount': amount, + }; + // remember the timestamp before issuing the request + const timestamp = this.milliseconds (); + const response = await this[method] (this.extend (request, params)); + // + // { + // 'orderNumber': '9805453960', + // 'resultingTrades': [ + // { + // 'amount': '200.00000000', + // 'date': '2019-12-15 16:04:10', + // 'rate': '0.00000355', + // 'total': '0.00071000', + // 'tradeID': '119871', + // 'type': 'buy', + // 'takerAdjustment': '200.00000000', + // }, + // ], + // 'fee': '0.00000000', + // 'currencyPair': 'BTC_MANA', + // } + // + const order = this.parseOrder (this.extend ({ + 'timestamp': timestamp, + 'status': 'open', + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + }, response), market); + const id = order['id']; + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async editOrder (id, symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + price = parseFloat (price); + const request = { + 'orderNumber': id, + 'rate': this.priceToPrecision (symbol, price), + }; + if (amount !== undefined) { + request['amount'] = this.amountToPrecision (symbol, amount); + } + const response = await this.privatePostMoveOrder (this.extend (request, params)); + let result = undefined; + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + const newid = response['orderNumber']; + this.orders[newid] = this.extend (this.orders[id], { + 'id': newid, + 'price': price, + 'status': 'open', + }); + if (amount !== undefined) { + this.orders[newid]['amount'] = amount; + } + result = this.extend (this.orders[newid], { 'info': response }); + } else { + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + result = this.parseOrder (response, market); + this.orders[result['id']] = result; + } + return result; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + let response = undefined; + try { + response = await this.privatePostCancelOrder (this.extend ({ + 'orderNumber': id, + }, params)); + } catch (e) { + if (e instanceof CancelPending) { + // A request to cancel the order has been sent already. + // If we then attempt to cancel the order the second time + // before the first request is processed the exchange will + // throw a CancelPending exception. Poloniex won't show the + // order in the list of active (open) orders and the cached + // order will be marked as 'closed' (see #1801 for details). + // To avoid that we proactively mark the order as 'canceled' + // here. If for some reason the order does not get canceled + // and still appears in the active list then the order cache + // will eventually get back in sync on a call to `fetchOrder`. + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + } + throw e; + } + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + async cancelAllOrders (symbol = undefined, params = {}) { + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['currencyPair'] = market['id']; + } + const response = await this.privatePostCancelAllOrders (this.extend (request, params)); + // + // { + // "success": 1, + // "message": "Orders canceled", + // "orderNumbers": [ + // 503749, + // 888321, + // 7315825, + // 7316824 + // ] + // } + // + const orderIds = this.safeValue (response, 'orderNumbers', []); + for (let i = 0; i < orderIds.length; i++) { + const id = orderIds[i].toString (); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + } + return response; + } + + async fetchOpenOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + id = id.toString (); + const response = await this.privatePostReturnOrderStatus (this.extend ({ + 'orderNumber': id, + }, params)); + // + // { + // success: 1, + // result: { + // '6071071': { + // status: 'Open', + // rate: '0.40000000', + // amount: '1.00000000', + // currencyPair: 'BTC_ETH', + // date: '2018-10-17 17:04:50', + // total: '0.40000000', + // type: 'buy', + // startingAmount: '1.00000', + // }, + // }, + // } + // + const result = this.safeValue (response['result'], id); + if (result === undefined) { + throw new OrderNotFound (this.id + ' order id ' + id + ' not found'); + } + const order = this.parseOrder (result); + order['id'] = id; + this.orders[id] = order; + return order; + } + + async fetchOrderStatus (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const orders = await this.fetchOpenOrders (symbol, undefined, undefined, params); + const indexed = this.indexBy (orders, 'id'); + return (id in indexed) ? 'open' : 'closed'; + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderNumber': id, + }; + const trades = await this.privatePostReturnOrderTrades (this.extend (request, params)); + return this.parseTrades (trades); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostGenerateNewAddress (this.extend (request, params)); + let address = undefined; + let tag = undefined; + if (response['success'] === 1) { + address = this.safeString (response, 'response'); + } + this.checkAddress (address); + const depositAddress = this.safeString (currency['info'], 'depositAddress'); + if (depositAddress !== undefined) { + tag = address; + address = depositAddress; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const response = await this.privatePostReturnDepositAddresses (params); + const currencyId = currency['id']; + let address = this.safeString (response, currencyId); + let tag = undefined; + this.checkAddress (address); + const depositAddress = this.safeString (currency['info'], 'depositAddress'); + if (depositAddress !== undefined) { + tag = address; + address = depositAddress; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + }; + if (tag) { + request['paymentId'] = tag; + } + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': this.safeString (response, 'withdrawalNumber'), + }; + } + + async fetchTransactionsHelper (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const year = 31104000; // 60 * 60 * 24 * 30 * 12 = one year of history, why not + const now = this.seconds (); + const start = (since !== undefined) ? parseInt (since / 1000) : now - 10 * year; + const request = { + 'start': start, // UNIX timestamp, required + 'end': now, // UNIX timestamp, required + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privatePostReturnDepositsWithdrawals (this.extend (request, params)); + // + // { deposits: [ { currency: "BTC", + // address: "1MEtiqJWru53FhhHrfJPPvd2tC3TPDVcmW", + // amount: "0.01063000", + // confirmations: 1, + // txid: "952b0e1888d6d491591facc0d37b5ebec540ac1efb241fdbc22bcc20d1822fb6", + // timestamp: 1507916888, + // status: "COMPLETE" }, + // { currency: "ETH", + // address: "0x20108ba20b65c04d82909e91df06618107460197", + // amount: "4.00000000", + // confirmations: 38, + // txid: "0x4be260073491fe63935e9e0da42bd71138fdeb803732f41501015a2d46eb479d", + // timestamp: 1525060430, + // status: "COMPLETE" } ], + // withdrawals: [ { withdrawalNumber: 8224394, + // currency: "EMC2", + // address: "EYEKyCrqTNmVCpdDV8w49XvSKRP9N3EUyF", + // amount: "63.10796020", + // fee: "0.01000000", + // timestamp: 1510819838, + // status: "COMPLETE: d37354f9d02cb24d98c8c4fc17aa42f475530b5727effdf668ee5a43ce667fd6", + // ipAddress: "5.220.220.200" }, + // { withdrawalNumber: 9290444, + // currency: "ETH", + // address: "0x191015ff2e75261d50433fbd05bd57e942336149", + // amount: "0.15500000", + // fee: "0.00500000", + // timestamp: 1514099289, + // status: "COMPLETE: 0x12d444493b4bca668992021fd9e54b5292b8e71d9927af1f076f554e4bea5b2d", + // ipAddress: "5.228.227.214" }, + // { withdrawalNumber: 11518260, + // currency: "BTC", + // address: "8JoDXAmE1GY2LRK8jD1gmAmgRPq54kXJ4t", + // amount: "0.20000000", + // fee: "0.00050000", + // timestamp: 1527918155, + // status: "COMPLETE: 1864f4ebb277d90b0b1ff53259b36b97fa1990edc7ad2be47c5e0ab41916b5ff", + // ipAddress: "211.8.195.26" } ] } + // + return response; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.fetchTransactionsHelper (code, since, limit, params); + for (let i = 0; i < response['deposits'].length; i++) { + response['deposits'][i]['type'] = 'deposit'; + } + for (let i = 0; i < response['withdrawals'].length; i++) { + response['withdrawals'][i]['type'] = 'withdrawal'; + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); + const deposits = this.parseTransactions (response['deposits'], currency, since, limit); + const transactions = this.arrayConcat (deposits, withdrawals); + return this.filterByCurrencySinceLimit (this.sortBy (transactions, 'timestamp'), code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.fetchTransactionsHelper (code, since, limit, params); + for (let i = 0; i < response['withdrawals'].length; i++) { + response['withdrawals'][i]['type'] = 'withdrawal'; + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const withdrawals = this.parseTransactions (response['withdrawals'], currency, since, limit); + return this.filterByCurrencySinceLimit (withdrawals, code, since, limit); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + const response = await this.fetchTransactionsHelper (code, since, limit, params); + for (let i = 0; i < response['deposits'].length; i++) { + response['deposits'][i]['type'] = 'deposit'; + } + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + const deposits = this.parseTransactions (response['deposits'], currency, since, limit); + return this.filterByCurrencySinceLimit (deposits, code, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'COMPLETE': 'ok', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // deposits + // + // { + // "txid": "f49d489616911db44b740612d19464521179c76ebe9021af85b6de1e2f8d68cd", + // "type": "deposit", + // "amount": "49798.01987021", + // "status": "COMPLETE", + // "address": "DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT", + // "currency": "DOGE", + // "timestamp": 1524321838, + // "confirmations": 3371, + // "depositNumber": 134587098 + // } + // + // withdrawals + // + // { + // "fee": "0.00050000", + // "type": "withdrawal", + // "amount": "0.40234387", + // "status": "COMPLETE: fbabb2bf7d81c076f396f3441166d5f60f6cea5fdfe69e02adcc3b27af8c2746", + // "address": "1EdAqY4cqHoJGAgNfUFER7yZpg1Jc9DUa3", + // "currency": "BTC", + // "canCancel": 0, + // "ipAddress": "185.230.101.31", + // "paymentID": null, + // "timestamp": 1523834337, + // "canResendEmail": 0, + // "withdrawalNumber": 11162900 + // } + // + const timestamp = this.safeTimestamp (transaction, 'timestamp'); + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + let status = this.safeString (transaction, 'status', 'pending'); + let txid = this.safeString (transaction, 'txid'); + if (status !== undefined) { + const parts = status.split (': '); + const numParts = parts.length; + status = parts[0]; + if ((numParts > 1) && (txid === undefined)) { + txid = parts[1]; + } + status = this.parseTransactionStatus (status); + } + const type = this.safeString (transaction, 'type'); + const id = this.safeString2 (transaction, 'withdrawalNumber', 'depositNumber'); + let amount = this.safeFloat (transaction, 'amount'); + const address = this.safeString (transaction, 'address'); + let feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost === undefined) { + // according to https://poloniex.com/fees/ + feeCost = 0; // FIXME: remove hardcoded value that may change any time + } + if (type === 'withdrawal') { + // poloniex withdrawal amount includes the fee + amount = amount - feeCost; + } + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.extend ({ 'command': path }, params); + if (api === 'public') { + url += '?' + this.urlencode (query); + } else { + this.checkRequiredCredentials (); + query['nonce'] = this.nonce (); + body = this.urlencode (query); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // {"error":"Permission denied."} + if ('error' in response) { + const message = response['error']; + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/rightbtc.js b/TaskServer/node_modules/ccxt/js/rightbtc.js new file mode 100644 index 000000000..2fb83c274 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/rightbtc.js @@ -0,0 +1,761 @@ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ROUND } = require ('./base/functions/number'); +const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors'); + +module.exports = class rightbtc extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'rightbtc', + 'name': 'RightBTC', + 'countries': [ 'AE' ], + 'has': { + 'privateAPI': false, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': false, + 'fetchOrder': 'emulated', + 'fetchMyTrades': true, + }, + 'timeframes': { + '1m': 'min1', + '5m': 'min5', + '15m': 'min15', + '30m': 'min30', + '1h': 'hr1', + '1d': 'day1', + '1w': 'week', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/42633917-7d20757e-85ea-11e8-9f53-fffe9fbb7695.jpg', + 'api': 'https://www.rightbtc.com/api', + 'www': 'https://www.rightbtc.com', + 'doc': [ + 'https://docs.rightbtc.com/api/', + ], + // eslint-disable-next-line no-useless-escape + // 'fees': 'https://www.rightbtc.com/\#\!/support/fee', + }, + 'api': { + 'public': { + 'get': [ + // 'getAssetsTradingPairs/zh', // 404 + 'trading_pairs', + 'ticker/{trading_pair}', + 'tickers', + 'depth/{trading_pair}', + 'depth/{trading_pair}/{count}', + 'trades/{trading_pair}', + 'trades/{trading_pair}/{count}', + 'candlestick/latest/{trading_pair}', + 'candlestick/{timeSymbol}/{trading_pair}', + 'candlestick/{timeSymbol}/{trading_pair}/{count}', + ], + }, + 'trader': { + 'get': [ + 'balance/{symbol}', + 'balances', + 'deposits/{asset}/{page}', + 'withdrawals/{asset}/{page}', + 'orderpage/{trading_pair}/{cursor}', + 'orders/{trading_pair}/{ids}', // ids are a slash-separated list of {id}/{id}/{id}/... + 'history/{trading_pair}/{ids}', + 'historys/{trading_pair}/{page}', + 'trading_pairs', + ], + 'post': [ + 'order', + ], + 'delete': [ + 'order/{trading_pair}/{ids}', + ], + }, + }, + // HARDCODING IS DEPRECATED, THE FEES BELOW SHOULD BE REWRITTEN + 'fees': { + 'trading': { + // min trading fees + // 0.0001 BTC + // 0.01 ETP + // 0.001 ETH + // 0.1 BITCNY + 'maker': 0.1 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'withdraw': { + // 'BTM': n => 3 + n * (1 / 100), + // 'ZDC': n => 1 + n * (0.5 / 100), + // 'ZGC': n => 0.5 + n * (0.5 / 100), + // 'BTS': n => 1 + n * (1 / 100), + // 'DLT': n => 3 + n * (1 / 100), + // 'SNT': n => 10 + n * (1 / 100), + // 'XNC': n => 1 + n * (1 / 100), + // 'ICO': n => 3 + n * (1 / 100), + // 'CMC': n => 1 + n * (0.5 / 100), + // 'GXS': n => 0.2 + n * (1 / 100), + // 'OBITS': n => 0.3 + n * (1 / 100), + // 'ICS': n => 2 + n * (1 / 100), + // 'TIC': n => 2 + n * (1 / 100), + // 'IND': n => 20 + n * (1 / 100), + // 'MVC': n => 20 + n * (1 / 100), + // 'BitCNY': n => 0.1 + n * (1 / 100), + // 'MTX': n => 1 + n * (1 / 100), + 'ETP': 0.01, + 'BTC': 0.0005, + 'ETH': 0.005, + 'ETC': 0.01, + 'STORJ': 3, + 'LTC': 0.01, + 'ZEC': 0.001, + 'BCC': 0.001, + 'XRB': 0, + 'NXS': 0.1, + }, + }, + }, + 'commonCurrencies': { + 'XRB': 'NANO', + }, + 'exceptions': { + 'ERR_USERTOKEN_NOT_FOUND': AuthenticationError, + 'ERR_ASSET_NOT_EXISTS': ExchangeError, + 'ERR_ASSET_NOT_AVAILABLE': ExchangeError, + 'ERR_BALANCE_NOT_ENOUGH': InsufficientFunds, + 'ERR_CREATE_ORDER': InvalidOrder, + 'ERR_CANDLESTICK_DATA': ExchangeError, + }, + }); + } + + async fetchMarkets (params = {}) { + // let zh = await this.publicGetGetAssetsTradingPairsZh (); + const markets = await this.publicGetTradingPairs (params); + const marketIds = Object.keys (markets); + const result = []; + for (let i = 0; i < marketIds.length; i++) { + const id = marketIds[i]; + const market = markets[id]; + const baseId = this.safeString (market, 'bid_asset_symbol'); + const quoteId = this.safeString (market, 'ask_asset_symbol'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'bid_asset_decimals'), + 'price': this.safeInteger (market, 'ask_asset_decimals'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': Math.pow (10, precision['price']), + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': Math.pow (10, precision['price']), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + divideSafeFloat (x, key, divisor) { + const value = this.safeFloat (x, key); + if (value !== undefined) { + return value / divisor; + } + return value; + } + + parseTicker (ticker, market = undefined) { + const symbol = market['symbol']; + const timestamp = this.safeInteger (ticker, 'date'); + const last = this.divideSafeFloat (ticker, 'last', 1e8); + const high = this.divideSafeFloat (ticker, 'high', 1e8); + const low = this.divideSafeFloat (ticker, 'low', 1e8); + const bid = this.divideSafeFloat (ticker, 'buy', 1e8); + const ask = this.divideSafeFloat (ticker, 'sell', 1e8); + const baseVolume = this.divideSafeFloat (ticker, 'vol24h', 1e8); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': high, + 'low': low, + 'bid': bid, + 'bidVolume': undefined, + 'ask': ask, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + }; + const response = await this.publicGetTickerTradingPair (this.extend (request, params)); + const result = this.safeValue (response, 'result'); + if (result === undefined) { + throw new ExchangeError (this.id + ' fetchTicker returned an empty response for symbol ' + symbol); + } + return this.parseTicker (result, market); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTickers (params); + const tickers = response['result']; + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const id = ticker['market']; + if (!(id in this.marketsById)) { + continue; + } + const market = this.marketsById[id]; + const symbol = market['symbol']; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'trading_pair': this.marketId (symbol), + }; + let method = 'publicGetDepthTradingPair'; + if (limit !== undefined) { + method += 'Count'; + request['count'] = limit; + } + const response = await this[method] (this.extend (request, params)); + const bidsasks = {}; + const types = ['bid', 'ask']; + for (let ti = 0; ti < types.length; ti++) { + const type = types[ti]; + bidsasks[type] = []; + for (let i = 0; i < response['result'][type].length; i++) { + const [ price, amount, total ] = response['result'][type][i]; + bidsasks[type].push ([ + price / 1e8, + amount / 1e8, + total / 1e8, + ]); + } + } + return this.parseOrderBook (bidsasks, undefined, 'bid', 'ask'); + } + + parseTrade (trade, market = undefined) { + // + // { + // "order_id": 118735, + // "trade_id": 7, + // "trading_pair": "BTCCNY", + // "side": "B", + // "quantity": 1000000000, + // "price": 900000000, + // "created_at": "2017-06-06T20:45:27.000Z" + // } + // + let timestamp = this.safeInteger (trade, 'date'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + } + let id = this.safeString (trade, 'tid'); + id = this.safeString (trade, 'trade_id', id); + const orderId = this.safeString (trade, 'order_id'); + const price = this.divideSafeFloat (trade, 'price', 1e8); + let amount = this.safeFloat (trade, 'amount'); + amount = this.safeFloat (trade, 'quantity', amount); + if (amount !== undefined) { + amount = amount / 1e8; + } + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (trade, 'trading_pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = this.costToPrecision (symbol, price * amount); + cost = parseFloat (cost); + let side = this.safeStringLower (trade, 'side'); + if (side === 'b') { + side = 'buy'; + } else if (side === 's') { + side = 'sell'; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + }; + const response = await this.publicGetTradesTradingPair (this.extend (request, params)); + return this.parseTrades (response['result'], market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + return [ + parseInt (ohlcv[0]), + parseFloat (ohlcv[2]) / 1e8, + parseFloat (ohlcv[3]) / 1e8, + parseFloat (ohlcv[4]) / 1e8, + parseFloat (ohlcv[5]) / 1e8, + parseFloat (ohlcv[1]) / 1e8, + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'timeSymbol': this.timeframes[timeframe], + }; + const response = await this.publicGetCandlestickTimeSymbolTradingPair (this.extend (request, params)); + return this.parseOHLCVs (response['result'], market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.traderGetBalances (params); + // + // { + // "status": { + // "success": 1, + // "message": "GET_BALANCES" + // }, + // "result": [ + // { + // "asset": "ETP", + // "balance": "5000000000000", + // "frozen": "0", + // "state": "1" + // }, + // { + // "asset": "CNY", + // "balance": "10000000000000", + // "frozen": "240790000", + // "state": "1" + // } + // ] + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'result', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'asset'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + // https://github.com/ccxt/ccxt/issues/3873 + account['free'] = this.divideSafeFloat (balance, 'balance', 1e8); + account['used'] = this.divideSafeFloat (balance, 'frozen', 1e8); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const order = { + 'trading_pair': market['id'], + // We need to use decimalToPrecision here, since + // 0.036*1e8 === 3599999.9999999995 + // which would get truncated to 3599999 after parseInt + // which would then be rejected by rightBtc because it's too precise + 'quantity': parseInt (this.decimalToPrecision (amount * 1e8, ROUND, 0, this.precisionMode)), + 'limit': parseInt (this.decimalToPrecision (price * 1e8, ROUND, 0, this.precisionMode)), + 'type': type.toUpperCase (), + 'side': side.toUpperCase (), + }; + const response = await this.traderPostOrder (this.extend (order, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'ids': id, + }; + const response = await this.traderDeleteOrderTradingPairIds (this.extend (request, params)); + return response; + } + + parseOrderStatus (status) { + const statuses = { + 'NEW': 'open', + 'TRADE': 'closed', // TRADE means filled or partially filled orders + 'CANCEL': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder / fetchOpenOrders + // + // { + // "id": 4180528, + // "quantity": 20000000, + // "rest": 20000000, + // "limit": 1000000, + // "price": null, + // "side": "BUY", + // "created": 1496005693738 + // } + // + // fetchOrders + // + // { + // "trading_pair": "ETPCNY", + // "status": "TRADE", + // "fee": 0.23, + // "min_fee": 10000000, + // "created_at": "2017-05-25T00:12:27.000Z", + // "cost": 1152468000000, + // "limit": 3600000000, + // "id": 11060, + // "quantity": 32013000000, + // "filled_quantity": 32013000000 + // } + // + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const marketId = this.safeString (order, 'trading_pair'); + if (market === undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let symbol = marketId; + if (market !== undefined) { + symbol = market['symbol']; + } + let timestamp = this.safeInteger (order, 'created'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (order, 'created_at')); + } + if ('time' in order) { + timestamp = order['time']; + } else if ('transactTime' in order) { + timestamp = order['transactTime']; + } + let price = this.safeFloat2 (order, 'limit', 'price'); + if (price !== undefined) { + price = price / 1e8; + } + const amount = this.divideSafeFloat (order, 'quantity', 1e8); + let filled = this.divideSafeFloat (order, 'filled_quantity', 1e8); + let remaining = this.divideSafeFloat (order, 'rest', 1e8); + const cost = this.divideSafeFloat (order, 'cost', 1e8); + // lines 483-494 should be generalized into a base class method + if (amount !== undefined) { + if (remaining === undefined) { + if (filled !== undefined) { + remaining = Math.max (0, amount - filled); + } + } + if (filled === undefined) { + if (remaining !== undefined) { + filled = Math.max (0, amount - remaining); + } + } + } + const type = 'limit'; + const side = this.safeStringLower (order, 'side'); + const feeCost = this.divideSafeFloat (order, 'min_fee', 1e8); + let fee = undefined; + if (feeCost !== undefined) { + let feeCurrency = undefined; + if (market !== undefined) { + feeCurrency = market['quote']; + } + fee = { + 'rate': this.safeFloat (order, 'fee'), + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + const trades = undefined; + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'ids': id, + }; + const response = await this.traderGetOrdersTradingPairIds (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": "SUC_LIST_AVTICE_ORDERS" + // }, + // "result": [ + // { + // "id": 4180528, + // "quantity": 20000000, + // "rest": 20000000, + // "limit": 1000000, + // "price": null, + // "side": "BUY", + // "created": 1496005693738 + // } + // ] + // } + // + const orders = this.parseOrders (response['result'], market); + const ordersById = this.indexBy (orders, 'id'); + if (!(id in ordersById)) { + throw new OrderNotFound (this.id + ' fetchOrder could not find order ' + id.toString () + ' in open orders.'); + } + return ordersById[id]; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'cursor': 0, + }; + const response = await this.traderGetOrderpageTradingPairCursor (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": "SUC_LIST_AVTICE_ORDERS_PAGE" + // }, + // "result": { + // "cursor": "0", + // "orders": [ + // { + // "id": 4180528, + // "quantity": 20000000, + // "rest": 20000000, + // "limit": 1000000, + // "price": null, + // "side": "BUY", + // "created": 1496005693738 + // } + // ] + // } + // } + // + return this.parseOrders (response['result']['orders'], market, since, limit); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + let ids = this.safeString (params, 'ids'); + if ((symbol === undefined) || (ids === undefined)) { + throw new ArgumentsRequired (this.id + " fetchOrders requires a 'symbol' argument and an extra 'ids' parameter. The 'ids' should be an array or a string of one or more order ids separated with slashes."); // eslint-disable-line quotes + } + if (Array.isArray (ids)) { + ids = ids.join ('/'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'ids': ids, + }; + const response = await this.traderGetHistoryTradingPairIds (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": null + // }, + // "result": [ + // { + // "trading_pair": "ETPCNY", + // "status": "TRADE", + // "fee": 0.23, + // "min_fee": 10000000, + // "created_at": "2017-05-25T00:12:27.000Z", + // "cost": 1152468000000, + // "limit": 3600000000, + // "id": 11060, + // "quantity": 32013000000, + // "filled_quantity": 32013000000 + // } + // ] + // } + // + return this.parseOrders (response['result'], undefined, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'trading_pair': market['id'], + 'page': 0, + }; + const response = await this.traderGetHistorysTradingPairPage (this.extend (request, params)); + // + // let response = { + // "status": { + // "success": 1, + // "message": null + // }, + // "result": [ + // { + // "order_id": 118735, + // "trade_id": 7, + // "trading_pair": "BTCCNY", + // "side": "B", + // "quantity": 1000000000, + // "price": 900000000, + // "created_at": "2017-06-06T20:45:27.000Z" + // }, + // { + // "order_id": 118734, + // "trade_id": 7, + // "trading_pair": "BTCCNY", + // "side": "S", + // "quantity": 1000000000, + // "price": 900000000, + // "created_at": "2017-06-06T20:45:27.000Z" + // } + // ] + // } + // + return this.parseTrades (response['result'], undefined, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + '/' + api + '/' + this.implodeParams (path, params); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + headers = { + 'apikey': this.apiKey, + 'signature': this.secret, + }; + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + headers['Content-Type'] = 'application/json'; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + const status = this.safeValue (response, 'status'); + if (status !== undefined) { + // + // {"status":{"success":0,"message":"ERR_USERTOKEN_NOT_FOUND"}} + // + const success = this.safeString (status, 'success'); + if (success !== '1') { + const message = this.safeString (status, 'message'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions, message, feedback); + throw new ExchangeError (feedback); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/southxchange.js b/TaskServer/node_modules/ccxt/js/southxchange.js new file mode 100644 index 000000000..3ebd4e3dd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/southxchange.js @@ -0,0 +1,362 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class southxchange extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'southxchange', + 'name': 'SouthXchange', + 'countries': [ 'AR' ], // Argentina + 'rateLimit': 1000, + 'has': { + 'CORS': true, + 'createDepositAddress': true, + 'fetchOpenOrders': true, + 'fetchTickers': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27838912-4f94ec8a-60f6-11e7-9e5d-bbf9bd50a559.jpg', + 'api': 'https://www.southxchange.com/api', + 'www': 'https://www.southxchange.com', + 'doc': 'https://www.southxchange.com/Home/Api', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'price/{symbol}', + 'prices', + 'book/{symbol}', + 'trades/{symbol}', + ], + }, + 'private': { + 'post': [ + 'cancelMarketOrders', + 'cancelOrder', + 'getOrder', + 'generatenewaddress', + 'listOrders', + 'listBalances', + 'listTransactions', + 'placeOrder', + 'withdraw', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.1 / 100, + 'taker': 0.3 / 100, + }, + }, + 'commonCurrencies': { + 'SMT': 'SmartNode', + 'MTC': 'Marinecoin', + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetMarkets (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseId = market[0]; + const quoteId = market[1]; + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = baseId + '/' + quoteId; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': undefined, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostListBalances (params); + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'Currency'); + const code = this.safeCurrencyCode (currencyId); + const deposited = this.safeFloat (balance, 'Deposited'); + const unconfirmed = this.safeFloat (balance, 'Unconfirmed'); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'Available'); + account['total'] = this.sum (deposited, unconfirmed); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'symbol': this.marketId (symbol), + }; + const response = await this.publicGetBookSymbol (this.extend (request, params)); + return this.parseOrderBook (response, undefined, 'BuyOrders', 'SellOrders', 'Price', 'Amount'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'Last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'Bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'Ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'Variation24Hr'), + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'Volume24Hr'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetPrices (params); + const tickers = this.indexBy (response, 'Market'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetPriceSymbol (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market) { + const timestamp = this.safeTimestamp (trade, 'At'); + const price = this.safeFloat (trade, 'Price'); + const amount = this.safeFloat (trade, 'Amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const side = this.safeString (trade, 'Type'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': undefined, + 'order': undefined, + 'type': undefined, + 'side': side, + 'price': price, + 'takerOrMaker': undefined, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + }; + const response = await this.publicGetTradesSymbol (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + const status = 'open'; + const baseId = this.safeString (order, 'ListingCurrency'); + const quoteId = this.safeString (order, 'ReferenceCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const timestamp = undefined; + const price = this.safeFloat (order, 'LimitPrice'); + const amount = this.safeFloat (order, 'OriginalAmount'); + const remaining = this.safeFloat (order, 'Amount'); + let filled = undefined; + let cost = undefined; + if (amount !== undefined) { + cost = price * amount; + if (remaining !== undefined) { + filled = amount - remaining; + } + } + const type = 'limit'; + const side = this.safeStringLower (order, 'Type'); + const id = this.safeString (order, 'Code'); + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + return result; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const response = await this.privatePostListOrders (params); + return this.parseOrders (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'listingCurrency': market['base'], + 'referenceCurrency': market['quote'], + 'type': side, + 'amount': amount, + }; + if (type === 'limit') { + request['limitPrice'] = price; + } + const response = await this.privatePostPlaceOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response.toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderCode': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privatePostGeneratenewaddress (this.extend (request, params)); + const parts = response.split ('|'); + const numParts = parts.length; + const address = parts[0]; + this.checkAddress (address); + let tag = undefined; + if (numParts > 1) { + tag = parts[1]; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + 'address': address, + 'amount': amount, + }; + if (tag !== undefined) { + request['address'] = address + '|' + tag; + } + const response = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const url = this.urls['api'] + '/' + this.implodeParams (path, params); + let query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + query = this.extend ({ + 'key': this.apiKey, + 'nonce': nonce, + }, query); + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + 'Hash': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/BN/bn.js b/TaskServer/node_modules/ccxt/js/static_dependencies/BN/bn.js new file mode 100644 index 000000000..6b2485698 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/BN/bn.js @@ -0,0 +1,3526 @@ +(function (module, exports) { + 'use strict'; + + // Utils + function assert (val, msg) { + if (!val) throw new Error(msg || 'Assertion failed'); + } + + // Could use `inherits` module, but don't want to move from single file + // architecture yet. + function inherits (ctor, superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; + } + + // BN + + function BN (number, base, endian) { + if (BN.isBN(number)) { + return number; + } + + this.negative = 0; + this.words = null; + this.length = 0; + + // Reduction context + this.red = null; + + if (number !== null) { + if (base === 'le' || base === 'be') { + endian = base; + base = 10; + } + + this._init(number || 0, base || 10, endian || 'be'); + } + } + if (typeof module === 'object') { + module.exports = BN; + } else { + exports.BN = BN; + } + + BN.BN = BN; + BN.wordSize = 26; + + var Buffer; + try { + Buffer = require('buffer').Buffer; + } catch (e) { + } + + BN.isBN = function isBN (num) { + if (num instanceof BN) { + return true; + } + + return num !== null && typeof num === 'object' && + num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); + }; + + BN.max = function max (left, right) { + if (left.cmp(right) > 0) return left; + return right; + }; + + BN.min = function min (left, right) { + if (left.cmp(right) < 0) return left; + return right; + }; + + BN.prototype._init = function init (number, base, endian) { + if (typeof number === 'number') { + return this._initNumber(number, base, endian); + } + + if (typeof number === 'object') { + return this._initArray(number, base, endian); + } + + if (base === 'hex') { + base = 16; + } + assert(base === (base | 0) && base >= 2 && base <= 36); + + number = number.toString().replace(/\s+/g, ''); + var start = 0; + if (number[0] === '-') { + start++; + } + + if (base === 16) { + this._parseHex(number, start); + } else { + this._parseBase(number, base, start); + } + + if (number[0] === '-') { + this.negative = 1; + } + + this._strip(); + + if (endian !== 'le') return; + + this._initArray(this.toArray(), base, endian); + }; + + BN.prototype._initNumber = function _initNumber (number, base, endian) { + if (number < 0) { + this.negative = 1; + number = -number; + } + if (number < 0x4000000) { + this.words = [number & 0x3ffffff]; + this.length = 1; + } else if (number < 0x10000000000000) { + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff + ]; + this.length = 2; + } else { + assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff, + 1 + ]; + this.length = 3; + } + + if (endian !== 'le') return; + + // Reverse the bytes + this._initArray(this.toArray(), base, endian); + }; + + BN.prototype._initArray = function _initArray (number, base, endian) { + // Perhaps a Uint8Array + assert(typeof number.length === 'number'); + if (number.length <= 0) { + this.words = [0]; + this.length = 1; + return this; + } + + this.length = Math.ceil(number.length / 3); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + var j, w; + var off = 0; + if (endian === 'be') { + for (i = number.length - 1, j = 0; i >= 0; i -= 3) { + w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } else if (endian === 'le') { + for (i = 0, j = 0; i < number.length; i += 3) { + w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } + return this._strip(); + }; + + function parseHex (str, start, end) { + var r = 0; + var len = Math.min(str.length, end); + var z = 0; + for (var i = start; i < len; i++) { + var c = str.charCodeAt(i) - 48; + + r <<= 4; + + var b; + + // 'a' - 'f' + if (c >= 49 && c <= 54) { + b = c - 49 + 0xa; + + // 'A' - 'F' + } else if (c >= 17 && c <= 22) { + b = c - 17 + 0xa; + + // '0' - '9' + } else { + b = c; + } + + r |= b; + z |= b; + } + + assert(!(z & 0xf0), 'Invalid character in ' + str); + return r; + } + + BN.prototype._parseHex = function _parseHex (number, start) { + // Create possibly bigger array to ensure that it fits the number + this.length = Math.ceil((number.length - start) / 6); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + var j, w; + // Scan 24-bit chunks and add them to the number + var off = 0; + for (i = number.length - 6, j = 0; i >= start; i -= 6) { + w = parseHex(number, i, i + 6); + this.words[j] |= (w << off) & 0x3ffffff; + // NOTE: `0x3fffff` is intentional here, 26bits max shift + 24bit hex limb + this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + if (i + 6 !== start) { + w = parseHex(number, start, i + 6); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; + } + this._strip(); + }; + + function parseBase (str, start, end, mul) { + var r = 0; + var b = 0; + var len = Math.min(str.length, end); + for (var i = start; i < len; i++) { + var c = str.charCodeAt(i) - 48; + + r *= mul; + + // 'a' + if (c >= 49) { + b = c - 49 + 0xa; + + // 'A' + } else if (c >= 17) { + b = c - 17 + 0xa; + + // '0' - '9' + } else { + b = c; + } + assert(c >= 0 && b < mul, 'Invalid character'); + r += b; + } + return r; + } + + BN.prototype._parseBase = function _parseBase (number, base, start) { + // Initialize as zero + this.words = [0]; + this.length = 1; + + // Find length of limb in base + for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { + limbLen++; + } + limbLen--; + limbPow = (limbPow / base) | 0; + + var total = number.length - start; + var mod = total % limbLen; + var end = Math.min(total, total - mod) + start; + + var word = 0; + for (var i = start; i < end; i += limbLen) { + word = parseBase(number, i, i + limbLen, base); + + this.imuln(limbPow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + + if (mod !== 0) { + var pow = 1; + word = parseBase(number, i, number.length, base); + + for (i = 0; i < mod; i++) { + pow *= base; + } + + this.imuln(pow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + }; + + BN.prototype.copy = function copy (dest) { + dest.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + dest.words[i] = this.words[i]; + } + dest.length = this.length; + dest.negative = this.negative; + dest.red = this.red; + }; + + BN.prototype._move = function _move (dest) { + dest.words = this.words; + dest.length = this.length; + dest.negative = this.negative; + dest.red = this.red; + }; + + BN.prototype.clone = function clone () { + var r = new BN(null); + this.copy(r); + return r; + }; + + BN.prototype._expand = function _expand (size) { + while (this.length < size) { + this.words[this.length++] = 0; + } + return this; + }; + + // Remove leading `0` from `this` + BN.prototype._strip = function strip () { + while (this.length > 1 && this.words[this.length - 1] === 0) { + this.length--; + } + return this._normSign(); + }; + + BN.prototype._normSign = function _normSign () { + // -0 = 0 + if (this.length === 1 && this.words[0] === 0) { + this.negative = 0; + } + return this; + }; + + // Check Symbol.for because not everywhere where Symbol defined + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility + if (typeof Symbol !== 'undefined' && typeof Symbol.for === 'function') { + BN.prototype[Symbol.for('nodejs.util.inspect.custom')] = inspect; + } else { + BN.prototype.inspect = inspect; + } + + function inspect () { + return (this.red ? ''; + } + + /* + + var zeros = []; + var groupSizes = []; + var groupBases = []; + + var s = ''; + var i = -1; + while (++i < BN.wordSize) { + zeros[i] = s; + s += '0'; + } + groupSizes[0] = 0; + groupSizes[1] = 0; + groupBases[0] = 0; + groupBases[1] = 0; + var base = 2 - 1; + while (++base < 36 + 1) { + var groupSize = 0; + var groupBase = 1; + while (groupBase < (1 << BN.wordSize) / base) { + groupBase *= base; + groupSize += 1; + } + groupSizes[base] = groupSize; + groupBases[base] = groupBase; + } + + */ + + var zeros = [ + '', + '0', + '00', + '000', + '0000', + '00000', + '000000', + '0000000', + '00000000', + '000000000', + '0000000000', + '00000000000', + '000000000000', + '0000000000000', + '00000000000000', + '000000000000000', + '0000000000000000', + '00000000000000000', + '000000000000000000', + '0000000000000000000', + '00000000000000000000', + '000000000000000000000', + '0000000000000000000000', + '00000000000000000000000', + '000000000000000000000000', + '0000000000000000000000000' + ]; + + var groupSizes = [ + 0, 0, + 25, 16, 12, 11, 10, 9, 8, + 8, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 5, 5, + 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5 + ]; + + var groupBases = [ + 0, 0, + 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, + 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, + 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, + 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, + 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 + ]; + + BN.prototype.toString = function toString (base, padding) { + base = base || 10; + padding = padding | 0 || 1; + + var out; + if (base === 16 || base === 'hex') { + out = ''; + var off = 0; + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = this.words[i]; + var word = (((w << off) | carry) & 0xffffff).toString(16); + carry = (w >>> (24 - off)) & 0xffffff; + if (carry !== 0 || i !== this.length - 1) { + out = zeros[6 - word.length] + word + out; + } else { + out = word + out; + } + off += 2; + if (off >= 26) { + off -= 26; + i--; + } + } + if (carry !== 0) { + out = carry.toString(16) + out; + } + while (out.length % padding !== 0) { + out = '0' + out; + } + if (this.negative !== 0) { + out = '-' + out; + } + return out; + } + + if (base === (base | 0) && base >= 2 && base <= 36) { + // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); + var groupSize = groupSizes[base]; + // var groupBase = Math.pow(base, groupSize); + var groupBase = groupBases[base]; + out = ''; + var c = this.clone(); + c.negative = 0; + while (!c.isZero()) { + var r = c.modrn(groupBase).toString(base); + c = c.idivn(groupBase); + + if (!c.isZero()) { + out = zeros[groupSize - r.length] + r + out; + } else { + out = r + out; + } + } + if (this.isZero()) { + out = '0' + out; + } + while (out.length % padding !== 0) { + out = '0' + out; + } + if (this.negative !== 0) { + out = '-' + out; + } + return out; + } + + assert(false, 'Base should be between 2 and 36'); + }; + + BN.prototype.toNumber = function toNumber () { + var ret = this.words[0]; + if (this.length === 2) { + ret += this.words[1] * 0x4000000; + } else if (this.length === 3 && this.words[2] === 0x01) { + // NOTE: at this stage it is known that the top bit is set + ret += 0x10000000000000 + (this.words[1] * 0x4000000); + } else if (this.length > 2) { + assert(false, 'Number can only safely store up to 53 bits'); + } + return (this.negative !== 0) ? -ret : ret; + }; + + BN.prototype.toJSON = function toJSON () { + return this.toString(16, 2); + }; + + if (Buffer) { + BN.prototype.toBuffer = function toBuffer (endian, length) { + return this.toArrayLike(Buffer, endian, length); + }; + } + + BN.prototype.toArray = function toArray (endian, length) { + return this.toArrayLike(Array, endian, length); + }; + + var allocate = function allocate (ArrayType, size) { + if (ArrayType.allocUnsafe) { + return ArrayType.allocUnsafe(size); + } + return new ArrayType(size); + }; + + BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { + this._strip(); + + var byteLength = this.byteLength(); + var reqLength = length || Math.max(1, byteLength); + assert(byteLength <= reqLength, 'byte array longer than desired length'); + assert(reqLength > 0, 'Requested array length <= 0'); + + var res = allocate(ArrayType, reqLength); + var postfix = endian === 'le' ? 'LE' : 'BE'; + this['_toArrayLike' + postfix](res, byteLength); + return res; + }; + + BN.prototype._toArrayLikeLE = function _toArrayLikeLE (res, byteLength) { + var position = 0; + var carry = 0; + + for (var i = 0, shift = 0; i < this.length; i++) { + var word = (this.words[i] << shift) | carry; + + res[position++] = word & 0xff; + if (position < res.length) { + res[position++] = (word >> 8) & 0xff; + } + if (position < res.length) { + res[position++] = (word >> 16) & 0xff; + } + + if (shift === 6) { + if (position < res.length) { + res[position++] = (word >> 24) & 0xff; + } + carry = 0; + shift = 0; + } else { + carry = word >>> 24; + shift += 2; + } + } + + if (position < res.length) { + res[position++] = carry; + + while (position < res.length) { + res[position++] = 0; + } + } + }; + + BN.prototype._toArrayLikeBE = function _toArrayLikeBE (res, byteLength) { + var position = res.length - 1; + var carry = 0; + + for (var i = 0, shift = 0; i < this.length; i++) { + var word = (this.words[i] << shift) | carry; + + res[position--] = word & 0xff; + if (position >= 0) { + res[position--] = (word >> 8) & 0xff; + } + if (position >= 0) { + res[position--] = (word >> 16) & 0xff; + } + + if (shift === 6) { + if (position >= 0) { + res[position--] = (word >> 24) & 0xff; + } + carry = 0; + shift = 0; + } else { + carry = word >>> 24; + shift += 2; + } + } + + if (position >= 0) { + res[position--] = carry; + + while (position >= 0) { + res[position--] = 0; + } + } + }; + + if (Math.clz32) { + BN.prototype._countBits = function _countBits (w) { + return 32 - Math.clz32(w); + }; + } else { + BN.prototype._countBits = function _countBits (w) { + var t = w; + var r = 0; + if (t >= 0x1000) { + r += 13; + t >>>= 13; + } + if (t >= 0x40) { + r += 7; + t >>>= 7; + } + if (t >= 0x8) { + r += 4; + t >>>= 4; + } + if (t >= 0x02) { + r += 2; + t >>>= 2; + } + return r + t; + }; + } + + BN.prototype._zeroBits = function _zeroBits (w) { + // Short-cut + if (w === 0) return 26; + + var t = w; + var r = 0; + if ((t & 0x1fff) === 0) { + r += 13; + t >>>= 13; + } + if ((t & 0x7f) === 0) { + r += 7; + t >>>= 7; + } + if ((t & 0xf) === 0) { + r += 4; + t >>>= 4; + } + if ((t & 0x3) === 0) { + r += 2; + t >>>= 2; + } + if ((t & 0x1) === 0) { + r++; + } + return r; + }; + + // Return number of used bits in a BN + BN.prototype.bitLength = function bitLength () { + var w = this.words[this.length - 1]; + var hi = this._countBits(w); + return (this.length - 1) * 26 + hi; + }; + + function toBitArray (num) { + var w = new Array(num.bitLength()); + + for (var bit = 0; bit < w.length; bit++) { + var off = (bit / 26) | 0; + var wbit = bit % 26; + + w[bit] = (num.words[off] >>> wbit) & 0x01; + } + + return w; + } + + // Number of trailing zero bits + BN.prototype.zeroBits = function zeroBits () { + if (this.isZero()) return 0; + + var r = 0; + for (var i = 0; i < this.length; i++) { + var b = this._zeroBits(this.words[i]); + r += b; + if (b !== 26) break; + } + return r; + }; + + BN.prototype.byteLength = function byteLength () { + return Math.ceil(this.bitLength() / 8); + }; + + BN.prototype.toTwos = function toTwos (width) { + if (this.negative !== 0) { + return this.abs().inotn(width).iaddn(1); + } + return this.clone(); + }; + + BN.prototype.fromTwos = function fromTwos (width) { + if (this.testn(width - 1)) { + return this.notn(width).iaddn(1).ineg(); + } + return this.clone(); + }; + + BN.prototype.isNeg = function isNeg () { + return this.negative !== 0; + }; + + // Return negative clone of `this` + BN.prototype.neg = function neg () { + return this.clone().ineg(); + }; + + BN.prototype.ineg = function ineg () { + if (!this.isZero()) { + this.negative ^= 1; + } + + return this; + }; + + // Or `num` with `this` in-place + BN.prototype.iuor = function iuor (num) { + while (this.length < num.length) { + this.words[this.length++] = 0; + } + + for (var i = 0; i < num.length; i++) { + this.words[i] = this.words[i] | num.words[i]; + } + + return this._strip(); + }; + + BN.prototype.ior = function ior (num) { + assert((this.negative | num.negative) === 0); + return this.iuor(num); + }; + + // Or `num` with `this` + BN.prototype.or = function or (num) { + if (this.length > num.length) return this.clone().ior(num); + return num.clone().ior(this); + }; + + BN.prototype.uor = function uor (num) { + if (this.length > num.length) return this.clone().iuor(num); + return num.clone().iuor(this); + }; + + // And `num` with `this` in-place + BN.prototype.iuand = function iuand (num) { + // b = min-length(num, this) + var b; + if (this.length > num.length) { + b = num; + } else { + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = this.words[i] & num.words[i]; + } + + this.length = b.length; + + return this._strip(); + }; + + BN.prototype.iand = function iand (num) { + assert((this.negative | num.negative) === 0); + return this.iuand(num); + }; + + // And `num` with `this` + BN.prototype.and = function and (num) { + if (this.length > num.length) return this.clone().iand(num); + return num.clone().iand(this); + }; + + BN.prototype.uand = function uand (num) { + if (this.length > num.length) return this.clone().iuand(num); + return num.clone().iuand(this); + }; + + // Xor `num` with `this` in-place + BN.prototype.iuxor = function iuxor (num) { + // a.length > b.length + var a; + var b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = a.words[i] ^ b.words[i]; + } + + if (this !== a) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = a.length; + + return this._strip(); + }; + + BN.prototype.ixor = function ixor (num) { + assert((this.negative | num.negative) === 0); + return this.iuxor(num); + }; + + // Xor `num` with `this` + BN.prototype.xor = function xor (num) { + if (this.length > num.length) return this.clone().ixor(num); + return num.clone().ixor(this); + }; + + BN.prototype.uxor = function uxor (num) { + if (this.length > num.length) return this.clone().iuxor(num); + return num.clone().iuxor(this); + }; + + // Not ``this`` with ``width`` bitwidth + BN.prototype.inotn = function inotn (width) { + assert(typeof width === 'number' && width >= 0); + + var bytesNeeded = Math.ceil(width / 26) | 0; + var bitsLeft = width % 26; + + // Extend the buffer with leading zeroes + this._expand(bytesNeeded); + + if (bitsLeft > 0) { + bytesNeeded--; + } + + // Handle complete words + for (var i = 0; i < bytesNeeded; i++) { + this.words[i] = ~this.words[i] & 0x3ffffff; + } + + // Handle the residue + if (bitsLeft > 0) { + this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); + } + + // And remove leading zeroes + return this._strip(); + }; + + BN.prototype.notn = function notn (width) { + return this.clone().inotn(width); + }; + + // Set `bit` of `this` + BN.prototype.setn = function setn (bit, val) { + assert(typeof bit === 'number' && bit >= 0); + + var off = (bit / 26) | 0; + var wbit = bit % 26; + + this._expand(off + 1); + + if (val) { + this.words[off] = this.words[off] | (1 << wbit); + } else { + this.words[off] = this.words[off] & ~(1 << wbit); + } + + return this._strip(); + }; + + // Add `num` to `this` in-place + BN.prototype.iadd = function iadd (num) { + var r; + + // negative + positive + if (this.negative !== 0 && num.negative === 0) { + this.negative = 0; + r = this.isub(num); + this.negative ^= 1; + return this._normSign(); + + // positive + negative + } else if (this.negative === 0 && num.negative !== 0) { + num.negative = 0; + r = this.isub(num); + num.negative = 1; + return r._normSign(); + } + + // a.length > b.length + var a, b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) + (b.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + + this.length = a.length; + if (carry !== 0) { + this.words[this.length] = carry; + this.length++; + // Copy the rest of the words + } else if (a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + return this; + }; + + // Add `num` to `this` + BN.prototype.add = function add (num) { + var res; + if (num.negative !== 0 && this.negative === 0) { + num.negative = 0; + res = this.sub(num); + num.negative ^= 1; + return res; + } else if (num.negative === 0 && this.negative !== 0) { + this.negative = 0; + res = num.sub(this); + this.negative = 1; + return res; + } + + if (this.length > num.length) return this.clone().iadd(num); + + return num.clone().iadd(this); + }; + + // Subtract `num` from `this` in-place + BN.prototype.isub = function isub (num) { + // this - (-num) = this + num + if (num.negative !== 0) { + num.negative = 0; + var r = this.iadd(num); + num.negative = 1; + return r._normSign(); + + // -this - num = -(this + num) + } else if (this.negative !== 0) { + this.negative = 0; + this.iadd(num); + this.negative = 1; + return this._normSign(); + } + + // At this point both numbers are positive + var cmp = this.cmp(num); + + // Optimization - zeroify + if (cmp === 0) { + this.negative = 0; + this.length = 1; + this.words[0] = 0; + return this; + } + + // a > b + var a, b; + if (cmp > 0) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) - (b.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + + // Copy rest of the words + if (carry === 0 && i < a.length && a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = Math.max(this.length, i); + + if (a !== this) { + this.negative = 1; + } + + return this._strip(); + }; + + // Subtract `num` from `this` + BN.prototype.sub = function sub (num) { + return this.clone().isub(num); + }; + + function smallMulTo (self, num, out) { + out.negative = num.negative ^ self.negative; + var len = (self.length + num.length) | 0; + out.length = len; + len = (len - 1) | 0; + + // Peel one iteration (compiler can't do it, because of code complexity) + var a = self.words[0] | 0; + var b = num.words[0] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + var carry = (r / 0x4000000) | 0; + out.words[0] = lo; + + for (var k = 1; k < len; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = carry >>> 26; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = (k - j) | 0; + a = self.words[i] | 0; + b = num.words[j] | 0; + r = a * b + rword; + ncarry += (r / 0x4000000) | 0; + rword = r & 0x3ffffff; + } + out.words[k] = rword | 0; + carry = ncarry | 0; + } + if (carry !== 0) { + out.words[k] = carry | 0; + } else { + out.length--; + } + + return out._strip(); + } + + // TODO(indutny): it may be reasonable to omit it for users who don't need + // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit + // multiplication (like elliptic secp256k1). + var comb10MulTo = function comb10MulTo (self, num, out) { + var a = self.words; + var b = num.words; + var o = out.words; + var c = 0; + var lo; + var mid; + var hi; + var a0 = a[0] | 0; + var al0 = a0 & 0x1fff; + var ah0 = a0 >>> 13; + var a1 = a[1] | 0; + var al1 = a1 & 0x1fff; + var ah1 = a1 >>> 13; + var a2 = a[2] | 0; + var al2 = a2 & 0x1fff; + var ah2 = a2 >>> 13; + var a3 = a[3] | 0; + var al3 = a3 & 0x1fff; + var ah3 = a3 >>> 13; + var a4 = a[4] | 0; + var al4 = a4 & 0x1fff; + var ah4 = a4 >>> 13; + var a5 = a[5] | 0; + var al5 = a5 & 0x1fff; + var ah5 = a5 >>> 13; + var a6 = a[6] | 0; + var al6 = a6 & 0x1fff; + var ah6 = a6 >>> 13; + var a7 = a[7] | 0; + var al7 = a7 & 0x1fff; + var ah7 = a7 >>> 13; + var a8 = a[8] | 0; + var al8 = a8 & 0x1fff; + var ah8 = a8 >>> 13; + var a9 = a[9] | 0; + var al9 = a9 & 0x1fff; + var ah9 = a9 >>> 13; + var b0 = b[0] | 0; + var bl0 = b0 & 0x1fff; + var bh0 = b0 >>> 13; + var b1 = b[1] | 0; + var bl1 = b1 & 0x1fff; + var bh1 = b1 >>> 13; + var b2 = b[2] | 0; + var bl2 = b2 & 0x1fff; + var bh2 = b2 >>> 13; + var b3 = b[3] | 0; + var bl3 = b3 & 0x1fff; + var bh3 = b3 >>> 13; + var b4 = b[4] | 0; + var bl4 = b4 & 0x1fff; + var bh4 = b4 >>> 13; + var b5 = b[5] | 0; + var bl5 = b5 & 0x1fff; + var bh5 = b5 >>> 13; + var b6 = b[6] | 0; + var bl6 = b6 & 0x1fff; + var bh6 = b6 >>> 13; + var b7 = b[7] | 0; + var bl7 = b7 & 0x1fff; + var bh7 = b7 >>> 13; + var b8 = b[8] | 0; + var bl8 = b8 & 0x1fff; + var bh8 = b8 >>> 13; + var b9 = b[9] | 0; + var bl9 = b9 & 0x1fff; + var bh9 = b9 >>> 13; + + out.negative = self.negative ^ num.negative; + out.length = 19; + /* k = 0 */ + lo = Math.imul(al0, bl0); + mid = Math.imul(al0, bh0); + mid = (mid + Math.imul(ah0, bl0)) | 0; + hi = Math.imul(ah0, bh0); + var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; + w0 &= 0x3ffffff; + /* k = 1 */ + lo = Math.imul(al1, bl0); + mid = Math.imul(al1, bh0); + mid = (mid + Math.imul(ah1, bl0)) | 0; + hi = Math.imul(ah1, bh0); + lo = (lo + Math.imul(al0, bl1)) | 0; + mid = (mid + Math.imul(al0, bh1)) | 0; + mid = (mid + Math.imul(ah0, bl1)) | 0; + hi = (hi + Math.imul(ah0, bh1)) | 0; + var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; + w1 &= 0x3ffffff; + /* k = 2 */ + lo = Math.imul(al2, bl0); + mid = Math.imul(al2, bh0); + mid = (mid + Math.imul(ah2, bl0)) | 0; + hi = Math.imul(ah2, bh0); + lo = (lo + Math.imul(al1, bl1)) | 0; + mid = (mid + Math.imul(al1, bh1)) | 0; + mid = (mid + Math.imul(ah1, bl1)) | 0; + hi = (hi + Math.imul(ah1, bh1)) | 0; + lo = (lo + Math.imul(al0, bl2)) | 0; + mid = (mid + Math.imul(al0, bh2)) | 0; + mid = (mid + Math.imul(ah0, bl2)) | 0; + hi = (hi + Math.imul(ah0, bh2)) | 0; + var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; + w2 &= 0x3ffffff; + /* k = 3 */ + lo = Math.imul(al3, bl0); + mid = Math.imul(al3, bh0); + mid = (mid + Math.imul(ah3, bl0)) | 0; + hi = Math.imul(ah3, bh0); + lo = (lo + Math.imul(al2, bl1)) | 0; + mid = (mid + Math.imul(al2, bh1)) | 0; + mid = (mid + Math.imul(ah2, bl1)) | 0; + hi = (hi + Math.imul(ah2, bh1)) | 0; + lo = (lo + Math.imul(al1, bl2)) | 0; + mid = (mid + Math.imul(al1, bh2)) | 0; + mid = (mid + Math.imul(ah1, bl2)) | 0; + hi = (hi + Math.imul(ah1, bh2)) | 0; + lo = (lo + Math.imul(al0, bl3)) | 0; + mid = (mid + Math.imul(al0, bh3)) | 0; + mid = (mid + Math.imul(ah0, bl3)) | 0; + hi = (hi + Math.imul(ah0, bh3)) | 0; + var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; + w3 &= 0x3ffffff; + /* k = 4 */ + lo = Math.imul(al4, bl0); + mid = Math.imul(al4, bh0); + mid = (mid + Math.imul(ah4, bl0)) | 0; + hi = Math.imul(ah4, bh0); + lo = (lo + Math.imul(al3, bl1)) | 0; + mid = (mid + Math.imul(al3, bh1)) | 0; + mid = (mid + Math.imul(ah3, bl1)) | 0; + hi = (hi + Math.imul(ah3, bh1)) | 0; + lo = (lo + Math.imul(al2, bl2)) | 0; + mid = (mid + Math.imul(al2, bh2)) | 0; + mid = (mid + Math.imul(ah2, bl2)) | 0; + hi = (hi + Math.imul(ah2, bh2)) | 0; + lo = (lo + Math.imul(al1, bl3)) | 0; + mid = (mid + Math.imul(al1, bh3)) | 0; + mid = (mid + Math.imul(ah1, bl3)) | 0; + hi = (hi + Math.imul(ah1, bh3)) | 0; + lo = (lo + Math.imul(al0, bl4)) | 0; + mid = (mid + Math.imul(al0, bh4)) | 0; + mid = (mid + Math.imul(ah0, bl4)) | 0; + hi = (hi + Math.imul(ah0, bh4)) | 0; + var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; + w4 &= 0x3ffffff; + /* k = 5 */ + lo = Math.imul(al5, bl0); + mid = Math.imul(al5, bh0); + mid = (mid + Math.imul(ah5, bl0)) | 0; + hi = Math.imul(ah5, bh0); + lo = (lo + Math.imul(al4, bl1)) | 0; + mid = (mid + Math.imul(al4, bh1)) | 0; + mid = (mid + Math.imul(ah4, bl1)) | 0; + hi = (hi + Math.imul(ah4, bh1)) | 0; + lo = (lo + Math.imul(al3, bl2)) | 0; + mid = (mid + Math.imul(al3, bh2)) | 0; + mid = (mid + Math.imul(ah3, bl2)) | 0; + hi = (hi + Math.imul(ah3, bh2)) | 0; + lo = (lo + Math.imul(al2, bl3)) | 0; + mid = (mid + Math.imul(al2, bh3)) | 0; + mid = (mid + Math.imul(ah2, bl3)) | 0; + hi = (hi + Math.imul(ah2, bh3)) | 0; + lo = (lo + Math.imul(al1, bl4)) | 0; + mid = (mid + Math.imul(al1, bh4)) | 0; + mid = (mid + Math.imul(ah1, bl4)) | 0; + hi = (hi + Math.imul(ah1, bh4)) | 0; + lo = (lo + Math.imul(al0, bl5)) | 0; + mid = (mid + Math.imul(al0, bh5)) | 0; + mid = (mid + Math.imul(ah0, bl5)) | 0; + hi = (hi + Math.imul(ah0, bh5)) | 0; + var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; + w5 &= 0x3ffffff; + /* k = 6 */ + lo = Math.imul(al6, bl0); + mid = Math.imul(al6, bh0); + mid = (mid + Math.imul(ah6, bl0)) | 0; + hi = Math.imul(ah6, bh0); + lo = (lo + Math.imul(al5, bl1)) | 0; + mid = (mid + Math.imul(al5, bh1)) | 0; + mid = (mid + Math.imul(ah5, bl1)) | 0; + hi = (hi + Math.imul(ah5, bh1)) | 0; + lo = (lo + Math.imul(al4, bl2)) | 0; + mid = (mid + Math.imul(al4, bh2)) | 0; + mid = (mid + Math.imul(ah4, bl2)) | 0; + hi = (hi + Math.imul(ah4, bh2)) | 0; + lo = (lo + Math.imul(al3, bl3)) | 0; + mid = (mid + Math.imul(al3, bh3)) | 0; + mid = (mid + Math.imul(ah3, bl3)) | 0; + hi = (hi + Math.imul(ah3, bh3)) | 0; + lo = (lo + Math.imul(al2, bl4)) | 0; + mid = (mid + Math.imul(al2, bh4)) | 0; + mid = (mid + Math.imul(ah2, bl4)) | 0; + hi = (hi + Math.imul(ah2, bh4)) | 0; + lo = (lo + Math.imul(al1, bl5)) | 0; + mid = (mid + Math.imul(al1, bh5)) | 0; + mid = (mid + Math.imul(ah1, bl5)) | 0; + hi = (hi + Math.imul(ah1, bh5)) | 0; + lo = (lo + Math.imul(al0, bl6)) | 0; + mid = (mid + Math.imul(al0, bh6)) | 0; + mid = (mid + Math.imul(ah0, bl6)) | 0; + hi = (hi + Math.imul(ah0, bh6)) | 0; + var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; + w6 &= 0x3ffffff; + /* k = 7 */ + lo = Math.imul(al7, bl0); + mid = Math.imul(al7, bh0); + mid = (mid + Math.imul(ah7, bl0)) | 0; + hi = Math.imul(ah7, bh0); + lo = (lo + Math.imul(al6, bl1)) | 0; + mid = (mid + Math.imul(al6, bh1)) | 0; + mid = (mid + Math.imul(ah6, bl1)) | 0; + hi = (hi + Math.imul(ah6, bh1)) | 0; + lo = (lo + Math.imul(al5, bl2)) | 0; + mid = (mid + Math.imul(al5, bh2)) | 0; + mid = (mid + Math.imul(ah5, bl2)) | 0; + hi = (hi + Math.imul(ah5, bh2)) | 0; + lo = (lo + Math.imul(al4, bl3)) | 0; + mid = (mid + Math.imul(al4, bh3)) | 0; + mid = (mid + Math.imul(ah4, bl3)) | 0; + hi = (hi + Math.imul(ah4, bh3)) | 0; + lo = (lo + Math.imul(al3, bl4)) | 0; + mid = (mid + Math.imul(al3, bh4)) | 0; + mid = (mid + Math.imul(ah3, bl4)) | 0; + hi = (hi + Math.imul(ah3, bh4)) | 0; + lo = (lo + Math.imul(al2, bl5)) | 0; + mid = (mid + Math.imul(al2, bh5)) | 0; + mid = (mid + Math.imul(ah2, bl5)) | 0; + hi = (hi + Math.imul(ah2, bh5)) | 0; + lo = (lo + Math.imul(al1, bl6)) | 0; + mid = (mid + Math.imul(al1, bh6)) | 0; + mid = (mid + Math.imul(ah1, bl6)) | 0; + hi = (hi + Math.imul(ah1, bh6)) | 0; + lo = (lo + Math.imul(al0, bl7)) | 0; + mid = (mid + Math.imul(al0, bh7)) | 0; + mid = (mid + Math.imul(ah0, bl7)) | 0; + hi = (hi + Math.imul(ah0, bh7)) | 0; + var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; + w7 &= 0x3ffffff; + /* k = 8 */ + lo = Math.imul(al8, bl0); + mid = Math.imul(al8, bh0); + mid = (mid + Math.imul(ah8, bl0)) | 0; + hi = Math.imul(ah8, bh0); + lo = (lo + Math.imul(al7, bl1)) | 0; + mid = (mid + Math.imul(al7, bh1)) | 0; + mid = (mid + Math.imul(ah7, bl1)) | 0; + hi = (hi + Math.imul(ah7, bh1)) | 0; + lo = (lo + Math.imul(al6, bl2)) | 0; + mid = (mid + Math.imul(al6, bh2)) | 0; + mid = (mid + Math.imul(ah6, bl2)) | 0; + hi = (hi + Math.imul(ah6, bh2)) | 0; + lo = (lo + Math.imul(al5, bl3)) | 0; + mid = (mid + Math.imul(al5, bh3)) | 0; + mid = (mid + Math.imul(ah5, bl3)) | 0; + hi = (hi + Math.imul(ah5, bh3)) | 0; + lo = (lo + Math.imul(al4, bl4)) | 0; + mid = (mid + Math.imul(al4, bh4)) | 0; + mid = (mid + Math.imul(ah4, bl4)) | 0; + hi = (hi + Math.imul(ah4, bh4)) | 0; + lo = (lo + Math.imul(al3, bl5)) | 0; + mid = (mid + Math.imul(al3, bh5)) | 0; + mid = (mid + Math.imul(ah3, bl5)) | 0; + hi = (hi + Math.imul(ah3, bh5)) | 0; + lo = (lo + Math.imul(al2, bl6)) | 0; + mid = (mid + Math.imul(al2, bh6)) | 0; + mid = (mid + Math.imul(ah2, bl6)) | 0; + hi = (hi + Math.imul(ah2, bh6)) | 0; + lo = (lo + Math.imul(al1, bl7)) | 0; + mid = (mid + Math.imul(al1, bh7)) | 0; + mid = (mid + Math.imul(ah1, bl7)) | 0; + hi = (hi + Math.imul(ah1, bh7)) | 0; + lo = (lo + Math.imul(al0, bl8)) | 0; + mid = (mid + Math.imul(al0, bh8)) | 0; + mid = (mid + Math.imul(ah0, bl8)) | 0; + hi = (hi + Math.imul(ah0, bh8)) | 0; + var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; + w8 &= 0x3ffffff; + /* k = 9 */ + lo = Math.imul(al9, bl0); + mid = Math.imul(al9, bh0); + mid = (mid + Math.imul(ah9, bl0)) | 0; + hi = Math.imul(ah9, bh0); + lo = (lo + Math.imul(al8, bl1)) | 0; + mid = (mid + Math.imul(al8, bh1)) | 0; + mid = (mid + Math.imul(ah8, bl1)) | 0; + hi = (hi + Math.imul(ah8, bh1)) | 0; + lo = (lo + Math.imul(al7, bl2)) | 0; + mid = (mid + Math.imul(al7, bh2)) | 0; + mid = (mid + Math.imul(ah7, bl2)) | 0; + hi = (hi + Math.imul(ah7, bh2)) | 0; + lo = (lo + Math.imul(al6, bl3)) | 0; + mid = (mid + Math.imul(al6, bh3)) | 0; + mid = (mid + Math.imul(ah6, bl3)) | 0; + hi = (hi + Math.imul(ah6, bh3)) | 0; + lo = (lo + Math.imul(al5, bl4)) | 0; + mid = (mid + Math.imul(al5, bh4)) | 0; + mid = (mid + Math.imul(ah5, bl4)) | 0; + hi = (hi + Math.imul(ah5, bh4)) | 0; + lo = (lo + Math.imul(al4, bl5)) | 0; + mid = (mid + Math.imul(al4, bh5)) | 0; + mid = (mid + Math.imul(ah4, bl5)) | 0; + hi = (hi + Math.imul(ah4, bh5)) | 0; + lo = (lo + Math.imul(al3, bl6)) | 0; + mid = (mid + Math.imul(al3, bh6)) | 0; + mid = (mid + Math.imul(ah3, bl6)) | 0; + hi = (hi + Math.imul(ah3, bh6)) | 0; + lo = (lo + Math.imul(al2, bl7)) | 0; + mid = (mid + Math.imul(al2, bh7)) | 0; + mid = (mid + Math.imul(ah2, bl7)) | 0; + hi = (hi + Math.imul(ah2, bh7)) | 0; + lo = (lo + Math.imul(al1, bl8)) | 0; + mid = (mid + Math.imul(al1, bh8)) | 0; + mid = (mid + Math.imul(ah1, bl8)) | 0; + hi = (hi + Math.imul(ah1, bh8)) | 0; + lo = (lo + Math.imul(al0, bl9)) | 0; + mid = (mid + Math.imul(al0, bh9)) | 0; + mid = (mid + Math.imul(ah0, bl9)) | 0; + hi = (hi + Math.imul(ah0, bh9)) | 0; + var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; + w9 &= 0x3ffffff; + /* k = 10 */ + lo = Math.imul(al9, bl1); + mid = Math.imul(al9, bh1); + mid = (mid + Math.imul(ah9, bl1)) | 0; + hi = Math.imul(ah9, bh1); + lo = (lo + Math.imul(al8, bl2)) | 0; + mid = (mid + Math.imul(al8, bh2)) | 0; + mid = (mid + Math.imul(ah8, bl2)) | 0; + hi = (hi + Math.imul(ah8, bh2)) | 0; + lo = (lo + Math.imul(al7, bl3)) | 0; + mid = (mid + Math.imul(al7, bh3)) | 0; + mid = (mid + Math.imul(ah7, bl3)) | 0; + hi = (hi + Math.imul(ah7, bh3)) | 0; + lo = (lo + Math.imul(al6, bl4)) | 0; + mid = (mid + Math.imul(al6, bh4)) | 0; + mid = (mid + Math.imul(ah6, bl4)) | 0; + hi = (hi + Math.imul(ah6, bh4)) | 0; + lo = (lo + Math.imul(al5, bl5)) | 0; + mid = (mid + Math.imul(al5, bh5)) | 0; + mid = (mid + Math.imul(ah5, bl5)) | 0; + hi = (hi + Math.imul(ah5, bh5)) | 0; + lo = (lo + Math.imul(al4, bl6)) | 0; + mid = (mid + Math.imul(al4, bh6)) | 0; + mid = (mid + Math.imul(ah4, bl6)) | 0; + hi = (hi + Math.imul(ah4, bh6)) | 0; + lo = (lo + Math.imul(al3, bl7)) | 0; + mid = (mid + Math.imul(al3, bh7)) | 0; + mid = (mid + Math.imul(ah3, bl7)) | 0; + hi = (hi + Math.imul(ah3, bh7)) | 0; + lo = (lo + Math.imul(al2, bl8)) | 0; + mid = (mid + Math.imul(al2, bh8)) | 0; + mid = (mid + Math.imul(ah2, bl8)) | 0; + hi = (hi + Math.imul(ah2, bh8)) | 0; + lo = (lo + Math.imul(al1, bl9)) | 0; + mid = (mid + Math.imul(al1, bh9)) | 0; + mid = (mid + Math.imul(ah1, bl9)) | 0; + hi = (hi + Math.imul(ah1, bh9)) | 0; + var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; + w10 &= 0x3ffffff; + /* k = 11 */ + lo = Math.imul(al9, bl2); + mid = Math.imul(al9, bh2); + mid = (mid + Math.imul(ah9, bl2)) | 0; + hi = Math.imul(ah9, bh2); + lo = (lo + Math.imul(al8, bl3)) | 0; + mid = (mid + Math.imul(al8, bh3)) | 0; + mid = (mid + Math.imul(ah8, bl3)) | 0; + hi = (hi + Math.imul(ah8, bh3)) | 0; + lo = (lo + Math.imul(al7, bl4)) | 0; + mid = (mid + Math.imul(al7, bh4)) | 0; + mid = (mid + Math.imul(ah7, bl4)) | 0; + hi = (hi + Math.imul(ah7, bh4)) | 0; + lo = (lo + Math.imul(al6, bl5)) | 0; + mid = (mid + Math.imul(al6, bh5)) | 0; + mid = (mid + Math.imul(ah6, bl5)) | 0; + hi = (hi + Math.imul(ah6, bh5)) | 0; + lo = (lo + Math.imul(al5, bl6)) | 0; + mid = (mid + Math.imul(al5, bh6)) | 0; + mid = (mid + Math.imul(ah5, bl6)) | 0; + hi = (hi + Math.imul(ah5, bh6)) | 0; + lo = (lo + Math.imul(al4, bl7)) | 0; + mid = (mid + Math.imul(al4, bh7)) | 0; + mid = (mid + Math.imul(ah4, bl7)) | 0; + hi = (hi + Math.imul(ah4, bh7)) | 0; + lo = (lo + Math.imul(al3, bl8)) | 0; + mid = (mid + Math.imul(al3, bh8)) | 0; + mid = (mid + Math.imul(ah3, bl8)) | 0; + hi = (hi + Math.imul(ah3, bh8)) | 0; + lo = (lo + Math.imul(al2, bl9)) | 0; + mid = (mid + Math.imul(al2, bh9)) | 0; + mid = (mid + Math.imul(ah2, bl9)) | 0; + hi = (hi + Math.imul(ah2, bh9)) | 0; + var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; + w11 &= 0x3ffffff; + /* k = 12 */ + lo = Math.imul(al9, bl3); + mid = Math.imul(al9, bh3); + mid = (mid + Math.imul(ah9, bl3)) | 0; + hi = Math.imul(ah9, bh3); + lo = (lo + Math.imul(al8, bl4)) | 0; + mid = (mid + Math.imul(al8, bh4)) | 0; + mid = (mid + Math.imul(ah8, bl4)) | 0; + hi = (hi + Math.imul(ah8, bh4)) | 0; + lo = (lo + Math.imul(al7, bl5)) | 0; + mid = (mid + Math.imul(al7, bh5)) | 0; + mid = (mid + Math.imul(ah7, bl5)) | 0; + hi = (hi + Math.imul(ah7, bh5)) | 0; + lo = (lo + Math.imul(al6, bl6)) | 0; + mid = (mid + Math.imul(al6, bh6)) | 0; + mid = (mid + Math.imul(ah6, bl6)) | 0; + hi = (hi + Math.imul(ah6, bh6)) | 0; + lo = (lo + Math.imul(al5, bl7)) | 0; + mid = (mid + Math.imul(al5, bh7)) | 0; + mid = (mid + Math.imul(ah5, bl7)) | 0; + hi = (hi + Math.imul(ah5, bh7)) | 0; + lo = (lo + Math.imul(al4, bl8)) | 0; + mid = (mid + Math.imul(al4, bh8)) | 0; + mid = (mid + Math.imul(ah4, bl8)) | 0; + hi = (hi + Math.imul(ah4, bh8)) | 0; + lo = (lo + Math.imul(al3, bl9)) | 0; + mid = (mid + Math.imul(al3, bh9)) | 0; + mid = (mid + Math.imul(ah3, bl9)) | 0; + hi = (hi + Math.imul(ah3, bh9)) | 0; + var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; + w12 &= 0x3ffffff; + /* k = 13 */ + lo = Math.imul(al9, bl4); + mid = Math.imul(al9, bh4); + mid = (mid + Math.imul(ah9, bl4)) | 0; + hi = Math.imul(ah9, bh4); + lo = (lo + Math.imul(al8, bl5)) | 0; + mid = (mid + Math.imul(al8, bh5)) | 0; + mid = (mid + Math.imul(ah8, bl5)) | 0; + hi = (hi + Math.imul(ah8, bh5)) | 0; + lo = (lo + Math.imul(al7, bl6)) | 0; + mid = (mid + Math.imul(al7, bh6)) | 0; + mid = (mid + Math.imul(ah7, bl6)) | 0; + hi = (hi + Math.imul(ah7, bh6)) | 0; + lo = (lo + Math.imul(al6, bl7)) | 0; + mid = (mid + Math.imul(al6, bh7)) | 0; + mid = (mid + Math.imul(ah6, bl7)) | 0; + hi = (hi + Math.imul(ah6, bh7)) | 0; + lo = (lo + Math.imul(al5, bl8)) | 0; + mid = (mid + Math.imul(al5, bh8)) | 0; + mid = (mid + Math.imul(ah5, bl8)) | 0; + hi = (hi + Math.imul(ah5, bh8)) | 0; + lo = (lo + Math.imul(al4, bl9)) | 0; + mid = (mid + Math.imul(al4, bh9)) | 0; + mid = (mid + Math.imul(ah4, bl9)) | 0; + hi = (hi + Math.imul(ah4, bh9)) | 0; + var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; + w13 &= 0x3ffffff; + /* k = 14 */ + lo = Math.imul(al9, bl5); + mid = Math.imul(al9, bh5); + mid = (mid + Math.imul(ah9, bl5)) | 0; + hi = Math.imul(ah9, bh5); + lo = (lo + Math.imul(al8, bl6)) | 0; + mid = (mid + Math.imul(al8, bh6)) | 0; + mid = (mid + Math.imul(ah8, bl6)) | 0; + hi = (hi + Math.imul(ah8, bh6)) | 0; + lo = (lo + Math.imul(al7, bl7)) | 0; + mid = (mid + Math.imul(al7, bh7)) | 0; + mid = (mid + Math.imul(ah7, bl7)) | 0; + hi = (hi + Math.imul(ah7, bh7)) | 0; + lo = (lo + Math.imul(al6, bl8)) | 0; + mid = (mid + Math.imul(al6, bh8)) | 0; + mid = (mid + Math.imul(ah6, bl8)) | 0; + hi = (hi + Math.imul(ah6, bh8)) | 0; + lo = (lo + Math.imul(al5, bl9)) | 0; + mid = (mid + Math.imul(al5, bh9)) | 0; + mid = (mid + Math.imul(ah5, bl9)) | 0; + hi = (hi + Math.imul(ah5, bh9)) | 0; + var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; + w14 &= 0x3ffffff; + /* k = 15 */ + lo = Math.imul(al9, bl6); + mid = Math.imul(al9, bh6); + mid = (mid + Math.imul(ah9, bl6)) | 0; + hi = Math.imul(ah9, bh6); + lo = (lo + Math.imul(al8, bl7)) | 0; + mid = (mid + Math.imul(al8, bh7)) | 0; + mid = (mid + Math.imul(ah8, bl7)) | 0; + hi = (hi + Math.imul(ah8, bh7)) | 0; + lo = (lo + Math.imul(al7, bl8)) | 0; + mid = (mid + Math.imul(al7, bh8)) | 0; + mid = (mid + Math.imul(ah7, bl8)) | 0; + hi = (hi + Math.imul(ah7, bh8)) | 0; + lo = (lo + Math.imul(al6, bl9)) | 0; + mid = (mid + Math.imul(al6, bh9)) | 0; + mid = (mid + Math.imul(ah6, bl9)) | 0; + hi = (hi + Math.imul(ah6, bh9)) | 0; + var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; + w15 &= 0x3ffffff; + /* k = 16 */ + lo = Math.imul(al9, bl7); + mid = Math.imul(al9, bh7); + mid = (mid + Math.imul(ah9, bl7)) | 0; + hi = Math.imul(ah9, bh7); + lo = (lo + Math.imul(al8, bl8)) | 0; + mid = (mid + Math.imul(al8, bh8)) | 0; + mid = (mid + Math.imul(ah8, bl8)) | 0; + hi = (hi + Math.imul(ah8, bh8)) | 0; + lo = (lo + Math.imul(al7, bl9)) | 0; + mid = (mid + Math.imul(al7, bh9)) | 0; + mid = (mid + Math.imul(ah7, bl9)) | 0; + hi = (hi + Math.imul(ah7, bh9)) | 0; + var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; + w16 &= 0x3ffffff; + /* k = 17 */ + lo = Math.imul(al9, bl8); + mid = Math.imul(al9, bh8); + mid = (mid + Math.imul(ah9, bl8)) | 0; + hi = Math.imul(ah9, bh8); + lo = (lo + Math.imul(al8, bl9)) | 0; + mid = (mid + Math.imul(al8, bh9)) | 0; + mid = (mid + Math.imul(ah8, bl9)) | 0; + hi = (hi + Math.imul(ah8, bh9)) | 0; + var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; + w17 &= 0x3ffffff; + /* k = 18 */ + lo = Math.imul(al9, bl9); + mid = Math.imul(al9, bh9); + mid = (mid + Math.imul(ah9, bl9)) | 0; + hi = Math.imul(ah9, bh9); + var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; + w18 &= 0x3ffffff; + o[0] = w0; + o[1] = w1; + o[2] = w2; + o[3] = w3; + o[4] = w4; + o[5] = w5; + o[6] = w6; + o[7] = w7; + o[8] = w8; + o[9] = w9; + o[10] = w10; + o[11] = w11; + o[12] = w12; + o[13] = w13; + o[14] = w14; + o[15] = w15; + o[16] = w16; + o[17] = w17; + o[18] = w18; + if (c !== 0) { + o[19] = c; + out.length++; + } + return out; + }; + + // Polyfill comb + if (!Math.imul) { + comb10MulTo = smallMulTo; + } + + function bigMulTo (self, num, out) { + out.negative = num.negative ^ self.negative; + out.length = self.length + num.length; + + var carry = 0; + var hncarry = 0; + for (var k = 0; k < out.length - 1; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = hncarry; + hncarry = 0; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = k - j; + var a = self.words[i] | 0; + var b = num.words[j] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; + lo = (lo + rword) | 0; + rword = lo & 0x3ffffff; + ncarry = (ncarry + (lo >>> 26)) | 0; + + hncarry += ncarry >>> 26; + ncarry &= 0x3ffffff; + } + out.words[k] = rword; + carry = ncarry; + ncarry = hncarry; + } + if (carry !== 0) { + out.words[k] = carry; + } else { + out.length--; + } + + return out._strip(); + } + + function jumboMulTo (self, num, out) { + // Temporary disable, see https://github.com/indutny/bn.js/issues/211 + // var fftm = new FFTM(); + // return fftm.mulp(self, num, out); + return bigMulTo(self, num, out); + } + + BN.prototype.mulTo = function mulTo (num, out) { + var res; + var len = this.length + num.length; + if (this.length === 10 && num.length === 10) { + res = comb10MulTo(this, num, out); + } else if (len < 63) { + res = smallMulTo(this, num, out); + } else if (len < 1024) { + res = bigMulTo(this, num, out); + } else { + res = jumboMulTo(this, num, out); + } + + return res; + }; + + // Cooley-Tukey algorithm for FFT + // slightly revisited to rely on looping instead of recursion + + function FFTM (x, y) { + this.x = x; + this.y = y; + } + + FFTM.prototype.makeRBT = function makeRBT (N) { + var t = new Array(N); + var l = BN.prototype._countBits(N) - 1; + for (var i = 0; i < N; i++) { + t[i] = this.revBin(i, l, N); + } + + return t; + }; + + // Returns binary-reversed representation of `x` + FFTM.prototype.revBin = function revBin (x, l, N) { + if (x === 0 || x === N - 1) return x; + + var rb = 0; + for (var i = 0; i < l; i++) { + rb |= (x & 1) << (l - i - 1); + x >>= 1; + } + + return rb; + }; + + // Performs "tweedling" phase, therefore 'emulating' + // behaviour of the recursive algorithm + FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { + for (var i = 0; i < N; i++) { + rtws[i] = rws[rbt[i]]; + itws[i] = iws[rbt[i]]; + } + }; + + FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { + this.permute(rbt, rws, iws, rtws, itws, N); + + for (var s = 1; s < N; s <<= 1) { + var l = s << 1; + + var rtwdf = Math.cos(2 * Math.PI / l); + var itwdf = Math.sin(2 * Math.PI / l); + + for (var p = 0; p < N; p += l) { + var rtwdf_ = rtwdf; + var itwdf_ = itwdf; + + for (var j = 0; j < s; j++) { + var re = rtws[p + j]; + var ie = itws[p + j]; + + var ro = rtws[p + j + s]; + var io = itws[p + j + s]; + + var rx = rtwdf_ * ro - itwdf_ * io; + + io = rtwdf_ * io + itwdf_ * ro; + ro = rx; + + rtws[p + j] = re + ro; + itws[p + j] = ie + io; + + rtws[p + j + s] = re - ro; + itws[p + j + s] = ie - io; + + /* jshint maxdepth : false */ + if (j !== l) { + rx = rtwdf * rtwdf_ - itwdf * itwdf_; + + itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; + rtwdf_ = rx; + } + } + } + } + }; + + FFTM.prototype.guessLen13b = function guessLen13b (n, m) { + var N = Math.max(m, n) | 1; + var odd = N & 1; + var i = 0; + for (N = N / 2 | 0; N; N = N >>> 1) { + i++; + } + + return 1 << i + 1 + odd; + }; + + FFTM.prototype.conjugate = function conjugate (rws, iws, N) { + if (N <= 1) return; + + for (var i = 0; i < N / 2; i++) { + var t = rws[i]; + + rws[i] = rws[N - i - 1]; + rws[N - i - 1] = t; + + t = iws[i]; + + iws[i] = -iws[N - i - 1]; + iws[N - i - 1] = -t; + } + }; + + FFTM.prototype.normalize13b = function normalize13b (ws, N) { + var carry = 0; + for (var i = 0; i < N / 2; i++) { + var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + + Math.round(ws[2 * i] / N) + + carry; + + ws[i] = w & 0x3ffffff; + + if (w < 0x4000000) { + carry = 0; + } else { + carry = w / 0x4000000 | 0; + } + } + + return ws; + }; + + FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { + var carry = 0; + for (var i = 0; i < len; i++) { + carry = carry + (ws[i] | 0); + + rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; + rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; + } + + // Pad with zeroes + for (i = 2 * len; i < N; ++i) { + rws[i] = 0; + } + + assert(carry === 0); + assert((carry & ~0x1fff) === 0); + }; + + FFTM.prototype.stub = function stub (N) { + var ph = new Array(N); + for (var i = 0; i < N; i++) { + ph[i] = 0; + } + + return ph; + }; + + FFTM.prototype.mulp = function mulp (x, y, out) { + var N = 2 * this.guessLen13b(x.length, y.length); + + var rbt = this.makeRBT(N); + + var _ = this.stub(N); + + var rws = new Array(N); + var rwst = new Array(N); + var iwst = new Array(N); + + var nrws = new Array(N); + var nrwst = new Array(N); + var niwst = new Array(N); + + var rmws = out.words; + rmws.length = N; + + this.convert13b(x.words, x.length, rws, N); + this.convert13b(y.words, y.length, nrws, N); + + this.transform(rws, _, rwst, iwst, N, rbt); + this.transform(nrws, _, nrwst, niwst, N, rbt); + + for (var i = 0; i < N; i++) { + var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; + iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; + rwst[i] = rx; + } + + this.conjugate(rwst, iwst, N); + this.transform(rwst, iwst, rmws, _, N, rbt); + this.conjugate(rmws, _, N); + this.normalize13b(rmws, N); + + out.negative = x.negative ^ y.negative; + out.length = x.length + y.length; + return out._strip(); + }; + + // Multiply `this` by `num` + BN.prototype.mul = function mul (num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return this.mulTo(num, out); + }; + + // Multiply employing FFT + BN.prototype.mulf = function mulf (num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return jumboMulTo(this, num, out); + }; + + // In-place Multiplication + BN.prototype.imul = function imul (num) { + return this.clone().mulTo(num, this); + }; + + BN.prototype.imuln = function imuln (num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(typeof num === 'number'); + assert(num < 0x4000000); + + // Carry + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = (this.words[i] | 0) * num; + var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); + carry >>= 26; + carry += (w / 0x4000000) | 0; + // NOTE: lo is 27bit maximum + carry += lo >>> 26; + this.words[i] = lo & 0x3ffffff; + } + + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + + return isNegNum ? this.ineg() : this; + }; + + BN.prototype.muln = function muln (num) { + return this.clone().imuln(num); + }; + + // `this` * `this` + BN.prototype.sqr = function sqr () { + return this.mul(this); + }; + + // `this` * `this` in-place + BN.prototype.isqr = function isqr () { + return this.imul(this.clone()); + }; + + // Math.pow(`this`, `num`) + BN.prototype.pow = function pow (num) { + var w = toBitArray(num); + if (w.length === 0) return new BN(1); + + // Skip leading zeroes + var res = this; + for (var i = 0; i < w.length; i++, res = res.sqr()) { + if (w[i] !== 0) break; + } + + if (++i < w.length) { + for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { + if (w[i] === 0) continue; + + res = res.mul(q); + } + } + + return res; + }; + + // Shift-left in-place + BN.prototype.iushln = function iushln (bits) { + assert(typeof bits === 'number' && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); + var i; + + if (r !== 0) { + var carry = 0; + + for (i = 0; i < this.length; i++) { + var newCarry = this.words[i] & carryMask; + var c = ((this.words[i] | 0) - newCarry) << r; + this.words[i] = c | carry; + carry = newCarry >>> (26 - r); + } + + if (carry) { + this.words[i] = carry; + this.length++; + } + } + + if (s !== 0) { + for (i = this.length - 1; i >= 0; i--) { + this.words[i + s] = this.words[i]; + } + + for (i = 0; i < s; i++) { + this.words[i] = 0; + } + + this.length += s; + } + + return this._strip(); + }; + + BN.prototype.ishln = function ishln (bits) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushln(bits); + }; + + // Shift-right in-place + // NOTE: `hint` is a lowest bit before trailing zeroes + // NOTE: if `extended` is present - it will be filled with destroyed bits + BN.prototype.iushrn = function iushrn (bits, hint, extended) { + assert(typeof bits === 'number' && bits >= 0); + var h; + if (hint) { + h = (hint - (hint % 26)) / 26; + } else { + h = 0; + } + + var r = bits % 26; + var s = Math.min((bits - r) / 26, this.length); + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + var maskedWords = extended; + + h -= s; + h = Math.max(0, h); + + // Extended mode, copy masked part + if (maskedWords) { + for (var i = 0; i < s; i++) { + maskedWords.words[i] = this.words[i]; + } + maskedWords.length = s; + } + + if (s === 0) { + // No-op, we should not move anything at all + } else if (this.length > s) { + this.length -= s; + for (i = 0; i < this.length; i++) { + this.words[i] = this.words[i + s]; + } + } else { + this.words[0] = 0; + this.length = 1; + } + + var carry = 0; + for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { + var word = this.words[i] | 0; + this.words[i] = (carry << (26 - r)) | (word >>> r); + carry = word & mask; + } + + // Push carried bits as a mask + if (maskedWords && carry !== 0) { + maskedWords.words[maskedWords.length++] = carry; + } + + if (this.length === 0) { + this.words[0] = 0; + this.length = 1; + } + + return this._strip(); + }; + + BN.prototype.ishrn = function ishrn (bits, hint, extended) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushrn(bits, hint, extended); + }; + + // Shift-left + BN.prototype.shln = function shln (bits) { + return this.clone().ishln(bits); + }; + + BN.prototype.ushln = function ushln (bits) { + return this.clone().iushln(bits); + }; + + // Shift-right + BN.prototype.shrn = function shrn (bits) { + return this.clone().ishrn(bits); + }; + + BN.prototype.ushrn = function ushrn (bits) { + return this.clone().iushrn(bits); + }; + + // Test if n bit is set + BN.prototype.testn = function testn (bit) { + assert(typeof bit === 'number' && bit >= 0); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) return false; + + // Check bit and return + var w = this.words[s]; + + return !!(w & q); + }; + + // Return only lowers bits of number (in-place) + BN.prototype.imaskn = function imaskn (bits) { + assert(typeof bits === 'number' && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + + assert(this.negative === 0, 'imaskn works only with positive numbers'); + + if (this.length <= s) { + return this; + } + + if (r !== 0) { + s++; + } + this.length = Math.min(s, this.length); + + if (r !== 0) { + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + this.words[this.length - 1] &= mask; + } + + return this._strip(); + }; + + // Return only lowers bits of number + BN.prototype.maskn = function maskn (bits) { + return this.clone().imaskn(bits); + }; + + // Add plain number `num` to `this` + BN.prototype.iaddn = function iaddn (num) { + assert(typeof num === 'number'); + assert(num < 0x4000000); + if (num < 0) return this.isubn(-num); + + // Possible sign change + if (this.negative !== 0) { + if (this.length === 1 && (this.words[0] | 0) <= num) { + this.words[0] = num - (this.words[0] | 0); + this.negative = 0; + return this; + } + + this.negative = 0; + this.isubn(num); + this.negative = 1; + return this; + } + + // Add without checks + return this._iaddn(num); + }; + + BN.prototype._iaddn = function _iaddn (num) { + this.words[0] += num; + + // Carry + for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { + this.words[i] -= 0x4000000; + if (i === this.length - 1) { + this.words[i + 1] = 1; + } else { + this.words[i + 1]++; + } + } + this.length = Math.max(this.length, i + 1); + + return this; + }; + + // Subtract plain number `num` from `this` + BN.prototype.isubn = function isubn (num) { + assert(typeof num === 'number'); + assert(num < 0x4000000); + if (num < 0) return this.iaddn(-num); + + if (this.negative !== 0) { + this.negative = 0; + this.iaddn(num); + this.negative = 1; + return this; + } + + this.words[0] -= num; + + if (this.length === 1 && this.words[0] < 0) { + this.words[0] = -this.words[0]; + this.negative = 1; + } else { + // Carry + for (var i = 0; i < this.length && this.words[i] < 0; i++) { + this.words[i] += 0x4000000; + this.words[i + 1] -= 1; + } + } + + return this._strip(); + }; + + BN.prototype.addn = function addn (num) { + return this.clone().iaddn(num); + }; + + BN.prototype.subn = function subn (num) { + return this.clone().isubn(num); + }; + + BN.prototype.iabs = function iabs () { + this.negative = 0; + + return this; + }; + + BN.prototype.abs = function abs () { + return this.clone().iabs(); + }; + + BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { + var len = num.length + shift; + var i; + + this._expand(len); + + var w; + var carry = 0; + for (i = 0; i < num.length; i++) { + w = (this.words[i + shift] | 0) + carry; + var right = (num.words[i] | 0) * mul; + w -= right & 0x3ffffff; + carry = (w >> 26) - ((right / 0x4000000) | 0); + this.words[i + shift] = w & 0x3ffffff; + } + for (; i < this.length - shift; i++) { + w = (this.words[i + shift] | 0) + carry; + carry = w >> 26; + this.words[i + shift] = w & 0x3ffffff; + } + + if (carry === 0) return this._strip(); + + // Subtraction overflow + assert(carry === -1); + carry = 0; + for (i = 0; i < this.length; i++) { + w = -(this.words[i] | 0) + carry; + carry = w >> 26; + this.words[i] = w & 0x3ffffff; + } + this.negative = 1; + + return this._strip(); + }; + + BN.prototype._wordDiv = function _wordDiv (num, mode) { + var shift = this.length - num.length; + + var a = this.clone(); + var b = num; + + // Normalize + var bhi = b.words[b.length - 1] | 0; + var bhiBits = this._countBits(bhi); + shift = 26 - bhiBits; + if (shift !== 0) { + b = b.ushln(shift); + a.iushln(shift); + bhi = b.words[b.length - 1] | 0; + } + + // Initialize quotient + var m = a.length - b.length; + var q; + + if (mode !== 'mod') { + q = new BN(null); + q.length = m + 1; + q.words = new Array(q.length); + for (var i = 0; i < q.length; i++) { + q.words[i] = 0; + } + } + + var diff = a.clone()._ishlnsubmul(b, 1, m); + if (diff.negative === 0) { + a = diff; + if (q) { + q.words[m] = 1; + } + } + + for (var j = m - 1; j >= 0; j--) { + var qj = (a.words[b.length + j] | 0) * 0x4000000 + + (a.words[b.length + j - 1] | 0); + + // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max + // (0x7ffffff) + qj = Math.min((qj / bhi) | 0, 0x3ffffff); + + a._ishlnsubmul(b, qj, j); + while (a.negative !== 0) { + qj--; + a.negative = 0; + a._ishlnsubmul(b, 1, j); + if (!a.isZero()) { + a.negative ^= 1; + } + } + if (q) { + q.words[j] = qj; + } + } + if (q) { + q._strip(); + } + a._strip(); + + // Denormalize + if (mode !== 'div' && shift !== 0) { + a.iushrn(shift); + } + + return { + div: q || null, + mod: a + }; + }; + + // NOTE: 1) `mode` can be set to `mod` to request mod only, + // to `div` to request div only, or be absent to + // request both div & mod + // 2) `positive` is true if unsigned mod is requested + BN.prototype.divmod = function divmod (num, mode, positive) { + assert(!num.isZero()); + + if (this.isZero()) { + return { + div: new BN(0), + mod: new BN(0) + }; + } + + var div, mod, res; + if (this.negative !== 0 && num.negative === 0) { + res = this.neg().divmod(num, mode); + + if (mode !== 'mod') { + div = res.div.neg(); + } + + if (mode !== 'div') { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.iadd(num); + } + } + + return { + div: div, + mod: mod + }; + } + + if (this.negative === 0 && num.negative !== 0) { + res = this.divmod(num.neg(), mode); + + if (mode !== 'mod') { + div = res.div.neg(); + } + + return { + div: div, + mod: res.mod + }; + } + + if ((this.negative & num.negative) !== 0) { + res = this.neg().divmod(num.neg(), mode); + + if (mode !== 'div') { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.isub(num); + } + } + + return { + div: res.div, + mod: mod + }; + } + + // Both numbers are positive at this point + + // Strip both numbers to approximate shift value + if (num.length > this.length || this.cmp(num) < 0) { + return { + div: new BN(0), + mod: this + }; + } + + // Very short reduction + if (num.length === 1) { + if (mode === 'div') { + return { + div: this.divn(num.words[0]), + mod: null + }; + } + + if (mode === 'mod') { + return { + div: null, + mod: new BN(this.modrn(num.words[0])) + }; + } + + return { + div: this.divn(num.words[0]), + mod: new BN(this.modrn(num.words[0])) + }; + } + + return this._wordDiv(num, mode); + }; + + // Find `this` / `num` + BN.prototype.div = function div (num) { + return this.divmod(num, 'div', false).div; + }; + + // Find `this` % `num` + BN.prototype.mod = function mod (num) { + return this.divmod(num, 'mod', false).mod; + }; + + BN.prototype.umod = function umod (num) { + return this.divmod(num, 'mod', true).mod; + }; + + // Find Round(`this` / `num`) + BN.prototype.divRound = function divRound (num) { + var dm = this.divmod(num); + + // Fast case - exact division + if (dm.mod.isZero()) return dm.div; + + var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; + + var half = num.ushrn(1); + var r2 = num.andln(1); + var cmp = mod.cmp(half); + + // Round down + if (cmp < 0 || (r2 === 1 && cmp === 0)) return dm.div; + + // Round up + return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); + }; + + BN.prototype.modrn = function modrn (num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(num <= 0x3ffffff); + var p = (1 << 26) % num; + + var acc = 0; + for (var i = this.length - 1; i >= 0; i--) { + acc = (p * acc + (this.words[i] | 0)) % num; + } + + return isNegNum ? -acc : acc; + }; + + // WARNING: DEPRECATED + BN.prototype.modn = function modn (num) { + return this.modrn(num); + }; + + // In-place division by number + BN.prototype.idivn = function idivn (num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(num <= 0x3ffffff); + + var carry = 0; + for (var i = this.length - 1; i >= 0; i--) { + var w = (this.words[i] | 0) + carry * 0x4000000; + this.words[i] = (w / num) | 0; + carry = w % num; + } + + this._strip(); + return isNegNum ? this.ineg() : this; + }; + + BN.prototype.divn = function divn (num) { + return this.clone().idivn(num); + }; + + BN.prototype.egcd = function egcd (p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var x = this; + var y = p.clone(); + + if (x.negative !== 0) { + x = x.umod(p); + } else { + x = x.clone(); + } + + // A * x + B * y = x + var A = new BN(1); + var B = new BN(0); + + // C * x + D * y = y + var C = new BN(0); + var D = new BN(1); + + var g = 0; + + while (x.isEven() && y.isEven()) { + x.iushrn(1); + y.iushrn(1); + ++g; + } + + var yp = y.clone(); + var xp = x.clone(); + + while (!x.isZero()) { + for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + x.iushrn(i); + while (i-- > 0) { + if (A.isOdd() || B.isOdd()) { + A.iadd(yp); + B.isub(xp); + } + + A.iushrn(1); + B.iushrn(1); + } + } + + for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + y.iushrn(j); + while (j-- > 0) { + if (C.isOdd() || D.isOdd()) { + C.iadd(yp); + D.isub(xp); + } + + C.iushrn(1); + D.iushrn(1); + } + } + + if (x.cmp(y) >= 0) { + x.isub(y); + A.isub(C); + B.isub(D); + } else { + y.isub(x); + C.isub(A); + D.isub(B); + } + } + + return { + a: C, + b: D, + gcd: y.iushln(g) + }; + }; + + // This is reduced incarnation of the binary EEA + // above, designated to invert members of the + // _prime_ fields F(p) at a maximal speed + BN.prototype._invmp = function _invmp (p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var a = this; + var b = p.clone(); + + if (a.negative !== 0) { + a = a.umod(p); + } else { + a = a.clone(); + } + + var x1 = new BN(1); + var x2 = new BN(0); + + var delta = b.clone(); + + while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { + for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + a.iushrn(i); + while (i-- > 0) { + if (x1.isOdd()) { + x1.iadd(delta); + } + + x1.iushrn(1); + } + } + + for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + b.iushrn(j); + while (j-- > 0) { + if (x2.isOdd()) { + x2.iadd(delta); + } + + x2.iushrn(1); + } + } + + if (a.cmp(b) >= 0) { + a.isub(b); + x1.isub(x2); + } else { + b.isub(a); + x2.isub(x1); + } + } + + var res; + if (a.cmpn(1) === 0) { + res = x1; + } else { + res = x2; + } + + if (res.cmpn(0) < 0) { + res.iadd(p); + } + + return res; + }; + + BN.prototype.gcd = function gcd (num) { + if (this.isZero()) return num.abs(); + if (num.isZero()) return this.abs(); + + var a = this.clone(); + var b = num.clone(); + a.negative = 0; + b.negative = 0; + + // Remove common factor of two + for (var shift = 0; a.isEven() && b.isEven(); shift++) { + a.iushrn(1); + b.iushrn(1); + } + + do { + while (a.isEven()) { + a.iushrn(1); + } + while (b.isEven()) { + b.iushrn(1); + } + + var r = a.cmp(b); + if (r < 0) { + // Swap `a` and `b` to make `a` always bigger than `b` + var t = a; + a = b; + b = t; + } else if (r === 0 || b.cmpn(1) === 0) { + break; + } + + a.isub(b); + } while (true); + + return b.iushln(shift); + }; + + // Invert number in the field F(num) + BN.prototype.invm = function invm (num) { + return this.egcd(num).a.umod(num); + }; + + BN.prototype.isEven = function isEven () { + return (this.words[0] & 1) === 0; + }; + + BN.prototype.isOdd = function isOdd () { + return (this.words[0] & 1) === 1; + }; + + // And first word and num + BN.prototype.andln = function andln (num) { + return this.words[0] & num; + }; + + // Increment at the bit position in-line + BN.prototype.bincn = function bincn (bit) { + assert(typeof bit === 'number'); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) { + this._expand(s + 1); + this.words[s] |= q; + return this; + } + + // Add bit and propagate, if needed + var carry = q; + for (var i = s; carry !== 0 && i < this.length; i++) { + var w = this.words[i] | 0; + w += carry; + carry = w >>> 26; + w &= 0x3ffffff; + this.words[i] = w; + } + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + return this; + }; + + BN.prototype.isZero = function isZero () { + return this.length === 1 && this.words[0] === 0; + }; + + BN.prototype.cmpn = function cmpn (num) { + var negative = num < 0; + + if (this.negative !== 0 && !negative) return -1; + if (this.negative === 0 && negative) return 1; + + this._strip(); + + var res; + if (this.length > 1) { + res = 1; + } else { + if (negative) { + num = -num; + } + + assert(num <= 0x3ffffff, 'Number is too big'); + + var w = this.words[0] | 0; + res = w === num ? 0 : w < num ? -1 : 1; + } + if (this.negative !== 0) return -res | 0; + return res; + }; + + // Compare two numbers and return: + // 1 - if `this` > `num` + // 0 - if `this` == `num` + // -1 - if `this` < `num` + BN.prototype.cmp = function cmp (num) { + if (this.negative !== 0 && num.negative === 0) return -1; + if (this.negative === 0 && num.negative !== 0) return 1; + + var res = this.ucmp(num); + if (this.negative !== 0) return -res | 0; + return res; + }; + + // Unsigned comparison + BN.prototype.ucmp = function ucmp (num) { + // At this point both numbers have the same sign + if (this.length > num.length) return 1; + if (this.length < num.length) return -1; + + var res = 0; + for (var i = this.length - 1; i >= 0; i--) { + var a = this.words[i] | 0; + var b = num.words[i] | 0; + + if (a === b) continue; + if (a < b) { + res = -1; + } else if (a > b) { + res = 1; + } + break; + } + return res; + }; + + BN.prototype.gtn = function gtn (num) { + return this.cmpn(num) === 1; + }; + + BN.prototype.gt = function gt (num) { + return this.cmp(num) === 1; + }; + + BN.prototype.gten = function gten (num) { + return this.cmpn(num) >= 0; + }; + + BN.prototype.gte = function gte (num) { + return this.cmp(num) >= 0; + }; + + BN.prototype.ltn = function ltn (num) { + return this.cmpn(num) === -1; + }; + + BN.prototype.lt = function lt (num) { + return this.cmp(num) === -1; + }; + + BN.prototype.lten = function lten (num) { + return this.cmpn(num) <= 0; + }; + + BN.prototype.lte = function lte (num) { + return this.cmp(num) <= 0; + }; + + BN.prototype.eqn = function eqn (num) { + return this.cmpn(num) === 0; + }; + + BN.prototype.eq = function eq (num) { + return this.cmp(num) === 0; + }; + + // + // A reduce context, could be using montgomery or something better, depending + // on the `m` itself. + // + BN.red = function red (num) { + return new Red(num); + }; + + BN.prototype.toRed = function toRed (ctx) { + assert(!this.red, 'Already a number in reduction context'); + assert(this.negative === 0, 'red works only with positives'); + return ctx.convertTo(this)._forceRed(ctx); + }; + + BN.prototype.fromRed = function fromRed () { + assert(this.red, 'fromRed works only with numbers in reduction context'); + return this.red.convertFrom(this); + }; + + BN.prototype._forceRed = function _forceRed (ctx) { + this.red = ctx; + return this; + }; + + BN.prototype.forceRed = function forceRed (ctx) { + assert(!this.red, 'Already a number in reduction context'); + return this._forceRed(ctx); + }; + + BN.prototype.redAdd = function redAdd (num) { + assert(this.red, 'redAdd works only with red numbers'); + return this.red.add(this, num); + }; + + BN.prototype.redIAdd = function redIAdd (num) { + assert(this.red, 'redIAdd works only with red numbers'); + return this.red.iadd(this, num); + }; + + BN.prototype.redSub = function redSub (num) { + assert(this.red, 'redSub works only with red numbers'); + return this.red.sub(this, num); + }; + + BN.prototype.redISub = function redISub (num) { + assert(this.red, 'redISub works only with red numbers'); + return this.red.isub(this, num); + }; + + BN.prototype.redShl = function redShl (num) { + assert(this.red, 'redShl works only with red numbers'); + return this.red.shl(this, num); + }; + + BN.prototype.redMul = function redMul (num) { + assert(this.red, 'redMul works only with red numbers'); + this.red._verify2(this, num); + return this.red.mul(this, num); + }; + + BN.prototype.redIMul = function redIMul (num) { + assert(this.red, 'redMul works only with red numbers'); + this.red._verify2(this, num); + return this.red.imul(this, num); + }; + + BN.prototype.redSqr = function redSqr () { + assert(this.red, 'redSqr works only with red numbers'); + this.red._verify1(this); + return this.red.sqr(this); + }; + + BN.prototype.redISqr = function redISqr () { + assert(this.red, 'redISqr works only with red numbers'); + this.red._verify1(this); + return this.red.isqr(this); + }; + + // Square root over p + BN.prototype.redSqrt = function redSqrt () { + assert(this.red, 'redSqrt works only with red numbers'); + this.red._verify1(this); + return this.red.sqrt(this); + }; + + BN.prototype.redInvm = function redInvm () { + assert(this.red, 'redInvm works only with red numbers'); + this.red._verify1(this); + return this.red.invm(this); + }; + + // Return negative clone of `this` % `red modulo` + BN.prototype.redNeg = function redNeg () { + assert(this.red, 'redNeg works only with red numbers'); + this.red._verify1(this); + return this.red.neg(this); + }; + + BN.prototype.redPow = function redPow (num) { + assert(this.red && !num.red, 'redPow(normalNum)'); + this.red._verify1(this); + return this.red.pow(this, num); + }; + + // Prime numbers with efficient reduction + var primes = { + k256: null, + p224: null, + p192: null, + p25519: null + }; + + // Pseudo-Mersenne prime + function MPrime (name, p) { + // P = 2 ^ N - K + this.name = name; + this.p = new BN(p, 16); + this.n = this.p.bitLength(); + this.k = new BN(1).iushln(this.n).isub(this.p); + + this.tmp = this._tmp(); + } + + MPrime.prototype._tmp = function _tmp () { + var tmp = new BN(null); + tmp.words = new Array(Math.ceil(this.n / 13)); + return tmp; + }; + + MPrime.prototype.ireduce = function ireduce (num) { + // Assumes that `num` is less than `P^2` + // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) + var r = num; + var rlen; + + do { + this.split(r, this.tmp); + r = this.imulK(r); + r = r.iadd(this.tmp); + rlen = r.bitLength(); + } while (rlen > this.n); + + var cmp = rlen < this.n ? -1 : r.ucmp(this.p); + if (cmp === 0) { + r.words[0] = 0; + r.length = 1; + } else if (cmp > 0) { + r.isub(this.p); + } else { + r._strip(); + } + + return r; + }; + + MPrime.prototype.split = function split (input, out) { + input.iushrn(this.n, 0, out); + }; + + MPrime.prototype.imulK = function imulK (num) { + return num.imul(this.k); + }; + + function K256 () { + MPrime.call( + this, + 'k256', + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); + } + inherits(K256, MPrime); + + K256.prototype.split = function split (input, output) { + // 256 = 9 * 26 + 22 + var mask = 0x3fffff; + + var outLen = Math.min(input.length, 9); + for (var i = 0; i < outLen; i++) { + output.words[i] = input.words[i]; + } + output.length = outLen; + + if (input.length <= 9) { + input.words[0] = 0; + input.length = 1; + return; + } + + // Shift by 9 limbs + var prev = input.words[9]; + output.words[output.length++] = prev & mask; + + for (i = 10; i < input.length; i++) { + var next = input.words[i] | 0; + input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); + prev = next; + } + prev >>>= 22; + input.words[i - 10] = prev; + if (prev === 0 && input.length > 10) { + input.length -= 10; + } else { + input.length -= 9; + } + }; + + K256.prototype.imulK = function imulK (num) { + // K = 0x1000003d1 = [ 0x40, 0x3d1 ] + num.words[num.length] = 0; + num.words[num.length + 1] = 0; + num.length += 2; + + // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 + var lo = 0; + for (var i = 0; i < num.length; i++) { + var w = num.words[i] | 0; + lo += w * 0x3d1; + num.words[i] = lo & 0x3ffffff; + lo = w * 0x40 + ((lo / 0x4000000) | 0); + } + + // Fast length reduction + if (num.words[num.length - 1] === 0) { + num.length--; + if (num.words[num.length - 1] === 0) { + num.length--; + } + } + return num; + }; + + function P224 () { + MPrime.call( + this, + 'p224', + 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); + } + inherits(P224, MPrime); + + function P192 () { + MPrime.call( + this, + 'p192', + 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); + } + inherits(P192, MPrime); + + function P25519 () { + // 2 ^ 255 - 19 + MPrime.call( + this, + '25519', + '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); + } + inherits(P25519, MPrime); + + P25519.prototype.imulK = function imulK (num) { + // K = 0x13 + var carry = 0; + for (var i = 0; i < num.length; i++) { + var hi = (num.words[i] | 0) * 0x13 + carry; + var lo = hi & 0x3ffffff; + hi >>>= 26; + + num.words[i] = lo; + carry = hi; + } + if (carry !== 0) { + num.words[num.length++] = carry; + } + return num; + }; + + // Exported mostly for testing purposes, use plain name instead + BN._prime = function prime (name) { + // Cached version of prime + if (primes[name]) return primes[name]; + + var prime; + if (name === 'k256') { + prime = new K256(); + } else if (name === 'p224') { + prime = new P224(); + } else if (name === 'p192') { + prime = new P192(); + } else if (name === 'p25519') { + prime = new P25519(); + } else { + throw new Error('Unknown prime ' + name); + } + primes[name] = prime; + + return prime; + }; + + // + // Base reduction engine + // + function Red (m) { + if (typeof m === 'string') { + var prime = BN._prime(m); + this.m = prime.p; + this.prime = prime; + } else { + assert(m.gtn(1), 'modulus must be greater than 1'); + this.m = m; + this.prime = null; + } + } + + Red.prototype._verify1 = function _verify1 (a) { + assert(a.negative === 0, 'red works only with positives'); + assert(a.red, 'red works only with red numbers'); + }; + + Red.prototype._verify2 = function _verify2 (a, b) { + assert((a.negative | b.negative) === 0, 'red works only with positives'); + assert(a.red && a.red === b.red, + 'red works only with red numbers'); + }; + + Red.prototype.imod = function imod (a) { + if (this.prime) return this.prime.ireduce(a)._forceRed(this); + + a.umod(this.m)._forceRed(this)._move(a); + return a; + }; + + Red.prototype.neg = function neg (a) { + if (a.isZero()) { + return a.clone(); + } + + return this.m.sub(a)._forceRed(this); + }; + + Red.prototype.add = function add (a, b) { + this._verify2(a, b); + + var res = a.add(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res._forceRed(this); + }; + + Red.prototype.iadd = function iadd (a, b) { + this._verify2(a, b); + + var res = a.iadd(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res; + }; + + Red.prototype.sub = function sub (a, b) { + this._verify2(a, b); + + var res = a.sub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res._forceRed(this); + }; + + Red.prototype.isub = function isub (a, b) { + this._verify2(a, b); + + var res = a.isub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res; + }; + + Red.prototype.shl = function shl (a, num) { + this._verify1(a); + return this.imod(a.ushln(num)); + }; + + Red.prototype.imul = function imul (a, b) { + this._verify2(a, b); + return this.imod(a.imul(b)); + }; + + Red.prototype.mul = function mul (a, b) { + this._verify2(a, b); + return this.imod(a.mul(b)); + }; + + Red.prototype.isqr = function isqr (a) { + return this.imul(a, a.clone()); + }; + + Red.prototype.sqr = function sqr (a) { + return this.mul(a, a); + }; + + Red.prototype.sqrt = function sqrt (a) { + if (a.isZero()) return a.clone(); + + var mod3 = this.m.andln(3); + assert(mod3 % 2 === 1); + + // Fast case + if (mod3 === 3) { + var pow = this.m.add(new BN(1)).iushrn(2); + return this.pow(a, pow); + } + + // Tonelli-Shanks algorithm (Totally unoptimized and slow) + // + // Find Q and S, that Q * 2 ^ S = (P - 1) + var q = this.m.subn(1); + var s = 0; + while (!q.isZero() && q.andln(1) === 0) { + s++; + q.iushrn(1); + } + assert(!q.isZero()); + + var one = new BN(1).toRed(this); + var nOne = one.redNeg(); + + // Find quadratic non-residue + // NOTE: Max is such because of generalized Riemann hypothesis. + var lpow = this.m.subn(1).iushrn(1); + var z = this.m.bitLength(); + z = new BN(2 * z * z).toRed(this); + + while (this.pow(z, lpow).cmp(nOne) !== 0) { + z.redIAdd(nOne); + } + + var c = this.pow(z, q); + var r = this.pow(a, q.addn(1).iushrn(1)); + var t = this.pow(a, q); + var m = s; + while (t.cmp(one) !== 0) { + var tmp = t; + for (var i = 0; tmp.cmp(one) !== 0; i++) { + tmp = tmp.redSqr(); + } + assert(i < m); + var b = this.pow(c, new BN(1).iushln(m - i - 1)); + + r = r.redMul(b); + c = b.redSqr(); + t = t.redMul(c); + m = i; + } + + return r; + }; + + Red.prototype.invm = function invm (a) { + var inv = a._invmp(this.m); + if (inv.negative !== 0) { + inv.negative = 0; + return this.imod(inv).redNeg(); + } else { + return this.imod(inv); + } + }; + + Red.prototype.pow = function pow (a, num) { + if (num.isZero()) return new BN(1).toRed(this); + if (num.cmpn(1) === 0) return a.clone(); + + var windowSize = 4; + var wnd = new Array(1 << windowSize); + wnd[0] = new BN(1).toRed(this); + wnd[1] = a; + for (var i = 2; i < wnd.length; i++) { + wnd[i] = this.mul(wnd[i - 1], a); + } + + var res = wnd[0]; + var current = 0; + var currentLen = 0; + var start = num.bitLength() % 26; + if (start === 0) { + start = 26; + } + + for (i = num.length - 1; i >= 0; i--) { + var word = num.words[i]; + for (var j = start - 1; j >= 0; j--) { + var bit = (word >> j) & 1; + if (res !== wnd[0]) { + res = this.sqr(res); + } + + if (bit === 0 && current === 0) { + currentLen = 0; + continue; + } + + current <<= 1; + current |= bit; + currentLen++; + if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; + + res = this.mul(res, wnd[current]); + currentLen = 0; + current = 0; + } + start = 26; + } + + return res; + }; + + Red.prototype.convertTo = function convertTo (num) { + var r = num.umod(this.m); + + return r === num ? r.clone() : r; + }; + + Red.prototype.convertFrom = function convertFrom (num) { + var res = num.clone(); + res.red = null; + return res; + }; + + // + // Montgomery method engine + // + + BN.mont = function mont (num) { + return new Mont(num); + }; + + function Mont (m) { + Red.call(this, m); + + this.shift = this.m.bitLength(); + if (this.shift % 26 !== 0) { + this.shift += 26 - (this.shift % 26); + } + + this.r = new BN(1).iushln(this.shift); + this.r2 = this.imod(this.r.sqr()); + this.rinv = this.r._invmp(this.m); + + this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); + this.minv = this.minv.umod(this.r); + this.minv = this.r.sub(this.minv); + } + inherits(Mont, Red); + + Mont.prototype.convertTo = function convertTo (num) { + return this.imod(num.ushln(this.shift)); + }; + + Mont.prototype.convertFrom = function convertFrom (num) { + var r = this.imod(num.mul(this.rinv)); + r.red = null; + return r; + }; + + Mont.prototype.imul = function imul (a, b) { + if (a.isZero() || b.isZero()) { + a.words[0] = 0; + a.length = 1; + return a; + } + + var t = a.imul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); + }; + + Mont.prototype.mul = function mul (a, b) { + if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); + + var t = a.mul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); + }; + + Mont.prototype.invm = function invm (a) { + // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R + var res = this.imod(a._invmp(this.m).mul(this.r2)); + return res._forceRed(this); + }; +})(typeof module === 'undefined' || module, this); diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/crypto-js/crypto-js.js b/TaskServer/node_modules/ccxt/js/static_dependencies/crypto-js/crypto-js.js new file mode 100644 index 000000000..17f2b1163 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/crypto-js/crypto-js.js @@ -0,0 +1,5988 @@ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(); + } + else if (typeof define === "function" && define.amd) { + // AMD + define([], factory); + } + else { + // Global (browser) + root.CryptoJS = factory(); + } +}(this, function () { + + /** + * CryptoJS core components. + */ + var CryptoJS = CryptoJS || (function (Math, undefined) { + /* + * Local polyfil of Object.create + */ + var create = Object.create || (function () { + function F() {}; + + return function (obj) { + var subtype; + + F.prototype = obj; + + subtype = new F(); + + F.prototype = null; + + return subtype; + }; + }()) + + /** + * CryptoJS namespace. + */ + var C = {}; + + /** + * Library namespace. + */ + var C_lib = C.lib = {}; + + /** + * Base object for prototypal inheritance. + */ + var Base = C_lib.Base = (function () { + + + return { + /** + * Creates a new object that inherits from this object. + * + * @param {Object} overrides Properties to copy into the new object. + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * field: 'value', + * + * method: function () { + * } + * }); + */ + extend: function (overrides) { + // Spawn + var subtype = create(this); + + // Augment + if (overrides) { + subtype.mixIn(overrides); + } + + // Create default initializer + if (!subtype.hasOwnProperty('init') || this.init === subtype.init) { + subtype.init = function () { + subtype.$super.init.apply(this, arguments); + }; + } + + // Initializer's prototype is the subtype object + subtype.init.prototype = subtype; + + // Reference supertype + subtype.$super = this; + + return subtype; + }, + + /** + * Extends this object and runs the init method. + * Arguments to create() will be passed to init(). + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var instance = MyType.create(); + */ + create: function () { + var instance = this.extend(); + instance.init.apply(instance, arguments); + + return instance; + }, + + /** + * Initializes a newly created object. + * Override this method to add some logic when your objects are created. + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * init: function () { + * // ... + * } + * }); + */ + init: function () { + }, + + /** + * Copies properties into this object. + * + * @param {Object} properties The properties to mix in. + * + * @example + * + * MyType.mixIn({ + * field: 'value' + * }); + */ + mixIn: function (properties) { + for (var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + + // IE won't copy toString using the loop above + if (properties.hasOwnProperty('toString')) { + this.toString = properties.toString; + } + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = instance.clone(); + */ + clone: function () { + return this.init.prototype.extend(this); + } + }; + }()); + + /** + * An array of 32-bit words. + * + * @property {Array} words The array of 32-bit words. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var WordArray = C_lib.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of 32-bit words. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.create(); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 4; + } + }, + + /** + * Converts this word array to a string. + * + * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex + * + * @return {string} The stringified word array. + * + * @example + * + * var string = wordArray + ''; + * var string = wordArray.toString(); + * var string = wordArray.toString(CryptoJS.enc.Utf8); + */ + toString: function (encoder) { + return (encoder || Hex).stringify(this); + }, + + /** + * Concatenates a word array to this word array. + * + * @param {WordArray} wordArray The word array to append. + * + * @return {WordArray} This word array. + * + * @example + * + * wordArray1.concat(wordArray2); + */ + concat: function (wordArray) { + // Shortcuts + var thisWords = this.words; + var thatWords = wordArray.words; + var thisSigBytes = this.sigBytes; + var thatSigBytes = wordArray.sigBytes; + + // Clamp excess bits + this.clamp(); + + // Concat + if (thisSigBytes % 4) { + // Copy one byte at a time + for (var i = 0; i < thatSigBytes; i++) { + var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); + } + } else { + // Copy one word at a time + for (var i = 0; i < thatSigBytes; i += 4) { + thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; + } + } + this.sigBytes += thatSigBytes; + + // Chainable + return this; + }, + + /** + * Removes insignificant bits. + * + * @example + * + * wordArray.clamp(); + */ + clamp: function () { + // Shortcuts + var words = this.words; + var sigBytes = this.sigBytes; + + // Clamp + words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); + words.length = Math.ceil(sigBytes / 4); + }, + + /** + * Creates a copy of this word array. + * + * @return {WordArray} The clone. + * + * @example + * + * var clone = wordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone.words = this.words.slice(0); + + return clone; + }, + + /** + * Creates a word array filled with random bytes. + * + * @param {number} nBytes The number of random bytes to generate. + * + * @return {WordArray} The random word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.random(16); + */ + random: function (nBytes) { + var words = []; + + var r = (function (m_w) { + var m_w = m_w; + var m_z = 0x3ade68b1; + var mask = 0xffffffff; + + return function () { + m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; + m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; + var result = ((m_z << 0x10) + m_w) & mask; + result /= 0x100000000; + result += 0.5; + return result * (Math.random() > .5 ? 1 : -1); + } + }); + + for (var i = 0, rcache; i < nBytes; i += 4) { + var _r = r((rcache || Math.random()) * 0x100000000); + + rcache = _r() * 0x3ade67b7; + words.push((_r() * 0x100000000) | 0); + } + + return new WordArray.init(words, nBytes); + } + }); + + /** + * Encoder namespace. + */ + var C_enc = C.enc = {}; + + /** + * Hex encoding strategy. + */ + var Hex = C_enc.Hex = { + /** + * Converts a word array to a hex string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The hex string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.enc.Hex.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var hexChars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + hexChars.push((bite >>> 4).toString(16)); + hexChars.push((bite & 0x0f).toString(16)); + } + + return hexChars.join(''); + }, + + /** + * Converts a hex string to a word array. + * + * @param {string} hexStr The hex string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Hex.parse(hexString); + */ + parse: function (hexStr) { + // Shortcut + var hexStrLength = hexStr.length; + + // Convert + var words = []; + for (var i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new WordArray.init(words, hexStrLength / 2); + } + }; + + /** + * Latin1 encoding strategy. + */ + var Latin1 = C_enc.Latin1 = { + /** + * Converts a word array to a Latin1 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Latin1 string. + * + * @static + * + * @example + * + * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var latin1Chars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + latin1Chars.push(String.fromCharCode(bite)); + } + + return latin1Chars.join(''); + }, + + /** + * Converts a Latin1 string to a word array. + * + * @param {string} latin1Str The Latin1 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); + */ + parse: function (latin1Str) { + // Shortcut + var latin1StrLength = latin1Str.length; + + // Convert + var words = []; + for (var i = 0; i < latin1StrLength; i++) { + words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); + } + + return new WordArray.init(words, latin1StrLength); + } + }; + + /** + * UTF-8 encoding strategy. + */ + var Utf8 = C_enc.Utf8 = { + /** + * Converts a word array to a UTF-8 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-8 string. + * + * @static + * + * @example + * + * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); + */ + stringify: function (wordArray) { + try { + return decodeURIComponent(escape(Latin1.stringify(wordArray))); + } catch (e) { + throw new Error('Malformed UTF-8 data'); + } + }, + + /** + * Converts a UTF-8 string to a word array. + * + * @param {string} utf8Str The UTF-8 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); + */ + parse: function (utf8Str) { + return Latin1.parse(unescape(encodeURIComponent(utf8Str))); + } + }; + + /** + * Abstract buffered block algorithm template. + * + * The property blockSize must be implemented in a concrete subtype. + * + * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 + */ + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ + /** + * Resets this block algorithm's data buffer to its initial state. + * + * @example + * + * bufferedBlockAlgorithm.reset(); + */ + reset: function () { + // Initial values + this._data = new WordArray.init(); + this._nDataBytes = 0; + }, + + /** + * Adds new data to this block algorithm's buffer. + * + * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. + * + * @example + * + * bufferedBlockAlgorithm._append('data'); + * bufferedBlockAlgorithm._append(wordArray); + */ + _append: function (data) { + // Convert string to WordArray, else assume WordArray already + if (typeof data == 'string') { + data = Utf8.parse(data); + } + + // Append + this._data.concat(data); + this._nDataBytes += data.sigBytes; + }, + + /** + * Processes available data blocks. + * + * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. + * + * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. + * + * @return {WordArray} The processed data. + * + * @example + * + * var processedData = bufferedBlockAlgorithm._process(); + * var processedData = bufferedBlockAlgorithm._process(!!'flush'); + */ + _process: function (doFlush) { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var dataSigBytes = data.sigBytes; + var blockSize = this.blockSize; + var blockSizeBytes = blockSize * 4; + + // Count blocks ready + var nBlocksReady = dataSigBytes / blockSizeBytes; + if (doFlush) { + // Round up to include partial blocks + nBlocksReady = Math.ceil(nBlocksReady); + } else { + // Round down to include only full blocks, + // less the number of blocks that must remain in the buffer + nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); + } + + // Count words ready + var nWordsReady = nBlocksReady * blockSize; + + // Count bytes ready + var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); + + // Process blocks + if (nWordsReady) { + for (var offset = 0; offset < nWordsReady; offset += blockSize) { + // Perform concrete-algorithm logic + this._doProcessBlock(dataWords, offset); + } + + // Remove processed words + var processedWords = dataWords.splice(0, nWordsReady); + data.sigBytes -= nBytesReady; + } + + // Return processed words + return new WordArray.init(processedWords, nBytesReady); + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = bufferedBlockAlgorithm.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone._data = this._data.clone(); + + return clone; + }, + + _minBufferSize: 0 + }); + + /** + * Abstract hasher template. + * + * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) + */ + var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + */ + cfg: Base.extend(), + + /** + * Initializes a newly created hasher. + * + * @param {Object} cfg (Optional) The configuration options to use for this hash computation. + * + * @example + * + * var hasher = CryptoJS.algo.SHA256.create(); + */ + init: function (cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Set initial values + this.reset(); + }, + + /** + * Resets this hasher to its initial state. + * + * @example + * + * hasher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-hasher logic + this._doReset(); + }, + + /** + * Updates this hasher with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {Hasher} This hasher. + * + * @example + * + * hasher.update('message'); + * hasher.update(wordArray); + */ + update: function (messageUpdate) { + // Append + this._append(messageUpdate); + + // Update the hash + this._process(); + + // Chainable + return this; + }, + + /** + * Finalizes the hash computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The hash. + * + * @example + * + * var hash = hasher.finalize(); + * var hash = hasher.finalize('message'); + * var hash = hasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Final message update + if (messageUpdate) { + this._append(messageUpdate); + } + + // Perform concrete-hasher logic + var hash = this._doFinalize(); + + return hash; + }, + + blockSize: 512/32, + + /** + * Creates a shortcut function to a hasher's object interface. + * + * @param {Hasher} hasher The hasher to create a helper for. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); + */ + _createHelper: function (hasher) { + return function (message, cfg) { + return new hasher.init(cfg).finalize(message); + }; + }, + + /** + * Creates a shortcut function to the HMAC's object interface. + * + * @param {Hasher} hasher The hasher to use in this HMAC helper. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); + */ + _createHmacHelper: function (hasher) { + return function (message, key) { + return new C_algo.HMAC.init(hasher, key).finalize(message); + }; + } + }); + + /** + * Algorithm namespace. + */ + var C_algo = C.algo = {}; + + return C; + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_enc = C.enc; + + /** + * Base64 encoding strategy. + */ + var Base64 = C_enc.Base64 = { + /** + * Converts a word array to a Base64 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Base64 string. + * + * @static + * + * @example + * + * var base64String = CryptoJS.enc.Base64.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + var map = this._map; + + // Clamp excess bits + wordArray.clamp(); + + // Convert + var base64Chars = []; + for (var i = 0; i < sigBytes; i += 3) { + var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; + var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; + + var triplet = (byte1 << 16) | (byte2 << 8) | byte3; + + for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { + base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); + } + } + + // Add padding + var paddingChar = map.charAt(64); + if (paddingChar) { + while (base64Chars.length % 4) { + base64Chars.push(paddingChar); + } + } + + return base64Chars.join(''); + }, + + /** + * Converts a Base64 string to a word array. + * + * @param {string} base64Str The Base64 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Base64.parse(base64String); + */ + parse: function (base64Str) { + // Shortcuts + var base64StrLength = base64Str.length; + var map = this._map; + var reverseMap = this._reverseMap; + + if (!reverseMap) { + reverseMap = this._reverseMap = []; + for (var j = 0; j < map.length; j++) { + reverseMap[map.charCodeAt(j)] = j; + } + } + + // Ignore padding + var paddingChar = map.charAt(64); + if (paddingChar) { + var paddingIndex = base64Str.indexOf(paddingChar); + if (paddingIndex !== -1) { + base64StrLength = paddingIndex; + } + } + + // Convert + return parseLoop(base64Str, base64StrLength, reverseMap); + + }, + + _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + }; + + function parseLoop(base64Str, base64StrLength, reverseMap) { + var words = []; + var nBytes = 0; + for (var i = 0; i < base64StrLength; i++) { + if (i % 4) { + var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2); + var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2); + words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); + nBytes++; + } + } + return WordArray.create(words, nBytes); + } + }()); + + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Constants table + var T = []; + + // Compute constants + (function () { + for (var i = 0; i < 64; i++) { + T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0; + } + }()); + + /** + * MD5 hash algorithm. + */ + var MD5 = C_algo.MD5 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0x67452301, 0xefcdab89, + 0x98badcfe, 0x10325476 + ]); + }, + + _doProcessBlock: function (M, offset) { + // Swap endian + for (var i = 0; i < 16; i++) { + // Shortcuts + var offset_i = offset + i; + var M_offset_i = M[offset_i]; + + M[offset_i] = ( + (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | + (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) + ); + } + + // Shortcuts + var H = this._hash.words; + + var M_offset_0 = M[offset + 0]; + var M_offset_1 = M[offset + 1]; + var M_offset_2 = M[offset + 2]; + var M_offset_3 = M[offset + 3]; + var M_offset_4 = M[offset + 4]; + var M_offset_5 = M[offset + 5]; + var M_offset_6 = M[offset + 6]; + var M_offset_7 = M[offset + 7]; + var M_offset_8 = M[offset + 8]; + var M_offset_9 = M[offset + 9]; + var M_offset_10 = M[offset + 10]; + var M_offset_11 = M[offset + 11]; + var M_offset_12 = M[offset + 12]; + var M_offset_13 = M[offset + 13]; + var M_offset_14 = M[offset + 14]; + var M_offset_15 = M[offset + 15]; + + // Working varialbes + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + + // Computation + a = FF(a, b, c, d, M_offset_0, 7, T[0]); + d = FF(d, a, b, c, M_offset_1, 12, T[1]); + c = FF(c, d, a, b, M_offset_2, 17, T[2]); + b = FF(b, c, d, a, M_offset_3, 22, T[3]); + a = FF(a, b, c, d, M_offset_4, 7, T[4]); + d = FF(d, a, b, c, M_offset_5, 12, T[5]); + c = FF(c, d, a, b, M_offset_6, 17, T[6]); + b = FF(b, c, d, a, M_offset_7, 22, T[7]); + a = FF(a, b, c, d, M_offset_8, 7, T[8]); + d = FF(d, a, b, c, M_offset_9, 12, T[9]); + c = FF(c, d, a, b, M_offset_10, 17, T[10]); + b = FF(b, c, d, a, M_offset_11, 22, T[11]); + a = FF(a, b, c, d, M_offset_12, 7, T[12]); + d = FF(d, a, b, c, M_offset_13, 12, T[13]); + c = FF(c, d, a, b, M_offset_14, 17, T[14]); + b = FF(b, c, d, a, M_offset_15, 22, T[15]); + + a = GG(a, b, c, d, M_offset_1, 5, T[16]); + d = GG(d, a, b, c, M_offset_6, 9, T[17]); + c = GG(c, d, a, b, M_offset_11, 14, T[18]); + b = GG(b, c, d, a, M_offset_0, 20, T[19]); + a = GG(a, b, c, d, M_offset_5, 5, T[20]); + d = GG(d, a, b, c, M_offset_10, 9, T[21]); + c = GG(c, d, a, b, M_offset_15, 14, T[22]); + b = GG(b, c, d, a, M_offset_4, 20, T[23]); + a = GG(a, b, c, d, M_offset_9, 5, T[24]); + d = GG(d, a, b, c, M_offset_14, 9, T[25]); + c = GG(c, d, a, b, M_offset_3, 14, T[26]); + b = GG(b, c, d, a, M_offset_8, 20, T[27]); + a = GG(a, b, c, d, M_offset_13, 5, T[28]); + d = GG(d, a, b, c, M_offset_2, 9, T[29]); + c = GG(c, d, a, b, M_offset_7, 14, T[30]); + b = GG(b, c, d, a, M_offset_12, 20, T[31]); + + a = HH(a, b, c, d, M_offset_5, 4, T[32]); + d = HH(d, a, b, c, M_offset_8, 11, T[33]); + c = HH(c, d, a, b, M_offset_11, 16, T[34]); + b = HH(b, c, d, a, M_offset_14, 23, T[35]); + a = HH(a, b, c, d, M_offset_1, 4, T[36]); + d = HH(d, a, b, c, M_offset_4, 11, T[37]); + c = HH(c, d, a, b, M_offset_7, 16, T[38]); + b = HH(b, c, d, a, M_offset_10, 23, T[39]); + a = HH(a, b, c, d, M_offset_13, 4, T[40]); + d = HH(d, a, b, c, M_offset_0, 11, T[41]); + c = HH(c, d, a, b, M_offset_3, 16, T[42]); + b = HH(b, c, d, a, M_offset_6, 23, T[43]); + a = HH(a, b, c, d, M_offset_9, 4, T[44]); + d = HH(d, a, b, c, M_offset_12, 11, T[45]); + c = HH(c, d, a, b, M_offset_15, 16, T[46]); + b = HH(b, c, d, a, M_offset_2, 23, T[47]); + + a = II(a, b, c, d, M_offset_0, 6, T[48]); + d = II(d, a, b, c, M_offset_7, 10, T[49]); + c = II(c, d, a, b, M_offset_14, 15, T[50]); + b = II(b, c, d, a, M_offset_5, 21, T[51]); + a = II(a, b, c, d, M_offset_12, 6, T[52]); + d = II(d, a, b, c, M_offset_3, 10, T[53]); + c = II(c, d, a, b, M_offset_10, 15, T[54]); + b = II(b, c, d, a, M_offset_1, 21, T[55]); + a = II(a, b, c, d, M_offset_8, 6, T[56]); + d = II(d, a, b, c, M_offset_15, 10, T[57]); + c = II(c, d, a, b, M_offset_6, 15, T[58]); + b = II(b, c, d, a, M_offset_13, 21, T[59]); + a = II(a, b, c, d, M_offset_4, 6, T[60]); + d = II(d, a, b, c, M_offset_11, 10, T[61]); + c = II(c, d, a, b, M_offset_2, 15, T[62]); + b = II(b, c, d, a, M_offset_9, 21, T[63]); + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + + var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000); + var nBitsTotalL = nBitsTotal; + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = ( + (((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) | + (((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00) + ); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( + (((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) | + (((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00) + ); + + data.sigBytes = (dataWords.length + 1) * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var hash = this._hash; + var H = hash.words; + + // Swap endian + for (var i = 0; i < 4; i++) { + // Shortcut + var H_i = H[i]; + + H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | + (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); + } + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + function FF(a, b, c, d, x, s, t) { + var n = a + ((b & c) | (~b & d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function GG(a, b, c, d, x, s, t) { + var n = a + ((b & d) | (c & ~d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function HH(a, b, c, d, x, s, t) { + var n = a + (b ^ c ^ d) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + function II(a, b, c, d, x, s, t) { + var n = a + (c ^ (b | ~d)) + x + t; + return ((n << s) | (n >>> (32 - s))) + b; + } + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.MD5('message'); + * var hash = CryptoJS.MD5(wordArray); + */ + C.MD5 = Hasher._createHelper(MD5); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacMD5(message, key); + */ + C.HmacMD5 = Hasher._createHmacHelper(MD5); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Reusable object + var W = []; + + /** + * SHA-1 hash algorithm. + */ + var SHA1 = C_algo.SHA1 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0x67452301, 0xefcdab89, + 0x98badcfe, 0x10325476, + 0xc3d2e1f0 + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + + // Computation + for (var i = 0; i < 80; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; + W[i] = (n << 1) | (n >>> 31); + } + + var t = ((a << 5) | (a >>> 27)) + e + W[i]; + if (i < 20) { + t += ((b & c) | (~b & d)) + 0x5a827999; + } else if (i < 40) { + t += (b ^ c ^ d) + 0x6ed9eba1; + } else if (i < 60) { + t += ((b & c) | (b & d) | (c & d)) - 0x70e44324; + } else /* if (i < 80) */ { + t += (b ^ c ^ d) - 0x359d3e2a; + } + + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA1('message'); + * var hash = CryptoJS.SHA1(wordArray); + */ + C.SHA1 = Hasher._createHelper(SHA1); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA1(message, key); + */ + C.HmacSHA1 = Hasher._createHmacHelper(SHA1); + }()); + + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Initialization and round constants tables + var H = []; + var K = []; + + // Compute constants + (function () { + function isPrime(n) { + var sqrtN = Math.sqrt(n); + for (var factor = 2; factor <= sqrtN; factor++) { + if (!(n % factor)) { + return false; + } + } + + return true; + } + + function getFractionalBits(n) { + return ((n - (n | 0)) * 0x100000000) | 0; + } + + var n = 2; + var nPrime = 0; + while (nPrime < 64) { + if (isPrime(n)) { + if (nPrime < 8) { + H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2)); + } + K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3)); + + nPrime++; + } + + n++; + } + }()); + + // Reusable object + var W = []; + + /** + * SHA-256 hash algorithm. + */ + var SHA256 = C_algo.SHA256 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init(H.slice(0)); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + var f = H[5]; + var g = H[6]; + var h = H[7]; + + // Computation + for (var i = 0; i < 64; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var gamma0x = W[i - 15]; + var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ + ((gamma0x << 14) | (gamma0x >>> 18)) ^ + (gamma0x >>> 3); + + var gamma1x = W[i - 2]; + var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ + ((gamma1x << 13) | (gamma1x >>> 19)) ^ + (gamma1x >>> 10); + + W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; + } + + var ch = (e & f) ^ (~e & g); + var maj = (a & b) ^ (a & c) ^ (b & c); + + var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); + var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); + + var t1 = h + sigma1 + ch + K[i] + W[i]; + var t2 = sigma0 + maj; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + H[5] = (H[5] + f) | 0; + H[6] = (H[6] + g) | 0; + H[7] = (H[7] + h) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA256('message'); + * var hash = CryptoJS.SHA256(wordArray); + */ + C.SHA256 = Hasher._createHelper(SHA256); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA256(message, key); + */ + C.HmacSHA256 = Hasher._createHmacHelper(SHA256); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_enc = C.enc; + + /** + * UTF-16 BE encoding strategy. + */ + var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = { + /** + * Converts a word array to a UTF-16 BE string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-16 BE string. + * + * @static + * + * @example + * + * var utf16String = CryptoJS.enc.Utf16.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var utf16Chars = []; + for (var i = 0; i < sigBytes; i += 2) { + var codePoint = (words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff; + utf16Chars.push(String.fromCharCode(codePoint)); + } + + return utf16Chars.join(''); + }, + + /** + * Converts a UTF-16 BE string to a word array. + * + * @param {string} utf16Str The UTF-16 BE string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf16.parse(utf16String); + */ + parse: function (utf16Str) { + // Shortcut + var utf16StrLength = utf16Str.length; + + // Convert + var words = []; + for (var i = 0; i < utf16StrLength; i++) { + words[i >>> 1] |= utf16Str.charCodeAt(i) << (16 - (i % 2) * 16); + } + + return WordArray.create(words, utf16StrLength * 2); + } + }; + + /** + * UTF-16 LE encoding strategy. + */ + C_enc.Utf16LE = { + /** + * Converts a word array to a UTF-16 LE string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-16 LE string. + * + * @static + * + * @example + * + * var utf16Str = CryptoJS.enc.Utf16LE.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var utf16Chars = []; + for (var i = 0; i < sigBytes; i += 2) { + var codePoint = swapEndian((words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff); + utf16Chars.push(String.fromCharCode(codePoint)); + } + + return utf16Chars.join(''); + }, + + /** + * Converts a UTF-16 LE string to a word array. + * + * @param {string} utf16Str The UTF-16 LE string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf16LE.parse(utf16Str); + */ + parse: function (utf16Str) { + // Shortcut + var utf16StrLength = utf16Str.length; + + // Convert + var words = []; + for (var i = 0; i < utf16StrLength; i++) { + words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << (16 - (i % 2) * 16)); + } + + return WordArray.create(words, utf16StrLength * 2); + } + }; + + function swapEndian(word) { + return ((word << 8) & 0xff00ff00) | ((word >>> 8) & 0x00ff00ff); + } + }()); + + + (function () { + // Check if typed arrays are supported + if (typeof ArrayBuffer != 'function') { + return; + } + + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + + // Reference original init + var superInit = WordArray.init; + + // Augment WordArray.init to handle typed arrays + var subInit = WordArray.init = function (typedArray) { + // Convert buffers to uint8 + if (typedArray instanceof ArrayBuffer) { + typedArray = new Uint8Array(typedArray); + } + + // Convert other array views to uint8 + if ( + typedArray instanceof Int8Array || + (typeof Uint8ClampedArray !== "undefined" && typedArray instanceof Uint8ClampedArray) || + typedArray instanceof Int16Array || + typedArray instanceof Uint16Array || + typedArray instanceof Int32Array || + typedArray instanceof Uint32Array || + typedArray instanceof Float32Array || + typedArray instanceof Float64Array + ) { + typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); + } + + // Handle Uint8Array + if (typedArray instanceof Uint8Array) { + // Shortcut + var typedArrayByteLength = typedArray.byteLength; + + // Extract bytes + var words = []; + for (var i = 0; i < typedArrayByteLength; i++) { + words[i >>> 2] |= typedArray[i] << (24 - (i % 4) * 8); + } + + // Initialize this word array + superInit.call(this, words, typedArrayByteLength); + } else { + // Else call normal init + superInit.apply(this, arguments); + } + }; + + subInit.prototype = WordArray; + }()); + + + /** @preserve + (c) 2012 by Cédric Mesnil. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Constants table + var _zl = WordArray.create([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]); + var _zr = WordArray.create([ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]); + var _sl = WordArray.create([ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ]); + var _sr = WordArray.create([ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ]); + + var _hl = WordArray.create([ 0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]); + var _hr = WordArray.create([ 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]); + + /** + * RIPEMD160 hash algorithm. + */ + var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({ + _doReset: function () { + this._hash = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]); + }, + + _doProcessBlock: function (M, offset) { + + // Swap endian + for (var i = 0; i < 16; i++) { + // Shortcuts + var offset_i = offset + i; + var M_offset_i = M[offset_i]; + + // Swap + M[offset_i] = ( + (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | + (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) + ); + } + // Shortcut + var H = this._hash.words; + var hl = _hl.words; + var hr = _hr.words; + var zl = _zl.words; + var zr = _zr.words; + var sl = _sl.words; + var sr = _sr.words; + + // Working variables + var al, bl, cl, dl, el; + var ar, br, cr, dr, er; + + ar = al = H[0]; + br = bl = H[1]; + cr = cl = H[2]; + dr = dl = H[3]; + er = el = H[4]; + // Computation + var t; + for (var i = 0; i < 80; i += 1) { + t = (al + M[offset+zl[i]])|0; + if (i<16){ + t += f1(bl,cl,dl) + hl[0]; + } else if (i<32) { + t += f2(bl,cl,dl) + hl[1]; + } else if (i<48) { + t += f3(bl,cl,dl) + hl[2]; + } else if (i<64) { + t += f4(bl,cl,dl) + hl[3]; + } else {// if (i<80) { + t += f5(bl,cl,dl) + hl[4]; + } + t = t|0; + t = rotl(t,sl[i]); + t = (t+el)|0; + al = el; + el = dl; + dl = rotl(cl, 10); + cl = bl; + bl = t; + + t = (ar + M[offset+zr[i]])|0; + if (i<16){ + t += f5(br,cr,dr) + hr[0]; + } else if (i<32) { + t += f4(br,cr,dr) + hr[1]; + } else if (i<48) { + t += f3(br,cr,dr) + hr[2]; + } else if (i<64) { + t += f2(br,cr,dr) + hr[3]; + } else {// if (i<80) { + t += f1(br,cr,dr) + hr[4]; + } + t = t|0; + t = rotl(t,sr[i]) ; + t = (t+er)|0; + ar = er; + er = dr; + dr = rotl(cr, 10); + cr = br; + br = t; + } + // Intermediate hash value + t = (H[1] + cl + dr)|0; + H[1] = (H[2] + dl + er)|0; + H[2] = (H[3] + el + ar)|0; + H[3] = (H[4] + al + br)|0; + H[4] = (H[0] + bl + cr)|0; + H[0] = t; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( + (((nBitsTotal << 8) | (nBitsTotal >>> 24)) & 0x00ff00ff) | + (((nBitsTotal << 24) | (nBitsTotal >>> 8)) & 0xff00ff00) + ); + data.sigBytes = (dataWords.length + 1) * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var hash = this._hash; + var H = hash.words; + + // Swap endian + for (var i = 0; i < 5; i++) { + // Shortcut + var H_i = H[i]; + + // Swap + H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | + (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); + } + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + + function f1(x, y, z) { + return ((x) ^ (y) ^ (z)); + + } + + function f2(x, y, z) { + return (((x)&(y)) | ((~x)&(z))); + } + + function f3(x, y, z) { + return (((x) | (~(y))) ^ (z)); + } + + function f4(x, y, z) { + return (((x) & (z)) | ((y)&(~(z)))); + } + + function f5(x, y, z) { + return ((x) ^ ((y) |(~(z)))); + + } + + function rotl(x,n) { + return (x<>>(32-n)); + } + + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.RIPEMD160('message'); + * var hash = CryptoJS.RIPEMD160(wordArray); + */ + C.RIPEMD160 = Hasher._createHelper(RIPEMD160); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacRIPEMD160(message, key); + */ + C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var C_enc = C.enc; + var Utf8 = C_enc.Utf8; + var C_algo = C.algo; + + /** + * HMAC algorithm. + */ + var HMAC = C_algo.HMAC = Base.extend({ + /** + * Initializes a newly created HMAC. + * + * @param {Hasher} hasher The hash algorithm to use. + * @param {WordArray|string} key The secret key. + * + * @example + * + * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key); + */ + init: function (hasher, key) { + // Init hasher + hasher = this._hasher = new hasher.init(); + + // Convert string to WordArray, else assume WordArray already + if (typeof key == 'string') { + key = Utf8.parse(key); + } + + // Shortcuts + var hasherBlockSize = hasher.blockSize; + var hasherBlockSizeBytes = hasherBlockSize * 4; + + // Allow arbitrary length keys + if (key.sigBytes > hasherBlockSizeBytes) { + key = hasher.finalize(key); + } + + // Clamp excess bits + key.clamp(); + + // Clone key for inner and outer pads + var oKey = this._oKey = key.clone(); + var iKey = this._iKey = key.clone(); + + // Shortcuts + var oKeyWords = oKey.words; + var iKeyWords = iKey.words; + + // XOR keys with pad constants + for (var i = 0; i < hasherBlockSize; i++) { + oKeyWords[i] ^= 0x5c5c5c5c; + iKeyWords[i] ^= 0x36363636; + } + oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes; + + // Set initial values + this.reset(); + }, + + /** + * Resets this HMAC to its initial state. + * + * @example + * + * hmacHasher.reset(); + */ + reset: function () { + // Shortcut + var hasher = this._hasher; + + // Reset + hasher.reset(); + hasher.update(this._iKey); + }, + + /** + * Updates this HMAC with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {HMAC} This HMAC instance. + * + * @example + * + * hmacHasher.update('message'); + * hmacHasher.update(wordArray); + */ + update: function (messageUpdate) { + this._hasher.update(messageUpdate); + + // Chainable + return this; + }, + + /** + * Finalizes the HMAC computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The HMAC. + * + * @example + * + * var hmac = hmacHasher.finalize(); + * var hmac = hmacHasher.finalize('message'); + * var hmac = hmacHasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Shortcut + var hasher = this._hasher; + + // Compute HMAC + var innerHash = hasher.finalize(messageUpdate); + hasher.reset(); + var hmac = hasher.finalize(this._oKey.clone().concat(innerHash)); + + return hmac; + } + }); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var SHA1 = C_algo.SHA1; + var HMAC = C_algo.HMAC; + + /** + * Password-Based Key Derivation Function 2 algorithm. + */ + var PBKDF2 = C_algo.PBKDF2 = Base.extend({ + /** + * Configuration options. + * + * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) + * @property {Hasher} hasher The hasher to use. Default: SHA1 + * @property {number} iterations The number of iterations to perform. Default: 1 + */ + cfg: Base.extend({ + keySize: 128/32, + hasher: SHA1, + iterations: 1 + }), + + /** + * Initializes a newly created key derivation function. + * + * @param {Object} cfg (Optional) The configuration options to use for the derivation. + * + * @example + * + * var kdf = CryptoJS.algo.PBKDF2.create(); + * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 }); + * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 }); + */ + init: function (cfg) { + this.cfg = this.cfg.extend(cfg); + }, + + /** + * Computes the Password-Based Key Derivation Function 2. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * + * @return {WordArray} The derived key. + * + * @example + * + * var key = kdf.compute(password, salt); + */ + compute: function (password, salt) { + // Shortcut + var cfg = this.cfg; + + // Init HMAC + var hmac = HMAC.create(cfg.hasher, password); + + // Initial values + var derivedKey = WordArray.create(); + var blockIndex = WordArray.create([0x00000001]); + + // Shortcuts + var derivedKeyWords = derivedKey.words; + var blockIndexWords = blockIndex.words; + var keySize = cfg.keySize; + var iterations = cfg.iterations; + + // Generate key + while (derivedKeyWords.length < keySize) { + var block = hmac.update(salt).finalize(blockIndex); + hmac.reset(); + + // Shortcuts + var blockWords = block.words; + var blockWordsLength = blockWords.length; + + // Iterations + var intermediate = block; + for (var i = 1; i < iterations; i++) { + intermediate = hmac.finalize(intermediate); + hmac.reset(); + + // Shortcut + var intermediateWords = intermediate.words; + + // XOR intermediate with block + for (var j = 0; j < blockWordsLength; j++) { + blockWords[j] ^= intermediateWords[j]; + } + } + + derivedKey.concat(block); + blockIndexWords[0]++; + } + derivedKey.sigBytes = keySize * 4; + + return derivedKey; + } + }); + + /** + * Computes the Password-Based Key Derivation Function 2. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * @param {Object} cfg (Optional) The configuration options to use for this computation. + * + * @return {WordArray} The derived key. + * + * @static + * + * @example + * + * var key = CryptoJS.PBKDF2(password, salt); + * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 }); + * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 }); + */ + C.PBKDF2 = function (password, salt, cfg) { + return PBKDF2.create(cfg).compute(password, salt); + }; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var MD5 = C_algo.MD5; + + /** + * This key derivation function is meant to conform with EVP_BytesToKey. + * www.openssl.org/docs/crypto/EVP_BytesToKey.html + */ + var EvpKDF = C_algo.EvpKDF = Base.extend({ + /** + * Configuration options. + * + * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) + * @property {Hasher} hasher The hash algorithm to use. Default: MD5 + * @property {number} iterations The number of iterations to perform. Default: 1 + */ + cfg: Base.extend({ + keySize: 128/32, + hasher: MD5, + iterations: 1 + }), + + /** + * Initializes a newly created key derivation function. + * + * @param {Object} cfg (Optional) The configuration options to use for the derivation. + * + * @example + * + * var kdf = CryptoJS.algo.EvpKDF.create(); + * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 }); + * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 }); + */ + init: function (cfg) { + this.cfg = this.cfg.extend(cfg); + }, + + /** + * Derives a key from a password. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * + * @return {WordArray} The derived key. + * + * @example + * + * var key = kdf.compute(password, salt); + */ + compute: function (password, salt) { + // Shortcut + var cfg = this.cfg; + + // Init hasher + var hasher = cfg.hasher.create(); + + // Initial values + var derivedKey = WordArray.create(); + + // Shortcuts + var derivedKeyWords = derivedKey.words; + var keySize = cfg.keySize; + var iterations = cfg.iterations; + + // Generate key + while (derivedKeyWords.length < keySize) { + if (block) { + hasher.update(block); + } + var block = hasher.update(password).finalize(salt); + hasher.reset(); + + // Iterations + for (var i = 1; i < iterations; i++) { + block = hasher.finalize(block); + hasher.reset(); + } + + derivedKey.concat(block); + } + derivedKey.sigBytes = keySize * 4; + + return derivedKey; + } + }); + + /** + * Derives a key from a password. + * + * @param {WordArray|string} password The password. + * @param {WordArray|string} salt A salt. + * @param {Object} cfg (Optional) The configuration options to use for this computation. + * + * @return {WordArray} The derived key. + * + * @static + * + * @example + * + * var key = CryptoJS.EvpKDF(password, salt); + * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 }); + * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 }); + */ + C.EvpKDF = function (password, salt, cfg) { + return EvpKDF.create(cfg).compute(password, salt); + }; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_algo = C.algo; + var SHA256 = C_algo.SHA256; + + /** + * SHA-224 hash algorithm. + */ + var SHA224 = C_algo.SHA224 = SHA256.extend({ + _doReset: function () { + this._hash = new WordArray.init([ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 + ]); + }, + + _doFinalize: function () { + var hash = SHA256._doFinalize.call(this); + + hash.sigBytes -= 4; + + return hash; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA224('message'); + * var hash = CryptoJS.SHA224(wordArray); + */ + C.SHA224 = SHA256._createHelper(SHA224); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA224(message, key); + */ + C.HmacSHA224 = SHA256._createHmacHelper(SHA224); + }()); + + + (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var X32WordArray = C_lib.WordArray; + + /** + * x64 namespace. + */ + var C_x64 = C.x64 = {}; + + /** + * A 64-bit word. + */ + var X64Word = C_x64.Word = Base.extend({ + /** + * Initializes a newly created 64-bit word. + * + * @param {number} high The high 32 bits. + * @param {number} low The low 32 bits. + * + * @example + * + * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607); + */ + init: function (high, low) { + this.high = high; + this.low = low; + } + + /** + * Bitwise NOTs this word. + * + * @return {X64Word} A new x64-Word object after negating. + * + * @example + * + * var negated = x64Word.not(); + */ + // not: function () { + // var high = ~this.high; + // var low = ~this.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ANDs this word with the passed word. + * + * @param {X64Word} word The x64-Word to AND with this word. + * + * @return {X64Word} A new x64-Word object after ANDing. + * + * @example + * + * var anded = x64Word.and(anotherX64Word); + */ + // and: function (word) { + // var high = this.high & word.high; + // var low = this.low & word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise ORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to OR with this word. + * + * @return {X64Word} A new x64-Word object after ORing. + * + * @example + * + * var ored = x64Word.or(anotherX64Word); + */ + // or: function (word) { + // var high = this.high | word.high; + // var low = this.low | word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Bitwise XORs this word with the passed word. + * + * @param {X64Word} word The x64-Word to XOR with this word. + * + * @return {X64Word} A new x64-Word object after XORing. + * + * @example + * + * var xored = x64Word.xor(anotherX64Word); + */ + // xor: function (word) { + // var high = this.high ^ word.high; + // var low = this.low ^ word.low; + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the left. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftL(25); + */ + // shiftL: function (n) { + // if (n < 32) { + // var high = (this.high << n) | (this.low >>> (32 - n)); + // var low = this.low << n; + // } else { + // var high = this.low << (n - 32); + // var low = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Shifts this word n bits to the right. + * + * @param {number} n The number of bits to shift. + * + * @return {X64Word} A new x64-Word object after shifting. + * + * @example + * + * var shifted = x64Word.shiftR(7); + */ + // shiftR: function (n) { + // if (n < 32) { + // var low = (this.low >>> n) | (this.high << (32 - n)); + // var high = this.high >>> n; + // } else { + // var low = this.high >>> (n - 32); + // var high = 0; + // } + + // return X64Word.create(high, low); + // }, + + /** + * Rotates this word n bits to the left. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotL(25); + */ + // rotL: function (n) { + // return this.shiftL(n).or(this.shiftR(64 - n)); + // }, + + /** + * Rotates this word n bits to the right. + * + * @param {number} n The number of bits to rotate. + * + * @return {X64Word} A new x64-Word object after rotating. + * + * @example + * + * var rotated = x64Word.rotR(7); + */ + // rotR: function (n) { + // return this.shiftR(n).or(this.shiftL(64 - n)); + // }, + + /** + * Adds this word with the passed word. + * + * @param {X64Word} word The x64-Word to add with this word. + * + * @return {X64Word} A new x64-Word object after adding. + * + * @example + * + * var added = x64Word.add(anotherX64Word); + */ + // add: function (word) { + // var low = (this.low + word.low) | 0; + // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0; + // var high = (this.high + word.high + carry) | 0; + + // return X64Word.create(high, low); + // } + }); + + /** + * An array of 64-bit words. + * + * @property {Array} words The array of CryptoJS.x64.Word objects. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var X64WordArray = C_x64.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.x64.WordArray.create(); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ]); + * + * var wordArray = CryptoJS.x64.WordArray.create([ + * CryptoJS.x64.Word.create(0x00010203, 0x04050607), + * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f) + * ], 10); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 8; + } + }, + + /** + * Converts this 64-bit word array to a 32-bit word array. + * + * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array. + * + * @example + * + * var x32WordArray = x64WordArray.toX32(); + */ + toX32: function () { + // Shortcuts + var x64Words = this.words; + var x64WordsLength = x64Words.length; + + // Convert + var x32Words = []; + for (var i = 0; i < x64WordsLength; i++) { + var x64Word = x64Words[i]; + x32Words.push(x64Word.high); + x32Words.push(x64Word.low); + } + + return X32WordArray.create(x32Words, this.sigBytes); + }, + + /** + * Creates a copy of this word array. + * + * @return {X64WordArray} The clone. + * + * @example + * + * var clone = x64WordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + + // Clone "words" array + var words = clone.words = this.words.slice(0); + + // Clone each X64Word object + var wordsLength = words.length; + for (var i = 0; i < wordsLength; i++) { + words[i] = words[i].clone(); + } + + return clone; + } + }); + }()); + + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var C_algo = C.algo; + + // Constants tables + var RHO_OFFSETS = []; + var PI_INDEXES = []; + var ROUND_CONSTANTS = []; + + // Compute Constants + (function () { + // Compute rho offset constants + var x = 1, y = 0; + for (var t = 0; t < 24; t++) { + RHO_OFFSETS[x + 5 * y] = ((t + 1) * (t + 2) / 2) % 64; + + var newX = y % 5; + var newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + + // Compute pi index constants + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + PI_INDEXES[x + 5 * y] = y + ((2 * x + 3 * y) % 5) * 5; + } + } + + // Compute round constants + var LFSR = 0x01; + for (var i = 0; i < 24; i++) { + var roundConstantMsw = 0; + var roundConstantLsw = 0; + + for (var j = 0; j < 7; j++) { + if (LFSR & 0x01) { + var bitPosition = (1 << j) - 1; + if (bitPosition < 32) { + roundConstantLsw ^= 1 << bitPosition; + } else /* if (bitPosition >= 32) */ { + roundConstantMsw ^= 1 << (bitPosition - 32); + } + } + + // Compute next LFSR + if (LFSR & 0x80) { + // Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1 + LFSR = (LFSR << 1) ^ 0x71; + } else { + LFSR <<= 1; + } + } + + ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw); + } + }()); + + // Reusable objects for temporary values + var T = []; + (function () { + for (var i = 0; i < 25; i++) { + T[i] = X64Word.create(); + } + }()); + + /** + * SHA-3 hash algorithm. + */ + var SHA3 = C_algo.SHA3 = Hasher.extend({ + /** + * Configuration options. + * + * @property {number} outputLength + * The desired number of bits in the output hash. + * Only values permitted are: 224, 256, 384, 512. + * Default: 512 + */ + cfg: Hasher.cfg.extend({ + outputLength: 512 + }), + + _doReset: function () { + var state = this._state = [] + for (var i = 0; i < 25; i++) { + state[i] = new X64Word.init(); + } + + this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32; + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var state = this._state; + var nBlockSizeLanes = this.blockSize / 2; + + // Absorb + for (var i = 0; i < nBlockSizeLanes; i++) { + // Shortcuts + var M2i = M[offset + 2 * i]; + var M2i1 = M[offset + 2 * i + 1]; + + // Swap endian + M2i = ( + (((M2i << 8) | (M2i >>> 24)) & 0x00ff00ff) | + (((M2i << 24) | (M2i >>> 8)) & 0xff00ff00) + ); + M2i1 = ( + (((M2i1 << 8) | (M2i1 >>> 24)) & 0x00ff00ff) | + (((M2i1 << 24) | (M2i1 >>> 8)) & 0xff00ff00) + ); + + // Absorb message into state + var lane = state[i]; + lane.high ^= M2i1; + lane.low ^= M2i; + } + + // Rounds + for (var round = 0; round < 24; round++) { + // Theta + for (var x = 0; x < 5; x++) { + // Mix column lanes + var tMsw = 0, tLsw = 0; + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + tMsw ^= lane.high; + tLsw ^= lane.low; + } + + // Temporary values + var Tx = T[x]; + Tx.high = tMsw; + Tx.low = tLsw; + } + for (var x = 0; x < 5; x++) { + // Shortcuts + var Tx4 = T[(x + 4) % 5]; + var Tx1 = T[(x + 1) % 5]; + var Tx1Msw = Tx1.high; + var Tx1Lsw = Tx1.low; + + // Mix surrounding columns + var tMsw = Tx4.high ^ ((Tx1Msw << 1) | (Tx1Lsw >>> 31)); + var tLsw = Tx4.low ^ ((Tx1Lsw << 1) | (Tx1Msw >>> 31)); + for (var y = 0; y < 5; y++) { + var lane = state[x + 5 * y]; + lane.high ^= tMsw; + lane.low ^= tLsw; + } + } + + // Rho Pi + for (var laneIndex = 1; laneIndex < 25; laneIndex++) { + // Shortcuts + var lane = state[laneIndex]; + var laneMsw = lane.high; + var laneLsw = lane.low; + var rhoOffset = RHO_OFFSETS[laneIndex]; + + // Rotate lanes + if (rhoOffset < 32) { + var tMsw = (laneMsw << rhoOffset) | (laneLsw >>> (32 - rhoOffset)); + var tLsw = (laneLsw << rhoOffset) | (laneMsw >>> (32 - rhoOffset)); + } else /* if (rhoOffset >= 32) */ { + var tMsw = (laneLsw << (rhoOffset - 32)) | (laneMsw >>> (64 - rhoOffset)); + var tLsw = (laneMsw << (rhoOffset - 32)) | (laneLsw >>> (64 - rhoOffset)); + } + + // Transpose lanes + var TPiLane = T[PI_INDEXES[laneIndex]]; + TPiLane.high = tMsw; + TPiLane.low = tLsw; + } + + // Rho pi at x = y = 0 + var T0 = T[0]; + var state0 = state[0]; + T0.high = state0.high; + T0.low = state0.low; + + // Chi + for (var x = 0; x < 5; x++) { + for (var y = 0; y < 5; y++) { + // Shortcuts + var laneIndex = x + 5 * y; + var lane = state[laneIndex]; + var TLane = T[laneIndex]; + var Tx1Lane = T[((x + 1) % 5) + 5 * y]; + var Tx2Lane = T[((x + 2) % 5) + 5 * y]; + + // Mix rows + lane.high = TLane.high ^ (~Tx1Lane.high & Tx2Lane.high); + lane.low = TLane.low ^ (~Tx1Lane.low & Tx2Lane.low); + } + } + + // Iota + var lane = state[0]; + var roundConstant = ROUND_CONSTANTS[round]; + lane.high ^= roundConstant.high; + lane.low ^= roundConstant.low;; + } + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + var blockSizeBits = this.blockSize * 32; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x1 << (24 - nBitsLeft % 32); + dataWords[((Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits) >>> 5) - 1] |= 0x80; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Shortcuts + var state = this._state; + var outputLengthBytes = this.cfg.outputLength / 8; + var outputLengthLanes = outputLengthBytes / 8; + + // Squeeze + var hashWords = []; + for (var i = 0; i < outputLengthLanes; i++) { + // Shortcuts + var lane = state[i]; + var laneMsw = lane.high; + var laneLsw = lane.low; + + // Swap endian + laneMsw = ( + (((laneMsw << 8) | (laneMsw >>> 24)) & 0x00ff00ff) | + (((laneMsw << 24) | (laneMsw >>> 8)) & 0xff00ff00) + ); + laneLsw = ( + (((laneLsw << 8) | (laneLsw >>> 24)) & 0x00ff00ff) | + (((laneLsw << 24) | (laneLsw >>> 8)) & 0xff00ff00) + ); + + // Squeeze state to retrieve hash + hashWords.push(laneLsw); + hashWords.push(laneMsw); + } + + // Return final computed hash + return new WordArray.init(hashWords, outputLengthBytes); + }, + + clone: function () { + var clone = Hasher.clone.call(this); + + var state = clone._state = this._state.slice(0); + for (var i = 0; i < 25; i++) { + state[i] = state[i].clone(); + } + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA3('message'); + * var hash = CryptoJS.SHA3(wordArray); + */ + C.SHA3 = Hasher._createHelper(SHA3); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA3(message, key); + */ + C.HmacSHA3 = Hasher._createHmacHelper(SHA3); + }(Math)); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Hasher = C_lib.Hasher; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var X64WordArray = C_x64.WordArray; + var C_algo = C.algo; + + function X64Word_create() { + return X64Word.create.apply(X64Word, arguments); + } + + // Constants + var K = [ + X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd), + X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc), + X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019), + X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118), + X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe), + X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2), + X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1), + X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694), + X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3), + X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65), + X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483), + X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5), + X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210), + X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4), + X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725), + X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70), + X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926), + X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df), + X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8), + X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b), + X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001), + X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30), + X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910), + X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8), + X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53), + X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8), + X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb), + X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3), + X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60), + X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec), + X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9), + X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b), + X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207), + X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178), + X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6), + X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b), + X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493), + X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c), + X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a), + X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817) + ]; + + // Reusable objects + var W = []; + (function () { + for (var i = 0; i < 80; i++) { + W[i] = X64Word_create(); + } + }()); + + /** + * SHA-512 hash algorithm. + */ + var SHA512 = C_algo.SHA512 = Hasher.extend({ + _doReset: function () { + this._hash = new X64WordArray.init([ + new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b), + new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1), + new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f), + new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179) + ]); + }, + + _doProcessBlock: function (M, offset) { + // Shortcuts + var H = this._hash.words; + + var H0 = H[0]; + var H1 = H[1]; + var H2 = H[2]; + var H3 = H[3]; + var H4 = H[4]; + var H5 = H[5]; + var H6 = H[6]; + var H7 = H[7]; + + var H0h = H0.high; + var H0l = H0.low; + var H1h = H1.high; + var H1l = H1.low; + var H2h = H2.high; + var H2l = H2.low; + var H3h = H3.high; + var H3l = H3.low; + var H4h = H4.high; + var H4l = H4.low; + var H5h = H5.high; + var H5l = H5.low; + var H6h = H6.high; + var H6l = H6.low; + var H7h = H7.high; + var H7l = H7.low; + + // Working variables + var ah = H0h; + var al = H0l; + var bh = H1h; + var bl = H1l; + var ch = H2h; + var cl = H2l; + var dh = H3h; + var dl = H3l; + var eh = H4h; + var el = H4l; + var fh = H5h; + var fl = H5l; + var gh = H6h; + var gl = H6l; + var hh = H7h; + var hl = H7l; + + // Rounds + for (var i = 0; i < 80; i++) { + // Shortcut + var Wi = W[i]; + + // Extend message + if (i < 16) { + var Wih = Wi.high = M[offset + i * 2] | 0; + var Wil = Wi.low = M[offset + i * 2 + 1] | 0; + } else { + // Gamma0 + var gamma0x = W[i - 15]; + var gamma0xh = gamma0x.high; + var gamma0xl = gamma0x.low; + var gamma0h = ((gamma0xh >>> 1) | (gamma0xl << 31)) ^ ((gamma0xh >>> 8) | (gamma0xl << 24)) ^ (gamma0xh >>> 7); + var gamma0l = ((gamma0xl >>> 1) | (gamma0xh << 31)) ^ ((gamma0xl >>> 8) | (gamma0xh << 24)) ^ ((gamma0xl >>> 7) | (gamma0xh << 25)); + + // Gamma1 + var gamma1x = W[i - 2]; + var gamma1xh = gamma1x.high; + var gamma1xl = gamma1x.low; + var gamma1h = ((gamma1xh >>> 19) | (gamma1xl << 13)) ^ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^ (gamma1xh >>> 6); + var gamma1l = ((gamma1xl >>> 19) | (gamma1xh << 13)) ^ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^ ((gamma1xl >>> 6) | (gamma1xh << 26)); + + // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16] + var Wi7 = W[i - 7]; + var Wi7h = Wi7.high; + var Wi7l = Wi7.low; + + var Wi16 = W[i - 16]; + var Wi16h = Wi16.high; + var Wi16l = Wi16.low; + + var Wil = gamma0l + Wi7l; + var Wih = gamma0h + Wi7h + ((Wil >>> 0) < (gamma0l >>> 0) ? 1 : 0); + var Wil = Wil + gamma1l; + var Wih = Wih + gamma1h + ((Wil >>> 0) < (gamma1l >>> 0) ? 1 : 0); + var Wil = Wil + Wi16l; + var Wih = Wih + Wi16h + ((Wil >>> 0) < (Wi16l >>> 0) ? 1 : 0); + + Wi.high = Wih; + Wi.low = Wil; + } + + var chh = (eh & fh) ^ (~eh & gh); + var chl = (el & fl) ^ (~el & gl); + var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch); + var majl = (al & bl) ^ (al & cl) ^ (bl & cl); + + var sigma0h = ((ah >>> 28) | (al << 4)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7)); + var sigma0l = ((al >>> 28) | (ah << 4)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7)); + var sigma1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((eh << 23) | (el >>> 9)); + var sigma1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((el << 23) | (eh >>> 9)); + + // t1 = h + sigma1 + ch + K[i] + W[i] + var Ki = K[i]; + var Kih = Ki.high; + var Kil = Ki.low; + + var t1l = hl + sigma1l; + var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0); + var t1l = t1l + chl; + var t1h = t1h + chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0); + var t1l = t1l + Kil; + var t1h = t1h + Kih + ((t1l >>> 0) < (Kil >>> 0) ? 1 : 0); + var t1l = t1l + Wil; + var t1h = t1h + Wih + ((t1l >>> 0) < (Wil >>> 0) ? 1 : 0); + + // t2 = sigma0 + maj + var t2l = sigma0l + majl; + var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0); + + // Update working variables + hh = gh; + hl = gl; + gh = fh; + gl = fl; + fh = eh; + fl = el; + el = (dl + t1l) | 0; + eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0; + dh = ch; + dl = cl; + ch = bh; + cl = bl; + bh = ah; + bl = al; + al = (t1l + t2l) | 0; + ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0; + } + + // Intermediate hash value + H0l = H0.low = (H0l + al); + H0.high = (H0h + ah + ((H0l >>> 0) < (al >>> 0) ? 1 : 0)); + H1l = H1.low = (H1l + bl); + H1.high = (H1h + bh + ((H1l >>> 0) < (bl >>> 0) ? 1 : 0)); + H2l = H2.low = (H2l + cl); + H2.high = (H2h + ch + ((H2l >>> 0) < (cl >>> 0) ? 1 : 0)); + H3l = H3.low = (H3l + dl); + H3.high = (H3h + dh + ((H3l >>> 0) < (dl >>> 0) ? 1 : 0)); + H4l = H4.low = (H4l + el); + H4.high = (H4h + eh + ((H4l >>> 0) < (el >>> 0) ? 1 : 0)); + H5l = H5.low = (H5l + fl); + H5.high = (H5h + fh + ((H5l >>> 0) < (fl >>> 0) ? 1 : 0)); + H6l = H6.low = (H6l + gl); + H6.high = (H6h + gh + ((H6l >>> 0) < (gl >>> 0) ? 1 : 0)); + H7l = H7.low = (H7l + hl); + H7.high = (H7h + hh + ((H7l >>> 0) < (hl >>> 0) ? 1 : 0)); + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 30] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 128) >>> 10) << 5) + 31] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Convert hash to 32-bit word array before returning + var hash = this._hash.toX32(); + + // Return final computed hash + return hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + }, + + blockSize: 1024/32 + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA512('message'); + * var hash = CryptoJS.SHA512(wordArray); + */ + C.SHA512 = Hasher._createHelper(SHA512); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA512(message, key); + */ + C.HmacSHA512 = Hasher._createHmacHelper(SHA512); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_x64 = C.x64; + var X64Word = C_x64.Word; + var X64WordArray = C_x64.WordArray; + var C_algo = C.algo; + var SHA512 = C_algo.SHA512; + + /** + * SHA-384 hash algorithm. + */ + var SHA384 = C_algo.SHA384 = SHA512.extend({ + _doReset: function () { + this._hash = new X64WordArray.init([ + new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507), + new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939), + new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511), + new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4) + ]); + }, + + _doFinalize: function () { + var hash = SHA512._doFinalize.call(this); + + hash.sigBytes -= 16; + + return hash; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA384('message'); + * var hash = CryptoJS.SHA384(wordArray); + */ + C.SHA384 = SHA512._createHelper(SHA384); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA384(message, key); + */ + C.HmacSHA384 = SHA512._createHmacHelper(SHA384); + }()); + + + /** + * Cipher core components. + */ + CryptoJS.lib.Cipher || (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm; + var C_enc = C.enc; + var Utf8 = C_enc.Utf8; + var Base64 = C_enc.Base64; + var C_algo = C.algo; + var EvpKDF = C_algo.EvpKDF; + + /** + * Abstract base cipher template. + * + * @property {number} keySize This cipher's key size. Default: 4 (128 bits) + * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits) + * @property {number} _ENC_XFORM_MODE A constant representing encryption mode. + * @property {number} _DEC_XFORM_MODE A constant representing decryption mode. + */ + var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + * + * @property {WordArray} iv The IV to use for this operation. + */ + cfg: Base.extend(), + + /** + * Creates this cipher in encryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray }); + */ + createEncryptor: function (key, cfg) { + return this.create(this._ENC_XFORM_MODE, key, cfg); + }, + + /** + * Creates this cipher in decryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray }); + */ + createDecryptor: function (key, cfg) { + return this.create(this._DEC_XFORM_MODE, key, cfg); + }, + + /** + * Initializes a newly created cipher. + * + * @param {number} xformMode Either the encryption or decryption transormation mode constant. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @example + * + * var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray }); + */ + init: function (xformMode, key, cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Store transform mode and key + this._xformMode = xformMode; + this._key = key; + + // Set initial values + this.reset(); + }, + + /** + * Resets this cipher to its initial state. + * + * @example + * + * cipher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-cipher logic + this._doReset(); + }, + + /** + * Adds data to be encrypted or decrypted. + * + * @param {WordArray|string} dataUpdate The data to encrypt or decrypt. + * + * @return {WordArray} The data after processing. + * + * @example + * + * var encrypted = cipher.process('data'); + * var encrypted = cipher.process(wordArray); + */ + process: function (dataUpdate) { + // Append + this._append(dataUpdate); + + // Process available blocks + return this._process(); + }, + + /** + * Finalizes the encryption or decryption process. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt. + * + * @return {WordArray} The data after final processing. + * + * @example + * + * var encrypted = cipher.finalize(); + * var encrypted = cipher.finalize('data'); + * var encrypted = cipher.finalize(wordArray); + */ + finalize: function (dataUpdate) { + // Final data update + if (dataUpdate) { + this._append(dataUpdate); + } + + // Perform concrete-cipher logic + var finalProcessedData = this._doFinalize(); + + return finalProcessedData; + }, + + keySize: 128/32, + + ivSize: 128/32, + + _ENC_XFORM_MODE: 1, + + _DEC_XFORM_MODE: 2, + + /** + * Creates shortcut functions to a cipher's object interface. + * + * @param {Cipher} cipher The cipher to create a helper for. + * + * @return {Object} An object with encrypt and decrypt shortcut functions. + * + * @static + * + * @example + * + * var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES); + */ + _createHelper: (function () { + function selectCipherStrategy(key) { + if (typeof key == 'string') { + return PasswordBasedCipher; + } else { + return SerializableCipher; + } + } + + return function (cipher) { + return { + encrypt: function (message, key, cfg) { + return selectCipherStrategy(key).encrypt(cipher, message, key, cfg); + }, + + decrypt: function (ciphertext, key, cfg) { + return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg); + } + }; + }; + }()) + }); + + /** + * Abstract base stream cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits) + */ + var StreamCipher = C_lib.StreamCipher = Cipher.extend({ + _doFinalize: function () { + // Process partial blocks + var finalProcessedBlocks = this._process(!!'flush'); + + return finalProcessedBlocks; + }, + + blockSize: 1 + }); + + /** + * Mode namespace. + */ + var C_mode = C.mode = {}; + + /** + * Abstract base block cipher mode template. + */ + var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({ + /** + * Creates this mode for encryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words); + */ + createEncryptor: function (cipher, iv) { + return this.Encryptor.create(cipher, iv); + }, + + /** + * Creates this mode for decryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words); + */ + createDecryptor: function (cipher, iv) { + return this.Decryptor.create(cipher, iv); + }, + + /** + * Initializes a newly created mode. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @example + * + * var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words); + */ + init: function (cipher, iv) { + this._cipher = cipher; + this._iv = iv; + } + }); + + /** + * Cipher Block Chaining mode. + */ + var CBC = C_mode.CBC = (function () { + /** + * Abstract base CBC mode. + */ + var CBC = BlockCipherMode.extend(); + + /** + * CBC encryptor. + */ + CBC.Encryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // XOR and encrypt + xorBlock.call(this, words, offset, blockSize); + cipher.encryptBlock(words, offset); + + // Remember this block to use with next block + this._prevBlock = words.slice(offset, offset + blockSize); + } + }); + + /** + * CBC decryptor. + */ + CBC.Decryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // Remember this block to use with next block + var thisBlock = words.slice(offset, offset + blockSize); + + // Decrypt and XOR + cipher.decryptBlock(words, offset); + xorBlock.call(this, words, offset, blockSize); + + // This block becomes the previous block + this._prevBlock = thisBlock; + } + }); + + function xorBlock(words, offset, blockSize) { + // Shortcut + var iv = this._iv; + + // Choose mixing block + if (iv) { + var block = iv; + + // Remove IV for subsequent blocks + this._iv = undefined; + } else { + var block = this._prevBlock; + } + + // XOR blocks + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= block[i]; + } + } + + return CBC; + }()); + + /** + * Padding namespace. + */ + var C_pad = C.pad = {}; + + /** + * PKCS #5/7 padding strategy. + */ + var Pkcs7 = C_pad.Pkcs7 = { + /** + * Pads data using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to pad. + * @param {number} blockSize The multiple that the data should be padded to. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.pad(wordArray, 4); + */ + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; + + // Create padding word + var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes; + + // Create padding + var paddingWords = []; + for (var i = 0; i < nPaddingBytes; i += 4) { + paddingWords.push(paddingWord); + } + var padding = WordArray.create(paddingWords, nPaddingBytes); + + // Add padding + data.concat(padding); + }, + + /** + * Unpads data that had been padded using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to unpad. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.unpad(wordArray); + */ + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + /** + * Abstract base block cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits) + */ + var BlockCipher = C_lib.BlockCipher = Cipher.extend({ + /** + * Configuration options. + * + * @property {Mode} mode The block mode to use. Default: CBC + * @property {Padding} padding The padding strategy to use. Default: Pkcs7 + */ + cfg: Cipher.cfg.extend({ + mode: CBC, + padding: Pkcs7 + }), + + reset: function () { + // Reset cipher + Cipher.reset.call(this); + + // Shortcuts + var cfg = this.cfg; + var iv = cfg.iv; + var mode = cfg.mode; + + // Reset block mode + if (this._xformMode == this._ENC_XFORM_MODE) { + var modeCreator = mode.createEncryptor; + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + var modeCreator = mode.createDecryptor; + // Keep at least one block in the buffer for unpadding + this._minBufferSize = 1; + } + + if (this._mode && this._mode.__creator == modeCreator) { + this._mode.init(this, iv && iv.words); + } else { + this._mode = modeCreator.call(mode, this, iv && iv.words); + this._mode.__creator = modeCreator; + } + }, + + _doProcessBlock: function (words, offset) { + this._mode.processBlock(words, offset); + }, + + _doFinalize: function () { + // Shortcut + var padding = this.cfg.padding; + + // Finalize + if (this._xformMode == this._ENC_XFORM_MODE) { + // Pad data + padding.pad(this._data, this.blockSize); + + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + + // Unpad data + padding.unpad(finalProcessedBlocks); + } + + return finalProcessedBlocks; + }, + + blockSize: 128/32 + }); + + /** + * A collection of cipher parameters. + * + * @property {WordArray} ciphertext The raw ciphertext. + * @property {WordArray} key The key to this ciphertext. + * @property {WordArray} iv The IV used in the ciphering operation. + * @property {WordArray} salt The salt used with a key derivation function. + * @property {Cipher} algorithm The cipher algorithm. + * @property {Mode} mode The block mode used in the ciphering operation. + * @property {Padding} padding The padding scheme used in the ciphering operation. + * @property {number} blockSize The block size of the cipher. + * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string. + */ + var CipherParams = C_lib.CipherParams = Base.extend({ + /** + * Initializes a newly created cipher params object. + * + * @param {Object} cipherParams An object with any of the possible cipher parameters. + * + * @example + * + * var cipherParams = CryptoJS.lib.CipherParams.create({ + * ciphertext: ciphertextWordArray, + * key: keyWordArray, + * iv: ivWordArray, + * salt: saltWordArray, + * algorithm: CryptoJS.algo.AES, + * mode: CryptoJS.mode.CBC, + * padding: CryptoJS.pad.PKCS7, + * blockSize: 4, + * formatter: CryptoJS.format.OpenSSL + * }); + */ + init: function (cipherParams) { + this.mixIn(cipherParams); + }, + + /** + * Converts this cipher params object to a string. + * + * @param {Format} formatter (Optional) The formatting strategy to use. + * + * @return {string} The stringified cipher params. + * + * @throws Error If neither the formatter nor the default formatter is set. + * + * @example + * + * var string = cipherParams + ''; + * var string = cipherParams.toString(); + * var string = cipherParams.toString(CryptoJS.format.OpenSSL); + */ + toString: function (formatter) { + return (formatter || this.formatter).stringify(this); + } + }); + + /** + * Format namespace. + */ + var C_format = C.format = {}; + + /** + * OpenSSL formatting strategy. + */ + var OpenSSLFormatter = C_format.OpenSSL = { + /** + * Converts a cipher params object to an OpenSSL-compatible string. + * + * @param {CipherParams} cipherParams The cipher params object. + * + * @return {string} The OpenSSL-compatible string. + * + * @static + * + * @example + * + * var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams); + */ + stringify: function (cipherParams) { + // Shortcuts + var ciphertext = cipherParams.ciphertext; + var salt = cipherParams.salt; + + // Format + if (salt) { + var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext); + } else { + var wordArray = ciphertext; + } + + return wordArray.toString(Base64); + }, + + /** + * Converts an OpenSSL-compatible string to a cipher params object. + * + * @param {string} openSSLStr The OpenSSL-compatible string. + * + * @return {CipherParams} The cipher params object. + * + * @static + * + * @example + * + * var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString); + */ + parse: function (openSSLStr) { + // Parse base64 + var ciphertext = Base64.parse(openSSLStr); + + // Shortcut + var ciphertextWords = ciphertext.words; + + // Test for salt + if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) { + // Extract salt + var salt = WordArray.create(ciphertextWords.slice(2, 4)); + + // Remove salt from ciphertext + ciphertextWords.splice(0, 4); + ciphertext.sigBytes -= 16; + } + + return CipherParams.create({ ciphertext: ciphertext, salt: salt }); + } + }; + + /** + * A cipher wrapper that returns ciphertext as a serializable cipher params object. + */ + var SerializableCipher = C_lib.SerializableCipher = Base.extend({ + /** + * Configuration options. + * + * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL + */ + cfg: Base.extend({ + format: OpenSSLFormatter + }), + + /** + * Encrypts a message. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv }); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Encrypt + var encryptor = cipher.createEncryptor(key, cfg); + var ciphertext = encryptor.finalize(message); + + // Shortcut + var cipherCfg = encryptor.cfg; + + // Create and return serializable cipher params + return CipherParams.create({ + ciphertext: ciphertext, + key: key, + iv: cipherCfg.iv, + algorithm: cipher, + mode: cipherCfg.mode, + padding: cipherCfg.padding, + blockSize: cipher.blockSize, + formatter: cfg.format + }); + }, + + /** + * Decrypts serialized ciphertext. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Decrypt + var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext); + + return plaintext; + }, + + /** + * Converts serialized ciphertext to CipherParams, + * else assumed CipherParams already and returns ciphertext unchanged. + * + * @param {CipherParams|string} ciphertext The ciphertext. + * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext. + * + * @return {CipherParams} The unserialized ciphertext. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format); + */ + _parse: function (ciphertext, format) { + if (typeof ciphertext == 'string') { + return format.parse(ciphertext, this); + } else { + return ciphertext; + } + } + }); + + /** + * Key derivation function namespace. + */ + var C_kdf = C.kdf = {}; + + /** + * OpenSSL key derivation function. + */ + var OpenSSLKdf = C_kdf.OpenSSL = { + /** + * Derives a key and IV from a password. + * + * @param {string} password The password to derive from. + * @param {number} keySize The size in words of the key to generate. + * @param {number} ivSize The size in words of the IV to generate. + * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly. + * + * @return {CipherParams} A cipher params object with the key, IV, and salt. + * + * @static + * + * @example + * + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32); + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt'); + */ + execute: function (password, keySize, ivSize, salt) { + // Generate random salt + if (!salt) { + salt = WordArray.random(64/8); + } + + // Derive key and IV + var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt); + + // Separate key and IV + var iv = WordArray.create(key.words.slice(keySize), ivSize * 4); + key.sigBytes = keySize * 4; + + // Return params + return CipherParams.create({ key: key, iv: iv, salt: salt }); + } + }; + + /** + * A serializable cipher wrapper that derives the key from a password, + * and returns ciphertext as a serializable cipher params object. + */ + var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({ + /** + * Configuration options. + * + * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL + */ + cfg: SerializableCipher.cfg.extend({ + kdf: OpenSSLKdf + }), + + /** + * Encrypts a message using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password'); + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Encrypt + var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg); + + // Mix in derived params + ciphertext.mixIn(derivedParams); + + return ciphertext; + }, + + /** + * Decrypts serialized ciphertext using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Decrypt + var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg); + + return plaintext; + } + }); + }()); + + + /** + * Cipher Feedback block mode. + */ + CryptoJS.mode.CFB = (function () { + var CFB = CryptoJS.lib.BlockCipherMode.extend(); + + CFB.Encryptor = CFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); + + // Remember this block to use with next block + this._prevBlock = words.slice(offset, offset + blockSize); + } + }); + + CFB.Decryptor = CFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // Remember this block to use with next block + var thisBlock = words.slice(offset, offset + blockSize); + + generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher); + + // This block becomes the previous block + this._prevBlock = thisBlock; + } + }); + + function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) { + // Shortcut + var iv = this._iv; + + // Generate keystream + if (iv) { + var keystream = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } else { + var keystream = this._prevBlock; + } + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + + return CFB; + }()); + + + /** + * Electronic Codebook block mode. + */ + CryptoJS.mode.ECB = (function () { + var ECB = CryptoJS.lib.BlockCipherMode.extend(); + + ECB.Encryptor = ECB.extend({ + processBlock: function (words, offset) { + this._cipher.encryptBlock(words, offset); + } + }); + + ECB.Decryptor = ECB.extend({ + processBlock: function (words, offset) { + this._cipher.decryptBlock(words, offset); + } + }); + + return ECB; + }()); + + + /** + * ANSI X.923 padding strategy. + */ + CryptoJS.pad.AnsiX923 = { + pad: function (data, blockSize) { + // Shortcuts + var dataSigBytes = data.sigBytes; + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes; + + // Compute last byte position + var lastBytePos = dataSigBytes + nPaddingBytes - 1; + + // Pad + data.clamp(); + data.words[lastBytePos >>> 2] |= nPaddingBytes << (24 - (lastBytePos % 4) * 8); + data.sigBytes += nPaddingBytes; + }, + + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + + /** + * ISO 10126 padding strategy. + */ + CryptoJS.pad.Iso10126 = { + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; + + // Pad + data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)). + concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1)); + }, + + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + + /** + * ISO/IEC 9797-1 Padding Method 2. + */ + CryptoJS.pad.Iso97971 = { + pad: function (data, blockSize) { + // Add 0x80 byte + data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1)); + + // Zero pad the rest + CryptoJS.pad.ZeroPadding.pad(data, blockSize); + }, + + unpad: function (data) { + // Remove zero padding + CryptoJS.pad.ZeroPadding.unpad(data); + + // Remove one more byte -- the 0x80 byte + data.sigBytes--; + } + }; + + + /** + * Output Feedback block mode. + */ + CryptoJS.mode.OFB = (function () { + var OFB = CryptoJS.lib.BlockCipherMode.extend(); + + var Encryptor = OFB.Encryptor = OFB.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var keystream = this._keystream; + + // Generate keystream + if (iv) { + keystream = this._keystream = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + OFB.Decryptor = Encryptor; + + return OFB; + }()); + + + /** + * A noop padding strategy. + */ + CryptoJS.pad.NoPadding = { + pad: function () { + }, + + unpad: function () { + } + }; + + + (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var CipherParams = C_lib.CipherParams; + var C_enc = C.enc; + var Hex = C_enc.Hex; + var C_format = C.format; + + var HexFormatter = C_format.Hex = { + /** + * Converts the ciphertext of a cipher params object to a hexadecimally encoded string. + * + * @param {CipherParams} cipherParams The cipher params object. + * + * @return {string} The hexadecimally encoded string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.format.Hex.stringify(cipherParams); + */ + stringify: function (cipherParams) { + return cipherParams.ciphertext.toString(Hex); + }, + + /** + * Converts a hexadecimally encoded ciphertext string to a cipher params object. + * + * @param {string} input The hexadecimally encoded string. + * + * @return {CipherParams} The cipher params object. + * + * @static + * + * @example + * + * var cipherParams = CryptoJS.format.Hex.parse(hexString); + */ + parse: function (input) { + var ciphertext = Hex.parse(input); + return CipherParams.create({ ciphertext: ciphertext }); + } + }; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var BlockCipher = C_lib.BlockCipher; + var C_algo = C.algo; + + // Lookup tables + var SBOX = []; + var INV_SBOX = []; + var SUB_MIX_0 = []; + var SUB_MIX_1 = []; + var SUB_MIX_2 = []; + var SUB_MIX_3 = []; + var INV_SUB_MIX_0 = []; + var INV_SUB_MIX_1 = []; + var INV_SUB_MIX_2 = []; + var INV_SUB_MIX_3 = []; + + // Compute lookup tables + (function () { + // Compute double table + var d = []; + for (var i = 0; i < 256; i++) { + if (i < 128) { + d[i] = i << 1; + } else { + d[i] = (i << 1) ^ 0x11b; + } + } + + // Walk GF(2^8) + var x = 0; + var xi = 0; + for (var i = 0; i < 256; i++) { + // Compute sbox + var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); + sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; + SBOX[x] = sx; + INV_SBOX[sx] = x; + + // Compute multiplication + var x2 = d[x]; + var x4 = d[x2]; + var x8 = d[x4]; + + // Compute sub bytes, mix columns tables + var t = (d[sx] * 0x101) ^ (sx * 0x1010100); + SUB_MIX_0[x] = (t << 24) | (t >>> 8); + SUB_MIX_1[x] = (t << 16) | (t >>> 16); + SUB_MIX_2[x] = (t << 8) | (t >>> 24); + SUB_MIX_3[x] = t; + + // Compute inv sub bytes, inv mix columns tables + var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); + INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8); + INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16); + INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24); + INV_SUB_MIX_3[sx] = t; + + // Compute next counter + if (!x) { + x = xi = 1; + } else { + x = x2 ^ d[d[d[x8 ^ x2]]]; + xi ^= d[d[xi]]; + } + } + }()); + + // Precomputed Rcon lookup + var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; + + /** + * AES block cipher algorithm. + */ + var AES = C_algo.AES = BlockCipher.extend({ + _doReset: function () { + // Skip reset of nRounds has been set before and key did not change + if (this._nRounds && this._keyPriorReset === this._key) { + return; + } + + // Shortcuts + var key = this._keyPriorReset = this._key; + var keyWords = key.words; + var keySize = key.sigBytes / 4; + + // Compute number of rounds + var nRounds = this._nRounds = keySize + 6; + + // Compute number of key schedule rows + var ksRows = (nRounds + 1) * 4; + + // Compute key schedule + var keySchedule = this._keySchedule = []; + for (var ksRow = 0; ksRow < ksRows; ksRow++) { + if (ksRow < keySize) { + keySchedule[ksRow] = keyWords[ksRow]; + } else { + var t = keySchedule[ksRow - 1]; + + if (!(ksRow % keySize)) { + // Rot word + t = (t << 8) | (t >>> 24); + + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + + // Mix Rcon + t ^= RCON[(ksRow / keySize) | 0] << 24; + } else if (keySize > 6 && ksRow % keySize == 4) { + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + } + + keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t; + } + } + + // Compute inv key schedule + var invKeySchedule = this._invKeySchedule = []; + for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) { + var ksRow = ksRows - invKsRow; + + if (invKsRow % 4) { + var t = keySchedule[ksRow]; + } else { + var t = keySchedule[ksRow - 4]; + } + + if (invKsRow < 4 || ksRow <= 4) { + invKeySchedule[invKsRow] = t; + } else { + invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^ + INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]]; + } + } + }, + + encryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX); + }, + + decryptBlock: function (M, offset) { + // Swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + + this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX); + + // Inv swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + }, + + _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) { + // Shortcut + var nRounds = this._nRounds; + + // Get input, add round key + var s0 = M[offset] ^ keySchedule[0]; + var s1 = M[offset + 1] ^ keySchedule[1]; + var s2 = M[offset + 2] ^ keySchedule[2]; + var s3 = M[offset + 3] ^ keySchedule[3]; + + // Key schedule row counter + var ksRow = 4; + + // Rounds + for (var round = 1; round < nRounds; round++) { + // Shift rows, sub bytes, mix columns, add round key + var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++]; + var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++]; + var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++]; + var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++]; + + // Update state + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + // Shift rows, sub bytes, add round key + var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++]; + var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++]; + var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++]; + var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++]; + + // Set output + M[offset] = t0; + M[offset + 1] = t1; + M[offset + 2] = t2; + M[offset + 3] = t3; + }, + + keySize: 256/32 + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.AES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg); + */ + C.AES = BlockCipher._createHelper(AES); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var BlockCipher = C_lib.BlockCipher; + var C_algo = C.algo; + + // Permuted Choice 1 constants + var PC1 = [ + 57, 49, 41, 33, 25, 17, 9, 1, + 58, 50, 42, 34, 26, 18, 10, 2, + 59, 51, 43, 35, 27, 19, 11, 3, + 60, 52, 44, 36, 63, 55, 47, 39, + 31, 23, 15, 7, 62, 54, 46, 38, + 30, 22, 14, 6, 61, 53, 45, 37, + 29, 21, 13, 5, 28, 20, 12, 4 + ]; + + // Permuted Choice 2 constants + var PC2 = [ + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 + ]; + + // Cumulative bit shift constants + var BIT_SHIFTS = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28]; + + // SBOXes and round permutation constants + var SBOX_P = [ + { + 0x0: 0x808200, + 0x10000000: 0x8000, + 0x20000000: 0x808002, + 0x30000000: 0x2, + 0x40000000: 0x200, + 0x50000000: 0x808202, + 0x60000000: 0x800202, + 0x70000000: 0x800000, + 0x80000000: 0x202, + 0x90000000: 0x800200, + 0xa0000000: 0x8200, + 0xb0000000: 0x808000, + 0xc0000000: 0x8002, + 0xd0000000: 0x800002, + 0xe0000000: 0x0, + 0xf0000000: 0x8202, + 0x8000000: 0x0, + 0x18000000: 0x808202, + 0x28000000: 0x8202, + 0x38000000: 0x8000, + 0x48000000: 0x808200, + 0x58000000: 0x200, + 0x68000000: 0x808002, + 0x78000000: 0x2, + 0x88000000: 0x800200, + 0x98000000: 0x8200, + 0xa8000000: 0x808000, + 0xb8000000: 0x800202, + 0xc8000000: 0x800002, + 0xd8000000: 0x8002, + 0xe8000000: 0x202, + 0xf8000000: 0x800000, + 0x1: 0x8000, + 0x10000001: 0x2, + 0x20000001: 0x808200, + 0x30000001: 0x800000, + 0x40000001: 0x808002, + 0x50000001: 0x8200, + 0x60000001: 0x200, + 0x70000001: 0x800202, + 0x80000001: 0x808202, + 0x90000001: 0x808000, + 0xa0000001: 0x800002, + 0xb0000001: 0x8202, + 0xc0000001: 0x202, + 0xd0000001: 0x800200, + 0xe0000001: 0x8002, + 0xf0000001: 0x0, + 0x8000001: 0x808202, + 0x18000001: 0x808000, + 0x28000001: 0x800000, + 0x38000001: 0x200, + 0x48000001: 0x8000, + 0x58000001: 0x800002, + 0x68000001: 0x2, + 0x78000001: 0x8202, + 0x88000001: 0x8002, + 0x98000001: 0x800202, + 0xa8000001: 0x202, + 0xb8000001: 0x808200, + 0xc8000001: 0x800200, + 0xd8000001: 0x0, + 0xe8000001: 0x8200, + 0xf8000001: 0x808002 + }, + { + 0x0: 0x40084010, + 0x1000000: 0x4000, + 0x2000000: 0x80000, + 0x3000000: 0x40080010, + 0x4000000: 0x40000010, + 0x5000000: 0x40084000, + 0x6000000: 0x40004000, + 0x7000000: 0x10, + 0x8000000: 0x84000, + 0x9000000: 0x40004010, + 0xa000000: 0x40000000, + 0xb000000: 0x84010, + 0xc000000: 0x80010, + 0xd000000: 0x0, + 0xe000000: 0x4010, + 0xf000000: 0x40080000, + 0x800000: 0x40004000, + 0x1800000: 0x84010, + 0x2800000: 0x10, + 0x3800000: 0x40004010, + 0x4800000: 0x40084010, + 0x5800000: 0x40000000, + 0x6800000: 0x80000, + 0x7800000: 0x40080010, + 0x8800000: 0x80010, + 0x9800000: 0x0, + 0xa800000: 0x4000, + 0xb800000: 0x40080000, + 0xc800000: 0x40000010, + 0xd800000: 0x84000, + 0xe800000: 0x40084000, + 0xf800000: 0x4010, + 0x10000000: 0x0, + 0x11000000: 0x40080010, + 0x12000000: 0x40004010, + 0x13000000: 0x40084000, + 0x14000000: 0x40080000, + 0x15000000: 0x10, + 0x16000000: 0x84010, + 0x17000000: 0x4000, + 0x18000000: 0x4010, + 0x19000000: 0x80000, + 0x1a000000: 0x80010, + 0x1b000000: 0x40000010, + 0x1c000000: 0x84000, + 0x1d000000: 0x40004000, + 0x1e000000: 0x40000000, + 0x1f000000: 0x40084010, + 0x10800000: 0x84010, + 0x11800000: 0x80000, + 0x12800000: 0x40080000, + 0x13800000: 0x4000, + 0x14800000: 0x40004000, + 0x15800000: 0x40084010, + 0x16800000: 0x10, + 0x17800000: 0x40000000, + 0x18800000: 0x40084000, + 0x19800000: 0x40000010, + 0x1a800000: 0x40004010, + 0x1b800000: 0x80010, + 0x1c800000: 0x0, + 0x1d800000: 0x4010, + 0x1e800000: 0x40080010, + 0x1f800000: 0x84000 + }, + { + 0x0: 0x104, + 0x100000: 0x0, + 0x200000: 0x4000100, + 0x300000: 0x10104, + 0x400000: 0x10004, + 0x500000: 0x4000004, + 0x600000: 0x4010104, + 0x700000: 0x4010000, + 0x800000: 0x4000000, + 0x900000: 0x4010100, + 0xa00000: 0x10100, + 0xb00000: 0x4010004, + 0xc00000: 0x4000104, + 0xd00000: 0x10000, + 0xe00000: 0x4, + 0xf00000: 0x100, + 0x80000: 0x4010100, + 0x180000: 0x4010004, + 0x280000: 0x0, + 0x380000: 0x4000100, + 0x480000: 0x4000004, + 0x580000: 0x10000, + 0x680000: 0x10004, + 0x780000: 0x104, + 0x880000: 0x4, + 0x980000: 0x100, + 0xa80000: 0x4010000, + 0xb80000: 0x10104, + 0xc80000: 0x10100, + 0xd80000: 0x4000104, + 0xe80000: 0x4010104, + 0xf80000: 0x4000000, + 0x1000000: 0x4010100, + 0x1100000: 0x10004, + 0x1200000: 0x10000, + 0x1300000: 0x4000100, + 0x1400000: 0x100, + 0x1500000: 0x4010104, + 0x1600000: 0x4000004, + 0x1700000: 0x0, + 0x1800000: 0x4000104, + 0x1900000: 0x4000000, + 0x1a00000: 0x4, + 0x1b00000: 0x10100, + 0x1c00000: 0x4010000, + 0x1d00000: 0x104, + 0x1e00000: 0x10104, + 0x1f00000: 0x4010004, + 0x1080000: 0x4000000, + 0x1180000: 0x104, + 0x1280000: 0x4010100, + 0x1380000: 0x0, + 0x1480000: 0x10004, + 0x1580000: 0x4000100, + 0x1680000: 0x100, + 0x1780000: 0x4010004, + 0x1880000: 0x10000, + 0x1980000: 0x4010104, + 0x1a80000: 0x10104, + 0x1b80000: 0x4000004, + 0x1c80000: 0x4000104, + 0x1d80000: 0x4010000, + 0x1e80000: 0x4, + 0x1f80000: 0x10100 + }, + { + 0x0: 0x80401000, + 0x10000: 0x80001040, + 0x20000: 0x401040, + 0x30000: 0x80400000, + 0x40000: 0x0, + 0x50000: 0x401000, + 0x60000: 0x80000040, + 0x70000: 0x400040, + 0x80000: 0x80000000, + 0x90000: 0x400000, + 0xa0000: 0x40, + 0xb0000: 0x80001000, + 0xc0000: 0x80400040, + 0xd0000: 0x1040, + 0xe0000: 0x1000, + 0xf0000: 0x80401040, + 0x8000: 0x80001040, + 0x18000: 0x40, + 0x28000: 0x80400040, + 0x38000: 0x80001000, + 0x48000: 0x401000, + 0x58000: 0x80401040, + 0x68000: 0x0, + 0x78000: 0x80400000, + 0x88000: 0x1000, + 0x98000: 0x80401000, + 0xa8000: 0x400000, + 0xb8000: 0x1040, + 0xc8000: 0x80000000, + 0xd8000: 0x400040, + 0xe8000: 0x401040, + 0xf8000: 0x80000040, + 0x100000: 0x400040, + 0x110000: 0x401000, + 0x120000: 0x80000040, + 0x130000: 0x0, + 0x140000: 0x1040, + 0x150000: 0x80400040, + 0x160000: 0x80401000, + 0x170000: 0x80001040, + 0x180000: 0x80401040, + 0x190000: 0x80000000, + 0x1a0000: 0x80400000, + 0x1b0000: 0x401040, + 0x1c0000: 0x80001000, + 0x1d0000: 0x400000, + 0x1e0000: 0x40, + 0x1f0000: 0x1000, + 0x108000: 0x80400000, + 0x118000: 0x80401040, + 0x128000: 0x0, + 0x138000: 0x401000, + 0x148000: 0x400040, + 0x158000: 0x80000000, + 0x168000: 0x80001040, + 0x178000: 0x40, + 0x188000: 0x80000040, + 0x198000: 0x1000, + 0x1a8000: 0x80001000, + 0x1b8000: 0x80400040, + 0x1c8000: 0x1040, + 0x1d8000: 0x80401000, + 0x1e8000: 0x400000, + 0x1f8000: 0x401040 + }, + { + 0x0: 0x80, + 0x1000: 0x1040000, + 0x2000: 0x40000, + 0x3000: 0x20000000, + 0x4000: 0x20040080, + 0x5000: 0x1000080, + 0x6000: 0x21000080, + 0x7000: 0x40080, + 0x8000: 0x1000000, + 0x9000: 0x20040000, + 0xa000: 0x20000080, + 0xb000: 0x21040080, + 0xc000: 0x21040000, + 0xd000: 0x0, + 0xe000: 0x1040080, + 0xf000: 0x21000000, + 0x800: 0x1040080, + 0x1800: 0x21000080, + 0x2800: 0x80, + 0x3800: 0x1040000, + 0x4800: 0x40000, + 0x5800: 0x20040080, + 0x6800: 0x21040000, + 0x7800: 0x20000000, + 0x8800: 0x20040000, + 0x9800: 0x0, + 0xa800: 0x21040080, + 0xb800: 0x1000080, + 0xc800: 0x20000080, + 0xd800: 0x21000000, + 0xe800: 0x1000000, + 0xf800: 0x40080, + 0x10000: 0x40000, + 0x11000: 0x80, + 0x12000: 0x20000000, + 0x13000: 0x21000080, + 0x14000: 0x1000080, + 0x15000: 0x21040000, + 0x16000: 0x20040080, + 0x17000: 0x1000000, + 0x18000: 0x21040080, + 0x19000: 0x21000000, + 0x1a000: 0x1040000, + 0x1b000: 0x20040000, + 0x1c000: 0x40080, + 0x1d000: 0x20000080, + 0x1e000: 0x0, + 0x1f000: 0x1040080, + 0x10800: 0x21000080, + 0x11800: 0x1000000, + 0x12800: 0x1040000, + 0x13800: 0x20040080, + 0x14800: 0x20000000, + 0x15800: 0x1040080, + 0x16800: 0x80, + 0x17800: 0x21040000, + 0x18800: 0x40080, + 0x19800: 0x21040080, + 0x1a800: 0x0, + 0x1b800: 0x21000000, + 0x1c800: 0x1000080, + 0x1d800: 0x40000, + 0x1e800: 0x20040000, + 0x1f800: 0x20000080 + }, + { + 0x0: 0x10000008, + 0x100: 0x2000, + 0x200: 0x10200000, + 0x300: 0x10202008, + 0x400: 0x10002000, + 0x500: 0x200000, + 0x600: 0x200008, + 0x700: 0x10000000, + 0x800: 0x0, + 0x900: 0x10002008, + 0xa00: 0x202000, + 0xb00: 0x8, + 0xc00: 0x10200008, + 0xd00: 0x202008, + 0xe00: 0x2008, + 0xf00: 0x10202000, + 0x80: 0x10200000, + 0x180: 0x10202008, + 0x280: 0x8, + 0x380: 0x200000, + 0x480: 0x202008, + 0x580: 0x10000008, + 0x680: 0x10002000, + 0x780: 0x2008, + 0x880: 0x200008, + 0x980: 0x2000, + 0xa80: 0x10002008, + 0xb80: 0x10200008, + 0xc80: 0x0, + 0xd80: 0x10202000, + 0xe80: 0x202000, + 0xf80: 0x10000000, + 0x1000: 0x10002000, + 0x1100: 0x10200008, + 0x1200: 0x10202008, + 0x1300: 0x2008, + 0x1400: 0x200000, + 0x1500: 0x10000000, + 0x1600: 0x10000008, + 0x1700: 0x202000, + 0x1800: 0x202008, + 0x1900: 0x0, + 0x1a00: 0x8, + 0x1b00: 0x10200000, + 0x1c00: 0x2000, + 0x1d00: 0x10002008, + 0x1e00: 0x10202000, + 0x1f00: 0x200008, + 0x1080: 0x8, + 0x1180: 0x202000, + 0x1280: 0x200000, + 0x1380: 0x10000008, + 0x1480: 0x10002000, + 0x1580: 0x2008, + 0x1680: 0x10202008, + 0x1780: 0x10200000, + 0x1880: 0x10202000, + 0x1980: 0x10200008, + 0x1a80: 0x2000, + 0x1b80: 0x202008, + 0x1c80: 0x200008, + 0x1d80: 0x0, + 0x1e80: 0x10000000, + 0x1f80: 0x10002008 + }, + { + 0x0: 0x100000, + 0x10: 0x2000401, + 0x20: 0x400, + 0x30: 0x100401, + 0x40: 0x2100401, + 0x50: 0x0, + 0x60: 0x1, + 0x70: 0x2100001, + 0x80: 0x2000400, + 0x90: 0x100001, + 0xa0: 0x2000001, + 0xb0: 0x2100400, + 0xc0: 0x2100000, + 0xd0: 0x401, + 0xe0: 0x100400, + 0xf0: 0x2000000, + 0x8: 0x2100001, + 0x18: 0x0, + 0x28: 0x2000401, + 0x38: 0x2100400, + 0x48: 0x100000, + 0x58: 0x2000001, + 0x68: 0x2000000, + 0x78: 0x401, + 0x88: 0x100401, + 0x98: 0x2000400, + 0xa8: 0x2100000, + 0xb8: 0x100001, + 0xc8: 0x400, + 0xd8: 0x2100401, + 0xe8: 0x1, + 0xf8: 0x100400, + 0x100: 0x2000000, + 0x110: 0x100000, + 0x120: 0x2000401, + 0x130: 0x2100001, + 0x140: 0x100001, + 0x150: 0x2000400, + 0x160: 0x2100400, + 0x170: 0x100401, + 0x180: 0x401, + 0x190: 0x2100401, + 0x1a0: 0x100400, + 0x1b0: 0x1, + 0x1c0: 0x0, + 0x1d0: 0x2100000, + 0x1e0: 0x2000001, + 0x1f0: 0x400, + 0x108: 0x100400, + 0x118: 0x2000401, + 0x128: 0x2100001, + 0x138: 0x1, + 0x148: 0x2000000, + 0x158: 0x100000, + 0x168: 0x401, + 0x178: 0x2100400, + 0x188: 0x2000001, + 0x198: 0x2100000, + 0x1a8: 0x0, + 0x1b8: 0x2100401, + 0x1c8: 0x100401, + 0x1d8: 0x400, + 0x1e8: 0x2000400, + 0x1f8: 0x100001 + }, + { + 0x0: 0x8000820, + 0x1: 0x20000, + 0x2: 0x8000000, + 0x3: 0x20, + 0x4: 0x20020, + 0x5: 0x8020820, + 0x6: 0x8020800, + 0x7: 0x800, + 0x8: 0x8020000, + 0x9: 0x8000800, + 0xa: 0x20800, + 0xb: 0x8020020, + 0xc: 0x820, + 0xd: 0x0, + 0xe: 0x8000020, + 0xf: 0x20820, + 0x80000000: 0x800, + 0x80000001: 0x8020820, + 0x80000002: 0x8000820, + 0x80000003: 0x8000000, + 0x80000004: 0x8020000, + 0x80000005: 0x20800, + 0x80000006: 0x20820, + 0x80000007: 0x20, + 0x80000008: 0x8000020, + 0x80000009: 0x820, + 0x8000000a: 0x20020, + 0x8000000b: 0x8020800, + 0x8000000c: 0x0, + 0x8000000d: 0x8020020, + 0x8000000e: 0x8000800, + 0x8000000f: 0x20000, + 0x10: 0x20820, + 0x11: 0x8020800, + 0x12: 0x20, + 0x13: 0x800, + 0x14: 0x8000800, + 0x15: 0x8000020, + 0x16: 0x8020020, + 0x17: 0x20000, + 0x18: 0x0, + 0x19: 0x20020, + 0x1a: 0x8020000, + 0x1b: 0x8000820, + 0x1c: 0x8020820, + 0x1d: 0x20800, + 0x1e: 0x820, + 0x1f: 0x8000000, + 0x80000010: 0x20000, + 0x80000011: 0x800, + 0x80000012: 0x8020020, + 0x80000013: 0x20820, + 0x80000014: 0x20, + 0x80000015: 0x8020000, + 0x80000016: 0x8000000, + 0x80000017: 0x8000820, + 0x80000018: 0x8020820, + 0x80000019: 0x8000020, + 0x8000001a: 0x8000800, + 0x8000001b: 0x0, + 0x8000001c: 0x20800, + 0x8000001d: 0x820, + 0x8000001e: 0x20020, + 0x8000001f: 0x8020800 + } + ]; + + // Masks that select the SBOX input + var SBOX_MASK = [ + 0xf8000001, 0x1f800000, 0x01f80000, 0x001f8000, + 0x0001f800, 0x00001f80, 0x000001f8, 0x8000001f + ]; + + /** + * DES block cipher algorithm. + */ + var DES = C_algo.DES = BlockCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + + // Select 56 bits according to PC1 + var keyBits = []; + for (var i = 0; i < 56; i++) { + var keyBitPos = PC1[i] - 1; + keyBits[i] = (keyWords[keyBitPos >>> 5] >>> (31 - keyBitPos % 32)) & 1; + } + + // Assemble 16 subkeys + var subKeys = this._subKeys = []; + for (var nSubKey = 0; nSubKey < 16; nSubKey++) { + // Create subkey + var subKey = subKeys[nSubKey] = []; + + // Shortcut + var bitShift = BIT_SHIFTS[nSubKey]; + + // Select 48 bits according to PC2 + for (var i = 0; i < 24; i++) { + // Select from the left 28 key bits + subKey[(i / 6) | 0] |= keyBits[((PC2[i] - 1) + bitShift) % 28] << (31 - i % 6); + + // Select from the right 28 key bits + subKey[4 + ((i / 6) | 0)] |= keyBits[28 + (((PC2[i + 24] - 1) + bitShift) % 28)] << (31 - i % 6); + } + + // Since each subkey is applied to an expanded 32-bit input, + // the subkey can be broken into 8 values scaled to 32-bits, + // which allows the key to be used without expansion + subKey[0] = (subKey[0] << 1) | (subKey[0] >>> 31); + for (var i = 1; i < 7; i++) { + subKey[i] = subKey[i] >>> ((i - 1) * 4 + 3); + } + subKey[7] = (subKey[7] << 5) | (subKey[7] >>> 27); + } + + // Compute inverse subkeys + var invSubKeys = this._invSubKeys = []; + for (var i = 0; i < 16; i++) { + invSubKeys[i] = subKeys[15 - i]; + } + }, + + encryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._subKeys); + }, + + decryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._invSubKeys); + }, + + _doCryptBlock: function (M, offset, subKeys) { + // Get input + this._lBlock = M[offset]; + this._rBlock = M[offset + 1]; + + // Initial permutation + exchangeLR.call(this, 4, 0x0f0f0f0f); + exchangeLR.call(this, 16, 0x0000ffff); + exchangeRL.call(this, 2, 0x33333333); + exchangeRL.call(this, 8, 0x00ff00ff); + exchangeLR.call(this, 1, 0x55555555); + + // Rounds + for (var round = 0; round < 16; round++) { + // Shortcuts + var subKey = subKeys[round]; + var lBlock = this._lBlock; + var rBlock = this._rBlock; + + // Feistel function + var f = 0; + for (var i = 0; i < 8; i++) { + f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0]; + } + this._lBlock = rBlock; + this._rBlock = lBlock ^ f; + } + + // Undo swap from last round + var t = this._lBlock; + this._lBlock = this._rBlock; + this._rBlock = t; + + // Final permutation + exchangeLR.call(this, 1, 0x55555555); + exchangeRL.call(this, 8, 0x00ff00ff); + exchangeRL.call(this, 2, 0x33333333); + exchangeLR.call(this, 16, 0x0000ffff); + exchangeLR.call(this, 4, 0x0f0f0f0f); + + // Set output + M[offset] = this._lBlock; + M[offset + 1] = this._rBlock; + }, + + keySize: 64/32, + + ivSize: 64/32, + + blockSize: 64/32 + }); + + // Swap bits across the left and right words + function exchangeLR(offset, mask) { + var t = ((this._lBlock >>> offset) ^ this._rBlock) & mask; + this._rBlock ^= t; + this._lBlock ^= t << offset; + } + + function exchangeRL(offset, mask) { + var t = ((this._rBlock >>> offset) ^ this._lBlock) & mask; + this._lBlock ^= t; + this._rBlock ^= t << offset; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.DES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.DES.decrypt(ciphertext, key, cfg); + */ + C.DES = BlockCipher._createHelper(DES); + + /** + * Triple-DES block cipher algorithm. + */ + var TripleDES = C_algo.TripleDES = BlockCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + + // Create DES instances + this._des1 = DES.createEncryptor(WordArray.create(keyWords.slice(0, 2))); + this._des2 = DES.createEncryptor(WordArray.create(keyWords.slice(2, 4))); + this._des3 = DES.createEncryptor(WordArray.create(keyWords.slice(4, 6))); + }, + + encryptBlock: function (M, offset) { + this._des1.encryptBlock(M, offset); + this._des2.decryptBlock(M, offset); + this._des3.encryptBlock(M, offset); + }, + + decryptBlock: function (M, offset) { + this._des3.decryptBlock(M, offset); + this._des2.encryptBlock(M, offset); + this._des1.decryptBlock(M, offset); + }, + + keySize: 192/32, + + ivSize: 64/32, + + blockSize: 64/32 + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg); + */ + C.TripleDES = BlockCipher._createHelper(TripleDES); + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + /** + * RC4 stream cipher algorithm. + */ + var RC4 = C_algo.RC4 = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + var keySigBytes = key.sigBytes; + + // Init sbox + var S = this._S = []; + for (var i = 0; i < 256; i++) { + S[i] = i; + } + + // Key setup + for (var i = 0, j = 0; i < 256; i++) { + var keyByteIndex = i % keySigBytes; + var keyByte = (keyWords[keyByteIndex >>> 2] >>> (24 - (keyByteIndex % 4) * 8)) & 0xff; + + j = (j + S[i] + keyByte) % 256; + + // Swap + var t = S[i]; + S[i] = S[j]; + S[j] = t; + } + + // Counters + this._i = this._j = 0; + }, + + _doProcessBlock: function (M, offset) { + M[offset] ^= generateKeystreamWord.call(this); + }, + + keySize: 256/32, + + ivSize: 0 + }); + + function generateKeystreamWord() { + // Shortcuts + var S = this._S; + var i = this._i; + var j = this._j; + + // Generate keystream word + var keystreamWord = 0; + for (var n = 0; n < 4; n++) { + i = (i + 1) % 256; + j = (j + S[i]) % 256; + + // Swap + var t = S[i]; + S[i] = S[j]; + S[j] = t; + + keystreamWord |= S[(S[i] + S[j]) % 256] << (24 - n * 8); + } + + // Update counters + this._i = i; + this._j = j; + + return keystreamWord; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg); + */ + C.RC4 = StreamCipher._createHelper(RC4); + + /** + * Modified RC4 stream cipher algorithm. + */ + var RC4Drop = C_algo.RC4Drop = RC4.extend({ + /** + * Configuration options. + * + * @property {number} drop The number of keystream words to drop. Default 192 + */ + cfg: RC4.cfg.extend({ + drop: 192 + }), + + _doReset: function () { + RC4._doReset.call(this); + + // Drop + for (var i = this.cfg.drop; i > 0; i--) { + generateKeystreamWord.call(this); + } + } + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg); + */ + C.RC4Drop = StreamCipher._createHelper(RC4Drop); + }()); + + + /** @preserve + * Counter block mode compatible with Dr Brian Gladman fileenc.c + * derived from CryptoJS.mode.CTR + * Jan Hruby jhruby.web@gmail.com + */ + CryptoJS.mode.CTRGladman = (function () { + var CTRGladman = CryptoJS.lib.BlockCipherMode.extend(); + + function incWord(word) + { + if (((word >> 24) & 0xff) === 0xff) { //overflow + var b1 = (word >> 16)&0xff; + var b2 = (word >> 8)&0xff; + var b3 = word & 0xff; + + if (b1 === 0xff) // overflow b1 + { + b1 = 0; + if (b2 === 0xff) + { + b2 = 0; + if (b3 === 0xff) + { + b3 = 0; + } + else + { + ++b3; + } + } + else + { + ++b2; + } + } + else + { + ++b1; + } + + word = 0; + word += (b1 << 16); + word += (b2 << 8); + word += b3; + } + else + { + word += (0x01 << 24); + } + return word; + } + + function incCounter(counter) + { + if ((counter[0] = incWord(counter[0])) === 0) + { + // encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8 + counter[1] = incWord(counter[1]); + } + return counter; + } + + var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var counter = this._counter; + + // Generate keystream + if (iv) { + counter = this._counter = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + + incCounter(counter); + + var keystream = counter.slice(0); + cipher.encryptBlock(keystream, 0); + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + CTRGladman.Decryptor = Encryptor; + + return CTRGladman; + }()); + + + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + // Reusable objects + var S = []; + var C_ = []; + var G = []; + + /** + * Rabbit stream cipher algorithm + */ + var Rabbit = C_algo.Rabbit = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var K = this._key.words; + var iv = this.cfg.iv; + + // Swap endian + for (var i = 0; i < 4; i++) { + K[i] = (((K[i] << 8) | (K[i] >>> 24)) & 0x00ff00ff) | + (((K[i] << 24) | (K[i] >>> 8)) & 0xff00ff00); + } + + // Generate initial state values + var X = this._X = [ + K[0], (K[3] << 16) | (K[2] >>> 16), + K[1], (K[0] << 16) | (K[3] >>> 16), + K[2], (K[1] << 16) | (K[0] >>> 16), + K[3], (K[2] << 16) | (K[1] >>> 16) + ]; + + // Generate initial counter values + var C = this._C = [ + (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), + (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), + (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), + (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) + ]; + + // Carry bit + this._b = 0; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + + // Modify the counters + for (var i = 0; i < 8; i++) { + C[i] ^= X[(i + 4) & 7]; + } + + // IV setup + if (iv) { + // Shortcuts + var IV = iv.words; + var IV_0 = IV[0]; + var IV_1 = IV[1]; + + // Generate four subvectors + var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); + var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); + var i1 = (i0 >>> 16) | (i2 & 0xffff0000); + var i3 = (i2 << 16) | (i0 & 0x0000ffff); + + // Modify counter values + C[0] ^= i0; + C[1] ^= i1; + C[2] ^= i2; + C[3] ^= i3; + C[4] ^= i0; + C[5] ^= i1; + C[6] ^= i2; + C[7] ^= i3; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + } + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var X = this._X; + + // Iterate the system + nextState.call(this); + + // Generate four keystream words + S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); + S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); + S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); + S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); + + for (var i = 0; i < 4; i++) { + // Swap endian + S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | + (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); + + // Encrypt + M[offset + i] ^= S[i]; + } + }, + + blockSize: 128/32, + + ivSize: 64/32 + }); + + function nextState() { + // Shortcuts + var X = this._X; + var C = this._C; + + // Save old counter values + for (var i = 0; i < 8; i++) { + C_[i] = C[i]; + } + + // Calculate new counter values + C[0] = (C[0] + 0x4d34d34d + this._b) | 0; + C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; + C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; + C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; + C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; + C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; + C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; + C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; + this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; + + // Calculate the g-values + for (var i = 0; i < 8; i++) { + var gx = X[i] + C[i]; + + // Construct high and low argument for squaring + var ga = gx & 0xffff; + var gb = gx >>> 16; + + // Calculate high and low result of squaring + var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; + var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); + + // High XOR low + G[i] = gh ^ gl; + } + + // Calculate new state values + X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; + X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; + X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; + X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; + X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; + X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; + X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; + X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.Rabbit.encrypt(message, key, cfg); + * var plaintext = CryptoJS.Rabbit.decrypt(ciphertext, key, cfg); + */ + C.Rabbit = StreamCipher._createHelper(Rabbit); + }()); + + + /** + * Counter block mode. + */ + CryptoJS.mode.CTR = (function () { + var CTR = CryptoJS.lib.BlockCipherMode.extend(); + + var Encryptor = CTR.Encryptor = CTR.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var counter = this._counter; + + // Generate keystream + if (iv) { + counter = this._counter = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + var keystream = counter.slice(0); + cipher.encryptBlock(keystream, 0); + + // Increment counter + counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + CTR.Decryptor = Encryptor; + + return CTR; + }()); + + + (function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var StreamCipher = C_lib.StreamCipher; + var C_algo = C.algo; + + // Reusable objects + var S = []; + var C_ = []; + var G = []; + + /** + * Rabbit stream cipher algorithm. + * + * This is a legacy version that neglected to convert the key to little-endian. + * This error doesn't affect the cipher's security, + * but it does affect its compatibility with other implementations. + */ + var RabbitLegacy = C_algo.RabbitLegacy = StreamCipher.extend({ + _doReset: function () { + // Shortcuts + var K = this._key.words; + var iv = this.cfg.iv; + + // Generate initial state values + var X = this._X = [ + K[0], (K[3] << 16) | (K[2] >>> 16), + K[1], (K[0] << 16) | (K[3] >>> 16), + K[2], (K[1] << 16) | (K[0] >>> 16), + K[3], (K[2] << 16) | (K[1] >>> 16) + ]; + + // Generate initial counter values + var C = this._C = [ + (K[2] << 16) | (K[2] >>> 16), (K[0] & 0xffff0000) | (K[1] & 0x0000ffff), + (K[3] << 16) | (K[3] >>> 16), (K[1] & 0xffff0000) | (K[2] & 0x0000ffff), + (K[0] << 16) | (K[0] >>> 16), (K[2] & 0xffff0000) | (K[3] & 0x0000ffff), + (K[1] << 16) | (K[1] >>> 16), (K[3] & 0xffff0000) | (K[0] & 0x0000ffff) + ]; + + // Carry bit + this._b = 0; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + + // Modify the counters + for (var i = 0; i < 8; i++) { + C[i] ^= X[(i + 4) & 7]; + } + + // IV setup + if (iv) { + // Shortcuts + var IV = iv.words; + var IV_0 = IV[0]; + var IV_1 = IV[1]; + + // Generate four subvectors + var i0 = (((IV_0 << 8) | (IV_0 >>> 24)) & 0x00ff00ff) | (((IV_0 << 24) | (IV_0 >>> 8)) & 0xff00ff00); + var i2 = (((IV_1 << 8) | (IV_1 >>> 24)) & 0x00ff00ff) | (((IV_1 << 24) | (IV_1 >>> 8)) & 0xff00ff00); + var i1 = (i0 >>> 16) | (i2 & 0xffff0000); + var i3 = (i2 << 16) | (i0 & 0x0000ffff); + + // Modify counter values + C[0] ^= i0; + C[1] ^= i1; + C[2] ^= i2; + C[3] ^= i3; + C[4] ^= i0; + C[5] ^= i1; + C[6] ^= i2; + C[7] ^= i3; + + // Iterate the system four times + for (var i = 0; i < 4; i++) { + nextState.call(this); + } + } + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var X = this._X; + + // Iterate the system + nextState.call(this); + + // Generate four keystream words + S[0] = X[0] ^ (X[5] >>> 16) ^ (X[3] << 16); + S[1] = X[2] ^ (X[7] >>> 16) ^ (X[5] << 16); + S[2] = X[4] ^ (X[1] >>> 16) ^ (X[7] << 16); + S[3] = X[6] ^ (X[3] >>> 16) ^ (X[1] << 16); + + for (var i = 0; i < 4; i++) { + // Swap endian + S[i] = (((S[i] << 8) | (S[i] >>> 24)) & 0x00ff00ff) | + (((S[i] << 24) | (S[i] >>> 8)) & 0xff00ff00); + + // Encrypt + M[offset + i] ^= S[i]; + } + }, + + blockSize: 128/32, + + ivSize: 64/32 + }); + + function nextState() { + // Shortcuts + var X = this._X; + var C = this._C; + + // Save old counter values + for (var i = 0; i < 8; i++) { + C_[i] = C[i]; + } + + // Calculate new counter values + C[0] = (C[0] + 0x4d34d34d + this._b) | 0; + C[1] = (C[1] + 0xd34d34d3 + ((C[0] >>> 0) < (C_[0] >>> 0) ? 1 : 0)) | 0; + C[2] = (C[2] + 0x34d34d34 + ((C[1] >>> 0) < (C_[1] >>> 0) ? 1 : 0)) | 0; + C[3] = (C[3] + 0x4d34d34d + ((C[2] >>> 0) < (C_[2] >>> 0) ? 1 : 0)) | 0; + C[4] = (C[4] + 0xd34d34d3 + ((C[3] >>> 0) < (C_[3] >>> 0) ? 1 : 0)) | 0; + C[5] = (C[5] + 0x34d34d34 + ((C[4] >>> 0) < (C_[4] >>> 0) ? 1 : 0)) | 0; + C[6] = (C[6] + 0x4d34d34d + ((C[5] >>> 0) < (C_[5] >>> 0) ? 1 : 0)) | 0; + C[7] = (C[7] + 0xd34d34d3 + ((C[6] >>> 0) < (C_[6] >>> 0) ? 1 : 0)) | 0; + this._b = (C[7] >>> 0) < (C_[7] >>> 0) ? 1 : 0; + + // Calculate the g-values + for (var i = 0; i < 8; i++) { + var gx = X[i] + C[i]; + + // Construct high and low argument for squaring + var ga = gx & 0xffff; + var gb = gx >>> 16; + + // Calculate high and low result of squaring + var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb; + var gl = (((gx & 0xffff0000) * gx) | 0) + (((gx & 0x0000ffff) * gx) | 0); + + // High XOR low + G[i] = gh ^ gl; + } + + // Calculate new state values + X[0] = (G[0] + ((G[7] << 16) | (G[7] >>> 16)) + ((G[6] << 16) | (G[6] >>> 16))) | 0; + X[1] = (G[1] + ((G[0] << 8) | (G[0] >>> 24)) + G[7]) | 0; + X[2] = (G[2] + ((G[1] << 16) | (G[1] >>> 16)) + ((G[0] << 16) | (G[0] >>> 16))) | 0; + X[3] = (G[3] + ((G[2] << 8) | (G[2] >>> 24)) + G[1]) | 0; + X[4] = (G[4] + ((G[3] << 16) | (G[3] >>> 16)) + ((G[2] << 16) | (G[2] >>> 16))) | 0; + X[5] = (G[5] + ((G[4] << 8) | (G[4] >>> 24)) + G[3]) | 0; + X[6] = (G[6] + ((G[5] << 16) | (G[5] >>> 16)) + ((G[4] << 16) | (G[4] >>> 16))) | 0; + X[7] = (G[7] + ((G[6] << 8) | (G[6] >>> 24)) + G[5]) | 0; + } + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.RabbitLegacy.encrypt(message, key, cfg); + * var plaintext = CryptoJS.RabbitLegacy.decrypt(ciphertext, key, cfg); + */ + C.RabbitLegacy = StreamCipher._createHelper(RabbitLegacy); + }()); + + + /** + * Zero padding strategy. + */ + CryptoJS.pad.ZeroPadding = { + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Pad + data.clamp(); + data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes); + }, + + unpad: function (data) { + // Shortcut + var dataWords = data.words; + + // Unpad + var i = data.sigBytes - 1; + while (!((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) { + i--; + } + data.sigBytes = i + 1; + } + }; + + + return CryptoJS; + +})); \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic.js new file mode 100644 index 000000000..0baf749bc --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic.js @@ -0,0 +1,21 @@ +'use strict'; + +var elliptic = exports; + +// hello ladies ;) +function inherits (ctor, superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; +} + +elliptic.inherits = inherits +elliptic.version = '6.5.0'; +elliptic.utils = require('./elliptic/utils'); +elliptic.curve = require('./elliptic/curve'); +elliptic.curves = require('./elliptic/curves'); + +// Protocols +elliptic.ec = require('./elliptic/ec'); diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/base.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/base.js new file mode 100644 index 000000000..051a106e3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/base.js @@ -0,0 +1,375 @@ +'use strict'; + +var BN = require('../../../../BN/bn'); +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var getNAF = utils.getNAF; +var getJSF = utils.getJSF; +var assert = utils.assert; + +function BaseCurve(type, conf) { + this.type = type; + this.p = new BN(conf.p, 16); + + // Use Montgomery, when there is no fast reduction for the prime + this.red = conf.prime ? BN.red(conf.prime) : BN.mont(this.p); + + // Useful for many curves + this.zero = new BN(0).toRed(this.red); + this.one = new BN(1).toRed(this.red); + this.two = new BN(2).toRed(this.red); + + // Curve configuration, optional + this.n = conf.n && new BN(conf.n, 16); + this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed); + + // Temporary arrays + this._wnafT1 = new Array(4); + this._wnafT2 = new Array(4); + this._wnafT3 = new Array(4); + this._wnafT4 = new Array(4); + + // Generalized Greg Maxwell's trick + var adjustCount = this.n && this.p.div(this.n); + if (!adjustCount || adjustCount.cmpn(100) > 0) { + this.redN = null; + } else { + this._maxwellTrick = true; + this.redN = this.n.toRed(this.red); + } +} +module.exports = BaseCurve; + +BaseCurve.prototype.point = function point() { + throw new Error('Not implemented'); +}; + +BaseCurve.prototype.validate = function validate() { + throw new Error('Not implemented'); +}; + +BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) { + assert(p.precomputed); + var doubles = p._getDoubles(); + + var naf = getNAF(k, 1); + var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1); + I /= 3; + + // Translate into more windowed form + var repr = []; + for (var j = 0; j < naf.length; j += doubles.step) { + var nafW = 0; + for (var k = j + doubles.step - 1; k >= j; k--) + nafW = (nafW << 1) + naf[k]; + repr.push(nafW); + } + + var a = this.jpoint(null, null, null); + var b = this.jpoint(null, null, null); + for (var i = I; i > 0; i--) { + for (var j = 0; j < repr.length; j++) { + var nafW = repr[j]; + if (nafW === i) + b = b.mixedAdd(doubles.points[j]); + else if (nafW === -i) + b = b.mixedAdd(doubles.points[j].neg()); + } + a = a.add(b); + } + return a.toP(); +}; + +BaseCurve.prototype._wnafMul = function _wnafMul(p, k) { + var w = 4; + + // Precompute window + var nafPoints = p._getNAFPoints(w); + w = nafPoints.wnd; + var wnd = nafPoints.points; + + // Get NAF form + var naf = getNAF(k, w); + + // Add `this`*(N+1) for every w-NAF index + var acc = this.jpoint(null, null, null); + for (var i = naf.length - 1; i >= 0; i--) { + // Count zeroes + for (var k = 0; i >= 0 && naf[i] === 0; i--) + k++; + if (i >= 0) + k++; + acc = acc.dblp(k); + + if (i < 0) + break; + var z = naf[i]; + assert(z !== 0); + if (p.type === 'affine') { + // J +- P + if (z > 0) + acc = acc.mixedAdd(wnd[(z - 1) >> 1]); + else + acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg()); + } else { + // J +- J + if (z > 0) + acc = acc.add(wnd[(z - 1) >> 1]); + else + acc = acc.add(wnd[(-z - 1) >> 1].neg()); + } + } + return p.type === 'affine' ? acc.toP() : acc; +}; + +BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW, + points, + coeffs, + len, + jacobianResult) { + var wndWidth = this._wnafT1; + var wnd = this._wnafT2; + var naf = this._wnafT3; + + // Fill all arrays + var max = 0; + for (var i = 0; i < len; i++) { + var p = points[i]; + var nafPoints = p._getNAFPoints(defW); + wndWidth[i] = nafPoints.wnd; + wnd[i] = nafPoints.points; + } + + // Comb small window NAFs + for (var i = len - 1; i >= 1; i -= 2) { + var a = i - 1; + var b = i; + if (wndWidth[a] !== 1 || wndWidth[b] !== 1) { + naf[a] = getNAF(coeffs[a], wndWidth[a]); + naf[b] = getNAF(coeffs[b], wndWidth[b]); + max = Math.max(naf[a].length, max); + max = Math.max(naf[b].length, max); + continue; + } + + var comb = [ + points[a], /* 1 */ + null, /* 3 */ + null, /* 5 */ + points[b] /* 7 */ + ]; + + // Try to avoid Projective points, if possible + if (points[a].y.cmp(points[b].y) === 0) { + comb[1] = points[a].add(points[b]); + comb[2] = points[a].toJ().mixedAdd(points[b].neg()); + } else if (points[a].y.cmp(points[b].y.redNeg()) === 0) { + comb[1] = points[a].toJ().mixedAdd(points[b]); + comb[2] = points[a].add(points[b].neg()); + } else { + comb[1] = points[a].toJ().mixedAdd(points[b]); + comb[2] = points[a].toJ().mixedAdd(points[b].neg()); + } + + var index = [ + -3, /* -1 -1 */ + -1, /* -1 0 */ + -5, /* -1 1 */ + -7, /* 0 -1 */ + 0, /* 0 0 */ + 7, /* 0 1 */ + 5, /* 1 -1 */ + 1, /* 1 0 */ + 3 /* 1 1 */ + ]; + + var jsf = getJSF(coeffs[a], coeffs[b]); + max = Math.max(jsf[0].length, max); + naf[a] = new Array(max); + naf[b] = new Array(max); + for (var j = 0; j < max; j++) { + var ja = jsf[0][j] | 0; + var jb = jsf[1][j] | 0; + + naf[a][j] = index[(ja + 1) * 3 + (jb + 1)]; + naf[b][j] = 0; + wnd[a] = comb; + } + } + + var acc = this.jpoint(null, null, null); + var tmp = this._wnafT4; + for (var i = max; i >= 0; i--) { + var k = 0; + + while (i >= 0) { + var zero = true; + for (var j = 0; j < len; j++) { + tmp[j] = naf[j][i] | 0; + if (tmp[j] !== 0) + zero = false; + } + if (!zero) + break; + k++; + i--; + } + if (i >= 0) + k++; + acc = acc.dblp(k); + if (i < 0) + break; + + for (var j = 0; j < len; j++) { + var z = tmp[j]; + var p; + if (z === 0) + continue; + else if (z > 0) + p = wnd[j][(z - 1) >> 1]; + else if (z < 0) + p = wnd[j][(-z - 1) >> 1].neg(); + + if (p.type === 'affine') + acc = acc.mixedAdd(p); + else + acc = acc.add(p); + } + } + // Zeroify references + for (var i = 0; i < len; i++) + wnd[i] = null; + + if (jacobianResult) + return acc; + else + return acc.toP(); +}; + +function BasePoint(curve, type) { + this.curve = curve; + this.type = type; + this.precomputed = null; +} +BaseCurve.BasePoint = BasePoint; + +BasePoint.prototype.eq = function eq(/*other*/) { + throw new Error('Not implemented'); +}; + +BasePoint.prototype.validate = function validate() { + return this.curve.validate(this); +}; + +BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) { + bytes = utils.toArray(bytes, enc); + + var len = this.p.byteLength(); + + // uncompressed, hybrid-odd, hybrid-even + if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) && + bytes.length - 1 === 2 * len) { + if (bytes[0] === 0x06) + assert(bytes[bytes.length - 1] % 2 === 0); + else if (bytes[0] === 0x07) + assert(bytes[bytes.length - 1] % 2 === 1); + + var res = this.point(bytes.slice(1, 1 + len), + bytes.slice(1 + len, 1 + 2 * len)); + + return res; + } else if ((bytes[0] === 0x02 || bytes[0] === 0x03) && + bytes.length - 1 === len) { + return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03); + } + throw new Error('Unknown point format'); +}; + +BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) { + return this.encode(enc, true); +}; + +BasePoint.prototype._encode = function _encode(compact) { + var len = this.curve.p.byteLength(); + var x = this.getX().toArray('be', len); + + if (compact) + return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x); + + return [ 0x04 ].concat(x, this.getY().toArray('be', len)) ; +}; + +BasePoint.prototype.encode = function encode(enc, compact) { + return utils.encode(this._encode(compact), enc); +}; + +BasePoint.prototype.precompute = function precompute(power) { + if (this.precomputed) + return this; + + var precomputed = { + doubles: null, + naf: null, + beta: null + }; + precomputed.naf = this._getNAFPoints(8); + precomputed.doubles = this._getDoubles(4, power); + precomputed.beta = this._getBeta(); + this.precomputed = precomputed; + + return this; +}; + +BasePoint.prototype._hasDoubles = function _hasDoubles(k) { + if (!this.precomputed) + return false; + + var doubles = this.precomputed.doubles; + if (!doubles) + return false; + + return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step); +}; + +BasePoint.prototype._getDoubles = function _getDoubles(step, power) { + if (this.precomputed && this.precomputed.doubles) + return this.precomputed.doubles; + + var doubles = [ this ]; + var acc = this; + for (var i = 0; i < power; i += step) { + for (var j = 0; j < step; j++) + acc = acc.dbl(); + doubles.push(acc); + } + return { + step: step, + points: doubles + }; +}; + +BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) { + if (this.precomputed && this.precomputed.naf) + return this.precomputed.naf; + + var res = [ this ]; + var max = (1 << wnd) - 1; + var dbl = max === 1 ? null : this.dbl(); + for (var i = 1; i < max; i++) + res[i] = res[i - 1].add(dbl); + return { + wnd: wnd, + points: res + }; +}; + +BasePoint.prototype._getBeta = function _getBeta() { + return null; +}; + +BasePoint.prototype.dblp = function dblp(k) { + var r = this; + for (var i = 0; i < k; i++) + r = r.dbl(); + return r; +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/edwards.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/edwards.js new file mode 100644 index 000000000..28eeb1b00 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/edwards.js @@ -0,0 +1,433 @@ +'use strict'; + +var curve = require('../curve'); +var elliptic = require('../../elliptic'); +var BN = require('../../../../BN/bn'); +var inherits = elliptic.inherits; +var Base = curve.base; + +var assert = elliptic.utils.assert; + +function EdwardsCurve(conf) { + // NOTE: Important as we are creating point in Base.call() + this.twisted = (conf.a | 0) !== 1; + this.mOneA = this.twisted && (conf.a | 0) === -1; + this.extended = this.mOneA; + + Base.call(this, 'edwards', conf); + + this.a = new BN(conf.a, 16).umod(this.red.m); + this.a = this.a.toRed(this.red); + this.c = new BN(conf.c, 16).toRed(this.red); + this.c2 = this.c.redSqr(); + this.d = new BN(conf.d, 16).toRed(this.red); + this.dd = this.d.redAdd(this.d); + + assert(!this.twisted || this.c.fromRed().cmpn(1) === 0); + this.oneC = (conf.c | 0) === 1; +} +inherits(EdwardsCurve, Base); +module.exports = EdwardsCurve; + +EdwardsCurve.prototype._mulA = function _mulA(num) { + if (this.mOneA) + return num.redNeg(); + else + return this.a.redMul(num); +}; + +EdwardsCurve.prototype._mulC = function _mulC(num) { + if (this.oneC) + return num; + else + return this.c.redMul(num); +}; + +// Just for compatibility with Short curve +EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) { + return this.point(x, y, z, t); +}; + +EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) { + x = new BN(x, 16); + if (!x.red) + x = x.toRed(this.red); + + var x2 = x.redSqr(); + var rhs = this.c2.redSub(this.a.redMul(x2)); + var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); + + var y2 = rhs.redMul(lhs.redInvm()); + var y = y2.redSqrt(); + if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) + throw new Error('invalid point'); + + var isOdd = y.fromRed().isOdd(); + if (odd && !isOdd || !odd && isOdd) + y = y.redNeg(); + + return this.point(x, y); +}; + +EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) { + y = new BN(y, 16); + if (!y.red) + y = y.toRed(this.red); + + // x^2 = (y^2 - 1) / (d y^2 + 1) + var y2 = y.redSqr(); + var lhs = y2.redSub(this.one); + var rhs = y2.redMul(this.d).redAdd(this.one); + var x2 = lhs.redMul(rhs.redInvm()); + + if (x2.cmp(this.zero) === 0) { + if (odd) + throw new Error('invalid point'); + else + return this.point(this.zero, y); + } + + var x = x2.redSqrt(); + if (x.redSqr().redSub(x2).cmp(this.zero) !== 0) + throw new Error('invalid point'); + + if (x.isOdd() !== odd) + x = x.redNeg(); + + return this.point(x, y); +}; + +EdwardsCurve.prototype.validate = function validate(point) { + if (point.isInfinity()) + return true; + + // Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2) + point.normalize(); + + var x2 = point.x.redSqr(); + var y2 = point.y.redSqr(); + var lhs = x2.redMul(this.a).redAdd(y2); + var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); + + return lhs.cmp(rhs) === 0; +}; + +function Point(curve, x, y, z, t) { + Base.BasePoint.call(this, curve, 'projective'); + if (x === null && y === null && z === null) { + this.x = this.curve.zero; + this.y = this.curve.one; + this.z = this.curve.one; + this.t = this.curve.zero; + this.zOne = true; + } else { + this.x = new BN(x, 16); + this.y = new BN(y, 16); + this.z = z ? new BN(z, 16) : this.curve.one; + this.t = t && new BN(t, 16); + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.y.red) + this.y = this.y.toRed(this.curve.red); + if (!this.z.red) + this.z = this.z.toRed(this.curve.red); + if (this.t && !this.t.red) + this.t = this.t.toRed(this.curve.red); + this.zOne = this.z === this.curve.one; + + // Use extended coordinates + if (this.curve.extended && !this.t) { + this.t = this.x.redMul(this.y); + if (!this.zOne) + this.t = this.t.redMul(this.z.redInvm()); + } + } +} +inherits(Point, Base.BasePoint); + +EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) { + return Point.fromJSON(this, obj); +}; + +EdwardsCurve.prototype.point = function point(x, y, z, t) { + return new Point(this, x, y, z, t); +}; + +Point.fromJSON = function fromJSON(curve, obj) { + return new Point(curve, obj[0], obj[1], obj[2]); +}; + +Point.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +Point.prototype.isInfinity = function isInfinity() { + // XXX This code assumes that zero is always zero in red + return this.x.cmpn(0) === 0 && + this.y.cmp(this.z) === 0; +}; + +Point.prototype._extDbl = function _extDbl() { + // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + // #doubling-dbl-2008-hwcd + // 4M + 4S + + // A = X1^2 + var a = this.x.redSqr(); + // B = Y1^2 + var b = this.y.redSqr(); + // C = 2 * Z1^2 + var c = this.z.redSqr(); + c = c.redIAdd(c); + // D = a * A + var d = this.curve._mulA(a); + // E = (X1 + Y1)^2 - A - B + var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b); + // G = D + B + var g = d.redAdd(b); + // F = G - C + var f = g.redSub(c); + // H = D - B + var h = d.redSub(b); + // X3 = E * F + var nx = e.redMul(f); + // Y3 = G * H + var ny = g.redMul(h); + // T3 = E * H + var nt = e.redMul(h); + // Z3 = F * G + var nz = f.redMul(g); + return this.curve.point(nx, ny, nz, nt); +}; + +Point.prototype._projDbl = function _projDbl() { + // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html + // #doubling-dbl-2008-bbjlp + // #doubling-dbl-2007-bl + // and others + // Generally 3M + 4S or 2M + 4S + + // B = (X1 + Y1)^2 + var b = this.x.redAdd(this.y).redSqr(); + // C = X1^2 + var c = this.x.redSqr(); + // D = Y1^2 + var d = this.y.redSqr(); + + var nx; + var ny; + var nz; + if (this.curve.twisted) { + // E = a * C + var e = this.curve._mulA(c); + // F = E + D + var f = e.redAdd(d); + if (this.zOne) { + // X3 = (B - C - D) * (F - 2) + nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two)); + // Y3 = F * (E - D) + ny = f.redMul(e.redSub(d)); + // Z3 = F^2 - 2 * F + nz = f.redSqr().redSub(f).redSub(f); + } else { + // H = Z1^2 + var h = this.z.redSqr(); + // J = F - 2 * H + var j = f.redSub(h).redISub(h); + // X3 = (B-C-D)*J + nx = b.redSub(c).redISub(d).redMul(j); + // Y3 = F * (E - D) + ny = f.redMul(e.redSub(d)); + // Z3 = F * J + nz = f.redMul(j); + } + } else { + // E = C + D + var e = c.redAdd(d); + // H = (c * Z1)^2 + var h = this.curve._mulC(this.c.redMul(this.z)).redSqr(); + // J = E - 2 * H + var j = e.redSub(h).redSub(h); + // X3 = c * (B - E) * J + nx = this.curve._mulC(b.redISub(e)).redMul(j); + // Y3 = c * E * (C - D) + ny = this.curve._mulC(e).redMul(c.redISub(d)); + // Z3 = E * J + nz = e.redMul(j); + } + return this.curve.point(nx, ny, nz); +}; + +Point.prototype.dbl = function dbl() { + if (this.isInfinity()) + return this; + + // Double in extended coordinates + if (this.curve.extended) + return this._extDbl(); + else + return this._projDbl(); +}; + +Point.prototype._extAdd = function _extAdd(p) { + // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + // #addition-add-2008-hwcd-3 + // 8M + + // A = (Y1 - X1) * (Y2 - X2) + var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x)); + // B = (Y1 + X1) * (Y2 + X2) + var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x)); + // C = T1 * k * T2 + var c = this.t.redMul(this.curve.dd).redMul(p.t); + // D = Z1 * 2 * Z2 + var d = this.z.redMul(p.z.redAdd(p.z)); + // E = B - A + var e = b.redSub(a); + // F = D - C + var f = d.redSub(c); + // G = D + C + var g = d.redAdd(c); + // H = B + A + var h = b.redAdd(a); + // X3 = E * F + var nx = e.redMul(f); + // Y3 = G * H + var ny = g.redMul(h); + // T3 = E * H + var nt = e.redMul(h); + // Z3 = F * G + var nz = f.redMul(g); + return this.curve.point(nx, ny, nz, nt); +}; + +Point.prototype._projAdd = function _projAdd(p) { + // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html + // #addition-add-2008-bbjlp + // #addition-add-2007-bl + // 10M + 1S + + // A = Z1 * Z2 + var a = this.z.redMul(p.z); + // B = A^2 + var b = a.redSqr(); + // C = X1 * X2 + var c = this.x.redMul(p.x); + // D = Y1 * Y2 + var d = this.y.redMul(p.y); + // E = d * C * D + var e = this.curve.d.redMul(c).redMul(d); + // F = B - E + var f = b.redSub(e); + // G = B + E + var g = b.redAdd(e); + // X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) + var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d); + var nx = a.redMul(f).redMul(tmp); + var ny; + var nz; + if (this.curve.twisted) { + // Y3 = A * G * (D - a * C) + ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); + // Z3 = F * G + nz = f.redMul(g); + } else { + // Y3 = A * G * (D - C) + ny = a.redMul(g).redMul(d.redSub(c)); + // Z3 = c * F * G + nz = this.curve._mulC(f).redMul(g); + } + return this.curve.point(nx, ny, nz); +}; + +Point.prototype.add = function add(p) { + if (this.isInfinity()) + return p; + if (p.isInfinity()) + return this; + + if (this.curve.extended) + return this._extAdd(p); + else + return this._projAdd(p); +}; + +Point.prototype.mul = function mul(k) { + if (this._hasDoubles(k)) + return this.curve._fixedNafMul(this, k); + else + return this.curve._wnafMul(this, k); +}; + +Point.prototype.mulAdd = function mulAdd(k1, p, k2) { + return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false); +}; + +Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) { + return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true); +}; + +Point.prototype.normalize = function normalize() { + if (this.zOne) + return this; + + // Normalize coordinates + var zi = this.z.redInvm(); + this.x = this.x.redMul(zi); + this.y = this.y.redMul(zi); + if (this.t) + this.t = this.t.redMul(zi); + this.z = this.curve.one; + this.zOne = true; + return this; +}; + +Point.prototype.neg = function neg() { + return this.curve.point(this.x.redNeg(), + this.y, + this.z, + this.t && this.t.redNeg()); +}; + +Point.prototype.getX = function getX() { + this.normalize(); + return this.x.fromRed(); +}; + +Point.prototype.getY = function getY() { + this.normalize(); + return this.y.fromRed(); +}; + +Point.prototype.eq = function eq(other) { + return this === other || + this.getX().cmp(other.getX()) === 0 && + this.getY().cmp(other.getY()) === 0; +}; + +Point.prototype.eqXToP = function eqXToP(x) { + var rx = x.toRed(this.curve.red).redMul(this.z); + if (this.x.cmp(rx) === 0) + return true; + + var xc = x.clone(); + var t = this.curve.redN.redMul(this.z); + for (;;) { + xc.iadd(this.curve.n); + if (xc.cmp(this.curve.p) >= 0) + return false; + + rx.redIAdd(t); + if (this.x.cmp(rx) === 0) + return true; + } + return false; +}; + +// Compatibility with BaseCurve +Point.prototype.toP = Point.prototype.normalize; +Point.prototype.mixedAdd = Point.prototype.add; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/index.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/index.js new file mode 100644 index 000000000..c589281df --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/index.js @@ -0,0 +1,8 @@ +'use strict'; + +var curve = exports; + +curve.base = require('./base'); +curve.short = require('./short'); +curve.mont = require('./mont'); +curve.edwards = require('./edwards'); diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/mont.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/mont.js new file mode 100644 index 000000000..297620f62 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/mont.js @@ -0,0 +1,180 @@ +'use strict'; + +var curve = require('../curve'); +var BN = require('../../../../BN/bn'); +var inherits = require('../../elliptic').inherits; +var Base = curve.base; + +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; + +function MontCurve(conf) { + Base.call(this, 'mont', conf); + + this.a = new BN(conf.a, 16).toRed(this.red); + this.b = new BN(conf.b, 16).toRed(this.red); + this.i4 = new BN(4).toRed(this.red).redInvm(); + this.two = new BN(2).toRed(this.red); + this.a24 = this.i4.redMul(this.a.redAdd(this.two)); +} +inherits(MontCurve, Base); +module.exports = MontCurve; + +MontCurve.prototype.validate = function validate(point) { + var x = point.normalize().x; + var x2 = x.redSqr(); + var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x); + var y = rhs.redSqrt(); + + return y.redSqr().cmp(rhs) === 0; +}; + +function Point(curve, x, z) { + Base.BasePoint.call(this, curve, 'projective'); + if (x === null && z === null) { + this.x = this.curve.one; + this.z = this.curve.zero; + } else { + this.x = new BN(x, 16); + this.z = new BN(z, 16); + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.z.red) + this.z = this.z.toRed(this.curve.red); + } +} +inherits(Point, Base.BasePoint); + +MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) { + return this.point(utils.toArray(bytes, enc), 1); +}; + +MontCurve.prototype.point = function point(x, z) { + return new Point(this, x, z); +}; + +MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) { + return Point.fromJSON(this, obj); +}; + +Point.prototype.precompute = function precompute() { + // No-op +}; + +Point.prototype._encode = function _encode() { + return this.getX().toArray('be', this.curve.p.byteLength()); +}; + +Point.fromJSON = function fromJSON(curve, obj) { + return new Point(curve, obj[0], obj[1] || curve.one); +}; + +Point.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +Point.prototype.isInfinity = function isInfinity() { + // XXX This code assumes that zero is always zero in red + return this.z.cmpn(0) === 0; +}; + +Point.prototype.dbl = function dbl() { + // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3 + // 2M + 2S + 4A + + // A = X1 + Z1 + var a = this.x.redAdd(this.z); + // AA = A^2 + var aa = a.redSqr(); + // B = X1 - Z1 + var b = this.x.redSub(this.z); + // BB = B^2 + var bb = b.redSqr(); + // C = AA - BB + var c = aa.redSub(bb); + // X3 = AA * BB + var nx = aa.redMul(bb); + // Z3 = C * (BB + A24 * C) + var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c))); + return this.curve.point(nx, nz); +}; + +Point.prototype.add = function add() { + throw new Error('Not supported on Montgomery curve'); +}; + +Point.prototype.diffAdd = function diffAdd(p, diff) { + // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3 + // 4M + 2S + 6A + + // A = X2 + Z2 + var a = this.x.redAdd(this.z); + // B = X2 - Z2 + var b = this.x.redSub(this.z); + // C = X3 + Z3 + var c = p.x.redAdd(p.z); + // D = X3 - Z3 + var d = p.x.redSub(p.z); + // DA = D * A + var da = d.redMul(a); + // CB = C * B + var cb = c.redMul(b); + // X5 = Z1 * (DA + CB)^2 + var nx = diff.z.redMul(da.redAdd(cb).redSqr()); + // Z5 = X1 * (DA - CB)^2 + var nz = diff.x.redMul(da.redISub(cb).redSqr()); + return this.curve.point(nx, nz); +}; + +Point.prototype.mul = function mul(k) { + var t = k.clone(); + var a = this; // (N / 2) * Q + Q + var b = this.curve.point(null, null); // (N / 2) * Q + var c = this; // Q + + for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1)) + bits.push(t.andln(1)); + + for (var i = bits.length - 1; i >= 0; i--) { + if (bits[i] === 0) { + // N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q + a = a.diffAdd(b, c); + // N * Q = 2 * ((N / 2) * Q + Q)) + b = b.dbl(); + } else { + // N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q) + b = a.diffAdd(b, c); + // N * Q + Q = 2 * ((N / 2) * Q + Q) + a = a.dbl(); + } + } + return b; +}; + +Point.prototype.mulAdd = function mulAdd() { + throw new Error('Not supported on Montgomery curve'); +}; + +Point.prototype.jumlAdd = function jumlAdd() { + throw new Error('Not supported on Montgomery curve'); +}; + +Point.prototype.eq = function eq(other) { + return this.getX().cmp(other.getX()) === 0; +}; + +Point.prototype.normalize = function normalize() { + this.x = this.x.redMul(this.z.redInvm()); + this.z = this.curve.one; + return this; +}; + +Point.prototype.getX = function getX() { + // Normalize coordinates + this.normalize(); + + return this.x.fromRed(); +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/short.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/short.js new file mode 100644 index 000000000..5f79988d7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curve/short.js @@ -0,0 +1,938 @@ +'use strict'; + +var curve = require('../curve'); +var elliptic = require('../../elliptic'); +var BN = require('../../../../BN/bn'); +var inherits = elliptic.inherits; +var Base = curve.base; + +var assert = elliptic.utils.assert; + +function ShortCurve(conf) { + Base.call(this, 'short', conf); + + this.a = new BN(conf.a, 16).toRed(this.red); + this.b = new BN(conf.b, 16).toRed(this.red); + this.tinv = this.two.redInvm(); + + this.zeroA = this.a.fromRed().cmpn(0) === 0; + this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0; + + // If the curve is endomorphic, precalculate beta and lambda + this.endo = this._getEndomorphism(conf); + this._endoWnafT1 = new Array(4); + this._endoWnafT2 = new Array(4); +} +inherits(ShortCurve, Base); +module.exports = ShortCurve; + +ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) { + // No efficient endomorphism + if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1) + return; + + // Compute beta and lambda, that lambda * P = (beta * Px; Py) + var beta; + var lambda; + if (conf.beta) { + beta = new BN(conf.beta, 16).toRed(this.red); + } else { + var betas = this._getEndoRoots(this.p); + // Choose the smallest beta + beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1]; + beta = beta.toRed(this.red); + } + if (conf.lambda) { + lambda = new BN(conf.lambda, 16); + } else { + // Choose the lambda that is matching selected beta + var lambdas = this._getEndoRoots(this.n); + if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) { + lambda = lambdas[0]; + } else { + lambda = lambdas[1]; + assert(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0); + } + } + + // Get basis vectors, used for balanced length-two representation + var basis; + if (conf.basis) { + basis = conf.basis.map(function(vec) { + return { + a: new BN(vec.a, 16), + b: new BN(vec.b, 16) + }; + }); + } else { + basis = this._getEndoBasis(lambda); + } + + return { + beta: beta, + lambda: lambda, + basis: basis + }; +}; + +ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) { + // Find roots of for x^2 + x + 1 in F + // Root = (-1 +- Sqrt(-3)) / 2 + // + var red = num === this.p ? this.red : BN.mont(num); + var tinv = new BN(2).toRed(red).redInvm(); + var ntinv = tinv.redNeg(); + + var s = new BN(3).toRed(red).redNeg().redSqrt().redMul(tinv); + + var l1 = ntinv.redAdd(s).fromRed(); + var l2 = ntinv.redSub(s).fromRed(); + return [ l1, l2 ]; +}; + +ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) { + // aprxSqrt >= sqrt(this.n) + var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2)); + + // 3.74 + // Run EGCD, until r(L + 1) < aprxSqrt + var u = lambda; + var v = this.n.clone(); + var x1 = new BN(1); + var y1 = new BN(0); + var x2 = new BN(0); + var y2 = new BN(1); + + // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n) + var a0; + var b0; + // First vector + var a1; + var b1; + // Second vector + var a2; + var b2; + + var prevR; + var i = 0; + var r; + var x; + while (u.cmpn(0) !== 0) { + var q = v.div(u); + r = v.sub(q.mul(u)); + x = x2.sub(q.mul(x1)); + var y = y2.sub(q.mul(y1)); + + if (!a1 && r.cmp(aprxSqrt) < 0) { + a0 = prevR.neg(); + b0 = x1; + a1 = r.neg(); + b1 = x; + } else if (a1 && ++i === 2) { + break; + } + prevR = r; + + v = u; + u = r; + x2 = x1; + x1 = x; + y2 = y1; + y1 = y; + } + a2 = r.neg(); + b2 = x; + + var len1 = a1.sqr().add(b1.sqr()); + var len2 = a2.sqr().add(b2.sqr()); + if (len2.cmp(len1) >= 0) { + a2 = a0; + b2 = b0; + } + + // Normalize signs + if (a1.negative) { + a1 = a1.neg(); + b1 = b1.neg(); + } + if (a2.negative) { + a2 = a2.neg(); + b2 = b2.neg(); + } + + return [ + { a: a1, b: b1 }, + { a: a2, b: b2 } + ]; +}; + +ShortCurve.prototype._endoSplit = function _endoSplit(k) { + var basis = this.endo.basis; + var v1 = basis[0]; + var v2 = basis[1]; + + var c1 = v2.b.mul(k).divRound(this.n); + var c2 = v1.b.neg().mul(k).divRound(this.n); + + var p1 = c1.mul(v1.a); + var p2 = c2.mul(v2.a); + var q1 = c1.mul(v1.b); + var q2 = c2.mul(v2.b); + + // Calculate answer + var k1 = k.sub(p1).sub(p2); + var k2 = q1.add(q2).neg(); + return { k1: k1, k2: k2 }; +}; + +ShortCurve.prototype.pointFromX = function pointFromX(x, odd) { + x = new BN(x, 16); + if (!x.red) + x = x.toRed(this.red); + + var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b); + var y = y2.redSqrt(); + if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) + throw new Error('invalid point'); + + // XXX Is there any way to tell if the number is odd without converting it + // to non-red form? + var isOdd = y.fromRed().isOdd(); + if (odd && !isOdd || !odd && isOdd) + y = y.redNeg(); + + return this.point(x, y); +}; + +ShortCurve.prototype.validate = function validate(point) { + if (point.inf) + return true; + + var x = point.x; + var y = point.y; + + var ax = this.a.redMul(x); + var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b); + return y.redSqr().redISub(rhs).cmpn(0) === 0; +}; + +ShortCurve.prototype._endoWnafMulAdd = + function _endoWnafMulAdd(points, coeffs, jacobianResult) { + var npoints = this._endoWnafT1; + var ncoeffs = this._endoWnafT2; + for (var i = 0; i < points.length; i++) { + var split = this._endoSplit(coeffs[i]); + var p = points[i]; + var beta = p._getBeta(); + + if (split.k1.negative) { + split.k1.ineg(); + p = p.neg(true); + } + if (split.k2.negative) { + split.k2.ineg(); + beta = beta.neg(true); + } + + npoints[i * 2] = p; + npoints[i * 2 + 1] = beta; + ncoeffs[i * 2] = split.k1; + ncoeffs[i * 2 + 1] = split.k2; + } + var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult); + + // Clean-up references to points and coefficients + for (var j = 0; j < i * 2; j++) { + npoints[j] = null; + ncoeffs[j] = null; + } + return res; +}; + +function Point(curve, x, y, isRed) { + Base.BasePoint.call(this, curve, 'affine'); + if (x === null && y === null) { + this.x = null; + this.y = null; + this.inf = true; + } else { + this.x = new BN(x, 16); + this.y = new BN(y, 16); + // Force redgomery representation when loading from JSON + if (isRed) { + this.x.forceRed(this.curve.red); + this.y.forceRed(this.curve.red); + } + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.y.red) + this.y = this.y.toRed(this.curve.red); + this.inf = false; + } +} +inherits(Point, Base.BasePoint); + +ShortCurve.prototype.point = function point(x, y, isRed) { + return new Point(this, x, y, isRed); +}; + +ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) { + return Point.fromJSON(this, obj, red); +}; + +Point.prototype._getBeta = function _getBeta() { + if (!this.curve.endo) + return; + + var pre = this.precomputed; + if (pre && pre.beta) + return pre.beta; + + var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y); + if (pre) { + var curve = this.curve; + var endoMul = function(p) { + return curve.point(p.x.redMul(curve.endo.beta), p.y); + }; + pre.beta = beta; + beta.precomputed = { + beta: null, + naf: pre.naf && { + wnd: pre.naf.wnd, + points: pre.naf.points.map(endoMul) + }, + doubles: pre.doubles && { + step: pre.doubles.step, + points: pre.doubles.points.map(endoMul) + } + }; + } + return beta; +}; + +Point.prototype.toJSON = function toJSON() { + if (!this.precomputed) + return [ this.x, this.y ]; + + return [ this.x, this.y, this.precomputed && { + doubles: this.precomputed.doubles && { + step: this.precomputed.doubles.step, + points: this.precomputed.doubles.points.slice(1) + }, + naf: this.precomputed.naf && { + wnd: this.precomputed.naf.wnd, + points: this.precomputed.naf.points.slice(1) + } + } ]; +}; + +Point.fromJSON = function fromJSON(curve, obj, red) { + if (typeof obj === 'string') + obj = JSON.parse(obj); + var res = curve.point(obj[0], obj[1], red); + if (!obj[2]) + return res; + + function obj2point(obj) { + return curve.point(obj[0], obj[1], red); + } + + var pre = obj[2]; + res.precomputed = { + beta: null, + doubles: pre.doubles && { + step: pre.doubles.step, + points: [ res ].concat(pre.doubles.points.map(obj2point)) + }, + naf: pre.naf && { + wnd: pre.naf.wnd, + points: [ res ].concat(pre.naf.points.map(obj2point)) + } + }; + return res; +}; + +Point.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +Point.prototype.isInfinity = function isInfinity() { + return this.inf; +}; + +Point.prototype.add = function add(p) { + // O + P = P + if (this.inf) + return p; + + // P + O = P + if (p.inf) + return this; + + // P + P = 2P + if (this.eq(p)) + return this.dbl(); + + // P + (-P) = O + if (this.neg().eq(p)) + return this.curve.point(null, null); + + // P + Q = O + if (this.x.cmp(p.x) === 0) + return this.curve.point(null, null); + + var c = this.y.redSub(p.y); + if (c.cmpn(0) !== 0) + c = c.redMul(this.x.redSub(p.x).redInvm()); + var nx = c.redSqr().redISub(this.x).redISub(p.x); + var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); + return this.curve.point(nx, ny); +}; + +Point.prototype.dbl = function dbl() { + if (this.inf) + return this; + + // 2P = O + var ys1 = this.y.redAdd(this.y); + if (ys1.cmpn(0) === 0) + return this.curve.point(null, null); + + var a = this.curve.a; + + var x2 = this.x.redSqr(); + var dyinv = ys1.redInvm(); + var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv); + + var nx = c.redSqr().redISub(this.x.redAdd(this.x)); + var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); + return this.curve.point(nx, ny); +}; + +Point.prototype.getX = function getX() { + return this.x.fromRed(); +}; + +Point.prototype.getY = function getY() { + return this.y.fromRed(); +}; + +Point.prototype.mul = function mul(k) { + k = new BN(k, 16); + + if (this._hasDoubles(k)) + return this.curve._fixedNafMul(this, k); + else if (this.curve.endo) + return this.curve._endoWnafMulAdd([ this ], [ k ]); + else + return this.curve._wnafMul(this, k); +}; + +Point.prototype.mulAdd = function mulAdd(k1, p2, k2) { + var points = [ this, p2 ]; + var coeffs = [ k1, k2 ]; + if (this.curve.endo) + return this.curve._endoWnafMulAdd(points, coeffs); + else + return this.curve._wnafMulAdd(1, points, coeffs, 2); +}; + +Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) { + var points = [ this, p2 ]; + var coeffs = [ k1, k2 ]; + if (this.curve.endo) + return this.curve._endoWnafMulAdd(points, coeffs, true); + else + return this.curve._wnafMulAdd(1, points, coeffs, 2, true); +}; + +Point.prototype.eq = function eq(p) { + return this === p || + this.inf === p.inf && + (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0); +}; + +Point.prototype.neg = function neg(_precompute) { + if (this.inf) + return this; + + var res = this.curve.point(this.x, this.y.redNeg()); + if (_precompute && this.precomputed) { + var pre = this.precomputed; + var negate = function(p) { + return p.neg(); + }; + res.precomputed = { + naf: pre.naf && { + wnd: pre.naf.wnd, + points: pre.naf.points.map(negate) + }, + doubles: pre.doubles && { + step: pre.doubles.step, + points: pre.doubles.points.map(negate) + } + }; + } + return res; +}; + +Point.prototype.toJ = function toJ() { + if (this.inf) + return this.curve.jpoint(null, null, null); + + var res = this.curve.jpoint(this.x, this.y, this.curve.one); + return res; +}; + +function JPoint(curve, x, y, z) { + Base.BasePoint.call(this, curve, 'jacobian'); + if (x === null && y === null && z === null) { + this.x = this.curve.one; + this.y = this.curve.one; + this.z = new BN(0); + } else { + this.x = new BN(x, 16); + this.y = new BN(y, 16); + this.z = new BN(z, 16); + } + if (!this.x.red) + this.x = this.x.toRed(this.curve.red); + if (!this.y.red) + this.y = this.y.toRed(this.curve.red); + if (!this.z.red) + this.z = this.z.toRed(this.curve.red); + + this.zOne = this.z === this.curve.one; +} +inherits(JPoint, Base.BasePoint); + +ShortCurve.prototype.jpoint = function jpoint(x, y, z) { + return new JPoint(this, x, y, z); +}; + +JPoint.prototype.toP = function toP() { + if (this.isInfinity()) + return this.curve.point(null, null); + + var zinv = this.z.redInvm(); + var zinv2 = zinv.redSqr(); + var ax = this.x.redMul(zinv2); + var ay = this.y.redMul(zinv2).redMul(zinv); + + return this.curve.point(ax, ay); +}; + +JPoint.prototype.neg = function neg() { + return this.curve.jpoint(this.x, this.y.redNeg(), this.z); +}; + +JPoint.prototype.add = function add(p) { + // O + P = P + if (this.isInfinity()) + return p; + + // P + O = P + if (p.isInfinity()) + return this; + + // 12M + 4S + 7A + var pz2 = p.z.redSqr(); + var z2 = this.z.redSqr(); + var u1 = this.x.redMul(pz2); + var u2 = p.x.redMul(z2); + var s1 = this.y.redMul(pz2.redMul(p.z)); + var s2 = p.y.redMul(z2.redMul(this.z)); + + var h = u1.redSub(u2); + var r = s1.redSub(s2); + if (h.cmpn(0) === 0) { + if (r.cmpn(0) !== 0) + return this.curve.jpoint(null, null, null); + else + return this.dbl(); + } + + var h2 = h.redSqr(); + var h3 = h2.redMul(h); + var v = u1.redMul(h2); + + var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); + var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); + var nz = this.z.redMul(p.z).redMul(h); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.mixedAdd = function mixedAdd(p) { + // O + P = P + if (this.isInfinity()) + return p.toJ(); + + // P + O = P + if (p.isInfinity()) + return this; + + // 8M + 3S + 7A + var z2 = this.z.redSqr(); + var u1 = this.x; + var u2 = p.x.redMul(z2); + var s1 = this.y; + var s2 = p.y.redMul(z2).redMul(this.z); + + var h = u1.redSub(u2); + var r = s1.redSub(s2); + if (h.cmpn(0) === 0) { + if (r.cmpn(0) !== 0) + return this.curve.jpoint(null, null, null); + else + return this.dbl(); + } + + var h2 = h.redSqr(); + var h3 = h2.redMul(h); + var v = u1.redMul(h2); + + var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); + var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); + var nz = this.z.redMul(h); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.dblp = function dblp(pow) { + if (pow === 0) + return this; + if (this.isInfinity()) + return this; + if (!pow) + return this.dbl(); + + if (this.curve.zeroA || this.curve.threeA) { + var r = this; + for (var i = 0; i < pow; i++) + r = r.dbl(); + return r; + } + + // 1M + 2S + 1A + N * (4S + 5M + 8A) + // N = 1 => 6M + 6S + 9A + var a = this.curve.a; + var tinv = this.curve.tinv; + + var jx = this.x; + var jy = this.y; + var jz = this.z; + var jz4 = jz.redSqr().redSqr(); + + // Reuse results + var jyd = jy.redAdd(jy); + for (var i = 0; i < pow; i++) { + var jx2 = jx.redSqr(); + var jyd2 = jyd.redSqr(); + var jyd4 = jyd2.redSqr(); + var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); + + var t1 = jx.redMul(jyd2); + var nx = c.redSqr().redISub(t1.redAdd(t1)); + var t2 = t1.redISub(nx); + var dny = c.redMul(t2); + dny = dny.redIAdd(dny).redISub(jyd4); + var nz = jyd.redMul(jz); + if (i + 1 < pow) + jz4 = jz4.redMul(jyd4); + + jx = nx; + jz = nz; + jyd = dny; + } + + return this.curve.jpoint(jx, jyd.redMul(tinv), jz); +}; + +JPoint.prototype.dbl = function dbl() { + if (this.isInfinity()) + return this; + + if (this.curve.zeroA) + return this._zeroDbl(); + else if (this.curve.threeA) + return this._threeDbl(); + else + return this._dbl(); +}; + +JPoint.prototype._zeroDbl = function _zeroDbl() { + var nx; + var ny; + var nz; + // Z = 1 + if (this.zOne) { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html + // #doubling-mdbl-2007-bl + // 1M + 5S + 14A + + // XX = X1^2 + var xx = this.x.redSqr(); + // YY = Y1^2 + var yy = this.y.redSqr(); + // YYYY = YY^2 + var yyyy = yy.redSqr(); + // S = 2 * ((X1 + YY)^2 - XX - YYYY) + var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); + s = s.redIAdd(s); + // M = 3 * XX + a; a = 0 + var m = xx.redAdd(xx).redIAdd(xx); + // T = M ^ 2 - 2*S + var t = m.redSqr().redISub(s).redISub(s); + + // 8 * YYYY + var yyyy8 = yyyy.redIAdd(yyyy); + yyyy8 = yyyy8.redIAdd(yyyy8); + yyyy8 = yyyy8.redIAdd(yyyy8); + + // X3 = T + nx = t; + // Y3 = M * (S - T) - 8 * YYYY + ny = m.redMul(s.redISub(t)).redISub(yyyy8); + // Z3 = 2*Y1 + nz = this.y.redAdd(this.y); + } else { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html + // #doubling-dbl-2009-l + // 2M + 5S + 13A + + // A = X1^2 + var a = this.x.redSqr(); + // B = Y1^2 + var b = this.y.redSqr(); + // C = B^2 + var c = b.redSqr(); + // D = 2 * ((X1 + B)^2 - A - C) + var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c); + d = d.redIAdd(d); + // E = 3 * A + var e = a.redAdd(a).redIAdd(a); + // F = E^2 + var f = e.redSqr(); + + // 8 * C + var c8 = c.redIAdd(c); + c8 = c8.redIAdd(c8); + c8 = c8.redIAdd(c8); + + // X3 = F - 2 * D + nx = f.redISub(d).redISub(d); + // Y3 = E * (D - X3) - 8 * C + ny = e.redMul(d.redISub(nx)).redISub(c8); + // Z3 = 2 * Y1 * Z1 + nz = this.y.redMul(this.z); + nz = nz.redIAdd(nz); + } + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype._threeDbl = function _threeDbl() { + var nx; + var ny; + var nz; + // Z = 1 + if (this.zOne) { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html + // #doubling-mdbl-2007-bl + // 1M + 5S + 15A + + // XX = X1^2 + var xx = this.x.redSqr(); + // YY = Y1^2 + var yy = this.y.redSqr(); + // YYYY = YY^2 + var yyyy = yy.redSqr(); + // S = 2 * ((X1 + YY)^2 - XX - YYYY) + var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); + s = s.redIAdd(s); + // M = 3 * XX + a + var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a); + // T = M^2 - 2 * S + var t = m.redSqr().redISub(s).redISub(s); + // X3 = T + nx = t; + // Y3 = M * (S - T) - 8 * YYYY + var yyyy8 = yyyy.redIAdd(yyyy); + yyyy8 = yyyy8.redIAdd(yyyy8); + yyyy8 = yyyy8.redIAdd(yyyy8); + ny = m.redMul(s.redISub(t)).redISub(yyyy8); + // Z3 = 2 * Y1 + nz = this.y.redAdd(this.y); + } else { + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b + // 3M + 5S + + // delta = Z1^2 + var delta = this.z.redSqr(); + // gamma = Y1^2 + var gamma = this.y.redSqr(); + // beta = X1 * gamma + var beta = this.x.redMul(gamma); + // alpha = 3 * (X1 - delta) * (X1 + delta) + var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta)); + alpha = alpha.redAdd(alpha).redIAdd(alpha); + // X3 = alpha^2 - 8 * beta + var beta4 = beta.redIAdd(beta); + beta4 = beta4.redIAdd(beta4); + var beta8 = beta4.redAdd(beta4); + nx = alpha.redSqr().redISub(beta8); + // Z3 = (Y1 + Z1)^2 - gamma - delta + nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta); + // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2 + var ggamma8 = gamma.redSqr(); + ggamma8 = ggamma8.redIAdd(ggamma8); + ggamma8 = ggamma8.redIAdd(ggamma8); + ggamma8 = ggamma8.redIAdd(ggamma8); + ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8); + } + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype._dbl = function _dbl() { + var a = this.curve.a; + + // 4M + 6S + 10A + var jx = this.x; + var jy = this.y; + var jz = this.z; + var jz4 = jz.redSqr().redSqr(); + + var jx2 = jx.redSqr(); + var jy2 = jy.redSqr(); + + var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); + + var jxd4 = jx.redAdd(jx); + jxd4 = jxd4.redIAdd(jxd4); + var t1 = jxd4.redMul(jy2); + var nx = c.redSqr().redISub(t1.redAdd(t1)); + var t2 = t1.redISub(nx); + + var jyd8 = jy2.redSqr(); + jyd8 = jyd8.redIAdd(jyd8); + jyd8 = jyd8.redIAdd(jyd8); + jyd8 = jyd8.redIAdd(jyd8); + var ny = c.redMul(t2).redISub(jyd8); + var nz = jy.redAdd(jy).redMul(jz); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.trpl = function trpl() { + if (!this.curve.zeroA) + return this.dbl().add(this); + + // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl + // 5M + 10S + ... + + // XX = X1^2 + var xx = this.x.redSqr(); + // YY = Y1^2 + var yy = this.y.redSqr(); + // ZZ = Z1^2 + var zz = this.z.redSqr(); + // YYYY = YY^2 + var yyyy = yy.redSqr(); + // M = 3 * XX + a * ZZ2; a = 0 + var m = xx.redAdd(xx).redIAdd(xx); + // MM = M^2 + var mm = m.redSqr(); + // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM + var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); + e = e.redIAdd(e); + e = e.redAdd(e).redIAdd(e); + e = e.redISub(mm); + // EE = E^2 + var ee = e.redSqr(); + // T = 16*YYYY + var t = yyyy.redIAdd(yyyy); + t = t.redIAdd(t); + t = t.redIAdd(t); + t = t.redIAdd(t); + // U = (M + E)^2 - MM - EE - T + var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t); + // X3 = 4 * (X1 * EE - 4 * YY * U) + var yyu4 = yy.redMul(u); + yyu4 = yyu4.redIAdd(yyu4); + yyu4 = yyu4.redIAdd(yyu4); + var nx = this.x.redMul(ee).redISub(yyu4); + nx = nx.redIAdd(nx); + nx = nx.redIAdd(nx); + // Y3 = 8 * Y1 * (U * (T - U) - E * EE) + var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee))); + ny = ny.redIAdd(ny); + ny = ny.redIAdd(ny); + ny = ny.redIAdd(ny); + // Z3 = (Z1 + E)^2 - ZZ - EE + var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee); + + return this.curve.jpoint(nx, ny, nz); +}; + +JPoint.prototype.mul = function mul(k, kbase) { + k = new BN(k, kbase); + + return this.curve._wnafMul(this, k); +}; + +JPoint.prototype.eq = function eq(p) { + if (p.type === 'affine') + return this.eq(p.toJ()); + + if (this === p) + return true; + + // x1 * z2^2 == x2 * z1^2 + var z2 = this.z.redSqr(); + var pz2 = p.z.redSqr(); + if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0) + return false; + + // y1 * z2^3 == y2 * z1^3 + var z3 = z2.redMul(this.z); + var pz3 = pz2.redMul(p.z); + return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0; +}; + +JPoint.prototype.eqXToP = function eqXToP(x) { + var zs = this.z.redSqr(); + var rx = x.toRed(this.curve.red).redMul(zs); + if (this.x.cmp(rx) === 0) + return true; + + var xc = x.clone(); + var t = this.curve.redN.redMul(zs); + for (;;) { + xc.iadd(this.curve.n); + if (xc.cmp(this.curve.p) >= 0) + return false; + + rx.redIAdd(t); + if (this.x.cmp(rx) === 0) + return true; + } + return false; +}; + +JPoint.prototype.inspect = function inspect() { + if (this.isInfinity()) + return ''; + return ''; +}; + +JPoint.prototype.isInfinity = function isInfinity() { + // XXX This code assumes that zero is always zero in red + return this.z.cmpn(0) === 0; +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curves.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curves.js new file mode 100644 index 000000000..1d5e598b0 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/curves.js @@ -0,0 +1,204 @@ +'use strict'; + +var curves = exports; + +var elliptic = require('../elliptic'); + +var assert = elliptic.utils.assert; + +function PresetCurve(options) { + if (options.type === 'short') + this.curve = new elliptic.curve.short(options); + else if (options.type === 'edwards') + this.curve = new elliptic.curve.edwards(options); + else + this.curve = new elliptic.curve.mont(options); + this.g = this.curve.g; + this.n = this.curve.n; + this.hash = options.hash; + + assert(this.g.validate(), 'Invalid curve'); + assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N != O'); +} +curves.PresetCurve = PresetCurve; + +function defineCurve(name, options) { + Object.defineProperty(curves, name, { + configurable: true, + enumerable: true, + get: function() { + var curve = new PresetCurve(options); + Object.defineProperty(curves, name, { + configurable: true, + enumerable: true, + value: curve + }); + return curve; + } + }); +} + +defineCurve('p192', { + type: 'short', + prime: 'p192', + p: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff', + a: 'ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc', + b: '64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1', + n: 'ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831', + hash: 'SHA256', + gRed: false, + g: [ + '188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012', + '07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811' + ] +}); + +defineCurve('p224', { + type: 'short', + prime: 'p224', + p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001', + a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe', + b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4', + n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d', + hash: 'SHA256', + gRed: false, + g: [ + 'b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21', + 'bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34' + ] +}); + +defineCurve('p256', { + type: 'short', + prime: null, + p: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff', + a: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc', + b: '5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b', + n: 'ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551', + hash: 'SHA256', + gRed: false, + g: [ + '6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296', + '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5' + ] +}); + +defineCurve('p384', { + type: 'short', + prime: null, + p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'fffffffe ffffffff 00000000 00000000 ffffffff', + a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'fffffffe ffffffff 00000000 00000000 fffffffc', + b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' + + '5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef', + n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' + + 'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973', + hash: 'SHA384', + gRed: false, + g: [ + 'aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 ' + + '5502f25d bf55296c 3a545e38 72760ab7', + '3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 ' + + '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f' + ] +}); + +defineCurve('p521', { + type: 'short', + prime: null, + p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff ffffffff', + a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff ffffffff ffffffff fffffffc', + b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' + + '99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' + + '3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00', + n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + + 'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' + + 'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409', + hash: 'SHA512', + gRed: false, + g: [ + '000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 ' + + '053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 ' + + 'a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66', + '00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 ' + + '579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 ' + + '3fad0761 353c7086 a272c240 88be9476 9fd16650' + ] +}); + +defineCurve('curve25519', { + type: 'mont', + prime: 'p25519', + p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', + a: '76d06', + b: '1', + n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', + hash: 'SHA256', + gRed: false, + g: [ + '9' + ] +}); + +defineCurve('ed25519', { + type: 'edwards', + prime: 'p25519', + p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', + a: '-1', + c: '1', + // -121665 * (121666^(-1)) (mod P) + d: '52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3', + n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', + hash: 'SHA256', + gRed: false, + g: [ + '216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a', + + // 4/5 + '6666666666666666666666666666666666666666666666666666666666666658' + ] +}); + +var pre; +try { + pre = require('./precomputed/secp256k1'); +} catch (e) { + pre = undefined; +} + +defineCurve('secp256k1', { + type: 'short', + prime: 'k256', + p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f', + a: '0', + b: '7', + n: 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141', + h: '1', + hash: 'SHA256', + + // Precomputed endomorphism + beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', + lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', + basis: [ + { + a: '3086d221a7d46bcde86c90e49284eb15', + b: '-e4437ed6010e88286f547fa90abfe4c3' + }, + { + a: '114ca50f7a8e2f3f657c1108d9d44cfd8', + b: '3086d221a7d46bcde86c90e49284eb15' + } + ], + + gRed: false, + g: [ + '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', + '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', + pre + ] +}); diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/index.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/index.js new file mode 100644 index 000000000..b117d53a1 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/index.js @@ -0,0 +1,240 @@ +'use strict'; + +var BN = require('../../../../BN/bn'); +var HmacDRBG = require('../../hmac-drbg/hmac-drbg'); +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var assert = utils.assert; + +var KeyPair = require('./key'); +var Signature = require('./signature'); + +function EC(options) { + if (!(this instanceof EC)) + return new EC(options); + + // Shortcut `elliptic.ec(curve-name)` + if (typeof options === 'string') { + assert(elliptic.curves.hasOwnProperty(options), 'Unknown curve ' + options); + + options = elliptic.curves[options]; + } + + // Shortcut for `elliptic.ec(elliptic.curves.curveName)` + if (options instanceof elliptic.curves.PresetCurve) + options = { curve: options }; + + this.curve = options.curve.curve; + this.n = this.curve.n; + this.nh = this.n.ushrn(1); + this.g = this.curve.g; + + // Point on curve + this.g = options.curve.g; + this.g.precompute(options.curve.n.bitLength() + 1); + + // Hash for function for DRBG + this.hash = options.hash || options.curve.hash; +} +module.exports = EC; + +EC.prototype.keyPair = function keyPair(options) { + return new KeyPair(this, options); +}; + +EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) { + return KeyPair.fromPrivate(this, priv, enc); +}; + +EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) { + return KeyPair.fromPublic(this, pub, enc); +}; + +EC.prototype.genKeyPair = function genKeyPair(options) { + if (!options) + options = {}; + + // Instantiate Hmac_DRBG + var drbg = new HmacDRBG({ + hash: this.hash, + pers: options.pers, + persEnc: options.persEnc || 'utf8', + entropy: options.entropy || null, //elliptic.rand(this.hash.hmacStrength), + entropyEnc: options.entropy && options.entropyEnc || 'utf8', + nonce: this.n.toArray() + }); + + var bytes = this.n.byteLength(); + var ns2 = this.n.sub(new BN(2)); + do { + var priv = new BN(drbg.generate(bytes)); + if (priv.cmp(ns2) > 0) + continue; + + priv.iaddn(1); + return this.keyFromPrivate(priv); + } while (true); +}; + +EC.prototype._truncateToN = function truncateToN(msg, truncOnly) { + var delta = msg.byteLength() * 8 - this.n.bitLength(); + if (delta > 0) + msg = msg.ushrn(delta); + if (!truncOnly && msg.cmp(this.n) >= 0) + return msg.sub(this.n); + else + return msg; +}; + +EC.prototype.sign = function sign(msg, key, enc, options) { + if (typeof enc === 'object') { + options = enc; + enc = null; + } + if (!options) + options = {}; + + key = this.keyFromPrivate(key, enc); + msg = this._truncateToN(new BN(msg, 16)); + + // Zero-extend key to provide enough entropy + var bytes = this.n.byteLength(); + var bkey = key.getPrivate().toArray('be', bytes); + + // Zero-extend nonce to have the same byte size as N + var nonce = msg.toArray('be', bytes); + + // Instantiate Hmac_DRBG + var drbg = new HmacDRBG({ + hash: this.hash, + entropy: options['extraEntropy'] ? bkey.concat (options['extraEntropy']) : bkey, + nonce: nonce, + pers: options.pers, + persEnc: options.persEnc || 'utf8' + }); + + // Number of bytes to generate + var ns1 = this.n.sub(new BN(1)); + + for (var iter = 0; true; iter++) { + var k = options.k ? + options.k(iter) : + new BN(drbg.generate(this.n.byteLength())); + k = this._truncateToN(k, true); + if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) + continue; + + var kp = this.g.mul(k); + if (kp.isInfinity()) + continue; + + var kpX = kp.getX(); + var r = kpX.umod(this.n); + if (r.cmpn(0) === 0) + continue; + + var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg)); + s = s.umod(this.n); + if (s.cmpn(0) === 0) + continue; + + var recoveryParam = (kp.getY().isOdd() ? 1 : 0) | + (kpX.cmp(r) !== 0 ? 2 : 0); + + // Use complement of `s`, if it is > `n / 2` + if (options.canonical && s.cmp(this.nh) > 0) { + s = this.n.sub(s); + recoveryParam ^= 1; + } + + return new Signature({ r: r, s: s, recoveryParam: recoveryParam }); + } +}; + +EC.prototype.verify = function verify(msg, signature, key, enc) { + msg = this._truncateToN(new BN(msg, 16)); + key = this.keyFromPublic(key, enc); + signature = new Signature(signature, 'hex'); + + // Perform primitive values validation + var r = signature.r; + var s = signature.s; + if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0) + return false; + if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0) + return false; + + // Validate signature + var sinv = s.invm(this.n); + var u1 = sinv.mul(msg).umod(this.n); + var u2 = sinv.mul(r).umod(this.n); + + if (!this.curve._maxwellTrick) { + var p = this.g.mulAdd(u1, key.getPublic(), u2); + if (p.isInfinity()) + return false; + + return p.getX().umod(this.n).cmp(r) === 0; + } + + // NOTE: Greg Maxwell's trick, inspired by: + // https://git.io/vad3K + + var p = this.g.jmulAdd(u1, key.getPublic(), u2); + if (p.isInfinity()) + return false; + + // Compare `p.x` of Jacobian point with `r`, + // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the + // inverse of `p.z^2` + return p.eqXToP(r); +}; + +EC.prototype.recoverPubKey = function(msg, signature, j, enc) { + assert((3 & j) === j, 'The recovery param is more than two bits'); + signature = new Signature(signature, enc); + + var n = this.n; + var e = new BN(msg); + var r = signature.r; + var s = signature.s; + + // A set LSB signifies that the y-coordinate is odd + var isYOdd = j & 1; + var isSecondKey = j >> 1; + if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey) + throw new Error('Unable to find sencond key candinate'); + + // 1.1. Let x = r + jn. + if (isSecondKey) + r = this.curve.pointFromX(r.add(this.curve.n), isYOdd); + else + r = this.curve.pointFromX(r, isYOdd); + + var rInv = signature.r.invm(n); + var s1 = n.sub(e).mul(rInv).umod(n); + var s2 = s.mul(rInv).umod(n); + + // 1.6.1 Compute Q = r^-1 (sR - eG) + // Q = r^-1 (sR + -eG) + return this.g.mulAdd(s1, r, s2); +}; + +EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { + signature = new Signature(signature, enc); + if (signature.recoveryParam !== null) + return signature.recoveryParam; + + for (var i = 0; i < 4; i++) { + var Qprime; + try { + Qprime = this.recoverPubKey(e, signature, i); + } catch (e) { + continue; + } + + if (Qprime.eq(Q)) + return i; + } + throw new Error('Unable to find valid recovery factor'); +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/key.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/key.js new file mode 100644 index 000000000..6154740f5 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/key.js @@ -0,0 +1,119 @@ +'use strict'; + +var BN = require('../../../../BN/bn'); +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var assert = utils.assert; + +function KeyPair(ec, options) { + this.ec = ec; + this.priv = null; + this.pub = null; + + // KeyPair(ec, { priv: ..., pub: ... }) + if (options.priv) + this._importPrivate(options.priv, options.privEnc); + if (options.pub) + this._importPublic(options.pub, options.pubEnc); +} +module.exports = KeyPair; + +KeyPair.fromPublic = function fromPublic(ec, pub, enc) { + if (pub instanceof KeyPair) + return pub; + + return new KeyPair(ec, { + pub: pub, + pubEnc: enc + }); +}; + +KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) { + if (priv instanceof KeyPair) + return priv; + + return new KeyPair(ec, { + priv: priv, + privEnc: enc + }); +}; + +KeyPair.prototype.validate = function validate() { + var pub = this.getPublic(); + + if (pub.isInfinity()) + return { result: false, reason: 'Invalid public key' }; + if (!pub.validate()) + return { result: false, reason: 'Public key is not a point' }; + if (!pub.mul(this.ec.curve.n).isInfinity()) + return { result: false, reason: 'Public key * N != O' }; + + return { result: true, reason: null }; +}; + +KeyPair.prototype.getPublic = function getPublic(compact, enc) { + // compact is optional argument + if (typeof compact === 'string') { + enc = compact; + compact = null; + } + + if (!this.pub) + this.pub = this.ec.g.mul(this.priv); + + if (!enc) + return this.pub; + + return this.pub.encode(enc, compact); +}; + +KeyPair.prototype.getPrivate = function getPrivate(enc) { + if (enc === 'hex') + return this.priv.toString(16, 2); + else + return this.priv; +}; + +KeyPair.prototype._importPrivate = function _importPrivate(key, enc) { + this.priv = new BN(key, enc || 16); + + // Ensure that the priv won't be bigger than n, otherwise we may fail + // in fixed multiplication method + this.priv = this.priv.umod(this.ec.curve.n); +}; + +KeyPair.prototype._importPublic = function _importPublic(key, enc) { + if (key.x || key.y) { + // Montgomery points only have an `x` coordinate. + // Weierstrass/Edwards points on the other hand have both `x` and + // `y` coordinates. + if (this.ec.curve.type === 'mont') { + assert(key.x, 'Need x coordinate'); + } else if (this.ec.curve.type === 'short' || + this.ec.curve.type === 'edwards') { + assert(key.x && key.y, 'Need both x and y coordinate'); + } + this.pub = this.ec.curve.point(key.x, key.y); + return; + } + this.pub = this.ec.curve.decodePoint(key, enc); +}; + +// ECDH +KeyPair.prototype.derive = function derive(pub) { + return pub.mul(this.priv).getX(); +}; + +// ECDSA +KeyPair.prototype.sign = function sign(msg, enc, options) { + return this.ec.sign(msg, this, enc, options); +}; + +KeyPair.prototype.verify = function verify(msg, signature) { + return this.ec.verify(msg, signature, this); +}; + +KeyPair.prototype.inspect = function inspect() { + return ''; +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/signature.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/signature.js new file mode 100644 index 000000000..df2136c95 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/ec/signature.js @@ -0,0 +1,24 @@ +'use strict'; + +var BN = require('../../../../BN/bn'); + +var elliptic = require('../../elliptic'); +var utils = elliptic.utils; +var assert = utils.assert; + +function Signature(options, enc) { + if (options instanceof Signature) + return options; + + //if (this._importDER(options, enc)) + // return; + + assert(options.r && options.s, 'Signature without r or s'); + this.r = new BN(options.r, 16); + this.s = new BN(options.s, 16); + if (options.recoveryParam === undefined) + this.recoveryParam = null; + else + this.recoveryParam = options.recoveryParam; +} +module.exports = Signature; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/precomputed/secp256k1.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/precomputed/secp256k1.js new file mode 100644 index 000000000..e4c91e543 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/precomputed/secp256k1.js @@ -0,0 +1,780 @@ +module.exports = { + doubles: { + step: 4, + points: [ + [ + 'e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a', + 'f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821' + ], + [ + '8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508', + '11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf' + ], + [ + '175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739', + 'd3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695' + ], + [ + '363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640', + '4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9' + ], + [ + '8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c', + '4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36' + ], + [ + '723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda', + '96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f' + ], + [ + 'eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa', + '5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999' + ], + [ + '100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0', + 'cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09' + ], + [ + 'e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d', + '9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d' + ], + [ + 'feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d', + 'e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088' + ], + [ + 'da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1', + '9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d' + ], + [ + '53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0', + '5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8' + ], + [ + '8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047', + '10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a' + ], + [ + '385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862', + '283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453' + ], + [ + '6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7', + '7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160' + ], + [ + '3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd', + '56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0' + ], + [ + '85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83', + '7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6' + ], + [ + '948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a', + '53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589' + ], + [ + '6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8', + 'bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17' + ], + [ + 'e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d', + '4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda' + ], + [ + 'e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725', + '7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd' + ], + [ + '213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754', + '4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2' + ], + [ + '4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c', + '17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6' + ], + [ + 'fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6', + '6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f' + ], + [ + '76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39', + 'c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01' + ], + [ + 'c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891', + '893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3' + ], + [ + 'd895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b', + 'febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f' + ], + [ + 'b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03', + '2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7' + ], + [ + 'e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d', + 'eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78' + ], + [ + 'a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070', + '7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1' + ], + [ + '90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4', + 'e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150' + ], + [ + '8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da', + '662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82' + ], + [ + 'e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11', + '1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc' + ], + [ + '8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e', + 'efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b' + ], + [ + 'e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41', + '2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51' + ], + [ + 'b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef', + '67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45' + ], + [ + 'd68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8', + 'db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120' + ], + [ + '324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d', + '648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84' + ], + [ + '4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96', + '35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d' + ], + [ + '9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd', + 'ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d' + ], + [ + '6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5', + '9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8' + ], + [ + 'a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266', + '40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8' + ], + [ + '7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71', + '34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac' + ], + [ + '928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac', + 'c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f' + ], + [ + '85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751', + '1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962' + ], + [ + 'ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e', + '493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907' + ], + [ + '827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241', + 'c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec' + ], + [ + 'eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3', + 'be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d' + ], + [ + 'e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f', + '4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414' + ], + [ + '1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19', + 'aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd' + ], + [ + '146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be', + 'b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0' + ], + [ + 'fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9', + '6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811' + ], + [ + 'da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2', + '8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1' + ], + [ + 'a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13', + '7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c' + ], + [ + '174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c', + 'ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73' + ], + [ + '959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba', + '2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd' + ], + [ + 'd2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151', + 'e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405' + ], + [ + '64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073', + 'd99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589' + ], + [ + '8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458', + '38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e' + ], + [ + '13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b', + '69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27' + ], + [ + 'bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366', + 'd3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1' + ], + [ + '8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa', + '40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482' + ], + [ + '8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0', + '620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945' + ], + [ + 'dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787', + '7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573' + ], + [ + 'f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e', + 'ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82' + ] + ] + }, + naf: { + wnd: 7, + points: [ + [ + 'f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9', + '388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672' + ], + [ + '2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4', + 'd8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6' + ], + [ + '5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc', + '6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da' + ], + [ + 'acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe', + 'cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37' + ], + [ + '774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb', + 'd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b' + ], + [ + 'f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8', + 'ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81' + ], + [ + 'd7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e', + '581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58' + ], + [ + 'defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34', + '4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77' + ], + [ + '2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c', + '85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a' + ], + [ + '352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5', + '321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c' + ], + [ + '2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f', + '2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67' + ], + [ + '9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714', + '73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402' + ], + [ + 'daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729', + 'a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55' + ], + [ + 'c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db', + '2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482' + ], + [ + '6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4', + 'e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82' + ], + [ + '1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5', + 'b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396' + ], + [ + '605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479', + '2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49' + ], + [ + '62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d', + '80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf' + ], + [ + '80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f', + '1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a' + ], + [ + '7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb', + 'd0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7' + ], + [ + 'd528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9', + 'eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933' + ], + [ + '49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963', + '758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a' + ], + [ + '77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74', + '958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6' + ], + [ + 'f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530', + 'e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37' + ], + [ + '463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b', + '5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e' + ], + [ + 'f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247', + 'cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6' + ], + [ + 'caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1', + 'cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476' + ], + [ + '2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120', + '4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40' + ], + [ + '7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435', + '91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61' + ], + [ + '754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18', + '673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683' + ], + [ + 'e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8', + '59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5' + ], + [ + '186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb', + '3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b' + ], + [ + 'df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f', + '55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417' + ], + [ + '5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143', + 'efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868' + ], + [ + '290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba', + 'e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a' + ], + [ + 'af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45', + 'f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6' + ], + [ + '766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a', + '744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996' + ], + [ + '59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e', + 'c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e' + ], + [ + 'f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8', + 'e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d' + ], + [ + '7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c', + '30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2' + ], + [ + '948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519', + 'e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e' + ], + [ + '7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab', + '100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437' + ], + [ + '3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca', + 'ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311' + ], + [ + 'd3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf', + '8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4' + ], + [ + '1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610', + '68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575' + ], + [ + '733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4', + 'f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d' + ], + [ + '15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c', + 'd56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d' + ], + [ + 'a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940', + 'edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629' + ], + [ + 'e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980', + 'a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06' + ], + [ + '311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3', + '66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374' + ], + [ + '34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf', + '9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee' + ], + [ + 'f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63', + '4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1' + ], + [ + 'd7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448', + 'fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b' + ], + [ + '32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf', + '5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661' + ], + [ + '7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5', + '8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6' + ], + [ + 'ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6', + '8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e' + ], + [ + '16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5', + '5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d' + ], + [ + 'eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99', + 'f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc' + ], + [ + '78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51', + 'f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4' + ], + [ + '494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5', + '42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c' + ], + [ + 'a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5', + '204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b' + ], + [ + 'c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997', + '4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913' + ], + [ + '841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881', + '73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154' + ], + [ + '5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5', + '39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865' + ], + [ + '36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66', + 'd2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc' + ], + [ + '336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726', + 'ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224' + ], + [ + '8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede', + '6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e' + ], + [ + '1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94', + '60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6' + ], + [ + '85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31', + '3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511' + ], + [ + '29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51', + 'b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b' + ], + [ + 'a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252', + 'ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2' + ], + [ + '4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5', + 'cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c' + ], + [ + 'd24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b', + '6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3' + ], + [ + 'ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4', + '322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d' + ], + [ + 'af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f', + '6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700' + ], + [ + 'e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889', + '2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4' + ], + [ + '591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246', + 'b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196' + ], + [ + '11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984', + '998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4' + ], + [ + '3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a', + 'b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257' + ], + [ + 'cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030', + 'bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13' + ], + [ + 'c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197', + '6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096' + ], + [ + 'c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593', + 'c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38' + ], + [ + 'a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef', + '21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f' + ], + [ + '347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38', + '60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448' + ], + [ + 'da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a', + '49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a' + ], + [ + 'c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111', + '5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4' + ], + [ + '4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502', + '7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437' + ], + [ + '3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea', + 'be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7' + ], + [ + 'cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26', + '8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d' + ], + [ + 'b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986', + '39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a' + ], + [ + 'd4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e', + '62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54' + ], + [ + '48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4', + '25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77' + ], + [ + 'dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda', + 'ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517' + ], + [ + '6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859', + 'cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10' + ], + [ + 'e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f', + 'f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125' + ], + [ + 'eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c', + '6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e' + ], + [ + '13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942', + 'fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1' + ], + [ + 'ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a', + '1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2' + ], + [ + 'b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80', + '5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423' + ], + [ + 'ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d', + '438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8' + ], + [ + '8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1', + 'cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758' + ], + [ + '52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63', + 'c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375' + ], + [ + 'e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352', + '6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d' + ], + [ + '7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193', + 'ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec' + ], + [ + '5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00', + '9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0' + ], + [ + '32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58', + 'ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c' + ], + [ + 'e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7', + 'd3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4' + ], + [ + '8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8', + 'c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f' + ], + [ + '4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e', + '67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649' + ], + [ + '3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d', + 'cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826' + ], + [ + '674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b', + '299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5' + ], + [ + 'd32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f', + 'f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87' + ], + [ + '30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6', + '462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b' + ], + [ + 'be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297', + '62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc' + ], + [ + '93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a', + '7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c' + ], + [ + 'b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c', + 'ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f' + ], + [ + 'd5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52', + '4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a' + ], + [ + 'd3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb', + 'bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46' + ], + [ + '463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065', + 'bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f' + ], + [ + '7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917', + '603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03' + ], + [ + '74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9', + 'cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08' + ], + [ + '30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3', + '553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8' + ], + [ + '9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57', + '712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373' + ], + [ + '176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66', + 'ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3' + ], + [ + '75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8', + '9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8' + ], + [ + '809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721', + '9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1' + ], + [ + '1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180', + '4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9' + ] + ] + } +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/utils.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/utils.js new file mode 100644 index 000000000..6f68ca3f7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/elliptic/utils.js @@ -0,0 +1,156 @@ +'use strict'; + +var utils = exports; +var BN = require('../../../BN/bn'); + +utils.assert = function (condition, errorMessage) { + if (!condition) { + throw new Error(errorMessage) + } +}; + +// Represent num in a w-NAF form +function getNAF(num, w) { + var naf = []; + var ws = 1 << (w + 1); + var k = num.clone(); + while (k.cmpn(1) >= 0) { + var z; + if (k.isOdd()) { + var mod = k.andln(ws - 1); + if (mod > (ws >> 1) - 1) + z = (ws >> 1) - mod; + else + z = mod; + k.isubn(z); + } else { + z = 0; + } + naf.push(z); + + // Optimization, shift by word if possible + var shift = (k.cmpn(0) !== 0 && k.andln(ws - 1) === 0) ? (w + 1) : 1; + for (var i = 1; i < shift; i++) + naf.push(0); + k.iushrn(shift); + } + + return naf; +} +utils.getNAF = getNAF; + +// Represent k1, k2 in a Joint Sparse Form +function getJSF(k1, k2) { + var jsf = [ + [], + [] + ]; + + k1 = k1.clone(); + k2 = k2.clone(); + var d1 = 0; + var d2 = 0; + while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) { + + // First phase + var m14 = (k1.andln(3) + d1) & 3; + var m24 = (k2.andln(3) + d2) & 3; + if (m14 === 3) + m14 = -1; + if (m24 === 3) + m24 = -1; + var u1; + if ((m14 & 1) === 0) { + u1 = 0; + } else { + var m8 = (k1.andln(7) + d1) & 7; + if ((m8 === 3 || m8 === 5) && m24 === 2) + u1 = -m14; + else + u1 = m14; + } + jsf[0].push(u1); + + var u2; + if ((m24 & 1) === 0) { + u2 = 0; + } else { + var m8 = (k2.andln(7) + d2) & 7; + if ((m8 === 3 || m8 === 5) && m14 === 2) + u2 = -m24; + else + u2 = m24; + } + jsf[1].push(u2); + + // Second phase + if (2 * d1 === u1 + 1) + d1 = 1 - d1; + if (2 * d2 === u2 + 1) + d2 = 1 - d2; + k1.iushrn(1); + k2.iushrn(1); + } + + return jsf; +} +utils.getJSF = getJSF; + +function cachedProperty(obj, name, computer) { + var key = '_' + name; + obj.prototype[name] = function cachedProperty() { + return this[key] !== undefined ? this[key] : + this[key] = computer.call(this); + }; +} +utils.cachedProperty = cachedProperty; + +function parseBytes(bytes) { + return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') : + bytes; +} +utils.parseBytes = parseBytes; + +function intFromLE(bytes) { + return new BN(bytes, 'hex', 'le'); +} +utils.intFromLE = intFromLE; + +// used to convert `CryptoJS` wordArrays into `crypto` hex buffers +function wordToByteArray(word, length) { + var ba = [], + xFF = 0xFF; + if (length > 0) + ba.push(word >>> 24); + if (length > 1) + ba.push((word >>> 16) & xFF); + if (length > 2) + ba.push((word >>> 8) & xFF); + if (length > 3) + ba.push(word & xFF); + + return ba; +} + +function wordArrayToBuffer(wordArray) { + let length = undefined; + if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) { + length = wordArray.sigBytes; + wordArray = wordArray.words; + } else { + throw Error('Argument not a wordArray') + } + + const result = [] + let bytes = [] + let i = 0; + while (length > 0) { + bytes = wordToByteArray(wordArray[i], Math.min(4, length)); + length -= bytes.length; + result.push(bytes); + i++; + } + return [].concat.apply([], result) +} + +utils.wordArrayToBuffer = wordArrayToBuffer; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/hmac-drbg/hmac-drbg.js b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/hmac-drbg/hmac-drbg.js new file mode 100644 index 000000000..c0be3eaef --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/elliptic/lib/hmac-drbg/hmac-drbg.js @@ -0,0 +1,114 @@ +'use strict'; + +var CryptoJS = require('../../../crypto-js/crypto-js'); +var assert = require('../elliptic/utils').assert; +var utils = require ('../elliptic/utils') +var { byteArrayToWordArray } = require('../../../../base/functions/encode'); + +// some static stuff +const ONE = CryptoJS.enc.Utf8.parse ('\x01') +const ZERO = CryptoJS.enc.Utf8.parse ('\x00') + +function HmacDRBG(options) { + if (!(this instanceof HmacDRBG)) + return new HmacDRBG(options); + this.hash = options.hash + this.predResist = !!options.predResist; + + this.outLen = this.hash.slice (3, 6); // only support SHAXXX hashes + this.minEntropy = options.minEntropy || 192; + + this._reseed = null; + this.reseedInterval = null; + this.K = null; + this.V = null; + + var entropy = options.entropy + var nonce = options.nonce + var pers = [] + assert(entropy.length >= (this.minEntropy / 8), + 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); + this._init(entropy, nonce, pers); +} +module.exports = HmacDRBG; + +HmacDRBG.prototype._init = function init(entropy, nonce, pers) { + var seed = entropy.concat(nonce).concat(pers); + + this.K = new CryptoJS.lib.WordArray.init () + this.V = new CryptoJS.lib.WordArray.init () + const magicNumber = (1 << 24) + (1 << 16) + (1 << 8) + 1 + this.V.words = Array (this.outLen / 32).fill (magicNumber) + this.V.sigBytes = 32 + this._update(seed); + this._reseed = 1; + this.reseedInterval = 0x1000000000000; // 2^48 +}; + +HmacDRBG.prototype._hmac = function hmac() { + return new CryptoJS.lib.WordArray.init (); +}; + +HmacDRBG.prototype._update = function update(seed) { + var kmac = this._hmac () + kmac.concat (this.V) + kmac.concat (ZERO) + if (seed) + kmac.concat (byteArrayToWordArray (seed)); + this.K = CryptoJS['Hmac' + this.hash] (kmac, this.K) + this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) + if (!seed) + return; + + kmac = this._hmac () + kmac.concat (this.V) + kmac.concat (ONE) + kmac.concat (byteArrayToWordArray (seed)) + this.K = CryptoJS['Hmac' + this.hash] (kmac, this.K) + this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) +}; + +HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) { + // Optional entropy enc + if (typeof entropyEnc !== 'string') { + addEnc = add; + add = entropyEnc; + entropyEnc = null; + } + + //entropy = utils.toArray(entropy, entropyEnc); + //add = utils.toArray(add, addEnc); + + assert(entropy.length >= (this.minEntropy / 8), + 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); + + this._update(entropy.concat(add || [])); + this._reseed = 1; +}; + +HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) { + if (this._reseed > this.reseedInterval) + throw new Error ('Reseed is required'); + + // Optional encoding + if (typeof enc !== 'string') { + addEnc = add; + add = enc; + enc = null; + } + + // Optional additional data + if (add) { + add = CryptoJS.enc.Utf8.parse (add); + this._update (add); + } + + var res = new CryptoJS.lib.WordArray.init (); + while (res.sigBytes < len) { + this.V = CryptoJS['Hmac' + this.hash] (this.V, this.K) + res.concat (this.V); + } + this._update (add); + this._reseed++; + return utils.wordArrayToBuffer(res); +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/fetch-ponyfill/fetch-node.js b/TaskServer/node_modules/ccxt/js/static_dependencies/fetch-ponyfill/fetch-node.js new file mode 100644 index 000000000..3755285c1 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/fetch-ponyfill/fetch-node.js @@ -0,0 +1,39 @@ +'use strict'; + +var fetch = require('../node-fetch/index'); + +function wrapFetchForNode(fetch) { + // Support schemaless URIs on the server for parity with the browser. + // https://github.com/matthew-andrews/isomorphic-fetch/pull/10 + const f = function (u, options) { + if (typeof u === 'string' && u.slice(0, 2) === '//') { + return fetch('https:' + u, options); + } + + return fetch(u, options); + }; + // Igor Kroitor 2019 Dec 30 + // expose http and https + // https://github.com/ccxt/ccxt/issues/6327 + f.http = fetch.http; + f.https = fetch.https; + return f; +} + +module.exports = function (context) { + // This modifies the global `node-fetch` object, which isn't great, since + // different callers to `fetch-ponyfill` which pass a different Promise + // implementation would each expect to have their implementation used. But, + // given the way `node-fetch` is implemented, this is the only way to make + // it work at all. + if (context && context.Promise) { + fetch.Promise = context.Promise; + } + + return { + fetch: wrapFetchForNode(fetch), + Headers: fetch.Headers, + Request: fetch.Request, + Response: fetch.Response + }; +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-fetch/index.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-fetch/index.js new file mode 100644 index 000000000..8fcbabfdd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-fetch/index.js @@ -0,0 +1,1564 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js +// (MIT licensed) + +const BUFFER = Symbol('buffer'); +const TYPE = Symbol('type'); + +class Blob { + constructor() { + this[TYPE] = ''; + + const blobParts = arguments[0]; + const options = arguments[1]; + + const buffers = []; + + if (blobParts) { + const a = blobParts; + const length = Number(a.length); + for (let i = 0; i < length; i++) { + const element = a[i]; + let buffer; + if (element instanceof Buffer) { + buffer = element; + } else if (ArrayBuffer.isView(element)) { + buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); + } else if (element instanceof ArrayBuffer) { + buffer = Buffer.from(element); + } else if (element instanceof Blob) { + buffer = element[BUFFER]; + } else { + buffer = Buffer.from(typeof element === 'string' ? element : String(element)); + } + buffers.push(buffer); + } + } + + this[BUFFER] = Buffer.concat(buffers); + + let type = options && options.type !== undefined && String(options.type).toLowerCase(); + if (type && !/[^\u0020-\u007E]/.test(type)) { + this[TYPE] = type; + } + } + get size() { + return this[BUFFER].length; + } + get type() { + return this[TYPE]; + } + slice() { + const size = this.size; + + const start = arguments[0]; + const end = arguments[1]; + let relativeStart, relativeEnd; + if (start === undefined) { + relativeStart = 0; + } else if (start < 0) { + relativeStart = Math.max(size + start, 0); + } else { + relativeStart = Math.min(start, size); + } + if (end === undefined) { + relativeEnd = size; + } else if (end < 0) { + relativeEnd = Math.max(size + end, 0); + } else { + relativeEnd = Math.min(end, size); + } + const span = Math.max(relativeEnd - relativeStart, 0); + + const buffer = this[BUFFER]; + const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); + const blob = new Blob([], { type: arguments[2] }); + blob[BUFFER] = slicedBuffer; + return blob; + } +} + +Object.defineProperties(Blob.prototype, { + size: { enumerable: true }, + type: { enumerable: true }, + slice: { enumerable: true } +}); + +Object.defineProperty(Blob.prototype, Symbol.toStringTag, { + value: 'Blob', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * fetch-error.js + * + * FetchError interface for operational errors + */ + +/** + * Create FetchError instance + * + * @param String message Error message for human + * @param String type Error type for machine + * @param String systemError For Node.js system error + * @return FetchError + */ +function FetchError(message, type, systemError) { + Error.call(this, message); + + this.message = message; + this.type = type; + + // when err.type is `system`, err.code contains system error code + if (systemError) { + this.code = this.errno = systemError.code; + } + + // hide custom error implementation details from end-users + Error.captureStackTrace(this, this.constructor); +} + +FetchError.prototype = Object.create(Error.prototype); +FetchError.prototype.constructor = FetchError; +FetchError.prototype.name = 'FetchError'; + +/** + * body.js + * + * Body interface provides common methods for Request and Response + */ + +const Stream = require('stream'); + +var _require = require('stream'); + +const PassThrough = _require.PassThrough; + +/* +let convert; +try { + convert = require('encoding').convert; +} catch (e) {} +*/ + +const INTERNALS = Symbol('Body internals'); + +/** + * Body mixin + * + * Ref: https://fetch.spec.whatwg.org/#body + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +function Body(body) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$size = _ref.size; + + let size = _ref$size === undefined ? 0 : _ref$size; + var _ref$timeout = _ref.timeout; + let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; + + if (body == null) { + // body is undefined or null + body = null; + } else if (typeof body === 'string') { + // body is string + } else if (isURLSearchParams(body)) { + // body is a URLSearchParams + } else if (body instanceof Blob) { + // body is blob + } else if (Buffer.isBuffer(body)) { + // body is buffer + } else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') { + // body is array buffer + } else if (body instanceof Stream) { + // body is stream + } else { + // none of the above + // coerce to string + body = String(body); + } + this[INTERNALS] = { + body, + disturbed: false, + error: null + }; + this.size = size; + this.timeout = timeout; + + if (body instanceof Stream) { + body.on('error', function (err) { + _this[INTERNALS].error = new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err); + }); + } +} + +Body.prototype = { + get body() { + return this[INTERNALS].body; + }, + + get bodyUsed() { + return this[INTERNALS].disturbed; + }, + + /** + * Decode response as ArrayBuffer + * + * @return Promise + */ + arrayBuffer() { + return consumeBody.call(this).then(function (buf) { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + }); + }, + + /** + * Return raw response as Blob + * + * @return Promise + */ + blob() { + let ct = this.headers && this.headers.get('content-type') || ''; + return consumeBody.call(this).then(function (buf) { + return Object.assign( + // Prevent copying + new Blob([], { + type: ct.toLowerCase() + }), { + [BUFFER]: buf + }); + }); + }, + + /** + * Decode response as json + * + * @return Promise + */ + json() { + var _this2 = this; + + return consumeBody.call(this).then(function (buffer) { + try { + return JSON.parse(buffer.toString()); + } catch (err) { + return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json')); + } + }); + }, + + /** + * Decode response as text + * + * @return Promise + */ + text() { + return consumeBody.call(this).then(function (buffer) { + return buffer.toString(); + }); + }, + + /** + * Decode response as buffer (non-spec api) + * + * @return Promise + */ + buffer() { + return consumeBody.call(this); + }, + + /** + * Decode response as text, while automatically detecting the encoding and + * trying to decode to UTF-8 (non-spec api) + * + * @return Promise + */ + textConverted() { + var _this3 = this; + + return consumeBody.call(this).then(function (buffer) { + return convertBody(buffer, _this3.headers); + }); + } + +}; + +// In browsers, all properties are enumerable. +Object.defineProperties(Body.prototype, { + body: { enumerable: true }, + bodyUsed: { enumerable: true }, + arrayBuffer: { enumerable: true }, + blob: { enumerable: true }, + json: { enumerable: true }, + text: { enumerable: true } +}); + +Body.mixIn = function (proto) { + for (const name of Object.getOwnPropertyNames(Body.prototype)) { + // istanbul ignore else: future proof + if (!(name in proto)) { + const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); + Object.defineProperty(proto, name, desc); + } + } +}; + +/** + * Consume and convert an entire Body to a Buffer. + * + * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body + * + * @return Promise + */ +function consumeBody() { + var _this4 = this; + + if (this[INTERNALS].disturbed) { + return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`)); + } + + this[INTERNALS].disturbed = true; + + if (this[INTERNALS].error) { + return Body.Promise.reject(this[INTERNALS].error); + } + + // body is null + if (this.body === null) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is string + if (typeof this.body === 'string') { + return Body.Promise.resolve(Buffer.from(this.body)); + } + + // body is blob + if (this.body instanceof Blob) { + return Body.Promise.resolve(this.body[BUFFER]); + } + + // body is buffer + if (Buffer.isBuffer(this.body)) { + return Body.Promise.resolve(this.body); + } + + // body is buffer + if (Object.prototype.toString.call(this.body) === '[object ArrayBuffer]') { + return Body.Promise.resolve(Buffer.from(this.body)); + } + + // istanbul ignore if: should never happen + if (!(this.body instanceof Stream)) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is stream + // get ready to actually consume the body + let accum = []; + let accumBytes = 0; + let abort = false; + + return new Body.Promise(function (resolve, reject) { + let resTimeout; + + // allow timeout on slow response body + if (_this4.timeout) { + resTimeout = setTimeout(function () { + abort = true; + reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout')); + }, _this4.timeout); + } + + // handle stream error, such as incorrect content-encoding + _this4.body.on('error', function (err) { + reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err)); + }); + + _this4.body.on('data', function (chunk) { + if (abort || chunk === null) { + return; + } + + if (_this4.size && accumBytes + chunk.length > _this4.size) { + abort = true; + reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size')); + return; + } + + accumBytes += chunk.length; + accum.push(chunk); + }); + + _this4.body.on('end', function () { + if (abort) { + return; + } + + clearTimeout(resTimeout); + + try { + resolve(Buffer.concat(accum)); + } catch (err) { + // handle streams that have accumulated too much data (issue #414) + reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + }); +} + +/** + * Detect buffer encoding and convert to target encoding + * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding + * + * @param Buffer buffer Incoming buffer + * @param String encoding Target encoding + * @return String + */ +function convertBody(buffer, headers) { + if (typeof convert !== 'function') { + throw new Error('The package `encoding` must be installed to use the textConverted() function'); + } + + const ct = headers.get('content-type'); + let charset = 'utf-8'; + let res, str; + + // header + if (ct) { + res = /charset=([^;]*)/i.exec(ct); + } + + // no charset in content type, peek at response body for at most 1024 bytes + str = buffer.slice(0, 1024).toString(); + + // html5 + if (!res && str) { + res = / 0 && arguments[0] !== undefined ? arguments[0] : undefined; + + this[MAP] = Object.create(null); + + if (init instanceof Headers) { + const rawHeaders = init.raw(); + const headerNames = Object.keys(rawHeaders); + + for (const headerName of headerNames) { + for (const value of rawHeaders[headerName]) { + this.append(headerName, value); + } + } + + return; + } + + // We don't worry about converting prop to ByteString here as append() + // will handle it. + if (init == null) { + // no op + } else if (typeof init === 'object') { + const method = init[Symbol.iterator]; + if (method != null) { + if (typeof method !== 'function') { + throw new TypeError('Header pairs must be iterable'); + } + + // sequence> + // Note: per spec we have to first exhaust the lists then process them + const pairs = []; + for (const pair of init) { + if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { + throw new TypeError('Each header pair must be iterable'); + } + pairs.push(Array.from(pair)); + } + + for (const pair of pairs) { + if (pair.length !== 2) { + throw new TypeError('Each header pair must be a name/value tuple'); + } + this.append(pair[0], pair[1]); + } + } else { + // record + for (const key of Object.keys(init)) { + const value = init[key]; + this.append(key, value); + } + } + } else { + throw new TypeError('Provided initializer must be an object'); + } + } + + /** + * Return combined header value given name + * + * @param String name Header name + * @return Mixed + */ + get(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key === undefined) { + return null; + } + + return this[MAP][key].join(', '); + } + + /** + * Iterate over all headers + * + * @param Function callback Executed for each item with parameters (value, name, thisArg) + * @param Boolean thisArg `this` context for callback function + * @return Void + */ + forEach(callback) { + let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; + + let pairs = getHeaders(this); + let i = 0; + while (i < pairs.length) { + var _pairs$i = pairs[i]; + const name = _pairs$i[0], + value = _pairs$i[1]; + + callback.call(thisArg, value, name, this); + pairs = getHeaders(this); + i++; + } + } + + /** + * Overwrite header values given name + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + set(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + this[MAP][key !== undefined ? key : name] = [value]; + } + + /** + * Append a value onto existing header + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + append(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + if (key !== undefined) { + this[MAP][key].push(value); + } else { + this[MAP][name] = [value]; + } + } + + /** + * Check for header name existence + * + * @param String name Header name + * @return Boolean + */ + has(name) { + name = `${name}`; + validateName(name); + return find(this[MAP], name) !== undefined; + } + + /** + * Delete all header values given name + * + * @param String name Header name + * @return Void + */ + delete(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key !== undefined) { + delete this[MAP][key]; + } + } + + /** + * Return raw headers (non-spec api) + * + * @return Object + */ + raw() { + return this[MAP]; + } + + /** + * Get an iterator on keys. + * + * @return Iterator + */ + keys() { + return createHeadersIterator(this, 'key'); + } + + /** + * Get an iterator on values. + * + * @return Iterator + */ + values() { + return createHeadersIterator(this, 'value'); + } + + /** + * Get an iterator on entries. + * + * This is the default iterator of the Headers object. + * + * @return Iterator + */ + [Symbol.iterator]() { + return createHeadersIterator(this, 'key+value'); + } +} +Headers.prototype.entries = Headers.prototype[Symbol.iterator]; + +Object.defineProperty(Headers.prototype, Symbol.toStringTag, { + value: 'Headers', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Headers.prototype, { + get: { enumerable: true }, + forEach: { enumerable: true }, + set: { enumerable: true }, + append: { enumerable: true }, + has: { enumerable: true }, + delete: { enumerable: true }, + keys: { enumerable: true }, + values: { enumerable: true }, + entries: { enumerable: true } +}); + +function getHeaders(headers) { + let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value'; + + const keys = Object.keys(headers[MAP]).sort(); + return keys.map(kind === 'key' ? function (k) { + return k.toLowerCase(); + } : kind === 'value' ? function (k) { + return headers[MAP][k].join(', '); + } : function (k) { + return [k.toLowerCase(), headers[MAP][k].join(', ')]; + }); +} + +const INTERNAL = Symbol('internal'); + +function createHeadersIterator(target, kind) { + const iterator = Object.create(HeadersIteratorPrototype); + iterator[INTERNAL] = { + target, + kind, + index: 0 + }; + return iterator; +} + +const HeadersIteratorPrototype = Object.setPrototypeOf({ + next() { + // istanbul ignore if + if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { + throw new TypeError('Value of `this` is not a HeadersIterator'); + } + + var _INTERNAL = this[INTERNAL]; + const target = _INTERNAL.target, + kind = _INTERNAL.kind, + index = _INTERNAL.index; + + const values = getHeaders(target, kind); + const len = values.length; + if (index >= len) { + return { + value: undefined, + done: true + }; + } + + this[INTERNAL].index = index + 1; + + return { + value: values[index], + done: false + }; + } +}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); + +Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { + value: 'HeadersIterator', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * Export the Headers object in a form that Node.js can consume. + * + * @param Headers headers + * @return Object + */ +function exportNodeCompatibleHeaders(headers) { + const obj = Object.assign({ __proto__: null }, headers[MAP]); + + // http.request() only supports string as Host header. This hack makes + // specifying custom Host header possible. + const hostHeaderKey = find(headers[MAP], 'Host'); + if (hostHeaderKey !== undefined) { + obj[hostHeaderKey] = obj[hostHeaderKey][0]; + } + + return obj; +} + +/** + * Create a Headers object from an object of headers, ignoring those that do + * not conform to HTTP grammar productions. + * + * @param Object obj Object of headers + * @return Headers + */ +function createHeadersLenient(obj) { + const headers = new Headers(); + for (const name of Object.keys(obj)) { + if (invalidTokenRegex.test(name)) { + continue; + } + if (Array.isArray(obj[name])) { + for (const val of obj[name]) { + if (invalidHeaderCharRegex.test(val)) { + continue; + } + if (headers[MAP][name] === undefined) { + headers[MAP][name] = [val]; + } else { + headers[MAP][name].push(val); + } + } + } else if (!invalidHeaderCharRegex.test(obj[name])) { + headers[MAP][name] = [obj[name]]; + } + } + return headers; +} + +/** + * response.js + * + * Response class provides content decoding + */ + +var _require$1 = require('http'); + +const STATUS_CODES = _require$1.STATUS_CODES; + + +const INTERNALS$1 = Symbol('Response internals'); + +/** + * Response class + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +class Response { + constructor() { + let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + Body.call(this, body, opts); + + const status = opts.status || 200; + + this[INTERNALS$1] = { + url: opts.url, + status, + statusText: opts.statusText || STATUS_CODES[status], + headers: new Headers(opts.headers) + }; + } + + get url() { + return this[INTERNALS$1].url; + } + + get status() { + return this[INTERNALS$1].status; + } + + /** + * Convenience property representing if the request ended normally + */ + get ok() { + return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300; + } + + get statusText() { + return this[INTERNALS$1].statusText; + } + + get headers() { + return this[INTERNALS$1].headers; + } + + /** + * Clone this response + * + * @return Response + */ + clone() { + return new Response(clone(this), { + url: this.url, + status: this.status, + statusText: this.statusText, + headers: this.headers, + ok: this.ok + }); + } +} + +Body.mixIn(Response.prototype); + +Object.defineProperties(Response.prototype, { + url: { enumerable: true }, + status: { enumerable: true }, + ok: { enumerable: true }, + statusText: { enumerable: true }, + headers: { enumerable: true }, + clone: { enumerable: true } +}); + +Object.defineProperty(Response.prototype, Symbol.toStringTag, { + value: 'Response', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * request.js + * + * Request class contains server only options + * + * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. + */ + +var _require$2 = require('url'); + +const format_url = _require$2.format; +const parse_url = _require$2.parse; + + +const INTERNALS$2 = Symbol('Request internals'); + +/** + * Check if a value is an instance of Request. + * + * @param Mixed input + * @return Boolean + */ +function isRequest(input) { + return typeof input === 'object' && typeof input[INTERNALS$2] === 'object'; +} + +/** + * Request class + * + * @param Mixed input Url or Request instance + * @param Object init Custom options + * @return Void + */ +class Request { + constructor(input) { + let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + let parsedURL; + + // normalize input + if (!isRequest(input)) { + if (input && input.href) { + // in order to support Node.js' Url objects; though WHATWG's URL objects + // will fall into this branch also (since their `toString()` will return + // `href` property anyway) + parsedURL = parse_url(input.href); + } else { + // coerce input to a string before attempting to parse + parsedURL = parse_url(`${input}`); + } + input = {}; + } else { + parsedURL = parse_url(input.url); + } + + let method = init.method || input.method || 'GET'; + method = method.toUpperCase(); + + if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) { + throw new TypeError('Request with GET/HEAD method cannot have body'); + } + + let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null; + + Body.call(this, inputBody, { + timeout: init.timeout || input.timeout || 0, + size: init.size || input.size || 0 + }); + + const headers = new Headers(init.headers || input.headers || {}); + + if (init.body != null) { + const contentType = extractContentType(this); + if (contentType !== null && !headers.has('Content-Type')) { + headers.append('Content-Type', contentType); + } + } + + this[INTERNALS$2] = { + method, + redirect: init.redirect || input.redirect || 'follow', + headers, + parsedURL + }; + + // node-fetch-only options + this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20; + this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true; + this.counter = init.counter || input.counter || 0; + this.agent = init.agent || input.agent; + } + + get method() { + return this[INTERNALS$2].method; + } + + get url() { + return format_url(this[INTERNALS$2].parsedURL); + } + + get headers() { + return this[INTERNALS$2].headers; + } + + get redirect() { + return this[INTERNALS$2].redirect; + } + + /** + * Clone this request + * + * @return Request + */ + clone() { + return new Request(this); + } +} + +Body.mixIn(Request.prototype); + +Object.defineProperty(Request.prototype, Symbol.toStringTag, { + value: 'Request', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Request.prototype, { + method: { enumerable: true }, + url: { enumerable: true }, + headers: { enumerable: true }, + redirect: { enumerable: true }, + clone: { enumerable: true } +}); + +/** + * Convert a Request to Node.js http request options. + * + * @param Request A Request instance + * @return Object The options object to be passed to http.request + */ +function getNodeRequestOptions(request) { + const parsedURL = request[INTERNALS$2].parsedURL; + const headers = new Headers(request[INTERNALS$2].headers); + + // fetch step 1.3 + if (!headers.has('Accept')) { + headers.set('Accept', '*/*'); + } + + // Basic fetch + if (!parsedURL.protocol || !parsedURL.hostname) { + throw new TypeError('Only absolute URLs are supported'); + } + + if (!/^https?:$/.test(parsedURL.protocol)) { + throw new TypeError('Only HTTP(S) protocols are supported'); + } + + // HTTP-network-or-cache fetch steps 2.4-2.7 + let contentLengthValue = null; + if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { + contentLengthValue = '0'; + } + if (request.body != null) { + const totalBytes = getTotalBytes(request); + if (typeof totalBytes === 'number') { + contentLengthValue = String(totalBytes); + } + } + if (contentLengthValue) { + headers.set('Content-Length', contentLengthValue); + } + + // HTTP-network-or-cache fetch step 2.11 + if (!headers.has('User-Agent')) { + headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)'); + } + + // HTTP-network-or-cache fetch step 2.15 + if (request.compress) { + headers.set('Accept-Encoding', 'gzip,deflate'); + } + if (!headers.has('Connection') && !request.agent) { + headers.set('Connection', 'close'); + } + + // HTTP-network fetch step 4.2 + // chunked encoding is handled by Node.js + + return Object.assign({}, parsedURL, { + method: request.method, + headers: exportNodeCompatibleHeaders(headers), + agent: request.agent + }); +} + +/** + * index.js + * + * a request API compatible with window.fetch + * + * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. + */ + +const http = require('http'); +const https = require('https'); + +var _require$3 = require('stream'); + +const PassThrough$1 = _require$3.PassThrough; + +var _require2 = require('url'); + +const resolve_url = _require2.resolve; + +const zlib = require('zlib'); + +/** + * Fetch function + * + * @param Mixed url Absolute url or Request instance + * @param Object opts Fetch options + * @return Promise + */ +function fetch(url, opts) { + + // allow custom promise + if (!fetch.Promise) { + throw new Error('native promise missing, set fetch.Promise to your favorite alternative'); + } + + Body.Promise = fetch.Promise; + + // wrap http.request into fetch + return new fetch.Promise(function (resolve, reject) { + // build request object + const request = new Request(url, opts); + const options = getNodeRequestOptions(request); + + const send = (options.protocol === 'https:' ? https : http).request; + + // send request + const req = send(options); + let reqTimeout; + + function finalize() { + req.abort(); + clearTimeout(reqTimeout); + } + + if (request.timeout) { + req.once('socket', function (socket) { + reqTimeout = setTimeout(function () { + reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); + finalize(); + }, request.timeout); + }); + } + + req.on('error', function (err) { + reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); + finalize(); + }); + + req.on('response', function (res) { + clearTimeout(reqTimeout); + + const headers = createHeadersLenient(res.headers); + + // HTTP fetch step 5 + if (fetch.isRedirect(res.statusCode)) { + // HTTP fetch step 5.2 + const location = headers.get('Location'); + + // HTTP fetch step 5.3 + const locationURL = location === null ? null : resolve_url(request.url, location); + + // HTTP fetch step 5.5 + switch (request.redirect) { + case 'error': + reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); + finalize(); + return; + case 'manual': + // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL. + if (locationURL !== null) { + headers.set('Location', locationURL); + } + break; + case 'follow': + // HTTP-redirect fetch step 2 + if (locationURL === null) { + break; + } + + // HTTP-redirect fetch step 5 + if (request.counter >= request.follow) { + reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 6 (counter increment) + // Create a new Request object. + const requestOpts = { + headers: new Headers(request.headers), + follow: request.follow, + counter: request.counter + 1, + agent: request.agent, + compress: request.compress, + method: request.method, + body: request.body + }; + + // HTTP-redirect fetch step 9 + if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { + reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 11 + if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { + requestOpts.method = 'GET'; + requestOpts.body = undefined; + requestOpts.headers.delete('content-length'); + } + + // HTTP-redirect fetch step 15 + resolve(fetch(new Request(locationURL, requestOpts))); + finalize(); + return; + } + } + + // prepare response + let body = res.pipe(new PassThrough$1()); + const response_options = { + url: request.url, + status: res.statusCode, + statusText: res.statusMessage, + headers: headers, + size: request.size, + timeout: request.timeout + }; + + // HTTP-network fetch step 12.1.1.3 + const codings = headers.get('Content-Encoding'); + + // HTTP-network fetch step 12.1.1.4: handle content codings + + // in following scenarios we ignore compression support + // 1. compression support is disabled + // 2. HEAD request + // 3. no Content-Encoding header + // 4. no content response (204) + // 5. content not modified response (304) + if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { + resolve(new Response(body, response_options)); + return; + } + + // For Node v6+ + // Be less strict when decoding compressed responses, since sometimes + // servers send slightly invalid responses that are still accepted + // by common browsers. + // Always using Z_SYNC_FLUSH is what cURL does. + const zlibOptions = { + flush: zlib.Z_SYNC_FLUSH, + finishFlush: zlib.Z_SYNC_FLUSH + }; + + // for gzip + if (codings == 'gzip' || codings == 'x-gzip') { + body = body.pipe(zlib.createGunzip(zlibOptions)); + resolve(new Response(body, response_options)); + return; + } + + // for deflate + if (codings == 'deflate' || codings == 'x-deflate') { + // handle the infamous raw deflate response from old servers + // a hack for old IIS and Apache servers + const raw = res.pipe(new PassThrough$1()); + raw.once('data', function (chunk) { + // see http://stackoverflow.com/questions/37519828 + if ((chunk[0] & 0x0F) === 0x08) { + body = body.pipe(zlib.createInflate()); + } else { + body = body.pipe(zlib.createInflateRaw()); + } + resolve(new Response(body, response_options)); + }); + return; + } + + // otherwise, use response as-is + resolve(new Response(body, response_options)); + }); + + writeToStream(req, request); + }); +} + +/** + * Redirect code matching + * + * @param Number code Status code + * @return Boolean + */ +fetch.isRedirect = function (code) { + return code === 301 || code === 302 || code === 303 || code === 307 || code === 308; +}; + +// Needed for TypeScript. +fetch.default = fetch; + +// expose Promise +fetch.Promise = global.Promise; + +// Igor Kroitor 2019 Dec 30 +// expose http and https +// https://github.com/ccxt/ccxt/issues/6327 +fetch.http = http +fetch.https = https + +module.exports = exports = fetch; +exports.Headers = Headers; +exports.Request = Request; +exports.Response = Response; +exports.FetchError = FetchError; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/NodeRSA.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/NodeRSA.js new file mode 100644 index 000000000..d03983525 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/NodeRSA.js @@ -0,0 +1,223 @@ +/*! + * RSA library for Node.js + * + * Author: rzcoder + * License MIT + */ + +var rsa = require('./libs/rsa.js'); +var _ = require('./utils')._; +var schemes = require('./schemes/schemes.js'); +var formats = require('./formats/formats.js'); + +module.exports = (function () { + var SUPPORTED_HASH_ALGORITHMS = { + browser: ['md5', 'ripemd160', 'sha1', 'sha256', 'sha512'] + }; + + var DEFAULT_ENCRYPTION_SCHEME = 'pkcs1'; + var DEFAULT_SIGNING_SCHEME = 'pkcs1'; + + var EXPORT_FORMAT_ALIASES = { + 'private': 'pkcs1-private-pem', + 'private-der': 'pkcs1-private-der', + 'public': 'pkcs8-public-pem', + 'public-der': 'pkcs8-public-der', + }; + + /** + * @param key {string|buffer|object} Key in PEM format, or data for generate key {b: bits, e: exponent} + * @constructor + */ + function NodeRSA(key, format, options) { + if (!(this instanceof NodeRSA)) { + return new NodeRSA(key, format, options); + } + + if (_.isObject(format)) { + options = format; + format = undefined; + } + + this.$options = { + signingScheme: DEFAULT_SIGNING_SCHEME, + signingSchemeOptions: { + hash: 'sha256', + saltLength: null + }, + encryptionScheme: DEFAULT_ENCRYPTION_SCHEME, + encryptionSchemeOptions: { + hash: 'sha1', + label: null + }, + environment: 'browser', + rsaUtils: this + }; + this.keyPair = new rsa.Key(); + this.$cache = {}; + + if (Buffer.isBuffer(key) || _.isString(key)) { + this.importKey(key, format); + } else if (_.isObject(key)) { + this.generateKeyPair(key.b, key.e); + } + + this.setOptions(options); + } + + /** + * Set and validate options for key instance + * @param options + */ + NodeRSA.prototype.setOptions = function (options) { + options = options || {}; + if (options.environment) { + this.$options.environment = options.environment; + } + + if (options.signingScheme) { + if (_.isString(options.signingScheme)) { + var signingScheme = options.signingScheme.toLowerCase().split('-'); + if (signingScheme.length == 1) { + if (SUPPORTED_HASH_ALGORITHMS.node.indexOf(signingScheme[0]) > -1) { + this.$options.signingSchemeOptions = { + hash: signingScheme[0] + }; + this.$options.signingScheme = DEFAULT_SIGNING_SCHEME; + } else { + this.$options.signingScheme = signingScheme[0]; + this.$options.signingSchemeOptions = { + hash: null + }; + } + } else { + this.$options.signingSchemeOptions = { + hash: signingScheme[1] + }; + this.$options.signingScheme = signingScheme[0]; + } + } else if (_.isObject(options.signingScheme)) { + this.$options.signingScheme = options.signingScheme.scheme || DEFAULT_SIGNING_SCHEME; + this.$options.signingSchemeOptions = _.omit(options.signingScheme, 'scheme'); + } + + if (!schemes.isSignature(this.$options.signingScheme)) { + throw Error('Unsupported signing scheme'); + } + + if (this.$options.signingSchemeOptions.hash && + SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.signingSchemeOptions.hash) === -1) { + throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); + } + } + + if (options.encryptionScheme) { + if (_.isString(options.encryptionScheme)) { + this.$options.encryptionScheme = options.encryptionScheme.toLowerCase(); + this.$options.encryptionSchemeOptions = {}; + } else if (_.isObject(options.encryptionScheme)) { + this.$options.encryptionScheme = options.encryptionScheme.scheme || DEFAULT_ENCRYPTION_SCHEME; + this.$options.encryptionSchemeOptions = _.omit(options.encryptionScheme, 'scheme'); + } + + if (!schemes.isEncryption(this.$options.encryptionScheme)) { + throw Error('Unsupported encryption scheme'); + } + + if (this.$options.encryptionSchemeOptions.hash && + SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.encryptionSchemeOptions.hash) === -1) { + throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment'); + } + } + + this.keyPair.setOptions(this.$options); + }; + + /** + * Importing key + * @param keyData {string|buffer|Object} + * @param format {string} + */ + NodeRSA.prototype.importKey = function (keyData, format) { + if (!keyData) { + throw Error("Empty key given"); + } + + if (format) { + format = EXPORT_FORMAT_ALIASES[format] || format; + } + + if (!formats.detectAndImport(this.keyPair, keyData, format) && format === undefined) { + throw Error("Key format must be specified"); + } + + this.$cache = {}; + + return this; + }; + + /** + * Check if key pair contains private key + */ + NodeRSA.prototype.isPrivate = function () { + return this.keyPair.isPrivate(); + }; + + /** + * Check if key pair contains public key + * @param [strict] {boolean} - public key only, return false if have private exponent + */ + NodeRSA.prototype.isPublic = function (strict) { + return this.keyPair.isPublic(strict); + }; + + /** + * Check if key pair doesn't contains any data + */ + NodeRSA.prototype.isEmpty = function (strict) { + return !(this.keyPair.n || this.keyPair.e || this.keyPair.d); + }; + + /** + * Signing data + * + * @param buffer {string|number|object|array|Buffer} - data for signing. Object and array will convert to JSON string. + * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. + * @param source_encoding {string} - optional. Encoding for given string. Default utf8. + * @returns {string|Buffer} + */ + NodeRSA.prototype.sign = function (buffer, encoding, source_encoding) { + if (!this.isPrivate()) { + throw Error("This is not private key"); + } + + var res = this.keyPair.sign(this.$getDataForEncrypt(buffer, source_encoding)); + + if (encoding && encoding != 'buffer') { + res = res.toString(encoding); + } + + return res; + }; + + /** + * Preparing given data for encrypting/signing. Just make new/return Buffer object. + * + * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. + * @param encoding {string} - optional. Encoding for given string. Default utf8. + * @returns {Buffer} + */ + NodeRSA.prototype.$getDataForEncrypt = function (buffer, encoding) { + if (_.isString(buffer) || _.isNumber(buffer)) { + return Buffer.from('' + buffer, encoding || 'utf8'); + } else if (Buffer.isBuffer(buffer)) { + return buffer; + } else if (_.isObject(buffer)) { + return Buffer.from(JSON.stringify(buffer)); + } else { + throw Error("Unexpected data type"); + } + }; + + return NodeRSA; +})(); diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/errors.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/errors.js new file mode 100644 index 000000000..4557b8aea --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/errors.js @@ -0,0 +1,13 @@ +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + + newInvalidAsn1Error: function (msg) { + var e = new Error(); + e.name = 'InvalidAsn1Error'; + e.message = msg || ''; + return e; + } + +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/index.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/index.js new file mode 100644 index 000000000..f6000d9f3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/index.js @@ -0,0 +1,21 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +var errors = require('./errors'); +var types = require('./types'); + +var Reader = require('./reader'); + +// --- Exports + +module.exports = { + Reader: Reader, +}; + +for (var t in types) { + if (types.hasOwnProperty(t)) + module.exports[t] = types[t]; +} +for (var e in errors) { + if (errors.hasOwnProperty(e)) + module.exports[e] = errors[e]; +} diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/reader.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/reader.js new file mode 100644 index 000000000..b1d0cb6d5 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/reader.js @@ -0,0 +1,262 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = require('assert'); +// var Buffer = require('safer-buffer').Buffer; + +var ASN1 = require('./types'); +var errors = require('./errors'); + + +// --- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + + + +// --- API + +function Reader(data) { + if (!data || !Buffer.isBuffer(data)) + throw new TypeError('data must be a node Buffer'); + + this._buf = data; + this._size = data.length; + + // These hold the "current" state + this._len = 0; + this._offset = 0; +} + +Object.defineProperty(Reader.prototype, 'length', { + enumerable: true, + get: function () { return (this._len); } +}); + +Object.defineProperty(Reader.prototype, 'offset', { + enumerable: true, + get: function () { return (this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'remain', { + get: function () { return (this._size - this._offset); } +}); + +Object.defineProperty(Reader.prototype, 'buffer', { + get: function () { return (this._buf.slice(this._offset)); } +}); + + +/** + * Reads a single byte and advances offset; you can pass in `true` to make this + * a "peek" operation (i.e., get the byte, but don't advance the offset). + * + * @param {Boolean} peek true means don't move offset. + * @return {Number} the next byte, null if not enough data. + */ +Reader.prototype.readByte = function (peek) { + if (this._size - this._offset < 1) + return null; + + var b = this._buf[this._offset] & 0xff; + + if (!peek) + this._offset += 1; + + return b; +}; + + +Reader.prototype.peek = function () { + return this.readByte(true); +}; + + +/** + * Reads a (potentially) variable length off the BER buffer. This call is + * not really meant to be called directly, as callers have to manipulate + * the internal buffer afterwards. + * + * As a result of this call, you can call `Reader.length`, until the + * next thing called that does a readLength. + * + * @return {Number} the amount of offset to advance the buffer. + * @throws {InvalidAsn1Error} on bad ASN.1 + */ +Reader.prototype.readLength = function (offset) { + if (offset === undefined) + offset = this._offset; + + if (offset >= this._size) + return null; + + var lenB = this._buf[offset++] & 0xff; + if (lenB === null) + return null; + + if ((lenB & 0x80) === 0x80) { + lenB &= 0x7f; + + if (lenB === 0) + throw newInvalidAsn1Error('Indefinite length not supported'); + + if (lenB > 4) + throw newInvalidAsn1Error('encoding too long'); + + if (this._size - offset < lenB) + return null; + + this._len = 0; + for (var i = 0; i < lenB; i++) + this._len = (this._len << 8) + (this._buf[offset++] & 0xff); + + } else { + // Wasn't a variable length + this._len = lenB; + } + + return offset; +}; + + +/** + * Parses the next sequence in this BER buffer. + * + * To get the length of the sequence, call `Reader.length`. + * + * @return {Number} the sequence's tag. + */ +Reader.prototype.readSequence = function (tag) { + var seq = this.peek(); + if (seq === null) + return null; + if (tag !== undefined && tag !== seq) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + seq.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + this._offset = o; + return seq; +}; + + +Reader.prototype.readInt = function () { + return this._readTag(ASN1.Integer); +}; + + +Reader.prototype.readBoolean = function () { + return (this._readTag(ASN1.Boolean) === 0 ? false : true); +}; + + +Reader.prototype.readEnumeration = function () { + return this._readTag(ASN1.Enumeration); +}; + + +Reader.prototype.readString = function (tag, retbuf) { + if (!tag) + tag = ASN1.OctetString; + + var b = this.peek(); + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + + if (o === null) + return null; + + if (this.length > this._size - o) + return null; + + this._offset = o; + + if (this.length === 0) + return retbuf ? Buffer.alloc(0) : ''; + + var str = this._buf.slice(this._offset, this._offset + this.length); + this._offset += this.length; + + return retbuf ? str : str.toString('utf8'); +}; + +Reader.prototype.readOID = function (tag) { + if (!tag) + tag = ASN1.OID; + + var b = this.readString(tag, true); + if (b === null) + return null; + + var values = []; + var value = 0; + + for (var i = 0; i < b.length; i++) { + var byte = b[i] & 0xff; + + value <<= 7; + value += byte & 0x7f; + if ((byte & 0x80) === 0) { + values.push(value); + value = 0; + } + } + + value = values.shift(); + values.unshift(value % 40); + values.unshift((value / 40) >> 0); + + return values.join('.'); +}; + + +Reader.prototype._readTag = function (tag) { + assert.ok(tag !== undefined); + + var b = this.peek(); + + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + if (this.length > 4) + throw newInvalidAsn1Error('Integer too long: ' + this.length); + + if (this.length > this._size - o) + return null; + this._offset = o; + + var fb = this._buf[this._offset]; + var value = 0; + + for (var i = 0; i < this.length; i++) { + value <<= 8; + value |= (this._buf[this._offset++] & 0xff); + } + + if ((fb & 0x80) === 0x80 && i !== 4) + value -= (1 << (i * 8)); + + return value >> 0; +}; + + + +// --- Exported API + +module.exports = Reader; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/types.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/types.js new file mode 100644 index 000000000..8aea00013 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/ber/types.js @@ -0,0 +1,36 @@ +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + EOC: 0, + Boolean: 1, + Integer: 2, + BitString: 3, + OctetString: 4, + Null: 5, + OID: 6, + ObjectDescriptor: 7, + External: 8, + Real: 9, // float + Enumeration: 10, + PDV: 11, + Utf8String: 12, + RelativeOID: 13, + Sequence: 16, + Set: 17, + NumericString: 18, + PrintableString: 19, + T61String: 20, + VideotexString: 21, + IA5String: 22, + UTCTime: 23, + GeneralizedTime: 24, + GraphicString: 25, + VisibleString: 26, + GeneralString: 28, + UniversalString: 29, + CharacterString: 30, + BMPString: 31, + Constructor: 32, + Context: 128 +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/index.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/index.js new file mode 100644 index 000000000..abc8ffb76 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/asn1/index.js @@ -0,0 +1,17 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +// If you have no idea what ASN.1 or BER is, see this: +// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc + +var Ber = require('./ber/index'); + + + +// --- Exported API + +module.exports = { + + Ber: Ber, + + BerReader: Ber.Reader, +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/encryptEngines/js.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/encryptEngines/js.js new file mode 100644 index 000000000..f14844153 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/encryptEngines/js.js @@ -0,0 +1,34 @@ +var BigInteger = require('../libs/jsbn.js'); +var schemes = require('../schemes/schemes.js'); + +module.exports = function (keyPair, options) { + var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options); + + return { + encrypt: function (buffer, usePrivate) { + var m, c; + if (usePrivate) { + /* Type 1: zeros padding for private key encrypt */ + m = new BigInteger(pkcs1Scheme.encPad(buffer, {type: 1})); + c = keyPair.$doPrivate(m); + } else { + m = new BigInteger(keyPair.encryptionScheme.encPad(buffer)); + c = keyPair.$doPublic(m); + } + return c.toBuffer(keyPair.encryptedDataLength); + }, + + decrypt: function (buffer, usePublic) { + var m, c = new BigInteger(buffer); + + if (usePublic) { + m = keyPair.$doPublic(c); + /* Type 1: zeros padding for private key decrypt */ + return pkcs1Scheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength), {type: 1}); + } else { + m = keyPair.$doPrivate(c); + return keyPair.encryptionScheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength)); + } + } + }; +}; \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/components.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/components.js new file mode 100644 index 000000000..a27531480 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/components.js @@ -0,0 +1,71 @@ +var _ = require('../utils')._; +var utils = require('../utils'); + +module.exports = { + privateExport: function (key, options) { + return { + n: key.n.toBuffer(), + e: key.e, + d: key.d.toBuffer(), + p: key.p.toBuffer(), + q: key.q.toBuffer(), + dmp1: key.dmp1.toBuffer(), + dmq1: key.dmq1.toBuffer(), + coeff: key.coeff.toBuffer() + }; + }, + + privateImport: function (key, data, options) { + if (data.n && data.e && data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { + key.setPrivate( + data.n, + data.e, + data.d, + data.p, + data.q, + data.dmp1, + data.dmq1, + data.coeff + ); + } else { + throw Error("Invalid key data"); + } + }, + + publicExport: function (key, options) { + return { + n: key.n.toBuffer(), + e: key.e + }; + }, + + publicImport: function (key, data, options) { + if (data.n && data.e) { + key.setPublic( + data.n, + data.e + ); + } else { + throw Error("Invalid key data"); + } + }, + + /** + * Trying autodetect and import key + * @param key + * @param data + */ + autoImport: function (key, data) { + if (data.n && data.e) { + if (data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) { + module.exports.privateImport(key, data); + return true; + } else { + module.exports.publicImport(key, data); + return true; + } + } + + return false; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/formats.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/formats.js new file mode 100644 index 000000000..c5c676e00 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/formats.js @@ -0,0 +1,31 @@ +var _ = require('../utils')._; + +module.exports = { + pkcs1: require('./pkcs1'), + pkcs8: require('./pkcs8'), + components: require('./components'), + + detectAndImport: function (key, data, format) { + if (format === undefined) { + for (var scheme in module.exports) { + if (typeof module.exports[scheme].autoImport === 'function' && module.exports[scheme].autoImport(key, data)) { + return true; + } + } + } else if (format) { + var fmt = formatParse(format); + + if (module.exports[fmt.scheme]) { + if (fmt.keyType === 'private') { + module.exports[fmt.scheme].privateImport(key, data, fmt.keyOpt); + } else { + module.exports[fmt.scheme].publicImport(key, data, fmt.keyOpt); + } + } else { + throw Error('Unsupported key format'); + } + } + + return false; + }, +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/pkcs1.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/pkcs1.js new file mode 100644 index 000000000..2199c7380 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/pkcs1.js @@ -0,0 +1,148 @@ +var ber = require('../asn1/index').Ber; +var _ = require('../utils')._; +var utils = require('../utils'); + +const PRIVATE_OPENING_BOUNDARY = '-----BEGIN RSA PRIVATE KEY-----'; +const PRIVATE_CLOSING_BOUNDARY = '-----END RSA PRIVATE KEY-----'; + +const PUBLIC_OPENING_BOUNDARY = '-----BEGIN RSA PUBLIC KEY-----'; +const PUBLIC_CLOSING_BOUNDARY = '-----END RSA PUBLIC KEY-----'; + +module.exports = { + privateExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var d = key.d.toBuffer(); + var p = key.p.toBuffer(); + var q = key.q.toBuffer(); + var dmp1 = key.dmp1.toBuffer(); + var dmq1 = key.dmq1.toBuffer(); + var coeff = key.coeff.toBuffer(); + + var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic + var writer = new ber.Writer({size: length}); + + writer.startSequence(); + writer.writeInt(0); + writer.writeBuffer(n, 2); + writer.writeInt(key.e); + writer.writeBuffer(d, 2); + writer.writeBuffer(p, 2); + writer.writeBuffer(q, 2); + writer.writeBuffer(dmp1, 2); + writer.writeBuffer(dmq1, 2); + writer.writeBuffer(coeff, 2); + writer.endSequence(); + + if (options.type === 'der') { + return writer.buffer; + } else { + return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; + } + }, + + privateImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } else { + throw Error('Unsupported key format'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var reader = new ber.Reader(buffer); + reader.readSequence(); + reader.readString(2, true); // just zero + key.setPrivate( + reader.readString(2, true), // modulus + reader.readString(2, true), // publicExponent + reader.readString(2, true), // privateExponent + reader.readString(2, true), // prime1 + reader.readString(2, true), // prime2 + reader.readString(2, true), // exponent1 -- d mod (p1) + reader.readString(2, true), // exponent2 -- d mod (q-1) + reader.readString(2, true) // coefficient -- (inverse of q) mod p + ); + }, + + publicExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var length = n.length + 512; // magic + + var bodyWriter = new ber.Writer({size: length}); + bodyWriter.startSequence(); + bodyWriter.writeBuffer(n, 2); + bodyWriter.writeInt(key.e); + bodyWriter.endSequence(); + + if (options.type === 'der') { + return bodyWriter.buffer; + } else { + return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; + } + }, + + publicImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var body = new ber.Reader(buffer); + body.readSequence(); + key.setPublic( + body.readString(0x02, true), // modulus + body.readString(0x02, true) // publicExponent + ); + }, + + /** + * Trying autodetect and import key + * @param key + * @param data + */ + autoImport: function (key, data) { + // [\S\s]* matches zero or more of any character + if (/^[\S\s]*-----BEGIN RSA PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PRIVATE KEY-----[\S\s]*$/g.test(data)) { + module.exports.privateImport(key, data); + return true; + } + + if (/^[\S\s]*-----BEGIN RSA PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PUBLIC KEY-----[\S\s]*$/g.test(data)) { + module.exports.publicImport(key, data); + return true; + } + + return false; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/pkcs8.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/pkcs8.js new file mode 100644 index 000000000..15012bda8 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/formats/pkcs8.js @@ -0,0 +1,187 @@ +var ber = require('../asn1/index').Ber; +var _ = require('../utils')._; +var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1'; +var utils = require('../utils'); + +const PRIVATE_OPENING_BOUNDARY = '-----BEGIN PRIVATE KEY-----'; +const PRIVATE_CLOSING_BOUNDARY = '-----END PRIVATE KEY-----'; + +const PUBLIC_OPENING_BOUNDARY = '-----BEGIN PUBLIC KEY-----'; +const PUBLIC_CLOSING_BOUNDARY = '-----END PUBLIC KEY-----'; + +module.exports = { + privateExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var d = key.d.toBuffer(); + var p = key.p.toBuffer(); + var q = key.q.toBuffer(); + var dmp1 = key.dmp1.toBuffer(); + var dmq1 = key.dmq1.toBuffer(); + var coeff = key.coeff.toBuffer(); + + var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic + var bodyWriter = new ber.Writer({size: length}); + + bodyWriter.startSequence(); + bodyWriter.writeInt(0); + bodyWriter.writeBuffer(n, 2); + bodyWriter.writeInt(key.e); + bodyWriter.writeBuffer(d, 2); + bodyWriter.writeBuffer(p, 2); + bodyWriter.writeBuffer(q, 2); + bodyWriter.writeBuffer(dmp1, 2); + bodyWriter.writeBuffer(dmq1, 2); + bodyWriter.writeBuffer(coeff, 2); + bodyWriter.endSequence(); + + var writer = new ber.Writer({size: length}); + writer.startSequence(); + writer.writeInt(0); + writer.startSequence(); + writer.writeOID(PUBLIC_RSA_OID); + writer.writeNull(); + writer.endSequence(); + writer.writeBuffer(bodyWriter.buffer, 4); + writer.endSequence(); + + if (options.type === 'der') { + return writer.buffer; + } else { + return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; + } + }, + + privateImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY) + .replace('-----END PRIVATE KEY-----', '') + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } else { + throw Error('Unsupported key format'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var reader = new ber.Reader(buffer); + reader.readSequence(); + reader.readInt(0); + var header = new ber.Reader(reader.readString(0x30, true)); + + if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { + throw Error('Invalid Public key format'); + } + + var body = new ber.Reader(reader.readString(0x04, true)); + body.readSequence(); + body.readString(2, true); // just zero + key.setPrivate( + body.readString(2, true), // modulus + body.readString(2, true), // publicExponent + body.readString(2, true), // privateExponent + body.readString(2, true), // prime1 + body.readString(2, true), // prime2 + body.readString(2, true), // exponent1 -- d mod (p1) + body.readString(2, true), // exponent2 -- d mod (q-1) + body.readString(2, true) // coefficient -- (inverse of q) mod p + ); + }, + + publicExport: function (key, options) { + options = options || {}; + + var n = key.n.toBuffer(); + var length = n.length + 512; // magic + + var bodyWriter = new ber.Writer({size: length}); + bodyWriter.writeByte(0); + bodyWriter.startSequence(); + bodyWriter.writeBuffer(n, 2); + bodyWriter.writeInt(key.e); + bodyWriter.endSequence(); + + var writer = new ber.Writer({size: length}); + writer.startSequence(); + writer.startSequence(); + writer.writeOID(PUBLIC_RSA_OID); + writer.writeNull(); + writer.endSequence(); + writer.writeBuffer(bodyWriter.buffer, 3); + writer.endSequence(); + + if (options.type === 'der') { + return writer.buffer; + } else { + return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; + } + }, + + publicImport: function (key, data, options) { + options = options || {}; + var buffer; + + if (options.type !== 'der') { + if (Buffer.isBuffer(data)) { + data = data.toString('utf8'); + } + + if (_.isString(data)) { + var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) + .replace(/\s+|\n\r|\n|\r$/gm, ''); + buffer = Buffer.from(pem, 'base64'); + } + } else if (Buffer.isBuffer(data)) { + buffer = data; + } else { + throw Error('Unsupported key format'); + } + + var reader = new ber.Reader(buffer); + reader.readSequence(); + var header = new ber.Reader(reader.readString(0x30, true)); + + if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) { + throw Error('Invalid Public key format'); + } + + var body = new ber.Reader(reader.readString(0x03, true)); + body.readByte(); + body.readSequence(); + key.setPublic( + body.readString(0x02, true), // modulus + body.readString(0x02, true) // publicExponent + ); + }, + + /** + * Trying autodetect and import key + * @param key + * @param data + */ + autoImport: function (key, data) { + if (/^[\S\s]*-----BEGIN PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PRIVATE KEY-----[\S\s]*$/g.test(data)) { + module.exports.privateImport(key, data); + return true; + } + + if (/^[\S\s]*-----BEGIN PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PUBLIC KEY-----[\S\s]*$/g.test(data)) { + module.exports.publicImport(key, data); + return true; + } + + return false; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/libs/jsbn.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/libs/jsbn.js new file mode 100644 index 000000000..7276f976b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/libs/jsbn.js @@ -0,0 +1,1252 @@ +/* + * Basic JavaScript BN library - subset useful for RSA encryption. + * + * Copyright (c) 2003-2005 Tom Wu + * ~BSD license~ + * + * Added Node.js Buffers support + * 2014 rzcoder + * + * Deleted some stuff to save bytes + * frosty00 + */ + +var _ = require('../utils')._; + +// Bits per digit +var dbits; + +// (public) Constructor +function BigInteger(a, b) { + if (a != null) { + if ("number" == typeof a) { + this.fromNumber(a, b); + } else if (Buffer.isBuffer(a)) { + this.fromBuffer(a); + } else if (b == null && "string" != typeof a) { + this.fromByteArray(a); + } else { + this.fromString(a, b); + } + } +} + +// return new, unset BigInteger +function nbi() { + return new BigInteger(null); +} + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i, x, w, j, c, n) { + var xl = x & 0x3fff, xh = x >> 14; + while (--n >= 0) { + var l = this[i] & 0x3fff; + var h = this[i++] >> 14; + var m = xh * l + h * xl; + l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; + c = (l >> 28) + (m >> 14) + xh * h; + w[j++] = l & 0xfffffff; + } + return c; +} + +// We need to select the fastest one that works in this environment. +//if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) { +// BigInteger.prototype.am = am2; +// dbits = 30; +//} else if (j_lm && (navigator.appName != "Netscape")) { +// BigInteger.prototype.am = am1; +// dbits = 26; +//} else { // Mozilla/Netscape seems to prefer am3 +// BigInteger.prototype.am = am3; +// dbits = 28; +//} + +// For node.js, we pick am3 with max dbits to 28. +BigInteger.prototype.am = am3; +dbits = 28; + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1 << dbits) - 1); +BigInteger.prototype.DV = (1 << dbits); + +var BI_FP = 52; +BigInteger.prototype.FV = Math.pow(2, BI_FP); +BigInteger.prototype.F1 = BI_FP - dbits; +BigInteger.prototype.F2 = 2 * dbits - BI_FP; + +// Digit conversions +var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; +var BI_RC = new Array(); +var rr, vv; +rr = "0".charCodeAt(0); +for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; +rr = "a".charCodeAt(0); +for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; +rr = "A".charCodeAt(0); +for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; + +function int2char(n) { + return BI_RM.charAt(n); +} +function intAt(s, i) { + var c = BI_RC[s.charCodeAt(i)]; + return (c == null) ? -1 : c; +} + +// (protected) copy this to r +function bnpCopyTo(r) { + for (var i = this.t - 1; i >= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x < 0) ? -1 : 0; + if (x > 0) this[0] = x; + else if (x < -1) this[0] = x + DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { + var r = nbi(); + r.fromInt(i); + return r; +} + +// (protected) set from string and radix +function bnpFromString(data, radix, unsigned) { + var k; + switch (radix) { + case 2: + k = 1; + break; + case 4: + k = 2; + break; + case 8: + k = 3; + break; + case 16: + k = 4; + break; + case 32: + k = 5; + break; + case 256: + k = 8; + break; + default: + this.fromRadix(data, radix); + return; + } + + this.t = 0; + this.s = 0; + + var i = data.length; + var mi = false; + var sh = 0; + + while (--i >= 0) { + var x = (k == 8) ? data[i] & 0xff : intAt(data, i); + if (x < 0) { + if (data.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if (sh === 0) + this[this.t++] = x; + else if (sh + k > this.DB) { + this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh; + this[this.t++] = (x >> (this.DB - sh)); + } + else + this[this.t - 1] |= x << sh; + sh += k; + if (sh >= this.DB) sh -= this.DB; + } + if ((!unsigned) && k == 8 && (data[0] & 0x80) != 0) { + this.s = -1; + if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; + } + this.clamp(); + if (mi) BigInteger.ZERO.subTo(this, this); +} + +function bnpFromByteArray(a, unsigned) { + this.fromString(a, 256, unsigned) +} + +function bnpFromBuffer(a) { + this.fromString(a, 256, true) +} + +// (protected) clamp off excess high words +function bnpClamp() { + var c = this.s & this.DM; + while (this.t > 0 && this[this.t - 1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if (this.s < 0) return "-" + this.negate().toString(b); + var k; + if (b == 16) k = 4; + else if (b == 8) k = 3; + else if (b == 2) k = 1; + else if (b == 32) k = 5; + else if (b == 4) k = 2; + else return this.toRadix(b); + var km = (1 << k) - 1, d, m = false, r = "", i = this.t; + var p = this.DB - (i * this.DB) % k; + if (i-- > 0) { + if (p < this.DB && (d = this[i] >> p) > 0) { + m = true; + r = int2char(d); + } + while (i >= 0) { + if (p < k) { + d = (this[i] & ((1 << p) - 1)) << (k - p); + d |= this[--i] >> (p += this.DB - k); + } + else { + d = (this[i] >> (p -= k)) & km; + if (p <= 0) { + p += this.DB; + --i; + } + } + if (d > 0) m = true; + if (m) r += int2char(d); + } + } + return m ? r : "0"; +} + +// (public) -this +function bnNegate() { + var r = nbi(); + BigInteger.ZERO.subTo(this, r); + return r; +} + +// (public) |this| +function bnAbs() { + return (this.s < 0) ? this.negate() : this; +} + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s - a.s; + if (r != 0) return r; + var i = this.t; + r = i - a.t; + if (r != 0) return (this.s < 0) ? -r : r; + while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if ((t = x >>> 16) != 0) { + x = t; + r += 16; + } + if ((t = x >> 8) != 0) { + x = t; + r += 8; + } + if ((t = x >> 4) != 0) { + x = t; + r += 4; + } + if ((t = x >> 2) != 0) { + x = t; + r += 2; + } + if ((t = x >> 1) != 0) { + x = t; + r += 1; + } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if (this.t <= 0) return 0; + return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n, r) { + var i; + for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i]; + for (i = n - 1; i >= 0; --i) r[i] = 0; + r.t = this.t + n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n, r) { + for (var i = n; i < this.t; ++i) r[i - n] = this[i]; + r.t = Math.max(this.t - n, 0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n, r) { + var bs = n % this.DB; + var cbs = this.DB - bs; + var bm = (1 << cbs) - 1; + var ds = Math.floor(n / this.DB), c = (this.s << bs) & this.DM, i; + for (i = this.t - 1; i >= 0; --i) { + r[i + ds + 1] = (this[i] >> cbs) | c; + c = (this[i] & bm) << bs; + } + for (i = ds - 1; i >= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t + ds + 1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n, r) { + r.s = this.s; + var ds = Math.floor(n / this.DB); + if (ds >= this.t) { + r.t = 0; + return; + } + var bs = n % this.DB; + var cbs = this.DB - bs; + var bm = (1 << bs) - 1; + r[0] = this[ds] >> bs; + for (var i = ds + 1; i < this.t; ++i) { + r[i - ds - 1] |= (this[i] & bm) << cbs; + r[i - ds] = this[i] >> bs; + } + if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs; + r.t = this.t - ds; + r.clamp(); +} + +// (protected) r = this - a +function bnpSubTo(a, r) { + var i = 0, c = 0, m = Math.min(a.t, this.t); + while (i < m) { + c += this[i] - a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + if (a.t < this.t) { + c -= a.s; + while (i < this.t) { + c += this[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while (i < a.t) { + c -= a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c < -1) r[i++] = this.DV + c; + else if (c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a, r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i + y.t; + while (--i >= 0) r[i] = 0; + for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); + r.s = 0; + r.clamp(); + if (this.s != a.s) BigInteger.ZERO.subTo(r, r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2 * x.t; + while (--i >= 0) r[i] = 0; + for (i = 0; i < x.t - 1; ++i) { + var c = x.am(i, x[i], r, 2 * i, 0, 1); + if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { + r[i + x.t] -= x.DV; + r[i + x.t + 1] = 1; + } + } + if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m, q, r) { + var pm = m.abs(); + if (pm.t <= 0) return; + var pt = this.abs(); + if (pt.t < pm.t) { + if (q != null) q.fromInt(0); + if (r != null) this.copyTo(r); + return; + } + if (r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus + if (nsh > 0) { + pm.lShiftTo(nsh, y); + pt.lShiftTo(nsh, r); + } + else { + pm.copyTo(y); + pt.copyTo(r); + } + var ys = y.t; + var y0 = y[ys - 1]; + if (y0 === 0) return; + var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0); + var d1 = this.FV / yt, d2 = (1 << this.F1) / yt, e = 1 << this.F2; + var i = r.t, j = i - ys, t = (q == null) ? nbi() : q; + y.dlShiftTo(j, t); + if (r.compareTo(t) >= 0) { + r[r.t++] = 1; + r.subTo(t, r); + } + BigInteger.ONE.dlShiftTo(ys, t); + t.subTo(y, y); // "negative" y so we can replace sub with am later + while (y.t < ys) y[y.t++] = 0; + while (--j >= 0) { + // Estimate quotient digit + var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); + if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out + y.dlShiftTo(j, t); + r.subTo(t, r); + while (r[i] < --qd) r.subTo(t, r); + } + } + if (q != null) { + r.drShiftTo(ys, q); + if (ts != ms) BigInteger.ZERO.subTo(q, q); + } + r.t = ys; + r.clamp(); + if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder + if (ts < 0) BigInteger.ZERO.subTo(r, r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a, null, r); + if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { + this.m = m; +} +function cConvert(x) { + if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { + return x; +} +function cReduce(x) { + x.divRemTo(this.m, null, x); +} +function cMulTo(x, y, r) { + x.multiplyTo(y, r); + this.reduce(r); +} +function cSqrTo(x, r) { + x.squareTo(r); + this.reduce(r); +} + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if (this.t < 1) return 0; + var x = this[0]; + if ((x & 1) === 0) return 0; + var y = x & 3; // y == 1/x mod 2^2 + y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 + y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 + y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y > 0) ? this.DV - y : -y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp & 0x7fff; + this.mph = this.mp >> 15; + this.um = (1 << (m.DB - 15)) - 1; + this.mt2 = 2 * m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t, r); + r.divRemTo(this.m, null, r); + if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while (x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for (var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i] & 0x7fff; + var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; + // use am to combine the multiply-shift-add into one call + j = i + this.m.t; + x[j] += this.m.am(0, u0, x, i, 0, this.m.t); + // propagate carry + while (x[j] >= x.DV) { + x[j] -= x.DV; + x[++j]++; + } + } + x.clamp(); + x.drShiftTo(this.m.t, x); + if (x.compareTo(this.m) >= 0) x.subTo(this.m, x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x, r) { + x.squareTo(r); + this.reduce(r); +} + +// r = "xy/R mod m"; x,y != r +function montMulTo(x, y, r) { + x.multiplyTo(y, r); + this.reduce(r); +} + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { + return ((this.t > 0) ? (this[0] & 1) : this.s) === 0; +} + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e, z) { + if (e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1; + g.copyTo(r); + while (--i >= 0) { + z.sqrTo(r, r2); + if ((e & (1 << i)) > 0) z.mulTo(r2, g, r); + else { + var t = r; + r = r2; + r2 = t; + } + } + return z.revert(r); +} + +// Copyright (c) 2005-2009 Tom Wu +// All Rights Reserved. +// See "LICENSE" for details. + +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero +// Version 1.2: square() API, isProbablePrime fix + +//(public) +function bnClone() { + var r = nbi(); + this.copyTo(r); + return r; +} + +//(public) return value as integer +function bnIntValue() { + if (this.s < 0) { + if (this.t == 1) return this[0] - this.DV; + else if (this.t === 0) return -1; + } + else if (this.t == 1) return this[0]; + else if (this.t === 0) return 0; +// assumes 16 < DB < 32 + return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0]; +} + +//(protected) return x s.t. r^x < DV +function bnpChunkSize(r) { + return Math.floor(Math.LN2 * this.DB / Math.log(r)); +} + +//(public) 0 if this === 0, 1 if this > 0 +function bnSigNum() { + if (this.s < 0) return -1; + else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +//(protected) convert to radix string +function bnpToRadix(b) { + if (b == null) b = 10; + if (this.signum() === 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b, cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d, y, z); + while (y.signum() > 0) { + r = (a + z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d, y, z); + } + return z.intValue().toString(b) + r; +} + +//(protected) convert from radix string +function bnpFromRadix(s, b) { + this.fromInt(0); + if (b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b, cs), mi = false, j = 0, w = 0; + for (var i = 0; i < s.length; ++i) { + var x = intAt(s, i); + if (x < 0) { + if (s.charAt(i) == "-" && this.signum() === 0) mi = true; + continue; + } + w = b * w + x; + if (++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w, 0); + j = 0; + w = 0; + } + } + if (j > 0) { + this.dMultiply(Math.pow(b, j)); + this.dAddOffset(w, 0); + } + if (mi) BigInteger.ZERO.subTo(this, this); +} + +//(public) convert to bigendian byte array +function bnToByteArray() { + var i = this.t, r = new Array(); + r[0] = this.s; + var p = this.DB - (i * this.DB) % 8, d, k = 0; + if (i-- > 0) { + if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) + r[k++] = d | (this.s << (this.DB - p)); + while (i >= 0) { + if (p < 8) { + d = (this[i] & ((1 << p) - 1)) << (8 - p); + d |= this[--i] >> (p += this.DB - 8); + } + else { + d = (this[i] >> (p -= 8)) & 0xff; + if (p <= 0) { + p += this.DB; + --i; + } + } + if ((d & 0x80) != 0) d |= -256; + if (k === 0 && (this.s & 0x80) != (d & 0x80)) ++k; + if (k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +/** + * return Buffer object + * @param trim {boolean} slice buffer if first element == 0 + * @returns {Buffer} + */ +function bnToBuffer(trimOrSize) { + var res = Buffer.from(this.toByteArray()); + if (trimOrSize === true && res[0] === 0) { + res = res.slice(1); + } else if (_.isNumber(trimOrSize)) { + if (res.length > trimOrSize) { + for (var i = 0; i < res.length - trimOrSize; i++) { + if (res[i] !== 0) { + return null; + } + } + return res.slice(res.length - trimOrSize); + } else if (res.length < trimOrSize) { + var padded = Buffer.alloc(trimOrSize); + padded.fill(0, 0, trimOrSize - res.length); + res.copy(padded, trimOrSize - res.length); + return padded; + } + } + return res; +} + +function bnEquals(a) { + return (this.compareTo(a) == 0); +} +function bnMin(a) { + return (this.compareTo(a) < 0) ? this : a; +} +function bnMax(a) { + return (this.compareTo(a) > 0) ? this : a; +} + +//(protected) r = this op a (bitwise) +function bnpBitwiseTo(a, op, r) { + var i, f, m = Math.min(a.t, this.t); + for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]); + if (a.t < this.t) { + f = a.s & this.DM; + for (i = m; i < this.t; ++i) r[i] = op(this[i], f); + r.t = this.t; + } + else { + f = this.s & this.DM; + for (i = m; i < a.t; ++i) r[i] = op(f, a[i]); + r.t = a.t; + } + r.s = op(this.s, a.s); + r.clamp(); +} + +//(public) this & a +function op_and(x, y) { + return x & y; +} +function bnAnd(a) { + var r = nbi(); + this.bitwiseTo(a, op_and, r); + return r; +} + +//(public) this | a +function op_or(x, y) { + return x | y; +} +function bnOr(a) { + var r = nbi(); + this.bitwiseTo(a, op_or, r); + return r; +} + +//(public) this ^ a +function op_xor(x, y) { + return x ^ y; +} +function bnXor(a) { + var r = nbi(); + this.bitwiseTo(a, op_xor, r); + return r; +} + +//(public) this & ~a +function op_andnot(x, y) { + return x & ~y; +} + +//(public) ~this +function bnNot() { + var r = nbi(); + for (var i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +//return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if (x === 0) return -1; + var r = 0; + if ((x & 0xffff) === 0) { + x >>= 16; + r += 16; + } + if ((x & 0xff) === 0) { + x >>= 8; + r += 8; + } + if ((x & 0xf) === 0) { + x >>= 4; + r += 4; + } + if ((x & 3) === 0) { + x >>= 2; + r += 2; + } + if ((x & 1) === 0) ++r; + return r; +} + +//(public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for (var i = 0; i < this.t; ++i) + if (this[i] != 0) return i * this.DB + lbit(this[i]); + if (this.s < 0) return this.t * this.DB; + return -1; +} + +//return number of 1 bits in x +function cbit(x) { + var r = 0; + while (x != 0) { + x &= x - 1; + ++r; + } + return r; +} + +//(protected) r = this + a +function bnpAddTo(a, r) { + var i = 0, c = 0, m = Math.min(a.t, this.t); + while (i < m) { + c += this[i] + a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + if (a.t < this.t) { + c += a.s; + while (i < this.t) { + c += this[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while (i < a.t) { + c += a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c > 0) r[i++] = c; + else if (c < -1) r[i++] = this.DV + c; + r.t = i; + r.clamp(); +} + +//(public) this + a +function bnAdd(a) { + var r = nbi(); + this.addTo(a, r); + return r; +} + +//(public) this - a +function bnSubtract(a) { + var r = nbi(); + this.subTo(a, r); + return r; +} + +//(public) this * a +function bnMultiply(a) { + var r = nbi(); + this.multiplyTo(a, r); + return r; +} + +// (public) this^2 +function bnSquare() { + var r = nbi(); + this.squareTo(r); + return r; +} + +//(public) this / a +function bnDivide(a) { + var r = nbi(); + this.divRemTo(a, r, null); + return r; +} + +//(public) this % a +function bnRemainder(a) { + var r = nbi(); + this.divRemTo(a, null, r); + return r; +} + +//(protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0, n - 1, this, 0, 0, this.t); + ++this.t; + this.clamp(); +} + +//(protected) this += n << w words, this >= 0 +function bnpDAddOffset(n, w) { + if (n === 0) return; + while (this.t <= w) this[this.t++] = 0; + this[w] += n; + while (this[w] >= this.DV) { + this[w] -= this.DV; + if (++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +//A "null" reducer +function NullExp() { +} +function nNop(x) { + return x; +} +function nMulTo(x, y, r) { + x.multiplyTo(y, r); +} +function nSqrTo(x, r) { + x.squareTo(r); +} + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +//(public) this^e +function bnPow(e) { + return this.exp(e, new NullExp()); +} + +//(protected) r = lower n words of "this * a", a.t <= n +//"this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a, n, r) { + var i = Math.min(this.t + a.t, n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while (i > 0) r[--i] = 0; + var j; + for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t); + for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i); + r.clamp(); +} + +//(protected) r = "this * a" without lower n words, n > 0 +//"this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a, n, r) { + --n; + var i = r.t = this.t + a.t - n; + r.s = 0; // assumes a,this >= 0 + while (--i >= 0) r[i] = 0; + for (i = Math.max(n - this.t, 0); i < a.t; ++i) + r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); + r.clamp(); + r.drShiftTo(1, r); +} + +//Barrett modular reduction +function Barrett(m) { +// setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2 * m.t, this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m); + else if (x.compareTo(this.m) < 0) return x; + else { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; + } +} + +function barrettRevert(x) { + return x; +} + +//x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t - 1, this.r2); + if (x.t > this.m.t + 1) { + x.t = this.m.t + 1; + x.clamp(); + } + this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); + this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); + while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1); + x.subTo(this.r2, x); + while (x.compareTo(this.m) >= 0) x.subTo(this.m, x); +} + +//r = x^2 mod m; x != r +function barrettSqrTo(x, r) { + x.squareTo(r); + this.reduce(r); +} + +//r = x*y mod m; x,y != r +function barrettMulTo(x, y, r) { + x.multiplyTo(y, r); + this.reduce(r); +} + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +//(public) this^e % m (HAC 14.85) +function bnModPow(e, m) { + var i = e.bitLength(), k, r = nbv(1), z; + if (i <= 0) return r; + else if (i < 18) k = 1; + else if (i < 48) k = 3; + else if (i < 144) k = 4; + else if (i < 768) k = 5; + else k = 6; + if (i < 8) + z = new Classic(m); + else if (m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + +// precomputation + var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1; + g[1] = z.convert(this); + if (k > 1) { + var g2 = nbi(); + z.sqrTo(g[1], g2); + while (n <= km) { + g[n] = nbi(); + z.mulTo(g2, g[n - 2], g[n]); + n += 2; + } + } + + var j = e.t - 1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j]) - 1; + while (j >= 0) { + if (i >= k1) w = (e[j] >> (i - k1)) & km; + else { + w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); + if (j > 0) w |= e[j - 1] >> (this.DB + i - k1); + } + + n = k; + while ((w & 1) === 0) { + w >>= 1; + --n; + } + if ((i -= n) < 0) { + i += this.DB; + --j; + } + if (is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while (n > 1) { + z.sqrTo(r, r2); + z.sqrTo(r2, r); + n -= 2; + } + if (n > 0) z.sqrTo(r, r2); else { + t = r; + r = r2; + r2 = t; + } + z.mulTo(r2, g[w], r); + } + + while (j >= 0 && (e[j] & (1 << i)) === 0) { + z.sqrTo(r, r2); + t = r; + r = r2; + r2 = t; + if (--i < 0) { + i = this.DB - 1; + --j; + } + } + } + return z.revert(r); +} + +//(public) gcd(this,a) (HAC 14.54) +function bnGCD(a) { + var x = (this.s < 0) ? this.negate() : this.clone(); + var y = (a.s < 0) ? a.negate() : a.clone(); + if (x.compareTo(y) < 0) { + var t = x; + x = y; + y = t; + } + var i = x.getLowestSetBit(), g = y.getLowestSetBit(); + if (g < 0) return x; + if (i < g) g = i; + if (g > 0) { + x.rShiftTo(g, x); + y.rShiftTo(g, y); + } + while (x.signum() > 0) { + if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x); + if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y); + if (x.compareTo(y) >= 0) { + x.subTo(y, x); + x.rShiftTo(1, x); + } + else { + y.subTo(x, y); + y.rShiftTo(1, y); + } + } + if (g > 0) y.lShiftTo(g, y); + return y; +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.fromByteArray = bnpFromByteArray; +BigInteger.prototype.fromBuffer = bnpFromBuffer; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; + +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.toBuffer = bnToBuffer; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.int2char = int2char; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); + +// JSBN-specific extension +BigInteger.prototype.square = bnSquare; + +//BigInteger interfaces not implemented in jsbn: + +//BigInteger(int signum, byte[] magnitude) +//double doubleValue() +//float floatValue() +//int hashCode() +//long longValue() +//static BigInteger valueOf(long val) + +module.exports = BigInteger; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/libs/rsa.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/libs/rsa.js new file mode 100644 index 000000000..23256a300 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/libs/rsa.js @@ -0,0 +1,147 @@ +/* + * RSA Encryption / Decryption with PKCS1 v2 Padding. + * + * ~MIT License~ + * Contributors: + * Tom Wu, rzcoder, frosty00 +*/ + +var _ = require('../utils')._; +var BigInteger = require('./jsbn.js'); +var utils = require('../utils.js'); +var schemes = require('../schemes/schemes.js'); + +//exports.BigInteger = BigInteger; +module.exports.Key = (function () { + /** + * RSA key constructor + * + * n - modulus + * e - publicExponent + * d - privateExponent + * p - prime1 + * q - prime2 + * dmp1 - exponent1 -- d mod (p1) + * dmq1 - exponent2 -- d mod (q-1) + * coeff - coefficient -- (inverse of q) mod p + */ + function RSAKey() { + this.n = null; + this.e = 0; + this.d = null; + this.p = null; + this.q = null; + this.dmp1 = null; + this.dmq1 = null; + this.coeff = null; + } + + RSAKey.prototype.setOptions = function (options) { + var signingSchemeProvider = schemes[options.signingScheme]; + var encryptionSchemeProvider = schemes[options.encryptionScheme]; + + if (signingSchemeProvider === encryptionSchemeProvider) { + this.signingScheme = this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); + } else { + this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options); + this.signingScheme = signingSchemeProvider.makeScheme(this, options); + } + + }; + /** + * Set the private key fields N, e, d and CRT params from buffers + * + * @param N + * @param E + * @param D + * @param P + * @param Q + * @param DP + * @param DQ + * @param C + */ + RSAKey.prototype.setPrivate = function (N, E, D, P, Q, DP, DQ, C) { + if (N && E && D && N.length > 0 && (_.isNumber(E) || E.length > 0) && D.length > 0) { + this.n = new BigInteger(N); + this.e = _.isNumber(E) ? E : utils.get32IntFromBuffer(E, 0); + this.d = new BigInteger(D); + + if (P && Q && DP && DQ && C) { + this.p = new BigInteger(P); + this.q = new BigInteger(Q); + this.dmp1 = new BigInteger(DP); + this.dmq1 = new BigInteger(DQ); + this.coeff = new BigInteger(C); + } else { + // TODO: re-calculate any missing CRT params + } + this.$$recalculateCache(); + } else { + throw Error("Invalid RSA private key"); + } + }; + + /** + * private + * Perform raw private operation on "x": return x^d (mod n) + * + * @param x + * @returns {*} + */ + RSAKey.prototype.$doPrivate = function (x) { + if (this.p || this.q) { + return x.modPow(this.d, this.n); + } + + // TODO: re-calculate any missing CRT params + var xp = x.mod(this.p).modPow(this.dmp1, this.p); + var xq = x.mod(this.q).modPow(this.dmq1, this.q); + + while (xp.compareTo(xq) < 0) { + xp = xp.add(this.p); + } + return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); + }; + + RSAKey.prototype.sign = function (buffer) { + return this.signingScheme.sign.apply(this.signingScheme, arguments); + }; + + /** + * Check if key pair contains private key + */ + RSAKey.prototype.isPrivate = function () { + return this.n && this.e && this.d || false; + }; + + Object.defineProperty(RSAKey.prototype, 'keySize', { + get: function () { + return this.cache.keyBitLength; + } + }); + + Object.defineProperty(RSAKey.prototype, 'encryptedDataLength', { + get: function () { + return this.cache.keyByteLength; + } + }); + + Object.defineProperty(RSAKey.prototype, 'maxMessageLength', { + get: function () { + return this.encryptionScheme.maxMessageLength(); + } + }); + + /** + * Caching key data + */ + RSAKey.prototype.$$recalculateCache = function () { + this.cache = this.cache || {}; + // Bit & byte length + this.cache.keyBitLength = this.n.bitLength(); + this.cache.keyByteLength = (this.cache.keyBitLength + 6) >> 3; + }; + + return RSAKey; +})(); + diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/schemes/pkcs1.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/schemes/pkcs1.js new file mode 100644 index 000000000..3578ca186 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/schemes/pkcs1.js @@ -0,0 +1,176 @@ +/** + * PKCS1 padding and signature scheme + */ + +var BigInteger = require('../libs/jsbn'); +var CryptoJS = require('../../crypto-js/crypto-js'); +var constants = { RSA_NO_PADDING: 3 } +require('constants'); +var SIGN_INFO_HEAD = { + md2: Buffer.from('3020300c06082a864886f70d020205000410', 'hex'), + md5: Buffer.from('3020300c06082a864886f70d020505000410', 'hex'), + sha1: Buffer.from('3021300906052b0e03021a05000414', 'hex'), + sha224: Buffer.from('302d300d06096086480165030402040500041c', 'hex'), + sha256: Buffer.from('3031300d060960864801650304020105000420', 'hex'), + sha384: Buffer.from('3041300d060960864801650304020205000430', 'hex'), + sha512: Buffer.from('3051300d060960864801650304020305000440', 'hex'), + ripemd160: Buffer.from('3021300906052b2403020105000414', 'hex'), + rmd160: Buffer.from('3021300906052b2403020105000414', 'hex') +}; + +var SIGN_ALG_TO_HASH_ALIASES = { + 'ripemd160': 'rmd160' +}; + +var DEFAULT_HASH_FUNCTION = 'sha256'; + +module.exports = { + isEncryption: true, + isSignature: true +}; + +module.exports.makeScheme = function (key, options) { + function Scheme(key, options) { + this.key = key; + this.options = options; + } + + Scheme.prototype.maxMessageLength = function () { + if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { + return this.key.encryptedDataLength; + } + return this.key.encryptedDataLength - 11; + }; + + /** + * Unpad input Buffer and, if valid, return the Buffer object + * alg: PKCS#1 (type 2, random) + * @param buffer + * @returns {Buffer} + */ + Scheme.prototype.encUnPad = function (buffer, options) { + options = options || {}; + var i = 0; + + if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) { + //RSA_NO_PADDING treated like JAVA left pad with zero character + var unPad; + if (typeof buffer.lastIndexOf == "function") { //patch for old node version + unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length); + } else { + unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length); + } + return unPad; + } + + if (buffer.length < 4) { + return null; + } + + /* Type 1: zeros padding for private key decrypt */ + if (options.type === 1) { + if (buffer[0] !== 0 && buffer[1] !== 1) { + return null; + } + i = 3; + while (buffer[i] !== 0) { + if (buffer[i] != 0xFF || ++i >= buffer.length) { + return null; + } + } + } else { + /* random padding for public key decrypt */ + if (buffer[0] !== 0 && buffer[1] !== 2) { + return null; + } + i = 3; + while (buffer[i] !== 0) { + if (++i >= buffer.length) { + return null; + } + } + } + return buffer.slice(i + 1, buffer.length); + }; + + Scheme.prototype.sign = function (buffer) { + var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION; + if (this.options.environment === 'browser') { + hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm; + var hasher = CryptoJS[hashAlgorithm.toUpperCase()] (buffer.toString()); + var asBuffer = wordArrayToBuffer(hasher) + var paddedHash = this.pkcs1pad(asBuffer, hashAlgorithm); + var res = this.key.$doPrivate(new BigInteger(paddedHash)).toBuffer(this.key.encryptedDataLength); + return res; + } else { + throw new Error ('CCXT only supports browser mode :P'); + } + }; + + /** + * PKCS#1 pad input buffer to max data length + * @param hashBuf + * @param hashAlgorithm + * @returns {*} + */ + Scheme.prototype.pkcs1pad = function (hashBuf, hashAlgorithm) { + var digest = SIGN_INFO_HEAD[hashAlgorithm]; + if (!digest) { + throw Error('Unsupported hash algorithm'); + } + + var data = Buffer.concat([digest, hashBuf]); + + if (data.length + 10 > this.key.encryptedDataLength) { + throw Error('Key is too short for signing algorithm (' + hashAlgorithm + ')'); + } + + var filled = Buffer.alloc(this.key.encryptedDataLength - data.length - 1); + filled.fill(0xff, 0, filled.length - 1); + filled[0] = 1; + filled[filled.length - 1] = 0; + + var res = Buffer.concat([filled, data]); + + return res; + }; + + return new Scheme(key, options); +}; + +// used to convert `CryptoJS` wordArrays into `crypto` hex buffers +function wordToByteArray(word, length) { + var ba = [], + xFF = 0xFF; + if (length > 0) + ba.push(word >>> 24); + if (length > 1) + ba.push((word >>> 16) & xFF); + if (length > 2) + ba.push((word >>> 8) & xFF); + if (length > 3) + ba.push(word & xFF); + + return ba; +} + +function wordArrayToBuffer(wordArray) { + let length = undefined; + if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) { + length = wordArray.sigBytes; + wordArray = wordArray.words; + } else { + throw Error('Argument not a wordArray') + } + + const result = [] + let bytes = [] + let i = 0; + while (length > 0) { + bytes = wordToByteArray(wordArray[i], Math.min(4, length)); + length -= bytes.length; + result.push(bytes); + i++; + } + return new Buffer.from([].concat.apply([], result), 'hex'); +} diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/schemes/schemes.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/schemes/schemes.js new file mode 100644 index 000000000..5faf139f2 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/schemes/schemes.js @@ -0,0 +1,21 @@ +module.exports = { + pkcs1: require('./pkcs1'), + + /** + * Check if scheme has padding methods + * @param scheme {string} + * @returns {Boolean} + */ + isEncryption: function (scheme) { + return module.exports[scheme] && module.exports[scheme].isEncryption; + }, + + /** + * Check if scheme has sign/verify methods + * @param scheme {string} + * @returns {Boolean} + */ + isSignature: function (scheme) { + return module.exports[scheme] && module.exports[scheme].isSignature; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/utils.js b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/utils.js new file mode 100644 index 000000000..7d3c693f7 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/node-rsa/utils.js @@ -0,0 +1,98 @@ +/* + * Utils functions + * + */ + +/** + * Break string str each maxLen symbols + * @param str + * @param maxLen + * @returns {string} + */ +module.exports.linebrk = function (str, maxLen) { + var res = ''; + var i = 0; + while (i + maxLen < str.length) { + res += str.substring(i, i + maxLen) + "\n"; + i += maxLen; + } + return res + str.substring(i, str.length); +}; + +/** + * Trying get a 32-bit unsigned integer from the partial buffer + * @param buffer + * @param offset + * @returns {Number} + */ +module.exports.get32IntFromBuffer = function (buffer, offset) { + offset = offset || 0; + var size = 0; + if ((size = buffer.length - offset) > 0) { + if (size >= 4) { + return buffer.readUInt32BE(offset); + } else { + var res = 0; + for (var i = offset + size, d = 0; i > offset; i--, d += 2) { + res += buffer[i - 1] * Math.pow(16, d); + } + return res; + } + } else { + return NaN; + } +}; + +module.exports._ = { + isObject: function (value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + }, + + isString: function (value) { + return typeof value == 'string' || value instanceof String; + }, + + isNumber: function (value) { + return typeof value == 'number' || !isNaN(parseFloat(value)) && isFinite(value); + }, + + /** + * Returns copy of `obj` without `removeProp` field. + * @param obj + * @param removeProp + * @returns Object + */ + omit: function (obj, removeProp) { + var newObj = {}; + for (var prop in obj) { + if (!obj.hasOwnProperty(prop) || prop === removeProp) { + continue; + } + newObj[prop] = obj[prop]; + } + + return newObj; + } +}; + +/** + * Strips everything around the opening and closing lines, including the lines + * themselves. + */ +module.exports.trimSurroundingText = function (data, opening, closing) { + var trimStartIndex = 0; + var trimEndIndex = data.length; + + var openingBoundaryIndex = data.indexOf(opening); + if (openingBoundaryIndex >= 0) { + trimStartIndex = openingBoundaryIndex + opening.length; + } + + var closingBoundaryIndex = data.indexOf(closing, openingBoundaryIndex); + if (closingBoundaryIndex >= 0) { + trimEndIndex = closingBoundaryIndex; + } + + return data.substring(trimStartIndex, trimEndIndex); +} diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/qs/formats.js b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/formats.js new file mode 100644 index 000000000..df4599752 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/formats.js @@ -0,0 +1,18 @@ +'use strict'; + +var replace = String.prototype.replace; +var percentTwenties = /%20/g; + +module.exports = { + 'default': 'RFC3986', + formatters: { + RFC1738: function (value) { + return replace.call(value, percentTwenties, '+'); + }, + RFC3986: function (value) { + return value; + } + }, + RFC1738: 'RFC1738', + RFC3986: 'RFC3986' +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/qs/index.js b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/index.js new file mode 100644 index 000000000..0d6a97dcf --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/index.js @@ -0,0 +1,11 @@ +'use strict'; + +var stringify = require('./stringify'); +var parse = require('./parse'); +var formats = require('./formats'); + +module.exports = { + formats: formats, + parse: parse, + stringify: stringify +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/qs/parse.js b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/parse.js new file mode 100644 index 000000000..d81628b57 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/parse.js @@ -0,0 +1,242 @@ +'use strict'; + +var utils = require('./utils'); + +var has = Object.prototype.hasOwnProperty; + +var defaults = { + allowDots: false, + allowPrototypes: false, + arrayLimit: 20, + charset: 'utf-8', + charsetSentinel: false, + comma: false, + decoder: utils.decode, + delimiter: '&', + depth: 5, + ignoreQueryPrefix: false, + interpretNumericEntities: false, + parameterLimit: 1000, + parseArrays: true, + plainObjects: false, + strictNullHandling: false +}; + +var interpretNumericEntities = function (str) { + return str.replace(/&#(\d+);/g, function ($0, numberStr) { + return String.fromCharCode(parseInt(numberStr, 10)); + }); +}; + +// This is what browsers will submit when the ✓ character occurs in an +// application/x-www-form-urlencoded body and the encoding of the page containing +// the form is iso-8859-1, or when the submitted form has an accept-charset +// attribute of iso-8859-1. Presumably also with other charsets that do not contain +// the ✓ character, such as us-ascii. +var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓') + +// These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded. +var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓') + +var parseValues = function parseQueryStringValues(str, options) { + var obj = {}; + var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str; + var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit; + var parts = cleanStr.split(options.delimiter, limit); + var skipIndex = -1; // Keep track of where the utf8 sentinel was found + var i; + + var charset = options.charset; + if (options.charsetSentinel) { + for (i = 0; i < parts.length; ++i) { + if (parts[i].indexOf('utf8=') === 0) { + if (parts[i] === charsetSentinel) { + charset = 'utf-8'; + } else if (parts[i] === isoSentinel) { + charset = 'iso-8859-1'; + } + skipIndex = i; + i = parts.length; // The eslint settings do not allow break; + } + } + } + + for (i = 0; i < parts.length; ++i) { + if (i === skipIndex) { + continue; + } + var part = parts[i]; + + var bracketEqualsPos = part.indexOf(']='); + var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1; + + var key, val; + if (pos === -1) { + key = options.decoder(part, defaults.decoder, charset); + val = options.strictNullHandling ? null : ''; + } else { + key = options.decoder(part.slice(0, pos), defaults.decoder, charset); + val = options.decoder(part.slice(pos + 1), defaults.decoder, charset); + } + + if (val && options.interpretNumericEntities && charset === 'iso-8859-1') { + val = interpretNumericEntities(val); + } + + if (val && options.comma && val.indexOf(',') > -1) { + val = val.split(','); + } + + if (has.call(obj, key)) { + obj[key] = utils.combine(obj[key], val); + } else { + obj[key] = val; + } + } + + return obj; +}; + +var parseObject = function (chain, val, options) { + var leaf = val; + + for (var i = chain.length - 1; i >= 0; --i) { + var obj; + var root = chain[i]; + + if (root === '[]' && options.parseArrays) { + obj = [].concat(leaf); + } else { + obj = options.plainObjects ? Object.create(null) : {}; + var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root; + var index = parseInt(cleanRoot, 10); + if (!options.parseArrays && cleanRoot === '') { + obj = { 0: leaf }; + } else if ( + !isNaN(index) + && root !== cleanRoot + && String(index) === cleanRoot + && index >= 0 + && (options.parseArrays && index <= options.arrayLimit) + ) { + obj = []; + obj[index] = leaf; + } else { + obj[cleanRoot] = leaf; + } + } + + leaf = obj; + } + + return leaf; +}; + +var parseKeys = function parseQueryStringKeys(givenKey, val, options) { + if (!givenKey) { + return; + } + + // Transform dot notation to bracket notation + var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey; + + // The regex chunks + + var brackets = /(\[[^[\]]*])/; + var child = /(\[[^[\]]*])/g; + + // Get the parent + + var segment = brackets.exec(key); + var parent = segment ? key.slice(0, segment.index) : key; + + // Stash the parent if it exists + + var keys = []; + if (parent) { + // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties + if (!options.plainObjects && has.call(Object.prototype, parent)) { + if (!options.allowPrototypes) { + return; + } + } + + keys.push(parent); + } + + // Loop through children appending to the array until we hit depth + + var i = 0; + while ((segment = child.exec(key)) !== null && i < options.depth) { + i += 1; + if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) { + if (!options.allowPrototypes) { + return; + } + } + keys.push(segment[1]); + } + + // If there's a remainder, just add whatever is left + + if (segment) { + keys.push('[' + key.slice(segment.index) + ']'); + } + + return parseObject(keys, val, options); +}; + +var normalizeParseOptions = function normalizeParseOptions(opts) { + if (!opts) { + return defaults; + } + + if (opts.decoder !== null && opts.decoder !== undefined && typeof opts.decoder !== 'function') { + throw new TypeError('Decoder has to be a function.'); + } + + if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { + throw new Error('The charset option must be either utf-8, iso-8859-1, or undefined'); + } + var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset; + + return { + allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots, + allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes, + arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit, + charset: charset, + charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, + comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma, + decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder, + delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter, + depth: typeof opts.depth === 'number' ? opts.depth : defaults.depth, + ignoreQueryPrefix: opts.ignoreQueryPrefix === true, + interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities, + parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit, + parseArrays: opts.parseArrays !== false, + plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects, + strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling + }; +}; + +module.exports = function (str, opts) { + var options = normalizeParseOptions(opts); + + if (str === '' || str === null || typeof str === 'undefined') { + return options.plainObjects ? Object.create(null) : {}; + } + + var tempObj = typeof str === 'string' ? parseValues(str, options) : str; + var obj = options.plainObjects ? Object.create(null) : {}; + + // Iterate over the keys and setup the new object + + var keys = Object.keys(tempObj); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var newObj = parseKeys(key, tempObj[key], options); + obj = utils.merge(obj, newObj, options); + } + + return utils.compact(obj); +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/qs/stringify.js b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/stringify.js new file mode 100644 index 000000000..7455049ca --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/stringify.js @@ -0,0 +1,269 @@ +'use strict'; + +var utils = require('./utils'); +var formats = require('./formats'); +var has = Object.prototype.hasOwnProperty; + +var arrayPrefixGenerators = { + brackets: function brackets(prefix) { // eslint-disable-line func-name-matching + return prefix + '[]'; + }, + comma: 'comma', + indices: function indices(prefix, key) { // eslint-disable-line func-name-matching + return prefix + '[' + key + ']'; + }, + repeat: function repeat(prefix) { // eslint-disable-line func-name-matching + return prefix; + } +}; + +var isArray = Array.isArray; +var push = Array.prototype.push; +var pushToArray = function (arr, valueOrArray) { + push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]); +}; + +var toISO = Date.prototype.toISOString; + +var defaults = { + addQueryPrefix: false, + allowDots: false, + charset: 'utf-8', + charsetSentinel: false, + delimiter: '&', + encode: true, + encoder: utils.encode, + encodeValuesOnly: false, + formatter: formats.formatters[formats['default']], + // deprecated + indices: false, + serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching + return toISO.call(date); + }, + skipNulls: false, + strictNullHandling: false +}; + +var stringify = function stringify( // eslint-disable-line func-name-matching + object, + prefix, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly, + charset +) { + var obj = object; + if (typeof filter === 'function') { + obj = filter(prefix, obj); + } else if (obj instanceof Date) { + obj = serializeDate(obj); + } else if (generateArrayPrefix === 'comma' && isArray(obj)) { + obj = obj.join(','); + } + + if (obj === null) { + if (strictNullHandling) { + return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix; + } + + obj = ''; + } + + if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) { + if (encoder) { + var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset); + return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))]; + } + return [formatter(prefix) + '=' + formatter(String(obj))]; + } + + var values = []; + + if (typeof obj === 'undefined') { + return values; + } + + var objKeys; + if (isArray(filter)) { + objKeys = filter; + } else { + var keys = Object.keys(obj); + objKeys = sort ? keys.sort(sort) : keys; + } + + for (var i = 0; i < objKeys.length; ++i) { + var key = objKeys[i]; + + if (skipNulls && obj[key] === null) { + continue; + } + + if (isArray(obj)) { + pushToArray(values, stringify( + obj[key], + typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix, + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly, + charset + )); + } else { + pushToArray(values, stringify( + obj[key], + prefix + (allowDots ? '.' + key : '[' + key + ']'), + generateArrayPrefix, + strictNullHandling, + skipNulls, + encoder, + filter, + sort, + allowDots, + serializeDate, + formatter, + encodeValuesOnly, + charset + )); + } + } + + return values; +}; + +var normalizeStringifyOptions = function normalizeStringifyOptions(opts) { + if (!opts) { + return defaults; + } + + if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') { + throw new TypeError('Encoder has to be a function.'); + } + + var charset = opts.charset || defaults.charset; + if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') { + throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'); + } + + var format = formats['default']; + if (typeof opts.format !== 'undefined') { + if (!has.call(formats.formatters, opts.format)) { + throw new TypeError('Unknown format option provided.'); + } + format = opts.format; + } + var formatter = formats.formatters[format]; + + var filter = defaults.filter; + if (typeof opts.filter === 'function' || isArray(opts.filter)) { + filter = opts.filter; + } + + return { + addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix, + allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots, + charset: charset, + charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel, + delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter, + encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode, + encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder, + encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly, + filter: filter, + formatter: formatter, + serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate, + skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls, + sort: typeof opts.sort === 'function' ? opts.sort : null, + strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling + }; +}; + +module.exports = function (object, opts) { + var obj = object; + var options = normalizeStringifyOptions(opts); + + var objKeys; + var filter; + + if (typeof options.filter === 'function') { + filter = options.filter; + obj = filter('', obj); + } else if (isArray(options.filter)) { + filter = options.filter; + objKeys = filter; + } + + var keys = []; + + if (typeof obj !== 'object' || obj === null) { + return ''; + } + + var arrayFormat; + if (opts && opts.arrayFormat in arrayPrefixGenerators) { + arrayFormat = opts.arrayFormat; + } else if (opts && 'indices' in opts) { + arrayFormat = opts.indices ? 'indices' : 'repeat'; + } else { + arrayFormat = 'indices'; + } + + var generateArrayPrefix = arrayPrefixGenerators[arrayFormat]; + + if (!objKeys) { + objKeys = Object.keys(obj); + } + + if (options.sort) { + objKeys.sort(options.sort); + } + + for (var i = 0; i < objKeys.length; ++i) { + var key = objKeys[i]; + + if (options.skipNulls && obj[key] === null) { + continue; + } + pushToArray(keys, stringify( + obj[key], + key, + generateArrayPrefix, + options.strictNullHandling, + options.skipNulls, + options.encode ? options.encoder : null, + options.filter, + options.sort, + options.allowDots, + options.serializeDate, + options.formatter, + options.encodeValuesOnly, + options.charset + )); + } + + var joined = keys.join(options.delimiter); + var prefix = options.addQueryPrefix === true ? '?' : ''; + + if (options.charsetSentinel) { + if (options.charset === 'iso-8859-1') { + // encodeURIComponent('✓'), the "numeric entity" representation of a checkmark + prefix += 'utf8=%26%2310003%3B&'; + } else { + // encodeURIComponent('✓') + prefix += 'utf8=%E2%9C%93&'; + } + } + + return joined.length > 0 ? prefix + joined : ''; +}; diff --git a/TaskServer/node_modules/ccxt/js/static_dependencies/qs/utils.js b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/utils.js new file mode 100644 index 000000000..1b219cdde --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/static_dependencies/qs/utils.js @@ -0,0 +1,230 @@ +'use strict'; + +var has = Object.prototype.hasOwnProperty; +var isArray = Array.isArray; + +var hexTable = (function () { + var array = []; + for (var i = 0; i < 256; ++i) { + array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); + } + + return array; +}()); + +var compactQueue = function compactQueue(queue) { + while (queue.length > 1) { + var item = queue.pop(); + var obj = item.obj[item.prop]; + + if (isArray(obj)) { + var compacted = []; + + for (var j = 0; j < obj.length; ++j) { + if (typeof obj[j] !== 'undefined') { + compacted.push(obj[j]); + } + } + + item.obj[item.prop] = compacted; + } + } +}; + +var arrayToObject = function arrayToObject(source, options) { + var obj = options && options.plainObjects ? Object.create(null) : {}; + for (var i = 0; i < source.length; ++i) { + if (typeof source[i] !== 'undefined') { + obj[i] = source[i]; + } + } + + return obj; +}; + +var merge = function merge(target, source, options) { + if (!source) { + return target; + } + + if (typeof source !== 'object') { + if (isArray(target)) { + target.push(source); + } else if (target && typeof target === 'object') { + if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) { + target[source] = true; + } + } else { + return [target, source]; + } + + return target; + } + + if (!target || typeof target !== 'object') { + return [target].concat(source); + } + + var mergeTarget = target; + if (isArray(target) && !isArray(source)) { + mergeTarget = arrayToObject(target, options); + } + + if (isArray(target) && isArray(source)) { + source.forEach(function (item, i) { + if (has.call(target, i)) { + var targetItem = target[i]; + if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { + target[i] = merge(targetItem, item, options); + } else { + target.push(item); + } + } else { + target[i] = item; + } + }); + return target; + } + + return Object.keys(source).reduce(function (acc, key) { + var value = source[key]; + + if (has.call(acc, key)) { + acc[key] = merge(acc[key], value, options); + } else { + acc[key] = value; + } + return acc; + }, mergeTarget); +}; + +var assign = function assignSingleSource(target, source) { + return Object.keys(source).reduce(function (acc, key) { + acc[key] = source[key]; + return acc; + }, target); +}; + +var decode = function (str, decoder, charset) { + var strWithoutPlus = str.replace(/\+/g, ' '); + if (charset === 'iso-8859-1') { + // unescape never throws, no try...catch needed: + return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape); + } + // utf-8 + try { + return decodeURIComponent(strWithoutPlus); + } catch (e) { + return strWithoutPlus; + } +}; + +var encode = function encode(str, defaultEncoder, charset) { + // This code was originally written by Brian White (mscdex) for the io.js core querystring library. + // It has been adapted here for stricter adherence to RFC 3986 + if (str.length === 0) { + return str; + } + + var string = typeof str === 'string' ? str : String(str); + + if (charset === 'iso-8859-1') { + return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) { + return '%26%23' + parseInt($0.slice(2), 16) + '%3B'; + }); + } + + var out = ''; + for (var i = 0; i < string.length; ++i) { + var c = string.charCodeAt(i); + + if ( + c === 0x2D // - + || c === 0x2E // . + || c === 0x5F // _ + || c === 0x7E // ~ + || (c >= 0x30 && c <= 0x39) // 0-9 + || (c >= 0x41 && c <= 0x5A) // a-z + || (c >= 0x61 && c <= 0x7A) // A-Z + ) { + out += string.charAt(i); + continue; + } + + if (c < 0x80) { + out = out + hexTable[c]; + continue; + } + + if (c < 0x800) { + out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + if (c < 0xD800 || c >= 0xE000) { + out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + i += 1; + c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); + out += hexTable[0xF0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3F)] + + hexTable[0x80 | ((c >> 6) & 0x3F)] + + hexTable[0x80 | (c & 0x3F)]; + } + + return out; +}; + +var compact = function compact(value) { + var queue = [{ obj: { o: value }, prop: 'o' }]; + var refs = []; + + for (var i = 0; i < queue.length; ++i) { + var item = queue[i]; + var obj = item.obj[item.prop]; + + var keys = Object.keys(obj); + for (var j = 0; j < keys.length; ++j) { + var key = keys[j]; + var val = obj[key]; + if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { + queue.push({ obj: obj, prop: key }); + refs.push(val); + } + } + } + + compactQueue(queue); + + return value; +}; + +var isRegExp = function isRegExp(obj) { + return Object.prototype.toString.call(obj) === '[object RegExp]'; +}; + +var isBuffer = function isBuffer(obj) { + if (!obj || typeof obj !== 'object') { + return false; + } + + return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); +}; + +var combine = function combine(a, b) { + return [].concat(a, b); +}; + +module.exports = { + arrayToObject: arrayToObject, + assign: assign, + combine: combine, + compact: compact, + decode: decode, + encode: encode, + isBuffer: isBuffer, + isRegExp: isRegExp, + merge: merge +}; diff --git a/TaskServer/node_modules/ccxt/js/stex.js b/TaskServer/node_modules/ccxt/js/stex.js new file mode 100644 index 000000000..b70f504fd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/stex.js @@ -0,0 +1,1795 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ArgumentsRequired, AuthenticationError, ExchangeError, InsufficientFunds, OrderNotFound, PermissionDenied, BadRequest, DDoSProtection } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class stex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'stex', + 'name': 'STEX', // formerly known as stocks.exchange + 'countries': [ 'EE' ], // Estonia + 'rateLimit': 500, // https://help.stex.com/en/articles/2815043-api-3-rate-limits + 'certified': false, + // new metainfo interface + 'has': { + 'CORS': false, + 'createMarketOrder': false, // limit orders only + 'fetchCurrencies': true, + 'fetchMarkets': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchOrderBook': true, + 'fetchOHLCV': true, + 'fetchBalance': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchMyTrades': true, + 'fetchOrderTrades': true, + 'fetchDepositAddress': true, + 'createDepositAddress': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'withdraw': true, + 'fetchFundingFees': true, + }, + 'version': 'v3', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/69680782-03fd0b80-10bd-11ea-909e-7f603500e9cc.jpg', + 'api': 'https://api3.stex.com', + 'www': 'https://www.stex.com', + 'doc': [ + 'https://help.stex.com/en/collections/1593608-api-v3-documentation', + ], + 'fees': 'https://app.stex.com/en/pairs-specification', + 'referral': 'https://app.stex.com?ref=36416021', + }, + 'requiredCredentials': { + 'apiKey': false, + 'secret': false, + 'token': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '30m': '30', + '1h': '60', + '4h': '240', + '12h': '720', + '1d': '1D', // default + }, + 'api': { + 'public': { + 'get': [ + 'currencies', // Available Currencies + 'currencies/{currencyId}', // Get currency info + 'markets', // Available markets + 'pairs-groups', // Available currency pairs groups (as displayed at stex trading page) + 'currency_pairs/list/{code}', // Available currency pairs + 'currency_pairs/group/{currencyPairGroupId}', // Available currency pairs for a given group + 'currency_pairs/{currencyPairId}', // Get currency pair information + 'ticker', // Tickers list for all currency pairs + 'ticker/{currencyPairId}', // Ticker for currency pair + 'trades/{currencyPairId}', // Trades for given currency pair + 'orderbook/{currencyPairId}', // Orderbook for given currency pair + 'chart/{currencyPairId}/{candlesType}', // A list of candles for given currency pair + 'deposit-statuses', // Available Deposit Statuses + 'deposit-statuses/{statusId}', // Get deposit status info + 'withdrawal-statuses', // Available Withdrawal Statuses + 'withdrawal-statuses/{statusId}', // Get status info + 'ping', // Test API is working and get server time + 'mobile-versions', // Shows the official mobile applications data + ], + }, + 'trading': { + 'get': [ + 'fees/{currencyPairId}', // Returns the user's fees for a given currency pair + 'orders', // List your currently open orders + 'orders/{currencyPairId}', // List your currently open orders for given currency pair + 'order/{orderId}', // Get a single order + ], + 'post': [ + 'orders/{currencyPairId}', // Create new order and put it to the orders processing queue + ], + 'delete': [ + 'orders', // Delete all active orders + 'orders/{currencyPairId}', // Delete active orders for given currency pair + 'order/{orderId}', // Cancel order + ], + }, + 'reports': { + 'get': [ + 'orders', // Get past orders + 'orders/{orderId}', // Get specified order details + 'trades/{currencyPairId}', // Get a list of user trades according to request parameters + 'background/{listMode}', // Get reports list for category + 'background/{id}', // Get some report info + 'background/download/{id}', // Get file by id + ], + 'post': [ + 'background/create', // Create new report + ], + 'delete': [ + 'background/{id}', // Remove report by id + ], + }, + 'profile': { + 'get': [ + 'info', // Account information + 'wallets', // Get a list of user wallets + 'wallets/{walletId}', // Single wallet information + 'wallets/address/{walletId}', // Get deposit address for given wallet + 'deposits', // Get a list of deposits made by user + 'deposits/{id}', // Get deposit by id + 'withdrawals', // Get a list of withdrawals made by user + 'withdrawals/{id}', // Get withdrawal by id + 'notifications', // Get notifications + 'favorite/currency_pairs', // Get favorite currency pairs + 'token-scopes', // Get current token scopes + ], + 'post': [ + 'wallets/burn/{walletId}', // Burns the given wallet + 'wallets/{currencyId}', // Create a wallet for given currency + 'wallets/address/{walletId}', // Create new deposit address + 'withdraw', // Create withdrawal request + 'referral/program', // Create referral program + 'referral/insert/{code}', // Insert referral code + 'referral/bonus_transfer/{currencyId}', // Transfer referral bonuses balance to main balance for given currency + ], + 'put': [ + 'profile/favorite/currency_pairs/set', // Set favorite currency pairs + ], + 'delete': [ + 'profile/withdraw/{withdrawalId}', // Cancel unconfirmed withdrawal + ], + }, + 'verification': { + 'get': [ + 'verification/countries', // Countries list, beta + 'verification/stex', // Get information about your KYC, beta + ], + 'post': [ + 'verification/stex', // Update information regarding of your KYC verification, beta + ], + }, + 'settings': { + 'get': [ + 'notifications/{event}', // User event notification settings + 'notifications', // User events notification settings + ], + 'put': [ + 'notifications', // Set notification settings + 'notifications/set', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.002, + 'maker': 0.002, + }, + }, + 'options': { + 'parseOrderToPrecision': false, + }, + 'exceptions': { + 'exact': { + // {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}} + // {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}} + 'Wrong parameters': BadRequest, + 'Unauthenticated.': AuthenticationError, // {"message":"Unauthenticated."} + 'Server Error': ExchangeError, // { "message": "Server Error" } + 'This feature is only enabled for users verifies by Cryptonomica': PermissionDenied, // {"success":false,"message":"This feature is only enabled for users verifies by Cryptonomica"} + 'Too Many Attempts.': DDoSProtection, // { "message": "Too Many Attempts." } + }, + 'broad': { + 'Not enough': InsufficientFunds, // {"success":false,"message":"Not enough ETH"} + }, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // "success":true, + // "data":[ + // { + // "id":1, + // "code":"BTC", + // "name":"Bitcoin", + // "active":true, + // "delisted":false, + // "precision":8, + // "minimum_tx_confirmations":1, + // "minimum_withdrawal_amount":"0.00200000", + // "minimum_deposit_amount":"0.00000000", + // "deposit_fee_currency_id":1, + // "deposit_fee_currency_code":"BTC", + // "deposit_fee_const":"0.00000000", + // "deposit_fee_percent":"0.00000000", + // "withdrawal_fee_currency_id":1, + // "withdrawal_fee_currency_code":"BTC", + // "withdrawal_fee_const":"0.00100000", + // "withdrawal_fee_percent":"0.00000000", + // "block_explorer_url":"https:\/\/blockchain.info\/tx\/", + // "protocol_specific_settings":null + // }, + // ] + // } + // + const result = {}; + const currencies = this.safeValue (response, 'data', []); + for (let i = 0; i < currencies.length; i++) { + const currency = currencies[i]; + const id = this.safeString (currency, 'id'); + const numericId = this.safeInteger (currency, 'id'); + // todo: will need to rethink the fees + // to add support for multiple withdrawal/deposit methods and + // differentiated fees for each particular method + const code = this.safeCurrencyCode (this.safeString (currency, 'code')); + const precision = this.safeInteger (currency, 'precision'); + const fee = this.safeFloat (currency, 'withdrawal_fee_const'); // todo: redesign + const active = this.safeValue (currency, 'active', true); + result[code] = { + 'id': id, + 'numericId': numericId, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': this.safeString (currency, 'name'), + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'amount': { 'min': Math.pow (10, -precision), 'max': undefined }, + 'price': { 'min': Math.pow (10, -precision), 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + 'deposit': { + 'min': this.safeFloat (currency, 'minimum_deposit_amount'), + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minimum_withdrawal_amount'), + 'max': undefined, + }, + }, + }; + } + return result; + } + + async fetchMarkets (params = {}) { + const request = { + 'code': 'ALL', + }; + const response = await this.publicGetCurrencyPairsListCode (this.extend (request, params)); + // + // { + // "success":true, + // "data":[ + // { + // "id":935, + // "currency_id":662, + // "currency_code":"ABET", + // "currency_name":"Altbet", + // "market_currency_id":1, + // "market_code":"BTC", + // "market_name":"Bitcoin", + // "min_order_amount":"0.00000010", + // "min_buy_price":"0.00000001", + // "min_sell_price":"0.00000001", + // "buy_fee_percent":"0.20000000", + // "sell_fee_percent":"0.20000000", + // "active":true, + // "delisted":false, + // "pair_message":"", + // "currency_precision":8, + // "market_precision":8, + // "symbol":"ABET_BTC", + // "group_name":"BTC", + // "group_id":1 + // } + // ] + // } + // + const result = []; + const markets = this.safeValue (response, 'data', []); + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const numericId = this.safeInteger (market, 'id'); + const baseId = this.safeString (market, 'currency_id'); + const quoteId = this.safeString (market, 'market_currency_id'); + const baseNumericId = this.safeInteger (market, 'currency_id'); + const quoteNumericId = this.safeInteger (market, 'market_currency_id'); + const base = this.safeCurrencyCode (this.safeString (market, 'currency_code')); + const quote = this.safeCurrencyCode (this.safeString (market, 'market_code')); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'currency_precision'), + 'price': this.safeInteger (market, 'market_precision'), + }; + const active = this.safeValue (market, 'active'); + const minBuyPrice = this.safeFloat (market, 'min_buy_price'); + const minSellPrice = this.safeFloat (market, 'min_sell_price'); + const minPrice = Math.max (minBuyPrice, minSellPrice); + const buyFee = this.safeFloat (market, 'buy_fee_percent') / 100; + const sellFee = this.safeFloat (market, 'sell_fee_percent') / 100; + const fee = Math.max (buyFee, sellFee); + result.push ({ + 'id': id, + 'numericId': numericId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'baseNumericId': baseNumericId, + 'quoteNumericId': quoteNumericId, + 'info': market, + 'active': active, + 'maker': fee, + 'taker': fee, + 'precision': precision, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'min_order_amount'), + 'max': undefined, + }, + 'price': { 'min': minPrice, 'max': undefined }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + }; + const response = await this.publicGetTickerCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 2, + // "amount_multiplier": 1, + // "currency_code": "ETH", + // "market_code": "BTC", + // "currency_name": "Ethereum", + // "market_name": "Bitcoin", + // "symbol": "ETH_BTC", + // "group_name": "BTC", + // "group_id": 1, + // "ask": "0.02069998", + // "bid": "0.02028622", + // "last": "0.02049224", + // "open": "0.02059605", + // "low": "0.01977744", + // "high": "0.02097005", + // "volume": "480.43248971", + // "volumeQuote": "23491.29826130", + // "count": "7384", + // "fiatsRate": { + // "USD": 7230.86, + // "EUR": 6590.79, + // "UAH": 173402, + // "AUD": 10595.51, + // "IDR": 101568085, + // "CNY": 50752, + // "KRW": 8452295, + // "JPY": 784607, + // "VND": 167315119, + // "INR": 517596, + // "GBP": 5607.25, + // "CAD": 9602.63, + // "BRL": 30472, + // "RUB": 460718 + // }, + // "timestamp": 1574698235601 + // } + // } + // + const ticker = this.safeValue (response, 'data', {}); + return this.parseTicker (ticker, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + }; + if (limit !== undefined) { + request['limit_bids'] = limit; // returns all if set to 0, default 100 + request['limit_asks'] = limit; // returns all if set to 0, default 100 + } + const response = await this.publicGetOrderbookCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "ask": [ + // { "currency_pair_id": 2, "amount": "2.17865373", "price": "0.02062917", "amount2": "0.04494382", "count": 1, "cumulative_amount": 2.17865373 }, + // { "currency_pair_id": 2, "amount": "2.27521743", "price": "0.02062918", "amount2": "0.04693587", "count": 1, "cumulative_amount": 4.45387116 }, + // { "currency_pair_id": 2, "amount": "1.26980049", "price": "0.02063170", "amount2": "0.02619814", "count": 1, "cumulative_amount": 5.72367165 }, + // ], + // "bid": [ + // { "currency_pair_id": 2, "amount": "0.00978005", "price": "0.02057000", "amount2": "0.00020118", "count": 1, "cumulative_amount": 0.00978005 }, + // { "currency_pair_id": 2, "amount": "0.00500000", "price": "0.02056000", "amount2": "0.00010280", "count": 1, "cumulative_amount": 0.01478005 }, + // { "currency_pair_id": 2, "amount": "0.77679882", "price": "0.02054001", "amount2": "0.01595546", "count": 1, "cumulative_amount": 0.79157887 }, + // ], + // "ask_total_amount": 2555.749174609999, + // "bid_total_amount": 29.180037330000005 + // } + // } + // + const orderbook = this.safeValue (response, 'data', {}); + return this.parseOrderBook (orderbook, undefined, 'bid', 'ask', 'price', 'amount'); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "id": 2, + // "amount_multiplier": 1, + // "currency_code": "ETH", + // "market_code": "BTC", + // "currency_name": "Ethereum", + // "market_name": "Bitcoin", + // "symbol": "ETH_BTC", + // "group_name": "BTC", + // "group_id": 1, + // "ask": "0.02069998", + // "bid": "0.02028622", + // "last": "0.02049224", + // "open": "0.02059605", + // "low": "0.01977744", + // "high": "0.02097005", + // "volume": "480.43248971", + // "volumeQuote": "23491.29826130", + // "count": "7384", + // "fiatsRate": { + // "USD": 7230.86, + // "EUR": 6590.79, + // "UAH": 173402, + // "AUD": 10595.51, + // "IDR": 101568085, + // "CNY": 50752, + // "KRW": 8452295, + // "JPY": 784607, + // "VND": 167315119, + // "INR": 517596, + // "GBP": 5607.25, + // "CAD": 9602.63, + // "BRL": 30472, + // "RUB": 460718 + // }, + // "timestamp": 1574698235601 + // } + // + const timestamp = this.safeInteger (ticker, 'timestamp'); + let symbol = undefined; + let marketId = this.safeString (ticker, 'id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + marketId = this.safeString (ticker, 'symbol'); + if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open'); + let change = undefined; + let percentage = undefined; + if (last !== undefined) { + if (open !== undefined) { + change = last - open; + percentage = ((100 / open) * last) - 100; + } + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, // previous day close + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volumeQuote'), + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + parseTickers (tickers, symbols = undefined) { + const result = []; + for (let i = 0; i < tickers.length; i++) { + result.push (this.parseTicker (tickers[i])); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + // + // { + // "success":true, + // "data":[ + // { + // "id":262, + // "amount_multiplier":1, + // "currency_code":"ARDR", + // "market_code":"BTC", + // "currency_name":"ARDOR", + // "market_name":"Bitcoin", + // "symbol":"ARDR_BTC", + // "group_name":"BTC", + // "group_id":1, + // "ask":"0.00000630", + // "bid":"0.00000613", + // "last":"0.00000617", + // "open":"0.00000620", + // "low":"0.00000614", + // "high":"0.00000630", + // "volume":"30.37795305", + // "volumeQuote":"4911487.01996544", + // "count":"710", + // "fiatsRate":{ + // "USD":7230.86, + // "EUR":6590.79, + // "UAH":173402, + // "AUD":10744.52, + // "IDR":101568085, + // "CNY":50752, + // "KRW":8452295, + // "JPY":784607, + // "VND":167315119, + // "INR":517596, + // "GBP":5607.25, + // "CAD":9602.63, + // "BRL":30472, + // "RUB":467358 + // }, + // "timestamp":1574698617304, + // "group_position":1 + // }, + // ] + // } + // + const tickers = this.safeValue (response, 'data', []); + return this.parseTickers (tickers, symbols); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + // + // { + // "time": 1566086400000, + // "close": 0.01895, + // "open": 0.01812427, + // "high": 0.0191588, + // "low": 0.01807001, + // "volume": 2588.597813750006 + // } + // + return [ + this.safeInteger (ohlcv, 'time'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volume'), + ]; + } + + async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + 'candlesType': this.timeframes[timeframe], // default 1d + // 'timeStart': 1574709092, // unix timestamp in seconds, required + // 'timeEnd': 1574709092, // unix timestamp in seconds, required + // 'limit': 100, // default 100, optional + // 'offset' 100, // optional, pagination within timerange + }; + if (limit === undefined) { + limit = 100; + } else { + request['limit'] = limit; + } + const duration = this.parseTimeframe (timeframe); + const timerange = limit * duration; + if (since === undefined) { + request['timeEnd'] = this.seconds (); + request['timeStart'] = request['timeEnd'] - timerange; + } else { + request['timeStart'] = parseInt (since / 1000); + request['timeEnd'] = this.sum (request['timeStart'], timerange); + } + const response = await this.publicGetChartCurrencyPairIdCandlesType (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "time": 1566086400000, + // "close": 0.01895, + // "open": 0.01812427, + // "high": 0.0191588, + // "low": 0.01807001, + // "volume": 2588.597813750006 + // }, + // ] + // } + // + const ohlcvs = this.safeValue (response, 'data', []); + return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // public fetchTrades + // + // { + // "id": 35989317, + // "price": "0.02033813", + // "amount": "3.60000000", + // "type": "BUY", + // "timestamp": "1574713503" + // } + // + // private fetchMyTrades, fetchClosedOrder, fetchOrderTrades + // + // { + // "id": 658745, + // "buy_order_id": 6587453, + // "sell_order_id": 6587459, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // + const id = this.safeString (trade, 'id'); + const timestamp = this.safeTimestamp (trade, 'timestamp'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if ((price !== undefined) && (amount !== undefined)) { + cost = price * amount; + } + let symbol = undefined; + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const side = this.safeStringLower2 (trade, 'type', 'trade_type'); + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': undefined, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + // 'sort': 'ASC', // ASC or DESC, default DESC + // 'from': 1574709092, // unix timestamp, optional + // 'till': 1574709092, // unix timestamp, optional + // 'limit': 100, // default 100, optional + // 'offset': 100, // optional + }; + if (limit !== undefined) { + request['limit'] = limit; // currently limited to 100 or fewer + } + if (since !== undefined) { + request['sort'] = 'ASC'; // needed to make the from param work + request['from'] = parseInt (since / 1000); + } + const response = await this.publicGetTradesCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 35989317, + // "price": "0.02033813", + // "amount": "3.60000000", + // "type": "BUY", + // "timestamp": "1574713503" + // }, + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + // await this.loadAccounts (); + const response = await this.profileGetWallets (params); + // + // { + // "success": true, + // "data": [ + // { + // "id": null, + // "currency_id": 665, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "currency_code": "ORM", + // "currency_name": "Orium", + // "currency_type_id": 5, + // "balance": "0", + // "frozen_balance": "0", + // "bonus_balance": "0", + // "total_balance": "0", + // "protocol_specific_settings": null, + // "rates": { "BTC": "0.00000000020", "USD": "0.00000147" }, + // }, + // { + // "id": null, + // "currency_id": 272, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "currency_code": "USDT", + // "currency_name": "TetherUSD", + // "currency_type_id": 23, + // "balance": "0", + // "frozen_balance": "0", + // "bonus_balance": "0", + // "total_balance": "0", + // "protocol_specific_settings": [ + // { "protocol_name": "OMNI", "protocol_id": 10, "active": true, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 10, "withdrawal_fee_percent": 0, "block_explorer_url": "https://omniexplorer.info/search/" }, + // { "protocol_name": "ERC20", "protocol_id": 5, "active": true, "withdrawal_fee_const": 1.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://etherscan.io/tx/" }, + // { "protocol_name": "TRON", "protocol_id": 24, "active": true, "withdrawal_fee_currency_id": 272, "withdrawal_fee_const": 0.2, "withdrawal_fee_percent": 0, "block_explorer_url": "https://tronscan.org/#/transaction/" } + // ], + // "rates": { "BTC": "0.00013893", "USD": "1" }, + // }, + // ] + // } + // + const result = { 'info': response }; + const balances = this.safeValue (response, 'data', []); + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const code = this.safeCurrencyCode (this.safeString (balance, 'currency_id')); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'frozen_balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + parseOrderStatus (status) { + const statuses = { + 'PROCESSING': 'open', + 'PENDING': 'open', + 'PARTIAL': 'open', + 'FINISHED': 'closed', + 'CANCELLED': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // createOrder, fetchOpenOrders, fetchClosedOrders, cancelOrder, fetchOrder, fetchClosedOrder + // + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", // missing in fetchClosedOrder + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // // fetchClosedOrder only + // "trades": [ + // { + // "id": 658745, + // "buy_order_id": 658745, + // "sell_order_id": 828680665, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // ], + // // fetchClosedOrder only + // "fees": [ + // { + // "id": 1234567, + // "currency_id": 1, + // "amount": 0.00025, + // "timestamp": "1548149238" + // } + // ] + // } + // + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + let symbol = undefined; + let marketId = this.safeString (order, 'currency_pair_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } else { + marketId = this.safeString (order, 'currency_pair_name'); + if (marketId !== undefined) { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.safeTimestamp (order, 'timestamp'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'initial_amount'); + const filled = this.safeFloat (order, 'processed_amount'); + let remaining = undefined; + let cost = undefined; + if (filled !== undefined) { + if (amount !== undefined) { + remaining = amount - filled; + if (this.options['parseOrderToPrecision']) { + remaining = parseFloat (this.amountToPrecision (symbol, remaining)); + } + remaining = Math.max (remaining, 0.0); + } + if (price !== undefined) { + if (cost === undefined) { + cost = price * filled; + } + } + } + let type = this.safeString (order, 'original_type'); + if ((type === 'BUY') || (type === 'SELL')) { + type = undefined; + } + const side = this.safeStringLower (order, 'type'); + const rawTrades = this.safeValue (order, 'trades'); + let trades = undefined; + if (rawTrades !== undefined) { + trades = this.parseTrades (rawTrades, market, undefined, undefined, { + 'symbol': symbol, + 'order': id, + }); + } + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'trades': trades, + }; + const fees = this.safeValue (order, 'fees'); + if (fees === undefined) { + result['fee'] = undefined; + } else { + const numFees = fees.length; + if (numFees > 0) { + result['fees'] = []; + for (let i = 0; i < fees.length; i++) { + const feeCost = this.safeFloat (fees[i], 'amount'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (fees[i], 'currency_id'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + result['fees'].push ({ + 'cost': feeCost, + 'currency': feeCurrencyCode, + }); + } + } + } else { + result['fee'] = undefined; + } + } + return result; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' createOrder allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + if (type === 'limit') { + type = side; + } + const request = { + 'currencyPairId': market['id'], + 'type': type.toUpperCase (), // 'BUY', 'SELL', 'STOP_LIMIT_BUY', 'STOP_LIMIT_SELL' + 'amount': parseFloat (this.amountToPrecision (symbol, amount)), // required + 'price': parseFloat (this.priceToPrecision (symbol, price)), // required + // 'trigger_price': 123.45 // required for STOP_LIMIT_BUY or STOP_LIMIT_SELL + }; + const response = await this.tradingPostOrdersCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseOrder (data, market); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.tradingGetOrderOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseOrder (data, market); + } + + async fetchClosedOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.reportsGetOrdersOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 5478965, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.00013800", + // "initial_amount": "1.00000000", + // "type": "BUY", + // "created": "2019-01-22 09:27:17", + // "timestamp": 1548149237, + // "status": "FINISHED", + // "trades": [ + // { + // "id": 658745, + // "buy_order_id": 6587453, + // "sell_order_id": 6587459, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // ], + // "fees": [ + // { + // "id": 1234567, + // "currency_id": 1, + // "amount": 0.00025, + // "timestamp": "1548149238" + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseOrder (data, market); + } + + async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) { + const order = await this.fetchClosedOrder (id, symbol, params); + return order['trades']; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + let method = 'tradingGetOrders'; + const request = { + // 'limit': 100, // default 100 + // 'offset': 100, + }; + if (symbol !== undefined) { + method = 'tradingGetOrdersCurrencyPairId'; + market = this.market (symbol); + request['currencyPairId'] = market['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + return this.parseOrders (data, market, since, limit); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderId': id, + }; + const response = await this.tradingDeleteOrderOrderId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "put_into_processing_queue": [ + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // ], + // "not_put_into_processing_queue": [ + // { + // "id": 828680665, + // "currency_pair_id": 1, + // "currency_pair_name": "NXT_BTC", + // "price": "0.011384", + // "trigger_price": 0.011385, + // "initial_amount": "13.942", + // "processed_amount": "3.724", + // "type": "SELL", + // "original_type": "STOP_LIMIT_SELL", + // "created": "2019-01-17 10:14:48", + // "timestamp": "1547720088", + // "status": "PARTIAL" + // } + // ], + // "message": "string" + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const acceptedOrders = this.safeValue (data, 'put_into_processing_queue', []); + const rejectedOrders = this.safeValue (data, 'not_put_into_processing_queue', []); + const numAcceptedOrders = acceptedOrders.length; + const numRejectedOrders = rejectedOrders.length; + if (numAcceptedOrders < 1) { + if (numRejectedOrders < 1) { + throw new OrderNotFound (this.id + ' cancelOrder received an empty response: ' + this.json (response)); + } else { + return this.parseOrder (rejectedOrders[0]); + } + } else { + if (numRejectedOrders < 1) { + return this.parseOrder (acceptedOrders[0]); + } else { + throw new OrderNotFound (this.id + ' cancelOrder received an empty response: ' + this.json (response)); + } + } + } + + async cancelAllOrders (symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let method = 'tradingDeleteOrders'; + if (symbol !== undefined) { + const market = this.market (symbol); + request['currencyPairId'] = market['id']; + method = 'tradingDeleteOrdersCurrencyPairId'; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "success":true, + // "data":{ + // "put_into_processing_queue":[], + // "not_put_into_processing_queue":[], + // "message":"Orders operations are handled in processing queue, therefore cancelling is not immediate." + // } + // } + // + return response; + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currencyPairId': market['id'], + // 'timeStart': '2019-11-26T19:54:55.901Z', // datetime in iso format + // 'timeEnd': '2019-11-26T19:54:55.901Z', // datetime in iso format + // 'limit': 100, // default 100 + // 'offset': 100, + }; + if (since !== undefined) { + request['timeStart'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.reportsGetTradesCurrencyPairId (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 658745, + // "buy_order_id": 6587453, + // "sell_order_id": 6587459, + // "price": 0.012285, + // "amount": 6.35, + // "trade_type": "SELL", + // "timestamp": "1538737692" + // } + // ] + // } + // + const trades = this.safeValue (response, 'data', []); + return this.parseTrades (trades, market, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currencyId': currency['id'], + // Default value is the value that represents legacy protocol. + // In case of USDT it is 10 as Tether OMNI was the default previously. + // The list of protocols can be obtained from the /public/currencies/{currencyId} + // 'protocol_id': 10, + }; + const response = await this.profilePostWalletsCurrencyId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 45875, + // "currency_id": 1, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "code": "BTC", + // "balance": "0.198752", + // "frozen_balance": "1.5784", + // "bonus_balance": "0.000", + // "deposit_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // }, + // "multi_deposit_addresses": [ + // { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // ], + // "withdrawal_additional_field_name": "Payment ID (optional)", + // "rates": { "BTC": 0.000001 }, + // "protocol_specific_settings": [ + // { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "active": true, + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_const": 0.002, + // "withdrawal_fee_percent": 0, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', {}); + const depositAddress = this.safeValue (data, 'deposit_address', {}); + const address = this.safeString (depositAddress, 'address'); + const tag = this.safeString (depositAddress, 'additional_address_parameter'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const balance = await this.fetchBalance (); + const wallets = this.safeValue (balance['info'], 'data', []); + const walletsByCurrencyId = this.indexBy (wallets, 'currency_id'); + const currency = this.currency (code); + const wallet = this.safeValue (walletsByCurrencyId, currency['id']); + if (wallet === undefined) { + throw new ExchangeError (this.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first'); + } + const walletId = this.safeInteger (wallet, 'id'); + if (walletId === undefined) { + throw new ExchangeError (this.id + ' fetchDepositAddress() could not find the wallet id for currency code ' + code + ', try to call createDepositAddress() first'); + } + const request = { + 'walletId': walletId, + }; + const response = await this.profileGetWalletsWalletId (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 45875, + // "currency_id": 1, + // "delisted": false, + // "disabled": false, + // "disable_deposits": false, + // "code": "BTC", + // "balance": "0.198752", + // "frozen_balance": "1.5784", + // "bonus_balance": "0.000", + // "deposit_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // }, + // "multi_deposit_addresses": [ + // { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // ], + // "withdrawal_additional_field_name": "Payment ID (optional)", + // "rates": { "BTC": 0.000001 }, + // "protocol_specific_settings": [ + // { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "active": true, + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_const": 0.002, + // "withdrawal_fee_percent": 0, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // ] + // } + // } + // + const data = this.safeValue (response, 'data', []); + const depositAddress = this.safeValue (data, 'deposit_address', {}); + const address = this.safeString (depositAddress, 'address'); + const tag = this.safeString (depositAddress, 'additional_address_parameter'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + api + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + headers = { + 'Authorization': 'Bearer ' + this.token, + }; + if (method === 'GET' || method === 'DELETE') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + body = this.json (query); + if (Object.keys (query).length) { + headers['Content-Type'] = 'application/json'; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + parseTransactionStatus (status) { + const statuses = { + 'processing': 'pending', + 'checking by system': 'pending', + 'hodl': 'pending', + 'amount too low': 'failed', + 'not confirmed': 'pending', + 'cancelled by User': 'canceled', + 'approved': 'pending', + 'finished': 'ok', + 'withdrawal error': 'failed', + 'deposit error': 'failed', + 'cancelled by admin': 'canceled', + 'awaiting': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "id": 123654789, + // "currency_id": 1, + // "currency_code": "BTC", + // "deposit_fee_currency_id": 1, + // "deposit_fee_currency_code": "BTC", + // "amount": 0.25, + // "fee": 0.00025, + // "txid": "qwertyuhgfdsasdfgh", + // "protocol_id": 0, + // "deposit_status_id": 1, + // "status": "PROCESSING", + // "status_color": "#BC3D51", + // "created_at": "2018-11-28 12:32:08", + // "timestamp": "1543409389", + // "confirmations": "1 of 2" + // } + // + // fetchWithdrawals + // + // { + // "id": 65899, + // "amount": "0.00600000", + // "currency_id": 1, + // "currency_code": "BTC", + // "fee": "0.00400000", + // "fee_currency_id": 1, + // "fee_currency_code": "BTC", + // "withdrawal_status_id": 1, + // "status": "Not Confirmed", + // "status_color": "#BC3D51", + // "created_at": "2019-01-21 09:36:05", + // "created_ts": "1548063365", + // "updated_at": "2019-01-21 09:36:05", + // "updated_ts": "1548063365", + // "txid": null, + // "protocol_id": 0, + // "withdrawal_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // } + // + const id = this.safeString (transaction, 'id'); + const withdrawalAddress = this.safeValue (transaction, 'withdrawal_address', {}); + const address = this.safeString (withdrawalAddress, 'address'); + const tag = this.safeString (withdrawalAddress, 'additional_address_parameter'); + const currencyId = this.safeString (transaction, 'currency_id'); + let code = undefined; + if (currencyId in this.currencies_by_id) { + currency = this.currencies_by_id[currencyId]; + } else { + code = this.commonCurrencyCode (this.safeString (transaction, 'currency_code')); + } + if ((code === undefined) && (currency !== undefined)) { + code = currency['code']; + } + const type = ('deposit_status_id' in transaction) ? 'deposit' : 'withdrawal'; + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeStringLower (transaction, 'status')); + const timestamp = this.safeTimestamp2 (transaction, 'timestamp', 'created_ts'); + const updated = this.safeTimestamp (transaction, 'updated_ts'); + const txid = this.safeString (transaction, 'txid'); + let fee = undefined; + const feeCost = this.safeFloat (transaction, 'fee'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (transaction, 'fee_currency_id', 'deposit_fee_currency_id'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + return { + 'info': transaction, + 'id': id, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'addressFrom': undefined, + 'address': address, + 'addressTo': address, + 'tagFrom': undefined, + 'tag': tag, + 'tagTo': tag, + 'type': type, + 'amount': amount, + 'currency': code, + 'status': status, + 'updated': updated, + 'fee': fee, + }; + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currencyId'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['timeStart'] = since; + } + const response = await this.profileGetDeposits (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 123654789, + // "currency_id": 1, + // "currency_code": "BTC", + // "deposit_fee_currency_id": 1, + // "deposit_fee_currency_code": "BTC", + // "amount": 0.25, + // "fee": 0.00025, + // "txid": "qwertyuhgfdsasdfgh", + // "protocol_id": 0, + // "deposit_status_id": 1, + // "status": "PROCESSING", + // "status_color": "#BC3D51", + // "created_at": "2018-11-28 12:32:08", + // "timestamp": "1543409389", + // "confirmations": "1 of 2", + // "protocol_specific_settings": { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // } + // ] + // } + // + const deposits = this.safeValue (response, 'data', []); + return this.parseTransactions (deposits, code, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let currency = undefined; + const request = {}; + if (code !== undefined) { + currency = this.currency (code); + request['currencyId'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + if (since !== undefined) { + request['timeStart'] = since; + } + const response = await this.profileGetWithdrawals (this.extend (request, params)); + // + // { + // "success": true, + // "data": [ + // { + // "id": 65899, + // "amount": "0.00600000", + // "currency_id": 1, + // "currency_code": "BTC", + // "fee": "0.00400000", + // "fee_currency_id": 1, + // "fee_currency_code": "BTC", + // "withdrawal_status_id": 1, + // "status": "Not Confirmed", + // "status_color": "#BC3D51", + // "created_at": "2019-01-21 09:36:05", + // "created_ts": "1548063365", + // "updated_at": "2019-01-21 09:36:05", + // "updated_ts": "1548063365", + // "txid": null, + // "protocol_id": 0, + // "withdrawal_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // }, + // "protocol_specific_settings": { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // } + // ] + // } + // + const withdrawals = this.safeValue (response, 'data', []); + return this.parseTransactions (withdrawals, code, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency_id': currency['id'], + 'amount': parseFloat (this.currencyToPrecision (code, amount)), + 'address': address, + // 'protocol_id': 10, // optional, to be used with multicurrency wallets like USDT + // 'additional_address_parameter': tag, // optional + }; + if (tag !== undefined) { + request['additional_address_parameter'] = tag; + } + const response = await this.profilePostWithdraw (this.extend (request, params)); + // + // { + // "success": true, + // "data": { + // "id": 65899, + // "amount": "0.00600000", + // "currency_id": 1, + // "currency_code": "BTC", + // "fee": "0.00400000", + // "fee_currency_id": 1, + // "fee_currency_code": "BTC", + // "withdrawal_status_id": 1, + // "status": "Not Confirmed", + // "status_color": "#BC3D51", + // "created_at": "2019-01-21 09:36:05", + // "created_ts": "1548063365", + // "updated_at": "2019-01-21 09:36:05", + // "updated_ts": "1548063365", + // "txid": null, + // "protocol_id": 0, + // "withdrawal_address": { + // "address": "0X12WERTYUIIJHGFVBNMJHGDFGHJ765SDFGHJ", + // "address_name": "Address", + // "additional_address_parameter": "qwertyuiopasdfghjkl", + // "additional_address_parameter_name": "Destination Tag", + // "notification": "", + // "protocol_id": 10, + // "protocol_name": "Tether OMNI", + // "supports_new_address_creation": false + // } + // } + // } + // + const data = this.safeValue (response, 'data', {}); + return this.parseTransaction (data, currency); + } + + async fetchFundingFees (codes = undefined, params = {}) { + const response = await this.publicGetCurrencies (params); + // + // { + // "success": true, + // "data": [ + // { + // "id": 1, + // "code": "BTC", + // "name": "Bitcoin", + // "active": true, + // "delisted": false, + // "precision": 8, + // "minimum_tx_confirmations": 24, + // "minimum_withdrawal_amount": "0.009", + // "minimum_deposit_amount": "0.000003", + // "deposit_fee_currency_id": 1, + // "deposit_fee_currency_code": "ETH", + // "deposit_fee_const": "0.00001", + // "deposit_fee_percent": "0", + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_currency_code": "ETH", + // "withdrawal_fee_const": "0.0015", + // "withdrawal_fee_percent": "0", + // "withdrawal_limit": "string", + // "block_explorer_url": "https://blockchain.info/tx/", + // "protocol_specific_settings": [ + // { + // "protocol_name": "Tether OMNI", + // "protocol_id": 10, + // "active": true, + // "withdrawal_fee_currency_id": 1, + // "withdrawal_fee_const": 0.002, + // "withdrawal_fee_percent": 0, + // "block_explorer_url": "https://omniexplorer.info/search/" + // } + // ] + // } + // ] + // } + // + const data = this.safeValue (response, 'data', []); + const withdrawFees = {}; + const depositFees = {}; + for (let i = 0; i < data.length; i++) { + const id = this.safeString (data[i], 'id'); + const code = this.safeCurrencyCode (id); + withdrawFees[code] = this.safeFloat (data[i], 'withdrawal_fee_const'); + depositFees[code] = this.safeFloat (data[i], 'deposit_fee_const'); + } + return { + 'withdraw': withdrawFees, + 'deposit': depositFees, + 'info': response, + }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // {"success":false,"message":"Wrong parameters","errors":{"candleType":["Invalid Candle Type!"]}} + // {"success":false,"message":"Wrong parameters","errors":{"time":["timeStart or timeEnd is less then 1"]}} + // {"success":false,"message":"Not enough ETH"} + // + const success = this.safeValue (response, 'success', false); + if (!success) { + const message = this.safeString (response, 'message'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/stronghold.js b/TaskServer/node_modules/ccxt/js/stronghold.js new file mode 100644 index 000000000..86e06d6ef --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/stronghold.js @@ -0,0 +1,787 @@ +'use strict'; + +// ---------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { InvalidNonce, AuthenticationError, AccountSuspended, InsufficientFunds, ExchangeError, ArgumentsRequired, NotSupported } = require ('./base/errors'); + +// ---------------------------------------------------------------------------- + +module.exports = class stronghold extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'stronghold', + 'name': 'Stronghold', + 'country': [ 'US' ], + 'rateLimit': 1000, + 'version': 'v1', + 'comment': 'This comment is optional', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/52160042-98c1f300-26be-11e9-90dd-da8473944c83.jpg', + 'api': { + 'public': 'https://api.stronghold.co', + 'private': 'https://api.stronghold.co', + }, + 'www': 'https://stronghold.co', + 'doc': [ + 'https://docs.stronghold.co', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'password': true, + }, + 'has': { + 'fetchMarkets': true, + 'fetchCurrencies': true, + 'fetchOrderBook': true, + 'fetchOpenOrders': true, + 'fetchTime': true, + 'fetchTrades': true, + 'fetchMyTrades': true, + 'fetchDepositAddress': false, + 'createDepositAddress': true, + 'withdraw': true, + 'fetchTicker': false, + 'fetchTickers': false, + 'fetchAccounts': true, + 'fetchTransactions': true, + }, + 'api': { + 'public': { + 'get': [ + 'utilities/time', + 'utilities/uuid', + 'venues', + 'venues/{venueId}/assets', + 'venues/{venueId}/markets', + 'venues/{venueId}/markets/{marketId}/orderbook', + 'venues/{venueId}/markets/{marketId}/trades', + ], + 'post': [ + 'venues/{venueId}/assets', + 'iam/credentials', + 'identities', + ], + 'patch': [ + 'identities', + ], + 'put': [ + 'iam/credentials/{credentialId}', + ], + 'delete': [ + 'iam/credentials/{credentialId}', + ], + }, + 'private': { + 'get': [ + 'venues', + 'venues/{venueId}/accounts', + 'venues/{venueId}/accounts/{accountId}', + 'venues/{venueId}/accounts/{accountId}/payments/{paymentId}', + 'venues/{venueId}/accounts/{accountId}/orders', + 'venues/{venueId}/accounts/{accountId}/trades', + 'venues/{venueId}/accounts/{accountId}/transactions', + ], + 'post': [ + 'venues/{venueId}/accounts', + 'venues/{venueId}/accounts/{accountId}/orders', + 'venues/{venueId}/accounts/{accountId}/deposit', + 'venues/{venueId}/accounts/{accountId}/withdrawal', + 'venues/{venueId}/accounts/{accountId}/payments', + 'venues/{venueId}/accounts/{accountId}/payments/{paymentId}/stop', + 'venues/{venueId}/custody/accounts/{accountId}/operations/{operationId}/signatures', + 'venues/{venueId}/anchor/withdrawal', + 'venues/{venueId}/testing/friendbot', + ], + 'delete': [ + 'venues/{venueId}/accounts/{accountId}/orders/{orderId}', + ], + }, + }, + 'options': { + 'accountId': undefined, + 'venueId': 'trade-public', + 'venues': { + 'trade': 'trade-public', + 'sandbox': 'sandbox-public', + }, + 'paymentMethods': { + 'ETH': 'ethereum', + 'BTC': 'bitcoin', + 'XLM': 'stellar', + 'XRP': 'ripple', + 'LTC': 'litecoin', + 'SHX': 'stellar', + }, + }, + 'exceptions': { + 'CREDENTIAL_MISSING': AuthenticationError, + 'CREDENTIAL_INVALID': AuthenticationError, + 'CREDENTIAL_REVOKED': AccountSuspended, + 'CREDENTIAL_NO_IDENTITY': AuthenticationError, + 'PASSPHRASE_INVALID': AuthenticationError, + 'SIGNATURE_INVALID': AuthenticationError, + 'TIME_INVALID': InvalidNonce, + 'BYPASS_INVALID': AuthenticationError, + 'INSUFFICIENT_FUNDS': InsufficientFunds, + }, + }); + } + + async getActiveAccount () { + if (this.options['accountId'] !== undefined) { + return this.options['accountId']; + } + await this.loadAccounts (); + const numAccounts = this.accounts.length; + if (numAccounts > 0) { + return this.accounts[0]['id']; + } + throw new ExchangeError (this.id + ' requires an accountId.'); + } + + async fetchAccounts (params = {}) { + const request = { + 'venueId': this.options['venueId'], + }; + const response = await this.privateGetVenuesVenueIdAccounts (this.extend (request, params)); + // + // [ { id: '34080200-b25a-483d-a734-255d30ba324d', + // venueSpecificId: '' } ... ] + // + return response['result']; + } + + async fetchTime (params = {}) { + const response = await this.publicGetUtilitiesTime (params); + // + // { + // "requestId": "6de8f506-ad9d-4d0d-94f3-ec4d55dfcdb9", + // "timestamp": 1536436649207281, + // "success": true, + // "statusCode": 200, + // "result": { + // "timestamp": "2018-09-08T19:57:29.207282Z" + // } + // } + // + return this.parse8601 (this.safeString (response['result'], 'timestamp')); + } + + async fetchMarkets (params = {}) { + const request = { + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdMarkets (this.extend (request, params)); + const data = response['result']; + // + // [ + // { + // id: 'SHXUSD', + // baseAssetId: 'SHX/stronghold.co', + // counterAssetId: 'USD/stronghold.co', + // minimumOrderSize: '1.0000000', + // minimumOrderIncrement: '1.0000000', + // minimumPriceIncrement: '0.00010000', + // displayDecimalsPrice: 4, + // displayDecimalsAmount: 0 + // }, + // ... + // ] + // + const result = {}; + for (let i = 0; i < data.length; i++) { + const entry = data[i]; + const marketId = entry['id']; + const baseId = this.safeString (entry, 'baseAssetId'); + const quoteId = this.safeString (entry, 'counterAssetId'); + const baseAssetId = baseId.split ('/')[0]; + const quoteAssetId = quoteId.split ('/')[0]; + const base = this.safeCurrencyCode (baseAssetId); + const quote = this.safeCurrencyCode (quoteAssetId); + const symbol = base + '/' + quote; + const limits = { + 'amount': { + 'min': this.safeFloat (entry, 'minimumOrderSize'), + 'max': undefined, + }, + }; + const precision = { + 'price': this.safeInteger (entry, 'displayDecimalsPrice'), + 'amount': this.safeInteger (entry, 'displayDecimalsAmount'), + }; + result[symbol] = { + 'symbol': symbol, + 'id': marketId, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'precision': precision, + 'info': entry, + 'limits': limits, + }; + } + return result; + } + + async fetchCurrencies (params = {}) { + const request = { + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdAssets (this.extend (request, params)); + // + // [ + // { + // id: 'XLM/native', + // alias: '', + // code: 'XLM', + // name: '', + // displayDecimalsFull: 7, + // displayDecimalsSignificant: 2, + // }, + // ... + // ] + // + const data = response['result']; + const result = {}; + const limits = { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': undefined, + 'max': undefined, + }, + }; + for (let i = 0; i < data.length; i++) { + const entry = data[i]; + const assetId = this.safeString (entry, 'id'); + const currencyId = this.safeString (entry, 'code'); + const code = this.safeCurrencyCode (currencyId); + const precision = this.safeInteger (entry, 'displayDecimalsFull'); + result[code] = { + 'code': code, + 'id': assetId, + 'precision': precision, + 'info': entry, + 'active': undefined, + 'name': undefined, + 'limits': limits, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const marketId = this.marketId (symbol); + const request = { + 'marketId': marketId, + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdMarketsMarketIdOrderbook (this.extend (request, params)); + // + // { + // marketId: 'ETHBTC', + // bids: [ + // [ '0.031500', '7.385000' ], + // ..., + // ], + // asks: [ + // [ '0.031500', '7.385000' ], + // ..., + // ], + // } + // + const data = response['result']; + const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); + return this.parseOrderBook (data, timestamp); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'marketId': market['id'], + 'venueId': this.options['venueId'], + }; + const response = await this.publicGetVenuesVenueIdMarketsMarketIdTrades (this.extend (request, params)); + // + // { + // "requestId": "4d343700-b53f-4975-afcc-732ae9d3c828", + // "timestamp": "2018-11-08T19:22:11.399543Z", + // "success": true, + // "statusCode": 200, + // "result": { + // "marketId": "", + // "trades": [ + // [ "0.9", "3.10", "sell", "2018-11-08T19:22:11.399547Z" ], + // ... + // ], + // } + // } + // + return this.parseTrades (response['result']['trades'], market, since, limit); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // [ '0.03177000', '0.0643501', 'sell', '2019-01-27T23:02:04Z' ] + // + // fetchMyTrades (private) + // + // { + // id: '9cdb109c-d035-47e2-81f8-a0c802c9c5f9', + // orderId: 'a38d8bcb-9ff5-4c52-81a0-a40196a66462', + // marketId: 'XLMUSD', + // side: 'sell', + // size: '1.0000000', + // price: '0.10440600', + // settled: true, + // maker: false, + // executedAt: '2019-02-01T18:44:21Z' + // } + // + let id = undefined; + let takerOrMaker = undefined; + let price = undefined; + let amount = undefined; + let cost = undefined; + let side = undefined; + let timestamp = undefined; + let orderId = undefined; + if (Array.isArray (trade)) { + price = parseFloat (trade[0]); + amount = parseFloat (trade[1]); + side = trade[2]; + timestamp = this.parse8601 (trade[3]); + } else { + id = this.safeString (trade, 'id'); + price = this.safeFloat (trade, 'price'); + amount = this.safeFloat (trade, 'size'); + side = this.safeString (trade, 'side'); + timestamp = this.parse8601 (this.safeString (trade, 'executedAt')); + orderId = this.safeString (trade, 'orderId'); + const marketId = this.safeString (trade, 'marketId'); + market = this.safeValue (this.markets_by_id, marketId); + const isMaker = this.safeValue (trade, 'maker'); + takerOrMaker = isMaker ? 'maker' : 'taker'; + } + if (amount !== undefined && price !== undefined) { + cost = amount * price; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'order': orderId, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': { + 'cost': undefined, + 'currency': undefined, + 'rate': undefined, + }, + }; + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " fetchTransactions requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountIdTransactions (request); + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + } + return this.parseTransactions (response['result'], currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'queued': 'pending', + 'settling': 'pending', + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // { + // "id": "6408e003-0f14-4457-9340-ba608992ad5c", + // "status": "queued", + // "direction": "outgoing", + // "amount": "98.95000000", + // "assetId": "XLM/native", + // "sourceAccount": { + // "id": "774fa8ef-600b-4636-b9ed-cd6d23421915", + // "venueSpecificId": "GC5FIBIQZTQRMJE34GYF5EKH77GEQ3OHFX3NIP5OKDIZFA6VERLZSHY6" + // }, + // "destinationAccount": { + // "id": "f72b9fb5-9607-4dd3-b31f-6ded21337056", + // "venueSpecificId": "GAOWV6CYBE7DEWSWPODXLMI5YB75VXXZJX5OYVQ2YLZH2TVA3TMMSNYW" + // } + // } + const id = this.safeString (transaction, 'id'); + const assetId = this.safeString (transaction, 'assetId'); + let code = undefined; + if (assetId !== undefined) { + const currencyId = assetId.split ('/')[0]; + code = this.safeCurrencyCode (currencyId); + } else { + if (currency !== undefined) { + code = currency['code']; + } + } + const amount = this.safeFloat (transaction, 'amount'); + const status = this.parseTransactionStatus (this.safeString (transaction, 'status')); + const feeCost = this.safeFloat (transaction, 'feeAmount'); + let feeRate = undefined; + if (feeCost !== undefined) { + feeRate = feeCost / amount; + } + const direction = this.safeString (transaction, 'direction'); + const datetime = this.safeString (transaction, 'requestedAt'); + const timestamp = this.parse8601 (datetime); + const updated = this.parse8601 (this.safeString (transaction, 'updatedAt')); + const type = (direction === 'outgoing' || direction === 'withdrawal') ? 'withdrawal' : 'deposit'; + const fee = { + 'cost': feeCost, + 'rate': feeRate, + }; + return { + 'id': id, + 'info': transaction, + 'currency': code, + 'amount': amount, + 'status': status, + 'fee': fee, + 'tag': undefined, + 'type': type, + 'updated': updated, + 'address': undefined, + 'txid': undefined, + 'timestamp': timestamp, + 'datetime': datetime, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'marketID': market['id'], + 'type': type, + 'side': side, + 'size': this.amountToPrecision (symbol, amount), + 'price': this.priceToPrecision (symbol, price), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " createOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privatePostVenuesVenueIdAccountsAccountIdOrders (request); + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'orderId': id, + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateDeleteVenuesVenueIdAccountsAccountIdOrdersOrderId (request); + return this.parseOrder (response); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountIdOrders (request); + return this.parseOrders (response['result'], market, since, limit); + } + + parseOrder (order, market = undefined) { + // { id: '178596', + // marketId: 'XLMUSD', + // side: 'buy', + // size: '1.0000000', + // sizeFilled: '0', + // price: '0.10000000', + // placedAt: '2019-02-01T19:47:52Z' } + const marketId = this.safeString (order, 'marketId'); + if (marketId !== undefined) { + market = this.safeValue (this.marketsById, marketId); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const id = this.safeString (order, 'id'); + const datetime = this.safeString (order, 'placedAt'); + const amount = this.safeFloat (order, 'size'); + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'sizeFilled'); + let cost = undefined; + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'info': order, + 'symbol': symbol, + 'datetime': datetime, + 'timestamp': this.parse8601 (datetime), + 'side': this.safeString (order, 'side'), + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'price': price, + 'cost': cost, + 'trades': [], + 'lastTradeTimestamp': undefined, + 'status': undefined, + 'type': undefined, + 'average': undefined, + }; + } + + nonce () { + return this.seconds (); + } + + setSandboxMode (enabled) { + if (enabled) { + this.options['venueId'] = this.options['venues']['sandbox']; + } else { + this.options['venueId'] = this.options['venues']['trade']; + } + } + + async fetchBalance (params = {}) { + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!('accountId' in request)) { + throw new ArgumentsRequired (this.id + " fetchBalance requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountId (request); + const balances = this.safeValue (response['result'], 'balances'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const assetId = this.safeString (balance, 'assetId'); + if (assetId !== undefined) { + const currencyId = assetId.split ('/')[0]; + const code = this.safeCurrencyCode (currencyId); + const account = {}; + account['total'] = this.safeFloat (balance, 'amount'); + account['free'] = this.safeFloat (balance, 'availableForTrade'); + result[code] = account; + } + } + return this.parseBalance (result); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " fetchMyTrades requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privateGetVenuesVenueIdAccountsAccountIdTrades (request); + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.parseTrades (response['result'], market, since, limit); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const paymentMethod = this.safeString (this.options['paymentMethods'], code); + if (paymentMethod === undefined) { + throw new NotSupported (this.id + ' createDepositAddress requires code to be BTC, ETH, or XLM'); + } + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'assetId': this.currencyId (code), + 'paymentMethod': paymentMethod, + }, params); + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " createDepositAddress requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privatePostVenuesVenueIdAccountsAccountIdDeposit (request); + // + // { + // assetId: 'BTC/stronghold.co', + // paymentMethod: 'bitcoin', + // paymentMethodInstructions: { + // deposit_address: 'mzMT9Cfw8JXVWK7rMonrpGfY9tt57ytHt4', + // reference: 'sometimes-exists', + // }, + // direction: 'deposit', + // } + // + const data = response['result']['paymentMethodInstructions']; + const address = data['deposit_address']; + const tag = this.safeString (data, 'reference'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + await this.loadMarkets (); + const paymentMethod = this.safeString (this.options['paymentMethods'], code); + if (paymentMethod === undefined) { + throw new NotSupported (this.id + ' withdraw requires code to be BTC, ETH, or XLM'); + } + const request = this.extend ({ + 'venueId': this.options['venueId'], + 'accountId': await this.getActiveAccount (), + 'assetId': this.currencyId (code), + 'amount': amount, + 'paymentMethod': paymentMethod, + 'paymentMethodDetails': { + 'withdrawal_address': address, + }, + }, params); + if (tag !== undefined) { + request['paymentMethodDetails']['reference'] = tag; + } + if (!request['accountId']) { + throw new ArgumentsRequired (this.id + " withdraw requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'."); + } + const response = await this.privatePostVenuesVenueIdAccountsAccountIdWithdrawal (request); + // + // { + // "id": "5be48892-1b6e-4431-a3cf-34b38811e82c", + // "assetId": "BTC/stronghold.co", + // "amount": "10", + // "feeAmount": "0.01", + // "paymentMethod": "bitcoin", + // "paymentMethodDetails": { + // "withdrawal_address": "1vHysJeXYV6nqhroBaGi52QWFarbJ1dmQ" + // }, + // "direction": "withdrawal", + // "status": "pending" + // } + // + const data = response['result']; + return { + 'id': this.safeString (data, 'id'), + 'info': response, + }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (!response) { + return; // fallback to base error handler by default + } + // + // { + // requestId: '3e7d17ab-b316-4721-b5aa-f7e6497eeab9', + // timestamp: '2019-01-31T21:59:06.696855Z', + // success: true, + // statusCode: 200, + // result: [] + // } + // + const errorCode = this.safeString (response, 'errorCode'); + if (errorCode in this.exceptions) { + const Exception = this.exceptions[errorCode]; + throw new Exception (this.id + ' ' + body); + } + const success = this.safeValue (response, 'success'); + if (!success) { + throw new ExchangeError (this.id + ' ' + body); + } + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + request; + if (Object.keys (query).length) { + if (method === 'GET') { + url += '?' + this.urlencode (query); + } else { + body = this.json (query); + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.nonce ().toString (); + let payload = timestamp + method + request; + if (body !== undefined) { + payload += body; + } + const secret = this.base64ToBinary (this.secret); + headers = { + 'SH-CRED-ID': this.apiKey, + 'SH-CRED-SIG': this.hmac (this.encode (payload), secret, 'sha256', 'base64'), + 'SH-CRED-TIME': timestamp, + 'SH-CRED-PASS': this.password, + 'Content-Type': 'application/json', + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/surbitcoin.js b/TaskServer/node_modules/ccxt/js/surbitcoin.js new file mode 100644 index 000000000..b67310033 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/surbitcoin.js @@ -0,0 +1,32 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const foxbit = require ('./foxbit.js'); + +// --------------------------------------------------------------------------- + +module.exports = class surbitcoin extends foxbit { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'surbitcoin', + 'name': 'SurBitcoin', + 'countries': [ 'VE' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991511-f0a50194-6481-11e7-99b5-8f02932424cc.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://surbitcoin.com', + 'doc': 'https://blinktrade.com/docs', + }, + 'options': { + 'brokerId': '1', // https://blinktrade.com/docs/#brokers + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/test/.eslintrc.js b/TaskServer/node_modules/ccxt/js/test/.eslintrc.js new file mode 100644 index 000000000..a29b92fbf --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/.eslintrc.js @@ -0,0 +1,43 @@ +'use strict'; + +module.exports = { + "env": { + "node": true, + "mocha": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "script", + }, + "extends": ["eslint:recommended"], + "rules": { + "semi": ["error", "never"], + "no-unused-vars": ["off"], + "quotes": ["off", "single"], + "func-call-spacing": ["error", "always"], + "one-var": "off", + "indent": ["error", 4], + "comma-style": "off", + "no-multi-spaces": "off", + "comma-dangle": "off", + "spaced-comment": "off", + "camelcase": "off", + "padded-blocks": "off", + "multiline-comment-style": "off", + "curly": "off", + "rest-spread-spacing": "off", + "no-multiple-empty-lines": "off", + "no-undef-init": "off", + "no-useless-return": "off", + "no-console": "off", + "operator-linebreak": "off", + "key-spacing": "off", + "brace-style": "off", + "padding-line-between-statements": ["off", + { "blankLine": "always", "prev":"function", "next": "*" }, + { "blankLine": "always", "prev":"directive", "next": "*" }, + { "blankLine": "always", "prev":"*", "next": "cjs-export" }, + ], + "import/no-extraneous-dependencies": ["error", { "devDependencies": true }], + }, +} diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.balance.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.balance.js new file mode 100644 index 000000000..4b0a783db --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.balance.js @@ -0,0 +1,61 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , chai = require ('chai') + , assert = chai.assert + +// ---------------------------------------------------------------------------- + +module.exports = (exchange, balance, method) => { + + let currencies = [ + 'USD', + 'CNY', + 'EUR', + 'BTC', + 'ETH', + 'JPY', + 'LTC', + 'DASH', + 'DOGE', + 'UAH', + 'RUB', + 'XRP', + ] + + // log.yellow (balance) + + assert (typeof balance['total'] === 'object') + assert (typeof balance['free'] === 'object') + assert (typeof balance['used'] === 'object') + + for (let currency of Object.keys (balance['total'])) { + let total = balance['total'][currency] + let free = balance['free'][currency] + let used = balance['used'][currency] + if (total !== undefined && free !== undefined && used !== undefined) { + assert (total === free + used, 'free and used do not sum to total ' + exchange.id) + } + } + + let result = currencies + .filter (currency => (currency in balance) && + (balance[currency]['total'] !== undefined)) + + if (result.length > 0) { + result = result.map (currency => currency + ': ' + balance[currency]['total']) + if (exchange.currencies.length > result.length) + result = result.join (', ') + ' + more...' + else + result = result.join (', ') + + } else { + + result = 'zero balance' + } + + log (result) + +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.currency.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.currency.js new file mode 100644 index 000000000..f5b93dbbd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.currency.js @@ -0,0 +1,55 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, currency, method) => { + + const format = { + 'id': 'btc', // string literal for referencing within an exchange + 'code': 'BTC', // uppercase string literal of a pair of currencies + //---------------------------------------------------------------------- + // commented temporarily to bring currencies to consistency first + // 'name': 'Bitcoin', // uppercase string, base currency, 3 or more letters + // 'quote': 'USD', // uppercase string, quote currency, 3 or more letters + // 'withdraw': true, // can withdraw + // 'deposit': true, // can deposit + // 'active': true, // can both withdraw & deposit + // 'precision': 8, // number of decimal digits "after the dot" + // 'limits': { // value limits when placing orders on this market + // 'amount': { + // 'min': 0.01, // order amount should be > min + // 'max': 1000, // order amount should be < max + // }, + // 'price': { + // 'min': 0.01, // order price should be > min + // 'max': 1000, // order price should be < max + // }, + // 'cost': { // order cost = price * amount + // 'min': 0.01, // order cost should be > min + // 'max': 1000, // order cost should be < max + // }, + // }, + // 'info': {}, // the original unparsed market info from the exchange + //---------------------------------------------------------------------- + } + + expect (currency).to.deep.include.all.keys (format) + + // expect (currency['precision']).to.not.be.undefined + + // expect (currency['limits']['amount']['min']).to.not.be.undefined + // expect (currency['limits']['price']['min']).to.not.be.undefined + // expect (market['limits']['cost']['min']).to.not.be.undefined + + // log (market) + + return currency +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchBalance.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchBalance.js new file mode 100644 index 000000000..47ec8741f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchBalance.js @@ -0,0 +1,24 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , testBalance = require ('./test.balance.js') + +// ---------------------------------------------------------------------------- + +module.exports = async (exchange) => { + + if (!(exchange.has.fetchBalance)) { + log (exchange.id.green, ' does not have fetchBalance') + return + } + + log ('fetching balance...') + + const response = await exchange.fetchBalance () + + testBalance (exchange, response) + + return response +} diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchClosedOrders.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchClosedOrders.js new file mode 100644 index 000000000..2a9155746 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchClosedOrders.js @@ -0,0 +1,40 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testOrder = require ('./test.order.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + if (exchange.has.fetchClosedOrders) { + + // log ('fetching closed orders...') + + let orders = await exchange.fetchClosedOrders (symbol) + + log ('fetched', orders.length.toString ().green, 'closed orders, testing each') + + assert (orders instanceof Array) + + let now = Date.now () + + for (let i = 0; i < orders.length; i++) { + let order = orders[i] + testOrder (exchange, order, symbol, now) + assert (order.status === 'closed' || order.status === 'canceled') + } + + // log (asTable (orders)) + + } else { + + log ('fetching closed orders not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchCurrencies.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchCurrencies.js new file mode 100644 index 000000000..dd9a5a919 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchCurrencies.js @@ -0,0 +1,38 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testCurrency = require ('./test.currency.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange) => { + + const skippedExchanges = [ + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchCurrencies...') + return + } + + if (exchange.has.fetchCurrencies) { + + // log ('fetching currencies...') + + const method = 'fetchCurrencies' + const currencies = await exchange[method] () + Object.values (currencies).forEach (currency => testCurrency (exchange, currency, method)) + return currencies + + } else { + + log ('fetching currencies not supported') + } +} + diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchDeposits.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchDeposits.js new file mode 100644 index 000000000..2169356a4 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchDeposits.js @@ -0,0 +1,39 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testTransaction = require ('./test.transaction.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, code) => { + + if (exchange.has.fetchDeposits) { + + // log ('fetching deposits...') + + let transactions = await exchange.fetchDeposits (code) + + log ('fetched', transactions.length.toString ().green, 'deposits, asserting each...') + + assert (transactions instanceof Array) + + let now = Date.now () + + for (let i = 0; i < transactions.length; i++) { + let transaction = transactions[i] + testTransaction (exchange, transaction, code, now) + } + + // log (asTable (transactions)) + + } else { + + log ('fetching deposits not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchFundingFees.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchFundingFees.js new file mode 100644 index 000000000..de16a33bc --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchFundingFees.js @@ -0,0 +1,38 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange) => { + + const skippedExchanges = [ + 'bibox', // fetchFundingFees should be rewritten to fetchFundingFee + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchFundingFees...') + return + } + + if (exchange.has.fetchFundingFees) { + + // log ('fetching funding fees...') + + const method = 'fetchFundingFees' + const fees = await exchange[method] () + log.green (fees) + return fees + + } else { + + log ('fetching funding fees not supported') + } +} + diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchL2OrderBook.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchL2OrderBook.js new file mode 100644 index 000000000..e831fac69 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchL2OrderBook.js @@ -0,0 +1,31 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testOrderBook = require ('./test.orderbook.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + // log (symbol.green, 'fetching order book...') + + const method = 'fetchL2OrderBook' + + if (exchange.has[method]) { + + let orderbook = await exchange[method] (symbol) + testOrderBook (exchange, orderbook, method, symbol) + return orderbook + + } else { + + log (method + '() not supported') + } + +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchLedger.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchLedger.js new file mode 100644 index 000000000..bb6b7e1f6 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchLedger.js @@ -0,0 +1,47 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testLedgerItem = require ('./test.ledgerItem') + +require ('ansicolor').nice + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, code) => { + + if (exchange.has.fetchLedger) { + + let items = await exchange.fetchLedger (code) + + assert (items instanceof Array) + + log ('fetched', items.length.toString ().green, 'ledger items') + + let now = Date.now () + + for (let i = 0; i < items.length; i++) { + testLedgerItem (exchange, items[i], code, now) + if (i > 0) { + assert (items[i].timestamp >= items[i - 1].timestamp) + } + } + + if (exchange.has.fetchLedgerItem) { + let { id } = items.pop () + let item = await exchange.fetchLedgerItem (id) + if (Array.isArray (item)) { + item = item[0] + } + testLedgerItem (exchange, item, code, now) + } + + } else { + + log ('fetching ledger items not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchMarkets.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchMarkets.js new file mode 100644 index 000000000..afdba73f3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchMarkets.js @@ -0,0 +1,39 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testMarket = require ('./test.market.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange) => { + + const skippedExchanges = [ + 'bitforex', + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchMarkets...') + return + } + + if (exchange.has.fetchMarkets) { + + // log ('fetching markets...') + + const method = 'fetchMarkets' + const markets = await exchange[method] () + Object.values (markets).forEach (market => testMarket (exchange, market, method)) + return markets + + } else { + + log ('fetching markets not supported') + } +} + diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchMyTrades.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchMyTrades.js new file mode 100644 index 000000000..c28f7a18f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchMyTrades.js @@ -0,0 +1,53 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testTrade = require ('./test.trade.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + const skippedExchanges = [ + 'bitso', + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchMyTrades...') + return + } + + if (exchange.has.fetchMyTrades) { + + // log ('fetching my trades...') + + const since = exchange.milliseconds () - 60 * 60 * 1000 + + let trades = await exchange.fetchMyTrades (symbol, since) + + assert (trades instanceof Array) + + log ('fetched', trades.length.toString ().green, 'trades') + + let now = Date.now () + + for (let i = 0; i < trades.length; i++) { + testTrade (exchange, trades[i], symbol, now) + if (i > 0) { + assert (trades[i].timestamp >= trades[i - 1].timestamp) + } + } + + // trades.forEach (trade => log.dim ('-'.repeat (80), "\n", trade)) + // log (asTable (trades)) + + } else { + + log ('fetching my trades not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOHLCV.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOHLCV.js new file mode 100644 index 000000000..230e1cf77 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOHLCV.js @@ -0,0 +1,56 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testOHLCV = require ('./test.ohlcv.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + const skippedExchanges = [ + 'btcalpha', // issue with 404 on a documented endpoint https://travis-ci.org/ccxt/ccxt/builds/643930431#L2213 + 'bitmex', // an issue with null values,to be resolved later + 'cex', + 'okex', + 'okcoinusd', + 'mandala', + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchOHLCV...') + return + } + + if (exchange.has.fetchOHLCV) { + + const timeframe = Object.keys (exchange.timeframes || { '1d': '1d' })[0] + const limit = 10 + const duration = exchange.parseTimeframe (timeframe) + const since = exchange.milliseconds () - duration * limit * 1000 - 1000 + + // log (symbol.green, 'fetching OHLCV...') + + const ohlcvs = await exchange.fetchOHLCV (symbol, timeframe, since, limit) + + const now = Date.now () + + for (let i = 0; i < ohlcvs.length; i++) { + const ohlcv = ohlcvs[i] + testOHLCV (exchange, ohlcv, symbol, now) + } + + log (symbol.green, 'fetched', Object.keys (ohlcvs).length.toString ().green, 'OHLCVs') + + return ohlcvs + + } else { + + log ('fetching OHLCV not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOpenOrders.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOpenOrders.js new file mode 100644 index 000000000..dd0a4531e --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOpenOrders.js @@ -0,0 +1,40 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testOrder = require ('./test.order.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + if (exchange.has.fetchOpenOrders) { + + // log ('fetching open orders...') + + let orders = await exchange.fetchOpenOrders (symbol) + + assert (orders instanceof Array) + + log ('fetched', orders.length.toString ().green, 'open orders') + + let now = Date.now () + + for (let i = 0; i < orders.length; i++) { + let order = orders[i] + testOrder (exchange, order, symbol, now) + assert (order.status === 'open') + } + + // log (asTable (orders)) + + } else { + + log ('fetching open orders not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrderBook.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrderBook.js new file mode 100644 index 000000000..a0076b0bc --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrderBook.js @@ -0,0 +1,32 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testOrderBook = require ('./test.orderbook.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + // log (symbol.green, 'fetching order book...') + + const method = 'fetchOrderBook' + + if (exchange.has[method]) { + + let orderbook = await exchange[method] (symbol) + + testOrderBook (exchange, orderbook, method, symbol) + + return orderbook + + } else { + + log (method + '() not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrderBooks.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrderBooks.js new file mode 100644 index 000000000..718df81b3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrderBooks.js @@ -0,0 +1,45 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testOrderBook = require ('./test.orderbook.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange) => { + + const randomSymbols = exchange.symbols.sort (() => 0.5 - Math.random ()).slice (0, 2) + const customExchangeParams = ([ + 'yobit', + 'tidex', + 'ccex', + 'liqui', + ]).reduce ((params, id) => ({ ...params, [id]: [randomSymbols], }), {}) + + const args = (exchange.id in customExchangeParams) ? customExchangeParams[exchange.id] : [] + const method = 'fetchOrderBooks' + + if (exchange.has[method]) { + + // log ('fetching order books...') + + let orderbooks = await exchange[method] (...args) + + // log.green (orderbooks) + + Object.entries (orderbooks).forEach (([symbol, orderbook]) => { + testOrderBook (exchange, orderbook, method, symbol) + }) + + // return orderbooks + + } else { + + log (method + '() not supported') + } +} diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrders.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrders.js new file mode 100644 index 000000000..aba8a1696 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchOrders.js @@ -0,0 +1,49 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testOrder = require ('./test.order.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + const skippedExchanges = [ + 'mandala', + 'rightbtc', + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchMyTrades...') + return + } + + if (exchange.has.fetchOrders) { + + // log ('fetching orders...') + + const orders = await exchange.fetchOrders (symbol) + + log ('fetched', orders.length.toString ().green, 'orders, asserting each...') + + assert (orders instanceof Array) + + const now = Date.now () + + for (let i = 0; i < orders.length; i++) { + const order = orders[i] + testOrder (exchange, order, symbol, now) + } + + // log (asTable (orders)) + + } else { + + log ('fetching orders not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchStatus.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchStatus.js new file mode 100644 index 000000000..8d4d4893d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchStatus.js @@ -0,0 +1,37 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange) => { + + if (exchange.has.fetchStatus) { + + // log ('fetching status...') + + const method = 'fetchStatus' + const status = await exchange[method] () + + const sampleStatus = { + 'status': 'ok', // 'ok', 'shutdown', 'error', 'maintenance' + 'updated': undefined, // integer, last updated timestamp in milliseconds if updated via the API + 'eta': undefined, // when the maintenance or outage is expected to end + 'url': undefined, // a link to a GitHub issue or to an exchange post on the subject + } + + assert.containsAllKeys (status, sampleStatus) + + return status + + } else { + + log ('fetching status not supported') + } +} diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTicker.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTicker.js new file mode 100644 index 000000000..5fecece5c --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTicker.js @@ -0,0 +1,55 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testTicker = require ('./test.ticker.js') + +// ---------------------------------------------------------------------------- + +const printTickerOneLiner = (ticker, method, symbol) => { + + log (symbol.toString ().green, + method, + ticker['datetime'], + 'bid: ' + (ticker['bid']), + 'ask: ' + (ticker['ask'])) +} + +// ---------------------------------------------------------------------------- + +module.exports = async (exchange, symbol) => { + + const method = 'fetchTicker' + + const skippedExchanges = [ + 'digifinex', + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping ' + method + '...') + return + } + + if (exchange.has[method]) { + + // log (symbol.green, 'fetching ticker...') + + let ticker = await exchange.fetchTicker (symbol) + + testTicker (exchange, ticker, method, symbol) + + printTickerOneLiner (ticker, method, symbol) + + return ticker + + } else { + + log (symbol.green, 'fetchTicker () not supported') + } +} + diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTickers.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTickers.js new file mode 100644 index 000000000..4bb7da251 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTickers.js @@ -0,0 +1,53 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testTicker = require ('./test.ticker.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + const skippedExchanges = [ + 'binance', + 'digifinex', + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetch all tickers...') + return + } + + if (exchange.has.fetchTickers) { + + // log ('fetching all tickers at once...') + + const method = 'fetchTickers' + let tickers = undefined + + try { + + tickers = await exchange[method] () + log ('fetched all', Object.keys (tickers).length.toString ().green, 'tickers') + + } catch (e) { + + log ('failed to fetch all tickers, fetching multiple tickers at once...') + tickers = await exchange[method] ([ symbol ]) + log ('fetched', Object.keys (tickers).length.toString ().green, 'tickers') + } + + Object.values (tickers).forEach (ticker => testTicker (exchange, ticker, method, symbol)) + return tickers + + } else { + + log ('fetching all tickers at once not supported') + } +} + diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTrades.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTrades.js new file mode 100644 index 000000000..a480b3dee --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTrades.js @@ -0,0 +1,48 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testTrade = require ('./test.trade.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + const skippedExchanges = [ + // 'adara', + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchTrades...') + return + } + + if (exchange.has.fetchTrades) { + + // log (symbol.green, 'fetching trades...') + + let trades = await exchange.fetchTrades (symbol) + assert (trades instanceof Array) + log (symbol.green, 'fetched', Object.values (trades).length.toString ().green, 'trades') + let now = Date.now () + for (let i = 0; i < trades.length; i++) { + testTrade (exchange, trades[i], symbol, now) + if (i > 0) { + if (trades[i].timestamp && trades[i - 1].timestamp) { + assert (trades[i].timestamp >= trades[i - 1].timestamp) + } + } + + } + // log (asTable (trades)) + + } else { + + log (symbol.green, 'fetchTrades () not supported'.yellow) + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTradingFees.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTradingFees.js new file mode 100644 index 000000000..fd38d55d0 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTradingFees.js @@ -0,0 +1,37 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange) => { + + const skippedExchanges = [ + ] + + if (skippedExchanges.includes (exchange.id)) { + log (exchange.id, 'found in ignored exchanges, skipping fetchTradingFees...') + return + } + + if (exchange.has.fetchTradingFees) { + + // log ('fetching trading fees...') + + const method = 'fetchTradingFees' + const fees = await exchange[method] () + log.green ({ 'maker': fees['maker'], 'taker': fees['taker'] }) + return fees + + } else { + + log ('fetching trading fees not supported') + } +} + diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTransactions.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTransactions.js new file mode 100644 index 000000000..ad7d15880 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchTransactions.js @@ -0,0 +1,39 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testTransaction = require ('./test.transaction.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, code) => { + + if (exchange.has.fetchTransactions) { + + // log ('fetching transactions...') + + let transactions = await exchange.fetchTransactions (code) + + log ('fetched', transactions.length.toString ().green, 'transactions, asserting each...') + + assert (transactions instanceof Array) + + let now = Date.now () + + for (let i = 0; i < transactions.length; i++) { + let transaction = transactions[i] + testTransaction (exchange, transaction, code, now) + } + + // log (asTable (transactions)) + + } else { + + log ('fetching transactions not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchWithdrawals.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchWithdrawals.js new file mode 100644 index 000000000..bbeb2715d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.fetchWithdrawals.js @@ -0,0 +1,39 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + , testTransaction = require ('./test.transaction.js') + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, code) => { + + if (exchange.has.fetchWithdrawals) { + + // log ('fetching deposits...') + + let transactions = await exchange.fetchWithdrawals (code) + + log ('fetched', transactions.length.toString ().green, 'withdrawals, asserting each...') + + assert (transactions instanceof Array) + + let now = Date.now () + + for (let i = 0; i < transactions.length; i++) { + let transaction = transactions[i] + testTransaction (exchange, transaction, code, now) + } + + // log (asTable (transactions)) + + } else { + + log ('fetching withdrawals not supported') + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.ledgerItem.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.ledgerItem.js new file mode 100644 index 000000000..cece5f4c2 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.ledgerItem.js @@ -0,0 +1,50 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, item, code, now) => { + expect (item).to.be.an ('object') + expect (item).to.have.property ('id') + expect (typeof item.id).to.be.oneOf (['string', 'undefined']) + expect (item).to.have.property ('direction') + expect (item.direction).to.be.oneOf (['in', 'out']) + expect (item).to.have.property ('account') + expect (typeof item.account).to.be.oneOf (['string', 'undefined']) + expect (item).to.have.property ('referenceId') + expect (typeof item.referenceId).to.be.oneOf (['string', 'undefined']) + expect (item).to.have.property ('referenceAccount') + expect (typeof item.referenceAccount).to.be.oneOf (['string', 'undefined']) + expect (item).to.have.property ('type') + // expect (item.type).to.be.oneOf (['trade', 'transaction', 'margin', 'cashback', 'referral', 'transfer', 'fee', /* TODO: add more types here */ ]) + expect (item).to.have.property ('currency') + expect (exchange.currencies).to.have.property (item.currency) + expect (item).to.have.property ('amount') + expect (typeof item.amount).to.be.oneOf (['number', 'undefined']) + expect (item).to.have.property ('before') + expect (typeof item.balanceBefore).to.be.oneOf (['number', 'undefined']) + expect (item).to.have.property ('after') + expect (typeof item.balanceAfter).to.be.oneOf (['number', 'undefined']) + expect (item).to.have.property ('timestamp') + expect (typeof item.timestamp).to.be.oneOf (['number', 'undefined']) + expect (item.timestamp).to.be.gt (1230940800000) + expect (item.timestamp).to.be.lt (now) + expect (item).to.have.property ('datetime') + expect (item.datetime).to.be.equal (exchange.iso8601 (item.timestamp)) + expect (item).to.have.property ('fee') + if (item.fee !== undefined) { + expect (item.fee).to.be.an ('object') + expect (item.fee).to.have.property ('cost') + expect (typeof item.fee.cost).to.be.oneOf (['number', 'undefined']) + expect (item.fee).to.have.property ('currency') + expect (item.fee.currency).to.be.equal (item.currency) + } + expect (item).to.have.property ('info').that.is.an ('object') +} diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.market.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.market.js new file mode 100644 index 000000000..67c19a4f8 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.market.js @@ -0,0 +1,59 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, market, method) => { + + const format = { + 'id': 'btcusd', // string literal for referencing within an exchange + 'symbol': 'BTC/USD', // uppercase string literal of a pair of currencies + 'base': 'BTC', // uppercase string, base currency, 3 or more letters + 'quote': 'USD', // uppercase string, quote currency, 3 or more letters + //--------------------------------------------------------------------- + // commented temporarily to bring markets to consistency first + // 'active': true, // boolean, market status + // 'precision': { // number of decimal digits "after the dot" + // 'price': 8, // integer + // 'amount': 8, // integer + // 'cost': 8, // integer + // }, + // 'limits': { // value limits when placing orders on this market + // 'amount': { + // 'min': 0.01, // order amount should be > min + // 'max': 1000, // order amount should be < max + // }, + // 'price': { + // 'min': 0.01, // order price should be > min + // 'max': 1000, // order price should be < max + // }, + // 'cost': { // order cost = price * amount + // 'min': 0.01, // order cost should be > min + // 'max': 1000, // order cost should be < max + // }, + // }, + // 'info': {}, // the original unparsed market info from the exchange + //--------------------------------------------------------------------- + + } + + expect (market).to.deep.include.all.keys (format) + expect (market).to.not.have.key ('lot') + + // expect (market['precision']['amount']).to.not.be.undefined + // expect (market['precision']['price']).to.not.be.undefined + + // expect (market['limits']['amount']['min']).to.not.be.undefined + // expect (market['limits']['price']['min']).to.not.be.undefined + // expect (market['limits']['cost']['min']).to.not.be.undefined + + // log (market) + +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.ohlcv.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.ohlcv.js new file mode 100644 index 000000000..664103259 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.ohlcv.js @@ -0,0 +1,27 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, ohlcv, symbol, now) => { + assert.isOk (ohlcv) + assert (Array.isArray (ohlcv)) + assert (ohlcv.length >= 6) + for (let i = 0; i < ohlcv.length; i++) { + assert (ohlcv[i] === undefined || typeof ohlcv[i] === 'number') + } + + assert (ohlcv[0] > 1230940800000) // 03 Jan 2009 - first block + assert (ohlcv[0] < 2147483648000) // 19 Jan 2038 - int32 overflows + + assert (ohlcv[1] === undefined || ohlcv[2] === undefined || ohlcv[1] <= ohlcv[2], 'open > high!') // open <= high + assert (ohlcv[3] === undefined || ohlcv[2] === undefined || ohlcv[3] <= ohlcv[2], 'low > high!') // low <= high + assert (ohlcv[3] === undefined || ohlcv[4] === undefined || ohlcv[3] <= ohlcv[4], 'low > close!') // low <= close +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.order.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.order.js new file mode 100644 index 000000000..a24bf7323 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.order.js @@ -0,0 +1,46 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, order, symbol, now) => { + assert.isOk (order) + assert (typeof order.id === 'string') + assert (typeof order.timestamp === 'number') + assert (order.timestamp > 1230940800000) // 03 Jan 2009 - first block + assert (order.timestamp < now) + assert ('lastTradeTimestamp' in order) + assert (order.datetime === exchange.iso8601 (order.timestamp)) + assert (order.status === 'open' || order.status === 'closed' || order.status === 'canceled') + assert (order.symbol === symbol) + assert (typeof order.type === 'string') + assert (order.side === 'buy' || order.side === 'sell') + assert (typeof order.price === 'number') + assert (order.price > 0) + assert (typeof order.amount === 'number') + assert (order.amount >= 0) + if (order.filled) { + assert (typeof order.filled === 'number') + assert (order.filled >= 0 && order.filled <= order.amount) + } + if (order.remaining) { + assert (typeof order.remaining === 'number') + assert (order.remaining >= 0 && order.remaining <= order.amount) + } + if (order.trades) { + assert (order.trades instanceof Array) + } + if (order.fee) { + assert (typeof order.fee.cost === 'number') + if (order.fee.cost !== 0) + assert (typeof order.fee.currency === 'string') + } + assert.isOk (order.info) +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.orderbook.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.orderbook.js new file mode 100644 index 000000000..7d658434c --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.orderbook.js @@ -0,0 +1,75 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +// ---------------------------------------------------------------------------- + +const printOrderBookOneLiner = (orderbook, method, symbol) => { + + const bids = orderbook.bids + const asks = orderbook.asks + + log (symbol.toString ().green, + method, + orderbook['nonce'] || orderbook['datetime'], + bids.length, 'bids:', bids[0], + asks.length, 'asks:', asks[0]) +} + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, orderbook, method, symbol) => { + + const format = { + 'bids': [], + 'asks': [], + 'timestamp': 1234567890, + 'datetime': '2017-09-01T00:00:00', + 'nonce': 134234234, + // 'info': {}, + } + + expect (orderbook).to.have.all.keys (format) + + const bids = orderbook.bids + const asks = orderbook.asks + + for (let i = 0; i < bids.length; i++) { + if (bids.length > (i + 1)) { + assert (bids[i][0] >= bids[i + 1][0]) + } + assert (typeof bids[i][0] === 'number') + assert (typeof bids[i][1] === 'number') + } + + for (let i = 0; i < asks.length; i++) { + if (asks.length > (i + 1)) { + assert (asks[i][0] <= asks[i + 1][0]) + } + assert (typeof asks[i][0] === 'number') + assert (typeof asks[i][1] === 'number') + } + + if (![ + + 'coinmarketcap', + 'xbtce', + 'coinsecure', + 'upbit', // an orderbook might have a 0-price ask occasionally + + ].includes (exchange.id)) { + + if (bids.length && asks.length) + assert (bids[0][0] <= asks[0][0], + `bids[0][0]: ${bids[0][0]} (of ${bids.length}); asks[0][0]:${asks[0][0]} (of ${asks.length})`) + + } + + printOrderBookOneLiner (orderbook, method, symbol) +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.ticker.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.ticker.js new file mode 100644 index 000000000..9601714f6 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.ticker.js @@ -0,0 +1,96 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, ticker, method, symbol) => { + + const format = { + 'symbol': 'ETH/BTC', + 'info': {}, + 'timestamp': 1234567890, + 'datetime': '2017-09-01T00:00:00', + 'high': 1.234, // highest price + 'low': 1.234, // lowest price + 'bid': 1.234, // current best bid (buy) price + 'bidVolume': 1.234, // current best bid (buy) amount (may be missing or undefined) + 'ask': 1.234, // current best ask (sell) price + 'askVolume': 1.234, // current best ask (sell) amount (may be missing or undefined) + 'vwap': 1.234, // volume weighed average price + 'open': 1.234, // opening price + 'close': 1.234, // price of last trade (closing price for current period) + 'last': 1.234, // same as `close`, duplicated for convenience + 'previousClose': 1.234, // closing price for the previous period + 'change': 1.234, // absolute change, `last - open` + 'percentage': 1.234, // relative change, `(change/open) * 100` + 'average': 1.234, // average price, `(last + open) / 2` + 'baseVolume': 1.234, // volume of base currency + 'quoteVolume': 1.234, // volume of quote currency + } + + expect (ticker).to.have.all.keys (format) + + const keys = [ 'datetime', 'timestamp', 'high', 'low', 'bid', 'ask', 'baseVolume', 'quoteVolume', 'vwap' ] + + assert (!('first' in ticker), '`first` field leftover in ' + exchange.id) + assert (ticker['last'] === ticker['close'], '`last` != `close` in ' + exchange.id) + + log (ticker['datetime'], exchange.id, method, ticker['symbol'].green, ticker['last']) + + keys.forEach (key => assert (key in ticker)) + + const { high, low, vwap, baseVolume, quoteVolume } = ticker + + // this assert breaks QuadrigaCX sometimes... still investigating + // if (vwap) + // assert (vwap >= low && vwap <= high) + + /* + if (baseVolume && quoteVolume && high && low) { + assert (quoteVolume >= baseVolume * low) // this assertion breaks therock + assert (quoteVolume <= baseVolume * high) + } + */ + + if (baseVolume && vwap) + assert (quoteVolume) + + if (quoteVolume && vwap) + assert (baseVolume) + + // log (symbol.green, 'ticker', + // ticker['datetime'], + // ... (keys.map (key => + // key + ': ' + ticker[key]))) + + if (![ + + 'coinmarketcap', + 'xbtce', + 'coss', + 'idex', + 'mandala', + 'mercado', + 'okex', + 'southxchange', // https://user-images.githubusercontent.com/1294454/59953532-314bea80-9489-11e9-85b3-2a711ca49aa7.png + 'bitmart', + + ].includes (exchange.id)) { + + if (ticker['baseVolume'] || ticker['quoteVolume']) { + if (ticker['bid'] && ticker['ask']) { + assert (ticker['bid'] <= ticker['ask'], 'ticker bid is greater than ticker ask!') + } + } + + } + + return ticker +} diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.trade.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.trade.js new file mode 100644 index 000000000..2ee1639ca --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.trade.js @@ -0,0 +1,92 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, trade, symbol, now) => { + + assert.isOk (trade) + + const sampleTrade = { + 'info': { a: 1, b: 2, c: 3 }, // the original decoded JSON as is + 'id': '12345-67890:09876/54321', // string trade id + 'timestamp': 1502962946216, // Unix timestamp in milliseconds + 'datetime': '2017-08-17 12:42:48.000', // ISO8601 datetime with milliseconds + 'symbol': 'ETH/BTC', // symbol + 'order': '12345-67890:09876/54321', // string order id or undefined/None/null + 'type': 'limit', // order type, 'market', 'limit' or undefined/None/null + 'side': 'buy', // direction of the trade, 'buy' or 'sell' + 'takerOrMaker': 'taker', // string, 'taker' or 'maker' + 'price': 0.06917684, // float price in quote currency + 'amount': 1.5, // amount of base currency + 'cost': 0.10376526, // total cost (including fees), `price * amount` + } + + assert.containsAllKeys (trade, sampleTrade) + assert.hasAnyKeys (trade, [ 'fee', 'fees' ]) + + const feeKeys = [ 'cost', 'currency' ] + + if (trade.fee !== undefined) { + assert (typeof trade.fee === 'object') + assert.containsAllKeys (trade.fee, feeKeys) + } + + if (trade.fees !== undefined) { + assert (typeof trade.fees === 'object') + assert (Array.isArray (trade.fees)) + trade.fees.forEach (fee => assert.containsAllKeys (fee, feeKeys)) + } + + assert (trade.id === undefined || typeof trade.id === 'string') + assert (typeof trade.timestamp === 'number' || trade.timestamp === undefined) + + if (trade.timestamp) { + assert (trade.timestamp > 1230940800000) // 03 Jan 2009 - first block + assert (trade.timestamp < 2147483648000) // 19 Jan 2038 - int32 overflows + } + + //------------------------------------------------------------------ + + const adjustedNow = now + 60000 + + const exchangesExcludedFromTimestampCheck = [ + 'coinbasepro', + 'coinbaseprime', // ... as well, probably + 'kucoin2', + ] + + if (trade.timestamp) { + if (!exchangesExcludedFromTimestampCheck.includes (exchange.id)) { + assert (trade.timestamp < adjustedNow, 'trade.timestamp is greater than or equal to current time: trade: ' + exchange.iso8601 (trade.timestamp) + ' now: ' + exchange.iso8601 (now)) + } + } + + //------------------------------------------------------------------ + + assert (trade.datetime === exchange.iso8601 (trade.timestamp)) + + const isExchangeLackingFilteringTradesBySymbol = [ + 'kraken', // override for kraken and possibly other exchanges as well, can't return private trades per symbol at all + ].includes (exchange.id) + + if (!isExchangeLackingFilteringTradesBySymbol) + assert (trade.symbol === symbol, 'trade symbol is not equal to requested symbol: trade: ' + trade.symbol + ' reqeusted: ' + symbol) + + assert (trade.type === undefined || typeof trade.type === 'string') + assert (trade.side === undefined || trade.side === 'buy' || trade.side === 'sell') + assert (trade.order === undefined || typeof trade.order === 'string') + assert (typeof trade.price === 'number', 'trade.price is not a number') + assert (trade.price > 0) + assert (typeof trade.amount === 'number', 'trade.amount is not a number') + assert (trade.amount >= 0) + assert (trade.takerOrMaker === undefined || trade.takerOrMaker === 'taker' || trade.takerOrMaker === 'maker') + assert.isOk (trade.info) +} diff --git a/TaskServer/node_modules/ccxt/js/test/Exchange/test.transaction.js b/TaskServer/node_modules/ccxt/js/test/Exchange/test.transaction.js new file mode 100644 index 000000000..6cc6e24e3 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/Exchange/test.transaction.js @@ -0,0 +1,37 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = (exchange, transaction, code, now) => { + assert.isOk (transaction) + assert (typeof transaction.id === 'string') + assert (typeof transaction.timestamp === 'number') + assert (transaction.timestamp > 1230940800000) // 03 Jan 2009 - first block + assert (transaction.timestamp < now) + assert ('updated' in transaction) + assert ('address' in transaction) + assert ('tag' in transaction) + assert ('txid' in transaction) + assert (transaction.datetime === exchange.iso8601 (transaction.timestamp)) + assert (transaction.status === 'ok' || transaction.status === 'pending' || transaction.status === 'canceled') + assert (transaction.currency === code) + assert (typeof transaction.type === 'string') + assert (transaction.type === 'deposit' || transaction.type === 'withdrawal') + assert (typeof transaction.amount === 'number') + assert (transaction.amount >= 0) + if (transaction.fee) { + assert (typeof transaction.fee.cost === 'number') + if (transaction.fee.cost !== 0) { + assert (typeof transaction.fee.currency === 'string') + } + } + assert.isOk (transaction.info) +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/base/functions/test.crypto.js b/TaskServer/node_modules/ccxt/js/test/base/functions/test.crypto.js new file mode 100644 index 000000000..91f71c13f --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/base/functions/test.crypto.js @@ -0,0 +1,110 @@ +const { hash, ecdsa, jwt } = require ('../../../base/functions/crypto') +const { encode } = require ('../../../base/functions/encode') +const Exchange = require ('../../../base/Exchange'); +const assert = require ('assert') + +function equals (a, b) { + // does not check if b has more properties than a + for (const prop of Object.keys (a)) { + if (a[prop] !== b[prop]) { + return false + } + } + return true +} + +const exchange = new Exchange (); + +// --------------------------------------------------------------------------------------------------------------------- + +assert (hash (encode (''), 'sha256', 'hex') === 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); +assert (hash (encode ('cheese'), 'sha256', 'hex') === '873ac9ffea4dd04fa719e8920cd6938f0c23cd678af330939cff53c3d2855f34'); + +assert (hash (encode (''), 'md5', 'hex') === 'd41d8cd98f00b204e9800998ecf8427e'); +assert (hash (encode ('sexyfish'), 'md5', 'hex') === 'c8a35464aa9d5683585786f44d5889f8'); + +assert (hash (encode (''), 'sha1', 'hex') === 'da39a3ee5e6b4b0d3255bfef95601890afd80709'); +assert (hash (encode ('nutella'), 'sha1', 'hex') === 'b3d60a34b744159793c483b067c56d8affc5111a'); + +// --------------------------------------------------------------------------------------------------------------------- + +const privateKey = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a'; + +assert (equals (ecdsa ('1a', privateKey, 'p256', 'sha256'), { + 'r': '3f4537ef9e72240cdefaea19d68fd483b5e10227e89747c58a3b543bfaf9cc46', + 's': '278d65adebd8d52dac34820c02cee0a0e3348f9aee91a21b8e9bfa0a23b40001', + 'v': 1 +})); + + +assert (equals (ecdsa (privateKey, privateKey, 'p256', undefined), { + 'r': '9aadbfe7feca28a8b665731abd8fdd19f8b1be5fcdd1264d111b291a01c8dea2', + 's': '5084765e08e169fbba470ed1f573c9fed8d68a9d883049054b601f66a6648912', + 'v': 0, +})); + +assert (equals (ecdsa ('1a', privateKey, 'secp256k1', 'sha256'), { + 'r': '23dcb2a2a3728a35eb1a35cc01743c4609550d9cceaf2083550f13a9eb135f9f', + 's': '317963fcac18e4ec9f7921b97d7ea0c82a873dd6299cbfb6af016e08ef5ed667', + 'v': 0, +})); + + +assert (equals (ecdsa (privateKey, privateKey, 'secp256k1', undefined), { + 'r': 'b84a36a6fbabd5277ede578448b93d48e70b38efb5b15b1d4e2a298accf938b1', + 's': '66ebfb8221cda925526e699a59cd221bb4cc84bdc563024b1802c4d9e1d8bbe9', + 'v': 1, +})); + +// --------------------------------------------------------------------------------------------------------------------- + +assert (exchange.hashMessage (privateKey) === '0x59ea5d98c3500c3729f95cf98aa91663f498518cc401360df2912742c232207f'); + +assert (equals (exchange.signHash ('0x59ea5d98c3500c3729f95cf98aa91663f498518cc401360df2912742c232207f', privateKey), { + 'r': '0x6f684aa41c02da83dac3039d8805ddbe79a03b1297e247c7742cab8dfc19d341', + 's': '0x62473881674550563cb028ff40a7846fd53620ddf40a20cc1003b8484a109a4a', + 'v': 27 +})); + +assert (equals (exchange.signMessage (privateKey, privateKey), { + 'r': '0x6f684aa41c02da83dac3039d8805ddbe79a03b1297e247c7742cab8dfc19d341', + 's': '0x62473881674550563cb028ff40a7846fd53620ddf40a20cc1003b8484a109a4a', + 'v': 27 +})); + +// --------------------------------------------------------------------------------------------------------------------- + +const pemKeyArray = [ + '-----BEGIN RSA PRIVATE KEY-----', + 'MIIEpAIBAAKCAQEAqUtXQXv2uSm9zPvdJTRpu5/65rBjAoHmIiowAs+u7fY0QP9O', + '+T8CZRQjvZrfPomBiccjCxDo8mm7GkmL67rs9s7VOMQFvpTTtMBeglNSEbXJ3wTt', + '7WM5gD3ZAbhfGtwWdGpcOMX0hV5/d/fiw1zQk0+AooayTryZ7HV+DQcD1sGTYYir', + 'LePUGkKV2NCvKtGOQfRAtuo0ccigMxMvEhWuExCc9Fhd077OpXMc0n7OiFqA2JWN', + 'jlocM9S71Srvw/lDos8a7lGaFUV8gP1CsQY/4qTju6hhsRd4lFujVr5J9FqmDbnv', + 'wQ79SSu6+lT0+ToFdDHiYOorBK2ESFR7EzlyLwIDAQABAoIBAEobQMbZjNbg/sSM', + 'O/HdT6tiDGKPM8gVNLgf34RbhSeFbrpFCDzy6Al3F24YLUEi0CGPmjdt34q93blU', + 'GHvIB5LCV3PR2vHiFAo7ayOBdZtrCEMn1T7lAHaynBu0qW0IiovLQzNW9AKtqv7I', + '8+qw5lyVoKmEbOkqhfaMN/Fb8MJAo3yEVyhyp4EjtFhqDQ9DuHEjp1jGthnMgm+4', + 'qFyELf0DXZSAN+R9IGDqWPyo78lOcPW4pgeLuoLQ6Nn/2JAEGHFt1a7zMEw5yYbR', + 'XB89Bh0dheSmopBN6Heay4YuhnNKua37OlL1/nhKpLGUNE3z/UOXeiUiMeE1C2i+', + 'GjK+xgECgYEA4XH63rJ3VYDHHWUGlWffFWkEpgR4BDEkZ8MyVYQoUjQA2TpCvQ2+', + 'bR7aAIGXhBnRTrIBNu9m4+am0aUfi1hVmVHqt1b2vFgEb9uKuO4U62tCFpeji+Rz', + '8C8QJyu5v5OUWdQZ4EA0d7ljeoTl50g8tpcGsDhnImLt+/jvJuFt8dECgYEAwD0n', + 'rhlkEjKVHxu1xwKeczxYCqqcUV9Quq8HnPh4VJQ77ljfZ57V7sAKrFDi5XfVD9jS', + 'oJPwxjw6za134VobhdvCM6WNs6AJmbR8E/b5kDMbGaZn27vt+/8JfnGaWBqpDi6W', + 'a5xoJOqmcMBQBK1YleZDoY4PLbk+onVKLMfGI/8CgYEAkyUIv8euGdGWpHnm5SdJ', + 'tLi5vv4Vs267uzntJWG/y3+DukTLgIdy7dgAI+pxkVgkg/+syUVSW5eU9CqZPyLl', + 'o8+Sqh2Jp36vTq71iSRj5RA5r3ND3K+8eFzPZzGj6AWUA1lrljFxzV7kLfiF8gH1', + 'FpvWUrhNoGT/vcFJno/uabECgYAecRC5hxfLserfVDoC261PvjyK492BHUDhbxob', + 'h1U2v4qGAdjOxd5Gwm5uPxjPEZzRt5oTB5pXKe5953xWWTiGh/hGyW6ZBTy/9E65', + 'sqBub0lZVHqZ1zamcwqD1WWFkiM3NbVoMQpk3iuhKzMAqpqekiofiSlqKi16+GvY', + 'j4IW7QKBgQCrIUckPZ6IY3ERIN1IL4TIcK2gOJcznp7fLWpC+sv35ya3OhtDXKFt', + 'GjIHmwLuiUNc0iPzA9Rw89W0zIkKWmWcxM88/ithdxh3MEeNDUGSnd4hQ9SECwxB', + 'Wem3eBT7I4VtFYoaTE3/bX1SKfgBdTzIRqWKSDpgBNZg/P2Tc+s11g==', + '-----END RSA PRIVATE KEY-----', +]; + +const pemKey = pemKeyArray.join ("\n"); + +assert (jwt ({'chicken': 'salad'}, encode (pemKey), 'RS256') === 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGlja2VuIjoic2FsYWQifQ.FSKD5Y6RNzkHTuHdvG3753U7QNZ-u-GUSPfP1FMjEaK0Rr_iyQTSSmHhkdYSFFnmBvrrN_l-UwKwir52WlsgmQm9HYm0kidxbj7fWwrK2E1oe0P7OjupFjv1BZxc5W69WeaHtOPWe28tiHiON1LCnax6HgfI5lcIBsESGIIBZMVeaioQn9gDVwea7JxJvAlrhDIWZowIHTIdCQocXip7g5jREWHeEIuJNug67mwnfAFxCjvTRiTd0Bw6oBwjM3FLya-RyEyWrejQOWSuC8CNWVUHISaSmEyZ7uM6wTi2m_58TaE9mQwlef32OPErPvvBpgL5pZIyQ4ymwrCIFQLBQQ'); +assert (jwt ({'lil': 'xan'}, encode ('betrayed'), 'HS256') === 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsaWwiOiJ4YW4ifQ.md-oFvZagA-NXmZoRNyJOQ7zwK-PWUMmMQ_LI9ZOKaM'); diff --git a/TaskServer/node_modules/ccxt/js/test/base/functions/test.datetime.js b/TaskServer/node_modules/ccxt/js/test/base/functions/test.datetime.js new file mode 100644 index 000000000..3168304f5 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/base/functions/test.datetime.js @@ -0,0 +1,62 @@ +'use strict' + +const ccxt = require ('../../../../ccxt'); +const assert = require ('assert'); +const { ROUND_UP, ROUND_DOWN } = require ('../../../base/functions/misc'); + +const exchange = new ccxt.Exchange ({ + 'id': 'regirock', +}); + +// ---------------------------------------------------------------------------- + +assert (exchange.iso8601 (514862627000) === '1986-04-26T01:23:47.000Z'); +assert (exchange.iso8601 (514862627559) === '1986-04-26T01:23:47.559Z'); +assert (exchange.iso8601 (514862627062) === '1986-04-26T01:23:47.062Z'); + +assert (exchange.iso8601 (0) === '1970-01-01T00:00:00.000Z'); + +assert (exchange.iso8601 (-1) === undefined); +assert (exchange.iso8601 () === undefined); +assert (exchange.iso8601 (undefined) === undefined); +assert (exchange.iso8601 ('') === undefined); +assert (exchange.iso8601 ('a') === undefined); +assert (exchange.iso8601 ({}) === undefined); + +// ---------------------------------------------------------------------------- + +assert (exchange.parse8601 ('1986-04-26T01:23:47.000Z') === 514862627000); +assert (exchange.parse8601 ('1986-04-26T01:23:47.559Z') === 514862627559); +assert (exchange.parse8601 ('1986-04-26T01:23:47.062Z') === 514862627062); + +assert (exchange.parse8601 ('1986-04-26T01:23:47.06Z') === 514862627060); +assert (exchange.parse8601 ('1986-04-26T01:23:47.6Z') === 514862627600); + +assert (exchange.parse8601 ('1977-13-13T00:00:00.000Z') === undefined); +assert (exchange.parse8601 ('1986-04-26T25:71:47.000Z') === undefined); + +assert (exchange.parse8601 ('3333') === undefined); +assert (exchange.parse8601 ('Sr90') === undefined); +assert (exchange.parse8601 ('') === undefined); +assert (exchange.parse8601 () === undefined); +assert (exchange.parse8601 (undefined) === undefined); +assert (exchange.parse8601 ({}) === undefined); +assert (exchange.parse8601 (33) === undefined); + +// ---------------------------------------------------------------------------- + +assert (exchange.parseDate ('1986-04-26 00:00:00') === 514857600000); +assert (exchange.parseDate ('1986-04-26T01:23:47.000Z') === 514862627000); +assert (exchange.parseDate ('1986-13-13 00:00:00') === undefined); + + +assert (exchange.roundTimeframe('5m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 13:20:00')); +assert (exchange.roundTimeframe('10m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 13:20:00')); +assert (exchange.roundTimeframe('30m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 13:00:00')); +assert (exchange.roundTimeframe('1d', exchange.parse8601('2019-08-12 13:22:08'), ROUND_DOWN) === exchange.parse8601('2019-08-12 00:00:00')); + +assert (exchange.roundTimeframe('5m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 13:25:00')); +assert (exchange.roundTimeframe('10m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 13:30:00')); +assert (exchange.roundTimeframe('30m', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 13:30:00')); +assert (exchange.roundTimeframe('1h', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-12 14:00:00')); +assert (exchange.roundTimeframe('1d', exchange.parse8601('2019-08-12 13:22:08'), ROUND_UP) === exchange.parse8601('2019-08-13 00:00:00')); diff --git a/TaskServer/node_modules/ccxt/js/test/base/functions/test.generic.js b/TaskServer/node_modules/ccxt/js/test/base/functions/test.generic.js new file mode 100644 index 000000000..147862ec0 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/base/functions/test.generic.js @@ -0,0 +1,155 @@ +'use strict' + +/* ------------------------------------------------------------------------ */ + +const { deepExtend, groupBy, filterBy, omit, sum, sortBy } = require ('../../../../ccxt') +const { strictEqual: equal, deepEqual } = require ('assert') + +/* ------------------------------------------------------------------------ */ + +it ('deepExtend() works', () => { + + let count = 0 + + const values = [{ + a: 1, + b: 2, + d: { + a: 1, + b: [], + c: { test1: 123, test2: 321 }}, + f: 5, + g: 123, + i: 321, + j: [1, 2], + }, + { + b: 3, + c: 5, + d: { + b: { first: 'one', second: 'two' }, + c: { test2: 222 }}, + e: { one: 1, two: 2 }, + f: [{ 'foo': 'bar' }], + g: (void 0), + h: /abc/g, + i: null, + j: [3, 4] + }] + + const extended = deepExtend (...values) + deepEqual ({ + a: 1, + b: 3, + d: { + a: 1, + b: { first: 'one', second: 'two' }, + c: { test1: 123, test2: 222 } + }, + f: [{ 'foo': 'bar' }], + g: undefined, + c: 5, + e: { one: 1, two: 2 }, + h: /abc/g, + i: null, + j: [3, 4] + }, extended) + + deepEqual (deepExtend (undefined, undefined, {'foo': 'bar' }), { 'foo': 'bar' }) +}) + +/* ------------------------------------------------------------------------ */ + +it ('groupBy() works', () => { + + const array = [ + { 'foo': 'a' }, + { 'foo': 'b' }, + { 'foo': 'c' }, + { 'foo': 'b' }, + { 'foo': 'c' }, + { 'foo': 'c' }, + ] + + deepEqual (groupBy (array, 'foo'), { + 'a': [ { 'foo': 'a' } ], + 'b': [ { 'foo': 'b' }, { 'foo': 'b' } ], + 'c': [ { 'foo': 'c' }, { 'foo': 'c' }, { 'foo': 'c' } ], + }) +}) + +/* ------------------------------------------------------------------------ */ + +it ('filterBy() works', () => { + + const array = [ + { 'foo': 'a' }, + { 'foo': undefined }, + { 'foo': 'b' }, + { }, + { 'foo': 'a', 'bar': 'b' }, + { 'foo': 'c' }, + { 'foo': 'd' }, + { 'foo': 'b' }, + { 'foo': 'c' }, + { 'foo': 'c' }, + ] + + deepEqual (filterBy (array, 'foo', 'a'), [ + { 'foo': 'a' }, + { 'foo': 'a', 'bar': 'b' }, + ]) +}) + +/* ------------------------------------------------------------------------ */ + +it ('omit works', () => { + + deepEqual (omit ({ }, 'foo'), {}) + deepEqual (omit ({ foo: 2 }, 'foo'), { }) + deepEqual (omit ({ foo: 2, bar: 3 }, 'foo'), { bar: 3 }) + deepEqual (omit ({ foo: 2, bar: 3 }, ['foo']), { bar: 3 }) + deepEqual (omit ({ foo: 2, bar: 3 }), { foo: 2, bar: 3 }) + deepEqual (omit ({ foo: 2, bar: 3 }, 'foo', 'bar'), {}) + deepEqual (omit ({ foo: 2, bar: 3 }, ['foo'], 'bar'), {}) + deepEqual (omit ({ 5: 2, bar: 3 }, [5]), { bar: 3 }) + deepEqual (omit ({ 5: 2, bar: 3 }, 5), { bar: 3 }) +}) + +/* ------------------------------------------------------------------------ */ + +it ('sum works', () => { + + equal (undefined, sum ()) + equal (2, sum (2)) + equal (432, sum (2,30,400)) + equal (432, sum (2, undefined, [88], 30, '7', 400, null)) +}) + +/* ------------------------------------------------------------------------ */ + +it ('sortBy works', () => { + + const arr = [{ x: 5 }, { x: 2 }, { x: 4 }, { x: 0 },{ x: 1 },{ x: 3 }] + sortBy (arr, 'x') + + deepEqual (arr + [ { x: 0 }, + { x: 1 }, + { x: 2 }, + { x: 3 }, + { x: 4 }, + { x: 5 } ]) + + deepEqual (sortBy (arr, 'x', true), + [ { x: 5 }, + { x: 4 }, + { x: 3 }, + { x: 2 }, + { x: 1 }, + { x: 0 } ]) + + deepEqual (sortBy ([], 'x'), []) +}) + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/test/base/functions/test.number.js b/TaskServer/node_modules/ccxt/js/test/base/functions/test.number.js new file mode 100644 index 000000000..be3e8f49b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/base/functions/test.number.js @@ -0,0 +1,220 @@ +'use strict' + +const { numberToString, decimalToPrecision, ROUND, TRUNCATE, DECIMAL_PLACES, TICK_SIZE, PAD_WITH_ZERO, SIGNIFICANT_DIGITS } = require ('../../../../ccxt'); +const assert = require ('assert'); + +// ---------------------------------------------------------------------------- +// numberToString + +assert (numberToString (-7.8e-7) === '-0.00000078'); +assert (numberToString (7.8e-7) === '0.00000078'); +assert (numberToString (-17.805e-7) === '-0.0000017805'); +assert (numberToString (17.805e-7) === '0.0000017805'); +assert (numberToString (-7.0005e27) === '-7000500000000000000000000000'); +assert (numberToString (7.0005e27) === '7000500000000000000000000000'); +assert (numberToString (-7.9e27) === '-7900000000000000000000000000'); +assert (numberToString (7.9e27) === '7900000000000000000000000000'); +assert (numberToString (-12.345) === '-12.345'); +assert (numberToString (12.345) === '12.345'); +assert (numberToString (0) === '0'); +// this line breaks the test +// see https://github.com/ccxt/ccxt/issues/5744 +// assert (numberToString (0.00000001) === '0.00000001'); + +// ---------------------------------------------------------------------------- +// testDecimalToPrecisionTruncationToNDigitsAfterDot + +assert (decimalToPrecision ('12.3456000', TRUNCATE, 100, DECIMAL_PLACES) === '12.3456'); +assert (decimalToPrecision ('12.3456', TRUNCATE, 100, DECIMAL_PLACES) === '12.3456'); +assert (decimalToPrecision ('12.3456', TRUNCATE, 4, DECIMAL_PLACES) === '12.3456'); +assert (decimalToPrecision ('12.3456', TRUNCATE, 3, DECIMAL_PLACES) === '12.345'); +assert (decimalToPrecision ('12.3456', TRUNCATE, 2, DECIMAL_PLACES) === '12.34'); +assert (decimalToPrecision ('12.3456', TRUNCATE, 1, DECIMAL_PLACES) === '12.3'); +assert (decimalToPrecision ('12.3456', TRUNCATE, 0, DECIMAL_PLACES) === '12'); + +assert (decimalToPrecision ('0.0000001', TRUNCATE, 8, DECIMAL_PLACES) === '0.0000001'); +assert (decimalToPrecision ('0.00000001', TRUNCATE, 8, DECIMAL_PLACES) === '0.00000001'); + +assert (decimalToPrecision ('0.000000000', TRUNCATE, 9, DECIMAL_PLACES, PAD_WITH_ZERO) === '0.000000000'); +assert (decimalToPrecision ('0.000000001', TRUNCATE, 9, DECIMAL_PLACES, PAD_WITH_ZERO) === '0.000000001'); + +assert (decimalToPrecision ('12.3456', TRUNCATE, -1, DECIMAL_PLACES) === '10'); +assert (decimalToPrecision ('123.456', TRUNCATE, -1, DECIMAL_PLACES) === '120'); +assert (decimalToPrecision ('123.456', TRUNCATE, -2, DECIMAL_PLACES) === '100'); +assert (decimalToPrecision ('9.99999', TRUNCATE, -1, DECIMAL_PLACES) === '0'); +assert (decimalToPrecision ('99.9999', TRUNCATE, -1, DECIMAL_PLACES) === '90'); +assert (decimalToPrecision ('99.9999', TRUNCATE, -2, DECIMAL_PLACES) === '0'); + +assert (decimalToPrecision ('0', TRUNCATE, 0, DECIMAL_PLACES) === '0'); +assert (decimalToPrecision ('-0.9', TRUNCATE, 0, DECIMAL_PLACES) === '0'); + +// ---------------------------------------------------------------------------- +// testDecimalToPrecisionTruncationToNSignificantDigits + +assert (decimalToPrecision ('0.000123456700', TRUNCATE, 100, SIGNIFICANT_DIGITS) === '0.0001234567'); +assert (decimalToPrecision ('0.0001234567', TRUNCATE, 100, SIGNIFICANT_DIGITS) === '0.0001234567'); +assert (decimalToPrecision ('0.0001234567', TRUNCATE, 7, SIGNIFICANT_DIGITS) === '0.0001234567'); + +assert (decimalToPrecision ('0.000123456', TRUNCATE, 6, SIGNIFICANT_DIGITS) === '0.000123456'); +assert (decimalToPrecision ('0.000123456', TRUNCATE, 5, SIGNIFICANT_DIGITS) === '0.00012345'); +assert (decimalToPrecision ('0.000123456', TRUNCATE, 2, SIGNIFICANT_DIGITS) === '0.00012'); +assert (decimalToPrecision ('0.000123456', TRUNCATE, 1, SIGNIFICANT_DIGITS) === '0.0001'); + +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 10, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.0000987'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 8, SIGNIFICANT_DIGITS) === '123.00009'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 7, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.0000'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 6, SIGNIFICANT_DIGITS) === '123'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 5, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.00'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 4, SIGNIFICANT_DIGITS) === '123'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 4, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123.0'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 3, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '123'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 2, SIGNIFICANT_DIGITS) === '120'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 1, SIGNIFICANT_DIGITS) === '100'); +assert (decimalToPrecision ('123.0000987654', TRUNCATE, 1, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '100'); + +assert (decimalToPrecision ('1234', TRUNCATE, 5, SIGNIFICANT_DIGITS) === '1234'); +assert (decimalToPrecision ('1234', TRUNCATE, 5, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '1234.0'); +assert (decimalToPrecision ('1234', TRUNCATE, 4, SIGNIFICANT_DIGITS) === '1234'); +assert (decimalToPrecision ('1234', TRUNCATE, 4, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '1234'); +assert (decimalToPrecision ('1234.69', TRUNCATE, 0, SIGNIFICANT_DIGITS) === '0'); +assert (decimalToPrecision ('1234.69', TRUNCATE, 0, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0'); + +// ---------------------------------------------------------------------------- +// testDecimalToPrecisionRoundingToNDigitsAfterDot + +assert (decimalToPrecision ('12.3456000', ROUND, 100, DECIMAL_PLACES) === '12.3456'); +assert (decimalToPrecision ('12.3456', ROUND, 100, DECIMAL_PLACES) === '12.3456'); +assert (decimalToPrecision ('12.3456', ROUND, 4, DECIMAL_PLACES) === '12.3456'); +assert (decimalToPrecision ('12.3456', ROUND, 3, DECIMAL_PLACES) === '12.346'); +assert (decimalToPrecision ('12.3456', ROUND, 2, DECIMAL_PLACES) === '12.35'); +assert (decimalToPrecision ('12.3456', ROUND, 1, DECIMAL_PLACES) === '12.3'); +assert (decimalToPrecision ('12.3456', ROUND, 0, DECIMAL_PLACES) === '12'); + +// a problematic case in PHP +assert (decimalToPrecision ('10000', ROUND, 6, DECIMAL_PLACES) === '10000'); +assert (decimalToPrecision ('0.00003186', ROUND, 8, DECIMAL_PLACES) === '0.00003186'); + +assert (decimalToPrecision ('12.3456', ROUND, -1, DECIMAL_PLACES) === '10'); +assert (decimalToPrecision ('123.456', ROUND, -1, DECIMAL_PLACES) === '120'); +assert (decimalToPrecision ('123.456', ROUND, -2, DECIMAL_PLACES) === '100'); +assert (decimalToPrecision ('9.99999', ROUND, -1, DECIMAL_PLACES) === '10'); +assert (decimalToPrecision ('99.9999', ROUND, -1, DECIMAL_PLACES) === '100'); +assert (decimalToPrecision ('99.9999', ROUND, -2, DECIMAL_PLACES) === '100'); + +assert (decimalToPrecision ('9.999', ROUND, 3, DECIMAL_PLACES) === '9.999'); +assert (decimalToPrecision ('9.999', ROUND, 2, DECIMAL_PLACES) === '10'); +assert (decimalToPrecision ('9.999', ROUND, 2, DECIMAL_PLACES, PAD_WITH_ZERO) === '10.00'); +assert (decimalToPrecision ('99.999', ROUND, 2, DECIMAL_PLACES, PAD_WITH_ZERO) === '100.00'); +assert (decimalToPrecision ('-99.999', ROUND, 2, DECIMAL_PLACES, PAD_WITH_ZERO) === '-100.00'); + +// ---------------------------------------------------------------------------- +// testDecimalToPrecisionRoundingToNSignificantDigits + +assert (decimalToPrecision ('0.000123456700', ROUND, 100, SIGNIFICANT_DIGITS) === '0.0001234567'); +assert (decimalToPrecision ('0.0001234567', ROUND, 100, SIGNIFICANT_DIGITS) === '0.0001234567'); +assert (decimalToPrecision ('0.0001234567', ROUND, 7, SIGNIFICANT_DIGITS) === '0.0001234567'); + +assert (decimalToPrecision ('0.000123456', ROUND, 6, SIGNIFICANT_DIGITS) === '0.000123456'); +assert (decimalToPrecision ('0.000123456', ROUND, 5, SIGNIFICANT_DIGITS) === '0.00012346'); +assert (decimalToPrecision ('0.000123456', ROUND, 4, SIGNIFICANT_DIGITS) === '0.0001235'); +assert (decimalToPrecision ('0.00012', ROUND, 2, SIGNIFICANT_DIGITS) === '0.00012'); +assert (decimalToPrecision ('0.0001', ROUND, 1, SIGNIFICANT_DIGITS) === '0.0001'); + +assert (decimalToPrecision ('123.0000987654', ROUND, 7, SIGNIFICANT_DIGITS) === '123.0001'); +assert (decimalToPrecision ('123.0000987654', ROUND, 6, SIGNIFICANT_DIGITS) === '123'); + +assert (decimalToPrecision ('0.00098765', ROUND, 2, SIGNIFICANT_DIGITS) === '0.00099'); +assert (decimalToPrecision ('0.00098765', ROUND, 2, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0.00099'); + +assert (decimalToPrecision ('0.00098765', ROUND, 1, SIGNIFICANT_DIGITS) === '0.001'); +assert (decimalToPrecision ('0.00098765', ROUND, 10, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0.0009876500000'); + +assert (decimalToPrecision ('0.098765', ROUND, 1, SIGNIFICANT_DIGITS, PAD_WITH_ZERO) === '0.1'); + +assert (decimalToPrecision ('0', ROUND, 0, SIGNIFICANT_DIGITS) === '0'); +assert (decimalToPrecision ('-0.123', ROUND, 0, SIGNIFICANT_DIGITS) === '0'); + +assert (decimalToPrecision ('0.00000044', ROUND, 5, SIGNIFICANT_DIGITS) === '0.00000044'); + +// ---------------------------------------------------------------------------- +// testDecimalToPrecisionRoundingToTickSize + +assert (decimalToPrecision ('0.000123456700', ROUND, 0.00012, TICK_SIZE) === '0.00012'); +assert (decimalToPrecision ('0.0001234567', ROUND, 0.00013, TICK_SIZE) === '0.00013'); +assert (decimalToPrecision ('0.0001234567', TRUNCATE, 0.00013, TICK_SIZE) === '0'); +assert (decimalToPrecision ('101.000123456700', ROUND, 100, TICK_SIZE) === '100'); +assert (decimalToPrecision ('0.000123456700', ROUND, 100, TICK_SIZE) === '0'); +assert (decimalToPrecision ('165', TRUNCATE, 110, TICK_SIZE) === '110'); +assert (decimalToPrecision ('3210', TRUNCATE, 1110, TICK_SIZE) === '2220'); +assert (decimalToPrecision ('165', ROUND, 110, TICK_SIZE) === '220'); +assert (decimalToPrecision ('0.000123456789', ROUND, 0.00000012, TICK_SIZE) === '0.00012348'); +assert (decimalToPrecision ('0.000123456789', TRUNCATE, 0.00000012, TICK_SIZE) === '0.00012336'); + +assert (decimalToPrecision ('0.01', ROUND, 0.0001, TICK_SIZE, PAD_WITH_ZERO) === '0.0100'); +assert (decimalToPrecision ('0.01', TRUNCATE, 0.0001, TICK_SIZE, PAD_WITH_ZERO) === '0.0100'); + +assert (decimalToPrecision ('-0.000123456789', ROUND, 0.00000012, TICK_SIZE) === '-0.00012348'); +assert (decimalToPrecision ('-0.000123456789', TRUNCATE, 0.00000012, TICK_SIZE) === '-0.00012336'); +assert (decimalToPrecision ('-165', TRUNCATE, 110, TICK_SIZE) === '-110'); +assert (decimalToPrecision ('-165', ROUND, 110, TICK_SIZE) === '-220'); + +// ---------------------------------------------------------------------------- +// testDecimalToPrecisionNegativeNumbers + +assert (decimalToPrecision ('-0.123456', TRUNCATE, 5, DECIMAL_PLACES) === '-0.12345'); +assert (decimalToPrecision ('-0.123456', ROUND, 5, DECIMAL_PLACES) === '-0.12346'); + +// ---------------------------------------------------------------------------- +// decimalToPrecision: without dot / trailing dot + +assert (decimalToPrecision ('123', TRUNCATE, 0) === '123'); + +assert (decimalToPrecision ('123', TRUNCATE, 5, DECIMAL_PLACES) === '123'); +assert (decimalToPrecision ('123', TRUNCATE, 5, DECIMAL_PLACES, PAD_WITH_ZERO) === '123.00000'); + +assert (decimalToPrecision ('123.', TRUNCATE, 0, DECIMAL_PLACES) === '123'); +assert (decimalToPrecision ('123.', TRUNCATE, 5, DECIMAL_PLACES, PAD_WITH_ZERO) === '123.00000'); + +assert (decimalToPrecision ('0.', TRUNCATE, 0) === '0'); +assert (decimalToPrecision ('0.', TRUNCATE, 5, DECIMAL_PLACES, PAD_WITH_ZERO) === '0.00000'); + +// ---------------------------------------------------------------------------- +// decimalToPrecision: rounding for equidistant digits + +assert (decimalToPrecision ('1.44', ROUND, 1, DECIMAL_PLACES) === '1.4'); +assert (decimalToPrecision ('1.45', ROUND, 1, DECIMAL_PLACES) === '1.5'); +assert (decimalToPrecision ('1.45', ROUND, 0, DECIMAL_PLACES) === '1'); // not 2 + +// ---------------------------------------------------------------------------- +// negative precision only implemented so far in python +// pretty useless for decimal applications as anything |x| < 5 === 0 +// NO_PADDING and PAD_WITH_ZERO are ignored + +assert (decimalToPrecision ('5', ROUND, -1, DECIMAL_PLACES) === '10'); +assert (decimalToPrecision ('4.999', ROUND, -1, DECIMAL_PLACES) === '0'); +assert (decimalToPrecision ('0.0431531423', ROUND, -1, DECIMAL_PLACES) === '0'); +assert (decimalToPrecision ('-69.3', ROUND, -1, DECIMAL_PLACES) === '-70'); +assert (decimalToPrecision ('5001', ROUND, -4, DECIMAL_PLACES) === '10000'); +assert (decimalToPrecision ('4999.999', ROUND, -4, DECIMAL_PLACES) === '0'); + +assert (decimalToPrecision ('69.3', TRUNCATE, -2, DECIMAL_PLACES) === '0'); +assert (decimalToPrecision ('-69.3', TRUNCATE, -2, DECIMAL_PLACES) === '0'); +assert (decimalToPrecision ('69.3', TRUNCATE, -1, SIGNIFICANT_DIGITS) === '60'); +assert (decimalToPrecision ('-69.3', TRUNCATE, -1, SIGNIFICANT_DIGITS) === '-60'); +assert (decimalToPrecision ('69.3', TRUNCATE, -2, SIGNIFICANT_DIGITS) === '0'); +assert (decimalToPrecision ('1602000000000000000000', TRUNCATE, 3, SIGNIFICANT_DIGITS) === '1600000000000000000000'); + +// ---------------------------------------------------------------------------- +// testDecimalToPrecisionErrorHandling (todo) +// +// throws (() => +// decimalToPrecision ('123456.789', TRUNCATE, -2, DECIMAL_PLACES), +// 'negative precision is not yet supported') +// +// throws (() => +// decimalToPrecision ('foo'), +// "invalid number (contains an illegal character 'f')") +// +// throws (() => +// decimalToPrecision ('0.01', TRUNCATE, -1, TICK_SIZE), +// "TICK_SIZE cant be used with negative numPrecisionDigits") diff --git a/TaskServer/node_modules/ccxt/js/test/base/functions/test.time.js b/TaskServer/node_modules/ccxt/js/test/base/functions/test.time.js new file mode 100644 index 000000000..3d778e64d --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/base/functions/test.time.js @@ -0,0 +1,71 @@ +'use strict' + +/* ------------------------------------------------------------------------ */ + +const { setTimeout_safe + , timeout + , now + , isWindows } = require ('../../../../ccxt') + +const { strictEqual: equal } = require ('assert') + , approxEqual = (a, b) => equal (true, Math.abs (a - b) <= 20) + , rejectsWith = (msg, x) => x.then (() => { throw new Error ('not rejected!') }) + .catch (e => { equal (e.message, msg) }) + +/* ------------------------------------------------------------------------ */ + +it ('setTimeout_safe is working', (done) => { + + // TODO: make a more robust test that is not failing on certain machines under certain conditions + + if (isWindows) { + + // temporarily skip this test with appveyor + // todo: refix timer functions for windows properly + + done () + + } else { + + const start = now () + const calls = [] + + const brokenSetTimeout = (done, ms) => { + calls.push ({ when: now () - start, ms_asked: ms }) + return setTimeout (done, 100) // simulates a defect setTimeout implementation that sleeps wrong time (100ms always in this test) + } + + // ask to sleep 250ms + setTimeout_safe (() => { + approxEqual (calls[0].ms_asked, 250) + approxEqual (calls[1].ms_asked, 150) + approxEqual (calls[2].ms_asked, 50) + done () + }, 250, brokenSetTimeout) + + } +}) + +/* ------------------------------------------------------------------------ */ + +it ('setTimeout_safe canceling is working', (done) => { + + const brokenSetTimeout = (done, ms) => setTimeout (done, 100) // simulates a defect setTimeout implementation that sleeps wrong time (100ms always in this test) + + const clear = setTimeout_safe (() => { throw new Error ('shouldnt happen!') }, 250, brokenSetTimeout) + + setTimeout (() => { clear () }, 200) + setTimeout (() => { done () }, 400) +}) + +/* ------------------------------------------------------------------------ */ + +it ('timeout() is working', async () => { + + equal ('foo', await timeout (200, new Promise (resolve => setTimeout (() => resolve ('foo'), 100)))) + + await rejectsWith ('foo', timeout (100, Promise.reject (new Error ('foo')))) + await rejectsWith ('timed out', timeout (100, new Promise ((resolve, reject) => setTimeout (() => reject (new Error ('foo')), 200)))) +}) + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/test/base/functions/test.type.js b/TaskServer/node_modules/ccxt/js/test/base/functions/test.type.js new file mode 100644 index 000000000..a32a4fc04 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/base/functions/test.type.js @@ -0,0 +1,54 @@ +'use strict' + +/* ------------------------------------------------------------------------ */ + +const { safeFloat, safeInteger, safeValue } = require ('../../../../ccxt') +const { strictEqual: equal, deepEqual } = require ('assert') + +/* ------------------------------------------------------------------------ */ + +it ('safeFloat/safeInteger is robust', async () => { + + const $default = {} + + const fns = { safeFloat, safeInteger } + + for (const fn of ['safeFloat', 'safeInteger']) { + + equal (fns[fn] ({ 'x': false }, 'x', $default), $default) + equal (fns[fn] ({ 'x': true }, 'x', $default), $default) + equal (fns[fn] ({ 'x': [] }, 'x', $default), $default) + equal (fns[fn] ({ 'x': [0] }, 'x', $default), $default) + equal (fns[fn] ({ 'x': [1] }, 'x', $default), $default) + equal (fns[fn] ({ 'x': {} }, 'x', $default), $default) + equal (fns[fn] ({ 'x': Number.NaN }, 'x'), undefined) + equal (fns[fn] ({ 'x': Number.POSITIVE_INFINITY }, 'x'), undefined) + equal (fns[fn] ({ 'x': null }, 'x', undefined), undefined) + equal (fns[fn] ({ 'x': null }, 'x', $default), $default) + equal (fns[fn] ({ 'x': '1.0' }, 'x'), 1.0) + equal (fns[fn] ({ 'x': '-1.0' }, 'x'), -1.0) + equal (fns[fn] ({ 'x': 1.0 }, 'x'), 1.0) + equal (fns[fn] ({ 'x': 0 }, 'x'), 0) + equal (fns[fn] ({ 'x': undefined }, 'x', $default), $default) + equal (fns[fn] ({ 'x': "" }, 'x'), undefined) + equal (fns[fn] ({ 'x': "" }, 'x', 0), 0) + equal (fns[fn] ({}, 'x'), undefined) + equal (fns[fn] ({}, 'x', 0), 0) + } + + equal (safeFloat ({ 'x': 1.59999999 }, 'x'), 1.59999999) + equal (safeInteger ({ 'x': 1.59999999 }, 'x'), 1) +}) + +/* ------------------------------------------------------------------------ */ + +it ('safeValue works', () => { + + equal (safeValue ({}, 'foo'), undefined) + equal (safeValue ({}, 'foo', 'bar'), 'bar') + equal (safeValue ({ 'foo': 'bar' }, 'foo'), 'bar') + equal (safeValue ({ 'foo': '' }, 'foo'), '') + equal (safeValue ({ 'foo': 0 }, 'foo'), 0) +}) + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/test/base/test.base.js b/TaskServer/node_modules/ccxt/js/test/base/test.base.js new file mode 100644 index 000000000..7a5e05787 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/base/test.base.js @@ -0,0 +1,242 @@ +'use strict' + +/* ------------------------------------------------------------------------ */ + +global.log = require ('ololog') // for easier debugging + +/* ------------------------------------------------------------------------ */ + +const { Exchange, keys, values, unique, index, aggregate } = require ('../../../ccxt') +const { strictEqual: equal, deepEqual } = require ('assert') + +/* ------------------------------------------------------------------------ */ + +describe ('ccxt base code', () => { + +/* ------------------------------------------------------------------------ */ + + require ('./functions/test.generic') + require ('./functions/test.time') + require ('./functions/test.type') + // Some test files are transpiled to other languages and must be kept very simple so we put `it` call externally to them + // Some mocha reporters will swallow `assert` failures if they are outside of `it` clause so we must call `it` + it ('decimalToPrecision() works', () => { + require ('./functions/test.number') + }) + it ('date&time functions work', () => { + require('./functions/test.datetime') + }) + it ('cryptography code works', () => { + require('./functions/test.crypto') + }) + +/* ------------------------------------------------------------------------ */ + + it ('calculateFee() works', () => { + + const price = 100.00 + const amount = 10.00 + const taker = 0.0025 + const maker = 0.0010 + const fees = { taker, maker } + const market = { + 'id': 'foobar', + 'symbol': 'FOO/BAR', + 'base': 'FOO', + 'quote': 'BAR', + 'taker': taker, + 'maker': maker, + 'precision': { + 'amount': 8, + 'price': 8, + }, + } + + const exchange = new Exchange ({ + 'id': 'mock', + 'markets': { + 'FOO/BAR': market, + }, + }) + + Object.keys (fees).forEach (takerOrMaker => { + + const result = exchange.calculateFee (market['symbol'], 'limit', 'sell', amount, price, takerOrMaker, {}) + + deepEqual (result, { + 'type': takerOrMaker, + 'currency': 'BAR', + 'rate': fees[takerOrMaker], + 'cost': fees[takerOrMaker] * amount * price, + }) + }) + }) + +/* ------------------------------------------------------------------------ */ + + it.skip ('exchange config extension works', () => { + + + cost = { min: 0.001, max: 1000 } + precision = { amount: 3 } + const exchange = new binance ({ + 'markets': { + 'ETH/BTC': { limits: { cost }, precision }, + }, + }) + + deepEqual (exchange.markets['ETH/BTC'].limits.cost, cost) + deepEqual (exchange.markets['ETH/BTC'].precision, { price: 6, amount: 3 }) + deepEqual (exchange.markets['ETH/BTC'].symbol, 'ETH/BTC') + }) + +/* ------------------------------------------------------------------------ */ + + it ('aggregate() works', () => { + + const bids = [ + [ 789.1, 123.0 ], + [ 789.100, 123.0 ], + [ 123.0, 456.0 ], + [ 789.0, 123.0 ], + [ 789.10, 123.0 ], + ] + + const asks = [ + [ 123.0, 456.0 ], + [ 789.0, 123.0 ], + [ 789.10, 123.0 ], + ] + + deepEqual (aggregate (bids.sort ()), [ + [ 123.0, 456.0 ], + [ 789.0, 123.0 ], + [ 789.1, 369.0 ], + ]) + + deepEqual (aggregate (asks.sort ()), [ + [ 123.0, 456.0 ], + [ 789.0, 123.0 ], + [ 789.10, 123.0 ], + ]) + + deepEqual (aggregate ([]), []) + }) + +/* ------------------------------------------------------------------------ */ + + it ('parseBalance() works', () => { + + const exchange = new Exchange ({ + 'markets': { + 'ETH/BTC': { 'id': 'ETH/BTC', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', } + } + }) + + const input = { + 'ETH': { 'free': 10, 'used': 10, 'total': 20 }, + 'ZEC': { 'free': 0, 'used': 0, 'total': 0 }, + } + + const expected = { + 'ETH': { 'free': 10, 'used': 10, 'total': 20 }, + 'ZEC': { 'free': 0, 'used': 0, 'total': 0 }, + 'free': { + 'ETH': 10, + 'ZEC': 0, + }, + 'used': { + 'ETH': 10, + 'ZEC': 0, + }, + 'total': { + 'ETH': 20, + 'ZEC': 0, + }, + } + + const actual = exchange.parseBalance (input) + + deepEqual (actual, expected) + }) + +/* ------------------------------------------------------------------------ */ + + it ('camelCase/camel_case property conversion works', () => { + + const exchange = new Exchange ({ 'id': 'mock' }) + + const propsSeenBefore = index ( + ["isNode", "empty", "keys", "values", "extend", "clone", "index", "ordered", "unique", "keysort", "indexBy", "groupBy", "filterBy", "sortBy", "flatten", "pluck", "omit", "sum", "deepExtend", "uuid", "unCamelCase", "capitalize", "isNumber", "isArray", "isObject", "isString", "isStringCoercible", "isDictionary", "hasProps", "prop", "asFloat", "asInteger", "safeFloat", "safeInteger", "safeValue", "safeString", "decimal", "toFixed", "truncate", "truncateToString", "precisionFromString", "stringToBinary", "stringToBase64", "utf16ToBase64", "base64ToBinary", "base64ToString", "binaryToString", "binaryConcat", "urlencode", "rawencode", "urlencodeBase64", "hash", "hmac", "jwt", "time", "setTimeout_safe", "sleep", "TimedOut", "timeout", "throttle", "json", "unjson", "aggregate", "is_node", "index_by", "group_by", "filter_by", "sort_by", "deep_extend", "un_camel_case", "is_number", "is_array", "is_object", "is_string", "is_string_coercible", "is_dictionary", "has_props", "as_float", "as_integer", "safe_float", "safe_integer", "safe_value", "safe_string", "to_fixed", "truncate_to_string", "precision_from_string", "string_to_binary", "string_to_base64", "utf16To_base64", "base64To_binary", "base64To_string", "binary_to_string", "binary_concat", "urlencode_base64", "set_timeout_safe", "Timed_out", "encode", "decode", "userAgents", "headers", "proxy", "origin", "iso8601", "parse8601", "milliseconds", "microseconds", "seconds", "id", "enableRateLimit", "rateLimit", "parseJsonResponse", "substituteCommonCurrencyCodes", "parseBalanceFromOpenOrders", "verbose", "debug", "journal", "userAgent", "twofa", "timeframes", "hasPublicAPI", "hasPrivateAPI", "hasCORS", "hasDeposit", "hasFetchBalance", "hasFetchClosedOrders", "hasFetchCurrencies", "hasFetchMyTrades", "hasFetchOHLCV", "hasFetchOpenOrders", "hasFetchOrder", "hasFetchOrderBook", "hasFetchOrders", "hasFetchTicker", "hasFetchTickers", "hasFetchBidsAsks", "hasFetchTrades", "hasWithdraw", "hasCreateOrder", "hasCancelOrder", "apiKey", "secret", "uid", "login", "password", "requiredCredentials", "exceptions", "balance", "orderbooks", "tickers", "fees", "orders", "trades", "currencies", "last_http_response", "last_json_response", "arrayConcat", "market_id", "market_ids", "array_concat", "implode_params", "extract_params", "fetch_balance", "fetch_free_balance", "fetch_used_balance","fetch_total_balance", "fetch_l2_order_book", "fetch_order_book", "fetch_bids_asks", "fetch_tickers", "fetch_ticker", "fetch_trades", "fetch_order", "fetch_orders", "fetch_open_orders", "fetch_closed_orders", "fetch_order_status", "fetch_markets", "load_markets", "set_markets", "parse_balance", "parse_bid_ask", "parse_bids_asks", "parse_order_book", "parse_trades", "parse_orders", "parse_ohlcv", "parse_ohlcvs", "edit_limit_buy_order", "edit_limit_sell_order", "edit_limit_order", "edit_order", "create_limit_buy_order", "create_limit_sell_order", "create_market_buy_order", "create_market_sell_order", "create_order", "calculate_fee", "common_currency_code", "price_to_precision", "amount_to_precision", "amount_to_string", "fee_to_precision", "cost_to_precision", "constructor", "getMarket", "describe", "defaults", "nonce", "encodeURIComponent", "checkRequiredCredentials", "initRestRateLimiter", "defineRestApi", "fetch", "fetch2", "request", "handleErrors", "defaultErrorHandler", "handleRestErrors", "handleRestResponse", "setMarkets", "loadMarkets", "fetchBidsAsks", "fetchTickers", "fetchOrder", "fetchOrders", "fetchOpenOrders", "fetchClosedOrders", "fetchMyTrades", "fetchCurrencies", "fetchMarkets", "fetchOrderStatus", "account", "commonCurrencyCode", "currency", "market", "marketId", "marketIds", "symbol", "extractParams", "implodeParams", "url", "parseBidAsk", "parseBidsAsks", "fetchL2OrderBook", "parseOrderBook", "parseBalance", "fetchPartialBalance", "fetchFreeBalance", "fetchUsedBalance", "fetchTotalBalance", "filterBySinceLimit", "parseTrades", "parseOrders", "filterOrdersBySymbol", "parseOHLCV", "parseOHLCVs", "editLimitBuyOrder", "editLimitSellOrder", "editLimitOrder", "editOrder", "createLimitBuyOrder", "createLimitSellOrder", "createMarketBuyOrder", "createMarketSellOrder", "costToPrecision", "priceToPrecision", "amountToPrecision", "amountToLots", "feeToPrecision", "calculateFee", "Ymd", "YmdHMS"] + ) + + const props = index (Object.getOwnPropertyNames (exchange).concat ( + Object.getOwnPropertyNames (exchange.constructor.prototype))) + + for (const k of Array.from (propsSeenBefore)) + if (this[k] && !props.has (k)) + throw new Error (`missing prop: ${k}`) + + for (const k of Array.from (props)) + if (!propsSeenBefore.has (k)) + log.magenta.noLocate (`+ ${k}`) + + }) + +/* ------------------------------------------------------------------------ */ + + it ('camelCase/camel_case property conversion works #2', () => { + + class Derived extends Exchange {} + const derived = new Derived () + equal (typeof derived.load_markets, 'function') + }) + +/* ------------------------------------------------------------------------ */ + + it ('legacy .hasSomething maps to has.something automatically', () => { + + const exchange = new Exchange ({ + id: 'mock', + has: { + CORS: true, + publicAPI: false, + fetchDepositAddress: 'emulated' + } + }) + + equal (exchange.hasCORS, true) + equal (exchange.hasPublicAPI, false) + equal (exchange.hasFetchDepositAddress, true) + }) + +/* ------------------------------------------------------------------------ */ + + if (Exchange.hasWeb3 ()) { + it ('getZeroExOrderHashV2 return valid signature for example order', () => { + // based on https://github.com/0xProject/0x-monorepo/blob/development/python-packages/order_utils/test/test_generate_order_hash_hex.py + const exampleOrder = { + "makerAddress": "0x0000000000000000000000000000000000000000", + "takerAddress": "0x0000000000000000000000000000000000000000", + "senderAddress": "0x0000000000000000000000000000000000000000", + "feeRecipientAddress": "0x0000000000000000000000000000000000000000", + "makerAssetData": "0x0000000000000000000000000000000000000000", + "takerAssetData": "0x0000000000000000000000000000000000000000", + "salt": "0", + "makerFee": "0", + "takerFee": "0", + "makerAssetAmount": "0", + "takerAssetAmount": "0", + "expirationTimeSeconds": "0", + "exchangeAddress": "0x0000000000000000000000000000000000000000", + } + const expectedOrderHash = '0xfaa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422'; + class Derived extends Exchange {} + const derived = new Derived () + const orderHash = derived.getZeroExOrderHashV2(exampleOrder) + equal (orderHash, expectedOrderHash) + }) + } +}) + +/* ------------------------------------------------------------------------ */ diff --git a/TaskServer/node_modules/ccxt/js/test/errors/test.InsufficientFunds.js b/TaskServer/node_modules/ccxt/js/test/errors/test.InsufficientFunds.js new file mode 100644 index 000000000..fb0c3235b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/errors/test.InsufficientFunds.js @@ -0,0 +1,90 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , ccxt = require ('../../../ccxt.js') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ +// will try to place a buy order at the minimum price level on minimum amount possible +// will skip if balance is positive or market limits are not set + +module.exports = async (exchange, symbol, balance) => { + + if (!exchange.has.createOrder) { + log ('createOrder not supported') + return + } + + const markets = await exchange.loadMarkets () + const market = markets[symbol] + if (market.limits === undefined) { + log ('market.limits property is not set, will not test order creation') + return + } + + const { price, amount, cost } = market.limits + + if (price === undefined || amount === undefined || cost === undefined) { + log ('market.limits.[price|amount|cost] property is not set, will not test order creation') + return + } + + let minPrice = price.min + let minAmount = amount.min // will be adjusted co cover minCost if needed + const minCost = cost.min + + if (minPrice === undefined || minAmount === undefined || minCost === undefined) { + log ('min limits are not set, will not test order creation') + return + } + + if (minCost > minPrice * minAmount) { + minAmount = minCost / minPrice + } + + minPrice = exchange.priceToPrecision (symbol, minPrice) + minAmount = exchange.amountToPrecision (symbol, minAmount) + + if (balance === undefined) { + log ('balance is not set, cannot ensure safety, will not test order creation') + return + } + + const { base, quote } = market + if (balance[quote] !== undefined && balance[quote].total > 0) { + log ('balance is not empty, will not test order creation') + return + } + + try { + + log ('creating limit buy order...', symbol, minAmount, minPrice) + + let order = await exchange.createLimitBuyOrder (symbol, minAmount, minPrice) + + log ('order created although it should not had to - cleaning up') + + log (order) + + await exchange.cancelOrder (order.id, symbol) + + assert.fail () + + } catch (e) { + + if (e instanceof ccxt.InsufficientFunds) { + + log ('InsufficientFunds thrown as expected') + + } else { + + log ('InsufficientFunds failed, exception follows:') + throw e + } + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/errors/test.InvalidNonce.js b/TaskServer/node_modules/ccxt/js/test/errors/test.InvalidNonce.js new file mode 100644 index 000000000..53073249c --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/errors/test.InvalidNonce.js @@ -0,0 +1,67 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , ccxt = require ('../../../ccxt.js') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + log.green ('AuthenticationError (bad nonce) test...') + + const hasFetchBalance = exchange.has.fetchBalance + const hasFetchMyTrades = exchange.has.fetchMyTrades + const hasFetchOrders = exchange.has.fetchOrders + + if (hasFetchBalance || hasFetchMyTrades || hasFetchOrders) { + + // save the nonce temporarily and replace it with a fake one + const nonce = exchange.nonce + exchange.nonce = () => 1 + + try { + + // check if handleErrors() throws AuthenticationError if an exchange + // responds with an error on a bad nonce + // (still, some exchanges that require nonce silently eat bad nonce w/o an error) + + if (hasFetchBalance) + await exchange.fetchBalance () + else if (hasFetchMyTrades) + await exchange.fetchMyTrades (symbol, 0) + else + await exchange.fetchOrders (symbol) + + // restore the nonce so the caller may proceed in case bad nonce was accepted by an exchange + exchange.nonce = nonce + log.warn (exchange.id + ': AuthenticationError: bad nonce swallowed') + + } catch (e) { + + // restore the nonce so the caller may proceed in case the test failed + exchange.nonce = nonce + + if (e instanceof ccxt.AuthenticationError || e instanceof ccxt.InvalidNonce) { + + // it has thrown the exception as expected + log.green ('AuthenticationError test passed') + + } else { + + // rethrow an unexpected error if any + throw e + } + } + + } else { + + log (exchange.id + ' has no means of testing for bad nonce') + + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/errors/test.InvalidOrder.js b/TaskServer/node_modules/ccxt/js/test/errors/test.InvalidOrder.js new file mode 100644 index 000000000..5d6b0884b --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/errors/test.InvalidOrder.js @@ -0,0 +1,39 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , ccxt = require ('../../../ccxt.js') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + if (!exchange.has.createOrder) { + log ('createOrder not supported') + return + } + + try { + + await exchange.createLimitBuyOrder (symbol, 0, 0) + assert.fail () + + } catch (e) { + + if (e instanceof ccxt.InvalidOrder) { + + log ('InvalidOrder thrown as expected') + return + + } else { + + log ('InvalidOrder failed, exception follows:') + throw e + } + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/errors/test.OrderNotFound.js b/TaskServer/node_modules/ccxt/js/test/errors/test.OrderNotFound.js new file mode 100644 index 000000000..fd75a668c --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/errors/test.OrderNotFound.js @@ -0,0 +1,43 @@ +'use strict' + +// ---------------------------------------------------------------------------- + +const log = require ('ololog') + , ansi = require ('ansicolor').nice + , chai = require ('chai') + , ccxt = require ('../../../ccxt.js') + , expect = chai.expect + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +module.exports = async (exchange, symbol) => { + + if (!exchange.has.createOrder) { + log ('createOrder not supported -> test skipped') + return + } + + let id = 1 + + try { + + await exchange.cancelOrder (id, symbol) + + log ('test failed') + + assert.fail () + + } catch (e) { + + if (e instanceof ccxt.OrderNotFound) { + + log ('OrderNotFound thrown as expected') + + } else { + + log ('OrderNotFound test failed') + throw e + } + } +} \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/test/test.js b/TaskServer/node_modules/ccxt/js/test/test.js new file mode 100644 index 000000000..e8a879571 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/test.js @@ -0,0 +1,381 @@ +'use strict' + +/* ------------------------------------------------------------------------ */ + +const [processPath, , exchangeId = null, exchangeSymbol = null] = process.argv.filter (x => !x.startsWith ('--')) +const verbose = process.argv.includes ('--verbose') || false +const debug = process.argv.includes ('--debug') || false + +/* ------------------------------------------------------------------------ */ + +const asTable = require ('as-table') + , log = require ('ololog') + , ansi = require ('ansicolor').nice + , fs = require ('fs') + , ccxt = require ('../../ccxt.js') + , chai = require ('chai') + , assert = chai.assert + +/* ------------------------------------------------------------------------ */ + +const warn = log.bright.yellow.error // .error → stderr + +/* ------------------------------------------------------------------------ */ + +process.on ('uncaughtException', e => { log.bright.red.error (e); process.exit (1) }) +process.on ('unhandledRejection', e => { log.bright.red.error (e); process.exit (1) }) + +/* ------------------------------------------------------------------------ */ + +log.bright ('\nTESTING', { exchange: exchangeId, symbol: exchangeSymbol || 'all' }, '\n') + +/* ------------------------------------------------------------------------ */ + +let proxies = [ + '', + 'https://cors-anywhere.herokuapp.com/', + // 'https://crossorigin.me/', +] + +//----------------------------------------------------------------------------- + +const enableRateLimit = true + +const { Agent } = require ('https') + +const agent = new Agent ({ + ecdhCurve: 'auto', +}) + +const exchange = new (ccxt)[exchangeId] ({ + agent, + verbose, + enableRateLimit, + debug, + timeout: 20000, +}) + +//----------------------------------------------------------------------------- + +const tests = {} +const properties = Object.keys (exchange.has) +properties + .filter (property => fs.existsSync (__dirname + '/Exchange/test.' + property + '.js')) + .forEach (property => { + // eslint-disable-next-line import/no-dynamic-require + tests[property] = require (__dirname + '/Exchange/test.' + property + '.js') + }) + +const errors = require ('../base/errors.js') + +Object.keys (errors) + .filter (error => fs.existsSync (__dirname + '/errors/test.' + error + '.js')) + .forEach (error => { + // eslint-disable-next-line import/no-dynamic-require + tests[error] = require (__dirname + '/errors/test.' + error + '.js') + }) + +//----------------------------------------------------------------------------- + +const keysGlobal = 'keys.json' +const keysLocal = 'keys.local.json' + +const keysFile = fs.existsSync (keysLocal) ? keysLocal : keysGlobal +// eslint-disable-next-line import/no-dynamic-require +const settings = require (__dirname + '/../../' + keysFile)[exchangeId] + +if (settings) { + for (const key in settings) { + if (settings[key]) { + settings[key] = ccxt.deepExtend (exchange[key] || {}, settings[key]) + } + } +} + +Object.assign (exchange, settings) + +if (settings && settings.skip) { + log.error.bright ('[Skipped]', { exchange: exchangeId, symbol: exchangeSymbol || 'all' }) + process.exit () +} + +//----------------------------------------------------------------------------- + +let testSymbol = async (exchange, symbol) => { + + if (exchange.id !== 'coinmarketcap') { + await tests['fetchMarkets'] (exchange) + await tests['fetchCurrencies'] (exchange) + } + + await tests['fetchTicker'] (exchange, symbol) + await tests['fetchTickers'] (exchange, symbol) + await tests['fetchOHLCV'] (exchange, symbol) + await tests['fetchTrades'] (exchange, symbol) + + if (exchange.id === 'coinmarketcap') { + + log (await exchange.fetchTickers ()) + log (await exchange.fetchGlobal ()) + + } else if (exchange.id === 'coinbase') { + + // do nothing for now + + } else { + + await tests['fetchOrderBook'] (exchange, symbol) + await tests['fetchL2OrderBook'] (exchange, symbol) + await tests['fetchOrderBooks'] (exchange) + } +} + +//----------------------------------------------------------------------------- + +let loadExchange = async exchange => { + + let markets = await exchange.loadMarkets () + + assert (typeof exchange.markets === 'object', '.markets is not an object') + assert (Array.isArray (exchange.symbols), '.symbols is not an array') + assert (exchange.symbols.length > 0, '.symbols.length <= 0 (less than or equal to zero)') + assert (Object.keys (exchange.markets).length > 0, 'Object.keys (.markets).length <= 0 (less than or equal to zero)') + assert (exchange.symbols.length === Object.keys (exchange.markets).length, 'number of .symbols is not equal to the number of .markets') + + let symbols = [ + 'BTC/CNY', + 'BTC/USD', + 'BTC/USDT', + 'BTC/EUR', + 'BTC/ETH', + 'ETH/BTC', + 'BTC/JPY', + 'ETH/EUR', + 'ETH/JPY', + 'ETH/CNY', + 'LTC/CNY', + 'DASH/BTC', + 'DOGE/BTC', + 'BTC/AUD', + 'BTC/PLN', + 'USD/SLL', + 'BTC/RUB', + 'BTC/UAH', + 'LTC/BTC', + ] + + let result = exchange.symbols.filter (symbol => symbols.indexOf (symbol) >= 0) + + if (result.length > 0) + if (exchange.symbols.length > result.length) + result = result.join (', ') + ' + more...' + else + result = result.join (', ') + + log (exchange.symbols.length.toString ().bright.green, 'symbols', result) +} + +//----------------------------------------------------------------------------- + +let testExchange = async exchange => { + + const codes = [ + 'BTC', + 'ETH', + 'XRP', + 'LTC', + 'BCH', + 'EOS', + 'BNB', + 'BSV', + 'USDT', + 'ATOM', + 'BAT', + 'BTG', + 'DASH', + 'DOGE', + 'ETC', + 'IOTA', + 'LSK', + 'MKR', + 'NEO', + 'PAX', + 'QTUM', + 'TRX', + 'TUSD', + 'USD', + 'USDC', + 'WAVES', + 'XEM', + 'XMR', + 'ZEC', + 'ZRX', + ] + let code = codes[0] + for (let c in codes) { + if (c in exchange.currencies) { + code = c + } + } + + await loadExchange (exchange) + + let symbol = exchange.symbols[0] + const symbols = [ + 'BTC/USD', + 'BTC/USDT', + 'BTC/CNY', + 'BTC/EUR', + 'BTC/ETH', + 'ETH/BTC', + 'BTC/JPY', + 'LTC/BTC', + 'ZRX/WETH', + ] + for (let s in symbols) { + if (exchange.symbols.includes (symbols[s]) && + (('active' in exchange.markets[symbols[s]]) ? exchange.markets[symbols[s]]['active'] : true)) { + symbol = symbols[s] + break + } + } + + if (exchange.id === 'okex') { + // okex has different order creation params for spot and futures markets + // this will stick okex to spot market until there is a way to test + // several markets per exchange + symbol = 'BTC/USDT' + } + + log.green ('SYMBOL:', symbol) + if ((symbol.indexOf ('.d') < 0)) { + await testSymbol (exchange, symbol) + } + + if (!exchange.privateKey && (!exchange.apiKey || (exchange.apiKey.length < 1))) + return true + + exchange.checkRequiredCredentials () + + // move to testnet/sandbox if possible before accessing the balance + // if (exchange.urls['test']) + // exchange.urls['api'] = exchange.urls['test'] + + let balance = await tests['fetchBalance'] (exchange) + + await tests['fetchFundingFees'] (exchange) + await tests['fetchTradingFees'] (exchange) + await tests['fetchStatus'] (exchange) + + await tests['fetchOrders'] (exchange, symbol) + await tests['fetchOpenOrders'] (exchange, symbol) + await tests['fetchClosedOrders'] (exchange, symbol) + await tests['fetchMyTrades'] (exchange, symbol) + + if ('fetchLedger' in tests) { + await tests['fetchLedger'] (exchange, code) + } + + // const code = exchange.markets[symbol]['quote'] + // await tests['fetchTransactions'] (exchange, code) + // await tests['fetchDeposits'] (exchange, code) + // await tests['fetchWithdrawals'] (exchange, code) + + if (exchange.extendedTest) { + + await tests['InvalidNonce'] (exchange, symbol) + await tests['OrderNotFound'] (exchange, symbol) + await tests['InvalidOrder'] (exchange, symbol) + await tests['InsufficientFunds'] (exchange, symbol, balance) // danger zone - won't execute with non-empty balance + } + + // try { + // let marketSellOrder = + // await exchange.createMarketSellOrder (exchange.symbols[0], 1) + // console.log (exchange.id, 'ok', marketSellOrder) + // } catch (e) { + // console.log (exchange.id, 'error', 'market sell', e) + // } + + // try { + // let marketBuyOrder = await exchange.createMarketBuyOrder (exchange.symbols[0], 1) + // console.log (exchange.id, 'ok', marketBuyOrder) + // } catch (e) { + // console.log (exchange.id, 'error', 'market buy', e) + // } + + // try { + // let limitSellOrder = await exchange.createLimitSellOrder (exchange.symbols[0], 1, 3000) + // console.log (exchange.id, 'ok', limitSellOrder) + // } catch (e) { + // console.log (exchange.id, 'error', 'limit sell', e) + // } + + // try { + // let limitBuyOrder = await exchange.createLimitBuyOrder (exchange.symbols[0], 1, 3000) + // console.log (exchange.id, 'ok', limitBuyOrder) + // } catch (e) { + // console.log (exchange.id, 'error', 'limit buy', e) + // } +} + +//----------------------------------------------------------------------------- + +let tryAllProxies = async function (exchange, proxies) { + + let currentProxy = 0 + let maxRetries = proxies.length + + if (settings && ('proxy' in settings)) + currentProxy = proxies.indexOf (settings.proxy) + + for (let numRetries = 0; numRetries < maxRetries; numRetries++) { + + try { + + exchange.proxy = proxies[currentProxy] + + // add random origin for proxies + if (exchange.proxy.length > 0) + exchange.origin = exchange.uuid () + + await testExchange (exchange) + break + + } catch (e) { + + currentProxy = ++currentProxy % proxies.length + if (e instanceof ccxt.DDoSProtection) { + warn ('[DDoS Protection]' + e.message.slice (0, 200)) + } else if (e instanceof ccxt.RequestTimeout) { + warn ('[Request Timeout] ' + e.message.slice (0, 200)) + } else if (e instanceof ccxt.ExchangeNotAvailable) { + warn ('[Exchange Not Available] ' + e.message.slice (0, 200)) + } else if (e instanceof ccxt.AuthenticationError) { + warn ('[Authentication Error] ' + e.message.slice (0, 200)) + return + } else if (e instanceof ccxt.InvalidNonce) { + warn ('[Invalid Nonce] ' + e.message.slice (0, 200)) + return + } else { + throw e + } + } + } +} + +//----------------------------------------------------------------------------- + +;(async function test () { + + if (exchangeSymbol) { + + await loadExchange (exchange) + await testSymbol (exchange, exchangeSymbol) + + } else { + + await tryAllProxies (exchange, proxies) + } + +}) () diff --git a/TaskServer/node_modules/ccxt/js/test/test.timeout_hang.js b/TaskServer/node_modules/ccxt/js/test/test.timeout_hang.js new file mode 100644 index 000000000..41cdb87d8 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/test/test.timeout_hang.js @@ -0,0 +1,12 @@ +// run with `node test_timeout_hang` +// TODO: integrate with CI tests somehow... + +const { timeout } = require ('../base/functions') + +;(async function () { + + await timeout (10000, Promise.resolve ('foo')) + + console.log ('Look ma, no hangs!') // should terminate the process immediately.. + +}) () \ No newline at end of file diff --git a/TaskServer/node_modules/ccxt/js/theocean.js b/TaskServer/node_modules/ccxt/js/theocean.js new file mode 100644 index 000000000..062ddd4ec --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/theocean.js @@ -0,0 +1,911 @@ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, AuthenticationError, InvalidOrder, OrderNotFound, NotSupported, OrderNotFillable, InvalidAddress, InsufficientFunds } = require ('./base/errors'); + +module.exports = class theocean extends Exchange { + describe () { + this.checkRequiredDependencies (); + return this.deepExtend (super.describe (), { + 'id': 'theocean', + 'name': 'The Ocean', + 'countries': [ 'US' ], + 'rateLimit': 3000, + 'version': 'v1', + 'requiresWeb3': true, + 'timeframes': { + '5m': '300', + '15m': '900', + '1h': '3600', + '6h': '21600', + '1d': '86400', + }, + 'has': { + 'cancelAllOrders': true, + 'CORS': false, // ? + 'fetchClosedOrders': true, + 'fetchOHLCV': false, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchTickers': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/43103756-d56613ce-8ed7-11e8-924e-68f9d4bcacab.jpg', + 'api': 'https://api.theocean.trade', + 'www': 'https://theocean.trade', + 'doc': 'https://docs.theocean.trade', + 'fees': 'https://theocean.trade/fees', + }, + 'api': { + 'public': { + 'get': [ + 'fee_components', + 'token_pairs', + 'ticker', + 'tickers', + 'candlesticks', + 'candlesticks/intervals', + 'trade_history', + 'order_book', + 'order/{orderHash}', + 'version', + ], + }, + 'private': { + 'get': [ + 'balance', + 'available_balance', + 'order_history', + 'order/unsigned', + 'order/unsigned/market', + ], + 'post': [ + 'order', + ], + 'delete': [ + 'order/{orderHash}', + 'order', + ], + }, + }, + 'exceptions': { + 'exact': { + 'Order not found': OrderNotFound, // {"message":"Order not found","errors":...} + }, + 'broad': { + "Price can't exceed 8 digits in precision.": InvalidOrder, // {"message":"Price can't exceed 8 digits in precision.","type":"paramPrice"} + 'Order cannot be canceled': InvalidOrder, // {"message":"Order cannot be canceled","type":"General error"} + 'Greater than available wallet balance.': InsufficientFunds, + 'Fillable amount under minimum': InvalidOrder, // {"message":"Fillable amount under minimum WETH trade size.","type":"paramQuoteTokenAmount"} + 'Fillable amount over maximum': InvalidOrder, // {"message":"Fillable amount over maximum TUSD trade size.","type":"paramQuoteTokenAmount"} + "Schema validation failed for 'params'": BadRequest, // // {"message":"Schema validation failed for 'params'"} + 'Service Temporarily Unavailable': ExchangeNotAvailable, + }, + }, + 'options': { + 'decimals': {}, + 'fetchOrderMethod': 'fetch_order_from_history', + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetTokenPairs (params); + // + // [ + // "baseToken": { + // "symbol": "ZRX", + // "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570", + // "name": "0x Protocol Token", + // "decimals": "18", + // "minAmount": "10000000000000000000", + // "maxAmount": "10000000000000000000000", + // "precision": "-8" + // }, + // "quoteToken": { + // "symbol": "ETH", + // "address": "0xd0a1e359811322d97991e03f863a0c30c2cf029c", + // "name": "Ether Token", + // "decimals": "18", + // "minAmount": "20000000000000000", + // "maxAmount": "20000000000000000000", + // "precision": "-8" + // } + // ] + // + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const baseToken = this.safeValue (market, 'baseToken', {}); + const quoteToken = this.safeValue (market, 'quoteToken', {}); + const baseId = this.safeString (baseToken, 'address'); + const quoteId = this.safeString (quoteToken, 'address'); + const base = this.safeCurrencyCode (this.safeString (baseToken, 'symbol')); + const quote = this.safeCurrencyCode (this.safeString (quoteToken, 'symbol')); + const symbol = base + '/' + quote; + const id = baseId + '/' + quoteId; + const baseDecimals = this.safeInteger (baseToken, 'decimals'); + const quoteDecimals = this.safeInteger (quoteToken, 'decimals'); + this.options['decimals'][base] = baseDecimals; + this.options['decimals'][quote] = quoteDecimals; + const precision = { + 'amount': -parseInt (baseToken['precision']), + 'price': -parseInt (quoteToken['precision']), + }; + const amountLimits = { + 'min': this.fromWei (this.safeString (baseToken, 'minAmount'), 'ether', baseDecimals), + 'max': this.fromWei (this.safeString (baseToken, 'maxAmount'), 'ether', baseDecimals), + }; + const priceLimits = { + 'min': undefined, + 'max': undefined, + }; + const costLimits = { + 'min': this.fromWei (this.safeString (quoteToken, 'minAmount'), 'ether', quoteDecimals), + 'max': this.fromWei (this.safeString (quoteToken, 'maxAmount'), 'ether', quoteDecimals), + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const active = true; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) { + const baseDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); + return [ + this.safeTimestamp (ohlcv, 'startTime'), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.fromWei (this.safeString (ohlcv, 'baseVolume'), 'ether', baseDecimals), + ]; + } + + async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + 'interval': this.timeframes[timeframe], + }; + if (since === undefined) { + throw new ExchangeError (this.id + ' fetchOHLCV requires a since argument'); + } + since = parseInt (since); + request['startTime'] = since; + const response = await this.publicGetCandlesticks (this.extend (request, params)); + // + // [ + // { + // "high": "100.52", + // "low": "97.23", + // "open": "98.45", + // "close": "99.23", + // "baseVolume": "2400000000000000000000", + // "quoteVolume": "1200000000000000000000", + // "startTime": "1512929323784" + // }, + // { + // "high": "100.52", + // "low": "97.23", + // "open": "98.45", + // "close": "99.23", + // "volume": "2400000000000000000000", + // "startTime": "1512929198980" + // } + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalanceByCode (code, params = {}) { + if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { + throw new InvalidAddress (this.id + ' fetchBalanceByCode() requires the .walletAddress to be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377"'); + } + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'walletAddress': this.walletAddress.toLowerCase (), + 'tokenAddress': currency['id'], + }; + const response = await this.privateGetBalance (this.extend (request, params)); + // + // {"available":"0","committed":"0","total":"0"} + // + const decimals = this.safeInteger (this.options['decimals'], code, 18); + const free = this.fromWei (this.safeString (response, 'available'), 'ether', decimals); + const used = this.fromWei (this.safeString (response, 'committed'), 'ether', decimals); + const total = this.fromWei (this.safeString (response, 'total'), 'ether', decimals); + return { + 'free': free, + 'used': used, + 'total': total, + }; + } + + async fetchBalance (params = {}) { + if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { + throw new InvalidAddress (this.id + ' fetchBalance() requires the .walletAddress to be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377"'); + } + let codes = this.safeValue (this.options, 'fetchBalanceCurrencies'); + if (codes === undefined) { + codes = this.safeValue (params, 'codes'); + } + if ((codes === undefined) || (!Array.isArray (codes))) { + throw new ExchangeError (this.id + ' fetchBalance() requires a `codes` parameter (an array of currency codes)'); + } + await this.loadMarkets (); + const result = {}; + for (let i = 0; i < codes.length; i++) { + const code = codes[i]; + result[code] = await this.fetchBalanceByCode (code); + } + return this.parseBalance (result); + } + + parseBidAsk (bidask, priceKey = 0, amountKey = 1, market = undefined) { + if (market === undefined) { + throw new ArgumentsRequired (this.id + ' parseBidAsk requires a market argument'); + } + const price = parseFloat (bidask[priceKey]); + const amountDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); + const amount = this.fromWei (bidask[amountKey], 'ether', amountDecimals); + return [ price, amount ]; + } + + parseOrderBook (orderbook, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1, market = undefined) { + const result = { + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + const sides = [ bidsKey, asksKey ]; + for (let i = 0; i < sides.length; i++) { + const side = sides[i]; + const orders = []; + const bidasks = this.safeValue (orderbook, side); + for (let k = 0; k < bidasks.length; k++) { + orders.push (this.parseBidAsk (bidasks[k], priceKey, amountKey, market)); + } + result[side] = orders; + } + result[bidsKey] = this.sortBy (result[bidsKey], 0, true); + result[asksKey] = this.sortBy (result[asksKey], 0); + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + }; + if (limit !== undefined) { + request['depth'] = limit; + } + const response = await this.publicGetOrderBook (this.extend (request, params)); + // + // { + // "bids": [ + // { orderHash: '0xe2b7f80198edb561cc66cd85cb8e5f420073cf1e5143193d8add8774bd8236c4', + // price: '30', + // availableAmount: '500000000000000000', + // creationTimestamp: '1547193525', + // expirationTimestampInSec: '1549789124' + // } + // ], + // "asks": [ + // { orderHash: '0xe2b7f80198edb561cc66cd85cb8e5f420073cf1e5143193d8add8774bd8236c4', + // price: '30', + // availableAmount: '500000000000000000', + // creationTimestamp: '1547193525', + // expirationTimestampInSec: '1549789124' + // } + // ] + // } + // + return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'availableAmount', market); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "bid": "0.00050915", + // "ask": "0.00054134", + // "last": "0.00052718", + // "volume": "3000000000000000000", + // "timestamp": "1512929327792" + // } + // + const timestamp = parseInt (this.safeInteger (ticker, 'timestamp') / 1000); + let symbol = undefined; + let base = undefined; + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + } + const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); + const baseVolume = this.fromWei (this.safeString (ticker, 'volume'), 'ether', baseDecimals); + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': undefined, + 'low': undefined, + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': this.safeFloat (ticker, 'priceChange'), + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTickers (params); + // + // [{ + // "baseTokenAddress": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d", + // "quoteTokenAddress": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a", + // "ticker": { + // "bid": "0.00050915", + // "ask": "0.00054134", + // "last": "0.00052718", + // "volume": "3000000000000000000", + // "timestamp": "1512929327792" + // } + // }] + // + const result = {}; + for (let i = 0; i < tickers.length; i++) { + const ticker = tickers[i]; + const baseId = this.safeString (ticker, 'baseTokenAddress'); + const quoteId = this.safeString (ticker, 'quoteTokenAddress'); + const marketId = baseId + '/' + quoteId; + let market = undefined; + let symbol = marketId; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker['ticker'], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + }; + const response = await this.publicGetTicker (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades + // + // { + // "id": "37212", + // "transactionHash": "0x5e6e75e1aa681b51b034296f62ac19be7460411a2ad94042dd8ba637e13eac0c", + // "amount": "300000000000000000", + // "price": "0.00052718", + // ------- they also have a "confirmed" status here ↓ ----------------- + // "status": "filled", // filled | settled | failed + // "lastUpdated": "1520265048996" + // } + // + // parseOrder trades (timeline "actions", "fills") + // + // { action: "confirmed", + // amount: "1000000000000000000", + // intentID: "MARKET_INTENT:90jjw2s7gj90jjw2s7gkjjw2s7gl", + // txHash: "0x043488fdc3f995bf9e632a32424e41ed126de90f8cb340a1ff006c2a74ca8336", + // blockNumber: "8094822", + // timestamp: "1532261686" } + // + let timestamp = this.safeInteger (trade, 'lastUpdated'); + if (timestamp !== undefined) { + timestamp /= 1000; + } + const price = this.safeFloat (trade, 'price'); + const id = this.safeString (trade, 'id'); + const side = this.safeString (trade, 'side'); + let symbol = undefined; + let base = undefined; + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + } + const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); + const amount = this.fromWei (this.safeString (trade, 'amount'), 'ether', baseDecimals); + let cost = undefined; + if (amount !== undefined && price !== undefined) { + cost = amount * price; + } + const takerOrMaker = 'taker'; + return { + 'id': id, + 'order': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'baseTokenAddress': market['baseId'], + 'quoteTokenAddress': market['quoteId'], + }; + const response = await this.publicGetTradeHistory (this.extend (request, params)); + // + // [ + // { + // "id": "37212", + // "transactionHash": "0x5e6e75e1aa681b51b034296f62ac19be7460411a2ad94042dd8ba637e13eac0c", + // "amount": "300000000000000000", + // "price": "0.00052718", + // "status": "filled", // filled | settled | failed + // "lastUpdated": "1520265048996" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + const errorMessage = this.id + ' createOrder() requires `exchange.walletAddress` and `exchange.privateKey`. The .walletAddress should be a "0x"-prefixed hexstring like "0xbF2d65B3b2907214EEA3562f21B80f6Ed7220377". The .privateKey for that wallet should be a "0x"-prefixed hexstring like "0xe4f40d465efa94c98aec1a51f574329344c772c1bce33be07fa20a56795fdd09".'; + if (!this.walletAddress || (this.walletAddress.indexOf ('0x') !== 0)) { + throw new InvalidAddress (errorMessage); + } + if (!this.privateKey || (this.privateKey.indexOf ('0x') !== 0)) { + throw new InvalidAddress (errorMessage); + } + const orderParams = await this.fetchOrderParamsToSign (symbol, type, side, amount, price, params); + const unsignedOrder = orderParams['unsignedZeroExOrder']; + if (unsignedOrder === undefined) { + throw new OrderNotFillable (this.id + ' ' + type + ' order to ' + side + ' ' + symbol + ' is not fillable at the moment'); + } + const signedOrder = await this.signZeroExOrderV2 (unsignedOrder, this.privateKey); + const id = this.safeString (signedOrder, 'orderHash'); + await this.postSignedOrder (signedOrder, orderParams, params); + const order = await this.fetchOrder (id); + order['type'] = type; + return order; + } + + async fetchOrderParamsToSign (symbol, type, side, amount, price = undefined, params = {}) { + if (side !== 'buy' && side !== 'sell') { + throw new ExchangeError (side + ' is not valid side param. Use \'buy\' or \'sell\''); + } + if (type !== 'market' && type !== 'limit') { + throw new ExchangeError (type + ' is not valid type param. Use \'market\' or \'limit\''); + } + if (type === 'limit' && price === undefined) { + throw new ExchangeError ('Price is not provided for limit order'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const baseDecimals = this.safeInteger (this.options['decimals'], market['base'], 18); + const request = { + 'walletAddress': this.walletAddress.toLowerCase (), // Your Wallet Address + 'baseTokenAddress': market['baseId'], // Base token address + 'quoteTokenAddress': market['quoteId'], // Quote token address + 'side': side, // "buy" or "sell" + 'amount': this.toWei (this.amountToPrecision (symbol, amount), 'ether', baseDecimals), // Base token amount in wei + }; + let method = undefined; + if (type === 'limit') { + method = 'privateGetOrderUnsigned'; + request['price'] = this.priceToPrecision (symbol, price); + } else if (type === 'market') { + method = 'privateGetOrderUnsignedMarket'; + } else { + throw new ExchangeError ('Unsupported order type: ' + type); + } + const response = await this[method] (this.extend (request, params)); + return response; + } + + async postSignedOrder (signedOrder, requestParams, params = {}) { + let request = requestParams; + request['signedZeroExOrder'] = signedOrder; + request = this.omit (request, 'unsignedZeroExOrder'); + const response = await this.privatePostOrder (this.extend (request, params)); + return response; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderHash': id, + }; + const response = await this.privateDeleteOrderOrderHash (this.extend (request, params)); + // + // { + // "canceledOrder": { + // "orderHash": "0x3d6b287c1dc79262d2391ae2ca9d050fdbbab2c8b3180e4a46f9f321a7f1d7a9", + // "amount": "100000000000" + // } + // } + // + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + } + return this.extend (this.parseOrder (response['canceledOrder'], market), { + 'status': 'canceled', + }); + } + + async cancelAllOrders (symbol = undefined, params = {}) { + const response = await this.privateDeleteOrder (params); + // + // [{ + // "canceledOrder": { + // "orderHash": "0x3d6b287c1dc79262d2391ae2ca9d050fdbbab2c8b3180e4a46f9f321a7f1d7a9", + // "amount": "100000000000" + // } + // }] + // + return response; + } + + parseOrder (order, market = undefined) { + const zeroExOrder = this.safeValue (order, 'zeroExOrder'); + let id = this.safeString (order, 'orderHash'); + if ((id === undefined) && (zeroExOrder !== undefined)) { + id = this.safeString (zeroExOrder, 'orderHash'); + } + const side = this.safeString (order, 'side'); + const type = this.safeString (order, 'type'); // injected from outside + let timestamp = this.safeInteger (order, 'creationTimestamp'); + if (timestamp !== 'undefined') { + timestamp = parseInt (timestamp / 1000); + } + let symbol = undefined; + const baseId = this.safeString (order, 'baseTokenAddress'); + const quoteId = this.safeString (order, 'quoteTokenAddress'); + let marketId = undefined; + if (baseId !== undefined && quoteId !== undefined) { + marketId = baseId + '/' + quoteId; + } + market = this.safeValue (this.markets_by_id, marketId, market); + let base = undefined; + if (market !== undefined) { + symbol = market['symbol']; + base = market['base']; + } + const baseDecimals = this.safeInteger (this.options['decimals'], base, 18); + const price = this.safeFloat (order, 'price'); + const filledAmount = this.fromWei (this.safeString (order, 'filledAmount'), 'ether', baseDecimals); + const settledAmount = this.fromWei (this.safeString (order, 'settledAmount'), 'ether', baseDecimals); + const confirmedAmount = this.fromWei (this.safeString (order, 'confirmedAmount'), 'ether', baseDecimals); + const failedAmount = this.fromWei (this.safeString (order, 'failedAmount'), 'ether', baseDecimals); + const deadAmount = this.fromWei (this.safeString (order, 'deadAmount'), 'ether', baseDecimals); + const prunedAmount = this.fromWei (this.safeString (order, 'prunedAmount'), 'ether', baseDecimals); + const amount = this.fromWei (this.safeString (order, 'initialAmount'), 'ether', baseDecimals); + const filled = this.sum (filledAmount, settledAmount, confirmedAmount); + let remaining = undefined; + let lastTradeTimestamp = undefined; + const timeline = this.safeValue (order, 'timeline'); + let trades = undefined; + let status = undefined; + if (timeline !== undefined) { + const numEvents = timeline.length; + if (numEvents > 0) { + const timelineEventsGroupedByAction = this.groupBy (timeline, 'action'); + if ('error' in timelineEventsGroupedByAction) { + status = 'failed'; + } + if ('filled' in timelineEventsGroupedByAction) { + const fillEvents = this.safeValue (timelineEventsGroupedByAction, 'filled'); + const numFillEvents = fillEvents.length; + lastTradeTimestamp = this.safeInteger (fillEvents[numFillEvents - 1], 'timestamp'); + lastTradeTimestamp = (lastTradeTimestamp !== undefined) ? lastTradeTimestamp : lastTradeTimestamp; + trades = []; + for (let i = 0; i < numFillEvents; i++) { + const trade = this.parseTrade (this.extend (fillEvents[i], { + 'price': price, + }), market); + trades.push (this.extend (trade, { + 'order': id, + 'type': type, + 'side': side, + })); + } + } + } + } + let cost = undefined; + if (filled !== undefined) { + if (remaining === undefined) { + if (amount !== undefined) { + remaining = amount - filled; + } + } + if (price !== undefined) { + cost = filled * price; + } + } + let fee = undefined; + const feeCost = this.safeString (order, 'feeAmount'); + if (feeCost !== undefined) { + const feeOption = this.safeString (order, 'feeOption'); + let feeCurrency = undefined; + if (feeOption === 'feeInNative') { + if (market !== undefined) { + feeCurrency = market['base']; + } + } else if (feeOption === 'feeInZRX') { + feeCurrency = 'ZRX'; + } else { + throw new NotSupported (this.id + ' encountered an unsupported order fee option: ' + feeOption); + } + const feeDecimals = this.safeInteger (this.options['decimals'], feeCurrency, 18); + fee = { + 'cost': this.fromWei (feeCost, 'ether', feeDecimals), + 'currency': feeCurrency, + }; + } + const amountPrecision = market ? market['precision']['amount'] : 8; + if (remaining !== undefined) { + if (status === undefined) { + status = 'open'; + const rest = remaining - failedAmount - deadAmount - prunedAmount; + if (rest < Math.pow (10, -amountPrecision)) { + status = (filled < amount) ? 'canceled' : 'closed'; + } + } + } + const result = { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + return result; + } + + async fetchOpenOrder (id, symbol = undefined, params = {}) { + const method = this.options['fetchOrderMethod']; + return await this[method] (id, symbol, this.extend ({ + 'openAmount': 1, + }, params)); + } + + async fetchClosedOrder (id, symbol = undefined, params = {}) { + const method = this.options['fetchOrderMethod']; + return await this[method] (id, symbol, this.extend (params)); + } + + async fetchOrderFromHistory (id, symbol = undefined, params = {}) { + const request = { + 'orderHash': id, + }; + const orders = await this.fetchOrders (symbol, undefined, undefined, this.extend (request, params)); + const ordersById = this.indexBy (orders, 'id'); + if (id in ordersById) { + return ordersById[id]; + } + throw new OrderNotFound (this.id + ' could not find order ' + id + ' in order history'); + } + + async fetchOrderById (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderHash': id, + }; + const response = await this.publicGetOrderOrderHash (this.extend (request, params)); + // { + // baseTokenAddress: '0xb18845c260f680d5b9d84649638813e342e4f8c9', + // quoteTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + // side: 'sell', + // price: '30', + // feeTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + // amount: '500000000000000000', + // created: '1547194003', + // expires: '1549786003', + // zeroExOrder: { + // salt: '71810414258284992779348693906799008280152689028521273772736250669496045815907', + // maker: '0xfa1a3371bcbfcf3deaa8a6f67784bfbe5b886d7f', + // taker: '0x77b18613579d49f252bd237ef113884eb37a7090', + // makerFee: '0', + // takerFee: '0', + // orderHash: '0x368540323af55868dd9ce6ac248e6a91d9b7595252ca061c4ada7612b09af1cf', + // feeRecipient: '0x88a64b5e882e5ad851bea5e7a3c8ba7c523fecbe', + // makerTokenAmount: '500000000000000000', + // takerTokenAmount: '14845250714350000000', + // makerTokenAddress: '0xb18845c260f680d5b9d84649638813e342e4f8c9', + // takerTokenAddress: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + // exchangeContractAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', + // expirationUnixTimestampSec: '1549789602' + // }, + // feeAmount: '154749285650000000', + // feeOption: 'feeInNative', + // cancelAfter: '1549786003' + // } + return this.parseOrder (response); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + const request = { + 'orderHash': id, + }; + const orders = await this.fetchOrders (symbol, undefined, undefined, this.extend (request, params)); + const numOrders = orders.length; + if (numOrders !== 1) { + throw new OrderNotFound (this.id + ' order ' + id + ' not found'); + } + return orders[0]; + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['baseTokenAddress'] = market['baseId']; + request['quoteTokenAddress'] = market['quoteId']; + } + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.privateGetOrderHistory (this.extend (request, params)); + // + // [ + // { + // "orderHash": "0x94629386298dee69ae63cd3e414336ae153b3f02cffb9ffc53ad71e166615618", + // "baseTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d", + // "quoteTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990", + // "side": "buy", + // "openAmount": "10000000000000000000", + // "filledAmount": "0", + // "reservedAmount": "0", + // "settledAmount": "0", + // "confirmedAmount": "0", + // "deadAmount": "0", + // "price": "0.00050915", + // "timeline": [ + // { + // "action": "placed", + // "amount": "10000000000000000000", + // "timestamp": "1512929327792" + // } + // ] + // } + // ] + // + return this.parseOrders (response, undefined, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openAmount': 1, // returns open orders with remaining openAmount >= 1 + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'openAmount': 0, // returns closed orders with remaining openAmount === 0 + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const timestamp = this.seconds ().toString (); + let prehash = this.apiKey + timestamp + method; + if (method === 'POST') { + body = this.json (query); + prehash += body; + } else { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + prehash += this.json ({}); + } + const signature = this.hmac (this.encode (prehash), this.encode (this.secret), 'sha256', 'base64'); + headers = { + 'TOX-ACCESS-KEY': this.apiKey, + 'TOX-ACCESS-SIGN': signature, + 'TOX-ACCESS-TIMESTAMP': timestamp, + 'Content-Type': 'application/json', + }; + } else if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // code 401 and plain body 'Authentication failed' (with single quotes) + // this error is sent if you do not submit a proper Content-Type + if (body === "'Authentication failed'") { + throw new AuthenticationError (this.id + ' ' + body); + } + const message = this.safeString (response, 'message'); + if (message !== undefined) { + // + // {"message":"Schema validation failed for 'query'","errors":[{"name":"required","argument":"startTime","message":"requires property \"startTime\"","instance":{"baseTokenAddress":"0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570","quoteTokenAddress":"0xd0a1e359811322d97991e03f863a0c30c2cf029c","interval":"300"},"property":"instance"}]} + // {"message":"Logic validation failed for 'query'","errors":[{"message":"startTime should be between 0 and current date","type":"startTime"}]} + // {"message":"Order not found","errors":[]} + // {"message":"Orderbook exhausted for intent MARKET_INTENT:8yjjzd8b0e8yjjzd8b0fjjzd8b0g"} + // {"message":"Intent validation failed.","errors":[{"message":"Greater than available wallet balance.","type":"walletBaseTokenAmount"}]} + // {"message":"Schema validation failed for 'body'","errors":[{"name":"anyOf","argument":["[subschema 0]","[subschema 1]","[subschema 2]"],"message":"is not any of [subschema 0],[subschema 1],[subschema 2]","instance":{"signedTargetOrder":{"error":{"message":"Unsigned target order validation failed.","errors":[{"message":"Greater than available wallet balance.","type":"walletBaseTokenAmount"}]},"maker":"0x1709c02cd7327d391a39a7671af8a91a1ef8a47b","orderHash":"0xda007ea8b5eca71ac96fe4072f7c1209bb151d898a9cc89bbeaa594f0491ee49","ecSignature":{"v":27,"r":"0xb23ce6c4a7b5d51d77e2d00f6d1d472a3b2e72d5b2be1510cfeb122f9366b79e","s":"0x07d274e6d7a00b65fc3026c2f9019215b1e47a5ac4d1f05e03f90550d27109be"}}},"property":"instance"}]} + // {"message":"Schema validation failed for 'params'","errors":[{"name":"pattern","argument":"^0x[0-9a-fA-F]{64}$","message":"does not match pattern \"^0x[0-9a-fA-F]{64}$\"","instance":"1","property":"instance.orderHash"}]} + // + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/therock.js b/TaskServer/node_modules/ccxt/js/therock.js new file mode 100644 index 000000000..285471d79 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/therock.js @@ -0,0 +1,1228 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, BadRequest, OrderNotFound, InvalidAddress } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class therock extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'therock', + 'name': 'TheRockTrading', + 'countries': [ 'MT' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'fetchMyTrades': true, + 'fetchLedger': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': 'emulated', + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg', + 'api': 'https://api.therocktrading.com', + 'www': 'https://therocktrading.com', + 'doc': [ + 'https://api.therocktrading.com/doc/v1/index.html', + 'https://api.therocktrading.com/doc/', + ], + }, + 'api': { + 'public': { + 'get': [ + 'funds', + 'funds/{id}/orderbook', + 'funds/{id}/ticker', + 'funds/{id}/trades', + 'funds/tickers', + ], + }, + 'private': { + 'get': [ + 'balances', + 'balances/{id}', + 'discounts', + 'discounts/{id}', + 'funds', + 'funds/{id}', + 'funds/{id}/trades', + 'funds/{fund_id}/orders', + 'funds/{fund_id}/orders/{id}', + 'funds/{fund_id}/position_balances', + 'funds/{fund_id}/positions', + 'funds/{fund_id}/positions/{id}', + 'transactions', + 'transactions/{id}', + 'withdraw_limits/{id}', + 'withdraw_limits', + ], + 'post': [ + 'atms/withdraw', + 'funds/{fund_id}/orders', + ], + 'delete': [ + 'funds/{fund_id}/orders/{id}', + 'funds/{fund_id}/orders/remove_all', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': { + 'BTC': 0.0005, + 'BCH': 0.0005, + 'PPC': 0.02, + 'ETH': 0.001, + 'ZEC': 0.001, + 'LTC': 0.002, + 'EUR': 2.5, // worst-case scenario: https://therocktrading.com/en/pages/fees + }, + 'deposit': { + 'BTC': 0, + 'BCH': 0, + 'PPC': 0, + 'ETH': 0, + 'ZEC': 0, + 'LTC': 0, + 'EUR': 0, + }, + }, + }, + 'exceptions': { + 'exact': { + 'Request already running': BadRequest, + 'cannot specify multiple address types': BadRequest, + 'Currency is not included in the list': BadRequest, + 'Record not found': OrderNotFound, + }, + 'broad': { + 'before must be greater than after param': BadRequest, + 'must be shorter than 60 days': BadRequest, + 'must be a multiple of (period param) in minutes': BadRequest, + 'Address allocation limit reached for currency': InvalidAddress, + 'is not a valid value for param currency': BadRequest, + ' is invalid': InvalidAddress, + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetFunds (params); + // + // { funds: [ { id: "BTCEUR", + // description: "Trade Bitcoin with Euro", + // type: "currency", + // base_currency: "EUR", + // trade_currency: "BTC", + // buy_fee: 0.2, + // sell_fee: 0.2, + // minimum_price_offer: 0.01, + // minimum_quantity_offer: 0.0005, + // base_currency_decimals: 2, + // trade_currency_decimals: 4, + // leverages: [] }, + // { id: "LTCEUR", + // description: "Trade Litecoin with Euro", + // type: "currency", + // base_currency: "EUR", + // trade_currency: "LTC", + // buy_fee: 0.2, + // sell_fee: 0.2, + // minimum_price_offer: 0.01, + // minimum_quantity_offer: 0.01, + // base_currency_decimals: 2, + // trade_currency_decimals: 2, + // leverages: [] } ] } + // + const markets = this.safeValue (response, 'funds'); + const result = []; + if (markets === undefined) { + throw new ExchangeError (this.id + ' fetchMarkets got an unexpected response'); + } else { + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'id'); + const baseId = this.safeString (market, 'trade_currency'); + const quoteId = this.safeString (market, 'base_currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const buy_fee = this.safeFloat (market, 'buy_fee'); + const sell_fee = this.safeFloat (market, 'sell_fee'); + let taker = Math.max (buy_fee, sell_fee); + taker = taker / 100; + const maker = taker; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': true, + 'maker': maker, + 'taker': taker, + 'precision': { + 'amount': this.safeInteger (market, 'trade_currency_decimals'), + 'price': this.safeInteger (market, 'base_currency_decimals'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minimum_quantity_offer'), + 'max': undefined, + }, + 'price': { + 'min': this.safeFloat (market, 'minimum_price_offer'), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalances (params); + const balances = this.safeValue (response, 'balances', []); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'trading_balance'); + account['total'] = this.safeFloat (balance, 'balance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': this.marketId (symbol), + }; + const orderbook = await this.publicGetFundsIdOrderbook (this.extend (request, params)); + const timestamp = this.parse8601 (this.safeString (orderbook, 'date')); + return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount'); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.parse8601 (ticker['date']); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': this.safeFloat (ticker, 'close'), // previous day close, if any + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume_traded'), + 'quoteVolume': this.safeFloat (ticker, 'volume'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetFundsTickers (params); + const tickers = this.indexBy (response['tickers'], 'fund_id'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const market = this.markets_by_id[id]; + const symbol = market['symbol']; + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const ticker = await this.publicGetFundsIdTicker (this.extend ({ + 'id': market['id'], + }, params)); + return this.parseTicker (ticker, market); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades, fetchOrder trades + // + // { id: 4493548, + // fund_id: "ETHBTC", + // amount: 0.203, + // price: 0.02783576, + // side: "buy", + // dark: false, + // date: "2018-11-30T08:19:18.236Z" } + // + // fetchMyTrades + // + // { id: 237338, + // fund_id: "BTCEUR", + // amount: 0.348, + // price: 348, + // side: "sell", + // dark: false, + // order_id: 14920648, + // date: "2015-06-03T00:49:49.000Z", + // transactions: [ { id: 2770768, + // date: "2015-06-03T00:49:49.000Z", + // type: "sold_currency_to_fund", + // price: 121.1, + // currency: "EUR" }, + // { id: 2770769, + // date: "2015-06-03T00:49:49.000Z", + // type: "released_currency_to_fund", + // price: 0.348, + // currency: "BTC" }, + // { id: 2770772, + // date: "2015-06-03T00:49:49.000Z", + // type: "paid_commission", + // price: 0.06, + // currency: "EUR", + // trade_id: 440492 } ] } + // + if (!market) { + market = this.markets_by_id[trade['fund_id']]; + } + const timestamp = this.parse8601 (this.safeString (trade, 'date')); + const id = this.safeString (trade, 'id'); + const orderId = this.safeString (trade, 'order_id'); + const side = this.safeString (trade, 'side'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let fee = undefined; + let feeCost = undefined; + const transactions = this.safeValue (trade, 'transactions', []); + const transactionsByType = this.groupBy (transactions, 'type'); + const feeTransactions = this.safeValue (transactionsByType, 'paid_commission', []); + for (let i = 0; i < feeTransactions.length; i++) { + if (feeCost === undefined) { + feeCost = 0; + } + feeCost = this.sum (feeCost, this.safeFloat (feeTransactions[i], 'price')); + } + if (feeCost !== undefined) { + fee = { + 'cost': feeCost, + 'currency': market['quote'], + }; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + parseLedgerEntryDirection (direction) { + const directions = { + 'affiliate_earnings': 'in', + 'atm_payment': 'in', + 'bought_currency_from_fund': 'out', + 'bought_shares': 'out', + 'paid_commission': 'out', + 'paypal_payment': 'in', + 'pos_payment': 'in', + 'released_currency_to_fund': 'out', + 'rollover_commission': 'out', + 'sold_currency_to_fund': 'in', + 'sold_shares': 'in', + 'transfer_received': 'in', + 'transfer_sent': 'out', + 'withdraw': 'out', + // commented types will be shown as-is + // 'acquired_currency_from_fund': '', + // 'acquired_insurance': '', + // 'dividend_distributed_to_holders': '', + // 'dividend_from_shares': '', + // 'exposed_position': '', + // 'insurances_reimbursement': '', + // 'lent_currency': '', + // 'linden_lab_assessment': '', + // 'position_transfer_received': '', + // 'return_lent_currency': '', + // 'returned_lent_currency': '', + // 'the_rock_assessment': '', + }; + return this.safeString (directions, direction, direction); + } + + parseLedgerEntryType (type) { + const types = { + 'affiliate_earnings': 'referral', + 'atm_payment': 'transaction', + 'bought_currency_from_fund': 'trade', + 'bought_shares': 'trade', + 'paid_commission': 'fee', + 'paypal_payment': 'transaction', + 'pos_payment': 'transaction', + 'released_currency_to_fund': 'trade', + 'rollover_commission': 'fee', + 'sold_currency_to_fund': 'trade', + 'sold_shares': 'trade', + 'transfer_received': 'transfer', + 'transfer_sent': 'transfer', + 'withdraw': 'transaction', + // commented types will be shown as-is + // 'acquired_currency_from_fund': '', + // 'acquired_insurance': '', + // 'dividend_distributed_to_holders': '', + // 'dividend_from_shares': '', + // 'exposed_position': '', + // 'insurances_reimbursement': '', + // 'lent_currency': '', + // 'linden_lab_assessment': '', + // 'position_transfer_received': '', + // 'return_lent_currency': '', + // 'returned_lent_currency': '', + // 'the_rock_assessment': '', + }; + return this.safeString (types, type, type); + } + + parseLedgerEntry (item, currency = undefined) { + // + // withdrawal + // + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // } + // + // deposit + // + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // } + // + // trade fee + // + // { + // "id": 21311221, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "paid_commission", + // "price": 0.0001, + // "fund_id": "BTCEUR", + // "order_id": 12832371, + // "trade_id": 12923212, + // "currency": "BTC", + // "transfer_detail": null + // } + // + const id = this.safeString (item, 'id'); + let referenceId = undefined; + let type = this.safeString (item, 'type'); + const direction = this.parseLedgerEntryDirection (type); + type = this.parseLedgerEntryType (type); + if (type === 'trade' || type === 'fee') { + referenceId = this.safeString (item, 'trade_id'); + } + const currencyId = this.safeString (item, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const amount = this.safeFloat (item, 'price'); + const timestamp = this.parse8601 (this.safeString (item, 'date')); + const status = 'ok'; + return { + 'info': item, + 'id': id, + 'direction': direction, + 'account': undefined, + 'referenceId': referenceId, + 'referenceAccount': undefined, + 'type': type, + 'currency': code, + 'amount': amount, + 'before': undefined, + 'after': undefined, + 'status': status, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page': 1, + // 'fund_id': 'ETHBTC', // filter by fund symbol + // 'currency': 'BTC', // filter by currency + // 'after': '2015-02-06T08:47:26Z', // filter after a certain timestamp + // 'before': '2015-02-06T08:47:26Z', + // 'type': 'withdraw', + // 'order_id': '12832371', // filter by a specific order ID + // 'trade_id': '12923212', // filter by a specific trade ID + // 'transfer_method': 'bitcoin', // wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin + // 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // filter by a specific recipient (e.g. Bitcoin address, IBAN) + // 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', // filter by a specific transfer ID (e.g. Bitcoin TX hash) + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.privateGetTransactions (this.extend (request, params)); + // + // { + // "transactions": [ + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // }, + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // }, + // { + // "id": 21311221, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "paid_commission", + // "price": 0.0001, + // "fund_id": "BTCEUR", + // "order_id": 12832371, + // "trade_id": 12923212, + // "currency": "BTC", + // "transfer_detail": null + // } + // ], + // "meta": { + // "total_count": 1221, + // "first": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "previous": null, + // "current": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "next": { "page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2" }, + // "last": { "page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221" } + // } + // } + // + const transactions = this.safeValue (response, 'transactions', []); + return this.parseLedger (transactions, currency, since, limit); + } + + parseTransactionType (type) { + const types = { + 'withdraw': 'withdrawal', + 'atm_payment': 'deposit', + }; + return this.safeString (types, type, type); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchWithdrawals + // + // // fiat + // + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // } + // + // { + // "id": 12564223, + // "date": "2017-08-07T08:13:50.023Z", + // "note": "GB7IDL401573388", + // "type": "withdraw", + // "price": 4345.93, + // "fund_id": null, + // "currency": "EUR", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "id": "EXECUTEDBUTUNCHECKED", + // "method": "wire_transfer", + // "recipient": "GB7IDL401573388", + // "confirmations": 0 + // } + // } + // + // // crypto + // + // { + // id: 20914695, + // date: '2018-02-24T07:13:23.002Z', + // type: 'withdraw', + // price: 2.70883607, + // currency: 'BCH', + // fund_id: null, + // order_id: null, + // trade_id: null, + // note: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', + // transfer_detail: { + // method: 'bitcoin_cash', + // id: '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', + // recipient: '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', + // confirmations: 0 + // } + // } + // + // + // fetchDeposits + // + // // fiat + // + // { + // id: 16176632, + // date: '2017-11-20T21:00:13.355Z', + // type: 'atm_payment', + // price: 5000, + // currency: 'EUR', + // fund_id: null, + // order_id: null, + // trade_id: null, + // note: 'Mistral deposit', + // transfer_detail: { + // method: 'wire_transfer', + // id: '972JQ49337DX769T', + // recipient: null, + // confirmations: 0 + // } + // } + // + // // crypto + // + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // } + // + const id = this.safeString (transaction, 'id'); + const type = this.parseTransactionType (this.safeString (transaction, 'type')); + const detail = this.safeValue (transaction, 'transfer_detail', {}); + const method = this.safeString (detail, 'method'); + let txid = undefined; + let address = undefined; + if (method !== undefined) { + if (method !== 'wire_transfer') { + txid = this.safeString (detail, 'id'); + address = this.safeString (detail, 'recipient'); + } + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const amount = this.safeFloat (transaction, 'price'); + const timestamp = this.parse8601 (this.safeString (transaction, 'date')); + const status = 'ok'; + // todo parse tags + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'addressFrom': undefined, + 'addressTo': address, + 'address': address, + 'tagFrom': undefined, + 'tagTo': undefined, + 'tag': undefined, + 'status': status, + 'type': type, + 'updated': undefined, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': undefined, + }; + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'withdraw', + }; + return await this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'type': 'atm_payment', + }; + return await this.fetchTransactions (code, since, limit, this.extend (request, params)); + } + + async fetchTransactions (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page': 1, + // 'fund_id': 'ETHBTC', // filter by fund symbol + // 'currency': 'BTC', // filter by currency + // 'after': '2015-02-06T08:47:26Z', // filter after a certain timestamp + // 'before': '2015-02-06T08:47:26Z', + // 'type': 'withdraw', + // 'order_id': '12832371', // filter by a specific order ID + // 'trade_id': '12923212', // filter by a specific trade ID + // 'transfer_method': 'bitcoin', // wire_transfer, ripple, greenaddress, bitcoin, litecoin, namecoin, peercoin, dogecoin + // 'transfer_recipient': '1MAHLhJoz9W2ydbRf972WSgJYJ3Ui7aotm', // filter by a specific recipient (e.g. Bitcoin address, IBAN) + // 'transfer_id': '8261949194985b01985006724dca5d6059989e096fa95608271d00dd902327fa', // filter by a specific transfer ID (e.g. Bitcoin TX hash) + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + params = this.extend (request, params); + const response = await this.privateGetTransactions (params); + // + // { + // "transactions": [ + // { + // "id": 21311223, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "withdraw", + // "price": 103.00, + // "currency": "EUR", + // "fund_id": null, + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "wire_transfer", + // "id": "F112DD3", + // "recipient": "IT123456789012", + // "confirmations": 0 + // } + // }, + // { + // "id": 21311222, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "atm_payment", + // "price": 2.01291, + // "currency": "BTC", + // "fund_id": "null", + // "order_id": null, + // "trade_id": null, + // "transfer_detail": { + // "method": "bitcoin", + // "id": "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098", + // "recipient": "mzb3NgX9Dr6jgGAu31L6jsPGB2zkaFxxyf", + // "confirmations": 3 + // } + // }, + // { + // "id": 21311221, + // "date": "2015-06-30T13:55:11.000Z", + // "type": "paid_commission", + // "price": 0.0001, + // "fund_id": "BTCEUR", + // "order_id": 12832371, + // "trade_id": 12923212, + // "currency": "BTC", + // "transfer_detail": null + // } + // ], + // "meta": { + // "total_count": 1221, + // "first": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "previous": null, + // "current": { "page": 1, "href": "https://api.therocktrading.com/v1/transactions?page=1" }, + // "next": { "page": 2, "href": "https://api.therocktrading.com/v1/transactions?page=2" }, + // "last": { "page": 1221, "href": "https://api.therocktrading.com/v1/transactions?page=1221" } + // } + // } + // + const transactions = this.safeValue (response, 'transactions', []); + const transactionTypes = [ 'withdraw', 'atm_payment' ]; + const depositsAndWithdrawals = this.filterByArray (transactions, 'type', transactionTypes, false); + return this.parseTransactions (depositsAndWithdrawals, currency, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'active': 'open', + 'executed': 'closed', + 'deleted': 'canceled', + // don't know what this status means + // 'conditional': '?', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "id": 4325578, + // "fund_id":"BTCEUR", + // "side":"buy", + // "type":"limit", + // "status":"executed", + // "price":0.0102, + // "amount": 50.0, + // "amount_unfilled": 0.0, + // "conditional_type": null, + // "conditional_price": null, + // "date":"2015-06-03T00:49:48.000Z", + // "close_on": nil, + // "leverage": 1.0, + // "position_id": null, + // "trades": [ + // { + // "id":237338, + // "fund_id":"BTCEUR", + // "amount":50, + // "price":0.0102, + // "side":"buy", + // "dark":false, + // "date":"2015-06-03T00:49:49.000Z" + // } + // ] + // } + // + const id = this.safeString (order, 'id'); + let symbol = undefined; + const marketId = this.safeString (order, 'fund_id'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.parse8601 (this.safeString (order, 'date')); + const type = this.safeString (order, 'type'); + const side = this.safeString (order, 'side'); + const amount = this.safeFloat (order, 'amount'); + const remaining = this.safeFloat (order, 'amount_unfilled'); + let filled = undefined; + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + } + } + const price = this.safeFloat (order, 'price'); + let trades = this.safeValue (order, 'trades'); + let cost = undefined; + let average = undefined; + let lastTradeTimestamp = undefined; + if (trades !== undefined) { + const numTrades = trades.length; + if (numTrades > 0) { + trades = this.parseTrades (trades, market, undefined, undefined, { + 'orderId': id, + }); + // todo: determine the cost and the average price from trades + cost = 0; + filled = 0; + for (let i = 0; i < numTrades; i++) { + const trade = trades[i]; + cost = this.sum (cost, trade['cost']); + filled = this.sum (filled, trade['amount']); + } + if (filled > 0) { + average = cost / filled; + } + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + } else { + cost = 0; + } + } + return { + 'id': id, + 'info': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'average': average, + 'remaining': remaining, + 'fee': undefined, + 'trades': trades, + }; + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'active', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const request = { + 'status': 'executed', + }; + return await this.fetchOrders (symbol, since, limit, this.extend (request, params)); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'fund_id': market['id'], + // 'after': '2015-02-06T08:47:26Z', + // 'before': '2015-02-06T08:47:26Z' + // 'status': 'active', // 'executed', 'conditional' + // 'side': 'buy', // 'sell' + // 'position_id': 123, // filter orders by margin position id + }; + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.privateGetFundsFundIdOrders (this.extend (request, params)); + // + // { + // orders: [ + // { + // id: 299333648, + // fund_id: 'BTCEUR', + // side: 'sell', + // type: 'limit', + // status: 'executed', + // price: 5821, + // amount: 0.1, + // amount_unfilled: 0, + // conditional_type: null, + // conditional_price: null, + // date: '2018-06-18T17:38:16.129Z', + // close_on: null, + // dark: false, + // leverage: 1, + // position_id: 0 + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + 'fund_id': market['id'], + }; + const response = await this.privatePostFundsFundIdOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + price = 0; + } + const request = { + 'fund_id': this.marketId (symbol), + 'side': side, + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostFundsFundIdOrders (this.extend (request, params)); + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + 'fund_id': this.marketId (symbol), + }; + const response = await this.privateDeleteFundsFundIdOrdersId (this.extend (request, params)); + return this.parseOrder (response); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + if (limit !== undefined) { + request['per_page'] = limit; // default 25 max 200 + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.privateGetFundsIdTrades (this.extend (request, params)); + // + // { trades: [ { id: 237338, + // fund_id: "BTCEUR", + // amount: 0.348, + // price: 348, + // side: "sell", + // dark: false, + // order_id: 14920648, + // date: "2015-06-03T00:49:49.000Z", + // transactions: [ { id: 2770768, + // date: "2015-06-03T00:49:49.000Z", + // type: "sold_currency_to_fund", + // price: 121.1, + // currency: "EUR" }, + // { id: 2770769, + // date: "2015-06-03T00:49:49.000Z", + // type: "released_currency_to_fund", + // price: 0.348, + // currency: "BTC" }, + // { id: 2770772, + // date: "2015-06-03T00:49:49.000Z", + // type: "paid_commission", + // price: 0.06, + // currency: "EUR", + // trade_id: 440492 } ] } ], + // meta: { total_count: 31, + // first: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" }, + // previous: null, + // current: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=1" }, + // next: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" }, + // last: { href: "https://api.therocktrading.com/v1/funds/BTCXRP/trades?page=2" } } } + // + return this.parseTrades (response['trades'], market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': market['id'], + }; + if (limit !== undefined) { + request['per_page'] = limit; // default 25 max 200 + } + if (since !== undefined) { + request['after'] = this.iso8601 (since); + } + const response = await this.publicGetFundsIdTrades (this.extend (request, params)); + // + // { trades: [ { id: 4493548, + // fund_id: "ETHBTC", + // amount: 0.203, + // price: 0.02783576, + // side: "buy", + // dark: false, + // date: "2018-11-30T08:19:18.236Z" }, + // { id: 4492926, + // fund_id: "ETHBTC", + // amount: 0.04, + // price: 0.02767034, + // side: "buy", + // dark: false, + // date: "2018-11-30T07:03:03.897Z" } ], + // meta: { total_count: null, + // first: { page: 1, + // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" }, + // previous: null, + // current: { page: 1, + // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=1" }, + // next: { page: 2, + // href: "https://api.therocktrading.com/v1/funds/ETHBTC/trades?page=2" }, + // last: null } } + // + return this.parseTrades (response['trades'], market, since, limit); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + headers = (headers === undefined) ? {} : headers; + if (api === 'private') { + this.checkRequiredCredentials (); + if (Object.keys (query).length) { + if (method === 'POST') { + body = this.json (query); + headers['Content-Type'] = 'application/json'; + } else { + const queryString = this.rawencode (query); + if (queryString.length) { + url += '?' + queryString; + } + } + } + const nonce = this.nonce ().toString (); + const auth = nonce + url; + headers['X-TRT-KEY'] = this.apiKey; + headers['X-TRT-NONCE'] = nonce; + headers['X-TRT-SIGN'] = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512'); + } else if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.rawencode (query); + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // { + // "errors": + // [ + // { "message": ":currency is not a valid value for param currency","code": "11","meta": { "key":"currency","value":":currency"} }, + // { "message": "Address allocation limit reached for currency :currency.","code": "13" }, + // { "message": "Request already running", "code": "50"}, + // { "message": "cannot specify multiple address types", "code": "12" }, + // { "message": ":address_type is invalid", "code": "12" } + // ] + // } + // + const errors = this.safeValue (response, 'errors', []); + const numErrors = errors.length; + if (numErrors > 0) { + const feedback = this.id + ' ' + body; + // here we throw the first error we can identify + for (let i = 0; i < numErrors; i++) { + const error = errors[i]; + const message = this.safeString (error, 'message'); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + } + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/tidebit.js b/TaskServer/node_modules/ccxt/js/tidebit.js new file mode 100644 index 000000000..2cce034e4 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/tidebit.js @@ -0,0 +1,491 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, InsufficientFunds, OrderNotFound } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class tidebit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'tidebit', + 'name': 'TideBit', + 'countries': [ 'HK' ], + 'rateLimit': 1000, + 'version': 'v2', + 'has': { + 'fetchDepositAddress': true, + 'CORS': true, + 'fetchTickers': true, + 'fetchOHLCV': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1', + '5m': '5', + '15m': '15', + '30m': '30', + '1h': '60', + '2h': '120', + '4h': '240', + '12h': '720', + '1d': '1440', + '3d': '4320', + '1w': '10080', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/39034921-e3acf016-4480-11e8-9945-a6086a1082fe.jpg', + 'api': 'https://www.tidebit.com', + 'www': 'https://www.tidebit.com', + 'doc': [ + 'https://www.tidebit.com/documents/api/guide', + 'https://www.tidebit.com/swagger/#/default', + ], + 'referral': 'http://bit.ly/2IX0LrM', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'tickers', + 'tickers/{market}', + 'timestamp', + 'trades', + 'trades/{market}', + 'order_book', + 'order', + 'k_with_pending_trades', + 'k', + 'depth', + ], + 'post': [], + }, + 'private': { + 'get': [ + 'addresses/{address}', + 'deposits/history', + 'deposits/get_deposit', + 'deposits/deposit_address', + 'historys/orders', + 'historys/vouchers', + 'historys/accounts', + 'historys/snapshots', + 'linkage/get_status', + 'members/me', + 'order', + 'orders', + 'partners/orders/{id}/trades', + 'referral_commissions/get_undeposited', + 'referral_commissions/get_graph_data', + 'trades/my', + 'withdraws/bind_account_list', + 'withdraws/get_withdraw_account', + 'withdraws/fetch_bind_info', + ], + 'post': [ + 'deposits/deposit_cash', + 'favorite_markets/update', + 'order/delete', + 'orders', + 'orders/multi', + 'orders/clear', + 'referral_commissions/deposit', + 'withdraws/apply', + 'withdraws/bind_bank', + 'withdraws/bind_address', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.3 / 100, + 'taker': 0.3 / 100, + }, + 'funding': { + 'tierBased': false, + 'percentage': true, + 'withdraw': {}, // There is only 1% fee on withdrawals to your bank account. + }, + }, + 'exceptions': { + '2002': InsufficientFunds, + '2003': OrderNotFound, + }, + }); + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetDepositAddress (this.extend (request, params)); + if ('success' in response) { + if (response['success']) { + const address = this.safeString (response, 'address'); + const tag = this.safeString (response, 'addressTag'); + return { + 'currency': code, + 'address': this.checkAddress (address), + 'tag': tag, + 'info': response, + }; + } + } + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'id'); + const symbol = this.safeString (market, 'name'); + const [ baseId, quoteId ] = symbol.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetMembersMe (params); + const balances = this.safeValue (response, 'accounts'); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 300 + } + request['market'] = market['id']; + const response = await this.publicGetDepth (this.extend (request, params)); + const timestamp = this.safeTimestamp (response, 'timestamp'); + return this.parseOrderBook (response, timestamp); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.safeTimestamp (ticker, 'at'); + ticker = this.safeValue (ticker, 'ticker', {}); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'ask': this.safeFloat (ticker, 'sell'), + 'bidVolume': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'change': undefined, + 'percentage': undefined, + 'previousClose': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const tickers = await this.publicGetTickers (params); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + let symbol = id; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTickersMarket (this.extend (request, params)); + return this.parseTicker (response, market); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + const id = this.safeString (trade, 'id'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'volume'); + const cost = this.safeFloat (trade, 'funds'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': undefined, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, + ohlcv[1], + ohlcv[2], + ohlcv[3], + ohlcv[4], + ohlcv[5], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 30; // default is 30 + } + const request = { + 'market': market['id'], + 'period': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + request['timestamp'] = parseInt (since / 1000); + } else { + request['timestamp'] = 1800000; + } + const response = await this.publicGetK (this.extend (request, params)); + if (response === 'null') { + return []; + } + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + parseOrderStatus (status) { + const statuses = { + 'done': 'closed', + 'wait': 'open', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } else { + const marketId = order['market']; + symbol = this.markets_by_id[marketId]['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + const id = this.safeString (order, 'id'); + const type = this.safeString (order, 'ord_type'); + const side = this.safeString (order, 'side'); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + const filled = this.safeFloat (order, 'executed_volume'); + const remaining = this.safeFloat (order, 'remaining_volume'); + let cost = undefined; + if (price !== undefined) { + if (filled !== undefined) { + cost = price * filled; + } + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'cost': cost, + 'trades': undefined, + 'fee': undefined, + 'info': order, + }; + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'market': this.marketId (symbol), + 'side': side, + 'volume': amount.toString (), + 'ord_type': type, + }; + if (type === 'limit') { + request['price'] = price.toString (); + } + const response = await this.privatePostOrders (this.extend (request, params)); + const market = this.markets_by_id[response['market']]; + return this.parseOrder (response, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + const result = await this.privatePostOrderDelete (this.extend (request, params)); + const order = this.parseOrder (result); + const status = this.safeString (order, 'status'); + if (status === 'closed' || status === 'canceled') { + throw new OrderNotFound (this.id + ' ' + this.json (order)); + } + return order; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const id = this.safeString (params, 'id'); + if (id === undefined) { + throw new ExchangeError (this.id + ' withdraw() requires an extra `id` param (withdraw account id according to withdraws/bind_account_list endpoint'); + } + const request = { + 'id': id, + 'currency_type': 'coin', // or 'cash' + 'currency': currency['id'], + 'body': amount, + // 'address': address, // they don't allow withdrawing to direct addresses? + }; + if (tag !== undefined) { + request['memo'] = tag; + } + const result = await this.privatePostWithdrawsApply (this.extend (request, params)); + return { + 'info': result, + 'id': undefined, + }; + } + + nonce () { + return this.milliseconds (); + } + + encodeParams (params) { + return this.urlencode (this.keysort (params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + const request = '/' + 'api/' + this.version + '/' + this.implodeParams (path, params) + '.json'; + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'] + request; + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce ().toString (); + const sortedByKey = this.keysort (this.extend ({ + 'access_key': this.apiKey, + 'tonce': nonce, + }, params)); + const query = this.urlencode (sortedByKey); + const payload = method + '|' + request + '|' + query; + const signature = this.hmac (this.encode (payload), this.encode (this.secret)); + const suffix = query + '&signature=' + signature; + if (method === 'GET') { + url += '?' + suffix; + } else { + body = suffix; + headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 400) || (response === undefined)) { + const feedback = this.id + ' ' + body; + if (response === undefined) { + throw new ExchangeError (feedback); + } + const error = this.safeValue (response, 'error', {}); + const errorCode = this.safeString (error, 'code'); + this.throwExactlyMatchedException (this.exceptions, errorCode, feedback); + // fallback to default error handler + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/tidex.js b/TaskServer/node_modules/ccxt/js/tidex.js new file mode 100644 index 000000000..4b08cb872 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/tidex.js @@ -0,0 +1,854 @@ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +module.exports = class tidex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'tidex', + 'name': 'Tidex', + 'countries': [ 'UK' ], + 'rateLimit': 2000, + 'version': '3', + 'userAgent': this.userAgents['chrome'], + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchOrderBooks': true, + 'fetchOrder': true, + 'fetchOrders': 'emulated', + 'fetchOpenOrders': true, + 'fetchClosedOrders': 'emulated', + 'fetchTickers': true, + 'fetchMyTrades': true, + 'withdraw': true, + 'fetchCurrencies': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/30781780-03149dc4-a12e-11e7-82bb-313b269d24d4.jpg', + 'api': { + 'web': 'https://gate.tidex.com/api', + 'public': 'https://api.tidex.com/api/3', + 'private': 'https://api.tidex.com/tapi', + }, + 'www': 'https://tidex.com', + 'doc': 'https://tidex.com/exchange/public-api', + 'referral': 'https://tidex.com/exchange/?ref=57f5638d9cd7', + 'fees': [ + 'https://tidex.com/exchange/assets-spec', + 'https://tidex.com/exchange/pairs-spec', + ], + }, + 'api': { + 'web': { + 'get': [ + 'currency', + 'pairs', + 'tickers', + 'orders', + 'ordershistory', + 'trade-data', + 'trade-data/{id}', + ], + }, + 'public': { + 'get': [ + 'info', + 'ticker/{pair}', + 'depth/{pair}', + 'trades/{pair}', + ], + }, + 'private': { + 'post': [ + 'getInfoExt', + 'getInfo', + 'Trade', + 'ActiveOrders', + 'OrderInfo', + 'CancelOrder', + 'TradeHistory', + 'CoinDepositAddress', + 'WithdrawCoin', + 'CreateCoupon', + 'RedeemCoupon', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.1 / 100, + 'maker': 0.1 / 100, + }, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + 'EMGO': 'MGO', + 'MGO': 'WMGO', + }, + 'exceptions': { + 'exact': { + '803': InvalidOrder, // "Count could not be less than 0.001." (selling below minAmount) + '804': InvalidOrder, // "Count could not be more than 10000." (buying above maxAmount) + '805': InvalidOrder, // "price could not be less than X." (minPrice violation on buy & sell) + '806': InvalidOrder, // "price could not be more than X." (maxPrice violation on buy & sell) + '807': InvalidOrder, // "cost could not be less than X." (minCost violation on buy & sell) + '831': InsufficientFunds, // "Not enougth X to create buy order." (buying with balance.quote < order.cost) + '832': InsufficientFunds, // "Not enougth X to create sell order." (selling with balance.base < order.amount) + '833': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) + }, + 'broad': { + 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} + 'invalid api key': AuthenticationError, + 'invalid sign': AuthenticationError, + 'api key dont have trade permission': AuthenticationError, + 'invalid parameter': InvalidOrder, + 'invalid order': InvalidOrder, + 'Requests too often': DDoSProtection, + 'not available': ExchangeNotAvailable, + 'data unavailable': ExchangeNotAvailable, + 'external service unavailable': ExchangeNotAvailable, + }, + }, + 'options': { + 'fetchTickersMaxLength': 2048, + }, + }); + } + + async fetchCurrencies (params = {}) { + const response = await this.webGetCurrency (params); + const result = {}; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + const id = this.safeString (currency, 'symbol'); + const precision = currency['amountPoint']; + const code = this.safeCurrencyCode (id); + let active = currency['visible'] === true; + const canWithdraw = currency['withdrawEnable'] === true; + const canDeposit = currency['depositEnable'] === true; + if (!canWithdraw || !canDeposit) { + active = false; + } + const name = this.safeString (currency, 'name'); + result[code] = { + 'id': id, + 'code': code, + 'name': name, + 'active': active, + 'precision': precision, + 'funding': { + 'withdraw': { + 'active': canWithdraw, + 'fee': currency['withdrawFee'], + }, + 'deposit': { + 'active': canDeposit, + 'fee': 0.0, + }, + }, + 'limits': { + 'amount': { + 'min': undefined, + 'max': Math.pow (10, precision), + }, + 'price': { + 'min': Math.pow (10, -precision), + 'max': Math.pow (10, precision), + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': currency['withdrawMinAmout'], + 'max': undefined, + }, + 'deposit': { + 'min': currency['depositMinAmount'], + 'max': undefined, + }, + }, + 'info': currency, + }; + } + return result; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInfo (params); + const markets = response['pairs']; + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'decimal_places'), + 'price': this.safeInteger (market, 'decimal_places'), + }; + const limits = { + 'amount': { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }, + 'price': { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }, + 'cost': { + 'min': this.safeFloat (market, 'min_total'), + }, + }; + const hidden = this.safeInteger (market, 'hidden'); + const active = (hidden === 0); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'taker': market['fee'] / 100, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfoExt (params); + const balances = this.safeValue (response, 'return'); + const result = { 'info': balances }; + const funds = this.safeValue (balances, 'funds', {}); + const currencyIds = Object.keys (funds); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (funds, currencyId, {}); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'value'); + account['used'] = this.safeFloat (balance, 'inOrders'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const market_id_in_reponse = (market['id'] in response); + if (!market_id_in_reponse) { + throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); + } + const orderbook = response[market['id']]; + return this.parseOrderBook (orderbook); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join ('-'); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const result = {}; + ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + if (id in this.markets_by_id) { + symbol = this.markets_by_id[id]['symbol']; + } + result[symbol] = this.parseOrderBook (response[id]); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { high: 0.03497582, + // low: 0.03248474, + // avg: 0.03373028, + // vol: 120.11485715062999, + // vol_cur: 3572.24914074, + // last: 0.0337611, + // buy: 0.0337442, + // sell: 0.03377798, + // updated: 1537522009 } + // + let timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + if (!market['active']) { + timestamp = undefined; + } + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': this.safeFloat (ticker, 'vol_cur'), + 'quoteVolume': this.safeFloat (ticker, 'vol'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = this.ids; + if (symbols === undefined) { + const numIds = ids.length; + ids = ids.join ('-'); + // max URL length is 2048 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); + throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + const response = await this.publicGetTickerPair (this.extend (request, params)); + const result = {}; + const keys = Object.keys (response); + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (response[id], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let side = this.safeString (trade, 'type'); + if (side === 'ask') { + side = 'sell'; + } else if (side === 'bid') { + side = 'buy'; + } + const price = this.safeFloat2 (trade, 'rate', 'price'); + const id = this.safeString2 (trade, 'trade_id', 'tid'); + const orderId = this.safeString (trade, 'order_id'); + if ('pair' in trade) { + const marketId = this.safeString (trade, 'pair'); + market = this.safeValue (this.markets_by_id, marketId, market); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const amount = this.safeFloat (trade, 'amount'); + const type = 'limit'; // all trades are still limit trades + let takerOrMaker = undefined; + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const isYourOrder = this.safeValue (trade, 'is_your_order'); + if (isYourOrder !== undefined) { + takerOrMaker = 'taker'; + if (isYourOrder) { + takerOrMaker = 'maker'; + } + if (fee === undefined) { + fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); + } + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTradesPair (this.extend (request, params)); + if (Array.isArray (response)) { + const numElements = response.length; + if (numElements === 0) { + return []; + } + } + return this.parseTrades (response[market['id']], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'amount': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + }; + price = parseFloat (price); + amount = parseFloat (amount); + const response = await this.privatePostTrade (this.extend (request, params)); + let id = undefined; + let status = 'open'; + let filled = 0.0; + let remaining = amount; + if ('return' in response) { + id = this.safeString (response['return'], 'order_id'); + if (id === '0') { + id = this.safeString (response['return'], 'init_order_id'); + status = 'closed'; + } + filled = this.safeFloat (response['return'], 'received', 0.0); + remaining = this.safeFloat (response['return'], 'remains', amount); + } + const timestamp = this.milliseconds (); + const order = { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * filled, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'fee': undefined, + // 'trades': this.parseTrades (order['trades'], market), + }; + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'closed', + '2': 'canceled', + '3': 'canceled', // or partially-filled and still open? https://github.com/ccxt/ccxt/issues/1594 + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.safeTimestamp (order, 'timestamp_created'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + let remaining = undefined; + let amount = undefined; + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if ('start_amount' in order) { + amount = this.safeFloat (order, 'start_amount'); + remaining = this.safeFloat (order, 'amount'); + } else { + remaining = this.safeFloat (order, 'amount'); + if (id in this.orders) { + amount = this.orders[id]['amount']; + } + } + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + cost = price * filled; + } + } + const fee = undefined; + return { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'type': 'limit', + 'side': order['type'], + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + }; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + id = id.toString (); + const newOrder = this.parseOrder (this.extend ({ 'id': id }, response['return'][id])); + const oldOrder = (id in this.orders) ? this.orders[id] : {}; + this.orders[id] = this.extend (oldOrder, newOrder); + return this.orders[id]; + } + + updateCachedOrders (openOrders, symbol) { + // update local cache with open orders + // this will add unseen orders and overwrite existing ones + for (let j = 0; j < openOrders.length; j++) { + const id = openOrders[j]['id']; + this.orders[id] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + for (let k = 0; k < cachedOrderIds.length; k++) { + // match each cached order to an order in the open orders array + // possible reasons why a cached order may be missing in the open orders array: + // - order was closed or canceled -> update cache + // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip + const cachedOrderId = cachedOrderIds[k]; + let cachedOrder = this.orders[cachedOrderId]; + if (!(cachedOrderId in openOrdersIndexedById)) { + // cached order is not in open orders array + // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order + if (symbol !== undefined && symbol !== cachedOrder['symbol']) { + continue; + } + // cached order is absent from the list of open orders -> mark the cached order as closed + if (cachedOrder['status'] === 'open') { + cachedOrder = this.extend (cachedOrder, { + 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") + 'cost': undefined, + 'filled': cachedOrder['amount'], + 'remaining': 0.0, + }); + if (cachedOrder['cost'] === undefined) { + if (cachedOrder['filled'] !== undefined) { + cachedOrder['cost'] = cachedOrder['filled'] * cachedOrder['price']; + } + } + this.orders[cachedOrderId] = cachedOrder; + } + } + } + return this.toArray (this.orders); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if ('fetchOrdersRequiresSymbol' in this.options) { + if (this.options['fetchOrdersRequiresSymbol']) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a `symbol` argument'); + } + } + } + await this.loadMarkets (); + const request = {}; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostActiveOrders (this.extend (request, params)); + // it can only return 'open' orders (i.e. no way to fetch 'closed' orders) + const orders = this.safeValue (response, 'return', []); + const openOrders = this.parseOrders (orders, market); + const allOrders = this.updateCachedOrders (openOrders, symbol); + const result = this.filterBySymbol (allOrders, symbol); + return this.filterBySinceLimit (result, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'open'); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + // some derived classes use camelcase notation for request fields + const request = { + // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) + // 'count': 1000, // the number of trades for display numerical, default = 1000 + // 'from_id': trade ID, from which the display starts numerical 0 + // 'end_id': trade ID on which the display ends numerical ∞ + // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) + // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) + // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) + // 'pair': 'eth_btc', // default = all markets + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['pair'] = market['id']; + } + if (limit !== undefined) { + request['count'] = parseInt (limit); + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + const trades = this.safeValue (response, 'return', []); + return this.parseTrades (trades, market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coinName': currency['id'], + 'amount': parseFloat (amount), + 'address': address, + }; + // no docs on the tag, yet... + if (tag !== undefined) { + throw new ExchangeError (this.id + ' withdraw() does not support the tag argument yet due to a lack of docs on withdrawing with tag/memo on behalf of the exchange.'); + } + const response = await this.privatePostWithdrawCoin (this.extend (request, params)); + return { + 'info': response, + 'id': response['return']['tId'], + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + 'method': path, + }, query)); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': signature, + }; + } else if (api === 'public') { + url += '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + url += '/' + this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + if (Object.keys (query).length) { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // 1 - The exchange only returns the integer 'success' key from their private API + // + // { "success": 1, ... } httpCode === 200 + // { "success": 0, ... } httpCode === 200 + // + // 2 - However, derived exchanges can return non-integers + // + // It can be a numeric string + // { "sucesss": "1", ... } + // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) + // + // Or just a string + // { "success": "true", ... } + // { "success": "false", ... }, httpCode >= 200 + // + // Or a boolean + // { "success": true, ... } + // { "success": false, ... }, httpCode >= 200 + // + // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types + // + // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui + // + // To cover points 1, 2, 3 and 4 combined this handler should work like this: + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/timex.js b/TaskServer/node_modules/ccxt/js/timex.js new file mode 100644 index 000000000..e20edc6aa --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/timex.js @@ -0,0 +1,1235 @@ +'use strict'; + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, PermissionDenied, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, RateLimitExceeded, NotSupported, BadRequest, AuthenticationError } = require ('./base/errors'); + +module.exports = class timex extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'timex', + 'name': 'TimeX', + 'countries': [ 'AU' ], + 'version': 'v1', + 'rateLimit': 1500, + 'has': { + 'CORS': false, + 'cancelOrders': true, + 'editOrder': true, + 'fetchClosedOrders': true, + 'fetchCurrencies': true, + 'fetchMyTrades': true, + 'fetchOHLCV': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchTickers': true, + 'fetchTradingFee': true, // maker fee only + }, + 'timeframes': { + '1m': 'I1', + '5m': 'I5', + '15m': 'I15', + '30m': 'I30', + '1h': 'H1', + '2h': 'H2', + '4h': 'H4', + '6h': 'H6', + '12h': 'H12', + '1d': 'D1', + '1w': 'W1', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/70423869-6839ab00-1a7f-11ea-8f94-13ae72c31115.jpg', + 'api': 'https://plasma-relay-backend.timex.io', + 'www': 'https://timex.io', + 'doc': 'https://docs.timex.io', + }, + 'api': { + 'custody': { + 'get': [ + 'credentials', // Get api key for address + 'credentials/h/{hash}', // Get api key by hash + 'credentials/k/{key}', // Get api key by key + 'credentials/me/address', // Get api key by hash + 'deposit-addresses', // Get deposit addresses list + 'deposit-addresses/h/{hash}', // Get deposit address by hash + ], + }, + 'history': { + 'get': [ + 'orders', // Gets historical orders + 'orders/details', // Gets order details + 'orders/export/csv', // Export orders to csv + 'trades', // Gets historical trades + 'trades/export/csv', // Export trades to csv + ], + }, + 'currencies': { + 'get': [ + 'a/{address}', // Gets currency by address + 'i/{id}', // Gets currency by id + 's/{symbol}', // Gets currency by symbol + ], + 'post': [ + 'perform', // Creates new currency + 'prepare', // Prepare creates new currency + 'remove/perform', // Removes currency by symbol + 's/{symbol}/remove/prepare', // Prepare remove currency by symbol + 's/{symbol}/update/perform', // Prepare update currency by symbol + 's/{symbol}/update/prepare', // Prepare update currency by symbol + ], + }, + 'markets': { + 'get': [ + 'i/{id}', // Gets market by id + 's/{symbol}', // Gets market by symbol + ], + 'post': [ + 'perform', // Creates new market + 'prepare', // Prepare creates new market + 'remove/perform', // Removes market by symbol + 's/{symbol}/remove/prepare', // Prepare remove market by symbol + 's/{symbol}/update/perform', // Prepare update market by symbol + 's/{symbol}/update/prepare', // Prepare update market by symbol + ], + }, + 'public': { + 'get': [ + 'candles', // Gets candles + 'currencies', // Gets all the currencies + 'markets', // Gets all the markets + 'orderbook', // Gets orderbook + 'orderbook/raw', // Gets raw orderbook + 'orderbook/v2', // Gets orderbook v2 + 'tickers', // Gets all the tickers + 'trades', // Gets trades + ], + }, + 'statistics': { + 'get': [ + 'address', // calculateAddressStatistics + ], + }, + 'trading': { + 'get': [ + 'balances', // Get trading balances for all (or selected) currencies + 'fees', // Get trading fee rates for all (or selected) markets + 'orders', // Gets open orders + ], + 'post': [ + 'orders', // Create new order + 'orders/json', // Create orders + ], + 'put': [ + 'orders', // Cancel or update orders + 'orders/json', // Update orders + ], + 'delete': [ + 'orders', // Delete orders + 'orders/json', // Delete orders + ], + }, + 'tradingview': { + 'get': [ + 'config', // Gets config + 'history', // Gets history + 'symbol_info', // Gets symbol info + 'time', // Gets time + ], + }, + }, + 'exceptions': { + 'exact': { + '0': ExchangeError, + '1': NotSupported, + '4000': BadRequest, + '4001': BadRequest, + '4002': InsufficientFunds, + '4003': AuthenticationError, + '4004': AuthenticationError, + '4005': BadRequest, + '4006': BadRequest, + '4007': BadRequest, + '4300': PermissionDenied, + '4100': AuthenticationError, + '4400': OrderNotFound, + '5001': InvalidOrder, + '5002': ExchangeError, + '400': BadRequest, + '401': AuthenticationError, + '403': PermissionDenied, + '404': OrderNotFound, + '429': RateLimitExceeded, + '500': ExchangeError, + '503': ExchangeNotAvailable, + }, + 'broad': { + 'Insufficient': InsufficientFunds, + }, + }, + 'options': { + 'fetchTickers': { + 'period': '1d', + }, + 'fetchTrades': { + 'sort': 'timestamp,asc', + }, + 'fetchMyTrades': { + 'sort': 'timestamp,asc', + }, + 'fetchOpenOrders': { + 'sort': 'createdAt,asc', + }, + 'fetchClosedOrders': { + 'sort': 'createdAt,asc', + }, + 'defaultSort': 'timestamp,asc', + 'defaultSortOrders': 'createdAt,asc', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarkets (params); + // + // [ + // { + // "symbol": "ETHBTC", + // "name": "ETH/BTC", + // "baseCurrency": "ETH", + // "baseTokenAddress": "0x45932db54b38af1f5a57136302eeba66a5975c15", + // "quoteCurrency": "BTC", + // "quoteTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "feeCurrency": "BTC", + // "feeTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "quantityIncrement": "0.0000001", + // "takerFee": "0.005", + // "makerFee": "0.0025", + // "tickSize": "0.00000001", + // "baseMinSize": "0.0001", + // "quoteMinSize": "0.00001", + // "locked": false + // } + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + result.push (this.parseMarket (response[i])); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicGetCurrencies (params); + // + // [ + // { + // "symbol": "BTC", + // "name": "Bitcoin", + // "address": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggb3BhY2l0eT0iMC41IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMwIDUzQzQyLjcwMjUgNTMgNTMgNDIuNzAyNSA1MyAzMEM1MyAxNy4yOTc1IDQyLjcwMjUgNyAzMCA3QzE3LjI5NzUgNyA3IDE3LjI5NzUgNyAzMEM3IDQyLjcwMjUgMTcuMjk3NSA1MyAzMCA1M1pNMzAgNTVDNDMuODA3MSA1NSA1NSA0My44MDcxIDU1IDMwQzU1IDE2LjE5MjkgNDMuODA3MSA1IDMwIDVDMTYuMTkyOSA1IDUgMTYuMTkyOSA1IDMwQzUgNDMuODA3MSAxNi4xOTI5IDU1IDMwIDU1WiIvPgo8cGF0aCBkPSJNNDAuOTQyNSAyNi42NTg1QzQxLjQwMDMgMjMuNjExMyAzOS4wNzA1IDIxLjk3MzIgMzUuODg0OCAyMC44ODA0TDM2LjkxODIgMTYuNzUyNkwzNC4zOTUxIDE2LjEyNjRMMzMuMzg5IDIwLjE0NTVDMzIuNzI1OCAxOS45ODA5IDMyLjA0NDUgMTkuODI1NiAzMS4zNjc1IDE5LjY3MTdMMzIuMzgwOCAxNS42MjYyTDI5Ljg1OTEgMTVMMjguODI1IDE5LjEyNjRDMjguMjc2IDE5LjAwMTkgMjcuNzM3IDE4Ljg3ODggMjcuMjEzOSAxOC43NDkzTDI3LjIxNjggMTguNzM2NEwyMy43MzcyIDE3Ljg3MTJMMjMuMDY2IDIwLjU1NDhDMjMuMDY2IDIwLjU1NDggMjQuOTM4IDIwLjk4MjEgMjQuODk4NSAyMS4wMDg1QzI1LjkyMDQgMjEuMjYyNiAyNi4xMDUgMjEuOTM2IDI2LjA3NDEgMjIuNDY5OUwyNC44OTcgMjcuMTcyNEMyNC45Njc1IDI3LjE5MDMgMjUuMDU4NyAyNy4yMTYgMjUuMTU5MyAyNy4yNTYxQzI1LjA3NTMgMjcuMjM1NCAyNC45ODU0IDI3LjIxMjQgMjQuODkyNyAyNy4xOTAzTDIzLjI0MjggMzMuNzc3OEMyMy4xMTc3IDM0LjA4NjkgMjIuODAwOCAzNC41NTA2IDIyLjA4NjUgMzQuMzc0NkMyMi4xMTE3IDM0LjQxMTEgMjAuMjUyNiAzMy45MTg3IDIwLjI1MjYgMzMuOTE4N0wxOSAzNi43OTQ5TDIyLjI4MzQgMzcuNjFDMjIuODk0MiAzNy43NjI0IDIzLjQ5MjggMzcuOTIyIDI0LjA4MjEgMzguMDcyM0wyMy4wMzggNDIuMjQ3NEwyNS41NTgyIDQyLjg3MzZMMjYuNTkyMyAzOC43NDI5QzI3LjI4MDcgMzguOTI5IDI3Ljk0OSAzOS4xMDA3IDI4LjYwMyAzOS4yNjI0TDI3LjU3MjUgNDMuMzczOEwzMC4wOTU2IDQ0TDMxLjEzOTcgMzkuODMyOEMzNS40NDIyIDQwLjY0MzYgMzguNjc3NCA0MC4zMTY2IDQwLjAzOTIgMzYuNDQxNEM0MS4xMzY1IDMzLjMyMTIgMzkuOTg0NiAzMS41MjEzIDM3LjcyMDkgMzAuMzQ3N0MzOS4zNjk0IDI5Ljk2OTEgNDAuNjExMiAyOC44ODkyIDQwLjk0MjUgMjYuNjU4NVYyNi42NTg1Wk0zNS4xNzc3IDM0LjcwODhDMzQuMzk4IDM3LjgyOSAyOS4xMjI2IDM2LjE0MjIgMjcuNDEyMiAzNS43MTkzTDI4Ljc5NzcgMzAuMTg4MUMzMC41MDgxIDMwLjYxMzIgMzUuOTkyNiAzMS40NTQ4IDM1LjE3NzcgMzQuNzA4OFpNMzUuOTU4MSAyNi42MTM0QzM1LjI0NjcgMjkuNDUxNyAzMC44NTU5IDI4LjAwOTcgMjkuNDMxNiAyNy42NTYxTDMwLjY4NzcgMjIuNjM5NUMzMi4xMTIgMjIuOTkzIDM2LjY5OSAyMy42NTI4IDM1Ljk1ODEgMjYuNjEzNFoiLz4KPC9zdmc+Cg==", + // "background": "transparent", + // "fiatSymbol": "BTC", + // "decimals": 8, + // "tradeDecimals": 20, + // "displayDecimals": 4, + // "crypto": true, + // "depositEnabled": true, + // "withdrawalEnabled": true, + // "transferEnabled": true, + // "buyEnabled": false, + // "purchaseEnabled": false, + // "redeemEnabled": false, + // "active": true, + // "withdrawalFee": "50000000000000000", + // "purchaseCommissions": [] + // }, + // ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const currency = response[i]; + result.push (this.parseCurrency (currency)); + } + return this.indexBy (result, 'code'); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const period = this.safeString (this.options['fetchTickers'], 'period', '1d'); + const request = { + 'period': this.timeframes[period], // I1, I5, I15, I30, H1, H2, H4, H6, H12, D1, W1 + }; + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ + // { + // "ask": 0.017, + // "bid": 0.016, + // "high": 0.019, + // "last": 0.017, + // "low": 0.015, + // "market": "TIME/ETH", + // "open": 0.016, + // "period": "H1", + // "timestamp": "2018-12-14T20:50:36.134Z", + // "volume": 4.57, + // "volumeQuote": 0.07312 + // } + // ] + // + return this.parseTickers (response, symbols); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const period = this.safeString (this.options['fetchTickers'], 'period', '1d'); + const request = { + 'market': market['id'], + 'period': this.timeframes[period], // I1, I5, I15, I30, H1, H2, H4, H6, H12, D1, W1 + }; + const response = await this.publicGetTickers (this.extend (request, params)); + // + // [ + // { + // "ask": 0.017, + // "bid": 0.016, + // "high": 0.019, + // "last": 0.017, + // "low": 0.015, + // "market": "TIME/ETH", + // "open": 0.016, + // "period": "H1", + // "timestamp": "2018-12-14T20:50:36.134Z", + // "volume": 4.57, + // "volumeQuote": 0.07312 + // } + // ] + // + const ticker = this.safeValue (response, 0); + return this.parseTicker (ticker, market); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetOrderbookV2 (this.extend (request, params)); + // + // { + // "timestamp":"2019-12-05T00:21:09.538", + // "bid":[ + // { + // "index":"2", + // "price":"0.02024007", + // "baseTokenAmount":"0.0096894", + // "baseTokenCumulativeAmount":"0.0096894", + // "quoteTokenAmount":"0.000196114134258", + // "quoteTokenCumulativeAmount":"0.000196114134258" + // }, + // "ask":[ + // { + // "index":"-3", + // "price":"0.02024012", + // "baseTokenAmount":"0.005", + // "baseTokenCumulativeAmount":"0.005", + // "quoteTokenAmount":"0.0001012006", + // "quoteTokenCumulativeAmount":"0.0001012006" + // }, + // ] + // } + // + const timestamp = this.parse8601 (this.safeString (response, 'timestamp')); + return this.parseOrderBook (response, timestamp, 'bid', 'ask', 'price', 'baseTokenAmount'); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const options = this.safeValue (this.options, 'fetchTrades', {}); + const defaultSort = this.safeValue (options, 'sort', 'timestamp,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'address': 'string', // trade’s member account (?) + // 'cursor': 1234, // int64 (?) + // 'from': this.iso8601 (since), + 'market': market['id'], + // 'page': 0, // results page you want to retrieve 0 .. N + // 'size': limit, // number of records per page, 100 by default + 'sort': sort, // array[string], sorting criteria in the format "property,asc" or "property,desc", default is ascending + // 'till': this.iso8601 (this.milliseconds ()), + }; + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['size'] = limit; // default is 100 + } + const response = await this.publicGetTrades (this.extend (request, query)); + // + // [ + // { + // "id":1, + // "timestamp":"2019-06-25T17:01:50.309", + // "direction":"BUY", + // "price":"0.027", + // "quantity":"0.001" + // } + // ] + // + return this.parseTrades (response, market, since, limit); + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'period': this.timeframes[timeframe], + }; + // if since and limit are not specified + const duration = this.parseTimeframe (timeframe); + if (since !== undefined) { + request['from'] = this.iso8601 (since); + if (limit !== undefined) { + request['till'] = this.iso8601 (this.sum (since, this.sum (limit, 1) * duration * 1000)); + } + } else if (limit !== undefined) { + const now = this.milliseconds (); + request['till'] = this.iso8601 (now); + request['from'] = this.iso8601 (now - limit * duration * 1000 - 1); + } else { + request['till'] = this.iso8601 (this.milliseconds ()); + } + const response = await this.publicGetCandles (this.extend (request, params)); + // + // [ + // { + // "timestamp":"2019-12-04T23:00:00", + // "open":"0.02024009", + // "high":"0.02024009", + // "low":"0.02024009", + // "close":"0.02024009", + // "volume":"0.00008096036", + // "volumeQuote":"0.004", + // }, + // ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.tradingGetBalances (params); + // + // [ + // {"currency":"BTC","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"AUDT","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"ETH","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"TIME","totalBalance":"0","lockedBalance":"0"}, + // {"currency":"USDT","totalBalance":"0","lockedBalance":"0"} + // ] + // + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['total'] = this.safeFloat (balance, 'totalBalance'); + account['used'] = this.safeFloat (balance, 'lockedBalance'); + result[code] = account; + } + return this.parseBalance (result); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'symbol': market['id'], + 'quantity': this.amountToPrecision (symbol, amount), + 'side': side.toUpperCase (), + // 'clientOrderId': '123', + // 'expireIn': 1575523308, // in seconds + // 'expireTime': 1575523308, // unix timestamp + }; + let query = params; + if (type === 'limit') { + request['price'] = this.priceToPrecision (symbol, price); + const defaultExpireIn = this.safeInteger (this.options, 'expireIn'); + const expireTime = this.safeValue (params, 'expireTime'); + const expireIn = this.safeValue (params, 'expireIn', defaultExpireIn); + if (expireTime !== undefined) { + request['expireTime'] = expireTime; + } else if (expireIn !== undefined) { + request['expireIn'] = expireIn; + } else { + throw new InvalidOrder (this.id + ' createOrder method requires a expireTime or expireIn param for a ' + type + ' order, you can also set the expireIn exchange-wide option'); + } + query = this.omit (params, [ 'expireTime', 'expireIn' ]); + } else { + request['price'] = 0; + } + const response = await this.tradingPostOrders (this.extend (request, query)); + // + // { + // "orders": [ + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + const order = this.safeValue (orders, 0, {}); + return this.parseOrder (order, market); + } + + async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'id': id, + }; + if (amount !== undefined) { + request['quantity'] = this.amountToPrecision (symbol, amount); + } + if (price !== undefined) { + request['price'] = this.priceToPrecision (symbol, price); + } + const response = await this.tradingPutOrders (this.extend (request, params)); + // + // { + // "changedOrders": [ + // { + // "newOrder": { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // }, + // "oldId": "string", + // }, + // ], + // "unchangedOrders": [ "string" ], + // } + // + if ('unchangedOrders' in response) { + const orderIds = this.safeValue (response, 'unchangedOrders', []); + const orderId = this.safeString (orderIds, 0); + return { + 'id': orderId, + 'info': response, + }; + } + const orders = this.safeValue (response, 'changedOrders', []); + const firstOrder = this.safeValue (orders, 0, {}); + const order = this.safeValue (firstOrder, 'newOrder', {}); + return this.parseOrder (order, market); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + return await this.cancelOrders ([ id ], symbol, params); + } + + async cancelOrders (ids, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': ids, + }; + const response = await this.tradingDeleteOrders (this.extend (request, params)); + // + // { + // "changedOrders": [ + // { + // "newOrder": { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // }, + // "oldId": "string", + // }, + // ], + // "unchangedOrders": [ "string" ], + // } + return response; + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'orderHash': id, + }; + const response = await this.historyGetOrdersDetails (request); + // + // { + // "order": { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // }, + // "trades": [ + // { + // "fee": "0.3", + // "id": 100, + // "makerOrTaker": "MAKER", + // "makerOrderId": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "takerOrderId": "string", + // "timestamp": "2019-12-05T07:48:26.310Z" + // } + // ] + // } + // + const order = this.safeValue (response, 'order', {}); + const trades = this.safeValue (response, 'trades', []); + return this.parseOrder (this.extend (order, { 'trades': trades })); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const options = this.safeValue (this.options, 'fetchOpenOrders', {}); + const defaultSort = this.safeValue (options, 'sort', 'createdAt,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'clientOrderId': '123', // order’s client id list for filter + // page: 0, // results page you want to retrieve (0 .. N) + 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.tradingGetOrders (this.extend (request, query)); + // + // { + // "orders": [ + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const options = this.safeValue (this.options, 'fetchClosedOrders', {}); + const defaultSort = this.safeValue (options, 'sort', 'createdAt,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'clientOrderId': '123', // order’s client id list for filter + // page: 0, // results page you want to retrieve (0 .. N) + 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported + 'side': 'BUY', // or 'SELL' + // 'till': this.iso8601 (this.milliseconds ()), + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.historyGetOrders (this.extend (request, query)); + // + // { + // "orders": [ + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // } + // ] + // } + // + const orders = this.safeValue (response, 'orders', []); + return this.parseOrders (orders, market, since, limit); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const options = this.safeValue (this.options, 'fetchMyTrades', {}); + const defaultSort = this.safeValue (options, 'sort', 'timestamp,asc'); + const sort = this.safeString (params, 'sort', defaultSort); + const query = this.omit (params, 'sort'); + const request = { + // 'cursorId': 123, // int64 (?) + // 'from': this.iso8601 (since), + // 'makerOrderId': '1234', // maker order hash + // 'owner': '...', // owner address (?) + // 'page': 0, // results page you want to retrieve (0 .. N) + // 'side': 'BUY', // or 'SELL' + // 'size': limit, + 'sort': sort, // sorting criteria in the format "property,asc" or "property,desc", default order is ascending, multiple sort criteria are supported + // 'symbol': market['id'], + // 'takerOrderId': '1234', + // 'till': this.iso8601 (this.milliseconds ()), + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['symbol'] = market['id']; + } + if (since !== undefined) { + request['from'] = this.iso8601 (since); + } + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.historyGetTrades (this.extend (request, query)); + // + // { + // "trades": [ + // { + // "fee": "0.3", + // "id": 100, + // "makerOrTaker": "MAKER", + // "makerOrderId": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "takerOrderId": "string", + // "timestamp": "2019-12-08T04:54:11.171Z" + // } + // ] + // } + // + const trades = this.safeValue (response, 'trades', []); + return this.parseTrades (trades, market, since, limit); + } + + async fetchTradingFee (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'markets': market['id'], + }; + const response = await this.tradingGetFees (this.extend (request, params)); + // + // [ + // { + // "fee": 0.0075, + // "market": "ETHBTC" + // } + // ] + // + const result = this.safeValue (response, 0, {}); + return { + 'info': response, + 'maker': this.safeFloat (result, 'fee'), + 'taker': undefined, + }; + } + + parseMarket (market) { + // + // { + // "symbol": "ETHBTC", + // "name": "ETH/BTC", + // "baseCurrency": "ETH", + // "baseTokenAddress": "0x45932db54b38af1f5a57136302eeba66a5975c15", + // "quoteCurrency": "BTC", + // "quoteTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "feeCurrency": "BTC", + // "feeTokenAddress": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "quantityIncrement": "0.0000001", + // "takerFee": "0.005", + // "makerFee": "0.0025", + // "tickSize": "0.00000001", + // "baseMinSize": "0.0001", + // "quoteMinSize": "0.00001", + // "locked": false + // } + // + const locked = this.safeValue (market, 'locked'); + const active = !locked; + const id = this.safeString (market, 'symbol'); + const baseId = this.safeString (market, 'baseCurrency'); + const quoteId = this.safeString (market, 'quoteCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.precisionFromString (this.safeString (market, 'quantityIncrement')), + 'price': this.precisionFromString (this.safeString (market, 'tickSize')), + }; + const amountIncrement = this.safeFloat (market, 'quantityIncrement'); + const minBase = this.safeFloat (market, 'baseMinSize'); + const minAmount = Math.max (amountIncrement, minBase); + const priceIncrement = this.safeFloat (market, 'tickSize'); + const minCost = this.safeFloat (market, 'quoteMinSize'); + const limits = { + 'amount': { 'min': minAmount, 'max': undefined }, + 'price': { 'min': priceIncrement, 'max': undefined }, + 'cost': { 'min': Math.max (minCost, minAmount * priceIncrement), 'max': undefined }, + }; + const taker = this.safeFloat (market, 'takerFee'); + const maker = this.safeFloat (market, 'makerFee'); + return { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'type': 'spot', + 'active': active, + 'precision': precision, + 'limits': limits, + 'taker': taker, + 'maker': maker, + 'info': market, + }; + } + + parseCurrency (currency) { + // + // { + // "symbol": "BTC", + // "name": "Bitcoin", + // "address": "0x8370fbc6ddec1e18b4e41e72ed943e238458487c", + // "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR...mc+Cg==", + // "background": "transparent", + // "fiatSymbol": "BTC", + // "decimals": 8, + // "tradeDecimals": 20, + // "displayDecimals": 4, + // "crypto": true, + // "depositEnabled": true, + // "withdrawalEnabled": true, + // "transferEnabled": true, + // "buyEnabled": false, + // "purchaseEnabled": false, + // "redeemEnabled": false, + // "active": true, + // "withdrawalFee": "50000000000000000", + // "purchaseCommissions": [] + // }, + // + const id = this.safeString (currency, 'symbol'); + const code = this.safeCurrencyCode (id); + const name = this.safeString (currency, 'name'); + const precision = this.safeInteger (currency, 'decimals'); + const active = this.safeValue (currency, 'active'); + // const fee = this.safeFloat (currency, 'withdrawalFee'); + const feeString = this.safeString (currency, 'withdrawalFee'); + const feeStringLen = feeString.length; + const tradeDecimals = this.safeInteger (currency, 'tradeDecimals'); + let fee = undefined; + const dotIndex = feeStringLen - tradeDecimals; + if (dotIndex > 0) { + const whole = feeString.slice (0, dotIndex); + const fraction = feeString.slice (-dotIndex); + fee = parseFloat (whole + '.' + fraction); + } else { + let fraction = '.'; + for (let i = 0; i < -dotIndex; i++) { + fraction += '0'; + } + fee = parseFloat (fraction + feeString); + } + return { + 'id': code, + 'code': code, + 'info': currency, + 'type': undefined, + 'name': name, + 'active': active, + 'fee': fee, + 'precision': precision, + 'limits': { + 'withdraw': { 'min': fee, 'max': undefined }, + 'amount': { 'min': undefined, 'max': undefined }, + 'price': { 'min': undefined, 'max': undefined }, + 'cost': { 'min': undefined, 'max': undefined }, + }, + }; + } + + parseTickers (rawTickers, symbols = undefined) { + const tickers = []; + for (let i = 0; i < rawTickers.length; i++) { + tickers.push (this.parseTicker (rawTickers[i])); + } + return this.filterByArray (tickers, 'symbol', symbols); + } + + parseTicker (ticker, market = undefined) { + // + // { + // "ask": 0.017, + // "bid": 0.016, + // "high": 0.019, + // "last": 0.017, + // "low": 0.015, + // "market": "TIME/ETH", + // "open": 0.016, + // "period": "H1", + // "timestamp": "2018-12-14T20:50:36.134Z", + // "volume": 4.57, + // "volumeQuote": 0.07312 + // } + // + let symbol = undefined; + const marketId = this.safeString (ticker, 'market'); + if (marketId !== undefined) { + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp')); + const last = this.safeFloat (ticker, 'last'); + const open = this.safeFloat (ticker, 'open'); + let change = undefined; + let average = undefined; + if (last !== undefined && open !== undefined) { + change = last - open; + average = this.sum (last, open) / 2; + } + let percentage = undefined; + if (change !== undefined && open) { + percentage = (change / open) * 100; + } + return { + 'symbol': symbol, + 'info': ticker, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': open, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': average, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'volumeQuote'), + }; + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades (public) + // + // { + // "id":1, + // "timestamp":"2019-06-25T17:01:50.309", + // "direction":"BUY", + // "price":"0.027", + // "quantity":"0.001" + // } + // + // fetchMyTrades, fetchOrder (private) + // + // { + // "fee": "0.3", + // "id": 100, + // "makerOrTaker": "MAKER", + // "makerOrderId": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "takerOrderId": "string", + // "timestamp": "2019-12-08T04:54:11.171Z" + // } + // + let symbol = undefined; + const marketId = this.safeString (trade, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (trade, 'timestamp')); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'quantity'); + const id = this.safeString (trade, 'id'); + const side = this.safeStringLower2 (trade, 'direction', 'side'); + const takerOrMaker = this.safeStringLower (trade, 'makerOrTaker'); + let orderId = undefined; + if (takerOrMaker !== undefined) { + orderId = this.safeString (trade, takerOrMaker + 'OrderId'); + } + let fee = undefined; + const feeCost = this.safeFloat (trade, 'fee'); + if (feeCost !== undefined) { + const feeCurrency = (market === undefined) ? undefined : market['quote']; + fee = { + 'cost': feeCost, + 'currency': feeCurrency, + }; + } + let cost = undefined; + if ((price !== undefined) && (amount !== undefined)) { + cost = this.costToPrecision (symbol, amount * price); + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': orderId, + 'type': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'takerOrMaker': takerOrMaker, + 'fee': fee, + }; + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + // + // { + // "timestamp":"2019-12-04T23:00:00", + // "open":"0.02024009", + // "high":"0.02024009", + // "low":"0.02024009", + // "close":"0.02024009", + // "volume":"0.00008096036", + // "volumeQuote":"0.004", + // } + // + return [ + this.parse8601 (this.safeString (ohlcv, 'timestamp')), + this.safeFloat (ohlcv, 'open'), + this.safeFloat (ohlcv, 'high'), + this.safeFloat (ohlcv, 'low'), + this.safeFloat (ohlcv, 'close'), + this.safeFloat (ohlcv, 'volumeQuote'), + ]; + } + + parseOrder (order, market = undefined) { + // + // fetchOrder, createOrder, cancelOrder, cancelOrders, fetchOpenOrders, fetchClosedOrders + // + // { + // "cancelledQuantity": "0.3", + // "clientOrderId": "my-order-1", + // "createdAt": "1970-01-01T00:00:00", + // "cursorId": 50, + // "expireTime": "1970-01-01T00:00:00", + // "filledQuantity": "0.3", + // "id": "string", + // "price": "0.017", + // "quantity": "0.3", + // "side": "BUY", + // "symbol": "TIMEETH", + // "type": "LIMIT", + // "updatedAt": "1970-01-01T00:00:00" + // "trades": [], // injected from the outside + // } + // + const id = this.safeString (order, 'id'); + const type = this.safeStringLower (order, 'type'); + const side = this.safeStringLower (order, 'side'); + let symbol = undefined; + const marketId = this.safeString (order, 'symbol'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } + if ((symbol === undefined) && (market !== undefined)) { + symbol = market['symbol']; + } + const timestamp = this.parse8601 (this.safeString (order, 'createdAt')); + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'quantity'); + const filled = this.safeFloat (order, 'filledQuantity'); + const canceledQuantity = this.safeFloat (order, 'cancelledQuantity'); + let remaining = undefined; + let status = undefined; + if ((amount !== undefined) && (filled !== undefined)) { + remaining = Math.max (amount - filled, 0.0); + if (filled >= amount) { + status = 'closed'; + } else if ((canceledQuantity !== undefined) && (canceledQuantity > 0)) { + status = 'canceled'; + } else { + status = 'open'; + } + } + const cost = parseFloat (this.costToPrecision (symbol, price * filled)); + const fee = undefined; + let lastTradeTimestamp = undefined; + let trades = undefined; + const rawTrades = this.safeValue (order, 'trades'); + if (rawTrades !== undefined) { + trades = this.parseTrades (rawTrades, market, undefined, undefined, { + 'order': id, + }); + } + if (trades !== undefined) { + const numTrades = trades.length; + if (numTrades > 0) { + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + } + } + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'average': undefined, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/' + api + '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencodeWithArrayRepeat (params); + } + if (api !== 'public') { + this.checkRequiredCredentials (); + const auth = this.stringToBase64 (this.encode (this.apiKey + ':' + this.secret)); + const secret = 'Basic ' + this.decode (auth); + headers = { 'authorization': secret }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + if (statusCode >= 400) { + // + // {"error":{"timestamp":"05.12.2019T05:25:43.584+0000","status":"BAD_REQUEST","message":"Insufficient ETH balance. Required: 1, actual: 0.","code":4001}} + // {"error":{"timestamp":"05.12.2019T04:03:25.419+0000","status":"FORBIDDEN","message":"Access denied","code":4300}} + // + const feedback = this.id + ' ' + responseBody; + let error = this.safeValue (response, 'error'); + if (error === undefined) { + error = response; + } + const code = this.safeString2 (error, 'code', 'status'); + const message = this.safeString2 (error, 'message', 'debugMessage'); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/upbit.js b/TaskServer/node_modules/ccxt/js/upbit.js new file mode 100644 index 000000000..763aa9a78 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/upbit.js @@ -0,0 +1,1528 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class upbit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'upbit', + 'name': 'Upbit', + 'countries': [ 'KR' ], + 'version': 'v1', + 'rateLimit': 1000, + 'certified': true, + // new metainfo interface + 'has': { + 'CORS': true, + 'createDepositAddress': true, + 'createMarketOrder': true, + 'fetchDepositAddress': true, + 'fetchClosedOrders': true, + 'fetchMyTrades': false, + 'fetchOHLCV': true, + 'fetchOrder': true, + 'fetchOrderBooks': true, + 'fetchOpenOrders': true, + 'fetchOrders': false, + 'fetchTickers': true, + 'withdraw': true, + 'fetchDeposits': true, + 'fetchWithdrawals': true, + 'fetchTransactions': false, + }, + 'timeframes': { + '1m': 'minutes', + '3m': 'minutes', + '5m': 'minutes', + '15m': 'minutes', + '30m': 'minutes', + '1h': 'minutes', + '4h': 'minutes', + '1d': 'days', + '1w': 'weeks', + '1M': 'months', + }, + 'hostname': 'api.upbit.com', + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg', + 'api': 'https://{hostname}', + 'www': 'https://upbit.com', + 'doc': 'https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C', + 'fees': 'https://upbit.com/service_center/guide', + }, + 'api': { + 'public': { + 'get': [ + 'market/all', + 'candles/{timeframe}', + 'candles/{timeframe}/{unit}', + 'candles/minutes/{unit}', + 'candles/minutes/1', + 'candles/minutes/3', + 'candles/minutes/5', + 'candles/minutes/15', + 'candles/minutes/30', + 'candles/minutes/60', + 'candles/minutes/240', + 'candles/days', + 'candles/weeks', + 'candles/months', + 'trades/ticks', + 'ticker', + 'orderbook', + ], + }, + 'private': { + 'get': [ + 'accounts', + 'orders/chance', + 'order', + 'orders', + 'withdraws', + 'withdraw', + 'withdraws/chance', + 'deposits', + 'deposit', + 'deposits/coin_addresses', + 'deposits/coin_address', + ], + 'post': [ + 'orders', + 'withdraws/coin', + 'withdraws/krw', + 'deposits/generate_coin_address', + ], + 'delete': [ + 'order', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'maker': 0.0025, + 'taker': 0.0025, + }, + 'funding': { + 'tierBased': false, + 'percentage': false, + 'withdraw': {}, + 'deposit': {}, + }, + }, + 'exceptions': { + 'exact': { + 'This key has expired.': AuthenticationError, + 'Missing request parameter error. Check the required parameters!': BadRequest, + 'side is missing, side does not have a valid value': InvalidOrder, + }, + 'broad': { + 'thirdparty_agreement_required': PermissionDenied, + 'out_of_scope': PermissionDenied, + 'order_not_found': OrderNotFound, + 'insufficient_funds': InsufficientFunds, + 'invalid_access_key': AuthenticationError, + 'jwt_verification': AuthenticationError, + 'create_ask_error': ExchangeError, + 'create_bid_error': ExchangeError, + 'volume_too_large': InvalidOrder, + 'invalid_funds': InvalidOrder, + }, + }, + 'options': { + 'createMarketBuyOrderRequiresPrice': true, + 'fetchTickersMaxLength': 4096, // 2048, + 'fetchOrderBooksMaxLength': 4096, // 2048, + 'symbolSeparator': '-', + 'tradingFeesByQuoteCurrency': { + 'KRW': 0.0005, + }, + }, + }); + } + + async fetchCurrency (code, params = {}) { + // this method is for retrieving funding fees and limits per currency + // it requires private access and API keys properly set up + await this.loadMarkets (); + const currency = this.currency (code); + return await this.fetchCurrencyById (currency['id'], params); + } + + async fetchCurrencyById (id, params = {}) { + // this method is for retrieving funding fees and limits per currency + // it requires private access and API keys properly set up + const request = { + 'currency': id, + }; + const response = await this.privateGetWithdrawsChance (this.extend (request, params)); + // + // { + // "member_level": { + // "security_level": 3, + // "fee_level": 0, + // "email_verified": true, + // "identity_auth_verified": true, + // "bank_account_verified": true, + // "kakao_pay_auth_verified": false, + // "locked": false, + // "wallet_locked": false + // }, + // "currency": { + // "code": "BTC", + // "withdraw_fee": "0.0005", + // "is_coin": true, + // "wallet_state": "working", + // "wallet_support": [ "deposit", "withdraw" ] + // }, + // "account": { + // "currency": "BTC", + // "balance": "10.0", + // "locked": "0.0", + // "avg_krw_buy_price": "8042000", + // "modified": false + // }, + // "withdraw_limit": { + // "currency": "BTC", + // "minimum": null, + // "onetime": null, + // "daily": "10.0", + // "remaining_daily": "10.0", + // "remaining_daily_krw": "0.0", + // "fixed": null, + // "can_withdraw": true + // } + // } + // + const memberInfo = this.safeValue (response, 'member_level', {}); + const currencyInfo = this.safeValue (response, 'currency', {}); + const withdrawLimits = this.safeValue (response, 'withdraw_limit', {}); + const canWithdraw = this.safeValue (withdrawLimits, 'can_withdraw'); + const walletState = this.safeString (currencyInfo, 'wallet_state'); + const walletLocked = this.safeValue (memberInfo, 'wallet_locked'); + const locked = this.safeValue (memberInfo, 'locked'); + let active = true; + if ((canWithdraw !== undefined) && canWithdraw) { + active = false; + } else if (walletState !== 'working') { + active = false; + } else if ((walletLocked !== undefined) && walletLocked) { + active = false; + } else if ((locked !== undefined) && locked) { + active = false; + } + const maxOnetimeWithdrawal = this.safeFloat (withdrawLimits, 'onetime'); + const maxDailyWithdrawal = this.safeFloat (withdrawLimits, 'daily', maxOnetimeWithdrawal); + const remainingDailyWithdrawal = this.safeFloat (withdrawLimits, 'remaining_daily', maxDailyWithdrawal); + let maxWithdrawLimit = undefined; + if (remainingDailyWithdrawal > 0) { + maxWithdrawLimit = remainingDailyWithdrawal; + } else { + maxWithdrawLimit = maxDailyWithdrawal; + } + const precision = undefined; + const currencyId = this.safeString (currencyInfo, 'code'); + const code = this.safeCurrencyCode (currencyId); + return { + 'info': response, + 'id': currencyId, + 'code': code, + 'name': code, + 'active': active, + 'fee': this.safeFloat (currencyInfo, 'withdraw_fee'), + 'precision': precision, + 'limits': { + 'withdraw': { + 'min': this.safeFloat (withdrawLimits, 'minimum'), + 'max': maxWithdrawLimit, + }, + }, + }; + } + + async fetchMarket (symbol, params = {}) { + // this method is for retrieving trading fees and limits per market + // it requires private access and API keys properly set up + await this.loadMarkets (); + const market = this.market (symbol); + return await this.fetchMarketById (market['id'], params); + } + + async fetchMarketById (id, params = {}) { + // this method is for retrieving trading fees and limits per market + // it requires private access and API keys properly set up + const request = { + 'market': id, + }; + const response = await this.privateGetOrdersChance (this.extend (request, params)); + // + // { bid_fee: "0.0005", + // ask_fee: "0.0005", + // market: { id: "KRW-BTC", + // name: "BTC/KRW", + // order_types: ["limit"], + // order_sides: ["ask", "bid"], + // bid: { currency: "KRW", + // price_unit: null, + // min_total: 1000 }, + // ask: { currency: "BTC", + // price_unit: null, + // min_total: 1000 }, + // max_total: "1000000000.0", + // state: "active" }, + // bid_account: { currency: "KRW", + // balance: "0.0", + // locked: "0.0", + // avg_krw_buy_price: "0", + // modified: false }, + // ask_account: { currency: "BTC", + // balance: "0.00780836", + // locked: "0.0", + // avg_krw_buy_price: "6465564.67", + // modified: false } } + // + const marketInfo = this.safeValue (response, 'market'); + const bid = this.safeValue (marketInfo, 'bid'); + const ask = this.safeValue (marketInfo, 'ask'); + const marketId = this.safeString (marketInfo, 'id'); + const baseId = this.safeString (ask, 'currency'); + const quoteId = this.safeString (bid, 'currency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + const state = this.safeString (marketInfo, 'state'); + const active = (state === 'active'); + const bidFee = this.safeFloat (response, 'bid_fee'); + const askFee = this.safeFloat (response, 'ask_fee'); + const fee = Math.max (bidFee, askFee); + return { + 'info': response, + 'id': marketId, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'precision': precision, + 'maker': fee, + 'taker': fee, + 'limits': { + 'amount': { + 'min': this.safeFloat (ask, 'min_total'), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (bid, 'min_total'), + 'max': this.safeFloat (marketInfo, 'max_total'), + }, + }, + }; + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetMarketAll (params); + // + // [ { market: "KRW-BTC", + // korean_name: "비트코ì�¸", + // english_name: "Bitcoin" }, + // { market: "KRW-DASH", + // korean_name: "대시", + // english_name: "Dash" }, + // { market: "KRW-ETH", + // korean_name: "ì�´ë�”리움", + // english_name: "Ethereum" }, + // { market: "BTC-ETH", + // korean_name: "ì�´ë�”리움", + // english_name: "Ethereum" }, + // ..., + // { market: "BTC-BSV", + // korean_name: "비트코ì�¸ì—�스브ì�´", + // english_name: "Bitcoin SV" } ] + // + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'market'); + const [ quoteId, baseId ] = id.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': 8, + 'price': 8, + }; + const active = true; + const makerFee = this.safeFloat (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['maker']); + const takerFee = this.safeFloat (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['taker']); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'info': market, + 'precision': precision, + 'maker': makerFee, + 'taker': takerFee, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetAccounts (params); + // + // [ { currency: "BTC", + // balance: "0.005", + // locked: "0.0", + // avg_krw_buy_price: "7446000", + // modified: false }, + // { currency: "ETH", + // balance: "0.1", + // locked: "0.0", + // avg_krw_buy_price: "250000", + // modified: false } ] + // + const result = { 'info': response }; + for (let i = 0; i < response.length; i++) { + const balance = response[i]; + const currencyId = this.safeString (balance, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'balance'); + account['used'] = this.safeFloat (balance, 'locked'); + result[code] = account; + } + return this.parseBalance (result); + } + + getSymbolFromMarketId (marketId, market = undefined) { + if (marketId === undefined) { + return undefined; + } + market = this.safeValue (this.markets_by_id, marketId, market); + if (market !== undefined) { + return market['symbol']; + } + const [ baseId, quoteId ] = marketId.split (this.options['symbolSeparator']); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + return base + '/' + quote; + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join (','); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchOrderBooksMaxLength']) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols (' + ids.length.toString () + ' characters) exceeding max URL length (' + this.options['fetchOrderBooksMaxLength'].toString () + ' characters), you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join (','); + } + const request = { + 'markets': ids, + }; + const response = await this.publicGetOrderbook (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // timestamp: 1542899030043, + // total_ask_size: 109.57065201, + // total_bid_size: 125.74430631, + // orderbook_units: [ { ask_price: 0.02926679, + // bid_price: 0.02919904, + // ask_size: 4.20293961, + // bid_size: 11.65043576 }, + // ..., + // { ask_price: 0.02938209, + // bid_price: 0.0291231, + // ask_size: 0.05135782, + // bid_size: 13.5595 } ] }, + // { market: "KRW-BTC", + // timestamp: 1542899034662, + // total_ask_size: 12.89790974, + // total_bid_size: 4.88395783, + // orderbook_units: [ { ask_price: 5164000, + // bid_price: 5162000, + // ask_size: 2.57606495, + // bid_size: 0.214 }, + // ..., + // { ask_price: 5176000, + // bid_price: 5152000, + // ask_size: 2.752, + // bid_size: 0.4650305 } ] } ] + // + const result = {}; + for (let i = 0; i < response.length; i++) { + const orderbook = response[i]; + const symbol = this.getSymbolFromMarketId (this.safeString (orderbook, 'market')); + const timestamp = this.safeInteger (orderbook, 'timestamp'); + result[symbol] = { + 'bids': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'bid_price', 'bid_size'), 0, true), + 'asks': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'ask_price', 'ask_size'), 0), + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'nonce': undefined, + }; + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + const orderbooks = await this.fetchOrderBooks ([ symbol ], limit, params); + return this.safeValue (orderbooks, symbol); + } + + parseTicker (ticker, market = undefined) { + // + // { market: "BTC-ETH", + // trade_date: "20181122", + // trade_time: "104543", + // trade_date_kst: "20181122", + // trade_time_kst: "194543", + // trade_timestamp: 1542883543097, + // opening_price: 0.02976455, + // high_price: 0.02992577, + // low_price: 0.02934283, + // trade_price: 0.02947773, + // prev_closing_price: 0.02966, + // change: "FALL", + // change_price: 0.00018227, + // change_rate: 0.0061453136, + // signed_change_price: -0.00018227, + // signed_change_rate: -0.0061453136, + // trade_volume: 1.00000005, + // acc_trade_price: 100.95825586, + // acc_trade_price_24h: 289.58650166, + // acc_trade_volume: 3409.85311036, + // acc_trade_volume_24h: 9754.40510513, + // highest_52_week_price: 0.12345678, + // highest_52_week_date: "2018-02-01", + // lowest_52_week_price: 0.023936, + // lowest_52_week_date: "2017-12-08", + // timestamp: 1542883543813 } + // + const timestamp = this.safeInteger (ticker, 'trade_timestamp'); + const symbol = this.getSymbolFromMarketId (this.safeString (ticker, 'market'), market); + const previous = this.safeFloat (ticker, 'prev_closing_price'); + const last = this.safeFloat (ticker, 'trade_price'); + const change = this.safeFloat (ticker, 'signed_change_price'); + const percentage = this.safeFloat (ticker, 'signed_change_rate'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high_price'), + 'low': this.safeFloat (ticker, 'low_price'), + 'bid': undefined, + 'bidVolume': undefined, + 'ask': undefined, + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'opening_price'), + 'close': last, + 'last': last, + 'previousClose': previous, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'acc_trade_volume_24h'), + 'quoteVolume': this.safeFloat (ticker, 'acc_trade_price_24h'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join (','); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join (','); + } + const request = { + 'markets': ids, + }; + const response = await this.publicGetTicker (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // trade_date: "20181122", + // trade_time: "104543", + // trade_date_kst: "20181122", + // trade_time_kst: "194543", + // trade_timestamp: 1542883543097, + // opening_price: 0.02976455, + // high_price: 0.02992577, + // low_price: 0.02934283, + // trade_price: 0.02947773, + // prev_closing_price: 0.02966, + // change: "FALL", + // change_price: 0.00018227, + // change_rate: 0.0061453136, + // signed_change_price: -0.00018227, + // signed_change_rate: -0.0061453136, + // trade_volume: 1.00000005, + // acc_trade_price: 100.95825586, + // acc_trade_price_24h: 289.58650166, + // acc_trade_volume: 3409.85311036, + // acc_trade_volume_24h: 9754.40510513, + // highest_52_week_price: 0.12345678, + // highest_52_week_date: "2018-02-01", + // lowest_52_week_price: 0.023936, + // lowest_52_week_date: "2017-12-08", + // timestamp: 1542883543813 } ] + // + const result = {}; + for (let t = 0; t < response.length; t++) { + const ticker = this.parseTicker (response[t]); + const symbol = ticker['symbol']; + result[symbol] = ticker; + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return this.safeValue (tickers, symbol); + } + + parseTrade (trade, market = undefined) { + // + // fetchTrades + // + // { market: "BTC-ETH", + // trade_date_utc: "2018-11-22", + // trade_time_utc: "13:55:24", + // timestamp: 1542894924397, + // trade_price: 0.02914289, + // trade_volume: 0.20074397, + // prev_closing_price: 0.02966, + // change_price: -0.00051711, + // ask_bid: "ASK", + // sequential_id: 15428949259430000 } + // + // fetchOrder trades + // + // { + // "market": "KRW-BTC", + // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", + // "price": "101000.0", + // "volume": "0.77368323", + // "funds": "78142.00623", + // "ask_fee": "117.213009345", + // "bid_fee": "117.213009345", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid", + // } + // + const id = this.safeString2 (trade, 'sequential_id', 'uuid'); + const orderId = undefined; + let timestamp = this.safeInteger (trade, 'timestamp'); + if (timestamp === undefined) { + timestamp = this.parse8601 (this.safeString (trade, 'created_at')); + } + let side = undefined; + const askOrBid = this.safeStringLower2 (trade, 'ask_bid', 'side'); + if (askOrBid === 'ask') { + side = 'sell'; + } else if (askOrBid === 'bid') { + side = 'buy'; + } + let cost = this.safeFloat (trade, 'funds'); + const price = this.safeFloat2 (trade, 'trade_price', 'price'); + const amount = this.safeFloat2 (trade, 'trade_volume', 'volume'); + if (cost === undefined) { + if (amount !== undefined) { + if (price !== undefined) { + cost = price * amount; + } + } + } + const marketId = this.safeString (trade, 'market'); + market = this.safeValue (this.markets_by_id, marketId); + let fee = undefined; + let feeCurrency = undefined; + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + const feeCost = this.safeString (trade, askOrBid + '_fee'); + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + } + return { + 'id': id, + 'info': trade, + 'order': orderId, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 200; + } + const request = { + 'market': market['id'], + 'count': limit, + }; + const response = await this.publicGetTradesTicks (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // trade_date_utc: "2018-11-22", + // trade_time_utc: "13:55:24", + // timestamp: 1542894924397, + // trade_price: 0.02914289, + // trade_volume: 0.20074397, + // prev_closing_price: 0.02966, + // change_price: -0.00051711, + // ask_bid: "ASK", + // sequential_id: 15428949259430000 }, + // { market: "BTC-ETH", + // trade_date_utc: "2018-11-22", + // trade_time_utc: "13:03:10", + // timestamp: 1542891790123, + // trade_price: 0.02917, + // trade_volume: 7.392, + // prev_closing_price: 0.02966, + // change_price: -0.00049, + // ask_bid: "ASK", + // sequential_id: 15428917910540000 } ] + // + return this.parseTrades (response, market, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) { + // + // { market: "BTC-ETH", + // candle_date_time_utc: "2018-11-22T13:47:00", + // candle_date_time_kst: "2018-11-22T22:47:00", + // opening_price: 0.02915963, + // high_price: 0.02915963, + // low_price: 0.02915448, + // trade_price: 0.02915448, + // timestamp: 1542894473674, + // candle_acc_trade_price: 0.0981629437535248, + // candle_acc_trade_volume: 3.36693173, + // unit: 1 }, + // + return [ + this.parse8601 (this.safeString (ohlcv, 'candle_date_time_utc')), + this.safeFloat (ohlcv, 'opening_price'), + this.safeFloat (ohlcv, 'high_price'), + this.safeFloat (ohlcv, 'low_price'), + this.safeFloat (ohlcv, 'trade_price'), + this.safeFloat (ohlcv, 'candle_acc_trade_volume'), // base volume + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const timeframePeriod = this.parseTimeframe (timeframe); + const timeframeValue = this.timeframes[timeframe]; + if (limit === undefined) { + limit = 200; + } + const request = { + 'market': market['id'], + 'timeframe': timeframeValue, + 'count': limit, + }; + let method = 'publicGetCandlesTimeframe'; + if (timeframeValue === 'minutes') { + const numMinutes = Math.round (timeframePeriod / 60); + request['unit'] = numMinutes; + method += 'Unit'; + } + if (since !== undefined) { + // convert `since` to `to` value + request['to'] = this.iso8601 (this.sum (since, timeframePeriod * limit * 1000)); + } + const response = await this[method] (this.extend (request, params)); + // + // [ { market: "BTC-ETH", + // candle_date_time_utc: "2018-11-22T13:47:00", + // candle_date_time_kst: "2018-11-22T22:47:00", + // opening_price: 0.02915963, + // high_price: 0.02915963, + // low_price: 0.02915448, + // trade_price: 0.02915448, + // timestamp: 1542894473674, + // candle_acc_trade_price: 0.0981629437535248, + // candle_acc_trade_volume: 3.36693173, + // unit: 1 }, + // { market: "BTC-ETH", + // candle_date_time_utc: "2018-11-22T10:06:00", + // candle_date_time_kst: "2018-11-22T19:06:00", + // opening_price: 0.0294, + // high_price: 0.02940882, + // low_price: 0.02934283, + // trade_price: 0.02937354, + // timestamp: 1542881219276, + // candle_acc_trade_price: 0.0762597110943884, + // candle_acc_trade_volume: 2.5949617, + // unit: 1 } ] + // + return this.parseOHLCVs (response, market, timeframe, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + // for market buy it requires the amount of quote currency to spend + if (side === 'buy') { + if (this.options['createMarketBuyOrderRequiresPrice']) { + if (price === undefined) { + throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)"); + } else { + amount = amount * price; + } + } + } + } + let orderSide = undefined; + if (side === 'buy') { + orderSide = 'bid'; + } else if (side === 'sell') { + orderSide = 'ask'; + } else { + throw new InvalidOrder (this.id + ' createOrder allows buy or sell side only!'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'side': orderSide, + }; + if (type === 'limit') { + request['volume'] = this.amountToPrecision (symbol, amount); + request['price'] = this.priceToPrecision (symbol, price); + request['ord_type'] = type; + } else if (type === 'market') { + if (side === 'buy') { + request['ord_type'] = 'price'; + request['price'] = this.priceToPrecision (symbol, amount); + } else if (side === 'sell') { + request['ord_type'] = type; + request['volume'] = this.amountToPrecision (symbol, amount); + } + } + const response = await this.privatePostOrders (this.extend (request, params)); + // + // { + // 'uuid': 'cdd92199-2897-4e14-9448-f923320408ad', + // 'side': 'bid', + // 'ord_type': 'limit', + // 'price': '100.0', + // 'avg_price': '0.0', + // 'state': 'wait', + // 'market': 'KRW-BTC', + // 'created_at': '2018-04-10T15:42:23+09:00', + // 'volume': '0.01', + // 'remaining_volume': '0.01', + // 'reserved_fee': '0.0015', + // 'remaining_fee': '0.0015', + // 'paid_fee': '0.0', + // 'locked': '1.0015', + // 'executed_volume': '0.0', + // 'trades_count': 0 + // } + // + return this.parseOrder (response); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'uuid': id, + }; + const response = await this.privateDeleteOrder (this.extend (request, params)); + // + // { + // "uuid": "cdd92199-2897-4e14-9448-f923320408ad", + // "side": "bid", + // "ord_type": "limit", + // "price": "100.0", + // "state": "wait", + // "market": "KRW-BTC", + // "created_at": "2018-04-10T15:42:23+09:00", + // "volume": "0.01", + // "remaining_volume": "0.01", + // "reserved_fee": "0.0015", + // "remaining_fee": "0.0015", + // "paid_fee": "0.0", + // "locked": "1.0015", + // "executed_volume": "0.0", + // "trades_count": 0 + // } + // + return this.parseOrder (response); + } + + async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'page': 1, + // 'order_by': 'asc', // 'desc' + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + const response = await this.privateGetDeposits (this.extend (request, params)); + // + // [ + // { + // "type": "deposit", + // "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830", + // "currency": "KRW", + // "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a", + // "state": "ACCEPTED", + // "created_at": "2017-12-08T15:38:02+09:00", + // "done_at": "2017-12-08T15:38:02+09:00", + // "amount": "100000.0", + // "fee": "0.0" + // }, + // ..., + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'state': 'submitting', // 'submitted', 'almost_accepted', 'rejected', 'accepted', 'processing', 'done', 'canceled' + }; + let currency = undefined; + if (code !== undefined) { + currency = this.currency (code); + request['currency'] = currency['id']; + } + if (limit !== undefined) { + request['limit'] = limit; // default is 100 + } + const response = await this.privateGetWithdraws (this.extend (request, params)); + // + // [ + // { + // "type": "withdraw", + // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", + // "currency": "BTC", + // "txid": null, + // "state": "processing", + // "created_at": "2018-04-13T11:24:01+09:00", + // "done_at": null, + // "amount": "0.01", + // "fee": "0.0", + // "krw_amount": "80420.0" + // }, + // ..., + // ] + // + return this.parseTransactions (response, currency, since, limit); + } + + parseTransactionStatus (status) { + const statuses = { + 'ACCEPTED': 'ok', // deposits + // withdrawals: + 'submitting': 'pending', // 처리 중 + 'submitted': 'pending', // 처리 완료 + 'almost_accepted': 'pending', // 출금대기중 + 'rejected': 'failed', // ê±°ë¶€ + 'accepted': 'pending', // 승ì�¸ë�¨ + 'processing': 'pending', // 처리 중 + 'done': 'ok', // 완료 + 'canceled': 'canceled', // 취소ë�¨ + }; + return this.safeString (statuses, status, status); + } + + parseTransaction (transaction, currency = undefined) { + // + // fetchDeposits + // + // { + // "type": "deposit", + // "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830", + // "currency": "KRW", + // "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a", + // "state": "ACCEPTED", + // "created_at": "2017-12-08T15:38:02+09:00", + // "done_at": "2017-12-08T15:38:02+09:00", + // "amount": "100000.0", + // "fee": "0.0" + // } + // + // fetchWithdrawals + // + // { + // "type": "withdraw", + // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", + // "currency": "BTC", + // "txid": "cd81e9b45df8da29f936836e58c907a106057e454a45767a7b06fcb19b966bba", + // "state": "processing", + // "created_at": "2018-04-13T11:24:01+09:00", + // "done_at": null, + // "amount": "0.01", + // "fee": "0.0", + // "krw_amount": "80420.0" + // } + // + const id = this.safeString (transaction, 'uuid'); + const amount = this.safeFloat (transaction, 'amount'); + const address = undefined; // not present in the data structure received from the exchange + const tag = undefined; // not present in the data structure received from the exchange + const txid = this.safeString (transaction, 'txid'); + const updated = this.parse8601 (this.safeString (transaction, 'done_at')); + const timestamp = this.parse8601 (this.safeString (transaction, 'created_at', updated)); + let type = this.safeString (transaction, 'type'); + if (type === 'withdraw') { + type = 'withdrawal'; + } + const currencyId = this.safeString (transaction, 'currency'); + const code = this.safeCurrencyCode (currencyId); + const status = this.parseTransactionStatus (this.safeString (transaction, 'state')); + const feeCost = this.safeFloat (transaction, 'fee'); + return { + 'info': transaction, + 'id': id, + 'currency': code, + 'amount': amount, + 'address': address, + 'tag': tag, + 'status': status, + 'type': type, + 'updated': updated, + 'txid': txid, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'fee': { + 'currency': code, + 'cost': feeCost, + }, + }; + } + + parseOrderStatus (status) { + const statuses = { + 'wait': 'open', + 'done': 'closed', + 'cancel': 'canceled', + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + // + // { + // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", + // "side": "bid", + // "ord_type": "limit", + // "price": "17417000.0", + // "state": "done", + // "market": "KRW-BTC", + // "created_at": "2018-04-05T14:09:14+09:00", + // "volume": "1.0", + // "remaining_volume": "0.0", + // "reserved_fee": "26125.5", + // "remaining_fee": "25974.0", + // "paid_fee": "151.5", + // "locked": "17341974.0", + // "executed_volume": "1.0", + // "trades_count": 2, + // "trades": [ + // { + // "market": "KRW-BTC", + // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", + // "price": "101000.0", + // "volume": "0.77368323", + // "funds": "78142.00623", + // "ask_fee": "117.213009345", + // "bid_fee": "117.213009345", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid", + // }, + // { + // "market": "KRW-BTC", + // "uuid": "f73da467-c42f-407d-92fa-e10d86450a20", + // "price": "101000.0", + // "volume": "0.22631677", + // "funds": "22857.99377", + // "ask_fee": "34.286990655", // missing in market orders + // "bid_fee": "34.286990655", // missing in market orders + // "created_at": "2018-04-05T14:09:15+09:00", // missing in market orders + // "side": "bid", + // }, + // ], + // } + // + const id = this.safeString (order, 'uuid'); + let side = this.safeString (order, 'side'); + if (side === 'bid') { + side = 'buy'; + } else { + side = 'sell'; + } + let type = this.safeString (order, 'ord_type'); + const timestamp = this.parse8601 (this.safeString (order, 'created_at')); + const status = this.parseOrderStatus (this.safeString (order, 'state')); + let lastTradeTimestamp = undefined; + let price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'volume'); + const remaining = this.safeFloat (order, 'remaining_volume'); + const filled = this.safeFloat (order, 'executed_volume'); + let cost = undefined; + if (type === 'price') { + type = 'market'; + cost = price; + price = undefined; + } + let average = undefined; + let fee = undefined; + let feeCost = this.safeFloat (order, 'paid_fee'); + let feeCurrency = undefined; + const marketId = this.safeString (order, 'market'); + market = this.safeValue (this.markets_by_id, marketId); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + feeCurrency = market['quote']; + } else { + const [ baseId, quoteId ] = marketId.split ('-'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + feeCurrency = quote; + } + let trades = this.safeValue (order, 'trades', []); + trades = this.parseTrades (trades, market, undefined, undefined, { 'order': id }); + const numTrades = trades.length; + if (numTrades > 0) { + // the timestamp in fetchOrder trades is missing + lastTradeTimestamp = trades[numTrades - 1]['timestamp']; + let getFeesFromTrades = false; + if (feeCost === undefined) { + getFeesFromTrades = true; + feeCost = 0; + } + cost = 0; + for (let i = 0; i < numTrades; i++) { + const trade = trades[i]; + cost = this.sum (cost, trade['cost']); + if (getFeesFromTrades) { + const tradeFee = this.safeValue (trades[i], 'fee', {}); + const tradeFeeCost = this.safeFloat (tradeFee, 'cost'); + if (tradeFeeCost !== undefined) { + feeCost = this.sum (feeCost, tradeFeeCost); + } + } + } + average = cost / filled; + } + if (feeCost !== undefined) { + fee = { + 'currency': feeCurrency, + 'cost': feeCost, + }; + } + const result = { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': lastTradeTimestamp, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'average': average, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': fee, + 'trades': trades, + }; + return result; + } + + async fetchOrdersByState (state, symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + // 'market': this.marketId (symbol), + 'state': state, + // 'page': 1, + // 'order_by': 'asc', + }; + let market = undefined; + if (symbol !== undefined) { + market = this.market (symbol); + request['market'] = market['id']; + } + const response = await this.privateGetOrders (this.extend (request, params)); + // + // [ + // { + // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", + // "side": "bid", + // "ord_type": "limit", + // "price": "17417000.0", + // "state": "done", + // "market": "KRW-BTC", + // "created_at": "2018-04-05T14:09:14+09:00", + // "volume": "1.0", + // "remaining_volume": "0.0", + // "reserved_fee": "26125.5", + // "remaining_fee": "25974.0", + // "paid_fee": "151.5", + // "locked": "17341974.0", + // "executed_volume": "1.0", + // "trades_count":2 + // }, + // ] + // + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('wait', symbol, since, limit, params); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('done', symbol, since, limit, params); + } + + async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + return await this.fetchOrdersByState ('cancel', symbol, since, limit, params); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'uuid': id, + }; + const response = await this.privateGetOrder (this.extend (request, params)); + // + // { + // "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee", + // "side": "bid", + // "ord_type": "limit", + // "price": "17417000.0", + // "state": "done", + // "market": "KRW-BTC", + // "created_at": "2018-04-05T14:09:14+09:00", + // "volume": "1.0", + // "remaining_volume": "0.0", + // "reserved_fee": "26125.5", + // "remaining_fee": "25974.0", + // "paid_fee": "151.5", + // "locked": "17341974.0", + // "executed_volume": "1.0", + // "trades_count": 2, + // "trades": [ + // { + // "market": "KRW-BTC", + // "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3", + // "price": "101000.0", + // "volume": "0.77368323", + // "funds": "78142.00623", + // "ask_fee": "117.213009345", + // "bid_fee": "117.213009345", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid" + // }, + // { + // "market": "KRW-BTC", + // "uuid": "f73da467-c42f-407d-92fa-e10d86450a20", + // "price": "101000.0", + // "volume": "0.22631677", + // "funds": "22857.99377", + // "ask_fee": "34.286990655", + // "bid_fee": "34.286990655", + // "created_at": "2018-04-05T14:09:15+09:00", + // "side": "bid" + // } + // ] + // } + // + return this.parseOrder (response); + } + + parseDepositAddresses (addresses) { + const result = []; + for (let i = 0; i < addresses.length; i++) { + result.push (this.parseDepositAddress (addresses[i])); + } + return result; + } + + async fetchDepositAddresses (codes = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.privateGetDepositsCoinAddresses (params); + // + // [ + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // }, + // { + // "currency": "ETH", + // "deposit_address": "0x0d73e0a482b8cf568976d2e8688f4a899d29301c", + // "secondary_address": null + // }, + // { + // "currency": "XRP", + // "deposit_address": "rN9qNpgnBaZwqCg8CvUZRPqCcPPY7wfWep", + // "secondary_address": "3057887915" + // } + // ] + // + return this.parseDepositAddresses (response); + } + + parseDepositAddress (depositAddress, currency = undefined) { + // + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // } + // + const address = this.safeString (depositAddress, 'deposit_address'); + const tag = this.safeString (depositAddress, 'secondary_address'); + const currencyId = this.safeString (depositAddress, 'currency'); + const code = this.safeCurrencyCode (currencyId); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': depositAddress, + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const response = await this.privateGetDepositsCoinAddress (this.extend ({ + 'currency': currency['id'], + }, params)); + // + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // } + // + return this.parseDepositAddress (response); + } + + async createDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + // + // https://docs.upbit.com/v1.0/reference#%EC%9E%85%EA%B8%88-%EC%A3%BC%EC%86%8C-%EC%83%9D%EC%84%B1-%EC%9A%94%EC%B2%AD + // can be any of the two responses: + // + // { + // "success" : true, + // "message" : "Creating BTC deposit address." + // } + // + // { + // "currency": "BTC", + // "deposit_address": "3EusRwybuZUhVDeHL7gh3HSLmbhLcy7NqD", + // "secondary_address": null + // } + // + const message = this.safeString (response, 'message'); + if (message !== undefined) { + return { + 'currency': code, + 'address': undefined, + 'tag': undefined, + 'info': response, + }; + } + return this.parseDepositAddress (response); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'amount': amount, + }; + let method = 'privatePostWithdraws'; + if (code !== 'KRW') { + method += 'Coin'; + request['currency'] = currency['id']; + request['address'] = address; + if (tag !== undefined) { + request['secondary_address'] = tag; + } + } else { + method += 'Krw'; + } + const response = await this[method] (this.extend (request, params)); + // + // { + // "type": "withdraw", + // "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79", + // "currency": "BTC", + // "txid": "ebe6937b-130e-4066-8ac6-4b0e67f28adc", + // "state": "processing", + // "created_at": "2018-04-13T11:24:01+09:00", + // "done_at": null, + // "amount": "0.01", + // "fee": "0.0", + // "krw_amount": "80420.0" + // } + // + return this.parseTransaction (response); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.implodeParams (this.urls['api'], { + 'hostname': this.hostname, + }); + url += '/' + this.version + '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (method !== 'POST') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + const request = { + 'access_key': this.apiKey, + 'nonce': nonce, + }; + if (Object.keys (query).length) { + const auth = this.urlencode (query); + const hash = this.hash (this.encode (auth), 'sha512'); + request['query_hash'] = hash; + request['query_hash_alg'] = 'SHA512'; + } + const jwt = this.jwt (request, this.encode (this.secret)); + headers = { + 'Authorization': 'Bearer ' + jwt, + }; + if ((method !== 'GET') && (method !== 'DELETE')) { + body = this.json (params); + headers['Content-Type'] = 'application/json'; + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + // + // { 'error': { 'message': "Missing request parameter error. Check the required parameters!", 'name': 400 } }, + // { 'error': { 'message': "side is missing, side does not have a valid value", 'name': "validation_error" } }, + // { 'error': { 'message': "ê°œì�¸ì •ë³´ ì œ 3ìž� 제공 ë�™ì�˜ê°€ 필요합니다.", 'name': "thirdparty_agreement_required" } }, + // { 'error': { 'message': "권한ì�´ 부족합니다.", 'name': "out_of_scope" } }, + // { 'error': { 'message': "주문ì�„ 찾지 못했습니다.", 'name': "order_not_found" } }, + // { 'error': { 'message': "주문가능한 금액(ETH)ì�´ 부족합니다.", 'name': "insufficient_funds_ask" } }, + // { 'error': { 'message': "주문가능한 금액(BTC)ì�´ 부족합니다.", 'name': "insufficient_funds_bid" } }, + // { 'error': { 'message': "잘못ë�œ 엑세스 키입니다.", 'name': "invalid_access_key" } }, + // { 'error': { 'message': "Jwt 토í�° ê²€ì¦�ì—� 실패했습니다.", 'name': "jwt_verification" } } + // + const error = this.safeValue (response, 'error'); + if (error !== undefined) { + const message = this.safeString (error, 'message'); + const name = this.safeString (error, 'name'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], name, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], name, feedback); + throw new ExchangeError (feedback); // unknown message + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/vaultoro.js b/TaskServer/node_modules/ccxt/js/vaultoro.js new file mode 100644 index 000000000..4d57970dd --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/vaultoro.js @@ -0,0 +1,200 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); + +// --------------------------------------------------------------------------- + +module.exports = class vaultoro extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'vaultoro', + 'name': 'Vaultoro', + 'countries': [ 'CH' ], + 'rateLimit': 1000, + 'version': '1', + 'has': { + 'CORS': true, + 'fetchMarkets': true, + 'fetchOrderBook': true, + 'fetchBalance': true, + 'createOrder': true, + 'cancelOrder': true, + 'fetchTrades': true, + 'fetchTicker': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766880-f205e870-5ee9-11e7-8fe2-0d5b15880752.jpg', + 'api': 'https://api.vaultoro.com', + 'www': 'https://www.vaultoro.com', + 'doc': 'https://api.vaultoro.com', + }, + 'commonCurrencies': { + 'GLD': 'Gold', + }, + 'api': { + 'public': { + 'get': [ + 'bidandask', + 'buyorders', + 'latest', + 'latesttrades', + 'markets', + 'orderbook', + 'sellorders', + 'transactions/day', + 'transactions/hour', + 'transactions/month', + ], + }, + 'private': { + 'get': [ + 'balance', + 'mytrades', + 'orders', + ], + 'post': [ + 'buy/{symbol}/{type}', + 'cancel/{id}', + 'sell/{symbol}/{type}', + 'withdraw', + ], + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const result = []; + const response = await this.publicGetMarkets (params); + const market = this.safeValue (response, 'data'); + const baseId = this.safeString (market, 'MarketCurrency'); + const quoteId = this.safeString (market, 'BaseCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const id = this.safeString (market, 'MarketName'); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetBalance (params); + const balances = this.safeValue (response, 'data'); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'currency_code'); + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (balance, 'cash'); + account['used'] = this.safeFloat (balance, 'reserved'); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetOrderbook (params); + const orderbook = { + 'bids': response['data'][0]['b'], + 'asks': response['data'][1]['s'], + }; + return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'Gold_Price', 'Gold_Amount'); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.parse8601 (this.safeString (trade, 'Time')); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (trade, 'Gold_Price'); + const amount = this.safeFloat (trade, 'Gold_Amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + return { + 'id': undefined, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'order': undefined, + 'type': undefined, + 'side': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const response = await this.publicGetTransactionsDay (params); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const method = 'privatePost' + this.capitalize (side) + 'SymbolType'; + const request = { + 'symbol': market['quoteId'].toLowerCase (), + 'type': type, + 'gld': amount, + 'price': price || 1, + }; + const response = await this[method] (this.extend (request, params)); + return { + 'info': response, + 'id': response['data']['Order_ID'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id, + }; + return await this.privatePostCancelId (this.extend (request, params)); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/'; + if (api === 'public') { + url += path; + } else { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + url += this.version + '/' + this.implodeParams (path, params); + const query = this.extend ({ + 'nonce': nonce, + 'apikey': this.apiKey, + }, this.omit (params, this.extractParams (path))); + url += '?' + this.urlencode (query); + headers = { + 'Content-Type': 'application/json', + 'X-Signature': this.hmac (this.encode (url), this.encode (this.secret)), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/vbtc.js b/TaskServer/node_modules/ccxt/js/vbtc.js new file mode 100644 index 000000000..d547b0458 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/vbtc.js @@ -0,0 +1,32 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const foxbit = require ('./foxbit.js'); + +// --------------------------------------------------------------------------- + +module.exports = class vbtc extends foxbit { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'vbtc', + 'name': 'VBTC', + 'countries': [ 'VN' ], + 'has': { + 'CORS': false, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27991481-1f53d1d8-6481-11e7-884e-21d17e7939db.jpg', + 'api': { + 'public': 'https://api.blinktrade.com/api', + 'private': 'https://api.blinktrade.com/tapi', + }, + 'www': 'https://vbtc.exchange', + 'doc': 'https://blinktrade.com/docs', + }, + 'options': { + 'brokerId': '3', // https://blinktrade.com/docs/#brokers + }, + }); + } +}; diff --git a/TaskServer/node_modules/ccxt/js/whitebit.js b/TaskServer/node_modules/ccxt/js/whitebit.js new file mode 100644 index 000000000..8f8c435d6 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/whitebit.js @@ -0,0 +1,629 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, DDoSProtection, BadSymbol } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class whitebit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'whitebit', + 'name': 'WhiteBit', + 'version': 'v2', + 'countries': [ 'EE' ], + 'rateLimit': 500, + 'has': { + 'cancelOrder': false, + 'CORS': false, + 'createDepositAddress': false, + 'createLimitOrder': false, + 'createMarketOrder': false, + 'createOrder': false, + 'deposit': false, + 'editOrder': false, + 'fetchBalance': false, + 'fetchBidsAsks': false, + 'fetchCurrencies': true, + 'fetchMarkets': true, + 'fetchOHLCV': true, + 'fetchOrderBook': true, + 'fetchStatus': true, + 'fetchTicker': true, + 'fetchTickers': true, + 'fetchTrades': true, + 'privateAPI': false, + 'publicAPI': true, + }, + 'timeframes': { + '1m': '1m', + '3m': '3m', + '5m': '5m', + '15m': '15m', + '30m': '30m', + '1h': '1h', + '2h': '2h', + '4h': '4h', + '6h': '6h', + '8h': '8h', + '12h': '12h', + '1d': '1d', + '3d': '3d', + '1w': '1w', + '1M': '1M', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/66732963-8eb7dd00-ee66-11e9-849b-10d9282bb9e0.jpg', + 'api': { + 'web': 'https://whitebit.com/', + 'publicV2': 'https://whitebit.com/api/v2/public', + 'publicV1': 'https://whitebit.com/api/v1/public', + }, + 'www': 'https://www.whitebit.com', + 'doc': 'https://documenter.getpostman.com/view/7473075/SVSPomwS?version=latest#intro', + 'fees': 'https://whitebit.com/fee-schedule', + 'referral': 'https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963', + }, + 'api': { + 'web': { + 'get': [ + 'v1/healthcheck', + ], + }, + 'publicV1': { + 'get': [ + 'markets', + 'tickers', + 'ticker', + 'symbols', + 'depth/result', + 'history', + 'kline', + ], + }, + 'publicV2': { + 'get': [ + 'markets', + 'ticker', + 'assets', + 'fee', + 'depth/{market}', + 'trades/{market}', + ], + }, + }, + 'fees': { + 'trading': { + 'tierBased': false, + 'percentage': true, + 'taker': 0.001, + 'maker': 0.001, + }, + }, + 'options': { + 'fetchTradesMethod': 'fetchTradesV1', + }, + 'exceptions': { + 'exact': { + }, + 'broad': { + 'Market is not available': BadSymbol, // {"success":false,"message":{"market":["Market is not available"]},"result":[]} + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.publicV2GetMarkets (params); + // + // { + // "success":true, + // "message":"", + // "result":[ + // { + // "name":"BTC_USD", + // "moneyPrec":"2", + // "stock":"BTC", + // "money":"USD", + // "stockPrec":"6", + // "feePrec":"4", + // "minAmount":"0.001", + // "tradesEnabled":true, + // "minTotal":"0.001" + // } + // ] + // } + // + const markets = this.safeValue (response, 'result'); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'name'); + const baseId = this.safeString (market, 'stock'); + const quoteId = this.safeString (market, 'money'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const active = this.safeValue (market, 'tradesEnabled'); + const entry = { + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + 'active': active, + 'precision': { + 'amount': this.safeInteger (market, 'stockPrec'), + 'price': this.safeInteger (market, 'moneyPrec'), + }, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'minAmount'), + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': this.safeFloat (market, 'minTotal'), + 'max': undefined, + }, + }, + }; + result.push (entry); + } + return result; + } + + async fetchCurrencies (params = {}) { + const response = await this.publicV2GetAssets (params); + // + // { + // "success":true, + // "message":"", + // "result":{ + // "BTC":{ + // "id":"4f37bc79-f612-4a63-9a81-d37f7f9ff622", + // "lastUpdateTimestamp":"2019-10-12T04:40:05.000Z", + // "name":"Bitcoin", + // "canWithdraw":true, + // "canDeposit":true, + // "minWithdrawal":"0.001", + // "maxWithdrawal":"0", + // "makerFee":"0.1", + // "takerFee":"0.1" + // } + // } + // } + // + const currencies = this.safeValue (response, 'result'); + const ids = Object.keys (currencies); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const currency = currencies[id]; + // breaks down in Python due to utf8 encoding issues on the exchange side + // const name = this.safeString (currency, 'name'); + const canDeposit = this.safeValue (currency, 'canDeposit', true); + const canWithdraw = this.safeValue (currency, 'canWithdraw', true); + const active = canDeposit && canWithdraw; + const code = this.safeCurrencyCode (id); + result[code] = { + 'id': id, + 'code': code, + 'info': currency, // the original payload + 'name': undefined, // see the comment above + 'active': active, + 'fee': undefined, + 'precision': undefined, + 'limits': { + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'min': undefined, + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + 'withdraw': { + 'min': this.safeFloat (currency, 'minWithdrawal'), + 'max': this.safeFloat (currency, 'maxWithdrawal'), + }, + }, + }; + } + return result; + } + + async fetchTradingFees (params = {}) { + const response = await this.publicV2GetFee (params); + const fees = this.safeValue (response, 'result'); + return { + 'maker': this.safeFloat (fees, 'makerFee'), + 'taker': this.safeFloat (fees, 'takerFee'), + }; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + const response = await this.publicV1GetTicker (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result": { + // "bid":"0.021979", + // "ask":"0.021996", + // "open":"0.02182", + // "high":"0.022039", + // "low":"0.02161", + // "last":"0.021987", + // "volume":"2810.267", + // "deal":"61.383565474", + // "change":"0.76", + // }, + // } + // + const ticker = this.safeValue (response, 'result', {}); + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + // + // fetchTicker + // + // { + // "bid":"0.021979", + // "ask":"0.021996", + // "open":"0.02182", + // "high":"0.022039", + // "low":"0.02161", + // "last":"0.021987", + // "volume":"2810.267", + // "deal":"61.383565474", + // "change":"0.76", + // } + // + // fetchTickers v1 + // + // { + // "at":1571022144, + // "ticker": { + // "bid":"0.022024", + // "ask":"0.022042", + // "low":"0.02161", + // "high":"0.022062", + // "last":"0.022036", + // "vol":"2813.503", + // "deal":"61.457279261", + // "change":"0.95" + // } + // } + // + const timestamp = this.safeTimestamp (ticker, 'at', this.milliseconds ()); + ticker = this.safeValue (ticker, 'ticker', ticker); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + const percentage = this.safeFloat (ticker, 'change'); + let change = undefined; + if (percentage !== undefined) { + change = this.numberToString (percentage * 0.01); + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': this.safeFloat (ticker, 'open'), + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': change, + 'percentage': percentage, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'volume'), + 'quoteVolume': this.safeFloat (ticker, 'deal'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicV1GetTickers (params); + // + // { + // "success":true, + // "message":"", + // "result": { + // "ETH_BTC": { + // "at":1571022144, + // "ticker": { + // "bid":"0.022024", + // "ask":"0.022042", + // "low":"0.02161", + // "high":"0.022062", + // "last":"0.022036", + // "vol":"2813.503", + // "deal":"61.457279261", + // "change":"0.95" + // } + // }, + // }, + // } + // + const data = this.safeValue (response, 'result'); + const marketIds = Object.keys (data); + const result = {}; + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + let market = undefined; + let symbol = marketId; + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + symbol = market['symbol']; + } else { + const [ baseId, quoteId ] = marketId.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = this.parseTicker (data[marketId], market); + result[symbol] = this.extend (ticker, { 'symbol': symbol }); + } + return this.filterByArray (result, 'symbol', symbols); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 50, maximum = 100 + } + const response = await this.publicV2GetDepthMarket (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result":{ + // "lastUpdateTimestamp":"2019-10-14T03:15:47.000Z", + // "asks":[ + // ["0.02204","2.03"], + // ["0.022041","2.492"], + // ["0.022042","2.254"], + // ], + // "bids":[ + // ["0.022018","2.327"], + // ["0.022017","1.336"], + // ["0.022015","2.089"], + // ], + // } + // } + // + const result = this.safeValue (response, 'result', {}); + const timestamp = this.parse8601 (this.safeString (result, 'lastUpdateTimestamp')); + return this.parseOrderBook (result, timestamp); + } + + async fetchTradesV1 (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'lastId': 1, // todo add since + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 50, maximum = 10000 + } + const response = await this.publicV1GetHistory (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result":[ + // { + // "id":11887426, + // "type":"buy", + // "time":1571023057.413769, + // "amount":"0.171", + // "price":"0.022052" + // } + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchTradesV2 (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 50, maximum = 10000 + } + const response = await this.publicV2GetTradesMarket (this.extend (request, params)); + // + // { + // "success":true, + // "message":"", + // "result": [ + // { + // "tradeId":11903347, + // "price":"0.022044", + // "volume":"0.029", + // "time":"2019-10-14T06:30:57.000Z", + // "isBuyerMaker":false + // }, + // ], + // } + // + const result = this.safeValue (response, 'result', []); + return this.parseTrades (result, market, since, limit); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + const method = this.safeString (this.options, 'fetchTradesMethod', 'fetchTradesV2'); + return await this[method] (symbol, since, limit, params); + } + + parseTrade (trade, market = undefined) { + // + // fetchTradesV1 + // + // { + // "id":11887426, + // "type":"buy", + // "time":1571023057.413769, + // "amount":"0.171", + // "price":"0.022052" + // } + // + // fetchTradesV2 + // + // { + // "tradeId":11903347, + // "price":"0.022044", + // "volume":"0.029", + // "time":"2019-10-14T06:30:57.000Z", + // "isBuyerMaker":false + // } + // + let timestamp = this.safeValue (trade, 'time'); + if (typeof timestamp === 'string') { + timestamp = this.parse8601 (timestamp); + } else { + timestamp = parseInt (timestamp * 1000); + } + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat2 (trade, 'amount', 'volume'); + const id = this.safeString2 (trade, 'id', 'tradeId'); + let side = this.safeString (trade, 'type'); + if (side === undefined) { + const isBuyerMaker = this.safeValue (trade, 'isBuyerMaker'); + side = isBuyerMaker ? 'buy' : 'sell'; + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + let cost = undefined; + if (amount !== undefined && price !== undefined) { + cost = amount * price; + } + return { + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'id': id, + 'order': undefined, + 'type': undefined, + 'takerOrMaker': undefined, + 'side': side, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'market': market['id'], + 'interval': this.timeframes[timeframe], + }; + if (since !== undefined) { + request['start'] = parseInt (since / 1000); + } + if (limit !== undefined) { + request['limit'] = limit; // default == max == 500 + } + const response = await this.publicV1GetKline (this.extend (request, params)); + const result = this.safeValue (response, 'result'); + return this.parseOHLCVs (result, market, timeframe, since, limit); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv[0] * 1000, // timestamp + parseFloat (ohlcv[1]), // open + parseFloat (ohlcv[3]), // high + parseFloat (ohlcv[4]), // low + parseFloat (ohlcv[2]), // close + parseFloat (ohlcv[5]), // volume + ]; + } + + async fetchStatus (params = {}) { + const response = await this.webGetV1Healthcheck (); + const status = this.safeInteger (response, 'status'); + let formattedStatus = 'ok'; + if (status === 503) { + formattedStatus = 'maintenance'; + } + this.status = this.extend (this.status, { + 'status': formattedStatus, + 'updated': this.milliseconds (), + }); + return this.status; + } + + sign (path, api = 'publicV1', method = 'GET', params = {}, headers = undefined, body = undefined) { + const query = this.omit (params, this.extractParams (path)); + let url = this.urls['api'][api] + '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if ((code === 418) || (code === 429)) { + throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body); + } + if (code === 404) { + throw new ExchangeError (this.id + ' ' + code.toString () + ' endpoint not found'); + } + if (response !== undefined) { + const success = this.safeValue (response, 'success'); + if (!success) { + const feedback = this.id + ' ' + body; + const message = this.safeValue (response, 'message'); + if (typeof message === 'string') { + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + } + this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback); + throw new ExchangeError (feedback); + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/xbtce.js b/TaskServer/node_modules/ccxt/js/xbtce.js new file mode 100644 index 000000000..250a26592 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/xbtce.js @@ -0,0 +1,360 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, NotSupported, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class xbtce extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'xbtce', + 'name': 'xBTCe', + 'countries': [ 'RU' ], + 'rateLimit': 2000, // responses are cached every 2 seconds + 'version': 'v1', + 'has': { + 'CORS': false, + 'fetchTickers': true, + 'createMarketOrder': false, + 'fetchOHLCV': false, + }, + 'urls': { + 'referral': 'https://xbtce.com/?agent=XX97BTCXXXG687021000B', + 'logo': 'https://user-images.githubusercontent.com/1294454/28059414-e235970c-662c-11e7-8c3a-08e31f78684b.jpg', + 'api': 'https://cryptottlivewebapi.xbtce.net:8443/api', + 'www': 'https://www.xbtce.com', + 'doc': [ + 'https://www.xbtce.com/tradeapi', + 'https://support.xbtce.info/Knowledgebase/Article/View/52/25/xbtce-exchange-api', + ], + }, + 'requiredCredentials': { + 'apiKey': true, + 'secret': true, + 'uid': true, + }, + 'api': { + 'public': { + 'get': [ + 'currency', + 'currency/{filter}', + 'level2', + 'level2/{filter}', + 'quotehistory/{symbol}/{periodicity}/bars/ask', + 'quotehistory/{symbol}/{periodicity}/bars/bid', + 'quotehistory/{symbol}/level2', + 'quotehistory/{symbol}/ticks', + 'symbol', + 'symbol/{filter}', + 'tick', + 'tick/{filter}', + 'ticker', + 'ticker/{filter}', + 'tradesession', + ], + }, + 'private': { + 'get': [ + 'tradeserverinfo', + 'tradesession', + 'currency', + 'currency/{filter}', + 'level2', + 'level2/{filter}', + 'symbol', + 'symbol/{filter}', + 'tick', + 'tick/{filter}', + 'account', + 'asset', + 'asset/{id}', + 'position', + 'position/{id}', + 'trade', + 'trade/{id}', + 'quotehistory/{symbol}/{periodicity}/bars/ask', + 'quotehistory/{symbol}/{periodicity}/bars/ask/info', + 'quotehistory/{symbol}/{periodicity}/bars/bid', + 'quotehistory/{symbol}/{periodicity}/bars/bid/info', + 'quotehistory/{symbol}/level2', + 'quotehistory/{symbol}/level2/info', + 'quotehistory/{symbol}/periodicities', + 'quotehistory/{symbol}/ticks', + 'quotehistory/{symbol}/ticks/info', + 'quotehistory/cache/{symbol}/{periodicity}/bars/ask', + 'quotehistory/cache/{symbol}/{periodicity}/bars/bid', + 'quotehistory/cache/{symbol}/level2', + 'quotehistory/cache/{symbol}/ticks', + 'quotehistory/symbols', + 'quotehistory/version', + ], + 'post': [ + 'trade', + 'tradehistory', + ], + 'put': [ + 'trade', + ], + 'delete': [ + 'trade', + ], + }, + }, + 'commonCurrencies': { + 'DSH': 'DASH', + }, + }); + } + + async fetchMarkets (params = {}) { + const response = await this.privateGetSymbol (params); + const result = []; + for (let i = 0; i < response.length; i++) { + const market = response[i]; + const id = this.safeString (market, 'Symbol'); + const baseId = this.safeString (market, 'MarginCurrency'); + const quoteId = this.safeString (market, 'ProfitCurrency'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + let symbol = base + '/' + quote; + symbol = market['IsTradeAllowed'] ? symbol : id; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const balances = await this.privateGetAsset (params); + const result = { 'info': balances }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + const currencyId = this.safeString (balance, 'Currency'); + const code = this.safeCurrencyCode (currencyId); + const account = { + 'free': this.safeFloat (balance, 'FreeAmount'), + 'used': this.safeFloat (balance, 'LockedAmount'), + 'total': this.safeFloat (balance, 'Amount'), + }; + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'filter': market['id'], + }; + const response = await this.privateGetLevel2Filter (this.extend (request, params)); + const orderbook = response[0]; + const timestamp = this.safeInteger (orderbook, 'Timestamp'); + return this.parseOrderBook (orderbook, timestamp, 'Bids', 'Asks', 'Price', 'Volume'); + } + + parseTicker (ticker, market = undefined) { + let timestamp = 0; + let last = undefined; + if ('LastBuyTimestamp' in ticker) { + if (timestamp < ticker['LastBuyTimestamp']) { + timestamp = ticker['LastBuyTimestamp']; + last = ticker['LastBuyPrice']; + } + } + if ('LastSellTimestamp' in ticker) { + if (timestamp < ticker['LastSellTimestamp']) { + timestamp = ticker['LastSellTimestamp']; + last = ticker['LastSellPrice']; + } + } + if (!timestamp) { + timestamp = this.milliseconds (); + } + let symbol = undefined; + if (market) { + symbol = market['symbol']; + } + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': ticker['DailyBestBuyPrice'], + 'low': ticker['DailyBestSellPrice'], + 'bid': ticker['BestBid'], + 'bidVolume': undefined, + 'ask': ticker['BestAsk'], + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': ticker['DailyTradedTotalVolume'], + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetTicker (params); + const tickers = this.indexBy (response, 'Symbol'); + const ids = Object.keys (tickers); + const result = {}; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let market = undefined; + let symbol = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } else { + const baseId = id.slice (0, 3); + const quoteId = id.slice (3, 6); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + symbol = base + '/' + quote; + } + const ticker = tickers[id]; + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'filter': market['id'], + }; + const response = await this.publicGetTickerFilter (this.extend (request, params)); + const length = response.length; + if (length < 1) { + throw new ExchangeError (this.id + ' fetchTicker returned empty response, xBTCe public API error'); + } + const tickers = this.indexBy (response, 'Symbol'); + const ticker = tickers[market['id']]; + return this.parseTicker (ticker, market); + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + // no method for trades? + return await this.privateGetTrade (params); + } + + parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) { + return [ + ohlcv['Timestamp'], + ohlcv['Open'], + ohlcv['High'], + ohlcv['Low'], + ohlcv['Close'], + ohlcv['Volume'], + ]; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + // let minutes = parseInt (timeframe / 60); // 1 minute by default + // let periodicity = minutes.toString (); + // await this.loadMarkets (); + // let market = this.market (symbol); + // if (since === undefined) + // since = this.seconds () - 86400 * 7; // last day by defulat + // if (limit === undefined) + // limit = 1000; // default + // let response = await this.privateGetQuotehistorySymbolPeriodicityBarsBid (this.extend ({ + // 'symbol': market['id'], + // 'periodicity': periodicity, + // 'timestamp': since, + // 'count': limit, + // }, params)); + // return this.parseOHLCVs (response['Bars'], market, timeframe, since, limit); + throw new NotSupported (this.id + ' fetchOHLCV is disabled by the exchange'); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'pair': this.marketId (symbol), + 'type': side, + 'amount': amount, + 'rate': price, + }; + const response = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': response, + 'id': response['Id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'Type': 'Cancel', + 'Id': id, + }; + return await this.privateDeleteTrade (this.extend (request, params)); + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + if (!this.apiKey) { + throw new AuthenticationError (this.id + ' requires apiKey for all requests, their public API is always busy'); + } + if (!this.uid) { + throw new AuthenticationError (this.id + ' requires uid property for authentication and trading, their public API is always busy'); + } + let url = this.urls['api'] + '/' + this.version; + if (api === 'public') { + url += '/' + api; + } + url += '/' + this.implodeParams (path, params); + const query = this.omit (params, this.extractParams (path)); + if (api === 'public') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + this.checkRequiredCredentials (); + headers = { 'Accept-Encoding': 'gzip, deflate' }; + const nonce = this.nonce ().toString (); + if (method === 'POST') { + if (Object.keys (query).length) { + headers['Content-Type'] = 'application/json'; + body = this.json (query); + } else { + url += '?' + this.urlencode (query); + } + } + let auth = nonce + this.uid + this.apiKey + method + url; + if (body) { + auth += body; + } + const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64'); + const credentials = this.uid + ':' + this.apiKey + ':' + nonce + ':' + this.decode (signature); + headers['Authorization'] = 'HMAC ' + credentials; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } +}; diff --git a/TaskServer/node_modules/ccxt/js/yobit.js b/TaskServer/node_modules/ccxt/js/yobit.js new file mode 100644 index 000000000..95ed4f6c6 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/yobit.js @@ -0,0 +1,926 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InvalidNonce, InsufficientFunds, OrderNotFound, DDoSProtection, InvalidOrder, AuthenticationError } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class yobit extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'yobit', + 'name': 'YoBit', + 'countries': [ 'RU' ], + 'rateLimit': 3000, // responses are cached every 2 seconds + 'version': '3', + 'has': { + 'CORS': false, + 'createDepositAddress': true, + 'createMarketOrder': false, + 'fetchClosedOrders': 'emulated', + 'fetchDepositAddress': true, + 'fetchDeposits': false, + 'fetchMyTrades': true, + 'fetchOpenOrders': true, + 'fetchOrder': true, + 'fetchOrderBooks': true, + 'fetchOrders': 'emulated', + 'fetchTickers': true, + 'fetchTransactions': false, + 'fetchWithdrawals': false, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766910-cdcbfdae-5eea-11e7-9859-03fea873272d.jpg', + 'api': { + 'public': 'https://yobit.net/api', + 'private': 'https://yobit.net/tapi', + }, + 'www': 'https://www.yobit.net', + 'doc': 'https://www.yobit.net/en/api/', + 'fees': 'https://www.yobit.net/en/fees/', + }, + 'api': { + 'public': { + 'get': [ + 'depth/{pair}', + 'info', + 'ticker/{pair}', + 'trades/{pair}', + ], + }, + 'private': { + 'post': [ + 'ActiveOrders', + 'CancelOrder', + 'GetDepositAddress', + 'getInfo', + 'OrderInfo', + 'Trade', + 'TradeHistory', + 'WithdrawCoinsToAddress', + ], + }, + }, + 'fees': { + 'trading': { + 'maker': 0.002, + 'taker': 0.002, + }, + 'funding': { + 'withdraw': {}, + }, + }, + 'commonCurrencies': { + 'AIR': 'AirCoin', + 'ANI': 'ANICoin', + 'ANT': 'AntsCoin', // what is this, a coin for ants? + 'ATMCHA': 'ATM', + 'ASN': 'Ascension', + 'AST': 'Astral', + 'ATM': 'Autumncoin', + 'BCC': 'BCH', + 'BCS': 'BitcoinStake', + 'BLN': 'Bulleon', + 'BOT': 'BOTcoin', + 'BON': 'BONES', + 'BPC': 'BitcoinPremium', + 'BTS': 'Bitshares2', + 'CAT': 'BitClave', + 'CMT': 'CometCoin', + 'COV': 'Coven Coin', + 'COVX': 'COV', + 'CPC': 'Capricoin', + 'CS': 'CryptoSpots', + 'DCT': 'Discount', + 'DFT': 'DraftCoin', + 'DGD': 'DarkGoldCoin', + 'DIRT': 'DIRTY', + 'DROP': 'FaucetCoin', + 'DSH': 'DASH', + 'EKO': 'EkoCoin', + 'ENTER': 'ENTRC', + 'EPC': 'ExperienceCoin', + 'ESC': 'EdwardSnowden', + 'EUROPE': 'EUROP', + 'EXT': 'LifeExtension', + 'FUNK': 'FUNKCoin', + 'GCC': 'GlobalCryptocurrency', + 'GEN': 'Genstake', + 'GENE': 'Genesiscoin', + 'GOLD': 'GoldMint', + 'GOT': 'Giotto Coin', + 'HTML5': 'HTML', + 'HYPERX': 'HYPER', + 'ICN': 'iCoin', + 'INSANE': 'INSN', + 'JNT': 'JointCoin', + 'JPC': 'JupiterCoin', + 'KNC': 'KingN Coin', + 'LBTCX': 'LiteBitcoin', + 'LIZI': 'LiZi', + 'LOC': 'LocoCoin', + 'LOCX': 'LOC', + 'LUNYR': 'LUN', + 'LUN': 'LunarCoin', // they just change the ticker if it is already taken + 'MDT': 'Midnight', + 'NAV': 'NavajoCoin', + 'NBT': 'NiceBytes', + 'OMG': 'OMGame', + 'PAC': '$PAC', + 'PLAY': 'PlayCoin', + 'PIVX': 'Darknet', + 'PRS': 'PRE', + 'PUTIN': 'PUT', + 'STK': 'StakeCoin', + 'SUB': 'Subscriptio', + 'PAY': 'EPAY', + 'PLC': 'Platin Coin', + 'RCN': 'RCoin', + 'REP': 'Republicoin', + 'RUR': 'RUB', + 'TTC': 'TittieCoin', + 'XIN': 'XINCoin', + }, + 'options': { + // 'fetchTickersMaxLength': 2048, + 'fetchOrdersRequiresSymbol': true, + 'fetchTickersMaxLength': 512, + }, + 'exceptions': { + 'exact': { + '803': InvalidOrder, // "Count could not be less than 0.001." (selling below minAmount) + '804': InvalidOrder, // "Count could not be more than 10000." (buying above maxAmount) + '805': InvalidOrder, // "price could not be less than X." (minPrice violation on buy & sell) + '806': InvalidOrder, // "price could not be more than X." (maxPrice violation on buy & sell) + '807': InvalidOrder, // "cost could not be less than X." (minCost violation on buy & sell) + '831': InsufficientFunds, // "Not enougth X to create buy order." (buying with balance.quote < order.cost) + '832': InsufficientFunds, // "Not enougth X to create sell order." (selling with balance.base < order.amount) + '833': OrderNotFound, // "Order with id X was not found." (cancelling non-existent, closed and cancelled order) + }, + 'broad': { + 'Invalid pair name': ExchangeError, // {"success":0,"error":"Invalid pair name: btc_eth"} + 'invalid api key': AuthenticationError, + 'invalid sign': AuthenticationError, + 'api key dont have trade permission': AuthenticationError, + 'invalid parameter': InvalidOrder, + 'invalid order': InvalidOrder, + 'Requests too often': DDoSProtection, + 'not available': ExchangeNotAvailable, + 'data unavailable': ExchangeNotAvailable, + 'external service unavailable': ExchangeNotAvailable, + 'Total transaction amount': ExchangeError, // { "success": 0, "error": "Total transaction amount is less than minimal total: 0.00010000"} + 'Insufficient funds': InsufficientFunds, + 'invalid key': AuthenticationError, + 'invalid nonce': InvalidNonce, // {"success":0,"error":"invalid nonce (has already been used)"}' + }, + }, + }); + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfo (params); + // + // { + // "success":1, + // "return":{ + // "funds":{ + // "ltc":22, + // "nvc":423.998, + // "ppc":10, + // }, + // "funds_incl_orders":{ + // "ltc":32, + // "nvc":523.998, + // "ppc":20, + // }, + // "rights":{ + // "info":1, + // "trade":0, + // "withdraw":0 + // }, + // "transaction_count":0, + // "open_orders":1, + // "server_time":1418654530 + // } + // } + // + const balances = this.safeValue (response, 'return', {}); + const result = { 'info': response }; + const free = this.safeValue (balances, 'funds', {}); + const total = this.safeValue (balances, 'funds_incl_orders', {}); + const currencyIds = Object.keys (this.extend (free, total)); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const account = this.account (); + account['free'] = this.safeFloat (free, currencyId); + account['total'] = this.safeFloat (total, currencyId); + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchMarkets (params = {}) { + const response = await this.publicGetInfo (params); + const markets = this.safeValue (response, 'pairs'); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ baseId, quoteId ] = id.split ('_'); + let base = baseId.toUpperCase (); + let quote = quoteId.toUpperCase (); + base = this.safeCurrencyCode (base); + quote = this.safeCurrencyCode (quote); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'decimal_places'), + 'price': this.safeInteger (market, 'decimal_places'), + }; + const amountLimits = { + 'min': this.safeFloat (market, 'min_amount'), + 'max': this.safeFloat (market, 'max_amount'), + }; + const priceLimits = { + 'min': this.safeFloat (market, 'min_price'), + 'max': this.safeFloat (market, 'max_price'), + }; + const costLimits = { + 'min': this.safeFloat (market, 'min_total'), + }; + const limits = { + 'amount': amountLimits, + 'price': priceLimits, + 'cost': costLimits, + }; + const hidden = this.safeInteger (market, 'hidden'); + const active = (hidden === 0); + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': active, + 'taker': market['fee'] / 100, + 'precision': precision, + 'limits': limits, + 'info': market, + }); + } + return result; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; // default = 150, max = 2000 + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const market_id_in_reponse = (market['id'] in response); + if (!market_id_in_reponse) { + throw new ExchangeError (this.id + ' ' + market['symbol'] + ' order book is empty or not available'); + } + const orderbook = response[market['id']]; + return this.parseOrderBook (orderbook); + } + + async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let ids = undefined; + if (symbols === undefined) { + ids = this.ids.join ('-'); + // max URL length is 2083 symbols, including http schema, hostname, tld, etc... + if (ids.length > 2048) { + const numIds = this.ids.length; + throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchOrderBooks'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + // 'ignore_invalid': true, + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetDepthPair (this.extend (request, params)); + const result = {}; + ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + let symbol = id; + if (id in this.markets_by_id) { + const market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseOrderBook (response[id]); + } + return result; + } + + parseTicker (ticker, market = undefined) { + // + // { high: 0.03497582, + // low: 0.03248474, + // avg: 0.03373028, + // vol: 120.11485715062999, + // vol_cur: 3572.24914074, + // last: 0.0337611, + // buy: 0.0337442, + // sell: 0.03377798, + // updated: 1537522009 } + // + const timestamp = this.safeTimestamp (ticker, 'updated'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': this.safeFloat (ticker, 'avg'), + 'baseVolume': this.safeFloat (ticker, 'vol_cur'), + 'quoteVolume': this.safeFloat (ticker, 'vol'), + 'info': ticker, + }; + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + let ids = this.ids; + if (symbols === undefined) { + const numIds = ids.length; + ids = ids.join ('-'); + const maxLength = this.safeInteger (this.options, 'fetchTickersMaxLength', 2048); + // max URL length is 2048 symbols, including http schema, hostname, tld, etc... + if (ids.length > this.options['fetchTickersMaxLength']) { + throw new ArgumentsRequired (this.id + ' has ' + numIds.toString () + ' markets exceeding max URL length for this endpoint (' + maxLength.toString () + ' characters), please, specify a list of symbols of interest in the first argument to fetchTickers'); + } + } else { + ids = this.marketIds (symbols); + ids = ids.join ('-'); + } + const request = { + 'pair': ids, + }; + const tickers = await this.publicGetTickerPair (this.extend (request, params)); + const result = {}; + const keys = Object.keys (tickers); + for (let k = 0; k < keys.length; k++) { + const id = keys[k]; + const ticker = tickers[id]; + let symbol = id; + let market = undefined; + if (id in this.markets_by_id) { + market = this.markets_by_id[id]; + symbol = market['symbol']; + } + result[symbol] = this.parseTicker (ticker, market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + const tickers = await this.fetchTickers ([ symbol ], params); + return tickers[symbol]; + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'timestamp'); + let side = this.safeString (trade, 'type'); + if (side === 'ask') { + side = 'sell'; + } else if (side === 'bid') { + side = 'buy'; + } + const price = this.safeFloat2 (trade, 'rate', 'price'); + const id = this.safeString2 (trade, 'trade_id', 'tid'); + const order = this.safeString (trade, 'order_id'); + if ('pair' in trade) { + const marketId = this.safeString (trade, 'pair'); + market = this.safeValue (this.markets_by_id, marketId, market); + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const amount = this.safeFloat (trade, 'amount'); + const type = 'limit'; // all trades are still limit trades + let takerOrMaker = undefined; + let fee = undefined; + const feeCost = this.safeFloat (trade, 'commission'); + if (feeCost !== undefined) { + const feeCurrencyId = this.safeString (trade, 'commissionCurrency'); + const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); + fee = { + 'cost': feeCost, + 'currency': feeCurrencyCode, + }; + } + const isYourOrder = this.safeValue (trade, 'is_your_order'); + if (isYourOrder !== undefined) { + takerOrMaker = 'taker'; + if (isYourOrder) { + takerOrMaker = 'maker'; + } + if (fee === undefined) { + fee = this.calculateFee (symbol, type, side, amount, price, takerOrMaker); + } + } + let cost = undefined; + if (amount !== undefined) { + if (price !== undefined) { + cost = amount * price; + } + } + return { + 'id': id, + 'order': order, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': type, + 'side': side, + 'takerOrMaker': takerOrMaker, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': fee, + 'info': trade, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + if (limit !== undefined) { + request['limit'] = limit; + } + const response = await this.publicGetTradesPair (this.extend (request, params)); + if (Array.isArray (response)) { + const numElements = response.length; + if (numElements === 0) { + return []; + } + } + return this.parseTrades (response[market['id']], market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type === 'market') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + 'type': side, + 'amount': this.amountToPrecision (symbol, amount), + 'rate': this.priceToPrecision (symbol, price), + }; + price = parseFloat (price); + amount = parseFloat (amount); + const response = await this.privatePostTrade (this.extend (request, params)); + let id = undefined; + let status = 'open'; + let filled = 0.0; + let remaining = amount; + if ('return' in response) { + id = this.safeString (response['return'], 'order_id'); + if (id === '0') { + id = this.safeString (response['return'], 'init_order_id'); + status = 'closed'; + } + filled = this.safeFloat (response['return'], 'received', 0.0); + remaining = this.safeFloat (response['return'], 'remains', amount); + } + const timestamp = this.milliseconds (); + const order = { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': status, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'cost': price * filled, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'fee': undefined, + // 'trades': this.parseTrades (order['trades'], market), + }; + this.orders[id] = order; + return this.extend ({ 'info': response }, order); + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostCancelOrder (this.extend (request, params)); + if (id in this.orders) { + this.orders[id]['status'] = 'canceled'; + } + return response; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'closed', + '2': 'canceled', + '3': 'open', // or partially-filled and canceled? https://github.com/ccxt/ccxt/issues/1594 + }; + return this.safeString (statuses, status, status); + } + + parseOrder (order, market = undefined) { + const id = this.safeString (order, 'id'); + const status = this.parseOrderStatus (this.safeString (order, 'status')); + const timestamp = this.safeTimestamp (order, 'timestamp_created'); + let symbol = undefined; + if (market === undefined) { + const marketId = this.safeString (order, 'pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + if (market !== undefined) { + symbol = market['symbol']; + } + const remaining = this.safeFloat (order, 'amount'); + let amount = undefined; + const price = this.safeFloat (order, 'rate'); + let filled = undefined; + let cost = undefined; + if ('start_amount' in order) { + amount = this.safeFloat (order, 'start_amount'); + } else { + if (id in this.orders) { + amount = this.orders[id]['amount']; + } + } + if (amount !== undefined) { + if (remaining !== undefined) { + filled = amount - remaining; + cost = price * filled; + } + } + const fee = undefined; + const type = 'limit'; + const side = this.safeString (order, 'type'); + const result = { + 'info': order, + 'id': id, + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'type': type, + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'remaining': remaining, + 'filled': filled, + 'status': status, + 'fee': fee, + }; + return result; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'order_id': parseInt (id), + }; + const response = await this.privatePostOrderInfo (this.extend (request, params)); + id = id.toString (); + const newOrder = this.parseOrder (this.extend ({ 'id': id }, response['return'][id])); + const oldOrder = (id in this.orders) ? this.orders[id] : {}; + this.orders[id] = this.extend (oldOrder, newOrder); + return this.orders[id]; + } + + updateCachedOrders (openOrders, symbol) { + // update local cache with open orders + // this will add unseen orders and overwrite existing ones + for (let j = 0; j < openOrders.length; j++) { + const id = openOrders[j]['id']; + this.orders[id] = openOrders[j]; + } + const openOrdersIndexedById = this.indexBy (openOrders, 'id'); + const cachedOrderIds = Object.keys (this.orders); + for (let k = 0; k < cachedOrderIds.length; k++) { + // match each cached order to an order in the open orders array + // possible reasons why a cached order may be missing in the open orders array: + // - order was closed or canceled -> update cache + // - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip + const cachedOrderId = cachedOrderIds[k]; + let cachedOrder = this.orders[cachedOrderId]; + if (!(cachedOrderId in openOrdersIndexedById)) { + // cached order is not in open orders array + // if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order + if (symbol !== undefined && symbol !== cachedOrder['symbol']) { + continue; + } + // cached order is absent from the list of open orders -> mark the cached order as closed + if (cachedOrder['status'] === 'open') { + cachedOrder = this.extend (cachedOrder, { + 'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us") + 'cost': undefined, + 'filled': cachedOrder['amount'], + 'remaining': 0.0, + }); + if (cachedOrder['cost'] === undefined) { + if (cachedOrder['filled'] !== undefined) { + cachedOrder['cost'] = cachedOrder['filled'] * cachedOrder['price']; + } + } + this.orders[cachedOrderId] = cachedOrder; + } + } + } + return this.toArray (this.orders); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument'); + } + await this.loadMarkets (); + const request = {}; + const market = undefined; + if (symbol !== undefined) { + const market = this.market (symbol); + request['pair'] = market['id']; + } + const response = await this.privatePostActiveOrders (this.extend (request, params)); + // can only return 'open' orders (i.e. no way to fetch 'closed' orders) + let openOrders = []; + if ('return' in response) { + openOrders = this.parseOrders (response['return'], market); + } + const allOrders = this.updateCachedOrders (openOrders, symbol); + const result = this.filterBySymbol (allOrders, symbol); + return this.filterBySinceLimit (result, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'open'); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + const orders = await this.fetchOrders (symbol, since, limit, params); + return this.filterBy (orders, 'status', 'closed'); + } + + async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument'); + } + await this.loadMarkets (); + const market = this.market (symbol); + // some derived classes use camelcase notation for request fields + const request = { + // 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field) + // 'count': 1000, // the number of trades for display numerical, default = 1000 + // 'from_id': trade ID, from which the display starts numerical 0 + // 'end_id': trade ID on which the display ends numerical ∞ + // 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last) + // 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field) + // 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field) + 'pair': market['id'], + }; + if (limit !== undefined) { + request['count'] = parseInt (limit); + } + if (since !== undefined) { + request['since'] = parseInt (since / 1000); + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + const trades = this.safeValue (response, 'return', {}); + const ids = Object.keys (trades); + const result = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const trade = this.parseTrade (this.extend (trades[id], { + 'trade_id': id, + }), market); + result.push (trade); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async createDepositAddress (code, params = {}) { + const request = { + 'need_new': 1, + }; + const response = await this.fetchDepositAddress (code, this.extend (request, params)); + const address = this.safeString (response, 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response['info'], + }; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coinName': currency['id'], + 'need_new': 0, + }; + const response = await this.privatePostGetDepositAddress (this.extend (request, params)); + const address = this.safeString (response['return'], 'address'); + this.checkAddress (address); + return { + 'currency': code, + 'address': address, + 'tag': undefined, + 'info': response, + }; + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'coinName': currency['id'], + 'amount': amount, + 'address': address, + }; + // no docs on the tag, yet... + if (tag !== undefined) { + throw new ExchangeError (this.id + ' withdraw() does not support the tag argument yet due to a lack of docs on withdrawing with tag/memo on behalf of the exchange.'); + } + const response = await this.privatePostWithdrawCoinsToAddress (this.extend (request, params)); + return { + 'info': response, + 'id': undefined, + }; + } + + calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) { + const market = this.markets[symbol]; + let key = 'quote'; + const rate = market[takerOrMaker]; + let cost = parseFloat (this.costToPrecision (symbol, amount * rate)); + if (side === 'sell') { + cost *= price; + } else { + key = 'base'; + } + return { + 'type': takerOrMaker, + 'currency': market[key], + 'rate': rate, + 'cost': cost, + }; + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + const query = this.omit (params, this.extractParams (path)); + if (api === 'private') { + this.checkRequiredCredentials (); + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'nonce': nonce, + 'method': path, + }, query)); + const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512'); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': signature, + }; + } else if (api === 'public') { + url += '/' + this.version + '/' + this.implodeParams (path, params); + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + url += '/' + this.implodeParams (path, params); + if (method === 'GET') { + if (Object.keys (query).length) { + url += '?' + this.urlencode (query); + } + } else { + if (Object.keys (query).length) { + body = this.json (query); + headers = { + 'Content-Type': 'application/json', + }; + } + } + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if ('success' in response) { + // + // 1 - Liqui only returns the integer 'success' key from their private API + // + // { "success": 1, ... } httpCode === 200 + // { "success": 0, ... } httpCode === 200 + // + // 2 - However, exchanges derived from Liqui, can return non-integers + // + // It can be a numeric string + // { "sucesss": "1", ... } + // { "sucesss": "0", ... }, httpCode >= 200 (can be 403, 502, etc) + // + // Or just a string + // { "success": "true", ... } + // { "success": "false", ... }, httpCode >= 200 + // + // Or a boolean + // { "success": true, ... } + // { "success": false, ... }, httpCode >= 200 + // + // 3 - Oversimplified, Python PEP8 forbids comparison operator (===) of different types + // + // 4 - We do not want to copy-paste and duplicate the code of this handler to other exchanges derived from Liqui + // + // To cover points 1, 2, 3 and 4 combined this handler should work like this: + // + let success = this.safeValue (response, 'success', false); + if (typeof success === 'string') { + if ((success === 'true') || (success === '1')) { + success = true; + } else { + success = false; + } + } + if (!success) { + const code = this.safeString (response, 'code'); + const message = this.safeString (response, 'error'); + const feedback = this.id + ' ' + body; + this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback); + this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback); + throw new ExchangeError (feedback); // unknown message + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/zaif.js b/TaskServer/node_modules/ccxt/js/zaif.js new file mode 100644 index 000000000..9c43065cf --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/zaif.js @@ -0,0 +1,497 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { ExchangeError, BadRequest } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class zaif extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'zaif', + 'name': 'Zaif', + 'countries': [ 'JP' ], + 'rateLimit': 2000, + 'version': '1', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchOpenOrders': true, + 'fetchClosedOrders': true, + 'withdraw': true, + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/27766927-39ca2ada-5eeb-11e7-972f-1b4199518ca6.jpg', + 'api': 'https://api.zaif.jp', + 'www': 'https://zaif.jp', + 'doc': [ + 'https://techbureau-api-document.readthedocs.io/ja/latest/index.html', + 'https://corp.zaif.jp/api-docs', + 'https://corp.zaif.jp/api-docs/api_links', + 'https://www.npmjs.com/package/zaif.jp', + 'https://github.com/you21979/node-zaif', + ], + 'fees': 'https://zaif.jp/fee?lang=en', + }, + 'fees': { + 'trading': { + 'percentage': true, + 'taker': 0.1 / 100, + 'maker': 0, + }, + }, + 'api': { + 'public': { + 'get': [ + 'depth/{pair}', + 'currencies/{pair}', + 'currencies/all', + 'currency_pairs/{pair}', + 'currency_pairs/all', + 'last_price/{pair}', + 'ticker/{pair}', + 'trades/{pair}', + ], + }, + 'private': { + 'post': [ + 'active_orders', + 'cancel_order', + 'deposit_history', + 'get_id_info', + 'get_info', + 'get_info2', + 'get_personal_info', + 'trade', + 'trade_history', + 'withdraw', + 'withdraw_history', + ], + }, + 'ecapi': { + 'post': [ + 'createInvoice', + 'getInvoice', + 'getInvoiceIdsByOrderNumber', + 'cancelInvoice', + ], + }, + 'tlapi': { + 'post': [ + 'get_positions', + 'position_history', + 'active_positions', + 'create_position', + 'change_position', + 'cancel_position', + ], + }, + 'fapi': { + 'get': [ + 'groups/{group_id}', + 'last_price/{group_id}/{pair}', + 'ticker/{group_id}/{pair}', + 'trades/{group_id}/{pair}', + 'depth/{group_id}/{pair}', + ], + }, + }, + 'options': { + // zaif schedule defines several market-specific fees + 'fees': { + 'BTC/JPY': { 'maker': 0, 'taker': 0 }, + 'BCH/JPY': { 'maker': 0, 'taker': 0.3 / 100 }, + 'BCH/BTC': { 'maker': 0, 'taker': 0.3 / 100 }, + 'PEPECASH/JPY': { 'maker': 0, 'taker': 0.01 / 100 }, + 'PEPECASH/BT': { 'maker': 0, 'taker': 0.01 / 100 }, + }, + }, + 'exceptions': { + 'exact': { + 'unsupported currency_pair': BadRequest, // {"error": "unsupported currency_pair"} + }, + 'broad': { + }, + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetCurrencyPairsAll (params); + const result = []; + for (let i = 0; i < markets.length; i++) { + const market = markets[i]; + const id = this.safeString (market, 'currency_pair'); + const name = this.safeString (market, 'name'); + const [ baseId, quoteId ] = name.split ('/'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': -Math.log10 (market['item_unit_step']), + 'price': market['aux_unit_point'], + }; + const fees = this.safeValue (this.options['fees'], symbol, this.fees['trading']); + const taker = fees['taker']; + const maker = fees['maker']; + result.push ({ + 'id': id, + 'symbol': symbol, + 'base': base, + 'quote': quote, + 'baseId': baseId, + 'quoteId': quoteId, + 'active': true, // can trade or not + 'precision': precision, + 'taker': taker, + 'maker': maker, + 'limits': { + 'amount': { + 'min': this.safeFloat (market, 'item_unit_min'), + 'max': undefined, + }, + 'price': { + 'min': this.safeFloat (market, 'aux_unit_min'), + 'max': undefined, + }, + 'cost': { + 'min': undefined, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privatePostGetInfo (params); + const balances = this.safeValue (response, 'return', {}); + const result = { 'info': response }; + const funds = this.safeValue (balances, 'funds', {}); + const currencyIds = Object.keys (funds); + for (let i = 0; i < currencyIds.length; i++) { + const currencyId = currencyIds[i]; + const code = this.safeCurrencyCode (currencyId); + const balance = this.safeValue (funds, currencyId); + const account = { + 'free': balance, + 'used': 0.0, + 'total': balance, + }; + if ('deposit' in balances) { + if (currencyId in balances['deposit']) { + account['total'] = this.safeFloat (balances['deposit'], currencyId); + account['used'] = account['total'] - account['free']; + } + } + result[code] = account; + } + return this.parseBalance (result); + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const response = await this.publicGetDepthPair (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const request = { + 'pair': this.marketId (symbol), + }; + const ticker = await this.publicGetTickerPair (this.extend (request, params)); + const timestamp = this.milliseconds (); + const vwap = this.safeFloat (ticker, 'vwap'); + const baseVolume = this.safeFloat (ticker, 'volume'); + let quoteVolume = undefined; + if (baseVolume !== undefined && vwap !== undefined) { + quoteVolume = baseVolume * vwap; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'bid'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'ask'), + 'askVolume': undefined, + 'vwap': vwap, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': baseVolume, + 'quoteVolume': quoteVolume, + 'info': ticker, + }; + } + + parseTrade (trade, market = undefined) { + let side = this.safeString (trade, 'trade_type'); + side = (side === 'bid') ? 'buy' : 'sell'; + const timestamp = this.safeTimestamp (trade, 'date'); + const id = this.safeString2 (trade, 'id', 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = amount * price; + } + } + if (market === undefined) { + const marketId = this.safeString (trade, 'currency_pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'info': trade, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'pair': market['id'], + }; + let response = await this.publicGetTradesPair (this.extend (request, params)); + const numTrades = response.length; + if (numTrades === 1) { + const firstTrade = response[0]; + if (!Object.keys (firstTrade).length) { + response = []; + } + } + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + await this.loadMarkets (); + if (type !== 'limit') { + throw new ExchangeError (this.id + ' allows limit orders only'); + } + const request = { + 'currency_pair': this.marketId (symbol), + 'action': (side === 'buy') ? 'bid' : 'ask', + 'amount': amount, + 'price': price, + }; + const response = await this.privatePostTrade (this.extend (request, params)); + return { + 'info': response, + 'id': response['return']['order_id'].toString (), + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + const request = { + 'order_id': id, + }; + return await this.privatePostCancelOrder (this.extend (request, params)); + } + + parseOrder (order, market = undefined) { + let side = this.safeString (order, 'action'); + side = (side === 'bid') ? 'buy' : 'sell'; + const timestamp = this.safeTimestamp (order, 'timestamp'); + if (!market) { + const marketId = this.safeString (order, 'currency_pair'); + if (marketId in this.markets_by_id) { + market = this.markets_by_id[marketId]; + } + } + const price = this.safeFloat (order, 'price'); + const amount = this.safeFloat (order, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + const id = this.safeString (order, 'id'); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'status': 'open', + 'symbol': symbol, + 'type': 'limit', + 'side': side, + 'price': price, + 'cost': cost, + 'amount': amount, + 'filled': undefined, + 'remaining': undefined, + 'trades': undefined, + 'fee': undefined, + }; + } + + parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) { + const result = []; + const ids = Object.keys (orders); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const order = this.extend ({ 'id': id }, orders[id]); + result.push (this.extend (this.parseOrder (order, market), params)); + } + return this.filterBySymbolSinceLimit (result, symbol, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + // 'is_token': false, + // 'is_token_both': false, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['currency_pair'] = market['id']; + } + const response = await this.privatePostActiveOrders (this.extend (request, params)); + return this.parseOrders (response['return'], market, since, limit); + } + + async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + let market = undefined; + const request = { + // 'from': 0, + // 'count': 1000, + // 'from_id': 0, + // 'end_id': 1000, + // 'order': 'DESC', + // 'since': 1503821051, + // 'end': 1503821051, + // 'is_token': false, + }; + if (symbol !== undefined) { + market = this.market (symbol); + request['currency_pair'] = market['id']; + } + const response = await this.privatePostTradeHistory (this.extend (request, params)); + return this.parseOrders (response['return'], market, since, limit); + } + + async withdraw (code, amount, address, tag = undefined, params = {}) { + this.checkAddress (address); + await this.loadMarkets (); + const currency = this.currency (code); + if (code === 'JPY') { + throw new ExchangeError (this.id + ' withdraw() does not allow ' + code + ' withdrawals'); + } + const request = { + 'currency': currency['id'], + 'amount': amount, + 'address': address, + // 'message': 'Hi!', // XEM and others + // 'opt_fee': 0.003, // BTC and MONA only + }; + if (tag !== undefined) { + request['message'] = tag; + } + const result = await this.privatePostWithdraw (this.extend (request, params)); + return { + 'info': result, + 'id': result['return']['txid'], + 'fee': result['return']['fee'], + }; + } + + nonce () { + const nonce = parseFloat (this.milliseconds () / 1000); + return nonce.toFixed (8); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'] + '/'; + if (api === 'public') { + url += 'api/' + this.version + '/' + this.implodeParams (path, params); + } else if (api === 'fapi') { + url += 'fapi/' + this.version + '/' + this.implodeParams (path, params); + } else { + this.checkRequiredCredentials (); + if (api === 'ecapi') { + url += 'ecapi'; + } else if (api === 'tlapi') { + url += 'tlapi'; + } else { + url += 'tapi'; + } + const nonce = this.nonce (); + body = this.urlencode (this.extend ({ + 'method': path, + 'nonce': nonce, + }, params)); + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Key': this.apiKey, + 'Sign': this.hmac (this.encode (body), this.encode (this.secret), 'sha512'), + }; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; + } + // + // {"error": "unsupported currency_pair"} + // + const feedback = this.id + ' ' + body; + const error = this.safeString (response, 'error'); + if (error !== undefined) { + this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback); + this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback); + throw new ExchangeError (feedback); // unknown message + } + const success = this.safeValue (response, 'success', true); + if (!success) { + throw new ExchangeError (feedback); + } + } +}; diff --git a/TaskServer/node_modules/ccxt/js/zb.js b/TaskServer/node_modules/ccxt/js/zb.js new file mode 100644 index 000000000..f267337a8 --- /dev/null +++ b/TaskServer/node_modules/ccxt/js/zb.js @@ -0,0 +1,647 @@ +'use strict'; + +// --------------------------------------------------------------------------- + +const Exchange = require ('./base/Exchange'); +const { BadRequest, ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, OrderNotFound, ExchangeNotAvailable, DDoSProtection, InvalidOrder } = require ('./base/errors'); + +// --------------------------------------------------------------------------- + +module.exports = class zb extends Exchange { + describe () { + return this.deepExtend (super.describe (), { + 'id': 'zb', + 'name': 'ZB', + 'countries': [ 'CN' ], + 'rateLimit': 1000, + 'version': 'v1', + 'has': { + 'CORS': false, + 'createMarketOrder': false, + 'fetchDepositAddress': true, + 'fetchOrder': true, + 'fetchOrders': true, + 'fetchOpenOrders': true, + 'fetchOHLCV': true, + 'fetchTickers': true, + 'withdraw': true, + }, + 'timeframes': { + '1m': '1min', + '3m': '3min', + '5m': '5min', + '15m': '15min', + '30m': '30min', + '1h': '1hour', + '2h': '2hour', + '4h': '4hour', + '6h': '6hour', + '12h': '12hour', + '1d': '1day', + '3d': '3day', + '1w': '1week', + }, + 'exceptions': { + // '1000': 'Successful operation', + '1001': ExchangeError, // 'General error message', + '1002': ExchangeError, // 'Internal error', + '1003': AuthenticationError, // 'Verification does not pass', + '1004': AuthenticationError, // 'Funding security password lock', + '1005': AuthenticationError, // 'Funds security password is incorrect, please confirm and re-enter.', + '1006': AuthenticationError, // 'Real-name certification pending approval or audit does not pass', + '1009': ExchangeNotAvailable, // 'This interface is under maintenance', + '2001': InsufficientFunds, // 'Insufficient CNY Balance', + '2002': InsufficientFunds, // 'Insufficient BTC Balance', + '2003': InsufficientFunds, // 'Insufficient LTC Balance', + '2005': InsufficientFunds, // 'Insufficient ETH Balance', + '2006': InsufficientFunds, // 'Insufficient ETC Balance', + '2007': InsufficientFunds, // 'Insufficient BTS Balance', + '2009': InsufficientFunds, // 'Account balance is not enough', + '3001': OrderNotFound, // 'Pending orders not found', + '3002': InvalidOrder, // 'Invalid price', + '3003': InvalidOrder, // 'Invalid amount', + '3004': AuthenticationError, // 'User does not exist', + '3005': BadRequest, // 'Invalid parameter', + '3006': AuthenticationError, // 'Invalid IP or inconsistent with the bound IP', + '3007': AuthenticationError, // 'The request time has expired', + '3008': OrderNotFound, // 'Transaction records not found', + '4001': ExchangeNotAvailable, // 'API interface is locked or not enabled', + '4002': DDoSProtection, // 'Request too often', + }, + 'urls': { + 'logo': 'https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg', + 'api': { + 'public': 'http://api.zb.cn/data', // no https for public API + 'private': 'https://trade.zb.cn/api', + }, + 'www': 'https://www.zb.com', + 'doc': 'https://www.zb.com/i/developer', + 'fees': 'https://www.zb.com/i/rate', + }, + 'api': { + 'public': { + 'get': [ + 'markets', + 'ticker', + 'allTicker', + 'depth', + 'trades', + 'kline', + ], + }, + 'private': { + 'get': [ + // spot API + 'order', + 'cancelOrder', + 'getOrder', + 'getOrders', + 'getOrdersNew', + 'getOrdersIgnoreTradeType', + 'getUnfinishedOrdersIgnoreTradeType', + 'getAccountInfo', + 'getUserAddress', + 'getWithdrawAddress', + 'getWithdrawRecord', + 'getChargeRecord', + 'getCnyWithdrawRecord', + 'getCnyChargeRecord', + 'withdraw', + // leverage API + 'getLeverAssetsInfo', + 'getLeverBills', + 'transferInLever', + 'transferOutLever', + 'loan', + 'cancelLoan', + 'getLoans', + 'getLoanRecords', + 'borrow', + 'repay', + 'getRepayments', + ], + }, + }, + 'fees': { + 'funding': { + 'withdraw': { + 'BTC': 0.0001, + 'BCH': 0.0006, + 'LTC': 0.005, + 'ETH': 0.01, + 'ETC': 0.01, + 'BTS': 3, + 'EOS': 1, + 'QTUM': 0.01, + 'HSR': 0.001, + 'XRP': 0.1, + 'USDT': '0.1%', + 'QCASH': 5, + 'DASH': 0.002, + 'BCD': 0, + 'UBTC': 0, + 'SBTC': 0, + 'INK': 20, + 'TV': 0.1, + 'BTH': 0, + 'BCX': 0, + 'LBTC': 0, + 'CHAT': 20, + 'bitCNY': 20, + 'HLC': 20, + 'BTP': 0, + 'BCW': 0, + }, + }, + 'trading': { + 'maker': 0.2 / 100, + 'taker': 0.2 / 100, + }, + }, + 'commonCurrencies': { + 'ENT': 'ENTCash', + }, + }); + } + + async fetchMarkets (params = {}) { + const markets = await this.publicGetMarkets (params); + const keys = Object.keys (markets); + const result = []; + for (let i = 0; i < keys.length; i++) { + const id = keys[i]; + const market = markets[id]; + const [ baseId, quoteId ] = id.split ('_'); + const base = this.safeCurrencyCode (baseId); + const quote = this.safeCurrencyCode (quoteId); + const symbol = base + '/' + quote; + const precision = { + 'amount': this.safeInteger (market, 'amountScale'), + 'price': this.safeInteger (market, 'priceScale'), + }; + result.push ({ + 'id': id, + 'symbol': symbol, + 'baseId': baseId, + 'quoteId': quoteId, + 'base': base, + 'quote': quote, + 'active': true, + 'precision': precision, + 'limits': { + 'amount': { + 'min': Math.pow (10, -precision['amount']), + 'max': undefined, + }, + 'price': { + 'min': Math.pow (10, -precision['price']), + 'max': undefined, + }, + 'cost': { + 'min': 0, + 'max': undefined, + }, + }, + 'info': market, + }); + } + return result; + } + + async fetchBalance (params = {}) { + await this.loadMarkets (); + const response = await this.privateGetGetAccountInfo (params); + // todo: use this somehow + // let permissions = response['result']['base']; + const balances = this.safeValue (response['result'], 'coins'); + const result = { 'info': response }; + for (let i = 0; i < balances.length; i++) { + const balance = balances[i]; + // { enName: "BTC", + // freez: "0.00000000", + // unitDecimal: 8, // always 8 + // cnName: "BTC", + // isCanRecharge: true, // TODO: should use this + // unitTag: "฿", + // isCanWithdraw: true, // TODO: should use this + // available: "0.00000000", + // key: "btc" } + const account = this.account (); + const currencyId = this.safeString (balance, 'key'); + const code = this.safeCurrencyCode (currencyId); + account['free'] = this.safeFloat (balance, 'available'); + account['used'] = this.safeFloat (balance, 'freez'); + result[code] = account; + } + return this.parseBalance (result); + } + + getMarketFieldName () { + return 'market'; + } + + async fetchDepositAddress (code, params = {}) { + await this.loadMarkets (); + const currency = this.currency (code); + const request = { + 'currency': currency['id'], + }; + const response = await this.privateGetGetUserAddress (this.extend (request, params)); + let address = response['message']['datas']['key']; + let tag = undefined; + if (address.indexOf ('_') >= 0) { + const parts = address.split ('_'); + address = parts[0]; // WARNING: MAY BE tag_address INSTEAD OF address_tag FOR SOME CURRENCIES!! + tag = parts[1]; + } + return { + 'currency': code, + 'address': address, + 'tag': tag, + 'info': response, + }; + } + + async fetchOrderBook (symbol, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketFieldName = this.getMarketFieldName (); + const request = {}; + request[marketFieldName] = market['id']; + if (limit !== undefined) { + request['size'] = limit; + } + const response = await this.publicGetDepth (this.extend (request, params)); + return this.parseOrderBook (response); + } + + async fetchTickers (symbols = undefined, params = {}) { + await this.loadMarkets (); + const response = await this.publicGetAllTicker (params); + const result = {}; + const anotherMarketsById = {}; + const marketIds = Object.keys (this.marketsById); + for (let i = 0; i < marketIds.length; i++) { + const tickerId = marketIds[i].replace ('_', ''); + anotherMarketsById[tickerId] = this.marketsById[marketIds[i]]; + } + const ids = Object.keys (response); + for (let i = 0; i < ids.length; i++) { + const market = anotherMarketsById[ids[i]]; + result[market['symbol']] = this.parseTicker (response[ids[i]], market); + } + return result; + } + + async fetchTicker (symbol, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketFieldName = this.getMarketFieldName (); + const request = {}; + request[marketFieldName] = market['id']; + const response = await this.publicGetTicker (this.extend (request, params)); + const ticker = response['ticker']; + return this.parseTicker (ticker, market); + } + + parseTicker (ticker, market = undefined) { + const timestamp = this.milliseconds (); + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + const last = this.safeFloat (ticker, 'last'); + return { + 'symbol': symbol, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'high': this.safeFloat (ticker, 'high'), + 'low': this.safeFloat (ticker, 'low'), + 'bid': this.safeFloat (ticker, 'buy'), + 'bidVolume': undefined, + 'ask': this.safeFloat (ticker, 'sell'), + 'askVolume': undefined, + 'vwap': undefined, + 'open': undefined, + 'close': last, + 'last': last, + 'previousClose': undefined, + 'change': undefined, + 'percentage': undefined, + 'average': undefined, + 'baseVolume': this.safeFloat (ticker, 'vol'), + 'quoteVolume': undefined, + 'info': ticker, + }; + } + + async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + if (limit === undefined) { + limit = 1000; + } + const request = { + 'market': market['id'], + 'type': this.timeframes[timeframe], + 'limit': limit, + }; + if (since !== undefined) { + request['since'] = since; + } + const response = await this.publicGetKline (this.extend (request, params)); + const data = this.safeValue (response, 'data', []); + return this.parseOHLCVs (data, market, timeframe, since, limit); + } + + parseTrade (trade, market = undefined) { + const timestamp = this.safeTimestamp (trade, 'date'); + let side = this.safeString (trade, 'trade_type'); + side = (side === 'bid') ? 'buy' : 'sell'; + const id = this.safeString (trade, 'tid'); + const price = this.safeFloat (trade, 'price'); + const amount = this.safeFloat (trade, 'amount'); + let cost = undefined; + if (price !== undefined) { + if (amount !== undefined) { + cost = price * amount; + } + } + let symbol = undefined; + if (market !== undefined) { + symbol = market['symbol']; + } + return { + 'info': trade, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'symbol': symbol, + 'type': undefined, + 'side': side, + 'order': undefined, + 'takerOrMaker': undefined, + 'price': price, + 'amount': amount, + 'cost': cost, + 'fee': undefined, + }; + } + + async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { + await this.loadMarkets (); + const market = this.market (symbol); + const marketFieldName = this.getMarketFieldName (); + const request = {}; + request[marketFieldName] = market['id']; + const response = await this.publicGetTrades (this.extend (request, params)); + return this.parseTrades (response, market, since, limit); + } + + async createOrder (symbol, type, side, amount, price = undefined, params = {}) { + if (type !== 'limit') { + throw new InvalidOrder (this.id + ' allows limit orders only'); + } + await this.loadMarkets (); + const request = { + 'price': this.priceToPrecision (symbol, price), + 'amount': this.amountToPrecision (symbol, amount), + 'tradeType': (side === 'buy') ? '1' : '0', + 'currency': this.marketId (symbol), + }; + const response = await this.privateGetOrder (this.extend (request, params)); + return { + 'info': response, + 'id': response['id'], + }; + } + + async cancelOrder (id, symbol = undefined, params = {}) { + await this.loadMarkets (); + const request = { + 'id': id.toString (), + 'currency': this.marketId (symbol), + }; + return await this.privateGetCancelOrder (this.extend (request, params)); + } + + async fetchOrder (id, symbol = undefined, params = {}) { + if (symbol === undefined) { + throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument'); + } + await this.loadMarkets (); + const request = { + 'id': id.toString (), + 'currency': this.marketId (symbol), + }; + const response = await this.privateGetGetOrder (this.extend (request, params)); + // + // { + // 'total_amount': 0.01, + // 'id': '20180910244276459', + // 'price': 180.0, + // 'trade_date': 1536576744960, + // 'status': 2, + // 'trade_money': '1.96742', + // 'trade_amount': 0.01, + // 'type': 0, + // 'currency': 'eth_usdt' + // } + // + return this.parseOrder (response, undefined); + } + + async fetchOrders (symbol = undefined, since = undefined, limit = 50, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + 'fetchOrders requires a symbol parameter'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'pageIndex': 1, // default pageIndex is 1 + 'pageSize': limit, // default pageSize is 50 + }; + let method = 'privateGetGetOrdersIgnoreTradeType'; + // tradeType 交易类型1/0[buy/sell] + if ('tradeType' in params) { + method = 'privateGetGetOrdersNew'; + } + let response = undefined; + try { + response = await this[method] (this.extend (request, params)); + } catch (e) { + if (e instanceof OrderNotFound) { + return []; + } + throw e; + } + return this.parseOrders (response, market, since, limit); + } + + async fetchOpenOrders (symbol = undefined, since = undefined, limit = 10, params = {}) { + if (symbol === undefined) { + throw new ExchangeError (this.id + 'fetchOpenOrders requires a symbol parameter'); + } + await this.loadMarkets (); + const market = this.market (symbol); + const request = { + 'currency': market['id'], + 'pageIndex': 1, // default pageIndex is 1 + 'pageSize': limit, // default pageSize is 10 + }; + let method = 'privateGetGetUnfinishedOrdersIgnoreTradeType'; + // tradeType 交易类型1/0[buy/sell] + if ('tradeType' in params) { + method = 'privateGetGetOrdersNew'; + } + let response = undefined; + try { + response = await this[method] (this.extend (request, params)); + } catch (e) { + if (e instanceof OrderNotFound) { + return []; + } + throw e; + } + return this.parseOrders (response, market, since, limit); + } + + parseOrder (order, market = undefined) { + // + // fetchOrder + // + // { + // 'total_amount': 0.01, + // 'id': '20180910244276459', + // 'price': 180.0, + // 'trade_date': 1536576744960, + // 'status': 2, + // 'trade_money': '1.96742', + // 'trade_amount': 0.01, + // 'type': 0, + // 'currency': 'eth_usdt' + // } + // + let side = this.safeInteger (order, 'type'); + side = (side === 1) ? 'buy' : 'sell'; + const type = 'limit'; // market order is not availalbe in ZB + let timestamp = undefined; + const createDateField = this.getCreateDateField (); + if (createDateField in order) { + timestamp = order[createDateField]; + } + let symbol = undefined; + const marketId = this.safeString (order, 'currency'); + if (marketId in this.markets_by_id) { + // get symbol from currency + market = this.marketsById[marketId]; + } + if (market !== undefined) { + symbol = market['symbol']; + } + const price = this.safeFloat (order, 'price'); + const filled = this.safeFloat (order, 'trade_amount'); + const amount = this.safeFloat (order, 'total_amount'); + let remaining = undefined; + if (amount !== undefined) { + if (filled !== undefined) { + remaining = amount - filled; + } + } + const cost = this.safeFloat (order, 'trade_money'); + let average = undefined; + const status = this.parseOrderStatus (this.safeString (order, 'status')); + if ((cost !== undefined) && (filled !== undefined) && (filled > 0)) { + average = cost / filled; + } + const id = this.safeString (order, 'id'); + return { + 'info': order, + 'id': id, + 'timestamp': timestamp, + 'datetime': this.iso8601 (timestamp), + 'lastTradeTimestamp': undefined, + 'symbol': symbol, + 'type': type, + 'side': side, + 'price': price, + 'average': average, + 'cost': cost, + 'amount': amount, + 'filled': filled, + 'remaining': remaining, + 'status': status, + 'fee': undefined, + }; + } + + parseOrderStatus (status) { + const statuses = { + '0': 'open', + '1': 'canceled', + '2': 'closed', + '3': 'open', // partial + }; + return this.safeString (statuses, status, status); + } + + getCreateDateField () { + return 'trade_date'; + } + + nonce () { + return this.milliseconds (); + } + + sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { + let url = this.urls['api'][api]; + if (api === 'public') { + url += '/' + this.version + '/' + path; + if (Object.keys (params).length) { + url += '?' + this.urlencode (params); + } + } else { + let query = this.keysort (this.extend ({ + 'method': path, + 'accesskey': this.apiKey, + }, params)); + const nonce = this.nonce (); + query = this.keysort (query); + const auth = this.rawencode (query); + const secret = this.hash (this.encode (this.secret), 'sha1'); + const signature = this.hmac (this.encode (auth), this.encode (secret), 'md5'); + const suffix = 'sign=' + signature + '&reqTime=' + nonce.toString (); + url += '/' + path + '?' + auth + '&' + suffix; + } + return { 'url': url, 'method': method, 'body': body, 'headers': headers }; + } + + handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) { + if (response === undefined) { + return; // fallback to default error handler + } + if (body[0] === '{') { + const feedback = this.id + ' ' + body; + if ('code' in response) { + const code = this.safeString (response, 'code'); + this.throwExactlyMatchedException (this.exceptions, code, feedback); + if (code !== '1000') { + throw new ExchangeError (feedback); + } + } + // special case for {"result":false,"message":"æœ�务端忙碌"} (a "Busy Server" reply) + const result = this.safeValue (response, 'result'); + if (result !== undefined) { + if (!result) { + const message = this.safeString (response, 'message'); + if (message === 'æœ�务端忙碌') { + throw new ExchangeNotAvailable (feedback); + } else { + throw new ExchangeError (feedback); + } + } + } + } + } +}; diff --git a/TaskServer/node_modules/ccxt/package.json b/TaskServer/node_modules/ccxt/package.json new file mode 100644 index 000000000..fd4d61952 --- /dev/null +++ b/TaskServer/node_modules/ccxt/package.json @@ -0,0 +1,524 @@ +{ + "_from": "ccxt@1.22.39", + "_id": "ccxt@1.22.39", + "_inBundle": false, + "_integrity": "sha512-Vq4BzYS/FwRudPKQ6BQGU3SVntYv7EAQGT+2guf+e7qm45CJMhdoVwR1z89kl42fbK39XMnl8sUnRCwsKPs38A==", + "_location": "/ccxt", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "ccxt@1.22.39", + "name": "ccxt", + "escapedName": "ccxt", + "rawSpec": "1.22.39", + "saveSpec": null, + "fetchSpec": "1.22.39" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/ccxt/-/ccxt-1.22.39.tgz", + "_shasum": "ca28a2dafd94089238279c9e5f2f4a041348d065", + "_spec": "ccxt@1.22.39", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "author": { + "name": "Igor Kroitor", + "email": "igor.kroitor@gmail.com", + "url": "https://github.com/kroitor" + }, + "bugs": { + "url": "https://github.com/ccxt/ccxt/issues" + }, + "bundleDependencies": false, + "collective": { + "type": "opencollective", + "url": "https://opencollective.com/ccxt", + "logo": "https://opencollective.com/ccxt/logo.txt" + }, + "dependencies": {}, + "deprecated": false, + "description": "A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges", + "devDependencies": { + "ansicolor": "1.1.81", + "as-table": "1.0.37", + "asciichart": "1.5.7", + "browserify": "14.5.0", + "chai": "4.1.2", + "chai-as-promised": "7.1.1", + "eslint": "^4.19.1", + "eslint-config-airbnb-base": "12.1.0", + "eslint-plugin-import": "2.16.0", + "mocha": "5.2.0", + "ololog": "1.1.121" + }, + "engines": { + "node": ">=7.6.0" + }, + "ethereum": "0x26a3CB49578F07000575405a57888681249c35Fd", + "homepage": "https://ccxt.trade", + "keywords": [ + "algorithmic", + "algotrading", + "altcoin", + "altcoins", + "api", + "arbitrage", + "backtest", + "backtesting", + "bitcoin", + "bot", + "btc", + "cny", + "coin", + "coins", + "crypto", + "cryptocurrency", + "crypto currency", + "crypto market", + "currency", + "currencies", + "darkcoin", + "dash", + "digital currency", + "doge", + "dogecoin", + "e-commerce", + "etc", + "eth", + "ether", + "ethereum", + "exchange", + "exchanges", + "eur", + "framework", + "invest", + "investing", + "investor", + "library", + "light", + "litecoin", + "ltc", + "market", + "market data", + "markets", + "merchandise", + "merchant", + "minimal", + "ohlcv", + "order", + "orderbook", + "order book", + "price", + "price data", + "pricefeed", + "private", + "public", + "ripple", + "strategy", + "ticker", + "tickers", + "toolkit", + "trade", + "trader", + "trading", + "usd", + "volume", + "xbt", + "xrp", + "zec", + "zerocoin", + "1Broker", + "1BTCXE", + "ACX", + "acx.io", + "allcoin", + "allcoin.com", + "ANX", + "ANXPro", + "bibox", + "bibox.com", + "Binance", + "binance.com", + "bit2c.co.il", + "Bit2C", + "BitBay", + "BitBays", + "bitcoincoid", + "Bitcoin.co.id", + "Bitfinex", + "bitFLyer", + "bitflyer.jp", + "bithumb", + "bithumb.com", + "bitlish", + "BitMarket", + "BitMEX", + "Bitso", + "Bitstamp", + "Bittrex", + "BL3P", + "Bleutrade", + "bleutrade.com", + "BlinkTrade", + "braziliex", + "braziliex.com", + "BtcBox", + "btcbox.co.jp", + "BTCC", + "BTCChina", + "BTC-e", + "BTCe", + "BTCExchange", + "btcexchange.ph", + "BTC Markets", + "btcmarkets", + "btcmarkets.net", + "BTCTrader", + "btctrader.com", + "btc-trade.com.ua", + "BTC Trade UA", + "BTCTurk", + "btcturk.com", + "BTCX", + "btc-x", + "bter", + "Bter.com", + "BX.in.th", + "ccex", + "C-CEX", + "cex", + "CEX.IO", + "CHBTC", + "ChileBit", + "chilebit.net", + "coincheck", + "CoinExchange", + "coinexchange.io", + "coingi", + "coingi.com", + "CoinMarketCap", + "CoinMate", + "Coinsecure", + "CoinSpot", + "coinspot.com.au", + "Crypto Capital", + "cryptocapital.co", + "DSX", + "dsx.uk", + "EXMO", + "flowBTC", + "flowbtc.com", + "FoxBit", + "foxbit.exchange", + "FYB-SE", + "FYB-SG", + "Gatecoin", + "GDAX", + "Gemini", + "HitBTC", + "Huobi", + "HuobiPRO", + "huobi.pro", + "Independent Reserve", + "independentreserve.com", + "itBit", + "jubi.com", + "Kraken", + "Kucoin", + "Kuna", + "LakeBTC", + "lakebtc.com", + "LiveCoin", + "Liqui", + "liqui.io", + "luno", + "mercado", + "MercadoBitcoin", + "mercadobitcoin.br", + "mixcoins", + "mixcoins.com", + "nova", + "novaexchange", + "novaexchange.com", + "OKCoin", + "OKCoin.com", + "OKCoin.cn", + "OKEX", + "okex.com", + "Paymium", + "Poloniex", + "QuadrigaCX", + "Qryptos", + "QUOINEX", + "Southxchange", + "SurBitcoin", + "surbitcoin.com", + "Tidex", + "tidex.com", + "TheRockTrading", + "UrduBit", + "urdubit.com", + "Vaultoro", + "VBTC", + "vbtc.exchange", + "vbtc.vn", + "VirWoX", + "WEX", + "wex.nz", + "xBTCe", + "xbtce.com", + "YoBit", + "yobit.net", + "YUNBI", + "Zaif", + "ZB", + "1btcxe.com", + "Allcoin", + "anxpro.com", + "anybits.com", + "Anybits", + "bcex.top", + "BCEX", + "Bibox", + "big.one", + "BigONE", + "bitbank.cc", + "bitbank", + "bitbay.net", + "bitfinex.com", + "bitFlyer", + "bitforex.com", + "Bitforex", + "Bithumb", + "bitibu.com", + "Bitibu", + "bitkk.com", + "bitkk", + "bitlish.com", + "Bitlish", + "bitmarket.pl", + "bitmarket.net", + "bitmex.com", + "bitsane.com", + "Bitsane", + "bitso.com", + "bitstamp.net", + "bittrex.com", + "bit-z.com", + "Bit-Z", + "bl3p.eu", + "bitonic.nl", + "Braziliex", + "btc-alpha.com", + "BTC-Alpha", + "btcchina.com", + "btctrade.im", + "BtcTrade.im", + "buda.com", + "Buda", + "bx.in.th", + "c-cex.com", + "cex.io", + "trade.chbtc.com", + "cobinhood.com", + "COBINHOOD", + "coinbase.com", + "Coinbase", + "prime.coinbase.com", + "Coinbase Prime", + "pro.coinbase.com", + "Coinbase Pro", + "coincheck.com", + "coinegg.com", + "CoinEgg", + "coinex.com", + "CoinEx", + "coinfalcon.com", + "CoinFalcon", + "coinfloor.co.uk", + "coinfloor", + "Coingi", + "coinmarketcap.com", + "coinmate.io", + "coinnest.co.kr", + "coinnest", + "coinone.co.kr", + "CoinOne", + "cointiger.pro", + "CoinTiger", + "coolcoin.com", + "CoolCoin", + "coss.io", + "COSS", + "crex24.com", + "CREX24", + "cryptonbtc.com", + "Crypton", + "deribit.com", + "Deribit", + "ethfinex.com", + "Ethfinex", + "exmo.me", + "exx.com", + "EXX", + "fcoin.com", + "FCoin", + "trader.flowbtc.com", + "fybse.se", + "fybsg.com", + "gatecoin.com", + "gate.io", + "Gate.io", + "gdax.com", + "gemini.com", + "getbtc.org", + "GetBTC", + "hadax.com", + "HADAX", + "hitbtc.com", + "Huobi Pro", + "huobi.com.ru", + "Huobi Russia", + "ice3x.com", + "ice3x.co.za", + "ICE3X", + "indodax.com", + "INDODAX", + "itbit.com", + "kkex.com", + "KKEX", + "kraken.com", + "kucoin.com", + "KuCoin", + "kuna.io", + "lbank.info", + "LBank", + "liquid.com", + "Liquid", + "livecoin.net", + "luno.com", + "lykke.com", + "Lykke", + "mercadobitcoin.com.br", + "Mercado Bitcoin", + "MixCoins", + "negociecoins.com.br", + "NegocieCoins", + "Novaexchange", + "okcoin.cn", + "OKCoin CNY", + "okcoin.com", + "OKCoin USD", + "paymium.com", + "poloniex.com", + "quadrigacx.com", + "rightbtc.com", + "RightBTC", + "southxchange.com", + "SouthXchange", + "stronghold.co", + "Stronghold", + "theocean.trade", + "The Ocean", + "therocktrading.com", + "tidebit.com", + "TideBit", + "uex.com", + "UEX", + "upbit.com", + "Upbit", + "vaultoro.com", + "virwox.com", + "yunbi.com", + "zaif.jp", + "zb.com", + "fcoinjp.com", + "FCoinJP", + "binance.je", + "Binance Jersey", + "bequant.io", + "Bequant", + "mandalaex.com", + "Mandala", + "dx.exchange", + "DX.Exchange", + "oceanex.pro.com", + "OceanEx", + "flowbtc.com.br", + "foxbit.com.br", + "latoken.com", + "Latoken", + "bitmart.com", + "BitMart", + "digifinex.vip", + "DigiFinex", + "idex.market", + "IDEX", + "adara.io", + "Adara", + "binance.us", + "Binance US", + "whitebit.com", + "WhiteBit", + "bitmax.io", + "BitMax", + "bytetrade.com", + "ByteTrade", + "ftx.com", + "FTX", + "{hostname}", + "bw.com", + "stex.com", + "STEX", + "BW", + "timex.io", + "TimeX", + "bitz.com" + ], + "license": "MIT", + "main": "./ccxt.js", + "name": "ccxt", + "repository": { + "type": "git", + "url": "git+https://github.com/ccxt/ccxt.git" + }, + "scripts": { + "browserify": "browserify ./ccxt.browser.js > ./dist/ccxt.browser.js", + "build": "npm run build-without-docs && npm run pandoc-all && npm run update-badges", + "build-without-docs": "npm run export-exchanges && npm run vss && npm run check-js-syntax && npm run browserify && npm run transpile && npm run qa", + "check-js-syntax": "pwd && eslint --version && eslint 'js/*.js'", + "check-php-syntax": "php -f php/test/syntax.php", + "check-python-syntax": "cd python && tox -e qa && cd ..", + "check-syntax": "npm run transpile && npm run check-js-syntax && npm run check-python-syntax && npm run check-php-syntax", + "convert-md-2-rst": "bash build/convert-md-2-rst", + "docker": "docker-compose run --rm ccxt", + "export-exchanges": "node build/export-exchanges", + "fast-test": "node run-tests --js", + "git-ignore-generated-files": "node build/git-ignore-generated-files", + "git-unignore-generated-files": "node build/git-ignore-generated-files --unignore", + "lint": "eslint", + "pandoc-all": "npm run pandoc-python-readme && npm run pandoc-doc-readme && npm run pandoc-doc-faq && npm run pandoc-doc-manual && npm run pandoc-doc-install && npm run pandoc-doc-exchanges && npm run pandoc-doc-exchanges-by-country", + "pandoc-doc-exchanges": "pandoc --wrap=preserve --columns=10000 --from=markdown --to=rst --output=doc/exchanges.rst wiki/Exchange-Markets.md", + "pandoc-doc-exchanges-by-country": "pandoc --wrap=preserve --columns=10000 --from=markdown --to=rst --output=doc/exchanges-by-country.rst wiki/Exchange-Markets-By-Country.md", + "pandoc-doc-faq": "pandoc --wrap=preserve --columns=10000 --from=markdown --to=rst --output=doc/FAQ.rst wiki/FAQ.md", + "pandoc-doc-install": "pandoc --wrap=preserve --columns=10000 --from=markdown --to=rst --output=doc/install.rst wiki/Install.md", + "pandoc-doc-manual": "pandoc --wrap=preserve --columns=10000 --from=markdown --to=rst --output=doc/manual.rst wiki/Manual.md", + "pandoc-doc-readme": "pandoc --wrap=preserve --columns=10000 --from=markdown --to=rst --output=doc/README.rst README.md", + "pandoc-python-readme": "pandoc --wrap=preserve --columns=10000 --from=markdown --to=rst --output=python/README.rst README.md", + "postinstall": "node postinstall", + "qa": "npm run check-python-syntax && npm run check-php-syntax", + "test": "npm run build && node run-tests", + "test-base": "npm run test-js-base && npm run test-python-base && npm run test-php-base", + "test-js-base": "mocha js/test/base/test.base.js --reporter ololog/reporter", + "test-php-base": "php -f php/test/decimal_to_precision.php && php -f php/test/test_crypto.php", + "test-python-base": "python python/test/test_decimal_to_precision.py", + "transpile": "node build/transpile", + "update-badges": "node build/update-badges", + "vss": "node build/vss" + }, + "types": "./ccxt.d.ts", + "unpkg": "dist/ccxt.browser.js", + "version": "1.22.39" +} diff --git a/TaskServer/node_modules/ccxt/postinstall.js b/TaskServer/node_modules/ccxt/postinstall.js new file mode 100644 index 000000000..20f5b2fd4 --- /dev/null +++ b/TaskServer/node_modules/ccxt/postinstall.js @@ -0,0 +1,95 @@ +const fetch = require ('./js/static_dependencies/fetch-ponyfill/fetch-node') ().fetch + +function style(s, style) { + return style + s + '\033[0m' +} + +const colors = { + black: 30, + red: 31, + green: 32, + yellow: 33, + blue: 34, + white: 37, + gray: 90, +} + +let colorFunctions = {} +for (let color of Object.keys (colors)) { + colorFunctions[color] = (s) => console.log (style (s, '\033[' + colors[color].toString () + 'm')) +} + +let ascii = [ + ' ', + ' :Siiiiiiiiiiir rSiiiiiiiiiiS:', + ' r&9hh&&&&&&&A5 SG99h&&&&&&GHr', + ' ;hX32;::::::;, i9X9S:;:::::;,', + ' ;hX9S ihXhr ', + ' ;hX32::::::,:, i9X9i::::::,:.', + ' rG999GGGGGGGAS iG99hGGGGGGGAr', + ' ;2S55SSSSSSS2r r2555SSSSSSS2;', + ' ;2S5s ;2S2r r2SS555555SS2;', + ' rAh&2 sAhAS SAGGh9999GGGAr', + ' .:,::rrrs::::, ,:,,;9X3X:,,:.', + ' &A&H, ,hX33 ', + ' ,;:;;;;;r;;:;, ,hX3X. ', + ' rHGAX sAGA5 :&9h9. ', + ' :Ssir ;isir ,Siii ', + ' ', +] + +let footer = [ + ' ', + ' ---------------------------------------------------', + ' ', + ' You can contribute in crypto directly: ', + ' ', + ' ETH 0x26a3CB49578F07000575405a57888681249c35Fd ', + ' BTC 33RmVRfhK2WZVQR1R83h2e9yXoqRNDvJva ', + ' BCH 1GN9p233TvNcNQFthCgfiHUnj5JRKEc2Ze ', + ' LTC LbT8mkAqQBphc4yxLXEDgYDfEax74et3bP ', + ' ', + ' ---------------------------------------------------', + ' ', + ' Thank you! ', + ' ', +] + +async function getData () { + const collectiveData = await (await fetch ('https://opencollective.com/ccxt.json')).json () + const githubData = await (await fetch ('https://api.github.com/repos/ccxt/ccxt')).json () + + return { + contributors: collectiveData['contributorsCount'].toLocaleString (), + backers: collectiveData['backersCount'].toLocaleString (), + balance: Math.floor (collectiveData['balance'] / 100).toLocaleString (), + budget: Math.floor (collectiveData['yearlyIncome'] / 100).toLocaleString (), + stars: githubData['stargazers_count'].toLocaleString (), + forks: githubData['forks_count'].toLocaleString (), + size: (githubData['size'] / 1000000).toFixed (2) + } +} + +function pad (string) { + const padding = 80 - string.length + const half = Math.floor (padding / 2) + return ' '.repeat (half + (padding % 2)) + string + ' '.repeat (half) +} + +async function main () { + const data = await getData() + + colorFunctions['blue'] (ascii.join ('\n')) + colorFunctions['red'] (pad (`Stars: ${data.stars}`)) + colorFunctions['red'] (pad (`Forks: ${data.forks}`)) + colorFunctions['red'] (pad (`Contributors: ${data.contributors}`)) + colorFunctions['red'] (pad (`Size: ${data.size}MB`)) + colorFunctions['yellow'] ('\n' + pad ('Thanks for installing ccxt ðŸ™�')) + colorFunctions['gray'] (pad ('Please consider donating to our open collective')) + colorFunctions['gray'] (pad ('to help us maintain this package.')) + colorFunctions['yellow'] (pad ('👉 Donate: https://opencollective.com/ccxt/donate 🎉')) + colorFunctions['white'] (pad (`Thanks to our ${data.backers} backers we are operating on an annual budget of $${data.budget}`)) + colorFunctions['yellow'] (footer.join ('\n')) +} + +main() diff --git a/TaskServer/node_modules/debug/dist/debug.js b/TaskServer/node_modules/debug/dist/debug.js new file mode 100644 index 000000000..89ad0c217 --- /dev/null +++ b/TaskServer/node_modules/debug/dist/debug.js @@ -0,0 +1,912 @@ +"use strict"; + +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } + +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } + +function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } + +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +(function (f) { + if ((typeof exports === "undefined" ? "undefined" : _typeof(exports)) === "object" && typeof module !== "undefined") { + module.exports = f(); + } else if (typeof define === "function" && define.amd) { + define([], f); + } else { + var g; + + if (typeof window !== "undefined") { + g = window; + } else if (typeof global !== "undefined") { + g = global; + } else if (typeof self !== "undefined") { + g = self; + } else { + g = this; + } + + g.debug = f(); + } +})(function () { + var define, module, exports; + return function () { + function r(e, n, t) { + function o(i, f) { + if (!n[i]) { + if (!e[i]) { + var c = "function" == typeof require && require; + if (!f && c) return c(i, !0); + if (u) return u(i, !0); + var a = new Error("Cannot find module '" + i + "'"); + throw a.code = "MODULE_NOT_FOUND", a; + } + + var p = n[i] = { + exports: {} + }; + e[i][0].call(p.exports, function (r) { + var n = e[i][1][r]; + return o(n || r); + }, p, p.exports, r, e, n, t); + } + + return n[i].exports; + } + + for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) { + o(t[i]); + } + + return o; + } + + return r; + }()({ + 1: [function (require, module, exports) { + /** + * Helpers. + */ + var s = 1000; + var m = s * 60; + var h = m * 60; + var d = h * 24; + var w = d * 7; + var y = d * 365.25; + /** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + + module.exports = function (val, options) { + options = options || {}; + + var type = _typeof(val); + + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + + throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val)); + }; + /** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + + + function parse(str) { + str = String(str); + + if (str.length > 100) { + return; + } + + var match = /^((?:\d+)?\-?\d?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str); + + if (!match) { + return; + } + + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + + case 'weeks': + case 'week': + case 'w': + return n * w; + + case 'days': + case 'day': + case 'd': + return n * d; + + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + + default: + return undefined; + } + } + /** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + + function fmtShort(ms) { + var msAbs = Math.abs(ms); + + if (msAbs >= d) { + return Math.round(ms / d) + 'd'; + } + + if (msAbs >= h) { + return Math.round(ms / h) + 'h'; + } + + if (msAbs >= m) { + return Math.round(ms / m) + 'm'; + } + + if (msAbs >= s) { + return Math.round(ms / s) + 's'; + } + + return ms + 'ms'; + } + /** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + + function fmtLong(ms) { + var msAbs = Math.abs(ms); + + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day'); + } + + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour'); + } + + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute'); + } + + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second'); + } + + return ms + ' ms'; + } + /** + * Pluralization helper. + */ + + + function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); + } + }, {}], + 2: [function (require, module, exports) { + // shim for using process in browser + var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + + function defaultClearTimeout() { + throw new Error('clearTimeout has not been defined'); + } + + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + })(); + + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } // if setTimeout wasn't available but was latter defined + + + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch (e) { + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch (e) { + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + } + + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } // if clearTimeout wasn't available but was latter defined + + + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e) { + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e) { + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + } + + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + + draining = false; + + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + + var timeout = runTimeout(cleanUpNextTick); + draining = true; + var len = queue.length; + + while (len) { + currentQueue = queue; + queue = []; + + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + + queueIndex = -1; + len = queue.length; + } + + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + + queue.push(new Item(fun, args)); + + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; // v8 likes predictible objects + + + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + process.prependListener = noop; + process.prependOnceListener = noop; + + process.listeners = function (name) { + return []; + }; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { + return '/'; + }; + + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + + process.umask = function () { + return 0; + }; + }, {}], + 3: [function (require, module, exports) { + /** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + */ + function setup(env) { + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable; + createDebug.enable = enable; + createDebug.enabled = enabled; + createDebug.humanize = require('ms'); + Object.keys(env).forEach(function (key) { + createDebug[key] = env[key]; + }); + /** + * Active `debug` instances. + */ + + createDebug.instances = []; + /** + * The currently active debug mode names, and names to skip. + */ + + createDebug.names = []; + createDebug.skips = []; + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + + createDebug.formatters = {}; + /** + * Selects a color for a debug namespace + * @param {String} namespace The namespace string for the for the debug instance to be colored + * @return {Number|String} An ANSI color code for the given namespace + * @api private + */ + + function selectColor(namespace) { + var hash = 0; + + for (var i = 0; i < namespace.length; i++) { + hash = (hash << 5) - hash + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; + } + + createDebug.selectColor = selectColor; + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + + function createDebug(namespace) { + var prevTime; + + function debug() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + // Disabled? + if (!debug.enabled) { + return; + } + + var self = debug; // Set `diff` timestamp + + var curr = Number(new Date()); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + args[0] = createDebug.coerce(args[0]); + + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O'); + } // Apply any `formatters` transformations + + + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return match; + } + + index++; + var formatter = createDebug.formatters[format]; + + if (typeof formatter === 'function') { + var val = args[index]; + match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format` + + args.splice(index, 1); + index--; + } + + return match; + }); // Apply env-specific formatting (colors, etc.) + + createDebug.formatArgs.call(self, args); + var logFn = self.log || createDebug.log; + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = createDebug.enabled(namespace); + debug.useColors = createDebug.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; + debug.extend = extend; // Debug.formatArgs = formatArgs; + // debug.rawLog = rawLog; + // env-specific initialization logic for debug instances + + if (typeof createDebug.init === 'function') { + createDebug.init(debug); + } + + createDebug.instances.push(debug); + return debug; + } + + function destroy() { + var index = createDebug.instances.indexOf(this); + + if (index !== -1) { + createDebug.instances.splice(index, 1); + return true; + } + + return false; + } + + function extend(namespace, delimiter) { + var newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); + newDebug.log = this.log; + return newDebug; + } + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + + + function enable(namespaces) { + createDebug.save(namespaces); + createDebug.names = []; + createDebug.skips = []; + var i; + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; + + for (i = 0; i < len; i++) { + if (!split[i]) { + // ignore empty strings + continue; + } + + namespaces = split[i].replace(/\*/g, '.*?'); + + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')); + } + } + + for (i = 0; i < createDebug.instances.length; i++) { + var instance = createDebug.instances[i]; + instance.enabled = createDebug.enabled(instance.namespace); + } + } + /** + * Disable debug output. + * + * @return {String} namespaces + * @api public + */ + + + function disable() { + var namespaces = [].concat(_toConsumableArray(createDebug.names.map(toNamespace)), _toConsumableArray(createDebug.skips.map(toNamespace).map(function (namespace) { + return '-' + namespace; + }))).join(','); + createDebug.enable(''); + return namespaces; + } + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + + + function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + + var i; + var len; + + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false; + } + } + + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true; + } + } + + return false; + } + /** + * Convert regexp to namespace + * + * @param {RegExp} regxep + * @return {String} namespace + * @api private + */ + + + function toNamespace(regexp) { + return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, '*'); + } + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + + + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + + return val; + } + + createDebug.enable(createDebug.load()); + return createDebug; + } + + module.exports = setup; + }, { + "ms": 1 + }], + 4: [function (require, module, exports) { + (function (process) { + /* eslint-env browser */ + + /** + * This is the web browser implementation of `debug()`. + */ + exports.log = log; + exports.formatArgs = formatArgs; + exports.save = save; + exports.load = load; + exports.useColors = useColors; + exports.storage = localstorage(); + /** + * Colors. + */ + + exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33']; + /** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + // eslint-disable-next-line complexity + + function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { + return true; + } // Internet Explorer and Edge do not support colors. + + + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } // Is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + + + return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 + typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); + } + /** + * Colorize log arguments if enabled. + * + * @api public + */ + + + function formatArgs(args) { + args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff); + + if (!this.useColors) { + return; + } + + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); // The final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function (match) { + if (match === '%%') { + return; + } + + index++; + + if (match === '%c') { + // We only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + args.splice(lastC, 0, c); + } + /** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + + + function log() { + var _console; + + // This hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return (typeof console === "undefined" ? "undefined" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments); + } + /** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + + function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces); + } else { + exports.storage.removeItem('debug'); + } + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } + } + /** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + + + function load() { + var r; + + try { + r = exports.storage.getItem('debug'); + } catch (error) {} // Swallow + // XXX (@Qix-) should we be logging these? + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + + + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; + } + /** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + + + function localstorage() { + try { + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context + // The Browser also has localStorage in the global context. + return localStorage; + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } + } + + module.exports = require('./common')(exports); + var formatters = module.exports.formatters; + /** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + + formatters.j = function (v) { + try { + return JSON.stringify(v); + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message; + } + }; + }).call(this, require('_process')); + }, { + "./common": 3, + "_process": 2 + }] + }, {}, [4])(4); +}); diff --git a/TaskServer/node_modules/debug/package.json b/TaskServer/node_modules/debug/package.json new file mode 100644 index 000000000..0bb80da2a --- /dev/null +++ b/TaskServer/node_modules/debug/package.json @@ -0,0 +1,102 @@ +{ + "_from": "debug@^4.0.1", + "_id": "debug@4.1.1", + "_inBundle": false, + "_integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "_location": "/debug", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "debug@^4.0.1", + "name": "debug", + "escapedName": "debug", + "rawSpec": "^4.0.1", + "saveSpec": null, + "fetchSpec": "^4.0.1" + }, + "_requiredBy": [ + "/telegraf" + ], + "_resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "_shasum": "3b72260255109c6b589cee050f1d516139664791", + "_spec": "debug@^4.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\telegraf", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "browser": "./src/browser.js", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + }, + { + "name": "Andrew Rhyne", + "email": "rhyneandrew@gmail.com" + } + ], + "dependencies": { + "ms": "^2.1.1" + }, + "deprecated": false, + "description": "small debugging utility", + "devDependencies": { + "@babel/cli": "^7.0.0", + "@babel/core": "^7.0.0", + "@babel/preset-env": "^7.0.0", + "browserify": "14.4.0", + "chai": "^3.5.0", + "concurrently": "^3.1.0", + "coveralls": "^3.0.2", + "istanbul": "^0.4.5", + "karma": "^3.0.0", + "karma-chai": "^0.1.0", + "karma-mocha": "^1.3.0", + "karma-phantomjs-launcher": "^1.0.2", + "mocha": "^5.2.0", + "mocha-lcov-reporter": "^1.2.0", + "rimraf": "^2.5.4", + "xo": "^0.23.0" + }, + "files": [ + "src", + "dist/debug.js", + "LICENSE", + "README.md" + ], + "homepage": "https://github.com/visionmedia/debug#readme", + "keywords": [ + "debug", + "log", + "debugger" + ], + "license": "MIT", + "main": "./src/index.js", + "name": "debug", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "scripts": { + "build": "npm run build:debug && npm run build:test", + "build:debug": "babel -o dist/debug.js dist/debug.es6.js > dist/debug.js", + "build:test": "babel -d dist test.js", + "clean": "rimraf dist coverage", + "lint": "xo", + "prebuild:debug": "mkdir -p dist && browserify --standalone debug -o dist/debug.es6.js .", + "pretest:browser": "npm run build", + "test": "npm run test:node && npm run test:browser", + "test:browser": "karma start --single-run", + "test:coverage": "cat ./coverage/lcov.info | coveralls", + "test:node": "istanbul cover _mocha -- test.js" + }, + "unpkg": "./dist/debug.js", + "version": "4.1.1" +} diff --git a/TaskServer/node_modules/debug/src/browser.js b/TaskServer/node_modules/debug/src/browser.js new file mode 100644 index 000000000..5f34c0d0a --- /dev/null +++ b/TaskServer/node_modules/debug/src/browser.js @@ -0,0 +1,264 @@ +/* eslint-env browser */ + +/** + * This is the web browser implementation of `debug()`. + */ + +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + '#0000CC', + '#0000FF', + '#0033CC', + '#0033FF', + '#0066CC', + '#0066FF', + '#0099CC', + '#0099FF', + '#00CC00', + '#00CC33', + '#00CC66', + '#00CC99', + '#00CCCC', + '#00CCFF', + '#3300CC', + '#3300FF', + '#3333CC', + '#3333FF', + '#3366CC', + '#3366FF', + '#3399CC', + '#3399FF', + '#33CC00', + '#33CC33', + '#33CC66', + '#33CC99', + '#33CCCC', + '#33CCFF', + '#6600CC', + '#6600FF', + '#6633CC', + '#6633FF', + '#66CC00', + '#66CC33', + '#9900CC', + '#9900FF', + '#9933CC', + '#9933FF', + '#99CC00', + '#99CC33', + '#CC0000', + '#CC0033', + '#CC0066', + '#CC0099', + '#CC00CC', + '#CC00FF', + '#CC3300', + '#CC3333', + '#CC3366', + '#CC3399', + '#CC33CC', + '#CC33FF', + '#CC6600', + '#CC6633', + '#CC9900', + '#CC9933', + '#CCCC00', + '#CCCC33', + '#FF0000', + '#FF0033', + '#FF0066', + '#FF0099', + '#FF00CC', + '#FF00FF', + '#FF3300', + '#FF3333', + '#FF3366', + '#FF3399', + '#FF33CC', + '#FF33FF', + '#FF6600', + '#FF6633', + '#FF9900', + '#FF9933', + '#FFCC00', + '#FFCC33' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +// eslint-disable-next-line complexity +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { + return true; + } + + // Internet Explorer and Edge do not support colors. + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } + + // Is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // Is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // Double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); +} + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs(args) { + args[0] = (this.useColors ? '%c' : '') + + this.namespace + + (this.useColors ? ' %c' : ' ') + + args[0] + + (this.useColors ? '%c ' : ' ') + + '+' + module.exports.humanize(this.diff); + + if (!this.useColors) { + return; + } + + const c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); + + // The final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + let index = 0; + let lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, match => { + if (match === '%%') { + return; + } + index++; + if (match === '%c') { + // We only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ +function log(...args) { + // This hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return typeof console === 'object' && + console.log && + console.log(...args); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ +function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces); + } else { + exports.storage.removeItem('debug'); + } + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ +function load() { + let r; + try { + r = exports.storage.getItem('debug'); + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } + + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; +} + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage() { + try { + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context + // The Browser also has localStorage in the global context. + return localStorage; + } catch (error) { + // Swallow + // XXX (@Qix-) should we be logging these? + } +} + +module.exports = require('./common')(exports); + +const {formatters} = module.exports; + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +formatters.j = function (v) { + try { + return JSON.stringify(v); + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message; + } +}; diff --git a/TaskServer/node_modules/debug/src/common.js b/TaskServer/node_modules/debug/src/common.js new file mode 100644 index 000000000..2f82b8dc7 --- /dev/null +++ b/TaskServer/node_modules/debug/src/common.js @@ -0,0 +1,266 @@ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + */ + +function setup(env) { + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable; + createDebug.enable = enable; + createDebug.enabled = enabled; + createDebug.humanize = require('ms'); + + Object.keys(env).forEach(key => { + createDebug[key] = env[key]; + }); + + /** + * Active `debug` instances. + */ + createDebug.instances = []; + + /** + * The currently active debug mode names, and names to skip. + */ + + createDebug.names = []; + createDebug.skips = []; + + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + createDebug.formatters = {}; + + /** + * Selects a color for a debug namespace + * @param {String} namespace The namespace string for the for the debug instance to be colored + * @return {Number|String} An ANSI color code for the given namespace + * @api private + */ + function selectColor(namespace) { + let hash = 0; + + for (let i = 0; i < namespace.length; i++) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; + } + createDebug.selectColor = selectColor; + + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + function createDebug(namespace) { + let prevTime; + + function debug(...args) { + // Disabled? + if (!debug.enabled) { + return; + } + + const self = debug; + + // Set `diff` timestamp + const curr = Number(new Date()); + const ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + args[0] = createDebug.coerce(args[0]); + + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O'); + } + + // Apply any `formatters` transformations + let index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return match; + } + index++; + const formatter = createDebug.formatters[format]; + if (typeof formatter === 'function') { + const val = args[index]; + match = formatter.call(self, val); + + // Now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + // Apply env-specific formatting (colors, etc.) + createDebug.formatArgs.call(self, args); + + const logFn = self.log || createDebug.log; + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = createDebug.enabled(namespace); + debug.useColors = createDebug.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; + debug.extend = extend; + // Debug.formatArgs = formatArgs; + // debug.rawLog = rawLog; + + // env-specific initialization logic for debug instances + if (typeof createDebug.init === 'function') { + createDebug.init(debug); + } + + createDebug.instances.push(debug); + + return debug; + } + + function destroy() { + const index = createDebug.instances.indexOf(this); + if (index !== -1) { + createDebug.instances.splice(index, 1); + return true; + } + return false; + } + + function extend(namespace, delimiter) { + const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); + newDebug.log = this.log; + return newDebug; + } + + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + function enable(namespaces) { + createDebug.save(namespaces); + + createDebug.names = []; + createDebug.skips = []; + + let i; + const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + const len = split.length; + + for (i = 0; i < len; i++) { + if (!split[i]) { + // ignore empty strings + continue; + } + + namespaces = split[i].replace(/\*/g, '.*?'); + + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')); + } + } + + for (i = 0; i < createDebug.instances.length; i++) { + const instance = createDebug.instances[i]; + instance.enabled = createDebug.enabled(instance.namespace); + } + } + + /** + * Disable debug output. + * + * @return {String} namespaces + * @api public + */ + function disable() { + const namespaces = [ + ...createDebug.names.map(toNamespace), + ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace) + ].join(','); + createDebug.enable(''); + return namespaces; + } + + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + + let i; + let len; + + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false; + } + } + + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true; + } + } + + return false; + } + + /** + * Convert regexp to namespace + * + * @param {RegExp} regxep + * @return {String} namespace + * @api private + */ + function toNamespace(regexp) { + return regexp.toString() + .substring(2, regexp.toString().length - 2) + .replace(/\.\*\?$/, '*'); + } + + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + return val; + } + + createDebug.enable(createDebug.load()); + + return createDebug; +} + +module.exports = setup; diff --git a/TaskServer/node_modules/debug/src/index.js b/TaskServer/node_modules/debug/src/index.js new file mode 100644 index 000000000..bf4c57f25 --- /dev/null +++ b/TaskServer/node_modules/debug/src/index.js @@ -0,0 +1,10 @@ +/** + * Detect Electron renderer / nwjs process, which is node, but we should + * treat as a browser. + */ + +if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) { + module.exports = require('./browser.js'); +} else { + module.exports = require('./node.js'); +} diff --git a/TaskServer/node_modules/debug/src/node.js b/TaskServer/node_modules/debug/src/node.js new file mode 100644 index 000000000..5e1f1541a --- /dev/null +++ b/TaskServer/node_modules/debug/src/node.js @@ -0,0 +1,257 @@ +/** + * Module dependencies. + */ + +const tty = require('tty'); +const util = require('util'); + +/** + * This is the Node.js implementation of `debug()`. + */ + +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +try { + // Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json) + // eslint-disable-next-line import/no-extraneous-dependencies + const supportsColor = require('supports-color'); + + if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) { + exports.colors = [ + 20, + 21, + 26, + 27, + 32, + 33, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 56, + 57, + 62, + 63, + 68, + 69, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 92, + 93, + 98, + 99, + 112, + 113, + 128, + 129, + 134, + 135, + 148, + 149, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 178, + 179, + 184, + 185, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 214, + 215, + 220, + 221 + ]; + } +} catch (error) { + // Swallow - we only care if `supports-color` is available; it doesn't have to be. +} + +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ + +exports.inspectOpts = Object.keys(process.env).filter(key => { + return /^debug_/i.test(key); +}).reduce((obj, key) => { + // Camel-case + const prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, (_, k) => { + return k.toUpperCase(); + }); + + // Coerce string value into JS value + let val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) { + val = true; + } else if (/^(no|off|false|disabled)$/i.test(val)) { + val = false; + } else if (val === 'null') { + val = null; + } else { + val = Number(val); + } + + obj[prop] = val; + return obj; +}, {}); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + return 'colors' in exports.inspectOpts ? + Boolean(exports.inspectOpts.colors) : + tty.isatty(process.stderr.fd); +} + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs(args) { + const {namespace: name, useColors} = this; + + if (useColors) { + const c = this.color; + const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c); + const prefix = ` ${colorCode};1m${name} \u001B[0m`; + + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m'); + } else { + args[0] = getDate() + name + ' ' + args[0]; + } +} + +function getDate() { + if (exports.inspectOpts.hideDate) { + return ''; + } + return new Date().toISOString() + ' '; +} + +/** + * Invokes `util.format()` with the specified arguments and writes to stderr. + */ + +function log(...args) { + return process.stderr.write(util.format(...args) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ +function save(namespaces) { + if (namespaces) { + process.env.DEBUG = namespaces; + } else { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ + +function init(debug) { + debug.inspectOpts = {}; + + const keys = Object.keys(exports.inspectOpts); + for (let i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } +} + +module.exports = require('./common')(exports); + +const {formatters} = module.exports; + +/** + * Map %o to `util.inspect()`, all on a single line. + */ + +formatters.o = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .replace(/\s*\n\s*/g, ' '); +}; + +/** + * Map %O to `util.inspect()`, allowing multiple lines if needed. + */ + +formatters.O = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; diff --git a/TaskServer/node_modules/dotenv/config.js b/TaskServer/node_modules/dotenv/config.js new file mode 100644 index 000000000..c7e6447fc --- /dev/null +++ b/TaskServer/node_modules/dotenv/config.js @@ -0,0 +1,11 @@ +/* @flow */ + +(function () { + require('./lib/main').config( + Object.assign( + {}, + require('./lib/env-options'), + require('./lib/cli-options')(process.argv) + ) + ) +})() diff --git a/TaskServer/node_modules/dotenv/lib/cli-options.js b/TaskServer/node_modules/dotenv/lib/cli-options.js new file mode 100644 index 000000000..ad69fabd9 --- /dev/null +++ b/TaskServer/node_modules/dotenv/lib/cli-options.js @@ -0,0 +1,13 @@ +/* @flow */ + +const re = /^dotenv_config_(encoding|path|debug)=(.+)$/ + +module.exports = function optionMatcher (args /*: Array */) { + return args.reduce(function (acc, cur) { + const matches = cur.match(re) + if (matches) { + acc[matches[1]] = matches[2] + } + return acc + }, {}) +} diff --git a/TaskServer/node_modules/dotenv/lib/env-options.js b/TaskServer/node_modules/dotenv/lib/env-options.js new file mode 100644 index 000000000..b4c4a6068 --- /dev/null +++ b/TaskServer/node_modules/dotenv/lib/env-options.js @@ -0,0 +1,18 @@ +/* @flow */ + +// ../config.js accepts options via environment variables +const options = {} + +if (process.env.DOTENV_CONFIG_ENCODING != null) { + options.encoding = process.env.DOTENV_CONFIG_ENCODING +} + +if (process.env.DOTENV_CONFIG_PATH != null) { + options.path = process.env.DOTENV_CONFIG_PATH +} + +if (process.env.DOTENV_CONFIG_DEBUG != null) { + options.debug = process.env.DOTENV_CONFIG_DEBUG +} + +module.exports = options diff --git a/TaskServer/node_modules/dotenv/lib/main.js b/TaskServer/node_modules/dotenv/lib/main.js new file mode 100644 index 000000000..e6c591c6b --- /dev/null +++ b/TaskServer/node_modules/dotenv/lib/main.js @@ -0,0 +1,113 @@ +/* @flow */ +/*:: + +type DotenvParseOptions = { + debug?: boolean +} + +// keys and values from src +type DotenvParseOutput = { [string]: string } + +type DotenvConfigOptions = { + path?: string, // path to .env file + encoding?: string, // encoding of .env file + debug?: string // turn on logging for debugging purposes +} + +type DotenvConfigOutput = { + parsed?: DotenvParseOutput, + error?: Error +} + +*/ + +const fs = require('fs') +const path = require('path') + +function log (message /*: string */) { + console.log(`[dotenv][DEBUG] ${message}`) +} + +const NEWLINE = '\n' +const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/ +const RE_NEWLINES = /\\n/g +const NEWLINES_MATCH = /\n|\r|\r\n/ + +// Parses src into an Object +function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ { + const debug = Boolean(options && options.debug) + const obj = {} + + // convert Buffers before splitting into lines and processing + src.toString().split(NEWLINES_MATCH).forEach(function (line, idx) { + // matching "KEY' and 'VAL' in 'KEY=VAL' + const keyValueArr = line.match(RE_INI_KEY_VAL) + // matched? + if (keyValueArr != null) { + const key = keyValueArr[1] + // default undefined or missing values to empty string + let val = (keyValueArr[2] || '') + const end = val.length - 1 + const isDoubleQuoted = val[0] === '"' && val[end] === '"' + const isSingleQuoted = val[0] === "'" && val[end] === "'" + + // if single or double quoted, remove quotes + if (isSingleQuoted || isDoubleQuoted) { + val = val.substring(1, end) + + // if double quoted, expand newlines + if (isDoubleQuoted) { + val = val.replace(RE_NEWLINES, NEWLINE) + } + } else { + // remove surrounding whitespace + val = val.trim() + } + + obj[key] = val + } else if (debug) { + log(`did not match key and value when parsing line ${idx + 1}: ${line}`) + } + }) + + return obj +} + +// Populates process.env from .env file +function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ { + let dotenvPath = path.resolve(process.cwd(), '.env') + let encoding /*: string */ = 'utf8' + let debug = false + + if (options) { + if (options.path != null) { + dotenvPath = options.path + } + if (options.encoding != null) { + encoding = options.encoding + } + if (options.debug != null) { + debug = true + } + } + + try { + // specifying an encoding returns a string instead of a buffer + const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug }) + + Object.keys(parsed).forEach(function (key) { + if (!Object.prototype.hasOwnProperty.call(process.env, key)) { + process.env[key] = parsed[key] + } else if (debug) { + log(`"${key}" is already defined in \`process.env\` and will not be overwritten`) + } + }) + + return { parsed } + } catch (e) { + return { error: e } + } +} + +module.exports.config = config +module.exports.parse = parse diff --git a/TaskServer/node_modules/dotenv/package.json b/TaskServer/node_modules/dotenv/package.json new file mode 100644 index 000000000..40e1c3ed6 --- /dev/null +++ b/TaskServer/node_modules/dotenv/package.json @@ -0,0 +1,80 @@ +{ + "_from": "dotenv@8.2.0", + "_id": "dotenv@8.2.0", + "_inBundle": false, + "_integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "_location": "/dotenv", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "dotenv@8.2.0", + "name": "dotenv", + "escapedName": "dotenv", + "rawSpec": "8.2.0", + "saveSpec": null, + "fetchSpec": "8.2.0" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "_shasum": "97e619259ada750eea3e4ea3e26bceea5424b16a", + "_spec": "dotenv@8.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "bugs": { + "url": "https://github.com/motdotla/dotenv/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Loads environment variables from .env file", + "devDependencies": { + "decache": "^4.5.1", + "dtslint": "^0.9.8", + "flow-bin": "^0.109.0", + "sinon": "^7.5.0", + "standard": "^13.1.0", + "standard-markdown": "^5.1.0", + "standard-version": "^7.0.0", + "tap": "^14.7.0" + }, + "engines": { + "node": ">=8" + }, + "homepage": "https://github.com/motdotla/dotenv#readme", + "keywords": [ + "dotenv", + "env", + ".env", + "environment", + "variables", + "config", + "settings" + ], + "license": "BSD-2-Clause", + "main": "lib/main.js", + "name": "dotenv", + "repository": { + "type": "git", + "url": "git://github.com/motdotla/dotenv.git" + }, + "scripts": { + "dtslint": "dtslint types", + "flow": "flow", + "lint": "standard", + "postlint": "standard-markdown", + "prerelease": "npm test", + "pretest": "npm run lint && npm run dtslint", + "release": "standard-version", + "test": "tap tests/*.js --100" + }, + "standard": { + "ignore": [ + "flow-typed/" + ] + }, + "types": "types", + "version": "8.2.0" +} diff --git a/TaskServer/node_modules/dotenv/types/tsconfig.json b/TaskServer/node_modules/dotenv/types/tsconfig.json new file mode 100644 index 000000000..012b05dd0 --- /dev/null +++ b/TaskServer/node_modules/dotenv/types/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es6"], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noEmit": true, + "baseUrl": ".", + "paths": { + "dotenv": ["."] + } + } +} diff --git a/TaskServer/node_modules/dotenv/types/tslint.json b/TaskServer/node_modules/dotenv/types/tslint.json new file mode 100644 index 000000000..c634f996e --- /dev/null +++ b/TaskServer/node_modules/dotenv/types/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "dtslint/dtslint.json", + "rules": { + "strict-export-declare-modifiers": false + } +} diff --git a/TaskServer/node_modules/easy-stack/es5.js b/TaskServer/node_modules/easy-stack/es5.js new file mode 100644 index 000000000..a756a7bbf --- /dev/null +++ b/TaskServer/node_modules/easy-stack/es5.js @@ -0,0 +1,74 @@ +function Stack(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToStack + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearStack + }, + contents:{ + enumerable:false, + get:getStack, + set:setStack + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var stack=[]; + var running=false; + var stop=false; + + function clearStack(){ + stack=[]; + return stack; + } + + function getStack(){ + return stack; + } + + function setStack(val){ + stack=val; + return stack; + } + + function addToStack(){ + for(var i in arguments){ + stack.unshift(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(stack.length<1 || this.stop){ + running=false; + return; + } + + stack.shift().bind(this)(); + } +} diff --git a/TaskServer/node_modules/easy-stack/example/basic.js b/TaskServer/node_modules/easy-stack/example/basic.js new file mode 100644 index 000000000..f0d7fbb11 --- /dev/null +++ b/TaskServer/node_modules/easy-stack/example/basic.js @@ -0,0 +1,18 @@ +var Stack=require('../stack'); +//create a new Stack instance +var stack=new Stack; +stack.autoRun=false; + +for(var i=0; i<50; i++){ + //add a bunch of stuff to the stack + stack.add(makeRequest.bind(stack,i)); +} + +stack.next(); + +function makeRequest(index){ + //do stuff + console.log(`making LIFO request ${index}`); + + this.next(); +} diff --git a/TaskServer/node_modules/easy-stack/package.json b/TaskServer/node_modules/easy-stack/package.json new file mode 100644 index 000000000..42a030529 --- /dev/null +++ b/TaskServer/node_modules/easy-stack/package.json @@ -0,0 +1,62 @@ +{ + "_args": [ + [ + "easy-stack@1.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer" + ] + ], + "_from": "easy-stack@1.0.0", + "_id": "easy-stack@1.0.0", + "_inBundle": false, + "_integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "_location": "/easy-stack", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "easy-stack@1.0.0", + "name": "easy-stack", + "escapedName": "easy-stack", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/js-queue" + ], + "_resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "_spec": "1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/easy-stack/issues" + }, + "description": "Simple JS stack with auto run for node and browsers", + "engines": { + "node": ">=6.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/easy-stack#readme", + "keywords": [ + "stack", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "stack.js", + "name": "easy-stack", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/easy-stack.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.0" +} diff --git a/TaskServer/node_modules/easy-stack/stack.js b/TaskServer/node_modules/easy-stack/stack.js new file mode 100644 index 000000000..bfadd1dee --- /dev/null +++ b/TaskServer/node_modules/easy-stack/stack.js @@ -0,0 +1,39 @@ +class Stack{ + constructor(){ + this.stack=[]; + this.autoRun=true; + this.running=false; + this.stop=false; + } + + clear(){ + this.stack=[]; + return this.stack; + } + + contents(val){ + if(val){ + this.stack=val; + } + return this.stack; + } + + add(...callbacks){ + this.stack.push(...callbacks); + if(!this.running && !this.stop && this.autoRun){ + this.next(); + } + } + + next(){ + this.running=true; + if(this.stack.length<1 || this.stop){ + this.running=false; + return; + } + + this.stack.pop().bind(this)(); + } +} + +module.exports=Stack; diff --git a/TaskServer/node_modules/event-pubsub/bower.json b/TaskServer/node_modules/event-pubsub/bower.json new file mode 100644 index 000000000..a207b0a5d --- /dev/null +++ b/TaskServer/node_modules/event-pubsub/bower.json @@ -0,0 +1,28 @@ +{ + "name": "event-pubsub", + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "main": "event-pubsub.js", + "authors": [ + "Brandon Nozaki Miller" + ], + "license": "DBAD", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "moduleType": [ + "globals", + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/TaskServer/node_modules/event-pubsub/es5.js b/TaskServer/node_modules/event-pubsub/es5.js new file mode 100644 index 000000000..7faf8b737 --- /dev/null +++ b/TaskServer/node_modules/event-pubsub/es5.js @@ -0,0 +1,143 @@ +'use strict'; + +function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} + +module.exports=EventPubSub; diff --git a/TaskServer/node_modules/event-pubsub/es6.js b/TaskServer/node_modules/event-pubsub/es6.js new file mode 100644 index 000000000..e9e859f9b --- /dev/null +++ b/TaskServer/node_modules/event-pubsub/es6.js @@ -0,0 +1,99 @@ +'use strict'; + +class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +module.exports = EventPubSub; diff --git a/TaskServer/node_modules/event-pubsub/event-pubsub-browser-es5.js b/TaskServer/node_modules/event-pubsub/event-pubsub-browser-es5.js new file mode 100644 index 000000000..03f27625e --- /dev/null +++ b/TaskServer/node_modules/event-pubsub/event-pubsub-browser-es5.js @@ -0,0 +1,141 @@ +'use strict'; + +window.EventPubSub=function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/TaskServer/node_modules/event-pubsub/event-pubsub-browser.js b/TaskServer/node_modules/event-pubsub/event-pubsub-browser.js new file mode 100644 index 000000000..d0bcd7b56 --- /dev/null +++ b/TaskServer/node_modules/event-pubsub/event-pubsub-browser.js @@ -0,0 +1,130 @@ +'use strict'; + +window.EventPubSub=class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/TaskServer/node_modules/event-pubsub/event-pubsub.js b/TaskServer/node_modules/event-pubsub/event-pubsub.js new file mode 100644 index 000000000..65bd02419 --- /dev/null +++ b/TaskServer/node_modules/event-pubsub/event-pubsub.js @@ -0,0 +1,8 @@ +'use strict'; + +let EventPubSub = require('./es5'); +if(process.version[1]>5){ + EventPubSub = require('./es6'); +} + +module.exports=EventPubSub; diff --git a/TaskServer/node_modules/event-pubsub/package.json b/TaskServer/node_modules/event-pubsub/package.json new file mode 100644 index 000000000..b5d342361 --- /dev/null +++ b/TaskServer/node_modules/event-pubsub/package.json @@ -0,0 +1,62 @@ +{ + "_args": [ + [ + "event-pubsub@4.3.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer" + ] + ], + "_from": "event-pubsub@4.3.0", + "_id": "event-pubsub@4.3.0", + "_inBundle": false, + "_integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "_location": "/event-pubsub", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "event-pubsub@4.3.0", + "name": "event-pubsub", + "escapedName": "event-pubsub", + "rawSpec": "4.3.0", + "saveSpec": null, + "fetchSpec": "4.3.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "_spec": "4.3.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/event-pubsub/issues" + }, + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "directories": { + "example": "examples" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "license": "Unlicense", + "main": "event-pubsub.js", + "name": "event-pubsub", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/event-pubsub.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "4.3.0" +} diff --git a/TaskServer/node_modules/js-message/Message.js b/TaskServer/node_modules/js-message/Message.js new file mode 100644 index 000000000..9a4698232 --- /dev/null +++ b/TaskServer/node_modules/js-message/Message.js @@ -0,0 +1,71 @@ +function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } +} + +module.exports=Message; diff --git a/TaskServer/node_modules/js-message/js-message-vanilla.js b/TaskServer/node_modules/js-message/js-message-vanilla.js new file mode 100644 index 000000000..0c49c2b6c --- /dev/null +++ b/TaskServer/node_modules/js-message/js-message-vanilla.js @@ -0,0 +1,75 @@ +( + function(){ + function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } + } + + window.message=Message; + } +)(); diff --git a/TaskServer/node_modules/js-message/package.json b/TaskServer/node_modules/js-message/package.json new file mode 100644 index 000000000..329078ca6 --- /dev/null +++ b/TaskServer/node_modules/js-message/package.json @@ -0,0 +1,84 @@ +{ + "_args": [ + [ + "js-message@1.0.5", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer" + ] + ], + "_from": "js-message@1.0.5", + "_id": "js-message@1.0.5", + "_inBundle": false, + "_integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "_location": "/js-message", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-message@1.0.5", + "name": "js-message", + "escapedName": "js-message", + "rawSpec": "1.0.5", + "saveSpec": null, + "fetchSpec": "1.0.5" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "_spec": "1.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-message/issues" + }, + "dependencies": {}, + "description": "normalized JS Object and JSON message and event protocol for node.js, vanialla js, react.js, components, actions, stores and dispatchers", + "devDependencies": {}, + "engines": { + "node": ">=0.6.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-message#readme", + "keywords": [ + "message", + "normalize", + "events", + "js", + "json", + "protocol", + "ipc", + "node", + "nodejs", + "node.js", + "react", + "react.js", + "reactjs", + "websocket", + "websockets", + "web", + "socket", + "sockets", + "ws", + "flux", + "reflux", + "component", + "components", + "store", + "stores", + "action", + "actions" + ], + "license": "DBAD", + "main": "Message.js", + "name": "js-message", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-message.git" + }, + "scripts": { + "start": "node devServer.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.5" +} diff --git a/TaskServer/node_modules/js-queue/package.json b/TaskServer/node_modules/js-queue/package.json new file mode 100644 index 000000000..d9f3ab07a --- /dev/null +++ b/TaskServer/node_modules/js-queue/package.json @@ -0,0 +1,65 @@ +{ + "_args": [ + [ + "js-queue@2.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer" + ] + ], + "_from": "js-queue@2.0.0", + "_id": "js-queue@2.0.0", + "_inBundle": false, + "_integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "_location": "/js-queue", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-queue@2.0.0", + "name": "js-queue", + "escapedName": "js-queue", + "rawSpec": "2.0.0", + "saveSpec": null, + "fetchSpec": "2.0.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "_spec": "2.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-queue/issues" + }, + "dependencies": { + "easy-stack": "^1.0.0" + }, + "description": "Simple JS queue with auto run for node and browsers", + "engines": { + "node": ">=1.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-queue#readme", + "keywords": [ + "queue", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "queue.js", + "name": "js-queue", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-queue.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "2.0.0" +} diff --git a/TaskServer/node_modules/js-queue/queue-vanilla.js b/TaskServer/node_modules/js-queue/queue-vanilla.js new file mode 100644 index 000000000..6f15495ae --- /dev/null +++ b/TaskServer/node_modules/js-queue/queue-vanilla.js @@ -0,0 +1,74 @@ +function Queue(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} diff --git a/TaskServer/node_modules/js-queue/queue.js b/TaskServer/node_modules/js-queue/queue.js new file mode 100644 index 000000000..a8698d7e9 --- /dev/null +++ b/TaskServer/node_modules/js-queue/queue.js @@ -0,0 +1,76 @@ +function Queue(asStack){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} + +module.exports=Queue; diff --git a/TaskServer/node_modules/js-queue/stack.js b/TaskServer/node_modules/js-queue/stack.js new file mode 100644 index 000000000..a9c25640e --- /dev/null +++ b/TaskServer/node_modules/js-queue/stack.js @@ -0,0 +1,3 @@ +const Stack=require('easy-stack'); + +module.exports = Stack; diff --git a/TaskServer/node_modules/minimist/example/parse.js b/TaskServer/node_modules/minimist/example/parse.js new file mode 100644 index 000000000..abff3e8ee --- /dev/null +++ b/TaskServer/node_modules/minimist/example/parse.js @@ -0,0 +1,2 @@ +var argv = require('../')(process.argv.slice(2)); +console.dir(argv); diff --git a/TaskServer/node_modules/minimist/index.js b/TaskServer/node_modules/minimist/index.js new file mode 100644 index 000000000..6a0559d58 --- /dev/null +++ b/TaskServer/node_modules/minimist/index.js @@ -0,0 +1,236 @@ +module.exports = function (args, opts) { + if (!opts) opts = {}; + + var flags = { bools : {}, strings : {}, unknownFn: null }; + + if (typeof opts['unknown'] === 'function') { + flags.unknownFn = opts['unknown']; + } + + if (typeof opts['boolean'] === 'boolean' && opts['boolean']) { + flags.allBools = true; + } else { + [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { + flags.bools[key] = true; + }); + } + + var aliases = {}; + Object.keys(opts.alias || {}).forEach(function (key) { + aliases[key] = [].concat(opts.alias[key]); + aliases[key].forEach(function (x) { + aliases[x] = [key].concat(aliases[key].filter(function (y) { + return x !== y; + })); + }); + }); + + [].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true; + if (aliases[key]) { + flags.strings[aliases[key]] = true; + } + }); + + var defaults = opts['default'] || {}; + + var argv = { _ : [] }; + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] === undefined ? false : defaults[key]); + }); + + var notFlags = []; + + if (args.indexOf('--') !== -1) { + notFlags = args.slice(args.indexOf('--')+1); + args = args.slice(0, args.indexOf('--')); + } + + function argDefined(key, arg) { + return (flags.allBools && /^--[^=]+$/.test(arg)) || + flags.strings[key] || flags.bools[key] || aliases[key]; + } + + function setArg (key, val, arg) { + if (arg && flags.unknownFn && !argDefined(key, arg)) { + if (flags.unknownFn(arg) === false) return; + } + + var value = !flags.strings[key] && isNumber(val) + ? Number(val) : val + ; + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), value); + }); + } + + function setKey (obj, keys, value) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + if (o[key] === undefined) o[key] = {}; + o = o[key]; + }); + + var key = keys[keys.length - 1]; + if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') { + o[key] = value; + } + else if (Array.isArray(o[key])) { + o[key].push(value); + } + else { + o[key] = [ o[key], value ]; + } + } + + function aliasIsBoolean(key) { + return aliases[key].some(function (x) { + return flags.bools[x]; + }); + } + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + if (/^--.+=/.test(arg)) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + var m = arg.match(/^--([^=]+)=([\s\S]*)$/); + var key = m[1]; + var value = m[2]; + if (flags.bools[key]) { + value = value !== 'false'; + } + setArg(key, value, arg); + } + else if (/^--no-.+/.test(arg)) { + var key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false, arg); + } + else if (/^--.+/.test(arg)) { + var key = arg.match(/^--(.+)/)[1]; + var next = args[i + 1]; + if (next !== undefined && !/^-/.test(next) + && !flags.bools[key] + && !flags.allBools + && (aliases[key] ? !aliasIsBoolean(key) : true)) { + setArg(key, next, arg); + i++; + } + else if (/^(true|false)$/.test(next)) { + setArg(key, next === 'true', arg); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true, arg); + } + } + else if (/^-[^-]+/.test(arg)) { + var letters = arg.slice(1,-1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + var next = arg.slice(j+2); + + if (next === '-') { + setArg(letters[j], next, arg) + continue; + } + + if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { + setArg(letters[j], next.split('=')[1], arg); + broken = true; + break; + } + + if (/[A-Za-z]/.test(letters[j]) + && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next, arg); + broken = true; + break; + } + + if (letters[j+1] && letters[j+1].match(/\W/)) { + setArg(letters[j], arg.slice(j+2), arg); + broken = true; + break; + } + else { + setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg); + } + } + + var key = arg.slice(-1)[0]; + if (!broken && key !== '-') { + if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) + && !flags.bools[key] + && (aliases[key] ? !aliasIsBoolean(key) : true)) { + setArg(key, args[i+1], arg); + i++; + } + else if (args[i+1] && /true|false/.test(args[i+1])) { + setArg(key, args[i+1] === 'true', arg); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true, arg); + } + } + } + else { + if (!flags.unknownFn || flags.unknownFn(arg) !== false) { + argv._.push( + flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) + ); + } + if (opts.stopEarly) { + argv._.push.apply(argv._, args.slice(i + 1)); + break; + } + } + } + + Object.keys(defaults).forEach(function (key) { + if (!hasKey(argv, key.split('.'))) { + setKey(argv, key.split('.'), defaults[key]); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), defaults[key]); + }); + } + }); + + if (opts['--']) { + argv['--'] = new Array(); + notFlags.forEach(function(key) { + argv['--'].push(key); + }); + } + else { + notFlags.forEach(function(key) { + argv._.push(key); + }); + } + + return argv; +}; + +function hasKey (obj, keys) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + o = (o[key] || {}); + }); + + var key = keys[keys.length - 1]; + return key in o; +} + +function isNumber (x) { + if (typeof x === 'number') return true; + if (/^0x[0-9a-f]+$/i.test(x)) return true; + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); +} + diff --git a/TaskServer/node_modules/minimist/package.json b/TaskServer/node_modules/minimist/package.json new file mode 100644 index 000000000..7e99e2076 --- /dev/null +++ b/TaskServer/node_modules/minimist/package.json @@ -0,0 +1,73 @@ +{ + "_from": "minimist@^1.2.0", + "_id": "minimist@1.2.0", + "_inBundle": false, + "_integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "_location": "/minimist", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "minimist@^1.2.0", + "name": "minimist", + "escapedName": "minimist", + "rawSpec": "^1.2.0", + "saveSpec": null, + "fetchSpec": "^1.2.0" + }, + "_requiredBy": [ + "/telegraf" + ], + "_resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "_shasum": "a35008b20f41383eec1fb914f4cd5df79a264284", + "_spec": "minimist@^1.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\telegraf", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bugs": { + "url": "https://github.com/substack/minimist/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "parse argument options", + "devDependencies": { + "covert": "^1.0.0", + "tap": "~0.4.0", + "tape": "^3.5.0" + }, + "homepage": "https://github.com/substack/minimist", + "keywords": [ + "argv", + "getopt", + "parser", + "optimist" + ], + "license": "MIT", + "main": "index.js", + "name": "minimist", + "repository": { + "type": "git", + "url": "git://github.com/substack/minimist.git" + }, + "scripts": { + "coverage": "covert test/*.js", + "test": "tap test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/6..latest", + "ff/5", + "firefox/latest", + "chrome/10", + "chrome/latest", + "safari/5.1", + "safari/latest", + "opera/12" + ] + }, + "version": "1.2.0" +} diff --git a/TaskServer/node_modules/minimist/test/all_bool.js b/TaskServer/node_modules/minimist/test/all_bool.js new file mode 100644 index 000000000..ac835483d --- /dev/null +++ b/TaskServer/node_modules/minimist/test/all_bool.js @@ -0,0 +1,32 @@ +var parse = require('../'); +var test = require('tape'); + +test('flag boolean true (default all --args to boolean)', function (t) { + var argv = parse(['moo', '--honk', 'cow'], { + boolean: true + }); + + t.deepEqual(argv, { + honk: true, + _: ['moo', 'cow'] + }); + + t.deepEqual(typeof argv.honk, 'boolean'); + t.end(); +}); + +test('flag boolean true only affects double hyphen arguments without equals signs', function (t) { + var argv = parse(['moo', '--honk', 'cow', '-p', '55', '--tacos=good'], { + boolean: true + }); + + t.deepEqual(argv, { + honk: true, + tacos: 'good', + p: 55, + _: ['moo', 'cow'] + }); + + t.deepEqual(typeof argv.honk, 'boolean'); + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/bool.js b/TaskServer/node_modules/minimist/test/bool.js new file mode 100644 index 000000000..14b0717ce --- /dev/null +++ b/TaskServer/node_modules/minimist/test/bool.js @@ -0,0 +1,166 @@ +var parse = require('../'); +var test = require('tape'); + +test('flag boolean default false', function (t) { + var argv = parse(['moo'], { + boolean: ['t', 'verbose'], + default: { verbose: false, t: false } + }); + + t.deepEqual(argv, { + verbose: false, + t: false, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); + +}); + +test('boolean groups', function (t) { + var argv = parse([ '-x', '-z', 'one', 'two', 'three' ], { + boolean: ['x','y','z'] + }); + + t.deepEqual(argv, { + x : true, + y : false, + z : true, + _ : [ 'one', 'two', 'three' ] + }); + + t.deepEqual(typeof argv.x, 'boolean'); + t.deepEqual(typeof argv.y, 'boolean'); + t.deepEqual(typeof argv.z, 'boolean'); + t.end(); +}); +test('boolean and alias with chainable api', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = parse(aliased, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var propertyArgv = parse(regular, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias with options hash', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + alias: { 'h': 'herp' }, + boolean: 'herp' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias array with options hash', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var alt = [ '--harp', 'derp' ]; + var opts = { + alias: { 'h': ['herp', 'harp'] }, + boolean: 'h' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var altPropertyArgv = parse(alt, opts); + var expected = { + harp: true, + herp: true, + h: true, + '_': [ 'derp' ] + }; + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.same(altPropertyArgv, expected); + t.end(); +}); + +test('boolean and alias using explicit true', function (t) { + var aliased = [ '-h', 'true' ]; + var regular = [ '--herp', 'true' ]; + var opts = { + alias: { h: 'herp' }, + boolean: 'h' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +// regression, see https://github.com/substack/node-optimist/issues/71 +test('boolean and --x=true', function(t) { + var parsed = parse(['--boool', '--other=true'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'true'); + + parsed = parse(['--boool', '--other=false'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'false'); + t.end(); +}); + +test('boolean --boool=true', function (t) { + var parsed = parse(['--boool=true'], { + default: { + boool: false + }, + boolean: ['boool'] + }); + + t.same(parsed.boool, true); + t.end(); +}); + +test('boolean --boool=false', function (t) { + var parsed = parse(['--boool=false'], { + default: { + boool: true + }, + boolean: ['boool'] + }); + + t.same(parsed.boool, false); + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/dash.js b/TaskServer/node_modules/minimist/test/dash.js new file mode 100644 index 000000000..5a4fa5be4 --- /dev/null +++ b/TaskServer/node_modules/minimist/test/dash.js @@ -0,0 +1,31 @@ +var parse = require('../'); +var test = require('tape'); + +test('-', function (t) { + t.plan(5); + t.deepEqual(parse([ '-n', '-' ]), { n: '-', _: [] }); + t.deepEqual(parse([ '-' ]), { _: [ '-' ] }); + t.deepEqual(parse([ '-f-' ]), { f: '-', _: [] }); + t.deepEqual( + parse([ '-b', '-' ], { boolean: 'b' }), + { b: true, _: [ '-' ] } + ); + t.deepEqual( + parse([ '-s', '-' ], { string: 's' }), + { s: '-', _: [] } + ); +}); + +test('-a -- b', function (t) { + t.plan(3); + t.deepEqual(parse([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); +}); + +test('move arguments after the -- into their own `--` array', function(t) { + t.plan(1); + t.deepEqual( + parse([ '--name', 'John', 'before', '--', 'after' ], { '--': true }), + { name: 'John', _: [ 'before' ], '--': [ 'after' ] }); +}); diff --git a/TaskServer/node_modules/minimist/test/default_bool.js b/TaskServer/node_modules/minimist/test/default_bool.js new file mode 100644 index 000000000..780a31127 --- /dev/null +++ b/TaskServer/node_modules/minimist/test/default_bool.js @@ -0,0 +1,35 @@ +var test = require('tape'); +var parse = require('../'); + +test('boolean default true', function (t) { + var argv = parse([], { + boolean: 'sometrue', + default: { sometrue: true } + }); + t.equal(argv.sometrue, true); + t.end(); +}); + +test('boolean default false', function (t) { + var argv = parse([], { + boolean: 'somefalse', + default: { somefalse: false } + }); + t.equal(argv.somefalse, false); + t.end(); +}); + +test('boolean default to null', function (t) { + var argv = parse([], { + boolean: 'maybe', + default: { maybe: null } + }); + t.equal(argv.maybe, null); + var argv = parse(['--maybe'], { + boolean: 'maybe', + default: { maybe: null } + }); + t.equal(argv.maybe, true); + t.end(); + +}) diff --git a/TaskServer/node_modules/minimist/test/dotted.js b/TaskServer/node_modules/minimist/test/dotted.js new file mode 100644 index 000000000..d8b3e856e --- /dev/null +++ b/TaskServer/node_modules/minimist/test/dotted.js @@ -0,0 +1,22 @@ +var parse = require('../'); +var test = require('tape'); + +test('dotted alias', function (t) { + var argv = parse(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 22); + t.equal(argv.aa.bb, 22); + t.end(); +}); + +test('dotted default', function (t) { + var argv = parse('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 11); + t.equal(argv.aa.bb, 11); + t.end(); +}); + +test('dotted default with no alias', function (t) { + var argv = parse('', {default: {'a.b': 11}}); + t.equal(argv.a.b, 11); + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/kv_short.js b/TaskServer/node_modules/minimist/test/kv_short.js new file mode 100644 index 000000000..f813b3050 --- /dev/null +++ b/TaskServer/node_modules/minimist/test/kv_short.js @@ -0,0 +1,16 @@ +var parse = require('../'); +var test = require('tape'); + +test('short -k=v' , function (t) { + t.plan(1); + + var argv = parse([ '-b=123' ]); + t.deepEqual(argv, { b: 123, _: [] }); +}); + +test('multi short -k=v' , function (t) { + t.plan(1); + + var argv = parse([ '-a=whatever', '-b=robots' ]); + t.deepEqual(argv, { a: 'whatever', b: 'robots', _: [] }); +}); diff --git a/TaskServer/node_modules/minimist/test/long.js b/TaskServer/node_modules/minimist/test/long.js new file mode 100644 index 000000000..5d3a1e09d --- /dev/null +++ b/TaskServer/node_modules/minimist/test/long.js @@ -0,0 +1,31 @@ +var test = require('tape'); +var parse = require('../'); + +test('long opts', function (t) { + t.deepEqual( + parse([ '--bool' ]), + { bool : true, _ : [] }, + 'long boolean' + ); + t.deepEqual( + parse([ '--pow', 'xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture sp' + ); + t.deepEqual( + parse([ '--pow=xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture eq' + ); + t.deepEqual( + parse([ '--host', 'localhost', '--port', '555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures sp' + ); + t.deepEqual( + parse([ '--host=localhost', '--port=555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures eq' + ); + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/num.js b/TaskServer/node_modules/minimist/test/num.js new file mode 100644 index 000000000..2cc77f4d6 --- /dev/null +++ b/TaskServer/node_modules/minimist/test/num.js @@ -0,0 +1,36 @@ +var parse = require('../'); +var test = require('tape'); + +test('nums', function (t) { + var argv = parse([ + '-x', '1234', + '-y', '5.67', + '-z', '1e7', + '-w', '10f', + '--hex', '0xdeadbeef', + '789' + ]); + t.deepEqual(argv, { + x : 1234, + y : 5.67, + z : 1e7, + w : '10f', + hex : 0xdeadbeef, + _ : [ 789 ] + }); + t.deepEqual(typeof argv.x, 'number'); + t.deepEqual(typeof argv.y, 'number'); + t.deepEqual(typeof argv.z, 'number'); + t.deepEqual(typeof argv.w, 'string'); + t.deepEqual(typeof argv.hex, 'number'); + t.deepEqual(typeof argv._[0], 'number'); + t.end(); +}); + +test('already a number', function (t) { + var argv = parse([ '-x', 1234, 789 ]); + t.deepEqual(argv, { x : 1234, _ : [ 789 ] }); + t.deepEqual(typeof argv.x, 'number'); + t.deepEqual(typeof argv._[0], 'number'); + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/parse.js b/TaskServer/node_modules/minimist/test/parse.js new file mode 100644 index 000000000..7b4a2a17c --- /dev/null +++ b/TaskServer/node_modules/minimist/test/parse.js @@ -0,0 +1,197 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse args', function (t) { + t.deepEqual( + parse([ '--no-moo' ]), + { moo : false, _ : [] }, + 'no' + ); + t.deepEqual( + parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]), + { v : ['a','b','c'], _ : [] }, + 'multi' + ); + t.end(); +}); + +test('comprehensive', function (t) { + t.deepEqual( + parse([ + '--name=meowmers', 'bare', '-cats', 'woo', + '-h', 'awesome', '--multi=quux', + '--key', 'value', + '-b', '--bool', '--no-meep', '--multi=baz', + '--', '--not-a-flag', 'eek' + ]), + { + c : true, + a : true, + t : true, + s : 'woo', + h : 'awesome', + b : true, + bool : true, + key : 'value', + multi : [ 'quux', 'baz' ], + meep : false, + name : 'meowmers', + _ : [ 'bare', '--not-a-flag', 'eek' ] + } + ); + t.end(); +}); + +test('flag boolean', function (t) { + var argv = parse([ '-t', 'moo' ], { boolean: 't' }); + t.deepEqual(argv, { t : true, _ : [ 'moo' ] }); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('flag boolean value', function (t) { + var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], { + boolean: [ 't', 'verbose' ], + default: { verbose: true } + }); + + t.deepEqual(argv, { + verbose: false, + t: true, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('newlines in params' , function (t) { + var args = parse([ '-s', "X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + + // reproduce in bash: + // VALUE="new + // line" + // node program.js --s="$VALUE" + args = parse([ "--s=X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + t.end(); +}); + +test('strings' , function (t) { + var s = parse([ '-s', '0001234' ], { string: 's' }).s; + t.equal(s, '0001234'); + t.equal(typeof s, 'string'); + + var x = parse([ '-x', '56' ], { string: 'x' }).x; + t.equal(x, '56'); + t.equal(typeof x, 'string'); + t.end(); +}); + +test('stringArgs', function (t) { + var s = parse([ ' ', ' ' ], { string: '_' })._; + t.same(s.length, 2); + t.same(typeof s[0], 'string'); + t.same(s[0], ' '); + t.same(typeof s[1], 'string'); + t.same(s[1], ' '); + t.end(); +}); + +test('empty strings', function(t) { + var s = parse([ '-s' ], { string: 's' }).s; + t.equal(s, ''); + t.equal(typeof s, 'string'); + + var str = parse([ '--str' ], { string: 'str' }).str; + t.equal(str, ''); + t.equal(typeof str, 'string'); + + var letters = parse([ '-art' ], { + string: [ 'a', 't' ] + }); + + t.equal(letters.a, ''); + t.equal(letters.r, true); + t.equal(letters.t, ''); + + t.end(); +}); + + +test('string and alias', function(t) { + var x = parse([ '--str', '000123' ], { + string: 's', + alias: { s: 'str' } + }); + + t.equal(x.str, '000123'); + t.equal(typeof x.str, 'string'); + t.equal(x.s, '000123'); + t.equal(typeof x.s, 'string'); + + var y = parse([ '-s', '000123' ], { + string: 'str', + alias: { str: 's' } + }); + + t.equal(y.str, '000123'); + t.equal(typeof y.str, 'string'); + t.equal(y.s, '000123'); + t.equal(typeof y.s, 'string'); + t.end(); +}); + +test('slashBreak', function (t) { + t.same( + parse([ '-I/foo/bar/baz' ]), + { I : '/foo/bar/baz', _ : [] } + ); + t.same( + parse([ '-xyz/foo/bar/baz' ]), + { x : true, y : true, z : '/foo/bar/baz', _ : [] } + ); + t.end(); +}); + +test('alias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: 'zoom' } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.f, 11); + t.end(); +}); + +test('multiAlias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: [ 'zm', 'zoom' ] } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.z, argv.zm); + t.equal(argv.f, 11); + t.end(); +}); + +test('nested dotted objects', function (t) { + var argv = parse([ + '--foo.bar', '3', '--foo.baz', '4', + '--foo.quux.quibble', '5', '--foo.quux.o_O', + '--beep.boop' + ]); + + t.same(argv.foo, { + bar : 3, + baz : 4, + quux : { + quibble : 5, + o_O : true + } + }); + t.same(argv.beep, { boop : true }); + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/parse_modified.js b/TaskServer/node_modules/minimist/test/parse_modified.js new file mode 100644 index 000000000..ab620dc5e --- /dev/null +++ b/TaskServer/node_modules/minimist/test/parse_modified.js @@ -0,0 +1,9 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse with modifier functions' , function (t) { + t.plan(1); + + var argv = parse([ '-b', '123' ], { boolean: 'b' }); + t.deepEqual(argv, { b: true, _: [123] }); +}); diff --git a/TaskServer/node_modules/minimist/test/short.js b/TaskServer/node_modules/minimist/test/short.js new file mode 100644 index 000000000..d513a1c25 --- /dev/null +++ b/TaskServer/node_modules/minimist/test/short.js @@ -0,0 +1,67 @@ +var parse = require('../'); +var test = require('tape'); + +test('numeric short args', function (t) { + t.plan(2); + t.deepEqual(parse([ '-n123' ]), { n: 123, _: [] }); + t.deepEqual( + parse([ '-123', '456' ]), + { 1: true, 2: true, 3: 456, _: [] } + ); +}); + +test('short', function (t) { + t.deepEqual( + parse([ '-b' ]), + { b : true, _ : [] }, + 'short boolean' + ); + t.deepEqual( + parse([ 'foo', 'bar', 'baz' ]), + { _ : [ 'foo', 'bar', 'baz' ] }, + 'bare' + ); + t.deepEqual( + parse([ '-cats' ]), + { c : true, a : true, t : true, s : true, _ : [] }, + 'group' + ); + t.deepEqual( + parse([ '-cats', 'meow' ]), + { c : true, a : true, t : true, s : 'meow', _ : [] }, + 'short group next' + ); + t.deepEqual( + parse([ '-h', 'localhost' ]), + { h : 'localhost', _ : [] }, + 'short capture' + ); + t.deepEqual( + parse([ '-h', 'localhost', '-p', '555' ]), + { h : 'localhost', p : 555, _ : [] }, + 'short captures' + ); + t.end(); +}); + +test('mixed short bool and capture', function (t) { + t.same( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); + +test('short and long', function (t) { + t.deepEqual( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/stop_early.js b/TaskServer/node_modules/minimist/test/stop_early.js new file mode 100644 index 000000000..bdf9fbcb0 --- /dev/null +++ b/TaskServer/node_modules/minimist/test/stop_early.js @@ -0,0 +1,15 @@ +var parse = require('../'); +var test = require('tape'); + +test('stops parsing on the first non-option when stopEarly is set', function (t) { + var argv = parse(['--aaa', 'bbb', 'ccc', '--ddd'], { + stopEarly: true + }); + + t.deepEqual(argv, { + aaa: 'bbb', + _: ['ccc', '--ddd'] + }); + + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/unknown.js b/TaskServer/node_modules/minimist/test/unknown.js new file mode 100644 index 000000000..462a36bdd --- /dev/null +++ b/TaskServer/node_modules/minimist/test/unknown.js @@ -0,0 +1,102 @@ +var parse = require('../'); +var test = require('tape'); + +test('boolean and alias is not unknown', function (t) { + var unknown = []; + function unknownFn(arg) { + unknown.push(arg); + return false; + } + var aliased = [ '-h', 'true', '--derp', 'true' ]; + var regular = [ '--herp', 'true', '-d', 'true' ]; + var opts = { + alias: { h: 'herp' }, + boolean: 'h', + unknown: unknownFn + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + + t.same(unknown, ['--derp', '-d']); + t.end(); +}); + +test('flag boolean true any double hyphen argument is not unknown', function (t) { + var unknown = []; + function unknownFn(arg) { + unknown.push(arg); + return false; + } + var argv = parse(['--honk', '--tacos=good', 'cow', '-p', '55'], { + boolean: true, + unknown: unknownFn + }); + t.same(unknown, ['--tacos=good', 'cow', '-p']); + t.same(argv, { + honk: true, + _: [] + }); + t.end(); +}); + +test('string and alias is not unknown', function (t) { + var unknown = []; + function unknownFn(arg) { + unknown.push(arg); + return false; + } + var aliased = [ '-h', 'hello', '--derp', 'goodbye' ]; + var regular = [ '--herp', 'hello', '-d', 'moon' ]; + var opts = { + alias: { h: 'herp' }, + string: 'h', + unknown: unknownFn + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + + t.same(unknown, ['--derp', '-d']); + t.end(); +}); + +test('default and alias is not unknown', function (t) { + var unknown = []; + function unknownFn(arg) { + unknown.push(arg); + return false; + } + var aliased = [ '-h', 'hello' ]; + var regular = [ '--herp', 'hello' ]; + var opts = { + default: { 'h': 'bar' }, + alias: { 'h': 'herp' }, + unknown: unknownFn + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + + t.same(unknown, []); + t.end(); + unknownFn(); // exercise fn for 100% coverage +}); + +test('value following -- is not unknown', function (t) { + var unknown = []; + function unknownFn(arg) { + unknown.push(arg); + return false; + } + var aliased = [ '--bad', '--', 'good', 'arg' ]; + var opts = { + '--': true, + unknown: unknownFn + }; + var argv = parse(aliased, opts); + + t.same(unknown, ['--bad']); + t.same(argv, { + '--': ['good', 'arg'], + '_': [] + }) + t.end(); +}); diff --git a/TaskServer/node_modules/minimist/test/whitespace.js b/TaskServer/node_modules/minimist/test/whitespace.js new file mode 100644 index 000000000..8a52a58ce --- /dev/null +++ b/TaskServer/node_modules/minimist/test/whitespace.js @@ -0,0 +1,8 @@ +var parse = require('../'); +var test = require('tape'); + +test('whitespace should be whitespace' , function (t) { + t.plan(1); + var x = parse([ '-x', '\t' ]).x; + t.equal(x, '\t'); +}); diff --git a/TaskServer/node_modules/module-alias/index.js b/TaskServer/node_modules/module-alias/index.js new file mode 100644 index 000000000..8f2e570dc --- /dev/null +++ b/TaskServer/node_modules/module-alias/index.js @@ -0,0 +1,224 @@ +'use strict' + +var BuiltinModule = require('module') + +// Guard against poorly mocked module constructors +var Module = module.constructor.length > 1 + ? module.constructor + : BuiltinModule + +var nodePath = require('path') + +var modulePaths = [] +var moduleAliases = {} +var moduleAliasNames = [] + +var oldNodeModulePaths = Module._nodeModulePaths +Module._nodeModulePaths = function (from) { + var paths = oldNodeModulePaths.call(this, from) + + // Only include the module path for top-level modules + // that were not installed: + if (from.indexOf('node_modules') === -1) { + paths = modulePaths.concat(paths) + } + + return paths +} + +var oldResolveFilename = Module._resolveFilename +Module._resolveFilename = function (request, parentModule, isMain, options) { + for (var i = moduleAliasNames.length; i-- > 0;) { + var alias = moduleAliasNames[i] + if (isPathMatchesAlias(request, alias)) { + var aliasTarget = moduleAliases[alias] + // Custom function handler + if (typeof moduleAliases[alias] === 'function') { + var fromPath = parentModule.filename + aliasTarget = moduleAliases[alias](fromPath, request, alias) + if (!aliasTarget || typeof aliasTarget !== 'string') { + throw new Error('[module-alias] Expecting custom handler function to return path.') + } + } + request = nodePath.join(aliasTarget, request.substr(alias.length)) + // Only use the first match + break + } + } + + return oldResolveFilename.call(this, request, parentModule, isMain, options) +} + +function isPathMatchesAlias (path, alias) { + // Matching /^alias(\/|$)/ + if (path.indexOf(alias) === 0) { + if (path.length === alias.length) return true + if (path[alias.length] === '/') return true + } + + return false +} + +function addPathHelper (path, targetArray) { + path = nodePath.normalize(path) + if (targetArray && targetArray.indexOf(path) === -1) { + targetArray.unshift(path) + } +} + +function removePathHelper (path, targetArray) { + if (targetArray) { + var index = targetArray.indexOf(path) + if (index !== -1) { + targetArray.splice(index, 1) + } + } +} + +function addPath (path) { + var parent + path = nodePath.normalize(path) + + if (modulePaths.indexOf(path) === -1) { + modulePaths.push(path) + // Enable the search path for the current top-level module + var mainModule = getMainModule() + if (mainModule) { + addPathHelper(path, mainModule.paths) + } + parent = module.parent + + // Also modify the paths of the module that was used to load the + // app-module-paths module and all of it's parents + while (parent && parent !== mainModule) { + addPathHelper(path, parent.paths) + parent = parent.parent + } + } +} + +function addAliases (aliases) { + for (var alias in aliases) { + addAlias(alias, aliases[alias]) + } +} + +function addAlias (alias, target) { + moduleAliases[alias] = target + // Cost of sorting is lower here than during resolution + moduleAliasNames = Object.keys(moduleAliases) + moduleAliasNames.sort() +} + +/** + * Reset any changes maded (resets all registered aliases + * and custom module directories) + * The function is undocumented and for testing purposes only + */ +function reset () { + var mainModule = getMainModule() + + // Reset all changes in paths caused by addPath function + modulePaths.forEach(function (path) { + if (mainModule) { + removePathHelper(path, mainModule.paths) + } + + // Delete from require.cache if the module has been required before. + // This is required for node >= 11 + Object.getOwnPropertyNames(require.cache).forEach(function (name) { + if (name.indexOf(path) !== -1) { + delete require.cache[name] + } + }) + + var parent = module.parent + while (parent && parent !== mainModule) { + removePathHelper(path, parent.paths) + parent = parent.parent + } + }) + + modulePaths = [] + moduleAliases = {} + moduleAliasNames = [] +} + +/** + * Import aliases from package.json + * @param {object} options + */ +function init (options) { + if (typeof options === 'string') { + options = { base: options } + } + + options = options || {} + + var candidatePackagePaths + if (options.base) { + candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))] + } else { + // There is probably 99% chance that the project root directory in located + // above the node_modules directory, + // Or that package.json is in the node process' current working directory (when + // running a package manager script, e.g. `yarn start` / `npm run start`) + candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()] + } + + var npmPackage + var base + for (var i in candidatePackagePaths) { + try { + base = candidatePackagePaths[i] + + npmPackage = require(nodePath.join(base, 'package.json')) + break + } catch (e) { + // noop + } + } + + if (typeof npmPackage !== 'object') { + var pathString = candidatePackagePaths.join(',\n') + throw new Error('Unable to find package.json in any of:\n[' + pathString + ']') + } + + // + // Import aliases + // + + var aliases = npmPackage._moduleAliases || {} + + for (var alias in aliases) { + if (aliases[alias][0] !== '/') { + aliases[alias] = nodePath.join(base, aliases[alias]) + } + } + + addAliases(aliases) + + // + // Register custom module directories (like node_modules) + // + + if (npmPackage._moduleDirectories instanceof Array) { + npmPackage._moduleDirectories.forEach(function (dir) { + if (dir === 'node_modules') return + + var modulePath = nodePath.join(base, dir) + addPath(modulePath) + }) + } +} + +function getMainModule () { + return require.main._simulateRepl ? undefined : require.main +} + +module.exports = init +module.exports.addPath = addPath +module.exports.addAlias = addAlias +module.exports.addAliases = addAliases +module.exports.isPathMatchesAlias = isPathMatchesAlias +module.exports.reset = reset diff --git a/TaskServer/node_modules/module-alias/package.json b/TaskServer/node_modules/module-alias/package.json new file mode 100644 index 000000000..c2d2a5eea --- /dev/null +++ b/TaskServer/node_modules/module-alias/package.json @@ -0,0 +1,76 @@ +{ + "_from": "module-alias@^2.2.2", + "_id": "module-alias@2.2.2", + "_inBundle": false, + "_integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==", + "_location": "/module-alias", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "module-alias@^2.2.2", + "name": "module-alias", + "escapedName": "module-alias", + "rawSpec": "^2.2.2", + "saveSpec": null, + "fetchSpec": "^2.2.2" + }, + "_requiredBy": [ + "/telegraf" + ], + "_resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", + "_shasum": "151cdcecc24e25739ff0aa6e51e1c5716974c0e0", + "_spec": "module-alias@^2.2.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\telegraf", + "author": { + "name": "Nick Gavrilov", + "email": "artnikpro@gmail.com" + }, + "bugs": { + "url": "https://github.com/ilearnio/module-alias/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Create aliases of directories and register custom module paths", + "devDependencies": { + "chai": "^3.5.0", + "hello-world-classic": "github:ilearnio/hello-world-classic", + "husky": "^3.0.2", + "mocha": "^2.4.5", + "semver": "^6.1.1", + "standard": "^12.0.1" + }, + "files": [ + "index.js", + "register.js", + "README.md", + "LICENSE" + ], + "homepage": "https://github.com/ilearnio/module-alias", + "husky": { + "hooks": { + "pre-push": "npm run test" + } + }, + "keywords": [ + "extend", + "modules", + "node", + "path", + "resolve" + ], + "license": "MIT", + "main": "index.js", + "name": "module-alias", + "repository": { + "type": "git", + "url": "git+https://github.com/ilearnio/module-alias.git" + }, + "scripts": { + "lint": "standard", + "test": "npm run lint && npm run testonly", + "testonly": "NODE_ENV=test mocha test/specs.js", + "testonly-watch": "NODE_ENV=test mocha -w test/specs.js" + }, + "version": "2.2.2" +} diff --git a/TaskServer/node_modules/module-alias/register.js b/TaskServer/node_modules/module-alias/register.js new file mode 100644 index 000000000..5b07ef549 --- /dev/null +++ b/TaskServer/node_modules/module-alias/register.js @@ -0,0 +1 @@ +require('.')() diff --git a/TaskServer/node_modules/ms/index.js b/TaskServer/node_modules/ms/index.js new file mode 100644 index 000000000..c4498bcc2 --- /dev/null +++ b/TaskServer/node_modules/ms/index.js @@ -0,0 +1,162 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var w = d * 7; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isFinite(val)) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'weeks': + case 'week': + case 'w': + return n * w; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return Math.round(ms / d) + 'd'; + } + if (msAbs >= h) { + return Math.round(ms / h) + 'h'; + } + if (msAbs >= m) { + return Math.round(ms / m) + 'm'; + } + if (msAbs >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day'); + } + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour'); + } + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute'); + } + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second'); + } + return ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); +} diff --git a/TaskServer/node_modules/ms/package.json b/TaskServer/node_modules/ms/package.json new file mode 100644 index 000000000..ad7908279 --- /dev/null +++ b/TaskServer/node_modules/ms/package.json @@ -0,0 +1,69 @@ +{ + "_from": "ms@^2.1.1", + "_id": "ms@2.1.2", + "_inBundle": false, + "_integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "_location": "/ms", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "ms@^2.1.1", + "name": "ms", + "escapedName": "ms", + "rawSpec": "^2.1.1", + "saveSpec": null, + "fetchSpec": "^2.1.1" + }, + "_requiredBy": [ + "/debug" + ], + "_resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "_shasum": "d09d1f357b443f493382a8eb3ccd183872ae6009", + "_spec": "ms@^2.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\debug", + "bugs": { + "url": "https://github.com/zeit/ms/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Tiny millisecond conversion utility", + "devDependencies": { + "eslint": "4.12.1", + "expect.js": "0.3.1", + "husky": "0.14.3", + "lint-staged": "5.0.0", + "mocha": "4.0.1" + }, + "eslintConfig": { + "extends": "eslint:recommended", + "env": { + "node": true, + "es6": true + } + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/zeit/ms#readme", + "license": "MIT", + "lint-staged": { + "*.js": [ + "npm run lint", + "prettier --single-quote --write", + "git add" + ] + }, + "main": "./index", + "name": "ms", + "repository": { + "type": "git", + "url": "git+https://github.com/zeit/ms.git" + }, + "scripts": { + "lint": "eslint lib/* bin/*", + "precommit": "lint-staged", + "test": "mocha tests.js" + }, + "version": "2.1.2" +} diff --git a/TaskServer/node_modules/node-fetch/browser.js b/TaskServer/node_modules/node-fetch/browser.js new file mode 100644 index 000000000..0ad5de004 --- /dev/null +++ b/TaskServer/node_modules/node-fetch/browser.js @@ -0,0 +1,23 @@ +"use strict"; + +// ref: https://github.com/tc39/proposal-global +var getGlobal = function () { + // the only reliable means to get the global object is + // `Function('return this')()` + // However, this causes CSP violations in Chrome apps. + if (typeof self !== 'undefined') { return self; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + throw new Error('unable to locate global object'); +} + +var global = getGlobal(); + +module.exports = exports = global.fetch; + +// Needed for TypeScript and Webpack. +exports.default = global.fetch.bind(global); + +exports.Headers = global.Headers; +exports.Request = global.Request; +exports.Response = global.Response; \ No newline at end of file diff --git a/TaskServer/node_modules/node-fetch/lib/index.es.js b/TaskServer/node_modules/node-fetch/lib/index.es.js new file mode 100644 index 000000000..37d022c91 --- /dev/null +++ b/TaskServer/node_modules/node-fetch/lib/index.es.js @@ -0,0 +1,1633 @@ +process.emitWarning("The .es.js file is deprecated. Use .mjs instead."); + +import Stream from 'stream'; +import http from 'http'; +import Url from 'url'; +import https from 'https'; +import zlib from 'zlib'; + +// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js + +// fix for "Readable" isn't a named export issue +const Readable = Stream.Readable; + +const BUFFER = Symbol('buffer'); +const TYPE = Symbol('type'); + +class Blob { + constructor() { + this[TYPE] = ''; + + const blobParts = arguments[0]; + const options = arguments[1]; + + const buffers = []; + let size = 0; + + if (blobParts) { + const a = blobParts; + const length = Number(a.length); + for (let i = 0; i < length; i++) { + const element = a[i]; + let buffer; + if (element instanceof Buffer) { + buffer = element; + } else if (ArrayBuffer.isView(element)) { + buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); + } else if (element instanceof ArrayBuffer) { + buffer = Buffer.from(element); + } else if (element instanceof Blob) { + buffer = element[BUFFER]; + } else { + buffer = Buffer.from(typeof element === 'string' ? element : String(element)); + } + size += buffer.length; + buffers.push(buffer); + } + } + + this[BUFFER] = Buffer.concat(buffers); + + let type = options && options.type !== undefined && String(options.type).toLowerCase(); + if (type && !/[^\u0020-\u007E]/.test(type)) { + this[TYPE] = type; + } + } + get size() { + return this[BUFFER].length; + } + get type() { + return this[TYPE]; + } + text() { + return Promise.resolve(this[BUFFER].toString()); + } + arrayBuffer() { + const buf = this[BUFFER]; + const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + return Promise.resolve(ab); + } + stream() { + const readable = new Readable(); + readable._read = function () {}; + readable.push(this[BUFFER]); + readable.push(null); + return readable; + } + toString() { + return '[object Blob]'; + } + slice() { + const size = this.size; + + const start = arguments[0]; + const end = arguments[1]; + let relativeStart, relativeEnd; + if (start === undefined) { + relativeStart = 0; + } else if (start < 0) { + relativeStart = Math.max(size + start, 0); + } else { + relativeStart = Math.min(start, size); + } + if (end === undefined) { + relativeEnd = size; + } else if (end < 0) { + relativeEnd = Math.max(size + end, 0); + } else { + relativeEnd = Math.min(end, size); + } + const span = Math.max(relativeEnd - relativeStart, 0); + + const buffer = this[BUFFER]; + const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); + const blob = new Blob([], { type: arguments[2] }); + blob[BUFFER] = slicedBuffer; + return blob; + } +} + +Object.defineProperties(Blob.prototype, { + size: { enumerable: true }, + type: { enumerable: true }, + slice: { enumerable: true } +}); + +Object.defineProperty(Blob.prototype, Symbol.toStringTag, { + value: 'Blob', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * fetch-error.js + * + * FetchError interface for operational errors + */ + +/** + * Create FetchError instance + * + * @param String message Error message for human + * @param String type Error type for machine + * @param String systemError For Node.js system error + * @return FetchError + */ +function FetchError(message, type, systemError) { + Error.call(this, message); + + this.message = message; + this.type = type; + + // when err.type is `system`, err.code contains system error code + if (systemError) { + this.code = this.errno = systemError.code; + } + + // hide custom error implementation details from end-users + Error.captureStackTrace(this, this.constructor); +} + +FetchError.prototype = Object.create(Error.prototype); +FetchError.prototype.constructor = FetchError; +FetchError.prototype.name = 'FetchError'; + +let convert; +try { + convert = require('encoding').convert; +} catch (e) {} + +const INTERNALS = Symbol('Body internals'); + +// fix an issue where "PassThrough" isn't a named export for node <10 +const PassThrough = Stream.PassThrough; + +/** + * Body mixin + * + * Ref: https://fetch.spec.whatwg.org/#body + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +function Body(body) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$size = _ref.size; + + let size = _ref$size === undefined ? 0 : _ref$size; + var _ref$timeout = _ref.timeout; + let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; + + if (body == null) { + // body is undefined or null + body = null; + } else if (isURLSearchParams(body)) { + // body is a URLSearchParams + body = Buffer.from(body.toString()); + } else if (isBlob(body)) ; else if (Buffer.isBuffer(body)) ; else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') { + // body is ArrayBuffer + body = Buffer.from(body); + } else if (ArrayBuffer.isView(body)) { + // body is ArrayBufferView + body = Buffer.from(body.buffer, body.byteOffset, body.byteLength); + } else if (body instanceof Stream) ; else { + // none of the above + // coerce to string then buffer + body = Buffer.from(String(body)); + } + this[INTERNALS] = { + body, + disturbed: false, + error: null + }; + this.size = size; + this.timeout = timeout; + + if (body instanceof Stream) { + body.on('error', function (err) { + const error = err.name === 'AbortError' ? err : new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err); + _this[INTERNALS].error = error; + }); + } +} + +Body.prototype = { + get body() { + return this[INTERNALS].body; + }, + + get bodyUsed() { + return this[INTERNALS].disturbed; + }, + + /** + * Decode response as ArrayBuffer + * + * @return Promise + */ + arrayBuffer() { + return consumeBody.call(this).then(function (buf) { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + }); + }, + + /** + * Return raw response as Blob + * + * @return Promise + */ + blob() { + let ct = this.headers && this.headers.get('content-type') || ''; + return consumeBody.call(this).then(function (buf) { + return Object.assign( + // Prevent copying + new Blob([], { + type: ct.toLowerCase() + }), { + [BUFFER]: buf + }); + }); + }, + + /** + * Decode response as json + * + * @return Promise + */ + json() { + var _this2 = this; + + return consumeBody.call(this).then(function (buffer) { + try { + return JSON.parse(buffer.toString()); + } catch (err) { + return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json')); + } + }); + }, + + /** + * Decode response as text + * + * @return Promise + */ + text() { + return consumeBody.call(this).then(function (buffer) { + return buffer.toString(); + }); + }, + + /** + * Decode response as buffer (non-spec api) + * + * @return Promise + */ + buffer() { + return consumeBody.call(this); + }, + + /** + * Decode response as text, while automatically detecting the encoding and + * trying to decode to UTF-8 (non-spec api) + * + * @return Promise + */ + textConverted() { + var _this3 = this; + + return consumeBody.call(this).then(function (buffer) { + return convertBody(buffer, _this3.headers); + }); + } +}; + +// In browsers, all properties are enumerable. +Object.defineProperties(Body.prototype, { + body: { enumerable: true }, + bodyUsed: { enumerable: true }, + arrayBuffer: { enumerable: true }, + blob: { enumerable: true }, + json: { enumerable: true }, + text: { enumerable: true } +}); + +Body.mixIn = function (proto) { + for (const name of Object.getOwnPropertyNames(Body.prototype)) { + // istanbul ignore else: future proof + if (!(name in proto)) { + const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); + Object.defineProperty(proto, name, desc); + } + } +}; + +/** + * Consume and convert an entire Body to a Buffer. + * + * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body + * + * @return Promise + */ +function consumeBody() { + var _this4 = this; + + if (this[INTERNALS].disturbed) { + return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`)); + } + + this[INTERNALS].disturbed = true; + + if (this[INTERNALS].error) { + return Body.Promise.reject(this[INTERNALS].error); + } + + let body = this.body; + + // body is null + if (body === null) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is blob + if (isBlob(body)) { + body = body.stream(); + } + + // body is buffer + if (Buffer.isBuffer(body)) { + return Body.Promise.resolve(body); + } + + // istanbul ignore if: should never happen + if (!(body instanceof Stream)) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is stream + // get ready to actually consume the body + let accum = []; + let accumBytes = 0; + let abort = false; + + return new Body.Promise(function (resolve, reject) { + let resTimeout; + + // allow timeout on slow response body + if (_this4.timeout) { + resTimeout = setTimeout(function () { + abort = true; + reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout')); + }, _this4.timeout); + } + + // handle stream errors + body.on('error', function (err) { + if (err.name === 'AbortError') { + // if the request was aborted, reject with this Error + abort = true; + reject(err); + } else { + // other errors, such as incorrect content-encoding + reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + + body.on('data', function (chunk) { + if (abort || chunk === null) { + return; + } + + if (_this4.size && accumBytes + chunk.length > _this4.size) { + abort = true; + reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size')); + return; + } + + accumBytes += chunk.length; + accum.push(chunk); + }); + + body.on('end', function () { + if (abort) { + return; + } + + clearTimeout(resTimeout); + + try { + resolve(Buffer.concat(accum, accumBytes)); + } catch (err) { + // handle streams that have accumulated too much data (issue #414) + reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + }); +} + +/** + * Detect buffer encoding and convert to target encoding + * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding + * + * @param Buffer buffer Incoming buffer + * @param String encoding Target encoding + * @return String + */ +function convertBody(buffer, headers) { + if (typeof convert !== 'function') { + throw new Error('The package `encoding` must be installed to use the textConverted() function'); + } + + const ct = headers.get('content-type'); + let charset = 'utf-8'; + let res, str; + + // header + if (ct) { + res = /charset=([^;]*)/i.exec(ct); + } + + // no charset in content type, peek at response body for at most 1024 bytes + str = buffer.slice(0, 1024).toString(); + + // html5 + if (!res && str) { + res = / 0 && arguments[0] !== undefined ? arguments[0] : undefined; + + this[MAP] = Object.create(null); + + if (init instanceof Headers) { + const rawHeaders = init.raw(); + const headerNames = Object.keys(rawHeaders); + + for (const headerName of headerNames) { + for (const value of rawHeaders[headerName]) { + this.append(headerName, value); + } + } + + return; + } + + // We don't worry about converting prop to ByteString here as append() + // will handle it. + if (init == null) ; else if (typeof init === 'object') { + const method = init[Symbol.iterator]; + if (method != null) { + if (typeof method !== 'function') { + throw new TypeError('Header pairs must be iterable'); + } + + // sequence> + // Note: per spec we have to first exhaust the lists then process them + const pairs = []; + for (const pair of init) { + if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { + throw new TypeError('Each header pair must be iterable'); + } + pairs.push(Array.from(pair)); + } + + for (const pair of pairs) { + if (pair.length !== 2) { + throw new TypeError('Each header pair must be a name/value tuple'); + } + this.append(pair[0], pair[1]); + } + } else { + // record + for (const key of Object.keys(init)) { + const value = init[key]; + this.append(key, value); + } + } + } else { + throw new TypeError('Provided initializer must be an object'); + } + } + + /** + * Return combined header value given name + * + * @param String name Header name + * @return Mixed + */ + get(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key === undefined) { + return null; + } + + return this[MAP][key].join(', '); + } + + /** + * Iterate over all headers + * + * @param Function callback Executed for each item with parameters (value, name, thisArg) + * @param Boolean thisArg `this` context for callback function + * @return Void + */ + forEach(callback) { + let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; + + let pairs = getHeaders(this); + let i = 0; + while (i < pairs.length) { + var _pairs$i = pairs[i]; + const name = _pairs$i[0], + value = _pairs$i[1]; + + callback.call(thisArg, value, name, this); + pairs = getHeaders(this); + i++; + } + } + + /** + * Overwrite header values given name + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + set(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + this[MAP][key !== undefined ? key : name] = [value]; + } + + /** + * Append a value onto existing header + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + append(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + if (key !== undefined) { + this[MAP][key].push(value); + } else { + this[MAP][name] = [value]; + } + } + + /** + * Check for header name existence + * + * @param String name Header name + * @return Boolean + */ + has(name) { + name = `${name}`; + validateName(name); + return find(this[MAP], name) !== undefined; + } + + /** + * Delete all header values given name + * + * @param String name Header name + * @return Void + */ + delete(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key !== undefined) { + delete this[MAP][key]; + } + } + + /** + * Return raw headers (non-spec api) + * + * @return Object + */ + raw() { + return this[MAP]; + } + + /** + * Get an iterator on keys. + * + * @return Iterator + */ + keys() { + return createHeadersIterator(this, 'key'); + } + + /** + * Get an iterator on values. + * + * @return Iterator + */ + values() { + return createHeadersIterator(this, 'value'); + } + + /** + * Get an iterator on entries. + * + * This is the default iterator of the Headers object. + * + * @return Iterator + */ + [Symbol.iterator]() { + return createHeadersIterator(this, 'key+value'); + } +} +Headers.prototype.entries = Headers.prototype[Symbol.iterator]; + +Object.defineProperty(Headers.prototype, Symbol.toStringTag, { + value: 'Headers', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Headers.prototype, { + get: { enumerable: true }, + forEach: { enumerable: true }, + set: { enumerable: true }, + append: { enumerable: true }, + has: { enumerable: true }, + delete: { enumerable: true }, + keys: { enumerable: true }, + values: { enumerable: true }, + entries: { enumerable: true } +}); + +function getHeaders(headers) { + let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value'; + + const keys = Object.keys(headers[MAP]).sort(); + return keys.map(kind === 'key' ? function (k) { + return k.toLowerCase(); + } : kind === 'value' ? function (k) { + return headers[MAP][k].join(', '); + } : function (k) { + return [k.toLowerCase(), headers[MAP][k].join(', ')]; + }); +} + +const INTERNAL = Symbol('internal'); + +function createHeadersIterator(target, kind) { + const iterator = Object.create(HeadersIteratorPrototype); + iterator[INTERNAL] = { + target, + kind, + index: 0 + }; + return iterator; +} + +const HeadersIteratorPrototype = Object.setPrototypeOf({ + next() { + // istanbul ignore if + if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { + throw new TypeError('Value of `this` is not a HeadersIterator'); + } + + var _INTERNAL = this[INTERNAL]; + const target = _INTERNAL.target, + kind = _INTERNAL.kind, + index = _INTERNAL.index; + + const values = getHeaders(target, kind); + const len = values.length; + if (index >= len) { + return { + value: undefined, + done: true + }; + } + + this[INTERNAL].index = index + 1; + + return { + value: values[index], + done: false + }; + } +}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); + +Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { + value: 'HeadersIterator', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * Export the Headers object in a form that Node.js can consume. + * + * @param Headers headers + * @return Object + */ +function exportNodeCompatibleHeaders(headers) { + const obj = Object.assign({ __proto__: null }, headers[MAP]); + + // http.request() only supports string as Host header. This hack makes + // specifying custom Host header possible. + const hostHeaderKey = find(headers[MAP], 'Host'); + if (hostHeaderKey !== undefined) { + obj[hostHeaderKey] = obj[hostHeaderKey][0]; + } + + return obj; +} + +/** + * Create a Headers object from an object of headers, ignoring those that do + * not conform to HTTP grammar productions. + * + * @param Object obj Object of headers + * @return Headers + */ +function createHeadersLenient(obj) { + const headers = new Headers(); + for (const name of Object.keys(obj)) { + if (invalidTokenRegex.test(name)) { + continue; + } + if (Array.isArray(obj[name])) { + for (const val of obj[name]) { + if (invalidHeaderCharRegex.test(val)) { + continue; + } + if (headers[MAP][name] === undefined) { + headers[MAP][name] = [val]; + } else { + headers[MAP][name].push(val); + } + } + } else if (!invalidHeaderCharRegex.test(obj[name])) { + headers[MAP][name] = [obj[name]]; + } + } + return headers; +} + +const INTERNALS$1 = Symbol('Response internals'); + +// fix an issue where "STATUS_CODES" aren't a named export for node <10 +const STATUS_CODES = http.STATUS_CODES; + +/** + * Response class + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +class Response { + constructor() { + let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + Body.call(this, body, opts); + + const status = opts.status || 200; + const headers = new Headers(opts.headers); + + if (body != null && !headers.has('Content-Type')) { + const contentType = extractContentType(body); + if (contentType) { + headers.append('Content-Type', contentType); + } + } + + this[INTERNALS$1] = { + url: opts.url, + status, + statusText: opts.statusText || STATUS_CODES[status], + headers, + counter: opts.counter + }; + } + + get url() { + return this[INTERNALS$1].url || ''; + } + + get status() { + return this[INTERNALS$1].status; + } + + /** + * Convenience property representing if the request ended normally + */ + get ok() { + return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300; + } + + get redirected() { + return this[INTERNALS$1].counter > 0; + } + + get statusText() { + return this[INTERNALS$1].statusText; + } + + get headers() { + return this[INTERNALS$1].headers; + } + + /** + * Clone this response + * + * @return Response + */ + clone() { + return new Response(clone(this), { + url: this.url, + status: this.status, + statusText: this.statusText, + headers: this.headers, + ok: this.ok, + redirected: this.redirected + }); + } +} + +Body.mixIn(Response.prototype); + +Object.defineProperties(Response.prototype, { + url: { enumerable: true }, + status: { enumerable: true }, + ok: { enumerable: true }, + redirected: { enumerable: true }, + statusText: { enumerable: true }, + headers: { enumerable: true }, + clone: { enumerable: true } +}); + +Object.defineProperty(Response.prototype, Symbol.toStringTag, { + value: 'Response', + writable: false, + enumerable: false, + configurable: true +}); + +const INTERNALS$2 = Symbol('Request internals'); + +// fix an issue where "format", "parse" aren't a named export for node <10 +const parse_url = Url.parse; +const format_url = Url.format; + +const streamDestructionSupported = 'destroy' in Stream.Readable.prototype; + +/** + * Check if a value is an instance of Request. + * + * @param Mixed input + * @return Boolean + */ +function isRequest(input) { + return typeof input === 'object' && typeof input[INTERNALS$2] === 'object'; +} + +function isAbortSignal(signal) { + const proto = signal && typeof signal === 'object' && Object.getPrototypeOf(signal); + return !!(proto && proto.constructor.name === 'AbortSignal'); +} + +/** + * Request class + * + * @param Mixed input Url or Request instance + * @param Object init Custom options + * @return Void + */ +class Request { + constructor(input) { + let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + let parsedURL; + + // normalize input + if (!isRequest(input)) { + if (input && input.href) { + // in order to support Node.js' Url objects; though WHATWG's URL objects + // will fall into this branch also (since their `toString()` will return + // `href` property anyway) + parsedURL = parse_url(input.href); + } else { + // coerce input to a string before attempting to parse + parsedURL = parse_url(`${input}`); + } + input = {}; + } else { + parsedURL = parse_url(input.url); + } + + let method = init.method || input.method || 'GET'; + method = method.toUpperCase(); + + if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) { + throw new TypeError('Request with GET/HEAD method cannot have body'); + } + + let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null; + + Body.call(this, inputBody, { + timeout: init.timeout || input.timeout || 0, + size: init.size || input.size || 0 + }); + + const headers = new Headers(init.headers || input.headers || {}); + + if (inputBody != null && !headers.has('Content-Type')) { + const contentType = extractContentType(inputBody); + if (contentType) { + headers.append('Content-Type', contentType); + } + } + + let signal = isRequest(input) ? input.signal : null; + if ('signal' in init) signal = init.signal; + + if (signal != null && !isAbortSignal(signal)) { + throw new TypeError('Expected signal to be an instanceof AbortSignal'); + } + + this[INTERNALS$2] = { + method, + redirect: init.redirect || input.redirect || 'follow', + headers, + parsedURL, + signal + }; + + // node-fetch-only options + this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20; + this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true; + this.counter = init.counter || input.counter || 0; + this.agent = init.agent || input.agent; + } + + get method() { + return this[INTERNALS$2].method; + } + + get url() { + return format_url(this[INTERNALS$2].parsedURL); + } + + get headers() { + return this[INTERNALS$2].headers; + } + + get redirect() { + return this[INTERNALS$2].redirect; + } + + get signal() { + return this[INTERNALS$2].signal; + } + + /** + * Clone this request + * + * @return Request + */ + clone() { + return new Request(this); + } +} + +Body.mixIn(Request.prototype); + +Object.defineProperty(Request.prototype, Symbol.toStringTag, { + value: 'Request', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Request.prototype, { + method: { enumerable: true }, + url: { enumerable: true }, + headers: { enumerable: true }, + redirect: { enumerable: true }, + clone: { enumerable: true }, + signal: { enumerable: true } +}); + +/** + * Convert a Request to Node.js http request options. + * + * @param Request A Request instance + * @return Object The options object to be passed to http.request + */ +function getNodeRequestOptions(request) { + const parsedURL = request[INTERNALS$2].parsedURL; + const headers = new Headers(request[INTERNALS$2].headers); + + // fetch step 1.3 + if (!headers.has('Accept')) { + headers.set('Accept', '*/*'); + } + + // Basic fetch + if (!parsedURL.protocol || !parsedURL.hostname) { + throw new TypeError('Only absolute URLs are supported'); + } + + if (!/^https?:$/.test(parsedURL.protocol)) { + throw new TypeError('Only HTTP(S) protocols are supported'); + } + + if (request.signal && request.body instanceof Stream.Readable && !streamDestructionSupported) { + throw new Error('Cancellation of streamed requests with AbortSignal is not supported in node < 8'); + } + + // HTTP-network-or-cache fetch steps 2.4-2.7 + let contentLengthValue = null; + if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { + contentLengthValue = '0'; + } + if (request.body != null) { + const totalBytes = getTotalBytes(request); + if (typeof totalBytes === 'number') { + contentLengthValue = String(totalBytes); + } + } + if (contentLengthValue) { + headers.set('Content-Length', contentLengthValue); + } + + // HTTP-network-or-cache fetch step 2.11 + if (!headers.has('User-Agent')) { + headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)'); + } + + // HTTP-network-or-cache fetch step 2.15 + if (request.compress && !headers.has('Accept-Encoding')) { + headers.set('Accept-Encoding', 'gzip,deflate'); + } + + let agent = request.agent; + if (typeof agent === 'function') { + agent = agent(parsedURL); + } + + if (!headers.has('Connection') && !agent) { + headers.set('Connection', 'close'); + } + + // HTTP-network fetch step 4.2 + // chunked encoding is handled by Node.js + + return Object.assign({}, parsedURL, { + method: request.method, + headers: exportNodeCompatibleHeaders(headers), + agent + }); +} + +/** + * abort-error.js + * + * AbortError interface for cancelled requests + */ + +/** + * Create AbortError instance + * + * @param String message Error message for human + * @return AbortError + */ +function AbortError(message) { + Error.call(this, message); + + this.type = 'aborted'; + this.message = message; + + // hide custom error implementation details from end-users + Error.captureStackTrace(this, this.constructor); +} + +AbortError.prototype = Object.create(Error.prototype); +AbortError.prototype.constructor = AbortError; +AbortError.prototype.name = 'AbortError'; + +// fix an issue where "PassThrough", "resolve" aren't a named export for node <10 +const PassThrough$1 = Stream.PassThrough; +const resolve_url = Url.resolve; + +/** + * Fetch function + * + * @param Mixed url Absolute url or Request instance + * @param Object opts Fetch options + * @return Promise + */ +function fetch(url, opts) { + + // allow custom promise + if (!fetch.Promise) { + throw new Error('native promise missing, set fetch.Promise to your favorite alternative'); + } + + Body.Promise = fetch.Promise; + + // wrap http.request into fetch + return new fetch.Promise(function (resolve, reject) { + // build request object + const request = new Request(url, opts); + const options = getNodeRequestOptions(request); + + const send = (options.protocol === 'https:' ? https : http).request; + const signal = request.signal; + + let response = null; + + const abort = function abort() { + let error = new AbortError('The user aborted a request.'); + reject(error); + if (request.body && request.body instanceof Stream.Readable) { + request.body.destroy(error); + } + if (!response || !response.body) return; + response.body.emit('error', error); + }; + + if (signal && signal.aborted) { + abort(); + return; + } + + const abortAndFinalize = function abortAndFinalize() { + abort(); + finalize(); + }; + + // send request + const req = send(options); + let reqTimeout; + + if (signal) { + signal.addEventListener('abort', abortAndFinalize); + } + + function finalize() { + req.abort(); + if (signal) signal.removeEventListener('abort', abortAndFinalize); + clearTimeout(reqTimeout); + } + + if (request.timeout) { + req.once('socket', function (socket) { + reqTimeout = setTimeout(function () { + reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); + finalize(); + }, request.timeout); + }); + } + + req.on('error', function (err) { + reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); + finalize(); + }); + + req.on('response', function (res) { + clearTimeout(reqTimeout); + + const headers = createHeadersLenient(res.headers); + + // HTTP fetch step 5 + if (fetch.isRedirect(res.statusCode)) { + // HTTP fetch step 5.2 + const location = headers.get('Location'); + + // HTTP fetch step 5.3 + const locationURL = location === null ? null : resolve_url(request.url, location); + + // HTTP fetch step 5.5 + switch (request.redirect) { + case 'error': + reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); + finalize(); + return; + case 'manual': + // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL. + if (locationURL !== null) { + // handle corrupted header + try { + headers.set('Location', locationURL); + } catch (err) { + // istanbul ignore next: nodejs server prevent invalid response headers, we can't test this through normal request + reject(err); + } + } + break; + case 'follow': + // HTTP-redirect fetch step 2 + if (locationURL === null) { + break; + } + + // HTTP-redirect fetch step 5 + if (request.counter >= request.follow) { + reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 6 (counter increment) + // Create a new Request object. + const requestOpts = { + headers: new Headers(request.headers), + follow: request.follow, + counter: request.counter + 1, + agent: request.agent, + compress: request.compress, + method: request.method, + body: request.body, + signal: request.signal, + timeout: request.timeout + }; + + // HTTP-redirect fetch step 9 + if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { + reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 11 + if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { + requestOpts.method = 'GET'; + requestOpts.body = undefined; + requestOpts.headers.delete('content-length'); + } + + // HTTP-redirect fetch step 15 + resolve(fetch(new Request(locationURL, requestOpts))); + finalize(); + return; + } + } + + // prepare response + res.once('end', function () { + if (signal) signal.removeEventListener('abort', abortAndFinalize); + }); + let body = res.pipe(new PassThrough$1()); + + const response_options = { + url: request.url, + status: res.statusCode, + statusText: res.statusMessage, + headers: headers, + size: request.size, + timeout: request.timeout, + counter: request.counter + }; + + // HTTP-network fetch step 12.1.1.3 + const codings = headers.get('Content-Encoding'); + + // HTTP-network fetch step 12.1.1.4: handle content codings + + // in following scenarios we ignore compression support + // 1. compression support is disabled + // 2. HEAD request + // 3. no Content-Encoding header + // 4. no content response (204) + // 5. content not modified response (304) + if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { + response = new Response(body, response_options); + resolve(response); + return; + } + + // For Node v6+ + // Be less strict when decoding compressed responses, since sometimes + // servers send slightly invalid responses that are still accepted + // by common browsers. + // Always using Z_SYNC_FLUSH is what cURL does. + const zlibOptions = { + flush: zlib.Z_SYNC_FLUSH, + finishFlush: zlib.Z_SYNC_FLUSH + }; + + // for gzip + if (codings == 'gzip' || codings == 'x-gzip') { + body = body.pipe(zlib.createGunzip(zlibOptions)); + response = new Response(body, response_options); + resolve(response); + return; + } + + // for deflate + if (codings == 'deflate' || codings == 'x-deflate') { + // handle the infamous raw deflate response from old servers + // a hack for old IIS and Apache servers + const raw = res.pipe(new PassThrough$1()); + raw.once('data', function (chunk) { + // see http://stackoverflow.com/questions/37519828 + if ((chunk[0] & 0x0F) === 0x08) { + body = body.pipe(zlib.createInflate()); + } else { + body = body.pipe(zlib.createInflateRaw()); + } + response = new Response(body, response_options); + resolve(response); + }); + return; + } + + // for br + if (codings == 'br' && typeof zlib.createBrotliDecompress === 'function') { + body = body.pipe(zlib.createBrotliDecompress()); + response = new Response(body, response_options); + resolve(response); + return; + } + + // otherwise, use response as-is + response = new Response(body, response_options); + resolve(response); + }); + + writeToStream(req, request); + }); +} +/** + * Redirect code matching + * + * @param Number code Status code + * @return Boolean + */ +fetch.isRedirect = function (code) { + return code === 301 || code === 302 || code === 303 || code === 307 || code === 308; +}; + +// expose Promise +fetch.Promise = global.Promise; + +export default fetch; +export { Headers, Request, Response, FetchError }; diff --git a/TaskServer/node_modules/node-fetch/lib/index.js b/TaskServer/node_modules/node-fetch/lib/index.js new file mode 100644 index 000000000..daa44bcaf --- /dev/null +++ b/TaskServer/node_modules/node-fetch/lib/index.js @@ -0,0 +1,1642 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var Stream = _interopDefault(require('stream')); +var http = _interopDefault(require('http')); +var Url = _interopDefault(require('url')); +var https = _interopDefault(require('https')); +var zlib = _interopDefault(require('zlib')); + +// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js + +// fix for "Readable" isn't a named export issue +const Readable = Stream.Readable; + +const BUFFER = Symbol('buffer'); +const TYPE = Symbol('type'); + +class Blob { + constructor() { + this[TYPE] = ''; + + const blobParts = arguments[0]; + const options = arguments[1]; + + const buffers = []; + let size = 0; + + if (blobParts) { + const a = blobParts; + const length = Number(a.length); + for (let i = 0; i < length; i++) { + const element = a[i]; + let buffer; + if (element instanceof Buffer) { + buffer = element; + } else if (ArrayBuffer.isView(element)) { + buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); + } else if (element instanceof ArrayBuffer) { + buffer = Buffer.from(element); + } else if (element instanceof Blob) { + buffer = element[BUFFER]; + } else { + buffer = Buffer.from(typeof element === 'string' ? element : String(element)); + } + size += buffer.length; + buffers.push(buffer); + } + } + + this[BUFFER] = Buffer.concat(buffers); + + let type = options && options.type !== undefined && String(options.type).toLowerCase(); + if (type && !/[^\u0020-\u007E]/.test(type)) { + this[TYPE] = type; + } + } + get size() { + return this[BUFFER].length; + } + get type() { + return this[TYPE]; + } + text() { + return Promise.resolve(this[BUFFER].toString()); + } + arrayBuffer() { + const buf = this[BUFFER]; + const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + return Promise.resolve(ab); + } + stream() { + const readable = new Readable(); + readable._read = function () {}; + readable.push(this[BUFFER]); + readable.push(null); + return readable; + } + toString() { + return '[object Blob]'; + } + slice() { + const size = this.size; + + const start = arguments[0]; + const end = arguments[1]; + let relativeStart, relativeEnd; + if (start === undefined) { + relativeStart = 0; + } else if (start < 0) { + relativeStart = Math.max(size + start, 0); + } else { + relativeStart = Math.min(start, size); + } + if (end === undefined) { + relativeEnd = size; + } else if (end < 0) { + relativeEnd = Math.max(size + end, 0); + } else { + relativeEnd = Math.min(end, size); + } + const span = Math.max(relativeEnd - relativeStart, 0); + + const buffer = this[BUFFER]; + const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); + const blob = new Blob([], { type: arguments[2] }); + blob[BUFFER] = slicedBuffer; + return blob; + } +} + +Object.defineProperties(Blob.prototype, { + size: { enumerable: true }, + type: { enumerable: true }, + slice: { enumerable: true } +}); + +Object.defineProperty(Blob.prototype, Symbol.toStringTag, { + value: 'Blob', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * fetch-error.js + * + * FetchError interface for operational errors + */ + +/** + * Create FetchError instance + * + * @param String message Error message for human + * @param String type Error type for machine + * @param String systemError For Node.js system error + * @return FetchError + */ +function FetchError(message, type, systemError) { + Error.call(this, message); + + this.message = message; + this.type = type; + + // when err.type is `system`, err.code contains system error code + if (systemError) { + this.code = this.errno = systemError.code; + } + + // hide custom error implementation details from end-users + Error.captureStackTrace(this, this.constructor); +} + +FetchError.prototype = Object.create(Error.prototype); +FetchError.prototype.constructor = FetchError; +FetchError.prototype.name = 'FetchError'; + +let convert; +try { + convert = require('encoding').convert; +} catch (e) {} + +const INTERNALS = Symbol('Body internals'); + +// fix an issue where "PassThrough" isn't a named export for node <10 +const PassThrough = Stream.PassThrough; + +/** + * Body mixin + * + * Ref: https://fetch.spec.whatwg.org/#body + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +function Body(body) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$size = _ref.size; + + let size = _ref$size === undefined ? 0 : _ref$size; + var _ref$timeout = _ref.timeout; + let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; + + if (body == null) { + // body is undefined or null + body = null; + } else if (isURLSearchParams(body)) { + // body is a URLSearchParams + body = Buffer.from(body.toString()); + } else if (isBlob(body)) ; else if (Buffer.isBuffer(body)) ; else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') { + // body is ArrayBuffer + body = Buffer.from(body); + } else if (ArrayBuffer.isView(body)) { + // body is ArrayBufferView + body = Buffer.from(body.buffer, body.byteOffset, body.byteLength); + } else if (body instanceof Stream) ; else { + // none of the above + // coerce to string then buffer + body = Buffer.from(String(body)); + } + this[INTERNALS] = { + body, + disturbed: false, + error: null + }; + this.size = size; + this.timeout = timeout; + + if (body instanceof Stream) { + body.on('error', function (err) { + const error = err.name === 'AbortError' ? err : new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err); + _this[INTERNALS].error = error; + }); + } +} + +Body.prototype = { + get body() { + return this[INTERNALS].body; + }, + + get bodyUsed() { + return this[INTERNALS].disturbed; + }, + + /** + * Decode response as ArrayBuffer + * + * @return Promise + */ + arrayBuffer() { + return consumeBody.call(this).then(function (buf) { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + }); + }, + + /** + * Return raw response as Blob + * + * @return Promise + */ + blob() { + let ct = this.headers && this.headers.get('content-type') || ''; + return consumeBody.call(this).then(function (buf) { + return Object.assign( + // Prevent copying + new Blob([], { + type: ct.toLowerCase() + }), { + [BUFFER]: buf + }); + }); + }, + + /** + * Decode response as json + * + * @return Promise + */ + json() { + var _this2 = this; + + return consumeBody.call(this).then(function (buffer) { + try { + return JSON.parse(buffer.toString()); + } catch (err) { + return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json')); + } + }); + }, + + /** + * Decode response as text + * + * @return Promise + */ + text() { + return consumeBody.call(this).then(function (buffer) { + return buffer.toString(); + }); + }, + + /** + * Decode response as buffer (non-spec api) + * + * @return Promise + */ + buffer() { + return consumeBody.call(this); + }, + + /** + * Decode response as text, while automatically detecting the encoding and + * trying to decode to UTF-8 (non-spec api) + * + * @return Promise + */ + textConverted() { + var _this3 = this; + + return consumeBody.call(this).then(function (buffer) { + return convertBody(buffer, _this3.headers); + }); + } +}; + +// In browsers, all properties are enumerable. +Object.defineProperties(Body.prototype, { + body: { enumerable: true }, + bodyUsed: { enumerable: true }, + arrayBuffer: { enumerable: true }, + blob: { enumerable: true }, + json: { enumerable: true }, + text: { enumerable: true } +}); + +Body.mixIn = function (proto) { + for (const name of Object.getOwnPropertyNames(Body.prototype)) { + // istanbul ignore else: future proof + if (!(name in proto)) { + const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); + Object.defineProperty(proto, name, desc); + } + } +}; + +/** + * Consume and convert an entire Body to a Buffer. + * + * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body + * + * @return Promise + */ +function consumeBody() { + var _this4 = this; + + if (this[INTERNALS].disturbed) { + return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`)); + } + + this[INTERNALS].disturbed = true; + + if (this[INTERNALS].error) { + return Body.Promise.reject(this[INTERNALS].error); + } + + let body = this.body; + + // body is null + if (body === null) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is blob + if (isBlob(body)) { + body = body.stream(); + } + + // body is buffer + if (Buffer.isBuffer(body)) { + return Body.Promise.resolve(body); + } + + // istanbul ignore if: should never happen + if (!(body instanceof Stream)) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is stream + // get ready to actually consume the body + let accum = []; + let accumBytes = 0; + let abort = false; + + return new Body.Promise(function (resolve, reject) { + let resTimeout; + + // allow timeout on slow response body + if (_this4.timeout) { + resTimeout = setTimeout(function () { + abort = true; + reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout')); + }, _this4.timeout); + } + + // handle stream errors + body.on('error', function (err) { + if (err.name === 'AbortError') { + // if the request was aborted, reject with this Error + abort = true; + reject(err); + } else { + // other errors, such as incorrect content-encoding + reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + + body.on('data', function (chunk) { + if (abort || chunk === null) { + return; + } + + if (_this4.size && accumBytes + chunk.length > _this4.size) { + abort = true; + reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size')); + return; + } + + accumBytes += chunk.length; + accum.push(chunk); + }); + + body.on('end', function () { + if (abort) { + return; + } + + clearTimeout(resTimeout); + + try { + resolve(Buffer.concat(accum, accumBytes)); + } catch (err) { + // handle streams that have accumulated too much data (issue #414) + reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + }); +} + +/** + * Detect buffer encoding and convert to target encoding + * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding + * + * @param Buffer buffer Incoming buffer + * @param String encoding Target encoding + * @return String + */ +function convertBody(buffer, headers) { + if (typeof convert !== 'function') { + throw new Error('The package `encoding` must be installed to use the textConverted() function'); + } + + const ct = headers.get('content-type'); + let charset = 'utf-8'; + let res, str; + + // header + if (ct) { + res = /charset=([^;]*)/i.exec(ct); + } + + // no charset in content type, peek at response body for at most 1024 bytes + str = buffer.slice(0, 1024).toString(); + + // html5 + if (!res && str) { + res = / 0 && arguments[0] !== undefined ? arguments[0] : undefined; + + this[MAP] = Object.create(null); + + if (init instanceof Headers) { + const rawHeaders = init.raw(); + const headerNames = Object.keys(rawHeaders); + + for (const headerName of headerNames) { + for (const value of rawHeaders[headerName]) { + this.append(headerName, value); + } + } + + return; + } + + // We don't worry about converting prop to ByteString here as append() + // will handle it. + if (init == null) ; else if (typeof init === 'object') { + const method = init[Symbol.iterator]; + if (method != null) { + if (typeof method !== 'function') { + throw new TypeError('Header pairs must be iterable'); + } + + // sequence> + // Note: per spec we have to first exhaust the lists then process them + const pairs = []; + for (const pair of init) { + if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { + throw new TypeError('Each header pair must be iterable'); + } + pairs.push(Array.from(pair)); + } + + for (const pair of pairs) { + if (pair.length !== 2) { + throw new TypeError('Each header pair must be a name/value tuple'); + } + this.append(pair[0], pair[1]); + } + } else { + // record + for (const key of Object.keys(init)) { + const value = init[key]; + this.append(key, value); + } + } + } else { + throw new TypeError('Provided initializer must be an object'); + } + } + + /** + * Return combined header value given name + * + * @param String name Header name + * @return Mixed + */ + get(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key === undefined) { + return null; + } + + return this[MAP][key].join(', '); + } + + /** + * Iterate over all headers + * + * @param Function callback Executed for each item with parameters (value, name, thisArg) + * @param Boolean thisArg `this` context for callback function + * @return Void + */ + forEach(callback) { + let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; + + let pairs = getHeaders(this); + let i = 0; + while (i < pairs.length) { + var _pairs$i = pairs[i]; + const name = _pairs$i[0], + value = _pairs$i[1]; + + callback.call(thisArg, value, name, this); + pairs = getHeaders(this); + i++; + } + } + + /** + * Overwrite header values given name + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + set(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + this[MAP][key !== undefined ? key : name] = [value]; + } + + /** + * Append a value onto existing header + * + * @param String name Header name + * @param String value Header value + * @return Void + */ + append(name, value) { + name = `${name}`; + value = `${value}`; + validateName(name); + validateValue(value); + const key = find(this[MAP], name); + if (key !== undefined) { + this[MAP][key].push(value); + } else { + this[MAP][name] = [value]; + } + } + + /** + * Check for header name existence + * + * @param String name Header name + * @return Boolean + */ + has(name) { + name = `${name}`; + validateName(name); + return find(this[MAP], name) !== undefined; + } + + /** + * Delete all header values given name + * + * @param String name Header name + * @return Void + */ + delete(name) { + name = `${name}`; + validateName(name); + const key = find(this[MAP], name); + if (key !== undefined) { + delete this[MAP][key]; + } + } + + /** + * Return raw headers (non-spec api) + * + * @return Object + */ + raw() { + return this[MAP]; + } + + /** + * Get an iterator on keys. + * + * @return Iterator + */ + keys() { + return createHeadersIterator(this, 'key'); + } + + /** + * Get an iterator on values. + * + * @return Iterator + */ + values() { + return createHeadersIterator(this, 'value'); + } + + /** + * Get an iterator on entries. + * + * This is the default iterator of the Headers object. + * + * @return Iterator + */ + [Symbol.iterator]() { + return createHeadersIterator(this, 'key+value'); + } +} +Headers.prototype.entries = Headers.prototype[Symbol.iterator]; + +Object.defineProperty(Headers.prototype, Symbol.toStringTag, { + value: 'Headers', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Headers.prototype, { + get: { enumerable: true }, + forEach: { enumerable: true }, + set: { enumerable: true }, + append: { enumerable: true }, + has: { enumerable: true }, + delete: { enumerable: true }, + keys: { enumerable: true }, + values: { enumerable: true }, + entries: { enumerable: true } +}); + +function getHeaders(headers) { + let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value'; + + const keys = Object.keys(headers[MAP]).sort(); + return keys.map(kind === 'key' ? function (k) { + return k.toLowerCase(); + } : kind === 'value' ? function (k) { + return headers[MAP][k].join(', '); + } : function (k) { + return [k.toLowerCase(), headers[MAP][k].join(', ')]; + }); +} + +const INTERNAL = Symbol('internal'); + +function createHeadersIterator(target, kind) { + const iterator = Object.create(HeadersIteratorPrototype); + iterator[INTERNAL] = { + target, + kind, + index: 0 + }; + return iterator; +} + +const HeadersIteratorPrototype = Object.setPrototypeOf({ + next() { + // istanbul ignore if + if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { + throw new TypeError('Value of `this` is not a HeadersIterator'); + } + + var _INTERNAL = this[INTERNAL]; + const target = _INTERNAL.target, + kind = _INTERNAL.kind, + index = _INTERNAL.index; + + const values = getHeaders(target, kind); + const len = values.length; + if (index >= len) { + return { + value: undefined, + done: true + }; + } + + this[INTERNAL].index = index + 1; + + return { + value: values[index], + done: false + }; + } +}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); + +Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { + value: 'HeadersIterator', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * Export the Headers object in a form that Node.js can consume. + * + * @param Headers headers + * @return Object + */ +function exportNodeCompatibleHeaders(headers) { + const obj = Object.assign({ __proto__: null }, headers[MAP]); + + // http.request() only supports string as Host header. This hack makes + // specifying custom Host header possible. + const hostHeaderKey = find(headers[MAP], 'Host'); + if (hostHeaderKey !== undefined) { + obj[hostHeaderKey] = obj[hostHeaderKey][0]; + } + + return obj; +} + +/** + * Create a Headers object from an object of headers, ignoring those that do + * not conform to HTTP grammar productions. + * + * @param Object obj Object of headers + * @return Headers + */ +function createHeadersLenient(obj) { + const headers = new Headers(); + for (const name of Object.keys(obj)) { + if (invalidTokenRegex.test(name)) { + continue; + } + if (Array.isArray(obj[name])) { + for (const val of obj[name]) { + if (invalidHeaderCharRegex.test(val)) { + continue; + } + if (headers[MAP][name] === undefined) { + headers[MAP][name] = [val]; + } else { + headers[MAP][name].push(val); + } + } + } else if (!invalidHeaderCharRegex.test(obj[name])) { + headers[MAP][name] = [obj[name]]; + } + } + return headers; +} + +const INTERNALS$1 = Symbol('Response internals'); + +// fix an issue where "STATUS_CODES" aren't a named export for node <10 +const STATUS_CODES = http.STATUS_CODES; + +/** + * Response class + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +class Response { + constructor() { + let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + Body.call(this, body, opts); + + const status = opts.status || 200; + const headers = new Headers(opts.headers); + + if (body != null && !headers.has('Content-Type')) { + const contentType = extractContentType(body); + if (contentType) { + headers.append('Content-Type', contentType); + } + } + + this[INTERNALS$1] = { + url: opts.url, + status, + statusText: opts.statusText || STATUS_CODES[status], + headers, + counter: opts.counter + }; + } + + get url() { + return this[INTERNALS$1].url || ''; + } + + get status() { + return this[INTERNALS$1].status; + } + + /** + * Convenience property representing if the request ended normally + */ + get ok() { + return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300; + } + + get redirected() { + return this[INTERNALS$1].counter > 0; + } + + get statusText() { + return this[INTERNALS$1].statusText; + } + + get headers() { + return this[INTERNALS$1].headers; + } + + /** + * Clone this response + * + * @return Response + */ + clone() { + return new Response(clone(this), { + url: this.url, + status: this.status, + statusText: this.statusText, + headers: this.headers, + ok: this.ok, + redirected: this.redirected + }); + } +} + +Body.mixIn(Response.prototype); + +Object.defineProperties(Response.prototype, { + url: { enumerable: true }, + status: { enumerable: true }, + ok: { enumerable: true }, + redirected: { enumerable: true }, + statusText: { enumerable: true }, + headers: { enumerable: true }, + clone: { enumerable: true } +}); + +Object.defineProperty(Response.prototype, Symbol.toStringTag, { + value: 'Response', + writable: false, + enumerable: false, + configurable: true +}); + +const INTERNALS$2 = Symbol('Request internals'); + +// fix an issue where "format", "parse" aren't a named export for node <10 +const parse_url = Url.parse; +const format_url = Url.format; + +const streamDestructionSupported = 'destroy' in Stream.Readable.prototype; + +/** + * Check if a value is an instance of Request. + * + * @param Mixed input + * @return Boolean + */ +function isRequest(input) { + return typeof input === 'object' && typeof input[INTERNALS$2] === 'object'; +} + +function isAbortSignal(signal) { + const proto = signal && typeof signal === 'object' && Object.getPrototypeOf(signal); + return !!(proto && proto.constructor.name === 'AbortSignal'); +} + +/** + * Request class + * + * @param Mixed input Url or Request instance + * @param Object init Custom options + * @return Void + */ +class Request { + constructor(input) { + let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + let parsedURL; + + // normalize input + if (!isRequest(input)) { + if (input && input.href) { + // in order to support Node.js' Url objects; though WHATWG's URL objects + // will fall into this branch also (since their `toString()` will return + // `href` property anyway) + parsedURL = parse_url(input.href); + } else { + // coerce input to a string before attempting to parse + parsedURL = parse_url(`${input}`); + } + input = {}; + } else { + parsedURL = parse_url(input.url); + } + + let method = init.method || input.method || 'GET'; + method = method.toUpperCase(); + + if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) { + throw new TypeError('Request with GET/HEAD method cannot have body'); + } + + let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null; + + Body.call(this, inputBody, { + timeout: init.timeout || input.timeout || 0, + size: init.size || input.size || 0 + }); + + const headers = new Headers(init.headers || input.headers || {}); + + if (inputBody != null && !headers.has('Content-Type')) { + const contentType = extractContentType(inputBody); + if (contentType) { + headers.append('Content-Type', contentType); + } + } + + let signal = isRequest(input) ? input.signal : null; + if ('signal' in init) signal = init.signal; + + if (signal != null && !isAbortSignal(signal)) { + throw new TypeError('Expected signal to be an instanceof AbortSignal'); + } + + this[INTERNALS$2] = { + method, + redirect: init.redirect || input.redirect || 'follow', + headers, + parsedURL, + signal + }; + + // node-fetch-only options + this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20; + this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true; + this.counter = init.counter || input.counter || 0; + this.agent = init.agent || input.agent; + } + + get method() { + return this[INTERNALS$2].method; + } + + get url() { + return format_url(this[INTERNALS$2].parsedURL); + } + + get headers() { + return this[INTERNALS$2].headers; + } + + get redirect() { + return this[INTERNALS$2].redirect; + } + + get signal() { + return this[INTERNALS$2].signal; + } + + /** + * Clone this request + * + * @return Request + */ + clone() { + return new Request(this); + } +} + +Body.mixIn(Request.prototype); + +Object.defineProperty(Request.prototype, Symbol.toStringTag, { + value: 'Request', + writable: false, + enumerable: false, + configurable: true +}); + +Object.defineProperties(Request.prototype, { + method: { enumerable: true }, + url: { enumerable: true }, + headers: { enumerable: true }, + redirect: { enumerable: true }, + clone: { enumerable: true }, + signal: { enumerable: true } +}); + +/** + * Convert a Request to Node.js http request options. + * + * @param Request A Request instance + * @return Object The options object to be passed to http.request + */ +function getNodeRequestOptions(request) { + const parsedURL = request[INTERNALS$2].parsedURL; + const headers = new Headers(request[INTERNALS$2].headers); + + // fetch step 1.3 + if (!headers.has('Accept')) { + headers.set('Accept', '*/*'); + } + + // Basic fetch + if (!parsedURL.protocol || !parsedURL.hostname) { + throw new TypeError('Only absolute URLs are supported'); + } + + if (!/^https?:$/.test(parsedURL.protocol)) { + throw new TypeError('Only HTTP(S) protocols are supported'); + } + + if (request.signal && request.body instanceof Stream.Readable && !streamDestructionSupported) { + throw new Error('Cancellation of streamed requests with AbortSignal is not supported in node < 8'); + } + + // HTTP-network-or-cache fetch steps 2.4-2.7 + let contentLengthValue = null; + if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { + contentLengthValue = '0'; + } + if (request.body != null) { + const totalBytes = getTotalBytes(request); + if (typeof totalBytes === 'number') { + contentLengthValue = String(totalBytes); + } + } + if (contentLengthValue) { + headers.set('Content-Length', contentLengthValue); + } + + // HTTP-network-or-cache fetch step 2.11 + if (!headers.has('User-Agent')) { + headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)'); + } + + // HTTP-network-or-cache fetch step 2.15 + if (request.compress && !headers.has('Accept-Encoding')) { + headers.set('Accept-Encoding', 'gzip,deflate'); + } + + let agent = request.agent; + if (typeof agent === 'function') { + agent = agent(parsedURL); + } + + if (!headers.has('Connection') && !agent) { + headers.set('Connection', 'close'); + } + + // HTTP-network fetch step 4.2 + // chunked encoding is handled by Node.js + + return Object.assign({}, parsedURL, { + method: request.method, + headers: exportNodeCompatibleHeaders(headers), + agent + }); +} + +/** + * abort-error.js + * + * AbortError interface for cancelled requests + */ + +/** + * Create AbortError instance + * + * @param String message Error message for human + * @return AbortError + */ +function AbortError(message) { + Error.call(this, message); + + this.type = 'aborted'; + this.message = message; + + // hide custom error implementation details from end-users + Error.captureStackTrace(this, this.constructor); +} + +AbortError.prototype = Object.create(Error.prototype); +AbortError.prototype.constructor = AbortError; +AbortError.prototype.name = 'AbortError'; + +// fix an issue where "PassThrough", "resolve" aren't a named export for node <10 +const PassThrough$1 = Stream.PassThrough; +const resolve_url = Url.resolve; + +/** + * Fetch function + * + * @param Mixed url Absolute url or Request instance + * @param Object opts Fetch options + * @return Promise + */ +function fetch(url, opts) { + + // allow custom promise + if (!fetch.Promise) { + throw new Error('native promise missing, set fetch.Promise to your favorite alternative'); + } + + Body.Promise = fetch.Promise; + + // wrap http.request into fetch + return new fetch.Promise(function (resolve, reject) { + // build request object + const request = new Request(url, opts); + const options = getNodeRequestOptions(request); + + const send = (options.protocol === 'https:' ? https : http).request; + const signal = request.signal; + + let response = null; + + const abort = function abort() { + let error = new AbortError('The user aborted a request.'); + reject(error); + if (request.body && request.body instanceof Stream.Readable) { + request.body.destroy(error); + } + if (!response || !response.body) return; + response.body.emit('error', error); + }; + + if (signal && signal.aborted) { + abort(); + return; + } + + const abortAndFinalize = function abortAndFinalize() { + abort(); + finalize(); + }; + + // send request + const req = send(options); + let reqTimeout; + + if (signal) { + signal.addEventListener('abort', abortAndFinalize); + } + + function finalize() { + req.abort(); + if (signal) signal.removeEventListener('abort', abortAndFinalize); + clearTimeout(reqTimeout); + } + + if (request.timeout) { + req.once('socket', function (socket) { + reqTimeout = setTimeout(function () { + reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); + finalize(); + }, request.timeout); + }); + } + + req.on('error', function (err) { + reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); + finalize(); + }); + + req.on('response', function (res) { + clearTimeout(reqTimeout); + + const headers = createHeadersLenient(res.headers); + + // HTTP fetch step 5 + if (fetch.isRedirect(res.statusCode)) { + // HTTP fetch step 5.2 + const location = headers.get('Location'); + + // HTTP fetch step 5.3 + const locationURL = location === null ? null : resolve_url(request.url, location); + + // HTTP fetch step 5.5 + switch (request.redirect) { + case 'error': + reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); + finalize(); + return; + case 'manual': + // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL. + if (locationURL !== null) { + // handle corrupted header + try { + headers.set('Location', locationURL); + } catch (err) { + // istanbul ignore next: nodejs server prevent invalid response headers, we can't test this through normal request + reject(err); + } + } + break; + case 'follow': + // HTTP-redirect fetch step 2 + if (locationURL === null) { + break; + } + + // HTTP-redirect fetch step 5 + if (request.counter >= request.follow) { + reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 6 (counter increment) + // Create a new Request object. + const requestOpts = { + headers: new Headers(request.headers), + follow: request.follow, + counter: request.counter + 1, + agent: request.agent, + compress: request.compress, + method: request.method, + body: request.body, + signal: request.signal, + timeout: request.timeout + }; + + // HTTP-redirect fetch step 9 + if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { + reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 11 + if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { + requestOpts.method = 'GET'; + requestOpts.body = undefined; + requestOpts.headers.delete('content-length'); + } + + // HTTP-redirect fetch step 15 + resolve(fetch(new Request(locationURL, requestOpts))); + finalize(); + return; + } + } + + // prepare response + res.once('end', function () { + if (signal) signal.removeEventListener('abort', abortAndFinalize); + }); + let body = res.pipe(new PassThrough$1()); + + const response_options = { + url: request.url, + status: res.statusCode, + statusText: res.statusMessage, + headers: headers, + size: request.size, + timeout: request.timeout, + counter: request.counter + }; + + // HTTP-network fetch step 12.1.1.3 + const codings = headers.get('Content-Encoding'); + + // HTTP-network fetch step 12.1.1.4: handle content codings + + // in following scenarios we ignore compression support + // 1. compression support is disabled + // 2. HEAD request + // 3. no Content-Encoding header + // 4. no content response (204) + // 5. content not modified response (304) + if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { + response = new Response(body, response_options); + resolve(response); + return; + } + + // For Node v6+ + // Be less strict when decoding compressed responses, since sometimes + // servers send slightly invalid responses that are still accepted + // by common browsers. + // Always using Z_SYNC_FLUSH is what cURL does. + const zlibOptions = { + flush: zlib.Z_SYNC_FLUSH, + finishFlush: zlib.Z_SYNC_FLUSH + }; + + // for gzip + if (codings == 'gzip' || codings == 'x-gzip') { + body = body.pipe(zlib.createGunzip(zlibOptions)); + response = new Response(body, response_options); + resolve(response); + return; + } + + // for deflate + if (codings == 'deflate' || codings == 'x-deflate') { + // handle the infamous raw deflate response from old servers + // a hack for old IIS and Apache servers + const raw = res.pipe(new PassThrough$1()); + raw.once('data', function (chunk) { + // see http://stackoverflow.com/questions/37519828 + if ((chunk[0] & 0x0F) === 0x08) { + body = body.pipe(zlib.createInflate()); + } else { + body = body.pipe(zlib.createInflateRaw()); + } + response = new Response(body, response_options); + resolve(response); + }); + return; + } + + // for br + if (codings == 'br' && typeof zlib.createBrotliDecompress === 'function') { + body = body.pipe(zlib.createBrotliDecompress()); + response = new Response(body, response_options); + resolve(response); + return; + } + + // otherwise, use response as-is + response = new Response(body, response_options); + resolve(response); + }); + + writeToStream(req, request); + }); +} +/** + * Redirect code matching + * + * @param Number code Status code + * @return Boolean + */ +fetch.isRedirect = function (code) { + return code === 301 || code === 302 || code === 303 || code === 307 || code === 308; +}; + +// expose Promise +fetch.Promise = global.Promise; + +module.exports = exports = fetch; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = exports; +exports.Headers = Headers; +exports.Request = Request; +exports.Response = Response; +exports.FetchError = FetchError; diff --git a/TaskServer/node_modules/node-fetch/package.json b/TaskServer/node_modules/node-fetch/package.json new file mode 100644 index 000000000..dcd996399 --- /dev/null +++ b/TaskServer/node_modules/node-fetch/package.json @@ -0,0 +1,93 @@ +{ + "_from": "node-fetch@^2.2.0", + "_id": "node-fetch@2.6.0", + "_inBundle": false, + "_integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "_location": "/node-fetch", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "node-fetch@^2.2.0", + "name": "node-fetch", + "escapedName": "node-fetch", + "rawSpec": "^2.2.0", + "saveSpec": null, + "fetchSpec": "^2.2.0" + }, + "_requiredBy": [ + "/telegraf" + ], + "_resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "_shasum": "e633456386d4aa55863f676a7ab0daa8fdecb0fd", + "_spec": "node-fetch@^2.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\telegraf", + "author": { + "name": "David Frank" + }, + "browser": "./browser.js", + "bugs": { + "url": "https://github.com/bitinn/node-fetch/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "A light-weight module that brings window.fetch to node.js", + "devDependencies": { + "@ungap/url-search-params": "^0.1.2", + "abort-controller": "^1.1.0", + "abortcontroller-polyfill": "^1.3.0", + "babel-core": "^6.26.3", + "babel-plugin-istanbul": "^4.1.6", + "babel-preset-env": "^1.6.1", + "babel-register": "^6.16.3", + "chai": "^3.5.0", + "chai-as-promised": "^7.1.1", + "chai-iterator": "^1.1.1", + "chai-string": "~1.3.0", + "codecov": "^3.3.0", + "cross-env": "^5.2.0", + "form-data": "^2.3.3", + "is-builtin-module": "^1.0.0", + "mocha": "^5.0.0", + "nyc": "11.9.0", + "parted": "^0.1.1", + "promise": "^8.0.3", + "resumer": "0.0.0", + "rollup": "^0.63.4", + "rollup-plugin-babel": "^3.0.7", + "string-to-arraybuffer": "^1.0.2", + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "files": [ + "lib/index.js", + "lib/index.mjs", + "lib/index.es.js", + "browser.js" + ], + "homepage": "https://github.com/bitinn/node-fetch", + "keywords": [ + "fetch", + "http", + "promise" + ], + "license": "MIT", + "main": "lib/index", + "module": "lib/index.mjs", + "name": "node-fetch", + "repository": { + "type": "git", + "url": "git+https://github.com/bitinn/node-fetch.git" + }, + "scripts": { + "build": "cross-env BABEL_ENV=rollup rollup -c", + "coverage": "cross-env BABEL_ENV=coverage nyc --reporter json --reporter text mocha -R spec test/test.js && codecov -f coverage/coverage-final.json", + "prepare": "npm run build", + "report": "cross-env BABEL_ENV=coverage nyc --reporter lcov --reporter text mocha -R spec test/test.js", + "test": "cross-env BABEL_ENV=test mocha --require babel-register --throw-deprecation test/test.js" + }, + "version": "2.6.0" +} diff --git a/TaskServer/node_modules/node-ipc/dao/client.js b/TaskServer/node_modules/node-ipc/dao/client.js new file mode 100644 index 000000000..41ce50411 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/dao/client.js @@ -0,0 +1,257 @@ +'use strict'; + +const net = require('net'), + tls = require('tls'), + EventParser = require('../entities/EventParser.js'), + Message = require('js-message'), + fs = require('fs'), + Queue = require('js-queue'); + +let Events = require('event-pubsub/es5'); +if(process.version[1]>4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Client extends Events{ + constructor(config,log){ + super(); + Object.assign( + this, + { + Client : Client, + config : config, + queue : new Queue, + socket : false, + connect : connect, + emit : emit, + log : log, + retriesRemaining:config.maxRetries||0, + explicitlyDisconnected: false + } + ); + + eventParser=new EventParser(this.config); + } +} + +function emit(type,data){ + this.log('dispatching event to ', this.id, this.path, ' : ', type, ',', data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(!this.config.sync){ + this.socket.write(message); + return; + } + + this.queue.add( + syncEmit.bind(this,message) + ); +} + +function syncEmit(message){ + this.log('dispatching event to ', this.id, this.path, ' : ', message); + this.socket.write(message); +} + +function connect(){ + //init client object for scope persistance especially inside of socket events. + let client=this; + + client.log('requested connection to ', client.id, client.path); + if(!this.path){ + client.log('\n\n######\nerror: ', client.id ,' client has not specified socket path it wishes to connect to.'); + return; + } + + const options={}; + + if(!client.port){ + client.log('Connecting client on Unix Socket :', client.path); + + options.path=client.path; + + if (process.platform ==='win32' && !client.path.startsWith('\\\\.\\pipe\\')){ + options.path = options.path.replace(/^\//, ''); + options.path = options.path.replace(/\//g, '-'); + options.path= `\\\\.\\pipe\\${options.path}`; + } + + client.socket = net.connect(options); + }else{ + options.host=client.path; + options.port=client.port; + + if(client.config.interface.localAddress){ + options.localAddress=client.config.interface.localAddress; + } + + if(client.config.interface.localPort){ + options.localPort=client.config.interface.localPort; + } + + if(client.config.interface.family){ + options.family=client.config.interface.family; + } + + if(client.config.interface.hints){ + options.hints=client.config.interface.hints; + } + + if(client.config.interface.lookup){ + options.lookup=client.config.interface.lookup; + } + + if(!client.config.tls){ + client.log('Connecting client via TCP to', options); + client.socket = net.connect(options); + }else{ + client.log('Connecting client via TLS to', client.path ,client.port,client.config.tls); + if(client.config.tls.private){ + client.config.tls.key=fs.readFileSync(client.config.tls.private); + } + if(client.config.tls.public){ + client.config.tls.cert=fs.readFileSync(client.config.tls.public); + } + if(client.config.tls.trustedConnections){ + if(typeof client.config.tls.trustedConnections === 'string'){ + client.config.tls.trustedConnections=[client.config.tls.trustedConnections]; + } + client.config.tls.ca=[]; + for(let i=0; i4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Server extends Events{ + constructor(path,config,log,port){ + super(); + Object.assign( + this, + { + config : config, + path : path, + port : port, + udp4 : false, + udp6 : false, + log : log, + server : false, + sockets : [], + emit : emit, + broadcast : broadcast + } + ); + + eventParser=new EventParser(this.config); + + this.on( + 'close', + serverClosed.bind(this) + ); + } + + onStart(socket){ + this.trigger( + 'start', + socket + ); + } + + stop(){ + this.server.close(); + } + + start(){ + if(!this.path){ + this.log('Socket Server Path not specified, refusing to start'); + return; + } + + if(this.config.unlink){ + fs.unlink( + this.path, + startServer.bind(this) + ); + }else{ + startServer.bind(this)(); + } + } +} + +function emit(socket, type, data){ + this.log('dispatching event to socket', ' : ', type, data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + this.log(this.config.encoding) + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + + if(!socket.address || !socket.port){ + this.log('Attempting to emit to a single UDP socket without supplying socket address or port. Redispatching event as broadcast to all connected sockets'); + this.broadcast(type,data); + return; + } + + this.server.write( + message, + socket + ); + return; + } + + socket.write(message); +} + +function broadcast(type,data){ + this.log('broadcasting event to all known sockets listening to ', this.path,' : ', ((this.port)?this.port:''), type, data); + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + for(let i=1, count=this.sockets.length; i0){ + let message=new Message; + message.load(data.shift()); + + // Only set the sock id if it is specified. + if (message.data && message.data.id){ + sock.id=message.data.id; + } + + this.log('received event of : ',message.type,message.data); + + this.publish( + message.type, + message.data, + sock + ); + } +} + +function socketClosed(socket){ + this.publish( + 'close', + socket + ); +} + +function serverCreated(socket) { + this.sockets.push(socket); + + if(socket.setEncoding){ + socket.setEncoding(this.config.encoding); + } + + this.log('## socket connection to server detected ##'); + socket.on( + 'close', + socketClosed.bind(this) + ); + + socket.on( + 'error', + function(err){ + this.log('server socket error',err); + + this.publish('error',err); + }.bind(this) + ); + + socket.on( + 'data', + gotData.bind(this,socket) + ); + + socket.on( + 'message', + function(msg,rinfo) { + if (!rinfo){ + return; + } + + this.log('Received UDP message from ', rinfo.address, rinfo.port); + let data; + + if(this.config.rawSocket){ + data=new Buffer(msg,this.config.encoding); + }else{ + data=msg.toString(); + } + socket.emit('data',data,rinfo); + }.bind(this) + ); + + this.publish( + 'connect', + socket + ); + + if(this.config.rawBuffer){ + return; + } +} + +function startServer() { + this.log( + 'starting server on ',this.path, + ((this.port)?`:${this.port}`:'') + ); + + if(!this.udp4 && !this.udp6){ + this.log('starting TLS server',this.config.tls); + if(!this.config.tls){ + this.server=net.createServer( + serverCreated.bind(this) + ); + }else{ + startTLSServer.bind(this)(); + } + }else{ + this.server=dgram.createSocket( + ((this.udp4)? 'udp4':'udp6') + ); + this.server.write=UDPWrite.bind(this); + this.server.on( + 'listening', + function UDPServerStarted() { + serverCreated.bind(this)(this.server); + }.bind(this) + ); + } + + this.server.on( + 'error', + function(err){ + this.log('server error',err); + + this.publish( + 'error', + err + ); + }.bind(this) + ); + + this.server.maxConnections=this.config.maxConnections; + + if(!this.port){ + this.log('starting server as', 'Unix || Windows Socket'); + if (process.platform ==='win32'){ + this.path = this.path.replace(/^\//, ''); + this.path = this.path.replace(/\//g, '-'); + this.path= `\\\\.\\pipe\\${this.path}`; + } + + this.server.listen( + this.path, + this.onStart.bind(this) + ); + + return; + } + + if(!this.udp4 && !this.udp6){ + this.log('starting server as', (this.config.tls?'TLS':'TCP')); + this.server.listen( + this.port, + this.path, + this.onStart.bind(this) + ); + return; + } + + this.log('starting server as',((this.udp4)? 'udp4':'udp6')); + + this.server.bind( + this.port, + this.path + ); + + this.onStart( + { + address : this.path, + port : this.port + } + ); +} + +function startTLSServer(){ + this.log('starting TLS server',this.config.tls); + if(this.config.tls.private){ + this.config.tls.key=fs.readFileSync(this.config.tls.private); + }else{ + this.config.tls.key=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/private/server.key`); + } + if(this.config.tls.public){ + this.config.tls.cert=fs.readFileSync(this.config.tls.public); + }else{ + this.config.tls.cert=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/server.pub`); + } + if(this.config.tls.dhparam){ + this.config.tls.dhparam=fs.readFileSync(this.config.tls.dhparam); + } + if(this.config.tls.trustedConnections){ + if(typeof this.config.tls.trustedConnections === 'string'){ + this.config.tls.trustedConnections=[this.config.tls.trustedConnections]; + } + this.config.tls.ca=[]; + for(let i=0; i 0) { + // getting the family of first network interface available + IPType = networkInterfaces [ + Object.keys( networkInterfaces )[0] + ][0].family; + } + return IPType; +} + +module.exports=Defaults; diff --git a/TaskServer/node_modules/node-ipc/entities/EventParser.js b/TaskServer/node_modules/node-ipc/entities/EventParser.js new file mode 100644 index 000000000..8ac1f2bda --- /dev/null +++ b/TaskServer/node_modules/node-ipc/entities/EventParser.js @@ -0,0 +1,32 @@ +'use strict'; + +const Defaults = require('./Defaults.js'); + +class Parser{ + constructor(config){ + if(!config){ + config=new Defaults; + } + this.delimiter=config.delimiter; + } + + format(message){ + if(!message.data && message.data!==false && message.data!==0){ + message.data={}; + } + if(message.data['_maxListeners']){ + message.data={}; + } + + message=message.JSON+this.delimiter; + return message; + } + + parse(data){ + let events=data.split(this.delimiter); + events.pop(); + return events; + } +} + +module.exports=Parser; diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..9d5ce05d6 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..74136333d --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,50 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..2bef44794 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js new file mode 100644 index 000000000..6ae692edf --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js new file mode 100644 index 000000000..6c8790170 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.maxConnections=1; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log('DISCONNECTED\n\n',arguments); + } + ); + } +); + +ipc.server.on( + 'error', + function(err){ + ipc.log('Got an ERROR!',err); + } +); + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js new file mode 100644 index 000000000..292415053 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js @@ -0,0 +1,46 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data,'\n\n'); + } + ); + } +); + +console.log(ipc); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js new file mode 100644 index 000000000..3e817da13 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync = true; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + //fake some synch procedural code + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..b6ac81f6b --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js b/TaskServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..5da4de4d5 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js new file mode 100644 index 000000000..508702d01 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js new file mode 100644 index 000000000..16fd21fc3 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js new file mode 100644 index 000000000..0c52c268d --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js @@ -0,0 +1,55 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js new file mode 100644 index 000000000..3cbcd8e38 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +//node-ipc will default to its local certs +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js new file mode 100644 index 000000000..bda3eb701 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js new file mode 100644 index 000000000..56eee122b --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js new file mode 100644 index 000000000..26d5b1da6 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js new file mode 100644 index 000000000..ee984a4e1 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js new file mode 100644 index 000000000..a52c3602a --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js new file mode 100644 index 000000000..405db4468 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js new file mode 100644 index 000000000..aec87c100 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js new file mode 100644 index 000000000..f0c66ad22 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js b/TaskServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js new file mode 100644 index 000000000..f0d0b29af --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + console.log('connection detected'); + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js b/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..d98a7b061 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8002, //we set the port here because the hello client and world server are already using the default of 8000 and the port 8001. So we can not bind to those while hello and world are connected to them. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Goodbye' + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js b/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..ecc0b45a4 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js b/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..97f927088 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,63 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + 'udp4', + function(){ + console.log(123); + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + messages[data.id]=true; + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling evryone how muchg I am loved!'); + ipc.server.broadcast( + 'message', + { + id : ipc.config.id, + message : 'Everybody Loves The World! Got messages from hello and goodbye!' + } + ); + } + } + ); + + console.log(ipc.server); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js b/TaskServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js new file mode 100644 index 000000000..f731b1ba8 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js @@ -0,0 +1,52 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js b/TaskServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js new file mode 100644 index 000000000..0f7aa4543 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js b/TaskServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..73cd13ac5 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data){ + ipc.log('got a message from world ', data, data.toString()); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'hello' + ); + } +); + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js b/TaskServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..d0c34f7e2 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js b/TaskServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js new file mode 100644 index 000000000..eca2a889c --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const ipc = require('../../node-ipc'); + +const socketPath = '/tmp/ipc.sock'; + +//loop forever so you can see the pid of the cluster sever change in the logs +setInterval( + function() { + ipc.connectTo( + 'world', + socketPath, + connecting + ); + }, + 2000 +); + +function connecting(socket) { + ipc.of.world.on( + 'connect', + function() { + ipc.of.world.emit( + 'currentDate', + { + message: new Date().toISOString() + } + ); + ipc.disconnect('world'); + } + ); +} diff --git a/TaskServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js b/TaskServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js new file mode 100644 index 000000000..b0942a338 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const ipc=require('../../../node-ipc'); +const cpuCount = require('os').cpus().length; +const cluster = require('cluster'); +const socketPath = '/tmp/ipc.sock'; + +ipc.config.unlink = false; + +if (cluster.isMaster) { + if (fs.existsSync(socketPath)) { + fs.unlinkSync(socketPath); + } + + for (let i = 0; i < cpuCount; i++) { + cluster.fork(); + } +}else{ + ipc.serve( + socketPath, + function() { + ipc.server.on( + 'currentDate', + function(data,socket) { + console.log(`pid ${process.pid} got: `, data); + } + ); + } + ); + + ipc.server.start(); + console.log(`pid ${process.pid} listening on ${socketPath}`); +} diff --git a/TaskServer/node_modules/node-ipc/example/rawBuffer/hello-client.js b/TaskServer/node_modules/node-ipc/example/rawBuffer/hello-client.js new file mode 100644 index 000000000..f22ab5376 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/rawBuffer/hello-client.js @@ -0,0 +1,43 @@ +'use strict'; + +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + //make a 6 byte buffer for example + const myBuffer=new Buffer(6).fill(0); + + myBuffer.writeUInt16BE(0x02,0); + myBuffer.writeUInt32BE(0xffeecc,2); + + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + myBuffer + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/rawBuffer/world.server.js b/TaskServer/node_modules/node-ipc/example/rawBuffer/world.server.js new file mode 100644 index 000000000..f27a02cd5 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + [0xaa] + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data); + ipc.server.emit( + socket, + [0x0d,0xee] + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..57d455eec --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..d556c4694 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..69cfb31c0 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js new file mode 100644 index 000000000..c6d87cb27 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js new file mode 100644 index 000000000..6b3052242 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js @@ -0,0 +1,33 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js new file mode 100644 index 000000000..3142677a2 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; +ipc.config.sync= true; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello'+i + } + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js new file mode 100644 index 000000000..c76e1bb6f --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + setTimeout( + function(){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + }, + 2000 + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..9c7042a90 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..c3ed83c9a --- /dev/null +++ b/TaskServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/TaskServer/node_modules/node-ipc/node-ipc.js b/TaskServer/node_modules/node-ipc/node-ipc.js new file mode 100644 index 000000000..1c6336061 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/node-ipc.js @@ -0,0 +1,21 @@ +'use strict'; + +const IPC = require('./services/IPC.js'); + +class IPCModule extends IPC{ + constructor(){ + super(); + //include IPC to make extensible + Object.defineProperty( + this, + 'IPC', + { + enumerable:true, + writable:false, + value:IPC + } + ) + } +} + +module.exports=new IPCModule; diff --git a/TaskServer/node_modules/node-ipc/package.json b/TaskServer/node_modules/node-ipc/package.json new file mode 100644 index 000000000..696d18b97 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/package.json @@ -0,0 +1,94 @@ +{ + "_args": [ + [ + "node-ipc@9.1.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer" + ] + ], + "_from": "node-ipc@9.1.1", + "_id": "node-ipc@9.1.1", + "_inBundle": false, + "_integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "_location": "/node-ipc", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "node-ipc@9.1.1", + "name": "node-ipc", + "escapedName": "node-ipc", + "rawSpec": "9.1.1", + "saveSpec": null, + "fetchSpec": "9.1.1" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "_spec": "9.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/node-ipc/issues" + }, + "dependencies": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + }, + "description": "A nodejs module for local and remote Inter Process Communication (IPC), Neural Networking, and able to facilitate machine learning.", + "devDependencies": { + "codacy-coverage": "2.0.0", + "istanbul": "0.4.1", + "jasmine": "2.4.1", + "node-cmd": "2.0.0" + }, + "directories": { + "example": "example" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "http://riaevangelist.github.io/node-ipc/", + "keywords": [ + "IPC", + "Neural Networking", + "Machine Learning", + "inter", + "process", + "communication", + "unix", + "windows", + "win", + "socket", + "TCP", + "UDP", + "domain", + "sockets", + "threaded", + "communication", + "multi", + "process", + "shared", + "memory" + ], + "license": "DBAD", + "main": "node-ipc.js", + "name": "node-ipc", + "pre-commit": [ + "cover" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/node-ipc.git" + }, + "scripts": { + "cover": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "coverup": "cat ./spec/coverage/lcov.info | codacy-coverage", + "test": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "test-windows": "istanbul cover -x **/spec/** -dir ./spec/coverage ./node_modules/jasmine/bin/jasmine.js" + }, + "version": "9.1.1" +} diff --git a/TaskServer/node_modules/node-ipc/services/IPC.js b/TaskServer/node_modules/node-ipc/services/IPC.js new file mode 100644 index 000000000..3a6edc7f5 --- /dev/null +++ b/TaskServer/node_modules/node-ipc/services/IPC.js @@ -0,0 +1,337 @@ +'use strict'; + +const Defaults = require('../entities/Defaults.js'), + Client = require('../dao/client.js'), + Server = require('../dao/socketServer.js'), + util = require('util'); + +class IPC{ + constructor(){ + Object.defineProperties( + this, + { + config : { + enumerable:true, + writable:true, + value:new Defaults + }, + connectTo : { + enumerable:true, + writable:false, + value:connect + }, + connectToNet: { + enumerable:true, + writable:false, + value:connectNet + }, + disconnect : { + enumerable:true, + writable:false, + value:disconnect + }, + serve : { + enumerable:true, + writable:false, + value:serve + }, + serveNet : { + enumerable:true, + writable:false, + value:serveNet + }, + of : { + enumerable:true, + writable:true, + value:{} + }, + server : { + enumerable:true, + writable:true, + configurable:true, + value:false + }, + log : { + enumerable:true, + writable:false, + value:log + } + } + ); + } +} + +function log(...args){ + if(this.config.silent){ + return; + } + + for(let i=0, count=args.length; i= 0.10" + }, + "homepage": "https://github.com/connrs/node-sandwich-stream#readme", + "husky": { + "hooks": { + "pre-push": "npm run build && npm test", + "pre-commit": "npm run lint && npm run docs" + } + }, + "keywords": [ + "stream", + "sandwich", + "readable", + "typescript", + "concatenation" + ], + "license": "Apache-2.0", + "licenses": [ + { + "type": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + ], + "main": "dist/sandwich-stream", + "module": "dist/sandwich-stream.mjs", + "name": "sandwich-stream", + "repository": { + "type": "git", + "url": "git+https://github.com/connrs/node-sandwich-stream.git" + }, + "scripts": { + "build": "npm run rollup:build", + "docs": "typedoc --out ./docs/ ./src/", + "jest": "jest --config jest.config.json --ci --runInBand --detectOpenHandles --forceExit --no-cache", + "lint": "tslint --config tslint.json --project .", + "rollup:build": "rollup -c", + "rollup:watch": "npm run rollup:build -- --watch", + "test": "snyk test && npm run jest" + }, + "types": "dist/sandwich-stream.d.ts", + "version": "2.0.2" +} diff --git a/TaskServer/node_modules/sandwich-stream/rollup.config.js b/TaskServer/node_modules/sandwich-stream/rollup.config.js new file mode 100644 index 000000000..fa370452a --- /dev/null +++ b/TaskServer/node_modules/sandwich-stream/rollup.config.js @@ -0,0 +1,40 @@ +import typescript from 'rollup-plugin-typescript2'; + +import nodeOs from 'os'; +import nodePath from 'path'; + +import pkg from './package.json'; + +export default [ + { + input: 'src/sandwich-stream.ts', + external: [ + 'stream' + ], + plugins: [ + typescript({ + cacheRoot: nodePath.join( + nodeOs.tmpdir(), + String(Date.now), + '.rpt2_cache' + ), + tsconfigOverride: { + compilerOptions: { + composite: false + } + } + }) + ], + output: [ + { + file: `${pkg.main}.js`, + format: 'cjs', + exports: 'named' + }, + { + file: `${pkg.main}.mjs`, + format: 'esm' + } + ] + } +]; diff --git a/TaskServer/node_modules/telegraf/composer.js b/TaskServer/node_modules/telegraf/composer.js new file mode 100644 index 000000000..3a82cce32 --- /dev/null +++ b/TaskServer/node_modules/telegraf/composer.js @@ -0,0 +1,408 @@ +const Context = require('./context') + +class Composer { + constructor (...fns) { + this.handler = Composer.compose(fns) + } + + use (...fns) { + this.handler = Composer.compose([this.handler, ...fns]) + return this + } + + on (updateTypes, ...fns) { + return this.use(Composer.mount(updateTypes, ...fns)) + } + + hears (triggers, ...fns) { + return this.use(Composer.hears(triggers, ...fns)) + } + + command (commands, ...fns) { + return this.use(Composer.command(commands, ...fns)) + } + + action (triggers, ...fns) { + return this.use(Composer.action(triggers, ...fns)) + } + + inlineQuery (triggers, ...fns) { + return this.use(Composer.inlineQuery(triggers, ...fns)) + } + + gameQuery (...fns) { + return this.use(Composer.gameQuery(...fns)) + } + + drop (predicate) { + return this.use(Composer.drop(predicate)) + } + + filter (predicate) { + return this.use(Composer.filter(predicate)) + } + + entity (...args) { + return this.use(Composer.entity(...args)) + } + + email (...args) { + return this.use(Composer.email(...args)) + } + + url (...args) { + return this.use(Composer.url(...args)) + } + + textLink (...args) { + return this.use(Composer.textLink(...args)) + } + + textMention (...args) { + return this.use(Composer.textMention(...args)) + } + + mention (...args) { + return this.use(Composer.mention(...args)) + } + + phone (...args) { + return this.use(Composer.phone(...args)) + } + + hashtag (...args) { + return this.use(Composer.hashtag(...args)) + } + + cashtag (...args) { + return this.use(Composer.cashtag(...args)) + } + + start (...fns) { + return this.command('start', Composer.tap((ctx) => { + ctx.startPayload = ctx.message.text.substring(7) + }), ...fns) + } + + help (...fns) { + return this.command('help', ...fns) + } + + settings (...fns) { + return this.command('settings', ...fns) + } + + middleware () { + return this.handler + } + + static reply (...args) { + return (ctx) => ctx.reply(...args) + } + + static catchAll (...fns) { + return Composer.catch((err) => { + console.error() + console.error((err.stack || err.toString()).replace(/^/gm, ' ')) + console.error() + }, ...fns) + } + + static catch (errorHandler, ...fns) { + const handler = Composer.compose(fns) + return (ctx, next) => Promise.resolve(handler(ctx, next)) + .catch((err) => errorHandler(err, ctx)) + } + + static fork (middleware) { + const handler = Composer.unwrap(middleware) + return (ctx, next) => { + setImmediate(handler, ctx, Composer.safePassThru()) + return next(ctx) + } + } + + static tap (middleware) { + const handler = Composer.unwrap(middleware) + return (ctx, next) => Promise.resolve(handler(ctx, Composer.safePassThru())).then(() => next(ctx)) + } + + static passThru () { + return (ctx, next) => next(ctx) + } + + static safePassThru () { + return (ctx, next) => typeof next === 'function' ? next(ctx) : Promise.resolve() + } + + static lazy (factoryFn) { + if (typeof factoryFn !== 'function') { + throw new Error('Argument must be a function') + } + return (ctx, next) => Promise.resolve(factoryFn(ctx)) + .then((middleware) => Composer.unwrap(middleware)(ctx, next)) + } + + static log (logFn = console.log) { + return Composer.fork((ctx) => logFn(JSON.stringify(ctx.update, null, 2))) + } + + static branch (predicate, trueMiddleware, falseMiddleware) { + if (typeof predicate !== 'function') { + return predicate ? trueMiddleware : falseMiddleware + } + return Composer.lazy((ctx) => Promise.resolve(predicate(ctx)) + .then((value) => value ? trueMiddleware : falseMiddleware)) + } + + static optional (predicate, ...fns) { + return Composer.branch(predicate, Composer.compose(fns), Composer.safePassThru()) + } + + static filter (predicate) { + return Composer.branch(predicate, Composer.safePassThru(), () => { }) + } + + static drop (predicate) { + return Composer.branch(predicate, () => { }, Composer.safePassThru()) + } + + static dispatch (routeFn, handlers) { + return typeof routeFn === 'function' + ? Composer.lazy((ctx) => Promise.resolve(routeFn(ctx)).then((value) => handlers[value])) + : handlers[routeFn] + } + + static mount (updateType, ...fns) { + const updateTypes = normalizeTextArguments(updateType) + const predicate = (ctx) => updateTypes.includes(ctx.updateType) || updateTypes.some((type) => ctx.updateSubTypes.includes(type)) + return Composer.optional(predicate, ...fns) + } + + static entity (predicate, ...fns) { + if (typeof predicate !== 'function') { + const entityTypes = normalizeTextArguments(predicate) + return Composer.entity(({ type }) => entityTypes.includes(type), ...fns) + } + return Composer.optional((ctx) => { + const message = ctx.message || ctx.channelPost + const entities = message && (message.entities || message.caption_entities) + const text = message && (message.text || message.caption) + return entities && entities.some((entity) => + predicate(entity, text.substring(entity.offset, entity.offset + entity.length), ctx) + ) + }, ...fns) + } + + static entityText (entityType, predicate, ...fns) { + if (fns.length === 0) { + return Array.isArray(predicate) + ? Composer.entity(entityType, ...predicate) + : Composer.entity(entityType, predicate) + } + const triggers = normalizeTriggers(predicate) + return Composer.entity(({ type }, value, ctx) => { + if (type !== entityType) { + return false + } + for (const trigger of triggers) { + ctx.match = trigger(value, ctx) + if (ctx.match) { + return true + } + } + }, ...fns) + } + + static email (email, ...fns) { + return Composer.entityText('email', email, ...fns) + } + + static phone (number, ...fns) { + return Composer.entityText('phone_number', number, ...fns) + } + + static url (url, ...fns) { + return Composer.entityText('url', url, ...fns) + } + + static textLink (link, ...fns) { + return Composer.entityText('text_link', link, ...fns) + } + + static textMention (mention, ...fns) { + return Composer.entityText('text_mention', mention, ...fns) + } + + static mention (mention, ...fns) { + return Composer.entityText('mention', normalizeTextArguments(mention, '@'), ...fns) + } + + static hashtag (hashtag, ...fns) { + return Composer.entityText('hashtag', normalizeTextArguments(hashtag, '#'), ...fns) + } + + static cashtag (cashtag, ...fns) { + return Composer.entityText('cashtag', normalizeTextArguments(cashtag, '$'), ...fns) + } + + static match (triggers, ...fns) { + return Composer.optional((ctx) => { + const text = ( + (ctx.message && (ctx.message.caption || ctx.message.text)) || + (ctx.callbackQuery && ctx.callbackQuery.data) || + (ctx.inlineQuery && ctx.inlineQuery.query) + ) + for (const trigger of triggers) { + ctx.match = trigger(text, ctx) + if (ctx.match) { + return true + } + } + }, ...fns) + } + + static hears (triggers, ...fns) { + return Composer.mount('text', Composer.match(normalizeTriggers(triggers), ...fns)) + } + + static command (command, ...fns) { + if (fns.length === 0) { + return Composer.entity(['bot_command'], command) + } + const commands = normalizeTextArguments(command, '/') + return Composer.mount('text', Composer.lazy((ctx) => { + const groupCommands = ctx.me && (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup') + ? commands.map((command) => `${command}@${ctx.me}`) + : [] + return Composer.entity(({ offset, type }, value) => + offset === 0 && + type === 'bot_command' && + (commands.includes(value) || groupCommands.includes(value)) + , ...fns) + })) + } + + static action (triggers, ...fns) { + return Composer.mount('callback_query', Composer.match(normalizeTriggers(triggers), ...fns)) + } + + static inlineQuery (triggers, ...fns) { + return Composer.mount('inline_query', Composer.match(normalizeTriggers(triggers), ...fns)) + } + + static acl (userId, ...fns) { + if (typeof userId === 'function') { + return Composer.optional(userId, ...fns) + } + const allowed = Array.isArray(userId) ? userId : [userId] + return Composer.optional((ctx) => !ctx.from || allowed.includes(ctx.from.id), ...fns) + } + + static memberStatus (status, ...fns) { + const statuses = Array.isArray(status) ? status : [status] + return Composer.optional((ctx) => ctx.message && ctx.getChatMember(ctx.message.from.id) + .then(member => member && statuses.includes(member.status)) + , ...fns) + } + + static admin (...fns) { + return Composer.memberStatus(['administrator', 'creator'], ...fns) + } + + static creator (...fns) { + return Composer.memberStatus('creator', ...fns) + } + + static chatType (type, ...fns) { + const types = Array.isArray(type) ? type : [type] + return Composer.optional((ctx) => ctx.chat && types.includes(ctx.chat.type), ...fns) + } + + static privateChat (...fns) { + return Composer.chatType('private', ...fns) + } + + static groupChat (...fns) { + return Composer.chatType(['group', 'supergroup'], ...fns) + } + + static gameQuery (...fns) { + return Composer.mount('callback_query', Composer.optional((ctx) => ctx.callbackQuery.game_short_name, ...fns)) + } + + static unwrap (handler) { + if (!handler) { + throw new Error('Handler is undefined') + } + return typeof handler.middleware === 'function' + ? handler.middleware() + : handler + } + + static compose (middlewares) { + if (!Array.isArray(middlewares)) { + throw new Error('Middlewares must be an array') + } + if (middlewares.length === 0) { + return Composer.safePassThru() + } + if (middlewares.length === 1) { + return Composer.unwrap(middlewares[0]) + } + return (ctx, next) => { + let index = -1 + return execute(0, ctx) + function execute (i, context) { + if (!(context instanceof Context)) { + return Promise.reject(new Error('next(ctx) called with invalid context')) + } + if (i <= index) { + return Promise.reject(new Error('next() called multiple times')) + } + index = i + const handler = middlewares[i] ? Composer.unwrap(middlewares[i]) : next + if (!handler) { + return Promise.resolve() + } + try { + return Promise.resolve( + handler(context, (ctx = context) => execute(i + 1, ctx)) + ) + } catch (err) { + return Promise.reject(err) + } + } + } + } +} + +function normalizeTriggers (triggers) { + if (!Array.isArray(triggers)) { + triggers = [triggers] + } + return triggers.map((trigger) => { + if (!trigger) { + throw new Error('Invalid trigger') + } + if (typeof trigger === 'function') { + return trigger + } + if (trigger instanceof RegExp) { + return (value) => { + trigger.lastIndex = 0 + return trigger.exec(value || '') + } + } + return (value) => trigger === value ? value : null + }) +} + +function normalizeTextArguments (argument, prefix) { + const args = Array.isArray(argument) ? argument : [argument] + return args + .filter(Boolean) + .map((arg) => prefix && typeof arg === 'string' && !arg.startsWith(prefix) ? `${prefix}${arg}` : arg) +} + +module.exports = Composer diff --git a/TaskServer/node_modules/telegraf/context.js b/TaskServer/node_modules/telegraf/context.js new file mode 100644 index 000000000..0b497d1dc --- /dev/null +++ b/TaskServer/node_modules/telegraf/context.js @@ -0,0 +1,568 @@ +const UpdateTypes = [ + 'callback_query', + 'channel_post', + 'chosen_inline_result', + 'edited_channel_post', + 'edited_message', + 'inline_query', + 'shipping_query', + 'pre_checkout_query', + 'message', + 'poll', + 'poll_answer' +] + +const MessageSubTypes = [ + 'voice', + 'video_note', + 'video', + 'animation', + 'venue', + 'text', + 'supergroup_chat_created', + 'successful_payment', + 'sticker', + 'pinned_message', + 'photo', + 'new_chat_title', + 'new_chat_photo', + 'new_chat_members', + 'migrate_to_chat_id', + 'migrate_from_chat_id', + 'location', + 'left_chat_member', + 'invoice', + 'group_chat_created', + 'game', + 'document', + 'delete_chat_photo', + 'contact', + 'channel_chat_created', + 'audio', + 'connected_website', + 'passport_data', + 'poll', + 'forward_date' +] + +const MessageSubTypesMapping = { + forward_date: 'forward' +} + +class TelegrafContext { + constructor (update, telegram, options) { + this.tg = telegram + this.update = update + this.options = options + this.updateType = UpdateTypes.find((key) => key in this.update) + if (this.updateType === 'message' || (this.options.channelMode && this.updateType === 'channel_post')) { + this.updateSubTypes = MessageSubTypes + .filter((key) => key in this.update[this.updateType]) + .map((type) => MessageSubTypesMapping[type] || type) + } else { + this.updateSubTypes = [] + } + Object.getOwnPropertyNames(TelegrafContext.prototype) + .filter((key) => key !== 'constructor' && typeof this[key] === 'function') + .forEach((key) => (this[key] = this[key].bind(this))) + } + + get me () { + return this.options && this.options.username + } + + get telegram () { + return this.tg + } + + get message () { + return this.update.message + } + + get editedMessage () { + return this.update.edited_message + } + + get inlineQuery () { + return this.update.inline_query + } + + get shippingQuery () { + return this.update.shipping_query + } + + get preCheckoutQuery () { + return this.update.pre_checkout_query + } + + get chosenInlineResult () { + return this.update.chosen_inline_result + } + + get channelPost () { + return this.update.channel_post + } + + get editedChannelPost () { + return this.update.edited_channel_post + } + + get callbackQuery () { + return this.update.callback_query + } + + get poll () { + return this.update.poll + } + + get pollAnswer () { + return this.update.poll_answer + } + + get chat () { + return (this.message && this.message.chat) || + (this.editedMessage && this.editedMessage.chat) || + (this.callbackQuery && this.callbackQuery.message && this.callbackQuery.message.chat) || + (this.channelPost && this.channelPost.chat) || + (this.editedChannelPost && this.editedChannelPost.chat) + } + + get from () { + return (this.message && this.message.from) || + (this.editedMessage && this.editedMessage.from) || + (this.callbackQuery && this.callbackQuery.from) || + (this.inlineQuery && this.inlineQuery.from) || + (this.channelPost && this.channelPost.from) || + (this.editedChannelPost && this.editedChannelPost.from) || + (this.shippingQuery && this.shippingQuery.from) || + (this.preCheckoutQuery && this.preCheckoutQuery.from) || + (this.chosenInlineResult && this.chosenInlineResult.from) + } + + get inlineMessageId () { + return (this.callbackQuery && this.callbackQuery.inline_message_id) || (this.chosenInlineResult && this.chosenInlineResult.inline_message_id) + } + + get passportData () { + return this.message && this.message.passport_data + } + + get state () { + if (!this.contextState) { + this.contextState = {} + } + return this.contextState + } + + set state (value) { + this.contextState = { ...value } + } + + get webhookReply () { + return this.tg.webhookReply + } + + set webhookReply (enable) { + this.tg.webhookReply = enable + } + + assert (value, method) { + if (!value) { + throw new Error(`Telegraf: "${method}" isn't available for "${this.updateType}::${this.updateSubTypes}"`) + } + } + + answerInlineQuery (...args) { + this.assert(this.inlineQuery, 'answerInlineQuery') + return this.telegram.answerInlineQuery(this.inlineQuery.id, ...args) + } + + answerCbQuery (...args) { + this.assert(this.callbackQuery, 'answerCbQuery') + return this.telegram.answerCbQuery(this.callbackQuery.id, ...args) + } + + answerGameQuery (...args) { + this.assert(this.callbackQuery, 'answerGameQuery') + return this.telegram.answerGameQuery(this.callbackQuery.id, ...args) + } + + answerShippingQuery (...args) { + this.assert(this.shippingQuery, 'answerShippingQuery') + return this.telegram.answerShippingQuery(this.shippingQuery.id, ...args) + } + + answerPreCheckoutQuery (...args) { + this.assert(this.preCheckoutQuery, 'answerPreCheckoutQuery') + return this.telegram.answerPreCheckoutQuery(this.preCheckoutQuery.id, ...args) + } + + editMessageText (text, extra) { + this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageText') + return this.inlineMessageId + ? this.telegram.editMessageText( + undefined, + undefined, + this.inlineMessageId, + text, + extra + ) + : this.telegram.editMessageText( + this.chat.id, + this.callbackQuery.message.message_id, + undefined, + text, + extra + ) + } + + editMessageCaption (caption, extra) { + this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageCaption') + return this.inlineMessageId + ? this.telegram.editMessageCaption( + undefined, + undefined, + this.inlineMessageId, + caption, + extra + ) + : this.telegram.editMessageCaption( + this.chat.id, + this.callbackQuery.message.message_id, + undefined, + caption, + extra + ) + } + + editMessageMedia (media, extra) { + this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageMedia') + return this.inlineMessageId + ? this.telegram.editMessageMedia( + undefined, + undefined, + this.inlineMessageId, + media, + extra + ) + : this.telegram.editMessageMedia( + this.chat.id, + this.callbackQuery.message.message_id, + undefined, + media, + extra + ) + } + + editMessageReplyMarkup (markup) { + this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageReplyMarkup') + return this.inlineMessageId + ? this.telegram.editMessageReplyMarkup( + undefined, + undefined, + this.inlineMessageId, + markup + ) + : this.telegram.editMessageReplyMarkup( + this.chat.id, + this.callbackQuery.message.message_id, + undefined, + markup + ) + } + + editMessageLiveLocation (latitude, longitude, markup) { + this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageLiveLocation') + return this.inlineMessageId + ? this.telegram.editMessageLiveLocation( + latitude, + longitude, + undefined, + undefined, + this.inlineMessageId, + markup + ) + : this.telegram.editMessageLiveLocation( + latitude, + longitude, + this.chat.id, + this.callbackQuery.message.message_id, + undefined, + markup + ) + } + + stopMessageLiveLocation (markup) { + this.assert(this.callbackQuery || this.inlineMessageId, 'stopMessageLiveLocation') + return this.inlineMessageId + ? this.telegram.stopMessageLiveLocation( + undefined, + undefined, + this.inlineMessageId, + markup + ) + : this.telegram.stopMessageLiveLocation( + this.chat.id, + this.callbackQuery.message.message_id, + undefined, + markup + ) + } + + reply (...args) { + this.assert(this.chat, 'reply') + return this.telegram.sendMessage(this.chat.id, ...args) + } + + getChat (...args) { + this.assert(this.chat, 'getChat') + return this.telegram.getChat(this.chat.id, ...args) + } + + exportChatInviteLink (...args) { + this.assert(this.chat, 'exportChatInviteLink') + return this.telegram.exportChatInviteLink(this.chat.id, ...args) + } + + kickChatMember (...args) { + this.assert(this.chat, 'kickChatMember') + return this.telegram.kickChatMember(this.chat.id, ...args) + } + + restrictChatMember (...args) { + this.assert(this.chat, 'restrictChatMember') + return this.telegram.restrictChatMember(this.chat.id, ...args) + } + + promoteChatMember (...args) { + this.assert(this.chat, 'promoteChatMember') + return this.telegram.promoteChatMember(this.chat.id, ...args) + } + + setChatAdministratorCustomTitle (...args) { + this.assert(this.chat, 'setChatAdministratorCustomTitle') + return this.telegram.setChatAdministratorCustomTitle(this.chat.id, ...args) + } + + setChatPhoto (...args) { + this.assert(this.chat, 'setChatPhoto') + return this.telegram.setChatPhoto(this.chat.id, ...args) + } + + deleteChatPhoto (...args) { + this.assert(this.chat, 'deleteChatPhoto') + return this.telegram.deleteChatPhoto(this.chat.id, ...args) + } + + setChatTitle (...args) { + this.assert(this.chat, 'setChatTitle') + return this.telegram.setChatTitle(this.chat.id, ...args) + } + + setChatDescription (...args) { + this.assert(this.chat, 'setChatDescription') + return this.telegram.setChatDescription(this.chat.id, ...args) + } + + pinChatMessage (...args) { + this.assert(this.chat, 'pinChatMessage') + return this.telegram.pinChatMessage(this.chat.id, ...args) + } + + unpinChatMessage (...args) { + this.assert(this.chat, 'unpinChatMessage') + return this.telegram.unpinChatMessage(this.chat.id, ...args) + } + + leaveChat (...args) { + this.assert(this.chat, 'leaveChat') + return this.telegram.leaveChat(this.chat.id, ...args) + } + + setChatPermissions (...args) { + this.assert(this.chat, 'setChatPermissions') + return this.telegram.setChatPermissions(this.chat.id, ...args) + } + + getChatAdministrators (...args) { + this.assert(this.chat, 'getChatAdministrators') + return this.telegram.getChatAdministrators(this.chat.id, ...args) + } + + getChatMember (...args) { + this.assert(this.chat, 'getChatMember') + return this.telegram.getChatMember(this.chat.id, ...args) + } + + getChatMembersCount (...args) { + this.assert(this.chat, 'getChatMembersCount') + return this.telegram.getChatMembersCount(this.chat.id, ...args) + } + + setPassportDataErrors (errors) { + this.assert(this.chat, 'setPassportDataErrors') + return this.telegram.setPassportDataErrors(this.from.id, errors) + } + + replyWithPhoto (...args) { + this.assert(this.chat, 'replyWithPhoto') + return this.telegram.sendPhoto(this.chat.id, ...args) + } + + replyWithMediaGroup (...args) { + this.assert(this.chat, 'replyWithMediaGroup') + return this.telegram.sendMediaGroup(this.chat.id, ...args) + } + + replyWithAudio (...args) { + this.assert(this.chat, 'replyWithAudio') + return this.telegram.sendAudio(this.chat.id, ...args) + } + + replyWithDocument (...args) { + this.assert(this.chat, 'replyWithDocument') + return this.telegram.sendDocument(this.chat.id, ...args) + } + + replyWithSticker (...args) { + this.assert(this.chat, 'replyWithSticker') + return this.telegram.sendSticker(this.chat.id, ...args) + } + + replyWithVideo (...args) { + this.assert(this.chat, 'replyWithVideo') + return this.telegram.sendVideo(this.chat.id, ...args) + } + + replyWithAnimation (...args) { + this.assert(this.chat, 'replyWithAnimation') + return this.telegram.sendAnimation(this.chat.id, ...args) + } + + replyWithVideoNote (...args) { + this.assert(this.chat, 'replyWithVideoNote') + return this.telegram.sendVideoNote(this.chat.id, ...args) + } + + replyWithInvoice (...args) { + this.assert(this.chat, 'replyWithInvoice') + return this.telegram.sendInvoice(this.chat.id, ...args) + } + + replyWithGame (...args) { + this.assert(this.chat, 'replyWithGame') + return this.telegram.sendGame(this.chat.id, ...args) + } + + replyWithVoice (...args) { + this.assert(this.chat, 'replyWithVoice') + return this.telegram.sendVoice(this.chat.id, ...args) + } + + replyWithPoll (...args) { + this.assert(this.chat, 'replyWithPoll') + return this.telegram.sendPoll(this.chat.id, ...args) + } + + replyWithQuiz (...args) { + this.assert(this.chat, 'replyWithQuiz') + return this.telegram.sendQuiz(this.chat.id, ...args) + } + + stopPoll (...args) { + this.assert(this.chat, 'stopPoll') + return this.telegram.stopPoll(this.chat.id, ...args) + } + + replyWithChatAction (...args) { + this.assert(this.chat, 'replyWithChatAction') + return this.telegram.sendChatAction(this.chat.id, ...args) + } + + replyWithLocation (...args) { + this.assert(this.chat, 'replyWithLocation') + return this.telegram.sendLocation(this.chat.id, ...args) + } + + replyWithVenue (...args) { + this.assert(this.chat, 'replyWithVenue') + return this.telegram.sendVenue(this.chat.id, ...args) + } + + replyWithContact (...args) { + this.assert(this.from, 'replyWithContact') + return this.telegram.sendContact(this.chat.id, ...args) + } + + getStickerSet (setName) { + return this.telegram.getStickerSet(setName) + } + + setChatStickerSet (setName) { + this.assert(this.chat, 'setChatStickerSet') + return this.telegram.setChatStickerSet(this.chat.id, setName) + } + + deleteChatStickerSet () { + this.assert(this.chat, 'deleteChatStickerSet') + return this.telegram.deleteChatStickerSet(this.chat.id) + } + + setStickerPositionInSet (sticker, position) { + return this.telegram.setStickerPositionInSet(sticker, position) + } + + deleteStickerFromSet (sticker) { + return this.telegram.deleteStickerFromSet(sticker) + } + + uploadStickerFile (...args) { + this.assert(this.from, 'uploadStickerFile') + return this.telegram.uploadStickerFile(this.from.id, ...args) + } + + createNewStickerSet (...args) { + this.assert(this.from, 'createNewStickerSet') + return this.telegram.createNewStickerSet(this.from.id, ...args) + } + + addStickerToSet (...args) { + this.assert(this.from, 'addStickerToSet') + return this.telegram.addStickerToSet(this.from.id, ...args) + } + + replyWithMarkdown (markdown, extra) { + return this.reply(markdown, { parse_mode: 'Markdown', ...extra }) + } + + replyWithHTML (html, extra) { + return this.reply(html, { parse_mode: 'HTML', ...extra }) + } + + deleteMessage (messageId) { + this.assert(this.chat, 'deleteMessage') + if (typeof messageId !== 'undefined') { + return this.telegram.deleteMessage(this.chat.id, messageId) + } + const message = this.message || + this.editedMessage || + this.channelPost || + this.editedChannelPost || + (this.callbackQuery && this.callbackQuery.message) + this.assert(message, 'deleteMessage') + return this.telegram.deleteMessage(this.chat.id, message.message_id) + } + + forwardMessage (chatId, extra) { + this.assert(this.chat, 'forwardMessage') + const message = this.message || + this.editedMessage || + this.channelPost || + this.editedChannelPost || + (this.callbackQuery && this.callbackQuery.message) + this.assert(message, 'forwardMessage') + return this.telegram.forwardMessage(chatId, this.chat.id, message.message_id, extra) + } +} + +module.exports = TelegrafContext diff --git a/TaskServer/node_modules/telegraf/core/network/client.js b/TaskServer/node_modules/telegraf/core/network/client.js new file mode 100644 index 000000000..e53961b3d --- /dev/null +++ b/TaskServer/node_modules/telegraf/core/network/client.js @@ -0,0 +1,288 @@ +const debug = require('debug')('telegraf:client') +const crypto = require('crypto') +const fetch = require('node-fetch').default +const fs = require('fs') +const https = require('https') +const path = require('path') +const TelegramError = require('./error') +const MultipartStream = require('./multipart-stream') +const { isStream } = MultipartStream + +const WEBHOOK_BLACKLIST = [ + 'getChat', + 'getChatAdministrators', + 'getChatMember', + 'getChatMembersCount', + 'getFile', + 'getFileLink', + 'getGameHighScores', + 'getMe', + 'getUserProfilePhotos', + 'getWebhookInfo', + 'exportChatInviteLink' +] + +const DEFAULT_EXTENSIONS = { + audio: 'mp3', + photo: 'jpg', + sticker: 'webp', + video: 'mp4', + animation: 'mp4', + video_note: 'mp4', + voice: 'ogg' +} + +const DEFAULT_OPTIONS = { + apiRoot: 'https://api.telegram.org', + webhookReply: true, + agent: new https.Agent({ + keepAlive: true, + keepAliveMsecs: 10000 + }) +} + +const WEBHOOK_REPLY_STUB = { + webhook: true, + details: 'https://core.telegram.org/bots/api#making-requests-when-getting-updates' +} + +function safeJSONParse (text) { + try { + return JSON.parse(text) + } catch (err) { + debug('JSON parse failed', err) + } +} + +function includesMedia (payload) { + return Object.keys(payload).some( + (key) => { + const value = payload[key] + if (Array.isArray(value)) { + return value.some(({ media }) => media && typeof media === 'object' && (media.source || media.url)) + } + return (typeof value === 'object') && ( + value.source || + value.url || + (typeof value.media === 'object' && (value.media.source || value.media.url)) + ) + } + ) +} + +function buildJSONConfig (payload) { + return Promise.resolve({ + method: 'POST', + compress: true, + headers: { 'content-type': 'application/json', connection: 'keep-alive' }, + body: JSON.stringify(payload) + }) +} + +const FORM_DATA_JSON_FIELDS = [ + 'results', + 'reply_markup', + 'mask_position', + 'shipping_options', + 'errors' +] + +function buildFormDataConfig (payload, agent) { + for (const field of FORM_DATA_JSON_FIELDS) { + if (field in payload && typeof payload[field] !== 'string') { + payload[field] = JSON.stringify(payload[field]) + } + } + const boundary = crypto.randomBytes(32).toString('hex') + const formData = new MultipartStream(boundary) + const tasks = Object.keys(payload).map((key) => attachFormValue(formData, key, payload[key], agent)) + return Promise.all(tasks).then(() => { + return { + method: 'POST', + compress: true, + headers: { 'content-type': `multipart/form-data; boundary=${boundary}`, connection: 'keep-alive' }, + body: formData + } + }) +} + +function attachFormValue (form, id, value, agent) { + if (!value) { + return Promise.resolve() + } + const valueType = typeof value + if (valueType === 'string' || valueType === 'boolean' || valueType === 'number') { + form.addPart({ + headers: { 'content-disposition': `form-data; name="${id}"` }, + body: `${value}` + }) + return Promise.resolve() + } + if (id === 'thumb') { + const attachmentId = crypto.randomBytes(16).toString('hex') + return attachFormMedia(form, value, attachmentId, agent) + .then(() => form.addPart({ + headers: { 'content-disposition': `form-data; name="${id}"` }, + body: `attach://${attachmentId}` + })) + } + if (Array.isArray(value)) { + return Promise.all( + value.map((item) => { + if (typeof item.media !== 'object') { + return Promise.resolve(item) + } + const attachmentId = crypto.randomBytes(16).toString('hex') + return attachFormMedia(form, item.media, attachmentId, agent) + .then(() => ({ ...item, media: `attach://${attachmentId}` })) + }) + ).then((items) => form.addPart({ + headers: { 'content-disposition': `form-data; name="${id}"` }, + body: JSON.stringify(items) + })) + } + if (typeof value.media !== 'undefined' && typeof value.type !== 'undefined') { + const attachmentId = crypto.randomBytes(16).toString('hex') + return attachFormMedia(form, value.media, attachmentId, agent) + .then(() => form.addPart({ + headers: { 'content-disposition': `form-data; name="${id}"` }, + body: JSON.stringify({ + ...value, + media: `attach://${attachmentId}` + }) + })) + } + return attachFormMedia(form, value, id, agent) +} + +function attachFormMedia (form, media, id, agent) { + let fileName = media.filename || `${id}.${DEFAULT_EXTENSIONS[id] || 'dat'}` + if (media.url) { + return fetch(media.url, { agent }).then((res) => + form.addPart({ + headers: { 'content-disposition': `form-data; name="${id}"; filename="${fileName}"` }, + body: res.body + }) + ) + } + if (media.source) { + if (fs.existsSync(media.source)) { + fileName = media.filename || path.basename(media.source) + media.source = fs.createReadStream(media.source) + } + if (isStream(media.source) || Buffer.isBuffer(media.source)) { + form.addPart({ + headers: { 'content-disposition': `form-data; name="${id}"; filename="${fileName}"` }, + body: media.source + }) + } + } + return Promise.resolve() +} + +function isKoaResponse (response) { + return typeof response.set === 'function' && typeof response.header === 'object' +} + +function answerToWebhook (response, payload = {}, options) { + if (!includesMedia(payload)) { + if (isKoaResponse(response)) { + response.body = payload + return Promise.resolve(WEBHOOK_REPLY_STUB) + } + if (!response.headersSent) { + response.setHeader('content-type', 'application/json') + } + return new Promise((resolve) => { + if (response.end.length === 2) { + response.end(JSON.stringify(payload), 'utf-8') + return resolve(WEBHOOK_REPLY_STUB) + } + response.end(JSON.stringify(payload), 'utf-8', () => resolve(WEBHOOK_REPLY_STUB)) + }) + } + + return buildFormDataConfig(payload, options.agent) + .then(({ headers, body }) => { + if (isKoaResponse(response)) { + Object.keys(headers).forEach(key => response.set(key, headers[key])) + response.body = body + return Promise.resolve(WEBHOOK_REPLY_STUB) + } + if (!response.headersSent) { + Object.keys(headers).forEach(key => response.setHeader(key, headers[key])) + } + return new Promise((resolve) => { + response.on('finish', () => resolve(WEBHOOK_REPLY_STUB)) + body.pipe(response) + }) + }) +} + +class ApiClient { + constructor (token, options, webhookResponse) { + this.token = token + this.options = { + ...DEFAULT_OPTIONS, + ...options + } + if (this.options.apiRoot.startsWith('http://')) { + this.options.agent = null + } + this.response = webhookResponse + } + + set webhookReply (enable) { + this.options.webhookReply = enable + } + + get webhookReply () { + return this.options.webhookReply + } + + callApi (method, data = {}) { + const { token, options, response, responseEnd } = this + + const payload = Object.keys(data) + .filter((key) => typeof data[key] !== 'undefined' && data[key] !== null) + .reduce((acc, key) => ({ ...acc, [key]: data[key] }), {}) + + if (options.webhookReply && response && !responseEnd && !WEBHOOK_BLACKLIST.includes(method)) { + debug('Call via webhook', method, payload) + this.responseEnd = true + return answerToWebhook(response, { method, ...payload }, options) + } + + if (!token) { + throw new TelegramError({ error_code: 401, description: 'Bot Token is required' }) + } + + debug('HTTP call', method, payload) + const buildConfig = includesMedia(payload) + ? buildFormDataConfig({ method, ...payload }, options.agent) + : buildJSONConfig(payload) + return buildConfig + .then((config) => { + const apiUrl = `${options.apiRoot}/bot${token}/${method}` + config.agent = options.agent + return fetch(apiUrl, config) + }) + .then((res) => res.text()) + .then((text) => { + return safeJSONParse(text) || { + error_code: 500, + description: 'Unsupported http response from Telegram', + response: text + } + }) + .then((data) => { + if (!data.ok) { + debug('API call failed', data) + throw new TelegramError(data, { method, payload }) + } + return data.result + }) + } +} + +module.exports = ApiClient diff --git a/TaskServer/node_modules/telegraf/core/network/error.js b/TaskServer/node_modules/telegraf/core/network/error.js new file mode 100644 index 000000000..4066519a1 --- /dev/null +++ b/TaskServer/node_modules/telegraf/core/network/error.js @@ -0,0 +1,12 @@ +class TelegramError extends Error { + constructor (payload = {}, on) { + super(`${payload.error_code}: ${payload.description}`) + this.code = payload.error_code + this.response = payload + this.description = payload.description + this.parameters = payload.parameters || {} + this.on = on || {} + } +} + +module.exports = TelegramError diff --git a/TaskServer/node_modules/telegraf/core/network/multipart-stream.js b/TaskServer/node_modules/telegraf/core/network/multipart-stream.js new file mode 100644 index 000000000..bbea7a6de --- /dev/null +++ b/TaskServer/node_modules/telegraf/core/network/multipart-stream.js @@ -0,0 +1,37 @@ +const stream = require('stream') +const { SandwichStream } = require('sandwich-stream') +const CRNL = '\r\n' + +class MultipartStream extends SandwichStream { + constructor (boundary) { + super({ + head: `--${boundary}${CRNL}`, + tail: `${CRNL}--${boundary}--`, + separator: `${CRNL}--${boundary}${CRNL}` + }) + } + + addPart (part) { + part = part || {} + const partStream = new stream.PassThrough() + if (part.headers) { + for (const key in part.headers) { + const header = part.headers[key] + partStream.write(`${key}:${header}${CRNL}`) + } + } + partStream.write(CRNL) + if (MultipartStream.isStream(part.body)) { + part.body.pipe(partStream) + } else { + partStream.end(part.body) + } + this.add(partStream) + } + + static isStream (stream) { + return stream && typeof stream === 'object' && typeof stream.pipe === 'function' + } +} + +module.exports = MultipartStream diff --git a/TaskServer/node_modules/telegraf/core/network/webhook.js b/TaskServer/node_modules/telegraf/core/network/webhook.js new file mode 100644 index 000000000..ec412930a --- /dev/null +++ b/TaskServer/node_modules/telegraf/core/network/webhook.js @@ -0,0 +1,39 @@ +const debug = require('debug')('telegraf:webhook') + +module.exports = function (hookPath, updateHandler, errorHandler) { + return (req, res, next) => { + debug('Incoming request', req.method, req.url) + if (req.method !== 'POST' || req.url !== hookPath) { + if (typeof next === 'function') { + return next() + } + res.statusCode = 403 + return res.end() + } + let body = '' + req.on('data', (chunk) => { + body += chunk.toString() + }) + req.on('end', () => { + let update = {} + try { + update = JSON.parse(body) + } catch (error) { + res.writeHead(415) + res.end() + return errorHandler(error) + } + updateHandler(update, res) + .then(() => { + if (!res.finished) { + res.end() + } + }) + .catch((err) => { + debug('Webhook error', err) + res.writeHead(500) + res.end() + }) + }) + } +} diff --git a/TaskServer/node_modules/telegraf/core/replicators.js b/TaskServer/node_modules/telegraf/core/replicators.js new file mode 100644 index 000000000..1d93758e2 --- /dev/null +++ b/TaskServer/node_modules/telegraf/core/replicators.js @@ -0,0 +1,140 @@ +const { formatHTML } = require('../markup') + +module.exports = { + copyMethods: { + audio: 'sendAudio', + contact: 'sendContact', + document: 'sendDocument', + location: 'sendLocation', + photo: 'sendPhoto', + sticker: 'sendSticker', + text: 'sendMessage', + venue: 'sendVenue', + video: 'sendVideo', + video_note: 'sendVideoNote', + animation: 'sendAnimation', + voice: 'sendVoice', + poll: 'sendPoll' + }, + text: (message) => { + const entities = message.entities || [] + return { + reply_markup: message.reply_markup, + parse_mode: entities.length > 0 ? 'HTML' : '', + text: formatHTML(message.text, entities) + } + }, + contact: (message) => { + return { + reply_markup: message.reply_markup, + phone_number: message.contact.phone_number, + first_name: message.contact.first_name, + last_name: message.contact.last_name + } + }, + location: (message) => { + return { + reply_markup: message.reply_markup, + latitude: message.location.latitude, + longitude: message.location.longitude + } + }, + venue: (message) => { + return { + reply_markup: message.reply_markup, + latitude: message.venue.location.latitude, + longitude: message.venue.location.longitude, + title: message.venue.title, + address: message.venue.address, + foursquare_id: message.venue.foursquare_id + } + }, + voice: (message) => { + const entities = message.caption_entities || [] + return { + reply_markup: message.reply_markup, + voice: message.voice.file_id, + duration: message.voice.duration, + caption: formatHTML(message.caption, entities), + parse_mode: entities.length > 0 ? 'HTML' : '' + } + }, + audio: (message) => { + const entities = message.caption_entities || [] + return { + reply_markup: message.reply_markup, + audio: message.audio.file_id, + thumb: message.audio.thumb, + duration: message.audio.duration, + performer: message.audio.performer, + title: message.audio.title, + caption: formatHTML(message.caption, entities), + parse_mode: entities.length > 0 ? 'HTML' : '' + } + }, + video: (message) => { + const entities = message.caption_entities || [] + return { + reply_markup: message.reply_markup, + video: message.video.file_id, + thumb: message.video.thumb, + caption: formatHTML(message.caption, entities), + parse_mode: entities.length > 0 ? 'HTML' : '', + duration: message.video.duration, + width: message.video.width, + height: message.video.height, + supports_streaming: !!message.video.supports_streaming + } + }, + document: (message) => { + const entities = message.caption_entities || [] + return { + reply_markup: message.reply_markup, + document: message.document.file_id, + caption: formatHTML(message.caption, entities), + parse_mode: entities.length > 0 ? 'HTML' : '' + } + }, + sticker: (message) => { + return { + reply_markup: message.reply_markup, + sticker: message.sticker.file_id + } + }, + photo: (message) => { + const entities = message.caption_entities || [] + return { + reply_markup: message.reply_markup, + photo: message.photo[message.photo.length - 1].file_id, + parse_mode: entities.length > 0 ? 'HTML' : '', + caption: formatHTML(message.caption, entities) + } + }, + video_note: (message) => { + return { + reply_markup: message.reply_markup, + video_note: message.video_note.file_id, + thumb: message.video_note.thumb, + length: message.video_note.length, + duration: message.video_note.duration + } + }, + animation: (message) => { + return { + reply_markup: message.reply_markup, + animation: message.animation.file_id, + thumb: message.animation.thumb, + duration: message.animation.duration + } + }, + poll: (message) => { + return { + question: message.poll.question, + type: message.poll.type, + is_anonymous: message.poll.is_anonymous, + allows_multiple_answers: message.poll.allows_multiple_answers, + correct_option_id: message.poll.correct_option_id, + options: message.poll.options.map(({ text }) => text) + } + } +} diff --git a/TaskServer/node_modules/telegraf/extra.js b/TaskServer/node_modules/telegraf/extra.js new file mode 100644 index 000000000..61fea593f --- /dev/null +++ b/TaskServer/node_modules/telegraf/extra.js @@ -0,0 +1,85 @@ +const Markup = require('./markup') + +class Extra { + constructor (opts) { + this.load(opts) + } + + load (opts = {}) { + return Object.assign(this, opts) + } + + inReplyTo (messageId) { + this.reply_to_message_id = messageId + return this + } + + notifications (value = true) { + this.disable_notification = !value + return this + } + + webPreview (value = true) { + this.disable_web_page_preview = !value + return this + } + + markup (markup) { + if (typeof markup === 'function') { + markup = markup(new Markup()) + } + this.reply_markup = { ...markup } + return this + } + + HTML (value = true) { + this.parse_mode = value ? 'HTML' : undefined + return this + } + + markdown (value = true) { + this.parse_mode = value ? 'Markdown' : undefined + return this + } + + caption (caption = '') { + this.caption = caption + return this + } + + static inReplyTo (messageId) { + return new Extra().inReplyTo(messageId) + } + + static notifications (value) { + return new Extra().notifications(value) + } + + static webPreview (value) { + return new Extra().webPreview(value) + } + + static load (opts) { + return new Extra(opts) + } + + static markup (markup) { + return new Extra().markup(markup) + } + + static HTML (value) { + return new Extra().HTML(value) + } + + static markdown (value) { + return new Extra().markdown(value) + } + + static caption (caption) { + return new Extra().caption(caption) + } +} + +Extra.Markup = Markup + +module.exports = Extra diff --git a/TaskServer/node_modules/telegraf/markup.js b/TaskServer/node_modules/telegraf/markup.js new file mode 100644 index 000000000..2d16f3abc --- /dev/null +++ b/TaskServer/node_modules/telegraf/markup.js @@ -0,0 +1,270 @@ +class Markup { + forceReply (value = true) { + this.force_reply = value + return this + } + + removeKeyboard (value = true) { + this.remove_keyboard = value + return this + } + + selective (value = true) { + this.selective = value + return this + } + + extra (options) { + return { + reply_markup: { ...this }, + ...options + } + } + + keyboard (buttons, options) { + const keyboard = buildKeyboard(buttons, { columns: 1, ...options }) + if (keyboard && keyboard.length > 0) { + this.keyboard = keyboard + } + return this + } + + resize (value = true) { + this.resize_keyboard = value + return this + } + + oneTime (value = true) { + this.one_time_keyboard = value + return this + } + + inlineKeyboard (buttons, options) { + const keyboard = buildKeyboard(buttons, { columns: buttons.length, ...options }) + if (keyboard && keyboard.length > 0) { + this.inline_keyboard = keyboard + } + return this + } + + button (text, hide) { + return Markup.button(text, hide) + } + + contactRequestButton (text, hide) { + return Markup.contactRequestButton(text, hide) + } + + locationRequestButton (text, hide) { + return Markup.locationRequestButton(text, hide) + } + + urlButton (text, url, hide) { + return Markup.urlButton(text, url, hide) + } + + callbackButton (text, data, hide) { + return Markup.callbackButton(text, data, hide) + } + + switchToChatButton (text, value, hide) { + return Markup.switchToChatButton(text, value, hide) + } + + switchToCurrentChatButton (text, value, hide) { + return Markup.switchToCurrentChatButton(text, value, hide) + } + + gameButton (text, hide) { + return Markup.gameButton(text, hide) + } + + payButton (text, hide) { + return Markup.payButton(text, hide) + } + + loginButton (text, url, opts, hide) { + return Markup.loginButton(text, url, opts, hide) + } + + static removeKeyboard (value) { + return new Markup().removeKeyboard(value) + } + + static forceReply (value) { + return new Markup().forceReply(value) + } + + static keyboard (buttons, options) { + return new Markup().keyboard(buttons, options) + } + + static inlineKeyboard (buttons, options) { + return new Markup().inlineKeyboard(buttons, options) + } + + static resize (value = true) { + return new Markup().resize(value) + } + + static selective (value = true) { + return new Markup().selective(value) + } + + static oneTime (value = true) { + return new Markup().oneTime(value) + } + + static button (text, hide = false) { + return { text: text, hide: hide } + } + + static contactRequestButton (text, hide = false) { + return { text: text, request_contact: true, hide: hide } + } + + static locationRequestButton (text, hide = false) { + return { text: text, request_location: true, hide: hide } + } + + static pollRequestButton (text, type, hide = false) { + return { text: text, request_poll: { type }, hide: hide } + } + + static urlButton (text, url, hide = false) { + return { text: text, url: url, hide: hide } + } + + static callbackButton (text, data, hide = false) { + return { text: text, callback_data: data, hide: hide } + } + + static switchToChatButton (text, value, hide = false) { + return { text: text, switch_inline_query: value, hide: hide } + } + + static switchToCurrentChatButton (text, value, hide = false) { + return { text: text, switch_inline_query_current_chat: value, hide: hide } + } + + static gameButton (text, hide = false) { + return { text: text, callback_game: {}, hide: hide } + } + + static payButton (text, hide = false) { + return { text: text, pay: true, hide: hide } + } + + static loginButton (text, url, opts = {}, hide = false) { + return { + text: text, + login_url: { ...opts, url: url }, + hide: hide + } + } + + static formatHTML (text, entities) { + const chars = [...text] + const available = [...entities] + const opened = [] + const result = [] + for (let offset = 0; offset < chars.length; offset++) { + while (true) { + const index = available.findIndex((entity) => entity.offset === offset) + if (index === -1) { + break + } + const entity = available[index] + switch (entity.type) { + case 'bold': + result.push('') + break + case 'italic': + result.push('') + break + case 'code': + result.push('') + break + case 'pre': + result.push('
')
+            break
+          case 'strikethrough':
+            result.push('')
+            break
+          case 'underline':
+            result.push('')
+            break
+          case 'text_link':
+            result.push(``)
+            break
+        }
+        opened.unshift(entity)
+        available.splice(index, 1)
+      }
+
+      result.push(chars[offset])
+
+      while (true) {
+        const index = opened.findIndex((entity) => entity.offset + entity.length - 1 === offset)
+        if (index === -1) {
+          break
+        }
+        const entity = opened[index]
+        switch (entity.type) {
+          case 'bold':
+            result.push('')
+            break
+          case 'italic':
+            result.push('')
+            break
+          case 'code':
+            result.push('')
+            break
+          case 'pre':
+            result.push('
') + break + case 'strikethrough': + result.push('') + break + case 'underline': + result.push('') + break + case 'text_link': + result.push('') + break + } + opened.splice(index, 1) + } + } + return result.join('') + } +} + +function buildKeyboard (buttons, options) { + const result = [] + if (!Array.isArray(buttons)) { + return result + } + if (buttons.find(Array.isArray)) { + return buttons.map(row => row.filter((button) => !button.hide)) + } + const wrapFn = options.wrap + ? options.wrap + : (btn, index, currentRow) => currentRow.length >= options.columns + let currentRow = [] + let index = 0 + for (const btn of buttons.filter((button) => !button.hide)) { + if (wrapFn(btn, index, currentRow) && currentRow.length > 0) { + result.push(currentRow) + currentRow = [] + } + currentRow.push(btn) + index++ + } + if (currentRow.length > 0) { + result.push(currentRow) + } + return result +} + +module.exports = Markup diff --git a/TaskServer/node_modules/telegraf/package.json b/TaskServer/node_modules/telegraf/package.json new file mode 100644 index 000000000..3a67e53db --- /dev/null +++ b/TaskServer/node_modules/telegraf/package.json @@ -0,0 +1,95 @@ +{ + "_from": "telegraf@3.36.0", + "_id": "telegraf@3.36.0", + "_inBundle": false, + "_integrity": "sha512-9o6AJKRiTm5vMWYI6WpTfBHzu4FMpWBNKxvnMxRds/cbMY9RnsVVjdi8i4bFFlfd+xbi73EbrnI3dybayryICA==", + "_location": "/telegraf", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "telegraf@3.36.0", + "name": "telegraf", + "escapedName": "telegraf", + "rawSpec": "3.36.0", + "saveSpec": null, + "fetchSpec": "3.36.0" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.36.0.tgz", + "_shasum": "eab1160159df6033d3cb92ac990902523277822a", + "_spec": "telegraf@3.36.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer", + "author": { + "name": "Vitaly Domnikov", + "email": "oss@vitaly.codes" + }, + "bin": { + "telegraf": "./bin/telegraf" + }, + "bugs": { + "url": "https://github.com/telegraf/telegraf/issues" + }, + "bundleDependencies": false, + "dependencies": { + "@types/node": "^13.1.0", + "debug": "^4.0.1", + "minimist": "^1.2.0", + "module-alias": "^2.2.2", + "node-fetch": "^2.2.0", + "sandwich-stream": "^2.0.1", + "telegram-typings": "^3.6.0" + }, + "deprecated": false, + "description": "Modern Telegram Bot Framework", + "devDependencies": { + "ava": "^3.0.0", + "eslint": "^6.2.2", + "eslint-config-standard": "^14.1.0", + "eslint-plugin-ava": "^10.0.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-node": "^11.0.0", + "eslint-plugin-promise": "^4.0.0", + "eslint-plugin-standard": "^4.0.0", + "husky": "^4.2.0", + "typescript": "^3.0.1" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "bin/*", + "core/**/*.js", + "scenes/**/*.js", + "typings/*.d.ts", + "*.js" + ], + "homepage": "https://github.com/telegraf/telegraf#readme", + "keywords": [ + "telegraf", + "telegram", + "telegram bot api", + "bot", + "botapi", + "bot framework" + ], + "license": "MIT", + "main": "telegraf.js", + "name": "telegraf", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/telegraf/telegraf.git" + }, + "scripts": { + "lint": "eslint .", + "precommit": "npm run lint && npm run typecheck && npm test", + "test": "ava test/*", + "typecheck": "tsc" + }, + "type": "commonjs", + "types": "./typings/index.d.ts", + "version": "3.36.0" +} diff --git a/TaskServer/node_modules/telegraf/router.js b/TaskServer/node_modules/telegraf/router.js new file mode 100644 index 000000000..ab20a8533 --- /dev/null +++ b/TaskServer/node_modules/telegraf/router.js @@ -0,0 +1,43 @@ +const { compose, lazy, passThru } = require('./composer') + +class Router { + constructor (routeFn, handlers = new Map()) { + if (!routeFn) { + throw new Error('Missing routing function') + } + this.routeFn = routeFn + this.handlers = handlers + this.otherwiseHandler = passThru() + } + + on (route, ...fns) { + if (fns.length === 0) { + throw new TypeError('At least one handler must be provided') + } + this.handlers.set(route, compose(fns)) + return this + } + + otherwise (...fns) { + if (fns.length === 0) { + throw new TypeError('At least one otherwise handler must be provided') + } + this.otherwiseHandler = compose(fns) + return this + } + + middleware () { + return lazy((ctx) => { + return Promise.resolve(this.routeFn(ctx)).then((result) => { + if (!result || !result.route || !this.handlers.has(result.route)) { + return this.otherwiseHandler + } + Object.assign(ctx, result.context) + Object.assign(ctx.state, result.state) + return this.handlers.get(result.route) + }) + }) + } +} + +module.exports = Router diff --git a/TaskServer/node_modules/telegraf/scenes/base.js b/TaskServer/node_modules/telegraf/scenes/base.js new file mode 100644 index 000000000..c84a8679c --- /dev/null +++ b/TaskServer/node_modules/telegraf/scenes/base.js @@ -0,0 +1,46 @@ +const Composer = require('../composer') +const { compose } = Composer + +class BaseScene extends Composer { + constructor (id, options) { + const opts = { + handlers: [], + enterHandlers: [], + leaveHandlers: [], + ...options + } + super(...opts.handlers) + this.id = id + this.options = opts + this.enterHandler = compose(opts.enterHandlers) + this.leaveHandler = compose(opts.leaveHandlers) + } + + set ttl (value) { + this.options.ttl = value + } + + get ttl () { + return this.options.ttl + } + + enter (...fns) { + this.enterHandler = compose([this.enterHandler, ...fns]) + return this + } + + leave (...fns) { + this.leaveHandler = compose([this.leaveHandler, ...fns]) + return this + } + + enterMiddleware () { + return this.enterHandler + } + + leaveMiddleware () { + return this.leaveHandler + } +} + +module.exports = BaseScene diff --git a/TaskServer/node_modules/telegraf/scenes/context.js b/TaskServer/node_modules/telegraf/scenes/context.js new file mode 100644 index 000000000..c47e06e98 --- /dev/null +++ b/TaskServer/node_modules/telegraf/scenes/context.js @@ -0,0 +1,79 @@ +const debug = require('debug')('telegraf:scenes:context') +const { safePassThru } = require('../composer') + +const noop = () => Promise.resolve() +const now = () => Math.floor(new Date().getTime() / 1000) + +class SceneContext { + constructor (ctx, scenes, options) { + this.ctx = ctx + this.scenes = scenes + this.options = options + } + + get session () { + const sessionName = this.options.sessionName + let session = this.ctx[sessionName].__scenes || {} + if (session.expires < now()) { + session = {} + } + this.ctx[sessionName].__scenes = session + return session + } + + get state () { + this.session.state = this.session.state || {} + return this.session.state + } + + set state (value) { + this.session.state = { ...value } + } + + get current () { + const sceneId = this.session.current || this.options.default + return (sceneId && this.scenes.has(sceneId)) ? this.scenes.get(sceneId) : null + } + + reset () { + const sessionName = this.options.sessionName + delete this.ctx[sessionName].__scenes + } + + enter (sceneId, initialState, silent) { + if (!sceneId || !this.scenes.has(sceneId)) { + throw new Error(`Can't find scene: ${sceneId}`) + } + const leave = silent ? noop() : this.leave() + return leave.then(() => { + debug('Enter scene', sceneId, initialState, silent) + this.session.current = sceneId + this.state = initialState + const ttl = this.current.ttl || this.options.ttl + if (ttl) { + this.session.expires = now() + ttl + } + if (silent) { + return Promise.resolve() + } + const handler = typeof this.current.enterMiddleware === 'function' + ? this.current.enterMiddleware() + : this.current.middleware() + return handler(this.ctx, noop) + }) + } + + reenter () { + return this.enter(this.session.current, this.state) + } + + leave () { + debug('Leave scene') + const handler = this.current && this.current.leaveMiddleware + ? this.current.leaveMiddleware() + : safePassThru() + return handler(this.ctx, noop).then(() => this.reset()) + } +} + +module.exports = SceneContext diff --git a/TaskServer/node_modules/telegraf/scenes/wizard/context.js b/TaskServer/node_modules/telegraf/scenes/wizard/context.js new file mode 100644 index 000000000..8208fd3f2 --- /dev/null +++ b/TaskServer/node_modules/telegraf/scenes/wizard/context.js @@ -0,0 +1,28 @@ +class WizardContext { + constructor (ctx, steps) { + this.ctx = ctx + this.steps = steps + this.state = ctx.scene.state + this.cursor = ctx.scene.session.cursor || 0 + } + + get step () { + return this.cursor >= 0 && this.steps[this.cursor] + } + + selectStep (index) { + this.cursor = index + this.ctx.scene.session.cursor = index + return this + } + + next () { + return this.selectStep(this.cursor + 1) + } + + back () { + return this.selectStep(this.cursor - 1) + } +} + +module.exports = WizardContext diff --git a/TaskServer/node_modules/telegraf/scenes/wizard/index.js b/TaskServer/node_modules/telegraf/scenes/wizard/index.js new file mode 100644 index 000000000..fb312c614 --- /dev/null +++ b/TaskServer/node_modules/telegraf/scenes/wizard/index.js @@ -0,0 +1,51 @@ +const Composer = require('../../composer') +const WizardContext = require('./context') +const { compose, unwrap } = Composer + +class WizardScene extends Composer { + constructor (id, options, ...steps) { + super() + this.id = id + this.options = typeof options === 'function' + ? { steps: [options, ...steps], leaveHandlers: [] } + : { steps: steps, leaveHandlers: [], ...options } + this.leaveHandler = compose(this.options.leaveHandlers) + } + + set ttl (value) { + this.options.ttl = value + } + + get ttl () { + return this.options.ttl + } + + leave (...fns) { + this.leaveHandler = compose([this.leaveHandler, ...fns]) + return this + } + + leaveMiddleware () { + return this.leaveHandler + } + + middleware () { + return compose([ + (ctx, next) => { + const wizard = new WizardContext(ctx, this.options.steps) + ctx.wizard = wizard + return next() + }, + super.middleware(), + (ctx, next) => { + if (!ctx.wizard.step) { + ctx.wizard.selectStep(0) + return ctx.scene.leave() + } + return unwrap(ctx.wizard.step)(ctx, next) + } + ]) + } +} + +module.exports = WizardScene diff --git a/TaskServer/node_modules/telegraf/session.js b/TaskServer/node_modules/telegraf/session.js new file mode 100644 index 000000000..1f58ed269 --- /dev/null +++ b/TaskServer/node_modules/telegraf/session.js @@ -0,0 +1,33 @@ +module.exports = function (opts) { + const options = { + property: 'session', + store: new Map(), + getSessionKey: (ctx) => ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}`, + ...opts + } + + const ttlMs = options.ttl && options.ttl * 1000 + + return (ctx, next) => { + const key = options.getSessionKey(ctx) + if (!key) { + return next(ctx) + } + const now = new Date().getTime() + return Promise.resolve(options.store.get(key)) + .then((state) => state || { session: {} }) + .then(({ session, expires }) => { + if (expires && expires < now) { + session = {} + } + Object.defineProperty(ctx, options.property, { + get: function () { return session }, + set: function (newValue) { session = { ...newValue } } + }) + return next(ctx).then(() => options.store.set(key, { + session, + expires: ttlMs ? now + ttlMs : null + })) + }) + } +} diff --git a/TaskServer/node_modules/telegraf/stage.js b/TaskServer/node_modules/telegraf/stage.js new file mode 100644 index 000000000..7af7c9a25 --- /dev/null +++ b/TaskServer/node_modules/telegraf/stage.js @@ -0,0 +1,51 @@ +const SceneContext = require('./scenes/context') +const Composer = require('./composer') +const { compose, optional, lazy, safePassThru } = Composer + +class Stage extends Composer { + constructor (scenes = [], options) { + super() + this.options = { + sessionName: 'session', + ...options + } + this.scenes = new Map() + scenes.forEach((scene) => this.register(scene)) + } + + register (...scenes) { + scenes.forEach((scene) => { + if (!scene || !scene.id || typeof scene.middleware !== 'function') { + throw new Error('telegraf: Unsupported scene') + } + this.scenes.set(scene.id, scene) + }) + return this + } + + middleware () { + const handler = compose([ + (ctx, next) => { + ctx.scene = new SceneContext(ctx, this.scenes, this.options) + return next() + }, + super.middleware(), + lazy((ctx) => ctx.scene.current || safePassThru()) + ]) + return optional((ctx) => ctx[this.options.sessionName], handler) + } + + static enter (...args) { + return (ctx) => ctx.scene.enter(...args) + } + + static reenter (...args) { + return (ctx) => ctx.scene.reenter(...args) + } + + static leave (...args) { + return (ctx) => ctx.scene.leave(...args) + } +} + +module.exports = Stage diff --git a/TaskServer/node_modules/telegraf/telegraf.js b/TaskServer/node_modules/telegraf/telegraf.js new file mode 100644 index 000000000..2d6e73373 --- /dev/null +++ b/TaskServer/node_modules/telegraf/telegraf.js @@ -0,0 +1,229 @@ +const debug = require('debug')('telegraf:core') +const Telegram = require('./telegram') +const Extra = require('./extra') +const Composer = require('./composer') +const Markup = require('./markup') +const session = require('./session') +const Router = require('./router') +const Stage = require('./stage') +const BaseScene = require('./scenes/base') +const Context = require('./context') +const generateCallback = require('./core/network/webhook') +const crypto = require('crypto') +const { URL } = require('url') + +const DEFAULT_OPTIONS = { + retryAfter: 1, + handlerTimeout: 0, + contextType: Context +} + +const noop = () => { } + +class Telegraf extends Composer { + constructor (token, options) { + super() + this.options = { + ...DEFAULT_OPTIONS, + ...options + } + this.token = token + this.handleError = (err) => { + console.error() + console.error((err.stack || err.toString()).replace(/^/gm, ' ')) + console.error() + throw err + } + this.context = {} + this.polling = { + offset: 0, + started: false + } + } + + set token (token) { + this.telegram = new Telegram(token, this.telegram + ? this.telegram.options + : this.options.telegram + ) + } + + get token () { + return this.telegram.token + } + + set webhookReply (webhookReply) { + this.telegram.webhookReply = webhookReply + } + + get webhookReply () { + return this.telegram.webhookReply + }/* eslint brace-style: 0 */ + + catch (handler) { + this.handleError = handler + return this + } + + webhookCallback (path = '/') { + return generateCallback(path, (update, res) => this.handleUpdate(update, res), debug) + } + + startPolling (timeout = 30, limit = 100, allowedUpdates, stopCallback = noop) { + this.polling.timeout = timeout + this.polling.limit = limit + this.polling.allowedUpdates = allowedUpdates + ? Array.isArray(allowedUpdates) ? allowedUpdates : [`${allowedUpdates}`] + : null + this.polling.stopCallback = stopCallback + if (!this.polling.started) { + this.polling.started = true + this.fetchUpdates() + } + return this + } + + startWebhook (hookPath, tlsOptions, port, host, cb) { + const webhookCb = this.webhookCallback(hookPath) + const callback = cb && typeof cb === 'function' + ? (req, res) => webhookCb(req, res, () => cb(req, res)) + : webhookCb + this.webhookServer = tlsOptions + ? require('https').createServer(tlsOptions, callback) + : require('http').createServer(callback) + this.webhookServer.listen(port, host, () => { + debug('Webhook listening on port: %s', port) + }) + return this + } + + launch (config = {}) { + debug('Connecting to Telegram') + return this.telegram.getMe() + .then((botInfo) => { + debug(`Launching @${botInfo.username}`) + this.options.username = botInfo.username + this.context.botInfo = botInfo + if (!config.webhook) { + const { timeout, limit, allowedUpdates, stopCallback } = config.polling || {} + return this.telegram.deleteWebhook() + .then(() => this.startPolling(timeout, limit, allowedUpdates, stopCallback)) + .then(() => debug('Bot started with long-polling')) + } + if (typeof config.webhook.domain !== 'string' && typeof config.webhook.hookPath !== 'string') { + throw new Error('Webhook domain or webhook path is required') + } + let domain = config.webhook.domain || '' + if (domain.startsWith('https://') || domain.startsWith('http://')) { + domain = new URL(domain).host + } + const hookPath = config.webhook.hookPath || `/telegraf/${crypto.randomBytes(32).toString('hex')}` + const { port, host, tlsOptions, cb } = config.webhook + this.startWebhook(hookPath, tlsOptions, port, host, cb) + if (!domain) { + debug('Bot started with webhook') + return + } + return this.telegram + .setWebhook(`https://${domain}${hookPath}`) + .then(() => debug(`Bot started with webhook @ https://${domain}`)) + }) + .catch((err) => { + console.error('Launch failed') + console.error(err.stack || err.toString()) + }) + } + + stop (cb = noop) { + debug('Stopping bot...') + return new Promise((resolve) => { + const done = () => resolve() & cb() + if (this.webhookServer) { + return this.webhookServer.close(done) + } else if (!this.polling.started) { + return done() + } + this.polling.stopCallback = done + this.polling.started = false + }) + } + + handleUpdates (updates) { + if (!Array.isArray(updates)) { + return Promise.reject(new Error('Updates must be an array')) + } + const processAll = Promise.all(updates.map((update) => this.handleUpdate(update))) + if (this.options.handlerTimeout === 0) { + return processAll + } + return Promise.race([ + processAll, + new Promise((resolve) => setTimeout(resolve, this.options.handlerTimeout)) + ]) + } + + handleUpdate (update, webhookResponse) { + debug('Processing update', update.update_id) + const tg = new Telegram(this.token, this.telegram.options, webhookResponse) + const TelegrafContext = this.options.contextType + const ctx = new TelegrafContext(update, tg, this.options) + Object.assign(ctx, this.context) + return this.middleware()(ctx).catch((err) => this.handleError(err, ctx)) + } + + fetchUpdates () { + if (!this.polling.started) { + this.polling.stopCallback && this.polling.stopCallback() + return + } + const { timeout, limit, offset, allowedUpdates } = this.polling + this.telegram.getUpdates(timeout, limit, offset, allowedUpdates) + .catch((err) => { + if (err.code === 401 || err.code === 409) { + throw err + } + const wait = (err.parameters && err.parameters.retry_after) || this.options.retryAfter + console.error(`Failed to fetch updates. Waiting: ${wait}s`, err.message) + return new Promise((resolve) => setTimeout(resolve, wait * 1000, [])) + }) + .then((updates) => this.polling.started + ? this.handleUpdates(updates).then(() => updates) + : [] + ) + .catch((err) => { + console.error('Failed to process updates.', err) + this.polling.started = false + this.polling.offset = 0 + this.polling.stopCallback && this.polling.stopCallback() + return [] + }) + .then((updates) => { + if (updates.length > 0) { + this.polling.offset = updates[updates.length - 1].update_id + 1 + } + this.fetchUpdates() + }) + } +} + +module.exports = Object.assign(Telegraf, { + Context, + Composer, + Extra, + Markup, + Router, + Telegram, + session +}) + +module.exports.default = Object.assign(Telegraf, { + Context, + Composer, + Extra, + Markup, + Router, + Telegram, + Stage, + BaseScene, + session +}) diff --git a/TaskServer/node_modules/telegraf/telegram.js b/TaskServer/node_modules/telegraf/telegram.js new file mode 100644 index 000000000..d217fde43 --- /dev/null +++ b/TaskServer/node_modules/telegraf/telegram.js @@ -0,0 +1,417 @@ +const replicators = require('./core/replicators') +const ApiClient = require('./core/network/client') + +class Telegram extends ApiClient { + getMe () { + return this.callApi('getMe') + } + + getFile (fileId) { + return this.callApi('getFile', { file_id: fileId }) + } + + getFileLink (fileId) { + return Promise.resolve(fileId) + .then((fileId) => { + if (fileId && fileId.file_path) { + return fileId + } + const id = fileId && fileId.file_id ? fileId.file_id : fileId + return this.getFile(id) + }) + .then((file) => `${this.options.apiRoot}/file/bot${this.token}/${file.file_path}`) + } + + getUpdates (timeout, limit, offset, allowedUpdates) { + const url = `getUpdates?offset=${offset}&limit=${limit}&timeout=${timeout}` + return this.callApi(url, { + allowed_updates: allowedUpdates + }) + } + + getWebhookInfo () { + return this.callApi('getWebhookInfo') + } + + getGameHighScores (userId, inlineMessageId, chatId, messageId) { + return this.callApi('getGameHighScores', { + user_id: userId, + inline_message_id: inlineMessageId, + chat_id: chatId, + message_id: messageId + }) + } + + setGameScore (userId, score, inlineMessageId, chatId, messageId, editMessage = true, force) { + return this.callApi('setGameScore', { + force, + score, + user_id: userId, + inline_message_id: inlineMessageId, + chat_id: chatId, + message_id: messageId, + disable_edit_message: !editMessage + }) + } + + setWebhook (url, certificate, maxConnections, allowedUpdates) { + return this.callApi('setWebhook', { + url, + certificate, + max_connections: maxConnections, + allowed_updates: allowedUpdates + }) + } + + deleteWebhook () { + return this.callApi('deleteWebhook') + } + + sendMessage (chatId, text, extra) { + return this.callApi('sendMessage', { chat_id: chatId, text, ...extra }) + } + + forwardMessage (chatId, fromChatId, messageId, extra) { + return this.callApi('forwardMessage', { + chat_id: chatId, + from_chat_id: fromChatId, + message_id: messageId, + ...extra + }) + } + + sendChatAction (chatId, action) { + return this.callApi('sendChatAction', { chat_id: chatId, action }) + } + + getUserProfilePhotos (userId, offset, limit) { + return this.callApi('getUserProfilePhotos', { user_id: userId, offset, limit }) + } + + sendLocation (chatId, latitude, longitude, extra) { + return this.callApi('sendLocation', { chat_id: chatId, latitude, longitude, ...extra }) + } + + sendVenue (chatId, latitude, longitude, title, address, extra) { + return this.callApi('sendVenue', { + latitude, + longitude, + title, + address, + chat_id: chatId, + ...extra + }) + } + + sendInvoice (chatId, invoice, extra) { + return this.callApi('sendInvoice', { chat_id: chatId, ...invoice, ...extra }) + } + + sendContact (chatId, phoneNumber, firstName, extra) { + return this.callApi('sendContact', { chat_id: chatId, phone_number: phoneNumber, first_name: firstName, ...extra }) + } + + sendPhoto (chatId, photo, extra) { + return this.callApi('sendPhoto', { chat_id: chatId, photo, ...extra }) + } + + sendDocument (chatId, document, extra) { + return this.callApi('sendDocument', { chat_id: chatId, document, ...extra }) + } + + sendAudio (chatId, audio, extra) { + return this.callApi('sendAudio', { chat_id: chatId, audio, ...extra }) + } + + sendSticker (chatId, sticker, extra) { + return this.callApi('sendSticker', { chat_id: chatId, sticker, ...extra }) + } + + sendVideo (chatId, video, extra) { + return this.callApi('sendVideo', { chat_id: chatId, video, ...extra }) + } + + sendAnimation (chatId, animation, extra) { + return this.callApi('sendAnimation', { chat_id: chatId, animation, ...extra }) + } + + sendVideoNote (chatId, videoNote, extra) { + return this.callApi('sendVideoNote', { chat_id: chatId, video_note: videoNote, ...extra }) + } + + sendVoice (chatId, voice, extra) { + return this.callApi('sendVoice', { chat_id: chatId, voice, ...extra }) + } + + sendGame (chatId, gameName, extra) { + return this.callApi('sendGame', { chat_id: chatId, game_short_name: gameName, ...extra }) + } + + sendMediaGroup (chatId, media, extra) { + return this.callApi('sendMediaGroup', { chat_id: chatId, media, ...extra }) + } + + sendPoll (chatId, question, options, extra) { + return this.callApi('sendPoll', { chat_id: chatId, type: 'regular', question, options, ...extra }) + } + + sendQuiz (chatId, question, options, extra) { + return this.callApi('sendPoll', { chat_id: chatId, type: 'quiz', question, options, ...extra }) + } + + stopPoll (chatId, messageId, extra) { + return this.callApi('stopPoll', { chat_id: chatId, message_id: messageId, ...extra }) + } + + getChat (chatId) { + return this.callApi('getChat', { chat_id: chatId }) + } + + getChatAdministrators (chatId) { + return this.callApi('getChatAdministrators', { chat_id: chatId }) + } + + getChatMember (chatId, userId) { + return this.callApi('getChatMember', { chat_id: chatId, user_id: userId }) + } + + getChatMembersCount (chatId) { + return this.callApi('getChatMembersCount', { chat_id: chatId }) + } + + answerInlineQuery (inlineQueryId, results, extra) { + return this.callApi('answerInlineQuery', { inline_query_id: inlineQueryId, results, ...extra }) + } + + setChatPermissions (chatId, permissions) { + return this.callApi('setChatPermissions', { chat_id: chatId, permissions }) + } + + kickChatMember (chatId, userId, untilDate) { + return this.callApi('kickChatMember', { chat_id: chatId, user_id: userId, until_date: untilDate }) + } + + promoteChatMember (chatId, userId, extra) { + return this.callApi('promoteChatMember', { chat_id: chatId, user_id: userId, ...extra }) + } + + restrictChatMember (chatId, userId, extra) { + return this.callApi('restrictChatMember', { chat_id: chatId, user_id: userId, ...extra }) + } + + setChatAdministratorCustomTitle (chatId, userId, title) { + return this.callApi('setChatAdministratorCustomTitle', { chat_id: chatId, user_id: userId, custom_title: title }) + } + + exportChatInviteLink (chatId) { + return this.callApi('exportChatInviteLink', { chat_id: chatId }) + } + + setChatPhoto (chatId, photo) { + return this.callApi('setChatPhoto', { chat_id: chatId, photo }) + } + + deleteChatPhoto (chatId) { + return this.callApi('deleteChatPhoto', { chat_id: chatId }) + } + + setChatTitle (chatId, title) { + return this.callApi('setChatTitle', { chat_id: chatId, title }) + } + + setChatDescription (chatId, description) { + return this.callApi('setChatDescription', { chat_id: chatId, description }) + } + + pinChatMessage (chatId, messageId, extra) { + return this.callApi('pinChatMessage', { chat_id: chatId, message_id: messageId, ...extra }) + } + + unpinChatMessage (chatId) { + return this.callApi('unpinChatMessage', { chat_id: chatId }) + } + + leaveChat (chatId) { + return this.callApi('leaveChat', { chat_id: chatId }) + } + + unbanChatMember (chatId, userId) { + return this.callApi('unbanChatMember', { chat_id: chatId, user_id: userId }) + } + + answerCbQuery (callbackQueryId, text, showAlert, extra) { + return this.callApi('answerCallbackQuery', { + text, + show_alert: showAlert, + callback_query_id: callbackQueryId, + ...extra + }) + } + + answerGameQuery (callbackQueryId, url) { + return this.callApi('answerCallbackQuery', { + url, + callback_query_id: callbackQueryId + }) + } + + answerShippingQuery (shippingQueryId, ok, shippingOptions, errorMessage) { + return this.callApi('answerShippingQuery', { + ok, + shipping_query_id: shippingQueryId, + shipping_options: shippingOptions, + error_message: errorMessage + }) + } + + answerPreCheckoutQuery (preCheckoutQueryId, ok, errorMessage) { + return this.callApi('answerPreCheckoutQuery', { + ok, + pre_checkout_query_id: preCheckoutQueryId, + error_message: errorMessage + }) + } + + editMessageText (chatId, messageId, inlineMessageId, text, extra) { + return this.callApi('editMessageText', { + text, + chat_id: chatId, + message_id: messageId, + inline_message_id: inlineMessageId, + ...extra + }) + } + + editMessageCaption (chatId, messageId, inlineMessageId, caption, extra = {}) { + return this.callApi('editMessageCaption', { + caption, + chat_id: chatId, + message_id: messageId, + inline_message_id: inlineMessageId, + parse_mode: extra.parse_mode, + reply_markup: extra.parse_mode || extra.reply_markup ? extra.reply_markup : extra + }) + } + + editMessageMedia (chatId, messageId, inlineMessageId, media, extra = {}) { + return this.callApi('editMessageMedia', { + chat_id: chatId, + message_id: messageId, + inline_message_id: inlineMessageId, + media: { ...media, parse_mode: extra.parse_mode }, + reply_markup: extra.reply_markup ? extra.reply_markup : extra + }) + } + + editMessageReplyMarkup (chatId, messageId, inlineMessageId, markup) { + return this.callApi('editMessageReplyMarkup', { + chat_id: chatId, + message_id: messageId, + inline_message_id: inlineMessageId, + reply_markup: markup + }) + } + + editMessageLiveLocation (latitude, longitude, chatId, messageId, inlineMessageId, markup) { + return this.callApi('editMessageLiveLocation', { + latitude, + longitude, + chat_id: chatId, + message_id: messageId, + inline_message_id: inlineMessageId, + reply_markup: markup + }) + } + + stopMessageLiveLocation (chatId, messageId, inlineMessageId, markup) { + return this.callApi('stopMessageLiveLocation', { + chat_id: chatId, + message_id: messageId, + inline_message_id: inlineMessageId, + reply_markup: markup + }) + } + + deleteMessage (chatId, messageId) { + return this.callApi('deleteMessage', { + chat_id: chatId, + message_id: messageId + }) + } + + setChatStickerSet (chatId, setName) { + return this.callApi('setChatStickerSet', { + chat_id: chatId, + sticker_set_name: setName + }) + } + + deleteChatStickerSet (chatId) { + return this.callApi('deleteChatStickerSet', { chat_id: chatId }) + } + + getStickerSet (name) { + return this.callApi('getStickerSet', { name }) + } + + uploadStickerFile (ownerId, stickerFile) { + return this.callApi('uploadStickerFile', { + user_id: ownerId, + png_sticker: stickerFile + }) + } + + createNewStickerSet (ownerId, name, title, stickerData) { + return this.callApi('createNewStickerSet', { + name, + title, + user_id: ownerId, + ...stickerData + }) + } + + addStickerToSet (ownerId, name, stickerData, isMasks) { + return this.callApi('addStickerToSet', { + name, + user_id: ownerId, + is_masks: isMasks, + ...stickerData + }) + } + + setStickerPositionInSet (sticker, position) { + return this.callApi('setStickerPositionInSet', { + sticker, + position + }) + } + + deleteStickerFromSet (sticker) { + return this.callApi('deleteStickerFromSet', { sticker }) + } + + setPassportDataErrors (userId, errors) { + return this.callApi('setPassportDataErrors', { + user_id: userId, + errors: errors + }) + } + + sendCopy (chatId, message, extra) { + if (!message) { + throw new Error('Message is required') + } + const type = Object.keys(replicators.copyMethods).find((type) => message[type]) + if (!type) { + throw new Error('Unsupported message type') + } + const opts = { + chat_id: chatId, + ...replicators[type](message), + ...extra + } + return this.callApi(replicators.copyMethods[type], opts) + } +} + +module.exports = Telegram diff --git a/TaskServer/node_modules/telegram-typings/index.js b/TaskServer/node_modules/telegram-typings/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/TaskServer/node_modules/telegram-typings/package.json b/TaskServer/node_modules/telegram-typings/package.json new file mode 100644 index 000000000..18e7cb384 --- /dev/null +++ b/TaskServer/node_modules/telegram-typings/package.json @@ -0,0 +1,59 @@ +{ + "_from": "telegram-typings@^3.6.0", + "_id": "telegram-typings@3.6.1", + "_inBundle": false, + "_integrity": "sha512-njVv1EAhIZnmQVLocZEADYUyqA1WIXuVcDYlsp+mXua/XB0pxx+PKtMSPeZ/EE4wPWTw9h/hA9ASTT6yQelkiw==", + "_location": "/telegram-typings", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "telegram-typings@^3.6.0", + "name": "telegram-typings", + "escapedName": "telegram-typings", + "rawSpec": "^3.6.0", + "saveSpec": null, + "fetchSpec": "^3.6.0" + }, + "_requiredBy": [ + "/telegraf" + ], + "_resolved": "https://registry.npmjs.org/telegram-typings/-/telegram-typings-3.6.1.tgz", + "_shasum": "1288d547f8694b61f1c01c2993e295f3114d9e25", + "_spec": "telegram-typings@^3.6.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\telegraf", + "author": { + "name": "Sergey Sova", + "email": "mail@sergeysova.com", + "url": "https://sergeysova.com" + }, + "bugs": { + "url": "https://github.com/sergeysova/telegram-typings/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Telegram typings for TypeScript and Flowtype to create bots", + "homepage": "https://github.com/sergeysova/telegram-typings#readme", + "keywords": [ + "telegram", + "bots", + "typings", + "types", + "flow", + "type", + "typescript", + "script", + "build", + "generate" + ], + "license": "MIT", + "main": "index.js", + "name": "telegram-typings", + "repository": { + "type": "git", + "url": "git+https://github.com/sergeysova/telegram-typings.git" + }, + "typings": "telegram-typings.d.ts", + "version": "3.6.1" +} diff --git a/TaskServer/package-lock.json b/TaskServer/package-lock.json new file mode 100644 index 000000000..bdb728c2e --- /dev/null +++ b/TaskServer/package-lock.json @@ -0,0 +1,108 @@ +{ + "name": "@superalgos/taskserver", + "version": "2.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", + "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==" + }, + "ccxt": { + "version": "1.22.39", + "resolved": "https://registry.npmjs.org/ccxt/-/ccxt-1.22.39.tgz", + "integrity": "sha512-Vq4BzYS/FwRudPKQ6BQGU3SVntYv7EAQGT+2guf+e7qm45CJMhdoVwR1z89kl42fbK39XMnl8sUnRCwsKPs38A==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "2.1.2" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "easy-stack": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=" + }, + "event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==" + }, + "js-message": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=" + }, + "js-queue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "requires": { + "easy-stack": "1.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "module-alias": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", + "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "node-ipc": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "requires": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + } + }, + "sandwich-stream": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", + "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==" + }, + "telegraf": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.36.0.tgz", + "integrity": "sha512-9o6AJKRiTm5vMWYI6WpTfBHzu4FMpWBNKxvnMxRds/cbMY9RnsVVjdi8i4bFFlfd+xbi73EbrnI3dybayryICA==", + "requires": { + "@types/node": "13.7.0", + "debug": "4.1.1", + "minimist": "1.2.0", + "module-alias": "2.2.2", + "node-fetch": "2.6.0", + "sandwich-stream": "2.0.2", + "telegram-typings": "3.6.1" + } + }, + "telegram-typings": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/telegram-typings/-/telegram-typings-3.6.1.tgz", + "integrity": "sha512-njVv1EAhIZnmQVLocZEADYUyqA1WIXuVcDYlsp+mXua/XB0pxx+PKtMSPeZ/EE4wPWTw9h/hA9ASTT6yQelkiw==" + } + } +} diff --git a/TaskServer/package.json b/TaskServer/package.json new file mode 100644 index 000000000..a81037332 --- /dev/null +++ b/TaskServer/package.json @@ -0,0 +1,21 @@ +{ + "name": "@superalgos/taskserver", + "version": "2.0.0", + "description": "Executes Tasks.", + "author": "Superalgos.org", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/Superalgos/TaskServer.git" + }, + "main": "Run.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "ccxt": "^1.22.39", + "dotenv": "^8.2.0", + "node-ipc": "^9.1.1", + "telegraf": "^3.36.0" + } +} diff --git a/TaskServer/server.js b/TaskServer/server.js new file mode 100644 index 000000000..8d5c2d432 --- /dev/null +++ b/TaskServer/server.js @@ -0,0 +1,262 @@ +/* Callbacks default responses. */ + +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" +}; + +/* Process Events */ + +process.on('uncaughtException', function (err) { + console.log('[ERROR] Task Server -> server -> uncaughtException -> err.message = ' + err.message) + console.log('[ERROR] Task Server -> server -> uncaughtException -> err.stack = ' + err.stack) + process.exit(1) +}) + +process.on('unhandledRejection', (reason, p) => { + console.log('[ERROR] Task Server -> server -> unhandledRejection -> reason = ' + JSON.stringify(reason)) + console.log('[ERROR] Task Server -> server -> unhandledRejection -> p = ' + JSON.stringify(p)) + process.exit(1) +}) + +process.on('exit', function (code) { + + /* We send an event signaling that the Task is being terminated. */ + let key = global.TASK_NODE.name + '-' + global.TASK_NODE.type + '-' + global.TASK_NODE.id + + global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Stopped') // Meaning Task Stopped + global.SYSTEM_EVENT_HANDLER.deleteEventHandler(key) + global.SYSTEM_EVENT_HANDLER.finalize() + global.SYSTEM_EVENT_HANDLER = undefined + + //console.log('[INFO] Task Server -> server -> process.on.exit -> About to exit -> code = ' + code) +}) + +/* Here we listen for the message to stop this Task / Process comming from the Task Manager, which is the paret of this node js process. */ +process.on('message', message => { + if (message === 'Stop this Task') { + + global.STOP_TASK_GRACEFULLY = true; + + /* + There are some process that might no be able to end grafully, for example the ones schedulle to process information in a future day or month. + In order to be sure that the process will be terminated, we schedulle one forced exit in 2 minutes from now. + */ + let key = global.TASK_NODE.name + '-' + global.TASK_NODE.type + '-' + global.TASK_NODE.id + console.log('[INFO] Task Server -> server -> process.on -> Stopping Task ' + key + '. Nodejs process will be exited in less than 1 minute.') + setTimeout(global.EXIT_NODE_PROCESS, 60000); + } +}); + +global.EXIT_NODE_PROCESS = function exitProcess() { + + /* Cleaning Before Exiting. */ + clearInterval(global.HEARTBEAT_INTERVAL_HANDLER) + + for (let i = 0; i < global.TASK_NODE.bot.processes.length; i++) { + let code = global.TASK_NODE.bot.processes[i].code + let process = global.TASK_NODE.bot.processes[i] + + key = process.name + '-' + process.type + '-' + process.id + global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Stopped') // Meaning Process Stopped + } + + //console.log("[INFO] Task Server -> " + global.TASK_NODE.name + " -> EXIT_NODE_PROCESS -> Task Server will stop in 10 seconds."); + + setTimeout(process.exit, 10000) // We will give 10 seconds to logs be written on file +} + +require('dotenv').config(); + +global.WRITE_LOGS_TO_FILES = process.env.WRITE_LOGS_TO_FILES + +/* +We need to count how many process instances we deployd and how many of them have already finished their job, either +because they just finished or because there was a request to stop the proceses. In this way, once we reach the +amount of instances started, we can safelly destroy the rest of the objects running and let this nodejs process die. +*/ + +global.ENDED_PROCESSES_COUNTER = 0 +global.TOTAL_PROCESS_INSTANCES_CREATED = 0 + +/* + +We read the first string sent as an argument when the process was created by the Task Manager. There we will find the information of the identity +of this Task and know exactly what to run within this server instance. + +*/ +let taskId = process.argv[2] // reading what comes as an argument of the nodejs process. + +/* Setting up the global Event Handler */ + +const EVENT_HANDLER_MODULE = require('./SystemEventHandler.js'); +const IPC = require('node-ipc'); +global.SYSTEM_EVENT_HANDLER = EVENT_HANDLER_MODULE.newSystemEventHandler(IPC) +global.SYSTEM_EVENT_HANDLER.initialize('Task Server', preLoader) +global.STOP_TASK_GRACEFULLY = false; + +function preLoader() { + if (taskId !== undefined) { + /* The Task Manager sent the info via a process argument. In this case we listen to an event with the Task Info that should be emitted at the UI */ + try { + //console.log('[INFO] Task Server -> server -> preLoader -> Listening to starting event -> key = ' + 'Task Server - ' + taskId) + global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server - ' + taskId, 'Run Task', undefined, undefined, undefined, eventReceived) + function eventReceived(message) { + global.TASK_NODE = message + global.TASK_NODE = JSON.parse(message.event.definition) + bootLoader() + } + } catch (err) { + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack) + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE)) + } + } + else { /* This process was started not by the Task Manager, but independently (most likely for debugging purposes). In this case we listen to an event with the Task Info that should be emitted at the UI */ + try { + //console.log('[INFO] Task Server -> server -> preLoader -> Waiting for event to start debugging...') + global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server', 'Debug Task Started', undefined, undefined, undefined, startDebugging) + function startDebugging(message) { + global.TASK_NODE = message + global.TASK_NODE = JSON.parse(message.event.definition) + bootLoader() + } + } catch (err) { + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack) + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE)) + } + } +} + +function bootLoader() { + + /* Heartbeat sent to the UI */ + + let key = global.TASK_NODE.name + '-' + global.TASK_NODE.type + '-' + global.TASK_NODE.id + + global.SYSTEM_EVENT_HANDLER.createEventHandler(key) + global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Running') // Meaning Task Running + global.HEARTBEAT_INTERVAL_HANDLER = setInterval(taskHearBeat, 1000) + + function taskHearBeat() { + + /* The heartbeat event is raised at the event handler of the instance of this task, created at the UI. */ + let event = { + seconds: (new Date()).getSeconds() + } + global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Heartbeat', event) + } + + for (let processIndex = 0; processIndex < global.TASK_NODE.bot.processes.length; processIndex++) { + let code = global.TASK_NODE.bot.processes[processIndex].code + + /* Validate that the minimun amount of input required are defined. */ + + if (global.TASK_NODE.bot.processes[processIndex].marketReference === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Process Instance without a Market Reference. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex])); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Market Reference without a Reference Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].marketReference)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.parentNode === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Market without a Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.parentNode.parentNode === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Exchange Markets without a Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.parentNode)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.baseAsset === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Market without a Base Asset. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.quotedAsset === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Market without a Quoted Asset. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.baseAsset.referenceParent === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Base Asset without a Reference Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.baseAsset)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.quotedAsset.referenceParent === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Quoted Asset without a Reference Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].marketReference.referenceParent.quotedAsset)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].referenceParent === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Process Instance without a Reference Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex])); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Process Definition without parent Bot Definition. -> Process Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Bot Definition without parent Data Mine. -> Bot Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Process Definition without a codeName defined. -> Process Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.code.codeName === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Bot Definition without a codeName defined. -> Bot Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName === undefined) { + console.log("[WARN] Task Server -> server -> bootLoader -> Data Mine without a codeName defined. -> Data Mine Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode)); + continue + } + + startRoot(processIndex); + } +} + +function startRoot(processIndex) { + + // console.log('[INFO] Task Server -> server -> startRoot -> Entering function. ') + + const ROOT_MODULE = require('./Root') + let root = ROOT_MODULE.newRoot() + + root.initialize(onInitialized) + + function onInitialized() { + //console.log('[INFO] Task Server -> server -> startRoot -> onInitialized -> Entering function. ') + root.start(processIndex) + } +} + diff --git a/WebServer/.env b/WebServer/.env new file mode 100644 index 000000000..7402606ae --- /dev/null +++ b/WebServer/.env @@ -0,0 +1,21 @@ +#PORT NUMBER: If this port is already in use by a different app, please pick a new number between 1 and 50000. +WEB_SERVER_PORT=9999 + +#Log options +CONSOLE_LOG=false +CONSOLE_ERROR_LOG=true +LOG_FILE_CONTENT=false + +# Server commands +REINITIALIZE_COMMAND=sdnacGYGdw756dnxasxbGGGjsdbd5576576gfdsxgvbnFFF + +PATH_TO_CANVAS_APP=../CanvasApp +PATH_TO_FILES_COMPONENT=../Files-Component/src + +GATEWAY_ENDPOINT=https://demo-api-dev.superalgos.org/graphql + +INTER_PROCESS_FILES_PATH=../Inter-Process-Files +STORAGE_PATH=../Data-Storage +PLOTTERS_PATH=../Legacy-Code +PATH_TO_WEB_SERVER =./ + diff --git a/WebServer/Ecosystem.js b/WebServer/Ecosystem.js new file mode 100644 index 000000000..beb6f5573 --- /dev/null +++ b/WebServer/Ecosystem.js @@ -0,0 +1,149 @@ + +function newEcosystem() { + + let thisObject = { + getHost: getHost, + getCompetition: getCompetition, + getDataMine: getDataMine, + getDataMines: getDataMines, + getBot: getBot, + getProduct: getProduct, + getDataSet: getDataSet, + getExchange: getExchange, + getPlotter: getPlotter, + getPlotterModule: getPlotterModule, + getStoragePermissions: getStoragePermissions, + setEvent: setEvent, + initialize: initialize + } + + let ECOSYSTEM = JSON.parse(window.localStorage.getItem('ecosystem')); + if (ECOSYSTEM === null || ECOSYSTEM === undefined) { + ECOSYSTEM = getUserEcosystem() + } + + return thisObject; + + function initialize() { + + } + + function setEvent(input) { + ECOSYSTEM.hosts = input; + } + + function getStoragePermissions() { + + return ECOSYSTEM.STORAGE_PERMISSIONS; + + } + + function getHost(pHostCodeName) { + + for (let i = 0; i < ECOSYSTEM.hosts.length; i++) { + + if (ECOSYSTEM.hosts[i].codeName === pHostCodeName) { + + return ECOSYSTEM.hosts[i]; + } + } + } + + function getCompetition(pHost, pCompetitionCodeName) { + + for (let i = 0; i < pHost.competitions.length; i++) { + + if (pHost.competitions[i].codeName === pCompetitionCodeName) { + + return pHost.competitions[i]; + } + } + } + + function getDataMines() { + + return ECOSYSTEM.dataMines; + + } + + function getDataMine(pDataMineCodeName) { + + for (let i = 0; i < ECOSYSTEM.dataMines.length; i++) { + + if (ECOSYSTEM.dataMines[i].codeName === pDataMineCodeName) { + + return ECOSYSTEM.dataMines[i]; + } + } + } + + function getBot(pDataMine, pBotCodeName) { + + for (let i = 0; i < pDataMine.bots.length; i++) { + + if (pDataMine.bots[i].codeName === pBotCodeName) { + + return pDataMine.bots[i]; + } + } + } + + function getPlotter(pDataMine, pPlotterCodeName) { + + for (let i = 0; i < pDataMine.plotters.length; i++) { + + if (pDataMine.plotters[i].codeName === pPlotterCodeName) { + + return pDataMine.plotters[i]; + } + } + } + + function getPlotterModule(pPlotter, pModuleCodeName) { + + for (let i = 0; i < pPlotter.modules.length; i++) { + + if (pPlotter.modules[i].codeName === pModuleCodeName) { + + return pPlotter.modules[i]; + } + } + } + + function getProduct(pBot, pProductCodeName) { + + for (let i = 0; i < pBot.products.length; i++) { + + if (pBot.products[i].codeName === pProductCodeName) { + + return pBot.products[i]; + } + } + } + + function getDataSet(pProduct, pSetCodeName) { + + for (let i = 0; i < pProduct.dataSets.length; i++) { + + if (pProduct.dataSets[i].codeName === pSetCodeName) { + + return pProduct.dataSets[i]; + } + } + } + + function getExchange(pProduct, pName) { + + for (let i = 0; i < pProduct.exchangeList.length; i++) { + + if (pProduct.exchangeList[i].name === pName) { + + return pProduct.exchangeList[i]; + } + } + } + + + + return false; +} diff --git a/WebServer/Images/Icons/style-01/150-next-phase-event.png b/WebServer/Images/Icons/style-01/150-next-phase-event.png new file mode 100644 index 000000000..fe469f85f Binary files /dev/null and b/WebServer/Images/Icons/style-01/150-next-phase-event.png differ diff --git a/WebServer/Images/Icons/style-01/analysis-1.png b/WebServer/Images/Icons/style-01/analysis-1.png new file mode 100644 index 000000000..8b3d61b9e Binary files /dev/null and b/WebServer/Images/Icons/style-01/analysis-1.png differ diff --git a/WebServer/Images/Icons/style-01/analysis.png b/WebServer/Images/Icons/style-01/analysis.png new file mode 100644 index 000000000..80ede07af Binary files /dev/null and b/WebServer/Images/Icons/style-01/analysis.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-000.png b/WebServer/Images/Icons/style-01/angle-to-parent-000.png new file mode 100644 index 000000000..23b3aee31 Binary files /dev/null and b/WebServer/Images/Icons/style-01/angle-to-parent-000.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-045.png b/WebServer/Images/Icons/style-01/angle-to-parent-045.png new file mode 100644 index 000000000..cd495d5bd Binary files /dev/null and b/WebServer/Images/Icons/style-01/angle-to-parent-045.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-090.png b/WebServer/Images/Icons/style-01/angle-to-parent-090.png new file mode 100644 index 000000000..6c94bc491 Binary files /dev/null and b/WebServer/Images/Icons/style-01/angle-to-parent-090.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-180.png b/WebServer/Images/Icons/style-01/angle-to-parent-180.png new file mode 100644 index 000000000..b04398af2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/angle-to-parent-180.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-360.png b/WebServer/Images/Icons/style-01/angle-to-parent-360.png new file mode 100644 index 000000000..d66118a75 Binary files /dev/null and b/WebServer/Images/Icons/style-01/angle-to-parent-360.png differ diff --git a/WebServer/Images/Icons/style-01/approve.png b/WebServer/Images/Icons/style-01/approve.png new file mode 100644 index 000000000..2145a1626 Binary files /dev/null and b/WebServer/Images/Icons/style-01/approve.png differ diff --git a/WebServer/Images/Icons/style-01/asset.png b/WebServer/Images/Icons/style-01/asset.png new file mode 100644 index 000000000..ce6d5b5d8 Binary files /dev/null and b/WebServer/Images/Icons/style-01/asset.png differ diff --git a/WebServer/Images/Icons/style-01/attach.png b/WebServer/Images/Icons/style-01/attach.png new file mode 100644 index 000000000..678806e32 Binary files /dev/null and b/WebServer/Images/Icons/style-01/attach.png differ diff --git a/WebServer/Images/Icons/style-01/attractive.png b/WebServer/Images/Icons/style-01/attractive.png new file mode 100644 index 000000000..62e6fe2e7 Binary files /dev/null and b/WebServer/Images/Icons/style-01/attractive.png differ diff --git a/WebServer/Images/Icons/style-01/band.png b/WebServer/Images/Icons/style-01/band.png new file mode 100644 index 000000000..de6aa8c55 Binary files /dev/null and b/WebServer/Images/Icons/style-01/band.png differ diff --git a/WebServer/Images/Icons/style-01/binance-coin.png b/WebServer/Images/Icons/style-01/binance-coin.png new file mode 100644 index 000000000..60e7bd4c2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/binance-coin.png differ diff --git a/WebServer/Images/Icons/style-01/binance.png b/WebServer/Images/Icons/style-01/binance.png new file mode 100644 index 000000000..8f4f71952 Binary files /dev/null and b/WebServer/Images/Icons/style-01/binance.png differ diff --git a/WebServer/Images/Icons/style-01/bitcoin-cash.png b/WebServer/Images/Icons/style-01/bitcoin-cash.png new file mode 100644 index 000000000..a1fdc4f69 Binary files /dev/null and b/WebServer/Images/Icons/style-01/bitcoin-cash.png differ diff --git a/WebServer/Images/Icons/style-01/bitcoin.png b/WebServer/Images/Icons/style-01/bitcoin.png new file mode 100644 index 000000000..a4ee2a209 Binary files /dev/null and b/WebServer/Images/Icons/style-01/bitcoin.png differ diff --git a/WebServer/Images/Icons/style-01/bitfinex.png b/WebServer/Images/Icons/style-01/bitfinex.png new file mode 100644 index 000000000..d5d3db0e2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/bitfinex.png differ diff --git a/WebServer/Images/Icons/style-01/bitmex.png b/WebServer/Images/Icons/style-01/bitmex.png new file mode 100644 index 000000000..4ae5662db Binary files /dev/null and b/WebServer/Images/Icons/style-01/bitmex.png differ diff --git a/WebServer/Images/Icons/style-01/bittrex.png b/WebServer/Images/Icons/style-01/bittrex.png new file mode 100644 index 000000000..b11a8e692 Binary files /dev/null and b/WebServer/Images/Icons/style-01/bittrex.png differ diff --git a/WebServer/Images/Icons/style-01/bot-indicator.png b/WebServer/Images/Icons/style-01/bot-indicator.png new file mode 100644 index 000000000..96a7d7fd1 Binary files /dev/null and b/WebServer/Images/Icons/style-01/bot-indicator.png differ diff --git a/WebServer/Images/Icons/style-01/bot-sensor.png b/WebServer/Images/Icons/style-01/bot-sensor.png new file mode 100644 index 000000000..650a239d6 Binary files /dev/null and b/WebServer/Images/Icons/style-01/bot-sensor.png differ diff --git a/WebServer/Images/Icons/style-01/bot-trading.png b/WebServer/Images/Icons/style-01/bot-trading.png new file mode 100644 index 000000000..8ba2a5668 Binary files /dev/null and b/WebServer/Images/Icons/style-01/bot-trading.png differ diff --git a/WebServer/Images/Icons/style-01/brainstorming.png b/WebServer/Images/Icons/style-01/brainstorming.png new file mode 100644 index 000000000..993bf0ec2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/brainstorming.png differ diff --git a/WebServer/Images/Icons/style-01/broken-link.png b/WebServer/Images/Icons/style-01/broken-link.png new file mode 100644 index 000000000..1d13ad8ac Binary files /dev/null and b/WebServer/Images/Icons/style-01/broken-link.png differ diff --git a/WebServer/Images/Icons/style-01/calculations-procedure.png b/WebServer/Images/Icons/style-01/calculations-procedure.png new file mode 100644 index 000000000..0883d961e Binary files /dev/null and b/WebServer/Images/Icons/style-01/calculations-procedure.png differ diff --git a/WebServer/Images/Icons/style-01/candles.png b/WebServer/Images/Icons/style-01/candles.png new file mode 100644 index 000000000..8db4abd30 Binary files /dev/null and b/WebServer/Images/Icons/style-01/candles.png differ diff --git a/WebServer/Images/Icons/style-01/cardano.png b/WebServer/Images/Icons/style-01/cardano.png new file mode 100644 index 000000000..68b542366 Binary files /dev/null and b/WebServer/Images/Icons/style-01/cardano.png differ diff --git a/WebServer/Images/Icons/style-01/chart-point-formula.png b/WebServer/Images/Icons/style-01/chart-point-formula.png new file mode 100644 index 000000000..c1ebd1072 Binary files /dev/null and b/WebServer/Images/Icons/style-01/chart-point-formula.png differ diff --git a/WebServer/Images/Icons/style-01/chart-point.png b/WebServer/Images/Icons/style-01/chart-point.png new file mode 100644 index 000000000..9069eee60 Binary files /dev/null and b/WebServer/Images/Icons/style-01/chart-point.png differ diff --git a/WebServer/Images/Icons/style-01/chart-points.png b/WebServer/Images/Icons/style-01/chart-points.png new file mode 100644 index 000000000..7ad931273 Binary files /dev/null and b/WebServer/Images/Icons/style-01/chart-points.png differ diff --git a/WebServer/Images/Icons/style-01/charting-space.png b/WebServer/Images/Icons/style-01/charting-space.png new file mode 100644 index 000000000..b3e320916 Binary files /dev/null and b/WebServer/Images/Icons/style-01/charting-space.png differ diff --git a/WebServer/Images/Icons/style-01/chat.png b/WebServer/Images/Icons/style-01/chat.png new file mode 100644 index 000000000..0b424a267 Binary files /dev/null and b/WebServer/Images/Icons/style-01/chat.png differ diff --git a/WebServer/Images/Icons/style-01/chronometer.png b/WebServer/Images/Icons/style-01/chronometer.png new file mode 100644 index 000000000..9f4821359 Binary files /dev/null and b/WebServer/Images/Icons/style-01/chronometer.png differ diff --git a/WebServer/Images/Icons/style-01/clone.png b/WebServer/Images/Icons/style-01/clone.png new file mode 100644 index 000000000..9b3b41696 Binary files /dev/null and b/WebServer/Images/Icons/style-01/clone.png differ diff --git a/WebServer/Images/Icons/style-01/compass.png b/WebServer/Images/Icons/style-01/compass.png new file mode 100644 index 000000000..e9b80e7b7 Binary files /dev/null and b/WebServer/Images/Icons/style-01/compass.png differ diff --git a/WebServer/Images/Icons/style-01/competition.png b/WebServer/Images/Icons/style-01/competition.png new file mode 100644 index 000000000..96082a10d Binary files /dev/null and b/WebServer/Images/Icons/style-01/competition.png differ diff --git a/WebServer/Images/Icons/style-01/content.png b/WebServer/Images/Icons/style-01/content.png new file mode 100644 index 000000000..b46362501 Binary files /dev/null and b/WebServer/Images/Icons/style-01/content.png differ diff --git a/WebServer/Images/Icons/style-01/crypto-exchange.png b/WebServer/Images/Icons/style-01/crypto-exchange.png new file mode 100644 index 000000000..8c3dc3f12 Binary files /dev/null and b/WebServer/Images/Icons/style-01/crypto-exchange.png differ diff --git a/WebServer/Images/Icons/style-01/crypto-exchanges.png b/WebServer/Images/Icons/style-01/crypto-exchanges.png new file mode 100644 index 000000000..e94ca97b7 Binary files /dev/null and b/WebServer/Images/Icons/style-01/crypto-exchanges.png differ diff --git a/WebServer/Images/Icons/style-01/dash.png b/WebServer/Images/Icons/style-01/dash.png new file mode 100644 index 000000000..755214b1e Binary files /dev/null and b/WebServer/Images/Icons/style-01/dash.png differ diff --git a/WebServer/Images/Icons/style-01/dashboard.png b/WebServer/Images/Icons/style-01/dashboard.png new file mode 100644 index 000000000..9e579ab06 Binary files /dev/null and b/WebServer/Images/Icons/style-01/dashboard.png differ diff --git a/WebServer/Images/Icons/style-01/data-building-procedure.png b/WebServer/Images/Icons/style-01/data-building-procedure.png new file mode 100644 index 000000000..63de055e5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-building-procedure.png differ diff --git a/WebServer/Images/Icons/style-01/data-dependency.png b/WebServer/Images/Icons/style-01/data-dependency.png new file mode 100644 index 000000000..822adf1fd Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-dependency.png differ diff --git a/WebServer/Images/Icons/style-01/data-entry.png b/WebServer/Images/Icons/style-01/data-entry.png new file mode 100644 index 000000000..47f1740f8 Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-entry.png differ diff --git a/WebServer/Images/Icons/style-01/data-formula.png b/WebServer/Images/Icons/style-01/data-formula.png new file mode 100644 index 000000000..58eca845b Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-formula.png differ diff --git a/WebServer/Images/Icons/style-01/data-mine.png b/WebServer/Images/Icons/style-01/data-mine.png new file mode 100644 index 000000000..5987d984c Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-mine.png differ diff --git a/WebServer/Images/Icons/style-01/data-mining.png b/WebServer/Images/Icons/style-01/data-mining.png new file mode 100644 index 000000000..069479644 Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-mining.png differ diff --git a/WebServer/Images/Icons/style-01/data-product.png b/WebServer/Images/Icons/style-01/data-product.png new file mode 100644 index 000000000..a56737af4 Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-product.png differ diff --git a/WebServer/Images/Icons/style-01/data-storage.png b/WebServer/Images/Icons/style-01/data-storage.png new file mode 100644 index 000000000..0371317db Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-storage.png differ diff --git a/WebServer/Images/Icons/style-01/dataset-definition.png b/WebServer/Images/Icons/style-01/dataset-definition.png new file mode 100644 index 000000000..0f4735f92 Binary files /dev/null and b/WebServer/Images/Icons/style-01/dataset-definition.png differ diff --git a/WebServer/Images/Icons/style-01/dataset.png b/WebServer/Images/Icons/style-01/dataset.png new file mode 100644 index 000000000..8059cb8b5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/dataset.png differ diff --git a/WebServer/Images/Icons/style-01/delete.png b/WebServer/Images/Icons/style-01/delete.png new file mode 100644 index 000000000..a26ebea98 Binary files /dev/null and b/WebServer/Images/Icons/style-01/delete.png differ diff --git a/WebServer/Images/Icons/style-01/design-tool.png b/WebServer/Images/Icons/style-01/design-tool.png new file mode 100644 index 000000000..7eb24f192 Binary files /dev/null and b/WebServer/Images/Icons/style-01/design-tool.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-000.png b/WebServer/Images/Icons/style-01/distance-to-parent-000.png new file mode 100644 index 000000000..65e87718a Binary files /dev/null and b/WebServer/Images/Icons/style-01/distance-to-parent-000.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-025.png b/WebServer/Images/Icons/style-01/distance-to-parent-025.png new file mode 100644 index 000000000..bf08c48ac Binary files /dev/null and b/WebServer/Images/Icons/style-01/distance-to-parent-025.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-050.png b/WebServer/Images/Icons/style-01/distance-to-parent-050.png new file mode 100644 index 000000000..b5553349c Binary files /dev/null and b/WebServer/Images/Icons/style-01/distance-to-parent-050.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-100.png b/WebServer/Images/Icons/style-01/distance-to-parent-100.png new file mode 100644 index 000000000..44ad9bb60 Binary files /dev/null and b/WebServer/Images/Icons/style-01/distance-to-parent-100.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-150.png b/WebServer/Images/Icons/style-01/distance-to-parent-150.png new file mode 100644 index 000000000..c83632189 Binary files /dev/null and b/WebServer/Images/Icons/style-01/distance-to-parent-150.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-200.png b/WebServer/Images/Icons/style-01/distance-to-parent-200.png new file mode 100644 index 000000000..537db0065 Binary files /dev/null and b/WebServer/Images/Icons/style-01/distance-to-parent-200.png differ diff --git a/WebServer/Images/Icons/style-01/eos.png b/WebServer/Images/Icons/style-01/eos.png new file mode 100644 index 000000000..d78e25ddd Binary files /dev/null and b/WebServer/Images/Icons/style-01/eos.png differ diff --git a/WebServer/Images/Icons/style-01/ethereum-classic.png b/WebServer/Images/Icons/style-01/ethereum-classic.png new file mode 100644 index 000000000..dc7089844 Binary files /dev/null and b/WebServer/Images/Icons/style-01/ethereum-classic.png differ diff --git a/WebServer/Images/Icons/style-01/ethereum.png b/WebServer/Images/Icons/style-01/ethereum.png new file mode 100644 index 000000000..33b2853a1 Binary files /dev/null and b/WebServer/Images/Icons/style-01/ethereum.png differ diff --git a/WebServer/Images/Icons/style-01/exchange-account-asset.png b/WebServer/Images/Icons/style-01/exchange-account-asset.png new file mode 100644 index 000000000..083125690 Binary files /dev/null and b/WebServer/Images/Icons/style-01/exchange-account-asset.png differ diff --git a/WebServer/Images/Icons/style-01/exchange-account-key.png b/WebServer/Images/Icons/style-01/exchange-account-key.png new file mode 100644 index 000000000..2df386906 Binary files /dev/null and b/WebServer/Images/Icons/style-01/exchange-account-key.png differ diff --git a/WebServer/Images/Icons/style-01/exchange-accounts.png b/WebServer/Images/Icons/style-01/exchange-accounts.png new file mode 100644 index 000000000..3ac313f88 Binary files /dev/null and b/WebServer/Images/Icons/style-01/exchange-accounts.png differ diff --git a/WebServer/Images/Icons/style-01/exchange-assets.png b/WebServer/Images/Icons/style-01/exchange-assets.png new file mode 100644 index 000000000..e5131939d Binary files /dev/null and b/WebServer/Images/Icons/style-01/exchange-assets.png differ diff --git a/WebServer/Images/Icons/style-01/exchange-markets.png b/WebServer/Images/Icons/style-01/exchange-markets.png new file mode 100644 index 000000000..98e70efc1 Binary files /dev/null and b/WebServer/Images/Icons/style-01/exchange-markets.png differ diff --git a/WebServer/Images/Icons/style-01/execution-finished-event.png b/WebServer/Images/Icons/style-01/execution-finished-event.png new file mode 100644 index 000000000..a50061daf Binary files /dev/null and b/WebServer/Images/Icons/style-01/execution-finished-event.png differ diff --git a/WebServer/Images/Icons/style-01/execution-started-event.png b/WebServer/Images/Icons/style-01/execution-started-event.png new file mode 100644 index 000000000..4389a6f16 Binary files /dev/null and b/WebServer/Images/Icons/style-01/execution-started-event.png differ diff --git a/WebServer/Images/Icons/style-01/grid.png b/WebServer/Images/Icons/style-01/grid.png new file mode 100644 index 000000000..b68f70a44 Binary files /dev/null and b/WebServer/Images/Icons/style-01/grid.png differ diff --git a/WebServer/Images/Icons/style-01/hand-drag.png b/WebServer/Images/Icons/style-01/hand-drag.png new file mode 100644 index 000000000..44f0683e2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/hand-drag.png differ diff --git a/WebServer/Images/Icons/style-01/headphones.png b/WebServer/Images/Icons/style-01/headphones.png new file mode 100644 index 000000000..d7c099510 Binary files /dev/null and b/WebServer/Images/Icons/style-01/headphones.png differ diff --git a/WebServer/Images/Icons/style-01/histogram.png b/WebServer/Images/Icons/style-01/histogram.png new file mode 100644 index 000000000..6c5711764 Binary files /dev/null and b/WebServer/Images/Icons/style-01/histogram.png differ diff --git a/WebServer/Images/Icons/style-01/hitbtc.png b/WebServer/Images/Icons/style-01/hitbtc.png new file mode 100644 index 000000000..0a2f39624 Binary files /dev/null and b/WebServer/Images/Icons/style-01/hitbtc.png differ diff --git a/WebServer/Images/Icons/style-01/html.png b/WebServer/Images/Icons/style-01/html.png new file mode 100644 index 000000000..fa46b2b7b Binary files /dev/null and b/WebServer/Images/Icons/style-01/html.png differ diff --git a/WebServer/Images/Icons/style-01/image.png b/WebServer/Images/Icons/style-01/image.png new file mode 100644 index 000000000..60b436b12 Binary files /dev/null and b/WebServer/Images/Icons/style-01/image.png differ diff --git a/WebServer/Images/Icons/style-01/initial-definition.png b/WebServer/Images/Icons/style-01/initial-definition.png new file mode 100644 index 000000000..d6e3db3d5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/initial-definition.png differ diff --git a/WebServer/Images/Icons/style-01/key-alt.png b/WebServer/Images/Icons/style-01/key-alt.png new file mode 100644 index 000000000..b5ad38990 Binary files /dev/null and b/WebServer/Images/Icons/style-01/key-alt.png differ diff --git a/WebServer/Images/Icons/style-01/key-ctrl.png b/WebServer/Images/Icons/style-01/key-ctrl.png new file mode 100644 index 000000000..95a97ba22 Binary files /dev/null and b/WebServer/Images/Icons/style-01/key-ctrl.png differ diff --git a/WebServer/Images/Icons/style-01/key-down.png b/WebServer/Images/Icons/style-01/key-down.png new file mode 100644 index 000000000..cea62f2c6 Binary files /dev/null and b/WebServer/Images/Icons/style-01/key-down.png differ diff --git a/WebServer/Images/Icons/style-01/key-left.png b/WebServer/Images/Icons/style-01/key-left.png new file mode 100644 index 000000000..d359dc5a9 Binary files /dev/null and b/WebServer/Images/Icons/style-01/key-left.png differ diff --git a/WebServer/Images/Icons/style-01/key-right.png b/WebServer/Images/Icons/style-01/key-right.png new file mode 100644 index 000000000..9bb0c39c2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/key-right.png differ diff --git a/WebServer/Images/Icons/style-01/key-shift.png b/WebServer/Images/Icons/style-01/key-shift.png new file mode 100644 index 000000000..2d0f76800 Binary files /dev/null and b/WebServer/Images/Icons/style-01/key-shift.png differ diff --git a/WebServer/Images/Icons/style-01/key-up.png b/WebServer/Images/Icons/style-01/key-up.png new file mode 100644 index 000000000..0442350e0 Binary files /dev/null and b/WebServer/Images/Icons/style-01/key-up.png differ diff --git a/WebServer/Images/Icons/style-01/kraken.png b/WebServer/Images/Icons/style-01/kraken.png new file mode 100644 index 000000000..cf7e2d59e Binary files /dev/null and b/WebServer/Images/Icons/style-01/kraken.png differ diff --git a/WebServer/Images/Icons/style-01/kucoin.png b/WebServer/Images/Icons/style-01/kucoin.png new file mode 100644 index 000000000..c24a5e5c8 Binary files /dev/null and b/WebServer/Images/Icons/style-01/kucoin.png differ diff --git a/WebServer/Images/Icons/style-01/layer-manager.png b/WebServer/Images/Icons/style-01/layer-manager.png new file mode 100644 index 000000000..831267126 Binary files /dev/null and b/WebServer/Images/Icons/style-01/layer-manager.png differ diff --git a/WebServer/Images/Icons/style-01/layout.png b/WebServer/Images/Icons/style-01/layout.png new file mode 100644 index 000000000..eb93ce845 Binary files /dev/null and b/WebServer/Images/Icons/style-01/layout.png differ diff --git a/WebServer/Images/Icons/style-01/liquid.png b/WebServer/Images/Icons/style-01/liquid.png new file mode 100644 index 000000000..4dac18e6b Binary files /dev/null and b/WebServer/Images/Icons/style-01/liquid.png differ diff --git a/WebServer/Images/Icons/style-01/litecoin.png b/WebServer/Images/Icons/style-01/litecoin.png new file mode 100644 index 000000000..27080fd75 Binary files /dev/null and b/WebServer/Images/Icons/style-01/litecoin.png differ diff --git a/WebServer/Images/Icons/style-01/loading.png b/WebServer/Images/Icons/style-01/loading.png new file mode 100644 index 000000000..845c02cbd Binary files /dev/null and b/WebServer/Images/Icons/style-01/loading.png differ diff --git a/WebServer/Images/Icons/style-01/login.png b/WebServer/Images/Icons/style-01/login.png new file mode 100644 index 000000000..16a99c7b1 Binary files /dev/null and b/WebServer/Images/Icons/style-01/login.png differ diff --git a/WebServer/Images/Icons/style-01/market.png b/WebServer/Images/Icons/style-01/market.png new file mode 100644 index 000000000..ba224734d Binary files /dev/null and b/WebServer/Images/Icons/style-01/market.png differ diff --git a/WebServer/Images/Icons/style-01/menu-backup.png b/WebServer/Images/Icons/style-01/menu-backup.png new file mode 100644 index 000000000..142a2fa80 Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-backup.png differ diff --git a/WebServer/Images/Icons/style-01/menu-fix-pinned.png b/WebServer/Images/Icons/style-01/menu-fix-pinned.png new file mode 100644 index 000000000..ba637d28d Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-fix-pinned.png differ diff --git a/WebServer/Images/Icons/style-01/menu-fix-unpinned.png b/WebServer/Images/Icons/style-01/menu-fix-unpinned.png new file mode 100644 index 000000000..c574c5b19 Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-fix-unpinned.png differ diff --git a/WebServer/Images/Icons/style-01/menu-mobility-freeze.png b/WebServer/Images/Icons/style-01/menu-mobility-freeze.png new file mode 100644 index 000000000..fd89c6208 Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-mobility-freeze.png differ diff --git a/WebServer/Images/Icons/style-01/menu-mobility-unfreeze.png b/WebServer/Images/Icons/style-01/menu-mobility-unfreeze.png new file mode 100644 index 000000000..df97739ca Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-mobility-unfreeze.png differ diff --git a/WebServer/Images/Icons/style-01/menu-share.png b/WebServer/Images/Icons/style-01/menu-share.png new file mode 100644 index 000000000..c4998af5e Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-share.png differ diff --git a/WebServer/Images/Icons/style-01/menu-tensor-fixed-angles.png b/WebServer/Images/Icons/style-01/menu-tensor-fixed-angles.png new file mode 100644 index 000000000..9fe32b7a2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-tensor-fixed-angles.png differ diff --git a/WebServer/Images/Icons/style-01/menu-tensor-free-angles.png b/WebServer/Images/Icons/style-01/menu-tensor-free-angles.png new file mode 100644 index 000000000..2fdd02cca Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-tensor-free-angles.png differ diff --git a/WebServer/Images/Icons/style-01/menu-tree-minus.png b/WebServer/Images/Icons/style-01/menu-tree-minus.png new file mode 100644 index 000000000..2a8158670 Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-tree-minus.png differ diff --git a/WebServer/Images/Icons/style-01/menu-tree-plus.png b/WebServer/Images/Icons/style-01/menu-tree-plus.png new file mode 100644 index 000000000..637282507 Binary files /dev/null and b/WebServer/Images/Icons/style-01/menu-tree-plus.png differ diff --git a/WebServer/Images/Icons/style-01/monero.png b/WebServer/Images/Icons/style-01/monero.png new file mode 100644 index 000000000..eb938c3f7 Binary files /dev/null and b/WebServer/Images/Icons/style-01/monero.png differ diff --git a/WebServer/Images/Icons/style-01/mouse-left-click.png b/WebServer/Images/Icons/style-01/mouse-left-click.png new file mode 100644 index 000000000..be3e72840 Binary files /dev/null and b/WebServer/Images/Icons/style-01/mouse-left-click.png differ diff --git a/WebServer/Images/Icons/style-01/mouse-pointer.png b/WebServer/Images/Icons/style-01/mouse-pointer.png new file mode 100644 index 000000000..08b2ead5e Binary files /dev/null and b/WebServer/Images/Icons/style-01/mouse-pointer.png differ diff --git a/WebServer/Images/Icons/style-01/mouse-right-click.png b/WebServer/Images/Icons/style-01/mouse-right-click.png new file mode 100644 index 000000000..0e1a287b3 Binary files /dev/null and b/WebServer/Images/Icons/style-01/mouse-right-click.png differ diff --git a/WebServer/Images/Icons/style-01/mouse-wheel-click.png b/WebServer/Images/Icons/style-01/mouse-wheel-click.png new file mode 100644 index 000000000..6dab9a4ff Binary files /dev/null and b/WebServer/Images/Icons/style-01/mouse-wheel-click.png differ diff --git a/WebServer/Images/Icons/style-01/mouse-wheel-down.png b/WebServer/Images/Icons/style-01/mouse-wheel-down.png new file mode 100644 index 000000000..58386b47d Binary files /dev/null and b/WebServer/Images/Icons/style-01/mouse-wheel-down.png differ diff --git a/WebServer/Images/Icons/style-01/mouse-wheel-up.png b/WebServer/Images/Icons/style-01/mouse-wheel-up.png new file mode 100644 index 000000000..a11479633 Binary files /dev/null and b/WebServer/Images/Icons/style-01/mouse-wheel-up.png differ diff --git a/WebServer/Images/Icons/style-01/mouse-wheel.png b/WebServer/Images/Icons/style-01/mouse-wheel.png new file mode 100644 index 000000000..c26a3517a Binary files /dev/null and b/WebServer/Images/Icons/style-01/mouse-wheel.png differ diff --git a/WebServer/Images/Icons/style-01/moving-average.png b/WebServer/Images/Icons/style-01/moving-average.png new file mode 100644 index 000000000..5d62c67e3 Binary files /dev/null and b/WebServer/Images/Icons/style-01/moving-average.png differ diff --git a/WebServer/Images/Icons/style-01/neo.png b/WebServer/Images/Icons/style-01/neo.png new file mode 100644 index 000000000..ab608f9f9 Binary files /dev/null and b/WebServer/Images/Icons/style-01/neo.png differ diff --git a/WebServer/Images/Icons/style-01/network-node.png b/WebServer/Images/Icons/style-01/network-node.png new file mode 100644 index 000000000..30782f8e3 Binary files /dev/null and b/WebServer/Images/Icons/style-01/network-node.png differ diff --git a/WebServer/Images/Icons/style-01/network.png b/WebServer/Images/Icons/style-01/network.png new file mode 100644 index 000000000..0880e7e8d Binary files /dev/null and b/WebServer/Images/Icons/style-01/network.png differ diff --git a/WebServer/Images/Icons/style-01/next-phase-event.png b/WebServer/Images/Icons/style-01/next-phase-event.png new file mode 100644 index 000000000..efc14dbd0 Binary files /dev/null and b/WebServer/Images/Icons/style-01/next-phase-event.png differ diff --git a/WebServer/Images/Icons/style-01/oscillator.png b/WebServer/Images/Icons/style-01/oscillator.png new file mode 100644 index 000000000..7c4f9445a Binary files /dev/null and b/WebServer/Images/Icons/style-01/oscillator.png differ diff --git a/WebServer/Images/Icons/style-01/output-dataset.png b/WebServer/Images/Icons/style-01/output-dataset.png new file mode 100644 index 000000000..a8a34710a Binary files /dev/null and b/WebServer/Images/Icons/style-01/output-dataset.png differ diff --git a/WebServer/Images/Icons/style-01/pantone.png b/WebServer/Images/Icons/style-01/pantone.png new file mode 100644 index 000000000..0ea9cae40 Binary files /dev/null and b/WebServer/Images/Icons/style-01/pantone.png differ diff --git a/WebServer/Images/Icons/style-01/paper-plane.png b/WebServer/Images/Icons/style-01/paper-plane.png new file mode 100644 index 000000000..b61292bbc Binary files /dev/null and b/WebServer/Images/Icons/style-01/paper-plane.png differ diff --git a/WebServer/Images/Icons/style-01/photo-camera.png b/WebServer/Images/Icons/style-01/photo-camera.png new file mode 100644 index 000000000..77336bdff Binary files /dev/null and b/WebServer/Images/Icons/style-01/photo-camera.png differ diff --git a/WebServer/Images/Icons/style-01/piggy-bank.png b/WebServer/Images/Icons/style-01/piggy-bank.png new file mode 100644 index 000000000..5fe9f5062 Binary files /dev/null and b/WebServer/Images/Icons/style-01/piggy-bank.png differ diff --git a/WebServer/Images/Icons/style-01/pipette.png b/WebServer/Images/Icons/style-01/pipette.png new file mode 100644 index 000000000..5d854018c Binary files /dev/null and b/WebServer/Images/Icons/style-01/pipette.png differ diff --git a/WebServer/Images/Icons/style-01/pixel.png b/WebServer/Images/Icons/style-01/pixel.png new file mode 100644 index 000000000..0dd36069e Binary files /dev/null and b/WebServer/Images/Icons/style-01/pixel.png differ diff --git a/WebServer/Images/Icons/style-01/placeholder.png b/WebServer/Images/Icons/style-01/placeholder.png new file mode 100644 index 000000000..b9d3765cf Binary files /dev/null and b/WebServer/Images/Icons/style-01/placeholder.png differ diff --git a/WebServer/Images/Icons/style-01/planning.png b/WebServer/Images/Icons/style-01/planning.png new file mode 100644 index 000000000..d6e3db3d5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/planning.png differ diff --git a/WebServer/Images/Icons/style-01/plotter-module.png b/WebServer/Images/Icons/style-01/plotter-module.png new file mode 100644 index 000000000..476f02301 Binary files /dev/null and b/WebServer/Images/Icons/style-01/plotter-module.png differ diff --git a/WebServer/Images/Icons/style-01/plotter-panel.png b/WebServer/Images/Icons/style-01/plotter-panel.png new file mode 100644 index 000000000..001875359 Binary files /dev/null and b/WebServer/Images/Icons/style-01/plotter-panel.png differ diff --git a/WebServer/Images/Icons/style-01/plotter.png b/WebServer/Images/Icons/style-01/plotter.png new file mode 100644 index 000000000..5b1a23445 Binary files /dev/null and b/WebServer/Images/Icons/style-01/plotter.png differ diff --git a/WebServer/Images/Icons/style-01/poloniex.png b/WebServer/Images/Icons/style-01/poloniex.png new file mode 100644 index 000000000..2e29586a3 Binary files /dev/null and b/WebServer/Images/Icons/style-01/poloniex.png differ diff --git a/WebServer/Images/Icons/style-01/procedure-initialization.png b/WebServer/Images/Icons/style-01/procedure-initialization.png new file mode 100644 index 000000000..9adc971cf Binary files /dev/null and b/WebServer/Images/Icons/style-01/procedure-initialization.png differ diff --git a/WebServer/Images/Icons/style-01/procedure-loop.png b/WebServer/Images/Icons/style-01/procedure-loop.png new file mode 100644 index 000000000..bd5b5990a Binary files /dev/null and b/WebServer/Images/Icons/style-01/procedure-loop.png differ diff --git a/WebServer/Images/Icons/style-01/process-definition.png b/WebServer/Images/Icons/style-01/process-definition.png new file mode 100644 index 000000000..bd89e9373 Binary files /dev/null and b/WebServer/Images/Icons/style-01/process-definition.png differ diff --git a/WebServer/Images/Icons/style-01/process-dependencies.png b/WebServer/Images/Icons/style-01/process-dependencies.png new file mode 100644 index 000000000..dc000d321 Binary files /dev/null and b/WebServer/Images/Icons/style-01/process-dependencies.png differ diff --git a/WebServer/Images/Icons/style-01/process-output.png b/WebServer/Images/Icons/style-01/process-output.png new file mode 100644 index 000000000..f415f81a7 Binary files /dev/null and b/WebServer/Images/Icons/style-01/process-output.png differ diff --git a/WebServer/Images/Icons/style-01/process.png b/WebServer/Images/Icons/style-01/process.png new file mode 100644 index 000000000..bf6e71161 Binary files /dev/null and b/WebServer/Images/Icons/style-01/process.png differ diff --git a/WebServer/Images/Icons/style-01/product-definition.png b/WebServer/Images/Icons/style-01/product-definition.png new file mode 100644 index 000000000..80ef1048d Binary files /dev/null and b/WebServer/Images/Icons/style-01/product-definition.png differ diff --git a/WebServer/Images/Icons/style-01/production-environment.png b/WebServer/Images/Icons/style-01/production-environment.png new file mode 100644 index 000000000..75c356de6 Binary files /dev/null and b/WebServer/Images/Icons/style-01/production-environment.png differ diff --git a/WebServer/Images/Icons/style-01/promotion.png b/WebServer/Images/Icons/style-01/promotion.png new file mode 100644 index 000000000..2d38a607d Binary files /dev/null and b/WebServer/Images/Icons/style-01/promotion.png differ diff --git a/WebServer/Images/Icons/style-01/pyramid.png b/WebServer/Images/Icons/style-01/pyramid.png new file mode 100644 index 000000000..4b940968b Binary files /dev/null and b/WebServer/Images/Icons/style-01/pyramid.png differ diff --git a/WebServer/Images/Icons/style-01/qtum.png b/WebServer/Images/Icons/style-01/qtum.png new file mode 100644 index 000000000..c8df5a140 Binary files /dev/null and b/WebServer/Images/Icons/style-01/qtum.png differ diff --git a/WebServer/Images/Icons/style-01/quality.png b/WebServer/Images/Icons/style-01/quality.png new file mode 100644 index 000000000..10ee8b6bd Binary files /dev/null and b/WebServer/Images/Icons/style-01/quality.png differ diff --git a/WebServer/Images/Icons/style-01/rate-scale.png b/WebServer/Images/Icons/style-01/rate-scale.png new file mode 100644 index 000000000..7471b89ff Binary files /dev/null and b/WebServer/Images/Icons/style-01/rate-scale.png differ diff --git a/WebServer/Images/Icons/style-01/record-definition.png b/WebServer/Images/Icons/style-01/record-definition.png new file mode 100644 index 000000000..364d300c0 Binary files /dev/null and b/WebServer/Images/Icons/style-01/record-definition.png differ diff --git a/WebServer/Images/Icons/style-01/record-property.png b/WebServer/Images/Icons/style-01/record-property.png new file mode 100644 index 000000000..3bc17e408 Binary files /dev/null and b/WebServer/Images/Icons/style-01/record-property.png differ diff --git a/WebServer/Images/Icons/style-01/responsive.png b/WebServer/Images/Icons/style-01/responsive.png new file mode 100644 index 000000000..3de364530 Binary files /dev/null and b/WebServer/Images/Icons/style-01/responsive.png differ diff --git a/WebServer/Images/Icons/style-01/schedule.png b/WebServer/Images/Icons/style-01/schedule.png new file mode 100644 index 000000000..cf928a0ea Binary files /dev/null and b/WebServer/Images/Icons/style-01/schedule.png differ diff --git a/WebServer/Images/Icons/style-01/search.png b/WebServer/Images/Icons/style-01/search.png new file mode 100644 index 000000000..595845f03 Binary files /dev/null and b/WebServer/Images/Icons/style-01/search.png differ diff --git a/WebServer/Images/Icons/style-01/security.png b/WebServer/Images/Icons/style-01/security.png new file mode 100644 index 000000000..cc32f2462 Binary files /dev/null and b/WebServer/Images/Icons/style-01/security.png differ diff --git a/WebServer/Images/Icons/style-01/session-backtesting.png b/WebServer/Images/Icons/style-01/session-backtesting.png new file mode 100644 index 000000000..25170c9e9 Binary files /dev/null and b/WebServer/Images/Icons/style-01/session-backtesting.png differ diff --git a/WebServer/Images/Icons/style-01/session-based-data.png b/WebServer/Images/Icons/style-01/session-based-data.png new file mode 100644 index 000000000..cd3e2bd5b Binary files /dev/null and b/WebServer/Images/Icons/style-01/session-based-data.png differ diff --git a/WebServer/Images/Icons/style-01/session-forward-testing.png b/WebServer/Images/Icons/style-01/session-forward-testing.png new file mode 100644 index 000000000..d32066568 Binary files /dev/null and b/WebServer/Images/Icons/style-01/session-forward-testing.png differ diff --git a/WebServer/Images/Icons/style-01/session-independent-data.png b/WebServer/Images/Icons/style-01/session-independent-data.png new file mode 100644 index 000000000..1b69e88d5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/session-independent-data.png differ diff --git a/WebServer/Images/Icons/style-01/session-live-trading.png b/WebServer/Images/Icons/style-01/session-live-trading.png new file mode 100644 index 000000000..02d91af01 Binary files /dev/null and b/WebServer/Images/Icons/style-01/session-live-trading.png differ diff --git a/WebServer/Images/Icons/style-01/session-paper-trading.png b/WebServer/Images/Icons/style-01/session-paper-trading.png new file mode 100644 index 000000000..5e8413e7e Binary files /dev/null and b/WebServer/Images/Icons/style-01/session-paper-trading.png differ diff --git a/WebServer/Images/Icons/style-01/session-reference.png b/WebServer/Images/Icons/style-01/session-reference.png new file mode 100644 index 000000000..d3c017532 Binary files /dev/null and b/WebServer/Images/Icons/style-01/session-reference.png differ diff --git a/WebServer/Images/Icons/style-01/settings.png b/WebServer/Images/Icons/style-01/settings.png new file mode 100644 index 000000000..878a9aa46 Binary files /dev/null and b/WebServer/Images/Icons/style-01/settings.png differ diff --git a/WebServer/Images/Icons/style-01/shapes-polygon-body.png b/WebServer/Images/Icons/style-01/shapes-polygon-body.png new file mode 100644 index 000000000..33a546c98 Binary files /dev/null and b/WebServer/Images/Icons/style-01/shapes-polygon-body.png differ diff --git a/WebServer/Images/Icons/style-01/shapes-polygon-border.png b/WebServer/Images/Icons/style-01/shapes-polygon-border.png new file mode 100644 index 000000000..b8b56d398 Binary files /dev/null and b/WebServer/Images/Icons/style-01/shapes-polygon-border.png differ diff --git a/WebServer/Images/Icons/style-01/shapes-polygon-vertex.png b/WebServer/Images/Icons/style-01/shapes-polygon-vertex.png new file mode 100644 index 000000000..2eaa6c232 Binary files /dev/null and b/WebServer/Images/Icons/style-01/shapes-polygon-vertex.png differ diff --git a/WebServer/Images/Icons/style-01/shapes-polygon.png b/WebServer/Images/Icons/style-01/shapes-polygon.png new file mode 100644 index 000000000..5b7ac0185 Binary files /dev/null and b/WebServer/Images/Icons/style-01/shapes-polygon.png differ diff --git a/WebServer/Images/Icons/style-01/shapes.png b/WebServer/Images/Icons/style-01/shapes.png new file mode 100644 index 000000000..67644126d Binary files /dev/null and b/WebServer/Images/Icons/style-01/shapes.png differ diff --git a/WebServer/Images/Icons/style-01/single-market-data.png b/WebServer/Images/Icons/style-01/single-market-data.png new file mode 100644 index 000000000..d88dbefb3 Binary files /dev/null and b/WebServer/Images/Icons/style-01/single-market-data.png differ diff --git a/WebServer/Images/Icons/style-01/sitemap.png b/WebServer/Images/Icons/style-01/sitemap.png new file mode 100644 index 000000000..0c864590f Binary files /dev/null and b/WebServer/Images/Icons/style-01/sitemap.png differ diff --git a/WebServer/Images/Icons/style-01/social-bot.png b/WebServer/Images/Icons/style-01/social-bot.png new file mode 100644 index 000000000..1df5bcc28 Binary files /dev/null and b/WebServer/Images/Icons/style-01/social-bot.png differ diff --git a/WebServer/Images/Icons/style-01/stage-close.png b/WebServer/Images/Icons/style-01/stage-close.png new file mode 100644 index 000000000..beb6dc1bd Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-close.png differ diff --git a/WebServer/Images/Icons/style-01/stage-open-position-size.png b/WebServer/Images/Icons/style-01/stage-open-position-size.png new file mode 100644 index 000000000..7eab6812e Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-open-position-size.png differ diff --git a/WebServer/Images/Icons/style-01/stage-open-postion-rate.png b/WebServer/Images/Icons/style-01/stage-open-postion-rate.png new file mode 100644 index 000000000..ea634cd71 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-open-postion-rate.png differ diff --git a/WebServer/Images/Icons/style-01/stage-open-take-profit.png b/WebServer/Images/Icons/style-01/stage-open-take-profit.png new file mode 100644 index 000000000..c3db5908e Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-open-take-profit.png differ diff --git a/WebServer/Images/Icons/style-01/stage-open.png b/WebServer/Images/Icons/style-01/stage-open.png new file mode 100644 index 000000000..0a66b09e9 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-open.png differ diff --git a/WebServer/Images/Icons/style-01/stage-trigger-take-position.png b/WebServer/Images/Icons/style-01/stage-trigger-take-position.png new file mode 100644 index 000000000..5e03218d2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-trigger-take-position.png differ diff --git a/WebServer/Images/Icons/style-01/stage-trigger-trigger-off.png b/WebServer/Images/Icons/style-01/stage-trigger-trigger-off.png new file mode 100644 index 000000000..cb1076837 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-trigger-trigger-off.png differ diff --git a/WebServer/Images/Icons/style-01/stage-trigger-trigger-on.png b/WebServer/Images/Icons/style-01/stage-trigger-trigger-on.png new file mode 100644 index 000000000..e56d56f64 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-trigger-trigger-on.png differ diff --git a/WebServer/Images/Icons/style-01/stage-trigger.png b/WebServer/Images/Icons/style-01/stage-trigger.png new file mode 100644 index 000000000..83a87e2d6 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stage-trigger.png differ diff --git a/WebServer/Images/Icons/style-01/startup.png b/WebServer/Images/Icons/style-01/startup.png new file mode 100644 index 000000000..7ed8d6cb0 Binary files /dev/null and b/WebServer/Images/Icons/style-01/startup.png differ diff --git a/WebServer/Images/Icons/style-01/status-dependency.png b/WebServer/Images/Icons/style-01/status-dependency.png new file mode 100644 index 000000000..cb318dee5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/status-dependency.png differ diff --git a/WebServer/Images/Icons/style-01/status-report.png b/WebServer/Images/Icons/style-01/status-report.png new file mode 100644 index 000000000..f3361a6e9 Binary files /dev/null and b/WebServer/Images/Icons/style-01/status-report.png differ diff --git a/WebServer/Images/Icons/style-01/stellar.png b/WebServer/Images/Icons/style-01/stellar.png new file mode 100644 index 000000000..d853f8e51 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stellar.png differ diff --git a/WebServer/Images/Icons/style-01/stop.png b/WebServer/Images/Icons/style-01/stop.png new file mode 100644 index 000000000..65fff4f18 Binary files /dev/null and b/WebServer/Images/Icons/style-01/stop.png differ diff --git a/WebServer/Images/Icons/style-01/style-condition.png b/WebServer/Images/Icons/style-01/style-condition.png new file mode 100644 index 000000000..eb9a065c1 Binary files /dev/null and b/WebServer/Images/Icons/style-01/style-condition.png differ diff --git a/WebServer/Images/Icons/style-01/style.png b/WebServer/Images/Icons/style-01/style.png new file mode 100644 index 000000000..4ae248158 Binary files /dev/null and b/WebServer/Images/Icons/style-01/style.png differ diff --git a/WebServer/Images/Icons/style-01/support.png b/WebServer/Images/Icons/style-01/support.png new file mode 100644 index 000000000..fcafb34f3 Binary files /dev/null and b/WebServer/Images/Icons/style-01/support.png differ diff --git a/WebServer/Images/Icons/style-01/tap.png b/WebServer/Images/Icons/style-01/tap.png new file mode 100644 index 000000000..7f958daf5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/tap.png differ diff --git a/WebServer/Images/Icons/style-01/targeting.png b/WebServer/Images/Icons/style-01/targeting.png new file mode 100644 index 000000000..ef53bb1e6 Binary files /dev/null and b/WebServer/Images/Icons/style-01/targeting.png differ diff --git a/WebServer/Images/Icons/style-01/task-manager.png b/WebServer/Images/Icons/style-01/task-manager.png new file mode 100644 index 000000000..ee8d2bc25 Binary files /dev/null and b/WebServer/Images/Icons/style-01/task-manager.png differ diff --git a/WebServer/Images/Icons/style-01/task.png b/WebServer/Images/Icons/style-01/task.png new file mode 100644 index 000000000..e85e1ba2b Binary files /dev/null and b/WebServer/Images/Icons/style-01/task.png differ diff --git a/WebServer/Images/Icons/style-01/testing-environment.png b/WebServer/Images/Icons/style-01/testing-environment.png new file mode 100644 index 000000000..d634fbc0e Binary files /dev/null and b/WebServer/Images/Icons/style-01/testing-environment.png differ diff --git a/WebServer/Images/Icons/style-01/testing.png b/WebServer/Images/Icons/style-01/testing.png new file mode 100644 index 000000000..2c3a28848 Binary files /dev/null and b/WebServer/Images/Icons/style-01/testing.png differ diff --git a/WebServer/Images/Icons/style-01/tether.png b/WebServer/Images/Icons/style-01/tether.png new file mode 100644 index 000000000..af6e43f43 Binary files /dev/null and b/WebServer/Images/Icons/style-01/tether.png differ diff --git a/WebServer/Images/Icons/style-01/text.png b/WebServer/Images/Icons/style-01/text.png new file mode 100644 index 000000000..959e64e90 Binary files /dev/null and b/WebServer/Images/Icons/style-01/text.png differ diff --git a/WebServer/Images/Icons/style-01/time-frame-scale.png b/WebServer/Images/Icons/style-01/time-frame-scale.png new file mode 100644 index 000000000..7aeece127 Binary files /dev/null and b/WebServer/Images/Icons/style-01/time-frame-scale.png differ diff --git a/WebServer/Images/Icons/style-01/time-line-chart.png b/WebServer/Images/Icons/style-01/time-line-chart.png new file mode 100644 index 000000000..3525af058 Binary files /dev/null and b/WebServer/Images/Icons/style-01/time-line-chart.png differ diff --git a/WebServer/Images/Icons/style-01/time-machine.png b/WebServer/Images/Icons/style-01/time-machine.png new file mode 100644 index 000000000..f10e3dda7 Binary files /dev/null and b/WebServer/Images/Icons/style-01/time-machine.png differ diff --git a/WebServer/Images/Icons/style-01/time-period.png b/WebServer/Images/Icons/style-01/time-period.png new file mode 100644 index 000000000..15ea83404 Binary files /dev/null and b/WebServer/Images/Icons/style-01/time-period.png differ diff --git a/WebServer/Images/Icons/style-01/time-scale.png b/WebServer/Images/Icons/style-01/time-scale.png new file mode 100644 index 000000000..462140c6b Binary files /dev/null and b/WebServer/Images/Icons/style-01/time-scale.png differ diff --git a/WebServer/Images/Icons/style-01/timeline.png b/WebServer/Images/Icons/style-01/timeline.png new file mode 100644 index 000000000..8a7051d9f Binary files /dev/null and b/WebServer/Images/Icons/style-01/timeline.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-max.png b/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-max.png new file mode 100644 index 000000000..fa8168e6c Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-max.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-min-max.png b/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-min-max.png new file mode 100644 index 000000000..e7549e1e6 Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-min-max.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-min.png b/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-min.png new file mode 100644 index 000000000..22e67cda5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-scale-auto-min.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-scale-manual.png b/WebServer/Images/Icons/style-01/toggle-auto-scale-manual.png new file mode 100644 index 000000000..282f4dbac Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-scale-manual.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-max.png b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-max.png new file mode 100644 index 000000000..37b6f77b2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-max.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-min-max.png b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-min-max.png new file mode 100644 index 000000000..a193213f7 Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-min-max.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-min.png b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-min.png new file mode 100644 index 000000000..7c4a26043 Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-auto-min.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-auto-time-scale-manual.png b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-manual.png new file mode 100644 index 000000000..a8da7ca60 Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-auto-time-scale-manual.png differ diff --git a/WebServer/Images/Icons/style-01/toggle-panel-off.png b/WebServer/Images/Icons/style-01/toggle-panel-off.png new file mode 100644 index 000000000..032a33409 Binary files /dev/null and b/WebServer/Images/Icons/style-01/toggle-panel-off.png differ diff --git a/WebServer/Images/Icons/style-01/tools.png b/WebServer/Images/Icons/style-01/tools.png new file mode 100644 index 000000000..a22d7f0a3 Binary files /dev/null and b/WebServer/Images/Icons/style-01/tools.png differ diff --git a/WebServer/Images/Icons/style-01/trend.png b/WebServer/Images/Icons/style-01/trend.png new file mode 100644 index 000000000..1a57bb1a1 Binary files /dev/null and b/WebServer/Images/Icons/style-01/trend.png differ diff --git a/WebServer/Images/Icons/style-01/tron.png b/WebServer/Images/Icons/style-01/tron.png new file mode 100644 index 000000000..65ebcbd98 Binary files /dev/null and b/WebServer/Images/Icons/style-01/tron.png differ diff --git a/WebServer/Images/Icons/style-01/upbit.png b/WebServer/Images/Icons/style-01/upbit.png new file mode 100644 index 000000000..6e3cb476d Binary files /dev/null and b/WebServer/Images/Icons/style-01/upbit.png differ diff --git a/WebServer/Images/Icons/style-01/upload.png b/WebServer/Images/Icons/style-01/upload.png new file mode 100644 index 000000000..50e4d79c2 Binary files /dev/null and b/WebServer/Images/Icons/style-01/upload.png differ diff --git a/WebServer/Images/Icons/style-01/us-dollar.png b/WebServer/Images/Icons/style-01/us-dollar.png new file mode 100644 index 000000000..ba31d4f09 Binary files /dev/null and b/WebServer/Images/Icons/style-01/us-dollar.png differ diff --git a/WebServer/Images/Icons/style-01/user-account.png b/WebServer/Images/Icons/style-01/user-account.png new file mode 100644 index 000000000..8c3b677da Binary files /dev/null and b/WebServer/Images/Icons/style-01/user-account.png differ diff --git a/WebServer/Images/Icons/style-01/user-assets.png b/WebServer/Images/Icons/style-01/user-assets.png new file mode 100644 index 000000000..bf9f73b4e Binary files /dev/null and b/WebServer/Images/Icons/style-01/user-assets.png differ diff --git a/WebServer/Images/Icons/style-01/user-keys.png b/WebServer/Images/Icons/style-01/user-keys.png new file mode 100644 index 000000000..906fe456f Binary files /dev/null and b/WebServer/Images/Icons/style-01/user-keys.png differ diff --git a/WebServer/Images/Icons/style-01/vector.png b/WebServer/Images/Icons/style-01/vector.png new file mode 100644 index 000000000..b9b1e7a02 Binary files /dev/null and b/WebServer/Images/Icons/style-01/vector.png differ diff --git a/WebServer/Images/Icons/style-01/video-player.png b/WebServer/Images/Icons/style-01/video-player.png new file mode 100644 index 000000000..b382fe80e Binary files /dev/null and b/WebServer/Images/Icons/style-01/video-player.png differ diff --git a/WebServer/Images/Icons/style-01/viewport.png b/WebServer/Images/Icons/style-01/viewport.png new file mode 100644 index 000000000..1365ecd7b Binary files /dev/null and b/WebServer/Images/Icons/style-01/viewport.png differ diff --git a/WebServer/Images/Icons/style-01/volume.png b/WebServer/Images/Icons/style-01/volume.png new file mode 100644 index 000000000..60d213b6d Binary files /dev/null and b/WebServer/Images/Icons/style-01/volume.png differ diff --git a/WebServer/Images/superalgos-header-background.png b/WebServer/Images/superalgos-header-background.png new file mode 100644 index 000000000..5c980aa38 Binary files /dev/null and b/WebServer/Images/superalgos-header-background.png differ diff --git a/WebServer/Images/superalgos-logo-for-color-background.png b/WebServer/Images/superalgos-logo-for-color-background.png new file mode 100644 index 000000000..40f8de2ab Binary files /dev/null and b/WebServer/Images/superalgos-logo-for-color-background.png differ diff --git a/WebServer/Images/superalgos-logo.png b/WebServer/Images/superalgos-logo.png new file mode 100644 index 000000000..995d19538 Binary files /dev/null and b/WebServer/Images/superalgos-logo.png differ diff --git a/WebServer/Images/superalgos-platform.jpg b/WebServer/Images/superalgos-platform.jpg new file mode 100644 index 000000000..dab376c25 Binary files /dev/null and b/WebServer/Images/superalgos-platform.jpg differ diff --git a/WebServer/LegacyPlotter.js b/WebServer/LegacyPlotter.js new file mode 100644 index 000000000..cb4e49753 --- /dev/null +++ b/WebServer/LegacyPlotter.js @@ -0,0 +1,6 @@ +function getNewPlotter(pDataMineOrHost, pPlotter, pModule) { + let functionName = pDataMineOrHost + pPlotter + pModule; + functionName = functionName.replace(/-/g, ""); + functionName = 'new' + functionName + return window[functionName](); +} diff --git a/WebServer/PlotterPanel.js b/WebServer/PlotterPanel.js new file mode 100644 index 000000000..75e63863e --- /dev/null +++ b/WebServer/PlotterPanel.js @@ -0,0 +1,6 @@ +function getNewPlotterPanel(pDataMineOrHost, pPlotter, pModule, pPanel) { + let functionName = pDataMineOrHost + pPlotter + pModule + pPanel; + functionName = functionName.replace(/-/g, ""); + functionName = 'new' + functionName + return window[functionName](); +} diff --git a/WebServer/Scripts/AppLoader.js b/WebServer/Scripts/AppLoader.js new file mode 100644 index 000000000..5e4c43edd --- /dev/null +++ b/WebServer/Scripts/AppLoader.js @@ -0,0 +1,250 @@ +let dashboard + +function newAppLoader() { + const MODULE_NAME = 'App Loader' + const INFO_LOG = false + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + loadModules: loadModules + } + + return thisObject + + async function loadModules() { + try { + if (INFO_LOG === true) { logger.write('[INFO] loadModules -> Entering function.') } + + window.localStorage.removeItem('ecosystem') + + let plotters + let modulesArray = [] + + plotters = defaultPlotters() + modulesArray = ['UserEcosystem.js'] + + modulesArray = modulesArray.concat([ + 'ChartingSpace/Viewport.js', + + 'Globals.js', + 'Ecosystem.js', + 'UserEcosystem.js', + 'AppSchema.js', + 'MQService', + + 'ChartingSpace/EdgeEditor.js', + 'ChartingSpace/ChartUtilities.js', + 'ChartingSpace/PlottersManager.js', + 'ChartingSpace/TimelineChart.js', + 'ChartingSpace/TimeMachine.js', + 'ChartingSpace/CoordinateSystem.js', + + 'DesignSpace/Workspace/Workspace.js', + 'DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js', + 'DesignSpace/Workspace/FunctionLibraries/ChainAttachDetach.js', + 'DesignSpace/Workspace/FunctionLibraries/ReferenceAttachDetach.js', + 'DesignSpace/Workspace/FunctionLibraries/NodeDeleter.js', + 'DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js', + 'DesignSpace/Workspace/FunctionLibraries/NodeCloning.js', + 'DesignSpace/Workspace/FunctionLibraries/NodeChildren.js', + 'DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js', + 'DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js', + 'DesignSpace/Workspace/FunctionLibraries/ShortcutKeys.js', + 'DesignSpace/Workspace/FunctionLibraries/OnFocus.js', + + 'Utilities/RoundedCornersBackground.js', + + 'Panels/LayersPanel.js', + 'Panels/PanelTabButton.js', + 'Panels/Layer.js', + 'Panels/PlotterPanel.js', + 'Panels/PanelsVisibleButton.js', + + 'ControlsToolBox/SidePanel.js', + 'ControlsToolBox/SidePanelTab.js', + + 'Spaces/CockpitSpace.js', + 'Spaces/TopSpace.js', + 'Spaces/PanelsSpace.js', + 'Spaces/ChartingSpace.js', + 'Spaces/FloatingSpace.js', + 'Spaces/DesignSpace.js', + + 'Files/SingleFile.js', + 'Files/FileCloud.js', + 'Files/MarketFiles.js', + 'Files/DailyFiles.js', + 'Files/FileCursor.js', + 'Files/FileSequence.js', + 'Files/FileStorage.js', + + 'FloatingSpace/FloatingObject.js', + 'FloatingSpace/FloatingLayer.js', + 'FloatingSpace/UiObjectConstructor.js', + 'FloatingSpace/UiObject.js', + 'FloatingSpace/UiObjectTitle.js', + 'FloatingSpace/CircularMenu.js', + 'FloatingSpace/CircularMenuItem.js', + 'FloatingSpace/CircularProgressBar.js', + 'FloatingSpace/BusyProgressBar.js', + 'FloatingSpace/CodeEditor.js', + 'FloatingSpace/ConfigEditor.js', + 'FloatingSpace/ConditionEditor.js', + 'FloatingSpace/FormulaEditor.js', + + 'Scales/RateScale.js', + 'Scales/TimeScale.js', + 'Scales/TimeFrameScale.js', + 'Scales/Commons.js', + 'Scales/AutoScaleButton.js', + + 'CockpitSpace/AssetBalances.js', + 'CockpitSpace/Speedometer.js', + 'CockpitSpace/FullScreen.js', + + 'SystemEventHandler.js', + + 'VideoRecorder.js', + 'Plotter.js', + 'LegacyPlotter.js', + 'PlotterPanel.js', + + 'ProductStorage.js', + + 'SplashScreen.js', + 'Canvas.js', + 'EventHandler.js', + 'Frame.js', + + 'Animation.js', + + 'Container.js', + + 'Utilities.js', + 'PostLoader.js' + ]) + + modulesArray = modulesArray.concat(plotters) + + let downloadedCounter = 0 + let versionParam = window.canvasApp.version + if (versionParam === undefined) { versionParam = '' } else { + versionParam = '?' + versionParam + } + + for (let i = 0; i < modulesArray.length; i++) { + let path = window.canvasApp.urlPrefix + modulesArray[i] + versionParam + + 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.') } + setTimeout(() => { + dashboard = newDashboard() + dashboard.start() + }, 500) + + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] loadModules -> onRequired -> err = ' + err.stack) } + } + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] loadModules -> err = ' + err.stack) } + } + } + + function defaultPlotters() { + return [ + 'Plotters/AAMasters/Plotters-Candles-Volumes/Candles.js', + 'Plotters/AAMasters/Plotters-Candles-Volumes/CandlePanel.js', + 'Plotters/AAMasters/Plotters-Candles-Volumes/Volumes.js', + 'Plotters/AAMasters/Plotters-Candles-Volumes/VolumePanel.js', + 'Plotters/AAMasters/Plotters-Stairs-Patterns/CandleStairs.js', + 'Plotters/AAMasters/Plotters-Stairs-Patterns/VolumeStairs.js', + 'Plotters/AAMasters/Plotters-Bollinger-Bands/BollingerBands.js', + 'Plotters/AAMasters/Plotters-Bollinger-Bands/BollingerBandsPanel.js', + 'Plotters/AAMasters/Plotters-Bollinger-Bands/PercentageBandwidth.js', + 'Plotters/AAMasters/Plotters-Trading-Simulation/TradingSimulation.js', + 'Plotters/AAMasters/Plotters-Trading-Simulation/TradingSimulationPanel.js', + 'Plotters/AAMasters/Plotters-Trading-Simulation/Conditions.js', + 'Plotters/AAMasters/Plotters-Trading-Simulation/Strategies.js', + 'Plotters/AAMasters/Plotters-Trading-Simulation/Trades.js', + 'Plotters/AAMasters/Plotters-Trading-Simulation/SimulationExecution.js', + 'Plotters/AAMasters/Plotters-Trading-Simulation/SimulationExecutionPanel.js' + ] + } + + function getPlotters(ecosystem) { + if (INFO_LOG === true) { console.log('[INFO] server -> onBrowserRequest -> onFileRead -> addPlotters -> Entering function.') } + + let plotters = [] + let dataMines = ecosystem.dataMines + let hosts = ecosystem.hosts + + plotters = plotters.concat(addScript(dataMines)) + plotters = plotters.concat(addScript(hosts)) + + return plotters + } + + function addScript(pDataMinesOrHosts) { + if (INFO_LOG === true) { console.log('[INFO] server -> onBrowserRequest -> onFileRead -> addPlotters -> addScript -> Entering function.') } + const htmlLinePlotter = 'Plotters/@dataMine@/@repo@/@module@.js' + const htmlLinePlotterPanel = 'PlotterPanels/@dataMine@/@repo@/@module@.js' + let plotters = [] + + for (let i = 0; i < pDataMinesOrHosts.length; i++) { + let dataMine = pDataMinesOrHosts[i] + for (let j = 0; j < dataMine.plotters.length; j++) { + let plotter = dataMine.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('@dataMine@', dataMine.codeName) + stringToInsert = stringToInsert.replace('@repo@', plotter.repo) + stringToInsert = stringToInsert.replace('@module@', module.moduleName) + + plotters.push(stringToInsert) + + 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('@dataMine@', dataMine.codeName) + stringToInsert = stringToInsert.replace('@repo@', plotter.repo) + stringToInsert = stringToInsert.replace('@module@', panel.moduleName) + + plotters.push(stringToInsert) + } + } + } + } + } + } + return plotters + } +} + diff --git a/WebServer/Scripts/AppPreLoader.js b/WebServer/Scripts/AppPreLoader.js new file mode 100644 index 000000000..17da86bdc --- /dev/null +++ b/WebServer/Scripts/AppPreLoader.js @@ -0,0 +1,174 @@ + +/* 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 loadSuperalgos() { + + 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] loadSuperalgos -> Entering function."); } + + /* The first thing to do here is to add the canvas where all the action is going to happen. */ + + + let textArea = document.createElement('textarea'); + textArea.id = "textArea"; + textArea.spellcheck = false; + textArea.style = 'resize: none;' + + ' border: none;' + + ' outline: none;' + + 'box-shadow: none;' + + 'overflow:hidden;' + + 'font-family: ' + 'Saira' + ';' + + 'font-size: 12px;' + + 'background-color: rgb(255, 255, 255);' + + 'color:rgb(255, 255, 255);' + + 'width: ' + 800 + 'px;' + + 'height: ' + 600 + 'px' + + let textAreaDiv = document.getElementById('textAreaDiv'); + textAreaDiv.appendChild(textArea); + textAreaDiv.style = 'position:fixed; top:' + -1500 + 'px; left:' + 500 + 'px; z-index:10; ' + + let input = document.createElement('input'); + input.id = "input"; + input.spellcheck = false; + input.style = "border: none; outline: none; box-shadow: none; overflow:hidden; width: 0px; height: 0px;"; + + let inputDiv = document.getElementById('inputDiv'); + inputDiv.appendChild(input); + + + 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() { + + try { + 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(); + + } + } + catch (err) { + console.log(spacePad(MODULE_NAME, 50) + " : " + "[ERROR] loadDebugModule --> " + path + " --> err = " + err.stack); + } + } + + /* And Finally, we start loading all the scripts we will inmediatelly need. */ + + function loadModules() { + + try { + + 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(); + + } + } + catch (err) { + console.log(spacePad(MODULE_NAME, 50) + " : " + "[ERROR] loadModules --> " + path + " --> err = " + err.stack); + } + } +} + +function callServer(pContentToSend, pPath, callBackFunction) { + let xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function () { + if (this.readyState === 4 && this.status === 200) { + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, xhttp.responseText); + return; + } else if (this.readyState === 4 && this.status === 404) { + callBackFunction({ result: "Fail", message: xhttp.responseText.trim(), code: xhttp.responseText.trim() }); + return; + } + }; + + 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); } + callBackFunction({ result: "Fail", message: err.message }) + } + } +} diff --git a/WebServer/Scripts/require.js b/WebServer/Scripts/require.js new file mode 100644 index 000000000..f9476f532 --- /dev/null +++ b/WebServer/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/WebServer/externalScripts/CCapture.all.min.js b/WebServer/externalScripts/CCapture.all.min.js new file mode 100644 index 000000000..bda376d9f --- /dev/null +++ b/WebServer/externalScripts/CCapture.all.min.js @@ -0,0 +1,2 @@ +"use strict";function download(t,e,i){function n(t){var e=t.split(/[:;,]/),i=e[1],n="base64"==e[2]?atob:decodeURIComponent,r=n(e.pop()),o=r.length,a=0,s=new Uint8Array(o);for(a;a>8,this.data[this.pos++]=t},t.prototype.writeDoubleBE=function(t){for(var e=new Uint8Array(new Float64Array([t]).buffer),i=e.length-1;i>=0;i--)this.writeByte(e[i])},t.prototype.writeFloatBE=function(t){for(var e=new Uint8Array(new Float32Array([t]).buffer),i=e.length-1;i>=0;i--)this.writeByte(e[i])},t.prototype.writeString=function(t){for(var e=0;e>8),this.writeU8(t);break;case 3:this.writeU8(32|t>>16),this.writeU8(t>>8),this.writeU8(t);break;case 4:this.writeU8(16|t>>24),this.writeU8(t>>16),this.writeU8(t>>8),this.writeU8(t);break;case 5:this.writeU8(8|t/4294967296&7),this.writeU8(t>>24),this.writeU8(t>>16),this.writeU8(t>>8),this.writeU8(t);break;default:throw new RuntimeException("Bad EBML VINT size "+e)}},t.prototype.measureEBMLVarInt=function(t){if(t<127)return 1;if(t<16383)return 2;if(t<2097151)return 3;if(t<268435455)return 4;if(t<34359738367)return 5;throw new RuntimeException("EBML VINT size not supported "+t)},t.prototype.writeEBMLVarInt=function(t){this.writeEBMLVarIntWidth(t,this.measureEBMLVarInt(t))},t.prototype.writeUnsignedIntBE=function(t,e){switch(void 0===e&&(e=this.measureUnsignedInt(t)),e){case 5:this.writeU8(Math.floor(t/4294967296));case 4:this.writeU8(t>>24);case 3:this.writeU8(t>>16);case 2:this.writeU8(t>>8);case 1:this.writeU8(t);break;default:throw new RuntimeException("Bad UINT size "+e)}},t.prototype.measureUnsignedInt=function(t){return t<256?1:t<65536?2:t<1<<24?3:t<4294967296?4:5},t.prototype.getAsDataArray=function(){if(this.posthis.length)throw"Seeking beyond the end of file is not allowed";this.pos=t},this.write=function(e){var i={offset:this.pos,data:e,length:r(e)},f=i.offset>=this.length;this.pos+=i.length,this.length=Math.max(this.length,this.pos),a=a.then(function(){if(h)return new Promise(function(e,r){n(i.data).then(function(n){var r=0,o=Buffer.from(n.buffer),a=function(n,o,s){r+=o,r>=s.length?e():t.write(h,s,r,s.length-r,i.offset+r,a)};t.write(h,o,0,o.length,i.offset,a)})});if(s)return new Promise(function(t,e){s.onwriteend=t,s.seek(i.offset),s.write(new Blob([i.data]))});if(!f)for(var e=0;e=r.offset+r.length)){if(i.offsetr.offset+r.length)throw new Error("Overwrite crosses blob boundaries");return i.offset==r.offset&&i.length==r.length?void(r.data=i.data):n(r.data).then(function(t){return r.data=t,n(i.data)}).then(function(t){i.data=t,r.data.set(i.data,i.offset-r.offset)})}}o.push(i)})},this.complete=function(t){return a=h||s?a.then(function(){return null}):a.then(function(){for(var e=[],i=0;i0&&e.trackNumber<127))throw"TrackNumber must be > 0 and < 127";return i.writeEBMLVarInt(e.trackNumber),i.writeU16BE(e.timecode),i.writeByte(128),{id:163,data:[i.getAsDataArray(),e.frame]}}function l(t){return{id:524531317,data:[{id:231,data:Math.round(t.timecode)}]}}function c(t,e,i){_.push({id:187,data:[{id:179,data:e},{id:183,data:[{id:247,data:t},{id:241,data:a(i)}]}]})}function p(){var e={id:475249515,data:_},i=new t(16+32*_.length);h(i,S.pos,e),S.write(i.getAsDataArray()),D.Cues.positionEBML.data=a(e.offset)}function m(){if(0!=T.length){for(var e=0,i=0;i=E&&m()}function y(){var e=new t(x.size),i=S.pos;h(e,x.dataOffset,x.data),S.seek(x.dataOffset),S.write(e.getAsDataArray()),S.seek(i)}function v(){var e=new t(8),i=S.pos;e.writeDoubleBE(U),S.seek(M.dataOffset),S.write(e.getAsDataArray()),S.seek(i)}var b,k,B,x,E=5e3,A=1,L=!1,T=[],U=0,F=0,I={quality:.95,fileWriter:null,fd:null,frameDuration:null,frameRate:null},D={Cues:{id:new Uint8Array([28,83,187,107]),positionEBML:null},SegmentInfo:{id:new Uint8Array([21,73,169,102]),positionEBML:null},Tracks:{id:new Uint8Array([22,84,174,107]),positionEBML:null}},M={id:17545,data:new s(0)},_=[],S=new e(n.fileWriter||n.fd);this.addFrame=function(t){if(L){if(t.width!=b||t.height!=k)throw"Frame size differs from previous frames"}else b=t.width,k=t.height,u(),L=!0;var e=r(t,{quality:n.quality});if(!e)throw"Couldn't decode WebP frame, does the browser support WebP?";g({frame:o(e),duration:n.frameDuration})},this.complete=function(){return m(),p(),y(),v(),S.complete("video/webm")},this.getWrittenSize=function(){return S.length},n=i(I,n||{}),w()}};"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=t(require("./ArrayBufferDataStream"),require("./BlobBuffer")):window.WebMWriter=t(ArrayBufferDataStream,BlobBuffer)}(),function(){function t(t){var e,i=new Uint8Array(t);for(e=0;e>18&63]+o[t>>12&63]+o[t>>6&63]+o[63&t]}var i,n,r,a=t.length%3,s="";for(i=0,r=t.length-a;in&&(e.push({blocks:o,length:i}),o=[],i=0),o.push(t),i+=t.headerLength+t.inputLength}),e.push({blocks:o,length:i}),e.forEach(function(e){var i=new Uint8Array(e.length),n=0;e.blocks.forEach(function(t){i.set(t.header,n),n+=t.headerLength,i.set(t.input,n),n+=t.inputLength}),t.push(i)}),t.push(new Uint8Array(2*r)),new Blob(t,{type:"octet/stream"})},t.prototype.clear=function(){this.written=0,this.out=n.clean(e)},window.Tar=t}(),function(t){function e(t,i){if({}.hasOwnProperty.call(e.cache,t))return e.cache[t];var n=e.resolve(t);if(!n)throw new Error("Failed to resolve module "+t);var r={id:t,require:e,filename:t,exports:{},loaded:!1,parent:i,children:[]};i&&i.children.push(r);var o=t.slice(0,t.lastIndexOf("/")+1);return e.cache[t]=r.exports,n.call(r.exports,r,r.exports,o,t),r.loaded=!0,e.cache[t]=r.exports}e.modules={},e.cache={},e.resolve=function(t){return{}.hasOwnProperty.call(e.modules,t)?e.modules[t]:void 0},e.define=function(t,i){e.modules[t]=i};var i=function(e){return e="/",{title:"browser",version:"v0.10.26",browser:!0,env:{},argv:[],nextTick:t.setImmediate||function(t){setTimeout(t,0)},cwd:function(){return e},chdir:function(t){e=t}}}();e.define("/gif.coffee",function(t,i,n,r){function o(t,e){return{}.hasOwnProperty.call(t,e)}function a(t,e){for(var i=0,n=e.length;ithis.frames.length;0<=this.frames.length?++e:--e)t.push(e);return t}.apply(this,arguments),n=0,r=i.length;ne;0<=e?++i:--i)t.push(i);return t}.apply(this,arguments),n=0,r=i.length;nt;this.freeWorkers.length<=t?++i:--i)e.push(i);return e}.apply(this,arguments).forEach(function(t){return function(e){var i;return console.log("spawning worker "+e),i=new Worker(t.options.workerScript),i.onmessage=function(t){return function(e){return t.activeWorkers.splice(t.activeWorkers.indexOf(i),1),t.freeWorkers.push(i),t.frameFinished(e.data)}}(t),t.freeWorkers.push(i)}}(this)),t},e.prototype.frameFinished=function(t){return console.log("frame "+t.index+" finished - "+this.activeWorkers.length+" active"),this.finishedFrames++,this.emit("progress",this.finishedFrames/this.frames.length),this.imageParts[t.index]=t,a(null,this.imageParts)?this.renderNextFrame():this.finishRendering()},e.prototype.finishRendering=function(){var t,e,i,n,r,o,a;r=0;for(var s=0,h=this.imageParts.length;s=this.frames.length?void 0:(t=this.frames[this.nextFrame++],i=this.freeWorkers.shift(),e=this.getTask(t),console.log("starting frame "+(e.index+1)+" of "+this.frames.length),this.activeWorkers.push(i),i.postMessage(e))},e.prototype.getContextData=function(t){return t.getImageData(0,0,this.options.width,this.options.height).data},e.prototype.getImageData=function(t){var e;return null!=this._canvas||(this._canvas=document.createElement("canvas"),this._canvas.width=this.options.width,this._canvas.height=this.options.height),e=this._canvas.getContext("2d"),e.setFill=this.options.background,e.fillRect(0,0,this.options.width,this.options.height),e.drawImage(t,0,0),this.getContextData(e)},e.prototype.getTask=function(t){var e,i;if(e=this.frames.indexOf(t),i={index:e,last:e===this.frames.length-1,delay:t.delay,transparent:t.transparent,width:this.options.width,height:this.options.height,quality:this.options.quality,repeat:this.options.repeat,canTransfer:"chrome"===h.name},null!=t.data)i.data=t.data;else if(null!=t.context)i.data=this.getContextData(t.context);else{if(null==t.image)throw new Error("Invalid frame");i.data=this.getImageData(t.image)}return i},e}(u),t.exports=l}),e.define("/browser.coffee",function(t,e,i,n){var r,o,a,s,h;s=navigator.userAgent.toLowerCase(),a=navigator.platform.toLowerCase(),h=s.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0],o="ie"===h[1]&&document.documentMode,r={name:"version"===h[1]?h[3]:h[1],version:o||parseFloat("opera"===h[1]&&h[4]?h[4]:h[2]),platform:{name:s.match(/ip(?:ad|od|hone)/)?"ios":(s.match(/(?:webos|android)/)||a.match(/mac|win|linux/)||["other"])[0]}},r[r.name]=!0,r[r.name+parseInt(r.version,10)]=!0,r.platform[r.platform.name]=!0,t.exports=r}),e.define("events",function(t,e,n,r){i.EventEmitter||(i.EventEmitter=function(){});var o=e.EventEmitter=i.EventEmitter,a="function"==typeof Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},s=10;o.prototype.setMaxListeners=function(t){this._events||(this._events={}),this._events.maxListeners=t},o.prototype.emit=function(t){if("error"===t&&(!this._events||!this._events.error||a(this._events.error)&&!this._events.error.length))throw arguments[1]instanceof Error?arguments[1]:new Error("Uncaught, unspecified 'error' event.");if(!this._events)return!1;var e=this._events[t];if(!e)return!1;if("function"!=typeof e){if(a(e)){for(var i=Array.prototype.slice.call(arguments,1),n=e.slice(),r=0,o=n.length;r0&&this._events[t].length>i&&(this._events[t].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[t].length),console.trace())}this._events[t].push(e)}else this._events[t]=[this._events[t],e];else this._events[t]=e;return this},o.prototype.on=o.prototype.addListener,o.prototype.once=function(t,e){var i=this;return i.on(t,function n(){i.removeListener(t,n),e.apply(this,arguments)}),this},o.prototype.removeListener=function(t,e){if("function"!=typeof e)throw new Error("removeListener only takes instances of Function");if(!this._events||!this._events[t])return this;var i=this._events[t];if(a(i)){var n=i.indexOf(e);if(n<0)return this;i.splice(n,1),0==i.length&&delete this._events[t]}else this._events[t]===e&&delete this._events[t];return this},o.prototype.removeAllListeners=function(t){return t&&this._events&&this._events[t]&&(this._events[t]=null),this},o.prototype.listeners=function(t){return this._events||(this._events={}),this._events[t]||(this._events[t]=[]),a(this._events[t])||(this._events[t]=[this._events[t]]),this._events[t]}}),t.GIF=e("/gif.coffee")}.call(this,this),function(){function t(t){return t&&t.Object===Object?t:null}function e(t){return String("0000000"+t).slice(-7)}function i(){function t(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()}function n(t){var e={};this.settings=t,this.on=function(t,i){e[t]=i},this.emit=function(t){var i=e[t];i&&i.apply(null,Array.prototype.slice.call(arguments,1))},this.filename=t.name||i(),this.extension="",this.mimeType=""}function r(t){n.call(this,t),this.extension=".tar",this.mimeType="application/x-tar",this.fileExtension="",this.baseFilename=this.filename,this.tape=null,this.count=0,this.part=1,this.frames=0}function o(t){r.call(this,t),this.type="image/png",this.fileExtension=".png"}function a(t){r.call(this,t),this.type="image/jpeg",this.fileExtension=".jpg",this.quality=t.quality/100||.8}function s(t){var e=document.createElement("canvas");"image/webp"!==e.toDataURL("image/webp").substr(5,10)&&console.log("WebP not supported - try another export format"),n.call(this,t),this.quality=t.quality/100||.8,this.extension=".webm",this.mimeType="video/webm",this.baseFilename=this.filename,this.framerate=t.framerate,this.frames=0,this.part=1,this.videoWriter=new WebMWriter({quality:this.quality,fileWriter:null,fd:null,frameRate:this.framerate})}function h(t){n.call(this,t),t.quality=t.quality/100||.8,this.encoder=new FFMpegServer.Video(t),this.encoder.on("process",function(){this.emit("process")}.bind(this)),this.encoder.on("finished",function(t,e){var i=this.callback;i&&(this.callback=void 0,i(t,e))}.bind(this)),this.encoder.on("progress",function(t){this.settings.onProgress&&this.settings.onProgress(t)}.bind(this)),this.encoder.on("error",function(t){alert(JSON.stringify(t,null,2))}.bind(this))}function f(t){n.call(this,t),this.framerate=this.settings.framerate,this.type="video/webm",this.extension=".webm",this.stream=null,this.mediaRecorder=null,this.chunks=[]}function u(t){n.call(this,t),t.quality=31-(30*t.quality/100||10),t.workers=t.workers||4,this.extension=".gif",this.mimeType="image/gif",this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),this.sizeSet=!1,this.encoder=new GIF({workers:t.workers,quality:t.quality,workerScript:t.workersPath+"gif.worker.js"}),this.encoder.on("progress",function(t){this.settings.onProgress&&this.settings.onProgress(t)}.bind(this)),this.encoder.on("finished",function(t){var e=this.callback;e&&(this.callback=void 0,e(t))}.bind(this))}function d(t){function e(){function t(){return this._hooked||(this._hooked=!0,this._hookedTime=this.currentTime||0,this.pause(),it.push(this)),this._hookedTime+M.startTime}b("Capturer start"),U=window.Date.now(),T=U+M.startTime,I=window.performance.now(),F=I+M.startTime,window.Date.prototype.getTime=function(){return T},window.Date.now=function(){return T},window.setTimeout=function(t,e){var i={callback:t,time:e,triggerTime:T+e};return _.push(i),b("Timeout set to "+i.time),i},window.clearTimeout=function(t){for(var e=0;e<_.length;e++)_[e]!=t||(_.splice(e,1),b("Timeout cleared"))},window.setInterval=function(t,e){var i={callback:t,time:e,triggerTime:T+e};return S.push(i),b("Interval set to "+i.time),i},window.clearInterval=function(t){return b("clear Interval"),null},window.requestAnimationFrame=function(t){W.push(t)},window.performance.now=function(){return F};try{Object.defineProperty(HTMLVideoElement.prototype,"currentTime",{get:t}),Object.defineProperty(HTMLAudioElement.prototype,"currentTime",{get:t})}catch(t){b(t)}}function i(){e(),D.start(),R=!0}function n(){R=!1,D.stop(),l()}function r(t,e){Z(t,0,e)}function d(){r(y)}function l(){b("Capturer stop"),window.setTimeout=Z,window.setInterval=J,window.clearInterval=Y,window.clearTimeout=$,window.requestAnimationFrame=Q,window.Date.prototype.getTime=et,window.Date.now=X,window.performance.now=tt}function c(){var t=C/M.framerate;(M.frameLimit&&C>=M.frameLimit||M.timeLimit&&t>=M.timeLimit)&&(n(),v());var e=new Date(null);e.setSeconds(t),M.motionBlurFrames>2?j.textContent="CCapture "+M.format+" | "+C+" frames ("+O+" inter) | "+e.toISOString().substr(11,8):j.textContent="CCapture "+M.format+" | "+C+" frames | "+e.toISOString().substr(11,8)}function p(t){N.width===t.width&&N.height===t.height||(N.width=t.width,N.height=t.height,z=new Uint16Array(N.height*N.width*4),V.fillStyle="#0",V.fillRect(0,0,N.width,N.height))}function m(t){V.drawImage(t,0,0),q=V.getImageData(0,0,N.width,N.height);for(var e=0;e2?(p(t),m(t),O>=.5*M.motionBlurFrames?w():d()):(D.add(t),C++,b("Full Frame! "+C)))}function y(){var t=1e3/M.framerate,e=(C+O/M.motionBlurFrames)*t;T=U+e,F=I+e,it.forEach(function(t){t._hookedTime=e/1e3}),c(),b("Frame: "+C+" "+O);for(var i=0;i<_.length;i++)T>=_[i].triggerTime&&(r(_[i].callback),_.splice(i,1));for(var i=0;i=S[i].triggerTime&&(r(S[i].callback),S[i].triggerTime+=S[i].time);W.forEach(function(t){r(t,T-k)}),W=[]}function v(t){t||(t=function(t){return download(t,D.filename+D.extension,D.mimeType),!1}),D.save(t)}function b(t){A&&console.log(t)}function B(t,e){P[t]=e}function x(t){var e=P[t];e&&e.apply(null,Array.prototype.slice.call(arguments,1))}function E(t){x("progress",t)}var A,L,T,U,F,I,d,D,M=t||{},_=(new Date,[]),S=[],C=0,O=0,W=[],R=!1,P={};M.framerate=M.framerate||60,M.motionBlurFrames=2*(M.motionBlurFrames||1),A=M.verbose||!1,L=M.display||!1,M.step=1e3/M.framerate,M.timeLimit=M.timeLimit||0,M.frameLimit=M.frameLimit||0,M.startTime=M.startTime||0;var j=document.createElement("div");j.style.position="absolute",j.style.left=j.style.top=0,j.style.backgroundColor="black",j.style.fontFamily="monospace",j.style.fontSize="11px",j.style.padding="5px",j.style.color="red",j.style.zIndex=1e5,M.display&&document.body.appendChild(j);var z,q,N=document.createElement("canvas"),V=N.getContext("2d");b("Step is set to "+M.step+"ms");var G={gif:u,webm:s,ffmpegserver:h,png:o,jpg:a,"webm-mediarecorder":f},H=G[M.format];if(!H)throw"Error: Incorrect or missing format: Valid formats are "+Object.keys(G).join(", ");if(D=new H(M),D.step=d,D.on("process",y),D.on("progress",E),"performance"in window==0&&(window.performance={}),Date.now=Date.now||function(){return(new Date).getTime()},"now"in window.performance==0){var K=Date.now();performance.timing&&performance.timing.navigationStart&&(K=performance.timing.navigationStart),window.performance.now=function(){return Date.now()-K}}var Z=window.setTimeout,J=window.setInterval,Y=window.clearInterval,$=window.clearTimeout,Q=window.requestAnimationFrame,X=window.Date.now,tt=window.performance.now,et=window.Date.prototype.getTime,it=[];return{start:i,capture:g,stop:n,save:v,on:B}}var l={function:!0,object:!0},c=(parseFloat,parseInt,l[typeof exports]&&exports&&!exports.nodeType?exports:void 0),p=l[typeof module]&&module&&!module.nodeType?module:void 0,m=p&&p.exports===c?c:void 0,w=t(c&&p&&"object"==typeof global&&global),g=t(l[typeof self]&&self),y=t(l[typeof window]&&window),v=t(l[typeof this]&&this),b=w||y!==(v&&v.window)&&y||g||v||Function("return this")();"gc"in window||(window.gc=function(){}),HTMLCanvasElement.prototype.toBlob||Object.defineProperty(HTMLCanvasElement.prototype,"toBlob",{value:function(t,e,i){for(var n=atob(this.toDataURL(e,i).split(",")[1]),r=n.length,o=new Uint8Array(r),a=0;a0&&this.frames/this.settings.framerate>=this.settings.autoSaveTime?this.save(function(t){this.filename=this.baseFilename+"-part-"+e(this.part),download(t,this.filename+this.extension,this.mimeType);var i=this.count;this.dispose(),this.count=i+1,this.part++,this.filename=this.baseFilename+"-part-"+e(this.part),this.frames=0,this.step()}.bind(this)):(this.count++,this.frames++,this.step())}.bind(this),i.readAsArrayBuffer(t)},r.prototype.save=function(t){t(this.tape.save())},r.prototype.dispose=function(){this.tape=new Tar,this.count=0},o.prototype=Object.create(r.prototype),o.prototype.add=function(t){t.toBlob(function(t){r.prototype.add.call(this,t)}.bind(this),this.type)},a.prototype=Object.create(r.prototype),a.prototype.add=function(t){t.toBlob(function(t){r.prototype.add.call(this,t)}.bind(this),this.type,this.quality)},s.prototype=Object.create(n.prototype),s.prototype.start=function(t){this.dispose()},s.prototype.add=function(t){this.videoWriter.addFrame(t),this.settings.autoSaveTime>0&&this.frames/this.settings.framerate>=this.settings.autoSaveTime?this.save(function(t){this.filename=this.baseFilename+"-part-"+e(this.part),download(t,this.filename+this.extension,this.mimeType),this.dispose(),this.part++,this.filename=this.baseFilename+"-part-"+e(this.part),this.step()}.bind(this)):(this.frames++,this.step())},s.prototype.save=function(t){this.videoWriter.complete().then(t)},s.prototype.dispose=function(t){this.frames=0,this.videoWriter=new WebMWriter({quality:this.quality,fileWriter:null,fd:null,frameRate:this.framerate})},h.prototype=Object.create(n.prototype),h.prototype.start=function(){this.encoder.start(this.settings)},h.prototype.add=function(t){this.encoder.add(t)},h.prototype.save=function(t){this.callback=t,this.encoder.end()},h.prototype.safeToProceed=function(){return this.encoder.safeToProceed()},f.prototype=Object.create(n.prototype),f.prototype.add=function(t){this.stream||(this.stream=t.captureStream(this.framerate),this.mediaRecorder=new MediaRecorder(this.stream),this.mediaRecorder.start(),this.mediaRecorder.ondataavailable=function(t){this.chunks.push(t.data)}.bind(this)),this.step()},f.prototype.save=function(t){this.mediaRecorder.onstop=function(e){var i=new Blob(this.chunks,{type:"video/webm"});this.chunks=[],t(i)}.bind(this),this.mediaRecorder.stop()},u.prototype=Object.create(n.prototype),u.prototype.add=function(t){this.sizeSet||(this.encoder.setOption("width",t.width),this.encoder.setOption("height",t.height),this.sizeSet=!0),this.canvas.width=t.width,this.canvas.height=t.height,this.ctx.drawImage(t,0,0),this.encoder.addFrame(this.ctx,{copy:!0,delay:this.settings.step}),this.step()},u.prototype.save=function(t){this.callback=t,this.encoder.render()},(y||g||{}).CCapture=d,"function"==typeof define&&"object"==typeof define.amd&&define.amd?define(function(){return d}):c&&p?(m&&((p.exports=d).CCapture=d),c.CCapture=d):b.CCapture=d}(); \ No newline at end of file diff --git a/WebServer/externalScripts/gif.worker.js b/WebServer/externalScripts/gif.worker.js new file mode 100644 index 000000000..269624e61 --- /dev/null +++ b/WebServer/externalScripts/gif.worker.js @@ -0,0 +1,3 @@ +// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=ByteArray.pageSize)this.newPage();this.pages[this.page][this.cursor++]=val};ByteArray.prototype.writeUTFBytes=function(string){for(var l=string.length,i=0;i=0)this.dispose=disposalCode};GIFEncoder.prototype.setRepeat=function(repeat){this.repeat=repeat};GIFEncoder.prototype.setTransparent=function(color){this.transparent=color};GIFEncoder.prototype.addFrame=function(imageData){this.image=imageData;this.colorTab=this.globalPalette&&this.globalPalette.slice?this.globalPalette:null;this.getImagePixels();this.analyzePixels();if(this.globalPalette===true)this.globalPalette=this.colorTab;if(this.firstFrame){this.writeLSD();this.writePalette();if(this.repeat>=0){this.writeNetscapeExt()}}this.writeGraphicCtrlExt();this.writeImageDesc();if(!this.firstFrame&&!this.globalPalette)this.writePalette();this.writePixels();this.firstFrame=false};GIFEncoder.prototype.finish=function(){this.out.writeByte(59)};GIFEncoder.prototype.setQuality=function(quality){if(quality<1)quality=1;this.sample=quality};GIFEncoder.prototype.setDither=function(dither){if(dither===true)dither="FloydSteinberg";this.dither=dither};GIFEncoder.prototype.setGlobalPalette=function(palette){this.globalPalette=palette};GIFEncoder.prototype.getGlobalPalette=function(){return this.globalPalette&&this.globalPalette.slice&&this.globalPalette.slice(0)||this.globalPalette};GIFEncoder.prototype.writeHeader=function(){this.out.writeUTFBytes("GIF89a")};GIFEncoder.prototype.analyzePixels=function(){if(!this.colorTab){this.neuQuant=new NeuQuant(this.pixels,this.sample);this.neuQuant.buildColormap();this.colorTab=this.neuQuant.getColormap()}if(this.dither){this.ditherPixels(this.dither.replace("-serpentine",""),this.dither.match(/-serpentine/)!==null)}else{this.indexPixels()}this.pixels=null;this.colorDepth=8;this.palSize=7;if(this.transparent!==null){this.transIndex=this.findClosest(this.transparent,true)}};GIFEncoder.prototype.indexPixels=function(imgq){var nPix=this.pixels.length/3;this.indexedPixels=new Uint8Array(nPix);var k=0;for(var j=0;j=0&&x1+x=0&&y1+y>16,(c&65280)>>8,c&255,used)};GIFEncoder.prototype.findClosestRGB=function(r,g,b,used){if(this.colorTab===null)return-1;if(this.neuQuant&&!used){return this.neuQuant.lookupRGB(r,g,b)}var c=b|g<<8|r<<16;var minpos=0;var dmin=256*256*256;var len=this.colorTab.length;for(var i=0,index=0;i=0){disp=dispose&7}disp<<=2;this.out.writeByte(0|disp|0|transp);this.writeShort(this.delay);this.out.writeByte(this.transIndex);this.out.writeByte(0)};GIFEncoder.prototype.writeImageDesc=function(){this.out.writeByte(44);this.writeShort(0);this.writeShort(0);this.writeShort(this.width);this.writeShort(this.height);if(this.firstFrame||this.globalPalette){this.out.writeByte(0)}else{this.out.writeByte(128|0|0|0|this.palSize)}};GIFEncoder.prototype.writeLSD=function(){this.writeShort(this.width);this.writeShort(this.height);this.out.writeByte(128|112|0|this.palSize);this.out.writeByte(0);this.out.writeByte(0)};GIFEncoder.prototype.writeNetscapeExt=function(){this.out.writeByte(33);this.out.writeByte(255);this.out.writeByte(11);this.out.writeUTFBytes("NETSCAPE2.0");this.out.writeByte(3);this.out.writeByte(1);this.writeShort(this.repeat);this.out.writeByte(0)};GIFEncoder.prototype.writePalette=function(){this.out.writeBytes(this.colorTab);var n=3*256-this.colorTab.length;for(var i=0;i>8&255)};GIFEncoder.prototype.writePixels=function(){var enc=new LZWEncoder(this.width,this.height,this.indexedPixels,this.colorDepth);enc.encode(this.out)};GIFEncoder.prototype.stream=function(){return this.out};module.exports=GIFEncoder},{"./LZWEncoder.js":2,"./TypedNeuQuant.js":3}],2:[function(require,module,exports){var EOF=-1;var BITS=12;var HSIZE=5003;var masks=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535];function LZWEncoder(width,height,pixels,colorDepth){var initCodeSize=Math.max(2,colorDepth);var accum=new Uint8Array(256);var htab=new Int32Array(HSIZE);var codetab=new Int32Array(HSIZE);var cur_accum,cur_bits=0;var a_count;var free_ent=0;var maxcode;var clear_flg=false;var g_init_bits,ClearCode,EOFCode;function char_out(c,outs){accum[a_count++]=c;if(a_count>=254)flush_char(outs)}function cl_block(outs){cl_hash(HSIZE);free_ent=ClearCode+2;clear_flg=true;output(ClearCode,outs)}function cl_hash(hsize){for(var i=0;i=0){disp=hsize_reg-i;if(i===0)disp=1;do{if((i-=disp)<0)i+=hsize_reg;if(htab[i]===fcode){ent=codetab[i];continue outer_loop}}while(htab[i]>=0)}output(ent,outs);ent=c;if(free_ent<1<0){outs.writeByte(a_count);outs.writeBytes(accum,0,a_count);a_count=0}}function MAXCODE(n_bits){return(1<0)cur_accum|=code<=8){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}if(free_ent>maxcode||clear_flg){if(clear_flg){maxcode=MAXCODE(n_bits=g_init_bits);clear_flg=false}else{++n_bits;if(n_bits==BITS)maxcode=1<0){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}flush_char(outs)}}this.encode=encode}module.exports=LZWEncoder},{}],3:[function(require,module,exports){var ncycles=100;var netsize=256;var maxnetpos=netsize-1;var netbiasshift=4;var intbiasshift=16;var intbias=1<>betashift;var betagamma=intbias<>3;var radiusbiasshift=6;var radiusbias=1<>3);var i,v;for(i=0;i>=netbiasshift;network[i][1]>>=netbiasshift;network[i][2]>>=netbiasshift;network[i][3]=i}}function altersingle(alpha,i,b,g,r){network[i][0]-=alpha*(network[i][0]-b)/initalpha;network[i][1]-=alpha*(network[i][1]-g)/initalpha;network[i][2]-=alpha*(network[i][2]-r)/initalpha}function alterneigh(radius,i,b,g,r){var lo=Math.abs(i-radius);var hi=Math.min(i+radius,netsize);var j=i+1;var k=i-1;var m=1;var p,a;while(jlo){a=radpower[m++];if(jlo){p=network[k--];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}}}function contest(b,g,r){var bestd=~(1<<31);var bestbiasd=bestd;var bestpos=-1;var bestbiaspos=bestpos;var i,n,dist,biasdist,betafreq;for(i=0;i>intbiasshift-netbiasshift);if(biasdist>betashift;freq[i]-=betafreq;bias[i]+=betafreq<>1;for(j=previouscol+1;j>1;for(j=previouscol+1;j<256;j++)netindex[j]=maxnetpos}function inxsearch(b,g,r){var a,p,dist;var bestd=1e3;var best=-1;var i=netindex[g];var j=i-1;while(i=0){if(i=bestd)i=netsize;else{i++;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist=0){p=network[j];dist=g-p[1];if(dist>=bestd)j=-1;else{j--;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist>radiusbiasshift;if(rad<=1)rad=0;for(i=0;i=lengthcount)pix-=lengthcount;i++;if(delta===0)delta=1;if(i%delta===0){alpha-=alpha/alphadec;radius-=radius/radiusdec;rad=radius>>radiusbiasshift;if(rad<=1)rad=0;for(j=0;j 0 && this._events[type].length > m) { this._events[type].warned = true; console.error("(node) warning: possible EventEmitter memory " + "leak detected. %d listeners added. " + "Use emitter.setMaxListeners() to increase limit.", this._events[type].length); if (typeof console.trace === "function") { console.trace() } } } return this }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function (type, listener) { if (!isFunction(listener)) throw TypeError("listener must be a function"); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments) } } g.listener = listener; this.on(type, g); return this }; EventEmitter.prototype.removeListener = function (type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError("listener must be a function"); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || isFunction(list.listener) && list.listener === listener) { delete this._events[type]; if (this._events.removeListener) this.emit("removeListener", type, listener) } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || list[i].listener && list[i].listener === listener) { position = i; break } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type] } else { list.splice(position, 1) } if (this._events.removeListener) this.emit("removeListener", type, listener) } return this }; EventEmitter.prototype.removeAllListeners = function (type) { var key, listeners; if (!this._events) return this; if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this } if (arguments.length === 0) { for (key in this._events) { if (key === "removeListener") continue; this.removeAllListeners(key) } this.removeAllListeners("removeListener"); this._events = {}; return this } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners) } else if (listeners) { while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]) } delete this._events[type]; return this }; EventEmitter.prototype.listeners = function (type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret }; EventEmitter.prototype.listenerCount = function (type) { if (this._events) { var evlistener = this._events[type]; if (isFunction(evlistener)) return 1; else if (evlistener) return evlistener.length } return 0 }; EventEmitter.listenerCount = function (emitter, type) { return emitter.listenerCount(type) }; function isFunction(arg) { return typeof arg === "function" } function isNumber(arg) { return typeof arg === "number" } function isObject(arg) { return typeof arg === "object" && arg !== null } function isUndefined(arg) { return arg === void 0 } }, {}], 2: [function (require, module, exports) { var UA, browser, mode, platform, ua; ua = navigator.userAgent.toLowerCase(); platform = navigator.platform.toLowerCase(); UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, "unknown", 0]; mode = UA[1] === "ie" && document.documentMode; browser = { name: UA[1] === "version" ? UA[3] : UA[1], version: mode || parseFloat(UA[1] === "opera" && UA[4] ? UA[4] : UA[2]), platform: { name: ua.match(/ip(?:ad|od|hone)/) ? "ios" : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ["other"])[0] } }; browser[browser.name] = true; browser[browser.name + parseInt(browser.version, 10)] = true; browser.platform[browser.platform.name] = true; module.exports = browser }, {}], 3: [function (require, module, exports) { var EventEmitter, GIF, browser, extend = function (child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key] } function ctor() { this.constructor = child } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child }, hasProp = {}.hasOwnProperty, indexOf = [].indexOf || function (item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i } return -1 }, slice = [].slice; EventEmitter = require("events").EventEmitter; browser = require("./browser.coffee"); GIF = function (superClass) { var defaults, frameDefaults; extend(GIF, superClass); defaults = { workerScript: "gif.worker.js", workers: 2, repeat: 0, background: "#fff", quality: 10, width: null, height: null, transparent: null, debug: false, dither: false }; frameDefaults = { delay: 500, copy: false }; function GIF(options) { var base, key, value; this.running = false; this.options = {}; this.frames = []; this.freeWorkers = []; this.activeWorkers = []; this.setOptions(options); for (key in defaults) { value = defaults[key]; if ((base = this.options)[key] == null) { base[key] = value } } } GIF.prototype.setOption = function (key, value) { this.options[key] = value; if (this._canvas != null && (key === "width" || key === "height")) { return this._canvas[key] = value } }; GIF.prototype.setOptions = function (options) { var key, results, value; results = []; for (key in options) { if (!hasProp.call(options, key)) continue; value = options[key]; results.push(this.setOption(key, value)) } return results }; GIF.prototype.addFrame = function (image, options) { var frame, key; if (options == null) { options = {} } frame = {}; frame.transparent = this.options.transparent; for (key in frameDefaults) { frame[key] = options[key] || frameDefaults[key] } if (this.options.width == null) { this.setOption("width", image.width) } if (this.options.height == null) { this.setOption("height", image.height) } if (typeof ImageData !== "undefined" && ImageData !== null && image instanceof ImageData) { frame.data = image.data } else if (typeof CanvasRenderingContext2D !== "undefined" && CanvasRenderingContext2D !== null && image instanceof CanvasRenderingContext2D || typeof WebGLRenderingContext !== "undefined" && WebGLRenderingContext !== null && image instanceof WebGLRenderingContext) { if (options.copy) { frame.data = this.getContextData(image) } else { frame.context = image } } else if (image.childNodes != null) { if (options.copy) { frame.data = this.getImageData(image) } else { frame.image = image } } else { throw new Error("Invalid image") } return this.frames.push(frame) }; GIF.prototype.render = function () { var i, j, numWorkers, ref; if (this.running) { throw new Error("Already running") } if (this.options.width == null || this.options.height == null) { throw new Error("Width and height must be set prior to rendering") } this.running = true; this.nextFrame = 0; this.finishedFrames = 0; this.imageParts = function () { var j, ref, results; results = []; for (i = j = 0, ref = this.frames.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { results.push(null) } return results }.call(this); numWorkers = this.spawnWorkers(); if (this.options.globalPalette === true) { this.renderNextFrame() } else { for (i = j = 0, ref = numWorkers; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { this.renderNextFrame() } } this.emit("start"); return this.emit("progress", 0) }; GIF.prototype.abort = function () { var worker; while (true) { worker = this.activeWorkers.shift(); if (worker == null) { break } this.log("killing active worker"); worker.terminate() } this.running = false; return this.emit("abort") }; GIF.prototype.spawnWorkers = function () { var j, numWorkers, ref, results; numWorkers = Math.min(this.options.workers, this.frames.length); (function () { results = []; for (var j = ref = this.freeWorkers.length; ref <= numWorkers ? j < numWorkers : j > numWorkers; ref <= numWorkers ? j++ : j--) { results.push(j) } return results }).apply(this).forEach(function (_this) { return function (i) { var worker; _this.log("spawning worker " + i); worker = new Worker(_this.options.workerScript); worker.onmessage = function (event) { _this.activeWorkers.splice(_this.activeWorkers.indexOf(worker), 1); _this.freeWorkers.push(worker); return _this.frameFinished(event.data) }; return _this.freeWorkers.push(worker) } }(this)); return numWorkers }; GIF.prototype.frameFinished = function (frame) { var i, j, ref; this.log("frame " + frame.index + " finished - " + this.activeWorkers.length + " active"); this.finishedFrames++; this.emit("progress", this.finishedFrames / this.frames.length); this.imageParts[frame.index] = frame; if (this.options.globalPalette === true) { this.options.globalPalette = frame.globalPalette; this.log("global palette analyzed"); if (this.frames.length > 2) { for (i = j = 1, ref = this.freeWorkers.length; 1 <= ref ? j < ref : j > ref; i = 1 <= ref ? ++j : --j) { this.renderNextFrame() } } } if (indexOf.call(this.imageParts, null) >= 0) { return this.renderNextFrame() } else { return this.finishRendering() } }; GIF.prototype.finishRendering = function () { var data, frame, i, image, j, k, l, len, len1, len2, len3, offset, page, ref, ref1, ref2; len = 0; ref = this.imageParts; for (j = 0, len1 = ref.length; j < len1; j++) { frame = ref[j]; len += (frame.data.length - 1) * frame.pageSize + frame.cursor } len += frame.pageSize - frame.cursor; this.log("rendering finished - filesize " + Math.round(len / 1e3) + "kb"); data = new Uint8Array(len); offset = 0; ref1 = this.imageParts; for (k = 0, len2 = ref1.length; k < len2; k++) { frame = ref1[k]; ref2 = frame.data; for (i = l = 0, len3 = ref2.length; l < len3; i = ++l) { page = ref2[i]; data.set(page, offset); if (i === frame.data.length - 1) { offset += frame.cursor } else { offset += frame.pageSize } } } image = new Blob([data], { type: "image/gif" }); return this.emit("finished", image, data) }; GIF.prototype.renderNextFrame = function () { var frame, task, worker; if (this.freeWorkers.length === 0) { throw new Error("No free workers") } if (this.nextFrame >= this.frames.length) { return } frame = this.frames[this.nextFrame++]; worker = this.freeWorkers.shift(); task = this.getTask(frame); this.log("starting frame " + (task.index + 1) + " of " + this.frames.length); this.activeWorkers.push(worker); return worker.postMessage(task) }; GIF.prototype.getContextData = function (ctx) { return ctx.getImageData(0, 0, this.options.width, this.options.height).data }; GIF.prototype.getImageData = function (image) { var ctx; if (this._canvas == null) { this._canvas = document.createElement("canvas"); this._canvas.width = this.options.width; this._canvas.height = this.options.height } ctx = this._canvas.getContext("2d"); ctx.setFill = this.options.background; ctx.fillRect(0, 0, this.options.width, this.options.height); ctx.drawImage(image, 0, 0); return this.getContextData(ctx) }; GIF.prototype.getTask = function (frame) { var index, task; index = this.frames.indexOf(frame); task = { index: index, last: index === this.frames.length - 1, delay: frame.delay, transparent: frame.transparent, width: this.options.width, height: this.options.height, quality: this.options.quality, dither: this.options.dither, globalPalette: this.options.globalPalette, repeat: this.options.repeat, canTransfer: browser.name === "chrome" }; if (frame.data != null) { task.data = frame.data } else if (frame.context != null) { task.data = this.getContextData(frame.context) } else if (frame.image != null) { task.data = this.getImageData(frame.image) } else { throw new Error("Invalid frame") } return task }; GIF.prototype.log = function () { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; if (!this.options.debug) { return } return console.log.apply(console, args) }; return GIF }(EventEmitter); module.exports = GIF }, { "./browser.coffee": 2, events: 1 }] }, {}, [3])(3) }); +//# sourceMappingURL=gif.js.map \ No newline at end of file diff --git a/WebServer/index.html b/WebServer/index.html new file mode 100644 index 000000000..e0254bd0c --- /dev/null +++ b/WebServer/index.html @@ -0,0 +1,65 @@ + + + + + + Superalgos + + + + + + + + + + + + + + + + + + + +
+
+
+
 
+ + + diff --git a/WebServer/node_modules/@superalgos/mqservice/index.js b/WebServer/node_modules/@superalgos/mqservice/index.js new file mode 100644 index 000000000..b0c7c2643 --- /dev/null +++ b/WebServer/node_modules/@superalgos/mqservice/index.js @@ -0,0 +1,8 @@ +const { MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER, ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, + ORDER_MARGIN_ENABLED, createRecord, getRecord, getExpandedRecord, createRecordFromObject +} = require("./orderLifeCicle/index") + +module.exports = { + MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER, ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, + ORDER_MARGIN_ENABLED, createRecord, getRecord, getExpandedRecord, createRecordFromObject +} diff --git a/WebServer/node_modules/@superalgos/mqservice/orderLifeCicle/index.js b/WebServer/node_modules/@superalgos/mqservice/orderLifeCicle/index.js new file mode 100644 index 000000000..95760a1cf --- /dev/null +++ b/WebServer/node_modules/@superalgos/mqservice/orderLifeCicle/index.js @@ -0,0 +1,144 @@ +const MESSAGE_ENTITY = { + SimulationExecutor: 'EX', + TradingCokpit: 'CO', + SimulationEngine: 'EN', + TradingAssistant: 'AS' +} +const MESSAGE_TYPE = { + HeartBeat: 'HBT', + OrderAuthorizationRequest: 'REQ', + OrderAuthorizationResponse: 'RES', + Order: 'ORD', + OrderUpdate: 'UPD' +} +const ORDER_CREATOR = { SimulationEngine: 'S', HumanTrader: 'H' } +const ORDER_TYPE = { Market: 'M', Limit: 'L', Stop: 'S', } +const ORDER_OWNER = { Node: 'N', Team: 'T', User: 'U', } +const ORDER_DIRECTION = { Sell: 'Sell', Buy: 'Buy' } +const ORDER_STATUS = { + Signaled: 'SIG', // The order has been created and is ready to be authorized + ManualAuthorized: 'MAU', // Authorized by the user manually + ManualNotAuthorized: 'MNA', // Rejected by the user manually + AutoAuthorized: 'AAU', // Authorize all as been selected by the user + AutoNotAuthorized: 'ANA', // Reject all as been selected by the user + Executing: 'EXE', // The order is efectively being executed on the exchange + Cancelled: 'CAN', // The order has been canceled + Filled: 'FIL', // The order bas been completed on the exchange + PartiallyFilled: 'PRT', // The order is partially filled and is still on the exchange + Discarded: 'DIS', // Order discarded since there is already an order in place + Placed: 'PLA', // Order has been just been placed on the exchange + Rejected: 'REJ', // Order has been rejected by the exchange due to some error + Undeliverable: 'UND' // The order was not able to be delivered to the exchange +} +const ORDER_EXIT_OUTCOME = { StopLoss: 'SL', TakeProfit: 'TP' } +const ORDER_MARGIN_ENABLED = { True: 1, False: 0 } + +function createRecord(messageId, from, to, messageType, messageDateTime, orderId, creator, orderDateTime, + owner, exchange, market, marginEnabled, type, rate, stop, takeProfit, direction, size, status, sizeFilled, exitOutcome) { + + return [ + messageId, + from, + to, + messageType, + messageDateTime, + [ + orderId, + creator, + orderDateTime, + owner, + exchange, + market, + marginEnabled, + type, + rate, + stop, + takeProfit, + direction, + size, + status, + sizeFilled, + exitOutcome + ] + ] +} + +function createRecordFromObject(object) { + + return createRecord(object.id, object.from, object.to, object.messageType, object.dateTime, object.order.id, + object.order.creator, object.order.dateTime, object.order.owner, object.order.exchange, object.order.market, + object.order.marginEnabled, object.order.type, object.order.rate, object.order.stop, object.order.takeProfit, + object.order.direction, object.order.size, object.order.status, object.order.sizeFilled, object.order.exitOutcome) +} + +function getRecord(fileContent) { + return { + id: fileContent[0], + from: fileContent[1], + to: fileContent[2], + messageType: fileContent[3], + dateTime: fileContent[4], + order: { + id: fileContent[5][0], + creator: fileContent[5][1], + dateTime: fileContent[5][2], + owner: fileContent[5][3], + exchange: fileContent[5][4], + market: fileContent[5][5], + marginEnabled: fileContent[5][6], + type: fileContent[5][7], + rate: fileContent[5][8], + stop: fileContent[5][9], + takeProfit: fileContent[5][10], + direction: fileContent[5][11], + size: fileContent[5][12], + status: fileContent[5][13], + sizeFilled: fileContent[5][14], + exitOutcome: fileContent[5][15] + } + } +} + +function getExpandedRecord(fileContent) { + return { + id: fileContent[0], + from: getKeyByValue(MESSAGE_ENTITY, fileContent[1]), + to: getKeyByValue(MESSAGE_ENTITY, fileContent[2]), + messageType: getKeyByValue(MESSAGE_TYPE, fileContent[3]), + dateTime: fileContent[4], + order: { + id: fileContent[5][0], + creator: getKeyByValue(ORDER_CREATOR, fileContent[5][1]), + dateTime: fileContent[5][2], + owner: getKeyByValue(ORDER_OWNER, fileContent[5][3]), + exchange: fileContent[5][4], + market: fileContent[5][5], + marginEnabled: getKeyByValue(ORDER_MARGIN_ENABLED, fileContent[5][6]), + type: getKeyByValue(ORDER_TYPE, fileContent[5][7]), + rate: fileContent[5][8], + stop: fileContent[5][9], + takeProfit: fileContent[5][10], + direction: getKeyByValue(ORDER_DIRECTION, fileContent[5][11]), + size: fileContent[5][12], + status: getKeyByValue(ORDER_STATUS, fileContent[5][13]), + sizeFilled: fileContent[5][14], + exitOutcome: getKeyByValue(ORDER_EXIT_OUTCOME, fileContent[5][15]) + } + } +} + +function getKeyByValue(object, value) { + let retrievedValue = Object.keys(object).find(key => object[key] === value) + if (retrievedValue !== undefined) { + retrievedValue = retrievedValue.replace(/([A-Z])/g, ' $1').trim() + } + else { + throw new Error("The property is not valid: " + value + ". Available values: " + JSON.stringify(Object.values(object))) + } + return retrievedValue +} + +module.exports = { + MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER, ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, ORDER_MARGIN_ENABLED, + createRecord, getRecord, getExpandedRecord, createRecordFromObject +} diff --git a/WebServer/node_modules/@superalgos/mqservice/orderLifeCicle/webDependency.js b/WebServer/node_modules/@superalgos/mqservice/orderLifeCicle/webDependency.js new file mode 100644 index 000000000..29a189bb8 --- /dev/null +++ b/WebServer/node_modules/@superalgos/mqservice/orderLifeCicle/webDependency.js @@ -0,0 +1,163 @@ + +function newOrderRecord() { + + let thisObject = { + MESSAGE_ENTITY: MESSAGE_ENTITY, + MESSAGE_TYPE: MESSAGE_TYPE, + ORDER_CREATOR: ORDER_CREATOR, + ORDER_TYPE: ORDER_TYPE, + ORDER_OWNER: ORDER_OWNER, + ORDER_DIRECTION: ORDER_DIRECTION, + ORDER_STATUS: ORDER_STATUS, + ORDER_EXIT_OUTCOME: ORDER_EXIT_OUTCOME, + createRecord: createRecord, + getRecord: getRecord, + getExpandedRecord: getExpandedRecord, + createRecordFromObject: createRecordFromObject + }; + + return thisObject + + const MESSAGE_ENTITY = { + SimulationExecutor: 'EX', + TradingCokpit: 'CO', + SimulationEngine: 'EN', + TradingAssistant: 'AS' + } + const MESSAGE_TYPE = { + HeartBeat: 'HBT', + OrderAuthorizationRequest: 'REQ', + OrderAuthorizationResponse: 'RES', + Order: 'ORD', + OrderUpdate: 'UPD' + } + const ORDER_CREATOR = { SimulationEngine: 'S', HumanTrader: 'H' } + const ORDER_TYPE = { Market: 'M', Limit: 'L', Stop: 'S', } + const ORDER_OWNER = { Node: 'N', Team: 'T', User: 'U', } + const ORDER_DIRECTION = { Sell: 'Sell', Buy: 'Buy' } + const ORDER_STATUS = { + Signaled: 'SIG', + ManualAuthorized: 'MAU', + ManualNotAuthorized: 'MNA', + AutoAuthorized: 'AAU', + AutoNotAuthorized: 'ANA', + Executing: 'EXE', + Cancelled: 'CAN', + Filled: 'FIL', + PartiallyFilled: 'PRT', + Discarded: 'DIS', + Placed: 'PLA' + } + const ORDER_EXIT_OUTCOME = { StopLoss: 'SL', TakeProfit: 'TP' } + const ORDER_MARGIN_ENABLED = { True: 1, False: 0 } + + function createRecord(messageId, from, to, messageType, messageDateTime, orderId, creator, orderDateTime, + owner, exchange, market, marginEnabled, type, rate, stop, takeProfit, direction, size, status, sizeFilled, exitOutcome) { + + return [ + messageId, + from, + to, + messageType, + messageDateTime, + [ + orderId, + creator, + orderDateTime, + owner, + exchange, + market, + marginEnabled, + type, + rate, + stop, + takeProfit, + direction, + size, + status, + sizeFilled, + exitOutcome + ] + ] + } + + function createRecordFromObject(object) { + + return createRecord(object.id, object.from, object.to, object.messageType, object.dateTime, object.order.id, + object.order.creator, object.order.dateTime, object.order.owner, object.order.exchange, object.order.market, + object.order.marginEnabled, object.order.type, object.order.rate, object.order.stop, object.order.takeProfit, + object.order.direction, object.order.size, object.order.status, object.order.sizeFilled, object.order.exitOutcome) + } + + function getRecord(fileContent) { + let parsedFileContent = JSON.parse(fileContent) + + return { + id: parsedFileContent[0], + from: parsedFileContent[1], + to: parsedFileContent[2], + messageType: parsedFileContent[3], + dateTime: parsedFileContent[4], + order: { + id: parsedFileContent[5][0], + creator: parsedFileContent[5][1], + dateTime: parsedFileContent[5][2], + owner: parsedFileContent[5][3], + exchange: parsedFileContent[5][4], + market: parsedFileContent[5][5], + marginEnabled: parsedFileContent[5][6], + type: parsedFileContent[5][7], + rate: parsedFileContent[5][8], + stop: parsedFileContent[5][9], + takeProfit: parsedFileContent[5][10], + direction: parsedFileContent[5][11], + size: parsedFileContent[5][12], + status: parsedFileContent[5][13], + sizeFilled: parsedFileContent[5][14], + exitOutcome: parsedFileContent[5][15] + } + } + } + + function getExpandedRecord(fileContent) { + let parsedFileContent = JSON.parse(fileContent) + + return { + id: parsedFileContent[0], + from: getKeyByValue(MESSAGE_ENTITY, parsedFileContent[1]), + to: getKeyByValue(MESSAGE_ENTITY, parsedFileContent[2]), + messageType: getKeyByValue(MESSAGE_TYPE, parsedFileContent[3]), + dateTime: parsedFileContent[4], + order: { + id: parsedFileContent[5][0], + creator: getKeyByValue(ORDER_CREATOR, parsedFileContent[5][1]), + dateTime: parsedFileContent[5][2], + owner: getKeyByValue(ORDER_OWNER, parsedFileContent[5][3]), + exchange: parsedFileContent[5][4], + market: parsedFileContent[5][5], + marginEnabled: getKeyByValue(ORDER_MARGIN_ENABLED, parsedFileContent[5][6]), + type: getKeyByValue(ORDER_TYPE, parsedFileContent[5][7]), + rate: parsedFileContent[5][8], + stop: parsedFileContent[5][9], + takeProfit: parsedFileContent[5][10], + direction: getKeyByValue(ORDER_DIRECTION, parsedFileContent[5][11]), + size: parsedFileContent[5][12], + status: getKeyByValue(ORDER_STATUS, parsedFileContent[5][13]), + sizeFilled: parsedFileContent[5][14], + exitOutcome: getKeyByValue(ORDER_EXIT_OUTCOME, parsedFileContent[5][15]) + } + } + } + + function getKeyByValue(object, value) { + let retrievedValue = Object.keys(object).find(key => object[key] === value) + if (retrievedValue !== undefined) { + retrievedValue = retrievedValue.replace(/([A-Z])/g, ' $1').trim() + } + else { + throw new Error("The property is not valid: " + value + ". Available values: " + JSON.stringify(Object.values(object))) + } + return retrievedValue + } + +} diff --git a/WebServer/node_modules/@superalgos/mqservice/package.json b/WebServer/node_modules/@superalgos/mqservice/package.json new file mode 100644 index 000000000..d0d084315 --- /dev/null +++ b/WebServer/node_modules/@superalgos/mqservice/package.json @@ -0,0 +1,54 @@ +{ + "_args": [ + [ + "@superalgos/mqservice@0.0.12", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "@superalgos/mqservice@0.0.12", + "_id": "@superalgos/mqservice@0.0.12", + "_inBundle": false, + "_integrity": "sha512-Dw6VvjuqB1a7w+7Ghqi4pxvEJDUKWMbS1W/xNmSlTJhI3sW/YexmBbF8nmvdyIfxtajO/+Q1ZoqeBWsiPcjmcQ==", + "_location": "/@superalgos/mqservice", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "@superalgos/mqservice@0.0.12", + "name": "@superalgos/mqservice", + "escapedName": "@superalgos%2fmqservice", + "scope": "@superalgos", + "rawSpec": "0.0.12", + "saveSpec": null, + "fetchSpec": "0.0.12" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/@superalgos/mqservice/-/mqservice-0.0.12.tgz", + "_spec": "0.0.12", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Superalgos, ltd." + }, + "bugs": { + "url": "https://github.com/Superalgos/issues" + }, + "dependencies": { + "chai": "^4.2.0", + "mocha": "^6.0.2" + }, + "description": "Provide a common framework for objects between different modules.", + "homepage": "https://github.com/Superalgos/MQService#readme", + "license": "SEE LICENSE IN LICENSE.MD", + "main": "index.js", + "name": "@superalgos/mqservice", + "repository": { + "type": "git", + "url": "git+https://github.com/Superalgos/MQService.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "0.0.12" +} diff --git a/WebServer/node_modules/@superalgos/mqservice/test/orderLifeCicleTest/orderLifeCicleTest.js b/WebServer/node_modules/@superalgos/mqservice/test/orderLifeCicleTest/orderLifeCicleTest.js new file mode 100644 index 000000000..22d54c1ca --- /dev/null +++ b/WebServer/node_modules/@superalgos/mqservice/test/orderLifeCicleTest/orderLifeCicleTest.js @@ -0,0 +1,144 @@ +var assert = require("chai").assert +const { + MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER, + ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, ORDER_MARGIN_ENABLED, + createRecord, getRecord, getExpandedRecord, createRecordFromObject +} = require("../../orderLifeCicle/index") + +describe("SimulatorExecutor ", function () { + describe("Order Life Cicle Test", function () { + it("testCreateRecord", function () { + let expected = JSON.stringify([ + 90, + "EN", + "EX", + "ORD", + 1553850096262, + [ + 1, + "S", + 155385234234, + "U", + "Poloniex", + "BTC/USDT", + 0, + "L", + 6286.707, + 6381.007, + 0, + "Sell", + 0, + "SIG", + 0, + "SL" + ] + ]) + + let record = createRecord(90, MESSAGE_ENTITY.SimulationEngine, MESSAGE_ENTITY.SimulationExecutor, + MESSAGE_TYPE.Order, 1553850096262, 1, ORDER_CREATOR.SimulationEngine, 155385234234, ORDER_OWNER.User, + "Poloniex", "BTC/USDT", 0, ORDER_TYPE.Limit, 6286.707, 6381.007, ORDER_MARGIN_ENABLED.False, ORDER_DIRECTION.Sell, 0, + ORDER_STATUS.Signaled, 0, ORDER_EXIT_OUTCOME.StopLoss) + + let result = JSON.stringify(record) + assert.equal(result, expected) + }) + it("testGetRecord", function () { + let fileContent = '[90, "EN", "EX", "ORD", 1553850096262, [1, "S", 155385234234, "U", "Poloniex", "BTC/USDT", 0, "L", 6286.707, 6381.007, 0, "Sell", 0, "SIG", 0, "SL"]]' + + let expected = JSON.stringify({ + "id": 90, + "from": "EN", + "to": "EX", + "messageType": "ORD", + "dateTime": 1553850096262, + "order": { + "id": 1, + "creator": "S", + "dateTime": 155385234234, + "owner": "U", + "exchange": "Poloniex", + "market": "BTC/USDT", + "marginEnabled": 0, + "type": "L", + "rate": 6286.707, + "stop": 6381.007, + "takeProfit": 0, + "direction": "Sell", + "size": 0, + "status": "SIG", + "sizeFilled": 0, + "exitOutcome": "SL" + } + }) + + let record = getRecord(fileContent) + let result = JSON.stringify(record) + assert.equal(result, expected) + }) + it("testGetExpandedRecord", function () { + let fileContent = '[90, "EN", "EX", "ORD", 1553850096262, [1, "S", 155385234234, "U", "Poloniex", "BTC/USDT", 0, "L", 6286.707, 6381.007, 0, "Sell", 0, "SIG", 0, "SL"]]' + + let expected = JSON.stringify({ + "id": 90, + "from": "Simulation Engine", + "to": "Simulation Executor", + "messageType": "Order", + "dateTime": 1553850096262, + "order": { + "id": 1, + "creator": "Simulation Engine", + "dateTime": 155385234234, + "owner": "User", + "exchange": "Poloniex", + "market": "BTC/USDT", + "marginEnabled": "False", + "type": "Limit", + "rate": 6286.707, + "stop": 6381.007, + "takeProfit": 0, + "direction": "Sell", + "size": 0, + "status": "Signaled", + "sizeFilled": 0, + "exitOutcome": "Stop Loss" + } + }) + + let expandedRecord = getExpandedRecord(fileContent) + let result = JSON.stringify(expandedRecord) + assert.equal(result, expected) + }) + it("createRecordFromObject", function () { + let expected = JSON.stringify(JSON.parse('[136, "EN", "EX", "ORD", 1553850096262, [1, "S", 155385234234, "U", "Poloniex", "BTC/USDT", 0, "L", 6286.707, 6381.007, 0, "Sell", 0, "SIG", 0, "SL"]]')) + + let newRecord = {} + newRecord.id = 136 + newRecord.from = MESSAGE_ENTITY.SimulationEngine + newRecord.to = MESSAGE_ENTITY.SimulationExecutor + newRecord.messageType = MESSAGE_TYPE.Order + newRecord.dateTime = 1553850096262 + + newRecord.order = {} + newRecord.order.id = 1 + newRecord.order.creator = ORDER_CREATOR.SimulationEngine + newRecord.order.dateTime = 155385234234 + newRecord.order.owner = ORDER_OWNER.User + newRecord.order.exchange = "Poloniex" + newRecord.order.market = "BTC/USDT" + newRecord.order.marginEnabled = ORDER_MARGIN_ENABLED.False + newRecord.order.type = ORDER_TYPE.Limit + newRecord.order.rate = 6286.707 + newRecord.order.stop = 6381.007 + newRecord.order.takeProfit = 0 + newRecord.order.direction = ORDER_DIRECTION.Sell + newRecord.order.size = 0 + newRecord.order.status = ORDER_STATUS.Signaled + newRecord.order.sizeFilled = 0 + newRecord.order.exitOutcome = ORDER_EXIT_OUTCOME.StopLoss + + let recordFromObject = createRecordFromObject(newRecord) + let result = JSON.stringify(recordFromObject) + assert.equal(result, expected) + }) + }) +}) diff --git a/WebServer/node_modules/ansi-colors/index.js b/WebServer/node_modules/ansi-colors/index.js new file mode 100644 index 000000000..4e8ed08ab --- /dev/null +++ b/WebServer/node_modules/ansi-colors/index.js @@ -0,0 +1,114 @@ +'use strict'; + +const colors = { enabled: true, visible: true, styles: {}, keys: {} }; + +if ('FORCE_COLOR' in process.env) { + colors.enabled = process.env.FORCE_COLOR !== '0'; +} + +const ansi = style => { + style.open = `\u001b[${style.codes[0]}m`; + style.close = `\u001b[${style.codes[1]}m`; + style.regex = new RegExp(`\\u001b\\[${style.codes[1]}m`, 'g'); + return style; +}; + +const wrap = (style, str, nl) => { + let { open, close, regex } = style; + str = open + (str.includes(close) ? str.replace(regex, close + open) : str) + close; + // see https://github.com/chalk/chalk/pull/92, thanks to the + // chalk contributors for this fix. However, we've confirmed that + // this issue is also present in Windows terminals + return nl ? str.replace(/\r?\n/g, `${close}$&${open}`) : str; +}; + +const style = (input, stack) => { + if (input === '' || input == null) return ''; + if (colors.enabled === false) return input; + if (colors.visible === false) return ''; + let str = '' + input; + let nl = str.includes('\n'); + let n = stack.length; + while (n-- > 0) str = wrap(colors.styles[stack[n]], str, nl); + return str; +}; + +const define = (name, codes, type) => { + colors.styles[name] = ansi({ name, codes }); + let t = colors.keys[type] || (colors.keys[type] = []); + t.push(name); + + Reflect.defineProperty(colors, name, { + get() { + let color = input => style(input, color.stack); + Reflect.setPrototypeOf(color, colors); + color.stack = this.stack ? this.stack.concat(name) : [name]; + return color; + } + }); +}; + +define('reset', [0, 0], 'modifier'); +define('bold', [1, 22], 'modifier'); +define('dim', [2, 22], 'modifier'); +define('italic', [3, 23], 'modifier'); +define('underline', [4, 24], 'modifier'); +define('inverse', [7, 27], 'modifier'); +define('hidden', [8, 28], 'modifier'); +define('strikethrough', [9, 29], 'modifier'); + +define('black', [30, 39], 'color'); +define('red', [31, 39], 'color'); +define('green', [32, 39], 'color'); +define('yellow', [33, 39], 'color'); +define('blue', [34, 39], 'color'); +define('magenta', [35, 39], 'color'); +define('cyan', [36, 39], 'color'); +define('white', [37, 39], 'color'); +define('gray', [90, 39], 'color'); +define('grey', [90, 39], 'color'); + +define('bgBlack', [40, 49], 'bg'); +define('bgRed', [41, 49], 'bg'); +define('bgGreen', [42, 49], 'bg'); +define('bgYellow', [43, 49], 'bg'); +define('bgBlue', [44, 49], 'bg'); +define('bgMagenta', [45, 49], 'bg'); +define('bgCyan', [46, 49], 'bg'); +define('bgWhite', [47, 49], 'bg'); + +define('blackBright', [90, 39], 'bright'); +define('redBright', [91, 39], 'bright'); +define('greenBright', [92, 39], 'bright'); +define('yellowBright', [93, 39], 'bright'); +define('blueBright', [94, 39], 'bright'); +define('magentaBright', [95, 39], 'bright'); +define('cyanBright', [96, 39], 'bright'); +define('whiteBright', [97, 39], 'bright'); + +define('bgBlackBright', [100, 49], 'bgBright'); +define('bgRedBright', [101, 49], 'bgBright'); +define('bgGreenBright', [102, 49], 'bgBright'); +define('bgYellowBright', [103, 49], 'bgBright'); +define('bgBlueBright', [104, 49], 'bgBright'); +define('bgMagentaBright', [105, 49], 'bgBright'); +define('bgCyanBright', [106, 49], 'bgBright'); +define('bgWhiteBright', [107, 49], 'bgBright'); + +/* eslint-disable no-control-regex */ +const re = colors.ansiRegex = /\u001b\[\d+m/gm; +colors.hasColor = colors.hasAnsi = str => { + re.lastIndex = 0; + return !!str && typeof str === 'string' && re.test(str); +}; + +colors.unstyle = str => { + re.lastIndex = 0; + return typeof str === 'string' ? str.replace(re, '') : str; +}; + +colors.none = colors.clear = colors.noop = str => str; // no-op, for programmatic usage +colors.stripColor = colors.unstyle; +colors.symbols = require('./symbols'); +colors.define = define; +module.exports = colors; diff --git a/WebServer/node_modules/ansi-colors/package.json b/WebServer/node_modules/ansi-colors/package.json new file mode 100644 index 000000000..35469b738 --- /dev/null +++ b/WebServer/node_modules/ansi-colors/package.json @@ -0,0 +1,155 @@ +{ + "_args": [ + [ + "ansi-colors@3.2.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "ansi-colors@3.2.3", + "_id": "ansi-colors@3.2.3", + "_inBundle": false, + "_integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "_location": "/ansi-colors", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "ansi-colors@3.2.3", + "name": "ansi-colors", + "escapedName": "ansi-colors", + "rawSpec": "3.2.3", + "saveSpec": null, + "fetchSpec": "3.2.3" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "_spec": "3.2.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Brian Woodward", + "url": "https://github.com/doowb" + }, + "bugs": { + "url": "https://github.com/doowb/ansi-colors/issues" + }, + "contributors": [ + { + "name": "Brian Woodward", + "url": "https://twitter.com/doowb" + }, + { + "name": "Jason Schilling", + "url": "https://sourecode.de" + }, + { + "name": "Jon Schlinkert", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Jordan", + "url": "https://github.com/Silic0nS0ldier" + } + ], + "description": "Easily add ANSI colors to your text and symbols in the terminal. A faster drop-in replacement for chalk, kleur and turbocolor (without the dependencies and rendering bugs).", + "devDependencies": { + "decache": "^4.4.0", + "gulp-format-md": "^1.0.0", + "justified": "^1.0.1", + "mocha": "^5.2.0", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "symbols.js", + "types/index.d.ts" + ], + "homepage": "https://github.com/doowb/ansi-colors", + "keywords": [ + "ansi", + "bgblack", + "bgBlack", + "bgblue", + "bgBlue", + "bgcyan", + "bgCyan", + "bggreen", + "bgGreen", + "bgmagenta", + "bgMagenta", + "bgred", + "bgRed", + "bgwhite", + "bgWhite", + "bgyellow", + "bgYellow", + "black", + "blue", + "bold", + "clorox", + "colors", + "cyan", + "dim", + "gray", + "green", + "grey", + "hidden", + "inverse", + "italic", + "kleur", + "magenta", + "red", + "reset", + "strikethrough", + "underline", + "white", + "yellow" + ], + "license": "MIT", + "main": "index.js", + "name": "ansi-colors", + "repository": { + "type": "git", + "url": "git+https://github.com/doowb/ansi-colors.git" + }, + "scripts": { + "test": "mocha" + }, + "types": "./types/index.d.ts", + "verb": { + "toc": false, + "layout": "default", + "tasks": [ + "readme" + ], + "data": { + "author": { + "linkedin": "woodwardbrian", + "twitter": "doowb" + } + }, + "plugins": [ + "gulp-format-md" + ], + "lint": { + "reflinks": true + }, + "related": { + "list": [ + "ansi-wrap", + "strip-color" + ] + }, + "reflinks": [ + "chalk", + "colorette", + "colors", + "kleur" + ] + }, + "version": "3.2.3" +} diff --git a/WebServer/node_modules/ansi-colors/symbols.js b/WebServer/node_modules/ansi-colors/symbols.js new file mode 100644 index 000000000..9643d8d0a --- /dev/null +++ b/WebServer/node_modules/ansi-colors/symbols.js @@ -0,0 +1,46 @@ +'use strict'; + +const isWindows = process.platform === 'win32'; +const isLinux = process.platform === 'linux'; + +const windows = { + bullet: '•', + check: '√', + cross: '×', + ellipsis: '...', + heart: '�', + info: 'i', + line: '─', + middot: '·', + minus: '�', + plus: '+', + question: '?', + questionSmall: '﹖', + pointer: '>', + pointerSmall: '»', + warning: '‼' +}; + +const other = { + ballotCross: '✘', + bullet: '•', + check: '✔', + cross: '✖', + ellipsis: '…', + heart: '�', + info: 'ℹ', + line: '─', + middot: '·', + minus: '�', + plus: '+', + question: '?', + questionFull: '?', + questionSmall: '﹖', + pointer: isLinux ? '▸' : '�', + pointerSmall: isLinux ? '‣' : '›', + warning: '⚠' +}; + +module.exports = isWindows ? windows : other; +Reflect.defineProperty(module.exports, 'windows', { enumerable: false, value: windows }); +Reflect.defineProperty(module.exports, 'other', { enumerable: false, value: other }); diff --git a/WebServer/node_modules/ansi-regex/index.js b/WebServer/node_modules/ansi-regex/index.js new file mode 100644 index 000000000..c4aaecf50 --- /dev/null +++ b/WebServer/node_modules/ansi-regex/index.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = () => { + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, 'g'); +}; diff --git a/WebServer/node_modules/ansi-regex/package.json b/WebServer/node_modules/ansi-regex/package.json new file mode 100644 index 000000000..062435dae --- /dev/null +++ b/WebServer/node_modules/ansi-regex/package.json @@ -0,0 +1,88 @@ +{ + "_args": [ + [ + "ansi-regex@3.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "ansi-regex@3.0.0", + "_id": "ansi-regex@3.0.0", + "_inBundle": false, + "_integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "_location": "/ansi-regex", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "ansi-regex@3.0.0", + "name": "ansi-regex", + "escapedName": "ansi-regex", + "rawSpec": "3.0.0", + "saveSpec": null, + "fetchSpec": "3.0.0" + }, + "_requiredBy": [ + "/strip-ansi" + ], + "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "_spec": "3.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-regex/issues" + }, + "description": "Regular expression for matching ANSI escape codes", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-regex#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "license": "MIT", + "name": "ansi-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-regex.git" + }, + "scripts": { + "test": "xo && ava", + "view-supported": "node fixtures/view-codes.js" + }, + "version": "3.0.0" +} diff --git a/WebServer/node_modules/ansi-styles/index.js b/WebServer/node_modules/ansi-styles/index.js new file mode 100644 index 000000000..90a871c4d --- /dev/null +++ b/WebServer/node_modules/ansi-styles/index.js @@ -0,0 +1,165 @@ +'use strict'; +const colorConvert = require('color-convert'); + +const wrapAnsi16 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${code + offset}m`; +}; + +const wrapAnsi256 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};5;${code}m`; +}; + +const wrapAnsi16m = (fn, offset) => function () { + const rgb = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; + +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + gray: [90, 39], + + // Bright color + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } + }; + + // Fix humans + styles.color.grey = styles.color.gray; + + for (const groupName of Object.keys(styles)) { + const group = styles[groupName]; + + for (const styleName of Object.keys(group)) { + const style = group[styleName]; + + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + } + + const ansi2ansi = n => n; + const rgb2rgb = (r, g, b) => [r, g, b]; + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + styles.color.ansi = { + ansi: wrapAnsi16(ansi2ansi, 0) + }; + styles.color.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 0) + }; + styles.color.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 0) + }; + + styles.bgColor.ansi = { + ansi: wrapAnsi16(ansi2ansi, 10) + }; + styles.bgColor.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 10) + }; + styles.bgColor.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 10) + }; + + for (let key of Object.keys(colorConvert)) { + if (typeof colorConvert[key] !== 'object') { + continue; + } + + const suite = colorConvert[key]; + + if (key === 'ansi16') { + key = 'ansi'; + } + + if ('ansi16' in suite) { + styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); + styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); + } + + if ('ansi256' in suite) { + styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); + styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); + } + + if ('rgb' in suite) { + styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); + styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); + } + } + + return styles; +} + +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); diff --git a/WebServer/node_modules/ansi-styles/package.json b/WebServer/node_modules/ansi-styles/package.json new file mode 100644 index 000000000..44746a77b --- /dev/null +++ b/WebServer/node_modules/ansi-styles/package.json @@ -0,0 +1,91 @@ +{ + "_args": [ + [ + "ansi-styles@3.2.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "ansi-styles@3.2.1", + "_id": "ansi-styles@3.2.1", + "_inBundle": false, + "_integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "_location": "/ansi-styles", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "ansi-styles@3.2.1", + "name": "ansi-styles", + "escapedName": "ansi-styles", + "rawSpec": "3.2.1", + "saveSpec": null, + "fetchSpec": "3.2.1" + }, + "_requiredBy": [ + "/chalk" + ], + "_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "_spec": "3.2.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "ava": { + "require": "babel-polyfill" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-styles/issues" + }, + "dependencies": { + "color-convert": "^1.9.0" + }, + "description": "ANSI escape codes for styling strings in the terminal", + "devDependencies": { + "ava": "*", + "babel-polyfill": "^6.23.0", + "svg-term-cli": "^2.1.1", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-styles#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "ansi-styles", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-styles.git" + }, + "scripts": { + "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor", + "test": "xo && ava" + }, + "version": "3.2.1" +} diff --git a/WebServer/node_modules/argparse/index.js b/WebServer/node_modules/argparse/index.js new file mode 100644 index 000000000..3bbc14320 --- /dev/null +++ b/WebServer/node_modules/argparse/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./lib/argparse'); diff --git a/WebServer/node_modules/argparse/lib/action.js b/WebServer/node_modules/argparse/lib/action.js new file mode 100644 index 000000000..1483c79ff --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action.js @@ -0,0 +1,146 @@ +/** + * class Action + * + * Base class for all actions + * Do not call in your code, use this class only for inherits your own action + * + * Information about how to convert command line strings to Javascript objects. + * Action objects are used by an ArgumentParser to represent the information + * needed to parse a single argument from one or more strings from the command + * line. The keyword arguments to the Action constructor are also all attributes + * of Action instances. + * + * ##### Allowed keywords: + * + * - `store` + * - `storeConstant` + * - `storeTrue` + * - `storeFalse` + * - `append` + * - `appendConstant` + * - `count` + * - `help` + * - `version` + * + * Information about action options see [[Action.new]] + * + * See also [original guide](http://docs.python.org/dev/library/argparse.html#action) + * + **/ + +'use strict'; + + +// Constants +var c = require('./const'); + + +/** + * new Action(options) + * + * Base class for all actions. Used only for inherits + * + * + * ##### Options: + * + * - `optionStrings` A list of command-line option strings for the action. + * - `dest` Attribute to hold the created object(s) + * - `nargs` The number of command-line arguments that should be consumed. + * By default, one argument will be consumed and a single value will be + * produced. + * - `constant` Default value for an action with no value. + * - `defaultValue` The value to be produced if the option is not specified. + * - `type` Cast to 'string'|'int'|'float'|'complex'|function (string). If + * None, 'string'. + * - `choices` The choices available. + * - `required` True if the action must always be specified at the command + * line. + * - `help` The help describing the argument. + * - `metavar` The name to be used for the option's argument with the help + * string. If None, the 'dest' value will be used as the name. + * + * ##### nargs supported values: + * + * - `N` (an integer) consumes N arguments (and produces a list) + * - `?` consumes zero or one arguments + * - `*` consumes zero or more arguments (and produces a list) + * - `+` consumes one or more arguments (and produces a list) + * + * Note: that the difference between the default and nargs=1 is that with the + * default, a single value will be produced, while with nargs=1, a list + * containing a single value will be produced. + **/ +var Action = module.exports = function Action(options) { + options = options || {}; + this.optionStrings = options.optionStrings || []; + this.dest = options.dest; + this.nargs = typeof options.nargs !== 'undefined' ? options.nargs : null; + this.constant = typeof options.constant !== 'undefined' ? options.constant : null; + this.defaultValue = options.defaultValue; + this.type = typeof options.type !== 'undefined' ? options.type : null; + this.choices = typeof options.choices !== 'undefined' ? options.choices : null; + this.required = typeof options.required !== 'undefined' ? options.required : false; + this.help = typeof options.help !== 'undefined' ? options.help : null; + this.metavar = typeof options.metavar !== 'undefined' ? options.metavar : null; + + if (!(this.optionStrings instanceof Array)) { + throw new Error('optionStrings should be an array'); + } + if (typeof this.required !== 'undefined' && typeof this.required !== 'boolean') { + throw new Error('required should be a boolean'); + } +}; + +/** + * Action#getName -> String + * + * Tells action name + **/ +Action.prototype.getName = function () { + if (this.optionStrings.length > 0) { + return this.optionStrings.join('/'); + } else if (this.metavar !== null && this.metavar !== c.SUPPRESS) { + return this.metavar; + } else if (typeof this.dest !== 'undefined' && this.dest !== c.SUPPRESS) { + return this.dest; + } + return null; +}; + +/** + * Action#isOptional -> Boolean + * + * Return true if optional + **/ +Action.prototype.isOptional = function () { + return !this.isPositional(); +}; + +/** + * Action#isPositional -> Boolean + * + * Return true if positional + **/ +Action.prototype.isPositional = function () { + return (this.optionStrings.length === 0); +}; + +/** + * Action#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Call the action. Should be implemented in inherited classes + * + * ##### Example + * + * ActionCount.prototype.call = function (parser, namespace, values, optionString) { + * namespace.set(this.dest, (namespace[this.dest] || 0) + 1); + * }; + * + **/ +Action.prototype.call = function () { + throw new Error('.call() not defined');// Not Implemented error +}; diff --git a/WebServer/node_modules/argparse/lib/action/append.js b/WebServer/node_modules/argparse/lib/action/append.js new file mode 100644 index 000000000..b5da0de23 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/append.js @@ -0,0 +1,53 @@ +/*:nodoc:* + * class ActionAppend + * + * This action stores a list, and appends each argument value to the list. + * This is useful to allow an option to be specified multiple times. + * This class inherided from [[Action]] + * + **/ + +'use strict'; + +var util = require('util'); + +var Action = require('../action'); + +// Constants +var c = require('../const'); + +/*:nodoc:* + * new ActionAppend(options) + * - options (object): options hash see [[Action.new]] + * + * Note: options.nargs should be optional for constants + * and more then zero for other + **/ +var ActionAppend = module.exports = function ActionAppend(options) { + options = options || {}; + if (this.nargs <= 0) { + throw new Error('nargs for append actions must be > 0; if arg ' + + 'strings are not supplying the value to append, ' + + 'the append const action may be more appropriate'); + } + if (!!this.constant && this.nargs !== c.OPTIONAL) { + throw new Error('nargs must be OPTIONAL to supply const'); + } + Action.call(this, options); +}; +util.inherits(ActionAppend, Action); + +/*:nodoc:* + * ActionAppend#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Call the action. Save result in namespace object + **/ +ActionAppend.prototype.call = function (parser, namespace, values) { + var items = (namespace[this.dest] || []).slice(); + items.push(values); + namespace.set(this.dest, items); +}; diff --git a/WebServer/node_modules/argparse/lib/action/append/constant.js b/WebServer/node_modules/argparse/lib/action/append/constant.js new file mode 100644 index 000000000..313f5d2ef --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/append/constant.js @@ -0,0 +1,47 @@ +/*:nodoc:* + * class ActionAppendConstant + * + * This stores a list, and appends the value specified by + * the const keyword argument to the list. + * (Note that the const keyword argument defaults to null.) + * The 'appendConst' action is typically useful when multiple + * arguments need to store constants to the same list. + * + * This class inherited from [[Action]] + **/ + +'use strict'; + +var util = require('util'); + +var Action = require('../../action'); + +/*:nodoc:* + * new ActionAppendConstant(options) + * - options (object): options hash see [[Action.new]] + * + **/ +var ActionAppendConstant = module.exports = function ActionAppendConstant(options) { + options = options || {}; + options.nargs = 0; + if (typeof options.constant === 'undefined') { + throw new Error('constant option is required for appendAction'); + } + Action.call(this, options); +}; +util.inherits(ActionAppendConstant, Action); + +/*:nodoc:* + * ActionAppendConstant#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Call the action. Save result in namespace object + **/ +ActionAppendConstant.prototype.call = function (parser, namespace) { + var items = [].concat(namespace[this.dest] || []); + items.push(this.constant); + namespace.set(this.dest, items); +}; diff --git a/WebServer/node_modules/argparse/lib/action/count.js b/WebServer/node_modules/argparse/lib/action/count.js new file mode 100644 index 000000000..d6a5899d0 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/count.js @@ -0,0 +1,40 @@ +/*:nodoc:* + * class ActionCount + * + * This counts the number of times a keyword argument occurs. + * For example, this is useful for increasing verbosity levels + * + * This class inherided from [[Action]] + * + **/ +'use strict'; + +var util = require('util'); + +var Action = require('../action'); + +/*:nodoc:* + * new ActionCount(options) + * - options (object): options hash see [[Action.new]] + * + **/ +var ActionCount = module.exports = function ActionCount(options) { + options = options || {}; + options.nargs = 0; + + Action.call(this, options); +}; +util.inherits(ActionCount, Action); + +/*:nodoc:* + * ActionCount#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Call the action. Save result in namespace object + **/ +ActionCount.prototype.call = function (parser, namespace) { + namespace.set(this.dest, (namespace[this.dest] || 0) + 1); +}; diff --git a/WebServer/node_modules/argparse/lib/action/help.js b/WebServer/node_modules/argparse/lib/action/help.js new file mode 100644 index 000000000..b40e05a6f --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/help.js @@ -0,0 +1,47 @@ +/*:nodoc:* + * class ActionHelp + * + * Support action for printing help + * This class inherided from [[Action]] + **/ +'use strict'; + +var util = require('util'); + +var Action = require('../action'); + +// Constants +var c = require('../const'); + +/*:nodoc:* + * new ActionHelp(options) + * - options (object): options hash see [[Action.new]] + * + **/ +var ActionHelp = module.exports = function ActionHelp(options) { + options = options || {}; + if (options.defaultValue !== null) { + options.defaultValue = options.defaultValue; + } else { + options.defaultValue = c.SUPPRESS; + } + options.dest = (options.dest !== null ? options.dest : c.SUPPRESS); + options.nargs = 0; + Action.call(this, options); + +}; +util.inherits(ActionHelp, Action); + +/*:nodoc:* + * ActionHelp#call(parser, namespace, values, optionString) + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Print help and exit + **/ +ActionHelp.prototype.call = function (parser) { + parser.printHelp(); + parser.exit(); +}; diff --git a/WebServer/node_modules/argparse/lib/action/store.js b/WebServer/node_modules/argparse/lib/action/store.js new file mode 100644 index 000000000..283b86092 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/store.js @@ -0,0 +1,50 @@ +/*:nodoc:* + * class ActionStore + * + * This action just stores the argument’s value. This is the default action. + * + * This class inherited from [[Action]] + * + **/ +'use strict'; + +var util = require('util'); + +var Action = require('../action'); + +// Constants +var c = require('../const'); + + +/*:nodoc:* + * new ActionStore(options) + * - options (object): options hash see [[Action.new]] + * + **/ +var ActionStore = module.exports = function ActionStore(options) { + options = options || {}; + if (this.nargs <= 0) { + throw new Error('nargs for store actions must be > 0; if you ' + + 'have nothing to store, actions such as store ' + + 'true or store const may be more appropriate'); + + } + if (typeof this.constant !== 'undefined' && this.nargs !== c.OPTIONAL) { + throw new Error('nargs must be OPTIONAL to supply const'); + } + Action.call(this, options); +}; +util.inherits(ActionStore, Action); + +/*:nodoc:* + * ActionStore#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Call the action. Save result in namespace object + **/ +ActionStore.prototype.call = function (parser, namespace, values) { + namespace.set(this.dest, values); +}; diff --git a/WebServer/node_modules/argparse/lib/action/store/constant.js b/WebServer/node_modules/argparse/lib/action/store/constant.js new file mode 100644 index 000000000..23caa897b --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/store/constant.js @@ -0,0 +1,43 @@ +/*:nodoc:* + * class ActionStoreConstant + * + * This action stores the value specified by the const keyword argument. + * (Note that the const keyword argument defaults to the rather unhelpful null.) + * The 'store_const' action is most commonly used with optional + * arguments that specify some sort of flag. + * + * This class inherited from [[Action]] + **/ +'use strict'; + +var util = require('util'); + +var Action = require('../../action'); + +/*:nodoc:* + * new ActionStoreConstant(options) + * - options (object): options hash see [[Action.new]] + * + **/ +var ActionStoreConstant = module.exports = function ActionStoreConstant(options) { + options = options || {}; + options.nargs = 0; + if (typeof options.constant === 'undefined') { + throw new Error('constant option is required for storeAction'); + } + Action.call(this, options); +}; +util.inherits(ActionStoreConstant, Action); + +/*:nodoc:* + * ActionStoreConstant#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Call the action. Save result in namespace object + **/ +ActionStoreConstant.prototype.call = function (parser, namespace) { + namespace.set(this.dest, this.constant); +}; diff --git a/WebServer/node_modules/argparse/lib/action/store/false.js b/WebServer/node_modules/argparse/lib/action/store/false.js new file mode 100644 index 000000000..9924f461d --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/store/false.js @@ -0,0 +1,27 @@ +/*:nodoc:* + * class ActionStoreFalse + * + * This action store the values False respectively. + * This is special cases of 'storeConst' + * + * This class inherited from [[Action]] + **/ + +'use strict'; + +var util = require('util'); + +var ActionStoreConstant = require('./constant'); + +/*:nodoc:* + * new ActionStoreFalse(options) + * - options (object): hash of options see [[Action.new]] + * + **/ +var ActionStoreFalse = module.exports = function ActionStoreFalse(options) { + options = options || {}; + options.constant = false; + options.defaultValue = options.defaultValue !== null ? options.defaultValue : true; + ActionStoreConstant.call(this, options); +}; +util.inherits(ActionStoreFalse, ActionStoreConstant); diff --git a/WebServer/node_modules/argparse/lib/action/store/true.js b/WebServer/node_modules/argparse/lib/action/store/true.js new file mode 100644 index 000000000..9e22f7d44 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/store/true.js @@ -0,0 +1,26 @@ +/*:nodoc:* + * class ActionStoreTrue + * + * This action store the values True respectively. + * This isspecial cases of 'storeConst' + * + * This class inherited from [[Action]] + **/ +'use strict'; + +var util = require('util'); + +var ActionStoreConstant = require('./constant'); + +/*:nodoc:* + * new ActionStoreTrue(options) + * - options (object): options hash see [[Action.new]] + * + **/ +var ActionStoreTrue = module.exports = function ActionStoreTrue(options) { + options = options || {}; + options.constant = true; + options.defaultValue = options.defaultValue !== null ? options.defaultValue : false; + ActionStoreConstant.call(this, options); +}; +util.inherits(ActionStoreTrue, ActionStoreConstant); diff --git a/WebServer/node_modules/argparse/lib/action/subparsers.js b/WebServer/node_modules/argparse/lib/action/subparsers.js new file mode 100644 index 000000000..99dfedd0f --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/subparsers.js @@ -0,0 +1,149 @@ +/** internal + * class ActionSubparsers + * + * Support the creation of such sub-commands with the addSubparsers() + * + * This class inherited from [[Action]] + **/ +'use strict'; + +var util = require('util'); +var format = require('util').format; + + +var Action = require('../action'); + +// Constants +var c = require('../const'); + +// Errors +var argumentErrorHelper = require('../argument/error'); + + +/*:nodoc:* + * new ChoicesPseudoAction(name, help) + * + * Create pseudo action for correct help text + * + **/ +function ChoicesPseudoAction(name, help) { + var options = { + optionStrings: [], + dest: name, + help: help + }; + + Action.call(this, options); +} + +util.inherits(ChoicesPseudoAction, Action); + +/** + * new ActionSubparsers(options) + * - options (object): options hash see [[Action.new]] + * + **/ +function ActionSubparsers(options) { + options = options || {}; + options.dest = options.dest || c.SUPPRESS; + options.nargs = c.PARSER; + + this.debug = (options.debug === true); + + this._progPrefix = options.prog; + this._parserClass = options.parserClass; + this._nameParserMap = {}; + this._choicesActions = []; + + options.choices = this._nameParserMap; + Action.call(this, options); +} + +util.inherits(ActionSubparsers, Action); + +/*:nodoc:* + * ActionSubparsers#addParser(name, options) -> ArgumentParser + * - name (string): sub-command name + * - options (object): see [[ArgumentParser.new]] + * + * Note: + * addParser supports an additional aliases option, + * which allows multiple strings to refer to the same subparser. + * This example, like svn, aliases co as a shorthand for checkout + * + **/ +ActionSubparsers.prototype.addParser = function (name, options) { + var parser; + + var self = this; + + options = options || {}; + + options.debug = (this.debug === true); + + // set program from the existing prefix + if (!options.prog) { + options.prog = this._progPrefix + ' ' + name; + } + + var aliases = options.aliases || []; + + // create a pseudo-action to hold the choice help + if (!!options.help || typeof options.help === 'string') { + var help = options.help; + delete options.help; + + var choiceAction = new ChoicesPseudoAction(name, help); + this._choicesActions.push(choiceAction); + } + + // create the parser and add it to the map + parser = new this._parserClass(options); + this._nameParserMap[name] = parser; + + // make parser available under aliases also + aliases.forEach(function (alias) { + self._nameParserMap[alias] = parser; + }); + + return parser; +}; + +ActionSubparsers.prototype._getSubactions = function () { + return this._choicesActions; +}; + +/*:nodoc:* + * ActionSubparsers#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Call the action. Parse input aguments + **/ +ActionSubparsers.prototype.call = function (parser, namespace, values) { + var parserName = values[0]; + var argStrings = values.slice(1); + + // set the parser name if requested + if (this.dest !== c.SUPPRESS) { + namespace[this.dest] = parserName; + } + + // select the parser + if (this._nameParserMap[parserName]) { + parser = this._nameParserMap[parserName]; + } else { + throw argumentErrorHelper(format( + 'Unknown parser "%s" (choices: [%s]).', + parserName, + Object.keys(this._nameParserMap).join(', ') + )); + } + + // parse all the remaining options into the namespace + parser.parseArgs(argStrings, namespace); +}; + +module.exports = ActionSubparsers; diff --git a/WebServer/node_modules/argparse/lib/action/version.js b/WebServer/node_modules/argparse/lib/action/version.js new file mode 100644 index 000000000..8053328cd --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action/version.js @@ -0,0 +1,47 @@ +/*:nodoc:* + * class ActionVersion + * + * Support action for printing program version + * This class inherited from [[Action]] + **/ +'use strict'; + +var util = require('util'); + +var Action = require('../action'); + +// +// Constants +// +var c = require('../const'); + +/*:nodoc:* + * new ActionVersion(options) + * - options (object): options hash see [[Action.new]] + * + **/ +var ActionVersion = module.exports = function ActionVersion(options) { + options = options || {}; + options.defaultValue = (options.defaultValue ? options.defaultValue : c.SUPPRESS); + options.dest = (options.dest || c.SUPPRESS); + options.nargs = 0; + this.version = options.version; + Action.call(this, options); +}; +util.inherits(ActionVersion, Action); + +/*:nodoc:* + * ActionVersion#call(parser, namespace, values, optionString) -> Void + * - parser (ArgumentParser): current parser + * - namespace (Namespace): namespace for output data + * - values (Array): parsed values + * - optionString (Array): input option string(not parsed) + * + * Print version and exit + **/ +ActionVersion.prototype.call = function (parser) { + var version = this.version || parser.version; + var formatter = parser._getFormatter(); + formatter.addText(version); + parser.exit(0, formatter.formatHelp()); +}; diff --git a/WebServer/node_modules/argparse/lib/action_container.js b/WebServer/node_modules/argparse/lib/action_container.js new file mode 100644 index 000000000..6f1237bea --- /dev/null +++ b/WebServer/node_modules/argparse/lib/action_container.js @@ -0,0 +1,482 @@ +/** internal + * class ActionContainer + * + * Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]] + **/ + +'use strict'; + +var format = require('util').format; + +// Constants +var c = require('./const'); + +var $$ = require('./utils'); + +//Actions +var ActionHelp = require('./action/help'); +var ActionAppend = require('./action/append'); +var ActionAppendConstant = require('./action/append/constant'); +var ActionCount = require('./action/count'); +var ActionStore = require('./action/store'); +var ActionStoreConstant = require('./action/store/constant'); +var ActionStoreTrue = require('./action/store/true'); +var ActionStoreFalse = require('./action/store/false'); +var ActionVersion = require('./action/version'); +var ActionSubparsers = require('./action/subparsers'); + +// Errors +var argumentErrorHelper = require('./argument/error'); + +/** + * new ActionContainer(options) + * + * Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]] + * + * ##### Options: + * + * - `description` -- A description of what the program does + * - `prefixChars` -- Characters that prefix optional arguments + * - `argumentDefault` -- The default value for all arguments + * - `conflictHandler` -- The conflict handler to use for duplicate arguments + **/ +var ActionContainer = module.exports = function ActionContainer(options) { + options = options || {}; + + this.description = options.description; + this.argumentDefault = options.argumentDefault; + this.prefixChars = options.prefixChars || ''; + this.conflictHandler = options.conflictHandler; + + // set up registries + this._registries = {}; + + // register actions + this.register('action', null, ActionStore); + this.register('action', 'store', ActionStore); + this.register('action', 'storeConst', ActionStoreConstant); + this.register('action', 'storeTrue', ActionStoreTrue); + this.register('action', 'storeFalse', ActionStoreFalse); + this.register('action', 'append', ActionAppend); + this.register('action', 'appendConst', ActionAppendConstant); + this.register('action', 'count', ActionCount); + this.register('action', 'help', ActionHelp); + this.register('action', 'version', ActionVersion); + this.register('action', 'parsers', ActionSubparsers); + + // raise an exception if the conflict handler is invalid + this._getHandler(); + + // action storage + this._actions = []; + this._optionStringActions = {}; + + // groups + this._actionGroups = []; + this._mutuallyExclusiveGroups = []; + + // defaults storage + this._defaults = {}; + + // determines whether an "option" looks like a negative number + // -1, -1.5 -5e+4 + this._regexpNegativeNumber = new RegExp('^[-]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$'); + + // whether or not there are any optionals that look like negative + // numbers -- uses a list so it can be shared and edited + this._hasNegativeNumberOptionals = []; +}; + +// Groups must be required, then ActionContainer already defined +var ArgumentGroup = require('./argument/group'); +var MutuallyExclusiveGroup = require('./argument/exclusive'); + +// +// Registration methods +// + +/** + * ActionContainer#register(registryName, value, object) -> Void + * - registryName (String) : object type action|type + * - value (string) : keyword + * - object (Object|Function) : handler + * + * Register handlers + **/ +ActionContainer.prototype.register = function (registryName, value, object) { + this._registries[registryName] = this._registries[registryName] || {}; + this._registries[registryName][value] = object; +}; + +ActionContainer.prototype._registryGet = function (registryName, value, defaultValue) { + if (arguments.length < 3) { + defaultValue = null; + } + return this._registries[registryName][value] || defaultValue; +}; + +// +// Namespace default accessor methods +// + +/** + * ActionContainer#setDefaults(options) -> Void + * - options (object):hash of options see [[Action.new]] + * + * Set defaults + **/ +ActionContainer.prototype.setDefaults = function (options) { + options = options || {}; + for (var property in options) { + if ($$.has(options, property)) { + this._defaults[property] = options[property]; + } + } + + // if these defaults match any existing arguments, replace the previous + // default on the object with the new one + this._actions.forEach(function (action) { + if ($$.has(options, action.dest)) { + action.defaultValue = options[action.dest]; + } + }); +}; + +/** + * ActionContainer#getDefault(dest) -> Mixed + * - dest (string): action destination + * + * Return action default value + **/ +ActionContainer.prototype.getDefault = function (dest) { + var result = $$.has(this._defaults, dest) ? this._defaults[dest] : null; + + this._actions.forEach(function (action) { + if (action.dest === dest && $$.has(action, 'defaultValue')) { + result = action.defaultValue; + } + }); + + return result; +}; +// +// Adding argument actions +// + +/** + * ActionContainer#addArgument(args, options) -> Object + * - args (String|Array): argument key, or array of argument keys + * - options (Object): action objects see [[Action.new]] + * + * #### Examples + * - addArgument([ '-f', '--foo' ], { action: 'store', defaultValue: 1, ... }) + * - addArgument([ 'bar' ], { action: 'store', nargs: 1, ... }) + * - addArgument('--baz', { action: 'store', nargs: 1, ... }) + **/ +ActionContainer.prototype.addArgument = function (args, options) { + args = args; + options = options || {}; + + if (typeof args === 'string') { + args = [ args ]; + } + if (!Array.isArray(args)) { + throw new TypeError('addArgument first argument should be a string or an array'); + } + if (typeof options !== 'object' || Array.isArray(options)) { + throw new TypeError('addArgument second argument should be a hash'); + } + + // if no positional args are supplied or only one is supplied and + // it doesn't look like an option string, parse a positional argument + if (!args || args.length === 1 && this.prefixChars.indexOf(args[0][0]) < 0) { + if (args && !!options.dest) { + throw new Error('dest supplied twice for positional argument'); + } + options = this._getPositional(args, options); + + // otherwise, we're adding an optional argument + } else { + options = this._getOptional(args, options); + } + + // if no default was supplied, use the parser-level default + if (typeof options.defaultValue === 'undefined') { + var dest = options.dest; + if ($$.has(this._defaults, dest)) { + options.defaultValue = this._defaults[dest]; + } else if (typeof this.argumentDefault !== 'undefined') { + options.defaultValue = this.argumentDefault; + } + } + + // create the action object, and add it to the parser + var ActionClass = this._popActionClass(options); + if (typeof ActionClass !== 'function') { + throw new Error(format('Unknown action "%s".', ActionClass)); + } + var action = new ActionClass(options); + + // throw an error if the action type is not callable + var typeFunction = this._registryGet('type', action.type, action.type); + if (typeof typeFunction !== 'function') { + throw new Error(format('"%s" is not callable', typeFunction)); + } + + return this._addAction(action); +}; + +/** + * ActionContainer#addArgumentGroup(options) -> ArgumentGroup + * - options (Object): hash of options see [[ArgumentGroup.new]] + * + * Create new arguments groups + **/ +ActionContainer.prototype.addArgumentGroup = function (options) { + var group = new ArgumentGroup(this, options); + this._actionGroups.push(group); + return group; +}; + +/** + * ActionContainer#addMutuallyExclusiveGroup(options) -> ArgumentGroup + * - options (Object): {required: false} + * + * Create new mutual exclusive groups + **/ +ActionContainer.prototype.addMutuallyExclusiveGroup = function (options) { + var group = new MutuallyExclusiveGroup(this, options); + this._mutuallyExclusiveGroups.push(group); + return group; +}; + +ActionContainer.prototype._addAction = function (action) { + var self = this; + + // resolve any conflicts + this._checkConflict(action); + + // add to actions list + this._actions.push(action); + action.container = this; + + // index the action by any option strings it has + action.optionStrings.forEach(function (optionString) { + self._optionStringActions[optionString] = action; + }); + + // set the flag if any option strings look like negative numbers + action.optionStrings.forEach(function (optionString) { + if (optionString.match(self._regexpNegativeNumber)) { + if (!self._hasNegativeNumberOptionals.some(Boolean)) { + self._hasNegativeNumberOptionals.push(true); + } + } + }); + + // return the created action + return action; +}; + +ActionContainer.prototype._removeAction = function (action) { + var actionIndex = this._actions.indexOf(action); + if (actionIndex >= 0) { + this._actions.splice(actionIndex, 1); + } +}; + +ActionContainer.prototype._addContainerActions = function (container) { + // collect groups by titles + var titleGroupMap = {}; + this._actionGroups.forEach(function (group) { + if (titleGroupMap[group.title]) { + throw new Error(format('Cannot merge actions - two groups are named "%s".', group.title)); + } + titleGroupMap[group.title] = group; + }); + + // map each action to its group + var groupMap = {}; + function actionHash(action) { + // unique (hopefully?) string suitable as dictionary key + return action.getName(); + } + container._actionGroups.forEach(function (group) { + // if a group with the title exists, use that, otherwise + // create a new group matching the container's group + if (!titleGroupMap[group.title]) { + titleGroupMap[group.title] = this.addArgumentGroup({ + title: group.title, + description: group.description + }); + } + + // map the actions to their new group + group._groupActions.forEach(function (action) { + groupMap[actionHash(action)] = titleGroupMap[group.title]; + }); + }, this); + + // add container's mutually exclusive groups + // NOTE: if add_mutually_exclusive_group ever gains title= and + // description= then this code will need to be expanded as above + var mutexGroup; + container._mutuallyExclusiveGroups.forEach(function (group) { + mutexGroup = this.addMutuallyExclusiveGroup({ + required: group.required + }); + // map the actions to their new mutex group + group._groupActions.forEach(function (action) { + groupMap[actionHash(action)] = mutexGroup; + }); + }, this); // forEach takes a 'this' argument + + // add all actions to this container or their group + container._actions.forEach(function (action) { + var key = actionHash(action); + if (groupMap[key]) { + groupMap[key]._addAction(action); + } else { + this._addAction(action); + } + }); +}; + +ActionContainer.prototype._getPositional = function (dest, options) { + if (Array.isArray(dest)) { + dest = dest[0]; + } + // make sure required is not specified + if (options.required) { + throw new Error('"required" is an invalid argument for positionals.'); + } + + // mark positional arguments as required if at least one is + // always required + if (options.nargs !== c.OPTIONAL && options.nargs !== c.ZERO_OR_MORE) { + options.required = true; + } + if (options.nargs === c.ZERO_OR_MORE && typeof options.defaultValue === 'undefined') { + options.required = true; + } + + // return the keyword arguments with no option strings + options.dest = dest; + options.optionStrings = []; + return options; +}; + +ActionContainer.prototype._getOptional = function (args, options) { + var prefixChars = this.prefixChars; + var optionStrings = []; + var optionStringsLong = []; + + // determine short and long option strings + args.forEach(function (optionString) { + // error on strings that don't start with an appropriate prefix + if (prefixChars.indexOf(optionString[0]) < 0) { + throw new Error(format('Invalid option string "%s": must start with a "%s".', + optionString, + prefixChars + )); + } + + // strings starting with two prefix characters are long options + optionStrings.push(optionString); + if (optionString.length > 1 && prefixChars.indexOf(optionString[1]) >= 0) { + optionStringsLong.push(optionString); + } + }); + + // infer dest, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' + var dest = options.dest || null; + delete options.dest; + + if (!dest) { + var optionStringDest = optionStringsLong.length ? optionStringsLong[0] : optionStrings[0]; + dest = $$.trimChars(optionStringDest, this.prefixChars); + + if (dest.length === 0) { + throw new Error( + format('dest= is required for options like "%s"', optionStrings.join(', ')) + ); + } + dest = dest.replace(/-/g, '_'); + } + + // return the updated keyword arguments + options.dest = dest; + options.optionStrings = optionStrings; + + return options; +}; + +ActionContainer.prototype._popActionClass = function (options, defaultValue) { + defaultValue = defaultValue || null; + + var action = (options.action || defaultValue); + delete options.action; + + var actionClass = this._registryGet('action', action, action); + return actionClass; +}; + +ActionContainer.prototype._getHandler = function () { + var handlerString = this.conflictHandler; + var handlerFuncName = '_handleConflict' + $$.capitalize(handlerString); + var func = this[handlerFuncName]; + if (typeof func === 'undefined') { + var msg = 'invalid conflict resolution value: ' + handlerString; + throw new Error(msg); + } else { + return func; + } +}; + +ActionContainer.prototype._checkConflict = function (action) { + var optionStringActions = this._optionStringActions; + var conflictOptionals = []; + + // find all options that conflict with this option + // collect pairs, the string, and an existing action that it conflicts with + action.optionStrings.forEach(function (optionString) { + var conflOptional = optionStringActions[optionString]; + if (typeof conflOptional !== 'undefined') { + conflictOptionals.push([ optionString, conflOptional ]); + } + }); + + if (conflictOptionals.length > 0) { + var conflictHandler = this._getHandler(); + conflictHandler.call(this, action, conflictOptionals); + } +}; + +ActionContainer.prototype._handleConflictError = function (action, conflOptionals) { + var conflicts = conflOptionals.map(function (pair) { return pair[0]; }); + conflicts = conflicts.join(', '); + throw argumentErrorHelper( + action, + format('Conflicting option string(s): %s', conflicts) + ); +}; + +ActionContainer.prototype._handleConflictResolve = function (action, conflOptionals) { + // remove all conflicting options + var self = this; + conflOptionals.forEach(function (pair) { + var optionString = pair[0]; + var conflictingAction = pair[1]; + // remove the conflicting option string + var i = conflictingAction.optionStrings.indexOf(optionString); + if (i >= 0) { + conflictingAction.optionStrings.splice(i, 1); + } + delete self._optionStringActions[optionString]; + // if the option now has no option string, remove it from the + // container holding it + if (conflictingAction.optionStrings.length === 0) { + conflictingAction.container._removeAction(conflictingAction); + } + }); +}; diff --git a/WebServer/node_modules/argparse/lib/argparse.js b/WebServer/node_modules/argparse/lib/argparse.js new file mode 100644 index 000000000..f2a2c51d9 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/argparse.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports.ArgumentParser = require('./argument_parser.js'); +module.exports.Namespace = require('./namespace'); +module.exports.Action = require('./action'); +module.exports.HelpFormatter = require('./help/formatter.js'); +module.exports.Const = require('./const.js'); + +module.exports.ArgumentDefaultsHelpFormatter = + require('./help/added_formatters.js').ArgumentDefaultsHelpFormatter; +module.exports.RawDescriptionHelpFormatter = + require('./help/added_formatters.js').RawDescriptionHelpFormatter; +module.exports.RawTextHelpFormatter = + require('./help/added_formatters.js').RawTextHelpFormatter; diff --git a/WebServer/node_modules/argparse/lib/argument/error.js b/WebServer/node_modules/argparse/lib/argument/error.js new file mode 100644 index 000000000..c8a02a08b --- /dev/null +++ b/WebServer/node_modules/argparse/lib/argument/error.js @@ -0,0 +1,50 @@ +'use strict'; + + +var format = require('util').format; + + +var ERR_CODE = 'ARGError'; + +/*:nodoc:* + * argumentError(argument, message) -> TypeError + * - argument (Object): action with broken argument + * - message (String): error message + * + * Error format helper. An error from creating or using an argument + * (optional or positional). The string value of this exception + * is the message, augmented with information + * about the argument that caused it. + * + * #####Example + * + * var argumentErrorHelper = require('./argument/error'); + * if (conflictOptionals.length > 0) { + * throw argumentErrorHelper( + * action, + * format('Conflicting option string(s): %s', conflictOptionals.join(', ')) + * ); + * } + * + **/ +module.exports = function (argument, message) { + var argumentName = null; + var errMessage; + var err; + + if (argument.getName) { + argumentName = argument.getName(); + } else { + argumentName = '' + argument; + } + + if (!argumentName) { + errMessage = message; + } else { + errMessage = format('argument "%s": %s', argumentName, message); + } + + err = new TypeError(errMessage); + err.code = ERR_CODE; + return err; +}; diff --git a/WebServer/node_modules/argparse/lib/argument/exclusive.js b/WebServer/node_modules/argparse/lib/argument/exclusive.js new file mode 100644 index 000000000..8287e00d0 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/argument/exclusive.js @@ -0,0 +1,54 @@ +/** internal + * class MutuallyExclusiveGroup + * + * Group arguments. + * By default, ArgumentParser groups command-line arguments + * into “positional arguments� and “optional arguments� + * when displaying help messages. When there is a better + * conceptual grouping of arguments than this default one, + * appropriate groups can be created using the addArgumentGroup() method + * + * This class inherited from [[ArgumentContainer]] + **/ +'use strict'; + +var util = require('util'); + +var ArgumentGroup = require('./group'); + +/** + * new MutuallyExclusiveGroup(container, options) + * - container (object): main container + * - options (object): options.required -> true/false + * + * `required` could be an argument itself, but making it a property of + * the options argument is more consistent with the JS adaptation of the Python) + **/ +var MutuallyExclusiveGroup = module.exports = function MutuallyExclusiveGroup(container, options) { + var required; + options = options || {}; + required = options.required || false; + ArgumentGroup.call(this, container); + this.required = required; + +}; +util.inherits(MutuallyExclusiveGroup, ArgumentGroup); + + +MutuallyExclusiveGroup.prototype._addAction = function (action) { + var msg; + if (action.required) { + msg = 'mutually exclusive arguments must be optional'; + throw new Error(msg); + } + action = this._container._addAction(action); + this._groupActions.push(action); + return action; +}; + + +MutuallyExclusiveGroup.prototype._removeAction = function (action) { + this._container._removeAction(action); + this._groupActions.remove(action); +}; + diff --git a/WebServer/node_modules/argparse/lib/argument/group.js b/WebServer/node_modules/argparse/lib/argument/group.js new file mode 100644 index 000000000..58b271f2f --- /dev/null +++ b/WebServer/node_modules/argparse/lib/argument/group.js @@ -0,0 +1,75 @@ +/** internal + * class ArgumentGroup + * + * Group arguments. + * By default, ArgumentParser groups command-line arguments + * into “positional arguments� and “optional arguments� + * when displaying help messages. When there is a better + * conceptual grouping of arguments than this default one, + * appropriate groups can be created using the addArgumentGroup() method + * + * This class inherited from [[ArgumentContainer]] + **/ +'use strict'; + +var util = require('util'); + +var ActionContainer = require('../action_container'); + + +/** + * new ArgumentGroup(container, options) + * - container (object): main container + * - options (object): hash of group options + * + * #### options + * - **prefixChars** group name prefix + * - **argumentDefault** default argument value + * - **title** group title + * - **description** group description + * + **/ +var ArgumentGroup = module.exports = function ArgumentGroup(container, options) { + + options = options || {}; + + // add any missing keyword arguments by checking the container + options.conflictHandler = (options.conflictHandler || container.conflictHandler); + options.prefixChars = (options.prefixChars || container.prefixChars); + options.argumentDefault = (options.argumentDefault || container.argumentDefault); + + ActionContainer.call(this, options); + + // group attributes + this.title = options.title; + this._groupActions = []; + + // share most attributes with the container + this._container = container; + this._registries = container._registries; + this._actions = container._actions; + this._optionStringActions = container._optionStringActions; + this._defaults = container._defaults; + this._hasNegativeNumberOptionals = container._hasNegativeNumberOptionals; + this._mutuallyExclusiveGroups = container._mutuallyExclusiveGroups; +}; +util.inherits(ArgumentGroup, ActionContainer); + + +ArgumentGroup.prototype._addAction = function (action) { + // Parent add action + action = ActionContainer.prototype._addAction.call(this, action); + this._groupActions.push(action); + return action; +}; + + +ArgumentGroup.prototype._removeAction = function (action) { + // Parent remove action + ActionContainer.prototype._removeAction.call(this, action); + var actionIndex = this._groupActions.indexOf(action); + if (actionIndex >= 0) { + this._groupActions.splice(actionIndex, 1); + } +}; + diff --git a/WebServer/node_modules/argparse/lib/argument_parser.js b/WebServer/node_modules/argparse/lib/argument_parser.js new file mode 100644 index 000000000..bd9a59a45 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/argument_parser.js @@ -0,0 +1,1161 @@ +/** + * class ArgumentParser + * + * Object for parsing command line strings into js objects. + * + * Inherited from [[ActionContainer]] + **/ +'use strict'; + +var util = require('util'); +var format = require('util').format; +var Path = require('path'); +var sprintf = require('sprintf-js').sprintf; + +// Constants +var c = require('./const'); + +var $$ = require('./utils'); + +var ActionContainer = require('./action_container'); + +// Errors +var argumentErrorHelper = require('./argument/error'); + +var HelpFormatter = require('./help/formatter'); + +var Namespace = require('./namespace'); + + +/** + * new ArgumentParser(options) + * + * Create a new ArgumentParser object. + * + * ##### Options: + * - `prog` The name of the program (default: Path.basename(process.argv[1])) + * - `usage` A usage message (default: auto-generated from arguments) + * - `description` A description of what the program does + * - `epilog` Text following the argument descriptions + * - `parents` Parsers whose arguments should be copied into this one + * - `formatterClass` HelpFormatter class for printing help messages + * - `prefixChars` Characters that prefix optional arguments + * - `fromfilePrefixChars` Characters that prefix files containing additional arguments + * - `argumentDefault` The default value for all arguments + * - `addHelp` Add a -h/-help option + * - `conflictHandler` Specifies how to handle conflicting argument names + * - `debug` Enable debug mode. Argument errors throw exception in + * debug mode and process.exit in normal. Used for development and + * testing (default: false) + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#argumentparser-objects + **/ +function ArgumentParser(options) { + if (!(this instanceof ArgumentParser)) { + return new ArgumentParser(options); + } + var self = this; + options = options || {}; + + options.description = (options.description || null); + options.argumentDefault = (options.argumentDefault || null); + options.prefixChars = (options.prefixChars || '-'); + options.conflictHandler = (options.conflictHandler || 'error'); + ActionContainer.call(this, options); + + options.addHelp = typeof options.addHelp === 'undefined' || !!options.addHelp; + options.parents = options.parents || []; + // default program name + options.prog = (options.prog || Path.basename(process.argv[1])); + this.prog = options.prog; + this.usage = options.usage; + this.epilog = options.epilog; + this.version = options.version; + + this.debug = (options.debug === true); + + this.formatterClass = (options.formatterClass || HelpFormatter); + this.fromfilePrefixChars = options.fromfilePrefixChars || null; + this._positionals = this.addArgumentGroup({ title: 'Positional arguments' }); + this._optionals = this.addArgumentGroup({ title: 'Optional arguments' }); + this._subparsers = null; + + // register types + function FUNCTION_IDENTITY(o) { + return o; + } + this.register('type', 'auto', FUNCTION_IDENTITY); + this.register('type', null, FUNCTION_IDENTITY); + this.register('type', 'int', function (x) { + var result = parseInt(x, 10); + if (isNaN(result)) { + throw new Error(x + ' is not a valid integer.'); + } + return result; + }); + this.register('type', 'float', function (x) { + var result = parseFloat(x); + if (isNaN(result)) { + throw new Error(x + ' is not a valid float.'); + } + return result; + }); + this.register('type', 'string', function (x) { + return '' + x; + }); + + // add help and version arguments if necessary + var defaultPrefix = (this.prefixChars.indexOf('-') > -1) ? '-' : this.prefixChars[0]; + if (options.addHelp) { + this.addArgument( + [ defaultPrefix + 'h', defaultPrefix + defaultPrefix + 'help' ], + { + action: 'help', + defaultValue: c.SUPPRESS, + help: 'Show this help message and exit.' + } + ); + } + if (typeof this.version !== 'undefined') { + this.addArgument( + [ defaultPrefix + 'v', defaultPrefix + defaultPrefix + 'version' ], + { + action: 'version', + version: this.version, + defaultValue: c.SUPPRESS, + help: "Show program's version number and exit." + } + ); + } + + // add parent arguments and defaults + options.parents.forEach(function (parent) { + self._addContainerActions(parent); + if (typeof parent._defaults !== 'undefined') { + for (var defaultKey in parent._defaults) { + if (parent._defaults.hasOwnProperty(defaultKey)) { + self._defaults[defaultKey] = parent._defaults[defaultKey]; + } + } + } + }); +} + +util.inherits(ArgumentParser, ActionContainer); + +/** + * ArgumentParser#addSubparsers(options) -> [[ActionSubparsers]] + * - options (object): hash of options see [[ActionSubparsers.new]] + * + * See also [subcommands][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#sub-commands + **/ +ArgumentParser.prototype.addSubparsers = function (options) { + if (this._subparsers) { + this.error('Cannot have multiple subparser arguments.'); + } + + options = options || {}; + options.debug = (this.debug === true); + options.optionStrings = []; + options.parserClass = (options.parserClass || ArgumentParser); + + + if (!!options.title || !!options.description) { + + this._subparsers = this.addArgumentGroup({ + title: (options.title || 'subcommands'), + description: options.description + }); + delete options.title; + delete options.description; + + } else { + this._subparsers = this._positionals; + } + + // prog defaults to the usage message of this parser, skipping + // optional arguments and with no "usage:" prefix + if (!options.prog) { + var formatter = this._getFormatter(); + var positionals = this._getPositionalActions(); + var groups = this._mutuallyExclusiveGroups; + formatter.addUsage(this.usage, positionals, groups, ''); + options.prog = formatter.formatHelp().trim(); + } + + // create the parsers action and add it to the positionals list + var ParsersClass = this._popActionClass(options, 'parsers'); + var action = new ParsersClass(options); + this._subparsers._addAction(action); + + // return the created parsers action + return action; +}; + +ArgumentParser.prototype._addAction = function (action) { + if (action.isOptional()) { + this._optionals._addAction(action); + } else { + this._positionals._addAction(action); + } + return action; +}; + +ArgumentParser.prototype._getOptionalActions = function () { + return this._actions.filter(function (action) { + return action.isOptional(); + }); +}; + +ArgumentParser.prototype._getPositionalActions = function () { + return this._actions.filter(function (action) { + return action.isPositional(); + }); +}; + + +/** + * ArgumentParser#parseArgs(args, namespace) -> Namespace|Object + * - args (array): input elements + * - namespace (Namespace|Object): result object + * + * Parsed args and throws error if some arguments are not recognized + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#the-parse-args-method + **/ +ArgumentParser.prototype.parseArgs = function (args, namespace) { + var argv; + var result = this.parseKnownArgs(args, namespace); + + args = result[0]; + argv = result[1]; + if (argv && argv.length > 0) { + this.error( + format('Unrecognized arguments: %s.', argv.join(' ')) + ); + } + return args; +}; + +/** + * ArgumentParser#parseKnownArgs(args, namespace) -> array + * - args (array): input options + * - namespace (Namespace|Object): result object + * + * Parse known arguments and return tuple of result object + * and unknown args + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#partial-parsing + **/ +ArgumentParser.prototype.parseKnownArgs = function (args, namespace) { + var self = this; + + // args default to the system args + args = args || process.argv.slice(2); + + // default Namespace built from parser defaults + namespace = namespace || new Namespace(); + + self._actions.forEach(function (action) { + if (action.dest !== c.SUPPRESS) { + if (!$$.has(namespace, action.dest)) { + if (action.defaultValue !== c.SUPPRESS) { + var defaultValue = action.defaultValue; + if (typeof action.defaultValue === 'string') { + defaultValue = self._getValue(action, defaultValue); + } + namespace[action.dest] = defaultValue; + } + } + } + }); + + Object.keys(self._defaults).forEach(function (dest) { + namespace[dest] = self._defaults[dest]; + }); + + // parse the arguments and exit if there are any errors + try { + var res = this._parseKnownArgs(args, namespace); + + namespace = res[0]; + args = res[1]; + if ($$.has(namespace, c._UNRECOGNIZED_ARGS_ATTR)) { + args = $$.arrayUnion(args, namespace[c._UNRECOGNIZED_ARGS_ATTR]); + delete namespace[c._UNRECOGNIZED_ARGS_ATTR]; + } + return [ namespace, args ]; + } catch (e) { + this.error(e); + } +}; + +ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) { + var self = this; + + var extras = []; + + // replace arg strings that are file references + if (this.fromfilePrefixChars !== null) { + argStrings = this._readArgsFromFiles(argStrings); + } + // map all mutually exclusive arguments to the other arguments + // they can't occur with + // Python has 'conflicts = action_conflicts.setdefault(mutex_action, [])' + // though I can't conceive of a way in which an action could be a member + // of two different mutually exclusive groups. + + function actionHash(action) { + // some sort of hashable key for this action + // action itself cannot be a key in actionConflicts + // I think getName() (join of optionStrings) is unique enough + return action.getName(); + } + + var conflicts, key; + var actionConflicts = {}; + + this._mutuallyExclusiveGroups.forEach(function (mutexGroup) { + mutexGroup._groupActions.forEach(function (mutexAction, i, groupActions) { + key = actionHash(mutexAction); + if (!$$.has(actionConflicts, key)) { + actionConflicts[key] = []; + } + conflicts = actionConflicts[key]; + conflicts.push.apply(conflicts, groupActions.slice(0, i)); + conflicts.push.apply(conflicts, groupActions.slice(i + 1)); + }); + }); + + // find all option indices, and determine the arg_string_pattern + // which has an 'O' if there is an option at an index, + // an 'A' if there is an argument, or a '-' if there is a '--' + var optionStringIndices = {}; + + var argStringPatternParts = []; + + argStrings.forEach(function (argString, argStringIndex) { + if (argString === '--') { + argStringPatternParts.push('-'); + while (argStringIndex < argStrings.length) { + argStringPatternParts.push('A'); + argStringIndex++; + } + } else { + // otherwise, add the arg to the arg strings + // and note the index if it was an option + var pattern; + var optionTuple = self._parseOptional(argString); + if (!optionTuple) { + pattern = 'A'; + } else { + optionStringIndices[argStringIndex] = optionTuple; + pattern = 'O'; + } + argStringPatternParts.push(pattern); + } + }); + var argStringsPattern = argStringPatternParts.join(''); + + var seenActions = []; + var seenNonDefaultActions = []; + + + function takeAction(action, argumentStrings, optionString) { + seenActions.push(action); + var argumentValues = self._getValues(action, argumentStrings); + + // error if this argument is not allowed with other previously + // seen arguments, assuming that actions that use the default + // value don't really count as "present" + if (argumentValues !== action.defaultValue) { + seenNonDefaultActions.push(action); + if (actionConflicts[actionHash(action)]) { + actionConflicts[actionHash(action)].forEach(function (actionConflict) { + if (seenNonDefaultActions.indexOf(actionConflict) >= 0) { + throw argumentErrorHelper( + action, + format('Not allowed with argument "%s".', actionConflict.getName()) + ); + } + }); + } + } + + if (argumentValues !== c.SUPPRESS) { + action.call(self, namespace, argumentValues, optionString); + } + } + + function consumeOptional(startIndex) { + // get the optional identified at this index + var optionTuple = optionStringIndices[startIndex]; + var action = optionTuple[0]; + var optionString = optionTuple[1]; + var explicitArg = optionTuple[2]; + + // identify additional optionals in the same arg string + // (e.g. -xyz is the same as -x -y -z if no args are required) + var actionTuples = []; + + var args, argCount, start, stop; + + for (;;) { + if (!action) { + extras.push(argStrings[startIndex]); + return startIndex + 1; + } + if (explicitArg) { + argCount = self._matchArgument(action, 'A'); + + // if the action is a single-dash option and takes no + // arguments, try to parse more single-dash options out + // of the tail of the option string + var chars = self.prefixChars; + if (argCount === 0 && chars.indexOf(optionString[1]) < 0) { + actionTuples.push([ action, [], optionString ]); + optionString = optionString[0] + explicitArg[0]; + var newExplicitArg = explicitArg.slice(1) || null; + var optionalsMap = self._optionStringActions; + + if (Object.keys(optionalsMap).indexOf(optionString) >= 0) { + action = optionalsMap[optionString]; + explicitArg = newExplicitArg; + } else { + throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg)); + } + } else if (argCount === 1) { + // if the action expect exactly one argument, we've + // successfully matched the option; exit the loop + stop = startIndex + 1; + args = [ explicitArg ]; + actionTuples.push([ action, args, optionString ]); + break; + } else { + // error if a double-dash option did not use the + // explicit argument + throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg)); + } + } else { + // if there is no explicit argument, try to match the + // optional's string arguments with the following strings + // if successful, exit the loop + + start = startIndex + 1; + var selectedPatterns = argStringsPattern.substr(start); + + argCount = self._matchArgument(action, selectedPatterns); + stop = start + argCount; + + + args = argStrings.slice(start, stop); + + actionTuples.push([ action, args, optionString ]); + break; + } + + } + + // add the Optional to the list and return the index at which + // the Optional's string args stopped + if (actionTuples.length < 1) { + throw new Error('length should be > 0'); + } + for (var i = 0; i < actionTuples.length; i++) { + takeAction.apply(self, actionTuples[i]); + } + return stop; + } + + // the list of Positionals left to be parsed; this is modified + // by consume_positionals() + var positionals = self._getPositionalActions(); + + function consumePositionals(startIndex) { + // match as many Positionals as possible + var selectedPattern = argStringsPattern.substr(startIndex); + var argCounts = self._matchArgumentsPartial(positionals, selectedPattern); + + // slice off the appropriate arg strings for each Positional + // and add the Positional and its args to the list + for (var i = 0; i < positionals.length; i++) { + var action = positionals[i]; + var argCount = argCounts[i]; + if (typeof argCount === 'undefined') { + continue; + } + var args = argStrings.slice(startIndex, startIndex + argCount); + + startIndex += argCount; + takeAction(action, args); + } + + // slice off the Positionals that we just parsed and return the + // index at which the Positionals' string args stopped + positionals = positionals.slice(argCounts.length); + return startIndex; + } + + // consume Positionals and Optionals alternately, until we have + // passed the last option string + var startIndex = 0; + var position; + + var maxOptionStringIndex = -1; + + Object.keys(optionStringIndices).forEach(function (position) { + maxOptionStringIndex = Math.max(maxOptionStringIndex, parseInt(position, 10)); + }); + + var positionalsEndIndex, nextOptionStringIndex; + + while (startIndex <= maxOptionStringIndex) { + // consume any Positionals preceding the next option + nextOptionStringIndex = null; + for (position in optionStringIndices) { + if (!optionStringIndices.hasOwnProperty(position)) { continue; } + + position = parseInt(position, 10); + if (position >= startIndex) { + if (nextOptionStringIndex !== null) { + nextOptionStringIndex = Math.min(nextOptionStringIndex, position); + } else { + nextOptionStringIndex = position; + } + } + } + + if (startIndex !== nextOptionStringIndex) { + positionalsEndIndex = consumePositionals(startIndex); + // only try to parse the next optional if we didn't consume + // the option string during the positionals parsing + if (positionalsEndIndex > startIndex) { + startIndex = positionalsEndIndex; + continue; + } else { + startIndex = positionalsEndIndex; + } + } + + // if we consumed all the positionals we could and we're not + // at the index of an option string, there were extra arguments + if (!optionStringIndices[startIndex]) { + var strings = argStrings.slice(startIndex, nextOptionStringIndex); + extras = extras.concat(strings); + startIndex = nextOptionStringIndex; + } + // consume the next optional and any arguments for it + startIndex = consumeOptional(startIndex); + } + + // consume any positionals following the last Optional + var stopIndex = consumePositionals(startIndex); + + // if we didn't consume all the argument strings, there were extras + extras = extras.concat(argStrings.slice(stopIndex)); + + // if we didn't use all the Positional objects, there were too few + // arg strings supplied. + if (positionals.length > 0) { + self.error('too few arguments'); + } + + // make sure all required actions were present + self._actions.forEach(function (action) { + if (action.required) { + if (seenActions.indexOf(action) < 0) { + self.error(format('Argument "%s" is required', action.getName())); + } + } + }); + + // make sure all required groups have one option present + var actionUsed = false; + self._mutuallyExclusiveGroups.forEach(function (group) { + if (group.required) { + actionUsed = group._groupActions.some(function (action) { + return seenNonDefaultActions.indexOf(action) !== -1; + }); + + // if no actions were used, report the error + if (!actionUsed) { + var names = []; + group._groupActions.forEach(function (action) { + if (action.help !== c.SUPPRESS) { + names.push(action.getName()); + } + }); + names = names.join(' '); + var msg = 'one of the arguments ' + names + ' is required'; + self.error(msg); + } + } + }); + + // return the updated namespace and the extra arguments + return [ namespace, extras ]; +}; + +ArgumentParser.prototype._readArgsFromFiles = function (argStrings) { + // expand arguments referencing files + var self = this; + var fs = require('fs'); + var newArgStrings = []; + argStrings.forEach(function (argString) { + if (self.fromfilePrefixChars.indexOf(argString[0]) < 0) { + // for regular arguments, just add them back into the list + newArgStrings.push(argString); + } else { + // replace arguments referencing files with the file content + try { + var argstrs = []; + var filename = argString.slice(1); + var content = fs.readFileSync(filename, 'utf8'); + content = content.trim().split('\n'); + content.forEach(function (argLine) { + self.convertArgLineToArgs(argLine).forEach(function (arg) { + argstrs.push(arg); + }); + argstrs = self._readArgsFromFiles(argstrs); + }); + newArgStrings.push.apply(newArgStrings, argstrs); + } catch (error) { + return self.error(error.message); + } + } + }); + return newArgStrings; +}; + +ArgumentParser.prototype.convertArgLineToArgs = function (argLine) { + return [ argLine ]; +}; + +ArgumentParser.prototype._matchArgument = function (action, regexpArgStrings) { + + // match the pattern for this action to the arg strings + var regexpNargs = new RegExp('^' + this._getNargsPattern(action)); + var matches = regexpArgStrings.match(regexpNargs); + var message; + + // throw an exception if we weren't able to find a match + if (!matches) { + switch (action.nargs) { + /*eslint-disable no-undefined*/ + case undefined: + case null: + message = 'Expected one argument.'; + break; + case c.OPTIONAL: + message = 'Expected at most one argument.'; + break; + case c.ONE_OR_MORE: + message = 'Expected at least one argument.'; + break; + default: + message = 'Expected %s argument(s)'; + } + + throw argumentErrorHelper( + action, + format(message, action.nargs) + ); + } + // return the number of arguments matched + return matches[1].length; +}; + +ArgumentParser.prototype._matchArgumentsPartial = function (actions, regexpArgStrings) { + // progressively shorten the actions list by slicing off the + // final actions until we find a match + var self = this; + var result = []; + var actionSlice, pattern, matches; + var i, j; + + function getLength(string) { + return string.length; + } + + for (i = actions.length; i > 0; i--) { + pattern = ''; + actionSlice = actions.slice(0, i); + for (j = 0; j < actionSlice.length; j++) { + pattern += self._getNargsPattern(actionSlice[j]); + } + + pattern = new RegExp('^' + pattern); + matches = regexpArgStrings.match(pattern); + + if (matches && matches.length > 0) { + // need only groups + matches = matches.splice(1); + result = result.concat(matches.map(getLength)); + break; + } + } + + // return the list of arg string counts + return result; +}; + +ArgumentParser.prototype._parseOptional = function (argString) { + var action, optionString, argExplicit, optionTuples; + + // if it's an empty string, it was meant to be a positional + if (!argString) { + return null; + } + + // if it doesn't start with a prefix, it was meant to be positional + if (this.prefixChars.indexOf(argString[0]) < 0) { + return null; + } + + // if the option string is present in the parser, return the action + if (this._optionStringActions[argString]) { + return [ this._optionStringActions[argString], argString, null ]; + } + + // if it's just a single character, it was meant to be positional + if (argString.length === 1) { + return null; + } + + // if the option string before the "=" is present, return the action + if (argString.indexOf('=') >= 0) { + optionString = argString.split('=', 1)[0]; + argExplicit = argString.slice(optionString.length + 1); + + if (this._optionStringActions[optionString]) { + action = this._optionStringActions[optionString]; + return [ action, optionString, argExplicit ]; + } + } + + // search through all possible prefixes of the option string + // and all actions in the parser for possible interpretations + optionTuples = this._getOptionTuples(argString); + + // if multiple actions match, the option string was ambiguous + if (optionTuples.length > 1) { + var optionStrings = optionTuples.map(function (optionTuple) { + return optionTuple[1]; + }); + this.error(format( + 'Ambiguous option: "%s" could match %s.', + argString, optionStrings.join(', ') + )); + // if exactly one action matched, this segmentation is good, + // so return the parsed action + } else if (optionTuples.length === 1) { + return optionTuples[0]; + } + + // if it was not found as an option, but it looks like a negative + // number, it was meant to be positional + // unless there are negative-number-like options + if (argString.match(this._regexpNegativeNumber)) { + if (!this._hasNegativeNumberOptionals.some(Boolean)) { + return null; + } + } + // if it contains a space, it was meant to be a positional + if (argString.search(' ') >= 0) { + return null; + } + + // it was meant to be an optional but there is no such option + // in this parser (though it might be a valid option in a subparser) + return [ null, argString, null ]; +}; + +ArgumentParser.prototype._getOptionTuples = function (optionString) { + var result = []; + var chars = this.prefixChars; + var optionPrefix; + var argExplicit; + var action; + var actionOptionString; + + // option strings starting with two prefix characters are only split at + // the '=' + if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) >= 0) { + if (optionString.indexOf('=') >= 0) { + var optionStringSplit = optionString.split('=', 1); + + optionPrefix = optionStringSplit[0]; + argExplicit = optionStringSplit[1]; + } else { + optionPrefix = optionString; + argExplicit = null; + } + + for (actionOptionString in this._optionStringActions) { + if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) { + action = this._optionStringActions[actionOptionString]; + result.push([ action, actionOptionString, argExplicit ]); + } + } + + // single character options can be concatenated with their arguments + // but multiple character options always have to have their argument + // separate + } else if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) < 0) { + optionPrefix = optionString; + argExplicit = null; + var optionPrefixShort = optionString.substr(0, 2); + var argExplicitShort = optionString.substr(2); + + for (actionOptionString in this._optionStringActions) { + if (!$$.has(this._optionStringActions, actionOptionString)) continue; + + action = this._optionStringActions[actionOptionString]; + if (actionOptionString === optionPrefixShort) { + result.push([ action, actionOptionString, argExplicitShort ]); + } else if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) { + result.push([ action, actionOptionString, argExplicit ]); + } + } + + // shouldn't ever get here + } else { + throw new Error(format('Unexpected option string: %s.', optionString)); + } + // return the collected option tuples + return result; +}; + +ArgumentParser.prototype._getNargsPattern = function (action) { + // in all examples below, we have to allow for '--' args + // which are represented as '-' in the pattern + var regexpNargs; + + switch (action.nargs) { + // the default (null) is assumed to be a single argument + case undefined: + case null: + regexpNargs = '(-*A-*)'; + break; + // allow zero or more arguments + case c.OPTIONAL: + regexpNargs = '(-*A?-*)'; + break; + // allow zero or more arguments + case c.ZERO_OR_MORE: + regexpNargs = '(-*[A-]*)'; + break; + // allow one or more arguments + case c.ONE_OR_MORE: + regexpNargs = '(-*A[A-]*)'; + break; + // allow any number of options or arguments + case c.REMAINDER: + regexpNargs = '([-AO]*)'; + break; + // allow one argument followed by any number of options or arguments + case c.PARSER: + regexpNargs = '(-*A[-AO]*)'; + break; + // all others should be integers + default: + regexpNargs = '(-*' + $$.repeat('-*A', action.nargs) + '-*)'; + } + + // if this is an optional action, -- is not allowed + if (action.isOptional()) { + regexpNargs = regexpNargs.replace(/-\*/g, ''); + regexpNargs = regexpNargs.replace(/-/g, ''); + } + + // return the pattern + return regexpNargs; +}; + +// +// Value conversion methods +// + +ArgumentParser.prototype._getValues = function (action, argStrings) { + var self = this; + + // for everything but PARSER args, strip out '--' + if (action.nargs !== c.PARSER && action.nargs !== c.REMAINDER) { + argStrings = argStrings.filter(function (arrayElement) { + return arrayElement !== '--'; + }); + } + + var value, argString; + + // optional argument produces a default when not present + if (argStrings.length === 0 && action.nargs === c.OPTIONAL) { + + value = (action.isOptional()) ? action.constant : action.defaultValue; + + if (typeof (value) === 'string') { + value = this._getValue(action, value); + this._checkValue(action, value); + } + + // when nargs='*' on a positional, if there were no command-line + // args, use the default if it is anything other than None + } else if (argStrings.length === 0 && action.nargs === c.ZERO_OR_MORE && + action.optionStrings.length === 0) { + + value = (action.defaultValue || argStrings); + this._checkValue(action, value); + + // single argument or optional argument produces a single value + } else if (argStrings.length === 1 && + (!action.nargs || action.nargs === c.OPTIONAL)) { + + argString = argStrings[0]; + value = this._getValue(action, argString); + this._checkValue(action, value); + + // REMAINDER arguments convert all values, checking none + } else if (action.nargs === c.REMAINDER) { + value = argStrings.map(function (v) { + return self._getValue(action, v); + }); + + // PARSER arguments convert all values, but check only the first + } else if (action.nargs === c.PARSER) { + value = argStrings.map(function (v) { + return self._getValue(action, v); + }); + this._checkValue(action, value[0]); + + // all other types of nargs produce a list + } else { + value = argStrings.map(function (v) { + return self._getValue(action, v); + }); + value.forEach(function (v) { + self._checkValue(action, v); + }); + } + + // return the converted value + return value; +}; + +ArgumentParser.prototype._getValue = function (action, argString) { + var result; + + var typeFunction = this._registryGet('type', action.type, action.type); + if (typeof typeFunction !== 'function') { + var message = format('%s is not callable', typeFunction); + throw argumentErrorHelper(action, message); + } + + // convert the value to the appropriate type + try { + result = typeFunction(argString); + + // ArgumentTypeErrors indicate errors + // If action.type is not a registered string, it is a function + // Try to deduce its name for inclusion in the error message + // Failing that, include the error message it raised. + } catch (e) { + var name = null; + if (typeof action.type === 'string') { + name = action.type; + } else { + name = action.type.name || action.type.displayName || ''; + } + var msg = format('Invalid %s value: %s', name, argString); + if (name === '') { msg += '\n' + e.message; } + throw argumentErrorHelper(action, msg); + } + // return the converted value + return result; +}; + +ArgumentParser.prototype._checkValue = function (action, value) { + // converted value must be one of the choices (if specified) + var choices = action.choices; + if (choices) { + // choise for argument can by array or string + if ((typeof choices === 'string' || Array.isArray(choices)) && + choices.indexOf(value) !== -1) { + return; + } + // choise for subparsers can by only hash + if (typeof choices === 'object' && !Array.isArray(choices) && choices[value]) { + return; + } + + if (typeof choices === 'string') { + choices = choices.split('').join(', '); + } else if (Array.isArray(choices)) { + choices = choices.join(', '); + } else { + choices = Object.keys(choices).join(', '); + } + var message = format('Invalid choice: %s (choose from [%s])', value, choices); + throw argumentErrorHelper(action, message); + } +}; + +// +// Help formatting methods +// + +/** + * ArgumentParser#formatUsage -> string + * + * Return usage string + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#printing-help + **/ +ArgumentParser.prototype.formatUsage = function () { + var formatter = this._getFormatter(); + formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups); + return formatter.formatHelp(); +}; + +/** + * ArgumentParser#formatHelp -> string + * + * Return help + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#printing-help + **/ +ArgumentParser.prototype.formatHelp = function () { + var formatter = this._getFormatter(); + + // usage + formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups); + + // description + formatter.addText(this.description); + + // positionals, optionals and user-defined groups + this._actionGroups.forEach(function (actionGroup) { + formatter.startSection(actionGroup.title); + formatter.addText(actionGroup.description); + formatter.addArguments(actionGroup._groupActions); + formatter.endSection(); + }); + + // epilog + formatter.addText(this.epilog); + + // determine help from format above + return formatter.formatHelp(); +}; + +ArgumentParser.prototype._getFormatter = function () { + var FormatterClass = this.formatterClass; + var formatter = new FormatterClass({ prog: this.prog }); + return formatter; +}; + +// +// Print functions +// + +/** + * ArgumentParser#printUsage() -> Void + * + * Print usage + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#printing-help + **/ +ArgumentParser.prototype.printUsage = function () { + this._printMessage(this.formatUsage()); +}; + +/** + * ArgumentParser#printHelp() -> Void + * + * Print help + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#printing-help + **/ +ArgumentParser.prototype.printHelp = function () { + this._printMessage(this.formatHelp()); +}; + +ArgumentParser.prototype._printMessage = function (message, stream) { + if (!stream) { + stream = process.stdout; + } + if (message) { + stream.write('' + message); + } +}; + +// +// Exit functions +// + +/** + * ArgumentParser#exit(status=0, message) -> Void + * - status (int): exit status + * - message (string): message + * + * Print message in stderr/stdout and exit program + **/ +ArgumentParser.prototype.exit = function (status, message) { + if (message) { + if (status === 0) { + this._printMessage(message); + } else { + this._printMessage(message, process.stderr); + } + } + + process.exit(status); +}; + +/** + * ArgumentParser#error(message) -> Void + * - err (Error|string): message + * + * Error method Prints a usage message incorporating the message to stderr and + * exits. If you override this in a subclass, + * it should not return -- it should + * either exit or throw an exception. + * + **/ +ArgumentParser.prototype.error = function (err) { + var message; + if (err instanceof Error) { + if (this.debug === true) { + throw err; + } + message = err.message; + } else { + message = err; + } + var msg = format('%s: error: %s', this.prog, message) + c.EOL; + + if (this.debug === true) { + throw new Error(msg); + } + + this.printUsage(process.stderr); + + return this.exit(2, msg); +}; + +module.exports = ArgumentParser; diff --git a/WebServer/node_modules/argparse/lib/const.js b/WebServer/node_modules/argparse/lib/const.js new file mode 100644 index 000000000..b1fd4ced4 --- /dev/null +++ b/WebServer/node_modules/argparse/lib/const.js @@ -0,0 +1,21 @@ +// +// Constants +// + +'use strict'; + +module.exports.EOL = '\n'; + +module.exports.SUPPRESS = '==SUPPRESS=='; + +module.exports.OPTIONAL = '?'; + +module.exports.ZERO_OR_MORE = '*'; + +module.exports.ONE_OR_MORE = '+'; + +module.exports.PARSER = 'A...'; + +module.exports.REMAINDER = '...'; + +module.exports._UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args'; diff --git a/WebServer/node_modules/argparse/lib/help/added_formatters.js b/WebServer/node_modules/argparse/lib/help/added_formatters.js new file mode 100644 index 000000000..f8e42998e --- /dev/null +++ b/WebServer/node_modules/argparse/lib/help/added_formatters.js @@ -0,0 +1,87 @@ +'use strict'; + +var util = require('util'); + +// Constants +var c = require('../const'); + +var $$ = require('../utils'); +var HelpFormatter = require('./formatter.js'); + +/** + * new RawDescriptionHelpFormatter(options) + * new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...}) + * + * Help message formatter which adds default values to argument help. + * + * Only the name of this class is considered a public API. All the methods + * provided by the class are considered an implementation detail. + **/ + +function ArgumentDefaultsHelpFormatter(options) { + HelpFormatter.call(this, options); +} + +util.inherits(ArgumentDefaultsHelpFormatter, HelpFormatter); + +ArgumentDefaultsHelpFormatter.prototype._getHelpString = function (action) { + var help = action.help; + if (action.help.indexOf('%(defaultValue)s') === -1) { + if (action.defaultValue !== c.SUPPRESS) { + var defaulting_nargs = [ c.OPTIONAL, c.ZERO_OR_MORE ]; + if (action.isOptional() || (defaulting_nargs.indexOf(action.nargs) >= 0)) { + help += ' (default: %(defaultValue)s)'; + } + } + } + return help; +}; + +module.exports.ArgumentDefaultsHelpFormatter = ArgumentDefaultsHelpFormatter; + +/** + * new RawDescriptionHelpFormatter(options) + * new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...}) + * + * Help message formatter which retains any formatting in descriptions. + * + * Only the name of this class is considered a public API. All the methods + * provided by the class are considered an implementation detail. + **/ + +function RawDescriptionHelpFormatter(options) { + HelpFormatter.call(this, options); +} + +util.inherits(RawDescriptionHelpFormatter, HelpFormatter); + +RawDescriptionHelpFormatter.prototype._fillText = function (text, width, indent) { + var lines = text.split('\n'); + lines = lines.map(function (line) { + return $$.trimEnd(indent + line); + }); + return lines.join('\n'); +}; +module.exports.RawDescriptionHelpFormatter = RawDescriptionHelpFormatter; + +/** + * new RawTextHelpFormatter(options) + * new ArgumentParser({formatterClass: argparse.RawTextHelpFormatter, ...}) + * + * Help message formatter which retains formatting of all help text. + * + * Only the name of this class is considered a public API. All the methods + * provided by the class are considered an implementation detail. + **/ + +function RawTextHelpFormatter(options) { + RawDescriptionHelpFormatter.call(this, options); +} + +util.inherits(RawTextHelpFormatter, RawDescriptionHelpFormatter); + +RawTextHelpFormatter.prototype._splitLines = function (text) { + return text.split('\n'); +}; + +module.exports.RawTextHelpFormatter = RawTextHelpFormatter; diff --git a/WebServer/node_modules/argparse/lib/help/formatter.js b/WebServer/node_modules/argparse/lib/help/formatter.js new file mode 100644 index 000000000..29036c14b --- /dev/null +++ b/WebServer/node_modules/argparse/lib/help/formatter.js @@ -0,0 +1,795 @@ +/** + * class HelpFormatter + * + * Formatter for generating usage messages and argument help strings. Only the + * name of this class is considered a public API. All the methods provided by + * the class are considered an implementation detail. + * + * Do not call in your code, use this class only for inherits your own forvatter + * + * ToDo add [additonal formatters][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#formatter-class + **/ +'use strict'; + +var sprintf = require('sprintf-js').sprintf; + +// Constants +var c = require('../const'); + +var $$ = require('../utils'); + + +/*:nodoc:* internal + * new Support(parent, heding) + * - parent (object): parent section + * - heading (string): header string + * + **/ +function Section(parent, heading) { + this._parent = parent; + this._heading = heading; + this._items = []; +} + +/*:nodoc:* internal + * Section#addItem(callback) -> Void + * - callback (array): tuple with function and args + * + * Add function for single element + **/ +Section.prototype.addItem = function (callback) { + this._items.push(callback); +}; + +/*:nodoc:* internal + * Section#formatHelp(formatter) -> string + * - formatter (HelpFormatter): current formatter + * + * Form help section string + * + **/ +Section.prototype.formatHelp = function (formatter) { + var itemHelp, heading; + + // format the indented section + if (this._parent) { + formatter._indent(); + } + + itemHelp = this._items.map(function (item) { + var obj, func, args; + + obj = formatter; + func = item[0]; + args = item[1]; + return func.apply(obj, args); + }); + itemHelp = formatter._joinParts(itemHelp); + + if (this._parent) { + formatter._dedent(); + } + + // return nothing if the section was empty + if (!itemHelp) { + return ''; + } + + // add the heading if the section was non-empty + heading = ''; + if (this._heading && this._heading !== c.SUPPRESS) { + var currentIndent = formatter.currentIndent; + heading = $$.repeat(' ', currentIndent) + this._heading + ':' + c.EOL; + } + + // join the section-initialize newline, the heading and the help + return formatter._joinParts([ c.EOL, heading, itemHelp, c.EOL ]); +}; + +/** + * new HelpFormatter(options) + * + * #### Options: + * - `prog`: program name + * - `indentIncriment`: indent step, default value 2 + * - `maxHelpPosition`: max help position, default value = 24 + * - `width`: line width + * + **/ +var HelpFormatter = module.exports = function HelpFormatter(options) { + options = options || {}; + + this._prog = options.prog; + + this._maxHelpPosition = options.maxHelpPosition || 24; + this._width = (options.width || ((process.env.COLUMNS || 80) - 2)); + + this._currentIndent = 0; + this._indentIncriment = options.indentIncriment || 2; + this._level = 0; + this._actionMaxLength = 0; + + this._rootSection = new Section(null); + this._currentSection = this._rootSection; + + this._whitespaceMatcher = new RegExp('\\s+', 'g'); + this._longBreakMatcher = new RegExp(c.EOL + c.EOL + c.EOL + '+', 'g'); +}; + +HelpFormatter.prototype._indent = function () { + this._currentIndent += this._indentIncriment; + this._level += 1; +}; + +HelpFormatter.prototype._dedent = function () { + this._currentIndent -= this._indentIncriment; + this._level -= 1; + if (this._currentIndent < 0) { + throw new Error('Indent decreased below 0.'); + } +}; + +HelpFormatter.prototype._addItem = function (func, args) { + this._currentSection.addItem([ func, args ]); +}; + +// +// Message building methods +// + +/** + * HelpFormatter#startSection(heading) -> Void + * - heading (string): header string + * + * Start new help section + * + * See alse [code example][1] + * + * ##### Example + * + * formatter.startSection(actionGroup.title); + * formatter.addText(actionGroup.description); + * formatter.addArguments(actionGroup._groupActions); + * formatter.endSection(); + * + **/ +HelpFormatter.prototype.startSection = function (heading) { + this._indent(); + var section = new Section(this._currentSection, heading); + var func = section.formatHelp.bind(section); + this._addItem(func, [ this ]); + this._currentSection = section; +}; + +/** + * HelpFormatter#endSection -> Void + * + * End help section + * + * ##### Example + * + * formatter.startSection(actionGroup.title); + * formatter.addText(actionGroup.description); + * formatter.addArguments(actionGroup._groupActions); + * formatter.endSection(); + **/ +HelpFormatter.prototype.endSection = function () { + this._currentSection = this._currentSection._parent; + this._dedent(); +}; + +/** + * HelpFormatter#addText(text) -> Void + * - text (string): plain text + * + * Add plain text into current section + * + * ##### Example + * + * formatter.startSection(actionGroup.title); + * formatter.addText(actionGroup.description); + * formatter.addArguments(actionGroup._groupActions); + * formatter.endSection(); + * + **/ +HelpFormatter.prototype.addText = function (text) { + if (text && text !== c.SUPPRESS) { + this._addItem(this._formatText, [ text ]); + } +}; + +/** + * HelpFormatter#addUsage(usage, actions, groups, prefix) -> Void + * - usage (string): usage text + * - actions (array): actions list + * - groups (array): groups list + * - prefix (string): usage prefix + * + * Add usage data into current section + * + * ##### Example + * + * formatter.addUsage(this.usage, this._actions, []); + * return formatter.formatHelp(); + * + **/ +HelpFormatter.prototype.addUsage = function (usage, actions, groups, prefix) { + if (usage !== c.SUPPRESS) { + this._addItem(this._formatUsage, [ usage, actions, groups, prefix ]); + } +}; + +/** + * HelpFormatter#addArgument(action) -> Void + * - action (object): action + * + * Add argument into current section + * + * Single variant of [[HelpFormatter#addArguments]] + **/ +HelpFormatter.prototype.addArgument = function (action) { + if (action.help !== c.SUPPRESS) { + var self = this; + + // find all invocations + var invocations = [ this._formatActionInvocation(action) ]; + var invocationLength = invocations[0].length; + + var actionLength; + + if (action._getSubactions) { + this._indent(); + action._getSubactions().forEach(function (subaction) { + + var invocationNew = self._formatActionInvocation(subaction); + invocations.push(invocationNew); + invocationLength = Math.max(invocationLength, invocationNew.length); + + }); + this._dedent(); + } + + // update the maximum item length + actionLength = invocationLength + this._currentIndent; + this._actionMaxLength = Math.max(this._actionMaxLength, actionLength); + + // add the item to the list + this._addItem(this._formatAction, [ action ]); + } +}; + +/** + * HelpFormatter#addArguments(actions) -> Void + * - actions (array): actions list + * + * Mass add arguments into current section + * + * ##### Example + * + * formatter.startSection(actionGroup.title); + * formatter.addText(actionGroup.description); + * formatter.addArguments(actionGroup._groupActions); + * formatter.endSection(); + * + **/ +HelpFormatter.prototype.addArguments = function (actions) { + var self = this; + actions.forEach(function (action) { + self.addArgument(action); + }); +}; + +// +// Help-formatting methods +// + +/** + * HelpFormatter#formatHelp -> string + * + * Format help + * + * ##### Example + * + * formatter.addText(this.epilog); + * return formatter.formatHelp(); + * + **/ +HelpFormatter.prototype.formatHelp = function () { + var help = this._rootSection.formatHelp(this); + if (help) { + help = help.replace(this._longBreakMatcher, c.EOL + c.EOL); + help = $$.trimChars(help, c.EOL) + c.EOL; + } + return help; +}; + +HelpFormatter.prototype._joinParts = function (partStrings) { + return partStrings.filter(function (part) { + return (part && part !== c.SUPPRESS); + }).join(''); +}; + +HelpFormatter.prototype._formatUsage = function (usage, actions, groups, prefix) { + if (!prefix && typeof prefix !== 'string') { + prefix = 'usage: '; + } + + actions = actions || []; + groups = groups || []; + + + // if usage is specified, use that + if (usage) { + usage = sprintf(usage, { prog: this._prog }); + + // if no optionals or positionals are available, usage is just prog + } else if (!usage && actions.length === 0) { + usage = this._prog; + + // if optionals and positionals are available, calculate usage + } else if (!usage) { + var prog = this._prog; + var optionals = []; + var positionals = []; + var actionUsage; + var textWidth; + + // split optionals from positionals + actions.forEach(function (action) { + if (action.isOptional()) { + optionals.push(action); + } else { + positionals.push(action); + } + }); + + // build full usage string + actionUsage = this._formatActionsUsage([].concat(optionals, positionals), groups); + usage = [ prog, actionUsage ].join(' '); + + // wrap the usage parts if it's too long + textWidth = this._width - this._currentIndent; + if ((prefix.length + usage.length) > textWidth) { + + // break usage into wrappable parts + var regexpPart = new RegExp('\\(.*?\\)+|\\[.*?\\]+|\\S+', 'g'); + var optionalUsage = this._formatActionsUsage(optionals, groups); + var positionalUsage = this._formatActionsUsage(positionals, groups); + + + var optionalParts = optionalUsage.match(regexpPart); + var positionalParts = positionalUsage.match(regexpPart) || []; + + if (optionalParts.join(' ') !== optionalUsage) { + throw new Error('assert "optionalParts.join(\' \') === optionalUsage"'); + } + if (positionalParts.join(' ') !== positionalUsage) { + throw new Error('assert "positionalParts.join(\' \') === positionalUsage"'); + } + + // helper for wrapping lines + /*eslint-disable func-style*/ // node 0.10 compat + var _getLines = function (parts, indent, prefix) { + var lines = []; + var line = []; + + var lineLength = prefix ? prefix.length - 1 : indent.length - 1; + + parts.forEach(function (part) { + if (lineLength + 1 + part.length > textWidth) { + lines.push(indent + line.join(' ')); + line = []; + lineLength = indent.length - 1; + } + line.push(part); + lineLength += part.length + 1; + }); + + if (line) { + lines.push(indent + line.join(' ')); + } + if (prefix) { + lines[0] = lines[0].substr(indent.length); + } + return lines; + }; + + var lines, indent, parts; + // if prog is short, follow it with optionals or positionals + if (prefix.length + prog.length <= 0.75 * textWidth) { + indent = $$.repeat(' ', (prefix.length + prog.length + 1)); + if (optionalParts) { + lines = [].concat( + _getLines([ prog ].concat(optionalParts), indent, prefix), + _getLines(positionalParts, indent) + ); + } else if (positionalParts) { + lines = _getLines([ prog ].concat(positionalParts), indent, prefix); + } else { + lines = [ prog ]; + } + + // if prog is long, put it on its own line + } else { + indent = $$.repeat(' ', prefix.length); + parts = optionalParts.concat(positionalParts); + lines = _getLines(parts, indent); + if (lines.length > 1) { + lines = [].concat( + _getLines(optionalParts, indent), + _getLines(positionalParts, indent) + ); + } + lines = [ prog ].concat(lines); + } + // join lines into usage + usage = lines.join(c.EOL); + } + } + + // prefix with 'usage:' + return prefix + usage + c.EOL + c.EOL; +}; + +HelpFormatter.prototype._formatActionsUsage = function (actions, groups) { + // find group indices and identify actions in groups + var groupActions = []; + var inserts = []; + var self = this; + + groups.forEach(function (group) { + var end; + var i; + + var start = actions.indexOf(group._groupActions[0]); + if (start >= 0) { + end = start + group._groupActions.length; + + //if (actions.slice(start, end) === group._groupActions) { + if ($$.arrayEqual(actions.slice(start, end), group._groupActions)) { + group._groupActions.forEach(function (action) { + groupActions.push(action); + }); + + if (!group.required) { + if (inserts[start]) { + inserts[start] += ' ['; + } else { + inserts[start] = '['; + } + inserts[end] = ']'; + } else { + if (inserts[start]) { + inserts[start] += ' ('; + } else { + inserts[start] = '('; + } + inserts[end] = ')'; + } + for (i = start + 1; i < end; i += 1) { + inserts[i] = '|'; + } + } + } + }); + + // collect all actions format strings + var parts = []; + + actions.forEach(function (action, actionIndex) { + var part; + var optionString; + var argsDefault; + var argsString; + + // suppressed arguments are marked with None + // remove | separators for suppressed arguments + if (action.help === c.SUPPRESS) { + parts.push(null); + if (inserts[actionIndex] === '|') { + inserts.splice(actionIndex, actionIndex); + } else if (inserts[actionIndex + 1] === '|') { + inserts.splice(actionIndex + 1, actionIndex + 1); + } + + // produce all arg strings + } else if (!action.isOptional()) { + part = self._formatArgs(action, action.dest); + + // if it's in a group, strip the outer [] + if (groupActions.indexOf(action) >= 0) { + if (part[0] === '[' && part[part.length - 1] === ']') { + part = part.slice(1, -1); + } + } + // add the action string to the list + parts.push(part); + + // produce the first way to invoke the option in brackets + } else { + optionString = action.optionStrings[0]; + + // if the Optional doesn't take a value, format is: -s or --long + if (action.nargs === 0) { + part = '' + optionString; + + // if the Optional takes a value, format is: -s ARGS or --long ARGS + } else { + argsDefault = action.dest.toUpperCase(); + argsString = self._formatArgs(action, argsDefault); + part = optionString + ' ' + argsString; + } + // make it look optional if it's not required or in a group + if (!action.required && groupActions.indexOf(action) < 0) { + part = '[' + part + ']'; + } + // add the action string to the list + parts.push(part); + } + }); + + // insert things at the necessary indices + for (var i = inserts.length - 1; i >= 0; --i) { + if (inserts[i] !== null) { + parts.splice(i, 0, inserts[i]); + } + } + + // join all the action items with spaces + var text = parts.filter(function (part) { + return !!part; + }).join(' '); + + // clean up separators for mutually exclusive groups + text = text.replace(/([\[(]) /g, '$1'); // remove spaces + text = text.replace(/ ([\])])/g, '$1'); + text = text.replace(/\[ *\]/g, ''); // remove empty groups + text = text.replace(/\( *\)/g, ''); + text = text.replace(/\(([^|]*)\)/g, '$1'); // remove () from single action groups + + text = text.trim(); + + // return the text + return text; +}; + +HelpFormatter.prototype._formatText = function (text) { + text = sprintf(text, { prog: this._prog }); + var textWidth = this._width - this._currentIndent; + var indentIncriment = $$.repeat(' ', this._currentIndent); + return this._fillText(text, textWidth, indentIncriment) + c.EOL + c.EOL; +}; + +HelpFormatter.prototype._formatAction = function (action) { + var self = this; + + var helpText; + var helpLines; + var parts; + var indentFirst; + + // determine the required width and the entry label + var helpPosition = Math.min(this._actionMaxLength + 2, this._maxHelpPosition); + var helpWidth = this._width - helpPosition; + var actionWidth = helpPosition - this._currentIndent - 2; + var actionHeader = this._formatActionInvocation(action); + + // no help; start on same line and add a final newline + if (!action.help) { + actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL; + + // short action name; start on the same line and pad two spaces + } else if (actionHeader.length <= actionWidth) { + actionHeader = $$.repeat(' ', this._currentIndent) + + actionHeader + + ' ' + + $$.repeat(' ', actionWidth - actionHeader.length); + indentFirst = 0; + + // long action name; start on the next line + } else { + actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL; + indentFirst = helpPosition; + } + + // collect the pieces of the action help + parts = [ actionHeader ]; + + // if there was help for the action, add lines of help text + if (action.help) { + helpText = this._expandHelp(action); + helpLines = this._splitLines(helpText, helpWidth); + parts.push($$.repeat(' ', indentFirst) + helpLines[0] + c.EOL); + helpLines.slice(1).forEach(function (line) { + parts.push($$.repeat(' ', helpPosition) + line + c.EOL); + }); + + // or add a newline if the description doesn't end with one + } else if (actionHeader.charAt(actionHeader.length - 1) !== c.EOL) { + parts.push(c.EOL); + } + // if there are any sub-actions, add their help as well + if (action._getSubactions) { + this._indent(); + action._getSubactions().forEach(function (subaction) { + parts.push(self._formatAction(subaction)); + }); + this._dedent(); + } + // return a single string + return this._joinParts(parts); +}; + +HelpFormatter.prototype._formatActionInvocation = function (action) { + if (!action.isOptional()) { + var format_func = this._metavarFormatter(action, action.dest); + var metavars = format_func(1); + return metavars[0]; + } + + var parts = []; + var argsDefault; + var argsString; + + // if the Optional doesn't take a value, format is: -s, --long + if (action.nargs === 0) { + parts = parts.concat(action.optionStrings); + + // if the Optional takes a value, format is: -s ARGS, --long ARGS + } else { + argsDefault = action.dest.toUpperCase(); + argsString = this._formatArgs(action, argsDefault); + action.optionStrings.forEach(function (optionString) { + parts.push(optionString + ' ' + argsString); + }); + } + return parts.join(', '); +}; + +HelpFormatter.prototype._metavarFormatter = function (action, metavarDefault) { + var result; + + if (action.metavar || action.metavar === '') { + result = action.metavar; + } else if (action.choices) { + var choices = action.choices; + + if (typeof choices === 'string') { + choices = choices.split('').join(', '); + } else if (Array.isArray(choices)) { + choices = choices.join(','); + } else { + choices = Object.keys(choices).join(','); + } + result = '{' + choices + '}'; + } else { + result = metavarDefault; + } + + return function (size) { + if (Array.isArray(result)) { + return result; + } + + var metavars = []; + for (var i = 0; i < size; i += 1) { + metavars.push(result); + } + return metavars; + }; +}; + +HelpFormatter.prototype._formatArgs = function (action, metavarDefault) { + var result; + var metavars; + + var buildMetavar = this._metavarFormatter(action, metavarDefault); + + switch (action.nargs) { + /*eslint-disable no-undefined*/ + case undefined: + case null: + metavars = buildMetavar(1); + result = '' + metavars[0]; + break; + case c.OPTIONAL: + metavars = buildMetavar(1); + result = '[' + metavars[0] + ']'; + break; + case c.ZERO_OR_MORE: + metavars = buildMetavar(2); + result = '[' + metavars[0] + ' [' + metavars[1] + ' ...]]'; + break; + case c.ONE_OR_MORE: + metavars = buildMetavar(2); + result = '' + metavars[0] + ' [' + metavars[1] + ' ...]'; + break; + case c.REMAINDER: + result = '...'; + break; + case c.PARSER: + metavars = buildMetavar(1); + result = metavars[0] + ' ...'; + break; + default: + metavars = buildMetavar(action.nargs); + result = metavars.join(' '); + } + return result; +}; + +HelpFormatter.prototype._expandHelp = function (action) { + var params = { prog: this._prog }; + + Object.keys(action).forEach(function (actionProperty) { + var actionValue = action[actionProperty]; + + if (actionValue !== c.SUPPRESS) { + params[actionProperty] = actionValue; + } + }); + + if (params.choices) { + if (typeof params.choices === 'string') { + params.choices = params.choices.split('').join(', '); + } else if (Array.isArray(params.choices)) { + params.choices = params.choices.join(', '); + } else { + params.choices = Object.keys(params.choices).join(', '); + } + } + + return sprintf(this._getHelpString(action), params); +}; + +HelpFormatter.prototype._splitLines = function (text, width) { + var lines = []; + var delimiters = [ ' ', '.', ',', '!', '?' ]; + var re = new RegExp('[' + delimiters.join('') + '][^' + delimiters.join('') + ']*$'); + + text = text.replace(/[\n\|\t]/g, ' '); + + text = text.trim(); + text = text.replace(this._whitespaceMatcher, ' '); + + // Wraps the single paragraph in text (a string) so every line + // is at most width characters long. + text.split(c.EOL).forEach(function (line) { + if (width >= line.length) { + lines.push(line); + return; + } + + var wrapStart = 0; + var wrapEnd = width; + var delimiterIndex = 0; + while (wrapEnd <= line.length) { + if (wrapEnd !== line.length && delimiters.indexOf(line[wrapEnd] < -1)) { + delimiterIndex = (re.exec(line.substring(wrapStart, wrapEnd)) || {}).index; + wrapEnd = wrapStart + delimiterIndex + 1; + } + lines.push(line.substring(wrapStart, wrapEnd)); + wrapStart = wrapEnd; + wrapEnd += width; + } + if (wrapStart < line.length) { + lines.push(line.substring(wrapStart, wrapEnd)); + } + }); + + return lines; +}; + +HelpFormatter.prototype._fillText = function (text, width, indent) { + var lines = this._splitLines(text, width); + lines = lines.map(function (line) { + return indent + line; + }); + return lines.join(c.EOL); +}; + +HelpFormatter.prototype._getHelpString = function (action) { + return action.help; +}; diff --git a/WebServer/node_modules/argparse/lib/namespace.js b/WebServer/node_modules/argparse/lib/namespace.js new file mode 100644 index 000000000..a860de9ec --- /dev/null +++ b/WebServer/node_modules/argparse/lib/namespace.js @@ -0,0 +1,76 @@ +/** + * class Namespace + * + * Simple object for storing attributes. Implements equality by attribute names + * and values, and provides a simple string representation. + * + * See also [original guide][1] + * + * [1]:http://docs.python.org/dev/library/argparse.html#the-namespace-object + **/ +'use strict'; + +var $$ = require('./utils'); + +/** + * new Namespace(options) + * - options(object): predefined propertis for result object + * + **/ +var Namespace = module.exports = function Namespace(options) { + $$.extend(this, options); +}; + +/** + * Namespace#isset(key) -> Boolean + * - key (string|number): property name + * + * Tells whenever `namespace` contains given `key` or not. + **/ +Namespace.prototype.isset = function (key) { + return $$.has(this, key); +}; + +/** + * Namespace#set(key, value) -> self + * -key (string|number|object): propery name + * -value (mixed): new property value + * + * Set the property named key with value. + * If key object then set all key properties to namespace object + **/ +Namespace.prototype.set = function (key, value) { + if (typeof (key) === 'object') { + $$.extend(this, key); + } else { + this[key] = value; + } + return this; +}; + +/** + * Namespace#get(key, defaultValue) -> mixed + * - key (string|number): property name + * - defaultValue (mixed): default value + * + * Return the property key or defaulValue if not set + **/ +Namespace.prototype.get = function (key, defaultValue) { + return !this[key] ? defaultValue : this[key]; +}; + +/** + * Namespace#unset(key, defaultValue) -> mixed + * - key (string|number): property name + * - defaultValue (mixed): default value + * + * Return data[key](and delete it) or defaultValue + **/ +Namespace.prototype.unset = function (key, defaultValue) { + var value = this[key]; + if (value !== null) { + delete this[key]; + return value; + } + return defaultValue; +}; diff --git a/WebServer/node_modules/argparse/lib/utils.js b/WebServer/node_modules/argparse/lib/utils.js new file mode 100644 index 000000000..4a9cf3edb --- /dev/null +++ b/WebServer/node_modules/argparse/lib/utils.js @@ -0,0 +1,57 @@ +'use strict'; + +exports.repeat = function (str, num) { + var result = ''; + for (var i = 0; i < num; i++) { result += str; } + return result; +}; + +exports.arrayEqual = function (a, b) { + if (a.length !== b.length) { return false; } + for (var i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { return false; } + } + return true; +}; + +exports.trimChars = function (str, chars) { + var start = 0; + var end = str.length - 1; + while (chars.indexOf(str.charAt(start)) >= 0) { start++; } + while (chars.indexOf(str.charAt(end)) >= 0) { end--; } + return str.slice(start, end + 1); +}; + +exports.capitalize = function (str) { + return str.charAt(0).toUpperCase() + str.slice(1); +}; + +exports.arrayUnion = function () { + var result = []; + for (var i = 0, values = {}; i < arguments.length; i++) { + var arr = arguments[i]; + for (var j = 0; j < arr.length; j++) { + if (!values[arr[j]]) { + values[arr[j]] = true; + result.push(arr[j]); + } + } + } + return result; +}; + +function has(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +exports.has = has; + +exports.extend = function (dest, src) { + for (var i in src) { + if (has(src, i)) { dest[i] = src[i]; } + } +}; + +exports.trimEnd = function (str) { + return str.replace(/\s+$/g, ''); +}; diff --git a/WebServer/node_modules/argparse/package.json b/WebServer/node_modules/argparse/package.json new file mode 100644 index 000000000..4c3727f13 --- /dev/null +++ b/WebServer/node_modules/argparse/package.json @@ -0,0 +1,73 @@ +{ + "_args": [ + [ + "argparse@1.0.10", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "argparse@1.0.10", + "_id": "argparse@1.0.10", + "_inBundle": false, + "_integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "_location": "/argparse", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "argparse@1.0.10", + "name": "argparse", + "escapedName": "argparse", + "rawSpec": "1.0.10", + "saveSpec": null, + "fetchSpec": "1.0.10" + }, + "_requiredBy": [ + "/js-yaml" + ], + "_resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "_spec": "1.0.10", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "bugs": { + "url": "https://github.com/nodeca/argparse/issues" + }, + "contributors": [ + { + "name": "Eugene Shkuropat" + }, + { + "name": "Paul Jacobson" + } + ], + "dependencies": { + "sprintf-js": "~1.0.2" + }, + "description": "Very powerful CLI arguments parser. Native port of argparse - python's options parsing library", + "devDependencies": { + "eslint": "^2.13.1", + "istanbul": "^0.4.5", + "mocha": "^3.1.0", + "ndoc": "^5.0.1" + }, + "files": [ + "index.js", + "lib/" + ], + "homepage": "https://github.com/nodeca/argparse#readme", + "keywords": [ + "cli", + "parser", + "argparse", + "option", + "args" + ], + "license": "MIT", + "name": "argparse", + "repository": { + "type": "git", + "url": "git+https://github.com/nodeca/argparse.git" + }, + "scripts": { + "test": "make test" + }, + "version": "1.0.10" +} diff --git a/WebServer/node_modules/assertion-error/index.js b/WebServer/node_modules/assertion-error/index.js new file mode 100644 index 000000000..8466da8b2 --- /dev/null +++ b/WebServer/node_modules/assertion-error/index.js @@ -0,0 +1,116 @@ +/*! + * assertion-error + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Return a function that will copy properties from + * one object to another excluding any originally + * listed. Returned function will create a new `{}`. + * + * @param {String} excluded properties ... + * @return {Function} + */ + +function exclude () { + var excludes = [].slice.call(arguments); + + function excludeProps (res, obj) { + Object.keys(obj).forEach(function (key) { + if (!~excludes.indexOf(key)) res[key] = obj[key]; + }); + } + + return function extendExclude () { + var args = [].slice.call(arguments) + , i = 0 + , res = {}; + + for (; i < args.length; i++) { + excludeProps(res, args[i]); + } + + return res; + }; +}; + +/*! + * Primary Exports + */ + +module.exports = AssertionError; + +/** + * ### AssertionError + * + * An extension of the JavaScript `Error` constructor for + * assertion and validation scenarios. + * + * @param {String} message + * @param {Object} properties to include (optional) + * @param {callee} start stack function (optional) + */ + +function AssertionError (message, _props, ssf) { + var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') + , props = extend(_props || {}); + + // default values + this.message = message || 'Unspecified AssertionError'; + this.showDiff = false; + + // copy from properties + for (var key in props) { + this[key] = props[key]; + } + + // capture stack trace + ssf = ssf || AssertionError; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ssf); + } else { + try { + throw new Error(); + } catch(e) { + this.stack = e.stack; + } + } +} + +/*! + * Inherit from Error.prototype + */ + +AssertionError.prototype = Object.create(Error.prototype); + +/*! + * Statically set name + */ + +AssertionError.prototype.name = 'AssertionError'; + +/*! + * Ensure correct constructor + */ + +AssertionError.prototype.constructor = AssertionError; + +/** + * Allow errors to be converted to JSON for static transfer. + * + * @param {Boolean} include stack (default: `true`) + * @return {Object} object that can be `JSON.stringify` + */ + +AssertionError.prototype.toJSON = function (stack) { + var extend = exclude('constructor', 'toJSON', 'stack') + , props = extend({ name: this.name }, this); + + // include stack if exists and not turned off + if (false !== stack && this.stack) { + props.stack = this.stack; + } + + return props; +}; diff --git a/WebServer/node_modules/assertion-error/package.json b/WebServer/node_modules/assertion-error/package.json new file mode 100644 index 000000000..8a33e967c --- /dev/null +++ b/WebServer/node_modules/assertion-error/package.json @@ -0,0 +1,65 @@ +{ + "_args": [ + [ + "assertion-error@1.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "assertion-error@1.1.0", + "_id": "assertion-error@1.1.0", + "_inBundle": false, + "_integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "_location": "/assertion-error", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "assertion-error@1.1.0", + "name": "assertion-error", + "escapedName": "assertion-error", + "rawSpec": "1.1.0", + "saveSpec": null, + "fetchSpec": "1.1.0" + }, + "_requiredBy": [ + "/chai" + ], + "_resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "_spec": "1.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jake Luer", + "email": "jake@qualiancy.com", + "url": "http://qualiancy.com" + }, + "bugs": { + "url": "https://github.com/chaijs/assertion-error/issues" + }, + "dependencies": {}, + "description": "Error constructor for test and validation frameworks that implements standardized AssertionError specification.", + "devDependencies": { + "component": "*", + "typescript": "^2.6.1" + }, + "engines": { + "node": "*" + }, + "homepage": "https://github.com/chaijs/assertion-error#readme", + "keywords": [ + "test", + "assertion", + "assertion-error" + ], + "license": "MIT", + "main": "./index", + "name": "assertion-error", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/chaijs/assertion-error.git" + }, + "scripts": { + "test": "make test" + }, + "types": "./index.d.ts", + "version": "1.1.0" +} diff --git a/WebServer/node_modules/axios/dist/axios.js b/WebServer/node_modules/axios/dist/axios.js new file mode 100644 index 000000000..5cef419c2 --- /dev/null +++ b/WebServer/node_modules/axios/dist/axios.js @@ -0,0 +1,1668 @@ +/* axios v0.19.0 | (c) 2019 by Matt Zabriskie */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["axios"] = factory(); + else + root["axios"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; +/******/ +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(1); + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + var bind = __webpack_require__(3); + var Axios = __webpack_require__(5); + var mergeConfig = __webpack_require__(22); + var defaults = __webpack_require__(11); + + /** + * Create an instance of Axios + * + * @param {Object} defaultConfig The default config for the instance + * @return {Axios} A new instance of Axios + */ + function createInstance(defaultConfig) { + var context = new Axios(defaultConfig); + var instance = bind(Axios.prototype.request, context); + + // Copy axios.prototype to instance + utils.extend(instance, Axios.prototype, context); + + // Copy context to instance + utils.extend(instance, context); + + return instance; + } + + // Create the default instance to be exported + var axios = createInstance(defaults); + + // Expose Axios class to allow class inheritance + axios.Axios = Axios; + + // Factory for creating new instances + axios.create = function create(instanceConfig) { + return createInstance(mergeConfig(axios.defaults, instanceConfig)); + }; + + // Expose Cancel & CancelToken + axios.Cancel = __webpack_require__(23); + axios.CancelToken = __webpack_require__(24); + axios.isCancel = __webpack_require__(10); + + // Expose all/spread + axios.all = function all(promises) { + return Promise.all(promises); + }; + axios.spread = __webpack_require__(25); + + module.exports = axios; + + // Allow use of default import syntax in TypeScript + module.exports.default = axios; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var bind = __webpack_require__(3); + var isBuffer = __webpack_require__(4); + + /*global toString:true*/ + + // utils is a library of generic helper functions non-specific to axios + + var toString = Object.prototype.toString; + + /** + * Determine if a value is an Array + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Array, otherwise false + */ + function isArray(val) { + return toString.call(val) === '[object Array]'; + } + + /** + * Determine if a value is an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an ArrayBuffer, otherwise false + */ + function isArrayBuffer(val) { + return toString.call(val) === '[object ArrayBuffer]'; + } + + /** + * Determine if a value is a FormData + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an FormData, otherwise false + */ + function isFormData(val) { + return (typeof FormData !== 'undefined') && (val instanceof FormData); + } + + /** + * Determine if a value is a view on an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false + */ + function isArrayBufferView(val) { + var result; + if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { + result = ArrayBuffer.isView(val); + } else { + result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); + } + return result; + } + + /** + * Determine if a value is a String + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a String, otherwise false + */ + function isString(val) { + return typeof val === 'string'; + } + + /** + * Determine if a value is a Number + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Number, otherwise false + */ + function isNumber(val) { + return typeof val === 'number'; + } + + /** + * Determine if a value is undefined + * + * @param {Object} val The value to test + * @returns {boolean} True if the value is undefined, otherwise false + */ + function isUndefined(val) { + return typeof val === 'undefined'; + } + + /** + * Determine if a value is an Object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Object, otherwise false + */ + function isObject(val) { + return val !== null && typeof val === 'object'; + } + + /** + * Determine if a value is a Date + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Date, otherwise false + */ + function isDate(val) { + return toString.call(val) === '[object Date]'; + } + + /** + * Determine if a value is a File + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a File, otherwise false + */ + function isFile(val) { + return toString.call(val) === '[object File]'; + } + + /** + * Determine if a value is a Blob + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Blob, otherwise false + */ + function isBlob(val) { + return toString.call(val) === '[object Blob]'; + } + + /** + * Determine if a value is a Function + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Function, otherwise false + */ + function isFunction(val) { + return toString.call(val) === '[object Function]'; + } + + /** + * Determine if a value is a Stream + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Stream, otherwise false + */ + function isStream(val) { + return isObject(val) && isFunction(val.pipe); + } + + /** + * Determine if a value is a URLSearchParams object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a URLSearchParams object, otherwise false + */ + function isURLSearchParams(val) { + return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; + } + + /** + * Trim excess whitespace off the beginning and end of a string + * + * @param {String} str The String to trim + * @returns {String} The String freed of excess whitespace + */ + function trim(str) { + return str.replace(/^\s*/, '').replace(/\s*$/, ''); + } + + /** + * Determine if we're running in a standard browser environment + * + * This allows axios to run in a web worker, and react-native. + * Both environments support XMLHttpRequest, but not fully standard globals. + * + * web workers: + * typeof window -> undefined + * typeof document -> undefined + * + * react-native: + * navigator.product -> 'ReactNative' + * nativescript + * navigator.product -> 'NativeScript' or 'NS' + */ + function isStandardBrowserEnv() { + if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' || + navigator.product === 'NativeScript' || + navigator.product === 'NS')) { + return false; + } + return ( + typeof window !== 'undefined' && + typeof document !== 'undefined' + ); + } + + /** + * Iterate over an Array or an Object invoking a function for each item. + * + * If `obj` is an Array callback will be called passing + * the value, index, and complete array for each item. + * + * If 'obj' is an Object callback will be called passing + * the value, key, and complete object for each property. + * + * @param {Object|Array} obj The object to iterate + * @param {Function} fn The callback to invoke for each item + */ + function forEach(obj, fn) { + // Don't bother if no value provided + if (obj === null || typeof obj === 'undefined') { + return; + } + + // Force an array if not already something iterable + if (typeof obj !== 'object') { + /*eslint no-param-reassign:0*/ + obj = [obj]; + } + + if (isArray(obj)) { + // Iterate over array values + for (var i = 0, l = obj.length; i < l; i++) { + fn.call(null, obj[i], i, obj); + } + } else { + // Iterate over object keys + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + fn.call(null, obj[key], key, obj); + } + } + } + } + + /** + * Accepts varargs expecting each argument to be an object, then + * immutably merges the properties of each object and returns result. + * + * When multiple objects contain the same key the later object in + * the arguments list will take precedence. + * + * Example: + * + * ```js + * var result = merge({foo: 123}, {foo: 456}); + * console.log(result.foo); // outputs 456 + * ``` + * + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ + function merge(/* obj1, obj2, obj3, ... */) { + var result = {}; + function assignValue(val, key) { + if (typeof result[key] === 'object' && typeof val === 'object') { + result[key] = merge(result[key], val); + } else { + result[key] = val; + } + } + + for (var i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue); + } + return result; + } + + /** + * Function equal to merge with the difference being that no reference + * to original objects is kept. + * + * @see merge + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ + function deepMerge(/* obj1, obj2, obj3, ... */) { + var result = {}; + function assignValue(val, key) { + if (typeof result[key] === 'object' && typeof val === 'object') { + result[key] = deepMerge(result[key], val); + } else if (typeof val === 'object') { + result[key] = deepMerge({}, val); + } else { + result[key] = val; + } + } + + for (var i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue); + } + return result; + } + + /** + * Extends object a by mutably adding to it the properties of object b. + * + * @param {Object} a The object to be extended + * @param {Object} b The object to copy properties from + * @param {Object} thisArg The object to bind function to + * @return {Object} The resulting value of object a + */ + function extend(a, b, thisArg) { + forEach(b, function assignValue(val, key) { + if (thisArg && typeof val === 'function') { + a[key] = bind(val, thisArg); + } else { + a[key] = val; + } + }); + return a; + } + + module.exports = { + isArray: isArray, + isArrayBuffer: isArrayBuffer, + isBuffer: isBuffer, + isFormData: isFormData, + isArrayBufferView: isArrayBufferView, + isString: isString, + isNumber: isNumber, + isObject: isObject, + isUndefined: isUndefined, + isDate: isDate, + isFile: isFile, + isBlob: isBlob, + isFunction: isFunction, + isStream: isStream, + isURLSearchParams: isURLSearchParams, + isStandardBrowserEnv: isStandardBrowserEnv, + forEach: forEach, + merge: merge, + deepMerge: deepMerge, + extend: extend, + trim: trim + }; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + + 'use strict'; + + module.exports = function bind(fn, thisArg) { + return function wrap() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + return fn.apply(thisArg, args); + }; + }; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + + /*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ + + module.exports = function isBuffer (obj) { + return obj != null && obj.constructor != null && + typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) + } + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + var buildURL = __webpack_require__(6); + var InterceptorManager = __webpack_require__(7); + var dispatchRequest = __webpack_require__(8); + var mergeConfig = __webpack_require__(22); + + /** + * Create a new instance of Axios + * + * @param {Object} instanceConfig The default config for the instance + */ + function Axios(instanceConfig) { + this.defaults = instanceConfig; + this.interceptors = { + request: new InterceptorManager(), + response: new InterceptorManager() + }; + } + + /** + * Dispatch a request + * + * @param {Object} config The config specific for this request (merged with this.defaults) + */ + Axios.prototype.request = function request(config) { + /*eslint no-param-reassign:0*/ + // Allow for axios('example/url'[, config]) a la fetch API + if (typeof config === 'string') { + config = arguments[1] || {}; + config.url = arguments[0]; + } else { + config = config || {}; + } + + config = mergeConfig(this.defaults, config); + config.method = config.method ? config.method.toLowerCase() : 'get'; + + // Hook up interceptors middleware + var chain = [dispatchRequest, undefined]; + var promise = Promise.resolve(config); + + this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { + chain.unshift(interceptor.fulfilled, interceptor.rejected); + }); + + this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { + chain.push(interceptor.fulfilled, interceptor.rejected); + }); + + while (chain.length) { + promise = promise.then(chain.shift(), chain.shift()); + } + + return promise; + }; + + Axios.prototype.getUri = function getUri(config) { + config = mergeConfig(this.defaults, config); + return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''); + }; + + // Provide aliases for supported request methods + utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, config) { + return this.request(utils.merge(config || {}, { + method: method, + url: url + })); + }; + }); + + utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, data, config) { + return this.request(utils.merge(config || {}, { + method: method, + url: url, + data: data + })); + }; + }); + + module.exports = Axios; + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + function encode(val) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, '+'). + replace(/%5B/gi, '['). + replace(/%5D/gi, ']'); + } + + /** + * Build a URL by appending params to the end + * + * @param {string} url The base of the url (e.g., http://www.google.com) + * @param {object} [params] The params to be appended + * @returns {string} The formatted url + */ + module.exports = function buildURL(url, params, paramsSerializer) { + /*eslint no-param-reassign:0*/ + if (!params) { + return url; + } + + var serializedParams; + if (paramsSerializer) { + serializedParams = paramsSerializer(params); + } else if (utils.isURLSearchParams(params)) { + serializedParams = params.toString(); + } else { + var parts = []; + + utils.forEach(params, function serialize(val, key) { + if (val === null || typeof val === 'undefined') { + return; + } + + if (utils.isArray(val)) { + key = key + '[]'; + } else { + val = [val]; + } + + utils.forEach(val, function parseValue(v) { + if (utils.isDate(v)) { + v = v.toISOString(); + } else if (utils.isObject(v)) { + v = JSON.stringify(v); + } + parts.push(encode(key) + '=' + encode(v)); + }); + }); + + serializedParams = parts.join('&'); + } + + if (serializedParams) { + var hashmarkIndex = url.indexOf('#'); + if (hashmarkIndex !== -1) { + url = url.slice(0, hashmarkIndex); + } + + url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; + } + + return url; + }; + + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + function InterceptorManager() { + this.handlers = []; + } + + /** + * Add a new interceptor to the stack + * + * @param {Function} fulfilled The function to handle `then` for a `Promise` + * @param {Function} rejected The function to handle `reject` for a `Promise` + * + * @return {Number} An ID used to remove interceptor later + */ + InterceptorManager.prototype.use = function use(fulfilled, rejected) { + this.handlers.push({ + fulfilled: fulfilled, + rejected: rejected + }); + return this.handlers.length - 1; + }; + + /** + * Remove an interceptor from the stack + * + * @param {Number} id The ID that was returned by `use` + */ + InterceptorManager.prototype.eject = function eject(id) { + if (this.handlers[id]) { + this.handlers[id] = null; + } + }; + + /** + * Iterate over all the registered interceptors + * + * This method is particularly useful for skipping over any + * interceptors that may have become `null` calling `eject`. + * + * @param {Function} fn The function to call for each interceptor + */ + InterceptorManager.prototype.forEach = function forEach(fn) { + utils.forEach(this.handlers, function forEachHandler(h) { + if (h !== null) { + fn(h); + } + }); + }; + + module.exports = InterceptorManager; + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + var transformData = __webpack_require__(9); + var isCancel = __webpack_require__(10); + var defaults = __webpack_require__(11); + var isAbsoluteURL = __webpack_require__(20); + var combineURLs = __webpack_require__(21); + + /** + * Throws a `Cancel` if cancellation has been requested. + */ + function throwIfCancellationRequested(config) { + if (config.cancelToken) { + config.cancelToken.throwIfRequested(); + } + } + + /** + * Dispatch a request to the server using the configured adapter. + * + * @param {object} config The config that is to be used for the request + * @returns {Promise} The Promise to be fulfilled + */ + module.exports = function dispatchRequest(config) { + throwIfCancellationRequested(config); + + // Support baseURL config + if (config.baseURL && !isAbsoluteURL(config.url)) { + config.url = combineURLs(config.baseURL, config.url); + } + + // Ensure headers exist + config.headers = config.headers || {}; + + // Transform request data + config.data = transformData( + config.data, + config.headers, + config.transformRequest + ); + + // Flatten headers + config.headers = utils.merge( + config.headers.common || {}, + config.headers[config.method] || {}, + config.headers || {} + ); + + utils.forEach( + ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], + function cleanHeaderConfig(method) { + delete config.headers[method]; + } + ); + + var adapter = config.adapter || defaults.adapter; + + return adapter(config).then(function onAdapterResolution(response) { + throwIfCancellationRequested(config); + + // Transform response data + response.data = transformData( + response.data, + response.headers, + config.transformResponse + ); + + return response; + }, function onAdapterRejection(reason) { + if (!isCancel(reason)) { + throwIfCancellationRequested(config); + + // Transform response data + if (reason && reason.response) { + reason.response.data = transformData( + reason.response.data, + reason.response.headers, + config.transformResponse + ); + } + } + + return Promise.reject(reason); + }); + }; + + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + /** + * Transform the data for a request or a response + * + * @param {Object|String} data The data to be transformed + * @param {Array} headers The headers for the request or response + * @param {Array|Function} fns A single function or Array of functions + * @returns {*} The resulting transformed data + */ + module.exports = function transformData(data, headers, fns) { + /*eslint no-param-reassign:0*/ + utils.forEach(fns, function transform(fn) { + data = fn(data, headers); + }); + + return data; + }; + + +/***/ }), +/* 10 */ +/***/ (function(module, exports) { + + 'use strict'; + + module.exports = function isCancel(value) { + return !!(value && value.__CANCEL__); + }; + + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + var normalizeHeaderName = __webpack_require__(12); + + var DEFAULT_CONTENT_TYPE = { + 'Content-Type': 'application/x-www-form-urlencoded' + }; + + function setContentTypeIfUnset(headers, value) { + if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { + headers['Content-Type'] = value; + } + } + + function getDefaultAdapter() { + var adapter; + // Only Node.JS has a process variable that is of [[Class]] process + if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { + // For node use HTTP adapter + adapter = __webpack_require__(13); + } else if (typeof XMLHttpRequest !== 'undefined') { + // For browsers use XHR adapter + adapter = __webpack_require__(13); + } + return adapter; + } + + var defaults = { + adapter: getDefaultAdapter(), + + transformRequest: [function transformRequest(data, headers) { + normalizeHeaderName(headers, 'Accept'); + normalizeHeaderName(headers, 'Content-Type'); + if (utils.isFormData(data) || + utils.isArrayBuffer(data) || + utils.isBuffer(data) || + utils.isStream(data) || + utils.isFile(data) || + utils.isBlob(data) + ) { + return data; + } + if (utils.isArrayBufferView(data)) { + return data.buffer; + } + if (utils.isURLSearchParams(data)) { + setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); + return data.toString(); + } + if (utils.isObject(data)) { + setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); + return JSON.stringify(data); + } + return data; + }], + + transformResponse: [function transformResponse(data) { + /*eslint no-param-reassign:0*/ + if (typeof data === 'string') { + try { + data = JSON.parse(data); + } catch (e) { /* Ignore */ } + } + return data; + }], + + /** + * A timeout in milliseconds to abort a request. If set to 0 (default) a + * timeout is not created. + */ + timeout: 0, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', + + maxContentLength: -1, + + validateStatus: function validateStatus(status) { + return status >= 200 && status < 300; + } + }; + + defaults.headers = { + common: { + 'Accept': 'application/json, text/plain, */*' + } + }; + + utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { + defaults.headers[method] = {}; + }); + + utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); + }); + + module.exports = defaults; + + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + module.exports = function normalizeHeaderName(headers, normalizedName) { + utils.forEach(headers, function processHeader(value, name) { + if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { + headers[normalizedName] = value; + delete headers[name]; + } + }); + }; + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + var settle = __webpack_require__(14); + var buildURL = __webpack_require__(6); + var parseHeaders = __webpack_require__(17); + var isURLSameOrigin = __webpack_require__(18); + var createError = __webpack_require__(15); + + module.exports = function xhrAdapter(config) { + return new Promise(function dispatchXhrRequest(resolve, reject) { + var requestData = config.data; + var requestHeaders = config.headers; + + if (utils.isFormData(requestData)) { + delete requestHeaders['Content-Type']; // Let the browser set it + } + + var request = new XMLHttpRequest(); + + // HTTP basic authentication + if (config.auth) { + var username = config.auth.username || ''; + var password = config.auth.password || ''; + requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); + } + + request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true); + + // Set the request timeout in MS + request.timeout = config.timeout; + + // Listen for ready state + request.onreadystatechange = function handleLoad() { + if (!request || request.readyState !== 4) { + return; + } + + // The request errored out and we didn't get a response, this will be + // handled by onerror instead + // With one exception: request that using file: protocol, most browsers + // will return status as 0 even though it's a successful request + if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { + return; + } + + // Prepare the response + var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; + var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; + var response = { + data: responseData, + status: request.status, + statusText: request.statusText, + headers: responseHeaders, + config: config, + request: request + }; + + settle(resolve, reject, response); + + // Clean up request + request = null; + }; + + // Handle browser request cancellation (as opposed to a manual cancellation) + request.onabort = function handleAbort() { + if (!request) { + return; + } + + reject(createError('Request aborted', config, 'ECONNABORTED', request)); + + // Clean up request + request = null; + }; + + // Handle low level network errors + request.onerror = function handleError() { + // Real errors are hidden from us by the browser + // onerror should only fire if it's a network error + reject(createError('Network Error', config, null, request)); + + // Clean up request + request = null; + }; + + // Handle timeout + request.ontimeout = function handleTimeout() { + reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', + request)); + + // Clean up request + request = null; + }; + + // Add xsrf header + // This is only done if running in a standard browser environment. + // Specifically not if we're in a web worker, or react-native. + if (utils.isStandardBrowserEnv()) { + var cookies = __webpack_require__(19); + + // Add xsrf header + var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ? + cookies.read(config.xsrfCookieName) : + undefined; + + if (xsrfValue) { + requestHeaders[config.xsrfHeaderName] = xsrfValue; + } + } + + // Add headers to the request + if ('setRequestHeader' in request) { + utils.forEach(requestHeaders, function setRequestHeader(val, key) { + if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { + // Remove Content-Type if data is undefined + delete requestHeaders[key]; + } else { + // Otherwise add header to the request + request.setRequestHeader(key, val); + } + }); + } + + // Add withCredentials to request if needed + if (config.withCredentials) { + request.withCredentials = true; + } + + // Add responseType to request if needed + if (config.responseType) { + try { + request.responseType = config.responseType; + } catch (e) { + // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. + // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. + if (config.responseType !== 'json') { + throw e; + } + } + } + + // Handle progress if needed + if (typeof config.onDownloadProgress === 'function') { + request.addEventListener('progress', config.onDownloadProgress); + } + + // Not all browsers support upload events + if (typeof config.onUploadProgress === 'function' && request.upload) { + request.upload.addEventListener('progress', config.onUploadProgress); + } + + if (config.cancelToken) { + // Handle cancellation + config.cancelToken.promise.then(function onCanceled(cancel) { + if (!request) { + return; + } + + request.abort(); + reject(cancel); + // Clean up request + request = null; + }); + } + + if (requestData === undefined) { + requestData = null; + } + + // Send the request + request.send(requestData); + }); + }; + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var createError = __webpack_require__(15); + + /** + * Resolve or reject a Promise based on response status. + * + * @param {Function} resolve A function that resolves the promise. + * @param {Function} reject A function that rejects the promise. + * @param {object} response The response. + */ + module.exports = function settle(resolve, reject, response) { + var validateStatus = response.config.validateStatus; + if (!validateStatus || validateStatus(response.status)) { + resolve(response); + } else { + reject(createError( + 'Request failed with status code ' + response.status, + response.config, + null, + response.request, + response + )); + } + }; + + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var enhanceError = __webpack_require__(16); + + /** + * Create an Error with the specified message, config, error code, request and response. + * + * @param {string} message The error message. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The created error. + */ + module.exports = function createError(message, config, code, request, response) { + var error = new Error(message); + return enhanceError(error, config, code, request, response); + }; + + +/***/ }), +/* 16 */ +/***/ (function(module, exports) { + + 'use strict'; + + /** + * Update an Error with the specified config, error code, and response. + * + * @param {Error} error The error to update. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The error. + */ + module.exports = function enhanceError(error, config, code, request, response) { + error.config = config; + if (code) { + error.code = code; + } + + error.request = request; + error.response = response; + error.isAxiosError = true; + + error.toJSON = function() { + return { + // Standard + message: this.message, + name: this.name, + // Microsoft + description: this.description, + number: this.number, + // Mozilla + fileName: this.fileName, + lineNumber: this.lineNumber, + columnNumber: this.columnNumber, + stack: this.stack, + // Axios + config: this.config, + code: this.code + }; + }; + return error; + }; + + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + // Headers whose duplicates are ignored by node + // c.f. https://nodejs.org/api/http.html#http_message_headers + var ignoreDuplicateOf = [ + 'age', 'authorization', 'content-length', 'content-type', 'etag', + 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', + 'last-modified', 'location', 'max-forwards', 'proxy-authorization', + 'referer', 'retry-after', 'user-agent' + ]; + + /** + * Parse headers into an object + * + * ``` + * Date: Wed, 27 Aug 2014 08:58:49 GMT + * Content-Type: application/json + * Connection: keep-alive + * Transfer-Encoding: chunked + * ``` + * + * @param {String} headers Headers needing to be parsed + * @returns {Object} Headers parsed into an object + */ + module.exports = function parseHeaders(headers) { + var parsed = {}; + var key; + var val; + var i; + + if (!headers) { return parsed; } + + utils.forEach(headers.split('\n'), function parser(line) { + i = line.indexOf(':'); + key = utils.trim(line.substr(0, i)).toLowerCase(); + val = utils.trim(line.substr(i + 1)); + + if (key) { + if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) { + return; + } + if (key === 'set-cookie') { + parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]); + } else { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + } + }); + + return parsed; + }; + + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + module.exports = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs have full support of the APIs needed to test + // whether the request URL is of the same origin as current location. + (function standardBrowserEnv() { + var msie = /(msie|trident)/i.test(navigator.userAgent); + var urlParsingNode = document.createElement('a'); + var originURL; + + /** + * Parse a URL to discover it's components + * + * @param {String} url The URL to be parsed + * @returns {Object} + */ + function resolveURL(url) { + var href = url; + + if (msie) { + // IE needs attribute set twice to normalize properties + urlParsingNode.setAttribute('href', href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') ? + urlParsingNode.pathname : + '/' + urlParsingNode.pathname + }; + } + + originURL = resolveURL(window.location.href); + + /** + * Determine if a URL shares the same origin as the current location + * + * @param {String} requestURL The URL to test + * @returns {boolean} True if URL shares the same origin, otherwise false + */ + return function isURLSameOrigin(requestURL) { + var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; + return (parsed.protocol === originURL.protocol && + parsed.host === originURL.host); + }; + })() : + + // Non standard browser envs (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return function isURLSameOrigin() { + return true; + }; + })() + ); + + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + module.exports = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs support document.cookie + (function standardBrowserEnv() { + return { + write: function write(name, value, expires, path, domain, secure) { + var cookie = []; + cookie.push(name + '=' + encodeURIComponent(value)); + + if (utils.isNumber(expires)) { + cookie.push('expires=' + new Date(expires).toGMTString()); + } + + if (utils.isString(path)) { + cookie.push('path=' + path); + } + + if (utils.isString(domain)) { + cookie.push('domain=' + domain); + } + + if (secure === true) { + cookie.push('secure'); + } + + document.cookie = cookie.join('; '); + }, + + read: function read(name) { + var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); + return (match ? decodeURIComponent(match[3]) : null); + }, + + remove: function remove(name) { + this.write(name, '', Date.now() - 86400000); + } + }; + })() : + + // Non standard browser env (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return { + write: function write() {}, + read: function read() { return null; }, + remove: function remove() {} + }; + })() + ); + + +/***/ }), +/* 20 */ +/***/ (function(module, exports) { + + 'use strict'; + + /** + * Determines whether the specified URL is absolute + * + * @param {string} url The URL to test + * @returns {boolean} True if the specified URL is absolute, otherwise false + */ + module.exports = function isAbsoluteURL(url) { + // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed + // by any combination of letters, digits, plus, period, or hyphen. + return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); + }; + + +/***/ }), +/* 21 */ +/***/ (function(module, exports) { + + 'use strict'; + + /** + * Creates a new URL by combining the specified URLs + * + * @param {string} baseURL The base URL + * @param {string} relativeURL The relative URL + * @returns {string} The combined URL + */ + module.exports = function combineURLs(baseURL, relativeURL) { + return relativeURL + ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') + : baseURL; + }; + + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var utils = __webpack_require__(2); + + /** + * Config-specific merge-function which creates a new config-object + * by merging two configuration objects together. + * + * @param {Object} config1 + * @param {Object} config2 + * @returns {Object} New object resulting from merging config2 to config1 + */ + module.exports = function mergeConfig(config1, config2) { + // eslint-disable-next-line no-param-reassign + config2 = config2 || {}; + var config = {}; + + utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) { + if (typeof config2[prop] !== 'undefined') { + config[prop] = config2[prop]; + } + }); + + utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) { + if (utils.isObject(config2[prop])) { + config[prop] = utils.deepMerge(config1[prop], config2[prop]); + } else if (typeof config2[prop] !== 'undefined') { + config[prop] = config2[prop]; + } else if (utils.isObject(config1[prop])) { + config[prop] = utils.deepMerge(config1[prop]); + } else if (typeof config1[prop] !== 'undefined') { + config[prop] = config1[prop]; + } + }); + + utils.forEach([ + 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', + 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', + 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength', + 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken', + 'socketPath' + ], function defaultToConfig2(prop) { + if (typeof config2[prop] !== 'undefined') { + config[prop] = config2[prop]; + } else if (typeof config1[prop] !== 'undefined') { + config[prop] = config1[prop]; + } + }); + + return config; + }; + + +/***/ }), +/* 23 */ +/***/ (function(module, exports) { + + 'use strict'; + + /** + * A `Cancel` is an object that is thrown when an operation is canceled. + * + * @class + * @param {string=} message The message. + */ + function Cancel(message) { + this.message = message; + } + + Cancel.prototype.toString = function toString() { + return 'Cancel' + (this.message ? ': ' + this.message : ''); + }; + + Cancel.prototype.__CANCEL__ = true; + + module.exports = Cancel; + + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + + 'use strict'; + + var Cancel = __webpack_require__(23); + + /** + * A `CancelToken` is an object that can be used to request cancellation of an operation. + * + * @class + * @param {Function} executor The executor function. + */ + function CancelToken(executor) { + if (typeof executor !== 'function') { + throw new TypeError('executor must be a function.'); + } + + var resolvePromise; + this.promise = new Promise(function promiseExecutor(resolve) { + resolvePromise = resolve; + }); + + var token = this; + executor(function cancel(message) { + if (token.reason) { + // Cancellation has already been requested + return; + } + + token.reason = new Cancel(message); + resolvePromise(token.reason); + }); + } + + /** + * Throws a `Cancel` if cancellation has been requested. + */ + CancelToken.prototype.throwIfRequested = function throwIfRequested() { + if (this.reason) { + throw this.reason; + } + }; + + /** + * Returns an object that contains a new `CancelToken` and a function that, when called, + * cancels the `CancelToken`. + */ + CancelToken.source = function source() { + var cancel; + var token = new CancelToken(function executor(c) { + cancel = c; + }); + return { + token: token, + cancel: cancel + }; + }; + + module.exports = CancelToken; + + +/***/ }), +/* 25 */ +/***/ (function(module, exports) { + + 'use strict'; + + /** + * Syntactic sugar for invoking a function and expanding an array for arguments. + * + * Common use case would be to use `Function.prototype.apply`. + * + * ```js + * function f(x, y, z) {} + * var args = [1, 2, 3]; + * f.apply(null, args); + * ``` + * + * With `spread` this example can be re-written. + * + * ```js + * spread(function(x, y, z) {})([1, 2, 3]); + * ``` + * + * @param {Function} callback + * @returns {Function} + */ + module.exports = function spread(callback) { + return function wrap(arr) { + return callback.apply(null, arr); + }; + }; + + +/***/ }) +/******/ ]) +}); +; +//# sourceMappingURL=axios.map \ No newline at end of file diff --git a/WebServer/node_modules/axios/dist/axios.min.js b/WebServer/node_modules/axios/dist/axios.min.js new file mode 100644 index 000000000..0f3681630 --- /dev/null +++ b/WebServer/node_modules/axios/dist/axios.min.js @@ -0,0 +1,9 @@ +/* axios v0.19.0 | (c) 2019 by Matt Zabriskie */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(5),a=n(22),u=n(11),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(10),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"[object ArrayBuffer]"===j.call(e)}function s(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function a(e){return"string"==typeof e}function u(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===j.call(e)}function d(e){return"[object File]"===j.call(e)}function l(e){return"[object Blob]"===j.call(e)}function h(e){return"[object Function]"===j.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function g(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n + * @license MIT + */ +e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(6),i=n(7),a=n(8),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method=e.method?e.method.toLowerCase():"get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(9),i=n(10),a=n(11),u=n(20),c=n(21);e.exports=function(e){r(e),e.baseURL&&!u(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?e=n(13):"undefined"!=typeof XMLHttpRequest&&(e=n(13)),e}var s=n(2),i=n(12),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(14),s=n(6),i=n(17),a=n(18),u=n(15);e.exports=function(e){return new Promise(function(t,c){var f=e.data,p=e.headers;r.isFormData(f)&&delete p["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var l=e.auth.username||"",h=e.auth.password||"";p.Authorization="Basic "+btoa(l+":"+h)}if(d.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?i(d.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?d.response:d.responseText,s={data:r,status:d.status,statusText:d.statusText,headers:n,config:e,request:d};o(t,c,s),d=null}},d.onabort=function(){d&&(c(u("Request aborted",e,"ECONNABORTED",d)),d=null)},d.onerror=function(){c(u("Network Error",e,null,d)),d=null},d.ontimeout=function(){c(u("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED",d)),d=null},r.isStandardBrowserEnv()){var m=n(19),y=(e.withCredentials||a(e.url))&&e.xsrfCookieName?m.read(e.xsrfCookieName):void 0;y&&(p[e.xsrfHeaderName]=y)}if("setRequestHeader"in d&&r.forEach(p,function(e,t){"undefined"==typeof f&&"content-type"===t.toLowerCase()?delete p[t]:d.setRequestHeader(t,e)}),e.withCredentials&&(d.withCredentials=!0),e.responseType)try{d.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&d.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&d.upload&&d.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){d&&(d.abort(),c(e),d=null)}),void 0===f&&(f=null),d.send(f)})}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n){var o=n.config.validateStatus;!o||o(n.status)?e(n):t(r("Request failed with status code "+n.status,n.config,null,n.request,n))}},function(e,t,n){"use strict";var r=n(16);e.exports=function(e,t,n,o,s){var i=new Error(e);return r(i,t,n,o,s)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,s,i={};return e?(r.forEach(e.split("\n"),function(e){if(s=e.indexOf(":"),t=r.trim(e.substr(0,s)).toLowerCase(),n=r.trim(e.substr(s+1)),t){if(i[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?i[t]=(i[t]?i[t]:[]).concat([n]):i[t]=i[t]?i[t]+", "+n:n}}),i):i}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(s)&&a.push("domain="+s),i===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={};return r.forEach(["url","method","params","data"],function(e){"undefined"!=typeof t[e]&&(n[e]=t[e])}),r.forEach(["headers","auth","proxy"],function(o){r.isObject(t[o])?n[o]=r.deepMerge(e[o],t[o]):"undefined"!=typeof t[o]?n[o]=t[o]:r.isObject(e[o])?n[o]=r.deepMerge(e[o]):"undefined"!=typeof e[o]&&(n[o]=e[o])}),r.forEach(["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"],function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])}),n}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])}); +//# sourceMappingURL=axios.min.map \ No newline at end of file diff --git a/WebServer/node_modules/axios/index.js b/WebServer/node_modules/axios/index.js new file mode 100644 index 000000000..79dfd09dd --- /dev/null +++ b/WebServer/node_modules/axios/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/axios'); \ No newline at end of file diff --git a/WebServer/node_modules/axios/lib/adapters/http.js b/WebServer/node_modules/axios/lib/adapters/http.js new file mode 100644 index 000000000..06169ff2c --- /dev/null +++ b/WebServer/node_modules/axios/lib/adapters/http.js @@ -0,0 +1,275 @@ +'use strict'; + +var utils = require('./../utils'); +var settle = require('./../core/settle'); +var buildURL = require('./../helpers/buildURL'); +var http = require('http'); +var https = require('https'); +var httpFollow = require('follow-redirects').http; +var httpsFollow = require('follow-redirects').https; +var url = require('url'); +var zlib = require('zlib'); +var pkg = require('./../../package.json'); +var createError = require('../core/createError'); +var enhanceError = require('../core/enhanceError'); + +var isHttps = /https:?/; + +/*eslint consistent-return:0*/ +module.exports = function httpAdapter(config) { + return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) { + var timer; + var resolve = function resolve(value) { + clearTimeout(timer); + resolvePromise(value); + }; + var reject = function reject(value) { + clearTimeout(timer); + rejectPromise(value); + }; + var data = config.data; + var headers = config.headers; + + // Set User-Agent (required by some servers) + // Only set header if it hasn't been set in config + // See https://github.com/axios/axios/issues/69 + if (!headers['User-Agent'] && !headers['user-agent']) { + headers['User-Agent'] = 'axios/' + pkg.version; + } + + if (data && !utils.isStream(data)) { + if (Buffer.isBuffer(data)) { + // Nothing to do... + } else if (utils.isArrayBuffer(data)) { + data = Buffer.from(new Uint8Array(data)); + } else if (utils.isString(data)) { + data = Buffer.from(data, 'utf-8'); + } else { + return reject(createError( + 'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream', + config + )); + } + + // Add Content-Length header if data exists + headers['Content-Length'] = data.length; + } + + // HTTP basic authentication + var auth = undefined; + if (config.auth) { + var username = config.auth.username || ''; + var password = config.auth.password || ''; + auth = username + ':' + password; + } + + // Parse url + var parsed = url.parse(config.url); + var protocol = parsed.protocol || 'http:'; + + if (!auth && parsed.auth) { + var urlAuth = parsed.auth.split(':'); + var urlUsername = urlAuth[0] || ''; + var urlPassword = urlAuth[1] || ''; + auth = urlUsername + ':' + urlPassword; + } + + if (auth) { + delete headers.Authorization; + } + + var isHttpsRequest = isHttps.test(protocol); + var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent; + + var options = { + path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''), + method: config.method.toUpperCase(), + headers: headers, + agent: agent, + auth: auth + }; + + if (config.socketPath) { + options.socketPath = config.socketPath; + } else { + options.hostname = parsed.hostname; + options.port = parsed.port; + } + + var proxy = config.proxy; + if (!proxy && proxy !== false) { + var proxyEnv = protocol.slice(0, -1) + '_proxy'; + var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()]; + if (proxyUrl) { + var parsedProxyUrl = url.parse(proxyUrl); + var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY; + var shouldProxy = true; + + if (noProxyEnv) { + var noProxy = noProxyEnv.split(',').map(function trim(s) { + return s.trim(); + }); + + shouldProxy = !noProxy.some(function proxyMatch(proxyElement) { + if (!proxyElement) { + return false; + } + if (proxyElement === '*') { + return true; + } + if (proxyElement[0] === '.' && + parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement && + proxyElement.match(/\./g).length === parsed.hostname.match(/\./g).length) { + return true; + } + + return parsed.hostname === proxyElement; + }); + } + + + if (shouldProxy) { + proxy = { + host: parsedProxyUrl.hostname, + port: parsedProxyUrl.port + }; + + if (parsedProxyUrl.auth) { + var proxyUrlAuth = parsedProxyUrl.auth.split(':'); + proxy.auth = { + username: proxyUrlAuth[0], + password: proxyUrlAuth[1] + }; + } + } + } + } + + if (proxy) { + options.hostname = proxy.host; + options.host = proxy.host; + options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : ''); + options.port = proxy.port; + options.path = protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path; + + // Basic proxy authorization + if (proxy.auth) { + var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64'); + options.headers['Proxy-Authorization'] = 'Basic ' + base64; + } + } + + var transport; + var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true); + if (config.transport) { + transport = config.transport; + } else if (config.maxRedirects === 0) { + transport = isHttpsProxy ? https : http; + } else { + if (config.maxRedirects) { + options.maxRedirects = config.maxRedirects; + } + transport = isHttpsProxy ? httpsFollow : httpFollow; + } + + if (config.maxContentLength && config.maxContentLength > -1) { + options.maxBodyLength = config.maxContentLength; + } + + // Create the request + var req = transport.request(options, function handleResponse(res) { + if (req.aborted) return; + + // uncompress the response body transparently if required + var stream = res; + switch (res.headers['content-encoding']) { + /*eslint default-case:0*/ + case 'gzip': + case 'compress': + case 'deflate': + // add the unzipper to the body stream processing pipeline + stream = (res.statusCode === 204) ? stream : stream.pipe(zlib.createUnzip()); + + // remove the content-encoding in order to not confuse downstream operations + delete res.headers['content-encoding']; + break; + } + + // return the last request in case of redirects + var lastRequest = res.req || req; + + var response = { + status: res.statusCode, + statusText: res.statusMessage, + headers: res.headers, + config: config, + request: lastRequest + }; + + if (config.responseType === 'stream') { + response.data = stream; + settle(resolve, reject, response); + } else { + var responseBuffer = []; + stream.on('data', function handleStreamData(chunk) { + responseBuffer.push(chunk); + + // make sure the content length is not over the maxContentLength if specified + if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) { + stream.destroy(); + reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', + config, null, lastRequest)); + } + }); + + stream.on('error', function handleStreamError(err) { + if (req.aborted) return; + reject(enhanceError(err, config, null, lastRequest)); + }); + + stream.on('end', function handleStreamEnd() { + var responseData = Buffer.concat(responseBuffer); + if (config.responseType !== 'arraybuffer') { + responseData = responseData.toString(config.responseEncoding); + } + + response.data = responseData; + settle(resolve, reject, response); + }); + } + }); + + // Handle errors + req.on('error', function handleRequestError(err) { + if (req.aborted) return; + reject(enhanceError(err, config, null, req)); + }); + + // Handle request timeout + if (config.timeout) { + timer = setTimeout(function handleRequestTimeout() { + req.abort(); + reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req)); + }, config.timeout); + } + + if (config.cancelToken) { + // Handle cancellation + config.cancelToken.promise.then(function onCanceled(cancel) { + if (req.aborted) return; + + req.abort(); + reject(cancel); + }); + } + + // Send the request + if (utils.isStream(data)) { + data.on('error', function handleStreamError(err) { + reject(enhanceError(err, config, null, req)); + }).pipe(req); + } else { + req.end(data); + } + }); +}; diff --git a/WebServer/node_modules/axios/lib/adapters/xhr.js b/WebServer/node_modules/axios/lib/adapters/xhr.js new file mode 100644 index 000000000..8c98d114b --- /dev/null +++ b/WebServer/node_modules/axios/lib/adapters/xhr.js @@ -0,0 +1,174 @@ +'use strict'; + +var utils = require('./../utils'); +var settle = require('./../core/settle'); +var buildURL = require('./../helpers/buildURL'); +var parseHeaders = require('./../helpers/parseHeaders'); +var isURLSameOrigin = require('./../helpers/isURLSameOrigin'); +var createError = require('../core/createError'); + +module.exports = function xhrAdapter(config) { + return new Promise(function dispatchXhrRequest(resolve, reject) { + var requestData = config.data; + var requestHeaders = config.headers; + + if (utils.isFormData(requestData)) { + delete requestHeaders['Content-Type']; // Let the browser set it + } + + var request = new XMLHttpRequest(); + + // HTTP basic authentication + if (config.auth) { + var username = config.auth.username || ''; + var password = config.auth.password || ''; + requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); + } + + request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true); + + // Set the request timeout in MS + request.timeout = config.timeout; + + // Listen for ready state + request.onreadystatechange = function handleLoad() { + if (!request || request.readyState !== 4) { + return; + } + + // The request errored out and we didn't get a response, this will be + // handled by onerror instead + // With one exception: request that using file: protocol, most browsers + // will return status as 0 even though it's a successful request + if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { + return; + } + + // Prepare the response + var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; + var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; + var response = { + data: responseData, + status: request.status, + statusText: request.statusText, + headers: responseHeaders, + config: config, + request: request + }; + + settle(resolve, reject, response); + + // Clean up request + request = null; + }; + + // Handle browser request cancellation (as opposed to a manual cancellation) + request.onabort = function handleAbort() { + if (!request) { + return; + } + + reject(createError('Request aborted', config, 'ECONNABORTED', request)); + + // Clean up request + request = null; + }; + + // Handle low level network errors + request.onerror = function handleError() { + // Real errors are hidden from us by the browser + // onerror should only fire if it's a network error + reject(createError('Network Error', config, null, request)); + + // Clean up request + request = null; + }; + + // Handle timeout + request.ontimeout = function handleTimeout() { + reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', + request)); + + // Clean up request + request = null; + }; + + // Add xsrf header + // This is only done if running in a standard browser environment. + // Specifically not if we're in a web worker, or react-native. + if (utils.isStandardBrowserEnv()) { + var cookies = require('./../helpers/cookies'); + + // Add xsrf header + var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ? + cookies.read(config.xsrfCookieName) : + undefined; + + if (xsrfValue) { + requestHeaders[config.xsrfHeaderName] = xsrfValue; + } + } + + // Add headers to the request + if ('setRequestHeader' in request) { + utils.forEach(requestHeaders, function setRequestHeader(val, key) { + if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { + // Remove Content-Type if data is undefined + delete requestHeaders[key]; + } else { + // Otherwise add header to the request + request.setRequestHeader(key, val); + } + }); + } + + // Add withCredentials to request if needed + if (config.withCredentials) { + request.withCredentials = true; + } + + // Add responseType to request if needed + if (config.responseType) { + try { + request.responseType = config.responseType; + } catch (e) { + // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. + // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. + if (config.responseType !== 'json') { + throw e; + } + } + } + + // Handle progress if needed + if (typeof config.onDownloadProgress === 'function') { + request.addEventListener('progress', config.onDownloadProgress); + } + + // Not all browsers support upload events + if (typeof config.onUploadProgress === 'function' && request.upload) { + request.upload.addEventListener('progress', config.onUploadProgress); + } + + if (config.cancelToken) { + // Handle cancellation + config.cancelToken.promise.then(function onCanceled(cancel) { + if (!request) { + return; + } + + request.abort(); + reject(cancel); + // Clean up request + request = null; + }); + } + + if (requestData === undefined) { + requestData = null; + } + + // Send the request + request.send(requestData); + }); +}; diff --git a/WebServer/node_modules/axios/lib/axios.js b/WebServer/node_modules/axios/lib/axios.js new file mode 100644 index 000000000..814243798 --- /dev/null +++ b/WebServer/node_modules/axios/lib/axios.js @@ -0,0 +1,53 @@ +'use strict'; + +var utils = require('./utils'); +var bind = require('./helpers/bind'); +var Axios = require('./core/Axios'); +var mergeConfig = require('./core/mergeConfig'); +var defaults = require('./defaults'); + +/** + * Create an instance of Axios + * + * @param {Object} defaultConfig The default config for the instance + * @return {Axios} A new instance of Axios + */ +function createInstance(defaultConfig) { + var context = new Axios(defaultConfig); + var instance = bind(Axios.prototype.request, context); + + // Copy axios.prototype to instance + utils.extend(instance, Axios.prototype, context); + + // Copy context to instance + utils.extend(instance, context); + + return instance; +} + +// Create the default instance to be exported +var axios = createInstance(defaults); + +// Expose Axios class to allow class inheritance +axios.Axios = Axios; + +// Factory for creating new instances +axios.create = function create(instanceConfig) { + return createInstance(mergeConfig(axios.defaults, instanceConfig)); +}; + +// Expose Cancel & CancelToken +axios.Cancel = require('./cancel/Cancel'); +axios.CancelToken = require('./cancel/CancelToken'); +axios.isCancel = require('./cancel/isCancel'); + +// Expose all/spread +axios.all = function all(promises) { + return Promise.all(promises); +}; +axios.spread = require('./helpers/spread'); + +module.exports = axios; + +// Allow use of default import syntax in TypeScript +module.exports.default = axios; diff --git a/WebServer/node_modules/axios/lib/cancel/Cancel.js b/WebServer/node_modules/axios/lib/cancel/Cancel.js new file mode 100644 index 000000000..e0de4003f --- /dev/null +++ b/WebServer/node_modules/axios/lib/cancel/Cancel.js @@ -0,0 +1,19 @@ +'use strict'; + +/** + * A `Cancel` is an object that is thrown when an operation is canceled. + * + * @class + * @param {string=} message The message. + */ +function Cancel(message) { + this.message = message; +} + +Cancel.prototype.toString = function toString() { + return 'Cancel' + (this.message ? ': ' + this.message : ''); +}; + +Cancel.prototype.__CANCEL__ = true; + +module.exports = Cancel; diff --git a/WebServer/node_modules/axios/lib/cancel/CancelToken.js b/WebServer/node_modules/axios/lib/cancel/CancelToken.js new file mode 100644 index 000000000..6b46e6662 --- /dev/null +++ b/WebServer/node_modules/axios/lib/cancel/CancelToken.js @@ -0,0 +1,57 @@ +'use strict'; + +var Cancel = require('./Cancel'); + +/** + * A `CancelToken` is an object that can be used to request cancellation of an operation. + * + * @class + * @param {Function} executor The executor function. + */ +function CancelToken(executor) { + if (typeof executor !== 'function') { + throw new TypeError('executor must be a function.'); + } + + var resolvePromise; + this.promise = new Promise(function promiseExecutor(resolve) { + resolvePromise = resolve; + }); + + var token = this; + executor(function cancel(message) { + if (token.reason) { + // Cancellation has already been requested + return; + } + + token.reason = new Cancel(message); + resolvePromise(token.reason); + }); +} + +/** + * Throws a `Cancel` if cancellation has been requested. + */ +CancelToken.prototype.throwIfRequested = function throwIfRequested() { + if (this.reason) { + throw this.reason; + } +}; + +/** + * Returns an object that contains a new `CancelToken` and a function that, when called, + * cancels the `CancelToken`. + */ +CancelToken.source = function source() { + var cancel; + var token = new CancelToken(function executor(c) { + cancel = c; + }); + return { + token: token, + cancel: cancel + }; +}; + +module.exports = CancelToken; diff --git a/WebServer/node_modules/axios/lib/cancel/isCancel.js b/WebServer/node_modules/axios/lib/cancel/isCancel.js new file mode 100644 index 000000000..051f3ae4c --- /dev/null +++ b/WebServer/node_modules/axios/lib/cancel/isCancel.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function isCancel(value) { + return !!(value && value.__CANCEL__); +}; diff --git a/WebServer/node_modules/axios/lib/core/Axios.js b/WebServer/node_modules/axios/lib/core/Axios.js new file mode 100644 index 000000000..811cb3649 --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/Axios.js @@ -0,0 +1,86 @@ +'use strict'; + +var utils = require('./../utils'); +var buildURL = require('../helpers/buildURL'); +var InterceptorManager = require('./InterceptorManager'); +var dispatchRequest = require('./dispatchRequest'); +var mergeConfig = require('./mergeConfig'); + +/** + * Create a new instance of Axios + * + * @param {Object} instanceConfig The default config for the instance + */ +function Axios(instanceConfig) { + this.defaults = instanceConfig; + this.interceptors = { + request: new InterceptorManager(), + response: new InterceptorManager() + }; +} + +/** + * Dispatch a request + * + * @param {Object} config The config specific for this request (merged with this.defaults) + */ +Axios.prototype.request = function request(config) { + /*eslint no-param-reassign:0*/ + // Allow for axios('example/url'[, config]) a la fetch API + if (typeof config === 'string') { + config = arguments[1] || {}; + config.url = arguments[0]; + } else { + config = config || {}; + } + + config = mergeConfig(this.defaults, config); + config.method = config.method ? config.method.toLowerCase() : 'get'; + + // Hook up interceptors middleware + var chain = [dispatchRequest, undefined]; + var promise = Promise.resolve(config); + + this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { + chain.unshift(interceptor.fulfilled, interceptor.rejected); + }); + + this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { + chain.push(interceptor.fulfilled, interceptor.rejected); + }); + + while (chain.length) { + promise = promise.then(chain.shift(), chain.shift()); + } + + return promise; +}; + +Axios.prototype.getUri = function getUri(config) { + config = mergeConfig(this.defaults, config); + return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''); +}; + +// Provide aliases for supported request methods +utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, config) { + return this.request(utils.merge(config || {}, { + method: method, + url: url + })); + }; +}); + +utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + /*eslint func-names:0*/ + Axios.prototype[method] = function(url, data, config) { + return this.request(utils.merge(config || {}, { + method: method, + url: url, + data: data + })); + }; +}); + +module.exports = Axios; diff --git a/WebServer/node_modules/axios/lib/core/InterceptorManager.js b/WebServer/node_modules/axios/lib/core/InterceptorManager.js new file mode 100644 index 000000000..50d667bb4 --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/InterceptorManager.js @@ -0,0 +1,52 @@ +'use strict'; + +var utils = require('./../utils'); + +function InterceptorManager() { + this.handlers = []; +} + +/** + * Add a new interceptor to the stack + * + * @param {Function} fulfilled The function to handle `then` for a `Promise` + * @param {Function} rejected The function to handle `reject` for a `Promise` + * + * @return {Number} An ID used to remove interceptor later + */ +InterceptorManager.prototype.use = function use(fulfilled, rejected) { + this.handlers.push({ + fulfilled: fulfilled, + rejected: rejected + }); + return this.handlers.length - 1; +}; + +/** + * Remove an interceptor from the stack + * + * @param {Number} id The ID that was returned by `use` + */ +InterceptorManager.prototype.eject = function eject(id) { + if (this.handlers[id]) { + this.handlers[id] = null; + } +}; + +/** + * Iterate over all the registered interceptors + * + * This method is particularly useful for skipping over any + * interceptors that may have become `null` calling `eject`. + * + * @param {Function} fn The function to call for each interceptor + */ +InterceptorManager.prototype.forEach = function forEach(fn) { + utils.forEach(this.handlers, function forEachHandler(h) { + if (h !== null) { + fn(h); + } + }); +}; + +module.exports = InterceptorManager; diff --git a/WebServer/node_modules/axios/lib/core/createError.js b/WebServer/node_modules/axios/lib/core/createError.js new file mode 100644 index 000000000..933680f69 --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/createError.js @@ -0,0 +1,18 @@ +'use strict'; + +var enhanceError = require('./enhanceError'); + +/** + * Create an Error with the specified message, config, error code, request and response. + * + * @param {string} message The error message. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The created error. + */ +module.exports = function createError(message, config, code, request, response) { + var error = new Error(message); + return enhanceError(error, config, code, request, response); +}; diff --git a/WebServer/node_modules/axios/lib/core/dispatchRequest.js b/WebServer/node_modules/axios/lib/core/dispatchRequest.js new file mode 100644 index 000000000..9ea70f228 --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/dispatchRequest.js @@ -0,0 +1,86 @@ +'use strict'; + +var utils = require('./../utils'); +var transformData = require('./transformData'); +var isCancel = require('../cancel/isCancel'); +var defaults = require('../defaults'); +var isAbsoluteURL = require('./../helpers/isAbsoluteURL'); +var combineURLs = require('./../helpers/combineURLs'); + +/** + * Throws a `Cancel` if cancellation has been requested. + */ +function throwIfCancellationRequested(config) { + if (config.cancelToken) { + config.cancelToken.throwIfRequested(); + } +} + +/** + * Dispatch a request to the server using the configured adapter. + * + * @param {object} config The config that is to be used for the request + * @returns {Promise} The Promise to be fulfilled + */ +module.exports = function dispatchRequest(config) { + throwIfCancellationRequested(config); + + // Support baseURL config + if (config.baseURL && !isAbsoluteURL(config.url)) { + config.url = combineURLs(config.baseURL, config.url); + } + + // Ensure headers exist + config.headers = config.headers || {}; + + // Transform request data + config.data = transformData( + config.data, + config.headers, + config.transformRequest + ); + + // Flatten headers + config.headers = utils.merge( + config.headers.common || {}, + config.headers[config.method] || {}, + config.headers || {} + ); + + utils.forEach( + ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], + function cleanHeaderConfig(method) { + delete config.headers[method]; + } + ); + + var adapter = config.adapter || defaults.adapter; + + return adapter(config).then(function onAdapterResolution(response) { + throwIfCancellationRequested(config); + + // Transform response data + response.data = transformData( + response.data, + response.headers, + config.transformResponse + ); + + return response; + }, function onAdapterRejection(reason) { + if (!isCancel(reason)) { + throwIfCancellationRequested(config); + + // Transform response data + if (reason && reason.response) { + reason.response.data = transformData( + reason.response.data, + reason.response.headers, + config.transformResponse + ); + } + } + + return Promise.reject(reason); + }); +}; diff --git a/WebServer/node_modules/axios/lib/core/enhanceError.js b/WebServer/node_modules/axios/lib/core/enhanceError.js new file mode 100644 index 000000000..02fbbd6a9 --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/enhanceError.js @@ -0,0 +1,42 @@ +'use strict'; + +/** + * Update an Error with the specified config, error code, and response. + * + * @param {Error} error The error to update. + * @param {Object} config The config. + * @param {string} [code] The error code (for example, 'ECONNABORTED'). + * @param {Object} [request] The request. + * @param {Object} [response] The response. + * @returns {Error} The error. + */ +module.exports = function enhanceError(error, config, code, request, response) { + error.config = config; + if (code) { + error.code = code; + } + + error.request = request; + error.response = response; + error.isAxiosError = true; + + error.toJSON = function() { + return { + // Standard + message: this.message, + name: this.name, + // Microsoft + description: this.description, + number: this.number, + // Mozilla + fileName: this.fileName, + lineNumber: this.lineNumber, + columnNumber: this.columnNumber, + stack: this.stack, + // Axios + config: this.config, + code: this.code + }; + }; + return error; +}; diff --git a/WebServer/node_modules/axios/lib/core/mergeConfig.js b/WebServer/node_modules/axios/lib/core/mergeConfig.js new file mode 100644 index 000000000..6097a3e58 --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/mergeConfig.js @@ -0,0 +1,51 @@ +'use strict'; + +var utils = require('../utils'); + +/** + * Config-specific merge-function which creates a new config-object + * by merging two configuration objects together. + * + * @param {Object} config1 + * @param {Object} config2 + * @returns {Object} New object resulting from merging config2 to config1 + */ +module.exports = function mergeConfig(config1, config2) { + // eslint-disable-next-line no-param-reassign + config2 = config2 || {}; + var config = {}; + + utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) { + if (typeof config2[prop] !== 'undefined') { + config[prop] = config2[prop]; + } + }); + + utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) { + if (utils.isObject(config2[prop])) { + config[prop] = utils.deepMerge(config1[prop], config2[prop]); + } else if (typeof config2[prop] !== 'undefined') { + config[prop] = config2[prop]; + } else if (utils.isObject(config1[prop])) { + config[prop] = utils.deepMerge(config1[prop]); + } else if (typeof config1[prop] !== 'undefined') { + config[prop] = config1[prop]; + } + }); + + utils.forEach([ + 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', + 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', + 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength', + 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken', + 'socketPath' + ], function defaultToConfig2(prop) { + if (typeof config2[prop] !== 'undefined') { + config[prop] = config2[prop]; + } else if (typeof config1[prop] !== 'undefined') { + config[prop] = config1[prop]; + } + }); + + return config; +}; diff --git a/WebServer/node_modules/axios/lib/core/settle.js b/WebServer/node_modules/axios/lib/core/settle.js new file mode 100644 index 000000000..071d9e3e8 --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/settle.js @@ -0,0 +1,25 @@ +'use strict'; + +var createError = require('./createError'); + +/** + * Resolve or reject a Promise based on response status. + * + * @param {Function} resolve A function that resolves the promise. + * @param {Function} reject A function that rejects the promise. + * @param {object} response The response. + */ +module.exports = function settle(resolve, reject, response) { + var validateStatus = response.config.validateStatus; + if (!validateStatus || validateStatus(response.status)) { + resolve(response); + } else { + reject(createError( + 'Request failed with status code ' + response.status, + response.config, + null, + response.request, + response + )); + } +}; diff --git a/WebServer/node_modules/axios/lib/core/transformData.js b/WebServer/node_modules/axios/lib/core/transformData.js new file mode 100644 index 000000000..e0653620e --- /dev/null +++ b/WebServer/node_modules/axios/lib/core/transformData.js @@ -0,0 +1,20 @@ +'use strict'; + +var utils = require('./../utils'); + +/** + * Transform the data for a request or a response + * + * @param {Object|String} data The data to be transformed + * @param {Array} headers The headers for the request or response + * @param {Array|Function} fns A single function or Array of functions + * @returns {*} The resulting transformed data + */ +module.exports = function transformData(data, headers, fns) { + /*eslint no-param-reassign:0*/ + utils.forEach(fns, function transform(fn) { + data = fn(data, headers); + }); + + return data; +}; diff --git a/WebServer/node_modules/axios/lib/defaults.js b/WebServer/node_modules/axios/lib/defaults.js new file mode 100644 index 000000000..7cb8a2504 --- /dev/null +++ b/WebServer/node_modules/axios/lib/defaults.js @@ -0,0 +1,98 @@ +'use strict'; + +var utils = require('./utils'); +var normalizeHeaderName = require('./helpers/normalizeHeaderName'); + +var DEFAULT_CONTENT_TYPE = { + 'Content-Type': 'application/x-www-form-urlencoded' +}; + +function setContentTypeIfUnset(headers, value) { + if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { + headers['Content-Type'] = value; + } +} + +function getDefaultAdapter() { + var adapter; + // Only Node.JS has a process variable that is of [[Class]] process + if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { + // For node use HTTP adapter + adapter = require('./adapters/http'); + } else if (typeof XMLHttpRequest !== 'undefined') { + // For browsers use XHR adapter + adapter = require('./adapters/xhr'); + } + return adapter; +} + +var defaults = { + adapter: getDefaultAdapter(), + + transformRequest: [function transformRequest(data, headers) { + normalizeHeaderName(headers, 'Accept'); + normalizeHeaderName(headers, 'Content-Type'); + if (utils.isFormData(data) || + utils.isArrayBuffer(data) || + utils.isBuffer(data) || + utils.isStream(data) || + utils.isFile(data) || + utils.isBlob(data) + ) { + return data; + } + if (utils.isArrayBufferView(data)) { + return data.buffer; + } + if (utils.isURLSearchParams(data)) { + setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); + return data.toString(); + } + if (utils.isObject(data)) { + setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); + return JSON.stringify(data); + } + return data; + }], + + transformResponse: [function transformResponse(data) { + /*eslint no-param-reassign:0*/ + if (typeof data === 'string') { + try { + data = JSON.parse(data); + } catch (e) { /* Ignore */ } + } + return data; + }], + + /** + * A timeout in milliseconds to abort a request. If set to 0 (default) a + * timeout is not created. + */ + timeout: 0, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', + + maxContentLength: -1, + + validateStatus: function validateStatus(status) { + return status >= 200 && status < 300; + } +}; + +defaults.headers = { + common: { + 'Accept': 'application/json, text/plain, */*' + } +}; + +utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { + defaults.headers[method] = {}; +}); + +utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); +}); + +module.exports = defaults; diff --git a/WebServer/node_modules/axios/lib/helpers/bind.js b/WebServer/node_modules/axios/lib/helpers/bind.js new file mode 100644 index 000000000..6147c608e --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/bind.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function bind(fn, thisArg) { + return function wrap() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + return fn.apply(thisArg, args); + }; +}; diff --git a/WebServer/node_modules/axios/lib/helpers/buildURL.js b/WebServer/node_modules/axios/lib/helpers/buildURL.js new file mode 100644 index 000000000..8c40e4096 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/buildURL.js @@ -0,0 +1,71 @@ +'use strict'; + +var utils = require('./../utils'); + +function encode(val) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, '+'). + replace(/%5B/gi, '['). + replace(/%5D/gi, ']'); +} + +/** + * Build a URL by appending params to the end + * + * @param {string} url The base of the url (e.g., http://www.google.com) + * @param {object} [params] The params to be appended + * @returns {string} The formatted url + */ +module.exports = function buildURL(url, params, paramsSerializer) { + /*eslint no-param-reassign:0*/ + if (!params) { + return url; + } + + var serializedParams; + if (paramsSerializer) { + serializedParams = paramsSerializer(params); + } else if (utils.isURLSearchParams(params)) { + serializedParams = params.toString(); + } else { + var parts = []; + + utils.forEach(params, function serialize(val, key) { + if (val === null || typeof val === 'undefined') { + return; + } + + if (utils.isArray(val)) { + key = key + '[]'; + } else { + val = [val]; + } + + utils.forEach(val, function parseValue(v) { + if (utils.isDate(v)) { + v = v.toISOString(); + } else if (utils.isObject(v)) { + v = JSON.stringify(v); + } + parts.push(encode(key) + '=' + encode(v)); + }); + }); + + serializedParams = parts.join('&'); + } + + if (serializedParams) { + var hashmarkIndex = url.indexOf('#'); + if (hashmarkIndex !== -1) { + url = url.slice(0, hashmarkIndex); + } + + url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; + } + + return url; +}; diff --git a/WebServer/node_modules/axios/lib/helpers/combineURLs.js b/WebServer/node_modules/axios/lib/helpers/combineURLs.js new file mode 100644 index 000000000..f1b58a586 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/combineURLs.js @@ -0,0 +1,14 @@ +'use strict'; + +/** + * Creates a new URL by combining the specified URLs + * + * @param {string} baseURL The base URL + * @param {string} relativeURL The relative URL + * @returns {string} The combined URL + */ +module.exports = function combineURLs(baseURL, relativeURL) { + return relativeURL + ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') + : baseURL; +}; diff --git a/WebServer/node_modules/axios/lib/helpers/cookies.js b/WebServer/node_modules/axios/lib/helpers/cookies.js new file mode 100644 index 000000000..5a8a66661 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/cookies.js @@ -0,0 +1,53 @@ +'use strict'; + +var utils = require('./../utils'); + +module.exports = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs support document.cookie + (function standardBrowserEnv() { + return { + write: function write(name, value, expires, path, domain, secure) { + var cookie = []; + cookie.push(name + '=' + encodeURIComponent(value)); + + if (utils.isNumber(expires)) { + cookie.push('expires=' + new Date(expires).toGMTString()); + } + + if (utils.isString(path)) { + cookie.push('path=' + path); + } + + if (utils.isString(domain)) { + cookie.push('domain=' + domain); + } + + if (secure === true) { + cookie.push('secure'); + } + + document.cookie = cookie.join('; '); + }, + + read: function read(name) { + var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); + return (match ? decodeURIComponent(match[3]) : null); + }, + + remove: function remove(name) { + this.write(name, '', Date.now() - 86400000); + } + }; + })() : + + // Non standard browser env (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return { + write: function write() {}, + read: function read() { return null; }, + remove: function remove() {} + }; + })() +); diff --git a/WebServer/node_modules/axios/lib/helpers/deprecatedMethod.js b/WebServer/node_modules/axios/lib/helpers/deprecatedMethod.js new file mode 100644 index 000000000..ed40965ba --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/deprecatedMethod.js @@ -0,0 +1,24 @@ +'use strict'; + +/*eslint no-console:0*/ + +/** + * Supply a warning to the developer that a method they are using + * has been deprecated. + * + * @param {string} method The name of the deprecated method + * @param {string} [instead] The alternate method to use if applicable + * @param {string} [docs] The documentation URL to get further details + */ +module.exports = function deprecatedMethod(method, instead, docs) { + try { + console.warn( + 'DEPRECATED method `' + method + '`.' + + (instead ? ' Use `' + instead + '` instead.' : '') + + ' This method will be removed in a future release.'); + + if (docs) { + console.warn('For more information about usage see ' + docs); + } + } catch (e) { /* Ignore */ } +}; diff --git a/WebServer/node_modules/axios/lib/helpers/isAbsoluteURL.js b/WebServer/node_modules/axios/lib/helpers/isAbsoluteURL.js new file mode 100644 index 000000000..d33e99275 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/isAbsoluteURL.js @@ -0,0 +1,14 @@ +'use strict'; + +/** + * Determines whether the specified URL is absolute + * + * @param {string} url The URL to test + * @returns {boolean} True if the specified URL is absolute, otherwise false + */ +module.exports = function isAbsoluteURL(url) { + // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed + // by any combination of letters, digits, plus, period, or hyphen. + return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); +}; diff --git a/WebServer/node_modules/axios/lib/helpers/isURLSameOrigin.js b/WebServer/node_modules/axios/lib/helpers/isURLSameOrigin.js new file mode 100644 index 000000000..f1d89ad19 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/isURLSameOrigin.js @@ -0,0 +1,68 @@ +'use strict'; + +var utils = require('./../utils'); + +module.exports = ( + utils.isStandardBrowserEnv() ? + + // Standard browser envs have full support of the APIs needed to test + // whether the request URL is of the same origin as current location. + (function standardBrowserEnv() { + var msie = /(msie|trident)/i.test(navigator.userAgent); + var urlParsingNode = document.createElement('a'); + var originURL; + + /** + * Parse a URL to discover it's components + * + * @param {String} url The URL to be parsed + * @returns {Object} + */ + function resolveURL(url) { + var href = url; + + if (msie) { + // IE needs attribute set twice to normalize properties + urlParsingNode.setAttribute('href', href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') ? + urlParsingNode.pathname : + '/' + urlParsingNode.pathname + }; + } + + originURL = resolveURL(window.location.href); + + /** + * Determine if a URL shares the same origin as the current location + * + * @param {String} requestURL The URL to test + * @returns {boolean} True if URL shares the same origin, otherwise false + */ + return function isURLSameOrigin(requestURL) { + var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; + return (parsed.protocol === originURL.protocol && + parsed.host === originURL.host); + }; + })() : + + // Non standard browser envs (web workers, react-native) lack needed support. + (function nonStandardBrowserEnv() { + return function isURLSameOrigin() { + return true; + }; + })() +); diff --git a/WebServer/node_modules/axios/lib/helpers/normalizeHeaderName.js b/WebServer/node_modules/axios/lib/helpers/normalizeHeaderName.js new file mode 100644 index 000000000..738c9fe40 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/normalizeHeaderName.js @@ -0,0 +1,12 @@ +'use strict'; + +var utils = require('../utils'); + +module.exports = function normalizeHeaderName(headers, normalizedName) { + utils.forEach(headers, function processHeader(value, name) { + if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { + headers[normalizedName] = value; + delete headers[name]; + } + }); +}; diff --git a/WebServer/node_modules/axios/lib/helpers/parseHeaders.js b/WebServer/node_modules/axios/lib/helpers/parseHeaders.js new file mode 100644 index 000000000..8af2cc7f1 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/parseHeaders.js @@ -0,0 +1,53 @@ +'use strict'; + +var utils = require('./../utils'); + +// Headers whose duplicates are ignored by node +// c.f. https://nodejs.org/api/http.html#http_message_headers +var ignoreDuplicateOf = [ + 'age', 'authorization', 'content-length', 'content-type', 'etag', + 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', + 'last-modified', 'location', 'max-forwards', 'proxy-authorization', + 'referer', 'retry-after', 'user-agent' +]; + +/** + * Parse headers into an object + * + * ``` + * Date: Wed, 27 Aug 2014 08:58:49 GMT + * Content-Type: application/json + * Connection: keep-alive + * Transfer-Encoding: chunked + * ``` + * + * @param {String} headers Headers needing to be parsed + * @returns {Object} Headers parsed into an object + */ +module.exports = function parseHeaders(headers) { + var parsed = {}; + var key; + var val; + var i; + + if (!headers) { return parsed; } + + utils.forEach(headers.split('\n'), function parser(line) { + i = line.indexOf(':'); + key = utils.trim(line.substr(0, i)).toLowerCase(); + val = utils.trim(line.substr(i + 1)); + + if (key) { + if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) { + return; + } + if (key === 'set-cookie') { + parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]); + } else { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + } + }); + + return parsed; +}; diff --git a/WebServer/node_modules/axios/lib/helpers/spread.js b/WebServer/node_modules/axios/lib/helpers/spread.js new file mode 100644 index 000000000..25e3cdd39 --- /dev/null +++ b/WebServer/node_modules/axios/lib/helpers/spread.js @@ -0,0 +1,27 @@ +'use strict'; + +/** + * Syntactic sugar for invoking a function and expanding an array for arguments. + * + * Common use case would be to use `Function.prototype.apply`. + * + * ```js + * function f(x, y, z) {} + * var args = [1, 2, 3]; + * f.apply(null, args); + * ``` + * + * With `spread` this example can be re-written. + * + * ```js + * spread(function(x, y, z) {})([1, 2, 3]); + * ``` + * + * @param {Function} callback + * @returns {Function} + */ +module.exports = function spread(callback) { + return function wrap(arr) { + return callback.apply(null, arr); + }; +}; diff --git a/WebServer/node_modules/axios/lib/utils.js b/WebServer/node_modules/axios/lib/utils.js new file mode 100644 index 000000000..8946055da --- /dev/null +++ b/WebServer/node_modules/axios/lib/utils.js @@ -0,0 +1,334 @@ +'use strict'; + +var bind = require('./helpers/bind'); +var isBuffer = require('is-buffer'); + +/*global toString:true*/ + +// utils is a library of generic helper functions non-specific to axios + +var toString = Object.prototype.toString; + +/** + * Determine if a value is an Array + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Array, otherwise false + */ +function isArray(val) { + return toString.call(val) === '[object Array]'; +} + +/** + * Determine if a value is an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an ArrayBuffer, otherwise false + */ +function isArrayBuffer(val) { + return toString.call(val) === '[object ArrayBuffer]'; +} + +/** + * Determine if a value is a FormData + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an FormData, otherwise false + */ +function isFormData(val) { + return (typeof FormData !== 'undefined') && (val instanceof FormData); +} + +/** + * Determine if a value is a view on an ArrayBuffer + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false + */ +function isArrayBufferView(val) { + var result; + if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { + result = ArrayBuffer.isView(val); + } else { + result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); + } + return result; +} + +/** + * Determine if a value is a String + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a String, otherwise false + */ +function isString(val) { + return typeof val === 'string'; +} + +/** + * Determine if a value is a Number + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Number, otherwise false + */ +function isNumber(val) { + return typeof val === 'number'; +} + +/** + * Determine if a value is undefined + * + * @param {Object} val The value to test + * @returns {boolean} True if the value is undefined, otherwise false + */ +function isUndefined(val) { + return typeof val === 'undefined'; +} + +/** + * Determine if a value is an Object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is an Object, otherwise false + */ +function isObject(val) { + return val !== null && typeof val === 'object'; +} + +/** + * Determine if a value is a Date + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Date, otherwise false + */ +function isDate(val) { + return toString.call(val) === '[object Date]'; +} + +/** + * Determine if a value is a File + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a File, otherwise false + */ +function isFile(val) { + return toString.call(val) === '[object File]'; +} + +/** + * Determine if a value is a Blob + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Blob, otherwise false + */ +function isBlob(val) { + return toString.call(val) === '[object Blob]'; +} + +/** + * Determine if a value is a Function + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Function, otherwise false + */ +function isFunction(val) { + return toString.call(val) === '[object Function]'; +} + +/** + * Determine if a value is a Stream + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a Stream, otherwise false + */ +function isStream(val) { + return isObject(val) && isFunction(val.pipe); +} + +/** + * Determine if a value is a URLSearchParams object + * + * @param {Object} val The value to test + * @returns {boolean} True if value is a URLSearchParams object, otherwise false + */ +function isURLSearchParams(val) { + return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; +} + +/** + * Trim excess whitespace off the beginning and end of a string + * + * @param {String} str The String to trim + * @returns {String} The String freed of excess whitespace + */ +function trim(str) { + return str.replace(/^\s*/, '').replace(/\s*$/, ''); +} + +/** + * Determine if we're running in a standard browser environment + * + * This allows axios to run in a web worker, and react-native. + * Both environments support XMLHttpRequest, but not fully standard globals. + * + * web workers: + * typeof window -> undefined + * typeof document -> undefined + * + * react-native: + * navigator.product -> 'ReactNative' + * nativescript + * navigator.product -> 'NativeScript' or 'NS' + */ +function isStandardBrowserEnv() { + if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' || + navigator.product === 'NativeScript' || + navigator.product === 'NS')) { + return false; + } + return ( + typeof window !== 'undefined' && + typeof document !== 'undefined' + ); +} + +/** + * Iterate over an Array or an Object invoking a function for each item. + * + * If `obj` is an Array callback will be called passing + * the value, index, and complete array for each item. + * + * If 'obj' is an Object callback will be called passing + * the value, key, and complete object for each property. + * + * @param {Object|Array} obj The object to iterate + * @param {Function} fn The callback to invoke for each item + */ +function forEach(obj, fn) { + // Don't bother if no value provided + if (obj === null || typeof obj === 'undefined') { + return; + } + + // Force an array if not already something iterable + if (typeof obj !== 'object') { + /*eslint no-param-reassign:0*/ + obj = [obj]; + } + + if (isArray(obj)) { + // Iterate over array values + for (var i = 0, l = obj.length; i < l; i++) { + fn.call(null, obj[i], i, obj); + } + } else { + // Iterate over object keys + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + fn.call(null, obj[key], key, obj); + } + } + } +} + +/** + * Accepts varargs expecting each argument to be an object, then + * immutably merges the properties of each object and returns result. + * + * When multiple objects contain the same key the later object in + * the arguments list will take precedence. + * + * Example: + * + * ```js + * var result = merge({foo: 123}, {foo: 456}); + * console.log(result.foo); // outputs 456 + * ``` + * + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ +function merge(/* obj1, obj2, obj3, ... */) { + var result = {}; + function assignValue(val, key) { + if (typeof result[key] === 'object' && typeof val === 'object') { + result[key] = merge(result[key], val); + } else { + result[key] = val; + } + } + + for (var i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue); + } + return result; +} + +/** + * Function equal to merge with the difference being that no reference + * to original objects is kept. + * + * @see merge + * @param {Object} obj1 Object to merge + * @returns {Object} Result of all merge properties + */ +function deepMerge(/* obj1, obj2, obj3, ... */) { + var result = {}; + function assignValue(val, key) { + if (typeof result[key] === 'object' && typeof val === 'object') { + result[key] = deepMerge(result[key], val); + } else if (typeof val === 'object') { + result[key] = deepMerge({}, val); + } else { + result[key] = val; + } + } + + for (var i = 0, l = arguments.length; i < l; i++) { + forEach(arguments[i], assignValue); + } + return result; +} + +/** + * Extends object a by mutably adding to it the properties of object b. + * + * @param {Object} a The object to be extended + * @param {Object} b The object to copy properties from + * @param {Object} thisArg The object to bind function to + * @return {Object} The resulting value of object a + */ +function extend(a, b, thisArg) { + forEach(b, function assignValue(val, key) { + if (thisArg && typeof val === 'function') { + a[key] = bind(val, thisArg); + } else { + a[key] = val; + } + }); + return a; +} + +module.exports = { + isArray: isArray, + isArrayBuffer: isArrayBuffer, + isBuffer: isBuffer, + isFormData: isFormData, + isArrayBufferView: isArrayBufferView, + isString: isString, + isNumber: isNumber, + isObject: isObject, + isUndefined: isUndefined, + isDate: isDate, + isFile: isFile, + isBlob: isBlob, + isFunction: isFunction, + isStream: isStream, + isURLSearchParams: isURLSearchParams, + isStandardBrowserEnv: isStandardBrowserEnv, + forEach: forEach, + merge: merge, + deepMerge: deepMerge, + extend: extend, + trim: trim +}; diff --git a/WebServer/node_modules/axios/package.json b/WebServer/node_modules/axios/package.json new file mode 100644 index 000000000..7b7ba43e7 --- /dev/null +++ b/WebServer/node_modules/axios/package.json @@ -0,0 +1,115 @@ +{ + "_args": [ + [ + "axios@0.19.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "axios@0.19.0", + "_id": "axios@0.19.0", + "_inBundle": false, + "_integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "_location": "/axios", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "axios@0.19.0", + "name": "axios", + "escapedName": "axios", + "rawSpec": "0.19.0", + "saveSpec": null, + "fetchSpec": "0.19.0" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "_spec": "0.19.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Matt Zabriskie" + }, + "browser": { + "./lib/adapters/http.js": "./lib/adapters/xhr.js" + }, + "bugs": { + "url": "https://github.com/axios/axios/issues" + }, + "bundlesize": [ + { + "path": "./dist/axios.min.js", + "threshold": "5kB" + } + ], + "dependencies": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "description": "Promise based HTTP client for the browser and node.js", + "devDependencies": { + "bundlesize": "^0.17.0", + "coveralls": "^3.0.0", + "es6-promise": "^4.2.4", + "grunt": "^1.0.2", + "grunt-banner": "^0.6.0", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.1.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-eslint": "^20.1.0", + "grunt-karma": "^2.0.0", + "grunt-mocha-test": "^0.13.3", + "grunt-ts": "^6.0.0-beta.19", + "grunt-webpack": "^1.0.18", + "istanbul-instrumenter-loader": "^1.0.0", + "jasmine-core": "^2.4.1", + "karma": "^1.3.0", + "karma-chrome-launcher": "^2.2.0", + "karma-coverage": "^1.1.1", + "karma-firefox-launcher": "^1.1.0", + "karma-jasmine": "^1.1.1", + "karma-jasmine-ajax": "^0.1.13", + "karma-opera-launcher": "^1.0.0", + "karma-safari-launcher": "^1.0.0", + "karma-sauce-launcher": "^1.2.0", + "karma-sinon": "^1.0.5", + "karma-sourcemap-loader": "^0.3.7", + "karma-webpack": "^1.7.0", + "load-grunt-tasks": "^3.5.2", + "minimist": "^1.2.0", + "mocha": "^5.2.0", + "sinon": "^4.5.0", + "typescript": "^2.8.1", + "url-search-params": "^0.10.0", + "webpack": "^1.13.1", + "webpack-dev-server": "^1.14.1" + }, + "homepage": "https://github.com/axios/axios", + "keywords": [ + "xhr", + "http", + "ajax", + "promise", + "node" + ], + "license": "MIT", + "main": "index.js", + "name": "axios", + "repository": { + "type": "git", + "url": "git+https://github.com/axios/axios.git" + }, + "scripts": { + "build": "NODE_ENV=production grunt build", + "coveralls": "cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", + "examples": "node ./examples/server.js", + "fix": "eslint --fix lib/**/*.js", + "postversion": "git push && git push --tags", + "preversion": "npm test", + "start": "node ./sandbox/server.js", + "test": "grunt test && bundlesize", + "version": "npm run build && grunt version && git add -A dist && git add CHANGELOG.md bower.json package.json" + }, + "typings": "./index.d.ts", + "version": "0.19.0" +} diff --git a/WebServer/node_modules/balanced-match/index.js b/WebServer/node_modules/balanced-match/index.js new file mode 100644 index 000000000..1685a7629 --- /dev/null +++ b/WebServer/node_modules/balanced-match/index.js @@ -0,0 +1,59 @@ +'use strict'; +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); + + var r = range(a, b, str); + + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} + +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; +} + +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; + + if (ai >= 0 && bi > 0) { + begs = []; + left = str.length; + + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + + bi = str.indexOf(b, i + 1); + } + + i = ai < bi && ai >= 0 ? ai : bi; + } + + if (begs.length) { + result = [ left, right ]; + } + } + + return result; +} diff --git a/WebServer/node_modules/balanced-match/package.json b/WebServer/node_modules/balanced-match/package.json new file mode 100644 index 000000000..659e384db --- /dev/null +++ b/WebServer/node_modules/balanced-match/package.json @@ -0,0 +1,80 @@ +{ + "_args": [ + [ + "balanced-match@1.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "balanced-match@1.0.0", + "_id": "balanced-match@1.0.0", + "_inBundle": false, + "_integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "_location": "/balanced-match", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "balanced-match@1.0.0", + "name": "balanced-match", + "escapedName": "balanced-match", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/brace-expansion" + ], + "_resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "_spec": "1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "bugs": { + "url": "https://github.com/juliangruber/balanced-match/issues" + }, + "dependencies": {}, + "description": "Match balanced character pairs, like \"{\" and \"}\"", + "devDependencies": { + "matcha": "^0.7.0", + "tape": "^4.6.0" + }, + "homepage": "https://github.com/juliangruber/balanced-match", + "keywords": [ + "match", + "regexp", + "test", + "balanced", + "parse" + ], + "license": "MIT", + "main": "index.js", + "name": "balanced-match", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/balanced-match.git" + }, + "scripts": { + "bench": "make bench", + "test": "make test" + }, + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + }, + "version": "1.0.0" +} diff --git a/WebServer/node_modules/brace-expansion/index.js b/WebServer/node_modules/brace-expansion/index.js new file mode 100644 index 000000000..0478be81e --- /dev/null +++ b/WebServer/node_modules/brace-expansion/index.js @@ -0,0 +1,201 @@ +var concatMap = require('concat-map'); +var balanced = require('balanced-match'); + +module.exports = expandTop; + +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; + +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); +} + +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} + +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} + + +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; + + var parts = []; + var m = balanced('{', '}', str); + + if (!m) + return str.split(','); + + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); + + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } + + parts.push.apply(parts, p); + + return parts; +} + +function expandTop(str) { + if (!str) + return []; + + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); + } + + return expand(escapeBraces(str), true).map(unescapeBraces); +} + +function identity(e) { + return e; +} + +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} + +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} + +function expand(str, isTop) { + var expansions = []; + + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; + + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); + } + return [str]; + } + + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } + } + } + + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; + + var N; + + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); + + N = []; + + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } + + return expansions; +} + diff --git a/WebServer/node_modules/brace-expansion/package.json b/WebServer/node_modules/brace-expansion/package.json new file mode 100644 index 000000000..04a2a77df --- /dev/null +++ b/WebServer/node_modules/brace-expansion/package.json @@ -0,0 +1,78 @@ +{ + "_args": [ + [ + "brace-expansion@1.1.11", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "brace-expansion@1.1.11", + "_id": "brace-expansion@1.1.11", + "_inBundle": false, + "_integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "_location": "/brace-expansion", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "brace-expansion@1.1.11", + "name": "brace-expansion", + "escapedName": "brace-expansion", + "rawSpec": "1.1.11", + "saveSpec": null, + "fetchSpec": "1.1.11" + }, + "_requiredBy": [ + "/minimatch" + ], + "_resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "_spec": "1.1.11", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" + }, + "bugs": { + "url": "https://github.com/juliangruber/brace-expansion/issues" + }, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + }, + "description": "Brace expansion as known from sh/bash", + "devDependencies": { + "matcha": "^0.7.0", + "tape": "^4.6.0" + }, + "homepage": "https://github.com/juliangruber/brace-expansion", + "keywords": [], + "license": "MIT", + "main": "index.js", + "name": "brace-expansion", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/brace-expansion.git" + }, + "scripts": { + "bench": "matcha test/perf/bench.js", + "gentest": "bash test/generate.sh", + "test": "tape test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/8..latest", + "firefox/20..latest", + "firefox/nightly", + "chrome/25..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + }, + "version": "1.1.11" +} diff --git a/WebServer/node_modules/browser-stdout/index.js b/WebServer/node_modules/browser-stdout/index.js new file mode 100644 index 000000000..daf39c3b6 --- /dev/null +++ b/WebServer/node_modules/browser-stdout/index.js @@ -0,0 +1,25 @@ +var WritableStream = require('stream').Writable +var inherits = require('util').inherits + +module.exports = BrowserStdout + + +inherits(BrowserStdout, WritableStream) + +function BrowserStdout(opts) { + if (!(this instanceof BrowserStdout)) return new BrowserStdout(opts) + + opts = opts || {} + WritableStream.call(this, opts) + this.label = (opts.label !== undefined) ? opts.label : 'stdout' +} + +BrowserStdout.prototype._write = function(chunks, encoding, cb) { + var output = chunks.toString ? chunks.toString() : chunks + if (this.label === false) { + console.log(output) + } else { + console.log(this.label+':', output) + } + process.nextTick(cb) +} diff --git a/WebServer/node_modules/browser-stdout/package.json b/WebServer/node_modules/browser-stdout/package.json new file mode 100644 index 000000000..7033fbe93 --- /dev/null +++ b/WebServer/node_modules/browser-stdout/package.json @@ -0,0 +1,49 @@ +{ + "_args": [ + [ + "browser-stdout@1.3.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "browser-stdout@1.3.1", + "_id": "browser-stdout@1.3.1", + "_inBundle": false, + "_integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "_location": "/browser-stdout", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "browser-stdout@1.3.1", + "name": "browser-stdout", + "escapedName": "browser-stdout", + "rawSpec": "1.3.1", + "saveSpec": null, + "fetchSpec": "1.3.1" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "_spec": "1.3.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "kumavis" + }, + "bugs": { + "url": "https://github.com/kumavis/browser-stdout/issues" + }, + "description": "`process.stdout` in your browser.", + "homepage": "https://github.com/kumavis/browser-stdout#readme", + "license": "ISC", + "main": "index.js", + "name": "browser-stdout", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/kumavis/browser-stdout.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.3.1" +} diff --git a/WebServer/node_modules/camelcase/index.js b/WebServer/node_modules/camelcase/index.js new file mode 100644 index 000000000..579f99b47 --- /dev/null +++ b/WebServer/node_modules/camelcase/index.js @@ -0,0 +1,76 @@ +'use strict'; + +const preserveCamelCase = string => { + let isLastCharLower = false; + let isLastCharUpper = false; + let isLastLastCharUpper = false; + + for (let i = 0; i < string.length; i++) { + const character = string[i]; + + if (isLastCharLower && /[a-zA-Z]/.test(character) && character.toUpperCase() === character) { + string = string.slice(0, i) + '-' + string.slice(i); + isLastCharLower = false; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = true; + i++; + } else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(character) && character.toLowerCase() === character) { + string = string.slice(0, i - 1) + '-' + string.slice(i - 1); + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = false; + isLastCharLower = true; + } else { + isLastCharLower = character.toLowerCase() === character && character.toUpperCase() !== character; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = character.toUpperCase() === character && character.toLowerCase() !== character; + } + } + + return string; +}; + +const camelCase = (input, options) => { + if (!(typeof input === 'string' || Array.isArray(input))) { + throw new TypeError('Expected the input to be `string | string[]`'); + } + + options = Object.assign({ + pascalCase: false + }, options); + + const postProcess = x => options.pascalCase ? x.charAt(0).toUpperCase() + x.slice(1) : x; + + if (Array.isArray(input)) { + input = input.map(x => x.trim()) + .filter(x => x.length) + .join('-'); + } else { + input = input.trim(); + } + + if (input.length === 0) { + return ''; + } + + if (input.length === 1) { + return options.pascalCase ? input.toUpperCase() : input.toLowerCase(); + } + + const hasUpperCase = input !== input.toLowerCase(); + + if (hasUpperCase) { + input = preserveCamelCase(input); + } + + input = input + .replace(/^[_.\- ]+/, '') + .toLowerCase() + .replace(/[_.\- ]+(\w|$)/g, (_, p1) => p1.toUpperCase()) + .replace(/\d+(\w|$)/g, m => m.toUpperCase()); + + return postProcess(input); +}; + +module.exports = camelCase; +// TODO: Remove this for the next major release +module.exports.default = camelCase; diff --git a/WebServer/node_modules/camelcase/package.json b/WebServer/node_modules/camelcase/package.json new file mode 100644 index 000000000..144c8e993 --- /dev/null +++ b/WebServer/node_modules/camelcase/package.json @@ -0,0 +1,79 @@ +{ + "_args": [ + [ + "camelcase@5.3.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "camelcase@5.3.1", + "_id": "camelcase@5.3.1", + "_inBundle": false, + "_integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "_location": "/camelcase", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "camelcase@5.3.1", + "name": "camelcase", + "escapedName": "camelcase", + "rawSpec": "5.3.1", + "saveSpec": null, + "fetchSpec": "5.3.1" + }, + "_requiredBy": [ + "/yargs-parser", + "/yargs-unparser/yargs-parser" + ], + "_resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "_spec": "5.3.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/camelcase/issues" + }, + "description": "Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`", + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.1", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/camelcase#readme", + "keywords": [ + "camelcase", + "camel-case", + "camel", + "case", + "dash", + "hyphen", + "dot", + "underscore", + "separator", + "string", + "text", + "convert", + "pascalcase", + "pascal-case" + ], + "license": "MIT", + "name": "camelcase", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/camelcase.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "5.3.1" +} diff --git a/WebServer/node_modules/chai/bower.json b/WebServer/node_modules/chai/bower.json new file mode 100644 index 000000000..af2ee029b --- /dev/null +++ b/WebServer/node_modules/chai/bower.json @@ -0,0 +1,26 @@ +{ + "name": "chai", + "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", + "license": "MIT", + "keywords": [ + "test", + "assertion", + "assert", + "testing", + "chai" + ], + "main": "chai.js", + "ignore": [ + "build", + "components", + "lib", + "node_modules", + "support", + "test", + "index.js", + "Makefile", + ".*" + ], + "dependencies": {}, + "devDependencies": {} +} diff --git a/WebServer/node_modules/chai/chai.js b/WebServer/node_modules/chai/chai.js new file mode 100644 index 000000000..39429c51b --- /dev/null +++ b/WebServer/node_modules/chai/chai.js @@ -0,0 +1,10854 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.chai = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i + * MIT Licensed + */ + +var used = []; + +/*! + * Chai version + */ + +exports.version = '4.2.0'; + +/*! + * Assertion Error + */ + +exports.AssertionError = require('assertion-error'); + +/*! + * Utils for plugins (not exported) + */ + +var util = require('./chai/utils'); + +/** + * # .use(function) + * + * Provides a way to extend the internals of Chai. + * + * @param {Function} + * @returns {this} for chaining + * @api public + */ + +exports.use = function (fn) { + if (!~used.indexOf(fn)) { + fn(exports, util); + used.push(fn); + } + + return exports; +}; + +/*! + * Utility Functions + */ + +exports.util = util; + +/*! + * Configuration + */ + +var config = require('./chai/config'); +exports.config = config; + +/*! + * Primary `Assertion` prototype + */ + +var assertion = require('./chai/assertion'); +exports.use(assertion); + +/*! + * Core Assertions + */ + +var core = require('./chai/core/assertions'); +exports.use(core); + +/*! + * Expect interface + */ + +var expect = require('./chai/interface/expect'); +exports.use(expect); + +/*! + * Should interface + */ + +var should = require('./chai/interface/should'); +exports.use(should); + +/*! + * Assert interface + */ + +var assert = require('./chai/interface/assert'); +exports.use(assert); + +},{"./chai/assertion":3,"./chai/config":4,"./chai/core/assertions":5,"./chai/interface/assert":6,"./chai/interface/expect":7,"./chai/interface/should":8,"./chai/utils":22,"assertion-error":33}],3:[function(require,module,exports){ +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var config = require('./config'); + +module.exports = function (_chai, util) { + /*! + * Module dependencies. + */ + + var AssertionError = _chai.AssertionError + , flag = util.flag; + + /*! + * Module export. + */ + + _chai.Assertion = Assertion; + + /*! + * Assertion Constructor + * + * Creates object for chaining. + * + * `Assertion` objects contain metadata in the form of flags. Three flags can + * be assigned during instantiation by passing arguments to this constructor: + * + * - `object`: This flag contains the target of the assertion. For example, in + * the assertion `expect(numKittens).to.equal(7);`, the `object` flag will + * contain `numKittens` so that the `equal` assertion can reference it when + * needed. + * + * - `message`: This flag contains an optional custom error message to be + * prepended to the error message that's generated by the assertion when it + * fails. + * + * - `ssfi`: This flag stands for "start stack function indicator". It + * contains a function reference that serves as the starting point for + * removing frames from the stack trace of the error that's created by the + * assertion when it fails. The goal is to provide a cleaner stack trace to + * end users by removing Chai's internal functions. Note that it only works + * in environments that support `Error.captureStackTrace`, and only when + * `Chai.config.includeStack` hasn't been set to `false`. + * + * - `lockSsfi`: This flag controls whether or not the given `ssfi` flag + * should retain its current value, even as assertions are chained off of + * this object. This is usually set to `true` when creating a new assertion + * from within another assertion. It's also temporarily set to `true` before + * an overwritten assertion gets called by the overwriting assertion. + * + * @param {Mixed} obj target of the assertion + * @param {String} msg (optional) custom error message + * @param {Function} ssfi (optional) starting point for removing stack frames + * @param {Boolean} lockSsfi (optional) whether or not the ssfi flag is locked + * @api private + */ + + function Assertion (obj, msg, ssfi, lockSsfi) { + flag(this, 'ssfi', ssfi || Assertion); + flag(this, 'lockSsfi', lockSsfi); + flag(this, 'object', obj); + flag(this, 'message', msg); + + return util.proxify(this); + } + + Object.defineProperty(Assertion, 'includeStack', { + get: function() { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + return config.includeStack; + }, + set: function(value) { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + config.includeStack = value; + } + }); + + Object.defineProperty(Assertion, 'showDiff', { + get: function() { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + return config.showDiff; + }, + set: function(value) { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + config.showDiff = value; + } + }); + + Assertion.addProperty = function (name, fn) { + util.addProperty(this.prototype, name, fn); + }; + + Assertion.addMethod = function (name, fn) { + util.addMethod(this.prototype, name, fn); + }; + + Assertion.addChainableMethod = function (name, fn, chainingBehavior) { + util.addChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + Assertion.overwriteProperty = function (name, fn) { + util.overwriteProperty(this.prototype, name, fn); + }; + + Assertion.overwriteMethod = function (name, fn) { + util.overwriteMethod(this.prototype, name, fn); + }; + + Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { + util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + /** + * ### .assert(expression, message, negateMessage, expected, actual, showDiff) + * + * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. + * + * @name assert + * @param {Philosophical} expression to be tested + * @param {String|Function} message or function that returns message to display if expression fails + * @param {String|Function} negatedMessage or function that returns negatedMessage to display if negated expression fails + * @param {Mixed} expected value (remember to check for negation) + * @param {Mixed} actual (optional) will default to `this.obj` + * @param {Boolean} showDiff (optional) when set to `true`, assert will display a diff in addition to the message if expression fails + * @api private + */ + + Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { + var ok = util.test(this, arguments); + if (false !== showDiff) showDiff = true; + if (undefined === expected && undefined === _actual) showDiff = false; + if (true !== config.showDiff) showDiff = false; + + if (!ok) { + msg = util.getMessage(this, arguments); + var actual = util.getActual(this, arguments); + throw new AssertionError(msg, { + actual: actual + , expected: expected + , showDiff: showDiff + }, (config.includeStack) ? this.assert : flag(this, 'ssfi')); + } + }; + + /*! + * ### ._obj + * + * Quick reference to stored `actual` value for plugin developers. + * + * @api private + */ + + Object.defineProperty(Assertion.prototype, '_obj', + { get: function () { + return flag(this, 'object'); + } + , set: function (val) { + flag(this, 'object', val); + } + }); +}; + +},{"./config":4}],4:[function(require,module,exports){ +module.exports = { + + /** + * ### config.includeStack + * + * User configurable property, influences whether stack trace + * is included in Assertion error message. Default of false + * suppresses stack trace in the error message. + * + * chai.config.includeStack = true; // enable stack on error + * + * @param {Boolean} + * @api public + */ + + includeStack: false, + + /** + * ### config.showDiff + * + * User configurable property, influences whether or not + * the `showDiff` flag should be included in the thrown + * AssertionErrors. `false` will always be `false`; `true` + * will be true when the assertion has requested a diff + * be shown. + * + * @param {Boolean} + * @api public + */ + + showDiff: true, + + /** + * ### config.truncateThreshold + * + * User configurable property, sets length threshold for actual and + * expected values in assertion errors. If this threshold is exceeded, for + * example for large data structures, the value is replaced with something + * like `[ Array(3) ]` or `{ Object (prop1, prop2) }`. + * + * Set it to zero if you want to disable truncating altogether. + * + * This is especially userful when doing assertions on arrays: having this + * set to a reasonable large value makes the failure messages readily + * inspectable. + * + * chai.config.truncateThreshold = 0; // disable truncating + * + * @param {Number} + * @api public + */ + + truncateThreshold: 40, + + /** + * ### config.useProxy + * + * User configurable property, defines if chai will use a Proxy to throw + * an error when a non-existent property is read, which protects users + * from typos when using property-based assertions. + * + * Set it to false if you want to disable this feature. + * + * chai.config.useProxy = false; // disable use of Proxy + * + * This feature is automatically disabled regardless of this config value + * in environments that don't support proxies. + * + * @param {Boolean} + * @api public + */ + + useProxy: true, + + /** + * ### config.proxyExcludedKeys + * + * User configurable property, defines which properties should be ignored + * instead of throwing an error if they do not exist on the assertion. + * This is only applied if the environment Chai is running in supports proxies and + * if the `useProxy` configuration setting is enabled. + * By default, `then` and `inspect` will not throw an error if they do not exist on the + * assertion object because the `.inspect` property is read by `util.inspect` (for example, when + * using `console.log` on the assertion object) and `.then` is necessary for promise type-checking. + * + * // By default these keys will not throw an error if they do not exist on the assertion object + * chai.config.proxyExcludedKeys = ['then', 'inspect']; + * + * @param {Array} + * @api public + */ + + proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON'] +}; + +},{}],5:[function(require,module,exports){ +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, _) { + var Assertion = chai.Assertion + , AssertionError = chai.AssertionError + , flag = _.flag; + + /** + * ### Language Chains + * + * The following are provided as chainable getters to improve the readability + * of your assertions. + * + * **Chains** + * + * - to + * - be + * - been + * - is + * - that + * - which + * - and + * - has + * - have + * - with + * - at + * - of + * - same + * - but + * - does + * - still + * + * @name language chains + * @namespace BDD + * @api public + */ + + [ 'to', 'be', 'been', 'is' + , 'and', 'has', 'have', 'with' + , 'that', 'which', 'at', 'of' + , 'same', 'but', 'does', 'still' ].forEach(function (chain) { + Assertion.addProperty(chain); + }); + + /** + * ### .not + * + * Negates all assertions that follow in the chain. + * + * expect(function () {}).to.not.throw(); + * expect({a: 1}).to.not.have.property('b'); + * expect([1, 2]).to.be.an('array').that.does.not.include(3); + * + * Just because you can negate any assertion with `.not` doesn't mean you + * should. With great power comes great responsibility. It's often best to + * assert that the one expected output was produced, rather than asserting + * that one of countless unexpected outputs wasn't produced. See individual + * assertions for specific guidance. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.equal(1); // Not recommended + * + * @name not + * @namespace BDD + * @api public + */ + + Assertion.addProperty('not', function () { + flag(this, 'negate', true); + }); + + /** + * ### .deep + * + * Causes all `.equal`, `.include`, `.members`, `.keys`, and `.property` + * assertions that follow in the chain to use deep equality instead of strict + * (`===`) equality. See the `deep-eql` project page for info on the deep + * equality algorithm: https://github.com/chaijs/deep-eql. + * + * // Target object deeply (but not strictly) equals `{a: 1}` + * expect({a: 1}).to.deep.equal({a: 1}); + * expect({a: 1}).to.not.equal({a: 1}); + * + * // Target array deeply (but not strictly) includes `{a: 1}` + * expect([{a: 1}]).to.deep.include({a: 1}); + * expect([{a: 1}]).to.not.include({a: 1}); + * + * // Target object deeply (but not strictly) includes `x: {a: 1}` + * expect({x: {a: 1}}).to.deep.include({x: {a: 1}}); + * expect({x: {a: 1}}).to.not.include({x: {a: 1}}); + * + * // Target array deeply (but not strictly) has member `{a: 1}` + * expect([{a: 1}]).to.have.deep.members([{a: 1}]); + * expect([{a: 1}]).to.not.have.members([{a: 1}]); + * + * // Target set deeply (but not strictly) has key `{a: 1}` + * expect(new Set([{a: 1}])).to.have.deep.keys([{a: 1}]); + * expect(new Set([{a: 1}])).to.not.have.keys([{a: 1}]); + * + * // Target object deeply (but not strictly) has property `x: {a: 1}` + * expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); + * expect({x: {a: 1}}).to.not.have.property('x', {a: 1}); + * + * @name deep + * @namespace BDD + * @api public + */ + + Assertion.addProperty('deep', function () { + flag(this, 'deep', true); + }); + + /** + * ### .nested + * + * Enables dot- and bracket-notation in all `.property` and `.include` + * assertions that follow in the chain. + * + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]'); + * expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'}); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 'x'}}).to.have.nested.property('\\.a.\\[b\\]'); + * expect({'.a': {'[b]': 'x'}}).to.nested.include({'\\.a.\\[b\\]': 'x'}); + * + * `.nested` cannot be combined with `.own`. + * + * @name nested + * @namespace BDD + * @api public + */ + + Assertion.addProperty('nested', function () { + flag(this, 'nested', true); + }); + + /** + * ### .own + * + * Causes all `.property` and `.include` assertions that follow in the chain + * to ignore inherited properties. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.have.own.property('a'); + * expect({a: 1}).to.have.property('b'); + * expect({a: 1}).to.not.have.own.property('b'); + * + * expect({a: 1}).to.own.include({a: 1}); + * expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2}); + * + * `.own` cannot be combined with `.nested`. + * + * @name own + * @namespace BDD + * @api public + */ + + Assertion.addProperty('own', function () { + flag(this, 'own', true); + }); + + /** + * ### .ordered + * + * Causes all `.members` assertions that follow in the chain to require that + * members be in the same order. + * + * expect([1, 2]).to.have.ordered.members([1, 2]) + * .but.not.have.ordered.members([2, 1]); + * + * When `.include` and `.ordered` are combined, the ordering begins at the + * start of both arrays. + * + * expect([1, 2, 3]).to.include.ordered.members([1, 2]) + * .but.not.include.ordered.members([2, 3]); + * + * @name ordered + * @namespace BDD + * @api public + */ + + Assertion.addProperty('ordered', function () { + flag(this, 'ordered', true); + }); + + /** + * ### .any + * + * Causes all `.keys` assertions that follow in the chain to only require that + * the target have at least one of the given keys. This is the opposite of + * `.all`, which requires that the target have all of the given keys. + * + * expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd'); + * + * See the `.keys` doc for guidance on when to use `.any` or `.all`. + * + * @name any + * @namespace BDD + * @api public + */ + + Assertion.addProperty('any', function () { + flag(this, 'any', true); + flag(this, 'all', false); + }); + + /** + * ### .all + * + * Causes all `.keys` assertions that follow in the chain to require that the + * target have all of the given keys. This is the opposite of `.any`, which + * only requires that the target have at least one of the given keys. + * + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * + * Note that `.all` is used by default when neither `.all` nor `.any` are + * added earlier in the chain. However, it's often best to add `.all` anyway + * because it improves readability. + * + * See the `.keys` doc for guidance on when to use `.any` or `.all`. + * + * @name all + * @namespace BDD + * @api public + */ + + Assertion.addProperty('all', function () { + flag(this, 'all', true); + flag(this, 'any', false); + }); + + /** + * ### .a(type[, msg]) + * + * Asserts that the target's type is equal to the given string `type`. Types + * are case insensitive. See the `type-detect` project page for info on the + * type detection algorithm: https://github.com/chaijs/type-detect. + * + * expect('foo').to.be.a('string'); + * expect({a: 1}).to.be.an('object'); + * expect(null).to.be.a('null'); + * expect(undefined).to.be.an('undefined'); + * expect(new Error).to.be.an('error'); + * expect(Promise.resolve()).to.be.a('promise'); + * expect(new Float32Array).to.be.a('float32array'); + * expect(Symbol()).to.be.a('symbol'); + * + * `.a` supports objects that have a custom type set via `Symbol.toStringTag`. + * + * var myObj = { + * [Symbol.toStringTag]: 'myCustomType' + * }; + * + * expect(myObj).to.be.a('myCustomType').but.not.an('object'); + * + * It's often best to use `.a` to check a target's type before making more + * assertions on the same target. That way, you avoid unexpected behavior from + * any assertion that does different things based on the target's type. + * + * expect([1, 2, 3]).to.be.an('array').that.includes(2); + * expect([]).to.be.an('array').that.is.empty; + * + * Add `.not` earlier in the chain to negate `.a`. However, it's often best to + * assert that the target is the expected type, rather than asserting that it + * isn't one of many unexpected types. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.an('array'); // Not recommended + * + * `.a` accepts an optional `msg` argument which is a custom error message to + * show when the assertion fails. The message can also be given as the second + * argument to `expect`. + * + * expect(1).to.be.a('string', 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.a('string'); + * + * `.a` can also be used as a language chain to improve the readability of + * your assertions. + * + * expect({b: 2}).to.have.a.property('b'); + * + * The alias `.an` can be used interchangeably with `.a`. + * + * @name a + * @alias an + * @param {String} type + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function an (type, msg) { + if (msg) flag(this, 'message', msg); + type = type.toLowerCase(); + var obj = flag(this, 'object') + , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; + + this.assert( + type === _.type(obj).toLowerCase() + , 'expected #{this} to be ' + article + type + , 'expected #{this} not to be ' + article + type + ); + } + + Assertion.addChainableMethod('an', an); + Assertion.addChainableMethod('a', an); + + /** + * ### .include(val[, msg]) + * + * When the target is a string, `.include` asserts that the given string `val` + * is a substring of the target. + * + * expect('foobar').to.include('foo'); + * + * When the target is an array, `.include` asserts that the given `val` is a + * member of the target. + * + * expect([1, 2, 3]).to.include(2); + * + * When the target is an object, `.include` asserts that the given object + * `val`'s properties are a subset of the target's properties. + * + * expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2}); + * + * When the target is a Set or WeakSet, `.include` asserts that the given `val` is a + * member of the target. SameValueZero equality algorithm is used. + * + * expect(new Set([1, 2])).to.include(2); + * + * When the target is a Map, `.include` asserts that the given `val` is one of + * the values of the target. SameValueZero equality algorithm is used. + * + * expect(new Map([['a', 1], ['b', 2]])).to.include(2); + * + * Because `.include` does different things based on the target's type, it's + * important to check the target's type before using `.include`. See the `.a` + * doc for info on testing a target's type. + * + * expect([1, 2, 3]).to.be.an('array').that.includes(2); + * + * By default, strict (`===`) equality is used to compare array members and + * object properties. Add `.deep` earlier in the chain to use deep equality + * instead (WeakSet targets are not supported). See the `deep-eql` project + * page for info on the deep equality algorithm: https://github.com/chaijs/deep-eql. + * + * // Target array deeply (but not strictly) includes `{a: 1}` + * expect([{a: 1}]).to.deep.include({a: 1}); + * expect([{a: 1}]).to.not.include({a: 1}); + * + * // Target object deeply (but not strictly) includes `x: {a: 1}` + * expect({x: {a: 1}}).to.deep.include({x: {a: 1}}); + * expect({x: {a: 1}}).to.not.include({x: {a: 1}}); + * + * By default, all of the target's properties are searched when working with + * objects. This includes properties that are inherited and/or non-enumerable. + * Add `.own` earlier in the chain to exclude the target's inherited + * properties from the search. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.own.include({a: 1}); + * expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2}); + * + * Note that a target object is always only searched for `val`'s own + * enumerable properties. + * + * `.deep` and `.own` can be combined. + * + * expect({a: {b: 2}}).to.deep.own.include({a: {b: 2}}); + * + * Add `.nested` earlier in the chain to enable dot- and bracket-notation when + * referencing nested properties. + * + * expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'}); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 2}}).to.nested.include({'\\.a.\\[b\\]': 2}); + * + * `.deep` and `.nested` can be combined. + * + * expect({a: {b: [{c: 3}]}}).to.deep.nested.include({'a.b[0]': {c: 3}}); + * + * `.own` and `.nested` cannot be combined. + * + * Add `.not` earlier in the chain to negate `.include`. + * + * expect('foobar').to.not.include('taco'); + * expect([1, 2, 3]).to.not.include(4); + * + * However, it's dangerous to negate `.include` when the target is an object. + * The problem is that it creates uncertain expectations by asserting that the + * target object doesn't have all of `val`'s key/value pairs but may or may + * not have some of them. It's often best to identify the exact output that's + * expected, and then write an assertion that only accepts that exact output. + * + * When the target object isn't even expected to have `val`'s keys, it's + * often best to assert exactly that. + * + * expect({c: 3}).to.not.have.any.keys('a', 'b'); // Recommended + * expect({c: 3}).to.not.include({a: 1, b: 2}); // Not recommended + * + * When the target object is expected to have `val`'s keys, it's often best to + * assert that each of the properties has its expected value, rather than + * asserting that each property doesn't have one of many unexpected values. + * + * expect({a: 3, b: 4}).to.include({a: 3, b: 4}); // Recommended + * expect({a: 3, b: 4}).to.not.include({a: 1, b: 2}); // Not recommended + * + * `.include` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2, 3]).to.include(4, 'nooo why fail??'); + * expect([1, 2, 3], 'nooo why fail??').to.include(4); + * + * `.include` can also be used as a language chain, causing all `.members` and + * `.keys` assertions that follow in the chain to require the target to be a + * superset of the expected set, rather than an identical set. Note that + * `.members` ignores duplicates in the subset when `.include` is added. + * + * // Target object's keys are a superset of ['a', 'b'] but not identical + * expect({a: 1, b: 2, c: 3}).to.include.all.keys('a', 'b'); + * expect({a: 1, b: 2, c: 3}).to.not.have.all.keys('a', 'b'); + * + * // Target array is a superset of [1, 2] but not identical + * expect([1, 2, 3]).to.include.members([1, 2]); + * expect([1, 2, 3]).to.not.have.members([1, 2]); + * + * // Duplicates in the subset are ignored + * expect([1, 2, 3]).to.include.members([1, 2, 2, 2]); + * + * Note that adding `.any` earlier in the chain causes the `.keys` assertion + * to ignore `.include`. + * + * // Both assertions are identical + * expect({a: 1}).to.include.any.keys('a', 'b'); + * expect({a: 1}).to.have.any.keys('a', 'b'); + * + * The aliases `.includes`, `.contain`, and `.contains` can be used + * interchangeably with `.include`. + * + * @name include + * @alias contain + * @alias includes + * @alias contains + * @param {Mixed} val + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function SameValueZero(a, b) { + return (_.isNaN(a) && _.isNaN(b)) || a === b; + } + + function includeChainingBehavior () { + flag(this, 'contains', true); + } + + function include (val, msg) { + if (msg) flag(this, 'message', msg); + + var obj = flag(this, 'object') + , objType = _.type(obj).toLowerCase() + , flagMsg = flag(this, 'message') + , negate = flag(this, 'negate') + , ssfi = flag(this, 'ssfi') + , isDeep = flag(this, 'deep') + , descriptor = isDeep ? 'deep ' : ''; + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + var included = false; + + switch (objType) { + case 'string': + included = obj.indexOf(val) !== -1; + break; + + case 'weakset': + if (isDeep) { + throw new AssertionError( + flagMsg + 'unable to use .deep.include with WeakSet', + undefined, + ssfi + ); + } + + included = obj.has(val); + break; + + case 'map': + var isEql = isDeep ? _.eql : SameValueZero; + obj.forEach(function (item) { + included = included || isEql(item, val); + }); + break; + + case 'set': + if (isDeep) { + obj.forEach(function (item) { + included = included || _.eql(item, val); + }); + } else { + included = obj.has(val); + } + break; + + case 'array': + if (isDeep) { + included = obj.some(function (item) { + return _.eql(item, val); + }) + } else { + included = obj.indexOf(val) !== -1; + } + break; + + default: + // This block is for asserting a subset of properties in an object. + // `_.expectTypes` isn't used here because `.include` should work with + // objects with a custom `@@toStringTag`. + if (val !== Object(val)) { + throw new AssertionError( + flagMsg + 'object tested must be an array, a map, an object,' + + ' a set, a string, or a weakset, but ' + objType + ' given', + undefined, + ssfi + ); + } + + var props = Object.keys(val) + , firstErr = null + , numErrs = 0; + + props.forEach(function (prop) { + var propAssertion = new Assertion(obj); + _.transferFlags(this, propAssertion, true); + flag(propAssertion, 'lockSsfi', true); + + if (!negate || props.length === 1) { + propAssertion.property(prop, val[prop]); + return; + } + + try { + propAssertion.property(prop, val[prop]); + } catch (err) { + if (!_.checkError.compatibleConstructor(err, AssertionError)) { + throw err; + } + if (firstErr === null) firstErr = err; + numErrs++; + } + }, this); + + // When validating .not.include with multiple properties, we only want + // to throw an assertion error if all of the properties are included, + // in which case we throw the first property assertion error that we + // encountered. + if (negate && props.length > 1 && numErrs === props.length) { + throw firstErr; + } + return; + } + + // Assert inclusion in collection or substring in a string. + this.assert( + included + , 'expected #{this} to ' + descriptor + 'include ' + _.inspect(val) + , 'expected #{this} to not ' + descriptor + 'include ' + _.inspect(val)); + } + + Assertion.addChainableMethod('include', include, includeChainingBehavior); + Assertion.addChainableMethod('contain', include, includeChainingBehavior); + Assertion.addChainableMethod('contains', include, includeChainingBehavior); + Assertion.addChainableMethod('includes', include, includeChainingBehavior); + + /** + * ### .ok + * + * Asserts that the target is a truthy value (considered `true` in boolean context). + * However, it's often best to assert that the target is strictly (`===`) or + * deeply equal to its expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.ok; // Not recommended + * + * expect(true).to.be.true; // Recommended + * expect(true).to.be.ok; // Not recommended + * + * Add `.not` earlier in the chain to negate `.ok`. + * + * expect(0).to.equal(0); // Recommended + * expect(0).to.not.be.ok; // Not recommended + * + * expect(false).to.be.false; // Recommended + * expect(false).to.not.be.ok; // Not recommended + * + * expect(null).to.be.null; // Recommended + * expect(null).to.not.be.ok; // Not recommended + * + * expect(undefined).to.be.undefined; // Recommended + * expect(undefined).to.not.be.ok; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(false, 'nooo why fail??').to.be.ok; + * + * @name ok + * @namespace BDD + * @api public + */ + + Assertion.addProperty('ok', function () { + this.assert( + flag(this, 'object') + , 'expected #{this} to be truthy' + , 'expected #{this} to be falsy'); + }); + + /** + * ### .true + * + * Asserts that the target is strictly (`===`) equal to `true`. + * + * expect(true).to.be.true; + * + * Add `.not` earlier in the chain to negate `.true`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `true`. + * + * expect(false).to.be.false; // Recommended + * expect(false).to.not.be.true; // Not recommended + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.true; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(false, 'nooo why fail??').to.be.true; + * + * @name true + * @namespace BDD + * @api public + */ + + Assertion.addProperty('true', function () { + this.assert( + true === flag(this, 'object') + , 'expected #{this} to be true' + , 'expected #{this} to be false' + , flag(this, 'negate') ? false : true + ); + }); + + /** + * ### .false + * + * Asserts that the target is strictly (`===`) equal to `false`. + * + * expect(false).to.be.false; + * + * Add `.not` earlier in the chain to negate `.false`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to `false`. + * + * expect(true).to.be.true; // Recommended + * expect(true).to.not.be.false; // Not recommended + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.false; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(true, 'nooo why fail??').to.be.false; + * + * @name false + * @namespace BDD + * @api public + */ + + Assertion.addProperty('false', function () { + this.assert( + false === flag(this, 'object') + , 'expected #{this} to be false' + , 'expected #{this} to be true' + , flag(this, 'negate') ? true : false + ); + }); + + /** + * ### .null + * + * Asserts that the target is strictly (`===`) equal to `null`. + * + * expect(null).to.be.null; + * + * Add `.not` earlier in the chain to negate `.null`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `null`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.null; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.null; + * + * @name null + * @namespace BDD + * @api public + */ + + Assertion.addProperty('null', function () { + this.assert( + null === flag(this, 'object') + , 'expected #{this} to be null' + , 'expected #{this} not to be null' + ); + }); + + /** + * ### .undefined + * + * Asserts that the target is strictly (`===`) equal to `undefined`. + * + * expect(undefined).to.be.undefined; + * + * Add `.not` earlier in the chain to negate `.undefined`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to `undefined`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.undefined; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.undefined; + * + * @name undefined + * @namespace BDD + * @api public + */ + + Assertion.addProperty('undefined', function () { + this.assert( + undefined === flag(this, 'object') + , 'expected #{this} to be undefined' + , 'expected #{this} not to be undefined' + ); + }); + + /** + * ### .NaN + * + * Asserts that the target is exactly `NaN`. + * + * expect(NaN).to.be.NaN; + * + * Add `.not` earlier in the chain to negate `.NaN`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `NaN`. + * + * expect('foo').to.equal('foo'); // Recommended + * expect('foo').to.not.be.NaN; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.NaN; + * + * @name NaN + * @namespace BDD + * @api public + */ + + Assertion.addProperty('NaN', function () { + this.assert( + _.isNaN(flag(this, 'object')) + , 'expected #{this} to be NaN' + , 'expected #{this} not to be NaN' + ); + }); + + /** + * ### .exist + * + * Asserts that the target is not strictly (`===`) equal to either `null` or + * `undefined`. However, it's often best to assert that the target is equal to + * its expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.exist; // Not recommended + * + * expect(0).to.equal(0); // Recommended + * expect(0).to.exist; // Not recommended + * + * Add `.not` earlier in the chain to negate `.exist`. + * + * expect(null).to.be.null; // Recommended + * expect(null).to.not.exist; // Not recommended + * + * expect(undefined).to.be.undefined; // Recommended + * expect(undefined).to.not.exist; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(null, 'nooo why fail??').to.exist; + * + * @name exist + * @namespace BDD + * @api public + */ + + Assertion.addProperty('exist', function () { + var val = flag(this, 'object'); + this.assert( + val !== null && val !== undefined + , 'expected #{this} to exist' + , 'expected #{this} to not exist' + ); + }); + + /** + * ### .empty + * + * When the target is a string or array, `.empty` asserts that the target's + * `length` property is strictly (`===`) equal to `0`. + * + * expect([]).to.be.empty; + * expect('').to.be.empty; + * + * When the target is a map or set, `.empty` asserts that the target's `size` + * property is strictly equal to `0`. + * + * expect(new Set()).to.be.empty; + * expect(new Map()).to.be.empty; + * + * When the target is a non-function object, `.empty` asserts that the target + * doesn't have any own enumerable properties. Properties with Symbol-based + * keys are excluded from the count. + * + * expect({}).to.be.empty; + * + * Because `.empty` does different things based on the target's type, it's + * important to check the target's type before using `.empty`. See the `.a` + * doc for info on testing a target's type. + * + * expect([]).to.be.an('array').that.is.empty; + * + * Add `.not` earlier in the chain to negate `.empty`. However, it's often + * best to assert that the target contains its expected number of values, + * rather than asserting that it's not empty. + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.not.be.empty; // Not recommended + * + * expect(new Set([1, 2, 3])).to.have.property('size', 3); // Recommended + * expect(new Set([1, 2, 3])).to.not.be.empty; // Not recommended + * + * expect(Object.keys({a: 1})).to.have.lengthOf(1); // Recommended + * expect({a: 1}).to.not.be.empty; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect([1, 2, 3], 'nooo why fail??').to.be.empty; + * + * @name empty + * @namespace BDD + * @api public + */ + + Assertion.addProperty('empty', function () { + var val = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , flagMsg = flag(this, 'message') + , itemsCount; + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + switch (_.type(val).toLowerCase()) { + case 'array': + case 'string': + itemsCount = val.length; + break; + case 'map': + case 'set': + itemsCount = val.size; + break; + case 'weakmap': + case 'weakset': + throw new AssertionError( + flagMsg + '.empty was passed a weak collection', + undefined, + ssfi + ); + case 'function': + var msg = flagMsg + '.empty was passed a function ' + _.getName(val); + throw new AssertionError(msg.trim(), undefined, ssfi); + default: + if (val !== Object(val)) { + throw new AssertionError( + flagMsg + '.empty was passed non-string primitive ' + _.inspect(val), + undefined, + ssfi + ); + } + itemsCount = Object.keys(val).length; + } + + this.assert( + 0 === itemsCount + , 'expected #{this} to be empty' + , 'expected #{this} not to be empty' + ); + }); + + /** + * ### .arguments + * + * Asserts that the target is an `arguments` object. + * + * function test () { + * expect(arguments).to.be.arguments; + * } + * + * test(); + * + * Add `.not` earlier in the chain to negate `.arguments`. However, it's often + * best to assert which type the target is expected to be, rather than + * asserting that its not an `arguments` object. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.arguments; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({}, 'nooo why fail??').to.be.arguments; + * + * The alias `.Arguments` can be used interchangeably with `.arguments`. + * + * @name arguments + * @alias Arguments + * @namespace BDD + * @api public + */ + + function checkArguments () { + var obj = flag(this, 'object') + , type = _.type(obj); + this.assert( + 'Arguments' === type + , 'expected #{this} to be arguments but got ' + type + , 'expected #{this} to not be arguments' + ); + } + + Assertion.addProperty('arguments', checkArguments); + Assertion.addProperty('Arguments', checkArguments); + + /** + * ### .equal(val[, msg]) + * + * Asserts that the target is strictly (`===`) equal to the given `val`. + * + * expect(1).to.equal(1); + * expect('foo').to.equal('foo'); + * + * Add `.deep` earlier in the chain to use deep equality instead. See the + * `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target object deeply (but not strictly) equals `{a: 1}` + * expect({a: 1}).to.deep.equal({a: 1}); + * expect({a: 1}).to.not.equal({a: 1}); + * + * // Target array deeply (but not strictly) equals `[1, 2]` + * expect([1, 2]).to.deep.equal([1, 2]); + * expect([1, 2]).to.not.equal([1, 2]); + * + * Add `.not` earlier in the chain to negate `.equal`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to one of countless unexpected values. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.equal(2); // Not recommended + * + * `.equal` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.equal(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.equal(2); + * + * The aliases `.equals` and `eq` can be used interchangeably with `.equal`. + * + * @name equal + * @alias equals + * @alias eq + * @param {Mixed} val + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertEqual (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'deep')) { + var prevLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); + this.eql(val); + flag(this, 'lockSsfi', prevLockSsfi); + } else { + this.assert( + val === obj + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{exp}' + , val + , this._obj + , true + ); + } + } + + Assertion.addMethod('equal', assertEqual); + Assertion.addMethod('equals', assertEqual); + Assertion.addMethod('eq', assertEqual); + + /** + * ### .eql(obj[, msg]) + * + * Asserts that the target is deeply equal to the given `obj`. See the + * `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target object is deeply (but not strictly) equal to {a: 1} + * expect({a: 1}).to.eql({a: 1}).but.not.equal({a: 1}); + * + * // Target array is deeply (but not strictly) equal to [1, 2] + * expect([1, 2]).to.eql([1, 2]).but.not.equal([1, 2]); + * + * Add `.not` earlier in the chain to negate `.eql`. However, it's often best + * to assert that the target is deeply equal to its expected value, rather + * than not deeply equal to one of countless unexpected values. + * + * expect({a: 1}).to.eql({a: 1}); // Recommended + * expect({a: 1}).to.not.eql({b: 2}); // Not recommended + * + * `.eql` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect({a: 1}).to.eql({b: 2}, 'nooo why fail??'); + * expect({a: 1}, 'nooo why fail??').to.eql({b: 2}); + * + * The alias `.eqls` can be used interchangeably with `.eql`. + * + * The `.deep.equal` assertion is almost identical to `.eql` but with one + * difference: `.deep.equal` causes deep equality comparisons to also be used + * for any other assertions that follow in the chain. + * + * @name eql + * @alias eqls + * @param {Mixed} obj + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertEql(obj, msg) { + if (msg) flag(this, 'message', msg); + this.assert( + _.eql(obj, flag(this, 'object')) + , 'expected #{this} to deeply equal #{exp}' + , 'expected #{this} to not deeply equal #{exp}' + , obj + , this._obj + , true + ); + } + + Assertion.addMethod('eql', assertEql); + Assertion.addMethod('eqls', assertEql); + + /** + * ### .above(n[, msg]) + * + * Asserts that the target is a number or a date greater than the given number or date `n` respectively. + * However, it's often best to assert that the target is equal to its expected + * value. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.above(1); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is greater than the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.above(2); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.above(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.above`. + * + * expect(2).to.equal(2); // Recommended + * expect(1).to.not.be.above(2); // Not recommended + * + * `.above` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.above(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.above(2); + * + * The aliases `.gt` and `.greaterThan` can be used interchangeably with + * `.above`. + * + * @name above + * @alias gt + * @alias greaterThan + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertAbove (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to above must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to above must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount > n + , 'expected #{this} to have a ' + descriptor + ' above #{exp} but got #{act}' + , 'expected #{this} to not have a ' + descriptor + ' above #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj > n + , 'expected #{this} to be above #{exp}' + , 'expected #{this} to be at most #{exp}' + , n + ); + } + } + + Assertion.addMethod('above', assertAbove); + Assertion.addMethod('gt', assertAbove); + Assertion.addMethod('greaterThan', assertAbove); + + /** + * ### .least(n[, msg]) + * + * Asserts that the target is a number or a date greater than or equal to the given + * number or date `n` respectively. However, it's often best to assert that the target is equal to + * its expected value. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.at.least(1); // Not recommended + * expect(2).to.be.at.least(2); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is greater than or equal to the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.at.least(2); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.at.least(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.least`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.at.least(2); // Not recommended + * + * `.least` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.at.least(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.at.least(2); + * + * The alias `.gte` can be used interchangeably with `.least`. + * + * @name least + * @alias gte + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertLeast (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to least must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to least must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount >= n + , 'expected #{this} to have a ' + descriptor + ' at least #{exp} but got #{act}' + , 'expected #{this} to have a ' + descriptor + ' below #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj >= n + , 'expected #{this} to be at least #{exp}' + , 'expected #{this} to be below #{exp}' + , n + ); + } + } + + Assertion.addMethod('least', assertLeast); + Assertion.addMethod('gte', assertLeast); + + /** + * ### .below(n[, msg]) + * + * Asserts that the target is a number or a date less than the given number or date `n` respectively. + * However, it's often best to assert that the target is equal to its expected + * value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.below(2); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is less than the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.below(4); // Not recommended + * + * expect([1, 2, 3]).to.have.length(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.below(4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.below`. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.be.below(1); // Not recommended + * + * `.below` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(2).to.be.below(1, 'nooo why fail??'); + * expect(2, 'nooo why fail??').to.be.below(1); + * + * The aliases `.lt` and `.lessThan` can be used interchangeably with + * `.below`. + * + * @name below + * @alias lt + * @alias lessThan + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertBelow (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to below must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to below must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount < n + , 'expected #{this} to have a ' + descriptor + ' below #{exp} but got #{act}' + , 'expected #{this} to not have a ' + descriptor + ' below #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj < n + , 'expected #{this} to be below #{exp}' + , 'expected #{this} to be at least #{exp}' + , n + ); + } + } + + Assertion.addMethod('below', assertBelow); + Assertion.addMethod('lt', assertBelow); + Assertion.addMethod('lessThan', assertBelow); + + /** + * ### .most(n[, msg]) + * + * Asserts that the target is a number or a date less than or equal to the given number + * or date `n` respectively. However, it's often best to assert that the target is equal to its + * expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.at.most(2); // Not recommended + * expect(1).to.be.at.most(1); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is less than or equal to the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.at.most(4); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.at.most(4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.most`. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.be.at.most(1); // Not recommended + * + * `.most` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(2).to.be.at.most(1, 'nooo why fail??'); + * expect(2, 'nooo why fail??').to.be.at.most(1); + * + * The alias `.lte` can be used interchangeably with `.most`. + * + * @name most + * @alias lte + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertMost (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to most must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to most must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount <= n + , 'expected #{this} to have a ' + descriptor + ' at most #{exp} but got #{act}' + , 'expected #{this} to have a ' + descriptor + ' above #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj <= n + , 'expected #{this} to be at most #{exp}' + , 'expected #{this} to be above #{exp}' + , n + ); + } + } + + Assertion.addMethod('most', assertMost); + Assertion.addMethod('lte', assertMost); + + /** + * ### .within(start, finish[, msg]) + * + * Asserts that the target is a number or a date greater than or equal to the given + * number or date `start`, and less than or equal to the given number or date `finish` respectively. + * However, it's often best to assert that the target is equal to its expected + * value. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.within(1, 3); // Not recommended + * expect(2).to.be.within(2, 3); // Not recommended + * expect(2).to.be.within(1, 2); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is greater than or equal to the given number `start`, and less + * than or equal to the given number `finish`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.within(2, 4); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.within(2, 4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.within`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.within(2, 4); // Not recommended + * + * `.within` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(4).to.be.within(1, 3, 'nooo why fail??'); + * expect(4, 'nooo why fail??').to.be.within(1, 3); + * + * @name within + * @param {Number} start lower bound inclusive + * @param {Number} finish upper bound inclusive + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + Assertion.addMethod('within', function (start, finish, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , startType = _.type(start).toLowerCase() + , finishType = _.type(finish).toLowerCase() + , errorMessage + , shouldThrow = true + , range = (startType === 'date' && finishType === 'date') + ? start.toUTCString() + '..' + finish.toUTCString() + : start + '..' + finish; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) { + errorMessage = msgPrefix + 'the arguments to within must be dates'; + } else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the arguments to within must be numbers'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount >= start && itemsCount <= finish + , 'expected #{this} to have a ' + descriptor + ' within ' + range + , 'expected #{this} to not have a ' + descriptor + ' within ' + range + ); + } else { + this.assert( + obj >= start && obj <= finish + , 'expected #{this} to be within ' + range + , 'expected #{this} to not be within ' + range + ); + } + }); + + /** + * ### .instanceof(constructor[, msg]) + * + * Asserts that the target is an instance of the given `constructor`. + * + * function Cat () { } + * + * expect(new Cat()).to.be.an.instanceof(Cat); + * expect([1, 2]).to.be.an.instanceof(Array); + * + * Add `.not` earlier in the chain to negate `.instanceof`. + * + * expect({a: 1}).to.not.be.an.instanceof(Array); + * + * `.instanceof` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1).to.be.an.instanceof(Array, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.an.instanceof(Array); + * + * Due to limitations in ES5, `.instanceof` may not always work as expected + * when using a transpiler such as Babel or TypeScript. In particular, it may + * produce unexpected results when subclassing built-in object such as + * `Array`, `Error`, and `Map`. See your transpiler's docs for details: + * + * - ([Babel](https://babeljs.io/docs/usage/caveats/#classes)) + * - ([TypeScript](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work)) + * + * The alias `.instanceOf` can be used interchangeably with `.instanceof`. + * + * @name instanceof + * @param {Constructor} constructor + * @param {String} msg _optional_ + * @alias instanceOf + * @namespace BDD + * @api public + */ + + function assertInstanceOf (constructor, msg) { + if (msg) flag(this, 'message', msg); + + var target = flag(this, 'object') + var ssfi = flag(this, 'ssfi'); + var flagMsg = flag(this, 'message'); + + try { + var isInstanceOf = target instanceof constructor; + } catch (err) { + if (err instanceof TypeError) { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'The instanceof assertion needs a constructor but ' + + _.type(constructor) + ' was given.', + undefined, + ssfi + ); + } + throw err; + } + + var name = _.getName(constructor); + if (name === null) { + name = 'an unnamed constructor'; + } + + this.assert( + isInstanceOf + , 'expected #{this} to be an instance of ' + name + , 'expected #{this} to not be an instance of ' + name + ); + }; + + Assertion.addMethod('instanceof', assertInstanceOf); + Assertion.addMethod('instanceOf', assertInstanceOf); + + /** + * ### .property(name[, val[, msg]]) + * + * Asserts that the target has a property with the given key `name`. + * + * expect({a: 1}).to.have.property('a'); + * + * When `val` is provided, `.property` also asserts that the property's value + * is equal to the given `val`. + * + * expect({a: 1}).to.have.property('a', 1); + * + * By default, strict (`===`) equality is used. Add `.deep` earlier in the + * chain to use deep equality instead. See the `deep-eql` project page for + * info on the deep equality algorithm: https://github.com/chaijs/deep-eql. + * + * // Target object deeply (but not strictly) has property `x: {a: 1}` + * expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); + * expect({x: {a: 1}}).to.not.have.property('x', {a: 1}); + * + * The target's enumerable and non-enumerable properties are always included + * in the search. By default, both own and inherited properties are included. + * Add `.own` earlier in the chain to exclude inherited properties from the + * search. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.have.own.property('a'); + * expect({a: 1}).to.have.own.property('a', 1); + * expect({a: 1}).to.have.property('b'); + * expect({a: 1}).to.not.have.own.property('b'); + * + * `.deep` and `.own` can be combined. + * + * expect({x: {a: 1}}).to.have.deep.own.property('x', {a: 1}); + * + * Add `.nested` earlier in the chain to enable dot- and bracket-notation when + * referencing nested properties. + * + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]'); + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]', 'y'); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 'x'}}).to.have.nested.property('\\.a.\\[b\\]'); + * + * `.deep` and `.nested` can be combined. + * + * expect({a: {b: [{c: 3}]}}) + * .to.have.deep.nested.property('a.b[0]', {c: 3}); + * + * `.own` and `.nested` cannot be combined. + * + * Add `.not` earlier in the chain to negate `.property`. + * + * expect({a: 1}).to.not.have.property('b'); + * + * However, it's dangerous to negate `.property` when providing `val`. The + * problem is that it creates uncertain expectations by asserting that the + * target either doesn't have a property with the given key `name`, or that it + * does have a property with the given key `name` but its value isn't equal to + * the given `val`. It's often best to identify the exact output that's + * expected, and then write an assertion that only accepts that exact output. + * + * When the target isn't expected to have a property with the given key + * `name`, it's often best to assert exactly that. + * + * expect({b: 2}).to.not.have.property('a'); // Recommended + * expect({b: 2}).to.not.have.property('a', 1); // Not recommended + * + * When the target is expected to have a property with the given key `name`, + * it's often best to assert that the property has its expected value, rather + * than asserting that it doesn't have one of many unexpected values. + * + * expect({a: 3}).to.have.property('a', 3); // Recommended + * expect({a: 3}).to.not.have.property('a', 1); // Not recommended + * + * `.property` changes the target of any assertions that follow in the chain + * to be the value of the property from the original target object. + * + * expect({a: 1}).to.have.property('a').that.is.a('number'); + * + * `.property` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing `val`, only use the + * second form. + * + * // Recommended + * expect({a: 1}).to.have.property('a', 2, 'nooo why fail??'); + * expect({a: 1}, 'nooo why fail??').to.have.property('a', 2); + * expect({a: 1}, 'nooo why fail??').to.have.property('b'); + * + * // Not recommended + * expect({a: 1}).to.have.property('b', undefined, 'nooo why fail??'); + * + * The above assertion isn't the same thing as not providing `val`. Instead, + * it's asserting that the target object has a `b` property that's equal to + * `undefined`. + * + * The assertions `.ownProperty` and `.haveOwnProperty` can be used + * interchangeably with `.own.property`. + * + * @name property + * @param {String} name + * @param {Mixed} val (optional) + * @param {String} msg _optional_ + * @returns value of property for chaining + * @namespace BDD + * @api public + */ + + function assertProperty (name, val, msg) { + if (msg) flag(this, 'message', msg); + + var isNested = flag(this, 'nested') + , isOwn = flag(this, 'own') + , flagMsg = flag(this, 'message') + , obj = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , nameType = typeof name; + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + if (isNested) { + if (nameType !== 'string') { + throw new AssertionError( + flagMsg + 'the argument to property must be a string when using nested syntax', + undefined, + ssfi + ); + } + } else { + if (nameType !== 'string' && nameType !== 'number' && nameType !== 'symbol') { + throw new AssertionError( + flagMsg + 'the argument to property must be a string, number, or symbol', + undefined, + ssfi + ); + } + } + + if (isNested && isOwn) { + throw new AssertionError( + flagMsg + 'The "nested" and "own" flags cannot be combined.', + undefined, + ssfi + ); + } + + if (obj === null || obj === undefined) { + throw new AssertionError( + flagMsg + 'Target cannot be null or undefined.', + undefined, + ssfi + ); + } + + var isDeep = flag(this, 'deep') + , negate = flag(this, 'negate') + , pathInfo = isNested ? _.getPathInfo(obj, name) : null + , value = isNested ? pathInfo.value : obj[name]; + + var descriptor = ''; + if (isDeep) descriptor += 'deep '; + if (isOwn) descriptor += 'own '; + if (isNested) descriptor += 'nested '; + descriptor += 'property '; + + var hasProperty; + if (isOwn) hasProperty = Object.prototype.hasOwnProperty.call(obj, name); + else if (isNested) hasProperty = pathInfo.exists; + else hasProperty = _.hasProperty(obj, name); + + // When performing a negated assertion for both name and val, merely having + // a property with the given name isn't enough to cause the assertion to + // fail. It must both have a property with the given name, and the value of + // that property must equal the given val. Therefore, skip this assertion in + // favor of the next. + if (!negate || arguments.length === 1) { + this.assert( + hasProperty + , 'expected #{this} to have ' + descriptor + _.inspect(name) + , 'expected #{this} to not have ' + descriptor + _.inspect(name)); + } + + if (arguments.length > 1) { + this.assert( + hasProperty && (isDeep ? _.eql(val, value) : val === value) + , 'expected #{this} to have ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' + , 'expected #{this} to not have ' + descriptor + _.inspect(name) + ' of #{act}' + , val + , value + ); + } + + flag(this, 'object', value); + } + + Assertion.addMethod('property', assertProperty); + + function assertOwnProperty (name, value, msg) { + flag(this, 'own', true); + assertProperty.apply(this, arguments); + } + + Assertion.addMethod('ownProperty', assertOwnProperty); + Assertion.addMethod('haveOwnProperty', assertOwnProperty); + + /** + * ### .ownPropertyDescriptor(name[, descriptor[, msg]]) + * + * Asserts that the target has its own property descriptor with the given key + * `name`. Enumerable and non-enumerable properties are included in the + * search. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a'); + * + * When `descriptor` is provided, `.ownPropertyDescriptor` also asserts that + * the property's descriptor is deeply equal to the given `descriptor`. See + * the `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * Add `.not` earlier in the chain to negate `.ownPropertyDescriptor`. + * + * expect({a: 1}).to.not.have.ownPropertyDescriptor('b'); + * + * However, it's dangerous to negate `.ownPropertyDescriptor` when providing + * a `descriptor`. The problem is that it creates uncertain expectations by + * asserting that the target either doesn't have a property descriptor with + * the given key `name`, or that it does have a property descriptor with the + * given key `name` but its not deeply equal to the given `descriptor`. It's + * often best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to have a property descriptor with the given + * key `name`, it's often best to assert exactly that. + * + * // Recommended + * expect({b: 2}).to.not.have.ownPropertyDescriptor('a'); + * + * // Not recommended + * expect({b: 2}).to.not.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * When the target is expected to have a property descriptor with the given + * key `name`, it's often best to assert that the property has its expected + * descriptor, rather than asserting that it doesn't have one of many + * unexpected descriptors. + * + * // Recommended + * expect({a: 3}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 3, + * }); + * + * // Not recommended + * expect({a: 3}).to.not.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * `.ownPropertyDescriptor` changes the target of any assertions that follow + * in the chain to be the value of the property descriptor from the original + * target object. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a') + * .that.has.property('enumerable', true); + * + * `.ownPropertyDescriptor` accepts an optional `msg` argument which is a + * custom error message to show when the assertion fails. The message can also + * be given as the second argument to `expect`. When not providing + * `descriptor`, only use the second form. + * + * // Recommended + * expect({a: 1}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 2, + * }, 'nooo why fail??'); + * + * // Recommended + * expect({a: 1}, 'nooo why fail??').to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 2, + * }); + * + * // Recommended + * expect({a: 1}, 'nooo why fail??').to.have.ownPropertyDescriptor('b'); + * + * // Not recommended + * expect({a: 1}) + * .to.have.ownPropertyDescriptor('b', undefined, 'nooo why fail??'); + * + * The above assertion isn't the same thing as not providing `descriptor`. + * Instead, it's asserting that the target object has a `b` property + * descriptor that's deeply equal to `undefined`. + * + * The alias `.haveOwnPropertyDescriptor` can be used interchangeably with + * `.ownPropertyDescriptor`. + * + * @name ownPropertyDescriptor + * @alias haveOwnPropertyDescriptor + * @param {String} name + * @param {Object} descriptor _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertOwnPropertyDescriptor (name, descriptor, msg) { + if (typeof descriptor === 'string') { + msg = descriptor; + descriptor = null; + } + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var actualDescriptor = Object.getOwnPropertyDescriptor(Object(obj), name); + if (actualDescriptor && descriptor) { + this.assert( + _.eql(descriptor, actualDescriptor) + , 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to match ' + _.inspect(descriptor) + ', got ' + _.inspect(actualDescriptor) + , 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to not match ' + _.inspect(descriptor) + , descriptor + , actualDescriptor + , true + ); + } else { + this.assert( + actualDescriptor + , 'expected #{this} to have an own property descriptor for ' + _.inspect(name) + , 'expected #{this} to not have an own property descriptor for ' + _.inspect(name) + ); + } + flag(this, 'object', actualDescriptor); + } + + Assertion.addMethod('ownPropertyDescriptor', assertOwnPropertyDescriptor); + Assertion.addMethod('haveOwnPropertyDescriptor', assertOwnPropertyDescriptor); + + /** + * ### .lengthOf(n[, msg]) + * + * Asserts that the target's `length` or `size` is equal to the given number + * `n`. + * + * expect([1, 2, 3]).to.have.lengthOf(3); + * expect('foo').to.have.lengthOf(3); + * expect(new Set([1, 2, 3])).to.have.lengthOf(3); + * expect(new Map([['a', 1], ['b', 2], ['c', 3]])).to.have.lengthOf(3); + * + * Add `.not` earlier in the chain to negate `.lengthOf`. However, it's often + * best to assert that the target's `length` property is equal to its expected + * value, rather than not equal to one of many unexpected values. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.not.have.lengthOf(4); // Not recommended + * + * `.lengthOf` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2, 3]).to.have.lengthOf(2, 'nooo why fail??'); + * expect([1, 2, 3], 'nooo why fail??').to.have.lengthOf(2); + * + * `.lengthOf` can also be used as a language chain, causing all `.above`, + * `.below`, `.least`, `.most`, and `.within` assertions that follow in the + * chain to use the target's `length` property as the target. However, it's + * often best to assert that the target's `length` property is equal to its + * expected length, rather than asserting that its `length` property falls + * within some range of values. + * + * // Recommended + * expect([1, 2, 3]).to.have.lengthOf(3); + * + * // Not recommended + * expect([1, 2, 3]).to.have.lengthOf.above(2); + * expect([1, 2, 3]).to.have.lengthOf.below(4); + * expect([1, 2, 3]).to.have.lengthOf.at.least(3); + * expect([1, 2, 3]).to.have.lengthOf.at.most(3); + * expect([1, 2, 3]).to.have.lengthOf.within(2,4); + * + * Due to a compatibility issue, the alias `.length` can't be chained directly + * off of an uninvoked method such as `.a`. Therefore, `.length` can't be used + * interchangeably with `.lengthOf` in every situation. It's recommended to + * always use `.lengthOf` instead of `.length`. + * + * expect([1, 2, 3]).to.have.a.length(3); // incompatible; throws error + * expect([1, 2, 3]).to.have.a.lengthOf(3); // passes as expected + * + * @name lengthOf + * @alias length + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertLengthChain () { + flag(this, 'doLength', true); + } + + function assertLength (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , objType = _.type(obj).toLowerCase() + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi') + , descriptor = 'length' + , itemsCount; + + switch (objType) { + case 'map': + case 'set': + descriptor = 'size'; + itemsCount = obj.size; + break; + default: + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + itemsCount = obj.length; + } + + this.assert( + itemsCount == n + , 'expected #{this} to have a ' + descriptor + ' of #{exp} but got #{act}' + , 'expected #{this} to not have a ' + descriptor + ' of #{act}' + , n + , itemsCount + ); + } + + Assertion.addChainableMethod('length', assertLength, assertLengthChain); + Assertion.addChainableMethod('lengthOf', assertLength, assertLengthChain); + + /** + * ### .match(re[, msg]) + * + * Asserts that the target matches the given regular expression `re`. + * + * expect('foobar').to.match(/^foo/); + * + * Add `.not` earlier in the chain to negate `.match`. + * + * expect('foobar').to.not.match(/taco/); + * + * `.match` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect('foobar').to.match(/taco/, 'nooo why fail??'); + * expect('foobar', 'nooo why fail??').to.match(/taco/); + * + * The alias `.matches` can be used interchangeably with `.match`. + * + * @name match + * @alias matches + * @param {RegExp} re + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + function assertMatch(re, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + re.exec(obj) + , 'expected #{this} to match ' + re + , 'expected #{this} not to match ' + re + ); + } + + Assertion.addMethod('match', assertMatch); + Assertion.addMethod('matches', assertMatch); + + /** + * ### .string(str[, msg]) + * + * Asserts that the target string contains the given substring `str`. + * + * expect('foobar').to.have.string('bar'); + * + * Add `.not` earlier in the chain to negate `.string`. + * + * expect('foobar').to.not.have.string('taco'); + * + * `.string` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect('foobar').to.have.string('taco', 'nooo why fail??'); + * expect('foobar', 'nooo why fail??').to.have.string('taco'); + * + * @name string + * @param {String} str + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + Assertion.addMethod('string', function (str, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(obj, flagMsg, ssfi, true).is.a('string'); + + this.assert( + ~obj.indexOf(str) + , 'expected #{this} to contain ' + _.inspect(str) + , 'expected #{this} to not contain ' + _.inspect(str) + ); + }); + + /** + * ### .keys(key1[, key2[, ...]]) + * + * Asserts that the target object, array, map, or set has the given keys. Only + * the target's own inherited properties are included in the search. + * + * When the target is an object or array, keys can be provided as one or more + * string arguments, a single array argument, or a single object argument. In + * the latter case, only the keys in the given object matter; the values are + * ignored. + * + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * expect(['x', 'y']).to.have.all.keys(0, 1); + * + * expect({a: 1, b: 2}).to.have.all.keys(['a', 'b']); + * expect(['x', 'y']).to.have.all.keys([0, 1]); + * + * expect({a: 1, b: 2}).to.have.all.keys({a: 4, b: 5}); // ignore 4 and 5 + * expect(['x', 'y']).to.have.all.keys({0: 4, 1: 5}); // ignore 4 and 5 + * + * When the target is a map or set, each key must be provided as a separate + * argument. + * + * expect(new Map([['a', 1], ['b', 2]])).to.have.all.keys('a', 'b'); + * expect(new Set(['a', 'b'])).to.have.all.keys('a', 'b'); + * + * Because `.keys` does different things based on the target's type, it's + * important to check the target's type before using `.keys`. See the `.a` doc + * for info on testing a target's type. + * + * expect({a: 1, b: 2}).to.be.an('object').that.has.all.keys('a', 'b'); + * + * By default, strict (`===`) equality is used to compare keys of maps and + * sets. Add `.deep` earlier in the chain to use deep equality instead. See + * the `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target set deeply (but not strictly) has key `{a: 1}` + * expect(new Set([{a: 1}])).to.have.all.deep.keys([{a: 1}]); + * expect(new Set([{a: 1}])).to.not.have.all.keys([{a: 1}]); + * + * By default, the target must have all of the given keys and no more. Add + * `.any` earlier in the chain to only require that the target have at least + * one of the given keys. Also, add `.not` earlier in the chain to negate + * `.keys`. It's often best to add `.any` when negating `.keys`, and to use + * `.all` when asserting `.keys` without negation. + * + * When negating `.keys`, `.any` is preferred because `.not.any.keys` asserts + * exactly what's expected of the output, whereas `.not.all.keys` creates + * uncertain expectations. + * + * // Recommended; asserts that target doesn't have any of the given keys + * expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd'); + * + * // Not recommended; asserts that target doesn't have all of the given + * // keys but may or may not have some of them + * expect({a: 1, b: 2}).to.not.have.all.keys('c', 'd'); + * + * When asserting `.keys` without negation, `.all` is preferred because + * `.all.keys` asserts exactly what's expected of the output, whereas + * `.any.keys` creates uncertain expectations. + * + * // Recommended; asserts that target has all the given keys + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * + * // Not recommended; asserts that target has at least one of the given + * // keys but may or may not have more of them + * expect({a: 1, b: 2}).to.have.any.keys('a', 'b'); + * + * Note that `.all` is used by default when neither `.all` nor `.any` appear + * earlier in the chain. However, it's often best to add `.all` anyway because + * it improves readability. + * + * // Both assertions are identical + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); // Recommended + * expect({a: 1, b: 2}).to.have.keys('a', 'b'); // Not recommended + * + * Add `.include` earlier in the chain to require that the target's keys be a + * superset of the expected keys, rather than identical sets. + * + * // Target object's keys are a superset of ['a', 'b'] but not identical + * expect({a: 1, b: 2, c: 3}).to.include.all.keys('a', 'b'); + * expect({a: 1, b: 2, c: 3}).to.not.have.all.keys('a', 'b'); + * + * However, if `.any` and `.include` are combined, only the `.any` takes + * effect. The `.include` is ignored in this case. + * + * // Both assertions are identical + * expect({a: 1}).to.have.any.keys('a', 'b'); + * expect({a: 1}).to.include.any.keys('a', 'b'); + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.have.key('b'); + * + * The alias `.key` can be used interchangeably with `.keys`. + * + * @name keys + * @alias key + * @param {...String|Array|Object} keys + * @namespace BDD + * @api public + */ + + function assertKeys (keys) { + var obj = flag(this, 'object') + , objType = _.type(obj) + , keysType = _.type(keys) + , ssfi = flag(this, 'ssfi') + , isDeep = flag(this, 'deep') + , str + , deepStr = '' + , actual + , ok = true + , flagMsg = flag(this, 'message'); + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + var mixedArgsMsg = flagMsg + 'when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments'; + + if (objType === 'Map' || objType === 'Set') { + deepStr = isDeep ? 'deeply ' : ''; + actual = []; + + // Map and Set '.keys' aren't supported in IE 11. Therefore, use .forEach. + obj.forEach(function (val, key) { actual.push(key) }); + + if (keysType !== 'Array') { + keys = Array.prototype.slice.call(arguments); + } + } else { + actual = _.getOwnEnumerableProperties(obj); + + switch (keysType) { + case 'Array': + if (arguments.length > 1) { + throw new AssertionError(mixedArgsMsg, undefined, ssfi); + } + break; + case 'Object': + if (arguments.length > 1) { + throw new AssertionError(mixedArgsMsg, undefined, ssfi); + } + keys = Object.keys(keys); + break; + default: + keys = Array.prototype.slice.call(arguments); + } + + // Only stringify non-Symbols because Symbols would become "Symbol()" + keys = keys.map(function (val) { + return typeof val === 'symbol' ? val : String(val); + }); + } + + if (!keys.length) { + throw new AssertionError(flagMsg + 'keys required', undefined, ssfi); + } + + var len = keys.length + , any = flag(this, 'any') + , all = flag(this, 'all') + , expected = keys; + + if (!any && !all) { + all = true; + } + + // Has any + if (any) { + ok = expected.some(function(expectedKey) { + return actual.some(function(actualKey) { + if (isDeep) { + return _.eql(expectedKey, actualKey); + } else { + return expectedKey === actualKey; + } + }); + }); + } + + // Has all + if (all) { + ok = expected.every(function(expectedKey) { + return actual.some(function(actualKey) { + if (isDeep) { + return _.eql(expectedKey, actualKey); + } else { + return expectedKey === actualKey; + } + }); + }); + + if (!flag(this, 'contains')) { + ok = ok && keys.length == actual.length; + } + } + + // Key string + if (len > 1) { + keys = keys.map(function(key) { + return _.inspect(key); + }); + var last = keys.pop(); + if (all) { + str = keys.join(', ') + ', and ' + last; + } + if (any) { + str = keys.join(', ') + ', or ' + last; + } + } else { + str = _.inspect(keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; + + // Assertion + this.assert( + ok + , 'expected #{this} to ' + deepStr + str + , 'expected #{this} to not ' + deepStr + str + , expected.slice(0).sort(_.compareByInspect) + , actual.sort(_.compareByInspect) + , true + ); + } + + Assertion.addMethod('keys', assertKeys); + Assertion.addMethod('key', assertKeys); + + /** + * ### .throw([errorLike], [errMsgMatcher], [msg]) + * + * When no arguments are provided, `.throw` invokes the target function and + * asserts that an error is thrown. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(); + * + * When one argument is provided, and it's an error constructor, `.throw` + * invokes the target function and asserts that an error is thrown that's an + * instance of that error constructor. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(TypeError); + * + * When one argument is provided, and it's an error instance, `.throw` invokes + * the target function and asserts that an error is thrown that's strictly + * (`===`) equal to that error instance. + * + * var err = new TypeError('Illegal salmon!'); + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(err); + * + * When one argument is provided, and it's a string, `.throw` invokes the + * target function and asserts that an error is thrown with a message that + * contains that string. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw('salmon'); + * + * When one argument is provided, and it's a regular expression, `.throw` + * invokes the target function and asserts that an error is thrown with a + * message that matches that regular expression. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(/salmon/); + * + * When two arguments are provided, and the first is an error instance or + * constructor, and the second is a string or regular expression, `.throw` + * invokes the function and asserts that an error is thrown that fulfills both + * conditions as described above. + * + * var err = new TypeError('Illegal salmon!'); + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(TypeError, 'salmon'); + * expect(badFn).to.throw(TypeError, /salmon/); + * expect(badFn).to.throw(err, 'salmon'); + * expect(badFn).to.throw(err, /salmon/); + * + * Add `.not` earlier in the chain to negate `.throw`. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.not.throw(); + * + * However, it's dangerous to negate `.throw` when providing any arguments. + * The problem is that it creates uncertain expectations by asserting that the + * target either doesn't throw an error, or that it throws an error but of a + * different type than the given type, or that it throws an error of the given + * type but with a message that doesn't include the given string. It's often + * best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to throw an error, it's often best to assert + * exactly that. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.not.throw(); // Recommended + * expect(goodFn).to.not.throw(ReferenceError, 'x'); // Not recommended + * + * When the target is expected to throw an error, it's often best to assert + * that the error is of its expected type, and has a message that includes an + * expected string, rather than asserting that it doesn't have one of many + * unexpected types, and doesn't have a message that includes some string. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(TypeError, 'salmon'); // Recommended + * expect(badFn).to.not.throw(ReferenceError, 'x'); // Not recommended + * + * `.throw` changes the target of any assertions that follow in the chain to + * be the error object that's thrown. + * + * var err = new TypeError('Illegal salmon!'); + * err.code = 42; + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(TypeError).with.property('code', 42); + * + * `.throw` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. When not providing two arguments, always use + * the second form. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.throw(TypeError, 'x', 'nooo why fail??'); + * expect(goodFn, 'nooo why fail??').to.throw(); + * + * Due to limitations in ES5, `.throw` may not always work as expected when + * using a transpiler such as Babel or TypeScript. In particular, it may + * produce unexpected results when subclassing the built-in `Error` object and + * then passing the subclassed constructor to `.throw`. See your transpiler's + * docs for details: + * + * - ([Babel](https://babeljs.io/docs/usage/caveats/#classes)) + * - ([TypeScript](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work)) + * + * Beware of some common mistakes when using the `throw` assertion. One common + * mistake is to accidentally invoke the function yourself instead of letting + * the `throw` assertion invoke the function for you. For example, when + * testing if a function named `fn` throws, provide `fn` instead of `fn()` as + * the target for the assertion. + * + * expect(fn).to.throw(); // Good! Tests `fn` as desired + * expect(fn()).to.throw(); // Bad! Tests result of `fn()`, not `fn` + * + * If you need to assert that your function `fn` throws when passed certain + * arguments, then wrap a call to `fn` inside of another function. + * + * expect(function () { fn(42); }).to.throw(); // Function expression + * expect(() => fn(42)).to.throw(); // ES6 arrow function + * + * Another common mistake is to provide an object method (or any stand-alone + * function that relies on `this`) as the target of the assertion. Doing so is + * problematic because the `this` context will be lost when the function is + * invoked by `.throw`; there's no way for it to know what `this` is supposed + * to be. There are two ways around this problem. One solution is to wrap the + * method or function call inside of another function. Another solution is to + * use `bind`. + * + * expect(function () { cat.meow(); }).to.throw(); // Function expression + * expect(() => cat.meow()).to.throw(); // ES6 arrow function + * expect(cat.meow.bind(cat)).to.throw(); // Bind + * + * Finally, it's worth mentioning that it's a best practice in JavaScript to + * only throw `Error` and derivatives of `Error` such as `ReferenceError`, + * `TypeError`, and user-defined objects that extend `Error`. No other type of + * value will generate a stack trace when initialized. With that said, the + * `throw` assertion does technically support any type of value being thrown, + * not just `Error` and its derivatives. + * + * The aliases `.throws` and `.Throw` can be used interchangeably with + * `.throw`. + * + * @name throw + * @alias throws + * @alias Throw + * @param {Error|ErrorConstructor} errorLike + * @param {String|RegExp} errMsgMatcher error message + * @param {String} msg _optional_ + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @returns error for chaining (null if no error) + * @namespace BDD + * @api public + */ + + function assertThrows (errorLike, errMsgMatcher, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , flagMsg = flag(this, 'message') + , negate = flag(this, 'negate') || false; + new Assertion(obj, flagMsg, ssfi, true).is.a('function'); + + if (errorLike instanceof RegExp || typeof errorLike === 'string') { + errMsgMatcher = errorLike; + errorLike = null; + } + + var caughtErr; + try { + obj(); + } catch (err) { + caughtErr = err; + } + + // If we have the negate flag enabled and at least one valid argument it means we do expect an error + // but we want it to match a given set of criteria + var everyArgIsUndefined = errorLike === undefined && errMsgMatcher === undefined; + + // If we've got the negate flag enabled and both args, we should only fail if both aren't compatible + // See Issue #551 and PR #683@GitHub + var everyArgIsDefined = Boolean(errorLike && errMsgMatcher); + var errorLikeFail = false; + var errMsgMatcherFail = false; + + // Checking if error was thrown + if (everyArgIsUndefined || !everyArgIsUndefined && !negate) { + // We need this to display results correctly according to their types + var errorLikeString = 'an error'; + if (errorLike instanceof Error) { + errorLikeString = '#{exp}'; + } else if (errorLike) { + errorLikeString = _.checkError.getConstructorName(errorLike); + } + + this.assert( + caughtErr + , 'expected #{this} to throw ' + errorLikeString + , 'expected #{this} to not throw an error but #{act} was thrown' + , errorLike && errorLike.toString() + , (caughtErr instanceof Error ? + caughtErr.toString() : (typeof caughtErr === 'string' ? caughtErr : caughtErr && + _.checkError.getConstructorName(caughtErr))) + ); + } + + if (errorLike && caughtErr) { + // We should compare instances only if `errorLike` is an instance of `Error` + if (errorLike instanceof Error) { + var isCompatibleInstance = _.checkError.compatibleInstance(caughtErr, errorLike); + + if (isCompatibleInstance === negate) { + // These checks were created to ensure we won't fail too soon when we've got both args and a negate + // See Issue #551 and PR #683@GitHub + if (everyArgIsDefined && negate) { + errorLikeFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr && !negate ? ' but #{act} was thrown' : '') + , errorLike.toString() + , caughtErr.toString() + ); + } + } + } + + var isCompatibleConstructor = _.checkError.compatibleConstructor(caughtErr, errorLike); + if (isCompatibleConstructor === negate) { + if (everyArgIsDefined && negate) { + errorLikeFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') + , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) + , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) + ); + } + } + } + + if (caughtErr && errMsgMatcher !== undefined && errMsgMatcher !== null) { + // Here we check compatible messages + var placeholder = 'including'; + if (errMsgMatcher instanceof RegExp) { + placeholder = 'matching' + } + + var isCompatibleMessage = _.checkError.compatibleMessage(caughtErr, errMsgMatcher); + if (isCompatibleMessage === negate) { + if (everyArgIsDefined && negate) { + errMsgMatcherFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw error ' + placeholder + ' #{exp} but got #{act}' + , 'expected #{this} to throw error not ' + placeholder + ' #{exp}' + , errMsgMatcher + , _.checkError.getMessage(caughtErr) + ); + } + } + } + + // If both assertions failed and both should've matched we throw an error + if (errorLikeFail && errMsgMatcherFail) { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') + , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) + , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) + ); + } + + flag(this, 'object', caughtErr); + }; + + Assertion.addMethod('throw', assertThrows); + Assertion.addMethod('throws', assertThrows); + Assertion.addMethod('Throw', assertThrows); + + /** + * ### .respondTo(method[, msg]) + * + * When the target is a non-function object, `.respondTo` asserts that the + * target has a method with the given name `method`. The method can be own or + * inherited, and it can be enumerable or non-enumerable. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(new Cat()).to.respondTo('meow'); + * + * When the target is a function, `.respondTo` asserts that the target's + * `prototype` property has a method with the given name `method`. Again, the + * method can be own or inherited, and it can be enumerable or non-enumerable. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(Cat).to.respondTo('meow'); + * + * Add `.itself` earlier in the chain to force `.respondTo` to treat the + * target as a non-function object, even if it's a function. Thus, it asserts + * that the target has a method with the given name `method`, rather than + * asserting that the target's `prototype` property has a method with the + * given name `method`. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * Cat.hiss = function () {}; + * + * expect(Cat).itself.to.respondTo('hiss').but.not.respondTo('meow'); + * + * When not adding `.itself`, it's important to check the target's type before + * using `.respondTo`. See the `.a` doc for info on checking a target's type. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(new Cat()).to.be.an('object').that.respondsTo('meow'); + * + * Add `.not` earlier in the chain to negate `.respondTo`. + * + * function Dog () {} + * Dog.prototype.bark = function () {}; + * + * expect(new Dog()).to.not.respondTo('meow'); + * + * `.respondTo` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect({}).to.respondTo('meow', 'nooo why fail??'); + * expect({}, 'nooo why fail??').to.respondTo('meow'); + * + * The alias `.respondsTo` can be used interchangeably with `.respondTo`. + * + * @name respondTo + * @alias respondsTo + * @param {String} method + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function respondTo (method, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , itself = flag(this, 'itself') + , context = ('function' === typeof obj && !itself) + ? obj.prototype[method] + : obj[method]; + + this.assert( + 'function' === typeof context + , 'expected #{this} to respond to ' + _.inspect(method) + , 'expected #{this} to not respond to ' + _.inspect(method) + ); + } + + Assertion.addMethod('respondTo', respondTo); + Assertion.addMethod('respondsTo', respondTo); + + /** + * ### .itself + * + * Forces all `.respondTo` assertions that follow in the chain to behave as if + * the target is a non-function object, even if it's a function. Thus, it + * causes `.respondTo` to assert that the target has a method with the given + * name, rather than asserting that the target's `prototype` property has a + * method with the given name. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * Cat.hiss = function () {}; + * + * expect(Cat).itself.to.respondTo('hiss').but.not.respondTo('meow'); + * + * @name itself + * @namespace BDD + * @api public + */ + + Assertion.addProperty('itself', function () { + flag(this, 'itself', true); + }); + + /** + * ### .satisfy(matcher[, msg]) + * + * Invokes the given `matcher` function with the target being passed as the + * first argument, and asserts that the value returned is truthy. + * + * expect(1).to.satisfy(function(num) { + * return num > 0; + * }); + * + * Add `.not` earlier in the chain to negate `.satisfy`. + * + * expect(1).to.not.satisfy(function(num) { + * return num > 2; + * }); + * + * `.satisfy` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1).to.satisfy(function(num) { + * return num > 2; + * }, 'nooo why fail??'); + * + * expect(1, 'nooo why fail??').to.satisfy(function(num) { + * return num > 2; + * }); + * + * The alias `.satisfies` can be used interchangeably with `.satisfy`. + * + * @name satisfy + * @alias satisfies + * @param {Function} matcher + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function satisfy (matcher, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var result = matcher(obj); + this.assert( + result + , 'expected #{this} to satisfy ' + _.objDisplay(matcher) + , 'expected #{this} to not satisfy' + _.objDisplay(matcher) + , flag(this, 'negate') ? false : true + , result + ); + } + + Assertion.addMethod('satisfy', satisfy); + Assertion.addMethod('satisfies', satisfy); + + /** + * ### .closeTo(expected, delta[, msg]) + * + * Asserts that the target is a number that's within a given +/- `delta` range + * of the given number `expected`. However, it's often best to assert that the + * target is equal to its expected value. + * + * // Recommended + * expect(1.5).to.equal(1.5); + * + * // Not recommended + * expect(1.5).to.be.closeTo(1, 0.5); + * expect(1.5).to.be.closeTo(2, 0.5); + * expect(1.5).to.be.closeTo(1, 1); + * + * Add `.not` earlier in the chain to negate `.closeTo`. + * + * expect(1.5).to.equal(1.5); // Recommended + * expect(1.5).to.not.be.closeTo(3, 1); // Not recommended + * + * `.closeTo` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1.5).to.be.closeTo(3, 1, 'nooo why fail??'); + * expect(1.5, 'nooo why fail??').to.be.closeTo(3, 1); + * + * The alias `.approximately` can be used interchangeably with `.closeTo`. + * + * @name closeTo + * @alias approximately + * @param {Number} expected + * @param {Number} delta + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function closeTo(expected, delta, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + if (typeof expected !== 'number' || typeof delta !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the arguments to closeTo or approximately must be numbers', + undefined, + ssfi + ); + } + + this.assert( + Math.abs(obj - expected) <= delta + , 'expected #{this} to be close to ' + expected + ' +/- ' + delta + , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta + ); + } + + Assertion.addMethod('closeTo', closeTo); + Assertion.addMethod('approximately', closeTo); + + // Note: Duplicates are ignored if testing for inclusion instead of sameness. + function isSubsetOf(subset, superset, cmp, contains, ordered) { + if (!contains) { + if (subset.length !== superset.length) return false; + superset = superset.slice(); + } + + return subset.every(function(elem, idx) { + if (ordered) return cmp ? cmp(elem, superset[idx]) : elem === superset[idx]; + + if (!cmp) { + var matchIdx = superset.indexOf(elem); + if (matchIdx === -1) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + } + + return superset.some(function(elem2, matchIdx) { + if (!cmp(elem, elem2)) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + }); + }); + } + + /** + * ### .members(set[, msg]) + * + * Asserts that the target array has the same members as the given array + * `set`. + * + * expect([1, 2, 3]).to.have.members([2, 1, 3]); + * expect([1, 2, 2]).to.have.members([2, 1, 2]); + * + * By default, members are compared using strict (`===`) equality. Add `.deep` + * earlier in the chain to use deep equality instead. See the `deep-eql` + * project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target array deeply (but not strictly) has member `{a: 1}` + * expect([{a: 1}]).to.have.deep.members([{a: 1}]); + * expect([{a: 1}]).to.not.have.members([{a: 1}]); + * + * By default, order doesn't matter. Add `.ordered` earlier in the chain to + * require that members appear in the same order. + * + * expect([1, 2, 3]).to.have.ordered.members([1, 2, 3]); + * expect([1, 2, 3]).to.have.members([2, 1, 3]) + * .but.not.ordered.members([2, 1, 3]); + * + * By default, both arrays must be the same size. Add `.include` earlier in + * the chain to require that the target's members be a superset of the + * expected members. Note that duplicates are ignored in the subset when + * `.include` is added. + * + * // Target array is a superset of [1, 2] but not identical + * expect([1, 2, 3]).to.include.members([1, 2]); + * expect([1, 2, 3]).to.not.have.members([1, 2]); + * + * // Duplicates in the subset are ignored + * expect([1, 2, 3]).to.include.members([1, 2, 2, 2]); + * + * `.deep`, `.ordered`, and `.include` can all be combined. However, if + * `.include` and `.ordered` are combined, the ordering begins at the start of + * both arrays. + * + * expect([{a: 1}, {b: 2}, {c: 3}]) + * .to.include.deep.ordered.members([{a: 1}, {b: 2}]) + * .but.not.include.deep.ordered.members([{b: 2}, {c: 3}]); + * + * Add `.not` earlier in the chain to negate `.members`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the target array doesn't have all of the same members as + * the given array `set` but may or may not have some of them. It's often best + * to identify the exact output that's expected, and then write an assertion + * that only accepts that exact output. + * + * expect([1, 2]).to.not.include(3).and.not.include(4); // Recommended + * expect([1, 2]).to.not.have.members([3, 4]); // Not recommended + * + * `.members` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2]).to.have.members([1, 2, 3], 'nooo why fail??'); + * expect([1, 2], 'nooo why fail??').to.have.members([1, 2, 3]); + * + * @name members + * @param {Array} set + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + Assertion.addMethod('members', function (subset, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + new Assertion(obj, flagMsg, ssfi, true).to.be.an('array'); + new Assertion(subset, flagMsg, ssfi, true).to.be.an('array'); + + var contains = flag(this, 'contains'); + var ordered = flag(this, 'ordered'); + + var subject, failMsg, failNegateMsg; + + if (contains) { + subject = ordered ? 'an ordered superset' : 'a superset'; + failMsg = 'expected #{this} to be ' + subject + ' of #{exp}'; + failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}'; + } else { + subject = ordered ? 'ordered members' : 'members'; + failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}'; + failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}'; + } + + var cmp = flag(this, 'deep') ? _.eql : undefined; + + this.assert( + isSubsetOf(subset, obj, cmp, contains, ordered) + , failMsg + , failNegateMsg + , subset + , obj + , true + ); + }); + + /** + * ### .oneOf(list[, msg]) + * + * Asserts that the target is a member of the given array `list`. However, + * it's often best to assert that the target is equal to its expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.oneOf([1, 2, 3]); // Not recommended + * + * Comparisons are performed using strict (`===`) equality. + * + * Add `.not` earlier in the chain to negate `.oneOf`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.oneOf([2, 3, 4]); // Not recommended + * + * `.oneOf` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.oneOf([2, 3, 4], 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.oneOf([2, 3, 4]); + * + * @name oneOf + * @param {Array<*>} list + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function oneOf (list, msg) { + if (msg) flag(this, 'message', msg); + var expected = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(list, flagMsg, ssfi, true).to.be.an('array'); + + this.assert( + list.indexOf(expected) > -1 + , 'expected #{this} to be one of #{exp}' + , 'expected #{this} to not be one of #{exp}' + , list + , expected + ); + } + + Assertion.addMethod('oneOf', oneOf); + + /** + * ### .change(subject[, prop[, msg]]) + * + * When one argument is provided, `.change` asserts that the given function + * `subject` returns a different value when it's invoked before the target + * function compared to when it's invoked afterward. However, it's often best + * to assert that `subject` is equal to its expected value. + * + * var dots = '' + * , addDot = function () { dots += '.'; } + * , getDots = function () { return dots; }; + * + * // Recommended + * expect(getDots()).to.equal(''); + * addDot(); + * expect(getDots()).to.equal('.'); + * + * // Not recommended + * expect(addDot).to.change(getDots); + * + * When two arguments are provided, `.change` asserts that the value of the + * given object `subject`'s `prop` property is different before invoking the + * target function compared to afterward. + * + * var myObj = {dots: ''} + * , addDot = function () { myObj.dots += '.'; }; + * + * // Recommended + * expect(myObj).to.have.property('dots', ''); + * addDot(); + * expect(myObj).to.have.property('dots', '.'); + * + * // Not recommended + * expect(addDot).to.change(myObj, 'dots'); + * + * Strict (`===`) equality is used to compare before and after values. + * + * Add `.not` earlier in the chain to negate `.change`. + * + * var dots = '' + * , noop = function () {} + * , getDots = function () { return dots; }; + * + * expect(noop).to.not.change(getDots); + * + * var myObj = {dots: ''} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'dots'); + * + * `.change` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {dots: ''} + * , addDot = function () { myObj.dots += '.'; }; + * + * expect(addDot).to.not.change(myObj, 'dots', 'nooo why fail??'); + * + * var dots = '' + * , addDot = function () { dots += '.'; } + * , getDots = function () { return dots; }; + * + * expect(addDot, 'nooo why fail??').to.not.change(getDots); + * + * `.change` also causes all `.by` assertions that follow in the chain to + * assert how much a numeric subject was increased or decreased by. However, + * it's dangerous to use `.change.by`. The problem is that it creates + * uncertain expectations by asserting that the subject either increases by + * the given delta, or that it decreases by the given delta. It's often best + * to identify the exact output that's expected, and then write an assertion + * that only accepts that exact output. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; } + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * The alias `.changes` can be used interchangeably with `.change`. + * + * @name change + * @alias changes + * @param {String} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertChanges (subject, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + fn(); + + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + // This gets flagged because of the .by(delta) assertion + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'change'); + flag(this, 'realDelta', final !== initial); + + this.assert( + initial !== final + , 'expected ' + msgObj + ' to change' + , 'expected ' + msgObj + ' to not change' + ); + } + + Assertion.addMethod('change', assertChanges); + Assertion.addMethod('changes', assertChanges); + + /** + * ### .increase(subject[, prop[, msg]]) + * + * When one argument is provided, `.increase` asserts that the given function + * `subject` returns a greater number when it's invoked after invoking the + * target function compared to when it's invoked beforehand. `.increase` also + * causes all `.by` assertions that follow in the chain to assert how much + * greater of a number is returned. It's often best to assert that the return + * value increased by the expected amount, rather than asserting it increased + * by any amount. + * + * var val = 1 + * , addTwo = function () { val += 2; } + * , getVal = function () { return val; }; + * + * expect(addTwo).to.increase(getVal).by(2); // Recommended + * expect(addTwo).to.increase(getVal); // Not recommended + * + * When two arguments are provided, `.increase` asserts that the value of the + * given object `subject`'s `prop` property is greater after invoking the + * target function compared to beforehand. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.increase(myObj, 'val'); // Not recommended + * + * Add `.not` earlier in the chain to negate `.increase`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either decreases, or that it stays the same. + * It's often best to identify the exact output that's expected, and then + * write an assertion that only accepts that exact output. + * + * When the subject is expected to decrease, it's often best to assert that it + * decreased by the expected amount. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.not.increase(myObj, 'val'); // Not recommended + * + * When the subject is expected to stay the same, it's often best to assert + * exactly that. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'val'); // Recommended + * expect(noop).to.not.increase(myObj, 'val'); // Not recommended + * + * `.increase` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.increase(myObj, 'val', 'nooo why fail??'); + * + * var val = 1 + * , noop = function () {} + * , getVal = function () { return val; }; + * + * expect(noop, 'nooo why fail??').to.increase(getVal); + * + * The alias `.increases` can be used interchangeably with `.increase`. + * + * @name increase + * @alias increases + * @param {String|Function} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertIncreases (subject, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + // Make sure that the target is a number + new Assertion(initial, flagMsg, ssfi, true).is.a('number'); + + fn(); + + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'increase'); + flag(this, 'realDelta', final - initial); + + this.assert( + final - initial > 0 + , 'expected ' + msgObj + ' to increase' + , 'expected ' + msgObj + ' to not increase' + ); + } + + Assertion.addMethod('increase', assertIncreases); + Assertion.addMethod('increases', assertIncreases); + + /** + * ### .decrease(subject[, prop[, msg]]) + * + * When one argument is provided, `.decrease` asserts that the given function + * `subject` returns a lesser number when it's invoked after invoking the + * target function compared to when it's invoked beforehand. `.decrease` also + * causes all `.by` assertions that follow in the chain to assert how much + * lesser of a number is returned. It's often best to assert that the return + * value decreased by the expected amount, rather than asserting it decreased + * by any amount. + * + * var val = 1 + * , subtractTwo = function () { val -= 2; } + * , getVal = function () { return val; }; + * + * expect(subtractTwo).to.decrease(getVal).by(2); // Recommended + * expect(subtractTwo).to.decrease(getVal); // Not recommended + * + * When two arguments are provided, `.decrease` asserts that the value of the + * given object `subject`'s `prop` property is lesser after invoking the + * target function compared to beforehand. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.decrease(myObj, 'val'); // Not recommended + * + * Add `.not` earlier in the chain to negate `.decrease`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either increases, or that it stays the same. + * It's often best to identify the exact output that's expected, and then + * write an assertion that only accepts that exact output. + * + * When the subject is expected to increase, it's often best to assert that it + * increased by the expected amount. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.not.decrease(myObj, 'val'); // Not recommended + * + * When the subject is expected to stay the same, it's often best to assert + * exactly that. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'val'); // Recommended + * expect(noop).to.not.decrease(myObj, 'val'); // Not recommended + * + * `.decrease` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.decrease(myObj, 'val', 'nooo why fail??'); + * + * var val = 1 + * , noop = function () {} + * , getVal = function () { return val; }; + * + * expect(noop, 'nooo why fail??').to.decrease(getVal); + * + * The alias `.decreases` can be used interchangeably with `.decrease`. + * + * @name decrease + * @alias decreases + * @param {String|Function} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertDecreases (subject, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + // Make sure that the target is a number + new Assertion(initial, flagMsg, ssfi, true).is.a('number'); + + fn(); + + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'decrease'); + flag(this, 'realDelta', initial - final); + + this.assert( + final - initial < 0 + , 'expected ' + msgObj + ' to decrease' + , 'expected ' + msgObj + ' to not decrease' + ); + } + + Assertion.addMethod('decrease', assertDecreases); + Assertion.addMethod('decreases', assertDecreases); + + /** + * ### .by(delta[, msg]) + * + * When following an `.increase` assertion in the chain, `.by` asserts that + * the subject of the `.increase` assertion increased by the given `delta`. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); + * + * When following a `.decrease` assertion in the chain, `.by` asserts that the + * subject of the `.decrease` assertion decreased by the given `delta`. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); + * + * When following a `.change` assertion in the chain, `.by` asserts that the + * subject of the `.change` assertion either increased or decreased by the + * given `delta`. However, it's dangerous to use `.change.by`. The problem is + * that it creates uncertain expectations. It's often best to identify the + * exact output that's expected, and then write an assertion that only accepts + * that exact output. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; } + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.by`. However, it's often best + * to assert that the subject changed by its expected delta, rather than + * asserting that it didn't change by one of countless unexpected deltas. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * // Recommended + * expect(addTwo).to.increase(myObj, 'val').by(2); + * + * // Not recommended + * expect(addTwo).to.increase(myObj, 'val').but.not.by(3); + * + * `.by` accepts an optional `msg` argument which is a custom error message to + * show when the assertion fails. The message can also be given as the second + * argument to `expect`. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(3, 'nooo why fail??'); + * expect(addTwo, 'nooo why fail??').to.increase(myObj, 'val').by(3); + * + * @name by + * @param {Number} delta + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertDelta(delta, msg) { + if (msg) flag(this, 'message', msg); + + var msgObj = flag(this, 'deltaMsgObj'); + var initial = flag(this, 'initialDeltaValue'); + var final = flag(this, 'finalDeltaValue'); + var behavior = flag(this, 'deltaBehavior'); + var realDelta = flag(this, 'realDelta'); + + var expression; + if (behavior === 'change') { + expression = Math.abs(final - initial) === Math.abs(delta); + } else { + expression = realDelta === Math.abs(delta); + } + + this.assert( + expression + , 'expected ' + msgObj + ' to ' + behavior + ' by ' + delta + , 'expected ' + msgObj + ' to not ' + behavior + ' by ' + delta + ); + } + + Assertion.addMethod('by', assertDelta); + + /** + * ### .extensible + * + * Asserts that the target is extensible, which means that new properties can + * be added to it. Primitives are never extensible. + * + * expect({a: 1}).to.be.extensible; + * + * Add `.not` earlier in the chain to negate `.extensible`. + * + * var nonExtensibleObject = Object.preventExtensions({}) + * , sealedObject = Object.seal({}) + * , frozenObject = Object.freeze({}); + * + * expect(nonExtensibleObject).to.not.be.extensible; + * expect(sealedObject).to.not.be.extensible; + * expect(frozenObject).to.not.be.extensible; + * expect(1).to.not.be.extensible; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(1, 'nooo why fail??').to.be.extensible; + * + * @name extensible + * @namespace BDD + * @api public + */ + + Assertion.addProperty('extensible', function() { + var obj = flag(this, 'object'); + + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. + // In ES6, a non-object argument will be treated as if it was a non-extensible ordinary object, simply return false. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible + // The following provides ES6 behavior for ES5 environments. + + var isExtensible = obj === Object(obj) && Object.isExtensible(obj); + + this.assert( + isExtensible + , 'expected #{this} to be extensible' + , 'expected #{this} to not be extensible' + ); + }); + + /** + * ### .sealed + * + * Asserts that the target is sealed, which means that new properties can't be + * added to it, and its existing properties can't be reconfigured or deleted. + * However, it's possible that its existing properties can still be reassigned + * to different values. Primitives are always sealed. + * + * var sealedObject = Object.seal({}); + * var frozenObject = Object.freeze({}); + * + * expect(sealedObject).to.be.sealed; + * expect(frozenObject).to.be.sealed; + * expect(1).to.be.sealed; + * + * Add `.not` earlier in the chain to negate `.sealed`. + * + * expect({a: 1}).to.not.be.sealed; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.be.sealed; + * + * @name sealed + * @namespace BDD + * @api public + */ + + Assertion.addProperty('sealed', function() { + var obj = flag(this, 'object'); + + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. + // In ES6, a non-object argument will be treated as if it was a sealed ordinary object, simply return true. + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed + // The following provides ES6 behavior for ES5 environments. + + var isSealed = obj === Object(obj) ? Object.isSealed(obj) : true; + + this.assert( + isSealed + , 'expected #{this} to be sealed' + , 'expected #{this} to not be sealed' + ); + }); + + /** + * ### .frozen + * + * Asserts that the target is frozen, which means that new properties can't be + * added to it, and its existing properties can't be reassigned to different + * values, reconfigured, or deleted. Primitives are always frozen. + * + * var frozenObject = Object.freeze({}); + * + * expect(frozenObject).to.be.frozen; + * expect(1).to.be.frozen; + * + * Add `.not` earlier in the chain to negate `.frozen`. + * + * expect({a: 1}).to.not.be.frozen; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.be.frozen; + * + * @name frozen + * @namespace BDD + * @api public + */ + + Assertion.addProperty('frozen', function() { + var obj = flag(this, 'object'); + + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. + // In ES6, a non-object argument will be treated as if it was a frozen ordinary object, simply return true. + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen + // The following provides ES6 behavior for ES5 environments. + + var isFrozen = obj === Object(obj) ? Object.isFrozen(obj) : true; + + this.assert( + isFrozen + , 'expected #{this} to be frozen' + , 'expected #{this} to not be frozen' + ); + }); + + /** + * ### .finite + * + * Asserts that the target is a number, and isn't `NaN` or positive/negative + * `Infinity`. + * + * expect(1).to.be.finite; + * + * Add `.not` earlier in the chain to negate `.finite`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either isn't a number, or that it's `NaN`, or + * that it's positive `Infinity`, or that it's negative `Infinity`. It's often + * best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to be a number, it's often best to assert + * that it's the expected type, rather than asserting that it isn't one of + * many unexpected types. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.finite; // Not recommended + * + * When the target is expected to be `NaN`, it's often best to assert exactly + * that. + * + * expect(NaN).to.be.NaN; // Recommended + * expect(NaN).to.not.be.finite; // Not recommended + * + * When the target is expected to be positive infinity, it's often best to + * assert exactly that. + * + * expect(Infinity).to.equal(Infinity); // Recommended + * expect(Infinity).to.not.be.finite; // Not recommended + * + * When the target is expected to be negative infinity, it's often best to + * assert exactly that. + * + * expect(-Infinity).to.equal(-Infinity); // Recommended + * expect(-Infinity).to.not.be.finite; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect('foo', 'nooo why fail??').to.be.finite; + * + * @name finite + * @namespace BDD + * @api public + */ + + Assertion.addProperty('finite', function(msg) { + var obj = flag(this, 'object'); + + this.assert( + typeof obj === 'number' && isFinite(obj) + , 'expected #{this} to be a finite number' + , 'expected #{this} to not be a finite number' + ); + }); +}; + +},{}],6:[function(require,module,exports){ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + /*! + * Chai dependencies. + */ + + var Assertion = chai.Assertion + , flag = util.flag; + + /*! + * Module export. + */ + + /** + * ### assert(expression, message) + * + * Write your own test expressions. + * + * assert('foo' !== 'bar', 'foo is not bar'); + * assert(Array.isArray([]), 'empty arrays are arrays'); + * + * @param {Mixed} expression to test for truthiness + * @param {String} message to display on error + * @name assert + * @namespace Assert + * @api public + */ + + var assert = chai.assert = function (express, errmsg) { + var test = new Assertion(null, null, chai.assert, true); + test.assert( + express + , errmsg + , '[ negation message unavailable ]' + ); + }; + + /** + * ### .fail([message]) + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. Node.js `assert` module-compatible. + * + * assert.fail(); + * assert.fail("custom error message"); + * assert.fail(1, 2); + * assert.fail(1, 2, "custom error message"); + * assert.fail(1, 2, "custom error message", ">"); + * assert.fail(1, 2, undefined, ">"); + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @namespace Assert + * @api public + */ + + assert.fail = function (actual, expected, message, operator) { + if (arguments.length < 2) { + // Comply with Node's fail([message]) interface + + message = actual; + actual = undefined; + } + + message = message || 'assert.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, assert.fail); + }; + + /** + * ### .isOk(object, [message]) + * + * Asserts that `object` is truthy. + * + * assert.isOk('everything', 'everything is ok'); + * assert.isOk(false, 'this will fail'); + * + * @name isOk + * @alias ok + * @param {Mixed} object to test + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isOk = function (val, msg) { + new Assertion(val, msg, assert.isOk, true).is.ok; + }; + + /** + * ### .isNotOk(object, [message]) + * + * Asserts that `object` is falsy. + * + * assert.isNotOk('everything', 'this will fail'); + * assert.isNotOk(false, 'this will pass'); + * + * @name isNotOk + * @alias notOk + * @param {Mixed} object to test + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotOk = function (val, msg) { + new Assertion(val, msg, assert.isNotOk, true).is.not.ok; + }; + + /** + * ### .equal(actual, expected, [message]) + * + * Asserts non-strict equality (`==`) of `actual` and `expected`. + * + * assert.equal(3, '3', '== coerces values to strings'); + * + * @name equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.equal = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.equal, true); + + test.assert( + exp == flag(test, 'object') + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{act}' + , exp + , act + , true + ); + }; + + /** + * ### .notEqual(actual, expected, [message]) + * + * Asserts non-strict inequality (`!=`) of `actual` and `expected`. + * + * assert.notEqual(3, 4, 'these numbers are not equal'); + * + * @name notEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notEqual = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.notEqual, true); + + test.assert( + exp != flag(test, 'object') + , 'expected #{this} to not equal #{exp}' + , 'expected #{this} to equal #{act}' + , exp + , act + , true + ); + }; + + /** + * ### .strictEqual(actual, expected, [message]) + * + * Asserts strict equality (`===`) of `actual` and `expected`. + * + * assert.strictEqual(true, true, 'these booleans are strictly equal'); + * + * @name strictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.strictEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.strictEqual, true).to.equal(exp); + }; + + /** + * ### .notStrictEqual(actual, expected, [message]) + * + * Asserts strict inequality (`!==`) of `actual` and `expected`. + * + * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); + * + * @name notStrictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notStrictEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.notStrictEqual, true).to.not.equal(exp); + }; + + /** + * ### .deepEqual(actual, expected, [message]) + * + * Asserts that `actual` is deeply equal to `expected`. + * + * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); + * + * @name deepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @alias deepStrictEqual + * @namespace Assert + * @api public + */ + + assert.deepEqual = assert.deepStrictEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.deepEqual, true).to.eql(exp); + }; + + /** + * ### .notDeepEqual(actual, expected, [message]) + * + * Assert that `actual` is not deeply equal to `expected`. + * + * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); + * + * @name notDeepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.notDeepEqual, true).to.not.eql(exp); + }; + + /** + * ### .isAbove(valueToCheck, valueToBeAbove, [message]) + * + * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove`. + * + * assert.isAbove(5, 2, '5 is strictly greater than 2'); + * + * @name isAbove + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAbove + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isAbove = function (val, abv, msg) { + new Assertion(val, msg, assert.isAbove, true).to.be.above(abv); + }; + + /** + * ### .isAtLeast(valueToCheck, valueToBeAtLeast, [message]) + * + * Asserts `valueToCheck` is greater than or equal to (>=) `valueToBeAtLeast`. + * + * assert.isAtLeast(5, 2, '5 is greater or equal to 2'); + * assert.isAtLeast(3, 3, '3 is greater or equal to 3'); + * + * @name isAtLeast + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAtLeast + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isAtLeast = function (val, atlst, msg) { + new Assertion(val, msg, assert.isAtLeast, true).to.be.least(atlst); + }; + + /** + * ### .isBelow(valueToCheck, valueToBeBelow, [message]) + * + * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow`. + * + * assert.isBelow(3, 6, '3 is strictly less than 6'); + * + * @name isBelow + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeBelow + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isBelow = function (val, blw, msg) { + new Assertion(val, msg, assert.isBelow, true).to.be.below(blw); + }; + + /** + * ### .isAtMost(valueToCheck, valueToBeAtMost, [message]) + * + * Asserts `valueToCheck` is less than or equal to (<=) `valueToBeAtMost`. + * + * assert.isAtMost(3, 6, '3 is less than or equal to 6'); + * assert.isAtMost(4, 4, '4 is less than or equal to 4'); + * + * @name isAtMost + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAtMost + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isAtMost = function (val, atmst, msg) { + new Assertion(val, msg, assert.isAtMost, true).to.be.most(atmst); + }; + + /** + * ### .isTrue(value, [message]) + * + * Asserts that `value` is true. + * + * var teaServed = true; + * assert.isTrue(teaServed, 'the tea has been served'); + * + * @name isTrue + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isTrue = function (val, msg) { + new Assertion(val, msg, assert.isTrue, true).is['true']; + }; + + /** + * ### .isNotTrue(value, [message]) + * + * Asserts that `value` is not true. + * + * var tea = 'tasty chai'; + * assert.isNotTrue(tea, 'great, time for tea!'); + * + * @name isNotTrue + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotTrue = function (val, msg) { + new Assertion(val, msg, assert.isNotTrue, true).to.not.equal(true); + }; + + /** + * ### .isFalse(value, [message]) + * + * Asserts that `value` is false. + * + * var teaServed = false; + * assert.isFalse(teaServed, 'no tea yet? hmm...'); + * + * @name isFalse + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isFalse = function (val, msg) { + new Assertion(val, msg, assert.isFalse, true).is['false']; + }; + + /** + * ### .isNotFalse(value, [message]) + * + * Asserts that `value` is not false. + * + * var tea = 'tasty chai'; + * assert.isNotFalse(tea, 'great, time for tea!'); + * + * @name isNotFalse + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotFalse = function (val, msg) { + new Assertion(val, msg, assert.isNotFalse, true).to.not.equal(false); + }; + + /** + * ### .isNull(value, [message]) + * + * Asserts that `value` is null. + * + * assert.isNull(err, 'there was no error'); + * + * @name isNull + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNull = function (val, msg) { + new Assertion(val, msg, assert.isNull, true).to.equal(null); + }; + + /** + * ### .isNotNull(value, [message]) + * + * Asserts that `value` is not null. + * + * var tea = 'tasty chai'; + * assert.isNotNull(tea, 'great, time for tea!'); + * + * @name isNotNull + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotNull = function (val, msg) { + new Assertion(val, msg, assert.isNotNull, true).to.not.equal(null); + }; + + /** + * ### .isNaN + * + * Asserts that value is NaN. + * + * assert.isNaN(NaN, 'NaN is NaN'); + * + * @name isNaN + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNaN = function (val, msg) { + new Assertion(val, msg, assert.isNaN, true).to.be.NaN; + }; + + /** + * ### .isNotNaN + * + * Asserts that value is not NaN. + * + * assert.isNotNaN(4, '4 is not NaN'); + * + * @name isNotNaN + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + assert.isNotNaN = function (val, msg) { + new Assertion(val, msg, assert.isNotNaN, true).not.to.be.NaN; + }; + + /** + * ### .exists + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi'; + * + * assert.exists(foo, 'foo is neither `null` nor `undefined`'); + * + * @name exists + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.exists = function (val, msg) { + new Assertion(val, msg, assert.exists, true).to.exist; + }; + + /** + * ### .notExists + * + * Asserts that the target is either `null` or `undefined`. + * + * var bar = null + * , baz; + * + * assert.notExists(bar); + * assert.notExists(baz, 'baz is either null or undefined'); + * + * @name notExists + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notExists = function (val, msg) { + new Assertion(val, msg, assert.notExists, true).to.not.exist; + }; + + /** + * ### .isUndefined(value, [message]) + * + * Asserts that `value` is `undefined`. + * + * var tea; + * assert.isUndefined(tea, 'no tea defined'); + * + * @name isUndefined + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isUndefined = function (val, msg) { + new Assertion(val, msg, assert.isUndefined, true).to.equal(undefined); + }; + + /** + * ### .isDefined(value, [message]) + * + * Asserts that `value` is not `undefined`. + * + * var tea = 'cup of chai'; + * assert.isDefined(tea, 'tea has been defined'); + * + * @name isDefined + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isDefined = function (val, msg) { + new Assertion(val, msg, assert.isDefined, true).to.not.equal(undefined); + }; + + /** + * ### .isFunction(value, [message]) + * + * Asserts that `value` is a function. + * + * function serveTea() { return 'cup of tea'; }; + * assert.isFunction(serveTea, 'great, we can have tea now'); + * + * @name isFunction + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isFunction = function (val, msg) { + new Assertion(val, msg, assert.isFunction, true).to.be.a('function'); + }; + + /** + * ### .isNotFunction(value, [message]) + * + * Asserts that `value` is _not_ a function. + * + * var serveTea = [ 'heat', 'pour', 'sip' ]; + * assert.isNotFunction(serveTea, 'great, we have listed the steps'); + * + * @name isNotFunction + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotFunction = function (val, msg) { + new Assertion(val, msg, assert.isNotFunction, true).to.not.be.a('function'); + }; + + /** + * ### .isObject(value, [message]) + * + * Asserts that `value` is an object of type 'Object' (as revealed by `Object.prototype.toString`). + * _The assertion does not match subclassed objects._ + * + * var selection = { name: 'Chai', serve: 'with spices' }; + * assert.isObject(selection, 'tea selection is an object'); + * + * @name isObject + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isObject = function (val, msg) { + new Assertion(val, msg, assert.isObject, true).to.be.a('object'); + }; + + /** + * ### .isNotObject(value, [message]) + * + * Asserts that `value` is _not_ an object of type 'Object' (as revealed by `Object.prototype.toString`). + * + * var selection = 'chai' + * assert.isNotObject(selection, 'tea selection is not an object'); + * assert.isNotObject(null, 'null is not an object'); + * + * @name isNotObject + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotObject = function (val, msg) { + new Assertion(val, msg, assert.isNotObject, true).to.not.be.a('object'); + }; + + /** + * ### .isArray(value, [message]) + * + * Asserts that `value` is an array. + * + * var menu = [ 'green', 'chai', 'oolong' ]; + * assert.isArray(menu, 'what kind of tea do we want?'); + * + * @name isArray + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isArray = function (val, msg) { + new Assertion(val, msg, assert.isArray, true).to.be.an('array'); + }; + + /** + * ### .isNotArray(value, [message]) + * + * Asserts that `value` is _not_ an array. + * + * var menu = 'green|chai|oolong'; + * assert.isNotArray(menu, 'what kind of tea do we want?'); + * + * @name isNotArray + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotArray = function (val, msg) { + new Assertion(val, msg, assert.isNotArray, true).to.not.be.an('array'); + }; + + /** + * ### .isString(value, [message]) + * + * Asserts that `value` is a string. + * + * var teaOrder = 'chai'; + * assert.isString(teaOrder, 'order placed'); + * + * @name isString + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isString = function (val, msg) { + new Assertion(val, msg, assert.isString, true).to.be.a('string'); + }; + + /** + * ### .isNotString(value, [message]) + * + * Asserts that `value` is _not_ a string. + * + * var teaOrder = 4; + * assert.isNotString(teaOrder, 'order placed'); + * + * @name isNotString + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotString = function (val, msg) { + new Assertion(val, msg, assert.isNotString, true).to.not.be.a('string'); + }; + + /** + * ### .isNumber(value, [message]) + * + * Asserts that `value` is a number. + * + * var cups = 2; + * assert.isNumber(cups, 'how many cups'); + * + * @name isNumber + * @param {Number} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNumber = function (val, msg) { + new Assertion(val, msg, assert.isNumber, true).to.be.a('number'); + }; + + /** + * ### .isNotNumber(value, [message]) + * + * Asserts that `value` is _not_ a number. + * + * var cups = '2 cups please'; + * assert.isNotNumber(cups, 'how many cups'); + * + * @name isNotNumber + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotNumber = function (val, msg) { + new Assertion(val, msg, assert.isNotNumber, true).to.not.be.a('number'); + }; + + /** + * ### .isFinite(value, [message]) + * + * Asserts that `value` is a finite number. Unlike `.isNumber`, this will fail for `NaN` and `Infinity`. + * + * var cups = 2; + * assert.isFinite(cups, 'how many cups'); + * + * assert.isFinite(NaN); // throws + * + * @name isFinite + * @param {Number} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isFinite = function (val, msg) { + new Assertion(val, msg, assert.isFinite, true).to.be.finite; + }; + + /** + * ### .isBoolean(value, [message]) + * + * Asserts that `value` is a boolean. + * + * var teaReady = true + * , teaServed = false; + * + * assert.isBoolean(teaReady, 'is the tea ready'); + * assert.isBoolean(teaServed, 'has tea been served'); + * + * @name isBoolean + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isBoolean = function (val, msg) { + new Assertion(val, msg, assert.isBoolean, true).to.be.a('boolean'); + }; + + /** + * ### .isNotBoolean(value, [message]) + * + * Asserts that `value` is _not_ a boolean. + * + * var teaReady = 'yep' + * , teaServed = 'nope'; + * + * assert.isNotBoolean(teaReady, 'is the tea ready'); + * assert.isNotBoolean(teaServed, 'has tea been served'); + * + * @name isNotBoolean + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotBoolean = function (val, msg) { + new Assertion(val, msg, assert.isNotBoolean, true).to.not.be.a('boolean'); + }; + + /** + * ### .typeOf(value, name, [message]) + * + * Asserts that `value`'s type is `name`, as determined by + * `Object.prototype.toString`. + * + * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); + * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); + * assert.typeOf('tea', 'string', 'we have a string'); + * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); + * assert.typeOf(null, 'null', 'we have a null'); + * assert.typeOf(undefined, 'undefined', 'we have an undefined'); + * + * @name typeOf + * @param {Mixed} value + * @param {String} name + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.typeOf = function (val, type, msg) { + new Assertion(val, msg, assert.typeOf, true).to.be.a(type); + }; + + /** + * ### .notTypeOf(value, name, [message]) + * + * Asserts that `value`'s type is _not_ `name`, as determined by + * `Object.prototype.toString`. + * + * assert.notTypeOf('tea', 'number', 'strings are not numbers'); + * + * @name notTypeOf + * @param {Mixed} value + * @param {String} typeof name + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notTypeOf = function (val, type, msg) { + new Assertion(val, msg, assert.notTypeOf, true).to.not.be.a(type); + }; + + /** + * ### .instanceOf(object, constructor, [message]) + * + * Asserts that `value` is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new Tea('chai'); + * + * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); + * + * @name instanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.instanceOf = function (val, type, msg) { + new Assertion(val, msg, assert.instanceOf, true).to.be.instanceOf(type); + }; + + /** + * ### .notInstanceOf(object, constructor, [message]) + * + * Asserts `value` is not an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new String('chai'); + * + * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); + * + * @name notInstanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notInstanceOf = function (val, type, msg) { + new Assertion(val, msg, assert.notInstanceOf, true) + .to.not.be.instanceOf(type); + }; + + /** + * ### .include(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Can be used to assert the + * inclusion of a value in an array, a substring in a string, or a subset of + * properties in an object. + * + * assert.include([1,2,3], 2, 'array contains value'); + * assert.include('foobar', 'foo', 'string contains substring'); + * assert.include({ foo: 'bar', hello: 'universe' }, { foo: 'bar' }, 'object contains property'); + * + * Strict equality (===) is used. When asserting the inclusion of a value in + * an array, the array is searched for an element that's strictly equal to the + * given value. When asserting a subset of properties in an object, the object + * is searched for the given property keys, checking that each one is present + * and strictly equal to the given property value. For instance: + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.include([obj1, obj2], obj1); + * assert.include({foo: obj1, bar: obj2}, {foo: obj1}); + * assert.include({foo: obj1, bar: obj2}, {foo: obj1, bar: obj2}); + * + * @name include + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.include = function (exp, inc, msg) { + new Assertion(exp, msg, assert.include, true).include(inc); + }; + + /** + * ### .notInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Can be used to assert + * the absence of a value in an array, a substring in a string, or a subset of + * properties in an object. + * + * assert.notInclude([1,2,3], 4, "array doesn't contain value"); + * assert.notInclude('foobar', 'baz', "string doesn't contain substring"); + * assert.notInclude({ foo: 'bar', hello: 'universe' }, { foo: 'baz' }, 'object doesn't contain property'); + * + * Strict equality (===) is used. When asserting the absence of a value in an + * array, the array is searched to confirm the absence of an element that's + * strictly equal to the given value. When asserting a subset of properties in + * an object, the object is searched to confirm that at least one of the given + * property keys is either not present or not strictly equal to the given + * property value. For instance: + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.notInclude([obj1, obj2], {a: 1}); + * assert.notInclude({foo: obj1, bar: obj2}, {foo: {a: 1}}); + * assert.notInclude({foo: obj1, bar: obj2}, {foo: obj1, bar: {b: 2}}); + * + * @name notInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notInclude, true).not.include(inc); + }; + + /** + * ### .deepInclude(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Can be used to assert the + * inclusion of a value in an array or a subset of properties in an object. + * Deep equality is used. + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.deepInclude([obj1, obj2], {a: 1}); + * assert.deepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}}); + * assert.deepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 2}}); + * + * @name deepInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.deepInclude, true).deep.include(inc); + }; + + /** + * ### .notDeepInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Can be used to assert + * the absence of a value in an array or a subset of properties in an object. + * Deep equality is used. + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.notDeepInclude([obj1, obj2], {a: 9}); + * assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 9}}); + * assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 9}}); + * + * @name notDeepInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepInclude, true).not.deep.include(inc); + }; + + /** + * ### .nestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.nestedInclude({'.a': {'b': 'x'}}, {'\\.a.[b]': 'x'}); + * assert.nestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'x'}); + * + * @name nestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.nestedInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.nestedInclude, true).nested.include(inc); + }; + + /** + * ### .notNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' does not include 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.notNestedInclude({'.a': {'b': 'x'}}, {'\\.a.b': 'y'}); + * assert.notNestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'y'}); + * + * @name notNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notNestedInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notNestedInclude, true) + .not.nested.include(inc); + }; + + /** + * ### .deepNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while checking for deep equality. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {x: 1}}); + * assert.deepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {x: 1}}); + * + * @name deepNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepNestedInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.deepNestedInclude, true) + .deep.nested.include(inc); + }; + + /** + * ### .notDeepNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' does not include 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while checking for deep equality. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.notDeepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {y: 1}}) + * assert.notDeepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {y: 2}}); + * + * @name notDeepNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepNestedInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepNestedInclude, true) + .not.deep.nested.include(inc); + }; + + /** + * ### .ownInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while ignoring inherited properties. + * + * assert.ownInclude({ a: 1 }, { a: 1 }); + * + * @name ownInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.ownInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.ownInclude, true).own.include(inc); + }; + + /** + * ### .notOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while ignoring inherited properties. + * + * Object.prototype.b = 2; + * + * assert.notOwnInclude({ a: 1 }, { b: 2 }); + * + * @name notOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notOwnInclude, true).not.own.include(inc); + }; + + /** + * ### .deepOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while ignoring inherited properties and checking for deep equality. + * + * assert.deepOwnInclude({a: {b: 2}}, {a: {b: 2}}); + * + * @name deepOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.deepOwnInclude, true) + .deep.own.include(inc); + }; + + /** + * ### .notDeepOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while ignoring inherited properties and checking for deep equality. + * + * assert.notDeepOwnInclude({a: {b: 2}}, {a: {c: 3}}); + * + * @name notDeepOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepOwnInclude, true) + .not.deep.own.include(inc); + }; + + /** + * ### .match(value, regexp, [message]) + * + * Asserts that `value` matches the regular expression `regexp`. + * + * assert.match('foobar', /^foo/, 'regexp matches'); + * + * @name match + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.match = function (exp, re, msg) { + new Assertion(exp, msg, assert.match, true).to.match(re); + }; + + /** + * ### .notMatch(value, regexp, [message]) + * + * Asserts that `value` does not match the regular expression `regexp`. + * + * assert.notMatch('foobar', /^foo/, 'regexp does not match'); + * + * @name notMatch + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notMatch = function (exp, re, msg) { + new Assertion(exp, msg, assert.notMatch, true).to.not.match(re); + }; + + /** + * ### .property(object, property, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property`. + * + * assert.property({ tea: { green: 'matcha' }}, 'tea'); + * assert.property({ tea: { green: 'matcha' }}, 'toString'); + * + * @name property + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.property = function (obj, prop, msg) { + new Assertion(obj, msg, assert.property, true).to.have.property(prop); + }; + + /** + * ### .notProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property`. + * + * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); + * + * @name notProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.notProperty, true) + .to.not.have.property(prop); + }; + + /** + * ### .propertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property` with a value given by `value`. Uses a strict equality check + * (===). + * + * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); + * + * @name propertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.propertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.propertyVal, true) + .to.have.property(prop, val); + }; + + /** + * ### .notPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property` with value given by `value`. Uses a strict equality check + * (===). + * + * assert.notPropertyVal({ tea: 'is good' }, 'tea', 'is bad'); + * assert.notPropertyVal({ tea: 'is good' }, 'coffee', 'is good'); + * + * @name notPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notPropertyVal, true) + .to.not.have.property(prop, val); + }; + + /** + * ### .deepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property` with a value given by `value`. Uses a deep equality check. + * + * assert.deepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); + * + * @name deepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.deepPropertyVal, true) + .to.have.deep.property(prop, val); + }; + + /** + * ### .notDeepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property` with value given by `value`. Uses a deep equality check. + * + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha' }); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong' }); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha' }); + * + * @name notDeepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notDeepPropertyVal, true) + .to.not.have.deep.property(prop, val); + }; + + /** + * ### .ownProperty(object, property, [message]) + * + * Asserts that `object` has a direct property named by `property`. Inherited + * properties aren't checked. + * + * assert.ownProperty({ tea: { green: 'matcha' }}, 'tea'); + * + * @name ownProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.ownProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.ownProperty, true) + .to.have.own.property(prop); + }; + + /** + * ### .notOwnProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a direct property named by + * `property`. Inherited properties aren't checked. + * + * assert.notOwnProperty({ tea: { green: 'matcha' }}, 'coffee'); + * assert.notOwnProperty({}, 'toString'); + * + * @name notOwnProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notOwnProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.notOwnProperty, true) + .to.not.have.own.property(prop); + }; + + /** + * ### .ownPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct property named by `property` and a value + * equal to the provided `value`. Uses a strict equality check (===). + * Inherited properties aren't checked. + * + * assert.ownPropertyVal({ coffee: 'is good'}, 'coffee', 'is good'); + * + * @name ownPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.ownPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.ownPropertyVal, true) + .to.have.own.property(prop, value); + }; + + /** + * ### .notOwnPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct property named by `property` + * with a value equal to the provided `value`. Uses a strict equality check + * (===). Inherited properties aren't checked. + * + * assert.notOwnPropertyVal({ tea: 'is better'}, 'tea', 'is worse'); + * assert.notOwnPropertyVal({}, 'toString', Object.prototype.toString); + * + * @name notOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.notOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.notOwnPropertyVal, true) + .to.not.have.own.property(prop, value); + }; + + /** + * ### .deepOwnPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct property named by `property` and a value + * equal to the provided `value`. Uses a deep equality check. Inherited + * properties aren't checked. + * + * assert.deepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); + * + * @name deepOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.deepOwnPropertyVal, true) + .to.have.deep.own.property(prop, value); + }; + + /** + * ### .notDeepOwnPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct property named by `property` + * with a value equal to the provided `value`. Uses a deep equality check. + * Inherited properties aren't checked. + * + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha' }); + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong' }); + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha' }); + * assert.notDeepOwnPropertyVal({}, 'toString', Object.prototype.toString); + * + * @name notDeepOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.notDeepOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.notDeepOwnPropertyVal, true) + .to.not.have.deep.own.property(prop, value); + }; + + /** + * ### .nestedProperty(object, property, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property`, which can be a string using dot- and bracket-notation for + * nested reference. + * + * assert.nestedProperty({ tea: { green: 'matcha' }}, 'tea.green'); + * + * @name nestedProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.nestedProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.nestedProperty, true) + .to.have.nested.property(prop); + }; + + /** + * ### .notNestedProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`, which + * can be a string using dot- and bracket-notation for nested reference. The + * property cannot exist on the object nor anywhere in its prototype chain. + * + * assert.notNestedProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); + * + * @name notNestedProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notNestedProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.notNestedProperty, true) + .to.not.have.nested.property(prop); + }; + + /** + * ### .nestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. `property` can use dot- and bracket-notation for nested + * reference. Uses a strict equality check (===). + * + * assert.nestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); + * + * @name nestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.nestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.nestedPropertyVal, true) + .to.have.nested.property(prop, val); + }; + + /** + * ### .notNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. `property` can use dot- and bracket-notation for + * nested reference. Uses a strict equality check (===). + * + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'coffee.green', 'matcha'); + * + * @name notNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notNestedPropertyVal, true) + .to.not.have.nested.property(prop, val); + }; + + /** + * ### .deepNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with a value given + * by `value`. `property` can use dot- and bracket-notation for nested + * reference. Uses a deep equality check. + * + * assert.deepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yum' }); + * + * @name deepNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.deepNestedPropertyVal, true) + .to.have.deep.nested.property(prop, val); + }; + + /** + * ### .notDeepNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. `property` can use dot- and bracket-notation for + * nested reference. Uses a deep equality check. + * + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { oolong: 'yum' }); + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yuck' }); + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.black', { matcha: 'yum' }); + * + * @name notDeepNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notDeepNestedPropertyVal, true) + .to.not.have.deep.nested.property(prop, val); + } + + /** + * ### .lengthOf(object, length, [message]) + * + * Asserts that `object` has a `length` or `size` with the expected value. + * + * assert.lengthOf([1,2,3], 3, 'array has length of 3'); + * assert.lengthOf('foobar', 6, 'string has length of 6'); + * assert.lengthOf(new Set([1,2,3]), 3, 'set has size of 3'); + * assert.lengthOf(new Map([['a',1],['b',2],['c',3]]), 3, 'map has size of 3'); + * + * @name lengthOf + * @param {Mixed} object + * @param {Number} length + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.lengthOf = function (exp, len, msg) { + new Assertion(exp, msg, assert.lengthOf, true).to.have.lengthOf(len); + }; + + /** + * ### .hasAnyKeys(object, [keys], [message]) + * + * Asserts that `object` has at least one of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAnyKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'iDontExist', 'baz']); + * assert.hasAnyKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, iDontExist: 99, baz: 1337}); + * assert.hasAnyKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.hasAnyKeys(new Set([{foo: 'bar'}, 'anotherKey']), [{foo: 'bar'}, 'anotherKey']); + * + * @name hasAnyKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAnyKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAnyKeys, true).to.have.any.keys(keys); + } + + /** + * ### .hasAllKeys(object, [keys], [message]) + * + * Asserts that `object` has all and only all of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'bar', 'baz']); + * assert.hasAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, bar: 99, baz: 1337]); + * assert.hasAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.hasAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}, 'anotherKey']); + * + * @name hasAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAllKeys, true).to.have.all.keys(keys); + } + + /** + * ### .containsAllKeys(object, [keys], [message]) + * + * Asserts that `object` has all of the `keys` provided but may have more keys not listed. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'baz']); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'bar', 'baz']); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, baz: 1337}); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, bar: 99, baz: 1337}); + * assert.containsAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}]); + * assert.containsAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.containsAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}]); + * assert.containsAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}, 'anotherKey']); + * + * @name containsAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.containsAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.containsAllKeys, true) + .to.contain.all.keys(keys); + } + + /** + * ### .doesNotHaveAnyKeys(object, [keys], [message]) + * + * Asserts that `object` has none of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAnyKeys({foo: 1, bar: 2, baz: 3}, ['one', 'two', 'example']); + * assert.doesNotHaveAnyKeys({foo: 1, bar: 2, baz: 3}, {one: 1, two: 2, example: 'foo'}); + * assert.doesNotHaveAnyKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{one: 'two'}, 'example']); + * assert.doesNotHaveAnyKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{one: 'two'}, 'example']); + * + * @name doesNotHaveAnyKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAnyKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAnyKeys, true) + .to.not.have.any.keys(keys); + } + + /** + * ### .doesNotHaveAllKeys(object, [keys], [message]) + * + * Asserts that `object` does not have at least one of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAllKeys({foo: 1, bar: 2, baz: 3}, ['one', 'two', 'example']); + * assert.doesNotHaveAllKeys({foo: 1, bar: 2, baz: 3}, {one: 1, two: 2, example: 'foo'}); + * assert.doesNotHaveAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{one: 'two'}, 'example']); + * assert.doesNotHaveAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{one: 'two'}, 'example']); + * + * @name doesNotHaveAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAllKeys, true) + .to.not.have.all.keys(keys); + } + + /** + * ### .hasAnyDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has at least one of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'}); + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'}); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {three: 'three'}]); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name doesNotHaveAllKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAnyDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAnyDeepKeys, true) + .to.have.any.deep.keys(keys); + } + + /** + * ### .hasAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has all and only all of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne']]), {one: 'one'}); + * assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAllDeepKeys(new Set([{one: 'one'}]), {one: 'one'}); + * assert.hasAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name hasAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAllDeepKeys, true) + .to.have.all.deep.keys(keys); + } + + /** + * ### .containsAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` contains all of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'}); + * assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'}); + * assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name containsAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.containsAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.containsAllDeepKeys, true) + .to.contain.all.deep.keys(keys); + } + + /** + * ### .doesNotHaveAnyDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has none of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'}); + * assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {fifty: 'fifty'}]); + * assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'}); + * assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{twenty: 'twenty'}, {fifty: 'fifty'}]); + * + * @name doesNotHaveAnyDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAnyDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAnyDeepKeys, true) + .to.not.have.any.deep.keys(keys); + } + + /** + * ### .doesNotHaveAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` does not have at least one of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'}); + * assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {one: 'one'}]); + * assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'}); + * assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {fifty: 'fifty'}]); + * + * @name doesNotHaveAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAllDeepKeys, true) + .to.not.have.all.deep.keys(keys); + } + + /** + * ### .throws(fn, [errorLike/string/regexp], [string/regexp], [message]) + * + * If `errorLike` is an `Error` constructor, asserts that `fn` will throw an error that is an + * instance of `errorLike`. + * If `errorLike` is an `Error` instance, asserts that the error thrown is the same + * instance as `errorLike`. + * If `errMsgMatcher` is provided, it also asserts that the error thrown will have a + * message matching `errMsgMatcher`. + * + * assert.throws(fn, 'Error thrown must have this msg'); + * assert.throws(fn, /Error thrown must have a msg that matches this/); + * assert.throws(fn, ReferenceError); + * assert.throws(fn, errorInstance); + * assert.throws(fn, ReferenceError, 'Error thrown must be a ReferenceError and have this msg'); + * assert.throws(fn, errorInstance, 'Error thrown must be the same errorInstance and have this msg'); + * assert.throws(fn, ReferenceError, /Error thrown must be a ReferenceError and match this/); + * assert.throws(fn, errorInstance, /Error thrown must be the same errorInstance and match this/); + * + * @name throws + * @alias throw + * @alias Throw + * @param {Function} fn + * @param {ErrorConstructor|Error} errorLike + * @param {RegExp|String} errMsgMatcher + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Assert + * @api public + */ + + assert.throws = function (fn, errorLike, errMsgMatcher, msg) { + if ('string' === typeof errorLike || errorLike instanceof RegExp) { + errMsgMatcher = errorLike; + errorLike = null; + } + + var assertErr = new Assertion(fn, msg, assert.throws, true) + .to.throw(errorLike, errMsgMatcher); + return flag(assertErr, 'object'); + }; + + /** + * ### .doesNotThrow(fn, [errorLike/string/regexp], [string/regexp], [message]) + * + * If `errorLike` is an `Error` constructor, asserts that `fn` will _not_ throw an error that is an + * instance of `errorLike`. + * If `errorLike` is an `Error` instance, asserts that the error thrown is _not_ the same + * instance as `errorLike`. + * If `errMsgMatcher` is provided, it also asserts that the error thrown will _not_ have a + * message matching `errMsgMatcher`. + * + * assert.doesNotThrow(fn, 'Any Error thrown must not have this message'); + * assert.doesNotThrow(fn, /Any Error thrown must not match this/); + * assert.doesNotThrow(fn, Error); + * assert.doesNotThrow(fn, errorInstance); + * assert.doesNotThrow(fn, Error, 'Error must not have this message'); + * assert.doesNotThrow(fn, errorInstance, 'Error must not have this message'); + * assert.doesNotThrow(fn, Error, /Error must not match this/); + * assert.doesNotThrow(fn, errorInstance, /Error must not match this/); + * + * @name doesNotThrow + * @param {Function} fn + * @param {ErrorConstructor} errorLike + * @param {RegExp|String} errMsgMatcher + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Assert + * @api public + */ + + assert.doesNotThrow = function (fn, errorLike, errMsgMatcher, msg) { + if ('string' === typeof errorLike || errorLike instanceof RegExp) { + errMsgMatcher = errorLike; + errorLike = null; + } + + new Assertion(fn, msg, assert.doesNotThrow, true) + .to.not.throw(errorLike, errMsgMatcher); + }; + + /** + * ### .operator(val1, operator, val2, [message]) + * + * Compares two values using `operator`. + * + * assert.operator(1, '<', 2, 'everything is ok'); + * assert.operator(1, '>', 2, 'this will fail'); + * + * @name operator + * @param {Mixed} val1 + * @param {String} operator + * @param {Mixed} val2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.operator = function (val, operator, val2, msg) { + var ok; + switch(operator) { + case '==': + ok = val == val2; + break; + case '===': + ok = val === val2; + break; + case '>': + ok = val > val2; + break; + case '>=': + ok = val >= val2; + break; + case '<': + ok = val < val2; + break; + case '<=': + ok = val <= val2; + break; + case '!=': + ok = val != val2; + break; + case '!==': + ok = val !== val2; + break; + default: + msg = msg ? msg + ': ' : msg; + throw new chai.AssertionError( + msg + 'Invalid operator "' + operator + '"', + undefined, + assert.operator + ); + } + var test = new Assertion(ok, msg, assert.operator, true); + test.assert( + true === flag(test, 'object') + , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) + , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); + }; + + /** + * ### .closeTo(actual, expected, delta, [message]) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); + * + * @name closeTo + * @param {Number} actual + * @param {Number} expected + * @param {Number} delta + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.closeTo = function (act, exp, delta, msg) { + new Assertion(act, msg, assert.closeTo, true).to.be.closeTo(exp, delta); + }; + + /** + * ### .approximately(actual, expected, delta, [message]) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * assert.approximately(1.5, 1, 0.5, 'numbers are close'); + * + * @name approximately + * @param {Number} actual + * @param {Number} expected + * @param {Number} delta + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.approximately = function (act, exp, delta, msg) { + new Assertion(act, msg, assert.approximately, true) + .to.be.approximately(exp, delta); + }; + + /** + * ### .sameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in any order. Uses a + * strict equality check (===). + * + * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); + * + * @name sameMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameMembers, true) + .to.have.same.members(set2); + } + + /** + * ### .notSameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in any order. + * Uses a strict equality check (===). + * + * assert.notSameMembers([ 1, 2, 3 ], [ 5, 1, 3 ], 'not same members'); + * + * @name notSameMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameMembers, true) + .to.not.have.same.members(set2); + } + + /** + * ### .sameDeepMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in any order. Uses a + * deep equality check. + * + * assert.sameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members'); + * + * @name sameDeepMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameDeepMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameDeepMembers, true) + .to.have.same.deep.members(set2); + } + + /** + * ### .notSameDeepMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in any order. + * Uses a deep equality check. + * + * assert.notSameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { f: 5 }], 'not same deep members'); + * + * @name notSameDeepMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameDeepMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameDeepMembers, true) + .to.not.have.same.deep.members(set2); + } + + /** + * ### .sameOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in the same order. + * Uses a strict equality check (===). + * + * assert.sameOrderedMembers([ 1, 2, 3 ], [ 1, 2, 3 ], 'same ordered members'); + * + * @name sameOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameOrderedMembers, true) + .to.have.same.ordered.members(set2); + } + + /** + * ### .notSameOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in the same + * order. Uses a strict equality check (===). + * + * assert.notSameOrderedMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'not same ordered members'); + * + * @name notSameOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameOrderedMembers, true) + .to.not.have.same.ordered.members(set2); + } + + /** + * ### .sameDeepOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in the same order. + * Uses a deep equality check. + * + * assert.sameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { c: 3 } ], 'same deep ordered members'); + * + * @name sameDeepOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameDeepOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameDeepOrderedMembers, true) + .to.have.same.deep.ordered.members(set2); + } + + /** + * ### .notSameDeepOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in the same + * order. Uses a deep equality check. + * + * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { z: 5 } ], 'not same deep ordered members'); + * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { c: 3 } ], 'not same deep ordered members'); + * + * @name notSameDeepOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameDeepOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameDeepOrderedMembers, true) + .to.not.have.same.deep.ordered.members(set2); + } + + /** + * ### .includeMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in any order. Uses a + * strict equality check (===). Duplicates are ignored. + * + * assert.includeMembers([ 1, 2, 3 ], [ 2, 1, 2 ], 'include members'); + * + * @name includeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeMembers, true) + .to.include.members(subset); + } + + /** + * ### .notIncludeMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in any order. Uses a + * strict equality check (===). Duplicates are ignored. + * + * assert.notIncludeMembers([ 1, 2, 3 ], [ 5, 1 ], 'not include members'); + * + * @name notIncludeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeMembers, true) + .to.not.include.members(subset); + } + + /** + * ### .includeDeepMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in any order. Uses a deep + * equality check. Duplicates are ignored. + * + * assert.includeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { b: 2 } ], 'include deep members'); + * + * @name includeDeepMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeDeepMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeDeepMembers, true) + .to.include.deep.members(subset); + } + + /** + * ### .notIncludeDeepMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in any order. Uses a + * deep equality check. Duplicates are ignored. + * + * assert.notIncludeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { f: 5 } ], 'not include deep members'); + * + * @name notIncludeDeepMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeDeepMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeDeepMembers, true) + .to.not.include.deep.members(subset); + } + + /** + * ### .includeOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in the same order + * beginning with the first element in `superset`. Uses a strict equality + * check (===). + * + * assert.includeOrderedMembers([ 1, 2, 3 ], [ 1, 2 ], 'include ordered members'); + * + * @name includeOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeOrderedMembers, true) + .to.include.ordered.members(subset); + } + + /** + * ### .notIncludeOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in the same order + * beginning with the first element in `superset`. Uses a strict equality + * check (===). + * + * assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 1 ], 'not include ordered members'); + * assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 3 ], 'not include ordered members'); + * + * @name notIncludeOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeOrderedMembers, true) + .to.not.include.ordered.members(subset); + } + + /** + * ### .includeDeepOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in the same order + * beginning with the first element in `superset`. Uses a deep equality + * check. + * + * assert.includeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 } ], 'include deep ordered members'); + * + * @name includeDeepOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeDeepOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeDeepOrderedMembers, true) + .to.include.deep.ordered.members(subset); + } + + /** + * ### .notIncludeDeepOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in the same order + * beginning with the first element in `superset`. Uses a deep equality + * check. + * + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { f: 5 } ], 'not include deep ordered members'); + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 } ], 'not include deep ordered members'); + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { c: 3 } ], 'not include deep ordered members'); + * + * @name notIncludeDeepOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeDeepOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeDeepOrderedMembers, true) + .to.not.include.deep.ordered.members(subset); + } + + /** + * ### .oneOf(inList, list, [message]) + * + * Asserts that non-object, non-array value `inList` appears in the flat array `list`. + * + * assert.oneOf(1, [ 2, 1 ], 'Not found in list'); + * + * @name oneOf + * @param {*} inList + * @param {Array<*>} list + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.oneOf = function (inList, list, msg) { + new Assertion(inList, msg, assert.oneOf, true).to.be.oneOf(list); + } + + /** + * ### .changes(function, object, property, [message]) + * + * Asserts that a function changes the value of a property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 22 }; + * assert.changes(fn, obj, 'val'); + * + * @name changes + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changes = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changes, true).to.change(obj, prop); + } + + /** + * ### .changesBy(function, object, property, delta, [message]) + * + * Asserts that a function changes the value of a property by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 2 }; + * assert.changesBy(fn, obj, 'val', 2); + * + * @name changesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changesBy, true) + .to.change(obj, prop).by(delta); + } + + /** + * ### .doesNotChange(function, object, property, [message]) + * + * Asserts that a function does not change the value of a property. + * + * var obj = { val: 10 }; + * var fn = function() { console.log('foo'); }; + * assert.doesNotChange(fn, obj, 'val'); + * + * @name doesNotChange + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotChange = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotChange, true) + .to.not.change(obj, prop); + } + + /** + * ### .changesButNotBy(function, object, property, delta, [message]) + * + * Asserts that a function does not change the value of a property or of a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 10 }; + * assert.changesButNotBy(fn, obj, 'val', 5); + * + * @name changesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changesButNotBy, true) + .to.change(obj, prop).but.not.by(delta); + } + + /** + * ### .increases(function, object, property, [message]) + * + * Asserts that a function increases a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 13 }; + * assert.increases(fn, obj, 'val'); + * + * @name increases + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increases = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.increases, true) + .to.increase(obj, prop); + } + + /** + * ### .increasesBy(function, object, property, delta, [message]) + * + * Asserts that a function increases a numeric object property or a function's return value by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 10 }; + * assert.increasesBy(fn, obj, 'val', 10); + * + * @name increasesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increasesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.increasesBy, true) + .to.increase(obj, prop).by(delta); + } + + /** + * ### .doesNotIncrease(function, object, property, [message]) + * + * Asserts that a function does not increase a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 8 }; + * assert.doesNotIncrease(fn, obj, 'val'); + * + * @name doesNotIncrease + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotIncrease = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotIncrease, true) + .to.not.increase(obj, prop); + } + + /** + * ### .increasesButNotBy(function, object, property, [message]) + * + * Asserts that a function does not increase a numeric object property or function's return value by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 15 }; + * assert.increasesButNotBy(fn, obj, 'val', 10); + * + * @name increasesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increasesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.increasesButNotBy, true) + .to.increase(obj, prop).but.not.by(delta); + } + + /** + * ### .decreases(function, object, property, [message]) + * + * Asserts that a function decreases a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.decreases(fn, obj, 'val'); + * + * @name decreases + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreases = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.decreases, true) + .to.decrease(obj, prop); + } + + /** + * ### .decreasesBy(function, object, property, delta, [message]) + * + * Asserts that a function decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val -= 5 }; + * assert.decreasesBy(fn, obj, 'val', 5); + * + * @name decreasesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreasesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.decreasesBy, true) + .to.decrease(obj, prop).by(delta); + } + + /** + * ### .doesNotDecrease(function, object, property, [message]) + * + * Asserts that a function does not decreases a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 15 }; + * assert.doesNotDecrease(fn, obj, 'val'); + * + * @name doesNotDecrease + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotDecrease = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotDecrease, true) + .to.not.decrease(obj, prop); + } + + /** + * ### .doesNotDecreaseBy(function, object, property, delta, [message]) + * + * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.doesNotDecreaseBy(fn, obj, 'val', 1); + * + * @name doesNotDecrease + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotDecreaseBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotDecreaseBy, true) + .to.not.decrease(obj, prop).by(delta); + } + + /** + * ### .decreasesButNotBy(function, object, property, delta, [message]) + * + * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.decreasesButNotBy(fn, obj, 'val', 1); + * + * @name decreasesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreasesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.decreasesButNotBy, true) + .to.decrease(obj, prop).but.not.by(delta); + } + + /*! + * ### .ifError(object) + * + * Asserts if value is not a false value, and throws if it is a true value. + * This is added to allow for chai to be a drop-in replacement for Node's + * assert class. + * + * var err = new Error('I am a custom error'); + * assert.ifError(err); // Rethrows err! + * + * @name ifError + * @param {Object} object + * @namespace Assert + * @api public + */ + + assert.ifError = function (val) { + if (val) { + throw(val); + } + }; + + /** + * ### .isExtensible(object) + * + * Asserts that `object` is extensible (can have new properties added to it). + * + * assert.isExtensible({}); + * + * @name isExtensible + * @alias extensible + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isExtensible = function (obj, msg) { + new Assertion(obj, msg, assert.isExtensible, true).to.be.extensible; + }; + + /** + * ### .isNotExtensible(object) + * + * Asserts that `object` is _not_ extensible. + * + * var nonExtensibleObject = Object.preventExtensions({}); + * var sealedObject = Object.seal({}); + * var frozenObject = Object.freeze({}); + * + * assert.isNotExtensible(nonExtensibleObject); + * assert.isNotExtensible(sealedObject); + * assert.isNotExtensible(frozenObject); + * + * @name isNotExtensible + * @alias notExtensible + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotExtensible = function (obj, msg) { + new Assertion(obj, msg, assert.isNotExtensible, true).to.not.be.extensible; + }; + + /** + * ### .isSealed(object) + * + * Asserts that `object` is sealed (cannot have new properties added to it + * and its existing properties cannot be removed). + * + * var sealedObject = Object.seal({}); + * var frozenObject = Object.seal({}); + * + * assert.isSealed(sealedObject); + * assert.isSealed(frozenObject); + * + * @name isSealed + * @alias sealed + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isSealed = function (obj, msg) { + new Assertion(obj, msg, assert.isSealed, true).to.be.sealed; + }; + + /** + * ### .isNotSealed(object) + * + * Asserts that `object` is _not_ sealed. + * + * assert.isNotSealed({}); + * + * @name isNotSealed + * @alias notSealed + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotSealed = function (obj, msg) { + new Assertion(obj, msg, assert.isNotSealed, true).to.not.be.sealed; + }; + + /** + * ### .isFrozen(object) + * + * Asserts that `object` is frozen (cannot have new properties added to it + * and its existing properties cannot be modified). + * + * var frozenObject = Object.freeze({}); + * assert.frozen(frozenObject); + * + * @name isFrozen + * @alias frozen + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isFrozen = function (obj, msg) { + new Assertion(obj, msg, assert.isFrozen, true).to.be.frozen; + }; + + /** + * ### .isNotFrozen(object) + * + * Asserts that `object` is _not_ frozen. + * + * assert.isNotFrozen({}); + * + * @name isNotFrozen + * @alias notFrozen + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotFrozen = function (obj, msg) { + new Assertion(obj, msg, assert.isNotFrozen, true).to.not.be.frozen; + }; + + /** + * ### .isEmpty(target) + * + * Asserts that the target does not contain any values. + * For arrays and strings, it checks the `length` property. + * For `Map` and `Set` instances, it checks the `size` property. + * For non-function objects, it gets the count of own + * enumerable string keys. + * + * assert.isEmpty([]); + * assert.isEmpty(''); + * assert.isEmpty(new Map); + * assert.isEmpty({}); + * + * @name isEmpty + * @alias empty + * @param {Object|Array|String|Map|Set} target + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isEmpty = function(val, msg) { + new Assertion(val, msg, assert.isEmpty, true).to.be.empty; + }; + + /** + * ### .isNotEmpty(target) + * + * Asserts that the target contains values. + * For arrays and strings, it checks the `length` property. + * For `Map` and `Set` instances, it checks the `size` property. + * For non-function objects, it gets the count of own + * enumerable string keys. + * + * assert.isNotEmpty([1, 2]); + * assert.isNotEmpty('34'); + * assert.isNotEmpty(new Set([5, 6])); + * assert.isNotEmpty({ key: 7 }); + * + * @name isNotEmpty + * @alias notEmpty + * @param {Object|Array|String|Map|Set} target + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotEmpty = function(val, msg) { + new Assertion(val, msg, assert.isNotEmpty, true).to.not.be.empty; + }; + + /*! + * Aliases. + */ + + (function alias(name, as){ + assert[as] = assert[name]; + return alias; + }) + ('isOk', 'ok') + ('isNotOk', 'notOk') + ('throws', 'throw') + ('throws', 'Throw') + ('isExtensible', 'extensible') + ('isNotExtensible', 'notExtensible') + ('isSealed', 'sealed') + ('isNotSealed', 'notSealed') + ('isFrozen', 'frozen') + ('isNotFrozen', 'notFrozen') + ('isEmpty', 'empty') + ('isNotEmpty', 'notEmpty'); +}; + +},{}],7:[function(require,module,exports){ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + chai.expect = function (val, message) { + return new chai.Assertion(val, message); + }; + + /** + * ### .fail([message]) + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. + * + * expect.fail(); + * expect.fail("custom error message"); + * expect.fail(1, 2); + * expect.fail(1, 2, "custom error message"); + * expect.fail(1, 2, "custom error message", ">"); + * expect.fail(1, 2, undefined, ">"); + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @namespace BDD + * @api public + */ + + chai.expect.fail = function (actual, expected, message, operator) { + if (arguments.length < 2) { + message = actual; + actual = undefined; + } + + message = message || 'expect.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, chai.expect.fail); + }; +}; + +},{}],8:[function(require,module,exports){ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + var Assertion = chai.Assertion; + + function loadShould () { + // explicitly define this method as function as to have it's name to include as `ssfi` + function shouldGetter() { + if (this instanceof String + || this instanceof Number + || this instanceof Boolean + || typeof Symbol === 'function' && this instanceof Symbol) { + return new Assertion(this.valueOf(), null, shouldGetter); + } + return new Assertion(this, null, shouldGetter); + } + function shouldSetter(value) { + // See https://github.com/chaijs/chai/issues/86: this makes + // `whatever.should = someValue` actually set `someValue`, which is + // especially useful for `global.should = require('chai').should()`. + // + // Note that we have to use [[DefineProperty]] instead of [[Put]] + // since otherwise we would trigger this very setter! + Object.defineProperty(this, 'should', { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } + // modify Object.prototype to have `should` + Object.defineProperty(Object.prototype, 'should', { + set: shouldSetter + , get: shouldGetter + , configurable: true + }); + + var should = {}; + + /** + * ### .fail([message]) + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. + * + * should.fail(); + * should.fail("custom error message"); + * should.fail(1, 2); + * should.fail(1, 2, "custom error message"); + * should.fail(1, 2, "custom error message", ">"); + * should.fail(1, 2, undefined, ">"); + * + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @namespace BDD + * @api public + */ + + should.fail = function (actual, expected, message, operator) { + if (arguments.length < 2) { + message = actual; + actual = undefined; + } + + message = message || 'should.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, should.fail); + }; + + /** + * ### .equal(actual, expected, [message]) + * + * Asserts non-strict equality (`==`) of `actual` and `expected`. + * + * should.equal(3, '3', '== coerces values to strings'); + * + * @name equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Should + * @api public + */ + + should.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.equal(val2); + }; + + /** + * ### .throw(function, [constructor/string/regexp], [string/regexp], [message]) + * + * Asserts that `function` will throw an error that is an instance of + * `constructor`, or alternately that it will throw an error with message + * matching `regexp`. + * + * should.throw(fn, 'function throws a reference error'); + * should.throw(fn, /function throws a reference error/); + * should.throw(fn, ReferenceError); + * should.throw(fn, ReferenceError, 'function throws a reference error'); + * should.throw(fn, ReferenceError, /function throws a reference error/); + * + * @name throw + * @alias Throw + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Should + * @api public + */ + + should.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.Throw(errt, errs); + }; + + /** + * ### .exist + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi'; + * + * should.exist(foo, 'foo exists'); + * + * @name exist + * @namespace Should + * @api public + */ + + should.exist = function (val, msg) { + new Assertion(val, msg).to.exist; + } + + // negation + should.not = {} + + /** + * ### .not.equal(actual, expected, [message]) + * + * Asserts non-strict inequality (`!=`) of `actual` and `expected`. + * + * should.not.equal(3, 4, 'these numbers are not equal'); + * + * @name not.equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Should + * @api public + */ + + should.not.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.not.equal(val2); + }; + + /** + * ### .throw(function, [constructor/regexp], [message]) + * + * Asserts that `function` will _not_ throw an error that is an instance of + * `constructor`, or alternately that it will not throw an error with message + * matching `regexp`. + * + * should.not.throw(fn, Error, 'function does not throw'); + * + * @name not.throw + * @alias not.Throw + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Should + * @api public + */ + + should.not.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.not.Throw(errt, errs); + }; + + /** + * ### .not.exist + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var bar = null; + * + * should.not.exist(bar, 'bar does not exist'); + * + * @name not.exist + * @namespace Should + * @api public + */ + + should.not.exist = function (val, msg) { + new Assertion(val, msg).to.not.exist; + } + + should['throw'] = should['Throw']; + should.not['throw'] = should.not['Throw']; + + return should; + }; + + chai.should = loadShould; + chai.Should = loadShould; +}; + +},{}],9:[function(require,module,exports){ +/*! + * Chai - addChainingMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); + +/*! + * Module variables + */ + +// Check whether `Object.setPrototypeOf` is supported +var canSetPrototype = typeof Object.setPrototypeOf === 'function'; + +// Without `Object.setPrototypeOf` support, this module will need to add properties to a function. +// However, some of functions' own props are not configurable and should be skipped. +var testFn = function() {}; +var excludeNames = Object.getOwnPropertyNames(testFn).filter(function(name) { + var propDesc = Object.getOwnPropertyDescriptor(testFn, name); + + // Note: PhantomJS 1.x includes `callee` as one of `testFn`'s own properties, + // but then returns `undefined` as the property descriptor for `callee`. As a + // workaround, we perform an otherwise unnecessary type-check for `propDesc`, + // and then filter it out if it's not an object as it should be. + if (typeof propDesc !== 'object') + return true; + + return !propDesc.configurable; +}); + +// Cache `Function` properties +var call = Function.prototype.call, + apply = Function.prototype.apply; + +/** + * ### .addChainableMethod(ctx, name, method, chainingBehavior) + * + * Adds a method to an object, such that the method can also be chained. + * + * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); + * + * The result can then be used as both a method assertion, executing both `method` and + * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. + * + * expect(fooStr).to.be.foo('bar'); + * expect(fooStr).to.be.foo.equal('foo'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for `name`, when called + * @param {Function} chainingBehavior function to be called every time the property is accessed + * @namespace Utils + * @name addChainableMethod + * @api public + */ + +module.exports = function addChainableMethod(ctx, name, method, chainingBehavior) { + if (typeof chainingBehavior !== 'function') { + chainingBehavior = function () { }; + } + + var chainableBehavior = { + method: method + , chainingBehavior: chainingBehavior + }; + + // save the methods so we can overwrite them later, if we need to. + if (!ctx.__methods) { + ctx.__methods = {}; + } + ctx.__methods[name] = chainableBehavior; + + Object.defineProperty(ctx, name, + { get: function chainableMethodGetter() { + chainableBehavior.chainingBehavior.call(this); + + var chainableMethodWrapper = function () { + // Setting the `ssfi` flag to `chainableMethodWrapper` causes this + // function to be the starting point for removing implementation + // frames from the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then this assertion is being + // invoked from inside of another assertion. In this case, the `ssfi` + // flag has already been set by the outer assertion. + // + // Note that overwriting a chainable method merely replaces the saved + // methods in `ctx.__methods` instead of completely replacing the + // overwritten assertion. Therefore, an overwriting assertion won't + // set the `ssfi` or `lockSsfi` flags. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', chainableMethodWrapper); + } + + var result = chainableBehavior.method.apply(this, arguments); + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; + + addLengthGuard(chainableMethodWrapper, name, true); + + // Use `Object.setPrototypeOf` if available + if (canSetPrototype) { + // Inherit all properties from the object by replacing the `Function` prototype + var prototype = Object.create(this); + // Restore the `call` and `apply` methods from `Function` + prototype.call = call; + prototype.apply = apply; + Object.setPrototypeOf(chainableMethodWrapper, prototype); + } + // Otherwise, redefine all properties (slow!) + else { + var asserterNames = Object.getOwnPropertyNames(ctx); + asserterNames.forEach(function (asserterName) { + if (excludeNames.indexOf(asserterName) !== -1) { + return; + } + + var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); + Object.defineProperty(chainableMethodWrapper, asserterName, pd); + }); + } + + transferFlags(this, chainableMethodWrapper); + return proxify(chainableMethodWrapper); + } + , configurable: true + }); +}; + +},{"../../chai":2,"./addLengthGuard":10,"./flag":15,"./proxify":30,"./transferFlags":32}],10:[function(require,module,exports){ +var fnLengthDesc = Object.getOwnPropertyDescriptor(function () {}, 'length'); + +/*! + * Chai - addLengthGuard utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .addLengthGuard(fn, assertionName, isChainable) + * + * Define `length` as a getter on the given uninvoked method assertion. The + * getter acts as a guard against chaining `length` directly off of an uninvoked + * method assertion, which is a problem because it references `function`'s + * built-in `length` property instead of Chai's `length` assertion. When the + * getter catches the user making this mistake, it throws an error with a + * helpful message. + * + * There are two ways in which this mistake can be made. The first way is by + * chaining the `length` assertion directly off of an uninvoked chainable + * method. In this case, Chai suggests that the user use `lengthOf` instead. The + * second way is by chaining the `length` assertion directly off of an uninvoked + * non-chainable method. Non-chainable methods must be invoked prior to + * chaining. In this case, Chai suggests that the user consult the docs for the + * given assertion. + * + * If the `length` property of functions is unconfigurable, then return `fn` + * without modification. + * + * Note that in ES6, the function's `length` property is configurable, so once + * support for legacy environments is dropped, Chai's `length` property can + * replace the built-in function's `length` property, and this length guard will + * no longer be necessary. In the mean time, maintaining consistency across all + * environments is the priority. + * + * @param {Function} fn + * @param {String} assertionName + * @param {Boolean} isChainable + * @namespace Utils + * @name addLengthGuard + */ + +module.exports = function addLengthGuard (fn, assertionName, isChainable) { + if (!fnLengthDesc.configurable) return fn; + + Object.defineProperty(fn, 'length', { + get: function () { + if (isChainable) { + throw Error('Invalid Chai property: ' + assertionName + '.length. Due' + + ' to a compatibility issue, "length" cannot directly follow "' + + assertionName + '". Use "' + assertionName + '.lengthOf" instead.'); + } + + throw Error('Invalid Chai property: ' + assertionName + '.length. See' + + ' docs for proper usage of "' + assertionName + '".'); + } + }); + + return fn; +}; + +},{}],11:[function(require,module,exports){ +/*! + * Chai - addMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); + +/** + * ### .addMethod(ctx, name, method) + * + * Adds a method to the prototype of an object. + * + * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(fooStr).to.be.foo('bar'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for name + * @namespace Utils + * @name addMethod + * @api public + */ + +module.exports = function addMethod(ctx, name, method) { + var methodWrapper = function () { + // Setting the `ssfi` flag to `methodWrapper` causes this function to be the + // starting point for removing implementation frames from the stack trace of + // a failed assertion. + // + // However, we only want to use this function as the starting point if the + // `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked from + // inside of another assertion. In the first case, the `ssfi` flag has + // already been set by the overwriting assertion. In the second case, the + // `ssfi` flag has already been set by the outer assertion. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', methodWrapper); + } + + var result = method.apply(this, arguments); + if (result !== undefined) + return result; + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; + + addLengthGuard(methodWrapper, name, false); + ctx[name] = proxify(methodWrapper, name); +}; + +},{"../../chai":2,"./addLengthGuard":10,"./flag":15,"./proxify":30,"./transferFlags":32}],12:[function(require,module,exports){ +/*! + * Chai - addProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var chai = require('../../chai'); +var flag = require('./flag'); +var isProxyEnabled = require('./isProxyEnabled'); +var transferFlags = require('./transferFlags'); + +/** + * ### .addProperty(ctx, name, getter) + * + * Adds a property to the prototype of an object. + * + * utils.addProperty(chai.Assertion.prototype, 'foo', function () { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.instanceof(Foo); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.foo; + * + * @param {Object} ctx object to which the property is added + * @param {String} name of property to add + * @param {Function} getter function to be used for name + * @namespace Utils + * @name addProperty + * @api public + */ + +module.exports = function addProperty(ctx, name, getter) { + getter = getter === undefined ? function () {} : getter; + + Object.defineProperty(ctx, name, + { get: function propertyGetter() { + // Setting the `ssfi` flag to `propertyGetter` causes this function to + // be the starting point for removing implementation frames from the + // stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set and proxy protection is disabled. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked + // from inside of another assertion. In the first case, the `ssfi` flag + // has already been set by the overwriting assertion. In the second + // case, the `ssfi` flag has already been set by the outer assertion. + // + // If proxy protection is enabled, then the `ssfi` flag has already been + // set by the proxy getter. + if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { + flag(this, 'ssfi', propertyGetter); + } + + var result = getter.call(this); + if (result !== undefined) + return result; + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + } + , configurable: true + }); +}; + +},{"../../chai":2,"./flag":15,"./isProxyEnabled":25,"./transferFlags":32}],13:[function(require,module,exports){ +/*! + * Chai - compareByInspect utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var inspect = require('./inspect'); + +/** + * ### .compareByInspect(mixed, mixed) + * + * To be used as a compareFunction with Array.prototype.sort. Compares elements + * using inspect instead of default behavior of using toString so that Symbols + * and objects with irregular/missing toString can still be sorted without a + * TypeError. + * + * @param {Mixed} first element to compare + * @param {Mixed} second element to compare + * @returns {Number} -1 if 'a' should come before 'b'; otherwise 1 + * @name compareByInspect + * @namespace Utils + * @api public + */ + +module.exports = function compareByInspect(a, b) { + return inspect(a) < inspect(b) ? -1 : 1; +}; + +},{"./inspect":23}],14:[function(require,module,exports){ +/*! + * Chai - expectTypes utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .expectTypes(obj, types) + * + * Ensures that the object being tested against is of a valid type. + * + * utils.expectTypes(this, ['array', 'object', 'string']); + * + * @param {Mixed} obj constructed Assertion + * @param {Array} type A list of allowed types for this assertion + * @namespace Utils + * @name expectTypes + * @api public + */ + +var AssertionError = require('assertion-error'); +var flag = require('./flag'); +var type = require('type-detect'); + +module.exports = function expectTypes(obj, types) { + var flagMsg = flag(obj, 'message'); + var ssfi = flag(obj, 'ssfi'); + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + obj = flag(obj, 'object'); + types = types.map(function (t) { return t.toLowerCase(); }); + types.sort(); + + // Transforms ['lorem', 'ipsum'] into 'a lorem, or an ipsum' + var str = types.map(function (t, index) { + var art = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(t.charAt(0)) ? 'an' : 'a'; + var or = types.length > 1 && index === types.length - 1 ? 'or ' : ''; + return or + art + ' ' + t; + }).join(', '); + + var objType = type(obj).toLowerCase(); + + if (!types.some(function (expected) { return objType === expected; })) { + throw new AssertionError( + flagMsg + 'object tested must be ' + str + ', but ' + objType + ' given', + undefined, + ssfi + ); + } +}; + +},{"./flag":15,"assertion-error":33,"type-detect":38}],15:[function(require,module,exports){ +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .flag(object, key, [value]) + * + * Get or set a flag value on an object. If a + * value is provided it will be set, else it will + * return the currently set value or `undefined` if + * the value is not set. + * + * utils.flag(this, 'foo', 'bar'); // setter + * utils.flag(this, 'foo'); // getter, returns `bar` + * + * @param {Object} object constructed Assertion + * @param {String} key + * @param {Mixed} value (optional) + * @namespace Utils + * @name flag + * @api private + */ + +module.exports = function flag(obj, key, value) { + var flags = obj.__flags || (obj.__flags = Object.create(null)); + if (arguments.length === 3) { + flags[key] = value; + } else { + return flags[key]; + } +}; + +},{}],16:[function(require,module,exports){ +/*! + * Chai - getActual utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getActual(object, [actual]) + * + * Returns the `actual` value for an Assertion. + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @namespace Utils + * @name getActual + */ + +module.exports = function getActual(obj, args) { + return args.length > 4 ? args[4] : obj._obj; +}; + +},{}],17:[function(require,module,exports){ +/*! + * Chai - getEnumerableProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getEnumerableProperties(object) + * + * This allows the retrieval of enumerable property names of an object, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getEnumerableProperties + * @api public + */ + +module.exports = function getEnumerableProperties(object) { + var result = []; + for (var name in object) { + result.push(name); + } + return result; +}; + +},{}],18:[function(require,module,exports){ +/*! + * Chai - message composition utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var flag = require('./flag') + , getActual = require('./getActual') + , objDisplay = require('./objDisplay'); + +/** + * ### .getMessage(object, message, negateMessage) + * + * Construct the error message based on flags + * and template tags. Template tags will return + * a stringified inspection of the object referenced. + * + * Message template tags: + * - `#{this}` current asserted object + * - `#{act}` actual value + * - `#{exp}` expected value + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @namespace Utils + * @name getMessage + * @api public + */ + +module.exports = function getMessage(obj, args) { + var negate = flag(obj, 'negate') + , val = flag(obj, 'object') + , expected = args[3] + , actual = getActual(obj, args) + , msg = negate ? args[2] : args[1] + , flagMsg = flag(obj, 'message'); + + if(typeof msg === "function") msg = msg(); + msg = msg || ''; + msg = msg + .replace(/#\{this\}/g, function () { return objDisplay(val); }) + .replace(/#\{act\}/g, function () { return objDisplay(actual); }) + .replace(/#\{exp\}/g, function () { return objDisplay(expected); }); + + return flagMsg ? flagMsg + ': ' + msg : msg; +}; + +},{"./flag":15,"./getActual":16,"./objDisplay":26}],19:[function(require,module,exports){ +/*! + * Chai - getOwnEnumerableProperties utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var getOwnEnumerablePropertySymbols = require('./getOwnEnumerablePropertySymbols'); + +/** + * ### .getOwnEnumerableProperties(object) + * + * This allows the retrieval of directly-owned enumerable property names and + * symbols of an object. This function is necessary because Object.keys only + * returns enumerable property names, not enumerable property symbols. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getOwnEnumerableProperties + * @api public + */ + +module.exports = function getOwnEnumerableProperties(obj) { + return Object.keys(obj).concat(getOwnEnumerablePropertySymbols(obj)); +}; + +},{"./getOwnEnumerablePropertySymbols":20}],20:[function(require,module,exports){ +/*! + * Chai - getOwnEnumerablePropertySymbols utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +/** + * ### .getOwnEnumerablePropertySymbols(object) + * + * This allows the retrieval of directly-owned enumerable property symbols of an + * object. This function is necessary because Object.getOwnPropertySymbols + * returns both enumerable and non-enumerable property symbols. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getOwnEnumerablePropertySymbols + * @api public + */ + +module.exports = function getOwnEnumerablePropertySymbols(obj) { + if (typeof Object.getOwnPropertySymbols !== 'function') return []; + + return Object.getOwnPropertySymbols(obj).filter(function (sym) { + return Object.getOwnPropertyDescriptor(obj, sym).enumerable; + }); +}; + +},{}],21:[function(require,module,exports){ +/*! + * Chai - getProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getProperties(object) + * + * This allows the retrieval of property names of an object, enumerable or not, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getProperties + * @api public + */ + +module.exports = function getProperties(object) { + var result = Object.getOwnPropertyNames(object); + + function addProperty(property) { + if (result.indexOf(property) === -1) { + result.push(property); + } + } + + var proto = Object.getPrototypeOf(object); + while (proto !== null) { + Object.getOwnPropertyNames(proto).forEach(addProperty); + proto = Object.getPrototypeOf(proto); + } + + return result; +}; + +},{}],22:[function(require,module,exports){ +/*! + * chai + * Copyright(c) 2011 Jake Luer + * MIT Licensed + */ + +/*! + * Dependencies that are used for multiple exports are required here only once + */ + +var pathval = require('pathval'); + +/*! + * test utility + */ + +exports.test = require('./test'); + +/*! + * type utility + */ + +exports.type = require('type-detect'); + +/*! + * expectTypes utility + */ +exports.expectTypes = require('./expectTypes'); + +/*! + * message utility + */ + +exports.getMessage = require('./getMessage'); + +/*! + * actual utility + */ + +exports.getActual = require('./getActual'); + +/*! + * Inspect util + */ + +exports.inspect = require('./inspect'); + +/*! + * Object Display util + */ + +exports.objDisplay = require('./objDisplay'); + +/*! + * Flag utility + */ + +exports.flag = require('./flag'); + +/*! + * Flag transferring utility + */ + +exports.transferFlags = require('./transferFlags'); + +/*! + * Deep equal utility + */ + +exports.eql = require('deep-eql'); + +/*! + * Deep path info + */ + +exports.getPathInfo = pathval.getPathInfo; + +/*! + * Check if a property exists + */ + +exports.hasProperty = pathval.hasProperty; + +/*! + * Function name + */ + +exports.getName = require('get-func-name'); + +/*! + * add Property + */ + +exports.addProperty = require('./addProperty'); + +/*! + * add Method + */ + +exports.addMethod = require('./addMethod'); + +/*! + * overwrite Property + */ + +exports.overwriteProperty = require('./overwriteProperty'); + +/*! + * overwrite Method + */ + +exports.overwriteMethod = require('./overwriteMethod'); + +/*! + * Add a chainable method + */ + +exports.addChainableMethod = require('./addChainableMethod'); + +/*! + * Overwrite chainable method + */ + +exports.overwriteChainableMethod = require('./overwriteChainableMethod'); + +/*! + * Compare by inspect method + */ + +exports.compareByInspect = require('./compareByInspect'); + +/*! + * Get own enumerable property symbols method + */ + +exports.getOwnEnumerablePropertySymbols = require('./getOwnEnumerablePropertySymbols'); + +/*! + * Get own enumerable properties method + */ + +exports.getOwnEnumerableProperties = require('./getOwnEnumerableProperties'); + +/*! + * Checks error against a given set of criteria + */ + +exports.checkError = require('check-error'); + +/*! + * Proxify util + */ + +exports.proxify = require('./proxify'); + +/*! + * addLengthGuard util + */ + +exports.addLengthGuard = require('./addLengthGuard'); + +/*! + * isProxyEnabled helper + */ + +exports.isProxyEnabled = require('./isProxyEnabled'); + +/*! + * isNaN method + */ + +exports.isNaN = require('./isNaN'); + +},{"./addChainableMethod":9,"./addLengthGuard":10,"./addMethod":11,"./addProperty":12,"./compareByInspect":13,"./expectTypes":14,"./flag":15,"./getActual":16,"./getMessage":18,"./getOwnEnumerableProperties":19,"./getOwnEnumerablePropertySymbols":20,"./inspect":23,"./isNaN":24,"./isProxyEnabled":25,"./objDisplay":26,"./overwriteChainableMethod":27,"./overwriteMethod":28,"./overwriteProperty":29,"./proxify":30,"./test":31,"./transferFlags":32,"check-error":34,"deep-eql":35,"get-func-name":36,"pathval":37,"type-detect":38}],23:[function(require,module,exports){ +// This is (almost) directly from Node.js utils +// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js + +var getName = require('get-func-name'); +var getProperties = require('./getProperties'); +var getEnumerableProperties = require('./getEnumerableProperties'); +var config = require('../config'); + +module.exports = inspect; + +/** + * ### .inspect(obj, [showHidden], [depth], [colors]) + * + * Echoes the value of a value. Tries to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Boolean} showHidden Flag that shows hidden (not enumerable) + * properties of objects. Default is false. + * @param {Number} depth Depth in which to descend in object. Default is 2. + * @param {Boolean} colors Flag to turn on ANSI escape codes to color the + * output. Default is false (no coloring). + * @namespace Utils + * @name inspect + */ +function inspect(obj, showHidden, depth, colors) { + var ctx = { + showHidden: showHidden, + seen: [], + stylize: function (str) { return str; } + }; + return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); +} + +// Returns true if object is a DOM element. +var isDOMElement = function (object) { + if (typeof HTMLElement === 'object') { + return object instanceof HTMLElement; + } else { + return object && + typeof object === 'object' && + 'nodeType' in object && + object.nodeType === 1 && + typeof object.nodeName === 'string'; + } +}; + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (typeof ret !== 'string') { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // If this is a DOM element, try to get the outer HTML. + if (isDOMElement(value)) { + if ('outerHTML' in value) { + return value.outerHTML; + // This value does not have an outerHTML attribute, + // it could still be an XML element + } else { + // Attempt to serialize it + try { + if (document.xmlVersion) { + var xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(value); + } else { + // Firefox 11- do not support outerHTML + // It does, however, support innerHTML + // Use the following to render the element + var ns = "http://www.w3.org/1999/xhtml"; + var container = document.createElementNS(ns, '_'); + + container.appendChild(value.cloneNode(false)); + var html = container.innerHTML + .replace('><', '>' + value.innerHTML + '<'); + container.innerHTML = ''; + return html; + } + } catch (err) { + // This could be a non-native DOM implementation, + // continue with the normal flow: + // printing the element as if it is an object. + } + } + } + + // Look up the keys of the object. + var visibleKeys = getEnumerableProperties(value); + var keys = ctx.showHidden ? getProperties(value) : visibleKeys; + + var name, nameSuffix; + + // Some type of object without properties can be shortcut. + // In IE, errors have a single `stack` property, or if they are vanilla `Error`, + // a `stack` plus `description` property; ignore those for consistency. + if (keys.length === 0 || (isError(value) && ( + (keys.length === 1 && keys[0] === 'stack') || + (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') + ))) { + if (typeof value === 'function') { + name = getName(value); + nameSuffix = name ? ': ' + name : ''; + return ctx.stylize('[Function' + nameSuffix + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '' + , array = false + , typedArray = false + , braces = ['{', '}']; + + if (isTypedArray(value)) { + typedArray = true; + braces = ['[', ']']; + } + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + name = getName(value); + nameSuffix = name ? ': ' + name : ''; + base = ' [Function' + nameSuffix + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + return formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else if (typedArray) { + return formatTypedArray(value); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + +function formatPrimitive(ctx, value) { + switch (typeof value) { + case 'undefined': + return ctx.stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + + case 'number': + if (value === 0 && (1/value) === -Infinity) { + return ctx.stylize('-0', 'number'); + } + return ctx.stylize('' + value, 'number'); + + case 'boolean': + return ctx.stylize('' + value, 'boolean'); + + case 'symbol': + return ctx.stylize(value.toString(), 'symbol'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return ctx.stylize('null', 'null'); + } +} + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (Object.prototype.hasOwnProperty.call(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + +function formatTypedArray(value) { + var str = '[ '; + + for (var i = 0; i < value.length; ++i) { + if (str.length >= config.truncateThreshold - 7) { + str += '...'; + break; + } + str += value[i] + ', '; + } + str += ' ]'; + + // Removing trailing `, ` if the array was not truncated + if (str.indexOf(', ]') !== -1) { + str = str.replace(', ]', ' ]'); + } + + return str; +} + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name; + var propDescriptor = Object.getOwnPropertyDescriptor(value, key); + var str; + + if (propDescriptor) { + if (propDescriptor.get) { + if (propDescriptor.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (propDescriptor.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + } + if (visibleKeys.indexOf(key) < 0) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(value[key]) < 0) { + if (recurseTimes === null) { + str = formatValue(ctx, value[key], null); + } else { + str = formatValue(ctx, value[key], recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + +function reduceToSingleString(output, base, braces) { + var length = output.reduce(function(prev, cur) { + return prev + cur.length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + +function isTypedArray(ar) { + // Unfortunately there's no way to check if an object is a TypedArray + // We have to check if it's one of these types + return (typeof ar === 'object' && /\w+Array]$/.test(objectToString(ar))); +} + +function isArray(ar) { + return Array.isArray(ar) || + (typeof ar === 'object' && objectToString(ar) === '[object Array]'); +} + +function isRegExp(re) { + return typeof re === 'object' && objectToString(re) === '[object RegExp]'; +} + +function isDate(d) { + return typeof d === 'object' && objectToString(d) === '[object Date]'; +} + +function isError(e) { + return typeof e === 'object' && objectToString(e) === '[object Error]'; +} + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +},{"../config":4,"./getEnumerableProperties":17,"./getProperties":21,"get-func-name":36}],24:[function(require,module,exports){ +/*! + * Chai - isNaN utility + * Copyright(c) 2012-2015 Sakthipriyan Vairamani + * MIT Licensed + */ + +/** + * ### .isNaN(value) + * + * Checks if the given value is NaN or not. + * + * utils.isNaN(NaN); // true + * + * @param {Value} The value which has to be checked if it is NaN + * @name isNaN + * @api private + */ + +function isNaN(value) { + // Refer http://www.ecma-international.org/ecma-262/6.0/#sec-isnan-number + // section's NOTE. + return value !== value; +} + +// If ECMAScript 6's Number.isNaN is present, prefer that. +module.exports = Number.isNaN || isNaN; + +},{}],25:[function(require,module,exports){ +var config = require('../config'); + +/*! + * Chai - isProxyEnabled helper + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .isProxyEnabled() + * + * Helper function to check if Chai's proxy protection feature is enabled. If + * proxies are unsupported or disabled via the user's Chai config, then return + * false. Otherwise, return true. + * + * @namespace Utils + * @name isProxyEnabled + */ + +module.exports = function isProxyEnabled() { + return config.useProxy && + typeof Proxy !== 'undefined' && + typeof Reflect !== 'undefined'; +}; + +},{"../config":4}],26:[function(require,module,exports){ +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var inspect = require('./inspect'); +var config = require('../config'); + +/** + * ### .objDisplay(object) + * + * Determines if an object or an array matches + * criteria to be inspected in-line for error + * messages or should be truncated. + * + * @param {Mixed} javascript object to inspect + * @name objDisplay + * @namespace Utils + * @api public + */ + +module.exports = function objDisplay(obj) { + var str = inspect(obj) + , type = Object.prototype.toString.call(obj); + + if (config.truncateThreshold && str.length >= config.truncateThreshold) { + if (type === '[object Function]') { + return !obj.name || obj.name === '' + ? '[Function]' + : '[Function: ' + obj.name + ']'; + } else if (type === '[object Array]') { + return '[ Array(' + obj.length + ') ]'; + } else if (type === '[object Object]') { + var keys = Object.keys(obj) + , kstr = keys.length > 2 + ? keys.splice(0, 2).join(', ') + ', ...' + : keys.join(', '); + return '{ Object (' + kstr + ') }'; + } else { + return str; + } + } else { + return str; + } +}; + +},{"../config":4,"./inspect":23}],27:[function(require,module,exports){ +/*! + * Chai - overwriteChainableMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var chai = require('../../chai'); +var transferFlags = require('./transferFlags'); + +/** + * ### .overwriteChainableMethod(ctx, name, method, chainingBehavior) + * + * Overwrites an already existing chainable method + * and provides access to the previous function or + * property. Must return functions to be used for + * name. + * + * utils.overwriteChainableMethod(chai.Assertion.prototype, 'lengthOf', + * function (_super) { + * } + * , function (_super) { + * } + * ); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteChainableMethod('foo', fn, fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.have.lengthOf(3); + * expect(myFoo).to.have.lengthOf.above(3); + * + * @param {Object} ctx object whose method / property is to be overwritten + * @param {String} name of method / property to overwrite + * @param {Function} method function that returns a function to be used for name + * @param {Function} chainingBehavior function that returns a function to be used for property + * @namespace Utils + * @name overwriteChainableMethod + * @api public + */ + +module.exports = function overwriteChainableMethod(ctx, name, method, chainingBehavior) { + var chainableBehavior = ctx.__methods[name]; + + var _chainingBehavior = chainableBehavior.chainingBehavior; + chainableBehavior.chainingBehavior = function overwritingChainableMethodGetter() { + var result = chainingBehavior(_chainingBehavior).call(this); + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; + + var _method = chainableBehavior.method; + chainableBehavior.method = function overwritingChainableMethodWrapper() { + var result = method(_method).apply(this, arguments); + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; +}; + +},{"../../chai":2,"./transferFlags":32}],28:[function(require,module,exports){ +/*! + * Chai - overwriteMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); + +/** + * ### .overwriteMethod(ctx, name, fn) + * + * Overwrites an already existing method and provides + * access to previous function. Must return function + * to be used for name. + * + * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { + * return function (str) { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.value).to.equal(str); + * } else { + * _super.apply(this, arguments); + * } + * } + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.equal('bar'); + * + * @param {Object} ctx object whose method is to be overwritten + * @param {String} name of method to overwrite + * @param {Function} method function that returns a function to be used for name + * @namespace Utils + * @name overwriteMethod + * @api public + */ + +module.exports = function overwriteMethod(ctx, name, method) { + var _method = ctx[name] + , _super = function () { + throw new Error(name + ' is not a function'); + }; + + if (_method && 'function' === typeof _method) + _super = _method; + + var overwritingMethodWrapper = function () { + // Setting the `ssfi` flag to `overwritingMethodWrapper` causes this + // function to be the starting point for removing implementation frames from + // the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if the + // `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked from + // inside of another assertion. In the first case, the `ssfi` flag has + // already been set by the overwriting assertion. In the second case, the + // `ssfi` flag has already been set by the outer assertion. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', overwritingMethodWrapper); + } + + // Setting the `lockSsfi` flag to `true` prevents the overwritten assertion + // from changing the `ssfi` flag. By this point, the `ssfi` flag is already + // set to the correct starting point for this assertion. + var origLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); + var result = method(_super).apply(this, arguments); + flag(this, 'lockSsfi', origLockSsfi); + + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + } + + addLengthGuard(overwritingMethodWrapper, name, false); + ctx[name] = proxify(overwritingMethodWrapper, name); +}; + +},{"../../chai":2,"./addLengthGuard":10,"./flag":15,"./proxify":30,"./transferFlags":32}],29:[function(require,module,exports){ +/*! + * Chai - overwriteProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var chai = require('../../chai'); +var flag = require('./flag'); +var isProxyEnabled = require('./isProxyEnabled'); +var transferFlags = require('./transferFlags'); + +/** + * ### .overwriteProperty(ctx, name, fn) + * + * Overwrites an already existing property getter and provides + * access to previous value. Must return function to use as getter. + * + * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { + * return function () { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.name).to.equal('bar'); + * } else { + * _super.call(this); + * } + * } + * }); + * + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.ok; + * + * @param {Object} ctx object whose property is to be overwritten + * @param {String} name of property to overwrite + * @param {Function} getter function that returns a getter function to be used for name + * @namespace Utils + * @name overwriteProperty + * @api public + */ + +module.exports = function overwriteProperty(ctx, name, getter) { + var _get = Object.getOwnPropertyDescriptor(ctx, name) + , _super = function () {}; + + if (_get && 'function' === typeof _get.get) + _super = _get.get + + Object.defineProperty(ctx, name, + { get: function overwritingPropertyGetter() { + // Setting the `ssfi` flag to `overwritingPropertyGetter` causes this + // function to be the starting point for removing implementation frames + // from the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set and proxy protection is disabled. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked + // from inside of another assertion. In the first case, the `ssfi` flag + // has already been set by the overwriting assertion. In the second + // case, the `ssfi` flag has already been set by the outer assertion. + // + // If proxy protection is enabled, then the `ssfi` flag has already been + // set by the proxy getter. + if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { + flag(this, 'ssfi', overwritingPropertyGetter); + } + + // Setting the `lockSsfi` flag to `true` prevents the overwritten + // assertion from changing the `ssfi` flag. By this point, the `ssfi` + // flag is already set to the correct starting point for this assertion. + var origLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); + var result = getter(_super).call(this); + flag(this, 'lockSsfi', origLockSsfi); + + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + } + , configurable: true + }); +}; + +},{"../../chai":2,"./flag":15,"./isProxyEnabled":25,"./transferFlags":32}],30:[function(require,module,exports){ +var config = require('../config'); +var flag = require('./flag'); +var getProperties = require('./getProperties'); +var isProxyEnabled = require('./isProxyEnabled'); + +/*! + * Chai - proxify utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .proxify(object) + * + * Return a proxy of given object that throws an error when a non-existent + * property is read. By default, the root cause is assumed to be a misspelled + * property, and thus an attempt is made to offer a reasonable suggestion from + * the list of existing properties. However, if a nonChainableMethodName is + * provided, then the root cause is instead a failure to invoke a non-chainable + * method prior to reading the non-existent property. + * + * If proxies are unsupported or disabled via the user's Chai config, then + * return object without modification. + * + * @param {Object} obj + * @param {String} nonChainableMethodName + * @namespace Utils + * @name proxify + */ + +var builtins = ['__flags', '__methods', '_obj', 'assert']; + +module.exports = function proxify(obj, nonChainableMethodName) { + if (!isProxyEnabled()) return obj; + + return new Proxy(obj, { + get: function proxyGetter(target, property) { + // This check is here because we should not throw errors on Symbol properties + // such as `Symbol.toStringTag`. + // The values for which an error should be thrown can be configured using + // the `config.proxyExcludedKeys` setting. + if (typeof property === 'string' && + config.proxyExcludedKeys.indexOf(property) === -1 && + !Reflect.has(target, property)) { + // Special message for invalid property access of non-chainable methods. + if (nonChainableMethodName) { + throw Error('Invalid Chai property: ' + nonChainableMethodName + '.' + + property + '. See docs for proper usage of "' + + nonChainableMethodName + '".'); + } + + // If the property is reasonably close to an existing Chai property, + // suggest that property to the user. Only suggest properties with a + // distance less than 4. + var suggestion = null; + var suggestionDistance = 4; + getProperties(target).forEach(function(prop) { + if ( + !Object.prototype.hasOwnProperty(prop) && + builtins.indexOf(prop) === -1 + ) { + var dist = stringDistanceCapped( + property, + prop, + suggestionDistance + ); + if (dist < suggestionDistance) { + suggestion = prop; + suggestionDistance = dist; + } + } + }); + + if (suggestion !== null) { + throw Error('Invalid Chai property: ' + property + + '. Did you mean "' + suggestion + '"?'); + } else { + throw Error('Invalid Chai property: ' + property); + } + } + + // Use this proxy getter as the starting point for removing implementation + // frames from the stack trace of a failed assertion. For property + // assertions, this prevents the proxy getter from showing up in the stack + // trace since it's invoked before the property getter. For method and + // chainable method assertions, this flag will end up getting changed to + // the method wrapper, which is good since this frame will no longer be in + // the stack once the method is invoked. Note that Chai builtin assertion + // properties such as `__flags` are skipped since this is only meant to + // capture the starting point of an assertion. This step is also skipped + // if the `lockSsfi` flag is set, thus indicating that this assertion is + // being called from within another assertion. In that case, the `ssfi` + // flag is already set to the outer assertion's starting point. + if (builtins.indexOf(property) === -1 && !flag(target, 'lockSsfi')) { + flag(target, 'ssfi', proxyGetter); + } + + return Reflect.get(target, property); + } + }); +}; + +/** + * # stringDistanceCapped(strA, strB, cap) + * Return the Levenshtein distance between two strings, but no more than cap. + * @param {string} strA + * @param {string} strB + * @param {number} number + * @return {number} min(string distance between strA and strB, cap) + * @api private + */ + +function stringDistanceCapped(strA, strB, cap) { + if (Math.abs(strA.length - strB.length) >= cap) { + return cap; + } + + var memo = []; + // `memo` is a two-dimensional array containing distances. + // memo[i][j] is the distance between strA.slice(0, i) and + // strB.slice(0, j). + for (var i = 0; i <= strA.length; i++) { + memo[i] = Array(strB.length + 1).fill(0); + memo[i][0] = i; + } + for (var j = 0; j < strB.length; j++) { + memo[0][j] = j; + } + + for (var i = 1; i <= strA.length; i++) { + var ch = strA.charCodeAt(i - 1); + for (var j = 1; j <= strB.length; j++) { + if (Math.abs(i - j) >= cap) { + memo[i][j] = cap; + continue; + } + memo[i][j] = Math.min( + memo[i - 1][j] + 1, + memo[i][j - 1] + 1, + memo[i - 1][j - 1] + + (ch === strB.charCodeAt(j - 1) ? 0 : 1) + ); + } + } + + return memo[strA.length][strB.length]; +} + +},{"../config":4,"./flag":15,"./getProperties":21,"./isProxyEnabled":25}],31:[function(require,module,exports){ +/*! + * Chai - test utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var flag = require('./flag'); + +/** + * ### .test(object, expression) + * + * Test and object for expression. + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @namespace Utils + * @name test + */ + +module.exports = function test(obj, args) { + var negate = flag(obj, 'negate') + , expr = args[0]; + return negate ? !expr : expr; +}; + +},{"./flag":15}],32:[function(require,module,exports){ +/*! + * Chai - transferFlags utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .transferFlags(assertion, object, includeAll = true) + * + * Transfer all the flags for `assertion` to `object`. If + * `includeAll` is set to `false`, then the base Chai + * assertion flags (namely `object`, `ssfi`, `lockSsfi`, + * and `message`) will not be transferred. + * + * + * var newAssertion = new Assertion(); + * utils.transferFlags(assertion, newAssertion); + * + * var anotherAssertion = new Assertion(myObj); + * utils.transferFlags(assertion, anotherAssertion, false); + * + * @param {Assertion} assertion the assertion to transfer the flags from + * @param {Object} object the object to transfer the flags to; usually a new assertion + * @param {Boolean} includeAll + * @namespace Utils + * @name transferFlags + * @api private + */ + +module.exports = function transferFlags(assertion, object, includeAll) { + var flags = assertion.__flags || (assertion.__flags = Object.create(null)); + + if (!object.__flags) { + object.__flags = Object.create(null); + } + + includeAll = arguments.length === 3 ? includeAll : true; + + for (var flag in flags) { + if (includeAll || + (flag !== 'object' && flag !== 'ssfi' && flag !== 'lockSsfi' && flag != 'message')) { + object.__flags[flag] = flags[flag]; + } + } +}; + +},{}],33:[function(require,module,exports){ +/*! + * assertion-error + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Return a function that will copy properties from + * one object to another excluding any originally + * listed. Returned function will create a new `{}`. + * + * @param {String} excluded properties ... + * @return {Function} + */ + +function exclude () { + var excludes = [].slice.call(arguments); + + function excludeProps (res, obj) { + Object.keys(obj).forEach(function (key) { + if (!~excludes.indexOf(key)) res[key] = obj[key]; + }); + } + + return function extendExclude () { + var args = [].slice.call(arguments) + , i = 0 + , res = {}; + + for (; i < args.length; i++) { + excludeProps(res, args[i]); + } + + return res; + }; +}; + +/*! + * Primary Exports + */ + +module.exports = AssertionError; + +/** + * ### AssertionError + * + * An extension of the JavaScript `Error` constructor for + * assertion and validation scenarios. + * + * @param {String} message + * @param {Object} properties to include (optional) + * @param {callee} start stack function (optional) + */ + +function AssertionError (message, _props, ssf) { + var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') + , props = extend(_props || {}); + + // default values + this.message = message || 'Unspecified AssertionError'; + this.showDiff = false; + + // copy from properties + for (var key in props) { + this[key] = props[key]; + } + + // capture stack trace + ssf = ssf || AssertionError; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ssf); + } else { + try { + throw new Error(); + } catch(e) { + this.stack = e.stack; + } + } +} + +/*! + * Inherit from Error.prototype + */ + +AssertionError.prototype = Object.create(Error.prototype); + +/*! + * Statically set name + */ + +AssertionError.prototype.name = 'AssertionError'; + +/*! + * Ensure correct constructor + */ + +AssertionError.prototype.constructor = AssertionError; + +/** + * Allow errors to be converted to JSON for static transfer. + * + * @param {Boolean} include stack (default: `true`) + * @return {Object} object that can be `JSON.stringify` + */ + +AssertionError.prototype.toJSON = function (stack) { + var extend = exclude('constructor', 'toJSON', 'stack') + , props = extend({ name: this.name }, this); + + // include stack if exists and not turned off + if (false !== stack && this.stack) { + props.stack = this.stack; + } + + return props; +}; + +},{}],34:[function(require,module,exports){ +'use strict'; + +/* ! + * Chai - checkError utility + * Copyright(c) 2012-2016 Jake Luer + * MIT Licensed + */ + +/** + * ### .checkError + * + * Checks that an error conforms to a given set of criteria and/or retrieves information about it. + * + * @api public + */ + +/** + * ### .compatibleInstance(thrown, errorLike) + * + * Checks if two instances are compatible (strict equal). + * Returns false if errorLike is not an instance of Error, because instances + * can only be compatible if they're both error instances. + * + * @name compatibleInstance + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleInstance(thrown, errorLike) { + return errorLike instanceof Error && thrown === errorLike; +} + +/** + * ### .compatibleConstructor(thrown, errorLike) + * + * Checks if two constructors are compatible. + * This function can receive either an error constructor or + * an error instance as the `errorLike` argument. + * Constructors are compatible if they're the same or if one is + * an instance of another. + * + * @name compatibleConstructor + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleConstructor(thrown, errorLike) { + if (errorLike instanceof Error) { + // If `errorLike` is an instance of any error we compare their constructors + return thrown.constructor === errorLike.constructor || thrown instanceof errorLike.constructor; + } else if (errorLike.prototype instanceof Error || errorLike === Error) { + // If `errorLike` is a constructor that inherits from Error, we compare `thrown` to `errorLike` directly + return thrown.constructor === errorLike || thrown instanceof errorLike; + } + + return false; +} + +/** + * ### .compatibleMessage(thrown, errMatcher) + * + * Checks if an error's message is compatible with a matcher (String or RegExp). + * If the message contains the String or passes the RegExp test, + * it is considered compatible. + * + * @name compatibleMessage + * @param {Error} thrown error + * @param {String|RegExp} errMatcher to look for into the message + * @namespace Utils + * @api public + */ + +function compatibleMessage(thrown, errMatcher) { + var comparisonString = typeof thrown === 'string' ? thrown : thrown.message; + if (errMatcher instanceof RegExp) { + return errMatcher.test(comparisonString); + } else if (typeof errMatcher === 'string') { + return comparisonString.indexOf(errMatcher) !== -1; // eslint-disable-line no-magic-numbers + } + + return false; +} + +/** + * ### .getFunctionName(constructorFn) + * + * Returns the name of a function. + * This also includes a polyfill function if `constructorFn.name` is not defined. + * + * @name getFunctionName + * @param {Function} constructorFn + * @namespace Utils + * @api private + */ + +var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\(\/]+)/; +function getFunctionName(constructorFn) { + var name = ''; + if (typeof constructorFn.name === 'undefined') { + // Here we run a polyfill if constructorFn.name is not defined + var match = String(constructorFn).match(functionNameMatch); + if (match) { + name = match[1]; + } + } else { + name = constructorFn.name; + } + + return name; +} + +/** + * ### .getConstructorName(errorLike) + * + * Gets the constructor name for an Error instance or constructor itself. + * + * @name getConstructorName + * @param {Error|ErrorConstructor} errorLike + * @namespace Utils + * @api public + */ + +function getConstructorName(errorLike) { + var constructorName = errorLike; + if (errorLike instanceof Error) { + constructorName = getFunctionName(errorLike.constructor); + } else if (typeof errorLike === 'function') { + // If `err` is not an instance of Error it is an error constructor itself or another function. + // If we've got a common function we get its name, otherwise we may need to create a new instance + // of the error just in case it's a poorly-constructed error. Please see chaijs/chai/issues/45 to know more. + constructorName = getFunctionName(errorLike).trim() || + getFunctionName(new errorLike()); // eslint-disable-line new-cap + } + + return constructorName; +} + +/** + * ### .getMessage(errorLike) + * + * Gets the error message from an error. + * If `err` is a String itself, we return it. + * If the error has no message, we return an empty string. + * + * @name getMessage + * @param {Error|String} errorLike + * @namespace Utils + * @api public + */ + +function getMessage(errorLike) { + var msg = ''; + if (errorLike && errorLike.message) { + msg = errorLike.message; + } else if (typeof errorLike === 'string') { + msg = errorLike; + } + + return msg; +} + +module.exports = { + compatibleInstance: compatibleInstance, + compatibleConstructor: compatibleConstructor, + compatibleMessage: compatibleMessage, + getMessage: getMessage, + getConstructorName: getConstructorName, +}; + +},{}],35:[function(require,module,exports){ +'use strict'; +/* globals Symbol: false, Uint8Array: false, WeakMap: false */ +/*! + * deep-eql + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +var type = require('type-detect'); +function FakeMap() { + this._key = 'chai/deep-eql__' + Math.random() + Date.now(); +} + +FakeMap.prototype = { + get: function getMap(key) { + return key[this._key]; + }, + set: function setMap(key, value) { + if (Object.isExtensible(key)) { + Object.defineProperty(key, this._key, { + value: value, + configurable: true, + }); + } + }, +}; + +var MemoizeMap = typeof WeakMap === 'function' ? WeakMap : FakeMap; +/*! + * Check to see if the MemoizeMap has recorded a result of the two operands + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @returns {Boolean|null} result +*/ +function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return null; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + var result = leftHandMap.get(rightHandOperand); + if (typeof result === 'boolean') { + return result; + } + } + return null; +} + +/*! + * Set the result of the equality into the MemoizeMap + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @param {Boolean} result +*/ +function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + leftHandMap.set(rightHandOperand, result); + } else { + leftHandMap = new MemoizeMap(); + leftHandMap.set(rightHandOperand, result); + memoizeMap.set(leftHandOperand, leftHandMap); + } +} + +/*! + * Primary Export + */ + +module.exports = deepEqual; +module.exports.MemoizeMap = MemoizeMap; + +/** + * Assert deeply nested sameValue equality between two objects of any type. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match + */ +function deepEqual(leftHandOperand, rightHandOperand, options) { + // If we have a comparator, we can't assume anything; so bail to its check first. + if (options && options.comparator) { + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); + } + + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + return simpleResult; + } + + // Deeper comparisons are pushed through to a larger function + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); +} + +/** + * Many comparisons can be canceled out early via simple equality or primitive checks. + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @return {Boolean|null} equal match + */ +function simpleEqual(leftHandOperand, rightHandOperand) { + // Equal references (except for Numbers) can be returned early + if (leftHandOperand === rightHandOperand) { + // Handle +-0 cases + return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; + } + + // handle NaN cases + if ( + leftHandOperand !== leftHandOperand && // eslint-disable-line no-self-compare + rightHandOperand !== rightHandOperand // eslint-disable-line no-self-compare + ) { + return true; + } + + // Anything that is not an 'object', i.e. symbols, functions, booleans, numbers, + // strings, and undefined, can be compared by reference. + if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + // Easy out b/c it would have passed the first equality check + return false; + } + return null; +} + +/*! + * The main logic of the `deepEqual` function. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match +*/ +function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { + options = options || {}; + options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); + var comparator = options && options.comparator; + + // Check if a memoized result exists. + var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); + if (memoizeResultLeft !== null) { + return memoizeResultLeft; + } + var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); + if (memoizeResultRight !== null) { + return memoizeResultRight; + } + + // If a comparator is present, use it. + if (comparator) { + var comparatorResult = comparator(leftHandOperand, rightHandOperand); + // Comparators may return null, in which case we want to go back to default behavior. + if (comparatorResult === false || comparatorResult === true) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); + return comparatorResult; + } + // To allow comparators to override *any* behavior, we ran them first. Since it didn't decide + // what to do, we need to make sure to return the basic tests first before we move on. + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + // Don't memoize this, it takes longer to set/retrieve than to just compare. + return simpleResult; + } + } + + var leftHandType = type(leftHandOperand); + if (leftHandType !== type(rightHandOperand)) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); + return false; + } + + // Temporarily set the operands in the memoize object to prevent blowing the stack + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); + + var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); + return result; +} + +function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { + switch (leftHandType) { + case 'String': + case 'Number': + case 'Boolean': + case 'Date': + // If these types are their instance types (e.g. `new Number`) then re-deepEqual against their values + return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); + case 'Promise': + case 'Symbol': + case 'function': + case 'WeakMap': + case 'WeakSet': + case 'Error': + return leftHandOperand === rightHandOperand; + case 'Arguments': + case 'Int8Array': + case 'Uint8Array': + case 'Uint8ClampedArray': + case 'Int16Array': + case 'Uint16Array': + case 'Int32Array': + case 'Uint32Array': + case 'Float32Array': + case 'Float64Array': + case 'Array': + return iterableEqual(leftHandOperand, rightHandOperand, options); + case 'RegExp': + return regexpEqual(leftHandOperand, rightHandOperand); + case 'Generator': + return generatorEqual(leftHandOperand, rightHandOperand, options); + case 'DataView': + return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); + case 'ArrayBuffer': + return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); + case 'Set': + return entriesEqual(leftHandOperand, rightHandOperand, options); + case 'Map': + return entriesEqual(leftHandOperand, rightHandOperand, options); + default: + return objectEqual(leftHandOperand, rightHandOperand, options); + } +} + +/*! + * Compare two Regular Expressions for equality. + * + * @param {RegExp} leftHandOperand + * @param {RegExp} rightHandOperand + * @return {Boolean} result + */ + +function regexpEqual(leftHandOperand, rightHandOperand) { + return leftHandOperand.toString() === rightHandOperand.toString(); +} + +/*! + * Compare two Sets/Maps for equality. Faster than other equality functions. + * + * @param {Set} leftHandOperand + * @param {Set} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function entriesEqual(leftHandOperand, rightHandOperand, options) { + // IE11 doesn't support Set#entries or Set#@@iterator, so we need manually populate using Set#forEach + if (leftHandOperand.size !== rightHandOperand.size) { + return false; + } + if (leftHandOperand.size === 0) { + return true; + } + var leftHandItems = []; + var rightHandItems = []; + leftHandOperand.forEach(function gatherEntries(key, value) { + leftHandItems.push([ key, value ]); + }); + rightHandOperand.forEach(function gatherEntries(key, value) { + rightHandItems.push([ key, value ]); + }); + return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); +} + +/*! + * Simple equality for flat iterable objects such as Arrays, TypedArrays or Node.js buffers. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function iterableEqual(leftHandOperand, rightHandOperand, options) { + var length = leftHandOperand.length; + if (length !== rightHandOperand.length) { + return false; + } + if (length === 0) { + return true; + } + var index = -1; + while (++index < length) { + if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { + return false; + } + } + return true; +} + +/*! + * Simple equality for generator objects such as those returned by generator functions. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function generatorEqual(leftHandOperand, rightHandOperand, options) { + return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); +} + +/*! + * Determine if the given object has an @@iterator function. + * + * @param {Object} target + * @return {Boolean} `true` if the object has an @@iterator function. + */ +function hasIteratorFunction(target) { + return typeof Symbol !== 'undefined' && + typeof target === 'object' && + typeof Symbol.iterator !== 'undefined' && + typeof target[Symbol.iterator] === 'function'; +} + +/*! + * Gets all iterator entries from the given Object. If the Object has no @@iterator function, returns an empty array. + * This will consume the iterator - which could have side effects depending on the @@iterator implementation. + * + * @param {Object} target + * @returns {Array} an array of entries from the @@iterator function + */ +function getIteratorEntries(target) { + if (hasIteratorFunction(target)) { + try { + return getGeneratorEntries(target[Symbol.iterator]()); + } catch (iteratorError) { + return []; + } + } + return []; +} + +/*! + * Gets all entries from a Generator. This will consume the generator - which could have side effects. + * + * @param {Generator} target + * @returns {Array} an array of entries from the Generator. + */ +function getGeneratorEntries(generator) { + var generatorResult = generator.next(); + var accumulator = [ generatorResult.value ]; + while (generatorResult.done === false) { + generatorResult = generator.next(); + accumulator.push(generatorResult.value); + } + return accumulator; +} + +/*! + * Gets all own and inherited enumerable keys from a target. + * + * @param {Object} target + * @returns {Array} an array of own and inherited enumerable keys from the target. + */ +function getEnumerableKeys(target) { + var keys = []; + for (var key in target) { + keys.push(key); + } + return keys; +} + +/*! + * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of + * each key. If any value of the given key is not equal, the function will return false (early). + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Array} keys An array of keys to compare the values of leftHandOperand and rightHandOperand against + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ +function keysEqual(leftHandOperand, rightHandOperand, keys, options) { + var length = keys.length; + if (length === 0) { + return true; + } + for (var i = 0; i < length; i += 1) { + if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { + return false; + } + } + return true; +} + +/*! + * Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual` + * for each enumerable key in the object. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function objectEqual(leftHandOperand, rightHandOperand, options) { + var leftHandKeys = getEnumerableKeys(leftHandOperand); + var rightHandKeys = getEnumerableKeys(rightHandOperand); + if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { + leftHandKeys.sort(); + rightHandKeys.sort(); + if (iterableEqual(leftHandKeys, rightHandKeys) === false) { + return false; + } + return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); + } + + var leftHandEntries = getIteratorEntries(leftHandOperand); + var rightHandEntries = getIteratorEntries(rightHandOperand); + if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { + leftHandEntries.sort(); + rightHandEntries.sort(); + return iterableEqual(leftHandEntries, rightHandEntries, options); + } + + if (leftHandKeys.length === 0 && + leftHandEntries.length === 0 && + rightHandKeys.length === 0 && + rightHandEntries.length === 0) { + return true; + } + + return false; +} + +/*! + * Returns true if the argument is a primitive. + * + * This intentionally returns true for all objects that can be compared by reference, + * including functions and symbols. + * + * @param {Mixed} value + * @return {Boolean} result + */ +function isPrimitive(value) { + return value === null || typeof value !== 'object'; +} + +},{"type-detect":38}],36:[function(require,module,exports){ +'use strict'; + +/* ! + * Chai - getFuncName utility + * Copyright(c) 2012-2016 Jake Luer + * MIT Licensed + */ + +/** + * ### .getFuncName(constructorFn) + * + * Returns the name of a function. + * When a non-function instance is passed, returns `null`. + * This also includes a polyfill function if `aFunc.name` is not defined. + * + * @name getFuncName + * @param {Function} funct + * @namespace Utils + * @api public + */ + +var toString = Function.prototype.toString; +var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\s\(\/]+)/; +function getFuncName(aFunc) { + if (typeof aFunc !== 'function') { + return null; + } + + var name = ''; + if (typeof Function.prototype.name === 'undefined' && typeof aFunc.name === 'undefined') { + // Here we run a polyfill if Function does not support the `name` property and if aFunc.name is not defined + var match = toString.call(aFunc).match(functionNameMatch); + if (match) { + name = match[1]; + } + } else { + // If we've got a `name` property we just use it + name = aFunc.name; + } + + return name; +} + +module.exports = getFuncName; + +},{}],37:[function(require,module,exports){ +'use strict'; + +/* ! + * Chai - pathval utility + * Copyright(c) 2012-2014 Jake Luer + * @see https://github.com/logicalparadox/filtr + * MIT Licensed + */ + +/** + * ### .hasProperty(object, name) + * + * This allows checking whether an object has own + * or inherited from prototype chain named property. + * + * Basically does the same thing as the `in` + * operator but works properly with null/undefined values + * and other primitives. + * + * var obj = { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * + * The following would be the results. + * + * hasProperty(obj, 'str'); // true + * hasProperty(obj, 'constructor'); // true + * hasProperty(obj, 'bar'); // false + * + * hasProperty(obj.str, 'length'); // true + * hasProperty(obj.str, 1); // true + * hasProperty(obj.str, 5); // false + * + * hasProperty(obj.arr, 'length'); // true + * hasProperty(obj.arr, 2); // true + * hasProperty(obj.arr, 3); // false + * + * @param {Object} object + * @param {String|Symbol} name + * @returns {Boolean} whether it exists + * @namespace Utils + * @name hasProperty + * @api public + */ + +function hasProperty(obj, name) { + if (typeof obj === 'undefined' || obj === null) { + return false; + } + + // The `in` operator does not work with primitives. + return name in Object(obj); +} + +/* ! + * ## parsePath(path) + * + * Helper function used to parse string object + * paths. Use in conjunction with `internalGetPathValue`. + * + * var parsed = parsePath('myobject.property.subprop'); + * + * ### Paths: + * + * * Can be infinitely deep and nested. + * * Arrays are also valid using the formal `myobject.document[3].property`. + * * Literal dots and brackets (not delimiter) must be backslash-escaped. + * + * @param {String} path + * @returns {Object} parsed + * @api private + */ + +function parsePath(path) { + var str = path.replace(/([^\\])\[/g, '$1.['); + var parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function mapMatches(value) { + var regexp = /^\[(\d+)\]$/; + var mArr = regexp.exec(value); + var parsed = null; + if (mArr) { + parsed = { i: parseFloat(mArr[1]) }; + } else { + parsed = { p: value.replace(/\\([.\[\]])/g, '$1') }; + } + + return parsed; + }); +} + +/* ! + * ## internalGetPathValue(obj, parsed[, pathDepth]) + * + * Helper companion function for `.parsePath` that returns + * the value located at the parsed address. + * + * var value = getPathValue(obj, parsed); + * + * @param {Object} object to search against + * @param {Object} parsed definition from `parsePath`. + * @param {Number} depth (nesting level) of the property we want to retrieve + * @returns {Object|Undefined} value + * @api private + */ + +function internalGetPathValue(obj, parsed, pathDepth) { + var temporaryValue = obj; + var res = null; + pathDepth = (typeof pathDepth === 'undefined' ? parsed.length : pathDepth); + + for (var i = 0; i < pathDepth; i++) { + var part = parsed[i]; + if (temporaryValue) { + if (typeof part.p === 'undefined') { + temporaryValue = temporaryValue[part.i]; + } else { + temporaryValue = temporaryValue[part.p]; + } + + if (i === (pathDepth - 1)) { + res = temporaryValue; + } + } + } + + return res; +} + +/* ! + * ## internalSetPathValue(obj, value, parsed) + * + * Companion function for `parsePath` that sets + * the value located at a parsed address. + * + * internalSetPathValue(obj, 'value', parsed); + * + * @param {Object} object to search and define on + * @param {*} value to use upon set + * @param {Object} parsed definition from `parsePath` + * @api private + */ + +function internalSetPathValue(obj, val, parsed) { + var tempObj = obj; + var pathDepth = parsed.length; + var part = null; + // Here we iterate through every part of the path + for (var i = 0; i < pathDepth; i++) { + var propName = null; + var propVal = null; + part = parsed[i]; + + // If it's the last part of the path, we set the 'propName' value with the property name + if (i === (pathDepth - 1)) { + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Now we set the property with the name held by 'propName' on object with the desired val + tempObj[propName] = val; + } else if (typeof part.p !== 'undefined' && tempObj[part.p]) { + tempObj = tempObj[part.p]; + } else if (typeof part.i !== 'undefined' && tempObj[part.i]) { + tempObj = tempObj[part.i]; + } else { + // If the obj doesn't have the property we create one with that name to define it + var next = parsed[i + 1]; + // Here we set the name of the property which will be defined + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Here we decide if this property will be an array or a new object + propVal = typeof next.p === 'undefined' ? [] : {}; + tempObj[propName] = propVal; + tempObj = tempObj[propName]; + } + } +} + +/** + * ### .getPathInfo(object, path) + * + * This allows the retrieval of property info in an + * object given a string path. + * + * The path info consists of an object with the + * following properties: + * + * * parent - The parent object of the property referenced by `path` + * * name - The name of the final property, a number if it was an array indexer + * * value - The value of the property, if it exists, otherwise `undefined` + * * exists - Whether the property exists or not + * + * @param {Object} object + * @param {String} path + * @returns {Object} info + * @namespace Utils + * @name getPathInfo + * @api public + */ + +function getPathInfo(obj, path) { + var parsed = parsePath(path); + var last = parsed[parsed.length - 1]; + var info = { + parent: parsed.length > 1 ? internalGetPathValue(obj, parsed, parsed.length - 1) : obj, + name: last.p || last.i, + value: internalGetPathValue(obj, parsed), + }; + info.exists = hasProperty(info.parent, info.name); + + return info; +} + +/** + * ### .getPathValue(object, path) + * + * This allows the retrieval of values in an + * object given a string path. + * + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * } + * + * The following would be the results. + * + * getPathValue(obj, 'prop1.str'); // Hello + * getPathValue(obj, 'prop1.att[2]'); // b + * getPathValue(obj, 'prop2.arr[0].nested'); // Universe + * + * @param {Object} object + * @param {String} path + * @returns {Object} value or `undefined` + * @namespace Utils + * @name getPathValue + * @api public + */ + +function getPathValue(obj, path) { + var info = getPathInfo(obj, path); + return info.value; +} + +/** + * ### .setPathValue(object, path, value) + * + * Define the value in an object at a given string path. + * + * ```js + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * }; + * ``` + * + * The following would be acceptable. + * + * ```js + * var properties = require('tea-properties'); + * properties.set(obj, 'prop1.str', 'Hello Universe!'); + * properties.set(obj, 'prop1.arr[2]', 'B'); + * properties.set(obj, 'prop2.arr[0].nested.value', { hello: 'universe' }); + * ``` + * + * @param {Object} object + * @param {String} path + * @param {Mixed} value + * @api private + */ + +function setPathValue(obj, path, val) { + var parsed = parsePath(path); + internalSetPathValue(obj, val, parsed); + return obj; +} + +module.exports = { + hasProperty: hasProperty, + getPathInfo: getPathInfo, + getPathValue: getPathValue, + setPathValue: setPathValue, +}; + +},{}],38:[function(require,module,exports){ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.typeDetect = factory()); +}(this, (function () { 'use strict'; + +/* ! + * type-detect + * Copyright(c) 2013 jake luer + * MIT Licensed + */ +var promiseExists = typeof Promise === 'function'; + +/* eslint-disable no-undef */ +var globalObject = typeof self === 'object' ? self : global; // eslint-disable-line id-blacklist + +var symbolExists = typeof Symbol !== 'undefined'; +var mapExists = typeof Map !== 'undefined'; +var setExists = typeof Set !== 'undefined'; +var weakMapExists = typeof WeakMap !== 'undefined'; +var weakSetExists = typeof WeakSet !== 'undefined'; +var dataViewExists = typeof DataView !== 'undefined'; +var symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; +var symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; +var setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; +var mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; +var setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries()); +var mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries()); +var arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +var arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); +var stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function'; +var stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); +var toStringLeftSliceLength = 8; +var toStringRightSliceLength = -1; +/** + * ### typeOf (obj) + * + * Uses `Object.prototype.toString` to determine the type of an object, + * normalising behaviour across engine versions & well optimised. + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ +function typeDetect(obj) { + /* ! Speed optimisation + * Pre: + * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) + * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) + * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) + * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) + * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) + * Post: + * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) + * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) + * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) + * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) + * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) + */ + var typeofObj = typeof obj; + if (typeofObj !== 'object') { + return typeofObj; + } + + /* ! Speed optimisation + * Pre: + * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) + * Post: + * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) + */ + if (obj === null) { + return 'null'; + } + + /* ! Spec Conformance + * Test: `Object.prototype.toString.call(window)`` + * - Node === "[object global]" + * - Chrome === "[object global]" + * - Firefox === "[object Window]" + * - PhantomJS === "[object Window]" + * - Safari === "[object Window]" + * - IE 11 === "[object Window]" + * - IE Edge === "[object Window]" + * Test: `Object.prototype.toString.call(this)`` + * - Chrome Worker === "[object global]" + * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" + * - Safari Worker === "[object DedicatedWorkerGlobalScope]" + * - IE 11 Worker === "[object WorkerGlobalScope]" + * - IE Edge Worker === "[object WorkerGlobalScope]" + */ + if (obj === globalObject) { + return 'global'; + } + + /* ! Speed optimisation + * Pre: + * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) + * Post: + * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) + */ + if ( + Array.isArray(obj) && + (symbolToStringTagExists === false || !(Symbol.toStringTag in obj)) + ) { + return 'Array'; + } + + // Not caching existence of `window` and related properties due to potential + // for `window` to be unset before tests in quasi-browser environments. + if (typeof window === 'object' && window !== null) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/browsers.html#location) + * WhatWG HTML$7.7.3 - The `Location` interface + * Test: `Object.prototype.toString.call(window.location)`` + * - IE <=11 === "[object Object]" + * - IE Edge <=13 === "[object Object]" + */ + if (typeof window.location === 'object' && obj === window.location) { + return 'Location'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#document) + * WhatWG HTML$3.1.1 - The `Document` object + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * WhatWG HTML states: + * > For historical reasons, Window objects must also have a + * > writable, configurable, non-enumerable property named + * > HTMLDocument whose value is the Document interface object. + * Test: `Object.prototype.toString.call(document)`` + * - Chrome === "[object HTMLDocument]" + * - Firefox === "[object HTMLDocument]" + * - Safari === "[object HTMLDocument]" + * - IE <=10 === "[object Document]" + * - IE 11 === "[object HTMLDocument]" + * - IE Edge <=13 === "[object HTMLDocument]" + */ + if (typeof window.document === 'object' && obj === window.document) { + return 'Document'; + } + + if (typeof window.navigator === 'object') { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray + * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` + * - IE <=10 === "[object MSMimeTypesCollection]" + */ + if (typeof window.navigator.mimeTypes === 'object' && + obj === window.navigator.mimeTypes) { + return 'MimeTypeArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray + * Test: `Object.prototype.toString.call(navigator.plugins)`` + * - IE <=10 === "[object MSPluginsCollection]" + */ + if (typeof window.navigator.plugins === 'object' && + obj === window.navigator.plugins) { + return 'PluginArray'; + } + } + + if ((typeof window.HTMLElement === 'function' || + typeof window.HTMLElement === 'object') && + obj instanceof window.HTMLElement) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` + * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` + * - IE <=10 === "[object HTMLBlockElement]" + */ + if (obj.tagName === 'BLOCKQUOTE') { + return 'HTMLQuoteElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltabledatacellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('td')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TD') { + return 'HTMLTableDataCellElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltableheadercellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('th')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TH') { + return 'HTMLTableHeaderCellElement'; + } + } + } + + /* ! Speed optimisation + * Pre: + * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) + * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) + * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) + * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) + * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) + * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) + * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) + * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) + * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) + * Post: + * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) + * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) + * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) + * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) + * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) + * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) + * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) + * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) + * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) + */ + var stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); + if (typeof stringTag === 'string') { + return stringTag; + } + + var objPrototype = Object.getPrototypeOf(obj); + /* ! Speed optimisation + * Pre: + * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) + * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) + * Post: + * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) + * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) + */ + if (objPrototype === RegExp.prototype) { + return 'RegExp'; + } + + /* ! Speed optimisation + * Pre: + * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) + * Post: + * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) + */ + if (objPrototype === Date.prototype) { + return 'Date'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) + * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": + * Test: `Object.prototype.toString.call(Promise.resolve())`` + * - Chrome <=47 === "[object Object]" + * - Edge <=20 === "[object Object]" + * - Firefox 29-Latest === "[object Promise]" + * - Safari 7.1-Latest === "[object Promise]" + */ + if (promiseExists && objPrototype === Promise.prototype) { + return 'Promise'; + } + + /* ! Speed optimisation + * Pre: + * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) + * Post: + * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) + */ + if (setExists && objPrototype === Set.prototype) { + return 'Set'; + } + + /* ! Speed optimisation + * Pre: + * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) + * Post: + * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) + */ + if (mapExists && objPrototype === Map.prototype) { + return 'Map'; + } + + /* ! Speed optimisation + * Pre: + * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) + * Post: + * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) + */ + if (weakSetExists && objPrototype === WeakSet.prototype) { + return 'WeakSet'; + } + + /* ! Speed optimisation + * Pre: + * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) + * Post: + * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) + */ + if (weakMapExists && objPrototype === WeakMap.prototype) { + return 'WeakMap'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) + * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": + * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` + * - Edge <=13 === "[object Object]" + */ + if (dataViewExists && objPrototype === DataView.prototype) { + return 'DataView'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) + * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": + * Test: `Object.prototype.toString.call(new Map().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (mapExists && objPrototype === mapIteratorPrototype) { + return 'Map Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) + * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": + * Test: `Object.prototype.toString.call(new Set().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (setExists && objPrototype === setIteratorPrototype) { + return 'Set Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) + * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": + * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { + return 'Array Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) + * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": + * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (stringIteratorExists && objPrototype === stringIteratorPrototype) { + return 'String Iterator'; + } + + /* ! Speed optimisation + * Pre: + * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) + * Post: + * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) + */ + if (objPrototype === null) { + return 'Object'; + } + + return Object + .prototype + .toString + .call(obj) + .slice(toStringLeftSliceLength, toStringRightSliceLength); +} + +return typeDetect; + +}))); + +},{}]},{},[1])(1) +}); diff --git a/WebServer/node_modules/chai/index.js b/WebServer/node_modules/chai/index.js new file mode 100644 index 000000000..634483b02 --- /dev/null +++ b/WebServer/node_modules/chai/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/chai'); diff --git a/WebServer/node_modules/chai/karma.conf.js b/WebServer/node_modules/chai/karma.conf.js new file mode 100644 index 000000000..3dadb55c0 --- /dev/null +++ b/WebServer/node_modules/chai/karma.conf.js @@ -0,0 +1,28 @@ +module.exports = function(config) { + config.set({ + frameworks: [ 'mocha' ] + , files: [ + 'chai.js' + , 'test/bootstrap/index.js' + , 'test/*.js' + ] + , reporters: [ 'progress' ] + , colors: true + , logLevel: config.LOG_INFO + , autoWatch: false + , browsers: [ 'PhantomJS' ] + , browserDisconnectTimeout: 10000 + , browserDisconnectTolerance: 2 + , browserNoActivityTimeout: 20000 + , singleRun: true + }); + + switch (process.env.CHAI_TEST_ENV) { + case 'sauce': + require('./karma.sauce')(config); + break; + default: + // ... + break; + }; +}; diff --git a/WebServer/node_modules/chai/karma.sauce.js b/WebServer/node_modules/chai/karma.sauce.js new file mode 100644 index 000000000..3d65b3e7d --- /dev/null +++ b/WebServer/node_modules/chai/karma.sauce.js @@ -0,0 +1,41 @@ +var version = require('./package.json').version; +var ts = new Date().getTime(); + +module.exports = function(config) { + var auth; + + try { + auth = require('./test/auth/index'); + } catch(ex) { + auth = {}; + auth.SAUCE_USERNAME = process.env.SAUCE_USERNAME || null; + auth.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY || null; + } + + if (!auth.SAUCE_USERNAME || !auth.SAUCE_ACCESS_KEY) return; + if (process.env.SKIP_SAUCE) return; + + var branch = process.env.TRAVIS_BRANCH || 'local' + var browserConfig = require('./sauce.browsers'); + var browsers = Object.keys(browserConfig); + var tags = [ 'chaijs_' + version, auth.SAUCE_USERNAME + '@' + branch ]; + var tunnel = process.env.TRAVIS_JOB_NUMBER || ts; + + if (process.env.TRAVIS_JOB_NUMBER) { + tags.push('travis@' + process.env.TRAVIS_JOB_NUMBER); + } + + config.browsers = config.browsers.concat(browsers); + config.customLaunchers = browserConfig; + config.reporters.push('saucelabs'); + config.captureTimeout = 300000; + + config.sauceLabs = { + username: auth.SAUCE_USERNAME + , accessKey: auth.SAUCE_ACCESS_KEY + , startConnect: ('TRAVIS' in process.env) === false + , tags: tags + , testName: 'ChaiJS' + , tunnelIdentifier: tunnel + }; +}; diff --git a/WebServer/node_modules/chai/lib/chai.js b/WebServer/node_modules/chai/lib/chai.js new file mode 100644 index 000000000..efda439e8 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai.js @@ -0,0 +1,92 @@ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var used = []; + +/*! + * Chai version + */ + +exports.version = '4.2.0'; + +/*! + * Assertion Error + */ + +exports.AssertionError = require('assertion-error'); + +/*! + * Utils for plugins (not exported) + */ + +var util = require('./chai/utils'); + +/** + * # .use(function) + * + * Provides a way to extend the internals of Chai. + * + * @param {Function} + * @returns {this} for chaining + * @api public + */ + +exports.use = function (fn) { + if (!~used.indexOf(fn)) { + fn(exports, util); + used.push(fn); + } + + return exports; +}; + +/*! + * Utility Functions + */ + +exports.util = util; + +/*! + * Configuration + */ + +var config = require('./chai/config'); +exports.config = config; + +/*! + * Primary `Assertion` prototype + */ + +var assertion = require('./chai/assertion'); +exports.use(assertion); + +/*! + * Core Assertions + */ + +var core = require('./chai/core/assertions'); +exports.use(core); + +/*! + * Expect interface + */ + +var expect = require('./chai/interface/expect'); +exports.use(expect); + +/*! + * Should interface + */ + +var should = require('./chai/interface/should'); +exports.use(should); + +/*! + * Assert interface + */ + +var assert = require('./chai/interface/assert'); +exports.use(assert); diff --git a/WebServer/node_modules/chai/lib/chai/assertion.js b/WebServer/node_modules/chai/lib/chai/assertion.js new file mode 100644 index 000000000..4d4217fb4 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/assertion.js @@ -0,0 +1,165 @@ +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var config = require('./config'); + +module.exports = function (_chai, util) { + /*! + * Module dependencies. + */ + + var AssertionError = _chai.AssertionError + , flag = util.flag; + + /*! + * Module export. + */ + + _chai.Assertion = Assertion; + + /*! + * Assertion Constructor + * + * Creates object for chaining. + * + * `Assertion` objects contain metadata in the form of flags. Three flags can + * be assigned during instantiation by passing arguments to this constructor: + * + * - `object`: This flag contains the target of the assertion. For example, in + * the assertion `expect(numKittens).to.equal(7);`, the `object` flag will + * contain `numKittens` so that the `equal` assertion can reference it when + * needed. + * + * - `message`: This flag contains an optional custom error message to be + * prepended to the error message that's generated by the assertion when it + * fails. + * + * - `ssfi`: This flag stands for "start stack function indicator". It + * contains a function reference that serves as the starting point for + * removing frames from the stack trace of the error that's created by the + * assertion when it fails. The goal is to provide a cleaner stack trace to + * end users by removing Chai's internal functions. Note that it only works + * in environments that support `Error.captureStackTrace`, and only when + * `Chai.config.includeStack` hasn't been set to `false`. + * + * - `lockSsfi`: This flag controls whether or not the given `ssfi` flag + * should retain its current value, even as assertions are chained off of + * this object. This is usually set to `true` when creating a new assertion + * from within another assertion. It's also temporarily set to `true` before + * an overwritten assertion gets called by the overwriting assertion. + * + * @param {Mixed} obj target of the assertion + * @param {String} msg (optional) custom error message + * @param {Function} ssfi (optional) starting point for removing stack frames + * @param {Boolean} lockSsfi (optional) whether or not the ssfi flag is locked + * @api private + */ + + function Assertion (obj, msg, ssfi, lockSsfi) { + flag(this, 'ssfi', ssfi || Assertion); + flag(this, 'lockSsfi', lockSsfi); + flag(this, 'object', obj); + flag(this, 'message', msg); + + return util.proxify(this); + } + + Object.defineProperty(Assertion, 'includeStack', { + get: function() { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + return config.includeStack; + }, + set: function(value) { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + config.includeStack = value; + } + }); + + Object.defineProperty(Assertion, 'showDiff', { + get: function() { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + return config.showDiff; + }, + set: function(value) { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + config.showDiff = value; + } + }); + + Assertion.addProperty = function (name, fn) { + util.addProperty(this.prototype, name, fn); + }; + + Assertion.addMethod = function (name, fn) { + util.addMethod(this.prototype, name, fn); + }; + + Assertion.addChainableMethod = function (name, fn, chainingBehavior) { + util.addChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + Assertion.overwriteProperty = function (name, fn) { + util.overwriteProperty(this.prototype, name, fn); + }; + + Assertion.overwriteMethod = function (name, fn) { + util.overwriteMethod(this.prototype, name, fn); + }; + + Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { + util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + /** + * ### .assert(expression, message, negateMessage, expected, actual, showDiff) + * + * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. + * + * @name assert + * @param {Philosophical} expression to be tested + * @param {String|Function} message or function that returns message to display if expression fails + * @param {String|Function} negatedMessage or function that returns negatedMessage to display if negated expression fails + * @param {Mixed} expected value (remember to check for negation) + * @param {Mixed} actual (optional) will default to `this.obj` + * @param {Boolean} showDiff (optional) when set to `true`, assert will display a diff in addition to the message if expression fails + * @api private + */ + + Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { + var ok = util.test(this, arguments); + if (false !== showDiff) showDiff = true; + if (undefined === expected && undefined === _actual) showDiff = false; + if (true !== config.showDiff) showDiff = false; + + if (!ok) { + msg = util.getMessage(this, arguments); + var actual = util.getActual(this, arguments); + throw new AssertionError(msg, { + actual: actual + , expected: expected + , showDiff: showDiff + }, (config.includeStack) ? this.assert : flag(this, 'ssfi')); + } + }; + + /*! + * ### ._obj + * + * Quick reference to stored `actual` value for plugin developers. + * + * @api private + */ + + Object.defineProperty(Assertion.prototype, '_obj', + { get: function () { + return flag(this, 'object'); + } + , set: function (val) { + flag(this, 'object', val); + } + }); +}; diff --git a/WebServer/node_modules/chai/lib/chai/config.js b/WebServer/node_modules/chai/lib/chai/config.js new file mode 100644 index 000000000..412a0c806 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/config.js @@ -0,0 +1,94 @@ +module.exports = { + + /** + * ### config.includeStack + * + * User configurable property, influences whether stack trace + * is included in Assertion error message. Default of false + * suppresses stack trace in the error message. + * + * chai.config.includeStack = true; // enable stack on error + * + * @param {Boolean} + * @api public + */ + + includeStack: false, + + /** + * ### config.showDiff + * + * User configurable property, influences whether or not + * the `showDiff` flag should be included in the thrown + * AssertionErrors. `false` will always be `false`; `true` + * will be true when the assertion has requested a diff + * be shown. + * + * @param {Boolean} + * @api public + */ + + showDiff: true, + + /** + * ### config.truncateThreshold + * + * User configurable property, sets length threshold for actual and + * expected values in assertion errors. If this threshold is exceeded, for + * example for large data structures, the value is replaced with something + * like `[ Array(3) ]` or `{ Object (prop1, prop2) }`. + * + * Set it to zero if you want to disable truncating altogether. + * + * This is especially userful when doing assertions on arrays: having this + * set to a reasonable large value makes the failure messages readily + * inspectable. + * + * chai.config.truncateThreshold = 0; // disable truncating + * + * @param {Number} + * @api public + */ + + truncateThreshold: 40, + + /** + * ### config.useProxy + * + * User configurable property, defines if chai will use a Proxy to throw + * an error when a non-existent property is read, which protects users + * from typos when using property-based assertions. + * + * Set it to false if you want to disable this feature. + * + * chai.config.useProxy = false; // disable use of Proxy + * + * This feature is automatically disabled regardless of this config value + * in environments that don't support proxies. + * + * @param {Boolean} + * @api public + */ + + useProxy: true, + + /** + * ### config.proxyExcludedKeys + * + * User configurable property, defines which properties should be ignored + * instead of throwing an error if they do not exist on the assertion. + * This is only applied if the environment Chai is running in supports proxies and + * if the `useProxy` configuration setting is enabled. + * By default, `then` and `inspect` will not throw an error if they do not exist on the + * assertion object because the `.inspect` property is read by `util.inspect` (for example, when + * using `console.log` on the assertion object) and `.then` is necessary for promise type-checking. + * + * // By default these keys will not throw an error if they do not exist on the assertion object + * chai.config.proxyExcludedKeys = ['then', 'inspect']; + * + * @param {Array} + * @api public + */ + + proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON'] +}; diff --git a/WebServer/node_modules/chai/lib/chai/core/assertions.js b/WebServer/node_modules/chai/lib/chai/core/assertions.js new file mode 100644 index 000000000..cabcf622f --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/core/assertions.js @@ -0,0 +1,3804 @@ +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, _) { + var Assertion = chai.Assertion + , AssertionError = chai.AssertionError + , flag = _.flag; + + /** + * ### Language Chains + * + * The following are provided as chainable getters to improve the readability + * of your assertions. + * + * **Chains** + * + * - to + * - be + * - been + * - is + * - that + * - which + * - and + * - has + * - have + * - with + * - at + * - of + * - same + * - but + * - does + * - still + * + * @name language chains + * @namespace BDD + * @api public + */ + + [ 'to', 'be', 'been', 'is' + , 'and', 'has', 'have', 'with' + , 'that', 'which', 'at', 'of' + , 'same', 'but', 'does', 'still' ].forEach(function (chain) { + Assertion.addProperty(chain); + }); + + /** + * ### .not + * + * Negates all assertions that follow in the chain. + * + * expect(function () {}).to.not.throw(); + * expect({a: 1}).to.not.have.property('b'); + * expect([1, 2]).to.be.an('array').that.does.not.include(3); + * + * Just because you can negate any assertion with `.not` doesn't mean you + * should. With great power comes great responsibility. It's often best to + * assert that the one expected output was produced, rather than asserting + * that one of countless unexpected outputs wasn't produced. See individual + * assertions for specific guidance. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.equal(1); // Not recommended + * + * @name not + * @namespace BDD + * @api public + */ + + Assertion.addProperty('not', function () { + flag(this, 'negate', true); + }); + + /** + * ### .deep + * + * Causes all `.equal`, `.include`, `.members`, `.keys`, and `.property` + * assertions that follow in the chain to use deep equality instead of strict + * (`===`) equality. See the `deep-eql` project page for info on the deep + * equality algorithm: https://github.com/chaijs/deep-eql. + * + * // Target object deeply (but not strictly) equals `{a: 1}` + * expect({a: 1}).to.deep.equal({a: 1}); + * expect({a: 1}).to.not.equal({a: 1}); + * + * // Target array deeply (but not strictly) includes `{a: 1}` + * expect([{a: 1}]).to.deep.include({a: 1}); + * expect([{a: 1}]).to.not.include({a: 1}); + * + * // Target object deeply (but not strictly) includes `x: {a: 1}` + * expect({x: {a: 1}}).to.deep.include({x: {a: 1}}); + * expect({x: {a: 1}}).to.not.include({x: {a: 1}}); + * + * // Target array deeply (but not strictly) has member `{a: 1}` + * expect([{a: 1}]).to.have.deep.members([{a: 1}]); + * expect([{a: 1}]).to.not.have.members([{a: 1}]); + * + * // Target set deeply (but not strictly) has key `{a: 1}` + * expect(new Set([{a: 1}])).to.have.deep.keys([{a: 1}]); + * expect(new Set([{a: 1}])).to.not.have.keys([{a: 1}]); + * + * // Target object deeply (but not strictly) has property `x: {a: 1}` + * expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); + * expect({x: {a: 1}}).to.not.have.property('x', {a: 1}); + * + * @name deep + * @namespace BDD + * @api public + */ + + Assertion.addProperty('deep', function () { + flag(this, 'deep', true); + }); + + /** + * ### .nested + * + * Enables dot- and bracket-notation in all `.property` and `.include` + * assertions that follow in the chain. + * + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]'); + * expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'}); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 'x'}}).to.have.nested.property('\\.a.\\[b\\]'); + * expect({'.a': {'[b]': 'x'}}).to.nested.include({'\\.a.\\[b\\]': 'x'}); + * + * `.nested` cannot be combined with `.own`. + * + * @name nested + * @namespace BDD + * @api public + */ + + Assertion.addProperty('nested', function () { + flag(this, 'nested', true); + }); + + /** + * ### .own + * + * Causes all `.property` and `.include` assertions that follow in the chain + * to ignore inherited properties. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.have.own.property('a'); + * expect({a: 1}).to.have.property('b'); + * expect({a: 1}).to.not.have.own.property('b'); + * + * expect({a: 1}).to.own.include({a: 1}); + * expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2}); + * + * `.own` cannot be combined with `.nested`. + * + * @name own + * @namespace BDD + * @api public + */ + + Assertion.addProperty('own', function () { + flag(this, 'own', true); + }); + + /** + * ### .ordered + * + * Causes all `.members` assertions that follow in the chain to require that + * members be in the same order. + * + * expect([1, 2]).to.have.ordered.members([1, 2]) + * .but.not.have.ordered.members([2, 1]); + * + * When `.include` and `.ordered` are combined, the ordering begins at the + * start of both arrays. + * + * expect([1, 2, 3]).to.include.ordered.members([1, 2]) + * .but.not.include.ordered.members([2, 3]); + * + * @name ordered + * @namespace BDD + * @api public + */ + + Assertion.addProperty('ordered', function () { + flag(this, 'ordered', true); + }); + + /** + * ### .any + * + * Causes all `.keys` assertions that follow in the chain to only require that + * the target have at least one of the given keys. This is the opposite of + * `.all`, which requires that the target have all of the given keys. + * + * expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd'); + * + * See the `.keys` doc for guidance on when to use `.any` or `.all`. + * + * @name any + * @namespace BDD + * @api public + */ + + Assertion.addProperty('any', function () { + flag(this, 'any', true); + flag(this, 'all', false); + }); + + /** + * ### .all + * + * Causes all `.keys` assertions that follow in the chain to require that the + * target have all of the given keys. This is the opposite of `.any`, which + * only requires that the target have at least one of the given keys. + * + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * + * Note that `.all` is used by default when neither `.all` nor `.any` are + * added earlier in the chain. However, it's often best to add `.all` anyway + * because it improves readability. + * + * See the `.keys` doc for guidance on when to use `.any` or `.all`. + * + * @name all + * @namespace BDD + * @api public + */ + + Assertion.addProperty('all', function () { + flag(this, 'all', true); + flag(this, 'any', false); + }); + + /** + * ### .a(type[, msg]) + * + * Asserts that the target's type is equal to the given string `type`. Types + * are case insensitive. See the `type-detect` project page for info on the + * type detection algorithm: https://github.com/chaijs/type-detect. + * + * expect('foo').to.be.a('string'); + * expect({a: 1}).to.be.an('object'); + * expect(null).to.be.a('null'); + * expect(undefined).to.be.an('undefined'); + * expect(new Error).to.be.an('error'); + * expect(Promise.resolve()).to.be.a('promise'); + * expect(new Float32Array).to.be.a('float32array'); + * expect(Symbol()).to.be.a('symbol'); + * + * `.a` supports objects that have a custom type set via `Symbol.toStringTag`. + * + * var myObj = { + * [Symbol.toStringTag]: 'myCustomType' + * }; + * + * expect(myObj).to.be.a('myCustomType').but.not.an('object'); + * + * It's often best to use `.a` to check a target's type before making more + * assertions on the same target. That way, you avoid unexpected behavior from + * any assertion that does different things based on the target's type. + * + * expect([1, 2, 3]).to.be.an('array').that.includes(2); + * expect([]).to.be.an('array').that.is.empty; + * + * Add `.not` earlier in the chain to negate `.a`. However, it's often best to + * assert that the target is the expected type, rather than asserting that it + * isn't one of many unexpected types. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.an('array'); // Not recommended + * + * `.a` accepts an optional `msg` argument which is a custom error message to + * show when the assertion fails. The message can also be given as the second + * argument to `expect`. + * + * expect(1).to.be.a('string', 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.a('string'); + * + * `.a` can also be used as a language chain to improve the readability of + * your assertions. + * + * expect({b: 2}).to.have.a.property('b'); + * + * The alias `.an` can be used interchangeably with `.a`. + * + * @name a + * @alias an + * @param {String} type + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function an (type, msg) { + if (msg) flag(this, 'message', msg); + type = type.toLowerCase(); + var obj = flag(this, 'object') + , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; + + this.assert( + type === _.type(obj).toLowerCase() + , 'expected #{this} to be ' + article + type + , 'expected #{this} not to be ' + article + type + ); + } + + Assertion.addChainableMethod('an', an); + Assertion.addChainableMethod('a', an); + + /** + * ### .include(val[, msg]) + * + * When the target is a string, `.include` asserts that the given string `val` + * is a substring of the target. + * + * expect('foobar').to.include('foo'); + * + * When the target is an array, `.include` asserts that the given `val` is a + * member of the target. + * + * expect([1, 2, 3]).to.include(2); + * + * When the target is an object, `.include` asserts that the given object + * `val`'s properties are a subset of the target's properties. + * + * expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2}); + * + * When the target is a Set or WeakSet, `.include` asserts that the given `val` is a + * member of the target. SameValueZero equality algorithm is used. + * + * expect(new Set([1, 2])).to.include(2); + * + * When the target is a Map, `.include` asserts that the given `val` is one of + * the values of the target. SameValueZero equality algorithm is used. + * + * expect(new Map([['a', 1], ['b', 2]])).to.include(2); + * + * Because `.include` does different things based on the target's type, it's + * important to check the target's type before using `.include`. See the `.a` + * doc for info on testing a target's type. + * + * expect([1, 2, 3]).to.be.an('array').that.includes(2); + * + * By default, strict (`===`) equality is used to compare array members and + * object properties. Add `.deep` earlier in the chain to use deep equality + * instead (WeakSet targets are not supported). See the `deep-eql` project + * page for info on the deep equality algorithm: https://github.com/chaijs/deep-eql. + * + * // Target array deeply (but not strictly) includes `{a: 1}` + * expect([{a: 1}]).to.deep.include({a: 1}); + * expect([{a: 1}]).to.not.include({a: 1}); + * + * // Target object deeply (but not strictly) includes `x: {a: 1}` + * expect({x: {a: 1}}).to.deep.include({x: {a: 1}}); + * expect({x: {a: 1}}).to.not.include({x: {a: 1}}); + * + * By default, all of the target's properties are searched when working with + * objects. This includes properties that are inherited and/or non-enumerable. + * Add `.own` earlier in the chain to exclude the target's inherited + * properties from the search. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.own.include({a: 1}); + * expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2}); + * + * Note that a target object is always only searched for `val`'s own + * enumerable properties. + * + * `.deep` and `.own` can be combined. + * + * expect({a: {b: 2}}).to.deep.own.include({a: {b: 2}}); + * + * Add `.nested` earlier in the chain to enable dot- and bracket-notation when + * referencing nested properties. + * + * expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'}); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 2}}).to.nested.include({'\\.a.\\[b\\]': 2}); + * + * `.deep` and `.nested` can be combined. + * + * expect({a: {b: [{c: 3}]}}).to.deep.nested.include({'a.b[0]': {c: 3}}); + * + * `.own` and `.nested` cannot be combined. + * + * Add `.not` earlier in the chain to negate `.include`. + * + * expect('foobar').to.not.include('taco'); + * expect([1, 2, 3]).to.not.include(4); + * + * However, it's dangerous to negate `.include` when the target is an object. + * The problem is that it creates uncertain expectations by asserting that the + * target object doesn't have all of `val`'s key/value pairs but may or may + * not have some of them. It's often best to identify the exact output that's + * expected, and then write an assertion that only accepts that exact output. + * + * When the target object isn't even expected to have `val`'s keys, it's + * often best to assert exactly that. + * + * expect({c: 3}).to.not.have.any.keys('a', 'b'); // Recommended + * expect({c: 3}).to.not.include({a: 1, b: 2}); // Not recommended + * + * When the target object is expected to have `val`'s keys, it's often best to + * assert that each of the properties has its expected value, rather than + * asserting that each property doesn't have one of many unexpected values. + * + * expect({a: 3, b: 4}).to.include({a: 3, b: 4}); // Recommended + * expect({a: 3, b: 4}).to.not.include({a: 1, b: 2}); // Not recommended + * + * `.include` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2, 3]).to.include(4, 'nooo why fail??'); + * expect([1, 2, 3], 'nooo why fail??').to.include(4); + * + * `.include` can also be used as a language chain, causing all `.members` and + * `.keys` assertions that follow in the chain to require the target to be a + * superset of the expected set, rather than an identical set. Note that + * `.members` ignores duplicates in the subset when `.include` is added. + * + * // Target object's keys are a superset of ['a', 'b'] but not identical + * expect({a: 1, b: 2, c: 3}).to.include.all.keys('a', 'b'); + * expect({a: 1, b: 2, c: 3}).to.not.have.all.keys('a', 'b'); + * + * // Target array is a superset of [1, 2] but not identical + * expect([1, 2, 3]).to.include.members([1, 2]); + * expect([1, 2, 3]).to.not.have.members([1, 2]); + * + * // Duplicates in the subset are ignored + * expect([1, 2, 3]).to.include.members([1, 2, 2, 2]); + * + * Note that adding `.any` earlier in the chain causes the `.keys` assertion + * to ignore `.include`. + * + * // Both assertions are identical + * expect({a: 1}).to.include.any.keys('a', 'b'); + * expect({a: 1}).to.have.any.keys('a', 'b'); + * + * The aliases `.includes`, `.contain`, and `.contains` can be used + * interchangeably with `.include`. + * + * @name include + * @alias contain + * @alias includes + * @alias contains + * @param {Mixed} val + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function SameValueZero(a, b) { + return (_.isNaN(a) && _.isNaN(b)) || a === b; + } + + function includeChainingBehavior () { + flag(this, 'contains', true); + } + + function include (val, msg) { + if (msg) flag(this, 'message', msg); + + var obj = flag(this, 'object') + , objType = _.type(obj).toLowerCase() + , flagMsg = flag(this, 'message') + , negate = flag(this, 'negate') + , ssfi = flag(this, 'ssfi') + , isDeep = flag(this, 'deep') + , descriptor = isDeep ? 'deep ' : ''; + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + var included = false; + + switch (objType) { + case 'string': + included = obj.indexOf(val) !== -1; + break; + + case 'weakset': + if (isDeep) { + throw new AssertionError( + flagMsg + 'unable to use .deep.include with WeakSet', + undefined, + ssfi + ); + } + + included = obj.has(val); + break; + + case 'map': + var isEql = isDeep ? _.eql : SameValueZero; + obj.forEach(function (item) { + included = included || isEql(item, val); + }); + break; + + case 'set': + if (isDeep) { + obj.forEach(function (item) { + included = included || _.eql(item, val); + }); + } else { + included = obj.has(val); + } + break; + + case 'array': + if (isDeep) { + included = obj.some(function (item) { + return _.eql(item, val); + }) + } else { + included = obj.indexOf(val) !== -1; + } + break; + + default: + // This block is for asserting a subset of properties in an object. + // `_.expectTypes` isn't used here because `.include` should work with + // objects with a custom `@@toStringTag`. + if (val !== Object(val)) { + throw new AssertionError( + flagMsg + 'object tested must be an array, a map, an object,' + + ' a set, a string, or a weakset, but ' + objType + ' given', + undefined, + ssfi + ); + } + + var props = Object.keys(val) + , firstErr = null + , numErrs = 0; + + props.forEach(function (prop) { + var propAssertion = new Assertion(obj); + _.transferFlags(this, propAssertion, true); + flag(propAssertion, 'lockSsfi', true); + + if (!negate || props.length === 1) { + propAssertion.property(prop, val[prop]); + return; + } + + try { + propAssertion.property(prop, val[prop]); + } catch (err) { + if (!_.checkError.compatibleConstructor(err, AssertionError)) { + throw err; + } + if (firstErr === null) firstErr = err; + numErrs++; + } + }, this); + + // When validating .not.include with multiple properties, we only want + // to throw an assertion error if all of the properties are included, + // in which case we throw the first property assertion error that we + // encountered. + if (negate && props.length > 1 && numErrs === props.length) { + throw firstErr; + } + return; + } + + // Assert inclusion in collection or substring in a string. + this.assert( + included + , 'expected #{this} to ' + descriptor + 'include ' + _.inspect(val) + , 'expected #{this} to not ' + descriptor + 'include ' + _.inspect(val)); + } + + Assertion.addChainableMethod('include', include, includeChainingBehavior); + Assertion.addChainableMethod('contain', include, includeChainingBehavior); + Assertion.addChainableMethod('contains', include, includeChainingBehavior); + Assertion.addChainableMethod('includes', include, includeChainingBehavior); + + /** + * ### .ok + * + * Asserts that the target is a truthy value (considered `true` in boolean context). + * However, it's often best to assert that the target is strictly (`===`) or + * deeply equal to its expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.ok; // Not recommended + * + * expect(true).to.be.true; // Recommended + * expect(true).to.be.ok; // Not recommended + * + * Add `.not` earlier in the chain to negate `.ok`. + * + * expect(0).to.equal(0); // Recommended + * expect(0).to.not.be.ok; // Not recommended + * + * expect(false).to.be.false; // Recommended + * expect(false).to.not.be.ok; // Not recommended + * + * expect(null).to.be.null; // Recommended + * expect(null).to.not.be.ok; // Not recommended + * + * expect(undefined).to.be.undefined; // Recommended + * expect(undefined).to.not.be.ok; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(false, 'nooo why fail??').to.be.ok; + * + * @name ok + * @namespace BDD + * @api public + */ + + Assertion.addProperty('ok', function () { + this.assert( + flag(this, 'object') + , 'expected #{this} to be truthy' + , 'expected #{this} to be falsy'); + }); + + /** + * ### .true + * + * Asserts that the target is strictly (`===`) equal to `true`. + * + * expect(true).to.be.true; + * + * Add `.not` earlier in the chain to negate `.true`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `true`. + * + * expect(false).to.be.false; // Recommended + * expect(false).to.not.be.true; // Not recommended + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.true; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(false, 'nooo why fail??').to.be.true; + * + * @name true + * @namespace BDD + * @api public + */ + + Assertion.addProperty('true', function () { + this.assert( + true === flag(this, 'object') + , 'expected #{this} to be true' + , 'expected #{this} to be false' + , flag(this, 'negate') ? false : true + ); + }); + + /** + * ### .false + * + * Asserts that the target is strictly (`===`) equal to `false`. + * + * expect(false).to.be.false; + * + * Add `.not` earlier in the chain to negate `.false`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to `false`. + * + * expect(true).to.be.true; // Recommended + * expect(true).to.not.be.false; // Not recommended + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.false; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(true, 'nooo why fail??').to.be.false; + * + * @name false + * @namespace BDD + * @api public + */ + + Assertion.addProperty('false', function () { + this.assert( + false === flag(this, 'object') + , 'expected #{this} to be false' + , 'expected #{this} to be true' + , flag(this, 'negate') ? true : false + ); + }); + + /** + * ### .null + * + * Asserts that the target is strictly (`===`) equal to `null`. + * + * expect(null).to.be.null; + * + * Add `.not` earlier in the chain to negate `.null`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `null`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.null; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.null; + * + * @name null + * @namespace BDD + * @api public + */ + + Assertion.addProperty('null', function () { + this.assert( + null === flag(this, 'object') + , 'expected #{this} to be null' + , 'expected #{this} not to be null' + ); + }); + + /** + * ### .undefined + * + * Asserts that the target is strictly (`===`) equal to `undefined`. + * + * expect(undefined).to.be.undefined; + * + * Add `.not` earlier in the chain to negate `.undefined`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to `undefined`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.undefined; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.undefined; + * + * @name undefined + * @namespace BDD + * @api public + */ + + Assertion.addProperty('undefined', function () { + this.assert( + undefined === flag(this, 'object') + , 'expected #{this} to be undefined' + , 'expected #{this} not to be undefined' + ); + }); + + /** + * ### .NaN + * + * Asserts that the target is exactly `NaN`. + * + * expect(NaN).to.be.NaN; + * + * Add `.not` earlier in the chain to negate `.NaN`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `NaN`. + * + * expect('foo').to.equal('foo'); // Recommended + * expect('foo').to.not.be.NaN; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.NaN; + * + * @name NaN + * @namespace BDD + * @api public + */ + + Assertion.addProperty('NaN', function () { + this.assert( + _.isNaN(flag(this, 'object')) + , 'expected #{this} to be NaN' + , 'expected #{this} not to be NaN' + ); + }); + + /** + * ### .exist + * + * Asserts that the target is not strictly (`===`) equal to either `null` or + * `undefined`. However, it's often best to assert that the target is equal to + * its expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.exist; // Not recommended + * + * expect(0).to.equal(0); // Recommended + * expect(0).to.exist; // Not recommended + * + * Add `.not` earlier in the chain to negate `.exist`. + * + * expect(null).to.be.null; // Recommended + * expect(null).to.not.exist; // Not recommended + * + * expect(undefined).to.be.undefined; // Recommended + * expect(undefined).to.not.exist; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(null, 'nooo why fail??').to.exist; + * + * @name exist + * @namespace BDD + * @api public + */ + + Assertion.addProperty('exist', function () { + var val = flag(this, 'object'); + this.assert( + val !== null && val !== undefined + , 'expected #{this} to exist' + , 'expected #{this} to not exist' + ); + }); + + /** + * ### .empty + * + * When the target is a string or array, `.empty` asserts that the target's + * `length` property is strictly (`===`) equal to `0`. + * + * expect([]).to.be.empty; + * expect('').to.be.empty; + * + * When the target is a map or set, `.empty` asserts that the target's `size` + * property is strictly equal to `0`. + * + * expect(new Set()).to.be.empty; + * expect(new Map()).to.be.empty; + * + * When the target is a non-function object, `.empty` asserts that the target + * doesn't have any own enumerable properties. Properties with Symbol-based + * keys are excluded from the count. + * + * expect({}).to.be.empty; + * + * Because `.empty` does different things based on the target's type, it's + * important to check the target's type before using `.empty`. See the `.a` + * doc for info on testing a target's type. + * + * expect([]).to.be.an('array').that.is.empty; + * + * Add `.not` earlier in the chain to negate `.empty`. However, it's often + * best to assert that the target contains its expected number of values, + * rather than asserting that it's not empty. + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.not.be.empty; // Not recommended + * + * expect(new Set([1, 2, 3])).to.have.property('size', 3); // Recommended + * expect(new Set([1, 2, 3])).to.not.be.empty; // Not recommended + * + * expect(Object.keys({a: 1})).to.have.lengthOf(1); // Recommended + * expect({a: 1}).to.not.be.empty; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect([1, 2, 3], 'nooo why fail??').to.be.empty; + * + * @name empty + * @namespace BDD + * @api public + */ + + Assertion.addProperty('empty', function () { + var val = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , flagMsg = flag(this, 'message') + , itemsCount; + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + switch (_.type(val).toLowerCase()) { + case 'array': + case 'string': + itemsCount = val.length; + break; + case 'map': + case 'set': + itemsCount = val.size; + break; + case 'weakmap': + case 'weakset': + throw new AssertionError( + flagMsg + '.empty was passed a weak collection', + undefined, + ssfi + ); + case 'function': + var msg = flagMsg + '.empty was passed a function ' + _.getName(val); + throw new AssertionError(msg.trim(), undefined, ssfi); + default: + if (val !== Object(val)) { + throw new AssertionError( + flagMsg + '.empty was passed non-string primitive ' + _.inspect(val), + undefined, + ssfi + ); + } + itemsCount = Object.keys(val).length; + } + + this.assert( + 0 === itemsCount + , 'expected #{this} to be empty' + , 'expected #{this} not to be empty' + ); + }); + + /** + * ### .arguments + * + * Asserts that the target is an `arguments` object. + * + * function test () { + * expect(arguments).to.be.arguments; + * } + * + * test(); + * + * Add `.not` earlier in the chain to negate `.arguments`. However, it's often + * best to assert which type the target is expected to be, rather than + * asserting that its not an `arguments` object. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.arguments; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({}, 'nooo why fail??').to.be.arguments; + * + * The alias `.Arguments` can be used interchangeably with `.arguments`. + * + * @name arguments + * @alias Arguments + * @namespace BDD + * @api public + */ + + function checkArguments () { + var obj = flag(this, 'object') + , type = _.type(obj); + this.assert( + 'Arguments' === type + , 'expected #{this} to be arguments but got ' + type + , 'expected #{this} to not be arguments' + ); + } + + Assertion.addProperty('arguments', checkArguments); + Assertion.addProperty('Arguments', checkArguments); + + /** + * ### .equal(val[, msg]) + * + * Asserts that the target is strictly (`===`) equal to the given `val`. + * + * expect(1).to.equal(1); + * expect('foo').to.equal('foo'); + * + * Add `.deep` earlier in the chain to use deep equality instead. See the + * `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target object deeply (but not strictly) equals `{a: 1}` + * expect({a: 1}).to.deep.equal({a: 1}); + * expect({a: 1}).to.not.equal({a: 1}); + * + * // Target array deeply (but not strictly) equals `[1, 2]` + * expect([1, 2]).to.deep.equal([1, 2]); + * expect([1, 2]).to.not.equal([1, 2]); + * + * Add `.not` earlier in the chain to negate `.equal`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to one of countless unexpected values. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.equal(2); // Not recommended + * + * `.equal` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.equal(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.equal(2); + * + * The aliases `.equals` and `eq` can be used interchangeably with `.equal`. + * + * @name equal + * @alias equals + * @alias eq + * @param {Mixed} val + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertEqual (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'deep')) { + var prevLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); + this.eql(val); + flag(this, 'lockSsfi', prevLockSsfi); + } else { + this.assert( + val === obj + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{exp}' + , val + , this._obj + , true + ); + } + } + + Assertion.addMethod('equal', assertEqual); + Assertion.addMethod('equals', assertEqual); + Assertion.addMethod('eq', assertEqual); + + /** + * ### .eql(obj[, msg]) + * + * Asserts that the target is deeply equal to the given `obj`. See the + * `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target object is deeply (but not strictly) equal to {a: 1} + * expect({a: 1}).to.eql({a: 1}).but.not.equal({a: 1}); + * + * // Target array is deeply (but not strictly) equal to [1, 2] + * expect([1, 2]).to.eql([1, 2]).but.not.equal([1, 2]); + * + * Add `.not` earlier in the chain to negate `.eql`. However, it's often best + * to assert that the target is deeply equal to its expected value, rather + * than not deeply equal to one of countless unexpected values. + * + * expect({a: 1}).to.eql({a: 1}); // Recommended + * expect({a: 1}).to.not.eql({b: 2}); // Not recommended + * + * `.eql` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect({a: 1}).to.eql({b: 2}, 'nooo why fail??'); + * expect({a: 1}, 'nooo why fail??').to.eql({b: 2}); + * + * The alias `.eqls` can be used interchangeably with `.eql`. + * + * The `.deep.equal` assertion is almost identical to `.eql` but with one + * difference: `.deep.equal` causes deep equality comparisons to also be used + * for any other assertions that follow in the chain. + * + * @name eql + * @alias eqls + * @param {Mixed} obj + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertEql(obj, msg) { + if (msg) flag(this, 'message', msg); + this.assert( + _.eql(obj, flag(this, 'object')) + , 'expected #{this} to deeply equal #{exp}' + , 'expected #{this} to not deeply equal #{exp}' + , obj + , this._obj + , true + ); + } + + Assertion.addMethod('eql', assertEql); + Assertion.addMethod('eqls', assertEql); + + /** + * ### .above(n[, msg]) + * + * Asserts that the target is a number or a date greater than the given number or date `n` respectively. + * However, it's often best to assert that the target is equal to its expected + * value. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.above(1); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is greater than the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.above(2); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.above(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.above`. + * + * expect(2).to.equal(2); // Recommended + * expect(1).to.not.be.above(2); // Not recommended + * + * `.above` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.above(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.above(2); + * + * The aliases `.gt` and `.greaterThan` can be used interchangeably with + * `.above`. + * + * @name above + * @alias gt + * @alias greaterThan + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertAbove (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to above must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to above must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount > n + , 'expected #{this} to have a ' + descriptor + ' above #{exp} but got #{act}' + , 'expected #{this} to not have a ' + descriptor + ' above #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj > n + , 'expected #{this} to be above #{exp}' + , 'expected #{this} to be at most #{exp}' + , n + ); + } + } + + Assertion.addMethod('above', assertAbove); + Assertion.addMethod('gt', assertAbove); + Assertion.addMethod('greaterThan', assertAbove); + + /** + * ### .least(n[, msg]) + * + * Asserts that the target is a number or a date greater than or equal to the given + * number or date `n` respectively. However, it's often best to assert that the target is equal to + * its expected value. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.at.least(1); // Not recommended + * expect(2).to.be.at.least(2); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is greater than or equal to the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.at.least(2); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.at.least(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.least`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.at.least(2); // Not recommended + * + * `.least` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.at.least(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.at.least(2); + * + * The alias `.gte` can be used interchangeably with `.least`. + * + * @name least + * @alias gte + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertLeast (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to least must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to least must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount >= n + , 'expected #{this} to have a ' + descriptor + ' at least #{exp} but got #{act}' + , 'expected #{this} to have a ' + descriptor + ' below #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj >= n + , 'expected #{this} to be at least #{exp}' + , 'expected #{this} to be below #{exp}' + , n + ); + } + } + + Assertion.addMethod('least', assertLeast); + Assertion.addMethod('gte', assertLeast); + + /** + * ### .below(n[, msg]) + * + * Asserts that the target is a number or a date less than the given number or date `n` respectively. + * However, it's often best to assert that the target is equal to its expected + * value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.below(2); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is less than the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.below(4); // Not recommended + * + * expect([1, 2, 3]).to.have.length(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.below(4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.below`. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.be.below(1); // Not recommended + * + * `.below` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(2).to.be.below(1, 'nooo why fail??'); + * expect(2, 'nooo why fail??').to.be.below(1); + * + * The aliases `.lt` and `.lessThan` can be used interchangeably with + * `.below`. + * + * @name below + * @alias lt + * @alias lessThan + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertBelow (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to below must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to below must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount < n + , 'expected #{this} to have a ' + descriptor + ' below #{exp} but got #{act}' + , 'expected #{this} to not have a ' + descriptor + ' below #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj < n + , 'expected #{this} to be below #{exp}' + , 'expected #{this} to be at least #{exp}' + , n + ); + } + } + + Assertion.addMethod('below', assertBelow); + Assertion.addMethod('lt', assertBelow); + Assertion.addMethod('lessThan', assertBelow); + + /** + * ### .most(n[, msg]) + * + * Asserts that the target is a number or a date less than or equal to the given number + * or date `n` respectively. However, it's often best to assert that the target is equal to its + * expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.at.most(2); // Not recommended + * expect(1).to.be.at.most(1); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is less than or equal to the given number `n`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.at.most(4); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.at.most(4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.most`. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.be.at.most(1); // Not recommended + * + * `.most` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(2).to.be.at.most(1, 'nooo why fail??'); + * expect(2, 'nooo why fail??').to.be.at.most(1); + * + * The alias `.lte` can be used interchangeably with `.most`. + * + * @name most + * @alias lte + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertMost (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , nType = _.type(n).toLowerCase() + , errorMessage + , shouldThrow = true; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && nType !== 'date')) { + errorMessage = msgPrefix + 'the argument to most must be a date'; + } else if (nType !== 'number' && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the argument to most must be a number'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount <= n + , 'expected #{this} to have a ' + descriptor + ' at most #{exp} but got #{act}' + , 'expected #{this} to have a ' + descriptor + ' above #{exp}' + , n + , itemsCount + ); + } else { + this.assert( + obj <= n + , 'expected #{this} to be at most #{exp}' + , 'expected #{this} to be above #{exp}' + , n + ); + } + } + + Assertion.addMethod('most', assertMost); + Assertion.addMethod('lte', assertMost); + + /** + * ### .within(start, finish[, msg]) + * + * Asserts that the target is a number or a date greater than or equal to the given + * number or date `start`, and less than or equal to the given number or date `finish` respectively. + * However, it's often best to assert that the target is equal to its expected + * value. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.within(1, 3); // Not recommended + * expect(2).to.be.within(2, 3); // Not recommended + * expect(2).to.be.within(1, 2); // Not recommended + * + * Add `.lengthOf` earlier in the chain to assert that the target's `length` + * or `size` is greater than or equal to the given number `start`, and less + * than or equal to the given number `finish`. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.within(2, 4); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.within(2, 4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.within`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.within(2, 4); // Not recommended + * + * `.within` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(4).to.be.within(1, 3, 'nooo why fail??'); + * expect(4, 'nooo why fail??').to.be.within(1, 3); + * + * @name within + * @param {Number} start lower bound inclusive + * @param {Number} finish upper bound inclusive + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + Assertion.addMethod('within', function (start, finish, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') + , ssfi = flag(this, 'ssfi') + , objType = _.type(obj).toLowerCase() + , startType = _.type(start).toLowerCase() + , finishType = _.type(finish).toLowerCase() + , errorMessage + , shouldThrow = true + , range = (startType === 'date' && finishType === 'date') + ? start.toUTCString() + '..' + finish.toUTCString() + : start + '..' + finish; + + if (doLength && objType !== 'map' && objType !== 'set') { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } + + if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) { + errorMessage = msgPrefix + 'the arguments to within must be dates'; + } else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) { + errorMessage = msgPrefix + 'the arguments to within must be numbers'; + } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + var printObj = (objType === 'string') ? "'" + obj + "'" : obj; + errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; + } else { + shouldThrow = false; + } + + if (shouldThrow) { + throw new AssertionError(errorMessage, undefined, ssfi); + } + + if (doLength) { + var descriptor = 'length' + , itemsCount; + if (objType === 'map' || objType === 'set') { + descriptor = 'size'; + itemsCount = obj.size; + } else { + itemsCount = obj.length; + } + this.assert( + itemsCount >= start && itemsCount <= finish + , 'expected #{this} to have a ' + descriptor + ' within ' + range + , 'expected #{this} to not have a ' + descriptor + ' within ' + range + ); + } else { + this.assert( + obj >= start && obj <= finish + , 'expected #{this} to be within ' + range + , 'expected #{this} to not be within ' + range + ); + } + }); + + /** + * ### .instanceof(constructor[, msg]) + * + * Asserts that the target is an instance of the given `constructor`. + * + * function Cat () { } + * + * expect(new Cat()).to.be.an.instanceof(Cat); + * expect([1, 2]).to.be.an.instanceof(Array); + * + * Add `.not` earlier in the chain to negate `.instanceof`. + * + * expect({a: 1}).to.not.be.an.instanceof(Array); + * + * `.instanceof` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1).to.be.an.instanceof(Array, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.an.instanceof(Array); + * + * Due to limitations in ES5, `.instanceof` may not always work as expected + * when using a transpiler such as Babel or TypeScript. In particular, it may + * produce unexpected results when subclassing built-in object such as + * `Array`, `Error`, and `Map`. See your transpiler's docs for details: + * + * - ([Babel](https://babeljs.io/docs/usage/caveats/#classes)) + * - ([TypeScript](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work)) + * + * The alias `.instanceOf` can be used interchangeably with `.instanceof`. + * + * @name instanceof + * @param {Constructor} constructor + * @param {String} msg _optional_ + * @alias instanceOf + * @namespace BDD + * @api public + */ + + function assertInstanceOf (constructor, msg) { + if (msg) flag(this, 'message', msg); + + var target = flag(this, 'object') + var ssfi = flag(this, 'ssfi'); + var flagMsg = flag(this, 'message'); + + try { + var isInstanceOf = target instanceof constructor; + } catch (err) { + if (err instanceof TypeError) { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'The instanceof assertion needs a constructor but ' + + _.type(constructor) + ' was given.', + undefined, + ssfi + ); + } + throw err; + } + + var name = _.getName(constructor); + if (name === null) { + name = 'an unnamed constructor'; + } + + this.assert( + isInstanceOf + , 'expected #{this} to be an instance of ' + name + , 'expected #{this} to not be an instance of ' + name + ); + }; + + Assertion.addMethod('instanceof', assertInstanceOf); + Assertion.addMethod('instanceOf', assertInstanceOf); + + /** + * ### .property(name[, val[, msg]]) + * + * Asserts that the target has a property with the given key `name`. + * + * expect({a: 1}).to.have.property('a'); + * + * When `val` is provided, `.property` also asserts that the property's value + * is equal to the given `val`. + * + * expect({a: 1}).to.have.property('a', 1); + * + * By default, strict (`===`) equality is used. Add `.deep` earlier in the + * chain to use deep equality instead. See the `deep-eql` project page for + * info on the deep equality algorithm: https://github.com/chaijs/deep-eql. + * + * // Target object deeply (but not strictly) has property `x: {a: 1}` + * expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); + * expect({x: {a: 1}}).to.not.have.property('x', {a: 1}); + * + * The target's enumerable and non-enumerable properties are always included + * in the search. By default, both own and inherited properties are included. + * Add `.own` earlier in the chain to exclude inherited properties from the + * search. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.have.own.property('a'); + * expect({a: 1}).to.have.own.property('a', 1); + * expect({a: 1}).to.have.property('b'); + * expect({a: 1}).to.not.have.own.property('b'); + * + * `.deep` and `.own` can be combined. + * + * expect({x: {a: 1}}).to.have.deep.own.property('x', {a: 1}); + * + * Add `.nested` earlier in the chain to enable dot- and bracket-notation when + * referencing nested properties. + * + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]'); + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]', 'y'); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 'x'}}).to.have.nested.property('\\.a.\\[b\\]'); + * + * `.deep` and `.nested` can be combined. + * + * expect({a: {b: [{c: 3}]}}) + * .to.have.deep.nested.property('a.b[0]', {c: 3}); + * + * `.own` and `.nested` cannot be combined. + * + * Add `.not` earlier in the chain to negate `.property`. + * + * expect({a: 1}).to.not.have.property('b'); + * + * However, it's dangerous to negate `.property` when providing `val`. The + * problem is that it creates uncertain expectations by asserting that the + * target either doesn't have a property with the given key `name`, or that it + * does have a property with the given key `name` but its value isn't equal to + * the given `val`. It's often best to identify the exact output that's + * expected, and then write an assertion that only accepts that exact output. + * + * When the target isn't expected to have a property with the given key + * `name`, it's often best to assert exactly that. + * + * expect({b: 2}).to.not.have.property('a'); // Recommended + * expect({b: 2}).to.not.have.property('a', 1); // Not recommended + * + * When the target is expected to have a property with the given key `name`, + * it's often best to assert that the property has its expected value, rather + * than asserting that it doesn't have one of many unexpected values. + * + * expect({a: 3}).to.have.property('a', 3); // Recommended + * expect({a: 3}).to.not.have.property('a', 1); // Not recommended + * + * `.property` changes the target of any assertions that follow in the chain + * to be the value of the property from the original target object. + * + * expect({a: 1}).to.have.property('a').that.is.a('number'); + * + * `.property` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing `val`, only use the + * second form. + * + * // Recommended + * expect({a: 1}).to.have.property('a', 2, 'nooo why fail??'); + * expect({a: 1}, 'nooo why fail??').to.have.property('a', 2); + * expect({a: 1}, 'nooo why fail??').to.have.property('b'); + * + * // Not recommended + * expect({a: 1}).to.have.property('b', undefined, 'nooo why fail??'); + * + * The above assertion isn't the same thing as not providing `val`. Instead, + * it's asserting that the target object has a `b` property that's equal to + * `undefined`. + * + * The assertions `.ownProperty` and `.haveOwnProperty` can be used + * interchangeably with `.own.property`. + * + * @name property + * @param {String} name + * @param {Mixed} val (optional) + * @param {String} msg _optional_ + * @returns value of property for chaining + * @namespace BDD + * @api public + */ + + function assertProperty (name, val, msg) { + if (msg) flag(this, 'message', msg); + + var isNested = flag(this, 'nested') + , isOwn = flag(this, 'own') + , flagMsg = flag(this, 'message') + , obj = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , nameType = typeof name; + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + if (isNested) { + if (nameType !== 'string') { + throw new AssertionError( + flagMsg + 'the argument to property must be a string when using nested syntax', + undefined, + ssfi + ); + } + } else { + if (nameType !== 'string' && nameType !== 'number' && nameType !== 'symbol') { + throw new AssertionError( + flagMsg + 'the argument to property must be a string, number, or symbol', + undefined, + ssfi + ); + } + } + + if (isNested && isOwn) { + throw new AssertionError( + flagMsg + 'The "nested" and "own" flags cannot be combined.', + undefined, + ssfi + ); + } + + if (obj === null || obj === undefined) { + throw new AssertionError( + flagMsg + 'Target cannot be null or undefined.', + undefined, + ssfi + ); + } + + var isDeep = flag(this, 'deep') + , negate = flag(this, 'negate') + , pathInfo = isNested ? _.getPathInfo(obj, name) : null + , value = isNested ? pathInfo.value : obj[name]; + + var descriptor = ''; + if (isDeep) descriptor += 'deep '; + if (isOwn) descriptor += 'own '; + if (isNested) descriptor += 'nested '; + descriptor += 'property '; + + var hasProperty; + if (isOwn) hasProperty = Object.prototype.hasOwnProperty.call(obj, name); + else if (isNested) hasProperty = pathInfo.exists; + else hasProperty = _.hasProperty(obj, name); + + // When performing a negated assertion for both name and val, merely having + // a property with the given name isn't enough to cause the assertion to + // fail. It must both have a property with the given name, and the value of + // that property must equal the given val. Therefore, skip this assertion in + // favor of the next. + if (!negate || arguments.length === 1) { + this.assert( + hasProperty + , 'expected #{this} to have ' + descriptor + _.inspect(name) + , 'expected #{this} to not have ' + descriptor + _.inspect(name)); + } + + if (arguments.length > 1) { + this.assert( + hasProperty && (isDeep ? _.eql(val, value) : val === value) + , 'expected #{this} to have ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' + , 'expected #{this} to not have ' + descriptor + _.inspect(name) + ' of #{act}' + , val + , value + ); + } + + flag(this, 'object', value); + } + + Assertion.addMethod('property', assertProperty); + + function assertOwnProperty (name, value, msg) { + flag(this, 'own', true); + assertProperty.apply(this, arguments); + } + + Assertion.addMethod('ownProperty', assertOwnProperty); + Assertion.addMethod('haveOwnProperty', assertOwnProperty); + + /** + * ### .ownPropertyDescriptor(name[, descriptor[, msg]]) + * + * Asserts that the target has its own property descriptor with the given key + * `name`. Enumerable and non-enumerable properties are included in the + * search. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a'); + * + * When `descriptor` is provided, `.ownPropertyDescriptor` also asserts that + * the property's descriptor is deeply equal to the given `descriptor`. See + * the `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * Add `.not` earlier in the chain to negate `.ownPropertyDescriptor`. + * + * expect({a: 1}).to.not.have.ownPropertyDescriptor('b'); + * + * However, it's dangerous to negate `.ownPropertyDescriptor` when providing + * a `descriptor`. The problem is that it creates uncertain expectations by + * asserting that the target either doesn't have a property descriptor with + * the given key `name`, or that it does have a property descriptor with the + * given key `name` but its not deeply equal to the given `descriptor`. It's + * often best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to have a property descriptor with the given + * key `name`, it's often best to assert exactly that. + * + * // Recommended + * expect({b: 2}).to.not.have.ownPropertyDescriptor('a'); + * + * // Not recommended + * expect({b: 2}).to.not.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * When the target is expected to have a property descriptor with the given + * key `name`, it's often best to assert that the property has its expected + * descriptor, rather than asserting that it doesn't have one of many + * unexpected descriptors. + * + * // Recommended + * expect({a: 3}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 3, + * }); + * + * // Not recommended + * expect({a: 3}).to.not.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * `.ownPropertyDescriptor` changes the target of any assertions that follow + * in the chain to be the value of the property descriptor from the original + * target object. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a') + * .that.has.property('enumerable', true); + * + * `.ownPropertyDescriptor` accepts an optional `msg` argument which is a + * custom error message to show when the assertion fails. The message can also + * be given as the second argument to `expect`. When not providing + * `descriptor`, only use the second form. + * + * // Recommended + * expect({a: 1}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 2, + * }, 'nooo why fail??'); + * + * // Recommended + * expect({a: 1}, 'nooo why fail??').to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 2, + * }); + * + * // Recommended + * expect({a: 1}, 'nooo why fail??').to.have.ownPropertyDescriptor('b'); + * + * // Not recommended + * expect({a: 1}) + * .to.have.ownPropertyDescriptor('b', undefined, 'nooo why fail??'); + * + * The above assertion isn't the same thing as not providing `descriptor`. + * Instead, it's asserting that the target object has a `b` property + * descriptor that's deeply equal to `undefined`. + * + * The alias `.haveOwnPropertyDescriptor` can be used interchangeably with + * `.ownPropertyDescriptor`. + * + * @name ownPropertyDescriptor + * @alias haveOwnPropertyDescriptor + * @param {String} name + * @param {Object} descriptor _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertOwnPropertyDescriptor (name, descriptor, msg) { + if (typeof descriptor === 'string') { + msg = descriptor; + descriptor = null; + } + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var actualDescriptor = Object.getOwnPropertyDescriptor(Object(obj), name); + if (actualDescriptor && descriptor) { + this.assert( + _.eql(descriptor, actualDescriptor) + , 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to match ' + _.inspect(descriptor) + ', got ' + _.inspect(actualDescriptor) + , 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to not match ' + _.inspect(descriptor) + , descriptor + , actualDescriptor + , true + ); + } else { + this.assert( + actualDescriptor + , 'expected #{this} to have an own property descriptor for ' + _.inspect(name) + , 'expected #{this} to not have an own property descriptor for ' + _.inspect(name) + ); + } + flag(this, 'object', actualDescriptor); + } + + Assertion.addMethod('ownPropertyDescriptor', assertOwnPropertyDescriptor); + Assertion.addMethod('haveOwnPropertyDescriptor', assertOwnPropertyDescriptor); + + /** + * ### .lengthOf(n[, msg]) + * + * Asserts that the target's `length` or `size` is equal to the given number + * `n`. + * + * expect([1, 2, 3]).to.have.lengthOf(3); + * expect('foo').to.have.lengthOf(3); + * expect(new Set([1, 2, 3])).to.have.lengthOf(3); + * expect(new Map([['a', 1], ['b', 2], ['c', 3]])).to.have.lengthOf(3); + * + * Add `.not` earlier in the chain to negate `.lengthOf`. However, it's often + * best to assert that the target's `length` property is equal to its expected + * value, rather than not equal to one of many unexpected values. + * + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.not.have.lengthOf(4); // Not recommended + * + * `.lengthOf` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2, 3]).to.have.lengthOf(2, 'nooo why fail??'); + * expect([1, 2, 3], 'nooo why fail??').to.have.lengthOf(2); + * + * `.lengthOf` can also be used as a language chain, causing all `.above`, + * `.below`, `.least`, `.most`, and `.within` assertions that follow in the + * chain to use the target's `length` property as the target. However, it's + * often best to assert that the target's `length` property is equal to its + * expected length, rather than asserting that its `length` property falls + * within some range of values. + * + * // Recommended + * expect([1, 2, 3]).to.have.lengthOf(3); + * + * // Not recommended + * expect([1, 2, 3]).to.have.lengthOf.above(2); + * expect([1, 2, 3]).to.have.lengthOf.below(4); + * expect([1, 2, 3]).to.have.lengthOf.at.least(3); + * expect([1, 2, 3]).to.have.lengthOf.at.most(3); + * expect([1, 2, 3]).to.have.lengthOf.within(2,4); + * + * Due to a compatibility issue, the alias `.length` can't be chained directly + * off of an uninvoked method such as `.a`. Therefore, `.length` can't be used + * interchangeably with `.lengthOf` in every situation. It's recommended to + * always use `.lengthOf` instead of `.length`. + * + * expect([1, 2, 3]).to.have.a.length(3); // incompatible; throws error + * expect([1, 2, 3]).to.have.a.lengthOf(3); // passes as expected + * + * @name lengthOf + * @alias length + * @param {Number} n + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertLengthChain () { + flag(this, 'doLength', true); + } + + function assertLength (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , objType = _.type(obj).toLowerCase() + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi') + , descriptor = 'length' + , itemsCount; + + switch (objType) { + case 'map': + case 'set': + descriptor = 'size'; + itemsCount = obj.size; + break; + default: + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + itemsCount = obj.length; + } + + this.assert( + itemsCount == n + , 'expected #{this} to have a ' + descriptor + ' of #{exp} but got #{act}' + , 'expected #{this} to not have a ' + descriptor + ' of #{act}' + , n + , itemsCount + ); + } + + Assertion.addChainableMethod('length', assertLength, assertLengthChain); + Assertion.addChainableMethod('lengthOf', assertLength, assertLengthChain); + + /** + * ### .match(re[, msg]) + * + * Asserts that the target matches the given regular expression `re`. + * + * expect('foobar').to.match(/^foo/); + * + * Add `.not` earlier in the chain to negate `.match`. + * + * expect('foobar').to.not.match(/taco/); + * + * `.match` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect('foobar').to.match(/taco/, 'nooo why fail??'); + * expect('foobar', 'nooo why fail??').to.match(/taco/); + * + * The alias `.matches` can be used interchangeably with `.match`. + * + * @name match + * @alias matches + * @param {RegExp} re + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + function assertMatch(re, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + re.exec(obj) + , 'expected #{this} to match ' + re + , 'expected #{this} not to match ' + re + ); + } + + Assertion.addMethod('match', assertMatch); + Assertion.addMethod('matches', assertMatch); + + /** + * ### .string(str[, msg]) + * + * Asserts that the target string contains the given substring `str`. + * + * expect('foobar').to.have.string('bar'); + * + * Add `.not` earlier in the chain to negate `.string`. + * + * expect('foobar').to.not.have.string('taco'); + * + * `.string` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect('foobar').to.have.string('taco', 'nooo why fail??'); + * expect('foobar', 'nooo why fail??').to.have.string('taco'); + * + * @name string + * @param {String} str + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + Assertion.addMethod('string', function (str, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(obj, flagMsg, ssfi, true).is.a('string'); + + this.assert( + ~obj.indexOf(str) + , 'expected #{this} to contain ' + _.inspect(str) + , 'expected #{this} to not contain ' + _.inspect(str) + ); + }); + + /** + * ### .keys(key1[, key2[, ...]]) + * + * Asserts that the target object, array, map, or set has the given keys. Only + * the target's own inherited properties are included in the search. + * + * When the target is an object or array, keys can be provided as one or more + * string arguments, a single array argument, or a single object argument. In + * the latter case, only the keys in the given object matter; the values are + * ignored. + * + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * expect(['x', 'y']).to.have.all.keys(0, 1); + * + * expect({a: 1, b: 2}).to.have.all.keys(['a', 'b']); + * expect(['x', 'y']).to.have.all.keys([0, 1]); + * + * expect({a: 1, b: 2}).to.have.all.keys({a: 4, b: 5}); // ignore 4 and 5 + * expect(['x', 'y']).to.have.all.keys({0: 4, 1: 5}); // ignore 4 and 5 + * + * When the target is a map or set, each key must be provided as a separate + * argument. + * + * expect(new Map([['a', 1], ['b', 2]])).to.have.all.keys('a', 'b'); + * expect(new Set(['a', 'b'])).to.have.all.keys('a', 'b'); + * + * Because `.keys` does different things based on the target's type, it's + * important to check the target's type before using `.keys`. See the `.a` doc + * for info on testing a target's type. + * + * expect({a: 1, b: 2}).to.be.an('object').that.has.all.keys('a', 'b'); + * + * By default, strict (`===`) equality is used to compare keys of maps and + * sets. Add `.deep` earlier in the chain to use deep equality instead. See + * the `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target set deeply (but not strictly) has key `{a: 1}` + * expect(new Set([{a: 1}])).to.have.all.deep.keys([{a: 1}]); + * expect(new Set([{a: 1}])).to.not.have.all.keys([{a: 1}]); + * + * By default, the target must have all of the given keys and no more. Add + * `.any` earlier in the chain to only require that the target have at least + * one of the given keys. Also, add `.not` earlier in the chain to negate + * `.keys`. It's often best to add `.any` when negating `.keys`, and to use + * `.all` when asserting `.keys` without negation. + * + * When negating `.keys`, `.any` is preferred because `.not.any.keys` asserts + * exactly what's expected of the output, whereas `.not.all.keys` creates + * uncertain expectations. + * + * // Recommended; asserts that target doesn't have any of the given keys + * expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd'); + * + * // Not recommended; asserts that target doesn't have all of the given + * // keys but may or may not have some of them + * expect({a: 1, b: 2}).to.not.have.all.keys('c', 'd'); + * + * When asserting `.keys` without negation, `.all` is preferred because + * `.all.keys` asserts exactly what's expected of the output, whereas + * `.any.keys` creates uncertain expectations. + * + * // Recommended; asserts that target has all the given keys + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * + * // Not recommended; asserts that target has at least one of the given + * // keys but may or may not have more of them + * expect({a: 1, b: 2}).to.have.any.keys('a', 'b'); + * + * Note that `.all` is used by default when neither `.all` nor `.any` appear + * earlier in the chain. However, it's often best to add `.all` anyway because + * it improves readability. + * + * // Both assertions are identical + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); // Recommended + * expect({a: 1, b: 2}).to.have.keys('a', 'b'); // Not recommended + * + * Add `.include` earlier in the chain to require that the target's keys be a + * superset of the expected keys, rather than identical sets. + * + * // Target object's keys are a superset of ['a', 'b'] but not identical + * expect({a: 1, b: 2, c: 3}).to.include.all.keys('a', 'b'); + * expect({a: 1, b: 2, c: 3}).to.not.have.all.keys('a', 'b'); + * + * However, if `.any` and `.include` are combined, only the `.any` takes + * effect. The `.include` is ignored in this case. + * + * // Both assertions are identical + * expect({a: 1}).to.have.any.keys('a', 'b'); + * expect({a: 1}).to.include.any.keys('a', 'b'); + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.have.key('b'); + * + * The alias `.key` can be used interchangeably with `.keys`. + * + * @name keys + * @alias key + * @param {...String|Array|Object} keys + * @namespace BDD + * @api public + */ + + function assertKeys (keys) { + var obj = flag(this, 'object') + , objType = _.type(obj) + , keysType = _.type(keys) + , ssfi = flag(this, 'ssfi') + , isDeep = flag(this, 'deep') + , str + , deepStr = '' + , actual + , ok = true + , flagMsg = flag(this, 'message'); + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + var mixedArgsMsg = flagMsg + 'when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments'; + + if (objType === 'Map' || objType === 'Set') { + deepStr = isDeep ? 'deeply ' : ''; + actual = []; + + // Map and Set '.keys' aren't supported in IE 11. Therefore, use .forEach. + obj.forEach(function (val, key) { actual.push(key) }); + + if (keysType !== 'Array') { + keys = Array.prototype.slice.call(arguments); + } + } else { + actual = _.getOwnEnumerableProperties(obj); + + switch (keysType) { + case 'Array': + if (arguments.length > 1) { + throw new AssertionError(mixedArgsMsg, undefined, ssfi); + } + break; + case 'Object': + if (arguments.length > 1) { + throw new AssertionError(mixedArgsMsg, undefined, ssfi); + } + keys = Object.keys(keys); + break; + default: + keys = Array.prototype.slice.call(arguments); + } + + // Only stringify non-Symbols because Symbols would become "Symbol()" + keys = keys.map(function (val) { + return typeof val === 'symbol' ? val : String(val); + }); + } + + if (!keys.length) { + throw new AssertionError(flagMsg + 'keys required', undefined, ssfi); + } + + var len = keys.length + , any = flag(this, 'any') + , all = flag(this, 'all') + , expected = keys; + + if (!any && !all) { + all = true; + } + + // Has any + if (any) { + ok = expected.some(function(expectedKey) { + return actual.some(function(actualKey) { + if (isDeep) { + return _.eql(expectedKey, actualKey); + } else { + return expectedKey === actualKey; + } + }); + }); + } + + // Has all + if (all) { + ok = expected.every(function(expectedKey) { + return actual.some(function(actualKey) { + if (isDeep) { + return _.eql(expectedKey, actualKey); + } else { + return expectedKey === actualKey; + } + }); + }); + + if (!flag(this, 'contains')) { + ok = ok && keys.length == actual.length; + } + } + + // Key string + if (len > 1) { + keys = keys.map(function(key) { + return _.inspect(key); + }); + var last = keys.pop(); + if (all) { + str = keys.join(', ') + ', and ' + last; + } + if (any) { + str = keys.join(', ') + ', or ' + last; + } + } else { + str = _.inspect(keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; + + // Assertion + this.assert( + ok + , 'expected #{this} to ' + deepStr + str + , 'expected #{this} to not ' + deepStr + str + , expected.slice(0).sort(_.compareByInspect) + , actual.sort(_.compareByInspect) + , true + ); + } + + Assertion.addMethod('keys', assertKeys); + Assertion.addMethod('key', assertKeys); + + /** + * ### .throw([errorLike], [errMsgMatcher], [msg]) + * + * When no arguments are provided, `.throw` invokes the target function and + * asserts that an error is thrown. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(); + * + * When one argument is provided, and it's an error constructor, `.throw` + * invokes the target function and asserts that an error is thrown that's an + * instance of that error constructor. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(TypeError); + * + * When one argument is provided, and it's an error instance, `.throw` invokes + * the target function and asserts that an error is thrown that's strictly + * (`===`) equal to that error instance. + * + * var err = new TypeError('Illegal salmon!'); + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(err); + * + * When one argument is provided, and it's a string, `.throw` invokes the + * target function and asserts that an error is thrown with a message that + * contains that string. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw('salmon'); + * + * When one argument is provided, and it's a regular expression, `.throw` + * invokes the target function and asserts that an error is thrown with a + * message that matches that regular expression. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(/salmon/); + * + * When two arguments are provided, and the first is an error instance or + * constructor, and the second is a string or regular expression, `.throw` + * invokes the function and asserts that an error is thrown that fulfills both + * conditions as described above. + * + * var err = new TypeError('Illegal salmon!'); + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(TypeError, 'salmon'); + * expect(badFn).to.throw(TypeError, /salmon/); + * expect(badFn).to.throw(err, 'salmon'); + * expect(badFn).to.throw(err, /salmon/); + * + * Add `.not` earlier in the chain to negate `.throw`. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.not.throw(); + * + * However, it's dangerous to negate `.throw` when providing any arguments. + * The problem is that it creates uncertain expectations by asserting that the + * target either doesn't throw an error, or that it throws an error but of a + * different type than the given type, or that it throws an error of the given + * type but with a message that doesn't include the given string. It's often + * best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to throw an error, it's often best to assert + * exactly that. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.not.throw(); // Recommended + * expect(goodFn).to.not.throw(ReferenceError, 'x'); // Not recommended + * + * When the target is expected to throw an error, it's often best to assert + * that the error is of its expected type, and has a message that includes an + * expected string, rather than asserting that it doesn't have one of many + * unexpected types, and doesn't have a message that includes some string. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(TypeError, 'salmon'); // Recommended + * expect(badFn).to.not.throw(ReferenceError, 'x'); // Not recommended + * + * `.throw` changes the target of any assertions that follow in the chain to + * be the error object that's thrown. + * + * var err = new TypeError('Illegal salmon!'); + * err.code = 42; + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(TypeError).with.property('code', 42); + * + * `.throw` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. When not providing two arguments, always use + * the second form. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.throw(TypeError, 'x', 'nooo why fail??'); + * expect(goodFn, 'nooo why fail??').to.throw(); + * + * Due to limitations in ES5, `.throw` may not always work as expected when + * using a transpiler such as Babel or TypeScript. In particular, it may + * produce unexpected results when subclassing the built-in `Error` object and + * then passing the subclassed constructor to `.throw`. See your transpiler's + * docs for details: + * + * - ([Babel](https://babeljs.io/docs/usage/caveats/#classes)) + * - ([TypeScript](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work)) + * + * Beware of some common mistakes when using the `throw` assertion. One common + * mistake is to accidentally invoke the function yourself instead of letting + * the `throw` assertion invoke the function for you. For example, when + * testing if a function named `fn` throws, provide `fn` instead of `fn()` as + * the target for the assertion. + * + * expect(fn).to.throw(); // Good! Tests `fn` as desired + * expect(fn()).to.throw(); // Bad! Tests result of `fn()`, not `fn` + * + * If you need to assert that your function `fn` throws when passed certain + * arguments, then wrap a call to `fn` inside of another function. + * + * expect(function () { fn(42); }).to.throw(); // Function expression + * expect(() => fn(42)).to.throw(); // ES6 arrow function + * + * Another common mistake is to provide an object method (or any stand-alone + * function that relies on `this`) as the target of the assertion. Doing so is + * problematic because the `this` context will be lost when the function is + * invoked by `.throw`; there's no way for it to know what `this` is supposed + * to be. There are two ways around this problem. One solution is to wrap the + * method or function call inside of another function. Another solution is to + * use `bind`. + * + * expect(function () { cat.meow(); }).to.throw(); // Function expression + * expect(() => cat.meow()).to.throw(); // ES6 arrow function + * expect(cat.meow.bind(cat)).to.throw(); // Bind + * + * Finally, it's worth mentioning that it's a best practice in JavaScript to + * only throw `Error` and derivatives of `Error` such as `ReferenceError`, + * `TypeError`, and user-defined objects that extend `Error`. No other type of + * value will generate a stack trace when initialized. With that said, the + * `throw` assertion does technically support any type of value being thrown, + * not just `Error` and its derivatives. + * + * The aliases `.throws` and `.Throw` can be used interchangeably with + * `.throw`. + * + * @name throw + * @alias throws + * @alias Throw + * @param {Error|ErrorConstructor} errorLike + * @param {String|RegExp} errMsgMatcher error message + * @param {String} msg _optional_ + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @returns error for chaining (null if no error) + * @namespace BDD + * @api public + */ + + function assertThrows (errorLike, errMsgMatcher, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , flagMsg = flag(this, 'message') + , negate = flag(this, 'negate') || false; + new Assertion(obj, flagMsg, ssfi, true).is.a('function'); + + if (errorLike instanceof RegExp || typeof errorLike === 'string') { + errMsgMatcher = errorLike; + errorLike = null; + } + + var caughtErr; + try { + obj(); + } catch (err) { + caughtErr = err; + } + + // If we have the negate flag enabled and at least one valid argument it means we do expect an error + // but we want it to match a given set of criteria + var everyArgIsUndefined = errorLike === undefined && errMsgMatcher === undefined; + + // If we've got the negate flag enabled and both args, we should only fail if both aren't compatible + // See Issue #551 and PR #683@GitHub + var everyArgIsDefined = Boolean(errorLike && errMsgMatcher); + var errorLikeFail = false; + var errMsgMatcherFail = false; + + // Checking if error was thrown + if (everyArgIsUndefined || !everyArgIsUndefined && !negate) { + // We need this to display results correctly according to their types + var errorLikeString = 'an error'; + if (errorLike instanceof Error) { + errorLikeString = '#{exp}'; + } else if (errorLike) { + errorLikeString = _.checkError.getConstructorName(errorLike); + } + + this.assert( + caughtErr + , 'expected #{this} to throw ' + errorLikeString + , 'expected #{this} to not throw an error but #{act} was thrown' + , errorLike && errorLike.toString() + , (caughtErr instanceof Error ? + caughtErr.toString() : (typeof caughtErr === 'string' ? caughtErr : caughtErr && + _.checkError.getConstructorName(caughtErr))) + ); + } + + if (errorLike && caughtErr) { + // We should compare instances only if `errorLike` is an instance of `Error` + if (errorLike instanceof Error) { + var isCompatibleInstance = _.checkError.compatibleInstance(caughtErr, errorLike); + + if (isCompatibleInstance === negate) { + // These checks were created to ensure we won't fail too soon when we've got both args and a negate + // See Issue #551 and PR #683@GitHub + if (everyArgIsDefined && negate) { + errorLikeFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr && !negate ? ' but #{act} was thrown' : '') + , errorLike.toString() + , caughtErr.toString() + ); + } + } + } + + var isCompatibleConstructor = _.checkError.compatibleConstructor(caughtErr, errorLike); + if (isCompatibleConstructor === negate) { + if (everyArgIsDefined && negate) { + errorLikeFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') + , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) + , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) + ); + } + } + } + + if (caughtErr && errMsgMatcher !== undefined && errMsgMatcher !== null) { + // Here we check compatible messages + var placeholder = 'including'; + if (errMsgMatcher instanceof RegExp) { + placeholder = 'matching' + } + + var isCompatibleMessage = _.checkError.compatibleMessage(caughtErr, errMsgMatcher); + if (isCompatibleMessage === negate) { + if (everyArgIsDefined && negate) { + errMsgMatcherFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw error ' + placeholder + ' #{exp} but got #{act}' + , 'expected #{this} to throw error not ' + placeholder + ' #{exp}' + , errMsgMatcher + , _.checkError.getMessage(caughtErr) + ); + } + } + } + + // If both assertions failed and both should've matched we throw an error + if (errorLikeFail && errMsgMatcherFail) { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') + , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) + , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) + ); + } + + flag(this, 'object', caughtErr); + }; + + Assertion.addMethod('throw', assertThrows); + Assertion.addMethod('throws', assertThrows); + Assertion.addMethod('Throw', assertThrows); + + /** + * ### .respondTo(method[, msg]) + * + * When the target is a non-function object, `.respondTo` asserts that the + * target has a method with the given name `method`. The method can be own or + * inherited, and it can be enumerable or non-enumerable. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(new Cat()).to.respondTo('meow'); + * + * When the target is a function, `.respondTo` asserts that the target's + * `prototype` property has a method with the given name `method`. Again, the + * method can be own or inherited, and it can be enumerable or non-enumerable. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(Cat).to.respondTo('meow'); + * + * Add `.itself` earlier in the chain to force `.respondTo` to treat the + * target as a non-function object, even if it's a function. Thus, it asserts + * that the target has a method with the given name `method`, rather than + * asserting that the target's `prototype` property has a method with the + * given name `method`. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * Cat.hiss = function () {}; + * + * expect(Cat).itself.to.respondTo('hiss').but.not.respondTo('meow'); + * + * When not adding `.itself`, it's important to check the target's type before + * using `.respondTo`. See the `.a` doc for info on checking a target's type. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(new Cat()).to.be.an('object').that.respondsTo('meow'); + * + * Add `.not` earlier in the chain to negate `.respondTo`. + * + * function Dog () {} + * Dog.prototype.bark = function () {}; + * + * expect(new Dog()).to.not.respondTo('meow'); + * + * `.respondTo` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect({}).to.respondTo('meow', 'nooo why fail??'); + * expect({}, 'nooo why fail??').to.respondTo('meow'); + * + * The alias `.respondsTo` can be used interchangeably with `.respondTo`. + * + * @name respondTo + * @alias respondsTo + * @param {String} method + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function respondTo (method, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , itself = flag(this, 'itself') + , context = ('function' === typeof obj && !itself) + ? obj.prototype[method] + : obj[method]; + + this.assert( + 'function' === typeof context + , 'expected #{this} to respond to ' + _.inspect(method) + , 'expected #{this} to not respond to ' + _.inspect(method) + ); + } + + Assertion.addMethod('respondTo', respondTo); + Assertion.addMethod('respondsTo', respondTo); + + /** + * ### .itself + * + * Forces all `.respondTo` assertions that follow in the chain to behave as if + * the target is a non-function object, even if it's a function. Thus, it + * causes `.respondTo` to assert that the target has a method with the given + * name, rather than asserting that the target's `prototype` property has a + * method with the given name. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * Cat.hiss = function () {}; + * + * expect(Cat).itself.to.respondTo('hiss').but.not.respondTo('meow'); + * + * @name itself + * @namespace BDD + * @api public + */ + + Assertion.addProperty('itself', function () { + flag(this, 'itself', true); + }); + + /** + * ### .satisfy(matcher[, msg]) + * + * Invokes the given `matcher` function with the target being passed as the + * first argument, and asserts that the value returned is truthy. + * + * expect(1).to.satisfy(function(num) { + * return num > 0; + * }); + * + * Add `.not` earlier in the chain to negate `.satisfy`. + * + * expect(1).to.not.satisfy(function(num) { + * return num > 2; + * }); + * + * `.satisfy` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1).to.satisfy(function(num) { + * return num > 2; + * }, 'nooo why fail??'); + * + * expect(1, 'nooo why fail??').to.satisfy(function(num) { + * return num > 2; + * }); + * + * The alias `.satisfies` can be used interchangeably with `.satisfy`. + * + * @name satisfy + * @alias satisfies + * @param {Function} matcher + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function satisfy (matcher, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var result = matcher(obj); + this.assert( + result + , 'expected #{this} to satisfy ' + _.objDisplay(matcher) + , 'expected #{this} to not satisfy' + _.objDisplay(matcher) + , flag(this, 'negate') ? false : true + , result + ); + } + + Assertion.addMethod('satisfy', satisfy); + Assertion.addMethod('satisfies', satisfy); + + /** + * ### .closeTo(expected, delta[, msg]) + * + * Asserts that the target is a number that's within a given +/- `delta` range + * of the given number `expected`. However, it's often best to assert that the + * target is equal to its expected value. + * + * // Recommended + * expect(1.5).to.equal(1.5); + * + * // Not recommended + * expect(1.5).to.be.closeTo(1, 0.5); + * expect(1.5).to.be.closeTo(2, 0.5); + * expect(1.5).to.be.closeTo(1, 1); + * + * Add `.not` earlier in the chain to negate `.closeTo`. + * + * expect(1.5).to.equal(1.5); // Recommended + * expect(1.5).to.not.be.closeTo(3, 1); // Not recommended + * + * `.closeTo` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1.5).to.be.closeTo(3, 1, 'nooo why fail??'); + * expect(1.5, 'nooo why fail??').to.be.closeTo(3, 1); + * + * The alias `.approximately` can be used interchangeably with `.closeTo`. + * + * @name closeTo + * @alias approximately + * @param {Number} expected + * @param {Number} delta + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function closeTo(expected, delta, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + if (typeof expected !== 'number' || typeof delta !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the arguments to closeTo or approximately must be numbers', + undefined, + ssfi + ); + } + + this.assert( + Math.abs(obj - expected) <= delta + , 'expected #{this} to be close to ' + expected + ' +/- ' + delta + , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta + ); + } + + Assertion.addMethod('closeTo', closeTo); + Assertion.addMethod('approximately', closeTo); + + // Note: Duplicates are ignored if testing for inclusion instead of sameness. + function isSubsetOf(subset, superset, cmp, contains, ordered) { + if (!contains) { + if (subset.length !== superset.length) return false; + superset = superset.slice(); + } + + return subset.every(function(elem, idx) { + if (ordered) return cmp ? cmp(elem, superset[idx]) : elem === superset[idx]; + + if (!cmp) { + var matchIdx = superset.indexOf(elem); + if (matchIdx === -1) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + } + + return superset.some(function(elem2, matchIdx) { + if (!cmp(elem, elem2)) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + }); + }); + } + + /** + * ### .members(set[, msg]) + * + * Asserts that the target array has the same members as the given array + * `set`. + * + * expect([1, 2, 3]).to.have.members([2, 1, 3]); + * expect([1, 2, 2]).to.have.members([2, 1, 2]); + * + * By default, members are compared using strict (`===`) equality. Add `.deep` + * earlier in the chain to use deep equality instead. See the `deep-eql` + * project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target array deeply (but not strictly) has member `{a: 1}` + * expect([{a: 1}]).to.have.deep.members([{a: 1}]); + * expect([{a: 1}]).to.not.have.members([{a: 1}]); + * + * By default, order doesn't matter. Add `.ordered` earlier in the chain to + * require that members appear in the same order. + * + * expect([1, 2, 3]).to.have.ordered.members([1, 2, 3]); + * expect([1, 2, 3]).to.have.members([2, 1, 3]) + * .but.not.ordered.members([2, 1, 3]); + * + * By default, both arrays must be the same size. Add `.include` earlier in + * the chain to require that the target's members be a superset of the + * expected members. Note that duplicates are ignored in the subset when + * `.include` is added. + * + * // Target array is a superset of [1, 2] but not identical + * expect([1, 2, 3]).to.include.members([1, 2]); + * expect([1, 2, 3]).to.not.have.members([1, 2]); + * + * // Duplicates in the subset are ignored + * expect([1, 2, 3]).to.include.members([1, 2, 2, 2]); + * + * `.deep`, `.ordered`, and `.include` can all be combined. However, if + * `.include` and `.ordered` are combined, the ordering begins at the start of + * both arrays. + * + * expect([{a: 1}, {b: 2}, {c: 3}]) + * .to.include.deep.ordered.members([{a: 1}, {b: 2}]) + * .but.not.include.deep.ordered.members([{b: 2}, {c: 3}]); + * + * Add `.not` earlier in the chain to negate `.members`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the target array doesn't have all of the same members as + * the given array `set` but may or may not have some of them. It's often best + * to identify the exact output that's expected, and then write an assertion + * that only accepts that exact output. + * + * expect([1, 2]).to.not.include(3).and.not.include(4); // Recommended + * expect([1, 2]).to.not.have.members([3, 4]); // Not recommended + * + * `.members` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2]).to.have.members([1, 2, 3], 'nooo why fail??'); + * expect([1, 2], 'nooo why fail??').to.have.members([1, 2, 3]); + * + * @name members + * @param {Array} set + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + Assertion.addMethod('members', function (subset, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + new Assertion(obj, flagMsg, ssfi, true).to.be.an('array'); + new Assertion(subset, flagMsg, ssfi, true).to.be.an('array'); + + var contains = flag(this, 'contains'); + var ordered = flag(this, 'ordered'); + + var subject, failMsg, failNegateMsg; + + if (contains) { + subject = ordered ? 'an ordered superset' : 'a superset'; + failMsg = 'expected #{this} to be ' + subject + ' of #{exp}'; + failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}'; + } else { + subject = ordered ? 'ordered members' : 'members'; + failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}'; + failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}'; + } + + var cmp = flag(this, 'deep') ? _.eql : undefined; + + this.assert( + isSubsetOf(subset, obj, cmp, contains, ordered) + , failMsg + , failNegateMsg + , subset + , obj + , true + ); + }); + + /** + * ### .oneOf(list[, msg]) + * + * Asserts that the target is a member of the given array `list`. However, + * it's often best to assert that the target is equal to its expected value. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.oneOf([1, 2, 3]); // Not recommended + * + * Comparisons are performed using strict (`===`) equality. + * + * Add `.not` earlier in the chain to negate `.oneOf`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.oneOf([2, 3, 4]); // Not recommended + * + * `.oneOf` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.oneOf([2, 3, 4], 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.oneOf([2, 3, 4]); + * + * @name oneOf + * @param {Array<*>} list + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function oneOf (list, msg) { + if (msg) flag(this, 'message', msg); + var expected = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(list, flagMsg, ssfi, true).to.be.an('array'); + + this.assert( + list.indexOf(expected) > -1 + , 'expected #{this} to be one of #{exp}' + , 'expected #{this} to not be one of #{exp}' + , list + , expected + ); + } + + Assertion.addMethod('oneOf', oneOf); + + /** + * ### .change(subject[, prop[, msg]]) + * + * When one argument is provided, `.change` asserts that the given function + * `subject` returns a different value when it's invoked before the target + * function compared to when it's invoked afterward. However, it's often best + * to assert that `subject` is equal to its expected value. + * + * var dots = '' + * , addDot = function () { dots += '.'; } + * , getDots = function () { return dots; }; + * + * // Recommended + * expect(getDots()).to.equal(''); + * addDot(); + * expect(getDots()).to.equal('.'); + * + * // Not recommended + * expect(addDot).to.change(getDots); + * + * When two arguments are provided, `.change` asserts that the value of the + * given object `subject`'s `prop` property is different before invoking the + * target function compared to afterward. + * + * var myObj = {dots: ''} + * , addDot = function () { myObj.dots += '.'; }; + * + * // Recommended + * expect(myObj).to.have.property('dots', ''); + * addDot(); + * expect(myObj).to.have.property('dots', '.'); + * + * // Not recommended + * expect(addDot).to.change(myObj, 'dots'); + * + * Strict (`===`) equality is used to compare before and after values. + * + * Add `.not` earlier in the chain to negate `.change`. + * + * var dots = '' + * , noop = function () {} + * , getDots = function () { return dots; }; + * + * expect(noop).to.not.change(getDots); + * + * var myObj = {dots: ''} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'dots'); + * + * `.change` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {dots: ''} + * , addDot = function () { myObj.dots += '.'; }; + * + * expect(addDot).to.not.change(myObj, 'dots', 'nooo why fail??'); + * + * var dots = '' + * , addDot = function () { dots += '.'; } + * , getDots = function () { return dots; }; + * + * expect(addDot, 'nooo why fail??').to.not.change(getDots); + * + * `.change` also causes all `.by` assertions that follow in the chain to + * assert how much a numeric subject was increased or decreased by. However, + * it's dangerous to use `.change.by`. The problem is that it creates + * uncertain expectations by asserting that the subject either increases by + * the given delta, or that it decreases by the given delta. It's often best + * to identify the exact output that's expected, and then write an assertion + * that only accepts that exact output. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; } + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * The alias `.changes` can be used interchangeably with `.change`. + * + * @name change + * @alias changes + * @param {String} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertChanges (subject, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + fn(); + + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + // This gets flagged because of the .by(delta) assertion + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'change'); + flag(this, 'realDelta', final !== initial); + + this.assert( + initial !== final + , 'expected ' + msgObj + ' to change' + , 'expected ' + msgObj + ' to not change' + ); + } + + Assertion.addMethod('change', assertChanges); + Assertion.addMethod('changes', assertChanges); + + /** + * ### .increase(subject[, prop[, msg]]) + * + * When one argument is provided, `.increase` asserts that the given function + * `subject` returns a greater number when it's invoked after invoking the + * target function compared to when it's invoked beforehand. `.increase` also + * causes all `.by` assertions that follow in the chain to assert how much + * greater of a number is returned. It's often best to assert that the return + * value increased by the expected amount, rather than asserting it increased + * by any amount. + * + * var val = 1 + * , addTwo = function () { val += 2; } + * , getVal = function () { return val; }; + * + * expect(addTwo).to.increase(getVal).by(2); // Recommended + * expect(addTwo).to.increase(getVal); // Not recommended + * + * When two arguments are provided, `.increase` asserts that the value of the + * given object `subject`'s `prop` property is greater after invoking the + * target function compared to beforehand. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.increase(myObj, 'val'); // Not recommended + * + * Add `.not` earlier in the chain to negate `.increase`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either decreases, or that it stays the same. + * It's often best to identify the exact output that's expected, and then + * write an assertion that only accepts that exact output. + * + * When the subject is expected to decrease, it's often best to assert that it + * decreased by the expected amount. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.not.increase(myObj, 'val'); // Not recommended + * + * When the subject is expected to stay the same, it's often best to assert + * exactly that. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'val'); // Recommended + * expect(noop).to.not.increase(myObj, 'val'); // Not recommended + * + * `.increase` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.increase(myObj, 'val', 'nooo why fail??'); + * + * var val = 1 + * , noop = function () {} + * , getVal = function () { return val; }; + * + * expect(noop, 'nooo why fail??').to.increase(getVal); + * + * The alias `.increases` can be used interchangeably with `.increase`. + * + * @name increase + * @alias increases + * @param {String|Function} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertIncreases (subject, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + // Make sure that the target is a number + new Assertion(initial, flagMsg, ssfi, true).is.a('number'); + + fn(); + + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'increase'); + flag(this, 'realDelta', final - initial); + + this.assert( + final - initial > 0 + , 'expected ' + msgObj + ' to increase' + , 'expected ' + msgObj + ' to not increase' + ); + } + + Assertion.addMethod('increase', assertIncreases); + Assertion.addMethod('increases', assertIncreases); + + /** + * ### .decrease(subject[, prop[, msg]]) + * + * When one argument is provided, `.decrease` asserts that the given function + * `subject` returns a lesser number when it's invoked after invoking the + * target function compared to when it's invoked beforehand. `.decrease` also + * causes all `.by` assertions that follow in the chain to assert how much + * lesser of a number is returned. It's often best to assert that the return + * value decreased by the expected amount, rather than asserting it decreased + * by any amount. + * + * var val = 1 + * , subtractTwo = function () { val -= 2; } + * , getVal = function () { return val; }; + * + * expect(subtractTwo).to.decrease(getVal).by(2); // Recommended + * expect(subtractTwo).to.decrease(getVal); // Not recommended + * + * When two arguments are provided, `.decrease` asserts that the value of the + * given object `subject`'s `prop` property is lesser after invoking the + * target function compared to beforehand. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.decrease(myObj, 'val'); // Not recommended + * + * Add `.not` earlier in the chain to negate `.decrease`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either increases, or that it stays the same. + * It's often best to identify the exact output that's expected, and then + * write an assertion that only accepts that exact output. + * + * When the subject is expected to increase, it's often best to assert that it + * increased by the expected amount. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.not.decrease(myObj, 'val'); // Not recommended + * + * When the subject is expected to stay the same, it's often best to assert + * exactly that. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'val'); // Recommended + * expect(noop).to.not.decrease(myObj, 'val'); // Not recommended + * + * `.decrease` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.decrease(myObj, 'val', 'nooo why fail??'); + * + * var val = 1 + * , noop = function () {} + * , getVal = function () { return val; }; + * + * expect(noop, 'nooo why fail??').to.decrease(getVal); + * + * The alias `.decreases` can be used interchangeably with `.decrease`. + * + * @name decrease + * @alias decreases + * @param {String|Function} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertDecreases (subject, prop, msg) { + if (msg) flag(this, 'message', msg); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + // Make sure that the target is a number + new Assertion(initial, flagMsg, ssfi, true).is.a('number'); + + fn(); + + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'decrease'); + flag(this, 'realDelta', initial - final); + + this.assert( + final - initial < 0 + , 'expected ' + msgObj + ' to decrease' + , 'expected ' + msgObj + ' to not decrease' + ); + } + + Assertion.addMethod('decrease', assertDecreases); + Assertion.addMethod('decreases', assertDecreases); + + /** + * ### .by(delta[, msg]) + * + * When following an `.increase` assertion in the chain, `.by` asserts that + * the subject of the `.increase` assertion increased by the given `delta`. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); + * + * When following a `.decrease` assertion in the chain, `.by` asserts that the + * subject of the `.decrease` assertion decreased by the given `delta`. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); + * + * When following a `.change` assertion in the chain, `.by` asserts that the + * subject of the `.change` assertion either increased or decreased by the + * given `delta`. However, it's dangerous to use `.change.by`. The problem is + * that it creates uncertain expectations. It's often best to identify the + * exact output that's expected, and then write an assertion that only accepts + * that exact output. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; } + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.by`. However, it's often best + * to assert that the subject changed by its expected delta, rather than + * asserting that it didn't change by one of countless unexpected deltas. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * // Recommended + * expect(addTwo).to.increase(myObj, 'val').by(2); + * + * // Not recommended + * expect(addTwo).to.increase(myObj, 'val').but.not.by(3); + * + * `.by` accepts an optional `msg` argument which is a custom error message to + * show when the assertion fails. The message can also be given as the second + * argument to `expect`. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(3, 'nooo why fail??'); + * expect(addTwo, 'nooo why fail??').to.increase(myObj, 'val').by(3); + * + * @name by + * @param {Number} delta + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertDelta(delta, msg) { + if (msg) flag(this, 'message', msg); + + var msgObj = flag(this, 'deltaMsgObj'); + var initial = flag(this, 'initialDeltaValue'); + var final = flag(this, 'finalDeltaValue'); + var behavior = flag(this, 'deltaBehavior'); + var realDelta = flag(this, 'realDelta'); + + var expression; + if (behavior === 'change') { + expression = Math.abs(final - initial) === Math.abs(delta); + } else { + expression = realDelta === Math.abs(delta); + } + + this.assert( + expression + , 'expected ' + msgObj + ' to ' + behavior + ' by ' + delta + , 'expected ' + msgObj + ' to not ' + behavior + ' by ' + delta + ); + } + + Assertion.addMethod('by', assertDelta); + + /** + * ### .extensible + * + * Asserts that the target is extensible, which means that new properties can + * be added to it. Primitives are never extensible. + * + * expect({a: 1}).to.be.extensible; + * + * Add `.not` earlier in the chain to negate `.extensible`. + * + * var nonExtensibleObject = Object.preventExtensions({}) + * , sealedObject = Object.seal({}) + * , frozenObject = Object.freeze({}); + * + * expect(nonExtensibleObject).to.not.be.extensible; + * expect(sealedObject).to.not.be.extensible; + * expect(frozenObject).to.not.be.extensible; + * expect(1).to.not.be.extensible; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(1, 'nooo why fail??').to.be.extensible; + * + * @name extensible + * @namespace BDD + * @api public + */ + + Assertion.addProperty('extensible', function() { + var obj = flag(this, 'object'); + + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. + // In ES6, a non-object argument will be treated as if it was a non-extensible ordinary object, simply return false. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible + // The following provides ES6 behavior for ES5 environments. + + var isExtensible = obj === Object(obj) && Object.isExtensible(obj); + + this.assert( + isExtensible + , 'expected #{this} to be extensible' + , 'expected #{this} to not be extensible' + ); + }); + + /** + * ### .sealed + * + * Asserts that the target is sealed, which means that new properties can't be + * added to it, and its existing properties can't be reconfigured or deleted. + * However, it's possible that its existing properties can still be reassigned + * to different values. Primitives are always sealed. + * + * var sealedObject = Object.seal({}); + * var frozenObject = Object.freeze({}); + * + * expect(sealedObject).to.be.sealed; + * expect(frozenObject).to.be.sealed; + * expect(1).to.be.sealed; + * + * Add `.not` earlier in the chain to negate `.sealed`. + * + * expect({a: 1}).to.not.be.sealed; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.be.sealed; + * + * @name sealed + * @namespace BDD + * @api public + */ + + Assertion.addProperty('sealed', function() { + var obj = flag(this, 'object'); + + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. + // In ES6, a non-object argument will be treated as if it was a sealed ordinary object, simply return true. + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed + // The following provides ES6 behavior for ES5 environments. + + var isSealed = obj === Object(obj) ? Object.isSealed(obj) : true; + + this.assert( + isSealed + , 'expected #{this} to be sealed' + , 'expected #{this} to not be sealed' + ); + }); + + /** + * ### .frozen + * + * Asserts that the target is frozen, which means that new properties can't be + * added to it, and its existing properties can't be reassigned to different + * values, reconfigured, or deleted. Primitives are always frozen. + * + * var frozenObject = Object.freeze({}); + * + * expect(frozenObject).to.be.frozen; + * expect(1).to.be.frozen; + * + * Add `.not` earlier in the chain to negate `.frozen`. + * + * expect({a: 1}).to.not.be.frozen; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.be.frozen; + * + * @name frozen + * @namespace BDD + * @api public + */ + + Assertion.addProperty('frozen', function() { + var obj = flag(this, 'object'); + + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. + // In ES6, a non-object argument will be treated as if it was a frozen ordinary object, simply return true. + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen + // The following provides ES6 behavior for ES5 environments. + + var isFrozen = obj === Object(obj) ? Object.isFrozen(obj) : true; + + this.assert( + isFrozen + , 'expected #{this} to be frozen' + , 'expected #{this} to not be frozen' + ); + }); + + /** + * ### .finite + * + * Asserts that the target is a number, and isn't `NaN` or positive/negative + * `Infinity`. + * + * expect(1).to.be.finite; + * + * Add `.not` earlier in the chain to negate `.finite`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either isn't a number, or that it's `NaN`, or + * that it's positive `Infinity`, or that it's negative `Infinity`. It's often + * best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to be a number, it's often best to assert + * that it's the expected type, rather than asserting that it isn't one of + * many unexpected types. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.finite; // Not recommended + * + * When the target is expected to be `NaN`, it's often best to assert exactly + * that. + * + * expect(NaN).to.be.NaN; // Recommended + * expect(NaN).to.not.be.finite; // Not recommended + * + * When the target is expected to be positive infinity, it's often best to + * assert exactly that. + * + * expect(Infinity).to.equal(Infinity); // Recommended + * expect(Infinity).to.not.be.finite; // Not recommended + * + * When the target is expected to be negative infinity, it's often best to + * assert exactly that. + * + * expect(-Infinity).to.equal(-Infinity); // Recommended + * expect(-Infinity).to.not.be.finite; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect('foo', 'nooo why fail??').to.be.finite; + * + * @name finite + * @namespace BDD + * @api public + */ + + Assertion.addProperty('finite', function(msg) { + var obj = flag(this, 'object'); + + this.assert( + typeof obj === 'number' && isFinite(obj) + , 'expected #{this} to be a finite number' + , 'expected #{this} to not be a finite number' + ); + }); +}; diff --git a/WebServer/node_modules/chai/lib/chai/interface/assert.js b/WebServer/node_modules/chai/lib/chai/interface/assert.js new file mode 100644 index 000000000..2099c9b6e --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/interface/assert.js @@ -0,0 +1,3113 @@ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + /*! + * Chai dependencies. + */ + + var Assertion = chai.Assertion + , flag = util.flag; + + /*! + * Module export. + */ + + /** + * ### assert(expression, message) + * + * Write your own test expressions. + * + * assert('foo' !== 'bar', 'foo is not bar'); + * assert(Array.isArray([]), 'empty arrays are arrays'); + * + * @param {Mixed} expression to test for truthiness + * @param {String} message to display on error + * @name assert + * @namespace Assert + * @api public + */ + + var assert = chai.assert = function (express, errmsg) { + var test = new Assertion(null, null, chai.assert, true); + test.assert( + express + , errmsg + , '[ negation message unavailable ]' + ); + }; + + /** + * ### .fail([message]) + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. Node.js `assert` module-compatible. + * + * assert.fail(); + * assert.fail("custom error message"); + * assert.fail(1, 2); + * assert.fail(1, 2, "custom error message"); + * assert.fail(1, 2, "custom error message", ">"); + * assert.fail(1, 2, undefined, ">"); + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @namespace Assert + * @api public + */ + + assert.fail = function (actual, expected, message, operator) { + if (arguments.length < 2) { + // Comply with Node's fail([message]) interface + + message = actual; + actual = undefined; + } + + message = message || 'assert.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, assert.fail); + }; + + /** + * ### .isOk(object, [message]) + * + * Asserts that `object` is truthy. + * + * assert.isOk('everything', 'everything is ok'); + * assert.isOk(false, 'this will fail'); + * + * @name isOk + * @alias ok + * @param {Mixed} object to test + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isOk = function (val, msg) { + new Assertion(val, msg, assert.isOk, true).is.ok; + }; + + /** + * ### .isNotOk(object, [message]) + * + * Asserts that `object` is falsy. + * + * assert.isNotOk('everything', 'this will fail'); + * assert.isNotOk(false, 'this will pass'); + * + * @name isNotOk + * @alias notOk + * @param {Mixed} object to test + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotOk = function (val, msg) { + new Assertion(val, msg, assert.isNotOk, true).is.not.ok; + }; + + /** + * ### .equal(actual, expected, [message]) + * + * Asserts non-strict equality (`==`) of `actual` and `expected`. + * + * assert.equal(3, '3', '== coerces values to strings'); + * + * @name equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.equal = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.equal, true); + + test.assert( + exp == flag(test, 'object') + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{act}' + , exp + , act + , true + ); + }; + + /** + * ### .notEqual(actual, expected, [message]) + * + * Asserts non-strict inequality (`!=`) of `actual` and `expected`. + * + * assert.notEqual(3, 4, 'these numbers are not equal'); + * + * @name notEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notEqual = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.notEqual, true); + + test.assert( + exp != flag(test, 'object') + , 'expected #{this} to not equal #{exp}' + , 'expected #{this} to equal #{act}' + , exp + , act + , true + ); + }; + + /** + * ### .strictEqual(actual, expected, [message]) + * + * Asserts strict equality (`===`) of `actual` and `expected`. + * + * assert.strictEqual(true, true, 'these booleans are strictly equal'); + * + * @name strictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.strictEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.strictEqual, true).to.equal(exp); + }; + + /** + * ### .notStrictEqual(actual, expected, [message]) + * + * Asserts strict inequality (`!==`) of `actual` and `expected`. + * + * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); + * + * @name notStrictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notStrictEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.notStrictEqual, true).to.not.equal(exp); + }; + + /** + * ### .deepEqual(actual, expected, [message]) + * + * Asserts that `actual` is deeply equal to `expected`. + * + * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); + * + * @name deepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @alias deepStrictEqual + * @namespace Assert + * @api public + */ + + assert.deepEqual = assert.deepStrictEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.deepEqual, true).to.eql(exp); + }; + + /** + * ### .notDeepEqual(actual, expected, [message]) + * + * Assert that `actual` is not deeply equal to `expected`. + * + * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); + * + * @name notDeepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.notDeepEqual, true).to.not.eql(exp); + }; + + /** + * ### .isAbove(valueToCheck, valueToBeAbove, [message]) + * + * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove`. + * + * assert.isAbove(5, 2, '5 is strictly greater than 2'); + * + * @name isAbove + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAbove + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isAbove = function (val, abv, msg) { + new Assertion(val, msg, assert.isAbove, true).to.be.above(abv); + }; + + /** + * ### .isAtLeast(valueToCheck, valueToBeAtLeast, [message]) + * + * Asserts `valueToCheck` is greater than or equal to (>=) `valueToBeAtLeast`. + * + * assert.isAtLeast(5, 2, '5 is greater or equal to 2'); + * assert.isAtLeast(3, 3, '3 is greater or equal to 3'); + * + * @name isAtLeast + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAtLeast + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isAtLeast = function (val, atlst, msg) { + new Assertion(val, msg, assert.isAtLeast, true).to.be.least(atlst); + }; + + /** + * ### .isBelow(valueToCheck, valueToBeBelow, [message]) + * + * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow`. + * + * assert.isBelow(3, 6, '3 is strictly less than 6'); + * + * @name isBelow + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeBelow + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isBelow = function (val, blw, msg) { + new Assertion(val, msg, assert.isBelow, true).to.be.below(blw); + }; + + /** + * ### .isAtMost(valueToCheck, valueToBeAtMost, [message]) + * + * Asserts `valueToCheck` is less than or equal to (<=) `valueToBeAtMost`. + * + * assert.isAtMost(3, 6, '3 is less than or equal to 6'); + * assert.isAtMost(4, 4, '4 is less than or equal to 4'); + * + * @name isAtMost + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAtMost + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isAtMost = function (val, atmst, msg) { + new Assertion(val, msg, assert.isAtMost, true).to.be.most(atmst); + }; + + /** + * ### .isTrue(value, [message]) + * + * Asserts that `value` is true. + * + * var teaServed = true; + * assert.isTrue(teaServed, 'the tea has been served'); + * + * @name isTrue + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isTrue = function (val, msg) { + new Assertion(val, msg, assert.isTrue, true).is['true']; + }; + + /** + * ### .isNotTrue(value, [message]) + * + * Asserts that `value` is not true. + * + * var tea = 'tasty chai'; + * assert.isNotTrue(tea, 'great, time for tea!'); + * + * @name isNotTrue + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotTrue = function (val, msg) { + new Assertion(val, msg, assert.isNotTrue, true).to.not.equal(true); + }; + + /** + * ### .isFalse(value, [message]) + * + * Asserts that `value` is false. + * + * var teaServed = false; + * assert.isFalse(teaServed, 'no tea yet? hmm...'); + * + * @name isFalse + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isFalse = function (val, msg) { + new Assertion(val, msg, assert.isFalse, true).is['false']; + }; + + /** + * ### .isNotFalse(value, [message]) + * + * Asserts that `value` is not false. + * + * var tea = 'tasty chai'; + * assert.isNotFalse(tea, 'great, time for tea!'); + * + * @name isNotFalse + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotFalse = function (val, msg) { + new Assertion(val, msg, assert.isNotFalse, true).to.not.equal(false); + }; + + /** + * ### .isNull(value, [message]) + * + * Asserts that `value` is null. + * + * assert.isNull(err, 'there was no error'); + * + * @name isNull + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNull = function (val, msg) { + new Assertion(val, msg, assert.isNull, true).to.equal(null); + }; + + /** + * ### .isNotNull(value, [message]) + * + * Asserts that `value` is not null. + * + * var tea = 'tasty chai'; + * assert.isNotNull(tea, 'great, time for tea!'); + * + * @name isNotNull + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotNull = function (val, msg) { + new Assertion(val, msg, assert.isNotNull, true).to.not.equal(null); + }; + + /** + * ### .isNaN + * + * Asserts that value is NaN. + * + * assert.isNaN(NaN, 'NaN is NaN'); + * + * @name isNaN + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNaN = function (val, msg) { + new Assertion(val, msg, assert.isNaN, true).to.be.NaN; + }; + + /** + * ### .isNotNaN + * + * Asserts that value is not NaN. + * + * assert.isNotNaN(4, '4 is not NaN'); + * + * @name isNotNaN + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + assert.isNotNaN = function (val, msg) { + new Assertion(val, msg, assert.isNotNaN, true).not.to.be.NaN; + }; + + /** + * ### .exists + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi'; + * + * assert.exists(foo, 'foo is neither `null` nor `undefined`'); + * + * @name exists + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.exists = function (val, msg) { + new Assertion(val, msg, assert.exists, true).to.exist; + }; + + /** + * ### .notExists + * + * Asserts that the target is either `null` or `undefined`. + * + * var bar = null + * , baz; + * + * assert.notExists(bar); + * assert.notExists(baz, 'baz is either null or undefined'); + * + * @name notExists + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notExists = function (val, msg) { + new Assertion(val, msg, assert.notExists, true).to.not.exist; + }; + + /** + * ### .isUndefined(value, [message]) + * + * Asserts that `value` is `undefined`. + * + * var tea; + * assert.isUndefined(tea, 'no tea defined'); + * + * @name isUndefined + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isUndefined = function (val, msg) { + new Assertion(val, msg, assert.isUndefined, true).to.equal(undefined); + }; + + /** + * ### .isDefined(value, [message]) + * + * Asserts that `value` is not `undefined`. + * + * var tea = 'cup of chai'; + * assert.isDefined(tea, 'tea has been defined'); + * + * @name isDefined + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isDefined = function (val, msg) { + new Assertion(val, msg, assert.isDefined, true).to.not.equal(undefined); + }; + + /** + * ### .isFunction(value, [message]) + * + * Asserts that `value` is a function. + * + * function serveTea() { return 'cup of tea'; }; + * assert.isFunction(serveTea, 'great, we can have tea now'); + * + * @name isFunction + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isFunction = function (val, msg) { + new Assertion(val, msg, assert.isFunction, true).to.be.a('function'); + }; + + /** + * ### .isNotFunction(value, [message]) + * + * Asserts that `value` is _not_ a function. + * + * var serveTea = [ 'heat', 'pour', 'sip' ]; + * assert.isNotFunction(serveTea, 'great, we have listed the steps'); + * + * @name isNotFunction + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotFunction = function (val, msg) { + new Assertion(val, msg, assert.isNotFunction, true).to.not.be.a('function'); + }; + + /** + * ### .isObject(value, [message]) + * + * Asserts that `value` is an object of type 'Object' (as revealed by `Object.prototype.toString`). + * _The assertion does not match subclassed objects._ + * + * var selection = { name: 'Chai', serve: 'with spices' }; + * assert.isObject(selection, 'tea selection is an object'); + * + * @name isObject + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isObject = function (val, msg) { + new Assertion(val, msg, assert.isObject, true).to.be.a('object'); + }; + + /** + * ### .isNotObject(value, [message]) + * + * Asserts that `value` is _not_ an object of type 'Object' (as revealed by `Object.prototype.toString`). + * + * var selection = 'chai' + * assert.isNotObject(selection, 'tea selection is not an object'); + * assert.isNotObject(null, 'null is not an object'); + * + * @name isNotObject + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotObject = function (val, msg) { + new Assertion(val, msg, assert.isNotObject, true).to.not.be.a('object'); + }; + + /** + * ### .isArray(value, [message]) + * + * Asserts that `value` is an array. + * + * var menu = [ 'green', 'chai', 'oolong' ]; + * assert.isArray(menu, 'what kind of tea do we want?'); + * + * @name isArray + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isArray = function (val, msg) { + new Assertion(val, msg, assert.isArray, true).to.be.an('array'); + }; + + /** + * ### .isNotArray(value, [message]) + * + * Asserts that `value` is _not_ an array. + * + * var menu = 'green|chai|oolong'; + * assert.isNotArray(menu, 'what kind of tea do we want?'); + * + * @name isNotArray + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotArray = function (val, msg) { + new Assertion(val, msg, assert.isNotArray, true).to.not.be.an('array'); + }; + + /** + * ### .isString(value, [message]) + * + * Asserts that `value` is a string. + * + * var teaOrder = 'chai'; + * assert.isString(teaOrder, 'order placed'); + * + * @name isString + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isString = function (val, msg) { + new Assertion(val, msg, assert.isString, true).to.be.a('string'); + }; + + /** + * ### .isNotString(value, [message]) + * + * Asserts that `value` is _not_ a string. + * + * var teaOrder = 4; + * assert.isNotString(teaOrder, 'order placed'); + * + * @name isNotString + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotString = function (val, msg) { + new Assertion(val, msg, assert.isNotString, true).to.not.be.a('string'); + }; + + /** + * ### .isNumber(value, [message]) + * + * Asserts that `value` is a number. + * + * var cups = 2; + * assert.isNumber(cups, 'how many cups'); + * + * @name isNumber + * @param {Number} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNumber = function (val, msg) { + new Assertion(val, msg, assert.isNumber, true).to.be.a('number'); + }; + + /** + * ### .isNotNumber(value, [message]) + * + * Asserts that `value` is _not_ a number. + * + * var cups = '2 cups please'; + * assert.isNotNumber(cups, 'how many cups'); + * + * @name isNotNumber + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotNumber = function (val, msg) { + new Assertion(val, msg, assert.isNotNumber, true).to.not.be.a('number'); + }; + + /** + * ### .isFinite(value, [message]) + * + * Asserts that `value` is a finite number. Unlike `.isNumber`, this will fail for `NaN` and `Infinity`. + * + * var cups = 2; + * assert.isFinite(cups, 'how many cups'); + * + * assert.isFinite(NaN); // throws + * + * @name isFinite + * @param {Number} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isFinite = function (val, msg) { + new Assertion(val, msg, assert.isFinite, true).to.be.finite; + }; + + /** + * ### .isBoolean(value, [message]) + * + * Asserts that `value` is a boolean. + * + * var teaReady = true + * , teaServed = false; + * + * assert.isBoolean(teaReady, 'is the tea ready'); + * assert.isBoolean(teaServed, 'has tea been served'); + * + * @name isBoolean + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isBoolean = function (val, msg) { + new Assertion(val, msg, assert.isBoolean, true).to.be.a('boolean'); + }; + + /** + * ### .isNotBoolean(value, [message]) + * + * Asserts that `value` is _not_ a boolean. + * + * var teaReady = 'yep' + * , teaServed = 'nope'; + * + * assert.isNotBoolean(teaReady, 'is the tea ready'); + * assert.isNotBoolean(teaServed, 'has tea been served'); + * + * @name isNotBoolean + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isNotBoolean = function (val, msg) { + new Assertion(val, msg, assert.isNotBoolean, true).to.not.be.a('boolean'); + }; + + /** + * ### .typeOf(value, name, [message]) + * + * Asserts that `value`'s type is `name`, as determined by + * `Object.prototype.toString`. + * + * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); + * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); + * assert.typeOf('tea', 'string', 'we have a string'); + * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); + * assert.typeOf(null, 'null', 'we have a null'); + * assert.typeOf(undefined, 'undefined', 'we have an undefined'); + * + * @name typeOf + * @param {Mixed} value + * @param {String} name + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.typeOf = function (val, type, msg) { + new Assertion(val, msg, assert.typeOf, true).to.be.a(type); + }; + + /** + * ### .notTypeOf(value, name, [message]) + * + * Asserts that `value`'s type is _not_ `name`, as determined by + * `Object.prototype.toString`. + * + * assert.notTypeOf('tea', 'number', 'strings are not numbers'); + * + * @name notTypeOf + * @param {Mixed} value + * @param {String} typeof name + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notTypeOf = function (val, type, msg) { + new Assertion(val, msg, assert.notTypeOf, true).to.not.be.a(type); + }; + + /** + * ### .instanceOf(object, constructor, [message]) + * + * Asserts that `value` is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new Tea('chai'); + * + * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); + * + * @name instanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.instanceOf = function (val, type, msg) { + new Assertion(val, msg, assert.instanceOf, true).to.be.instanceOf(type); + }; + + /** + * ### .notInstanceOf(object, constructor, [message]) + * + * Asserts `value` is not an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new String('chai'); + * + * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); + * + * @name notInstanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notInstanceOf = function (val, type, msg) { + new Assertion(val, msg, assert.notInstanceOf, true) + .to.not.be.instanceOf(type); + }; + + /** + * ### .include(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Can be used to assert the + * inclusion of a value in an array, a substring in a string, or a subset of + * properties in an object. + * + * assert.include([1,2,3], 2, 'array contains value'); + * assert.include('foobar', 'foo', 'string contains substring'); + * assert.include({ foo: 'bar', hello: 'universe' }, { foo: 'bar' }, 'object contains property'); + * + * Strict equality (===) is used. When asserting the inclusion of a value in + * an array, the array is searched for an element that's strictly equal to the + * given value. When asserting a subset of properties in an object, the object + * is searched for the given property keys, checking that each one is present + * and strictly equal to the given property value. For instance: + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.include([obj1, obj2], obj1); + * assert.include({foo: obj1, bar: obj2}, {foo: obj1}); + * assert.include({foo: obj1, bar: obj2}, {foo: obj1, bar: obj2}); + * + * @name include + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.include = function (exp, inc, msg) { + new Assertion(exp, msg, assert.include, true).include(inc); + }; + + /** + * ### .notInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Can be used to assert + * the absence of a value in an array, a substring in a string, or a subset of + * properties in an object. + * + * assert.notInclude([1,2,3], 4, "array doesn't contain value"); + * assert.notInclude('foobar', 'baz', "string doesn't contain substring"); + * assert.notInclude({ foo: 'bar', hello: 'universe' }, { foo: 'baz' }, 'object doesn't contain property'); + * + * Strict equality (===) is used. When asserting the absence of a value in an + * array, the array is searched to confirm the absence of an element that's + * strictly equal to the given value. When asserting a subset of properties in + * an object, the object is searched to confirm that at least one of the given + * property keys is either not present or not strictly equal to the given + * property value. For instance: + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.notInclude([obj1, obj2], {a: 1}); + * assert.notInclude({foo: obj1, bar: obj2}, {foo: {a: 1}}); + * assert.notInclude({foo: obj1, bar: obj2}, {foo: obj1, bar: {b: 2}}); + * + * @name notInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notInclude, true).not.include(inc); + }; + + /** + * ### .deepInclude(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Can be used to assert the + * inclusion of a value in an array or a subset of properties in an object. + * Deep equality is used. + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.deepInclude([obj1, obj2], {a: 1}); + * assert.deepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}}); + * assert.deepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 2}}); + * + * @name deepInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.deepInclude, true).deep.include(inc); + }; + + /** + * ### .notDeepInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Can be used to assert + * the absence of a value in an array or a subset of properties in an object. + * Deep equality is used. + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.notDeepInclude([obj1, obj2], {a: 9}); + * assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 9}}); + * assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 9}}); + * + * @name notDeepInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepInclude, true).not.deep.include(inc); + }; + + /** + * ### .nestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.nestedInclude({'.a': {'b': 'x'}}, {'\\.a.[b]': 'x'}); + * assert.nestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'x'}); + * + * @name nestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.nestedInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.nestedInclude, true).nested.include(inc); + }; + + /** + * ### .notNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' does not include 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.notNestedInclude({'.a': {'b': 'x'}}, {'\\.a.b': 'y'}); + * assert.notNestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'y'}); + * + * @name notNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notNestedInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notNestedInclude, true) + .not.nested.include(inc); + }; + + /** + * ### .deepNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while checking for deep equality. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {x: 1}}); + * assert.deepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {x: 1}}); + * + * @name deepNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepNestedInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.deepNestedInclude, true) + .deep.nested.include(inc); + }; + + /** + * ### .notDeepNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' does not include 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while checking for deep equality. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.notDeepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {y: 1}}) + * assert.notDeepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {y: 2}}); + * + * @name notDeepNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepNestedInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepNestedInclude, true) + .not.deep.nested.include(inc); + }; + + /** + * ### .ownInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while ignoring inherited properties. + * + * assert.ownInclude({ a: 1 }, { a: 1 }); + * + * @name ownInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.ownInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.ownInclude, true).own.include(inc); + }; + + /** + * ### .notOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while ignoring inherited properties. + * + * Object.prototype.b = 2; + * + * assert.notOwnInclude({ a: 1 }, { b: 2 }); + * + * @name notOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notOwnInclude, true).not.own.include(inc); + }; + + /** + * ### .deepOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while ignoring inherited properties and checking for deep equality. + * + * assert.deepOwnInclude({a: {b: 2}}, {a: {b: 2}}); + * + * @name deepOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.deepOwnInclude, true) + .deep.own.include(inc); + }; + + /** + * ### .notDeepOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while ignoring inherited properties and checking for deep equality. + * + * assert.notDeepOwnInclude({a: {b: 2}}, {a: {c: 3}}); + * + * @name notDeepOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepOwnInclude, true) + .not.deep.own.include(inc); + }; + + /** + * ### .match(value, regexp, [message]) + * + * Asserts that `value` matches the regular expression `regexp`. + * + * assert.match('foobar', /^foo/, 'regexp matches'); + * + * @name match + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.match = function (exp, re, msg) { + new Assertion(exp, msg, assert.match, true).to.match(re); + }; + + /** + * ### .notMatch(value, regexp, [message]) + * + * Asserts that `value` does not match the regular expression `regexp`. + * + * assert.notMatch('foobar', /^foo/, 'regexp does not match'); + * + * @name notMatch + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notMatch = function (exp, re, msg) { + new Assertion(exp, msg, assert.notMatch, true).to.not.match(re); + }; + + /** + * ### .property(object, property, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property`. + * + * assert.property({ tea: { green: 'matcha' }}, 'tea'); + * assert.property({ tea: { green: 'matcha' }}, 'toString'); + * + * @name property + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.property = function (obj, prop, msg) { + new Assertion(obj, msg, assert.property, true).to.have.property(prop); + }; + + /** + * ### .notProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property`. + * + * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); + * + * @name notProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.notProperty, true) + .to.not.have.property(prop); + }; + + /** + * ### .propertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property` with a value given by `value`. Uses a strict equality check + * (===). + * + * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); + * + * @name propertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.propertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.propertyVal, true) + .to.have.property(prop, val); + }; + + /** + * ### .notPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property` with value given by `value`. Uses a strict equality check + * (===). + * + * assert.notPropertyVal({ tea: 'is good' }, 'tea', 'is bad'); + * assert.notPropertyVal({ tea: 'is good' }, 'coffee', 'is good'); + * + * @name notPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notPropertyVal, true) + .to.not.have.property(prop, val); + }; + + /** + * ### .deepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property` with a value given by `value`. Uses a deep equality check. + * + * assert.deepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); + * + * @name deepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.deepPropertyVal, true) + .to.have.deep.property(prop, val); + }; + + /** + * ### .notDeepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property` with value given by `value`. Uses a deep equality check. + * + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha' }); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong' }); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha' }); + * + * @name notDeepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notDeepPropertyVal, true) + .to.not.have.deep.property(prop, val); + }; + + /** + * ### .ownProperty(object, property, [message]) + * + * Asserts that `object` has a direct property named by `property`. Inherited + * properties aren't checked. + * + * assert.ownProperty({ tea: { green: 'matcha' }}, 'tea'); + * + * @name ownProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.ownProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.ownProperty, true) + .to.have.own.property(prop); + }; + + /** + * ### .notOwnProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a direct property named by + * `property`. Inherited properties aren't checked. + * + * assert.notOwnProperty({ tea: { green: 'matcha' }}, 'coffee'); + * assert.notOwnProperty({}, 'toString'); + * + * @name notOwnProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notOwnProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.notOwnProperty, true) + .to.not.have.own.property(prop); + }; + + /** + * ### .ownPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct property named by `property` and a value + * equal to the provided `value`. Uses a strict equality check (===). + * Inherited properties aren't checked. + * + * assert.ownPropertyVal({ coffee: 'is good'}, 'coffee', 'is good'); + * + * @name ownPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.ownPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.ownPropertyVal, true) + .to.have.own.property(prop, value); + }; + + /** + * ### .notOwnPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct property named by `property` + * with a value equal to the provided `value`. Uses a strict equality check + * (===). Inherited properties aren't checked. + * + * assert.notOwnPropertyVal({ tea: 'is better'}, 'tea', 'is worse'); + * assert.notOwnPropertyVal({}, 'toString', Object.prototype.toString); + * + * @name notOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.notOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.notOwnPropertyVal, true) + .to.not.have.own.property(prop, value); + }; + + /** + * ### .deepOwnPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct property named by `property` and a value + * equal to the provided `value`. Uses a deep equality check. Inherited + * properties aren't checked. + * + * assert.deepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); + * + * @name deepOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.deepOwnPropertyVal, true) + .to.have.deep.own.property(prop, value); + }; + + /** + * ### .notDeepOwnPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a direct property named by `property` + * with a value equal to the provided `value`. Uses a deep equality check. + * Inherited properties aren't checked. + * + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha' }); + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong' }); + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha' }); + * assert.notDeepOwnPropertyVal({}, 'toString', Object.prototype.toString); + * + * @name notDeepOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.notDeepOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.notDeepOwnPropertyVal, true) + .to.not.have.deep.own.property(prop, value); + }; + + /** + * ### .nestedProperty(object, property, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property`, which can be a string using dot- and bracket-notation for + * nested reference. + * + * assert.nestedProperty({ tea: { green: 'matcha' }}, 'tea.green'); + * + * @name nestedProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.nestedProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.nestedProperty, true) + .to.have.nested.property(prop); + }; + + /** + * ### .notNestedProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`, which + * can be a string using dot- and bracket-notation for nested reference. The + * property cannot exist on the object nor anywhere in its prototype chain. + * + * assert.notNestedProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); + * + * @name notNestedProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notNestedProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.notNestedProperty, true) + .to.not.have.nested.property(prop); + }; + + /** + * ### .nestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. `property` can use dot- and bracket-notation for nested + * reference. Uses a strict equality check (===). + * + * assert.nestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); + * + * @name nestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.nestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.nestedPropertyVal, true) + .to.have.nested.property(prop, val); + }; + + /** + * ### .notNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. `property` can use dot- and bracket-notation for + * nested reference. Uses a strict equality check (===). + * + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'coffee.green', 'matcha'); + * + * @name notNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notNestedPropertyVal, true) + .to.not.have.nested.property(prop, val); + }; + + /** + * ### .deepNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with a value given + * by `value`. `property` can use dot- and bracket-notation for nested + * reference. Uses a deep equality check. + * + * assert.deepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yum' }); + * + * @name deepNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.deepNestedPropertyVal, true) + .to.have.deep.nested.property(prop, val); + }; + + /** + * ### .notDeepNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. `property` can use dot- and bracket-notation for + * nested reference. Uses a deep equality check. + * + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { oolong: 'yum' }); + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yuck' }); + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.black', { matcha: 'yum' }); + * + * @name notDeepNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notDeepNestedPropertyVal, true) + .to.not.have.deep.nested.property(prop, val); + } + + /** + * ### .lengthOf(object, length, [message]) + * + * Asserts that `object` has a `length` or `size` with the expected value. + * + * assert.lengthOf([1,2,3], 3, 'array has length of 3'); + * assert.lengthOf('foobar', 6, 'string has length of 6'); + * assert.lengthOf(new Set([1,2,3]), 3, 'set has size of 3'); + * assert.lengthOf(new Map([['a',1],['b',2],['c',3]]), 3, 'map has size of 3'); + * + * @name lengthOf + * @param {Mixed} object + * @param {Number} length + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.lengthOf = function (exp, len, msg) { + new Assertion(exp, msg, assert.lengthOf, true).to.have.lengthOf(len); + }; + + /** + * ### .hasAnyKeys(object, [keys], [message]) + * + * Asserts that `object` has at least one of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAnyKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'iDontExist', 'baz']); + * assert.hasAnyKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, iDontExist: 99, baz: 1337}); + * assert.hasAnyKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.hasAnyKeys(new Set([{foo: 'bar'}, 'anotherKey']), [{foo: 'bar'}, 'anotherKey']); + * + * @name hasAnyKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAnyKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAnyKeys, true).to.have.any.keys(keys); + } + + /** + * ### .hasAllKeys(object, [keys], [message]) + * + * Asserts that `object` has all and only all of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'bar', 'baz']); + * assert.hasAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, bar: 99, baz: 1337]); + * assert.hasAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.hasAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}, 'anotherKey']); + * + * @name hasAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAllKeys, true).to.have.all.keys(keys); + } + + /** + * ### .containsAllKeys(object, [keys], [message]) + * + * Asserts that `object` has all of the `keys` provided but may have more keys not listed. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'baz']); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'bar', 'baz']); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, baz: 1337}); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, bar: 99, baz: 1337}); + * assert.containsAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}]); + * assert.containsAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.containsAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}]); + * assert.containsAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}, 'anotherKey']); + * + * @name containsAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.containsAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.containsAllKeys, true) + .to.contain.all.keys(keys); + } + + /** + * ### .doesNotHaveAnyKeys(object, [keys], [message]) + * + * Asserts that `object` has none of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAnyKeys({foo: 1, bar: 2, baz: 3}, ['one', 'two', 'example']); + * assert.doesNotHaveAnyKeys({foo: 1, bar: 2, baz: 3}, {one: 1, two: 2, example: 'foo'}); + * assert.doesNotHaveAnyKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{one: 'two'}, 'example']); + * assert.doesNotHaveAnyKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{one: 'two'}, 'example']); + * + * @name doesNotHaveAnyKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAnyKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAnyKeys, true) + .to.not.have.any.keys(keys); + } + + /** + * ### .doesNotHaveAllKeys(object, [keys], [message]) + * + * Asserts that `object` does not have at least one of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAllKeys({foo: 1, bar: 2, baz: 3}, ['one', 'two', 'example']); + * assert.doesNotHaveAllKeys({foo: 1, bar: 2, baz: 3}, {one: 1, two: 2, example: 'foo'}); + * assert.doesNotHaveAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{one: 'two'}, 'example']); + * assert.doesNotHaveAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{one: 'two'}, 'example']); + * + * @name doesNotHaveAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAllKeys, true) + .to.not.have.all.keys(keys); + } + + /** + * ### .hasAnyDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has at least one of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'}); + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'}); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {three: 'three'}]); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name doesNotHaveAllKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAnyDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAnyDeepKeys, true) + .to.have.any.deep.keys(keys); + } + + /** + * ### .hasAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has all and only all of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne']]), {one: 'one'}); + * assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAllDeepKeys(new Set([{one: 'one'}]), {one: 'one'}); + * assert.hasAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name hasAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAllDeepKeys, true) + .to.have.all.deep.keys(keys); + } + + /** + * ### .containsAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` contains all of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'}); + * assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'}); + * assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name containsAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.containsAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.containsAllDeepKeys, true) + .to.contain.all.deep.keys(keys); + } + + /** + * ### .doesNotHaveAnyDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has none of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'}); + * assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {fifty: 'fifty'}]); + * assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'}); + * assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{twenty: 'twenty'}, {fifty: 'fifty'}]); + * + * @name doesNotHaveAnyDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAnyDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAnyDeepKeys, true) + .to.not.have.any.deep.keys(keys); + } + + /** + * ### .doesNotHaveAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` does not have at least one of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'}); + * assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {one: 'one'}]); + * assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'}); + * assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {fifty: 'fifty'}]); + * + * @name doesNotHaveAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAllDeepKeys, true) + .to.not.have.all.deep.keys(keys); + } + + /** + * ### .throws(fn, [errorLike/string/regexp], [string/regexp], [message]) + * + * If `errorLike` is an `Error` constructor, asserts that `fn` will throw an error that is an + * instance of `errorLike`. + * If `errorLike` is an `Error` instance, asserts that the error thrown is the same + * instance as `errorLike`. + * If `errMsgMatcher` is provided, it also asserts that the error thrown will have a + * message matching `errMsgMatcher`. + * + * assert.throws(fn, 'Error thrown must have this msg'); + * assert.throws(fn, /Error thrown must have a msg that matches this/); + * assert.throws(fn, ReferenceError); + * assert.throws(fn, errorInstance); + * assert.throws(fn, ReferenceError, 'Error thrown must be a ReferenceError and have this msg'); + * assert.throws(fn, errorInstance, 'Error thrown must be the same errorInstance and have this msg'); + * assert.throws(fn, ReferenceError, /Error thrown must be a ReferenceError and match this/); + * assert.throws(fn, errorInstance, /Error thrown must be the same errorInstance and match this/); + * + * @name throws + * @alias throw + * @alias Throw + * @param {Function} fn + * @param {ErrorConstructor|Error} errorLike + * @param {RegExp|String} errMsgMatcher + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Assert + * @api public + */ + + assert.throws = function (fn, errorLike, errMsgMatcher, msg) { + if ('string' === typeof errorLike || errorLike instanceof RegExp) { + errMsgMatcher = errorLike; + errorLike = null; + } + + var assertErr = new Assertion(fn, msg, assert.throws, true) + .to.throw(errorLike, errMsgMatcher); + return flag(assertErr, 'object'); + }; + + /** + * ### .doesNotThrow(fn, [errorLike/string/regexp], [string/regexp], [message]) + * + * If `errorLike` is an `Error` constructor, asserts that `fn` will _not_ throw an error that is an + * instance of `errorLike`. + * If `errorLike` is an `Error` instance, asserts that the error thrown is _not_ the same + * instance as `errorLike`. + * If `errMsgMatcher` is provided, it also asserts that the error thrown will _not_ have a + * message matching `errMsgMatcher`. + * + * assert.doesNotThrow(fn, 'Any Error thrown must not have this message'); + * assert.doesNotThrow(fn, /Any Error thrown must not match this/); + * assert.doesNotThrow(fn, Error); + * assert.doesNotThrow(fn, errorInstance); + * assert.doesNotThrow(fn, Error, 'Error must not have this message'); + * assert.doesNotThrow(fn, errorInstance, 'Error must not have this message'); + * assert.doesNotThrow(fn, Error, /Error must not match this/); + * assert.doesNotThrow(fn, errorInstance, /Error must not match this/); + * + * @name doesNotThrow + * @param {Function} fn + * @param {ErrorConstructor} errorLike + * @param {RegExp|String} errMsgMatcher + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Assert + * @api public + */ + + assert.doesNotThrow = function (fn, errorLike, errMsgMatcher, msg) { + if ('string' === typeof errorLike || errorLike instanceof RegExp) { + errMsgMatcher = errorLike; + errorLike = null; + } + + new Assertion(fn, msg, assert.doesNotThrow, true) + .to.not.throw(errorLike, errMsgMatcher); + }; + + /** + * ### .operator(val1, operator, val2, [message]) + * + * Compares two values using `operator`. + * + * assert.operator(1, '<', 2, 'everything is ok'); + * assert.operator(1, '>', 2, 'this will fail'); + * + * @name operator + * @param {Mixed} val1 + * @param {String} operator + * @param {Mixed} val2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.operator = function (val, operator, val2, msg) { + var ok; + switch(operator) { + case '==': + ok = val == val2; + break; + case '===': + ok = val === val2; + break; + case '>': + ok = val > val2; + break; + case '>=': + ok = val >= val2; + break; + case '<': + ok = val < val2; + break; + case '<=': + ok = val <= val2; + break; + case '!=': + ok = val != val2; + break; + case '!==': + ok = val !== val2; + break; + default: + msg = msg ? msg + ': ' : msg; + throw new chai.AssertionError( + msg + 'Invalid operator "' + operator + '"', + undefined, + assert.operator + ); + } + var test = new Assertion(ok, msg, assert.operator, true); + test.assert( + true === flag(test, 'object') + , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) + , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); + }; + + /** + * ### .closeTo(actual, expected, delta, [message]) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); + * + * @name closeTo + * @param {Number} actual + * @param {Number} expected + * @param {Number} delta + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.closeTo = function (act, exp, delta, msg) { + new Assertion(act, msg, assert.closeTo, true).to.be.closeTo(exp, delta); + }; + + /** + * ### .approximately(actual, expected, delta, [message]) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * assert.approximately(1.5, 1, 0.5, 'numbers are close'); + * + * @name approximately + * @param {Number} actual + * @param {Number} expected + * @param {Number} delta + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.approximately = function (act, exp, delta, msg) { + new Assertion(act, msg, assert.approximately, true) + .to.be.approximately(exp, delta); + }; + + /** + * ### .sameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in any order. Uses a + * strict equality check (===). + * + * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); + * + * @name sameMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameMembers, true) + .to.have.same.members(set2); + } + + /** + * ### .notSameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in any order. + * Uses a strict equality check (===). + * + * assert.notSameMembers([ 1, 2, 3 ], [ 5, 1, 3 ], 'not same members'); + * + * @name notSameMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameMembers, true) + .to.not.have.same.members(set2); + } + + /** + * ### .sameDeepMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in any order. Uses a + * deep equality check. + * + * assert.sameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members'); + * + * @name sameDeepMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameDeepMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameDeepMembers, true) + .to.have.same.deep.members(set2); + } + + /** + * ### .notSameDeepMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in any order. + * Uses a deep equality check. + * + * assert.notSameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { f: 5 }], 'not same deep members'); + * + * @name notSameDeepMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameDeepMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameDeepMembers, true) + .to.not.have.same.deep.members(set2); + } + + /** + * ### .sameOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in the same order. + * Uses a strict equality check (===). + * + * assert.sameOrderedMembers([ 1, 2, 3 ], [ 1, 2, 3 ], 'same ordered members'); + * + * @name sameOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameOrderedMembers, true) + .to.have.same.ordered.members(set2); + } + + /** + * ### .notSameOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in the same + * order. Uses a strict equality check (===). + * + * assert.notSameOrderedMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'not same ordered members'); + * + * @name notSameOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameOrderedMembers, true) + .to.not.have.same.ordered.members(set2); + } + + /** + * ### .sameDeepOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in the same order. + * Uses a deep equality check. + * + * assert.sameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { c: 3 } ], 'same deep ordered members'); + * + * @name sameDeepOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameDeepOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameDeepOrderedMembers, true) + .to.have.same.deep.ordered.members(set2); + } + + /** + * ### .notSameDeepOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in the same + * order. Uses a deep equality check. + * + * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { z: 5 } ], 'not same deep ordered members'); + * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { c: 3 } ], 'not same deep ordered members'); + * + * @name notSameDeepOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameDeepOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameDeepOrderedMembers, true) + .to.not.have.same.deep.ordered.members(set2); + } + + /** + * ### .includeMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in any order. Uses a + * strict equality check (===). Duplicates are ignored. + * + * assert.includeMembers([ 1, 2, 3 ], [ 2, 1, 2 ], 'include members'); + * + * @name includeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeMembers, true) + .to.include.members(subset); + } + + /** + * ### .notIncludeMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in any order. Uses a + * strict equality check (===). Duplicates are ignored. + * + * assert.notIncludeMembers([ 1, 2, 3 ], [ 5, 1 ], 'not include members'); + * + * @name notIncludeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeMembers, true) + .to.not.include.members(subset); + } + + /** + * ### .includeDeepMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in any order. Uses a deep + * equality check. Duplicates are ignored. + * + * assert.includeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { b: 2 } ], 'include deep members'); + * + * @name includeDeepMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeDeepMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeDeepMembers, true) + .to.include.deep.members(subset); + } + + /** + * ### .notIncludeDeepMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in any order. Uses a + * deep equality check. Duplicates are ignored. + * + * assert.notIncludeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { f: 5 } ], 'not include deep members'); + * + * @name notIncludeDeepMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeDeepMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeDeepMembers, true) + .to.not.include.deep.members(subset); + } + + /** + * ### .includeOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in the same order + * beginning with the first element in `superset`. Uses a strict equality + * check (===). + * + * assert.includeOrderedMembers([ 1, 2, 3 ], [ 1, 2 ], 'include ordered members'); + * + * @name includeOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeOrderedMembers, true) + .to.include.ordered.members(subset); + } + + /** + * ### .notIncludeOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in the same order + * beginning with the first element in `superset`. Uses a strict equality + * check (===). + * + * assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 1 ], 'not include ordered members'); + * assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 3 ], 'not include ordered members'); + * + * @name notIncludeOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeOrderedMembers, true) + .to.not.include.ordered.members(subset); + } + + /** + * ### .includeDeepOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in the same order + * beginning with the first element in `superset`. Uses a deep equality + * check. + * + * assert.includeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 } ], 'include deep ordered members'); + * + * @name includeDeepOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeDeepOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeDeepOrderedMembers, true) + .to.include.deep.ordered.members(subset); + } + + /** + * ### .notIncludeDeepOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in the same order + * beginning with the first element in `superset`. Uses a deep equality + * check. + * + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { f: 5 } ], 'not include deep ordered members'); + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 } ], 'not include deep ordered members'); + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { c: 3 } ], 'not include deep ordered members'); + * + * @name notIncludeDeepOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeDeepOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeDeepOrderedMembers, true) + .to.not.include.deep.ordered.members(subset); + } + + /** + * ### .oneOf(inList, list, [message]) + * + * Asserts that non-object, non-array value `inList` appears in the flat array `list`. + * + * assert.oneOf(1, [ 2, 1 ], 'Not found in list'); + * + * @name oneOf + * @param {*} inList + * @param {Array<*>} list + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.oneOf = function (inList, list, msg) { + new Assertion(inList, msg, assert.oneOf, true).to.be.oneOf(list); + } + + /** + * ### .changes(function, object, property, [message]) + * + * Asserts that a function changes the value of a property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 22 }; + * assert.changes(fn, obj, 'val'); + * + * @name changes + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changes = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changes, true).to.change(obj, prop); + } + + /** + * ### .changesBy(function, object, property, delta, [message]) + * + * Asserts that a function changes the value of a property by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 2 }; + * assert.changesBy(fn, obj, 'val', 2); + * + * @name changesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changesBy, true) + .to.change(obj, prop).by(delta); + } + + /** + * ### .doesNotChange(function, object, property, [message]) + * + * Asserts that a function does not change the value of a property. + * + * var obj = { val: 10 }; + * var fn = function() { console.log('foo'); }; + * assert.doesNotChange(fn, obj, 'val'); + * + * @name doesNotChange + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotChange = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotChange, true) + .to.not.change(obj, prop); + } + + /** + * ### .changesButNotBy(function, object, property, delta, [message]) + * + * Asserts that a function does not change the value of a property or of a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 10 }; + * assert.changesButNotBy(fn, obj, 'val', 5); + * + * @name changesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changesButNotBy, true) + .to.change(obj, prop).but.not.by(delta); + } + + /** + * ### .increases(function, object, property, [message]) + * + * Asserts that a function increases a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 13 }; + * assert.increases(fn, obj, 'val'); + * + * @name increases + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increases = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.increases, true) + .to.increase(obj, prop); + } + + /** + * ### .increasesBy(function, object, property, delta, [message]) + * + * Asserts that a function increases a numeric object property or a function's return value by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 10 }; + * assert.increasesBy(fn, obj, 'val', 10); + * + * @name increasesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increasesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.increasesBy, true) + .to.increase(obj, prop).by(delta); + } + + /** + * ### .doesNotIncrease(function, object, property, [message]) + * + * Asserts that a function does not increase a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 8 }; + * assert.doesNotIncrease(fn, obj, 'val'); + * + * @name doesNotIncrease + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotIncrease = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotIncrease, true) + .to.not.increase(obj, prop); + } + + /** + * ### .increasesButNotBy(function, object, property, [message]) + * + * Asserts that a function does not increase a numeric object property or function's return value by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 15 }; + * assert.increasesButNotBy(fn, obj, 'val', 10); + * + * @name increasesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increasesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.increasesButNotBy, true) + .to.increase(obj, prop).but.not.by(delta); + } + + /** + * ### .decreases(function, object, property, [message]) + * + * Asserts that a function decreases a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.decreases(fn, obj, 'val'); + * + * @name decreases + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreases = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.decreases, true) + .to.decrease(obj, prop); + } + + /** + * ### .decreasesBy(function, object, property, delta, [message]) + * + * Asserts that a function decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val -= 5 }; + * assert.decreasesBy(fn, obj, 'val', 5); + * + * @name decreasesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreasesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.decreasesBy, true) + .to.decrease(obj, prop).by(delta); + } + + /** + * ### .doesNotDecrease(function, object, property, [message]) + * + * Asserts that a function does not decreases a numeric object property. + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 15 }; + * assert.doesNotDecrease(fn, obj, 'val'); + * + * @name doesNotDecrease + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotDecrease = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotDecrease, true) + .to.not.decrease(obj, prop); + } + + /** + * ### .doesNotDecreaseBy(function, object, property, delta, [message]) + * + * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.doesNotDecreaseBy(fn, obj, 'val', 1); + * + * @name doesNotDecrease + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotDecreaseBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotDecreaseBy, true) + .to.not.decrease(obj, prop).by(delta); + } + + /** + * ### .decreasesButNotBy(function, object, property, delta, [message]) + * + * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.decreasesButNotBy(fn, obj, 'val', 1); + * + * @name decreasesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreasesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.decreasesButNotBy, true) + .to.decrease(obj, prop).but.not.by(delta); + } + + /*! + * ### .ifError(object) + * + * Asserts if value is not a false value, and throws if it is a true value. + * This is added to allow for chai to be a drop-in replacement for Node's + * assert class. + * + * var err = new Error('I am a custom error'); + * assert.ifError(err); // Rethrows err! + * + * @name ifError + * @param {Object} object + * @namespace Assert + * @api public + */ + + assert.ifError = function (val) { + if (val) { + throw(val); + } + }; + + /** + * ### .isExtensible(object) + * + * Asserts that `object` is extensible (can have new properties added to it). + * + * assert.isExtensible({}); + * + * @name isExtensible + * @alias extensible + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isExtensible = function (obj, msg) { + new Assertion(obj, msg, assert.isExtensible, true).to.be.extensible; + }; + + /** + * ### .isNotExtensible(object) + * + * Asserts that `object` is _not_ extensible. + * + * var nonExtensibleObject = Object.preventExtensions({}); + * var sealedObject = Object.seal({}); + * var frozenObject = Object.freeze({}); + * + * assert.isNotExtensible(nonExtensibleObject); + * assert.isNotExtensible(sealedObject); + * assert.isNotExtensible(frozenObject); + * + * @name isNotExtensible + * @alias notExtensible + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotExtensible = function (obj, msg) { + new Assertion(obj, msg, assert.isNotExtensible, true).to.not.be.extensible; + }; + + /** + * ### .isSealed(object) + * + * Asserts that `object` is sealed (cannot have new properties added to it + * and its existing properties cannot be removed). + * + * var sealedObject = Object.seal({}); + * var frozenObject = Object.seal({}); + * + * assert.isSealed(sealedObject); + * assert.isSealed(frozenObject); + * + * @name isSealed + * @alias sealed + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isSealed = function (obj, msg) { + new Assertion(obj, msg, assert.isSealed, true).to.be.sealed; + }; + + /** + * ### .isNotSealed(object) + * + * Asserts that `object` is _not_ sealed. + * + * assert.isNotSealed({}); + * + * @name isNotSealed + * @alias notSealed + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotSealed = function (obj, msg) { + new Assertion(obj, msg, assert.isNotSealed, true).to.not.be.sealed; + }; + + /** + * ### .isFrozen(object) + * + * Asserts that `object` is frozen (cannot have new properties added to it + * and its existing properties cannot be modified). + * + * var frozenObject = Object.freeze({}); + * assert.frozen(frozenObject); + * + * @name isFrozen + * @alias frozen + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isFrozen = function (obj, msg) { + new Assertion(obj, msg, assert.isFrozen, true).to.be.frozen; + }; + + /** + * ### .isNotFrozen(object) + * + * Asserts that `object` is _not_ frozen. + * + * assert.isNotFrozen({}); + * + * @name isNotFrozen + * @alias notFrozen + * @param {Object} object + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotFrozen = function (obj, msg) { + new Assertion(obj, msg, assert.isNotFrozen, true).to.not.be.frozen; + }; + + /** + * ### .isEmpty(target) + * + * Asserts that the target does not contain any values. + * For arrays and strings, it checks the `length` property. + * For `Map` and `Set` instances, it checks the `size` property. + * For non-function objects, it gets the count of own + * enumerable string keys. + * + * assert.isEmpty([]); + * assert.isEmpty(''); + * assert.isEmpty(new Map); + * assert.isEmpty({}); + * + * @name isEmpty + * @alias empty + * @param {Object|Array|String|Map|Set} target + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isEmpty = function(val, msg) { + new Assertion(val, msg, assert.isEmpty, true).to.be.empty; + }; + + /** + * ### .isNotEmpty(target) + * + * Asserts that the target contains values. + * For arrays and strings, it checks the `length` property. + * For `Map` and `Set` instances, it checks the `size` property. + * For non-function objects, it gets the count of own + * enumerable string keys. + * + * assert.isNotEmpty([1, 2]); + * assert.isNotEmpty('34'); + * assert.isNotEmpty(new Set([5, 6])); + * assert.isNotEmpty({ key: 7 }); + * + * @name isNotEmpty + * @alias notEmpty + * @param {Object|Array|String|Map|Set} target + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotEmpty = function(val, msg) { + new Assertion(val, msg, assert.isNotEmpty, true).to.not.be.empty; + }; + + /*! + * Aliases. + */ + + (function alias(name, as){ + assert[as] = assert[name]; + return alias; + }) + ('isOk', 'ok') + ('isNotOk', 'notOk') + ('throws', 'throw') + ('throws', 'Throw') + ('isExtensible', 'extensible') + ('isNotExtensible', 'notExtensible') + ('isSealed', 'sealed') + ('isNotSealed', 'notSealed') + ('isFrozen', 'frozen') + ('isNotFrozen', 'notFrozen') + ('isEmpty', 'empty') + ('isNotEmpty', 'notEmpty'); +}; diff --git a/WebServer/node_modules/chai/lib/chai/interface/expect.js b/WebServer/node_modules/chai/lib/chai/interface/expect.js new file mode 100644 index 000000000..6867e2a93 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/interface/expect.js @@ -0,0 +1,47 @@ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + chai.expect = function (val, message) { + return new chai.Assertion(val, message); + }; + + /** + * ### .fail([message]) + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. + * + * expect.fail(); + * expect.fail("custom error message"); + * expect.fail(1, 2); + * expect.fail(1, 2, "custom error message"); + * expect.fail(1, 2, "custom error message", ">"); + * expect.fail(1, 2, undefined, ">"); + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @namespace BDD + * @api public + */ + + chai.expect.fail = function (actual, expected, message, operator) { + if (arguments.length < 2) { + message = actual; + actual = undefined; + } + + message = message || 'expect.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, chai.expect.fail); + }; +}; diff --git a/WebServer/node_modules/chai/lib/chai/interface/should.js b/WebServer/node_modules/chai/lib/chai/interface/should.js new file mode 100644 index 000000000..5406e5e44 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/interface/should.js @@ -0,0 +1,218 @@ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + var Assertion = chai.Assertion; + + function loadShould () { + // explicitly define this method as function as to have it's name to include as `ssfi` + function shouldGetter() { + if (this instanceof String + || this instanceof Number + || this instanceof Boolean + || typeof Symbol === 'function' && this instanceof Symbol) { + return new Assertion(this.valueOf(), null, shouldGetter); + } + return new Assertion(this, null, shouldGetter); + } + function shouldSetter(value) { + // See https://github.com/chaijs/chai/issues/86: this makes + // `whatever.should = someValue` actually set `someValue`, which is + // especially useful for `global.should = require('chai').should()`. + // + // Note that we have to use [[DefineProperty]] instead of [[Put]] + // since otherwise we would trigger this very setter! + Object.defineProperty(this, 'should', { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } + // modify Object.prototype to have `should` + Object.defineProperty(Object.prototype, 'should', { + set: shouldSetter + , get: shouldGetter + , configurable: true + }); + + var should = {}; + + /** + * ### .fail([message]) + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. + * + * should.fail(); + * should.fail("custom error message"); + * should.fail(1, 2); + * should.fail(1, 2, "custom error message"); + * should.fail(1, 2, "custom error message", ">"); + * should.fail(1, 2, undefined, ">"); + * + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @namespace BDD + * @api public + */ + + should.fail = function (actual, expected, message, operator) { + if (arguments.length < 2) { + message = actual; + actual = undefined; + } + + message = message || 'should.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, should.fail); + }; + + /** + * ### .equal(actual, expected, [message]) + * + * Asserts non-strict equality (`==`) of `actual` and `expected`. + * + * should.equal(3, '3', '== coerces values to strings'); + * + * @name equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Should + * @api public + */ + + should.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.equal(val2); + }; + + /** + * ### .throw(function, [constructor/string/regexp], [string/regexp], [message]) + * + * Asserts that `function` will throw an error that is an instance of + * `constructor`, or alternately that it will throw an error with message + * matching `regexp`. + * + * should.throw(fn, 'function throws a reference error'); + * should.throw(fn, /function throws a reference error/); + * should.throw(fn, ReferenceError); + * should.throw(fn, ReferenceError, 'function throws a reference error'); + * should.throw(fn, ReferenceError, /function throws a reference error/); + * + * @name throw + * @alias Throw + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Should + * @api public + */ + + should.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.Throw(errt, errs); + }; + + /** + * ### .exist + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi'; + * + * should.exist(foo, 'foo exists'); + * + * @name exist + * @namespace Should + * @api public + */ + + should.exist = function (val, msg) { + new Assertion(val, msg).to.exist; + } + + // negation + should.not = {} + + /** + * ### .not.equal(actual, expected, [message]) + * + * Asserts non-strict inequality (`!=`) of `actual` and `expected`. + * + * should.not.equal(3, 4, 'these numbers are not equal'); + * + * @name not.equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @namespace Should + * @api public + */ + + should.not.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.not.equal(val2); + }; + + /** + * ### .throw(function, [constructor/regexp], [message]) + * + * Asserts that `function` will _not_ throw an error that is an instance of + * `constructor`, or alternately that it will not throw an error with message + * matching `regexp`. + * + * should.not.throw(fn, Error, 'function does not throw'); + * + * @name not.throw + * @alias not.Throw + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @namespace Should + * @api public + */ + + should.not.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.not.Throw(errt, errs); + }; + + /** + * ### .not.exist + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var bar = null; + * + * should.not.exist(bar, 'bar does not exist'); + * + * @name not.exist + * @namespace Should + * @api public + */ + + should.not.exist = function (val, msg) { + new Assertion(val, msg).to.not.exist; + } + + should['throw'] = should['Throw']; + should.not['throw'] = should.not['Throw']; + + return should; + }; + + chai.should = loadShould; + chai.Should = loadShould; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/addChainableMethod.js b/WebServer/node_modules/chai/lib/chai/utils/addChainableMethod.js new file mode 100644 index 000000000..a713f6ac8 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/addChainableMethod.js @@ -0,0 +1,152 @@ +/*! + * Chai - addChainingMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); + +/*! + * Module variables + */ + +// Check whether `Object.setPrototypeOf` is supported +var canSetPrototype = typeof Object.setPrototypeOf === 'function'; + +// Without `Object.setPrototypeOf` support, this module will need to add properties to a function. +// However, some of functions' own props are not configurable and should be skipped. +var testFn = function() {}; +var excludeNames = Object.getOwnPropertyNames(testFn).filter(function(name) { + var propDesc = Object.getOwnPropertyDescriptor(testFn, name); + + // Note: PhantomJS 1.x includes `callee` as one of `testFn`'s own properties, + // but then returns `undefined` as the property descriptor for `callee`. As a + // workaround, we perform an otherwise unnecessary type-check for `propDesc`, + // and then filter it out if it's not an object as it should be. + if (typeof propDesc !== 'object') + return true; + + return !propDesc.configurable; +}); + +// Cache `Function` properties +var call = Function.prototype.call, + apply = Function.prototype.apply; + +/** + * ### .addChainableMethod(ctx, name, method, chainingBehavior) + * + * Adds a method to an object, such that the method can also be chained. + * + * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); + * + * The result can then be used as both a method assertion, executing both `method` and + * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. + * + * expect(fooStr).to.be.foo('bar'); + * expect(fooStr).to.be.foo.equal('foo'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for `name`, when called + * @param {Function} chainingBehavior function to be called every time the property is accessed + * @namespace Utils + * @name addChainableMethod + * @api public + */ + +module.exports = function addChainableMethod(ctx, name, method, chainingBehavior) { + if (typeof chainingBehavior !== 'function') { + chainingBehavior = function () { }; + } + + var chainableBehavior = { + method: method + , chainingBehavior: chainingBehavior + }; + + // save the methods so we can overwrite them later, if we need to. + if (!ctx.__methods) { + ctx.__methods = {}; + } + ctx.__methods[name] = chainableBehavior; + + Object.defineProperty(ctx, name, + { get: function chainableMethodGetter() { + chainableBehavior.chainingBehavior.call(this); + + var chainableMethodWrapper = function () { + // Setting the `ssfi` flag to `chainableMethodWrapper` causes this + // function to be the starting point for removing implementation + // frames from the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then this assertion is being + // invoked from inside of another assertion. In this case, the `ssfi` + // flag has already been set by the outer assertion. + // + // Note that overwriting a chainable method merely replaces the saved + // methods in `ctx.__methods` instead of completely replacing the + // overwritten assertion. Therefore, an overwriting assertion won't + // set the `ssfi` or `lockSsfi` flags. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', chainableMethodWrapper); + } + + var result = chainableBehavior.method.apply(this, arguments); + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; + + addLengthGuard(chainableMethodWrapper, name, true); + + // Use `Object.setPrototypeOf` if available + if (canSetPrototype) { + // Inherit all properties from the object by replacing the `Function` prototype + var prototype = Object.create(this); + // Restore the `call` and `apply` methods from `Function` + prototype.call = call; + prototype.apply = apply; + Object.setPrototypeOf(chainableMethodWrapper, prototype); + } + // Otherwise, redefine all properties (slow!) + else { + var asserterNames = Object.getOwnPropertyNames(ctx); + asserterNames.forEach(function (asserterName) { + if (excludeNames.indexOf(asserterName) !== -1) { + return; + } + + var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); + Object.defineProperty(chainableMethodWrapper, asserterName, pd); + }); + } + + transferFlags(this, chainableMethodWrapper); + return proxify(chainableMethodWrapper); + } + , configurable: true + }); +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/addLengthGuard.js b/WebServer/node_modules/chai/lib/chai/utils/addLengthGuard.js new file mode 100644 index 000000000..e51ce80e8 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/addLengthGuard.js @@ -0,0 +1,60 @@ +var fnLengthDesc = Object.getOwnPropertyDescriptor(function () {}, 'length'); + +/*! + * Chai - addLengthGuard utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .addLengthGuard(fn, assertionName, isChainable) + * + * Define `length` as a getter on the given uninvoked method assertion. The + * getter acts as a guard against chaining `length` directly off of an uninvoked + * method assertion, which is a problem because it references `function`'s + * built-in `length` property instead of Chai's `length` assertion. When the + * getter catches the user making this mistake, it throws an error with a + * helpful message. + * + * There are two ways in which this mistake can be made. The first way is by + * chaining the `length` assertion directly off of an uninvoked chainable + * method. In this case, Chai suggests that the user use `lengthOf` instead. The + * second way is by chaining the `length` assertion directly off of an uninvoked + * non-chainable method. Non-chainable methods must be invoked prior to + * chaining. In this case, Chai suggests that the user consult the docs for the + * given assertion. + * + * If the `length` property of functions is unconfigurable, then return `fn` + * without modification. + * + * Note that in ES6, the function's `length` property is configurable, so once + * support for legacy environments is dropped, Chai's `length` property can + * replace the built-in function's `length` property, and this length guard will + * no longer be necessary. In the mean time, maintaining consistency across all + * environments is the priority. + * + * @param {Function} fn + * @param {String} assertionName + * @param {Boolean} isChainable + * @namespace Utils + * @name addLengthGuard + */ + +module.exports = function addLengthGuard (fn, assertionName, isChainable) { + if (!fnLengthDesc.configurable) return fn; + + Object.defineProperty(fn, 'length', { + get: function () { + if (isChainable) { + throw Error('Invalid Chai property: ' + assertionName + '.length. Due' + + ' to a compatibility issue, "length" cannot directly follow "' + + assertionName + '". Use "' + assertionName + '.lengthOf" instead.'); + } + + throw Error('Invalid Chai property: ' + assertionName + '.length. See' + + ' docs for proper usage of "' + assertionName + '".'); + } + }); + + return fn; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/addMethod.js b/WebServer/node_modules/chai/lib/chai/utils/addMethod.js new file mode 100644 index 000000000..021f08042 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/addMethod.js @@ -0,0 +1,68 @@ +/*! + * Chai - addMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); + +/** + * ### .addMethod(ctx, name, method) + * + * Adds a method to the prototype of an object. + * + * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(fooStr).to.be.foo('bar'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for name + * @namespace Utils + * @name addMethod + * @api public + */ + +module.exports = function addMethod(ctx, name, method) { + var methodWrapper = function () { + // Setting the `ssfi` flag to `methodWrapper` causes this function to be the + // starting point for removing implementation frames from the stack trace of + // a failed assertion. + // + // However, we only want to use this function as the starting point if the + // `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked from + // inside of another assertion. In the first case, the `ssfi` flag has + // already been set by the overwriting assertion. In the second case, the + // `ssfi` flag has already been set by the outer assertion. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', methodWrapper); + } + + var result = method.apply(this, arguments); + if (result !== undefined) + return result; + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; + + addLengthGuard(methodWrapper, name, false); + ctx[name] = proxify(methodWrapper, name); +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/addProperty.js b/WebServer/node_modules/chai/lib/chai/utils/addProperty.js new file mode 100644 index 000000000..872a8cd7e --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/addProperty.js @@ -0,0 +1,72 @@ +/*! + * Chai - addProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var chai = require('../../chai'); +var flag = require('./flag'); +var isProxyEnabled = require('./isProxyEnabled'); +var transferFlags = require('./transferFlags'); + +/** + * ### .addProperty(ctx, name, getter) + * + * Adds a property to the prototype of an object. + * + * utils.addProperty(chai.Assertion.prototype, 'foo', function () { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.instanceof(Foo); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.foo; + * + * @param {Object} ctx object to which the property is added + * @param {String} name of property to add + * @param {Function} getter function to be used for name + * @namespace Utils + * @name addProperty + * @api public + */ + +module.exports = function addProperty(ctx, name, getter) { + getter = getter === undefined ? function () {} : getter; + + Object.defineProperty(ctx, name, + { get: function propertyGetter() { + // Setting the `ssfi` flag to `propertyGetter` causes this function to + // be the starting point for removing implementation frames from the + // stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set and proxy protection is disabled. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked + // from inside of another assertion. In the first case, the `ssfi` flag + // has already been set by the overwriting assertion. In the second + // case, the `ssfi` flag has already been set by the outer assertion. + // + // If proxy protection is enabled, then the `ssfi` flag has already been + // set by the proxy getter. + if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { + flag(this, 'ssfi', propertyGetter); + } + + var result = getter.call(this); + if (result !== undefined) + return result; + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + } + , configurable: true + }); +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/compareByInspect.js b/WebServer/node_modules/chai/lib/chai/utils/compareByInspect.js new file mode 100644 index 000000000..c8cd5e197 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/compareByInspect.js @@ -0,0 +1,31 @@ +/*! + * Chai - compareByInspect utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var inspect = require('./inspect'); + +/** + * ### .compareByInspect(mixed, mixed) + * + * To be used as a compareFunction with Array.prototype.sort. Compares elements + * using inspect instead of default behavior of using toString so that Symbols + * and objects with irregular/missing toString can still be sorted without a + * TypeError. + * + * @param {Mixed} first element to compare + * @param {Mixed} second element to compare + * @returns {Number} -1 if 'a' should come before 'b'; otherwise 1 + * @name compareByInspect + * @namespace Utils + * @api public + */ + +module.exports = function compareByInspect(a, b) { + return inspect(a) < inspect(b) ? -1 : 1; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/expectTypes.js b/WebServer/node_modules/chai/lib/chai/utils/expectTypes.js new file mode 100644 index 000000000..6293db748 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/expectTypes.js @@ -0,0 +1,51 @@ +/*! + * Chai - expectTypes utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .expectTypes(obj, types) + * + * Ensures that the object being tested against is of a valid type. + * + * utils.expectTypes(this, ['array', 'object', 'string']); + * + * @param {Mixed} obj constructed Assertion + * @param {Array} type A list of allowed types for this assertion + * @namespace Utils + * @name expectTypes + * @api public + */ + +var AssertionError = require('assertion-error'); +var flag = require('./flag'); +var type = require('type-detect'); + +module.exports = function expectTypes(obj, types) { + var flagMsg = flag(obj, 'message'); + var ssfi = flag(obj, 'ssfi'); + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + obj = flag(obj, 'object'); + types = types.map(function (t) { return t.toLowerCase(); }); + types.sort(); + + // Transforms ['lorem', 'ipsum'] into 'a lorem, or an ipsum' + var str = types.map(function (t, index) { + var art = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(t.charAt(0)) ? 'an' : 'a'; + var or = types.length > 1 && index === types.length - 1 ? 'or ' : ''; + return or + art + ' ' + t; + }).join(', '); + + var objType = type(obj).toLowerCase(); + + if (!types.some(function (expected) { return objType === expected; })) { + throw new AssertionError( + flagMsg + 'object tested must be ' + str + ', but ' + objType + ' given', + undefined, + ssfi + ); + } +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/flag.js b/WebServer/node_modules/chai/lib/chai/utils/flag.js new file mode 100644 index 000000000..dd53bfbf8 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/flag.js @@ -0,0 +1,33 @@ +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .flag(object, key, [value]) + * + * Get or set a flag value on an object. If a + * value is provided it will be set, else it will + * return the currently set value or `undefined` if + * the value is not set. + * + * utils.flag(this, 'foo', 'bar'); // setter + * utils.flag(this, 'foo'); // getter, returns `bar` + * + * @param {Object} object constructed Assertion + * @param {String} key + * @param {Mixed} value (optional) + * @namespace Utils + * @name flag + * @api private + */ + +module.exports = function flag(obj, key, value) { + var flags = obj.__flags || (obj.__flags = Object.create(null)); + if (arguments.length === 3) { + flags[key] = value; + } else { + return flags[key]; + } +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/getActual.js b/WebServer/node_modules/chai/lib/chai/utils/getActual.js new file mode 100644 index 000000000..976e11259 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/getActual.js @@ -0,0 +1,20 @@ +/*! + * Chai - getActual utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getActual(object, [actual]) + * + * Returns the `actual` value for an Assertion. + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @namespace Utils + * @name getActual + */ + +module.exports = function getActual(obj, args) { + return args.length > 4 ? args[4] : obj._obj; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/getEnumerableProperties.js b/WebServer/node_modules/chai/lib/chai/utils/getEnumerableProperties.js new file mode 100644 index 000000000..a84252cfd --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/getEnumerableProperties.js @@ -0,0 +1,26 @@ +/*! + * Chai - getEnumerableProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getEnumerableProperties(object) + * + * This allows the retrieval of enumerable property names of an object, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getEnumerableProperties + * @api public + */ + +module.exports = function getEnumerableProperties(object) { + var result = []; + for (var name in object) { + result.push(name); + } + return result; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/getMessage.js b/WebServer/node_modules/chai/lib/chai/utils/getMessage.js new file mode 100644 index 000000000..bb8371668 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/getMessage.js @@ -0,0 +1,50 @@ +/*! + * Chai - message composition utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var flag = require('./flag') + , getActual = require('./getActual') + , objDisplay = require('./objDisplay'); + +/** + * ### .getMessage(object, message, negateMessage) + * + * Construct the error message based on flags + * and template tags. Template tags will return + * a stringified inspection of the object referenced. + * + * Message template tags: + * - `#{this}` current asserted object + * - `#{act}` actual value + * - `#{exp}` expected value + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @namespace Utils + * @name getMessage + * @api public + */ + +module.exports = function getMessage(obj, args) { + var negate = flag(obj, 'negate') + , val = flag(obj, 'object') + , expected = args[3] + , actual = getActual(obj, args) + , msg = negate ? args[2] : args[1] + , flagMsg = flag(obj, 'message'); + + if(typeof msg === "function") msg = msg(); + msg = msg || ''; + msg = msg + .replace(/#\{this\}/g, function () { return objDisplay(val); }) + .replace(/#\{act\}/g, function () { return objDisplay(actual); }) + .replace(/#\{exp\}/g, function () { return objDisplay(expected); }); + + return flagMsg ? flagMsg + ': ' + msg : msg; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js b/WebServer/node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js new file mode 100644 index 000000000..a4aa83a4c --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js @@ -0,0 +1,29 @@ +/*! + * Chai - getOwnEnumerableProperties utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var getOwnEnumerablePropertySymbols = require('./getOwnEnumerablePropertySymbols'); + +/** + * ### .getOwnEnumerableProperties(object) + * + * This allows the retrieval of directly-owned enumerable property names and + * symbols of an object. This function is necessary because Object.keys only + * returns enumerable property names, not enumerable property symbols. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getOwnEnumerableProperties + * @api public + */ + +module.exports = function getOwnEnumerableProperties(obj) { + return Object.keys(obj).concat(getOwnEnumerablePropertySymbols(obj)); +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js b/WebServer/node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js new file mode 100644 index 000000000..823c6b7aa --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js @@ -0,0 +1,27 @@ +/*! + * Chai - getOwnEnumerablePropertySymbols utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +/** + * ### .getOwnEnumerablePropertySymbols(object) + * + * This allows the retrieval of directly-owned enumerable property symbols of an + * object. This function is necessary because Object.getOwnPropertySymbols + * returns both enumerable and non-enumerable property symbols. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getOwnEnumerablePropertySymbols + * @api public + */ + +module.exports = function getOwnEnumerablePropertySymbols(obj) { + if (typeof Object.getOwnPropertySymbols !== 'function') return []; + + return Object.getOwnPropertySymbols(obj).filter(function (sym) { + return Object.getOwnPropertyDescriptor(obj, sym).enumerable; + }); +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/getProperties.js b/WebServer/node_modules/chai/lib/chai/utils/getProperties.js new file mode 100644 index 000000000..ccf9631ab --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/getProperties.js @@ -0,0 +1,36 @@ +/*! + * Chai - getProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getProperties(object) + * + * This allows the retrieval of property names of an object, enumerable or not, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getProperties + * @api public + */ + +module.exports = function getProperties(object) { + var result = Object.getOwnPropertyNames(object); + + function addProperty(property) { + if (result.indexOf(property) === -1) { + result.push(property); + } + } + + var proto = Object.getPrototypeOf(object); + while (proto !== null) { + Object.getOwnPropertyNames(proto).forEach(addProperty); + proto = Object.getPrototypeOf(proto); + } + + return result; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/index.js b/WebServer/node_modules/chai/lib/chai/utils/index.js new file mode 100644 index 000000000..d4f329c75 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/index.js @@ -0,0 +1,172 @@ +/*! + * chai + * Copyright(c) 2011 Jake Luer + * MIT Licensed + */ + +/*! + * Dependencies that are used for multiple exports are required here only once + */ + +var pathval = require('pathval'); + +/*! + * test utility + */ + +exports.test = require('./test'); + +/*! + * type utility + */ + +exports.type = require('type-detect'); + +/*! + * expectTypes utility + */ +exports.expectTypes = require('./expectTypes'); + +/*! + * message utility + */ + +exports.getMessage = require('./getMessage'); + +/*! + * actual utility + */ + +exports.getActual = require('./getActual'); + +/*! + * Inspect util + */ + +exports.inspect = require('./inspect'); + +/*! + * Object Display util + */ + +exports.objDisplay = require('./objDisplay'); + +/*! + * Flag utility + */ + +exports.flag = require('./flag'); + +/*! + * Flag transferring utility + */ + +exports.transferFlags = require('./transferFlags'); + +/*! + * Deep equal utility + */ + +exports.eql = require('deep-eql'); + +/*! + * Deep path info + */ + +exports.getPathInfo = pathval.getPathInfo; + +/*! + * Check if a property exists + */ + +exports.hasProperty = pathval.hasProperty; + +/*! + * Function name + */ + +exports.getName = require('get-func-name'); + +/*! + * add Property + */ + +exports.addProperty = require('./addProperty'); + +/*! + * add Method + */ + +exports.addMethod = require('./addMethod'); + +/*! + * overwrite Property + */ + +exports.overwriteProperty = require('./overwriteProperty'); + +/*! + * overwrite Method + */ + +exports.overwriteMethod = require('./overwriteMethod'); + +/*! + * Add a chainable method + */ + +exports.addChainableMethod = require('./addChainableMethod'); + +/*! + * Overwrite chainable method + */ + +exports.overwriteChainableMethod = require('./overwriteChainableMethod'); + +/*! + * Compare by inspect method + */ + +exports.compareByInspect = require('./compareByInspect'); + +/*! + * Get own enumerable property symbols method + */ + +exports.getOwnEnumerablePropertySymbols = require('./getOwnEnumerablePropertySymbols'); + +/*! + * Get own enumerable properties method + */ + +exports.getOwnEnumerableProperties = require('./getOwnEnumerableProperties'); + +/*! + * Checks error against a given set of criteria + */ + +exports.checkError = require('check-error'); + +/*! + * Proxify util + */ + +exports.proxify = require('./proxify'); + +/*! + * addLengthGuard util + */ + +exports.addLengthGuard = require('./addLengthGuard'); + +/*! + * isProxyEnabled helper + */ + +exports.isProxyEnabled = require('./isProxyEnabled'); + +/*! + * isNaN method + */ + +exports.isNaN = require('./isNaN'); diff --git a/WebServer/node_modules/chai/lib/chai/utils/inspect.js b/WebServer/node_modules/chai/lib/chai/utils/inspect.js new file mode 100644 index 000000000..b054aebae --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/inspect.js @@ -0,0 +1,376 @@ +// This is (almost) directly from Node.js utils +// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js + +var getName = require('get-func-name'); +var getProperties = require('./getProperties'); +var getEnumerableProperties = require('./getEnumerableProperties'); +var config = require('../config'); + +module.exports = inspect; + +/** + * ### .inspect(obj, [showHidden], [depth], [colors]) + * + * Echoes the value of a value. Tries to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Boolean} showHidden Flag that shows hidden (not enumerable) + * properties of objects. Default is false. + * @param {Number} depth Depth in which to descend in object. Default is 2. + * @param {Boolean} colors Flag to turn on ANSI escape codes to color the + * output. Default is false (no coloring). + * @namespace Utils + * @name inspect + */ +function inspect(obj, showHidden, depth, colors) { + var ctx = { + showHidden: showHidden, + seen: [], + stylize: function (str) { return str; } + }; + return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); +} + +// Returns true if object is a DOM element. +var isDOMElement = function (object) { + if (typeof HTMLElement === 'object') { + return object instanceof HTMLElement; + } else { + return object && + typeof object === 'object' && + 'nodeType' in object && + object.nodeType === 1 && + typeof object.nodeName === 'string'; + } +}; + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (typeof ret !== 'string') { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // If this is a DOM element, try to get the outer HTML. + if (isDOMElement(value)) { + if ('outerHTML' in value) { + return value.outerHTML; + // This value does not have an outerHTML attribute, + // it could still be an XML element + } else { + // Attempt to serialize it + try { + if (document.xmlVersion) { + var xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(value); + } else { + // Firefox 11- do not support outerHTML + // It does, however, support innerHTML + // Use the following to render the element + var ns = "http://www.w3.org/1999/xhtml"; + var container = document.createElementNS(ns, '_'); + + container.appendChild(value.cloneNode(false)); + var html = container.innerHTML + .replace('><', '>' + value.innerHTML + '<'); + container.innerHTML = ''; + return html; + } + } catch (err) { + // This could be a non-native DOM implementation, + // continue with the normal flow: + // printing the element as if it is an object. + } + } + } + + // Look up the keys of the object. + var visibleKeys = getEnumerableProperties(value); + var keys = ctx.showHidden ? getProperties(value) : visibleKeys; + + var name, nameSuffix; + + // Some type of object without properties can be shortcut. + // In IE, errors have a single `stack` property, or if they are vanilla `Error`, + // a `stack` plus `description` property; ignore those for consistency. + if (keys.length === 0 || (isError(value) && ( + (keys.length === 1 && keys[0] === 'stack') || + (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') + ))) { + if (typeof value === 'function') { + name = getName(value); + nameSuffix = name ? ': ' + name : ''; + return ctx.stylize('[Function' + nameSuffix + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '' + , array = false + , typedArray = false + , braces = ['{', '}']; + + if (isTypedArray(value)) { + typedArray = true; + braces = ['[', ']']; + } + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + name = getName(value); + nameSuffix = name ? ': ' + name : ''; + base = ' [Function' + nameSuffix + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + return formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else if (typedArray) { + return formatTypedArray(value); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + +function formatPrimitive(ctx, value) { + switch (typeof value) { + case 'undefined': + return ctx.stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + + case 'number': + if (value === 0 && (1/value) === -Infinity) { + return ctx.stylize('-0', 'number'); + } + return ctx.stylize('' + value, 'number'); + + case 'boolean': + return ctx.stylize('' + value, 'boolean'); + + case 'symbol': + return ctx.stylize(value.toString(), 'symbol'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return ctx.stylize('null', 'null'); + } +} + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (Object.prototype.hasOwnProperty.call(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + +function formatTypedArray(value) { + var str = '[ '; + + for (var i = 0; i < value.length; ++i) { + if (str.length >= config.truncateThreshold - 7) { + str += '...'; + break; + } + str += value[i] + ', '; + } + str += ' ]'; + + // Removing trailing `, ` if the array was not truncated + if (str.indexOf(', ]') !== -1) { + str = str.replace(', ]', ' ]'); + } + + return str; +} + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name; + var propDescriptor = Object.getOwnPropertyDescriptor(value, key); + var str; + + if (propDescriptor) { + if (propDescriptor.get) { + if (propDescriptor.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (propDescriptor.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + } + if (visibleKeys.indexOf(key) < 0) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(value[key]) < 0) { + if (recurseTimes === null) { + str = formatValue(ctx, value[key], null); + } else { + str = formatValue(ctx, value[key], recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + +function reduceToSingleString(output, base, braces) { + var length = output.reduce(function(prev, cur) { + return prev + cur.length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + +function isTypedArray(ar) { + // Unfortunately there's no way to check if an object is a TypedArray + // We have to check if it's one of these types + return (typeof ar === 'object' && /\w+Array]$/.test(objectToString(ar))); +} + +function isArray(ar) { + return Array.isArray(ar) || + (typeof ar === 'object' && objectToString(ar) === '[object Array]'); +} + +function isRegExp(re) { + return typeof re === 'object' && objectToString(re) === '[object RegExp]'; +} + +function isDate(d) { + return typeof d === 'object' && objectToString(d) === '[object Date]'; +} + +function isError(e) { + return typeof e === 'object' && objectToString(e) === '[object Error]'; +} + +function objectToString(o) { + return Object.prototype.toString.call(o); +} diff --git a/WebServer/node_modules/chai/lib/chai/utils/isNaN.js b/WebServer/node_modules/chai/lib/chai/utils/isNaN.js new file mode 100644 index 000000000..d64f7f4aa --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/isNaN.js @@ -0,0 +1,26 @@ +/*! + * Chai - isNaN utility + * Copyright(c) 2012-2015 Sakthipriyan Vairamani + * MIT Licensed + */ + +/** + * ### .isNaN(value) + * + * Checks if the given value is NaN or not. + * + * utils.isNaN(NaN); // true + * + * @param {Value} The value which has to be checked if it is NaN + * @name isNaN + * @api private + */ + +function isNaN(value) { + // Refer http://www.ecma-international.org/ecma-262/6.0/#sec-isnan-number + // section's NOTE. + return value !== value; +} + +// If ECMAScript 6's Number.isNaN is present, prefer that. +module.exports = Number.isNaN || isNaN; diff --git a/WebServer/node_modules/chai/lib/chai/utils/isProxyEnabled.js b/WebServer/node_modules/chai/lib/chai/utils/isProxyEnabled.js new file mode 100644 index 000000000..11e58edfe --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/isProxyEnabled.js @@ -0,0 +1,24 @@ +var config = require('../config'); + +/*! + * Chai - isProxyEnabled helper + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .isProxyEnabled() + * + * Helper function to check if Chai's proxy protection feature is enabled. If + * proxies are unsupported or disabled via the user's Chai config, then return + * false. Otherwise, return true. + * + * @namespace Utils + * @name isProxyEnabled + */ + +module.exports = function isProxyEnabled() { + return config.useProxy && + typeof Proxy !== 'undefined' && + typeof Reflect !== 'undefined'; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/objDisplay.js b/WebServer/node_modules/chai/lib/chai/utils/objDisplay.js new file mode 100644 index 000000000..3010f46df --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/objDisplay.js @@ -0,0 +1,50 @@ +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var inspect = require('./inspect'); +var config = require('../config'); + +/** + * ### .objDisplay(object) + * + * Determines if an object or an array matches + * criteria to be inspected in-line for error + * messages or should be truncated. + * + * @param {Mixed} javascript object to inspect + * @name objDisplay + * @namespace Utils + * @api public + */ + +module.exports = function objDisplay(obj) { + var str = inspect(obj) + , type = Object.prototype.toString.call(obj); + + if (config.truncateThreshold && str.length >= config.truncateThreshold) { + if (type === '[object Function]') { + return !obj.name || obj.name === '' + ? '[Function]' + : '[Function: ' + obj.name + ']'; + } else if (type === '[object Array]') { + return '[ Array(' + obj.length + ') ]'; + } else if (type === '[object Object]') { + var keys = Object.keys(obj) + , kstr = keys.length > 2 + ? keys.splice(0, 2).join(', ') + ', ...' + : keys.join(', '); + return '{ Object (' + kstr + ') }'; + } else { + return str; + } + } else { + return str; + } +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/overwriteChainableMethod.js b/WebServer/node_modules/chai/lib/chai/utils/overwriteChainableMethod.js new file mode 100644 index 000000000..4b38569e4 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/overwriteChainableMethod.js @@ -0,0 +1,69 @@ +/*! + * Chai - overwriteChainableMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var chai = require('../../chai'); +var transferFlags = require('./transferFlags'); + +/** + * ### .overwriteChainableMethod(ctx, name, method, chainingBehavior) + * + * Overwrites an already existing chainable method + * and provides access to the previous function or + * property. Must return functions to be used for + * name. + * + * utils.overwriteChainableMethod(chai.Assertion.prototype, 'lengthOf', + * function (_super) { + * } + * , function (_super) { + * } + * ); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteChainableMethod('foo', fn, fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.have.lengthOf(3); + * expect(myFoo).to.have.lengthOf.above(3); + * + * @param {Object} ctx object whose method / property is to be overwritten + * @param {String} name of method / property to overwrite + * @param {Function} method function that returns a function to be used for name + * @param {Function} chainingBehavior function that returns a function to be used for property + * @namespace Utils + * @name overwriteChainableMethod + * @api public + */ + +module.exports = function overwriteChainableMethod(ctx, name, method, chainingBehavior) { + var chainableBehavior = ctx.__methods[name]; + + var _chainingBehavior = chainableBehavior.chainingBehavior; + chainableBehavior.chainingBehavior = function overwritingChainableMethodGetter() { + var result = chainingBehavior(_chainingBehavior).call(this); + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; + + var _method = chainableBehavior.method; + chainableBehavior.method = function overwritingChainableMethodWrapper() { + var result = method(_method).apply(this, arguments); + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + }; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/overwriteMethod.js b/WebServer/node_modules/chai/lib/chai/utils/overwriteMethod.js new file mode 100644 index 000000000..7925e05ff --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/overwriteMethod.js @@ -0,0 +1,92 @@ +/*! + * Chai - overwriteMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); + +/** + * ### .overwriteMethod(ctx, name, fn) + * + * Overwrites an already existing method and provides + * access to previous function. Must return function + * to be used for name. + * + * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { + * return function (str) { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.value).to.equal(str); + * } else { + * _super.apply(this, arguments); + * } + * } + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.equal('bar'); + * + * @param {Object} ctx object whose method is to be overwritten + * @param {String} name of method to overwrite + * @param {Function} method function that returns a function to be used for name + * @namespace Utils + * @name overwriteMethod + * @api public + */ + +module.exports = function overwriteMethod(ctx, name, method) { + var _method = ctx[name] + , _super = function () { + throw new Error(name + ' is not a function'); + }; + + if (_method && 'function' === typeof _method) + _super = _method; + + var overwritingMethodWrapper = function () { + // Setting the `ssfi` flag to `overwritingMethodWrapper` causes this + // function to be the starting point for removing implementation frames from + // the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if the + // `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked from + // inside of another assertion. In the first case, the `ssfi` flag has + // already been set by the overwriting assertion. In the second case, the + // `ssfi` flag has already been set by the outer assertion. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', overwritingMethodWrapper); + } + + // Setting the `lockSsfi` flag to `true` prevents the overwritten assertion + // from changing the `ssfi` flag. By this point, the `ssfi` flag is already + // set to the correct starting point for this assertion. + var origLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); + var result = method(_super).apply(this, arguments); + flag(this, 'lockSsfi', origLockSsfi); + + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + } + + addLengthGuard(overwritingMethodWrapper, name, false); + ctx[name] = proxify(overwritingMethodWrapper, name); +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/overwriteProperty.js b/WebServer/node_modules/chai/lib/chai/utils/overwriteProperty.js new file mode 100644 index 000000000..5f870b02a --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/overwriteProperty.js @@ -0,0 +1,92 @@ +/*! + * Chai - overwriteProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var chai = require('../../chai'); +var flag = require('./flag'); +var isProxyEnabled = require('./isProxyEnabled'); +var transferFlags = require('./transferFlags'); + +/** + * ### .overwriteProperty(ctx, name, fn) + * + * Overwrites an already existing property getter and provides + * access to previous value. Must return function to use as getter. + * + * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { + * return function () { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.name).to.equal('bar'); + * } else { + * _super.call(this); + * } + * } + * }); + * + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.ok; + * + * @param {Object} ctx object whose property is to be overwritten + * @param {String} name of property to overwrite + * @param {Function} getter function that returns a getter function to be used for name + * @namespace Utils + * @name overwriteProperty + * @api public + */ + +module.exports = function overwriteProperty(ctx, name, getter) { + var _get = Object.getOwnPropertyDescriptor(ctx, name) + , _super = function () {}; + + if (_get && 'function' === typeof _get.get) + _super = _get.get + + Object.defineProperty(ctx, name, + { get: function overwritingPropertyGetter() { + // Setting the `ssfi` flag to `overwritingPropertyGetter` causes this + // function to be the starting point for removing implementation frames + // from the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set and proxy protection is disabled. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked + // from inside of another assertion. In the first case, the `ssfi` flag + // has already been set by the overwriting assertion. In the second + // case, the `ssfi` flag has already been set by the outer assertion. + // + // If proxy protection is enabled, then the `ssfi` flag has already been + // set by the proxy getter. + if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { + flag(this, 'ssfi', overwritingPropertyGetter); + } + + // Setting the `lockSsfi` flag to `true` prevents the overwritten + // assertion from changing the `ssfi` flag. By this point, the `ssfi` + // flag is already set to the correct starting point for this assertion. + var origLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); + var result = getter(_super).call(this); + flag(this, 'lockSsfi', origLockSsfi); + + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; + } + , configurable: true + }); +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/proxify.js b/WebServer/node_modules/chai/lib/chai/utils/proxify.js new file mode 100644 index 000000000..4bbbee38c --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/proxify.js @@ -0,0 +1,147 @@ +var config = require('../config'); +var flag = require('./flag'); +var getProperties = require('./getProperties'); +var isProxyEnabled = require('./isProxyEnabled'); + +/*! + * Chai - proxify utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .proxify(object) + * + * Return a proxy of given object that throws an error when a non-existent + * property is read. By default, the root cause is assumed to be a misspelled + * property, and thus an attempt is made to offer a reasonable suggestion from + * the list of existing properties. However, if a nonChainableMethodName is + * provided, then the root cause is instead a failure to invoke a non-chainable + * method prior to reading the non-existent property. + * + * If proxies are unsupported or disabled via the user's Chai config, then + * return object without modification. + * + * @param {Object} obj + * @param {String} nonChainableMethodName + * @namespace Utils + * @name proxify + */ + +var builtins = ['__flags', '__methods', '_obj', 'assert']; + +module.exports = function proxify(obj, nonChainableMethodName) { + if (!isProxyEnabled()) return obj; + + return new Proxy(obj, { + get: function proxyGetter(target, property) { + // This check is here because we should not throw errors on Symbol properties + // such as `Symbol.toStringTag`. + // The values for which an error should be thrown can be configured using + // the `config.proxyExcludedKeys` setting. + if (typeof property === 'string' && + config.proxyExcludedKeys.indexOf(property) === -1 && + !Reflect.has(target, property)) { + // Special message for invalid property access of non-chainable methods. + if (nonChainableMethodName) { + throw Error('Invalid Chai property: ' + nonChainableMethodName + '.' + + property + '. See docs for proper usage of "' + + nonChainableMethodName + '".'); + } + + // If the property is reasonably close to an existing Chai property, + // suggest that property to the user. Only suggest properties with a + // distance less than 4. + var suggestion = null; + var suggestionDistance = 4; + getProperties(target).forEach(function(prop) { + if ( + !Object.prototype.hasOwnProperty(prop) && + builtins.indexOf(prop) === -1 + ) { + var dist = stringDistanceCapped( + property, + prop, + suggestionDistance + ); + if (dist < suggestionDistance) { + suggestion = prop; + suggestionDistance = dist; + } + } + }); + + if (suggestion !== null) { + throw Error('Invalid Chai property: ' + property + + '. Did you mean "' + suggestion + '"?'); + } else { + throw Error('Invalid Chai property: ' + property); + } + } + + // Use this proxy getter as the starting point for removing implementation + // frames from the stack trace of a failed assertion. For property + // assertions, this prevents the proxy getter from showing up in the stack + // trace since it's invoked before the property getter. For method and + // chainable method assertions, this flag will end up getting changed to + // the method wrapper, which is good since this frame will no longer be in + // the stack once the method is invoked. Note that Chai builtin assertion + // properties such as `__flags` are skipped since this is only meant to + // capture the starting point of an assertion. This step is also skipped + // if the `lockSsfi` flag is set, thus indicating that this assertion is + // being called from within another assertion. In that case, the `ssfi` + // flag is already set to the outer assertion's starting point. + if (builtins.indexOf(property) === -1 && !flag(target, 'lockSsfi')) { + flag(target, 'ssfi', proxyGetter); + } + + return Reflect.get(target, property); + } + }); +}; + +/** + * # stringDistanceCapped(strA, strB, cap) + * Return the Levenshtein distance between two strings, but no more than cap. + * @param {string} strA + * @param {string} strB + * @param {number} number + * @return {number} min(string distance between strA and strB, cap) + * @api private + */ + +function stringDistanceCapped(strA, strB, cap) { + if (Math.abs(strA.length - strB.length) >= cap) { + return cap; + } + + var memo = []; + // `memo` is a two-dimensional array containing distances. + // memo[i][j] is the distance between strA.slice(0, i) and + // strB.slice(0, j). + for (var i = 0; i <= strA.length; i++) { + memo[i] = Array(strB.length + 1).fill(0); + memo[i][0] = i; + } + for (var j = 0; j < strB.length; j++) { + memo[0][j] = j; + } + + for (var i = 1; i <= strA.length; i++) { + var ch = strA.charCodeAt(i - 1); + for (var j = 1; j <= strB.length; j++) { + if (Math.abs(i - j) >= cap) { + memo[i][j] = cap; + continue; + } + memo[i][j] = Math.min( + memo[i - 1][j] + 1, + memo[i][j - 1] + 1, + memo[i - 1][j - 1] + + (ch === strB.charCodeAt(j - 1) ? 0 : 1) + ); + } + } + + return memo[strA.length][strB.length]; +} diff --git a/WebServer/node_modules/chai/lib/chai/utils/test.js b/WebServer/node_modules/chai/lib/chai/utils/test.js new file mode 100644 index 000000000..09886d443 --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/test.js @@ -0,0 +1,28 @@ +/*! + * Chai - test utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var flag = require('./flag'); + +/** + * ### .test(object, expression) + * + * Test and object for expression. + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @namespace Utils + * @name test + */ + +module.exports = function test(obj, args) { + var negate = flag(obj, 'negate') + , expr = args[0]; + return negate ? !expr : expr; +}; diff --git a/WebServer/node_modules/chai/lib/chai/utils/transferFlags.js b/WebServer/node_modules/chai/lib/chai/utils/transferFlags.js new file mode 100644 index 000000000..7b501900d --- /dev/null +++ b/WebServer/node_modules/chai/lib/chai/utils/transferFlags.js @@ -0,0 +1,45 @@ +/*! + * Chai - transferFlags utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .transferFlags(assertion, object, includeAll = true) + * + * Transfer all the flags for `assertion` to `object`. If + * `includeAll` is set to `false`, then the base Chai + * assertion flags (namely `object`, `ssfi`, `lockSsfi`, + * and `message`) will not be transferred. + * + * + * var newAssertion = new Assertion(); + * utils.transferFlags(assertion, newAssertion); + * + * var anotherAssertion = new Assertion(myObj); + * utils.transferFlags(assertion, anotherAssertion, false); + * + * @param {Assertion} assertion the assertion to transfer the flags from + * @param {Object} object the object to transfer the flags to; usually a new assertion + * @param {Boolean} includeAll + * @namespace Utils + * @name transferFlags + * @api private + */ + +module.exports = function transferFlags(assertion, object, includeAll) { + var flags = assertion.__flags || (assertion.__flags = Object.create(null)); + + if (!object.__flags) { + object.__flags = Object.create(null); + } + + includeAll = arguments.length === 3 ? includeAll : true; + + for (var flag in flags) { + if (includeAll || + (flag !== 'object' && flag !== 'ssfi' && flag !== 'lockSsfi' && flag != 'message')) { + object.__flags[flag] = flags[flag]; + } + } +}; diff --git a/WebServer/node_modules/chai/package.json b/WebServer/node_modules/chai/package.json new file mode 100644 index 000000000..a01210c20 --- /dev/null +++ b/WebServer/node_modules/chai/package.json @@ -0,0 +1,99 @@ +{ + "_args": [ + [ + "chai@4.2.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "chai@4.2.0", + "_id": "chai@4.2.0", + "_inBundle": false, + "_integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "_location": "/chai", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "chai@4.2.0", + "name": "chai", + "escapedName": "chai", + "rawSpec": "4.2.0", + "saveSpec": null, + "fetchSpec": "4.2.0" + }, + "_requiredBy": [ + "/@superalgos/mqservice" + ], + "_resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "_spec": "4.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jake Luer", + "email": "jake@alogicalparadox.com" + }, + "bugs": { + "url": "https://github.com/chaijs/chai/issues" + }, + "contributors": [ + { + "name": "Jake Luer", + "email": "jake@alogicalparadox.com" + }, + { + "name": "Domenic Denicola", + "email": "domenic@domenicdenicola.com", + "url": "http://domenicdenicola.com" + }, + { + "name": "Veselin Todorov", + "email": "hi@vesln.com" + }, + { + "name": "John Firebaugh", + "email": "john.firebaugh@gmail.com" + } + ], + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + }, + "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", + "devDependencies": { + "browserify": "^16.0.0", + "bump-cli": "^1.1.3", + "codecov": "^3.0.0", + "istanbul": "^0.4.3", + "karma": "^2.0.0", + "karma-firefox-launcher": "^1.0.0", + "karma-mocha": "^1.0.1", + "karma-phantomjs-launcher": "^1.0.0", + "karma-sauce-launcher": "^1.2.0", + "mocha": "^5.0.0" + }, + "engines": { + "node": ">=4" + }, + "homepage": "http://chaijs.com", + "keywords": [ + "test", + "assertion", + "assert", + "testing", + "chai" + ], + "license": "MIT", + "main": "./index", + "name": "chai", + "repository": { + "type": "git", + "url": "git+https://github.com/chaijs/chai.git" + }, + "scripts": { + "test": "make test" + }, + "version": "4.2.0" +} diff --git a/WebServer/node_modules/chai/register-assert.js b/WebServer/node_modules/chai/register-assert.js new file mode 100644 index 000000000..f80cb4ded --- /dev/null +++ b/WebServer/node_modules/chai/register-assert.js @@ -0,0 +1 @@ +global.assert = require('./').assert; diff --git a/WebServer/node_modules/chai/register-expect.js b/WebServer/node_modules/chai/register-expect.js new file mode 100644 index 000000000..f905c3a20 --- /dev/null +++ b/WebServer/node_modules/chai/register-expect.js @@ -0,0 +1 @@ +global.expect = require('./').expect; diff --git a/WebServer/node_modules/chai/register-should.js b/WebServer/node_modules/chai/register-should.js new file mode 100644 index 000000000..5dab96fc5 --- /dev/null +++ b/WebServer/node_modules/chai/register-should.js @@ -0,0 +1 @@ +global.should = require('./').should(); diff --git a/WebServer/node_modules/chai/sauce.browsers.js b/WebServer/node_modules/chai/sauce.browsers.js new file mode 100644 index 000000000..62252dc42 --- /dev/null +++ b/WebServer/node_modules/chai/sauce.browsers.js @@ -0,0 +1,102 @@ + +/*! + * Chrome + */ + +exports['SL_Chrome'] = { + base: 'SauceLabs' + , browserName: 'chrome' +}; + +/*! + * Firefox + */ + + exports['SL_Firefox'] = { + base: 'SauceLabs' + , browserName: 'firefox' + }; + + exports['SL_Firefox_ESR'] = { + base: 'SauceLabs' + , browserName: 'firefox' + , version: 38 + }; + +/*! + * Internet Explorer + */ + +exports['SL_IE'] = { + base: 'SauceLabs' + , browserName: 'internet explorer' +}; + +exports['SL_IE_Old'] = { + base: 'SauceLabs' + , browserName: 'internet explorer' + , version: 10 +}; + +exports['SL_Edge'] = { + base: 'SauceLabs' + , browserName: 'microsoftedge' +}; + +/*! + * Safari + */ + +exports['SL_Safari'] = { + base: 'SauceLabs' + , browserName: 'safari' + , platform: 'Mac 10.11' +}; + +/*! + * iPhone + */ + +/*! + * TODO: These take forever to boot or shut down. Causes timeout. + * + +exports['SL_iPhone_6'] = { + base: 'SauceLabs' + , browserName: 'iphone' + , platform: 'Mac 10.8' + , version: '6' +}; + +exports['SL_iPhone_5-1'] = { + base: 'SauceLabs' + , browserName: 'iphone' + , platform: 'Mac 10.8' + , version: '5.1' +}; + +exports['SL_iPhone_5'] = { + base: 'SauceLabs' + , browserName: 'iphone' + , platform: 'Mac 10.6' + , version: '5' +}; + +*/ + +/*! + * Android + */ + +/*! + * TODO: fails because of error serialization + * + +exports['SL_Android_4'] = { + base: 'SauceLabs' + , browserName: 'android' + , platform: 'Linux' + , version: '4' +}; + +*/ diff --git a/WebServer/node_modules/chalk/index.js b/WebServer/node_modules/chalk/index.js new file mode 100644 index 000000000..1cc5fa89a --- /dev/null +++ b/WebServer/node_modules/chalk/index.js @@ -0,0 +1,228 @@ +'use strict'; +const escapeStringRegexp = require('escape-string-regexp'); +const ansiStyles = require('ansi-styles'); +const stdoutColor = require('supports-color').stdout; + +const template = require('./templates.js'); + +const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); + +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; + +// `color-convert` models to exclude from the Chalk API due to conflicts and such +const skipModels = new Set(['gray']); + +const styles = Object.create(null); + +function applyOptions(obj, options) { + options = options || {}; + + // Detect level if not set manually + const scLevel = stdoutColor ? stdoutColor.level : 0; + obj.level = options.level === undefined ? scLevel : options.level; + obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; +} + +function Chalk(options) { + // We check for this.template here since calling `chalk.constructor()` + // by itself will have a `this` of a previously constructed chalk object + if (!this || !(this instanceof Chalk) || this.template) { + const chalk = {}; + applyOptions(chalk, options); + + chalk.template = function () { + const args = [].slice.call(arguments); + return chalkTag.apply(null, [chalk.template].concat(args)); + }; + + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); + + chalk.template.constructor = Chalk; + + return chalk.template; + } + + applyOptions(this, options); +} + +// Use bright blue on Windows as the normal blue color is illegible +if (isSimpleWindowsTerm) { + ansiStyles.blue.open = '\u001B[94m'; +} + +for (const key of Object.keys(ansiStyles)) { + ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); + + styles[key] = { + get() { + const codes = ansiStyles[key]; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); + } + }; +} + +styles.visible = { + get() { + return build.call(this, this._styles || [], true, 'visible'); + } +}; + +ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); +for (const model of Object.keys(ansiStyles.color.ansi)) { + if (skipModels.has(model)) { + continue; + } + + styles[model] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.color.close, + closeRe: ansiStyles.color.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} + +ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); +for (const model of Object.keys(ansiStyles.bgColor.ansi)) { + if (skipModels.has(model)) { + continue; + } + + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.bgColor.close, + closeRe: ansiStyles.bgColor.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} + +const proto = Object.defineProperties(() => {}, styles); + +function build(_styles, _empty, key) { + const builder = function () { + return applyStyle.apply(builder, arguments); + }; + + builder._styles = _styles; + builder._empty = _empty; + + const self = this; + + Object.defineProperty(builder, 'level', { + enumerable: true, + get() { + return self.level; + }, + set(level) { + self.level = level; + } + }); + + Object.defineProperty(builder, 'enabled', { + enumerable: true, + get() { + return self.enabled; + }, + set(enabled) { + self.enabled = enabled; + } + }); + + // See below for fix regarding invisible grey/dim combination on Windows + builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + + // `__proto__` is used because we must return a function, but there is + // no way to create a function with a different prototype + builder.__proto__ = proto; // eslint-disable-line no-proto + + return builder; +} + +function applyStyle() { + // Support varags, but simply cast to string in case there's only one arg + const args = arguments; + const argsLen = args.length; + let str = String(arguments[0]); + + if (argsLen === 0) { + return ''; + } + + if (argsLen > 1) { + // Don't slice `arguments`, it prevents V8 optimizations + for (let a = 1; a < argsLen; a++) { + str += ' ' + args[a]; + } + } + + if (!this.enabled || this.level <= 0 || !str) { + return this._empty ? '' : str; + } + + // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, + // see https://github.com/chalk/chalk/issues/58 + // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. + const originalDim = ansiStyles.dim.open; + if (isSimpleWindowsTerm && this.hasGrey) { + ansiStyles.dim.open = ''; + } + + for (const code of this._styles.slice().reverse()) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + str = code.open + str.replace(code.closeRe, code.open) + code.close; + + // Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS + // https://github.com/chalk/chalk/pull/92 + str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); + } + + // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue + ansiStyles.dim.open = originalDim; + + return str; +} + +function chalkTag(chalk, strings) { + if (!Array.isArray(strings)) { + // If chalk() was called by itself or with a string, + // return the string itself as a string. + return [].slice.call(arguments, 1).join(' '); + } + + const args = [].slice.call(arguments, 2); + const parts = [strings.raw[0]]; + + for (let i = 1; i < strings.length; i++) { + parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); + parts.push(String(strings.raw[i])); + } + + return template(chalk, parts.join('')); +} + +Object.defineProperties(Chalk.prototype, styles); + +module.exports = Chalk(); // eslint-disable-line new-cap +module.exports.supportsColor = stdoutColor; +module.exports.default = module.exports; // For TypeScript diff --git a/WebServer/node_modules/chalk/node_modules/supports-color/browser.js b/WebServer/node_modules/chalk/node_modules/supports-color/browser.js new file mode 100644 index 000000000..62afa3a74 --- /dev/null +++ b/WebServer/node_modules/chalk/node_modules/supports-color/browser.js @@ -0,0 +1,5 @@ +'use strict'; +module.exports = { + stdout: false, + stderr: false +}; diff --git a/WebServer/node_modules/chalk/node_modules/supports-color/index.js b/WebServer/node_modules/chalk/node_modules/supports-color/index.js new file mode 100644 index 000000000..1704131bd --- /dev/null +++ b/WebServer/node_modules/chalk/node_modules/supports-color/index.js @@ -0,0 +1,131 @@ +'use strict'; +const os = require('os'); +const hasFlag = require('has-flag'); + +const env = process.env; + +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false')) { + forceColor = false; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = true; +} +if ('FORCE_COLOR' in env) { + forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; +} + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} + +function supportsColor(stream) { + if (forceColor === false) { + return 0; + } + + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + + if (stream && !stream.isTTY && forceColor !== true) { + return 0; + } + + const min = forceColor ? 1 : 0; + + if (process.platform === 'win32') { + // Node.js 7.5.0 is the first version of Node.js to include a patch to + // libuv that enables 256 color output on Windows. Anything earlier and it + // won't work. However, here we target Node.js 8 at minimum as it is an LTS + // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows + // release that supports 256 colors. Windows 10 build 14931 is the first release + // that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(process.versions.node.split('.')[0]) >= 8 && + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + if (env.TERM === 'dumb') { + return min; + } + + return min; +} + +function getSupportLevel(stream) { + const level = supportsColor(stream); + return translateLevel(level); +} + +module.exports = { + supportsColor: getSupportLevel, + stdout: getSupportLevel(process.stdout), + stderr: getSupportLevel(process.stderr) +}; diff --git a/WebServer/node_modules/chalk/node_modules/supports-color/package.json b/WebServer/node_modules/chalk/node_modules/supports-color/package.json new file mode 100644 index 000000000..8f556cc0d --- /dev/null +++ b/WebServer/node_modules/chalk/node_modules/supports-color/package.json @@ -0,0 +1,88 @@ +{ + "_args": [ + [ + "supports-color@5.5.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "supports-color@5.5.0", + "_id": "supports-color@5.5.0", + "_inBundle": false, + "_integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "_location": "/chalk/supports-color", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "supports-color@5.5.0", + "name": "supports-color", + "escapedName": "supports-color", + "rawSpec": "5.5.0", + "saveSpec": null, + "fetchSpec": "5.5.0" + }, + "_requiredBy": [ + "/chalk" + ], + "_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "_spec": "5.5.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "browser": "browser.js", + "bugs": { + "url": "https://github.com/chalk/supports-color/issues" + }, + "dependencies": { + "has-flag": "^3.0.0" + }, + "description": "Detect whether a terminal supports color", + "devDependencies": { + "ava": "^0.25.0", + "import-fresh": "^2.0.0", + "xo": "^0.20.0" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js", + "browser.js" + ], + "homepage": "https://github.com/chalk/supports-color#readme", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "ansi", + "styles", + "tty", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "support", + "supports", + "capability", + "detect", + "truecolor", + "16m" + ], + "license": "MIT", + "name": "supports-color", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/supports-color.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "5.5.0" +} diff --git a/WebServer/node_modules/chalk/package.json b/WebServer/node_modules/chalk/package.json new file mode 100644 index 000000000..9315806a2 --- /dev/null +++ b/WebServer/node_modules/chalk/package.json @@ -0,0 +1,108 @@ +{ + "_args": [ + [ + "chalk@2.4.2", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "chalk@2.4.2", + "_id": "chalk@2.4.2", + "_inBundle": false, + "_integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "_location": "/chalk", + "_phantomChildren": { + "has-flag": "3.0.0" + }, + "_requested": { + "type": "version", + "registry": true, + "raw": "chalk@2.4.2", + "name": "chalk", + "escapedName": "chalk", + "rawSpec": "2.4.2", + "saveSpec": null, + "fetchSpec": "2.4.2" + }, + "_requiredBy": [ + "/log-symbols" + ], + "_resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "_spec": "2.4.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "bugs": { + "url": "https://github.com/chalk/chalk/issues" + }, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "description": "Terminal string styling done right", + "devDependencies": { + "ava": "*", + "coveralls": "^3.0.0", + "execa": "^0.9.0", + "flow-bin": "^0.68.0", + "import-fresh": "^2.0.0", + "matcha": "^0.7.0", + "nyc": "^11.0.2", + "resolve-from": "^4.0.0", + "typescript": "^2.5.3", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js", + "templates.js", + "types/index.d.ts", + "index.js.flow" + ], + "homepage": "https://github.com/chalk/chalk#readme", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "str", + "ansi", + "style", + "styles", + "tty", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "chalk", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/chalk.git" + }, + "scripts": { + "bench": "matcha benchmark.js", + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "test": "xo && tsc --project types && flow --max-warnings=0 && nyc ava" + }, + "types": "types/index.d.ts", + "version": "2.4.2", + "xo": { + "envs": [ + "node", + "mocha" + ], + "ignores": [ + "test/_flow.js" + ] + } +} diff --git a/WebServer/node_modules/chalk/templates.js b/WebServer/node_modules/chalk/templates.js new file mode 100644 index 000000000..dbdf9b221 --- /dev/null +++ b/WebServer/node_modules/chalk/templates.js @@ -0,0 +1,128 @@ +'use strict'; +const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; +const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; +const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; +const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi; + +const ESCAPES = new Map([ + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], + ['b', '\b'], + ['f', '\f'], + ['v', '\v'], + ['0', '\0'], + ['\\', '\\'], + ['e', '\u001B'], + ['a', '\u0007'] +]); + +function unescape(c) { + if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + return String.fromCharCode(parseInt(c.slice(1), 16)); + } + + return ESCAPES.get(c) || c; +} + +function parseArguments(name, args) { + const results = []; + const chunks = args.trim().split(/\s*,\s*/g); + let matches; + + for (const chunk of chunks) { + if (!isNaN(chunk)) { + results.push(Number(chunk)); + } else if ((matches = chunk.match(STRING_REGEX))) { + results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr)); + } else { + throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); + } + } + + return results; +} + +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; + + const results = []; + let matches; + + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; + + if (matches[2]) { + const args = parseArguments(name, matches[2]); + results.push([name].concat(args)); + } else { + results.push([name]); + } + } + + return results; +} + +function buildStyle(chalk, styles) { + const enabled = {}; + + for (const layer of styles) { + for (const style of layer.styles) { + enabled[style[0]] = layer.inverse ? null : style.slice(1); + } + } + + let current = chalk; + for (const styleName of Object.keys(enabled)) { + if (Array.isArray(enabled[styleName])) { + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); + } + + if (enabled[styleName].length > 0) { + current = current[styleName].apply(current, enabled[styleName]); + } else { + current = current[styleName]; + } + } + } + + return current; +} + +module.exports = (chalk, tmp) => { + const styles = []; + const chunks = []; + let chunk = []; + + // eslint-disable-next-line max-params + tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => { + if (escapeChar) { + chunk.push(unescape(escapeChar)); + } else if (style) { + const str = chunk.join(''); + chunk = []; + chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); + styles.push({inverse, styles: parseStyle(style)}); + } else if (close) { + if (styles.length === 0) { + throw new Error('Found extraneous } in Chalk template literal'); + } + + chunks.push(buildStyle(chalk, styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(chr); + } + }); + + chunks.push(chunk.join('')); + + if (styles.length > 0) { + const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; + throw new Error(errMsg); + } + + return chunks.join(''); +}; diff --git a/WebServer/node_modules/check-error/check-error.js b/WebServer/node_modules/check-error/check-error.js new file mode 100644 index 000000000..8e47a65df --- /dev/null +++ b/WebServer/node_modules/check-error/check-error.js @@ -0,0 +1,176 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.checkError = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + * MIT Licensed + */ + +/** + * ### .checkError + * + * Checks that an error conforms to a given set of criteria and/or retrieves information about it. + * + * @api public + */ + +/** + * ### .compatibleInstance(thrown, errorLike) + * + * Checks if two instances are compatible (strict equal). + * Returns false if errorLike is not an instance of Error, because instances + * can only be compatible if they're both error instances. + * + * @name compatibleInstance + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleInstance(thrown, errorLike) { + return errorLike instanceof Error && thrown === errorLike; +} + +/** + * ### .compatibleConstructor(thrown, errorLike) + * + * Checks if two constructors are compatible. + * This function can receive either an error constructor or + * an error instance as the `errorLike` argument. + * Constructors are compatible if they're the same or if one is + * an instance of another. + * + * @name compatibleConstructor + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleConstructor(thrown, errorLike) { + if (errorLike instanceof Error) { + // If `errorLike` is an instance of any error we compare their constructors + return thrown.constructor === errorLike.constructor || thrown instanceof errorLike.constructor; + } else if (errorLike.prototype instanceof Error || errorLike === Error) { + // If `errorLike` is a constructor that inherits from Error, we compare `thrown` to `errorLike` directly + return thrown.constructor === errorLike || thrown instanceof errorLike; + } + + return false; +} + +/** + * ### .compatibleMessage(thrown, errMatcher) + * + * Checks if an error's message is compatible with a matcher (String or RegExp). + * If the message contains the String or passes the RegExp test, + * it is considered compatible. + * + * @name compatibleMessage + * @param {Error} thrown error + * @param {String|RegExp} errMatcher to look for into the message + * @namespace Utils + * @api public + */ + +function compatibleMessage(thrown, errMatcher) { + var comparisonString = typeof thrown === 'string' ? thrown : thrown.message; + if (errMatcher instanceof RegExp) { + return errMatcher.test(comparisonString); + } else if (typeof errMatcher === 'string') { + return comparisonString.indexOf(errMatcher) !== -1; // eslint-disable-line no-magic-numbers + } + + return false; +} + +/** + * ### .getFunctionName(constructorFn) + * + * Returns the name of a function. + * This also includes a polyfill function if `constructorFn.name` is not defined. + * + * @name getFunctionName + * @param {Function} constructorFn + * @namespace Utils + * @api private + */ + +var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\(\/]+)/; +function getFunctionName(constructorFn) { + var name = ''; + if (typeof constructorFn.name === 'undefined') { + // Here we run a polyfill if constructorFn.name is not defined + var match = String(constructorFn).match(functionNameMatch); + if (match) { + name = match[1]; + } + } else { + name = constructorFn.name; + } + + return name; +} + +/** + * ### .getConstructorName(errorLike) + * + * Gets the constructor name for an Error instance or constructor itself. + * + * @name getConstructorName + * @param {Error|ErrorConstructor} errorLike + * @namespace Utils + * @api public + */ + +function getConstructorName(errorLike) { + var constructorName = errorLike; + if (errorLike instanceof Error) { + constructorName = getFunctionName(errorLike.constructor); + } else if (typeof errorLike === 'function') { + // If `err` is not an instance of Error it is an error constructor itself or another function. + // If we've got a common function we get its name, otherwise we may need to create a new instance + // of the error just in case it's a poorly-constructed error. Please see chaijs/chai/issues/45 to know more. + constructorName = getFunctionName(errorLike).trim() || + getFunctionName(new errorLike()); // eslint-disable-line new-cap + } + + return constructorName; +} + +/** + * ### .getMessage(errorLike) + * + * Gets the error message from an error. + * If `err` is a String itself, we return it. + * If the error has no message, we return an empty string. + * + * @name getMessage + * @param {Error|String} errorLike + * @namespace Utils + * @api public + */ + +function getMessage(errorLike) { + var msg = ''; + if (errorLike && errorLike.message) { + msg = errorLike.message; + } else if (typeof errorLike === 'string') { + msg = errorLike; + } + + return msg; +} + +module.exports = { + compatibleInstance: compatibleInstance, + compatibleConstructor: compatibleConstructor, + compatibleMessage: compatibleMessage, + getMessage: getMessage, + getConstructorName: getConstructorName, +}; + +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/WebServer/node_modules/check-error/index.js b/WebServer/node_modules/check-error/index.js new file mode 100644 index 000000000..603ef50b1 --- /dev/null +++ b/WebServer/node_modules/check-error/index.js @@ -0,0 +1,172 @@ +'use strict'; + +/* ! + * Chai - checkError utility + * Copyright(c) 2012-2016 Jake Luer + * MIT Licensed + */ + +/** + * ### .checkError + * + * Checks that an error conforms to a given set of criteria and/or retrieves information about it. + * + * @api public + */ + +/** + * ### .compatibleInstance(thrown, errorLike) + * + * Checks if two instances are compatible (strict equal). + * Returns false if errorLike is not an instance of Error, because instances + * can only be compatible if they're both error instances. + * + * @name compatibleInstance + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleInstance(thrown, errorLike) { + return errorLike instanceof Error && thrown === errorLike; +} + +/** + * ### .compatibleConstructor(thrown, errorLike) + * + * Checks if two constructors are compatible. + * This function can receive either an error constructor or + * an error instance as the `errorLike` argument. + * Constructors are compatible if they're the same or if one is + * an instance of another. + * + * @name compatibleConstructor + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleConstructor(thrown, errorLike) { + if (errorLike instanceof Error) { + // If `errorLike` is an instance of any error we compare their constructors + return thrown.constructor === errorLike.constructor || thrown instanceof errorLike.constructor; + } else if (errorLike.prototype instanceof Error || errorLike === Error) { + // If `errorLike` is a constructor that inherits from Error, we compare `thrown` to `errorLike` directly + return thrown.constructor === errorLike || thrown instanceof errorLike; + } + + return false; +} + +/** + * ### .compatibleMessage(thrown, errMatcher) + * + * Checks if an error's message is compatible with a matcher (String or RegExp). + * If the message contains the String or passes the RegExp test, + * it is considered compatible. + * + * @name compatibleMessage + * @param {Error} thrown error + * @param {String|RegExp} errMatcher to look for into the message + * @namespace Utils + * @api public + */ + +function compatibleMessage(thrown, errMatcher) { + var comparisonString = typeof thrown === 'string' ? thrown : thrown.message; + if (errMatcher instanceof RegExp) { + return errMatcher.test(comparisonString); + } else if (typeof errMatcher === 'string') { + return comparisonString.indexOf(errMatcher) !== -1; // eslint-disable-line no-magic-numbers + } + + return false; +} + +/** + * ### .getFunctionName(constructorFn) + * + * Returns the name of a function. + * This also includes a polyfill function if `constructorFn.name` is not defined. + * + * @name getFunctionName + * @param {Function} constructorFn + * @namespace Utils + * @api private + */ + +var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\(\/]+)/; +function getFunctionName(constructorFn) { + var name = ''; + if (typeof constructorFn.name === 'undefined') { + // Here we run a polyfill if constructorFn.name is not defined + var match = String(constructorFn).match(functionNameMatch); + if (match) { + name = match[1]; + } + } else { + name = constructorFn.name; + } + + return name; +} + +/** + * ### .getConstructorName(errorLike) + * + * Gets the constructor name for an Error instance or constructor itself. + * + * @name getConstructorName + * @param {Error|ErrorConstructor} errorLike + * @namespace Utils + * @api public + */ + +function getConstructorName(errorLike) { + var constructorName = errorLike; + if (errorLike instanceof Error) { + constructorName = getFunctionName(errorLike.constructor); + } else if (typeof errorLike === 'function') { + // If `err` is not an instance of Error it is an error constructor itself or another function. + // If we've got a common function we get its name, otherwise we may need to create a new instance + // of the error just in case it's a poorly-constructed error. Please see chaijs/chai/issues/45 to know more. + constructorName = getFunctionName(errorLike).trim() || + getFunctionName(new errorLike()); // eslint-disable-line new-cap + } + + return constructorName; +} + +/** + * ### .getMessage(errorLike) + * + * Gets the error message from an error. + * If `err` is a String itself, we return it. + * If the error has no message, we return an empty string. + * + * @name getMessage + * @param {Error|String} errorLike + * @namespace Utils + * @api public + */ + +function getMessage(errorLike) { + var msg = ''; + if (errorLike && errorLike.message) { + msg = errorLike.message; + } else if (typeof errorLike === 'string') { + msg = errorLike; + } + + return msg; +} + +module.exports = { + compatibleInstance: compatibleInstance, + compatibleConstructor: compatibleConstructor, + compatibleMessage: compatibleMessage, + getMessage: getMessage, + getConstructorName: getConstructorName, +}; diff --git a/WebServer/node_modules/check-error/package.json b/WebServer/node_modules/check-error/package.json new file mode 100644 index 000000000..cbf569138 --- /dev/null +++ b/WebServer/node_modules/check-error/package.json @@ -0,0 +1,133 @@ +{ + "_args": [ + [ + "check-error@1.0.2", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "check-error@1.0.2", + "_id": "check-error@1.0.2", + "_inBundle": false, + "_integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "_location": "/check-error", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "check-error@1.0.2", + "name": "check-error", + "escapedName": "check-error", + "rawSpec": "1.0.2", + "saveSpec": null, + "fetchSpec": "1.0.2" + }, + "_requiredBy": [ + "/chai" + ], + "_resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "_spec": "1.0.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jake Luer", + "email": "jake@alogicalparadox.com", + "url": "http://alogicalparadox.com" + }, + "bugs": { + "url": "https://github.com/chaijs/check-error/issues" + }, + "config": { + "ghooks": { + "commit-msg": "validate-commit-msg" + } + }, + "contributors": [ + { + "name": "David Losert", + "url": "https://github.com/davelosert" + }, + { + "name": "Keith Cirkel", + "url": "https://github.com/keithamus" + }, + { + "name": "Miroslav Bajtoš", + "url": "https://github.com/bajtos" + }, + { + "name": "Lucas Fernandes da Costa", + "url": "https://github.com/lucasfcosta" + } + ], + "dependencies": {}, + "description": "Error comparison and information related utility for node and the browser", + "devDependencies": { + "browserify": "^13.0.0", + "browserify-istanbul": "^1.0.0", + "coveralls": "2.11.9", + "eslint": "^2.4.0", + "eslint-config-strict": "^8.5.0", + "eslint-plugin-filenames": "^0.2.0", + "ghooks": "^1.0.1", + "istanbul": "^0.4.2", + "karma": "^0.13.22", + "karma-browserify": "^5.0.2", + "karma-coverage": "^0.5.5", + "karma-mocha": "^0.2.2", + "karma-phantomjs-launcher": "^1.0.0", + "karma-sauce-launcher": "^0.3.1", + "lcov-result-merger": "^1.0.2", + "mocha": "^2.4.5", + "phantomjs-prebuilt": "^2.1.5", + "semantic-release": "^4.3.5", + "simple-assert": "^1.0.0", + "travis-after-all": "^1.4.4", + "validate-commit-msg": "^2.3.1" + }, + "engines": { + "node": "*" + }, + "eslintConfig": { + "extends": [ + "strict/es5" + ], + "env": { + "es6": true + }, + "globals": { + "HTMLElement": false + }, + "rules": { + "complexity": 0, + "max-statements": 0 + } + }, + "files": [ + "index.js", + "check-error.js" + ], + "homepage": "https://github.com/chaijs/check-error#readme", + "keywords": [ + "check-error", + "error", + "chai util" + ], + "license": "MIT", + "main": "./index.js", + "name": "check-error", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/chaijs/check-error.git" + }, + "scripts": { + "build": "browserify --bare $npm_package_main --standalone checkError -o check-error.js", + "lint": "eslint --ignore-path .gitignore .", + "prepublish": "npm run build", + "pretest": "npm run lint", + "semantic-release": "semantic-release pre && npm publish && semantic-release post", + "test": "npm run test:node && npm run test:browser && npm run upload-coverage", + "test:browser": "karma start --singleRun=true", + "test:node": "istanbul cover _mocha", + "upload-coverage": "lcov-result-merger 'coverage/**/lcov.info' | coveralls; exit 0" + }, + "version": "1.0.2" +} diff --git a/WebServer/node_modules/cliui/index.js b/WebServer/node_modules/cliui/index.js new file mode 100644 index 000000000..b42d9824e --- /dev/null +++ b/WebServer/node_modules/cliui/index.js @@ -0,0 +1,324 @@ +var stringWidth = require('string-width') +var stripAnsi = require('strip-ansi') +var wrap = require('wrap-ansi') +var align = { + right: alignRight, + center: alignCenter +} +var top = 0 +var right = 1 +var bottom = 2 +var left = 3 + +function UI (opts) { + this.width = opts.width + this.wrap = opts.wrap + this.rows = [] +} + +UI.prototype.span = function () { + var cols = this.div.apply(this, arguments) + cols.span = true +} + +UI.prototype.resetOutput = function () { + this.rows = [] +} + +UI.prototype.div = function () { + if (arguments.length === 0) this.div('') + if (this.wrap && this._shouldApplyLayoutDSL.apply(this, arguments)) { + return this._applyLayoutDSL(arguments[0]) + } + + var cols = [] + + for (var i = 0, arg; (arg = arguments[i]) !== undefined; i++) { + if (typeof arg === 'string') cols.push(this._colFromString(arg)) + else cols.push(arg) + } + + this.rows.push(cols) + return cols +} + +UI.prototype._shouldApplyLayoutDSL = function () { + return arguments.length === 1 && typeof arguments[0] === 'string' && + /[\t\n]/.test(arguments[0]) +} + +UI.prototype._applyLayoutDSL = function (str) { + var _this = this + var rows = str.split('\n') + var leftColumnWidth = 0 + + // simple heuristic for layout, make sure the + // second column lines up along the left-hand. + // don't allow the first column to take up more + // than 50% of the screen. + rows.forEach(function (row) { + var columns = row.split('\t') + if (columns.length > 1 && stringWidth(columns[0]) > leftColumnWidth) { + leftColumnWidth = Math.min( + Math.floor(_this.width * 0.5), + stringWidth(columns[0]) + ) + } + }) + + // generate a table: + // replacing ' ' with padding calculations. + // using the algorithmically generated width. + rows.forEach(function (row) { + var columns = row.split('\t') + _this.div.apply(_this, columns.map(function (r, i) { + return { + text: r.trim(), + padding: _this._measurePadding(r), + width: (i === 0 && columns.length > 1) ? leftColumnWidth : undefined + } + })) + }) + + return this.rows[this.rows.length - 1] +} + +UI.prototype._colFromString = function (str) { + return { + text: str, + padding: this._measurePadding(str) + } +} + +UI.prototype._measurePadding = function (str) { + // measure padding without ansi escape codes + var noAnsi = stripAnsi(str) + return [0, noAnsi.match(/\s*$/)[0].length, 0, noAnsi.match(/^\s*/)[0].length] +} + +UI.prototype.toString = function () { + var _this = this + var lines = [] + + _this.rows.forEach(function (row, i) { + _this.rowToString(row, lines) + }) + + // don't display any lines with the + // hidden flag set. + lines = lines.filter(function (line) { + return !line.hidden + }) + + return lines.map(function (line) { + return line.text + }).join('\n') +} + +UI.prototype.rowToString = function (row, lines) { + var _this = this + var padding + var rrows = this._rasterize(row) + var str = '' + var ts + var width + var wrapWidth + + rrows.forEach(function (rrow, r) { + str = '' + rrow.forEach(function (col, c) { + ts = '' // temporary string used during alignment/padding. + width = row[c].width // the width with padding. + wrapWidth = _this._negatePadding(row[c]) // the width without padding. + + ts += col + + for (var i = 0; i < wrapWidth - stringWidth(col); i++) { + ts += ' ' + } + + // align the string within its column. + if (row[c].align && row[c].align !== 'left' && _this.wrap) { + ts = align[row[c].align](ts, wrapWidth) + if (stringWidth(ts) < wrapWidth) ts += new Array(width - stringWidth(ts)).join(' ') + } + + // apply border and padding to string. + padding = row[c].padding || [0, 0, 0, 0] + if (padding[left]) str += new Array(padding[left] + 1).join(' ') + str += addBorder(row[c], ts, '| ') + str += ts + str += addBorder(row[c], ts, ' |') + if (padding[right]) str += new Array(padding[right] + 1).join(' ') + + // if prior row is span, try to render the + // current row on the prior line. + if (r === 0 && lines.length > 0) { + str = _this._renderInline(str, lines[lines.length - 1]) + } + }) + + // remove trailing whitespace. + lines.push({ + text: str.replace(/ +$/, ''), + span: row.span + }) + }) + + return lines +} + +function addBorder (col, ts, style) { + if (col.border) { + if (/[.']-+[.']/.test(ts)) return '' + else if (ts.trim().length) return style + else return ' ' + } + return '' +} + +// if the full 'source' can render in +// the target line, do so. +UI.prototype._renderInline = function (source, previousLine) { + var leadingWhitespace = source.match(/^ */)[0].length + var target = previousLine.text + var targetTextWidth = stringWidth(target.trimRight()) + + if (!previousLine.span) return source + + // if we're not applying wrapping logic, + // just always append to the span. + if (!this.wrap) { + previousLine.hidden = true + return target + source + } + + if (leadingWhitespace < targetTextWidth) return source + + previousLine.hidden = true + + return target.trimRight() + new Array(leadingWhitespace - targetTextWidth + 1).join(' ') + source.trimLeft() +} + +UI.prototype._rasterize = function (row) { + var _this = this + var i + var rrow + var rrows = [] + var widths = this._columnWidths(row) + var wrapped + + // word wrap all columns, and create + // a data-structure that is easy to rasterize. + row.forEach(function (col, c) { + // leave room for left and right padding. + col.width = widths[c] + if (_this.wrap) wrapped = wrap(col.text, _this._negatePadding(col), { hard: true }).split('\n') + else wrapped = col.text.split('\n') + + if (col.border) { + wrapped.unshift('.' + new Array(_this._negatePadding(col) + 3).join('-') + '.') + wrapped.push("'" + new Array(_this._negatePadding(col) + 3).join('-') + "'") + } + + // add top and bottom padding. + if (col.padding) { + for (i = 0; i < (col.padding[top] || 0); i++) wrapped.unshift('') + for (i = 0; i < (col.padding[bottom] || 0); i++) wrapped.push('') + } + + wrapped.forEach(function (str, r) { + if (!rrows[r]) rrows.push([]) + + rrow = rrows[r] + + for (var i = 0; i < c; i++) { + if (rrow[i] === undefined) rrow.push('') + } + rrow.push(str) + }) + }) + + return rrows +} + +UI.prototype._negatePadding = function (col) { + var wrapWidth = col.width + if (col.padding) wrapWidth -= (col.padding[left] || 0) + (col.padding[right] || 0) + if (col.border) wrapWidth -= 4 + return wrapWidth +} + +UI.prototype._columnWidths = function (row) { + var _this = this + var widths = [] + var unset = row.length + var unsetWidth + var remainingWidth = this.width + + // column widths can be set in config. + row.forEach(function (col, i) { + if (col.width) { + unset-- + widths[i] = col.width + remainingWidth -= col.width + } else { + widths[i] = undefined + } + }) + + // any unset widths should be calculated. + if (unset) unsetWidth = Math.floor(remainingWidth / unset) + widths.forEach(function (w, i) { + if (!_this.wrap) widths[i] = row[i].width || stringWidth(row[i].text) + else if (w === undefined) widths[i] = Math.max(unsetWidth, _minWidth(row[i])) + }) + + return widths +} + +// calculates the minimum width of +// a column, based on padding preferences. +function _minWidth (col) { + var padding = col.padding || [] + var minWidth = 1 + (padding[left] || 0) + (padding[right] || 0) + if (col.border) minWidth += 4 + return minWidth +} + +function getWindowWidth () { + if (typeof process === 'object' && process.stdout && process.stdout.columns) return process.stdout.columns +} + +function alignRight (str, width) { + str = str.trim() + var padding = '' + var strWidth = stringWidth(str) + + if (strWidth < width) { + padding = new Array(width - strWidth + 1).join(' ') + } + + return padding + str +} + +function alignCenter (str, width) { + str = str.trim() + var padding = '' + var strWidth = stringWidth(str.trim()) + + if (strWidth < width) { + padding = new Array(parseInt((width - strWidth) / 2, 10) + 1).join(' ') + } + + return padding + str +} + +module.exports = function (opts) { + opts = opts || {} + + return new UI({ + width: (opts || {}).width || getWindowWidth() || 80, + wrap: typeof opts.wrap === 'boolean' ? opts.wrap : true + }) +} diff --git a/WebServer/node_modules/cliui/package.json b/WebServer/node_modules/cliui/package.json new file mode 100644 index 000000000..8b2e38bf0 --- /dev/null +++ b/WebServer/node_modules/cliui/package.json @@ -0,0 +1,103 @@ +{ + "_args": [ + [ + "cliui@4.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "cliui@4.1.0", + "_id": "cliui@4.1.0", + "_inBundle": false, + "_integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "_location": "/cliui", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "cliui@4.1.0", + "name": "cliui", + "escapedName": "cliui", + "rawSpec": "4.1.0", + "saveSpec": null, + "fetchSpec": "4.1.0" + }, + "_requiredBy": [ + "/yargs", + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "_spec": "4.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/yargs/cliui/issues" + }, + "config": { + "blanket": { + "pattern": [ + "index.js" + ], + "data-cover-never": [ + "node_modules", + "test" + ], + "output-reporter": "spec" + } + }, + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "description": "easily create complex multi-column command-line-interfaces", + "devDependencies": { + "chai": "^3.5.0", + "chalk": "^1.1.2", + "coveralls": "^2.11.8", + "mocha": "^3.0.0", + "nyc": "^10.0.0", + "standard": "^8.0.0", + "standard-version": "^3.0.0" + }, + "engine": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/yargs/cliui#readme", + "keywords": [ + "cli", + "command-line", + "layout", + "design", + "console", + "wrap", + "table" + ], + "license": "ISC", + "main": "index.js", + "name": "cliui", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/yargs/cliui.git" + }, + "scripts": { + "coverage": "nyc --reporter=text-lcov mocha | coveralls", + "pretest": "standard", + "release": "standard-version", + "test": "nyc mocha" + }, + "standard": { + "ignore": [ + "**/example/**" + ], + "globals": [ + "it" + ] + }, + "version": "4.1.0" +} diff --git a/WebServer/node_modules/code-point-at/index.js b/WebServer/node_modules/code-point-at/index.js new file mode 100644 index 000000000..0432fe6a3 --- /dev/null +++ b/WebServer/node_modules/code-point-at/index.js @@ -0,0 +1,32 @@ +/* eslint-disable babel/new-cap, xo/throw-new-error */ +'use strict'; +module.exports = function (str, pos) { + if (str === null || str === undefined) { + throw TypeError(); + } + + str = String(str); + + var size = str.length; + var i = pos ? Number(pos) : 0; + + if (Number.isNaN(i)) { + i = 0; + } + + if (i < 0 || i >= size) { + return undefined; + } + + var first = str.charCodeAt(i); + + if (first >= 0xD800 && first <= 0xDBFF && size > i + 1) { + var second = str.charCodeAt(i + 1); + + if (second >= 0xDC00 && second <= 0xDFFF) { + return ((first - 0xD800) * 0x400) + second - 0xDC00 + 0x10000; + } + } + + return first; +}; diff --git a/WebServer/node_modules/code-point-at/package.json b/WebServer/node_modules/code-point-at/package.json new file mode 100644 index 000000000..df60d2dee --- /dev/null +++ b/WebServer/node_modules/code-point-at/package.json @@ -0,0 +1,73 @@ +{ + "_args": [ + [ + "code-point-at@1.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "code-point-at@1.1.0", + "_id": "code-point-at@1.1.0", + "_inBundle": false, + "_integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "_location": "/code-point-at", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "code-point-at@1.1.0", + "name": "code-point-at", + "escapedName": "code-point-at", + "rawSpec": "1.1.0", + "saveSpec": null, + "fetchSpec": "1.1.0" + }, + "_requiredBy": [ + "/wrap-ansi/string-width" + ], + "_resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "_spec": "1.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/code-point-at/issues" + }, + "description": "ES2015 `String#codePointAt()` ponyfill", + "devDependencies": { + "ava": "*", + "xo": "^0.16.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/code-point-at#readme", + "keywords": [ + "es2015", + "ponyfill", + "polyfill", + "shim", + "string", + "str", + "code", + "point", + "at", + "codepoint", + "unicode" + ], + "license": "MIT", + "name": "code-point-at", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/code-point-at.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.1.0" +} diff --git a/WebServer/node_modules/color-convert/conversions.js b/WebServer/node_modules/color-convert/conversions.js new file mode 100644 index 000000000..32172007e --- /dev/null +++ b/WebServer/node_modules/color-convert/conversions.js @@ -0,0 +1,868 @@ +/* MIT license */ +var cssKeywords = require('color-name'); + +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) + +var reverseKeywords = {}; +for (var key in cssKeywords) { + if (cssKeywords.hasOwnProperty(key)) { + reverseKeywords[cssKeywords[key]] = key; + } +} + +var convert = module.exports = { + rgb: {channels: 3, labels: 'rgb'}, + hsl: {channels: 3, labels: 'hsl'}, + hsv: {channels: 3, labels: 'hsv'}, + hwb: {channels: 3, labels: 'hwb'}, + cmyk: {channels: 4, labels: 'cmyk'}, + xyz: {channels: 3, labels: 'xyz'}, + lab: {channels: 3, labels: 'lab'}, + lch: {channels: 3, labels: 'lch'}, + hex: {channels: 1, labels: ['hex']}, + keyword: {channels: 1, labels: ['keyword']}, + ansi16: {channels: 1, labels: ['ansi16']}, + ansi256: {channels: 1, labels: ['ansi256']}, + hcg: {channels: 3, labels: ['h', 'c', 'g']}, + apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, + gray: {channels: 1, labels: ['gray']} +}; + +// hide .channels and .labels properties +for (var model in convert) { + if (convert.hasOwnProperty(model)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } + + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } + + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } + + var channels = convert[model].channels; + var labels = convert[model].labels; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); + } +} + +convert.rgb.hsl = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var l; + + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + l = (min + max) / 2; + + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } + + return [h, s * 100, l * 100]; +}; + +convert.rgb.hsv = function (rgb) { + var rdif; + var gdif; + var bdif; + var h; + var s; + + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var v = Math.max(r, g, b); + var diff = v - Math.min(r, g, b); + var diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; + + if (diff === 0) { + h = s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); + + if (r === v) { + h = bdif - gdif; + } else if (g === v) { + h = (1 / 3) + rdif - bdif; + } else if (b === v) { + h = (2 / 3) + gdif - rdif; + } + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } + } + + return [ + h * 360, + s * 100, + v * 100 + ]; +}; + +convert.rgb.hwb = function (rgb) { + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var h = convert.rgb.hsl(rgb)[0]; + var w = 1 / 255 * Math.min(r, Math.min(g, b)); + + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +}; + +convert.rgb.cmyk = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var c; + var m; + var y; + var k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + + return [c * 100, m * 100, y * 100, k * 100]; +}; + +/** + * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + * */ +function comparativeDistance(x, y) { + return ( + Math.pow(x[0] - y[0], 2) + + Math.pow(x[1] - y[1], 2) + + Math.pow(x[2] - y[2], 2) + ); +} + +convert.rgb.keyword = function (rgb) { + var reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } + + var currentClosestDistance = Infinity; + var currentClosestKeyword; + + for (var keyword in cssKeywords) { + if (cssKeywords.hasOwnProperty(keyword)) { + var value = cssKeywords[keyword]; + + // Compute comparative distance + var distance = comparativeDistance(rgb, value); + + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + } + + return currentClosestKeyword; +}; + +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; + +convert.rgb.xyz = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y * 100, z * 100]; +}; + +convert.rgb.lab = function (rgb) { + var xyz = convert.rgb.xyz(rgb); + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.hsl.rgb = function (hsl) { + var h = hsl[0] / 360; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var t1; + var t2; + var t3; + var rgb; + var val; + + if (s === 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } + + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } + if (t3 > 1) { + t3--; + } + + if (6 * t3 < 1) { + val = t1 + (t2 - t1) * 6 * t3; + } else if (2 * t3 < 1) { + val = t2; + } else if (3 * t3 < 2) { + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + } else { + val = t1; + } + + rgb[i] = val * 255; + } + + return rgb; +}; + +convert.hsl.hsv = function (hsl) { + var h = hsl[0]; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var smin = s; + var lmin = Math.max(l, 0.01); + var sv; + var v; + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + v = (l + s) / 2; + sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + + return [h, sv * 100, v * 100]; +}; + +convert.hsv.rgb = function (hsv) { + var h = hsv[0] / 60; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var hi = Math.floor(h) % 6; + + var f = h - Math.floor(h); + var p = 255 * v * (1 - s); + var q = 255 * v * (1 - (s * f)); + var t = 255 * v * (1 - (s * (1 - f))); + v *= 255; + + switch (hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +}; + +convert.hsv.hsl = function (hsv) { + var h = hsv[0]; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var vmin = Math.max(v, 0.01); + var lmin; + var sl; + var l; + + l = (2 - s) * v; + lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; + + return [h, sl * 100, l * 100]; +}; + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + var h = hwb[0] / 360; + var wh = hwb[1] / 100; + var bl = hwb[2] / 100; + var ratio = wh + bl; + var i; + var v; + var f; + var n; + + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; + + if ((i & 0x01) !== 0) { + f = 1 - f; + } + + n = wh + f * (v - wh); // linear interpolation + + var r; + var g; + var b; + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + + return [r * 255, g * 255, b * 255]; +}; + +convert.cmyk.rgb = function (cmyk) { + var c = cmyk[0] / 100; + var m = cmyk[1] / 100; + var y = cmyk[2] / 100; + var k = cmyk[3] / 100; + var r; + var g; + var b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.rgb = function (xyz) { + var x = xyz[0] / 100; + var y = xyz[1] / 100; + var z = xyz[2] / 100; + var r; + var g; + var b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // assume sRGB + r = r > 0.0031308 + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r * 12.92; + + g = g > 0.0031308 + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g * 12.92; + + b = b > 0.0031308 + ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b * 12.92; + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.lab = function (xyz) { + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.lab.xyz = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var x; + var y; + var z; + + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; + + var y2 = Math.pow(y, 3); + var x2 = Math.pow(x, 3); + var z2 = Math.pow(z, 3); + y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; + x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; + z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + + x *= 95.047; + y *= 100; + z *= 108.883; + + return [x, y, z]; +}; + +convert.lab.lch = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var hr; + var h; + var c; + + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + + if (h < 0) { + h += 360; + } + + c = Math.sqrt(a * a + b * b); + + return [l, c, h]; +}; + +convert.lch.lab = function (lch) { + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; + + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + + return [l, a, b]; +}; + +convert.rgb.ansi16 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization + + value = Math.round(value / 50); + + if (value === 0) { + return 30; + } + + var ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + + if (value === 2) { + ansi += 60; + } + + return ansi; +}; + +convert.hsv.ansi16 = function (args) { + // optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; + +convert.rgb.ansi256 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + + // we use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } + + if (r > 248) { + return 231; + } + + return Math.round(((r - 8) / 247) * 24) + 232; + } + + var ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + + return ansi; +}; + +convert.ansi16.rgb = function (args) { + var color = args % 10; + + // handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + + color = color / 10.5 * 255; + + return [color, color, color]; + } + + var mult = (~~(args > 50) + 1) * 0.5; + var r = ((color & 1) * mult) * 255; + var g = (((color >> 1) & 1) * mult) * 255; + var b = (((color >> 2) & 1) * mult) * 255; + + return [r, g, b]; +}; + +convert.ansi256.rgb = function (args) { + // handle greyscale + if (args >= 232) { + var c = (args - 232) * 10 + 8; + return [c, c, c]; + } + + args -= 16; + + var rem; + var r = Math.floor(args / 36) / 5 * 255; + var g = Math.floor((rem = args % 36) / 6) / 5 * 255; + var b = (rem % 6) / 5 * 255; + + return [r, g, b]; +}; + +convert.rgb.hex = function (args) { + var integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.hex.rgb = function (args) { + var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; + } + + var colorString = match[0]; + + if (match[0].length === 3) { + colorString = colorString.split('').map(function (char) { + return char + char; + }).join(''); + } + + var integer = parseInt(colorString, 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; + + return [r, g, b]; +}; + +convert.rgb.hcg = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var max = Math.max(Math.max(r, g), b); + var min = Math.min(Math.min(r, g), b); + var chroma = (max - min); + var grayscale; + var hue; + + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } + + if (chroma <= 0) { + hue = 0; + } else + if (max === r) { + hue = ((g - b) / chroma) % 6; + } else + if (max === g) { + hue = 2 + (b - r) / chroma; + } else { + hue = 4 + (r - g) / chroma + 4; + } + + hue /= 6; + hue %= 1; + + return [hue * 360, chroma * 100, grayscale * 100]; +}; + +convert.hsl.hcg = function (hsl) { + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var c = 1; + var f = 0; + + if (l < 0.5) { + c = 2.0 * s * l; + } else { + c = 2.0 * s * (1.0 - l); + } + + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); + } + + return [hsl[0], c * 100, f * 100]; +}; + +convert.hsv.hcg = function (hsv) { + var s = hsv[1] / 100; + var v = hsv[2] / 100; + + var c = s * v; + var f = 0; + + if (c < 1.0) { + f = (v - c) / (1 - c); + } + + return [hsv[0], c * 100, f * 100]; +}; + +convert.hcg.rgb = function (hcg) { + var h = hcg[0] / 360; + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } + + var pure = [0, 0, 0]; + var hi = (h % 1) * 6; + var v = hi % 1; + var w = 1 - v; + var mg = 0; + + switch (Math.floor(hi)) { + case 0: + pure[0] = 1; pure[1] = v; pure[2] = 0; break; + case 1: + pure[0] = w; pure[1] = 1; pure[2] = 0; break; + case 2: + pure[0] = 0; pure[1] = 1; pure[2] = v; break; + case 3: + pure[0] = 0; pure[1] = w; pure[2] = 1; break; + case 4: + pure[0] = v; pure[1] = 0; pure[2] = 1; break; + default: + pure[0] = 1; pure[1] = 0; pure[2] = w; + } + + mg = (1.0 - c) * g; + + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; + +convert.hcg.hsv = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + var v = c + g * (1.0 - c); + var f = 0; + + if (v > 0.0) { + f = c / v; + } + + return [hcg[0], f * 100, v * 100]; +}; + +convert.hcg.hsl = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + var l = g * (1.0 - c) + 0.5 * c; + var s = 0; + + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else + if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } + + return [hcg[0], s * 100, l * 100]; +}; + +convert.hcg.hwb = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; +}; + +convert.hwb.hcg = function (hwb) { + var w = hwb[1] / 100; + var b = hwb[2] / 100; + var v = 1 - b; + var c = v - w; + var g = 0; + + if (c < 1) { + g = (v - c) / (1 - c); + } + + return [hwb[0], c * 100, g * 100]; +}; + +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; + +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; + +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; + +convert.gray.hsl = convert.gray.hsv = function (args) { + return [0, 0, args[0]]; +}; + +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; + +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; + +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; + +convert.gray.hex = function (gray) { + var val = Math.round(gray[0] / 100 * 255) & 0xFF; + var integer = (val << 16) + (val << 8) + val; + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.rgb.gray = function (rgb) { + var val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; diff --git a/WebServer/node_modules/color-convert/index.js b/WebServer/node_modules/color-convert/index.js new file mode 100644 index 000000000..e65b5d775 --- /dev/null +++ b/WebServer/node_modules/color-convert/index.js @@ -0,0 +1,78 @@ +var conversions = require('./conversions'); +var route = require('./route'); + +var convert = {}; + +var models = Object.keys(conversions); + +function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + return fn(args); + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } + + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } + + var result = fn(args); + + // we're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (var len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +models.forEach(function (fromModel) { + convert[fromModel] = {}; + + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); + + var routes = route(fromModel); + var routeModels = Object.keys(routes); + + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); + +module.exports = convert; diff --git a/WebServer/node_modules/color-convert/package.json b/WebServer/node_modules/color-convert/package.json new file mode 100644 index 000000000..cfe093e8d --- /dev/null +++ b/WebServer/node_modules/color-convert/package.json @@ -0,0 +1,84 @@ +{ + "_args": [ + [ + "color-convert@1.9.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "color-convert@1.9.3", + "_id": "color-convert@1.9.3", + "_inBundle": false, + "_integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "_location": "/color-convert", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "color-convert@1.9.3", + "name": "color-convert", + "escapedName": "color-convert", + "rawSpec": "1.9.3", + "saveSpec": null, + "fetchSpec": "1.9.3" + }, + "_requiredBy": [ + "/ansi-styles" + ], + "_resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "_spec": "1.9.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Heather Arthur", + "email": "fayearthur@gmail.com" + }, + "bugs": { + "url": "https://github.com/Qix-/color-convert/issues" + }, + "dependencies": { + "color-name": "1.1.3" + }, + "description": "Plain color conversion functions", + "devDependencies": { + "chalk": "1.1.1", + "xo": "0.11.2" + }, + "files": [ + "index.js", + "conversions.js", + "css-keywords.js", + "route.js" + ], + "homepage": "https://github.com/Qix-/color-convert#readme", + "keywords": [ + "color", + "colour", + "convert", + "converter", + "conversion", + "rgb", + "hsl", + "hsv", + "hwb", + "cmyk", + "ansi", + "ansi16" + ], + "license": "MIT", + "name": "color-convert", + "repository": { + "type": "git", + "url": "git+https://github.com/Qix-/color-convert.git" + }, + "scripts": { + "pretest": "xo", + "test": "node test/basic.js" + }, + "version": "1.9.3", + "xo": { + "rules": { + "default-case": 0, + "no-inline-comments": 0, + "operator-linebreak": 0 + } + } +} diff --git a/WebServer/node_modules/color-convert/route.js b/WebServer/node_modules/color-convert/route.js new file mode 100644 index 000000000..0a1fdea68 --- /dev/null +++ b/WebServer/node_modules/color-convert/route.js @@ -0,0 +1,97 @@ +var conversions = require('./conversions'); + +/* + this function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +function buildGraph() { + var graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + var models = Object.keys(conversions); + + for (var len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); + + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; + + var cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +module.exports = function (fromModel) { + var graph = deriveBFS(fromModel); + var conversion = {}; + + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; + + if (node.parent === null) { + // no possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + diff --git a/WebServer/node_modules/color-name/.eslintrc.json b/WebServer/node_modules/color-name/.eslintrc.json new file mode 100644 index 000000000..c50c25044 --- /dev/null +++ b/WebServer/node_modules/color-name/.eslintrc.json @@ -0,0 +1,43 @@ +{ + "env": { + "browser": true, + "node": true, + "commonjs": true, + "es6": true + }, + "extends": "eslint:recommended", + "rules": { + "strict": 2, + "indent": 0, + "linebreak-style": 0, + "quotes": 0, + "semi": 0, + "no-cond-assign": 1, + "no-constant-condition": 1, + "no-duplicate-case": 1, + "no-empty": 1, + "no-ex-assign": 1, + "no-extra-boolean-cast": 1, + "no-extra-semi": 1, + "no-fallthrough": 1, + "no-func-assign": 1, + "no-global-assign": 1, + "no-implicit-globals": 2, + "no-inner-declarations": ["error", "functions"], + "no-irregular-whitespace": 2, + "no-loop-func": 1, + "no-multi-str": 1, + "no-mixed-spaces-and-tabs": 1, + "no-proto": 1, + "no-sequences": 1, + "no-throw-literal": 1, + "no-unmodified-loop-condition": 1, + "no-useless-call": 1, + "no-void": 1, + "no-with": 2, + "wrap-iife": 1, + "no-redeclare": 1, + "no-unused-vars": ["error", { "vars": "all", "args": "none" }], + "no-sparse-arrays": 1 + } +} diff --git a/WebServer/node_modules/color-name/index.js b/WebServer/node_modules/color-name/index.js new file mode 100644 index 000000000..e42aa68a5 --- /dev/null +++ b/WebServer/node_modules/color-name/index.js @@ -0,0 +1,152 @@ +'use strict' + +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; diff --git a/WebServer/node_modules/color-name/package.json b/WebServer/node_modules/color-name/package.json new file mode 100644 index 000000000..1443fa7fd --- /dev/null +++ b/WebServer/node_modules/color-name/package.json @@ -0,0 +1,56 @@ +{ + "_args": [ + [ + "color-name@1.1.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "color-name@1.1.3", + "_id": "color-name@1.1.3", + "_inBundle": false, + "_integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "_location": "/color-name", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "color-name@1.1.3", + "name": "color-name", + "escapedName": "color-name", + "rawSpec": "1.1.3", + "saveSpec": null, + "fetchSpec": "1.1.3" + }, + "_requiredBy": [ + "/color-convert" + ], + "_resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "_spec": "1.1.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "DY", + "email": "dfcreative@gmail.com" + }, + "bugs": { + "url": "https://github.com/dfcreative/color-name/issues" + }, + "description": "A list of color names and its values", + "homepage": "https://github.com/dfcreative/color-name", + "keywords": [ + "color-name", + "color", + "color-keyword", + "keyword" + ], + "license": "MIT", + "main": "index.js", + "name": "color-name", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/dfcreative/color-name.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.1.3" +} diff --git a/WebServer/node_modules/color-name/test.js b/WebServer/node_modules/color-name/test.js new file mode 100644 index 000000000..7a0874621 --- /dev/null +++ b/WebServer/node_modules/color-name/test.js @@ -0,0 +1,7 @@ +'use strict' + +var names = require('./'); +var assert = require('assert'); + +assert.deepEqual(names.red, [255,0,0]); +assert.deepEqual(names.aliceblue, [240,248,255]); diff --git a/WebServer/node_modules/concat-map/example/map.js b/WebServer/node_modules/concat-map/example/map.js new file mode 100644 index 000000000..33656217b --- /dev/null +++ b/WebServer/node_modules/concat-map/example/map.js @@ -0,0 +1,6 @@ +var concatMap = require('../'); +var xs = [ 1, 2, 3, 4, 5, 6 ]; +var ys = concatMap(xs, function (x) { + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; +}); +console.dir(ys); diff --git a/WebServer/node_modules/concat-map/index.js b/WebServer/node_modules/concat-map/index.js new file mode 100644 index 000000000..b29a7812e --- /dev/null +++ b/WebServer/node_modules/concat-map/index.js @@ -0,0 +1,13 @@ +module.exports = function (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + var x = fn(xs[i], i); + if (isArray(x)) res.push.apply(res, x); + else res.push(x); + } + return res; +}; + +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; diff --git a/WebServer/node_modules/concat-map/package.json b/WebServer/node_modules/concat-map/package.json new file mode 100644 index 000000000..9222b2a11 --- /dev/null +++ b/WebServer/node_modules/concat-map/package.json @@ -0,0 +1,91 @@ +{ + "_args": [ + [ + "concat-map@0.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "concat-map@0.0.1", + "_id": "concat-map@0.0.1", + "_inBundle": false, + "_integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "_location": "/concat-map", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "concat-map@0.0.1", + "name": "concat-map", + "escapedName": "concat-map", + "rawSpec": "0.0.1", + "saveSpec": null, + "fetchSpec": "0.0.1" + }, + "_requiredBy": [ + "/brace-expansion" + ], + "_resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "_spec": "0.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bugs": { + "url": "https://github.com/substack/node-concat-map/issues" + }, + "description": "concatenative mapdashery", + "devDependencies": { + "tape": "~2.4.0" + }, + "directories": { + "example": "example", + "test": "test" + }, + "homepage": "https://github.com/substack/node-concat-map#readme", + "keywords": [ + "concat", + "concatMap", + "map", + "functional", + "higher-order" + ], + "license": "MIT", + "main": "index.js", + "name": "concat-map", + "repository": { + "type": "git", + "url": "git://github.com/substack/node-concat-map.git" + }, + "scripts": { + "test": "tape test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": { + "ie": [ + 6, + 7, + 8, + 9 + ], + "ff": [ + 3.5, + 10, + 15 + ], + "chrome": [ + 10, + 22 + ], + "safari": [ + 5.1 + ], + "opera": [ + 12 + ] + } + }, + "version": "0.0.1" +} diff --git a/WebServer/node_modules/concat-map/test/map.js b/WebServer/node_modules/concat-map/test/map.js new file mode 100644 index 000000000..fdbd7022f --- /dev/null +++ b/WebServer/node_modules/concat-map/test/map.js @@ -0,0 +1,39 @@ +var concatMap = require('../'); +var test = require('tape'); + +test('empty or not', function (t) { + var xs = [ 1, 2, 3, 4, 5, 6 ]; + var ixes = []; + var ys = concatMap(xs, function (x, ix) { + ixes.push(ix); + return x % 2 ? [ x - 0.1, x, x + 0.1 ] : []; + }); + t.same(ys, [ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]); + t.same(ixes, [ 0, 1, 2, 3, 4, 5 ]); + t.end(); +}); + +test('always something', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function (x) { + return x === 'b' ? [ 'B', 'B', 'B' ] : [ x ]; + }); + t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]); + t.end(); +}); + +test('scalars', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function (x) { + return x === 'b' ? [ 'B', 'B', 'B' ] : x; + }); + t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]); + t.end(); +}); + +test('undefs', function (t) { + var xs = [ 'a', 'b', 'c', 'd' ]; + var ys = concatMap(xs, function () {}); + t.same(ys, [ undefined, undefined, undefined, undefined ]); + t.end(); +}); diff --git a/WebServer/node_modules/cross-spawn/index.js b/WebServer/node_modules/cross-spawn/index.js new file mode 100644 index 000000000..5509742ca --- /dev/null +++ b/WebServer/node_modules/cross-spawn/index.js @@ -0,0 +1,39 @@ +'use strict'; + +const cp = require('child_process'); +const parse = require('./lib/parse'); +const enoent = require('./lib/enoent'); + +function spawn(command, args, options) { + // Parse the arguments + const parsed = parse(command, args, options); + + // Spawn the child process + const spawned = cp.spawn(parsed.command, parsed.args, parsed.options); + + // Hook into child process "exit" event to emit an error if the command + // does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 + enoent.hookChildProcess(spawned, parsed); + + return spawned; +} + +function spawnSync(command, args, options) { + // Parse the arguments + const parsed = parse(command, args, options); + + // Spawn the child process + const result = cp.spawnSync(parsed.command, parsed.args, parsed.options); + + // Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 + result.error = result.error || enoent.verifyENOENTSync(result.status, parsed); + + return result; +} + +module.exports = spawn; +module.exports.spawn = spawn; +module.exports.sync = spawnSync; + +module.exports._parse = parse; +module.exports._enoent = enoent; diff --git a/WebServer/node_modules/cross-spawn/lib/enoent.js b/WebServer/node_modules/cross-spawn/lib/enoent.js new file mode 100644 index 000000000..14df9b623 --- /dev/null +++ b/WebServer/node_modules/cross-spawn/lib/enoent.js @@ -0,0 +1,59 @@ +'use strict'; + +const isWin = process.platform === 'win32'; + +function notFoundError(original, syscall) { + return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), { + code: 'ENOENT', + errno: 'ENOENT', + syscall: `${syscall} ${original.command}`, + path: original.command, + spawnargs: original.args, + }); +} + +function hookChildProcess(cp, parsed) { + if (!isWin) { + return; + } + + const originalEmit = cp.emit; + + cp.emit = function (name, arg1) { + // If emitting "exit" event and exit code is 1, we need to check if + // the command exists and emit an "error" instead + // See https://github.com/IndigoUnited/node-cross-spawn/issues/16 + if (name === 'exit') { + const err = verifyENOENT(arg1, parsed, 'spawn'); + + if (err) { + return originalEmit.call(cp, 'error', err); + } + } + + return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params + }; +} + +function verifyENOENT(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, 'spawn'); + } + + return null; +} + +function verifyENOENTSync(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, 'spawnSync'); + } + + return null; +} + +module.exports = { + hookChildProcess, + verifyENOENT, + verifyENOENTSync, + notFoundError, +}; diff --git a/WebServer/node_modules/cross-spawn/lib/parse.js b/WebServer/node_modules/cross-spawn/lib/parse.js new file mode 100644 index 000000000..962827a94 --- /dev/null +++ b/WebServer/node_modules/cross-spawn/lib/parse.js @@ -0,0 +1,125 @@ +'use strict'; + +const path = require('path'); +const niceTry = require('nice-try'); +const resolveCommand = require('./util/resolveCommand'); +const escape = require('./util/escape'); +const readShebang = require('./util/readShebang'); +const semver = require('semver'); + +const isWin = process.platform === 'win32'; +const isExecutableRegExp = /\.(?:com|exe)$/i; +const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; + +// `options.shell` is supported in Node ^4.8.0, ^5.7.0 and >= 6.0.0 +const supportsShellOption = niceTry(() => semver.satisfies(process.version, '^4.8.0 || ^5.7.0 || >= 6.0.0', true)) || false; + +function detectShebang(parsed) { + parsed.file = resolveCommand(parsed); + + const shebang = parsed.file && readShebang(parsed.file); + + if (shebang) { + parsed.args.unshift(parsed.file); + parsed.command = shebang; + + return resolveCommand(parsed); + } + + return parsed.file; +} + +function parseNonShell(parsed) { + if (!isWin) { + return parsed; + } + + // Detect & add support for shebangs + const commandFile = detectShebang(parsed); + + // We don't need a shell if the command filename is an executable + const needsShell = !isExecutableRegExp.test(commandFile); + + // If a shell is required, use cmd.exe and take care of escaping everything correctly + // Note that `forceShell` is an hidden option used only in tests + if (parsed.options.forceShell || needsShell) { + // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/` + // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument + // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called, + // we need to double escape them + const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile); + + // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar) + // This is necessary otherwise it will always fail with ENOENT in those cases + parsed.command = path.normalize(parsed.command); + + // Escape command & arguments + parsed.command = escape.command(parsed.command); + parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars)); + + const shellCommand = [parsed.command].concat(parsed.args).join(' '); + + parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`]; + parsed.command = process.env.comspec || 'cmd.exe'; + parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped + } + + return parsed; +} + +function parseShell(parsed) { + // If node supports the shell option, there's no need to mimic its behavior + if (supportsShellOption) { + return parsed; + } + + // Mimic node shell option + // See https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335 + const shellCommand = [parsed.command].concat(parsed.args).join(' '); + + if (isWin) { + parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe'; + parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`]; + parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped + } else { + if (typeof parsed.options.shell === 'string') { + parsed.command = parsed.options.shell; + } else if (process.platform === 'android') { + parsed.command = '/system/bin/sh'; + } else { + parsed.command = '/bin/sh'; + } + + parsed.args = ['-c', shellCommand]; + } + + return parsed; +} + +function parse(command, args, options) { + // Normalize arguments, similar to nodejs + if (args && !Array.isArray(args)) { + options = args; + args = null; + } + + args = args ? args.slice(0) : []; // Clone array to avoid changing the original + options = Object.assign({}, options); // Clone object to avoid changing the original + + // Build our parsed object + const parsed = { + command, + args, + options, + file: undefined, + original: { + command, + args, + }, + }; + + // Delegate further parsing to shell or non-shell + return options.shell ? parseShell(parsed) : parseNonShell(parsed); +} + +module.exports = parse; diff --git a/WebServer/node_modules/cross-spawn/lib/util/escape.js b/WebServer/node_modules/cross-spawn/lib/util/escape.js new file mode 100644 index 000000000..b0bb84c3a --- /dev/null +++ b/WebServer/node_modules/cross-spawn/lib/util/escape.js @@ -0,0 +1,45 @@ +'use strict'; + +// See http://www.robvanderwoude.com/escapechars.php +const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g; + +function escapeCommand(arg) { + // Escape meta chars + arg = arg.replace(metaCharsRegExp, '^$1'); + + return arg; +} + +function escapeArgument(arg, doubleEscapeMetaChars) { + // Convert to string + arg = `${arg}`; + + // Algorithm below is based on https://qntm.org/cmd + + // Sequence of backslashes followed by a double quote: + // double up all the backslashes and escape the double quote + arg = arg.replace(/(\\*)"/g, '$1$1\\"'); + + // Sequence of backslashes followed by the end of the string + // (which will become a double quote later): + // double up all the backslashes + arg = arg.replace(/(\\*)$/, '$1$1'); + + // All other backslashes occur literally + + // Quote the whole thing: + arg = `"${arg}"`; + + // Escape meta chars + arg = arg.replace(metaCharsRegExp, '^$1'); + + // Double escape meta chars if necessary + if (doubleEscapeMetaChars) { + arg = arg.replace(metaCharsRegExp, '^$1'); + } + + return arg; +} + +module.exports.command = escapeCommand; +module.exports.argument = escapeArgument; diff --git a/WebServer/node_modules/cross-spawn/lib/util/readShebang.js b/WebServer/node_modules/cross-spawn/lib/util/readShebang.js new file mode 100644 index 000000000..bd4f1280c --- /dev/null +++ b/WebServer/node_modules/cross-spawn/lib/util/readShebang.js @@ -0,0 +1,32 @@ +'use strict'; + +const fs = require('fs'); +const shebangCommand = require('shebang-command'); + +function readShebang(command) { + // Read the first 150 bytes from the file + const size = 150; + let buffer; + + if (Buffer.alloc) { + // Node.js v4.5+ / v5.10+ + buffer = Buffer.alloc(size); + } else { + // Old Node.js API + buffer = new Buffer(size); + buffer.fill(0); // zero-fill + } + + let fd; + + try { + fd = fs.openSync(command, 'r'); + fs.readSync(fd, buffer, 0, size, 0); + fs.closeSync(fd); + } catch (e) { /* Empty */ } + + // Attempt to extract shebang (null is returned if not a shebang) + return shebangCommand(buffer.toString()); +} + +module.exports = readShebang; diff --git a/WebServer/node_modules/cross-spawn/lib/util/resolveCommand.js b/WebServer/node_modules/cross-spawn/lib/util/resolveCommand.js new file mode 100644 index 000000000..2fd5ad270 --- /dev/null +++ b/WebServer/node_modules/cross-spawn/lib/util/resolveCommand.js @@ -0,0 +1,47 @@ +'use strict'; + +const path = require('path'); +const which = require('which'); +const pathKey = require('path-key')(); + +function resolveCommandAttempt(parsed, withoutPathExt) { + const cwd = process.cwd(); + const hasCustomCwd = parsed.options.cwd != null; + + // If a custom `cwd` was specified, we need to change the process cwd + // because `which` will do stat calls but does not support a custom cwd + if (hasCustomCwd) { + try { + process.chdir(parsed.options.cwd); + } catch (err) { + /* Empty */ + } + } + + let resolved; + + try { + resolved = which.sync(parsed.command, { + path: (parsed.options.env || process.env)[pathKey], + pathExt: withoutPathExt ? path.delimiter : undefined, + }); + } catch (e) { + /* Empty */ + } finally { + process.chdir(cwd); + } + + // If we successfully resolved, ensure that an absolute path is returned + // Note that when a custom `cwd` was used, we need to resolve to an absolute path based on it + if (resolved) { + resolved = path.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved); + } + + return resolved; +} + +function resolveCommand(parsed) { + return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true); +} + +module.exports = resolveCommand; diff --git a/WebServer/node_modules/cross-spawn/package.json b/WebServer/node_modules/cross-spawn/package.json new file mode 100644 index 000000000..98ec5d35b --- /dev/null +++ b/WebServer/node_modules/cross-spawn/package.json @@ -0,0 +1,110 @@ +{ + "_args": [ + [ + "cross-spawn@6.0.5", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "cross-spawn@6.0.5", + "_id": "cross-spawn@6.0.5", + "_inBundle": false, + "_integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "_location": "/cross-spawn", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "cross-spawn@6.0.5", + "name": "cross-spawn", + "escapedName": "cross-spawn", + "rawSpec": "6.0.5", + "saveSpec": null, + "fetchSpec": "6.0.5" + }, + "_requiredBy": [ + "/execa" + ], + "_resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "_spec": "6.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "André Cruz", + "email": "andre@moxy.studio" + }, + "bugs": { + "url": "https://github.com/moxystudio/node-cross-spawn/issues" + }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "description": "Cross platform child_process#spawn and child_process#spawnSync", + "devDependencies": { + "@commitlint/cli": "^6.0.0", + "@commitlint/config-conventional": "^6.0.2", + "babel-core": "^6.26.0", + "babel-jest": "^22.1.0", + "babel-preset-moxy": "^2.2.1", + "eslint": "^4.3.0", + "eslint-config-moxy": "^5.0.0", + "husky": "^0.14.3", + "jest": "^22.0.0", + "lint-staged": "^7.0.0", + "mkdirp": "^0.5.1", + "regenerator-runtime": "^0.11.1", + "rimraf": "^2.6.2", + "standard-version": "^4.2.0" + }, + "engines": { + "node": ">=4.8" + }, + "files": [ + "lib" + ], + "homepage": "https://github.com/moxystudio/node-cross-spawn", + "keywords": [ + "spawn", + "spawnSync", + "windows", + "cross-platform", + "path-ext", + "shebang", + "cmd", + "execute" + ], + "license": "MIT", + "lint-staged": { + "*.js": [ + "eslint --fix", + "git add" + ] + }, + "main": "index.js", + "name": "cross-spawn", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/moxystudio/node-cross-spawn.git" + }, + "scripts": { + "commitmsg": "commitlint -e $GIT_PARAMS", + "lint": "eslint .", + "precommit": "lint-staged", + "prerelease": "npm t && npm run lint", + "release": "standard-version", + "test": "jest --env node --coverage" + }, + "standard-version": { + "scripts": { + "posttag": "git push --follow-tags origin master && npm publish" + } + }, + "version": "6.0.5" +} diff --git a/WebServer/node_modules/debug/dist/debug.js b/WebServer/node_modules/debug/dist/debug.js new file mode 100644 index 000000000..f271e01c8 --- /dev/null +++ b/WebServer/node_modules/debug/dist/debug.js @@ -0,0 +1,886 @@ +"use strict"; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +(function (f) { + if ((typeof exports === "undefined" ? "undefined" : _typeof(exports)) === "object" && typeof module !== "undefined") { + module.exports = f(); + } else if (typeof define === "function" && define.amd) { + define([], f); + } else { + var g; + + if (typeof window !== "undefined") { + g = window; + } else if (typeof global !== "undefined") { + g = global; + } else if (typeof self !== "undefined") { + g = self; + } else { + g = this; + } + + g.debug = f(); + } +})(function () { + var define, module, exports; + return function () { + function r(e, n, t) { + function o(i, f) { + if (!n[i]) { + if (!e[i]) { + var c = "function" == typeof require && require; + if (!f && c) return c(i, !0); + if (u) return u(i, !0); + var a = new Error("Cannot find module '" + i + "'"); + throw a.code = "MODULE_NOT_FOUND", a; + } + + var p = n[i] = { + exports: {} + }; + e[i][0].call(p.exports, function (r) { + var n = e[i][1][r]; + return o(n || r); + }, p, p.exports, r, e, n, t); + } + + return n[i].exports; + } + + for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) { + o(t[i]); + } + + return o; + } + + return r; + }()({ + 1: [function (require, module, exports) { + /** + * Helpers. + */ + var s = 1000; + var m = s * 60; + var h = m * 60; + var d = h * 24; + var w = d * 7; + var y = d * 365.25; + /** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + + module.exports = function (val, options) { + options = options || {}; + + var type = _typeof(val); + + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + + throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val)); + }; + /** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + + + function parse(str) { + str = String(str); + + if (str.length > 100) { + return; + } + + var match = /^((?:\d+)?\-?\d?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str); + + if (!match) { + return; + } + + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + + case 'weeks': + case 'week': + case 'w': + return n * w; + + case 'days': + case 'day': + case 'd': + return n * d; + + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + + default: + return undefined; + } + } + /** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + + function fmtShort(ms) { + var msAbs = Math.abs(ms); + + if (msAbs >= d) { + return Math.round(ms / d) + 'd'; + } + + if (msAbs >= h) { + return Math.round(ms / h) + 'h'; + } + + if (msAbs >= m) { + return Math.round(ms / m) + 'm'; + } + + if (msAbs >= s) { + return Math.round(ms / s) + 's'; + } + + return ms + 'ms'; + } + /** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + + + function fmtLong(ms) { + var msAbs = Math.abs(ms); + + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day'); + } + + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour'); + } + + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute'); + } + + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second'); + } + + return ms + ' ms'; + } + /** + * Pluralization helper. + */ + + + function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); + } + }, {}], + 2: [function (require, module, exports) { + // shim for using process in browser + var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + + function defaultClearTimeout() { + throw new Error('clearTimeout has not been defined'); + } + + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + })(); + + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } // if setTimeout wasn't available but was latter defined + + + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch (e) { + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch (e) { + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + } + + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } // if clearTimeout wasn't available but was latter defined + + + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e) { + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e) { + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + } + + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + + draining = false; + + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + + var timeout = runTimeout(cleanUpNextTick); + draining = true; + var len = queue.length; + + while (len) { + currentQueue = queue; + queue = []; + + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + + queueIndex = -1; + len = queue.length; + } + + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + + queue.push(new Item(fun, args)); + + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; // v8 likes predictible objects + + + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + process.prependListener = noop; + process.prependOnceListener = noop; + + process.listeners = function (name) { + return []; + }; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { + return '/'; + }; + + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + + process.umask = function () { + return 0; + }; + }, {}], + 3: [function (require, module, exports) { + /** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + */ + function setup(env) { + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable; + createDebug.enable = enable; + createDebug.enabled = enabled; + createDebug.humanize = require('ms'); + Object.keys(env).forEach(function (key) { + createDebug[key] = env[key]; + }); + /** + * Active `debug` instances. + */ + + createDebug.instances = []; + /** + * The currently active debug mode names, and names to skip. + */ + + createDebug.names = []; + createDebug.skips = []; + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + + createDebug.formatters = {}; + /** + * Selects a color for a debug namespace + * @param {String} namespace The namespace string for the for the debug instance to be colored + * @return {Number|String} An ANSI color code for the given namespace + * @api private + */ + + function selectColor(namespace) { + var hash = 0; + + for (var i = 0; i < namespace.length; i++) { + hash = (hash << 5) - hash + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; + } + + createDebug.selectColor = selectColor; + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + + function createDebug(namespace) { + var prevTime; + + function debug() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + // Disabled? + if (!debug.enabled) { + return; + } + + var self = debug; // Set `diff` timestamp + + var curr = Number(new Date()); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + args[0] = createDebug.coerce(args[0]); + + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O'); + } // Apply any `formatters` transformations + + + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return match; + } + + index++; + var formatter = createDebug.formatters[format]; + + if (typeof formatter === 'function') { + var val = args[index]; + match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format` + + args.splice(index, 1); + index--; + } + + return match; + }); // Apply env-specific formatting (colors, etc.) + + createDebug.formatArgs.call(self, args); + var logFn = self.log || createDebug.log; + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = createDebug.enabled(namespace); + debug.useColors = createDebug.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; + debug.extend = extend; // Debug.formatArgs = formatArgs; + // debug.rawLog = rawLog; + // env-specific initialization logic for debug instances + + if (typeof createDebug.init === 'function') { + createDebug.init(debug); + } + + createDebug.instances.push(debug); + return debug; + } + + function destroy() { + var index = createDebug.instances.indexOf(this); + + if (index !== -1) { + createDebug.instances.splice(index, 1); + return true; + } + + return false; + } + + function extend(namespace, delimiter) { + return createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); + } + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + + + function enable(namespaces) { + createDebug.save(namespaces); + createDebug.names = []; + createDebug.skips = []; + var i; + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; + + for (i = 0; i < len; i++) { + if (!split[i]) { + // ignore empty strings + continue; + } + + namespaces = split[i].replace(/\*/g, '.*?'); + + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')); + } + } + + for (i = 0; i < createDebug.instances.length; i++) { + var instance = createDebug.instances[i]; + instance.enabled = createDebug.enabled(instance.namespace); + } + } + /** + * Disable debug output. + * + * @api public + */ + + + function disable() { + createDebug.enable(''); + } + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + + + function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + + var i; + var len; + + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false; + } + } + + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true; + } + } + + return false; + } + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + + + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + + return val; + } + + createDebug.enable(createDebug.load()); + return createDebug; + } + + module.exports = setup; + }, { + "ms": 1 + }], + 4: [function (require, module, exports) { + (function (process) { + /* eslint-env browser */ + + /** + * This is the web browser implementation of `debug()`. + */ + exports.log = log; + exports.formatArgs = formatArgs; + exports.save = save; + exports.load = load; + exports.useColors = useColors; + exports.storage = localstorage(); + /** + * Colors. + */ + + exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33']; + /** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + // eslint-disable-next-line complexity + + function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { + return true; + } // Internet Explorer and Edge do not support colors. + + + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } // Is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + + + return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 + typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); + } + /** + * Colorize log arguments if enabled. + * + * @api public + */ + + + function formatArgs(args) { + args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff); + + if (!this.useColors) { + return; + } + + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); // The final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function (match) { + if (match === '%%') { + return; + } + + index++; + + if (match === '%c') { + // We only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + args.splice(lastC, 0, c); + } + /** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + + + function log() { + var _console; + + // This hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return (typeof console === "undefined" ? "undefined" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments); + } + /** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + + function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces); + } else { + exports.storage.removeItem('debug'); + } + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } + } + /** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + + + function load() { + var r; + + try { + r = exports.storage.getItem('debug'); + } catch (error) {} // Swallow + // XXX (@Qix-) should we be logging these? + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + + + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; + } + /** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + + + function localstorage() { + try { + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context + // The Browser also has localStorage in the global context. + return localStorage; + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } + } + + module.exports = require('./common')(exports); + var formatters = module.exports.formatters; + /** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + + formatters.j = function (v) { + try { + return JSON.stringify(v); + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message; + } + }; + }).call(this, require('_process')); + }, { + "./common": 3, + "_process": 2 + }] + }, {}, [4])(4); +}); + diff --git a/WebServer/node_modules/debug/node.js b/WebServer/node_modules/debug/node.js new file mode 100644 index 000000000..7fc36fe6d --- /dev/null +++ b/WebServer/node_modules/debug/node.js @@ -0,0 +1 @@ +module.exports = require('./src/node'); diff --git a/WebServer/node_modules/debug/package.json b/WebServer/node_modules/debug/package.json new file mode 100644 index 000000000..be8e3fb8d --- /dev/null +++ b/WebServer/node_modules/debug/package.json @@ -0,0 +1,93 @@ +{ + "_args": [ + [ + "debug@3.2.6", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "debug@3.2.6", + "_id": "debug@3.2.6", + "_inBundle": false, + "_integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "_location": "/debug", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "debug@3.2.6", + "name": "debug", + "escapedName": "debug", + "rawSpec": "3.2.6", + "saveSpec": null, + "fetchSpec": "3.2.6" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "_spec": "3.2.6", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "browser": "./src/browser.js", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + }, + { + "name": "Andrew Rhyne", + "email": "rhyneandrew@gmail.com" + } + ], + "dependencies": { + "ms": "^2.1.1" + }, + "description": "small debugging utility", + "devDependencies": { + "@babel/cli": "^7.0.0", + "@babel/core": "^7.0.0", + "@babel/preset-env": "^7.0.0", + "browserify": "14.4.0", + "chai": "^3.5.0", + "concurrently": "^3.1.0", + "coveralls": "^3.0.2", + "istanbul": "^0.4.5", + "karma": "^3.0.0", + "karma-chai": "^0.1.0", + "karma-mocha": "^1.3.0", + "karma-phantomjs-launcher": "^1.0.2", + "mocha": "^5.2.0", + "mocha-lcov-reporter": "^1.2.0", + "rimraf": "^2.5.4", + "xo": "^0.23.0" + }, + "files": [ + "src", + "node.js", + "dist/debug.js", + "LICENSE", + "README.md" + ], + "homepage": "https://github.com/visionmedia/debug#readme", + "keywords": [ + "debug", + "log", + "debugger" + ], + "license": "MIT", + "main": "./src/index.js", + "name": "debug", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "unpkg": "./dist/debug.js", + "version": "3.2.6" +} diff --git a/WebServer/node_modules/debug/src/browser.js b/WebServer/node_modules/debug/src/browser.js new file mode 100644 index 000000000..c924b0ac4 --- /dev/null +++ b/WebServer/node_modules/debug/src/browser.js @@ -0,0 +1,180 @@ +"use strict"; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +/* eslint-env browser */ + +/** + * This is the web browser implementation of `debug()`. + */ +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = localstorage(); +/** + * Colors. + */ + +exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33']; +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ +// eslint-disable-next-line complexity + +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { + return true; + } // Internet Explorer and Edge do not support colors. + + + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } // Is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + + + return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 + typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); +} +/** + * Colorize log arguments if enabled. + * + * @api public + */ + + +function formatArgs(args) { + args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff); + + if (!this.useColors) { + return; + } + + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); // The final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function (match) { + if (match === '%%') { + return; + } + + index++; + + if (match === '%c') { + // We only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + args.splice(lastC, 0, c); +} +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + + +function log() { + var _console; + + // This hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return (typeof console === "undefined" ? "undefined" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments); +} +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + +function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces); + } else { + exports.storage.removeItem('debug'); + } + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } +} +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + + +function load() { + var r; + + try { + r = exports.storage.getItem('debug'); + } catch (error) {} // Swallow + // XXX (@Qix-) should we be logging these? + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + + + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; +} +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + + +function localstorage() { + try { + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context + // The Browser also has localStorage in the global context. + return localStorage; + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } +} + +module.exports = require('./common')(exports); +var formatters = module.exports.formatters; +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +formatters.j = function (v) { + try { + return JSON.stringify(v); + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message; + } +}; + diff --git a/WebServer/node_modules/debug/src/common.js b/WebServer/node_modules/debug/src/common.js new file mode 100644 index 000000000..e0de3fb53 --- /dev/null +++ b/WebServer/node_modules/debug/src/common.js @@ -0,0 +1,249 @@ +"use strict"; + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + */ +function setup(env) { + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable; + createDebug.enable = enable; + createDebug.enabled = enabled; + createDebug.humanize = require('ms'); + Object.keys(env).forEach(function (key) { + createDebug[key] = env[key]; + }); + /** + * Active `debug` instances. + */ + + createDebug.instances = []; + /** + * The currently active debug mode names, and names to skip. + */ + + createDebug.names = []; + createDebug.skips = []; + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + + createDebug.formatters = {}; + /** + * Selects a color for a debug namespace + * @param {String} namespace The namespace string for the for the debug instance to be colored + * @return {Number|String} An ANSI color code for the given namespace + * @api private + */ + + function selectColor(namespace) { + var hash = 0; + + for (var i = 0; i < namespace.length; i++) { + hash = (hash << 5) - hash + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; + } + + createDebug.selectColor = selectColor; + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + + function createDebug(namespace) { + var prevTime; + + function debug() { + // Disabled? + if (!debug.enabled) { + return; + } + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var self = debug; // Set `diff` timestamp + + var curr = Number(new Date()); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + args[0] = createDebug.coerce(args[0]); + + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O'); + } // Apply any `formatters` transformations + + + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return match; + } + + index++; + var formatter = createDebug.formatters[format]; + + if (typeof formatter === 'function') { + var val = args[index]; + match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format` + + args.splice(index, 1); + index--; + } + + return match; + }); // Apply env-specific formatting (colors, etc.) + + createDebug.formatArgs.call(self, args); + var logFn = self.log || createDebug.log; + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = createDebug.enabled(namespace); + debug.useColors = createDebug.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; + debug.extend = extend; // Debug.formatArgs = formatArgs; + // debug.rawLog = rawLog; + // env-specific initialization logic for debug instances + + if (typeof createDebug.init === 'function') { + createDebug.init(debug); + } + + createDebug.instances.push(debug); + return debug; + } + + function destroy() { + var index = createDebug.instances.indexOf(this); + + if (index !== -1) { + createDebug.instances.splice(index, 1); + return true; + } + + return false; + } + + function extend(namespace, delimiter) { + return createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); + } + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + + + function enable(namespaces) { + createDebug.save(namespaces); + createDebug.names = []; + createDebug.skips = []; + var i; + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; + + for (i = 0; i < len; i++) { + if (!split[i]) { + // ignore empty strings + continue; + } + + namespaces = split[i].replace(/\*/g, '.*?'); + + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')); + } + } + + for (i = 0; i < createDebug.instances.length; i++) { + var instance = createDebug.instances[i]; + instance.enabled = createDebug.enabled(instance.namespace); + } + } + /** + * Disable debug output. + * + * @api public + */ + + + function disable() { + createDebug.enable(''); + } + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + + + function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + + var i; + var len; + + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false; + } + } + + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true; + } + } + + return false; + } + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + + + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + + return val; + } + + createDebug.enable(createDebug.load()); + return createDebug; +} + +module.exports = setup; + diff --git a/WebServer/node_modules/debug/src/index.js b/WebServer/node_modules/debug/src/index.js new file mode 100644 index 000000000..021731593 --- /dev/null +++ b/WebServer/node_modules/debug/src/index.js @@ -0,0 +1,12 @@ +"use strict"; + +/** + * Detect Electron renderer / nwjs process, which is node, but we should + * treat as a browser. + */ +if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) { + module.exports = require('./browser.js'); +} else { + module.exports = require('./node.js'); +} + diff --git a/WebServer/node_modules/debug/src/node.js b/WebServer/node_modules/debug/src/node.js new file mode 100644 index 000000000..dbbb5f108 --- /dev/null +++ b/WebServer/node_modules/debug/src/node.js @@ -0,0 +1,174 @@ +"use strict"; + +/** + * Module dependencies. + */ +var tty = require('tty'); + +var util = require('util'); +/** + * This is the Node.js implementation of `debug()`. + */ + + +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +try { + // Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json) + // eslint-disable-next-line import/no-extraneous-dependencies + var supportsColor = require('supports-color'); + + if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) { + exports.colors = [20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 214, 215, 220, 221]; + } +} catch (error) {} // Swallow - we only care if `supports-color` is available; it doesn't have to be. + +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ + + +exports.inspectOpts = Object.keys(process.env).filter(function (key) { + return /^debug_/i.test(key); +}).reduce(function (obj, key) { + // Camel-case + var prop = key.substring(6).toLowerCase().replace(/_([a-z])/g, function (_, k) { + return k.toUpperCase(); + }); // Coerce string value into JS value + + var val = process.env[key]; + + if (/^(yes|on|true|enabled)$/i.test(val)) { + val = true; + } else if (/^(no|off|false|disabled)$/i.test(val)) { + val = false; + } else if (val === 'null') { + val = null; + } else { + val = Number(val); + } + + obj[prop] = val; + return obj; +}, {}); +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + return 'colors' in exports.inspectOpts ? Boolean(exports.inspectOpts.colors) : tty.isatty(process.stderr.fd); +} +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + + +function formatArgs(args) { + var name = this.namespace, + useColors = this.useColors; + + if (useColors) { + var c = this.color; + var colorCode = "\x1B[3" + (c < 8 ? c : '8;5;' + c); + var prefix = " ".concat(colorCode, ";1m").concat(name, " \x1B[0m"); + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + "\x1B[0m"); + } else { + args[0] = getDate() + name + ' ' + args[0]; + } +} + +function getDate() { + if (exports.inspectOpts.hideDate) { + return ''; + } + + return new Date().toISOString() + ' '; +} +/** + * Invokes `util.format()` with the specified arguments and writes to stderr. + */ + + +function log() { + return process.stderr.write(util.format.apply(util, arguments) + '\n'); +} +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + +function save(namespaces) { + if (namespaces) { + process.env.DEBUG = namespaces; + } else { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } +} +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + + +function load() { + return process.env.DEBUG; +} +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ + + +function init(debug) { + debug.inspectOpts = {}; + var keys = Object.keys(exports.inspectOpts); + + for (var i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } +} + +module.exports = require('./common')(exports); +var formatters = module.exports.formatters; +/** + * Map %o to `util.inspect()`, all on a single line. + */ + +formatters.o = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts).replace(/\s*\n\s*/g, ' '); +}; +/** + * Map %O to `util.inspect()`, allowing multiple lines if needed. + */ + + +formatters.O = function (v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; + diff --git a/WebServer/node_modules/decamelize/index.js b/WebServer/node_modules/decamelize/index.js new file mode 100644 index 000000000..8d5bab7e4 --- /dev/null +++ b/WebServer/node_modules/decamelize/index.js @@ -0,0 +1,13 @@ +'use strict'; +module.exports = function (str, sep) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + sep = typeof sep === 'undefined' ? '_' : sep; + + return str + .replace(/([a-z\d])([A-Z])/g, '$1' + sep + '$2') + .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + sep + '$2') + .toLowerCase(); +}; diff --git a/WebServer/node_modules/decamelize/package.json b/WebServer/node_modules/decamelize/package.json new file mode 100644 index 000000000..8a55df5d7 --- /dev/null +++ b/WebServer/node_modules/decamelize/package.json @@ -0,0 +1,75 @@ +{ + "_args": [ + [ + "decamelize@1.2.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "decamelize@1.2.0", + "_id": "decamelize@1.2.0", + "_inBundle": false, + "_integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "_location": "/decamelize", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "decamelize@1.2.0", + "name": "decamelize", + "escapedName": "decamelize", + "rawSpec": "1.2.0", + "saveSpec": null, + "fetchSpec": "1.2.0" + }, + "_requiredBy": [ + "/yargs-parser", + "/yargs-unparser/yargs", + "/yargs-unparser/yargs-parser" + ], + "_resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "_spec": "1.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/decamelize/issues" + }, + "description": "Convert a camelized string into a lowercased one with a custom separator: unicornRainbow → unicorn_rainbow", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/decamelize#readme", + "keywords": [ + "decamelize", + "decamelcase", + "camelcase", + "lowercase", + "case", + "dash", + "hyphen", + "string", + "str", + "text", + "convert" + ], + "license": "MIT", + "name": "decamelize", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/decamelize.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.2.0" +} diff --git a/WebServer/node_modules/deep-eql/deep-eql.js b/WebServer/node_modules/deep-eql/deep-eql.js new file mode 100644 index 000000000..86f0b41ae --- /dev/null +++ b/WebServer/node_modules/deep-eql/deep-eql.js @@ -0,0 +1,833 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.deepEqual = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + * MIT Licensed + */ + +var type = require('type-detect'); +function FakeMap() { + this._key = 'chai/deep-eql__' + Math.random() + Date.now(); +} + +FakeMap.prototype = { + get: function getMap(key) { + return key[this._key]; + }, + set: function setMap(key, value) { + if (Object.isExtensible(key)) { + Object.defineProperty(key, this._key, { + value: value, + configurable: true, + }); + } + }, +}; + +var MemoizeMap = typeof WeakMap === 'function' ? WeakMap : FakeMap; +/*! + * Check to see if the MemoizeMap has recorded a result of the two operands + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @returns {Boolean|null} result +*/ +function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return null; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + var result = leftHandMap.get(rightHandOperand); + if (typeof result === 'boolean') { + return result; + } + } + return null; +} + +/*! + * Set the result of the equality into the MemoizeMap + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @param {Boolean} result +*/ +function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + leftHandMap.set(rightHandOperand, result); + } else { + leftHandMap = new MemoizeMap(); + leftHandMap.set(rightHandOperand, result); + memoizeMap.set(leftHandOperand, leftHandMap); + } +} + +/*! + * Primary Export + */ + +module.exports = deepEqual; +module.exports.MemoizeMap = MemoizeMap; + +/** + * Assert deeply nested sameValue equality between two objects of any type. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match + */ +function deepEqual(leftHandOperand, rightHandOperand, options) { + // If we have a comparator, we can't assume anything; so bail to its check first. + if (options && options.comparator) { + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); + } + + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + return simpleResult; + } + + // Deeper comparisons are pushed through to a larger function + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); +} + +/** + * Many comparisons can be canceled out early via simple equality or primitive checks. + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @return {Boolean|null} equal match + */ +function simpleEqual(leftHandOperand, rightHandOperand) { + // Equal references (except for Numbers) can be returned early + if (leftHandOperand === rightHandOperand) { + // Handle +-0 cases + return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; + } + + // handle NaN cases + if ( + leftHandOperand !== leftHandOperand && // eslint-disable-line no-self-compare + rightHandOperand !== rightHandOperand // eslint-disable-line no-self-compare + ) { + return true; + } + + // Anything that is not an 'object', i.e. symbols, functions, booleans, numbers, + // strings, and undefined, can be compared by reference. + if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + // Easy out b/c it would have passed the first equality check + return false; + } + return null; +} + +/*! + * The main logic of the `deepEqual` function. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match +*/ +function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { + options = options || {}; + options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); + var comparator = options && options.comparator; + + // Check if a memoized result exists. + var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); + if (memoizeResultLeft !== null) { + return memoizeResultLeft; + } + var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); + if (memoizeResultRight !== null) { + return memoizeResultRight; + } + + // If a comparator is present, use it. + if (comparator) { + var comparatorResult = comparator(leftHandOperand, rightHandOperand); + // Comparators may return null, in which case we want to go back to default behavior. + if (comparatorResult === false || comparatorResult === true) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); + return comparatorResult; + } + // To allow comparators to override *any* behavior, we ran them first. Since it didn't decide + // what to do, we need to make sure to return the basic tests first before we move on. + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + // Don't memoize this, it takes longer to set/retrieve than to just compare. + return simpleResult; + } + } + + var leftHandType = type(leftHandOperand); + if (leftHandType !== type(rightHandOperand)) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); + return false; + } + + // Temporarily set the operands in the memoize object to prevent blowing the stack + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); + + var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); + return result; +} + +function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { + switch (leftHandType) { + case 'String': + case 'Number': + case 'Boolean': + case 'Date': + // If these types are their instance types (e.g. `new Number`) then re-deepEqual against their values + return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); + case 'Promise': + case 'Symbol': + case 'function': + case 'WeakMap': + case 'WeakSet': + case 'Error': + return leftHandOperand === rightHandOperand; + case 'Arguments': + case 'Int8Array': + case 'Uint8Array': + case 'Uint8ClampedArray': + case 'Int16Array': + case 'Uint16Array': + case 'Int32Array': + case 'Uint32Array': + case 'Float32Array': + case 'Float64Array': + case 'Array': + return iterableEqual(leftHandOperand, rightHandOperand, options); + case 'RegExp': + return regexpEqual(leftHandOperand, rightHandOperand); + case 'Generator': + return generatorEqual(leftHandOperand, rightHandOperand, options); + case 'DataView': + return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); + case 'ArrayBuffer': + return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); + case 'Set': + return entriesEqual(leftHandOperand, rightHandOperand, options); + case 'Map': + return entriesEqual(leftHandOperand, rightHandOperand, options); + default: + return objectEqual(leftHandOperand, rightHandOperand, options); + } +} + +/*! + * Compare two Regular Expressions for equality. + * + * @param {RegExp} leftHandOperand + * @param {RegExp} rightHandOperand + * @return {Boolean} result + */ + +function regexpEqual(leftHandOperand, rightHandOperand) { + return leftHandOperand.toString() === rightHandOperand.toString(); +} + +/*! + * Compare two Sets/Maps for equality. Faster than other equality functions. + * + * @param {Set} leftHandOperand + * @param {Set} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function entriesEqual(leftHandOperand, rightHandOperand, options) { + // IE11 doesn't support Set#entries or Set#@@iterator, so we need manually populate using Set#forEach + if (leftHandOperand.size !== rightHandOperand.size) { + return false; + } + if (leftHandOperand.size === 0) { + return true; + } + var leftHandItems = []; + var rightHandItems = []; + leftHandOperand.forEach(function gatherEntries(key, value) { + leftHandItems.push([ key, value ]); + }); + rightHandOperand.forEach(function gatherEntries(key, value) { + rightHandItems.push([ key, value ]); + }); + return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); +} + +/*! + * Simple equality for flat iterable objects such as Arrays, TypedArrays or Node.js buffers. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function iterableEqual(leftHandOperand, rightHandOperand, options) { + var length = leftHandOperand.length; + if (length !== rightHandOperand.length) { + return false; + } + if (length === 0) { + return true; + } + var index = -1; + while (++index < length) { + if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { + return false; + } + } + return true; +} + +/*! + * Simple equality for generator objects such as those returned by generator functions. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function generatorEqual(leftHandOperand, rightHandOperand, options) { + return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); +} + +/*! + * Determine if the given object has an @@iterator function. + * + * @param {Object} target + * @return {Boolean} `true` if the object has an @@iterator function. + */ +function hasIteratorFunction(target) { + return typeof Symbol !== 'undefined' && + typeof target === 'object' && + typeof Symbol.iterator !== 'undefined' && + typeof target[Symbol.iterator] === 'function'; +} + +/*! + * Gets all iterator entries from the given Object. If the Object has no @@iterator function, returns an empty array. + * This will consume the iterator - which could have side effects depending on the @@iterator implementation. + * + * @param {Object} target + * @returns {Array} an array of entries from the @@iterator function + */ +function getIteratorEntries(target) { + if (hasIteratorFunction(target)) { + try { + return getGeneratorEntries(target[Symbol.iterator]()); + } catch (iteratorError) { + return []; + } + } + return []; +} + +/*! + * Gets all entries from a Generator. This will consume the generator - which could have side effects. + * + * @param {Generator} target + * @returns {Array} an array of entries from the Generator. + */ +function getGeneratorEntries(generator) { + var generatorResult = generator.next(); + var accumulator = [ generatorResult.value ]; + while (generatorResult.done === false) { + generatorResult = generator.next(); + accumulator.push(generatorResult.value); + } + return accumulator; +} + +/*! + * Gets all own and inherited enumerable keys from a target. + * + * @param {Object} target + * @returns {Array} an array of own and inherited enumerable keys from the target. + */ +function getEnumerableKeys(target) { + var keys = []; + for (var key in target) { + keys.push(key); + } + return keys; +} + +/*! + * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of + * each key. If any value of the given key is not equal, the function will return false (early). + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Array} keys An array of keys to compare the values of leftHandOperand and rightHandOperand against + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ +function keysEqual(leftHandOperand, rightHandOperand, keys, options) { + var length = keys.length; + if (length === 0) { + return true; + } + for (var i = 0; i < length; i += 1) { + if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { + return false; + } + } + return true; +} + +/*! + * Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual` + * for each enumerable key in the object. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function objectEqual(leftHandOperand, rightHandOperand, options) { + var leftHandKeys = getEnumerableKeys(leftHandOperand); + var rightHandKeys = getEnumerableKeys(rightHandOperand); + if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { + leftHandKeys.sort(); + rightHandKeys.sort(); + if (iterableEqual(leftHandKeys, rightHandKeys) === false) { + return false; + } + return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); + } + + var leftHandEntries = getIteratorEntries(leftHandOperand); + var rightHandEntries = getIteratorEntries(rightHandOperand); + if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { + leftHandEntries.sort(); + rightHandEntries.sort(); + return iterableEqual(leftHandEntries, rightHandEntries, options); + } + + if (leftHandKeys.length === 0 && + leftHandEntries.length === 0 && + rightHandKeys.length === 0 && + rightHandEntries.length === 0) { + return true; + } + + return false; +} + +/*! + * Returns true if the argument is a primitive. + * + * This intentionally returns true for all objects that can be compared by reference, + * including functions and symbols. + * + * @param {Mixed} value + * @return {Boolean} result + */ +function isPrimitive(value) { + return value === null || typeof value !== 'object'; +} + +},{"type-detect":2}],2:[function(require,module,exports){ +(function (global){ +'use strict'; + +/* ! + * type-detect + * Copyright(c) 2013 jake luer + * MIT Licensed + */ +var promiseExists = typeof Promise === 'function'; +var globalObject = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : self; // eslint-disable-line +var isDom = 'location' in globalObject && 'document' in globalObject; +var symbolExists = typeof Symbol !== 'undefined'; +var mapExists = typeof Map !== 'undefined'; +var setExists = typeof Set !== 'undefined'; +var weakMapExists = typeof WeakMap !== 'undefined'; +var weakSetExists = typeof WeakSet !== 'undefined'; +var dataViewExists = typeof DataView !== 'undefined'; +var symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; +var symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; +var setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; +var mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; +var setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries()); +var mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries()); +var arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +var arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); +var stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function'; +var stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); +var toStringLeftSliceLength = 8; +var toStringRightSliceLength = -1; +/** + * ### typeOf (obj) + * + * Uses `Object.prototype.toString` to determine the type of an object, + * normalising behaviour across engine versions & well optimised. + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ +module.exports = function typeDetect(obj) { + /* ! Speed optimisation + * Pre: + * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) + * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) + * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) + * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) + * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) + * Post: + * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) + * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) + * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) + * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) + * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) + */ + var typeofObj = typeof obj; + if (typeofObj !== 'object') { + return typeofObj; + } + + /* ! Speed optimisation + * Pre: + * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) + * Post: + * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) + */ + if (obj === null) { + return 'null'; + } + + /* ! Spec Conformance + * Test: `Object.prototype.toString.call(window)`` + * - Node === "[object global]" + * - Chrome === "[object global]" + * - Firefox === "[object Window]" + * - PhantomJS === "[object Window]" + * - Safari === "[object Window]" + * - IE 11 === "[object Window]" + * - IE Edge === "[object Window]" + * Test: `Object.prototype.toString.call(this)`` + * - Chrome Worker === "[object global]" + * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" + * - Safari Worker === "[object DedicatedWorkerGlobalScope]" + * - IE 11 Worker === "[object WorkerGlobalScope]" + * - IE Edge Worker === "[object WorkerGlobalScope]" + */ + if (obj === globalObject) { + return 'global'; + } + + /* ! Speed optimisation + * Pre: + * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) + * Post: + * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) + */ + if ( + Array.isArray(obj) && + (symbolToStringTagExists === false || !(Symbol.toStringTag in obj)) + ) { + return 'Array'; + } + + if (isDom) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/browsers.html#location) + * WhatWG HTML$7.7.3 - The `Location` interface + * Test: `Object.prototype.toString.call(window.location)`` + * - IE <=11 === "[object Object]" + * - IE Edge <=13 === "[object Object]" + */ + if (obj === globalObject.location) { + return 'Location'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#document) + * WhatWG HTML$3.1.1 - The `Document` object + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * WhatWG HTML states: + * > For historical reasons, Window objects must also have a + * > writable, configurable, non-enumerable property named + * > HTMLDocument whose value is the Document interface object. + * Test: `Object.prototype.toString.call(document)`` + * - Chrome === "[object HTMLDocument]" + * - Firefox === "[object HTMLDocument]" + * - Safari === "[object HTMLDocument]" + * - IE <=10 === "[object Document]" + * - IE 11 === "[object HTMLDocument]" + * - IE Edge <=13 === "[object HTMLDocument]" + */ + if (obj === globalObject.document) { + return 'Document'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray + * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` + * - IE <=10 === "[object MSMimeTypesCollection]" + */ + if (obj === (globalObject.navigator || {}).mimeTypes) { + return 'MimeTypeArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray + * Test: `Object.prototype.toString.call(navigator.plugins)`` + * - IE <=10 === "[object MSPluginsCollection]" + */ + if (obj === (globalObject.navigator || {}).plugins) { + return 'PluginArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` + * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` + * - IE <=10 === "[object HTMLBlockElement]" + */ + if (obj instanceof HTMLElement && obj.tagName === 'BLOCKQUOTE') { + return 'HTMLQuoteElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltabledatacellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('td')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj instanceof HTMLElement && obj.tagName === 'TD') { + return 'HTMLTableDataCellElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltableheadercellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('th')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj instanceof HTMLElement && obj.tagName === 'TH') { + return 'HTMLTableHeaderCellElement'; + } + } + + /* ! Speed optimisation + * Pre: + * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) + * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) + * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) + * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) + * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) + * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) + * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) + * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) + * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) + * Post: + * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) + * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) + * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) + * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) + * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) + * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) + * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) + * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) + * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) + */ + var stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); + if (typeof stringTag === 'string') { + return stringTag; + } + + var objPrototype = Object.getPrototypeOf(obj); + /* ! Speed optimisation + * Pre: + * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) + * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) + * Post: + * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) + * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) + */ + if (objPrototype === RegExp.prototype) { + return 'RegExp'; + } + + /* ! Speed optimisation + * Pre: + * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) + * Post: + * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) + */ + if (objPrototype === Date.prototype) { + return 'Date'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) + * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": + * Test: `Object.prototype.toString.call(Promise.resolve())`` + * - Chrome <=47 === "[object Object]" + * - Edge <=20 === "[object Object]" + * - Firefox 29-Latest === "[object Promise]" + * - Safari 7.1-Latest === "[object Promise]" + */ + if (promiseExists && objPrototype === Promise.prototype) { + return 'Promise'; + } + + /* ! Speed optimisation + * Pre: + * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) + * Post: + * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) + */ + if (setExists && objPrototype === Set.prototype) { + return 'Set'; + } + + /* ! Speed optimisation + * Pre: + * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) + * Post: + * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) + */ + if (mapExists && objPrototype === Map.prototype) { + return 'Map'; + } + + /* ! Speed optimisation + * Pre: + * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) + * Post: + * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) + */ + if (weakSetExists && objPrototype === WeakSet.prototype) { + return 'WeakSet'; + } + + /* ! Speed optimisation + * Pre: + * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) + * Post: + * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) + */ + if (weakMapExists && objPrototype === WeakMap.prototype) { + return 'WeakMap'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) + * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": + * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` + * - Edge <=13 === "[object Object]" + */ + if (dataViewExists && objPrototype === DataView.prototype) { + return 'DataView'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) + * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": + * Test: `Object.prototype.toString.call(new Map().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (mapExists && objPrototype === mapIteratorPrototype) { + return 'Map Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) + * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": + * Test: `Object.prototype.toString.call(new Set().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (setExists && objPrototype === setIteratorPrototype) { + return 'Set Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) + * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": + * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { + return 'Array Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) + * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": + * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (stringIteratorExists && objPrototype === stringIteratorPrototype) { + return 'String Iterator'; + } + + /* ! Speed optimisation + * Pre: + * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) + * Post: + * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) + */ + if (objPrototype === null) { + return 'Object'; + } + + return Object + .prototype + .toString + .call(obj) + .slice(toStringLeftSliceLength, toStringRightSliceLength); +}; + +module.exports.typeDetect = module.exports; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/WebServer/node_modules/deep-eql/index.js b/WebServer/node_modules/deep-eql/index.js new file mode 100644 index 000000000..b1b529512 --- /dev/null +++ b/WebServer/node_modules/deep-eql/index.js @@ -0,0 +1,455 @@ +'use strict'; +/* globals Symbol: false, Uint8Array: false, WeakMap: false */ +/*! + * deep-eql + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +var type = require('type-detect'); +function FakeMap() { + this._key = 'chai/deep-eql__' + Math.random() + Date.now(); +} + +FakeMap.prototype = { + get: function getMap(key) { + return key[this._key]; + }, + set: function setMap(key, value) { + if (Object.isExtensible(key)) { + Object.defineProperty(key, this._key, { + value: value, + configurable: true, + }); + } + }, +}; + +var MemoizeMap = typeof WeakMap === 'function' ? WeakMap : FakeMap; +/*! + * Check to see if the MemoizeMap has recorded a result of the two operands + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @returns {Boolean|null} result +*/ +function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return null; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + var result = leftHandMap.get(rightHandOperand); + if (typeof result === 'boolean') { + return result; + } + } + return null; +} + +/*! + * Set the result of the equality into the MemoizeMap + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @param {Boolean} result +*/ +function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + leftHandMap.set(rightHandOperand, result); + } else { + leftHandMap = new MemoizeMap(); + leftHandMap.set(rightHandOperand, result); + memoizeMap.set(leftHandOperand, leftHandMap); + } +} + +/*! + * Primary Export + */ + +module.exports = deepEqual; +module.exports.MemoizeMap = MemoizeMap; + +/** + * Assert deeply nested sameValue equality between two objects of any type. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match + */ +function deepEqual(leftHandOperand, rightHandOperand, options) { + // If we have a comparator, we can't assume anything; so bail to its check first. + if (options && options.comparator) { + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); + } + + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + return simpleResult; + } + + // Deeper comparisons are pushed through to a larger function + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); +} + +/** + * Many comparisons can be canceled out early via simple equality or primitive checks. + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @return {Boolean|null} equal match + */ +function simpleEqual(leftHandOperand, rightHandOperand) { + // Equal references (except for Numbers) can be returned early + if (leftHandOperand === rightHandOperand) { + // Handle +-0 cases + return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; + } + + // handle NaN cases + if ( + leftHandOperand !== leftHandOperand && // eslint-disable-line no-self-compare + rightHandOperand !== rightHandOperand // eslint-disable-line no-self-compare + ) { + return true; + } + + // Anything that is not an 'object', i.e. symbols, functions, booleans, numbers, + // strings, and undefined, can be compared by reference. + if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + // Easy out b/c it would have passed the first equality check + return false; + } + return null; +} + +/*! + * The main logic of the `deepEqual` function. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match +*/ +function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { + options = options || {}; + options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); + var comparator = options && options.comparator; + + // Check if a memoized result exists. + var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); + if (memoizeResultLeft !== null) { + return memoizeResultLeft; + } + var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); + if (memoizeResultRight !== null) { + return memoizeResultRight; + } + + // If a comparator is present, use it. + if (comparator) { + var comparatorResult = comparator(leftHandOperand, rightHandOperand); + // Comparators may return null, in which case we want to go back to default behavior. + if (comparatorResult === false || comparatorResult === true) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); + return comparatorResult; + } + // To allow comparators to override *any* behavior, we ran them first. Since it didn't decide + // what to do, we need to make sure to return the basic tests first before we move on. + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + // Don't memoize this, it takes longer to set/retrieve than to just compare. + return simpleResult; + } + } + + var leftHandType = type(leftHandOperand); + if (leftHandType !== type(rightHandOperand)) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); + return false; + } + + // Temporarily set the operands in the memoize object to prevent blowing the stack + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); + + var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); + return result; +} + +function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { + switch (leftHandType) { + case 'String': + case 'Number': + case 'Boolean': + case 'Date': + // If these types are their instance types (e.g. `new Number`) then re-deepEqual against their values + return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); + case 'Promise': + case 'Symbol': + case 'function': + case 'WeakMap': + case 'WeakSet': + case 'Error': + return leftHandOperand === rightHandOperand; + case 'Arguments': + case 'Int8Array': + case 'Uint8Array': + case 'Uint8ClampedArray': + case 'Int16Array': + case 'Uint16Array': + case 'Int32Array': + case 'Uint32Array': + case 'Float32Array': + case 'Float64Array': + case 'Array': + return iterableEqual(leftHandOperand, rightHandOperand, options); + case 'RegExp': + return regexpEqual(leftHandOperand, rightHandOperand); + case 'Generator': + return generatorEqual(leftHandOperand, rightHandOperand, options); + case 'DataView': + return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); + case 'ArrayBuffer': + return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); + case 'Set': + return entriesEqual(leftHandOperand, rightHandOperand, options); + case 'Map': + return entriesEqual(leftHandOperand, rightHandOperand, options); + default: + return objectEqual(leftHandOperand, rightHandOperand, options); + } +} + +/*! + * Compare two Regular Expressions for equality. + * + * @param {RegExp} leftHandOperand + * @param {RegExp} rightHandOperand + * @return {Boolean} result + */ + +function regexpEqual(leftHandOperand, rightHandOperand) { + return leftHandOperand.toString() === rightHandOperand.toString(); +} + +/*! + * Compare two Sets/Maps for equality. Faster than other equality functions. + * + * @param {Set} leftHandOperand + * @param {Set} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function entriesEqual(leftHandOperand, rightHandOperand, options) { + // IE11 doesn't support Set#entries or Set#@@iterator, so we need manually populate using Set#forEach + if (leftHandOperand.size !== rightHandOperand.size) { + return false; + } + if (leftHandOperand.size === 0) { + return true; + } + var leftHandItems = []; + var rightHandItems = []; + leftHandOperand.forEach(function gatherEntries(key, value) { + leftHandItems.push([ key, value ]); + }); + rightHandOperand.forEach(function gatherEntries(key, value) { + rightHandItems.push([ key, value ]); + }); + return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); +} + +/*! + * Simple equality for flat iterable objects such as Arrays, TypedArrays or Node.js buffers. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function iterableEqual(leftHandOperand, rightHandOperand, options) { + var length = leftHandOperand.length; + if (length !== rightHandOperand.length) { + return false; + } + if (length === 0) { + return true; + } + var index = -1; + while (++index < length) { + if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { + return false; + } + } + return true; +} + +/*! + * Simple equality for generator objects such as those returned by generator functions. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function generatorEqual(leftHandOperand, rightHandOperand, options) { + return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); +} + +/*! + * Determine if the given object has an @@iterator function. + * + * @param {Object} target + * @return {Boolean} `true` if the object has an @@iterator function. + */ +function hasIteratorFunction(target) { + return typeof Symbol !== 'undefined' && + typeof target === 'object' && + typeof Symbol.iterator !== 'undefined' && + typeof target[Symbol.iterator] === 'function'; +} + +/*! + * Gets all iterator entries from the given Object. If the Object has no @@iterator function, returns an empty array. + * This will consume the iterator - which could have side effects depending on the @@iterator implementation. + * + * @param {Object} target + * @returns {Array} an array of entries from the @@iterator function + */ +function getIteratorEntries(target) { + if (hasIteratorFunction(target)) { + try { + return getGeneratorEntries(target[Symbol.iterator]()); + } catch (iteratorError) { + return []; + } + } + return []; +} + +/*! + * Gets all entries from a Generator. This will consume the generator - which could have side effects. + * + * @param {Generator} target + * @returns {Array} an array of entries from the Generator. + */ +function getGeneratorEntries(generator) { + var generatorResult = generator.next(); + var accumulator = [ generatorResult.value ]; + while (generatorResult.done === false) { + generatorResult = generator.next(); + accumulator.push(generatorResult.value); + } + return accumulator; +} + +/*! + * Gets all own and inherited enumerable keys from a target. + * + * @param {Object} target + * @returns {Array} an array of own and inherited enumerable keys from the target. + */ +function getEnumerableKeys(target) { + var keys = []; + for (var key in target) { + keys.push(key); + } + return keys; +} + +/*! + * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of + * each key. If any value of the given key is not equal, the function will return false (early). + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Array} keys An array of keys to compare the values of leftHandOperand and rightHandOperand against + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ +function keysEqual(leftHandOperand, rightHandOperand, keys, options) { + var length = keys.length; + if (length === 0) { + return true; + } + for (var i = 0; i < length; i += 1) { + if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { + return false; + } + } + return true; +} + +/*! + * Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual` + * for each enumerable key in the object. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function objectEqual(leftHandOperand, rightHandOperand, options) { + var leftHandKeys = getEnumerableKeys(leftHandOperand); + var rightHandKeys = getEnumerableKeys(rightHandOperand); + if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { + leftHandKeys.sort(); + rightHandKeys.sort(); + if (iterableEqual(leftHandKeys, rightHandKeys) === false) { + return false; + } + return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); + } + + var leftHandEntries = getIteratorEntries(leftHandOperand); + var rightHandEntries = getIteratorEntries(rightHandOperand); + if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { + leftHandEntries.sort(); + rightHandEntries.sort(); + return iterableEqual(leftHandEntries, rightHandEntries, options); + } + + if (leftHandKeys.length === 0 && + leftHandEntries.length === 0 && + rightHandKeys.length === 0 && + rightHandEntries.length === 0) { + return true; + } + + return false; +} + +/*! + * Returns true if the argument is a primitive. + * + * This intentionally returns true for all objects that can be compared by reference, + * including functions and symbols. + * + * @param {Mixed} value + * @return {Boolean} result + */ +function isPrimitive(value) { + return value === null || typeof value !== 'object'; +} diff --git a/WebServer/node_modules/deep-eql/package.json b/WebServer/node_modules/deep-eql/package.json new file mode 100644 index 000000000..f4274934e --- /dev/null +++ b/WebServer/node_modules/deep-eql/package.json @@ -0,0 +1,134 @@ +{ + "_args": [ + [ + "deep-eql@3.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "deep-eql@3.0.1", + "_id": "deep-eql@3.0.1", + "_inBundle": false, + "_integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "_location": "/deep-eql", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "deep-eql@3.0.1", + "name": "deep-eql", + "escapedName": "deep-eql", + "rawSpec": "3.0.1", + "saveSpec": null, + "fetchSpec": "3.0.1" + }, + "_requiredBy": [ + "/chai" + ], + "_resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "_spec": "3.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jake Luer", + "email": "jake@alogicalparadox.com" + }, + "bugs": { + "url": "https://github.com/chaijs/deep-eql/issues" + }, + "config": { + "ghooks": { + "commit-msg": "validate-commit-msg" + } + }, + "contributors": [ + { + "name": "Keith Cirkel", + "url": "https://github.com/keithamus" + }, + { + "name": "dougluce", + "url": "https://github.com/dougluce" + }, + { + "name": "Lorenz Leutgeb", + "url": "https://github.com/flowlo" + } + ], + "dependencies": { + "type-detect": "^4.0.0" + }, + "description": "Improved deep equality testing for Node.js and the browser.", + "devDependencies": { + "benchmark": "^2.1.0", + "browserify": "^13.0.0", + "browserify-istanbul": "^1.0.0", + "component": "*", + "coveralls": "2.11.8", + "eslint": "^2.4.0", + "eslint-config-strict": "^8.5.0", + "eslint-plugin-filenames": "^0.2.0", + "ghooks": "^1.0.1", + "istanbul": "^0.4.2", + "karma": "^0.13.22", + "karma-browserify": "^5.0.2", + "karma-coverage": "^0.5.5", + "karma-mocha": "^0.2.2", + "karma-phantomjs-launcher": "^1.0.0", + "karma-sauce-launcher": "^0.3.1", + "kewlr": "^0.3.1", + "lcov-result-merger": "^1.0.2", + "lodash.isequal": "^4.4.0", + "mocha": "^3.1.2", + "phantomjs-prebuilt": "^2.1.5", + "semantic-release": "^4.3.5", + "simple-assert": "^1.0.0", + "travis-after-all": "^1.4.4", + "validate-commit-msg": "^2.3.1", + "watchify": "^3.7.0" + }, + "engines": { + "node": ">=0.12" + }, + "eslintConfig": { + "extends": [ + "strict/es5" + ], + "rules": { + "complexity": 0, + "spaced-comment": 0, + "no-underscore-dangle": 0, + "no-use-before-define": 0 + } + }, + "files": [ + "index.js", + "deep-eql.js" + ], + "homepage": "https://github.com/chaijs/deep-eql#readme", + "keywords": [ + "chai util", + "deep equal", + "object equal", + "testing" + ], + "license": "MIT", + "main": "./index", + "name": "deep-eql", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/chaijs/deep-eql.git" + }, + "scripts": { + "bench": "node bench", + "build": "browserify $npm_package_main --standalone deepEqual -o deep-eql.js", + "lint": "eslint --ignore-path .gitignore .", + "prepublish": "npm run build", + "pretest": "npm run lint", + "semantic-release": "semantic-release pre && npm publish && semantic-release post", + "test": "npm run test:node && npm run test:browser", + "test:browser": "karma start --singleRun=true", + "test:node": "istanbul cover _mocha", + "upload-coverage": "lcov-result-merger 'coverage/**/lcov.info' | coveralls; exit 0", + "watch": "karma start --auto-watch --singleRun=false" + }, + "version": "3.0.1" +} diff --git a/WebServer/node_modules/define-properties/.jscs.json b/WebServer/node_modules/define-properties/.jscs.json new file mode 100644 index 000000000..6f2d7f9ff --- /dev/null +++ b/WebServer/node_modules/define-properties/.jscs.json @@ -0,0 +1,175 @@ +{ + "es3": true, + + "additionalRules": [], + + "requireSemicolons": true, + + "disallowMultipleSpaces": true, + + "disallowIdentifierNames": [], + + "requireCurlyBraces": { + "allExcept": [], + "keywords": ["if", "else", "for", "while", "do", "try", "catch"] + }, + + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"], + + "disallowSpaceAfterKeywords": [], + + "disallowSpaceBeforeComma": true, + "disallowSpaceAfterComma": false, + "disallowSpaceBeforeSemicolon": true, + + "disallowNodeTypes": [ + "DebuggerStatement", + "LabeledStatement", + "SwitchCase", + "SwitchStatement", + "WithStatement" + ], + + "requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] }, + + "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true }, + "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true }, + "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true }, + "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true }, + "disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true }, + + "requireSpaceBetweenArguments": true, + + "disallowSpacesInsideParentheses": true, + + "disallowSpacesInsideArrayBrackets": true, + + "disallowQuotedKeysInObjects": { "allExcept": ["reserved"] }, + + "disallowSpaceAfterObjectKeys": true, + + "requireCommaBeforeLineBreak": true, + + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "requireSpaceAfterPrefixUnaryOperators": [], + + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "requireSpaceBeforePostfixUnaryOperators": [], + + "disallowSpaceBeforeBinaryOperators": [], + "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + + "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + "disallowSpaceAfterBinaryOperators": [], + + "disallowImplicitTypeConversion": ["binary", "string"], + + "disallowKeywords": ["with", "eval"], + + "requireKeywordsOnNewLine": [], + "disallowKeywordsOnNewLine": ["else"], + + "requireLineFeedAtFileEnd": true, + + "disallowTrailingWhitespace": true, + + "disallowTrailingComma": true, + + "excludeFiles": ["node_modules/**", "vendor/**"], + + "disallowMultipleLineStrings": true, + + "requireDotNotation": { "allExcept": ["keywords"] }, + + "requireParenthesesAroundIIFE": true, + + "validateLineBreaks": "LF", + + "validateQuoteMarks": { + "escape": true, + "mark": "'" + }, + + "disallowOperatorBeforeLineBreak": [], + + "requireSpaceBeforeKeywords": [ + "do", + "for", + "if", + "else", + "switch", + "case", + "try", + "catch", + "finally", + "while", + "with", + "return" + ], + + "validateAlignedFunctionParameters": { + "lineBreakAfterOpeningBraces": true, + "lineBreakBeforeClosingBraces": true + }, + + "requirePaddingNewLinesBeforeExport": true, + + "validateNewlineAfterArrayElements": { + "maximum": 3 + }, + + "requirePaddingNewLinesAfterUseStrict": true, + + "disallowArrowFunctions": true, + + "disallowMultiLineTernary": true, + + "validateOrderInObjectKeys": "asc-insensitive", + + "disallowIdenticalDestructuringNames": true, + + "disallowNestedTernaries": { "maxLevel": 1 }, + + "requireSpaceAfterComma": { "allExcept": ["trailing"] }, + "requireAlignedMultilineParams": false, + + "requireSpacesInGenerator": { + "afterStar": true + }, + + "disallowSpacesInGenerator": { + "beforeStar": true + }, + + "disallowVar": false, + + "requireArrayDestructuring": false, + + "requireEnhancedObjectLiterals": false, + + "requireObjectDestructuring": false, + + "requireEarlyReturn": false, + + "requireCapitalizedConstructorsNew": { + "allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array"] + }, + + "requireImportAlphabetized": false, + + "requireSpaceBeforeObjectValues": true, + "requireSpaceBeforeDestructuredValues": true, + + "disallowSpacesInsideTemplateStringPlaceholders": true, + + "disallowArrayDestructuringReturn": false, + + "requireNewlineBeforeSingleStatementsInIf": false, + + "disallowUnusedVariables": true, + + "requireSpacesInsideImportedObjectBraces": true, + + "requireUseStrict": true +} + diff --git a/WebServer/node_modules/define-properties/index.js b/WebServer/node_modules/define-properties/index.js new file mode 100644 index 000000000..cb3ae1c7c --- /dev/null +++ b/WebServer/node_modules/define-properties/index.js @@ -0,0 +1,58 @@ +'use strict'; + +var keys = require('object-keys'); +var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol'; + +var toStr = Object.prototype.toString; +var concat = Array.prototype.concat; +var origDefineProperty = Object.defineProperty; + +var isFunction = function (fn) { + return typeof fn === 'function' && toStr.call(fn) === '[object Function]'; +}; + +var arePropertyDescriptorsSupported = function () { + var obj = {}; + try { + origDefineProperty(obj, 'x', { enumerable: false, value: obj }); + // eslint-disable-next-line no-unused-vars, no-restricted-syntax + for (var _ in obj) { // jscs:ignore disallowUnusedVariables + return false; + } + return obj.x === obj; + } catch (e) { /* this is IE 8. */ + return false; + } +}; +var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported(); + +var defineProperty = function (object, name, value, predicate) { + if (name in object && (!isFunction(predicate) || !predicate())) { + return; + } + if (supportsDescriptors) { + origDefineProperty(object, name, { + configurable: true, + enumerable: false, + value: value, + writable: true + }); + } else { + object[name] = value; + } +}; + +var defineProperties = function (object, map) { + var predicates = arguments.length > 2 ? arguments[2] : {}; + var props = keys(map); + if (hasSymbols) { + props = concat.call(props, Object.getOwnPropertySymbols(map)); + } + for (var i = 0; i < props.length; i += 1) { + defineProperty(object, props[i], map[props[i]], predicates[props[i]]); + } +}; + +defineProperties.supportsDescriptors = !!supportsDescriptors; + +module.exports = defineProperties; diff --git a/WebServer/node_modules/define-properties/package.json b/WebServer/node_modules/define-properties/package.json new file mode 100644 index 000000000..ee8ab3869 --- /dev/null +++ b/WebServer/node_modules/define-properties/package.json @@ -0,0 +1,100 @@ +{ + "_args": [ + [ + "define-properties@1.1.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "define-properties@1.1.3", + "_id": "define-properties@1.1.3", + "_inBundle": false, + "_integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "_location": "/define-properties", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "define-properties@1.1.3", + "name": "define-properties", + "escapedName": "define-properties", + "rawSpec": "1.1.3", + "saveSpec": null, + "fetchSpec": "1.1.3" + }, + "_requiredBy": [ + "/object.assign", + "/object.getownpropertydescriptors" + ], + "_resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "_spec": "1.1.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jordan Harband" + }, + "bugs": { + "url": "https://github.com/ljharb/define-properties/issues" + }, + "dependencies": { + "object-keys": "^1.0.12" + }, + "description": "Define multiple non-enumerable properties at once. Uses `Object.defineProperty` when available; falls back to standard assignment in older engines.", + "devDependencies": { + "@ljharb/eslint-config": "^13.0.0", + "covert": "^1.1.0", + "eslint": "^5.3.0", + "jscs": "^3.0.7", + "nsp": "^3.2.1", + "tape": "^4.9.0" + }, + "engines": { + "node": ">= 0.4" + }, + "homepage": "https://github.com/ljharb/define-properties#readme", + "keywords": [ + "Object.defineProperty", + "Object.defineProperties", + "object", + "property descriptor", + "descriptor", + "define", + "ES5" + ], + "license": "MIT", + "main": "index.js", + "name": "define-properties", + "repository": { + "type": "git", + "url": "git://github.com/ljharb/define-properties.git" + }, + "scripts": { + "coverage": "covert test/*.js", + "coverage-quiet": "covert test/*.js --quiet", + "eslint": "eslint test/*.js *.js", + "jscs": "jscs test/*.js *.js", + "lint": "npm run --silent jscs && npm run --silent eslint", + "posttest": "npm run --silent security", + "pretest": "npm run --silent lint", + "security": "nsp check", + "test": "npm run --silent tests-only", + "tests-only": "node test/index.js" + }, + "testling": { + "files": "test/index.js", + "browsers": [ + "iexplore/6.0..latest", + "firefox/3.0..6.0", + "firefox/15.0..latest", + "firefox/nightly", + "chrome/4.0..10.0", + "chrome/20.0..latest", + "chrome/canary", + "opera/10.0..latest", + "opera/next", + "safari/4.0..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2" + ] + }, + "version": "1.1.3" +} diff --git a/WebServer/node_modules/define-properties/test/index.js b/WebServer/node_modules/define-properties/test/index.js new file mode 100644 index 000000000..3387f6bc7 --- /dev/null +++ b/WebServer/node_modules/define-properties/test/index.js @@ -0,0 +1,125 @@ +'use strict'; + +var define = require('../'); +var test = require('tape'); +var keys = require('object-keys'); + +var arePropertyDescriptorsSupported = function () { + var obj = { a: 1 }; + try { + Object.defineProperty(obj, 'x', { value: obj }); + return obj.x === obj; + } catch (e) { /* this is IE 8. */ + return false; + } +}; +var descriptorsSupported = !!Object.defineProperty && arePropertyDescriptorsSupported(); + +var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol'; + +test('defineProperties', function (dt) { + dt.test('with descriptor support', { skip: !descriptorsSupported }, function (t) { + var getDescriptor = function (value) { + return { + configurable: true, + enumerable: false, + value: value, + writable: true + }; + }; + + var obj = { + a: 1, + b: 2, + c: 3 + }; + t.deepEqual(keys(obj), ['a', 'b', 'c'], 'all literal-set keys start enumerable'); + define(obj, { + b: 3, + c: 4, + d: 5 + }); + t.deepEqual(obj, { + a: 1, + b: 2, + c: 3 + }, 'existing properties were not overridden'); + t.deepEqual(Object.getOwnPropertyDescriptor(obj, 'd'), getDescriptor(5), 'new property "d" was added and is not enumerable'); + t.deepEqual(['a', 'b', 'c'], keys(obj), 'new keys are not enumerable'); + + define(obj, { + a: 2, + b: 3, + c: 4 + }, { + a: function () { return true; }, + b: function () { return false; } + }); + t.deepEqual(obj, { + b: 2, + c: 3 + }, 'properties only overriden when predicate exists and returns true'); + t.deepEqual(Object.getOwnPropertyDescriptor(obj, 'd'), getDescriptor(5), 'existing property "d" remained and is not enumerable'); + t.deepEqual(Object.getOwnPropertyDescriptor(obj, 'a'), getDescriptor(2), 'existing property "a" was overridden and is not enumerable'); + t.deepEqual(['b', 'c'], keys(obj), 'overridden keys are not enumerable'); + + t.end(); + }); + + dt.test('without descriptor support', { skip: descriptorsSupported }, function (t) { + var obj = { + a: 1, + b: 2, + c: 3 + }; + define(obj, { + b: 3, + c: 4, + d: 5 + }); + t.deepEqual(obj, { + a: 1, + b: 2, + c: 3, + d: 5 + }, 'existing properties were not overridden, new properties were added'); + + define(obj, { + a: 2, + b: 3, + c: 4 + }, { + a: function () { return true; }, + b: function () { return false; } + }); + t.deepEqual(obj, { + a: 2, + b: 2, + c: 3, + d: 5 + }, 'properties only overriden when predicate exists and returns true'); + + t.end(); + }); + + dt.end(); +}); + +test('symbols', { skip: !hasSymbols }, function (t) { + var sym = Symbol('foo'); + var obj = {}; + var aValue = {}; + var bValue = {}; + var properties = { a: aValue }; + properties[sym] = bValue; + + define(obj, properties); + + t.deepEqual(Object.keys(obj), [], 'object has no enumerable keys'); + t.deepEqual(Object.getOwnPropertyNames(obj), ['a'], 'object has non-enumerable "a" key'); + t.deepEqual(Object.getOwnPropertySymbols(obj), [sym], 'object has non-enumerable symbol key'); + t.equal(obj.a, aValue, 'string keyed value is defined'); + t.equal(obj[sym], bValue, 'symbol keyed value is defined'); + + t.end(); +}); diff --git a/WebServer/node_modules/diff/dist/diff.js b/WebServer/node_modules/diff/dist/diff.js new file mode 100644 index 000000000..0b824f123 --- /dev/null +++ b/WebServer/node_modules/diff/dist/diff.js @@ -0,0 +1,1843 @@ +/*! + + diff v3.5.0 + +Software License Agreement (BSD License) + +Copyright (c) 2009-2015, Kevin Decker + +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of Kevin Decker nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +@license +*/ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["JsDiff"] = factory(); + else + root["JsDiff"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.canonicalize = exports.convertChangesToXML = exports.convertChangesToDMP = exports.merge = exports.parsePatch = exports.applyPatches = exports.applyPatch = exports.createPatch = exports.createTwoFilesPatch = exports.structuredPatch = exports.diffArrays = exports.diffJson = exports.diffCss = exports.diffSentences = exports.diffTrimmedLines = exports.diffLines = exports.diffWordsWithSpace = exports.diffWords = exports.diffChars = exports.Diff = undefined; + + /*istanbul ignore end*/var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_character = __webpack_require__(2) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_word = __webpack_require__(3) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_sentence = __webpack_require__(6) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_css = __webpack_require__(7) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_json = __webpack_require__(8) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_array = __webpack_require__(9) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_apply = __webpack_require__(10) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_merge = __webpack_require__(13) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_create = __webpack_require__(14) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_dmp = __webpack_require__(16) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_xml = __webpack_require__(17) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /* See LICENSE file for terms of use */ + + /* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ + exports. /*istanbul ignore end*/Diff = _base2['default']; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffChars = _character.diffChars; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWords = _word.diffWords; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = _word.diffWordsWithSpace; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffLines = _line.diffLines; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = _line.diffTrimmedLines; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffSentences = _sentence.diffSentences; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffCss = _css.diffCss; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffJson = _json.diffJson; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffArrays = _array.diffArrays; + /*istanbul ignore start*/exports. /*istanbul ignore end*/structuredPatch = _create.structuredPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = _create.createTwoFilesPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = _create.createPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatch = _apply.applyPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = _apply.applyPatches; + /*istanbul ignore start*/exports. /*istanbul ignore end*/parsePatch = _parse.parsePatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/merge = _merge.merge; + /*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToDMP = _dmp.convertChangesToDMP; + /*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToXML = _xml.convertChangesToXML; + /*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = _json.canonicalize; + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6WyJEaWZmIiwiZGlmZkNoYXJzIiwiZGlmZldvcmRzIiwiZGlmZldvcmRzV2l0aFNwYWNlIiwiZGlmZkxpbmVzIiwiZGlmZlRyaW1tZWRMaW5lcyIsImRpZmZTZW50ZW5jZXMiLCJkaWZmQ3NzIiwiZGlmZkpzb24iLCJkaWZmQXJyYXlzIiwic3RydWN0dXJlZFBhdGNoIiwiY3JlYXRlVHdvRmlsZXNQYXRjaCIsImNyZWF0ZVBhdGNoIiwiYXBwbHlQYXRjaCIsImFwcGx5UGF0Y2hlcyIsInBhcnNlUGF0Y2giLCJtZXJnZSIsImNvbnZlcnRDaGFuZ2VzVG9ETVAiLCJjb252ZXJ0Q2hhbmdlc1RvWE1MIiwiY2Fub25pY2FsaXplIl0sIm1hcHBpbmdzIjoiOzs7Ozt1QkFnQkE7Ozs7dUJBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBRUE7O0FBQ0E7O0FBRUE7O0FBRUE7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBRUE7O0FBQ0E7Ozs7QUFqQ0E7O0FBRUE7Ozs7Ozs7Ozs7Ozs7O2dDQWtDRUEsSTt5REFFQUMsUzt5REFDQUMsUzt5REFDQUMsa0I7eURBQ0FDLFM7eURBQ0FDLGdCO3lEQUNBQyxhO3lEQUVBQyxPO3lEQUNBQyxRO3lEQUVBQyxVO3lEQUVBQyxlO3lEQUNBQyxtQjt5REFDQUMsVzt5REFDQUMsVTt5REFDQUMsWTt5REFDQUMsVTt5REFDQUMsSzt5REFDQUMsbUI7eURBQ0FDLG1CO3lEQUNBQyxZIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLyogU2VlIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMgb2YgdXNlICovXG5cbi8qXG4gKiBUZXh0IGRpZmYgaW1wbGVtZW50YXRpb24uXG4gKlxuICogVGhpcyBsaWJyYXJ5IHN1cHBvcnRzIHRoZSBmb2xsb3dpbmcgQVBJUzpcbiAqIEpzRGlmZi5kaWZmQ2hhcnM6IENoYXJhY3RlciBieSBjaGFyYWN0ZXIgZGlmZlxuICogSnNEaWZmLmRpZmZXb3JkczogV29yZCAoYXMgZGVmaW5lZCBieSBcXGIgcmVnZXgpIGRpZmYgd2hpY2ggaWdub3JlcyB3aGl0ZXNwYWNlXG4gKiBKc0RpZmYuZGlmZkxpbmVzOiBMaW5lIGJhc2VkIGRpZmZcbiAqXG4gKiBKc0RpZmYuZGlmZkNzczogRGlmZiB0YXJnZXRlZCBhdCBDU1MgY29udGVudFxuICpcbiAqIFRoZXNlIG1ldGhvZHMgYXJlIGJhc2VkIG9uIHRoZSBpbXBsZW1lbnRhdGlvbiBwcm9wb3NlZCBpblxuICogXCJBbiBPKE5EKSBEaWZmZXJlbmNlIEFsZ29yaXRobSBhbmQgaXRzIFZhcmlhdGlvbnNcIiAoTXllcnMsIDE5ODYpLlxuICogaHR0cDovL2NpdGVzZWVyeC5pc3QucHN1LmVkdS92aWV3ZG9jL3N1bW1hcnk/ZG9pPTEwLjEuMS40LjY5MjdcbiAqL1xuaW1wb3J0IERpZmYgZnJvbSAnLi9kaWZmL2Jhc2UnO1xuaW1wb3J0IHtkaWZmQ2hhcnN9IGZyb20gJy4vZGlmZi9jaGFyYWN0ZXInO1xuaW1wb3J0IHtkaWZmV29yZHMsIGRpZmZXb3Jkc1dpdGhTcGFjZX0gZnJvbSAnLi9kaWZmL3dvcmQnO1xuaW1wb3J0IHtkaWZmTGluZXMsIGRpZmZUcmltbWVkTGluZXN9IGZyb20gJy4vZGlmZi9saW5lJztcbmltcG9ydCB7ZGlmZlNlbnRlbmNlc30gZnJvbSAnLi9kaWZmL3NlbnRlbmNlJztcblxuaW1wb3J0IHtkaWZmQ3NzfSBmcm9tICcuL2RpZmYvY3NzJztcbmltcG9ydCB7ZGlmZkpzb24sIGNhbm9uaWNhbGl6ZX0gZnJvbSAnLi9kaWZmL2pzb24nO1xuXG5pbXBvcnQge2RpZmZBcnJheXN9IGZyb20gJy4vZGlmZi9hcnJheSc7XG5cbmltcG9ydCB7YXBwbHlQYXRjaCwgYXBwbHlQYXRjaGVzfSBmcm9tICcuL3BhdGNoL2FwcGx5JztcbmltcG9ydCB7cGFyc2VQYXRjaH0gZnJvbSAnLi9wYXRjaC9wYXJzZSc7XG5pbXBvcnQge21lcmdlfSBmcm9tICcuL3BhdGNoL21lcmdlJztcbmltcG9ydCB7c3RydWN0dXJlZFBhdGNoLCBjcmVhdGVUd29GaWxlc1BhdGNoLCBjcmVhdGVQYXRjaH0gZnJvbSAnLi9wYXRjaC9jcmVhdGUnO1xuXG5pbXBvcnQge2NvbnZlcnRDaGFuZ2VzVG9ETVB9IGZyb20gJy4vY29udmVydC9kbXAnO1xuaW1wb3J0IHtjb252ZXJ0Q2hhbmdlc1RvWE1MfSBmcm9tICcuL2NvbnZlcnQveG1sJztcblxuZXhwb3J0IHtcbiAgRGlmZixcblxuICBkaWZmQ2hhcnMsXG4gIGRpZmZXb3JkcyxcbiAgZGlmZldvcmRzV2l0aFNwYWNlLFxuICBkaWZmTGluZXMsXG4gIGRpZmZUcmltbWVkTGluZXMsXG4gIGRpZmZTZW50ZW5jZXMsXG5cbiAgZGlmZkNzcyxcbiAgZGlmZkpzb24sXG5cbiAgZGlmZkFycmF5cyxcblxuICBzdHJ1Y3R1cmVkUGF0Y2gsXG4gIGNyZWF0ZVR3b0ZpbGVzUGF0Y2gsXG4gIGNyZWF0ZVBhdGNoLFxuICBhcHBseVBhdGNoLFxuICBhcHBseVBhdGNoZXMsXG4gIHBhcnNlUGF0Y2gsXG4gIG1lcmdlLFxuICBjb252ZXJ0Q2hhbmdlc1RvRE1QLFxuICBjb252ZXJ0Q2hhbmdlc1RvWE1MLFxuICBjYW5vbmljYWxpemVcbn07XG4iXX0= + + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports['default'] = /*istanbul ignore end*/Diff; + function Diff() {} + + Diff.prototype = { + /*istanbul ignore start*/ /*istanbul ignore end*/diff: function diff(oldString, newString) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var callback = options.callback; + if (typeof options === 'function') { + callback = options; + options = {}; + } + this.options = options; + + var self = this; + + function done(value) { + if (callback) { + setTimeout(function () { + callback(undefined, value); + }, 0); + return true; + } else { + return value; + } + } + + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString); + newString = this.castInput(newString); + + oldString = this.removeEmpty(this.tokenize(oldString)); + newString = this.removeEmpty(this.tokenize(newString)); + + var newLen = newString.length, + oldLen = oldString.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0, i.e. the content starts with the same values + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + // Identity per the equality and tokenizer + return done([{ value: this.join(newString), count: newString.length }]); + } + + // Main worker method. checks all permutations of a given edit length for acceptance. + function execEditLength() { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { + var basePath = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + var addPath = bestPath[diagonalPath - 1], + removePath = bestPath[diagonalPath + 1], + _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } + + var canAdd = addPath && addPath.newPos + 1 < newLen, + canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen; + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || canRemove && addPath.newPos < removePath.newPos) { + basePath = clonePath(removePath); + self.pushComponent(basePath.components, undefined, true); + } else { + basePath = addPath; // No need to clone, we've pulled it from the list + basePath.newPos++; + self.pushComponent(basePath.components, true, undefined); + } + + _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + + // If we have hit the end of both strings, then we are done + if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) { + return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken)); + } else { + // Otherwise track this path as a potential candidate and continue. + bestPath[diagonalPath] = basePath; + } + } + + editLength++; + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the + // sync and async mode which is never fun. Loops over execEditLength until a value + // is produced. + if (callback) { + (function exec() { + setTimeout(function () { + // This should not happen, but we want to be safe. + /* istanbul ignore next */ + if (editLength > maxEditLength) { + return callback(); + } + + if (!execEditLength()) { + exec(); + } + }, 0); + })(); + } else { + while (editLength <= maxEditLength) { + var ret = execEditLength(); + if (ret) { + return ret; + } + } + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/pushComponent: function pushComponent(components, added, removed) { + var last = components[components.length - 1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length - 1] = { count: last.count + 1, added: added, removed: removed }; + } else { + components.push({ count: 1, added: added, removed: removed }); + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } + + if (commonCount) { + basePath.components.push({ count: commonCount }); + } + + basePath.newPos = newPos; + return oldPos; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/equals: function equals(left, right) { + if (this.options.comparator) { + return this.options.comparator(left, right); + } else { + return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/removeEmpty: function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/castInput: function castInput(value) { + return value; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/tokenize: function tokenize(value) { + return value.split(''); + }, + /*istanbul ignore start*/ /*istanbul ignore end*/join: function join(chars) { + return chars.join(''); + } + }; + + function buildValues(diff, components, newString, oldString, useLongestToken) { + var componentPos = 0, + componentLen = components.length, + newPos = 0, + oldPos = 0; + + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + if (!component.removed) { + if (!component.added && useLongestToken) { + var value = newString.slice(newPos, newPos + component.count); + value = value.map(function (value, i) { + var oldValue = oldString[oldPos + i]; + return oldValue.length > value.length ? oldValue : value; + }); + + component.value = diff.join(value); + } else { + component.value = diff.join(newString.slice(newPos, newPos + component.count)); + } + newPos += component.count; + + // Common case + if (!component.added) { + oldPos += component.count; + } + } else { + component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); + oldPos += component.count; + + // Reverse add and remove so removes are output first to match common convention + // The diffing algorithm is tied to add then remove output and this is the simplest + // route to get the desired output with minimal overhead. + if (componentPos && components[componentPos - 1].added) { + var tmp = components[componentPos - 1]; + components[componentPos - 1] = components[componentPos]; + components[componentPos] = tmp; + } + } + } + + // Special case handle for when one terminal is ignored (i.e. whitespace). + // For this case we merge the terminal into the prior string and drop the change. + // This is only available for string mode. + var lastComponent = components[componentLen - 1]; + if (componentLen > 1 && typeof lastComponent.value === 'string' && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) { + components[componentLen - 2].value += lastComponent.value; + components.pop(); + } + + return components; + } + + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2Jhc2UuanMiXSwibmFtZXMiOlsiRGlmZiIsInByb3RvdHlwZSIsImRpZmYiLCJvbGRTdHJpbmciLCJuZXdTdHJpbmciLCJvcHRpb25zIiwiY2FsbGJhY2siLCJzZWxmIiwiZG9uZSIsInZhbHVlIiwic2V0VGltZW91dCIsInVuZGVmaW5lZCIsImNhc3RJbnB1dCIsInJlbW92ZUVtcHR5IiwidG9rZW5pemUiLCJuZXdMZW4iLCJsZW5ndGgiLCJvbGRMZW4iLCJlZGl0TGVuZ3RoIiwibWF4RWRpdExlbmd0aCIsImJlc3RQYXRoIiwibmV3UG9zIiwiY29tcG9uZW50cyIsIm9sZFBvcyIsImV4dHJhY3RDb21tb24iLCJqb2luIiwiY291bnQiLCJleGVjRWRpdExlbmd0aCIsImRpYWdvbmFsUGF0aCIsImJhc2VQYXRoIiwiYWRkUGF0aCIsInJlbW92ZVBhdGgiLCJjYW5BZGQiLCJjYW5SZW1vdmUiLCJjbG9uZVBhdGgiLCJwdXNoQ29tcG9uZW50IiwiYnVpbGRWYWx1ZXMiLCJ1c2VMb25nZXN0VG9rZW4iLCJleGVjIiwicmV0IiwiYWRkZWQiLCJyZW1vdmVkIiwibGFzdCIsInB1c2giLCJjb21tb25Db3VudCIsImVxdWFscyIsImxlZnQiLCJyaWdodCIsImNvbXBhcmF0b3IiLCJpZ25vcmVDYXNlIiwidG9Mb3dlckNhc2UiLCJhcnJheSIsImkiLCJzcGxpdCIsImNoYXJzIiwiY29tcG9uZW50UG9zIiwiY29tcG9uZW50TGVuIiwiY29tcG9uZW50Iiwic2xpY2UiLCJtYXAiLCJvbGRWYWx1ZSIsInRtcCIsImxhc3RDb21wb25lbnQiLCJwb3AiLCJwYXRoIl0sIm1hcHBpbmdzIjoiOzs7NENBQXdCQSxJO0FBQVQsU0FBU0EsSUFBVCxHQUFnQixDQUFFOztBQUVqQ0EsS0FBS0MsU0FBTCxHQUFpQjtBQUFBLG1EQUNmQyxJQURlLGdCQUNWQyxTQURVLEVBQ0NDLFNBREQsRUFDMEI7QUFBQSx3REFBZEMsT0FBYyx1RUFBSixFQUFJOztBQUN2QyxRQUFJQyxXQUFXRCxRQUFRQyxRQUF2QjtBQUNBLFFBQUksT0FBT0QsT0FBUCxLQUFtQixVQUF2QixFQUFtQztBQUNqQ0MsaUJBQVdELE9BQVg7QUFDQUEsZ0JBQVUsRUFBVjtBQUNEO0FBQ0QsU0FBS0EsT0FBTCxHQUFlQSxPQUFmOztBQUVBLFFBQUlFLE9BQU8sSUFBWDs7QUFFQSxhQUFTQyxJQUFULENBQWNDLEtBQWQsRUFBcUI7QUFDbkIsVUFBSUgsUUFBSixFQUFjO0FBQ1pJLG1CQUFXLFlBQVc7QUFBRUosbUJBQVNLLFNBQVQsRUFBb0JGLEtBQXBCO0FBQTZCLFNBQXJELEVBQXVELENBQXZEO0FBQ0EsZUFBTyxJQUFQO0FBQ0QsT0FIRCxNQUdPO0FBQ0wsZUFBT0EsS0FBUDtBQUNEO0FBQ0Y7O0FBRUQ7QUFDQU4sZ0JBQVksS0FBS1MsU0FBTCxDQUFlVCxTQUFmLENBQVo7QUFDQUMsZ0JBQVksS0FBS1EsU0FBTCxDQUFlUixTQUFmLENBQVo7O0FBRUFELGdCQUFZLEtBQUtVLFdBQUwsQ0FBaUIsS0FBS0MsUUFBTCxDQUFjWCxTQUFkLENBQWpCLENBQVo7QUFDQUMsZ0JBQVksS0FBS1MsV0FBTCxDQUFpQixLQUFLQyxRQUFMLENBQWNWLFNBQWQsQ0FBakIsQ0FBWjs7QUFFQSxRQUFJVyxTQUFTWCxVQUFVWSxNQUF2QjtBQUFBLFFBQStCQyxTQUFTZCxVQUFVYSxNQUFsRDtBQUNBLFFBQUlFLGFBQWEsQ0FBakI7QUFDQSxRQUFJQyxnQkFBZ0JKLFNBQVNFLE1BQTdCO0FBQ0EsUUFBSUcsV0FBVyxDQUFDLEVBQUVDLFFBQVEsQ0FBQyxDQUFYLEVBQWNDLFlBQVksRUFBMUIsRUFBRCxDQUFmOztBQUVBO0FBQ0EsUUFBSUMsU0FBUyxLQUFLQyxhQUFMLENBQW1CSixTQUFTLENBQVQsQ0FBbkIsRUFBZ0NoQixTQUFoQyxFQUEyQ0QsU0FBM0MsRUFBc0QsQ0FBdEQsQ0FBYjtBQUNBLFFBQUlpQixTQUFTLENBQVQsRUFBWUMsTUFBWixHQUFxQixDQUFyQixJQUEwQk4sTUFBMUIsSUFBb0NRLFNBQVMsQ0FBVCxJQUFjTixNQUF0RCxFQUE4RDtBQUM1RDtBQUNBLGFBQU9ULEtBQUssQ0FBQyxFQUFDQyxPQUFPLEtBQUtnQixJQUFMLENBQVVyQixTQUFWLENBQVIsRUFBOEJzQixPQUFPdEIsVUFBVVksTUFBL0MsRUFBRCxDQUFMLENBQVA7QUFDRDs7QUFFRDtBQUNBLGFBQVNXLGNBQVQsR0FBMEI7QUFDeEIsV0FBSyxJQUFJQyxlQUFlLENBQUMsQ0FBRCxHQUFLVixVQUE3QixFQUF5Q1UsZ0JBQWdCVixVQUF6RCxFQUFxRVUsZ0JBQWdCLENBQXJGLEVBQXdGO0FBQ3RGLFlBQUlDLDBDQUFKO0FBQ0EsWUFBSUMsVUFBVVYsU0FBU1EsZUFBZSxDQUF4QixDQUFkO0FBQUEsWUFDSUcsYUFBYVgsU0FBU1EsZUFBZSxDQUF4QixDQURqQjtBQUFBLFlBRUlMLFVBQVMsQ0FBQ1EsYUFBYUEsV0FBV1YsTUFBeEIsR0FBaUMsQ0FBbEMsSUFBdUNPLFlBRnBEO0FBR0EsWUFBSUUsT0FBSixFQUFhO0FBQ1g7QUFDQVYsbUJBQVNRLGVBQWUsQ0FBeEIsSUFBNkJqQixTQUE3QjtBQUNEOztBQUVELFlBQUlxQixTQUFTRixXQUFXQSxRQUFRVCxNQUFSLEdBQWlCLENBQWpCLEdBQXFCTixNQUE3QztBQUFBLFlBQ0lrQixZQUFZRixjQUFjLEtBQUtSLE9BQW5CLElBQTZCQSxVQUFTTixNQUR0RDtBQUVBLFlBQUksQ0FBQ2UsTUFBRCxJQUFXLENBQUNDLFNBQWhCLEVBQTJCO0FBQ3pCO0FBQ0FiLG1CQUFTUSxZQUFULElBQXlCakIsU0FBekI7QUFDQTtBQUNEOztBQUVEO0FBQ0E7QUFDQTtBQUNBLFlBQUksQ0FBQ3FCLE1BQUQsSUFBWUMsYUFBYUgsUUFBUVQsTUFBUixHQUFpQlUsV0FBV1YsTUFBekQsRUFBa0U7QUFDaEVRLHFCQUFXSyxVQUFVSCxVQUFWLENBQVg7QUFDQXhCLGVBQUs0QixhQUFMLENBQW1CTixTQUFTUCxVQUE1QixFQUF3Q1gsU0FBeEMsRUFBbUQsSUFBbkQ7QUFDRCxTQUhELE1BR087QUFDTGtCLHFCQUFXQyxPQUFYLENBREssQ0FDaUI7QUFDdEJELG1CQUFTUixNQUFUO0FBQ0FkLGVBQUs0QixhQUFMLENBQW1CTixTQUFTUCxVQUE1QixFQUF3QyxJQUF4QyxFQUE4Q1gsU0FBOUM7QUFDRDs7QUFFRFksa0JBQVNoQixLQUFLaUIsYUFBTCxDQUFtQkssUUFBbkIsRUFBNkJ6QixTQUE3QixFQUF3Q0QsU0FBeEMsRUFBbUR5QixZQUFuRCxDQUFUOztBQUVBO0FBQ0EsWUFBSUMsU0FBU1IsTUFBVCxHQUFrQixDQUFsQixJQUF1Qk4sTUFBdkIsSUFBaUNRLFVBQVMsQ0FBVCxJQUFjTixNQUFuRCxFQUEyRDtBQUN6RCxpQkFBT1QsS0FBSzRCLFlBQVk3QixJQUFaLEVBQWtCc0IsU0FBU1AsVUFBM0IsRUFBdUNsQixTQUF2QyxFQUFrREQsU0FBbEQsRUFBNkRJLEtBQUs4QixlQUFsRSxDQUFMLENBQVA7QUFDRCxTQUZELE1BRU87QUFDTDtBQUNBakIsbUJBQVNRLFlBQVQsSUFBeUJDLFFBQXpCO0FBQ0Q7QUFDRjs7QUFFRFg7QUFDRDs7QUFFRDtBQUNBO0FBQ0E7QUFDQSxRQUFJWixRQUFKLEVBQWM7QUFDWCxnQkFBU2dDLElBQVQsR0FBZ0I7QUFDZjVCLG1CQUFXLFlBQVc7QUFDcEI7QUFDQTtBQUNBLGNBQUlRLGFBQWFDLGFBQWpCLEVBQWdDO0FBQzlCLG1CQUFPYixVQUFQO0FBQ0Q7O0FBRUQsY0FBSSxDQUFDcUIsZ0JBQUwsRUFBdUI7QUFDckJXO0FBQ0Q7QUFDRixTQVZELEVBVUcsQ0FWSDtBQVdELE9BWkEsR0FBRDtBQWFELEtBZEQsTUFjTztBQUNMLGFBQU9wQixjQUFjQyxhQUFyQixFQUFvQztBQUNsQyxZQUFJb0IsTUFBTVosZ0JBQVY7QUFDQSxZQUFJWSxHQUFKLEVBQVM7QUFDUCxpQkFBT0EsR0FBUDtBQUNEO0FBQ0Y7QUFDRjtBQUNGLEdBOUdjO0FBQUEsbURBZ0hmSixhQWhIZSx5QkFnSERiLFVBaEhDLEVBZ0hXa0IsS0FoSFgsRUFnSGtCQyxPQWhIbEIsRUFnSDJCO0FBQ3hDLFFBQUlDLE9BQU9wQixXQUFXQSxXQUFXTixNQUFYLEdBQW9CLENBQS9CLENBQVg7QUFDQSxRQUFJMEIsUUFBUUEsS0FBS0YsS0FBTCxLQUFlQSxLQUF2QixJQUFnQ0UsS0FBS0QsT0FBTCxLQUFpQkEsT0FBckQsRUFBOEQ7QUFDNUQ7QUFDQTtBQUNBbkIsaUJBQVdBLFdBQVdOLE1BQVgsR0FBb0IsQ0FBL0IsSUFBb0MsRUFBQ1UsT0FBT2dCLEtBQUtoQixLQUFMLEdBQWEsQ0FBckIsRUFBd0JjLE9BQU9BLEtBQS9CLEVBQXNDQyxTQUFTQSxPQUEvQyxFQUFwQztBQUNELEtBSkQsTUFJTztBQUNMbkIsaUJBQVdxQixJQUFYLENBQWdCLEVBQUNqQixPQUFPLENBQVIsRUFBV2MsT0FBT0EsS0FBbEIsRUFBeUJDLFNBQVNBLE9BQWxDLEVBQWhCO0FBQ0Q7QUFDRixHQXpIYztBQUFBLG1EQTBIZmpCLGFBMUhlLHlCQTBIREssUUExSEMsRUEwSFN6QixTQTFIVCxFQTBIb0JELFNBMUhwQixFQTBIK0J5QixZQTFIL0IsRUEwSDZDO0FBQzFELFFBQUliLFNBQVNYLFVBQVVZLE1BQXZCO0FBQUEsUUFDSUMsU0FBU2QsVUFBVWEsTUFEdkI7QUFBQSxRQUVJSyxTQUFTUSxTQUFTUixNQUZ0QjtBQUFBLFFBR0lFLFNBQVNGLFNBQVNPLFlBSHRCO0FBQUEsUUFLSWdCLGNBQWMsQ0FMbEI7QUFNQSxXQUFPdkIsU0FBUyxDQUFULEdBQWFOLE1BQWIsSUFBdUJRLFNBQVMsQ0FBVCxHQUFhTixNQUFwQyxJQUE4QyxLQUFLNEIsTUFBTCxDQUFZekMsVUFBVWlCLFNBQVMsQ0FBbkIsQ0FBWixFQUFtQ2xCLFVBQVVvQixTQUFTLENBQW5CLENBQW5DLENBQXJELEVBQWdIO0FBQzlHRjtBQUNBRTtBQUNBcUI7QUFDRDs7QUFFRCxRQUFJQSxXQUFKLEVBQWlCO0FBQ2ZmLGVBQVNQLFVBQVQsQ0FBb0JxQixJQUFwQixDQUF5QixFQUFDakIsT0FBT2tCLFdBQVIsRUFBekI7QUFDRDs7QUFFRGYsYUFBU1IsTUFBVCxHQUFrQkEsTUFBbEI7QUFDQSxXQUFPRSxNQUFQO0FBQ0QsR0E3SWM7QUFBQSxtREErSWZzQixNQS9JZSxrQkErSVJDLElBL0lRLEVBK0lGQyxLQS9JRSxFQStJSztBQUNsQixRQUFJLEtBQUsxQyxPQUFMLENBQWEyQyxVQUFqQixFQUE2QjtBQUMzQixhQUFPLEtBQUszQyxPQUFMLENBQWEyQyxVQUFiLENBQXdCRixJQUF4QixFQUE4QkMsS0FBOUIsQ0FBUDtBQUNELEtBRkQsTUFFTztBQUNMLGFBQU9ELFNBQVNDLEtBQVQsSUFDRCxLQUFLMUMsT0FBTCxDQUFhNEMsVUFBYixJQUEyQkgsS0FBS0ksV0FBTCxPQUF1QkgsTUFBTUcsV0FBTixFQUR4RDtBQUVEO0FBQ0YsR0F0SmM7QUFBQSxtREF1SmZyQyxXQXZKZSx1QkF1SkhzQyxLQXZKRyxFQXVKSTtBQUNqQixRQUFJWixNQUFNLEVBQVY7QUFDQSxTQUFLLElBQUlhLElBQUksQ0FBYixFQUFnQkEsSUFBSUQsTUFBTW5DLE1BQTFCLEVBQWtDb0MsR0FBbEMsRUFBdUM7QUFDckMsVUFBSUQsTUFBTUMsQ0FBTixDQUFKLEVBQWM7QUFDWmIsWUFBSUksSUFBSixDQUFTUSxNQUFNQyxDQUFOLENBQVQ7QUFDRDtBQUNGO0FBQ0QsV0FBT2IsR0FBUDtBQUNELEdBL0pjO0FBQUEsbURBZ0tmM0IsU0FoS2UscUJBZ0tMSCxLQWhLSyxFQWdLRTtBQUNmLFdBQU9BLEtBQVA7QUFDRCxHQWxLYztBQUFBLG1EQW1LZkssUUFuS2Usb0JBbUtOTCxLQW5LTSxFQW1LQztBQUNkLFdBQU9BLE1BQU00QyxLQUFOLENBQVksRUFBWixDQUFQO0FBQ0QsR0FyS2M7QUFBQSxtREFzS2Y1QixJQXRLZSxnQkFzS1Y2QixLQXRLVSxFQXNLSDtBQUNWLFdBQU9BLE1BQU03QixJQUFOLENBQVcsRUFBWCxDQUFQO0FBQ0Q7QUF4S2MsQ0FBakI7O0FBMktBLFNBQVNXLFdBQVQsQ0FBcUJsQyxJQUFyQixFQUEyQm9CLFVBQTNCLEVBQXVDbEIsU0FBdkMsRUFBa0RELFNBQWxELEVBQTZEa0MsZUFBN0QsRUFBOEU7QUFDNUUsTUFBSWtCLGVBQWUsQ0FBbkI7QUFBQSxNQUNJQyxlQUFlbEMsV0FBV04sTUFEOUI7QUFBQSxNQUVJSyxTQUFTLENBRmI7QUFBQSxNQUdJRSxTQUFTLENBSGI7O0FBS0EsU0FBT2dDLGVBQWVDLFlBQXRCLEVBQW9DRCxjQUFwQyxFQUFvRDtBQUNsRCxRQUFJRSxZQUFZbkMsV0FBV2lDLFlBQVgsQ0FBaEI7QUFDQSxRQUFJLENBQUNFLFVBQVVoQixPQUFmLEVBQXdCO0FBQ3RCLFVBQUksQ0FBQ2dCLFVBQVVqQixLQUFYLElBQW9CSCxlQUF4QixFQUF5QztBQUN2QyxZQUFJNUIsUUFBUUwsVUFBVXNELEtBQVYsQ0FBZ0JyQyxNQUFoQixFQUF3QkEsU0FBU29DLFVBQVUvQixLQUEzQyxDQUFaO0FBQ0FqQixnQkFBUUEsTUFBTWtELEdBQU4sQ0FBVSxVQUFTbEQsS0FBVCxFQUFnQjJDLENBQWhCLEVBQW1CO0FBQ25DLGNBQUlRLFdBQVd6RCxVQUFVb0IsU0FBUzZCLENBQW5CLENBQWY7QUFDQSxpQkFBT1EsU0FBUzVDLE1BQVQsR0FBa0JQLE1BQU1PLE1BQXhCLEdBQWlDNEMsUUFBakMsR0FBNENuRCxLQUFuRDtBQUNELFNBSE8sQ0FBUjs7QUFLQWdELGtCQUFVaEQsS0FBVixHQUFrQlAsS0FBS3VCLElBQUwsQ0FBVWhCLEtBQVYsQ0FBbEI7QUFDRCxPQVJELE1BUU87QUFDTGdELGtCQUFVaEQsS0FBVixHQUFrQlAsS0FBS3VCLElBQUwsQ0FBVXJCLFVBQVVzRCxLQUFWLENBQWdCckMsTUFBaEIsRUFBd0JBLFNBQVNvQyxVQUFVL0IsS0FBM0MsQ0FBVixDQUFsQjtBQUNEO0FBQ0RMLGdCQUFVb0MsVUFBVS9CLEtBQXBCOztBQUVBO0FBQ0EsVUFBSSxDQUFDK0IsVUFBVWpCLEtBQWYsRUFBc0I7QUFDcEJqQixrQkFBVWtDLFVBQVUvQixLQUFwQjtBQUNEO0FBQ0YsS0FsQkQsTUFrQk87QUFDTCtCLGdCQUFVaEQsS0FBVixHQUFrQlAsS0FBS3VCLElBQUwsQ0FBVXRCLFVBQVV1RCxLQUFWLENBQWdCbkMsTUFBaEIsRUFBd0JBLFNBQVNrQyxVQUFVL0IsS0FBM0MsQ0FBVixDQUFsQjtBQUNBSCxnQkFBVWtDLFVBQVUvQixLQUFwQjs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxVQUFJNkIsZ0JBQWdCakMsV0FBV2lDLGVBQWUsQ0FBMUIsRUFBNkJmLEtBQWpELEVBQXdEO0FBQ3RELFlBQUlxQixNQUFNdkMsV0FBV2lDLGVBQWUsQ0FBMUIsQ0FBVjtBQUNBakMsbUJBQVdpQyxlQUFlLENBQTFCLElBQStCakMsV0FBV2lDLFlBQVgsQ0FBL0I7QUFDQWpDLG1CQUFXaUMsWUFBWCxJQUEyQk0sR0FBM0I7QUFDRDtBQUNGO0FBQ0Y7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsTUFBSUMsZ0JBQWdCeEMsV0FBV2tDLGVBQWUsQ0FBMUIsQ0FBcEI7QUFDQSxNQUFJQSxlQUFlLENBQWYsSUFDRyxPQUFPTSxjQUFjckQsS0FBckIsS0FBK0IsUUFEbEMsS0FFSXFELGNBQWN0QixLQUFkLElBQXVCc0IsY0FBY3JCLE9BRnpDLEtBR0d2QyxLQUFLMkMsTUFBTCxDQUFZLEVBQVosRUFBZ0JpQixjQUFjckQsS0FBOUIsQ0FIUCxFQUc2QztBQUMzQ2EsZUFBV2tDLGVBQWUsQ0FBMUIsRUFBNkIvQyxLQUE3QixJQUFzQ3FELGNBQWNyRCxLQUFwRDtBQUNBYSxlQUFXeUMsR0FBWDtBQUNEOztBQUVELFNBQU96QyxVQUFQO0FBQ0Q7O0FBRUQsU0FBU1ksU0FBVCxDQUFtQjhCLElBQW5CLEVBQXlCO0FBQ3ZCLFNBQU8sRUFBRTNDLFFBQVEyQyxLQUFLM0MsTUFBZixFQUF1QkMsWUFBWTBDLEtBQUsxQyxVQUFMLENBQWdCb0MsS0FBaEIsQ0FBc0IsQ0FBdEIsQ0FBbkMsRUFBUDtBQUNEIiwiZmlsZSI6ImJhc2UuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBEaWZmKCkge31cblxuRGlmZi5wcm90b3R5cGUgPSB7XG4gIGRpZmYob2xkU3RyaW5nLCBuZXdTdHJpbmcsIG9wdGlvbnMgPSB7fSkge1xuICAgIGxldCBjYWxsYmFjayA9IG9wdGlvbnMuY2FsbGJhY2s7XG4gICAgaWYgKHR5cGVvZiBvcHRpb25zID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICBjYWxsYmFjayA9IG9wdGlvbnM7XG4gICAgICBvcHRpb25zID0ge307XG4gICAgfVxuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnM7XG5cbiAgICBsZXQgc2VsZiA9IHRoaXM7XG5cbiAgICBmdW5jdGlvbiBkb25lKHZhbHVlKSB7XG4gICAgICBpZiAoY2FsbGJhY2spIHtcbiAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHsgY2FsbGJhY2sodW5kZWZpbmVkLCB2YWx1ZSk7IH0sIDApO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBbGxvdyBzdWJjbGFzc2VzIHRvIG1hc3NhZ2UgdGhlIGlucHV0IHByaW9yIHRvIHJ1bm5pbmdcbiAgICBvbGRTdHJpbmcgPSB0aGlzLmNhc3RJbnB1dChvbGRTdHJpbmcpO1xuICAgIG5ld1N0cmluZyA9IHRoaXMuY2FzdElucHV0KG5ld1N0cmluZyk7XG5cbiAgICBvbGRTdHJpbmcgPSB0aGlzLnJlbW92ZUVtcHR5KHRoaXMudG9rZW5pemUob2xkU3RyaW5nKSk7XG4gICAgbmV3U3RyaW5nID0gdGhpcy5yZW1vdmVFbXB0eSh0aGlzLnRva2VuaXplKG5ld1N0cmluZykpO1xuXG4gICAgbGV0IG5ld0xlbiA9IG5ld1N0cmluZy5sZW5ndGgsIG9sZExlbiA9IG9sZFN0cmluZy5sZW5ndGg7XG4gICAgbGV0IGVkaXRMZW5ndGggPSAxO1xuICAgIGxldCBtYXhFZGl0TGVuZ3RoID0gbmV3TGVuICsgb2xkTGVuO1xuICAgIGxldCBiZXN0UGF0aCA9IFt7IG5ld1BvczogLTEsIGNvbXBvbmVudHM6IFtdIH1dO1xuXG4gICAgLy8gU2VlZCBlZGl0TGVuZ3RoID0gMCwgaS5lLiB0aGUgY29udGVudCBzdGFydHMgd2l0aCB0aGUgc2FtZSB2YWx1ZXNcbiAgICBsZXQgb2xkUG9zID0gdGhpcy5leHRyYWN0Q29tbW9uKGJlc3RQYXRoWzBdLCBuZXdTdHJpbmcsIG9sZFN0cmluZywgMCk7XG4gICAgaWYgKGJlc3RQYXRoWzBdLm5ld1BvcyArIDEgPj0gbmV3TGVuICYmIG9sZFBvcyArIDEgPj0gb2xkTGVuKSB7XG4gICAgICAvLyBJZGVudGl0eSBwZXIgdGhlIGVxdWFsaXR5IGFuZCB0b2tlbml6ZXJcbiAgICAgIHJldHVybiBkb25lKFt7dmFsdWU6IHRoaXMuam9pbihuZXdTdHJpbmcpLCBjb3VudDogbmV3U3RyaW5nLmxlbmd0aH1dKTtcbiAgICB9XG5cbiAgICAvLyBNYWluIHdvcmtlciBtZXRob2QuIGNoZWNrcyBhbGwgcGVybXV0YXRpb25zIG9mIGEgZ2l2ZW4gZWRpdCBsZW5ndGggZm9yIGFjY2VwdGFuY2UuXG4gICAgZnVuY3Rpb24gZXhlY0VkaXRMZW5ndGgoKSB7XG4gICAgICBmb3IgKGxldCBkaWFnb25hbFBhdGggPSAtMSAqIGVkaXRMZW5ndGg7IGRpYWdvbmFsUGF0aCA8PSBlZGl0TGVuZ3RoOyBkaWFnb25hbFBhdGggKz0gMikge1xuICAgICAgICBsZXQgYmFzZVBhdGg7XG4gICAgICAgIGxldCBhZGRQYXRoID0gYmVzdFBhdGhbZGlhZ29uYWxQYXRoIC0gMV0sXG4gICAgICAgICAgICByZW1vdmVQYXRoID0gYmVzdFBhdGhbZGlhZ29uYWxQYXRoICsgMV0sXG4gICAgICAgICAgICBvbGRQb3MgPSAocmVtb3ZlUGF0aCA/IHJlbW92ZVBhdGgubmV3UG9zIDogMCkgLSBkaWFnb25hbFBhdGg7XG4gICAgICAgIGlmIChhZGRQYXRoKSB7XG4gICAgICAgICAgLy8gTm8gb25lIGVsc2UgaXMgZ29pbmcgdG8gYXR0ZW1wdCB0byB1c2UgdGhpcyB2YWx1ZSwgY2xlYXIgaXRcbiAgICAgICAgICBiZXN0UGF0aFtkaWFnb25hbFBhdGggLSAxXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBjYW5BZGQgPSBhZGRQYXRoICYmIGFkZFBhdGgubmV3UG9zICsgMSA8IG5ld0xlbixcbiAgICAgICAgICAgIGNhblJlbW92ZSA9IHJlbW92ZVBhdGggJiYgMCA8PSBvbGRQb3MgJiYgb2xkUG9zIDwgb2xkTGVuO1xuICAgICAgICBpZiAoIWNhbkFkZCAmJiAhY2FuUmVtb3ZlKSB7XG4gICAgICAgICAgLy8gSWYgdGhpcyBwYXRoIGlzIGEgdGVybWluYWwgdGhlbiBwcnVuZVxuICAgICAgICAgIGJlc3RQYXRoW2RpYWdvbmFsUGF0aF0gPSB1bmRlZmluZWQ7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTZWxlY3QgdGhlIGRpYWdvbmFsIHRoYXQgd2Ugd2FudCB0byBicmFuY2ggZnJvbS4gV2Ugc2VsZWN0IHRoZSBwcmlvclxuICAgICAgICAvLyBwYXRoIHdob3NlIHBvc2l0aW9uIGluIHRoZSBuZXcgc3RyaW5nIGlzIHRoZSBmYXJ0aGVzdCBmcm9tIHRoZSBvcmlnaW5cbiAgICAgICAgLy8gYW5kIGRvZXMgbm90IHBhc3MgdGhlIGJvdW5kcyBvZiB0aGUgZGlmZiBncmFwaFxuICAgICAgICBpZiAoIWNhbkFkZCB8fCAoY2FuUmVtb3ZlICYmIGFkZFBhdGgubmV3UG9zIDwgcmVtb3ZlUGF0aC5uZXdQb3MpKSB7XG4gICAgICAgICAgYmFzZVBhdGggPSBjbG9uZVBhdGgocmVtb3ZlUGF0aCk7XG4gICAgICAgICAgc2VsZi5wdXNoQ29tcG9uZW50KGJhc2VQYXRoLmNvbXBvbmVudHMsIHVuZGVmaW5lZCwgdHJ1ZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYmFzZVBhdGggPSBhZGRQYXRoOyAgIC8vIE5vIG5lZWQgdG8gY2xvbmUsIHdlJ3ZlIHB1bGxlZCBpdCBmcm9tIHRoZSBsaXN0XG4gICAgICAgICAgYmFzZVBhdGgubmV3UG9zKys7XG4gICAgICAgICAgc2VsZi5wdXNoQ29tcG9uZW50KGJhc2VQYXRoLmNvbXBvbmVudHMsIHRydWUsIHVuZGVmaW5lZCk7XG4gICAgICAgIH1cblxuICAgICAgICBvbGRQb3MgPSBzZWxmLmV4dHJhY3RDb21tb24oYmFzZVBhdGgsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBkaWFnb25hbFBhdGgpO1xuXG4gICAgICAgIC8vIElmIHdlIGhhdmUgaGl0IHRoZSBlbmQgb2YgYm90aCBzdHJpbmdzLCB0aGVuIHdlIGFyZSBkb25lXG4gICAgICAgIGlmIChiYXNlUGF0aC5uZXdQb3MgKyAxID49IG5ld0xlbiAmJiBvbGRQb3MgKyAxID49IG9sZExlbikge1xuICAgICAgICAgIHJldHVybiBkb25lKGJ1aWxkVmFsdWVzKHNlbGYsIGJhc2VQYXRoLmNvbXBvbmVudHMsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBzZWxmLnVzZUxvbmdlc3RUb2tlbikpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIE90aGVyd2lzZSB0cmFjayB0aGlzIHBhdGggYXMgYSBwb3RlbnRpYWwgY2FuZGlkYXRlIGFuZCBjb250aW51ZS5cbiAgICAgICAgICBiZXN0UGF0aFtkaWFnb25hbFBhdGhdID0gYmFzZVBhdGg7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZWRpdExlbmd0aCsrO1xuICAgIH1cblxuICAgIC8vIFBlcmZvcm1zIHRoZSBsZW5ndGggb2YgZWRpdCBpdGVyYXRpb24uIElzIGEgYml0IGZ1Z2x5IGFzIHRoaXMgaGFzIHRvIHN1cHBvcnQgdGhlXG4gICAgLy8gc3luYyBhbmQgYXN5bmMgbW9kZSB3aGljaCBpcyBuZXZlciBmdW4uIExvb3BzIG92ZXIgZXhlY0VkaXRMZW5ndGggdW50aWwgYSB2YWx1ZVxuICAgIC8vIGlzIHByb2R1Y2VkLlxuICAgIGlmIChjYWxsYmFjaykge1xuICAgICAgKGZ1bmN0aW9uIGV4ZWMoKSB7XG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gVGhpcyBzaG91bGQgbm90IGhhcHBlbiwgYnV0IHdlIHdhbnQgdG8gYmUgc2FmZS5cbiAgICAgICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuICAgICAgICAgIGlmIChlZGl0TGVuZ3RoID4gbWF4RWRpdExlbmd0aCkge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKCFleGVjRWRpdExlbmd0aCgpKSB7XG4gICAgICAgICAgICBleGVjKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9LCAwKTtcbiAgICAgIH0oKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHdoaWxlIChlZGl0TGVuZ3RoIDw9IG1heEVkaXRMZW5ndGgpIHtcbiAgICAgICAgbGV0IHJldCA9IGV4ZWNFZGl0TGVuZ3RoKCk7XG4gICAgICAgIGlmIChyZXQpIHtcbiAgICAgICAgICByZXR1cm4gcmV0O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIHB1c2hDb21wb25lbnQoY29tcG9uZW50cywgYWRkZWQsIHJlbW92ZWQpIHtcbiAgICBsZXQgbGFzdCA9IGNvbXBvbmVudHNbY29tcG9uZW50cy5sZW5ndGggLSAxXTtcbiAgICBpZiAobGFzdCAmJiBsYXN0LmFkZGVkID09PSBhZGRlZCAmJiBsYXN0LnJlbW92ZWQgPT09IHJlbW92ZWQpIHtcbiAgICAgIC8vIFdlIG5lZWQgdG8gY2xvbmUgaGVyZSBhcyB0aGUgY29tcG9uZW50IGNsb25lIG9wZXJhdGlvbiBpcyBqdXN0XG4gICAgICAvLyBhcyBzaGFsbG93IGFycmF5IGNsb25lXG4gICAgICBjb21wb25lbnRzW2NvbXBvbmVudHMubGVuZ3RoIC0gMV0gPSB7Y291bnQ6IGxhc3QuY291bnQgKyAxLCBhZGRlZDogYWRkZWQsIHJlbW92ZWQ6IHJlbW92ZWQgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29tcG9uZW50cy5wdXNoKHtjb3VudDogMSwgYWRkZWQ6IGFkZGVkLCByZW1vdmVkOiByZW1vdmVkIH0pO1xuICAgIH1cbiAgfSxcbiAgZXh0cmFjdENvbW1vbihiYXNlUGF0aCwgbmV3U3RyaW5nLCBvbGRTdHJpbmcsIGRpYWdvbmFsUGF0aCkge1xuICAgIGxldCBuZXdMZW4gPSBuZXdTdHJpbmcubGVuZ3RoLFxuICAgICAgICBvbGRMZW4gPSBvbGRTdHJpbmcubGVuZ3RoLFxuICAgICAgICBuZXdQb3MgPSBiYXNlUGF0aC5uZXdQb3MsXG4gICAgICAgIG9sZFBvcyA9IG5ld1BvcyAtIGRpYWdvbmFsUGF0aCxcblxuICAgICAgICBjb21tb25Db3VudCA9IDA7XG4gICAgd2hpbGUgKG5ld1BvcyArIDEgPCBuZXdMZW4gJiYgb2xkUG9zICsgMSA8IG9sZExlbiAmJiB0aGlzLmVxdWFscyhuZXdTdHJpbmdbbmV3UG9zICsgMV0sIG9sZFN0cmluZ1tvbGRQb3MgKyAxXSkpIHtcbiAgICAgIG5ld1BvcysrO1xuICAgICAgb2xkUG9zKys7XG4gICAgICBjb21tb25Db3VudCsrO1xuICAgIH1cblxuICAgIGlmIChjb21tb25Db3VudCkge1xuICAgICAgYmFzZVBhdGguY29tcG9uZW50cy5wdXNoKHtjb3VudDogY29tbW9uQ291bnR9KTtcbiAgICB9XG5cbiAgICBiYXNlUGF0aC5uZXdQb3MgPSBuZXdQb3M7XG4gICAgcmV0dXJuIG9sZFBvcztcbiAgfSxcblxuICBlcXVhbHMobGVmdCwgcmlnaHQpIHtcbiAgICBpZiAodGhpcy5vcHRpb25zLmNvbXBhcmF0b3IpIHtcbiAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMuY29tcGFyYXRvcihsZWZ0LCByaWdodCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBsZWZ0ID09PSByaWdodFxuICAgICAgICB8fCAodGhpcy5vcHRpb25zLmlnbm9yZUNhc2UgJiYgbGVmdC50b0xvd2VyQ2FzZSgpID09PSByaWdodC50b0xvd2VyQ2FzZSgpKTtcbiAgICB9XG4gIH0sXG4gIHJlbW92ZUVtcHR5KGFycmF5KSB7XG4gICAgbGV0IHJldCA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYXJyYXkubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChhcnJheVtpXSkge1xuICAgICAgICByZXQucHVzaChhcnJheVtpXSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXQ7XG4gIH0sXG4gIGNhc3RJbnB1dCh2YWx1ZSkge1xuICAgIHJldHVybiB2YWx1ZTtcbiAgfSxcbiAgdG9rZW5pemUodmFsdWUpIHtcbiAgICByZXR1cm4gdmFsdWUuc3BsaXQoJycpO1xuICB9LFxuICBqb2luKGNoYXJzKSB7XG4gICAgcmV0dXJuIGNoYXJzLmpvaW4oJycpO1xuICB9XG59O1xuXG5mdW5jdGlvbiBidWlsZFZhbHVlcyhkaWZmLCBjb21wb25lbnRzLCBuZXdTdHJpbmcsIG9sZFN0cmluZywgdXNlTG9uZ2VzdFRva2VuKSB7XG4gIGxldCBjb21wb25lbnRQb3MgPSAwLFxuICAgICAgY29tcG9uZW50TGVuID0gY29tcG9uZW50cy5sZW5ndGgsXG4gICAgICBuZXdQb3MgPSAwLFxuICAgICAgb2xkUG9zID0gMDtcblxuICBmb3IgKDsgY29tcG9uZW50UG9zIDwgY29tcG9uZW50TGVuOyBjb21wb25lbnRQb3MrKykge1xuICAgIGxldCBjb21wb25lbnQgPSBjb21wb25lbnRzW2NvbXBvbmVudFBvc107XG4gICAgaWYgKCFjb21wb25lbnQucmVtb3ZlZCkge1xuICAgICAgaWYgKCFjb21wb25lbnQuYWRkZWQgJiYgdXNlTG9uZ2VzdFRva2VuKSB7XG4gICAgICAgIGxldCB2YWx1ZSA9IG5ld1N0cmluZy5zbGljZShuZXdQb3MsIG5ld1BvcyArIGNvbXBvbmVudC5jb3VudCk7XG4gICAgICAgIHZhbHVlID0gdmFsdWUubWFwKGZ1bmN0aW9uKHZhbHVlLCBpKSB7XG4gICAgICAgICAgbGV0IG9sZFZhbHVlID0gb2xkU3RyaW5nW29sZFBvcyArIGldO1xuICAgICAgICAgIHJldHVybiBvbGRWYWx1ZS5sZW5ndGggPiB2YWx1ZS5sZW5ndGggPyBvbGRWYWx1ZSA6IHZhbHVlO1xuICAgICAgICB9KTtcblxuICAgICAgICBjb21wb25lbnQudmFsdWUgPSBkaWZmLmpvaW4odmFsdWUpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29tcG9uZW50LnZhbHVlID0gZGlmZi5qb2luKG5ld1N0cmluZy5zbGljZShuZXdQb3MsIG5ld1BvcyArIGNvbXBvbmVudC5jb3VudCkpO1xuICAgICAgfVxuICAgICAgbmV3UG9zICs9IGNvbXBvbmVudC5jb3VudDtcblxuICAgICAgLy8gQ29tbW9uIGNhc2VcbiAgICAgIGlmICghY29tcG9uZW50LmFkZGVkKSB7XG4gICAgICAgIG9sZFBvcyArPSBjb21wb25lbnQuY291bnQ7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbXBvbmVudC52YWx1ZSA9IGRpZmYuam9pbihvbGRTdHJpbmcuc2xpY2Uob2xkUG9zLCBvbGRQb3MgKyBjb21wb25lbnQuY291bnQpKTtcbiAgICAgIG9sZFBvcyArPSBjb21wb25lbnQuY291bnQ7XG5cbiAgICAgIC8vIFJldmVyc2UgYWRkIGFuZCByZW1vdmUgc28gcmVtb3ZlcyBhcmUgb3V0cHV0IGZpcnN0IHRvIG1hdGNoIGNvbW1vbiBjb252ZW50aW9uXG4gICAgICAvLyBUaGUgZGlmZmluZyBhbGdvcml0aG0gaXMgdGllZCB0byBhZGQgdGhlbiByZW1vdmUgb3V0cHV0IGFuZCB0aGlzIGlzIHRoZSBzaW1wbGVzdFxuICAgICAgLy8gcm91dGUgdG8gZ2V0IHRoZSBkZXNpcmVkIG91dHB1dCB3aXRoIG1pbmltYWwgb3ZlcmhlYWQuXG4gICAgICBpZiAoY29tcG9uZW50UG9zICYmIGNvbXBvbmVudHNbY29tcG9uZW50UG9zIC0gMV0uYWRkZWQpIHtcbiAgICAgICAgbGV0IHRtcCA9IGNvbXBvbmVudHNbY29tcG9uZW50UG9zIC0gMV07XG4gICAgICAgIGNvbXBvbmVudHNbY29tcG9uZW50UG9zIC0gMV0gPSBjb21wb25lbnRzW2NvbXBvbmVudFBvc107XG4gICAgICAgIGNvbXBvbmVudHNbY29tcG9uZW50UG9zXSA9IHRtcDtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBTcGVjaWFsIGNhc2UgaGFuZGxlIGZvciB3aGVuIG9uZSB0ZXJtaW5hbCBpcyBpZ25vcmVkIChpLmUuIHdoaXRlc3BhY2UpLlxuICAvLyBGb3IgdGhpcyBjYXNlIHdlIG1lcmdlIHRoZSB0ZXJtaW5hbCBpbnRvIHRoZSBwcmlvciBzdHJpbmcgYW5kIGRyb3AgdGhlIGNoYW5nZS5cbiAgLy8gVGhpcyBpcyBvbmx5IGF2YWlsYWJsZSBmb3Igc3RyaW5nIG1vZGUuXG4gIGxldCBsYXN0Q29tcG9uZW50ID0gY29tcG9uZW50c1tjb21wb25lbnRMZW4gLSAxXTtcbiAgaWYgKGNvbXBvbmVudExlbiA+IDFcbiAgICAgICYmIHR5cGVvZiBsYXN0Q29tcG9uZW50LnZhbHVlID09PSAnc3RyaW5nJ1xuICAgICAgJiYgKGxhc3RDb21wb25lbnQuYWRkZWQgfHwgbGFzdENvbXBvbmVudC5yZW1vdmVkKVxuICAgICAgJiYgZGlmZi5lcXVhbHMoJycsIGxhc3RDb21wb25lbnQudmFsdWUpKSB7XG4gICAgY29tcG9uZW50c1tjb21wb25lbnRMZW4gLSAyXS52YWx1ZSArPSBsYXN0Q29tcG9uZW50LnZhbHVlO1xuICAgIGNvbXBvbmVudHMucG9wKCk7XG4gIH1cblxuICByZXR1cm4gY29tcG9uZW50cztcbn1cblxuZnVuY3Rpb24gY2xvbmVQYXRoKHBhdGgpIHtcbiAgcmV0dXJuIHsgbmV3UG9zOiBwYXRoLm5ld1BvcywgY29tcG9uZW50czogcGF0aC5jb21wb25lbnRzLnNsaWNlKDApIH07XG59XG4iXX0= + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.characterDiff = undefined; + exports. /*istanbul ignore end*/diffChars = diffChars; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var characterDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/characterDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + function diffChars(oldStr, newStr, options) { + return characterDiff.diff(oldStr, newStr, options); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2NoYXJhY3Rlci5qcyJdLCJuYW1lcyI6WyJkaWZmQ2hhcnMiLCJjaGFyYWN0ZXJEaWZmIiwib2xkU3RyIiwibmV3U3RyIiwib3B0aW9ucyIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBR2dCQSxTLEdBQUFBLFM7O0FBSGhCOzs7Ozs7dUJBRU8sSUFBTUMseUZBQWdCLHdFQUF0QjtBQUNBLFNBQVNELFNBQVQsQ0FBbUJFLE1BQW5CLEVBQTJCQyxNQUEzQixFQUFtQ0MsT0FBbkMsRUFBNEM7QUFBRSxTQUFPSCxjQUFjSSxJQUFkLENBQW1CSCxNQUFuQixFQUEyQkMsTUFBM0IsRUFBbUNDLE9BQW5DLENBQVA7QUFBcUQiLCJmaWxlIjoiY2hhcmFjdGVyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcblxuZXhwb3J0IGNvbnN0IGNoYXJhY3RlckRpZmYgPSBuZXcgRGlmZigpO1xuZXhwb3J0IGZ1bmN0aW9uIGRpZmZDaGFycyhvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykgeyByZXR1cm4gY2hhcmFjdGVyRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTsgfVxuIl19 + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.wordDiff = undefined; + exports. /*istanbul ignore end*/diffWords = diffWords; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = diffWordsWithSpace; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_params = __webpack_require__(4) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/ // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode + // + // Ranges and exceptions: + // Latin-1 Supplement, 0080–00FF + // - U+00D7 × Multiplication sign + // - U+00F7 ÷ Division sign + // Latin Extended-A, 0100–017F + // Latin Extended-B, 0180–024F + // IPA Extensions, 0250–02AF + // Spacing Modifier Letters, 02B0–02FF + // - U+02C7 ˇ ˇ Caron + // - U+02D8 ˘ ˘ Breve + // - U+02D9 Ë™ ˙ Dot Above + // - U+02DA Ëš ˚ Ring Above + // - U+02DB Ë› ˛ Ogonek + // - U+02DC Ëœ ˜ Small Tilde + // - U+02DD Ë� ˝ Double Acute Accent + // Latin Extended Additional, 1E00–1EFF + var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; + + var reWhitespace = /\S/; + + var wordDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/wordDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + wordDiff.equals = function (left, right) { + if (this.options.ignoreCase) { + left = left.toLowerCase(); + right = right.toLowerCase(); + } + return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); + }; + wordDiff.tokenize = function (value) { + var tokens = value.split(/(\s+|\b)/); + + // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. + for (var i = 0; i < tokens.length - 1; i++) { + // If we have an empty string in the next field and we have only word chars before and after, merge + if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { + tokens[i] += tokens[i + 2]; + tokens.splice(i + 1, 2); + i--; + } + } + + return tokens; + }; + + function diffWords(oldStr, newStr, options) { + options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(options, { ignoreWhitespace: true }); + return wordDiff.diff(oldStr, newStr, options); + } + + function diffWordsWithSpace(oldStr, newStr, options) { + return wordDiff.diff(oldStr, newStr, options); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL3dvcmQuanMiXSwibmFtZXMiOlsiZGlmZldvcmRzIiwiZGlmZldvcmRzV2l0aFNwYWNlIiwiZXh0ZW5kZWRXb3JkQ2hhcnMiLCJyZVdoaXRlc3BhY2UiLCJ3b3JkRGlmZiIsImVxdWFscyIsImxlZnQiLCJyaWdodCIsIm9wdGlvbnMiLCJpZ25vcmVDYXNlIiwidG9Mb3dlckNhc2UiLCJpZ25vcmVXaGl0ZXNwYWNlIiwidGVzdCIsInRva2VuaXplIiwidmFsdWUiLCJ0b2tlbnMiLCJzcGxpdCIsImkiLCJsZW5ndGgiLCJzcGxpY2UiLCJvbGRTdHIiLCJuZXdTdHIiLCJkaWZmIl0sIm1hcHBpbmdzIjoiOzs7O2dDQW1EZ0JBLFMsR0FBQUEsUzt5REFLQUMsa0IsR0FBQUEsa0I7O0FBeERoQjs7Ozt1QkFDQTs7Ozt3QkFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFNQyxvQkFBb0IsK0RBQTFCOztBQUVBLElBQU1DLGVBQWUsSUFBckI7O0FBRU8sSUFBTUMsK0VBQVcsd0VBQWpCO0FBQ1BBLFNBQVNDLE1BQVQsR0FBa0IsVUFBU0MsSUFBVCxFQUFlQyxLQUFmLEVBQXNCO0FBQ3RDLE1BQUksS0FBS0MsT0FBTCxDQUFhQyxVQUFqQixFQUE2QjtBQUMzQkgsV0FBT0EsS0FBS0ksV0FBTCxFQUFQO0FBQ0FILFlBQVFBLE1BQU1HLFdBQU4sRUFBUjtBQUNEO0FBQ0QsU0FBT0osU0FBU0MsS0FBVCxJQUFtQixLQUFLQyxPQUFMLENBQWFHLGdCQUFiLElBQWlDLENBQUNSLGFBQWFTLElBQWIsQ0FBa0JOLElBQWxCLENBQWxDLElBQTZELENBQUNILGFBQWFTLElBQWIsQ0FBa0JMLEtBQWxCLENBQXhGO0FBQ0QsQ0FORDtBQU9BSCxTQUFTUyxRQUFULEdBQW9CLFVBQVNDLEtBQVQsRUFBZ0I7QUFDbEMsTUFBSUMsU0FBU0QsTUFBTUUsS0FBTixDQUFZLFVBQVosQ0FBYjs7QUFFQTtBQUNBLE9BQUssSUFBSUMsSUFBSSxDQUFiLEVBQWdCQSxJQUFJRixPQUFPRyxNQUFQLEdBQWdCLENBQXBDLEVBQXVDRCxHQUF2QyxFQUE0QztBQUMxQztBQUNBLFFBQUksQ0FBQ0YsT0FBT0UsSUFBSSxDQUFYLENBQUQsSUFBa0JGLE9BQU9FLElBQUksQ0FBWCxDQUFsQixJQUNLZixrQkFBa0JVLElBQWxCLENBQXVCRyxPQUFPRSxDQUFQLENBQXZCLENBREwsSUFFS2Ysa0JBQWtCVSxJQUFsQixDQUF1QkcsT0FBT0UsSUFBSSxDQUFYLENBQXZCLENBRlQsRUFFZ0Q7QUFDOUNGLGFBQU9FLENBQVAsS0FBYUYsT0FBT0UsSUFBSSxDQUFYLENBQWI7QUFDQUYsYUFBT0ksTUFBUCxDQUFjRixJQUFJLENBQWxCLEVBQXFCLENBQXJCO0FBQ0FBO0FBQ0Q7QUFDRjs7QUFFRCxTQUFPRixNQUFQO0FBQ0QsQ0FoQkQ7O0FBa0JPLFNBQVNmLFNBQVQsQ0FBbUJvQixNQUFuQixFQUEyQkMsTUFBM0IsRUFBbUNiLE9BQW5DLEVBQTRDO0FBQ2pEQSxZQUFVLDhFQUFnQkEsT0FBaEIsRUFBeUIsRUFBQ0csa0JBQWtCLElBQW5CLEVBQXpCLENBQVY7QUFDQSxTQUFPUCxTQUFTa0IsSUFBVCxDQUFjRixNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmIsT0FBOUIsQ0FBUDtBQUNEOztBQUVNLFNBQVNQLGtCQUFULENBQTRCbUIsTUFBNUIsRUFBb0NDLE1BQXBDLEVBQTRDYixPQUE1QyxFQUFxRDtBQUMxRCxTQUFPSixTQUFTa0IsSUFBVCxDQUFjRixNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmIsT0FBOUIsQ0FBUDtBQUNEIiwiZmlsZSI6IndvcmQuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgRGlmZiBmcm9tICcuL2Jhc2UnO1xuaW1wb3J0IHtnZW5lcmF0ZU9wdGlvbnN9IGZyb20gJy4uL3V0aWwvcGFyYW1zJztcblxuLy8gQmFzZWQgb24gaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGF0aW5fc2NyaXB0X2luX1VuaWNvZGVcbi8vXG4vLyBSYW5nZXMgYW5kIGV4Y2VwdGlvbnM6XG4vLyBMYXRpbi0xIFN1cHBsZW1lbnQsIDAwODDigJMwMEZGXG4vLyAgLSBVKzAwRDcgIMOXIE11bHRpcGxpY2F0aW9uIHNpZ25cbi8vICAtIFUrMDBGNyAgw7cgRGl2aXNpb24gc2lnblxuLy8gTGF0aW4gRXh0ZW5kZWQtQSwgMDEwMOKAkzAxN0Zcbi8vIExhdGluIEV4dGVuZGVkLUIsIDAxODDigJMwMjRGXG4vLyBJUEEgRXh0ZW5zaW9ucywgMDI1MOKAkzAyQUZcbi8vIFNwYWNpbmcgTW9kaWZpZXIgTGV0dGVycywgMDJCMOKAkzAyRkZcbi8vICAtIFUrMDJDNyAgy4cgJiM3MTE7ICBDYXJvblxuLy8gIC0gVSswMkQ4ICDLmCAmIzcyODsgIEJyZXZlXG4vLyAgLSBVKzAyRDkgIMuZICYjNzI5OyAgRG90IEFib3ZlXG4vLyAgLSBVKzAyREEgIMuaICYjNzMwOyAgUmluZyBBYm92ZVxuLy8gIC0gVSswMkRCICDLmyAmIzczMTsgIE9nb25la1xuLy8gIC0gVSswMkRDICDLnCAmIzczMjsgIFNtYWxsIFRpbGRlXG4vLyAgLSBVKzAyREQgIMudICYjNzMzOyAgRG91YmxlIEFjdXRlIEFjY2VudFxuLy8gTGF0aW4gRXh0ZW5kZWQgQWRkaXRpb25hbCwgMUUwMOKAkzFFRkZcbmNvbnN0IGV4dGVuZGVkV29yZENoYXJzID0gL15bYS16QS1aXFx1e0MwfS1cXHV7RkZ9XFx1e0Q4fS1cXHV7RjZ9XFx1e0Y4fS1cXHV7MkM2fVxcdXsyQzh9LVxcdXsyRDd9XFx1ezJERX0tXFx1ezJGRn1cXHV7MUUwMH0tXFx1ezFFRkZ9XSskL3U7XG5cbmNvbnN0IHJlV2hpdGVzcGFjZSA9IC9cXFMvO1xuXG5leHBvcnQgY29uc3Qgd29yZERpZmYgPSBuZXcgRGlmZigpO1xud29yZERpZmYuZXF1YWxzID0gZnVuY3Rpb24obGVmdCwgcmlnaHQpIHtcbiAgaWYgKHRoaXMub3B0aW9ucy5pZ25vcmVDYXNlKSB7XG4gICAgbGVmdCA9IGxlZnQudG9Mb3dlckNhc2UoKTtcbiAgICByaWdodCA9IHJpZ2h0LnRvTG93ZXJDYXNlKCk7XG4gIH1cbiAgcmV0dXJuIGxlZnQgPT09IHJpZ2h0IHx8ICh0aGlzLm9wdGlvbnMuaWdub3JlV2hpdGVzcGFjZSAmJiAhcmVXaGl0ZXNwYWNlLnRlc3QobGVmdCkgJiYgIXJlV2hpdGVzcGFjZS50ZXN0KHJpZ2h0KSk7XG59O1xud29yZERpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICBsZXQgdG9rZW5zID0gdmFsdWUuc3BsaXQoLyhcXHMrfFxcYikvKTtcblxuICAvLyBKb2luIHRoZSBib3VuZGFyeSBzcGxpdHMgdGhhdCB3ZSBkbyBub3QgY29uc2lkZXIgdG8gYmUgYm91bmRhcmllcy4gVGhpcyBpcyBwcmltYXJpbHkgdGhlIGV4dGVuZGVkIExhdGluIGNoYXJhY3RlciBzZXQuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdG9rZW5zLmxlbmd0aCAtIDE7IGkrKykge1xuICAgIC8vIElmIHdlIGhhdmUgYW4gZW1wdHkgc3RyaW5nIGluIHRoZSBuZXh0IGZpZWxkIGFuZCB3ZSBoYXZlIG9ubHkgd29yZCBjaGFycyBiZWZvcmUgYW5kIGFmdGVyLCBtZXJnZVxuICAgIGlmICghdG9rZW5zW2kgKyAxXSAmJiB0b2tlbnNbaSArIDJdXG4gICAgICAgICAgJiYgZXh0ZW5kZWRXb3JkQ2hhcnMudGVzdCh0b2tlbnNbaV0pXG4gICAgICAgICAgJiYgZXh0ZW5kZWRXb3JkQ2hhcnMudGVzdCh0b2tlbnNbaSArIDJdKSkge1xuICAgICAgdG9rZW5zW2ldICs9IHRva2Vuc1tpICsgMl07XG4gICAgICB0b2tlbnMuc3BsaWNlKGkgKyAxLCAyKTtcbiAgICAgIGktLTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gdG9rZW5zO1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIGRpZmZXb3JkcyhvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykge1xuICBvcHRpb25zID0gZ2VuZXJhdGVPcHRpb25zKG9wdGlvbnMsIHtpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlfSk7XG4gIHJldHVybiB3b3JkRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRpZmZXb3Jkc1dpdGhTcGFjZShvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykge1xuICByZXR1cm4gd29yZERpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucyk7XG59XG4iXX0= + + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/generateOptions = generateOptions; + function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } + } + return defaults; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL3BhcmFtcy5qcyJdLCJuYW1lcyI6WyJnZW5lcmF0ZU9wdGlvbnMiLCJvcHRpb25zIiwiZGVmYXVsdHMiLCJjYWxsYmFjayIsIm5hbWUiLCJoYXNPd25Qcm9wZXJ0eSJdLCJtYXBwaW5ncyI6Ijs7O2dDQUFnQkEsZSxHQUFBQSxlO0FBQVQsU0FBU0EsZUFBVCxDQUF5QkMsT0FBekIsRUFBa0NDLFFBQWxDLEVBQTRDO0FBQ2pELE1BQUksT0FBT0QsT0FBUCxLQUFtQixVQUF2QixFQUFtQztBQUNqQ0MsYUFBU0MsUUFBVCxHQUFvQkYsT0FBcEI7QUFDRCxHQUZELE1BRU8sSUFBSUEsT0FBSixFQUFhO0FBQ2xCLFNBQUssSUFBSUcsSUFBVCxJQUFpQkgsT0FBakIsRUFBMEI7QUFDeEI7QUFDQSxVQUFJQSxRQUFRSSxjQUFSLENBQXVCRCxJQUF2QixDQUFKLEVBQWtDO0FBQ2hDRixpQkFBU0UsSUFBVCxJQUFpQkgsUUFBUUcsSUFBUixDQUFqQjtBQUNEO0FBQ0Y7QUFDRjtBQUNELFNBQU9GLFFBQVA7QUFDRCIsImZpbGUiOiJwYXJhbXMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVPcHRpb25zKG9wdGlvbnMsIGRlZmF1bHRzKSB7XG4gIGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIGRlZmF1bHRzLmNhbGxiYWNrID0gb3B0aW9ucztcbiAgfSBlbHNlIGlmIChvcHRpb25zKSB7XG4gICAgZm9yIChsZXQgbmFtZSBpbiBvcHRpb25zKSB7XG4gICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICAgICAgaWYgKG9wdGlvbnMuaGFzT3duUHJvcGVydHkobmFtZSkpIHtcbiAgICAgICAgZGVmYXVsdHNbbmFtZV0gPSBvcHRpb25zW25hbWVdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gZGVmYXVsdHM7XG59XG4iXX0= + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.lineDiff = undefined; + exports. /*istanbul ignore end*/diffLines = diffLines; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = diffTrimmedLines; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_params = __webpack_require__(4) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var lineDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/lineDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + lineDiff.tokenize = function (value) { + var retLines = [], + linesAndNewlines = value.split(/(\n|\r\n)/); + + // Ignore the final empty token that occurs if the string ends with a new line + if (!linesAndNewlines[linesAndNewlines.length - 1]) { + linesAndNewlines.pop(); + } + + // Merge the content and line separators into single tokens + for (var i = 0; i < linesAndNewlines.length; i++) { + var line = linesAndNewlines[i]; + + if (i % 2 && !this.options.newlineIsToken) { + retLines[retLines.length - 1] += line; + } else { + if (this.options.ignoreWhitespace) { + line = line.trim(); + } + retLines.push(line); + } + } + + return retLines; + }; + + function diffLines(oldStr, newStr, callback) { + return lineDiff.diff(oldStr, newStr, callback); + } + function diffTrimmedLines(oldStr, newStr, callback) { + var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true }); + return lineDiff.diff(oldStr, newStr, options); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2xpbmUuanMiXSwibmFtZXMiOlsiZGlmZkxpbmVzIiwiZGlmZlRyaW1tZWRMaW5lcyIsImxpbmVEaWZmIiwidG9rZW5pemUiLCJ2YWx1ZSIsInJldExpbmVzIiwibGluZXNBbmROZXdsaW5lcyIsInNwbGl0IiwibGVuZ3RoIiwicG9wIiwiaSIsImxpbmUiLCJvcHRpb25zIiwibmV3bGluZUlzVG9rZW4iLCJpZ25vcmVXaGl0ZXNwYWNlIiwidHJpbSIsInB1c2giLCJvbGRTdHIiLCJuZXdTdHIiLCJjYWxsYmFjayIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBOEJnQkEsUyxHQUFBQSxTO3lEQUNBQyxnQixHQUFBQSxnQjs7QUEvQmhCOzs7O3VCQUNBOzs7O3VCQUVPLElBQU1DLCtFQUFXLHdFQUFqQjtBQUNQQSxTQUFTQyxRQUFULEdBQW9CLFVBQVNDLEtBQVQsRUFBZ0I7QUFDbEMsTUFBSUMsV0FBVyxFQUFmO0FBQUEsTUFDSUMsbUJBQW1CRixNQUFNRyxLQUFOLENBQVksV0FBWixDQUR2Qjs7QUFHQTtBQUNBLE1BQUksQ0FBQ0QsaUJBQWlCQSxpQkFBaUJFLE1BQWpCLEdBQTBCLENBQTNDLENBQUwsRUFBb0Q7QUFDbERGLHFCQUFpQkcsR0FBakI7QUFDRDs7QUFFRDtBQUNBLE9BQUssSUFBSUMsSUFBSSxDQUFiLEVBQWdCQSxJQUFJSixpQkFBaUJFLE1BQXJDLEVBQTZDRSxHQUE3QyxFQUFrRDtBQUNoRCxRQUFJQyxPQUFPTCxpQkFBaUJJLENBQWpCLENBQVg7O0FBRUEsUUFBSUEsSUFBSSxDQUFKLElBQVMsQ0FBQyxLQUFLRSxPQUFMLENBQWFDLGNBQTNCLEVBQTJDO0FBQ3pDUixlQUFTQSxTQUFTRyxNQUFULEdBQWtCLENBQTNCLEtBQWlDRyxJQUFqQztBQUNELEtBRkQsTUFFTztBQUNMLFVBQUksS0FBS0MsT0FBTCxDQUFhRSxnQkFBakIsRUFBbUM7QUFDakNILGVBQU9BLEtBQUtJLElBQUwsRUFBUDtBQUNEO0FBQ0RWLGVBQVNXLElBQVQsQ0FBY0wsSUFBZDtBQUNEO0FBQ0Y7O0FBRUQsU0FBT04sUUFBUDtBQUNELENBeEJEOztBQTBCTyxTQUFTTCxTQUFULENBQW1CaUIsTUFBbkIsRUFBMkJDLE1BQTNCLEVBQW1DQyxRQUFuQyxFQUE2QztBQUFFLFNBQU9qQixTQUFTa0IsSUFBVCxDQUFjSCxNQUFkLEVBQXNCQyxNQUF0QixFQUE4QkMsUUFBOUIsQ0FBUDtBQUFpRDtBQUNoRyxTQUFTbEIsZ0JBQVQsQ0FBMEJnQixNQUExQixFQUFrQ0MsTUFBbEMsRUFBMENDLFFBQTFDLEVBQW9EO0FBQ3pELE1BQUlQLFVBQVUsOEVBQWdCTyxRQUFoQixFQUEwQixFQUFDTCxrQkFBa0IsSUFBbkIsRUFBMUIsQ0FBZDtBQUNBLFNBQU9aLFNBQVNrQixJQUFULENBQWNILE1BQWQsRUFBc0JDLE1BQXRCLEVBQThCTixPQUE5QixDQUFQO0FBQ0QiLCJmaWxlIjoibGluZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5pbXBvcnQge2dlbmVyYXRlT3B0aW9uc30gZnJvbSAnLi4vdXRpbC9wYXJhbXMnO1xuXG5leHBvcnQgY29uc3QgbGluZURpZmYgPSBuZXcgRGlmZigpO1xubGluZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICBsZXQgcmV0TGluZXMgPSBbXSxcbiAgICAgIGxpbmVzQW5kTmV3bGluZXMgPSB2YWx1ZS5zcGxpdCgvKFxcbnxcXHJcXG4pLyk7XG5cbiAgLy8gSWdub3JlIHRoZSBmaW5hbCBlbXB0eSB0b2tlbiB0aGF0IG9jY3VycyBpZiB0aGUgc3RyaW5nIGVuZHMgd2l0aCBhIG5ldyBsaW5lXG4gIGlmICghbGluZXNBbmROZXdsaW5lc1tsaW5lc0FuZE5ld2xpbmVzLmxlbmd0aCAtIDFdKSB7XG4gICAgbGluZXNBbmROZXdsaW5lcy5wb3AoKTtcbiAgfVxuXG4gIC8vIE1lcmdlIHRoZSBjb250ZW50IGFuZCBsaW5lIHNlcGFyYXRvcnMgaW50byBzaW5nbGUgdG9rZW5zXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbGluZXNBbmROZXdsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBsaW5lID0gbGluZXNBbmROZXdsaW5lc1tpXTtcblxuICAgIGlmIChpICUgMiAmJiAhdGhpcy5vcHRpb25zLm5ld2xpbmVJc1Rva2VuKSB7XG4gICAgICByZXRMaW5lc1tyZXRMaW5lcy5sZW5ndGggLSAxXSArPSBsaW5lO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLmlnbm9yZVdoaXRlc3BhY2UpIHtcbiAgICAgICAgbGluZSA9IGxpbmUudHJpbSgpO1xuICAgICAgfVxuICAgICAgcmV0TGluZXMucHVzaChsaW5lKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmV0TGluZXM7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjaykgeyByZXR1cm4gbGluZURpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spOyB9XG5leHBvcnQgZnVuY3Rpb24gZGlmZlRyaW1tZWRMaW5lcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHtcbiAgbGV0IG9wdGlvbnMgPSBnZW5lcmF0ZU9wdGlvbnMoY2FsbGJhY2ssIHtpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlfSk7XG4gIHJldHVybiBsaW5lRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbn1cbiJdfQ== + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.sentenceDiff = undefined; + exports. /*istanbul ignore end*/diffSentences = diffSentences; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var sentenceDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/sentenceDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + sentenceDiff.tokenize = function (value) { + return value.split(/(\S.+?[.!?])(?=\s+|$)/); + }; + + function diffSentences(oldStr, newStr, callback) { + return sentenceDiff.diff(oldStr, newStr, callback); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL3NlbnRlbmNlLmpzIl0sIm5hbWVzIjpbImRpZmZTZW50ZW5jZXMiLCJzZW50ZW5jZURpZmYiLCJ0b2tlbml6ZSIsInZhbHVlIiwic3BsaXQiLCJvbGRTdHIiLCJuZXdTdHIiLCJjYWxsYmFjayIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBUWdCQSxhLEdBQUFBLGE7O0FBUmhCOzs7Ozs7dUJBR08sSUFBTUMsdUZBQWUsd0VBQXJCO0FBQ1BBLGFBQWFDLFFBQWIsR0FBd0IsVUFBU0MsS0FBVCxFQUFnQjtBQUN0QyxTQUFPQSxNQUFNQyxLQUFOLENBQVksdUJBQVosQ0FBUDtBQUNELENBRkQ7O0FBSU8sU0FBU0osYUFBVCxDQUF1QkssTUFBdkIsRUFBK0JDLE1BQS9CLEVBQXVDQyxRQUF2QyxFQUFpRDtBQUFFLFNBQU9OLGFBQWFPLElBQWIsQ0FBa0JILE1BQWxCLEVBQTBCQyxNQUExQixFQUFrQ0MsUUFBbEMsQ0FBUDtBQUFxRCIsImZpbGUiOiJzZW50ZW5jZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cblxuZXhwb3J0IGNvbnN0IHNlbnRlbmNlRGlmZiA9IG5ldyBEaWZmKCk7XG5zZW50ZW5jZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUuc3BsaXQoLyhcXFMuKz9bLiE/XSkoPz1cXHMrfCQpLyk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZlNlbnRlbmNlcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHsgcmV0dXJuIHNlbnRlbmNlRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjayk7IH1cbiJdfQ== + + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.cssDiff = undefined; + exports. /*istanbul ignore end*/diffCss = diffCss; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var cssDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/cssDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + cssDiff.tokenize = function (value) { + return value.split(/([{}:;,]|\s+)/); + }; + + function diffCss(oldStr, newStr, callback) { + return cssDiff.diff(oldStr, newStr, callback); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2Nzcy5qcyJdLCJuYW1lcyI6WyJkaWZmQ3NzIiwiY3NzRGlmZiIsInRva2VuaXplIiwidmFsdWUiLCJzcGxpdCIsIm9sZFN0ciIsIm5ld1N0ciIsImNhbGxiYWNrIiwiZGlmZiJdLCJtYXBwaW5ncyI6Ijs7OztnQ0FPZ0JBLE8sR0FBQUEsTzs7QUFQaEI7Ozs7Ozt1QkFFTyxJQUFNQyw2RUFBVSx3RUFBaEI7QUFDUEEsUUFBUUMsUUFBUixHQUFtQixVQUFTQyxLQUFULEVBQWdCO0FBQ2pDLFNBQU9BLE1BQU1DLEtBQU4sQ0FBWSxlQUFaLENBQVA7QUFDRCxDQUZEOztBQUlPLFNBQVNKLE9BQVQsQ0FBaUJLLE1BQWpCLEVBQXlCQyxNQUF6QixFQUFpQ0MsUUFBakMsRUFBMkM7QUFBRSxTQUFPTixRQUFRTyxJQUFSLENBQWFILE1BQWIsRUFBcUJDLE1BQXJCLEVBQTZCQyxRQUE3QixDQUFQO0FBQWdEIiwiZmlsZSI6ImNzcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cbmV4cG9ydCBjb25zdCBjc3NEaWZmID0gbmV3IERpZmYoKTtcbmNzc0RpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUuc3BsaXQoLyhbe306OyxdfFxccyspLyk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkNzcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHsgcmV0dXJuIGNzc0RpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spOyB9XG4iXX0= + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.jsonDiff = undefined; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports. /*istanbul ignore end*/diffJson = diffJson; + /*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = canonicalize; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var objectPrototypeToString = Object.prototype.toString; + + var jsonDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/jsonDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a + // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: + jsonDiff.useLongestToken = true; + + jsonDiff.tokenize = /*istanbul ignore start*/_line.lineDiff /*istanbul ignore end*/.tokenize; + jsonDiff.castInput = function (value) { + /*istanbul ignore start*/var _options = /*istanbul ignore end*/this.options, + undefinedReplacement = _options.undefinedReplacement, + _options$stringifyRep = _options.stringifyReplacer, + stringifyReplacer = _options$stringifyRep === undefined ? function (k, v) /*istanbul ignore start*/{ + return (/*istanbul ignore end*/typeof v === 'undefined' ? undefinedReplacement : v + ); + } : _options$stringifyRep; + + + return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); + }; + jsonDiff.equals = function (left, right) { + return (/*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')) + ); + }; + + function diffJson(oldObj, newObj, options) { + return jsonDiff.diff(oldObj, newObj, options); + } + + // This function handles the presence of circular references by bailing out when encountering an + // object that is already on the "stack" of items being processed. Accepts an optional replacer + function canonicalize(obj, stack, replacementStack, replacer, key) { + stack = stack || []; + replacementStack = replacementStack || []; + + if (replacer) { + obj = replacer(key, obj); + } + + var i = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + for (i = 0; i < stack.length; i += 1) { + if (stack[i] === obj) { + return replacementStack[i]; + } + } + + var canonicalizedObj = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + if ('[object Array]' === objectPrototypeToString.call(obj)) { + stack.push(obj); + canonicalizedObj = new Array(obj.length); + replacementStack.push(canonicalizedObj); + for (i = 0; i < obj.length; i += 1) { + canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); + } + stack.pop(); + replacementStack.pop(); + return canonicalizedObj; + } + + if (obj && obj.toJSON) { + obj = obj.toJSON(); + } + + if ( /*istanbul ignore start*/(typeof /*istanbul ignore end*/obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + replacementStack.push(canonicalizedObj); + var sortedKeys = [], + _key = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (_key in obj) { + /* istanbul ignore else */ + if (obj.hasOwnProperty(_key)) { + sortedKeys.push(_key); + } + } + sortedKeys.sort(); + for (i = 0; i < sortedKeys.length; i += 1) { + _key = sortedKeys[i]; + canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); + } + stack.pop(); + replacementStack.pop(); + } else { + canonicalizedObj = obj; + } + return canonicalizedObj; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2pzb24uanMiXSwibmFtZXMiOlsiZGlmZkpzb24iLCJjYW5vbmljYWxpemUiLCJvYmplY3RQcm90b3R5cGVUb1N0cmluZyIsIk9iamVjdCIsInByb3RvdHlwZSIsInRvU3RyaW5nIiwianNvbkRpZmYiLCJ1c2VMb25nZXN0VG9rZW4iLCJ0b2tlbml6ZSIsImNhc3RJbnB1dCIsInZhbHVlIiwib3B0aW9ucyIsInVuZGVmaW5lZFJlcGxhY2VtZW50Iiwic3RyaW5naWZ5UmVwbGFjZXIiLCJrIiwidiIsIkpTT04iLCJzdHJpbmdpZnkiLCJlcXVhbHMiLCJsZWZ0IiwicmlnaHQiLCJjYWxsIiwicmVwbGFjZSIsIm9sZE9iaiIsIm5ld09iaiIsImRpZmYiLCJvYmoiLCJzdGFjayIsInJlcGxhY2VtZW50U3RhY2siLCJyZXBsYWNlciIsImtleSIsImkiLCJsZW5ndGgiLCJjYW5vbmljYWxpemVkT2JqIiwicHVzaCIsIkFycmF5IiwicG9wIiwidG9KU09OIiwic29ydGVkS2V5cyIsImhhc093blByb3BlcnR5Iiwic29ydCJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztnQ0FxQmdCQSxRLEdBQUFBLFE7eURBSUFDLFksR0FBQUEsWTs7QUF6QmhCOzs7O3VCQUNBOzs7O3VCQUVBLElBQU1DLDBCQUEwQkMsT0FBT0MsU0FBUCxDQUFpQkMsUUFBakQ7O0FBR08sSUFBTUMsK0VBQVcsd0VBQWpCO0FBQ1A7QUFDQTtBQUNBQSxTQUFTQyxlQUFULEdBQTJCLElBQTNCOztBQUVBRCxTQUFTRSxRQUFULEdBQW9CLGdFQUFTQSxRQUE3QjtBQUNBRixTQUFTRyxTQUFULEdBQXFCLFVBQVNDLEtBQVQsRUFBZ0I7QUFBQSxpRUFDK0UsS0FBS0MsT0FEcEY7QUFBQSxNQUM1QkMsb0JBRDRCLFlBQzVCQSxvQkFENEI7QUFBQSx1Q0FDTkMsaUJBRE07QUFBQSxNQUNOQSxpQkFETSx5Q0FDYyxVQUFDQyxDQUFELEVBQUlDLENBQUo7QUFBQSxtQ0FBVSxPQUFPQSxDQUFQLEtBQWEsV0FBYixHQUEyQkgsb0JBQTNCLEdBQWtERztBQUE1RDtBQUFBLEdBRGQ7OztBQUduQyxTQUFPLE9BQU9MLEtBQVAsS0FBaUIsUUFBakIsR0FBNEJBLEtBQTVCLEdBQW9DTSxLQUFLQyxTQUFMLENBQWVoQixhQUFhUyxLQUFiLEVBQW9CLElBQXBCLEVBQTBCLElBQTFCLEVBQWdDRyxpQkFBaEMsQ0FBZixFQUFtRUEsaUJBQW5FLEVBQXNGLElBQXRGLENBQTNDO0FBQ0QsQ0FKRDtBQUtBUCxTQUFTWSxNQUFULEdBQWtCLFVBQVNDLElBQVQsRUFBZUMsS0FBZixFQUFzQjtBQUN0QyxTQUFPLG9FQUFLaEIsU0FBTCxDQUFlYyxNQUFmLENBQXNCRyxJQUF0QixDQUEyQmYsUUFBM0IsRUFBcUNhLEtBQUtHLE9BQUwsQ0FBYSxZQUFiLEVBQTJCLElBQTNCLENBQXJDLEVBQXVFRixNQUFNRSxPQUFOLENBQWMsWUFBZCxFQUE0QixJQUE1QixDQUF2RTtBQUFQO0FBQ0QsQ0FGRDs7QUFJTyxTQUFTdEIsUUFBVCxDQUFrQnVCLE1BQWxCLEVBQTBCQyxNQUExQixFQUFrQ2IsT0FBbEMsRUFBMkM7QUFBRSxTQUFPTCxTQUFTbUIsSUFBVCxDQUFjRixNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmIsT0FBOUIsQ0FBUDtBQUFnRDs7QUFFcEc7QUFDQTtBQUNPLFNBQVNWLFlBQVQsQ0FBc0J5QixHQUF0QixFQUEyQkMsS0FBM0IsRUFBa0NDLGdCQUFsQyxFQUFvREMsUUFBcEQsRUFBOERDLEdBQTlELEVBQW1FO0FBQ3hFSCxVQUFRQSxTQUFTLEVBQWpCO0FBQ0FDLHFCQUFtQkEsb0JBQW9CLEVBQXZDOztBQUVBLE1BQUlDLFFBQUosRUFBYztBQUNaSCxVQUFNRyxTQUFTQyxHQUFULEVBQWNKLEdBQWQsQ0FBTjtBQUNEOztBQUVELE1BQUlLLG1DQUFKOztBQUVBLE9BQUtBLElBQUksQ0FBVCxFQUFZQSxJQUFJSixNQUFNSyxNQUF0QixFQUE4QkQsS0FBSyxDQUFuQyxFQUFzQztBQUNwQyxRQUFJSixNQUFNSSxDQUFOLE1BQWFMLEdBQWpCLEVBQXNCO0FBQ3BCLGFBQU9FLGlCQUFpQkcsQ0FBakIsQ0FBUDtBQUNEO0FBQ0Y7O0FBRUQsTUFBSUUsa0RBQUo7O0FBRUEsTUFBSSxxQkFBcUIvQix3QkFBd0JtQixJQUF4QixDQUE2QkssR0FBN0IsQ0FBekIsRUFBNEQ7QUFDMURDLFVBQU1PLElBQU4sQ0FBV1IsR0FBWDtBQUNBTyx1QkFBbUIsSUFBSUUsS0FBSixDQUFVVCxJQUFJTSxNQUFkLENBQW5CO0FBQ0FKLHFCQUFpQk0sSUFBakIsQ0FBc0JELGdCQUF0QjtBQUNBLFNBQUtGLElBQUksQ0FBVCxFQUFZQSxJQUFJTCxJQUFJTSxNQUFwQixFQUE0QkQsS0FBSyxDQUFqQyxFQUFvQztBQUNsQ0UsdUJBQWlCRixDQUFqQixJQUFzQjlCLGFBQWF5QixJQUFJSyxDQUFKLENBQWIsRUFBcUJKLEtBQXJCLEVBQTRCQyxnQkFBNUIsRUFBOENDLFFBQTlDLEVBQXdEQyxHQUF4RCxDQUF0QjtBQUNEO0FBQ0RILFVBQU1TLEdBQU47QUFDQVIscUJBQWlCUSxHQUFqQjtBQUNBLFdBQU9ILGdCQUFQO0FBQ0Q7O0FBRUQsTUFBSVAsT0FBT0EsSUFBSVcsTUFBZixFQUF1QjtBQUNyQlgsVUFBTUEsSUFBSVcsTUFBSixFQUFOO0FBQ0Q7O0FBRUQsTUFBSSx5REFBT1gsR0FBUCx5Q0FBT0EsR0FBUCxPQUFlLFFBQWYsSUFBMkJBLFFBQVEsSUFBdkMsRUFBNkM7QUFDM0NDLFVBQU1PLElBQU4sQ0FBV1IsR0FBWDtBQUNBTyx1QkFBbUIsRUFBbkI7QUFDQUwscUJBQWlCTSxJQUFqQixDQUFzQkQsZ0JBQXRCO0FBQ0EsUUFBSUssYUFBYSxFQUFqQjtBQUFBLFFBQ0lSLHNDQURKO0FBRUEsU0FBS0EsSUFBTCxJQUFZSixHQUFaLEVBQWlCO0FBQ2Y7QUFDQSxVQUFJQSxJQUFJYSxjQUFKLENBQW1CVCxJQUFuQixDQUFKLEVBQTZCO0FBQzNCUSxtQkFBV0osSUFBWCxDQUFnQkosSUFBaEI7QUFDRDtBQUNGO0FBQ0RRLGVBQVdFLElBQVg7QUFDQSxTQUFLVCxJQUFJLENBQVQsRUFBWUEsSUFBSU8sV0FBV04sTUFBM0IsRUFBbUNELEtBQUssQ0FBeEMsRUFBMkM7QUFDekNELGFBQU1RLFdBQVdQLENBQVgsQ0FBTjtBQUNBRSx1QkFBaUJILElBQWpCLElBQXdCN0IsYUFBYXlCLElBQUlJLElBQUosQ0FBYixFQUF1QkgsS0FBdkIsRUFBOEJDLGdCQUE5QixFQUFnREMsUUFBaEQsRUFBMERDLElBQTFELENBQXhCO0FBQ0Q7QUFDREgsVUFBTVMsR0FBTjtBQUNBUixxQkFBaUJRLEdBQWpCO0FBQ0QsR0FuQkQsTUFtQk87QUFDTEgsdUJBQW1CUCxHQUFuQjtBQUNEO0FBQ0QsU0FBT08sZ0JBQVA7QUFDRCIsImZpbGUiOiJqc29uLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcbmltcG9ydCB7bGluZURpZmZ9IGZyb20gJy4vbGluZSc7XG5cbmNvbnN0IG9iamVjdFByb3RvdHlwZVRvU3RyaW5nID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZztcblxuXG5leHBvcnQgY29uc3QganNvbkRpZmYgPSBuZXcgRGlmZigpO1xuLy8gRGlzY3JpbWluYXRlIGJldHdlZW4gdHdvIGxpbmVzIG9mIHByZXR0eS1wcmludGVkLCBzZXJpYWxpemVkIEpTT04gd2hlcmUgb25lIG9mIHRoZW0gaGFzIGFcbi8vIGRhbmdsaW5nIGNvbW1hIGFuZCB0aGUgb3RoZXIgZG9lc24ndC4gVHVybnMgb3V0IGluY2x1ZGluZyB0aGUgZGFuZ2xpbmcgY29tbWEgeWllbGRzIHRoZSBuaWNlc3Qgb3V0cHV0OlxuanNvbkRpZmYudXNlTG9uZ2VzdFRva2VuID0gdHJ1ZTtcblxuanNvbkRpZmYudG9rZW5pemUgPSBsaW5lRGlmZi50b2tlbml6ZTtcbmpzb25EaWZmLmNhc3RJbnB1dCA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIGNvbnN0IHt1bmRlZmluZWRSZXBsYWNlbWVudCwgc3RyaW5naWZ5UmVwbGFjZXIgPSAoaywgdikgPT4gdHlwZW9mIHYgPT09ICd1bmRlZmluZWQnID8gdW5kZWZpbmVkUmVwbGFjZW1lbnQgOiB2fSA9IHRoaXMub3B0aW9ucztcblxuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/IHZhbHVlIDogSlNPTi5zdHJpbmdpZnkoY2Fub25pY2FsaXplKHZhbHVlLCBudWxsLCBudWxsLCBzdHJpbmdpZnlSZXBsYWNlciksIHN0cmluZ2lmeVJlcGxhY2VyLCAnICAnKTtcbn07XG5qc29uRGlmZi5lcXVhbHMgPSBmdW5jdGlvbihsZWZ0LCByaWdodCkge1xuICByZXR1cm4gRGlmZi5wcm90b3R5cGUuZXF1YWxzLmNhbGwoanNvbkRpZmYsIGxlZnQucmVwbGFjZSgvLChbXFxyXFxuXSkvZywgJyQxJyksIHJpZ2h0LnJlcGxhY2UoLywoW1xcclxcbl0pL2csICckMScpKTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBkaWZmSnNvbihvbGRPYmosIG5ld09iaiwgb3B0aW9ucykgeyByZXR1cm4ganNvbkRpZmYuZGlmZihvbGRPYmosIG5ld09iaiwgb3B0aW9ucyk7IH1cblxuLy8gVGhpcyBmdW5jdGlvbiBoYW5kbGVzIHRoZSBwcmVzZW5jZSBvZiBjaXJjdWxhciByZWZlcmVuY2VzIGJ5IGJhaWxpbmcgb3V0IHdoZW4gZW5jb3VudGVyaW5nIGFuXG4vLyBvYmplY3QgdGhhdCBpcyBhbHJlYWR5IG9uIHRoZSBcInN0YWNrXCIgb2YgaXRlbXMgYmVpbmcgcHJvY2Vzc2VkLiBBY2NlcHRzIGFuIG9wdGlvbmFsIHJlcGxhY2VyXG5leHBvcnQgZnVuY3Rpb24gY2Fub25pY2FsaXplKG9iaiwgc3RhY2ssIHJlcGxhY2VtZW50U3RhY2ssIHJlcGxhY2VyLCBrZXkpIHtcbiAgc3RhY2sgPSBzdGFjayB8fCBbXTtcbiAgcmVwbGFjZW1lbnRTdGFjayA9IHJlcGxhY2VtZW50U3RhY2sgfHwgW107XG5cbiAgaWYgKHJlcGxhY2VyKSB7XG4gICAgb2JqID0gcmVwbGFjZXIoa2V5LCBvYmopO1xuICB9XG5cbiAgbGV0IGk7XG5cbiAgZm9yIChpID0gMDsgaSA8IHN0YWNrLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgaWYgKHN0YWNrW2ldID09PSBvYmopIHtcbiAgICAgIHJldHVybiByZXBsYWNlbWVudFN0YWNrW2ldO1xuICAgIH1cbiAgfVxuXG4gIGxldCBjYW5vbmljYWxpemVkT2JqO1xuXG4gIGlmICgnW29iamVjdCBBcnJheV0nID09PSBvYmplY3RQcm90b3R5cGVUb1N0cmluZy5jYWxsKG9iaikpIHtcbiAgICBzdGFjay5wdXNoKG9iaik7XG4gICAgY2Fub25pY2FsaXplZE9iaiA9IG5ldyBBcnJheShvYmoubGVuZ3RoKTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnB1c2goY2Fub25pY2FsaXplZE9iaik7XG4gICAgZm9yIChpID0gMDsgaSA8IG9iai5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgY2Fub25pY2FsaXplZE9ialtpXSA9IGNhbm9uaWNhbGl6ZShvYmpbaV0sIHN0YWNrLCByZXBsYWNlbWVudFN0YWNrLCByZXBsYWNlciwga2V5KTtcbiAgICB9XG4gICAgc3RhY2sucG9wKCk7XG4gICAgcmVwbGFjZW1lbnRTdGFjay5wb3AoKTtcbiAgICByZXR1cm4gY2Fub25pY2FsaXplZE9iajtcbiAgfVxuXG4gIGlmIChvYmogJiYgb2JqLnRvSlNPTikge1xuICAgIG9iaiA9IG9iai50b0pTT04oKTtcbiAgfVxuXG4gIGlmICh0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiBvYmogIT09IG51bGwpIHtcbiAgICBzdGFjay5wdXNoKG9iaik7XG4gICAgY2Fub25pY2FsaXplZE9iaiA9IHt9O1xuICAgIHJlcGxhY2VtZW50U3RhY2sucHVzaChjYW5vbmljYWxpemVkT2JqKTtcbiAgICBsZXQgc29ydGVkS2V5cyA9IFtdLFxuICAgICAgICBrZXk7XG4gICAgZm9yIChrZXkgaW4gb2JqKSB7XG4gICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICAgICAgaWYgKG9iai5oYXNPd25Qcm9wZXJ0eShrZXkpKSB7XG4gICAgICAgIHNvcnRlZEtleXMucHVzaChrZXkpO1xuICAgICAgfVxuICAgIH1cbiAgICBzb3J0ZWRLZXlzLnNvcnQoKTtcbiAgICBmb3IgKGkgPSAwOyBpIDwgc29ydGVkS2V5cy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAga2V5ID0gc29ydGVkS2V5c1tpXTtcbiAgICAgIGNhbm9uaWNhbGl6ZWRPYmpba2V5XSA9IGNhbm9uaWNhbGl6ZShvYmpba2V5XSwgc3RhY2ssIHJlcGxhY2VtZW50U3RhY2ssIHJlcGxhY2VyLCBrZXkpO1xuICAgIH1cbiAgICBzdGFjay5wb3AoKTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnBvcCgpO1xuICB9IGVsc2Uge1xuICAgIGNhbm9uaWNhbGl6ZWRPYmogPSBvYmo7XG4gIH1cbiAgcmV0dXJuIGNhbm9uaWNhbGl6ZWRPYmo7XG59XG4iXX0= + + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.arrayDiff = undefined; + exports. /*istanbul ignore end*/diffArrays = diffArrays; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var arrayDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + arrayDiff.tokenize = function (value) { + return value.slice(); + }; + arrayDiff.join = arrayDiff.removeEmpty = function (value) { + return value; + }; + + function diffArrays(oldArr, newArr, callback) { + return arrayDiff.diff(oldArr, newArr, callback); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2FycmF5LmpzIl0sIm5hbWVzIjpbImRpZmZBcnJheXMiLCJhcnJheURpZmYiLCJ0b2tlbml6ZSIsInZhbHVlIiwic2xpY2UiLCJqb2luIiwicmVtb3ZlRW1wdHkiLCJvbGRBcnIiLCJuZXdBcnIiLCJjYWxsYmFjayIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBVWdCQSxVLEdBQUFBLFU7O0FBVmhCOzs7Ozs7dUJBRU8sSUFBTUMsaUZBQVksd0VBQWxCO0FBQ1BBLFVBQVVDLFFBQVYsR0FBcUIsVUFBU0MsS0FBVCxFQUFnQjtBQUNuQyxTQUFPQSxNQUFNQyxLQUFOLEVBQVA7QUFDRCxDQUZEO0FBR0FILFVBQVVJLElBQVYsR0FBaUJKLFVBQVVLLFdBQVYsR0FBd0IsVUFBU0gsS0FBVCxFQUFnQjtBQUN2RCxTQUFPQSxLQUFQO0FBQ0QsQ0FGRDs7QUFJTyxTQUFTSCxVQUFULENBQW9CTyxNQUFwQixFQUE0QkMsTUFBNUIsRUFBb0NDLFFBQXBDLEVBQThDO0FBQUUsU0FBT1IsVUFBVVMsSUFBVixDQUFlSCxNQUFmLEVBQXVCQyxNQUF2QixFQUErQkMsUUFBL0IsQ0FBUDtBQUFrRCIsImZpbGUiOiJhcnJheS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cbmV4cG9ydCBjb25zdCBhcnJheURpZmYgPSBuZXcgRGlmZigpO1xuYXJyYXlEaWZmLnRva2VuaXplID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgcmV0dXJuIHZhbHVlLnNsaWNlKCk7XG59O1xuYXJyYXlEaWZmLmpvaW4gPSBhcnJheURpZmYucmVtb3ZlRW1wdHkgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWU7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkFycmF5cyhvbGRBcnIsIG5ld0FyciwgY2FsbGJhY2spIHsgcmV0dXJuIGFycmF5RGlmZi5kaWZmKG9sZEFyciwgbmV3QXJyLCBjYWxsYmFjayk7IH1cbiJdfQ== + + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/applyPatch = applyPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = applyPatches; + + var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_distanceIterator = __webpack_require__(12) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _distanceIterator2 = _interopRequireDefault(_distanceIterator); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/function applyPatch(source, uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } + + if (Array.isArray(uniDiff)) { + if (uniDiff.length > 1) { + throw new Error('applyPatch only works with a single input.'); + } + + uniDiff = uniDiff[0]; + } + + // Apply the diff to the input + var lines = source.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) /*istanbul ignore start*/{ + return (/*istanbul ignore end*/line === patchContent + ); + }, + errorCount = 0, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0, + offset = 0, + removeEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + addEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + /** + * Checks if the hunk exactly fits on the provided location + */ + function hunkFits(hunk, toPos) { + for (var j = 0; j < hunk.lines.length; j++) { + var line = hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line; + + if (operation === ' ' || operation === '-') { + // Context sanity check + if (!compareLine(toPos + 1, lines[toPos], operation, content)) { + errorCount++; + + if (errorCount > fuzzFactor) { + return false; + } + } + toPos++; + } + } + + return true; + } + + // Search best fit offsets for each hunk based on the previous ones + for (var i = 0; i < hunks.length; i++) { + var hunk = hunks[i], + maxLine = lines.length - hunk.oldLines, + localOffset = 0, + toPos = offset + hunk.oldStart - 1; + + var iterator = /*istanbul ignore start*/(0, _distanceIterator2['default']) /*istanbul ignore end*/(toPos, minLine, maxLine); + + for (; localOffset !== undefined; localOffset = iterator()) { + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + break; + } + } + + if (localOffset === undefined) { + return false; + } + + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; + } + + // Apply patch hunks + var diffOffset = 0; + for (var _i = 0; _i < hunks.length; _i++) { + var _hunk = hunks[_i], + _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; + diffOffset += _hunk.newLines - _hunk.oldLines; + + if (_toPos < 0) { + // Creating a new file + _toPos = 0; + } + + for (var j = 0; j < _hunk.lines.length; j++) { + var line = _hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line, + delimiter = _hunk.linedelimiters[j]; + + if (operation === ' ') { + _toPos++; + } else if (operation === '-') { + lines.splice(_toPos, 1); + delimiters.splice(_toPos, 1); + /* istanbul ignore else */ + } else if (operation === '+') { + lines.splice(_toPos, 0, content); + delimiters.splice(_toPos, 0, delimiter); + _toPos++; + } else if (operation === '\\') { + var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; + if (previousOperation === '+') { + removeEOFNL = true; + } else if (previousOperation === '-') { + addEOFNL = true; + } + } + } + } + + // Handle EOFNL insertion/removal + if (removeEOFNL) { + while (!lines[lines.length - 1]) { + lines.pop(); + delimiters.pop(); + } + } else if (addEOFNL) { + lines.push(''); + delimiters.push('\n'); + } + for (var _k = 0; _k < lines.length - 1; _k++) { + lines[_k] = lines[_k] + delimiters[_k]; + } + return lines.join(''); + } + + // Wrapper that supports multiple file patches via callbacks. + function applyPatches(uniDiff, options) { + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } + + var currentIndex = 0; + function processIndex() { + var index = uniDiff[currentIndex++]; + if (!index) { + return options.complete(); + } + + options.loadFile(index, function (err, data) { + if (err) { + return options.complete(err); + } + + var updatedContent = applyPatch(data, index, options); + options.patched(index, updatedContent, function (err) { + if (err) { + return options.complete(err); + } + + processIndex(); + }); + }); + } + processIndex(); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9hcHBseS5qcyJdLCJuYW1lcyI6WyJhcHBseVBhdGNoIiwiYXBwbHlQYXRjaGVzIiwic291cmNlIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJBcnJheSIsImlzQXJyYXkiLCJsZW5ndGgiLCJFcnJvciIsImxpbmVzIiwic3BsaXQiLCJkZWxpbWl0ZXJzIiwibWF0Y2giLCJodW5rcyIsImNvbXBhcmVMaW5lIiwibGluZU51bWJlciIsImxpbmUiLCJvcGVyYXRpb24iLCJwYXRjaENvbnRlbnQiLCJlcnJvckNvdW50IiwiZnV6ekZhY3RvciIsIm1pbkxpbmUiLCJvZmZzZXQiLCJyZW1vdmVFT0ZOTCIsImFkZEVPRk5MIiwiaHVua0ZpdHMiLCJodW5rIiwidG9Qb3MiLCJqIiwiY29udGVudCIsInN1YnN0ciIsImkiLCJtYXhMaW5lIiwib2xkTGluZXMiLCJsb2NhbE9mZnNldCIsIm9sZFN0YXJ0IiwiaXRlcmF0b3IiLCJ1bmRlZmluZWQiLCJkaWZmT2Zmc2V0IiwibmV3TGluZXMiLCJkZWxpbWl0ZXIiLCJsaW5lZGVsaW1pdGVycyIsInNwbGljZSIsInByZXZpb3VzT3BlcmF0aW9uIiwicG9wIiwicHVzaCIsIl9rIiwiam9pbiIsImN1cnJlbnRJbmRleCIsInByb2Nlc3NJbmRleCIsImluZGV4IiwiY29tcGxldGUiLCJsb2FkRmlsZSIsImVyciIsImRhdGEiLCJ1cGRhdGVkQ29udGVudCIsInBhdGNoZWQiXSwibWFwcGluZ3MiOiI7OztnQ0FHZ0JBLFUsR0FBQUEsVTt5REFvSUFDLFksR0FBQUEsWTs7QUF2SWhCOztBQUNBOzs7Ozs7dUJBRU8sU0FBU0QsVUFBVCxDQUFvQkUsTUFBcEIsRUFBNEJDLE9BQTVCLEVBQW1EO0FBQUEsc0RBQWRDLE9BQWMsdUVBQUosRUFBSTs7QUFDeEQsTUFBSSxPQUFPRCxPQUFQLEtBQW1CLFFBQXZCLEVBQWlDO0FBQy9CQSxjQUFVLHdFQUFXQSxPQUFYLENBQVY7QUFDRDs7QUFFRCxNQUFJRSxNQUFNQyxPQUFOLENBQWNILE9BQWQsQ0FBSixFQUE0QjtBQUMxQixRQUFJQSxRQUFRSSxNQUFSLEdBQWlCLENBQXJCLEVBQXdCO0FBQ3RCLFlBQU0sSUFBSUMsS0FBSixDQUFVLDRDQUFWLENBQU47QUFDRDs7QUFFREwsY0FBVUEsUUFBUSxDQUFSLENBQVY7QUFDRDs7QUFFRDtBQUNBLE1BQUlNLFFBQVFQLE9BQU9RLEtBQVAsQ0FBYSxxQkFBYixDQUFaO0FBQUEsTUFDSUMsYUFBYVQsT0FBT1UsS0FBUCxDQUFhLHNCQUFiLEtBQXdDLEVBRHpEO0FBQUEsTUFFSUMsUUFBUVYsUUFBUVUsS0FGcEI7QUFBQSxNQUlJQyxjQUFjVixRQUFRVSxXQUFSLElBQXdCLFVBQUNDLFVBQUQsRUFBYUMsSUFBYixFQUFtQkMsU0FBbkIsRUFBOEJDLFlBQTlCO0FBQUEsbUNBQStDRixTQUFTRTtBQUF4RDtBQUFBLEdBSjFDO0FBQUEsTUFLSUMsYUFBYSxDQUxqQjtBQUFBLE1BTUlDLGFBQWFoQixRQUFRZ0IsVUFBUixJQUFzQixDQU52QztBQUFBLE1BT0lDLFVBQVUsQ0FQZDtBQUFBLE1BUUlDLFNBQVMsQ0FSYjtBQUFBLE1BVUlDLDZDQVZKO0FBQUEsTUFXSUMsMENBWEo7O0FBYUE7OztBQUdBLFdBQVNDLFFBQVQsQ0FBa0JDLElBQWxCLEVBQXdCQyxLQUF4QixFQUErQjtBQUM3QixTQUFLLElBQUlDLElBQUksQ0FBYixFQUFnQkEsSUFBSUYsS0FBS2pCLEtBQUwsQ0FBV0YsTUFBL0IsRUFBdUNxQixHQUF2QyxFQUE0QztBQUMxQyxVQUFJWixPQUFPVSxLQUFLakIsS0FBTCxDQUFXbUIsQ0FBWCxDQUFYO0FBQUEsVUFDSVgsWUFBYUQsS0FBS1QsTUFBTCxHQUFjLENBQWQsR0FBa0JTLEtBQUssQ0FBTCxDQUFsQixHQUE0QixHQUQ3QztBQUFBLFVBRUlhLFVBQVdiLEtBQUtULE1BQUwsR0FBYyxDQUFkLEdBQWtCUyxLQUFLYyxNQUFMLENBQVksQ0FBWixDQUFsQixHQUFtQ2QsSUFGbEQ7O0FBSUEsVUFBSUMsY0FBYyxHQUFkLElBQXFCQSxjQUFjLEdBQXZDLEVBQTRDO0FBQzFDO0FBQ0EsWUFBSSxDQUFDSCxZQUFZYSxRQUFRLENBQXBCLEVBQXVCbEIsTUFBTWtCLEtBQU4sQ0FBdkIsRUFBcUNWLFNBQXJDLEVBQWdEWSxPQUFoRCxDQUFMLEVBQStEO0FBQzdEVjs7QUFFQSxjQUFJQSxhQUFhQyxVQUFqQixFQUE2QjtBQUMzQixtQkFBTyxLQUFQO0FBQ0Q7QUFDRjtBQUNETztBQUNEO0FBQ0Y7O0FBRUQsV0FBTyxJQUFQO0FBQ0Q7O0FBRUQ7QUFDQSxPQUFLLElBQUlJLElBQUksQ0FBYixFQUFnQkEsSUFBSWxCLE1BQU1OLE1BQTFCLEVBQWtDd0IsR0FBbEMsRUFBdUM7QUFDckMsUUFBSUwsT0FBT2IsTUFBTWtCLENBQU4sQ0FBWDtBQUFBLFFBQ0lDLFVBQVV2QixNQUFNRixNQUFOLEdBQWVtQixLQUFLTyxRQURsQztBQUFBLFFBRUlDLGNBQWMsQ0FGbEI7QUFBQSxRQUdJUCxRQUFRTCxTQUFTSSxLQUFLUyxRQUFkLEdBQXlCLENBSHJDOztBQUtBLFFBQUlDLFdBQVcsb0ZBQWlCVCxLQUFqQixFQUF3Qk4sT0FBeEIsRUFBaUNXLE9BQWpDLENBQWY7O0FBRUEsV0FBT0UsZ0JBQWdCRyxTQUF2QixFQUFrQ0gsY0FBY0UsVUFBaEQsRUFBNEQ7QUFDMUQsVUFBSVgsU0FBU0MsSUFBVCxFQUFlQyxRQUFRTyxXQUF2QixDQUFKLEVBQXlDO0FBQ3ZDUixhQUFLSixNQUFMLEdBQWNBLFVBQVVZLFdBQXhCO0FBQ0E7QUFDRDtBQUNGOztBQUVELFFBQUlBLGdCQUFnQkcsU0FBcEIsRUFBK0I7QUFDN0IsYUFBTyxLQUFQO0FBQ0Q7O0FBRUQ7QUFDQTtBQUNBaEIsY0FBVUssS0FBS0osTUFBTCxHQUFjSSxLQUFLUyxRQUFuQixHQUE4QlQsS0FBS08sUUFBN0M7QUFDRDs7QUFFRDtBQUNBLE1BQUlLLGFBQWEsQ0FBakI7QUFDQSxPQUFLLElBQUlQLEtBQUksQ0FBYixFQUFnQkEsS0FBSWxCLE1BQU1OLE1BQTFCLEVBQWtDd0IsSUFBbEMsRUFBdUM7QUFDckMsUUFBSUwsUUFBT2IsTUFBTWtCLEVBQU4sQ0FBWDtBQUFBLFFBQ0lKLFNBQVFELE1BQUtTLFFBQUwsR0FBZ0JULE1BQUtKLE1BQXJCLEdBQThCZ0IsVUFBOUIsR0FBMkMsQ0FEdkQ7QUFFQUEsa0JBQWNaLE1BQUthLFFBQUwsR0FBZ0JiLE1BQUtPLFFBQW5DOztBQUVBLFFBQUlOLFNBQVEsQ0FBWixFQUFlO0FBQUU7QUFDZkEsZUFBUSxDQUFSO0FBQ0Q7O0FBRUQsU0FBSyxJQUFJQyxJQUFJLENBQWIsRUFBZ0JBLElBQUlGLE1BQUtqQixLQUFMLENBQVdGLE1BQS9CLEVBQXVDcUIsR0FBdkMsRUFBNEM7QUFDMUMsVUFBSVosT0FBT1UsTUFBS2pCLEtBQUwsQ0FBV21CLENBQVgsQ0FBWDtBQUFBLFVBQ0lYLFlBQWFELEtBQUtULE1BQUwsR0FBYyxDQUFkLEdBQWtCUyxLQUFLLENBQUwsQ0FBbEIsR0FBNEIsR0FEN0M7QUFBQSxVQUVJYSxVQUFXYixLQUFLVCxNQUFMLEdBQWMsQ0FBZCxHQUFrQlMsS0FBS2MsTUFBTCxDQUFZLENBQVosQ0FBbEIsR0FBbUNkLElBRmxEO0FBQUEsVUFHSXdCLFlBQVlkLE1BQUtlLGNBQUwsQ0FBb0JiLENBQXBCLENBSGhCOztBQUtBLFVBQUlYLGNBQWMsR0FBbEIsRUFBdUI7QUFDckJVO0FBQ0QsT0FGRCxNQUVPLElBQUlWLGNBQWMsR0FBbEIsRUFBdUI7QUFDNUJSLGNBQU1pQyxNQUFOLENBQWFmLE1BQWIsRUFBb0IsQ0FBcEI7QUFDQWhCLG1CQUFXK0IsTUFBWCxDQUFrQmYsTUFBbEIsRUFBeUIsQ0FBekI7QUFDRjtBQUNDLE9BSk0sTUFJQSxJQUFJVixjQUFjLEdBQWxCLEVBQXVCO0FBQzVCUixjQUFNaUMsTUFBTixDQUFhZixNQUFiLEVBQW9CLENBQXBCLEVBQXVCRSxPQUF2QjtBQUNBbEIsbUJBQVcrQixNQUFYLENBQWtCZixNQUFsQixFQUF5QixDQUF6QixFQUE0QmEsU0FBNUI7QUFDQWI7QUFDRCxPQUpNLE1BSUEsSUFBSVYsY0FBYyxJQUFsQixFQUF3QjtBQUM3QixZQUFJMEIsb0JBQW9CakIsTUFBS2pCLEtBQUwsQ0FBV21CLElBQUksQ0FBZixJQUFvQkYsTUFBS2pCLEtBQUwsQ0FBV21CLElBQUksQ0FBZixFQUFrQixDQUFsQixDQUFwQixHQUEyQyxJQUFuRTtBQUNBLFlBQUllLHNCQUFzQixHQUExQixFQUErQjtBQUM3QnBCLHdCQUFjLElBQWQ7QUFDRCxTQUZELE1BRU8sSUFBSW9CLHNCQUFzQixHQUExQixFQUErQjtBQUNwQ25CLHFCQUFXLElBQVg7QUFDRDtBQUNGO0FBQ0Y7QUFDRjs7QUFFRDtBQUNBLE1BQUlELFdBQUosRUFBaUI7QUFDZixXQUFPLENBQUNkLE1BQU1BLE1BQU1GLE1BQU4sR0FBZSxDQUFyQixDQUFSLEVBQWlDO0FBQy9CRSxZQUFNbUMsR0FBTjtBQUNBakMsaUJBQVdpQyxHQUFYO0FBQ0Q7QUFDRixHQUxELE1BS08sSUFBSXBCLFFBQUosRUFBYztBQUNuQmYsVUFBTW9DLElBQU4sQ0FBVyxFQUFYO0FBQ0FsQyxlQUFXa0MsSUFBWCxDQUFnQixJQUFoQjtBQUNEO0FBQ0QsT0FBSyxJQUFJQyxLQUFLLENBQWQsRUFBaUJBLEtBQUtyQyxNQUFNRixNQUFOLEdBQWUsQ0FBckMsRUFBd0N1QyxJQUF4QyxFQUE4QztBQUM1Q3JDLFVBQU1xQyxFQUFOLElBQVlyQyxNQUFNcUMsRUFBTixJQUFZbkMsV0FBV21DLEVBQVgsQ0FBeEI7QUFDRDtBQUNELFNBQU9yQyxNQUFNc0MsSUFBTixDQUFXLEVBQVgsQ0FBUDtBQUNEOztBQUVEO0FBQ08sU0FBUzlDLFlBQVQsQ0FBc0JFLE9BQXRCLEVBQStCQyxPQUEvQixFQUF3QztBQUM3QyxNQUFJLE9BQU9ELE9BQVAsS0FBbUIsUUFBdkIsRUFBaUM7QUFDL0JBLGNBQVUsd0VBQVdBLE9BQVgsQ0FBVjtBQUNEOztBQUVELE1BQUk2QyxlQUFlLENBQW5CO0FBQ0EsV0FBU0MsWUFBVCxHQUF3QjtBQUN0QixRQUFJQyxRQUFRL0MsUUFBUTZDLGNBQVIsQ0FBWjtBQUNBLFFBQUksQ0FBQ0UsS0FBTCxFQUFZO0FBQ1YsYUFBTzlDLFFBQVErQyxRQUFSLEVBQVA7QUFDRDs7QUFFRC9DLFlBQVFnRCxRQUFSLENBQWlCRixLQUFqQixFQUF3QixVQUFTRyxHQUFULEVBQWNDLElBQWQsRUFBb0I7QUFDMUMsVUFBSUQsR0FBSixFQUFTO0FBQ1AsZUFBT2pELFFBQVErQyxRQUFSLENBQWlCRSxHQUFqQixDQUFQO0FBQ0Q7O0FBRUQsVUFBSUUsaUJBQWlCdkQsV0FBV3NELElBQVgsRUFBaUJKLEtBQWpCLEVBQXdCOUMsT0FBeEIsQ0FBckI7QUFDQUEsY0FBUW9ELE9BQVIsQ0FBZ0JOLEtBQWhCLEVBQXVCSyxjQUF2QixFQUF1QyxVQUFTRixHQUFULEVBQWM7QUFDbkQsWUFBSUEsR0FBSixFQUFTO0FBQ1AsaUJBQU9qRCxRQUFRK0MsUUFBUixDQUFpQkUsR0FBakIsQ0FBUDtBQUNEOztBQUVESjtBQUNELE9BTkQ7QUFPRCxLQWJEO0FBY0Q7QUFDREE7QUFDRCIsImZpbGUiOiJhcHBseS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7cGFyc2VQYXRjaH0gZnJvbSAnLi9wYXJzZSc7XG5pbXBvcnQgZGlzdGFuY2VJdGVyYXRvciBmcm9tICcuLi91dGlsL2Rpc3RhbmNlLWl0ZXJhdG9yJztcblxuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5UGF0Y2goc291cmNlLCB1bmlEaWZmLCBvcHRpb25zID0ge30pIHtcbiAgaWYgKHR5cGVvZiB1bmlEaWZmID09PSAnc3RyaW5nJykge1xuICAgIHVuaURpZmYgPSBwYXJzZVBhdGNoKHVuaURpZmYpO1xuICB9XG5cbiAgaWYgKEFycmF5LmlzQXJyYXkodW5pRGlmZikpIHtcbiAgICBpZiAodW5pRGlmZi5sZW5ndGggPiAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2FwcGx5UGF0Y2ggb25seSB3b3JrcyB3aXRoIGEgc2luZ2xlIGlucHV0LicpO1xuICAgIH1cblxuICAgIHVuaURpZmYgPSB1bmlEaWZmWzBdO1xuICB9XG5cbiAgLy8gQXBwbHkgdGhlIGRpZmYgdG8gdGhlIGlucHV0XG4gIGxldCBsaW5lcyA9IHNvdXJjZS5zcGxpdCgvXFxyXFxufFtcXG5cXHZcXGZcXHJcXHg4NV0vKSxcbiAgICAgIGRlbGltaXRlcnMgPSBzb3VyY2UubWF0Y2goL1xcclxcbnxbXFxuXFx2XFxmXFxyXFx4ODVdL2cpIHx8IFtdLFxuICAgICAgaHVua3MgPSB1bmlEaWZmLmh1bmtzLFxuXG4gICAgICBjb21wYXJlTGluZSA9IG9wdGlvbnMuY29tcGFyZUxpbmUgfHwgKChsaW5lTnVtYmVyLCBsaW5lLCBvcGVyYXRpb24sIHBhdGNoQ29udGVudCkgPT4gbGluZSA9PT0gcGF0Y2hDb250ZW50KSxcbiAgICAgIGVycm9yQ291bnQgPSAwLFxuICAgICAgZnV6ekZhY3RvciA9IG9wdGlvbnMuZnV6ekZhY3RvciB8fCAwLFxuICAgICAgbWluTGluZSA9IDAsXG4gICAgICBvZmZzZXQgPSAwLFxuXG4gICAgICByZW1vdmVFT0ZOTCxcbiAgICAgIGFkZEVPRk5MO1xuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIGh1bmsgZXhhY3RseSBmaXRzIG9uIHRoZSBwcm92aWRlZCBsb2NhdGlvblxuICAgKi9cbiAgZnVuY3Rpb24gaHVua0ZpdHMoaHVuaywgdG9Qb3MpIHtcbiAgICBmb3IgKGxldCBqID0gMDsgaiA8IGh1bmsubGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgIGxldCBsaW5lID0gaHVuay5saW5lc1tqXSxcbiAgICAgICAgICBvcGVyYXRpb24gPSAobGluZS5sZW5ndGggPiAwID8gbGluZVswXSA6ICcgJyksXG4gICAgICAgICAgY29udGVudCA9IChsaW5lLmxlbmd0aCA+IDAgPyBsaW5lLnN1YnN0cigxKSA6IGxpbmUpO1xuXG4gICAgICBpZiAob3BlcmF0aW9uID09PSAnICcgfHwgb3BlcmF0aW9uID09PSAnLScpIHtcbiAgICAgICAgLy8gQ29udGV4dCBzYW5pdHkgY2hlY2tcbiAgICAgICAgaWYgKCFjb21wYXJlTGluZSh0b1BvcyArIDEsIGxpbmVzW3RvUG9zXSwgb3BlcmF0aW9uLCBjb250ZW50KSkge1xuICAgICAgICAgIGVycm9yQ291bnQrKztcblxuICAgICAgICAgIGlmIChlcnJvckNvdW50ID4gZnV6ekZhY3Rvcikge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0b1BvcysrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLy8gU2VhcmNoIGJlc3QgZml0IG9mZnNldHMgZm9yIGVhY2ggaHVuayBiYXNlZCBvbiB0aGUgcHJldmlvdXMgb25lc1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGh1bmtzLmxlbmd0aDsgaSsrKSB7XG4gICAgbGV0IGh1bmsgPSBodW5rc1tpXSxcbiAgICAgICAgbWF4TGluZSA9IGxpbmVzLmxlbmd0aCAtIGh1bmsub2xkTGluZXMsXG4gICAgICAgIGxvY2FsT2Zmc2V0ID0gMCxcbiAgICAgICAgdG9Qb3MgPSBvZmZzZXQgKyBodW5rLm9sZFN0YXJ0IC0gMTtcblxuICAgIGxldCBpdGVyYXRvciA9IGRpc3RhbmNlSXRlcmF0b3IodG9Qb3MsIG1pbkxpbmUsIG1heExpbmUpO1xuXG4gICAgZm9yICg7IGxvY2FsT2Zmc2V0ICE9PSB1bmRlZmluZWQ7IGxvY2FsT2Zmc2V0ID0gaXRlcmF0b3IoKSkge1xuICAgICAgaWYgKGh1bmtGaXRzKGh1bmssIHRvUG9zICsgbG9jYWxPZmZzZXQpKSB7XG4gICAgICAgIGh1bmsub2Zmc2V0ID0gb2Zmc2V0ICs9IGxvY2FsT2Zmc2V0O1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAobG9jYWxPZmZzZXQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIFNldCBsb3dlciB0ZXh0IGxpbWl0IHRvIGVuZCBvZiB0aGUgY3VycmVudCBodW5rLCBzbyBuZXh0IG9uZXMgZG9uJ3QgdHJ5XG4gICAgLy8gdG8gZml0IG92ZXIgYWxyZWFkeSBwYXRjaGVkIHRleHRcbiAgICBtaW5MaW5lID0gaHVuay5vZmZzZXQgKyBodW5rLm9sZFN0YXJ0ICsgaHVuay5vbGRMaW5lcztcbiAgfVxuXG4gIC8vIEFwcGx5IHBhdGNoIGh1bmtzXG4gIGxldCBkaWZmT2Zmc2V0ID0gMDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBodW5rcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBodW5rID0gaHVua3NbaV0sXG4gICAgICAgIHRvUG9zID0gaHVuay5vbGRTdGFydCArIGh1bmsub2Zmc2V0ICsgZGlmZk9mZnNldCAtIDE7XG4gICAgZGlmZk9mZnNldCArPSBodW5rLm5ld0xpbmVzIC0gaHVuay5vbGRMaW5lcztcblxuICAgIGlmICh0b1BvcyA8IDApIHsgLy8gQ3JlYXRpbmcgYSBuZXcgZmlsZVxuICAgICAgdG9Qb3MgPSAwO1xuICAgIH1cblxuICAgIGZvciAobGV0IGogPSAwOyBqIDwgaHVuay5saW5lcy5sZW5ndGg7IGorKykge1xuICAgICAgbGV0IGxpbmUgPSBodW5rLmxpbmVzW2pdLFxuICAgICAgICAgIG9wZXJhdGlvbiA9IChsaW5lLmxlbmd0aCA+IDAgPyBsaW5lWzBdIDogJyAnKSxcbiAgICAgICAgICBjb250ZW50ID0gKGxpbmUubGVuZ3RoID4gMCA/IGxpbmUuc3Vic3RyKDEpIDogbGluZSksXG4gICAgICAgICAgZGVsaW1pdGVyID0gaHVuay5saW5lZGVsaW1pdGVyc1tqXTtcblxuICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJyAnKSB7XG4gICAgICAgIHRvUG9zKys7XG4gICAgICB9IGVsc2UgaWYgKG9wZXJhdGlvbiA9PT0gJy0nKSB7XG4gICAgICAgIGxpbmVzLnNwbGljZSh0b1BvcywgMSk7XG4gICAgICAgIGRlbGltaXRlcnMuc3BsaWNlKHRvUG9zLCAxKTtcbiAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBlbHNlICovXG4gICAgICB9IGVsc2UgaWYgKG9wZXJhdGlvbiA9PT0gJysnKSB7XG4gICAgICAgIGxpbmVzLnNwbGljZSh0b1BvcywgMCwgY29udGVudCk7XG4gICAgICAgIGRlbGltaXRlcnMuc3BsaWNlKHRvUG9zLCAwLCBkZWxpbWl0ZXIpO1xuICAgICAgICB0b1BvcysrO1xuICAgICAgfSBlbHNlIGlmIChvcGVyYXRpb24gPT09ICdcXFxcJykge1xuICAgICAgICBsZXQgcHJldmlvdXNPcGVyYXRpb24gPSBodW5rLmxpbmVzW2ogLSAxXSA/IGh1bmsubGluZXNbaiAtIDFdWzBdIDogbnVsbDtcbiAgICAgICAgaWYgKHByZXZpb3VzT3BlcmF0aW9uID09PSAnKycpIHtcbiAgICAgICAgICByZW1vdmVFT0ZOTCA9IHRydWU7XG4gICAgICAgIH0gZWxzZSBpZiAocHJldmlvdXNPcGVyYXRpb24gPT09ICctJykge1xuICAgICAgICAgIGFkZEVPRk5MID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIEhhbmRsZSBFT0ZOTCBpbnNlcnRpb24vcmVtb3ZhbFxuICBpZiAocmVtb3ZlRU9GTkwpIHtcbiAgICB3aGlsZSAoIWxpbmVzW2xpbmVzLmxlbmd0aCAtIDFdKSB7XG4gICAgICBsaW5lcy5wb3AoKTtcbiAgICAgIGRlbGltaXRlcnMucG9wKCk7XG4gICAgfVxuICB9IGVsc2UgaWYgKGFkZEVPRk5MKSB7XG4gICAgbGluZXMucHVzaCgnJyk7XG4gICAgZGVsaW1pdGVycy5wdXNoKCdcXG4nKTtcbiAgfVxuICBmb3IgKGxldCBfayA9IDA7IF9rIDwgbGluZXMubGVuZ3RoIC0gMTsgX2srKykge1xuICAgIGxpbmVzW19rXSA9IGxpbmVzW19rXSArIGRlbGltaXRlcnNbX2tdO1xuICB9XG4gIHJldHVybiBsaW5lcy5qb2luKCcnKTtcbn1cblxuLy8gV3JhcHBlciB0aGF0IHN1cHBvcnRzIG11bHRpcGxlIGZpbGUgcGF0Y2hlcyB2aWEgY2FsbGJhY2tzLlxuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5UGF0Y2hlcyh1bmlEaWZmLCBvcHRpb25zKSB7XG4gIGlmICh0eXBlb2YgdW5pRGlmZiA9PT0gJ3N0cmluZycpIHtcbiAgICB1bmlEaWZmID0gcGFyc2VQYXRjaCh1bmlEaWZmKTtcbiAgfVxuXG4gIGxldCBjdXJyZW50SW5kZXggPSAwO1xuICBmdW5jdGlvbiBwcm9jZXNzSW5kZXgoKSB7XG4gICAgbGV0IGluZGV4ID0gdW5pRGlmZltjdXJyZW50SW5kZXgrK107XG4gICAgaWYgKCFpbmRleCkge1xuICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICBvcHRpb25zLmxvYWRGaWxlKGluZGV4LCBmdW5jdGlvbihlcnIsIGRhdGEpIHtcbiAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGxldGUoZXJyKTtcbiAgICAgIH1cblxuICAgICAgbGV0IHVwZGF0ZWRDb250ZW50ID0gYXBwbHlQYXRjaChkYXRhLCBpbmRleCwgb3B0aW9ucyk7XG4gICAgICBvcHRpb25zLnBhdGNoZWQoaW5kZXgsIHVwZGF0ZWRDb250ZW50LCBmdW5jdGlvbihlcnIpIHtcbiAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgIHJldHVybiBvcHRpb25zLmNvbXBsZXRlKGVycik7XG4gICAgICAgIH1cblxuICAgICAgICBwcm9jZXNzSW5kZXgoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG4gIHByb2Nlc3NJbmRleCgpO1xufVxuIl19 + + +/***/ }), +/* 11 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/parsePatch = parsePatch; + function parsePatch(uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], + list = [], + i = 0; + + function parseIndex() { + var index = {}; + list.push(index); + + // Parse diff metadata + while (i < diffstr.length) { + var line = diffstr[i]; + + // File header found, end parsing diff metadata + if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { + break; + } + + // Diff index + var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); + if (header) { + index.index = header[1]; + } + + i++; + } + + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); + parseFileHeader(index); + + // Parse hunks + index.hunks = []; + + while (i < diffstr.length) { + var _line = diffstr[i]; + + if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + break; + } else if (/^@@/.test(_line)) { + index.hunks.push(parseHunk()); + } else if (_line && options.strict) { + // Ignore unexpected content unless in strict mode + throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); + } else { + i++; + } + } + } + + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. + function parseFileHeader(index) { + var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); + if (fileHeader) { + var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; + var data = fileHeader[2].split('\t', 2); + var fileName = data[0].replace(/\\\\/g, '\\'); + if (/^".*"$/.test(fileName)) { + fileName = fileName.substr(1, fileName.length - 2); + } + index[keyPrefix + 'FileName'] = fileName; + index[keyPrefix + 'Header'] = (data[1] || '').trim(); + + i++; + } + } + + // Parses a hunk + // This assumes that we are at the start of a hunk. + function parseHunk() { + var chunkHeaderIndex = i, + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + + var hunk = { + oldStart: +chunkHeader[1], + oldLines: +chunkHeader[2] || 1, + newStart: +chunkHeader[3], + newLines: +chunkHeader[4] || 1, + lines: [], + linedelimiters: [] + }; + + var addCount = 0, + removeCount = 0; + for (; i < diffstr.length; i++) { + // Lines starting with '---' could be mistaken for the "remove line" operation + // But they could be the header for the next file. Therefore prune such cases out. + if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { + break; + } + var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; + + if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { + hunk.lines.push(diffstr[i]); + hunk.linedelimiters.push(delimiters[i] || '\n'); + + if (operation === '+') { + addCount++; + } else if (operation === '-') { + removeCount++; + } else if (operation === ' ') { + addCount++; + removeCount++; + } + } else { + break; + } + } + + // Handle the empty block count case + if (!addCount && hunk.newLines === 1) { + hunk.newLines = 0; + } + if (!removeCount && hunk.oldLines === 1) { + hunk.oldLines = 0; + } + + // Perform optional sanity checking + if (options.strict) { + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + } + + return hunk; + } + + while (i < diffstr.length) { + parseIndex(); + } + + return list; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9wYXJzZS5qcyJdLCJuYW1lcyI6WyJwYXJzZVBhdGNoIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJkaWZmc3RyIiwic3BsaXQiLCJkZWxpbWl0ZXJzIiwibWF0Y2giLCJsaXN0IiwiaSIsInBhcnNlSW5kZXgiLCJpbmRleCIsInB1c2giLCJsZW5ndGgiLCJsaW5lIiwidGVzdCIsImhlYWRlciIsImV4ZWMiLCJwYXJzZUZpbGVIZWFkZXIiLCJodW5rcyIsInBhcnNlSHVuayIsInN0cmljdCIsIkVycm9yIiwiSlNPTiIsInN0cmluZ2lmeSIsImZpbGVIZWFkZXIiLCJrZXlQcmVmaXgiLCJkYXRhIiwiZmlsZU5hbWUiLCJyZXBsYWNlIiwic3Vic3RyIiwidHJpbSIsImNodW5rSGVhZGVySW5kZXgiLCJjaHVua0hlYWRlckxpbmUiLCJjaHVua0hlYWRlciIsImh1bmsiLCJvbGRTdGFydCIsIm9sZExpbmVzIiwibmV3U3RhcnQiLCJuZXdMaW5lcyIsImxpbmVzIiwibGluZWRlbGltaXRlcnMiLCJhZGRDb3VudCIsInJlbW92ZUNvdW50IiwiaW5kZXhPZiIsIm9wZXJhdGlvbiJdLCJtYXBwaW5ncyI6Ijs7O2dDQUFnQkEsVSxHQUFBQSxVO0FBQVQsU0FBU0EsVUFBVCxDQUFvQkMsT0FBcEIsRUFBMkM7QUFBQSxzREFBZEMsT0FBYyx1RUFBSixFQUFJOztBQUNoRCxNQUFJQyxVQUFVRixRQUFRRyxLQUFSLENBQWMscUJBQWQsQ0FBZDtBQUFBLE1BQ0lDLGFBQWFKLFFBQVFLLEtBQVIsQ0FBYyxzQkFBZCxLQUF5QyxFQUQxRDtBQUFBLE1BRUlDLE9BQU8sRUFGWDtBQUFBLE1BR0lDLElBQUksQ0FIUjs7QUFLQSxXQUFTQyxVQUFULEdBQXNCO0FBQ3BCLFFBQUlDLFFBQVEsRUFBWjtBQUNBSCxTQUFLSSxJQUFMLENBQVVELEtBQVY7O0FBRUE7QUFDQSxXQUFPRixJQUFJTCxRQUFRUyxNQUFuQixFQUEyQjtBQUN6QixVQUFJQyxPQUFPVixRQUFRSyxDQUFSLENBQVg7O0FBRUE7QUFDQSxVQUFJLHdCQUF3Qk0sSUFBeEIsQ0FBNkJELElBQTdCLENBQUosRUFBd0M7QUFDdEM7QUFDRDs7QUFFRDtBQUNBLFVBQUlFLFNBQVUsMENBQUQsQ0FBNkNDLElBQTdDLENBQWtESCxJQUFsRCxDQUFiO0FBQ0EsVUFBSUUsTUFBSixFQUFZO0FBQ1ZMLGNBQU1BLEtBQU4sR0FBY0ssT0FBTyxDQUFQLENBQWQ7QUFDRDs7QUFFRFA7QUFDRDs7QUFFRDtBQUNBO0FBQ0FTLG9CQUFnQlAsS0FBaEI7QUFDQU8sb0JBQWdCUCxLQUFoQjs7QUFFQTtBQUNBQSxVQUFNUSxLQUFOLEdBQWMsRUFBZDs7QUFFQSxXQUFPVixJQUFJTCxRQUFRUyxNQUFuQixFQUEyQjtBQUN6QixVQUFJQyxRQUFPVixRQUFRSyxDQUFSLENBQVg7O0FBRUEsVUFBSSxpQ0FBaUNNLElBQWpDLENBQXNDRCxLQUF0QyxDQUFKLEVBQWlEO0FBQy9DO0FBQ0QsT0FGRCxNQUVPLElBQUksTUFBTUMsSUFBTixDQUFXRCxLQUFYLENBQUosRUFBc0I7QUFDM0JILGNBQU1RLEtBQU4sQ0FBWVAsSUFBWixDQUFpQlEsV0FBakI7QUFDRCxPQUZNLE1BRUEsSUFBSU4sU0FBUVgsUUFBUWtCLE1BQXBCLEVBQTRCO0FBQ2pDO0FBQ0EsY0FBTSxJQUFJQyxLQUFKLENBQVUsbUJBQW1CYixJQUFJLENBQXZCLElBQTRCLEdBQTVCLEdBQWtDYyxLQUFLQyxTQUFMLENBQWVWLEtBQWYsQ0FBNUMsQ0FBTjtBQUNELE9BSE0sTUFHQTtBQUNMTDtBQUNEO0FBQ0Y7QUFDRjs7QUFFRDtBQUNBO0FBQ0EsV0FBU1MsZUFBVCxDQUF5QlAsS0FBekIsRUFBZ0M7QUFDOUIsUUFBTWMsYUFBYyx1QkFBRCxDQUEwQlIsSUFBMUIsQ0FBK0JiLFFBQVFLLENBQVIsQ0FBL0IsQ0FBbkI7QUFDQSxRQUFJZ0IsVUFBSixFQUFnQjtBQUNkLFVBQUlDLFlBQVlELFdBQVcsQ0FBWCxNQUFrQixLQUFsQixHQUEwQixLQUExQixHQUFrQyxLQUFsRDtBQUNBLFVBQU1FLE9BQU9GLFdBQVcsQ0FBWCxFQUFjcEIsS0FBZCxDQUFvQixJQUFwQixFQUEwQixDQUExQixDQUFiO0FBQ0EsVUFBSXVCLFdBQVdELEtBQUssQ0FBTCxFQUFRRSxPQUFSLENBQWdCLE9BQWhCLEVBQXlCLElBQXpCLENBQWY7QUFDQSxVQUFJLFNBQVNkLElBQVQsQ0FBY2EsUUFBZCxDQUFKLEVBQTZCO0FBQzNCQSxtQkFBV0EsU0FBU0UsTUFBVCxDQUFnQixDQUFoQixFQUFtQkYsU0FBU2YsTUFBVCxHQUFrQixDQUFyQyxDQUFYO0FBQ0Q7QUFDREYsWUFBTWUsWUFBWSxVQUFsQixJQUFnQ0UsUUFBaEM7QUFDQWpCLFlBQU1lLFlBQVksUUFBbEIsSUFBOEIsQ0FBQ0MsS0FBSyxDQUFMLEtBQVcsRUFBWixFQUFnQkksSUFBaEIsRUFBOUI7O0FBRUF0QjtBQUNEO0FBQ0Y7O0FBRUQ7QUFDQTtBQUNBLFdBQVNXLFNBQVQsR0FBcUI7QUFDbkIsUUFBSVksbUJBQW1CdkIsQ0FBdkI7QUFBQSxRQUNJd0Isa0JBQWtCN0IsUUFBUUssR0FBUixDQUR0QjtBQUFBLFFBRUl5QixjQUFjRCxnQkFBZ0I1QixLQUFoQixDQUFzQiw0Q0FBdEIsQ0FGbEI7O0FBSUEsUUFBSThCLE9BQU87QUFDVEMsZ0JBQVUsQ0FBQ0YsWUFBWSxDQUFaLENBREY7QUFFVEcsZ0JBQVUsQ0FBQ0gsWUFBWSxDQUFaLENBQUQsSUFBbUIsQ0FGcEI7QUFHVEksZ0JBQVUsQ0FBQ0osWUFBWSxDQUFaLENBSEY7QUFJVEssZ0JBQVUsQ0FBQ0wsWUFBWSxDQUFaLENBQUQsSUFBbUIsQ0FKcEI7QUFLVE0sYUFBTyxFQUxFO0FBTVRDLHNCQUFnQjtBQU5QLEtBQVg7O0FBU0EsUUFBSUMsV0FBVyxDQUFmO0FBQUEsUUFDSUMsY0FBYyxDQURsQjtBQUVBLFdBQU9sQyxJQUFJTCxRQUFRUyxNQUFuQixFQUEyQkosR0FBM0IsRUFBZ0M7QUFDOUI7QUFDQTtBQUNBLFVBQUlMLFFBQVFLLENBQVIsRUFBV21DLE9BQVgsQ0FBbUIsTUFBbkIsTUFBK0IsQ0FBL0IsSUFDTW5DLElBQUksQ0FBSixHQUFRTCxRQUFRUyxNQUR0QixJQUVLVCxRQUFRSyxJQUFJLENBQVosRUFBZW1DLE9BQWYsQ0FBdUIsTUFBdkIsTUFBbUMsQ0FGeEMsSUFHS3hDLFFBQVFLLElBQUksQ0FBWixFQUFlbUMsT0FBZixDQUF1QixJQUF2QixNQUFpQyxDQUgxQyxFQUc2QztBQUN6QztBQUNIO0FBQ0QsVUFBSUMsWUFBYXpDLFFBQVFLLENBQVIsRUFBV0ksTUFBWCxJQUFxQixDQUFyQixJQUEwQkosS0FBTUwsUUFBUVMsTUFBUixHQUFpQixDQUFsRCxHQUF3RCxHQUF4RCxHQUE4RFQsUUFBUUssQ0FBUixFQUFXLENBQVgsQ0FBOUU7O0FBRUEsVUFBSW9DLGNBQWMsR0FBZCxJQUFxQkEsY0FBYyxHQUFuQyxJQUEwQ0EsY0FBYyxHQUF4RCxJQUErREEsY0FBYyxJQUFqRixFQUF1RjtBQUNyRlYsYUFBS0ssS0FBTCxDQUFXNUIsSUFBWCxDQUFnQlIsUUFBUUssQ0FBUixDQUFoQjtBQUNBMEIsYUFBS00sY0FBTCxDQUFvQjdCLElBQXBCLENBQXlCTixXQUFXRyxDQUFYLEtBQWlCLElBQTFDOztBQUVBLFlBQUlvQyxjQUFjLEdBQWxCLEVBQXVCO0FBQ3JCSDtBQUNELFNBRkQsTUFFTyxJQUFJRyxjQUFjLEdBQWxCLEVBQXVCO0FBQzVCRjtBQUNELFNBRk0sTUFFQSxJQUFJRSxjQUFjLEdBQWxCLEVBQXVCO0FBQzVCSDtBQUNBQztBQUNEO0FBQ0YsT0FaRCxNQVlPO0FBQ0w7QUFDRDtBQUNGOztBQUVEO0FBQ0EsUUFBSSxDQUFDRCxRQUFELElBQWFQLEtBQUtJLFFBQUwsS0FBa0IsQ0FBbkMsRUFBc0M7QUFDcENKLFdBQUtJLFFBQUwsR0FBZ0IsQ0FBaEI7QUFDRDtBQUNELFFBQUksQ0FBQ0ksV0FBRCxJQUFnQlIsS0FBS0UsUUFBTCxLQUFrQixDQUF0QyxFQUF5QztBQUN2Q0YsV0FBS0UsUUFBTCxHQUFnQixDQUFoQjtBQUNEOztBQUVEO0FBQ0EsUUFBSWxDLFFBQVFrQixNQUFaLEVBQW9CO0FBQ2xCLFVBQUlxQixhQUFhUCxLQUFLSSxRQUF0QixFQUFnQztBQUM5QixjQUFNLElBQUlqQixLQUFKLENBQVUsc0RBQXNEVSxtQkFBbUIsQ0FBekUsQ0FBVixDQUFOO0FBQ0Q7QUFDRCxVQUFJVyxnQkFBZ0JSLEtBQUtFLFFBQXpCLEVBQW1DO0FBQ2pDLGNBQU0sSUFBSWYsS0FBSixDQUFVLHdEQUF3RFUsbUJBQW1CLENBQTNFLENBQVYsQ0FBTjtBQUNEO0FBQ0Y7O0FBRUQsV0FBT0csSUFBUDtBQUNEOztBQUVELFNBQU8xQixJQUFJTCxRQUFRUyxNQUFuQixFQUEyQjtBQUN6Qkg7QUFDRDs7QUFFRCxTQUFPRixJQUFQO0FBQ0QiLCJmaWxlIjoicGFyc2UuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gcGFyc2VQYXRjaCh1bmlEaWZmLCBvcHRpb25zID0ge30pIHtcbiAgbGV0IGRpZmZzdHIgPSB1bmlEaWZmLnNwbGl0KC9cXHJcXG58W1xcblxcdlxcZlxcclxceDg1XS8pLFxuICAgICAgZGVsaW1pdGVycyA9IHVuaURpZmYubWF0Y2goL1xcclxcbnxbXFxuXFx2XFxmXFxyXFx4ODVdL2cpIHx8IFtdLFxuICAgICAgbGlzdCA9IFtdLFxuICAgICAgaSA9IDA7XG5cbiAgZnVuY3Rpb24gcGFyc2VJbmRleCgpIHtcbiAgICBsZXQgaW5kZXggPSB7fTtcbiAgICBsaXN0LnB1c2goaW5kZXgpO1xuXG4gICAgLy8gUGFyc2UgZGlmZiBtZXRhZGF0YVxuICAgIHdoaWxlIChpIDwgZGlmZnN0ci5sZW5ndGgpIHtcbiAgICAgIGxldCBsaW5lID0gZGlmZnN0cltpXTtcblxuICAgICAgLy8gRmlsZSBoZWFkZXIgZm91bmQsIGVuZCBwYXJzaW5nIGRpZmYgbWV0YWRhdGFcbiAgICAgIGlmICgvXihcXC1cXC1cXC18XFwrXFwrXFwrfEBAKVxccy8udGVzdChsaW5lKSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgLy8gRGlmZiBpbmRleFxuICAgICAgbGV0IGhlYWRlciA9ICgvXig/OkluZGV4OnxkaWZmKD86IC1yIFxcdyspKylcXHMrKC4rPylcXHMqJC8pLmV4ZWMobGluZSk7XG4gICAgICBpZiAoaGVhZGVyKSB7XG4gICAgICAgIGluZGV4LmluZGV4ID0gaGVhZGVyWzFdO1xuICAgICAgfVxuXG4gICAgICBpKys7XG4gICAgfVxuXG4gICAgLy8gUGFyc2UgZmlsZSBoZWFkZXJzIGlmIHRoZXkgYXJlIGRlZmluZWQuIFVuaWZpZWQgZGlmZiByZXF1aXJlcyB0aGVtLCBidXRcbiAgICAvLyB0aGVyZSdzIG5vIHRlY2huaWNhbCBpc3N1ZXMgdG8gaGF2ZSBhbiBpc29sYXRlZCBodW5rIHdpdGhvdXQgZmlsZSBoZWFkZXJcbiAgICBwYXJzZUZpbGVIZWFkZXIoaW5kZXgpO1xuICAgIHBhcnNlRmlsZUhlYWRlcihpbmRleCk7XG5cbiAgICAvLyBQYXJzZSBodW5rc1xuICAgIGluZGV4Lmh1bmtzID0gW107XG5cbiAgICB3aGlsZSAoaSA8IGRpZmZzdHIubGVuZ3RoKSB7XG4gICAgICBsZXQgbGluZSA9IGRpZmZzdHJbaV07XG5cbiAgICAgIGlmICgvXihJbmRleDp8ZGlmZnxcXC1cXC1cXC18XFwrXFwrXFwrKVxccy8udGVzdChsaW5lKSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH0gZWxzZSBpZiAoL15AQC8udGVzdChsaW5lKSkge1xuICAgICAgICBpbmRleC5odW5rcy5wdXNoKHBhcnNlSHVuaygpKTtcbiAgICAgIH0gZWxzZSBpZiAobGluZSAmJiBvcHRpb25zLnN0cmljdCkge1xuICAgICAgICAvLyBJZ25vcmUgdW5leHBlY3RlZCBjb250ZW50IHVubGVzcyBpbiBzdHJpY3QgbW9kZVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vua25vd24gbGluZSAnICsgKGkgKyAxKSArICcgJyArIEpTT04uc3RyaW5naWZ5KGxpbmUpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGkrKztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBQYXJzZXMgdGhlIC0tLSBhbmQgKysrIGhlYWRlcnMsIGlmIG5vbmUgYXJlIGZvdW5kLCBubyBsaW5lc1xuICAvLyBhcmUgY29uc3VtZWQuXG4gIGZ1bmN0aW9uIHBhcnNlRmlsZUhlYWRlcihpbmRleCkge1xuICAgIGNvbnN0IGZpbGVIZWFkZXIgPSAoL14oLS0tfFxcK1xcK1xcKylcXHMrKC4qKSQvKS5leGVjKGRpZmZzdHJbaV0pO1xuICAgIGlmIChmaWxlSGVhZGVyKSB7XG4gICAgICBsZXQga2V5UHJlZml4ID0gZmlsZUhlYWRlclsxXSA9PT0gJy0tLScgPyAnb2xkJyA6ICduZXcnO1xuICAgICAgY29uc3QgZGF0YSA9IGZpbGVIZWFkZXJbMl0uc3BsaXQoJ1xcdCcsIDIpO1xuICAgICAgbGV0IGZpbGVOYW1lID0gZGF0YVswXS5yZXBsYWNlKC9cXFxcXFxcXC9nLCAnXFxcXCcpO1xuICAgICAgaWYgKC9eXCIuKlwiJC8udGVzdChmaWxlTmFtZSkpIHtcbiAgICAgICAgZmlsZU5hbWUgPSBmaWxlTmFtZS5zdWJzdHIoMSwgZmlsZU5hbWUubGVuZ3RoIC0gMik7XG4gICAgICB9XG4gICAgICBpbmRleFtrZXlQcmVmaXggKyAnRmlsZU5hbWUnXSA9IGZpbGVOYW1lO1xuICAgICAgaW5kZXhba2V5UHJlZml4ICsgJ0hlYWRlciddID0gKGRhdGFbMV0gfHwgJycpLnRyaW0oKTtcblxuICAgICAgaSsrO1xuICAgIH1cbiAgfVxuXG4gIC8vIFBhcnNlcyBhIGh1bmtcbiAgLy8gVGhpcyBhc3N1bWVzIHRoYXQgd2UgYXJlIGF0IHRoZSBzdGFydCBvZiBhIGh1bmsuXG4gIGZ1bmN0aW9uIHBhcnNlSHVuaygpIHtcbiAgICBsZXQgY2h1bmtIZWFkZXJJbmRleCA9IGksXG4gICAgICAgIGNodW5rSGVhZGVyTGluZSA9IGRpZmZzdHJbaSsrXSxcbiAgICAgICAgY2h1bmtIZWFkZXIgPSBjaHVua0hlYWRlckxpbmUuc3BsaXQoL0BAIC0oXFxkKykoPzosKFxcZCspKT8gXFwrKFxcZCspKD86LChcXGQrKSk/IEBALyk7XG5cbiAgICBsZXQgaHVuayA9IHtcbiAgICAgIG9sZFN0YXJ0OiArY2h1bmtIZWFkZXJbMV0sXG4gICAgICBvbGRMaW5lczogK2NodW5rSGVhZGVyWzJdIHx8IDEsXG4gICAgICBuZXdTdGFydDogK2NodW5rSGVhZGVyWzNdLFxuICAgICAgbmV3TGluZXM6ICtjaHVua0hlYWRlcls0XSB8fCAxLFxuICAgICAgbGluZXM6IFtdLFxuICAgICAgbGluZWRlbGltaXRlcnM6IFtdXG4gICAgfTtcblxuICAgIGxldCBhZGRDb3VudCA9IDAsXG4gICAgICAgIHJlbW92ZUNvdW50ID0gMDtcbiAgICBmb3IgKDsgaSA8IGRpZmZzdHIubGVuZ3RoOyBpKyspIHtcbiAgICAgIC8vIExpbmVzIHN0YXJ0aW5nIHdpdGggJy0tLScgY291bGQgYmUgbWlzdGFrZW4gZm9yIHRoZSBcInJlbW92ZSBsaW5lXCIgb3BlcmF0aW9uXG4gICAgICAvLyBCdXQgdGhleSBjb3VsZCBiZSB0aGUgaGVhZGVyIGZvciB0aGUgbmV4dCBmaWxlLiBUaGVyZWZvcmUgcHJ1bmUgc3VjaCBjYXNlcyBvdXQuXG4gICAgICBpZiAoZGlmZnN0cltpXS5pbmRleE9mKCctLS0gJykgPT09IDBcbiAgICAgICAgICAgICYmIChpICsgMiA8IGRpZmZzdHIubGVuZ3RoKVxuICAgICAgICAgICAgJiYgZGlmZnN0cltpICsgMV0uaW5kZXhPZignKysrICcpID09PSAwXG4gICAgICAgICAgICAmJiBkaWZmc3RyW2kgKyAyXS5pbmRleE9mKCdAQCcpID09PSAwKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBsZXQgb3BlcmF0aW9uID0gKGRpZmZzdHJbaV0ubGVuZ3RoID09IDAgJiYgaSAhPSAoZGlmZnN0ci5sZW5ndGggLSAxKSkgPyAnICcgOiBkaWZmc3RyW2ldWzBdO1xuXG4gICAgICBpZiAob3BlcmF0aW9uID09PSAnKycgfHwgb3BlcmF0aW9uID09PSAnLScgfHwgb3BlcmF0aW9uID09PSAnICcgfHwgb3BlcmF0aW9uID09PSAnXFxcXCcpIHtcbiAgICAgICAgaHVuay5saW5lcy5wdXNoKGRpZmZzdHJbaV0pO1xuICAgICAgICBodW5rLmxpbmVkZWxpbWl0ZXJzLnB1c2goZGVsaW1pdGVyc1tpXSB8fCAnXFxuJyk7XG5cbiAgICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJysnKSB7XG4gICAgICAgICAgYWRkQ291bnQrKztcbiAgICAgICAgfSBlbHNlIGlmIChvcGVyYXRpb24gPT09ICctJykge1xuICAgICAgICAgIHJlbW92ZUNvdW50Kys7XG4gICAgICAgIH0gZWxzZSBpZiAob3BlcmF0aW9uID09PSAnICcpIHtcbiAgICAgICAgICBhZGRDb3VudCsrO1xuICAgICAgICAgIHJlbW92ZUNvdW50Kys7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEhhbmRsZSB0aGUgZW1wdHkgYmxvY2sgY291bnQgY2FzZVxuICAgIGlmICghYWRkQ291bnQgJiYgaHVuay5uZXdMaW5lcyA9PT0gMSkge1xuICAgICAgaHVuay5uZXdMaW5lcyA9IDA7XG4gICAgfVxuICAgIGlmICghcmVtb3ZlQ291bnQgJiYgaHVuay5vbGRMaW5lcyA9PT0gMSkge1xuICAgICAgaHVuay5vbGRMaW5lcyA9IDA7XG4gICAgfVxuXG4gICAgLy8gUGVyZm9ybSBvcHRpb25hbCBzYW5pdHkgY2hlY2tpbmdcbiAgICBpZiAob3B0aW9ucy5zdHJpY3QpIHtcbiAgICAgIGlmIChhZGRDb3VudCAhPT0gaHVuay5uZXdMaW5lcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0FkZGVkIGxpbmUgY291bnQgZGlkIG5vdCBtYXRjaCBmb3IgaHVuayBhdCBsaW5lICcgKyAoY2h1bmtIZWFkZXJJbmRleCArIDEpKTtcbiAgICAgIH1cbiAgICAgIGlmIChyZW1vdmVDb3VudCAhPT0gaHVuay5vbGRMaW5lcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlbW92ZWQgbGluZSBjb3VudCBkaWQgbm90IG1hdGNoIGZvciBodW5rIGF0IGxpbmUgJyArIChjaHVua0hlYWRlckluZGV4ICsgMSkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBodW5rO1xuICB9XG5cbiAgd2hpbGUgKGkgPCBkaWZmc3RyLmxlbmd0aCkge1xuICAgIHBhcnNlSW5kZXgoKTtcbiAgfVxuXG4gIHJldHVybiBsaXN0O1xufVxuIl19 + + +/***/ }), +/* 12 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/"use strict"; + + exports.__esModule = true; + + exports["default"] = /*istanbul ignore end*/function (start, minLine, maxLine) { + var wantForward = true, + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; + + return function iterator() { + if (wantForward && !forwardExhausted) { + if (backwardExhausted) { + localOffset++; + } else { + wantForward = false; + } + + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) + if (start + localOffset <= maxLine) { + return localOffset; + } + + forwardExhausted = true; + } + + if (!backwardExhausted) { + if (!forwardExhausted) { + wantForward = true; + } + + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location + if (minLine <= start - localOffset) { + return -localOffset++; + } + + backwardExhausted = true; + return iterator(); + } + + // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined + }; + }; + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL2Rpc3RhbmNlLWl0ZXJhdG9yLmpzIl0sIm5hbWVzIjpbInN0YXJ0IiwibWluTGluZSIsIm1heExpbmUiLCJ3YW50Rm9yd2FyZCIsImJhY2t3YXJkRXhoYXVzdGVkIiwiZm9yd2FyZEV4aGF1c3RlZCIsImxvY2FsT2Zmc2V0IiwiaXRlcmF0b3IiXSwibWFwcGluZ3MiOiI7Ozs7NENBR2UsVUFBU0EsS0FBVCxFQUFnQkMsT0FBaEIsRUFBeUJDLE9BQXpCLEVBQWtDO0FBQy9DLE1BQUlDLGNBQWMsSUFBbEI7QUFBQSxNQUNJQyxvQkFBb0IsS0FEeEI7QUFBQSxNQUVJQyxtQkFBbUIsS0FGdkI7QUFBQSxNQUdJQyxjQUFjLENBSGxCOztBQUtBLFNBQU8sU0FBU0MsUUFBVCxHQUFvQjtBQUN6QixRQUFJSixlQUFlLENBQUNFLGdCQUFwQixFQUFzQztBQUNwQyxVQUFJRCxpQkFBSixFQUF1QjtBQUNyQkU7QUFDRCxPQUZELE1BRU87QUFDTEgsc0JBQWMsS0FBZDtBQUNEOztBQUVEO0FBQ0E7QUFDQSxVQUFJSCxRQUFRTSxXQUFSLElBQXVCSixPQUEzQixFQUFvQztBQUNsQyxlQUFPSSxXQUFQO0FBQ0Q7O0FBRURELHlCQUFtQixJQUFuQjtBQUNEOztBQUVELFFBQUksQ0FBQ0QsaUJBQUwsRUFBd0I7QUFDdEIsVUFBSSxDQUFDQyxnQkFBTCxFQUF1QjtBQUNyQkYsc0JBQWMsSUFBZDtBQUNEOztBQUVEO0FBQ0E7QUFDQSxVQUFJRixXQUFXRCxRQUFRTSxXQUF2QixFQUFvQztBQUNsQyxlQUFPLENBQUNBLGFBQVI7QUFDRDs7QUFFREYsMEJBQW9CLElBQXBCO0FBQ0EsYUFBT0csVUFBUDtBQUNEOztBQUVEO0FBQ0E7QUFDRCxHQWxDRDtBQW1DRCxDIiwiZmlsZSI6ImRpc3RhbmNlLWl0ZXJhdG9yLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gSXRlcmF0b3IgdGhhdCB0cmF2ZXJzZXMgaW4gdGhlIHJhbmdlIG9mIFttaW4sIG1heF0sIHN0ZXBwaW5nXG4vLyBieSBkaXN0YW5jZSBmcm9tIGEgZ2l2ZW4gc3RhcnQgcG9zaXRpb24uIEkuZS4gZm9yIFswLCA0XSwgd2l0aFxuLy8gc3RhcnQgb2YgMiwgdGhpcyB3aWxsIGl0ZXJhdGUgMiwgMywgMSwgNCwgMC5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uKHN0YXJ0LCBtaW5MaW5lLCBtYXhMaW5lKSB7XG4gIGxldCB3YW50Rm9yd2FyZCA9IHRydWUsXG4gICAgICBiYWNrd2FyZEV4aGF1c3RlZCA9IGZhbHNlLFxuICAgICAgZm9yd2FyZEV4aGF1c3RlZCA9IGZhbHNlLFxuICAgICAgbG9jYWxPZmZzZXQgPSAxO1xuXG4gIHJldHVybiBmdW5jdGlvbiBpdGVyYXRvcigpIHtcbiAgICBpZiAod2FudEZvcndhcmQgJiYgIWZvcndhcmRFeGhhdXN0ZWQpIHtcbiAgICAgIGlmIChiYWNrd2FyZEV4aGF1c3RlZCkge1xuICAgICAgICBsb2NhbE9mZnNldCsrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgd2FudEZvcndhcmQgPSBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdHJ5aW5nIHRvIGZpdCBiZXlvbmQgdGV4dCBsZW5ndGgsIGFuZCBpZiBub3QsIGNoZWNrIGl0IGZpdHNcbiAgICAgIC8vIGFmdGVyIG9mZnNldCBsb2NhdGlvbiAob3IgZGVzaXJlZCBsb2NhdGlvbiBvbiBmaXJzdCBpdGVyYXRpb24pXG4gICAgICBpZiAoc3RhcnQgKyBsb2NhbE9mZnNldCA8PSBtYXhMaW5lKSB7XG4gICAgICAgIHJldHVybiBsb2NhbE9mZnNldDtcbiAgICAgIH1cblxuICAgICAgZm9yd2FyZEV4aGF1c3RlZCA9IHRydWU7XG4gICAgfVxuXG4gICAgaWYgKCFiYWNrd2FyZEV4aGF1c3RlZCkge1xuICAgICAgaWYgKCFmb3J3YXJkRXhoYXVzdGVkKSB7XG4gICAgICAgIHdhbnRGb3J3YXJkID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdHJ5aW5nIHRvIGZpdCBiZWZvcmUgdGV4dCBiZWdpbm5pbmcsIGFuZCBpZiBub3QsIGNoZWNrIGl0IGZpdHNcbiAgICAgIC8vIGJlZm9yZSBvZmZzZXQgbG9jYXRpb25cbiAgICAgIGlmIChtaW5MaW5lIDw9IHN0YXJ0IC0gbG9jYWxPZmZzZXQpIHtcbiAgICAgICAgcmV0dXJuIC1sb2NhbE9mZnNldCsrO1xuICAgICAgfVxuXG4gICAgICBiYWNrd2FyZEV4aGF1c3RlZCA9IHRydWU7XG4gICAgICByZXR1cm4gaXRlcmF0b3IoKTtcbiAgICB9XG5cbiAgICAvLyBXZSB0cmllZCB0byBmaXQgaHVuayBiZWZvcmUgdGV4dCBiZWdpbm5pbmcgYW5kIGJleW9uZCB0ZXh0IGxlbmd0aCwgdGhlblxuICAgIC8vIGh1bmsgY2FuJ3QgZml0IG9uIHRoZSB0ZXh0LiBSZXR1cm4gdW5kZWZpbmVkXG4gIH07XG59XG4iXX0= + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/calcLineCount = calcLineCount; + /*istanbul ignore start*/exports. /*istanbul ignore end*/merge = merge; + + var /*istanbul ignore start*/_create = __webpack_require__(14) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_array = __webpack_require__(15) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + + /*istanbul ignore end*/function calcLineCount(hunk) { + /*istanbul ignore start*/var _calcOldNewLineCount = /*istanbul ignore end*/calcOldNewLineCount(hunk.lines), + oldLines = _calcOldNewLineCount.oldLines, + newLines = _calcOldNewLineCount.newLines; + + if (oldLines !== undefined) { + hunk.oldLines = oldLines; + } else { + delete hunk.oldLines; + } + + if (newLines !== undefined) { + hunk.newLines = newLines; + } else { + delete hunk.newLines; + } + } + + function merge(mine, theirs, base) { + mine = loadPatch(mine, base); + theirs = loadPatch(theirs, base); + + var ret = {}; + + // For index we just let it pass through as it doesn't have any necessary meaning. + // Leaving sanity checks on this to the API consumer that may know more about the + // meaning in their own context. + if (mine.index || theirs.index) { + ret.index = mine.index || theirs.index; + } + + if (mine.newFileName || theirs.newFileName) { + if (!fileNameChanged(mine)) { + // No header or no change in ours, use theirs (and ours if theirs does not exist) + ret.oldFileName = theirs.oldFileName || mine.oldFileName; + ret.newFileName = theirs.newFileName || mine.newFileName; + ret.oldHeader = theirs.oldHeader || mine.oldHeader; + ret.newHeader = theirs.newHeader || mine.newHeader; + } else if (!fileNameChanged(theirs)) { + // No header or no change in theirs, use ours + ret.oldFileName = mine.oldFileName; + ret.newFileName = mine.newFileName; + ret.oldHeader = mine.oldHeader; + ret.newHeader = mine.newHeader; + } else { + // Both changed... figure it out + ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName); + ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName); + ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader); + ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); + } + } + + ret.hunks = []; + + var mineIndex = 0, + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; + + while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { + var mineCurrent = mine.hunks[mineIndex] || { oldStart: Infinity }, + theirsCurrent = theirs.hunks[theirsIndex] || { oldStart: Infinity }; + + if (hunkBefore(mineCurrent, theirsCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); + mineIndex++; + theirsOffset += mineCurrent.newLines - mineCurrent.oldLines; + } else if (hunkBefore(theirsCurrent, mineCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset)); + theirsIndex++; + mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines; + } else { + // Overlap, merge as best we can + var mergedHunk = { + oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart), + oldLines: 0, + newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset), + newLines: 0, + lines: [] + }; + mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines); + theirsIndex++; + mineIndex++; + + ret.hunks.push(mergedHunk); + } + } + + return ret; + } + + function loadPatch(param, base) { + if (typeof param === 'string') { + if (/^@@/m.test(param) || /^Index:/m.test(param)) { + return (/*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(param)[0] + ); + } + + if (!base) { + throw new Error('Must provide a base reference or pass in a patch'); + } + return (/*istanbul ignore start*/(0, _create.structuredPatch) /*istanbul ignore end*/(undefined, undefined, base, param) + ); + } + + return param; + } + + function fileNameChanged(patch) { + return patch.newFileName && patch.newFileName !== patch.oldFileName; + } + + function selectField(index, mine, theirs) { + if (mine === theirs) { + return mine; + } else { + index.conflict = true; + return { mine: mine, theirs: theirs }; + } + } + + function hunkBefore(test, check) { + return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; + } + + function cloneHunk(hunk, offset) { + return { + oldStart: hunk.oldStart, oldLines: hunk.oldLines, + newStart: hunk.newStart + offset, newLines: hunk.newLines, + lines: hunk.lines + }; + } + + function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { + // This will generally result in a conflicted hunk, but there are cases where the context + // is the only overlap where we can successfully merge the content here. + var mine = { offset: mineOffset, lines: mineLines, index: 0 }, + their = { offset: theirOffset, lines: theirLines, index: 0 }; + + // Handle any leading content + insertLeading(hunk, mine, their); + insertLeading(hunk, their, mine); + + // Now in the overlap content. Scan through and select the best changes from each. + while (mine.index < mine.lines.length && their.index < their.lines.length) { + var mineCurrent = mine.lines[mine.index], + theirCurrent = their.lines[their.index]; + + if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { + // Both modified ... + mutualChange(hunk, mine, their); + } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { + /*istanbul ignore start*/var _hunk$lines; + + /*istanbul ignore end*/ // Mine inserted + /*istanbul ignore start*/(_hunk$lines = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(mine))); + } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { + /*istanbul ignore start*/var _hunk$lines2; + + /*istanbul ignore end*/ // Theirs inserted + /*istanbul ignore start*/(_hunk$lines2 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(their))); + } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { + // Mine removed or edited + removal(hunk, mine, their); + } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') { + // Their removed or edited + removal(hunk, their, mine, true); + } else if (mineCurrent === theirCurrent) { + // Context identity + hunk.lines.push(mineCurrent); + mine.index++; + their.index++; + } else { + // Context mismatch + conflict(hunk, collectChange(mine), collectChange(their)); + } + } + + // Now push anything that may be remaining + insertTrailing(hunk, mine); + insertTrailing(hunk, their); + + calcLineCount(hunk); + } + + function mutualChange(hunk, mine, their) { + var myChanges = collectChange(mine), + theirChanges = collectChange(their); + + if (allRemoves(myChanges) && allRemoves(theirChanges)) { + // Special case for remove changes that are supersets of one another + if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { + /*istanbul ignore start*/var _hunk$lines3; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines3 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); + return; + } else if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { + /*istanbul ignore start*/var _hunk$lines4; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines4 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines4 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges)); + return; + } + } else if ( /*istanbul ignore start*/(0, _array.arrayEqual) /*istanbul ignore end*/(myChanges, theirChanges)) { + /*istanbul ignore start*/var _hunk$lines5; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines5 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines5 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); + return; + } + + conflict(hunk, myChanges, theirChanges); + } + + function removal(hunk, mine, their, swap) { + var myChanges = collectChange(mine), + theirChanges = collectContext(their, myChanges); + if (theirChanges.merged) { + /*istanbul ignore start*/var _hunk$lines6; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines6 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines6 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges.merged)); + } else { + conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); + } + } + + function conflict(hunk, mine, their) { + hunk.conflict = true; + hunk.lines.push({ + conflict: true, + mine: mine, + theirs: their + }); + } + + function insertLeading(hunk, insert, their) { + while (insert.offset < their.offset && insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + insert.offset++; + } + } + function insertTrailing(hunk, insert) { + while (insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + } + } + + function collectChange(state) { + var ret = [], + operation = state.lines[state.index][0]; + while (state.index < state.lines.length) { + var line = state.lines[state.index]; + + // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + if (operation === '-' && line[0] === '+') { + operation = '+'; + } + + if (operation === line[0]) { + ret.push(line); + state.index++; + } else { + break; + } + } + + return ret; + } + function collectContext(state, matchChanges) { + var changes = [], + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; + while (matchIndex < matchChanges.length && state.index < state.lines.length) { + var change = state.lines[state.index], + match = matchChanges[matchIndex]; + + // Once we've hit our add, then we are done + if (match[0] === '+') { + break; + } + + contextChanges = contextChanges || change[0] !== ' '; + + merged.push(match); + matchIndex++; + + // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this + if (change[0] === '+') { + conflicted = true; + + while (change[0] === '+') { + changes.push(change); + change = state.lines[++state.index]; + } + } + + if (match.substr(1) === change.substr(1)) { + changes.push(change); + state.index++; + } else { + conflicted = true; + } + } + + if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { + conflicted = true; + } + + if (conflicted) { + return changes; + } + + while (matchIndex < matchChanges.length) { + merged.push(matchChanges[matchIndex++]); + } + + return { + merged: merged, + changes: changes + }; + } + + function allRemoves(changes) { + return changes.reduce(function (prev, change) { + return prev && change[0] === '-'; + }, true); + } + function skipRemoveSuperset(state, removeChanges, delta) { + for (var i = 0; i < delta; i++) { + var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); + if (state.lines[state.index + i] !== ' ' + changeContent) { + return false; + } + } + + state.index += delta; + return true; + } + + function calcOldNewLineCount(lines) { + var oldLines = 0; + var newLines = 0; + + lines.forEach(function (line) { + if (typeof line !== 'string') { + var myCount = calcOldNewLineCount(line.mine); + var theirCount = calcOldNewLineCount(line.theirs); + + if (oldLines !== undefined) { + if (myCount.oldLines === theirCount.oldLines) { + oldLines += myCount.oldLines; + } else { + oldLines = undefined; + } + } + + if (newLines !== undefined) { + if (myCount.newLines === theirCount.newLines) { + newLines += myCount.newLines; + } else { + newLines = undefined; + } + } + } else { + if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { + newLines++; + } + if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { + oldLines++; + } + } + }); + + return { oldLines: oldLines, newLines: newLines }; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9tZXJnZS5qcyJdLCJuYW1lcyI6WyJjYWxjTGluZUNvdW50IiwibWVyZ2UiLCJodW5rIiwiY2FsY09sZE5ld0xpbmVDb3VudCIsImxpbmVzIiwib2xkTGluZXMiLCJuZXdMaW5lcyIsInVuZGVmaW5lZCIsIm1pbmUiLCJ0aGVpcnMiLCJiYXNlIiwibG9hZFBhdGNoIiwicmV0IiwiaW5kZXgiLCJuZXdGaWxlTmFtZSIsImZpbGVOYW1lQ2hhbmdlZCIsIm9sZEZpbGVOYW1lIiwib2xkSGVhZGVyIiwibmV3SGVhZGVyIiwic2VsZWN0RmllbGQiLCJodW5rcyIsIm1pbmVJbmRleCIsInRoZWlyc0luZGV4IiwibWluZU9mZnNldCIsInRoZWlyc09mZnNldCIsImxlbmd0aCIsIm1pbmVDdXJyZW50Iiwib2xkU3RhcnQiLCJJbmZpbml0eSIsInRoZWlyc0N1cnJlbnQiLCJodW5rQmVmb3JlIiwicHVzaCIsImNsb25lSHVuayIsIm1lcmdlZEh1bmsiLCJNYXRoIiwibWluIiwibmV3U3RhcnQiLCJtZXJnZUxpbmVzIiwicGFyYW0iLCJ0ZXN0IiwiRXJyb3IiLCJwYXRjaCIsImNvbmZsaWN0IiwiY2hlY2siLCJvZmZzZXQiLCJtaW5lTGluZXMiLCJ0aGVpck9mZnNldCIsInRoZWlyTGluZXMiLCJ0aGVpciIsImluc2VydExlYWRpbmciLCJ0aGVpckN1cnJlbnQiLCJtdXR1YWxDaGFuZ2UiLCJjb2xsZWN0Q2hhbmdlIiwicmVtb3ZhbCIsImluc2VydFRyYWlsaW5nIiwibXlDaGFuZ2VzIiwidGhlaXJDaGFuZ2VzIiwiYWxsUmVtb3ZlcyIsInNraXBSZW1vdmVTdXBlcnNldCIsInN3YXAiLCJjb2xsZWN0Q29udGV4dCIsIm1lcmdlZCIsImluc2VydCIsImxpbmUiLCJzdGF0ZSIsIm9wZXJhdGlvbiIsIm1hdGNoQ2hhbmdlcyIsImNoYW5nZXMiLCJtYXRjaEluZGV4IiwiY29udGV4dENoYW5nZXMiLCJjb25mbGljdGVkIiwiY2hhbmdlIiwibWF0Y2giLCJzdWJzdHIiLCJyZWR1Y2UiLCJwcmV2IiwicmVtb3ZlQ2hhbmdlcyIsImRlbHRhIiwiaSIsImNoYW5nZUNvbnRlbnQiLCJmb3JFYWNoIiwibXlDb3VudCIsInRoZWlyQ291bnQiXSwibWFwcGluZ3MiOiI7OztnQ0FLZ0JBLGEsR0FBQUEsYTt5REFnQkFDLEssR0FBQUEsSzs7QUFyQmhCOztBQUNBOztBQUVBOzs7O3VCQUVPLFNBQVNELGFBQVQsQ0FBdUJFLElBQXZCLEVBQTZCO0FBQUEsNkVBQ0xDLG9CQUFvQkQsS0FBS0UsS0FBekIsQ0FESztBQUFBLE1BQzNCQyxRQUQyQix3QkFDM0JBLFFBRDJCO0FBQUEsTUFDakJDLFFBRGlCLHdCQUNqQkEsUUFEaUI7O0FBR2xDLE1BQUlELGFBQWFFLFNBQWpCLEVBQTRCO0FBQzFCTCxTQUFLRyxRQUFMLEdBQWdCQSxRQUFoQjtBQUNELEdBRkQsTUFFTztBQUNMLFdBQU9ILEtBQUtHLFFBQVo7QUFDRDs7QUFFRCxNQUFJQyxhQUFhQyxTQUFqQixFQUE0QjtBQUMxQkwsU0FBS0ksUUFBTCxHQUFnQkEsUUFBaEI7QUFDRCxHQUZELE1BRU87QUFDTCxXQUFPSixLQUFLSSxRQUFaO0FBQ0Q7QUFDRjs7QUFFTSxTQUFTTCxLQUFULENBQWVPLElBQWYsRUFBcUJDLE1BQXJCLEVBQTZCQyxJQUE3QixFQUFtQztBQUN4Q0YsU0FBT0csVUFBVUgsSUFBVixFQUFnQkUsSUFBaEIsQ0FBUDtBQUNBRCxXQUFTRSxVQUFVRixNQUFWLEVBQWtCQyxJQUFsQixDQUFUOztBQUVBLE1BQUlFLE1BQU0sRUFBVjs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxNQUFJSixLQUFLSyxLQUFMLElBQWNKLE9BQU9JLEtBQXpCLEVBQWdDO0FBQzlCRCxRQUFJQyxLQUFKLEdBQVlMLEtBQUtLLEtBQUwsSUFBY0osT0FBT0ksS0FBakM7QUFDRDs7QUFFRCxNQUFJTCxLQUFLTSxXQUFMLElBQW9CTCxPQUFPSyxXQUEvQixFQUE0QztBQUMxQyxRQUFJLENBQUNDLGdCQUFnQlAsSUFBaEIsQ0FBTCxFQUE0QjtBQUMxQjtBQUNBSSxVQUFJSSxXQUFKLEdBQWtCUCxPQUFPTyxXQUFQLElBQXNCUixLQUFLUSxXQUE3QztBQUNBSixVQUFJRSxXQUFKLEdBQWtCTCxPQUFPSyxXQUFQLElBQXNCTixLQUFLTSxXQUE3QztBQUNBRixVQUFJSyxTQUFKLEdBQWdCUixPQUFPUSxTQUFQLElBQW9CVCxLQUFLUyxTQUF6QztBQUNBTCxVQUFJTSxTQUFKLEdBQWdCVCxPQUFPUyxTQUFQLElBQW9CVixLQUFLVSxTQUF6QztBQUNELEtBTkQsTUFNTyxJQUFJLENBQUNILGdCQUFnQk4sTUFBaEIsQ0FBTCxFQUE4QjtBQUNuQztBQUNBRyxVQUFJSSxXQUFKLEdBQWtCUixLQUFLUSxXQUF2QjtBQUNBSixVQUFJRSxXQUFKLEdBQWtCTixLQUFLTSxXQUF2QjtBQUNBRixVQUFJSyxTQUFKLEdBQWdCVCxLQUFLUyxTQUFyQjtBQUNBTCxVQUFJTSxTQUFKLEdBQWdCVixLQUFLVSxTQUFyQjtBQUNELEtBTk0sTUFNQTtBQUNMO0FBQ0FOLFVBQUlJLFdBQUosR0FBa0JHLFlBQVlQLEdBQVosRUFBaUJKLEtBQUtRLFdBQXRCLEVBQW1DUCxPQUFPTyxXQUExQyxDQUFsQjtBQUNBSixVQUFJRSxXQUFKLEdBQWtCSyxZQUFZUCxHQUFaLEVBQWlCSixLQUFLTSxXQUF0QixFQUFtQ0wsT0FBT0ssV0FBMUMsQ0FBbEI7QUFDQUYsVUFBSUssU0FBSixHQUFnQkUsWUFBWVAsR0FBWixFQUFpQkosS0FBS1MsU0FBdEIsRUFBaUNSLE9BQU9RLFNBQXhDLENBQWhCO0FBQ0FMLFVBQUlNLFNBQUosR0FBZ0JDLFlBQVlQLEdBQVosRUFBaUJKLEtBQUtVLFNBQXRCLEVBQWlDVCxPQUFPUyxTQUF4QyxDQUFoQjtBQUNEO0FBQ0Y7O0FBRUROLE1BQUlRLEtBQUosR0FBWSxFQUFaOztBQUVBLE1BQUlDLFlBQVksQ0FBaEI7QUFBQSxNQUNJQyxjQUFjLENBRGxCO0FBQUEsTUFFSUMsYUFBYSxDQUZqQjtBQUFBLE1BR0lDLGVBQWUsQ0FIbkI7O0FBS0EsU0FBT0gsWUFBWWIsS0FBS1ksS0FBTCxDQUFXSyxNQUF2QixJQUFpQ0gsY0FBY2IsT0FBT1csS0FBUCxDQUFhSyxNQUFuRSxFQUEyRTtBQUN6RSxRQUFJQyxjQUFjbEIsS0FBS1ksS0FBTCxDQUFXQyxTQUFYLEtBQXlCLEVBQUNNLFVBQVVDLFFBQVgsRUFBM0M7QUFBQSxRQUNJQyxnQkFBZ0JwQixPQUFPVyxLQUFQLENBQWFFLFdBQWIsS0FBNkIsRUFBQ0ssVUFBVUMsUUFBWCxFQURqRDs7QUFHQSxRQUFJRSxXQUFXSixXQUFYLEVBQXdCRyxhQUF4QixDQUFKLEVBQTRDO0FBQzFDO0FBQ0FqQixVQUFJUSxLQUFKLENBQVVXLElBQVYsQ0FBZUMsVUFBVU4sV0FBVixFQUF1QkgsVUFBdkIsQ0FBZjtBQUNBRjtBQUNBRyxzQkFBZ0JFLFlBQVlwQixRQUFaLEdBQXVCb0IsWUFBWXJCLFFBQW5EO0FBQ0QsS0FMRCxNQUtPLElBQUl5QixXQUFXRCxhQUFYLEVBQTBCSCxXQUExQixDQUFKLEVBQTRDO0FBQ2pEO0FBQ0FkLFVBQUlRLEtBQUosQ0FBVVcsSUFBVixDQUFlQyxVQUFVSCxhQUFWLEVBQXlCTCxZQUF6QixDQUFmO0FBQ0FGO0FBQ0FDLG9CQUFjTSxjQUFjdkIsUUFBZCxHQUF5QnVCLGNBQWN4QixRQUFyRDtBQUNELEtBTE0sTUFLQTtBQUNMO0FBQ0EsVUFBSTRCLGFBQWE7QUFDZk4sa0JBQVVPLEtBQUtDLEdBQUwsQ0FBU1QsWUFBWUMsUUFBckIsRUFBK0JFLGNBQWNGLFFBQTdDLENBREs7QUFFZnRCLGtCQUFVLENBRks7QUFHZitCLGtCQUFVRixLQUFLQyxHQUFMLENBQVNULFlBQVlVLFFBQVosR0FBdUJiLFVBQWhDLEVBQTRDTSxjQUFjRixRQUFkLEdBQXlCSCxZQUFyRSxDQUhLO0FBSWZsQixrQkFBVSxDQUpLO0FBS2ZGLGVBQU87QUFMUSxPQUFqQjtBQU9BaUMsaUJBQVdKLFVBQVgsRUFBdUJQLFlBQVlDLFFBQW5DLEVBQTZDRCxZQUFZdEIsS0FBekQsRUFBZ0V5QixjQUFjRixRQUE5RSxFQUF3RkUsY0FBY3pCLEtBQXRHO0FBQ0FrQjtBQUNBRDs7QUFFQVQsVUFBSVEsS0FBSixDQUFVVyxJQUFWLENBQWVFLFVBQWY7QUFDRDtBQUNGOztBQUVELFNBQU9yQixHQUFQO0FBQ0Q7O0FBRUQsU0FBU0QsU0FBVCxDQUFtQjJCLEtBQW5CLEVBQTBCNUIsSUFBMUIsRUFBZ0M7QUFDOUIsTUFBSSxPQUFPNEIsS0FBUCxLQUFpQixRQUFyQixFQUErQjtBQUM3QixRQUFJLE9BQU9DLElBQVAsQ0FBWUQsS0FBWixLQUF1QixXQUFXQyxJQUFYLENBQWdCRCxLQUFoQixDQUEzQixFQUFvRDtBQUNsRCxhQUFPLHlFQUFXQSxLQUFYLEVBQWtCLENBQWxCO0FBQVA7QUFDRDs7QUFFRCxRQUFJLENBQUM1QixJQUFMLEVBQVc7QUFDVCxZQUFNLElBQUk4QixLQUFKLENBQVUsa0RBQVYsQ0FBTjtBQUNEO0FBQ0QsV0FBTywrRUFBZ0JqQyxTQUFoQixFQUEyQkEsU0FBM0IsRUFBc0NHLElBQXRDLEVBQTRDNEIsS0FBNUM7QUFBUDtBQUNEOztBQUVELFNBQU9BLEtBQVA7QUFDRDs7QUFFRCxTQUFTdkIsZUFBVCxDQUF5QjBCLEtBQXpCLEVBQWdDO0FBQzlCLFNBQU9BLE1BQU0zQixXQUFOLElBQXFCMkIsTUFBTTNCLFdBQU4sS0FBc0IyQixNQUFNekIsV0FBeEQ7QUFDRDs7QUFFRCxTQUFTRyxXQUFULENBQXFCTixLQUFyQixFQUE0QkwsSUFBNUIsRUFBa0NDLE1BQWxDLEVBQTBDO0FBQ3hDLE1BQUlELFNBQVNDLE1BQWIsRUFBcUI7QUFDbkIsV0FBT0QsSUFBUDtBQUNELEdBRkQsTUFFTztBQUNMSyxVQUFNNkIsUUFBTixHQUFpQixJQUFqQjtBQUNBLFdBQU8sRUFBQ2xDLFVBQUQsRUFBT0MsY0FBUCxFQUFQO0FBQ0Q7QUFDRjs7QUFFRCxTQUFTcUIsVUFBVCxDQUFvQlMsSUFBcEIsRUFBMEJJLEtBQTFCLEVBQWlDO0FBQy9CLFNBQU9KLEtBQUtaLFFBQUwsR0FBZ0JnQixNQUFNaEIsUUFBdEIsSUFDRFksS0FBS1osUUFBTCxHQUFnQlksS0FBS2xDLFFBQXRCLEdBQWtDc0MsTUFBTWhCLFFBRDdDO0FBRUQ7O0FBRUQsU0FBU0ssU0FBVCxDQUFtQjlCLElBQW5CLEVBQXlCMEMsTUFBekIsRUFBaUM7QUFDL0IsU0FBTztBQUNMakIsY0FBVXpCLEtBQUt5QixRQURWLEVBQ29CdEIsVUFBVUgsS0FBS0csUUFEbkM7QUFFTCtCLGNBQVVsQyxLQUFLa0MsUUFBTCxHQUFnQlEsTUFGckIsRUFFNkJ0QyxVQUFVSixLQUFLSSxRQUY1QztBQUdMRixXQUFPRixLQUFLRTtBQUhQLEdBQVA7QUFLRDs7QUFFRCxTQUFTaUMsVUFBVCxDQUFvQm5DLElBQXBCLEVBQTBCcUIsVUFBMUIsRUFBc0NzQixTQUF0QyxFQUFpREMsV0FBakQsRUFBOERDLFVBQTlELEVBQTBFO0FBQ3hFO0FBQ0E7QUFDQSxNQUFJdkMsT0FBTyxFQUFDb0MsUUFBUXJCLFVBQVQsRUFBcUJuQixPQUFPeUMsU0FBNUIsRUFBdUNoQyxPQUFPLENBQTlDLEVBQVg7QUFBQSxNQUNJbUMsUUFBUSxFQUFDSixRQUFRRSxXQUFULEVBQXNCMUMsT0FBTzJDLFVBQTdCLEVBQXlDbEMsT0FBTyxDQUFoRCxFQURaOztBQUdBO0FBQ0FvQyxnQkFBYy9DLElBQWQsRUFBb0JNLElBQXBCLEVBQTBCd0MsS0FBMUI7QUFDQUMsZ0JBQWMvQyxJQUFkLEVBQW9COEMsS0FBcEIsRUFBMkJ4QyxJQUEzQjs7QUFFQTtBQUNBLFNBQU9BLEtBQUtLLEtBQUwsR0FBYUwsS0FBS0osS0FBTCxDQUFXcUIsTUFBeEIsSUFBa0N1QixNQUFNbkMsS0FBTixHQUFjbUMsTUFBTTVDLEtBQU4sQ0FBWXFCLE1BQW5FLEVBQTJFO0FBQ3pFLFFBQUlDLGNBQWNsQixLQUFLSixLQUFMLENBQVdJLEtBQUtLLEtBQWhCLENBQWxCO0FBQUEsUUFDSXFDLGVBQWVGLE1BQU01QyxLQUFOLENBQVk0QyxNQUFNbkMsS0FBbEIsQ0FEbkI7O0FBR0EsUUFBSSxDQUFDYSxZQUFZLENBQVosTUFBbUIsR0FBbkIsSUFBMEJBLFlBQVksQ0FBWixNQUFtQixHQUE5QyxNQUNJd0IsYUFBYSxDQUFiLE1BQW9CLEdBQXBCLElBQTJCQSxhQUFhLENBQWIsTUFBb0IsR0FEbkQsQ0FBSixFQUM2RDtBQUMzRDtBQUNBQyxtQkFBYWpELElBQWIsRUFBbUJNLElBQW5CLEVBQXlCd0MsS0FBekI7QUFDRCxLQUpELE1BSU8sSUFBSXRCLFlBQVksQ0FBWixNQUFtQixHQUFuQixJQUEwQndCLGFBQWEsQ0FBYixNQUFvQixHQUFsRCxFQUF1RDtBQUFBOztBQUFBLDhCQUM1RDtBQUNBLDBFQUFLOUMsS0FBTCxFQUFXMkIsSUFBWCw0TEFBb0JxQixjQUFjNUMsSUFBZCxDQUFwQjtBQUNELEtBSE0sTUFHQSxJQUFJMEMsYUFBYSxDQUFiLE1BQW9CLEdBQXBCLElBQTJCeEIsWUFBWSxDQUFaLE1BQW1CLEdBQWxELEVBQXVEO0FBQUE7O0FBQUEsOEJBQzVEO0FBQ0EsMkVBQUt0QixLQUFMLEVBQVcyQixJQUFYLDZMQUFvQnFCLGNBQWNKLEtBQWQsQ0FBcEI7QUFDRCxLQUhNLE1BR0EsSUFBSXRCLFlBQVksQ0FBWixNQUFtQixHQUFuQixJQUEwQndCLGFBQWEsQ0FBYixNQUFvQixHQUFsRCxFQUF1RDtBQUM1RDtBQUNBRyxjQUFRbkQsSUFBUixFQUFjTSxJQUFkLEVBQW9Cd0MsS0FBcEI7QUFDRCxLQUhNLE1BR0EsSUFBSUUsYUFBYSxDQUFiLE1BQW9CLEdBQXBCLElBQTJCeEIsWUFBWSxDQUFaLE1BQW1CLEdBQWxELEVBQXVEO0FBQzVEO0FBQ0EyQixjQUFRbkQsSUFBUixFQUFjOEMsS0FBZCxFQUFxQnhDLElBQXJCLEVBQTJCLElBQTNCO0FBQ0QsS0FITSxNQUdBLElBQUlrQixnQkFBZ0J3QixZQUFwQixFQUFrQztBQUN2QztBQUNBaEQsV0FBS0UsS0FBTCxDQUFXMkIsSUFBWCxDQUFnQkwsV0FBaEI7QUFDQWxCLFdBQUtLLEtBQUw7QUFDQW1DLFlBQU1uQyxLQUFOO0FBQ0QsS0FMTSxNQUtBO0FBQ0w7QUFDQTZCLGVBQVN4QyxJQUFULEVBQWVrRCxjQUFjNUMsSUFBZCxDQUFmLEVBQW9DNEMsY0FBY0osS0FBZCxDQUFwQztBQUNEO0FBQ0Y7O0FBRUQ7QUFDQU0saUJBQWVwRCxJQUFmLEVBQXFCTSxJQUFyQjtBQUNBOEMsaUJBQWVwRCxJQUFmLEVBQXFCOEMsS0FBckI7O0FBRUFoRCxnQkFBY0UsSUFBZDtBQUNEOztBQUVELFNBQVNpRCxZQUFULENBQXNCakQsSUFBdEIsRUFBNEJNLElBQTVCLEVBQWtDd0MsS0FBbEMsRUFBeUM7QUFDdkMsTUFBSU8sWUFBWUgsY0FBYzVDLElBQWQsQ0FBaEI7QUFBQSxNQUNJZ0QsZUFBZUosY0FBY0osS0FBZCxDQURuQjs7QUFHQSxNQUFJUyxXQUFXRixTQUFYLEtBQXlCRSxXQUFXRCxZQUFYLENBQTdCLEVBQXVEO0FBQ3JEO0FBQ0EsUUFBSSw4RUFBZ0JELFNBQWhCLEVBQTJCQyxZQUEzQixLQUNHRSxtQkFBbUJWLEtBQW5CLEVBQTBCTyxTQUExQixFQUFxQ0EsVUFBVTlCLE1BQVYsR0FBbUIrQixhQUFhL0IsTUFBckUsQ0FEUCxFQUNxRjtBQUFBOztBQUFBLDZCQUNuRixzRUFBS3JCLEtBQUwsRUFBVzJCLElBQVgsNkxBQW9Cd0IsU0FBcEI7QUFDQTtBQUNELEtBSkQsTUFJTyxJQUFJLDhFQUFnQkMsWUFBaEIsRUFBOEJELFNBQTlCLEtBQ0pHLG1CQUFtQmxELElBQW5CLEVBQXlCZ0QsWUFBekIsRUFBdUNBLGFBQWEvQixNQUFiLEdBQXNCOEIsVUFBVTlCLE1BQXZFLENBREEsRUFDZ0Y7QUFBQTs7QUFBQSw2QkFDckYsc0VBQUtyQixLQUFMLEVBQVcyQixJQUFYLDZMQUFvQnlCLFlBQXBCO0FBQ0E7QUFDRDtBQUNGLEdBWEQsTUFXTyxJQUFJLHlFQUFXRCxTQUFYLEVBQXNCQyxZQUF0QixDQUFKLEVBQXlDO0FBQUE7O0FBQUEsMkJBQzlDLHNFQUFLcEQsS0FBTCxFQUFXMkIsSUFBWCw2TEFBb0J3QixTQUFwQjtBQUNBO0FBQ0Q7O0FBRURiLFdBQVN4QyxJQUFULEVBQWVxRCxTQUFmLEVBQTBCQyxZQUExQjtBQUNEOztBQUVELFNBQVNILE9BQVQsQ0FBaUJuRCxJQUFqQixFQUF1Qk0sSUFBdkIsRUFBNkJ3QyxLQUE3QixFQUFvQ1csSUFBcEMsRUFBMEM7QUFDeEMsTUFBSUosWUFBWUgsY0FBYzVDLElBQWQsQ0FBaEI7QUFBQSxNQUNJZ0QsZUFBZUksZUFBZVosS0FBZixFQUFzQk8sU0FBdEIsQ0FEbkI7QUFFQSxNQUFJQyxhQUFhSyxNQUFqQixFQUF5QjtBQUFBOztBQUFBLDJCQUN2QixzRUFBS3pELEtBQUwsRUFBVzJCLElBQVgsNkxBQW9CeUIsYUFBYUssTUFBakM7QUFDRCxHQUZELE1BRU87QUFDTG5CLGFBQVN4QyxJQUFULEVBQWV5RCxPQUFPSCxZQUFQLEdBQXNCRCxTQUFyQyxFQUFnREksT0FBT0osU0FBUCxHQUFtQkMsWUFBbkU7QUFDRDtBQUNGOztBQUVELFNBQVNkLFFBQVQsQ0FBa0J4QyxJQUFsQixFQUF3Qk0sSUFBeEIsRUFBOEJ3QyxLQUE5QixFQUFxQztBQUNuQzlDLE9BQUt3QyxRQUFMLEdBQWdCLElBQWhCO0FBQ0F4QyxPQUFLRSxLQUFMLENBQVcyQixJQUFYLENBQWdCO0FBQ2RXLGNBQVUsSUFESTtBQUVkbEMsVUFBTUEsSUFGUTtBQUdkQyxZQUFRdUM7QUFITSxHQUFoQjtBQUtEOztBQUVELFNBQVNDLGFBQVQsQ0FBdUIvQyxJQUF2QixFQUE2QjRELE1BQTdCLEVBQXFDZCxLQUFyQyxFQUE0QztBQUMxQyxTQUFPYyxPQUFPbEIsTUFBUCxHQUFnQkksTUFBTUosTUFBdEIsSUFBZ0NrQixPQUFPakQsS0FBUCxHQUFlaUQsT0FBTzFELEtBQVAsQ0FBYXFCLE1BQW5FLEVBQTJFO0FBQ3pFLFFBQUlzQyxPQUFPRCxPQUFPMUQsS0FBUCxDQUFhMEQsT0FBT2pELEtBQVAsRUFBYixDQUFYO0FBQ0FYLFNBQUtFLEtBQUwsQ0FBVzJCLElBQVgsQ0FBZ0JnQyxJQUFoQjtBQUNBRCxXQUFPbEIsTUFBUDtBQUNEO0FBQ0Y7QUFDRCxTQUFTVSxjQUFULENBQXdCcEQsSUFBeEIsRUFBOEI0RCxNQUE5QixFQUFzQztBQUNwQyxTQUFPQSxPQUFPakQsS0FBUCxHQUFlaUQsT0FBTzFELEtBQVAsQ0FBYXFCLE1BQW5DLEVBQTJDO0FBQ3pDLFFBQUlzQyxPQUFPRCxPQUFPMUQsS0FBUCxDQUFhMEQsT0FBT2pELEtBQVAsRUFBYixDQUFYO0FBQ0FYLFNBQUtFLEtBQUwsQ0FBVzJCLElBQVgsQ0FBZ0JnQyxJQUFoQjtBQUNEO0FBQ0Y7O0FBRUQsU0FBU1gsYUFBVCxDQUF1QlksS0FBdkIsRUFBOEI7QUFDNUIsTUFBSXBELE1BQU0sRUFBVjtBQUFBLE1BQ0lxRCxZQUFZRCxNQUFNNUQsS0FBTixDQUFZNEQsTUFBTW5ELEtBQWxCLEVBQXlCLENBQXpCLENBRGhCO0FBRUEsU0FBT21ELE1BQU1uRCxLQUFOLEdBQWNtRCxNQUFNNUQsS0FBTixDQUFZcUIsTUFBakMsRUFBeUM7QUFDdkMsUUFBSXNDLE9BQU9DLE1BQU01RCxLQUFOLENBQVk0RCxNQUFNbkQsS0FBbEIsQ0FBWDs7QUFFQTtBQUNBLFFBQUlvRCxjQUFjLEdBQWQsSUFBcUJGLEtBQUssQ0FBTCxNQUFZLEdBQXJDLEVBQTBDO0FBQ3hDRSxrQkFBWSxHQUFaO0FBQ0Q7O0FBRUQsUUFBSUEsY0FBY0YsS0FBSyxDQUFMLENBQWxCLEVBQTJCO0FBQ3pCbkQsVUFBSW1CLElBQUosQ0FBU2dDLElBQVQ7QUFDQUMsWUFBTW5ELEtBQU47QUFDRCxLQUhELE1BR087QUFDTDtBQUNEO0FBQ0Y7O0FBRUQsU0FBT0QsR0FBUDtBQUNEO0FBQ0QsU0FBU2dELGNBQVQsQ0FBd0JJLEtBQXhCLEVBQStCRSxZQUEvQixFQUE2QztBQUMzQyxNQUFJQyxVQUFVLEVBQWQ7QUFBQSxNQUNJTixTQUFTLEVBRGI7QUFBQSxNQUVJTyxhQUFhLENBRmpCO0FBQUEsTUFHSUMsaUJBQWlCLEtBSHJCO0FBQUEsTUFJSUMsYUFBYSxLQUpqQjtBQUtBLFNBQU9GLGFBQWFGLGFBQWF6QyxNQUExQixJQUNFdUMsTUFBTW5ELEtBQU4sR0FBY21ELE1BQU01RCxLQUFOLENBQVlxQixNQURuQyxFQUMyQztBQUN6QyxRQUFJOEMsU0FBU1AsTUFBTTVELEtBQU4sQ0FBWTRELE1BQU1uRCxLQUFsQixDQUFiO0FBQUEsUUFDSTJELFFBQVFOLGFBQWFFLFVBQWIsQ0FEWjs7QUFHQTtBQUNBLFFBQUlJLE1BQU0sQ0FBTixNQUFhLEdBQWpCLEVBQXNCO0FBQ3BCO0FBQ0Q7O0FBRURILHFCQUFpQkEsa0JBQWtCRSxPQUFPLENBQVAsTUFBYyxHQUFqRDs7QUFFQVYsV0FBTzlCLElBQVAsQ0FBWXlDLEtBQVo7QUFDQUo7O0FBRUE7QUFDQTtBQUNBLFFBQUlHLE9BQU8sQ0FBUCxNQUFjLEdBQWxCLEVBQXVCO0FBQ3JCRCxtQkFBYSxJQUFiOztBQUVBLGFBQU9DLE9BQU8sQ0FBUCxNQUFjLEdBQXJCLEVBQTBCO0FBQ3hCSixnQkFBUXBDLElBQVIsQ0FBYXdDLE1BQWI7QUFDQUEsaUJBQVNQLE1BQU01RCxLQUFOLENBQVksRUFBRTRELE1BQU1uRCxLQUFwQixDQUFUO0FBQ0Q7QUFDRjs7QUFFRCxRQUFJMkQsTUFBTUMsTUFBTixDQUFhLENBQWIsTUFBb0JGLE9BQU9FLE1BQVAsQ0FBYyxDQUFkLENBQXhCLEVBQTBDO0FBQ3hDTixjQUFRcEMsSUFBUixDQUFhd0MsTUFBYjtBQUNBUCxZQUFNbkQsS0FBTjtBQUNELEtBSEQsTUFHTztBQUNMeUQsbUJBQWEsSUFBYjtBQUNEO0FBQ0Y7O0FBRUQsTUFBSSxDQUFDSixhQUFhRSxVQUFiLEtBQTRCLEVBQTdCLEVBQWlDLENBQWpDLE1BQXdDLEdBQXhDLElBQ0dDLGNBRFAsRUFDdUI7QUFDckJDLGlCQUFhLElBQWI7QUFDRDs7QUFFRCxNQUFJQSxVQUFKLEVBQWdCO0FBQ2QsV0FBT0gsT0FBUDtBQUNEOztBQUVELFNBQU9DLGFBQWFGLGFBQWF6QyxNQUFqQyxFQUF5QztBQUN2Q29DLFdBQU85QixJQUFQLENBQVltQyxhQUFhRSxZQUFiLENBQVo7QUFDRDs7QUFFRCxTQUFPO0FBQ0xQLGtCQURLO0FBRUxNO0FBRkssR0FBUDtBQUlEOztBQUVELFNBQVNWLFVBQVQsQ0FBb0JVLE9BQXBCLEVBQTZCO0FBQzNCLFNBQU9BLFFBQVFPLE1BQVIsQ0FBZSxVQUFTQyxJQUFULEVBQWVKLE1BQWYsRUFBdUI7QUFDM0MsV0FBT0ksUUFBUUosT0FBTyxDQUFQLE1BQWMsR0FBN0I7QUFDRCxHQUZNLEVBRUosSUFGSSxDQUFQO0FBR0Q7QUFDRCxTQUFTYixrQkFBVCxDQUE0Qk0sS0FBNUIsRUFBbUNZLGFBQW5DLEVBQWtEQyxLQUFsRCxFQUF5RDtBQUN2RCxPQUFLLElBQUlDLElBQUksQ0FBYixFQUFnQkEsSUFBSUQsS0FBcEIsRUFBMkJDLEdBQTNCLEVBQWdDO0FBQzlCLFFBQUlDLGdCQUFnQkgsY0FBY0EsY0FBY25ELE1BQWQsR0FBdUJvRCxLQUF2QixHQUErQkMsQ0FBN0MsRUFBZ0RMLE1BQWhELENBQXVELENBQXZELENBQXBCO0FBQ0EsUUFBSVQsTUFBTTVELEtBQU4sQ0FBWTRELE1BQU1uRCxLQUFOLEdBQWNpRSxDQUExQixNQUFpQyxNQUFNQyxhQUEzQyxFQUEwRDtBQUN4RCxhQUFPLEtBQVA7QUFDRDtBQUNGOztBQUVEZixRQUFNbkQsS0FBTixJQUFlZ0UsS0FBZjtBQUNBLFNBQU8sSUFBUDtBQUNEOztBQUVELFNBQVMxRSxtQkFBVCxDQUE2QkMsS0FBN0IsRUFBb0M7QUFDbEMsTUFBSUMsV0FBVyxDQUFmO0FBQ0EsTUFBSUMsV0FBVyxDQUFmOztBQUVBRixRQUFNNEUsT0FBTixDQUFjLFVBQVNqQixJQUFULEVBQWU7QUFDM0IsUUFBSSxPQUFPQSxJQUFQLEtBQWdCLFFBQXBCLEVBQThCO0FBQzVCLFVBQUlrQixVQUFVOUUsb0JBQW9CNEQsS0FBS3ZELElBQXpCLENBQWQ7QUFDQSxVQUFJMEUsYUFBYS9FLG9CQUFvQjRELEtBQUt0RCxNQUF6QixDQUFqQjs7QUFFQSxVQUFJSixhQUFhRSxTQUFqQixFQUE0QjtBQUMxQixZQUFJMEUsUUFBUTVFLFFBQVIsS0FBcUI2RSxXQUFXN0UsUUFBcEMsRUFBOEM7QUFDNUNBLHNCQUFZNEUsUUFBUTVFLFFBQXBCO0FBQ0QsU0FGRCxNQUVPO0FBQ0xBLHFCQUFXRSxTQUFYO0FBQ0Q7QUFDRjs7QUFFRCxVQUFJRCxhQUFhQyxTQUFqQixFQUE0QjtBQUMxQixZQUFJMEUsUUFBUTNFLFFBQVIsS0FBcUI0RSxXQUFXNUUsUUFBcEMsRUFBOEM7QUFDNUNBLHNCQUFZMkUsUUFBUTNFLFFBQXBCO0FBQ0QsU0FGRCxNQUVPO0FBQ0xBLHFCQUFXQyxTQUFYO0FBQ0Q7QUFDRjtBQUNGLEtBbkJELE1BbUJPO0FBQ0wsVUFBSUQsYUFBYUMsU0FBYixLQUEyQndELEtBQUssQ0FBTCxNQUFZLEdBQVosSUFBbUJBLEtBQUssQ0FBTCxNQUFZLEdBQTFELENBQUosRUFBb0U7QUFDbEV6RDtBQUNEO0FBQ0QsVUFBSUQsYUFBYUUsU0FBYixLQUEyQndELEtBQUssQ0FBTCxNQUFZLEdBQVosSUFBbUJBLEtBQUssQ0FBTCxNQUFZLEdBQTFELENBQUosRUFBb0U7QUFDbEUxRDtBQUNEO0FBQ0Y7QUFDRixHQTVCRDs7QUE4QkEsU0FBTyxFQUFDQSxrQkFBRCxFQUFXQyxrQkFBWCxFQUFQO0FBQ0QiLCJmaWxlIjoibWVyZ2UuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge3N0cnVjdHVyZWRQYXRjaH0gZnJvbSAnLi9jcmVhdGUnO1xuaW1wb3J0IHtwYXJzZVBhdGNofSBmcm9tICcuL3BhcnNlJztcblxuaW1wb3J0IHthcnJheUVxdWFsLCBhcnJheVN0YXJ0c1dpdGh9IGZyb20gJy4uL3V0aWwvYXJyYXknO1xuXG5leHBvcnQgZnVuY3Rpb24gY2FsY0xpbmVDb3VudChodW5rKSB7XG4gIGNvbnN0IHtvbGRMaW5lcywgbmV3TGluZXN9ID0gY2FsY09sZE5ld0xpbmVDb3VudChodW5rLmxpbmVzKTtcblxuICBpZiAob2xkTGluZXMgIT09IHVuZGVmaW5lZCkge1xuICAgIGh1bmsub2xkTGluZXMgPSBvbGRMaW5lcztcbiAgfSBlbHNlIHtcbiAgICBkZWxldGUgaHVuay5vbGRMaW5lcztcbiAgfVxuXG4gIGlmIChuZXdMaW5lcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgaHVuay5uZXdMaW5lcyA9IG5ld0xpbmVzO1xuICB9IGVsc2Uge1xuICAgIGRlbGV0ZSBodW5rLm5ld0xpbmVzO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZShtaW5lLCB0aGVpcnMsIGJhc2UpIHtcbiAgbWluZSA9IGxvYWRQYXRjaChtaW5lLCBiYXNlKTtcbiAgdGhlaXJzID0gbG9hZFBhdGNoKHRoZWlycywgYmFzZSk7XG5cbiAgbGV0IHJldCA9IHt9O1xuXG4gIC8vIEZvciBpbmRleCB3ZSBqdXN0IGxldCBpdCBwYXNzIHRocm91Z2ggYXMgaXQgZG9lc24ndCBoYXZlIGFueSBuZWNlc3NhcnkgbWVhbmluZy5cbiAgLy8gTGVhdmluZyBzYW5pdHkgY2hlY2tzIG9uIHRoaXMgdG8gdGhlIEFQSSBjb25zdW1lciB0aGF0IG1heSBrbm93IG1vcmUgYWJvdXQgdGhlXG4gIC8vIG1lYW5pbmcgaW4gdGhlaXIgb3duIGNvbnRleHQuXG4gIGlmIChtaW5lLmluZGV4IHx8IHRoZWlycy5pbmRleCkge1xuICAgIHJldC5pbmRleCA9IG1pbmUuaW5kZXggfHwgdGhlaXJzLmluZGV4O1xuICB9XG5cbiAgaWYgKG1pbmUubmV3RmlsZU5hbWUgfHwgdGhlaXJzLm5ld0ZpbGVOYW1lKSB7XG4gICAgaWYgKCFmaWxlTmFtZUNoYW5nZWQobWluZSkpIHtcbiAgICAgIC8vIE5vIGhlYWRlciBvciBubyBjaGFuZ2UgaW4gb3VycywgdXNlIHRoZWlycyAoYW5kIG91cnMgaWYgdGhlaXJzIGRvZXMgbm90IGV4aXN0KVxuICAgICAgcmV0Lm9sZEZpbGVOYW1lID0gdGhlaXJzLm9sZEZpbGVOYW1lIHx8IG1pbmUub2xkRmlsZU5hbWU7XG4gICAgICByZXQubmV3RmlsZU5hbWUgPSB0aGVpcnMubmV3RmlsZU5hbWUgfHwgbWluZS5uZXdGaWxlTmFtZTtcbiAgICAgIHJldC5vbGRIZWFkZXIgPSB0aGVpcnMub2xkSGVhZGVyIHx8IG1pbmUub2xkSGVhZGVyO1xuICAgICAgcmV0Lm5ld0hlYWRlciA9IHRoZWlycy5uZXdIZWFkZXIgfHwgbWluZS5uZXdIZWFkZXI7XG4gICAgfSBlbHNlIGlmICghZmlsZU5hbWVDaGFuZ2VkKHRoZWlycykpIHtcbiAgICAgIC8vIE5vIGhlYWRlciBvciBubyBjaGFuZ2UgaW4gdGhlaXJzLCB1c2Ugb3Vyc1xuICAgICAgcmV0Lm9sZEZpbGVOYW1lID0gbWluZS5vbGRGaWxlTmFtZTtcbiAgICAgIHJldC5uZXdGaWxlTmFtZSA9IG1pbmUubmV3RmlsZU5hbWU7XG4gICAgICByZXQub2xkSGVhZGVyID0gbWluZS5vbGRIZWFkZXI7XG4gICAgICByZXQubmV3SGVhZGVyID0gbWluZS5uZXdIZWFkZXI7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEJvdGggY2hhbmdlZC4uLiBmaWd1cmUgaXQgb3V0XG4gICAgICByZXQub2xkRmlsZU5hbWUgPSBzZWxlY3RGaWVsZChyZXQsIG1pbmUub2xkRmlsZU5hbWUsIHRoZWlycy5vbGRGaWxlTmFtZSk7XG4gICAgICByZXQubmV3RmlsZU5hbWUgPSBzZWxlY3RGaWVsZChyZXQsIG1pbmUubmV3RmlsZU5hbWUsIHRoZWlycy5uZXdGaWxlTmFtZSk7XG4gICAgICByZXQub2xkSGVhZGVyID0gc2VsZWN0RmllbGQocmV0LCBtaW5lLm9sZEhlYWRlciwgdGhlaXJzLm9sZEhlYWRlcik7XG4gICAgICByZXQubmV3SGVhZGVyID0gc2VsZWN0RmllbGQocmV0LCBtaW5lLm5ld0hlYWRlciwgdGhlaXJzLm5ld0hlYWRlcik7XG4gICAgfVxuICB9XG5cbiAgcmV0Lmh1bmtzID0gW107XG5cbiAgbGV0IG1pbmVJbmRleCA9IDAsXG4gICAgICB0aGVpcnNJbmRleCA9IDAsXG4gICAgICBtaW5lT2Zmc2V0ID0gMCxcbiAgICAgIHRoZWlyc09mZnNldCA9IDA7XG5cbiAgd2hpbGUgKG1pbmVJbmRleCA8IG1pbmUuaHVua3MubGVuZ3RoIHx8IHRoZWlyc0luZGV4IDwgdGhlaXJzLmh1bmtzLmxlbmd0aCkge1xuICAgIGxldCBtaW5lQ3VycmVudCA9IG1pbmUuaHVua3NbbWluZUluZGV4XSB8fCB7b2xkU3RhcnQ6IEluZmluaXR5fSxcbiAgICAgICAgdGhlaXJzQ3VycmVudCA9IHRoZWlycy5odW5rc1t0aGVpcnNJbmRleF0gfHwge29sZFN0YXJ0OiBJbmZpbml0eX07XG5cbiAgICBpZiAoaHVua0JlZm9yZShtaW5lQ3VycmVudCwgdGhlaXJzQ3VycmVudCkpIHtcbiAgICAgIC8vIFRoaXMgcGF0Y2ggZG9lcyBub3Qgb3ZlcmxhcCB3aXRoIGFueSBvZiB0aGUgb3RoZXJzLCB5YXkuXG4gICAgICByZXQuaHVua3MucHVzaChjbG9uZUh1bmsobWluZUN1cnJlbnQsIG1pbmVPZmZzZXQpKTtcbiAgICAgIG1pbmVJbmRleCsrO1xuICAgICAgdGhlaXJzT2Zmc2V0ICs9IG1pbmVDdXJyZW50Lm5ld0xpbmVzIC0gbWluZUN1cnJlbnQub2xkTGluZXM7XG4gICAgfSBlbHNlIGlmIChodW5rQmVmb3JlKHRoZWlyc0N1cnJlbnQsIG1pbmVDdXJyZW50KSkge1xuICAgICAgLy8gVGhpcyBwYXRjaCBkb2VzIG5vdCBvdmVybGFwIHdpdGggYW55IG9mIHRoZSBvdGhlcnMsIHlheS5cbiAgICAgIHJldC5odW5rcy5wdXNoKGNsb25lSHVuayh0aGVpcnNDdXJyZW50LCB0aGVpcnNPZmZzZXQpKTtcbiAgICAgIHRoZWlyc0luZGV4Kys7XG4gICAgICBtaW5lT2Zmc2V0ICs9IHRoZWlyc0N1cnJlbnQubmV3TGluZXMgLSB0aGVpcnNDdXJyZW50Lm9sZExpbmVzO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBPdmVybGFwLCBtZXJnZSBhcyBiZXN0IHdlIGNhblxuICAgICAgbGV0IG1lcmdlZEh1bmsgPSB7XG4gICAgICAgIG9sZFN0YXJ0OiBNYXRoLm1pbihtaW5lQ3VycmVudC5vbGRTdGFydCwgdGhlaXJzQ3VycmVudC5vbGRTdGFydCksXG4gICAgICAgIG9sZExpbmVzOiAwLFxuICAgICAgICBuZXdTdGFydDogTWF0aC5taW4obWluZUN1cnJlbnQubmV3U3RhcnQgKyBtaW5lT2Zmc2V0LCB0aGVpcnNDdXJyZW50Lm9sZFN0YXJ0ICsgdGhlaXJzT2Zmc2V0KSxcbiAgICAgICAgbmV3TGluZXM6IDAsXG4gICAgICAgIGxpbmVzOiBbXVxuICAgICAgfTtcbiAgICAgIG1lcmdlTGluZXMobWVyZ2VkSHVuaywgbWluZUN1cnJlbnQub2xkU3RhcnQsIG1pbmVDdXJyZW50LmxpbmVzLCB0aGVpcnNDdXJyZW50Lm9sZFN0YXJ0LCB0aGVpcnNDdXJyZW50LmxpbmVzKTtcbiAgICAgIHRoZWlyc0luZGV4Kys7XG4gICAgICBtaW5lSW5kZXgrKztcblxuICAgICAgcmV0Lmh1bmtzLnB1c2gobWVyZ2VkSHVuayk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHJldDtcbn1cblxuZnVuY3Rpb24gbG9hZFBhdGNoKHBhcmFtLCBiYXNlKSB7XG4gIGlmICh0eXBlb2YgcGFyYW0gPT09ICdzdHJpbmcnKSB7XG4gICAgaWYgKC9eQEAvbS50ZXN0KHBhcmFtKSB8fCAoL15JbmRleDovbS50ZXN0KHBhcmFtKSkpIHtcbiAgICAgIHJldHVybiBwYXJzZVBhdGNoKHBhcmFtKVswXTtcbiAgICB9XG5cbiAgICBpZiAoIWJhc2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTXVzdCBwcm92aWRlIGEgYmFzZSByZWZlcmVuY2Ugb3IgcGFzcyBpbiBhIHBhdGNoJyk7XG4gICAgfVxuICAgIHJldHVybiBzdHJ1Y3R1cmVkUGF0Y2godW5kZWZpbmVkLCB1bmRlZmluZWQsIGJhc2UsIHBhcmFtKTtcbiAgfVxuXG4gIHJldHVybiBwYXJhbTtcbn1cblxuZnVuY3Rpb24gZmlsZU5hbWVDaGFuZ2VkKHBhdGNoKSB7XG4gIHJldHVybiBwYXRjaC5uZXdGaWxlTmFtZSAmJiBwYXRjaC5uZXdGaWxlTmFtZSAhPT0gcGF0Y2gub2xkRmlsZU5hbWU7XG59XG5cbmZ1bmN0aW9uIHNlbGVjdEZpZWxkKGluZGV4LCBtaW5lLCB0aGVpcnMpIHtcbiAgaWYgKG1pbmUgPT09IHRoZWlycykge1xuICAgIHJldHVybiBtaW5lO1xuICB9IGVsc2Uge1xuICAgIGluZGV4LmNvbmZsaWN0ID0gdHJ1ZTtcbiAgICByZXR1cm4ge21pbmUsIHRoZWlyc307XG4gIH1cbn1cblxuZnVuY3Rpb24gaHVua0JlZm9yZSh0ZXN0LCBjaGVjaykge1xuICByZXR1cm4gdGVzdC5vbGRTdGFydCA8IGNoZWNrLm9sZFN0YXJ0XG4gICAgJiYgKHRlc3Qub2xkU3RhcnQgKyB0ZXN0Lm9sZExpbmVzKSA8IGNoZWNrLm9sZFN0YXJ0O1xufVxuXG5mdW5jdGlvbiBjbG9uZUh1bmsoaHVuaywgb2Zmc2V0KSB7XG4gIHJldHVybiB7XG4gICAgb2xkU3RhcnQ6IGh1bmsub2xkU3RhcnQsIG9sZExpbmVzOiBodW5rLm9sZExpbmVzLFxuICAgIG5ld1N0YXJ0OiBodW5rLm5ld1N0YXJ0ICsgb2Zmc2V0LCBuZXdMaW5lczogaHVuay5uZXdMaW5lcyxcbiAgICBsaW5lczogaHVuay5saW5lc1xuICB9O1xufVxuXG5mdW5jdGlvbiBtZXJnZUxpbmVzKGh1bmssIG1pbmVPZmZzZXQsIG1pbmVMaW5lcywgdGhlaXJPZmZzZXQsIHRoZWlyTGluZXMpIHtcbiAgLy8gVGhpcyB3aWxsIGdlbmVyYWxseSByZXN1bHQgaW4gYSBjb25mbGljdGVkIGh1bmssIGJ1dCB0aGVyZSBhcmUgY2FzZXMgd2hlcmUgdGhlIGNvbnRleHRcbiAgLy8gaXMgdGhlIG9ubHkgb3ZlcmxhcCB3aGVyZSB3ZSBjYW4gc3VjY2Vzc2Z1bGx5IG1lcmdlIHRoZSBjb250ZW50IGhlcmUuXG4gIGxldCBtaW5lID0ge29mZnNldDogbWluZU9mZnNldCwgbGluZXM6IG1pbmVMaW5lcywgaW5kZXg6IDB9LFxuICAgICAgdGhlaXIgPSB7b2Zmc2V0OiB0aGVpck9mZnNldCwgbGluZXM6IHRoZWlyTGluZXMsIGluZGV4OiAwfTtcblxuICAvLyBIYW5kbGUgYW55IGxlYWRpbmcgY29udGVudFxuICBpbnNlcnRMZWFkaW5nKGh1bmssIG1pbmUsIHRoZWlyKTtcbiAgaW5zZXJ0TGVhZGluZyhodW5rLCB0aGVpciwgbWluZSk7XG5cbiAgLy8gTm93IGluIHRoZSBvdmVybGFwIGNvbnRlbnQuIFNjYW4gdGhyb3VnaCBhbmQgc2VsZWN0IHRoZSBiZXN0IGNoYW5nZXMgZnJvbSBlYWNoLlxuICB3aGlsZSAobWluZS5pbmRleCA8IG1pbmUubGluZXMubGVuZ3RoICYmIHRoZWlyLmluZGV4IDwgdGhlaXIubGluZXMubGVuZ3RoKSB7XG4gICAgbGV0IG1pbmVDdXJyZW50ID0gbWluZS5saW5lc1ttaW5lLmluZGV4XSxcbiAgICAgICAgdGhlaXJDdXJyZW50ID0gdGhlaXIubGluZXNbdGhlaXIuaW5kZXhdO1xuXG4gICAgaWYgKChtaW5lQ3VycmVudFswXSA9PT0gJy0nIHx8IG1pbmVDdXJyZW50WzBdID09PSAnKycpXG4gICAgICAgICYmICh0aGVpckN1cnJlbnRbMF0gPT09ICctJyB8fCB0aGVpckN1cnJlbnRbMF0gPT09ICcrJykpIHtcbiAgICAgIC8vIEJvdGggbW9kaWZpZWQgLi4uXG4gICAgICBtdXR1YWxDaGFuZ2UoaHVuaywgbWluZSwgdGhlaXIpO1xuICAgIH0gZWxzZSBpZiAobWluZUN1cnJlbnRbMF0gPT09ICcrJyAmJiB0aGVpckN1cnJlbnRbMF0gPT09ICcgJykge1xuICAgICAgLy8gTWluZSBpbnNlcnRlZFxuICAgICAgaHVuay5saW5lcy5wdXNoKC4uLiBjb2xsZWN0Q2hhbmdlKG1pbmUpKTtcbiAgICB9IGVsc2UgaWYgKHRoZWlyQ3VycmVudFswXSA9PT0gJysnICYmIG1pbmVDdXJyZW50WzBdID09PSAnICcpIHtcbiAgICAgIC8vIFRoZWlycyBpbnNlcnRlZFxuICAgICAgaHVuay5saW5lcy5wdXNoKC4uLiBjb2xsZWN0Q2hhbmdlKHRoZWlyKSk7XG4gICAgfSBlbHNlIGlmIChtaW5lQ3VycmVudFswXSA9PT0gJy0nICYmIHRoZWlyQ3VycmVudFswXSA9PT0gJyAnKSB7XG4gICAgICAvLyBNaW5lIHJlbW92ZWQgb3IgZWRpdGVkXG4gICAgICByZW1vdmFsKGh1bmssIG1pbmUsIHRoZWlyKTtcbiAgICB9IGVsc2UgaWYgKHRoZWlyQ3VycmVudFswXSA9PT0gJy0nICYmIG1pbmVDdXJyZW50WzBdID09PSAnICcpIHtcbiAgICAgIC8vIFRoZWlyIHJlbW92ZWQgb3IgZWRpdGVkXG4gICAgICByZW1vdmFsKGh1bmssIHRoZWlyLCBtaW5lLCB0cnVlKTtcbiAgICB9IGVsc2UgaWYgKG1pbmVDdXJyZW50ID09PSB0aGVpckN1cnJlbnQpIHtcbiAgICAgIC8vIENvbnRleHQgaWRlbnRpdHlcbiAgICAgIGh1bmsubGluZXMucHVzaChtaW5lQ3VycmVudCk7XG4gICAgICBtaW5lLmluZGV4Kys7XG4gICAgICB0aGVpci5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDb250ZXh0IG1pc21hdGNoXG4gICAgICBjb25mbGljdChodW5rLCBjb2xsZWN0Q2hhbmdlKG1pbmUpLCBjb2xsZWN0Q2hhbmdlKHRoZWlyKSk7XG4gICAgfVxuICB9XG5cbiAgLy8gTm93IHB1c2ggYW55dGhpbmcgdGhhdCBtYXkgYmUgcmVtYWluaW5nXG4gIGluc2VydFRyYWlsaW5nKGh1bmssIG1pbmUpO1xuICBpbnNlcnRUcmFpbGluZyhodW5rLCB0aGVpcik7XG5cbiAgY2FsY0xpbmVDb3VudChodW5rKTtcbn1cblxuZnVuY3Rpb24gbXV0dWFsQ2hhbmdlKGh1bmssIG1pbmUsIHRoZWlyKSB7XG4gIGxldCBteUNoYW5nZXMgPSBjb2xsZWN0Q2hhbmdlKG1pbmUpLFxuICAgICAgdGhlaXJDaGFuZ2VzID0gY29sbGVjdENoYW5nZSh0aGVpcik7XG5cbiAgaWYgKGFsbFJlbW92ZXMobXlDaGFuZ2VzKSAmJiBhbGxSZW1vdmVzKHRoZWlyQ2hhbmdlcykpIHtcbiAgICAvLyBTcGVjaWFsIGNhc2UgZm9yIHJlbW92ZSBjaGFuZ2VzIHRoYXQgYXJlIHN1cGVyc2V0cyBvZiBvbmUgYW5vdGhlclxuICAgIGlmIChhcnJheVN0YXJ0c1dpdGgobXlDaGFuZ2VzLCB0aGVpckNoYW5nZXMpXG4gICAgICAgICYmIHNraXBSZW1vdmVTdXBlcnNldCh0aGVpciwgbXlDaGFuZ2VzLCBteUNoYW5nZXMubGVuZ3RoIC0gdGhlaXJDaGFuZ2VzLmxlbmd0aCkpIHtcbiAgICAgIGh1bmsubGluZXMucHVzaCguLi4gbXlDaGFuZ2VzKTtcbiAgICAgIHJldHVybjtcbiAgICB9IGVsc2UgaWYgKGFycmF5U3RhcnRzV2l0aCh0aGVpckNoYW5nZXMsIG15Q2hhbmdlcylcbiAgICAgICAgJiYgc2tpcFJlbW92ZVN1cGVyc2V0KG1pbmUsIHRoZWlyQ2hhbmdlcywgdGhlaXJDaGFuZ2VzLmxlbmd0aCAtIG15Q2hhbmdlcy5sZW5ndGgpKSB7XG4gICAgICBodW5rLmxpbmVzLnB1c2goLi4uIHRoZWlyQ2hhbmdlcyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICB9IGVsc2UgaWYgKGFycmF5RXF1YWwobXlDaGFuZ2VzLCB0aGVpckNoYW5nZXMpKSB7XG4gICAgaHVuay5saW5lcy5wdXNoKC4uLiBteUNoYW5nZXMpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbmZsaWN0KGh1bmssIG15Q2hhbmdlcywgdGhlaXJDaGFuZ2VzKTtcbn1cblxuZnVuY3Rpb24gcmVtb3ZhbChodW5rLCBtaW5lLCB0aGVpciwgc3dhcCkge1xuICBsZXQgbXlDaGFuZ2VzID0gY29sbGVjdENoYW5nZShtaW5lKSxcbiAgICAgIHRoZWlyQ2hhbmdlcyA9IGNvbGxlY3RDb250ZXh0KHRoZWlyLCBteUNoYW5nZXMpO1xuICBpZiAodGhlaXJDaGFuZ2VzLm1lcmdlZCkge1xuICAgIGh1bmsubGluZXMucHVzaCguLi4gdGhlaXJDaGFuZ2VzLm1lcmdlZCk7XG4gIH0gZWxzZSB7XG4gICAgY29uZmxpY3QoaHVuaywgc3dhcCA/IHRoZWlyQ2hhbmdlcyA6IG15Q2hhbmdlcywgc3dhcCA/IG15Q2hhbmdlcyA6IHRoZWlyQ2hhbmdlcyk7XG4gIH1cbn1cblxuZnVuY3Rpb24gY29uZmxpY3QoaHVuaywgbWluZSwgdGhlaXIpIHtcbiAgaHVuay5jb25mbGljdCA9IHRydWU7XG4gIGh1bmsubGluZXMucHVzaCh7XG4gICAgY29uZmxpY3Q6IHRydWUsXG4gICAgbWluZTogbWluZSxcbiAgICB0aGVpcnM6IHRoZWlyXG4gIH0pO1xufVxuXG5mdW5jdGlvbiBpbnNlcnRMZWFkaW5nKGh1bmssIGluc2VydCwgdGhlaXIpIHtcbiAgd2hpbGUgKGluc2VydC5vZmZzZXQgPCB0aGVpci5vZmZzZXQgJiYgaW5zZXJ0LmluZGV4IDwgaW5zZXJ0LmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBsaW5lID0gaW5zZXJ0LmxpbmVzW2luc2VydC5pbmRleCsrXTtcbiAgICBodW5rLmxpbmVzLnB1c2gobGluZSk7XG4gICAgaW5zZXJ0Lm9mZnNldCsrO1xuICB9XG59XG5mdW5jdGlvbiBpbnNlcnRUcmFpbGluZyhodW5rLCBpbnNlcnQpIHtcbiAgd2hpbGUgKGluc2VydC5pbmRleCA8IGluc2VydC5saW5lcy5sZW5ndGgpIHtcbiAgICBsZXQgbGluZSA9IGluc2VydC5saW5lc1tpbnNlcnQuaW5kZXgrK107XG4gICAgaHVuay5saW5lcy5wdXNoKGxpbmUpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGNvbGxlY3RDaGFuZ2Uoc3RhdGUpIHtcbiAgbGV0IHJldCA9IFtdLFxuICAgICAgb3BlcmF0aW9uID0gc3RhdGUubGluZXNbc3RhdGUuaW5kZXhdWzBdO1xuICB3aGlsZSAoc3RhdGUuaW5kZXggPCBzdGF0ZS5saW5lcy5sZW5ndGgpIHtcbiAgICBsZXQgbGluZSA9IHN0YXRlLmxpbmVzW3N0YXRlLmluZGV4XTtcblxuICAgIC8vIEdyb3VwIGFkZGl0aW9ucyB0aGF0IGFyZSBpbW1lZGlhdGVseSBhZnRlciBzdWJ0cmFjdGlvbnMgYW5kIHRyZWF0IHRoZW0gYXMgb25lIFwiYXRvbWljXCIgbW9kaWZ5IGNoYW5nZS5cbiAgICBpZiAob3BlcmF0aW9uID09PSAnLScgJiYgbGluZVswXSA9PT0gJysnKSB7XG4gICAgICBvcGVyYXRpb24gPSAnKyc7XG4gICAgfVxuXG4gICAgaWYgKG9wZXJhdGlvbiA9PT0gbGluZVswXSkge1xuICAgICAgcmV0LnB1c2gobGluZSk7XG4gICAgICBzdGF0ZS5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICBicmVhaztcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmV0O1xufVxuZnVuY3Rpb24gY29sbGVjdENvbnRleHQoc3RhdGUsIG1hdGNoQ2hhbmdlcykge1xuICBsZXQgY2hhbmdlcyA9IFtdLFxuICAgICAgbWVyZ2VkID0gW10sXG4gICAgICBtYXRjaEluZGV4ID0gMCxcbiAgICAgIGNvbnRleHRDaGFuZ2VzID0gZmFsc2UsXG4gICAgICBjb25mbGljdGVkID0gZmFsc2U7XG4gIHdoaWxlIChtYXRjaEluZGV4IDwgbWF0Y2hDaGFuZ2VzLmxlbmd0aFxuICAgICAgICAmJiBzdGF0ZS5pbmRleCA8IHN0YXRlLmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBjaGFuZ2UgPSBzdGF0ZS5saW5lc1tzdGF0ZS5pbmRleF0sXG4gICAgICAgIG1hdGNoID0gbWF0Y2hDaGFuZ2VzW21hdGNoSW5kZXhdO1xuXG4gICAgLy8gT25jZSB3ZSd2ZSBoaXQgb3VyIGFkZCwgdGhlbiB3ZSBhcmUgZG9uZVxuICAgIGlmIChtYXRjaFswXSA9PT0gJysnKSB7XG4gICAgICBicmVhaztcbiAgICB9XG5cbiAgICBjb250ZXh0Q2hhbmdlcyA9IGNvbnRleHRDaGFuZ2VzIHx8IGNoYW5nZVswXSAhPT0gJyAnO1xuXG4gICAgbWVyZ2VkLnB1c2gobWF0Y2gpO1xuICAgIG1hdGNoSW5kZXgrKztcblxuICAgIC8vIENvbnN1bWUgYW55IGFkZGl0aW9ucyBpbiB0aGUgb3RoZXIgYmxvY2sgYXMgYSBjb25mbGljdCB0byBhdHRlbXB0XG4gICAgLy8gdG8gcHVsbCBpbiB0aGUgcmVtYWluaW5nIGNvbnRleHQgYWZ0ZXIgdGhpc1xuICAgIGlmIChjaGFuZ2VbMF0gPT09ICcrJykge1xuICAgICAgY29uZmxpY3RlZCA9IHRydWU7XG5cbiAgICAgIHdoaWxlIChjaGFuZ2VbMF0gPT09ICcrJykge1xuICAgICAgICBjaGFuZ2VzLnB1c2goY2hhbmdlKTtcbiAgICAgICAgY2hhbmdlID0gc3RhdGUubGluZXNbKytzdGF0ZS5pbmRleF07XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG1hdGNoLnN1YnN0cigxKSA9PT0gY2hhbmdlLnN1YnN0cigxKSkge1xuICAgICAgY2hhbmdlcy5wdXNoKGNoYW5nZSk7XG4gICAgICBzdGF0ZS5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25mbGljdGVkID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBpZiAoKG1hdGNoQ2hhbmdlc1ttYXRjaEluZGV4XSB8fCAnJylbMF0gPT09ICcrJ1xuICAgICAgJiYgY29udGV4dENoYW5nZXMpIHtcbiAgICBjb25mbGljdGVkID0gdHJ1ZTtcbiAgfVxuXG4gIGlmIChjb25mbGljdGVkKSB7XG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICB3aGlsZSAobWF0Y2hJbmRleCA8IG1hdGNoQ2hhbmdlcy5sZW5ndGgpIHtcbiAgICBtZXJnZWQucHVzaChtYXRjaENoYW5nZXNbbWF0Y2hJbmRleCsrXSk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG1lcmdlZCxcbiAgICBjaGFuZ2VzXG4gIH07XG59XG5cbmZ1bmN0aW9uIGFsbFJlbW92ZXMoY2hhbmdlcykge1xuICByZXR1cm4gY2hhbmdlcy5yZWR1Y2UoZnVuY3Rpb24ocHJldiwgY2hhbmdlKSB7XG4gICAgcmV0dXJuIHByZXYgJiYgY2hhbmdlWzBdID09PSAnLSc7XG4gIH0sIHRydWUpO1xufVxuZnVuY3Rpb24gc2tpcFJlbW92ZVN1cGVyc2V0KHN0YXRlLCByZW1vdmVDaGFuZ2VzLCBkZWx0YSkge1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGRlbHRhOyBpKyspIHtcbiAgICBsZXQgY2hhbmdlQ29udGVudCA9IHJlbW92ZUNoYW5nZXNbcmVtb3ZlQ2hhbmdlcy5sZW5ndGggLSBkZWx0YSArIGldLnN1YnN0cigxKTtcbiAgICBpZiAoc3RhdGUubGluZXNbc3RhdGUuaW5kZXggKyBpXSAhPT0gJyAnICsgY2hhbmdlQ29udGVudCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHN0YXRlLmluZGV4ICs9IGRlbHRhO1xuICByZXR1cm4gdHJ1ZTtcbn1cblxuZnVuY3Rpb24gY2FsY09sZE5ld0xpbmVDb3VudChsaW5lcykge1xuICBsZXQgb2xkTGluZXMgPSAwO1xuICBsZXQgbmV3TGluZXMgPSAwO1xuXG4gIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgIGlmICh0eXBlb2YgbGluZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIGxldCBteUNvdW50ID0gY2FsY09sZE5ld0xpbmVDb3VudChsaW5lLm1pbmUpO1xuICAgICAgbGV0IHRoZWlyQ291bnQgPSBjYWxjT2xkTmV3TGluZUNvdW50KGxpbmUudGhlaXJzKTtcblxuICAgICAgaWYgKG9sZExpbmVzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKG15Q291bnQub2xkTGluZXMgPT09IHRoZWlyQ291bnQub2xkTGluZXMpIHtcbiAgICAgICAgICBvbGRMaW5lcyArPSBteUNvdW50Lm9sZExpbmVzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9sZExpbmVzID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChuZXdMaW5lcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmIChteUNvdW50Lm5ld0xpbmVzID09PSB0aGVpckNvdW50Lm5ld0xpbmVzKSB7XG4gICAgICAgICAgbmV3TGluZXMgKz0gbXlDb3VudC5uZXdMaW5lcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBuZXdMaW5lcyA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAobmV3TGluZXMgIT09IHVuZGVmaW5lZCAmJiAobGluZVswXSA9PT0gJysnIHx8IGxpbmVbMF0gPT09ICcgJykpIHtcbiAgICAgICAgbmV3TGluZXMrKztcbiAgICAgIH1cbiAgICAgIGlmIChvbGRMaW5lcyAhPT0gdW5kZWZpbmVkICYmIChsaW5lWzBdID09PSAnLScgfHwgbGluZVswXSA9PT0gJyAnKSkge1xuICAgICAgICBvbGRMaW5lcysrO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIHtvbGRMaW5lcywgbmV3TGluZXN9O1xufVxuIl19 + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/structuredPatch = structuredPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = createTwoFilesPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = createPatch; + + var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + + /*istanbul ignore end*/function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + if (!options) { + options = {}; + } + if (typeof options.context === 'undefined') { + options.context = 4; + } + + var diff = /*istanbul ignore start*/(0, _line.diffLines) /*istanbul ignore end*/(oldStr, newStr, options); + diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + + var hunks = []; + var oldRangeStart = 0, + newRangeStart = 0, + curRange = [], + oldLine = 1, + newLine = 1; + + /*istanbul ignore start*/var _loop = function _loop( /*istanbul ignore end*/i) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + /*istanbul ignore start*/var _curRange; + + /*istanbul ignore end*/ // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + + // Output our changes + /*istanbul ignore start*/(_curRange = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); + + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + /*istanbul ignore start*/var _curRange2; + + /*istanbul ignore end*/ // Overlapping + /*istanbul ignore start*/(_curRange2 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines))); + } else { + /*istanbul ignore start*/var _curRange3; + + /*istanbul ignore end*/ // end the range and output + var contextSize = Math.min(lines.length, options.context); + /*istanbul ignore start*/(_curRange3 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines.slice(0, contextSize)))); + + var hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + if (i >= diff.length - 2 && lines.length <= options.context) { + // EOF is inside this hunk + var oldEOFNewline = /\n$/.test(oldStr); + var newEOFNewline = /\n$/.test(newStr); + if (lines.length == 0 && !oldEOFNewline) { + // special case: old has no eol and no trailing context; no-nl can end up before adds + curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); + } else if (!oldEOFNewline || !newEOFNewline) { + curRange.push('\\ No newline at end of file'); + } + } + hunks.push(hunk); + + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + }; + + for (var i = 0; i < diff.length; i++) { + /*istanbul ignore start*/_loop( /*istanbul ignore end*/i); + } + + return { + oldFileName: oldFileName, newFileName: newFileName, + oldHeader: oldHeader, newHeader: newHeader, + hunks: hunks + }; + } + + function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + + var ret = []; + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } + ret.push('==================================================================='); + ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); + ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); + + for (var i = 0; i < diff.hunks.length; i++) { + var hunk = diff.hunks[i]; + ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); + ret.push.apply(ret, hunk.lines); + } + + return ret.join('\n') + '\n'; + } + + function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { + return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9jcmVhdGUuanMiXSwibmFtZXMiOlsic3RydWN0dXJlZFBhdGNoIiwiY3JlYXRlVHdvRmlsZXNQYXRjaCIsImNyZWF0ZVBhdGNoIiwib2xkRmlsZU5hbWUiLCJuZXdGaWxlTmFtZSIsIm9sZFN0ciIsIm5ld1N0ciIsIm9sZEhlYWRlciIsIm5ld0hlYWRlciIsIm9wdGlvbnMiLCJjb250ZXh0IiwiZGlmZiIsInB1c2giLCJ2YWx1ZSIsImxpbmVzIiwiY29udGV4dExpbmVzIiwibWFwIiwiZW50cnkiLCJodW5rcyIsIm9sZFJhbmdlU3RhcnQiLCJuZXdSYW5nZVN0YXJ0IiwiY3VyUmFuZ2UiLCJvbGRMaW5lIiwibmV3TGluZSIsImkiLCJjdXJyZW50IiwicmVwbGFjZSIsInNwbGl0IiwiYWRkZWQiLCJyZW1vdmVkIiwicHJldiIsInNsaWNlIiwibGVuZ3RoIiwiY29udGV4dFNpemUiLCJNYXRoIiwibWluIiwiaHVuayIsIm9sZFN0YXJ0Iiwib2xkTGluZXMiLCJuZXdTdGFydCIsIm5ld0xpbmVzIiwib2xkRU9GTmV3bGluZSIsInRlc3QiLCJuZXdFT0ZOZXdsaW5lIiwic3BsaWNlIiwicmV0IiwiYXBwbHkiLCJqb2luIiwiZmlsZU5hbWUiXSwibWFwcGluZ3MiOiI7OztnQ0FFZ0JBLGUsR0FBQUEsZTt5REFpR0FDLG1CLEdBQUFBLG1CO3lEQXdCQUMsVyxHQUFBQSxXOztBQTNIaEI7Ozs7dUJBRU8sU0FBU0YsZUFBVCxDQUF5QkcsV0FBekIsRUFBc0NDLFdBQXRDLEVBQW1EQyxNQUFuRCxFQUEyREMsTUFBM0QsRUFBbUVDLFNBQW5FLEVBQThFQyxTQUE5RSxFQUF5RkMsT0FBekYsRUFBa0c7QUFDdkcsTUFBSSxDQUFDQSxPQUFMLEVBQWM7QUFDWkEsY0FBVSxFQUFWO0FBQ0Q7QUFDRCxNQUFJLE9BQU9BLFFBQVFDLE9BQWYsS0FBMkIsV0FBL0IsRUFBNEM7QUFDMUNELFlBQVFDLE9BQVIsR0FBa0IsQ0FBbEI7QUFDRDs7QUFFRCxNQUFNQyxPQUFPLHNFQUFVTixNQUFWLEVBQWtCQyxNQUFsQixFQUEwQkcsT0FBMUIsQ0FBYjtBQUNBRSxPQUFLQyxJQUFMLENBQVUsRUFBQ0MsT0FBTyxFQUFSLEVBQVlDLE9BQU8sRUFBbkIsRUFBVixFQVR1RyxDQVNsRTs7QUFFckMsV0FBU0MsWUFBVCxDQUFzQkQsS0FBdEIsRUFBNkI7QUFDM0IsV0FBT0EsTUFBTUUsR0FBTixDQUFVLFVBQVNDLEtBQVQsRUFBZ0I7QUFBRSxhQUFPLE1BQU1BLEtBQWI7QUFBcUIsS0FBakQsQ0FBUDtBQUNEOztBQUVELE1BQUlDLFFBQVEsRUFBWjtBQUNBLE1BQUlDLGdCQUFnQixDQUFwQjtBQUFBLE1BQXVCQyxnQkFBZ0IsQ0FBdkM7QUFBQSxNQUEwQ0MsV0FBVyxFQUFyRDtBQUFBLE1BQ0lDLFVBQVUsQ0FEZDtBQUFBLE1BQ2lCQyxVQUFVLENBRDNCOztBQWhCdUcsOEVBa0I5RkMsQ0FsQjhGO0FBbUJyRyxRQUFNQyxVQUFVZCxLQUFLYSxDQUFMLENBQWhCO0FBQUEsUUFDTVYsUUFBUVcsUUFBUVgsS0FBUixJQUFpQlcsUUFBUVosS0FBUixDQUFjYSxPQUFkLENBQXNCLEtBQXRCLEVBQTZCLEVBQTdCLEVBQWlDQyxLQUFqQyxDQUF1QyxJQUF2QyxDQUQvQjtBQUVBRixZQUFRWCxLQUFSLEdBQWdCQSxLQUFoQjs7QUFFQSxRQUFJVyxRQUFRRyxLQUFSLElBQWlCSCxRQUFRSSxPQUE3QixFQUFzQztBQUFBOztBQUFBLDhCQUNwQztBQUNBLFVBQUksQ0FBQ1YsYUFBTCxFQUFvQjtBQUNsQixZQUFNVyxPQUFPbkIsS0FBS2EsSUFBSSxDQUFULENBQWI7QUFDQUwsd0JBQWdCRyxPQUFoQjtBQUNBRix3QkFBZ0JHLE9BQWhCOztBQUVBLFlBQUlPLElBQUosRUFBVTtBQUNSVCxxQkFBV1osUUFBUUMsT0FBUixHQUFrQixDQUFsQixHQUFzQkssYUFBYWUsS0FBS2hCLEtBQUwsQ0FBV2lCLEtBQVgsQ0FBaUIsQ0FBQ3RCLFFBQVFDLE9BQTFCLENBQWIsQ0FBdEIsR0FBeUUsRUFBcEY7QUFDQVMsMkJBQWlCRSxTQUFTVyxNQUExQjtBQUNBWiwyQkFBaUJDLFNBQVNXLE1BQTFCO0FBQ0Q7QUFDRjs7QUFFRDtBQUNBLDZFQUFTcEIsSUFBVCwwTEFBa0JFLE1BQU1FLEdBQU4sQ0FBVSxVQUFTQyxLQUFULEVBQWdCO0FBQzFDLGVBQU8sQ0FBQ1EsUUFBUUcsS0FBUixHQUFnQixHQUFoQixHQUFzQixHQUF2QixJQUE4QlgsS0FBckM7QUFDRCxPQUZpQixDQUFsQjs7QUFJQTtBQUNBLFVBQUlRLFFBQVFHLEtBQVosRUFBbUI7QUFDakJMLG1CQUFXVCxNQUFNa0IsTUFBakI7QUFDRCxPQUZELE1BRU87QUFDTFYsbUJBQVdSLE1BQU1rQixNQUFqQjtBQUNEO0FBQ0YsS0F6QkQsTUF5Qk87QUFDTDtBQUNBLFVBQUliLGFBQUosRUFBbUI7QUFDakI7QUFDQSxZQUFJTCxNQUFNa0IsTUFBTixJQUFnQnZCLFFBQVFDLE9BQVIsR0FBa0IsQ0FBbEMsSUFBdUNjLElBQUliLEtBQUtxQixNQUFMLEdBQWMsQ0FBN0QsRUFBZ0U7QUFBQTs7QUFBQSxrQ0FDOUQ7QUFDQSxrRkFBU3BCLElBQVQsMkxBQWtCRyxhQUFhRCxLQUFiLENBQWxCO0FBQ0QsU0FIRCxNQUdPO0FBQUE7O0FBQUEsa0NBQ0w7QUFDQSxjQUFJbUIsY0FBY0MsS0FBS0MsR0FBTCxDQUFTckIsTUFBTWtCLE1BQWYsRUFBdUJ2QixRQUFRQyxPQUEvQixDQUFsQjtBQUNBLGtGQUFTRSxJQUFULDJMQUFrQkcsYUFBYUQsTUFBTWlCLEtBQU4sQ0FBWSxDQUFaLEVBQWVFLFdBQWYsQ0FBYixDQUFsQjs7QUFFQSxjQUFJRyxPQUFPO0FBQ1RDLHNCQUFVbEIsYUFERDtBQUVUbUIsc0JBQVdoQixVQUFVSCxhQUFWLEdBQTBCYyxXQUY1QjtBQUdUTSxzQkFBVW5CLGFBSEQ7QUFJVG9CLHNCQUFXakIsVUFBVUgsYUFBVixHQUEwQmEsV0FKNUI7QUFLVG5CLG1CQUFPTztBQUxFLFdBQVg7QUFPQSxjQUFJRyxLQUFLYixLQUFLcUIsTUFBTCxHQUFjLENBQW5CLElBQXdCbEIsTUFBTWtCLE1BQU4sSUFBZ0J2QixRQUFRQyxPQUFwRCxFQUE2RDtBQUMzRDtBQUNBLGdCQUFJK0IsZ0JBQWlCLE1BQU1DLElBQU4sQ0FBV3JDLE1BQVgsQ0FBckI7QUFDQSxnQkFBSXNDLGdCQUFpQixNQUFNRCxJQUFOLENBQVdwQyxNQUFYLENBQXJCO0FBQ0EsZ0JBQUlRLE1BQU1rQixNQUFOLElBQWdCLENBQWhCLElBQXFCLENBQUNTLGFBQTFCLEVBQXlDO0FBQ3ZDO0FBQ0FwQix1QkFBU3VCLE1BQVQsQ0FBZ0JSLEtBQUtFLFFBQXJCLEVBQStCLENBQS9CLEVBQWtDLDhCQUFsQztBQUNELGFBSEQsTUFHTyxJQUFJLENBQUNHLGFBQUQsSUFBa0IsQ0FBQ0UsYUFBdkIsRUFBc0M7QUFDM0N0Qix1QkFBU1QsSUFBVCxDQUFjLDhCQUFkO0FBQ0Q7QUFDRjtBQUNETSxnQkFBTU4sSUFBTixDQUFXd0IsSUFBWDs7QUFFQWpCLDBCQUFnQixDQUFoQjtBQUNBQywwQkFBZ0IsQ0FBaEI7QUFDQUMscUJBQVcsRUFBWDtBQUNEO0FBQ0Y7QUFDREMsaUJBQVdSLE1BQU1rQixNQUFqQjtBQUNBVCxpQkFBV1QsTUFBTWtCLE1BQWpCO0FBQ0Q7QUF2Rm9HOztBQWtCdkcsT0FBSyxJQUFJUixJQUFJLENBQWIsRUFBZ0JBLElBQUliLEtBQUtxQixNQUF6QixFQUFpQ1IsR0FBakMsRUFBc0M7QUFBQSwyREFBN0JBLENBQTZCO0FBc0VyQzs7QUFFRCxTQUFPO0FBQ0xyQixpQkFBYUEsV0FEUixFQUNxQkMsYUFBYUEsV0FEbEM7QUFFTEcsZUFBV0EsU0FGTixFQUVpQkMsV0FBV0EsU0FGNUI7QUFHTFUsV0FBT0E7QUFIRixHQUFQO0FBS0Q7O0FBRU0sU0FBU2pCLG1CQUFULENBQTZCRSxXQUE3QixFQUEwQ0MsV0FBMUMsRUFBdURDLE1BQXZELEVBQStEQyxNQUEvRCxFQUF1RUMsU0FBdkUsRUFBa0ZDLFNBQWxGLEVBQTZGQyxPQUE3RixFQUFzRztBQUMzRyxNQUFNRSxPQUFPWCxnQkFBZ0JHLFdBQWhCLEVBQTZCQyxXQUE3QixFQUEwQ0MsTUFBMUMsRUFBa0RDLE1BQWxELEVBQTBEQyxTQUExRCxFQUFxRUMsU0FBckUsRUFBZ0ZDLE9BQWhGLENBQWI7O0FBRUEsTUFBTW9DLE1BQU0sRUFBWjtBQUNBLE1BQUkxQyxlQUFlQyxXQUFuQixFQUFnQztBQUM5QnlDLFFBQUlqQyxJQUFKLENBQVMsWUFBWVQsV0FBckI7QUFDRDtBQUNEMEMsTUFBSWpDLElBQUosQ0FBUyxxRUFBVDtBQUNBaUMsTUFBSWpDLElBQUosQ0FBUyxTQUFTRCxLQUFLUixXQUFkLElBQTZCLE9BQU9RLEtBQUtKLFNBQVosS0FBMEIsV0FBMUIsR0FBd0MsRUFBeEMsR0FBNkMsT0FBT0ksS0FBS0osU0FBdEYsQ0FBVDtBQUNBc0MsTUFBSWpDLElBQUosQ0FBUyxTQUFTRCxLQUFLUCxXQUFkLElBQTZCLE9BQU9PLEtBQUtILFNBQVosS0FBMEIsV0FBMUIsR0FBd0MsRUFBeEMsR0FBNkMsT0FBT0csS0FBS0gsU0FBdEYsQ0FBVDs7QUFFQSxPQUFLLElBQUlnQixJQUFJLENBQWIsRUFBZ0JBLElBQUliLEtBQUtPLEtBQUwsQ0FBV2MsTUFBL0IsRUFBdUNSLEdBQXZDLEVBQTRDO0FBQzFDLFFBQU1ZLE9BQU96QixLQUFLTyxLQUFMLENBQVdNLENBQVgsQ0FBYjtBQUNBcUIsUUFBSWpDLElBQUosQ0FDRSxTQUFTd0IsS0FBS0MsUUFBZCxHQUF5QixHQUF6QixHQUErQkQsS0FBS0UsUUFBcEMsR0FDRSxJQURGLEdBQ1NGLEtBQUtHLFFBRGQsR0FDeUIsR0FEekIsR0FDK0JILEtBQUtJLFFBRHBDLEdBRUUsS0FISjtBQUtBSyxRQUFJakMsSUFBSixDQUFTa0MsS0FBVCxDQUFlRCxHQUFmLEVBQW9CVCxLQUFLdEIsS0FBekI7QUFDRDs7QUFFRCxTQUFPK0IsSUFBSUUsSUFBSixDQUFTLElBQVQsSUFBaUIsSUFBeEI7QUFDRDs7QUFFTSxTQUFTN0MsV0FBVCxDQUFxQjhDLFFBQXJCLEVBQStCM0MsTUFBL0IsRUFBdUNDLE1BQXZDLEVBQStDQyxTQUEvQyxFQUEwREMsU0FBMUQsRUFBcUVDLE9BQXJFLEVBQThFO0FBQ25GLFNBQU9SLG9CQUFvQitDLFFBQXBCLEVBQThCQSxRQUE5QixFQUF3QzNDLE1BQXhDLEVBQWdEQyxNQUFoRCxFQUF3REMsU0FBeEQsRUFBbUVDLFNBQW5FLEVBQThFQyxPQUE5RSxDQUFQO0FBQ0QiLCJmaWxlIjoiY3JlYXRlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtkaWZmTGluZXN9IGZyb20gJy4uL2RpZmYvbGluZSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBzdHJ1Y3R1cmVkUGF0Y2gob2xkRmlsZU5hbWUsIG5ld0ZpbGVOYW1lLCBvbGRTdHIsIG5ld1N0ciwgb2xkSGVhZGVyLCBuZXdIZWFkZXIsIG9wdGlvbnMpIHtcbiAgaWYgKCFvcHRpb25zKSB7XG4gICAgb3B0aW9ucyA9IHt9O1xuICB9XG4gIGlmICh0eXBlb2Ygb3B0aW9ucy5jb250ZXh0ID09PSAndW5kZWZpbmVkJykge1xuICAgIG9wdGlvbnMuY29udGV4dCA9IDQ7XG4gIH1cblxuICBjb25zdCBkaWZmID0gZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbiAgZGlmZi5wdXNoKHt2YWx1ZTogJycsIGxpbmVzOiBbXX0pOyAgIC8vIEFwcGVuZCBhbiBlbXB0eSB2YWx1ZSB0byBtYWtlIGNsZWFudXAgZWFzaWVyXG5cbiAgZnVuY3Rpb24gY29udGV4dExpbmVzKGxpbmVzKSB7XG4gICAgcmV0dXJuIGxpbmVzLm1hcChmdW5jdGlvbihlbnRyeSkgeyByZXR1cm4gJyAnICsgZW50cnk7IH0pO1xuICB9XG5cbiAgbGV0IGh1bmtzID0gW107XG4gIGxldCBvbGRSYW5nZVN0YXJ0ID0gMCwgbmV3UmFuZ2VTdGFydCA9IDAsIGN1clJhbmdlID0gW10sXG4gICAgICBvbGRMaW5lID0gMSwgbmV3TGluZSA9IDE7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGlmZi5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGN1cnJlbnQgPSBkaWZmW2ldLFxuICAgICAgICAgIGxpbmVzID0gY3VycmVudC5saW5lcyB8fCBjdXJyZW50LnZhbHVlLnJlcGxhY2UoL1xcbiQvLCAnJykuc3BsaXQoJ1xcbicpO1xuICAgIGN1cnJlbnQubGluZXMgPSBsaW5lcztcblxuICAgIGlmIChjdXJyZW50LmFkZGVkIHx8IGN1cnJlbnQucmVtb3ZlZCkge1xuICAgICAgLy8gSWYgd2UgaGF2ZSBwcmV2aW91cyBjb250ZXh0LCBzdGFydCB3aXRoIHRoYXRcbiAgICAgIGlmICghb2xkUmFuZ2VTdGFydCkge1xuICAgICAgICBjb25zdCBwcmV2ID0gZGlmZltpIC0gMV07XG4gICAgICAgIG9sZFJhbmdlU3RhcnQgPSBvbGRMaW5lO1xuICAgICAgICBuZXdSYW5nZVN0YXJ0ID0gbmV3TGluZTtcblxuICAgICAgICBpZiAocHJldikge1xuICAgICAgICAgIGN1clJhbmdlID0gb3B0aW9ucy5jb250ZXh0ID4gMCA/IGNvbnRleHRMaW5lcyhwcmV2LmxpbmVzLnNsaWNlKC1vcHRpb25zLmNvbnRleHQpKSA6IFtdO1xuICAgICAgICAgIG9sZFJhbmdlU3RhcnQgLT0gY3VyUmFuZ2UubGVuZ3RoO1xuICAgICAgICAgIG5ld1JhbmdlU3RhcnQgLT0gY3VyUmFuZ2UubGVuZ3RoO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIE91dHB1dCBvdXIgY2hhbmdlc1xuICAgICAgY3VyUmFuZ2UucHVzaCguLi4gbGluZXMubWFwKGZ1bmN0aW9uKGVudHJ5KSB7XG4gICAgICAgIHJldHVybiAoY3VycmVudC5hZGRlZCA/ICcrJyA6ICctJykgKyBlbnRyeTtcbiAgICAgIH0pKTtcblxuICAgICAgLy8gVHJhY2sgdGhlIHVwZGF0ZWQgZmlsZSBwb3NpdGlvblxuICAgICAgaWYgKGN1cnJlbnQuYWRkZWQpIHtcbiAgICAgICAgbmV3TGluZSArPSBsaW5lcy5sZW5ndGg7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvbGRMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gSWRlbnRpY2FsIGNvbnRleHQgbGluZXMuIFRyYWNrIGxpbmUgY2hhbmdlc1xuICAgICAgaWYgKG9sZFJhbmdlU3RhcnQpIHtcbiAgICAgICAgLy8gQ2xvc2Ugb3V0IGFueSBjaGFuZ2VzIHRoYXQgaGF2ZSBiZWVuIG91dHB1dCAob3Igam9pbiBvdmVybGFwcGluZylcbiAgICAgICAgaWYgKGxpbmVzLmxlbmd0aCA8PSBvcHRpb25zLmNvbnRleHQgKiAyICYmIGkgPCBkaWZmLmxlbmd0aCAtIDIpIHtcbiAgICAgICAgICAvLyBPdmVybGFwcGluZ1xuICAgICAgICAgIGN1clJhbmdlLnB1c2goLi4uIGNvbnRleHRMaW5lcyhsaW5lcykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGVuZCB0aGUgcmFuZ2UgYW5kIG91dHB1dFxuICAgICAgICAgIGxldCBjb250ZXh0U2l6ZSA9IE1hdGgubWluKGxpbmVzLmxlbmd0aCwgb3B0aW9ucy5jb250ZXh0KTtcbiAgICAgICAgICBjdXJSYW5nZS5wdXNoKC4uLiBjb250ZXh0TGluZXMobGluZXMuc2xpY2UoMCwgY29udGV4dFNpemUpKSk7XG5cbiAgICAgICAgICBsZXQgaHVuayA9IHtcbiAgICAgICAgICAgIG9sZFN0YXJ0OiBvbGRSYW5nZVN0YXJ0LFxuICAgICAgICAgICAgb2xkTGluZXM6IChvbGRMaW5lIC0gb2xkUmFuZ2VTdGFydCArIGNvbnRleHRTaXplKSxcbiAgICAgICAgICAgIG5ld1N0YXJ0OiBuZXdSYW5nZVN0YXJ0LFxuICAgICAgICAgICAgbmV3TGluZXM6IChuZXdMaW5lIC0gbmV3UmFuZ2VTdGFydCArIGNvbnRleHRTaXplKSxcbiAgICAgICAgICAgIGxpbmVzOiBjdXJSYW5nZVxuICAgICAgICAgIH07XG4gICAgICAgICAgaWYgKGkgPj0gZGlmZi5sZW5ndGggLSAyICYmIGxpbmVzLmxlbmd0aCA8PSBvcHRpb25zLmNvbnRleHQpIHtcbiAgICAgICAgICAgIC8vIEVPRiBpcyBpbnNpZGUgdGhpcyBodW5rXG4gICAgICAgICAgICBsZXQgb2xkRU9GTmV3bGluZSA9ICgvXFxuJC8udGVzdChvbGRTdHIpKTtcbiAgICAgICAgICAgIGxldCBuZXdFT0ZOZXdsaW5lID0gKC9cXG4kLy50ZXN0KG5ld1N0cikpO1xuICAgICAgICAgICAgaWYgKGxpbmVzLmxlbmd0aCA9PSAwICYmICFvbGRFT0ZOZXdsaW5lKSB7XG4gICAgICAgICAgICAgIC8vIHNwZWNpYWwgY2FzZTogb2xkIGhhcyBubyBlb2wgYW5kIG5vIHRyYWlsaW5nIGNvbnRleHQ7IG5vLW5sIGNhbiBlbmQgdXAgYmVmb3JlIGFkZHNcbiAgICAgICAgICAgICAgY3VyUmFuZ2Uuc3BsaWNlKGh1bmsub2xkTGluZXMsIDAsICdcXFxcIE5vIG5ld2xpbmUgYXQgZW5kIG9mIGZpbGUnKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIW9sZEVPRk5ld2xpbmUgfHwgIW5ld0VPRk5ld2xpbmUpIHtcbiAgICAgICAgICAgICAgY3VyUmFuZ2UucHVzaCgnXFxcXCBObyBuZXdsaW5lIGF0IGVuZCBvZiBmaWxlJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGh1bmtzLnB1c2goaHVuayk7XG5cbiAgICAgICAgICBvbGRSYW5nZVN0YXJ0ID0gMDtcbiAgICAgICAgICBuZXdSYW5nZVN0YXJ0ID0gMDtcbiAgICAgICAgICBjdXJSYW5nZSA9IFtdO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBvbGRMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgIG5ld0xpbmUgKz0gbGluZXMubGVuZ3RoO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgb2xkRmlsZU5hbWU6IG9sZEZpbGVOYW1lLCBuZXdGaWxlTmFtZTogbmV3RmlsZU5hbWUsXG4gICAgb2xkSGVhZGVyOiBvbGRIZWFkZXIsIG5ld0hlYWRlcjogbmV3SGVhZGVyLFxuICAgIGh1bmtzOiBodW5rc1xuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVHdvRmlsZXNQYXRjaChvbGRGaWxlTmFtZSwgbmV3RmlsZU5hbWUsIG9sZFN0ciwgbmV3U3RyLCBvbGRIZWFkZXIsIG5ld0hlYWRlciwgb3B0aW9ucykge1xuICBjb25zdCBkaWZmID0gc3RydWN0dXJlZFBhdGNoKG9sZEZpbGVOYW1lLCBuZXdGaWxlTmFtZSwgb2xkU3RyLCBuZXdTdHIsIG9sZEhlYWRlciwgbmV3SGVhZGVyLCBvcHRpb25zKTtcblxuICBjb25zdCByZXQgPSBbXTtcbiAgaWYgKG9sZEZpbGVOYW1lID09IG5ld0ZpbGVOYW1lKSB7XG4gICAgcmV0LnB1c2goJ0luZGV4OiAnICsgb2xkRmlsZU5hbWUpO1xuICB9XG4gIHJldC5wdXNoKCc9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Jyk7XG4gIHJldC5wdXNoKCctLS0gJyArIGRpZmYub2xkRmlsZU5hbWUgKyAodHlwZW9mIGRpZmYub2xkSGVhZGVyID09PSAndW5kZWZpbmVkJyA/ICcnIDogJ1xcdCcgKyBkaWZmLm9sZEhlYWRlcikpO1xuICByZXQucHVzaCgnKysrICcgKyBkaWZmLm5ld0ZpbGVOYW1lICsgKHR5cGVvZiBkaWZmLm5ld0hlYWRlciA9PT0gJ3VuZGVmaW5lZCcgPyAnJyA6ICdcXHQnICsgZGlmZi5uZXdIZWFkZXIpKTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IGRpZmYuaHVua3MubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBodW5rID0gZGlmZi5odW5rc1tpXTtcbiAgICByZXQucHVzaChcbiAgICAgICdAQCAtJyArIGh1bmsub2xkU3RhcnQgKyAnLCcgKyBodW5rLm9sZExpbmVzXG4gICAgICArICcgKycgKyBodW5rLm5ld1N0YXJ0ICsgJywnICsgaHVuay5uZXdMaW5lc1xuICAgICAgKyAnIEBAJ1xuICAgICk7XG4gICAgcmV0LnB1c2guYXBwbHkocmV0LCBodW5rLmxpbmVzKTtcbiAgfVxuXG4gIHJldHVybiByZXQuam9pbignXFxuJykgKyAnXFxuJztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVBhdGNoKGZpbGVOYW1lLCBvbGRTdHIsIG5ld1N0ciwgb2xkSGVhZGVyLCBuZXdIZWFkZXIsIG9wdGlvbnMpIHtcbiAgcmV0dXJuIGNyZWF0ZVR3b0ZpbGVzUGF0Y2goZmlsZU5hbWUsIGZpbGVOYW1lLCBvbGRTdHIsIG5ld1N0ciwgb2xkSGVhZGVyLCBuZXdIZWFkZXIsIG9wdGlvbnMpO1xufVxuIl19 + + +/***/ }), +/* 15 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/"use strict"; + + exports.__esModule = true; + exports. /*istanbul ignore end*/arrayEqual = arrayEqual; + /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayStartsWith = arrayStartsWith; + function arrayEqual(a, b) { + if (a.length !== b.length) { + return false; + } + + return arrayStartsWith(a, b); + } + + function arrayStartsWith(array, start) { + if (start.length > array.length) { + return false; + } + + for (var i = 0; i < start.length; i++) { + if (start[i] !== array[i]) { + return false; + } + } + + return true; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL2FycmF5LmpzIl0sIm5hbWVzIjpbImFycmF5RXF1YWwiLCJhcnJheVN0YXJ0c1dpdGgiLCJhIiwiYiIsImxlbmd0aCIsImFycmF5Iiwic3RhcnQiLCJpIl0sIm1hcHBpbmdzIjoiOzs7Z0NBQWdCQSxVLEdBQUFBLFU7eURBUUFDLGUsR0FBQUEsZTtBQVJULFNBQVNELFVBQVQsQ0FBb0JFLENBQXBCLEVBQXVCQyxDQUF2QixFQUEwQjtBQUMvQixNQUFJRCxFQUFFRSxNQUFGLEtBQWFELEVBQUVDLE1BQW5CLEVBQTJCO0FBQ3pCLFdBQU8sS0FBUDtBQUNEOztBQUVELFNBQU9ILGdCQUFnQkMsQ0FBaEIsRUFBbUJDLENBQW5CLENBQVA7QUFDRDs7QUFFTSxTQUFTRixlQUFULENBQXlCSSxLQUF6QixFQUFnQ0MsS0FBaEMsRUFBdUM7QUFDNUMsTUFBSUEsTUFBTUYsTUFBTixHQUFlQyxNQUFNRCxNQUF6QixFQUFpQztBQUMvQixXQUFPLEtBQVA7QUFDRDs7QUFFRCxPQUFLLElBQUlHLElBQUksQ0FBYixFQUFnQkEsSUFBSUQsTUFBTUYsTUFBMUIsRUFBa0NHLEdBQWxDLEVBQXVDO0FBQ3JDLFFBQUlELE1BQU1DLENBQU4sTUFBYUYsTUFBTUUsQ0FBTixDQUFqQixFQUEyQjtBQUN6QixhQUFPLEtBQVA7QUFDRDtBQUNGOztBQUVELFNBQU8sSUFBUDtBQUNEIiwiZmlsZSI6ImFycmF5LmpzIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIGFycmF5RXF1YWwoYSwgYikge1xuICBpZiAoYS5sZW5ndGggIT09IGIubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcmV0dXJuIGFycmF5U3RhcnRzV2l0aChhLCBiKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFycmF5U3RhcnRzV2l0aChhcnJheSwgc3RhcnQpIHtcbiAgaWYgKHN0YXJ0Lmxlbmd0aCA+IGFycmF5Lmxlbmd0aCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgc3RhcnQubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoc3RhcnRbaV0gIT09IGFycmF5W2ldKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59XG4iXX0= + + +/***/ }), +/* 16 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/"use strict"; + + exports.__esModule = true; + exports. /*istanbul ignore end*/convertChangesToDMP = convertChangesToDMP; + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + function convertChangesToDMP(changes) { + var ret = [], + change = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + operation = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (var i = 0; i < changes.length; i++) { + change = changes[i]; + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; + } + + ret.push([operation, change.value]); + } + return ret; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb252ZXJ0L2RtcC5qcyJdLCJuYW1lcyI6WyJjb252ZXJ0Q2hhbmdlc1RvRE1QIiwiY2hhbmdlcyIsInJldCIsImNoYW5nZSIsIm9wZXJhdGlvbiIsImkiLCJsZW5ndGgiLCJhZGRlZCIsInJlbW92ZWQiLCJwdXNoIiwidmFsdWUiXSwibWFwcGluZ3MiOiI7OztnQ0FDZ0JBLG1CLEdBQUFBLG1CO0FBRGhCO0FBQ08sU0FBU0EsbUJBQVQsQ0FBNkJDLE9BQTdCLEVBQXNDO0FBQzNDLE1BQUlDLE1BQU0sRUFBVjtBQUFBLE1BQ0lDLHdDQURKO0FBQUEsTUFFSUMsMkNBRko7QUFHQSxPQUFLLElBQUlDLElBQUksQ0FBYixFQUFnQkEsSUFBSUosUUFBUUssTUFBNUIsRUFBb0NELEdBQXBDLEVBQXlDO0FBQ3ZDRixhQUFTRixRQUFRSSxDQUFSLENBQVQ7QUFDQSxRQUFJRixPQUFPSSxLQUFYLEVBQWtCO0FBQ2hCSCxrQkFBWSxDQUFaO0FBQ0QsS0FGRCxNQUVPLElBQUlELE9BQU9LLE9BQVgsRUFBb0I7QUFDekJKLGtCQUFZLENBQUMsQ0FBYjtBQUNELEtBRk0sTUFFQTtBQUNMQSxrQkFBWSxDQUFaO0FBQ0Q7O0FBRURGLFFBQUlPLElBQUosQ0FBUyxDQUFDTCxTQUFELEVBQVlELE9BQU9PLEtBQW5CLENBQVQ7QUFDRDtBQUNELFNBQU9SLEdBQVA7QUFDRCIsImZpbGUiOiJkbXAuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTZWU6IGh0dHA6Ly9jb2RlLmdvb2dsZS5jb20vcC9nb29nbGUtZGlmZi1tYXRjaC1wYXRjaC93aWtpL0FQSVxuZXhwb3J0IGZ1bmN0aW9uIGNvbnZlcnRDaGFuZ2VzVG9ETVAoY2hhbmdlcykge1xuICBsZXQgcmV0ID0gW10sXG4gICAgICBjaGFuZ2UsXG4gICAgICBvcGVyYXRpb247XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2hhbmdlcy5sZW5ndGg7IGkrKykge1xuICAgIGNoYW5nZSA9IGNoYW5nZXNbaV07XG4gICAgaWYgKGNoYW5nZS5hZGRlZCkge1xuICAgICAgb3BlcmF0aW9uID0gMTtcbiAgICB9IGVsc2UgaWYgKGNoYW5nZS5yZW1vdmVkKSB7XG4gICAgICBvcGVyYXRpb24gPSAtMTtcbiAgICB9IGVsc2Uge1xuICAgICAgb3BlcmF0aW9uID0gMDtcbiAgICB9XG5cbiAgICByZXQucHVzaChbb3BlcmF0aW9uLCBjaGFuZ2UudmFsdWVdKTtcbiAgfVxuICByZXR1cm4gcmV0O1xufVxuIl19 + + +/***/ }), +/* 17 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/convertChangesToXML = convertChangesToXML; + function convertChangesToXML(changes) { + var ret = []; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + } + + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } + //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb252ZXJ0L3htbC5qcyJdLCJuYW1lcyI6WyJjb252ZXJ0Q2hhbmdlc1RvWE1MIiwiY2hhbmdlcyIsInJldCIsImkiLCJsZW5ndGgiLCJjaGFuZ2UiLCJhZGRlZCIsInB1c2giLCJyZW1vdmVkIiwiZXNjYXBlSFRNTCIsInZhbHVlIiwiam9pbiIsInMiLCJuIiwicmVwbGFjZSJdLCJtYXBwaW5ncyI6Ijs7O2dDQUFnQkEsbUIsR0FBQUEsbUI7QUFBVCxTQUFTQSxtQkFBVCxDQUE2QkMsT0FBN0IsRUFBc0M7QUFDM0MsTUFBSUMsTUFBTSxFQUFWO0FBQ0EsT0FBSyxJQUFJQyxJQUFJLENBQWIsRUFBZ0JBLElBQUlGLFFBQVFHLE1BQTVCLEVBQW9DRCxHQUFwQyxFQUF5QztBQUN2QyxRQUFJRSxTQUFTSixRQUFRRSxDQUFSLENBQWI7QUFDQSxRQUFJRSxPQUFPQyxLQUFYLEVBQWtCO0FBQ2hCSixVQUFJSyxJQUFKLENBQVMsT0FBVDtBQUNELEtBRkQsTUFFTyxJQUFJRixPQUFPRyxPQUFYLEVBQW9CO0FBQ3pCTixVQUFJSyxJQUFKLENBQVMsT0FBVDtBQUNEOztBQUVETCxRQUFJSyxJQUFKLENBQVNFLFdBQVdKLE9BQU9LLEtBQWxCLENBQVQ7O0FBRUEsUUFBSUwsT0FBT0MsS0FBWCxFQUFrQjtBQUNoQkosVUFBSUssSUFBSixDQUFTLFFBQVQ7QUFDRCxLQUZELE1BRU8sSUFBSUYsT0FBT0csT0FBWCxFQUFvQjtBQUN6Qk4sVUFBSUssSUFBSixDQUFTLFFBQVQ7QUFDRDtBQUNGO0FBQ0QsU0FBT0wsSUFBSVMsSUFBSixDQUFTLEVBQVQsQ0FBUDtBQUNEOztBQUVELFNBQVNGLFVBQVQsQ0FBb0JHLENBQXBCLEVBQXVCO0FBQ3JCLE1BQUlDLElBQUlELENBQVI7QUFDQUMsTUFBSUEsRUFBRUMsT0FBRixDQUFVLElBQVYsRUFBZ0IsT0FBaEIsQ0FBSjtBQUNBRCxNQUFJQSxFQUFFQyxPQUFGLENBQVUsSUFBVixFQUFnQixNQUFoQixDQUFKO0FBQ0FELE1BQUlBLEVBQUVDLE9BQUYsQ0FBVSxJQUFWLEVBQWdCLE1BQWhCLENBQUo7QUFDQUQsTUFBSUEsRUFBRUMsT0FBRixDQUFVLElBQVYsRUFBZ0IsUUFBaEIsQ0FBSjs7QUFFQSxTQUFPRCxDQUFQO0FBQ0QiLCJmaWxlIjoieG1sLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIGNvbnZlcnRDaGFuZ2VzVG9YTUwoY2hhbmdlcykge1xuICBsZXQgcmV0ID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2hhbmdlcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBjaGFuZ2UgPSBjaGFuZ2VzW2ldO1xuICAgIGlmIChjaGFuZ2UuYWRkZWQpIHtcbiAgICAgIHJldC5wdXNoKCc8aW5zPicpO1xuICAgIH0gZWxzZSBpZiAoY2hhbmdlLnJlbW92ZWQpIHtcbiAgICAgIHJldC5wdXNoKCc8ZGVsPicpO1xuICAgIH1cblxuICAgIHJldC5wdXNoKGVzY2FwZUhUTUwoY2hhbmdlLnZhbHVlKSk7XG5cbiAgICBpZiAoY2hhbmdlLmFkZGVkKSB7XG4gICAgICByZXQucHVzaCgnPC9pbnM+Jyk7XG4gICAgfSBlbHNlIGlmIChjaGFuZ2UucmVtb3ZlZCkge1xuICAgICAgcmV0LnB1c2goJzwvZGVsPicpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmV0LmpvaW4oJycpO1xufVxuXG5mdW5jdGlvbiBlc2NhcGVIVE1MKHMpIHtcbiAgbGV0IG4gPSBzO1xuICBuID0gbi5yZXBsYWNlKC8mL2csICcmYW1wOycpO1xuICBuID0gbi5yZXBsYWNlKC88L2csICcmbHQ7Jyk7XG4gIG4gPSBuLnJlcGxhY2UoLz4vZywgJyZndDsnKTtcbiAgbiA9IG4ucmVwbGFjZSgvXCIvZywgJyZxdW90OycpO1xuXG4gIHJldHVybiBuO1xufVxuIl19 + + +/***/ }) +/******/ ]) +}); +; \ No newline at end of file diff --git a/WebServer/node_modules/diff/dist/diff.min.js b/WebServer/node_modules/diff/dist/diff.min.js new file mode 100644 index 000000000..5049f8487 --- /dev/null +++ b/WebServer/node_modules/diff/dist/diff.min.js @@ -0,0 +1,416 @@ +/*! + + diff v3.5.0 + +Software License Agreement (BSD License) + +Copyright (c) 2009-2015, Kevin Decker + +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of Kevin Decker nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +@license +*/ +!function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?exports.JsDiff=b():a.JsDiff=b()}(this,function(){/******/ +return function(a){/******/ +// The require function +/******/ +function b(d){/******/ +// Check if module is in cache +/******/ +if(c[d])/******/ +return c[d].exports;/******/ +// Create a new module (and put it into the cache) +/******/ +var e=c[d]={/******/ +exports:{},/******/ +id:d,/******/ +loaded:!1};/******/ +// Return the exports of the module +/******/ +/******/ +// Execute the module function +/******/ +/******/ +// Flag the module as loaded +/******/ +return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}// webpackBootstrap +/******/ +// The module cache +/******/ +var c={};/******/ +// Load entry module and return exports +/******/ +/******/ +// expose the modules object (__webpack_modules__) +/******/ +/******/ +// expose the module cache +/******/ +/******/ +// __webpack_public_path__ +/******/ +return b.m=a,b.c=c,b.p="",b(0)}([/* 0 */ +/***/ +function(a,b,c){/*istanbul ignore start*/ +"use strict";/*istanbul ignore start*/ +function d(a){return a&&a.__esModule?a:{"default":a}}b.__esModule=!0,b.canonicalize=b.convertChangesToXML=b.convertChangesToDMP=b.merge=b.parsePatch=b.applyPatches=b.applyPatch=b.createPatch=b.createTwoFilesPatch=b.structuredPatch=b.diffArrays=b.diffJson=b.diffCss=b.diffSentences=b.diffTrimmedLines=b.diffLines=b.diffWordsWithSpace=b.diffWords=b.diffChars=b.Diff=void 0;/*istanbul ignore end*/ +var/*istanbul ignore start*/e=c(1),f=d(e),/*istanbul ignore start*/g=c(2),/*istanbul ignore start*/h=c(3),/*istanbul ignore start*/i=c(5),/*istanbul ignore start*/j=c(6),/*istanbul ignore start*/k=c(7),/*istanbul ignore start*/l=c(8),/*istanbul ignore start*/m=c(9),/*istanbul ignore start*/n=c(10),/*istanbul ignore start*/o=c(11),/*istanbul ignore start*/p=c(13),/*istanbul ignore start*/q=c(14),/*istanbul ignore start*/r=c(16),/*istanbul ignore start*/s=c(17);/* See LICENSE file for terms of use */ +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ +b.Diff=f["default"],/*istanbul ignore start*/ +b.diffChars=g.diffChars,/*istanbul ignore start*/ +b.diffWords=h.diffWords,/*istanbul ignore start*/ +b.diffWordsWithSpace=h.diffWordsWithSpace,/*istanbul ignore start*/ +b.diffLines=i.diffLines,/*istanbul ignore start*/ +b.diffTrimmedLines=i.diffTrimmedLines,/*istanbul ignore start*/ +b.diffSentences=j.diffSentences,/*istanbul ignore start*/ +b.diffCss=k.diffCss,/*istanbul ignore start*/ +b.diffJson=l.diffJson,/*istanbul ignore start*/ +b.diffArrays=m.diffArrays,/*istanbul ignore start*/ +b.structuredPatch=q.structuredPatch,/*istanbul ignore start*/ +b.createTwoFilesPatch=q.createTwoFilesPatch,/*istanbul ignore start*/ +b.createPatch=q.createPatch,/*istanbul ignore start*/ +b.applyPatch=n.applyPatch,/*istanbul ignore start*/ +b.applyPatches=n.applyPatches,/*istanbul ignore start*/ +b.parsePatch=o.parsePatch,/*istanbul ignore start*/ +b.merge=p.merge,/*istanbul ignore start*/ +b.convertChangesToDMP=r.convertChangesToDMP,/*istanbul ignore start*/ +b.convertChangesToXML=s.convertChangesToXML,/*istanbul ignore start*/ +b.canonicalize=l.canonicalize},/* 1 */ +/***/ +function(a,b){/*istanbul ignore start*/ +"use strict";function c(){}function d(a,b,c,d,e){for(var f=0,g=b.length,h=0,i=0;fa.length?c:a}),j.value=a.join(l)}else j.value=a.join(c.slice(h,h+j.count));h+=j.count, +// Common case +j.added||(i+=j.count)}} +// Special case handle for when one terminal is ignored (i.e. whitespace). +// For this case we merge the terminal into the prior string and drop the change. +// This is only available for string mode. +var m=b[g-1];return g>1&&"string"==typeof m.value&&(m.added||m.removed)&&a.equals("",m.value)&&(b[g-2].value+=m.value,b.pop()),b}function e(a){return{newPos:a.newPos,components:a.components.slice(0)}}b.__esModule=!0,b["default"]=/*istanbul ignore end*/c,c.prototype={/*istanbul ignore start*/ +/*istanbul ignore end*/ +diff:function(a,b){function c(a){return h?(setTimeout(function(){h(void 0,a)},0),!0):a} +// Main worker method. checks all permutations of a given edit length for acceptance. +function f(){for(var f=-1*l;f<=l;f+=2){var g=/*istanbul ignore start*/void 0,h=n[f-1],m=n[f+1],o=(m?m.newPos:0)-f;h&&( +// No one else is going to attempt to use this value, clear it +n[f-1]=void 0);var p=h&&h.newPos+1=j&&o+1>=k)return c(d(i,g.components,b,a,i.useLongestToken)); +// Otherwise track this path as a potential candidate and continue. +n[f]=g}else +// If this path is a terminal then prune +n[f]=void 0}l++}/*istanbul ignore start*/ +var/*istanbul ignore end*/g=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},h=g.callback;"function"==typeof g&&(h=g,g={}),this.options=g;var i=this; +// Allow subclasses to massage the input prior to running +a=this.castInput(a),b=this.castInput(b),a=this.removeEmpty(this.tokenize(a)),b=this.removeEmpty(this.tokenize(b));var j=b.length,k=a.length,l=1,m=j+k,n=[{newPos:-1,components:[]}],o=this.extractCommon(n[0],b,a,0);if(n[0].newPos+1>=j&&o+1>=k) +// Identity per the equality and tokenizer +return c([{value:this.join(b),count:b.length}]); +// Performs the length of edit iteration. Is a bit fugly as this has to support the +// sync and async mode which is never fun. Loops over execEditLength until a value +// is produced. +if(h)!function q(){setTimeout(function(){ +// This should not happen, but we want to be safe. +/* istanbul ignore next */ +// This should not happen, but we want to be safe. +/* istanbul ignore next */ +return l>m?h():void(f()||q())},0)}();else for(;l<=m;){var p=f();if(p)return p}},/*istanbul ignore start*/ +/*istanbul ignore end*/ +pushComponent:function(a,b,c){var d=a[a.length-1];d&&d.added===b&&d.removed===c? +// We need to clone here as the component clone operation is just +// as shallow array clone +a[a.length-1]={count:d.count+1,added:b,removed:c}:a.push({count:1,added:b,removed:c})},/*istanbul ignore start*/ +/*istanbul ignore end*/ +extractCommon:function(a,b,c,d){for(var e=b.length,f=c.length,g=a.newPos,h=g-d,i=0;g+10?d[0]:" ",g=d.length>0?d.substr(1):d;if(" "===f||"-"===f){ +// Context sanity check +if(!j(b+1,e[b],f,g)&&(k++,k>l))return!1;b++}}return!0}/*istanbul ignore start*/ +var/*istanbul ignore end*/d=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if("string"==typeof b&&(b=/*istanbul ignore start*/(0,g.parsePatch)(b)),Array.isArray(b)){if(b.length>1)throw new Error("applyPatch only works with a single input.");b=b[0]} +// Search best fit offsets for each hunk based on the previous ones +for(var e=a.split(/\r\n|[\n\v\f\r\x85]/),f=a.match(/\r\n|[\n\v\f\r\x85]/g)||[],h=b.hunks,j=d.compareLine||function(a,b,c,d){/*istanbul ignore end*/ +return b===d},k=0,l=d.fuzzFactor||0,m=0,n=0,o=/*istanbul ignore start*/void 0,p=/*istanbul ignore start*/void 0,q=0;q0?B[0]:" ",D=B.length>0?B.substr(1):B,E=y.linedelimiters[A];if(" "===C)z++;else if("-"===C)e.splice(z,1),f.splice(z,1);else if("+"===C)e.splice(z,0,D),f.splice(z,0,E),z++;else if("\\"===C){var F=y.lines[A-1]?y.lines[A-1][0]:null;"+"===F?o=!0:"-"===F&&(p=!0)}}} +// Handle EOFNL insertion/removal +if(o)for(;!e[e.length-1];)e.pop(),f.pop();else p&&(e.push(""),f.push("\n"));for(var G=0;G1&&void 0!==arguments[1]?arguments[1]:{},f=a.split(/\r\n|[\n\v\f\r\x85]/),g=a.match(/\r\n|[\n\v\f\r\x85]/g)||[],h=[],i=0;i0?j(h.lines.slice(-i.context)):[],m-=o.length,n-=o.length)} +// Output our changes +/*istanbul ignore start*/ +(g=/*istanbul ignore end*/o).push.apply(/*istanbul ignore start*/g,/*istanbul ignore start*/d(/*istanbul ignore end*/f.map(function(a){return(b.added?"+":"-")+a}))), +// Track the updated file position +b.added?q+=f.length:p+=f.length}else{ +// Identical context lines. Track line changes +if(m) +// Close out any changes that have been output (or join overlapping) +if(f.length<=2*i.context&&a=k.length-2&&f.length<=i.context){ +// EOF is inside this hunk +var v=/\n$/.test(c),w=/\n$/.test(e);0!=f.length||v?v&&w||o.push("\\ No newline at end of file"): +// special case: old has no eol and no trailing context; no-nl can end up before adds +o.splice(u.oldLines,0,"\\ No newline at end of file")}l.push(u),m=0,n=0,o=[]}p+=f.length,q+=f.length}},s=0;sa.length)return!1;for(var c=0;c"):e.removed&&b.push(""),b.push(d(e.value)),e.added?b.push(""):e.removed&&b.push("")}return b.join("")}function d(a){var b=a;return b=b.replace(/&/g,"&"),b=b.replace(//g,">"),b=b.replace(/"/g,""")}b.__esModule=!0,b.convertChangesToXML=c}])}); \ No newline at end of file diff --git a/WebServer/node_modules/diff/lib/convert/dmp.js b/WebServer/node_modules/diff/lib/convert/dmp.js new file mode 100644 index 000000000..b9b646f00 --- /dev/null +++ b/WebServer/node_modules/diff/lib/convert/dmp.js @@ -0,0 +1,24 @@ +/*istanbul ignore start*/"use strict"; + +exports.__esModule = true; +exports. /*istanbul ignore end*/convertChangesToDMP = convertChangesToDMP; +// See: http://code.google.com/p/google-diff-match-patch/wiki/API +function convertChangesToDMP(changes) { + var ret = [], + change = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + operation = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (var i = 0; i < changes.length; i++) { + change = changes[i]; + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; + } + + ret.push([operation, change.value]); + } + return ret; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb252ZXJ0L2RtcC5qcyJdLCJuYW1lcyI6WyJjb252ZXJ0Q2hhbmdlc1RvRE1QIiwiY2hhbmdlcyIsInJldCIsImNoYW5nZSIsIm9wZXJhdGlvbiIsImkiLCJsZW5ndGgiLCJhZGRlZCIsInJlbW92ZWQiLCJwdXNoIiwidmFsdWUiXSwibWFwcGluZ3MiOiI7OztnQ0FDZ0JBLG1CLEdBQUFBLG1CO0FBRGhCO0FBQ08sU0FBU0EsbUJBQVQsQ0FBNkJDLE9BQTdCLEVBQXNDO0FBQzNDLE1BQUlDLE1BQU0sRUFBVjtBQUFBLE1BQ0lDLHdDQURKO0FBQUEsTUFFSUMsMkNBRko7QUFHQSxPQUFLLElBQUlDLElBQUksQ0FBYixFQUFnQkEsSUFBSUosUUFBUUssTUFBNUIsRUFBb0NELEdBQXBDLEVBQXlDO0FBQ3ZDRixhQUFTRixRQUFRSSxDQUFSLENBQVQ7QUFDQSxRQUFJRixPQUFPSSxLQUFYLEVBQWtCO0FBQ2hCSCxrQkFBWSxDQUFaO0FBQ0QsS0FGRCxNQUVPLElBQUlELE9BQU9LLE9BQVgsRUFBb0I7QUFDekJKLGtCQUFZLENBQUMsQ0FBYjtBQUNELEtBRk0sTUFFQTtBQUNMQSxrQkFBWSxDQUFaO0FBQ0Q7O0FBRURGLFFBQUlPLElBQUosQ0FBUyxDQUFDTCxTQUFELEVBQVlELE9BQU9PLEtBQW5CLENBQVQ7QUFDRDtBQUNELFNBQU9SLEdBQVA7QUFDRCIsImZpbGUiOiJkbXAuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTZWU6IGh0dHA6Ly9jb2RlLmdvb2dsZS5jb20vcC9nb29nbGUtZGlmZi1tYXRjaC1wYXRjaC93aWtpL0FQSVxuZXhwb3J0IGZ1bmN0aW9uIGNvbnZlcnRDaGFuZ2VzVG9ETVAoY2hhbmdlcykge1xuICBsZXQgcmV0ID0gW10sXG4gICAgICBjaGFuZ2UsXG4gICAgICBvcGVyYXRpb247XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2hhbmdlcy5sZW5ndGg7IGkrKykge1xuICAgIGNoYW5nZSA9IGNoYW5nZXNbaV07XG4gICAgaWYgKGNoYW5nZS5hZGRlZCkge1xuICAgICAgb3BlcmF0aW9uID0gMTtcbiAgICB9IGVsc2UgaWYgKGNoYW5nZS5yZW1vdmVkKSB7XG4gICAgICBvcGVyYXRpb24gPSAtMTtcbiAgICB9IGVsc2Uge1xuICAgICAgb3BlcmF0aW9uID0gMDtcbiAgICB9XG5cbiAgICByZXQucHVzaChbb3BlcmF0aW9uLCBjaGFuZ2UudmFsdWVdKTtcbiAgfVxuICByZXR1cm4gcmV0O1xufVxuIl19 diff --git a/WebServer/node_modules/diff/lib/convert/xml.js b/WebServer/node_modules/diff/lib/convert/xml.js new file mode 100644 index 000000000..331827a1c --- /dev/null +++ b/WebServer/node_modules/diff/lib/convert/xml.js @@ -0,0 +1,35 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/convertChangesToXML = convertChangesToXML; +function convertChangesToXML(changes) { + var ret = []; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); +} + +function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb252ZXJ0L3htbC5qcyJdLCJuYW1lcyI6WyJjb252ZXJ0Q2hhbmdlc1RvWE1MIiwiY2hhbmdlcyIsInJldCIsImkiLCJsZW5ndGgiLCJjaGFuZ2UiLCJhZGRlZCIsInB1c2giLCJyZW1vdmVkIiwiZXNjYXBlSFRNTCIsInZhbHVlIiwiam9pbiIsInMiLCJuIiwicmVwbGFjZSJdLCJtYXBwaW5ncyI6Ijs7O2dDQUFnQkEsbUIsR0FBQUEsbUI7QUFBVCxTQUFTQSxtQkFBVCxDQUE2QkMsT0FBN0IsRUFBc0M7QUFDM0MsTUFBSUMsTUFBTSxFQUFWO0FBQ0EsT0FBSyxJQUFJQyxJQUFJLENBQWIsRUFBZ0JBLElBQUlGLFFBQVFHLE1BQTVCLEVBQW9DRCxHQUFwQyxFQUF5QztBQUN2QyxRQUFJRSxTQUFTSixRQUFRRSxDQUFSLENBQWI7QUFDQSxRQUFJRSxPQUFPQyxLQUFYLEVBQWtCO0FBQ2hCSixVQUFJSyxJQUFKLENBQVMsT0FBVDtBQUNELEtBRkQsTUFFTyxJQUFJRixPQUFPRyxPQUFYLEVBQW9CO0FBQ3pCTixVQUFJSyxJQUFKLENBQVMsT0FBVDtBQUNEOztBQUVETCxRQUFJSyxJQUFKLENBQVNFLFdBQVdKLE9BQU9LLEtBQWxCLENBQVQ7O0FBRUEsUUFBSUwsT0FBT0MsS0FBWCxFQUFrQjtBQUNoQkosVUFBSUssSUFBSixDQUFTLFFBQVQ7QUFDRCxLQUZELE1BRU8sSUFBSUYsT0FBT0csT0FBWCxFQUFvQjtBQUN6Qk4sVUFBSUssSUFBSixDQUFTLFFBQVQ7QUFDRDtBQUNGO0FBQ0QsU0FBT0wsSUFBSVMsSUFBSixDQUFTLEVBQVQsQ0FBUDtBQUNEOztBQUVELFNBQVNGLFVBQVQsQ0FBb0JHLENBQXBCLEVBQXVCO0FBQ3JCLE1BQUlDLElBQUlELENBQVI7QUFDQUMsTUFBSUEsRUFBRUMsT0FBRixDQUFVLElBQVYsRUFBZ0IsT0FBaEIsQ0FBSjtBQUNBRCxNQUFJQSxFQUFFQyxPQUFGLENBQVUsSUFBVixFQUFnQixNQUFoQixDQUFKO0FBQ0FELE1BQUlBLEVBQUVDLE9BQUYsQ0FBVSxJQUFWLEVBQWdCLE1BQWhCLENBQUo7QUFDQUQsTUFBSUEsRUFBRUMsT0FBRixDQUFVLElBQVYsRUFBZ0IsUUFBaEIsQ0FBSjs7QUFFQSxTQUFPRCxDQUFQO0FBQ0QiLCJmaWxlIjoieG1sLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIGNvbnZlcnRDaGFuZ2VzVG9YTUwoY2hhbmdlcykge1xuICBsZXQgcmV0ID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2hhbmdlcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBjaGFuZ2UgPSBjaGFuZ2VzW2ldO1xuICAgIGlmIChjaGFuZ2UuYWRkZWQpIHtcbiAgICAgIHJldC5wdXNoKCc8aW5zPicpO1xuICAgIH0gZWxzZSBpZiAoY2hhbmdlLnJlbW92ZWQpIHtcbiAgICAgIHJldC5wdXNoKCc8ZGVsPicpO1xuICAgIH1cblxuICAgIHJldC5wdXNoKGVzY2FwZUhUTUwoY2hhbmdlLnZhbHVlKSk7XG5cbiAgICBpZiAoY2hhbmdlLmFkZGVkKSB7XG4gICAgICByZXQucHVzaCgnPC9pbnM+Jyk7XG4gICAgfSBlbHNlIGlmIChjaGFuZ2UucmVtb3ZlZCkge1xuICAgICAgcmV0LnB1c2goJzwvZGVsPicpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmV0LmpvaW4oJycpO1xufVxuXG5mdW5jdGlvbiBlc2NhcGVIVE1MKHMpIHtcbiAgbGV0IG4gPSBzO1xuICBuID0gbi5yZXBsYWNlKC8mL2csICcmYW1wOycpO1xuICBuID0gbi5yZXBsYWNlKC88L2csICcmbHQ7Jyk7XG4gIG4gPSBuLnJlcGxhY2UoLz4vZywgJyZndDsnKTtcbiAgbiA9IG4ucmVwbGFjZSgvXCIvZywgJyZxdW90OycpO1xuXG4gIHJldHVybiBuO1xufVxuIl19 diff --git a/WebServer/node_modules/diff/lib/diff/array.js b/WebServer/node_modules/diff/lib/diff/array.js new file mode 100644 index 000000000..f3daa4edb --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/array.js @@ -0,0 +1,24 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.arrayDiff = undefined; +exports. /*istanbul ignore end*/diffArrays = diffArrays; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var arrayDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); +arrayDiff.tokenize = function (value) { + return value.slice(); +}; +arrayDiff.join = arrayDiff.removeEmpty = function (value) { + return value; +}; + +function diffArrays(oldArr, newArr, callback) { + return arrayDiff.diff(oldArr, newArr, callback); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2FycmF5LmpzIl0sIm5hbWVzIjpbImRpZmZBcnJheXMiLCJhcnJheURpZmYiLCJ0b2tlbml6ZSIsInZhbHVlIiwic2xpY2UiLCJqb2luIiwicmVtb3ZlRW1wdHkiLCJvbGRBcnIiLCJuZXdBcnIiLCJjYWxsYmFjayIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBVWdCQSxVLEdBQUFBLFU7O0FBVmhCOzs7Ozs7dUJBRU8sSUFBTUMsaUZBQVksd0VBQWxCO0FBQ1BBLFVBQVVDLFFBQVYsR0FBcUIsVUFBU0MsS0FBVCxFQUFnQjtBQUNuQyxTQUFPQSxNQUFNQyxLQUFOLEVBQVA7QUFDRCxDQUZEO0FBR0FILFVBQVVJLElBQVYsR0FBaUJKLFVBQVVLLFdBQVYsR0FBd0IsVUFBU0gsS0FBVCxFQUFnQjtBQUN2RCxTQUFPQSxLQUFQO0FBQ0QsQ0FGRDs7QUFJTyxTQUFTSCxVQUFULENBQW9CTyxNQUFwQixFQUE0QkMsTUFBNUIsRUFBb0NDLFFBQXBDLEVBQThDO0FBQUUsU0FBT1IsVUFBVVMsSUFBVixDQUFlSCxNQUFmLEVBQXVCQyxNQUF2QixFQUErQkMsUUFBL0IsQ0FBUDtBQUFrRCIsImZpbGUiOiJhcnJheS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cbmV4cG9ydCBjb25zdCBhcnJheURpZmYgPSBuZXcgRGlmZigpO1xuYXJyYXlEaWZmLnRva2VuaXplID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgcmV0dXJuIHZhbHVlLnNsaWNlKCk7XG59O1xuYXJyYXlEaWZmLmpvaW4gPSBhcnJheURpZmYucmVtb3ZlRW1wdHkgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWU7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkFycmF5cyhvbGRBcnIsIG5ld0FyciwgY2FsbGJhY2spIHsgcmV0dXJuIGFycmF5RGlmZi5kaWZmKG9sZEFyciwgbmV3QXJyLCBjYWxsYmFjayk7IH1cbiJdfQ== diff --git a/WebServer/node_modules/diff/lib/diff/base.js b/WebServer/node_modules/diff/lib/diff/base.js new file mode 100644 index 000000000..763daec6d --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/base.js @@ -0,0 +1,235 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports['default'] = /*istanbul ignore end*/Diff; +function Diff() {} + +Diff.prototype = { + /*istanbul ignore start*/ /*istanbul ignore end*/diff: function diff(oldString, newString) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var callback = options.callback; + if (typeof options === 'function') { + callback = options; + options = {}; + } + this.options = options; + + var self = this; + + function done(value) { + if (callback) { + setTimeout(function () { + callback(undefined, value); + }, 0); + return true; + } else { + return value; + } + } + + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString); + newString = this.castInput(newString); + + oldString = this.removeEmpty(this.tokenize(oldString)); + newString = this.removeEmpty(this.tokenize(newString)); + + var newLen = newString.length, + oldLen = oldString.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0, i.e. the content starts with the same values + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + // Identity per the equality and tokenizer + return done([{ value: this.join(newString), count: newString.length }]); + } + + // Main worker method. checks all permutations of a given edit length for acceptance. + function execEditLength() { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { + var basePath = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + var addPath = bestPath[diagonalPath - 1], + removePath = bestPath[diagonalPath + 1], + _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } + + var canAdd = addPath && addPath.newPos + 1 < newLen, + canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen; + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || canRemove && addPath.newPos < removePath.newPos) { + basePath = clonePath(removePath); + self.pushComponent(basePath.components, undefined, true); + } else { + basePath = addPath; // No need to clone, we've pulled it from the list + basePath.newPos++; + self.pushComponent(basePath.components, true, undefined); + } + + _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + + // If we have hit the end of both strings, then we are done + if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) { + return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken)); + } else { + // Otherwise track this path as a potential candidate and continue. + bestPath[diagonalPath] = basePath; + } + } + + editLength++; + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the + // sync and async mode which is never fun. Loops over execEditLength until a value + // is produced. + if (callback) { + (function exec() { + setTimeout(function () { + // This should not happen, but we want to be safe. + /* istanbul ignore next */ + if (editLength > maxEditLength) { + return callback(); + } + + if (!execEditLength()) { + exec(); + } + }, 0); + })(); + } else { + while (editLength <= maxEditLength) { + var ret = execEditLength(); + if (ret) { + return ret; + } + } + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/pushComponent: function pushComponent(components, added, removed) { + var last = components[components.length - 1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length - 1] = { count: last.count + 1, added: added, removed: removed }; + } else { + components.push({ count: 1, added: added, removed: removed }); + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } + + if (commonCount) { + basePath.components.push({ count: commonCount }); + } + + basePath.newPos = newPos; + return oldPos; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/equals: function equals(left, right) { + if (this.options.comparator) { + return this.options.comparator(left, right); + } else { + return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/removeEmpty: function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/castInput: function castInput(value) { + return value; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/tokenize: function tokenize(value) { + return value.split(''); + }, + /*istanbul ignore start*/ /*istanbul ignore end*/join: function join(chars) { + return chars.join(''); + } +}; + +function buildValues(diff, components, newString, oldString, useLongestToken) { + var componentPos = 0, + componentLen = components.length, + newPos = 0, + oldPos = 0; + + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + if (!component.removed) { + if (!component.added && useLongestToken) { + var value = newString.slice(newPos, newPos + component.count); + value = value.map(function (value, i) { + var oldValue = oldString[oldPos + i]; + return oldValue.length > value.length ? oldValue : value; + }); + + component.value = diff.join(value); + } else { + component.value = diff.join(newString.slice(newPos, newPos + component.count)); + } + newPos += component.count; + + // Common case + if (!component.added) { + oldPos += component.count; + } + } else { + component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); + oldPos += component.count; + + // Reverse add and remove so removes are output first to match common convention + // The diffing algorithm is tied to add then remove output and this is the simplest + // route to get the desired output with minimal overhead. + if (componentPos && components[componentPos - 1].added) { + var tmp = components[componentPos - 1]; + components[componentPos - 1] = components[componentPos]; + components[componentPos] = tmp; + } + } + } + + // Special case handle for when one terminal is ignored (i.e. whitespace). + // For this case we merge the terminal into the prior string and drop the change. + // This is only available for string mode. + var lastComponent = components[componentLen - 1]; + if (componentLen > 1 && typeof lastComponent.value === 'string' && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) { + components[componentLen - 2].value += lastComponent.value; + components.pop(); + } + + return components; +} + +function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2Jhc2UuanMiXSwibmFtZXMiOlsiRGlmZiIsInByb3RvdHlwZSIsImRpZmYiLCJvbGRTdHJpbmciLCJuZXdTdHJpbmciLCJvcHRpb25zIiwiY2FsbGJhY2siLCJzZWxmIiwiZG9uZSIsInZhbHVlIiwic2V0VGltZW91dCIsInVuZGVmaW5lZCIsImNhc3RJbnB1dCIsInJlbW92ZUVtcHR5IiwidG9rZW5pemUiLCJuZXdMZW4iLCJsZW5ndGgiLCJvbGRMZW4iLCJlZGl0TGVuZ3RoIiwibWF4RWRpdExlbmd0aCIsImJlc3RQYXRoIiwibmV3UG9zIiwiY29tcG9uZW50cyIsIm9sZFBvcyIsImV4dHJhY3RDb21tb24iLCJqb2luIiwiY291bnQiLCJleGVjRWRpdExlbmd0aCIsImRpYWdvbmFsUGF0aCIsImJhc2VQYXRoIiwiYWRkUGF0aCIsInJlbW92ZVBhdGgiLCJjYW5BZGQiLCJjYW5SZW1vdmUiLCJjbG9uZVBhdGgiLCJwdXNoQ29tcG9uZW50IiwiYnVpbGRWYWx1ZXMiLCJ1c2VMb25nZXN0VG9rZW4iLCJleGVjIiwicmV0IiwiYWRkZWQiLCJyZW1vdmVkIiwibGFzdCIsInB1c2giLCJjb21tb25Db3VudCIsImVxdWFscyIsImxlZnQiLCJyaWdodCIsImNvbXBhcmF0b3IiLCJpZ25vcmVDYXNlIiwidG9Mb3dlckNhc2UiLCJhcnJheSIsImkiLCJzcGxpdCIsImNoYXJzIiwiY29tcG9uZW50UG9zIiwiY29tcG9uZW50TGVuIiwiY29tcG9uZW50Iiwic2xpY2UiLCJtYXAiLCJvbGRWYWx1ZSIsInRtcCIsImxhc3RDb21wb25lbnQiLCJwb3AiLCJwYXRoIl0sIm1hcHBpbmdzIjoiOzs7NENBQXdCQSxJO0FBQVQsU0FBU0EsSUFBVCxHQUFnQixDQUFFOztBQUVqQ0EsS0FBS0MsU0FBTCxHQUFpQjtBQUFBLG1EQUNmQyxJQURlLGdCQUNWQyxTQURVLEVBQ0NDLFNBREQsRUFDMEI7QUFBQSx3REFBZEMsT0FBYyx1RUFBSixFQUFJOztBQUN2QyxRQUFJQyxXQUFXRCxRQUFRQyxRQUF2QjtBQUNBLFFBQUksT0FBT0QsT0FBUCxLQUFtQixVQUF2QixFQUFtQztBQUNqQ0MsaUJBQVdELE9BQVg7QUFDQUEsZ0JBQVUsRUFBVjtBQUNEO0FBQ0QsU0FBS0EsT0FBTCxHQUFlQSxPQUFmOztBQUVBLFFBQUlFLE9BQU8sSUFBWDs7QUFFQSxhQUFTQyxJQUFULENBQWNDLEtBQWQsRUFBcUI7QUFDbkIsVUFBSUgsUUFBSixFQUFjO0FBQ1pJLG1CQUFXLFlBQVc7QUFBRUosbUJBQVNLLFNBQVQsRUFBb0JGLEtBQXBCO0FBQTZCLFNBQXJELEVBQXVELENBQXZEO0FBQ0EsZUFBTyxJQUFQO0FBQ0QsT0FIRCxNQUdPO0FBQ0wsZUFBT0EsS0FBUDtBQUNEO0FBQ0Y7O0FBRUQ7QUFDQU4sZ0JBQVksS0FBS1MsU0FBTCxDQUFlVCxTQUFmLENBQVo7QUFDQUMsZ0JBQVksS0FBS1EsU0FBTCxDQUFlUixTQUFmLENBQVo7O0FBRUFELGdCQUFZLEtBQUtVLFdBQUwsQ0FBaUIsS0FBS0MsUUFBTCxDQUFjWCxTQUFkLENBQWpCLENBQVo7QUFDQUMsZ0JBQVksS0FBS1MsV0FBTCxDQUFpQixLQUFLQyxRQUFMLENBQWNWLFNBQWQsQ0FBakIsQ0FBWjs7QUFFQSxRQUFJVyxTQUFTWCxVQUFVWSxNQUF2QjtBQUFBLFFBQStCQyxTQUFTZCxVQUFVYSxNQUFsRDtBQUNBLFFBQUlFLGFBQWEsQ0FBakI7QUFDQSxRQUFJQyxnQkFBZ0JKLFNBQVNFLE1BQTdCO0FBQ0EsUUFBSUcsV0FBVyxDQUFDLEVBQUVDLFFBQVEsQ0FBQyxDQUFYLEVBQWNDLFlBQVksRUFBMUIsRUFBRCxDQUFmOztBQUVBO0FBQ0EsUUFBSUMsU0FBUyxLQUFLQyxhQUFMLENBQW1CSixTQUFTLENBQVQsQ0FBbkIsRUFBZ0NoQixTQUFoQyxFQUEyQ0QsU0FBM0MsRUFBc0QsQ0FBdEQsQ0FBYjtBQUNBLFFBQUlpQixTQUFTLENBQVQsRUFBWUMsTUFBWixHQUFxQixDQUFyQixJQUEwQk4sTUFBMUIsSUFBb0NRLFNBQVMsQ0FBVCxJQUFjTixNQUF0RCxFQUE4RDtBQUM1RDtBQUNBLGFBQU9ULEtBQUssQ0FBQyxFQUFDQyxPQUFPLEtBQUtnQixJQUFMLENBQVVyQixTQUFWLENBQVIsRUFBOEJzQixPQUFPdEIsVUFBVVksTUFBL0MsRUFBRCxDQUFMLENBQVA7QUFDRDs7QUFFRDtBQUNBLGFBQVNXLGNBQVQsR0FBMEI7QUFDeEIsV0FBSyxJQUFJQyxlQUFlLENBQUMsQ0FBRCxHQUFLVixVQUE3QixFQUF5Q1UsZ0JBQWdCVixVQUF6RCxFQUFxRVUsZ0JBQWdCLENBQXJGLEVBQXdGO0FBQ3RGLFlBQUlDLDBDQUFKO0FBQ0EsWUFBSUMsVUFBVVYsU0FBU1EsZUFBZSxDQUF4QixDQUFkO0FBQUEsWUFDSUcsYUFBYVgsU0FBU1EsZUFBZSxDQUF4QixDQURqQjtBQUFBLFlBRUlMLFVBQVMsQ0FBQ1EsYUFBYUEsV0FBV1YsTUFBeEIsR0FBaUMsQ0FBbEMsSUFBdUNPLFlBRnBEO0FBR0EsWUFBSUUsT0FBSixFQUFhO0FBQ1g7QUFDQVYsbUJBQVNRLGVBQWUsQ0FBeEIsSUFBNkJqQixTQUE3QjtBQUNEOztBQUVELFlBQUlxQixTQUFTRixXQUFXQSxRQUFRVCxNQUFSLEdBQWlCLENBQWpCLEdBQXFCTixNQUE3QztBQUFBLFlBQ0lrQixZQUFZRixjQUFjLEtBQUtSLE9BQW5CLElBQTZCQSxVQUFTTixNQUR0RDtBQUVBLFlBQUksQ0FBQ2UsTUFBRCxJQUFXLENBQUNDLFNBQWhCLEVBQTJCO0FBQ3pCO0FBQ0FiLG1CQUFTUSxZQUFULElBQXlCakIsU0FBekI7QUFDQTtBQUNEOztBQUVEO0FBQ0E7QUFDQTtBQUNBLFlBQUksQ0FBQ3FCLE1BQUQsSUFBWUMsYUFBYUgsUUFBUVQsTUFBUixHQUFpQlUsV0FBV1YsTUFBekQsRUFBa0U7QUFDaEVRLHFCQUFXSyxVQUFVSCxVQUFWLENBQVg7QUFDQXhCLGVBQUs0QixhQUFMLENBQW1CTixTQUFTUCxVQUE1QixFQUF3Q1gsU0FBeEMsRUFBbUQsSUFBbkQ7QUFDRCxTQUhELE1BR087QUFDTGtCLHFCQUFXQyxPQUFYLENBREssQ0FDaUI7QUFDdEJELG1CQUFTUixNQUFUO0FBQ0FkLGVBQUs0QixhQUFMLENBQW1CTixTQUFTUCxVQUE1QixFQUF3QyxJQUF4QyxFQUE4Q1gsU0FBOUM7QUFDRDs7QUFFRFksa0JBQVNoQixLQUFLaUIsYUFBTCxDQUFtQkssUUFBbkIsRUFBNkJ6QixTQUE3QixFQUF3Q0QsU0FBeEMsRUFBbUR5QixZQUFuRCxDQUFUOztBQUVBO0FBQ0EsWUFBSUMsU0FBU1IsTUFBVCxHQUFrQixDQUFsQixJQUF1Qk4sTUFBdkIsSUFBaUNRLFVBQVMsQ0FBVCxJQUFjTixNQUFuRCxFQUEyRDtBQUN6RCxpQkFBT1QsS0FBSzRCLFlBQVk3QixJQUFaLEVBQWtCc0IsU0FBU1AsVUFBM0IsRUFBdUNsQixTQUF2QyxFQUFrREQsU0FBbEQsRUFBNkRJLEtBQUs4QixlQUFsRSxDQUFMLENBQVA7QUFDRCxTQUZELE1BRU87QUFDTDtBQUNBakIsbUJBQVNRLFlBQVQsSUFBeUJDLFFBQXpCO0FBQ0Q7QUFDRjs7QUFFRFg7QUFDRDs7QUFFRDtBQUNBO0FBQ0E7QUFDQSxRQUFJWixRQUFKLEVBQWM7QUFDWCxnQkFBU2dDLElBQVQsR0FBZ0I7QUFDZjVCLG1CQUFXLFlBQVc7QUFDcEI7QUFDQTtBQUNBLGNBQUlRLGFBQWFDLGFBQWpCLEVBQWdDO0FBQzlCLG1CQUFPYixVQUFQO0FBQ0Q7O0FBRUQsY0FBSSxDQUFDcUIsZ0JBQUwsRUFBdUI7QUFDckJXO0FBQ0Q7QUFDRixTQVZELEVBVUcsQ0FWSDtBQVdELE9BWkEsR0FBRDtBQWFELEtBZEQsTUFjTztBQUNMLGFBQU9wQixjQUFjQyxhQUFyQixFQUFvQztBQUNsQyxZQUFJb0IsTUFBTVosZ0JBQVY7QUFDQSxZQUFJWSxHQUFKLEVBQVM7QUFDUCxpQkFBT0EsR0FBUDtBQUNEO0FBQ0Y7QUFDRjtBQUNGLEdBOUdjO0FBQUEsbURBZ0hmSixhQWhIZSx5QkFnSERiLFVBaEhDLEVBZ0hXa0IsS0FoSFgsRUFnSGtCQyxPQWhIbEIsRUFnSDJCO0FBQ3hDLFFBQUlDLE9BQU9wQixXQUFXQSxXQUFXTixNQUFYLEdBQW9CLENBQS9CLENBQVg7QUFDQSxRQUFJMEIsUUFBUUEsS0FBS0YsS0FBTCxLQUFlQSxLQUF2QixJQUFnQ0UsS0FBS0QsT0FBTCxLQUFpQkEsT0FBckQsRUFBOEQ7QUFDNUQ7QUFDQTtBQUNBbkIsaUJBQVdBLFdBQVdOLE1BQVgsR0FBb0IsQ0FBL0IsSUFBb0MsRUFBQ1UsT0FBT2dCLEtBQUtoQixLQUFMLEdBQWEsQ0FBckIsRUFBd0JjLE9BQU9BLEtBQS9CLEVBQXNDQyxTQUFTQSxPQUEvQyxFQUFwQztBQUNELEtBSkQsTUFJTztBQUNMbkIsaUJBQVdxQixJQUFYLENBQWdCLEVBQUNqQixPQUFPLENBQVIsRUFBV2MsT0FBT0EsS0FBbEIsRUFBeUJDLFNBQVNBLE9BQWxDLEVBQWhCO0FBQ0Q7QUFDRixHQXpIYztBQUFBLG1EQTBIZmpCLGFBMUhlLHlCQTBIREssUUExSEMsRUEwSFN6QixTQTFIVCxFQTBIb0JELFNBMUhwQixFQTBIK0J5QixZQTFIL0IsRUEwSDZDO0FBQzFELFFBQUliLFNBQVNYLFVBQVVZLE1BQXZCO0FBQUEsUUFDSUMsU0FBU2QsVUFBVWEsTUFEdkI7QUFBQSxRQUVJSyxTQUFTUSxTQUFTUixNQUZ0QjtBQUFBLFFBR0lFLFNBQVNGLFNBQVNPLFlBSHRCO0FBQUEsUUFLSWdCLGNBQWMsQ0FMbEI7QUFNQSxXQUFPdkIsU0FBUyxDQUFULEdBQWFOLE1BQWIsSUFBdUJRLFNBQVMsQ0FBVCxHQUFhTixNQUFwQyxJQUE4QyxLQUFLNEIsTUFBTCxDQUFZekMsVUFBVWlCLFNBQVMsQ0FBbkIsQ0FBWixFQUFtQ2xCLFVBQVVvQixTQUFTLENBQW5CLENBQW5DLENBQXJELEVBQWdIO0FBQzlHRjtBQUNBRTtBQUNBcUI7QUFDRDs7QUFFRCxRQUFJQSxXQUFKLEVBQWlCO0FBQ2ZmLGVBQVNQLFVBQVQsQ0FBb0JxQixJQUFwQixDQUF5QixFQUFDakIsT0FBT2tCLFdBQVIsRUFBekI7QUFDRDs7QUFFRGYsYUFBU1IsTUFBVCxHQUFrQkEsTUFBbEI7QUFDQSxXQUFPRSxNQUFQO0FBQ0QsR0E3SWM7QUFBQSxtREErSWZzQixNQS9JZSxrQkErSVJDLElBL0lRLEVBK0lGQyxLQS9JRSxFQStJSztBQUNsQixRQUFJLEtBQUsxQyxPQUFMLENBQWEyQyxVQUFqQixFQUE2QjtBQUMzQixhQUFPLEtBQUszQyxPQUFMLENBQWEyQyxVQUFiLENBQXdCRixJQUF4QixFQUE4QkMsS0FBOUIsQ0FBUDtBQUNELEtBRkQsTUFFTztBQUNMLGFBQU9ELFNBQVNDLEtBQVQsSUFDRCxLQUFLMUMsT0FBTCxDQUFhNEMsVUFBYixJQUEyQkgsS0FBS0ksV0FBTCxPQUF1QkgsTUFBTUcsV0FBTixFQUR4RDtBQUVEO0FBQ0YsR0F0SmM7QUFBQSxtREF1SmZyQyxXQXZKZSx1QkF1SkhzQyxLQXZKRyxFQXVKSTtBQUNqQixRQUFJWixNQUFNLEVBQVY7QUFDQSxTQUFLLElBQUlhLElBQUksQ0FBYixFQUFnQkEsSUFBSUQsTUFBTW5DLE1BQTFCLEVBQWtDb0MsR0FBbEMsRUFBdUM7QUFDckMsVUFBSUQsTUFBTUMsQ0FBTixDQUFKLEVBQWM7QUFDWmIsWUFBSUksSUFBSixDQUFTUSxNQUFNQyxDQUFOLENBQVQ7QUFDRDtBQUNGO0FBQ0QsV0FBT2IsR0FBUDtBQUNELEdBL0pjO0FBQUEsbURBZ0tmM0IsU0FoS2UscUJBZ0tMSCxLQWhLSyxFQWdLRTtBQUNmLFdBQU9BLEtBQVA7QUFDRCxHQWxLYztBQUFBLG1EQW1LZkssUUFuS2Usb0JBbUtOTCxLQW5LTSxFQW1LQztBQUNkLFdBQU9BLE1BQU00QyxLQUFOLENBQVksRUFBWixDQUFQO0FBQ0QsR0FyS2M7QUFBQSxtREFzS2Y1QixJQXRLZSxnQkFzS1Y2QixLQXRLVSxFQXNLSDtBQUNWLFdBQU9BLE1BQU03QixJQUFOLENBQVcsRUFBWCxDQUFQO0FBQ0Q7QUF4S2MsQ0FBakI7O0FBMktBLFNBQVNXLFdBQVQsQ0FBcUJsQyxJQUFyQixFQUEyQm9CLFVBQTNCLEVBQXVDbEIsU0FBdkMsRUFBa0RELFNBQWxELEVBQTZEa0MsZUFBN0QsRUFBOEU7QUFDNUUsTUFBSWtCLGVBQWUsQ0FBbkI7QUFBQSxNQUNJQyxlQUFlbEMsV0FBV04sTUFEOUI7QUFBQSxNQUVJSyxTQUFTLENBRmI7QUFBQSxNQUdJRSxTQUFTLENBSGI7O0FBS0EsU0FBT2dDLGVBQWVDLFlBQXRCLEVBQW9DRCxjQUFwQyxFQUFvRDtBQUNsRCxRQUFJRSxZQUFZbkMsV0FBV2lDLFlBQVgsQ0FBaEI7QUFDQSxRQUFJLENBQUNFLFVBQVVoQixPQUFmLEVBQXdCO0FBQ3RCLFVBQUksQ0FBQ2dCLFVBQVVqQixLQUFYLElBQW9CSCxlQUF4QixFQUF5QztBQUN2QyxZQUFJNUIsUUFBUUwsVUFBVXNELEtBQVYsQ0FBZ0JyQyxNQUFoQixFQUF3QkEsU0FBU29DLFVBQVUvQixLQUEzQyxDQUFaO0FBQ0FqQixnQkFBUUEsTUFBTWtELEdBQU4sQ0FBVSxVQUFTbEQsS0FBVCxFQUFnQjJDLENBQWhCLEVBQW1CO0FBQ25DLGNBQUlRLFdBQVd6RCxVQUFVb0IsU0FBUzZCLENBQW5CLENBQWY7QUFDQSxpQkFBT1EsU0FBUzVDLE1BQVQsR0FBa0JQLE1BQU1PLE1BQXhCLEdBQWlDNEMsUUFBakMsR0FBNENuRCxLQUFuRDtBQUNELFNBSE8sQ0FBUjs7QUFLQWdELGtCQUFVaEQsS0FBVixHQUFrQlAsS0FBS3VCLElBQUwsQ0FBVWhCLEtBQVYsQ0FBbEI7QUFDRCxPQVJELE1BUU87QUFDTGdELGtCQUFVaEQsS0FBVixHQUFrQlAsS0FBS3VCLElBQUwsQ0FBVXJCLFVBQVVzRCxLQUFWLENBQWdCckMsTUFBaEIsRUFBd0JBLFNBQVNvQyxVQUFVL0IsS0FBM0MsQ0FBVixDQUFsQjtBQUNEO0FBQ0RMLGdCQUFVb0MsVUFBVS9CLEtBQXBCOztBQUVBO0FBQ0EsVUFBSSxDQUFDK0IsVUFBVWpCLEtBQWYsRUFBc0I7QUFDcEJqQixrQkFBVWtDLFVBQVUvQixLQUFwQjtBQUNEO0FBQ0YsS0FsQkQsTUFrQk87QUFDTCtCLGdCQUFVaEQsS0FBVixHQUFrQlAsS0FBS3VCLElBQUwsQ0FBVXRCLFVBQVV1RCxLQUFWLENBQWdCbkMsTUFBaEIsRUFBd0JBLFNBQVNrQyxVQUFVL0IsS0FBM0MsQ0FBVixDQUFsQjtBQUNBSCxnQkFBVWtDLFVBQVUvQixLQUFwQjs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxVQUFJNkIsZ0JBQWdCakMsV0FBV2lDLGVBQWUsQ0FBMUIsRUFBNkJmLEtBQWpELEVBQXdEO0FBQ3RELFlBQUlxQixNQUFNdkMsV0FBV2lDLGVBQWUsQ0FBMUIsQ0FBVjtBQUNBakMsbUJBQVdpQyxlQUFlLENBQTFCLElBQStCakMsV0FBV2lDLFlBQVgsQ0FBL0I7QUFDQWpDLG1CQUFXaUMsWUFBWCxJQUEyQk0sR0FBM0I7QUFDRDtBQUNGO0FBQ0Y7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsTUFBSUMsZ0JBQWdCeEMsV0FBV2tDLGVBQWUsQ0FBMUIsQ0FBcEI7QUFDQSxNQUFJQSxlQUFlLENBQWYsSUFDRyxPQUFPTSxjQUFjckQsS0FBckIsS0FBK0IsUUFEbEMsS0FFSXFELGNBQWN0QixLQUFkLElBQXVCc0IsY0FBY3JCLE9BRnpDLEtBR0d2QyxLQUFLMkMsTUFBTCxDQUFZLEVBQVosRUFBZ0JpQixjQUFjckQsS0FBOUIsQ0FIUCxFQUc2QztBQUMzQ2EsZUFBV2tDLGVBQWUsQ0FBMUIsRUFBNkIvQyxLQUE3QixJQUFzQ3FELGNBQWNyRCxLQUFwRDtBQUNBYSxlQUFXeUMsR0FBWDtBQUNEOztBQUVELFNBQU96QyxVQUFQO0FBQ0Q7O0FBRUQsU0FBU1ksU0FBVCxDQUFtQjhCLElBQW5CLEVBQXlCO0FBQ3ZCLFNBQU8sRUFBRTNDLFFBQVEyQyxLQUFLM0MsTUFBZixFQUF1QkMsWUFBWTBDLEtBQUsxQyxVQUFMLENBQWdCb0MsS0FBaEIsQ0FBc0IsQ0FBdEIsQ0FBbkMsRUFBUDtBQUNEIiwiZmlsZSI6ImJhc2UuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBEaWZmKCkge31cblxuRGlmZi5wcm90b3R5cGUgPSB7XG4gIGRpZmYob2xkU3RyaW5nLCBuZXdTdHJpbmcsIG9wdGlvbnMgPSB7fSkge1xuICAgIGxldCBjYWxsYmFjayA9IG9wdGlvbnMuY2FsbGJhY2s7XG4gICAgaWYgKHR5cGVvZiBvcHRpb25zID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICBjYWxsYmFjayA9IG9wdGlvbnM7XG4gICAgICBvcHRpb25zID0ge307XG4gICAgfVxuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnM7XG5cbiAgICBsZXQgc2VsZiA9IHRoaXM7XG5cbiAgICBmdW5jdGlvbiBkb25lKHZhbHVlKSB7XG4gICAgICBpZiAoY2FsbGJhY2spIHtcbiAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbigpIHsgY2FsbGJhY2sodW5kZWZpbmVkLCB2YWx1ZSk7IH0sIDApO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBbGxvdyBzdWJjbGFzc2VzIHRvIG1hc3NhZ2UgdGhlIGlucHV0IHByaW9yIHRvIHJ1bm5pbmdcbiAgICBvbGRTdHJpbmcgPSB0aGlzLmNhc3RJbnB1dChvbGRTdHJpbmcpO1xuICAgIG5ld1N0cmluZyA9IHRoaXMuY2FzdElucHV0KG5ld1N0cmluZyk7XG5cbiAgICBvbGRTdHJpbmcgPSB0aGlzLnJlbW92ZUVtcHR5KHRoaXMudG9rZW5pemUob2xkU3RyaW5nKSk7XG4gICAgbmV3U3RyaW5nID0gdGhpcy5yZW1vdmVFbXB0eSh0aGlzLnRva2VuaXplKG5ld1N0cmluZykpO1xuXG4gICAgbGV0IG5ld0xlbiA9IG5ld1N0cmluZy5sZW5ndGgsIG9sZExlbiA9IG9sZFN0cmluZy5sZW5ndGg7XG4gICAgbGV0IGVkaXRMZW5ndGggPSAxO1xuICAgIGxldCBtYXhFZGl0TGVuZ3RoID0gbmV3TGVuICsgb2xkTGVuO1xuICAgIGxldCBiZXN0UGF0aCA9IFt7IG5ld1BvczogLTEsIGNvbXBvbmVudHM6IFtdIH1dO1xuXG4gICAgLy8gU2VlZCBlZGl0TGVuZ3RoID0gMCwgaS5lLiB0aGUgY29udGVudCBzdGFydHMgd2l0aCB0aGUgc2FtZSB2YWx1ZXNcbiAgICBsZXQgb2xkUG9zID0gdGhpcy5leHRyYWN0Q29tbW9uKGJlc3RQYXRoWzBdLCBuZXdTdHJpbmcsIG9sZFN0cmluZywgMCk7XG4gICAgaWYgKGJlc3RQYXRoWzBdLm5ld1BvcyArIDEgPj0gbmV3TGVuICYmIG9sZFBvcyArIDEgPj0gb2xkTGVuKSB7XG4gICAgICAvLyBJZGVudGl0eSBwZXIgdGhlIGVxdWFsaXR5IGFuZCB0b2tlbml6ZXJcbiAgICAgIHJldHVybiBkb25lKFt7dmFsdWU6IHRoaXMuam9pbihuZXdTdHJpbmcpLCBjb3VudDogbmV3U3RyaW5nLmxlbmd0aH1dKTtcbiAgICB9XG5cbiAgICAvLyBNYWluIHdvcmtlciBtZXRob2QuIGNoZWNrcyBhbGwgcGVybXV0YXRpb25zIG9mIGEgZ2l2ZW4gZWRpdCBsZW5ndGggZm9yIGFjY2VwdGFuY2UuXG4gICAgZnVuY3Rpb24gZXhlY0VkaXRMZW5ndGgoKSB7XG4gICAgICBmb3IgKGxldCBkaWFnb25hbFBhdGggPSAtMSAqIGVkaXRMZW5ndGg7IGRpYWdvbmFsUGF0aCA8PSBlZGl0TGVuZ3RoOyBkaWFnb25hbFBhdGggKz0gMikge1xuICAgICAgICBsZXQgYmFzZVBhdGg7XG4gICAgICAgIGxldCBhZGRQYXRoID0gYmVzdFBhdGhbZGlhZ29uYWxQYXRoIC0gMV0sXG4gICAgICAgICAgICByZW1vdmVQYXRoID0gYmVzdFBhdGhbZGlhZ29uYWxQYXRoICsgMV0sXG4gICAgICAgICAgICBvbGRQb3MgPSAocmVtb3ZlUGF0aCA/IHJlbW92ZVBhdGgubmV3UG9zIDogMCkgLSBkaWFnb25hbFBhdGg7XG4gICAgICAgIGlmIChhZGRQYXRoKSB7XG4gICAgICAgICAgLy8gTm8gb25lIGVsc2UgaXMgZ29pbmcgdG8gYXR0ZW1wdCB0byB1c2UgdGhpcyB2YWx1ZSwgY2xlYXIgaXRcbiAgICAgICAgICBiZXN0UGF0aFtkaWFnb25hbFBhdGggLSAxXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBjYW5BZGQgPSBhZGRQYXRoICYmIGFkZFBhdGgubmV3UG9zICsgMSA8IG5ld0xlbixcbiAgICAgICAgICAgIGNhblJlbW92ZSA9IHJlbW92ZVBhdGggJiYgMCA8PSBvbGRQb3MgJiYgb2xkUG9zIDwgb2xkTGVuO1xuICAgICAgICBpZiAoIWNhbkFkZCAmJiAhY2FuUmVtb3ZlKSB7XG4gICAgICAgICAgLy8gSWYgdGhpcyBwYXRoIGlzIGEgdGVybWluYWwgdGhlbiBwcnVuZVxuICAgICAgICAgIGJlc3RQYXRoW2RpYWdvbmFsUGF0aF0gPSB1bmRlZmluZWQ7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTZWxlY3QgdGhlIGRpYWdvbmFsIHRoYXQgd2Ugd2FudCB0byBicmFuY2ggZnJvbS4gV2Ugc2VsZWN0IHRoZSBwcmlvclxuICAgICAgICAvLyBwYXRoIHdob3NlIHBvc2l0aW9uIGluIHRoZSBuZXcgc3RyaW5nIGlzIHRoZSBmYXJ0aGVzdCBmcm9tIHRoZSBvcmlnaW5cbiAgICAgICAgLy8gYW5kIGRvZXMgbm90IHBhc3MgdGhlIGJvdW5kcyBvZiB0aGUgZGlmZiBncmFwaFxuICAgICAgICBpZiAoIWNhbkFkZCB8fCAoY2FuUmVtb3ZlICYmIGFkZFBhdGgubmV3UG9zIDwgcmVtb3ZlUGF0aC5uZXdQb3MpKSB7XG4gICAgICAgICAgYmFzZVBhdGggPSBjbG9uZVBhdGgocmVtb3ZlUGF0aCk7XG4gICAgICAgICAgc2VsZi5wdXNoQ29tcG9uZW50KGJhc2VQYXRoLmNvbXBvbmVudHMsIHVuZGVmaW5lZCwgdHJ1ZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYmFzZVBhdGggPSBhZGRQYXRoOyAgIC8vIE5vIG5lZWQgdG8gY2xvbmUsIHdlJ3ZlIHB1bGxlZCBpdCBmcm9tIHRoZSBsaXN0XG4gICAgICAgICAgYmFzZVBhdGgubmV3UG9zKys7XG4gICAgICAgICAgc2VsZi5wdXNoQ29tcG9uZW50KGJhc2VQYXRoLmNvbXBvbmVudHMsIHRydWUsIHVuZGVmaW5lZCk7XG4gICAgICAgIH1cblxuICAgICAgICBvbGRQb3MgPSBzZWxmLmV4dHJhY3RDb21tb24oYmFzZVBhdGgsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBkaWFnb25hbFBhdGgpO1xuXG4gICAgICAgIC8vIElmIHdlIGhhdmUgaGl0IHRoZSBlbmQgb2YgYm90aCBzdHJpbmdzLCB0aGVuIHdlIGFyZSBkb25lXG4gICAgICAgIGlmIChiYXNlUGF0aC5uZXdQb3MgKyAxID49IG5ld0xlbiAmJiBvbGRQb3MgKyAxID49IG9sZExlbikge1xuICAgICAgICAgIHJldHVybiBkb25lKGJ1aWxkVmFsdWVzKHNlbGYsIGJhc2VQYXRoLmNvbXBvbmVudHMsIG5ld1N0cmluZywgb2xkU3RyaW5nLCBzZWxmLnVzZUxvbmdlc3RUb2tlbikpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIE90aGVyd2lzZSB0cmFjayB0aGlzIHBhdGggYXMgYSBwb3RlbnRpYWwgY2FuZGlkYXRlIGFuZCBjb250aW51ZS5cbiAgICAgICAgICBiZXN0UGF0aFtkaWFnb25hbFBhdGhdID0gYmFzZVBhdGg7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZWRpdExlbmd0aCsrO1xuICAgIH1cblxuICAgIC8vIFBlcmZvcm1zIHRoZSBsZW5ndGggb2YgZWRpdCBpdGVyYXRpb24uIElzIGEgYml0IGZ1Z2x5IGFzIHRoaXMgaGFzIHRvIHN1cHBvcnQgdGhlXG4gICAgLy8gc3luYyBhbmQgYXN5bmMgbW9kZSB3aGljaCBpcyBuZXZlciBmdW4uIExvb3BzIG92ZXIgZXhlY0VkaXRMZW5ndGggdW50aWwgYSB2YWx1ZVxuICAgIC8vIGlzIHByb2R1Y2VkLlxuICAgIGlmIChjYWxsYmFjaykge1xuICAgICAgKGZ1bmN0aW9uIGV4ZWMoKSB7XG4gICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgLy8gVGhpcyBzaG91bGQgbm90IGhhcHBlbiwgYnV0IHdlIHdhbnQgdG8gYmUgc2FmZS5cbiAgICAgICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgbmV4dCAqL1xuICAgICAgICAgIGlmIChlZGl0TGVuZ3RoID4gbWF4RWRpdExlbmd0aCkge1xuICAgICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKCFleGVjRWRpdExlbmd0aCgpKSB7XG4gICAgICAgICAgICBleGVjKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9LCAwKTtcbiAgICAgIH0oKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHdoaWxlIChlZGl0TGVuZ3RoIDw9IG1heEVkaXRMZW5ndGgpIHtcbiAgICAgICAgbGV0IHJldCA9IGV4ZWNFZGl0TGVuZ3RoKCk7XG4gICAgICAgIGlmIChyZXQpIHtcbiAgICAgICAgICByZXR1cm4gcmV0O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIHB1c2hDb21wb25lbnQoY29tcG9uZW50cywgYWRkZWQsIHJlbW92ZWQpIHtcbiAgICBsZXQgbGFzdCA9IGNvbXBvbmVudHNbY29tcG9uZW50cy5sZW5ndGggLSAxXTtcbiAgICBpZiAobGFzdCAmJiBsYXN0LmFkZGVkID09PSBhZGRlZCAmJiBsYXN0LnJlbW92ZWQgPT09IHJlbW92ZWQpIHtcbiAgICAgIC8vIFdlIG5lZWQgdG8gY2xvbmUgaGVyZSBhcyB0aGUgY29tcG9uZW50IGNsb25lIG9wZXJhdGlvbiBpcyBqdXN0XG4gICAgICAvLyBhcyBzaGFsbG93IGFycmF5IGNsb25lXG4gICAgICBjb21wb25lbnRzW2NvbXBvbmVudHMubGVuZ3RoIC0gMV0gPSB7Y291bnQ6IGxhc3QuY291bnQgKyAxLCBhZGRlZDogYWRkZWQsIHJlbW92ZWQ6IHJlbW92ZWQgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29tcG9uZW50cy5wdXNoKHtjb3VudDogMSwgYWRkZWQ6IGFkZGVkLCByZW1vdmVkOiByZW1vdmVkIH0pO1xuICAgIH1cbiAgfSxcbiAgZXh0cmFjdENvbW1vbihiYXNlUGF0aCwgbmV3U3RyaW5nLCBvbGRTdHJpbmcsIGRpYWdvbmFsUGF0aCkge1xuICAgIGxldCBuZXdMZW4gPSBuZXdTdHJpbmcubGVuZ3RoLFxuICAgICAgICBvbGRMZW4gPSBvbGRTdHJpbmcubGVuZ3RoLFxuICAgICAgICBuZXdQb3MgPSBiYXNlUGF0aC5uZXdQb3MsXG4gICAgICAgIG9sZFBvcyA9IG5ld1BvcyAtIGRpYWdvbmFsUGF0aCxcblxuICAgICAgICBjb21tb25Db3VudCA9IDA7XG4gICAgd2hpbGUgKG5ld1BvcyArIDEgPCBuZXdMZW4gJiYgb2xkUG9zICsgMSA8IG9sZExlbiAmJiB0aGlzLmVxdWFscyhuZXdTdHJpbmdbbmV3UG9zICsgMV0sIG9sZFN0cmluZ1tvbGRQb3MgKyAxXSkpIHtcbiAgICAgIG5ld1BvcysrO1xuICAgICAgb2xkUG9zKys7XG4gICAgICBjb21tb25Db3VudCsrO1xuICAgIH1cblxuICAgIGlmIChjb21tb25Db3VudCkge1xuICAgICAgYmFzZVBhdGguY29tcG9uZW50cy5wdXNoKHtjb3VudDogY29tbW9uQ291bnR9KTtcbiAgICB9XG5cbiAgICBiYXNlUGF0aC5uZXdQb3MgPSBuZXdQb3M7XG4gICAgcmV0dXJuIG9sZFBvcztcbiAgfSxcblxuICBlcXVhbHMobGVmdCwgcmlnaHQpIHtcbiAgICBpZiAodGhpcy5vcHRpb25zLmNvbXBhcmF0b3IpIHtcbiAgICAgIHJldHVybiB0aGlzLm9wdGlvbnMuY29tcGFyYXRvcihsZWZ0LCByaWdodCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBsZWZ0ID09PSByaWdodFxuICAgICAgICB8fCAodGhpcy5vcHRpb25zLmlnbm9yZUNhc2UgJiYgbGVmdC50b0xvd2VyQ2FzZSgpID09PSByaWdodC50b0xvd2VyQ2FzZSgpKTtcbiAgICB9XG4gIH0sXG4gIHJlbW92ZUVtcHR5KGFycmF5KSB7XG4gICAgbGV0IHJldCA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYXJyYXkubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChhcnJheVtpXSkge1xuICAgICAgICByZXQucHVzaChhcnJheVtpXSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXQ7XG4gIH0sXG4gIGNhc3RJbnB1dCh2YWx1ZSkge1xuICAgIHJldHVybiB2YWx1ZTtcbiAgfSxcbiAgdG9rZW5pemUodmFsdWUpIHtcbiAgICByZXR1cm4gdmFsdWUuc3BsaXQoJycpO1xuICB9LFxuICBqb2luKGNoYXJzKSB7XG4gICAgcmV0dXJuIGNoYXJzLmpvaW4oJycpO1xuICB9XG59O1xuXG5mdW5jdGlvbiBidWlsZFZhbHVlcyhkaWZmLCBjb21wb25lbnRzLCBuZXdTdHJpbmcsIG9sZFN0cmluZywgdXNlTG9uZ2VzdFRva2VuKSB7XG4gIGxldCBjb21wb25lbnRQb3MgPSAwLFxuICAgICAgY29tcG9uZW50TGVuID0gY29tcG9uZW50cy5sZW5ndGgsXG4gICAgICBuZXdQb3MgPSAwLFxuICAgICAgb2xkUG9zID0gMDtcblxuICBmb3IgKDsgY29tcG9uZW50UG9zIDwgY29tcG9uZW50TGVuOyBjb21wb25lbnRQb3MrKykge1xuICAgIGxldCBjb21wb25lbnQgPSBjb21wb25lbnRzW2NvbXBvbmVudFBvc107XG4gICAgaWYgKCFjb21wb25lbnQucmVtb3ZlZCkge1xuICAgICAgaWYgKCFjb21wb25lbnQuYWRkZWQgJiYgdXNlTG9uZ2VzdFRva2VuKSB7XG4gICAgICAgIGxldCB2YWx1ZSA9IG5ld1N0cmluZy5zbGljZShuZXdQb3MsIG5ld1BvcyArIGNvbXBvbmVudC5jb3VudCk7XG4gICAgICAgIHZhbHVlID0gdmFsdWUubWFwKGZ1bmN0aW9uKHZhbHVlLCBpKSB7XG4gICAgICAgICAgbGV0IG9sZFZhbHVlID0gb2xkU3RyaW5nW29sZFBvcyArIGldO1xuICAgICAgICAgIHJldHVybiBvbGRWYWx1ZS5sZW5ndGggPiB2YWx1ZS5sZW5ndGggPyBvbGRWYWx1ZSA6IHZhbHVlO1xuICAgICAgICB9KTtcblxuICAgICAgICBjb21wb25lbnQudmFsdWUgPSBkaWZmLmpvaW4odmFsdWUpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29tcG9uZW50LnZhbHVlID0gZGlmZi5qb2luKG5ld1N0cmluZy5zbGljZShuZXdQb3MsIG5ld1BvcyArIGNvbXBvbmVudC5jb3VudCkpO1xuICAgICAgfVxuICAgICAgbmV3UG9zICs9IGNvbXBvbmVudC5jb3VudDtcblxuICAgICAgLy8gQ29tbW9uIGNhc2VcbiAgICAgIGlmICghY29tcG9uZW50LmFkZGVkKSB7XG4gICAgICAgIG9sZFBvcyArPSBjb21wb25lbnQuY291bnQ7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbXBvbmVudC52YWx1ZSA9IGRpZmYuam9pbihvbGRTdHJpbmcuc2xpY2Uob2xkUG9zLCBvbGRQb3MgKyBjb21wb25lbnQuY291bnQpKTtcbiAgICAgIG9sZFBvcyArPSBjb21wb25lbnQuY291bnQ7XG5cbiAgICAgIC8vIFJldmVyc2UgYWRkIGFuZCByZW1vdmUgc28gcmVtb3ZlcyBhcmUgb3V0cHV0IGZpcnN0IHRvIG1hdGNoIGNvbW1vbiBjb252ZW50aW9uXG4gICAgICAvLyBUaGUgZGlmZmluZyBhbGdvcml0aG0gaXMgdGllZCB0byBhZGQgdGhlbiByZW1vdmUgb3V0cHV0IGFuZCB0aGlzIGlzIHRoZSBzaW1wbGVzdFxuICAgICAgLy8gcm91dGUgdG8gZ2V0IHRoZSBkZXNpcmVkIG91dHB1dCB3aXRoIG1pbmltYWwgb3ZlcmhlYWQuXG4gICAgICBpZiAoY29tcG9uZW50UG9zICYmIGNvbXBvbmVudHNbY29tcG9uZW50UG9zIC0gMV0uYWRkZWQpIHtcbiAgICAgICAgbGV0IHRtcCA9IGNvbXBvbmVudHNbY29tcG9uZW50UG9zIC0gMV07XG4gICAgICAgIGNvbXBvbmVudHNbY29tcG9uZW50UG9zIC0gMV0gPSBjb21wb25lbnRzW2NvbXBvbmVudFBvc107XG4gICAgICAgIGNvbXBvbmVudHNbY29tcG9uZW50UG9zXSA9IHRtcDtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBTcGVjaWFsIGNhc2UgaGFuZGxlIGZvciB3aGVuIG9uZSB0ZXJtaW5hbCBpcyBpZ25vcmVkIChpLmUuIHdoaXRlc3BhY2UpLlxuICAvLyBGb3IgdGhpcyBjYXNlIHdlIG1lcmdlIHRoZSB0ZXJtaW5hbCBpbnRvIHRoZSBwcmlvciBzdHJpbmcgYW5kIGRyb3AgdGhlIGNoYW5nZS5cbiAgLy8gVGhpcyBpcyBvbmx5IGF2YWlsYWJsZSBmb3Igc3RyaW5nIG1vZGUuXG4gIGxldCBsYXN0Q29tcG9uZW50ID0gY29tcG9uZW50c1tjb21wb25lbnRMZW4gLSAxXTtcbiAgaWYgKGNvbXBvbmVudExlbiA+IDFcbiAgICAgICYmIHR5cGVvZiBsYXN0Q29tcG9uZW50LnZhbHVlID09PSAnc3RyaW5nJ1xuICAgICAgJiYgKGxhc3RDb21wb25lbnQuYWRkZWQgfHwgbGFzdENvbXBvbmVudC5yZW1vdmVkKVxuICAgICAgJiYgZGlmZi5lcXVhbHMoJycsIGxhc3RDb21wb25lbnQudmFsdWUpKSB7XG4gICAgY29tcG9uZW50c1tjb21wb25lbnRMZW4gLSAyXS52YWx1ZSArPSBsYXN0Q29tcG9uZW50LnZhbHVlO1xuICAgIGNvbXBvbmVudHMucG9wKCk7XG4gIH1cblxuICByZXR1cm4gY29tcG9uZW50cztcbn1cblxuZnVuY3Rpb24gY2xvbmVQYXRoKHBhdGgpIHtcbiAgcmV0dXJuIHsgbmV3UG9zOiBwYXRoLm5ld1BvcywgY29tcG9uZW50czogcGF0aC5jb21wb25lbnRzLnNsaWNlKDApIH07XG59XG4iXX0= diff --git a/WebServer/node_modules/diff/lib/diff/character.js b/WebServer/node_modules/diff/lib/diff/character.js new file mode 100644 index 000000000..e15da7a16 --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/character.js @@ -0,0 +1,17 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.characterDiff = undefined; +exports. /*istanbul ignore end*/diffChars = diffChars; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var characterDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/characterDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); +function diffChars(oldStr, newStr, options) { + return characterDiff.diff(oldStr, newStr, options); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2NoYXJhY3Rlci5qcyJdLCJuYW1lcyI6WyJkaWZmQ2hhcnMiLCJjaGFyYWN0ZXJEaWZmIiwib2xkU3RyIiwibmV3U3RyIiwib3B0aW9ucyIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBR2dCQSxTLEdBQUFBLFM7O0FBSGhCOzs7Ozs7dUJBRU8sSUFBTUMseUZBQWdCLHdFQUF0QjtBQUNBLFNBQVNELFNBQVQsQ0FBbUJFLE1BQW5CLEVBQTJCQyxNQUEzQixFQUFtQ0MsT0FBbkMsRUFBNEM7QUFBRSxTQUFPSCxjQUFjSSxJQUFkLENBQW1CSCxNQUFuQixFQUEyQkMsTUFBM0IsRUFBbUNDLE9BQW5DLENBQVA7QUFBcUQiLCJmaWxlIjoiY2hhcmFjdGVyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcblxuZXhwb3J0IGNvbnN0IGNoYXJhY3RlckRpZmYgPSBuZXcgRGlmZigpO1xuZXhwb3J0IGZ1bmN0aW9uIGRpZmZDaGFycyhvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykgeyByZXR1cm4gY2hhcmFjdGVyRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTsgfVxuIl19 diff --git a/WebServer/node_modules/diff/lib/diff/css.js b/WebServer/node_modules/diff/lib/diff/css.js new file mode 100644 index 000000000..640af5e16 --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/css.js @@ -0,0 +1,21 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.cssDiff = undefined; +exports. /*istanbul ignore end*/diffCss = diffCss; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var cssDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/cssDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); +cssDiff.tokenize = function (value) { + return value.split(/([{}:;,]|\s+)/); +}; + +function diffCss(oldStr, newStr, callback) { + return cssDiff.diff(oldStr, newStr, callback); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2Nzcy5qcyJdLCJuYW1lcyI6WyJkaWZmQ3NzIiwiY3NzRGlmZiIsInRva2VuaXplIiwidmFsdWUiLCJzcGxpdCIsIm9sZFN0ciIsIm5ld1N0ciIsImNhbGxiYWNrIiwiZGlmZiJdLCJtYXBwaW5ncyI6Ijs7OztnQ0FPZ0JBLE8sR0FBQUEsTzs7QUFQaEI7Ozs7Ozt1QkFFTyxJQUFNQyw2RUFBVSx3RUFBaEI7QUFDUEEsUUFBUUMsUUFBUixHQUFtQixVQUFTQyxLQUFULEVBQWdCO0FBQ2pDLFNBQU9BLE1BQU1DLEtBQU4sQ0FBWSxlQUFaLENBQVA7QUFDRCxDQUZEOztBQUlPLFNBQVNKLE9BQVQsQ0FBaUJLLE1BQWpCLEVBQXlCQyxNQUF6QixFQUFpQ0MsUUFBakMsRUFBMkM7QUFBRSxTQUFPTixRQUFRTyxJQUFSLENBQWFILE1BQWIsRUFBcUJDLE1BQXJCLEVBQTZCQyxRQUE3QixDQUFQO0FBQWdEIiwiZmlsZSI6ImNzcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cbmV4cG9ydCBjb25zdCBjc3NEaWZmID0gbmV3IERpZmYoKTtcbmNzc0RpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUuc3BsaXQoLyhbe306OyxdfFxccyspLyk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkNzcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHsgcmV0dXJuIGNzc0RpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spOyB9XG4iXX0= diff --git a/WebServer/node_modules/diff/lib/diff/json.js b/WebServer/node_modules/diff/lib/diff/json.js new file mode 100644 index 000000000..ca21739a5 --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/json.js @@ -0,0 +1,108 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.jsonDiff = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +exports. /*istanbul ignore end*/diffJson = diffJson; +/*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = canonicalize; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +/*istanbul ignore end*/var /*istanbul ignore start*/_line = require('./line') /*istanbul ignore end*/; + +/*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var objectPrototypeToString = Object.prototype.toString; + +var jsonDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/jsonDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); +// Discriminate between two lines of pretty-printed, serialized JSON where one of them has a +// dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: +jsonDiff.useLongestToken = true; + +jsonDiff.tokenize = /*istanbul ignore start*/_line.lineDiff /*istanbul ignore end*/.tokenize; +jsonDiff.castInput = function (value) { + /*istanbul ignore start*/var _options = /*istanbul ignore end*/this.options, + undefinedReplacement = _options.undefinedReplacement, + _options$stringifyRep = _options.stringifyReplacer, + stringifyReplacer = _options$stringifyRep === undefined ? function (k, v) /*istanbul ignore start*/{ + return (/*istanbul ignore end*/typeof v === 'undefined' ? undefinedReplacement : v + ); + } : _options$stringifyRep; + + + return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); +}; +jsonDiff.equals = function (left, right) { + return (/*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')) + ); +}; + +function diffJson(oldObj, newObj, options) { + return jsonDiff.diff(oldObj, newObj, options); +} + +// This function handles the presence of circular references by bailing out when encountering an +// object that is already on the "stack" of items being processed. Accepts an optional replacer +function canonicalize(obj, stack, replacementStack, replacer, key) { + stack = stack || []; + replacementStack = replacementStack || []; + + if (replacer) { + obj = replacer(key, obj); + } + + var i = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + for (i = 0; i < stack.length; i += 1) { + if (stack[i] === obj) { + return replacementStack[i]; + } + } + + var canonicalizedObj = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + if ('[object Array]' === objectPrototypeToString.call(obj)) { + stack.push(obj); + canonicalizedObj = new Array(obj.length); + replacementStack.push(canonicalizedObj); + for (i = 0; i < obj.length; i += 1) { + canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); + } + stack.pop(); + replacementStack.pop(); + return canonicalizedObj; + } + + if (obj && obj.toJSON) { + obj = obj.toJSON(); + } + + if ( /*istanbul ignore start*/(typeof /*istanbul ignore end*/obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + replacementStack.push(canonicalizedObj); + var sortedKeys = [], + _key = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (_key in obj) { + /* istanbul ignore else */ + if (obj.hasOwnProperty(_key)) { + sortedKeys.push(_key); + } + } + sortedKeys.sort(); + for (i = 0; i < sortedKeys.length; i += 1) { + _key = sortedKeys[i]; + canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); + } + stack.pop(); + replacementStack.pop(); + } else { + canonicalizedObj = obj; + } + return canonicalizedObj; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2pzb24uanMiXSwibmFtZXMiOlsiZGlmZkpzb24iLCJjYW5vbmljYWxpemUiLCJvYmplY3RQcm90b3R5cGVUb1N0cmluZyIsIk9iamVjdCIsInByb3RvdHlwZSIsInRvU3RyaW5nIiwianNvbkRpZmYiLCJ1c2VMb25nZXN0VG9rZW4iLCJ0b2tlbml6ZSIsImNhc3RJbnB1dCIsInZhbHVlIiwib3B0aW9ucyIsInVuZGVmaW5lZFJlcGxhY2VtZW50Iiwic3RyaW5naWZ5UmVwbGFjZXIiLCJrIiwidiIsIkpTT04iLCJzdHJpbmdpZnkiLCJlcXVhbHMiLCJsZWZ0IiwicmlnaHQiLCJjYWxsIiwicmVwbGFjZSIsIm9sZE9iaiIsIm5ld09iaiIsImRpZmYiLCJvYmoiLCJzdGFjayIsInJlcGxhY2VtZW50U3RhY2siLCJyZXBsYWNlciIsImtleSIsImkiLCJsZW5ndGgiLCJjYW5vbmljYWxpemVkT2JqIiwicHVzaCIsIkFycmF5IiwicG9wIiwidG9KU09OIiwic29ydGVkS2V5cyIsImhhc093blByb3BlcnR5Iiwic29ydCJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztnQ0FxQmdCQSxRLEdBQUFBLFE7eURBSUFDLFksR0FBQUEsWTs7QUF6QmhCOzs7O3VCQUNBOzs7O3VCQUVBLElBQU1DLDBCQUEwQkMsT0FBT0MsU0FBUCxDQUFpQkMsUUFBakQ7O0FBR08sSUFBTUMsK0VBQVcsd0VBQWpCO0FBQ1A7QUFDQTtBQUNBQSxTQUFTQyxlQUFULEdBQTJCLElBQTNCOztBQUVBRCxTQUFTRSxRQUFULEdBQW9CLGdFQUFTQSxRQUE3QjtBQUNBRixTQUFTRyxTQUFULEdBQXFCLFVBQVNDLEtBQVQsRUFBZ0I7QUFBQSxpRUFDK0UsS0FBS0MsT0FEcEY7QUFBQSxNQUM1QkMsb0JBRDRCLFlBQzVCQSxvQkFENEI7QUFBQSx1Q0FDTkMsaUJBRE07QUFBQSxNQUNOQSxpQkFETSx5Q0FDYyxVQUFDQyxDQUFELEVBQUlDLENBQUo7QUFBQSxtQ0FBVSxPQUFPQSxDQUFQLEtBQWEsV0FBYixHQUEyQkgsb0JBQTNCLEdBQWtERztBQUE1RDtBQUFBLEdBRGQ7OztBQUduQyxTQUFPLE9BQU9MLEtBQVAsS0FBaUIsUUFBakIsR0FBNEJBLEtBQTVCLEdBQW9DTSxLQUFLQyxTQUFMLENBQWVoQixhQUFhUyxLQUFiLEVBQW9CLElBQXBCLEVBQTBCLElBQTFCLEVBQWdDRyxpQkFBaEMsQ0FBZixFQUFtRUEsaUJBQW5FLEVBQXNGLElBQXRGLENBQTNDO0FBQ0QsQ0FKRDtBQUtBUCxTQUFTWSxNQUFULEdBQWtCLFVBQVNDLElBQVQsRUFBZUMsS0FBZixFQUFzQjtBQUN0QyxTQUFPLG9FQUFLaEIsU0FBTCxDQUFlYyxNQUFmLENBQXNCRyxJQUF0QixDQUEyQmYsUUFBM0IsRUFBcUNhLEtBQUtHLE9BQUwsQ0FBYSxZQUFiLEVBQTJCLElBQTNCLENBQXJDLEVBQXVFRixNQUFNRSxPQUFOLENBQWMsWUFBZCxFQUE0QixJQUE1QixDQUF2RTtBQUFQO0FBQ0QsQ0FGRDs7QUFJTyxTQUFTdEIsUUFBVCxDQUFrQnVCLE1BQWxCLEVBQTBCQyxNQUExQixFQUFrQ2IsT0FBbEMsRUFBMkM7QUFBRSxTQUFPTCxTQUFTbUIsSUFBVCxDQUFjRixNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmIsT0FBOUIsQ0FBUDtBQUFnRDs7QUFFcEc7QUFDQTtBQUNPLFNBQVNWLFlBQVQsQ0FBc0J5QixHQUF0QixFQUEyQkMsS0FBM0IsRUFBa0NDLGdCQUFsQyxFQUFvREMsUUFBcEQsRUFBOERDLEdBQTlELEVBQW1FO0FBQ3hFSCxVQUFRQSxTQUFTLEVBQWpCO0FBQ0FDLHFCQUFtQkEsb0JBQW9CLEVBQXZDOztBQUVBLE1BQUlDLFFBQUosRUFBYztBQUNaSCxVQUFNRyxTQUFTQyxHQUFULEVBQWNKLEdBQWQsQ0FBTjtBQUNEOztBQUVELE1BQUlLLG1DQUFKOztBQUVBLE9BQUtBLElBQUksQ0FBVCxFQUFZQSxJQUFJSixNQUFNSyxNQUF0QixFQUE4QkQsS0FBSyxDQUFuQyxFQUFzQztBQUNwQyxRQUFJSixNQUFNSSxDQUFOLE1BQWFMLEdBQWpCLEVBQXNCO0FBQ3BCLGFBQU9FLGlCQUFpQkcsQ0FBakIsQ0FBUDtBQUNEO0FBQ0Y7O0FBRUQsTUFBSUUsa0RBQUo7O0FBRUEsTUFBSSxxQkFBcUIvQix3QkFBd0JtQixJQUF4QixDQUE2QkssR0FBN0IsQ0FBekIsRUFBNEQ7QUFDMURDLFVBQU1PLElBQU4sQ0FBV1IsR0FBWDtBQUNBTyx1QkFBbUIsSUFBSUUsS0FBSixDQUFVVCxJQUFJTSxNQUFkLENBQW5CO0FBQ0FKLHFCQUFpQk0sSUFBakIsQ0FBc0JELGdCQUF0QjtBQUNBLFNBQUtGLElBQUksQ0FBVCxFQUFZQSxJQUFJTCxJQUFJTSxNQUFwQixFQUE0QkQsS0FBSyxDQUFqQyxFQUFvQztBQUNsQ0UsdUJBQWlCRixDQUFqQixJQUFzQjlCLGFBQWF5QixJQUFJSyxDQUFKLENBQWIsRUFBcUJKLEtBQXJCLEVBQTRCQyxnQkFBNUIsRUFBOENDLFFBQTlDLEVBQXdEQyxHQUF4RCxDQUF0QjtBQUNEO0FBQ0RILFVBQU1TLEdBQU47QUFDQVIscUJBQWlCUSxHQUFqQjtBQUNBLFdBQU9ILGdCQUFQO0FBQ0Q7O0FBRUQsTUFBSVAsT0FBT0EsSUFBSVcsTUFBZixFQUF1QjtBQUNyQlgsVUFBTUEsSUFBSVcsTUFBSixFQUFOO0FBQ0Q7O0FBRUQsTUFBSSx5REFBT1gsR0FBUCx5Q0FBT0EsR0FBUCxPQUFlLFFBQWYsSUFBMkJBLFFBQVEsSUFBdkMsRUFBNkM7QUFDM0NDLFVBQU1PLElBQU4sQ0FBV1IsR0FBWDtBQUNBTyx1QkFBbUIsRUFBbkI7QUFDQUwscUJBQWlCTSxJQUFqQixDQUFzQkQsZ0JBQXRCO0FBQ0EsUUFBSUssYUFBYSxFQUFqQjtBQUFBLFFBQ0lSLHNDQURKO0FBRUEsU0FBS0EsSUFBTCxJQUFZSixHQUFaLEVBQWlCO0FBQ2Y7QUFDQSxVQUFJQSxJQUFJYSxjQUFKLENBQW1CVCxJQUFuQixDQUFKLEVBQTZCO0FBQzNCUSxtQkFBV0osSUFBWCxDQUFnQkosSUFBaEI7QUFDRDtBQUNGO0FBQ0RRLGVBQVdFLElBQVg7QUFDQSxTQUFLVCxJQUFJLENBQVQsRUFBWUEsSUFBSU8sV0FBV04sTUFBM0IsRUFBbUNELEtBQUssQ0FBeEMsRUFBMkM7QUFDekNELGFBQU1RLFdBQVdQLENBQVgsQ0FBTjtBQUNBRSx1QkFBaUJILElBQWpCLElBQXdCN0IsYUFBYXlCLElBQUlJLElBQUosQ0FBYixFQUF1QkgsS0FBdkIsRUFBOEJDLGdCQUE5QixFQUFnREMsUUFBaEQsRUFBMERDLElBQTFELENBQXhCO0FBQ0Q7QUFDREgsVUFBTVMsR0FBTjtBQUNBUixxQkFBaUJRLEdBQWpCO0FBQ0QsR0FuQkQsTUFtQk87QUFDTEgsdUJBQW1CUCxHQUFuQjtBQUNEO0FBQ0QsU0FBT08sZ0JBQVA7QUFDRCIsImZpbGUiOiJqc29uLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERpZmYgZnJvbSAnLi9iYXNlJztcbmltcG9ydCB7bGluZURpZmZ9IGZyb20gJy4vbGluZSc7XG5cbmNvbnN0IG9iamVjdFByb3RvdHlwZVRvU3RyaW5nID0gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZztcblxuXG5leHBvcnQgY29uc3QganNvbkRpZmYgPSBuZXcgRGlmZigpO1xuLy8gRGlzY3JpbWluYXRlIGJldHdlZW4gdHdvIGxpbmVzIG9mIHByZXR0eS1wcmludGVkLCBzZXJpYWxpemVkIEpTT04gd2hlcmUgb25lIG9mIHRoZW0gaGFzIGFcbi8vIGRhbmdsaW5nIGNvbW1hIGFuZCB0aGUgb3RoZXIgZG9lc24ndC4gVHVybnMgb3V0IGluY2x1ZGluZyB0aGUgZGFuZ2xpbmcgY29tbWEgeWllbGRzIHRoZSBuaWNlc3Qgb3V0cHV0OlxuanNvbkRpZmYudXNlTG9uZ2VzdFRva2VuID0gdHJ1ZTtcblxuanNvbkRpZmYudG9rZW5pemUgPSBsaW5lRGlmZi50b2tlbml6ZTtcbmpzb25EaWZmLmNhc3RJbnB1dCA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIGNvbnN0IHt1bmRlZmluZWRSZXBsYWNlbWVudCwgc3RyaW5naWZ5UmVwbGFjZXIgPSAoaywgdikgPT4gdHlwZW9mIHYgPT09ICd1bmRlZmluZWQnID8gdW5kZWZpbmVkUmVwbGFjZW1lbnQgOiB2fSA9IHRoaXMub3B0aW9ucztcblxuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/IHZhbHVlIDogSlNPTi5zdHJpbmdpZnkoY2Fub25pY2FsaXplKHZhbHVlLCBudWxsLCBudWxsLCBzdHJpbmdpZnlSZXBsYWNlciksIHN0cmluZ2lmeVJlcGxhY2VyLCAnICAnKTtcbn07XG5qc29uRGlmZi5lcXVhbHMgPSBmdW5jdGlvbihsZWZ0LCByaWdodCkge1xuICByZXR1cm4gRGlmZi5wcm90b3R5cGUuZXF1YWxzLmNhbGwoanNvbkRpZmYsIGxlZnQucmVwbGFjZSgvLChbXFxyXFxuXSkvZywgJyQxJyksIHJpZ2h0LnJlcGxhY2UoLywoW1xcclxcbl0pL2csICckMScpKTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBkaWZmSnNvbihvbGRPYmosIG5ld09iaiwgb3B0aW9ucykgeyByZXR1cm4ganNvbkRpZmYuZGlmZihvbGRPYmosIG5ld09iaiwgb3B0aW9ucyk7IH1cblxuLy8gVGhpcyBmdW5jdGlvbiBoYW5kbGVzIHRoZSBwcmVzZW5jZSBvZiBjaXJjdWxhciByZWZlcmVuY2VzIGJ5IGJhaWxpbmcgb3V0IHdoZW4gZW5jb3VudGVyaW5nIGFuXG4vLyBvYmplY3QgdGhhdCBpcyBhbHJlYWR5IG9uIHRoZSBcInN0YWNrXCIgb2YgaXRlbXMgYmVpbmcgcHJvY2Vzc2VkLiBBY2NlcHRzIGFuIG9wdGlvbmFsIHJlcGxhY2VyXG5leHBvcnQgZnVuY3Rpb24gY2Fub25pY2FsaXplKG9iaiwgc3RhY2ssIHJlcGxhY2VtZW50U3RhY2ssIHJlcGxhY2VyLCBrZXkpIHtcbiAgc3RhY2sgPSBzdGFjayB8fCBbXTtcbiAgcmVwbGFjZW1lbnRTdGFjayA9IHJlcGxhY2VtZW50U3RhY2sgfHwgW107XG5cbiAgaWYgKHJlcGxhY2VyKSB7XG4gICAgb2JqID0gcmVwbGFjZXIoa2V5LCBvYmopO1xuICB9XG5cbiAgbGV0IGk7XG5cbiAgZm9yIChpID0gMDsgaSA8IHN0YWNrLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgaWYgKHN0YWNrW2ldID09PSBvYmopIHtcbiAgICAgIHJldHVybiByZXBsYWNlbWVudFN0YWNrW2ldO1xuICAgIH1cbiAgfVxuXG4gIGxldCBjYW5vbmljYWxpemVkT2JqO1xuXG4gIGlmICgnW29iamVjdCBBcnJheV0nID09PSBvYmplY3RQcm90b3R5cGVUb1N0cmluZy5jYWxsKG9iaikpIHtcbiAgICBzdGFjay5wdXNoKG9iaik7XG4gICAgY2Fub25pY2FsaXplZE9iaiA9IG5ldyBBcnJheShvYmoubGVuZ3RoKTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnB1c2goY2Fub25pY2FsaXplZE9iaik7XG4gICAgZm9yIChpID0gMDsgaSA8IG9iai5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgY2Fub25pY2FsaXplZE9ialtpXSA9IGNhbm9uaWNhbGl6ZShvYmpbaV0sIHN0YWNrLCByZXBsYWNlbWVudFN0YWNrLCByZXBsYWNlciwga2V5KTtcbiAgICB9XG4gICAgc3RhY2sucG9wKCk7XG4gICAgcmVwbGFjZW1lbnRTdGFjay5wb3AoKTtcbiAgICByZXR1cm4gY2Fub25pY2FsaXplZE9iajtcbiAgfVxuXG4gIGlmIChvYmogJiYgb2JqLnRvSlNPTikge1xuICAgIG9iaiA9IG9iai50b0pTT04oKTtcbiAgfVxuXG4gIGlmICh0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiBvYmogIT09IG51bGwpIHtcbiAgICBzdGFjay5wdXNoKG9iaik7XG4gICAgY2Fub25pY2FsaXplZE9iaiA9IHt9O1xuICAgIHJlcGxhY2VtZW50U3RhY2sucHVzaChjYW5vbmljYWxpemVkT2JqKTtcbiAgICBsZXQgc29ydGVkS2V5cyA9IFtdLFxuICAgICAgICBrZXk7XG4gICAgZm9yIChrZXkgaW4gb2JqKSB7XG4gICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICAgICAgaWYgKG9iai5oYXNPd25Qcm9wZXJ0eShrZXkpKSB7XG4gICAgICAgIHNvcnRlZEtleXMucHVzaChrZXkpO1xuICAgICAgfVxuICAgIH1cbiAgICBzb3J0ZWRLZXlzLnNvcnQoKTtcbiAgICBmb3IgKGkgPSAwOyBpIDwgc29ydGVkS2V5cy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAga2V5ID0gc29ydGVkS2V5c1tpXTtcbiAgICAgIGNhbm9uaWNhbGl6ZWRPYmpba2V5XSA9IGNhbm9uaWNhbGl6ZShvYmpba2V5XSwgc3RhY2ssIHJlcGxhY2VtZW50U3RhY2ssIHJlcGxhY2VyLCBrZXkpO1xuICAgIH1cbiAgICBzdGFjay5wb3AoKTtcbiAgICByZXBsYWNlbWVudFN0YWNrLnBvcCgpO1xuICB9IGVsc2Uge1xuICAgIGNhbm9uaWNhbGl6ZWRPYmogPSBvYmo7XG4gIH1cbiAgcmV0dXJuIGNhbm9uaWNhbGl6ZWRPYmo7XG59XG4iXX0= diff --git a/WebServer/node_modules/diff/lib/diff/line.js b/WebServer/node_modules/diff/lib/diff/line.js new file mode 100644 index 000000000..f03eedbd1 --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/line.js @@ -0,0 +1,50 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.lineDiff = undefined; +exports. /*istanbul ignore end*/diffLines = diffLines; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = diffTrimmedLines; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +/*istanbul ignore end*/var /*istanbul ignore start*/_params = require('../util/params') /*istanbul ignore end*/; + +/*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var lineDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/lineDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); +lineDiff.tokenize = function (value) { + var retLines = [], + linesAndNewlines = value.split(/(\n|\r\n)/); + + // Ignore the final empty token that occurs if the string ends with a new line + if (!linesAndNewlines[linesAndNewlines.length - 1]) { + linesAndNewlines.pop(); + } + + // Merge the content and line separators into single tokens + for (var i = 0; i < linesAndNewlines.length; i++) { + var line = linesAndNewlines[i]; + + if (i % 2 && !this.options.newlineIsToken) { + retLines[retLines.length - 1] += line; + } else { + if (this.options.ignoreWhitespace) { + line = line.trim(); + } + retLines.push(line); + } + } + + return retLines; +}; + +function diffLines(oldStr, newStr, callback) { + return lineDiff.diff(oldStr, newStr, callback); +} +function diffTrimmedLines(oldStr, newStr, callback) { + var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true }); + return lineDiff.diff(oldStr, newStr, options); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL2xpbmUuanMiXSwibmFtZXMiOlsiZGlmZkxpbmVzIiwiZGlmZlRyaW1tZWRMaW5lcyIsImxpbmVEaWZmIiwidG9rZW5pemUiLCJ2YWx1ZSIsInJldExpbmVzIiwibGluZXNBbmROZXdsaW5lcyIsInNwbGl0IiwibGVuZ3RoIiwicG9wIiwiaSIsImxpbmUiLCJvcHRpb25zIiwibmV3bGluZUlzVG9rZW4iLCJpZ25vcmVXaGl0ZXNwYWNlIiwidHJpbSIsInB1c2giLCJvbGRTdHIiLCJuZXdTdHIiLCJjYWxsYmFjayIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBOEJnQkEsUyxHQUFBQSxTO3lEQUNBQyxnQixHQUFBQSxnQjs7QUEvQmhCOzs7O3VCQUNBOzs7O3VCQUVPLElBQU1DLCtFQUFXLHdFQUFqQjtBQUNQQSxTQUFTQyxRQUFULEdBQW9CLFVBQVNDLEtBQVQsRUFBZ0I7QUFDbEMsTUFBSUMsV0FBVyxFQUFmO0FBQUEsTUFDSUMsbUJBQW1CRixNQUFNRyxLQUFOLENBQVksV0FBWixDQUR2Qjs7QUFHQTtBQUNBLE1BQUksQ0FBQ0QsaUJBQWlCQSxpQkFBaUJFLE1BQWpCLEdBQTBCLENBQTNDLENBQUwsRUFBb0Q7QUFDbERGLHFCQUFpQkcsR0FBakI7QUFDRDs7QUFFRDtBQUNBLE9BQUssSUFBSUMsSUFBSSxDQUFiLEVBQWdCQSxJQUFJSixpQkFBaUJFLE1BQXJDLEVBQTZDRSxHQUE3QyxFQUFrRDtBQUNoRCxRQUFJQyxPQUFPTCxpQkFBaUJJLENBQWpCLENBQVg7O0FBRUEsUUFBSUEsSUFBSSxDQUFKLElBQVMsQ0FBQyxLQUFLRSxPQUFMLENBQWFDLGNBQTNCLEVBQTJDO0FBQ3pDUixlQUFTQSxTQUFTRyxNQUFULEdBQWtCLENBQTNCLEtBQWlDRyxJQUFqQztBQUNELEtBRkQsTUFFTztBQUNMLFVBQUksS0FBS0MsT0FBTCxDQUFhRSxnQkFBakIsRUFBbUM7QUFDakNILGVBQU9BLEtBQUtJLElBQUwsRUFBUDtBQUNEO0FBQ0RWLGVBQVNXLElBQVQsQ0FBY0wsSUFBZDtBQUNEO0FBQ0Y7O0FBRUQsU0FBT04sUUFBUDtBQUNELENBeEJEOztBQTBCTyxTQUFTTCxTQUFULENBQW1CaUIsTUFBbkIsRUFBMkJDLE1BQTNCLEVBQW1DQyxRQUFuQyxFQUE2QztBQUFFLFNBQU9qQixTQUFTa0IsSUFBVCxDQUFjSCxNQUFkLEVBQXNCQyxNQUF0QixFQUE4QkMsUUFBOUIsQ0FBUDtBQUFpRDtBQUNoRyxTQUFTbEIsZ0JBQVQsQ0FBMEJnQixNQUExQixFQUFrQ0MsTUFBbEMsRUFBMENDLFFBQTFDLEVBQW9EO0FBQ3pELE1BQUlQLFVBQVUsOEVBQWdCTyxRQUFoQixFQUEwQixFQUFDTCxrQkFBa0IsSUFBbkIsRUFBMUIsQ0FBZDtBQUNBLFNBQU9aLFNBQVNrQixJQUFULENBQWNILE1BQWQsRUFBc0JDLE1BQXRCLEVBQThCTixPQUE5QixDQUFQO0FBQ0QiLCJmaWxlIjoibGluZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5pbXBvcnQge2dlbmVyYXRlT3B0aW9uc30gZnJvbSAnLi4vdXRpbC9wYXJhbXMnO1xuXG5leHBvcnQgY29uc3QgbGluZURpZmYgPSBuZXcgRGlmZigpO1xubGluZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICBsZXQgcmV0TGluZXMgPSBbXSxcbiAgICAgIGxpbmVzQW5kTmV3bGluZXMgPSB2YWx1ZS5zcGxpdCgvKFxcbnxcXHJcXG4pLyk7XG5cbiAgLy8gSWdub3JlIHRoZSBmaW5hbCBlbXB0eSB0b2tlbiB0aGF0IG9jY3VycyBpZiB0aGUgc3RyaW5nIGVuZHMgd2l0aCBhIG5ldyBsaW5lXG4gIGlmICghbGluZXNBbmROZXdsaW5lc1tsaW5lc0FuZE5ld2xpbmVzLmxlbmd0aCAtIDFdKSB7XG4gICAgbGluZXNBbmROZXdsaW5lcy5wb3AoKTtcbiAgfVxuXG4gIC8vIE1lcmdlIHRoZSBjb250ZW50IGFuZCBsaW5lIHNlcGFyYXRvcnMgaW50byBzaW5nbGUgdG9rZW5zXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbGluZXNBbmROZXdsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBsaW5lID0gbGluZXNBbmROZXdsaW5lc1tpXTtcblxuICAgIGlmIChpICUgMiAmJiAhdGhpcy5vcHRpb25zLm5ld2xpbmVJc1Rva2VuKSB7XG4gICAgICByZXRMaW5lc1tyZXRMaW5lcy5sZW5ndGggLSAxXSArPSBsaW5lO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLmlnbm9yZVdoaXRlc3BhY2UpIHtcbiAgICAgICAgbGluZSA9IGxpbmUudHJpbSgpO1xuICAgICAgfVxuICAgICAgcmV0TGluZXMucHVzaChsaW5lKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmV0TGluZXM7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjaykgeyByZXR1cm4gbGluZURpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spOyB9XG5leHBvcnQgZnVuY3Rpb24gZGlmZlRyaW1tZWRMaW5lcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHtcbiAgbGV0IG9wdGlvbnMgPSBnZW5lcmF0ZU9wdGlvbnMoY2FsbGJhY2ssIHtpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlfSk7XG4gIHJldHVybiBsaW5lRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbn1cbiJdfQ== diff --git a/WebServer/node_modules/diff/lib/diff/sentence.js b/WebServer/node_modules/diff/lib/diff/sentence.js new file mode 100644 index 000000000..c1dcb204c --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/sentence.js @@ -0,0 +1,21 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.sentenceDiff = undefined; +exports. /*istanbul ignore end*/diffSentences = diffSentences; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/var sentenceDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/sentenceDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); +sentenceDiff.tokenize = function (value) { + return value.split(/(\S.+?[.!?])(?=\s+|$)/); +}; + +function diffSentences(oldStr, newStr, callback) { + return sentenceDiff.diff(oldStr, newStr, callback); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL3NlbnRlbmNlLmpzIl0sIm5hbWVzIjpbImRpZmZTZW50ZW5jZXMiLCJzZW50ZW5jZURpZmYiLCJ0b2tlbml6ZSIsInZhbHVlIiwic3BsaXQiLCJvbGRTdHIiLCJuZXdTdHIiLCJjYWxsYmFjayIsImRpZmYiXSwibWFwcGluZ3MiOiI7Ozs7Z0NBUWdCQSxhLEdBQUFBLGE7O0FBUmhCOzs7Ozs7dUJBR08sSUFBTUMsdUZBQWUsd0VBQXJCO0FBQ1BBLGFBQWFDLFFBQWIsR0FBd0IsVUFBU0MsS0FBVCxFQUFnQjtBQUN0QyxTQUFPQSxNQUFNQyxLQUFOLENBQVksdUJBQVosQ0FBUDtBQUNELENBRkQ7O0FBSU8sU0FBU0osYUFBVCxDQUF1QkssTUFBdkIsRUFBK0JDLE1BQS9CLEVBQXVDQyxRQUF2QyxFQUFpRDtBQUFFLFNBQU9OLGFBQWFPLElBQWIsQ0FBa0JILE1BQWxCLEVBQTBCQyxNQUExQixFQUFrQ0MsUUFBbEMsQ0FBUDtBQUFxRCIsImZpbGUiOiJzZW50ZW5jZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBEaWZmIGZyb20gJy4vYmFzZSc7XG5cblxuZXhwb3J0IGNvbnN0IHNlbnRlbmNlRGlmZiA9IG5ldyBEaWZmKCk7XG5zZW50ZW5jZURpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUuc3BsaXQoLyhcXFMuKz9bLiE/XSkoPz1cXHMrfCQpLyk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZGlmZlNlbnRlbmNlcyhvbGRTdHIsIG5ld1N0ciwgY2FsbGJhY2spIHsgcmV0dXJuIHNlbnRlbmNlRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBjYWxsYmFjayk7IH1cbiJdfQ== diff --git a/WebServer/node_modules/diff/lib/diff/word.js b/WebServer/node_modules/diff/lib/diff/word.js new file mode 100644 index 000000000..4af1b05d4 --- /dev/null +++ b/WebServer/node_modules/diff/lib/diff/word.js @@ -0,0 +1,70 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.wordDiff = undefined; +exports. /*istanbul ignore end*/diffWords = diffWords; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = diffWordsWithSpace; + +var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +/*istanbul ignore end*/var /*istanbul ignore start*/_params = require('../util/params') /*istanbul ignore end*/; + +/*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/ // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode +// +// Ranges and exceptions: +// Latin-1 Supplement, 0080–00FF +// - U+00D7 × Multiplication sign +// - U+00F7 ÷ Division sign +// Latin Extended-A, 0100–017F +// Latin Extended-B, 0180–024F +// IPA Extensions, 0250–02AF +// Spacing Modifier Letters, 02B0–02FF +// - U+02C7 ˇ ˇ Caron +// - U+02D8 ˘ ˘ Breve +// - U+02D9 Ë™ ˙ Dot Above +// - U+02DA Ëš ˚ Ring Above +// - U+02DB Ë› ˛ Ogonek +// - U+02DC Ëœ ˜ Small Tilde +// - U+02DD Ë� ˝ Double Acute Accent +// Latin Extended Additional, 1E00–1EFF +var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; + +var reWhitespace = /\S/; + +var wordDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/wordDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); +wordDiff.equals = function (left, right) { + if (this.options.ignoreCase) { + left = left.toLowerCase(); + right = right.toLowerCase(); + } + return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); +}; +wordDiff.tokenize = function (value) { + var tokens = value.split(/(\s+|\b)/); + + // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. + for (var i = 0; i < tokens.length - 1; i++) { + // If we have an empty string in the next field and we have only word chars before and after, merge + if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { + tokens[i] += tokens[i + 2]; + tokens.splice(i + 1, 2); + i--; + } + } + + return tokens; +}; + +function diffWords(oldStr, newStr, options) { + options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(options, { ignoreWhitespace: true }); + return wordDiff.diff(oldStr, newStr, options); +} + +function diffWordsWithSpace(oldStr, newStr, options) { + return wordDiff.diff(oldStr, newStr, options); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kaWZmL3dvcmQuanMiXSwibmFtZXMiOlsiZGlmZldvcmRzIiwiZGlmZldvcmRzV2l0aFNwYWNlIiwiZXh0ZW5kZWRXb3JkQ2hhcnMiLCJyZVdoaXRlc3BhY2UiLCJ3b3JkRGlmZiIsImVxdWFscyIsImxlZnQiLCJyaWdodCIsIm9wdGlvbnMiLCJpZ25vcmVDYXNlIiwidG9Mb3dlckNhc2UiLCJpZ25vcmVXaGl0ZXNwYWNlIiwidGVzdCIsInRva2VuaXplIiwidmFsdWUiLCJ0b2tlbnMiLCJzcGxpdCIsImkiLCJsZW5ndGgiLCJzcGxpY2UiLCJvbGRTdHIiLCJuZXdTdHIiLCJkaWZmIl0sIm1hcHBpbmdzIjoiOzs7O2dDQW1EZ0JBLFMsR0FBQUEsUzt5REFLQUMsa0IsR0FBQUEsa0I7O0FBeERoQjs7Ozt1QkFDQTs7Ozt3QkFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFNQyxvQkFBb0IsK0RBQTFCOztBQUVBLElBQU1DLGVBQWUsSUFBckI7O0FBRU8sSUFBTUMsK0VBQVcsd0VBQWpCO0FBQ1BBLFNBQVNDLE1BQVQsR0FBa0IsVUFBU0MsSUFBVCxFQUFlQyxLQUFmLEVBQXNCO0FBQ3RDLE1BQUksS0FBS0MsT0FBTCxDQUFhQyxVQUFqQixFQUE2QjtBQUMzQkgsV0FBT0EsS0FBS0ksV0FBTCxFQUFQO0FBQ0FILFlBQVFBLE1BQU1HLFdBQU4sRUFBUjtBQUNEO0FBQ0QsU0FBT0osU0FBU0MsS0FBVCxJQUFtQixLQUFLQyxPQUFMLENBQWFHLGdCQUFiLElBQWlDLENBQUNSLGFBQWFTLElBQWIsQ0FBa0JOLElBQWxCLENBQWxDLElBQTZELENBQUNILGFBQWFTLElBQWIsQ0FBa0JMLEtBQWxCLENBQXhGO0FBQ0QsQ0FORDtBQU9BSCxTQUFTUyxRQUFULEdBQW9CLFVBQVNDLEtBQVQsRUFBZ0I7QUFDbEMsTUFBSUMsU0FBU0QsTUFBTUUsS0FBTixDQUFZLFVBQVosQ0FBYjs7QUFFQTtBQUNBLE9BQUssSUFBSUMsSUFBSSxDQUFiLEVBQWdCQSxJQUFJRixPQUFPRyxNQUFQLEdBQWdCLENBQXBDLEVBQXVDRCxHQUF2QyxFQUE0QztBQUMxQztBQUNBLFFBQUksQ0FBQ0YsT0FBT0UsSUFBSSxDQUFYLENBQUQsSUFBa0JGLE9BQU9FLElBQUksQ0FBWCxDQUFsQixJQUNLZixrQkFBa0JVLElBQWxCLENBQXVCRyxPQUFPRSxDQUFQLENBQXZCLENBREwsSUFFS2Ysa0JBQWtCVSxJQUFsQixDQUF1QkcsT0FBT0UsSUFBSSxDQUFYLENBQXZCLENBRlQsRUFFZ0Q7QUFDOUNGLGFBQU9FLENBQVAsS0FBYUYsT0FBT0UsSUFBSSxDQUFYLENBQWI7QUFDQUYsYUFBT0ksTUFBUCxDQUFjRixJQUFJLENBQWxCLEVBQXFCLENBQXJCO0FBQ0FBO0FBQ0Q7QUFDRjs7QUFFRCxTQUFPRixNQUFQO0FBQ0QsQ0FoQkQ7O0FBa0JPLFNBQVNmLFNBQVQsQ0FBbUJvQixNQUFuQixFQUEyQkMsTUFBM0IsRUFBbUNiLE9BQW5DLEVBQTRDO0FBQ2pEQSxZQUFVLDhFQUFnQkEsT0FBaEIsRUFBeUIsRUFBQ0csa0JBQWtCLElBQW5CLEVBQXpCLENBQVY7QUFDQSxTQUFPUCxTQUFTa0IsSUFBVCxDQUFjRixNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmIsT0FBOUIsQ0FBUDtBQUNEOztBQUVNLFNBQVNQLGtCQUFULENBQTRCbUIsTUFBNUIsRUFBb0NDLE1BQXBDLEVBQTRDYixPQUE1QyxFQUFxRDtBQUMxRCxTQUFPSixTQUFTa0IsSUFBVCxDQUFjRixNQUFkLEVBQXNCQyxNQUF0QixFQUE4QmIsT0FBOUIsQ0FBUDtBQUNEIiwiZmlsZSI6IndvcmQuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgRGlmZiBmcm9tICcuL2Jhc2UnO1xuaW1wb3J0IHtnZW5lcmF0ZU9wdGlvbnN9IGZyb20gJy4uL3V0aWwvcGFyYW1zJztcblxuLy8gQmFzZWQgb24gaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGF0aW5fc2NyaXB0X2luX1VuaWNvZGVcbi8vXG4vLyBSYW5nZXMgYW5kIGV4Y2VwdGlvbnM6XG4vLyBMYXRpbi0xIFN1cHBsZW1lbnQsIDAwODDigJMwMEZGXG4vLyAgLSBVKzAwRDcgIMOXIE11bHRpcGxpY2F0aW9uIHNpZ25cbi8vICAtIFUrMDBGNyAgw7cgRGl2aXNpb24gc2lnblxuLy8gTGF0aW4gRXh0ZW5kZWQtQSwgMDEwMOKAkzAxN0Zcbi8vIExhdGluIEV4dGVuZGVkLUIsIDAxODDigJMwMjRGXG4vLyBJUEEgRXh0ZW5zaW9ucywgMDI1MOKAkzAyQUZcbi8vIFNwYWNpbmcgTW9kaWZpZXIgTGV0dGVycywgMDJCMOKAkzAyRkZcbi8vICAtIFUrMDJDNyAgy4cgJiM3MTE7ICBDYXJvblxuLy8gIC0gVSswMkQ4ICDLmCAmIzcyODsgIEJyZXZlXG4vLyAgLSBVKzAyRDkgIMuZICYjNzI5OyAgRG90IEFib3ZlXG4vLyAgLSBVKzAyREEgIMuaICYjNzMwOyAgUmluZyBBYm92ZVxuLy8gIC0gVSswMkRCICDLmyAmIzczMTsgIE9nb25la1xuLy8gIC0gVSswMkRDICDLnCAmIzczMjsgIFNtYWxsIFRpbGRlXG4vLyAgLSBVKzAyREQgIMudICYjNzMzOyAgRG91YmxlIEFjdXRlIEFjY2VudFxuLy8gTGF0aW4gRXh0ZW5kZWQgQWRkaXRpb25hbCwgMUUwMOKAkzFFRkZcbmNvbnN0IGV4dGVuZGVkV29yZENoYXJzID0gL15bYS16QS1aXFx1e0MwfS1cXHV7RkZ9XFx1e0Q4fS1cXHV7RjZ9XFx1e0Y4fS1cXHV7MkM2fVxcdXsyQzh9LVxcdXsyRDd9XFx1ezJERX0tXFx1ezJGRn1cXHV7MUUwMH0tXFx1ezFFRkZ9XSskL3U7XG5cbmNvbnN0IHJlV2hpdGVzcGFjZSA9IC9cXFMvO1xuXG5leHBvcnQgY29uc3Qgd29yZERpZmYgPSBuZXcgRGlmZigpO1xud29yZERpZmYuZXF1YWxzID0gZnVuY3Rpb24obGVmdCwgcmlnaHQpIHtcbiAgaWYgKHRoaXMub3B0aW9ucy5pZ25vcmVDYXNlKSB7XG4gICAgbGVmdCA9IGxlZnQudG9Mb3dlckNhc2UoKTtcbiAgICByaWdodCA9IHJpZ2h0LnRvTG93ZXJDYXNlKCk7XG4gIH1cbiAgcmV0dXJuIGxlZnQgPT09IHJpZ2h0IHx8ICh0aGlzLm9wdGlvbnMuaWdub3JlV2hpdGVzcGFjZSAmJiAhcmVXaGl0ZXNwYWNlLnRlc3QobGVmdCkgJiYgIXJlV2hpdGVzcGFjZS50ZXN0KHJpZ2h0KSk7XG59O1xud29yZERpZmYudG9rZW5pemUgPSBmdW5jdGlvbih2YWx1ZSkge1xuICBsZXQgdG9rZW5zID0gdmFsdWUuc3BsaXQoLyhcXHMrfFxcYikvKTtcblxuICAvLyBKb2luIHRoZSBib3VuZGFyeSBzcGxpdHMgdGhhdCB3ZSBkbyBub3QgY29uc2lkZXIgdG8gYmUgYm91bmRhcmllcy4gVGhpcyBpcyBwcmltYXJpbHkgdGhlIGV4dGVuZGVkIExhdGluIGNoYXJhY3RlciBzZXQuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdG9rZW5zLmxlbmd0aCAtIDE7IGkrKykge1xuICAgIC8vIElmIHdlIGhhdmUgYW4gZW1wdHkgc3RyaW5nIGluIHRoZSBuZXh0IGZpZWxkIGFuZCB3ZSBoYXZlIG9ubHkgd29yZCBjaGFycyBiZWZvcmUgYW5kIGFmdGVyLCBtZXJnZVxuICAgIGlmICghdG9rZW5zW2kgKyAxXSAmJiB0b2tlbnNbaSArIDJdXG4gICAgICAgICAgJiYgZXh0ZW5kZWRXb3JkQ2hhcnMudGVzdCh0b2tlbnNbaV0pXG4gICAgICAgICAgJiYgZXh0ZW5kZWRXb3JkQ2hhcnMudGVzdCh0b2tlbnNbaSArIDJdKSkge1xuICAgICAgdG9rZW5zW2ldICs9IHRva2Vuc1tpICsgMl07XG4gICAgICB0b2tlbnMuc3BsaWNlKGkgKyAxLCAyKTtcbiAgICAgIGktLTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gdG9rZW5zO1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIGRpZmZXb3JkcyhvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykge1xuICBvcHRpb25zID0gZ2VuZXJhdGVPcHRpb25zKG9wdGlvbnMsIHtpZ25vcmVXaGl0ZXNwYWNlOiB0cnVlfSk7XG4gIHJldHVybiB3b3JkRGlmZi5kaWZmKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRpZmZXb3Jkc1dpdGhTcGFjZShvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucykge1xuICByZXR1cm4gd29yZERpZmYuZGlmZihvbGRTdHIsIG5ld1N0ciwgb3B0aW9ucyk7XG59XG4iXX0= diff --git a/WebServer/node_modules/diff/lib/index.js b/WebServer/node_modules/diff/lib/index.js new file mode 100644 index 000000000..8608cafc3 --- /dev/null +++ b/WebServer/node_modules/diff/lib/index.js @@ -0,0 +1,74 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports.canonicalize = exports.convertChangesToXML = exports.convertChangesToDMP = exports.merge = exports.parsePatch = exports.applyPatches = exports.applyPatch = exports.createPatch = exports.createTwoFilesPatch = exports.structuredPatch = exports.diffArrays = exports.diffJson = exports.diffCss = exports.diffSentences = exports.diffTrimmedLines = exports.diffLines = exports.diffWordsWithSpace = exports.diffWords = exports.diffChars = exports.Diff = undefined; + +/*istanbul ignore end*/var /*istanbul ignore start*/_base = require('./diff/base') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + +/*istanbul ignore end*/var /*istanbul ignore start*/_character = require('./diff/character') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_word = require('./diff/word') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_line = require('./diff/line') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_sentence = require('./diff/sentence') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_css = require('./diff/css') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_json = require('./diff/json') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_array = require('./diff/array') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_apply = require('./patch/apply') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_parse = require('./patch/parse') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_merge = require('./patch/merge') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_create = require('./patch/create') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_dmp = require('./convert/dmp') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_xml = require('./convert/xml') /*istanbul ignore end*/; + +/*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/* See LICENSE file for terms of use */ + +/* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ +exports. /*istanbul ignore end*/Diff = _base2['default']; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffChars = _character.diffChars; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWords = _word.diffWords; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = _word.diffWordsWithSpace; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffLines = _line.diffLines; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = _line.diffTrimmedLines; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffSentences = _sentence.diffSentences; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffCss = _css.diffCss; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffJson = _json.diffJson; +/*istanbul ignore start*/exports. /*istanbul ignore end*/diffArrays = _array.diffArrays; +/*istanbul ignore start*/exports. /*istanbul ignore end*/structuredPatch = _create.structuredPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = _create.createTwoFilesPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = _create.createPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatch = _apply.applyPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = _apply.applyPatches; +/*istanbul ignore start*/exports. /*istanbul ignore end*/parsePatch = _parse.parsePatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/merge = _merge.merge; +/*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToDMP = _dmp.convertChangesToDMP; +/*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToXML = _xml.convertChangesToXML; +/*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = _json.canonicalize; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6WyJEaWZmIiwiZGlmZkNoYXJzIiwiZGlmZldvcmRzIiwiZGlmZldvcmRzV2l0aFNwYWNlIiwiZGlmZkxpbmVzIiwiZGlmZlRyaW1tZWRMaW5lcyIsImRpZmZTZW50ZW5jZXMiLCJkaWZmQ3NzIiwiZGlmZkpzb24iLCJkaWZmQXJyYXlzIiwic3RydWN0dXJlZFBhdGNoIiwiY3JlYXRlVHdvRmlsZXNQYXRjaCIsImNyZWF0ZVBhdGNoIiwiYXBwbHlQYXRjaCIsImFwcGx5UGF0Y2hlcyIsInBhcnNlUGF0Y2giLCJtZXJnZSIsImNvbnZlcnRDaGFuZ2VzVG9ETVAiLCJjb252ZXJ0Q2hhbmdlc1RvWE1MIiwiY2Fub25pY2FsaXplIl0sIm1hcHBpbmdzIjoiOzs7Ozt1QkFnQkE7Ozs7dUJBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBRUE7O0FBQ0E7O0FBRUE7O0FBRUE7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBRUE7O0FBQ0E7Ozs7QUFqQ0E7O0FBRUE7Ozs7Ozs7Ozs7Ozs7O2dDQWtDRUEsSTt5REFFQUMsUzt5REFDQUMsUzt5REFDQUMsa0I7eURBQ0FDLFM7eURBQ0FDLGdCO3lEQUNBQyxhO3lEQUVBQyxPO3lEQUNBQyxRO3lEQUVBQyxVO3lEQUVBQyxlO3lEQUNBQyxtQjt5REFDQUMsVzt5REFDQUMsVTt5REFDQUMsWTt5REFDQUMsVTt5REFDQUMsSzt5REFDQUMsbUI7eURBQ0FDLG1CO3lEQUNBQyxZIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLyogU2VlIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMgb2YgdXNlICovXG5cbi8qXG4gKiBUZXh0IGRpZmYgaW1wbGVtZW50YXRpb24uXG4gKlxuICogVGhpcyBsaWJyYXJ5IHN1cHBvcnRzIHRoZSBmb2xsb3dpbmcgQVBJUzpcbiAqIEpzRGlmZi5kaWZmQ2hhcnM6IENoYXJhY3RlciBieSBjaGFyYWN0ZXIgZGlmZlxuICogSnNEaWZmLmRpZmZXb3JkczogV29yZCAoYXMgZGVmaW5lZCBieSBcXGIgcmVnZXgpIGRpZmYgd2hpY2ggaWdub3JlcyB3aGl0ZXNwYWNlXG4gKiBKc0RpZmYuZGlmZkxpbmVzOiBMaW5lIGJhc2VkIGRpZmZcbiAqXG4gKiBKc0RpZmYuZGlmZkNzczogRGlmZiB0YXJnZXRlZCBhdCBDU1MgY29udGVudFxuICpcbiAqIFRoZXNlIG1ldGhvZHMgYXJlIGJhc2VkIG9uIHRoZSBpbXBsZW1lbnRhdGlvbiBwcm9wb3NlZCBpblxuICogXCJBbiBPKE5EKSBEaWZmZXJlbmNlIEFsZ29yaXRobSBhbmQgaXRzIFZhcmlhdGlvbnNcIiAoTXllcnMsIDE5ODYpLlxuICogaHR0cDovL2NpdGVzZWVyeC5pc3QucHN1LmVkdS92aWV3ZG9jL3N1bW1hcnk/ZG9pPTEwLjEuMS40LjY5MjdcbiAqL1xuaW1wb3J0IERpZmYgZnJvbSAnLi9kaWZmL2Jhc2UnO1xuaW1wb3J0IHtkaWZmQ2hhcnN9IGZyb20gJy4vZGlmZi9jaGFyYWN0ZXInO1xuaW1wb3J0IHtkaWZmV29yZHMsIGRpZmZXb3Jkc1dpdGhTcGFjZX0gZnJvbSAnLi9kaWZmL3dvcmQnO1xuaW1wb3J0IHtkaWZmTGluZXMsIGRpZmZUcmltbWVkTGluZXN9IGZyb20gJy4vZGlmZi9saW5lJztcbmltcG9ydCB7ZGlmZlNlbnRlbmNlc30gZnJvbSAnLi9kaWZmL3NlbnRlbmNlJztcblxuaW1wb3J0IHtkaWZmQ3NzfSBmcm9tICcuL2RpZmYvY3NzJztcbmltcG9ydCB7ZGlmZkpzb24sIGNhbm9uaWNhbGl6ZX0gZnJvbSAnLi9kaWZmL2pzb24nO1xuXG5pbXBvcnQge2RpZmZBcnJheXN9IGZyb20gJy4vZGlmZi9hcnJheSc7XG5cbmltcG9ydCB7YXBwbHlQYXRjaCwgYXBwbHlQYXRjaGVzfSBmcm9tICcuL3BhdGNoL2FwcGx5JztcbmltcG9ydCB7cGFyc2VQYXRjaH0gZnJvbSAnLi9wYXRjaC9wYXJzZSc7XG5pbXBvcnQge21lcmdlfSBmcm9tICcuL3BhdGNoL21lcmdlJztcbmltcG9ydCB7c3RydWN0dXJlZFBhdGNoLCBjcmVhdGVUd29GaWxlc1BhdGNoLCBjcmVhdGVQYXRjaH0gZnJvbSAnLi9wYXRjaC9jcmVhdGUnO1xuXG5pbXBvcnQge2NvbnZlcnRDaGFuZ2VzVG9ETVB9IGZyb20gJy4vY29udmVydC9kbXAnO1xuaW1wb3J0IHtjb252ZXJ0Q2hhbmdlc1RvWE1MfSBmcm9tICcuL2NvbnZlcnQveG1sJztcblxuZXhwb3J0IHtcbiAgRGlmZixcblxuICBkaWZmQ2hhcnMsXG4gIGRpZmZXb3JkcyxcbiAgZGlmZldvcmRzV2l0aFNwYWNlLFxuICBkaWZmTGluZXMsXG4gIGRpZmZUcmltbWVkTGluZXMsXG4gIGRpZmZTZW50ZW5jZXMsXG5cbiAgZGlmZkNzcyxcbiAgZGlmZkpzb24sXG5cbiAgZGlmZkFycmF5cyxcblxuICBzdHJ1Y3R1cmVkUGF0Y2gsXG4gIGNyZWF0ZVR3b0ZpbGVzUGF0Y2gsXG4gIGNyZWF0ZVBhdGNoLFxuICBhcHBseVBhdGNoLFxuICBhcHBseVBhdGNoZXMsXG4gIHBhcnNlUGF0Y2gsXG4gIG1lcmdlLFxuICBjb252ZXJ0Q2hhbmdlc1RvRE1QLFxuICBjb252ZXJ0Q2hhbmdlc1RvWE1MLFxuICBjYW5vbmljYWxpemVcbn07XG4iXX0= diff --git a/WebServer/node_modules/diff/lib/patch/apply.js b/WebServer/node_modules/diff/lib/patch/apply.js new file mode 100644 index 000000000..fa8301534 --- /dev/null +++ b/WebServer/node_modules/diff/lib/patch/apply.js @@ -0,0 +1,180 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/applyPatch = applyPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = applyPatches; + +var /*istanbul ignore start*/_parse = require('./parse') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_distanceIterator = require('../util/distance-iterator') /*istanbul ignore end*/; + +/*istanbul ignore start*/var _distanceIterator2 = _interopRequireDefault(_distanceIterator); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +/*istanbul ignore end*/function applyPatch(source, uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } + + if (Array.isArray(uniDiff)) { + if (uniDiff.length > 1) { + throw new Error('applyPatch only works with a single input.'); + } + + uniDiff = uniDiff[0]; + } + + // Apply the diff to the input + var lines = source.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) /*istanbul ignore start*/{ + return (/*istanbul ignore end*/line === patchContent + ); + }, + errorCount = 0, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0, + offset = 0, + removeEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + addEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + /** + * Checks if the hunk exactly fits on the provided location + */ + function hunkFits(hunk, toPos) { + for (var j = 0; j < hunk.lines.length; j++) { + var line = hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line; + + if (operation === ' ' || operation === '-') { + // Context sanity check + if (!compareLine(toPos + 1, lines[toPos], operation, content)) { + errorCount++; + + if (errorCount > fuzzFactor) { + return false; + } + } + toPos++; + } + } + + return true; + } + + // Search best fit offsets for each hunk based on the previous ones + for (var i = 0; i < hunks.length; i++) { + var hunk = hunks[i], + maxLine = lines.length - hunk.oldLines, + localOffset = 0, + toPos = offset + hunk.oldStart - 1; + + var iterator = /*istanbul ignore start*/(0, _distanceIterator2['default']) /*istanbul ignore end*/(toPos, minLine, maxLine); + + for (; localOffset !== undefined; localOffset = iterator()) { + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + break; + } + } + + if (localOffset === undefined) { + return false; + } + + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; + } + + // Apply patch hunks + var diffOffset = 0; + for (var _i = 0; _i < hunks.length; _i++) { + var _hunk = hunks[_i], + _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; + diffOffset += _hunk.newLines - _hunk.oldLines; + + if (_toPos < 0) { + // Creating a new file + _toPos = 0; + } + + for (var j = 0; j < _hunk.lines.length; j++) { + var line = _hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line, + delimiter = _hunk.linedelimiters[j]; + + if (operation === ' ') { + _toPos++; + } else if (operation === '-') { + lines.splice(_toPos, 1); + delimiters.splice(_toPos, 1); + /* istanbul ignore else */ + } else if (operation === '+') { + lines.splice(_toPos, 0, content); + delimiters.splice(_toPos, 0, delimiter); + _toPos++; + } else if (operation === '\\') { + var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; + if (previousOperation === '+') { + removeEOFNL = true; + } else if (previousOperation === '-') { + addEOFNL = true; + } + } + } + } + + // Handle EOFNL insertion/removal + if (removeEOFNL) { + while (!lines[lines.length - 1]) { + lines.pop(); + delimiters.pop(); + } + } else if (addEOFNL) { + lines.push(''); + delimiters.push('\n'); + } + for (var _k = 0; _k < lines.length - 1; _k++) { + lines[_k] = lines[_k] + delimiters[_k]; + } + return lines.join(''); +} + +// Wrapper that supports multiple file patches via callbacks. +function applyPatches(uniDiff, options) { + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } + + var currentIndex = 0; + function processIndex() { + var index = uniDiff[currentIndex++]; + if (!index) { + return options.complete(); + } + + options.loadFile(index, function (err, data) { + if (err) { + return options.complete(err); + } + + var updatedContent = applyPatch(data, index, options); + options.patched(index, updatedContent, function (err) { + if (err) { + return options.complete(err); + } + + processIndex(); + }); + }); + } + processIndex(); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9hcHBseS5qcyJdLCJuYW1lcyI6WyJhcHBseVBhdGNoIiwiYXBwbHlQYXRjaGVzIiwic291cmNlIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJBcnJheSIsImlzQXJyYXkiLCJsZW5ndGgiLCJFcnJvciIsImxpbmVzIiwic3BsaXQiLCJkZWxpbWl0ZXJzIiwibWF0Y2giLCJodW5rcyIsImNvbXBhcmVMaW5lIiwibGluZU51bWJlciIsImxpbmUiLCJvcGVyYXRpb24iLCJwYXRjaENvbnRlbnQiLCJlcnJvckNvdW50IiwiZnV6ekZhY3RvciIsIm1pbkxpbmUiLCJvZmZzZXQiLCJyZW1vdmVFT0ZOTCIsImFkZEVPRk5MIiwiaHVua0ZpdHMiLCJodW5rIiwidG9Qb3MiLCJqIiwiY29udGVudCIsInN1YnN0ciIsImkiLCJtYXhMaW5lIiwib2xkTGluZXMiLCJsb2NhbE9mZnNldCIsIm9sZFN0YXJ0IiwiaXRlcmF0b3IiLCJ1bmRlZmluZWQiLCJkaWZmT2Zmc2V0IiwibmV3TGluZXMiLCJkZWxpbWl0ZXIiLCJsaW5lZGVsaW1pdGVycyIsInNwbGljZSIsInByZXZpb3VzT3BlcmF0aW9uIiwicG9wIiwicHVzaCIsIl9rIiwiam9pbiIsImN1cnJlbnRJbmRleCIsInByb2Nlc3NJbmRleCIsImluZGV4IiwiY29tcGxldGUiLCJsb2FkRmlsZSIsImVyciIsImRhdGEiLCJ1cGRhdGVkQ29udGVudCIsInBhdGNoZWQiXSwibWFwcGluZ3MiOiI7OztnQ0FHZ0JBLFUsR0FBQUEsVTt5REFvSUFDLFksR0FBQUEsWTs7QUF2SWhCOztBQUNBOzs7Ozs7dUJBRU8sU0FBU0QsVUFBVCxDQUFvQkUsTUFBcEIsRUFBNEJDLE9BQTVCLEVBQW1EO0FBQUEsc0RBQWRDLE9BQWMsdUVBQUosRUFBSTs7QUFDeEQsTUFBSSxPQUFPRCxPQUFQLEtBQW1CLFFBQXZCLEVBQWlDO0FBQy9CQSxjQUFVLHdFQUFXQSxPQUFYLENBQVY7QUFDRDs7QUFFRCxNQUFJRSxNQUFNQyxPQUFOLENBQWNILE9BQWQsQ0FBSixFQUE0QjtBQUMxQixRQUFJQSxRQUFRSSxNQUFSLEdBQWlCLENBQXJCLEVBQXdCO0FBQ3RCLFlBQU0sSUFBSUMsS0FBSixDQUFVLDRDQUFWLENBQU47QUFDRDs7QUFFREwsY0FBVUEsUUFBUSxDQUFSLENBQVY7QUFDRDs7QUFFRDtBQUNBLE1BQUlNLFFBQVFQLE9BQU9RLEtBQVAsQ0FBYSxxQkFBYixDQUFaO0FBQUEsTUFDSUMsYUFBYVQsT0FBT1UsS0FBUCxDQUFhLHNCQUFiLEtBQXdDLEVBRHpEO0FBQUEsTUFFSUMsUUFBUVYsUUFBUVUsS0FGcEI7QUFBQSxNQUlJQyxjQUFjVixRQUFRVSxXQUFSLElBQXdCLFVBQUNDLFVBQUQsRUFBYUMsSUFBYixFQUFtQkMsU0FBbkIsRUFBOEJDLFlBQTlCO0FBQUEsbUNBQStDRixTQUFTRTtBQUF4RDtBQUFBLEdBSjFDO0FBQUEsTUFLSUMsYUFBYSxDQUxqQjtBQUFBLE1BTUlDLGFBQWFoQixRQUFRZ0IsVUFBUixJQUFzQixDQU52QztBQUFBLE1BT0lDLFVBQVUsQ0FQZDtBQUFBLE1BUUlDLFNBQVMsQ0FSYjtBQUFBLE1BVUlDLDZDQVZKO0FBQUEsTUFXSUMsMENBWEo7O0FBYUE7OztBQUdBLFdBQVNDLFFBQVQsQ0FBa0JDLElBQWxCLEVBQXdCQyxLQUF4QixFQUErQjtBQUM3QixTQUFLLElBQUlDLElBQUksQ0FBYixFQUFnQkEsSUFBSUYsS0FBS2pCLEtBQUwsQ0FBV0YsTUFBL0IsRUFBdUNxQixHQUF2QyxFQUE0QztBQUMxQyxVQUFJWixPQUFPVSxLQUFLakIsS0FBTCxDQUFXbUIsQ0FBWCxDQUFYO0FBQUEsVUFDSVgsWUFBYUQsS0FBS1QsTUFBTCxHQUFjLENBQWQsR0FBa0JTLEtBQUssQ0FBTCxDQUFsQixHQUE0QixHQUQ3QztBQUFBLFVBRUlhLFVBQVdiLEtBQUtULE1BQUwsR0FBYyxDQUFkLEdBQWtCUyxLQUFLYyxNQUFMLENBQVksQ0FBWixDQUFsQixHQUFtQ2QsSUFGbEQ7O0FBSUEsVUFBSUMsY0FBYyxHQUFkLElBQXFCQSxjQUFjLEdBQXZDLEVBQTRDO0FBQzFDO0FBQ0EsWUFBSSxDQUFDSCxZQUFZYSxRQUFRLENBQXBCLEVBQXVCbEIsTUFBTWtCLEtBQU4sQ0FBdkIsRUFBcUNWLFNBQXJDLEVBQWdEWSxPQUFoRCxDQUFMLEVBQStEO0FBQzdEVjs7QUFFQSxjQUFJQSxhQUFhQyxVQUFqQixFQUE2QjtBQUMzQixtQkFBTyxLQUFQO0FBQ0Q7QUFDRjtBQUNETztBQUNEO0FBQ0Y7O0FBRUQsV0FBTyxJQUFQO0FBQ0Q7O0FBRUQ7QUFDQSxPQUFLLElBQUlJLElBQUksQ0FBYixFQUFnQkEsSUFBSWxCLE1BQU1OLE1BQTFCLEVBQWtDd0IsR0FBbEMsRUFBdUM7QUFDckMsUUFBSUwsT0FBT2IsTUFBTWtCLENBQU4sQ0FBWDtBQUFBLFFBQ0lDLFVBQVV2QixNQUFNRixNQUFOLEdBQWVtQixLQUFLTyxRQURsQztBQUFBLFFBRUlDLGNBQWMsQ0FGbEI7QUFBQSxRQUdJUCxRQUFRTCxTQUFTSSxLQUFLUyxRQUFkLEdBQXlCLENBSHJDOztBQUtBLFFBQUlDLFdBQVcsb0ZBQWlCVCxLQUFqQixFQUF3Qk4sT0FBeEIsRUFBaUNXLE9BQWpDLENBQWY7O0FBRUEsV0FBT0UsZ0JBQWdCRyxTQUF2QixFQUFrQ0gsY0FBY0UsVUFBaEQsRUFBNEQ7QUFDMUQsVUFBSVgsU0FBU0MsSUFBVCxFQUFlQyxRQUFRTyxXQUF2QixDQUFKLEVBQXlDO0FBQ3ZDUixhQUFLSixNQUFMLEdBQWNBLFVBQVVZLFdBQXhCO0FBQ0E7QUFDRDtBQUNGOztBQUVELFFBQUlBLGdCQUFnQkcsU0FBcEIsRUFBK0I7QUFDN0IsYUFBTyxLQUFQO0FBQ0Q7O0FBRUQ7QUFDQTtBQUNBaEIsY0FBVUssS0FBS0osTUFBTCxHQUFjSSxLQUFLUyxRQUFuQixHQUE4QlQsS0FBS08sUUFBN0M7QUFDRDs7QUFFRDtBQUNBLE1BQUlLLGFBQWEsQ0FBakI7QUFDQSxPQUFLLElBQUlQLEtBQUksQ0FBYixFQUFnQkEsS0FBSWxCLE1BQU1OLE1BQTFCLEVBQWtDd0IsSUFBbEMsRUFBdUM7QUFDckMsUUFBSUwsUUFBT2IsTUFBTWtCLEVBQU4sQ0FBWDtBQUFBLFFBQ0lKLFNBQVFELE1BQUtTLFFBQUwsR0FBZ0JULE1BQUtKLE1BQXJCLEdBQThCZ0IsVUFBOUIsR0FBMkMsQ0FEdkQ7QUFFQUEsa0JBQWNaLE1BQUthLFFBQUwsR0FBZ0JiLE1BQUtPLFFBQW5DOztBQUVBLFFBQUlOLFNBQVEsQ0FBWixFQUFlO0FBQUU7QUFDZkEsZUFBUSxDQUFSO0FBQ0Q7O0FBRUQsU0FBSyxJQUFJQyxJQUFJLENBQWIsRUFBZ0JBLElBQUlGLE1BQUtqQixLQUFMLENBQVdGLE1BQS9CLEVBQXVDcUIsR0FBdkMsRUFBNEM7QUFDMUMsVUFBSVosT0FBT1UsTUFBS2pCLEtBQUwsQ0FBV21CLENBQVgsQ0FBWDtBQUFBLFVBQ0lYLFlBQWFELEtBQUtULE1BQUwsR0FBYyxDQUFkLEdBQWtCUyxLQUFLLENBQUwsQ0FBbEIsR0FBNEIsR0FEN0M7QUFBQSxVQUVJYSxVQUFXYixLQUFLVCxNQUFMLEdBQWMsQ0FBZCxHQUFrQlMsS0FBS2MsTUFBTCxDQUFZLENBQVosQ0FBbEIsR0FBbUNkLElBRmxEO0FBQUEsVUFHSXdCLFlBQVlkLE1BQUtlLGNBQUwsQ0FBb0JiLENBQXBCLENBSGhCOztBQUtBLFVBQUlYLGNBQWMsR0FBbEIsRUFBdUI7QUFDckJVO0FBQ0QsT0FGRCxNQUVPLElBQUlWLGNBQWMsR0FBbEIsRUFBdUI7QUFDNUJSLGNBQU1pQyxNQUFOLENBQWFmLE1BQWIsRUFBb0IsQ0FBcEI7QUFDQWhCLG1CQUFXK0IsTUFBWCxDQUFrQmYsTUFBbEIsRUFBeUIsQ0FBekI7QUFDRjtBQUNDLE9BSk0sTUFJQSxJQUFJVixjQUFjLEdBQWxCLEVBQXVCO0FBQzVCUixjQUFNaUMsTUFBTixDQUFhZixNQUFiLEVBQW9CLENBQXBCLEVBQXVCRSxPQUF2QjtBQUNBbEIsbUJBQVcrQixNQUFYLENBQWtCZixNQUFsQixFQUF5QixDQUF6QixFQUE0QmEsU0FBNUI7QUFDQWI7QUFDRCxPQUpNLE1BSUEsSUFBSVYsY0FBYyxJQUFsQixFQUF3QjtBQUM3QixZQUFJMEIsb0JBQW9CakIsTUFBS2pCLEtBQUwsQ0FBV21CLElBQUksQ0FBZixJQUFvQkYsTUFBS2pCLEtBQUwsQ0FBV21CLElBQUksQ0FBZixFQUFrQixDQUFsQixDQUFwQixHQUEyQyxJQUFuRTtBQUNBLFlBQUllLHNCQUFzQixHQUExQixFQUErQjtBQUM3QnBCLHdCQUFjLElBQWQ7QUFDRCxTQUZELE1BRU8sSUFBSW9CLHNCQUFzQixHQUExQixFQUErQjtBQUNwQ25CLHFCQUFXLElBQVg7QUFDRDtBQUNGO0FBQ0Y7QUFDRjs7QUFFRDtBQUNBLE1BQUlELFdBQUosRUFBaUI7QUFDZixXQUFPLENBQUNkLE1BQU1BLE1BQU1GLE1BQU4sR0FBZSxDQUFyQixDQUFSLEVBQWlDO0FBQy9CRSxZQUFNbUMsR0FBTjtBQUNBakMsaUJBQVdpQyxHQUFYO0FBQ0Q7QUFDRixHQUxELE1BS08sSUFBSXBCLFFBQUosRUFBYztBQUNuQmYsVUFBTW9DLElBQU4sQ0FBVyxFQUFYO0FBQ0FsQyxlQUFXa0MsSUFBWCxDQUFnQixJQUFoQjtBQUNEO0FBQ0QsT0FBSyxJQUFJQyxLQUFLLENBQWQsRUFBaUJBLEtBQUtyQyxNQUFNRixNQUFOLEdBQWUsQ0FBckMsRUFBd0N1QyxJQUF4QyxFQUE4QztBQUM1Q3JDLFVBQU1xQyxFQUFOLElBQVlyQyxNQUFNcUMsRUFBTixJQUFZbkMsV0FBV21DLEVBQVgsQ0FBeEI7QUFDRDtBQUNELFNBQU9yQyxNQUFNc0MsSUFBTixDQUFXLEVBQVgsQ0FBUDtBQUNEOztBQUVEO0FBQ08sU0FBUzlDLFlBQVQsQ0FBc0JFLE9BQXRCLEVBQStCQyxPQUEvQixFQUF3QztBQUM3QyxNQUFJLE9BQU9ELE9BQVAsS0FBbUIsUUFBdkIsRUFBaUM7QUFDL0JBLGNBQVUsd0VBQVdBLE9BQVgsQ0FBVjtBQUNEOztBQUVELE1BQUk2QyxlQUFlLENBQW5CO0FBQ0EsV0FBU0MsWUFBVCxHQUF3QjtBQUN0QixRQUFJQyxRQUFRL0MsUUFBUTZDLGNBQVIsQ0FBWjtBQUNBLFFBQUksQ0FBQ0UsS0FBTCxFQUFZO0FBQ1YsYUFBTzlDLFFBQVErQyxRQUFSLEVBQVA7QUFDRDs7QUFFRC9DLFlBQVFnRCxRQUFSLENBQWlCRixLQUFqQixFQUF3QixVQUFTRyxHQUFULEVBQWNDLElBQWQsRUFBb0I7QUFDMUMsVUFBSUQsR0FBSixFQUFTO0FBQ1AsZUFBT2pELFFBQVErQyxRQUFSLENBQWlCRSxHQUFqQixDQUFQO0FBQ0Q7O0FBRUQsVUFBSUUsaUJBQWlCdkQsV0FBV3NELElBQVgsRUFBaUJKLEtBQWpCLEVBQXdCOUMsT0FBeEIsQ0FBckI7QUFDQUEsY0FBUW9ELE9BQVIsQ0FBZ0JOLEtBQWhCLEVBQXVCSyxjQUF2QixFQUF1QyxVQUFTRixHQUFULEVBQWM7QUFDbkQsWUFBSUEsR0FBSixFQUFTO0FBQ1AsaUJBQU9qRCxRQUFRK0MsUUFBUixDQUFpQkUsR0FBakIsQ0FBUDtBQUNEOztBQUVESjtBQUNELE9BTkQ7QUFPRCxLQWJEO0FBY0Q7QUFDREE7QUFDRCIsImZpbGUiOiJhcHBseS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7cGFyc2VQYXRjaH0gZnJvbSAnLi9wYXJzZSc7XG5pbXBvcnQgZGlzdGFuY2VJdGVyYXRvciBmcm9tICcuLi91dGlsL2Rpc3RhbmNlLWl0ZXJhdG9yJztcblxuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5UGF0Y2goc291cmNlLCB1bmlEaWZmLCBvcHRpb25zID0ge30pIHtcbiAgaWYgKHR5cGVvZiB1bmlEaWZmID09PSAnc3RyaW5nJykge1xuICAgIHVuaURpZmYgPSBwYXJzZVBhdGNoKHVuaURpZmYpO1xuICB9XG5cbiAgaWYgKEFycmF5LmlzQXJyYXkodW5pRGlmZikpIHtcbiAgICBpZiAodW5pRGlmZi5sZW5ndGggPiAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2FwcGx5UGF0Y2ggb25seSB3b3JrcyB3aXRoIGEgc2luZ2xlIGlucHV0LicpO1xuICAgIH1cblxuICAgIHVuaURpZmYgPSB1bmlEaWZmWzBdO1xuICB9XG5cbiAgLy8gQXBwbHkgdGhlIGRpZmYgdG8gdGhlIGlucHV0XG4gIGxldCBsaW5lcyA9IHNvdXJjZS5zcGxpdCgvXFxyXFxufFtcXG5cXHZcXGZcXHJcXHg4NV0vKSxcbiAgICAgIGRlbGltaXRlcnMgPSBzb3VyY2UubWF0Y2goL1xcclxcbnxbXFxuXFx2XFxmXFxyXFx4ODVdL2cpIHx8IFtdLFxuICAgICAgaHVua3MgPSB1bmlEaWZmLmh1bmtzLFxuXG4gICAgICBjb21wYXJlTGluZSA9IG9wdGlvbnMuY29tcGFyZUxpbmUgfHwgKChsaW5lTnVtYmVyLCBsaW5lLCBvcGVyYXRpb24sIHBhdGNoQ29udGVudCkgPT4gbGluZSA9PT0gcGF0Y2hDb250ZW50KSxcbiAgICAgIGVycm9yQ291bnQgPSAwLFxuICAgICAgZnV6ekZhY3RvciA9IG9wdGlvbnMuZnV6ekZhY3RvciB8fCAwLFxuICAgICAgbWluTGluZSA9IDAsXG4gICAgICBvZmZzZXQgPSAwLFxuXG4gICAgICByZW1vdmVFT0ZOTCxcbiAgICAgIGFkZEVPRk5MO1xuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIGh1bmsgZXhhY3RseSBmaXRzIG9uIHRoZSBwcm92aWRlZCBsb2NhdGlvblxuICAgKi9cbiAgZnVuY3Rpb24gaHVua0ZpdHMoaHVuaywgdG9Qb3MpIHtcbiAgICBmb3IgKGxldCBqID0gMDsgaiA8IGh1bmsubGluZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgIGxldCBsaW5lID0gaHVuay5saW5lc1tqXSxcbiAgICAgICAgICBvcGVyYXRpb24gPSAobGluZS5sZW5ndGggPiAwID8gbGluZVswXSA6ICcgJyksXG4gICAgICAgICAgY29udGVudCA9IChsaW5lLmxlbmd0aCA+IDAgPyBsaW5lLnN1YnN0cigxKSA6IGxpbmUpO1xuXG4gICAgICBpZiAob3BlcmF0aW9uID09PSAnICcgfHwgb3BlcmF0aW9uID09PSAnLScpIHtcbiAgICAgICAgLy8gQ29udGV4dCBzYW5pdHkgY2hlY2tcbiAgICAgICAgaWYgKCFjb21wYXJlTGluZSh0b1BvcyArIDEsIGxpbmVzW3RvUG9zXSwgb3BlcmF0aW9uLCBjb250ZW50KSkge1xuICAgICAgICAgIGVycm9yQ291bnQrKztcblxuICAgICAgICAgIGlmIChlcnJvckNvdW50ID4gZnV6ekZhY3Rvcikge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0b1BvcysrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLy8gU2VhcmNoIGJlc3QgZml0IG9mZnNldHMgZm9yIGVhY2ggaHVuayBiYXNlZCBvbiB0aGUgcHJldmlvdXMgb25lc1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGh1bmtzLmxlbmd0aDsgaSsrKSB7XG4gICAgbGV0IGh1bmsgPSBodW5rc1tpXSxcbiAgICAgICAgbWF4TGluZSA9IGxpbmVzLmxlbmd0aCAtIGh1bmsub2xkTGluZXMsXG4gICAgICAgIGxvY2FsT2Zmc2V0ID0gMCxcbiAgICAgICAgdG9Qb3MgPSBvZmZzZXQgKyBodW5rLm9sZFN0YXJ0IC0gMTtcblxuICAgIGxldCBpdGVyYXRvciA9IGRpc3RhbmNlSXRlcmF0b3IodG9Qb3MsIG1pbkxpbmUsIG1heExpbmUpO1xuXG4gICAgZm9yICg7IGxvY2FsT2Zmc2V0ICE9PSB1bmRlZmluZWQ7IGxvY2FsT2Zmc2V0ID0gaXRlcmF0b3IoKSkge1xuICAgICAgaWYgKGh1bmtGaXRzKGh1bmssIHRvUG9zICsgbG9jYWxPZmZzZXQpKSB7XG4gICAgICAgIGh1bmsub2Zmc2V0ID0gb2Zmc2V0ICs9IGxvY2FsT2Zmc2V0O1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAobG9jYWxPZmZzZXQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIFNldCBsb3dlciB0ZXh0IGxpbWl0IHRvIGVuZCBvZiB0aGUgY3VycmVudCBodW5rLCBzbyBuZXh0IG9uZXMgZG9uJ3QgdHJ5XG4gICAgLy8gdG8gZml0IG92ZXIgYWxyZWFkeSBwYXRjaGVkIHRleHRcbiAgICBtaW5MaW5lID0gaHVuay5vZmZzZXQgKyBodW5rLm9sZFN0YXJ0ICsgaHVuay5vbGRMaW5lcztcbiAgfVxuXG4gIC8vIEFwcGx5IHBhdGNoIGh1bmtzXG4gIGxldCBkaWZmT2Zmc2V0ID0gMDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBodW5rcy5sZW5ndGg7IGkrKykge1xuICAgIGxldCBodW5rID0gaHVua3NbaV0sXG4gICAgICAgIHRvUG9zID0gaHVuay5vbGRTdGFydCArIGh1bmsub2Zmc2V0ICsgZGlmZk9mZnNldCAtIDE7XG4gICAgZGlmZk9mZnNldCArPSBodW5rLm5ld0xpbmVzIC0gaHVuay5vbGRMaW5lcztcblxuICAgIGlmICh0b1BvcyA8IDApIHsgLy8gQ3JlYXRpbmcgYSBuZXcgZmlsZVxuICAgICAgdG9Qb3MgPSAwO1xuICAgIH1cblxuICAgIGZvciAobGV0IGogPSAwOyBqIDwgaHVuay5saW5lcy5sZW5ndGg7IGorKykge1xuICAgICAgbGV0IGxpbmUgPSBodW5rLmxpbmVzW2pdLFxuICAgICAgICAgIG9wZXJhdGlvbiA9IChsaW5lLmxlbmd0aCA+IDAgPyBsaW5lWzBdIDogJyAnKSxcbiAgICAgICAgICBjb250ZW50ID0gKGxpbmUubGVuZ3RoID4gMCA/IGxpbmUuc3Vic3RyKDEpIDogbGluZSksXG4gICAgICAgICAgZGVsaW1pdGVyID0gaHVuay5saW5lZGVsaW1pdGVyc1tqXTtcblxuICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJyAnKSB7XG4gICAgICAgIHRvUG9zKys7XG4gICAgICB9IGVsc2UgaWYgKG9wZXJhdGlvbiA9PT0gJy0nKSB7XG4gICAgICAgIGxpbmVzLnNwbGljZSh0b1BvcywgMSk7XG4gICAgICAgIGRlbGltaXRlcnMuc3BsaWNlKHRvUG9zLCAxKTtcbiAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBlbHNlICovXG4gICAgICB9IGVsc2UgaWYgKG9wZXJhdGlvbiA9PT0gJysnKSB7XG4gICAgICAgIGxpbmVzLnNwbGljZSh0b1BvcywgMCwgY29udGVudCk7XG4gICAgICAgIGRlbGltaXRlcnMuc3BsaWNlKHRvUG9zLCAwLCBkZWxpbWl0ZXIpO1xuICAgICAgICB0b1BvcysrO1xuICAgICAgfSBlbHNlIGlmIChvcGVyYXRpb24gPT09ICdcXFxcJykge1xuICAgICAgICBsZXQgcHJldmlvdXNPcGVyYXRpb24gPSBodW5rLmxpbmVzW2ogLSAxXSA/IGh1bmsubGluZXNbaiAtIDFdWzBdIDogbnVsbDtcbiAgICAgICAgaWYgKHByZXZpb3VzT3BlcmF0aW9uID09PSAnKycpIHtcbiAgICAgICAgICByZW1vdmVFT0ZOTCA9IHRydWU7XG4gICAgICAgIH0gZWxzZSBpZiAocHJldmlvdXNPcGVyYXRpb24gPT09ICctJykge1xuICAgICAgICAgIGFkZEVPRk5MID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIEhhbmRsZSBFT0ZOTCBpbnNlcnRpb24vcmVtb3ZhbFxuICBpZiAocmVtb3ZlRU9GTkwpIHtcbiAgICB3aGlsZSAoIWxpbmVzW2xpbmVzLmxlbmd0aCAtIDFdKSB7XG4gICAgICBsaW5lcy5wb3AoKTtcbiAgICAgIGRlbGltaXRlcnMucG9wKCk7XG4gICAgfVxuICB9IGVsc2UgaWYgKGFkZEVPRk5MKSB7XG4gICAgbGluZXMucHVzaCgnJyk7XG4gICAgZGVsaW1pdGVycy5wdXNoKCdcXG4nKTtcbiAgfVxuICBmb3IgKGxldCBfayA9IDA7IF9rIDwgbGluZXMubGVuZ3RoIC0gMTsgX2srKykge1xuICAgIGxpbmVzW19rXSA9IGxpbmVzW19rXSArIGRlbGltaXRlcnNbX2tdO1xuICB9XG4gIHJldHVybiBsaW5lcy5qb2luKCcnKTtcbn1cblxuLy8gV3JhcHBlciB0aGF0IHN1cHBvcnRzIG11bHRpcGxlIGZpbGUgcGF0Y2hlcyB2aWEgY2FsbGJhY2tzLlxuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5UGF0Y2hlcyh1bmlEaWZmLCBvcHRpb25zKSB7XG4gIGlmICh0eXBlb2YgdW5pRGlmZiA9PT0gJ3N0cmluZycpIHtcbiAgICB1bmlEaWZmID0gcGFyc2VQYXRjaCh1bmlEaWZmKTtcbiAgfVxuXG4gIGxldCBjdXJyZW50SW5kZXggPSAwO1xuICBmdW5jdGlvbiBwcm9jZXNzSW5kZXgoKSB7XG4gICAgbGV0IGluZGV4ID0gdW5pRGlmZltjdXJyZW50SW5kZXgrK107XG4gICAgaWYgKCFpbmRleCkge1xuICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICBvcHRpb25zLmxvYWRGaWxlKGluZGV4LCBmdW5jdGlvbihlcnIsIGRhdGEpIHtcbiAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgcmV0dXJuIG9wdGlvbnMuY29tcGxldGUoZXJyKTtcbiAgICAgIH1cblxuICAgICAgbGV0IHVwZGF0ZWRDb250ZW50ID0gYXBwbHlQYXRjaChkYXRhLCBpbmRleCwgb3B0aW9ucyk7XG4gICAgICBvcHRpb25zLnBhdGNoZWQoaW5kZXgsIHVwZGF0ZWRDb250ZW50LCBmdW5jdGlvbihlcnIpIHtcbiAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgIHJldHVybiBvcHRpb25zLmNvbXBsZXRlKGVycik7XG4gICAgICAgIH1cblxuICAgICAgICBwcm9jZXNzSW5kZXgoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG4gIHByb2Nlc3NJbmRleCgpO1xufVxuIl19 diff --git a/WebServer/node_modules/diff/lib/patch/create.js b/WebServer/node_modules/diff/lib/patch/create.js new file mode 100644 index 000000000..be4d1876e --- /dev/null +++ b/WebServer/node_modules/diff/lib/patch/create.js @@ -0,0 +1,148 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/structuredPatch = structuredPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = createTwoFilesPatch; +/*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = createPatch; + +var /*istanbul ignore start*/_line = require('../diff/line') /*istanbul ignore end*/; + +/*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +/*istanbul ignore end*/function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + if (!options) { + options = {}; + } + if (typeof options.context === 'undefined') { + options.context = 4; + } + + var diff = /*istanbul ignore start*/(0, _line.diffLines) /*istanbul ignore end*/(oldStr, newStr, options); + diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + + var hunks = []; + var oldRangeStart = 0, + newRangeStart = 0, + curRange = [], + oldLine = 1, + newLine = 1; + + /*istanbul ignore start*/var _loop = function _loop( /*istanbul ignore end*/i) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + /*istanbul ignore start*/var _curRange; + + /*istanbul ignore end*/ // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + + // Output our changes + /*istanbul ignore start*/(_curRange = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); + + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + /*istanbul ignore start*/var _curRange2; + + /*istanbul ignore end*/ // Overlapping + /*istanbul ignore start*/(_curRange2 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines))); + } else { + /*istanbul ignore start*/var _curRange3; + + /*istanbul ignore end*/ // end the range and output + var contextSize = Math.min(lines.length, options.context); + /*istanbul ignore start*/(_curRange3 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines.slice(0, contextSize)))); + + var hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + if (i >= diff.length - 2 && lines.length <= options.context) { + // EOF is inside this hunk + var oldEOFNewline = /\n$/.test(oldStr); + var newEOFNewline = /\n$/.test(newStr); + if (lines.length == 0 && !oldEOFNewline) { + // special case: old has no eol and no trailing context; no-nl can end up before adds + curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); + } else if (!oldEOFNewline || !newEOFNewline) { + curRange.push('\\ No newline at end of file'); + } + } + hunks.push(hunk); + + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + }; + + for (var i = 0; i < diff.length; i++) { + /*istanbul ignore start*/_loop( /*istanbul ignore end*/i); + } + + return { + oldFileName: oldFileName, newFileName: newFileName, + oldHeader: oldHeader, newHeader: newHeader, + hunks: hunks + }; +} + +function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + + var ret = []; + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } + ret.push('==================================================================='); + ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); + ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); + + for (var i = 0; i < diff.hunks.length; i++) { + var hunk = diff.hunks[i]; + ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); + ret.push.apply(ret, hunk.lines); + } + + return ret.join('\n') + '\n'; +} + +function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { + return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9jcmVhdGUuanMiXSwibmFtZXMiOlsic3RydWN0dXJlZFBhdGNoIiwiY3JlYXRlVHdvRmlsZXNQYXRjaCIsImNyZWF0ZVBhdGNoIiwib2xkRmlsZU5hbWUiLCJuZXdGaWxlTmFtZSIsIm9sZFN0ciIsIm5ld1N0ciIsIm9sZEhlYWRlciIsIm5ld0hlYWRlciIsIm9wdGlvbnMiLCJjb250ZXh0IiwiZGlmZiIsInB1c2giLCJ2YWx1ZSIsImxpbmVzIiwiY29udGV4dExpbmVzIiwibWFwIiwiZW50cnkiLCJodW5rcyIsIm9sZFJhbmdlU3RhcnQiLCJuZXdSYW5nZVN0YXJ0IiwiY3VyUmFuZ2UiLCJvbGRMaW5lIiwibmV3TGluZSIsImkiLCJjdXJyZW50IiwicmVwbGFjZSIsInNwbGl0IiwiYWRkZWQiLCJyZW1vdmVkIiwicHJldiIsInNsaWNlIiwibGVuZ3RoIiwiY29udGV4dFNpemUiLCJNYXRoIiwibWluIiwiaHVuayIsIm9sZFN0YXJ0Iiwib2xkTGluZXMiLCJuZXdTdGFydCIsIm5ld0xpbmVzIiwib2xkRU9GTmV3bGluZSIsInRlc3QiLCJuZXdFT0ZOZXdsaW5lIiwic3BsaWNlIiwicmV0IiwiYXBwbHkiLCJqb2luIiwiZmlsZU5hbWUiXSwibWFwcGluZ3MiOiI7OztnQ0FFZ0JBLGUsR0FBQUEsZTt5REFpR0FDLG1CLEdBQUFBLG1CO3lEQXdCQUMsVyxHQUFBQSxXOztBQTNIaEI7Ozs7dUJBRU8sU0FBU0YsZUFBVCxDQUF5QkcsV0FBekIsRUFBc0NDLFdBQXRDLEVBQW1EQyxNQUFuRCxFQUEyREMsTUFBM0QsRUFBbUVDLFNBQW5FLEVBQThFQyxTQUE5RSxFQUF5RkMsT0FBekYsRUFBa0c7QUFDdkcsTUFBSSxDQUFDQSxPQUFMLEVBQWM7QUFDWkEsY0FBVSxFQUFWO0FBQ0Q7QUFDRCxNQUFJLE9BQU9BLFFBQVFDLE9BQWYsS0FBMkIsV0FBL0IsRUFBNEM7QUFDMUNELFlBQVFDLE9BQVIsR0FBa0IsQ0FBbEI7QUFDRDs7QUFFRCxNQUFNQyxPQUFPLHNFQUFVTixNQUFWLEVBQWtCQyxNQUFsQixFQUEwQkcsT0FBMUIsQ0FBYjtBQUNBRSxPQUFLQyxJQUFMLENBQVUsRUFBQ0MsT0FBTyxFQUFSLEVBQVlDLE9BQU8sRUFBbkIsRUFBVixFQVR1RyxDQVNsRTs7QUFFckMsV0FBU0MsWUFBVCxDQUFzQkQsS0FBdEIsRUFBNkI7QUFDM0IsV0FBT0EsTUFBTUUsR0FBTixDQUFVLFVBQVNDLEtBQVQsRUFBZ0I7QUFBRSxhQUFPLE1BQU1BLEtBQWI7QUFBcUIsS0FBakQsQ0FBUDtBQUNEOztBQUVELE1BQUlDLFFBQVEsRUFBWjtBQUNBLE1BQUlDLGdCQUFnQixDQUFwQjtBQUFBLE1BQXVCQyxnQkFBZ0IsQ0FBdkM7QUFBQSxNQUEwQ0MsV0FBVyxFQUFyRDtBQUFBLE1BQ0lDLFVBQVUsQ0FEZDtBQUFBLE1BQ2lCQyxVQUFVLENBRDNCOztBQWhCdUcsOEVBa0I5RkMsQ0FsQjhGO0FBbUJyRyxRQUFNQyxVQUFVZCxLQUFLYSxDQUFMLENBQWhCO0FBQUEsUUFDTVYsUUFBUVcsUUFBUVgsS0FBUixJQUFpQlcsUUFBUVosS0FBUixDQUFjYSxPQUFkLENBQXNCLEtBQXRCLEVBQTZCLEVBQTdCLEVBQWlDQyxLQUFqQyxDQUF1QyxJQUF2QyxDQUQvQjtBQUVBRixZQUFRWCxLQUFSLEdBQWdCQSxLQUFoQjs7QUFFQSxRQUFJVyxRQUFRRyxLQUFSLElBQWlCSCxRQUFRSSxPQUE3QixFQUFzQztBQUFBOztBQUFBLDhCQUNwQztBQUNBLFVBQUksQ0FBQ1YsYUFBTCxFQUFvQjtBQUNsQixZQUFNVyxPQUFPbkIsS0FBS2EsSUFBSSxDQUFULENBQWI7QUFDQUwsd0JBQWdCRyxPQUFoQjtBQUNBRix3QkFBZ0JHLE9BQWhCOztBQUVBLFlBQUlPLElBQUosRUFBVTtBQUNSVCxxQkFBV1osUUFBUUMsT0FBUixHQUFrQixDQUFsQixHQUFzQkssYUFBYWUsS0FBS2hCLEtBQUwsQ0FBV2lCLEtBQVgsQ0FBaUIsQ0FBQ3RCLFFBQVFDLE9BQTFCLENBQWIsQ0FBdEIsR0FBeUUsRUFBcEY7QUFDQVMsMkJBQWlCRSxTQUFTVyxNQUExQjtBQUNBWiwyQkFBaUJDLFNBQVNXLE1BQTFCO0FBQ0Q7QUFDRjs7QUFFRDtBQUNBLDZFQUFTcEIsSUFBVCwwTEFBa0JFLE1BQU1FLEdBQU4sQ0FBVSxVQUFTQyxLQUFULEVBQWdCO0FBQzFDLGVBQU8sQ0FBQ1EsUUFBUUcsS0FBUixHQUFnQixHQUFoQixHQUFzQixHQUF2QixJQUE4QlgsS0FBckM7QUFDRCxPQUZpQixDQUFsQjs7QUFJQTtBQUNBLFVBQUlRLFFBQVFHLEtBQVosRUFBbUI7QUFDakJMLG1CQUFXVCxNQUFNa0IsTUFBakI7QUFDRCxPQUZELE1BRU87QUFDTFYsbUJBQVdSLE1BQU1rQixNQUFqQjtBQUNEO0FBQ0YsS0F6QkQsTUF5Qk87QUFDTDtBQUNBLFVBQUliLGFBQUosRUFBbUI7QUFDakI7QUFDQSxZQUFJTCxNQUFNa0IsTUFBTixJQUFnQnZCLFFBQVFDLE9BQVIsR0FBa0IsQ0FBbEMsSUFBdUNjLElBQUliLEtBQUtxQixNQUFMLEdBQWMsQ0FBN0QsRUFBZ0U7QUFBQTs7QUFBQSxrQ0FDOUQ7QUFDQSxrRkFBU3BCLElBQVQsMkxBQWtCRyxhQUFhRCxLQUFiLENBQWxCO0FBQ0QsU0FIRCxNQUdPO0FBQUE7O0FBQUEsa0NBQ0w7QUFDQSxjQUFJbUIsY0FBY0MsS0FBS0MsR0FBTCxDQUFTckIsTUFBTWtCLE1BQWYsRUFBdUJ2QixRQUFRQyxPQUEvQixDQUFsQjtBQUNBLGtGQUFTRSxJQUFULDJMQUFrQkcsYUFBYUQsTUFBTWlCLEtBQU4sQ0FBWSxDQUFaLEVBQWVFLFdBQWYsQ0FBYixDQUFsQjs7QUFFQSxjQUFJRyxPQUFPO0FBQ1RDLHNCQUFVbEIsYUFERDtBQUVUbUIsc0JBQVdoQixVQUFVSCxhQUFWLEdBQTBCYyxXQUY1QjtBQUdUTSxzQkFBVW5CLGFBSEQ7QUFJVG9CLHNCQUFXakIsVUFBVUgsYUFBVixHQUEwQmEsV0FKNUI7QUFLVG5CLG1CQUFPTztBQUxFLFdBQVg7QUFPQSxjQUFJRyxLQUFLYixLQUFLcUIsTUFBTCxHQUFjLENBQW5CLElBQXdCbEIsTUFBTWtCLE1BQU4sSUFBZ0J2QixRQUFRQyxPQUFwRCxFQUE2RDtBQUMzRDtBQUNBLGdCQUFJK0IsZ0JBQWlCLE1BQU1DLElBQU4sQ0FBV3JDLE1BQVgsQ0FBckI7QUFDQSxnQkFBSXNDLGdCQUFpQixNQUFNRCxJQUFOLENBQVdwQyxNQUFYLENBQXJCO0FBQ0EsZ0JBQUlRLE1BQU1rQixNQUFOLElBQWdCLENBQWhCLElBQXFCLENBQUNTLGFBQTFCLEVBQXlDO0FBQ3ZDO0FBQ0FwQix1QkFBU3VCLE1BQVQsQ0FBZ0JSLEtBQUtFLFFBQXJCLEVBQStCLENBQS9CLEVBQWtDLDhCQUFsQztBQUNELGFBSEQsTUFHTyxJQUFJLENBQUNHLGFBQUQsSUFBa0IsQ0FBQ0UsYUFBdkIsRUFBc0M7QUFDM0N0Qix1QkFBU1QsSUFBVCxDQUFjLDhCQUFkO0FBQ0Q7QUFDRjtBQUNETSxnQkFBTU4sSUFBTixDQUFXd0IsSUFBWDs7QUFFQWpCLDBCQUFnQixDQUFoQjtBQUNBQywwQkFBZ0IsQ0FBaEI7QUFDQUMscUJBQVcsRUFBWDtBQUNEO0FBQ0Y7QUFDREMsaUJBQVdSLE1BQU1rQixNQUFqQjtBQUNBVCxpQkFBV1QsTUFBTWtCLE1BQWpCO0FBQ0Q7QUF2Rm9HOztBQWtCdkcsT0FBSyxJQUFJUixJQUFJLENBQWIsRUFBZ0JBLElBQUliLEtBQUtxQixNQUF6QixFQUFpQ1IsR0FBakMsRUFBc0M7QUFBQSwyREFBN0JBLENBQTZCO0FBc0VyQzs7QUFFRCxTQUFPO0FBQ0xyQixpQkFBYUEsV0FEUixFQUNxQkMsYUFBYUEsV0FEbEM7QUFFTEcsZUFBV0EsU0FGTixFQUVpQkMsV0FBV0EsU0FGNUI7QUFHTFUsV0FBT0E7QUFIRixHQUFQO0FBS0Q7O0FBRU0sU0FBU2pCLG1CQUFULENBQTZCRSxXQUE3QixFQUEwQ0MsV0FBMUMsRUFBdURDLE1BQXZELEVBQStEQyxNQUEvRCxFQUF1RUMsU0FBdkUsRUFBa0ZDLFNBQWxGLEVBQTZGQyxPQUE3RixFQUFzRztBQUMzRyxNQUFNRSxPQUFPWCxnQkFBZ0JHLFdBQWhCLEVBQTZCQyxXQUE3QixFQUEwQ0MsTUFBMUMsRUFBa0RDLE1BQWxELEVBQTBEQyxTQUExRCxFQUFxRUMsU0FBckUsRUFBZ0ZDLE9BQWhGLENBQWI7O0FBRUEsTUFBTW9DLE1BQU0sRUFBWjtBQUNBLE1BQUkxQyxlQUFlQyxXQUFuQixFQUFnQztBQUM5QnlDLFFBQUlqQyxJQUFKLENBQVMsWUFBWVQsV0FBckI7QUFDRDtBQUNEMEMsTUFBSWpDLElBQUosQ0FBUyxxRUFBVDtBQUNBaUMsTUFBSWpDLElBQUosQ0FBUyxTQUFTRCxLQUFLUixXQUFkLElBQTZCLE9BQU9RLEtBQUtKLFNBQVosS0FBMEIsV0FBMUIsR0FBd0MsRUFBeEMsR0FBNkMsT0FBT0ksS0FBS0osU0FBdEYsQ0FBVDtBQUNBc0MsTUFBSWpDLElBQUosQ0FBUyxTQUFTRCxLQUFLUCxXQUFkLElBQTZCLE9BQU9PLEtBQUtILFNBQVosS0FBMEIsV0FBMUIsR0FBd0MsRUFBeEMsR0FBNkMsT0FBT0csS0FBS0gsU0FBdEYsQ0FBVDs7QUFFQSxPQUFLLElBQUlnQixJQUFJLENBQWIsRUFBZ0JBLElBQUliLEtBQUtPLEtBQUwsQ0FBV2MsTUFBL0IsRUFBdUNSLEdBQXZDLEVBQTRDO0FBQzFDLFFBQU1ZLE9BQU96QixLQUFLTyxLQUFMLENBQVdNLENBQVgsQ0FBYjtBQUNBcUIsUUFBSWpDLElBQUosQ0FDRSxTQUFTd0IsS0FBS0MsUUFBZCxHQUF5QixHQUF6QixHQUErQkQsS0FBS0UsUUFBcEMsR0FDRSxJQURGLEdBQ1NGLEtBQUtHLFFBRGQsR0FDeUIsR0FEekIsR0FDK0JILEtBQUtJLFFBRHBDLEdBRUUsS0FISjtBQUtBSyxRQUFJakMsSUFBSixDQUFTa0MsS0FBVCxDQUFlRCxHQUFmLEVBQW9CVCxLQUFLdEIsS0FBekI7QUFDRDs7QUFFRCxTQUFPK0IsSUFBSUUsSUFBSixDQUFTLElBQVQsSUFBaUIsSUFBeEI7QUFDRDs7QUFFTSxTQUFTN0MsV0FBVCxDQUFxQjhDLFFBQXJCLEVBQStCM0MsTUFBL0IsRUFBdUNDLE1BQXZDLEVBQStDQyxTQUEvQyxFQUEwREMsU0FBMUQsRUFBcUVDLE9BQXJFLEVBQThFO0FBQ25GLFNBQU9SLG9CQUFvQitDLFFBQXBCLEVBQThCQSxRQUE5QixFQUF3QzNDLE1BQXhDLEVBQWdEQyxNQUFoRCxFQUF3REMsU0FBeEQsRUFBbUVDLFNBQW5FLEVBQThFQyxPQUE5RSxDQUFQO0FBQ0QiLCJmaWxlIjoiY3JlYXRlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtkaWZmTGluZXN9IGZyb20gJy4uL2RpZmYvbGluZSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBzdHJ1Y3R1cmVkUGF0Y2gob2xkRmlsZU5hbWUsIG5ld0ZpbGVOYW1lLCBvbGRTdHIsIG5ld1N0ciwgb2xkSGVhZGVyLCBuZXdIZWFkZXIsIG9wdGlvbnMpIHtcbiAgaWYgKCFvcHRpb25zKSB7XG4gICAgb3B0aW9ucyA9IHt9O1xuICB9XG4gIGlmICh0eXBlb2Ygb3B0aW9ucy5jb250ZXh0ID09PSAndW5kZWZpbmVkJykge1xuICAgIG9wdGlvbnMuY29udGV4dCA9IDQ7XG4gIH1cblxuICBjb25zdCBkaWZmID0gZGlmZkxpbmVzKG9sZFN0ciwgbmV3U3RyLCBvcHRpb25zKTtcbiAgZGlmZi5wdXNoKHt2YWx1ZTogJycsIGxpbmVzOiBbXX0pOyAgIC8vIEFwcGVuZCBhbiBlbXB0eSB2YWx1ZSB0byBtYWtlIGNsZWFudXAgZWFzaWVyXG5cbiAgZnVuY3Rpb24gY29udGV4dExpbmVzKGxpbmVzKSB7XG4gICAgcmV0dXJuIGxpbmVzLm1hcChmdW5jdGlvbihlbnRyeSkgeyByZXR1cm4gJyAnICsgZW50cnk7IH0pO1xuICB9XG5cbiAgbGV0IGh1bmtzID0gW107XG4gIGxldCBvbGRSYW5nZVN0YXJ0ID0gMCwgbmV3UmFuZ2VTdGFydCA9IDAsIGN1clJhbmdlID0gW10sXG4gICAgICBvbGRMaW5lID0gMSwgbmV3TGluZSA9IDE7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGlmZi5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGN1cnJlbnQgPSBkaWZmW2ldLFxuICAgICAgICAgIGxpbmVzID0gY3VycmVudC5saW5lcyB8fCBjdXJyZW50LnZhbHVlLnJlcGxhY2UoL1xcbiQvLCAnJykuc3BsaXQoJ1xcbicpO1xuICAgIGN1cnJlbnQubGluZXMgPSBsaW5lcztcblxuICAgIGlmIChjdXJyZW50LmFkZGVkIHx8IGN1cnJlbnQucmVtb3ZlZCkge1xuICAgICAgLy8gSWYgd2UgaGF2ZSBwcmV2aW91cyBjb250ZXh0LCBzdGFydCB3aXRoIHRoYXRcbiAgICAgIGlmICghb2xkUmFuZ2VTdGFydCkge1xuICAgICAgICBjb25zdCBwcmV2ID0gZGlmZltpIC0gMV07XG4gICAgICAgIG9sZFJhbmdlU3RhcnQgPSBvbGRMaW5lO1xuICAgICAgICBuZXdSYW5nZVN0YXJ0ID0gbmV3TGluZTtcblxuICAgICAgICBpZiAocHJldikge1xuICAgICAgICAgIGN1clJhbmdlID0gb3B0aW9ucy5jb250ZXh0ID4gMCA/IGNvbnRleHRMaW5lcyhwcmV2LmxpbmVzLnNsaWNlKC1vcHRpb25zLmNvbnRleHQpKSA6IFtdO1xuICAgICAgICAgIG9sZFJhbmdlU3RhcnQgLT0gY3VyUmFuZ2UubGVuZ3RoO1xuICAgICAgICAgIG5ld1JhbmdlU3RhcnQgLT0gY3VyUmFuZ2UubGVuZ3RoO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIE91dHB1dCBvdXIgY2hhbmdlc1xuICAgICAgY3VyUmFuZ2UucHVzaCguLi4gbGluZXMubWFwKGZ1bmN0aW9uKGVudHJ5KSB7XG4gICAgICAgIHJldHVybiAoY3VycmVudC5hZGRlZCA/ICcrJyA6ICctJykgKyBlbnRyeTtcbiAgICAgIH0pKTtcblxuICAgICAgLy8gVHJhY2sgdGhlIHVwZGF0ZWQgZmlsZSBwb3NpdGlvblxuICAgICAgaWYgKGN1cnJlbnQuYWRkZWQpIHtcbiAgICAgICAgbmV3TGluZSArPSBsaW5lcy5sZW5ndGg7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvbGRMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gSWRlbnRpY2FsIGNvbnRleHQgbGluZXMuIFRyYWNrIGxpbmUgY2hhbmdlc1xuICAgICAgaWYgKG9sZFJhbmdlU3RhcnQpIHtcbiAgICAgICAgLy8gQ2xvc2Ugb3V0IGFueSBjaGFuZ2VzIHRoYXQgaGF2ZSBiZWVuIG91dHB1dCAob3Igam9pbiBvdmVybGFwcGluZylcbiAgICAgICAgaWYgKGxpbmVzLmxlbmd0aCA8PSBvcHRpb25zLmNvbnRleHQgKiAyICYmIGkgPCBkaWZmLmxlbmd0aCAtIDIpIHtcbiAgICAgICAgICAvLyBPdmVybGFwcGluZ1xuICAgICAgICAgIGN1clJhbmdlLnB1c2goLi4uIGNvbnRleHRMaW5lcyhsaW5lcykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGVuZCB0aGUgcmFuZ2UgYW5kIG91dHB1dFxuICAgICAgICAgIGxldCBjb250ZXh0U2l6ZSA9IE1hdGgubWluKGxpbmVzLmxlbmd0aCwgb3B0aW9ucy5jb250ZXh0KTtcbiAgICAgICAgICBjdXJSYW5nZS5wdXNoKC4uLiBjb250ZXh0TGluZXMobGluZXMuc2xpY2UoMCwgY29udGV4dFNpemUpKSk7XG5cbiAgICAgICAgICBsZXQgaHVuayA9IHtcbiAgICAgICAgICAgIG9sZFN0YXJ0OiBvbGRSYW5nZVN0YXJ0LFxuICAgICAgICAgICAgb2xkTGluZXM6IChvbGRMaW5lIC0gb2xkUmFuZ2VTdGFydCArIGNvbnRleHRTaXplKSxcbiAgICAgICAgICAgIG5ld1N0YXJ0OiBuZXdSYW5nZVN0YXJ0LFxuICAgICAgICAgICAgbmV3TGluZXM6IChuZXdMaW5lIC0gbmV3UmFuZ2VTdGFydCArIGNvbnRleHRTaXplKSxcbiAgICAgICAgICAgIGxpbmVzOiBjdXJSYW5nZVxuICAgICAgICAgIH07XG4gICAgICAgICAgaWYgKGkgPj0gZGlmZi5sZW5ndGggLSAyICYmIGxpbmVzLmxlbmd0aCA8PSBvcHRpb25zLmNvbnRleHQpIHtcbiAgICAgICAgICAgIC8vIEVPRiBpcyBpbnNpZGUgdGhpcyBodW5rXG4gICAgICAgICAgICBsZXQgb2xkRU9GTmV3bGluZSA9ICgvXFxuJC8udGVzdChvbGRTdHIpKTtcbiAgICAgICAgICAgIGxldCBuZXdFT0ZOZXdsaW5lID0gKC9cXG4kLy50ZXN0KG5ld1N0cikpO1xuICAgICAgICAgICAgaWYgKGxpbmVzLmxlbmd0aCA9PSAwICYmICFvbGRFT0ZOZXdsaW5lKSB7XG4gICAgICAgICAgICAgIC8vIHNwZWNpYWwgY2FzZTogb2xkIGhhcyBubyBlb2wgYW5kIG5vIHRyYWlsaW5nIGNvbnRleHQ7IG5vLW5sIGNhbiBlbmQgdXAgYmVmb3JlIGFkZHNcbiAgICAgICAgICAgICAgY3VyUmFuZ2Uuc3BsaWNlKGh1bmsub2xkTGluZXMsIDAsICdcXFxcIE5vIG5ld2xpbmUgYXQgZW5kIG9mIGZpbGUnKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIW9sZEVPRk5ld2xpbmUgfHwgIW5ld0VPRk5ld2xpbmUpIHtcbiAgICAgICAgICAgICAgY3VyUmFuZ2UucHVzaCgnXFxcXCBObyBuZXdsaW5lIGF0IGVuZCBvZiBmaWxlJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGh1bmtzLnB1c2goaHVuayk7XG5cbiAgICAgICAgICBvbGRSYW5nZVN0YXJ0ID0gMDtcbiAgICAgICAgICBuZXdSYW5nZVN0YXJ0ID0gMDtcbiAgICAgICAgICBjdXJSYW5nZSA9IFtdO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBvbGRMaW5lICs9IGxpbmVzLmxlbmd0aDtcbiAgICAgIG5ld0xpbmUgKz0gbGluZXMubGVuZ3RoO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgb2xkRmlsZU5hbWU6IG9sZEZpbGVOYW1lLCBuZXdGaWxlTmFtZTogbmV3RmlsZU5hbWUsXG4gICAgb2xkSGVhZGVyOiBvbGRIZWFkZXIsIG5ld0hlYWRlcjogbmV3SGVhZGVyLFxuICAgIGh1bmtzOiBodW5rc1xuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVHdvRmlsZXNQYXRjaChvbGRGaWxlTmFtZSwgbmV3RmlsZU5hbWUsIG9sZFN0ciwgbmV3U3RyLCBvbGRIZWFkZXIsIG5ld0hlYWRlciwgb3B0aW9ucykge1xuICBjb25zdCBkaWZmID0gc3RydWN0dXJlZFBhdGNoKG9sZEZpbGVOYW1lLCBuZXdGaWxlTmFtZSwgb2xkU3RyLCBuZXdTdHIsIG9sZEhlYWRlciwgbmV3SGVhZGVyLCBvcHRpb25zKTtcblxuICBjb25zdCByZXQgPSBbXTtcbiAgaWYgKG9sZEZpbGVOYW1lID09IG5ld0ZpbGVOYW1lKSB7XG4gICAgcmV0LnB1c2goJ0luZGV4OiAnICsgb2xkRmlsZU5hbWUpO1xuICB9XG4gIHJldC5wdXNoKCc9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Jyk7XG4gIHJldC5wdXNoKCctLS0gJyArIGRpZmYub2xkRmlsZU5hbWUgKyAodHlwZW9mIGRpZmYub2xkSGVhZGVyID09PSAndW5kZWZpbmVkJyA/ICcnIDogJ1xcdCcgKyBkaWZmLm9sZEhlYWRlcikpO1xuICByZXQucHVzaCgnKysrICcgKyBkaWZmLm5ld0ZpbGVOYW1lICsgKHR5cGVvZiBkaWZmLm5ld0hlYWRlciA9PT0gJ3VuZGVmaW5lZCcgPyAnJyA6ICdcXHQnICsgZGlmZi5uZXdIZWFkZXIpKTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IGRpZmYuaHVua3MubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBodW5rID0gZGlmZi5odW5rc1tpXTtcbiAgICByZXQucHVzaChcbiAgICAgICdAQCAtJyArIGh1bmsub2xkU3RhcnQgKyAnLCcgKyBodW5rLm9sZExpbmVzXG4gICAgICArICcgKycgKyBodW5rLm5ld1N0YXJ0ICsgJywnICsgaHVuay5uZXdMaW5lc1xuICAgICAgKyAnIEBAJ1xuICAgICk7XG4gICAgcmV0LnB1c2guYXBwbHkocmV0LCBodW5rLmxpbmVzKTtcbiAgfVxuXG4gIHJldHVybiByZXQuam9pbignXFxuJykgKyAnXFxuJztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVBhdGNoKGZpbGVOYW1lLCBvbGRTdHIsIG5ld1N0ciwgb2xkSGVhZGVyLCBuZXdIZWFkZXIsIG9wdGlvbnMpIHtcbiAgcmV0dXJuIGNyZWF0ZVR3b0ZpbGVzUGF0Y2goZmlsZU5hbWUsIGZpbGVOYW1lLCBvbGRTdHIsIG5ld1N0ciwgb2xkSGVhZGVyLCBuZXdIZWFkZXIsIG9wdGlvbnMpO1xufVxuIl19 diff --git a/WebServer/node_modules/diff/lib/patch/merge.js b/WebServer/node_modules/diff/lib/patch/merge.js new file mode 100644 index 000000000..074c4bc9d --- /dev/null +++ b/WebServer/node_modules/diff/lib/patch/merge.js @@ -0,0 +1,396 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/calcLineCount = calcLineCount; +/*istanbul ignore start*/exports. /*istanbul ignore end*/merge = merge; + +var /*istanbul ignore start*/_create = require('./create') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_parse = require('./parse') /*istanbul ignore end*/; + +var /*istanbul ignore start*/_array = require('../util/array') /*istanbul ignore end*/; + +/*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +/*istanbul ignore end*/function calcLineCount(hunk) { + /*istanbul ignore start*/var _calcOldNewLineCount = /*istanbul ignore end*/calcOldNewLineCount(hunk.lines), + oldLines = _calcOldNewLineCount.oldLines, + newLines = _calcOldNewLineCount.newLines; + + if (oldLines !== undefined) { + hunk.oldLines = oldLines; + } else { + delete hunk.oldLines; + } + + if (newLines !== undefined) { + hunk.newLines = newLines; + } else { + delete hunk.newLines; + } +} + +function merge(mine, theirs, base) { + mine = loadPatch(mine, base); + theirs = loadPatch(theirs, base); + + var ret = {}; + + // For index we just let it pass through as it doesn't have any necessary meaning. + // Leaving sanity checks on this to the API consumer that may know more about the + // meaning in their own context. + if (mine.index || theirs.index) { + ret.index = mine.index || theirs.index; + } + + if (mine.newFileName || theirs.newFileName) { + if (!fileNameChanged(mine)) { + // No header or no change in ours, use theirs (and ours if theirs does not exist) + ret.oldFileName = theirs.oldFileName || mine.oldFileName; + ret.newFileName = theirs.newFileName || mine.newFileName; + ret.oldHeader = theirs.oldHeader || mine.oldHeader; + ret.newHeader = theirs.newHeader || mine.newHeader; + } else if (!fileNameChanged(theirs)) { + // No header or no change in theirs, use ours + ret.oldFileName = mine.oldFileName; + ret.newFileName = mine.newFileName; + ret.oldHeader = mine.oldHeader; + ret.newHeader = mine.newHeader; + } else { + // Both changed... figure it out + ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName); + ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName); + ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader); + ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); + } + } + + ret.hunks = []; + + var mineIndex = 0, + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; + + while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { + var mineCurrent = mine.hunks[mineIndex] || { oldStart: Infinity }, + theirsCurrent = theirs.hunks[theirsIndex] || { oldStart: Infinity }; + + if (hunkBefore(mineCurrent, theirsCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); + mineIndex++; + theirsOffset += mineCurrent.newLines - mineCurrent.oldLines; + } else if (hunkBefore(theirsCurrent, mineCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset)); + theirsIndex++; + mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines; + } else { + // Overlap, merge as best we can + var mergedHunk = { + oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart), + oldLines: 0, + newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset), + newLines: 0, + lines: [] + }; + mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines); + theirsIndex++; + mineIndex++; + + ret.hunks.push(mergedHunk); + } + } + + return ret; +} + +function loadPatch(param, base) { + if (typeof param === 'string') { + if (/^@@/m.test(param) || /^Index:/m.test(param)) { + return (/*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(param)[0] + ); + } + + if (!base) { + throw new Error('Must provide a base reference or pass in a patch'); + } + return (/*istanbul ignore start*/(0, _create.structuredPatch) /*istanbul ignore end*/(undefined, undefined, base, param) + ); + } + + return param; +} + +function fileNameChanged(patch) { + return patch.newFileName && patch.newFileName !== patch.oldFileName; +} + +function selectField(index, mine, theirs) { + if (mine === theirs) { + return mine; + } else { + index.conflict = true; + return { mine: mine, theirs: theirs }; + } +} + +function hunkBefore(test, check) { + return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; +} + +function cloneHunk(hunk, offset) { + return { + oldStart: hunk.oldStart, oldLines: hunk.oldLines, + newStart: hunk.newStart + offset, newLines: hunk.newLines, + lines: hunk.lines + }; +} + +function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { + // This will generally result in a conflicted hunk, but there are cases where the context + // is the only overlap where we can successfully merge the content here. + var mine = { offset: mineOffset, lines: mineLines, index: 0 }, + their = { offset: theirOffset, lines: theirLines, index: 0 }; + + // Handle any leading content + insertLeading(hunk, mine, their); + insertLeading(hunk, their, mine); + + // Now in the overlap content. Scan through and select the best changes from each. + while (mine.index < mine.lines.length && their.index < their.lines.length) { + var mineCurrent = mine.lines[mine.index], + theirCurrent = their.lines[their.index]; + + if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { + // Both modified ... + mutualChange(hunk, mine, their); + } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { + /*istanbul ignore start*/var _hunk$lines; + + /*istanbul ignore end*/ // Mine inserted + /*istanbul ignore start*/(_hunk$lines = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(mine))); + } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { + /*istanbul ignore start*/var _hunk$lines2; + + /*istanbul ignore end*/ // Theirs inserted + /*istanbul ignore start*/(_hunk$lines2 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(their))); + } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { + // Mine removed or edited + removal(hunk, mine, their); + } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') { + // Their removed or edited + removal(hunk, their, mine, true); + } else if (mineCurrent === theirCurrent) { + // Context identity + hunk.lines.push(mineCurrent); + mine.index++; + their.index++; + } else { + // Context mismatch + conflict(hunk, collectChange(mine), collectChange(their)); + } + } + + // Now push anything that may be remaining + insertTrailing(hunk, mine); + insertTrailing(hunk, their); + + calcLineCount(hunk); +} + +function mutualChange(hunk, mine, their) { + var myChanges = collectChange(mine), + theirChanges = collectChange(their); + + if (allRemoves(myChanges) && allRemoves(theirChanges)) { + // Special case for remove changes that are supersets of one another + if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { + /*istanbul ignore start*/var _hunk$lines3; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines3 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); + return; + } else if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { + /*istanbul ignore start*/var _hunk$lines4; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines4 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines4 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges)); + return; + } + } else if ( /*istanbul ignore start*/(0, _array.arrayEqual) /*istanbul ignore end*/(myChanges, theirChanges)) { + /*istanbul ignore start*/var _hunk$lines5; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines5 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines5 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); + return; + } + + conflict(hunk, myChanges, theirChanges); +} + +function removal(hunk, mine, their, swap) { + var myChanges = collectChange(mine), + theirChanges = collectContext(their, myChanges); + if (theirChanges.merged) { + /*istanbul ignore start*/var _hunk$lines6; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines6 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines6 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges.merged)); + } else { + conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); + } +} + +function conflict(hunk, mine, their) { + hunk.conflict = true; + hunk.lines.push({ + conflict: true, + mine: mine, + theirs: their + }); +} + +function insertLeading(hunk, insert, their) { + while (insert.offset < their.offset && insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + insert.offset++; + } +} +function insertTrailing(hunk, insert) { + while (insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + } +} + +function collectChange(state) { + var ret = [], + operation = state.lines[state.index][0]; + while (state.index < state.lines.length) { + var line = state.lines[state.index]; + + // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + if (operation === '-' && line[0] === '+') { + operation = '+'; + } + + if (operation === line[0]) { + ret.push(line); + state.index++; + } else { + break; + } + } + + return ret; +} +function collectContext(state, matchChanges) { + var changes = [], + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; + while (matchIndex < matchChanges.length && state.index < state.lines.length) { + var change = state.lines[state.index], + match = matchChanges[matchIndex]; + + // Once we've hit our add, then we are done + if (match[0] === '+') { + break; + } + + contextChanges = contextChanges || change[0] !== ' '; + + merged.push(match); + matchIndex++; + + // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this + if (change[0] === '+') { + conflicted = true; + + while (change[0] === '+') { + changes.push(change); + change = state.lines[++state.index]; + } + } + + if (match.substr(1) === change.substr(1)) { + changes.push(change); + state.index++; + } else { + conflicted = true; + } + } + + if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { + conflicted = true; + } + + if (conflicted) { + return changes; + } + + while (matchIndex < matchChanges.length) { + merged.push(matchChanges[matchIndex++]); + } + + return { + merged: merged, + changes: changes + }; +} + +function allRemoves(changes) { + return changes.reduce(function (prev, change) { + return prev && change[0] === '-'; + }, true); +} +function skipRemoveSuperset(state, removeChanges, delta) { + for (var i = 0; i < delta; i++) { + var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); + if (state.lines[state.index + i] !== ' ' + changeContent) { + return false; + } + } + + state.index += delta; + return true; +} + +function calcOldNewLineCount(lines) { + var oldLines = 0; + var newLines = 0; + + lines.forEach(function (line) { + if (typeof line !== 'string') { + var myCount = calcOldNewLineCount(line.mine); + var theirCount = calcOldNewLineCount(line.theirs); + + if (oldLines !== undefined) { + if (myCount.oldLines === theirCount.oldLines) { + oldLines += myCount.oldLines; + } else { + oldLines = undefined; + } + } + + if (newLines !== undefined) { + if (myCount.newLines === theirCount.newLines) { + newLines += myCount.newLines; + } else { + newLines = undefined; + } + } + } else { + if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { + newLines++; + } + if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { + oldLines++; + } + } + }); + + return { oldLines: oldLines, newLines: newLines }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9tZXJnZS5qcyJdLCJuYW1lcyI6WyJjYWxjTGluZUNvdW50IiwibWVyZ2UiLCJodW5rIiwiY2FsY09sZE5ld0xpbmVDb3VudCIsImxpbmVzIiwib2xkTGluZXMiLCJuZXdMaW5lcyIsInVuZGVmaW5lZCIsIm1pbmUiLCJ0aGVpcnMiLCJiYXNlIiwibG9hZFBhdGNoIiwicmV0IiwiaW5kZXgiLCJuZXdGaWxlTmFtZSIsImZpbGVOYW1lQ2hhbmdlZCIsIm9sZEZpbGVOYW1lIiwib2xkSGVhZGVyIiwibmV3SGVhZGVyIiwic2VsZWN0RmllbGQiLCJodW5rcyIsIm1pbmVJbmRleCIsInRoZWlyc0luZGV4IiwibWluZU9mZnNldCIsInRoZWlyc09mZnNldCIsImxlbmd0aCIsIm1pbmVDdXJyZW50Iiwib2xkU3RhcnQiLCJJbmZpbml0eSIsInRoZWlyc0N1cnJlbnQiLCJodW5rQmVmb3JlIiwicHVzaCIsImNsb25lSHVuayIsIm1lcmdlZEh1bmsiLCJNYXRoIiwibWluIiwibmV3U3RhcnQiLCJtZXJnZUxpbmVzIiwicGFyYW0iLCJ0ZXN0IiwiRXJyb3IiLCJwYXRjaCIsImNvbmZsaWN0IiwiY2hlY2siLCJvZmZzZXQiLCJtaW5lTGluZXMiLCJ0aGVpck9mZnNldCIsInRoZWlyTGluZXMiLCJ0aGVpciIsImluc2VydExlYWRpbmciLCJ0aGVpckN1cnJlbnQiLCJtdXR1YWxDaGFuZ2UiLCJjb2xsZWN0Q2hhbmdlIiwicmVtb3ZhbCIsImluc2VydFRyYWlsaW5nIiwibXlDaGFuZ2VzIiwidGhlaXJDaGFuZ2VzIiwiYWxsUmVtb3ZlcyIsInNraXBSZW1vdmVTdXBlcnNldCIsInN3YXAiLCJjb2xsZWN0Q29udGV4dCIsIm1lcmdlZCIsImluc2VydCIsImxpbmUiLCJzdGF0ZSIsIm9wZXJhdGlvbiIsIm1hdGNoQ2hhbmdlcyIsImNoYW5nZXMiLCJtYXRjaEluZGV4IiwiY29udGV4dENoYW5nZXMiLCJjb25mbGljdGVkIiwiY2hhbmdlIiwibWF0Y2giLCJzdWJzdHIiLCJyZWR1Y2UiLCJwcmV2IiwicmVtb3ZlQ2hhbmdlcyIsImRlbHRhIiwiaSIsImNoYW5nZUNvbnRlbnQiLCJmb3JFYWNoIiwibXlDb3VudCIsInRoZWlyQ291bnQiXSwibWFwcGluZ3MiOiI7OztnQ0FLZ0JBLGEsR0FBQUEsYTt5REFnQkFDLEssR0FBQUEsSzs7QUFyQmhCOztBQUNBOztBQUVBOzs7O3VCQUVPLFNBQVNELGFBQVQsQ0FBdUJFLElBQXZCLEVBQTZCO0FBQUEsNkVBQ0xDLG9CQUFvQkQsS0FBS0UsS0FBekIsQ0FESztBQUFBLE1BQzNCQyxRQUQyQix3QkFDM0JBLFFBRDJCO0FBQUEsTUFDakJDLFFBRGlCLHdCQUNqQkEsUUFEaUI7O0FBR2xDLE1BQUlELGFBQWFFLFNBQWpCLEVBQTRCO0FBQzFCTCxTQUFLRyxRQUFMLEdBQWdCQSxRQUFoQjtBQUNELEdBRkQsTUFFTztBQUNMLFdBQU9ILEtBQUtHLFFBQVo7QUFDRDs7QUFFRCxNQUFJQyxhQUFhQyxTQUFqQixFQUE0QjtBQUMxQkwsU0FBS0ksUUFBTCxHQUFnQkEsUUFBaEI7QUFDRCxHQUZELE1BRU87QUFDTCxXQUFPSixLQUFLSSxRQUFaO0FBQ0Q7QUFDRjs7QUFFTSxTQUFTTCxLQUFULENBQWVPLElBQWYsRUFBcUJDLE1BQXJCLEVBQTZCQyxJQUE3QixFQUFtQztBQUN4Q0YsU0FBT0csVUFBVUgsSUFBVixFQUFnQkUsSUFBaEIsQ0FBUDtBQUNBRCxXQUFTRSxVQUFVRixNQUFWLEVBQWtCQyxJQUFsQixDQUFUOztBQUVBLE1BQUlFLE1BQU0sRUFBVjs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxNQUFJSixLQUFLSyxLQUFMLElBQWNKLE9BQU9JLEtBQXpCLEVBQWdDO0FBQzlCRCxRQUFJQyxLQUFKLEdBQVlMLEtBQUtLLEtBQUwsSUFBY0osT0FBT0ksS0FBakM7QUFDRDs7QUFFRCxNQUFJTCxLQUFLTSxXQUFMLElBQW9CTCxPQUFPSyxXQUEvQixFQUE0QztBQUMxQyxRQUFJLENBQUNDLGdCQUFnQlAsSUFBaEIsQ0FBTCxFQUE0QjtBQUMxQjtBQUNBSSxVQUFJSSxXQUFKLEdBQWtCUCxPQUFPTyxXQUFQLElBQXNCUixLQUFLUSxXQUE3QztBQUNBSixVQUFJRSxXQUFKLEdBQWtCTCxPQUFPSyxXQUFQLElBQXNCTixLQUFLTSxXQUE3QztBQUNBRixVQUFJSyxTQUFKLEdBQWdCUixPQUFPUSxTQUFQLElBQW9CVCxLQUFLUyxTQUF6QztBQUNBTCxVQUFJTSxTQUFKLEdBQWdCVCxPQUFPUyxTQUFQLElBQW9CVixLQUFLVSxTQUF6QztBQUNELEtBTkQsTUFNTyxJQUFJLENBQUNILGdCQUFnQk4sTUFBaEIsQ0FBTCxFQUE4QjtBQUNuQztBQUNBRyxVQUFJSSxXQUFKLEdBQWtCUixLQUFLUSxXQUF2QjtBQUNBSixVQUFJRSxXQUFKLEdBQWtCTixLQUFLTSxXQUF2QjtBQUNBRixVQUFJSyxTQUFKLEdBQWdCVCxLQUFLUyxTQUFyQjtBQUNBTCxVQUFJTSxTQUFKLEdBQWdCVixLQUFLVSxTQUFyQjtBQUNELEtBTk0sTUFNQTtBQUNMO0FBQ0FOLFVBQUlJLFdBQUosR0FBa0JHLFlBQVlQLEdBQVosRUFBaUJKLEtBQUtRLFdBQXRCLEVBQW1DUCxPQUFPTyxXQUExQyxDQUFsQjtBQUNBSixVQUFJRSxXQUFKLEdBQWtCSyxZQUFZUCxHQUFaLEVBQWlCSixLQUFLTSxXQUF0QixFQUFtQ0wsT0FBT0ssV0FBMUMsQ0FBbEI7QUFDQUYsVUFBSUssU0FBSixHQUFnQkUsWUFBWVAsR0FBWixFQUFpQkosS0FBS1MsU0FBdEIsRUFBaUNSLE9BQU9RLFNBQXhDLENBQWhCO0FBQ0FMLFVBQUlNLFNBQUosR0FBZ0JDLFlBQVlQLEdBQVosRUFBaUJKLEtBQUtVLFNBQXRCLEVBQWlDVCxPQUFPUyxTQUF4QyxDQUFoQjtBQUNEO0FBQ0Y7O0FBRUROLE1BQUlRLEtBQUosR0FBWSxFQUFaOztBQUVBLE1BQUlDLFlBQVksQ0FBaEI7QUFBQSxNQUNJQyxjQUFjLENBRGxCO0FBQUEsTUFFSUMsYUFBYSxDQUZqQjtBQUFBLE1BR0lDLGVBQWUsQ0FIbkI7O0FBS0EsU0FBT0gsWUFBWWIsS0FBS1ksS0FBTCxDQUFXSyxNQUF2QixJQUFpQ0gsY0FBY2IsT0FBT1csS0FBUCxDQUFhSyxNQUFuRSxFQUEyRTtBQUN6RSxRQUFJQyxjQUFjbEIsS0FBS1ksS0FBTCxDQUFXQyxTQUFYLEtBQXlCLEVBQUNNLFVBQVVDLFFBQVgsRUFBM0M7QUFBQSxRQUNJQyxnQkFBZ0JwQixPQUFPVyxLQUFQLENBQWFFLFdBQWIsS0FBNkIsRUFBQ0ssVUFBVUMsUUFBWCxFQURqRDs7QUFHQSxRQUFJRSxXQUFXSixXQUFYLEVBQXdCRyxhQUF4QixDQUFKLEVBQTRDO0FBQzFDO0FBQ0FqQixVQUFJUSxLQUFKLENBQVVXLElBQVYsQ0FBZUMsVUFBVU4sV0FBVixFQUF1QkgsVUFBdkIsQ0FBZjtBQUNBRjtBQUNBRyxzQkFBZ0JFLFlBQVlwQixRQUFaLEdBQXVCb0IsWUFBWXJCLFFBQW5EO0FBQ0QsS0FMRCxNQUtPLElBQUl5QixXQUFXRCxhQUFYLEVBQTBCSCxXQUExQixDQUFKLEVBQTRDO0FBQ2pEO0FBQ0FkLFVBQUlRLEtBQUosQ0FBVVcsSUFBVixDQUFlQyxVQUFVSCxhQUFWLEVBQXlCTCxZQUF6QixDQUFmO0FBQ0FGO0FBQ0FDLG9CQUFjTSxjQUFjdkIsUUFBZCxHQUF5QnVCLGNBQWN4QixRQUFyRDtBQUNELEtBTE0sTUFLQTtBQUNMO0FBQ0EsVUFBSTRCLGFBQWE7QUFDZk4sa0JBQVVPLEtBQUtDLEdBQUwsQ0FBU1QsWUFBWUMsUUFBckIsRUFBK0JFLGNBQWNGLFFBQTdDLENBREs7QUFFZnRCLGtCQUFVLENBRks7QUFHZitCLGtCQUFVRixLQUFLQyxHQUFMLENBQVNULFlBQVlVLFFBQVosR0FBdUJiLFVBQWhDLEVBQTRDTSxjQUFjRixRQUFkLEdBQXlCSCxZQUFyRSxDQUhLO0FBSWZsQixrQkFBVSxDQUpLO0FBS2ZGLGVBQU87QUFMUSxPQUFqQjtBQU9BaUMsaUJBQVdKLFVBQVgsRUFBdUJQLFlBQVlDLFFBQW5DLEVBQTZDRCxZQUFZdEIsS0FBekQsRUFBZ0V5QixjQUFjRixRQUE5RSxFQUF3RkUsY0FBY3pCLEtBQXRHO0FBQ0FrQjtBQUNBRDs7QUFFQVQsVUFBSVEsS0FBSixDQUFVVyxJQUFWLENBQWVFLFVBQWY7QUFDRDtBQUNGOztBQUVELFNBQU9yQixHQUFQO0FBQ0Q7O0FBRUQsU0FBU0QsU0FBVCxDQUFtQjJCLEtBQW5CLEVBQTBCNUIsSUFBMUIsRUFBZ0M7QUFDOUIsTUFBSSxPQUFPNEIsS0FBUCxLQUFpQixRQUFyQixFQUErQjtBQUM3QixRQUFJLE9BQU9DLElBQVAsQ0FBWUQsS0FBWixLQUF1QixXQUFXQyxJQUFYLENBQWdCRCxLQUFoQixDQUEzQixFQUFvRDtBQUNsRCxhQUFPLHlFQUFXQSxLQUFYLEVBQWtCLENBQWxCO0FBQVA7QUFDRDs7QUFFRCxRQUFJLENBQUM1QixJQUFMLEVBQVc7QUFDVCxZQUFNLElBQUk4QixLQUFKLENBQVUsa0RBQVYsQ0FBTjtBQUNEO0FBQ0QsV0FBTywrRUFBZ0JqQyxTQUFoQixFQUEyQkEsU0FBM0IsRUFBc0NHLElBQXRDLEVBQTRDNEIsS0FBNUM7QUFBUDtBQUNEOztBQUVELFNBQU9BLEtBQVA7QUFDRDs7QUFFRCxTQUFTdkIsZUFBVCxDQUF5QjBCLEtBQXpCLEVBQWdDO0FBQzlCLFNBQU9BLE1BQU0zQixXQUFOLElBQXFCMkIsTUFBTTNCLFdBQU4sS0FBc0IyQixNQUFNekIsV0FBeEQ7QUFDRDs7QUFFRCxTQUFTRyxXQUFULENBQXFCTixLQUFyQixFQUE0QkwsSUFBNUIsRUFBa0NDLE1BQWxDLEVBQTBDO0FBQ3hDLE1BQUlELFNBQVNDLE1BQWIsRUFBcUI7QUFDbkIsV0FBT0QsSUFBUDtBQUNELEdBRkQsTUFFTztBQUNMSyxVQUFNNkIsUUFBTixHQUFpQixJQUFqQjtBQUNBLFdBQU8sRUFBQ2xDLFVBQUQsRUFBT0MsY0FBUCxFQUFQO0FBQ0Q7QUFDRjs7QUFFRCxTQUFTcUIsVUFBVCxDQUFvQlMsSUFBcEIsRUFBMEJJLEtBQTFCLEVBQWlDO0FBQy9CLFNBQU9KLEtBQUtaLFFBQUwsR0FBZ0JnQixNQUFNaEIsUUFBdEIsSUFDRFksS0FBS1osUUFBTCxHQUFnQlksS0FBS2xDLFFBQXRCLEdBQWtDc0MsTUFBTWhCLFFBRDdDO0FBRUQ7O0FBRUQsU0FBU0ssU0FBVCxDQUFtQjlCLElBQW5CLEVBQXlCMEMsTUFBekIsRUFBaUM7QUFDL0IsU0FBTztBQUNMakIsY0FBVXpCLEtBQUt5QixRQURWLEVBQ29CdEIsVUFBVUgsS0FBS0csUUFEbkM7QUFFTCtCLGNBQVVsQyxLQUFLa0MsUUFBTCxHQUFnQlEsTUFGckIsRUFFNkJ0QyxVQUFVSixLQUFLSSxRQUY1QztBQUdMRixXQUFPRixLQUFLRTtBQUhQLEdBQVA7QUFLRDs7QUFFRCxTQUFTaUMsVUFBVCxDQUFvQm5DLElBQXBCLEVBQTBCcUIsVUFBMUIsRUFBc0NzQixTQUF0QyxFQUFpREMsV0FBakQsRUFBOERDLFVBQTlELEVBQTBFO0FBQ3hFO0FBQ0E7QUFDQSxNQUFJdkMsT0FBTyxFQUFDb0MsUUFBUXJCLFVBQVQsRUFBcUJuQixPQUFPeUMsU0FBNUIsRUFBdUNoQyxPQUFPLENBQTlDLEVBQVg7QUFBQSxNQUNJbUMsUUFBUSxFQUFDSixRQUFRRSxXQUFULEVBQXNCMUMsT0FBTzJDLFVBQTdCLEVBQXlDbEMsT0FBTyxDQUFoRCxFQURaOztBQUdBO0FBQ0FvQyxnQkFBYy9DLElBQWQsRUFBb0JNLElBQXBCLEVBQTBCd0MsS0FBMUI7QUFDQUMsZ0JBQWMvQyxJQUFkLEVBQW9COEMsS0FBcEIsRUFBMkJ4QyxJQUEzQjs7QUFFQTtBQUNBLFNBQU9BLEtBQUtLLEtBQUwsR0FBYUwsS0FBS0osS0FBTCxDQUFXcUIsTUFBeEIsSUFBa0N1QixNQUFNbkMsS0FBTixHQUFjbUMsTUFBTTVDLEtBQU4sQ0FBWXFCLE1BQW5FLEVBQTJFO0FBQ3pFLFFBQUlDLGNBQWNsQixLQUFLSixLQUFMLENBQVdJLEtBQUtLLEtBQWhCLENBQWxCO0FBQUEsUUFDSXFDLGVBQWVGLE1BQU01QyxLQUFOLENBQVk0QyxNQUFNbkMsS0FBbEIsQ0FEbkI7O0FBR0EsUUFBSSxDQUFDYSxZQUFZLENBQVosTUFBbUIsR0FBbkIsSUFBMEJBLFlBQVksQ0FBWixNQUFtQixHQUE5QyxNQUNJd0IsYUFBYSxDQUFiLE1BQW9CLEdBQXBCLElBQTJCQSxhQUFhLENBQWIsTUFBb0IsR0FEbkQsQ0FBSixFQUM2RDtBQUMzRDtBQUNBQyxtQkFBYWpELElBQWIsRUFBbUJNLElBQW5CLEVBQXlCd0MsS0FBekI7QUFDRCxLQUpELE1BSU8sSUFBSXRCLFlBQVksQ0FBWixNQUFtQixHQUFuQixJQUEwQndCLGFBQWEsQ0FBYixNQUFvQixHQUFsRCxFQUF1RDtBQUFBOztBQUFBLDhCQUM1RDtBQUNBLDBFQUFLOUMsS0FBTCxFQUFXMkIsSUFBWCw0TEFBb0JxQixjQUFjNUMsSUFBZCxDQUFwQjtBQUNELEtBSE0sTUFHQSxJQUFJMEMsYUFBYSxDQUFiLE1BQW9CLEdBQXBCLElBQTJCeEIsWUFBWSxDQUFaLE1BQW1CLEdBQWxELEVBQXVEO0FBQUE7O0FBQUEsOEJBQzVEO0FBQ0EsMkVBQUt0QixLQUFMLEVBQVcyQixJQUFYLDZMQUFvQnFCLGNBQWNKLEtBQWQsQ0FBcEI7QUFDRCxLQUhNLE1BR0EsSUFBSXRCLFlBQVksQ0FBWixNQUFtQixHQUFuQixJQUEwQndCLGFBQWEsQ0FBYixNQUFvQixHQUFsRCxFQUF1RDtBQUM1RDtBQUNBRyxjQUFRbkQsSUFBUixFQUFjTSxJQUFkLEVBQW9Cd0MsS0FBcEI7QUFDRCxLQUhNLE1BR0EsSUFBSUUsYUFBYSxDQUFiLE1BQW9CLEdBQXBCLElBQTJCeEIsWUFBWSxDQUFaLE1BQW1CLEdBQWxELEVBQXVEO0FBQzVEO0FBQ0EyQixjQUFRbkQsSUFBUixFQUFjOEMsS0FBZCxFQUFxQnhDLElBQXJCLEVBQTJCLElBQTNCO0FBQ0QsS0FITSxNQUdBLElBQUlrQixnQkFBZ0J3QixZQUFwQixFQUFrQztBQUN2QztBQUNBaEQsV0FBS0UsS0FBTCxDQUFXMkIsSUFBWCxDQUFnQkwsV0FBaEI7QUFDQWxCLFdBQUtLLEtBQUw7QUFDQW1DLFlBQU1uQyxLQUFOO0FBQ0QsS0FMTSxNQUtBO0FBQ0w7QUFDQTZCLGVBQVN4QyxJQUFULEVBQWVrRCxjQUFjNUMsSUFBZCxDQUFmLEVBQW9DNEMsY0FBY0osS0FBZCxDQUFwQztBQUNEO0FBQ0Y7O0FBRUQ7QUFDQU0saUJBQWVwRCxJQUFmLEVBQXFCTSxJQUFyQjtBQUNBOEMsaUJBQWVwRCxJQUFmLEVBQXFCOEMsS0FBckI7O0FBRUFoRCxnQkFBY0UsSUFBZDtBQUNEOztBQUVELFNBQVNpRCxZQUFULENBQXNCakQsSUFBdEIsRUFBNEJNLElBQTVCLEVBQWtDd0MsS0FBbEMsRUFBeUM7QUFDdkMsTUFBSU8sWUFBWUgsY0FBYzVDLElBQWQsQ0FBaEI7QUFBQSxNQUNJZ0QsZUFBZUosY0FBY0osS0FBZCxDQURuQjs7QUFHQSxNQUFJUyxXQUFXRixTQUFYLEtBQXlCRSxXQUFXRCxZQUFYLENBQTdCLEVBQXVEO0FBQ3JEO0FBQ0EsUUFBSSw4RUFBZ0JELFNBQWhCLEVBQTJCQyxZQUEzQixLQUNHRSxtQkFBbUJWLEtBQW5CLEVBQTBCTyxTQUExQixFQUFxQ0EsVUFBVTlCLE1BQVYsR0FBbUIrQixhQUFhL0IsTUFBckUsQ0FEUCxFQUNxRjtBQUFBOztBQUFBLDZCQUNuRixzRUFBS3JCLEtBQUwsRUFBVzJCLElBQVgsNkxBQW9Cd0IsU0FBcEI7QUFDQTtBQUNELEtBSkQsTUFJTyxJQUFJLDhFQUFnQkMsWUFBaEIsRUFBOEJELFNBQTlCLEtBQ0pHLG1CQUFtQmxELElBQW5CLEVBQXlCZ0QsWUFBekIsRUFBdUNBLGFBQWEvQixNQUFiLEdBQXNCOEIsVUFBVTlCLE1BQXZFLENBREEsRUFDZ0Y7QUFBQTs7QUFBQSw2QkFDckYsc0VBQUtyQixLQUFMLEVBQVcyQixJQUFYLDZMQUFvQnlCLFlBQXBCO0FBQ0E7QUFDRDtBQUNGLEdBWEQsTUFXTyxJQUFJLHlFQUFXRCxTQUFYLEVBQXNCQyxZQUF0QixDQUFKLEVBQXlDO0FBQUE7O0FBQUEsMkJBQzlDLHNFQUFLcEQsS0FBTCxFQUFXMkIsSUFBWCw2TEFBb0J3QixTQUFwQjtBQUNBO0FBQ0Q7O0FBRURiLFdBQVN4QyxJQUFULEVBQWVxRCxTQUFmLEVBQTBCQyxZQUExQjtBQUNEOztBQUVELFNBQVNILE9BQVQsQ0FBaUJuRCxJQUFqQixFQUF1Qk0sSUFBdkIsRUFBNkJ3QyxLQUE3QixFQUFvQ1csSUFBcEMsRUFBMEM7QUFDeEMsTUFBSUosWUFBWUgsY0FBYzVDLElBQWQsQ0FBaEI7QUFBQSxNQUNJZ0QsZUFBZUksZUFBZVosS0FBZixFQUFzQk8sU0FBdEIsQ0FEbkI7QUFFQSxNQUFJQyxhQUFhSyxNQUFqQixFQUF5QjtBQUFBOztBQUFBLDJCQUN2QixzRUFBS3pELEtBQUwsRUFBVzJCLElBQVgsNkxBQW9CeUIsYUFBYUssTUFBakM7QUFDRCxHQUZELE1BRU87QUFDTG5CLGFBQVN4QyxJQUFULEVBQWV5RCxPQUFPSCxZQUFQLEdBQXNCRCxTQUFyQyxFQUFnREksT0FBT0osU0FBUCxHQUFtQkMsWUFBbkU7QUFDRDtBQUNGOztBQUVELFNBQVNkLFFBQVQsQ0FBa0J4QyxJQUFsQixFQUF3Qk0sSUFBeEIsRUFBOEJ3QyxLQUE5QixFQUFxQztBQUNuQzlDLE9BQUt3QyxRQUFMLEdBQWdCLElBQWhCO0FBQ0F4QyxPQUFLRSxLQUFMLENBQVcyQixJQUFYLENBQWdCO0FBQ2RXLGNBQVUsSUFESTtBQUVkbEMsVUFBTUEsSUFGUTtBQUdkQyxZQUFRdUM7QUFITSxHQUFoQjtBQUtEOztBQUVELFNBQVNDLGFBQVQsQ0FBdUIvQyxJQUF2QixFQUE2QjRELE1BQTdCLEVBQXFDZCxLQUFyQyxFQUE0QztBQUMxQyxTQUFPYyxPQUFPbEIsTUFBUCxHQUFnQkksTUFBTUosTUFBdEIsSUFBZ0NrQixPQUFPakQsS0FBUCxHQUFlaUQsT0FBTzFELEtBQVAsQ0FBYXFCLE1BQW5FLEVBQTJFO0FBQ3pFLFFBQUlzQyxPQUFPRCxPQUFPMUQsS0FBUCxDQUFhMEQsT0FBT2pELEtBQVAsRUFBYixDQUFYO0FBQ0FYLFNBQUtFLEtBQUwsQ0FBVzJCLElBQVgsQ0FBZ0JnQyxJQUFoQjtBQUNBRCxXQUFPbEIsTUFBUDtBQUNEO0FBQ0Y7QUFDRCxTQUFTVSxjQUFULENBQXdCcEQsSUFBeEIsRUFBOEI0RCxNQUE5QixFQUFzQztBQUNwQyxTQUFPQSxPQUFPakQsS0FBUCxHQUFlaUQsT0FBTzFELEtBQVAsQ0FBYXFCLE1BQW5DLEVBQTJDO0FBQ3pDLFFBQUlzQyxPQUFPRCxPQUFPMUQsS0FBUCxDQUFhMEQsT0FBT2pELEtBQVAsRUFBYixDQUFYO0FBQ0FYLFNBQUtFLEtBQUwsQ0FBVzJCLElBQVgsQ0FBZ0JnQyxJQUFoQjtBQUNEO0FBQ0Y7O0FBRUQsU0FBU1gsYUFBVCxDQUF1QlksS0FBdkIsRUFBOEI7QUFDNUIsTUFBSXBELE1BQU0sRUFBVjtBQUFBLE1BQ0lxRCxZQUFZRCxNQUFNNUQsS0FBTixDQUFZNEQsTUFBTW5ELEtBQWxCLEVBQXlCLENBQXpCLENBRGhCO0FBRUEsU0FBT21ELE1BQU1uRCxLQUFOLEdBQWNtRCxNQUFNNUQsS0FBTixDQUFZcUIsTUFBakMsRUFBeUM7QUFDdkMsUUFBSXNDLE9BQU9DLE1BQU01RCxLQUFOLENBQVk0RCxNQUFNbkQsS0FBbEIsQ0FBWDs7QUFFQTtBQUNBLFFBQUlvRCxjQUFjLEdBQWQsSUFBcUJGLEtBQUssQ0FBTCxNQUFZLEdBQXJDLEVBQTBDO0FBQ3hDRSxrQkFBWSxHQUFaO0FBQ0Q7O0FBRUQsUUFBSUEsY0FBY0YsS0FBSyxDQUFMLENBQWxCLEVBQTJCO0FBQ3pCbkQsVUFBSW1CLElBQUosQ0FBU2dDLElBQVQ7QUFDQUMsWUFBTW5ELEtBQU47QUFDRCxLQUhELE1BR087QUFDTDtBQUNEO0FBQ0Y7O0FBRUQsU0FBT0QsR0FBUDtBQUNEO0FBQ0QsU0FBU2dELGNBQVQsQ0FBd0JJLEtBQXhCLEVBQStCRSxZQUEvQixFQUE2QztBQUMzQyxNQUFJQyxVQUFVLEVBQWQ7QUFBQSxNQUNJTixTQUFTLEVBRGI7QUFBQSxNQUVJTyxhQUFhLENBRmpCO0FBQUEsTUFHSUMsaUJBQWlCLEtBSHJCO0FBQUEsTUFJSUMsYUFBYSxLQUpqQjtBQUtBLFNBQU9GLGFBQWFGLGFBQWF6QyxNQUExQixJQUNFdUMsTUFBTW5ELEtBQU4sR0FBY21ELE1BQU01RCxLQUFOLENBQVlxQixNQURuQyxFQUMyQztBQUN6QyxRQUFJOEMsU0FBU1AsTUFBTTVELEtBQU4sQ0FBWTRELE1BQU1uRCxLQUFsQixDQUFiO0FBQUEsUUFDSTJELFFBQVFOLGFBQWFFLFVBQWIsQ0FEWjs7QUFHQTtBQUNBLFFBQUlJLE1BQU0sQ0FBTixNQUFhLEdBQWpCLEVBQXNCO0FBQ3BCO0FBQ0Q7O0FBRURILHFCQUFpQkEsa0JBQWtCRSxPQUFPLENBQVAsTUFBYyxHQUFqRDs7QUFFQVYsV0FBTzlCLElBQVAsQ0FBWXlDLEtBQVo7QUFDQUo7O0FBRUE7QUFDQTtBQUNBLFFBQUlHLE9BQU8sQ0FBUCxNQUFjLEdBQWxCLEVBQXVCO0FBQ3JCRCxtQkFBYSxJQUFiOztBQUVBLGFBQU9DLE9BQU8sQ0FBUCxNQUFjLEdBQXJCLEVBQTBCO0FBQ3hCSixnQkFBUXBDLElBQVIsQ0FBYXdDLE1BQWI7QUFDQUEsaUJBQVNQLE1BQU01RCxLQUFOLENBQVksRUFBRTRELE1BQU1uRCxLQUFwQixDQUFUO0FBQ0Q7QUFDRjs7QUFFRCxRQUFJMkQsTUFBTUMsTUFBTixDQUFhLENBQWIsTUFBb0JGLE9BQU9FLE1BQVAsQ0FBYyxDQUFkLENBQXhCLEVBQTBDO0FBQ3hDTixjQUFRcEMsSUFBUixDQUFhd0MsTUFBYjtBQUNBUCxZQUFNbkQsS0FBTjtBQUNELEtBSEQsTUFHTztBQUNMeUQsbUJBQWEsSUFBYjtBQUNEO0FBQ0Y7O0FBRUQsTUFBSSxDQUFDSixhQUFhRSxVQUFiLEtBQTRCLEVBQTdCLEVBQWlDLENBQWpDLE1BQXdDLEdBQXhDLElBQ0dDLGNBRFAsRUFDdUI7QUFDckJDLGlCQUFhLElBQWI7QUFDRDs7QUFFRCxNQUFJQSxVQUFKLEVBQWdCO0FBQ2QsV0FBT0gsT0FBUDtBQUNEOztBQUVELFNBQU9DLGFBQWFGLGFBQWF6QyxNQUFqQyxFQUF5QztBQUN2Q29DLFdBQU85QixJQUFQLENBQVltQyxhQUFhRSxZQUFiLENBQVo7QUFDRDs7QUFFRCxTQUFPO0FBQ0xQLGtCQURLO0FBRUxNO0FBRkssR0FBUDtBQUlEOztBQUVELFNBQVNWLFVBQVQsQ0FBb0JVLE9BQXBCLEVBQTZCO0FBQzNCLFNBQU9BLFFBQVFPLE1BQVIsQ0FBZSxVQUFTQyxJQUFULEVBQWVKLE1BQWYsRUFBdUI7QUFDM0MsV0FBT0ksUUFBUUosT0FBTyxDQUFQLE1BQWMsR0FBN0I7QUFDRCxHQUZNLEVBRUosSUFGSSxDQUFQO0FBR0Q7QUFDRCxTQUFTYixrQkFBVCxDQUE0Qk0sS0FBNUIsRUFBbUNZLGFBQW5DLEVBQWtEQyxLQUFsRCxFQUF5RDtBQUN2RCxPQUFLLElBQUlDLElBQUksQ0FBYixFQUFnQkEsSUFBSUQsS0FBcEIsRUFBMkJDLEdBQTNCLEVBQWdDO0FBQzlCLFFBQUlDLGdCQUFnQkgsY0FBY0EsY0FBY25ELE1BQWQsR0FBdUJvRCxLQUF2QixHQUErQkMsQ0FBN0MsRUFBZ0RMLE1BQWhELENBQXVELENBQXZELENBQXBCO0FBQ0EsUUFBSVQsTUFBTTVELEtBQU4sQ0FBWTRELE1BQU1uRCxLQUFOLEdBQWNpRSxDQUExQixNQUFpQyxNQUFNQyxhQUEzQyxFQUEwRDtBQUN4RCxhQUFPLEtBQVA7QUFDRDtBQUNGOztBQUVEZixRQUFNbkQsS0FBTixJQUFlZ0UsS0FBZjtBQUNBLFNBQU8sSUFBUDtBQUNEOztBQUVELFNBQVMxRSxtQkFBVCxDQUE2QkMsS0FBN0IsRUFBb0M7QUFDbEMsTUFBSUMsV0FBVyxDQUFmO0FBQ0EsTUFBSUMsV0FBVyxDQUFmOztBQUVBRixRQUFNNEUsT0FBTixDQUFjLFVBQVNqQixJQUFULEVBQWU7QUFDM0IsUUFBSSxPQUFPQSxJQUFQLEtBQWdCLFFBQXBCLEVBQThCO0FBQzVCLFVBQUlrQixVQUFVOUUsb0JBQW9CNEQsS0FBS3ZELElBQXpCLENBQWQ7QUFDQSxVQUFJMEUsYUFBYS9FLG9CQUFvQjRELEtBQUt0RCxNQUF6QixDQUFqQjs7QUFFQSxVQUFJSixhQUFhRSxTQUFqQixFQUE0QjtBQUMxQixZQUFJMEUsUUFBUTVFLFFBQVIsS0FBcUI2RSxXQUFXN0UsUUFBcEMsRUFBOEM7QUFDNUNBLHNCQUFZNEUsUUFBUTVFLFFBQXBCO0FBQ0QsU0FGRCxNQUVPO0FBQ0xBLHFCQUFXRSxTQUFYO0FBQ0Q7QUFDRjs7QUFFRCxVQUFJRCxhQUFhQyxTQUFqQixFQUE0QjtBQUMxQixZQUFJMEUsUUFBUTNFLFFBQVIsS0FBcUI0RSxXQUFXNUUsUUFBcEMsRUFBOEM7QUFDNUNBLHNCQUFZMkUsUUFBUTNFLFFBQXBCO0FBQ0QsU0FGRCxNQUVPO0FBQ0xBLHFCQUFXQyxTQUFYO0FBQ0Q7QUFDRjtBQUNGLEtBbkJELE1BbUJPO0FBQ0wsVUFBSUQsYUFBYUMsU0FBYixLQUEyQndELEtBQUssQ0FBTCxNQUFZLEdBQVosSUFBbUJBLEtBQUssQ0FBTCxNQUFZLEdBQTFELENBQUosRUFBb0U7QUFDbEV6RDtBQUNEO0FBQ0QsVUFBSUQsYUFBYUUsU0FBYixLQUEyQndELEtBQUssQ0FBTCxNQUFZLEdBQVosSUFBbUJBLEtBQUssQ0FBTCxNQUFZLEdBQTFELENBQUosRUFBb0U7QUFDbEUxRDtBQUNEO0FBQ0Y7QUFDRixHQTVCRDs7QUE4QkEsU0FBTyxFQUFDQSxrQkFBRCxFQUFXQyxrQkFBWCxFQUFQO0FBQ0QiLCJmaWxlIjoibWVyZ2UuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge3N0cnVjdHVyZWRQYXRjaH0gZnJvbSAnLi9jcmVhdGUnO1xuaW1wb3J0IHtwYXJzZVBhdGNofSBmcm9tICcuL3BhcnNlJztcblxuaW1wb3J0IHthcnJheUVxdWFsLCBhcnJheVN0YXJ0c1dpdGh9IGZyb20gJy4uL3V0aWwvYXJyYXknO1xuXG5leHBvcnQgZnVuY3Rpb24gY2FsY0xpbmVDb3VudChodW5rKSB7XG4gIGNvbnN0IHtvbGRMaW5lcywgbmV3TGluZXN9ID0gY2FsY09sZE5ld0xpbmVDb3VudChodW5rLmxpbmVzKTtcblxuICBpZiAob2xkTGluZXMgIT09IHVuZGVmaW5lZCkge1xuICAgIGh1bmsub2xkTGluZXMgPSBvbGRMaW5lcztcbiAgfSBlbHNlIHtcbiAgICBkZWxldGUgaHVuay5vbGRMaW5lcztcbiAgfVxuXG4gIGlmIChuZXdMaW5lcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgaHVuay5uZXdMaW5lcyA9IG5ld0xpbmVzO1xuICB9IGVsc2Uge1xuICAgIGRlbGV0ZSBodW5rLm5ld0xpbmVzO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZShtaW5lLCB0aGVpcnMsIGJhc2UpIHtcbiAgbWluZSA9IGxvYWRQYXRjaChtaW5lLCBiYXNlKTtcbiAgdGhlaXJzID0gbG9hZFBhdGNoKHRoZWlycywgYmFzZSk7XG5cbiAgbGV0IHJldCA9IHt9O1xuXG4gIC8vIEZvciBpbmRleCB3ZSBqdXN0IGxldCBpdCBwYXNzIHRocm91Z2ggYXMgaXQgZG9lc24ndCBoYXZlIGFueSBuZWNlc3NhcnkgbWVhbmluZy5cbiAgLy8gTGVhdmluZyBzYW5pdHkgY2hlY2tzIG9uIHRoaXMgdG8gdGhlIEFQSSBjb25zdW1lciB0aGF0IG1heSBrbm93IG1vcmUgYWJvdXQgdGhlXG4gIC8vIG1lYW5pbmcgaW4gdGhlaXIgb3duIGNvbnRleHQuXG4gIGlmIChtaW5lLmluZGV4IHx8IHRoZWlycy5pbmRleCkge1xuICAgIHJldC5pbmRleCA9IG1pbmUuaW5kZXggfHwgdGhlaXJzLmluZGV4O1xuICB9XG5cbiAgaWYgKG1pbmUubmV3RmlsZU5hbWUgfHwgdGhlaXJzLm5ld0ZpbGVOYW1lKSB7XG4gICAgaWYgKCFmaWxlTmFtZUNoYW5nZWQobWluZSkpIHtcbiAgICAgIC8vIE5vIGhlYWRlciBvciBubyBjaGFuZ2UgaW4gb3VycywgdXNlIHRoZWlycyAoYW5kIG91cnMgaWYgdGhlaXJzIGRvZXMgbm90IGV4aXN0KVxuICAgICAgcmV0Lm9sZEZpbGVOYW1lID0gdGhlaXJzLm9sZEZpbGVOYW1lIHx8IG1pbmUub2xkRmlsZU5hbWU7XG4gICAgICByZXQubmV3RmlsZU5hbWUgPSB0aGVpcnMubmV3RmlsZU5hbWUgfHwgbWluZS5uZXdGaWxlTmFtZTtcbiAgICAgIHJldC5vbGRIZWFkZXIgPSB0aGVpcnMub2xkSGVhZGVyIHx8IG1pbmUub2xkSGVhZGVyO1xuICAgICAgcmV0Lm5ld0hlYWRlciA9IHRoZWlycy5uZXdIZWFkZXIgfHwgbWluZS5uZXdIZWFkZXI7XG4gICAgfSBlbHNlIGlmICghZmlsZU5hbWVDaGFuZ2VkKHRoZWlycykpIHtcbiAgICAgIC8vIE5vIGhlYWRlciBvciBubyBjaGFuZ2UgaW4gdGhlaXJzLCB1c2Ugb3Vyc1xuICAgICAgcmV0Lm9sZEZpbGVOYW1lID0gbWluZS5vbGRGaWxlTmFtZTtcbiAgICAgIHJldC5uZXdGaWxlTmFtZSA9IG1pbmUubmV3RmlsZU5hbWU7XG4gICAgICByZXQub2xkSGVhZGVyID0gbWluZS5vbGRIZWFkZXI7XG4gICAgICByZXQubmV3SGVhZGVyID0gbWluZS5uZXdIZWFkZXI7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEJvdGggY2hhbmdlZC4uLiBmaWd1cmUgaXQgb3V0XG4gICAgICByZXQub2xkRmlsZU5hbWUgPSBzZWxlY3RGaWVsZChyZXQsIG1pbmUub2xkRmlsZU5hbWUsIHRoZWlycy5vbGRGaWxlTmFtZSk7XG4gICAgICByZXQubmV3RmlsZU5hbWUgPSBzZWxlY3RGaWVsZChyZXQsIG1pbmUubmV3RmlsZU5hbWUsIHRoZWlycy5uZXdGaWxlTmFtZSk7XG4gICAgICByZXQub2xkSGVhZGVyID0gc2VsZWN0RmllbGQocmV0LCBtaW5lLm9sZEhlYWRlciwgdGhlaXJzLm9sZEhlYWRlcik7XG4gICAgICByZXQubmV3SGVhZGVyID0gc2VsZWN0RmllbGQocmV0LCBtaW5lLm5ld0hlYWRlciwgdGhlaXJzLm5ld0hlYWRlcik7XG4gICAgfVxuICB9XG5cbiAgcmV0Lmh1bmtzID0gW107XG5cbiAgbGV0IG1pbmVJbmRleCA9IDAsXG4gICAgICB0aGVpcnNJbmRleCA9IDAsXG4gICAgICBtaW5lT2Zmc2V0ID0gMCxcbiAgICAgIHRoZWlyc09mZnNldCA9IDA7XG5cbiAgd2hpbGUgKG1pbmVJbmRleCA8IG1pbmUuaHVua3MubGVuZ3RoIHx8IHRoZWlyc0luZGV4IDwgdGhlaXJzLmh1bmtzLmxlbmd0aCkge1xuICAgIGxldCBtaW5lQ3VycmVudCA9IG1pbmUuaHVua3NbbWluZUluZGV4XSB8fCB7b2xkU3RhcnQ6IEluZmluaXR5fSxcbiAgICAgICAgdGhlaXJzQ3VycmVudCA9IHRoZWlycy5odW5rc1t0aGVpcnNJbmRleF0gfHwge29sZFN0YXJ0OiBJbmZpbml0eX07XG5cbiAgICBpZiAoaHVua0JlZm9yZShtaW5lQ3VycmVudCwgdGhlaXJzQ3VycmVudCkpIHtcbiAgICAgIC8vIFRoaXMgcGF0Y2ggZG9lcyBub3Qgb3ZlcmxhcCB3aXRoIGFueSBvZiB0aGUgb3RoZXJzLCB5YXkuXG4gICAgICByZXQuaHVua3MucHVzaChjbG9uZUh1bmsobWluZUN1cnJlbnQsIG1pbmVPZmZzZXQpKTtcbiAgICAgIG1pbmVJbmRleCsrO1xuICAgICAgdGhlaXJzT2Zmc2V0ICs9IG1pbmVDdXJyZW50Lm5ld0xpbmVzIC0gbWluZUN1cnJlbnQub2xkTGluZXM7XG4gICAgfSBlbHNlIGlmIChodW5rQmVmb3JlKHRoZWlyc0N1cnJlbnQsIG1pbmVDdXJyZW50KSkge1xuICAgICAgLy8gVGhpcyBwYXRjaCBkb2VzIG5vdCBvdmVybGFwIHdpdGggYW55IG9mIHRoZSBvdGhlcnMsIHlheS5cbiAgICAgIHJldC5odW5rcy5wdXNoKGNsb25lSHVuayh0aGVpcnNDdXJyZW50LCB0aGVpcnNPZmZzZXQpKTtcbiAgICAgIHRoZWlyc0luZGV4Kys7XG4gICAgICBtaW5lT2Zmc2V0ICs9IHRoZWlyc0N1cnJlbnQubmV3TGluZXMgLSB0aGVpcnNDdXJyZW50Lm9sZExpbmVzO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBPdmVybGFwLCBtZXJnZSBhcyBiZXN0IHdlIGNhblxuICAgICAgbGV0IG1lcmdlZEh1bmsgPSB7XG4gICAgICAgIG9sZFN0YXJ0OiBNYXRoLm1pbihtaW5lQ3VycmVudC5vbGRTdGFydCwgdGhlaXJzQ3VycmVudC5vbGRTdGFydCksXG4gICAgICAgIG9sZExpbmVzOiAwLFxuICAgICAgICBuZXdTdGFydDogTWF0aC5taW4obWluZUN1cnJlbnQubmV3U3RhcnQgKyBtaW5lT2Zmc2V0LCB0aGVpcnNDdXJyZW50Lm9sZFN0YXJ0ICsgdGhlaXJzT2Zmc2V0KSxcbiAgICAgICAgbmV3TGluZXM6IDAsXG4gICAgICAgIGxpbmVzOiBbXVxuICAgICAgfTtcbiAgICAgIG1lcmdlTGluZXMobWVyZ2VkSHVuaywgbWluZUN1cnJlbnQub2xkU3RhcnQsIG1pbmVDdXJyZW50LmxpbmVzLCB0aGVpcnNDdXJyZW50Lm9sZFN0YXJ0LCB0aGVpcnNDdXJyZW50LmxpbmVzKTtcbiAgICAgIHRoZWlyc0luZGV4Kys7XG4gICAgICBtaW5lSW5kZXgrKztcblxuICAgICAgcmV0Lmh1bmtzLnB1c2gobWVyZ2VkSHVuayk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHJldDtcbn1cblxuZnVuY3Rpb24gbG9hZFBhdGNoKHBhcmFtLCBiYXNlKSB7XG4gIGlmICh0eXBlb2YgcGFyYW0gPT09ICdzdHJpbmcnKSB7XG4gICAgaWYgKC9eQEAvbS50ZXN0KHBhcmFtKSB8fCAoL15JbmRleDovbS50ZXN0KHBhcmFtKSkpIHtcbiAgICAgIHJldHVybiBwYXJzZVBhdGNoKHBhcmFtKVswXTtcbiAgICB9XG5cbiAgICBpZiAoIWJhc2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTXVzdCBwcm92aWRlIGEgYmFzZSByZWZlcmVuY2Ugb3IgcGFzcyBpbiBhIHBhdGNoJyk7XG4gICAgfVxuICAgIHJldHVybiBzdHJ1Y3R1cmVkUGF0Y2godW5kZWZpbmVkLCB1bmRlZmluZWQsIGJhc2UsIHBhcmFtKTtcbiAgfVxuXG4gIHJldHVybiBwYXJhbTtcbn1cblxuZnVuY3Rpb24gZmlsZU5hbWVDaGFuZ2VkKHBhdGNoKSB7XG4gIHJldHVybiBwYXRjaC5uZXdGaWxlTmFtZSAmJiBwYXRjaC5uZXdGaWxlTmFtZSAhPT0gcGF0Y2gub2xkRmlsZU5hbWU7XG59XG5cbmZ1bmN0aW9uIHNlbGVjdEZpZWxkKGluZGV4LCBtaW5lLCB0aGVpcnMpIHtcbiAgaWYgKG1pbmUgPT09IHRoZWlycykge1xuICAgIHJldHVybiBtaW5lO1xuICB9IGVsc2Uge1xuICAgIGluZGV4LmNvbmZsaWN0ID0gdHJ1ZTtcbiAgICByZXR1cm4ge21pbmUsIHRoZWlyc307XG4gIH1cbn1cblxuZnVuY3Rpb24gaHVua0JlZm9yZSh0ZXN0LCBjaGVjaykge1xuICByZXR1cm4gdGVzdC5vbGRTdGFydCA8IGNoZWNrLm9sZFN0YXJ0XG4gICAgJiYgKHRlc3Qub2xkU3RhcnQgKyB0ZXN0Lm9sZExpbmVzKSA8IGNoZWNrLm9sZFN0YXJ0O1xufVxuXG5mdW5jdGlvbiBjbG9uZUh1bmsoaHVuaywgb2Zmc2V0KSB7XG4gIHJldHVybiB7XG4gICAgb2xkU3RhcnQ6IGh1bmsub2xkU3RhcnQsIG9sZExpbmVzOiBodW5rLm9sZExpbmVzLFxuICAgIG5ld1N0YXJ0OiBodW5rLm5ld1N0YXJ0ICsgb2Zmc2V0LCBuZXdMaW5lczogaHVuay5uZXdMaW5lcyxcbiAgICBsaW5lczogaHVuay5saW5lc1xuICB9O1xufVxuXG5mdW5jdGlvbiBtZXJnZUxpbmVzKGh1bmssIG1pbmVPZmZzZXQsIG1pbmVMaW5lcywgdGhlaXJPZmZzZXQsIHRoZWlyTGluZXMpIHtcbiAgLy8gVGhpcyB3aWxsIGdlbmVyYWxseSByZXN1bHQgaW4gYSBjb25mbGljdGVkIGh1bmssIGJ1dCB0aGVyZSBhcmUgY2FzZXMgd2hlcmUgdGhlIGNvbnRleHRcbiAgLy8gaXMgdGhlIG9ubHkgb3ZlcmxhcCB3aGVyZSB3ZSBjYW4gc3VjY2Vzc2Z1bGx5IG1lcmdlIHRoZSBjb250ZW50IGhlcmUuXG4gIGxldCBtaW5lID0ge29mZnNldDogbWluZU9mZnNldCwgbGluZXM6IG1pbmVMaW5lcywgaW5kZXg6IDB9LFxuICAgICAgdGhlaXIgPSB7b2Zmc2V0OiB0aGVpck9mZnNldCwgbGluZXM6IHRoZWlyTGluZXMsIGluZGV4OiAwfTtcblxuICAvLyBIYW5kbGUgYW55IGxlYWRpbmcgY29udGVudFxuICBpbnNlcnRMZWFkaW5nKGh1bmssIG1pbmUsIHRoZWlyKTtcbiAgaW5zZXJ0TGVhZGluZyhodW5rLCB0aGVpciwgbWluZSk7XG5cbiAgLy8gTm93IGluIHRoZSBvdmVybGFwIGNvbnRlbnQuIFNjYW4gdGhyb3VnaCBhbmQgc2VsZWN0IHRoZSBiZXN0IGNoYW5nZXMgZnJvbSBlYWNoLlxuICB3aGlsZSAobWluZS5pbmRleCA8IG1pbmUubGluZXMubGVuZ3RoICYmIHRoZWlyLmluZGV4IDwgdGhlaXIubGluZXMubGVuZ3RoKSB7XG4gICAgbGV0IG1pbmVDdXJyZW50ID0gbWluZS5saW5lc1ttaW5lLmluZGV4XSxcbiAgICAgICAgdGhlaXJDdXJyZW50ID0gdGhlaXIubGluZXNbdGhlaXIuaW5kZXhdO1xuXG4gICAgaWYgKChtaW5lQ3VycmVudFswXSA9PT0gJy0nIHx8IG1pbmVDdXJyZW50WzBdID09PSAnKycpXG4gICAgICAgICYmICh0aGVpckN1cnJlbnRbMF0gPT09ICctJyB8fCB0aGVpckN1cnJlbnRbMF0gPT09ICcrJykpIHtcbiAgICAgIC8vIEJvdGggbW9kaWZpZWQgLi4uXG4gICAgICBtdXR1YWxDaGFuZ2UoaHVuaywgbWluZSwgdGhlaXIpO1xuICAgIH0gZWxzZSBpZiAobWluZUN1cnJlbnRbMF0gPT09ICcrJyAmJiB0aGVpckN1cnJlbnRbMF0gPT09ICcgJykge1xuICAgICAgLy8gTWluZSBpbnNlcnRlZFxuICAgICAgaHVuay5saW5lcy5wdXNoKC4uLiBjb2xsZWN0Q2hhbmdlKG1pbmUpKTtcbiAgICB9IGVsc2UgaWYgKHRoZWlyQ3VycmVudFswXSA9PT0gJysnICYmIG1pbmVDdXJyZW50WzBdID09PSAnICcpIHtcbiAgICAgIC8vIFRoZWlycyBpbnNlcnRlZFxuICAgICAgaHVuay5saW5lcy5wdXNoKC4uLiBjb2xsZWN0Q2hhbmdlKHRoZWlyKSk7XG4gICAgfSBlbHNlIGlmIChtaW5lQ3VycmVudFswXSA9PT0gJy0nICYmIHRoZWlyQ3VycmVudFswXSA9PT0gJyAnKSB7XG4gICAgICAvLyBNaW5lIHJlbW92ZWQgb3IgZWRpdGVkXG4gICAgICByZW1vdmFsKGh1bmssIG1pbmUsIHRoZWlyKTtcbiAgICB9IGVsc2UgaWYgKHRoZWlyQ3VycmVudFswXSA9PT0gJy0nICYmIG1pbmVDdXJyZW50WzBdID09PSAnICcpIHtcbiAgICAgIC8vIFRoZWlyIHJlbW92ZWQgb3IgZWRpdGVkXG4gICAgICByZW1vdmFsKGh1bmssIHRoZWlyLCBtaW5lLCB0cnVlKTtcbiAgICB9IGVsc2UgaWYgKG1pbmVDdXJyZW50ID09PSB0aGVpckN1cnJlbnQpIHtcbiAgICAgIC8vIENvbnRleHQgaWRlbnRpdHlcbiAgICAgIGh1bmsubGluZXMucHVzaChtaW5lQ3VycmVudCk7XG4gICAgICBtaW5lLmluZGV4Kys7XG4gICAgICB0aGVpci5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDb250ZXh0IG1pc21hdGNoXG4gICAgICBjb25mbGljdChodW5rLCBjb2xsZWN0Q2hhbmdlKG1pbmUpLCBjb2xsZWN0Q2hhbmdlKHRoZWlyKSk7XG4gICAgfVxuICB9XG5cbiAgLy8gTm93IHB1c2ggYW55dGhpbmcgdGhhdCBtYXkgYmUgcmVtYWluaW5nXG4gIGluc2VydFRyYWlsaW5nKGh1bmssIG1pbmUpO1xuICBpbnNlcnRUcmFpbGluZyhodW5rLCB0aGVpcik7XG5cbiAgY2FsY0xpbmVDb3VudChodW5rKTtcbn1cblxuZnVuY3Rpb24gbXV0dWFsQ2hhbmdlKGh1bmssIG1pbmUsIHRoZWlyKSB7XG4gIGxldCBteUNoYW5nZXMgPSBjb2xsZWN0Q2hhbmdlKG1pbmUpLFxuICAgICAgdGhlaXJDaGFuZ2VzID0gY29sbGVjdENoYW5nZSh0aGVpcik7XG5cbiAgaWYgKGFsbFJlbW92ZXMobXlDaGFuZ2VzKSAmJiBhbGxSZW1vdmVzKHRoZWlyQ2hhbmdlcykpIHtcbiAgICAvLyBTcGVjaWFsIGNhc2UgZm9yIHJlbW92ZSBjaGFuZ2VzIHRoYXQgYXJlIHN1cGVyc2V0cyBvZiBvbmUgYW5vdGhlclxuICAgIGlmIChhcnJheVN0YXJ0c1dpdGgobXlDaGFuZ2VzLCB0aGVpckNoYW5nZXMpXG4gICAgICAgICYmIHNraXBSZW1vdmVTdXBlcnNldCh0aGVpciwgbXlDaGFuZ2VzLCBteUNoYW5nZXMubGVuZ3RoIC0gdGhlaXJDaGFuZ2VzLmxlbmd0aCkpIHtcbiAgICAgIGh1bmsubGluZXMucHVzaCguLi4gbXlDaGFuZ2VzKTtcbiAgICAgIHJldHVybjtcbiAgICB9IGVsc2UgaWYgKGFycmF5U3RhcnRzV2l0aCh0aGVpckNoYW5nZXMsIG15Q2hhbmdlcylcbiAgICAgICAgJiYgc2tpcFJlbW92ZVN1cGVyc2V0KG1pbmUsIHRoZWlyQ2hhbmdlcywgdGhlaXJDaGFuZ2VzLmxlbmd0aCAtIG15Q2hhbmdlcy5sZW5ndGgpKSB7XG4gICAgICBodW5rLmxpbmVzLnB1c2goLi4uIHRoZWlyQ2hhbmdlcyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICB9IGVsc2UgaWYgKGFycmF5RXF1YWwobXlDaGFuZ2VzLCB0aGVpckNoYW5nZXMpKSB7XG4gICAgaHVuay5saW5lcy5wdXNoKC4uLiBteUNoYW5nZXMpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbmZsaWN0KGh1bmssIG15Q2hhbmdlcywgdGhlaXJDaGFuZ2VzKTtcbn1cblxuZnVuY3Rpb24gcmVtb3ZhbChodW5rLCBtaW5lLCB0aGVpciwgc3dhcCkge1xuICBsZXQgbXlDaGFuZ2VzID0gY29sbGVjdENoYW5nZShtaW5lKSxcbiAgICAgIHRoZWlyQ2hhbmdlcyA9IGNvbGxlY3RDb250ZXh0KHRoZWlyLCBteUNoYW5nZXMpO1xuICBpZiAodGhlaXJDaGFuZ2VzLm1lcmdlZCkge1xuICAgIGh1bmsubGluZXMucHVzaCguLi4gdGhlaXJDaGFuZ2VzLm1lcmdlZCk7XG4gIH0gZWxzZSB7XG4gICAgY29uZmxpY3QoaHVuaywgc3dhcCA/IHRoZWlyQ2hhbmdlcyA6IG15Q2hhbmdlcywgc3dhcCA/IG15Q2hhbmdlcyA6IHRoZWlyQ2hhbmdlcyk7XG4gIH1cbn1cblxuZnVuY3Rpb24gY29uZmxpY3QoaHVuaywgbWluZSwgdGhlaXIpIHtcbiAgaHVuay5jb25mbGljdCA9IHRydWU7XG4gIGh1bmsubGluZXMucHVzaCh7XG4gICAgY29uZmxpY3Q6IHRydWUsXG4gICAgbWluZTogbWluZSxcbiAgICB0aGVpcnM6IHRoZWlyXG4gIH0pO1xufVxuXG5mdW5jdGlvbiBpbnNlcnRMZWFkaW5nKGh1bmssIGluc2VydCwgdGhlaXIpIHtcbiAgd2hpbGUgKGluc2VydC5vZmZzZXQgPCB0aGVpci5vZmZzZXQgJiYgaW5zZXJ0LmluZGV4IDwgaW5zZXJ0LmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBsaW5lID0gaW5zZXJ0LmxpbmVzW2luc2VydC5pbmRleCsrXTtcbiAgICBodW5rLmxpbmVzLnB1c2gobGluZSk7XG4gICAgaW5zZXJ0Lm9mZnNldCsrO1xuICB9XG59XG5mdW5jdGlvbiBpbnNlcnRUcmFpbGluZyhodW5rLCBpbnNlcnQpIHtcbiAgd2hpbGUgKGluc2VydC5pbmRleCA8IGluc2VydC5saW5lcy5sZW5ndGgpIHtcbiAgICBsZXQgbGluZSA9IGluc2VydC5saW5lc1tpbnNlcnQuaW5kZXgrK107XG4gICAgaHVuay5saW5lcy5wdXNoKGxpbmUpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGNvbGxlY3RDaGFuZ2Uoc3RhdGUpIHtcbiAgbGV0IHJldCA9IFtdLFxuICAgICAgb3BlcmF0aW9uID0gc3RhdGUubGluZXNbc3RhdGUuaW5kZXhdWzBdO1xuICB3aGlsZSAoc3RhdGUuaW5kZXggPCBzdGF0ZS5saW5lcy5sZW5ndGgpIHtcbiAgICBsZXQgbGluZSA9IHN0YXRlLmxpbmVzW3N0YXRlLmluZGV4XTtcblxuICAgIC8vIEdyb3VwIGFkZGl0aW9ucyB0aGF0IGFyZSBpbW1lZGlhdGVseSBhZnRlciBzdWJ0cmFjdGlvbnMgYW5kIHRyZWF0IHRoZW0gYXMgb25lIFwiYXRvbWljXCIgbW9kaWZ5IGNoYW5nZS5cbiAgICBpZiAob3BlcmF0aW9uID09PSAnLScgJiYgbGluZVswXSA9PT0gJysnKSB7XG4gICAgICBvcGVyYXRpb24gPSAnKyc7XG4gICAgfVxuXG4gICAgaWYgKG9wZXJhdGlvbiA9PT0gbGluZVswXSkge1xuICAgICAgcmV0LnB1c2gobGluZSk7XG4gICAgICBzdGF0ZS5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICBicmVhaztcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmV0O1xufVxuZnVuY3Rpb24gY29sbGVjdENvbnRleHQoc3RhdGUsIG1hdGNoQ2hhbmdlcykge1xuICBsZXQgY2hhbmdlcyA9IFtdLFxuICAgICAgbWVyZ2VkID0gW10sXG4gICAgICBtYXRjaEluZGV4ID0gMCxcbiAgICAgIGNvbnRleHRDaGFuZ2VzID0gZmFsc2UsXG4gICAgICBjb25mbGljdGVkID0gZmFsc2U7XG4gIHdoaWxlIChtYXRjaEluZGV4IDwgbWF0Y2hDaGFuZ2VzLmxlbmd0aFxuICAgICAgICAmJiBzdGF0ZS5pbmRleCA8IHN0YXRlLmxpbmVzLmxlbmd0aCkge1xuICAgIGxldCBjaGFuZ2UgPSBzdGF0ZS5saW5lc1tzdGF0ZS5pbmRleF0sXG4gICAgICAgIG1hdGNoID0gbWF0Y2hDaGFuZ2VzW21hdGNoSW5kZXhdO1xuXG4gICAgLy8gT25jZSB3ZSd2ZSBoaXQgb3VyIGFkZCwgdGhlbiB3ZSBhcmUgZG9uZVxuICAgIGlmIChtYXRjaFswXSA9PT0gJysnKSB7XG4gICAgICBicmVhaztcbiAgICB9XG5cbiAgICBjb250ZXh0Q2hhbmdlcyA9IGNvbnRleHRDaGFuZ2VzIHx8IGNoYW5nZVswXSAhPT0gJyAnO1xuXG4gICAgbWVyZ2VkLnB1c2gobWF0Y2gpO1xuICAgIG1hdGNoSW5kZXgrKztcblxuICAgIC8vIENvbnN1bWUgYW55IGFkZGl0aW9ucyBpbiB0aGUgb3RoZXIgYmxvY2sgYXMgYSBjb25mbGljdCB0byBhdHRlbXB0XG4gICAgLy8gdG8gcHVsbCBpbiB0aGUgcmVtYWluaW5nIGNvbnRleHQgYWZ0ZXIgdGhpc1xuICAgIGlmIChjaGFuZ2VbMF0gPT09ICcrJykge1xuICAgICAgY29uZmxpY3RlZCA9IHRydWU7XG5cbiAgICAgIHdoaWxlIChjaGFuZ2VbMF0gPT09ICcrJykge1xuICAgICAgICBjaGFuZ2VzLnB1c2goY2hhbmdlKTtcbiAgICAgICAgY2hhbmdlID0gc3RhdGUubGluZXNbKytzdGF0ZS5pbmRleF07XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG1hdGNoLnN1YnN0cigxKSA9PT0gY2hhbmdlLnN1YnN0cigxKSkge1xuICAgICAgY2hhbmdlcy5wdXNoKGNoYW5nZSk7XG4gICAgICBzdGF0ZS5pbmRleCsrO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25mbGljdGVkID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBpZiAoKG1hdGNoQ2hhbmdlc1ttYXRjaEluZGV4XSB8fCAnJylbMF0gPT09ICcrJ1xuICAgICAgJiYgY29udGV4dENoYW5nZXMpIHtcbiAgICBjb25mbGljdGVkID0gdHJ1ZTtcbiAgfVxuXG4gIGlmIChjb25mbGljdGVkKSB7XG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICB3aGlsZSAobWF0Y2hJbmRleCA8IG1hdGNoQ2hhbmdlcy5sZW5ndGgpIHtcbiAgICBtZXJnZWQucHVzaChtYXRjaENoYW5nZXNbbWF0Y2hJbmRleCsrXSk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIG1lcmdlZCxcbiAgICBjaGFuZ2VzXG4gIH07XG59XG5cbmZ1bmN0aW9uIGFsbFJlbW92ZXMoY2hhbmdlcykge1xuICByZXR1cm4gY2hhbmdlcy5yZWR1Y2UoZnVuY3Rpb24ocHJldiwgY2hhbmdlKSB7XG4gICAgcmV0dXJuIHByZXYgJiYgY2hhbmdlWzBdID09PSAnLSc7XG4gIH0sIHRydWUpO1xufVxuZnVuY3Rpb24gc2tpcFJlbW92ZVN1cGVyc2V0KHN0YXRlLCByZW1vdmVDaGFuZ2VzLCBkZWx0YSkge1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGRlbHRhOyBpKyspIHtcbiAgICBsZXQgY2hhbmdlQ29udGVudCA9IHJlbW92ZUNoYW5nZXNbcmVtb3ZlQ2hhbmdlcy5sZW5ndGggLSBkZWx0YSArIGldLnN1YnN0cigxKTtcbiAgICBpZiAoc3RhdGUubGluZXNbc3RhdGUuaW5kZXggKyBpXSAhPT0gJyAnICsgY2hhbmdlQ29udGVudCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHN0YXRlLmluZGV4ICs9IGRlbHRhO1xuICByZXR1cm4gdHJ1ZTtcbn1cblxuZnVuY3Rpb24gY2FsY09sZE5ld0xpbmVDb3VudChsaW5lcykge1xuICBsZXQgb2xkTGluZXMgPSAwO1xuICBsZXQgbmV3TGluZXMgPSAwO1xuXG4gIGxpbmVzLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgIGlmICh0eXBlb2YgbGluZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIGxldCBteUNvdW50ID0gY2FsY09sZE5ld0xpbmVDb3VudChsaW5lLm1pbmUpO1xuICAgICAgbGV0IHRoZWlyQ291bnQgPSBjYWxjT2xkTmV3TGluZUNvdW50KGxpbmUudGhlaXJzKTtcblxuICAgICAgaWYgKG9sZExpbmVzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgaWYgKG15Q291bnQub2xkTGluZXMgPT09IHRoZWlyQ291bnQub2xkTGluZXMpIHtcbiAgICAgICAgICBvbGRMaW5lcyArPSBteUNvdW50Lm9sZExpbmVzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9sZExpbmVzID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChuZXdMaW5lcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGlmIChteUNvdW50Lm5ld0xpbmVzID09PSB0aGVpckNvdW50Lm5ld0xpbmVzKSB7XG4gICAgICAgICAgbmV3TGluZXMgKz0gbXlDb3VudC5uZXdMaW5lcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBuZXdMaW5lcyA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAobmV3TGluZXMgIT09IHVuZGVmaW5lZCAmJiAobGluZVswXSA9PT0gJysnIHx8IGxpbmVbMF0gPT09ICcgJykpIHtcbiAgICAgICAgbmV3TGluZXMrKztcbiAgICAgIH1cbiAgICAgIGlmIChvbGRMaW5lcyAhPT0gdW5kZWZpbmVkICYmIChsaW5lWzBdID09PSAnLScgfHwgbGluZVswXSA9PT0gJyAnKSkge1xuICAgICAgICBvbGRMaW5lcysrO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIHtvbGRMaW5lcywgbmV3TGluZXN9O1xufVxuIl19 diff --git a/WebServer/node_modules/diff/lib/patch/parse.js b/WebServer/node_modules/diff/lib/patch/parse.js new file mode 100644 index 000000000..e5f17301a --- /dev/null +++ b/WebServer/node_modules/diff/lib/patch/parse.js @@ -0,0 +1,147 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/parsePatch = parsePatch; +function parsePatch(uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], + list = [], + i = 0; + + function parseIndex() { + var index = {}; + list.push(index); + + // Parse diff metadata + while (i < diffstr.length) { + var line = diffstr[i]; + + // File header found, end parsing diff metadata + if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { + break; + } + + // Diff index + var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); + if (header) { + index.index = header[1]; + } + + i++; + } + + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); + parseFileHeader(index); + + // Parse hunks + index.hunks = []; + + while (i < diffstr.length) { + var _line = diffstr[i]; + + if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + break; + } else if (/^@@/.test(_line)) { + index.hunks.push(parseHunk()); + } else if (_line && options.strict) { + // Ignore unexpected content unless in strict mode + throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); + } else { + i++; + } + } + } + + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. + function parseFileHeader(index) { + var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); + if (fileHeader) { + var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; + var data = fileHeader[2].split('\t', 2); + var fileName = data[0].replace(/\\\\/g, '\\'); + if (/^".*"$/.test(fileName)) { + fileName = fileName.substr(1, fileName.length - 2); + } + index[keyPrefix + 'FileName'] = fileName; + index[keyPrefix + 'Header'] = (data[1] || '').trim(); + + i++; + } + } + + // Parses a hunk + // This assumes that we are at the start of a hunk. + function parseHunk() { + var chunkHeaderIndex = i, + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + + var hunk = { + oldStart: +chunkHeader[1], + oldLines: +chunkHeader[2] || 1, + newStart: +chunkHeader[3], + newLines: +chunkHeader[4] || 1, + lines: [], + linedelimiters: [] + }; + + var addCount = 0, + removeCount = 0; + for (; i < diffstr.length; i++) { + // Lines starting with '---' could be mistaken for the "remove line" operation + // But they could be the header for the next file. Therefore prune such cases out. + if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { + break; + } + var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; + + if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { + hunk.lines.push(diffstr[i]); + hunk.linedelimiters.push(delimiters[i] || '\n'); + + if (operation === '+') { + addCount++; + } else if (operation === '-') { + removeCount++; + } else if (operation === ' ') { + addCount++; + removeCount++; + } + } else { + break; + } + } + + // Handle the empty block count case + if (!addCount && hunk.newLines === 1) { + hunk.newLines = 0; + } + if (!removeCount && hunk.oldLines === 1) { + hunk.oldLines = 0; + } + + // Perform optional sanity checking + if (options.strict) { + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + } + + return hunk; + } + + while (i < diffstr.length) { + parseIndex(); + } + + return list; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXRjaC9wYXJzZS5qcyJdLCJuYW1lcyI6WyJwYXJzZVBhdGNoIiwidW5pRGlmZiIsIm9wdGlvbnMiLCJkaWZmc3RyIiwic3BsaXQiLCJkZWxpbWl0ZXJzIiwibWF0Y2giLCJsaXN0IiwiaSIsInBhcnNlSW5kZXgiLCJpbmRleCIsInB1c2giLCJsZW5ndGgiLCJsaW5lIiwidGVzdCIsImhlYWRlciIsImV4ZWMiLCJwYXJzZUZpbGVIZWFkZXIiLCJodW5rcyIsInBhcnNlSHVuayIsInN0cmljdCIsIkVycm9yIiwiSlNPTiIsInN0cmluZ2lmeSIsImZpbGVIZWFkZXIiLCJrZXlQcmVmaXgiLCJkYXRhIiwiZmlsZU5hbWUiLCJyZXBsYWNlIiwic3Vic3RyIiwidHJpbSIsImNodW5rSGVhZGVySW5kZXgiLCJjaHVua0hlYWRlckxpbmUiLCJjaHVua0hlYWRlciIsImh1bmsiLCJvbGRTdGFydCIsIm9sZExpbmVzIiwibmV3U3RhcnQiLCJuZXdMaW5lcyIsImxpbmVzIiwibGluZWRlbGltaXRlcnMiLCJhZGRDb3VudCIsInJlbW92ZUNvdW50IiwiaW5kZXhPZiIsIm9wZXJhdGlvbiJdLCJtYXBwaW5ncyI6Ijs7O2dDQUFnQkEsVSxHQUFBQSxVO0FBQVQsU0FBU0EsVUFBVCxDQUFvQkMsT0FBcEIsRUFBMkM7QUFBQSxzREFBZEMsT0FBYyx1RUFBSixFQUFJOztBQUNoRCxNQUFJQyxVQUFVRixRQUFRRyxLQUFSLENBQWMscUJBQWQsQ0FBZDtBQUFBLE1BQ0lDLGFBQWFKLFFBQVFLLEtBQVIsQ0FBYyxzQkFBZCxLQUF5QyxFQUQxRDtBQUFBLE1BRUlDLE9BQU8sRUFGWDtBQUFBLE1BR0lDLElBQUksQ0FIUjs7QUFLQSxXQUFTQyxVQUFULEdBQXNCO0FBQ3BCLFFBQUlDLFFBQVEsRUFBWjtBQUNBSCxTQUFLSSxJQUFMLENBQVVELEtBQVY7O0FBRUE7QUFDQSxXQUFPRixJQUFJTCxRQUFRUyxNQUFuQixFQUEyQjtBQUN6QixVQUFJQyxPQUFPVixRQUFRSyxDQUFSLENBQVg7O0FBRUE7QUFDQSxVQUFJLHdCQUF3Qk0sSUFBeEIsQ0FBNkJELElBQTdCLENBQUosRUFBd0M7QUFDdEM7QUFDRDs7QUFFRDtBQUNBLFVBQUlFLFNBQVUsMENBQUQsQ0FBNkNDLElBQTdDLENBQWtESCxJQUFsRCxDQUFiO0FBQ0EsVUFBSUUsTUFBSixFQUFZO0FBQ1ZMLGNBQU1BLEtBQU4sR0FBY0ssT0FBTyxDQUFQLENBQWQ7QUFDRDs7QUFFRFA7QUFDRDs7QUFFRDtBQUNBO0FBQ0FTLG9CQUFnQlAsS0FBaEI7QUFDQU8sb0JBQWdCUCxLQUFoQjs7QUFFQTtBQUNBQSxVQUFNUSxLQUFOLEdBQWMsRUFBZDs7QUFFQSxXQUFPVixJQUFJTCxRQUFRUyxNQUFuQixFQUEyQjtBQUN6QixVQUFJQyxRQUFPVixRQUFRSyxDQUFSLENBQVg7O0FBRUEsVUFBSSxpQ0FBaUNNLElBQWpDLENBQXNDRCxLQUF0QyxDQUFKLEVBQWlEO0FBQy9DO0FBQ0QsT0FGRCxNQUVPLElBQUksTUFBTUMsSUFBTixDQUFXRCxLQUFYLENBQUosRUFBc0I7QUFDM0JILGNBQU1RLEtBQU4sQ0FBWVAsSUFBWixDQUFpQlEsV0FBakI7QUFDRCxPQUZNLE1BRUEsSUFBSU4sU0FBUVgsUUFBUWtCLE1BQXBCLEVBQTRCO0FBQ2pDO0FBQ0EsY0FBTSxJQUFJQyxLQUFKLENBQVUsbUJBQW1CYixJQUFJLENBQXZCLElBQTRCLEdBQTVCLEdBQWtDYyxLQUFLQyxTQUFMLENBQWVWLEtBQWYsQ0FBNUMsQ0FBTjtBQUNELE9BSE0sTUFHQTtBQUNMTDtBQUNEO0FBQ0Y7QUFDRjs7QUFFRDtBQUNBO0FBQ0EsV0FBU1MsZUFBVCxDQUF5QlAsS0FBekIsRUFBZ0M7QUFDOUIsUUFBTWMsYUFBYyx1QkFBRCxDQUEwQlIsSUFBMUIsQ0FBK0JiLFFBQVFLLENBQVIsQ0FBL0IsQ0FBbkI7QUFDQSxRQUFJZ0IsVUFBSixFQUFnQjtBQUNkLFVBQUlDLFlBQVlELFdBQVcsQ0FBWCxNQUFrQixLQUFsQixHQUEwQixLQUExQixHQUFrQyxLQUFsRDtBQUNBLFVBQU1FLE9BQU9GLFdBQVcsQ0FBWCxFQUFjcEIsS0FBZCxDQUFvQixJQUFwQixFQUEwQixDQUExQixDQUFiO0FBQ0EsVUFBSXVCLFdBQVdELEtBQUssQ0FBTCxFQUFRRSxPQUFSLENBQWdCLE9BQWhCLEVBQXlCLElBQXpCLENBQWY7QUFDQSxVQUFJLFNBQVNkLElBQVQsQ0FBY2EsUUFBZCxDQUFKLEVBQTZCO0FBQzNCQSxtQkFBV0EsU0FBU0UsTUFBVCxDQUFnQixDQUFoQixFQUFtQkYsU0FBU2YsTUFBVCxHQUFrQixDQUFyQyxDQUFYO0FBQ0Q7QUFDREYsWUFBTWUsWUFBWSxVQUFsQixJQUFnQ0UsUUFBaEM7QUFDQWpCLFlBQU1lLFlBQVksUUFBbEIsSUFBOEIsQ0FBQ0MsS0FBSyxDQUFMLEtBQVcsRUFBWixFQUFnQkksSUFBaEIsRUFBOUI7O0FBRUF0QjtBQUNEO0FBQ0Y7O0FBRUQ7QUFDQTtBQUNBLFdBQVNXLFNBQVQsR0FBcUI7QUFDbkIsUUFBSVksbUJBQW1CdkIsQ0FBdkI7QUFBQSxRQUNJd0Isa0JBQWtCN0IsUUFBUUssR0FBUixDQUR0QjtBQUFBLFFBRUl5QixjQUFjRCxnQkFBZ0I1QixLQUFoQixDQUFzQiw0Q0FBdEIsQ0FGbEI7O0FBSUEsUUFBSThCLE9BQU87QUFDVEMsZ0JBQVUsQ0FBQ0YsWUFBWSxDQUFaLENBREY7QUFFVEcsZ0JBQVUsQ0FBQ0gsWUFBWSxDQUFaLENBQUQsSUFBbUIsQ0FGcEI7QUFHVEksZ0JBQVUsQ0FBQ0osWUFBWSxDQUFaLENBSEY7QUFJVEssZ0JBQVUsQ0FBQ0wsWUFBWSxDQUFaLENBQUQsSUFBbUIsQ0FKcEI7QUFLVE0sYUFBTyxFQUxFO0FBTVRDLHNCQUFnQjtBQU5QLEtBQVg7O0FBU0EsUUFBSUMsV0FBVyxDQUFmO0FBQUEsUUFDSUMsY0FBYyxDQURsQjtBQUVBLFdBQU9sQyxJQUFJTCxRQUFRUyxNQUFuQixFQUEyQkosR0FBM0IsRUFBZ0M7QUFDOUI7QUFDQTtBQUNBLFVBQUlMLFFBQVFLLENBQVIsRUFBV21DLE9BQVgsQ0FBbUIsTUFBbkIsTUFBK0IsQ0FBL0IsSUFDTW5DLElBQUksQ0FBSixHQUFRTCxRQUFRUyxNQUR0QixJQUVLVCxRQUFRSyxJQUFJLENBQVosRUFBZW1DLE9BQWYsQ0FBdUIsTUFBdkIsTUFBbUMsQ0FGeEMsSUFHS3hDLFFBQVFLLElBQUksQ0FBWixFQUFlbUMsT0FBZixDQUF1QixJQUF2QixNQUFpQyxDQUgxQyxFQUc2QztBQUN6QztBQUNIO0FBQ0QsVUFBSUMsWUFBYXpDLFFBQVFLLENBQVIsRUFBV0ksTUFBWCxJQUFxQixDQUFyQixJQUEwQkosS0FBTUwsUUFBUVMsTUFBUixHQUFpQixDQUFsRCxHQUF3RCxHQUF4RCxHQUE4RFQsUUFBUUssQ0FBUixFQUFXLENBQVgsQ0FBOUU7O0FBRUEsVUFBSW9DLGNBQWMsR0FBZCxJQUFxQkEsY0FBYyxHQUFuQyxJQUEwQ0EsY0FBYyxHQUF4RCxJQUErREEsY0FBYyxJQUFqRixFQUF1RjtBQUNyRlYsYUFBS0ssS0FBTCxDQUFXNUIsSUFBWCxDQUFnQlIsUUFBUUssQ0FBUixDQUFoQjtBQUNBMEIsYUFBS00sY0FBTCxDQUFvQjdCLElBQXBCLENBQXlCTixXQUFXRyxDQUFYLEtBQWlCLElBQTFDOztBQUVBLFlBQUlvQyxjQUFjLEdBQWxCLEVBQXVCO0FBQ3JCSDtBQUNELFNBRkQsTUFFTyxJQUFJRyxjQUFjLEdBQWxCLEVBQXVCO0FBQzVCRjtBQUNELFNBRk0sTUFFQSxJQUFJRSxjQUFjLEdBQWxCLEVBQXVCO0FBQzVCSDtBQUNBQztBQUNEO0FBQ0YsT0FaRCxNQVlPO0FBQ0w7QUFDRDtBQUNGOztBQUVEO0FBQ0EsUUFBSSxDQUFDRCxRQUFELElBQWFQLEtBQUtJLFFBQUwsS0FBa0IsQ0FBbkMsRUFBc0M7QUFDcENKLFdBQUtJLFFBQUwsR0FBZ0IsQ0FBaEI7QUFDRDtBQUNELFFBQUksQ0FBQ0ksV0FBRCxJQUFnQlIsS0FBS0UsUUFBTCxLQUFrQixDQUF0QyxFQUF5QztBQUN2Q0YsV0FBS0UsUUFBTCxHQUFnQixDQUFoQjtBQUNEOztBQUVEO0FBQ0EsUUFBSWxDLFFBQVFrQixNQUFaLEVBQW9CO0FBQ2xCLFVBQUlxQixhQUFhUCxLQUFLSSxRQUF0QixFQUFnQztBQUM5QixjQUFNLElBQUlqQixLQUFKLENBQVUsc0RBQXNEVSxtQkFBbUIsQ0FBekUsQ0FBVixDQUFOO0FBQ0Q7QUFDRCxVQUFJVyxnQkFBZ0JSLEtBQUtFLFFBQXpCLEVBQW1DO0FBQ2pDLGNBQU0sSUFBSWYsS0FBSixDQUFVLHdEQUF3RFUsbUJBQW1CLENBQTNFLENBQVYsQ0FBTjtBQUNEO0FBQ0Y7O0FBRUQsV0FBT0csSUFBUDtBQUNEOztBQUVELFNBQU8xQixJQUFJTCxRQUFRUyxNQUFuQixFQUEyQjtBQUN6Qkg7QUFDRDs7QUFFRCxTQUFPRixJQUFQO0FBQ0QiLCJmaWxlIjoicGFyc2UuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gcGFyc2VQYXRjaCh1bmlEaWZmLCBvcHRpb25zID0ge30pIHtcbiAgbGV0IGRpZmZzdHIgPSB1bmlEaWZmLnNwbGl0KC9cXHJcXG58W1xcblxcdlxcZlxcclxceDg1XS8pLFxuICAgICAgZGVsaW1pdGVycyA9IHVuaURpZmYubWF0Y2goL1xcclxcbnxbXFxuXFx2XFxmXFxyXFx4ODVdL2cpIHx8IFtdLFxuICAgICAgbGlzdCA9IFtdLFxuICAgICAgaSA9IDA7XG5cbiAgZnVuY3Rpb24gcGFyc2VJbmRleCgpIHtcbiAgICBsZXQgaW5kZXggPSB7fTtcbiAgICBsaXN0LnB1c2goaW5kZXgpO1xuXG4gICAgLy8gUGFyc2UgZGlmZiBtZXRhZGF0YVxuICAgIHdoaWxlIChpIDwgZGlmZnN0ci5sZW5ndGgpIHtcbiAgICAgIGxldCBsaW5lID0gZGlmZnN0cltpXTtcblxuICAgICAgLy8gRmlsZSBoZWFkZXIgZm91bmQsIGVuZCBwYXJzaW5nIGRpZmYgbWV0YWRhdGFcbiAgICAgIGlmICgvXihcXC1cXC1cXC18XFwrXFwrXFwrfEBAKVxccy8udGVzdChsaW5lKSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgLy8gRGlmZiBpbmRleFxuICAgICAgbGV0IGhlYWRlciA9ICgvXig/OkluZGV4OnxkaWZmKD86IC1yIFxcdyspKylcXHMrKC4rPylcXHMqJC8pLmV4ZWMobGluZSk7XG4gICAgICBpZiAoaGVhZGVyKSB7XG4gICAgICAgIGluZGV4LmluZGV4ID0gaGVhZGVyWzFdO1xuICAgICAgfVxuXG4gICAgICBpKys7XG4gICAgfVxuXG4gICAgLy8gUGFyc2UgZmlsZSBoZWFkZXJzIGlmIHRoZXkgYXJlIGRlZmluZWQuIFVuaWZpZWQgZGlmZiByZXF1aXJlcyB0aGVtLCBidXRcbiAgICAvLyB0aGVyZSdzIG5vIHRlY2huaWNhbCBpc3N1ZXMgdG8gaGF2ZSBhbiBpc29sYXRlZCBodW5rIHdpdGhvdXQgZmlsZSBoZWFkZXJcbiAgICBwYXJzZUZpbGVIZWFkZXIoaW5kZXgpO1xuICAgIHBhcnNlRmlsZUhlYWRlcihpbmRleCk7XG5cbiAgICAvLyBQYXJzZSBodW5rc1xuICAgIGluZGV4Lmh1bmtzID0gW107XG5cbiAgICB3aGlsZSAoaSA8IGRpZmZzdHIubGVuZ3RoKSB7XG4gICAgICBsZXQgbGluZSA9IGRpZmZzdHJbaV07XG5cbiAgICAgIGlmICgvXihJbmRleDp8ZGlmZnxcXC1cXC1cXC18XFwrXFwrXFwrKVxccy8udGVzdChsaW5lKSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH0gZWxzZSBpZiAoL15AQC8udGVzdChsaW5lKSkge1xuICAgICAgICBpbmRleC5odW5rcy5wdXNoKHBhcnNlSHVuaygpKTtcbiAgICAgIH0gZWxzZSBpZiAobGluZSAmJiBvcHRpb25zLnN0cmljdCkge1xuICAgICAgICAvLyBJZ25vcmUgdW5leHBlY3RlZCBjb250ZW50IHVubGVzcyBpbiBzdHJpY3QgbW9kZVxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vua25vd24gbGluZSAnICsgKGkgKyAxKSArICcgJyArIEpTT04uc3RyaW5naWZ5KGxpbmUpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGkrKztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBQYXJzZXMgdGhlIC0tLSBhbmQgKysrIGhlYWRlcnMsIGlmIG5vbmUgYXJlIGZvdW5kLCBubyBsaW5lc1xuICAvLyBhcmUgY29uc3VtZWQuXG4gIGZ1bmN0aW9uIHBhcnNlRmlsZUhlYWRlcihpbmRleCkge1xuICAgIGNvbnN0IGZpbGVIZWFkZXIgPSAoL14oLS0tfFxcK1xcK1xcKylcXHMrKC4qKSQvKS5leGVjKGRpZmZzdHJbaV0pO1xuICAgIGlmIChmaWxlSGVhZGVyKSB7XG4gICAgICBsZXQga2V5UHJlZml4ID0gZmlsZUhlYWRlclsxXSA9PT0gJy0tLScgPyAnb2xkJyA6ICduZXcnO1xuICAgICAgY29uc3QgZGF0YSA9IGZpbGVIZWFkZXJbMl0uc3BsaXQoJ1xcdCcsIDIpO1xuICAgICAgbGV0IGZpbGVOYW1lID0gZGF0YVswXS5yZXBsYWNlKC9cXFxcXFxcXC9nLCAnXFxcXCcpO1xuICAgICAgaWYgKC9eXCIuKlwiJC8udGVzdChmaWxlTmFtZSkpIHtcbiAgICAgICAgZmlsZU5hbWUgPSBmaWxlTmFtZS5zdWJzdHIoMSwgZmlsZU5hbWUubGVuZ3RoIC0gMik7XG4gICAgICB9XG4gICAgICBpbmRleFtrZXlQcmVmaXggKyAnRmlsZU5hbWUnXSA9IGZpbGVOYW1lO1xuICAgICAgaW5kZXhba2V5UHJlZml4ICsgJ0hlYWRlciddID0gKGRhdGFbMV0gfHwgJycpLnRyaW0oKTtcblxuICAgICAgaSsrO1xuICAgIH1cbiAgfVxuXG4gIC8vIFBhcnNlcyBhIGh1bmtcbiAgLy8gVGhpcyBhc3N1bWVzIHRoYXQgd2UgYXJlIGF0IHRoZSBzdGFydCBvZiBhIGh1bmsuXG4gIGZ1bmN0aW9uIHBhcnNlSHVuaygpIHtcbiAgICBsZXQgY2h1bmtIZWFkZXJJbmRleCA9IGksXG4gICAgICAgIGNodW5rSGVhZGVyTGluZSA9IGRpZmZzdHJbaSsrXSxcbiAgICAgICAgY2h1bmtIZWFkZXIgPSBjaHVua0hlYWRlckxpbmUuc3BsaXQoL0BAIC0oXFxkKykoPzosKFxcZCspKT8gXFwrKFxcZCspKD86LChcXGQrKSk/IEBALyk7XG5cbiAgICBsZXQgaHVuayA9IHtcbiAgICAgIG9sZFN0YXJ0OiArY2h1bmtIZWFkZXJbMV0sXG4gICAgICBvbGRMaW5lczogK2NodW5rSGVhZGVyWzJdIHx8IDEsXG4gICAgICBuZXdTdGFydDogK2NodW5rSGVhZGVyWzNdLFxuICAgICAgbmV3TGluZXM6ICtjaHVua0hlYWRlcls0XSB8fCAxLFxuICAgICAgbGluZXM6IFtdLFxuICAgICAgbGluZWRlbGltaXRlcnM6IFtdXG4gICAgfTtcblxuICAgIGxldCBhZGRDb3VudCA9IDAsXG4gICAgICAgIHJlbW92ZUNvdW50ID0gMDtcbiAgICBmb3IgKDsgaSA8IGRpZmZzdHIubGVuZ3RoOyBpKyspIHtcbiAgICAgIC8vIExpbmVzIHN0YXJ0aW5nIHdpdGggJy0tLScgY291bGQgYmUgbWlzdGFrZW4gZm9yIHRoZSBcInJlbW92ZSBsaW5lXCIgb3BlcmF0aW9uXG4gICAgICAvLyBCdXQgdGhleSBjb3VsZCBiZSB0aGUgaGVhZGVyIGZvciB0aGUgbmV4dCBmaWxlLiBUaGVyZWZvcmUgcHJ1bmUgc3VjaCBjYXNlcyBvdXQuXG4gICAgICBpZiAoZGlmZnN0cltpXS5pbmRleE9mKCctLS0gJykgPT09IDBcbiAgICAgICAgICAgICYmIChpICsgMiA8IGRpZmZzdHIubGVuZ3RoKVxuICAgICAgICAgICAgJiYgZGlmZnN0cltpICsgMV0uaW5kZXhPZignKysrICcpID09PSAwXG4gICAgICAgICAgICAmJiBkaWZmc3RyW2kgKyAyXS5pbmRleE9mKCdAQCcpID09PSAwKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBsZXQgb3BlcmF0aW9uID0gKGRpZmZzdHJbaV0ubGVuZ3RoID09IDAgJiYgaSAhPSAoZGlmZnN0ci5sZW5ndGggLSAxKSkgPyAnICcgOiBkaWZmc3RyW2ldWzBdO1xuXG4gICAgICBpZiAob3BlcmF0aW9uID09PSAnKycgfHwgb3BlcmF0aW9uID09PSAnLScgfHwgb3BlcmF0aW9uID09PSAnICcgfHwgb3BlcmF0aW9uID09PSAnXFxcXCcpIHtcbiAgICAgICAgaHVuay5saW5lcy5wdXNoKGRpZmZzdHJbaV0pO1xuICAgICAgICBodW5rLmxpbmVkZWxpbWl0ZXJzLnB1c2goZGVsaW1pdGVyc1tpXSB8fCAnXFxuJyk7XG5cbiAgICAgICAgaWYgKG9wZXJhdGlvbiA9PT0gJysnKSB7XG4gICAgICAgICAgYWRkQ291bnQrKztcbiAgICAgICAgfSBlbHNlIGlmIChvcGVyYXRpb24gPT09ICctJykge1xuICAgICAgICAgIHJlbW92ZUNvdW50Kys7XG4gICAgICAgIH0gZWxzZSBpZiAob3BlcmF0aW9uID09PSAnICcpIHtcbiAgICAgICAgICBhZGRDb3VudCsrO1xuICAgICAgICAgIHJlbW92ZUNvdW50Kys7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEhhbmRsZSB0aGUgZW1wdHkgYmxvY2sgY291bnQgY2FzZVxuICAgIGlmICghYWRkQ291bnQgJiYgaHVuay5uZXdMaW5lcyA9PT0gMSkge1xuICAgICAgaHVuay5uZXdMaW5lcyA9IDA7XG4gICAgfVxuICAgIGlmICghcmVtb3ZlQ291bnQgJiYgaHVuay5vbGRMaW5lcyA9PT0gMSkge1xuICAgICAgaHVuay5vbGRMaW5lcyA9IDA7XG4gICAgfVxuXG4gICAgLy8gUGVyZm9ybSBvcHRpb25hbCBzYW5pdHkgY2hlY2tpbmdcbiAgICBpZiAob3B0aW9ucy5zdHJpY3QpIHtcbiAgICAgIGlmIChhZGRDb3VudCAhPT0gaHVuay5uZXdMaW5lcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0FkZGVkIGxpbmUgY291bnQgZGlkIG5vdCBtYXRjaCBmb3IgaHVuayBhdCBsaW5lICcgKyAoY2h1bmtIZWFkZXJJbmRleCArIDEpKTtcbiAgICAgIH1cbiAgICAgIGlmIChyZW1vdmVDb3VudCAhPT0gaHVuay5vbGRMaW5lcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlbW92ZWQgbGluZSBjb3VudCBkaWQgbm90IG1hdGNoIGZvciBodW5rIGF0IGxpbmUgJyArIChjaHVua0hlYWRlckluZGV4ICsgMSkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBodW5rO1xuICB9XG5cbiAgd2hpbGUgKGkgPCBkaWZmc3RyLmxlbmd0aCkge1xuICAgIHBhcnNlSW5kZXgoKTtcbiAgfVxuXG4gIHJldHVybiBsaXN0O1xufVxuIl19 diff --git a/WebServer/node_modules/diff/lib/util/array.js b/WebServer/node_modules/diff/lib/util/array.js new file mode 100644 index 000000000..1bb42562c --- /dev/null +++ b/WebServer/node_modules/diff/lib/util/array.js @@ -0,0 +1,27 @@ +/*istanbul ignore start*/"use strict"; + +exports.__esModule = true; +exports. /*istanbul ignore end*/arrayEqual = arrayEqual; +/*istanbul ignore start*/exports. /*istanbul ignore end*/arrayStartsWith = arrayStartsWith; +function arrayEqual(a, b) { + if (a.length !== b.length) { + return false; + } + + return arrayStartsWith(a, b); +} + +function arrayStartsWith(array, start) { + if (start.length > array.length) { + return false; + } + + for (var i = 0; i < start.length; i++) { + if (start[i] !== array[i]) { + return false; + } + } + + return true; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL2FycmF5LmpzIl0sIm5hbWVzIjpbImFycmF5RXF1YWwiLCJhcnJheVN0YXJ0c1dpdGgiLCJhIiwiYiIsImxlbmd0aCIsImFycmF5Iiwic3RhcnQiLCJpIl0sIm1hcHBpbmdzIjoiOzs7Z0NBQWdCQSxVLEdBQUFBLFU7eURBUUFDLGUsR0FBQUEsZTtBQVJULFNBQVNELFVBQVQsQ0FBb0JFLENBQXBCLEVBQXVCQyxDQUF2QixFQUEwQjtBQUMvQixNQUFJRCxFQUFFRSxNQUFGLEtBQWFELEVBQUVDLE1BQW5CLEVBQTJCO0FBQ3pCLFdBQU8sS0FBUDtBQUNEOztBQUVELFNBQU9ILGdCQUFnQkMsQ0FBaEIsRUFBbUJDLENBQW5CLENBQVA7QUFDRDs7QUFFTSxTQUFTRixlQUFULENBQXlCSSxLQUF6QixFQUFnQ0MsS0FBaEMsRUFBdUM7QUFDNUMsTUFBSUEsTUFBTUYsTUFBTixHQUFlQyxNQUFNRCxNQUF6QixFQUFpQztBQUMvQixXQUFPLEtBQVA7QUFDRDs7QUFFRCxPQUFLLElBQUlHLElBQUksQ0FBYixFQUFnQkEsSUFBSUQsTUFBTUYsTUFBMUIsRUFBa0NHLEdBQWxDLEVBQXVDO0FBQ3JDLFFBQUlELE1BQU1DLENBQU4sTUFBYUYsTUFBTUUsQ0FBTixDQUFqQixFQUEyQjtBQUN6QixhQUFPLEtBQVA7QUFDRDtBQUNGOztBQUVELFNBQU8sSUFBUDtBQUNEIiwiZmlsZSI6ImFycmF5LmpzIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIGFycmF5RXF1YWwoYSwgYikge1xuICBpZiAoYS5sZW5ndGggIT09IGIubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcmV0dXJuIGFycmF5U3RhcnRzV2l0aChhLCBiKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFycmF5U3RhcnRzV2l0aChhcnJheSwgc3RhcnQpIHtcbiAgaWYgKHN0YXJ0Lmxlbmd0aCA+IGFycmF5Lmxlbmd0aCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgc3RhcnQubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoc3RhcnRbaV0gIT09IGFycmF5W2ldKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59XG4iXX0= diff --git a/WebServer/node_modules/diff/lib/util/distance-iterator.js b/WebServer/node_modules/diff/lib/util/distance-iterator.js new file mode 100644 index 000000000..95e4675dd --- /dev/null +++ b/WebServer/node_modules/diff/lib/util/distance-iterator.js @@ -0,0 +1,47 @@ +/*istanbul ignore start*/"use strict"; + +exports.__esModule = true; + +exports["default"] = /*istanbul ignore end*/function (start, minLine, maxLine) { + var wantForward = true, + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; + + return function iterator() { + if (wantForward && !forwardExhausted) { + if (backwardExhausted) { + localOffset++; + } else { + wantForward = false; + } + + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) + if (start + localOffset <= maxLine) { + return localOffset; + } + + forwardExhausted = true; + } + + if (!backwardExhausted) { + if (!forwardExhausted) { + wantForward = true; + } + + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location + if (minLine <= start - localOffset) { + return -localOffset++; + } + + backwardExhausted = true; + return iterator(); + } + + // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined + }; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL2Rpc3RhbmNlLWl0ZXJhdG9yLmpzIl0sIm5hbWVzIjpbInN0YXJ0IiwibWluTGluZSIsIm1heExpbmUiLCJ3YW50Rm9yd2FyZCIsImJhY2t3YXJkRXhoYXVzdGVkIiwiZm9yd2FyZEV4aGF1c3RlZCIsImxvY2FsT2Zmc2V0IiwiaXRlcmF0b3IiXSwibWFwcGluZ3MiOiI7Ozs7NENBR2UsVUFBU0EsS0FBVCxFQUFnQkMsT0FBaEIsRUFBeUJDLE9BQXpCLEVBQWtDO0FBQy9DLE1BQUlDLGNBQWMsSUFBbEI7QUFBQSxNQUNJQyxvQkFBb0IsS0FEeEI7QUFBQSxNQUVJQyxtQkFBbUIsS0FGdkI7QUFBQSxNQUdJQyxjQUFjLENBSGxCOztBQUtBLFNBQU8sU0FBU0MsUUFBVCxHQUFvQjtBQUN6QixRQUFJSixlQUFlLENBQUNFLGdCQUFwQixFQUFzQztBQUNwQyxVQUFJRCxpQkFBSixFQUF1QjtBQUNyQkU7QUFDRCxPQUZELE1BRU87QUFDTEgsc0JBQWMsS0FBZDtBQUNEOztBQUVEO0FBQ0E7QUFDQSxVQUFJSCxRQUFRTSxXQUFSLElBQXVCSixPQUEzQixFQUFvQztBQUNsQyxlQUFPSSxXQUFQO0FBQ0Q7O0FBRURELHlCQUFtQixJQUFuQjtBQUNEOztBQUVELFFBQUksQ0FBQ0QsaUJBQUwsRUFBd0I7QUFDdEIsVUFBSSxDQUFDQyxnQkFBTCxFQUF1QjtBQUNyQkYsc0JBQWMsSUFBZDtBQUNEOztBQUVEO0FBQ0E7QUFDQSxVQUFJRixXQUFXRCxRQUFRTSxXQUF2QixFQUFvQztBQUNsQyxlQUFPLENBQUNBLGFBQVI7QUFDRDs7QUFFREYsMEJBQW9CLElBQXBCO0FBQ0EsYUFBT0csVUFBUDtBQUNEOztBQUVEO0FBQ0E7QUFDRCxHQWxDRDtBQW1DRCxDIiwiZmlsZSI6ImRpc3RhbmNlLWl0ZXJhdG9yLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gSXRlcmF0b3IgdGhhdCB0cmF2ZXJzZXMgaW4gdGhlIHJhbmdlIG9mIFttaW4sIG1heF0sIHN0ZXBwaW5nXG4vLyBieSBkaXN0YW5jZSBmcm9tIGEgZ2l2ZW4gc3RhcnQgcG9zaXRpb24uIEkuZS4gZm9yIFswLCA0XSwgd2l0aFxuLy8gc3RhcnQgb2YgMiwgdGhpcyB3aWxsIGl0ZXJhdGUgMiwgMywgMSwgNCwgMC5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uKHN0YXJ0LCBtaW5MaW5lLCBtYXhMaW5lKSB7XG4gIGxldCB3YW50Rm9yd2FyZCA9IHRydWUsXG4gICAgICBiYWNrd2FyZEV4aGF1c3RlZCA9IGZhbHNlLFxuICAgICAgZm9yd2FyZEV4aGF1c3RlZCA9IGZhbHNlLFxuICAgICAgbG9jYWxPZmZzZXQgPSAxO1xuXG4gIHJldHVybiBmdW5jdGlvbiBpdGVyYXRvcigpIHtcbiAgICBpZiAod2FudEZvcndhcmQgJiYgIWZvcndhcmRFeGhhdXN0ZWQpIHtcbiAgICAgIGlmIChiYWNrd2FyZEV4aGF1c3RlZCkge1xuICAgICAgICBsb2NhbE9mZnNldCsrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgd2FudEZvcndhcmQgPSBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdHJ5aW5nIHRvIGZpdCBiZXlvbmQgdGV4dCBsZW5ndGgsIGFuZCBpZiBub3QsIGNoZWNrIGl0IGZpdHNcbiAgICAgIC8vIGFmdGVyIG9mZnNldCBsb2NhdGlvbiAob3IgZGVzaXJlZCBsb2NhdGlvbiBvbiBmaXJzdCBpdGVyYXRpb24pXG4gICAgICBpZiAoc3RhcnQgKyBsb2NhbE9mZnNldCA8PSBtYXhMaW5lKSB7XG4gICAgICAgIHJldHVybiBsb2NhbE9mZnNldDtcbiAgICAgIH1cblxuICAgICAgZm9yd2FyZEV4aGF1c3RlZCA9IHRydWU7XG4gICAgfVxuXG4gICAgaWYgKCFiYWNrd2FyZEV4aGF1c3RlZCkge1xuICAgICAgaWYgKCFmb3J3YXJkRXhoYXVzdGVkKSB7XG4gICAgICAgIHdhbnRGb3J3YXJkID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgaWYgdHJ5aW5nIHRvIGZpdCBiZWZvcmUgdGV4dCBiZWdpbm5pbmcsIGFuZCBpZiBub3QsIGNoZWNrIGl0IGZpdHNcbiAgICAgIC8vIGJlZm9yZSBvZmZzZXQgbG9jYXRpb25cbiAgICAgIGlmIChtaW5MaW5lIDw9IHN0YXJ0IC0gbG9jYWxPZmZzZXQpIHtcbiAgICAgICAgcmV0dXJuIC1sb2NhbE9mZnNldCsrO1xuICAgICAgfVxuXG4gICAgICBiYWNrd2FyZEV4aGF1c3RlZCA9IHRydWU7XG4gICAgICByZXR1cm4gaXRlcmF0b3IoKTtcbiAgICB9XG5cbiAgICAvLyBXZSB0cmllZCB0byBmaXQgaHVuayBiZWZvcmUgdGV4dCBiZWdpbm5pbmcgYW5kIGJleW9uZCB0ZXh0IGxlbmd0aCwgdGhlblxuICAgIC8vIGh1bmsgY2FuJ3QgZml0IG9uIHRoZSB0ZXh0LiBSZXR1cm4gdW5kZWZpbmVkXG4gIH07XG59XG4iXX0= diff --git a/WebServer/node_modules/diff/lib/util/params.js b/WebServer/node_modules/diff/lib/util/params.js new file mode 100644 index 000000000..6ff04832a --- /dev/null +++ b/WebServer/node_modules/diff/lib/util/params.js @@ -0,0 +1,18 @@ +/*istanbul ignore start*/'use strict'; + +exports.__esModule = true; +exports. /*istanbul ignore end*/generateOptions = generateOptions; +function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } + } + return defaults; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlsL3BhcmFtcy5qcyJdLCJuYW1lcyI6WyJnZW5lcmF0ZU9wdGlvbnMiLCJvcHRpb25zIiwiZGVmYXVsdHMiLCJjYWxsYmFjayIsIm5hbWUiLCJoYXNPd25Qcm9wZXJ0eSJdLCJtYXBwaW5ncyI6Ijs7O2dDQUFnQkEsZSxHQUFBQSxlO0FBQVQsU0FBU0EsZUFBVCxDQUF5QkMsT0FBekIsRUFBa0NDLFFBQWxDLEVBQTRDO0FBQ2pELE1BQUksT0FBT0QsT0FBUCxLQUFtQixVQUF2QixFQUFtQztBQUNqQ0MsYUFBU0MsUUFBVCxHQUFvQkYsT0FBcEI7QUFDRCxHQUZELE1BRU8sSUFBSUEsT0FBSixFQUFhO0FBQ2xCLFNBQUssSUFBSUcsSUFBVCxJQUFpQkgsT0FBakIsRUFBMEI7QUFDeEI7QUFDQSxVQUFJQSxRQUFRSSxjQUFSLENBQXVCRCxJQUF2QixDQUFKLEVBQWtDO0FBQ2hDRixpQkFBU0UsSUFBVCxJQUFpQkgsUUFBUUcsSUFBUixDQUFqQjtBQUNEO0FBQ0Y7QUFDRjtBQUNELFNBQU9GLFFBQVA7QUFDRCIsImZpbGUiOiJwYXJhbXMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVPcHRpb25zKG9wdGlvbnMsIGRlZmF1bHRzKSB7XG4gIGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIGRlZmF1bHRzLmNhbGxiYWNrID0gb3B0aW9ucztcbiAgfSBlbHNlIGlmIChvcHRpb25zKSB7XG4gICAgZm9yIChsZXQgbmFtZSBpbiBvcHRpb25zKSB7XG4gICAgICAvKiBpc3RhbmJ1bCBpZ25vcmUgZWxzZSAqL1xuICAgICAgaWYgKG9wdGlvbnMuaGFzT3duUHJvcGVydHkobmFtZSkpIHtcbiAgICAgICAgZGVmYXVsdHNbbmFtZV0gPSBvcHRpb25zW25hbWVdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gZGVmYXVsdHM7XG59XG4iXX0= diff --git a/WebServer/node_modules/diff/package.json b/WebServer/node_modules/diff/package.json new file mode 100644 index 000000000..069597207 --- /dev/null +++ b/WebServer/node_modules/diff/package.json @@ -0,0 +1,100 @@ +{ + "_args": [ + [ + "diff@3.5.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "diff@3.5.0", + "_id": "diff@3.5.0", + "_inBundle": false, + "_integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "_location": "/diff", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "diff@3.5.0", + "name": "diff", + "escapedName": "diff", + "rawSpec": "3.5.0", + "saveSpec": null, + "fetchSpec": "3.5.0" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "_spec": "3.5.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "browser": "./dist/diff.js", + "bugs": { + "url": "http://github.com/kpdecker/jsdiff/issues", + "email": "kpdecker@gmail.com" + }, + "dependencies": {}, + "description": "A javascript text diff implementation.", + "devDependencies": { + "async": "^1.4.2", + "babel-core": "^6.0.0", + "babel-loader": "^6.0.0", + "babel-preset-es2015-mod": "^6.3.13", + "babel-preset-es3": "^1.0.1", + "chai": "^3.3.0", + "colors": "^1.1.2", + "eslint": "^1.6.0", + "grunt": "^0.4.5", + "grunt-babel": "^6.0.0", + "grunt-clean": "^0.4.0", + "grunt-cli": "^0.1.13", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-uglify": "^1.0.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-eslint": "^17.3.1", + "grunt-karma": "^0.12.1", + "grunt-mocha-istanbul": "^3.0.1", + "grunt-mocha-test": "^0.12.7", + "grunt-webpack": "^1.0.11", + "istanbul": "github:kpdecker/istanbul", + "karma": "^0.13.11", + "karma-mocha": "^0.2.0", + "karma-mocha-reporter": "^2.0.0", + "karma-phantomjs-launcher": "^1.0.0", + "karma-sauce-launcher": "^0.3.0", + "karma-sourcemap-loader": "^0.3.6", + "karma-webpack": "^1.7.0", + "mocha": "^2.3.3", + "phantomjs-prebuilt": "^2.1.5", + "semver": "^5.0.3", + "webpack": "^1.12.2", + "webpack-dev-server": "^1.12.0" + }, + "engines": { + "node": ">=0.3.1" + }, + "homepage": "https://github.com/kpdecker/jsdiff#readme", + "keywords": [ + "diff", + "javascript" + ], + "license": "BSD-3-Clause", + "main": "./lib", + "maintainers": [ + { + "name": "Kevin Decker", + "email": "kpdecker@gmail.com", + "url": "http://incaseofstairs.com" + } + ], + "name": "diff", + "optionalDependencies": {}, + "repository": { + "type": "git", + "url": "git://github.com/kpdecker/jsdiff.git" + }, + "scripts": { + "test": "grunt" + }, + "version": "3.5.0" +} diff --git a/WebServer/node_modules/diff/runtime.js b/WebServer/node_modules/diff/runtime.js new file mode 100644 index 000000000..fd8ca6ea2 --- /dev/null +++ b/WebServer/node_modules/diff/runtime.js @@ -0,0 +1,3 @@ +require('babel-core/register')({ + ignore: /\/lib\/|\/node_modules\// +}); diff --git a/WebServer/node_modules/dotenv/config.js b/WebServer/node_modules/dotenv/config.js new file mode 100644 index 000000000..c7e6447fc --- /dev/null +++ b/WebServer/node_modules/dotenv/config.js @@ -0,0 +1,11 @@ +/* @flow */ + +(function () { + require('./lib/main').config( + Object.assign( + {}, + require('./lib/env-options'), + require('./lib/cli-options')(process.argv) + ) + ) +})() diff --git a/WebServer/node_modules/dotenv/lib/cli-options.js b/WebServer/node_modules/dotenv/lib/cli-options.js new file mode 100644 index 000000000..ad69fabd9 --- /dev/null +++ b/WebServer/node_modules/dotenv/lib/cli-options.js @@ -0,0 +1,13 @@ +/* @flow */ + +const re = /^dotenv_config_(encoding|path|debug)=(.+)$/ + +module.exports = function optionMatcher (args /*: Array */) { + return args.reduce(function (acc, cur) { + const matches = cur.match(re) + if (matches) { + acc[matches[1]] = matches[2] + } + return acc + }, {}) +} diff --git a/WebServer/node_modules/dotenv/lib/env-options.js b/WebServer/node_modules/dotenv/lib/env-options.js new file mode 100644 index 000000000..b4c4a6068 --- /dev/null +++ b/WebServer/node_modules/dotenv/lib/env-options.js @@ -0,0 +1,18 @@ +/* @flow */ + +// ../config.js accepts options via environment variables +const options = {} + +if (process.env.DOTENV_CONFIG_ENCODING != null) { + options.encoding = process.env.DOTENV_CONFIG_ENCODING +} + +if (process.env.DOTENV_CONFIG_PATH != null) { + options.path = process.env.DOTENV_CONFIG_PATH +} + +if (process.env.DOTENV_CONFIG_DEBUG != null) { + options.debug = process.env.DOTENV_CONFIG_DEBUG +} + +module.exports = options diff --git a/WebServer/node_modules/dotenv/lib/main.js b/WebServer/node_modules/dotenv/lib/main.js new file mode 100644 index 000000000..38f36f310 --- /dev/null +++ b/WebServer/node_modules/dotenv/lib/main.js @@ -0,0 +1,112 @@ +/* @flow */ +/*:: + +type DotenvParseOptions = { + debug?: boolean +} + +// keys and values from src +type DotenvParseOutput = { [string]: string } + +type DotenvConfigOptions = { + path?: string, // path to .env file + encoding?: string, // encoding of .env file + debug?: string // turn on logging for debugging purposes +} + +type DotenvConfigOutput = { + parsed?: DotenvParseOutput, + error?: Error +} + +*/ + +const fs = require('fs') +const path = require('path') + +function log (message /*: string */) { + console.log(`[dotenv][DEBUG] ${message}`) +} + +const NEWLINE = '\n' +const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/ +const RE_NEWLINES = /\\n/g + +// Parses src into an Object +function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ { + const debug = Boolean(options && options.debug) + const obj = {} + + // convert Buffers before splitting into lines and processing + src.toString().split(NEWLINE).forEach(function (line, idx) { + // matching "KEY' and 'VAL' in 'KEY=VAL' + const keyValueArr = line.match(RE_INI_KEY_VAL) + // matched? + if (keyValueArr != null) { + const key = keyValueArr[1] + // default undefined or missing values to empty string + let val = (keyValueArr[2] || '') + const end = val.length - 1 + const isDoubleQuoted = val[0] === '"' && val[end] === '"' + const isSingleQuoted = val[0] === "'" && val[end] === "'" + + // if single or double quoted, remove quotes + if (isSingleQuoted || isDoubleQuoted) { + val = val.substring(1, end) + + // if double quoted, expand newlines + if (isDoubleQuoted) { + val = val.replace(RE_NEWLINES, NEWLINE) + } + } else { + // remove surrounding whitespace + val = val.trim() + } + + obj[key] = val + } else if (debug) { + log(`did not match key and value when parsing line ${idx + 1}: ${line}`) + } + }) + + return obj +} + +// Populates process.env from .env file +function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ { + let dotenvPath = path.resolve(process.cwd(), '.env') + let encoding /*: string */ = 'utf8' + let debug = false + + if (options) { + if (options.path != null) { + dotenvPath = options.path + } + if (options.encoding != null) { + encoding = options.encoding + } + if (options.debug != null) { + debug = true + } + } + + try { + // specifying an encoding returns a string instead of a buffer + const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug }) + + Object.keys(parsed).forEach(function (key) { + if (!process.env.hasOwnProperty(key)) { + process.env[key] = parsed[key] + } else if (debug) { + log(`"${key}" is already defined in \`process.env\` and will not be overwritten`) + } + }) + + return { parsed } + } catch (e) { + return { error: e } + } +} + +module.exports.config = config +module.exports.parse = parse diff --git a/WebServer/node_modules/dotenv/package.json b/WebServer/node_modules/dotenv/package.json new file mode 100644 index 000000000..f68f52372 --- /dev/null +++ b/WebServer/node_modules/dotenv/package.json @@ -0,0 +1,79 @@ +{ + "_args": [ + [ + "dotenv@8.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "dotenv@8.0.0", + "_id": "dotenv@8.0.0", + "_inBundle": false, + "_integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==", + "_location": "/dotenv", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "dotenv@8.0.0", + "name": "dotenv", + "escapedName": "dotenv", + "rawSpec": "8.0.0", + "saveSpec": null, + "fetchSpec": "8.0.0" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz", + "_spec": "8.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "bugs": { + "url": "https://github.com/motdotla/dotenv/issues" + }, + "dependencies": {}, + "description": "Loads environment variables from .env file", + "devDependencies": { + "decache": "^4.5.1", + "flow-bin": "^0.98.0", + "sinon": "^7.3.2", + "standard": "^12.0.1", + "standard-markdown": "^5.0.1", + "standard-version": "^5.0.2", + "tap": "^13.1.2" + }, + "engines": { + "node": ">=8" + }, + "homepage": "https://github.com/motdotla/dotenv#readme", + "keywords": [ + "dotenv", + "env", + ".env", + "environment", + "variables", + "config", + "settings" + ], + "license": "BSD-2-Clause", + "main": "lib/main.js", + "name": "dotenv", + "repository": { + "type": "git", + "url": "git://github.com/motdotla/dotenv.git" + }, + "scripts": { + "flow": "flow", + "lint": "standard", + "postlint": "standard-markdown", + "prerelease": "npm test", + "pretest": "npm run lint", + "release": "standard-version", + "test": "tap tests/*.js --100" + }, + "standard": { + "ignore": [ + "flow-typed/" + ] + }, + "version": "8.0.0" +} diff --git a/WebServer/node_modules/emoji-regex/es2015/index.js b/WebServer/node_modules/emoji-regex/es2015/index.js new file mode 100644 index 000000000..0216db958 --- /dev/null +++ b/WebServer/node_modules/emoji-regex/es2015/index.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = () => { + // https://mths.be/emoji + return /\u{1F3F4}(?:\u{E0067}\u{E0062}(?:\u{E0065}\u{E006E}\u{E0067}|\u{E0077}\u{E006C}\u{E0073}|\u{E0073}\u{E0063}\u{E0074})\u{E007F}|\u200D\u2620\uFE0F)|\u{1F469}\u200D\u{1F469}\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F468}(?:\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F468}|[\u{1F468}\u{1F469}]\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}])|[\u{1F3FB}-\u{1F3FF}]\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}])|\u{1F469}\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D[\u{1F468}\u{1F469}]|[\u{1F468}\u{1F469}])|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}])|\u{1F469}\u200D\u{1F466}\u200D\u{1F466}|(?:\u{1F441}\uFE0F\u200D\u{1F5E8}|\u{1F469}[\u{1F3FB}-\u{1F3FF}]\u200D[\u2695\u2696\u2708]|\u{1F468}(?:[\u{1F3FB}-\u{1F3FF}]\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}]\uFE0F|[\u{1F46F}\u{1F93C}\u{1F9DE}\u{1F9DF}])\u200D[\u2640\u2642]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9D6}-\u{1F9DD}](?:[\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\u{1F469}\u200D[\u2695\u2696\u2708])\uFE0F|\u{1F469}\u200D\u{1F467}\u200D[\u{1F466}\u{1F467}]|\u{1F469}\u200D\u{1F469}\u200D[\u{1F466}\u{1F467}]|\u{1F468}(?:\u200D(?:[\u{1F468}\u{1F469}]\u200D[\u{1F466}\u{1F467}]|[\u{1F466}\u{1F467}])|[\u{1F3FB}-\u{1F3FF}])|\u{1F3F3}\uFE0F\u200D\u{1F308}|\u{1F469}\u200D\u{1F467}|\u{1F469}[\u{1F3FB}-\u{1F3FF}]\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}]|\u{1F469}\u200D\u{1F466}|\u{1F1F6}\u{1F1E6}|\u{1F1FD}\u{1F1F0}|\u{1F1F4}\u{1F1F2}|\u{1F469}[\u{1F3FB}-\u{1F3FF}]|\u{1F1ED}[\u{1F1F0}\u{1F1F2}\u{1F1F3}\u{1F1F7}\u{1F1F9}\u{1F1FA}]|\u{1F1EC}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EE}\u{1F1F1}-\u{1F1F3}\u{1F1F5}-\u{1F1FA}\u{1F1FC}\u{1F1FE}]|\u{1F1EA}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1ED}\u{1F1F7}-\u{1F1FA}]|\u{1F1E8}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1EE}\u{1F1F0}-\u{1F1F5}\u{1F1F7}\u{1F1FA}-\u{1F1FF}]|\u{1F1F2}[\u{1F1E6}\u{1F1E8}-\u{1F1ED}\u{1F1F0}-\u{1F1FF}]|\u{1F1F3}[\u{1F1E6}\u{1F1E8}\u{1F1EA}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F4}\u{1F1F5}\u{1F1F7}\u{1F1FA}\u{1F1FF}]|\u{1F1FC}[\u{1F1EB}\u{1F1F8}]|\u{1F1FA}[\u{1F1E6}\u{1F1EC}\u{1F1F2}\u{1F1F3}\u{1F1F8}\u{1F1FE}\u{1F1FF}]|\u{1F1F0}[\u{1F1EA}\u{1F1EC}-\u{1F1EE}\u{1F1F2}\u{1F1F3}\u{1F1F5}\u{1F1F7}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1EF}[\u{1F1EA}\u{1F1F2}\u{1F1F4}\u{1F1F5}]|\u{1F1F8}[\u{1F1E6}-\u{1F1EA}\u{1F1EC}-\u{1F1F4}\u{1F1F7}-\u{1F1F9}\u{1F1FB}\u{1F1FD}-\u{1F1FF}]|\u{1F1EE}[\u{1F1E8}-\u{1F1EA}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}]|\u{1F1FF}[\u{1F1E6}\u{1F1F2}\u{1F1FC}]|\u{1F1EB}[\u{1F1EE}-\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1F7}]|\u{1F1F5}[\u{1F1E6}\u{1F1EA}-\u{1F1ED}\u{1F1F0}-\u{1F1F3}\u{1F1F7}-\u{1F1F9}\u{1F1FC}\u{1F1FE}]|\u{1F1E9}[\u{1F1EA}\u{1F1EC}\u{1F1EF}\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1FF}]|\u{1F1F9}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1ED}\u{1F1EF}-\u{1F1F4}\u{1F1F7}\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FF}]|\u{1F1E7}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EF}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|[#\*0-9]\uFE0F\u20E3|\u{1F1F1}[\u{1F1E6}-\u{1F1E8}\u{1F1EE}\u{1F1F0}\u{1F1F7}-\u{1F1FB}\u{1F1FE}]|\u{1F1E6}[\u{1F1E8}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F2}\u{1F1F4}\u{1F1F6}-\u{1F1FA}\u{1F1FC}\u{1F1FD}\u{1F1FF}]|\u{1F1F7}[\u{1F1EA}\u{1F1F4}\u{1F1F8}\u{1F1FA}\u{1F1FC}]|\u{1F1FB}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1EE}\u{1F1F3}\u{1F1FA}]|\u{1F1FE}[\u{1F1EA}\u{1F1F9}]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9D6}-\u{1F9DD}][\u{1F3FB}-\u{1F3FF}]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]|[\u261D\u270A-\u270D\u{1F385}\u{1F3C2}\u{1F3C7}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}\u{1F467}\u{1F470}\u{1F472}\u{1F474}-\u{1F476}\u{1F478}\u{1F47C}\u{1F483}\u{1F485}\u{1F4AA}\u{1F574}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F64C}\u{1F64F}\u{1F6C0}\u{1F6CC}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F930}-\u{1F936}\u{1F9B5}\u{1F9B6}\u{1F9D1}-\u{1F9D5}][\u{1F3FB}-\u{1F3FF}]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55\u{1F004}\u{1F0CF}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F236}\u{1F238}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F320}\u{1F32D}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F37E}-\u{1F393}\u{1F3A0}-\u{1F3CA}\u{1F3CF}-\u{1F3D3}\u{1F3E0}-\u{1F3F0}\u{1F3F4}\u{1F3F8}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4FC}\u{1F4FF}-\u{1F53D}\u{1F54B}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F57A}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5FB}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CC}\u{1F6D0}-\u{1F6D2}\u{1F6EB}\u{1F6EC}\u{1F6F4}-\u{1F6F9}\u{1F910}-\u{1F93A}\u{1F93C}-\u{1F93E}\u{1F940}-\u{1F945}\u{1F947}-\u{1F970}\u{1F973}-\u{1F976}\u{1F97A}\u{1F97C}-\u{1F9A2}\u{1F9B0}-\u{1F9B9}\u{1F9C0}-\u{1F9C2}\u{1F9D0}-\u{1F9FF}]|[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\u{1F004}\u{1F0CF}\u{1F170}\u{1F171}\u{1F17E}\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F321}\u{1F324}-\u{1F393}\u{1F396}\u{1F397}\u{1F399}-\u{1F39B}\u{1F39E}-\u{1F3F0}\u{1F3F3}-\u{1F3F5}\u{1F3F7}-\u{1F4FD}\u{1F4FF}-\u{1F53D}\u{1F549}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F56F}\u{1F570}\u{1F573}-\u{1F57A}\u{1F587}\u{1F58A}-\u{1F58D}\u{1F590}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5A5}\u{1F5A8}\u{1F5B1}\u{1F5B2}\u{1F5BC}\u{1F5C2}-\u{1F5C4}\u{1F5D1}-\u{1F5D3}\u{1F5DC}-\u{1F5DE}\u{1F5E1}\u{1F5E3}\u{1F5E8}\u{1F5EF}\u{1F5F3}\u{1F5FA}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CB}-\u{1F6D2}\u{1F6E0}-\u{1F6E5}\u{1F6E9}\u{1F6EB}\u{1F6EC}\u{1F6F0}\u{1F6F3}-\u{1F6F9}\u{1F910}-\u{1F93A}\u{1F93C}-\u{1F93E}\u{1F940}-\u{1F945}\u{1F947}-\u{1F970}\u{1F973}-\u{1F976}\u{1F97A}\u{1F97C}-\u{1F9A2}\u{1F9B0}-\u{1F9B9}\u{1F9C0}-\u{1F9C2}\u{1F9D0}-\u{1F9FF}]\uFE0F|[\u261D\u26F9\u270A-\u270D\u{1F385}\u{1F3C2}-\u{1F3C4}\u{1F3C7}\u{1F3CA}-\u{1F3CC}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}-\u{1F469}\u{1F46E}\u{1F470}-\u{1F478}\u{1F47C}\u{1F481}-\u{1F483}\u{1F485}-\u{1F487}\u{1F4AA}\u{1F574}\u{1F575}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F645}-\u{1F647}\u{1F64B}-\u{1F64F}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F6C0}\u{1F6CC}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F926}\u{1F930}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B5}\u{1F9B6}\u{1F9B8}\u{1F9B9}\u{1F9D1}-\u{1F9DD}]/gu; +}; diff --git a/WebServer/node_modules/emoji-regex/es2015/text.js b/WebServer/node_modules/emoji-regex/es2015/text.js new file mode 100644 index 000000000..d0a771d36 --- /dev/null +++ b/WebServer/node_modules/emoji-regex/es2015/text.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = () => { + // https://mths.be/emoji + return /\u{1F3F4}(?:\u{E0067}\u{E0062}(?:\u{E0065}\u{E006E}\u{E0067}|\u{E0077}\u{E006C}\u{E0073}|\u{E0073}\u{E0063}\u{E0074})\u{E007F}|\u200D\u2620\uFE0F)|\u{1F469}\u200D\u{1F469}\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F468}(?:\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D)?\u{1F468}|[\u{1F468}\u{1F469}]\u200D(?:\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}])|\u{1F466}\u200D\u{1F466}|\u{1F467}\u200D[\u{1F466}\u{1F467}]|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}])|[\u{1F3FB}-\u{1F3FF}]\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}])|\u{1F469}\u200D(?:\u2764\uFE0F\u200D(?:\u{1F48B}\u200D[\u{1F468}\u{1F469}]|[\u{1F468}\u{1F469}])|[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}])|\u{1F469}\u200D\u{1F466}\u200D\u{1F466}|(?:\u{1F441}\uFE0F\u200D\u{1F5E8}|\u{1F469}[\u{1F3FB}-\u{1F3FF}]\u200D[\u2695\u2696\u2708]|\u{1F468}(?:[\u{1F3FB}-\u{1F3FF}]\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}]\uFE0F|[\u{1F46F}\u{1F93C}\u{1F9DE}\u{1F9DF}])\u200D[\u2640\u2642]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9D6}-\u{1F9DD}](?:[\u{1F3FB}-\u{1F3FF}]\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\u{1F469}\u200D[\u2695\u2696\u2708])\uFE0F|\u{1F469}\u200D\u{1F467}\u200D[\u{1F466}\u{1F467}]|\u{1F469}\u200D\u{1F469}\u200D[\u{1F466}\u{1F467}]|\u{1F468}(?:\u200D(?:[\u{1F468}\u{1F469}]\u200D[\u{1F466}\u{1F467}]|[\u{1F466}\u{1F467}])|[\u{1F3FB}-\u{1F3FF}])|\u{1F3F3}\uFE0F\u200D\u{1F308}|\u{1F469}\u200D\u{1F467}|\u{1F469}[\u{1F3FB}-\u{1F3FF}]\u200D[\u{1F33E}\u{1F373}\u{1F393}\u{1F3A4}\u{1F3A8}\u{1F3EB}\u{1F3ED}\u{1F4BB}\u{1F4BC}\u{1F527}\u{1F52C}\u{1F680}\u{1F692}\u{1F9B0}-\u{1F9B3}]|\u{1F469}\u200D\u{1F466}|\u{1F1F6}\u{1F1E6}|\u{1F1FD}\u{1F1F0}|\u{1F1F4}\u{1F1F2}|\u{1F469}[\u{1F3FB}-\u{1F3FF}]|\u{1F1ED}[\u{1F1F0}\u{1F1F2}\u{1F1F3}\u{1F1F7}\u{1F1F9}\u{1F1FA}]|\u{1F1EC}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EE}\u{1F1F1}-\u{1F1F3}\u{1F1F5}-\u{1F1FA}\u{1F1FC}\u{1F1FE}]|\u{1F1EA}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1ED}\u{1F1F7}-\u{1F1FA}]|\u{1F1E8}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1EE}\u{1F1F0}-\u{1F1F5}\u{1F1F7}\u{1F1FA}-\u{1F1FF}]|\u{1F1F2}[\u{1F1E6}\u{1F1E8}-\u{1F1ED}\u{1F1F0}-\u{1F1FF}]|\u{1F1F3}[\u{1F1E6}\u{1F1E8}\u{1F1EA}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F4}\u{1F1F5}\u{1F1F7}\u{1F1FA}\u{1F1FF}]|\u{1F1FC}[\u{1F1EB}\u{1F1F8}]|\u{1F1FA}[\u{1F1E6}\u{1F1EC}\u{1F1F2}\u{1F1F3}\u{1F1F8}\u{1F1FE}\u{1F1FF}]|\u{1F1F0}[\u{1F1EA}\u{1F1EC}-\u{1F1EE}\u{1F1F2}\u{1F1F3}\u{1F1F5}\u{1F1F7}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|\u{1F1EF}[\u{1F1EA}\u{1F1F2}\u{1F1F4}\u{1F1F5}]|\u{1F1F8}[\u{1F1E6}-\u{1F1EA}\u{1F1EC}-\u{1F1F4}\u{1F1F7}-\u{1F1F9}\u{1F1FB}\u{1F1FD}-\u{1F1FF}]|\u{1F1EE}[\u{1F1E8}-\u{1F1EA}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}]|\u{1F1FF}[\u{1F1E6}\u{1F1F2}\u{1F1FC}]|\u{1F1EB}[\u{1F1EE}-\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1F7}]|\u{1F1F5}[\u{1F1E6}\u{1F1EA}-\u{1F1ED}\u{1F1F0}-\u{1F1F3}\u{1F1F7}-\u{1F1F9}\u{1F1FC}\u{1F1FE}]|\u{1F1E9}[\u{1F1EA}\u{1F1EC}\u{1F1EF}\u{1F1F0}\u{1F1F2}\u{1F1F4}\u{1F1FF}]|\u{1F1F9}[\u{1F1E6}\u{1F1E8}\u{1F1E9}\u{1F1EB}-\u{1F1ED}\u{1F1EF}-\u{1F1F4}\u{1F1F7}\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FF}]|\u{1F1E7}[\u{1F1E6}\u{1F1E7}\u{1F1E9}-\u{1F1EF}\u{1F1F1}-\u{1F1F4}\u{1F1F6}-\u{1F1F9}\u{1F1FB}\u{1F1FC}\u{1F1FE}\u{1F1FF}]|[#\*0-9]\uFE0F\u20E3|\u{1F1F1}[\u{1F1E6}-\u{1F1E8}\u{1F1EE}\u{1F1F0}\u{1F1F7}-\u{1F1FB}\u{1F1FE}]|\u{1F1E6}[\u{1F1E8}-\u{1F1EC}\u{1F1EE}\u{1F1F1}\u{1F1F2}\u{1F1F4}\u{1F1F6}-\u{1F1FA}\u{1F1FC}\u{1F1FD}\u{1F1FF}]|\u{1F1F7}[\u{1F1EA}\u{1F1F4}\u{1F1F8}\u{1F1FA}\u{1F1FC}]|\u{1F1FB}[\u{1F1E6}\u{1F1E8}\u{1F1EA}\u{1F1EC}\u{1F1EE}\u{1F1F3}\u{1F1FA}]|\u{1F1FE}[\u{1F1EA}\u{1F1F9}]|[\u{1F3C3}\u{1F3C4}\u{1F3CA}\u{1F46E}\u{1F471}\u{1F473}\u{1F477}\u{1F481}\u{1F482}\u{1F486}\u{1F487}\u{1F645}-\u{1F647}\u{1F64B}\u{1F64D}\u{1F64E}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F926}\u{1F937}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B8}\u{1F9B9}\u{1F9D6}-\u{1F9DD}][\u{1F3FB}-\u{1F3FF}]|[\u26F9\u{1F3CB}\u{1F3CC}\u{1F575}][\u{1F3FB}-\u{1F3FF}]|[\u261D\u270A-\u270D\u{1F385}\u{1F3C2}\u{1F3C7}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}\u{1F467}\u{1F470}\u{1F472}\u{1F474}-\u{1F476}\u{1F478}\u{1F47C}\u{1F483}\u{1F485}\u{1F4AA}\u{1F574}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F64C}\u{1F64F}\u{1F6C0}\u{1F6CC}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F930}-\u{1F936}\u{1F9B5}\u{1F9B6}\u{1F9D1}-\u{1F9D5}][\u{1F3FB}-\u{1F3FF}]|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55\u{1F004}\u{1F0CF}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F236}\u{1F238}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F320}\u{1F32D}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F37E}-\u{1F393}\u{1F3A0}-\u{1F3CA}\u{1F3CF}-\u{1F3D3}\u{1F3E0}-\u{1F3F0}\u{1F3F4}\u{1F3F8}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4FC}\u{1F4FF}-\u{1F53D}\u{1F54B}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F57A}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5FB}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CC}\u{1F6D0}-\u{1F6D2}\u{1F6EB}\u{1F6EC}\u{1F6F4}-\u{1F6F9}\u{1F910}-\u{1F93A}\u{1F93C}-\u{1F93E}\u{1F940}-\u{1F945}\u{1F947}-\u{1F970}\u{1F973}-\u{1F976}\u{1F97A}\u{1F97C}-\u{1F9A2}\u{1F9B0}-\u{1F9B9}\u{1F9C0}-\u{1F9C2}\u{1F9D0}-\u{1F9FF}]|[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299\u{1F004}\u{1F0CF}\u{1F170}\u{1F171}\u{1F17E}\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E6}-\u{1F1FF}\u{1F201}\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}\u{1F251}\u{1F300}-\u{1F321}\u{1F324}-\u{1F393}\u{1F396}\u{1F397}\u{1F399}-\u{1F39B}\u{1F39E}-\u{1F3F0}\u{1F3F3}-\u{1F3F5}\u{1F3F7}-\u{1F4FD}\u{1F4FF}-\u{1F53D}\u{1F549}-\u{1F54E}\u{1F550}-\u{1F567}\u{1F56F}\u{1F570}\u{1F573}-\u{1F57A}\u{1F587}\u{1F58A}-\u{1F58D}\u{1F590}\u{1F595}\u{1F596}\u{1F5A4}\u{1F5A5}\u{1F5A8}\u{1F5B1}\u{1F5B2}\u{1F5BC}\u{1F5C2}-\u{1F5C4}\u{1F5D1}-\u{1F5D3}\u{1F5DC}-\u{1F5DE}\u{1F5E1}\u{1F5E3}\u{1F5E8}\u{1F5EF}\u{1F5F3}\u{1F5FA}-\u{1F64F}\u{1F680}-\u{1F6C5}\u{1F6CB}-\u{1F6D2}\u{1F6E0}-\u{1F6E5}\u{1F6E9}\u{1F6EB}\u{1F6EC}\u{1F6F0}\u{1F6F3}-\u{1F6F9}\u{1F910}-\u{1F93A}\u{1F93C}-\u{1F93E}\u{1F940}-\u{1F945}\u{1F947}-\u{1F970}\u{1F973}-\u{1F976}\u{1F97A}\u{1F97C}-\u{1F9A2}\u{1F9B0}-\u{1F9B9}\u{1F9C0}-\u{1F9C2}\u{1F9D0}-\u{1F9FF}]\uFE0F?|[\u261D\u26F9\u270A-\u270D\u{1F385}\u{1F3C2}-\u{1F3C4}\u{1F3C7}\u{1F3CA}-\u{1F3CC}\u{1F442}\u{1F443}\u{1F446}-\u{1F450}\u{1F466}-\u{1F469}\u{1F46E}\u{1F470}-\u{1F478}\u{1F47C}\u{1F481}-\u{1F483}\u{1F485}-\u{1F487}\u{1F4AA}\u{1F574}\u{1F575}\u{1F57A}\u{1F590}\u{1F595}\u{1F596}\u{1F645}-\u{1F647}\u{1F64B}-\u{1F64F}\u{1F6A3}\u{1F6B4}-\u{1F6B6}\u{1F6C0}\u{1F6CC}\u{1F918}-\u{1F91C}\u{1F91E}\u{1F91F}\u{1F926}\u{1F930}-\u{1F939}\u{1F93D}\u{1F93E}\u{1F9B5}\u{1F9B6}\u{1F9B8}\u{1F9B9}\u{1F9D1}-\u{1F9DD}]/gu; +}; diff --git a/WebServer/node_modules/emoji-regex/index.js b/WebServer/node_modules/emoji-regex/index.js new file mode 100644 index 000000000..e2237a4e8 --- /dev/null +++ b/WebServer/node_modules/emoji-regex/index.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = function () { + // https://mths.be/emoji + return /\uD83C\uDFF4(?:\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74)\uDB40\uDC7F|\u200D\u2620\uFE0F)|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC68(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3])|(?:\uD83C[\uDFFB-\uDFFF])\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3]))|\uD83D\uDC69\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2695\u2696\u2708]|\uD83D\uDC68(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83D\uDC69\u200D[\u2695\u2696\u2708])\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC68(?:\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3])|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDD1-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF9]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD70\uDD73-\uDD76\uDD7A\uDD7C-\uDDA2\uDDB0-\uDDB9\uDDC0-\uDDC2\uDDD0-\uDDFF])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF9]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD70\uDD73-\uDD76\uDD7A\uDD7C-\uDDA2\uDDB0-\uDDB9\uDDC0-\uDDC2\uDDD0-\uDDFF])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDD1-\uDDDD])/g; +}; diff --git a/WebServer/node_modules/emoji-regex/package.json b/WebServer/node_modules/emoji-regex/package.json new file mode 100644 index 000000000..e657c59f4 --- /dev/null +++ b/WebServer/node_modules/emoji-regex/package.json @@ -0,0 +1,81 @@ +{ + "_args": [ + [ + "emoji-regex@7.0.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "emoji-regex@7.0.3", + "_id": "emoji-regex@7.0.3", + "_inBundle": false, + "_integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "_location": "/emoji-regex", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "emoji-regex@7.0.3", + "name": "emoji-regex", + "escapedName": "emoji-regex", + "rawSpec": "7.0.3", + "saveSpec": null, + "fetchSpec": "7.0.3" + }, + "_requiredBy": [ + "/yargs/string-width" + ], + "_resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "_spec": "7.0.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + "bugs": { + "url": "https://github.com/mathiasbynens/emoji-regex/issues" + }, + "description": "A regular expression to match all Emoji-only symbols as per the Unicode Standard.", + "devDependencies": { + "@babel/cli": "^7.0.0", + "@babel/core": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.0.0", + "@babel/preset-env": "^7.0.0", + "mocha": "^5.2.0", + "regexgen": "^1.3.0", + "unicode-11.0.0": "^0.7.7", + "unicode-tr51": "^9.0.1" + }, + "files": [ + "LICENSE-MIT.txt", + "index.js", + "index.d.ts", + "text.js", + "es2015/index.js", + "es2015/text.js" + ], + "homepage": "https://mths.be/emoji-regex", + "keywords": [ + "unicode", + "regex", + "regexp", + "regular expressions", + "code points", + "symbols", + "characters", + "emoji" + ], + "license": "MIT", + "main": "index.js", + "name": "emoji-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/mathiasbynens/emoji-regex.git" + }, + "scripts": { + "build": "rm -rf -- es2015; babel src -d .; NODE_ENV=es2015 babel src -d ./es2015; node script/inject-sequences.js", + "test": "mocha", + "test:watch": "npm run test -- --watch" + }, + "types": "index.d.ts", + "version": "7.0.3" +} diff --git a/WebServer/node_modules/emoji-regex/text.js b/WebServer/node_modules/emoji-regex/text.js new file mode 100644 index 000000000..199ae3be3 --- /dev/null +++ b/WebServer/node_modules/emoji-regex/text.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = function () { + // https://mths.be/emoji + return /\uD83C\uDFF4(?:\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74)\uDB40\uDC7F|\u200D\u2620\uFE0F)|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC68(?:\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3])|(?:\uD83C[\uDFFB-\uDFFF])\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3]))|\uD83D\uDC69\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2695\u2696\u2708]|\uD83D\uDC68(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83D\uDC69\u200D[\u2695\u2696\u2708])\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC68(?:\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C[\uDFFB-\uDFFF])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDB0-\uDDB3])|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDD1-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF9]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD70\uDD73-\uDD76\uDD7A\uDD7C-\uDDA2\uDDB0-\uDDB9\uDDC0-\uDDC2\uDDD0-\uDDFF])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF9]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD70\uDD73-\uDD76\uDD7A\uDD7C-\uDDA2\uDDB0-\uDDB9\uDDC0-\uDDC2\uDDD0-\uDDFF])\uFE0F?|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDD1-\uDDDD])/g; +}; diff --git a/WebServer/node_modules/end-of-stream/index.js b/WebServer/node_modules/end-of-stream/index.js new file mode 100644 index 000000000..be426c227 --- /dev/null +++ b/WebServer/node_modules/end-of-stream/index.js @@ -0,0 +1,87 @@ +var once = require('once'); + +var noop = function() {}; + +var isRequest = function(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +}; + +var isChildProcess = function(stream) { + return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3 +}; + +var eos = function(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; + + callback = once(callback || noop); + + var ws = stream._writableState; + var rs = stream._readableState; + var readable = opts.readable || (opts.readable !== false && stream.readable); + var writable = opts.writable || (opts.writable !== false && stream.writable); + + var onlegacyfinish = function() { + if (!stream.writable) onfinish(); + }; + + var onfinish = function() { + writable = false; + if (!readable) callback.call(stream); + }; + + var onend = function() { + readable = false; + if (!writable) callback.call(stream); + }; + + var onexit = function(exitCode) { + callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null); + }; + + var onerror = function(err) { + callback.call(stream, err); + }; + + var onclose = function() { + if (readable && !(rs && rs.ended)) return callback.call(stream, new Error('premature close')); + if (writable && !(ws && ws.ended)) return callback.call(stream, new Error('premature close')); + }; + + var onrequest = function() { + stream.req.on('finish', onfinish); + }; + + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest(); + else stream.on('request', onrequest); + } else if (writable && !ws) { // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } + + if (isChildProcess(stream)) stream.on('exit', onexit); + + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); + + return function() { + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('exit', onexit); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; +}; + +module.exports = eos; diff --git a/WebServer/node_modules/end-of-stream/package.json b/WebServer/node_modules/end-of-stream/package.json new file mode 100644 index 000000000..71f54f324 --- /dev/null +++ b/WebServer/node_modules/end-of-stream/package.json @@ -0,0 +1,65 @@ +{ + "_args": [ + [ + "end-of-stream@1.4.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "end-of-stream@1.4.1", + "_id": "end-of-stream@1.4.1", + "_inBundle": false, + "_integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "_location": "/end-of-stream", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "end-of-stream@1.4.1", + "name": "end-of-stream", + "escapedName": "end-of-stream", + "rawSpec": "1.4.1", + "saveSpec": null, + "fetchSpec": "1.4.1" + }, + "_requiredBy": [ + "/pump" + ], + "_resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "_spec": "1.4.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Mathias Buus", + "email": "mathiasbuus@gmail.com" + }, + "bugs": { + "url": "https://github.com/mafintosh/end-of-stream/issues" + }, + "dependencies": { + "once": "^1.4.0" + }, + "description": "Call a callback when a readable/writable/duplex stream has completed or failed.", + "files": [ + "index.js" + ], + "homepage": "https://github.com/mafintosh/end-of-stream", + "keywords": [ + "stream", + "streams", + "callback", + "finish", + "close", + "end", + "wait" + ], + "license": "MIT", + "main": "index.js", + "name": "end-of-stream", + "repository": { + "type": "git", + "url": "git://github.com/mafintosh/end-of-stream.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.4.1" +} diff --git a/WebServer/node_modules/es-abstract/GetIntrinsic.js b/WebServer/node_modules/es-abstract/GetIntrinsic.js new file mode 100644 index 000000000..62dbf05d6 --- /dev/null +++ b/WebServer/node_modules/es-abstract/GetIntrinsic.js @@ -0,0 +1,177 @@ +'use strict'; + +/* globals + Set, + Map, + WeakSet, + WeakMap, + + Promise, + + Symbol, + Proxy, + + Atomics, + SharedArrayBuffer, + + ArrayBuffer, + DataView, + Uint8Array, + Float32Array, + Float64Array, + Int8Array, + Int16Array, + Int32Array, + Uint8ClampedArray, + Uint16Array, + Uint32Array, +*/ + +var undefined; // eslint-disable-line no-shadow-restricted-names + +var ThrowTypeError = Object.getOwnPropertyDescriptor + ? (function () { return Object.getOwnPropertyDescriptor(arguments, 'callee').get; }()) + : function () { throw new TypeError(); }; + +var hasSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'; + +var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto + +var generator; // = function * () {}; +var generatorFunction = generator ? getProto(generator) : undefined; +var asyncFn; // async function() {}; +var asyncFunction = asyncFn ? asyncFn.constructor : undefined; +var asyncGen; // async function * () {}; +var asyncGenFunction = asyncGen ? getProto(asyncGen) : undefined; +var asyncGenIterator = asyncGen ? asyncGen() : undefined; + +var TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array); + +var INTRINSICS = { + '$ %Array%': Array, + '$ %ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer, + '$ %ArrayBufferPrototype%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer.prototype, + '$ %ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined, + '$ %ArrayPrototype%': Array.prototype, + '$ %ArrayProto_entries%': Array.prototype.entries, + '$ %ArrayProto_forEach%': Array.prototype.forEach, + '$ %ArrayProto_keys%': Array.prototype.keys, + '$ %ArrayProto_values%': Array.prototype.values, + '$ %AsyncFromSyncIteratorPrototype%': undefined, + '$ %AsyncFunction%': asyncFunction, + '$ %AsyncFunctionPrototype%': asyncFunction ? asyncFunction.prototype : undefined, + '$ %AsyncGenerator%': asyncGen ? getProto(asyncGenIterator) : undefined, + '$ %AsyncGeneratorFunction%': asyncGenFunction, + '$ %AsyncGeneratorPrototype%': asyncGenFunction ? asyncGenFunction.prototype : undefined, + '$ %AsyncIteratorPrototype%': asyncGenIterator && hasSymbols && Symbol.asyncIterator ? asyncGenIterator[Symbol.asyncIterator]() : undefined, + '$ %Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics, + '$ %Boolean%': Boolean, + '$ %BooleanPrototype%': Boolean.prototype, + '$ %DataView%': typeof DataView === 'undefined' ? undefined : DataView, + '$ %DataViewPrototype%': typeof DataView === 'undefined' ? undefined : DataView.prototype, + '$ %Date%': Date, + '$ %DatePrototype%': Date.prototype, + '$ %decodeURI%': decodeURI, + '$ %decodeURIComponent%': decodeURIComponent, + '$ %encodeURI%': encodeURI, + '$ %encodeURIComponent%': encodeURIComponent, + '$ %Error%': Error, + '$ %ErrorPrototype%': Error.prototype, + '$ %eval%': eval, // eslint-disable-line no-eval + '$ %EvalError%': EvalError, + '$ %EvalErrorPrototype%': EvalError.prototype, + '$ %Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array, + '$ %Float32ArrayPrototype%': typeof Float32Array === 'undefined' ? undefined : Float32Array.prototype, + '$ %Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array, + '$ %Float64ArrayPrototype%': typeof Float64Array === 'undefined' ? undefined : Float64Array.prototype, + '$ %Function%': Function, + '$ %FunctionPrototype%': Function.prototype, + '$ %Generator%': generator ? getProto(generator()) : undefined, + '$ %GeneratorFunction%': generatorFunction, + '$ %GeneratorPrototype%': generatorFunction ? generatorFunction.prototype : undefined, + '$ %Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array, + '$ %Int8ArrayPrototype%': typeof Int8Array === 'undefined' ? undefined : Int8Array.prototype, + '$ %Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array, + '$ %Int16ArrayPrototype%': typeof Int16Array === 'undefined' ? undefined : Int8Array.prototype, + '$ %Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array, + '$ %Int32ArrayPrototype%': typeof Int32Array === 'undefined' ? undefined : Int32Array.prototype, + '$ %isFinite%': isFinite, + '$ %isNaN%': isNaN, + '$ %IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined, + '$ %JSON%': JSON, + '$ %JSONParse%': JSON.parse, + '$ %Map%': typeof Map === 'undefined' ? undefined : Map, + '$ %MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()), + '$ %MapPrototype%': typeof Map === 'undefined' ? undefined : Map.prototype, + '$ %Math%': Math, + '$ %Number%': Number, + '$ %NumberPrototype%': Number.prototype, + '$ %Object%': Object, + '$ %ObjectPrototype%': Object.prototype, + '$ %ObjProto_toString%': Object.prototype.toString, + '$ %ObjProto_valueOf%': Object.prototype.valueOf, + '$ %parseFloat%': parseFloat, + '$ %parseInt%': parseInt, + '$ %Promise%': typeof Promise === 'undefined' ? undefined : Promise, + '$ %PromisePrototype%': typeof Promise === 'undefined' ? undefined : Promise.prototype, + '$ %PromiseProto_then%': typeof Promise === 'undefined' ? undefined : Promise.prototype.then, + '$ %Promise_all%': typeof Promise === 'undefined' ? undefined : Promise.all, + '$ %Promise_reject%': typeof Promise === 'undefined' ? undefined : Promise.reject, + '$ %Promise_resolve%': typeof Promise === 'undefined' ? undefined : Promise.resolve, + '$ %Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy, + '$ %RangeError%': RangeError, + '$ %RangeErrorPrototype%': RangeError.prototype, + '$ %ReferenceError%': ReferenceError, + '$ %ReferenceErrorPrototype%': ReferenceError.prototype, + '$ %Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect, + '$ %RegExp%': RegExp, + '$ %RegExpPrototype%': RegExp.prototype, + '$ %Set%': typeof Set === 'undefined' ? undefined : Set, + '$ %SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()), + '$ %SetPrototype%': typeof Set === 'undefined' ? undefined : Set.prototype, + '$ %SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer, + '$ %SharedArrayBufferPrototype%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer.prototype, + '$ %String%': String, + '$ %StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined, + '$ %StringPrototype%': String.prototype, + '$ %Symbol%': hasSymbols ? Symbol : undefined, + '$ %SymbolPrototype%': hasSymbols ? Symbol.prototype : undefined, + '$ %SyntaxError%': SyntaxError, + '$ %SyntaxErrorPrototype%': SyntaxError.prototype, + '$ %ThrowTypeError%': ThrowTypeError, + '$ %TypedArray%': TypedArray, + '$ %TypedArrayPrototype%': TypedArray ? TypedArray.prototype : undefined, + '$ %TypeError%': TypeError, + '$ %TypeErrorPrototype%': TypeError.prototype, + '$ %Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array, + '$ %Uint8ArrayPrototype%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array.prototype, + '$ %Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray, + '$ %Uint8ClampedArrayPrototype%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray.prototype, + '$ %Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array, + '$ %Uint16ArrayPrototype%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array.prototype, + '$ %Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array, + '$ %Uint32ArrayPrototype%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array.prototype, + '$ %URIError%': URIError, + '$ %URIErrorPrototype%': URIError.prototype, + '$ %WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap, + '$ %WeakMapPrototype%': typeof WeakMap === 'undefined' ? undefined : WeakMap.prototype, + '$ %WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet, + '$ %WeakSetPrototype%': typeof WeakSet === 'undefined' ? undefined : WeakSet.prototype +}; + +module.exports = function GetIntrinsic(name, allowMissing) { + if (arguments.length > 1 && typeof allowMissing !== 'boolean') { + throw new TypeError('"allowMissing" argument must be a boolean'); + } + + var key = '$ ' + name; + if (!(key in INTRINSICS)) { + throw new SyntaxError('intrinsic ' + name + ' does not exist!'); + } + + // istanbul ignore if // hopefully this is impossible to test :-) + if (typeof INTRINSICS[key] === 'undefined' && !allowMissing) { + throw new TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!'); + } + return INTRINSICS[key]; +}; diff --git a/WebServer/node_modules/es-abstract/es2015.js b/WebServer/node_modules/es-abstract/es2015.js new file mode 100644 index 000000000..4b83657ff --- /dev/null +++ b/WebServer/node_modules/es-abstract/es2015.js @@ -0,0 +1,790 @@ +'use strict'; + +var has = require('has'); +var toPrimitive = require('es-to-primitive/es6'); +var keys = require('object-keys'); + +var GetIntrinsic = require('./GetIntrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); +var $SyntaxError = GetIntrinsic('%SyntaxError%'); +var $Array = GetIntrinsic('%Array%'); +var $String = GetIntrinsic('%String%'); +var $Object = GetIntrinsic('%Object%'); +var $Number = GetIntrinsic('%Number%'); +var $Symbol = GetIntrinsic('%Symbol%', true); +var $RegExp = GetIntrinsic('%RegExp%'); + +var hasSymbols = !!$Symbol; + +var assertRecord = require('./helpers/assertRecord'); +var $isNaN = require('./helpers/isNaN'); +var $isFinite = require('./helpers/isFinite'); +var MAX_SAFE_INTEGER = $Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1; + +var assign = require('./helpers/assign'); +var sign = require('./helpers/sign'); +var mod = require('./helpers/mod'); +var isPrimitive = require('./helpers/isPrimitive'); +var parseInteger = parseInt; +var bind = require('function-bind'); +var arraySlice = bind.call(Function.call, $Array.prototype.slice); +var strSlice = bind.call(Function.call, $String.prototype.slice); +var isBinary = bind.call(Function.call, $RegExp.prototype.test, /^0b[01]+$/i); +var isOctal = bind.call(Function.call, $RegExp.prototype.test, /^0o[0-7]+$/i); +var regexExec = bind.call(Function.call, $RegExp.prototype.exec); +var nonWS = ['\u0085', '\u200b', '\ufffe'].join(''); +var nonWSregex = new $RegExp('[' + nonWS + ']', 'g'); +var hasNonWS = bind.call(Function.call, $RegExp.prototype.test, nonWSregex); +var invalidHexLiteral = /^[-+]0x[0-9a-f]+$/i; +var isInvalidHexLiteral = bind.call(Function.call, $RegExp.prototype.test, invalidHexLiteral); +var $charCodeAt = bind.call(Function.call, $String.prototype.charCodeAt); + +var toStr = bind.call(Function.call, Object.prototype.toString); + +var $NumberValueOf = bind.call(Function.call, GetIntrinsic('%NumberPrototype%').valueOf); +var $BooleanValueOf = bind.call(Function.call, GetIntrinsic('%BooleanPrototype%').valueOf); +var $StringValueOf = bind.call(Function.call, GetIntrinsic('%StringPrototype%').valueOf); +var $DateValueOf = bind.call(Function.call, GetIntrinsic('%DatePrototype%').valueOf); + +var $floor = Math.floor; +var $abs = Math.abs; + +var $ObjectCreate = Object.create; +var $gOPD = $Object.getOwnPropertyDescriptor; + +var $isExtensible = $Object.isExtensible; + +var $defineProperty = $Object.defineProperty; + +// whitespace from: http://es5.github.io/#x15.5.4.20 +// implementation from https://github.com/es-shims/es5-shim/blob/v3.4.0/es5-shim.js#L1304-L1324 +var ws = [ + '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003', + '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028', + '\u2029\uFEFF' +].join(''); +var trimRegex = new RegExp('(^[' + ws + ']+)|([' + ws + ']+$)', 'g'); +var replace = bind.call(Function.call, $String.prototype.replace); +var trim = function (value) { + return replace(value, trimRegex, ''); +}; + +var ES5 = require('./es5'); + +var hasRegExpMatcher = require('is-regex'); + +// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-abstract-operations +var ES6 = assign(assign({}, ES5), { + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-call-f-v-args + Call: function Call(F, V) { + var args = arguments.length > 2 ? arguments[2] : []; + if (!this.IsCallable(F)) { + throw new $TypeError(F + ' is not a function'); + } + return F.apply(V, args); + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-toprimitive + ToPrimitive: toPrimitive, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-toboolean + // ToBoolean: ES5.ToBoolean, + + // https://ecma-international.org/ecma-262/6.0/#sec-tonumber + ToNumber: function ToNumber(argument) { + var value = isPrimitive(argument) ? argument : toPrimitive(argument, $Number); + if (typeof value === 'symbol') { + throw new $TypeError('Cannot convert a Symbol value to a number'); + } + if (typeof value === 'string') { + if (isBinary(value)) { + return this.ToNumber(parseInteger(strSlice(value, 2), 2)); + } else if (isOctal(value)) { + return this.ToNumber(parseInteger(strSlice(value, 2), 8)); + } else if (hasNonWS(value) || isInvalidHexLiteral(value)) { + return NaN; + } else { + var trimmed = trim(value); + if (trimmed !== value) { + return this.ToNumber(trimmed); + } + } + } + return $Number(value); + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tointeger + // ToInteger: ES5.ToNumber, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-toint32 + // ToInt32: ES5.ToInt32, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-touint32 + // ToUint32: ES5.ToUint32, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-toint16 + ToInt16: function ToInt16(argument) { + var int16bit = this.ToUint16(argument); + return int16bit >= 0x8000 ? int16bit - 0x10000 : int16bit; + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-touint16 + // ToUint16: ES5.ToUint16, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-toint8 + ToInt8: function ToInt8(argument) { + var int8bit = this.ToUint8(argument); + return int8bit >= 0x80 ? int8bit - 0x100 : int8bit; + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-touint8 + ToUint8: function ToUint8(argument) { + var number = this.ToNumber(argument); + if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; } + var posInt = sign(number) * $floor($abs(number)); + return mod(posInt, 0x100); + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-touint8clamp + ToUint8Clamp: function ToUint8Clamp(argument) { + var number = this.ToNumber(argument); + if ($isNaN(number) || number <= 0) { return 0; } + if (number >= 0xFF) { return 0xFF; } + var f = $floor(argument); + if (f + 0.5 < number) { return f + 1; } + if (number < f + 0.5) { return f; } + if (f % 2 !== 0) { return f + 1; } + return f; + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tostring + ToString: function ToString(argument) { + if (typeof argument === 'symbol') { + throw new $TypeError('Cannot convert a Symbol value to a string'); + } + return $String(argument); + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-toobject + ToObject: function ToObject(value) { + this.RequireObjectCoercible(value); + return $Object(value); + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-topropertykey + ToPropertyKey: function ToPropertyKey(argument) { + var key = this.ToPrimitive(argument, $String); + return typeof key === 'symbol' ? key : this.ToString(key); + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength + ToLength: function ToLength(argument) { + var len = this.ToInteger(argument); + if (len <= 0) { return 0; } // includes converting -0 to +0 + if (len > MAX_SAFE_INTEGER) { return MAX_SAFE_INTEGER; } + return len; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-canonicalnumericindexstring + CanonicalNumericIndexString: function CanonicalNumericIndexString(argument) { + if (toStr(argument) !== '[object String]') { + throw new $TypeError('must be a string'); + } + if (argument === '-0') { return -0; } + var n = this.ToNumber(argument); + if (this.SameValue(this.ToString(n), argument)) { return n; } + return void 0; + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-requireobjectcoercible + RequireObjectCoercible: ES5.CheckObjectCoercible, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-isarray + IsArray: $Array.isArray || function IsArray(argument) { + return toStr(argument) === '[object Array]'; + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-iscallable + // IsCallable: ES5.IsCallable, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-isconstructor + IsConstructor: function IsConstructor(argument) { + return typeof argument === 'function' && !!argument.prototype; // unfortunately there's no way to truly check this without try/catch `new argument` + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-isextensible-o + IsExtensible: Object.preventExtensions + ? function IsExtensible(obj) { + if (isPrimitive(obj)) { + return false; + } + return $isExtensible(obj); + } + : function isExtensible(obj) { return true; }, // eslint-disable-line no-unused-vars + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-isinteger + IsInteger: function IsInteger(argument) { + if (typeof argument !== 'number' || $isNaN(argument) || !$isFinite(argument)) { + return false; + } + var abs = $abs(argument); + return $floor(abs) === abs; + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ispropertykey + IsPropertyKey: function IsPropertyKey(argument) { + return typeof argument === 'string' || typeof argument === 'symbol'; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-isregexp + IsRegExp: function IsRegExp(argument) { + if (!argument || typeof argument !== 'object') { + return false; + } + if (hasSymbols) { + var isRegExp = argument[$Symbol.match]; + if (typeof isRegExp !== 'undefined') { + return ES5.ToBoolean(isRegExp); + } + } + return hasRegExpMatcher(argument); + }, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevalue + // SameValue: ES5.SameValue, + + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero + SameValueZero: function SameValueZero(x, y) { + return (x === y) || ($isNaN(x) && $isNaN(y)); + }, + + /** + * 7.3.2 GetV (V, P) + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let O be ToObject(V). + * 3. ReturnIfAbrupt(O). + * 4. Return O.[[Get]](P, V). + */ + GetV: function GetV(V, P) { + // 7.3.2.1 + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + + // 7.3.2.2-3 + var O = this.ToObject(V); + + // 7.3.2.4 + return O[P]; + }, + + /** + * 7.3.9 - https://ecma-international.org/ecma-262/6.0/#sec-getmethod + * 1. Assert: IsPropertyKey(P) is true. + * 2. Let func be GetV(O, P). + * 3. ReturnIfAbrupt(func). + * 4. If func is either undefined or null, return undefined. + * 5. If IsCallable(func) is false, throw a TypeError exception. + * 6. Return func. + */ + GetMethod: function GetMethod(O, P) { + // 7.3.9.1 + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + + // 7.3.9.2 + var func = this.GetV(O, P); + + // 7.3.9.4 + if (func == null) { + return void 0; + } + + // 7.3.9.5 + if (!this.IsCallable(func)) { + throw new $TypeError(P + 'is not a function'); + } + + // 7.3.9.6 + return func; + }, + + /** + * 7.3.1 Get (O, P) - https://ecma-international.org/ecma-262/6.0/#sec-get-o-p + * 1. Assert: Type(O) is Object. + * 2. Assert: IsPropertyKey(P) is true. + * 3. Return O.[[Get]](P, O). + */ + Get: function Get(O, P) { + // 7.3.1.1 + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + // 7.3.1.2 + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + // 7.3.1.3 + return O[P]; + }, + + Type: function Type(x) { + if (typeof x === 'symbol') { + return 'Symbol'; + } + return ES5.Type(x); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-speciesconstructor + SpeciesConstructor: function SpeciesConstructor(O, defaultConstructor) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + var C = O.constructor; + if (typeof C === 'undefined') { + return defaultConstructor; + } + if (this.Type(C) !== 'Object') { + throw new $TypeError('O.constructor is not an Object'); + } + var S = hasSymbols && $Symbol.species ? C[$Symbol.species] : void 0; + if (S == null) { + return defaultConstructor; + } + if (this.IsConstructor(S)) { + return S; + } + throw new $TypeError('no constructor found'); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-completepropertydescriptor + CompletePropertyDescriptor: function CompletePropertyDescriptor(Desc) { + assertRecord(this, 'Property Descriptor', 'Desc', Desc); + + if (this.IsGenericDescriptor(Desc) || this.IsDataDescriptor(Desc)) { + if (!has(Desc, '[[Value]]')) { + Desc['[[Value]]'] = void 0; + } + if (!has(Desc, '[[Writable]]')) { + Desc['[[Writable]]'] = false; + } + } else { + if (!has(Desc, '[[Get]]')) { + Desc['[[Get]]'] = void 0; + } + if (!has(Desc, '[[Set]]')) { + Desc['[[Set]]'] = void 0; + } + } + if (!has(Desc, '[[Enumerable]]')) { + Desc['[[Enumerable]]'] = false; + } + if (!has(Desc, '[[Configurable]]')) { + Desc['[[Configurable]]'] = false; + } + return Desc; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-set-o-p-v-throw + Set: function Set(O, P, V, Throw) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('O must be an Object'); + } + if (!this.IsPropertyKey(P)) { + throw new $TypeError('P must be a Property Key'); + } + if (this.Type(Throw) !== 'Boolean') { + throw new $TypeError('Throw must be a Boolean'); + } + if (Throw) { + O[P] = V; + return true; + } else { + try { + O[P] = V; + } catch (e) { + return false; + } + } + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-hasownproperty + HasOwnProperty: function HasOwnProperty(O, P) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('O must be an Object'); + } + if (!this.IsPropertyKey(P)) { + throw new $TypeError('P must be a Property Key'); + } + return has(O, P); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-hasproperty + HasProperty: function HasProperty(O, P) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('O must be an Object'); + } + if (!this.IsPropertyKey(P)) { + throw new $TypeError('P must be a Property Key'); + } + return P in O; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-isconcatspreadable + IsConcatSpreadable: function IsConcatSpreadable(O) { + if (this.Type(O) !== 'Object') { + return false; + } + if (hasSymbols && typeof $Symbol.isConcatSpreadable === 'symbol') { + var spreadable = this.Get(O, Symbol.isConcatSpreadable); + if (typeof spreadable !== 'undefined') { + return this.ToBoolean(spreadable); + } + } + return this.IsArray(O); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-invoke + Invoke: function Invoke(O, P) { + if (!this.IsPropertyKey(P)) { + throw new $TypeError('P must be a Property Key'); + } + var argumentsList = arraySlice(arguments, 2); + var func = this.GetV(O, P); + return this.Call(func, O, argumentsList); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-getiterator + GetIterator: function GetIterator(obj, method) { + if (!hasSymbols) { + throw new SyntaxError('ES.GetIterator depends on native iterator support.'); + } + + var actualMethod = method; + if (arguments.length < 2) { + actualMethod = this.GetMethod(obj, $Symbol.iterator); + } + var iterator = this.Call(actualMethod, obj); + if (this.Type(iterator) !== 'Object') { + throw new $TypeError('iterator must return an object'); + } + + return iterator; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-iteratornext + IteratorNext: function IteratorNext(iterator, value) { + var result = this.Invoke(iterator, 'next', arguments.length < 2 ? [] : [value]); + if (this.Type(result) !== 'Object') { + throw new $TypeError('iterator next must return an object'); + } + return result; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-iteratorcomplete + IteratorComplete: function IteratorComplete(iterResult) { + if (this.Type(iterResult) !== 'Object') { + throw new $TypeError('Assertion failed: Type(iterResult) is not Object'); + } + return this.ToBoolean(this.Get(iterResult, 'done')); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-iteratorvalue + IteratorValue: function IteratorValue(iterResult) { + if (this.Type(iterResult) !== 'Object') { + throw new $TypeError('Assertion failed: Type(iterResult) is not Object'); + } + return this.Get(iterResult, 'value'); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-iteratorstep + IteratorStep: function IteratorStep(iterator) { + var result = this.IteratorNext(iterator); + var done = this.IteratorComplete(result); + return done === true ? false : result; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-iteratorclose + IteratorClose: function IteratorClose(iterator, completion) { + if (this.Type(iterator) !== 'Object') { + throw new $TypeError('Assertion failed: Type(iterator) is not Object'); + } + if (!this.IsCallable(completion)) { + throw new $TypeError('Assertion failed: completion is not a thunk for a Completion Record'); + } + var completionThunk = completion; + + var iteratorReturn = this.GetMethod(iterator, 'return'); + + if (typeof iteratorReturn === 'undefined') { + return completionThunk(); + } + + var completionRecord; + try { + var innerResult = this.Call(iteratorReturn, iterator, []); + } catch (e) { + // if we hit here, then "e" is the innerResult completion that needs re-throwing + + // if the completion is of type "throw", this will throw. + completionRecord = completionThunk(); + completionThunk = null; // ensure it's not called twice. + + // if not, then return the innerResult completion + throw e; + } + completionRecord = completionThunk(); // if innerResult worked, then throw if the completion does + completionThunk = null; // ensure it's not called twice. + + if (this.Type(innerResult) !== 'Object') { + throw new $TypeError('iterator .return must return an object'); + } + + return completionRecord; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-createiterresultobject + CreateIterResultObject: function CreateIterResultObject(value, done) { + if (this.Type(done) !== 'Boolean') { + throw new $TypeError('Assertion failed: Type(done) is not Boolean'); + } + return { + value: value, + done: done + }; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-regexpexec + RegExpExec: function RegExpExec(R, S) { + if (this.Type(R) !== 'Object') { + throw new $TypeError('R must be an Object'); + } + if (this.Type(S) !== 'String') { + throw new $TypeError('S must be a String'); + } + var exec = this.Get(R, 'exec'); + if (this.IsCallable(exec)) { + var result = this.Call(exec, R, [S]); + if (result === null || this.Type(result) === 'Object') { + return result; + } + throw new $TypeError('"exec" method must return `null` or an Object'); + } + return regexExec(R, S); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-arrayspeciescreate + ArraySpeciesCreate: function ArraySpeciesCreate(originalArray, length) { + if (!this.IsInteger(length) || length < 0) { + throw new $TypeError('Assertion failed: length must be an integer >= 0'); + } + var len = length === 0 ? 0 : length; + var C; + var isArray = this.IsArray(originalArray); + if (isArray) { + C = this.Get(originalArray, 'constructor'); + // TODO: figure out how to make a cross-realm normal Array, a same-realm Array + // if (this.IsConstructor(C)) { + // if C is another realm's Array, C = undefined + // Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(Array))) === null ? + // } + if (this.Type(C) === 'Object' && hasSymbols && $Symbol.species) { + C = this.Get(C, $Symbol.species); + if (C === null) { + C = void 0; + } + } + } + if (typeof C === 'undefined') { + return $Array(len); + } + if (!this.IsConstructor(C)) { + throw new $TypeError('C must be a constructor'); + } + return new C(len); // this.Construct(C, len); + }, + + CreateDataProperty: function CreateDataProperty(O, P, V) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + var oldDesc = $gOPD(O, P); + var extensible = oldDesc || (typeof $isExtensible !== 'function' || $isExtensible(O)); + var immutable = oldDesc && (!oldDesc.writable || !oldDesc.configurable); + if (immutable || !extensible) { + return false; + } + var newDesc = { + configurable: true, + enumerable: true, + value: V, + writable: true + }; + $defineProperty(O, P, newDesc); + return true; + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-createdatapropertyorthrow + CreateDataPropertyOrThrow: function CreateDataPropertyOrThrow(O, P, V) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + var success = this.CreateDataProperty(O, P, V); + if (!success) { + throw new $TypeError('unable to create data property'); + } + return success; + }, + + // https://www.ecma-international.org/ecma-262/6.0/#sec-objectcreate + ObjectCreate: function ObjectCreate(proto, internalSlotsList) { + if (proto !== null && this.Type(proto) !== 'Object') { + throw new $TypeError('Assertion failed: proto must be null or an object'); + } + var slots = arguments.length < 2 ? [] : internalSlotsList; + if (slots.length > 0) { + throw new $SyntaxError('es-abstract does not yet support internal slots'); + } + + if (proto === null && !$ObjectCreate) { + throw new $SyntaxError('native Object.create support is required to create null objects'); + } + + return $ObjectCreate(proto); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-advancestringindex + AdvanceStringIndex: function AdvanceStringIndex(S, index, unicode) { + if (this.Type(S) !== 'String') { + throw new $TypeError('S must be a String'); + } + if (!this.IsInteger(index) || index < 0 || index > MAX_SAFE_INTEGER) { + throw new $TypeError('Assertion failed: length must be an integer >= 0 and <= 2**53'); + } + if (this.Type(unicode) !== 'Boolean') { + throw new $TypeError('Assertion failed: unicode must be a Boolean'); + } + if (!unicode) { + return index + 1; + } + var length = S.length; + if ((index + 1) >= length) { + return index + 1; + } + + var first = $charCodeAt(S, index); + if (first < 0xD800 || first > 0xDBFF) { + return index + 1; + } + + var second = $charCodeAt(S, index + 1); + if (second < 0xDC00 || second > 0xDFFF) { + return index + 1; + } + + return index + 2; + }, + + // https://www.ecma-international.org/ecma-262/6.0/#sec-createmethodproperty + CreateMethodProperty: function CreateMethodProperty(O, P, V) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + + var newDesc = { + configurable: true, + enumerable: false, + value: V, + writable: true + }; + return !!$defineProperty(O, P, newDesc); + }, + + // https://www.ecma-international.org/ecma-262/6.0/#sec-definepropertyorthrow + DefinePropertyOrThrow: function DefinePropertyOrThrow(O, P, desc) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + + return !!$defineProperty(O, P, desc); + }, + + // https://www.ecma-international.org/ecma-262/6.0/#sec-deletepropertyorthrow + DeletePropertyOrThrow: function DeletePropertyOrThrow(O, P) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + + if (!this.IsPropertyKey(P)) { + throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true'); + } + + var success = delete O[P]; + if (!success) { + throw new TypeError('Attempt to delete property failed.'); + } + return success; + }, + + // https://www.ecma-international.org/ecma-262/6.0/#sec-enumerableownnames + EnumerableOwnNames: function EnumerableOwnNames(O) { + if (this.Type(O) !== 'Object') { + throw new $TypeError('Assertion failed: Type(O) is not Object'); + } + + return keys(O); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-number-prototype-object + thisNumberValue: function thisNumberValue(value) { + if (this.Type(value) === 'Number') { + return value; + } + + return $NumberValueOf(value); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-boolean-prototype-object + thisBooleanValue: function thisBooleanValue(value) { + if (this.Type(value) === 'Boolean') { + return value; + } + + return $BooleanValueOf(value); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-string-prototype-object + thisStringValue: function thisStringValue(value) { + if (this.Type(value) === 'String') { + return value; + } + + return $StringValueOf(value); + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-date-prototype-object + thisTimeValue: function thisTimeValue(value) { + return $DateValueOf(value); + } +}); + +delete ES6.CheckObjectCoercible; // renamed in ES6 to RequireObjectCoercible + +module.exports = ES6; diff --git a/WebServer/node_modules/es-abstract/es2016.js b/WebServer/node_modules/es-abstract/es2016.js new file mode 100644 index 000000000..c9166cea7 --- /dev/null +++ b/WebServer/node_modules/es-abstract/es2016.js @@ -0,0 +1,16 @@ +'use strict'; + +var ES2015 = require('./es2015'); +var assign = require('./helpers/assign'); + +var ES2016 = assign(assign({}, ES2015), { + // https://github.com/tc39/ecma262/pull/60 + SameValueNonNumber: function SameValueNonNumber(x, y) { + if (typeof x === 'number' || typeof x !== typeof y) { + throw new TypeError('SameValueNonNumber requires two non-number values of the same type.'); + } + return this.SameValue(x, y); + } +}); + +module.exports = ES2016; diff --git a/WebServer/node_modules/es-abstract/es2017.js b/WebServer/node_modules/es-abstract/es2017.js new file mode 100644 index 000000000..cdef99c11 --- /dev/null +++ b/WebServer/node_modules/es-abstract/es2017.js @@ -0,0 +1,54 @@ +'use strict'; + +var bind = require('function-bind'); + +var ES2016 = require('./es2016'); +var assign = require('./helpers/assign'); +var forEach = require('./helpers/forEach'); + +var GetIntrinsic = require('./GetIntrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); +var $isEnumerable = bind.call(Function.call, GetIntrinsic('%ObjectPrototype%').propertyIsEnumerable); +var $pushApply = bind.call(Function.apply, GetIntrinsic('%ArrayPrototype%').push); + +var ES2017 = assign(assign({}, ES2016), { + ToIndex: function ToIndex(value) { + if (typeof value === 'undefined') { + return 0; + } + var integerIndex = this.ToInteger(value); + if (integerIndex < 0) { + throw new RangeError('index must be >= 0'); + } + var index = this.ToLength(integerIndex); + if (!this.SameValueZero(integerIndex, index)) { + throw new RangeError('index must be >= 0 and < 2 ** 53 - 1'); + } + return index; + }, + + // https://www.ecma-international.org/ecma-262/8.0/#sec-enumerableownproperties + EnumerableOwnProperties: function EnumerableOwnProperties(O, kind) { + var keys = ES2016.EnumerableOwnNames(O); + if (kind === 'key') { + return keys; + } + if (kind === 'value' || kind === 'key+value') { + var results = []; + forEach(keys, function (key) { + if ($isEnumerable(O, key)) { + $pushApply(results, [ + kind === 'value' ? O[key] : [key, O[key]] + ]); + } + }); + return results; + } + throw new $TypeError('Assertion failed: "kind" is not "key", "value", or "key+value": ' + kind); + } +}); + +delete ES2017.EnumerableOwnNames; // replaced with EnumerableOwnProperties + +module.exports = ES2017; diff --git a/WebServer/node_modules/es-abstract/es2018.js b/WebServer/node_modules/es-abstract/es2018.js new file mode 100644 index 000000000..065dc8360 --- /dev/null +++ b/WebServer/node_modules/es-abstract/es2018.js @@ -0,0 +1,149 @@ +'use strict'; + +var bind = require('function-bind'); +var keys = require('object-keys'); + +var ES2017 = require('./es2017'); +var assign = require('./helpers/assign'); +var forEach = require('./helpers/forEach'); + +var GetIntrinsic = require('./GetIntrinsic'); + +var $String = GetIntrinsic('%String%'); +var $Object = GetIntrinsic('%Object%'); + +var $SymbolProto = GetIntrinsic('%SymbolPrototype%', true); +var $SymbolValueOf = $SymbolProto ? bind.call(Function.call, $SymbolProto.valueOf) : null; +var $StringProto = GetIntrinsic('%StringPrototype%'); +var $charAt = bind.call(Function.call, $StringProto.charAt); + +var $PromiseResolveOrig = GetIntrinsic('%Promise_resolve%', true); +var $PromiseResolve = $PromiseResolveOrig ? bind.call(Function.call, $PromiseResolveOrig) : null; + +var $isEnumerable = bind.call(Function.call, GetIntrinsic('%ObjectPrototype%').propertyIsEnumerable); +var $pushApply = bind.call(Function.apply, GetIntrinsic('%ArrayPrototype%').push); +var $gOPS = $SymbolValueOf ? $Object.getOwnPropertySymbols : null; + +var OwnPropertyKeys = function OwnPropertyKeys(ES, source) { + var ownKeys = keys(source); + if ($gOPS) { + $pushApply(ownKeys, $gOPS(source)); + } + return ownKeys; +}; + +var ES2018 = assign(assign({}, ES2017), { + EnumerableOwnPropertyNames: ES2017.EnumerableOwnProperties, + + // https://ecma-international.org/ecma-262/9.0/#sec-thissymbolvalue + thisSymbolValue: function thisSymbolValue(value) { + if (!$SymbolValueOf) { + throw new SyntaxError('Symbols are not supported; thisSymbolValue requires that `value` be a Symbol or a Symbol object'); + } + if (this.Type(value) === 'Symbol') { + return value; + } + return $SymbolValueOf(value); + }, + + // https://www.ecma-international.org/ecma-262/9.0/#sec-isstringprefix + IsStringPrefix: function IsStringPrefix(p, q) { + if (this.Type(p) !== 'String') { + throw new TypeError('Assertion failed: "p" must be a String'); + } + + if (this.Type(q) !== 'String') { + throw new TypeError('Assertion failed: "q" must be a String'); + } + + if (p === q || p === '') { + return true; + } + + var pLength = p.length; + var qLength = q.length; + if (pLength >= qLength) { + return false; + } + + // assert: pLength < qLength + + for (var i = 0; i < pLength; i += 1) { + if ($charAt(p, i) !== $charAt(q, i)) { + return false; + } + } + return true; + }, + + // https://www.ecma-international.org/ecma-262/9.0/#sec-tostring-applied-to-the-number-type + NumberToString: function NumberToString(m) { + if (this.Type(m) !== 'Number') { + throw new TypeError('Assertion failed: "m" must be a String'); + } + + return $String(m); + }, + + // https://www.ecma-international.org/ecma-262/9.0/#sec-copydataproperties + CopyDataProperties: function CopyDataProperties(target, source, excludedItems) { + if (this.Type(target) !== 'Object') { + throw new TypeError('Assertion failed: "target" must be an Object'); + } + + if (!this.IsArray(excludedItems)) { + throw new TypeError('Assertion failed: "excludedItems" must be a List of Property Keys'); + } + for (var i = 0; i < excludedItems.length; i += 1) { + if (!this.IsPropertyKey(excludedItems[i])) { + throw new TypeError('Assertion failed: "excludedItems" must be a List of Property Keys'); + } + } + + if (typeof source === 'undefined' || source === null) { + return target; + } + + var ES = this; + + var fromObj = ES.ToObject(source); + + var sourceKeys = OwnPropertyKeys(ES, fromObj); + forEach(sourceKeys, function (nextKey) { + var excluded = false; + + forEach(excludedItems, function (e) { + if (ES.SameValue(e, nextKey) === true) { + excluded = true; + } + }); + + var enumerable = $isEnumerable(fromObj, nextKey) || ( + // this is to handle string keys being non-enumerable in older engines + typeof source === 'string' + && nextKey >= 0 + && ES.IsInteger(ES.ToNumber(nextKey)) + ); + if (excluded === false && enumerable) { + var propValue = ES.Get(fromObj, nextKey); + ES.CreateDataProperty(target, nextKey, propValue); + } + }); + + return target; + }, + + // https://ecma-international.org/ecma-262/9.0/#sec-promise-resolve + PromiseResolve: function PromiseResolve(C, x) { + if (!$PromiseResolve) { + throw new SyntaxError('This environment does not support Promises.'); + } + return $PromiseResolve(C, x); + } +}); + +delete ES2018.EnumerableOwnProperties; // replaced with EnumerableOwnPropertyNames + +delete ES2018.IsPropertyDescriptor; // not an actual abstract operation + +module.exports = ES2018; diff --git a/WebServer/node_modules/es-abstract/es5.js b/WebServer/node_modules/es-abstract/es5.js new file mode 100644 index 000000000..37c0baf0f --- /dev/null +++ b/WebServer/node_modules/es-abstract/es5.js @@ -0,0 +1,235 @@ +'use strict'; + +var GetIntrinsic = require('./GetIntrinsic'); + +var $Object = GetIntrinsic('%Object%'); +var $TypeError = GetIntrinsic('%TypeError%'); +var $String = GetIntrinsic('%String%'); + +var assertRecord = require('./helpers/assertRecord'); +var $isNaN = require('./helpers/isNaN'); +var $isFinite = require('./helpers/isFinite'); + +var sign = require('./helpers/sign'); +var mod = require('./helpers/mod'); + +var IsCallable = require('is-callable'); +var toPrimitive = require('es-to-primitive/es5'); + +var has = require('has'); + +// https://es5.github.io/#x9 +var ES5 = { + ToPrimitive: toPrimitive, + + ToBoolean: function ToBoolean(value) { + return !!value; + }, + ToNumber: function ToNumber(value) { + return +value; // eslint-disable-line no-implicit-coercion + }, + ToInteger: function ToInteger(value) { + var number = this.ToNumber(value); + if ($isNaN(number)) { return 0; } + if (number === 0 || !$isFinite(number)) { return number; } + return sign(number) * Math.floor(Math.abs(number)); + }, + ToInt32: function ToInt32(x) { + return this.ToNumber(x) >> 0; + }, + ToUint32: function ToUint32(x) { + return this.ToNumber(x) >>> 0; + }, + ToUint16: function ToUint16(value) { + var number = this.ToNumber(value); + if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; } + var posInt = sign(number) * Math.floor(Math.abs(number)); + return mod(posInt, 0x10000); + }, + ToString: function ToString(value) { + return $String(value); + }, + ToObject: function ToObject(value) { + this.CheckObjectCoercible(value); + return $Object(value); + }, + CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) { + /* jshint eqnull:true */ + if (value == null) { + throw new $TypeError(optMessage || 'Cannot call method on ' + value); + } + return value; + }, + IsCallable: IsCallable, + SameValue: function SameValue(x, y) { + if (x === y) { // 0 === -0, but they are not identical. + if (x === 0) { return 1 / x === 1 / y; } + return true; + } + return $isNaN(x) && $isNaN(y); + }, + + // https://www.ecma-international.org/ecma-262/5.1/#sec-8 + Type: function Type(x) { + if (x === null) { + return 'Null'; + } + if (typeof x === 'undefined') { + return 'Undefined'; + } + if (typeof x === 'function' || typeof x === 'object') { + return 'Object'; + } + if (typeof x === 'number') { + return 'Number'; + } + if (typeof x === 'boolean') { + return 'Boolean'; + } + if (typeof x === 'string') { + return 'String'; + } + }, + + // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type + IsPropertyDescriptor: function IsPropertyDescriptor(Desc) { + if (this.Type(Desc) !== 'Object') { + return false; + } + var allowed = { + '[[Configurable]]': true, + '[[Enumerable]]': true, + '[[Get]]': true, + '[[Set]]': true, + '[[Value]]': true, + '[[Writable]]': true + }; + + for (var key in Desc) { // eslint-disable-line + if (has(Desc, key) && !allowed[key]) { + return false; + } + } + + var isData = has(Desc, '[[Value]]'); + var IsAccessor = has(Desc, '[[Get]]') || has(Desc, '[[Set]]'); + if (isData && IsAccessor) { + throw new $TypeError('Property Descriptors may not be both accessor and data descriptors'); + } + return true; + }, + + // https://ecma-international.org/ecma-262/5.1/#sec-8.10.1 + IsAccessorDescriptor: function IsAccessorDescriptor(Desc) { + if (typeof Desc === 'undefined') { + return false; + } + + assertRecord(this, 'Property Descriptor', 'Desc', Desc); + + if (!has(Desc, '[[Get]]') && !has(Desc, '[[Set]]')) { + return false; + } + + return true; + }, + + // https://ecma-international.org/ecma-262/5.1/#sec-8.10.2 + IsDataDescriptor: function IsDataDescriptor(Desc) { + if (typeof Desc === 'undefined') { + return false; + } + + assertRecord(this, 'Property Descriptor', 'Desc', Desc); + + if (!has(Desc, '[[Value]]') && !has(Desc, '[[Writable]]')) { + return false; + } + + return true; + }, + + // https://ecma-international.org/ecma-262/5.1/#sec-8.10.3 + IsGenericDescriptor: function IsGenericDescriptor(Desc) { + if (typeof Desc === 'undefined') { + return false; + } + + assertRecord(this, 'Property Descriptor', 'Desc', Desc); + + if (!this.IsAccessorDescriptor(Desc) && !this.IsDataDescriptor(Desc)) { + return true; + } + + return false; + }, + + // https://ecma-international.org/ecma-262/5.1/#sec-8.10.4 + FromPropertyDescriptor: function FromPropertyDescriptor(Desc) { + if (typeof Desc === 'undefined') { + return Desc; + } + + assertRecord(this, 'Property Descriptor', 'Desc', Desc); + + if (this.IsDataDescriptor(Desc)) { + return { + value: Desc['[[Value]]'], + writable: !!Desc['[[Writable]]'], + enumerable: !!Desc['[[Enumerable]]'], + configurable: !!Desc['[[Configurable]]'] + }; + } else if (this.IsAccessorDescriptor(Desc)) { + return { + get: Desc['[[Get]]'], + set: Desc['[[Set]]'], + enumerable: !!Desc['[[Enumerable]]'], + configurable: !!Desc['[[Configurable]]'] + }; + } else { + throw new $TypeError('FromPropertyDescriptor must be called with a fully populated Property Descriptor'); + } + }, + + // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5 + ToPropertyDescriptor: function ToPropertyDescriptor(Obj) { + if (this.Type(Obj) !== 'Object') { + throw new $TypeError('ToPropertyDescriptor requires an object'); + } + + var desc = {}; + if (has(Obj, 'enumerable')) { + desc['[[Enumerable]]'] = this.ToBoolean(Obj.enumerable); + } + if (has(Obj, 'configurable')) { + desc['[[Configurable]]'] = this.ToBoolean(Obj.configurable); + } + if (has(Obj, 'value')) { + desc['[[Value]]'] = Obj.value; + } + if (has(Obj, 'writable')) { + desc['[[Writable]]'] = this.ToBoolean(Obj.writable); + } + if (has(Obj, 'get')) { + var getter = Obj.get; + if (typeof getter !== 'undefined' && !this.IsCallable(getter)) { + throw new TypeError('getter must be a function'); + } + desc['[[Get]]'] = getter; + } + if (has(Obj, 'set')) { + var setter = Obj.set; + if (typeof setter !== 'undefined' && !this.IsCallable(setter)) { + throw new $TypeError('setter must be a function'); + } + desc['[[Set]]'] = setter; + } + + if ((has(desc, '[[Get]]') || has(desc, '[[Set]]')) && (has(desc, '[[Value]]') || has(desc, '[[Writable]]'))) { + throw new $TypeError('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute'); + } + return desc; + } +}; + +module.exports = ES5; diff --git a/WebServer/node_modules/es-abstract/es6.js b/WebServer/node_modules/es-abstract/es6.js new file mode 100644 index 000000000..2d1f4dc92 --- /dev/null +++ b/WebServer/node_modules/es-abstract/es6.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./es2015'); diff --git a/WebServer/node_modules/es-abstract/es7.js b/WebServer/node_modules/es-abstract/es7.js new file mode 100644 index 000000000..f2f15c0a8 --- /dev/null +++ b/WebServer/node_modules/es-abstract/es7.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./es2016'); diff --git a/WebServer/node_modules/es-abstract/helpers/assertRecord.js b/WebServer/node_modules/es-abstract/helpers/assertRecord.js new file mode 100644 index 000000000..e8f06176f --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/assertRecord.js @@ -0,0 +1,49 @@ +'use strict'; + +var GetIntrinsic = require('../GetIntrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); +var $SyntaxError = GetIntrinsic('%SyntaxError%'); + +var has = require('has'); + +var predicates = { + // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type + 'Property Descriptor': function isPropertyDescriptor(ES, Desc) { + if (ES.Type(Desc) !== 'Object') { + return false; + } + var allowed = { + '[[Configurable]]': true, + '[[Enumerable]]': true, + '[[Get]]': true, + '[[Set]]': true, + '[[Value]]': true, + '[[Writable]]': true + }; + + for (var key in Desc) { // eslint-disable-line + if (has(Desc, key) && !allowed[key]) { + return false; + } + } + + var isData = has(Desc, '[[Value]]'); + var IsAccessor = has(Desc, '[[Get]]') || has(Desc, '[[Set]]'); + if (isData && IsAccessor) { + throw new $TypeError('Property Descriptors may not be both accessor and data descriptors'); + } + return true; + } +}; + +module.exports = function assertRecord(ES, recordType, argumentName, value) { + var predicate = predicates[recordType]; + if (typeof predicate !== 'function') { + throw new $SyntaxError('unknown record type: ' + recordType); + } + if (!predicate(ES, value)) { + throw new $TypeError(argumentName + ' must be a ' + recordType); + } + console.log(predicate(ES, value), value); +}; diff --git a/WebServer/node_modules/es-abstract/helpers/assign.js b/WebServer/node_modules/es-abstract/helpers/assign.js new file mode 100644 index 000000000..2533d20a3 --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/assign.js @@ -0,0 +1,17 @@ +var bind = require('function-bind'); +var has = bind.call(Function.call, Object.prototype.hasOwnProperty); + +var $assign = Object.assign; + +module.exports = function assign(target, source) { + if ($assign) { + return $assign(target, source); + } + + for (var key in source) { + if (has(source, key)) { + target[key] = source[key]; + } + } + return target; +}; diff --git a/WebServer/node_modules/es-abstract/helpers/forEach.js b/WebServer/node_modules/es-abstract/helpers/forEach.js new file mode 100644 index 000000000..854e7f2b5 --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/forEach.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = function forEach(array, callback) { + for (var i = 0; i < array.length; i += 1) { + callback(array[i], i, array); + } +}; diff --git a/WebServer/node_modules/es-abstract/helpers/isFinite.js b/WebServer/node_modules/es-abstract/helpers/isFinite.js new file mode 100644 index 000000000..46585376b --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/isFinite.js @@ -0,0 +1,3 @@ +var $isNaN = Number.isNaN || function (a) { return a !== a; }; + +module.exports = Number.isFinite || function (x) { return typeof x === 'number' && !$isNaN(x) && x !== Infinity && x !== -Infinity; }; diff --git a/WebServer/node_modules/es-abstract/helpers/isNaN.js b/WebServer/node_modules/es-abstract/helpers/isNaN.js new file mode 100644 index 000000000..e4d4f95f3 --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/isNaN.js @@ -0,0 +1,3 @@ +module.exports = Number.isNaN || function isNaN(a) { + return a !== a; +}; diff --git a/WebServer/node_modules/es-abstract/helpers/isPrimitive.js b/WebServer/node_modules/es-abstract/helpers/isPrimitive.js new file mode 100644 index 000000000..366915645 --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/isPrimitive.js @@ -0,0 +1,3 @@ +module.exports = function isPrimitive(value) { + return value === null || (typeof value !== 'function' && typeof value !== 'object'); +}; diff --git a/WebServer/node_modules/es-abstract/helpers/mod.js b/WebServer/node_modules/es-abstract/helpers/mod.js new file mode 100644 index 000000000..5867fd979 --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/mod.js @@ -0,0 +1,4 @@ +module.exports = function mod(number, modulo) { + var remain = number % modulo; + return Math.floor(remain >= 0 ? remain : remain + modulo); +}; diff --git a/WebServer/node_modules/es-abstract/helpers/sign.js b/WebServer/node_modules/es-abstract/helpers/sign.js new file mode 100644 index 000000000..2ac0bf1b1 --- /dev/null +++ b/WebServer/node_modules/es-abstract/helpers/sign.js @@ -0,0 +1,3 @@ +module.exports = function sign(number) { + return number >= 0 ? 1 : -1; +}; diff --git a/WebServer/node_modules/es-abstract/index.js b/WebServer/node_modules/es-abstract/index.js new file mode 100644 index 000000000..9a10051f7 --- /dev/null +++ b/WebServer/node_modules/es-abstract/index.js @@ -0,0 +1,24 @@ +'use strict'; + +var assign = require('./helpers/assign'); + +var ES5 = require('./es5'); +var ES2015 = require('./es2015'); +var ES2016 = require('./es2016'); +var ES2017 = require('./es2017'); +var ES2018 = require('./es2018'); + +var ES = { + ES5: ES5, + ES6: ES2015, + ES2015: ES2015, + ES7: ES2016, + ES2016: ES2016, + ES2017: ES2017, + ES2018: ES2018 +}; +assign(ES, ES5); +delete ES.CheckObjectCoercible; // renamed in ES6 to RequireObjectCoercible +assign(ES, ES2015); + +module.exports = ES; diff --git a/WebServer/node_modules/es-abstract/operations/2015.js b/WebServer/node_modules/es-abstract/operations/2015.js new file mode 100644 index 000000000..84d12e0f4 --- /dev/null +++ b/WebServer/node_modules/es-abstract/operations/2015.js @@ -0,0 +1,79 @@ +'use strict'; + +module.exports = { + IsPropertyDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type', + IsAccessorDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-isaccessordescriptor', + IsDataDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-isdatadescriptor', + IsGenericDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-isgenericdescriptor', + FromPropertyDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-frompropertydescriptor', + ToPropertyDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-topropertydescriptor', + CompletePropertyDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-completepropertydescriptor', + ToPrimitive: 'https://ecma-international.org/ecma-262/6.0/#sec-toprimitive', + ToBoolean: 'https://ecma-international.org/ecma-262/6.0/#sec-toboolean', + ToNumber: 'https://ecma-international.org/ecma-262/6.0/#sec-tonumber', + ToInteger: 'https://ecma-international.org/ecma-262/6.0/#sec-tointeger', + ToInt32: 'https://ecma-international.org/ecma-262/6.0/#sec-toint32', + ToUint32: 'https://ecma-international.org/ecma-262/6.0/#sec-touint32', + ToInt16: 'https://ecma-international.org/ecma-262/6.0/#sec-toint16', + ToUint16: 'https://ecma-international.org/ecma-262/6.0/#sec-touint16', + ToInt8: 'https://ecma-international.org/ecma-262/6.0/#sec-toint8', + ToUint8: 'https://ecma-international.org/ecma-262/6.0/#sec-touint8', + ToUint8Clamp: 'https://ecma-international.org/ecma-262/6.0/#sec-touint8clamp', + ToString: 'https://ecma-international.org/ecma-262/6.0/#sec-tostring', + ToObject: 'https://ecma-international.org/ecma-262/6.0/#sec-toobject', + ToPropertyKey: 'https://ecma-international.org/ecma-262/6.0/#sec-topropertykey', + ToLength: 'https://ecma-international.org/ecma-262/6.0/#sec-tolength', + CanonicalNumericIndexString: 'https://ecma-international.org/ecma-262/6.0/#sec-canonicalnumericindexstring', + RequireObjectCoercible: 'https://ecma-international.org/ecma-262/6.0/#sec-requireobjectcoercible', + IsArray: 'https://ecma-international.org/ecma-262/6.0/#sec-isarray', + IsCallable: 'https://ecma-international.org/ecma-262/6.0/#sec-iscallable', + IsConstructor: 'https://ecma-international.org/ecma-262/6.0/#sec-isconstructor', + IsExtensible: 'https://ecma-international.org/ecma-262/6.0/#sec-isextensible-o', + IsInteger: 'https://ecma-international.org/ecma-262/6.0/#sec-isinteger', + IsPropertyKey: 'https://ecma-international.org/ecma-262/6.0/#sec-ispropertykey', + IsRegExp: 'https://ecma-international.org/ecma-262/6.0/#sec-isregexp', + SameValue: 'https://ecma-international.org/ecma-262/6.0/#sec-samevalue', + SameValueZero: 'https://ecma-international.org/ecma-262/6.0/#sec-samevaluezero', + Get: 'https://ecma-international.org/ecma-262/6.0/#sec-get-o-p', + GetV: 'https://ecma-international.org/ecma-262/6.0/#sec-getv', + Set: 'https://ecma-international.org/ecma-262/6.0/#sec-set-o-p-v-throw', + CreateDataProperty: 'https://ecma-international.org/ecma-262/6.0/#sec-createdataproperty', + CreateMethodProperty: 'https://ecma-international.org/ecma-262/6.0/#sec-createmethodproperty', + CreateDataPropertyOrThrow: 'https://ecma-international.org/ecma-262/6.0/#sec-createdatapropertyorthrow', + DefinePropertyOrThrow: 'https://ecma-international.org/ecma-262/6.0/#sec-definepropertyorthrow', + DeletePropertyOrThrow: 'https://ecma-international.org/ecma-262/6.0/#sec-deletepropertyorthrow', + GetMethod: 'https://ecma-international.org/ecma-262/6.0/#sec-getmethod', + HasProperty: 'https://ecma-international.org/ecma-262/6.0/#sec-hasproperty', + HasOwnProperty: 'https://ecma-international.org/ecma-262/6.0/#sec-hasownproperty', + Call: 'https://ecma-international.org/ecma-262/6.0/#sec-call', + Construct: 'https://ecma-international.org/ecma-262/6.0/#sec-construct', + SetIntegrityLevel: 'https://ecma-international.org/ecma-262/6.0/#sec-setintegritylevel', + TestIntegrityLevel: 'https://ecma-international.org/ecma-262/6.0/#sec-testintegritylevel', + CreateArrayFromList: 'https://ecma-international.org/ecma-262/6.0/#sec-createarrayfromlist', + CreateListFromArrayLike: 'https://ecma-international.org/ecma-262/6.0/#sec-createlistfromarraylike', + Invoke: 'https://ecma-international.org/ecma-262/6.0/#sec-invoke', + OrdinaryHasInstance: 'https://ecma-international.org/ecma-262/6.0/#sec-ordinaryhasinstance', + SpeciesConstructor: 'https://ecma-international.org/ecma-262/6.0/#sec-speciesconstructor', + EnumerableOwnNames: 'https://ecma-international.org/ecma-262/6.0/#sec-enumerableownnames', + GetIterator: 'https://ecma-international.org/ecma-262/6.0/#sec-getiterator', + IteratorNext: 'https://ecma-international.org/ecma-262/6.0/#sec-iteratornext', + IteratorComplete: 'https://ecma-international.org/ecma-262/6.0/#sec-iteratorcomplete', + IteratorValue: 'https://ecma-international.org/ecma-262/6.0/#sec-iteratorvalue', + IteratorStep: 'https://ecma-international.org/ecma-262/6.0/#sec-iteratorstep', + IteratorClose: 'https://ecma-international.org/ecma-262/6.0/#sec-iteratorclose', + CreateIterResultObject: 'https://ecma-international.org/ecma-262/6.0/#sec-createiterresultobject', + CreateListIterator: 'https://ecma-international.org/ecma-262/6.0/#sec-createlistiterator', + Type: 'https://ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types', + thisBooleanValue: 'https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-boolean-prototype-object', + thisNumberValue: 'https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-number-prototype-object', + thisTimeValue: 'https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-date-prototype-object', + thisStringValue: 'https://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-string-prototype-object', + RegExpExec: 'https://ecma-international.org/ecma-262/6.0/#sec-regexpexec', + RegExpBuiltinExec: 'https://ecma-international.org/ecma-262/6.0/#sec-regexpbuiltinexec', + IsConcatSpreadable: 'https://ecma-international.org/ecma-262/6.0/#sec-isconcatspreadable', + IsPromise: 'https://ecma-international.org/ecma-262/6.0/#sec-ispromise', + ArraySpeciesCreate: 'https://ecma-international.org/ecma-262/6.0/#sec-arrayspeciescreate', + ObjectCreate: 'https://ecma-international.org/ecma-262/6.0/#sec-objectcreate', + AdvanceStringIndex: 'https://ecma-international.org/ecma-262/6.0/#sec-advancestringindex', + NormalCompletion: 'https://ecma-international.org/ecma-262/6.0/#sec-normalcompletion' +}; diff --git a/WebServer/node_modules/es-abstract/operations/2016.js b/WebServer/node_modules/es-abstract/operations/2016.js new file mode 100644 index 000000000..65b5beff0 --- /dev/null +++ b/WebServer/node_modules/es-abstract/operations/2016.js @@ -0,0 +1,283 @@ +'use strict'; + +module.exports = { + IsPropertyDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type', // not actually an abstract op + + abs: 'https://ecma-international.org/ecma-262/7.0/#sec-algorithm-conventions', + 'Abstract Equality Comparison': 'https://ecma-international.org/ecma-262/7.0/#sec-abstract-equality-comparison', + 'Abstract Relational Comparison': 'https://ecma-international.org/ecma-262/7.0/#sec-abstract-relational-comparison', + AddRestrictedFunctionProperties: 'https://ecma-international.org/ecma-262/7.0/#sec-addrestrictedfunctionproperties', + AdvanceStringIndex: 'https://ecma-international.org/ecma-262/7.0/#sec-advancestringindex', + AllocateArrayBuffer: 'https://ecma-international.org/ecma-262/7.0/#sec-allocatearraybuffer', + AllocateTypedArray: 'https://ecma-international.org/ecma-262/7.0/#sec-allocatetypedarray', + AllocateTypedArrayBuffer: 'https://ecma-international.org/ecma-262/7.0/#sec-allocatetypedarraybuffer', + ArrayCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-arraycreate', + ArraySetLength: 'https://ecma-international.org/ecma-262/7.0/#sec-arraysetlength', + ArraySpeciesCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-arrayspeciescreate', + BlockDeclarationInstantiation: 'https://ecma-international.org/ecma-262/7.0/#sec-blockdeclarationinstantiation', + BoundFunctionCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-boundfunctioncreate', + Call: 'https://ecma-international.org/ecma-262/7.0/#sec-call', + Canonicalize: 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-canonicalize-ch', + CanonicalNumericIndexString: 'https://ecma-international.org/ecma-262/7.0/#sec-canonicalnumericindexstring', + CharacterRange: 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-characterrange-abstract-operation', + CharacterRangeOrUnion: 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-characterrangeorunion-abstract-operation', + CharacterSetMatcher: 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-charactersetmatcher-abstract-operation', + CloneArrayBuffer: 'https://ecma-international.org/ecma-262/7.0/#sec-clonearraybuffer', + CompletePropertyDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-completepropertydescriptor', + Completion: 'https://ecma-international.org/ecma-262/7.0/#sec-completion-record-specification-type', + Construct: 'https://ecma-international.org/ecma-262/7.0/#sec-construct', + CopyDataBlockBytes: 'https://ecma-international.org/ecma-262/7.0/#sec-copydatablockbytes', + CreateArrayFromList: 'https://ecma-international.org/ecma-262/7.0/#sec-createarrayfromlist', + CreateArrayIterator: 'https://ecma-international.org/ecma-262/7.0/#sec-createarrayiterator', + CreateBuiltinFunction: 'https://ecma-international.org/ecma-262/7.0/#sec-createbuiltinfunction', + CreateByteDataBlock: 'https://ecma-international.org/ecma-262/7.0/#sec-createbytedatablock', + CreateDataProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-createdataproperty', + CreateDataPropertyOrThrow: 'https://ecma-international.org/ecma-262/7.0/#sec-createdatapropertyorthrow', + CreateDynamicFunction: 'https://ecma-international.org/ecma-262/7.0/#sec-createdynamicfunction', + CreateHTML: 'https://ecma-international.org/ecma-262/7.0/#sec-createhtml', + CreateIntrinsics: 'https://ecma-international.org/ecma-262/7.0/#sec-createintrinsics', + CreateIterResultObject: 'https://ecma-international.org/ecma-262/7.0/#sec-createiterresultobject', + CreateListFromArrayLike: 'https://ecma-international.org/ecma-262/7.0/#sec-createlistfromarraylike', + CreateListIterator: 'https://ecma-international.org/ecma-262/7.0/#sec-createlistiterator', + CreateMapIterator: 'https://ecma-international.org/ecma-262/7.0/#sec-createmapiterator', + CreateMappedArgumentsObject: 'https://ecma-international.org/ecma-262/7.0/#sec-createmappedargumentsobject', + CreateMethodProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-createmethodproperty', + CreatePerIterationEnvironment: 'https://ecma-international.org/ecma-262/7.0/#sec-createperiterationenvironment', + CreateRealm: 'https://ecma-international.org/ecma-262/7.0/#sec-createrealm', + CreateResolvingFunctions: 'https://ecma-international.org/ecma-262/7.0/#sec-createresolvingfunctions', + CreateSetIterator: 'https://ecma-international.org/ecma-262/7.0/#sec-createsetiterator', + CreateStringIterator: 'https://ecma-international.org/ecma-262/7.0/#sec-createstringiterator', + CreateUnmappedArgumentsObject: 'https://ecma-international.org/ecma-262/7.0/#sec-createunmappedargumentsobject', + DateFromTime: 'https://ecma-international.org/ecma-262/7.0/#sec-date-number', + Day: 'https://ecma-international.org/ecma-262/7.0/#sec-day-number-and-time-within-day', + DayFromYear: 'https://ecma-international.org/ecma-262/7.0/#sec-year-number', + DaysInYear: 'https://ecma-international.org/ecma-262/7.0/#sec-year-number', + DayWithinYear: 'https://ecma-international.org/ecma-262/7.0/#sec-month-number', + Decode: 'https://ecma-international.org/ecma-262/7.0/#sec-decode', + DefinePropertyOrThrow: 'https://ecma-international.org/ecma-262/7.0/#sec-definepropertyorthrow', + DeletePropertyOrThrow: 'https://ecma-international.org/ecma-262/7.0/#sec-deletepropertyorthrow', + DetachArrayBuffer: 'https://ecma-international.org/ecma-262/7.0/#sec-detacharraybuffer', + Encode: 'https://ecma-international.org/ecma-262/7.0/#sec-encode', + EnqueueJob: 'https://ecma-international.org/ecma-262/7.0/#sec-enqueuejob', + EnumerableOwnNames: 'https://ecma-international.org/ecma-262/7.0/#sec-enumerableownnames', + EnumerateObjectProperties: 'https://ecma-international.org/ecma-262/7.0/#sec-enumerate-object-properties', + EscapeRegExpPattern: 'https://ecma-international.org/ecma-262/7.0/#sec-escaperegexppattern', + EvalDeclarationInstantiation: 'https://ecma-international.org/ecma-262/7.0/#sec-evaldeclarationinstantiation', + EvaluateCall: 'https://ecma-international.org/ecma-262/7.0/#sec-evaluatecall', + EvaluateDirectCall: 'https://ecma-international.org/ecma-262/7.0/#sec-evaluatedirectcall', + EvaluateNew: 'https://ecma-international.org/ecma-262/7.0/#sec-evaluatenew', + floor: 'https://ecma-international.org/ecma-262/7.0/#sec-algorithm-conventions', + ForBodyEvaluation: 'https://ecma-international.org/ecma-262/7.0/#sec-forbodyevaluation', + 'ForIn/OfBodyEvaluation': 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset', + 'ForIn/OfHeadEvaluation': 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind', + FromPropertyDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-frompropertydescriptor', + FulfillPromise: 'https://ecma-international.org/ecma-262/7.0/#sec-fulfillpromise', + FunctionAllocate: 'https://ecma-international.org/ecma-262/7.0/#sec-functionallocate', + FunctionCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-functioncreate', + FunctionDeclarationInstantiation: 'https://ecma-international.org/ecma-262/7.0/#sec-functiondeclarationinstantiation', + FunctionInitialize: 'https://ecma-international.org/ecma-262/7.0/#sec-functioninitialize', + GeneratorFunctionCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-generatorfunctioncreate', + GeneratorResume: 'https://ecma-international.org/ecma-262/7.0/#sec-generatorresume', + GeneratorResumeAbrupt: 'https://ecma-international.org/ecma-262/7.0/#sec-generatorresumeabrupt', + GeneratorStart: 'https://ecma-international.org/ecma-262/7.0/#sec-generatorstart', + GeneratorValidate: 'https://ecma-international.org/ecma-262/7.0/#sec-generatorvalidate', + GeneratorYield: 'https://ecma-international.org/ecma-262/7.0/#sec-generatoryield', + Get: 'https://ecma-international.org/ecma-262/7.0/#sec-get-o-p', + GetActiveScriptOrModule: 'https://ecma-international.org/ecma-262/7.0/#sec-getactivescriptormodule', + GetFunctionRealm: 'https://ecma-international.org/ecma-262/7.0/#sec-getfunctionrealm', + GetGlobalObject: 'https://ecma-international.org/ecma-262/7.0/#sec-getglobalobject', + GetIdentifierReference: 'https://ecma-international.org/ecma-262/7.0/#sec-getidentifierreference', + GetIterator: 'https://ecma-international.org/ecma-262/7.0/#sec-getiterator', + GetMethod: 'https://ecma-international.org/ecma-262/7.0/#sec-getmethod', + GetModuleNamespace: 'https://ecma-international.org/ecma-262/7.0/#sec-getmodulenamespace', + GetNewTarget: 'https://ecma-international.org/ecma-262/7.0/#sec-getnewtarget', + GetOwnPropertyKeys: 'https://ecma-international.org/ecma-262/7.0/#sec-getownpropertykeys', + GetPrototypeFromConstructor: 'https://ecma-international.org/ecma-262/7.0/#sec-getprototypefromconstructor', + GetSubstitution: 'https://ecma-international.org/ecma-262/7.0/#sec-getsubstitution', + GetSuperConstructor: 'https://ecma-international.org/ecma-262/7.0/#sec-getsuperconstructor', + GetTemplateObject: 'https://ecma-international.org/ecma-262/7.0/#sec-gettemplateobject', + GetThisEnvironment: 'https://ecma-international.org/ecma-262/7.0/#sec-getthisenvironment', + GetThisValue: 'https://ecma-international.org/ecma-262/7.0/#sec-getthisvalue', + GetV: 'https://ecma-international.org/ecma-262/7.0/#sec-getv', + GetValue: 'https://ecma-international.org/ecma-262/7.0/#sec-getvalue', + GetValueFromBuffer: 'https://ecma-international.org/ecma-262/7.0/#sec-getvaluefrombuffer', + GetViewValue: 'https://ecma-international.org/ecma-262/7.0/#sec-getviewvalue', + GlobalDeclarationInstantiation: 'https://ecma-international.org/ecma-262/7.0/#sec-globaldeclarationinstantiation', + HasOwnProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-hasownproperty', + HasProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-hasproperty', + HostPromiseRejectionTracker: 'https://ecma-international.org/ecma-262/7.0/#sec-host-promise-rejection-tracker', + HostReportErrors: 'https://ecma-international.org/ecma-262/7.0/#sec-host-report-errors', + HostResolveImportedModule: 'https://ecma-international.org/ecma-262/7.0/#sec-hostresolveimportedmodule', + HourFromTime: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + HoursPerDay: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + IfAbruptRejectPromise: 'https://ecma-international.org/ecma-262/7.0/#sec-ifabruptrejectpromise', + ImportedLocalNames: 'https://ecma-international.org/ecma-262/7.0/#sec-importedlocalnames', + InitializeBoundName: 'https://ecma-international.org/ecma-262/7.0/#sec-initializeboundname', + InitializeHostDefinedRealm: 'https://ecma-international.org/ecma-262/7.0/#sec-initializehostdefinedrealm', + InitializeReferencedBinding: 'https://ecma-international.org/ecma-262/7.0/#sec-initializereferencedbinding', + InLeapYear: 'https://ecma-international.org/ecma-262/7.0/#sec-year-number', + InstanceofOperator: 'https://ecma-international.org/ecma-262/7.0/#sec-instanceofoperator', + IntegerIndexedElementGet: 'https://ecma-international.org/ecma-262/7.0/#sec-integerindexedelementget', + IntegerIndexedElementSet: 'https://ecma-international.org/ecma-262/7.0/#sec-integerindexedelementset', + IntegerIndexedObjectCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-integerindexedobjectcreate', + InternalizeJSONProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-internalizejsonproperty', + Invoke: 'https://ecma-international.org/ecma-262/7.0/#sec-invoke', + IsAccessorDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-isaccessordescriptor', + IsAnonymousFunctionDefinition: 'https://ecma-international.org/ecma-262/7.0/#sec-isanonymousfunctiondefinition', + IsArray: 'https://ecma-international.org/ecma-262/7.0/#sec-isarray', + IsCallable: 'https://ecma-international.org/ecma-262/7.0/#sec-iscallable', + IsCompatiblePropertyDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-iscompatiblepropertydescriptor', + IsConcatSpreadable: 'https://ecma-international.org/ecma-262/7.0/#sec-isconcatspreadable', + IsConstructor: 'https://ecma-international.org/ecma-262/7.0/#sec-isconstructor', + IsDataDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-isdatadescriptor', + IsDetachedBuffer: 'https://ecma-international.org/ecma-262/7.0/#sec-isdetachedbuffer', + IsExtensible: 'https://ecma-international.org/ecma-262/7.0/#sec-isextensible-o', + IsGenericDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-isgenericdescriptor', + IsInTailPosition: 'https://ecma-international.org/ecma-262/7.0/#sec-isintailposition', + IsInteger: 'https://ecma-international.org/ecma-262/7.0/#sec-isinteger', + IsLabelledFunction: 'https://ecma-international.org/ecma-262/7.0/#sec-islabelledfunction', + IsPromise: 'https://ecma-international.org/ecma-262/7.0/#sec-ispromise', + IsPropertyKey: 'https://ecma-international.org/ecma-262/7.0/#sec-ispropertykey', + IsRegExp: 'https://ecma-international.org/ecma-262/7.0/#sec-isregexp', + IsWordChar: 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-iswordchar-abstract-operation', + IterableToArrayLike: 'https://ecma-international.org/ecma-262/7.0/#sec-iterabletoarraylike', + IteratorClose: 'https://ecma-international.org/ecma-262/7.0/#sec-iteratorclose', + IteratorComplete: 'https://ecma-international.org/ecma-262/7.0/#sec-iteratorcomplete', + IteratorNext: 'https://ecma-international.org/ecma-262/7.0/#sec-iteratornext', + IteratorStep: 'https://ecma-international.org/ecma-262/7.0/#sec-iteratorstep', + IteratorValue: 'https://ecma-international.org/ecma-262/7.0/#sec-iteratorvalue', + LocalTime: 'https://ecma-international.org/ecma-262/7.0/#sec-localtime', + LoopContinues: 'https://ecma-international.org/ecma-262/7.0/#sec-loopcontinues', + MakeArgGetter: 'https://ecma-international.org/ecma-262/7.0/#sec-makearggetter', + MakeArgSetter: 'https://ecma-international.org/ecma-262/7.0/#sec-makeargsetter', + MakeClassConstructor: 'https://ecma-international.org/ecma-262/7.0/#sec-makeclassconstructor', + MakeConstructor: 'https://ecma-international.org/ecma-262/7.0/#sec-makeconstructor', + MakeDate: 'https://ecma-international.org/ecma-262/7.0/#sec-makedate', + MakeDay: 'https://ecma-international.org/ecma-262/7.0/#sec-makeday', + MakeMethod: 'https://ecma-international.org/ecma-262/7.0/#sec-makemethod', + MakeSuperPropertyReference: 'https://ecma-international.org/ecma-262/7.0/#sec-makesuperpropertyreference', + MakeTime: 'https://ecma-international.org/ecma-262/7.0/#sec-maketime', + max: 'https://ecma-international.org/ecma-262/7.0/#sec-algorithm-conventions', + min: 'https://ecma-international.org/ecma-262/7.0/#sec-algorithm-conventions', + MinFromTime: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + MinutesPerHour: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + ModuleNamespaceCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-modulenamespacecreate', + modulo: 'https://ecma-international.org/ecma-262/7.0/#sec-algorithm-conventions', + MonthFromTime: 'https://ecma-international.org/ecma-262/7.0/#sec-month-number', + msFromTime: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + msPerDay: 'https://ecma-international.org/ecma-262/7.0/#sec-day-number-and-time-within-day', + msPerHour: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + msPerMinute: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + msPerSecond: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + NewDeclarativeEnvironment: 'https://ecma-international.org/ecma-262/7.0/#sec-newdeclarativeenvironment', + NewFunctionEnvironment: 'https://ecma-international.org/ecma-262/7.0/#sec-newfunctionenvironment', + NewGlobalEnvironment: 'https://ecma-international.org/ecma-262/7.0/#sec-newglobalenvironment', + NewModuleEnvironment: 'https://ecma-international.org/ecma-262/7.0/#sec-newmoduleenvironment', + NewObjectEnvironment: 'https://ecma-international.org/ecma-262/7.0/#sec-newobjectenvironment', + NewPromiseCapability: 'https://ecma-international.org/ecma-262/7.0/#sec-newpromisecapability', + NextJob: 'https://ecma-international.org/ecma-262/7.0/#sec-nextjob-result', + NormalCompletion: 'https://ecma-international.org/ecma-262/7.0/#sec-normalcompletion', + ObjectCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-objectcreate', + ObjectDefineProperties: 'https://ecma-international.org/ecma-262/7.0/#sec-objectdefineproperties', + OrdinaryCallBindThis: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarycallbindthis', + OrdinaryCallEvaluateBody: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarycallevaluatebody', + OrdinaryCreateFromConstructor: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarycreatefromconstructor', + OrdinaryDefineOwnProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarydefineownproperty', + OrdinaryDelete: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarydelete', + OrdinaryGet: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinaryget', + OrdinaryGetOwnProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarygetownproperty', + OrdinaryGetPrototypeOf: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarygetprototypeof', + OrdinaryHasInstance: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinaryhasinstance', + OrdinaryHasProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinaryhasproperty', + OrdinaryIsExtensible: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinaryisextensible', + OrdinaryOwnPropertyKeys: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinaryownpropertykeys', + OrdinaryPreventExtensions: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarypreventextensions', + OrdinarySet: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinaryset', + OrdinarySetPrototypeOf: 'https://ecma-international.org/ecma-262/7.0/#sec-ordinarysetprototypeof', + ParseModule: 'https://ecma-international.org/ecma-262/7.0/#sec-parsemodule', + ParseScript: 'https://ecma-international.org/ecma-262/7.0/#sec-parse-script', + PerformEval: 'https://ecma-international.org/ecma-262/7.0/#sec-performeval', + PerformPromiseAll: 'https://ecma-international.org/ecma-262/7.0/#sec-performpromiseall', + PerformPromiseRace: 'https://ecma-international.org/ecma-262/7.0/#sec-performpromiserace', + PerformPromiseThen: 'https://ecma-international.org/ecma-262/7.0/#sec-performpromisethen', + PrepareForOrdinaryCall: 'https://ecma-international.org/ecma-262/7.0/#sec-prepareforordinarycall', + PrepareForTailCall: 'https://ecma-international.org/ecma-262/7.0/#sec-preparefortailcall', + PromiseReactionJob: 'https://ecma-international.org/ecma-262/7.0/#sec-promisereactionjob', + PromiseResolveThenableJob: 'https://ecma-international.org/ecma-262/7.0/#sec-promiseresolvethenablejob', + ProxyCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-proxycreate', + PutValue: 'https://ecma-international.org/ecma-262/7.0/#sec-putvalue', + QuoteJSONString: 'https://ecma-international.org/ecma-262/7.0/#sec-quotejsonstring', + RegExpAlloc: 'https://ecma-international.org/ecma-262/7.0/#sec-regexpalloc', + RegExpBuiltinExec: 'https://ecma-international.org/ecma-262/7.0/#sec-regexpbuiltinexec', + RegExpCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-regexpcreate', + RegExpExec: 'https://ecma-international.org/ecma-262/7.0/#sec-regexpexec', + RegExpInitialize: 'https://ecma-international.org/ecma-262/7.0/#sec-regexpinitialize', + RejectPromise: 'https://ecma-international.org/ecma-262/7.0/#sec-rejectpromise', + RepeatMatcher: 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-repeatmatcher-abstract-operation', + RequireObjectCoercible: 'https://ecma-international.org/ecma-262/7.0/#sec-requireobjectcoercible', + ResolveBinding: 'https://ecma-international.org/ecma-262/7.0/#sec-resolvebinding', + ResolveThisBinding: 'https://ecma-international.org/ecma-262/7.0/#sec-resolvethisbinding', + ReturnIfAbrupt: 'https://ecma-international.org/ecma-262/7.0/#sec-returnifabrupt', + SameValue: 'https://ecma-international.org/ecma-262/7.0/#sec-samevalue', + SameValueNonNumber: 'https://ecma-international.org/ecma-262/7.0/#sec-samevaluenonnumber', + SameValueZero: 'https://ecma-international.org/ecma-262/7.0/#sec-samevaluezero', + ScriptEvaluation: 'https://ecma-international.org/ecma-262/7.0/#sec-runtime-semantics-scriptevaluation', + ScriptEvaluationJob: 'https://ecma-international.org/ecma-262/7.0/#sec-scriptevaluationjob', + SecFromTime: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + SecondsPerMinute: 'https://ecma-international.org/ecma-262/7.0/#sec-hours-minutes-second-and-milliseconds', + SerializeJSONArray: 'https://ecma-international.org/ecma-262/7.0/#sec-serializejsonarray', + SerializeJSONObject: 'https://ecma-international.org/ecma-262/7.0/#sec-serializejsonobject', + SerializeJSONProperty: 'https://ecma-international.org/ecma-262/7.0/#sec-serializejsonproperty', + Set: 'https://ecma-international.org/ecma-262/7.0/#sec-set-o-p-v-throw', + SetDefaultGlobalBindings: 'https://ecma-international.org/ecma-262/7.0/#sec-setdefaultglobalbindings', + SetFunctionName: 'https://ecma-international.org/ecma-262/7.0/#sec-setfunctionname', + SetIntegrityLevel: 'https://ecma-international.org/ecma-262/7.0/#sec-setintegritylevel', + SetRealmGlobalObject: 'https://ecma-international.org/ecma-262/7.0/#sec-setrealmglobalobject', + SetValueInBuffer: 'https://ecma-international.org/ecma-262/7.0/#sec-setvalueinbuffer', + SetViewValue: 'https://ecma-international.org/ecma-262/7.0/#sec-setviewvalue', + SortCompare: 'https://ecma-international.org/ecma-262/7.0/#sec-sortcompare', + SpeciesConstructor: 'https://ecma-international.org/ecma-262/7.0/#sec-speciesconstructor', + SplitMatch: 'https://ecma-international.org/ecma-262/7.0/#sec-splitmatch', + 'Strict Equality Comparison': 'https://ecma-international.org/ecma-262/7.0/#sec-strict-equality-comparison', + StringCreate: 'https://ecma-international.org/ecma-262/7.0/#sec-stringcreate', + SymbolDescriptiveString: 'https://ecma-international.org/ecma-262/7.0/#sec-symboldescriptivestring', + TestIntegrityLevel: 'https://ecma-international.org/ecma-262/7.0/#sec-testintegritylevel', + thisBooleanValue: 'https://ecma-international.org/ecma-262/7.0/#sec-thisbooleanvalue', + thisNumberValue: 'https://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-number-prototype-object', + thisStringValue: 'https://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-string-prototype-object', + thisTimeValue: 'https://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-date-prototype-object', + TimeClip: 'https://ecma-international.org/ecma-262/7.0/#sec-timeclip', + TimeFromYear: 'https://ecma-international.org/ecma-262/7.0/#sec-year-number', + TimeWithinDay: 'https://ecma-international.org/ecma-262/7.0/#sec-day-number-and-time-within-day', + ToBoolean: 'https://ecma-international.org/ecma-262/7.0/#sec-toboolean', + ToDateString: 'https://ecma-international.org/ecma-262/7.0/#sec-todatestring', + ToInt16: 'https://ecma-international.org/ecma-262/7.0/#sec-toint16', + ToInt32: 'https://ecma-international.org/ecma-262/7.0/#sec-toint32', + ToInt8: 'https://ecma-international.org/ecma-262/7.0/#sec-toint8', + ToInteger: 'https://ecma-international.org/ecma-262/7.0/#sec-tointeger', + ToLength: 'https://ecma-international.org/ecma-262/7.0/#sec-tolength', + ToNumber: 'https://ecma-international.org/ecma-262/7.0/#sec-tonumber', + ToObject: 'https://ecma-international.org/ecma-262/7.0/#sec-toobject', + TopLevelModuleEvaluationJob: 'https://ecma-international.org/ecma-262/7.0/#sec-toplevelmoduleevaluationjob', + ToPrimitive: 'https://ecma-international.org/ecma-262/7.0/#sec-toprimitive', + ToPropertyDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-topropertydescriptor', + ToPropertyKey: 'https://ecma-international.org/ecma-262/7.0/#sec-topropertykey', + ToString: 'https://ecma-international.org/ecma-262/7.0/#sec-tostring', + 'ToString Applied to the Number Type': 'https://ecma-international.org/ecma-262/7.0/#sec-tostring-applied-to-the-number-type', + ToUint16: 'https://ecma-international.org/ecma-262/7.0/#sec-touint16', + ToUint32: 'https://ecma-international.org/ecma-262/7.0/#sec-touint32', + ToUint8: 'https://ecma-international.org/ecma-262/7.0/#sec-touint8', + ToUint8Clamp: 'https://ecma-international.org/ecma-262/7.0/#sec-touint8clamp', + TriggerPromiseReactions: 'https://ecma-international.org/ecma-262/7.0/#sec-triggerpromisereactions', + Type: 'https://ecma-international.org/ecma-262/7.0/#sec-ecmascript-data-types-and-values', + TypedArrayCreate: 'https://ecma-international.org/ecma-262/7.0/#typedarray-create', + TypedArraySpeciesCreate: 'https://ecma-international.org/ecma-262/7.0/#typedarray-species-create', + UpdateEmpty: 'https://ecma-international.org/ecma-262/7.0/#sec-updateempty', + UTC: 'https://ecma-international.org/ecma-262/7.0/#sec-utc-t', + UTF16Decode: 'https://ecma-international.org/ecma-262/7.0/#sec-utf16decode', + UTF16Encoding: 'https://ecma-international.org/ecma-262/7.0/#sec-utf16encoding', + ValidateAndApplyPropertyDescriptor: 'https://ecma-international.org/ecma-262/7.0/#sec-validateandapplypropertydescriptor', + ValidateTypedArray: 'https://ecma-international.org/ecma-262/7.0/#sec-validatetypedarray', + WeekDay: 'https://ecma-international.org/ecma-262/7.0/#sec-week-day', + YearFromTime: 'https://ecma-international.org/ecma-262/7.0/#sec-year-number' +}; diff --git a/WebServer/node_modules/es-abstract/operations/2017.js b/WebServer/node_modules/es-abstract/operations/2017.js new file mode 100644 index 000000000..f75aeb93d --- /dev/null +++ b/WebServer/node_modules/es-abstract/operations/2017.js @@ -0,0 +1,331 @@ +'use strict'; + +module.exports = { + IsPropertyDescriptor: 'https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type', // not actually an abstract op + + abs: 'https://ecma-international.org/ecma-262/8.0/#eqn-abs', + 'Abstract Equality Comparison': 'https://ecma-international.org/ecma-262/8.0/#sec-abstract-equality-comparison', + 'Abstract Relational Comparison': 'https://ecma-international.org/ecma-262/8.0/#sec-abstract-relational-comparison', + AddRestrictedFunctionProperties: 'https://ecma-international.org/ecma-262/8.0/#sec-addrestrictedfunctionproperties', + AddWaiter: 'https://ecma-international.org/ecma-262/8.0/#sec-addwaiter', + AdvanceStringIndex: 'https://ecma-international.org/ecma-262/8.0/#sec-advancestringindex', + 'agent-order': 'https://ecma-international.org/ecma-262/8.0/#sec-agent-order', + AgentCanSuspend: 'https://ecma-international.org/ecma-262/8.0/#sec-agentcansuspend', + AgentSignifier: 'https://ecma-international.org/ecma-262/8.0/#sec-agentsignifier', + AllocateArrayBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-allocatearraybuffer', + AllocateSharedArrayBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-allocatesharedarraybuffer', + AllocateTypedArray: 'https://ecma-international.org/ecma-262/8.0/#sec-allocatetypedarray', + AllocateTypedArrayBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-allocatetypedarraybuffer', + ArrayCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-arraycreate', + ArraySetLength: 'https://ecma-international.org/ecma-262/8.0/#sec-arraysetlength', + ArraySpeciesCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-arrayspeciescreate', + AsyncFunctionAwait: 'https://ecma-international.org/ecma-262/8.0/#sec-async-functions-abstract-operations-async-function-await', + AsyncFunctionCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-async-functions-abstract-operations-async-function-create', + AsyncFunctionStart: 'https://ecma-international.org/ecma-262/8.0/#sec-async-functions-abstract-operations-async-function-start', + AtomicLoad: 'https://ecma-international.org/ecma-262/8.0/#sec-atomicload', + AtomicReadModifyWrite: 'https://ecma-international.org/ecma-262/8.0/#sec-atomicreadmodifywrite', + BlockDeclarationInstantiation: 'https://ecma-international.org/ecma-262/8.0/#sec-blockdeclarationinstantiation', + BoundFunctionCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-boundfunctioncreate', + Call: 'https://ecma-international.org/ecma-262/8.0/#sec-call', + Canonicalize: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-canonicalize-ch', + CanonicalNumericIndexString: 'https://ecma-international.org/ecma-262/8.0/#sec-canonicalnumericindexstring', + CharacterRange: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-characterrange-abstract-operation', + CharacterRangeOrUnion: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-characterrangeorunion-abstract-operation', + CharacterSetMatcher: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-charactersetmatcher-abstract-operation', + CloneArrayBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-clonearraybuffer', + CompletePropertyDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-completepropertydescriptor', + Completion: 'https://ecma-international.org/ecma-262/8.0/#sec-completion-record-specification-type', + ComposeWriteEventBytes: 'https://ecma-international.org/ecma-262/8.0/#sec-composewriteeventbytes', + Construct: 'https://ecma-international.org/ecma-262/8.0/#sec-construct', + CopyDataBlockBytes: 'https://ecma-international.org/ecma-262/8.0/#sec-copydatablockbytes', + CreateArrayFromList: 'https://ecma-international.org/ecma-262/8.0/#sec-createarrayfromlist', + CreateArrayIterator: 'https://ecma-international.org/ecma-262/8.0/#sec-createarrayiterator', + CreateBuiltinFunction: 'https://ecma-international.org/ecma-262/8.0/#sec-createbuiltinfunction', + CreateByteDataBlock: 'https://ecma-international.org/ecma-262/8.0/#sec-createbytedatablock', + CreateDataProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-createdataproperty', + CreateDataPropertyOrThrow: 'https://ecma-international.org/ecma-262/8.0/#sec-createdatapropertyorthrow', + CreateDynamicFunction: 'https://ecma-international.org/ecma-262/8.0/#sec-createdynamicfunction', + CreateHTML: 'https://ecma-international.org/ecma-262/8.0/#sec-createhtml', + CreateIntrinsics: 'https://ecma-international.org/ecma-262/8.0/#sec-createintrinsics', + CreateIterResultObject: 'https://ecma-international.org/ecma-262/8.0/#sec-createiterresultobject', + CreateListFromArrayLike: 'https://ecma-international.org/ecma-262/8.0/#sec-createlistfromarraylike', + CreateListIterator: 'https://ecma-international.org/ecma-262/8.0/#sec-createlistiterator', + CreateMapIterator: 'https://ecma-international.org/ecma-262/8.0/#sec-createmapiterator', + CreateMappedArgumentsObject: 'https://ecma-international.org/ecma-262/8.0/#sec-createmappedargumentsobject', + CreateMethodProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-createmethodproperty', + CreatePerIterationEnvironment: 'https://ecma-international.org/ecma-262/8.0/#sec-createperiterationenvironment', + CreateRealm: 'https://ecma-international.org/ecma-262/8.0/#sec-createrealm', + CreateResolvingFunctions: 'https://ecma-international.org/ecma-262/8.0/#sec-createresolvingfunctions', + CreateSetIterator: 'https://ecma-international.org/ecma-262/8.0/#sec-createsetiterator', + CreateSharedByteDataBlock: 'https://ecma-international.org/ecma-262/8.0/#sec-createsharedbytedatablock', + CreateStringIterator: 'https://ecma-international.org/ecma-262/8.0/#sec-createstringiterator', + CreateUnmappedArgumentsObject: 'https://ecma-international.org/ecma-262/8.0/#sec-createunmappedargumentsobject', + DateFromTime: 'https://ecma-international.org/ecma-262/8.0/#sec-date-number', + Day: 'https://ecma-international.org/ecma-262/8.0/#eqn-Day', + DayFromYear: 'https://ecma-international.org/ecma-262/8.0/#eqn-DaysFromYear', + DaysInYear: 'https://ecma-international.org/ecma-262/8.0/#eqn-DaysInYear', + DayWithinYear: 'https://ecma-international.org/ecma-262/8.0/#eqn-DayWithinYear', + Decode: 'https://ecma-international.org/ecma-262/8.0/#sec-decode', + DefinePropertyOrThrow: 'https://ecma-international.org/ecma-262/8.0/#sec-definepropertyorthrow', + DeletePropertyOrThrow: 'https://ecma-international.org/ecma-262/8.0/#sec-deletepropertyorthrow', + DetachArrayBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-detacharraybuffer', + Encode: 'https://ecma-international.org/ecma-262/8.0/#sec-encode', + EnqueueJob: 'https://ecma-international.org/ecma-262/8.0/#sec-enqueuejob', + EnterCriticalSection: 'https://ecma-international.org/ecma-262/8.0/#sec-entercriticalsection', + EnumerableOwnProperties: 'https://ecma-international.org/ecma-262/8.0/#sec-enumerableownproperties', + EnumerateObjectProperties: 'https://ecma-international.org/ecma-262/8.0/#sec-enumerate-object-properties', + EscapeRegExpPattern: 'https://ecma-international.org/ecma-262/8.0/#sec-escaperegexppattern', + EvalDeclarationInstantiation: 'https://ecma-international.org/ecma-262/8.0/#sec-evaldeclarationinstantiation', + EvaluateCall: 'https://ecma-international.org/ecma-262/8.0/#sec-evaluatecall', + EvaluateDirectCall: 'https://ecma-international.org/ecma-262/8.0/#sec-evaluatedirectcall', + EvaluateNew: 'https://ecma-international.org/ecma-262/8.0/#sec-evaluatenew', + EventSet: 'https://ecma-international.org/ecma-262/8.0/#sec-event-set', + floor: 'https://ecma-international.org/ecma-262/8.0/#eqn-floor', + ForBodyEvaluation: 'https://ecma-international.org/ecma-262/8.0/#sec-forbodyevaluation', + 'ForIn/OfBodyEvaluation': 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset', + 'ForIn/OfHeadEvaluation': 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind', + FromPropertyDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-frompropertydescriptor', + FulfillPromise: 'https://ecma-international.org/ecma-262/8.0/#sec-fulfillpromise', + FunctionAllocate: 'https://ecma-international.org/ecma-262/8.0/#sec-functionallocate', + FunctionCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-functioncreate', + FunctionDeclarationInstantiation: 'https://ecma-international.org/ecma-262/8.0/#sec-functiondeclarationinstantiation', + FunctionInitialize: 'https://ecma-international.org/ecma-262/8.0/#sec-functioninitialize', + GeneratorFunctionCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-generatorfunctioncreate', + GeneratorResume: 'https://ecma-international.org/ecma-262/8.0/#sec-generatorresume', + GeneratorResumeAbrupt: 'https://ecma-international.org/ecma-262/8.0/#sec-generatorresumeabrupt', + GeneratorStart: 'https://ecma-international.org/ecma-262/8.0/#sec-generatorstart', + GeneratorValidate: 'https://ecma-international.org/ecma-262/8.0/#sec-generatorvalidate', + GeneratorYield: 'https://ecma-international.org/ecma-262/8.0/#sec-generatoryield', + Get: 'https://ecma-international.org/ecma-262/8.0/#sec-get-o-p', + GetActiveScriptOrModule: 'https://ecma-international.org/ecma-262/8.0/#sec-getactivescriptormodule', + GetBase: 'https://ecma-international.org/ecma-262/8.0/#ao-getbase', + GetFunctionRealm: 'https://ecma-international.org/ecma-262/8.0/#sec-getfunctionrealm', + GetGlobalObject: 'https://ecma-international.org/ecma-262/8.0/#sec-getglobalobject', + GetIdentifierReference: 'https://ecma-international.org/ecma-262/8.0/#sec-getidentifierreference', + GetIterator: 'https://ecma-international.org/ecma-262/8.0/#sec-getiterator', + GetMethod: 'https://ecma-international.org/ecma-262/8.0/#sec-getmethod', + GetModifySetValueInBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-getmodifysetvalueinbuffer', + GetModuleNamespace: 'https://ecma-international.org/ecma-262/8.0/#sec-getmodulenamespace', + GetNewTarget: 'https://ecma-international.org/ecma-262/8.0/#sec-getnewtarget', + GetOwnPropertyKeys: 'https://ecma-international.org/ecma-262/8.0/#sec-getownpropertykeys', + GetPrototypeFromConstructor: 'https://ecma-international.org/ecma-262/8.0/#sec-getprototypefromconstructor', + GetReferencedName: 'https://ecma-international.org/ecma-262/8.0/#ao-getreferencedname', + GetSubstitution: 'https://ecma-international.org/ecma-262/8.0/#sec-getsubstitution', + GetSuperConstructor: 'https://ecma-international.org/ecma-262/8.0/#sec-getsuperconstructor', + GetTemplateObject: 'https://ecma-international.org/ecma-262/8.0/#sec-gettemplateobject', + GetThisEnvironment: 'https://ecma-international.org/ecma-262/8.0/#sec-getthisenvironment', + GetThisValue: 'https://ecma-international.org/ecma-262/8.0/#sec-getthisvalue', + GetV: 'https://ecma-international.org/ecma-262/8.0/#sec-getv', + GetValue: 'https://ecma-international.org/ecma-262/8.0/#sec-getvalue', + GetValueFromBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-getvaluefrombuffer', + GetViewValue: 'https://ecma-international.org/ecma-262/8.0/#sec-getviewvalue', + GetWaiterList: 'https://ecma-international.org/ecma-262/8.0/#sec-getwaiterlist', + GlobalDeclarationInstantiation: 'https://ecma-international.org/ecma-262/8.0/#sec-globaldeclarationinstantiation', + 'happens-before': 'https://ecma-international.org/ecma-262/8.0/#sec-happens-before', + HasOwnProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-hasownproperty', + HasPrimitiveBase: 'https://ecma-international.org/ecma-262/8.0/#ao-hasprimitivebase', + HasProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-hasproperty', + 'host-synchronizes-with': 'https://ecma-international.org/ecma-262/8.0/#sec-host-synchronizes-with', + HostEnsureCanCompileStrings: 'https://ecma-international.org/ecma-262/8.0/#sec-hostensurecancompilestrings', + HostEventSet: 'https://ecma-international.org/ecma-262/8.0/#sec-hosteventset', + HostPromiseRejectionTracker: 'https://ecma-international.org/ecma-262/8.0/#sec-host-promise-rejection-tracker', + HostReportErrors: 'https://ecma-international.org/ecma-262/8.0/#sec-host-report-errors', + HostResolveImportedModule: 'https://ecma-international.org/ecma-262/8.0/#sec-hostresolveimportedmodule', + HourFromTime: 'https://ecma-international.org/ecma-262/8.0/#eqn-HourFromTime', + HoursPerDay: 'https://ecma-international.org/ecma-262/8.0/#eqn-HoursPerDay', + IfAbruptRejectPromise: 'https://ecma-international.org/ecma-262/8.0/#sec-ifabruptrejectpromise', + ImportedLocalNames: 'https://ecma-international.org/ecma-262/8.0/#sec-importedlocalnames', + InitializeBoundName: 'https://ecma-international.org/ecma-262/8.0/#sec-initializeboundname', + InitializeHostDefinedRealm: 'https://ecma-international.org/ecma-262/8.0/#sec-initializehostdefinedrealm', + InitializeReferencedBinding: 'https://ecma-international.org/ecma-262/8.0/#sec-initializereferencedbinding', + InLeapYear: 'https://ecma-international.org/ecma-262/8.0/#eqn-InLeapYear', + InstanceofOperator: 'https://ecma-international.org/ecma-262/8.0/#sec-instanceofoperator', + IntegerIndexedElementGet: 'https://ecma-international.org/ecma-262/8.0/#sec-integerindexedelementget', + IntegerIndexedElementSet: 'https://ecma-international.org/ecma-262/8.0/#sec-integerindexedelementset', + IntegerIndexedObjectCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-integerindexedobjectcreate', + InternalizeJSONProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-internalizejsonproperty', + Invoke: 'https://ecma-international.org/ecma-262/8.0/#sec-invoke', + IsAccessorDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-isaccessordescriptor', + IsAnonymousFunctionDefinition: 'https://ecma-international.org/ecma-262/8.0/#sec-isanonymousfunctiondefinition', + IsArray: 'https://ecma-international.org/ecma-262/8.0/#sec-isarray', + IsCallable: 'https://ecma-international.org/ecma-262/8.0/#sec-iscallable', + IsCompatiblePropertyDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-iscompatiblepropertydescriptor', + IsConcatSpreadable: 'https://ecma-international.org/ecma-262/8.0/#sec-isconcatspreadable', + IsConstructor: 'https://ecma-international.org/ecma-262/8.0/#sec-isconstructor', + IsDataDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-isdatadescriptor', + IsDetachedBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-isdetachedbuffer', + IsExtensible: 'https://ecma-international.org/ecma-262/8.0/#sec-isextensible-o', + IsGenericDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-isgenericdescriptor', + IsInTailPosition: 'https://ecma-international.org/ecma-262/8.0/#sec-isintailposition', + IsInteger: 'https://ecma-international.org/ecma-262/8.0/#sec-isinteger', + IsLabelledFunction: 'https://ecma-international.org/ecma-262/8.0/#sec-islabelledfunction', + IsPromise: 'https://ecma-international.org/ecma-262/8.0/#sec-ispromise', + IsPropertyKey: 'https://ecma-international.org/ecma-262/8.0/#sec-ispropertykey', + IsPropertyReference: 'https://ecma-international.org/ecma-262/8.0/#ao-ispropertyreference', + IsRegExp: 'https://ecma-international.org/ecma-262/8.0/#sec-isregexp', + IsSharedArrayBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-issharedarraybuffer', + IsStrictReference: 'https://ecma-international.org/ecma-262/8.0/#ao-isstrictreference', + IsSuperReference: 'https://ecma-international.org/ecma-262/8.0/#ao-issuperreference', + IsUnresolvableReference: 'https://ecma-international.org/ecma-262/8.0/#ao-isunresolvablereference', + IsWordChar: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-iswordchar-abstract-operation', + IterableToList: 'https://ecma-international.org/ecma-262/8.0/#sec-iterabletolist', + IteratorClose: 'https://ecma-international.org/ecma-262/8.0/#sec-iteratorclose', + IteratorComplete: 'https://ecma-international.org/ecma-262/8.0/#sec-iteratorcomplete', + IteratorNext: 'https://ecma-international.org/ecma-262/8.0/#sec-iteratornext', + IteratorStep: 'https://ecma-international.org/ecma-262/8.0/#sec-iteratorstep', + IteratorValue: 'https://ecma-international.org/ecma-262/8.0/#sec-iteratorvalue', + LeaveCriticalSection: 'https://ecma-international.org/ecma-262/8.0/#sec-leavecriticalsection', + LocalTime: 'https://ecma-international.org/ecma-262/8.0/#sec-localtime', + LoopContinues: 'https://ecma-international.org/ecma-262/8.0/#sec-loopcontinues', + MakeArgGetter: 'https://ecma-international.org/ecma-262/8.0/#sec-makearggetter', + MakeArgSetter: 'https://ecma-international.org/ecma-262/8.0/#sec-makeargsetter', + MakeClassConstructor: 'https://ecma-international.org/ecma-262/8.0/#sec-makeclassconstructor', + MakeConstructor: 'https://ecma-international.org/ecma-262/8.0/#sec-makeconstructor', + MakeDate: 'https://ecma-international.org/ecma-262/8.0/#sec-makedate', + MakeDay: 'https://ecma-international.org/ecma-262/8.0/#sec-makeday', + MakeMethod: 'https://ecma-international.org/ecma-262/8.0/#sec-makemethod', + MakeSuperPropertyReference: 'https://ecma-international.org/ecma-262/8.0/#sec-makesuperpropertyreference', + MakeTime: 'https://ecma-international.org/ecma-262/8.0/#sec-maketime', + max: 'https://ecma-international.org/ecma-262/8.0/#eqn-max', + 'memory-order': 'https://ecma-international.org/ecma-262/8.0/#sec-memory-order', + min: 'https://ecma-international.org/ecma-262/8.0/#eqn-min', + MinFromTime: 'https://ecma-international.org/ecma-262/8.0/#eqn-MinFromTime', + MinutesPerHour: 'https://ecma-international.org/ecma-262/8.0/#eqn-MinutesPerHour', + ModuleNamespaceCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-modulenamespacecreate', + modulo: 'https://ecma-international.org/ecma-262/8.0/#eqn-modulo', + MonthFromTime: 'https://ecma-international.org/ecma-262/8.0/#eqn-MonthFromTime', + msFromTime: 'https://ecma-international.org/ecma-262/8.0/#eqn-msFromTime', + msPerDay: 'https://ecma-international.org/ecma-262/8.0/#eqn-msPerDay', + msPerHour: 'https://ecma-international.org/ecma-262/8.0/#eqn-msPerHour', + msPerMinute: 'https://ecma-international.org/ecma-262/8.0/#eqn-msPerMinute', + msPerSecond: 'https://ecma-international.org/ecma-262/8.0/#eqn-msPerSecond', + NewDeclarativeEnvironment: 'https://ecma-international.org/ecma-262/8.0/#sec-newdeclarativeenvironment', + NewFunctionEnvironment: 'https://ecma-international.org/ecma-262/8.0/#sec-newfunctionenvironment', + NewGlobalEnvironment: 'https://ecma-international.org/ecma-262/8.0/#sec-newglobalenvironment', + NewModuleEnvironment: 'https://ecma-international.org/ecma-262/8.0/#sec-newmoduleenvironment', + NewObjectEnvironment: 'https://ecma-international.org/ecma-262/8.0/#sec-newobjectenvironment', + NewPromiseCapability: 'https://ecma-international.org/ecma-262/8.0/#sec-newpromisecapability', + NormalCompletion: 'https://ecma-international.org/ecma-262/8.0/#sec-normalcompletion', + NumberToRawBytes: 'https://ecma-international.org/ecma-262/8.0/#sec-numbertorawbytes', + ObjectCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-objectcreate', + ObjectDefineProperties: 'https://ecma-international.org/ecma-262/8.0/#sec-objectdefineproperties', + OrdinaryCallBindThis: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarycallbindthis', + OrdinaryCallEvaluateBody: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarycallevaluatebody', + OrdinaryCreateFromConstructor: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarycreatefromconstructor', + OrdinaryDefineOwnProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarydefineownproperty', + OrdinaryDelete: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarydelete', + OrdinaryGet: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinaryget', + OrdinaryGetOwnProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarygetownproperty', + OrdinaryGetPrototypeOf: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarygetprototypeof', + OrdinaryHasInstance: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinaryhasinstance', + OrdinaryHasProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinaryhasproperty', + OrdinaryIsExtensible: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinaryisextensible', + OrdinaryOwnPropertyKeys: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinaryownpropertykeys', + OrdinaryPreventExtensions: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarypreventextensions', + OrdinarySet: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinaryset', + OrdinarySetPrototypeOf: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarysetprototypeof', + OrdinaryToPrimitive: 'https://ecma-international.org/ecma-262/8.0/#sec-ordinarytoprimitive', + ParseModule: 'https://ecma-international.org/ecma-262/8.0/#sec-parsemodule', + ParseScript: 'https://ecma-international.org/ecma-262/8.0/#sec-parse-script', + PerformEval: 'https://ecma-international.org/ecma-262/8.0/#sec-performeval', + PerformPromiseAll: 'https://ecma-international.org/ecma-262/8.0/#sec-performpromiseall', + PerformPromiseRace: 'https://ecma-international.org/ecma-262/8.0/#sec-performpromiserace', + PerformPromiseThen: 'https://ecma-international.org/ecma-262/8.0/#sec-performpromisethen', + PrepareForOrdinaryCall: 'https://ecma-international.org/ecma-262/8.0/#sec-prepareforordinarycall', + PrepareForTailCall: 'https://ecma-international.org/ecma-262/8.0/#sec-preparefortailcall', + PromiseReactionJob: 'https://ecma-international.org/ecma-262/8.0/#sec-promisereactionjob', + PromiseResolveThenableJob: 'https://ecma-international.org/ecma-262/8.0/#sec-promiseresolvethenablejob', + ProxyCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-proxycreate', + PutValue: 'https://ecma-international.org/ecma-262/8.0/#sec-putvalue', + QuoteJSONString: 'https://ecma-international.org/ecma-262/8.0/#sec-quotejsonstring', + RawBytesToNumber: 'https://ecma-international.org/ecma-262/8.0/#sec-rawbytestonumber', + 'reads-bytes-from': 'https://ecma-international.org/ecma-262/8.0/#sec-reads-bytes-from', + 'reads-from': 'https://ecma-international.org/ecma-262/8.0/#sec-reads-from', + RegExpAlloc: 'https://ecma-international.org/ecma-262/8.0/#sec-regexpalloc', + RegExpBuiltinExec: 'https://ecma-international.org/ecma-262/8.0/#sec-regexpbuiltinexec', + RegExpCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-regexpcreate', + RegExpExec: 'https://ecma-international.org/ecma-262/8.0/#sec-regexpexec', + RegExpInitialize: 'https://ecma-international.org/ecma-262/8.0/#sec-regexpinitialize', + RejectPromise: 'https://ecma-international.org/ecma-262/8.0/#sec-rejectpromise', + RemoveWaiter: 'https://ecma-international.org/ecma-262/8.0/#sec-removewaiter', + RemoveWaiters: 'https://ecma-international.org/ecma-262/8.0/#sec-removewaiters', + RepeatMatcher: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-repeatmatcher-abstract-operation', + RequireObjectCoercible: 'https://ecma-international.org/ecma-262/8.0/#sec-requireobjectcoercible', + ResolveBinding: 'https://ecma-international.org/ecma-262/8.0/#sec-resolvebinding', + ResolveThisBinding: 'https://ecma-international.org/ecma-262/8.0/#sec-resolvethisbinding', + ReturnIfAbrupt: 'https://ecma-international.org/ecma-262/8.0/#sec-returnifabrupt', + RunJobs: 'https://ecma-international.org/ecma-262/8.0/#sec-runjobs', + SameValue: 'https://ecma-international.org/ecma-262/8.0/#sec-samevalue', + SameValueNonNumber: 'https://ecma-international.org/ecma-262/8.0/#sec-samevaluenonnumber', + SameValueZero: 'https://ecma-international.org/ecma-262/8.0/#sec-samevaluezero', + ScriptEvaluation: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-scriptevaluation', + ScriptEvaluationJob: 'https://ecma-international.org/ecma-262/8.0/#sec-scriptevaluationjob', + SecFromTime: 'https://ecma-international.org/ecma-262/8.0/#eqn-SecFromTime', + SecondsPerMinute: 'https://ecma-international.org/ecma-262/8.0/#eqn-SecondsPerMinute', + SerializeJSONArray: 'https://ecma-international.org/ecma-262/8.0/#sec-serializejsonarray', + SerializeJSONObject: 'https://ecma-international.org/ecma-262/8.0/#sec-serializejsonobject', + SerializeJSONProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-serializejsonproperty', + Set: 'https://ecma-international.org/ecma-262/8.0/#sec-set-o-p-v-throw', + SetDefaultGlobalBindings: 'https://ecma-international.org/ecma-262/8.0/#sec-setdefaultglobalbindings', + SetFunctionName: 'https://ecma-international.org/ecma-262/8.0/#sec-setfunctionname', + SetImmutablePrototype: 'https://ecma-international.org/ecma-262/8.0/#sec-set-immutable-prototype', + SetIntegrityLevel: 'https://ecma-international.org/ecma-262/8.0/#sec-setintegritylevel', + SetRealmGlobalObject: 'https://ecma-international.org/ecma-262/8.0/#sec-setrealmglobalobject', + SetValueInBuffer: 'https://ecma-international.org/ecma-262/8.0/#sec-setvalueinbuffer', + SetViewValue: 'https://ecma-international.org/ecma-262/8.0/#sec-setviewvalue', + SharedDataBlockEventSet: 'https://ecma-international.org/ecma-262/8.0/#sec-sharedatablockeventset', + SortCompare: 'https://ecma-international.org/ecma-262/8.0/#sec-sortcompare', + SpeciesConstructor: 'https://ecma-international.org/ecma-262/8.0/#sec-speciesconstructor', + SplitMatch: 'https://ecma-international.org/ecma-262/8.0/#sec-splitmatch', + 'Strict Equality Comparison': 'https://ecma-international.org/ecma-262/8.0/#sec-strict-equality-comparison', + StringCreate: 'https://ecma-international.org/ecma-262/8.0/#sec-stringcreate', + StringGetOwnProperty: 'https://ecma-international.org/ecma-262/8.0/#sec-stringgetownproperty', + Suspend: 'https://ecma-international.org/ecma-262/8.0/#sec-suspend', + SymbolDescriptiveString: 'https://ecma-international.org/ecma-262/8.0/#sec-symboldescriptivestring', + 'synchronizes-with': 'https://ecma-international.org/ecma-262/8.0/#sec-synchronizes-with', + TestIntegrityLevel: 'https://ecma-international.org/ecma-262/8.0/#sec-testintegritylevel', + thisBooleanValue: 'https://ecma-international.org/ecma-262/8.0/#sec-thisbooleanvalue', + thisNumberValue: 'https://ecma-international.org/ecma-262/8.0/#sec-thisnumbervalue', + thisStringValue: 'https://ecma-international.org/ecma-262/8.0/#sec-thisstringvalue', + thisTimeValue: 'https://ecma-international.org/ecma-262/8.0/#sec-thistimevalue', + TimeClip: 'https://ecma-international.org/ecma-262/8.0/#sec-timeclip', + TimeFromYear: 'https://ecma-international.org/ecma-262/8.0/#eqn-TimeFromYear', + TimeWithinDay: 'https://ecma-international.org/ecma-262/8.0/#eqn-TimeWithinDay', + ToBoolean: 'https://ecma-international.org/ecma-262/8.0/#sec-toboolean', + ToDateString: 'https://ecma-international.org/ecma-262/8.0/#sec-todatestring', + ToIndex: 'https://ecma-international.org/ecma-262/8.0/#sec-toindex', + ToInt16: 'https://ecma-international.org/ecma-262/8.0/#sec-toint16', + ToInt32: 'https://ecma-international.org/ecma-262/8.0/#sec-toint32', + ToInt8: 'https://ecma-international.org/ecma-262/8.0/#sec-toint8', + ToInteger: 'https://ecma-international.org/ecma-262/8.0/#sec-tointeger', + ToLength: 'https://ecma-international.org/ecma-262/8.0/#sec-tolength', + ToNumber: 'https://ecma-international.org/ecma-262/8.0/#sec-tonumber', + ToObject: 'https://ecma-international.org/ecma-262/8.0/#sec-toobject', + TopLevelModuleEvaluationJob: 'https://ecma-international.org/ecma-262/8.0/#sec-toplevelmoduleevaluationjob', + ToPrimitive: 'https://ecma-international.org/ecma-262/8.0/#sec-toprimitive', + ToPropertyDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-topropertydescriptor', + ToPropertyKey: 'https://ecma-international.org/ecma-262/8.0/#sec-topropertykey', + ToString: 'https://ecma-international.org/ecma-262/8.0/#sec-tostring', + 'ToString Applied to the Number Type': 'https://ecma-international.org/ecma-262/8.0/#sec-tostring-applied-to-the-number-type', + ToUint16: 'https://ecma-international.org/ecma-262/8.0/#sec-touint16', + ToUint32: 'https://ecma-international.org/ecma-262/8.0/#sec-touint32', + ToUint8: 'https://ecma-international.org/ecma-262/8.0/#sec-touint8', + ToUint8Clamp: 'https://ecma-international.org/ecma-262/8.0/#sec-touint8clamp', + TriggerPromiseReactions: 'https://ecma-international.org/ecma-262/8.0/#sec-triggerpromisereactions', + Type: 'https://ecma-international.org/ecma-262/8.0/#sec-ecmascript-data-types-and-values', + TypedArrayCreate: 'https://ecma-international.org/ecma-262/8.0/#typedarray-create', + TypedArraySpeciesCreate: 'https://ecma-international.org/ecma-262/8.0/#typedarray-species-create', + UpdateEmpty: 'https://ecma-international.org/ecma-262/8.0/#sec-updateempty', + UTC: 'https://ecma-international.org/ecma-262/8.0/#sec-utc-t', + UTF16Decode: 'https://ecma-international.org/ecma-262/8.0/#sec-utf16decode', + UTF16Encoding: 'https://ecma-international.org/ecma-262/8.0/#sec-utf16encoding', + ValidateAndApplyPropertyDescriptor: 'https://ecma-international.org/ecma-262/8.0/#sec-validateandapplypropertydescriptor', + ValidateAtomicAccess: 'https://ecma-international.org/ecma-262/8.0/#sec-validateatomicaccess', + ValidateSharedIntegerTypedArray: 'https://ecma-international.org/ecma-262/8.0/#sec-validatesharedintegertypedarray', + ValidateTypedArray: 'https://ecma-international.org/ecma-262/8.0/#sec-validatetypedarray', + ValueOfReadEvent: 'https://ecma-international.org/ecma-262/8.0/#sec-valueofreadevent', + WakeWaiter: 'https://ecma-international.org/ecma-262/8.0/#sec-wakewaiter', + WeekDay: 'https://ecma-international.org/ecma-262/8.0/#sec-week-day', + WordCharacters: 'https://ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-wordcharacters-abstract-operation', + YearFromTime: 'https://ecma-international.org/ecma-262/8.0/#eqn-YearFromTime' +}; diff --git a/WebServer/node_modules/es-abstract/operations/2018.js b/WebServer/node_modules/es-abstract/operations/2018.js new file mode 100644 index 000000000..7d4b388b5 --- /dev/null +++ b/WebServer/node_modules/es-abstract/operations/2018.js @@ -0,0 +1,357 @@ +'use strict'; + +module.exports = { + abs: 'https://ecma-international.org/ecma-262/9.0/#eqn-abs', + 'Abstract Equality Comparison': 'https://ecma-international.org/ecma-262/9.0/#sec-abstract-equality-comparison', + 'Abstract Relational Comparison': 'https://ecma-international.org/ecma-262/9.0/#sec-abstract-relational-comparison', + AddRestrictedFunctionProperties: 'https://ecma-international.org/ecma-262/9.0/#sec-addrestrictedfunctionproperties', + AddWaiter: 'https://ecma-international.org/ecma-262/9.0/#sec-addwaiter', + AdvanceStringIndex: 'https://ecma-international.org/ecma-262/9.0/#sec-advancestringindex', + 'agent-order': 'https://ecma-international.org/ecma-262/9.0/#sec-agent-order', + AgentCanSuspend: 'https://ecma-international.org/ecma-262/9.0/#sec-agentcansuspend', + AgentSignifier: 'https://ecma-international.org/ecma-262/9.0/#sec-agentsignifier', + AllocateArrayBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-allocatearraybuffer', + AllocateSharedArrayBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-allocatesharedarraybuffer', + AllocateTypedArray: 'https://ecma-international.org/ecma-262/9.0/#sec-allocatetypedarray', + AllocateTypedArrayBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-allocatetypedarraybuffer', + ArrayCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-arraycreate', + ArraySetLength: 'https://ecma-international.org/ecma-262/9.0/#sec-arraysetlength', + ArraySpeciesCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-arrayspeciescreate', + AsyncFunctionCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-async-functions-abstract-operations-async-function-create', + AsyncFunctionStart: 'https://ecma-international.org/ecma-262/9.0/#sec-async-functions-abstract-operations-async-function-start', + AsyncGeneratorEnqueue: 'https://ecma-international.org/ecma-262/9.0/#sec-asyncgeneratorenqueue', + AsyncGeneratorFunctionCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-asyncgeneratorfunctioncreate', + AsyncGeneratorReject: 'https://ecma-international.org/ecma-262/9.0/#sec-asyncgeneratorreject', + AsyncGeneratorResolve: 'https://ecma-international.org/ecma-262/9.0/#sec-asyncgeneratorresolve', + AsyncGeneratorResumeNext: 'https://ecma-international.org/ecma-262/9.0/#sec-asyncgeneratorresumenext', + AsyncGeneratorStart: 'https://ecma-international.org/ecma-262/9.0/#sec-asyncgeneratorstart', + AsyncGeneratorYield: 'https://ecma-international.org/ecma-262/9.0/#sec-asyncgeneratoryield', + AsyncIteratorClose: 'https://ecma-international.org/ecma-262/9.0/#sec-asynciteratorclose', + AtomicLoad: 'https://ecma-international.org/ecma-262/9.0/#sec-atomicload', + AtomicReadModifyWrite: 'https://ecma-international.org/ecma-262/9.0/#sec-atomicreadmodifywrite', + Await: 'https://ecma-international.org/ecma-262/9.0/#await', + BackreferenceMatcher: 'https://ecma-international.org/ecma-262/9.0/#sec-backreference-matcher', + BlockDeclarationInstantiation: 'https://ecma-international.org/ecma-262/9.0/#sec-blockdeclarationinstantiation', + BoundFunctionCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-boundfunctioncreate', + Call: 'https://ecma-international.org/ecma-262/9.0/#sec-call', + Canonicalize: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-canonicalize-ch', + CanonicalNumericIndexString: 'https://ecma-international.org/ecma-262/9.0/#sec-canonicalnumericindexstring', + CaseClauseIsSelected: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-caseclauseisselected', + CharacterRange: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-characterrange-abstract-operation', + CharacterRangeOrUnion: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-characterrangeorunion-abstract-operation', + CharacterSetMatcher: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-charactersetmatcher-abstract-operation', + CloneArrayBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-clonearraybuffer', + CompletePropertyDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-completepropertydescriptor', + Completion: 'https://ecma-international.org/ecma-262/9.0/#sec-completion-record-specification-type', + ComposeWriteEventBytes: 'https://ecma-international.org/ecma-262/9.0/#sec-composewriteeventbytes', + Construct: 'https://ecma-international.org/ecma-262/9.0/#sec-construct', + CopyDataBlockBytes: 'https://ecma-international.org/ecma-262/9.0/#sec-copydatablockbytes', + CopyDataProperties: 'https://ecma-international.org/ecma-262/9.0/#sec-copydataproperties', + CreateArrayFromList: 'https://ecma-international.org/ecma-262/9.0/#sec-createarrayfromlist', + CreateArrayIterator: 'https://ecma-international.org/ecma-262/9.0/#sec-createarrayiterator', + CreateAsyncFromSyncIterator: 'https://ecma-international.org/ecma-262/9.0/#sec-createasyncfromsynciterator', + CreateBuiltinFunction: 'https://ecma-international.org/ecma-262/9.0/#sec-createbuiltinfunction', + CreateByteDataBlock: 'https://ecma-international.org/ecma-262/9.0/#sec-createbytedatablock', + CreateDataProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-createdataproperty', + CreateDataPropertyOrThrow: 'https://ecma-international.org/ecma-262/9.0/#sec-createdatapropertyorthrow', + CreateDynamicFunction: 'https://ecma-international.org/ecma-262/9.0/#sec-createdynamicfunction', + CreateHTML: 'https://ecma-international.org/ecma-262/9.0/#sec-createhtml', + CreateIntrinsics: 'https://ecma-international.org/ecma-262/9.0/#sec-createintrinsics', + CreateIterResultObject: 'https://ecma-international.org/ecma-262/9.0/#sec-createiterresultobject', + CreateListFromArrayLike: 'https://ecma-international.org/ecma-262/9.0/#sec-createlistfromarraylike', + CreateListIteratorRecord: 'https://ecma-international.org/ecma-262/9.0/#sec-createlistiteratorRecord', + CreateMapIterator: 'https://ecma-international.org/ecma-262/9.0/#sec-createmapiterator', + CreateMappedArgumentsObject: 'https://ecma-international.org/ecma-262/9.0/#sec-createmappedargumentsobject', + CreateMethodProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-createmethodproperty', + CreatePerIterationEnvironment: 'https://ecma-international.org/ecma-262/9.0/#sec-createperiterationenvironment', + CreateRealm: 'https://ecma-international.org/ecma-262/9.0/#sec-createrealm', + CreateResolvingFunctions: 'https://ecma-international.org/ecma-262/9.0/#sec-createresolvingfunctions', + CreateSetIterator: 'https://ecma-international.org/ecma-262/9.0/#sec-createsetiterator', + CreateSharedByteDataBlock: 'https://ecma-international.org/ecma-262/9.0/#sec-createsharedbytedatablock', + CreateStringIterator: 'https://ecma-international.org/ecma-262/9.0/#sec-createstringiterator', + CreateUnmappedArgumentsObject: 'https://ecma-international.org/ecma-262/9.0/#sec-createunmappedargumentsobject', + DateFromTime: 'https://ecma-international.org/ecma-262/9.0/#sec-date-number', + DateString: 'https://ecma-international.org/ecma-262/9.0/#sec-datestring', + Day: 'https://ecma-international.org/ecma-262/9.0/#eqn-Day', + DayFromYear: 'https://ecma-international.org/ecma-262/9.0/#eqn-DaysFromYear', + DaysInYear: 'https://ecma-international.org/ecma-262/9.0/#eqn-DaysInYear', + DayWithinYear: 'https://ecma-international.org/ecma-262/9.0/#eqn-DayWithinYear', + Decode: 'https://ecma-international.org/ecma-262/9.0/#sec-decode', + DefinePropertyOrThrow: 'https://ecma-international.org/ecma-262/9.0/#sec-definepropertyorthrow', + DeletePropertyOrThrow: 'https://ecma-international.org/ecma-262/9.0/#sec-deletepropertyorthrow', + DetachArrayBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-detacharraybuffer', + Encode: 'https://ecma-international.org/ecma-262/9.0/#sec-encode', + EnqueueJob: 'https://ecma-international.org/ecma-262/9.0/#sec-enqueuejob', + EnterCriticalSection: 'https://ecma-international.org/ecma-262/9.0/#sec-entercriticalsection', + EnumerableOwnPropertyNames: 'https://ecma-international.org/ecma-262/9.0/#sec-enumerableownpropertynames', + EnumerateObjectProperties: 'https://ecma-international.org/ecma-262/9.0/#sec-enumerate-object-properties', + EscapeRegExpPattern: 'https://ecma-international.org/ecma-262/9.0/#sec-escaperegexppattern', + EvalDeclarationInstantiation: 'https://ecma-international.org/ecma-262/9.0/#sec-evaldeclarationinstantiation', + EvaluateCall: 'https://ecma-international.org/ecma-262/9.0/#sec-evaluatecall', + EvaluateNew: 'https://ecma-international.org/ecma-262/9.0/#sec-evaluatenew', + EventSet: 'https://ecma-international.org/ecma-262/9.0/#sec-event-set', + floor: 'https://ecma-international.org/ecma-262/9.0/#eqn-floor', + ForBodyEvaluation: 'https://ecma-international.org/ecma-262/9.0/#sec-forbodyevaluation', + 'ForIn/OfBodyEvaluation': 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset', + 'ForIn/OfHeadEvaluation': 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind', + FromPropertyDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-frompropertydescriptor', + FulfillPromise: 'https://ecma-international.org/ecma-262/9.0/#sec-fulfillpromise', + FunctionAllocate: 'https://ecma-international.org/ecma-262/9.0/#sec-functionallocate', + FunctionCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-functioncreate', + FunctionDeclarationInstantiation: 'https://ecma-international.org/ecma-262/9.0/#sec-functiondeclarationinstantiation', + FunctionInitialize: 'https://ecma-international.org/ecma-262/9.0/#sec-functioninitialize', + GeneratorFunctionCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-generatorfunctioncreate', + GeneratorResume: 'https://ecma-international.org/ecma-262/9.0/#sec-generatorresume', + GeneratorResumeAbrupt: 'https://ecma-international.org/ecma-262/9.0/#sec-generatorresumeabrupt', + GeneratorStart: 'https://ecma-international.org/ecma-262/9.0/#sec-generatorstart', + GeneratorValidate: 'https://ecma-international.org/ecma-262/9.0/#sec-generatorvalidate', + GeneratorYield: 'https://ecma-international.org/ecma-262/9.0/#sec-generatoryield', + Get: 'https://ecma-international.org/ecma-262/9.0/#sec-get-o-p', + GetActiveScriptOrModule: 'https://ecma-international.org/ecma-262/9.0/#sec-getactivescriptormodule', + GetBase: 'https://ecma-international.org/ecma-262/9.0/#sec-getbase', + GetFunctionRealm: 'https://ecma-international.org/ecma-262/9.0/#sec-getfunctionrealm', + GetGeneratorKind: 'https://ecma-international.org/ecma-262/9.0/#sec-getgeneratorkind', + GetGlobalObject: 'https://ecma-international.org/ecma-262/9.0/#sec-getglobalobject', + GetIdentifierReference: 'https://ecma-international.org/ecma-262/9.0/#sec-getidentifierreference', + GetIterator: 'https://ecma-international.org/ecma-262/9.0/#sec-getiterator', + GetMethod: 'https://ecma-international.org/ecma-262/9.0/#sec-getmethod', + GetModifySetValueInBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-getmodifysetvalueinbuffer', + GetModuleNamespace: 'https://ecma-international.org/ecma-262/9.0/#sec-getmodulenamespace', + GetNewTarget: 'https://ecma-international.org/ecma-262/9.0/#sec-getnewtarget', + GetOwnPropertyKeys: 'https://ecma-international.org/ecma-262/9.0/#sec-getownpropertykeys', + GetPrototypeFromConstructor: 'https://ecma-international.org/ecma-262/9.0/#sec-getprototypefromconstructor', + GetReferencedName: 'https://ecma-international.org/ecma-262/9.0/#sec-getreferencedname', + GetSubstitution: 'https://ecma-international.org/ecma-262/9.0/#sec-getsubstitution', + GetSuperConstructor: 'https://ecma-international.org/ecma-262/9.0/#sec-getsuperconstructor', + GetTemplateObject: 'https://ecma-international.org/ecma-262/9.0/#sec-gettemplateobject', + GetThisEnvironment: 'https://ecma-international.org/ecma-262/9.0/#sec-getthisenvironment', + GetThisValue: 'https://ecma-international.org/ecma-262/9.0/#sec-getthisvalue', + GetV: 'https://ecma-international.org/ecma-262/9.0/#sec-getv', + GetValue: 'https://ecma-international.org/ecma-262/9.0/#sec-getvalue', + GetValueFromBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-getvaluefrombuffer', + GetViewValue: 'https://ecma-international.org/ecma-262/9.0/#sec-getviewvalue', + GetWaiterList: 'https://ecma-international.org/ecma-262/9.0/#sec-getwaiterlist', + GlobalDeclarationInstantiation: 'https://ecma-international.org/ecma-262/9.0/#sec-globaldeclarationinstantiation', + 'happens-before': 'https://ecma-international.org/ecma-262/9.0/#sec-happens-before', + HasOwnProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-hasownproperty', + HasPrimitiveBase: 'https://ecma-international.org/ecma-262/9.0/#sec-hasprimitivebase', + HasProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-hasproperty', + 'host-synchronizes-with': 'https://ecma-international.org/ecma-262/9.0/#sec-host-synchronizes-with', + HostEnsureCanCompileStrings: 'https://ecma-international.org/ecma-262/9.0/#sec-hostensurecancompilestrings', + HostEventSet: 'https://ecma-international.org/ecma-262/9.0/#sec-hosteventset', + HostPromiseRejectionTracker: 'https://ecma-international.org/ecma-262/9.0/#sec-host-promise-rejection-tracker', + HostReportErrors: 'https://ecma-international.org/ecma-262/9.0/#sec-host-report-errors', + HostResolveImportedModule: 'https://ecma-international.org/ecma-262/9.0/#sec-hostresolveimportedmodule', + HourFromTime: 'https://ecma-international.org/ecma-262/9.0/#eqn-HourFromTime', + HoursPerDay: 'https://ecma-international.org/ecma-262/9.0/#eqn-HoursPerDay', + IfAbruptRejectPromise: 'https://ecma-international.org/ecma-262/9.0/#sec-ifabruptrejectpromise', + ImportedLocalNames: 'https://ecma-international.org/ecma-262/9.0/#sec-importedlocalnames', + InitializeBoundName: 'https://ecma-international.org/ecma-262/9.0/#sec-initializeboundname', + InitializeHostDefinedRealm: 'https://ecma-international.org/ecma-262/9.0/#sec-initializehostdefinedrealm', + InitializeReferencedBinding: 'https://ecma-international.org/ecma-262/9.0/#sec-initializereferencedbinding', + InLeapYear: 'https://ecma-international.org/ecma-262/9.0/#eqn-InLeapYear', + InnerModuleEvaluation: 'https://ecma-international.org/ecma-262/9.0/#sec-innermoduleevaluation', + InnerModuleInstantiation: 'https://ecma-international.org/ecma-262/9.0/#sec-innermoduleinstantiation', + InstanceofOperator: 'https://ecma-international.org/ecma-262/9.0/#sec-instanceofoperator', + IntegerIndexedElementGet: 'https://ecma-international.org/ecma-262/9.0/#sec-integerindexedelementget', + IntegerIndexedElementSet: 'https://ecma-international.org/ecma-262/9.0/#sec-integerindexedelementset', + IntegerIndexedObjectCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-integerindexedobjectcreate', + InternalizeJSONProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-internalizejsonproperty', + Invoke: 'https://ecma-international.org/ecma-262/9.0/#sec-invoke', + IsAccessorDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-isaccessordescriptor', + IsAnonymousFunctionDefinition: 'https://ecma-international.org/ecma-262/9.0/#sec-isanonymousfunctiondefinition', + IsArray: 'https://ecma-international.org/ecma-262/9.0/#sec-isarray', + IsCallable: 'https://ecma-international.org/ecma-262/9.0/#sec-iscallable', + IsCompatiblePropertyDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-iscompatiblepropertydescriptor', + IsConcatSpreadable: 'https://ecma-international.org/ecma-262/9.0/#sec-isconcatspreadable', + IsConstructor: 'https://ecma-international.org/ecma-262/9.0/#sec-isconstructor', + IsDataDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-isdatadescriptor', + IsDetachedBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-isdetachedbuffer', + IsExtensible: 'https://ecma-international.org/ecma-262/9.0/#sec-isextensible-o', + IsGenericDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-isgenericdescriptor', + IsInTailPosition: 'https://ecma-international.org/ecma-262/9.0/#sec-isintailposition', + IsInteger: 'https://ecma-international.org/ecma-262/9.0/#sec-isinteger', + IsLabelledFunction: 'https://ecma-international.org/ecma-262/9.0/#sec-islabelledfunction', + IsPromise: 'https://ecma-international.org/ecma-262/9.0/#sec-ispromise', + IsPropertyKey: 'https://ecma-international.org/ecma-262/9.0/#sec-ispropertykey', + IsPropertyReference: 'https://ecma-international.org/ecma-262/9.0/#sec-ispropertyreference', + IsRegExp: 'https://ecma-international.org/ecma-262/9.0/#sec-isregexp', + IsSharedArrayBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-issharedarraybuffer', + IsStrictReference: 'https://ecma-international.org/ecma-262/9.0/#sec-isstrictreference', + IsStringPrefix: 'https://ecma-international.org/ecma-262/9.0/#sec-isstringprefix', + IsSuperReference: 'https://ecma-international.org/ecma-262/9.0/#sec-issuperreference', + IsUnresolvableReference: 'https://ecma-international.org/ecma-262/9.0/#sec-isunresolvablereference', + IsWordChar: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-iswordchar-abstract-operation', + IterableToList: 'https://ecma-international.org/ecma-262/9.0/#sec-iterabletolist', + IteratorClose: 'https://ecma-international.org/ecma-262/9.0/#sec-iteratorclose', + IteratorComplete: 'https://ecma-international.org/ecma-262/9.0/#sec-iteratorcomplete', + IteratorNext: 'https://ecma-international.org/ecma-262/9.0/#sec-iteratornext', + IteratorStep: 'https://ecma-international.org/ecma-262/9.0/#sec-iteratorstep', + IteratorValue: 'https://ecma-international.org/ecma-262/9.0/#sec-iteratorvalue', + LeaveCriticalSection: 'https://ecma-international.org/ecma-262/9.0/#sec-leavecriticalsection', + LocalTime: 'https://ecma-international.org/ecma-262/9.0/#sec-localtime', + LoopContinues: 'https://ecma-international.org/ecma-262/9.0/#sec-loopcontinues', + MakeArgGetter: 'https://ecma-international.org/ecma-262/9.0/#sec-makearggetter', + MakeArgSetter: 'https://ecma-international.org/ecma-262/9.0/#sec-makeargsetter', + MakeClassConstructor: 'https://ecma-international.org/ecma-262/9.0/#sec-makeclassconstructor', + MakeConstructor: 'https://ecma-international.org/ecma-262/9.0/#sec-makeconstructor', + MakeDate: 'https://ecma-international.org/ecma-262/9.0/#sec-makedate', + MakeDay: 'https://ecma-international.org/ecma-262/9.0/#sec-makeday', + MakeMethod: 'https://ecma-international.org/ecma-262/9.0/#sec-makemethod', + MakeSuperPropertyReference: 'https://ecma-international.org/ecma-262/9.0/#sec-makesuperpropertyreference', + MakeTime: 'https://ecma-international.org/ecma-262/9.0/#sec-maketime', + max: 'https://ecma-international.org/ecma-262/9.0/#eqn-max', + 'memory-order': 'https://ecma-international.org/ecma-262/9.0/#sec-memory-order', + min: 'https://ecma-international.org/ecma-262/9.0/#eqn-min', + MinFromTime: 'https://ecma-international.org/ecma-262/9.0/#eqn-MinFromTime', + MinutesPerHour: 'https://ecma-international.org/ecma-262/9.0/#eqn-MinutesPerHour', + ModuleDeclarationEnvironmentSetup: 'https://ecma-international.org/ecma-262/9.0/#sec-moduledeclarationenvironmentsetup', + ModuleExecution: 'https://ecma-international.org/ecma-262/9.0/#sec-moduleexecution', + ModuleNamespaceCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-modulenamespacecreate', + modulo: 'https://ecma-international.org/ecma-262/9.0/#eqn-modulo', + MonthFromTime: 'https://ecma-international.org/ecma-262/9.0/#eqn-MonthFromTime', + msFromTime: 'https://ecma-international.org/ecma-262/9.0/#eqn-msFromTime', + msPerDay: 'https://ecma-international.org/ecma-262/9.0/#eqn-msPerDay', + msPerHour: 'https://ecma-international.org/ecma-262/9.0/#eqn-msPerHour', + msPerMinute: 'https://ecma-international.org/ecma-262/9.0/#eqn-msPerMinute', + msPerSecond: 'https://ecma-international.org/ecma-262/9.0/#eqn-msPerSecond', + NewDeclarativeEnvironment: 'https://ecma-international.org/ecma-262/9.0/#sec-newdeclarativeenvironment', + NewFunctionEnvironment: 'https://ecma-international.org/ecma-262/9.0/#sec-newfunctionenvironment', + NewGlobalEnvironment: 'https://ecma-international.org/ecma-262/9.0/#sec-newglobalenvironment', + NewModuleEnvironment: 'https://ecma-international.org/ecma-262/9.0/#sec-newmoduleenvironment', + NewObjectEnvironment: 'https://ecma-international.org/ecma-262/9.0/#sec-newobjectenvironment', + NewPromiseCapability: 'https://ecma-international.org/ecma-262/9.0/#sec-newpromisecapability', + NormalCompletion: 'https://ecma-international.org/ecma-262/9.0/#sec-normalcompletion', + NumberToRawBytes: 'https://ecma-international.org/ecma-262/9.0/#sec-numbertorawbytes', + NumberToString: 'https://ecma-international.org/ecma-262/9.0/#sec-tostring-applied-to-the-number-type', + ObjectCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-objectcreate', + ObjectDefineProperties: 'https://ecma-international.org/ecma-262/9.0/#sec-objectdefineproperties', + OrdinaryCallBindThis: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarycallbindthis', + OrdinaryCallEvaluateBody: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarycallevaluatebody', + OrdinaryCreateFromConstructor: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarycreatefromconstructor', + OrdinaryDefineOwnProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarydefineownproperty', + OrdinaryDelete: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarydelete', + OrdinaryGet: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinaryget', + OrdinaryGetOwnProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarygetownproperty', + OrdinaryGetPrototypeOf: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarygetprototypeof', + OrdinaryHasInstance: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinaryhasinstance', + OrdinaryHasProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinaryhasproperty', + OrdinaryIsExtensible: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinaryisextensible', + OrdinaryOwnPropertyKeys: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinaryownpropertykeys', + OrdinaryPreventExtensions: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarypreventextensions', + OrdinarySet: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinaryset', + OrdinarySetPrototypeOf: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarysetprototypeof', + OrdinarySetWithOwnDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarysetwithowndescriptor', + OrdinaryToPrimitive: 'https://ecma-international.org/ecma-262/9.0/#sec-ordinarytoprimitive', + ParseModule: 'https://ecma-international.org/ecma-262/9.0/#sec-parsemodule', + ParseScript: 'https://ecma-international.org/ecma-262/9.0/#sec-parse-script', + PerformEval: 'https://ecma-international.org/ecma-262/9.0/#sec-performeval', + PerformPromiseAll: 'https://ecma-international.org/ecma-262/9.0/#sec-performpromiseall', + PerformPromiseRace: 'https://ecma-international.org/ecma-262/9.0/#sec-performpromiserace', + PerformPromiseThen: 'https://ecma-international.org/ecma-262/9.0/#sec-performpromisethen', + PrepareForOrdinaryCall: 'https://ecma-international.org/ecma-262/9.0/#sec-prepareforordinarycall', + PrepareForTailCall: 'https://ecma-international.org/ecma-262/9.0/#sec-preparefortailcall', + PromiseReactionJob: 'https://ecma-international.org/ecma-262/9.0/#sec-promisereactionjob', + PromiseResolve: 'https://ecma-international.org/ecma-262/9.0/#sec-promise-resolve', + PromiseResolveThenableJob: 'https://ecma-international.org/ecma-262/9.0/#sec-promiseresolvethenablejob', + ProxyCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-proxycreate', + PutValue: 'https://ecma-international.org/ecma-262/9.0/#sec-putvalue', + QuoteJSONString: 'https://ecma-international.org/ecma-262/9.0/#sec-quotejsonstring', + RawBytesToNumber: 'https://ecma-international.org/ecma-262/9.0/#sec-rawbytestonumber', + 'reads-bytes-from': 'https://ecma-international.org/ecma-262/9.0/#sec-reads-bytes-from', + 'reads-from': 'https://ecma-international.org/ecma-262/9.0/#sec-reads-from', + RegExpAlloc: 'https://ecma-international.org/ecma-262/9.0/#sec-regexpalloc', + RegExpBuiltinExec: 'https://ecma-international.org/ecma-262/9.0/#sec-regexpbuiltinexec', + RegExpCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-regexpcreate', + RegExpExec: 'https://ecma-international.org/ecma-262/9.0/#sec-regexpexec', + RegExpInitialize: 'https://ecma-international.org/ecma-262/9.0/#sec-regexpinitialize', + RejectPromise: 'https://ecma-international.org/ecma-262/9.0/#sec-rejectpromise', + RemoveWaiter: 'https://ecma-international.org/ecma-262/9.0/#sec-removewaiter', + RemoveWaiters: 'https://ecma-international.org/ecma-262/9.0/#sec-removewaiters', + RepeatMatcher: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-repeatmatcher-abstract-operation', + RequireObjectCoercible: 'https://ecma-international.org/ecma-262/9.0/#sec-requireobjectcoercible', + ResolveBinding: 'https://ecma-international.org/ecma-262/9.0/#sec-resolvebinding', + ResolveThisBinding: 'https://ecma-international.org/ecma-262/9.0/#sec-resolvethisbinding', + ReturnIfAbrupt: 'https://ecma-international.org/ecma-262/9.0/#sec-returnifabrupt', + RunJobs: 'https://ecma-international.org/ecma-262/9.0/#sec-runjobs', + SameValue: 'https://ecma-international.org/ecma-262/9.0/#sec-samevalue', + SameValueNonNumber: 'https://ecma-international.org/ecma-262/9.0/#sec-samevaluenonnumber', + SameValueZero: 'https://ecma-international.org/ecma-262/9.0/#sec-samevaluezero', + ScriptEvaluation: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-scriptevaluation', + ScriptEvaluationJob: 'https://ecma-international.org/ecma-262/9.0/#sec-scriptevaluationjob', + SecFromTime: 'https://ecma-international.org/ecma-262/9.0/#eqn-SecFromTime', + SecondsPerMinute: 'https://ecma-international.org/ecma-262/9.0/#eqn-SecondsPerMinute', + SerializeJSONArray: 'https://ecma-international.org/ecma-262/9.0/#sec-serializejsonarray', + SerializeJSONObject: 'https://ecma-international.org/ecma-262/9.0/#sec-serializejsonobject', + SerializeJSONProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-serializejsonproperty', + Set: 'https://ecma-international.org/ecma-262/9.0/#sec-set-o-p-v-throw', + SetDefaultGlobalBindings: 'https://ecma-international.org/ecma-262/9.0/#sec-setdefaultglobalbindings', + SetFunctionLength: 'https://ecma-international.org/ecma-262/9.0/#sec-setfunctionlength', + SetFunctionName: 'https://ecma-international.org/ecma-262/9.0/#sec-setfunctionname', + SetImmutablePrototype: 'https://ecma-international.org/ecma-262/9.0/#sec-set-immutable-prototype', + SetIntegrityLevel: 'https://ecma-international.org/ecma-262/9.0/#sec-setintegritylevel', + SetRealmGlobalObject: 'https://ecma-international.org/ecma-262/9.0/#sec-setrealmglobalobject', + SetValueInBuffer: 'https://ecma-international.org/ecma-262/9.0/#sec-setvalueinbuffer', + SetViewValue: 'https://ecma-international.org/ecma-262/9.0/#sec-setviewvalue', + SharedDataBlockEventSet: 'https://ecma-international.org/ecma-262/9.0/#sec-sharedatablockeventset', + SortCompare: 'https://ecma-international.org/ecma-262/9.0/#sec-sortcompare', + SpeciesConstructor: 'https://ecma-international.org/ecma-262/9.0/#sec-speciesconstructor', + SplitMatch: 'https://ecma-international.org/ecma-262/9.0/#sec-splitmatch', + 'Strict Equality Comparison': 'https://ecma-international.org/ecma-262/9.0/#sec-strict-equality-comparison', + StringCreate: 'https://ecma-international.org/ecma-262/9.0/#sec-stringcreate', + StringGetOwnProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-stringgetownproperty', + Suspend: 'https://ecma-international.org/ecma-262/9.0/#sec-suspend', + SymbolDescriptiveString: 'https://ecma-international.org/ecma-262/9.0/#sec-symboldescriptivestring', + 'synchronizes-with': 'https://ecma-international.org/ecma-262/9.0/#sec-synchronizes-with', + TestIntegrityLevel: 'https://ecma-international.org/ecma-262/9.0/#sec-testintegritylevel', + thisBooleanValue: 'https://ecma-international.org/ecma-262/9.0/#sec-thisbooleanvalue', + thisNumberValue: 'https://ecma-international.org/ecma-262/9.0/#sec-thisnumbervalue', + thisStringValue: 'https://ecma-international.org/ecma-262/9.0/#sec-thisstringvalue', + thisSymbolValue: 'https://ecma-international.org/ecma-262/9.0/#sec-thissymbolvalue', + thisTimeValue: 'https://ecma-international.org/ecma-262/9.0/#sec-thistimevalue', + ThrowCompletion: 'https://ecma-international.org/ecma-262/9.0/#sec-throwcompletion', + TimeClip: 'https://ecma-international.org/ecma-262/9.0/#sec-timeclip', + TimeFromYear: 'https://ecma-international.org/ecma-262/9.0/#eqn-TimeFromYear', + TimeString: 'https://ecma-international.org/ecma-262/9.0/#sec-timestring', + TimeWithinDay: 'https://ecma-international.org/ecma-262/9.0/#eqn-TimeWithinDay', + TimeZoneString: 'https://ecma-international.org/ecma-262/9.0/#sec-timezoneestring', + ToBoolean: 'https://ecma-international.org/ecma-262/9.0/#sec-toboolean', + ToDateString: 'https://ecma-international.org/ecma-262/9.0/#sec-todatestring', + ToIndex: 'https://ecma-international.org/ecma-262/9.0/#sec-toindex', + ToInt16: 'https://ecma-international.org/ecma-262/9.0/#sec-toint16', + ToInt32: 'https://ecma-international.org/ecma-262/9.0/#sec-toint32', + ToInt8: 'https://ecma-international.org/ecma-262/9.0/#sec-toint8', + ToInteger: 'https://ecma-international.org/ecma-262/9.0/#sec-tointeger', + ToLength: 'https://ecma-international.org/ecma-262/9.0/#sec-tolength', + ToNumber: 'https://ecma-international.org/ecma-262/9.0/#sec-tonumber', + ToObject: 'https://ecma-international.org/ecma-262/9.0/#sec-toobject', + TopLevelModuleEvaluationJob: 'https://ecma-international.org/ecma-262/9.0/#sec-toplevelmoduleevaluationjob', + ToPrimitive: 'https://ecma-international.org/ecma-262/9.0/#sec-toprimitive', + ToPropertyDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-topropertydescriptor', + ToPropertyKey: 'https://ecma-international.org/ecma-262/9.0/#sec-topropertykey', + ToString: 'https://ecma-international.org/ecma-262/9.0/#sec-tostring', + ToUint16: 'https://ecma-international.org/ecma-262/9.0/#sec-touint16', + ToUint32: 'https://ecma-international.org/ecma-262/9.0/#sec-touint32', + ToUint8: 'https://ecma-international.org/ecma-262/9.0/#sec-touint8', + ToUint8Clamp: 'https://ecma-international.org/ecma-262/9.0/#sec-touint8clamp', + TriggerPromiseReactions: 'https://ecma-international.org/ecma-262/9.0/#sec-triggerpromisereactions', + Type: 'https://ecma-international.org/ecma-262/9.0/#sec-ecmascript-data-types-and-values', + TypedArrayCreate: 'https://ecma-international.org/ecma-262/9.0/#typedarray-create', + TypedArraySpeciesCreate: 'https://ecma-international.org/ecma-262/9.0/#typedarray-species-create', + UnicodeEscape: 'https://ecma-international.org/ecma-262/9.0/#sec-unicodeescape', + UnicodeMatchProperty: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-unicodematchproperty-p', + UnicodeMatchPropertyValue: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-unicodematchpropertyvalue-p-v', + UpdateEmpty: 'https://ecma-international.org/ecma-262/9.0/#sec-updateempty', + UTC: 'https://ecma-international.org/ecma-262/9.0/#sec-utc-t', + UTF16Decode: 'https://ecma-international.org/ecma-262/9.0/#sec-utf16decode', + UTF16Encoding: 'https://ecma-international.org/ecma-262/9.0/#sec-utf16encoding', + ValidateAndApplyPropertyDescriptor: 'https://ecma-international.org/ecma-262/9.0/#sec-validateandapplypropertydescriptor', + ValidateAtomicAccess: 'https://ecma-international.org/ecma-262/9.0/#sec-validateatomicaccess', + ValidateSharedIntegerTypedArray: 'https://ecma-international.org/ecma-262/9.0/#sec-validatesharedintegertypedarray', + ValidateTypedArray: 'https://ecma-international.org/ecma-262/9.0/#sec-validatetypedarray', + ValueOfReadEvent: 'https://ecma-international.org/ecma-262/9.0/#sec-valueofreadevent', + WakeWaiter: 'https://ecma-international.org/ecma-262/9.0/#sec-wakewaiter', + WeekDay: 'https://ecma-international.org/ecma-262/9.0/#sec-week-day', + WordCharacters: 'https://ecma-international.org/ecma-262/9.0/#sec-runtime-semantics-wordcharacters-abstract-operation', + YearFromTime: 'https://ecma-international.org/ecma-262/9.0/#eqn-YearFromTime' +}; diff --git a/WebServer/node_modules/es-abstract/operations/es5.js b/WebServer/node_modules/es-abstract/operations/es5.js new file mode 100644 index 000000000..6048df42f --- /dev/null +++ b/WebServer/node_modules/es-abstract/operations/es5.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + IsAccessorDescriptor: 'https://ecma-international.org/ecma-262/5.1/#sec-8.10.1', + IsDataDescriptor: 'https://ecma-international.org/ecma-262/5.1/#sec-8.10.2', + IsGenericDescriptor: 'https://ecma-international.org/ecma-262/5.1/#sec-8.10.3', + FromPropertyDescriptor: 'https://ecma-international.org/ecma-262/5.1/#sec-8.10.4', + ToPropertyDescriptor: 'https://ecma-international.org/ecma-262/5.1/#sec-8.10.5' +}; diff --git a/WebServer/node_modules/es-abstract/operations/getOps.js b/WebServer/node_modules/es-abstract/operations/getOps.js new file mode 100644 index 000000000..315e3055f --- /dev/null +++ b/WebServer/node_modules/es-abstract/operations/getOps.js @@ -0,0 +1,61 @@ +#!/usr/bin/env node + +var fs = require('fs'); +var path = require('path'); +var execSync = require('child_process').execSync; + +var $ = require('cheerio'); +var fromEntries = require('object.fromentries'); + +if (process.argv.length !== 3) { + throw new RangeError('please provide a year'); +} +var year = parseInt(process.argv[2]); +if (year < 2016) { + throw new RangeError('ES2016+ only'); +} +var edition = year - 2009; + +var specHTMLurl = new URL('https://raw.githubusercontent.com/tc39/ecma262/es' + year + '/spec.html'); + +var specHTML = String(execSync('curl --silent ' + specHTMLurl)); + +var root = $(specHTML); + +var aOps = root.filter('[aoid]').add(root.find('[aoid]')); + +var missings = []; + +var entries = aOps.toArray().map(function (x) { + var op = $(x); + var aoid = op.attr('aoid'); + var id = op.attr('id'); + + if (!id) { + id = op.closest('[id]').attr('id'); + } + + if (!id) { + missings.push(aoid); + } + + return [ + aoid, + 'https://ecma-international.org/ecma-262/' + edition + '.0/#' + id + ]; +}); + +if (missings.length > 0) { + console.error('Missing URLs:', missings); + process.exit(1); +} + +entries.sort(function (a, b) { return a[0].localeCompare(b[0]); }); + +var obj = fromEntries(entries); + +var outputPath = path.join('operations', year + '.js'); +fs.writeFileSync(outputPath, '\'use strict\';\n\nmodule.exports = ' + JSON.stringify(obj, null, '\t')+ ';\n'); + +console.log('npx eslint --quiet --fix ' + outputPath); +execSync('npx eslint --quiet --fix ' + outputPath); diff --git a/WebServer/node_modules/es-abstract/package.json b/WebServer/node_modules/es-abstract/package.json new file mode 100644 index 000000000..c900764e7 --- /dev/null +++ b/WebServer/node_modules/es-abstract/package.json @@ -0,0 +1,131 @@ +{ + "_args": [ + [ + "es-abstract@1.13.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "es-abstract@1.13.0", + "_id": "es-abstract@1.13.0", + "_inBundle": false, + "_integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "_location": "/es-abstract", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "es-abstract@1.13.0", + "name": "es-abstract", + "escapedName": "es-abstract", + "rawSpec": "1.13.0", + "saveSpec": null, + "fetchSpec": "1.13.0" + }, + "_requiredBy": [ + "/object.getownpropertydescriptors" + ], + "_resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "_spec": "1.13.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes" + }, + "bugs": { + "url": "https://github.com/ljharb/es-abstract/issues" + }, + "contributors": [ + { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes" + } + ], + "dependencies": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + }, + "description": "ECMAScript spec abstract operations.", + "devDependencies": { + "@ljharb/eslint-config": "^13.1.1", + "cheerio": "^1.0.0-rc.2", + "editorconfig-tools": "^0.1.1", + "eslint": "^5.11.1", + "foreach": "^2.0.5", + "nyc": "^10.3.2", + "object-inspect": "^1.6.0", + "object-is": "^1.0.1", + "object.assign": "^4.1.0", + "object.fromentries": "^2.0.0", + "replace": "^1.0.1", + "safe-publish-latest": "^1.1.2", + "semver": "^5.6.0", + "tape": "^4.9.2" + }, + "engines": { + "node": ">= 0.4" + }, + "greenkeeper": { + "//": "nyc is ignored because it requires node 4+, and we support older than that", + "ignore": [ + "nyc" + ] + }, + "homepage": "https://github.com/ljharb/es-abstract#readme", + "keywords": [ + "ECMAScript", + "ES", + "abstract", + "operation", + "abstract operation", + "JavaScript", + "ES5", + "ES6", + "ES7" + ], + "license": "MIT", + "main": "index.js", + "name": "es-abstract", + "repository": { + "type": "git", + "url": "git://github.com/ljharb/es-abstract.git" + }, + "scripts": { + "audit": "npm audit", + "coverage": "nyc npm run --silent tests-only >/dev/null", + "eccheck": "editorconfig-tools check *.js **/*.js > /dev/null", + "lint": "eslint test/*.js *.js", + "postaudit": "rm package-lock.json", + "postcoverage": "nyc report", + "posttest": "npm run audit", + "preaudit": "npm install --package-lock --package-lock-only", + "prepublish": "safe-publish-latest", + "pretest": "npm run --silent lint", + "test": "npm run tests-only", + "tests-only": "node test" + }, + "testling": { + "files": "test/index.js", + "browsers": [ + "iexplore/6.0..latest", + "firefox/3.0..6.0", + "firefox/15.0..latest", + "firefox/nightly", + "chrome/4.0..10.0", + "chrome/20.0..latest", + "chrome/canary", + "opera/10.0..latest", + "opera/next", + "safari/4.0..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2" + ] + }, + "version": "1.13.0" +} diff --git a/WebServer/node_modules/es-abstract/test/GetIntrinsic.js b/WebServer/node_modules/es-abstract/test/GetIntrinsic.js new file mode 100644 index 000000000..ed8e7ecc6 --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/GetIntrinsic.js @@ -0,0 +1,34 @@ +'use strict'; + +var GetIntrinsic = require('../GetIntrinsic'); + +var test = require('tape'); +var forEach = require('foreach'); +var debug = require('object-inspect'); + +var v = require('./helpers/values'); + +test('export', function (t) { + t.equal(typeof GetIntrinsic, 'function', 'it is a function'); + t.equal(GetIntrinsic.length, 2, 'function has length of 2'); + + t.end(); +}); + +test('throws', function (t) { + t['throws']( + function () { GetIntrinsic('not an intrinsic'); }, + SyntaxError, + 'nonexistent intrinsic throws a syntax error' + ); + + forEach(v.nonBooleans, function (nonBoolean) { + t['throws']( + function () { GetIntrinsic('%', nonBoolean); }, + TypeError, + debug(nonBoolean) + ' is not a Boolean' + ); + }); + + t.end(); +}); diff --git a/WebServer/node_modules/es-abstract/test/diffOps.js b/WebServer/node_modules/es-abstract/test/diffOps.js new file mode 100644 index 000000000..0fb2fc40c --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/diffOps.js @@ -0,0 +1,26 @@ +'use strict'; + +var keys = require('object-keys'); +var forEach = require('foreach'); + +module.exports = function diffOperations(actual, expected, expectedMissing) { + var actualKeys = keys(actual); + var expectedKeys = keys(expected); + + var extra = []; + var missing = []; + forEach(actualKeys, function (op) { + if (!(op in expected)) { + extra.push(op); + } else if (expectedMissing.indexOf(op) !== -1) { + extra.push(op); + } + }); + forEach(expectedKeys, function (op) { + if (typeof actual[op] !== 'function' && expectedMissing.indexOf(op) === -1) { + missing.push(op); + } + }); + + return { missing: missing, extra: extra }; +}; diff --git a/WebServer/node_modules/es-abstract/test/es2015.js b/WebServer/node_modules/es-abstract/test/es2015.js new file mode 100644 index 000000000..3aa299c0e --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/es2015.js @@ -0,0 +1,9 @@ +'use strict'; + +var ES = require('../').ES2015; + +var ops = require('../operations/2015'); + +var expectedMissing = ['Construct', 'SetIntegrityLevel', 'TestIntegrityLevel', 'CreateArrayFromList', 'CreateListFromArrayLike', 'OrdinaryHasInstance', 'CreateListIterator', 'RegExpBuiltinExec', 'IsPromise', 'NormalCompletion']; + +require('./tests').es2015(ES, ops, expectedMissing); diff --git a/WebServer/node_modules/es-abstract/test/es2016.js b/WebServer/node_modules/es-abstract/test/es2016.js new file mode 100644 index 000000000..d2b3f9d22 --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/es2016.js @@ -0,0 +1,9 @@ +'use strict'; + +var ES = require('../').ES2016; + +var ops = require('../operations/2016'); + +var expectedMissing = ['Abstract Equality Comparison', 'Abstract Relational Comparison', 'AddRestrictedFunctionProperties', 'AllocateArrayBuffer', 'AllocateTypedArray', 'AllocateTypedArrayBuffer', 'ArrayCreate', 'ArraySetLength', 'BlockDeclarationInstantiation', 'BoundFunctionCreate', 'Canonicalize', 'CharacterRange', 'CharacterRangeOrUnion', 'CharacterSetMatcher', 'CloneArrayBuffer', 'Completion', 'Construct', 'CopyDataBlockBytes', 'CreateArrayFromList', 'CreateArrayIterator', 'CreateBuiltinFunction', 'CreateByteDataBlock', 'CreateDynamicFunction', 'CreateHTML', 'CreateIntrinsics', 'CreateListFromArrayLike', 'CreateListIterator', 'CreateMapIterator', 'CreateMappedArgumentsObject', 'CreatePerIterationEnvironment', 'CreateRealm', 'CreateResolvingFunctions', 'CreateSetIterator', 'CreateStringIterator', 'CreateUnmappedArgumentsObject', 'DateFromTime', 'Day', 'DayFromYear', 'DayWithinYear', 'DaysInYear', 'Decode', 'DetachArrayBuffer', 'Encode', 'EnqueueJob', 'EnumerateObjectProperties', 'EscapeRegExpPattern', 'EvalDeclarationInstantiation', 'EvaluateCall', 'EvaluateDirectCall', 'EvaluateNew', 'ForBodyEvaluation', 'ForIn/OfBodyEvaluation', 'ForIn/OfHeadEvaluation', 'FulfillPromise', 'FunctionAllocate', 'FunctionCreate', 'FunctionDeclarationInstantiation', 'FunctionInitialize', 'GeneratorFunctionCreate', 'GeneratorResume', 'GeneratorResumeAbrupt', 'GeneratorStart', 'GeneratorValidate', 'GeneratorYield', 'GetActiveScriptOrModule', 'GetFunctionRealm', 'GetGlobalObject', 'GetIdentifierReference', 'GetModuleNamespace', 'GetNewTarget', 'GetOwnPropertyKeys', 'GetPrototypeFromConstructor', 'GetSubstitution', 'GetSuperConstructor', 'GetTemplateObject', 'GetThisEnvironment', 'GetThisValue', 'GetValue', 'GetValueFromBuffer', 'GetViewValue', 'GlobalDeclarationInstantiation', 'HostPromiseRejectionTracker', 'HostReportErrors', 'HostResolveImportedModule', 'HourFromTime', 'HoursPerDay', 'IfAbruptRejectPromise', 'ImportedLocalNames', 'InLeapYear', 'InitializeBoundName', 'InitializeHostDefinedRealm', 'InitializeReferencedBinding', 'InstanceofOperator', 'IntegerIndexedElementGet', 'IntegerIndexedElementSet', 'IntegerIndexedObjectCreate', 'InternalizeJSONProperty', 'IsAnonymousFunctionDefinition', 'IsCompatiblePropertyDescriptor', 'IsDetachedBuffer', 'IsInTailPosition', 'IsLabelledFunction', 'IsPromise', 'IsWordChar', 'IterableToArrayLike', 'LocalTime', 'LoopContinues', 'MakeArgGetter', 'MakeArgSetter', 'MakeClassConstructor', 'MakeConstructor', 'MakeDate', 'MakeDay', 'MakeMethod', 'MakeSuperPropertyReference', 'MakeTime', 'MinFromTime', 'MinutesPerHour', 'ModuleNamespaceCreate', 'MonthFromTime', 'NewDeclarativeEnvironment', 'NewFunctionEnvironment', 'NewGlobalEnvironment', 'NewModuleEnvironment', 'NewObjectEnvironment', 'NewPromiseCapability', 'NextJob', 'NormalCompletion', 'ObjectDefineProperties', 'OrdinaryCallBindThis', 'OrdinaryCallEvaluateBody', 'OrdinaryCreateFromConstructor', 'OrdinaryDefineOwnProperty', 'OrdinaryDelete', 'OrdinaryGet', 'OrdinaryGetOwnProperty', 'OrdinaryGetPrototypeOf', 'OrdinaryHasInstance', 'OrdinaryHasProperty', 'OrdinaryIsExtensible', 'OrdinaryOwnPropertyKeys', 'OrdinaryPreventExtensions', 'OrdinarySet', 'OrdinarySetPrototypeOf', 'ParseModule', 'ParseScript', 'PerformEval', 'PerformPromiseAll', 'PerformPromiseRace', 'PerformPromiseThen', 'PrepareForOrdinaryCall', 'PrepareForTailCall', 'PromiseReactionJob', 'PromiseResolveThenableJob', 'ProxyCreate', 'PutValue', 'QuoteJSONString', 'RegExpAlloc', 'RegExpBuiltinExec', 'RegExpCreate', 'RegExpInitialize', 'RejectPromise', 'RepeatMatcher', 'ResolveBinding', 'ResolveThisBinding', 'ReturnIfAbrupt', 'ScriptEvaluation', 'ScriptEvaluationJob', 'SecFromTime', 'SecondsPerMinute', 'SerializeJSONArray', 'SerializeJSONObject', 'SerializeJSONProperty', 'SetDefaultGlobalBindings', 'SetFunctionName', 'SetIntegrityLevel', 'SetRealmGlobalObject', 'SetValueInBuffer', 'SetViewValue', 'SortCompare', 'SplitMatch', 'Strict Equality Comparison', 'StringCreate', 'SymbolDescriptiveString', 'TestIntegrityLevel', 'TimeClip', 'TimeFromYear', 'TimeWithinDay', 'ToDateString', 'ToString Applied to the Number Type', 'TopLevelModuleEvaluationJob', 'TriggerPromiseReactions', 'TypedArrayCreate', 'TypedArraySpeciesCreate', 'UTC', 'UTF16Decode', 'UTF16Encoding', 'UpdateEmpty', 'ValidateAndApplyPropertyDescriptor', 'ValidateTypedArray', 'WeekDay', 'YearFromTime', 'abs', 'floor', 'max', 'min', 'modulo', 'msFromTime', 'msPerDay', 'msPerHour', 'msPerMinute', 'msPerSecond']; + +require('./tests').es2016(ES, ops, expectedMissing); diff --git a/WebServer/node_modules/es-abstract/test/es2017.js b/WebServer/node_modules/es-abstract/test/es2017.js new file mode 100644 index 000000000..b5dfae72c --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/es2017.js @@ -0,0 +1,9 @@ +'use strict'; + +var ES = require('../').ES2017; + +var ops = require('../operations/2017'); + +var expectedMissing = ['Abstract Equality Comparison', 'Abstract Relational Comparison', 'AddRestrictedFunctionProperties', 'AddWaiter', 'AgentCanSuspend', 'AgentSignifier', 'AllocateArrayBuffer', 'AllocateSharedArrayBuffer', 'AllocateTypedArray', 'AllocateTypedArrayBuffer', 'ArrayCreate', 'ArraySetLength', 'AsyncFunctionAwait', 'AsyncFunctionCreate', 'AsyncFunctionStart', 'AtomicLoad', 'AtomicReadModifyWrite', 'BlockDeclarationInstantiation', 'BoundFunctionCreate', 'Canonicalize', 'CharacterRange', 'CharacterRangeOrUnion', 'CharacterSetMatcher', 'CloneArrayBuffer', 'Completion', 'ComposeWriteEventBytes', 'Construct', 'CopyDataBlockBytes', 'CreateArrayFromList', 'CreateArrayIterator', 'CreateBuiltinFunction', 'CreateByteDataBlock', 'CreateDynamicFunction', 'CreateHTML', 'CreateIntrinsics', 'CreateListFromArrayLike', 'CreateListIterator', 'CreateMapIterator', 'CreateMappedArgumentsObject', 'CreatePerIterationEnvironment', 'CreateRealm', 'CreateResolvingFunctions', 'CreateSetIterator', 'CreateSharedByteDataBlock', 'CreateStringIterator', 'CreateUnmappedArgumentsObject', 'DateFromTime', 'Day', 'DayFromYear', 'DayWithinYear', 'DaysInYear', 'Decode', 'DetachArrayBuffer', 'Encode', 'EnqueueJob', 'EnterCriticalSection', 'EnumerateObjectProperties', 'EscapeRegExpPattern', 'EvalDeclarationInstantiation', 'EvaluateCall', 'EvaluateDirectCall', 'EvaluateNew', 'EventSet', 'ForBodyEvaluation', 'ForIn/OfBodyEvaluation', 'ForIn/OfHeadEvaluation', 'FulfillPromise', 'FunctionAllocate', 'FunctionCreate', 'FunctionDeclarationInstantiation', 'FunctionInitialize', 'GeneratorFunctionCreate', 'GeneratorResume', 'GeneratorResumeAbrupt', 'GeneratorStart', 'GeneratorValidate', 'GeneratorYield', 'GetActiveScriptOrModule', 'GetBase', 'GetFunctionRealm', 'GetGlobalObject', 'GetIdentifierReference', 'GetModifySetValueInBuffer', 'GetModuleNamespace', 'GetNewTarget', 'GetOwnPropertyKeys', 'GetPrototypeFromConstructor', 'GetReferencedName', 'GetSubstitution', 'GetSuperConstructor', 'GetTemplateObject', 'GetThisEnvironment', 'GetThisValue', 'GetValue', 'GetValueFromBuffer', 'GetViewValue', 'GetWaiterList', 'GlobalDeclarationInstantiation', 'HasPrimitiveBase', 'HostEnsureCanCompileStrings', 'HostEventSet', 'HostPromiseRejectionTracker', 'HostReportErrors', 'HostResolveImportedModule', 'HourFromTime', 'HoursPerDay', 'IfAbruptRejectPromise', 'ImportedLocalNames', 'InLeapYear', 'InitializeBoundName', 'InitializeHostDefinedRealm', 'InitializeReferencedBinding', 'InstanceofOperator', 'IntegerIndexedElementGet', 'IntegerIndexedElementSet', 'IntegerIndexedObjectCreate', 'InternalizeJSONProperty', 'IsAnonymousFunctionDefinition', 'IsCompatiblePropertyDescriptor', 'IsDetachedBuffer', 'IsInTailPosition', 'IsLabelledFunction', 'IsPromise', 'IsPropertyReference', 'IsSharedArrayBuffer', 'IsStrictReference', 'IsSuperReference', 'IsUnresolvableReference', 'IsWordChar', 'IterableToList', 'LeaveCriticalSection', 'LocalTime', 'LoopContinues', 'MakeArgGetter', 'MakeArgSetter', 'MakeClassConstructor', 'MakeConstructor', 'MakeDate', 'MakeDay', 'MakeMethod', 'MakeSuperPropertyReference', 'MakeTime', 'MinFromTime', 'MinutesPerHour', 'ModuleNamespaceCreate', 'MonthFromTime', 'NewDeclarativeEnvironment', 'NewFunctionEnvironment', 'NewGlobalEnvironment', 'NewModuleEnvironment', 'NewObjectEnvironment', 'NewPromiseCapability', 'NormalCompletion', 'NumberToRawBytes', 'ObjectDefineProperties', 'OrdinaryCallBindThis', 'OrdinaryCallEvaluateBody', 'OrdinaryCreateFromConstructor', 'OrdinaryDefineOwnProperty', 'OrdinaryDelete', 'OrdinaryGet', 'OrdinaryGetOwnProperty', 'OrdinaryGetPrototypeOf', 'OrdinaryHasInstance', 'OrdinaryHasProperty', 'OrdinaryIsExtensible', 'OrdinaryOwnPropertyKeys', 'OrdinaryPreventExtensions', 'OrdinarySet', 'OrdinarySetPrototypeOf', 'OrdinaryToPrimitive', 'ParseModule', 'ParseScript', 'PerformEval', 'PerformPromiseAll', 'PerformPromiseRace', 'PerformPromiseThen', 'PrepareForOrdinaryCall', 'PrepareForTailCall', 'PromiseReactionJob', 'PromiseResolveThenableJob', 'ProxyCreate', 'PutValue', 'QuoteJSONString', 'RawBytesToNumber', 'RegExpAlloc', 'RegExpBuiltinExec', 'RegExpCreate', 'RegExpInitialize', 'RejectPromise', 'RemoveWaiter', 'RemoveWaiters', 'RepeatMatcher', 'ResolveBinding', 'ResolveThisBinding', 'ReturnIfAbrupt', 'RunJobs', 'ScriptEvaluation', 'ScriptEvaluationJob', 'SecFromTime', 'SecondsPerMinute', 'SerializeJSONArray', 'SerializeJSONObject', 'SerializeJSONProperty', 'SetDefaultGlobalBindings', 'SetFunctionName', 'SetImmutablePrototype', 'SetIntegrityLevel', 'SetRealmGlobalObject', 'SetValueInBuffer', 'SetViewValue', 'SharedDataBlockEventSet', 'SortCompare', 'SplitMatch', 'Strict Equality Comparison', 'StringCreate', 'StringGetOwnProperty', 'Suspend', 'SymbolDescriptiveString', 'TestIntegrityLevel', 'TimeClip', 'TimeFromYear', 'TimeWithinDay', 'ToDateString', 'ToString Applied to the Number Type', 'TopLevelModuleEvaluationJob', 'TriggerPromiseReactions', 'TypedArrayCreate', 'TypedArraySpeciesCreate', 'UTC', 'UTF16Decode', 'UTF16Encoding', 'UpdateEmpty', 'ValidateAndApplyPropertyDescriptor', 'ValidateAtomicAccess', 'ValidateSharedIntegerTypedArray', 'ValidateTypedArray', 'ValueOfReadEvent', 'WakeWaiter', 'WeekDay', 'WordCharacters', 'YearFromTime', 'abs', 'agent-order', 'floor', 'happens-before', 'host-synchronizes-with', 'max', 'memory-order', 'min', 'modulo', 'msFromTime', 'msPerDay', 'msPerHour', 'msPerMinute', 'msPerSecond', 'reads-bytes-from', 'reads-from', 'synchronizes-with']; + +require('./tests').es2017(ES, ops, expectedMissing); diff --git a/WebServer/node_modules/es-abstract/test/es2018.js b/WebServer/node_modules/es-abstract/test/es2018.js new file mode 100644 index 000000000..5147be8bd --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/es2018.js @@ -0,0 +1,9 @@ +'use strict'; + +var ES = require('../').ES2018; + +var ops = require('../operations/2018'); + +var expectedMissing = ['abs', 'Abstract Equality Comparison', 'Abstract Relational Comparison', 'AddRestrictedFunctionProperties', 'AddWaiter', 'agent-order', 'AgentCanSuspend', 'AgentSignifier', 'AllocateArrayBuffer', 'AllocateSharedArrayBuffer', 'AllocateTypedArray', 'AllocateTypedArrayBuffer', 'ArrayCreate', 'ArraySetLength', 'AsyncFunctionStart', 'AsyncGeneratorEnqueue', 'AsyncGeneratorReject', 'AsyncGeneratorResolve', 'AsyncGeneratorResumeNext', 'AsyncGeneratorStart', 'AsyncGeneratorYield', 'AtomicLoad', 'AtomicReadModifyWrite', 'Await', 'BlockDeclarationInstantiation', 'BoundFunctionCreate', 'Canonicalize', 'CaseClauseIsSelected', 'CharacterRange', 'CharacterRangeOrUnion', 'CharacterSetMatcher', 'CloneArrayBuffer', 'Completion', 'ComposeWriteEventBytes', 'CopyDataBlockBytes', 'CreateArrayIterator', 'CreateAsyncFromSyncIterator', 'CreateBuiltinFunction', 'CreateByteDataBlock', 'CreateDynamicFunction', 'CreateHTML', 'CreateIntrinsics', 'CreateMapIterator', 'CreateMappedArgumentsObject', 'CreatePerIterationEnvironment', 'CreateRealm', 'CreateResolvingFunctions', 'CreateSetIterator', 'CreateSharedByteDataBlock', 'CreateStringIterator', 'CreateUnmappedArgumentsObject', 'DateFromTime', 'Day', 'DayFromYear', 'DaysInYear', 'DayWithinYear', 'Decode', 'DetachArrayBuffer', 'Encode', 'EnqueueJob', 'EnterCriticalSection', 'EnumerateObjectProperties', 'EscapeRegExpPattern', 'EvalDeclarationInstantiation', 'EvaluateCall', 'EvaluateNew', 'EventSet', 'floor', 'ForBodyEvaluation', 'ForIn/OfBodyEvaluation', 'ForIn/OfHeadEvaluation', 'FulfillPromise', 'FunctionAllocate', 'FunctionCreate', 'FunctionDeclarationInstantiation', 'FunctionInitialize', 'GeneratorFunctionCreate', 'GeneratorResume', 'GeneratorResumeAbrupt', 'GeneratorStart', 'GeneratorValidate', 'GeneratorYield', 'GetActiveScriptOrModule', 'GetBase', 'GetFunctionRealm', 'GetGeneratorKind', 'GetGlobalObject', 'GetIdentifierReference', 'GetModifySetValueInBuffer', 'GetModuleNamespace', 'GetNewTarget', 'GetOwnPropertyKeys', 'GetPrototypeFromConstructor', 'GetReferencedName', 'GetSubstitution', 'GetSuperConstructor', 'GetTemplateObject', 'GetThisEnvironment', 'GetThisValue', 'GetValue', 'GetValueFromBuffer', 'GetViewValue', 'GetWaiterList', 'GlobalDeclarationInstantiation', 'happens-before', 'HasPrimitiveBase', 'host-synchronizes-with', 'HostEnsureCanCompileStrings', 'HostEventSet', 'HostPromiseRejectionTracker', 'HostReportErrors', 'HostResolveImportedModule', 'HourFromTime', 'HoursPerDay', 'IfAbruptRejectPromise', 'ImportedLocalNames', 'InitializeBoundName', 'InitializeHostDefinedRealm', 'InitializeReferencedBinding', 'InLeapYear', 'InnerModuleEvaluation', 'InnerModuleInstantiation', 'InstanceofOperator', 'IntegerIndexedElementGet', 'IntegerIndexedElementSet', 'IntegerIndexedObjectCreate', 'InternalizeJSONProperty', 'IsAnonymousFunctionDefinition', 'IsCompatiblePropertyDescriptor', 'IsDetachedBuffer', 'IsInTailPosition', 'IsLabelledFunction', 'IsPropertyReference', 'IsSharedArrayBuffer', 'IsStrictReference', 'IsSuperReference', 'IsUnresolvableReference', 'IsWordChar', 'IterableToList', 'LeaveCriticalSection', 'LocalTime', 'LoopContinues', 'MakeArgGetter', 'MakeArgSetter', 'MakeClassConstructor', 'MakeConstructor', 'MakeDate', 'MakeDay', 'MakeMethod', 'MakeSuperPropertyReference', 'MakeTime', 'max', 'memory-order', 'min', 'MinFromTime', 'MinutesPerHour', 'ModuleDeclarationEnvironmentSetup', 'ModuleExecution', 'ModuleNamespaceCreate', 'modulo', 'MonthFromTime', 'msFromTime', 'msPerDay', 'msPerHour', 'msPerMinute', 'msPerSecond', 'NewDeclarativeEnvironment', 'NewFunctionEnvironment', 'NewGlobalEnvironment', 'NewModuleEnvironment', 'NewObjectEnvironment', 'NewPromiseCapability', 'NumberToRawBytes', 'ObjectDefineProperties', 'OrdinaryCallBindThis', 'OrdinaryCallEvaluateBody', 'OrdinaryCreateFromConstructor', 'OrdinaryDefineOwnProperty', 'OrdinaryDelete', 'OrdinaryGet', 'OrdinaryGetOwnProperty', 'OrdinaryGetPrototypeOf', 'OrdinaryHasProperty', 'OrdinaryIsExtensible', 'OrdinaryOwnPropertyKeys', 'OrdinaryPreventExtensions', 'OrdinarySetPrototypeOf', 'OrdinaryToPrimitive', 'ParseModule', 'ParseScript', 'PerformEval', 'PerformPromiseAll', 'PerformPromiseRace', 'PerformPromiseThen', 'PrepareForOrdinaryCall', 'PrepareForTailCall', 'PromiseReactionJob', 'PromiseResolveThenableJob', 'ProxyCreate', 'PutValue', 'QuoteJSONString', 'RawBytesToNumber', 'reads-bytes-from', 'reads-from', 'RegExpAlloc', 'RegExpCreate', 'RegExpInitialize', 'RejectPromise', 'RemoveWaiter', 'RemoveWaiters', 'RepeatMatcher', 'ResolveBinding', 'ResolveThisBinding', 'ReturnIfAbrupt', 'RunJobs', 'ScriptEvaluation', 'ScriptEvaluationJob', 'SecFromTime', 'SecondsPerMinute', 'SerializeJSONArray', 'SerializeJSONObject', 'SerializeJSONProperty', 'SetDefaultGlobalBindings', 'SetFunctionName', 'SetImmutablePrototype', 'SetRealmGlobalObject', 'SetValueInBuffer', 'SetViewValue', 'SharedDataBlockEventSet', 'SortCompare', 'SplitMatch', 'Strict Equality Comparison', 'StringCreate', 'StringGetOwnProperty', 'Suspend', 'SymbolDescriptiveString', 'synchronizes-with', 'TimeClip', 'TimeFromYear', 'TimeWithinDay', 'TimeZoneString', 'TopLevelModuleEvaluationJob', 'TriggerPromiseReactions', 'TypedArrayCreate', 'TypedArraySpeciesCreate', 'UnicodeEscape', 'UpdateEmpty', 'UTC', 'UTF16Decode', 'UTF16Encoding', 'ValidateAndApplyPropertyDescriptor', 'ValidateAtomicAccess', 'ValidateSharedIntegerTypedArray', 'ValidateTypedArray', 'ValueOfReadEvent', 'WakeWaiter', 'WeekDay', 'WordCharacters', 'YearFromTime', 'AsyncFunctionCreate', 'AsyncGeneratorFunctionCreate', 'AsyncIteratorClose', 'BackreferenceMatcher', 'Construct', 'CreateArrayFromList', 'CreateListFromArrayLike', 'CreateListIteratorRecord', 'DateString', 'IsPromise', 'NormalCompletion', 'OrdinaryHasInstance', 'OrdinarySet', 'OrdinarySetWithOwnDescriptor', 'RegExpBuiltinExec', 'SetFunctionLength', 'SetIntegrityLevel', 'TestIntegrityLevel', 'ThrowCompletion', 'TimeString', 'ToDateString', 'UnicodeMatchProperty', 'UnicodeMatchPropertyValue']; + +require('./tests').es2018(ES, ops, expectedMissing); diff --git a/WebServer/node_modules/es-abstract/test/es5.js b/WebServer/node_modules/es-abstract/test/es5.js new file mode 100644 index 000000000..cca303040 --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/es5.js @@ -0,0 +1,415 @@ +'use strict'; + +var ES = require('../').ES5; +var test = require('tape'); + +var forEach = require('foreach'); +var is = require('object-is'); + +var coercibleObject = { valueOf: function () { return '3'; }, toString: function () { return 42; } }; +var coercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return 42; } +}; +var valueOfOnlyObject = { valueOf: function () { return 4; }, toString: function () { return {}; } }; +var toStringOnlyObject = { valueOf: function () { return {}; }, toString: function () { return 7; } }; +var uncoercibleObject = { valueOf: function () { return {}; }, toString: function () { return {}; } }; +var uncoercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return function toStrFn() {}; } +}; +var objects = [{}, coercibleObject, toStringOnlyObject, valueOfOnlyObject]; +var numbers = [0, -0, Infinity, -Infinity, 42]; +var nonNullPrimitives = [true, false, 'foo', ''].concat(numbers); +var primitives = [undefined, null].concat(nonNullPrimitives); + +test('ToPrimitive', function (t) { + t.test('primitives', function (st) { + var testPrimitive = function (primitive) { + st.ok(is(ES.ToPrimitive(primitive), primitive), primitive + ' is returned correctly'); + }; + forEach(primitives, testPrimitive); + st.end(); + }); + + t.test('objects', function (st) { + st.equal(ES.ToPrimitive(coercibleObject), coercibleObject.valueOf(), 'coercibleObject coerces to valueOf'); + st.equal(ES.ToPrimitive(coercibleObject, Number), coercibleObject.valueOf(), 'coercibleObject with hint Number coerces to valueOf'); + st.equal(ES.ToPrimitive(coercibleObject, String), coercibleObject.toString(), 'coercibleObject with hint String coerces to toString'); + st.equal(ES.ToPrimitive(coercibleFnObject), coercibleFnObject.toString(), 'coercibleFnObject coerces to toString'); + st.equal(ES.ToPrimitive(toStringOnlyObject), toStringOnlyObject.toString(), 'toStringOnlyObject returns toString'); + st.equal(ES.ToPrimitive(valueOfOnlyObject), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject returns valueOf'); + st.equal(ES.ToPrimitive({}), '[object Object]', '{} with no hint coerces to Object#toString'); + st.equal(ES.ToPrimitive({}, String), '[object Object]', '{} with hint String coerces to Object#toString'); + st.equal(ES.ToPrimitive({}, Number), '[object Object]', '{} with hint Number coerces to Object#toString'); + st['throws'](function () { return ES.ToPrimitive(uncoercibleObject); }, TypeError, 'uncoercibleObject throws a TypeError'); + st['throws'](function () { return ES.ToPrimitive(uncoercibleFnObject); }, TypeError, 'uncoercibleFnObject throws a TypeError'); + st.end(); + }); + + t.end(); +}); + +test('ToBoolean', function (t) { + t.equal(false, ES.ToBoolean(undefined), 'undefined coerces to false'); + t.equal(false, ES.ToBoolean(null), 'null coerces to false'); + t.equal(false, ES.ToBoolean(false), 'false returns false'); + t.equal(true, ES.ToBoolean(true), 'true returns true'); + forEach([0, -0, NaN], function (falsyNumber) { + t.equal(false, ES.ToBoolean(falsyNumber), 'falsy number ' + falsyNumber + ' coerces to false'); + }); + forEach([Infinity, 42, 1, -Infinity], function (truthyNumber) { + t.equal(true, ES.ToBoolean(truthyNumber), 'truthy number ' + truthyNumber + ' coerces to true'); + }); + t.equal(false, ES.ToBoolean(''), 'empty string coerces to false'); + t.equal(true, ES.ToBoolean('foo'), 'nonempty string coerces to true'); + forEach(objects, function (obj) { + t.equal(true, ES.ToBoolean(obj), 'object coerces to true'); + }); + t.equal(true, ES.ToBoolean(uncoercibleObject), 'uncoercibleObject coerces to true'); + t.end(); +}); + +test('ToNumber', function (t) { + t.ok(is(NaN, ES.ToNumber(undefined)), 'undefined coerces to NaN'); + t.ok(is(ES.ToNumber(null), 0), 'null coerces to +0'); + t.ok(is(ES.ToNumber(false), 0), 'false coerces to +0'); + t.equal(1, ES.ToNumber(true), 'true coerces to 1'); + t.ok(is(NaN, ES.ToNumber(NaN)), 'NaN returns itself'); + forEach([0, -0, 42, Infinity, -Infinity], function (num) { + t.equal(num, ES.ToNumber(num), num + ' returns itself'); + }); + forEach(['foo', '0', '4a', '2.0', 'Infinity', '-Infinity'], function (numString) { + t.ok(is(+numString, ES.ToNumber(numString)), '"' + numString + '" coerces to ' + Number(numString)); + }); + forEach(objects, function (object) { + t.ok(is(ES.ToNumber(object), ES.ToNumber(ES.ToPrimitive(object))), 'object ' + object + ' coerces to same as ToPrimitive of object does'); + }); + t['throws'](function () { return ES.ToNumber(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.end(); +}); + +test('ToInteger', function (t) { + t.ok(is(0, ES.ToInteger(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity, 42], function (num) { + t.ok(is(num, ES.ToInteger(num)), num + ' returns itself'); + t.ok(is(-num, ES.ToInteger(-num)), '-' + num + ' returns itself'); + }); + t.equal(3, ES.ToInteger(Math.PI), 'pi returns 3'); + t['throws'](function () { return ES.ToInteger(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.end(); +}); + +test('ToInt32', function (t) { + t.ok(is(0, ES.ToInt32(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToInt32(num)), num + ' returns +0'); + t.ok(is(0, ES.ToInt32(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToInt32(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToInt32(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToInt32(0x100000000 - 1), -1), '2^32 - 1 returns -1'); + t.ok(is(ES.ToInt32(0x80000000), -0x80000000), '2^31 returns -2^31'); + t.ok(is(ES.ToInt32(0x80000000 - 1), 0x80000000 - 1), '2^31 - 1 returns 2^31 - 1'); + forEach([0, Infinity, NaN, 0x100000000, 0x80000000, 0x10000, 0x42], function (num) { + t.ok(is(ES.ToInt32(num), ES.ToInt32(ES.ToUint32(num))), 'ToInt32(x) === ToInt32(ToUint32(x)) for 0x' + num.toString(16)); + t.ok(is(ES.ToInt32(-num), ES.ToInt32(ES.ToUint32(-num))), 'ToInt32(x) === ToInt32(ToUint32(x)) for -0x' + num.toString(16)); + }); + t.end(); +}); + +test('ToUint32', function (t) { + t.ok(is(0, ES.ToUint32(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToUint32(num)), num + ' returns +0'); + t.ok(is(0, ES.ToUint32(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToUint32(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToUint32(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToUint32(0x100000000 - 1), 0x100000000 - 1), '2^32 - 1 returns 2^32 - 1'); + t.ok(is(ES.ToUint32(0x80000000), 0x80000000), '2^31 returns 2^31'); + t.ok(is(ES.ToUint32(0x80000000 - 1), 0x80000000 - 1), '2^31 - 1 returns 2^31 - 1'); + forEach([0, Infinity, NaN, 0x100000000, 0x80000000, 0x10000, 0x42], function (num) { + t.ok(is(ES.ToUint32(num), ES.ToUint32(ES.ToInt32(num))), 'ToUint32(x) === ToUint32(ToInt32(x)) for 0x' + num.toString(16)); + t.ok(is(ES.ToUint32(-num), ES.ToUint32(ES.ToInt32(-num))), 'ToUint32(x) === ToUint32(ToInt32(x)) for -0x' + num.toString(16)); + }); + t.end(); +}); + +test('ToUint16', function (t) { + t.ok(is(0, ES.ToUint16(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToUint16(num)), num + ' returns +0'); + t.ok(is(0, ES.ToUint16(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToUint16(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToUint16(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToUint16(0x100000000 - 1), 0x10000 - 1), '2^32 - 1 returns 2^16 - 1'); + t.ok(is(ES.ToUint16(0x80000000), 0), '2^31 returns +0'); + t.ok(is(ES.ToUint16(0x80000000 - 1), 0x10000 - 1), '2^31 - 1 returns 2^16 - 1'); + t.ok(is(ES.ToUint16(0x10000), 0), '2^16 returns +0'); + t.ok(is(ES.ToUint16(0x10000 - 1), 0x10000 - 1), '2^16 - 1 returns 2^16 - 1'); + t.end(); +}); + +test('ToString', function (t) { + t['throws'](function () { return ES.ToString(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.end(); +}); + +test('ToObject', function (t) { + t['throws'](function () { return ES.ToObject(undefined); }, TypeError, 'undefined throws'); + t['throws'](function () { return ES.ToObject(null); }, TypeError, 'null throws'); + forEach(numbers, function (number) { + var obj = ES.ToObject(number); + t.equal(typeof obj, 'object', 'number ' + number + ' coerces to object'); + t.equal(true, obj instanceof Number, 'object of ' + number + ' is Number object'); + t.ok(is(obj.valueOf(), number), 'object of ' + number + ' coerces to ' + number); + }); + t.end(); +}); + +test('CheckObjectCoercible', function (t) { + t['throws'](function () { return ES.CheckObjectCoercible(undefined); }, TypeError, 'undefined throws'); + t['throws'](function () { return ES.CheckObjectCoercible(null); }, TypeError, 'null throws'); + var checkCoercible = function (value) { + t.doesNotThrow(function () { return ES.CheckObjectCoercible(value); }, '"' + value + '" does not throw'); + }; + forEach(objects.concat(nonNullPrimitives), checkCoercible); + t.end(); +}); + +test('IsCallable', function (t) { + t.equal(true, ES.IsCallable(function () {}), 'function is callable'); + var nonCallables = [/a/g, {}, Object.prototype, NaN].concat(primitives); + forEach(nonCallables, function (nonCallable) { + t.equal(false, ES.IsCallable(nonCallable), nonCallable + ' is not callable'); + }); + t.end(); +}); + +test('SameValue', function (t) { + t.equal(true, ES.SameValue(NaN, NaN), 'NaN is SameValue as NaN'); + t.equal(false, ES.SameValue(0, -0), '+0 is not SameValue as -0'); + forEach(objects.concat(primitives), function (val) { + t.equal(val === val, ES.SameValue(val, val), '"' + val + '" is SameValue to itself'); + }); + t.end(); +}); + +test('Type', function (t) { + t.equal(ES.Type(), 'Undefined', 'Type() is Undefined'); + t.equal(ES.Type(undefined), 'Undefined', 'Type(undefined) is Undefined'); + t.equal(ES.Type(null), 'Null', 'Type(null) is Null'); + t.equal(ES.Type(true), 'Boolean', 'Type(true) is Boolean'); + t.equal(ES.Type(false), 'Boolean', 'Type(false) is Boolean'); + t.equal(ES.Type(0), 'Number', 'Type(0) is Number'); + t.equal(ES.Type(NaN), 'Number', 'Type(NaN) is Number'); + t.equal(ES.Type('abc'), 'String', 'Type("abc") is String'); + t.equal(ES.Type(function () {}), 'Object', 'Type(function () {}) is Object'); + t.equal(ES.Type({}), 'Object', 'Type({}) is Object'); + t.end(); +}); + +var bothDescriptor = function () { + return { '[[Get]]': function () {}, '[[Value]]': true }; +}; +var accessorDescriptor = function () { + return { + '[[Get]]': function () {}, + '[[Enumerable]]': true, + '[[Configurable]]': true + }; +}; +var mutatorDescriptor = function () { + return { + '[[Set]]': function () {}, + '[[Enumerable]]': true, + '[[Configurable]]': true + }; +}; +var dataDescriptor = function () { + return { + '[[Value]]': 42, + '[[Writable]]': false, + '[[Configurable]]': false + }; +}; +var genericDescriptor = function () { + return { + '[[Configurable]]': true, + '[[Enumerable]]': false + }; +}; + +test('IsPropertyDescriptor', function (t) { + forEach(primitives, function (primitive) { + t.equal(ES.IsPropertyDescriptor(primitive), false, primitive + ' is not a Property Descriptor'); + }); + + t.equal(ES.IsPropertyDescriptor({ invalid: true }), false, 'invalid keys not allowed on a Property Descriptor'); + + t.equal(ES.IsPropertyDescriptor({}), true, 'empty object is an incomplete Property Descriptor'); + + t.equal(ES.IsPropertyDescriptor(accessorDescriptor()), true, 'accessor descriptor is a Property Descriptor'); + t.equal(ES.IsPropertyDescriptor(mutatorDescriptor()), true, 'mutator descriptor is a Property Descriptor'); + t.equal(ES.IsPropertyDescriptor(dataDescriptor()), true, 'data descriptor is a Property Descriptor'); + t.equal(ES.IsPropertyDescriptor(genericDescriptor()), true, 'generic descriptor is a Property Descriptor'); + + t['throws'](function () { + ES.IsPropertyDescriptor(bothDescriptor()); + }, TypeError, 'a Property Descriptor can not be both a Data and an Accessor Descriptor'); + + t.end(); +}); + +test('IsAccessorDescriptor', function (t) { + forEach(nonNullPrimitives.concat(null), function (primitive) { + t['throws'](function () { ES.IsAccessorDescriptor(primitive); }, TypeError, primitive + ' is not a Property Descriptor'); + }); + + t.equal(ES.IsAccessorDescriptor(), false, 'no value is not an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(undefined), false, 'undefined value is not an Accessor Descriptor'); + + t.equal(ES.IsAccessorDescriptor(accessorDescriptor()), true, 'accessor descriptor is an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(mutatorDescriptor()), true, 'mutator descriptor is an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(dataDescriptor()), false, 'data descriptor is not an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(genericDescriptor()), false, 'generic descriptor is not an Accessor Descriptor'); + + t.end(); +}); + +test('IsDataDescriptor', function (t) { + forEach(nonNullPrimitives.concat(null), function (primitive) { + t['throws'](function () { ES.IsDataDescriptor(primitive); }, TypeError, primitive + ' is not a Property Descriptor'); + }); + + t.equal(ES.IsDataDescriptor(), false, 'no value is not a Data Descriptor'); + t.equal(ES.IsDataDescriptor(undefined), false, 'undefined value is not a Data Descriptor'); + + t.equal(ES.IsDataDescriptor(accessorDescriptor()), false, 'accessor descriptor is not a Data Descriptor'); + t.equal(ES.IsDataDescriptor(mutatorDescriptor()), false, 'mutator descriptor is not a Data Descriptor'); + t.equal(ES.IsDataDescriptor(dataDescriptor()), true, 'data descriptor is a Data Descriptor'); + t.equal(ES.IsDataDescriptor(genericDescriptor()), false, 'generic descriptor is not a Data Descriptor'); + + t.end(); +}); + +test('IsGenericDescriptor', function (t) { + forEach(nonNullPrimitives.concat(null), function (primitive) { + t['throws']( + function () { ES.IsGenericDescriptor(primitive); }, + TypeError, + primitive + ' is not a Property Descriptor' + ); + }); + + t.equal(ES.IsGenericDescriptor(), false, 'no value is not a Data Descriptor'); + t.equal(ES.IsGenericDescriptor(undefined), false, 'undefined value is not a Data Descriptor'); + + t.equal(ES.IsGenericDescriptor(accessorDescriptor()), false, 'accessor descriptor is not a generic Descriptor'); + t.equal(ES.IsGenericDescriptor(mutatorDescriptor()), false, 'mutator descriptor is not a generic Descriptor'); + t.equal(ES.IsGenericDescriptor(dataDescriptor()), false, 'data descriptor is not a generic Descriptor'); + + t.equal(ES.IsGenericDescriptor(genericDescriptor()), true, 'generic descriptor is a generic Descriptor'); + + t.end(); +}); + +test('FromPropertyDescriptor', function (t) { + t.equal(ES.FromPropertyDescriptor(), undefined, 'no value begets undefined'); + t.equal(ES.FromPropertyDescriptor(undefined), undefined, 'undefined value begets undefined'); + + forEach(nonNullPrimitives.concat(null), function (primitive) { + t['throws']( + function () { ES.FromPropertyDescriptor(primitive); }, + TypeError, + primitive + ' is not a Property Descriptor' + ); + }); + + var accessor = accessorDescriptor(); + t.deepEqual(ES.FromPropertyDescriptor(accessor), { + get: accessor['[[Get]]'], + set: accessor['[[Set]]'], + enumerable: !!accessor['[[Enumerable]]'], + configurable: !!accessor['[[Configurable]]'] + }); + + var mutator = mutatorDescriptor(); + t.deepEqual(ES.FromPropertyDescriptor(mutator), { + get: mutator['[[Get]]'], + set: mutator['[[Set]]'], + enumerable: !!mutator['[[Enumerable]]'], + configurable: !!mutator['[[Configurable]]'] + }); + var data = dataDescriptor(); + t.deepEqual(ES.FromPropertyDescriptor(data), { + value: data['[[Value]]'], + writable: data['[[Writable]]'], + enumerable: !!data['[[Enumerable]]'], + configurable: !!data['[[Configurable]]'] + }); + + t['throws']( + function () { ES.FromPropertyDescriptor(genericDescriptor()); }, + TypeError, + 'a complete Property Descriptor is required' + ); + + t.end(); +}); + +test('ToPropertyDescriptor', function (t) { + forEach(nonNullPrimitives.concat(null), function (primitive) { + t['throws']( + function () { ES.ToPropertyDescriptor(primitive); }, + TypeError, + primitive + ' is not an Object' + ); + }); + + var accessor = accessorDescriptor(); + t.deepEqual(ES.ToPropertyDescriptor({ + get: accessor['[[Get]]'], + enumerable: !!accessor['[[Enumerable]]'], + configurable: !!accessor['[[Configurable]]'] + }), accessor); + + var mutator = mutatorDescriptor(); + t.deepEqual(ES.ToPropertyDescriptor({ + set: mutator['[[Set]]'], + enumerable: !!mutator['[[Enumerable]]'], + configurable: !!mutator['[[Configurable]]'] + }), mutator); + + var data = dataDescriptor(); + t.deepEqual(ES.ToPropertyDescriptor({ + value: data['[[Value]]'], + writable: data['[[Writable]]'], + configurable: !!data['[[Configurable]]'] + }), data); + + var both = bothDescriptor(); + t['throws']( + function () { + ES.ToPropertyDescriptor({ get: both['[[Get]]'], value: both['[[Value]]'] }); + }, + TypeError, + 'data and accessor descriptors are mutually exclusive' + ); + + t['throws']( + function () { ES.ToPropertyDescriptor({ get: 'not callable' }); }, + TypeError, + '"get" must be undefined or callable' + ); + + t['throws']( + function () { ES.ToPropertyDescriptor({ set: 'not callable' }); }, + TypeError, + '"set" must be undefined or callable' + ); + + t.end(); +}); diff --git a/WebServer/node_modules/es-abstract/test/es6.js b/WebServer/node_modules/es-abstract/test/es6.js new file mode 100644 index 000000000..e7c9d98a2 --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/es6.js @@ -0,0 +1,18 @@ +'use strict'; + +var test = require('tape'); + +var ES = require('../'); +var ES6 = ES.ES6; +var ES2015 = ES.ES2015; +var ES6entry = require('../es6'); + +test('legacy es6 export', function (t) { + t.equal(ES6, ES2015, 'main ES6 === main ES2015'); + t.end(); +}); + +test('legacy es6 entry point', function (t) { + t.equal(ES6, ES6entry, 'main ES6 === ES6 entry point'); + t.end(); +}); diff --git a/WebServer/node_modules/es-abstract/test/es7.js b/WebServer/node_modules/es-abstract/test/es7.js new file mode 100644 index 000000000..ee57e153b --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/es7.js @@ -0,0 +1,18 @@ +'use strict'; + +var test = require('tape'); + +var ES = require('../'); +var ES7 = ES.ES7; +var ES2016 = ES.ES2016; +var ES7entry = require('../es7'); + +test('legacy es7 export', function (t) { + t.equal(ES7, ES2016, 'main ES7 === main ES2016'); + t.end(); +}); + +test('legacy es7 entry point', function (t) { + t.equal(ES7, ES7entry, 'main ES7 === ES7 entry point'); + t.end(); +}); diff --git a/WebServer/node_modules/es-abstract/test/helpers/assertRecord.js b/WebServer/node_modules/es-abstract/test/helpers/assertRecord.js new file mode 100644 index 000000000..2af989461 --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/helpers/assertRecord.js @@ -0,0 +1,60 @@ +'use strict'; + +var forEach = require('foreach'); +var debug = require('object-inspect'); + +var assertRecord = require('../../helpers/assertRecord'); +var v = require('./values'); + +module.exports = function assertRecordTests(ES, test) { + test('Property Descriptor', function (t) { + var record = 'Property Descriptor'; + + forEach(v.nonUndefinedPrimitives, function (primitive) { + t['throws']( + function () { assertRecord(ES, record, 'arg', primitive); }, + TypeError, + debug(primitive) + ' is not a Property Descriptor' + ); + }); + + t['throws']( + function () { assertRecord(ES, record, 'arg', { invalid: true }); }, + TypeError, + 'invalid keys not allowed on a Property Descriptor' + ); + + t.doesNotThrow( + function () { assertRecord(ES, record, 'arg', {}); }, + 'empty object is an incomplete Property Descriptor' + ); + + t.doesNotThrow( + function () { assertRecord(ES, record, 'arg', v.accessorDescriptor()); }, + 'accessor descriptor is a Property Descriptor' + ); + + t.doesNotThrow( + function () { assertRecord(ES, record, 'arg', v.mutatorDescriptor()); }, + 'mutator descriptor is a Property Descriptor' + ); + + t.doesNotThrow( + function () { assertRecord(ES, record, 'arg', v.dataDescriptor()); }, + 'data descriptor is a Property Descriptor' + ); + + t.doesNotThrow( + function () { assertRecord(ES, record, 'arg', v.genericDescriptor()); }, + 'generic descriptor is a Property Descriptor' + ); + + t['throws']( + function () { assertRecord(ES, record, 'arg', v.bothDescriptor()); }, + TypeError, + 'a Property Descriptor can not be both a Data and an Accessor Descriptor' + ); + + t.end(); + }); +}; diff --git a/WebServer/node_modules/es-abstract/test/helpers/values.js b/WebServer/node_modules/es-abstract/test/helpers/values.js new file mode 100644 index 000000000..b05507cb0 --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/helpers/values.js @@ -0,0 +1,84 @@ +'use strict'; + +var hasSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'; + +var coercibleObject = { valueOf: function () { return 3; }, toString: function () { return 42; } }; +var valueOfOnlyObject = { valueOf: function () { return 4; }, toString: function () { return {}; } }; +var toStringOnlyObject = { valueOf: function () { return {}; }, toString: function () { return 7; } }; +var uncoercibleObject = { valueOf: function () { return {}; }, toString: function () { return {}; } }; +var objects = [{}, coercibleObject, toStringOnlyObject, valueOfOnlyObject]; +var nullPrimitives = [undefined, null]; +var nonIntegerNumbers = [-1.3, 0.2, 1.8, 1 / 3]; +var numbers = [0, -0, Infinity, -Infinity, 42].concat(nonIntegerNumbers); +var strings = ['', 'foo', 'a\uD83D\uDCA9c']; +var booleans = [true, false]; +var symbols = hasSymbols ? [Symbol.iterator, Symbol('foo')] : []; +var nonSymbolPrimitives = [].concat(nullPrimitives, booleans, strings, numbers); +var nonNumberPrimitives = [].concat(nullPrimitives, booleans, strings, symbols); +var nonNullPrimitives = [].concat(booleans, strings, numbers, symbols); +var nonUndefinedPrimitives = [].concat(null, nonNullPrimitives); +var nonStrings = [].concat(nullPrimitives, booleans, numbers, symbols, objects); +var primitives = [].concat(nullPrimitives, nonNullPrimitives); +var nonPropertyKeys = [].concat(nullPrimitives, booleans, numbers, objects); +var propertyKeys = [].concat(strings, symbols); +var nonBooleans = [].concat(nullPrimitives, strings, symbols, numbers, objects); +var falsies = [].concat(nullPrimitives, false, '', 0, -0, NaN); +var truthies = [].concat(true, 'foo', 42, symbols, objects); +var timestamps = [].concat(0, 946713600000, 1546329600000); + +module.exports = { + coercibleObject: coercibleObject, + valueOfOnlyObject: valueOfOnlyObject, + toStringOnlyObject: toStringOnlyObject, + uncoercibleObject: uncoercibleObject, + objects: objects, + nullPrimitives: nullPrimitives, + numbers: numbers, + strings: strings, + booleans: booleans, + symbols: symbols, + hasSymbols: hasSymbols, + nonSymbolPrimitives: nonSymbolPrimitives, + nonNumberPrimitives: nonNumberPrimitives, + nonNullPrimitives: nonNullPrimitives, + nonUndefinedPrimitives: nonUndefinedPrimitives, + nonStrings: nonStrings, + nonNumbers: nonNumberPrimitives.concat(objects), + nonIntegerNumbers: nonIntegerNumbers, + primitives: primitives, + nonPropertyKeys: nonPropertyKeys, + propertyKeys: propertyKeys, + nonBooleans: nonBooleans, + falsies: falsies, + truthies: truthies, + timestamps: timestamps, + bothDescriptor: function () { + return { '[[Get]]': function () {}, '[[Value]]': true }; + }, + accessorDescriptor: function () { + return { + '[[Get]]': function () {}, + '[[Enumerable]]': true, + '[[Configurable]]': true + }; + }, + mutatorDescriptor: function () { + return { + '[[Set]]': function () {}, + '[[Enumerable]]': true, + '[[Configurable]]': true + }; + }, + dataDescriptor: function () { + return { + '[[Value]]': 42, + '[[Writable]]': false + }; + }, + genericDescriptor: function () { + return { + '[[Configurable]]': true, + '[[Enumerable]]': false + }; + } +}; diff --git a/WebServer/node_modules/es-abstract/test/index.js b/WebServer/node_modules/es-abstract/test/index.js new file mode 100644 index 000000000..15c49162f --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/index.js @@ -0,0 +1,29 @@ +'use strict'; + +var ES = require('../'); +var test = require('tape'); + +var ESkeys = Object.keys(ES).sort(); +var ES6keys = Object.keys(ES.ES6).sort(); + +test('exposed properties', function (t) { + t.deepEqual(ESkeys, ES6keys.concat(['ES2018', 'ES2017', 'ES7', 'ES2016', 'ES6', 'ES2015', 'ES5']).sort(), 'main ES object keys match ES6 keys'); + t.end(); +}); + +test('methods match', function (t) { + ES6keys.forEach(function (key) { + t.equal(ES.ES6[key], ES[key], 'method ' + key + ' on main ES object is ES6 method'); + }); + t.end(); +}); + +require('./GetIntrinsic'); + +require('./es5'); +require('./es6'); +require('./es2015'); +require('./es7'); +require('./es2016'); +require('./es2017'); +require('./es2018'); diff --git a/WebServer/node_modules/es-abstract/test/tests.js b/WebServer/node_modules/es-abstract/test/tests.js new file mode 100644 index 000000000..b9b6ee7db --- /dev/null +++ b/WebServer/node_modules/es-abstract/test/tests.js @@ -0,0 +1,2136 @@ +'use strict'; + +var test = require('tape'); + +var forEach = require('foreach'); +var is = require('object-is'); +var debug = require('object-inspect'); +var assign = require('object.assign'); +var keys = require('object-keys'); +var has = require('has'); + +var assertRecordTests = require('./helpers/assertRecord'); +var v = require('./helpers/values'); +var diffOps = require('./diffOps'); + +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1; + +var getArraySubclassWithSpeciesConstructor = function getArraySubclass(speciesConstructor) { + var Bar = function Bar() { + var inst = []; + Object.setPrototypeOf(inst, Bar.prototype); + Object.defineProperty(inst, 'constructor', { value: Bar }); + return inst; + }; + Bar.prototype = Object.create(Array.prototype); + Object.setPrototypeOf(Bar, Array); + Object.defineProperty(Bar, Symbol.species, { value: speciesConstructor }); + + return Bar; +}; + +var hasSpecies = v.hasSymbols && Symbol.species; + +var hasGroups = 'groups' in (/a/).exec('a'); +var groups = function groups(matchObject) { + return hasGroups ? assign(matchObject, { groups: matchObject.groups }) : matchObject; +}; + +var testEnumerableOwnNames = function (t, enumerableOwnNames) { + forEach(v.primitives, function (nonObject) { + t['throws']( + function () { enumerableOwnNames(nonObject); }, + debug(nonObject) + ' is not an Object' + ); + }); + + var Child = function Child() { + this.own = {}; + }; + Child.prototype = { + inherited: {} + }; + + var obj = new Child(); + + t.equal('own' in obj, true, 'has "own"'); + t.equal(has(obj, 'own'), true, 'has own "own"'); + t.equal(Object.prototype.propertyIsEnumerable.call(obj, 'own'), true, 'has enumerable "own"'); + + t.equal('inherited' in obj, true, 'has "inherited"'); + t.equal(has(obj, 'inherited'), false, 'has non-own "inherited"'); + t.equal(has(Child.prototype, 'inherited'), true, 'Child.prototype has own "inherited"'); + t.equal(Child.prototype.inherited, obj.inherited, 'Child.prototype.inherited === obj.inherited'); + t.equal(Object.prototype.propertyIsEnumerable.call(Child.prototype, 'inherited'), true, 'has enumerable "inherited"'); + + t.equal('toString' in obj, true, 'has "toString"'); + t.equal(has(obj, 'toString'), false, 'has non-own "toString"'); + t.equal(has(Object.prototype, 'toString'), true, 'Object.prototype has own "toString"'); + t.equal(Object.prototype.toString, obj.toString, 'Object.prototype.toString === obj.toString'); + // eslint-disable-next-line no-useless-call + t.equal(Object.prototype.propertyIsEnumerable.call(Object.prototype, 'toString'), false, 'has non-enumerable "toString"'); + + return obj; +}; + +var es2015 = function ES2015(ES, ops, expectedMissing, skips) { + test('has expected operations', function (t) { + var diff = diffOps(ES, ops, expectedMissing); + + t.deepEqual(diff.extra, [], 'no extra ops'); + + t.deepEqual(diff.missing, [], 'no unexpected missing ops'); + + t.end(); + }); + + test('ToPrimitive', function (t) { + t.test('primitives', function (st) { + var testPrimitive = function (primitive) { + st.ok(is(ES.ToPrimitive(primitive), primitive), debug(primitive) + ' is returned correctly'); + }; + forEach(v.primitives, testPrimitive); + st.end(); + }); + + t.test('objects', function (st) { + st.equal(ES.ToPrimitive(v.coercibleObject), 3, 'coercibleObject with no hint coerces to valueOf'); + st.ok(is(ES.ToPrimitive({}), '[object Object]'), '{} with no hint coerces to Object#toString'); + st.equal(ES.ToPrimitive(v.coercibleObject, Number), 3, 'coercibleObject with hint Number coerces to valueOf'); + st.ok(is(ES.ToPrimitive({}, Number), '[object Object]'), '{} with hint Number coerces to NaN'); + st.equal(ES.ToPrimitive(v.coercibleObject, String), 42, 'coercibleObject with hint String coerces to nonstringified toString'); + st.equal(ES.ToPrimitive({}, String), '[object Object]', '{} with hint String coerces to Object#toString'); + st.equal(ES.ToPrimitive(v.toStringOnlyObject), 7, 'toStringOnlyObject returns non-stringified toString'); + st.equal(ES.ToPrimitive(v.valueOfOnlyObject), 4, 'valueOfOnlyObject returns valueOf'); + st['throws'](function () { return ES.ToPrimitive(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws a TypeError'); + st.end(); + }); + + t.test('dates', function (st) { + var invalid = new Date(NaN); + st.equal(ES.ToPrimitive(invalid), Date.prototype.toString.call(invalid), 'invalid Date coerces to Date#toString'); + var now = new Date(); + st.equal(ES.ToPrimitive(now), Date.prototype.toString.call(now), 'Date coerces to Date#toString'); + st.end(); + }); + + t.end(); + }); + + test('ToBoolean', function (t) { + t.equal(false, ES.ToBoolean(undefined), 'undefined coerces to false'); + t.equal(false, ES.ToBoolean(null), 'null coerces to false'); + t.equal(false, ES.ToBoolean(false), 'false returns false'); + t.equal(true, ES.ToBoolean(true), 'true returns true'); + + t.test('numbers', function (st) { + forEach([0, -0, NaN], function (falsyNumber) { + st.equal(false, ES.ToBoolean(falsyNumber), 'falsy number ' + falsyNumber + ' coerces to false'); + }); + forEach([Infinity, 42, 1, -Infinity], function (truthyNumber) { + st.equal(true, ES.ToBoolean(truthyNumber), 'truthy number ' + truthyNumber + ' coerces to true'); + }); + + st.end(); + }); + + t.equal(false, ES.ToBoolean(''), 'empty string coerces to false'); + t.equal(true, ES.ToBoolean('foo'), 'nonempty string coerces to true'); + + t.test('objects', function (st) { + forEach(v.objects, function (obj) { + st.equal(true, ES.ToBoolean(obj), 'object coerces to true'); + }); + st.equal(true, ES.ToBoolean(v.uncoercibleObject), 'uncoercibleObject coerces to true'); + + st.end(); + }); + + t.end(); + }); + + test('ToNumber', function (t) { + t.ok(is(NaN, ES.ToNumber(undefined)), 'undefined coerces to NaN'); + t.ok(is(ES.ToNumber(null), 0), 'null coerces to +0'); + t.ok(is(ES.ToNumber(false), 0), 'false coerces to +0'); + t.equal(1, ES.ToNumber(true), 'true coerces to 1'); + + t.test('numbers', function (st) { + st.ok(is(NaN, ES.ToNumber(NaN)), 'NaN returns itself'); + forEach([0, -0, 42, Infinity, -Infinity], function (num) { + st.equal(num, ES.ToNumber(num), num + ' returns itself'); + }); + forEach(['foo', '0', '4a', '2.0', 'Infinity', '-Infinity'], function (numString) { + st.ok(is(+numString, ES.ToNumber(numString)), '"' + numString + '" coerces to ' + Number(numString)); + }); + st.end(); + }); + + t.test('objects', function (st) { + forEach(v.objects, function (object) { + st.ok(is(ES.ToNumber(object), ES.ToNumber(ES.ToPrimitive(object))), 'object ' + object + ' coerces to same as ToPrimitive of object does'); + }); + st['throws'](function () { return ES.ToNumber(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + st.end(); + }); + + t.test('binary literals', function (st) { + st.equal(ES.ToNumber('0b10'), 2, '0b10 is 2'); + st.equal(ES.ToNumber({ toString: function () { return '0b11'; } }), 3, 'Object that toStrings to 0b11 is 3'); + + st.equal(true, is(ES.ToNumber('0b12'), NaN), '0b12 is NaN'); + st.equal(true, is(ES.ToNumber({ toString: function () { return '0b112'; } }), NaN), 'Object that toStrings to 0b112 is NaN'); + st.end(); + }); + + t.test('octal literals', function (st) { + st.equal(ES.ToNumber('0o10'), 8, '0o10 is 8'); + st.equal(ES.ToNumber({ toString: function () { return '0o11'; } }), 9, 'Object that toStrings to 0o11 is 9'); + + st.equal(true, is(ES.ToNumber('0o18'), NaN), '0o18 is NaN'); + st.equal(true, is(ES.ToNumber({ toString: function () { return '0o118'; } }), NaN), 'Object that toStrings to 0o118 is NaN'); + st.end(); + }); + + t.test('signed hex numbers', function (st) { + st.equal(true, is(ES.ToNumber('-0xF'), NaN), '-0xF is NaN'); + st.equal(true, is(ES.ToNumber(' -0xF '), NaN), 'space-padded -0xF is NaN'); + st.equal(true, is(ES.ToNumber('+0xF'), NaN), '+0xF is NaN'); + st.equal(true, is(ES.ToNumber(' +0xF '), NaN), 'space-padded +0xF is NaN'); + + st.end(); + }); + + t.test('trimming of whitespace and non-whitespace characters', function (st) { + var whitespace = ' \t\x0b\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'; + st.equal(0, ES.ToNumber(whitespace + 0 + whitespace), 'whitespace is trimmed'); + + // Zero-width space (zws), next line character (nel), and non-character (bom) are not whitespace. + var nonWhitespaces = { + '\\u0085': '\u0085', + '\\u200b': '\u200b', + '\\ufffe': '\ufffe' + }; + + forEach(nonWhitespaces, function (desc, nonWS) { + st.equal(true, is(ES.ToNumber(nonWS + 0 + nonWS), NaN), 'non-whitespace ' + desc + ' not trimmed'); + }); + + st.end(); + }); + + forEach(v.symbols, function (symbol) { + t['throws']( + function () { ES.ToNumber(symbol); }, + TypeError, + 'Symbols can’t be converted to a Number: ' + debug(symbol) + ); + }); + + t.test('dates', function (st) { + var invalid = new Date(NaN); + st.ok(is(ES.ToNumber(invalid), NaN), 'invalid Date coerces to NaN'); + var now = Date.now(); + st.equal(ES.ToNumber(new Date(now)), now, 'Date coerces to timestamp'); + st.end(); + }); + + t.end(); + }); + + test('ToInteger', function (t) { + t.ok(is(0, ES.ToInteger(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity, 42], function (num) { + t.ok(is(num, ES.ToInteger(num)), num + ' returns itself'); + t.ok(is(-num, ES.ToInteger(-num)), '-' + num + ' returns itself'); + }); + t.equal(3, ES.ToInteger(Math.PI), 'pi returns 3'); + t['throws'](function () { return ES.ToInteger(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.end(); + }); + + test('ToInt32', function (t) { + t.ok(is(0, ES.ToInt32(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToInt32(num)), num + ' returns +0'); + t.ok(is(0, ES.ToInt32(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToInt32(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToInt32(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToInt32(0x100000000 - 1), -1), '2^32 - 1 returns -1'); + t.ok(is(ES.ToInt32(0x80000000), -0x80000000), '2^31 returns -2^31'); + t.ok(is(ES.ToInt32(0x80000000 - 1), 0x80000000 - 1), '2^31 - 1 returns 2^31 - 1'); + forEach([0, Infinity, NaN, 0x100000000, 0x80000000, 0x10000, 0x42], function (num) { + t.ok(is(ES.ToInt32(num), ES.ToInt32(ES.ToUint32(num))), 'ToInt32(x) === ToInt32(ToUint32(x)) for 0x' + num.toString(16)); + t.ok(is(ES.ToInt32(-num), ES.ToInt32(ES.ToUint32(-num))), 'ToInt32(x) === ToInt32(ToUint32(x)) for -0x' + num.toString(16)); + }); + t.end(); + }); + + test('ToUint32', function (t) { + t.ok(is(0, ES.ToUint32(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToUint32(num)), num + ' returns +0'); + t.ok(is(0, ES.ToUint32(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToUint32(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToUint32(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToUint32(0x100000000 - 1), 0x100000000 - 1), '2^32 - 1 returns 2^32 - 1'); + t.ok(is(ES.ToUint32(0x80000000), 0x80000000), '2^31 returns 2^31'); + t.ok(is(ES.ToUint32(0x80000000 - 1), 0x80000000 - 1), '2^31 - 1 returns 2^31 - 1'); + forEach([0, Infinity, NaN, 0x100000000, 0x80000000, 0x10000, 0x42], function (num) { + t.ok(is(ES.ToUint32(num), ES.ToUint32(ES.ToInt32(num))), 'ToUint32(x) === ToUint32(ToInt32(x)) for 0x' + num.toString(16)); + t.ok(is(ES.ToUint32(-num), ES.ToUint32(ES.ToInt32(-num))), 'ToUint32(x) === ToUint32(ToInt32(x)) for -0x' + num.toString(16)); + }); + t.end(); + }); + + test('ToInt16', function (t) { + t.ok(is(0, ES.ToInt16(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToInt16(num)), num + ' returns +0'); + t.ok(is(0, ES.ToInt16(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToInt16(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToInt16(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToInt16(0x100000000 - 1), -1), '2^32 - 1 returns -1'); + t.ok(is(ES.ToInt16(0x80000000), 0), '2^31 returns +0'); + t.ok(is(ES.ToInt16(0x80000000 - 1), -1), '2^31 - 1 returns -1'); + t.ok(is(ES.ToInt16(0x10000), 0), '2^16 returns +0'); + t.ok(is(ES.ToInt16(0x10000 - 1), -1), '2^16 - 1 returns -1'); + t.end(); + }); + + test('ToUint16', function (t) { + t.ok(is(0, ES.ToUint16(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToUint16(num)), num + ' returns +0'); + t.ok(is(0, ES.ToUint16(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToUint16(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToUint16(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToUint16(0x100000000 - 1), 0x10000 - 1), '2^32 - 1 returns 2^16 - 1'); + t.ok(is(ES.ToUint16(0x80000000), 0), '2^31 returns +0'); + t.ok(is(ES.ToUint16(0x80000000 - 1), 0x10000 - 1), '2^31 - 1 returns 2^16 - 1'); + t.ok(is(ES.ToUint16(0x10000), 0), '2^16 returns +0'); + t.ok(is(ES.ToUint16(0x10000 - 1), 0x10000 - 1), '2^16 - 1 returns 2^16 - 1'); + t.end(); + }); + + test('ToInt8', function (t) { + t.ok(is(0, ES.ToInt8(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToInt8(num)), num + ' returns +0'); + t.ok(is(0, ES.ToInt8(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToInt8(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToInt8(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToInt8(0x100000000 - 1), -1), '2^32 - 1 returns -1'); + t.ok(is(ES.ToInt8(0x80000000), 0), '2^31 returns +0'); + t.ok(is(ES.ToInt8(0x80000000 - 1), -1), '2^31 - 1 returns -1'); + t.ok(is(ES.ToInt8(0x10000), 0), '2^16 returns +0'); + t.ok(is(ES.ToInt8(0x10000 - 1), -1), '2^16 - 1 returns -1'); + t.ok(is(ES.ToInt8(0x100), 0), '2^8 returns +0'); + t.ok(is(ES.ToInt8(0x100 - 1), -1), '2^8 - 1 returns -1'); + t.ok(is(ES.ToInt8(0x10), 0x10), '2^4 returns 2^4'); + t.end(); + }); + + test('ToUint8', function (t) { + t.ok(is(0, ES.ToUint8(NaN)), 'NaN coerces to +0'); + forEach([0, Infinity], function (num) { + t.ok(is(0, ES.ToUint8(num)), num + ' returns +0'); + t.ok(is(0, ES.ToUint8(-num)), '-' + num + ' returns +0'); + }); + t['throws'](function () { return ES.ToUint8(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + t.ok(is(ES.ToUint8(0x100000000), 0), '2^32 returns +0'); + t.ok(is(ES.ToUint8(0x100000000 - 1), 0x100 - 1), '2^32 - 1 returns 2^8 - 1'); + t.ok(is(ES.ToUint8(0x80000000), 0), '2^31 returns +0'); + t.ok(is(ES.ToUint8(0x80000000 - 1), 0x100 - 1), '2^31 - 1 returns 2^8 - 1'); + t.ok(is(ES.ToUint8(0x10000), 0), '2^16 returns +0'); + t.ok(is(ES.ToUint8(0x10000 - 1), 0x100 - 1), '2^16 - 1 returns 2^8 - 1'); + t.ok(is(ES.ToUint8(0x100), 0), '2^8 returns +0'); + t.ok(is(ES.ToUint8(0x100 - 1), 0x100 - 1), '2^8 - 1 returns 2^16 - 1'); + t.ok(is(ES.ToUint8(0x10), 0x10), '2^4 returns 2^4'); + t.ok(is(ES.ToUint8(0x10 - 1), 0x10 - 1), '2^4 - 1 returns 2^4 - 1'); + t.end(); + }); + + test('ToUint8Clamp', function (t) { + t.ok(is(0, ES.ToUint8Clamp(NaN)), 'NaN coerces to +0'); + t.ok(is(0, ES.ToUint8Clamp(0)), '+0 returns +0'); + t.ok(is(0, ES.ToUint8Clamp(-0)), '-0 returns +0'); + t.ok(is(0, ES.ToUint8Clamp(-Infinity)), '-Infinity returns +0'); + t['throws'](function () { return ES.ToUint8Clamp(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + forEach([255, 256, 0x100000, Infinity], function (number) { + t.ok(is(255, ES.ToUint8Clamp(number)), number + ' coerces to 255'); + }); + t.equal(1, ES.ToUint8Clamp(1.49), '1.49 coerces to 1'); + t.equal(2, ES.ToUint8Clamp(1.5), '1.5 coerces to 2, because 2 is even'); + t.equal(2, ES.ToUint8Clamp(1.51), '1.51 coerces to 2'); + + t.equal(2, ES.ToUint8Clamp(2.49), '2.49 coerces to 2'); + t.equal(2, ES.ToUint8Clamp(2.5), '2.5 coerces to 2, because 2 is even'); + t.equal(3, ES.ToUint8Clamp(2.51), '2.51 coerces to 3'); + t.end(); + }); + + test('ToString', function (t) { + forEach(v.objects.concat(v.nonSymbolPrimitives), function (item) { + t.equal(ES.ToString(item), String(item), 'ES.ToString(' + debug(item) + ') ToStrings to String(' + debug(item) + ')'); + }); + + t['throws'](function () { return ES.ToString(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); + + forEach(v.symbols, function (symbol) { + t['throws'](function () { return ES.ToString(symbol); }, TypeError, debug(symbol) + ' throws'); + }); + t.end(); + }); + + test('ToObject', function (t) { + t['throws'](function () { return ES.ToObject(undefined); }, TypeError, 'undefined throws'); + t['throws'](function () { return ES.ToObject(null); }, TypeError, 'null throws'); + forEach(v.numbers, function (number) { + var obj = ES.ToObject(number); + t.equal(typeof obj, 'object', 'number ' + number + ' coerces to object'); + t.equal(true, obj instanceof Number, 'object of ' + number + ' is Number object'); + t.ok(is(obj.valueOf(), number), 'object of ' + number + ' coerces to ' + number); + }); + t.end(); + }); + + test('RequireObjectCoercible', function (t) { + t.equal(false, 'CheckObjectCoercible' in ES, 'CheckObjectCoercible -> RequireObjectCoercible in ES6'); + t['throws'](function () { return ES.RequireObjectCoercible(undefined); }, TypeError, 'undefined throws'); + t['throws'](function () { return ES.RequireObjectCoercible(null); }, TypeError, 'null throws'); + var isCoercible = function (value) { + t.doesNotThrow(function () { return ES.RequireObjectCoercible(value); }, debug(value) + ' does not throw'); + }; + forEach(v.objects.concat(v.nonNullPrimitives), isCoercible); + t.end(); + }); + + test('IsCallable', function (t) { + t.equal(true, ES.IsCallable(function () {}), 'function is callable'); + var nonCallables = [/a/g, {}, Object.prototype, NaN].concat(v.primitives); + forEach(nonCallables, function (nonCallable) { + t.equal(false, ES.IsCallable(nonCallable), debug(nonCallable) + ' is not callable'); + }); + t.end(); + }); + + test('SameValue', function (t) { + t.equal(true, ES.SameValue(NaN, NaN), 'NaN is SameValue as NaN'); + t.equal(false, ES.SameValue(0, -0), '+0 is not SameValue as -0'); + forEach(v.objects.concat(v.primitives), function (val) { + t.equal(val === val, ES.SameValue(val, val), debug(val) + ' is SameValue to itself'); + }); + t.end(); + }); + + test('SameValueZero', function (t) { + t.equal(true, ES.SameValueZero(NaN, NaN), 'NaN is SameValueZero as NaN'); + t.equal(true, ES.SameValueZero(0, -0), '+0 is SameValueZero as -0'); + forEach(v.objects.concat(v.primitives), function (val) { + t.equal(val === val, ES.SameValueZero(val, val), debug(val) + ' is SameValueZero to itself'); + }); + t.end(); + }); + + test('ToPropertyKey', function (t) { + forEach(v.objects.concat(v.nonSymbolPrimitives), function (value) { + t.equal(ES.ToPropertyKey(value), String(value), 'ToPropertyKey(value) === String(value) for non-Symbols'); + }); + + forEach(v.symbols, function (symbol) { + t.equal( + ES.ToPropertyKey(symbol), + symbol, + 'ToPropertyKey(' + debug(symbol) + ') === ' + debug(symbol) + ); + t.equal( + ES.ToPropertyKey(Object(symbol)), + symbol, + 'ToPropertyKey(' + debug(Object(symbol)) + ') === ' + debug(symbol) + ); + }); + + t.end(); + }); + + test('ToLength', function (t) { + t['throws'](function () { return ES.ToLength(v.uncoercibleObject); }, TypeError, 'uncoercibleObject throws a TypeError'); + t.equal(3, ES.ToLength(v.coercibleObject), 'coercibleObject coerces to 3'); + t.equal(42, ES.ToLength('42.5'), '"42.5" coerces to 42'); + t.equal(7, ES.ToLength(7.3), '7.3 coerces to 7'); + forEach([-0, -1, -42, -Infinity], function (negative) { + t.ok(is(0, ES.ToLength(negative)), negative + ' coerces to +0'); + }); + t.equal(MAX_SAFE_INTEGER, ES.ToLength(MAX_SAFE_INTEGER + 1), '2^53 coerces to 2^53 - 1'); + t.equal(MAX_SAFE_INTEGER, ES.ToLength(MAX_SAFE_INTEGER + 3), '2^53 + 2 coerces to 2^53 - 1'); + t.end(); + }); + + test('IsArray', function (t) { + t.equal(true, ES.IsArray([]), '[] is array'); + t.equal(false, ES.IsArray({}), '{} is not array'); + t.equal(false, ES.IsArray({ length: 1, 0: true }), 'arraylike object is not array'); + forEach(v.objects.concat(v.primitives), function (value) { + t.equal(false, ES.IsArray(value), debug(value) + ' is not array'); + }); + t.end(); + }); + + test('IsRegExp', function (t) { + forEach([/a/g, new RegExp('a', 'g')], function (regex) { + t.equal(true, ES.IsRegExp(regex), regex + ' is regex'); + }); + + forEach(v.objects.concat(v.primitives), function (nonRegex) { + t.equal(false, ES.IsRegExp(nonRegex), debug(nonRegex) + ' is not regex'); + }); + + t.test('Symbol.match', { skip: !v.hasSymbols || !Symbol.match }, function (st) { + var obj = {}; + obj[Symbol.match] = true; + st.equal(true, ES.IsRegExp(obj), 'object with truthy Symbol.match is regex'); + + var regex = /a/; + regex[Symbol.match] = false; + st.equal(false, ES.IsRegExp(regex), 'regex with falsy Symbol.match is not regex'); + + st.end(); + }); + + t.end(); + }); + + test('IsPropertyKey', function (t) { + forEach(v.numbers.concat(v.objects), function (notKey) { + t.equal(false, ES.IsPropertyKey(notKey), debug(notKey) + ' is not property key'); + }); + + t.equal(true, ES.IsPropertyKey('foo'), 'string is property key'); + + forEach(v.symbols, function (symbol) { + t.equal(true, ES.IsPropertyKey(symbol), debug(symbol) + ' is property key'); + }); + t.end(); + }); + + test('IsInteger', function (t) { + for (var i = -100; i < 100; i += 10) { + t.equal(true, ES.IsInteger(i), i + ' is integer'); + t.equal(false, ES.IsInteger(i + 0.2), (i + 0.2) + ' is not integer'); + } + t.equal(true, ES.IsInteger(-0), '-0 is integer'); + var notInts = v.nonNumbers.concat(v.nonIntegerNumbers, [Infinity, -Infinity, NaN, [], new Date()]); + forEach(notInts, function (notInt) { + t.equal(false, ES.IsInteger(notInt), debug(notInt) + ' is not integer'); + }); + t.equal(false, ES.IsInteger(v.uncoercibleObject), 'uncoercibleObject is not integer'); + t.end(); + }); + + test('IsExtensible', function (t) { + forEach(v.objects, function (object) { + t.equal(true, ES.IsExtensible(object), debug(object) + ' object is extensible'); + }); + forEach(v.primitives, function (primitive) { + t.equal(false, ES.IsExtensible(primitive), debug(primitive) + ' is not extensible'); + }); + if (Object.preventExtensions) { + t.equal(false, ES.IsExtensible(Object.preventExtensions({})), 'object with extensions prevented is not extensible'); + } + t.end(); + }); + + test('CanonicalNumericIndexString', function (t) { + var throwsOnNonString = function (notString) { + t['throws']( + function () { return ES.CanonicalNumericIndexString(notString); }, + TypeError, + debug(notString) + ' is not a string' + ); + }; + forEach(v.objects.concat(v.numbers), throwsOnNonString); + t.ok(is(-0, ES.CanonicalNumericIndexString('-0')), '"-0" returns -0'); + for (var i = -50; i < 50; i += 10) { + t.equal(i, ES.CanonicalNumericIndexString(String(i)), '"' + i + '" returns ' + i); + t.equal(undefined, ES.CanonicalNumericIndexString(String(i) + 'a'), '"' + i + 'a" returns undefined'); + } + t.end(); + }); + + test('IsConstructor', function (t) { + t.equal(true, ES.IsConstructor(function () {}), 'function is constructor'); + t.equal(false, ES.IsConstructor(/a/g), 'regex is not constructor'); + forEach(v.objects, function (object) { + t.equal(false, ES.IsConstructor(object), object + ' object is not constructor'); + }); + + try { + var foo = Function('return class Foo {}')(); // eslint-disable-line no-new-func + t.equal(ES.IsConstructor(foo), true, 'class is constructor'); + } catch (e) { + t.comment('SKIP: class syntax not supported.'); + } + t.end(); + }); + + test('Call', function (t) { + var receiver = {}; + var notFuncs = v.objects.concat(v.primitives).concat([/a/g, new RegExp('a', 'g')]); + t.plan(notFuncs.length + 4); + var throwsIfNotCallable = function (notFunc) { + t['throws']( + function () { return ES.Call(notFunc, receiver); }, + TypeError, + debug(notFunc) + ' (' + typeof notFunc + ') is not callable' + ); + }; + forEach(notFuncs, throwsIfNotCallable); + ES.Call(function (a, b) { + t.equal(this, receiver, 'context matches expected'); + t.deepEqual([a, b], [1, 2], 'named args are correct'); + t.equal(arguments.length, 3, 'extra argument was passed'); + t.equal(arguments[2], 3, 'extra argument was correct'); + }, receiver, [1, 2, 3]); + t.end(); + }); + + test('GetV', function (t) { + t['throws'](function () { return ES.GetV({ 7: 7 }, 7); }, TypeError, 'Throws a TypeError if `P` is not a property key'); + var obj = { a: function () {} }; + t.equal(ES.GetV(obj, 'a'), obj.a, 'returns property if it exists'); + t.equal(ES.GetV(obj, 'b'), undefined, 'returns undefiend if property does not exist'); + t.end(); + }); + + test('GetMethod', function (t) { + t['throws'](function () { return ES.GetMethod({ 7: 7 }, 7); }, TypeError, 'Throws a TypeError if `P` is not a property key'); + t.equal(ES.GetMethod({}, 'a'), undefined, 'returns undefined in property is undefined'); + t.equal(ES.GetMethod({ a: null }, 'a'), undefined, 'returns undefined if property is null'); + t.equal(ES.GetMethod({ a: undefined }, 'a'), undefined, 'returns undefined if property is undefined'); + var obj = { a: function () {} }; + t['throws'](function () { ES.GetMethod({ a: 'b' }, 'a'); }, TypeError, 'throws TypeError if property exists and is not callable'); + t.equal(ES.GetMethod(obj, 'a'), obj.a, 'returns property if it is callable'); + t.end(); + }); + + test('Get', function (t) { + t['throws'](function () { return ES.Get('a', 'a'); }, TypeError, 'Throws a TypeError if `O` is not an Object'); + t['throws'](function () { return ES.Get({ 7: 7 }, 7); }, TypeError, 'Throws a TypeError if `P` is not a property key'); + + var value = {}; + t.test('Symbols', { skip: !v.hasSymbols }, function (st) { + var sym = Symbol('sym'); + var obj = {}; + obj[sym] = value; + st.equal(ES.Get(obj, sym), value, 'returns property `P` if it exists on object `O`'); + st.end(); + }); + t.equal(ES.Get({ a: value }, 'a'), value, 'returns property `P` if it exists on object `O`'); + t.end(); + }); + + test('Type', { skip: !v.hasSymbols }, function (t) { + t.equal(ES.Type(Symbol.iterator), 'Symbol', 'Type(Symbol.iterator) is Symbol'); + t.end(); + }); + + test('SpeciesConstructor', function (t) { + t['throws'](function () { ES.SpeciesConstructor(null); }, TypeError); + t['throws'](function () { ES.SpeciesConstructor(undefined); }, TypeError); + + var defaultConstructor = function Foo() {}; + + t.equal( + ES.SpeciesConstructor({ constructor: undefined }, defaultConstructor), + defaultConstructor, + 'undefined constructor returns defaultConstructor' + ); + + t['throws']( + function () { return ES.SpeciesConstructor({ constructor: null }, defaultConstructor); }, + TypeError, + 'non-undefined non-object constructor throws' + ); + + t.test('with Symbol.species', { skip: !hasSpecies }, function (st) { + var Bar = function Bar() {}; + Bar[Symbol.species] = null; + + st.equal( + ES.SpeciesConstructor(new Bar(), defaultConstructor), + defaultConstructor, + 'undefined/null Symbol.species returns default constructor' + ); + + var Baz = function Baz() {}; + Baz[Symbol.species] = Bar; + st.equal( + ES.SpeciesConstructor(new Baz(), defaultConstructor), + Bar, + 'returns Symbol.species constructor value' + ); + + Baz[Symbol.species] = {}; + st['throws']( + function () { ES.SpeciesConstructor(new Baz(), defaultConstructor); }, + TypeError, + 'throws when non-constructor non-null non-undefined species value found' + ); + + st.end(); + }); + + t.end(); + }); + + test('IsPropertyDescriptor', { skip: skips && skips.IsPropertyDescriptor }, function (t) { + forEach(v.nonUndefinedPrimitives, function (primitive) { + t.equal( + ES.IsPropertyDescriptor(primitive), + false, + debug(primitive) + ' is not a Property Descriptor' + ); + }); + + t.equal(ES.IsPropertyDescriptor({ invalid: true }), false, 'invalid keys not allowed on a Property Descriptor'); + + t.equal(ES.IsPropertyDescriptor({}), true, 'empty object is an incomplete Property Descriptor'); + + t.equal(ES.IsPropertyDescriptor(v.accessorDescriptor()), true, 'accessor descriptor is a Property Descriptor'); + t.equal(ES.IsPropertyDescriptor(v.mutatorDescriptor()), true, 'mutator descriptor is a Property Descriptor'); + t.equal(ES.IsPropertyDescriptor(v.dataDescriptor()), true, 'data descriptor is a Property Descriptor'); + t.equal(ES.IsPropertyDescriptor(v.genericDescriptor()), true, 'generic descriptor is a Property Descriptor'); + + t['throws'](function () { + ES.IsPropertyDescriptor(v.bothDescriptor()); + }, TypeError, 'a Property Descriptor can not be both a Data and an Accessor Descriptor'); + + t.end(); + }); + + assertRecordTests(ES, test); + + test('IsAccessorDescriptor', function (t) { + forEach(v.nonUndefinedPrimitives, function (primitive) { + t['throws']( + function () { ES.IsAccessorDescriptor(primitive); }, + TypeError, + debug(primitive) + ' is not a Property Descriptor' + ); + }); + + t.equal(ES.IsAccessorDescriptor(), false, 'no value is not an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(undefined), false, 'undefined value is not an Accessor Descriptor'); + + t.equal(ES.IsAccessorDescriptor(v.accessorDescriptor()), true, 'accessor descriptor is an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(v.mutatorDescriptor()), true, 'mutator descriptor is an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(v.dataDescriptor()), false, 'data descriptor is not an Accessor Descriptor'); + t.equal(ES.IsAccessorDescriptor(v.genericDescriptor()), false, 'generic descriptor is not an Accessor Descriptor'); + + t.end(); + }); + + test('IsDataDescriptor', function (t) { + forEach(v.nonUndefinedPrimitives, function (primitive) { + t['throws']( + function () { ES.IsDataDescriptor(primitive); }, + TypeError, + debug(primitive) + ' is not a Property Descriptor' + ); + }); + + t.equal(ES.IsDataDescriptor(), false, 'no value is not a Data Descriptor'); + t.equal(ES.IsDataDescriptor(undefined), false, 'undefined value is not a Data Descriptor'); + + t.equal(ES.IsDataDescriptor(v.accessorDescriptor()), false, 'accessor descriptor is not a Data Descriptor'); + t.equal(ES.IsDataDescriptor(v.mutatorDescriptor()), false, 'mutator descriptor is not a Data Descriptor'); + t.equal(ES.IsDataDescriptor(v.dataDescriptor()), true, 'data descriptor is a Data Descriptor'); + t.equal(ES.IsDataDescriptor(v.genericDescriptor()), false, 'generic descriptor is not a Data Descriptor'); + + t.end(); + }); + + test('IsGenericDescriptor', function (t) { + forEach(v.nonUndefinedPrimitives, function (primitive) { + t['throws']( + function () { ES.IsGenericDescriptor(primitive); }, + TypeError, + debug(primitive) + ' is not a Property Descriptor' + ); + }); + + t.equal(ES.IsGenericDescriptor(), false, 'no value is not a Data Descriptor'); + t.equal(ES.IsGenericDescriptor(undefined), false, 'undefined value is not a Data Descriptor'); + + t.equal(ES.IsGenericDescriptor(v.accessorDescriptor()), false, 'accessor descriptor is not a generic Descriptor'); + t.equal(ES.IsGenericDescriptor(v.mutatorDescriptor()), false, 'mutator descriptor is not a generic Descriptor'); + t.equal(ES.IsGenericDescriptor(v.dataDescriptor()), false, 'data descriptor is not a generic Descriptor'); + + t.equal(ES.IsGenericDescriptor(v.genericDescriptor()), true, 'generic descriptor is a generic Descriptor'); + + t.end(); + }); + + test('FromPropertyDescriptor', function (t) { + t.equal(ES.FromPropertyDescriptor(), undefined, 'no value begets undefined'); + t.equal(ES.FromPropertyDescriptor(undefined), undefined, 'undefined value begets undefined'); + + forEach(v.nonUndefinedPrimitives, function (primitive) { + t['throws']( + function () { ES.FromPropertyDescriptor(primitive); }, + TypeError, + debug(primitive) + ' is not a Property Descriptor' + ); + }); + + var accessor = v.accessorDescriptor(); + t.deepEqual(ES.FromPropertyDescriptor(accessor), { + get: accessor['[[Get]]'], + set: accessor['[[Set]]'], + enumerable: !!accessor['[[Enumerable]]'], + configurable: !!accessor['[[Configurable]]'] + }); + + var mutator = v.mutatorDescriptor(); + t.deepEqual(ES.FromPropertyDescriptor(mutator), { + get: mutator['[[Get]]'], + set: mutator['[[Set]]'], + enumerable: !!mutator['[[Enumerable]]'], + configurable: !!mutator['[[Configurable]]'] + }); + var data = v.dataDescriptor(); + t.deepEqual(ES.FromPropertyDescriptor(data), { + value: data['[[Value]]'], + writable: data['[[Writable]]'], + enumerable: !!data['[[Enumerable]]'], + configurable: !!data['[[Configurable]]'] + }); + + t['throws']( + function () { ES.FromPropertyDescriptor(v.genericDescriptor()); }, + TypeError, + 'a complete Property Descriptor is required' + ); + + t.end(); + }); + + test('ToPropertyDescriptor', function (t) { + forEach(v.nonUndefinedPrimitives, function (primitive) { + t['throws']( + function () { ES.ToPropertyDescriptor(primitive); }, + TypeError, + debug(primitive) + ' is not an Object' + ); + }); + + var accessor = v.accessorDescriptor(); + t.deepEqual(ES.ToPropertyDescriptor({ + get: accessor['[[Get]]'], + enumerable: !!accessor['[[Enumerable]]'], + configurable: !!accessor['[[Configurable]]'] + }), accessor); + + var mutator = v.mutatorDescriptor(); + t.deepEqual(ES.ToPropertyDescriptor({ + set: mutator['[[Set]]'], + enumerable: !!mutator['[[Enumerable]]'], + configurable: !!mutator['[[Configurable]]'] + }), mutator); + + var data = v.dataDescriptor(); + t.deepEqual(ES.ToPropertyDescriptor({ + value: data['[[Value]]'], + writable: data['[[Writable]]'], + configurable: !!data['[[Configurable]]'] + }), assign(data, { '[[Configurable]]': false })); + + var both = v.bothDescriptor(); + t['throws']( + function () { + ES.FromPropertyDescriptor({ get: both['[[Get]]'], value: both['[[Value]]'] }); + }, + TypeError, + 'data and accessor descriptors are mutually exclusive' + ); + + t.end(); + }); + + test('CompletePropertyDescriptor', function (t) { + forEach(v.nonUndefinedPrimitives, function (primitive) { + t['throws']( + function () { ES.CompletePropertyDescriptor(primitive); }, + TypeError, + debug(primitive) + ' is not a Property Descriptor' + ); + }); + + var generic = v.genericDescriptor(); + t.deepEqual(ES.CompletePropertyDescriptor(generic), { + '[[Configurable]]': !!generic['[[Configurable]]'], + '[[Enumerable]]': !!generic['[[Enumerable]]'], + '[[Value]]': undefined, + '[[Writable]]': false + }, 'completes a Generic Descriptor'); + + var data = v.dataDescriptor(); + t.deepEqual(ES.CompletePropertyDescriptor(data), { + '[[Configurable]]': !!data['[[Configurable]]'], + '[[Enumerable]]': false, + '[[Value]]': data['[[Value]]'], + '[[Writable]]': !!data['[[Writable]]'] + }, 'completes a Data Descriptor'); + + var accessor = v.accessorDescriptor(); + t.deepEqual(ES.CompletePropertyDescriptor(accessor), { + '[[Get]]': accessor['[[Get]]'], + '[[Enumerable]]': !!accessor['[[Enumerable]]'], + '[[Configurable]]': !!accessor['[[Configurable]]'], + '[[Set]]': undefined + }, 'completes an Accessor Descriptor'); + + var mutator = v.mutatorDescriptor(); + t.deepEqual(ES.CompletePropertyDescriptor(mutator), { + '[[Set]]': mutator['[[Set]]'], + '[[Enumerable]]': !!mutator['[[Enumerable]]'], + '[[Configurable]]': !!mutator['[[Configurable]]'], + '[[Get]]': undefined + }, 'completes a mutator Descriptor'); + + t['throws']( + function () { ES.CompletePropertyDescriptor(v.bothDescriptor()); }, + TypeError, + 'data and accessor descriptors are mutually exclusive' + ); + + t.end(); + }); + + test('Set', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.Set(primitive, '', null, false); }, + TypeError, + debug(primitive) + ' is not an Object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonKey) { + t['throws']( + function () { ES.Set({}, nonKey, null, false); }, + TypeError, + debug(nonKey) + ' is not a Property Key' + ); + }); + + forEach(v.nonBooleans, function (nonBoolean) { + t['throws']( + function () { ES.Set({}, '', null, nonBoolean); }, + TypeError, + debug(nonBoolean) + ' is not a Boolean' + ); + }); + + var o = {}; + var value = {}; + ES.Set(o, 'key', value, true); + t.deepEqual(o, { key: value }, 'key is set'); + + t.test('nonwritable', { skip: !Object.defineProperty }, function (st) { + var obj = { a: value }; + Object.defineProperty(obj, 'a', { writable: false }); + + st['throws']( + function () { ES.Set(obj, 'a', value, true); }, + TypeError, + 'can not Set nonwritable property' + ); + + st.doesNotThrow( + function () { ES.Set(obj, 'a', value, false); }, + 'setting Throw to false prevents an exception' + ); + + st.end(); + }); + + t.test('nonconfigurable', { skip: !Object.defineProperty }, function (st) { + var obj = { a: value }; + Object.defineProperty(obj, 'a', { configurable: false }); + + ES.Set(obj, 'a', value, true); + st.deepEqual(obj, { a: value }, 'key is set'); + + st.end(); + }); + + t.end(); + }); + + test('HasOwnProperty', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.HasOwnProperty(primitive, 'key'); }, + TypeError, + debug(primitive) + ' is not an Object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonKey) { + t['throws']( + function () { ES.HasOwnProperty({}, nonKey); }, + TypeError, + debug(nonKey) + ' is not a Property Key' + ); + }); + + t.equal(ES.HasOwnProperty({}, 'toString'), false, 'inherited properties are not own'); + t.equal( + ES.HasOwnProperty({ toString: 1 }, 'toString'), + true, + 'shadowed inherited own properties are own' + ); + t.equal(ES.HasOwnProperty({ a: 1 }, 'a'), true, 'own properties are own'); + + t.end(); + }); + + test('HasProperty', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.HasProperty(primitive, 'key'); }, + TypeError, + debug(primitive) + ' is not an Object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonKey) { + t['throws']( + function () { ES.HasProperty({}, nonKey); }, + TypeError, + debug(nonKey) + ' is not a Property Key' + ); + }); + + t.equal(ES.HasProperty({}, 'nope'), false, 'object does not have nonexistent properties'); + t.equal(ES.HasProperty({}, 'toString'), true, 'object has inherited properties'); + t.equal( + ES.HasProperty({ toString: 1 }, 'toString'), + true, + 'object has shadowed inherited own properties' + ); + t.equal(ES.HasProperty({ a: 1 }, 'a'), true, 'object has own properties'); + + t.end(); + }); + + test('IsConcatSpreadable', function (t) { + forEach(v.primitives, function (primitive) { + t.equal(ES.IsConcatSpreadable(primitive), false, debug(primitive) + ' is not an Object'); + }); + + var hasSymbolConcatSpreadable = v.hasSymbols && Symbol.isConcatSpreadable; + t.test('Symbol.isConcatSpreadable', { skip: !hasSymbolConcatSpreadable }, function (st) { + forEach(v.falsies, function (falsy) { + var obj = {}; + obj[Symbol.isConcatSpreadable] = falsy; + st.equal( + ES.IsConcatSpreadable(obj), + false, + 'an object with ' + debug(falsy) + ' as Symbol.isConcatSpreadable is not concat spreadable' + ); + }); + + forEach(v.truthies, function (truthy) { + var obj = {}; + obj[Symbol.isConcatSpreadable] = truthy; + st.equal( + ES.IsConcatSpreadable(obj), + true, + 'an object with ' + debug(truthy) + ' as Symbol.isConcatSpreadable is concat spreadable' + ); + }); + + st.end(); + }); + + forEach(v.objects, function (object) { + t.equal( + ES.IsConcatSpreadable(object), + false, + 'non-array without Symbol.isConcatSpreadable is not concat spreadable' + ); + }); + + t.equal(ES.IsConcatSpreadable([]), true, 'arrays are concat spreadable'); + + t.end(); + }); + + test('Invoke', function (t) { + forEach(v.nonPropertyKeys, function (nonKey) { + t['throws']( + function () { ES.Invoke({}, nonKey); }, + TypeError, + debug(nonKey) + ' is not a Property Key' + ); + }); + + t['throws'](function () { ES.Invoke({ o: false }, 'o'); }, TypeError, 'fails on a non-function'); + + t.test('invoked callback', function (st) { + var aValue = {}; + var bValue = {}; + var obj = { + f: function (a) { + st.equal(arguments.length, 2, '2 args passed'); + st.equal(a, aValue, 'first arg is correct'); + st.equal(arguments[1], bValue, 'second arg is correct'); + } + }; + st.plan(3); + ES.Invoke(obj, 'f', aValue, bValue); + }); + + t.end(); + }); + + test('GetIterator', { skip: true }); + + test('IteratorNext', { skip: true }); + + test('IteratorComplete', { skip: true }); + + test('IteratorValue', { skip: true }); + + test('IteratorStep', { skip: true }); + + test('IteratorClose', { skip: true }); + + test('CreateIterResultObject', function (t) { + forEach(v.nonBooleans, function (nonBoolean) { + t['throws']( + function () { ES.CreateIterResultObject({}, nonBoolean); }, + TypeError, + '"done" argument must be a boolean; ' + debug(nonBoolean) + ' is not' + ); + }); + + var value = {}; + t.deepEqual(ES.CreateIterResultObject(value, true), { + value: value, + done: true + }, 'creates a "done" iteration result'); + t.deepEqual(ES.CreateIterResultObject(value, false), { + value: value, + done: false + }, 'creates a "not done" iteration result'); + + t.end(); + }); + + test('RegExpExec', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.RegExpExec(primitive); }, + TypeError, + '"R" argument must be an object; ' + debug(primitive) + ' is not' + ); + }); + + forEach(v.nonStrings, function (nonString) { + t['throws']( + function () { ES.RegExpExec({}, nonString); }, + TypeError, + '"S" argument must be a String; ' + debug(nonString) + ' is not' + ); + }); + + t.test('gets and calls a callable "exec"', function (st) { + var str = '123'; + var o = { + exec: function (S) { + st.equal(this, o, '"exec" receiver is R'); + st.equal(S, str, '"exec" argument is S'); + + return null; + } + }; + st.plan(2); + ES.RegExpExec(o, str); + st.end(); + }); + + t.test('throws if a callable "exec" returns a non-null non-object', function (st) { + var str = '123'; + st.plan(v.nonNullPrimitives.length); + forEach(v.nonNullPrimitives, function (nonNullPrimitive) { + st['throws']( + function () { ES.RegExpExec({ exec: function () { return nonNullPrimitive; } }, str); }, + TypeError, + '"exec" method must return `null` or an Object; ' + debug(nonNullPrimitive) + ' is not' + ); + }); + st.end(); + }); + + t.test('actual regex that should match against a string', function (st) { + var S = 'aabc'; + var R = /a/g; + var match1 = ES.RegExpExec(R, S); + var match2 = ES.RegExpExec(R, S); + var match3 = ES.RegExpExec(R, S); + st.deepEqual(match1, assign(['a'], groups({ index: 0, input: S })), 'match object 1 is as expected'); + st.deepEqual(match2, assign(['a'], groups({ index: 1, input: S })), 'match object 2 is as expected'); + st.equal(match3, null, 'match 3 is null as expected'); + st.end(); + }); + + t.test('actual regex that should match against a string, with shadowed "exec"', function (st) { + var S = 'aabc'; + var R = /a/g; + R.exec = undefined; + var match1 = ES.RegExpExec(R, S); + var match2 = ES.RegExpExec(R, S); + var match3 = ES.RegExpExec(R, S); + st.deepEqual(match1, assign(['a'], groups({ index: 0, input: S })), 'match object 1 is as expected'); + st.deepEqual(match2, assign(['a'], groups({ index: 1, input: S })), 'match object 2 is as expected'); + st.equal(match3, null, 'match 3 is null as expected'); + st.end(); + }); + t.end(); + }); + + test('ArraySpeciesCreate', function (t) { + t.test('errors', function (st) { + var testNonNumber = function (nonNumber) { + st['throws']( + function () { ES.ArraySpeciesCreate([], nonNumber); }, + TypeError, + debug(nonNumber) + ' is not a number' + ); + }; + forEach(v.nonNumbers, testNonNumber); + + st['throws']( + function () { ES.ArraySpeciesCreate([], -1); }, + TypeError, + '-1 is not >= 0' + ); + st['throws']( + function () { ES.ArraySpeciesCreate([], -Infinity); }, + TypeError, + '-Infinity is not >= 0' + ); + + var testNonIntegers = function (nonInteger) { + st['throws']( + function () { ES.ArraySpeciesCreate([], nonInteger); }, + TypeError, + debug(nonInteger) + ' is not an integer' + ); + }; + forEach(v.nonIntegerNumbers, testNonIntegers); + + st.end(); + }); + + t.test('works with a non-array', function (st) { + forEach(v.objects.concat(v.primitives), function (nonArray) { + var arr = ES.ArraySpeciesCreate(nonArray, 0); + st.ok(ES.IsArray(arr), 'is an array'); + st.equal(arr.length, 0, 'length is correct'); + st.equal(arr.constructor, Array, 'constructor is correct'); + }); + + st.end(); + }); + + t.test('works with a normal array', function (st) { + var len = 2; + var orig = [1, 2, 3]; + var arr = ES.ArraySpeciesCreate(orig, len); + + st.ok(ES.IsArray(arr), 'is an array'); + st.equal(arr.length, len, 'length is correct'); + st.equal(arr.constructor, orig.constructor, 'constructor is correct'); + + st.end(); + }); + + t.test('-0 length produces +0 length', function (st) { + var len = -0; + st.ok(is(len, -0), '-0 is negative zero'); + st.notOk(is(len, 0), '-0 is not positive zero'); + + var orig = [1, 2, 3]; + var arr = ES.ArraySpeciesCreate(orig, len); + + st.equal(ES.IsArray(arr), true); + st.ok(is(arr.length, 0)); + st.equal(arr.constructor, orig.constructor); + + st.end(); + }); + + t.test('works with species construtor', { skip: !hasSpecies }, function (st) { + var sentinel = {}; + var Foo = function Foo(len) { + this.length = len; + this.sentinel = sentinel; + }; + var Bar = getArraySubclassWithSpeciesConstructor(Foo); + var bar = new Bar(); + + t.equal(ES.IsArray(bar), true, 'Bar instance is an array'); + + var arr = ES.ArraySpeciesCreate(bar, 3); + st.equal(arr.constructor, Foo, 'result used species constructor'); + st.equal(arr.length, 3, 'length property is correct'); + st.equal(arr.sentinel, sentinel, 'Foo constructor was exercised'); + + st.end(); + }); + + t.test('works with null species constructor', { skip: !hasSpecies }, function (st) { + var Bar = getArraySubclassWithSpeciesConstructor(null); + var bar = new Bar(); + + t.equal(ES.IsArray(bar), true, 'Bar instance is an array'); + + var arr = ES.ArraySpeciesCreate(bar, 3); + st.equal(arr.constructor, Array, 'result used default constructor'); + st.equal(arr.length, 3, 'length property is correct'); + + st.end(); + }); + + t.test('works with undefined species constructor', { skip: !hasSpecies }, function (st) { + var Bar = getArraySubclassWithSpeciesConstructor(); + var bar = new Bar(); + + t.equal(ES.IsArray(bar), true, 'Bar instance is an array'); + + var arr = ES.ArraySpeciesCreate(bar, 3); + st.equal(arr.constructor, Array, 'result used default constructor'); + st.equal(arr.length, 3, 'length property is correct'); + + st.end(); + }); + + t.test('throws with object non-construtor species constructor', { skip: !hasSpecies }, function (st) { + forEach(v.objects, function (obj) { + var Bar = getArraySubclassWithSpeciesConstructor(obj); + var bar = new Bar(); + + st.equal(ES.IsArray(bar), true, 'Bar instance is an array'); + + st['throws']( + function () { ES.ArraySpeciesCreate(bar, 3); }, + TypeError, + debug(obj) + ' is not a constructor' + ); + }); + + st.end(); + }); + + t.end(); + }); + + test('CreateDataProperty', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.CreateDataProperty(primitive); }, + TypeError, + debug(primitive) + ' is not an object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonPropertyKey) { + t['throws']( + function () { ES.CreateDataProperty({}, nonPropertyKey); }, + TypeError, + debug(nonPropertyKey) + ' is not a property key' + ); + }); + + var sentinel = {}; + forEach(v.propertyKeys, function (propertyKey) { + var obj = {}; + var status = ES.CreateDataProperty(obj, propertyKey, sentinel); + t.equal(status, true, 'status is true'); + t.equal( + obj[propertyKey], + sentinel, + debug(sentinel) + ' is installed on "' + debug(propertyKey) + '" on the object' + ); + + if (typeof Object.defineProperty === 'function') { + var nonWritable = Object.defineProperty({}, propertyKey, { configurable: true, writable: false }); + + var nonWritableStatus = ES.CreateDataProperty(nonWritable, propertyKey, sentinel); + t.equal(nonWritableStatus, false, 'create data property failed'); + t.notEqual( + nonWritable[propertyKey], + sentinel, + debug(sentinel) + ' is not installed on "' + debug(propertyKey) + '" on the object when key is nonwritable' + ); + + var nonConfigurable = Object.defineProperty({}, propertyKey, { configurable: false, writable: true }); + + var nonConfigurableStatus = ES.CreateDataProperty(nonConfigurable, propertyKey, sentinel); + t.equal(nonConfigurableStatus, false, 'create data property failed'); + t.notEqual( + nonConfigurable[propertyKey], + sentinel, + debug(sentinel) + ' is not installed on "' + debug(propertyKey) + '" on the object when key is nonconfigurable' + ); + } + }); + + t.end(); + }); + + test('CreateDataPropertyOrThrow', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.CreateDataPropertyOrThrow(primitive); }, + TypeError, + debug(primitive) + ' is not an object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonPropertyKey) { + t['throws']( + function () { ES.CreateDataPropertyOrThrow({}, nonPropertyKey); }, + TypeError, + debug(nonPropertyKey) + ' is not a property key' + ); + }); + + var sentinel = {}; + forEach(v.propertyKeys, function (propertyKey) { + var obj = {}; + var status = ES.CreateDataPropertyOrThrow(obj, propertyKey, sentinel); + t.equal(status, true, 'status is true'); + t.equal( + obj[propertyKey], + sentinel, + debug(sentinel) + ' is installed on "' + debug(propertyKey) + '" on the object' + ); + + if (typeof Object.preventExtensions === 'function') { + var notExtensible = {}; + Object.preventExtensions(notExtensible); + + t['throws']( + function () { ES.CreateDataPropertyOrThrow(notExtensible, propertyKey, sentinel); }, + TypeError, + 'can not install ' + debug(propertyKey) + ' on non-extensible object' + ); + t.notEqual( + notExtensible[propertyKey], + sentinel, + debug(sentinel) + ' is not installed on "' + debug(propertyKey) + '" on the object' + ); + } + }); + + t.end(); + }); + + test('ObjectCreate', function (t) { + forEach(v.nonNullPrimitives, function (value) { + t['throws']( + function () { ES.ObjectCreate(value); }, + TypeError, + debug(value) + ' is not null, or an object' + ); + }); + + t.test('proto arg', function (st) { + var Parent = function Parent() {}; + Parent.prototype.foo = {}; + var child = ES.ObjectCreate(Parent.prototype); + st.equal(child instanceof Parent, true, 'child is instanceof Parent'); + st.equal(child.foo, Parent.prototype.foo, 'child inherits properties from Parent.prototype'); + + st.end(); + }); + + t.test('internal slots arg', function (st) { + st.doesNotThrow(function () { ES.ObjectCreate(null, []); }, 'an empty slot list is valid'); + + st['throws']( + function () { ES.ObjectCreate(null, ['a']); }, + SyntaxError, + 'internal slots are not supported' + ); + + st.end(); + }); + + t.test('null proto', { skip: !Object.create }, function (st) { + st.equal('toString' in ({}), true, 'normal objects have toString'); + st.equal('toString' in ES.ObjectCreate(null), false, 'makes a null object'); + + st.end(); + }); + + t.test('null proto when no native Object.create', { skip: Object.create }, function (st) { + st['throws']( + function () { ES.ObjectCreate(null); }, + SyntaxError, + 'without a native Object.create, can not create null objects' + ); + + st.end(); + }); + + t.end(); + }); + + test('AdvanceStringIndex', function (t) { + forEach(v.nonStrings, function (nonString) { + t['throws']( + function () { ES.AdvanceStringIndex(nonString); }, + TypeError, + '"S" argument must be a String; ' + debug(nonString) + ' is not' + ); + }); + + var notInts = v.nonNumbers.concat( + v.nonIntegerNumbers, + [Infinity, -Infinity, NaN, [], new Date(), Math.pow(2, 53), -1] + ); + forEach(notInts, function (nonInt) { + t['throws']( + function () { ES.AdvanceStringIndex('abc', nonInt); }, + TypeError, + '"index" argument must be an integer, ' + debug(nonInt) + ' is not.' + ); + }); + + forEach(v.nonBooleans, function (nonBoolean) { + t['throws']( + function () { ES.AdvanceStringIndex('abc', 0, nonBoolean); }, + TypeError, + debug(nonBoolean) + ' is not a Boolean' + ); + }); + + var str = 'a\uD83D\uDCA9c'; + + t.test('non-unicode mode', function (st) { + for (var i = 0; i < str.length + 2; i += 1) { + st.equal(ES.AdvanceStringIndex(str, i, false), i + 1, i + ' advances to ' + (i + 1)); + } + + st.end(); + }); + + t.test('unicode mode', function (st) { + st.equal(ES.AdvanceStringIndex(str, 0, true), 1, '0 advances to 1'); + st.equal(ES.AdvanceStringIndex(str, 1, true), 3, '1 advances to 3'); + st.equal(ES.AdvanceStringIndex(str, 2, true), 3, '2 advances to 3'); + st.equal(ES.AdvanceStringIndex(str, 3, true), 4, '3 advances to 4'); + st.equal(ES.AdvanceStringIndex(str, 4, true), 5, '4 advances to 5'); + + st.end(); + }); + + t.test('lone surrogates', function (st) { + var halfPoo = 'a\uD83Dc'; + + st.equal(ES.AdvanceStringIndex(halfPoo, 0, true), 1, '0 advances to 1'); + st.equal(ES.AdvanceStringIndex(halfPoo, 1, true), 2, '1 advances to 2'); + st.equal(ES.AdvanceStringIndex(halfPoo, 2, true), 3, '2 advances to 3'); + st.equal(ES.AdvanceStringIndex(halfPoo, 3, true), 4, '3 advances to 4'); + + st.end(); + }); + + t.test('surrogate pairs', function (st) { + var lowestPair = String.fromCharCode('0xD800') + String.fromCharCode('0xDC00'); + var highestPair = String.fromCharCode('0xDBFF') + String.fromCharCode('0xDFFF'); + var poop = String.fromCharCode('0xD83D') + String.fromCharCode('0xDCA9'); + + st.equal(ES.AdvanceStringIndex(lowestPair, 0, true), 2, 'lowest surrogate pair, 0 -> 2'); + st.equal(ES.AdvanceStringIndex(highestPair, 0, true), 2, 'highest surrogate pair, 0 -> 2'); + st.equal(ES.AdvanceStringIndex(poop, 0, true), 2, 'poop, 0 -> 2'); + + st.end(); + }); + + t.end(); + }); + + test('CreateMethodProperty', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.CreateMethodProperty(primitive, 'key'); }, + TypeError, + 'O must be an Object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonPropertyKey) { + t['throws']( + function () { ES.CreateMethodProperty({}, nonPropertyKey); }, + TypeError, + debug(nonPropertyKey) + ' is not a Property Key' + ); + }); + + t.test('defines correctly', function (st) { + var obj = {}; + var key = 'the key'; + var value = { foo: 'bar' }; + + st.equal(ES.CreateMethodProperty(obj, key, value), true, 'defines property successfully'); + st.test('property descriptor', { skip: !Object.getOwnPropertyDescriptor }, function (s2t) { + s2t.deepEqual( + Object.getOwnPropertyDescriptor(obj, key), + { + configurable: true, + enumerable: false, + value: value, + writable: true + }, + 'sets the correct property descriptor' + ); + + s2t.end(); + }); + st.equal(obj[key], value, 'sets the correct value'); + + st.end(); + }); + + t.test('fails as expected on a frozen object', { skip: !Object.freeze }, function (st) { + var obj = Object.freeze({ foo: 'bar' }); + st['throws']( + function () { ES.CreateMethodProperty(obj, 'foo', { value: 'baz' }); }, + TypeError, + 'nonconfigurable key can not be defined' + ); + + st.end(); + }); + + var hasNonConfigurableFunctionName = !Object.getOwnPropertyDescriptor + || !Object.getOwnPropertyDescriptor(function () {}, 'name').configurable; + t.test('fails as expected on a function with a nonconfigurable name', { skip: !hasNonConfigurableFunctionName }, function (st) { + st['throws']( + function () { ES.CreateMethodProperty(function () {}, 'name', { value: 'baz' }); }, + TypeError, + 'nonconfigurable function name can not be defined' + ); + st.end(); + }); + + t.end(); + }); + + test('DefinePropertyOrThrow', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.DefinePropertyOrThrow(primitive, 'key', {}); }, + TypeError, + 'O must be an Object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonPropertyKey) { + t['throws']( + function () { ES.DefinePropertyOrThrow({}, nonPropertyKey, {}); }, + TypeError, + debug(nonPropertyKey) + ' is not a Property Key' + ); + }); + + t.test('defines correctly', function (st) { + var obj = {}; + var key = 'the key'; + var descriptor = { + configurable: true, + enumerable: false, + value: { foo: 'bar' }, + writable: true + }; + + st.equal(ES.DefinePropertyOrThrow(obj, key, descriptor), true, 'defines property successfully'); + st.test('property descriptor', { skip: !Object.getOwnPropertyDescriptor }, function (s2t) { + s2t.deepEqual( + Object.getOwnPropertyDescriptor(obj, key), + descriptor, + 'sets the correct property descriptor' + ); + + s2t.end(); + }); + st.deepEqual(obj[key], descriptor.value, 'sets the correct value'); + + st.end(); + }); + + t.test('fails as expected on a frozen object', { skip: !Object.freeze }, function (st) { + var obj = Object.freeze({ foo: 'bar' }); + st['throws']( + function () { + ES.DefinePropertyOrThrow(obj, 'foo', { configurable: true, value: 'baz' }); + }, + TypeError, + 'nonconfigurable key can not be defined' + ); + + st.end(); + }); + + var hasNonConfigurableFunctionName = !Object.getOwnPropertyDescriptor + || !Object.getOwnPropertyDescriptor(function () {}, 'name').configurable; + t.test('fails as expected on a function with a nonconfigurable name', { skip: !hasNonConfigurableFunctionName }, function (st) { + st['throws']( + function () { + ES.DefinePropertyOrThrow(function () {}, 'name', { configurable: true, value: 'baz' }); + }, + TypeError, + 'nonconfigurable function name can not be defined' + ); + st.end(); + }); + + t.end(); + }); + + test('DeletePropertyOrThrow', function (t) { + forEach(v.primitives, function (primitive) { + t['throws']( + function () { ES.DeletePropertyOrThrow(primitive, 'key', {}); }, + TypeError, + 'O must be an Object' + ); + }); + + forEach(v.nonPropertyKeys, function (nonPropertyKey) { + t['throws']( + function () { ES.DeletePropertyOrThrow({}, nonPropertyKey, {}); }, + TypeError, + debug(nonPropertyKey) + ' is not a Property Key' + ); + }); + + t.test('defines correctly', function (st) { + var obj = { 'the key': 42 }; + var key = 'the key'; + + st.equal(ES.DeletePropertyOrThrow(obj, key), true, 'deletes property successfully'); + st.equal(key in obj, false, 'key is no longer in the object'); + + st.end(); + }); + + t.test('fails as expected on a frozen object', { skip: !Object.freeze }, function (st) { + var obj = Object.freeze({ foo: 'bar' }); + st['throws']( + function () { ES.DeletePropertyOrThrow(obj, 'foo'); }, + TypeError, + 'nonconfigurable key can not be deleted' + ); + + st.end(); + }); + + var hasNonConfigurableFunctionName = !Object.getOwnPropertyDescriptor + || !Object.getOwnPropertyDescriptor(function () {}, 'name').configurable; + t.test('fails as expected on a function with a nonconfigurable name', { skip: !hasNonConfigurableFunctionName }, function (st) { + st['throws']( + function () { ES.DeletePropertyOrThrow(function () {}, 'name'); }, + TypeError, + 'nonconfigurable function name can not be deleted' + ); + st.end(); + }); + + t.end(); + }); + + test('EnumerableOwnNames', { skip: skips && skips.EnumerableOwnNames }, function (t) { + var obj = testEnumerableOwnNames(t, function (O) { return ES.EnumerableOwnNames(O); }); + + t.deepEqual( + ES.EnumerableOwnNames(obj), + ['own'], + 'returns enumerable own names' + ); + + t.end(); + }); + + test('thisNumberValue', function (t) { + forEach(v.nonNumbers, function (nonNumber) { + t['throws']( + function () { ES.thisNumberValue(nonNumber); }, + TypeError, + debug(nonNumber) + ' is not a Number' + ); + }); + + forEach(v.numbers, function (number) { + t.equal(ES.thisNumberValue(number), number, debug(number) + ' is its own thisNumberValue'); + var obj = Object(number); + t.equal(ES.thisNumberValue(obj), number, debug(obj) + ' is the boxed thisNumberValue'); + }); + + t.end(); + }); + + test('thisBooleanValue', function (t) { + forEach(v.nonBooleans, function (nonBoolean) { + t['throws']( + function () { ES.thisBooleanValue(nonBoolean); }, + TypeError, + debug(nonBoolean) + ' is not a Boolean' + ); + }); + + forEach(v.booleans, function (boolean) { + t.equal(ES.thisBooleanValue(boolean), boolean, debug(boolean) + ' is its own thisBooleanValue'); + var obj = Object(boolean); + t.equal(ES.thisBooleanValue(obj), boolean, debug(obj) + ' is the boxed thisBooleanValue'); + }); + + t.end(); + }); + + test('thisStringValue', function (t) { + forEach(v.nonStrings, function (nonString) { + t['throws']( + function () { ES.thisStringValue(nonString); }, + TypeError, + debug(nonString) + ' is not a String' + ); + }); + + forEach(v.strings, function (string) { + t.equal(ES.thisStringValue(string), string, debug(string) + ' is its own thisStringValue'); + var obj = Object(string); + t.equal(ES.thisStringValue(obj), string, debug(obj) + ' is the boxed thisStringValue'); + }); + + t.end(); + }); + + test('thisTimeValue', function (t) { + forEach(v.primitives.concat(v.objects), function (nonDate) { + t['throws']( + function () { ES.thisTimeValue(nonDate); }, + TypeError, + debug(nonDate) + ' is not a Date' + ); + }); + + forEach(v.timestamps, function (timestamp) { + var date = new Date(timestamp); + + t.equal(ES.thisTimeValue(date), timestamp, debug(date) + ' is its own thisTimeValue'); + }); + + t.end(); + }); +}; + +var es2016 = function ES2016(ES, ops, expectedMissing, skips) { + es2015(ES, ops, expectedMissing, skips); + + test('SameValueNonNumber', function (t) { + var willThrow = [ + [3, 4], + [NaN, 4], + [4, ''], + ['abc', true], + [{}, false] + ]; + forEach(willThrow, function (nums) { + t['throws'](function () { return ES.SameValueNonNumber.apply(ES, nums); }, TypeError, 'value must be same type and non-number'); + }); + + forEach(v.objects.concat(v.nonNumberPrimitives), function (val) { + t.equal(val === val, ES.SameValueNonNumber(val, val), debug(val) + ' is SameValueNonNumber to itself'); + }); + + t.end(); + }); +}; + +var es2017 = function ES2017(ES, ops, expectedMissing, skips) { + es2016(ES, ops, expectedMissing, assign({}, skips, { + EnumerableOwnNames: true + })); + + test('ToIndex', function (t) { + t.ok(is(ES.ToIndex(), 0), 'no value gives 0'); + t.ok(is(ES.ToIndex(undefined), 0), 'undefined value gives 0'); + + t['throws'](function () { ES.ToIndex(-1); }, RangeError, 'negative numbers throw'); + + t['throws'](function () { ES.ToIndex(MAX_SAFE_INTEGER + 1); }, RangeError, 'too large numbers throw'); + + t.equal(ES.ToIndex(3), 3, 'numbers work'); + t.equal(ES.ToIndex(v.valueOfOnlyObject), 4, 'coercible objects are coerced'); + + t.end(); + }); + + test('EnumerableOwnProperties', { skip: skips && skips.EnumerableOwnProperties }, function (t) { + var obj = testEnumerableOwnNames(t, function (O) { + return ES.EnumerableOwnProperties(O, 'key'); + }); + + t.deepEqual( + ES.EnumerableOwnProperties(obj, 'value'), + [obj.own], + 'returns enumerable own values' + ); + + t.deepEqual( + ES.EnumerableOwnProperties(obj, 'key+value'), + [['own', obj.own]], + 'returns enumerable own entries' + ); + + t.end(); + }); +}; + +var es2018 = function ES2018(ES, ops, expectedMissing, skips) { + es2017(ES, ops, expectedMissing, assign({}, skips, { + EnumerableOwnProperties: true, + IsPropertyDescriptor: true + })); + + test('thisSymbolValue', function (t) { + forEach(v.nonSymbolPrimitives.concat(v.objects), function (nonSymbol) { + t['throws']( + function () { ES.thisSymbolValue(nonSymbol); }, + v.hasSymbols ? TypeError : SyntaxError, + debug(nonSymbol) + ' is not a Symbol' + ); + }); + + t.test('no native Symbols', { skip: v.hasSymbols }, function (st) { + forEach(v.objects.concat(v.primitives), function (value) { + st['throws']( + function () { ES.thisSymbolValue(value); }, + SyntaxError, + 'Symbols are not supported' + ); + }); + st.end(); + }); + + t.test('symbol values', { skip: !v.hasSymbols }, function (st) { + forEach(v.symbols, function (symbol) { + st.equal(ES.thisSymbolValue(symbol), symbol, 'Symbol value of ' + debug(symbol) + ' is same symbol'); + + st.equal( + ES.thisSymbolValue(Object(symbol)), + symbol, + 'Symbol value of ' + debug(Object(symbol)) + ' is ' + debug(symbol) + ); + }); + + st.end(); + }); + + t.end(); + }); + + test('IsStringPrefix', function (t) { + forEach(v.nonStrings, function (nonString) { + t['throws']( + function () { ES.IsStringPrefix(nonString, 'a'); }, + TypeError, + 'first arg: ' + debug(nonString) + ' is not a string' + ); + t['throws']( + function () { ES.IsStringPrefix('a', nonString); }, + TypeError, + 'second arg: ' + debug(nonString) + ' is not a string' + ); + }); + + forEach(v.strings, function (string) { + t.equal(ES.IsStringPrefix(string, string), true, debug(string) + ' is a prefix of itself'); + + t.equal(ES.IsStringPrefix('', string), true, 'the empty string is a prefix of everything'); + }); + + t.equal(ES.IsStringPrefix('abc', 'abcd'), true, '"abc" is a prefix of "abcd"'); + t.equal(ES.IsStringPrefix('abcd', 'abc'), false, '"abcd" is not a prefix of "abc"'); + + t.equal(ES.IsStringPrefix('a', 'bc'), false, '"a" is not a prefix of "bc"'); + + t.end(); + }); + + test('NumberToString', function (t) { + forEach(v.nonNumbers, function (nonNumber) { + t['throws']( + function () { ES.NumberToString(nonNumber); }, + TypeError, + debug(nonNumber) + ' is not a Number' + ); + }); + + forEach(v.numbers, function (number) { + t.equal(ES.NumberToString(number), String(number), debug(number) + ' stringifies to ' + number); + }); + + t.end(); + }); + + test('CopyDataProperties', function (t) { + t.test('first argument: target', function (st) { + forEach(v.primitives, function (primitive) { + st['throws']( + function () { ES.CopyDataProperties(primitive, {}, []); }, + TypeError, + debug(primitive) + ' is not an Object' + ); + }); + st.end(); + }); + + t.test('second argument: source', function (st) { + var frozenTarget = Object.freeze ? Object.freeze({}) : {}; + forEach(v.nullPrimitives, function (nullish) { + st.equal( + ES.CopyDataProperties(frozenTarget, nullish, []), + frozenTarget, + debug(nullish) + ' "source" yields identical, unmodified target' + ); + }); + + forEach(v.nonNullPrimitives, function (objectCoercible) { + var target = {}; + var result = ES.CopyDataProperties(target, objectCoercible, []); + st.equal(result, target, 'result === target'); + st.deepEqual(keys(result), keys(Object(objectCoercible)), 'target ends up with keys of ' + debug(objectCoercible)); + }); + + st.end(); + }); + + t.test('third argument: excludedItems', function (st) { + forEach(v.objects.concat(v.primitives), function (nonArray) { + st['throws']( + function () { ES.CopyDataProperties({}, {}, nonArray); }, + TypeError, + debug(nonArray) + ' is not an Array' + ); + }); + + forEach(v.nonPropertyKeys, function (nonPropertyKey) { + st['throws']( + function () { ES.CopyDataProperties({}, {}, [nonPropertyKey]); }, + TypeError, + debug(nonPropertyKey) + ' is not a Property Key' + ); + }); + + var result = ES.CopyDataProperties({}, { a: 1, b: 2, c: 3 }, ['b']); + st.deepEqual(keys(result), ['a', 'c'], 'excluded string keys are excluded'); + + st.test('excluding symbols', { skip: !v.hasSymbols }, function (s2t) { + var source = {}; + forEach(v.symbols, function (symbol) { + source[symbol] = true; + }); + + var includedSymbols = v.symbols.slice(1); + var excludedSymbols = v.symbols.slice(0, 1); + var target = ES.CopyDataProperties({}, source, excludedSymbols); + + forEach(includedSymbols, function (symbol) { + s2t.equal(has(target, symbol), true, debug(symbol) + ' is included'); + }); + + forEach(excludedSymbols, function (symbol) { + s2t.equal(has(target, symbol), false, debug(symbol) + ' is excluded'); + }); + + s2t.end(); + }); + + st.end(); + }); + + t.end(); + }); + + test('PromiseResolve', function (t) { + t.test('Promises unsupported', { skip: typeof Promise === 'function' }, function (st) { + st['throws']( + function () { ES.PromiseResolve(); }, + SyntaxError, + 'Promises are not supported' + ); + st.end(); + }); + + t.test('Promises supported', { skip: typeof Promise !== 'function' }, function (st) { + st.plan(2); + + var a = {}; + var b = {}; + var fulfilled = Promise.resolve(a); + var rejected = Promise.reject(b); + + ES.PromiseResolve(Promise, fulfilled).then(function (x) { + st.equal(x, a, 'fulfilled promise resolves to fulfilled'); + }); + + ES.PromiseResolve(Promise, rejected)['catch'](function (e) { + st.equal(e, b, 'rejected promise resolves to rejected'); + }); + }); + + t.end(); + }); + + test('EnumerableOwnPropertyNames', { skip: skips && skips.EnumerableOwnPropertyNames }, function (t) { + var obj = testEnumerableOwnNames(t, function (O) { + return ES.EnumerableOwnPropertyNames(O, 'key'); + }); + + t.deepEqual( + ES.EnumerableOwnPropertyNames(obj, 'value'), + [obj.own], + 'returns enumerable own values' + ); + + t.deepEqual( + ES.EnumerableOwnPropertyNames(obj, 'key+value'), + [['own', obj.own]], + 'returns enumerable own entries' + ); + + t.end(); + }); +}; + +module.exports = { + es2015: es2015, + es2016: es2016, + es2017: es2017, + es2018: es2018 +}; diff --git a/WebServer/node_modules/es-to-primitive/.jscs.json b/WebServer/node_modules/es-to-primitive/.jscs.json new file mode 100644 index 000000000..8666c750d --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/.jscs.json @@ -0,0 +1,176 @@ +{ + "es3": true, + + "additionalRules": [], + + "requireSemicolons": true, + + "disallowMultipleSpaces": true, + + "disallowIdentifierNames": [], + + "requireCurlyBraces": { + "allExcept": [], + "keywords": ["if", "else", "for", "while", "do", "try", "catch"] + }, + + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"], + + "disallowSpaceAfterKeywords": [], + + "disallowSpaceBeforeComma": true, + "disallowSpaceAfterComma": false, + "disallowSpaceBeforeSemicolon": true, + + "disallowNodeTypes": [ + "DebuggerStatement", + "ForInStatement", + "LabeledStatement", + "SwitchCase", + "SwitchStatement", + "WithStatement" + ], + + "requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] }, + + "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true }, + "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true }, + "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true }, + "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true }, + "disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true }, + + "requireSpaceBetweenArguments": true, + + "disallowSpacesInsideParentheses": true, + + "disallowSpacesInsideArrayBrackets": true, + + "disallowQuotedKeysInObjects": { "allExcept": ["reserved"] }, + + "disallowSpaceAfterObjectKeys": true, + + "requireCommaBeforeLineBreak": true, + + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "requireSpaceAfterPrefixUnaryOperators": [], + + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "requireSpaceBeforePostfixUnaryOperators": [], + + "disallowSpaceBeforeBinaryOperators": [], + "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + + "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + "disallowSpaceAfterBinaryOperators": [], + + "disallowImplicitTypeConversion": ["binary", "string"], + + "disallowKeywords": ["with", "eval"], + + "requireKeywordsOnNewLine": [], + "disallowKeywordsOnNewLine": ["else"], + + "requireLineFeedAtFileEnd": true, + + "disallowTrailingWhitespace": true, + + "disallowTrailingComma": true, + + "excludeFiles": ["node_modules/**", "vendor/**"], + + "disallowMultipleLineStrings": true, + + "requireDotNotation": { "allExcept": ["keywords"] }, + + "requireParenthesesAroundIIFE": true, + + "validateLineBreaks": "LF", + + "validateQuoteMarks": { + "escape": true, + "mark": "'" + }, + + "disallowOperatorBeforeLineBreak": [], + + "requireSpaceBeforeKeywords": [ + "do", + "for", + "if", + "else", + "switch", + "case", + "try", + "catch", + "finally", + "while", + "with", + "return" + ], + + "validateAlignedFunctionParameters": { + "lineBreakAfterOpeningBraces": true, + "lineBreakBeforeClosingBraces": true + }, + + "requirePaddingNewLinesBeforeExport": true, + + "validateNewlineAfterArrayElements": { + "maximum": 12 + }, + + "requirePaddingNewLinesAfterUseStrict": true, + + "disallowArrowFunctions": true, + + "disallowMultiLineTernary": true, + + "validateOrderInObjectKeys": false, + + "disallowIdenticalDestructuringNames": true, + + "disallowNestedTernaries": { "maxLevel": 1 }, + + "requireSpaceAfterComma": { "allExcept": ["trailing"] }, + "requireAlignedMultilineParams": false, + + "requireSpacesInGenerator": { + "afterStar": true + }, + + "disallowSpacesInGenerator": { + "beforeStar": true + }, + + "disallowVar": false, + + "requireArrayDestructuring": false, + + "requireEnhancedObjectLiterals": false, + + "requireObjectDestructuring": false, + + "requireEarlyReturn": false, + + "requireCapitalizedConstructorsNew": { + "allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array", "GetMethod"] + }, + + "requireImportAlphabetized": false, + + "requireSpaceBeforeObjectValues": true, + "requireSpaceBeforeDestructuredValues": true, + + "disallowSpacesInsideTemplateStringPlaceholders": true, + + "disallowArrayDestructuringReturn": false, + + "requireNewlineBeforeSingleStatementsInIf": false, + + "disallowUnusedVariables": true, + + "requireSpacesInsideImportedObjectBraces": true, + + "requireUseStrict": true +} + diff --git a/WebServer/node_modules/es-to-primitive/es2015.js b/WebServer/node_modules/es-to-primitive/es2015.js new file mode 100644 index 000000000..4a11a346c --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/es2015.js @@ -0,0 +1,75 @@ +'use strict'; + +var hasSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'; + +var isPrimitive = require('./helpers/isPrimitive'); +var isCallable = require('is-callable'); +var isDate = require('is-date-object'); +var isSymbol = require('is-symbol'); + +var ordinaryToPrimitive = function OrdinaryToPrimitive(O, hint) { + if (typeof O === 'undefined' || O === null) { + throw new TypeError('Cannot call method on ' + O); + } + if (typeof hint !== 'string' || (hint !== 'number' && hint !== 'string')) { + throw new TypeError('hint must be "string" or "number"'); + } + var methodNames = hint === 'string' ? ['toString', 'valueOf'] : ['valueOf', 'toString']; + var method, result, i; + for (i = 0; i < methodNames.length; ++i) { + method = O[methodNames[i]]; + if (isCallable(method)) { + result = method.call(O); + if (isPrimitive(result)) { + return result; + } + } + } + throw new TypeError('No default value'); +}; + +var GetMethod = function GetMethod(O, P) { + var func = O[P]; + if (func !== null && typeof func !== 'undefined') { + if (!isCallable(func)) { + throw new TypeError(func + ' returned for property ' + P + ' of object ' + O + ' is not a function'); + } + return func; + } + return void 0; +}; + +// http://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive +module.exports = function ToPrimitive(input) { + if (isPrimitive(input)) { + return input; + } + var hint = 'default'; + if (arguments.length > 1) { + if (arguments[1] === String) { + hint = 'string'; + } else if (arguments[1] === Number) { + hint = 'number'; + } + } + + var exoticToPrim; + if (hasSymbols) { + if (Symbol.toPrimitive) { + exoticToPrim = GetMethod(input, Symbol.toPrimitive); + } else if (isSymbol(input)) { + exoticToPrim = Symbol.prototype.valueOf; + } + } + if (typeof exoticToPrim !== 'undefined') { + var result = exoticToPrim.call(input, hint); + if (isPrimitive(result)) { + return result; + } + throw new TypeError('unable to convert exotic object to primitive'); + } + if (hint === 'default' && (isDate(input) || isSymbol(input))) { + hint = 'string'; + } + return ordinaryToPrimitive(input, hint === 'default' ? 'number' : hint); +}; diff --git a/WebServer/node_modules/es-to-primitive/es5.js b/WebServer/node_modules/es-to-primitive/es5.js new file mode 100644 index 000000000..602aa362c --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/es5.js @@ -0,0 +1,45 @@ +'use strict'; + +var toStr = Object.prototype.toString; + +var isPrimitive = require('./helpers/isPrimitive'); + +var isCallable = require('is-callable'); + +// http://ecma-international.org/ecma-262/5.1/#sec-8.12.8 +var ES5internalSlots = { + '[[DefaultValue]]': function (O) { + var actualHint; + if (arguments.length > 1) { + actualHint = arguments[1]; + } else { + actualHint = toStr.call(O) === '[object Date]' ? String : Number; + } + + if (actualHint === String || actualHint === Number) { + var methods = actualHint === String ? ['toString', 'valueOf'] : ['valueOf', 'toString']; + var value, i; + for (i = 0; i < methods.length; ++i) { + if (isCallable(O[methods[i]])) { + value = O[methods[i]](); + if (isPrimitive(value)) { + return value; + } + } + } + throw new TypeError('No default value'); + } + throw new TypeError('invalid [[DefaultValue]] hint supplied'); + } +}; + +// http://ecma-international.org/ecma-262/5.1/#sec-9.1 +module.exports = function ToPrimitive(input) { + if (isPrimitive(input)) { + return input; + } + if (arguments.length > 1) { + return ES5internalSlots['[[DefaultValue]]'](input, arguments[1]); + } + return ES5internalSlots['[[DefaultValue]]'](input); +}; diff --git a/WebServer/node_modules/es-to-primitive/es6.js b/WebServer/node_modules/es-to-primitive/es6.js new file mode 100644 index 000000000..2d1f4dc92 --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/es6.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./es2015'); diff --git a/WebServer/node_modules/es-to-primitive/helpers/isPrimitive.js b/WebServer/node_modules/es-to-primitive/helpers/isPrimitive.js new file mode 100644 index 000000000..366915645 --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/helpers/isPrimitive.js @@ -0,0 +1,3 @@ +module.exports = function isPrimitive(value) { + return value === null || (typeof value !== 'function' && typeof value !== 'object'); +}; diff --git a/WebServer/node_modules/es-to-primitive/index.js b/WebServer/node_modules/es-to-primitive/index.js new file mode 100644 index 000000000..e60d912e1 --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var ES5 = require('./es5'); +var ES6 = require('./es6'); +var ES2015 = require('./es2015'); + +if (Object.defineProperty) { + Object.defineProperty(ES2015, 'ES5', { enumerable: false, value: ES5 }); + Object.defineProperty(ES2015, 'ES6', { enumerable: false, value: ES6 }); + Object.defineProperty(ES2015, 'ES2015', { enumerable: false, value: ES2015 }); +} else { + ES6.ES5 = ES5; + ES6.ES6 = ES6; + ES6.ES2015 = ES2015; +} + +module.exports = ES2015; diff --git a/WebServer/node_modules/es-to-primitive/package.json b/WebServer/node_modules/es-to-primitive/package.json new file mode 100644 index 000000000..877803e5f --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/package.json @@ -0,0 +1,116 @@ +{ + "_args": [ + [ + "es-to-primitive@1.2.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "es-to-primitive@1.2.0", + "_id": "es-to-primitive@1.2.0", + "_inBundle": false, + "_integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "_location": "/es-to-primitive", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "es-to-primitive@1.2.0", + "name": "es-to-primitive", + "escapedName": "es-to-primitive", + "rawSpec": "1.2.0", + "saveSpec": null, + "fetchSpec": "1.2.0" + }, + "_requiredBy": [ + "/es-abstract" + ], + "_resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "_spec": "1.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jordan Harband" + }, + "bugs": { + "url": "https://github.com/ljharb/es-to-primitive/issues" + }, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "description": "ECMAScript “ToPrimitiveâ€� algorithm. Provides ES5 and ES2015 versions.", + "devDependencies": { + "@ljharb/eslint-config": "^13.0.0", + "covert": "^1.1.0", + "eslint": "^5.6.0", + "foreach": "^2.0.5", + "function.prototype.name": "^1.1.0", + "jscs": "^3.0.7", + "nsp": "^3.2.1", + "object-inspect": "^1.6.0", + "object-is": "^1.0.1", + "replace": "^1.0.0", + "semver": "^5.5.1", + "tape": "^4.9.1" + }, + "engines": { + "node": ">= 0.4" + }, + "homepage": "https://github.com/ljharb/es-to-primitive#readme", + "keywords": [ + "primitive", + "abstract", + "ecmascript", + "es5", + "es6", + "es2015", + "toPrimitive", + "coerce", + "type", + "object", + "string", + "number", + "boolean", + "symbol", + "null", + "undefined" + ], + "license": "MIT", + "main": "index.js", + "name": "es-to-primitive", + "repository": { + "type": "git", + "url": "git://github.com/ljharb/es-to-primitive.git" + }, + "scripts": { + "coverage": "covert test/*.js", + "coverage-quiet": "covert test/*.js --quiet", + "eslint": "eslint test/*.js *.js", + "jscs": "jscs test/*.js *.js", + "lint": "npm run --silent jscs && npm run --silent eslint", + "posttest": "npm run --silent security", + "pretest": "npm run --silent lint", + "security": "nsp check", + "test": "npm run --silent tests-only", + "tests-only": "node --es-staging test" + }, + "testling": { + "files": "test", + "browsers": [ + "iexplore/6.0..latest", + "firefox/3.0..6.0", + "firefox/15.0..latest", + "firefox/nightly", + "chrome/4.0..10.0", + "chrome/20.0..latest", + "chrome/canary", + "opera/10.0..latest", + "opera/next", + "safari/4.0..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2" + ] + }, + "version": "1.2.0" +} diff --git a/WebServer/node_modules/es-to-primitive/test/es2015.js b/WebServer/node_modules/es-to-primitive/test/es2015.js new file mode 100644 index 000000000..80f4083dd --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/test/es2015.js @@ -0,0 +1,151 @@ +'use strict'; + +var test = require('tape'); +var toPrimitive = require('../es2015'); +var is = require('object-is'); +var forEach = require('foreach'); +var functionName = require('function.prototype.name'); +var debug = require('object-inspect'); + +var hasSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'; +var hasSymbolToPrimitive = hasSymbols && typeof Symbol.toPrimitive === 'symbol'; + +test('function properties', function (t) { + t.equal(toPrimitive.length, 1, 'length is 1'); + t.equal(functionName(toPrimitive), 'ToPrimitive', 'name is ToPrimitive'); + + t.end(); +}); + +var primitives = [null, undefined, true, false, 0, -0, 42, NaN, Infinity, -Infinity, '', 'abc']; + +test('primitives', function (t) { + forEach(primitives, function (i) { + t.ok(is(toPrimitive(i), i), 'toPrimitive(' + debug(i) + ') returns the same value'); + t.ok(is(toPrimitive(i, String), i), 'toPrimitive(' + debug(i) + ', String) returns the same value'); + t.ok(is(toPrimitive(i, Number), i), 'toPrimitive(' + debug(i) + ', Number) returns the same value'); + }); + t.end(); +}); + +test('Symbols', { skip: !hasSymbols }, function (t) { + var symbols = [ + Symbol('foo'), + Symbol.iterator, + Symbol['for']('foo') // eslint-disable-line no-restricted-properties + ]; + forEach(symbols, function (sym) { + t.equal(toPrimitive(sym), sym, 'toPrimitive(' + debug(sym) + ') returns the same value'); + t.equal(toPrimitive(sym, String), sym, 'toPrimitive(' + debug(sym) + ', String) returns the same value'); + t.equal(toPrimitive(sym, Number), sym, 'toPrimitive(' + debug(sym) + ', Number) returns the same value'); + }); + + var primitiveSym = Symbol('primitiveSym'); + var objectSym = Object(primitiveSym); + t.equal(toPrimitive(objectSym), primitiveSym, 'toPrimitive(' + debug(objectSym) + ') returns ' + debug(primitiveSym)); + t.equal(toPrimitive(objectSym, String), primitiveSym, 'toPrimitive(' + debug(objectSym) + ', String) returns ' + debug(primitiveSym)); + t.equal(toPrimitive(objectSym, Number), primitiveSym, 'toPrimitive(' + debug(objectSym) + ', Number) returns ' + debug(primitiveSym)); + t.end(); +}); + +test('Arrays', function (t) { + var arrays = [[], ['a', 'b'], [1, 2]]; + forEach(arrays, function (arr) { + t.equal(toPrimitive(arr), String(arr), 'toPrimitive(' + debug(arr) + ') returns the string version of the array'); + t.equal(toPrimitive(arr, String), String(arr), 'toPrimitive(' + debug(arr) + ') returns the string version of the array'); + t.equal(toPrimitive(arr, Number), String(arr), 'toPrimitive(' + debug(arr) + ') returns the string version of the array'); + }); + t.end(); +}); + +test('Dates', function (t) { + var dates = [new Date(), new Date(0), new Date(NaN)]; + forEach(dates, function (date) { + t.equal(toPrimitive(date), String(date), 'toPrimitive(' + debug(date) + ') returns the string version of the date'); + t.equal(toPrimitive(date, String), String(date), 'toPrimitive(' + debug(date) + ') returns the string version of the date'); + t.ok(is(toPrimitive(date, Number), Number(date)), 'toPrimitive(' + debug(date) + ') returns the number version of the date'); + }); + t.end(); +}); + +var coercibleObject = { valueOf: function () { return 3; }, toString: function () { return 42; } }; +var valueOfOnlyObject = { valueOf: function () { return 4; }, toString: function () { return {}; } }; +var toStringOnlyObject = { valueOf: function () { return {}; }, toString: function () { return 7; } }; +var coercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return 42; } +}; +var uncoercibleObject = { valueOf: function () { return {}; }, toString: function () { return {}; } }; +var uncoercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return function toStrFn() {}; } +}; + +test('Objects', function (t) { + t.equal(toPrimitive(coercibleObject), coercibleObject.valueOf(), 'coercibleObject with no hint coerces to valueOf'); + t.equal(toPrimitive(coercibleObject, Number), coercibleObject.valueOf(), 'coercibleObject with hint Number coerces to valueOf'); + t.equal(toPrimitive(coercibleObject, String), coercibleObject.toString(), 'coercibleObject with hint String coerces to non-stringified toString'); + + t.equal(toPrimitive(coercibleFnObject), coercibleFnObject.toString(), 'coercibleFnObject coerces to non-stringified toString'); + t.equal(toPrimitive(coercibleFnObject, Number), coercibleFnObject.toString(), 'coercibleFnObject with hint Number coerces to non-stringified toString'); + t.equal(toPrimitive(coercibleFnObject, String), coercibleFnObject.toString(), 'coercibleFnObject with hint String coerces to non-stringified toString'); + + t.equal(toPrimitive({}), '[object Object]', '{} with no hint coerces to Object#toString'); + t.equal(toPrimitive({}, Number), '[object Object]', '{} with hint Number coerces to Object#toString'); + t.equal(toPrimitive({}, String), '[object Object]', '{} with hint String coerces to Object#toString'); + + t.equal(toPrimitive(toStringOnlyObject), toStringOnlyObject.toString(), 'toStringOnlyObject returns non-stringified toString'); + t.equal(toPrimitive(toStringOnlyObject, Number), toStringOnlyObject.toString(), 'toStringOnlyObject with hint Number returns non-stringified toString'); + t.equal(toPrimitive(toStringOnlyObject, String), toStringOnlyObject.toString(), 'toStringOnlyObject with hint String returns non-stringified toString'); + + t.equal(toPrimitive(valueOfOnlyObject), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject returns valueOf'); + t.equal(toPrimitive(valueOfOnlyObject, Number), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject with hint Number returns valueOf'); + t.equal(toPrimitive(valueOfOnlyObject, String), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject with hint String returns non-stringified valueOf'); + + t.test('Symbol.toPrimitive', { skip: !hasSymbolToPrimitive }, function (st) { + var overriddenObject = { toString: st.fail, valueOf: st.fail }; + overriddenObject[Symbol.toPrimitive] = function (hint) { return String(hint); }; + + st.equal(toPrimitive(overriddenObject), 'default', 'object with Symbol.toPrimitive + no hint invokes that'); + st.equal(toPrimitive(overriddenObject, Number), 'number', 'object with Symbol.toPrimitive + hint Number invokes that'); + st.equal(toPrimitive(overriddenObject, String), 'string', 'object with Symbol.toPrimitive + hint String invokes that'); + + var nullToPrimitive = { toString: coercibleObject.toString, valueOf: coercibleObject.valueOf }; + nullToPrimitive[Symbol.toPrimitive] = null; + st.equal(toPrimitive(nullToPrimitive), toPrimitive(coercibleObject), 'object with no hint + null Symbol.toPrimitive ignores it'); + st.equal(toPrimitive(nullToPrimitive, Number), toPrimitive(coercibleObject, Number), 'object with hint Number + null Symbol.toPrimitive ignores it'); + st.equal(toPrimitive(nullToPrimitive, String), toPrimitive(coercibleObject, String), 'object with hint String + null Symbol.toPrimitive ignores it'); + + st.test('exceptions', function (sst) { + var nonFunctionToPrimitive = { toString: sst.fail, valueOf: sst.fail }; + nonFunctionToPrimitive[Symbol.toPrimitive] = {}; + sst['throws'](toPrimitive.bind(null, nonFunctionToPrimitive), TypeError, 'Symbol.toPrimitive returning a non-function throws'); + + var uncoercibleToPrimitive = { toString: sst.fail, valueOf: sst.fail }; + uncoercibleToPrimitive[Symbol.toPrimitive] = function (hint) { + return { toString: function () { return hint; } }; + }; + sst['throws'](toPrimitive.bind(null, uncoercibleToPrimitive), TypeError, 'Symbol.toPrimitive returning an object throws'); + + var throwingToPrimitive = { toString: sst.fail, valueOf: sst.fail }; + throwingToPrimitive[Symbol.toPrimitive] = function (hint) { throw new RangeError(hint); }; + sst['throws'](toPrimitive.bind(null, throwingToPrimitive), RangeError, 'Symbol.toPrimitive throwing throws'); + + sst.end(); + }); + + st.end(); + }); + + t.test('exceptions', function (st) { + st['throws'](toPrimitive.bind(null, uncoercibleObject), TypeError, 'uncoercibleObject throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleObject, Number), TypeError, 'uncoercibleObject with hint Number throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleObject, String), TypeError, 'uncoercibleObject with hint String throws a TypeError'); + + st['throws'](toPrimitive.bind(null, uncoercibleFnObject), TypeError, 'uncoercibleFnObject throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleFnObject, Number), TypeError, 'uncoercibleFnObject with hint Number throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleFnObject, String), TypeError, 'uncoercibleFnObject with hint String throws a TypeError'); + st.end(); + }); + t.end(); +}); diff --git a/WebServer/node_modules/es-to-primitive/test/es5.js b/WebServer/node_modules/es-to-primitive/test/es5.js new file mode 100644 index 000000000..8b80ff5bd --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/test/es5.js @@ -0,0 +1,94 @@ +'use strict'; + +var test = require('tape'); +var toPrimitive = require('../es5'); +var is = require('object-is'); +var forEach = require('foreach'); +var functionName = require('function.prototype.name'); +var debug = require('object-inspect'); + +test('function properties', function (t) { + t.equal(toPrimitive.length, 1, 'length is 1'); + t.equal(functionName(toPrimitive), 'ToPrimitive', 'name is ToPrimitive'); + + t.end(); +}); + +var primitives = [null, undefined, true, false, 0, -0, 42, NaN, Infinity, -Infinity, '', 'abc']; + +test('primitives', function (t) { + forEach(primitives, function (i) { + t.ok(is(toPrimitive(i), i), 'toPrimitive(' + debug(i) + ') returns the same value'); + t.ok(is(toPrimitive(i, String), i), 'toPrimitive(' + debug(i) + ', String) returns the same value'); + t.ok(is(toPrimitive(i, Number), i), 'toPrimitive(' + debug(i) + ', Number) returns the same value'); + }); + t.end(); +}); + +test('Arrays', function (t) { + var arrays = [[], ['a', 'b'], [1, 2]]; + forEach(arrays, function (arr) { + t.ok(is(toPrimitive(arr), arr.toString()), 'toPrimitive(' + debug(arr) + ') returns toString of the array'); + t.equal(toPrimitive(arr, String), arr.toString(), 'toPrimitive(' + debug(arr) + ') returns toString of the array'); + t.ok(is(toPrimitive(arr, Number), arr.toString()), 'toPrimitive(' + debug(arr) + ') returns toString of the array'); + }); + t.end(); +}); + +test('Dates', function (t) { + var dates = [new Date(), new Date(0), new Date(NaN)]; + forEach(dates, function (date) { + t.equal(toPrimitive(date), date.toString(), 'toPrimitive(' + debug(date) + ') returns toString of the date'); + t.equal(toPrimitive(date, String), date.toString(), 'toPrimitive(' + debug(date) + ') returns toString of the date'); + t.ok(is(toPrimitive(date, Number), date.valueOf()), 'toPrimitive(' + debug(date) + ') returns valueOf of the date'); + }); + t.end(); +}); + +var coercibleObject = { valueOf: function () { return 3; }, toString: function () { return 42; } }; +var valueOfOnlyObject = { valueOf: function () { return 4; }, toString: function () { return {}; } }; +var toStringOnlyObject = { valueOf: function () { return {}; }, toString: function () { return 7; } }; +var coercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return 42; } +}; +var uncoercibleObject = { valueOf: function () { return {}; }, toString: function () { return {}; } }; +var uncoercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return function toStrFn() {}; } +}; + +test('Objects', function (t) { + t.equal(toPrimitive(coercibleObject), coercibleObject.valueOf(), 'coercibleObject with no hint coerces to valueOf'); + t.equal(toPrimitive(coercibleObject, String), coercibleObject.toString(), 'coercibleObject with hint String coerces to toString'); + t.equal(toPrimitive(coercibleObject, Number), coercibleObject.valueOf(), 'coercibleObject with hint Number coerces to valueOf'); + + t.equal(toPrimitive(coercibleFnObject), coercibleFnObject.toString(), 'coercibleFnObject coerces to toString'); + t.equal(toPrimitive(coercibleFnObject, String), coercibleFnObject.toString(), 'coercibleFnObject with hint String coerces to toString'); + t.equal(toPrimitive(coercibleFnObject, Number), coercibleFnObject.toString(), 'coercibleFnObject with hint Number coerces to toString'); + + t.ok(is(toPrimitive({}), '[object Object]'), '{} with no hint coerces to Object#toString'); + t.equal(toPrimitive({}, String), '[object Object]', '{} with hint String coerces to Object#toString'); + t.ok(is(toPrimitive({}, Number), '[object Object]'), '{} with hint Number coerces to Object#toString'); + + t.equal(toPrimitive(toStringOnlyObject), toStringOnlyObject.toString(), 'toStringOnlyObject returns toString'); + t.equal(toPrimitive(toStringOnlyObject, String), toStringOnlyObject.toString(), 'toStringOnlyObject with hint String returns toString'); + t.equal(toPrimitive(toStringOnlyObject, Number), toStringOnlyObject.toString(), 'toStringOnlyObject with hint Number returns toString'); + + t.equal(toPrimitive(valueOfOnlyObject), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject returns valueOf'); + t.equal(toPrimitive(valueOfOnlyObject, String), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject with hint String returns valueOf'); + t.equal(toPrimitive(valueOfOnlyObject, Number), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject with hint Number returns valueOf'); + + t.test('exceptions', function (st) { + st['throws'](toPrimitive.bind(null, uncoercibleObject), TypeError, 'uncoercibleObject throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleObject, String), TypeError, 'uncoercibleObject with hint String throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleObject, Number), TypeError, 'uncoercibleObject with hint Number throws a TypeError'); + + st['throws'](toPrimitive.bind(null, uncoercibleFnObject), TypeError, 'uncoercibleFnObject throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleFnObject, String), TypeError, 'uncoercibleFnObject with hint String throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleFnObject, Number), TypeError, 'uncoercibleFnObject with hint Number throws a TypeError'); + st.end(); + }); + + t.end(); +}); diff --git a/WebServer/node_modules/es-to-primitive/test/es6.js b/WebServer/node_modules/es-to-primitive/test/es6.js new file mode 100644 index 000000000..c6df63fb6 --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/test/es6.js @@ -0,0 +1,151 @@ +'use strict'; + +var test = require('tape'); +var toPrimitive = require('../es6'); +var is = require('object-is'); +var forEach = require('foreach'); +var functionName = require('function.prototype.name'); +var debug = require('object-inspect'); + +var hasSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'; +var hasSymbolToPrimitive = hasSymbols && typeof Symbol.toPrimitive === 'symbol'; + +test('function properties', function (t) { + t.equal(toPrimitive.length, 1, 'length is 1'); + t.equal(functionName(toPrimitive), 'ToPrimitive', 'name is ToPrimitive'); + + t.end(); +}); + +var primitives = [null, undefined, true, false, 0, -0, 42, NaN, Infinity, -Infinity, '', 'abc']; + +test('primitives', function (t) { + forEach(primitives, function (i) { + t.ok(is(toPrimitive(i), i), 'toPrimitive(' + debug(i) + ') returns the same value'); + t.ok(is(toPrimitive(i, String), i), 'toPrimitive(' + debug(i) + ', String) returns the same value'); + t.ok(is(toPrimitive(i, Number), i), 'toPrimitive(' + debug(i) + ', Number) returns the same value'); + }); + t.end(); +}); + +test('Symbols', { skip: !hasSymbols }, function (t) { + var symbols = [ + Symbol('foo'), + Symbol.iterator, + Symbol['for']('foo') // eslint-disable-line no-restricted-properties + ]; + forEach(symbols, function (sym) { + t.equal(toPrimitive(sym), sym, 'toPrimitive(' + debug(sym) + ') returns the same value'); + t.equal(toPrimitive(sym, String), sym, 'toPrimitive(' + debug(sym) + ', String) returns the same value'); + t.equal(toPrimitive(sym, Number), sym, 'toPrimitive(' + debug(sym) + ', Number) returns the same value'); + }); + + var primitiveSym = Symbol('primitiveSym'); + var objectSym = Object(primitiveSym); + t.equal(toPrimitive(objectSym), primitiveSym, 'toPrimitive(' + debug(objectSym) + ') returns ' + debug(primitiveSym)); + t.equal(toPrimitive(objectSym, String), primitiveSym, 'toPrimitive(' + debug(objectSym) + ', String) returns ' + debug(primitiveSym)); + t.equal(toPrimitive(objectSym, Number), primitiveSym, 'toPrimitive(' + debug(objectSym) + ', Number) returns ' + debug(primitiveSym)); + t.end(); +}); + +test('Arrays', function (t) { + var arrays = [[], ['a', 'b'], [1, 2]]; + forEach(arrays, function (arr) { + t.equal(toPrimitive(arr), String(arr), 'toPrimitive(' + debug(arr) + ') returns the string version of the array'); + t.equal(toPrimitive(arr, String), String(arr), 'toPrimitive(' + debug(arr) + ') returns the string version of the array'); + t.equal(toPrimitive(arr, Number), String(arr), 'toPrimitive(' + debug(arr) + ') returns the string version of the array'); + }); + t.end(); +}); + +test('Dates', function (t) { + var dates = [new Date(), new Date(0), new Date(NaN)]; + forEach(dates, function (date) { + t.equal(toPrimitive(date), String(date), 'toPrimitive(' + debug(date) + ') returns the string version of the date'); + t.equal(toPrimitive(date, String), String(date), 'toPrimitive(' + debug(date) + ') returns the string version of the date'); + t.ok(is(toPrimitive(date, Number), Number(date)), 'toPrimitive(' + debug(date) + ') returns the number version of the date'); + }); + t.end(); +}); + +var coercibleObject = { valueOf: function () { return 3; }, toString: function () { return 42; } }; +var valueOfOnlyObject = { valueOf: function () { return 4; }, toString: function () { return {}; } }; +var toStringOnlyObject = { valueOf: function () { return {}; }, toString: function () { return 7; } }; +var coercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return 42; } +}; +var uncoercibleObject = { valueOf: function () { return {}; }, toString: function () { return {}; } }; +var uncoercibleFnObject = { + valueOf: function () { return function valueOfFn() {}; }, + toString: function () { return function toStrFn() {}; } +}; + +test('Objects', function (t) { + t.equal(toPrimitive(coercibleObject), coercibleObject.valueOf(), 'coercibleObject with no hint coerces to valueOf'); + t.equal(toPrimitive(coercibleObject, Number), coercibleObject.valueOf(), 'coercibleObject with hint Number coerces to valueOf'); + t.equal(toPrimitive(coercibleObject, String), coercibleObject.toString(), 'coercibleObject with hint String coerces to non-stringified toString'); + + t.equal(toPrimitive(coercibleFnObject), coercibleFnObject.toString(), 'coercibleFnObject coerces to non-stringified toString'); + t.equal(toPrimitive(coercibleFnObject, Number), coercibleFnObject.toString(), 'coercibleFnObject with hint Number coerces to non-stringified toString'); + t.equal(toPrimitive(coercibleFnObject, String), coercibleFnObject.toString(), 'coercibleFnObject with hint String coerces to non-stringified toString'); + + t.equal(toPrimitive({}), '[object Object]', '{} with no hint coerces to Object#toString'); + t.equal(toPrimitive({}, Number), '[object Object]', '{} with hint Number coerces to Object#toString'); + t.equal(toPrimitive({}, String), '[object Object]', '{} with hint String coerces to Object#toString'); + + t.equal(toPrimitive(toStringOnlyObject), toStringOnlyObject.toString(), 'toStringOnlyObject returns non-stringified toString'); + t.equal(toPrimitive(toStringOnlyObject, Number), toStringOnlyObject.toString(), 'toStringOnlyObject with hint Number returns non-stringified toString'); + t.equal(toPrimitive(toStringOnlyObject, String), toStringOnlyObject.toString(), 'toStringOnlyObject with hint String returns non-stringified toString'); + + t.equal(toPrimitive(valueOfOnlyObject), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject returns valueOf'); + t.equal(toPrimitive(valueOfOnlyObject, Number), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject with hint Number returns valueOf'); + t.equal(toPrimitive(valueOfOnlyObject, String), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject with hint String returns non-stringified valueOf'); + + t.test('Symbol.toPrimitive', { skip: !hasSymbolToPrimitive }, function (st) { + var overriddenObject = { toString: st.fail, valueOf: st.fail }; + overriddenObject[Symbol.toPrimitive] = function (hint) { return String(hint); }; + + st.equal(toPrimitive(overriddenObject), 'default', 'object with Symbol.toPrimitive + no hint invokes that'); + st.equal(toPrimitive(overriddenObject, Number), 'number', 'object with Symbol.toPrimitive + hint Number invokes that'); + st.equal(toPrimitive(overriddenObject, String), 'string', 'object with Symbol.toPrimitive + hint String invokes that'); + + var nullToPrimitive = { toString: coercibleObject.toString, valueOf: coercibleObject.valueOf }; + nullToPrimitive[Symbol.toPrimitive] = null; + st.equal(toPrimitive(nullToPrimitive), toPrimitive(coercibleObject), 'object with no hint + null Symbol.toPrimitive ignores it'); + st.equal(toPrimitive(nullToPrimitive, Number), toPrimitive(coercibleObject, Number), 'object with hint Number + null Symbol.toPrimitive ignores it'); + st.equal(toPrimitive(nullToPrimitive, String), toPrimitive(coercibleObject, String), 'object with hint String + null Symbol.toPrimitive ignores it'); + + st.test('exceptions', function (sst) { + var nonFunctionToPrimitive = { toString: sst.fail, valueOf: sst.fail }; + nonFunctionToPrimitive[Symbol.toPrimitive] = {}; + sst['throws'](toPrimitive.bind(null, nonFunctionToPrimitive), TypeError, 'Symbol.toPrimitive returning a non-function throws'); + + var uncoercibleToPrimitive = { toString: sst.fail, valueOf: sst.fail }; + uncoercibleToPrimitive[Symbol.toPrimitive] = function (hint) { + return { toString: function () { return hint; } }; + }; + sst['throws'](toPrimitive.bind(null, uncoercibleToPrimitive), TypeError, 'Symbol.toPrimitive returning an object throws'); + + var throwingToPrimitive = { toString: sst.fail, valueOf: sst.fail }; + throwingToPrimitive[Symbol.toPrimitive] = function (hint) { throw new RangeError(hint); }; + sst['throws'](toPrimitive.bind(null, throwingToPrimitive), RangeError, 'Symbol.toPrimitive throwing throws'); + + sst.end(); + }); + + st.end(); + }); + + t.test('exceptions', function (st) { + st['throws'](toPrimitive.bind(null, uncoercibleObject), TypeError, 'uncoercibleObject throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleObject, Number), TypeError, 'uncoercibleObject with hint Number throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleObject, String), TypeError, 'uncoercibleObject with hint String throws a TypeError'); + + st['throws'](toPrimitive.bind(null, uncoercibleFnObject), TypeError, 'uncoercibleFnObject throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleFnObject, Number), TypeError, 'uncoercibleFnObject with hint Number throws a TypeError'); + st['throws'](toPrimitive.bind(null, uncoercibleFnObject, String), TypeError, 'uncoercibleFnObject with hint String throws a TypeError'); + st.end(); + }); + t.end(); +}); diff --git a/WebServer/node_modules/es-to-primitive/test/index.js b/WebServer/node_modules/es-to-primitive/test/index.js new file mode 100644 index 000000000..ad71f39e2 --- /dev/null +++ b/WebServer/node_modules/es-to-primitive/test/index.js @@ -0,0 +1,20 @@ +'use strict'; + +var toPrimitive = require('../'); +var ES5 = require('../es5'); +var ES6 = require('../es6'); +var ES2015 = require('../es2015'); + +var test = require('tape'); + +test('default export', function (t) { + t.equal(toPrimitive, ES2015, 'default export is ES2015'); + t.equal(toPrimitive.ES5, ES5, 'ES5 property has ES5 method'); + t.equal(toPrimitive.ES6, ES6, 'ES6 property has ES6 method'); + t.equal(toPrimitive.ES2015, ES2015, 'ES2015 property has ES2015 method'); + t.end(); +}); + +require('./es5'); +require('./es6'); +require('./es2015'); diff --git a/WebServer/node_modules/escape-string-regexp/index.js b/WebServer/node_modules/escape-string-regexp/index.js new file mode 100644 index 000000000..7834bf9b2 --- /dev/null +++ b/WebServer/node_modules/escape-string-regexp/index.js @@ -0,0 +1,11 @@ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; diff --git a/WebServer/node_modules/escape-string-regexp/package.json b/WebServer/node_modules/escape-string-regexp/package.json new file mode 100644 index 000000000..2482191e8 --- /dev/null +++ b/WebServer/node_modules/escape-string-regexp/package.json @@ -0,0 +1,85 @@ +{ + "_args": [ + [ + "escape-string-regexp@1.0.5", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "escape-string-regexp@1.0.5", + "_id": "escape-string-regexp@1.0.5", + "_inBundle": false, + "_integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "_location": "/escape-string-regexp", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "escape-string-regexp@1.0.5", + "name": "escape-string-regexp", + "escapedName": "escape-string-regexp", + "rawSpec": "1.0.5", + "saveSpec": null, + "fetchSpec": "1.0.5" + }, + "_requiredBy": [ + "/chalk", + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "_spec": "1.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/escape-string-regexp/issues" + }, + "description": "Escape RegExp special characters", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=0.8.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/escape-string-regexp#readme", + "keywords": [ + "escape", + "regex", + "regexp", + "re", + "regular", + "expression", + "string", + "str", + "special", + "characters" + ], + "license": "MIT", + "maintainers": [ + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + { + "name": "Joshua Boy Nicolai Appelman", + "email": "joshua@jbna.nl", + "url": "jbna.nl" + } + ], + "name": "escape-string-regexp", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/escape-string-regexp.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.0.5" +} diff --git a/WebServer/node_modules/esprima/bin/esparse.js b/WebServer/node_modules/esprima/bin/esparse.js new file mode 100644 index 000000000..45d05fbb7 --- /dev/null +++ b/WebServer/node_modules/esprima/bin/esparse.js @@ -0,0 +1,139 @@ +#!/usr/bin/env node +/* + Copyright JS Foundation and other contributors, https://js.foundation/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*jslint sloppy:true node:true rhino:true */ + +var fs, esprima, fname, forceFile, content, options, syntax; + +if (typeof require === 'function') { + fs = require('fs'); + try { + esprima = require('esprima'); + } catch (e) { + esprima = require('../'); + } +} else if (typeof load === 'function') { + try { + load('esprima.js'); + } catch (e) { + load('../esprima.js'); + } +} + +// Shims to Node.js objects when running under Rhino. +if (typeof console === 'undefined' && typeof process === 'undefined') { + console = { log: print }; + fs = { readFileSync: readFile }; + process = { argv: arguments, exit: quit }; + process.argv.unshift('esparse.js'); + process.argv.unshift('rhino'); +} + +function showUsage() { + console.log('Usage:'); + console.log(' esparse [options] [file.js]'); + console.log(); + console.log('Available options:'); + console.log(); + console.log(' --comment Gather all line and block comments in an array'); + console.log(' --loc Include line-column location info for each syntax node'); + console.log(' --range Include index-based range for each syntax node'); + console.log(' --raw Display the raw value of literals'); + console.log(' --tokens List all tokens in an array'); + console.log(' --tolerant Tolerate errors on a best-effort basis (experimental)'); + console.log(' -v, --version Shows program version'); + console.log(); + process.exit(1); +} + +options = {}; + +process.argv.splice(2).forEach(function (entry) { + + if (forceFile || entry === '-' || entry.slice(0, 1) !== '-') { + if (typeof fname === 'string') { + console.log('Error: more than one input file.'); + process.exit(1); + } else { + fname = entry; + } + } else if (entry === '-h' || entry === '--help') { + showUsage(); + } else if (entry === '-v' || entry === '--version') { + console.log('ECMAScript Parser (using Esprima version', esprima.version, ')'); + console.log(); + process.exit(0); + } else if (entry === '--comment') { + options.comment = true; + } else if (entry === '--loc') { + options.loc = true; + } else if (entry === '--range') { + options.range = true; + } else if (entry === '--raw') { + options.raw = true; + } else if (entry === '--tokens') { + options.tokens = true; + } else if (entry === '--tolerant') { + options.tolerant = true; + } else if (entry === '--') { + forceFile = true; + } else { + console.log('Error: unknown option ' + entry + '.'); + process.exit(1); + } +}); + +// Special handling for regular expression literal since we need to +// convert it to a string literal, otherwise it will be decoded +// as object "{}" and the regular expression would be lost. +function adjustRegexLiteral(key, value) { + if (key === 'value' && value instanceof RegExp) { + value = value.toString(); + } + return value; +} + +function run(content) { + syntax = esprima.parse(content, options); + console.log(JSON.stringify(syntax, adjustRegexLiteral, 4)); +} + +try { + if (fname && (fname !== '-' || forceFile)) { + run(fs.readFileSync(fname, 'utf-8')); + } else { + var content = ''; + process.stdin.resume(); + process.stdin.on('data', function(chunk) { + content += chunk; + }); + process.stdin.on('end', function() { + run(content); + }); + } +} catch (e) { + console.log('Error: ' + e.message); + process.exit(1); +} diff --git a/WebServer/node_modules/esprima/bin/esvalidate.js b/WebServer/node_modules/esprima/bin/esvalidate.js new file mode 100644 index 000000000..d49a7e40a --- /dev/null +++ b/WebServer/node_modules/esprima/bin/esvalidate.js @@ -0,0 +1,236 @@ +#!/usr/bin/env node +/* + Copyright JS Foundation and other contributors, https://js.foundation/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/*jslint sloppy:true plusplus:true node:true rhino:true */ +/*global phantom:true */ + +var fs, system, esprima, options, fnames, forceFile, count; + +if (typeof esprima === 'undefined') { + // PhantomJS can only require() relative files + if (typeof phantom === 'object') { + fs = require('fs'); + system = require('system'); + esprima = require('./esprima'); + } else if (typeof require === 'function') { + fs = require('fs'); + try { + esprima = require('esprima'); + } catch (e) { + esprima = require('../'); + } + } else if (typeof load === 'function') { + try { + load('esprima.js'); + } catch (e) { + load('../esprima.js'); + } + } +} + +// Shims to Node.js objects when running under PhantomJS 1.7+. +if (typeof phantom === 'object') { + fs.readFileSync = fs.read; + process = { + argv: [].slice.call(system.args), + exit: phantom.exit, + on: function (evt, callback) { + callback(); + } + }; + process.argv.unshift('phantomjs'); +} + +// Shims to Node.js objects when running under Rhino. +if (typeof console === 'undefined' && typeof process === 'undefined') { + console = { log: print }; + fs = { readFileSync: readFile }; + process = { + argv: arguments, + exit: quit, + on: function (evt, callback) { + callback(); + } + }; + process.argv.unshift('esvalidate.js'); + process.argv.unshift('rhino'); +} + +function showUsage() { + console.log('Usage:'); + console.log(' esvalidate [options] [file.js...]'); + console.log(); + console.log('Available options:'); + console.log(); + console.log(' --format=type Set the report format, plain (default) or junit'); + console.log(' -v, --version Print program version'); + console.log(); + process.exit(1); +} + +options = { + format: 'plain' +}; + +fnames = []; + +process.argv.splice(2).forEach(function (entry) { + + if (forceFile || entry === '-' || entry.slice(0, 1) !== '-') { + fnames.push(entry); + } else if (entry === '-h' || entry === '--help') { + showUsage(); + } else if (entry === '-v' || entry === '--version') { + console.log('ECMAScript Validator (using Esprima version', esprima.version, ')'); + console.log(); + process.exit(0); + } else if (entry.slice(0, 9) === '--format=') { + options.format = entry.slice(9); + if (options.format !== 'plain' && options.format !== 'junit') { + console.log('Error: unknown report format ' + options.format + '.'); + process.exit(1); + } + } else if (entry === '--') { + forceFile = true; + } else { + console.log('Error: unknown option ' + entry + '.'); + process.exit(1); + } +}); + +if (fnames.length === 0) { + fnames.push(''); +} + +if (options.format === 'junit') { + console.log(''); + console.log(''); +} + +count = 0; + +function run(fname, content) { + var timestamp, syntax, name; + try { + if (typeof content !== 'string') { + throw content; + } + + if (content[0] === '#' && content[1] === '!') { + content = '//' + content.substr(2, content.length); + } + + timestamp = Date.now(); + syntax = esprima.parse(content, { tolerant: true }); + + if (options.format === 'junit') { + + name = fname; + if (name.lastIndexOf('/') >= 0) { + name = name.slice(name.lastIndexOf('/') + 1); + } + + console.log(''); + + syntax.errors.forEach(function (error) { + var msg = error.message; + msg = msg.replace(/^Line\ [0-9]*\:\ /, ''); + console.log(' '); + console.log(' ' + + error.message + '(' + name + ':' + error.lineNumber + ')' + + ''); + console.log(' '); + }); + + console.log(''); + + } else if (options.format === 'plain') { + + syntax.errors.forEach(function (error) { + var msg = error.message; + msg = msg.replace(/^Line\ [0-9]*\:\ /, ''); + msg = fname + ':' + error.lineNumber + ': ' + msg; + console.log(msg); + ++count; + }); + + } + } catch (e) { + ++count; + if (options.format === 'junit') { + console.log(''); + console.log(' '); + console.log(' ' + + e.message + '(' + fname + ((e.lineNumber) ? ':' + e.lineNumber : '') + + ')'); + console.log(' '); + console.log(''); + } else { + console.log(fname + ':' + e.lineNumber + ': ' + e.message.replace(/^Line\ [0-9]*\:\ /, '')); + } + } +} + +fnames.forEach(function (fname) { + var content = ''; + try { + if (fname && (fname !== '-' || forceFile)) { + content = fs.readFileSync(fname, 'utf-8'); + } else { + fname = ''; + process.stdin.resume(); + process.stdin.on('data', function(chunk) { + content += chunk; + }); + process.stdin.on('end', function() { + run(fname, content); + }); + return; + } + } catch (e) { + content = e; + } + run(fname, content); +}); + +process.on('exit', function () { + if (options.format === 'junit') { + console.log(''); + } + + if (count > 0) { + process.exit(1); + } + + if (count === 0 && typeof phantom === 'object') { + process.exit(0); + } +}); diff --git a/WebServer/node_modules/esprima/dist/esprima.js b/WebServer/node_modules/esprima/dist/esprima.js new file mode 100644 index 000000000..2af3eee12 --- /dev/null +++ b/WebServer/node_modules/esprima/dist/esprima.js @@ -0,0 +1,6709 @@ +(function webpackUniversalModuleDefinition(root, factory) { +/* istanbul ignore next */ + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); +/* istanbul ignore next */ + else if(typeof exports === 'object') + exports["esprima"] = factory(); + else + root["esprima"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/* istanbul ignore if */ +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + /* + Copyright JS Foundation and other contributors, https://js.foundation/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + Object.defineProperty(exports, "__esModule", { value: true }); + var comment_handler_1 = __webpack_require__(1); + var jsx_parser_1 = __webpack_require__(3); + var parser_1 = __webpack_require__(8); + var tokenizer_1 = __webpack_require__(15); + function parse(code, options, delegate) { + var commentHandler = null; + var proxyDelegate = function (node, metadata) { + if (delegate) { + delegate(node, metadata); + } + if (commentHandler) { + commentHandler.visit(node, metadata); + } + }; + var parserDelegate = (typeof delegate === 'function') ? proxyDelegate : null; + var collectComment = false; + if (options) { + collectComment = (typeof options.comment === 'boolean' && options.comment); + var attachComment = (typeof options.attachComment === 'boolean' && options.attachComment); + if (collectComment || attachComment) { + commentHandler = new comment_handler_1.CommentHandler(); + commentHandler.attach = attachComment; + options.comment = true; + parserDelegate = proxyDelegate; + } + } + var isModule = false; + if (options && typeof options.sourceType === 'string') { + isModule = (options.sourceType === 'module'); + } + var parser; + if (options && typeof options.jsx === 'boolean' && options.jsx) { + parser = new jsx_parser_1.JSXParser(code, options, parserDelegate); + } + else { + parser = new parser_1.Parser(code, options, parserDelegate); + } + var program = isModule ? parser.parseModule() : parser.parseScript(); + var ast = program; + if (collectComment && commentHandler) { + ast.comments = commentHandler.comments; + } + if (parser.config.tokens) { + ast.tokens = parser.tokens; + } + if (parser.config.tolerant) { + ast.errors = parser.errorHandler.errors; + } + return ast; + } + exports.parse = parse; + function parseModule(code, options, delegate) { + var parsingOptions = options || {}; + parsingOptions.sourceType = 'module'; + return parse(code, parsingOptions, delegate); + } + exports.parseModule = parseModule; + function parseScript(code, options, delegate) { + var parsingOptions = options || {}; + parsingOptions.sourceType = 'script'; + return parse(code, parsingOptions, delegate); + } + exports.parseScript = parseScript; + function tokenize(code, options, delegate) { + var tokenizer = new tokenizer_1.Tokenizer(code, options); + var tokens; + tokens = []; + try { + while (true) { + var token = tokenizer.getNextToken(); + if (!token) { + break; + } + if (delegate) { + token = delegate(token); + } + tokens.push(token); + } + } + catch (e) { + tokenizer.errorHandler.tolerate(e); + } + if (tokenizer.errorHandler.tolerant) { + tokens.errors = tokenizer.errors(); + } + return tokens; + } + exports.tokenize = tokenize; + var syntax_1 = __webpack_require__(2); + exports.Syntax = syntax_1.Syntax; + // Sync with *.json manifests. + exports.version = '4.0.1'; + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var syntax_1 = __webpack_require__(2); + var CommentHandler = (function () { + function CommentHandler() { + this.attach = false; + this.comments = []; + this.stack = []; + this.leading = []; + this.trailing = []; + } + CommentHandler.prototype.insertInnerComments = function (node, metadata) { + // innnerComments for properties empty block + // `function a() {/** comments **\/}` + if (node.type === syntax_1.Syntax.BlockStatement && node.body.length === 0) { + var innerComments = []; + for (var i = this.leading.length - 1; i >= 0; --i) { + var entry = this.leading[i]; + if (metadata.end.offset >= entry.start) { + innerComments.unshift(entry.comment); + this.leading.splice(i, 1); + this.trailing.splice(i, 1); + } + } + if (innerComments.length) { + node.innerComments = innerComments; + } + } + }; + CommentHandler.prototype.findTrailingComments = function (metadata) { + var trailingComments = []; + if (this.trailing.length > 0) { + for (var i = this.trailing.length - 1; i >= 0; --i) { + var entry_1 = this.trailing[i]; + if (entry_1.start >= metadata.end.offset) { + trailingComments.unshift(entry_1.comment); + } + } + this.trailing.length = 0; + return trailingComments; + } + var entry = this.stack[this.stack.length - 1]; + if (entry && entry.node.trailingComments) { + var firstComment = entry.node.trailingComments[0]; + if (firstComment && firstComment.range[0] >= metadata.end.offset) { + trailingComments = entry.node.trailingComments; + delete entry.node.trailingComments; + } + } + return trailingComments; + }; + CommentHandler.prototype.findLeadingComments = function (metadata) { + var leadingComments = []; + var target; + while (this.stack.length > 0) { + var entry = this.stack[this.stack.length - 1]; + if (entry && entry.start >= metadata.start.offset) { + target = entry.node; + this.stack.pop(); + } + else { + break; + } + } + if (target) { + var count = target.leadingComments ? target.leadingComments.length : 0; + for (var i = count - 1; i >= 0; --i) { + var comment = target.leadingComments[i]; + if (comment.range[1] <= metadata.start.offset) { + leadingComments.unshift(comment); + target.leadingComments.splice(i, 1); + } + } + if (target.leadingComments && target.leadingComments.length === 0) { + delete target.leadingComments; + } + return leadingComments; + } + for (var i = this.leading.length - 1; i >= 0; --i) { + var entry = this.leading[i]; + if (entry.start <= metadata.start.offset) { + leadingComments.unshift(entry.comment); + this.leading.splice(i, 1); + } + } + return leadingComments; + }; + CommentHandler.prototype.visitNode = function (node, metadata) { + if (node.type === syntax_1.Syntax.Program && node.body.length > 0) { + return; + } + this.insertInnerComments(node, metadata); + var trailingComments = this.findTrailingComments(metadata); + var leadingComments = this.findLeadingComments(metadata); + if (leadingComments.length > 0) { + node.leadingComments = leadingComments; + } + if (trailingComments.length > 0) { + node.trailingComments = trailingComments; + } + this.stack.push({ + node: node, + start: metadata.start.offset + }); + }; + CommentHandler.prototype.visitComment = function (node, metadata) { + var type = (node.type[0] === 'L') ? 'Line' : 'Block'; + var comment = { + type: type, + value: node.value + }; + if (node.range) { + comment.range = node.range; + } + if (node.loc) { + comment.loc = node.loc; + } + this.comments.push(comment); + if (this.attach) { + var entry = { + comment: { + type: type, + value: node.value, + range: [metadata.start.offset, metadata.end.offset] + }, + start: metadata.start.offset + }; + if (node.loc) { + entry.comment.loc = node.loc; + } + node.type = type; + this.leading.push(entry); + this.trailing.push(entry); + } + }; + CommentHandler.prototype.visit = function (node, metadata) { + if (node.type === 'LineComment') { + this.visitComment(node, metadata); + } + else if (node.type === 'BlockComment') { + this.visitComment(node, metadata); + } + else if (this.attach) { + this.visitNode(node, metadata); + } + }; + return CommentHandler; + }()); + exports.CommentHandler = CommentHandler; + + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Syntax = { + AssignmentExpression: 'AssignmentExpression', + AssignmentPattern: 'AssignmentPattern', + ArrayExpression: 'ArrayExpression', + ArrayPattern: 'ArrayPattern', + ArrowFunctionExpression: 'ArrowFunctionExpression', + AwaitExpression: 'AwaitExpression', + BlockStatement: 'BlockStatement', + BinaryExpression: 'BinaryExpression', + BreakStatement: 'BreakStatement', + CallExpression: 'CallExpression', + CatchClause: 'CatchClause', + ClassBody: 'ClassBody', + ClassDeclaration: 'ClassDeclaration', + ClassExpression: 'ClassExpression', + ConditionalExpression: 'ConditionalExpression', + ContinueStatement: 'ContinueStatement', + DoWhileStatement: 'DoWhileStatement', + DebuggerStatement: 'DebuggerStatement', + EmptyStatement: 'EmptyStatement', + ExportAllDeclaration: 'ExportAllDeclaration', + ExportDefaultDeclaration: 'ExportDefaultDeclaration', + ExportNamedDeclaration: 'ExportNamedDeclaration', + ExportSpecifier: 'ExportSpecifier', + ExpressionStatement: 'ExpressionStatement', + ForStatement: 'ForStatement', + ForOfStatement: 'ForOfStatement', + ForInStatement: 'ForInStatement', + FunctionDeclaration: 'FunctionDeclaration', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + IfStatement: 'IfStatement', + ImportDeclaration: 'ImportDeclaration', + ImportDefaultSpecifier: 'ImportDefaultSpecifier', + ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', + ImportSpecifier: 'ImportSpecifier', + Literal: 'Literal', + LabeledStatement: 'LabeledStatement', + LogicalExpression: 'LogicalExpression', + MemberExpression: 'MemberExpression', + MetaProperty: 'MetaProperty', + MethodDefinition: 'MethodDefinition', + NewExpression: 'NewExpression', + ObjectExpression: 'ObjectExpression', + ObjectPattern: 'ObjectPattern', + Program: 'Program', + Property: 'Property', + RestElement: 'RestElement', + ReturnStatement: 'ReturnStatement', + SequenceExpression: 'SequenceExpression', + SpreadElement: 'SpreadElement', + Super: 'Super', + SwitchCase: 'SwitchCase', + SwitchStatement: 'SwitchStatement', + TaggedTemplateExpression: 'TaggedTemplateExpression', + TemplateElement: 'TemplateElement', + TemplateLiteral: 'TemplateLiteral', + ThisExpression: 'ThisExpression', + ThrowStatement: 'ThrowStatement', + TryStatement: 'TryStatement', + UnaryExpression: 'UnaryExpression', + UpdateExpression: 'UpdateExpression', + VariableDeclaration: 'VariableDeclaration', + VariableDeclarator: 'VariableDeclarator', + WhileStatement: 'WhileStatement', + WithStatement: 'WithStatement', + YieldExpression: 'YieldExpression' + }; + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; +/* istanbul ignore next */ + var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + Object.defineProperty(exports, "__esModule", { value: true }); + var character_1 = __webpack_require__(4); + var JSXNode = __webpack_require__(5); + var jsx_syntax_1 = __webpack_require__(6); + var Node = __webpack_require__(7); + var parser_1 = __webpack_require__(8); + var token_1 = __webpack_require__(13); + var xhtml_entities_1 = __webpack_require__(14); + token_1.TokenName[100 /* Identifier */] = 'JSXIdentifier'; + token_1.TokenName[101 /* Text */] = 'JSXText'; + // Fully qualified element name, e.g. returns "svg:path" + function getQualifiedElementName(elementName) { + var qualifiedName; + switch (elementName.type) { + case jsx_syntax_1.JSXSyntax.JSXIdentifier: + var id = elementName; + qualifiedName = id.name; + break; + case jsx_syntax_1.JSXSyntax.JSXNamespacedName: + var ns = elementName; + qualifiedName = getQualifiedElementName(ns.namespace) + ':' + + getQualifiedElementName(ns.name); + break; + case jsx_syntax_1.JSXSyntax.JSXMemberExpression: + var expr = elementName; + qualifiedName = getQualifiedElementName(expr.object) + '.' + + getQualifiedElementName(expr.property); + break; + /* istanbul ignore next */ + default: + break; + } + return qualifiedName; + } + var JSXParser = (function (_super) { + __extends(JSXParser, _super); + function JSXParser(code, options, delegate) { + return _super.call(this, code, options, delegate) || this; + } + JSXParser.prototype.parsePrimaryExpression = function () { + return this.match('<') ? this.parseJSXRoot() : _super.prototype.parsePrimaryExpression.call(this); + }; + JSXParser.prototype.startJSX = function () { + // Unwind the scanner before the lookahead token. + this.scanner.index = this.startMarker.index; + this.scanner.lineNumber = this.startMarker.line; + this.scanner.lineStart = this.startMarker.index - this.startMarker.column; + }; + JSXParser.prototype.finishJSX = function () { + // Prime the next lookahead. + this.nextToken(); + }; + JSXParser.prototype.reenterJSX = function () { + this.startJSX(); + this.expectJSX('}'); + // Pop the closing '}' added from the lookahead. + if (this.config.tokens) { + this.tokens.pop(); + } + }; + JSXParser.prototype.createJSXNode = function () { + this.collectComments(); + return { + index: this.scanner.index, + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + }; + }; + JSXParser.prototype.createJSXChildNode = function () { + return { + index: this.scanner.index, + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + }; + }; + JSXParser.prototype.scanXHTMLEntity = function (quote) { + var result = '&'; + var valid = true; + var terminated = false; + var numeric = false; + var hex = false; + while (!this.scanner.eof() && valid && !terminated) { + var ch = this.scanner.source[this.scanner.index]; + if (ch === quote) { + break; + } + terminated = (ch === ';'); + result += ch; + ++this.scanner.index; + if (!terminated) { + switch (result.length) { + case 2: + // e.g. '{' + numeric = (ch === '#'); + break; + case 3: + if (numeric) { + // e.g. 'A' + hex = (ch === 'x'); + valid = hex || character_1.Character.isDecimalDigit(ch.charCodeAt(0)); + numeric = numeric && !hex; + } + break; + default: + valid = valid && !(numeric && !character_1.Character.isDecimalDigit(ch.charCodeAt(0))); + valid = valid && !(hex && !character_1.Character.isHexDigit(ch.charCodeAt(0))); + break; + } + } + } + if (valid && terminated && result.length > 2) { + // e.g. 'A' becomes just '#x41' + var str = result.substr(1, result.length - 2); + if (numeric && str.length > 1) { + result = String.fromCharCode(parseInt(str.substr(1), 10)); + } + else if (hex && str.length > 2) { + result = String.fromCharCode(parseInt('0' + str.substr(1), 16)); + } + else if (!numeric && !hex && xhtml_entities_1.XHTMLEntities[str]) { + result = xhtml_entities_1.XHTMLEntities[str]; + } + } + return result; + }; + // Scan the next JSX token. This replaces Scanner#lex when in JSX mode. + JSXParser.prototype.lexJSX = function () { + var cp = this.scanner.source.charCodeAt(this.scanner.index); + // < > / : = { } + if (cp === 60 || cp === 62 || cp === 47 || cp === 58 || cp === 61 || cp === 123 || cp === 125) { + var value = this.scanner.source[this.scanner.index++]; + return { + type: 7 /* Punctuator */, + value: value, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: this.scanner.index - 1, + end: this.scanner.index + }; + } + // " ' + if (cp === 34 || cp === 39) { + var start = this.scanner.index; + var quote = this.scanner.source[this.scanner.index++]; + var str = ''; + while (!this.scanner.eof()) { + var ch = this.scanner.source[this.scanner.index++]; + if (ch === quote) { + break; + } + else if (ch === '&') { + str += this.scanXHTMLEntity(quote); + } + else { + str += ch; + } + } + return { + type: 8 /* StringLiteral */, + value: str, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + } + // ... or . + if (cp === 46) { + var n1 = this.scanner.source.charCodeAt(this.scanner.index + 1); + var n2 = this.scanner.source.charCodeAt(this.scanner.index + 2); + var value = (n1 === 46 && n2 === 46) ? '...' : '.'; + var start = this.scanner.index; + this.scanner.index += value.length; + return { + type: 7 /* Punctuator */, + value: value, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + } + // ` + if (cp === 96) { + // Only placeholder, since it will be rescanned as a real assignment expression. + return { + type: 10 /* Template */, + value: '', + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: this.scanner.index, + end: this.scanner.index + }; + } + // Identifer can not contain backslash (char code 92). + if (character_1.Character.isIdentifierStart(cp) && (cp !== 92)) { + var start = this.scanner.index; + ++this.scanner.index; + while (!this.scanner.eof()) { + var ch = this.scanner.source.charCodeAt(this.scanner.index); + if (character_1.Character.isIdentifierPart(ch) && (ch !== 92)) { + ++this.scanner.index; + } + else if (ch === 45) { + // Hyphen (char code 45) can be part of an identifier. + ++this.scanner.index; + } + else { + break; + } + } + var id = this.scanner.source.slice(start, this.scanner.index); + return { + type: 100 /* Identifier */, + value: id, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + } + return this.scanner.lex(); + }; + JSXParser.prototype.nextJSXToken = function () { + this.collectComments(); + this.startMarker.index = this.scanner.index; + this.startMarker.line = this.scanner.lineNumber; + this.startMarker.column = this.scanner.index - this.scanner.lineStart; + var token = this.lexJSX(); + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + if (this.config.tokens) { + this.tokens.push(this.convertToken(token)); + } + return token; + }; + JSXParser.prototype.nextJSXText = function () { + this.startMarker.index = this.scanner.index; + this.startMarker.line = this.scanner.lineNumber; + this.startMarker.column = this.scanner.index - this.scanner.lineStart; + var start = this.scanner.index; + var text = ''; + while (!this.scanner.eof()) { + var ch = this.scanner.source[this.scanner.index]; + if (ch === '{' || ch === '<') { + break; + } + ++this.scanner.index; + text += ch; + if (character_1.Character.isLineTerminator(ch.charCodeAt(0))) { + ++this.scanner.lineNumber; + if (ch === '\r' && this.scanner.source[this.scanner.index] === '\n') { + ++this.scanner.index; + } + this.scanner.lineStart = this.scanner.index; + } + } + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + var token = { + type: 101 /* Text */, + value: text, + lineNumber: this.scanner.lineNumber, + lineStart: this.scanner.lineStart, + start: start, + end: this.scanner.index + }; + if ((text.length > 0) && this.config.tokens) { + this.tokens.push(this.convertToken(token)); + } + return token; + }; + JSXParser.prototype.peekJSXToken = function () { + var state = this.scanner.saveState(); + this.scanner.scanComments(); + var next = this.lexJSX(); + this.scanner.restoreState(state); + return next; + }; + // Expect the next JSX token to match the specified punctuator. + // If not, an exception will be thrown. + JSXParser.prototype.expectJSX = function (value) { + var token = this.nextJSXToken(); + if (token.type !== 7 /* Punctuator */ || token.value !== value) { + this.throwUnexpectedToken(token); + } + }; + // Return true if the next JSX token matches the specified punctuator. + JSXParser.prototype.matchJSX = function (value) { + var next = this.peekJSXToken(); + return next.type === 7 /* Punctuator */ && next.value === value; + }; + JSXParser.prototype.parseJSXIdentifier = function () { + var node = this.createJSXNode(); + var token = this.nextJSXToken(); + if (token.type !== 100 /* Identifier */) { + this.throwUnexpectedToken(token); + } + return this.finalize(node, new JSXNode.JSXIdentifier(token.value)); + }; + JSXParser.prototype.parseJSXElementName = function () { + var node = this.createJSXNode(); + var elementName = this.parseJSXIdentifier(); + if (this.matchJSX(':')) { + var namespace = elementName; + this.expectJSX(':'); + var name_1 = this.parseJSXIdentifier(); + elementName = this.finalize(node, new JSXNode.JSXNamespacedName(namespace, name_1)); + } + else if (this.matchJSX('.')) { + while (this.matchJSX('.')) { + var object = elementName; + this.expectJSX('.'); + var property = this.parseJSXIdentifier(); + elementName = this.finalize(node, new JSXNode.JSXMemberExpression(object, property)); + } + } + return elementName; + }; + JSXParser.prototype.parseJSXAttributeName = function () { + var node = this.createJSXNode(); + var attributeName; + var identifier = this.parseJSXIdentifier(); + if (this.matchJSX(':')) { + var namespace = identifier; + this.expectJSX(':'); + var name_2 = this.parseJSXIdentifier(); + attributeName = this.finalize(node, new JSXNode.JSXNamespacedName(namespace, name_2)); + } + else { + attributeName = identifier; + } + return attributeName; + }; + JSXParser.prototype.parseJSXStringLiteralAttribute = function () { + var node = this.createJSXNode(); + var token = this.nextJSXToken(); + if (token.type !== 8 /* StringLiteral */) { + this.throwUnexpectedToken(token); + } + var raw = this.getTokenRaw(token); + return this.finalize(node, new Node.Literal(token.value, raw)); + }; + JSXParser.prototype.parseJSXExpressionAttribute = function () { + var node = this.createJSXNode(); + this.expectJSX('{'); + this.finishJSX(); + if (this.match('}')) { + this.tolerateError('JSX attributes must only be assigned a non-empty expression'); + } + var expression = this.parseAssignmentExpression(); + this.reenterJSX(); + return this.finalize(node, new JSXNode.JSXExpressionContainer(expression)); + }; + JSXParser.prototype.parseJSXAttributeValue = function () { + return this.matchJSX('{') ? this.parseJSXExpressionAttribute() : + this.matchJSX('<') ? this.parseJSXElement() : this.parseJSXStringLiteralAttribute(); + }; + JSXParser.prototype.parseJSXNameValueAttribute = function () { + var node = this.createJSXNode(); + var name = this.parseJSXAttributeName(); + var value = null; + if (this.matchJSX('=')) { + this.expectJSX('='); + value = this.parseJSXAttributeValue(); + } + return this.finalize(node, new JSXNode.JSXAttribute(name, value)); + }; + JSXParser.prototype.parseJSXSpreadAttribute = function () { + var node = this.createJSXNode(); + this.expectJSX('{'); + this.expectJSX('...'); + this.finishJSX(); + var argument = this.parseAssignmentExpression(); + this.reenterJSX(); + return this.finalize(node, new JSXNode.JSXSpreadAttribute(argument)); + }; + JSXParser.prototype.parseJSXAttributes = function () { + var attributes = []; + while (!this.matchJSX('/') && !this.matchJSX('>')) { + var attribute = this.matchJSX('{') ? this.parseJSXSpreadAttribute() : + this.parseJSXNameValueAttribute(); + attributes.push(attribute); + } + return attributes; + }; + JSXParser.prototype.parseJSXOpeningElement = function () { + var node = this.createJSXNode(); + this.expectJSX('<'); + var name = this.parseJSXElementName(); + var attributes = this.parseJSXAttributes(); + var selfClosing = this.matchJSX('/'); + if (selfClosing) { + this.expectJSX('/'); + } + this.expectJSX('>'); + return this.finalize(node, new JSXNode.JSXOpeningElement(name, selfClosing, attributes)); + }; + JSXParser.prototype.parseJSXBoundaryElement = function () { + var node = this.createJSXNode(); + this.expectJSX('<'); + if (this.matchJSX('/')) { + this.expectJSX('/'); + var name_3 = this.parseJSXElementName(); + this.expectJSX('>'); + return this.finalize(node, new JSXNode.JSXClosingElement(name_3)); + } + var name = this.parseJSXElementName(); + var attributes = this.parseJSXAttributes(); + var selfClosing = this.matchJSX('/'); + if (selfClosing) { + this.expectJSX('/'); + } + this.expectJSX('>'); + return this.finalize(node, new JSXNode.JSXOpeningElement(name, selfClosing, attributes)); + }; + JSXParser.prototype.parseJSXEmptyExpression = function () { + var node = this.createJSXChildNode(); + this.collectComments(); + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + return this.finalize(node, new JSXNode.JSXEmptyExpression()); + }; + JSXParser.prototype.parseJSXExpressionContainer = function () { + var node = this.createJSXNode(); + this.expectJSX('{'); + var expression; + if (this.matchJSX('}')) { + expression = this.parseJSXEmptyExpression(); + this.expectJSX('}'); + } + else { + this.finishJSX(); + expression = this.parseAssignmentExpression(); + this.reenterJSX(); + } + return this.finalize(node, new JSXNode.JSXExpressionContainer(expression)); + }; + JSXParser.prototype.parseJSXChildren = function () { + var children = []; + while (!this.scanner.eof()) { + var node = this.createJSXChildNode(); + var token = this.nextJSXText(); + if (token.start < token.end) { + var raw = this.getTokenRaw(token); + var child = this.finalize(node, new JSXNode.JSXText(token.value, raw)); + children.push(child); + } + if (this.scanner.source[this.scanner.index] === '{') { + var container = this.parseJSXExpressionContainer(); + children.push(container); + } + else { + break; + } + } + return children; + }; + JSXParser.prototype.parseComplexJSXElement = function (el) { + var stack = []; + while (!this.scanner.eof()) { + el.children = el.children.concat(this.parseJSXChildren()); + var node = this.createJSXChildNode(); + var element = this.parseJSXBoundaryElement(); + if (element.type === jsx_syntax_1.JSXSyntax.JSXOpeningElement) { + var opening = element; + if (opening.selfClosing) { + var child = this.finalize(node, new JSXNode.JSXElement(opening, [], null)); + el.children.push(child); + } + else { + stack.push(el); + el = { node: node, opening: opening, closing: null, children: [] }; + } + } + if (element.type === jsx_syntax_1.JSXSyntax.JSXClosingElement) { + el.closing = element; + var open_1 = getQualifiedElementName(el.opening.name); + var close_1 = getQualifiedElementName(el.closing.name); + if (open_1 !== close_1) { + this.tolerateError('Expected corresponding JSX closing tag for %0', open_1); + } + if (stack.length > 0) { + var child = this.finalize(el.node, new JSXNode.JSXElement(el.opening, el.children, el.closing)); + el = stack[stack.length - 1]; + el.children.push(child); + stack.pop(); + } + else { + break; + } + } + } + return el; + }; + JSXParser.prototype.parseJSXElement = function () { + var node = this.createJSXNode(); + var opening = this.parseJSXOpeningElement(); + var children = []; + var closing = null; + if (!opening.selfClosing) { + var el = this.parseComplexJSXElement({ node: node, opening: opening, closing: closing, children: children }); + children = el.children; + closing = el.closing; + } + return this.finalize(node, new JSXNode.JSXElement(opening, children, closing)); + }; + JSXParser.prototype.parseJSXRoot = function () { + // Pop the opening '<' added from the lookahead. + if (this.config.tokens) { + this.tokens.pop(); + } + this.startJSX(); + var element = this.parseJSXElement(); + this.finishJSX(); + return element; + }; + JSXParser.prototype.isStartOfExpression = function () { + return _super.prototype.isStartOfExpression.call(this) || this.match('<'); + }; + return JSXParser; + }(parser_1.Parser)); + exports.JSXParser = JSXParser; + + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + // See also tools/generate-unicode-regex.js. + var Regex = { + // Unicode v8.0.0 NonAsciiIdentifierStart: + NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/, + // Unicode v8.0.0 NonAsciiIdentifierPart: + NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ + }; + exports.Character = { + /* tslint:disable:no-bitwise */ + fromCodePoint: function (cp) { + return (cp < 0x10000) ? String.fromCharCode(cp) : + String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) + + String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023)); + }, + // https://tc39.github.io/ecma262/#sec-white-space + isWhiteSpace: function (cp) { + return (cp === 0x20) || (cp === 0x09) || (cp === 0x0B) || (cp === 0x0C) || (cp === 0xA0) || + (cp >= 0x1680 && [0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(cp) >= 0); + }, + // https://tc39.github.io/ecma262/#sec-line-terminators + isLineTerminator: function (cp) { + return (cp === 0x0A) || (cp === 0x0D) || (cp === 0x2028) || (cp === 0x2029); + }, + // https://tc39.github.io/ecma262/#sec-names-and-keywords + isIdentifierStart: function (cp) { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp === 0x5C) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierStart.test(exports.Character.fromCodePoint(cp))); + }, + isIdentifierPart: function (cp) { + return (cp === 0x24) || (cp === 0x5F) || + (cp >= 0x41 && cp <= 0x5A) || + (cp >= 0x61 && cp <= 0x7A) || + (cp >= 0x30 && cp <= 0x39) || + (cp === 0x5C) || + ((cp >= 0x80) && Regex.NonAsciiIdentifierPart.test(exports.Character.fromCodePoint(cp))); + }, + // https://tc39.github.io/ecma262/#sec-literals-numeric-literals + isDecimalDigit: function (cp) { + return (cp >= 0x30 && cp <= 0x39); // 0..9 + }, + isHexDigit: function (cp) { + return (cp >= 0x30 && cp <= 0x39) || + (cp >= 0x41 && cp <= 0x46) || + (cp >= 0x61 && cp <= 0x66); // a..f + }, + isOctalDigit: function (cp) { + return (cp >= 0x30 && cp <= 0x37); // 0..7 + } + }; + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var jsx_syntax_1 = __webpack_require__(6); + /* tslint:disable:max-classes-per-file */ + var JSXClosingElement = (function () { + function JSXClosingElement(name) { + this.type = jsx_syntax_1.JSXSyntax.JSXClosingElement; + this.name = name; + } + return JSXClosingElement; + }()); + exports.JSXClosingElement = JSXClosingElement; + var JSXElement = (function () { + function JSXElement(openingElement, children, closingElement) { + this.type = jsx_syntax_1.JSXSyntax.JSXElement; + this.openingElement = openingElement; + this.children = children; + this.closingElement = closingElement; + } + return JSXElement; + }()); + exports.JSXElement = JSXElement; + var JSXEmptyExpression = (function () { + function JSXEmptyExpression() { + this.type = jsx_syntax_1.JSXSyntax.JSXEmptyExpression; + } + return JSXEmptyExpression; + }()); + exports.JSXEmptyExpression = JSXEmptyExpression; + var JSXExpressionContainer = (function () { + function JSXExpressionContainer(expression) { + this.type = jsx_syntax_1.JSXSyntax.JSXExpressionContainer; + this.expression = expression; + } + return JSXExpressionContainer; + }()); + exports.JSXExpressionContainer = JSXExpressionContainer; + var JSXIdentifier = (function () { + function JSXIdentifier(name) { + this.type = jsx_syntax_1.JSXSyntax.JSXIdentifier; + this.name = name; + } + return JSXIdentifier; + }()); + exports.JSXIdentifier = JSXIdentifier; + var JSXMemberExpression = (function () { + function JSXMemberExpression(object, property) { + this.type = jsx_syntax_1.JSXSyntax.JSXMemberExpression; + this.object = object; + this.property = property; + } + return JSXMemberExpression; + }()); + exports.JSXMemberExpression = JSXMemberExpression; + var JSXAttribute = (function () { + function JSXAttribute(name, value) { + this.type = jsx_syntax_1.JSXSyntax.JSXAttribute; + this.name = name; + this.value = value; + } + return JSXAttribute; + }()); + exports.JSXAttribute = JSXAttribute; + var JSXNamespacedName = (function () { + function JSXNamespacedName(namespace, name) { + this.type = jsx_syntax_1.JSXSyntax.JSXNamespacedName; + this.namespace = namespace; + this.name = name; + } + return JSXNamespacedName; + }()); + exports.JSXNamespacedName = JSXNamespacedName; + var JSXOpeningElement = (function () { + function JSXOpeningElement(name, selfClosing, attributes) { + this.type = jsx_syntax_1.JSXSyntax.JSXOpeningElement; + this.name = name; + this.selfClosing = selfClosing; + this.attributes = attributes; + } + return JSXOpeningElement; + }()); + exports.JSXOpeningElement = JSXOpeningElement; + var JSXSpreadAttribute = (function () { + function JSXSpreadAttribute(argument) { + this.type = jsx_syntax_1.JSXSyntax.JSXSpreadAttribute; + this.argument = argument; + } + return JSXSpreadAttribute; + }()); + exports.JSXSpreadAttribute = JSXSpreadAttribute; + var JSXText = (function () { + function JSXText(value, raw) { + this.type = jsx_syntax_1.JSXSyntax.JSXText; + this.value = value; + this.raw = raw; + } + return JSXText; + }()); + exports.JSXText = JSXText; + + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.JSXSyntax = { + JSXAttribute: 'JSXAttribute', + JSXClosingElement: 'JSXClosingElement', + JSXElement: 'JSXElement', + JSXEmptyExpression: 'JSXEmptyExpression', + JSXExpressionContainer: 'JSXExpressionContainer', + JSXIdentifier: 'JSXIdentifier', + JSXMemberExpression: 'JSXMemberExpression', + JSXNamespacedName: 'JSXNamespacedName', + JSXOpeningElement: 'JSXOpeningElement', + JSXSpreadAttribute: 'JSXSpreadAttribute', + JSXText: 'JSXText' + }; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var syntax_1 = __webpack_require__(2); + /* tslint:disable:max-classes-per-file */ + var ArrayExpression = (function () { + function ArrayExpression(elements) { + this.type = syntax_1.Syntax.ArrayExpression; + this.elements = elements; + } + return ArrayExpression; + }()); + exports.ArrayExpression = ArrayExpression; + var ArrayPattern = (function () { + function ArrayPattern(elements) { + this.type = syntax_1.Syntax.ArrayPattern; + this.elements = elements; + } + return ArrayPattern; + }()); + exports.ArrayPattern = ArrayPattern; + var ArrowFunctionExpression = (function () { + function ArrowFunctionExpression(params, body, expression) { + this.type = syntax_1.Syntax.ArrowFunctionExpression; + this.id = null; + this.params = params; + this.body = body; + this.generator = false; + this.expression = expression; + this.async = false; + } + return ArrowFunctionExpression; + }()); + exports.ArrowFunctionExpression = ArrowFunctionExpression; + var AssignmentExpression = (function () { + function AssignmentExpression(operator, left, right) { + this.type = syntax_1.Syntax.AssignmentExpression; + this.operator = operator; + this.left = left; + this.right = right; + } + return AssignmentExpression; + }()); + exports.AssignmentExpression = AssignmentExpression; + var AssignmentPattern = (function () { + function AssignmentPattern(left, right) { + this.type = syntax_1.Syntax.AssignmentPattern; + this.left = left; + this.right = right; + } + return AssignmentPattern; + }()); + exports.AssignmentPattern = AssignmentPattern; + var AsyncArrowFunctionExpression = (function () { + function AsyncArrowFunctionExpression(params, body, expression) { + this.type = syntax_1.Syntax.ArrowFunctionExpression; + this.id = null; + this.params = params; + this.body = body; + this.generator = false; + this.expression = expression; + this.async = true; + } + return AsyncArrowFunctionExpression; + }()); + exports.AsyncArrowFunctionExpression = AsyncArrowFunctionExpression; + var AsyncFunctionDeclaration = (function () { + function AsyncFunctionDeclaration(id, params, body) { + this.type = syntax_1.Syntax.FunctionDeclaration; + this.id = id; + this.params = params; + this.body = body; + this.generator = false; + this.expression = false; + this.async = true; + } + return AsyncFunctionDeclaration; + }()); + exports.AsyncFunctionDeclaration = AsyncFunctionDeclaration; + var AsyncFunctionExpression = (function () { + function AsyncFunctionExpression(id, params, body) { + this.type = syntax_1.Syntax.FunctionExpression; + this.id = id; + this.params = params; + this.body = body; + this.generator = false; + this.expression = false; + this.async = true; + } + return AsyncFunctionExpression; + }()); + exports.AsyncFunctionExpression = AsyncFunctionExpression; + var AwaitExpression = (function () { + function AwaitExpression(argument) { + this.type = syntax_1.Syntax.AwaitExpression; + this.argument = argument; + } + return AwaitExpression; + }()); + exports.AwaitExpression = AwaitExpression; + var BinaryExpression = (function () { + function BinaryExpression(operator, left, right) { + var logical = (operator === '||' || operator === '&&'); + this.type = logical ? syntax_1.Syntax.LogicalExpression : syntax_1.Syntax.BinaryExpression; + this.operator = operator; + this.left = left; + this.right = right; + } + return BinaryExpression; + }()); + exports.BinaryExpression = BinaryExpression; + var BlockStatement = (function () { + function BlockStatement(body) { + this.type = syntax_1.Syntax.BlockStatement; + this.body = body; + } + return BlockStatement; + }()); + exports.BlockStatement = BlockStatement; + var BreakStatement = (function () { + function BreakStatement(label) { + this.type = syntax_1.Syntax.BreakStatement; + this.label = label; + } + return BreakStatement; + }()); + exports.BreakStatement = BreakStatement; + var CallExpression = (function () { + function CallExpression(callee, args) { + this.type = syntax_1.Syntax.CallExpression; + this.callee = callee; + this.arguments = args; + } + return CallExpression; + }()); + exports.CallExpression = CallExpression; + var CatchClause = (function () { + function CatchClause(param, body) { + this.type = syntax_1.Syntax.CatchClause; + this.param = param; + this.body = body; + } + return CatchClause; + }()); + exports.CatchClause = CatchClause; + var ClassBody = (function () { + function ClassBody(body) { + this.type = syntax_1.Syntax.ClassBody; + this.body = body; + } + return ClassBody; + }()); + exports.ClassBody = ClassBody; + var ClassDeclaration = (function () { + function ClassDeclaration(id, superClass, body) { + this.type = syntax_1.Syntax.ClassDeclaration; + this.id = id; + this.superClass = superClass; + this.body = body; + } + return ClassDeclaration; + }()); + exports.ClassDeclaration = ClassDeclaration; + var ClassExpression = (function () { + function ClassExpression(id, superClass, body) { + this.type = syntax_1.Syntax.ClassExpression; + this.id = id; + this.superClass = superClass; + this.body = body; + } + return ClassExpression; + }()); + exports.ClassExpression = ClassExpression; + var ComputedMemberExpression = (function () { + function ComputedMemberExpression(object, property) { + this.type = syntax_1.Syntax.MemberExpression; + this.computed = true; + this.object = object; + this.property = property; + } + return ComputedMemberExpression; + }()); + exports.ComputedMemberExpression = ComputedMemberExpression; + var ConditionalExpression = (function () { + function ConditionalExpression(test, consequent, alternate) { + this.type = syntax_1.Syntax.ConditionalExpression; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } + return ConditionalExpression; + }()); + exports.ConditionalExpression = ConditionalExpression; + var ContinueStatement = (function () { + function ContinueStatement(label) { + this.type = syntax_1.Syntax.ContinueStatement; + this.label = label; + } + return ContinueStatement; + }()); + exports.ContinueStatement = ContinueStatement; + var DebuggerStatement = (function () { + function DebuggerStatement() { + this.type = syntax_1.Syntax.DebuggerStatement; + } + return DebuggerStatement; + }()); + exports.DebuggerStatement = DebuggerStatement; + var Directive = (function () { + function Directive(expression, directive) { + this.type = syntax_1.Syntax.ExpressionStatement; + this.expression = expression; + this.directive = directive; + } + return Directive; + }()); + exports.Directive = Directive; + var DoWhileStatement = (function () { + function DoWhileStatement(body, test) { + this.type = syntax_1.Syntax.DoWhileStatement; + this.body = body; + this.test = test; + } + return DoWhileStatement; + }()); + exports.DoWhileStatement = DoWhileStatement; + var EmptyStatement = (function () { + function EmptyStatement() { + this.type = syntax_1.Syntax.EmptyStatement; + } + return EmptyStatement; + }()); + exports.EmptyStatement = EmptyStatement; + var ExportAllDeclaration = (function () { + function ExportAllDeclaration(source) { + this.type = syntax_1.Syntax.ExportAllDeclaration; + this.source = source; + } + return ExportAllDeclaration; + }()); + exports.ExportAllDeclaration = ExportAllDeclaration; + var ExportDefaultDeclaration = (function () { + function ExportDefaultDeclaration(declaration) { + this.type = syntax_1.Syntax.ExportDefaultDeclaration; + this.declaration = declaration; + } + return ExportDefaultDeclaration; + }()); + exports.ExportDefaultDeclaration = ExportDefaultDeclaration; + var ExportNamedDeclaration = (function () { + function ExportNamedDeclaration(declaration, specifiers, source) { + this.type = syntax_1.Syntax.ExportNamedDeclaration; + this.declaration = declaration; + this.specifiers = specifiers; + this.source = source; + } + return ExportNamedDeclaration; + }()); + exports.ExportNamedDeclaration = ExportNamedDeclaration; + var ExportSpecifier = (function () { + function ExportSpecifier(local, exported) { + this.type = syntax_1.Syntax.ExportSpecifier; + this.exported = exported; + this.local = local; + } + return ExportSpecifier; + }()); + exports.ExportSpecifier = ExportSpecifier; + var ExpressionStatement = (function () { + function ExpressionStatement(expression) { + this.type = syntax_1.Syntax.ExpressionStatement; + this.expression = expression; + } + return ExpressionStatement; + }()); + exports.ExpressionStatement = ExpressionStatement; + var ForInStatement = (function () { + function ForInStatement(left, right, body) { + this.type = syntax_1.Syntax.ForInStatement; + this.left = left; + this.right = right; + this.body = body; + this.each = false; + } + return ForInStatement; + }()); + exports.ForInStatement = ForInStatement; + var ForOfStatement = (function () { + function ForOfStatement(left, right, body) { + this.type = syntax_1.Syntax.ForOfStatement; + this.left = left; + this.right = right; + this.body = body; + } + return ForOfStatement; + }()); + exports.ForOfStatement = ForOfStatement; + var ForStatement = (function () { + function ForStatement(init, test, update, body) { + this.type = syntax_1.Syntax.ForStatement; + this.init = init; + this.test = test; + this.update = update; + this.body = body; + } + return ForStatement; + }()); + exports.ForStatement = ForStatement; + var FunctionDeclaration = (function () { + function FunctionDeclaration(id, params, body, generator) { + this.type = syntax_1.Syntax.FunctionDeclaration; + this.id = id; + this.params = params; + this.body = body; + this.generator = generator; + this.expression = false; + this.async = false; + } + return FunctionDeclaration; + }()); + exports.FunctionDeclaration = FunctionDeclaration; + var FunctionExpression = (function () { + function FunctionExpression(id, params, body, generator) { + this.type = syntax_1.Syntax.FunctionExpression; + this.id = id; + this.params = params; + this.body = body; + this.generator = generator; + this.expression = false; + this.async = false; + } + return FunctionExpression; + }()); + exports.FunctionExpression = FunctionExpression; + var Identifier = (function () { + function Identifier(name) { + this.type = syntax_1.Syntax.Identifier; + this.name = name; + } + return Identifier; + }()); + exports.Identifier = Identifier; + var IfStatement = (function () { + function IfStatement(test, consequent, alternate) { + this.type = syntax_1.Syntax.IfStatement; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } + return IfStatement; + }()); + exports.IfStatement = IfStatement; + var ImportDeclaration = (function () { + function ImportDeclaration(specifiers, source) { + this.type = syntax_1.Syntax.ImportDeclaration; + this.specifiers = specifiers; + this.source = source; + } + return ImportDeclaration; + }()); + exports.ImportDeclaration = ImportDeclaration; + var ImportDefaultSpecifier = (function () { + function ImportDefaultSpecifier(local) { + this.type = syntax_1.Syntax.ImportDefaultSpecifier; + this.local = local; + } + return ImportDefaultSpecifier; + }()); + exports.ImportDefaultSpecifier = ImportDefaultSpecifier; + var ImportNamespaceSpecifier = (function () { + function ImportNamespaceSpecifier(local) { + this.type = syntax_1.Syntax.ImportNamespaceSpecifier; + this.local = local; + } + return ImportNamespaceSpecifier; + }()); + exports.ImportNamespaceSpecifier = ImportNamespaceSpecifier; + var ImportSpecifier = (function () { + function ImportSpecifier(local, imported) { + this.type = syntax_1.Syntax.ImportSpecifier; + this.local = local; + this.imported = imported; + } + return ImportSpecifier; + }()); + exports.ImportSpecifier = ImportSpecifier; + var LabeledStatement = (function () { + function LabeledStatement(label, body) { + this.type = syntax_1.Syntax.LabeledStatement; + this.label = label; + this.body = body; + } + return LabeledStatement; + }()); + exports.LabeledStatement = LabeledStatement; + var Literal = (function () { + function Literal(value, raw) { + this.type = syntax_1.Syntax.Literal; + this.value = value; + this.raw = raw; + } + return Literal; + }()); + exports.Literal = Literal; + var MetaProperty = (function () { + function MetaProperty(meta, property) { + this.type = syntax_1.Syntax.MetaProperty; + this.meta = meta; + this.property = property; + } + return MetaProperty; + }()); + exports.MetaProperty = MetaProperty; + var MethodDefinition = (function () { + function MethodDefinition(key, computed, value, kind, isStatic) { + this.type = syntax_1.Syntax.MethodDefinition; + this.key = key; + this.computed = computed; + this.value = value; + this.kind = kind; + this.static = isStatic; + } + return MethodDefinition; + }()); + exports.MethodDefinition = MethodDefinition; + var Module = (function () { + function Module(body) { + this.type = syntax_1.Syntax.Program; + this.body = body; + this.sourceType = 'module'; + } + return Module; + }()); + exports.Module = Module; + var NewExpression = (function () { + function NewExpression(callee, args) { + this.type = syntax_1.Syntax.NewExpression; + this.callee = callee; + this.arguments = args; + } + return NewExpression; + }()); + exports.NewExpression = NewExpression; + var ObjectExpression = (function () { + function ObjectExpression(properties) { + this.type = syntax_1.Syntax.ObjectExpression; + this.properties = properties; + } + return ObjectExpression; + }()); + exports.ObjectExpression = ObjectExpression; + var ObjectPattern = (function () { + function ObjectPattern(properties) { + this.type = syntax_1.Syntax.ObjectPattern; + this.properties = properties; + } + return ObjectPattern; + }()); + exports.ObjectPattern = ObjectPattern; + var Property = (function () { + function Property(kind, key, computed, value, method, shorthand) { + this.type = syntax_1.Syntax.Property; + this.key = key; + this.computed = computed; + this.value = value; + this.kind = kind; + this.method = method; + this.shorthand = shorthand; + } + return Property; + }()); + exports.Property = Property; + var RegexLiteral = (function () { + function RegexLiteral(value, raw, pattern, flags) { + this.type = syntax_1.Syntax.Literal; + this.value = value; + this.raw = raw; + this.regex = { pattern: pattern, flags: flags }; + } + return RegexLiteral; + }()); + exports.RegexLiteral = RegexLiteral; + var RestElement = (function () { + function RestElement(argument) { + this.type = syntax_1.Syntax.RestElement; + this.argument = argument; + } + return RestElement; + }()); + exports.RestElement = RestElement; + var ReturnStatement = (function () { + function ReturnStatement(argument) { + this.type = syntax_1.Syntax.ReturnStatement; + this.argument = argument; + } + return ReturnStatement; + }()); + exports.ReturnStatement = ReturnStatement; + var Script = (function () { + function Script(body) { + this.type = syntax_1.Syntax.Program; + this.body = body; + this.sourceType = 'script'; + } + return Script; + }()); + exports.Script = Script; + var SequenceExpression = (function () { + function SequenceExpression(expressions) { + this.type = syntax_1.Syntax.SequenceExpression; + this.expressions = expressions; + } + return SequenceExpression; + }()); + exports.SequenceExpression = SequenceExpression; + var SpreadElement = (function () { + function SpreadElement(argument) { + this.type = syntax_1.Syntax.SpreadElement; + this.argument = argument; + } + return SpreadElement; + }()); + exports.SpreadElement = SpreadElement; + var StaticMemberExpression = (function () { + function StaticMemberExpression(object, property) { + this.type = syntax_1.Syntax.MemberExpression; + this.computed = false; + this.object = object; + this.property = property; + } + return StaticMemberExpression; + }()); + exports.StaticMemberExpression = StaticMemberExpression; + var Super = (function () { + function Super() { + this.type = syntax_1.Syntax.Super; + } + return Super; + }()); + exports.Super = Super; + var SwitchCase = (function () { + function SwitchCase(test, consequent) { + this.type = syntax_1.Syntax.SwitchCase; + this.test = test; + this.consequent = consequent; + } + return SwitchCase; + }()); + exports.SwitchCase = SwitchCase; + var SwitchStatement = (function () { + function SwitchStatement(discriminant, cases) { + this.type = syntax_1.Syntax.SwitchStatement; + this.discriminant = discriminant; + this.cases = cases; + } + return SwitchStatement; + }()); + exports.SwitchStatement = SwitchStatement; + var TaggedTemplateExpression = (function () { + function TaggedTemplateExpression(tag, quasi) { + this.type = syntax_1.Syntax.TaggedTemplateExpression; + this.tag = tag; + this.quasi = quasi; + } + return TaggedTemplateExpression; + }()); + exports.TaggedTemplateExpression = TaggedTemplateExpression; + var TemplateElement = (function () { + function TemplateElement(value, tail) { + this.type = syntax_1.Syntax.TemplateElement; + this.value = value; + this.tail = tail; + } + return TemplateElement; + }()); + exports.TemplateElement = TemplateElement; + var TemplateLiteral = (function () { + function TemplateLiteral(quasis, expressions) { + this.type = syntax_1.Syntax.TemplateLiteral; + this.quasis = quasis; + this.expressions = expressions; + } + return TemplateLiteral; + }()); + exports.TemplateLiteral = TemplateLiteral; + var ThisExpression = (function () { + function ThisExpression() { + this.type = syntax_1.Syntax.ThisExpression; + } + return ThisExpression; + }()); + exports.ThisExpression = ThisExpression; + var ThrowStatement = (function () { + function ThrowStatement(argument) { + this.type = syntax_1.Syntax.ThrowStatement; + this.argument = argument; + } + return ThrowStatement; + }()); + exports.ThrowStatement = ThrowStatement; + var TryStatement = (function () { + function TryStatement(block, handler, finalizer) { + this.type = syntax_1.Syntax.TryStatement; + this.block = block; + this.handler = handler; + this.finalizer = finalizer; + } + return TryStatement; + }()); + exports.TryStatement = TryStatement; + var UnaryExpression = (function () { + function UnaryExpression(operator, argument) { + this.type = syntax_1.Syntax.UnaryExpression; + this.operator = operator; + this.argument = argument; + this.prefix = true; + } + return UnaryExpression; + }()); + exports.UnaryExpression = UnaryExpression; + var UpdateExpression = (function () { + function UpdateExpression(operator, argument, prefix) { + this.type = syntax_1.Syntax.UpdateExpression; + this.operator = operator; + this.argument = argument; + this.prefix = prefix; + } + return UpdateExpression; + }()); + exports.UpdateExpression = UpdateExpression; + var VariableDeclaration = (function () { + function VariableDeclaration(declarations, kind) { + this.type = syntax_1.Syntax.VariableDeclaration; + this.declarations = declarations; + this.kind = kind; + } + return VariableDeclaration; + }()); + exports.VariableDeclaration = VariableDeclaration; + var VariableDeclarator = (function () { + function VariableDeclarator(id, init) { + this.type = syntax_1.Syntax.VariableDeclarator; + this.id = id; + this.init = init; + } + return VariableDeclarator; + }()); + exports.VariableDeclarator = VariableDeclarator; + var WhileStatement = (function () { + function WhileStatement(test, body) { + this.type = syntax_1.Syntax.WhileStatement; + this.test = test; + this.body = body; + } + return WhileStatement; + }()); + exports.WhileStatement = WhileStatement; + var WithStatement = (function () { + function WithStatement(object, body) { + this.type = syntax_1.Syntax.WithStatement; + this.object = object; + this.body = body; + } + return WithStatement; + }()); + exports.WithStatement = WithStatement; + var YieldExpression = (function () { + function YieldExpression(argument, delegate) { + this.type = syntax_1.Syntax.YieldExpression; + this.argument = argument; + this.delegate = delegate; + } + return YieldExpression; + }()); + exports.YieldExpression = YieldExpression; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var assert_1 = __webpack_require__(9); + var error_handler_1 = __webpack_require__(10); + var messages_1 = __webpack_require__(11); + var Node = __webpack_require__(7); + var scanner_1 = __webpack_require__(12); + var syntax_1 = __webpack_require__(2); + var token_1 = __webpack_require__(13); + var ArrowParameterPlaceHolder = 'ArrowParameterPlaceHolder'; + var Parser = (function () { + function Parser(code, options, delegate) { + if (options === void 0) { options = {}; } + this.config = { + range: (typeof options.range === 'boolean') && options.range, + loc: (typeof options.loc === 'boolean') && options.loc, + source: null, + tokens: (typeof options.tokens === 'boolean') && options.tokens, + comment: (typeof options.comment === 'boolean') && options.comment, + tolerant: (typeof options.tolerant === 'boolean') && options.tolerant + }; + if (this.config.loc && options.source && options.source !== null) { + this.config.source = String(options.source); + } + this.delegate = delegate; + this.errorHandler = new error_handler_1.ErrorHandler(); + this.errorHandler.tolerant = this.config.tolerant; + this.scanner = new scanner_1.Scanner(code, this.errorHandler); + this.scanner.trackComment = this.config.comment; + this.operatorPrecedence = { + ')': 0, + ';': 0, + ',': 0, + '=': 0, + ']': 0, + '||': 1, + '&&': 2, + '|': 3, + '^': 4, + '&': 5, + '==': 6, + '!=': 6, + '===': 6, + '!==': 6, + '<': 7, + '>': 7, + '<=': 7, + '>=': 7, + '<<': 8, + '>>': 8, + '>>>': 8, + '+': 9, + '-': 9, + '*': 11, + '/': 11, + '%': 11 + }; + this.lookahead = { + type: 2 /* EOF */, + value: '', + lineNumber: this.scanner.lineNumber, + lineStart: 0, + start: 0, + end: 0 + }; + this.hasLineTerminator = false; + this.context = { + isModule: false, + await: false, + allowIn: true, + allowStrictDirective: true, + allowYield: true, + firstCoverInitializedNameError: null, + isAssignmentTarget: false, + isBindingElement: false, + inFunctionBody: false, + inIteration: false, + inSwitch: false, + labelSet: {}, + strict: false + }; + this.tokens = []; + this.startMarker = { + index: 0, + line: this.scanner.lineNumber, + column: 0 + }; + this.lastMarker = { + index: 0, + line: this.scanner.lineNumber, + column: 0 + }; + this.nextToken(); + this.lastMarker = { + index: this.scanner.index, + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + }; + } + Parser.prototype.throwError = function (messageFormat) { + var values = []; + for (var _i = 1; _i < arguments.length; _i++) { + values[_i - 1] = arguments[_i]; + } + var args = Array.prototype.slice.call(arguments, 1); + var msg = messageFormat.replace(/%(\d)/g, function (whole, idx) { + assert_1.assert(idx < args.length, 'Message reference must be in range'); + return args[idx]; + }); + var index = this.lastMarker.index; + var line = this.lastMarker.line; + var column = this.lastMarker.column + 1; + throw this.errorHandler.createError(index, line, column, msg); + }; + Parser.prototype.tolerateError = function (messageFormat) { + var values = []; + for (var _i = 1; _i < arguments.length; _i++) { + values[_i - 1] = arguments[_i]; + } + var args = Array.prototype.slice.call(arguments, 1); + var msg = messageFormat.replace(/%(\d)/g, function (whole, idx) { + assert_1.assert(idx < args.length, 'Message reference must be in range'); + return args[idx]; + }); + var index = this.lastMarker.index; + var line = this.scanner.lineNumber; + var column = this.lastMarker.column + 1; + this.errorHandler.tolerateError(index, line, column, msg); + }; + // Throw an exception because of the token. + Parser.prototype.unexpectedTokenError = function (token, message) { + var msg = message || messages_1.Messages.UnexpectedToken; + var value; + if (token) { + if (!message) { + msg = (token.type === 2 /* EOF */) ? messages_1.Messages.UnexpectedEOS : + (token.type === 3 /* Identifier */) ? messages_1.Messages.UnexpectedIdentifier : + (token.type === 6 /* NumericLiteral */) ? messages_1.Messages.UnexpectedNumber : + (token.type === 8 /* StringLiteral */) ? messages_1.Messages.UnexpectedString : + (token.type === 10 /* Template */) ? messages_1.Messages.UnexpectedTemplate : + messages_1.Messages.UnexpectedToken; + if (token.type === 4 /* Keyword */) { + if (this.scanner.isFutureReservedWord(token.value)) { + msg = messages_1.Messages.UnexpectedReserved; + } + else if (this.context.strict && this.scanner.isStrictModeReservedWord(token.value)) { + msg = messages_1.Messages.StrictReservedWord; + } + } + } + value = token.value; + } + else { + value = 'ILLEGAL'; + } + msg = msg.replace('%0', value); + if (token && typeof token.lineNumber === 'number') { + var index = token.start; + var line = token.lineNumber; + var lastMarkerLineStart = this.lastMarker.index - this.lastMarker.column; + var column = token.start - lastMarkerLineStart + 1; + return this.errorHandler.createError(index, line, column, msg); + } + else { + var index = this.lastMarker.index; + var line = this.lastMarker.line; + var column = this.lastMarker.column + 1; + return this.errorHandler.createError(index, line, column, msg); + } + }; + Parser.prototype.throwUnexpectedToken = function (token, message) { + throw this.unexpectedTokenError(token, message); + }; + Parser.prototype.tolerateUnexpectedToken = function (token, message) { + this.errorHandler.tolerate(this.unexpectedTokenError(token, message)); + }; + Parser.prototype.collectComments = function () { + if (!this.config.comment) { + this.scanner.scanComments(); + } + else { + var comments = this.scanner.scanComments(); + if (comments.length > 0 && this.delegate) { + for (var i = 0; i < comments.length; ++i) { + var e = comments[i]; + var node = void 0; + node = { + type: e.multiLine ? 'BlockComment' : 'LineComment', + value: this.scanner.source.slice(e.slice[0], e.slice[1]) + }; + if (this.config.range) { + node.range = e.range; + } + if (this.config.loc) { + node.loc = e.loc; + } + var metadata = { + start: { + line: e.loc.start.line, + column: e.loc.start.column, + offset: e.range[0] + }, + end: { + line: e.loc.end.line, + column: e.loc.end.column, + offset: e.range[1] + } + }; + this.delegate(node, metadata); + } + } + } + }; + // From internal representation to an external structure + Parser.prototype.getTokenRaw = function (token) { + return this.scanner.source.slice(token.start, token.end); + }; + Parser.prototype.convertToken = function (token) { + var t = { + type: token_1.TokenName[token.type], + value: this.getTokenRaw(token) + }; + if (this.config.range) { + t.range = [token.start, token.end]; + } + if (this.config.loc) { + t.loc = { + start: { + line: this.startMarker.line, + column: this.startMarker.column + }, + end: { + line: this.scanner.lineNumber, + column: this.scanner.index - this.scanner.lineStart + } + }; + } + if (token.type === 9 /* RegularExpression */) { + var pattern = token.pattern; + var flags = token.flags; + t.regex = { pattern: pattern, flags: flags }; + } + return t; + }; + Parser.prototype.nextToken = function () { + var token = this.lookahead; + this.lastMarker.index = this.scanner.index; + this.lastMarker.line = this.scanner.lineNumber; + this.lastMarker.column = this.scanner.index - this.scanner.lineStart; + this.collectComments(); + if (this.scanner.index !== this.startMarker.index) { + this.startMarker.index = this.scanner.index; + this.startMarker.line = this.scanner.lineNumber; + this.startMarker.column = this.scanner.index - this.scanner.lineStart; + } + var next = this.scanner.lex(); + this.hasLineTerminator = (token.lineNumber !== next.lineNumber); + if (next && this.context.strict && next.type === 3 /* Identifier */) { + if (this.scanner.isStrictModeReservedWord(next.value)) { + next.type = 4 /* Keyword */; + } + } + this.lookahead = next; + if (this.config.tokens && next.type !== 2 /* EOF */) { + this.tokens.push(this.convertToken(next)); + } + return token; + }; + Parser.prototype.nextRegexToken = function () { + this.collectComments(); + var token = this.scanner.scanRegExp(); + if (this.config.tokens) { + // Pop the previous token, '/' or '/=' + // This is added from the lookahead token. + this.tokens.pop(); + this.tokens.push(this.convertToken(token)); + } + // Prime the next lookahead. + this.lookahead = token; + this.nextToken(); + return token; + }; + Parser.prototype.createNode = function () { + return { + index: this.startMarker.index, + line: this.startMarker.line, + column: this.startMarker.column + }; + }; + Parser.prototype.startNode = function (token, lastLineStart) { + if (lastLineStart === void 0) { lastLineStart = 0; } + var column = token.start - token.lineStart; + var line = token.lineNumber; + if (column < 0) { + column += lastLineStart; + line--; + } + return { + index: token.start, + line: line, + column: column + }; + }; + Parser.prototype.finalize = function (marker, node) { + if (this.config.range) { + node.range = [marker.index, this.lastMarker.index]; + } + if (this.config.loc) { + node.loc = { + start: { + line: marker.line, + column: marker.column, + }, + end: { + line: this.lastMarker.line, + column: this.lastMarker.column + } + }; + if (this.config.source) { + node.loc.source = this.config.source; + } + } + if (this.delegate) { + var metadata = { + start: { + line: marker.line, + column: marker.column, + offset: marker.index + }, + end: { + line: this.lastMarker.line, + column: this.lastMarker.column, + offset: this.lastMarker.index + } + }; + this.delegate(node, metadata); + } + return node; + }; + // Expect the next token to match the specified punctuator. + // If not, an exception will be thrown. + Parser.prototype.expect = function (value) { + var token = this.nextToken(); + if (token.type !== 7 /* Punctuator */ || token.value !== value) { + this.throwUnexpectedToken(token); + } + }; + // Quietly expect a comma when in tolerant mode, otherwise delegates to expect(). + Parser.prototype.expectCommaSeparator = function () { + if (this.config.tolerant) { + var token = this.lookahead; + if (token.type === 7 /* Punctuator */ && token.value === ',') { + this.nextToken(); + } + else if (token.type === 7 /* Punctuator */ && token.value === ';') { + this.nextToken(); + this.tolerateUnexpectedToken(token); + } + else { + this.tolerateUnexpectedToken(token, messages_1.Messages.UnexpectedToken); + } + } + else { + this.expect(','); + } + }; + // Expect the next token to match the specified keyword. + // If not, an exception will be thrown. + Parser.prototype.expectKeyword = function (keyword) { + var token = this.nextToken(); + if (token.type !== 4 /* Keyword */ || token.value !== keyword) { + this.throwUnexpectedToken(token); + } + }; + // Return true if the next token matches the specified punctuator. + Parser.prototype.match = function (value) { + return this.lookahead.type === 7 /* Punctuator */ && this.lookahead.value === value; + }; + // Return true if the next token matches the specified keyword + Parser.prototype.matchKeyword = function (keyword) { + return this.lookahead.type === 4 /* Keyword */ && this.lookahead.value === keyword; + }; + // Return true if the next token matches the specified contextual keyword + // (where an identifier is sometimes a keyword depending on the context) + Parser.prototype.matchContextualKeyword = function (keyword) { + return this.lookahead.type === 3 /* Identifier */ && this.lookahead.value === keyword; + }; + // Return true if the next token is an assignment operator + Parser.prototype.matchAssign = function () { + if (this.lookahead.type !== 7 /* Punctuator */) { + return false; + } + var op = this.lookahead.value; + return op === '=' || + op === '*=' || + op === '**=' || + op === '/=' || + op === '%=' || + op === '+=' || + op === '-=' || + op === '<<=' || + op === '>>=' || + op === '>>>=' || + op === '&=' || + op === '^=' || + op === '|='; + }; + // Cover grammar support. + // + // When an assignment expression position starts with an left parenthesis, the determination of the type + // of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead) + // or the first comma. This situation also defers the determination of all the expressions nested in the pair. + // + // There are three productions that can be parsed in a parentheses pair that needs to be determined + // after the outermost pair is closed. They are: + // + // 1. AssignmentExpression + // 2. BindingElements + // 3. AssignmentTargets + // + // In order to avoid exponential backtracking, we use two flags to denote if the production can be + // binding element or assignment target. + // + // The three productions have the relationship: + // + // BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression + // + // with a single exception that CoverInitializedName when used directly in an Expression, generates + // an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the + // first usage of CoverInitializedName and report it when we reached the end of the parentheses pair. + // + // isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not + // effect the current flags. This means the production the parser parses is only used as an expression. Therefore + // the CoverInitializedName check is conducted. + // + // inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates + // the flags outside of the parser. This means the production the parser parses is used as a part of a potential + // pattern. The CoverInitializedName check is deferred. + Parser.prototype.isolateCoverGrammar = function (parseFunction) { + var previousIsBindingElement = this.context.isBindingElement; + var previousIsAssignmentTarget = this.context.isAssignmentTarget; + var previousFirstCoverInitializedNameError = this.context.firstCoverInitializedNameError; + this.context.isBindingElement = true; + this.context.isAssignmentTarget = true; + this.context.firstCoverInitializedNameError = null; + var result = parseFunction.call(this); + if (this.context.firstCoverInitializedNameError !== null) { + this.throwUnexpectedToken(this.context.firstCoverInitializedNameError); + } + this.context.isBindingElement = previousIsBindingElement; + this.context.isAssignmentTarget = previousIsAssignmentTarget; + this.context.firstCoverInitializedNameError = previousFirstCoverInitializedNameError; + return result; + }; + Parser.prototype.inheritCoverGrammar = function (parseFunction) { + var previousIsBindingElement = this.context.isBindingElement; + var previousIsAssignmentTarget = this.context.isAssignmentTarget; + var previousFirstCoverInitializedNameError = this.context.firstCoverInitializedNameError; + this.context.isBindingElement = true; + this.context.isAssignmentTarget = true; + this.context.firstCoverInitializedNameError = null; + var result = parseFunction.call(this); + this.context.isBindingElement = this.context.isBindingElement && previousIsBindingElement; + this.context.isAssignmentTarget = this.context.isAssignmentTarget && previousIsAssignmentTarget; + this.context.firstCoverInitializedNameError = previousFirstCoverInitializedNameError || this.context.firstCoverInitializedNameError; + return result; + }; + Parser.prototype.consumeSemicolon = function () { + if (this.match(';')) { + this.nextToken(); + } + else if (!this.hasLineTerminator) { + if (this.lookahead.type !== 2 /* EOF */ && !this.match('}')) { + this.throwUnexpectedToken(this.lookahead); + } + this.lastMarker.index = this.startMarker.index; + this.lastMarker.line = this.startMarker.line; + this.lastMarker.column = this.startMarker.column; + } + }; + // https://tc39.github.io/ecma262/#sec-primary-expression + Parser.prototype.parsePrimaryExpression = function () { + var node = this.createNode(); + var expr; + var token, raw; + switch (this.lookahead.type) { + case 3 /* Identifier */: + if ((this.context.isModule || this.context.await) && this.lookahead.value === 'await') { + this.tolerateUnexpectedToken(this.lookahead); + } + expr = this.matchAsyncFunction() ? this.parseFunctionExpression() : this.finalize(node, new Node.Identifier(this.nextToken().value)); + break; + case 6 /* NumericLiteral */: + case 8 /* StringLiteral */: + if (this.context.strict && this.lookahead.octal) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.StrictOctalLiteral); + } + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + token = this.nextToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.Literal(token.value, raw)); + break; + case 1 /* BooleanLiteral */: + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + token = this.nextToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.Literal(token.value === 'true', raw)); + break; + case 5 /* NullLiteral */: + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + token = this.nextToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.Literal(null, raw)); + break; + case 10 /* Template */: + expr = this.parseTemplateLiteral(); + break; + case 7 /* Punctuator */: + switch (this.lookahead.value) { + case '(': + this.context.isBindingElement = false; + expr = this.inheritCoverGrammar(this.parseGroupExpression); + break; + case '[': + expr = this.inheritCoverGrammar(this.parseArrayInitializer); + break; + case '{': + expr = this.inheritCoverGrammar(this.parseObjectInitializer); + break; + case '/': + case '/=': + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + this.scanner.index = this.startMarker.index; + token = this.nextRegexToken(); + raw = this.getTokenRaw(token); + expr = this.finalize(node, new Node.RegexLiteral(token.regex, raw, token.pattern, token.flags)); + break; + default: + expr = this.throwUnexpectedToken(this.nextToken()); + } + break; + case 4 /* Keyword */: + if (!this.context.strict && this.context.allowYield && this.matchKeyword('yield')) { + expr = this.parseIdentifierName(); + } + else if (!this.context.strict && this.matchKeyword('let')) { + expr = this.finalize(node, new Node.Identifier(this.nextToken().value)); + } + else { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + if (this.matchKeyword('function')) { + expr = this.parseFunctionExpression(); + } + else if (this.matchKeyword('this')) { + this.nextToken(); + expr = this.finalize(node, new Node.ThisExpression()); + } + else if (this.matchKeyword('class')) { + expr = this.parseClassExpression(); + } + else { + expr = this.throwUnexpectedToken(this.nextToken()); + } + } + break; + default: + expr = this.throwUnexpectedToken(this.nextToken()); + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-array-initializer + Parser.prototype.parseSpreadElement = function () { + var node = this.createNode(); + this.expect('...'); + var arg = this.inheritCoverGrammar(this.parseAssignmentExpression); + return this.finalize(node, new Node.SpreadElement(arg)); + }; + Parser.prototype.parseArrayInitializer = function () { + var node = this.createNode(); + var elements = []; + this.expect('['); + while (!this.match(']')) { + if (this.match(',')) { + this.nextToken(); + elements.push(null); + } + else if (this.match('...')) { + var element = this.parseSpreadElement(); + if (!this.match(']')) { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + this.expect(','); + } + elements.push(element); + } + else { + elements.push(this.inheritCoverGrammar(this.parseAssignmentExpression)); + if (!this.match(']')) { + this.expect(','); + } + } + } + this.expect(']'); + return this.finalize(node, new Node.ArrayExpression(elements)); + }; + // https://tc39.github.io/ecma262/#sec-object-initializer + Parser.prototype.parsePropertyMethod = function (params) { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = params.simple; + var body = this.isolateCoverGrammar(this.parseFunctionSourceElements); + if (this.context.strict && params.firstRestricted) { + this.tolerateUnexpectedToken(params.firstRestricted, params.message); + } + if (this.context.strict && params.stricted) { + this.tolerateUnexpectedToken(params.stricted, params.message); + } + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + return body; + }; + Parser.prototype.parsePropertyMethodFunction = function () { + var isGenerator = false; + var node = this.createNode(); + var previousAllowYield = this.context.allowYield; + this.context.allowYield = true; + var params = this.parseFormalParameters(); + var method = this.parsePropertyMethod(params); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator)); + }; + Parser.prototype.parsePropertyMethodAsyncFunction = function () { + var node = this.createNode(); + var previousAllowYield = this.context.allowYield; + var previousAwait = this.context.await; + this.context.allowYield = false; + this.context.await = true; + var params = this.parseFormalParameters(); + var method = this.parsePropertyMethod(params); + this.context.allowYield = previousAllowYield; + this.context.await = previousAwait; + return this.finalize(node, new Node.AsyncFunctionExpression(null, params.params, method)); + }; + Parser.prototype.parseObjectPropertyKey = function () { + var node = this.createNode(); + var token = this.nextToken(); + var key; + switch (token.type) { + case 8 /* StringLiteral */: + case 6 /* NumericLiteral */: + if (this.context.strict && token.octal) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictOctalLiteral); + } + var raw = this.getTokenRaw(token); + key = this.finalize(node, new Node.Literal(token.value, raw)); + break; + case 3 /* Identifier */: + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 4 /* Keyword */: + key = this.finalize(node, new Node.Identifier(token.value)); + break; + case 7 /* Punctuator */: + if (token.value === '[') { + key = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.expect(']'); + } + else { + key = this.throwUnexpectedToken(token); + } + break; + default: + key = this.throwUnexpectedToken(token); + } + return key; + }; + Parser.prototype.isPropertyKey = function (key, value) { + return (key.type === syntax_1.Syntax.Identifier && key.name === value) || + (key.type === syntax_1.Syntax.Literal && key.value === value); + }; + Parser.prototype.parseObjectProperty = function (hasProto) { + var node = this.createNode(); + var token = this.lookahead; + var kind; + var key = null; + var value = null; + var computed = false; + var method = false; + var shorthand = false; + var isAsync = false; + if (token.type === 3 /* Identifier */) { + var id = token.value; + this.nextToken(); + computed = this.match('['); + isAsync = !this.hasLineTerminator && (id === 'async') && + !this.match(':') && !this.match('(') && !this.match('*') && !this.match(','); + key = isAsync ? this.parseObjectPropertyKey() : this.finalize(node, new Node.Identifier(id)); + } + else if (this.match('*')) { + this.nextToken(); + } + else { + computed = this.match('['); + key = this.parseObjectPropertyKey(); + } + var lookaheadPropertyKey = this.qualifiedPropertyName(this.lookahead); + if (token.type === 3 /* Identifier */ && !isAsync && token.value === 'get' && lookaheadPropertyKey) { + kind = 'get'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + this.context.allowYield = false; + value = this.parseGetterMethod(); + } + else if (token.type === 3 /* Identifier */ && !isAsync && token.value === 'set' && lookaheadPropertyKey) { + kind = 'set'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseSetterMethod(); + } + else if (token.type === 7 /* Punctuator */ && token.value === '*' && lookaheadPropertyKey) { + kind = 'init'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseGeneratorMethod(); + method = true; + } + else { + if (!key) { + this.throwUnexpectedToken(this.lookahead); + } + kind = 'init'; + if (this.match(':') && !isAsync) { + if (!computed && this.isPropertyKey(key, '__proto__')) { + if (hasProto.value) { + this.tolerateError(messages_1.Messages.DuplicateProtoProperty); + } + hasProto.value = true; + } + this.nextToken(); + value = this.inheritCoverGrammar(this.parseAssignmentExpression); + } + else if (this.match('(')) { + value = isAsync ? this.parsePropertyMethodAsyncFunction() : this.parsePropertyMethodFunction(); + method = true; + } + else if (token.type === 3 /* Identifier */) { + var id = this.finalize(node, new Node.Identifier(token.value)); + if (this.match('=')) { + this.context.firstCoverInitializedNameError = this.lookahead; + this.nextToken(); + shorthand = true; + var init = this.isolateCoverGrammar(this.parseAssignmentExpression); + value = this.finalize(node, new Node.AssignmentPattern(id, init)); + } + else { + shorthand = true; + value = id; + } + } + else { + this.throwUnexpectedToken(this.nextToken()); + } + } + return this.finalize(node, new Node.Property(kind, key, computed, value, method, shorthand)); + }; + Parser.prototype.parseObjectInitializer = function () { + var node = this.createNode(); + this.expect('{'); + var properties = []; + var hasProto = { value: false }; + while (!this.match('}')) { + properties.push(this.parseObjectProperty(hasProto)); + if (!this.match('}')) { + this.expectCommaSeparator(); + } + } + this.expect('}'); + return this.finalize(node, new Node.ObjectExpression(properties)); + }; + // https://tc39.github.io/ecma262/#sec-template-literals + Parser.prototype.parseTemplateHead = function () { + assert_1.assert(this.lookahead.head, 'Template literal must start with a template head'); + var node = this.createNode(); + var token = this.nextToken(); + var raw = token.value; + var cooked = token.cooked; + return this.finalize(node, new Node.TemplateElement({ raw: raw, cooked: cooked }, token.tail)); + }; + Parser.prototype.parseTemplateElement = function () { + if (this.lookahead.type !== 10 /* Template */) { + this.throwUnexpectedToken(); + } + var node = this.createNode(); + var token = this.nextToken(); + var raw = token.value; + var cooked = token.cooked; + return this.finalize(node, new Node.TemplateElement({ raw: raw, cooked: cooked }, token.tail)); + }; + Parser.prototype.parseTemplateLiteral = function () { + var node = this.createNode(); + var expressions = []; + var quasis = []; + var quasi = this.parseTemplateHead(); + quasis.push(quasi); + while (!quasi.tail) { + expressions.push(this.parseExpression()); + quasi = this.parseTemplateElement(); + quasis.push(quasi); + } + return this.finalize(node, new Node.TemplateLiteral(quasis, expressions)); + }; + // https://tc39.github.io/ecma262/#sec-grouping-operator + Parser.prototype.reinterpretExpressionAsPattern = function (expr) { + switch (expr.type) { + case syntax_1.Syntax.Identifier: + case syntax_1.Syntax.MemberExpression: + case syntax_1.Syntax.RestElement: + case syntax_1.Syntax.AssignmentPattern: + break; + case syntax_1.Syntax.SpreadElement: + expr.type = syntax_1.Syntax.RestElement; + this.reinterpretExpressionAsPattern(expr.argument); + break; + case syntax_1.Syntax.ArrayExpression: + expr.type = syntax_1.Syntax.ArrayPattern; + for (var i = 0; i < expr.elements.length; i++) { + if (expr.elements[i] !== null) { + this.reinterpretExpressionAsPattern(expr.elements[i]); + } + } + break; + case syntax_1.Syntax.ObjectExpression: + expr.type = syntax_1.Syntax.ObjectPattern; + for (var i = 0; i < expr.properties.length; i++) { + this.reinterpretExpressionAsPattern(expr.properties[i].value); + } + break; + case syntax_1.Syntax.AssignmentExpression: + expr.type = syntax_1.Syntax.AssignmentPattern; + delete expr.operator; + this.reinterpretExpressionAsPattern(expr.left); + break; + default: + // Allow other node type for tolerant parsing. + break; + } + }; + Parser.prototype.parseGroupExpression = function () { + var expr; + this.expect('('); + if (this.match(')')) { + this.nextToken(); + if (!this.match('=>')) { + this.expect('=>'); + } + expr = { + type: ArrowParameterPlaceHolder, + params: [], + async: false + }; + } + else { + var startToken = this.lookahead; + var params = []; + if (this.match('...')) { + expr = this.parseRestElement(params); + this.expect(')'); + if (!this.match('=>')) { + this.expect('=>'); + } + expr = { + type: ArrowParameterPlaceHolder, + params: [expr], + async: false + }; + } + else { + var arrow = false; + this.context.isBindingElement = true; + expr = this.inheritCoverGrammar(this.parseAssignmentExpression); + if (this.match(',')) { + var expressions = []; + this.context.isAssignmentTarget = false; + expressions.push(expr); + while (this.lookahead.type !== 2 /* EOF */) { + if (!this.match(',')) { + break; + } + this.nextToken(); + if (this.match(')')) { + this.nextToken(); + for (var i = 0; i < expressions.length; i++) { + this.reinterpretExpressionAsPattern(expressions[i]); + } + arrow = true; + expr = { + type: ArrowParameterPlaceHolder, + params: expressions, + async: false + }; + } + else if (this.match('...')) { + if (!this.context.isBindingElement) { + this.throwUnexpectedToken(this.lookahead); + } + expressions.push(this.parseRestElement(params)); + this.expect(')'); + if (!this.match('=>')) { + this.expect('=>'); + } + this.context.isBindingElement = false; + for (var i = 0; i < expressions.length; i++) { + this.reinterpretExpressionAsPattern(expressions[i]); + } + arrow = true; + expr = { + type: ArrowParameterPlaceHolder, + params: expressions, + async: false + }; + } + else { + expressions.push(this.inheritCoverGrammar(this.parseAssignmentExpression)); + } + if (arrow) { + break; + } + } + if (!arrow) { + expr = this.finalize(this.startNode(startToken), new Node.SequenceExpression(expressions)); + } + } + if (!arrow) { + this.expect(')'); + if (this.match('=>')) { + if (expr.type === syntax_1.Syntax.Identifier && expr.name === 'yield') { + arrow = true; + expr = { + type: ArrowParameterPlaceHolder, + params: [expr], + async: false + }; + } + if (!arrow) { + if (!this.context.isBindingElement) { + this.throwUnexpectedToken(this.lookahead); + } + if (expr.type === syntax_1.Syntax.SequenceExpression) { + for (var i = 0; i < expr.expressions.length; i++) { + this.reinterpretExpressionAsPattern(expr.expressions[i]); + } + } + else { + this.reinterpretExpressionAsPattern(expr); + } + var parameters = (expr.type === syntax_1.Syntax.SequenceExpression ? expr.expressions : [expr]); + expr = { + type: ArrowParameterPlaceHolder, + params: parameters, + async: false + }; + } + } + this.context.isBindingElement = false; + } + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-left-hand-side-expressions + Parser.prototype.parseArguments = function () { + this.expect('('); + var args = []; + if (!this.match(')')) { + while (true) { + var expr = this.match('...') ? this.parseSpreadElement() : + this.isolateCoverGrammar(this.parseAssignmentExpression); + args.push(expr); + if (this.match(')')) { + break; + } + this.expectCommaSeparator(); + if (this.match(')')) { + break; + } + } + } + this.expect(')'); + return args; + }; + Parser.prototype.isIdentifierName = function (token) { + return token.type === 3 /* Identifier */ || + token.type === 4 /* Keyword */ || + token.type === 1 /* BooleanLiteral */ || + token.type === 5 /* NullLiteral */; + }; + Parser.prototype.parseIdentifierName = function () { + var node = this.createNode(); + var token = this.nextToken(); + if (!this.isIdentifierName(token)) { + this.throwUnexpectedToken(token); + } + return this.finalize(node, new Node.Identifier(token.value)); + }; + Parser.prototype.parseNewExpression = function () { + var node = this.createNode(); + var id = this.parseIdentifierName(); + assert_1.assert(id.name === 'new', 'New expression must start with `new`'); + var expr; + if (this.match('.')) { + this.nextToken(); + if (this.lookahead.type === 3 /* Identifier */ && this.context.inFunctionBody && this.lookahead.value === 'target') { + var property = this.parseIdentifierName(); + expr = new Node.MetaProperty(id, property); + } + else { + this.throwUnexpectedToken(this.lookahead); + } + } + else { + var callee = this.isolateCoverGrammar(this.parseLeftHandSideExpression); + var args = this.match('(') ? this.parseArguments() : []; + expr = new Node.NewExpression(callee, args); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + return this.finalize(node, expr); + }; + Parser.prototype.parseAsyncArgument = function () { + var arg = this.parseAssignmentExpression(); + this.context.firstCoverInitializedNameError = null; + return arg; + }; + Parser.prototype.parseAsyncArguments = function () { + this.expect('('); + var args = []; + if (!this.match(')')) { + while (true) { + var expr = this.match('...') ? this.parseSpreadElement() : + this.isolateCoverGrammar(this.parseAsyncArgument); + args.push(expr); + if (this.match(')')) { + break; + } + this.expectCommaSeparator(); + if (this.match(')')) { + break; + } + } + } + this.expect(')'); + return args; + }; + Parser.prototype.parseLeftHandSideExpressionAllowCall = function () { + var startToken = this.lookahead; + var maybeAsync = this.matchContextualKeyword('async'); + var previousAllowIn = this.context.allowIn; + this.context.allowIn = true; + var expr; + if (this.matchKeyword('super') && this.context.inFunctionBody) { + expr = this.createNode(); + this.nextToken(); + expr = this.finalize(expr, new Node.Super()); + if (!this.match('(') && !this.match('.') && !this.match('[')) { + this.throwUnexpectedToken(this.lookahead); + } + } + else { + expr = this.inheritCoverGrammar(this.matchKeyword('new') ? this.parseNewExpression : this.parsePrimaryExpression); + } + while (true) { + if (this.match('.')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('.'); + var property = this.parseIdentifierName(); + expr = this.finalize(this.startNode(startToken), new Node.StaticMemberExpression(expr, property)); + } + else if (this.match('(')) { + var asyncArrow = maybeAsync && (startToken.lineNumber === this.lookahead.lineNumber); + this.context.isBindingElement = false; + this.context.isAssignmentTarget = false; + var args = asyncArrow ? this.parseAsyncArguments() : this.parseArguments(); + expr = this.finalize(this.startNode(startToken), new Node.CallExpression(expr, args)); + if (asyncArrow && this.match('=>')) { + for (var i = 0; i < args.length; ++i) { + this.reinterpretExpressionAsPattern(args[i]); + } + expr = { + type: ArrowParameterPlaceHolder, + params: args, + async: true + }; + } + } + else if (this.match('[')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('['); + var property = this.isolateCoverGrammar(this.parseExpression); + this.expect(']'); + expr = this.finalize(this.startNode(startToken), new Node.ComputedMemberExpression(expr, property)); + } + else if (this.lookahead.type === 10 /* Template */ && this.lookahead.head) { + var quasi = this.parseTemplateLiteral(); + expr = this.finalize(this.startNode(startToken), new Node.TaggedTemplateExpression(expr, quasi)); + } + else { + break; + } + } + this.context.allowIn = previousAllowIn; + return expr; + }; + Parser.prototype.parseSuper = function () { + var node = this.createNode(); + this.expectKeyword('super'); + if (!this.match('[') && !this.match('.')) { + this.throwUnexpectedToken(this.lookahead); + } + return this.finalize(node, new Node.Super()); + }; + Parser.prototype.parseLeftHandSideExpression = function () { + assert_1.assert(this.context.allowIn, 'callee of new expression always allow in keyword.'); + var node = this.startNode(this.lookahead); + var expr = (this.matchKeyword('super') && this.context.inFunctionBody) ? this.parseSuper() : + this.inheritCoverGrammar(this.matchKeyword('new') ? this.parseNewExpression : this.parsePrimaryExpression); + while (true) { + if (this.match('[')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('['); + var property = this.isolateCoverGrammar(this.parseExpression); + this.expect(']'); + expr = this.finalize(node, new Node.ComputedMemberExpression(expr, property)); + } + else if (this.match('.')) { + this.context.isBindingElement = false; + this.context.isAssignmentTarget = true; + this.expect('.'); + var property = this.parseIdentifierName(); + expr = this.finalize(node, new Node.StaticMemberExpression(expr, property)); + } + else if (this.lookahead.type === 10 /* Template */ && this.lookahead.head) { + var quasi = this.parseTemplateLiteral(); + expr = this.finalize(node, new Node.TaggedTemplateExpression(expr, quasi)); + } + else { + break; + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-update-expressions + Parser.prototype.parseUpdateExpression = function () { + var expr; + var startToken = this.lookahead; + if (this.match('++') || this.match('--')) { + var node = this.startNode(startToken); + var token = this.nextToken(); + expr = this.inheritCoverGrammar(this.parseUnaryExpression); + if (this.context.strict && expr.type === syntax_1.Syntax.Identifier && this.scanner.isRestrictedWord(expr.name)) { + this.tolerateError(messages_1.Messages.StrictLHSPrefix); + } + if (!this.context.isAssignmentTarget) { + this.tolerateError(messages_1.Messages.InvalidLHSInAssignment); + } + var prefix = true; + expr = this.finalize(node, new Node.UpdateExpression(token.value, expr, prefix)); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + else { + expr = this.inheritCoverGrammar(this.parseLeftHandSideExpressionAllowCall); + if (!this.hasLineTerminator && this.lookahead.type === 7 /* Punctuator */) { + if (this.match('++') || this.match('--')) { + if (this.context.strict && expr.type === syntax_1.Syntax.Identifier && this.scanner.isRestrictedWord(expr.name)) { + this.tolerateError(messages_1.Messages.StrictLHSPostfix); + } + if (!this.context.isAssignmentTarget) { + this.tolerateError(messages_1.Messages.InvalidLHSInAssignment); + } + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var operator = this.nextToken().value; + var prefix = false; + expr = this.finalize(this.startNode(startToken), new Node.UpdateExpression(operator, expr, prefix)); + } + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-unary-operators + Parser.prototype.parseAwaitExpression = function () { + var node = this.createNode(); + this.nextToken(); + var argument = this.parseUnaryExpression(); + return this.finalize(node, new Node.AwaitExpression(argument)); + }; + Parser.prototype.parseUnaryExpression = function () { + var expr; + if (this.match('+') || this.match('-') || this.match('~') || this.match('!') || + this.matchKeyword('delete') || this.matchKeyword('void') || this.matchKeyword('typeof')) { + var node = this.startNode(this.lookahead); + var token = this.nextToken(); + expr = this.inheritCoverGrammar(this.parseUnaryExpression); + expr = this.finalize(node, new Node.UnaryExpression(token.value, expr)); + if (this.context.strict && expr.operator === 'delete' && expr.argument.type === syntax_1.Syntax.Identifier) { + this.tolerateError(messages_1.Messages.StrictDelete); + } + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + else if (this.context.await && this.matchContextualKeyword('await')) { + expr = this.parseAwaitExpression(); + } + else { + expr = this.parseUpdateExpression(); + } + return expr; + }; + Parser.prototype.parseExponentiationExpression = function () { + var startToken = this.lookahead; + var expr = this.inheritCoverGrammar(this.parseUnaryExpression); + if (expr.type !== syntax_1.Syntax.UnaryExpression && this.match('**')) { + this.nextToken(); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var left = expr; + var right = this.isolateCoverGrammar(this.parseExponentiationExpression); + expr = this.finalize(this.startNode(startToken), new Node.BinaryExpression('**', left, right)); + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-exp-operator + // https://tc39.github.io/ecma262/#sec-multiplicative-operators + // https://tc39.github.io/ecma262/#sec-additive-operators + // https://tc39.github.io/ecma262/#sec-bitwise-shift-operators + // https://tc39.github.io/ecma262/#sec-relational-operators + // https://tc39.github.io/ecma262/#sec-equality-operators + // https://tc39.github.io/ecma262/#sec-binary-bitwise-operators + // https://tc39.github.io/ecma262/#sec-binary-logical-operators + Parser.prototype.binaryPrecedence = function (token) { + var op = token.value; + var precedence; + if (token.type === 7 /* Punctuator */) { + precedence = this.operatorPrecedence[op] || 0; + } + else if (token.type === 4 /* Keyword */) { + precedence = (op === 'instanceof' || (this.context.allowIn && op === 'in')) ? 7 : 0; + } + else { + precedence = 0; + } + return precedence; + }; + Parser.prototype.parseBinaryExpression = function () { + var startToken = this.lookahead; + var expr = this.inheritCoverGrammar(this.parseExponentiationExpression); + var token = this.lookahead; + var prec = this.binaryPrecedence(token); + if (prec > 0) { + this.nextToken(); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var markers = [startToken, this.lookahead]; + var left = expr; + var right = this.isolateCoverGrammar(this.parseExponentiationExpression); + var stack = [left, token.value, right]; + var precedences = [prec]; + while (true) { + prec = this.binaryPrecedence(this.lookahead); + if (prec <= 0) { + break; + } + // Reduce: make a binary expression from the three topmost entries. + while ((stack.length > 2) && (prec <= precedences[precedences.length - 1])) { + right = stack.pop(); + var operator = stack.pop(); + precedences.pop(); + left = stack.pop(); + markers.pop(); + var node = this.startNode(markers[markers.length - 1]); + stack.push(this.finalize(node, new Node.BinaryExpression(operator, left, right))); + } + // Shift. + stack.push(this.nextToken().value); + precedences.push(prec); + markers.push(this.lookahead); + stack.push(this.isolateCoverGrammar(this.parseExponentiationExpression)); + } + // Final reduce to clean-up the stack. + var i = stack.length - 1; + expr = stack[i]; + var lastMarker = markers.pop(); + while (i > 1) { + var marker = markers.pop(); + var lastLineStart = lastMarker && lastMarker.lineStart; + var node = this.startNode(marker, lastLineStart); + var operator = stack[i - 1]; + expr = this.finalize(node, new Node.BinaryExpression(operator, stack[i - 2], expr)); + i -= 2; + lastMarker = marker; + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-conditional-operator + Parser.prototype.parseConditionalExpression = function () { + var startToken = this.lookahead; + var expr = this.inheritCoverGrammar(this.parseBinaryExpression); + if (this.match('?')) { + this.nextToken(); + var previousAllowIn = this.context.allowIn; + this.context.allowIn = true; + var consequent = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.context.allowIn = previousAllowIn; + this.expect(':'); + var alternate = this.isolateCoverGrammar(this.parseAssignmentExpression); + expr = this.finalize(this.startNode(startToken), new Node.ConditionalExpression(expr, consequent, alternate)); + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-assignment-operators + Parser.prototype.checkPatternParam = function (options, param) { + switch (param.type) { + case syntax_1.Syntax.Identifier: + this.validateParam(options, param, param.name); + break; + case syntax_1.Syntax.RestElement: + this.checkPatternParam(options, param.argument); + break; + case syntax_1.Syntax.AssignmentPattern: + this.checkPatternParam(options, param.left); + break; + case syntax_1.Syntax.ArrayPattern: + for (var i = 0; i < param.elements.length; i++) { + if (param.elements[i] !== null) { + this.checkPatternParam(options, param.elements[i]); + } + } + break; + case syntax_1.Syntax.ObjectPattern: + for (var i = 0; i < param.properties.length; i++) { + this.checkPatternParam(options, param.properties[i].value); + } + break; + default: + break; + } + options.simple = options.simple && (param instanceof Node.Identifier); + }; + Parser.prototype.reinterpretAsCoverFormalsList = function (expr) { + var params = [expr]; + var options; + var asyncArrow = false; + switch (expr.type) { + case syntax_1.Syntax.Identifier: + break; + case ArrowParameterPlaceHolder: + params = expr.params; + asyncArrow = expr.async; + break; + default: + return null; + } + options = { + simple: true, + paramSet: {} + }; + for (var i = 0; i < params.length; ++i) { + var param = params[i]; + if (param.type === syntax_1.Syntax.AssignmentPattern) { + if (param.right.type === syntax_1.Syntax.YieldExpression) { + if (param.right.argument) { + this.throwUnexpectedToken(this.lookahead); + } + param.right.type = syntax_1.Syntax.Identifier; + param.right.name = 'yield'; + delete param.right.argument; + delete param.right.delegate; + } + } + else if (asyncArrow && param.type === syntax_1.Syntax.Identifier && param.name === 'await') { + this.throwUnexpectedToken(this.lookahead); + } + this.checkPatternParam(options, param); + params[i] = param; + } + if (this.context.strict || !this.context.allowYield) { + for (var i = 0; i < params.length; ++i) { + var param = params[i]; + if (param.type === syntax_1.Syntax.YieldExpression) { + this.throwUnexpectedToken(this.lookahead); + } + } + } + if (options.message === messages_1.Messages.StrictParamDupe) { + var token = this.context.strict ? options.stricted : options.firstRestricted; + this.throwUnexpectedToken(token, options.message); + } + return { + simple: options.simple, + params: params, + stricted: options.stricted, + firstRestricted: options.firstRestricted, + message: options.message + }; + }; + Parser.prototype.parseAssignmentExpression = function () { + var expr; + if (!this.context.allowYield && this.matchKeyword('yield')) { + expr = this.parseYieldExpression(); + } + else { + var startToken = this.lookahead; + var token = startToken; + expr = this.parseConditionalExpression(); + if (token.type === 3 /* Identifier */ && (token.lineNumber === this.lookahead.lineNumber) && token.value === 'async') { + if (this.lookahead.type === 3 /* Identifier */ || this.matchKeyword('yield')) { + var arg = this.parsePrimaryExpression(); + this.reinterpretExpressionAsPattern(arg); + expr = { + type: ArrowParameterPlaceHolder, + params: [arg], + async: true + }; + } + } + if (expr.type === ArrowParameterPlaceHolder || this.match('=>')) { + // https://tc39.github.io/ecma262/#sec-arrow-function-definitions + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + var isAsync = expr.async; + var list = this.reinterpretAsCoverFormalsList(expr); + if (list) { + if (this.hasLineTerminator) { + this.tolerateUnexpectedToken(this.lookahead); + } + this.context.firstCoverInitializedNameError = null; + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = list.simple; + var previousAllowYield = this.context.allowYield; + var previousAwait = this.context.await; + this.context.allowYield = true; + this.context.await = isAsync; + var node = this.startNode(startToken); + this.expect('=>'); + var body = void 0; + if (this.match('{')) { + var previousAllowIn = this.context.allowIn; + this.context.allowIn = true; + body = this.parseFunctionSourceElements(); + this.context.allowIn = previousAllowIn; + } + else { + body = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + var expression = body.type !== syntax_1.Syntax.BlockStatement; + if (this.context.strict && list.firstRestricted) { + this.throwUnexpectedToken(list.firstRestricted, list.message); + } + if (this.context.strict && list.stricted) { + this.tolerateUnexpectedToken(list.stricted, list.message); + } + expr = isAsync ? this.finalize(node, new Node.AsyncArrowFunctionExpression(list.params, body, expression)) : + this.finalize(node, new Node.ArrowFunctionExpression(list.params, body, expression)); + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + this.context.allowYield = previousAllowYield; + this.context.await = previousAwait; + } + } + else { + if (this.matchAssign()) { + if (!this.context.isAssignmentTarget) { + this.tolerateError(messages_1.Messages.InvalidLHSInAssignment); + } + if (this.context.strict && expr.type === syntax_1.Syntax.Identifier) { + var id = expr; + if (this.scanner.isRestrictedWord(id.name)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictLHSAssignment); + } + if (this.scanner.isStrictModeReservedWord(id.name)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictReservedWord); + } + } + if (!this.match('=')) { + this.context.isAssignmentTarget = false; + this.context.isBindingElement = false; + } + else { + this.reinterpretExpressionAsPattern(expr); + } + token = this.nextToken(); + var operator = token.value; + var right = this.isolateCoverGrammar(this.parseAssignmentExpression); + expr = this.finalize(this.startNode(startToken), new Node.AssignmentExpression(operator, expr, right)); + this.context.firstCoverInitializedNameError = null; + } + } + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-comma-operator + Parser.prototype.parseExpression = function () { + var startToken = this.lookahead; + var expr = this.isolateCoverGrammar(this.parseAssignmentExpression); + if (this.match(',')) { + var expressions = []; + expressions.push(expr); + while (this.lookahead.type !== 2 /* EOF */) { + if (!this.match(',')) { + break; + } + this.nextToken(); + expressions.push(this.isolateCoverGrammar(this.parseAssignmentExpression)); + } + expr = this.finalize(this.startNode(startToken), new Node.SequenceExpression(expressions)); + } + return expr; + }; + // https://tc39.github.io/ecma262/#sec-block + Parser.prototype.parseStatementListItem = function () { + var statement; + this.context.isAssignmentTarget = true; + this.context.isBindingElement = true; + if (this.lookahead.type === 4 /* Keyword */) { + switch (this.lookahead.value) { + case 'export': + if (!this.context.isModule) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.IllegalExportDeclaration); + } + statement = this.parseExportDeclaration(); + break; + case 'import': + if (!this.context.isModule) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.IllegalImportDeclaration); + } + statement = this.parseImportDeclaration(); + break; + case 'const': + statement = this.parseLexicalDeclaration({ inFor: false }); + break; + case 'function': + statement = this.parseFunctionDeclaration(); + break; + case 'class': + statement = this.parseClassDeclaration(); + break; + case 'let': + statement = this.isLexicalDeclaration() ? this.parseLexicalDeclaration({ inFor: false }) : this.parseStatement(); + break; + default: + statement = this.parseStatement(); + break; + } + } + else { + statement = this.parseStatement(); + } + return statement; + }; + Parser.prototype.parseBlock = function () { + var node = this.createNode(); + this.expect('{'); + var block = []; + while (true) { + if (this.match('}')) { + break; + } + block.push(this.parseStatementListItem()); + } + this.expect('}'); + return this.finalize(node, new Node.BlockStatement(block)); + }; + // https://tc39.github.io/ecma262/#sec-let-and-const-declarations + Parser.prototype.parseLexicalBinding = function (kind, options) { + var node = this.createNode(); + var params = []; + var id = this.parsePattern(params, kind); + if (this.context.strict && id.type === syntax_1.Syntax.Identifier) { + if (this.scanner.isRestrictedWord(id.name)) { + this.tolerateError(messages_1.Messages.StrictVarName); + } + } + var init = null; + if (kind === 'const') { + if (!this.matchKeyword('in') && !this.matchContextualKeyword('of')) { + if (this.match('=')) { + this.nextToken(); + init = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + else { + this.throwError(messages_1.Messages.DeclarationMissingInitializer, 'const'); + } + } + } + else if ((!options.inFor && id.type !== syntax_1.Syntax.Identifier) || this.match('=')) { + this.expect('='); + init = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + return this.finalize(node, new Node.VariableDeclarator(id, init)); + }; + Parser.prototype.parseBindingList = function (kind, options) { + var list = [this.parseLexicalBinding(kind, options)]; + while (this.match(',')) { + this.nextToken(); + list.push(this.parseLexicalBinding(kind, options)); + } + return list; + }; + Parser.prototype.isLexicalDeclaration = function () { + var state = this.scanner.saveState(); + this.scanner.scanComments(); + var next = this.scanner.lex(); + this.scanner.restoreState(state); + return (next.type === 3 /* Identifier */) || + (next.type === 7 /* Punctuator */ && next.value === '[') || + (next.type === 7 /* Punctuator */ && next.value === '{') || + (next.type === 4 /* Keyword */ && next.value === 'let') || + (next.type === 4 /* Keyword */ && next.value === 'yield'); + }; + Parser.prototype.parseLexicalDeclaration = function (options) { + var node = this.createNode(); + var kind = this.nextToken().value; + assert_1.assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const'); + var declarations = this.parseBindingList(kind, options); + this.consumeSemicolon(); + return this.finalize(node, new Node.VariableDeclaration(declarations, kind)); + }; + // https://tc39.github.io/ecma262/#sec-destructuring-binding-patterns + Parser.prototype.parseBindingRestElement = function (params, kind) { + var node = this.createNode(); + this.expect('...'); + var arg = this.parsePattern(params, kind); + return this.finalize(node, new Node.RestElement(arg)); + }; + Parser.prototype.parseArrayPattern = function (params, kind) { + var node = this.createNode(); + this.expect('['); + var elements = []; + while (!this.match(']')) { + if (this.match(',')) { + this.nextToken(); + elements.push(null); + } + else { + if (this.match('...')) { + elements.push(this.parseBindingRestElement(params, kind)); + break; + } + else { + elements.push(this.parsePatternWithDefault(params, kind)); + } + if (!this.match(']')) { + this.expect(','); + } + } + } + this.expect(']'); + return this.finalize(node, new Node.ArrayPattern(elements)); + }; + Parser.prototype.parsePropertyPattern = function (params, kind) { + var node = this.createNode(); + var computed = false; + var shorthand = false; + var method = false; + var key; + var value; + if (this.lookahead.type === 3 /* Identifier */) { + var keyToken = this.lookahead; + key = this.parseVariableIdentifier(); + var init = this.finalize(node, new Node.Identifier(keyToken.value)); + if (this.match('=')) { + params.push(keyToken); + shorthand = true; + this.nextToken(); + var expr = this.parseAssignmentExpression(); + value = this.finalize(this.startNode(keyToken), new Node.AssignmentPattern(init, expr)); + } + else if (!this.match(':')) { + params.push(keyToken); + shorthand = true; + value = init; + } + else { + this.expect(':'); + value = this.parsePatternWithDefault(params, kind); + } + } + else { + computed = this.match('['); + key = this.parseObjectPropertyKey(); + this.expect(':'); + value = this.parsePatternWithDefault(params, kind); + } + return this.finalize(node, new Node.Property('init', key, computed, value, method, shorthand)); + }; + Parser.prototype.parseObjectPattern = function (params, kind) { + var node = this.createNode(); + var properties = []; + this.expect('{'); + while (!this.match('}')) { + properties.push(this.parsePropertyPattern(params, kind)); + if (!this.match('}')) { + this.expect(','); + } + } + this.expect('}'); + return this.finalize(node, new Node.ObjectPattern(properties)); + }; + Parser.prototype.parsePattern = function (params, kind) { + var pattern; + if (this.match('[')) { + pattern = this.parseArrayPattern(params, kind); + } + else if (this.match('{')) { + pattern = this.parseObjectPattern(params, kind); + } + else { + if (this.matchKeyword('let') && (kind === 'const' || kind === 'let')) { + this.tolerateUnexpectedToken(this.lookahead, messages_1.Messages.LetInLexicalBinding); + } + params.push(this.lookahead); + pattern = this.parseVariableIdentifier(kind); + } + return pattern; + }; + Parser.prototype.parsePatternWithDefault = function (params, kind) { + var startToken = this.lookahead; + var pattern = this.parsePattern(params, kind); + if (this.match('=')) { + this.nextToken(); + var previousAllowYield = this.context.allowYield; + this.context.allowYield = true; + var right = this.isolateCoverGrammar(this.parseAssignmentExpression); + this.context.allowYield = previousAllowYield; + pattern = this.finalize(this.startNode(startToken), new Node.AssignmentPattern(pattern, right)); + } + return pattern; + }; + // https://tc39.github.io/ecma262/#sec-variable-statement + Parser.prototype.parseVariableIdentifier = function (kind) { + var node = this.createNode(); + var token = this.nextToken(); + if (token.type === 4 /* Keyword */ && token.value === 'yield') { + if (this.context.strict) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictReservedWord); + } + else if (!this.context.allowYield) { + this.throwUnexpectedToken(token); + } + } + else if (token.type !== 3 /* Identifier */) { + if (this.context.strict && token.type === 4 /* Keyword */ && this.scanner.isStrictModeReservedWord(token.value)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictReservedWord); + } + else { + if (this.context.strict || token.value !== 'let' || kind !== 'var') { + this.throwUnexpectedToken(token); + } + } + } + else if ((this.context.isModule || this.context.await) && token.type === 3 /* Identifier */ && token.value === 'await') { + this.tolerateUnexpectedToken(token); + } + return this.finalize(node, new Node.Identifier(token.value)); + }; + Parser.prototype.parseVariableDeclaration = function (options) { + var node = this.createNode(); + var params = []; + var id = this.parsePattern(params, 'var'); + if (this.context.strict && id.type === syntax_1.Syntax.Identifier) { + if (this.scanner.isRestrictedWord(id.name)) { + this.tolerateError(messages_1.Messages.StrictVarName); + } + } + var init = null; + if (this.match('=')) { + this.nextToken(); + init = this.isolateCoverGrammar(this.parseAssignmentExpression); + } + else if (id.type !== syntax_1.Syntax.Identifier && !options.inFor) { + this.expect('='); + } + return this.finalize(node, new Node.VariableDeclarator(id, init)); + }; + Parser.prototype.parseVariableDeclarationList = function (options) { + var opt = { inFor: options.inFor }; + var list = []; + list.push(this.parseVariableDeclaration(opt)); + while (this.match(',')) { + this.nextToken(); + list.push(this.parseVariableDeclaration(opt)); + } + return list; + }; + Parser.prototype.parseVariableStatement = function () { + var node = this.createNode(); + this.expectKeyword('var'); + var declarations = this.parseVariableDeclarationList({ inFor: false }); + this.consumeSemicolon(); + return this.finalize(node, new Node.VariableDeclaration(declarations, 'var')); + }; + // https://tc39.github.io/ecma262/#sec-empty-statement + Parser.prototype.parseEmptyStatement = function () { + var node = this.createNode(); + this.expect(';'); + return this.finalize(node, new Node.EmptyStatement()); + }; + // https://tc39.github.io/ecma262/#sec-expression-statement + Parser.prototype.parseExpressionStatement = function () { + var node = this.createNode(); + var expr = this.parseExpression(); + this.consumeSemicolon(); + return this.finalize(node, new Node.ExpressionStatement(expr)); + }; + // https://tc39.github.io/ecma262/#sec-if-statement + Parser.prototype.parseIfClause = function () { + if (this.context.strict && this.matchKeyword('function')) { + this.tolerateError(messages_1.Messages.StrictFunction); + } + return this.parseStatement(); + }; + Parser.prototype.parseIfStatement = function () { + var node = this.createNode(); + var consequent; + var alternate = null; + this.expectKeyword('if'); + this.expect('('); + var test = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + consequent = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + consequent = this.parseIfClause(); + if (this.matchKeyword('else')) { + this.nextToken(); + alternate = this.parseIfClause(); + } + } + return this.finalize(node, new Node.IfStatement(test, consequent, alternate)); + }; + // https://tc39.github.io/ecma262/#sec-do-while-statement + Parser.prototype.parseDoWhileStatement = function () { + var node = this.createNode(); + this.expectKeyword('do'); + var previousInIteration = this.context.inIteration; + this.context.inIteration = true; + var body = this.parseStatement(); + this.context.inIteration = previousInIteration; + this.expectKeyword('while'); + this.expect('('); + var test = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + } + else { + this.expect(')'); + if (this.match(';')) { + this.nextToken(); + } + } + return this.finalize(node, new Node.DoWhileStatement(body, test)); + }; + // https://tc39.github.io/ecma262/#sec-while-statement + Parser.prototype.parseWhileStatement = function () { + var node = this.createNode(); + var body; + this.expectKeyword('while'); + this.expect('('); + var test = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + body = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + var previousInIteration = this.context.inIteration; + this.context.inIteration = true; + body = this.parseStatement(); + this.context.inIteration = previousInIteration; + } + return this.finalize(node, new Node.WhileStatement(test, body)); + }; + // https://tc39.github.io/ecma262/#sec-for-statement + // https://tc39.github.io/ecma262/#sec-for-in-and-for-of-statements + Parser.prototype.parseForStatement = function () { + var init = null; + var test = null; + var update = null; + var forIn = true; + var left, right; + var node = this.createNode(); + this.expectKeyword('for'); + this.expect('('); + if (this.match(';')) { + this.nextToken(); + } + else { + if (this.matchKeyword('var')) { + init = this.createNode(); + this.nextToken(); + var previousAllowIn = this.context.allowIn; + this.context.allowIn = false; + var declarations = this.parseVariableDeclarationList({ inFor: true }); + this.context.allowIn = previousAllowIn; + if (declarations.length === 1 && this.matchKeyword('in')) { + var decl = declarations[0]; + if (decl.init && (decl.id.type === syntax_1.Syntax.ArrayPattern || decl.id.type === syntax_1.Syntax.ObjectPattern || this.context.strict)) { + this.tolerateError(messages_1.Messages.ForInOfLoopInitializer, 'for-in'); + } + init = this.finalize(init, new Node.VariableDeclaration(declarations, 'var')); + this.nextToken(); + left = init; + right = this.parseExpression(); + init = null; + } + else if (declarations.length === 1 && declarations[0].init === null && this.matchContextualKeyword('of')) { + init = this.finalize(init, new Node.VariableDeclaration(declarations, 'var')); + this.nextToken(); + left = init; + right = this.parseAssignmentExpression(); + init = null; + forIn = false; + } + else { + init = this.finalize(init, new Node.VariableDeclaration(declarations, 'var')); + this.expect(';'); + } + } + else if (this.matchKeyword('const') || this.matchKeyword('let')) { + init = this.createNode(); + var kind = this.nextToken().value; + if (!this.context.strict && this.lookahead.value === 'in') { + init = this.finalize(init, new Node.Identifier(kind)); + this.nextToken(); + left = init; + right = this.parseExpression(); + init = null; + } + else { + var previousAllowIn = this.context.allowIn; + this.context.allowIn = false; + var declarations = this.parseBindingList(kind, { inFor: true }); + this.context.allowIn = previousAllowIn; + if (declarations.length === 1 && declarations[0].init === null && this.matchKeyword('in')) { + init = this.finalize(init, new Node.VariableDeclaration(declarations, kind)); + this.nextToken(); + left = init; + right = this.parseExpression(); + init = null; + } + else if (declarations.length === 1 && declarations[0].init === null && this.matchContextualKeyword('of')) { + init = this.finalize(init, new Node.VariableDeclaration(declarations, kind)); + this.nextToken(); + left = init; + right = this.parseAssignmentExpression(); + init = null; + forIn = false; + } + else { + this.consumeSemicolon(); + init = this.finalize(init, new Node.VariableDeclaration(declarations, kind)); + } + } + } + else { + var initStartToken = this.lookahead; + var previousAllowIn = this.context.allowIn; + this.context.allowIn = false; + init = this.inheritCoverGrammar(this.parseAssignmentExpression); + this.context.allowIn = previousAllowIn; + if (this.matchKeyword('in')) { + if (!this.context.isAssignmentTarget || init.type === syntax_1.Syntax.AssignmentExpression) { + this.tolerateError(messages_1.Messages.InvalidLHSInForIn); + } + this.nextToken(); + this.reinterpretExpressionAsPattern(init); + left = init; + right = this.parseExpression(); + init = null; + } + else if (this.matchContextualKeyword('of')) { + if (!this.context.isAssignmentTarget || init.type === syntax_1.Syntax.AssignmentExpression) { + this.tolerateError(messages_1.Messages.InvalidLHSInForLoop); + } + this.nextToken(); + this.reinterpretExpressionAsPattern(init); + left = init; + right = this.parseAssignmentExpression(); + init = null; + forIn = false; + } + else { + if (this.match(',')) { + var initSeq = [init]; + while (this.match(',')) { + this.nextToken(); + initSeq.push(this.isolateCoverGrammar(this.parseAssignmentExpression)); + } + init = this.finalize(this.startNode(initStartToken), new Node.SequenceExpression(initSeq)); + } + this.expect(';'); + } + } + } + if (typeof left === 'undefined') { + if (!this.match(';')) { + test = this.parseExpression(); + } + this.expect(';'); + if (!this.match(')')) { + update = this.parseExpression(); + } + } + var body; + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + body = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + var previousInIteration = this.context.inIteration; + this.context.inIteration = true; + body = this.isolateCoverGrammar(this.parseStatement); + this.context.inIteration = previousInIteration; + } + return (typeof left === 'undefined') ? + this.finalize(node, new Node.ForStatement(init, test, update, body)) : + forIn ? this.finalize(node, new Node.ForInStatement(left, right, body)) : + this.finalize(node, new Node.ForOfStatement(left, right, body)); + }; + // https://tc39.github.io/ecma262/#sec-continue-statement + Parser.prototype.parseContinueStatement = function () { + var node = this.createNode(); + this.expectKeyword('continue'); + var label = null; + if (this.lookahead.type === 3 /* Identifier */ && !this.hasLineTerminator) { + var id = this.parseVariableIdentifier(); + label = id; + var key = '$' + id.name; + if (!Object.prototype.hasOwnProperty.call(this.context.labelSet, key)) { + this.throwError(messages_1.Messages.UnknownLabel, id.name); + } + } + this.consumeSemicolon(); + if (label === null && !this.context.inIteration) { + this.throwError(messages_1.Messages.IllegalContinue); + } + return this.finalize(node, new Node.ContinueStatement(label)); + }; + // https://tc39.github.io/ecma262/#sec-break-statement + Parser.prototype.parseBreakStatement = function () { + var node = this.createNode(); + this.expectKeyword('break'); + var label = null; + if (this.lookahead.type === 3 /* Identifier */ && !this.hasLineTerminator) { + var id = this.parseVariableIdentifier(); + var key = '$' + id.name; + if (!Object.prototype.hasOwnProperty.call(this.context.labelSet, key)) { + this.throwError(messages_1.Messages.UnknownLabel, id.name); + } + label = id; + } + this.consumeSemicolon(); + if (label === null && !this.context.inIteration && !this.context.inSwitch) { + this.throwError(messages_1.Messages.IllegalBreak); + } + return this.finalize(node, new Node.BreakStatement(label)); + }; + // https://tc39.github.io/ecma262/#sec-return-statement + Parser.prototype.parseReturnStatement = function () { + if (!this.context.inFunctionBody) { + this.tolerateError(messages_1.Messages.IllegalReturn); + } + var node = this.createNode(); + this.expectKeyword('return'); + var hasArgument = (!this.match(';') && !this.match('}') && + !this.hasLineTerminator && this.lookahead.type !== 2 /* EOF */) || + this.lookahead.type === 8 /* StringLiteral */ || + this.lookahead.type === 10 /* Template */; + var argument = hasArgument ? this.parseExpression() : null; + this.consumeSemicolon(); + return this.finalize(node, new Node.ReturnStatement(argument)); + }; + // https://tc39.github.io/ecma262/#sec-with-statement + Parser.prototype.parseWithStatement = function () { + if (this.context.strict) { + this.tolerateError(messages_1.Messages.StrictModeWith); + } + var node = this.createNode(); + var body; + this.expectKeyword('with'); + this.expect('('); + var object = this.parseExpression(); + if (!this.match(')') && this.config.tolerant) { + this.tolerateUnexpectedToken(this.nextToken()); + body = this.finalize(this.createNode(), new Node.EmptyStatement()); + } + else { + this.expect(')'); + body = this.parseStatement(); + } + return this.finalize(node, new Node.WithStatement(object, body)); + }; + // https://tc39.github.io/ecma262/#sec-switch-statement + Parser.prototype.parseSwitchCase = function () { + var node = this.createNode(); + var test; + if (this.matchKeyword('default')) { + this.nextToken(); + test = null; + } + else { + this.expectKeyword('case'); + test = this.parseExpression(); + } + this.expect(':'); + var consequent = []; + while (true) { + if (this.match('}') || this.matchKeyword('default') || this.matchKeyword('case')) { + break; + } + consequent.push(this.parseStatementListItem()); + } + return this.finalize(node, new Node.SwitchCase(test, consequent)); + }; + Parser.prototype.parseSwitchStatement = function () { + var node = this.createNode(); + this.expectKeyword('switch'); + this.expect('('); + var discriminant = this.parseExpression(); + this.expect(')'); + var previousInSwitch = this.context.inSwitch; + this.context.inSwitch = true; + var cases = []; + var defaultFound = false; + this.expect('{'); + while (true) { + if (this.match('}')) { + break; + } + var clause = this.parseSwitchCase(); + if (clause.test === null) { + if (defaultFound) { + this.throwError(messages_1.Messages.MultipleDefaultsInSwitch); + } + defaultFound = true; + } + cases.push(clause); + } + this.expect('}'); + this.context.inSwitch = previousInSwitch; + return this.finalize(node, new Node.SwitchStatement(discriminant, cases)); + }; + // https://tc39.github.io/ecma262/#sec-labelled-statements + Parser.prototype.parseLabelledStatement = function () { + var node = this.createNode(); + var expr = this.parseExpression(); + var statement; + if ((expr.type === syntax_1.Syntax.Identifier) && this.match(':')) { + this.nextToken(); + var id = expr; + var key = '$' + id.name; + if (Object.prototype.hasOwnProperty.call(this.context.labelSet, key)) { + this.throwError(messages_1.Messages.Redeclaration, 'Label', id.name); + } + this.context.labelSet[key] = true; + var body = void 0; + if (this.matchKeyword('class')) { + this.tolerateUnexpectedToken(this.lookahead); + body = this.parseClassDeclaration(); + } + else if (this.matchKeyword('function')) { + var token = this.lookahead; + var declaration = this.parseFunctionDeclaration(); + if (this.context.strict) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictFunction); + } + else if (declaration.generator) { + this.tolerateUnexpectedToken(token, messages_1.Messages.GeneratorInLegacyContext); + } + body = declaration; + } + else { + body = this.parseStatement(); + } + delete this.context.labelSet[key]; + statement = new Node.LabeledStatement(id, body); + } + else { + this.consumeSemicolon(); + statement = new Node.ExpressionStatement(expr); + } + return this.finalize(node, statement); + }; + // https://tc39.github.io/ecma262/#sec-throw-statement + Parser.prototype.parseThrowStatement = function () { + var node = this.createNode(); + this.expectKeyword('throw'); + if (this.hasLineTerminator) { + this.throwError(messages_1.Messages.NewlineAfterThrow); + } + var argument = this.parseExpression(); + this.consumeSemicolon(); + return this.finalize(node, new Node.ThrowStatement(argument)); + }; + // https://tc39.github.io/ecma262/#sec-try-statement + Parser.prototype.parseCatchClause = function () { + var node = this.createNode(); + this.expectKeyword('catch'); + this.expect('('); + if (this.match(')')) { + this.throwUnexpectedToken(this.lookahead); + } + var params = []; + var param = this.parsePattern(params); + var paramMap = {}; + for (var i = 0; i < params.length; i++) { + var key = '$' + params[i].value; + if (Object.prototype.hasOwnProperty.call(paramMap, key)) { + this.tolerateError(messages_1.Messages.DuplicateBinding, params[i].value); + } + paramMap[key] = true; + } + if (this.context.strict && param.type === syntax_1.Syntax.Identifier) { + if (this.scanner.isRestrictedWord(param.name)) { + this.tolerateError(messages_1.Messages.StrictCatchVariable); + } + } + this.expect(')'); + var body = this.parseBlock(); + return this.finalize(node, new Node.CatchClause(param, body)); + }; + Parser.prototype.parseFinallyClause = function () { + this.expectKeyword('finally'); + return this.parseBlock(); + }; + Parser.prototype.parseTryStatement = function () { + var node = this.createNode(); + this.expectKeyword('try'); + var block = this.parseBlock(); + var handler = this.matchKeyword('catch') ? this.parseCatchClause() : null; + var finalizer = this.matchKeyword('finally') ? this.parseFinallyClause() : null; + if (!handler && !finalizer) { + this.throwError(messages_1.Messages.NoCatchOrFinally); + } + return this.finalize(node, new Node.TryStatement(block, handler, finalizer)); + }; + // https://tc39.github.io/ecma262/#sec-debugger-statement + Parser.prototype.parseDebuggerStatement = function () { + var node = this.createNode(); + this.expectKeyword('debugger'); + this.consumeSemicolon(); + return this.finalize(node, new Node.DebuggerStatement()); + }; + // https://tc39.github.io/ecma262/#sec-ecmascript-language-statements-and-declarations + Parser.prototype.parseStatement = function () { + var statement; + switch (this.lookahead.type) { + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 6 /* NumericLiteral */: + case 8 /* StringLiteral */: + case 10 /* Template */: + case 9 /* RegularExpression */: + statement = this.parseExpressionStatement(); + break; + case 7 /* Punctuator */: + var value = this.lookahead.value; + if (value === '{') { + statement = this.parseBlock(); + } + else if (value === '(') { + statement = this.parseExpressionStatement(); + } + else if (value === ';') { + statement = this.parseEmptyStatement(); + } + else { + statement = this.parseExpressionStatement(); + } + break; + case 3 /* Identifier */: + statement = this.matchAsyncFunction() ? this.parseFunctionDeclaration() : this.parseLabelledStatement(); + break; + case 4 /* Keyword */: + switch (this.lookahead.value) { + case 'break': + statement = this.parseBreakStatement(); + break; + case 'continue': + statement = this.parseContinueStatement(); + break; + case 'debugger': + statement = this.parseDebuggerStatement(); + break; + case 'do': + statement = this.parseDoWhileStatement(); + break; + case 'for': + statement = this.parseForStatement(); + break; + case 'function': + statement = this.parseFunctionDeclaration(); + break; + case 'if': + statement = this.parseIfStatement(); + break; + case 'return': + statement = this.parseReturnStatement(); + break; + case 'switch': + statement = this.parseSwitchStatement(); + break; + case 'throw': + statement = this.parseThrowStatement(); + break; + case 'try': + statement = this.parseTryStatement(); + break; + case 'var': + statement = this.parseVariableStatement(); + break; + case 'while': + statement = this.parseWhileStatement(); + break; + case 'with': + statement = this.parseWithStatement(); + break; + default: + statement = this.parseExpressionStatement(); + break; + } + break; + default: + statement = this.throwUnexpectedToken(this.lookahead); + } + return statement; + }; + // https://tc39.github.io/ecma262/#sec-function-definitions + Parser.prototype.parseFunctionSourceElements = function () { + var node = this.createNode(); + this.expect('{'); + var body = this.parseDirectivePrologues(); + var previousLabelSet = this.context.labelSet; + var previousInIteration = this.context.inIteration; + var previousInSwitch = this.context.inSwitch; + var previousInFunctionBody = this.context.inFunctionBody; + this.context.labelSet = {}; + this.context.inIteration = false; + this.context.inSwitch = false; + this.context.inFunctionBody = true; + while (this.lookahead.type !== 2 /* EOF */) { + if (this.match('}')) { + break; + } + body.push(this.parseStatementListItem()); + } + this.expect('}'); + this.context.labelSet = previousLabelSet; + this.context.inIteration = previousInIteration; + this.context.inSwitch = previousInSwitch; + this.context.inFunctionBody = previousInFunctionBody; + return this.finalize(node, new Node.BlockStatement(body)); + }; + Parser.prototype.validateParam = function (options, param, name) { + var key = '$' + name; + if (this.context.strict) { + if (this.scanner.isRestrictedWord(name)) { + options.stricted = param; + options.message = messages_1.Messages.StrictParamName; + } + if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.stricted = param; + options.message = messages_1.Messages.StrictParamDupe; + } + } + else if (!options.firstRestricted) { + if (this.scanner.isRestrictedWord(name)) { + options.firstRestricted = param; + options.message = messages_1.Messages.StrictParamName; + } + else if (this.scanner.isStrictModeReservedWord(name)) { + options.firstRestricted = param; + options.message = messages_1.Messages.StrictReservedWord; + } + else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.stricted = param; + options.message = messages_1.Messages.StrictParamDupe; + } + } + /* istanbul ignore next */ + if (typeof Object.defineProperty === 'function') { + Object.defineProperty(options.paramSet, key, { value: true, enumerable: true, writable: true, configurable: true }); + } + else { + options.paramSet[key] = true; + } + }; + Parser.prototype.parseRestElement = function (params) { + var node = this.createNode(); + this.expect('...'); + var arg = this.parsePattern(params); + if (this.match('=')) { + this.throwError(messages_1.Messages.DefaultRestParameter); + } + if (!this.match(')')) { + this.throwError(messages_1.Messages.ParameterAfterRestParameter); + } + return this.finalize(node, new Node.RestElement(arg)); + }; + Parser.prototype.parseFormalParameter = function (options) { + var params = []; + var param = this.match('...') ? this.parseRestElement(params) : this.parsePatternWithDefault(params); + for (var i = 0; i < params.length; i++) { + this.validateParam(options, params[i], params[i].value); + } + options.simple = options.simple && (param instanceof Node.Identifier); + options.params.push(param); + }; + Parser.prototype.parseFormalParameters = function (firstRestricted) { + var options; + options = { + simple: true, + params: [], + firstRestricted: firstRestricted + }; + this.expect('('); + if (!this.match(')')) { + options.paramSet = {}; + while (this.lookahead.type !== 2 /* EOF */) { + this.parseFormalParameter(options); + if (this.match(')')) { + break; + } + this.expect(','); + if (this.match(')')) { + break; + } + } + } + this.expect(')'); + return { + simple: options.simple, + params: options.params, + stricted: options.stricted, + firstRestricted: options.firstRestricted, + message: options.message + }; + }; + Parser.prototype.matchAsyncFunction = function () { + var match = this.matchContextualKeyword('async'); + if (match) { + var state = this.scanner.saveState(); + this.scanner.scanComments(); + var next = this.scanner.lex(); + this.scanner.restoreState(state); + match = (state.lineNumber === next.lineNumber) && (next.type === 4 /* Keyword */) && (next.value === 'function'); + } + return match; + }; + Parser.prototype.parseFunctionDeclaration = function (identifierIsOptional) { + var node = this.createNode(); + var isAsync = this.matchContextualKeyword('async'); + if (isAsync) { + this.nextToken(); + } + this.expectKeyword('function'); + var isGenerator = isAsync ? false : this.match('*'); + if (isGenerator) { + this.nextToken(); + } + var message; + var id = null; + var firstRestricted = null; + if (!identifierIsOptional || !this.match('(')) { + var token = this.lookahead; + id = this.parseVariableIdentifier(); + if (this.context.strict) { + if (this.scanner.isRestrictedWord(token.value)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictFunctionName); + } + } + else { + if (this.scanner.isRestrictedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictFunctionName; + } + else if (this.scanner.isStrictModeReservedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictReservedWord; + } + } + } + var previousAllowAwait = this.context.await; + var previousAllowYield = this.context.allowYield; + this.context.await = isAsync; + this.context.allowYield = !isGenerator; + var formalParameters = this.parseFormalParameters(firstRestricted); + var params = formalParameters.params; + var stricted = formalParameters.stricted; + firstRestricted = formalParameters.firstRestricted; + if (formalParameters.message) { + message = formalParameters.message; + } + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = formalParameters.simple; + var body = this.parseFunctionSourceElements(); + if (this.context.strict && firstRestricted) { + this.throwUnexpectedToken(firstRestricted, message); + } + if (this.context.strict && stricted) { + this.tolerateUnexpectedToken(stricted, message); + } + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + this.context.await = previousAllowAwait; + this.context.allowYield = previousAllowYield; + return isAsync ? this.finalize(node, new Node.AsyncFunctionDeclaration(id, params, body)) : + this.finalize(node, new Node.FunctionDeclaration(id, params, body, isGenerator)); + }; + Parser.prototype.parseFunctionExpression = function () { + var node = this.createNode(); + var isAsync = this.matchContextualKeyword('async'); + if (isAsync) { + this.nextToken(); + } + this.expectKeyword('function'); + var isGenerator = isAsync ? false : this.match('*'); + if (isGenerator) { + this.nextToken(); + } + var message; + var id = null; + var firstRestricted; + var previousAllowAwait = this.context.await; + var previousAllowYield = this.context.allowYield; + this.context.await = isAsync; + this.context.allowYield = !isGenerator; + if (!this.match('(')) { + var token = this.lookahead; + id = (!this.context.strict && !isGenerator && this.matchKeyword('yield')) ? this.parseIdentifierName() : this.parseVariableIdentifier(); + if (this.context.strict) { + if (this.scanner.isRestrictedWord(token.value)) { + this.tolerateUnexpectedToken(token, messages_1.Messages.StrictFunctionName); + } + } + else { + if (this.scanner.isRestrictedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictFunctionName; + } + else if (this.scanner.isStrictModeReservedWord(token.value)) { + firstRestricted = token; + message = messages_1.Messages.StrictReservedWord; + } + } + } + var formalParameters = this.parseFormalParameters(firstRestricted); + var params = formalParameters.params; + var stricted = formalParameters.stricted; + firstRestricted = formalParameters.firstRestricted; + if (formalParameters.message) { + message = formalParameters.message; + } + var previousStrict = this.context.strict; + var previousAllowStrictDirective = this.context.allowStrictDirective; + this.context.allowStrictDirective = formalParameters.simple; + var body = this.parseFunctionSourceElements(); + if (this.context.strict && firstRestricted) { + this.throwUnexpectedToken(firstRestricted, message); + } + if (this.context.strict && stricted) { + this.tolerateUnexpectedToken(stricted, message); + } + this.context.strict = previousStrict; + this.context.allowStrictDirective = previousAllowStrictDirective; + this.context.await = previousAllowAwait; + this.context.allowYield = previousAllowYield; + return isAsync ? this.finalize(node, new Node.AsyncFunctionExpression(id, params, body)) : + this.finalize(node, new Node.FunctionExpression(id, params, body, isGenerator)); + }; + // https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive + Parser.prototype.parseDirective = function () { + var token = this.lookahead; + var node = this.createNode(); + var expr = this.parseExpression(); + var directive = (expr.type === syntax_1.Syntax.Literal) ? this.getTokenRaw(token).slice(1, -1) : null; + this.consumeSemicolon(); + return this.finalize(node, directive ? new Node.Directive(expr, directive) : new Node.ExpressionStatement(expr)); + }; + Parser.prototype.parseDirectivePrologues = function () { + var firstRestricted = null; + var body = []; + while (true) { + var token = this.lookahead; + if (token.type !== 8 /* StringLiteral */) { + break; + } + var statement = this.parseDirective(); + body.push(statement); + var directive = statement.directive; + if (typeof directive !== 'string') { + break; + } + if (directive === 'use strict') { + this.context.strict = true; + if (firstRestricted) { + this.tolerateUnexpectedToken(firstRestricted, messages_1.Messages.StrictOctalLiteral); + } + if (!this.context.allowStrictDirective) { + this.tolerateUnexpectedToken(token, messages_1.Messages.IllegalLanguageModeDirective); + } + } + else { + if (!firstRestricted && token.octal) { + firstRestricted = token; + } + } + } + return body; + }; + // https://tc39.github.io/ecma262/#sec-method-definitions + Parser.prototype.qualifiedPropertyName = function (token) { + switch (token.type) { + case 3 /* Identifier */: + case 8 /* StringLiteral */: + case 1 /* BooleanLiteral */: + case 5 /* NullLiteral */: + case 6 /* NumericLiteral */: + case 4 /* Keyword */: + return true; + case 7 /* Punctuator */: + return token.value === '['; + default: + break; + } + return false; + }; + Parser.prototype.parseGetterMethod = function () { + var node = this.createNode(); + var isGenerator = false; + var previousAllowYield = this.context.allowYield; + this.context.allowYield = !isGenerator; + var formalParameters = this.parseFormalParameters(); + if (formalParameters.params.length > 0) { + this.tolerateError(messages_1.Messages.BadGetterArity); + } + var method = this.parsePropertyMethod(formalParameters); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator)); + }; + Parser.prototype.parseSetterMethod = function () { + var node = this.createNode(); + var isGenerator = false; + var previousAllowYield = this.context.allowYield; + this.context.allowYield = !isGenerator; + var formalParameters = this.parseFormalParameters(); + if (formalParameters.params.length !== 1) { + this.tolerateError(messages_1.Messages.BadSetterArity); + } + else if (formalParameters.params[0] instanceof Node.RestElement) { + this.tolerateError(messages_1.Messages.BadSetterRestParameter); + } + var method = this.parsePropertyMethod(formalParameters); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, formalParameters.params, method, isGenerator)); + }; + Parser.prototype.parseGeneratorMethod = function () { + var node = this.createNode(); + var isGenerator = true; + var previousAllowYield = this.context.allowYield; + this.context.allowYield = true; + var params = this.parseFormalParameters(); + this.context.allowYield = false; + var method = this.parsePropertyMethod(params); + this.context.allowYield = previousAllowYield; + return this.finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator)); + }; + // https://tc39.github.io/ecma262/#sec-generator-function-definitions + Parser.prototype.isStartOfExpression = function () { + var start = true; + var value = this.lookahead.value; + switch (this.lookahead.type) { + case 7 /* Punctuator */: + start = (value === '[') || (value === '(') || (value === '{') || + (value === '+') || (value === '-') || + (value === '!') || (value === '~') || + (value === '++') || (value === '--') || + (value === '/') || (value === '/='); // regular expression literal + break; + case 4 /* Keyword */: + start = (value === 'class') || (value === 'delete') || + (value === 'function') || (value === 'let') || (value === 'new') || + (value === 'super') || (value === 'this') || (value === 'typeof') || + (value === 'void') || (value === 'yield'); + break; + default: + break; + } + return start; + }; + Parser.prototype.parseYieldExpression = function () { + var node = this.createNode(); + this.expectKeyword('yield'); + var argument = null; + var delegate = false; + if (!this.hasLineTerminator) { + var previousAllowYield = this.context.allowYield; + this.context.allowYield = false; + delegate = this.match('*'); + if (delegate) { + this.nextToken(); + argument = this.parseAssignmentExpression(); + } + else if (this.isStartOfExpression()) { + argument = this.parseAssignmentExpression(); + } + this.context.allowYield = previousAllowYield; + } + return this.finalize(node, new Node.YieldExpression(argument, delegate)); + }; + // https://tc39.github.io/ecma262/#sec-class-definitions + Parser.prototype.parseClassElement = function (hasConstructor) { + var token = this.lookahead; + var node = this.createNode(); + var kind = ''; + var key = null; + var value = null; + var computed = false; + var method = false; + var isStatic = false; + var isAsync = false; + if (this.match('*')) { + this.nextToken(); + } + else { + computed = this.match('['); + key = this.parseObjectPropertyKey(); + var id = key; + if (id.name === 'static' && (this.qualifiedPropertyName(this.lookahead) || this.match('*'))) { + token = this.lookahead; + isStatic = true; + computed = this.match('['); + if (this.match('*')) { + this.nextToken(); + } + else { + key = this.parseObjectPropertyKey(); + } + } + if ((token.type === 3 /* Identifier */) && !this.hasLineTerminator && (token.value === 'async')) { + var punctuator = this.lookahead.value; + if (punctuator !== ':' && punctuator !== '(' && punctuator !== '*') { + isAsync = true; + token = this.lookahead; + key = this.parseObjectPropertyKey(); + if (token.type === 3 /* Identifier */ && token.value === 'constructor') { + this.tolerateUnexpectedToken(token, messages_1.Messages.ConstructorIsAsync); + } + } + } + } + var lookaheadPropertyKey = this.qualifiedPropertyName(this.lookahead); + if (token.type === 3 /* Identifier */) { + if (token.value === 'get' && lookaheadPropertyKey) { + kind = 'get'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + this.context.allowYield = false; + value = this.parseGetterMethod(); + } + else if (token.value === 'set' && lookaheadPropertyKey) { + kind = 'set'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseSetterMethod(); + } + } + else if (token.type === 7 /* Punctuator */ && token.value === '*' && lookaheadPropertyKey) { + kind = 'init'; + computed = this.match('['); + key = this.parseObjectPropertyKey(); + value = this.parseGeneratorMethod(); + method = true; + } + if (!kind && key && this.match('(')) { + kind = 'init'; + value = isAsync ? this.parsePropertyMethodAsyncFunction() : this.parsePropertyMethodFunction(); + method = true; + } + if (!kind) { + this.throwUnexpectedToken(this.lookahead); + } + if (kind === 'init') { + kind = 'method'; + } + if (!computed) { + if (isStatic && this.isPropertyKey(key, 'prototype')) { + this.throwUnexpectedToken(token, messages_1.Messages.StaticPrototype); + } + if (!isStatic && this.isPropertyKey(key, 'constructor')) { + if (kind !== 'method' || !method || (value && value.generator)) { + this.throwUnexpectedToken(token, messages_1.Messages.ConstructorSpecialMethod); + } + if (hasConstructor.value) { + this.throwUnexpectedToken(token, messages_1.Messages.DuplicateConstructor); + } + else { + hasConstructor.value = true; + } + kind = 'constructor'; + } + } + return this.finalize(node, new Node.MethodDefinition(key, computed, value, kind, isStatic)); + }; + Parser.prototype.parseClassElementList = function () { + var body = []; + var hasConstructor = { value: false }; + this.expect('{'); + while (!this.match('}')) { + if (this.match(';')) { + this.nextToken(); + } + else { + body.push(this.parseClassElement(hasConstructor)); + } + } + this.expect('}'); + return body; + }; + Parser.prototype.parseClassBody = function () { + var node = this.createNode(); + var elementList = this.parseClassElementList(); + return this.finalize(node, new Node.ClassBody(elementList)); + }; + Parser.prototype.parseClassDeclaration = function (identifierIsOptional) { + var node = this.createNode(); + var previousStrict = this.context.strict; + this.context.strict = true; + this.expectKeyword('class'); + var id = (identifierIsOptional && (this.lookahead.type !== 3 /* Identifier */)) ? null : this.parseVariableIdentifier(); + var superClass = null; + if (this.matchKeyword('extends')) { + this.nextToken(); + superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall); + } + var classBody = this.parseClassBody(); + this.context.strict = previousStrict; + return this.finalize(node, new Node.ClassDeclaration(id, superClass, classBody)); + }; + Parser.prototype.parseClassExpression = function () { + var node = this.createNode(); + var previousStrict = this.context.strict; + this.context.strict = true; + this.expectKeyword('class'); + var id = (this.lookahead.type === 3 /* Identifier */) ? this.parseVariableIdentifier() : null; + var superClass = null; + if (this.matchKeyword('extends')) { + this.nextToken(); + superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall); + } + var classBody = this.parseClassBody(); + this.context.strict = previousStrict; + return this.finalize(node, new Node.ClassExpression(id, superClass, classBody)); + }; + // https://tc39.github.io/ecma262/#sec-scripts + // https://tc39.github.io/ecma262/#sec-modules + Parser.prototype.parseModule = function () { + this.context.strict = true; + this.context.isModule = true; + this.scanner.isModule = true; + var node = this.createNode(); + var body = this.parseDirectivePrologues(); + while (this.lookahead.type !== 2 /* EOF */) { + body.push(this.parseStatementListItem()); + } + return this.finalize(node, new Node.Module(body)); + }; + Parser.prototype.parseScript = function () { + var node = this.createNode(); + var body = this.parseDirectivePrologues(); + while (this.lookahead.type !== 2 /* EOF */) { + body.push(this.parseStatementListItem()); + } + return this.finalize(node, new Node.Script(body)); + }; + // https://tc39.github.io/ecma262/#sec-imports + Parser.prototype.parseModuleSpecifier = function () { + var node = this.createNode(); + if (this.lookahead.type !== 8 /* StringLiteral */) { + this.throwError(messages_1.Messages.InvalidModuleSpecifier); + } + var token = this.nextToken(); + var raw = this.getTokenRaw(token); + return this.finalize(node, new Node.Literal(token.value, raw)); + }; + // import {} ...; + Parser.prototype.parseImportSpecifier = function () { + var node = this.createNode(); + var imported; + var local; + if (this.lookahead.type === 3 /* Identifier */) { + imported = this.parseVariableIdentifier(); + local = imported; + if (this.matchContextualKeyword('as')) { + this.nextToken(); + local = this.parseVariableIdentifier(); + } + } + else { + imported = this.parseIdentifierName(); + local = imported; + if (this.matchContextualKeyword('as')) { + this.nextToken(); + local = this.parseVariableIdentifier(); + } + else { + this.throwUnexpectedToken(this.nextToken()); + } + } + return this.finalize(node, new Node.ImportSpecifier(local, imported)); + }; + // {foo, bar as bas} + Parser.prototype.parseNamedImports = function () { + this.expect('{'); + var specifiers = []; + while (!this.match('}')) { + specifiers.push(this.parseImportSpecifier()); + if (!this.match('}')) { + this.expect(','); + } + } + this.expect('}'); + return specifiers; + }; + // import ...; + Parser.prototype.parseImportDefaultSpecifier = function () { + var node = this.createNode(); + var local = this.parseIdentifierName(); + return this.finalize(node, new Node.ImportDefaultSpecifier(local)); + }; + // import <* as foo> ...; + Parser.prototype.parseImportNamespaceSpecifier = function () { + var node = this.createNode(); + this.expect('*'); + if (!this.matchContextualKeyword('as')) { + this.throwError(messages_1.Messages.NoAsAfterImportNamespace); + } + this.nextToken(); + var local = this.parseIdentifierName(); + return this.finalize(node, new Node.ImportNamespaceSpecifier(local)); + }; + Parser.prototype.parseImportDeclaration = function () { + if (this.context.inFunctionBody) { + this.throwError(messages_1.Messages.IllegalImportDeclaration); + } + var node = this.createNode(); + this.expectKeyword('import'); + var src; + var specifiers = []; + if (this.lookahead.type === 8 /* StringLiteral */) { + // import 'foo'; + src = this.parseModuleSpecifier(); + } + else { + if (this.match('{')) { + // import {bar} + specifiers = specifiers.concat(this.parseNamedImports()); + } + else if (this.match('*')) { + // import * as foo + specifiers.push(this.parseImportNamespaceSpecifier()); + } + else if (this.isIdentifierName(this.lookahead) && !this.matchKeyword('default')) { + // import foo + specifiers.push(this.parseImportDefaultSpecifier()); + if (this.match(',')) { + this.nextToken(); + if (this.match('*')) { + // import foo, * as foo + specifiers.push(this.parseImportNamespaceSpecifier()); + } + else if (this.match('{')) { + // import foo, {bar} + specifiers = specifiers.concat(this.parseNamedImports()); + } + else { + this.throwUnexpectedToken(this.lookahead); + } + } + } + else { + this.throwUnexpectedToken(this.nextToken()); + } + if (!this.matchContextualKeyword('from')) { + var message = this.lookahead.value ? messages_1.Messages.UnexpectedToken : messages_1.Messages.MissingFromClause; + this.throwError(message, this.lookahead.value); + } + this.nextToken(); + src = this.parseModuleSpecifier(); + } + this.consumeSemicolon(); + return this.finalize(node, new Node.ImportDeclaration(specifiers, src)); + }; + // https://tc39.github.io/ecma262/#sec-exports + Parser.prototype.parseExportSpecifier = function () { + var node = this.createNode(); + var local = this.parseIdentifierName(); + var exported = local; + if (this.matchContextualKeyword('as')) { + this.nextToken(); + exported = this.parseIdentifierName(); + } + return this.finalize(node, new Node.ExportSpecifier(local, exported)); + }; + Parser.prototype.parseExportDeclaration = function () { + if (this.context.inFunctionBody) { + this.throwError(messages_1.Messages.IllegalExportDeclaration); + } + var node = this.createNode(); + this.expectKeyword('export'); + var exportDeclaration; + if (this.matchKeyword('default')) { + // export default ... + this.nextToken(); + if (this.matchKeyword('function')) { + // export default function foo () {} + // export default function () {} + var declaration = this.parseFunctionDeclaration(true); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + else if (this.matchKeyword('class')) { + // export default class foo {} + var declaration = this.parseClassDeclaration(true); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + else if (this.matchContextualKeyword('async')) { + // export default async function f () {} + // export default async function () {} + // export default async x => x + var declaration = this.matchAsyncFunction() ? this.parseFunctionDeclaration(true) : this.parseAssignmentExpression(); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + else { + if (this.matchContextualKeyword('from')) { + this.throwError(messages_1.Messages.UnexpectedToken, this.lookahead.value); + } + // export default {}; + // export default []; + // export default (1 + 2); + var declaration = this.match('{') ? this.parseObjectInitializer() : + this.match('[') ? this.parseArrayInitializer() : this.parseAssignmentExpression(); + this.consumeSemicolon(); + exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration)); + } + } + else if (this.match('*')) { + // export * from 'foo'; + this.nextToken(); + if (!this.matchContextualKeyword('from')) { + var message = this.lookahead.value ? messages_1.Messages.UnexpectedToken : messages_1.Messages.MissingFromClause; + this.throwError(message, this.lookahead.value); + } + this.nextToken(); + var src = this.parseModuleSpecifier(); + this.consumeSemicolon(); + exportDeclaration = this.finalize(node, new Node.ExportAllDeclaration(src)); + } + else if (this.lookahead.type === 4 /* Keyword */) { + // export var f = 1; + var declaration = void 0; + switch (this.lookahead.value) { + case 'let': + case 'const': + declaration = this.parseLexicalDeclaration({ inFor: false }); + break; + case 'var': + case 'class': + case 'function': + declaration = this.parseStatementListItem(); + break; + default: + this.throwUnexpectedToken(this.lookahead); + } + exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(declaration, [], null)); + } + else if (this.matchAsyncFunction()) { + var declaration = this.parseFunctionDeclaration(); + exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(declaration, [], null)); + } + else { + var specifiers = []; + var source = null; + var isExportFromIdentifier = false; + this.expect('{'); + while (!this.match('}')) { + isExportFromIdentifier = isExportFromIdentifier || this.matchKeyword('default'); + specifiers.push(this.parseExportSpecifier()); + if (!this.match('}')) { + this.expect(','); + } + } + this.expect('}'); + if (this.matchContextualKeyword('from')) { + // export {default} from 'foo'; + // export {foo} from 'foo'; + this.nextToken(); + source = this.parseModuleSpecifier(); + this.consumeSemicolon(); + } + else if (isExportFromIdentifier) { + // export {default}; // missing fromClause + var message = this.lookahead.value ? messages_1.Messages.UnexpectedToken : messages_1.Messages.MissingFromClause; + this.throwError(message, this.lookahead.value); + } + else { + // export {foo}; + this.consumeSemicolon(); + } + exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(null, specifiers, source)); + } + return exportDeclaration; + }; + return Parser; + }()); + exports.Parser = Parser; + + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + "use strict"; + // Ensure the condition is true, otherwise throw an error. + // This is only to have a better contract semantic, i.e. another safety net + // to catch a logic error. The condition shall be fulfilled in normal case. + // Do NOT use this to enforce a certain condition on any user input. + Object.defineProperty(exports, "__esModule", { value: true }); + function assert(condition, message) { + /* istanbul ignore if */ + if (!condition) { + throw new Error('ASSERT: ' + message); + } + } + exports.assert = assert; + + +/***/ }, +/* 10 */ +/***/ function(module, exports) { + + "use strict"; + /* tslint:disable:max-classes-per-file */ + Object.defineProperty(exports, "__esModule", { value: true }); + var ErrorHandler = (function () { + function ErrorHandler() { + this.errors = []; + this.tolerant = false; + } + ErrorHandler.prototype.recordError = function (error) { + this.errors.push(error); + }; + ErrorHandler.prototype.tolerate = function (error) { + if (this.tolerant) { + this.recordError(error); + } + else { + throw error; + } + }; + ErrorHandler.prototype.constructError = function (msg, column) { + var error = new Error(msg); + try { + throw error; + } + catch (base) { + /* istanbul ignore else */ + if (Object.create && Object.defineProperty) { + error = Object.create(base); + Object.defineProperty(error, 'column', { value: column }); + } + } + /* istanbul ignore next */ + return error; + }; + ErrorHandler.prototype.createError = function (index, line, col, description) { + var msg = 'Line ' + line + ': ' + description; + var error = this.constructError(msg, col); + error.index = index; + error.lineNumber = line; + error.description = description; + return error; + }; + ErrorHandler.prototype.throwError = function (index, line, col, description) { + throw this.createError(index, line, col, description); + }; + ErrorHandler.prototype.tolerateError = function (index, line, col, description) { + var error = this.createError(index, line, col, description); + if (this.tolerant) { + this.recordError(error); + } + else { + throw error; + } + }; + return ErrorHandler; + }()); + exports.ErrorHandler = ErrorHandler; + + +/***/ }, +/* 11 */ +/***/ function(module, exports) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + // Error messages should be identical to V8. + exports.Messages = { + BadGetterArity: 'Getter must not have any formal parameters', + BadSetterArity: 'Setter must have exactly one formal parameter', + BadSetterRestParameter: 'Setter function argument must not be a rest parameter', + ConstructorIsAsync: 'Class constructor may not be an async method', + ConstructorSpecialMethod: 'Class constructor may not be an accessor', + DeclarationMissingInitializer: 'Missing initializer in %0 declaration', + DefaultRestParameter: 'Unexpected token =', + DuplicateBinding: 'Duplicate binding %0', + DuplicateConstructor: 'A class may only have one constructor', + DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals', + ForInOfLoopInitializer: '%0 loop variable declaration may not have an initializer', + GeneratorInLegacyContext: 'Generator declarations are not allowed in legacy contexts', + IllegalBreak: 'Illegal break statement', + IllegalContinue: 'Illegal continue statement', + IllegalExportDeclaration: 'Unexpected token', + IllegalImportDeclaration: 'Unexpected token', + IllegalLanguageModeDirective: 'Illegal \'use strict\' directive in function with non-simple parameter list', + IllegalReturn: 'Illegal return statement', + InvalidEscapedReservedWord: 'Keyword must not contain escaped characters', + InvalidHexEscapeSequence: 'Invalid hexadecimal escape sequence', + InvalidLHSInAssignment: 'Invalid left-hand side in assignment', + InvalidLHSInForIn: 'Invalid left-hand side in for-in', + InvalidLHSInForLoop: 'Invalid left-hand side in for-loop', + InvalidModuleSpecifier: 'Unexpected token', + InvalidRegExp: 'Invalid regular expression', + LetInLexicalBinding: 'let is disallowed as a lexically bound name', + MissingFromClause: 'Unexpected token', + MultipleDefaultsInSwitch: 'More than one default clause in switch statement', + NewlineAfterThrow: 'Illegal newline after throw', + NoAsAfterImportNamespace: 'Unexpected token', + NoCatchOrFinally: 'Missing catch or finally after try', + ParameterAfterRestParameter: 'Rest parameter must be last formal parameter', + Redeclaration: '%0 \'%1\' has already been declared', + StaticPrototype: 'Classes may not have static property named prototype', + StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', + StrictDelete: 'Delete of an unqualified identifier in strict mode.', + StrictFunction: 'In strict mode code, functions can only be declared at top level or inside a block', + StrictFunctionName: 'Function name may not be eval or arguments in strict mode', + StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', + StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', + StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', + StrictModeWith: 'Strict mode code may not include a with statement', + StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', + StrictParamDupe: 'Strict mode function may not have duplicate parameter names', + StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', + StrictReservedWord: 'Use of future reserved word in strict mode', + StrictVarName: 'Variable name may not be eval or arguments in strict mode', + TemplateOctalLiteral: 'Octal literals are not allowed in template strings.', + UnexpectedEOS: 'Unexpected end of input', + UnexpectedIdentifier: 'Unexpected identifier', + UnexpectedNumber: 'Unexpected number', + UnexpectedReserved: 'Unexpected reserved word', + UnexpectedString: 'Unexpected string', + UnexpectedTemplate: 'Unexpected quasi %0', + UnexpectedToken: 'Unexpected token %0', + UnexpectedTokenIllegal: 'Unexpected token ILLEGAL', + UnknownLabel: 'Undefined label \'%0\'', + UnterminatedRegExp: 'Invalid regular expression: missing /' + }; + + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var assert_1 = __webpack_require__(9); + var character_1 = __webpack_require__(4); + var messages_1 = __webpack_require__(11); + function hexValue(ch) { + return '0123456789abcdef'.indexOf(ch.toLowerCase()); + } + function octalValue(ch) { + return '01234567'.indexOf(ch); + } + var Scanner = (function () { + function Scanner(code, handler) { + this.source = code; + this.errorHandler = handler; + this.trackComment = false; + this.isModule = false; + this.length = code.length; + this.index = 0; + this.lineNumber = (code.length > 0) ? 1 : 0; + this.lineStart = 0; + this.curlyStack = []; + } + Scanner.prototype.saveState = function () { + return { + index: this.index, + lineNumber: this.lineNumber, + lineStart: this.lineStart + }; + }; + Scanner.prototype.restoreState = function (state) { + this.index = state.index; + this.lineNumber = state.lineNumber; + this.lineStart = state.lineStart; + }; + Scanner.prototype.eof = function () { + return this.index >= this.length; + }; + Scanner.prototype.throwUnexpectedToken = function (message) { + if (message === void 0) { message = messages_1.Messages.UnexpectedTokenIllegal; } + return this.errorHandler.throwError(this.index, this.lineNumber, this.index - this.lineStart + 1, message); + }; + Scanner.prototype.tolerateUnexpectedToken = function (message) { + if (message === void 0) { message = messages_1.Messages.UnexpectedTokenIllegal; } + this.errorHandler.tolerateError(this.index, this.lineNumber, this.index - this.lineStart + 1, message); + }; + // https://tc39.github.io/ecma262/#sec-comments + Scanner.prototype.skipSingleLineComment = function (offset) { + var comments = []; + var start, loc; + if (this.trackComment) { + comments = []; + start = this.index - offset; + loc = { + start: { + line: this.lineNumber, + column: this.index - this.lineStart - offset + }, + end: {} + }; + } + while (!this.eof()) { + var ch = this.source.charCodeAt(this.index); + ++this.index; + if (character_1.Character.isLineTerminator(ch)) { + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart - 1 + }; + var entry = { + multiLine: false, + slice: [start + offset, this.index - 1], + range: [start, this.index - 1], + loc: loc + }; + comments.push(entry); + } + if (ch === 13 && this.source.charCodeAt(this.index) === 10) { + ++this.index; + } + ++this.lineNumber; + this.lineStart = this.index; + return comments; + } + } + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; + var entry = { + multiLine: false, + slice: [start + offset, this.index], + range: [start, this.index], + loc: loc + }; + comments.push(entry); + } + return comments; + }; + Scanner.prototype.skipMultiLineComment = function () { + var comments = []; + var start, loc; + if (this.trackComment) { + comments = []; + start = this.index - 2; + loc = { + start: { + line: this.lineNumber, + column: this.index - this.lineStart - 2 + }, + end: {} + }; + } + while (!this.eof()) { + var ch = this.source.charCodeAt(this.index); + if (character_1.Character.isLineTerminator(ch)) { + if (ch === 0x0D && this.source.charCodeAt(this.index + 1) === 0x0A) { + ++this.index; + } + ++this.lineNumber; + ++this.index; + this.lineStart = this.index; + } + else if (ch === 0x2A) { + // Block comment ends with '*/'. + if (this.source.charCodeAt(this.index + 1) === 0x2F) { + this.index += 2; + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; + var entry = { + multiLine: true, + slice: [start + 2, this.index - 2], + range: [start, this.index], + loc: loc + }; + comments.push(entry); + } + return comments; + } + ++this.index; + } + else { + ++this.index; + } + } + // Ran off the end of the file - the whole thing is a comment + if (this.trackComment) { + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; + var entry = { + multiLine: true, + slice: [start + 2, this.index], + range: [start, this.index], + loc: loc + }; + comments.push(entry); + } + this.tolerateUnexpectedToken(); + return comments; + }; + Scanner.prototype.scanComments = function () { + var comments; + if (this.trackComment) { + comments = []; + } + var start = (this.index === 0); + while (!this.eof()) { + var ch = this.source.charCodeAt(this.index); + if (character_1.Character.isWhiteSpace(ch)) { + ++this.index; + } + else if (character_1.Character.isLineTerminator(ch)) { + ++this.index; + if (ch === 0x0D && this.source.charCodeAt(this.index) === 0x0A) { + ++this.index; + } + ++this.lineNumber; + this.lineStart = this.index; + start = true; + } + else if (ch === 0x2F) { + ch = this.source.charCodeAt(this.index + 1); + if (ch === 0x2F) { + this.index += 2; + var comment = this.skipSingleLineComment(2); + if (this.trackComment) { + comments = comments.concat(comment); + } + start = true; + } + else if (ch === 0x2A) { + this.index += 2; + var comment = this.skipMultiLineComment(); + if (this.trackComment) { + comments = comments.concat(comment); + } + } + else { + break; + } + } + else if (start && ch === 0x2D) { + // U+003E is '>' + if ((this.source.charCodeAt(this.index + 1) === 0x2D) && (this.source.charCodeAt(this.index + 2) === 0x3E)) { + // '-->' is a single-line comment + this.index += 3; + var comment = this.skipSingleLineComment(3); + if (this.trackComment) { + comments = comments.concat(comment); + } + } + else { + break; + } + } + else if (ch === 0x3C && !this.isModule) { + if (this.source.slice(this.index + 1, this.index + 4) === '!--') { + this.index += 4; // ` regexps + set = set.map(function (s, si, set) { + return s.map(this.parse, this) + }, this) + + this.debug(this.pattern, set) + + // filter out everything that didn't compile properly. + set = set.filter(function (s) { + return s.indexOf(false) === -1 + }) + + this.debug(this.pattern, set) + + this.set = set +} + +Minimatch.prototype.parseNegate = parseNegate +function parseNegate () { + var pattern = this.pattern + var negate = false + var options = this.options + var negateOffset = 0 + + if (options.nonegate) return + + for (var i = 0, l = pattern.length + ; i < l && pattern.charAt(i) === '!' + ; i++) { + negate = !negate + negateOffset++ + } + + if (negateOffset) this.pattern = pattern.substr(negateOffset) + this.negate = negate +} + +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +minimatch.braceExpand = function (pattern, options) { + return braceExpand(pattern, options) +} + +Minimatch.prototype.braceExpand = braceExpand + +function braceExpand (pattern, options) { + if (!options) { + if (this instanceof Minimatch) { + options = this.options + } else { + options = {} + } + } + + pattern = typeof pattern === 'undefined' + ? this.pattern : pattern + + if (typeof pattern === 'undefined') { + throw new TypeError('undefined pattern') + } + + if (options.nobrace || + !pattern.match(/\{.*\}/)) { + // shortcut. no need to expand. + return [pattern] + } + + return expand(pattern) +} + +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +Minimatch.prototype.parse = parse +var SUBPARSE = {} +function parse (pattern, isSub) { + if (pattern.length > 1024 * 64) { + throw new TypeError('pattern is too long') + } + + var options = this.options + + // shortcuts + if (!options.noglobstar && pattern === '**') return GLOBSTAR + if (pattern === '') return '' + + var re = '' + var hasMagic = !!options.nocase + var escaping = false + // ? => one single character + var patternListStack = [] + var negativeLists = [] + var stateChar + var inClass = false + var reClassStart = -1 + var classStart = -1 + // . and .. never match anything that doesn't start with ., + // even when options.dot is set. + var patternStart = pattern.charAt(0) === '.' ? '' // anything + // not (start or / followed by . or .. followed by / or end) + : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' + : '(?!\\.)' + var self = this + + function clearStateChar () { + if (stateChar) { + // we had some state-tracking character + // that wasn't consumed by this pass. + switch (stateChar) { + case '*': + re += star + hasMagic = true + break + case '?': + re += qmark + hasMagic = true + break + default: + re += '\\' + stateChar + break + } + self.debug('clearStateChar %j %j', stateChar, re) + stateChar = false + } + } + + for (var i = 0, len = pattern.length, c + ; (i < len) && (c = pattern.charAt(i)) + ; i++) { + this.debug('%s\t%s %s %j', pattern, i, re, c) + + // skip over any that are escaped. + if (escaping && reSpecials[c]) { + re += '\\' + c + escaping = false + continue + } + + switch (c) { + case '/': + // completely not allowed, even escaped. + // Should already be path-split by now. + return false + + case '\\': + clearStateChar() + escaping = true + continue + + // the various stateChar values + // for the "extglob" stuff. + case '?': + case '*': + case '+': + case '@': + case '!': + this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) + + // all of those are literals inside a class, except that + // the glob [!a] means [^a] in regexp + if (inClass) { + this.debug(' in class') + if (c === '!' && i === classStart + 1) c = '^' + re += c + continue + } + + // if we already have a stateChar, then it means + // that there was something like ** or +? in there. + // Handle the stateChar, then proceed with this one. + self.debug('call clearStateChar %j', stateChar) + clearStateChar() + stateChar = c + // if extglob is disabled, then +(asdf|foo) isn't a thing. + // just clear the statechar *now*, rather than even diving into + // the patternList stuff. + if (options.noext) clearStateChar() + continue + + case '(': + if (inClass) { + re += '(' + continue + } + + if (!stateChar) { + re += '\\(' + continue + } + + patternListStack.push({ + type: stateChar, + start: i - 1, + reStart: re.length, + open: plTypes[stateChar].open, + close: plTypes[stateChar].close + }) + // negation is (?:(?!js)[^/]*) + re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + this.debug('plType %j %j', stateChar, re) + stateChar = false + continue + + case ')': + if (inClass || !patternListStack.length) { + re += '\\)' + continue + } + + clearStateChar() + hasMagic = true + var pl = patternListStack.pop() + // negation is (?:(?!js)[^/]*) + // The others are (?:) + re += pl.close + if (pl.type === '!') { + negativeLists.push(pl) + } + pl.reEnd = re.length + continue + + case '|': + if (inClass || !patternListStack.length || escaping) { + re += '\\|' + escaping = false + continue + } + + clearStateChar() + re += '|' + continue + + // these are mostly the same in regexp and glob + case '[': + // swallow any state-tracking char before the [ + clearStateChar() + + if (inClass) { + re += '\\' + c + continue + } + + inClass = true + classStart = i + reClassStart = re.length + re += c + continue + + case ']': + // a right bracket shall lose its special + // meaning and represent itself in + // a bracket expression if it occurs + // first in the list. -- POSIX.2 2.8.3.2 + if (i === classStart + 1 || !inClass) { + re += '\\' + c + escaping = false + continue + } + + // handle the case where we left a class open. + // "[z-a]" is valid, equivalent to "\[z-a\]" + if (inClass) { + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue + } + } + + // finish up the class. + hasMagic = true + inClass = false + re += c + continue + + default: + // swallow any state char that wasn't consumed + clearStateChar() + + if (escaping) { + // no need + escaping = false + } else if (reSpecials[c] + && !(c === '^' && inClass)) { + re += '\\' + } + + re += c + + } // switch + } // for + + // handle the case where we left a class open. + // "[abc" is valid, equivalent to "\[abc" + if (inClass) { + // split where the last [ was, and escape it + // this is a huge pita. We now have to re-walk + // the contents of the would-be class to re-translate + // any characters that were passed through as-is + cs = pattern.substr(classStart + 1) + sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + hasMagic = hasMagic || sp[1] + } + + // handle the case where we had a +( thing at the *end* + // of the pattern. + // each pattern list stack adds 3 chars, and we need to go through + // and escape any | chars that were passed through as-is for the regexp. + // Go through and escape them, taking care not to double-escape any + // | chars that were already escaped. + for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { + var tail = re.slice(pl.reStart + pl.open.length) + this.debug('setting tail', re, pl) + // maybe some even number of \, then maybe 1 \, followed by a | + tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { + if (!$2) { + // the | isn't already escaped, so escape it. + $2 = '\\' + } + + // need to escape all those slashes *again*, without escaping the + // one that we need for escaping the | character. As it works out, + // escaping an even number of slashes can be done by simply repeating + // it exactly after itself. That's why this trick works. + // + // I am sorry that you have to see this. + return $1 + $1 + $2 + '|' + }) + + this.debug('tail=%j\n %s', tail, tail, pl, re) + var t = pl.type === '*' ? star + : pl.type === '?' ? qmark + : '\\' + pl.type + + hasMagic = true + re = re.slice(0, pl.reStart) + t + '\\(' + tail + } + + // handle trailing things that only matter at the very end. + clearStateChar() + if (escaping) { + // trailing \\ + re += '\\\\' + } + + // only need to apply the nodot start if the re starts with + // something that could conceivably capture a dot + var addPatternStart = false + switch (re.charAt(0)) { + case '.': + case '[': + case '(': addPatternStart = true + } + + // Hack to work around lack of negative lookbehind in JS + // A pattern like: *.!(x).!(y|z) needs to ensure that a name + // like 'a.xyz.yz' doesn't match. So, the first negative + // lookahead, has to look ALL the way ahead, to the end of + // the pattern. + for (var n = negativeLists.length - 1; n > -1; n--) { + var nl = negativeLists[n] + + var nlBefore = re.slice(0, nl.reStart) + var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) + var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + var nlAfter = re.slice(nl.reEnd) + + nlLast += nlAfter + + // Handle nested stuff like *(*.js|!(*.json)), where open parens + // mean that we should *not* include the ) in the bit that is considered + // "after" the negated section. + var openParensBefore = nlBefore.split('(').length - 1 + var cleanAfter = nlAfter + for (i = 0; i < openParensBefore; i++) { + cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') + } + nlAfter = cleanAfter + + var dollar = '' + if (nlAfter === '' && isSub !== SUBPARSE) { + dollar = '$' + } + var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast + re = newRe + } + + // if the re is not "" at this point, then we need to make sure + // it doesn't match against an empty path part. + // Otherwise a/* will match a/, which it should not. + if (re !== '' && hasMagic) { + re = '(?=.)' + re + } + + if (addPatternStart) { + re = patternStart + re + } + + // parsing just a piece of a larger pattern. + if (isSub === SUBPARSE) { + return [re, hasMagic] + } + + // skip the regexp for non-magical patterns + // unescape anything in it, though, so that it'll be + // an exact match against a file etc. + if (!hasMagic) { + return globUnescape(pattern) + } + + var flags = options.nocase ? 'i' : '' + try { + var regExp = new RegExp('^' + re + '$', flags) + } catch (er) { + // If it was an invalid regular expression, then it can't match + // anything. This trick looks for a character after the end of + // the string, which is of course impossible, except in multi-line + // mode, but it's not a /m regex. + return new RegExp('$.') + } + + regExp._glob = pattern + regExp._src = re + + return regExp +} + +minimatch.makeRe = function (pattern, options) { + return new Minimatch(pattern, options || {}).makeRe() +} + +Minimatch.prototype.makeRe = makeRe +function makeRe () { + if (this.regexp || this.regexp === false) return this.regexp + + // at this point, this.set is a 2d array of partial + // pattern strings, or "**". + // + // It's better to use .match(). This function shouldn't + // be used, really, but it's pretty convenient sometimes, + // when you just want to work with a regex. + var set = this.set + + if (!set.length) { + this.regexp = false + return this.regexp + } + var options = this.options + + var twoStar = options.noglobstar ? star + : options.dot ? twoStarDot + : twoStarNoDot + var flags = options.nocase ? 'i' : '' + + var re = set.map(function (pattern) { + return pattern.map(function (p) { + return (p === GLOBSTAR) ? twoStar + : (typeof p === 'string') ? regExpEscape(p) + : p._src + }).join('\\\/') + }).join('|') + + // must match entire pattern + // ending in a * or ** will make it less strict. + re = '^(?:' + re + ')$' + + // can match anything, as long as it's not this. + if (this.negate) re = '^(?!' + re + ').*$' + + try { + this.regexp = new RegExp(re, flags) + } catch (ex) { + this.regexp = false + } + return this.regexp +} + +minimatch.match = function (list, pattern, options) { + options = options || {} + var mm = new Minimatch(pattern, options) + list = list.filter(function (f) { + return mm.match(f) + }) + if (mm.options.nonull && !list.length) { + list.push(pattern) + } + return list +} + +Minimatch.prototype.match = match +function match (f, partial) { + this.debug('match', f, this.pattern) + // short-circuit in the case of busted things. + // comments, etc. + if (this.comment) return false + if (this.empty) return f === '' + + if (f === '/' && partial) return true + + var options = this.options + + // windows: need to use /, not \ + if (path.sep !== '/') { + f = f.split(path.sep).join('/') + } + + // treat the test path as a set of pathparts. + f = f.split(slashSplit) + this.debug(this.pattern, 'split', f) + + // just ONE of the pattern sets in this.set needs to match + // in order for it to be valid. If negating, then just one + // match means that we have failed. + // Either way, return on the first hit. + + var set = this.set + this.debug(this.pattern, 'set', set) + + // Find the basename of the path by looking for the last non-empty segment + var filename + var i + for (i = f.length - 1; i >= 0; i--) { + filename = f[i] + if (filename) break + } + + for (i = 0; i < set.length; i++) { + var pattern = set[i] + var file = f + if (options.matchBase && pattern.length === 1) { + file = [filename] + } + var hit = this.matchOne(file, pattern, partial) + if (hit) { + if (options.flipNegate) return true + return !this.negate + } + } + + // didn't get any hits. this is success if it's a negative + // pattern, failure otherwise. + if (options.flipNegate) return false + return this.negate +} + +// set partial to true to test if, for example, +// "/a/b" matches the start of "/*/b/*/d" +// Partial means, if you run out of file before you run +// out of pattern, then that's fine, as long as all +// the parts match. +Minimatch.prototype.matchOne = function (file, pattern, partial) { + var options = this.options + + this.debug('matchOne', + { 'this': this, file: file, pattern: pattern }) + + this.debug('matchOne', file.length, pattern.length) + + for (var fi = 0, + pi = 0, + fl = file.length, + pl = pattern.length + ; (fi < fl) && (pi < pl) + ; fi++, pi++) { + this.debug('matchOne loop') + var p = pattern[pi] + var f = file[fi] + + this.debug(pattern, p, f) + + // should be impossible. + // some invalid regexp stuff in the set. + if (p === false) return false + + if (p === GLOBSTAR) { + this.debug('GLOBSTAR', [pattern, p, f]) + + // "**" + // a/**/b/**/c would match the following: + // a/b/x/y/z/c + // a/x/y/z/b/c + // a/b/x/b/x/c + // a/b/c + // To do this, take the rest of the pattern after + // the **, and see if it would match the file remainder. + // If so, return success. + // If not, the ** "swallows" a segment, and try again. + // This is recursively awful. + // + // a/**/b/**/c matching a/b/x/y/z/c + // - a matches a + // - doublestar + // - matchOne(b/x/y/z/c, b/**/c) + // - b matches b + // - doublestar + // - matchOne(x/y/z/c, c) -> no + // - matchOne(y/z/c, c) -> no + // - matchOne(z/c, c) -> no + // - matchOne(c, c) yes, hit + var fr = fi + var pr = pi + 1 + if (pr === pl) { + this.debug('** at the end') + // a ** at the end will just swallow the rest. + // We have found a match. + // however, it will not swallow /.x, unless + // options.dot is set. + // . and .. are *never* matched by **, for explosively + // exponential reasons. + for (; fi < fl; fi++) { + if (file[fi] === '.' || file[fi] === '..' || + (!options.dot && file[fi].charAt(0) === '.')) return false + } + return true + } + + // ok, let's see if we can swallow whatever we can. + while (fr < fl) { + var swallowee = file[fr] + + this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + + // XXX remove this slice. Just pass the start index. + if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { + this.debug('globstar found match!', fr, fl, swallowee) + // found a match. + return true + } else { + // can't swallow "." or ".." ever. + // can only swallow ".foo" when explicitly asked. + if (swallowee === '.' || swallowee === '..' || + (!options.dot && swallowee.charAt(0) === '.')) { + this.debug('dot detected!', file, fr, pattern, pr) + break + } + + // ** swallows a segment, and continue. + this.debug('globstar swallow a segment, and continue') + fr++ + } + } + + // no match was found. + // However, in partial mode, we can't say this is necessarily over. + // If there's more *pattern* left, then + if (partial) { + // ran out of file + this.debug('\n>>> no match, partial?', file, fr, pattern, pr) + if (fr === fl) return true + } + return false + } + + // something other than ** + // non-magic patterns just have to match exactly + // patterns with magic have been turned into regexps. + var hit + if (typeof p === 'string') { + if (options.nocase) { + hit = f.toLowerCase() === p.toLowerCase() + } else { + hit = f === p + } + this.debug('string match', p, f, hit) + } else { + hit = f.match(p) + this.debug('pattern match', p, f, hit) + } + + if (!hit) return false + } + + // Note: ending in / means that we'll get a final "" + // at the end of the pattern. This can only match a + // corresponding "" at the end of the file. + // If the file ends in /, then it can only match a + // a pattern that ends in /, unless the pattern just + // doesn't have any more for it. But, a/b/ should *not* + // match "a/b/*", even though "" matches against the + // [^/]*? pattern, except in partial mode, where it might + // simply not be reached yet. + // However, a/b/ should still satisfy a/* + + // now either we fell off the end of the pattern, or we're done. + if (fi === fl && pi === pl) { + // ran out of pattern and filename at the same time. + // an exact hit! + return true + } else if (fi === fl) { + // ran out of file, but still had pattern left. + // this is ok if we're doing the match as part of + // a glob fs traversal. + return partial + } else if (pi === pl) { + // ran out of pattern, still have file left. + // this is only acceptable if we're on the very last + // empty segment of a file with a trailing slash. + // a/* should match a/b/ + var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') + return emptyFileEnd + } + + // should be unreachable. + throw new Error('wtf?') +} + +// replace stuff like \* with * +function globUnescape (s) { + return s.replace(/\\(.)/g, '$1') +} + +function regExpEscape (s) { + return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') +} diff --git a/WebServer/node_modules/minimatch/package.json b/WebServer/node_modules/minimatch/package.json new file mode 100644 index 000000000..da833d0b1 --- /dev/null +++ b/WebServer/node_modules/minimatch/package.json @@ -0,0 +1,67 @@ +{ + "_args": [ + [ + "minimatch@3.0.4", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "minimatch@3.0.4", + "_id": "minimatch@3.0.4", + "_inBundle": false, + "_integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "_location": "/minimatch", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "minimatch@3.0.4", + "name": "minimatch", + "escapedName": "minimatch", + "rawSpec": "3.0.4", + "saveSpec": null, + "fetchSpec": "3.0.4" + }, + "_requiredBy": [ + "/glob", + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "_spec": "3.0.4", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me" + }, + "bugs": { + "url": "https://github.com/isaacs/minimatch/issues" + }, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "description": "a glob matcher in javascript", + "devDependencies": { + "tap": "^10.3.2" + }, + "engines": { + "node": "*" + }, + "files": [ + "minimatch.js" + ], + "homepage": "https://github.com/isaacs/minimatch#readme", + "license": "ISC", + "main": "minimatch.js", + "name": "minimatch", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/minimatch.git" + }, + "scripts": { + "postpublish": "git push origin --all; git push origin --tags", + "postversion": "npm publish", + "preversion": "npm test", + "test": "tap test/*.js --cov" + }, + "version": "3.0.4" +} diff --git a/WebServer/node_modules/minimist/example/parse.js b/WebServer/node_modules/minimist/example/parse.js new file mode 100644 index 000000000..abff3e8ee --- /dev/null +++ b/WebServer/node_modules/minimist/example/parse.js @@ -0,0 +1,2 @@ +var argv = require('../')(process.argv.slice(2)); +console.dir(argv); diff --git a/WebServer/node_modules/minimist/index.js b/WebServer/node_modules/minimist/index.js new file mode 100644 index 000000000..584f551a6 --- /dev/null +++ b/WebServer/node_modules/minimist/index.js @@ -0,0 +1,187 @@ +module.exports = function (args, opts) { + if (!opts) opts = {}; + + var flags = { bools : {}, strings : {} }; + + [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { + flags.bools[key] = true; + }); + + [].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true; + }); + + var aliases = {}; + Object.keys(opts.alias || {}).forEach(function (key) { + aliases[key] = [].concat(opts.alias[key]); + aliases[key].forEach(function (x) { + aliases[x] = [key].concat(aliases[key].filter(function (y) { + return x !== y; + })); + }); + }); + + var defaults = opts['default'] || {}; + + var argv = { _ : [] }; + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] === undefined ? false : defaults[key]); + }); + + var notFlags = []; + + if (args.indexOf('--') !== -1) { + notFlags = args.slice(args.indexOf('--')+1); + args = args.slice(0, args.indexOf('--')); + } + + function setArg (key, val) { + var value = !flags.strings[key] && isNumber(val) + ? Number(val) : val + ; + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), value); + }); + } + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + if (/^--.+=/.test(arg)) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + var m = arg.match(/^--([^=]+)=([\s\S]*)$/); + setArg(m[1], m[2]); + } + else if (/^--no-.+/.test(arg)) { + var key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false); + } + else if (/^--.+/.test(arg)) { + var key = arg.match(/^--(.+)/)[1]; + var next = args[i + 1]; + if (next !== undefined && !/^-/.test(next) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, next); + i++; + } + else if (/^(true|false)$/.test(next)) { + setArg(key, next === 'true'); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true); + } + } + else if (/^-[^-]+/.test(arg)) { + var letters = arg.slice(1,-1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + var next = arg.slice(j+2); + + if (next === '-') { + setArg(letters[j], next) + continue; + } + + if (/[A-Za-z]/.test(letters[j]) + && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next); + broken = true; + break; + } + + if (letters[j+1] && letters[j+1].match(/\W/)) { + setArg(letters[j], arg.slice(j+2)); + broken = true; + break; + } + else { + setArg(letters[j], flags.strings[letters[j]] ? '' : true); + } + } + + var key = arg.slice(-1)[0]; + if (!broken && key !== '-') { + if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, args[i+1]); + i++; + } + else if (args[i+1] && /true|false/.test(args[i+1])) { + setArg(key, args[i+1] === 'true'); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true); + } + } + } + else { + argv._.push( + flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) + ); + } + } + + Object.keys(defaults).forEach(function (key) { + if (!hasKey(argv, key.split('.'))) { + setKey(argv, key.split('.'), defaults[key]); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), defaults[key]); + }); + } + }); + + notFlags.forEach(function(key) { + argv._.push(key); + }); + + return argv; +}; + +function hasKey (obj, keys) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + o = (o[key] || {}); + }); + + var key = keys[keys.length - 1]; + return key in o; +} + +function setKey (obj, keys, value) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + if (o[key] === undefined) o[key] = {}; + o = o[key]; + }); + + var key = keys[keys.length - 1]; + if (o[key] === undefined || typeof o[key] === 'boolean') { + o[key] = value; + } + else if (Array.isArray(o[key])) { + o[key].push(value); + } + else { + o[key] = [ o[key], value ]; + } +} + +function isNumber (x) { + if (typeof x === 'number') return true; + if (/^0x[0-9a-f]+$/i.test(x)) return true; + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); +} + +function longest (xs) { + return Math.max.apply(null, xs.map(function (x) { return x.length })); +} diff --git a/WebServer/node_modules/minimist/package.json b/WebServer/node_modules/minimist/package.json new file mode 100644 index 000000000..8312d0e1d --- /dev/null +++ b/WebServer/node_modules/minimist/package.json @@ -0,0 +1,74 @@ +{ + "_args": [ + [ + "minimist@0.0.8", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "minimist@0.0.8", + "_id": "minimist@0.0.8", + "_inBundle": false, + "_integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "_location": "/minimist", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "minimist@0.0.8", + "name": "minimist", + "escapedName": "minimist", + "rawSpec": "0.0.8", + "saveSpec": null, + "fetchSpec": "0.0.8" + }, + "_requiredBy": [ + "/mkdirp" + ], + "_resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "_spec": "0.0.8", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bugs": { + "url": "https://github.com/substack/minimist/issues" + }, + "description": "parse argument options", + "devDependencies": { + "tap": "~0.4.0", + "tape": "~1.0.4" + }, + "homepage": "https://github.com/substack/minimist", + "keywords": [ + "argv", + "getopt", + "parser", + "optimist" + ], + "license": "MIT", + "main": "index.js", + "name": "minimist", + "repository": { + "type": "git", + "url": "git://github.com/substack/minimist.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/6..latest", + "ff/5", + "firefox/latest", + "chrome/10", + "chrome/latest", + "safari/5.1", + "safari/latest", + "opera/12" + ] + }, + "version": "0.0.8" +} diff --git a/WebServer/node_modules/minimist/test/dash.js b/WebServer/node_modules/minimist/test/dash.js new file mode 100644 index 000000000..8b034b99a --- /dev/null +++ b/WebServer/node_modules/minimist/test/dash.js @@ -0,0 +1,24 @@ +var parse = require('../'); +var test = require('tape'); + +test('-', function (t) { + t.plan(5); + t.deepEqual(parse([ '-n', '-' ]), { n: '-', _: [] }); + t.deepEqual(parse([ '-' ]), { _: [ '-' ] }); + t.deepEqual(parse([ '-f-' ]), { f: '-', _: [] }); + t.deepEqual( + parse([ '-b', '-' ], { boolean: 'b' }), + { b: true, _: [ '-' ] } + ); + t.deepEqual( + parse([ '-s', '-' ], { string: 's' }), + { s: '-', _: [] } + ); +}); + +test('-a -- b', function (t) { + t.plan(3); + t.deepEqual(parse([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); + t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] }); +}); diff --git a/WebServer/node_modules/minimist/test/default_bool.js b/WebServer/node_modules/minimist/test/default_bool.js new file mode 100644 index 000000000..f0041ee40 --- /dev/null +++ b/WebServer/node_modules/minimist/test/default_bool.js @@ -0,0 +1,20 @@ +var test = require('tape'); +var parse = require('../'); + +test('boolean default true', function (t) { + var argv = parse([], { + boolean: 'sometrue', + default: { sometrue: true } + }); + t.equal(argv.sometrue, true); + t.end(); +}); + +test('boolean default false', function (t) { + var argv = parse([], { + boolean: 'somefalse', + default: { somefalse: false } + }); + t.equal(argv.somefalse, false); + t.end(); +}); diff --git a/WebServer/node_modules/minimist/test/dotted.js b/WebServer/node_modules/minimist/test/dotted.js new file mode 100644 index 000000000..ef0ae349b --- /dev/null +++ b/WebServer/node_modules/minimist/test/dotted.js @@ -0,0 +1,16 @@ +var parse = require('../'); +var test = require('tape'); + +test('dotted alias', function (t) { + var argv = parse(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 22); + t.equal(argv.aa.bb, 22); + t.end(); +}); + +test('dotted default', function (t) { + var argv = parse('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}}); + t.equal(argv.a.b, 11); + t.equal(argv.aa.bb, 11); + t.end(); +}); diff --git a/WebServer/node_modules/minimist/test/long.js b/WebServer/node_modules/minimist/test/long.js new file mode 100644 index 000000000..5d3a1e09d --- /dev/null +++ b/WebServer/node_modules/minimist/test/long.js @@ -0,0 +1,31 @@ +var test = require('tape'); +var parse = require('../'); + +test('long opts', function (t) { + t.deepEqual( + parse([ '--bool' ]), + { bool : true, _ : [] }, + 'long boolean' + ); + t.deepEqual( + parse([ '--pow', 'xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture sp' + ); + t.deepEqual( + parse([ '--pow=xixxle' ]), + { pow : 'xixxle', _ : [] }, + 'long capture eq' + ); + t.deepEqual( + parse([ '--host', 'localhost', '--port', '555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures sp' + ); + t.deepEqual( + parse([ '--host=localhost', '--port=555' ]), + { host : 'localhost', port : 555, _ : [] }, + 'long captures eq' + ); + t.end(); +}); diff --git a/WebServer/node_modules/minimist/test/parse.js b/WebServer/node_modules/minimist/test/parse.js new file mode 100644 index 000000000..8a9064669 --- /dev/null +++ b/WebServer/node_modules/minimist/test/parse.js @@ -0,0 +1,318 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse args', function (t) { + t.deepEqual( + parse([ '--no-moo' ]), + { moo : false, _ : [] }, + 'no' + ); + t.deepEqual( + parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]), + { v : ['a','b','c'], _ : [] }, + 'multi' + ); + t.end(); +}); + +test('comprehensive', function (t) { + t.deepEqual( + parse([ + '--name=meowmers', 'bare', '-cats', 'woo', + '-h', 'awesome', '--multi=quux', + '--key', 'value', + '-b', '--bool', '--no-meep', '--multi=baz', + '--', '--not-a-flag', 'eek' + ]), + { + c : true, + a : true, + t : true, + s : 'woo', + h : 'awesome', + b : true, + bool : true, + key : 'value', + multi : [ 'quux', 'baz' ], + meep : false, + name : 'meowmers', + _ : [ 'bare', '--not-a-flag', 'eek' ] + } + ); + t.end(); +}); + +test('nums', function (t) { + var argv = parse([ + '-x', '1234', + '-y', '5.67', + '-z', '1e7', + '-w', '10f', + '--hex', '0xdeadbeef', + '789' + ]); + t.deepEqual(argv, { + x : 1234, + y : 5.67, + z : 1e7, + w : '10f', + hex : 0xdeadbeef, + _ : [ 789 ] + }); + t.deepEqual(typeof argv.x, 'number'); + t.deepEqual(typeof argv.y, 'number'); + t.deepEqual(typeof argv.z, 'number'); + t.deepEqual(typeof argv.w, 'string'); + t.deepEqual(typeof argv.hex, 'number'); + t.deepEqual(typeof argv._[0], 'number'); + t.end(); +}); + +test('flag boolean', function (t) { + var argv = parse([ '-t', 'moo' ], { boolean: 't' }); + t.deepEqual(argv, { t : true, _ : [ 'moo' ] }); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('flag boolean value', function (t) { + var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], { + boolean: [ 't', 'verbose' ], + default: { verbose: true } + }); + + t.deepEqual(argv, { + verbose: false, + t: true, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); +}); + +test('flag boolean default false', function (t) { + var argv = parse(['moo'], { + boolean: ['t', 'verbose'], + default: { verbose: false, t: false } + }); + + t.deepEqual(argv, { + verbose: false, + t: false, + _: ['moo'] + }); + + t.deepEqual(typeof argv.verbose, 'boolean'); + t.deepEqual(typeof argv.t, 'boolean'); + t.end(); + +}); + +test('boolean groups', function (t) { + var argv = parse([ '-x', '-z', 'one', 'two', 'three' ], { + boolean: ['x','y','z'] + }); + + t.deepEqual(argv, { + x : true, + y : false, + z : true, + _ : [ 'one', 'two', 'three' ] + }); + + t.deepEqual(typeof argv.x, 'boolean'); + t.deepEqual(typeof argv.y, 'boolean'); + t.deepEqual(typeof argv.z, 'boolean'); + t.end(); +}); + +test('newlines in params' , function (t) { + var args = parse([ '-s', "X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + + // reproduce in bash: + // VALUE="new + // line" + // node program.js --s="$VALUE" + args = parse([ "--s=X\nX" ]) + t.deepEqual(args, { _ : [], s : "X\nX" }); + t.end(); +}); + +test('strings' , function (t) { + var s = parse([ '-s', '0001234' ], { string: 's' }).s; + t.equal(s, '0001234'); + t.equal(typeof s, 'string'); + + var x = parse([ '-x', '56' ], { string: 'x' }).x; + t.equal(x, '56'); + t.equal(typeof x, 'string'); + t.end(); +}); + +test('stringArgs', function (t) { + var s = parse([ ' ', ' ' ], { string: '_' })._; + t.same(s.length, 2); + t.same(typeof s[0], 'string'); + t.same(s[0], ' '); + t.same(typeof s[1], 'string'); + t.same(s[1], ' '); + t.end(); +}); + +test('empty strings', function(t) { + var s = parse([ '-s' ], { string: 's' }).s; + t.equal(s, ''); + t.equal(typeof s, 'string'); + + var str = parse([ '--str' ], { string: 'str' }).str; + t.equal(str, ''); + t.equal(typeof str, 'string'); + + var letters = parse([ '-art' ], { + string: [ 'a', 't' ] + }); + + t.equal(letters.a, ''); + t.equal(letters.r, true); + t.equal(letters.t, ''); + + t.end(); +}); + + +test('slashBreak', function (t) { + t.same( + parse([ '-I/foo/bar/baz' ]), + { I : '/foo/bar/baz', _ : [] } + ); + t.same( + parse([ '-xyz/foo/bar/baz' ]), + { x : true, y : true, z : '/foo/bar/baz', _ : [] } + ); + t.end(); +}); + +test('alias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: 'zoom' } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.f, 11); + t.end(); +}); + +test('multiAlias', function (t) { + var argv = parse([ '-f', '11', '--zoom', '55' ], { + alias: { z: [ 'zm', 'zoom' ] } + }); + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.z, argv.zm); + t.equal(argv.f, 11); + t.end(); +}); + +test('nested dotted objects', function (t) { + var argv = parse([ + '--foo.bar', '3', '--foo.baz', '4', + '--foo.quux.quibble', '5', '--foo.quux.o_O', + '--beep.boop' + ]); + + t.same(argv.foo, { + bar : 3, + baz : 4, + quux : { + quibble : 5, + o_O : true + } + }); + t.same(argv.beep, { boop : true }); + t.end(); +}); + +test('boolean and alias with chainable api', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = parse(aliased, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var propertyArgv = parse(regular, { + boolean: 'herp', + alias: { h: 'herp' } + }); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias with options hash', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + alias: { 'h': 'herp' }, + boolean: 'herp' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ 'derp' ] + }; + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias using explicit true', function (t) { + var aliased = [ '-h', 'true' ]; + var regular = [ '--herp', 'true' ]; + var opts = { + alias: { h: 'herp' }, + boolean: 'h' + }; + var aliasedArgv = parse(aliased, opts); + var propertyArgv = parse(regular, opts); + var expected = { + herp: true, + h: true, + '_': [ ] + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +// regression, see https://github.com/substack/node-optimist/issues/71 +test('boolean and --x=true', function(t) { + var parsed = parse(['--boool', '--other=true'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'true'); + + parsed = parse(['--boool', '--other=false'], { + boolean: 'boool' + }); + + t.same(parsed.boool, true); + t.same(parsed.other, 'false'); + t.end(); +}); diff --git a/WebServer/node_modules/minimist/test/parse_modified.js b/WebServer/node_modules/minimist/test/parse_modified.js new file mode 100644 index 000000000..21851b036 --- /dev/null +++ b/WebServer/node_modules/minimist/test/parse_modified.js @@ -0,0 +1,9 @@ +var parse = require('../'); +var test = require('tape'); + +test('parse with modifier functions' , function (t) { + t.plan(1); + + var argv = parse([ '-b', '123' ], { boolean: 'b' }); + t.deepEqual(argv, { b: true, _: ['123'] }); +}); diff --git a/WebServer/node_modules/minimist/test/short.js b/WebServer/node_modules/minimist/test/short.js new file mode 100644 index 000000000..d513a1c25 --- /dev/null +++ b/WebServer/node_modules/minimist/test/short.js @@ -0,0 +1,67 @@ +var parse = require('../'); +var test = require('tape'); + +test('numeric short args', function (t) { + t.plan(2); + t.deepEqual(parse([ '-n123' ]), { n: 123, _: [] }); + t.deepEqual( + parse([ '-123', '456' ]), + { 1: true, 2: true, 3: 456, _: [] } + ); +}); + +test('short', function (t) { + t.deepEqual( + parse([ '-b' ]), + { b : true, _ : [] }, + 'short boolean' + ); + t.deepEqual( + parse([ 'foo', 'bar', 'baz' ]), + { _ : [ 'foo', 'bar', 'baz' ] }, + 'bare' + ); + t.deepEqual( + parse([ '-cats' ]), + { c : true, a : true, t : true, s : true, _ : [] }, + 'group' + ); + t.deepEqual( + parse([ '-cats', 'meow' ]), + { c : true, a : true, t : true, s : 'meow', _ : [] }, + 'short group next' + ); + t.deepEqual( + parse([ '-h', 'localhost' ]), + { h : 'localhost', _ : [] }, + 'short capture' + ); + t.deepEqual( + parse([ '-h', 'localhost', '-p', '555' ]), + { h : 'localhost', p : 555, _ : [] }, + 'short captures' + ); + t.end(); +}); + +test('mixed short bool and capture', function (t) { + t.same( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); + +test('short and long', function (t) { + t.deepEqual( + parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ] + } + ); + t.end(); +}); diff --git a/WebServer/node_modules/minimist/test/whitespace.js b/WebServer/node_modules/minimist/test/whitespace.js new file mode 100644 index 000000000..8a52a58ce --- /dev/null +++ b/WebServer/node_modules/minimist/test/whitespace.js @@ -0,0 +1,8 @@ +var parse = require('../'); +var test = require('tape'); + +test('whitespace should be whitespace' , function (t) { + t.plan(1); + var x = parse([ '-x', '\t' ]).x; + t.equal(x, '\t'); +}); diff --git a/WebServer/node_modules/mkdirp/bin/cmd.js b/WebServer/node_modules/mkdirp/bin/cmd.js new file mode 100644 index 000000000..d95de15ae --- /dev/null +++ b/WebServer/node_modules/mkdirp/bin/cmd.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +var mkdirp = require('../'); +var minimist = require('minimist'); +var fs = require('fs'); + +var argv = minimist(process.argv.slice(2), { + alias: { m: 'mode', h: 'help' }, + string: [ 'mode' ] +}); +if (argv.help) { + fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout); + return; +} + +var paths = argv._.slice(); +var mode = argv.mode ? parseInt(argv.mode, 8) : undefined; + +(function next () { + if (paths.length === 0) return; + var p = paths.shift(); + + if (mode === undefined) mkdirp(p, cb) + else mkdirp(p, mode, cb) + + function cb (err) { + if (err) { + console.error(err.message); + process.exit(1); + } + else next(); + } +})(); diff --git a/WebServer/node_modules/mkdirp/examples/pow.js b/WebServer/node_modules/mkdirp/examples/pow.js new file mode 100644 index 000000000..e6924212e --- /dev/null +++ b/WebServer/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/WebServer/node_modules/mkdirp/index.js b/WebServer/node_modules/mkdirp/index.js new file mode 100644 index 000000000..6ce241b58 --- /dev/null +++ b/WebServer/node_modules/mkdirp/index.js @@ -0,0 +1,98 @@ +var path = require('path'); +var fs = require('fs'); +var _0777 = parseInt('0777', 8); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + p = path.resolve(p); + + try { + xfs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; diff --git a/WebServer/node_modules/mkdirp/package.json b/WebServer/node_modules/mkdirp/package.json new file mode 100644 index 000000000..d0eaa06d2 --- /dev/null +++ b/WebServer/node_modules/mkdirp/package.json @@ -0,0 +1,65 @@ +{ + "_args": [ + [ + "mkdirp@0.5.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "mkdirp@0.5.1", + "_id": "mkdirp@0.5.1", + "_inBundle": false, + "_integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "_location": "/mkdirp", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "mkdirp@0.5.1", + "name": "mkdirp", + "escapedName": "mkdirp", + "rawSpec": "0.5.1", + "saveSpec": null, + "fetchSpec": "0.5.1" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "_spec": "0.5.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "bugs": { + "url": "https://github.com/substack/node-mkdirp/issues" + }, + "dependencies": { + "minimist": "0.0.8" + }, + "description": "Recursively mkdir, like `mkdir -p`", + "devDependencies": { + "mock-fs": "2 >=2.7.0", + "tap": "1" + }, + "homepage": "https://github.com/substack/node-mkdirp#readme", + "keywords": [ + "mkdir", + "directory" + ], + "license": "MIT", + "main": "index.js", + "name": "mkdirp", + "repository": { + "type": "git", + "url": "git+https://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "version": "0.5.1" +} diff --git a/WebServer/node_modules/mkdirp/test/chmod.js b/WebServer/node_modules/mkdirp/test/chmod.js new file mode 100644 index 000000000..6a404b932 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); +var _0744 = parseInt('0744', 8); + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = _0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & _0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = _0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/clobber.js b/WebServer/node_modules/mkdirp/test/clobber.js new file mode 100644 index 000000000..2433b9ad5 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; +var _0755 = parseInt('0755', 8); + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, _0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/mkdirp.js b/WebServer/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 000000000..eaa8921c7 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('woo', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, _0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/opts_fs.js b/WebServer/node_modules/mkdirp/test/opts_fs.js new file mode 100644 index 000000000..97186b62e --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/opts_fs.js @@ -0,0 +1,29 @@ +var mkdirp = require('../'); +var path = require('path'); +var test = require('tap').test; +var mockfs = require('mock-fs'); +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('opts.fs', function (t) { + t.plan(5); + + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/beep/boop/' + [x,y,z].join('/'); + var xfs = mockfs.fs(); + + mkdirp(file, { fs: xfs, mode: _0755 }, function (err) { + t.ifError(err); + xfs.exists(file, function (ex) { + t.ok(ex, 'created file'); + xfs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/opts_fs_sync.js b/WebServer/node_modules/mkdirp/test/opts_fs_sync.js new file mode 100644 index 000000000..6c370aa6e --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/opts_fs_sync.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var test = require('tap').test; +var mockfs = require('mock-fs'); +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('opts.fs sync', function (t) { + t.plan(4); + + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/beep/boop/' + [x,y,z].join('/'); + var xfs = mockfs.fs(); + + mkdirp.sync(file, { fs: xfs, mode: _0755 }); + xfs.exists(file, function (ex) { + t.ok(ex, 'created file'); + xfs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/perm.js b/WebServer/node_modules/mkdirp/test/perm.js new file mode 100644 index 000000000..fbce44b82 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('async perm', function (t) { + t.plan(5); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, _0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', _0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/WebServer/node_modules/mkdirp/test/perm_sync.js b/WebServer/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 000000000..398229fe5 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,36 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('sync perm', function (t) { + t.plan(4); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, _0755); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); + +test('sync root perm', function (t) { + t.plan(3); + + var file = '/tmp'; + mkdirp.sync(file, _0755); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/race.js b/WebServer/node_modules/mkdirp/test/race.js new file mode 100644 index 000000000..b0b9e183c --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/race.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('race', function (t) { + t.plan(10); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file); + + mk(file); + + function mk (file, cb) { + mkdirp(file, _0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }) + }); + } +}); diff --git a/WebServer/node_modules/mkdirp/test/rel.js b/WebServer/node_modules/mkdirp/test/rel.js new file mode 100644 index 000000000..4ddb34276 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('rel', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, _0755, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + process.chdir(cwd); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }) + }) + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/return.js b/WebServer/node_modules/mkdirp/test/return.js new file mode 100644 index 000000000..bce68e561 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/return.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, '/tmp/' + x); + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, null); + }); + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/return_sync.js b/WebServer/node_modules/mkdirp/test/return_sync.js new file mode 100644 index 000000000..7c222d355 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/return_sync.js @@ -0,0 +1,24 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + // Note that this will throw on failure, which will fail the test. + var made = mkdirp.sync(file); + t.equal(made, '/tmp/' + x); + + // making the same file again should have no effect. + made = mkdirp.sync(file); + t.equal(made, null); +}); diff --git a/WebServer/node_modules/mkdirp/test/root.js b/WebServer/node_modules/mkdirp/test/root.js new file mode 100644 index 000000000..9e7d079d9 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/root.js @@ -0,0 +1,19 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; +var _0755 = parseInt('0755', 8); + +test('root', function (t) { + // '/' on unix, 'c:/' on windows. + var file = path.resolve('/'); + + mkdirp(file, _0755, function (err) { + if (err) throw err + fs.stat(file, function (er, stat) { + if (er) throw er + t.ok(stat.isDirectory(), 'target is a directory'); + t.end(); + }) + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/sync.js b/WebServer/node_modules/mkdirp/test/sync.js new file mode 100644 index 000000000..8c8dc938c --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('sync', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file, _0755); + } catch (err) { + t.fail(err); + return t.end(); + } + + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0755); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/umask.js b/WebServer/node_modules/mkdirp/test/umask.js new file mode 100644 index 000000000..2033c63a4 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('implicit mode from umask', function (t) { + t.plan(5); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + t.ifError(err); + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, _0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }) + }); +}); diff --git a/WebServer/node_modules/mkdirp/test/umask_sync.js b/WebServer/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 000000000..11a761474 --- /dev/null +++ b/WebServer/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var exists = fs.exists || path.exists; +var test = require('tap').test; +var _0777 = parseInt('0777', 8); +var _0755 = parseInt('0755', 8); + +test('umask sync modes', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file); + } catch (err) { + t.fail(err); + return t.end(); + } + + exists(file, function (ex) { + t.ok(ex, 'file created'); + fs.stat(file, function (err, stat) { + t.ifError(err); + t.equal(stat.mode & _0777, (_0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + }); + }); +}); diff --git a/WebServer/node_modules/mocha/assets/growl/error.png b/WebServer/node_modules/mocha/assets/growl/error.png new file mode 100644 index 000000000..a07a1ba5e Binary files /dev/null and b/WebServer/node_modules/mocha/assets/growl/error.png differ diff --git a/WebServer/node_modules/mocha/assets/growl/ok.png b/WebServer/node_modules/mocha/assets/growl/ok.png new file mode 100644 index 000000000..b3623a599 Binary files /dev/null and b/WebServer/node_modules/mocha/assets/growl/ok.png differ diff --git a/WebServer/node_modules/mocha/bin/options.js b/WebServer/node_modules/mocha/bin/options.js new file mode 100644 index 000000000..102fd5a9c --- /dev/null +++ b/WebServer/node_modules/mocha/bin/options.js @@ -0,0 +1,10 @@ +'use strict'; + +/* + * This module is deprecated and will be removed in a future version of Mocha. + * @deprecated Deprecated in v6.0.0; source moved into {@link module:lib/cli/options lib/cli/options module}. + * @module + * @exports module:lib/cli/options + */ + +module.exports = require('../lib/cli/options'); diff --git a/WebServer/node_modules/mocha/browser-entry.js b/WebServer/node_modules/mocha/browser-entry.js new file mode 100644 index 000000000..fd8911775 --- /dev/null +++ b/WebServer/node_modules/mocha/browser-entry.js @@ -0,0 +1,191 @@ +'use strict'; + +/* eslint no-unused-vars: off */ +/* eslint-env commonjs */ + +/** + * Shim process.stdout. + */ + +process.stdout = require('browser-stdout')({label: false}); + +var Mocha = require('./lib/mocha'); + +/** + * Create a Mocha instance. + * + * @return {undefined} + */ + +var mocha = new Mocha({reporter: 'html'}); + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date; +var setTimeout = global.setTimeout; +var setInterval = global.setInterval; +var clearTimeout = global.clearTimeout; +var clearInterval = global.clearInterval; + +var uncaughtExceptionHandlers = []; + +var originalOnerrorHandler = global.onerror; + +/** + * Remove uncaughtException listener. + * Revert to original onerror handler if previously defined. + */ + +process.removeListener = function(e, fn) { + if (e === 'uncaughtException') { + if (originalOnerrorHandler) { + global.onerror = originalOnerrorHandler; + } else { + global.onerror = function() {}; + } + var i = uncaughtExceptionHandlers.indexOf(fn); + if (i !== -1) { + uncaughtExceptionHandlers.splice(i, 1); + } + } +}; + +/** + * Implements uncaughtException listener. + */ + +process.on = function(e, fn) { + if (e === 'uncaughtException') { + global.onerror = function(err, url, line) { + fn(new Error(err + ' (' + url + ':' + line + ')')); + return !mocha.allowUncaught; + }; + uncaughtExceptionHandlers.push(fn); + } +}; + +// The BDD UI is registered by default, but no UI will be functional in the +// browser without an explicit call to the overridden `mocha.ui` (see below). +// Ensure that this default UI does not expose its methods to the global scope. +mocha.suite.removeAllListeners('pre-require'); + +var immediateQueue = []; +var immediateTimeout; + +function timeslice() { + var immediateStart = new Date().getTime(); + while (immediateQueue.length && new Date().getTime() - immediateStart < 100) { + immediateQueue.shift()(); + } + if (immediateQueue.length) { + immediateTimeout = setTimeout(timeslice, 0); + } else { + immediateTimeout = null; + } +} + +/** + * High-performance override of Runner.immediately. + */ + +Mocha.Runner.immediately = function(callback) { + immediateQueue.push(callback); + if (!immediateTimeout) { + immediateTimeout = setTimeout(timeslice, 0); + } +}; + +/** + * Function to allow assertion libraries to throw errors directly into mocha. + * This is useful when running tests in a browser because window.onerror will + * only receive the 'message' attribute of the Error. + */ +mocha.throwError = function(err) { + uncaughtExceptionHandlers.forEach(function(fn) { + fn(err); + }); + throw err; +}; + +/** + * Override ui to ensure that the ui functions are initialized. + * Normally this would happen in Mocha.prototype.loadFiles. + */ + +mocha.ui = function(ui) { + Mocha.prototype.ui.call(this, ui); + this.suite.emit('pre-require', global, null, this); + return this; +}; + +/** + * Setup mocha with the given setting options. + */ + +mocha.setup = function(opts) { + if (typeof opts === 'string') { + opts = {ui: opts}; + } + for (var opt in opts) { + if (opts.hasOwnProperty(opt)) { + this[opt](opts[opt]); + } + } + return this; +}; + +/** + * Run mocha, returning the Runner. + */ + +mocha.run = function(fn) { + var options = mocha.options; + mocha.globals('location'); + + var query = Mocha.utils.parseQuery(global.location.search || ''); + if (query.grep) { + mocha.grep(query.grep); + } + if (query.fgrep) { + mocha.fgrep(query.fgrep); + } + if (query.invert) { + mocha.invert(); + } + + return Mocha.prototype.run.call(mocha, function(err) { + // The DOM Document is not available in Web Workers. + var document = global.document; + if ( + document && + document.getElementById('mocha') && + options.noHighlighting !== true + ) { + Mocha.utils.highlightTags('code'); + } + if (fn) { + fn(err); + } + }); +}; + +/** + * Expose the process shim. + * https://github.com/mochajs/mocha/pull/916 + */ + +Mocha.process = process; + +/** + * Expose mocha. + */ + +global.Mocha = Mocha; +global.mocha = mocha; + +// this allows test/acceptance/required-tokens.js to pass; thus, +// you can now do `const describe = require('mocha').describe` in a +// browser context (assuming browserification). should fix #880 +module.exports = global; diff --git a/WebServer/node_modules/mocha/index.js b/WebServer/node_modules/mocha/index.js new file mode 100644 index 000000000..d2b7d1960 --- /dev/null +++ b/WebServer/node_modules/mocha/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./lib/mocha'); diff --git a/WebServer/node_modules/mocha/lib/browser/growl.js b/WebServer/node_modules/mocha/lib/browser/growl.js new file mode 100644 index 000000000..016798501 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/browser/growl.js @@ -0,0 +1,168 @@ +'use strict'; + +/** + * Web Notifications module. + * @module Growl + */ + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ +var Date = global.Date; +var setTimeout = global.setTimeout; +var EVENT_RUN_END = require('../runner').constants.EVENT_RUN_END; + +/** + * Checks if browser notification support exists. + * + * @public + * @see {@link https://caniuse.com/#feat=notifications|Browser support (notifications)} + * @see {@link https://caniuse.com/#feat=promises|Browser support (promises)} + * @see {@link Mocha#growl} + * @see {@link Mocha#isGrowlCapable} + * @return {boolean} whether browser notification support exists + */ +exports.isCapable = function() { + var hasNotificationSupport = 'Notification' in window; + var hasPromiseSupport = typeof Promise === 'function'; + return process.browser && hasNotificationSupport && hasPromiseSupport; +}; + +/** + * Implements browser notifications as a pseudo-reporter. + * + * @public + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/notification|Notification API} + * @see {@link https://developers.google.com/web/fundamentals/push-notifications/display-a-notification|Displaying a Notification} + * @see {@link Growl#isPermitted} + * @see {@link Mocha#_growl} + * @param {Runner} runner - Runner instance. + */ +exports.notify = function(runner) { + var promise = isPermitted(); + + /** + * Attempt notification. + */ + var sendNotification = function() { + // If user hasn't responded yet... "No notification for you!" (Seinfeld) + Promise.race([promise, Promise.resolve(undefined)]) + .then(canNotify) + .then(function() { + display(runner); + }) + .catch(notPermitted); + }; + + runner.once(EVENT_RUN_END, sendNotification); +}; + +/** + * Checks if browser notification is permitted by user. + * + * @private + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission|Notification.permission} + * @see {@link Mocha#growl} + * @see {@link Mocha#isGrowlPermitted} + * @returns {Promise} promise determining if browser notification + * permissible when fulfilled. + */ +function isPermitted() { + var permitted = { + granted: function allow() { + return Promise.resolve(true); + }, + denied: function deny() { + return Promise.resolve(false); + }, + default: function ask() { + return Notification.requestPermission().then(function(permission) { + return permission === 'granted'; + }); + } + }; + + return permitted[Notification.permission](); +} + +/** + * @summary + * Determines if notification should proceed. + * + * @description + * Notification shall not proceed unless `value` is true. + * + * `value` will equal one of: + *
    + *
  • true (from `isPermitted`)
  • + *
  • false (from `isPermitted`)
  • + *
  • undefined (from `Promise.race`)
  • + *
+ * + * @private + * @param {boolean|undefined} value - Determines if notification permissible. + * @returns {Promise} Notification can proceed + */ +function canNotify(value) { + if (!value) { + var why = value === false ? 'blocked' : 'unacknowledged'; + var reason = 'not permitted by user (' + why + ')'; + return Promise.reject(new Error(reason)); + } + return Promise.resolve(); +} + +/** + * Displays the notification. + * + * @private + * @param {Runner} runner - Runner instance. + */ +function display(runner) { + var stats = runner.stats; + var symbol = { + cross: '\u274C', + tick: '\u2705' + }; + var logo = require('../../package').notifyLogo; + var _message; + var message; + var title; + + if (stats.failures) { + _message = stats.failures + ' of ' + stats.tests + ' tests failed'; + message = symbol.cross + ' ' + _message; + title = 'Failed'; + } else { + _message = stats.passes + ' tests passed in ' + stats.duration + 'ms'; + message = symbol.tick + ' ' + _message; + title = 'Passed'; + } + + // Send notification + var options = { + badge: logo, + body: message, + dir: 'ltr', + icon: logo, + lang: 'en-US', + name: 'mocha', + requireInteraction: false, + timestamp: Date.now() + }; + var notification = new Notification(title, options); + + // Autoclose after brief delay (makes various browsers act same) + var FORCE_DURATION = 4000; + setTimeout(notification.close.bind(notification), FORCE_DURATION); +} + +/** + * As notifications are tangential to our purpose, just log the error. + * + * @private + * @param {Error} err - Why notification didn't happen. + */ +function notPermitted(err) { + console.error('notification error:', err.message); +} diff --git a/WebServer/node_modules/mocha/lib/browser/progress.js b/WebServer/node_modules/mocha/lib/browser/progress.js new file mode 100644 index 000000000..553fc6eb5 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/browser/progress.js @@ -0,0 +1,119 @@ +'use strict'; + +/** + * Expose `Progress`. + */ + +module.exports = Progress; + +/** + * Initialize a new `Progress` indicator. + */ +function Progress() { + this.percent = 0; + this.size(0); + this.fontSize(11); + this.font('helvetica, arial, sans-serif'); +} + +/** + * Set progress size to `size`. + * + * @public + * @param {number} size + * @return {Progress} Progress instance. + */ +Progress.prototype.size = function(size) { + this._size = size; + return this; +}; + +/** + * Set text to `text`. + * + * @public + * @param {string} text + * @return {Progress} Progress instance. + */ +Progress.prototype.text = function(text) { + this._text = text; + return this; +}; + +/** + * Set font size to `size`. + * + * @public + * @param {number} size + * @return {Progress} Progress instance. + */ +Progress.prototype.fontSize = function(size) { + this._fontSize = size; + return this; +}; + +/** + * Set font to `family`. + * + * @param {string} family + * @return {Progress} Progress instance. + */ +Progress.prototype.font = function(family) { + this._font = family; + return this; +}; + +/** + * Update percentage to `n`. + * + * @param {number} n + * @return {Progress} Progress instance. + */ +Progress.prototype.update = function(n) { + this.percent = n; + return this; +}; + +/** + * Draw on `ctx`. + * + * @param {CanvasRenderingContext2d} ctx + * @return {Progress} Progress instance. + */ +Progress.prototype.draw = function(ctx) { + try { + var percent = Math.min(this.percent, 100); + var size = this._size; + var half = size / 2; + var x = half; + var y = half; + var rad = half - 1; + var fontSize = this._fontSize; + + ctx.font = fontSize + 'px ' + this._font; + + var angle = Math.PI * 2 * (percent / 100); + ctx.clearRect(0, 0, size, size); + + // outer circle + ctx.strokeStyle = '#9f9f9f'; + ctx.beginPath(); + ctx.arc(x, y, rad, 0, angle, false); + ctx.stroke(); + + // inner circle + ctx.strokeStyle = '#eee'; + ctx.beginPath(); + ctx.arc(x, y, rad - 1, 0, angle, true); + ctx.stroke(); + + // text + var text = this._text || (percent | 0) + '%'; + var w = ctx.measureText(text).width; + + ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1); + } catch (ignore) { + // don't fail if we can't render progress + } + return this; +}; diff --git a/WebServer/node_modules/mocha/lib/browser/template.html b/WebServer/node_modules/mocha/lib/browser/template.html new file mode 100644 index 000000000..af4d3e4f4 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/browser/template.html @@ -0,0 +1,18 @@ + + + + + Mocha + + + + +
+ + + + + + diff --git a/WebServer/node_modules/mocha/lib/browser/tty.js b/WebServer/node_modules/mocha/lib/browser/tty.js new file mode 100644 index 000000000..e90336502 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/browser/tty.js @@ -0,0 +1,13 @@ +'use strict'; + +exports.isatty = function isatty() { + return true; +}; + +exports.getWindowSize = function getWindowSize() { + if ('innerHeight' in global) { + return [global.innerHeight, global.innerWidth]; + } + // In a Web Worker, the DOM Window is not available. + return [640, 480]; +}; diff --git a/WebServer/node_modules/mocha/lib/cli/cli.js b/WebServer/node_modules/mocha/lib/cli/cli.js new file mode 100644 index 000000000..b51c2a197 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/cli.js @@ -0,0 +1,69 @@ +'use strict'; + +/** + * This is where we finally parse and handle arguments passed to the `mocha` executable. + * Option parsing is handled by {@link https://npm.im/yargs yargs}. + * If executed via `node`, this module will run {@linkcode module:lib/cli/cli.main main()}. + * + * @private + * @module + */ + +const debug = require('debug')('mocha:cli:cli'); +const symbols = require('log-symbols'); +const yargs = require('yargs/yargs'); +const path = require('path'); +const {loadOptions, YARGS_PARSER_CONFIG} = require('./options'); +const commands = require('./commands'); +const ansi = require('ansi-colors'); +const {repository, homepage, version, gitter} = require('../../package.json'); + +/** + * - Accepts an `Array` of arguments + * - Modifies {@link https://nodejs.org/api/modules.html#modules_module_paths Node.js' search path} for easy loading of consumer modules + * - Sets {@linkcode https://nodejs.org/api/errors.html#errors_error_stacktracelimit Error.stackTraceLimit} to `Infinity` + * @summary Mocha's main entry point from the command-line. + * @param {string[]} argv - Array of arguments to parse, or by default the lovely `process.argv.slice(2)` + */ +exports.main = (argv = process.argv.slice(2)) => { + debug('entered main with raw args', argv); + // ensure we can require() from current working directory + module.paths.push(process.cwd(), path.resolve('node_modules')); + + Error.stackTraceLimit = Infinity; // configurable via --stack-trace-limit? + + yargs() + .scriptName('mocha') + .command(commands.run) + .command(commands.init) + .updateStrings({ + 'Positionals:': 'Positional Arguments', + 'Options:': 'Other Options', + 'Commands:': 'Commands' + }) + .fail((msg, err, yargs) => { + debug(err); + yargs.showHelp(); + console.error(`\n${symbols.error} ${ansi.red('ERROR:')} ${msg}`); + process.exit(1); + }) + .help('help', 'Show usage information & exit') + .alias('help', 'h') + .version('version', 'Show version number & exit', version) + .alias('version', 'V') + .wrap(process.stdout.columns ? Math.min(process.stdout.columns, 80) : 80) + .epilog( + `Mocha Resources + Chat: ${ansi.magenta(gitter)} + GitHub: ${ansi.blue(repository.url)} + Docs: ${ansi.yellow(homepage)} + ` + ) + .parserConfiguration(YARGS_PARSER_CONFIG) + .parse(argv, loadOptions(argv)); +}; + +// allow direct execution +if (require.main === module) { + exports.main(); +} diff --git a/WebServer/node_modules/mocha/lib/cli/commands.js b/WebServer/node_modules/mocha/lib/cli/commands.js new file mode 100644 index 000000000..eb10c6833 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/commands.js @@ -0,0 +1,13 @@ +'use strict'; + +/** + * Exports Yargs commands + * @see https://git.io/fpJ0G + * @private + * @module + */ + +exports.init = require('./init'); + +// default command +exports.run = require('./run'); diff --git a/WebServer/node_modules/mocha/lib/cli/config.js b/WebServer/node_modules/mocha/lib/cli/config.js new file mode 100644 index 000000000..6fa4e2dbc --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/config.js @@ -0,0 +1,101 @@ +'use strict'; + +/** + * Responsible for loading / finding Mocha's "rc" files. + * This doesn't have anything to do with `mocha.opts`. + * + * @private + * @module + */ + +const fs = require('fs'); +const path = require('path'); +const debug = require('debug')('mocha:cli:config'); +const findUp = require('find-up'); + +/** + * These are the valid config files, in order of precedence; + * e.g., if `.mocharc.js` is present, then `.mocharc.yaml` and the rest + * will be ignored. + * The user should still be able to explicitly specify a file. + * @private + */ +exports.CONFIG_FILES = [ + '.mocharc.js', + '.mocharc.yaml', + '.mocharc.yml', + '.mocharc.jsonc', + '.mocharc.json' +]; + +const isModuleNotFoundError = err => + err.code !== 'MODULE_NOT_FOUND' || + err.message.indexOf('Cannot find module') !== -1; + +/** + * Parsers for various config filetypes. Each accepts a filepath and + * returns an object (but could throw) + */ +const parsers = (exports.parsers = { + yaml: filepath => + require('js-yaml').safeLoad(fs.readFileSync(filepath, 'utf8')), + js: filepath => { + const cwdFilepath = path.resolve(filepath); + try { + debug(`parsers: load using cwd-relative path: "${cwdFilepath}"`); + return require(cwdFilepath); + } catch (err) { + if (isModuleNotFoundError(err)) { + debug(`parsers: retry load as module-relative path: "${filepath}"`); + return require(filepath); + } else { + throw err; // rethrow + } + } + }, + json: filepath => + JSON.parse( + require('strip-json-comments')(fs.readFileSync(filepath, 'utf8')) + ) +}); + +/** + * Loads and parses, based on file extension, a config file. + * "JSON" files may have comments. + * + * @private + * @param {string} filepath - Config file path to load + * @returns {Object} Parsed config object + */ +exports.loadConfig = filepath => { + let config = {}; + debug(`loadConfig: "${filepath}"`); + + const ext = path.extname(filepath); + try { + if (ext === '.yml' || ext === '.yaml') { + config = parsers.yaml(filepath); + } else if (ext === '.js') { + config = parsers.js(filepath); + } else { + config = parsers.json(filepath); + } + } catch (err) { + throw new Error(`failed to parse config "${filepath}": ${err}`); + } + return config; +}; + +/** + * Find ("find up") config file starting at `cwd` + * + * @param {string} [cwd] - Current working directory + * @returns {string|null} Filepath to config, if found + */ +exports.findConfig = (cwd = process.cwd()) => { + const filepath = findUp.sync(exports.CONFIG_FILES, {cwd}); + if (filepath) { + debug(`findConfig: found "${filepath}"`); + } + return filepath; +}; diff --git a/WebServer/node_modules/mocha/lib/cli/index.js b/WebServer/node_modules/mocha/lib/cli/index.js new file mode 100644 index 000000000..d792f0c9d --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/index.js @@ -0,0 +1,9 @@ +'use strict'; + +/** + * Just exports {@link module:lib/cli/cli} for convenience. + * @private + * @module lib/cli + * @exports module:lib/cli/cli + */ +module.exports = require('./cli'); diff --git a/WebServer/node_modules/mocha/lib/cli/init.js b/WebServer/node_modules/mocha/lib/cli/init.js new file mode 100644 index 000000000..ca847c6f1 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/init.js @@ -0,0 +1,37 @@ +'use strict'; + +/** + * Command module for "init" command + * + * @private + * @module + */ + +const fs = require('fs'); +const path = require('path'); +const mkdirp = require('mkdirp'); + +exports.command = 'init '; + +exports.description = 'create a client-side Mocha setup at '; + +exports.builder = yargs => + yargs.positional('path', { + type: 'string', + normalize: true + }); + +exports.handler = argv => { + const destdir = argv.path; + const srcdir = path.join(__dirname, '..', '..'); + mkdirp.sync(destdir); + const css = fs.readFileSync(path.join(srcdir, 'mocha.css')); + const js = fs.readFileSync(path.join(srcdir, 'mocha.js')); + const tmpl = fs.readFileSync( + path.join(srcdir, 'lib', 'browser', 'template.html') + ); + fs.writeFileSync(path.join(destdir, 'mocha.css'), css); + fs.writeFileSync(path.join(destdir, 'mocha.js'), js); + fs.writeFileSync(path.join(destdir, 'tests.spec.js'), ''); + fs.writeFileSync(path.join(destdir, 'index.html'), tmpl); +}; diff --git a/WebServer/node_modules/mocha/lib/cli/node-flags.js b/WebServer/node_modules/mocha/lib/cli/node-flags.js new file mode 100644 index 000000000..feffaa93e --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/node-flags.js @@ -0,0 +1,86 @@ +'use strict'; + +/** + * Some settings and code related to Mocha's handling of Node.js/V8 flags. + * @private + * @module + */ + +const nodeFlags = require('node-environment-flags'); +const unparse = require('yargs-unparser'); + +/** + * These flags are considered "debug" flags. + * @see {@link impliesNoTimeouts} + * @private + */ +const debugFlags = new Set(['debug', 'debug-brk', 'inspect', 'inspect-brk']); + +/** + * Mocha has historical support for various `node` and V8 flags which might not + * appear in `process.allowedNodeEnvironmentFlags`. + * These include: + * - `--preserve-symlinks` + * - `--harmony-*` + * - `--gc-global` + * - `--trace-*` + * - `--es-staging` + * - `--use-strict` + * - `--v8-*` (but *not* `--v8-options`) + * @summary Whether or not to pass a flag along to the `node` executable. + * @param {string} flag - Flag to test + * @param {boolean} [bareword=true] - If `false`, we expect `flag` to have one or two leading dashes. + * @returns {boolean} If the flag is considered a "Node" flag. + * @private + */ +exports.isNodeFlag = (flag, bareword = true) => { + if (!bareword) { + // check if the flag begins with dashes; if not, not a node flag. + if (!/^--?/.test(flag)) { + return false; + } + // strip the leading dashes to match against subsequent checks + flag = flag.replace(/^--?/, ''); + } + return ( + // treat --require/-r as Mocha flag even though it's also a node flag + !(flag === 'require' || flag === 'r') && + // check actual node flags from `process.allowedNodeEnvironmentFlags`, + // then historical support for various V8 and non-`NODE_OPTIONS` flags + // and also any V8 flags with `--v8-` prefix + (nodeFlags.has(flag) || + debugFlags.has(flag) || + /(?:preserve-symlinks(?:-main)?|harmony(?:[_-]|$)|(?:trace[_-].+$)|gc(?:[_-]global)?$|es[_-]staging$|use[_-]strict$|v8[_-](?!options).+?$)/.test( + flag + )) + ); +}; + +/** + * Returns `true` if the flag is a "debug-like" flag. These require timeouts + * to be suppressed, or pausing the debugger on breakpoints will cause test failures. + * @param {string} flag - Flag to test + * @returns {boolean} + * @private + */ +exports.impliesNoTimeouts = flag => debugFlags.has(flag); + +/** + * All non-strictly-boolean arguments to node--those with values--must specify those values using `=`, e.g., `--inspect=0.0.0.0`. + * Unparse these arguments using `yargs-unparser` (which would result in `--inspect 0.0.0.0`), then supply `=` where we have values. + * There's probably an easier or more robust way to do this; fixes welcome + * @param {Object} opts - Arguments object + * @returns {string[]} Unparsed arguments using `=` to specify values + * @private + */ +exports.unparseNodeFlags = opts => { + var args = unparse(opts); + return args.length + ? args + .join(' ') + .split(/\b/) + .map(arg => (arg === ' ' ? '=' : arg)) + .join('') + .split(' ') + : []; +}; diff --git a/WebServer/node_modules/mocha/lib/cli/one-and-dones.js b/WebServer/node_modules/mocha/lib/cli/one-and-dones.js new file mode 100644 index 000000000..c981b4bbb --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/one-and-dones.js @@ -0,0 +1,70 @@ +'use strict'; + +/** + * Contains "command" code for "one-and-dones"--options passed + * to Mocha which cause it to just dump some info and exit. + * See {@link module:lib/cli/one-and-dones.ONE_AND_DONE_ARGS ONE_AND_DONE_ARGS} for more info. + * @module + * @private + */ + +const align = require('wide-align'); +const Mocha = require('../mocha'); + +/** + * Dumps a sorted list of the enumerable, lower-case keys of some object + * to `STDOUT`. + * @param {Object} obj - Object, ostensibly having some enumerable keys + * @ignore + * @private + */ +const showKeys = obj => { + console.log(); + const keys = Object.keys(obj); + const maxKeyLength = keys.reduce((max, key) => Math.max(max, key.length), 0); + keys + .filter( + key => /^[a-z]/.test(key) && !obj[key].browserOnly && !obj[key].abstract + ) + .sort() + .forEach(key => { + const description = obj[key].description; + console.log( + ` ${align.left(key, maxKeyLength + 1)}${ + description ? `- ${description}` : '' + }` + ); + }); + console.log(); +}; + +/** + * Handlers for one-and-done options + * @namespace + * @private + */ +exports.ONE_AND_DONES = { + /** + * Dump list of built-in interfaces + * @private + */ + interfaces: () => { + showKeys(Mocha.interfaces); + }, + /** + * Dump list of built-in reporters + * @private + */ + reporters: () => { + showKeys(Mocha.reporters); + } +}; + +/** + * A Set of all one-and-done options + * @type Set + * @private + */ +exports.ONE_AND_DONE_ARGS = new Set( + ['help', 'h', 'version', 'V'].concat(Object.keys(exports.ONE_AND_DONES)) +); diff --git a/WebServer/node_modules/mocha/lib/cli/options.js b/WebServer/node_modules/mocha/lib/cli/options.js new file mode 100644 index 000000000..a033c39ac --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/options.js @@ -0,0 +1,347 @@ +'use strict'; + +/** + * Main entry point for handling filesystem-based configuration, + * whether that's `mocha.opts` or a config file or `package.json` or whatever. + * @module + */ + +const fs = require('fs'); +const yargsParser = require('yargs-parser'); +const {types, aliases} = require('./run-option-metadata'); +const {ONE_AND_DONE_ARGS} = require('./one-and-dones'); +const mocharc = require('../mocharc.json'); +const {list} = require('./run-helpers'); +const {loadConfig, findConfig} = require('./config'); +const findUp = require('find-up'); +const {deprecate} = require('../utils'); +const debug = require('debug')('mocha:cli:options'); +const {createMissingArgumentError} = require('../errors'); +const {isNodeFlag} = require('./node-flags'); + +/** + * The `yargs-parser` namespace + * @external yargsParser + * @see {@link https://npm.im/yargs-parser} + */ + +/** + * An object returned by a configured `yargs-parser` representing arguments + * @memberof external:yargsParser + * @interface Arguments + */ + +/** + * Base yargs parser configuration + * @private + */ +const YARGS_PARSER_CONFIG = { + 'combine-arrays': true, + 'short-option-groups': false, + 'dot-notation': false +}; + +/** + * This is the config pulled from the `yargs` property of Mocha's + * `package.json`, but it also disables camel case expansion as to + * avoid outputting non-canonical keynames, as we need to do some + * lookups. + * @private + * @ignore + */ +const configuration = Object.assign({}, YARGS_PARSER_CONFIG, { + 'camel-case-expansion': false +}); + +/** + * This is a really fancy way to ensure unique values for `array`-type + * options. + * This is passed as the `coerce` option to `yargs-parser` + * @private + * @ignore + */ +const coerceOpts = types.array.reduce( + (acc, arg) => Object.assign(acc, {[arg]: v => Array.from(new Set(list(v)))}), + {} +); + +/** + * We do not have a case when multiple arguments are ever allowed after a flag + * (e.g., `--foo bar baz quux`), so we fix the number of arguments to 1 across + * the board of non-boolean options. + * This is passed as the `narg` option to `yargs-parser` + * @private + * @ignore + */ +const nargOpts = types.array + .concat(types.string, types.number) + .reduce((acc, arg) => Object.assign(acc, {[arg]: 1}), {}); + +/** + * Wrapper around `yargs-parser` which applies our settings + * @param {string|string[]} args - Arguments to parse + * @param {...Object} configObjects - `configObjects` for yargs-parser + * @private + * @ignore + */ +const parse = (args = [], ...configObjects) => { + // save node-specific args for special handling. + // 1. when these args have a "=" they should be considered to have values + // 2. if they don't, they just boolean flags + // 3. to avoid explicitly defining the set of them, we tell yargs-parser they + // are ALL boolean flags. + // 4. we can then reapply the values after yargs-parser is done. + const nodeArgs = (Array.isArray(args) ? args : args.split(' ')).reduce( + (acc, arg) => { + const pair = arg.split('='); + let flag = pair[0]; + if (isNodeFlag(flag, false)) { + flag = flag.replace(/^--?/, ''); + return arg.includes('=') + ? acc.concat([[flag, pair[1]]]) + : acc.concat([[flag, true]]); + } + return acc; + }, + [] + ); + + const result = yargsParser.detailed(args, { + configuration, + configObjects, + coerce: coerceOpts, + narg: nargOpts, + alias: aliases, + string: types.string, + array: types.array, + number: types.number, + boolean: types.boolean.concat(nodeArgs.map(pair => pair[0])) + }); + if (result.error) { + throw createMissingArgumentError(result.error.message); + } + + // reapply "=" arg values from above + nodeArgs.forEach(([key, value]) => { + result.argv[key] = value; + }); + + return result.argv; +}; + +/** + * - Replaces comments with empty strings + * - Replaces escaped spaces (e.g., 'xxx\ yyy') with HTML space + * - Splits on whitespace, creating array of substrings + * - Filters empty string elements from array + * - Replaces any HTML space with space + * @summary Parses options read from run-control file. + * @private + * @param {string} content - Content read from run-control file. + * @returns {string[]} cmdline options (and associated arguments) + * @ignore + */ +const parseMochaOpts = content => + content + .replace(/^#.*$/gm, '') + .replace(/\\\s/g, '%20') + .split(/\s/) + .filter(Boolean) + .map(value => value.replace(/%20/g, ' ')); + +/** + * Prepends options from run-control file to the command line arguments. + * + * @deprecated Deprecated in v6.0.0; This function is no longer used internally and will be removed in a future version. + * @public + * @alias module:lib/cli/options + * @see {@link https://mochajs.org/#mochaopts|mocha.opts} + */ +module.exports = function getOptions() { + deprecate( + 'getOptions() is DEPRECATED and will be removed from a future version of Mocha. Use loadOptions() instead' + ); + if (process.argv.length === 3 && ONE_AND_DONE_ARGS.has(process.argv[2])) { + return; + } + + const optsPath = + process.argv.indexOf('--opts') === -1 + ? mocharc.opts + : process.argv[process.argv.indexOf('--opts') + 1]; + + try { + const options = parseMochaOpts(fs.readFileSync(optsPath, 'utf8')); + + process.argv = process.argv + .slice(0, 2) + .concat(options.concat(process.argv.slice(2))); + } catch (ignore) { + // NOTE: should console.error() and throw the error + } + + process.env.LOADED_MOCHA_OPTS = true; +}; + +/** + * Given filepath in `args.opts`, attempt to load and parse a `mocha.opts` file. + * @param {Object} [args] - Arguments object + * @param {string|boolean} [args.opts] - Filepath to mocha.opts; defaults to whatever's in `mocharc.opts`, or `false` to skip + * @returns {external:yargsParser.Arguments|void} If read, object containing parsed arguments + * @memberof module:lib/cli/options + * @public + */ +const loadMochaOpts = (args = {}) => { + let result; + let filepath = args.opts; + // /dev/null is backwards compat + if (filepath === false || filepath === '/dev/null') { + return result; + } + filepath = filepath || mocharc.opts; + result = {}; + let mochaOpts; + try { + mochaOpts = fs.readFileSync(filepath, 'utf8'); + debug(`read ${filepath}`); + } catch (err) { + if (args.opts) { + throw new Error(`Unable to read ${filepath}: ${err}`); + } + // ignore otherwise. we tried + debug(`No mocha.opts found at ${filepath}`); + } + + // real args should override `mocha.opts` which should override defaults. + // if there's an exception to catch here, I'm not sure what it is. + // by attaching the `no-opts` arg, we avoid re-parsing of `mocha.opts`. + if (mochaOpts) { + result = parse(parseMochaOpts(mochaOpts)); + debug(`${filepath} parsed succesfully`); + } + return result; +}; + +module.exports.loadMochaOpts = loadMochaOpts; + +/** + * Given path to config file in `args.config`, attempt to load & parse config file. + * @param {Object} [args] - Arguments object + * @param {string|boolean} [args.config] - Path to config file or `false` to skip + * @public + * @memberof module:lib/cli/options + * @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.config` is `false` + */ +const loadRc = (args = {}) => { + if (args.config !== false) { + const config = args.config || findConfig(); + return config ? loadConfig(config) : {}; + } +}; + +module.exports.loadRc = loadRc; + +/** + * Given path to `package.json` in `args.package`, attempt to load config from `mocha` prop. + * @param {Object} [args] - Arguments object + * @param {string|boolean} [args.config] - Path to `package.json` or `false` to skip + * @public + * @memberof module:lib/cli/options + * @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.package` is `false` + */ +const loadPkgRc = (args = {}) => { + let result; + if (args.package === false) { + return result; + } + result = {}; + const filepath = args.package || findUp.sync(mocharc.package); + if (filepath) { + try { + const pkg = JSON.parse(fs.readFileSync(filepath, 'utf8')); + if (pkg.mocha) { + debug(`'mocha' prop of package.json parsed:`, pkg.mocha); + result = pkg.mocha; + } else { + debug(`no config found in ${filepath}`); + } + } catch (err) { + if (args.package) { + throw new Error(`Unable to read/parse ${filepath}: ${err}`); + } + debug(`failed to read default package.json at ${filepath}; ignoring`); + } + } + return result; +}; + +module.exports.loadPkgRc = loadPkgRc; + +/** + * Priority list: + * + * 1. Command-line args + * 2. RC file (`.mocharc.js`, `.mocharc.ya?ml`, `mocharc.json`) + * 3. `mocha` prop of `package.json` + * 4. `mocha.opts` + * 5. default configuration (`lib/mocharc.json`) + * + * If a {@link module:lib/cli/one-and-dones.ONE_AND_DONE_ARGS "one-and-done" option} is present in the `argv` array, no external config files will be read. + * @summary Parses options read from `mocha.opts`, `.mocharc.*` and `package.json`. + * @param {string|string[]} [argv] - Arguments to parse + * @public + * @memberof module:lib/cli/options + * @returns {external:yargsParser.Arguments} Parsed args from everything + */ +const loadOptions = (argv = []) => { + let args = parse(argv); + // short-circuit: look for a flag that would abort loading of mocha.opts + if ( + Array.from(ONE_AND_DONE_ARGS).reduce( + (acc, arg) => acc || arg in args, + false + ) + ) { + return args; + } + + const rcConfig = loadRc(args); + const pkgConfig = loadPkgRc(args); + const optsConfig = loadMochaOpts(args); + + if (rcConfig) { + args.config = false; + args._ = args._.concat(rcConfig._ || []); + } + if (pkgConfig) { + args.package = false; + args._ = args._.concat(pkgConfig._ || []); + } + if (optsConfig) { + args.opts = false; + args._ = args._.concat(optsConfig._ || []); + } + + args = parse( + args._, + args, + rcConfig || {}, + pkgConfig || {}, + optsConfig || {}, + mocharc + ); + + // recombine positional arguments and "spec" + if (args.spec) { + args._ = args._.concat(args.spec); + delete args.spec; + } + + // make unique + args._ = Array.from(new Set(args._)); + + return args; +}; + +module.exports.loadOptions = loadOptions; +module.exports.YARGS_PARSER_CONFIG = YARGS_PARSER_CONFIG; diff --git a/WebServer/node_modules/mocha/lib/cli/run-helpers.js b/WebServer/node_modules/mocha/lib/cli/run-helpers.js new file mode 100644 index 000000000..732161b14 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/run-helpers.js @@ -0,0 +1,337 @@ +'use strict'; + +/** + * Helper scripts for the `run` command + * @see module:lib/cli/run + * @module + * @private + */ + +const fs = require('fs'); +const path = require('path'); +const ansi = require('ansi-colors'); +const debug = require('debug')('mocha:cli:run:helpers'); +const minimatch = require('minimatch'); +const Context = require('../context'); +const Mocha = require('../mocha'); +const utils = require('../utils'); + +const cwd = (exports.cwd = process.cwd()); + +/** + * Exits Mocha when tests + code under test has finished execution (default) + * @param {number} code - Exit code; typically # of failures + * @ignore + * @private + */ +const exitMochaLater = code => { + process.on('exit', () => { + process.exitCode = Math.min(code, 255); + }); +}; + +/** + * Exits Mocha when Mocha itself has finished execution, regardless of + * what the tests or code under test is doing. + * @param {number} code - Exit code; typically # of failures + * @ignore + * @private + */ +const exitMocha = code => { + const clampedCode = Math.min(code, 255); + let draining = 0; + + // Eagerly set the process's exit code in case stream.write doesn't + // execute its callback before the process terminates. + process.exitCode = clampedCode; + + // flush output for Node.js Windows pipe bug + // https://github.com/joyent/node/issues/6247 is just one bug example + // https://github.com/visionmedia/mocha/issues/333 has a good discussion + const done = () => { + if (!draining--) { + process.exit(clampedCode); + } + }; + + const streams = [process.stdout, process.stderr]; + + streams.forEach(stream => { + // submit empty write request and wait for completion + draining += 1; + stream.write('', done); + }); + + done(); +}; + +/** + * Hide the cursor. + * @ignore + * @private + */ +const hideCursor = () => { + process.stdout.write('\u001b[?25l'); +}; + +/** + * Show the cursor. + * @ignore + * @private + */ +const showCursor = () => { + process.stdout.write('\u001b[?25h'); +}; + +/** + * Stop cursor business + * @private + */ +const stop = () => { + process.stdout.write('\u001b[2K'); +}; + +/** + * Coerce a comma-delimited string (or array thereof) into a flattened array of + * strings + * @param {string|string[]} str - Value to coerce + * @returns {string[]} Array of strings + * @private + */ +exports.list = str => + Array.isArray(str) ? exports.list(str.join(',')) : str.split(/ *, */); + +/** + * `require()` the modules as required by `--require ` + * @param {string[]} requires - Modules to require + * @private + */ +exports.handleRequires = (requires = []) => { + requires.forEach(mod => { + let modpath = mod; + if (fs.existsSync(mod, {cwd}) || fs.existsSync(`${mod}.js`, {cwd})) { + modpath = path.resolve(mod); + debug(`resolved ${mod} to ${modpath}`); + } + require(modpath); + debug(`loaded require "${mod}"`); + }); +}; + +/** + * Smash together an array of test files in the correct order + * @param {Object} [opts] - Options + * @param {string[]} [opts.extension] - File extensions to use + * @param {string[]} [opts.spec] - Files, dirs, globs to run + * @param {string[]} [opts.exclude] - Files, dirs, globs to exclude + * @param {boolean} [opts.recursive=false] - Find files recursively + * @param {boolean} [opts.sort=false] - Sort test files + * @returns {string[]} List of files to test + * @private + */ +exports.handleFiles = ({ + exclude = [], + extension = [], + file = [], + recursive = false, + sort = false, + spec = [] +} = {}) => { + let files = []; + const unmatched = []; + spec.forEach(arg => { + let newFiles; + try { + newFiles = utils.lookupFiles(arg, extension, recursive); + } catch (err) { + if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') { + unmatched.push({message: err.message, pattern: err.pattern}); + return; + } + + throw err; + } + + if (typeof newFiles !== 'undefined') { + if (typeof newFiles === 'string') { + newFiles = [newFiles]; + } + newFiles = newFiles.filter(fileName => + exclude.every(pattern => !minimatch(fileName, pattern)) + ); + } + + files = files.concat(newFiles); + }); + + if (!files.length) { + // give full message details when only 1 file is missing + const noneFoundMsg = + unmatched.length === 1 + ? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw + : 'Error: No test files found'; + console.error(ansi.red(noneFoundMsg)); + process.exit(1); + } else { + // print messages as an warning + unmatched.forEach(warning => { + console.warn(ansi.yellow(`Warning: ${warning.message}`)); + }); + } + + const fileArgs = file.map(filepath => path.resolve(filepath)); + files = files.map(filepath => path.resolve(filepath)); + + // ensure we don't sort the stuff from fileArgs; order is important! + if (sort) { + files.sort(); + } + + // add files given through --file to be ran first + files = fileArgs.concat(files); + debug('files (in order): ', files); + return files; +}; + +/** + * Give Mocha files and tell it to run + * @param {Mocha} mocha - Mocha instance + * @param {Options} [opts] - Options + * @param {string[]} [opts.files] - List of test files + * @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete + * @returns {Runner} + * @private + */ +exports.singleRun = (mocha, {files = [], exit = false} = {}) => { + mocha.files = files; + return mocha.run(exit ? exitMocha : exitMochaLater); +}; + +/** + * Run Mocha in "watch" mode + * @param {Mocha} mocha - Mocha instance + * @param {Object} [opts] - Options + * @param {string[]} [opts.extension] - List of extensions to watch + * @param {string|RegExp} [opts.grep] - Grep for test titles + * @param {string} [opts.ui=bdd] - User interface + * @param {string[]} [files] - Array of test files + * @private + */ +exports.watchRun = ( + mocha, + {extension = ['js'], grep = '', ui = 'bdd', files = []} = {} +) => { + let runner; + + console.log(); + hideCursor(); + process.on('SIGINT', () => { + showCursor(); + console.log('\n'); + process.exit(130); + }); + + const watchFiles = utils.files(cwd, extension); + let runAgain = false; + + const loadAndRun = () => { + try { + mocha.files = files; + runAgain = false; + runner = mocha.run(() => { + runner = null; + if (runAgain) { + rerun(); + } + }); + } catch (e) { + console.log(e.stack); + } + }; + + const purge = () => { + watchFiles.forEach(Mocha.unloadFile); + }; + + loadAndRun(); + + const rerun = () => { + purge(); + stop(); + if (!grep) { + mocha.grep(null); + } + mocha.suite = mocha.suite.clone(); + mocha.suite.ctx = new Context(); + mocha.ui(ui); + loadAndRun(); + }; + + utils.watch(watchFiles, () => { + runAgain = true; + if (runner) { + runner.abort(); + } else { + rerun(); + } + }); +}; + +/** + * Actually run tests + * @param {Mocha} mocha - Mocha instance + * @param {Object} [opts] - Options + * @param {boolean} [opts.watch=false] - Enable watch mode + * @param {string[]} [opts.extension] - List of extensions to watch + * @param {string|RegExp} [opts.grep] - Grep for test titles + * @param {string} [opts.ui=bdd] - User interface + * @param {boolean} [opts.exit=false] - Force-exit Mocha when tests done + * @param {string[]} [files] - Array of test files + * @private + */ +exports.runMocha = ( + mocha, + {watch = false, extension = ['js'], grep = '', ui = 'bdd', exit = false} = {}, + files = [] +) => { + if (watch) { + exports.watchRun(mocha, {extension, grep, ui, files}); + } else { + exports.singleRun(mocha, {files, exit}); + } +}; + +/** + * Used for `--reporter` and `--ui`. Ensures there's only one, and asserts + * that it actually exists. + * @todo XXX This must get run after requires are processed, as it'll prevent + * interfaces from loading. + * @param {Object} opts - Options object + * @param {string} key - Resolvable module name or path + * @param {Object} [map] - An object perhaps having key `key` + * @private + */ +exports.validatePlugin = (opts, key, map = {}) => { + if (Array.isArray(opts[key])) { + throw new TypeError(`"--${key} <${key}>" can only be specified once`); + } + + const unknownError = () => new Error(`Unknown "${key}": ${opts[key]}`); + + if (!map[opts[key]]) { + try { + opts[key] = require(opts[key]); + } catch (err) { + if (err.code === 'MODULE_NOT_FOUND') { + // Try to load reporters from a path (absolute or relative) + try { + opts[key] = require(path.resolve(process.cwd(), opts[key])); + } catch (err) { + throw unknownError(); + } + } else { + throw unknownError(); + } + } + } +}; diff --git a/WebServer/node_modules/mocha/lib/cli/run-option-metadata.js b/WebServer/node_modules/mocha/lib/cli/run-option-metadata.js new file mode 100644 index 000000000..0838e6050 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/run-option-metadata.js @@ -0,0 +1,76 @@ +'use strict'; + +/** + * Metadata about various options of the `run` command + * @see module:lib/cli/run + * @module + * @private + */ + +/** + * Dictionary of yargs option types to list of options having said type + * @type {{string:string[]}} + * @private + */ +exports.types = { + array: [ + 'exclude', + 'extension', + 'file', + 'global', + 'require', + 'reporter-option', + 'spec' + ], + boolean: [ + 'allow-uncaught', + 'async-only', + 'bail', + 'check-leaks', + 'color', + 'delay', + 'diff', + 'exit', + 'forbid-only', + 'forbid-pending', + 'full-trace', + 'growl', + 'inline-diffs', + 'interfaces', + 'invert', + 'no-colors', + 'recursive', + 'reporters', + 'sort', + 'watch' + ], + number: ['retries', 'slow', 'timeout'], + string: ['fgrep', 'grep', 'package', 'reporter', 'ui'] +}; + +/** + * Option aliases keyed by canonical option name. + * Arrays used to reduce + * @type {{string:string[]}} + * @private + */ +exports.aliases = { + 'async-only': ['A'], + bail: ['b'], + color: ['c', 'colors'], + extension: ['watch-extensions'], + fgrep: ['f'], + global: ['globals'], + grep: ['g'], + growl: ['G'], + invert: ['i'], + 'no-colors': ['C'], + reporter: ['R'], + 'reporter-option': ['reporter-options', 'O'], + require: ['r'], + slow: ['s'], + sort: ['S'], + timeout: ['t', 'timeouts'], + ui: ['u'], + watch: ['w'] +}; diff --git a/WebServer/node_modules/mocha/lib/cli/run.js b/WebServer/node_modules/mocha/lib/cli/run.js new file mode 100644 index 000000000..3a6ba0072 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/cli/run.js @@ -0,0 +1,297 @@ +'use strict'; + +/** + * Definition for Mocha's default ("run tests") command + * + * @module + * @private + */ + +const Mocha = require('../mocha'); +const { + createUnsupportedError, + createInvalidArgumentValueError, + createMissingArgumentError +} = require('../errors'); + +const { + list, + handleFiles, + handleRequires, + validatePlugin, + runMocha +} = require('./run-helpers'); +const {ONE_AND_DONES, ONE_AND_DONE_ARGS} = require('./one-and-dones'); +const debug = require('debug')('mocha:cli:run'); +const defaults = require('../mocharc'); +const {types, aliases} = require('./run-option-metadata'); + +/** + * Logical option groups + * @constant + */ +const GROUPS = { + FILES: 'File Handling', + FILTERS: 'Test Filters', + NODEJS: 'Node.js & V8', + OUTPUT: 'Reporting & Output', + RULES: 'Rules & Behavior', + CONFIG: 'Configuration' +}; + +exports.command = ['$0 [spec..]', 'debug [spec..]']; + +exports.describe = 'Run tests with Mocha'; + +exports.builder = yargs => + yargs + .options({ + 'allow-uncaught': { + description: 'Allow uncaught errors to propagate', + group: GROUPS.RULES + }, + 'async-only': { + description: + 'Require all tests to use a callback (async) or return a Promise', + group: GROUPS.RULES + }, + bail: { + description: 'Abort ("bail") after first test failure', + group: GROUPS.RULES + }, + 'check-leaks': { + description: 'Check for global variable leaks', + group: GROUPS.RULES + }, + color: { + description: 'Force-enable color output', + group: GROUPS.OUTPUT + }, + config: { + config: true, + defaultDescription: '(nearest rc file)', + description: 'Path to config file', + group: GROUPS.CONFIG + }, + delay: { + description: 'Delay initial execution of root suite', + group: GROUPS.RULES + }, + diff: { + default: true, + description: 'Show diff on failure', + group: GROUPS.OUTPUT + }, + exclude: { + defaultDescription: '(none)', + description: 'Ignore file(s) or glob pattern(s)', + group: GROUPS.FILES, + requiresArg: true + }, + exit: { + description: 'Force Mocha to quit after tests complete', + group: GROUPS.RULES + }, + extension: { + default: defaults.extension, + defaultDescription: 'js', + description: 'File extension(s) to load and/or watch', + group: GROUPS.FILES, + requiresArg: true, + coerce: list + }, + fgrep: { + conflicts: 'grep', + description: 'Only run tests containing this string', + group: GROUPS.FILTERS, + requiresArg: true + }, + file: { + defaultDescription: '(none)', + description: + 'Specify file(s) to be loaded prior to root suite execution', + group: GROUPS.FILES, + normalize: true, + requiresArg: true + }, + 'forbid-only': { + description: 'Fail if exclusive test(s) encountered', + group: GROUPS.RULES + }, + 'forbid-pending': { + description: 'Fail if pending test(s) encountered', + group: GROUPS.RULES + }, + 'full-trace': { + description: 'Display full stack traces', + group: GROUPS.OUTPUT + }, + global: { + coerce: list, + description: 'List of allowed global variables', + group: GROUPS.RULES, + requiresArg: true + }, + grep: { + coerce: value => (!value ? null : value), + conflicts: 'fgrep', + description: 'Only run tests matching this string or regexp', + group: GROUPS.FILTERS, + requiresArg: true + }, + growl: { + description: 'Enable Growl notifications', + group: GROUPS.OUTPUT + }, + 'inline-diffs': { + description: + 'Display actual/expected differences inline within each string', + group: GROUPS.OUTPUT + }, + interfaces: { + conflicts: Array.from(ONE_AND_DONE_ARGS), + description: 'List built-in user interfaces & exit' + }, + invert: { + description: 'Inverts --grep and --fgrep matches', + group: GROUPS.FILTERS + }, + 'no-colors': { + description: 'Force-disable color output', + group: GROUPS.OUTPUT, + hidden: true + }, + opts: { + default: defaults.opts, + description: 'Path to `mocha.opts`', + group: GROUPS.CONFIG, + normalize: true, + requiresArg: true + }, + package: { + description: 'Path to package.json for config', + group: GROUPS.CONFIG, + normalize: true, + requiresArg: true + }, + recursive: { + description: 'Look for tests in subdirectories', + group: GROUPS.FILES + }, + reporter: { + default: defaults.reporter, + description: 'Specify reporter to use', + group: GROUPS.OUTPUT, + requiresArg: true + }, + reporters: { + conflicts: Array.from(ONE_AND_DONE_ARGS), + description: 'List built-in reporters & exit' + }, + 'reporter-option': { + coerce: opts => + list(opts).reduce((acc, opt) => { + const pair = opt.split('='); + + if (pair.length > 2 || !pair.length) { + throw createInvalidArgumentValueError( + `invalid reporter option '${opt}'`, + '--reporter-option', + opt, + 'expected "key=value" format' + ); + } + + acc[pair[0]] = pair.length === 2 ? pair[1] : true; + return acc; + }, {}), + description: 'Reporter-specific options ()', + group: GROUPS.OUTPUT, + requiresArg: true + }, + require: { + defaultDescription: '(none)', + description: 'Require module', + group: GROUPS.FILES, + requiresArg: true + }, + retries: { + description: 'Retry failed tests this many times', + group: GROUPS.RULES + }, + slow: { + default: defaults.slow, + description: 'Specify "slow" test threshold (in milliseconds)', + group: GROUPS.RULES + }, + sort: { + description: 'Sort test files', + group: GROUPS.FILES + }, + timeout: { + default: defaults.timeout, + description: 'Specify test timeout threshold (in milliseconds)', + group: GROUPS.RULES + }, + ui: { + default: defaults.ui, + description: 'Specify user interface', + group: GROUPS.RULES, + requiresArg: true + }, + watch: { + description: 'Watch files in the current working directory for changes', + group: GROUPS.FILES + } + }) + .positional('spec', { + default: ['test'], + description: 'One or more files, directories, or globs to test', + type: 'array' + }) + .check(argv => { + // "one-and-dones"; let yargs handle help and version + Object.keys(ONE_AND_DONES).forEach(opt => { + if (argv[opt]) { + ONE_AND_DONES[opt].call(null, yargs); + process.exit(); + } + }); + + // yargs.implies() isn't flexible enough to handle this + if (argv.invert && !('fgrep' in argv || 'grep' in argv)) { + throw createMissingArgumentError( + '"--invert" requires one of "--fgrep " or "--grep "', + '--fgrep|--grep', + 'string|regexp' + ); + } + + if (argv.compilers) { + throw createUnsupportedError( + `--compilers is DEPRECATED and no longer supported. + See https://git.io/vdcSr for migration information.` + ); + } + + // load requires first, because it can impact "plugin" validation + handleRequires(argv.require); + validatePlugin(argv, 'reporter', Mocha.reporters); + validatePlugin(argv, 'ui', Mocha.interfaces); + + return true; + }) + .array(types.array) + .boolean(types.boolean) + .string(types.string) + .number(types.number) + .alias(aliases); + +exports.handler = argv => { + debug('post-yargs config', argv); + const mocha = new Mocha(argv); + const files = handleFiles(argv); + + debug('running tests with files', files); + runMocha(mocha, argv, files); +}; diff --git a/WebServer/node_modules/mocha/lib/context.js b/WebServer/node_modules/mocha/lib/context.js new file mode 100644 index 000000000..4c6b0c23e --- /dev/null +++ b/WebServer/node_modules/mocha/lib/context.js @@ -0,0 +1,101 @@ +'use strict'; +/** + * @module Context + */ +/** + * Expose `Context`. + */ + +module.exports = Context; + +/** + * Initialize a new `Context`. + * + * @private + */ +function Context() {} + +/** + * Set or get the context `Runnable` to `runnable`. + * + * @private + * @param {Runnable} runnable + * @return {Context} context + */ +Context.prototype.runnable = function(runnable) { + if (!arguments.length) { + return this._runnable; + } + this.test = this._runnable = runnable; + return this; +}; + +/** + * Set or get test timeout `ms`. + * + * @private + * @param {number} ms + * @return {Context} self + */ +Context.prototype.timeout = function(ms) { + if (!arguments.length) { + return this.runnable().timeout(); + } + this.runnable().timeout(ms); + return this; +}; + +/** + * Set test timeout `enabled`. + * + * @private + * @param {boolean} enabled + * @return {Context} self + */ +Context.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this.runnable().enableTimeouts(); + } + this.runnable().enableTimeouts(enabled); + return this; +}; + +/** + * Set or get test slowness threshold `ms`. + * + * @private + * @param {number} ms + * @return {Context} self + */ +Context.prototype.slow = function(ms) { + if (!arguments.length) { + return this.runnable().slow(); + } + this.runnable().slow(ms); + return this; +}; + +/** + * Mark a test as skipped. + * + * @private + * @throws Pending + */ +Context.prototype.skip = function() { + this.runnable().skip(); +}; + +/** + * Set or get a number of allowed retries on failed tests + * + * @private + * @param {number} n + * @return {Context} self + */ +Context.prototype.retries = function(n) { + if (!arguments.length) { + return this.runnable().retries(); + } + this.runnable().retries(n); + return this; +}; diff --git a/WebServer/node_modules/mocha/lib/errors.js b/WebServer/node_modules/mocha/lib/errors.js new file mode 100644 index 000000000..fafee70ee --- /dev/null +++ b/WebServer/node_modules/mocha/lib/errors.js @@ -0,0 +1,141 @@ +'use strict'; +/** + * @module Errors + */ +/** + * Factory functions to create throwable error objects + */ + +/** + * Creates an error object to be thrown when no files to be tested could be found using specified pattern. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} pattern - User-specified argument value. + * @returns {Error} instance detailing the error condition + */ +function createNoFilesMatchPatternError(message, pattern) { + var err = new Error(message); + err.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; + err.pattern = pattern; + return err; +} + +/** + * Creates an error object to be thrown when the reporter specified in the options was not found. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} reporter - User-specified reporter value. + * @returns {Error} instance detailing the error condition + */ +function createInvalidReporterError(message, reporter) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_REPORTER'; + err.reporter = reporter; + return err; +} + +/** + * Creates an error object to be thrown when the interface specified in the options was not found. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} ui - User-specified interface value. + * @returns {Error} instance detailing the error condition + */ +function createInvalidInterfaceError(message, ui) { + var err = new Error(message); + err.code = 'ERR_MOCHA_INVALID_INTERFACE'; + err.interface = ui; + return err; +} + +/** + * Creates an error object to be thrown when a behavior, option, or parameter is unsupported. + * + * @public + * @param {string} message - Error message to be displayed. + * @returns {Error} instance detailing the error condition + */ +function createUnsupportedError(message) { + var err = new Error(message); + err.code = 'ERR_MOCHA_UNSUPPORTED'; + return err; +} + +/** + * Creates an error object to be thrown when an argument is missing. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - Argument name. + * @param {string} expected - Expected argument datatype. + * @returns {Error} instance detailing the error condition + */ +function createMissingArgumentError(message, argument, expected) { + return createInvalidArgumentTypeError(message, argument, expected); +} + +/** + * Creates an error object to be thrown when an argument did not use the supported type + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - Argument name. + * @param {string} expected - Expected argument datatype. + * @returns {Error} instance detailing the error condition + */ +function createInvalidArgumentTypeError(message, argument, expected) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_ARG_TYPE'; + err.argument = argument; + err.expected = expected; + err.actual = typeof argument; + return err; +} + +/** + * Creates an error object to be thrown when an argument did not use the supported value + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - Argument name. + * @param {string} value - Argument value. + * @param {string} [reason] - Why value is invalid. + * @returns {Error} instance detailing the error condition + */ +function createInvalidArgumentValueError(message, argument, value, reason) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_ARG_VALUE'; + err.argument = argument; + err.value = value; + err.reason = typeof reason !== 'undefined' ? reason : 'is invalid'; + return err; +} + +/** + * Creates an error object to be thrown when an exception was caught, but the `Error` is falsy or undefined. + * + * @public + * @param {string} message - Error message to be displayed. + * @returns {Error} instance detailing the error condition + */ +function createInvalidExceptionError(message, value) { + var err = new Error(message); + err.code = 'ERR_MOCHA_INVALID_EXCEPTION'; + err.valueType = typeof value; + err.value = value; + return err; +} + +module.exports = { + createInvalidArgumentTypeError: createInvalidArgumentTypeError, + createInvalidArgumentValueError: createInvalidArgumentValueError, + createInvalidExceptionError: createInvalidExceptionError, + createInvalidInterfaceError: createInvalidInterfaceError, + createInvalidReporterError: createInvalidReporterError, + createMissingArgumentError: createMissingArgumentError, + createNoFilesMatchPatternError: createNoFilesMatchPatternError, + createUnsupportedError: createUnsupportedError +}; diff --git a/WebServer/node_modules/mocha/lib/growl.js b/WebServer/node_modules/mocha/lib/growl.js new file mode 100644 index 000000000..53164563b --- /dev/null +++ b/WebServer/node_modules/mocha/lib/growl.js @@ -0,0 +1,136 @@ +'use strict'; + +/** + * Desktop Notifications module. + * @module Growl + */ + +const os = require('os'); +const path = require('path'); +const {sync: which} = require('which'); +const {EVENT_RUN_END} = require('./runner').constants; + +/** + * @summary + * Checks if Growl notification support seems likely. + * + * @description + * Glosses over the distinction between an unsupported platform + * and one that lacks prerequisite software installations. + * + * @public + * @see {@link https://github.com/tj/node-growl/blob/master/README.md|Prerequisite Installs} + * @see {@link Mocha#growl} + * @see {@link Mocha#isGrowlCapable} + * @return {boolean} whether Growl notification support can be expected + */ +exports.isCapable = () => { + if (!process.browser) { + return getSupportBinaries().reduce( + (acc, binary) => acc || Boolean(which(binary, {nothrow: true})), + false + ); + } + return false; +}; + +/** + * Implements desktop notifications as a pseudo-reporter. + * + * @public + * @see {@link Mocha#_growl} + * @param {Runner} runner - Runner instance. + */ +exports.notify = runner => { + runner.once(EVENT_RUN_END, () => { + display(runner); + }); +}; + +/** + * Displays the notification. + * + * @private + * @param {Runner} runner - Runner instance. + */ +const display = runner => { + const growl = require('growl'); + const stats = runner.stats; + const symbol = { + cross: '\u274C', + tick: '\u2705' + }; + let _message; + let message; + let title; + + if (stats.failures) { + _message = `${stats.failures} of ${stats.tests} tests failed`; + message = `${symbol.cross} ${_message}`; + title = 'Failed'; + } else { + _message = `${stats.passes} tests passed in ${stats.duration}ms`; + message = `${symbol.tick} ${_message}`; + title = 'Passed'; + } + + // Send notification + const options = { + image: logo(), + name: 'mocha', + title + }; + growl(message, options, onCompletion); +}; + +/** + * @summary + * Callback for result of attempted Growl notification. + * + * @description + * Despite its appearance, this is not an Error-first + * callback -- all parameters are populated regardless of success. + * + * @private + * @callback Growl~growlCB + * @param {*} err - Error object, or null if successful. + */ +function onCompletion(err) { + if (err) { + // As notifications are tangential to our purpose, just log the error. + const message = + err.code === 'ENOENT' ? 'prerequisite software not found' : err.message; + console.error('notification error:', message); + } +} + +/** + * Returns Mocha logo image path. + * + * @private + * @return {string} Pathname of Mocha logo + */ +const logo = () => { + return path.join(__dirname, '..', 'assets', 'mocha-logo-96.png'); +}; + +/** + * @summary + * Gets platform-specific Growl support binaries. + * + * @description + * Somewhat brittle dependency on `growl` package implementation, but it + * rarely changes. + * + * @private + * @see {@link https://github.com/tj/node-growl/blob/master/lib/growl.js#L28-L126|setupCmd} + * @return {string[]} names of Growl support binaries + */ +const getSupportBinaries = () => { + const binaries = { + Darwin: ['terminal-notifier', 'growlnotify'], + Linux: ['notify-send', 'growl'], + Windows_NT: ['growlnotify.exe'] + }; + return binaries[os.type()] || []; +}; diff --git a/WebServer/node_modules/mocha/lib/hook.js b/WebServer/node_modules/mocha/lib/hook.js new file mode 100644 index 000000000..71440d23d --- /dev/null +++ b/WebServer/node_modules/mocha/lib/hook.js @@ -0,0 +1,46 @@ +'use strict'; + +var Runnable = require('./runnable'); +var inherits = require('./utils').inherits; + +/** + * Expose `Hook`. + */ + +module.exports = Hook; + +/** + * Initialize a new `Hook` with the given `title` and callback `fn` + * + * @class + * @extends Runnable + * @param {String} title + * @param {Function} fn + */ +function Hook(title, fn) { + Runnable.call(this, title, fn); + this.type = 'hook'; +} + +/** + * Inherit from `Runnable.prototype`. + */ +inherits(Hook, Runnable); + +/** + * Get or set the test `err`. + * + * @memberof Hook + * @public + * @param {Error} err + * @return {Error} + */ +Hook.prototype.error = function(err) { + if (!arguments.length) { + err = this._error; + this._error = null; + return err; + } + + this._error = err; +}; diff --git a/WebServer/node_modules/mocha/lib/interfaces/bdd.js b/WebServer/node_modules/mocha/lib/interfaces/bdd.js new file mode 100644 index 000000000..ef8d281e1 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/interfaces/bdd.js @@ -0,0 +1,118 @@ +'use strict'; + +var Test = require('../test'); +var EVENT_FILE_PRE_REQUIRE = require('../suite').constants + .EVENT_FILE_PRE_REQUIRE; + +/** + * BDD-style interface: + * + * describe('Array', function() { + * describe('#indexOf()', function() { + * it('should return -1 when not present', function() { + * // ... + * }); + * + * it('should return the index when present', function() { + * // ... + * }); + * }); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function bddInterface(suite) { + var suites = [suite]; + + suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { + var common = require('./common')(suites, context, mocha); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.describe = context.context = function(title, fn) { + return common.suite.create({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Pending describe. + */ + + context.xdescribe = context.xcontext = context.describe.skip = function( + title, + fn + ) { + return common.suite.skip({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Exclusive suite. + */ + + context.describe.only = function(title, fn) { + return common.suite.only({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.it = context.specify = function(title, fn) { + var suite = suites[0]; + if (suite.isPending()) { + fn = null; + } + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.it.only = function(title, fn) { + return common.test.only(mocha, context.it(title, fn)); + }; + + /** + * Pending test case. + */ + + context.xit = context.xspecify = context.it.skip = function(title) { + return context.it(title); + }; + + /** + * Number of attempts to retry. + */ + context.it.retries = function(n) { + context.retries(n); + }; + }); +}; + +module.exports.description = 'BDD or RSpec style [default]'; diff --git a/WebServer/node_modules/mocha/lib/interfaces/common.js b/WebServer/node_modules/mocha/lib/interfaces/common.js new file mode 100644 index 000000000..1802fcc9d --- /dev/null +++ b/WebServer/node_modules/mocha/lib/interfaces/common.js @@ -0,0 +1,191 @@ +'use strict'; + +var Suite = require('../suite'); +var errors = require('../errors'); +var createMissingArgumentError = errors.createMissingArgumentError; + +/** + * Functions common to more than one interface. + * + * @param {Suite[]} suites + * @param {Context} context + * @param {Mocha} mocha + * @return {Object} An object containing common functions. + */ +module.exports = function(suites, context, mocha) { + /** + * Check if the suite should be tested. + * + * @private + * @param {Suite} suite - suite to check + * @returns {boolean} + */ + function shouldBeTested(suite) { + return ( + !mocha.options.grep || + (mocha.options.grep && + mocha.options.grep.test(suite.fullTitle()) && + !mocha.options.invert) + ); + } + + return { + /** + * This is only present if flag --delay is passed into Mocha. It triggers + * root suite execution. + * + * @param {Suite} suite The root suite. + * @return {Function} A function which runs the root suite + */ + runWithSuite: function runWithSuite(suite) { + return function run() { + suite.run(); + }; + }, + + /** + * Execute before running tests. + * + * @param {string} name + * @param {Function} fn + */ + before: function(name, fn) { + suites[0].beforeAll(name, fn); + }, + + /** + * Execute after running tests. + * + * @param {string} name + * @param {Function} fn + */ + after: function(name, fn) { + suites[0].afterAll(name, fn); + }, + + /** + * Execute before each test case. + * + * @param {string} name + * @param {Function} fn + */ + beforeEach: function(name, fn) { + suites[0].beforeEach(name, fn); + }, + + /** + * Execute after each test case. + * + * @param {string} name + * @param {Function} fn + */ + afterEach: function(name, fn) { + suites[0].afterEach(name, fn); + }, + + suite: { + /** + * Create an exclusive Suite; convenience function + * See docstring for create() below. + * + * @param {Object} opts + * @returns {Suite} + */ + only: function only(opts) { + opts.isOnly = true; + return this.create(opts); + }, + + /** + * Create a Suite, but skip it; convenience function + * See docstring for create() below. + * + * @param {Object} opts + * @returns {Suite} + */ + skip: function skip(opts) { + opts.pending = true; + return this.create(opts); + }, + + /** + * Creates a suite. + * + * @param {Object} opts Options + * @param {string} opts.title Title of Suite + * @param {Function} [opts.fn] Suite Function (not always applicable) + * @param {boolean} [opts.pending] Is Suite pending? + * @param {string} [opts.file] Filepath where this Suite resides + * @param {boolean} [opts.isOnly] Is Suite exclusive? + * @returns {Suite} + */ + create: function create(opts) { + var suite = Suite.create(suites[0], opts.title); + suite.pending = Boolean(opts.pending); + suite.file = opts.file; + suites.unshift(suite); + if (opts.isOnly) { + if (mocha.options.forbidOnly && shouldBeTested(suite)) { + throw new Error('`.only` forbidden'); + } + + suite.parent.appendOnlySuite(suite); + } + if (suite.pending) { + if (mocha.options.forbidPending && shouldBeTested(suite)) { + throw new Error('Pending test forbidden'); + } + } + if (typeof opts.fn === 'function') { + opts.fn.call(suite); + suites.shift(); + } else if (typeof opts.fn === 'undefined' && !suite.pending) { + throw createMissingArgumentError( + 'Suite "' + + suite.fullTitle() + + '" was defined but no callback was supplied. ' + + 'Supply a callback or explicitly skip the suite.', + 'callback', + 'function' + ); + } else if (!opts.fn && suite.pending) { + suites.shift(); + } + + return suite; + } + }, + + test: { + /** + * Exclusive test-case. + * + * @param {Object} mocha + * @param {Function} test + * @returns {*} + */ + only: function(mocha, test) { + test.parent.appendOnlyTest(test); + return test; + }, + + /** + * Pending test case. + * + * @param {string} title + */ + skip: function(title) { + context.test(title); + }, + + /** + * Number of retry attempts + * + * @param {number} n + */ + retries: function(n) { + context.retries(n); + } + } + }; +}; diff --git a/WebServer/node_modules/mocha/lib/interfaces/exports.js b/WebServer/node_modules/mocha/lib/interfaces/exports.js new file mode 100644 index 000000000..5b4c7bd6f --- /dev/null +++ b/WebServer/node_modules/mocha/lib/interfaces/exports.js @@ -0,0 +1,60 @@ +'use strict'; +var Suite = require('../suite'); +var Test = require('../test'); + +/** + * Exports-style (as Node.js module) interface: + * + * exports.Array = { + * '#indexOf()': { + * 'should return -1 when the value is not present': function() { + * + * }, + * + * 'should return the correct index when the value is present': function() { + * + * } + * } + * }; + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on(Suite.constants.EVENT_FILE_REQUIRE, visit); + + function visit(obj, file) { + var suite; + for (var key in obj) { + if (typeof obj[key] === 'function') { + var fn = obj[key]; + switch (key) { + case 'before': + suites[0].beforeAll(fn); + break; + case 'after': + suites[0].afterAll(fn); + break; + case 'beforeEach': + suites[0].beforeEach(fn); + break; + case 'afterEach': + suites[0].afterEach(fn); + break; + default: + var test = new Test(key, fn); + test.file = file; + suites[0].addTest(test); + } + } else { + suite = Suite.create(suites[0], key); + suites.unshift(suite); + visit(obj[key], file); + suites.shift(); + } + } + } +}; + +module.exports.description = 'Node.js module ("exports") style'; diff --git a/WebServer/node_modules/mocha/lib/interfaces/index.js b/WebServer/node_modules/mocha/lib/interfaces/index.js new file mode 100644 index 000000000..0bd810abb --- /dev/null +++ b/WebServer/node_modules/mocha/lib/interfaces/index.js @@ -0,0 +1,6 @@ +'use strict'; + +exports.bdd = require('./bdd'); +exports.tdd = require('./tdd'); +exports.qunit = require('./qunit'); +exports.exports = require('./exports'); diff --git a/WebServer/node_modules/mocha/lib/interfaces/qunit.js b/WebServer/node_modules/mocha/lib/interfaces/qunit.js new file mode 100644 index 000000000..a2dc33203 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/interfaces/qunit.js @@ -0,0 +1,99 @@ +'use strict'; + +var Test = require('../test'); +var EVENT_FILE_PRE_REQUIRE = require('../suite').constants + .EVENT_FILE_PRE_REQUIRE; + +/** + * QUnit-style interface: + * + * suite('Array'); + * + * test('#length', function() { + * var arr = [1,2,3]; + * ok(arr.length == 3); + * }); + * + * test('#indexOf()', function() { + * var arr = [1,2,3]; + * ok(arr.indexOf(1) == 0); + * ok(arr.indexOf(2) == 1); + * ok(arr.indexOf(3) == 2); + * }); + * + * suite('String'); + * + * test('#length', function() { + * ok('foo'.length == 3); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function qUnitInterface(suite) { + var suites = [suite]; + + suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { + var common = require('./common')(suites, context, mocha); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title`. + */ + + context.suite = function(title) { + if (suites.length > 1) { + suites.shift(); + } + return common.suite.create({ + title: title, + file: file, + fn: false + }); + }; + + /** + * Exclusive Suite. + */ + + context.suite.only = function(title) { + if (suites.length > 1) { + suites.shift(); + } + return common.suite.only({ + title: title, + file: file, + fn: false + }); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn) { + var test = new Test(title, fn); + test.file = file; + suites[0].addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + return common.test.only(mocha, context.test(title, fn)); + }; + + context.test.skip = common.test.skip; + context.test.retries = common.test.retries; + }); +}; + +module.exports.description = 'QUnit style'; diff --git a/WebServer/node_modules/mocha/lib/interfaces/tdd.js b/WebServer/node_modules/mocha/lib/interfaces/tdd.js new file mode 100644 index 000000000..a44c7fe27 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/interfaces/tdd.js @@ -0,0 +1,107 @@ +'use strict'; + +var Test = require('../test'); +var EVENT_FILE_PRE_REQUIRE = require('../suite').constants + .EVENT_FILE_PRE_REQUIRE; + +/** + * TDD-style interface: + * + * suite('Array', function() { + * suite('#indexOf()', function() { + * suiteSetup(function() { + * + * }); + * + * test('should return -1 when not present', function() { + * + * }); + * + * test('should return the index when present', function() { + * + * }); + * + * suiteTeardown(function() { + * + * }); + * }); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { + var common = require('./common')(suites, context, mocha); + + context.setup = common.beforeEach; + context.teardown = common.afterEach; + context.suiteSetup = common.before; + context.suiteTeardown = common.after; + context.run = mocha.options.delay && common.runWithSuite(suite); + + /** + * Describe a "suite" with the given `title` and callback `fn` containing + * nested suites and/or tests. + */ + context.suite = function(title, fn) { + return common.suite.create({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + return common.suite.skip({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Exclusive test-case. + */ + context.suite.only = function(title, fn) { + return common.suite.only({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Describe a specification or test-case with the given `title` and + * callback `fn` acting as a thunk. + */ + context.test = function(title, fn) { + var suite = suites[0]; + if (suite.isPending()) { + fn = null; + } + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + return common.test.only(mocha, context.test(title, fn)); + }; + + context.test.skip = common.test.skip; + context.test.retries = common.test.retries; + }); +}; + +module.exports.description = + 'traditional "suite"/"test" instead of BDD\'s "describe"/"it"'; diff --git a/WebServer/node_modules/mocha/lib/mocha.js b/WebServer/node_modules/mocha/lib/mocha.js new file mode 100644 index 000000000..aab560444 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/mocha.js @@ -0,0 +1,843 @@ +'use strict'; + +/*! + * mocha + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +var escapeRe = require('escape-string-regexp'); +var path = require('path'); +var builtinReporters = require('./reporters'); +var growl = require('./growl'); +var utils = require('./utils'); +var mocharc = require('./mocharc.json'); +var errors = require('./errors'); +var Suite = require('./suite'); +var createStatsCollector = require('./stats-collector'); +var createInvalidReporterError = errors.createInvalidReporterError; +var createInvalidInterfaceError = errors.createInvalidInterfaceError; +var EVENT_FILE_PRE_REQUIRE = Suite.constants.EVENT_FILE_PRE_REQUIRE; +var EVENT_FILE_POST_REQUIRE = Suite.constants.EVENT_FILE_POST_REQUIRE; +var EVENT_FILE_REQUIRE = Suite.constants.EVENT_FILE_REQUIRE; +var sQuote = utils.sQuote; + +exports = module.exports = Mocha; + +/** + * To require local UIs and reporters when running in node. + */ + +if (!process.browser) { + var cwd = process.cwd(); + module.paths.push(cwd, path.join(cwd, 'node_modules')); +} + +/** + * Expose internals. + */ + +/** + * @public + * @class utils + * @memberof Mocha + */ +exports.utils = utils; +exports.interfaces = require('./interfaces'); +/** + * @public + * @memberof Mocha + */ +exports.reporters = builtinReporters; +exports.Runnable = require('./runnable'); +exports.Context = require('./context'); +/** + * + * @memberof Mocha + */ +exports.Runner = require('./runner'); +exports.Suite = Suite; +exports.Hook = require('./hook'); +exports.Test = require('./test'); + +/** + * Constructs a new Mocha instance with `options`. + * + * @public + * @class Mocha + * @param {Object} [options] - Settings object. + * @param {boolean} [options.allowUncaught] - Propagate uncaught errors? + * @param {boolean} [options.asyncOnly] - Force `done` callback or promise? + * @param {boolean} [options.bail] - Bail after first test failure? + * @param {boolean} [options.checkLeaks] - If true, check leaks. + * @param {boolean} [options.delay] - Delay root suite execution? + * @param {boolean} [options.enableTimeouts] - Enable timeouts? + * @param {string} [options.fgrep] - Test filter given string. + * @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite? + * @param {boolean} [options.forbidPending] - Pending tests fail the suite? + * @param {boolean} [options.fullStackTrace] - Full stacktrace upon failure? + * @param {string[]} [options.global] - Variables expected in global scope. + * @param {RegExp|string} [options.grep] - Test filter given regular expression. + * @param {boolean} [options.growl] - Enable desktop notifications? + * @param {boolean} [options.hideDiff] - Suppress diffs from failures? + * @param {boolean} [options.ignoreLeaks] - Ignore global leaks? + * @param {boolean} [options.invert] - Invert test filter matches? + * @param {boolean} [options.noHighlighting] - Disable syntax highlighting? + * @param {string} [options.reporter] - Reporter name. + * @param {Object} [options.reporterOption] - Reporter settings object. + * @param {number} [options.retries] - Number of times to retry failed tests. + * @param {number} [options.slow] - Slow threshold value. + * @param {number|string} [options.timeout] - Timeout threshold value. + * @param {string} [options.ui] - Interface name. + * @param {boolean} [options.color] - Color TTY output from reporter? + * @param {boolean} [options.useInlineDiffs] - Use inline diffs? + */ +function Mocha(options) { + options = utils.assign({}, mocharc, options || {}); + this.files = []; + this.options = options; + // root suite + this.suite = new exports.Suite('', new exports.Context(), true); + + if ('useColors' in options) { + utils.deprecate( + 'useColors is DEPRECATED and will be removed from a future version of Mocha. Instead, use the "color" option' + ); + options.color = 'color' in options ? options.color : options.useColors; + } + + this.grep(options.grep) + .fgrep(options.fgrep) + .ui(options.ui) + .bail(options.bail) + .reporter(options.reporter, options.reporterOptions) + .useColors(options.color) + .slow(options.slow) + .useInlineDiffs(options.inlineDiffs) + .globals(options.globals); + + if ('enableTimeouts' in options) { + utils.deprecate( + 'enableTimeouts is DEPRECATED and will be removed from a future version of Mocha. Instead, use "timeout: false" to disable timeouts.' + ); + if (options.enableTimeouts === false) { + this.timeout(0); + } + } + + // this guard exists because Suite#timeout does not consider `undefined` to be valid input + if (typeof options.timeout !== 'undefined') { + this.timeout(options.timeout === false ? 0 : options.timeout); + } + + if ('retries' in options) { + this.retries(options.retries); + } + + if ('diff' in options) { + this.hideDiff(!options.diff); + } + + [ + 'allowUncaught', + 'asyncOnly', + 'checkLeaks', + 'delay', + 'forbidOnly', + 'forbidPending', + 'fullTrace', + 'growl', + 'invert' + ].forEach(function(opt) { + if (options[opt]) { + this[opt](); + } + }, this); +} + +/** + * Enables or disables bailing on the first failure. + * + * @public + * @see {@link https://mochajs.org/#-b---bail|CLI option} + * @param {boolean} [bail=true] - Whether to bail on first error. + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.bail = function(bail) { + if (!arguments.length) { + bail = true; + } + this.suite.bail(bail); + return this; +}; + +/** + * @summary + * Adds `file` to be loaded for execution. + * + * @description + * Useful for generic setup code that must be included within test suite. + * + * @public + * @see {@link https://mochajs.org/#--file-file|CLI option} + * @param {string} file - Pathname of file to be loaded. + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.addFile = function(file) { + this.files.push(file); + return this; +}; + +/** + * Sets reporter to `reporter`, defaults to "spec". + * + * @public + * @see {@link https://mochajs.org/#-r---reporter-name|CLI option} + * @see {@link https://mochajs.org/#reporters|Reporters} + * @param {String|Function} reporter - Reporter name or constructor. + * @param {Object} [reporterOptions] - Options used to configure the reporter. + * @returns {Mocha} this + * @chainable + * @throws {Error} if requested reporter cannot be loaded + * @example + * + * // Use XUnit reporter and direct its output to file + * mocha.reporter('xunit', { output: '/path/to/testspec.xunit.xml' }); + */ +Mocha.prototype.reporter = function(reporter, reporterOptions) { + if (typeof reporter === 'function') { + this._reporter = reporter; + } else { + reporter = reporter || 'spec'; + var _reporter; + // Try to load a built-in reporter. + if (builtinReporters[reporter]) { + _reporter = builtinReporters[reporter]; + } + // Try to load reporters from process.cwd() and node_modules + if (!_reporter) { + try { + _reporter = require(reporter); + } catch (err) { + if ( + err.code !== 'MODULE_NOT_FOUND' || + err.message.indexOf('Cannot find module') !== -1 + ) { + // Try to load reporters from a path (absolute or relative) + try { + _reporter = require(path.resolve(process.cwd(), reporter)); + } catch (_err) { + _err.code !== 'MODULE_NOT_FOUND' || + _err.message.indexOf('Cannot find module') !== -1 + ? console.warn(sQuote(reporter) + ' reporter not found') + : console.warn( + sQuote(reporter) + + ' reporter blew up with error:\n' + + err.stack + ); + } + } else { + console.warn( + sQuote(reporter) + ' reporter blew up with error:\n' + err.stack + ); + } + } + } + if (!_reporter) { + throw createInvalidReporterError( + 'invalid reporter ' + sQuote(reporter), + reporter + ); + } + this._reporter = _reporter; + } + this.options.reporterOptions = reporterOptions; + return this; +}; + +/** + * Sets test UI `name`, defaults to "bdd". + * + * @public + * @see {@link https://mochajs.org/#-u---ui-name|CLI option} + * @see {@link https://mochajs.org/#interfaces|Interface DSLs} + * @param {string|Function} [ui=bdd] - Interface name or class. + * @returns {Mocha} this + * @chainable + * @throws {Error} if requested interface cannot be loaded + */ +Mocha.prototype.ui = function(ui) { + var bindInterface; + if (typeof ui === 'function') { + bindInterface = ui; + } else { + ui = ui || 'bdd'; + bindInterface = exports.interfaces[ui]; + if (!bindInterface) { + try { + bindInterface = require(ui); + } catch (err) { + throw createInvalidInterfaceError( + 'invalid interface ' + sQuote(ui), + ui + ); + } + } + } + bindInterface(this.suite); + + this.suite.on(EVENT_FILE_PRE_REQUIRE, function(context) { + exports.afterEach = context.afterEach || context.teardown; + exports.after = context.after || context.suiteTeardown; + exports.beforeEach = context.beforeEach || context.setup; + exports.before = context.before || context.suiteSetup; + exports.describe = context.describe || context.suite; + exports.it = context.it || context.test; + exports.xit = context.xit || (context.test && context.test.skip); + exports.setup = context.setup || context.beforeEach; + exports.suiteSetup = context.suiteSetup || context.before; + exports.suiteTeardown = context.suiteTeardown || context.after; + exports.suite = context.suite || context.describe; + exports.teardown = context.teardown || context.afterEach; + exports.test = context.test || context.it; + exports.run = context.run; + }); + + return this; +}; + +/** + * Loads `files` prior to execution. + * + * @description + * The implementation relies on Node's `require` to execute + * the test interface functions and will be subject to its cache. + * + * @private + * @see {@link Mocha#addFile} + * @see {@link Mocha#run} + * @see {@link Mocha#unloadFiles} + * @param {Function} [fn] - Callback invoked upon completion. + */ +Mocha.prototype.loadFiles = function(fn) { + var self = this; + var suite = this.suite; + this.files.forEach(function(file) { + file = path.resolve(file); + suite.emit(EVENT_FILE_PRE_REQUIRE, global, file, self); + suite.emit(EVENT_FILE_REQUIRE, require(file), file, self); + suite.emit(EVENT_FILE_POST_REQUIRE, global, file, self); + }); + fn && fn(); +}; + +/** + * Removes a previously loaded file from Node's `require` cache. + * + * @private + * @static + * @see {@link Mocha#unloadFiles} + * @param {string} file - Pathname of file to be unloaded. + */ +Mocha.unloadFile = function(file) { + delete require.cache[require.resolve(file)]; +}; + +/** + * Unloads `files` from Node's `require` cache. + * + * @description + * This allows files to be "freshly" reloaded, providing the ability + * to reuse a Mocha instance programmatically. + * + * Intended for consumers — not used internally + * + * @public + * @see {@link Mocha.unloadFile} + * @see {@link Mocha#loadFiles} + * @see {@link Mocha#run} + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.unloadFiles = function() { + this.files.forEach(Mocha.unloadFile); + return this; +}; + +/** + * Sets `grep` filter after escaping RegExp special characters. + * + * @public + * @see {@link Mocha#grep} + * @param {string} str - Value to be converted to a regexp. + * @returns {Mocha} this + * @chainable + * @example + * + * // Select tests whose full title begins with `"foo"` followed by a period + * mocha.fgrep('foo.'); + */ +Mocha.prototype.fgrep = function(str) { + if (!str) { + return this; + } + return this.grep(new RegExp(escapeRe(str))); +}; + +/** + * @summary + * Sets `grep` filter used to select specific tests for execution. + * + * @description + * If `re` is a regexp-like string, it will be converted to regexp. + * The regexp is tested against the full title of each test (i.e., the + * name of the test preceded by titles of each its ancestral suites). + * As such, using an exact-match fixed pattern against the + * test name itself will not yield any matches. + *
+ * Previous filter value will be overwritten on each call! + * + * @public + * @see {@link https://mochajs.org/#-g---grep-pattern|CLI option} + * @see {@link Mocha#fgrep} + * @see {@link Mocha#invert} + * @param {RegExp|String} re - Regular expression used to select tests. + * @return {Mocha} this + * @chainable + * @example + * + * // Select tests whose full title contains `"match"`, ignoring case + * mocha.grep(/match/i); + * @example + * + * // Same as above but with regexp-like string argument + * mocha.grep('/match/i'); + * @example + * + * // ## Anti-example + * // Given embedded test `it('only-this-test')`... + * mocha.grep('/^only-this-test$/'); // NO! Use `.only()` to do this! + */ +Mocha.prototype.grep = function(re) { + if (utils.isString(re)) { + // extract args if it's regex-like, i.e: [string, pattern, flag] + var arg = re.match(/^\/(.*)\/(g|i|)$|.*/); + this.options.grep = new RegExp(arg[1] || arg[0], arg[2]); + } else { + this.options.grep = re; + } + return this; +}; + +/** + * Inverts `grep` matches. + * + * @public + * @see {@link Mocha#grep} + * @return {Mocha} this + * @chainable + * @example + * + * // Select tests whose full title does *not* contain `"match"`, ignoring case + * mocha.grep(/match/i).invert(); + */ +Mocha.prototype.invert = function() { + this.options.invert = true; + return this; +}; + +/** + * Enables or disables ignoring global leaks. + * + * @public + * @see {@link Mocha#checkLeaks} + * @param {boolean} ignoreLeaks - Whether to ignore global leaks. + * @return {Mocha} this + * @chainable + * @example + * + * // Ignore global leaks + * mocha.ignoreLeaks(true); + */ +Mocha.prototype.ignoreLeaks = function(ignoreLeaks) { + this.options.ignoreLeaks = Boolean(ignoreLeaks); + return this; +}; + +/** + * Enables checking for global variables leaked while running tests. + * + * @public + * @see {@link https://mochajs.org/#--check-leaks|CLI option} + * @see {@link Mocha#ignoreLeaks} + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.checkLeaks = function() { + this.options.ignoreLeaks = false; + return this; +}; + +/** + * Displays full stack trace upon test failure. + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.fullTrace = function() { + this.options.fullStackTrace = true; + return this; +}; + +/** + * Enables desktop notification support if prerequisite software installed. + * + * @public + * @see {@link Mocha#isGrowlCapable} + * @see {@link Mocha#_growl} + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.growl = function() { + this.options.growl = this.isGrowlCapable(); + if (!this.options.growl) { + var detail = process.browser + ? 'notification support not available in this browser...' + : 'notification support prerequisites not installed...'; + console.error(detail + ' cannot enable!'); + } + return this; +}; + +/** + * @summary + * Determines if Growl support seems likely. + * + * @description + * Not available when run in browser. + * + * @private + * @see {@link Growl#isCapable} + * @see {@link Mocha#growl} + * @return {boolean} whether Growl support can be expected + */ +Mocha.prototype.isGrowlCapable = growl.isCapable; + +/** + * Implements desktop notifications using a pseudo-reporter. + * + * @private + * @see {@link Mocha#growl} + * @see {@link Growl#notify} + * @param {Runner} runner - Runner instance. + */ +Mocha.prototype._growl = growl.notify; + +/** + * Specifies whitelist of variable names to be expected in global scope. + * + * @public + * @see {@link https://mochajs.org/#--globals-names|CLI option} + * @see {@link Mocha#checkLeaks} + * @param {String[]|String} globals - Accepted global variable name(s). + * @return {Mocha} this + * @chainable + * @example + * + * // Specify variables to be expected in global scope + * mocha.globals(['jQuery', 'MyLib']); + */ +Mocha.prototype.globals = function(globals) { + this.options.globals = (this.options.globals || []) + .concat(globals) + .filter(Boolean); + return this; +}; + +/** + * Enables or disables TTY color output by screen-oriented reporters. + * + * @public + * @param {boolean} colors - Whether to enable color output. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.useColors = function(colors) { + if (colors !== undefined) { + this.options.useColors = colors; + } + return this; +}; + +/** + * Determines if reporter should use inline diffs (rather than +/-) + * in test failure output. + * + * @public + * @param {boolean} inlineDiffs - Whether to use inline diffs. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.useInlineDiffs = function(inlineDiffs) { + this.options.useInlineDiffs = inlineDiffs !== undefined && inlineDiffs; + return this; +}; + +/** + * Determines if reporter should include diffs in test failure output. + * + * @public + * @param {boolean} hideDiff - Whether to hide diffs. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.hideDiff = function(hideDiff) { + this.options.hideDiff = hideDiff !== undefined && hideDiff; + return this; +}; + +/** + * @summary + * Sets timeout threshold value. + * + * @description + * A string argument can use shorthand (such as "2s") and will be converted. + * If the value is `0`, timeouts will be disabled. + * + * @public + * @see {@link https://mochajs.org/#-t---timeout-ms|CLI option} + * @see {@link https://mochajs.org/#--no-timeouts|CLI option} + * @see {@link https://mochajs.org/#timeouts|Timeouts} + * @see {@link Mocha#enableTimeouts} + * @param {number|string} msecs - Timeout threshold value. + * @return {Mocha} this + * @chainable + * @example + * + * // Sets timeout to one second + * mocha.timeout(1000); + * @example + * + * // Same as above but using string argument + * mocha.timeout('1s'); + */ +Mocha.prototype.timeout = function(msecs) { + this.suite.timeout(msecs); + return this; +}; + +/** + * Sets the number of times to retry failed tests. + * + * @public + * @see {@link https://mochajs.org/#retry-tests|Retry Tests} + * @param {number} retry - Number of times to retry failed tests. + * @return {Mocha} this + * @chainable + * @example + * + * // Allow any failed test to retry one more time + * mocha.retries(1); + */ +Mocha.prototype.retries = function(n) { + this.suite.retries(n); + return this; +}; + +/** + * Sets slowness threshold value. + * + * @public + * @see {@link https://mochajs.org/#-s---slow-ms|CLI option} + * @param {number} msecs - Slowness threshold value. + * @return {Mocha} this + * @chainable + * @example + * + * // Sets "slow" threshold to half a second + * mocha.slow(500); + * @example + * + * // Same as above but using string argument + * mocha.slow('0.5s'); + */ +Mocha.prototype.slow = function(msecs) { + this.suite.slow(msecs); + return this; +}; + +/** + * Enables or disables timeouts. + * + * @public + * @see {@link https://mochajs.org/#-t---timeout-ms|CLI option} + * @see {@link https://mochajs.org/#--no-timeouts|CLI option} + * @param {boolean} enableTimeouts - Whether to enable timeouts. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.enableTimeouts = function(enableTimeouts) { + this.suite.enableTimeouts( + arguments.length && enableTimeouts !== undefined ? enableTimeouts : true + ); + return this; +}; + +/** + * Forces all tests to either accept a `done` callback or return a promise. + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.asyncOnly = function() { + this.options.asyncOnly = true; + return this; +}; + +/** + * Disables syntax highlighting (in browser). + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + +/** + * Enables uncaught errors to propagate (in browser). + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.allowUncaught = function() { + this.options.allowUncaught = true; + return this; +}; + +/** + * @summary + * Delays root suite execution. + * + * @description + * Used to perform asynch operations before any suites are run. + * + * @public + * @see {@link https://mochajs.org/#delayed-root-suite|delayed root suite} + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.delay = function delay() { + this.options.delay = true; + return this; +}; + +/** + * Causes tests marked `only` to fail the suite. + * + * @public + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.forbidOnly = function() { + this.options.forbidOnly = true; + return this; +}; + +/** + * Causes pending tests and tests marked `skip` to fail the suite. + * + * @public + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.forbidPending = function() { + this.options.forbidPending = true; + return this; +}; + +/** + * Mocha version as specified by "package.json". + * + * @name Mocha#version + * @type string + * @readonly + */ +Object.defineProperty(Mocha.prototype, 'version', { + value: require('../package.json').version, + configurable: false, + enumerable: true, + writable: false +}); + +/** + * Callback to be invoked when test execution is complete. + * + * @callback DoneCB + * @param {number} failures - Number of failures that occurred. + */ + +/** + * Runs root suite and invokes `fn()` when complete. + * + * @description + * To run tests multiple times (or to run tests in files that are + * already in the `require` cache), make sure to clear them from + * the cache first! + * + * @public + * @see {@link Mocha#loadFiles} + * @see {@link Mocha#unloadFiles} + * @see {@link Runner#run} + * @param {DoneCB} [fn] - Callback invoked when test execution completed. + * @return {Runner} runner instance + */ +Mocha.prototype.run = function(fn) { + if (this.files.length) { + this.loadFiles(); + } + var suite = this.suite; + var options = this.options; + options.files = this.files; + var runner = new exports.Runner(suite, options.delay); + createStatsCollector(runner); + var reporter = new this._reporter(runner, options); + runner.ignoreLeaks = options.ignoreLeaks !== false; + runner.fullStackTrace = options.fullStackTrace; + runner.asyncOnly = options.asyncOnly; + runner.allowUncaught = options.allowUncaught; + runner.forbidOnly = options.forbidOnly; + runner.forbidPending = options.forbidPending; + if (options.grep) { + runner.grep(options.grep, options.invert); + } + if (options.globals) { + runner.globals(options.globals); + } + if (options.growl) { + this._growl(runner); + } + if (options.useColors !== undefined) { + exports.reporters.Base.useColors = options.useColors; + } + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + exports.reporters.Base.hideDiff = options.hideDiff; + + function done(failures) { + fn = fn || utils.noop; + if (reporter.done) { + reporter.done(failures, fn); + } else { + fn(failures); + } + } + + return runner.run(done); +}; diff --git a/WebServer/node_modules/mocha/lib/mocharc.json b/WebServer/node_modules/mocha/lib/mocharc.json new file mode 100644 index 000000000..b0cd26671 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/mocharc.json @@ -0,0 +1,10 @@ +{ + "diff": true, + "extension": ["js"], + "opts": "./test/mocha.opts", + "package": "./package.json", + "reporter": "spec", + "slow": 75, + "timeout": 2000, + "ui": "bdd" +} diff --git a/WebServer/node_modules/mocha/lib/pending.js b/WebServer/node_modules/mocha/lib/pending.js new file mode 100644 index 000000000..bb9a50533 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/pending.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = Pending; + +/** + * Initialize a new `Pending` error with the given message. + * + * @param {string} message + */ +function Pending(message) { + this.message = message; +} diff --git a/WebServer/node_modules/mocha/lib/reporters/base.js b/WebServer/node_modules/mocha/lib/reporters/base.js new file mode 100644 index 000000000..3736c018c --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/base.js @@ -0,0 +1,491 @@ +'use strict'; +/** + * @module Base + */ +/** + * Module dependencies. + */ + +var tty = require('tty'); +var diff = require('diff'); +var milliseconds = require('ms'); +var utils = require('../utils'); +var supportsColor = process.browser ? null : require('supports-color'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; + +/** + * Expose `Base`. + */ + +exports = module.exports = Base; + +/** + * Check if both stdio streams are associated with a tty. + */ + +var isatty = tty.isatty(1) && tty.isatty(2); + +/** + * Enable coloring by default, except in the browser interface. + */ + +exports.useColors = + !process.browser && + (supportsColor.stdout || process.env.MOCHA_COLORS !== undefined); + +/** + * Inline diffs instead of +/- + */ + +exports.inlineDiffs = false; + +/** + * Default color map. + */ + +exports.colors = { + pass: 90, + fail: 31, + 'bright pass': 92, + 'bright fail': 91, + 'bright yellow': 93, + pending: 36, + suite: 0, + 'error title': 0, + 'error message': 31, + 'error stack': 90, + checkmark: 32, + fast: 90, + medium: 33, + slow: 31, + green: 32, + light: 90, + 'diff gutter': 90, + 'diff added': 32, + 'diff removed': 31 +}; + +/** + * Default symbol map. + */ + +exports.symbols = { + ok: '✓', + err: '✖', + dot: '․', + comma: ',', + bang: '!' +}; + +// With node.js on Windows: use symbols available in terminal default fonts +if (process.platform === 'win32') { + exports.symbols.ok = '\u221A'; + exports.symbols.err = '\u00D7'; + exports.symbols.dot = '.'; +} + +/** + * Color `str` with the given `type`, + * allowing colors to be disabled, + * as well as user-defined color + * schemes. + * + * @private + * @param {string} type + * @param {string} str + * @return {string} + */ +var color = (exports.color = function(type, str) { + if (!exports.useColors) { + return String(str); + } + return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; +}); + +/** + * Expose term window size, with some defaults for when stderr is not a tty. + */ + +exports.window = { + width: 75 +}; + +if (isatty) { + exports.window.width = process.stdout.getWindowSize + ? process.stdout.getWindowSize(1)[0] + : tty.getWindowSize()[1]; +} + +/** + * Expose some basic cursor interactions that are common among reporters. + */ + +exports.cursor = { + hide: function() { + isatty && process.stdout.write('\u001b[?25l'); + }, + + show: function() { + isatty && process.stdout.write('\u001b[?25h'); + }, + + deleteLine: function() { + isatty && process.stdout.write('\u001b[2K'); + }, + + beginningOfLine: function() { + isatty && process.stdout.write('\u001b[0G'); + }, + + CR: function() { + if (isatty) { + exports.cursor.deleteLine(); + exports.cursor.beginningOfLine(); + } else { + process.stdout.write('\r'); + } + } +}; + +function showDiff(err) { + return ( + err && + err.showDiff !== false && + sameType(err.actual, err.expected) && + err.expected !== undefined + ); +} + +function stringifyDiffObjs(err) { + if (!utils.isString(err.actual) || !utils.isString(err.expected)) { + err.actual = utils.stringify(err.actual); + err.expected = utils.stringify(err.expected); + } +} + +/** + * Returns a diff between 2 strings with coloured ANSI output. + * + * @description + * The diff will be either inline or unified dependent on the value + * of `Base.inlineDiff`. + * + * @param {string} actual + * @param {string} expected + * @return {string} Diff + */ +var generateDiff = (exports.generateDiff = function(actual, expected) { + return exports.inlineDiffs + ? inlineDiff(actual, expected) + : unifiedDiff(actual, expected); +}); + +/** + * Outputs the given `failures` as a list. + * + * @public + * @memberof Mocha.reporters.Base + * @variation 1 + * @param {Object[]} failures - Each is Test instance with corresponding + * Error property + */ +exports.list = function(failures) { + console.log(); + failures.forEach(function(test, i) { + // format + var fmt = + color('error title', ' %s) %s:\n') + + color('error message', ' %s') + + color('error stack', '\n%s\n'); + + // msg + var msg; + var err = test.err; + var message; + if (err.message && typeof err.message.toString === 'function') { + message = err.message + ''; + } else if (typeof err.inspect === 'function') { + message = err.inspect() + ''; + } else { + message = ''; + } + var stack = err.stack || message; + var index = message ? stack.indexOf(message) : -1; + + if (index === -1) { + msg = message; + } else { + index += message.length; + msg = stack.slice(0, index); + // remove msg from stack + stack = stack.slice(index + 1); + } + + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + // explicitly show diff + if (!exports.hideDiff && showDiff(err)) { + stringifyDiffObjs(err); + fmt = + color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); + var match = message.match(/^([^:]+): expected/); + msg = '\n ' + color('error message', match ? match[1] : msg); + + msg += generateDiff(err.actual, err.expected); + } + + // indent stack trace + stack = stack.replace(/^/gm, ' '); + + // indented test title + var testTitle = ''; + test.titlePath().forEach(function(str, index) { + if (index !== 0) { + testTitle += '\n '; + } + for (var i = 0; i < index; i++) { + testTitle += ' '; + } + testTitle += str; + }); + + console.log(fmt, i + 1, testTitle, msg, stack); + }); +}; + +/** + * Constructs a new `Base` reporter instance. + * + * @description + * All other reporters generally inherit from this reporter. + * + * @public + * @class + * @memberof Mocha.reporters + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Base(runner, options) { + var failures = (this.failures = []); + + if (!runner) { + throw new TypeError('Missing runner argument'); + } + this.options = options || {}; + this.runner = runner; + this.stats = runner.stats; // assigned so Reporters keep a closer reference + + runner.on(EVENT_TEST_PASS, function(test) { + if (test.duration > test.slow()) { + test.speed = 'slow'; + } else if (test.duration > test.slow() / 2) { + test.speed = 'medium'; + } else { + test.speed = 'fast'; + } + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + if (showDiff(err)) { + stringifyDiffObjs(err); + } + test.err = err; + failures.push(test); + }); +} + +/** + * Outputs common epilogue used by many of the bundled reporters. + * + * @public + * @memberof Mocha.reporters.Base + */ +Base.prototype.epilogue = function() { + var stats = this.stats; + var fmt; + + console.log(); + + // passes + fmt = + color('bright pass', ' ') + + color('green', ' %d passing') + + color('light', ' (%s)'); + + console.log(fmt, stats.passes || 0, milliseconds(stats.duration)); + + // pending + if (stats.pending) { + fmt = color('pending', ' ') + color('pending', ' %d pending'); + + console.log(fmt, stats.pending); + } + + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + console.log(fmt, stats.failures); + + Base.list(this.failures); + console.log(); + } + + console.log(); +}; + +/** + * Pads the given `str` to `len`. + * + * @private + * @param {string} str + * @param {string} len + * @return {string} + */ +function pad(str, len) { + str = String(str); + return Array(len - str.length + 1).join(' ') + str; +} + +/** + * Returns inline diff between 2 strings with coloured ANSI output. + * + * @private + * @param {String} actual + * @param {String} expected + * @return {string} Diff + */ +function inlineDiff(actual, expected) { + var msg = errorDiff(actual, expected); + + // linenos + var lines = msg.split('\n'); + if (lines.length > 4) { + var width = String(lines.length).length; + msg = lines + .map(function(str, i) { + return pad(++i, width) + ' |' + ' ' + str; + }) + .join('\n'); + } + + // legend + msg = + '\n' + + color('diff removed', 'actual') + + ' ' + + color('diff added', 'expected') + + '\n\n' + + msg + + '\n'; + + // indent + msg = msg.replace(/^/gm, ' '); + return msg; +} + +/** + * Returns unified diff between two strings with coloured ANSI output. + * + * @private + * @param {String} actual + * @param {String} expected + * @return {string} The diff. + */ +function unifiedDiff(actual, expected) { + var indent = ' '; + function cleanUp(line) { + if (line[0] === '+') { + return indent + colorLines('diff added', line); + } + if (line[0] === '-') { + return indent + colorLines('diff removed', line); + } + if (line.match(/@@/)) { + return '--'; + } + if (line.match(/\\ No newline/)) { + return null; + } + return indent + line; + } + function notBlank(line) { + return typeof line !== 'undefined' && line !== null; + } + var msg = diff.createPatch('string', actual, expected); + var lines = msg.split('\n').splice(5); + return ( + '\n ' + + colorLines('diff added', '+ expected') + + ' ' + + colorLines('diff removed', '- actual') + + '\n\n' + + lines + .map(cleanUp) + .filter(notBlank) + .join('\n') + ); +} + +/** + * Returns character diff for `err`. + * + * @private + * @param {String} actual + * @param {String} expected + * @return {string} the diff + */ +function errorDiff(actual, expected) { + return diff + .diffWordsWithSpace(actual, expected) + .map(function(str) { + if (str.added) { + return colorLines('diff added', str.value); + } + if (str.removed) { + return colorLines('diff removed', str.value); + } + return str.value; + }) + .join(''); +} + +/** + * Colors lines for `str`, using the color `name`. + * + * @private + * @param {string} name + * @param {string} str + * @return {string} + */ +function colorLines(name, str) { + return str + .split('\n') + .map(function(str) { + return color(name, str); + }) + .join('\n'); +} + +/** + * Object#toString reference. + */ +var objToString = Object.prototype.toString; + +/** + * Checks that a / b have the same type. + * + * @private + * @param {Object} a + * @param {Object} b + * @return {boolean} + */ +function sameType(a, b) { + return objToString.call(a) === objToString.call(b); +} + +Base.abstract = true; diff --git a/WebServer/node_modules/mocha/lib/reporters/doc.js b/WebServer/node_modules/mocha/lib/reporters/doc.js new file mode 100644 index 000000000..efcb2d0ca --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/doc.js @@ -0,0 +1,85 @@ +'use strict'; +/** + * @module Doc + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; + +/** + * Expose `Doc`. + */ + +exports = module.exports = Doc; + +/** + * Constructs a new `Doc` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Doc(runner, options) { + Base.call(this, runner, options); + + var indents = 2; + + function indent() { + return Array(indents).join(' '); + } + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + if (suite.root) { + return; + } + ++indents; + console.log('%s
', indent()); + ++indents; + console.log('%s

%s

', indent(), utils.escape(suite.title)); + console.log('%s
', indent()); + }); + + runner.on(EVENT_SUITE_END, function(suite) { + if (suite.root) { + return; + } + console.log('%s
', indent()); + --indents; + console.log('%s
', indent()); + --indents; + }); + + runner.on(EVENT_TEST_PASS, function(test) { + console.log('%s
%s
', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.body)); + console.log('%s
%s
', indent(), code); + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + console.log( + '%s
%s
', + indent(), + utils.escape(test.title) + ); + var code = utils.escape(utils.clean(test.body)); + console.log( + '%s
%s
', + indent(), + code + ); + console.log('%s
%s
', indent(), utils.escape(err)); + }); +} + +Doc.description = 'HTML documentation'; diff --git a/WebServer/node_modules/mocha/lib/reporters/dot.js b/WebServer/node_modules/mocha/lib/reporters/dot.js new file mode 100644 index 000000000..c4c5ce5d9 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/dot.js @@ -0,0 +1,81 @@ +'use strict'; +/** + * @module Dot + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_RUN_END = constants.EVENT_RUN_END; + +/** + * Expose `Dot`. + */ + +exports = module.exports = Dot; + +/** + * Constructs a new `Dot` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Dot(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.75) | 0; + var n = -1; + + runner.on(EVENT_RUN_BEGIN, function() { + process.stdout.write('\n'); + }); + + runner.on(EVENT_TEST_PENDING, function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(Base.color('pending', Base.symbols.comma)); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + if (++n % width === 0) { + process.stdout.write('\n '); + } + if (test.speed === 'slow') { + process.stdout.write(Base.color('bright yellow', Base.symbols.dot)); + } else { + process.stdout.write(Base.color(test.speed, Base.symbols.dot)); + } + }); + + runner.on(EVENT_TEST_FAIL, function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(Base.color('fail', Base.symbols.bang)); + }); + + runner.once(EVENT_RUN_END, function() { + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Dot, Base); + +Dot.description = 'dot matrix representation'; diff --git a/WebServer/node_modules/mocha/lib/reporters/html.js b/WebServer/node_modules/mocha/lib/reporters/html.js new file mode 100644 index 000000000..d3f488a94 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/html.js @@ -0,0 +1,390 @@ +'use strict'; + +/* eslint-env browser */ +/** + * @module HTML + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var Progress = require('../browser/progress'); +var escapeRe = require('escape-string-regexp'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date; + +/** + * Expose `HTML`. + */ + +exports = module.exports = HTML; + +/** + * Stats template. + */ + +var statsTemplate = + ''; + +var playIcon = '‣'; + +/** + * Constructs a new `HTML` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function HTML(runner, options) { + Base.call(this, runner, options); + + var self = this; + var stats = this.stats; + var stat = fragment(statsTemplate); + var items = stat.getElementsByTagName('li'); + var passes = items[1].getElementsByTagName('em')[0]; + var passesLink = items[1].getElementsByTagName('a')[0]; + var failures = items[2].getElementsByTagName('em')[0]; + var failuresLink = items[2].getElementsByTagName('a')[0]; + var duration = items[3].getElementsByTagName('em')[0]; + var canvas = stat.getElementsByTagName('canvas')[0]; + var report = fragment('
    '); + var stack = [report]; + var progress; + var ctx; + var root = document.getElementById('mocha'); + + if (canvas.getContext) { + var ratio = window.devicePixelRatio || 1; + canvas.style.width = canvas.width; + canvas.style.height = canvas.height; + canvas.width *= ratio; + canvas.height *= ratio; + ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + progress = new Progress(); + } + + if (!root) { + return error('#mocha div missing, add it to your document'); + } + + // pass toggle + on(passesLink, 'click', function(evt) { + evt.preventDefault(); + unhide(); + var name = /pass/.test(report.className) ? '' : ' pass'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test pass'); + } + }); + + // failure toggle + on(failuresLink, 'click', function(evt) { + evt.preventDefault(); + unhide(); + var name = /fail/.test(report.className) ? '' : ' fail'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test fail'); + } + }); + + root.appendChild(stat); + root.appendChild(report); + + if (progress) { + progress.size(40); + } + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + if (suite.root) { + return; + } + + // suite + var url = self.suiteURL(suite); + var el = fragment( + '
  • %s

  • ', + url, + escape(suite.title) + ); + + // container + stack[0].appendChild(el); + stack.unshift(document.createElement('ul')); + el.appendChild(stack[0]); + }); + + runner.on(EVENT_SUITE_END, function(suite) { + if (suite.root) { + updateStats(); + return; + } + stack.shift(); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var url = self.testURL(test); + var markup = + '
  • %e%ems ' + + '' + + playIcon + + '

  • '; + var el = fragment(markup, test.speed, test.title, test.duration, url); + self.addCodeToggle(el, test.body); + appendToStack(el); + updateStats(); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + var el = fragment( + '
  • %e ' + + playIcon + + '

  • ', + test.title, + self.testURL(test) + ); + var stackString; // Note: Includes leading newline + var message = test.err.toString(); + + // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we + // check for the result of the stringifying. + if (message === '[object Error]') { + message = test.err.message; + } + + if (test.err.stack) { + var indexOfMessage = test.err.stack.indexOf(test.err.message); + if (indexOfMessage === -1) { + stackString = test.err.stack; + } else { + stackString = test.err.stack.substr( + test.err.message.length + indexOfMessage + ); + } + } else if (test.err.sourceURL && test.err.line !== undefined) { + // Safari doesn't give you a stack. Let's at least provide a source line. + stackString = '\n(' + test.err.sourceURL + ':' + test.err.line + ')'; + } + + stackString = stackString || ''; + + if (test.err.htmlMessage && stackString) { + el.appendChild( + fragment( + '
    %s\n
    %e
    ', + test.err.htmlMessage, + stackString + ) + ); + } else if (test.err.htmlMessage) { + el.appendChild( + fragment('
    %s
    ', test.err.htmlMessage) + ); + } else { + el.appendChild( + fragment('
    %e%e
    ', message, stackString) + ); + } + + self.addCodeToggle(el, test.body); + appendToStack(el); + updateStats(); + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + var el = fragment( + '
  • %e

  • ', + test.title + ); + appendToStack(el); + updateStats(); + }); + + function appendToStack(el) { + // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. + if (stack[0]) { + stack[0].appendChild(el); + } + } + + function updateStats() { + // TODO: add to stats + var percent = ((stats.tests / runner.total) * 100) | 0; + if (progress) { + progress.update(percent).draw(ctx); + } + + // update stats + var ms = new Date() - stats.start; + text(passes, stats.passes); + text(failures, stats.failures); + text(duration, (ms / 1000).toFixed(2)); + } +} + +/** + * Makes a URL, preserving querystring ("search") parameters. + * + * @param {string} s + * @return {string} A new URL. + */ +function makeUrl(s) { + var search = window.location.search; + + // Remove previous grep query parameter if present + if (search) { + search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?'); + } + + return ( + window.location.pathname + + (search ? search + '&' : '?') + + 'grep=' + + encodeURIComponent(escapeRe(s)) + ); +} + +/** + * Provide suite URL. + * + * @param {Object} [suite] + */ +HTML.prototype.suiteURL = function(suite) { + return makeUrl(suite.fullTitle()); +}; + +/** + * Provide test URL. + * + * @param {Object} [test] + */ +HTML.prototype.testURL = function(test) { + return makeUrl(test.fullTitle()); +}; + +/** + * Adds code toggle functionality for the provided test's list element. + * + * @param {HTMLLIElement} el + * @param {string} contents + */ +HTML.prototype.addCodeToggle = function(el, contents) { + var h2 = el.getElementsByTagName('h2')[0]; + + on(h2, 'click', function() { + pre.style.display = pre.style.display === 'none' ? 'block' : 'none'; + }); + + var pre = fragment('
    %e
    ', utils.clean(contents)); + el.appendChild(pre); + pre.style.display = 'none'; +}; + +/** + * Display error `msg`. + * + * @param {string} msg + */ +function error(msg) { + document.body.appendChild(fragment('
    %s
    ', msg)); +} + +/** + * Return a DOM fragment from `html`. + * + * @param {string} html + */ +function fragment(html) { + var args = arguments; + var div = document.createElement('div'); + var i = 1; + + div.innerHTML = html.replace(/%([se])/g, function(_, type) { + switch (type) { + case 's': + return String(args[i++]); + case 'e': + return escape(args[i++]); + // no default + } + }); + + return div.firstChild; +} + +/** + * Check for suites that do not have elements + * with `classname`, and hide them. + * + * @param {text} classname + */ +function hideSuitesWithout(classname) { + var suites = document.getElementsByClassName('suite'); + for (var i = 0; i < suites.length; i++) { + var els = suites[i].getElementsByClassName(classname); + if (!els.length) { + suites[i].className += ' hidden'; + } + } +} + +/** + * Unhide .hidden suites. + */ +function unhide() { + var els = document.getElementsByClassName('suite hidden'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); + } +} + +/** + * Set an element's text contents. + * + * @param {HTMLElement} el + * @param {string} contents + */ +function text(el, contents) { + if (el.textContent) { + el.textContent = contents; + } else { + el.innerText = contents; + } +} + +/** + * Listen on `event` with callback `fn`. + */ +function on(el, event, fn) { + if (el.addEventListener) { + el.addEventListener(event, fn, false); + } else { + el.attachEvent('on' + event, fn); + } +} + +HTML.browserOnly = true; diff --git a/WebServer/node_modules/mocha/lib/reporters/index.js b/WebServer/node_modules/mocha/lib/reporters/index.js new file mode 100644 index 000000000..d3b5481e4 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/index.js @@ -0,0 +1,19 @@ +'use strict'; + +// Alias exports to a their normalized format Mocha#reporter to prevent a need +// for dynamic (try/catch) requires, which Browserify doesn't handle. +exports.Base = exports.base = require('./base'); +exports.Dot = exports.dot = require('./dot'); +exports.Doc = exports.doc = require('./doc'); +exports.TAP = exports.tap = require('./tap'); +exports.JSON = exports.json = require('./json'); +exports.HTML = exports.html = require('./html'); +exports.List = exports.list = require('./list'); +exports.Min = exports.min = require('./min'); +exports.Spec = exports.spec = require('./spec'); +exports.Nyan = exports.nyan = require('./nyan'); +exports.XUnit = exports.xunit = require('./xunit'); +exports.Markdown = exports.markdown = require('./markdown'); +exports.Progress = exports.progress = require('./progress'); +exports.Landing = exports.landing = require('./landing'); +exports.JSONStream = exports['json-stream'] = require('./json-stream'); diff --git a/WebServer/node_modules/mocha/lib/reporters/json-stream.js b/WebServer/node_modules/mocha/lib/reporters/json-stream.js new file mode 100644 index 000000000..27282987e --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/json-stream.js @@ -0,0 +1,90 @@ +'use strict'; +/** + * @module JSONStream + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; + +/** + * Expose `JSONStream`. + */ + +exports = module.exports = JSONStream; + +/** + * Constructs a new `JSONStream` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function JSONStream(runner, options) { + Base.call(this, runner, options); + + var self = this; + var total = runner.total; + + runner.once(EVENT_RUN_BEGIN, function() { + writeEvent(['start', {total: total}]); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + writeEvent(['pass', clean(test)]); + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + test = clean(test); + test.err = err.message; + test.stack = err.stack || null; + writeEvent(['fail', test]); + }); + + runner.once(EVENT_RUN_END, function() { + writeEvent(['end', self.stats]); + }); +} + +/** + * Mocha event to be written to the output stream. + * @typedef {Array} JSONStream~MochaEvent + */ + +/** + * Writes Mocha event to reporter output stream. + * + * @private + * @param {JSONStream~MochaEvent} event - Mocha event to be output. + */ +function writeEvent(event) { + process.stdout.write(JSON.stringify(event) + '\n'); +} + +/** + * Returns an object literal representation of `test` + * free of cyclic properties, etc. + * + * @private + * @param {Test} test - Instance used as data source. + * @return {Object} object containing pared-down test instance data + */ +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + currentRetry: test.currentRetry() + }; +} + +JSONStream.description = 'newline delimited JSON events'; diff --git a/WebServer/node_modules/mocha/lib/reporters/json.js b/WebServer/node_modules/mocha/lib/reporters/json.js new file mode 100644 index 000000000..12b6289cd --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/json.js @@ -0,0 +1,135 @@ +'use strict'; +/** + * @module JSON + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; + +/** + * Expose `JSON`. + */ + +exports = module.exports = JSONReporter; + +/** + * Constructs a new `JSON` reporter instance. + * + * @public + * @class JSON + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function JSONReporter(runner, options) { + Base.call(this, runner, options); + + var self = this; + var tests = []; + var pending = []; + var failures = []; + var passes = []; + + runner.on(EVENT_TEST_END, function(test) { + tests.push(test); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + passes.push(test); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + failures.push(test); + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + pending.push(test); + }); + + runner.once(EVENT_RUN_END, function() { + var obj = { + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + }; + + runner.testResults = obj; + + process.stdout.write(JSON.stringify(obj, null, 2)); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + var err = test.err || {}; + if (err instanceof Error) { + err = errorJSON(err); + } + + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + currentRetry: test.currentRetry(), + err: cleanCycles(err) + }; +} + +/** + * Replaces any circular references inside `obj` with '[object Object]' + * + * @private + * @param {Object} obj + * @return {Object} + */ +function cleanCycles(obj) { + var cache = []; + return JSON.parse( + JSON.stringify(obj, function(key, value) { + if (typeof value === 'object' && value !== null) { + if (cache.indexOf(value) !== -1) { + // Instead of going in a circle, we'll print [object Object] + return '' + value; + } + cache.push(value); + } + + return value; + }) + ); +} + +/** + * Transform an Error object into a JSON object. + * + * @private + * @param {Error} err + * @return {Object} + */ +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} + +JSONReporter.description = 'single JSON object'; diff --git a/WebServer/node_modules/mocha/lib/reporters/landing.js b/WebServer/node_modules/mocha/lib/reporters/landing.js new file mode 100644 index 000000000..b124c7789 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/landing.js @@ -0,0 +1,108 @@ +'use strict'; +/** + * @module Landing + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var STATE_FAILED = require('../runnable').constants.STATE_FAILED; + +var cursor = Base.cursor; +var color = Base.color; + +/** + * Expose `Landing`. + */ + +exports = module.exports = Landing; + +/** + * Airplane color. + */ + +Base.colors.plane = 0; + +/** + * Airplane crash color. + */ + +Base.colors['plane crash'] = 31; + +/** + * Runway color. + */ + +Base.colors.runway = 90; + +/** + * Constructs a new `Landing` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Landing(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.75) | 0; + var total = runner.total; + var stream = process.stdout; + var plane = color('plane', '✈'); + var crashed = -1; + var n = 0; + + function runway() { + var buf = Array(width).join('-'); + return ' ' + color('runway', buf); + } + + runner.on(EVENT_RUN_BEGIN, function() { + stream.write('\n\n\n '); + cursor.hide(); + }); + + runner.on(EVENT_TEST_END, function(test) { + // check if the plane crashed + var col = crashed === -1 ? ((width * ++n) / total) | 0 : crashed; + + // show the crash + if (test.state === STATE_FAILED) { + plane = color('plane crash', '✈'); + crashed = col; + } + + // render landing strip + stream.write('\u001b[' + (width + 1) + 'D\u001b[2A'); + stream.write(runway()); + stream.write('\n '); + stream.write(color('runway', Array(col).join('⋅'))); + stream.write(plane); + stream.write(color('runway', Array(width - col).join('⋅') + '\n')); + stream.write(runway()); + stream.write('\u001b[0m'); + }); + + runner.once(EVENT_RUN_END, function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Landing, Base); + +Landing.description = 'Unicode landing strip'; diff --git a/WebServer/node_modules/mocha/lib/reporters/list.js b/WebServer/node_modules/mocha/lib/reporters/list.js new file mode 100644 index 000000000..cdf427964 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/list.js @@ -0,0 +1,78 @@ +'use strict'; +/** + * @module List + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Constructs a new `List` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function List(runner, options) { + Base.call(this, runner, options); + + var self = this; + var n = 0; + + runner.on(EVENT_RUN_BEGIN, function() { + console.log(); + }); + + runner.on(EVENT_TEST_BEGIN, function(test) { + process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + var fmt = color('checkmark', ' -') + color('pending', ' %s'); + console.log(fmt, test.fullTitle()); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var fmt = + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s: ') + + color(test.speed, '%dms'); + cursor.CR(); + console.log(fmt, test.fullTitle(), test.duration); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + cursor.CR(); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + }); + + runner.once(EVENT_RUN_END, self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(List, Base); + +List.description = 'like "spec" reporter but flat'; diff --git a/WebServer/node_modules/mocha/lib/reporters/markdown.js b/WebServer/node_modules/mocha/lib/reporters/markdown.js new file mode 100644 index 000000000..460e24840 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/markdown.js @@ -0,0 +1,112 @@ +'use strict'; +/** + * @module Markdown + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var constants = require('../runner').constants; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; + +/** + * Constants + */ + +var SUITE_PREFIX = '$'; + +/** + * Expose `Markdown`. + */ + +exports = module.exports = Markdown; + +/** + * Constructs a new `Markdown` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Markdown(runner, options) { + Base.call(this, runner, options); + + var level = 0; + var buf = ''; + + function title(str) { + return Array(level).join('#') + ' ' + str; + } + + function mapTOC(suite, obj) { + var ret = obj; + var key = SUITE_PREFIX + suite.title; + + obj = obj[key] = obj[key] || {suite: suite}; + suite.suites.forEach(function(suite) { + mapTOC(suite, obj); + }); + + return ret; + } + + function stringifyTOC(obj, level) { + ++level; + var buf = ''; + var link; + for (var key in obj) { + if (key === 'suite') { + continue; + } + if (key !== SUITE_PREFIX) { + link = ' - [' + key.substring(1) + ']'; + link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; + buf += Array(level).join(' ') + link; + } + buf += stringifyTOC(obj[key], level); + } + return buf; + } + + function generateTOC(suite) { + var obj = mapTOC(suite, {}); + return stringifyTOC(obj, 0); + } + + generateTOC(runner.suite); + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + ++level; + var slug = utils.slug(suite.fullTitle()); + buf += '' + '\n'; + buf += title(suite.title) + '\n'; + }); + + runner.on(EVENT_SUITE_END, function() { + --level; + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var code = utils.clean(test.body); + buf += test.title + '.\n'; + buf += '\n```js\n'; + buf += code + '\n'; + buf += '```\n\n'; + }); + + runner.once(EVENT_RUN_END, function() { + process.stdout.write('# TOC\n'); + process.stdout.write(generateTOC(runner.suite)); + process.stdout.write(buf); + }); +} + +Markdown.description = 'GitHub Flavored Markdown'; diff --git a/WebServer/node_modules/mocha/lib/reporters/min.js b/WebServer/node_modules/mocha/lib/reporters/min.js new file mode 100644 index 000000000..019ffe595 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/min.js @@ -0,0 +1,52 @@ +'use strict'; +/** + * @module Min + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; + +/** + * Expose `Min`. + */ + +exports = module.exports = Min; + +/** + * Constructs a new `Min` reporter instance. + * + * @description + * This minimal test reporter is best used with '--watch'. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Min(runner, options) { + Base.call(this, runner, options); + + runner.on(EVENT_RUN_BEGIN, function() { + // clear screen + process.stdout.write('\u001b[2J'); + // set cursor position + process.stdout.write('\u001b[1;3H'); + }); + + runner.once(EVENT_RUN_END, this.epilogue.bind(this)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Min, Base); + +Min.description = 'essentially just a summary'; diff --git a/WebServer/node_modules/mocha/lib/reporters/nyan.js b/WebServer/node_modules/mocha/lib/reporters/nyan.js new file mode 100644 index 000000000..4eda971e7 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/nyan.js @@ -0,0 +1,276 @@ +'use strict'; +/** + * @module Nyan + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var inherits = require('../utils').inherits; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; + +/** + * Expose `Dot`. + */ + +exports = module.exports = NyanCat; + +/** + * Constructs a new `Nyan` reporter instance. + * + * @public + * @class Nyan + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function NyanCat(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.75) | 0; + var nyanCatWidth = (this.nyanCatWidth = 11); + + this.colorIndex = 0; + this.numberOfLines = 4; + this.rainbowColors = self.generateColors(); + this.scoreboardWidth = 5; + this.tick = 0; + this.trajectories = [[], [], [], []]; + this.trajectoryWidthMax = width - nyanCatWidth; + + runner.on(EVENT_RUN_BEGIN, function() { + Base.cursor.hide(); + self.draw(); + }); + + runner.on(EVENT_TEST_PENDING, function() { + self.draw(); + }); + + runner.on(EVENT_TEST_PASS, function() { + self.draw(); + }); + + runner.on(EVENT_TEST_FAIL, function() { + self.draw(); + }); + + runner.once(EVENT_RUN_END, function() { + Base.cursor.show(); + for (var i = 0; i < self.numberOfLines; i++) { + write('\n'); + } + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(NyanCat, Base); + +/** + * Draw the nyan cat + * + * @private + */ + +NyanCat.prototype.draw = function() { + this.appendRainbow(); + this.drawScoreboard(); + this.drawRainbow(); + this.drawNyanCat(); + this.tick = !this.tick; +}; + +/** + * Draw the "scoreboard" showing the number + * of passes, failures and pending tests. + * + * @private + */ + +NyanCat.prototype.drawScoreboard = function() { + var stats = this.stats; + + function draw(type, n) { + write(' '); + write(Base.color(type, n)); + write('\n'); + } + + draw('green', stats.passes); + draw('fail', stats.failures); + draw('pending', stats.pending); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Append the rainbow. + * + * @private + */ + +NyanCat.prototype.appendRainbow = function() { + var segment = this.tick ? '_' : '-'; + var rainbowified = this.rainbowify(segment); + + for (var index = 0; index < this.numberOfLines; index++) { + var trajectory = this.trajectories[index]; + if (trajectory.length >= this.trajectoryWidthMax) { + trajectory.shift(); + } + trajectory.push(rainbowified); + } +}; + +/** + * Draw the rainbow. + * + * @private + */ + +NyanCat.prototype.drawRainbow = function() { + var self = this; + + this.trajectories.forEach(function(line) { + write('\u001b[' + self.scoreboardWidth + 'C'); + write(line.join('')); + write('\n'); + }); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw the nyan cat + * + * @private + */ +NyanCat.prototype.drawNyanCat = function() { + var self = this; + var startWidth = this.scoreboardWidth + this.trajectories[0].length; + var dist = '\u001b[' + startWidth + 'C'; + var padding = ''; + + write(dist); + write('_,------,'); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write('_|' + padding + '/\\_/\\ '); + write('\n'); + + write(dist); + padding = self.tick ? '_' : '__'; + var tail = self.tick ? '~' : '^'; + write(tail + '|' + padding + this.face() + ' '); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write(padding + '"" "" '); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw nyan cat face. + * + * @private + * @return {string} + */ + +NyanCat.prototype.face = function() { + var stats = this.stats; + if (stats.failures) { + return '( x .x)'; + } else if (stats.pending) { + return '( o .o)'; + } else if (stats.passes) { + return '( ^ .^)'; + } + return '( - .-)'; +}; + +/** + * Move cursor up `n`. + * + * @private + * @param {number} n + */ + +NyanCat.prototype.cursorUp = function(n) { + write('\u001b[' + n + 'A'); +}; + +/** + * Move cursor down `n`. + * + * @private + * @param {number} n + */ + +NyanCat.prototype.cursorDown = function(n) { + write('\u001b[' + n + 'B'); +}; + +/** + * Generate rainbow colors. + * + * @private + * @return {Array} + */ +NyanCat.prototype.generateColors = function() { + var colors = []; + + for (var i = 0; i < 6 * 7; i++) { + var pi3 = Math.floor(Math.PI / 3); + var n = i * (1.0 / 6); + var r = Math.floor(3 * Math.sin(n) + 3); + var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); + var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); + colors.push(36 * r + 6 * g + b + 16); + } + + return colors; +}; + +/** + * Apply rainbow to the given `str`. + * + * @private + * @param {string} str + * @return {string} + */ +NyanCat.prototype.rainbowify = function(str) { + if (!Base.useColors) { + return str; + } + var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; + this.colorIndex += 1; + return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; +}; + +/** + * Stdout helper. + * + * @param {string} string A message to write to stdout. + */ +function write(string) { + process.stdout.write(string); +} + +NyanCat.description = '"nyan cat"'; diff --git a/WebServer/node_modules/mocha/lib/reporters/progress.js b/WebServer/node_modules/mocha/lib/reporters/progress.js new file mode 100644 index 000000000..0432e4fb0 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/progress.js @@ -0,0 +1,104 @@ +'use strict'; +/** + * @module Progress + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `Progress`. + */ + +exports = module.exports = Progress; + +/** + * General progress bar color. + */ + +Base.colors.progress = 90; + +/** + * Constructs a new `Progress` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Progress(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.5) | 0; + var total = runner.total; + var complete = 0; + var lastN = -1; + + // default chars + options = options || {}; + var reporterOptions = options.reporterOptions || {}; + + options.open = reporterOptions.open || '['; + options.complete = reporterOptions.complete || '▬'; + options.incomplete = reporterOptions.incomplete || Base.symbols.dot; + options.close = reporterOptions.close || ']'; + options.verbose = reporterOptions.verbose || false; + + // tests started + runner.on(EVENT_RUN_BEGIN, function() { + console.log(); + cursor.hide(); + }); + + // tests complete + runner.on(EVENT_TEST_END, function() { + complete++; + + var percent = complete / total; + var n = (width * percent) | 0; + var i = width - n; + + if (n === lastN && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + + cursor.CR(); + process.stdout.write('\u001b[J'); + process.stdout.write(color('progress', ' ' + options.open)); + process.stdout.write(Array(n).join(options.complete)); + process.stdout.write(Array(i).join(options.incomplete)); + process.stdout.write(color('progress', options.close)); + if (options.verbose) { + process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + } + }); + + // tests are complete, output some stats + // and the failures if any + runner.once(EVENT_RUN_END, function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Progress, Base); + +Progress.description = 'a progress bar'; diff --git a/WebServer/node_modules/mocha/lib/reporters/spec.js b/WebServer/node_modules/mocha/lib/reporters/spec.js new file mode 100644 index 000000000..e1ae95e92 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/spec.js @@ -0,0 +1,99 @@ +'use strict'; +/** + * @module Spec + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var inherits = require('../utils').inherits; +var color = Base.color; + +/** + * Expose `Spec`. + */ + +exports = module.exports = Spec; + +/** + * Constructs a new `Spec` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Spec(runner, options) { + Base.call(this, runner, options); + + var self = this; + var indents = 0; + var n = 0; + + function indent() { + return Array(indents).join(' '); + } + + runner.on(EVENT_RUN_BEGIN, function() { + console.log(); + }); + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + ++indents; + console.log(color('suite', '%s%s'), indent(), suite.title); + }); + + runner.on(EVENT_SUITE_END, function() { + --indents; + if (indents === 1) { + console.log(); + } + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + var fmt = indent() + color('pending', ' - %s'); + console.log(fmt, test.title); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var fmt; + if (test.speed === 'fast') { + fmt = + indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s'); + console.log(fmt, test.title); + } else { + fmt = + indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s') + + color(test.speed, ' (%dms)'); + console.log(fmt, test.title, test.duration); + } + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + }); + + runner.once(EVENT_RUN_END, self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Spec, Base); + +Spec.description = 'hierarchical & verbose [default]'; diff --git a/WebServer/node_modules/mocha/lib/reporters/tap.js b/WebServer/node_modules/mocha/lib/reporters/tap.js new file mode 100644 index 000000000..12257a745 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/tap.js @@ -0,0 +1,294 @@ +'use strict'; +/** + * @module TAP + */ +/** + * Module dependencies. + */ + +var util = require('util'); +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var inherits = require('../utils').inherits; +var sprintf = util.format; + +/** + * Expose `TAP`. + */ + +exports = module.exports = TAP; + +/** + * Constructs a new `TAP` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function TAP(runner, options) { + Base.call(this, runner, options); + + var self = this; + var n = 1; + + var tapVersion = '12'; + if (options && options.reporterOptions) { + if (options.reporterOptions.tapVersion) { + tapVersion = options.reporterOptions.tapVersion.toString(); + } + } + + this._producer = createProducer(tapVersion); + + runner.once(EVENT_RUN_BEGIN, function() { + var ntests = runner.grepTotal(runner.suite); + self._producer.writeVersion(); + self._producer.writePlan(ntests); + }); + + runner.on(EVENT_TEST_END, function() { + ++n; + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + self._producer.writePending(n, test); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + self._producer.writePass(n, test); + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + self._producer.writeFail(n, test, err); + }); + + runner.once(EVENT_RUN_END, function() { + self._producer.writeEpilogue(runner.stats); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); + +/** + * Returns a TAP-safe title of `test`. + * + * @private + * @param {Test} test - Test instance. + * @return {String} title with any hash character removed + */ +function title(test) { + return test.fullTitle().replace(/#/g, ''); +} + +/** + * Writes newline-terminated formatted string to reporter output stream. + * + * @private + * @param {string} format - `printf`-like format string + * @param {...*} [varArgs] - Format string arguments + */ +function println(format, varArgs) { + var vargs = Array.from(arguments); + vargs[0] += '\n'; + process.stdout.write(sprintf.apply(null, vargs)); +} + +/** + * Returns a `tapVersion`-appropriate TAP producer instance, if possible. + * + * @private + * @param {string} tapVersion - Version of TAP specification to produce. + * @returns {TAPProducer} specification-appropriate instance + * @throws {Error} if specification version has no associated producer. + */ +function createProducer(tapVersion) { + var producers = { + '12': new TAP12Producer(), + '13': new TAP13Producer() + }; + var producer = producers[tapVersion]; + + if (!producer) { + throw new Error( + 'invalid or unsupported TAP version: ' + JSON.stringify(tapVersion) + ); + } + + return producer; +} + +/** + * @summary + * Constructs a new TAPProducer. + * + * @description + * Only to be used as an abstract base class. + * + * @private + * @constructor + */ +function TAPProducer() {} + +/** + * Writes the TAP version to reporter output stream. + * + * @abstract + */ +TAPProducer.prototype.writeVersion = function() {}; + +/** + * Writes the plan to reporter output stream. + * + * @abstract + * @param {number} ntests - Number of tests that are planned to run. + */ +TAPProducer.prototype.writePlan = function(ntests) { + println('%d..%d', 1, ntests); +}; + +/** + * Writes that test passed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that passed. + * @param {Test} test - Instance containing test information. + */ +TAPProducer.prototype.writePass = function(n, test) { + println('ok %d %s', n, title(test)); +}; + +/** + * Writes that test was skipped to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that was skipped. + * @param {Test} test - Instance containing test information. + */ +TAPProducer.prototype.writePending = function(n, test) { + println('ok %d %s # SKIP -', n, title(test)); +}; + +/** + * Writes that test failed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that failed. + * @param {Test} test - Instance containing test information. + * @param {Error} err - Reason the test failed. + */ +TAPProducer.prototype.writeFail = function(n, test, err) { + println('not ok %d %s', n, title(test)); +}; + +/** + * Writes the summary epilogue to reporter output stream. + * + * @abstract + * @param {Object} stats - Object containing run statistics. + */ +TAPProducer.prototype.writeEpilogue = function(stats) { + // :TBD: Why is this not counting pending tests? + println('# tests ' + (stats.passes + stats.failures)); + println('# pass ' + stats.passes); + // :TBD: Why are we not showing pending results? + println('# fail ' + stats.failures); +}; + +/** + * @summary + * Constructs a new TAP12Producer. + * + * @description + * Produces output conforming to the TAP12 specification. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-specification.html|Specification} + */ +function TAP12Producer() { + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); + if (err.message) { + println(err.message.replace(/^/gm, ' ')); + } + if (err.stack) { + println(err.stack.replace(/^/gm, ' ')); + } + }; +} + +/** + * Inherit from `TAPProducer.prototype`. + */ +inherits(TAP12Producer, TAPProducer); + +/** + * @summary + * Constructs a new TAP13Producer. + * + * @description + * Produces output conforming to the TAP13 specification. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-version-13-specification.html|Specification} + */ +function TAP13Producer() { + /** + * Writes the TAP version to reporter output stream. + * @override + */ + this.writeVersion = function() { + println('TAP version 13'); + }; + + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + println(indent(1) + '---'); + if (err.message) { + println(indent(2) + 'message: |-'); + println(err.message.replace(/^/gm, indent(3))); + } + if (err.stack) { + println(indent(2) + 'stack: |-'); + println(err.stack.replace(/^/gm, indent(3))); + } + println(indent(1) + '...'); + } + }; + + function indent(level) { + return Array(level + 1).join(' '); + } +} + +/** + * Inherit from `TAPProducer.prototype`. + */ +inherits(TAP13Producer, TAPProducer); + +TAP.description = 'TAP-compatible output'; diff --git a/WebServer/node_modules/mocha/lib/reporters/xunit.js b/WebServer/node_modules/mocha/lib/reporters/xunit.js new file mode 100644 index 000000000..09b32f1ca --- /dev/null +++ b/WebServer/node_modules/mocha/lib/reporters/xunit.js @@ -0,0 +1,216 @@ +'use strict'; +/** + * @module XUnit + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var fs = require('fs'); +var mkdirp = require('mkdirp'); +var path = require('path'); +var errors = require('../errors'); +var createUnsupportedError = errors.createUnsupportedError; +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var STATE_FAILED = require('../runnable').constants.STATE_FAILED; +var inherits = utils.inherits; +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ +var Date = global.Date; + +/** + * Expose `XUnit`. + */ + +exports = module.exports = XUnit; + +/** + * Constructs a new `XUnit` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function XUnit(runner, options) { + Base.call(this, runner, options); + + var stats = this.stats; + var tests = []; + var self = this; + + // the name of the test suite, as it will appear in the resulting XML file + var suiteName; + + // the default name of the test suite if none is provided + var DEFAULT_SUITE_NAME = 'Mocha Tests'; + + if (options && options.reporterOptions) { + if (options.reporterOptions.output) { + if (!fs.createWriteStream) { + throw createUnsupportedError('file output not supported in browser'); + } + + mkdirp.sync(path.dirname(options.reporterOptions.output)); + self.fileStream = fs.createWriteStream(options.reporterOptions.output); + } + + // get the suite name from the reporter options (if provided) + suiteName = options.reporterOptions.suiteName; + } + + // fall back to the default suite name + suiteName = suiteName || DEFAULT_SUITE_NAME; + + runner.on(EVENT_TEST_PENDING, function(test) { + tests.push(test); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + tests.push(test); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + tests.push(test); + }); + + runner.once(EVENT_RUN_END, function() { + self.write( + tag( + 'testsuite', + { + name: suiteName, + tests: stats.tests, + failures: 0, + errors: stats.failures, + skipped: stats.tests - stats.failures - stats.passes, + timestamp: new Date().toUTCString(), + time: stats.duration / 1000 || 0 + }, + false + ) + ); + + tests.forEach(function(t) { + self.test(t); + }); + + self.write(''); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(XUnit, Base); + +/** + * Override done to close the stream (if it's a file). + * + * @param failures + * @param {Function} fn + */ +XUnit.prototype.done = function(failures, fn) { + if (this.fileStream) { + this.fileStream.end(function() { + fn(failures); + }); + } else { + fn(failures); + } +}; + +/** + * Write out the given line. + * + * @param {string} line + */ +XUnit.prototype.write = function(line) { + if (this.fileStream) { + this.fileStream.write(line + '\n'); + } else if (typeof process === 'object' && process.stdout) { + process.stdout.write(line + '\n'); + } else { + console.log(line); + } +}; + +/** + * Output tag for the given `test.` + * + * @param {Test} test + */ +XUnit.prototype.test = function(test) { + Base.useColors = false; + + var attrs = { + classname: test.parent.fullTitle(), + name: test.title, + time: test.duration / 1000 || 0 + }; + + if (test.state === STATE_FAILED) { + var err = test.err; + var diff = + Base.hideDiff || !err.actual || !err.expected + ? '' + : '\n' + Base.generateDiff(err.actual, err.expected); + this.write( + tag( + 'testcase', + attrs, + false, + tag( + 'failure', + {}, + false, + escape(err.message) + escape(diff) + '\n' + escape(err.stack) + ) + ) + ); + } else if (test.isPending()) { + this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); + } else { + this.write(tag('testcase', attrs, true)); + } +}; + +/** + * HTML tag helper. + * + * @param name + * @param attrs + * @param close + * @param content + * @return {string} + */ +function tag(name, attrs, close, content) { + var end = close ? '/>' : '>'; + var pairs = []; + var tag; + + for (var key in attrs) { + if (Object.prototype.hasOwnProperty.call(attrs, key)) { + pairs.push(key + '="' + escape(attrs[key]) + '"'); + } + } + + tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; + if (content) { + tag += content + '0, 2^31-1]. + * If clamped value matches either range endpoint, timeouts will be disabled. + * + * @private + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value} + * @param {number|string} ms - Timeout threshold value. + * @returns {Runnable} this + * @chainable + */ +Runnable.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + + // Clamp to range + var INT_MAX = Math.pow(2, 31) - 1; + var range = [0, INT_MAX]; + ms = utils.clamp(ms, range); + + // see #1652 for reasoning + if (ms === range[0] || ms === range[1]) { + this._enableTimeouts = false; + } + debug('timeout %d', ms); + this._timeout = ms; + if (this.timer) { + this.resetTimeout(); + } + return this; +}; + +/** + * Set or get slow `ms`. + * + * @private + * @param {number|string} ms + * @return {Runnable|number} ms or Runnable instance. + */ +Runnable.prototype.slow = function(ms) { + if (!arguments.length || typeof ms === 'undefined') { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set and get whether timeout is `enabled`. + * + * @private + * @param {boolean} enabled + * @return {Runnable|boolean} enabled or Runnable instance. + */ +Runnable.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Halt and mark as pending. + * + * @memberof Mocha.Runnable + * @public + */ +Runnable.prototype.skip = function() { + throw new Pending('sync skip'); +}; + +/** + * Check if this runnable or its parent suite is marked as pending. + * + * @private + */ +Runnable.prototype.isPending = function() { + return this.pending || (this.parent && this.parent.isPending()); +}; + +/** + * Return `true` if this Runnable has failed. + * @return {boolean} + * @private + */ +Runnable.prototype.isFailed = function() { + return !this.isPending() && this.state === constants.STATE_FAILED; +}; + +/** + * Return `true` if this Runnable has passed. + * @return {boolean} + * @private + */ +Runnable.prototype.isPassed = function() { + return !this.isPending() && this.state === constants.STATE_PASSED; +}; + +/** + * Set or get number of retries. + * + * @private + */ +Runnable.prototype.retries = function(n) { + if (!arguments.length) { + return this._retries; + } + this._retries = n; +}; + +/** + * Set or get current retry + * + * @private + */ +Runnable.prototype.currentRetry = function(n) { + if (!arguments.length) { + return this._currentRetry; + } + this._currentRetry = n; +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @memberof Mocha.Runnable + * @public + * @return {string} + */ +Runnable.prototype.fullTitle = function() { + return this.titlePath().join(' '); +}; + +/** + * Return the title path generated by concatenating the parent's title path with the title. + * + * @memberof Mocha.Runnable + * @public + * @return {string} + */ +Runnable.prototype.titlePath = function() { + return this.parent.titlePath().concat([this.title]); +}; + +/** + * Clear the timeout. + * + * @private + */ +Runnable.prototype.clearTimeout = function() { + clearTimeout(this.timer); +}; + +/** + * Inspect the runnable void of private properties. + * + * @private + * @return {string} + */ +Runnable.prototype.inspect = function() { + return JSON.stringify( + this, + function(key, val) { + if (key[0] === '_') { + return; + } + if (key === 'parent') { + return '#'; + } + if (key === 'ctx') { + return '#'; + } + return val; + }, + 2 + ); +}; + +/** + * Reset the timeout. + * + * @private + */ +Runnable.prototype.resetTimeout = function() { + var self = this; + var ms = this.timeout() || 1e9; + + if (!this._enableTimeouts) { + return; + } + this.clearTimeout(); + this.timer = setTimeout(function() { + if (!self._enableTimeouts) { + return; + } + self.callback(self._timeoutError(ms)); + self.timedOut = true; + }, ms); +}; + +/** + * Set or get a list of whitelisted globals for this test run. + * + * @private + * @param {string[]} globals + */ +Runnable.prototype.globals = function(globals) { + if (!arguments.length) { + return this._allowedGlobals; + } + this._allowedGlobals = globals; +}; + +/** + * Run the test and invoke `fn(err)`. + * + * @param {Function} fn + * @private + */ +Runnable.prototype.run = function(fn) { + var self = this; + var start = new Date(); + var ctx = this.ctx; + var finished; + var emitted; + + // Sometimes the ctx exists, but it is not runnable + if (ctx && ctx.runnable) { + ctx.runnable(this); + } + + // called multiple times + function multiple(err) { + if (emitted) { + return; + } + emitted = true; + var msg = 'done() called multiple times'; + if (err && err.message) { + err.message += " (and Mocha's " + msg + ')'; + self.emit('error', err); + } else { + self.emit('error', new Error(msg)); + } + } + + // finished + function done(err) { + var ms = self.timeout(); + if (self.timedOut) { + return; + } + + if (finished) { + return multiple(err); + } + + self.clearTimeout(); + self.duration = new Date() - start; + finished = true; + if (!err && self.duration > ms && self._enableTimeouts) { + err = self._timeoutError(ms); + } + fn(err); + } + + // for .resetTimeout() + this.callback = done; + + // explicit async with `done` argument + if (this.async) { + this.resetTimeout(); + + // allows skip() to be used in an explicit async context + this.skip = function asyncSkip() { + done(new Pending('async skip call')); + // halt execution. the Runnable will be marked pending + // by the previous call, and the uncaught handler will ignore + // the failure. + throw new Pending('async skip; aborting execution'); + }; + + if (this.allowUncaught) { + return callFnAsync(this.fn); + } + try { + callFnAsync(this.fn); + } catch (err) { + emitted = true; + done(Runnable.toValueOrError(err)); + } + return; + } + + if (this.allowUncaught) { + if (this.isPending()) { + done(); + } else { + callFn(this.fn); + } + return; + } + + // sync or promise-returning + try { + if (this.isPending()) { + done(); + } else { + callFn(this.fn); + } + } catch (err) { + emitted = true; + done(Runnable.toValueOrError(err)); + } + + function callFn(fn) { + var result = fn.call(ctx); + if (result && typeof result.then === 'function') { + self.resetTimeout(); + result.then( + function() { + done(); + // Return null so libraries like bluebird do not warn about + // subsequently constructed Promises. + return null; + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')); + } + ); + } else { + if (self.asyncOnly) { + return done( + new Error( + '--async-only option in use without declaring `done()` or returning a promise' + ) + ); + } + + done(); + } + } + + function callFnAsync(fn) { + var result = fn.call(ctx, function(err) { + if (err instanceof Error || toString.call(err) === '[object Error]') { + return done(err); + } + if (err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done( + new Error('done() invoked with non-Error: ' + JSON.stringify(err)) + ); + } + return done(new Error('done() invoked with non-Error: ' + err)); + } + if (result && utils.isPromise(result)) { + return done( + new Error( + 'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.' + ) + ); + } + + done(); + }); + } +}; + +/** + * Instantiates a "timeout" error + * + * @param {number} ms - Timeout (in milliseconds) + * @returns {Error} a "timeout" error + * @private + */ +Runnable.prototype._timeoutError = function(ms) { + var msg = + 'Timeout of ' + + ms + + 'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.'; + if (this.file) { + msg += ' (' + this.file + ')'; + } + return new Error(msg); +}; + +var constants = utils.defineConstants( + /** + * {@link Runnable}-related constants. + * @public + * @memberof Runnable + * @readonly + * @static + * @alias constants + * @enum {string} + */ + { + /** + * Value of `state` prop when a `Runnable` has failed + */ + STATE_FAILED: 'failed', + /** + * Value of `state` prop when a `Runnable` has passed + */ + STATE_PASSED: 'passed' + } +); + +/** + * Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that. + * @param {*} [value] - Value to return, if present + * @returns {*|Error} `value`, otherwise an `Error` + * @private + */ +Runnable.toValueOrError = function(value) { + return ( + value || + createInvalidExceptionError( + 'Runnable failed with falsy or undefined exception. Please throw an Error instead.', + value + ) + ); +}; + +Runnable.constants = constants; diff --git a/WebServer/node_modules/mocha/lib/runner.js b/WebServer/node_modules/mocha/lib/runner.js new file mode 100644 index 000000000..e41f6c5f9 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/runner.js @@ -0,0 +1,1049 @@ +'use strict'; + +/** + * Module dependencies. + */ +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var Pending = require('./pending'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:runner'); +var Runnable = require('./runnable'); +var Suite = require('./suite'); +var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH; +var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH; +var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL; +var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL; +var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN; +var STATE_FAILED = Runnable.constants.STATE_FAILED; +var STATE_PASSED = Runnable.constants.STATE_PASSED; +var dQuote = utils.dQuote; +var ngettext = utils.ngettext; +var sQuote = utils.sQuote; +var stackFilter = utils.stackTraceFilter(); +var stringify = utils.stringify; +var type = utils.type; +var createInvalidExceptionError = require('./errors') + .createInvalidExceptionError; + +/** + * Non-enumerable globals. + * @readonly + */ +var globals = [ + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'XMLHttpRequest', + 'Date', + 'setImmediate', + 'clearImmediate' +]; + +var constants = utils.defineConstants( + /** + * {@link Runner}-related constants. + * @public + * @memberof Runner + * @readonly + * @alias constants + * @static + * @enum {string} + */ + { + /** + * Emitted when {@link Hook} execution begins + */ + EVENT_HOOK_BEGIN: 'hook', + /** + * Emitted when {@link Hook} execution ends + */ + EVENT_HOOK_END: 'hook end', + /** + * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution) + */ + EVENT_RUN_BEGIN: 'start', + /** + * Emitted when Root {@link Suite} execution has been delayed via `delay` option + */ + EVENT_DELAY_BEGIN: 'waiting', + /** + * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()` + */ + EVENT_DELAY_END: 'ready', + /** + * Emitted when Root {@link Suite} execution ends + */ + EVENT_RUN_END: 'end', + /** + * Emitted when {@link Suite} execution begins + */ + EVENT_SUITE_BEGIN: 'suite', + /** + * Emitted when {@link Suite} execution ends + */ + EVENT_SUITE_END: 'suite end', + /** + * Emitted when {@link Test} execution begins + */ + EVENT_TEST_BEGIN: 'test', + /** + * Emitted when {@link Test} execution ends + */ + EVENT_TEST_END: 'test end', + /** + * Emitted when {@link Test} execution fails + */ + EVENT_TEST_FAIL: 'fail', + /** + * Emitted when {@link Test} execution succeeds + */ + EVENT_TEST_PASS: 'pass', + /** + * Emitted when {@link Test} becomes pending + */ + EVENT_TEST_PENDING: 'pending', + /** + * Emitted when {@link Test} execution has failed, but will retry + */ + EVENT_TEST_RETRY: 'retry' + } +); + +module.exports = Runner; + +/** + * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}. + * + * @extends external:EventEmitter + * @public + * @class + * @param {Suite} suite Root suite + * @param {boolean} [delay] Whether or not to delay execution of root suite + * until ready. + */ +function Runner(suite, delay) { + var self = this; + this._globals = []; + this._abort = false; + this._delay = delay; + this.suite = suite; + this.started = false; + this.total = suite.total(); + this.failures = 0; + this.on(constants.EVENT_TEST_END, function(test) { + self.checkGlobals(test); + }); + this.on(constants.EVENT_HOOK_END, function(hook) { + self.checkGlobals(hook); + }); + this._defaultGrep = /.*/; + this.grep(this._defaultGrep); + this.globals(this.globalProps().concat(extraGlobals())); +} + +/** + * Wrapper for setImmediate, process.nextTick, or browser polyfill. + * + * @param {Function} fn + * @private + */ +Runner.immediately = global.setImmediate || process.nextTick; + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Runner, EventEmitter); + +/** + * Run tests with full titles matching `re`. Updates runner.total + * with number of tests matched. + * + * @public + * @memberof Runner + * @param {RegExp} re + * @param {boolean} invert + * @return {Runner} Runner instance. + */ +Runner.prototype.grep = function(re, invert) { + debug('grep %s', re); + this._grep = re; + this._invert = invert; + this.total = this.grepTotal(this.suite); + return this; +}; + +/** + * Returns the number of tests matching the grep search for the + * given suite. + * + * @memberof Runner + * @public + * @param {Suite} suite + * @return {number} + */ +Runner.prototype.grepTotal = function(suite) { + var self = this; + var total = 0; + + suite.eachTest(function(test) { + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (match) { + total++; + } + }); + + return total; +}; + +/** + * Return a list of global properties. + * + * @return {Array} + * @private + */ +Runner.prototype.globalProps = function() { + var props = Object.keys(global); + + // non-enumerables + for (var i = 0; i < globals.length; ++i) { + if (~props.indexOf(globals[i])) { + continue; + } + props.push(globals[i]); + } + + return props; +}; + +/** + * Allow the given `arr` of globals. + * + * @public + * @memberof Runner + * @param {Array} arr + * @return {Runner} Runner instance. + */ +Runner.prototype.globals = function(arr) { + if (!arguments.length) { + return this._globals; + } + debug('globals %j', arr); + this._globals = this._globals.concat(arr); + return this; +}; + +/** + * Check for global variable leaks. + * + * @private + */ +Runner.prototype.checkGlobals = function(test) { + if (this.ignoreLeaks) { + return; + } + var ok = this._globals; + + var globals = this.globalProps(); + var leaks; + + if (test) { + ok = ok.concat(test._allowedGlobals || []); + } + + if (this.prevGlobalsLength === globals.length) { + return; + } + this.prevGlobalsLength = globals.length; + + leaks = filterLeaks(ok, globals); + this._globals = this._globals.concat(leaks); + + if (leaks.length) { + var format = ngettext( + leaks.length, + 'global leak detected: %s', + 'global leaks detected: %s' + ); + var error = new Error(util.format(format, leaks.map(sQuote).join(', '))); + this.fail(test, error); + } +}; + +/** + * Fail the given `test`. + * + * @private + * @param {Test} test + * @param {Error} err + */ +Runner.prototype.fail = function(test, err) { + if (test.isPending()) { + return; + } + + ++this.failures; + test.state = STATE_FAILED; + + if (!isError(err)) { + err = thrown2Error(err); + } + + try { + err.stack = + this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack); + } catch (ignore) { + // some environments do not take kindly to monkeying with the stack + } + + this.emit(constants.EVENT_TEST_FAIL, test, err); +}; + +/** + * Fail the given `hook` with `err`. + * + * Hook failures work in the following pattern: + * - If bail, run corresponding `after each` and `after` hooks, + * then exit + * - Failed `before` hook skips all tests in a suite and subsuites, + * but jumps to corresponding `after` hook + * - Failed `before each` hook skips remaining tests in a + * suite and jumps to corresponding `after each` hook, + * which is run only once + * - Failed `after` hook does not alter + * execution order + * - Failed `after each` hook skips remaining tests in a + * suite and subsuites, but executes other `after each` + * hooks + * + * @private + * @param {Hook} hook + * @param {Error} err + */ +Runner.prototype.failHook = function(hook, err) { + hook.originalTitle = hook.originalTitle || hook.title; + if (hook.ctx && hook.ctx.currentTest) { + hook.title = + hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title); + } else { + var parentTitle; + if (hook.parent.title) { + parentTitle = hook.parent.title; + } else { + parentTitle = hook.parent.root ? '{root}' : ''; + } + hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle); + } + + this.fail(hook, err); +}; + +/** + * Run hook `name` callbacks and then invoke `fn()`. + * + * @private + * @param {string} name + * @param {Function} fn + */ + +Runner.prototype.hook = function(name, fn) { + var suite = this.suite; + var hooks = suite.getHooks(name); + var self = this; + + function next(i) { + var hook = hooks[i]; + if (!hook) { + return fn(); + } + self.currentRunnable = hook; + + if (name === HOOK_TYPE_BEFORE_ALL) { + hook.ctx.currentTest = hook.parent.tests[0]; + } else if (name === HOOK_TYPE_AFTER_ALL) { + hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1]; + } else { + hook.ctx.currentTest = self.test; + } + + hook.allowUncaught = self.allowUncaught; + + self.emit(constants.EVENT_HOOK_BEGIN, hook); + + if (!hook.listeners('error').length) { + hook.on('error', function(err) { + self.failHook(hook, err); + }); + } + + hook.run(function(err) { + var testError = hook.error(); + if (testError) { + self.fail(self.test, testError); + } + if (err) { + if (err instanceof Pending) { + if (name === HOOK_TYPE_AFTER_ALL) { + utils.deprecate( + 'Skipping a test within an "after all" hook is DEPRECATED and will throw an exception in a future version of Mocha. ' + + 'Use a return statement or other means to abort hook execution.' + ); + } + if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) { + if (self.test) { + self.test.pending = true; + } + } else { + suite.tests.forEach(function(test) { + test.pending = true; + }); + suite.suites.forEach(function(suite) { + suite.pending = true; + }); + // a pending hook won't be executed twice. + hook.pending = true; + } + } else { + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); + } + } + self.emit(constants.EVENT_HOOK_END, hook); + delete hook.ctx.currentTest; + next(++i); + }); + } + + Runner.immediately(function() { + next(0); + }); +}; + +/** + * Run hook `name` for the given array of `suites` + * in order, and callback `fn(err, errSuite)`. + * + * @private + * @param {string} name + * @param {Array} suites + * @param {Function} fn + */ +Runner.prototype.hooks = function(name, suites, fn) { + var self = this; + var orig = this.suite; + + function next(suite) { + self.suite = suite; + + if (!suite) { + self.suite = orig; + return fn(); + } + + self.hook(name, function(err) { + if (err) { + var errSuite = self.suite; + self.suite = orig; + return fn(err, errSuite); + } + + next(suites.pop()); + }); + } + + next(suites.pop()); +}; + +/** + * Run hooks from the top level down. + * + * @param {String} name + * @param {Function} fn + * @private + */ +Runner.prototype.hookUp = function(name, fn) { + var suites = [this.suite].concat(this.parents()).reverse(); + this.hooks(name, suites, fn); +}; + +/** + * Run hooks from the bottom up. + * + * @param {String} name + * @param {Function} fn + * @private + */ +Runner.prototype.hookDown = function(name, fn) { + var suites = [this.suite].concat(this.parents()); + this.hooks(name, suites, fn); +}; + +/** + * Return an array of parent Suites from + * closest to furthest. + * + * @return {Array} + * @private + */ +Runner.prototype.parents = function() { + var suite = this.suite; + var suites = []; + while (suite.parent) { + suite = suite.parent; + suites.push(suite); + } + return suites; +}; + +/** + * Run the current test and callback `fn(err)`. + * + * @param {Function} fn + * @private + */ +Runner.prototype.runTest = function(fn) { + var self = this; + var test = this.test; + + if (!test) { + return; + } + + var suite = this.parents().reverse()[0] || this.suite; + if (this.forbidOnly && suite.hasOnly()) { + fn(new Error('`.only` forbidden')); + return; + } + if (this.asyncOnly) { + test.asyncOnly = true; + } + test.on('error', function(err) { + self.fail(test, err); + }); + if (this.allowUncaught) { + test.allowUncaught = true; + return test.run(fn); + } + try { + test.run(fn); + } catch (err) { + fn(err); + } +}; + +/** + * Run tests in the given `suite` and invoke the callback `fn()` when complete. + * + * @private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runTests = function(suite, fn) { + var self = this; + var tests = suite.tests.slice(); + var test; + + function hookErr(_, errSuite, after) { + // before/after Each hook for errSuite failed: + var orig = self.suite; + + // for failed 'after each' hook start from errSuite parent, + // otherwise start from errSuite itself + self.suite = after ? errSuite.parent : errSuite; + + if (self.suite) { + // call hookUp afterEach + self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) { + self.suite = orig; + // some hooks may fail even now + if (err2) { + return hookErr(err2, errSuite2, true); + } + // report error suite + fn(errSuite); + }); + } else { + // there is no need calling other 'after each' hooks + self.suite = orig; + fn(errSuite); + } + } + + function next(err, errSuite) { + // if we bail after first err + if (self.failures && suite._bail) { + tests = []; + } + + if (self._abort) { + return fn(); + } + + if (err) { + return hookErr(err, errSuite, true); + } + + // next test + test = tests.shift(); + + // all done + if (!test) { + return fn(); + } + + // grep + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (!match) { + // Run immediately only if we have defined a grep. When we + // define a grep — It can cause maximum callstack error if + // the grep is doing a large recursive loop by neglecting + // all tests. The run immediately function also comes with + // a performance cost. So we don't want to run immediately + // if we run the whole test suite, because running the whole + // test suite don't do any immediate recursive loops. Thus, + // allowing a JS runtime to breathe. + if (self._grep !== self._defaultGrep) { + Runner.immediately(next); + } else { + next(); + } + return; + } + + if (test.isPending()) { + if (self.forbidPending) { + test.isPending = alwaysFalse; + self.fail(test, new Error('Pending test forbidden')); + delete test.isPending; + } else { + self.emit(constants.EVENT_TEST_PENDING, test); + } + self.emit(constants.EVENT_TEST_END, test); + return next(); + } + + // execute test and hook(s) + self.emit(constants.EVENT_TEST_BEGIN, (self.test = test)); + self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) { + if (test.isPending()) { + if (self.forbidPending) { + test.isPending = alwaysFalse; + self.fail(test, new Error('Pending test forbidden')); + delete test.isPending; + } else { + self.emit(constants.EVENT_TEST_PENDING, test); + } + self.emit(constants.EVENT_TEST_END, test); + return next(); + } + if (err) { + return hookErr(err, errSuite, false); + } + self.currentRunnable = self.test; + self.runTest(function(err) { + test = self.test; + if (err) { + var retry = test.currentRetry(); + if (err instanceof Pending && self.forbidPending) { + self.fail(test, new Error('Pending test forbidden')); + } else if (err instanceof Pending) { + test.pending = true; + self.emit(constants.EVENT_TEST_PENDING, test); + } else if (retry < test.retries()) { + var clonedTest = test.clone(); + clonedTest.currentRetry(retry + 1); + tests.unshift(clonedTest); + + self.emit(constants.EVENT_TEST_RETRY, test, err); + + // Early return + hook trigger so that it doesn't + // increment the count wrong + return self.hookUp(HOOK_TYPE_AFTER_EACH, next); + } else { + self.fail(test, err); + } + self.emit(constants.EVENT_TEST_END, test); + + if (err instanceof Pending) { + return next(); + } + + return self.hookUp(HOOK_TYPE_AFTER_EACH, next); + } + + test.state = STATE_PASSED; + self.emit(constants.EVENT_TEST_PASS, test); + self.emit(constants.EVENT_TEST_END, test); + self.hookUp(HOOK_TYPE_AFTER_EACH, next); + }); + }); + } + + this.next = next; + this.hookErr = hookErr; + next(); +}; + +function alwaysFalse() { + return false; +} + +/** + * Run the given `suite` and invoke the callback `fn()` when complete. + * + * @private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runSuite = function(suite, fn) { + var i = 0; + var self = this; + var total = this.grepTotal(suite); + var afterAllHookCalled = false; + + debug('run suite %s', suite.fullTitle()); + + if (!total || (self.failures && suite._bail)) { + return fn(); + } + + this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite)); + + function next(errSuite) { + if (errSuite) { + // current suite failed on a hook from errSuite + if (errSuite === suite) { + // if errSuite is current suite + // continue to the next sibling suite + return done(); + } + // errSuite is among the parents of current suite + // stop execution of errSuite and all sub-suites + return done(errSuite); + } + + if (self._abort) { + return done(); + } + + var curr = suite.suites[i++]; + if (!curr) { + return done(); + } + + // Avoid grep neglecting large number of tests causing a + // huge recursive loop and thus a maximum call stack error. + // See comment in `this.runTests()` for more information. + if (self._grep !== self._defaultGrep) { + Runner.immediately(function() { + self.runSuite(curr, next); + }); + } else { + self.runSuite(curr, next); + } + } + + function done(errSuite) { + self.suite = suite; + self.nextSuite = next; + + if (afterAllHookCalled) { + fn(errSuite); + } else { + // mark that the afterAll block has been called once + // and so can be skipped if there is an error in it. + afterAllHookCalled = true; + + // remove reference to test + delete self.test; + + self.hook(HOOK_TYPE_AFTER_ALL, function() { + self.emit(constants.EVENT_SUITE_END, suite); + fn(errSuite); + }); + } + } + + this.nextSuite = next; + + this.hook(HOOK_TYPE_BEFORE_ALL, function(err) { + if (err) { + return done(); + } + self.runTests(suite, next); + }); +}; + +/** + * Handle uncaught exceptions. + * + * @param {Error} err + * @private + */ +Runner.prototype.uncaught = function(err) { + if (err instanceof Pending) { + return; + } + if (err) { + debug('uncaught exception %O', err); + } else { + debug('uncaught undefined/falsy exception'); + err = createInvalidExceptionError( + 'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger', + err + ); + } + + if (!isError(err)) { + err = thrown2Error(err); + } + err.uncaught = true; + + var runnable = this.currentRunnable; + + if (!runnable) { + runnable = new Runnable('Uncaught error outside test suite'); + runnable.parent = this.suite; + + if (this.started) { + this.fail(runnable, err); + } else { + // Can't recover from this failure + this.emit(constants.EVENT_RUN_BEGIN); + this.fail(runnable, err); + this.emit(constants.EVENT_RUN_END); + } + + return; + } + + runnable.clearTimeout(); + + // Ignore errors if already failed or pending + // See #3226 + if (runnable.isFailed() || runnable.isPending()) { + return; + } + // we cannot recover gracefully if a Runnable has already passed + // then fails asynchronously + var alreadyPassed = runnable.isPassed(); + // this will change the state to "failed" regardless of the current value + this.fail(runnable, err); + if (!alreadyPassed) { + // recover from test + if (runnable.type === constants.EVENT_TEST_BEGIN) { + this.emit(constants.EVENT_TEST_END, runnable); + this.hookUp(HOOK_TYPE_AFTER_EACH, this.next); + return; + } + debug(runnable); + + // recover from hooks + var errSuite = this.suite; + + // XXX how about a less awful way to determine this? + // if hook failure is in afterEach block + if (runnable.fullTitle().indexOf('after each') > -1) { + return this.hookErr(err, errSuite, true); + } + // if hook failure is in beforeEach block + if (runnable.fullTitle().indexOf('before each') > -1) { + return this.hookErr(err, errSuite, false); + } + // if hook failure is in after or before blocks + return this.nextSuite(errSuite); + } + + // bail + this.emit(constants.EVENT_RUN_END); +}; + +/** + * Run the root suite and invoke `fn(failures)` + * on completion. + * + * @public + * @memberof Runner + * @param {Function} fn + * @return {Runner} Runner instance. + */ +Runner.prototype.run = function(fn) { + var self = this; + var rootSuite = this.suite; + + fn = fn || function() {}; + + function uncaught(err) { + self.uncaught(err); + } + + function start() { + // If there is an `only` filter + if (rootSuite.hasOnly()) { + rootSuite.filterOnly(); + } + self.started = true; + if (self._delay) { + self.emit(constants.EVENT_DELAY_END); + } + self.emit(constants.EVENT_RUN_BEGIN); + + self.runSuite(rootSuite, function() { + debug('finished running'); + self.emit(constants.EVENT_RUN_END); + }); + } + + debug(constants.EVENT_RUN_BEGIN); + + // references cleanup to avoid memory leaks + this.on(constants.EVENT_SUITE_END, function(suite) { + suite.cleanReferences(); + }); + + // callback + this.on(constants.EVENT_RUN_END, function() { + debug(constants.EVENT_RUN_END); + process.removeListener('uncaughtException', uncaught); + fn(self.failures); + }); + + // uncaught exception + process.on('uncaughtException', uncaught); + + if (this._delay) { + // for reporters, I guess. + // might be nice to debounce some dots while we wait. + this.emit(constants.EVENT_DELAY_BEGIN, rootSuite); + rootSuite.once(EVENT_ROOT_SUITE_RUN, start); + } else { + start(); + } + + return this; +}; + +/** + * Cleanly abort execution. + * + * @memberof Runner + * @public + * @return {Runner} Runner instance. + */ +Runner.prototype.abort = function() { + debug('aborting'); + this._abort = true; + + return this; +}; + +/** + * Filter leaks with the given globals flagged as `ok`. + * + * @private + * @param {Array} ok + * @param {Array} globals + * @return {Array} + */ +function filterLeaks(ok, globals) { + return globals.filter(function(key) { + // Firefox and Chrome exposes iframes as index inside the window object + if (/^\d+/.test(key)) { + return false; + } + + // in firefox + // if runner runs in an iframe, this iframe's window.getInterface method + // not init at first it is assigned in some seconds + if (global.navigator && /^getInterface/.test(key)) { + return false; + } + + // an iframe could be approached by window[iframeIndex] + // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak + if (global.navigator && /^\d+/.test(key)) { + return false; + } + + // Opera and IE expose global variables for HTML element IDs (issue #243) + if (/^mocha-/.test(key)) { + return false; + } + + var matched = ok.filter(function(ok) { + if (~ok.indexOf('*')) { + return key.indexOf(ok.split('*')[0]) === 0; + } + return key === ok; + }); + return !matched.length && (!global.navigator || key !== 'onerror'); + }); +} + +/** + * Check if argument is an instance of Error object or a duck-typed equivalent. + * + * @private + * @param {Object} err - object to check + * @param {string} err.message - error message + * @returns {boolean} + */ +function isError(err) { + return err instanceof Error || (err && typeof err.message === 'string'); +} + +/** + * + * Converts thrown non-extensible type into proper Error. + * + * @private + * @param {*} thrown - Non-extensible type thrown by code + * @return {Error} + */ +function thrown2Error(err) { + return new Error( + 'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)' + ); +} + +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @deprecated + * @todo remove; long since unsupported + * @private + */ +function extraGlobals() { + if (typeof process === 'object' && typeof process.version === 'string') { + var parts = process.version.split('.'); + var nodeVersion = parts.reduce(function(a, v) { + return (a << 8) | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + if (nodeVersion < 0x00090b) { + return ['errno']; + } + } + + return []; +} + +Runner.constants = constants; + +/** + * Node.js' `EventEmitter` + * @external EventEmitter + * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter} + */ diff --git a/WebServer/node_modules/mocha/lib/stats-collector.js b/WebServer/node_modules/mocha/lib/stats-collector.js new file mode 100644 index 000000000..938778fb0 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/stats-collector.js @@ -0,0 +1,83 @@ +'use strict'; + +/** + * Provides a factory function for a {@link StatsCollector} object. + * @module + */ + +var constants = require('./runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_END = constants.EVENT_TEST_END; + +/** + * Test statistics collector. + * + * @public + * @typedef {Object} StatsCollector + * @property {number} suites - integer count of suites run. + * @property {number} tests - integer count of tests run. + * @property {number} passes - integer count of passing tests. + * @property {number} pending - integer count of pending tests. + * @property {number} failures - integer count of failed tests. + * @property {Date} start - time when testing began. + * @property {Date} end - time when testing concluded. + * @property {number} duration - number of msecs that testing took. + */ + +var Date = global.Date; + +/** + * Provides stats such as test duration, number of tests passed / failed etc., by listening for events emitted by `runner`. + * + * @private + * @param {Runner} runner - Runner instance + * @throws {TypeError} If falsy `runner` + */ +function createStatsCollector(runner) { + /** + * @type StatsCollector + */ + var stats = { + suites: 0, + tests: 0, + passes: 0, + pending: 0, + failures: 0 + }; + + if (!runner) { + throw new TypeError('Missing runner argument'); + } + + runner.stats = stats; + + runner.once(EVENT_RUN_BEGIN, function() { + stats.start = new Date(); + }); + runner.on(EVENT_SUITE_BEGIN, function(suite) { + suite.root || stats.suites++; + }); + runner.on(EVENT_TEST_PASS, function() { + stats.passes++; + }); + runner.on(EVENT_TEST_FAIL, function() { + stats.failures++; + }); + runner.on(EVENT_TEST_PENDING, function() { + stats.pending++; + }); + runner.on(EVENT_TEST_END, function() { + stats.tests++; + }); + runner.once(EVENT_RUN_END, function() { + stats.end = new Date(); + stats.duration = stats.end - stats.start; + }); +} + +module.exports = createStatsCollector; diff --git a/WebServer/node_modules/mocha/lib/suite.js b/WebServer/node_modules/mocha/lib/suite.js new file mode 100644 index 000000000..191d946b5 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/suite.js @@ -0,0 +1,642 @@ +'use strict'; + +/** + * Module dependencies. + */ +var EventEmitter = require('events').EventEmitter; +var Hook = require('./hook'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:suite'); +var milliseconds = require('ms'); +var errors = require('./errors'); +var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; + +/** + * Expose `Suite`. + */ + +exports = module.exports = Suite; + +/** + * Create a new `Suite` with the given `title` and parent `Suite`. + * + * @public + * @param {Suite} parent - Parent suite (required!) + * @param {string} title - Title + * @return {Suite} + */ +Suite.create = function(parent, title) { + var suite = new Suite(title, parent.ctx); + suite.parent = parent; + title = suite.fullTitle(); + parent.addSuite(suite); + return suite; +}; + +/** + * Constructs a new `Suite` instance with the given `title`, `ctx`, and `isRoot`. + * + * @public + * @class + * @extends EventEmitter + * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter} + * @param {string} title - Suite title. + * @param {Context} parentContext - Parent context instance. + * @param {boolean} [isRoot=false] - Whether this is the root suite. + */ +function Suite(title, parentContext, isRoot) { + if (!utils.isString(title)) { + throw createInvalidArgumentTypeError( + 'Suite argument "title" must be a string. Received type "' + + typeof title + + '"', + 'title', + 'string' + ); + } + this.title = title; + function Context() {} + Context.prototype = parentContext; + this.ctx = new Context(); + this.suites = []; + this.tests = []; + this.pending = false; + this._beforeEach = []; + this._beforeAll = []; + this._afterEach = []; + this._afterAll = []; + this.root = isRoot === true; + this._timeout = 2000; + this._enableTimeouts = true; + this._slow = 75; + this._bail = false; + this._retries = -1; + this._onlyTests = []; + this._onlySuites = []; + this.delayed = false; + + this.on('newListener', function(event) { + if (deprecatedEvents[event]) { + utils.deprecate( + 'Event "' + + event + + '" is deprecated. Please let the Mocha team know about your use case: https://git.io/v6Lwm' + ); + } + }); +} + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Suite, EventEmitter); + +/** + * Return a clone of this `Suite`. + * + * @private + * @return {Suite} + */ +Suite.prototype.clone = function() { + var suite = new Suite(this.title); + debug('clone'); + suite.ctx = this.ctx; + suite.root = this.root; + suite.timeout(this.timeout()); + suite.retries(this.retries()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + return suite; +}; + +/** + * Set or get timeout `ms` or short-hand such as "2s". + * + * @private + * @todo Do not attempt to set value if `ms` is undefined + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (ms.toString() === '0') { + this._enableTimeouts = false; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._timeout = parseInt(ms, 10); + return this; +}; + +/** + * Set or get number of times to retry a failed test. + * + * @private + * @param {number|string} n + * @return {Suite|number} for chaining + */ +Suite.prototype.retries = function(n) { + if (!arguments.length) { + return this._retries; + } + debug('retries %d', n); + this._retries = parseInt(n, 10) || 0; + return this; +}; + +/** + * Set or get timeout to `enabled`. + * + * @private + * @param {boolean} enabled + * @return {Suite|boolean} self or enabled + */ +Suite.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Set or get slow `ms` or short-hand such as "2s". + * + * @private + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.slow = function(ms) { + if (!arguments.length) { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set or get whether to bail after first error. + * + * @private + * @param {boolean} bail + * @return {Suite|number} for chaining + */ +Suite.prototype.bail = function(bail) { + if (!arguments.length) { + return this._bail; + } + debug('bail %s', bail); + this._bail = bail; + return this; +}; + +/** + * Check if this suite or its parent suite is marked as pending. + * + * @private + */ +Suite.prototype.isPending = function() { + return this.pending || (this.parent && this.parent.isPending()); +}; + +/** + * Generic hook-creator. + * @private + * @param {string} title - Title of hook + * @param {Function} fn - Hook callback + * @returns {Hook} A new hook + */ +Suite.prototype._createHook = function(title, fn) { + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.retries(this.retries()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + hook.file = this.file; + return hook; +}; + +/** + * Run `fn(test[, done])` before running tests. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeAll = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before all" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._beforeAll.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook); + return this; +}; + +/** + * Run `fn(test[, done])` after running tests. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterAll = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after all" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._afterAll.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook); + return this; +}; + +/** + * Run `fn(test[, done])` before each test case. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeEach = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before each" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._beforeEach.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook); + return this; +}; + +/** + * Run `fn(test[, done])` after each test case. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterEach = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after each" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._afterEach.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook); + return this; +}; + +/** + * Add a test `suite`. + * + * @private + * @param {Suite} suite + * @return {Suite} for chaining + */ +Suite.prototype.addSuite = function(suite) { + suite.parent = this; + suite.root = false; + suite.timeout(this.timeout()); + suite.retries(this.retries()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + this.suites.push(suite); + this.emit(constants.EVENT_SUITE_ADD_SUITE, suite); + return this; +}; + +/** + * Add a `test` to this suite. + * + * @private + * @param {Test} test + * @return {Suite} for chaining + */ +Suite.prototype.addTest = function(test) { + test.parent = this; + test.timeout(this.timeout()); + test.retries(this.retries()); + test.enableTimeouts(this.enableTimeouts()); + test.slow(this.slow()); + test.ctx = this.ctx; + this.tests.push(test); + this.emit(constants.EVENT_SUITE_ADD_TEST, test); + return this; +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @memberof Suite + * @public + * @return {string} + */ +Suite.prototype.fullTitle = function() { + return this.titlePath().join(' '); +}; + +/** + * Return the title path generated by recursively concatenating the parent's + * title path. + * + * @memberof Suite + * @public + * @return {string} + */ +Suite.prototype.titlePath = function() { + var result = []; + if (this.parent) { + result = result.concat(this.parent.titlePath()); + } + if (!this.root) { + result.push(this.title); + } + return result; +}; + +/** + * Return the total number of tests. + * + * @memberof Suite + * @public + * @return {number} + */ +Suite.prototype.total = function() { + return ( + this.suites.reduce(function(sum, suite) { + return sum + suite.total(); + }, 0) + this.tests.length + ); +}; + +/** + * Iterates through each suite recursively to find all tests. Applies a + * function in the format `fn(test)`. + * + * @private + * @param {Function} fn + * @return {Suite} + */ +Suite.prototype.eachTest = function(fn) { + this.tests.forEach(fn); + this.suites.forEach(function(suite) { + suite.eachTest(fn); + }); + return this; +}; + +/** + * This will run the root suite if we happen to be running in delayed mode. + * @private + */ +Suite.prototype.run = function run() { + if (this.root) { + this.emit(constants.EVENT_ROOT_SUITE_RUN); + } +}; + +/** + * Determines whether a suite has an `only` test or suite as a descendant. + * + * @private + * @returns {Boolean} + */ +Suite.prototype.hasOnly = function hasOnly() { + return ( + this._onlyTests.length > 0 || + this._onlySuites.length > 0 || + this.suites.some(function(suite) { + return suite.hasOnly(); + }) + ); +}; + +/** + * Filter suites based on `isOnly` logic. + * + * @private + * @returns {Boolean} + */ +Suite.prototype.filterOnly = function filterOnly() { + if (this._onlyTests.length) { + // If the suite contains `only` tests, run those and ignore any nested suites. + this.tests = this._onlyTests; + this.suites = []; + } else { + // Otherwise, do not run any of the tests in this suite. + this.tests = []; + this._onlySuites.forEach(function(onlySuite) { + // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite. + // Otherwise, all of the tests on this `only` suite should be run, so don't filter it. + if (onlySuite.hasOnly()) { + onlySuite.filterOnly(); + } + }); + // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants. + var onlySuites = this._onlySuites; + this.suites = this.suites.filter(function(childSuite) { + return onlySuites.indexOf(childSuite) !== -1 || childSuite.filterOnly(); + }); + } + // Keep the suite only if there is something to run + return this.tests.length > 0 || this.suites.length > 0; +}; + +/** + * Adds a suite to the list of subsuites marked `only`. + * + * @private + * @param {Suite} suite + */ +Suite.prototype.appendOnlySuite = function(suite) { + this._onlySuites.push(suite); +}; + +/** + * Adds a test to the list of tests marked `only`. + * + * @private + * @param {Test} test + */ +Suite.prototype.appendOnlyTest = function(test) { + this._onlyTests.push(test); +}; + +/** + * Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants. + * @private + */ +Suite.prototype.getHooks = function getHooks(name) { + return this['_' + name]; +}; + +/** + * Cleans up the references to all the deferred functions + * (before/after/beforeEach/afterEach) and tests of a Suite. + * These must be deleted otherwise a memory leak can happen, + * as those functions may reference variables from closures, + * thus those variables can never be garbage collected as long + * as the deferred functions exist. + * + * @private + */ +Suite.prototype.cleanReferences = function cleanReferences() { + function cleanArrReferences(arr) { + for (var i = 0; i < arr.length; i++) { + delete arr[i].fn; + } + } + + if (Array.isArray(this._beforeAll)) { + cleanArrReferences(this._beforeAll); + } + + if (Array.isArray(this._beforeEach)) { + cleanArrReferences(this._beforeEach); + } + + if (Array.isArray(this._afterAll)) { + cleanArrReferences(this._afterAll); + } + + if (Array.isArray(this._afterEach)) { + cleanArrReferences(this._afterEach); + } + + for (var i = 0; i < this.tests.length; i++) { + delete this.tests[i].fn; + } +}; + +var constants = utils.defineConstants( + /** + * {@link Suite}-related constants. + * @public + * @memberof Suite + * @alias constants + * @readonly + * @static + * @enum {string} + */ + { + /** + * Event emitted after a test file has been loaded Not emitted in browser. + */ + EVENT_FILE_POST_REQUIRE: 'post-require', + /** + * Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected. + */ + EVENT_FILE_PRE_REQUIRE: 'pre-require', + /** + * Event emitted immediately after a test file has been loaded. Not emitted in browser. + */ + EVENT_FILE_REQUIRE: 'require', + /** + * Event emitted when `global.run()` is called (use with `delay` option) + */ + EVENT_ROOT_SUITE_RUN: 'run', + + /** + * Namespace for collection of a `Suite`'s "after all" hooks + */ + HOOK_TYPE_AFTER_ALL: 'afterAll', + /** + * Namespace for collection of a `Suite`'s "after each" hooks + */ + HOOK_TYPE_AFTER_EACH: 'afterEach', + /** + * Namespace for collection of a `Suite`'s "before all" hooks + */ + HOOK_TYPE_BEFORE_ALL: 'beforeAll', + /** + * Namespace for collection of a `Suite`'s "before all" hooks + */ + HOOK_TYPE_BEFORE_EACH: 'beforeEach', + + // the following events are all deprecated + + /** + * Emitted after an "after all" `Hook` has been added to a `Suite`. Deprecated + */ + EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll', + /** + * Emitted after an "after each" `Hook` has been added to a `Suite` Deprecated + */ + EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach', + /** + * Emitted after an "before all" `Hook` has been added to a `Suite` Deprecated + */ + EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll', + /** + * Emitted after an "before each" `Hook` has been added to a `Suite` Deprecated + */ + EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach', + /** + * Emitted after a child `Suite` has been added to a `Suite`. Deprecated + */ + EVENT_SUITE_ADD_SUITE: 'suite', + /** + * Emitted after a `Test` has been added to a `Suite`. Deprecated + */ + EVENT_SUITE_ADD_TEST: 'test' + } +); + +/** + * @summary There are no known use cases for these events. + * @desc This is a `Set`-like object having all keys being the constant's string value and the value being `true`. + * @todo Remove eventually + * @type {Object} + * @ignore + */ +var deprecatedEvents = Object.keys(constants) + .filter(function(constant) { + return constant.substring(0, 15) === 'EVENT_SUITE_ADD'; + }) + .reduce(function(acc, constant) { + acc[constants[constant]] = true; + return acc; + }, utils.createMap()); + +Suite.constants = constants; diff --git a/WebServer/node_modules/mocha/lib/test.js b/WebServer/node_modules/mocha/lib/test.js new file mode 100644 index 000000000..f32008a85 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/test.js @@ -0,0 +1,51 @@ +'use strict'; +var Runnable = require('./runnable'); +var utils = require('./utils'); +var errors = require('./errors'); +var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; +var isString = utils.isString; + +module.exports = Test; + +/** + * Initialize a new `Test` with the given `title` and callback `fn`. + * + * @public + * @class + * @extends Runnable + * @param {String} title - Test title (required) + * @param {Function} [fn] - Test callback. If omitted, the Test is considered "pending" + */ +function Test(title, fn) { + if (!isString(title)) { + throw createInvalidArgumentTypeError( + 'Test argument "title" should be a string. Received type "' + + typeof title + + '"', + 'title', + 'string' + ); + } + Runnable.call(this, title, fn); + this.pending = !fn; + this.type = 'test'; +} + +/** + * Inherit from `Runnable.prototype`. + */ +utils.inherits(Test, Runnable); + +Test.prototype.clone = function() { + var test = new Test(this.title, this.fn); + test.timeout(this.timeout()); + test.slow(this.slow()); + test.enableTimeouts(this.enableTimeouts()); + test.retries(this.retries()); + test.currentRetry(this.currentRetry()); + test.globals(this.globals()); + test.parent = this.parent; + test.file = this.file; + test.ctx = this.ctx; + return test; +}; diff --git a/WebServer/node_modules/mocha/lib/utils.js b/WebServer/node_modules/mocha/lib/utils.js new file mode 100644 index 000000000..86ba8f037 --- /dev/null +++ b/WebServer/node_modules/mocha/lib/utils.js @@ -0,0 +1,897 @@ +'use strict'; + +/** + * Various utility functions used throughout Mocha's codebase. + * @module utils + */ + +/** + * Module dependencies. + */ + +var fs = require('fs'); +var path = require('path'); +var util = require('util'); +var glob = require('glob'); +var he = require('he'); +var errors = require('./errors'); +var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError; +var createMissingArgumentError = errors.createMissingArgumentError; + +var assign = (exports.assign = require('object.assign').getPolyfill()); + +/** + * Inherit the prototype methods from one constructor into another. + * + * @param {function} ctor - Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor - Constructor function to inherit prototype from. + * @throws {TypeError} if either constructor is null, or if super constructor + * lacks a prototype. + */ +exports.inherits = util.inherits; + +/** + * Escape special characters in the given string of html. + * + * @private + * @param {string} html + * @return {string} + */ +exports.escape = function(html) { + return he.encode(String(html), {useNamedReferences: false}); +}; + +/** + * Test if the given obj is type of string. + * + * @private + * @param {Object} obj + * @return {boolean} + */ +exports.isString = function(obj) { + return typeof obj === 'string'; +}; + +/** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @private + * @param {Array} files + * @param {Function} fn + */ +exports.watch = function(files, fn) { + var options = {interval: 100}; + var debug = require('debug')('mocha:watch'); + files.forEach(function(file) { + debug('file %s', file); + fs.watchFile(file, options, function(curr, prev) { + if (prev.mtime < curr.mtime) { + fn(file); + } + }); + }); +}; + +/** + * Predicate to screen `pathname` for further consideration. + * + * @description + * Returns false for pathname referencing: + *
      + *
    • 'npm' package installation directory + *
    • 'git' version control directory + *
    + * + * @private + * @param {string} pathname - File or directory name to screen + * @return {boolean} whether pathname should be further considered + * @example + * ['node_modules', 'test.js'].filter(considerFurther); // => ['test.js'] + */ +function considerFurther(pathname) { + var ignore = ['node_modules', '.git']; + + return !~ignore.indexOf(pathname); +} + +/** + * Lookup files in the given `dir`. + * + * @description + * Filenames are returned in _traversal_ order by the OS/filesystem. + * **Make no assumption that the names will be sorted in any fashion.** + * + * @private + * @param {string} dir + * @param {string[]} [exts=['js']] + * @param {Array} [ret=[]] + * @return {Array} + */ +exports.files = function(dir, exts, ret) { + ret = ret || []; + exts = exts || ['js']; + + fs.readdirSync(dir) + .filter(considerFurther) + .forEach(function(dirent) { + var pathname = path.join(dir, dirent); + if (fs.lstatSync(pathname).isDirectory()) { + exports.files(pathname, exts, ret); + } else if (hasMatchingExtname(pathname, exts)) { + ret.push(pathname); + } + }); + + return ret; +}; + +/** + * Compute a slug from the given `str`. + * + * @private + * @param {string} str + * @return {string} + */ +exports.slug = function(str) { + return str + .toLowerCase() + .replace(/ +/g, '-') + .replace(/[^-\w]/g, ''); +}; + +/** + * Strip the function definition from `str`, and re-indent for pre whitespace. + * + * @param {string} str + * @return {string} + */ +exports.clean = function(str) { + str = str + .replace(/\r\n?|[\n\u2028\u2029]/g, '\n') + .replace(/^\uFEFF/, '') + // (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content + .replace( + /^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, + '$1$2$3' + ); + + var spaces = str.match(/^\n?( *)/)[1].length; + var tabs = str.match(/^\n?(\t*)/)[1].length; + var re = new RegExp( + '^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs || spaces) + '}', + 'gm' + ); + + str = str.replace(re, ''); + + return str.trim(); +}; + +/** + * Parse the given `qs`. + * + * @private + * @param {string} qs + * @return {Object} + */ +exports.parseQuery = function(qs) { + return qs + .replace('?', '') + .split('&') + .reduce(function(obj, pair) { + var i = pair.indexOf('='); + var key = pair.slice(0, i); + var val = pair.slice(++i); + + // Due to how the URLSearchParams API treats spaces + obj[key] = decodeURIComponent(val.replace(/\+/g, '%20')); + + return obj; + }, {}); +}; + +/** + * Highlight the given string of `js`. + * + * @private + * @param {string} js + * @return {string} + */ +function highlight(js) { + return js + .replace(//g, '>') + .replace(/\/\/(.*)/gm, '//$1') + .replace(/('.*?')/gm, '$1') + .replace(/(\d+\.\d+)/gm, '$1') + .replace(/(\d+)/gm, '$1') + .replace( + /\bnew[ \t]+(\w+)/gm, + 'new $1' + ) + .replace( + /\b(function|new|throw|return|var|if|else)\b/gm, + '$1' + ); +} + +/** + * Highlight the contents of tag `name`. + * + * @private + * @param {string} name + */ +exports.highlightTags = function(name) { + var code = document.getElementById('mocha').getElementsByTagName(name); + for (var i = 0, len = code.length; i < len; ++i) { + code[i].innerHTML = highlight(code[i].innerHTML); + } +}; + +/** + * If a value could have properties, and has none, this function is called, + * which returns a string representation of the empty value. + * + * Functions w/ no properties return `'[Function]'` + * Arrays w/ length === 0 return `'[]'` + * Objects w/ no properties return `'{}'` + * All else: return result of `value.toString()` + * + * @private + * @param {*} value The value to inspect. + * @param {string} typeHint The type of the value + * @returns {string} + */ +function emptyRepresentation(value, typeHint) { + switch (typeHint) { + case 'function': + return '[Function]'; + case 'object': + return '{}'; + case 'array': + return '[]'; + default: + return value.toString(); + } +} + +/** + * Takes some variable and asks `Object.prototype.toString()` what it thinks it + * is. + * + * @private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString + * @param {*} value The value to test. + * @returns {string} Computed type + * @example + * type({}) // 'object' + * type([]) // 'array' + * type(1) // 'number' + * type(false) // 'boolean' + * type(Infinity) // 'number' + * type(null) // 'null' + * type(new Date()) // 'date' + * type(/foo/) // 'regexp' + * type('type') // 'string' + * type(global) // 'global' + * type(new String('foo') // 'object' + */ +var type = (exports.type = function type(value) { + if (value === undefined) { + return 'undefined'; + } else if (value === null) { + return 'null'; + } else if (Buffer.isBuffer(value)) { + return 'buffer'; + } + return Object.prototype.toString + .call(value) + .replace(/^\[.+\s(.+?)]$/, '$1') + .toLowerCase(); +}); + +/** + * Stringify `value`. Different behavior depending on type of value: + * + * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively. + * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes. + * - If `value` is an *empty* object, function, or array, return result of function + * {@link emptyRepresentation}. + * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of + * JSON.stringify(). + * + * @private + * @see exports.type + * @param {*} value + * @return {string} + */ +exports.stringify = function(value) { + var typeHint = type(value); + + if (!~['object', 'array', 'function'].indexOf(typeHint)) { + if (typeHint === 'buffer') { + var json = Buffer.prototype.toJSON.call(value); + // Based on the toJSON result + return jsonStringify( + json.data && json.type ? json.data : json, + 2 + ).replace(/,(\n|$)/g, '$1'); + } + + // IE7/IE8 has a bizarre String constructor; needs to be coerced + // into an array and back to obj. + if (typeHint === 'string' && typeof value === 'object') { + value = value.split('').reduce(function(acc, char, idx) { + acc[idx] = char; + return acc; + }, {}); + typeHint = 'object'; + } else { + return jsonStringify(value); + } + } + + for (var prop in value) { + if (Object.prototype.hasOwnProperty.call(value, prop)) { + return jsonStringify( + exports.canonicalize(value, null, typeHint), + 2 + ).replace(/,(\n|$)/g, '$1'); + } + } + + return emptyRepresentation(value, typeHint); +}; + +/** + * like JSON.stringify but more sense. + * + * @private + * @param {Object} object + * @param {number=} spaces + * @param {number=} depth + * @returns {*} + */ +function jsonStringify(object, spaces, depth) { + if (typeof spaces === 'undefined') { + // primitive types + return _stringify(object); + } + + depth = depth || 1; + var space = spaces * depth; + var str = Array.isArray(object) ? '[' : '{'; + var end = Array.isArray(object) ? ']' : '}'; + var length = + typeof object.length === 'number' + ? object.length + : Object.keys(object).length; + // `.repeat()` polyfill + function repeat(s, n) { + return new Array(n).join(s); + } + + function _stringify(val) { + switch (type(val)) { + case 'null': + case 'undefined': + val = '[' + val + ']'; + break; + case 'array': + case 'object': + val = jsonStringify(val, spaces, depth + 1); + break; + case 'boolean': + case 'regexp': + case 'symbol': + case 'number': + val = + val === 0 && 1 / val === -Infinity // `-0` + ? '-0' + : val.toString(); + break; + case 'date': + var sDate = isNaN(val.getTime()) ? val.toString() : val.toISOString(); + val = '[Date: ' + sDate + ']'; + break; + case 'buffer': + var json = val.toJSON(); + // Based on the toJSON result + json = json.data && json.type ? json.data : json; + val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'; + break; + default: + val = + val === '[Function]' || val === '[Circular]' + ? val + : JSON.stringify(val); // string + } + return val; + } + + for (var i in object) { + if (!Object.prototype.hasOwnProperty.call(object, i)) { + continue; // not my business + } + --length; + str += + '\n ' + + repeat(' ', space) + + (Array.isArray(object) ? '' : '"' + i + '": ') + // key + _stringify(object[i]) + // value + (length ? ',' : ''); // comma + } + + return ( + str + + // [], {} + (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end) + ); +} + +/** + * Return a new Thing that has the keys in sorted order. Recursive. + * + * If the Thing... + * - has already been seen, return string `'[Circular]'` + * - is `undefined`, return string `'[undefined]'` + * - is `null`, return value `null` + * - is some other primitive, return the value + * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method + * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again. + * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()` + * + * @private + * @see {@link exports.stringify} + * @param {*} value Thing to inspect. May or may not have properties. + * @param {Array} [stack=[]] Stack of seen values + * @param {string} [typeHint] Type hint + * @return {(Object|Array|Function|string|undefined)} + */ +exports.canonicalize = function canonicalize(value, stack, typeHint) { + var canonicalizedObj; + /* eslint-disable no-unused-vars */ + var prop; + /* eslint-enable no-unused-vars */ + typeHint = typeHint || type(value); + function withStack(value, fn) { + stack.push(value); + fn(); + stack.pop(); + } + + stack = stack || []; + + if (stack.indexOf(value) !== -1) { + return '[Circular]'; + } + + switch (typeHint) { + case 'undefined': + case 'buffer': + case 'null': + canonicalizedObj = value; + break; + case 'array': + withStack(value, function() { + canonicalizedObj = value.map(function(item) { + return exports.canonicalize(item, stack); + }); + }); + break; + case 'function': + /* eslint-disable guard-for-in */ + for (prop in value) { + canonicalizedObj = {}; + break; + } + /* eslint-enable guard-for-in */ + if (!canonicalizedObj) { + canonicalizedObj = emptyRepresentation(value, typeHint); + break; + } + /* falls through */ + case 'object': + canonicalizedObj = canonicalizedObj || {}; + withStack(value, function() { + Object.keys(value) + .sort() + .forEach(function(key) { + canonicalizedObj[key] = exports.canonicalize(value[key], stack); + }); + }); + break; + case 'date': + case 'number': + case 'regexp': + case 'boolean': + case 'symbol': + canonicalizedObj = value; + break; + default: + canonicalizedObj = value + ''; + } + + return canonicalizedObj; +}; + +/** + * Determines if pathname has a matching file extension. + * + * @private + * @param {string} pathname - Pathname to check for match. + * @param {string[]} exts - List of file extensions (sans period). + * @return {boolean} whether file extension matches. + * @example + * hasMatchingExtname('foo.html', ['js', 'css']); // => false + */ +function hasMatchingExtname(pathname, exts) { + var suffix = path.extname(pathname).slice(1); + return exts.some(function(element) { + return suffix === element; + }); +} + +/** + * Determines if pathname would be a "hidden" file (or directory) on UN*X. + * + * @description + * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during + * typical usage. Dotfiles, plain-text configuration files, are prime examples. + * + * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names} + * + * @private + * @param {string} pathname - Pathname to check for match. + * @return {boolean} whether pathname would be considered a hidden file. + * @example + * isHiddenOnUnix('.profile'); // => true + */ +function isHiddenOnUnix(pathname) { + return path.basename(pathname)[0] === '.'; +} + +/** + * Lookup file names at the given `path`. + * + * @description + * Filenames are returned in _traversal_ order by the OS/filesystem. + * **Make no assumption that the names will be sorted in any fashion.** + * + * @public + * @memberof Mocha.utils + * @todo Fix extension handling + * @param {string} filepath - Base path to start searching from. + * @param {string[]} extensions - File extensions to look for. + * @param {boolean} recursive - Whether to recurse into subdirectories. + * @return {string[]} An array of paths. + * @throws {Error} if no files match pattern. + * @throws {TypeError} if `filepath` is directory and `extensions` not provided. + */ +exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { + var files = []; + var stat; + + if (!fs.existsSync(filepath)) { + if (fs.existsSync(filepath + '.js')) { + filepath += '.js'; + } else { + // Handle glob + files = glob.sync(filepath); + if (!files.length) { + throw createNoFilesMatchPatternError( + 'Cannot find any files matching pattern ' + exports.dQuote(filepath), + filepath + ); + } + return files; + } + } + + // Handle file + try { + stat = fs.statSync(filepath); + if (stat.isFile()) { + return filepath; + } + } catch (err) { + // ignore error + return; + } + + // Handle directory + fs.readdirSync(filepath).forEach(function(dirent) { + var pathname = path.join(filepath, dirent); + var stat; + + try { + stat = fs.statSync(pathname); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(pathname, extensions, recursive)); + } + return; + } + } catch (err) { + // ignore error + return; + } + if (!extensions) { + throw createMissingArgumentError( + util.format( + 'Argument %s required when argument %s is a directory', + exports.sQuote('extensions'), + exports.sQuote('filepath') + ), + 'extensions', + 'array' + ); + } + + if ( + !stat.isFile() || + !hasMatchingExtname(pathname, extensions) || + isHiddenOnUnix(pathname) + ) { + return; + } + files.push(pathname); + }); + + return files; +}; + +/** + * process.emitWarning or a polyfill + * @see https://nodejs.org/api/process.html#process_process_emitwarning_warning_options + * @ignore + */ +function emitWarning(msg, type) { + if (process.emitWarning) { + process.emitWarning(msg, type); + } else { + process.nextTick(function() { + console.warn(type + ': ' + msg); + }); + } +} + +/** + * Show a deprecation warning. Each distinct message is only displayed once. + * Ignores empty messages. + * + * @param {string} [msg] - Warning to print + * @private + */ +exports.deprecate = function deprecate(msg) { + msg = String(msg); + if (msg && !deprecate.cache[msg]) { + deprecate.cache[msg] = true; + emitWarning(msg, 'DeprecationWarning'); + } +}; +exports.deprecate.cache = {}; + +/** + * Show a generic warning. + * Ignores empty messages. + * + * @param {string} [msg] - Warning to print + * @private + */ +exports.warn = function warn(msg) { + if (msg) { + emitWarning(msg); + } +}; + +/** + * @summary + * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`) + * @description + * When invoking this function you get a filter function that get the Error.stack as an input, + * and return a prettify output. + * (i.e: strip Mocha and internal node functions from stack trace). + * @returns {Function} + */ +exports.stackTraceFilter = function() { + // TODO: Replace with `process.browser` + var is = typeof document === 'undefined' ? {node: true} : {browser: true}; + var slash = path.sep; + var cwd; + if (is.node) { + cwd = process.cwd() + slash; + } else { + cwd = (typeof location === 'undefined' + ? window.location + : location + ).href.replace(/\/[^/]*$/, '/'); + slash = '/'; + } + + function isMochaInternal(line) { + return ( + ~line.indexOf('node_modules' + slash + 'mocha' + slash) || + ~line.indexOf(slash + 'mocha.js') + ); + } + + function isNodeInternal(line) { + return ( + ~line.indexOf('(timers.js:') || + ~line.indexOf('(events.js:') || + ~line.indexOf('(node.js:') || + ~line.indexOf('(module.js:') || + ~line.indexOf('GeneratorFunctionPrototype.next (native)') || + false + ); + } + + return function(stack) { + stack = stack.split('\n'); + + stack = stack.reduce(function(list, line) { + if (isMochaInternal(line)) { + return list; + } + + if (is.node && isNodeInternal(line)) { + return list; + } + + // Clean up cwd(absolute) + if (/:\d+:\d+\)?$/.test(line)) { + line = line.replace('(' + cwd, '('); + } + + list.push(line); + return list; + }, []); + + return stack.join('\n'); + }; +}; + +/** + * Crude, but effective. + * @public + * @param {*} value + * @returns {boolean} Whether or not `value` is a Promise + */ +exports.isPromise = function isPromise(value) { + return ( + typeof value === 'object' && + value !== null && + typeof value.then === 'function' + ); +}; + +/** + * Clamps a numeric value to an inclusive range. + * + * @param {number} value - Value to be clamped. + * @param {numer[]} range - Two element array specifying [min, max] range. + * @returns {number} clamped value + */ +exports.clamp = function clamp(value, range) { + return Math.min(Math.max(value, range[0]), range[1]); +}; + +/** + * Single quote text by combining with undirectional ASCII quotation marks. + * + * @description + * Provides a simple means of markup for quoting text to be used in output. + * Use this to quote names of variables, methods, and packages. + * + * package 'foo' cannot be found + * + * @private + * @param {string} str - Value to be quoted. + * @returns {string} quoted value + * @example + * sQuote('n') // => 'n' + */ +exports.sQuote = function(str) { + return "'" + str + "'"; +}; + +/** + * Double quote text by combining with undirectional ASCII quotation marks. + * + * @description + * Provides a simple means of markup for quoting text to be used in output. + * Use this to quote names of datatypes, classes, pathnames, and strings. + * + * argument 'value' must be "string" or "number" + * + * @private + * @param {string} str - Value to be quoted. + * @returns {string} quoted value + * @example + * dQuote('number') // => "number" + */ +exports.dQuote = function(str) { + return '"' + str + '"'; +}; + +/** + * Provides simplistic message translation for dealing with plurality. + * + * @description + * Use this to create messages which need to be singular or plural. + * Some languages have several plural forms, so _complete_ message clauses + * are preferable to generating the message on the fly. + * + * @private + * @param {number} n - Non-negative integer + * @param {string} msg1 - Message to be used in English for `n = 1` + * @param {string} msg2 - Message to be used in English for `n = 0, 2, 3, ...` + * @returns {string} message corresponding to value of `n` + * @example + * var sprintf = require('util').format; + * var pkgs = ['one', 'two']; + * var msg = sprintf( + * ngettext( + * pkgs.length, + * 'cannot load package: %s', + * 'cannot load packages: %s' + * ), + * pkgs.map(sQuote).join(', ') + * ); + * console.log(msg); // => cannot load packages: 'one', 'two' + */ +exports.ngettext = function(n, msg1, msg2) { + if (typeof n === 'number' && n >= 0) { + return n === 1 ? msg1 : msg2; + } +}; + +/** + * It's a noop. + * @public + */ +exports.noop = function() {}; + +/** + * Creates a map-like object. + * + * @description + * A "map" is an object with no prototype, for our purposes. In some cases + * this would be more appropriate than a `Map`, especially if your environment + * doesn't support it. Recommended for use in Mocha's public APIs. + * + * @public + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map|MDN:Map} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects|MDN:Object.create - Custom objects} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign|MDN:Object.assign} + * @param {...*} [obj] - Arguments to `Object.assign()`. + * @returns {Object} An object with no prototype, having `...obj` properties + */ +exports.createMap = function(obj) { + return assign.apply( + null, + [Object.create(null)].concat(Array.prototype.slice.call(arguments)) + ); +}; + +/** + * Creates a read-only map-like object. + * + * @description + * This differs from {@link module:utils.createMap createMap} only in that + * the argument must be non-empty, because the result is frozen. + * + * @see {@link module:utils.createMap createMap} + * @param {...*} [obj] - Arguments to `Object.assign()`. + * @returns {Object} A frozen object with no prototype, having `...obj` properties + * @throws {TypeError} if argument is not a non-empty object. + */ +exports.defineConstants = function(obj) { + if (type(obj) !== 'object' || !Object.keys(obj).length) { + throw new TypeError('Invalid argument; expected a non-empty object'); + } + return Object.freeze(exports.createMap(obj)); +}; diff --git a/WebServer/node_modules/mocha/mocha.js b/WebServer/node_modules/mocha/mocha.js new file mode 100644 index 000000000..508a306db --- /dev/null +++ b/WebServer/node_modules/mocha/mocha.js @@ -0,0 +1,18076 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i} promise determining if browser notification + * permissible when fulfilled. + */ +function isPermitted() { + var permitted = { + granted: function allow() { + return Promise.resolve(true); + }, + denied: function deny() { + return Promise.resolve(false); + }, + default: function ask() { + return Notification.requestPermission().then(function(permission) { + return permission === 'granted'; + }); + } + }; + + return permitted[Notification.permission](); +} + +/** + * @summary + * Determines if notification should proceed. + * + * @description + * Notification shall not proceed unless `value` is true. + * + * `value` will equal one of: + *
      + *
    • true (from `isPermitted`)
    • + *
    • false (from `isPermitted`)
    • + *
    • undefined (from `Promise.race`)
    • + *
    + * + * @private + * @param {boolean|undefined} value - Determines if notification permissible. + * @returns {Promise} Notification can proceed + */ +function canNotify(value) { + if (!value) { + var why = value === false ? 'blocked' : 'unacknowledged'; + var reason = 'not permitted by user (' + why + ')'; + return Promise.reject(new Error(reason)); + } + return Promise.resolve(); +} + +/** + * Displays the notification. + * + * @private + * @param {Runner} runner - Runner instance. + */ +function display(runner) { + var stats = runner.stats; + var symbol = { + cross: '\u274C', + tick: '\u2705' + }; + var logo = require('../../package').notifyLogo; + var _message; + var message; + var title; + + if (stats.failures) { + _message = stats.failures + ' of ' + stats.tests + ' tests failed'; + message = symbol.cross + ' ' + _message; + title = 'Failed'; + } else { + _message = stats.passes + ' tests passed in ' + stats.duration + 'ms'; + message = symbol.tick + ' ' + _message; + title = 'Passed'; + } + + // Send notification + var options = { + badge: logo, + body: message, + dir: 'ltr', + icon: logo, + lang: 'en-US', + name: 'mocha', + requireInteraction: false, + timestamp: Date.now() + }; + var notification = new Notification(title, options); + + // Autoclose after brief delay (makes various browsers act same) + var FORCE_DURATION = 4000; + setTimeout(notification.close.bind(notification), FORCE_DURATION); +} + +/** + * As notifications are tangential to our purpose, just log the error. + * + * @private + * @param {Error} err - Why notification didn't happen. + */ +function notPermitted(err) { + console.error('notification error:', err.message); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../../package":90,"../runner":34,"_process":69}],3:[function(require,module,exports){ +'use strict'; + +/** + * Expose `Progress`. + */ + +module.exports = Progress; + +/** + * Initialize a new `Progress` indicator. + */ +function Progress() { + this.percent = 0; + this.size(0); + this.fontSize(11); + this.font('helvetica, arial, sans-serif'); +} + +/** + * Set progress size to `size`. + * + * @public + * @param {number} size + * @return {Progress} Progress instance. + */ +Progress.prototype.size = function(size) { + this._size = size; + return this; +}; + +/** + * Set text to `text`. + * + * @public + * @param {string} text + * @return {Progress} Progress instance. + */ +Progress.prototype.text = function(text) { + this._text = text; + return this; +}; + +/** + * Set font size to `size`. + * + * @public + * @param {number} size + * @return {Progress} Progress instance. + */ +Progress.prototype.fontSize = function(size) { + this._fontSize = size; + return this; +}; + +/** + * Set font to `family`. + * + * @param {string} family + * @return {Progress} Progress instance. + */ +Progress.prototype.font = function(family) { + this._font = family; + return this; +}; + +/** + * Update percentage to `n`. + * + * @param {number} n + * @return {Progress} Progress instance. + */ +Progress.prototype.update = function(n) { + this.percent = n; + return this; +}; + +/** + * Draw on `ctx`. + * + * @param {CanvasRenderingContext2d} ctx + * @return {Progress} Progress instance. + */ +Progress.prototype.draw = function(ctx) { + try { + var percent = Math.min(this.percent, 100); + var size = this._size; + var half = size / 2; + var x = half; + var y = half; + var rad = half - 1; + var fontSize = this._fontSize; + + ctx.font = fontSize + 'px ' + this._font; + + var angle = Math.PI * 2 * (percent / 100); + ctx.clearRect(0, 0, size, size); + + // outer circle + ctx.strokeStyle = '#9f9f9f'; + ctx.beginPath(); + ctx.arc(x, y, rad, 0, angle, false); + ctx.stroke(); + + // inner circle + ctx.strokeStyle = '#eee'; + ctx.beginPath(); + ctx.arc(x, y, rad - 1, 0, angle, true); + ctx.stroke(); + + // text + var text = this._text || (percent | 0) + '%'; + var w = ctx.measureText(text).width; + + ctx.fillText(text, x - w / 2 + 1, y + fontSize / 2 - 1); + } catch (ignore) { + // don't fail if we can't render progress + } + return this; +}; + +},{}],4:[function(require,module,exports){ +(function (global){ +'use strict'; + +exports.isatty = function isatty() { + return true; +}; + +exports.getWindowSize = function getWindowSize() { + if ('innerHeight' in global) { + return [global.innerHeight, global.innerWidth]; + } + // In a Web Worker, the DOM Window is not available. + return [640, 480]; +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],5:[function(require,module,exports){ +'use strict'; +/** + * @module Context + */ +/** + * Expose `Context`. + */ + +module.exports = Context; + +/** + * Initialize a new `Context`. + * + * @private + */ +function Context() {} + +/** + * Set or get the context `Runnable` to `runnable`. + * + * @private + * @param {Runnable} runnable + * @return {Context} context + */ +Context.prototype.runnable = function(runnable) { + if (!arguments.length) { + return this._runnable; + } + this.test = this._runnable = runnable; + return this; +}; + +/** + * Set or get test timeout `ms`. + * + * @private + * @param {number} ms + * @return {Context} self + */ +Context.prototype.timeout = function(ms) { + if (!arguments.length) { + return this.runnable().timeout(); + } + this.runnable().timeout(ms); + return this; +}; + +/** + * Set test timeout `enabled`. + * + * @private + * @param {boolean} enabled + * @return {Context} self + */ +Context.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this.runnable().enableTimeouts(); + } + this.runnable().enableTimeouts(enabled); + return this; +}; + +/** + * Set or get test slowness threshold `ms`. + * + * @private + * @param {number} ms + * @return {Context} self + */ +Context.prototype.slow = function(ms) { + if (!arguments.length) { + return this.runnable().slow(); + } + this.runnable().slow(ms); + return this; +}; + +/** + * Mark a test as skipped. + * + * @private + * @throws Pending + */ +Context.prototype.skip = function() { + this.runnable().skip(); +}; + +/** + * Set or get a number of allowed retries on failed tests + * + * @private + * @param {number} n + * @return {Context} self + */ +Context.prototype.retries = function(n) { + if (!arguments.length) { + return this.runnable().retries(); + } + this.runnable().retries(n); + return this; +}; + +},{}],6:[function(require,module,exports){ +'use strict'; +/** + * @module Errors + */ +/** + * Factory functions to create throwable error objects + */ + +/** + * Creates an error object to be thrown when no files to be tested could be found using specified pattern. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} pattern - User-specified argument value. + * @returns {Error} instance detailing the error condition + */ +function createNoFilesMatchPatternError(message, pattern) { + var err = new Error(message); + err.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; + err.pattern = pattern; + return err; +} + +/** + * Creates an error object to be thrown when the reporter specified in the options was not found. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} reporter - User-specified reporter value. + * @returns {Error} instance detailing the error condition + */ +function createInvalidReporterError(message, reporter) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_REPORTER'; + err.reporter = reporter; + return err; +} + +/** + * Creates an error object to be thrown when the interface specified in the options was not found. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} ui - User-specified interface value. + * @returns {Error} instance detailing the error condition + */ +function createInvalidInterfaceError(message, ui) { + var err = new Error(message); + err.code = 'ERR_MOCHA_INVALID_INTERFACE'; + err.interface = ui; + return err; +} + +/** + * Creates an error object to be thrown when a behavior, option, or parameter is unsupported. + * + * @public + * @param {string} message - Error message to be displayed. + * @returns {Error} instance detailing the error condition + */ +function createUnsupportedError(message) { + var err = new Error(message); + err.code = 'ERR_MOCHA_UNSUPPORTED'; + return err; +} + +/** + * Creates an error object to be thrown when an argument is missing. + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - Argument name. + * @param {string} expected - Expected argument datatype. + * @returns {Error} instance detailing the error condition + */ +function createMissingArgumentError(message, argument, expected) { + return createInvalidArgumentTypeError(message, argument, expected); +} + +/** + * Creates an error object to be thrown when an argument did not use the supported type + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - Argument name. + * @param {string} expected - Expected argument datatype. + * @returns {Error} instance detailing the error condition + */ +function createInvalidArgumentTypeError(message, argument, expected) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_ARG_TYPE'; + err.argument = argument; + err.expected = expected; + err.actual = typeof argument; + return err; +} + +/** + * Creates an error object to be thrown when an argument did not use the supported value + * + * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - Argument name. + * @param {string} value - Argument value. + * @param {string} [reason] - Why value is invalid. + * @returns {Error} instance detailing the error condition + */ +function createInvalidArgumentValueError(message, argument, value, reason) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_ARG_VALUE'; + err.argument = argument; + err.value = value; + err.reason = typeof reason !== 'undefined' ? reason : 'is invalid'; + return err; +} + +/** + * Creates an error object to be thrown when an exception was caught, but the `Error` is falsy or undefined. + * + * @public + * @param {string} message - Error message to be displayed. + * @returns {Error} instance detailing the error condition + */ +function createInvalidExceptionError(message, value) { + var err = new Error(message); + err.code = 'ERR_MOCHA_INVALID_EXCEPTION'; + err.valueType = typeof value; + err.value = value; + return err; +} + +module.exports = { + createInvalidArgumentTypeError: createInvalidArgumentTypeError, + createInvalidArgumentValueError: createInvalidArgumentValueError, + createInvalidExceptionError: createInvalidExceptionError, + createInvalidInterfaceError: createInvalidInterfaceError, + createInvalidReporterError: createInvalidReporterError, + createMissingArgumentError: createMissingArgumentError, + createNoFilesMatchPatternError: createNoFilesMatchPatternError, + createUnsupportedError: createUnsupportedError +}; + +},{}],7:[function(require,module,exports){ +'use strict'; + +var Runnable = require('./runnable'); +var inherits = require('./utils').inherits; + +/** + * Expose `Hook`. + */ + +module.exports = Hook; + +/** + * Initialize a new `Hook` with the given `title` and callback `fn` + * + * @class + * @extends Runnable + * @param {String} title + * @param {Function} fn + */ +function Hook(title, fn) { + Runnable.call(this, title, fn); + this.type = 'hook'; +} + +/** + * Inherit from `Runnable.prototype`. + */ +inherits(Hook, Runnable); + +/** + * Get or set the test `err`. + * + * @memberof Hook + * @public + * @param {Error} err + * @return {Error} + */ +Hook.prototype.error = function(err) { + if (!arguments.length) { + err = this._error; + this._error = null; + return err; + } + + this._error = err; +}; + +},{"./runnable":33,"./utils":38}],8:[function(require,module,exports){ +'use strict'; + +var Test = require('../test'); +var EVENT_FILE_PRE_REQUIRE = require('../suite').constants + .EVENT_FILE_PRE_REQUIRE; + +/** + * BDD-style interface: + * + * describe('Array', function() { + * describe('#indexOf()', function() { + * it('should return -1 when not present', function() { + * // ... + * }); + * + * it('should return the index when present', function() { + * // ... + * }); + * }); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function bddInterface(suite) { + var suites = [suite]; + + suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { + var common = require('./common')(suites, context, mocha); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title` + * and callback `fn` containing nested suites + * and/or tests. + */ + + context.describe = context.context = function(title, fn) { + return common.suite.create({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Pending describe. + */ + + context.xdescribe = context.xcontext = context.describe.skip = function( + title, + fn + ) { + return common.suite.skip({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Exclusive suite. + */ + + context.describe.only = function(title, fn) { + return common.suite.only({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.it = context.specify = function(title, fn) { + var suite = suites[0]; + if (suite.isPending()) { + fn = null; + } + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.it.only = function(title, fn) { + return common.test.only(mocha, context.it(title, fn)); + }; + + /** + * Pending test case. + */ + + context.xit = context.xspecify = context.it.skip = function(title) { + return context.it(title); + }; + + /** + * Number of attempts to retry. + */ + context.it.retries = function(n) { + context.retries(n); + }; + }); +}; + +module.exports.description = 'BDD or RSpec style [default]'; + +},{"../suite":36,"../test":37,"./common":9}],9:[function(require,module,exports){ +'use strict'; + +var Suite = require('../suite'); +var errors = require('../errors'); +var createMissingArgumentError = errors.createMissingArgumentError; + +/** + * Functions common to more than one interface. + * + * @param {Suite[]} suites + * @param {Context} context + * @param {Mocha} mocha + * @return {Object} An object containing common functions. + */ +module.exports = function(suites, context, mocha) { + /** + * Check if the suite should be tested. + * + * @private + * @param {Suite} suite - suite to check + * @returns {boolean} + */ + function shouldBeTested(suite) { + return ( + !mocha.options.grep || + (mocha.options.grep && + mocha.options.grep.test(suite.fullTitle()) && + !mocha.options.invert) + ); + } + + return { + /** + * This is only present if flag --delay is passed into Mocha. It triggers + * root suite execution. + * + * @param {Suite} suite The root suite. + * @return {Function} A function which runs the root suite + */ + runWithSuite: function runWithSuite(suite) { + return function run() { + suite.run(); + }; + }, + + /** + * Execute before running tests. + * + * @param {string} name + * @param {Function} fn + */ + before: function(name, fn) { + suites[0].beforeAll(name, fn); + }, + + /** + * Execute after running tests. + * + * @param {string} name + * @param {Function} fn + */ + after: function(name, fn) { + suites[0].afterAll(name, fn); + }, + + /** + * Execute before each test case. + * + * @param {string} name + * @param {Function} fn + */ + beforeEach: function(name, fn) { + suites[0].beforeEach(name, fn); + }, + + /** + * Execute after each test case. + * + * @param {string} name + * @param {Function} fn + */ + afterEach: function(name, fn) { + suites[0].afterEach(name, fn); + }, + + suite: { + /** + * Create an exclusive Suite; convenience function + * See docstring for create() below. + * + * @param {Object} opts + * @returns {Suite} + */ + only: function only(opts) { + opts.isOnly = true; + return this.create(opts); + }, + + /** + * Create a Suite, but skip it; convenience function + * See docstring for create() below. + * + * @param {Object} opts + * @returns {Suite} + */ + skip: function skip(opts) { + opts.pending = true; + return this.create(opts); + }, + + /** + * Creates a suite. + * + * @param {Object} opts Options + * @param {string} opts.title Title of Suite + * @param {Function} [opts.fn] Suite Function (not always applicable) + * @param {boolean} [opts.pending] Is Suite pending? + * @param {string} [opts.file] Filepath where this Suite resides + * @param {boolean} [opts.isOnly] Is Suite exclusive? + * @returns {Suite} + */ + create: function create(opts) { + var suite = Suite.create(suites[0], opts.title); + suite.pending = Boolean(opts.pending); + suite.file = opts.file; + suites.unshift(suite); + if (opts.isOnly) { + if (mocha.options.forbidOnly && shouldBeTested(suite)) { + throw new Error('`.only` forbidden'); + } + + suite.parent.appendOnlySuite(suite); + } + if (suite.pending) { + if (mocha.options.forbidPending && shouldBeTested(suite)) { + throw new Error('Pending test forbidden'); + } + } + if (typeof opts.fn === 'function') { + opts.fn.call(suite); + suites.shift(); + } else if (typeof opts.fn === 'undefined' && !suite.pending) { + throw createMissingArgumentError( + 'Suite "' + + suite.fullTitle() + + '" was defined but no callback was supplied. ' + + 'Supply a callback or explicitly skip the suite.', + 'callback', + 'function' + ); + } else if (!opts.fn && suite.pending) { + suites.shift(); + } + + return suite; + } + }, + + test: { + /** + * Exclusive test-case. + * + * @param {Object} mocha + * @param {Function} test + * @returns {*} + */ + only: function(mocha, test) { + test.parent.appendOnlyTest(test); + return test; + }, + + /** + * Pending test case. + * + * @param {string} title + */ + skip: function(title) { + context.test(title); + }, + + /** + * Number of retry attempts + * + * @param {number} n + */ + retries: function(n) { + context.retries(n); + } + } + }; +}; + +},{"../errors":6,"../suite":36}],10:[function(require,module,exports){ +'use strict'; +var Suite = require('../suite'); +var Test = require('../test'); + +/** + * Exports-style (as Node.js module) interface: + * + * exports.Array = { + * '#indexOf()': { + * 'should return -1 when the value is not present': function() { + * + * }, + * + * 'should return the correct index when the value is present': function() { + * + * } + * } + * }; + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on(Suite.constants.EVENT_FILE_REQUIRE, visit); + + function visit(obj, file) { + var suite; + for (var key in obj) { + if (typeof obj[key] === 'function') { + var fn = obj[key]; + switch (key) { + case 'before': + suites[0].beforeAll(fn); + break; + case 'after': + suites[0].afterAll(fn); + break; + case 'beforeEach': + suites[0].beforeEach(fn); + break; + case 'afterEach': + suites[0].afterEach(fn); + break; + default: + var test = new Test(key, fn); + test.file = file; + suites[0].addTest(test); + } + } else { + suite = Suite.create(suites[0], key); + suites.unshift(suite); + visit(obj[key], file); + suites.shift(); + } + } + } +}; + +module.exports.description = 'Node.js module ("exports") style'; + +},{"../suite":36,"../test":37}],11:[function(require,module,exports){ +'use strict'; + +exports.bdd = require('./bdd'); +exports.tdd = require('./tdd'); +exports.qunit = require('./qunit'); +exports.exports = require('./exports'); + +},{"./bdd":8,"./exports":10,"./qunit":12,"./tdd":13}],12:[function(require,module,exports){ +'use strict'; + +var Test = require('../test'); +var EVENT_FILE_PRE_REQUIRE = require('../suite').constants + .EVENT_FILE_PRE_REQUIRE; + +/** + * QUnit-style interface: + * + * suite('Array'); + * + * test('#length', function() { + * var arr = [1,2,3]; + * ok(arr.length == 3); + * }); + * + * test('#indexOf()', function() { + * var arr = [1,2,3]; + * ok(arr.indexOf(1) == 0); + * ok(arr.indexOf(2) == 1); + * ok(arr.indexOf(3) == 2); + * }); + * + * suite('String'); + * + * test('#length', function() { + * ok('foo'.length == 3); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function qUnitInterface(suite) { + var suites = [suite]; + + suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { + var common = require('./common')(suites, context, mocha); + + context.before = common.before; + context.after = common.after; + context.beforeEach = common.beforeEach; + context.afterEach = common.afterEach; + context.run = mocha.options.delay && common.runWithSuite(suite); + /** + * Describe a "suite" with the given `title`. + */ + + context.suite = function(title) { + if (suites.length > 1) { + suites.shift(); + } + return common.suite.create({ + title: title, + file: file, + fn: false + }); + }; + + /** + * Exclusive Suite. + */ + + context.suite.only = function(title) { + if (suites.length > 1) { + suites.shift(); + } + return common.suite.only({ + title: title, + file: file, + fn: false + }); + }; + + /** + * Describe a specification or test-case + * with the given `title` and callback `fn` + * acting as a thunk. + */ + + context.test = function(title, fn) { + var test = new Test(title, fn); + test.file = file; + suites[0].addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + return common.test.only(mocha, context.test(title, fn)); + }; + + context.test.skip = common.test.skip; + context.test.retries = common.test.retries; + }); +}; + +module.exports.description = 'QUnit style'; + +},{"../suite":36,"../test":37,"./common":9}],13:[function(require,module,exports){ +'use strict'; + +var Test = require('../test'); +var EVENT_FILE_PRE_REQUIRE = require('../suite').constants + .EVENT_FILE_PRE_REQUIRE; + +/** + * TDD-style interface: + * + * suite('Array', function() { + * suite('#indexOf()', function() { + * suiteSetup(function() { + * + * }); + * + * test('should return -1 when not present', function() { + * + * }); + * + * test('should return the index when present', function() { + * + * }); + * + * suiteTeardown(function() { + * + * }); + * }); + * }); + * + * @param {Suite} suite Root suite. + */ +module.exports = function(suite) { + var suites = [suite]; + + suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) { + var common = require('./common')(suites, context, mocha); + + context.setup = common.beforeEach; + context.teardown = common.afterEach; + context.suiteSetup = common.before; + context.suiteTeardown = common.after; + context.run = mocha.options.delay && common.runWithSuite(suite); + + /** + * Describe a "suite" with the given `title` and callback `fn` containing + * nested suites and/or tests. + */ + context.suite = function(title, fn) { + return common.suite.create({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Pending suite. + */ + context.suite.skip = function(title, fn) { + return common.suite.skip({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Exclusive test-case. + */ + context.suite.only = function(title, fn) { + return common.suite.only({ + title: title, + file: file, + fn: fn + }); + }; + + /** + * Describe a specification or test-case with the given `title` and + * callback `fn` acting as a thunk. + */ + context.test = function(title, fn) { + var suite = suites[0]; + if (suite.isPending()) { + fn = null; + } + var test = new Test(title, fn); + test.file = file; + suite.addTest(test); + return test; + }; + + /** + * Exclusive test-case. + */ + + context.test.only = function(title, fn) { + return common.test.only(mocha, context.test(title, fn)); + }; + + context.test.skip = common.test.skip; + context.test.retries = common.test.retries; + }); +}; + +module.exports.description = + 'traditional "suite"/"test" instead of BDD\'s "describe"/"it"'; + +},{"../suite":36,"../test":37,"./common":9}],14:[function(require,module,exports){ +(function (process,global){ +'use strict'; + +/*! + * mocha + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +var escapeRe = require('escape-string-regexp'); +var path = require('path'); +var builtinReporters = require('./reporters'); +var growl = require('./growl'); +var utils = require('./utils'); +var mocharc = require('./mocharc.json'); +var errors = require('./errors'); +var Suite = require('./suite'); +var createStatsCollector = require('./stats-collector'); +var createInvalidReporterError = errors.createInvalidReporterError; +var createInvalidInterfaceError = errors.createInvalidInterfaceError; +var EVENT_FILE_PRE_REQUIRE = Suite.constants.EVENT_FILE_PRE_REQUIRE; +var EVENT_FILE_POST_REQUIRE = Suite.constants.EVENT_FILE_POST_REQUIRE; +var EVENT_FILE_REQUIRE = Suite.constants.EVENT_FILE_REQUIRE; +var sQuote = utils.sQuote; + +exports = module.exports = Mocha; + +/** + * To require local UIs and reporters when running in node. + */ + +if (!process.browser) { + var cwd = process.cwd(); + module.paths.push(cwd, path.join(cwd, 'node_modules')); +} + +/** + * Expose internals. + */ + +/** + * @public + * @class utils + * @memberof Mocha + */ +exports.utils = utils; +exports.interfaces = require('./interfaces'); +/** + * @public + * @memberof Mocha + */ +exports.reporters = builtinReporters; +exports.Runnable = require('./runnable'); +exports.Context = require('./context'); +/** + * + * @memberof Mocha + */ +exports.Runner = require('./runner'); +exports.Suite = Suite; +exports.Hook = require('./hook'); +exports.Test = require('./test'); + +/** + * Constructs a new Mocha instance with `options`. + * + * @public + * @class Mocha + * @param {Object} [options] - Settings object. + * @param {boolean} [options.allowUncaught] - Propagate uncaught errors? + * @param {boolean} [options.asyncOnly] - Force `done` callback or promise? + * @param {boolean} [options.bail] - Bail after first test failure? + * @param {boolean} [options.checkLeaks] - If true, check leaks. + * @param {boolean} [options.delay] - Delay root suite execution? + * @param {boolean} [options.enableTimeouts] - Enable timeouts? + * @param {string} [options.fgrep] - Test filter given string. + * @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite? + * @param {boolean} [options.forbidPending] - Pending tests fail the suite? + * @param {boolean} [options.fullStackTrace] - Full stacktrace upon failure? + * @param {string[]} [options.global] - Variables expected in global scope. + * @param {RegExp|string} [options.grep] - Test filter given regular expression. + * @param {boolean} [options.growl] - Enable desktop notifications? + * @param {boolean} [options.hideDiff] - Suppress diffs from failures? + * @param {boolean} [options.ignoreLeaks] - Ignore global leaks? + * @param {boolean} [options.invert] - Invert test filter matches? + * @param {boolean} [options.noHighlighting] - Disable syntax highlighting? + * @param {string} [options.reporter] - Reporter name. + * @param {Object} [options.reporterOption] - Reporter settings object. + * @param {number} [options.retries] - Number of times to retry failed tests. + * @param {number} [options.slow] - Slow threshold value. + * @param {number|string} [options.timeout] - Timeout threshold value. + * @param {string} [options.ui] - Interface name. + * @param {boolean} [options.color] - Color TTY output from reporter? + * @param {boolean} [options.useInlineDiffs] - Use inline diffs? + */ +function Mocha(options) { + options = utils.assign({}, mocharc, options || {}); + this.files = []; + this.options = options; + // root suite + this.suite = new exports.Suite('', new exports.Context(), true); + + if ('useColors' in options) { + utils.deprecate( + 'useColors is DEPRECATED and will be removed from a future version of Mocha. Instead, use the "color" option' + ); + options.color = 'color' in options ? options.color : options.useColors; + } + + this.grep(options.grep) + .fgrep(options.fgrep) + .ui(options.ui) + .bail(options.bail) + .reporter(options.reporter, options.reporterOptions) + .useColors(options.color) + .slow(options.slow) + .useInlineDiffs(options.inlineDiffs) + .globals(options.globals); + + if ('enableTimeouts' in options) { + utils.deprecate( + 'enableTimeouts is DEPRECATED and will be removed from a future version of Mocha. Instead, use "timeout: false" to disable timeouts.' + ); + if (options.enableTimeouts === false) { + this.timeout(0); + } + } + + // this guard exists because Suite#timeout does not consider `undefined` to be valid input + if (typeof options.timeout !== 'undefined') { + this.timeout(options.timeout === false ? 0 : options.timeout); + } + + if ('retries' in options) { + this.retries(options.retries); + } + + if ('diff' in options) { + this.hideDiff(!options.diff); + } + + [ + 'allowUncaught', + 'asyncOnly', + 'checkLeaks', + 'delay', + 'forbidOnly', + 'forbidPending', + 'fullTrace', + 'growl', + 'invert' + ].forEach(function(opt) { + if (options[opt]) { + this[opt](); + } + }, this); +} + +/** + * Enables or disables bailing on the first failure. + * + * @public + * @see {@link https://mochajs.org/#-b---bail|CLI option} + * @param {boolean} [bail=true] - Whether to bail on first error. + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.bail = function(bail) { + if (!arguments.length) { + bail = true; + } + this.suite.bail(bail); + return this; +}; + +/** + * @summary + * Adds `file` to be loaded for execution. + * + * @description + * Useful for generic setup code that must be included within test suite. + * + * @public + * @see {@link https://mochajs.org/#--file-file|CLI option} + * @param {string} file - Pathname of file to be loaded. + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.addFile = function(file) { + this.files.push(file); + return this; +}; + +/** + * Sets reporter to `reporter`, defaults to "spec". + * + * @public + * @see {@link https://mochajs.org/#-r---reporter-name|CLI option} + * @see {@link https://mochajs.org/#reporters|Reporters} + * @param {String|Function} reporter - Reporter name or constructor. + * @param {Object} [reporterOptions] - Options used to configure the reporter. + * @returns {Mocha} this + * @chainable + * @throws {Error} if requested reporter cannot be loaded + * @example + * + * // Use XUnit reporter and direct its output to file + * mocha.reporter('xunit', { output: '/path/to/testspec.xunit.xml' }); + */ +Mocha.prototype.reporter = function(reporter, reporterOptions) { + if (typeof reporter === 'function') { + this._reporter = reporter; + } else { + reporter = reporter || 'spec'; + var _reporter; + // Try to load a built-in reporter. + if (builtinReporters[reporter]) { + _reporter = builtinReporters[reporter]; + } + // Try to load reporters from process.cwd() and node_modules + if (!_reporter) { + try { + _reporter = require(reporter); + } catch (err) { + if ( + err.code !== 'MODULE_NOT_FOUND' || + err.message.indexOf('Cannot find module') !== -1 + ) { + // Try to load reporters from a path (absolute or relative) + try { + _reporter = require(path.resolve(process.cwd(), reporter)); + } catch (_err) { + _err.code !== 'MODULE_NOT_FOUND' || + _err.message.indexOf('Cannot find module') !== -1 + ? console.warn(sQuote(reporter) + ' reporter not found') + : console.warn( + sQuote(reporter) + + ' reporter blew up with error:\n' + + err.stack + ); + } + } else { + console.warn( + sQuote(reporter) + ' reporter blew up with error:\n' + err.stack + ); + } + } + } + if (!_reporter) { + throw createInvalidReporterError( + 'invalid reporter ' + sQuote(reporter), + reporter + ); + } + this._reporter = _reporter; + } + this.options.reporterOptions = reporterOptions; + return this; +}; + +/** + * Sets test UI `name`, defaults to "bdd". + * + * @public + * @see {@link https://mochajs.org/#-u---ui-name|CLI option} + * @see {@link https://mochajs.org/#interfaces|Interface DSLs} + * @param {string|Function} [ui=bdd] - Interface name or class. + * @returns {Mocha} this + * @chainable + * @throws {Error} if requested interface cannot be loaded + */ +Mocha.prototype.ui = function(ui) { + var bindInterface; + if (typeof ui === 'function') { + bindInterface = ui; + } else { + ui = ui || 'bdd'; + bindInterface = exports.interfaces[ui]; + if (!bindInterface) { + try { + bindInterface = require(ui); + } catch (err) { + throw createInvalidInterfaceError( + 'invalid interface ' + sQuote(ui), + ui + ); + } + } + } + bindInterface(this.suite); + + this.suite.on(EVENT_FILE_PRE_REQUIRE, function(context) { + exports.afterEach = context.afterEach || context.teardown; + exports.after = context.after || context.suiteTeardown; + exports.beforeEach = context.beforeEach || context.setup; + exports.before = context.before || context.suiteSetup; + exports.describe = context.describe || context.suite; + exports.it = context.it || context.test; + exports.xit = context.xit || (context.test && context.test.skip); + exports.setup = context.setup || context.beforeEach; + exports.suiteSetup = context.suiteSetup || context.before; + exports.suiteTeardown = context.suiteTeardown || context.after; + exports.suite = context.suite || context.describe; + exports.teardown = context.teardown || context.afterEach; + exports.test = context.test || context.it; + exports.run = context.run; + }); + + return this; +}; + +/** + * Loads `files` prior to execution. + * + * @description + * The implementation relies on Node's `require` to execute + * the test interface functions and will be subject to its cache. + * + * @private + * @see {@link Mocha#addFile} + * @see {@link Mocha#run} + * @see {@link Mocha#unloadFiles} + * @param {Function} [fn] - Callback invoked upon completion. + */ +Mocha.prototype.loadFiles = function(fn) { + var self = this; + var suite = this.suite; + this.files.forEach(function(file) { + file = path.resolve(file); + suite.emit(EVENT_FILE_PRE_REQUIRE, global, file, self); + suite.emit(EVENT_FILE_REQUIRE, require(file), file, self); + suite.emit(EVENT_FILE_POST_REQUIRE, global, file, self); + }); + fn && fn(); +}; + +/** + * Removes a previously loaded file from Node's `require` cache. + * + * @private + * @static + * @see {@link Mocha#unloadFiles} + * @param {string} file - Pathname of file to be unloaded. + */ +Mocha.unloadFile = function(file) { + delete require.cache[require.resolve(file)]; +}; + +/** + * Unloads `files` from Node's `require` cache. + * + * @description + * This allows files to be "freshly" reloaded, providing the ability + * to reuse a Mocha instance programmatically. + * + * Intended for consumers — not used internally + * + * @public + * @see {@link Mocha.unloadFile} + * @see {@link Mocha#loadFiles} + * @see {@link Mocha#run} + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.unloadFiles = function() { + this.files.forEach(Mocha.unloadFile); + return this; +}; + +/** + * Sets `grep` filter after escaping RegExp special characters. + * + * @public + * @see {@link Mocha#grep} + * @param {string} str - Value to be converted to a regexp. + * @returns {Mocha} this + * @chainable + * @example + * + * // Select tests whose full title begins with `"foo"` followed by a period + * mocha.fgrep('foo.'); + */ +Mocha.prototype.fgrep = function(str) { + if (!str) { + return this; + } + return this.grep(new RegExp(escapeRe(str))); +}; + +/** + * @summary + * Sets `grep` filter used to select specific tests for execution. + * + * @description + * If `re` is a regexp-like string, it will be converted to regexp. + * The regexp is tested against the full title of each test (i.e., the + * name of the test preceded by titles of each its ancestral suites). + * As such, using an exact-match fixed pattern against the + * test name itself will not yield any matches. + *
    + * Previous filter value will be overwritten on each call! + * + * @public + * @see {@link https://mochajs.org/#-g---grep-pattern|CLI option} + * @see {@link Mocha#fgrep} + * @see {@link Mocha#invert} + * @param {RegExp|String} re - Regular expression used to select tests. + * @return {Mocha} this + * @chainable + * @example + * + * // Select tests whose full title contains `"match"`, ignoring case + * mocha.grep(/match/i); + * @example + * + * // Same as above but with regexp-like string argument + * mocha.grep('/match/i'); + * @example + * + * // ## Anti-example + * // Given embedded test `it('only-this-test')`... + * mocha.grep('/^only-this-test$/'); // NO! Use `.only()` to do this! + */ +Mocha.prototype.grep = function(re) { + if (utils.isString(re)) { + // extract args if it's regex-like, i.e: [string, pattern, flag] + var arg = re.match(/^\/(.*)\/(g|i|)$|.*/); + this.options.grep = new RegExp(arg[1] || arg[0], arg[2]); + } else { + this.options.grep = re; + } + return this; +}; + +/** + * Inverts `grep` matches. + * + * @public + * @see {@link Mocha#grep} + * @return {Mocha} this + * @chainable + * @example + * + * // Select tests whose full title does *not* contain `"match"`, ignoring case + * mocha.grep(/match/i).invert(); + */ +Mocha.prototype.invert = function() { + this.options.invert = true; + return this; +}; + +/** + * Enables or disables ignoring global leaks. + * + * @public + * @see {@link Mocha#checkLeaks} + * @param {boolean} ignoreLeaks - Whether to ignore global leaks. + * @return {Mocha} this + * @chainable + * @example + * + * // Ignore global leaks + * mocha.ignoreLeaks(true); + */ +Mocha.prototype.ignoreLeaks = function(ignoreLeaks) { + this.options.ignoreLeaks = Boolean(ignoreLeaks); + return this; +}; + +/** + * Enables checking for global variables leaked while running tests. + * + * @public + * @see {@link https://mochajs.org/#--check-leaks|CLI option} + * @see {@link Mocha#ignoreLeaks} + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.checkLeaks = function() { + this.options.ignoreLeaks = false; + return this; +}; + +/** + * Displays full stack trace upon test failure. + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.fullTrace = function() { + this.options.fullStackTrace = true; + return this; +}; + +/** + * Enables desktop notification support if prerequisite software installed. + * + * @public + * @see {@link Mocha#isGrowlCapable} + * @see {@link Mocha#_growl} + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.growl = function() { + this.options.growl = this.isGrowlCapable(); + if (!this.options.growl) { + var detail = process.browser + ? 'notification support not available in this browser...' + : 'notification support prerequisites not installed...'; + console.error(detail + ' cannot enable!'); + } + return this; +}; + +/** + * @summary + * Determines if Growl support seems likely. + * + * @description + * Not available when run in browser. + * + * @private + * @see {@link Growl#isCapable} + * @see {@link Mocha#growl} + * @return {boolean} whether Growl support can be expected + */ +Mocha.prototype.isGrowlCapable = growl.isCapable; + +/** + * Implements desktop notifications using a pseudo-reporter. + * + * @private + * @see {@link Mocha#growl} + * @see {@link Growl#notify} + * @param {Runner} runner - Runner instance. + */ +Mocha.prototype._growl = growl.notify; + +/** + * Specifies whitelist of variable names to be expected in global scope. + * + * @public + * @see {@link https://mochajs.org/#--globals-names|CLI option} + * @see {@link Mocha#checkLeaks} + * @param {String[]|String} globals - Accepted global variable name(s). + * @return {Mocha} this + * @chainable + * @example + * + * // Specify variables to be expected in global scope + * mocha.globals(['jQuery', 'MyLib']); + */ +Mocha.prototype.globals = function(globals) { + this.options.globals = (this.options.globals || []) + .concat(globals) + .filter(Boolean); + return this; +}; + +/** + * Enables or disables TTY color output by screen-oriented reporters. + * + * @public + * @param {boolean} colors - Whether to enable color output. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.useColors = function(colors) { + if (colors !== undefined) { + this.options.useColors = colors; + } + return this; +}; + +/** + * Determines if reporter should use inline diffs (rather than +/-) + * in test failure output. + * + * @public + * @param {boolean} inlineDiffs - Whether to use inline diffs. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.useInlineDiffs = function(inlineDiffs) { + this.options.useInlineDiffs = inlineDiffs !== undefined && inlineDiffs; + return this; +}; + +/** + * Determines if reporter should include diffs in test failure output. + * + * @public + * @param {boolean} hideDiff - Whether to hide diffs. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.hideDiff = function(hideDiff) { + this.options.hideDiff = hideDiff !== undefined && hideDiff; + return this; +}; + +/** + * @summary + * Sets timeout threshold value. + * + * @description + * A string argument can use shorthand (such as "2s") and will be converted. + * If the value is `0`, timeouts will be disabled. + * + * @public + * @see {@link https://mochajs.org/#-t---timeout-ms|CLI option} + * @see {@link https://mochajs.org/#--no-timeouts|CLI option} + * @see {@link https://mochajs.org/#timeouts|Timeouts} + * @see {@link Mocha#enableTimeouts} + * @param {number|string} msecs - Timeout threshold value. + * @return {Mocha} this + * @chainable + * @example + * + * // Sets timeout to one second + * mocha.timeout(1000); + * @example + * + * // Same as above but using string argument + * mocha.timeout('1s'); + */ +Mocha.prototype.timeout = function(msecs) { + this.suite.timeout(msecs); + return this; +}; + +/** + * Sets the number of times to retry failed tests. + * + * @public + * @see {@link https://mochajs.org/#retry-tests|Retry Tests} + * @param {number} retry - Number of times to retry failed tests. + * @return {Mocha} this + * @chainable + * @example + * + * // Allow any failed test to retry one more time + * mocha.retries(1); + */ +Mocha.prototype.retries = function(n) { + this.suite.retries(n); + return this; +}; + +/** + * Sets slowness threshold value. + * + * @public + * @see {@link https://mochajs.org/#-s---slow-ms|CLI option} + * @param {number} msecs - Slowness threshold value. + * @return {Mocha} this + * @chainable + * @example + * + * // Sets "slow" threshold to half a second + * mocha.slow(500); + * @example + * + * // Same as above but using string argument + * mocha.slow('0.5s'); + */ +Mocha.prototype.slow = function(msecs) { + this.suite.slow(msecs); + return this; +}; + +/** + * Enables or disables timeouts. + * + * @public + * @see {@link https://mochajs.org/#-t---timeout-ms|CLI option} + * @see {@link https://mochajs.org/#--no-timeouts|CLI option} + * @param {boolean} enableTimeouts - Whether to enable timeouts. + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.enableTimeouts = function(enableTimeouts) { + this.suite.enableTimeouts( + arguments.length && enableTimeouts !== undefined ? enableTimeouts : true + ); + return this; +}; + +/** + * Forces all tests to either accept a `done` callback or return a promise. + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.asyncOnly = function() { + this.options.asyncOnly = true; + return this; +}; + +/** + * Disables syntax highlighting (in browser). + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + +/** + * Enables uncaught errors to propagate (in browser). + * + * @public + * @return {Mocha} this + * @chainable + */ +Mocha.prototype.allowUncaught = function() { + this.options.allowUncaught = true; + return this; +}; + +/** + * @summary + * Delays root suite execution. + * + * @description + * Used to perform asynch operations before any suites are run. + * + * @public + * @see {@link https://mochajs.org/#delayed-root-suite|delayed root suite} + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.delay = function delay() { + this.options.delay = true; + return this; +}; + +/** + * Causes tests marked `only` to fail the suite. + * + * @public + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.forbidOnly = function() { + this.options.forbidOnly = true; + return this; +}; + +/** + * Causes pending tests and tests marked `skip` to fail the suite. + * + * @public + * @returns {Mocha} this + * @chainable + */ +Mocha.prototype.forbidPending = function() { + this.options.forbidPending = true; + return this; +}; + +/** + * Mocha version as specified by "package.json". + * + * @name Mocha#version + * @type string + * @readonly + */ +Object.defineProperty(Mocha.prototype, 'version', { + value: require('../package.json').version, + configurable: false, + enumerable: true, + writable: false +}); + +/** + * Callback to be invoked when test execution is complete. + * + * @callback DoneCB + * @param {number} failures - Number of failures that occurred. + */ + +/** + * Runs root suite and invokes `fn()` when complete. + * + * @description + * To run tests multiple times (or to run tests in files that are + * already in the `require` cache), make sure to clear them from + * the cache first! + * + * @public + * @see {@link Mocha#loadFiles} + * @see {@link Mocha#unloadFiles} + * @see {@link Runner#run} + * @param {DoneCB} [fn] - Callback invoked when test execution completed. + * @return {Runner} runner instance + */ +Mocha.prototype.run = function(fn) { + if (this.files.length) { + this.loadFiles(); + } + var suite = this.suite; + var options = this.options; + options.files = this.files; + var runner = new exports.Runner(suite, options.delay); + createStatsCollector(runner); + var reporter = new this._reporter(runner, options); + runner.ignoreLeaks = options.ignoreLeaks !== false; + runner.fullStackTrace = options.fullStackTrace; + runner.asyncOnly = options.asyncOnly; + runner.allowUncaught = options.allowUncaught; + runner.forbidOnly = options.forbidOnly; + runner.forbidPending = options.forbidPending; + if (options.grep) { + runner.grep(options.grep, options.invert); + } + if (options.globals) { + runner.globals(options.globals); + } + if (options.growl) { + this._growl(runner); + } + if (options.useColors !== undefined) { + exports.reporters.Base.useColors = options.useColors; + } + exports.reporters.Base.inlineDiffs = options.useInlineDiffs; + exports.reporters.Base.hideDiff = options.hideDiff; + + function done(failures) { + fn = fn || utils.noop; + if (reporter.done) { + reporter.done(failures, fn); + } else { + fn(failures); + } + } + + return runner.run(done); +}; + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../package.json":90,"./context":5,"./errors":6,"./growl":2,"./hook":7,"./interfaces":11,"./mocharc.json":15,"./reporters":21,"./runnable":33,"./runner":34,"./stats-collector":35,"./suite":36,"./test":37,"./utils":38,"_process":69,"escape-string-regexp":49,"path":42}],15:[function(require,module,exports){ +module.exports={ + "diff": true, + "extension": ["js"], + "opts": "./test/mocha.opts", + "package": "./package.json", + "reporter": "spec", + "slow": 75, + "timeout": 2000, + "ui": "bdd" +} + +},{}],16:[function(require,module,exports){ +'use strict'; + +module.exports = Pending; + +/** + * Initialize a new `Pending` error with the given message. + * + * @param {string} message + */ +function Pending(message) { + this.message = message; +} + +},{}],17:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module Base + */ +/** + * Module dependencies. + */ + +var tty = require('tty'); +var diff = require('diff'); +var milliseconds = require('ms'); +var utils = require('../utils'); +var supportsColor = process.browser ? null : require('supports-color'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; + +/** + * Expose `Base`. + */ + +exports = module.exports = Base; + +/** + * Check if both stdio streams are associated with a tty. + */ + +var isatty = tty.isatty(1) && tty.isatty(2); + +/** + * Enable coloring by default, except in the browser interface. + */ + +exports.useColors = + !process.browser && + (supportsColor.stdout || process.env.MOCHA_COLORS !== undefined); + +/** + * Inline diffs instead of +/- + */ + +exports.inlineDiffs = false; + +/** + * Default color map. + */ + +exports.colors = { + pass: 90, + fail: 31, + 'bright pass': 92, + 'bright fail': 91, + 'bright yellow': 93, + pending: 36, + suite: 0, + 'error title': 0, + 'error message': 31, + 'error stack': 90, + checkmark: 32, + fast: 90, + medium: 33, + slow: 31, + green: 32, + light: 90, + 'diff gutter': 90, + 'diff added': 32, + 'diff removed': 31 +}; + +/** + * Default symbol map. + */ + +exports.symbols = { + ok: '✓', + err: '✖', + dot: '․', + comma: ',', + bang: '!' +}; + +// With node.js on Windows: use symbols available in terminal default fonts +if (process.platform === 'win32') { + exports.symbols.ok = '\u221A'; + exports.symbols.err = '\u00D7'; + exports.symbols.dot = '.'; +} + +/** + * Color `str` with the given `type`, + * allowing colors to be disabled, + * as well as user-defined color + * schemes. + * + * @private + * @param {string} type + * @param {string} str + * @return {string} + */ +var color = (exports.color = function(type, str) { + if (!exports.useColors) { + return String(str); + } + return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; +}); + +/** + * Expose term window size, with some defaults for when stderr is not a tty. + */ + +exports.window = { + width: 75 +}; + +if (isatty) { + exports.window.width = process.stdout.getWindowSize + ? process.stdout.getWindowSize(1)[0] + : tty.getWindowSize()[1]; +} + +/** + * Expose some basic cursor interactions that are common among reporters. + */ + +exports.cursor = { + hide: function() { + isatty && process.stdout.write('\u001b[?25l'); + }, + + show: function() { + isatty && process.stdout.write('\u001b[?25h'); + }, + + deleteLine: function() { + isatty && process.stdout.write('\u001b[2K'); + }, + + beginningOfLine: function() { + isatty && process.stdout.write('\u001b[0G'); + }, + + CR: function() { + if (isatty) { + exports.cursor.deleteLine(); + exports.cursor.beginningOfLine(); + } else { + process.stdout.write('\r'); + } + } +}; + +function showDiff(err) { + return ( + err && + err.showDiff !== false && + sameType(err.actual, err.expected) && + err.expected !== undefined + ); +} + +function stringifyDiffObjs(err) { + if (!utils.isString(err.actual) || !utils.isString(err.expected)) { + err.actual = utils.stringify(err.actual); + err.expected = utils.stringify(err.expected); + } +} + +/** + * Returns a diff between 2 strings with coloured ANSI output. + * + * @description + * The diff will be either inline or unified dependent on the value + * of `Base.inlineDiff`. + * + * @param {string} actual + * @param {string} expected + * @return {string} Diff + */ +var generateDiff = (exports.generateDiff = function(actual, expected) { + return exports.inlineDiffs + ? inlineDiff(actual, expected) + : unifiedDiff(actual, expected); +}); + +/** + * Outputs the given `failures` as a list. + * + * @public + * @memberof Mocha.reporters.Base + * @variation 1 + * @param {Object[]} failures - Each is Test instance with corresponding + * Error property + */ +exports.list = function(failures) { + console.log(); + failures.forEach(function(test, i) { + // format + var fmt = + color('error title', ' %s) %s:\n') + + color('error message', ' %s') + + color('error stack', '\n%s\n'); + + // msg + var msg; + var err = test.err; + var message; + if (err.message && typeof err.message.toString === 'function') { + message = err.message + ''; + } else if (typeof err.inspect === 'function') { + message = err.inspect() + ''; + } else { + message = ''; + } + var stack = err.stack || message; + var index = message ? stack.indexOf(message) : -1; + + if (index === -1) { + msg = message; + } else { + index += message.length; + msg = stack.slice(0, index); + // remove msg from stack + stack = stack.slice(index + 1); + } + + // uncaught + if (err.uncaught) { + msg = 'Uncaught ' + msg; + } + // explicitly show diff + if (!exports.hideDiff && showDiff(err)) { + stringifyDiffObjs(err); + fmt = + color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); + var match = message.match(/^([^:]+): expected/); + msg = '\n ' + color('error message', match ? match[1] : msg); + + msg += generateDiff(err.actual, err.expected); + } + + // indent stack trace + stack = stack.replace(/^/gm, ' '); + + // indented test title + var testTitle = ''; + test.titlePath().forEach(function(str, index) { + if (index !== 0) { + testTitle += '\n '; + } + for (var i = 0; i < index; i++) { + testTitle += ' '; + } + testTitle += str; + }); + + console.log(fmt, i + 1, testTitle, msg, stack); + }); +}; + +/** + * Constructs a new `Base` reporter instance. + * + * @description + * All other reporters generally inherit from this reporter. + * + * @public + * @class + * @memberof Mocha.reporters + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Base(runner, options) { + var failures = (this.failures = []); + + if (!runner) { + throw new TypeError('Missing runner argument'); + } + this.options = options || {}; + this.runner = runner; + this.stats = runner.stats; // assigned so Reporters keep a closer reference + + runner.on(EVENT_TEST_PASS, function(test) { + if (test.duration > test.slow()) { + test.speed = 'slow'; + } else if (test.duration > test.slow() / 2) { + test.speed = 'medium'; + } else { + test.speed = 'fast'; + } + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + if (showDiff(err)) { + stringifyDiffObjs(err); + } + test.err = err; + failures.push(test); + }); +} + +/** + * Outputs common epilogue used by many of the bundled reporters. + * + * @public + * @memberof Mocha.reporters.Base + */ +Base.prototype.epilogue = function() { + var stats = this.stats; + var fmt; + + console.log(); + + // passes + fmt = + color('bright pass', ' ') + + color('green', ' %d passing') + + color('light', ' (%s)'); + + console.log(fmt, stats.passes || 0, milliseconds(stats.duration)); + + // pending + if (stats.pending) { + fmt = color('pending', ' ') + color('pending', ' %d pending'); + + console.log(fmt, stats.pending); + } + + // failures + if (stats.failures) { + fmt = color('fail', ' %d failing'); + + console.log(fmt, stats.failures); + + Base.list(this.failures); + console.log(); + } + + console.log(); +}; + +/** + * Pads the given `str` to `len`. + * + * @private + * @param {string} str + * @param {string} len + * @return {string} + */ +function pad(str, len) { + str = String(str); + return Array(len - str.length + 1).join(' ') + str; +} + +/** + * Returns inline diff between 2 strings with coloured ANSI output. + * + * @private + * @param {String} actual + * @param {String} expected + * @return {string} Diff + */ +function inlineDiff(actual, expected) { + var msg = errorDiff(actual, expected); + + // linenos + var lines = msg.split('\n'); + if (lines.length > 4) { + var width = String(lines.length).length; + msg = lines + .map(function(str, i) { + return pad(++i, width) + ' |' + ' ' + str; + }) + .join('\n'); + } + + // legend + msg = + '\n' + + color('diff removed', 'actual') + + ' ' + + color('diff added', 'expected') + + '\n\n' + + msg + + '\n'; + + // indent + msg = msg.replace(/^/gm, ' '); + return msg; +} + +/** + * Returns unified diff between two strings with coloured ANSI output. + * + * @private + * @param {String} actual + * @param {String} expected + * @return {string} The diff. + */ +function unifiedDiff(actual, expected) { + var indent = ' '; + function cleanUp(line) { + if (line[0] === '+') { + return indent + colorLines('diff added', line); + } + if (line[0] === '-') { + return indent + colorLines('diff removed', line); + } + if (line.match(/@@/)) { + return '--'; + } + if (line.match(/\\ No newline/)) { + return null; + } + return indent + line; + } + function notBlank(line) { + return typeof line !== 'undefined' && line !== null; + } + var msg = diff.createPatch('string', actual, expected); + var lines = msg.split('\n').splice(5); + return ( + '\n ' + + colorLines('diff added', '+ expected') + + ' ' + + colorLines('diff removed', '- actual') + + '\n\n' + + lines + .map(cleanUp) + .filter(notBlank) + .join('\n') + ); +} + +/** + * Returns character diff for `err`. + * + * @private + * @param {String} actual + * @param {String} expected + * @return {string} the diff + */ +function errorDiff(actual, expected) { + return diff + .diffWordsWithSpace(actual, expected) + .map(function(str) { + if (str.added) { + return colorLines('diff added', str.value); + } + if (str.removed) { + return colorLines('diff removed', str.value); + } + return str.value; + }) + .join(''); +} + +/** + * Colors lines for `str`, using the color `name`. + * + * @private + * @param {string} name + * @param {string} str + * @return {string} + */ +function colorLines(name, str) { + return str + .split('\n') + .map(function(str) { + return color(name, str); + }) + .join('\n'); +} + +/** + * Object#toString reference. + */ +var objToString = Object.prototype.toString; + +/** + * Checks that a / b have the same type. + * + * @private + * @param {Object} a + * @param {Object} b + * @return {boolean} + */ +function sameType(a, b) { + return objToString.call(a) === objToString.call(b); +} + +Base.abstract = true; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"_process":69,"diff":48,"ms":60,"supports-color":42,"tty":4}],18:[function(require,module,exports){ +'use strict'; +/** + * @module Doc + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; + +/** + * Expose `Doc`. + */ + +exports = module.exports = Doc; + +/** + * Constructs a new `Doc` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Doc(runner, options) { + Base.call(this, runner, options); + + var indents = 2; + + function indent() { + return Array(indents).join(' '); + } + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + if (suite.root) { + return; + } + ++indents; + console.log('%s
    ', indent()); + ++indents; + console.log('%s

    %s

    ', indent(), utils.escape(suite.title)); + console.log('%s
    ', indent()); + }); + + runner.on(EVENT_SUITE_END, function(suite) { + if (suite.root) { + return; + } + console.log('%s
    ', indent()); + --indents; + console.log('%s
    ', indent()); + --indents; + }); + + runner.on(EVENT_TEST_PASS, function(test) { + console.log('%s
    %s
    ', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.body)); + console.log('%s
    %s
    ', indent(), code); + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + console.log( + '%s
    %s
    ', + indent(), + utils.escape(test.title) + ); + var code = utils.escape(utils.clean(test.body)); + console.log( + '%s
    %s
    ', + indent(), + code + ); + console.log('%s
    %s
    ', indent(), utils.escape(err)); + }); +} + +Doc.description = 'HTML documentation'; + +},{"../runner":34,"../utils":38,"./base":17}],19:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module Dot + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_RUN_END = constants.EVENT_RUN_END; + +/** + * Expose `Dot`. + */ + +exports = module.exports = Dot; + +/** + * Constructs a new `Dot` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Dot(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.75) | 0; + var n = -1; + + runner.on(EVENT_RUN_BEGIN, function() { + process.stdout.write('\n'); + }); + + runner.on(EVENT_TEST_PENDING, function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(Base.color('pending', Base.symbols.comma)); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + if (++n % width === 0) { + process.stdout.write('\n '); + } + if (test.speed === 'slow') { + process.stdout.write(Base.color('bright yellow', Base.symbols.dot)); + } else { + process.stdout.write(Base.color(test.speed, Base.symbols.dot)); + } + }); + + runner.on(EVENT_TEST_FAIL, function() { + if (++n % width === 0) { + process.stdout.write('\n '); + } + process.stdout.write(Base.color('fail', Base.symbols.bang)); + }); + + runner.once(EVENT_RUN_END, function() { + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Dot, Base); + +Dot.description = 'dot matrix representation'; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"./base":17,"_process":69}],20:[function(require,module,exports){ +(function (global){ +'use strict'; + +/* eslint-env browser */ +/** + * @module HTML + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var Progress = require('../browser/progress'); +var escapeRe = require('escape-string-regexp'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ + +var Date = global.Date; + +/** + * Expose `HTML`. + */ + +exports = module.exports = HTML; + +/** + * Stats template. + */ + +var statsTemplate = + ''; + +var playIcon = '‣'; + +/** + * Constructs a new `HTML` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function HTML(runner, options) { + Base.call(this, runner, options); + + var self = this; + var stats = this.stats; + var stat = fragment(statsTemplate); + var items = stat.getElementsByTagName('li'); + var passes = items[1].getElementsByTagName('em')[0]; + var passesLink = items[1].getElementsByTagName('a')[0]; + var failures = items[2].getElementsByTagName('em')[0]; + var failuresLink = items[2].getElementsByTagName('a')[0]; + var duration = items[3].getElementsByTagName('em')[0]; + var canvas = stat.getElementsByTagName('canvas')[0]; + var report = fragment('
      '); + var stack = [report]; + var progress; + var ctx; + var root = document.getElementById('mocha'); + + if (canvas.getContext) { + var ratio = window.devicePixelRatio || 1; + canvas.style.width = canvas.width; + canvas.style.height = canvas.height; + canvas.width *= ratio; + canvas.height *= ratio; + ctx = canvas.getContext('2d'); + ctx.scale(ratio, ratio); + progress = new Progress(); + } + + if (!root) { + return error('#mocha div missing, add it to your document'); + } + + // pass toggle + on(passesLink, 'click', function(evt) { + evt.preventDefault(); + unhide(); + var name = /pass/.test(report.className) ? '' : ' pass'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test pass'); + } + }); + + // failure toggle + on(failuresLink, 'click', function(evt) { + evt.preventDefault(); + unhide(); + var name = /fail/.test(report.className) ? '' : ' fail'; + report.className = report.className.replace(/fail|pass/g, '') + name; + if (report.className.trim()) { + hideSuitesWithout('test fail'); + } + }); + + root.appendChild(stat); + root.appendChild(report); + + if (progress) { + progress.size(40); + } + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + if (suite.root) { + return; + } + + // suite + var url = self.suiteURL(suite); + var el = fragment( + '
    • %s

    • ', + url, + escape(suite.title) + ); + + // container + stack[0].appendChild(el); + stack.unshift(document.createElement('ul')); + el.appendChild(stack[0]); + }); + + runner.on(EVENT_SUITE_END, function(suite) { + if (suite.root) { + updateStats(); + return; + } + stack.shift(); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var url = self.testURL(test); + var markup = + '
    • %e%ems ' + + '' + + playIcon + + '

    • '; + var el = fragment(markup, test.speed, test.title, test.duration, url); + self.addCodeToggle(el, test.body); + appendToStack(el); + updateStats(); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + var el = fragment( + '
    • %e ' + + playIcon + + '

    • ', + test.title, + self.testURL(test) + ); + var stackString; // Note: Includes leading newline + var message = test.err.toString(); + + // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we + // check for the result of the stringifying. + if (message === '[object Error]') { + message = test.err.message; + } + + if (test.err.stack) { + var indexOfMessage = test.err.stack.indexOf(test.err.message); + if (indexOfMessage === -1) { + stackString = test.err.stack; + } else { + stackString = test.err.stack.substr( + test.err.message.length + indexOfMessage + ); + } + } else if (test.err.sourceURL && test.err.line !== undefined) { + // Safari doesn't give you a stack. Let's at least provide a source line. + stackString = '\n(' + test.err.sourceURL + ':' + test.err.line + ')'; + } + + stackString = stackString || ''; + + if (test.err.htmlMessage && stackString) { + el.appendChild( + fragment( + '
      %s\n
      %e
      ', + test.err.htmlMessage, + stackString + ) + ); + } else if (test.err.htmlMessage) { + el.appendChild( + fragment('
      %s
      ', test.err.htmlMessage) + ); + } else { + el.appendChild( + fragment('
      %e%e
      ', message, stackString) + ); + } + + self.addCodeToggle(el, test.body); + appendToStack(el); + updateStats(); + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + var el = fragment( + '
    • %e

    • ', + test.title + ); + appendToStack(el); + updateStats(); + }); + + function appendToStack(el) { + // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. + if (stack[0]) { + stack[0].appendChild(el); + } + } + + function updateStats() { + // TODO: add to stats + var percent = ((stats.tests / runner.total) * 100) | 0; + if (progress) { + progress.update(percent).draw(ctx); + } + + // update stats + var ms = new Date() - stats.start; + text(passes, stats.passes); + text(failures, stats.failures); + text(duration, (ms / 1000).toFixed(2)); + } +} + +/** + * Makes a URL, preserving querystring ("search") parameters. + * + * @param {string} s + * @return {string} A new URL. + */ +function makeUrl(s) { + var search = window.location.search; + + // Remove previous grep query parameter if present + if (search) { + search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?'); + } + + return ( + window.location.pathname + + (search ? search + '&' : '?') + + 'grep=' + + encodeURIComponent(escapeRe(s)) + ); +} + +/** + * Provide suite URL. + * + * @param {Object} [suite] + */ +HTML.prototype.suiteURL = function(suite) { + return makeUrl(suite.fullTitle()); +}; + +/** + * Provide test URL. + * + * @param {Object} [test] + */ +HTML.prototype.testURL = function(test) { + return makeUrl(test.fullTitle()); +}; + +/** + * Adds code toggle functionality for the provided test's list element. + * + * @param {HTMLLIElement} el + * @param {string} contents + */ +HTML.prototype.addCodeToggle = function(el, contents) { + var h2 = el.getElementsByTagName('h2')[0]; + + on(h2, 'click', function() { + pre.style.display = pre.style.display === 'none' ? 'block' : 'none'; + }); + + var pre = fragment('
      %e
      ', utils.clean(contents)); + el.appendChild(pre); + pre.style.display = 'none'; +}; + +/** + * Display error `msg`. + * + * @param {string} msg + */ +function error(msg) { + document.body.appendChild(fragment('
      %s
      ', msg)); +} + +/** + * Return a DOM fragment from `html`. + * + * @param {string} html + */ +function fragment(html) { + var args = arguments; + var div = document.createElement('div'); + var i = 1; + + div.innerHTML = html.replace(/%([se])/g, function(_, type) { + switch (type) { + case 's': + return String(args[i++]); + case 'e': + return escape(args[i++]); + // no default + } + }); + + return div.firstChild; +} + +/** + * Check for suites that do not have elements + * with `classname`, and hide them. + * + * @param {text} classname + */ +function hideSuitesWithout(classname) { + var suites = document.getElementsByClassName('suite'); + for (var i = 0; i < suites.length; i++) { + var els = suites[i].getElementsByClassName(classname); + if (!els.length) { + suites[i].className += ' hidden'; + } + } +} + +/** + * Unhide .hidden suites. + */ +function unhide() { + var els = document.getElementsByClassName('suite hidden'); + for (var i = 0; i < els.length; ++i) { + els[i].className = els[i].className.replace('suite hidden', 'suite'); + } +} + +/** + * Set an element's text contents. + * + * @param {HTMLElement} el + * @param {string} contents + */ +function text(el, contents) { + if (el.textContent) { + el.textContent = contents; + } else { + el.innerText = contents; + } +} + +/** + * Listen on `event` with callback `fn`. + */ +function on(el, event, fn) { + if (el.addEventListener) { + el.addEventListener(event, fn, false); + } else { + el.attachEvent('on' + event, fn); + } +} + +HTML.browserOnly = true; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../browser/progress":3,"../runner":34,"../utils":38,"./base":17,"escape-string-regexp":49}],21:[function(require,module,exports){ +'use strict'; + +// Alias exports to a their normalized format Mocha#reporter to prevent a need +// for dynamic (try/catch) requires, which Browserify doesn't handle. +exports.Base = exports.base = require('./base'); +exports.Dot = exports.dot = require('./dot'); +exports.Doc = exports.doc = require('./doc'); +exports.TAP = exports.tap = require('./tap'); +exports.JSON = exports.json = require('./json'); +exports.HTML = exports.html = require('./html'); +exports.List = exports.list = require('./list'); +exports.Min = exports.min = require('./min'); +exports.Spec = exports.spec = require('./spec'); +exports.Nyan = exports.nyan = require('./nyan'); +exports.XUnit = exports.xunit = require('./xunit'); +exports.Markdown = exports.markdown = require('./markdown'); +exports.Progress = exports.progress = require('./progress'); +exports.Landing = exports.landing = require('./landing'); +exports.JSONStream = exports['json-stream'] = require('./json-stream'); + +},{"./base":17,"./doc":18,"./dot":19,"./html":20,"./json":23,"./json-stream":22,"./landing":24,"./list":25,"./markdown":26,"./min":27,"./nyan":28,"./progress":29,"./spec":30,"./tap":31,"./xunit":32}],22:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module JSONStream + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; + +/** + * Expose `JSONStream`. + */ + +exports = module.exports = JSONStream; + +/** + * Constructs a new `JSONStream` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function JSONStream(runner, options) { + Base.call(this, runner, options); + + var self = this; + var total = runner.total; + + runner.once(EVENT_RUN_BEGIN, function() { + writeEvent(['start', {total: total}]); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + writeEvent(['pass', clean(test)]); + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + test = clean(test); + test.err = err.message; + test.stack = err.stack || null; + writeEvent(['fail', test]); + }); + + runner.once(EVENT_RUN_END, function() { + writeEvent(['end', self.stats]); + }); +} + +/** + * Mocha event to be written to the output stream. + * @typedef {Array} JSONStream~MochaEvent + */ + +/** + * Writes Mocha event to reporter output stream. + * + * @private + * @param {JSONStream~MochaEvent} event - Mocha event to be output. + */ +function writeEvent(event) { + process.stdout.write(JSON.stringify(event) + '\n'); +} + +/** + * Returns an object literal representation of `test` + * free of cyclic properties, etc. + * + * @private + * @param {Test} test - Instance used as data source. + * @return {Object} object containing pared-down test instance data + */ +function clean(test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + currentRetry: test.currentRetry() + }; +} + +JSONStream.description = 'newline delimited JSON events'; + +}).call(this,require('_process')) +},{"../runner":34,"./base":17,"_process":69}],23:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module JSON + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; + +/** + * Expose `JSON`. + */ + +exports = module.exports = JSONReporter; + +/** + * Constructs a new `JSON` reporter instance. + * + * @public + * @class JSON + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function JSONReporter(runner, options) { + Base.call(this, runner, options); + + var self = this; + var tests = []; + var pending = []; + var failures = []; + var passes = []; + + runner.on(EVENT_TEST_END, function(test) { + tests.push(test); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + passes.push(test); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + failures.push(test); + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + pending.push(test); + }); + + runner.once(EVENT_RUN_END, function() { + var obj = { + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) + }; + + runner.testResults = obj; + + process.stdout.write(JSON.stringify(obj, null, 2)); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @private + * @param {Object} test + * @return {Object} + */ +function clean(test) { + var err = test.err || {}; + if (err instanceof Error) { + err = errorJSON(err); + } + + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + currentRetry: test.currentRetry(), + err: cleanCycles(err) + }; +} + +/** + * Replaces any circular references inside `obj` with '[object Object]' + * + * @private + * @param {Object} obj + * @return {Object} + */ +function cleanCycles(obj) { + var cache = []; + return JSON.parse( + JSON.stringify(obj, function(key, value) { + if (typeof value === 'object' && value !== null) { + if (cache.indexOf(value) !== -1) { + // Instead of going in a circle, we'll print [object Object] + return '' + value; + } + cache.push(value); + } + + return value; + }) + ); +} + +/** + * Transform an Error object into a JSON object. + * + * @private + * @param {Error} err + * @return {Object} + */ +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} + +JSONReporter.description = 'single JSON object'; + +}).call(this,require('_process')) +},{"../runner":34,"./base":17,"_process":69}],24:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module Landing + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var STATE_FAILED = require('../runnable').constants.STATE_FAILED; + +var cursor = Base.cursor; +var color = Base.color; + +/** + * Expose `Landing`. + */ + +exports = module.exports = Landing; + +/** + * Airplane color. + */ + +Base.colors.plane = 0; + +/** + * Airplane crash color. + */ + +Base.colors['plane crash'] = 31; + +/** + * Runway color. + */ + +Base.colors.runway = 90; + +/** + * Constructs a new `Landing` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Landing(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.75) | 0; + var total = runner.total; + var stream = process.stdout; + var plane = color('plane', '✈'); + var crashed = -1; + var n = 0; + + function runway() { + var buf = Array(width).join('-'); + return ' ' + color('runway', buf); + } + + runner.on(EVENT_RUN_BEGIN, function() { + stream.write('\n\n\n '); + cursor.hide(); + }); + + runner.on(EVENT_TEST_END, function(test) { + // check if the plane crashed + var col = crashed === -1 ? ((width * ++n) / total) | 0 : crashed; + + // show the crash + if (test.state === STATE_FAILED) { + plane = color('plane crash', '✈'); + crashed = col; + } + + // render landing strip + stream.write('\u001b[' + (width + 1) + 'D\u001b[2A'); + stream.write(runway()); + stream.write('\n '); + stream.write(color('runway', Array(col).join('⋅'))); + stream.write(plane); + stream.write(color('runway', Array(width - col).join('⋅') + '\n')); + stream.write(runway()); + stream.write('\u001b[0m'); + }); + + runner.once(EVENT_RUN_END, function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Landing, Base); + +Landing.description = 'Unicode landing strip'; + +}).call(this,require('_process')) +},{"../runnable":33,"../runner":34,"../utils":38,"./base":17,"_process":69}],25:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module List + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `List`. + */ + +exports = module.exports = List; + +/** + * Constructs a new `List` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function List(runner, options) { + Base.call(this, runner, options); + + var self = this; + var n = 0; + + runner.on(EVENT_RUN_BEGIN, function() { + console.log(); + }); + + runner.on(EVENT_TEST_BEGIN, function(test) { + process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + var fmt = color('checkmark', ' -') + color('pending', ' %s'); + console.log(fmt, test.fullTitle()); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var fmt = + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s: ') + + color(test.speed, '%dms'); + cursor.CR(); + console.log(fmt, test.fullTitle(), test.duration); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + cursor.CR(); + console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + }); + + runner.once(EVENT_RUN_END, self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(List, Base); + +List.description = 'like "spec" reporter but flat'; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"./base":17,"_process":69}],26:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module Markdown + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var constants = require('../runner').constants; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; + +/** + * Constants + */ + +var SUITE_PREFIX = '$'; + +/** + * Expose `Markdown`. + */ + +exports = module.exports = Markdown; + +/** + * Constructs a new `Markdown` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Markdown(runner, options) { + Base.call(this, runner, options); + + var level = 0; + var buf = ''; + + function title(str) { + return Array(level).join('#') + ' ' + str; + } + + function mapTOC(suite, obj) { + var ret = obj; + var key = SUITE_PREFIX + suite.title; + + obj = obj[key] = obj[key] || {suite: suite}; + suite.suites.forEach(function(suite) { + mapTOC(suite, obj); + }); + + return ret; + } + + function stringifyTOC(obj, level) { + ++level; + var buf = ''; + var link; + for (var key in obj) { + if (key === 'suite') { + continue; + } + if (key !== SUITE_PREFIX) { + link = ' - [' + key.substring(1) + ']'; + link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; + buf += Array(level).join(' ') + link; + } + buf += stringifyTOC(obj[key], level); + } + return buf; + } + + function generateTOC(suite) { + var obj = mapTOC(suite, {}); + return stringifyTOC(obj, 0); + } + + generateTOC(runner.suite); + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + ++level; + var slug = utils.slug(suite.fullTitle()); + buf += '' + '\n'; + buf += title(suite.title) + '\n'; + }); + + runner.on(EVENT_SUITE_END, function() { + --level; + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var code = utils.clean(test.body); + buf += test.title + '.\n'; + buf += '\n```js\n'; + buf += code + '\n'; + buf += '```\n\n'; + }); + + runner.once(EVENT_RUN_END, function() { + process.stdout.write('# TOC\n'); + process.stdout.write(generateTOC(runner.suite)); + process.stdout.write(buf); + }); +} + +Markdown.description = 'GitHub Flavored Markdown'; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"./base":17,"_process":69}],27:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module Min + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var inherits = require('../utils').inherits; +var constants = require('../runner').constants; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; + +/** + * Expose `Min`. + */ + +exports = module.exports = Min; + +/** + * Constructs a new `Min` reporter instance. + * + * @description + * This minimal test reporter is best used with '--watch'. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Min(runner, options) { + Base.call(this, runner, options); + + runner.on(EVENT_RUN_BEGIN, function() { + // clear screen + process.stdout.write('\u001b[2J'); + // set cursor position + process.stdout.write('\u001b[1;3H'); + }); + + runner.once(EVENT_RUN_END, this.epilogue.bind(this)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Min, Base); + +Min.description = 'essentially just a summary'; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"./base":17,"_process":69}],28:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module Nyan + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var inherits = require('../utils').inherits; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; + +/** + * Expose `Dot`. + */ + +exports = module.exports = NyanCat; + +/** + * Constructs a new `Nyan` reporter instance. + * + * @public + * @class Nyan + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function NyanCat(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.75) | 0; + var nyanCatWidth = (this.nyanCatWidth = 11); + + this.colorIndex = 0; + this.numberOfLines = 4; + this.rainbowColors = self.generateColors(); + this.scoreboardWidth = 5; + this.tick = 0; + this.trajectories = [[], [], [], []]; + this.trajectoryWidthMax = width - nyanCatWidth; + + runner.on(EVENT_RUN_BEGIN, function() { + Base.cursor.hide(); + self.draw(); + }); + + runner.on(EVENT_TEST_PENDING, function() { + self.draw(); + }); + + runner.on(EVENT_TEST_PASS, function() { + self.draw(); + }); + + runner.on(EVENT_TEST_FAIL, function() { + self.draw(); + }); + + runner.once(EVENT_RUN_END, function() { + Base.cursor.show(); + for (var i = 0; i < self.numberOfLines; i++) { + write('\n'); + } + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(NyanCat, Base); + +/** + * Draw the nyan cat + * + * @private + */ + +NyanCat.prototype.draw = function() { + this.appendRainbow(); + this.drawScoreboard(); + this.drawRainbow(); + this.drawNyanCat(); + this.tick = !this.tick; +}; + +/** + * Draw the "scoreboard" showing the number + * of passes, failures and pending tests. + * + * @private + */ + +NyanCat.prototype.drawScoreboard = function() { + var stats = this.stats; + + function draw(type, n) { + write(' '); + write(Base.color(type, n)); + write('\n'); + } + + draw('green', stats.passes); + draw('fail', stats.failures); + draw('pending', stats.pending); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Append the rainbow. + * + * @private + */ + +NyanCat.prototype.appendRainbow = function() { + var segment = this.tick ? '_' : '-'; + var rainbowified = this.rainbowify(segment); + + for (var index = 0; index < this.numberOfLines; index++) { + var trajectory = this.trajectories[index]; + if (trajectory.length >= this.trajectoryWidthMax) { + trajectory.shift(); + } + trajectory.push(rainbowified); + } +}; + +/** + * Draw the rainbow. + * + * @private + */ + +NyanCat.prototype.drawRainbow = function() { + var self = this; + + this.trajectories.forEach(function(line) { + write('\u001b[' + self.scoreboardWidth + 'C'); + write(line.join('')); + write('\n'); + }); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw the nyan cat + * + * @private + */ +NyanCat.prototype.drawNyanCat = function() { + var self = this; + var startWidth = this.scoreboardWidth + this.trajectories[0].length; + var dist = '\u001b[' + startWidth + 'C'; + var padding = ''; + + write(dist); + write('_,------,'); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write('_|' + padding + '/\\_/\\ '); + write('\n'); + + write(dist); + padding = self.tick ? '_' : '__'; + var tail = self.tick ? '~' : '^'; + write(tail + '|' + padding + this.face() + ' '); + write('\n'); + + write(dist); + padding = self.tick ? ' ' : ' '; + write(padding + '"" "" '); + write('\n'); + + this.cursorUp(this.numberOfLines); +}; + +/** + * Draw nyan cat face. + * + * @private + * @return {string} + */ + +NyanCat.prototype.face = function() { + var stats = this.stats; + if (stats.failures) { + return '( x .x)'; + } else if (stats.pending) { + return '( o .o)'; + } else if (stats.passes) { + return '( ^ .^)'; + } + return '( - .-)'; +}; + +/** + * Move cursor up `n`. + * + * @private + * @param {number} n + */ + +NyanCat.prototype.cursorUp = function(n) { + write('\u001b[' + n + 'A'); +}; + +/** + * Move cursor down `n`. + * + * @private + * @param {number} n + */ + +NyanCat.prototype.cursorDown = function(n) { + write('\u001b[' + n + 'B'); +}; + +/** + * Generate rainbow colors. + * + * @private + * @return {Array} + */ +NyanCat.prototype.generateColors = function() { + var colors = []; + + for (var i = 0; i < 6 * 7; i++) { + var pi3 = Math.floor(Math.PI / 3); + var n = i * (1.0 / 6); + var r = Math.floor(3 * Math.sin(n) + 3); + var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); + var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); + colors.push(36 * r + 6 * g + b + 16); + } + + return colors; +}; + +/** + * Apply rainbow to the given `str`. + * + * @private + * @param {string} str + * @return {string} + */ +NyanCat.prototype.rainbowify = function(str) { + if (!Base.useColors) { + return str; + } + var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; + this.colorIndex += 1; + return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; +}; + +/** + * Stdout helper. + * + * @param {string} string A message to write to stdout. + */ +function write(string) { + process.stdout.write(string); +} + +NyanCat.description = '"nyan cat"'; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"./base":17,"_process":69}],29:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module Progress + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var inherits = require('../utils').inherits; +var color = Base.color; +var cursor = Base.cursor; + +/** + * Expose `Progress`. + */ + +exports = module.exports = Progress; + +/** + * General progress bar color. + */ + +Base.colors.progress = 90; + +/** + * Constructs a new `Progress` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Progress(runner, options) { + Base.call(this, runner, options); + + var self = this; + var width = (Base.window.width * 0.5) | 0; + var total = runner.total; + var complete = 0; + var lastN = -1; + + // default chars + options = options || {}; + var reporterOptions = options.reporterOptions || {}; + + options.open = reporterOptions.open || '['; + options.complete = reporterOptions.complete || '▬'; + options.incomplete = reporterOptions.incomplete || Base.symbols.dot; + options.close = reporterOptions.close || ']'; + options.verbose = reporterOptions.verbose || false; + + // tests started + runner.on(EVENT_RUN_BEGIN, function() { + console.log(); + cursor.hide(); + }); + + // tests complete + runner.on(EVENT_TEST_END, function() { + complete++; + + var percent = complete / total; + var n = (width * percent) | 0; + var i = width - n; + + if (n === lastN && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + + cursor.CR(); + process.stdout.write('\u001b[J'); + process.stdout.write(color('progress', ' ' + options.open)); + process.stdout.write(Array(n).join(options.complete)); + process.stdout.write(Array(i).join(options.incomplete)); + process.stdout.write(color('progress', options.close)); + if (options.verbose) { + process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); + } + }); + + // tests are complete, output some stats + // and the failures if any + runner.once(EVENT_RUN_END, function() { + cursor.show(); + console.log(); + self.epilogue(); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Progress, Base); + +Progress.description = 'a progress bar'; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"./base":17,"_process":69}],30:[function(require,module,exports){ +'use strict'; +/** + * @module Spec + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_SUITE_END = constants.EVENT_SUITE_END; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var inherits = require('../utils').inherits; +var color = Base.color; + +/** + * Expose `Spec`. + */ + +exports = module.exports = Spec; + +/** + * Constructs a new `Spec` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function Spec(runner, options) { + Base.call(this, runner, options); + + var self = this; + var indents = 0; + var n = 0; + + function indent() { + return Array(indents).join(' '); + } + + runner.on(EVENT_RUN_BEGIN, function() { + console.log(); + }); + + runner.on(EVENT_SUITE_BEGIN, function(suite) { + ++indents; + console.log(color('suite', '%s%s'), indent(), suite.title); + }); + + runner.on(EVENT_SUITE_END, function() { + --indents; + if (indents === 1) { + console.log(); + } + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + var fmt = indent() + color('pending', ' - %s'); + console.log(fmt, test.title); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + var fmt; + if (test.speed === 'fast') { + fmt = + indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s'); + console.log(fmt, test.title); + } else { + fmt = + indent() + + color('checkmark', ' ' + Base.symbols.ok) + + color('pass', ' %s') + + color(test.speed, ' (%dms)'); + console.log(fmt, test.title, test.duration); + } + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + }); + + runner.once(EVENT_RUN_END, self.epilogue.bind(self)); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(Spec, Base); + +Spec.description = 'hierarchical & verbose [default]'; + +},{"../runner":34,"../utils":38,"./base":17}],31:[function(require,module,exports){ +(function (process){ +'use strict'; +/** + * @module TAP + */ +/** + * Module dependencies. + */ + +var util = require('util'); +var Base = require('./base'); +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_TEST_END = constants.EVENT_TEST_END; +var inherits = require('../utils').inherits; +var sprintf = util.format; + +/** + * Expose `TAP`. + */ + +exports = module.exports = TAP; + +/** + * Constructs a new `TAP` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function TAP(runner, options) { + Base.call(this, runner, options); + + var self = this; + var n = 1; + + var tapVersion = '12'; + if (options && options.reporterOptions) { + if (options.reporterOptions.tapVersion) { + tapVersion = options.reporterOptions.tapVersion.toString(); + } + } + + this._producer = createProducer(tapVersion); + + runner.once(EVENT_RUN_BEGIN, function() { + var ntests = runner.grepTotal(runner.suite); + self._producer.writeVersion(); + self._producer.writePlan(ntests); + }); + + runner.on(EVENT_TEST_END, function() { + ++n; + }); + + runner.on(EVENT_TEST_PENDING, function(test) { + self._producer.writePending(n, test); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + self._producer.writePass(n, test); + }); + + runner.on(EVENT_TEST_FAIL, function(test, err) { + self._producer.writeFail(n, test, err); + }); + + runner.once(EVENT_RUN_END, function() { + self._producer.writeEpilogue(runner.stats); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); + +/** + * Returns a TAP-safe title of `test`. + * + * @private + * @param {Test} test - Test instance. + * @return {String} title with any hash character removed + */ +function title(test) { + return test.fullTitle().replace(/#/g, ''); +} + +/** + * Writes newline-terminated formatted string to reporter output stream. + * + * @private + * @param {string} format - `printf`-like format string + * @param {...*} [varArgs] - Format string arguments + */ +function println(format, varArgs) { + var vargs = Array.from(arguments); + vargs[0] += '\n'; + process.stdout.write(sprintf.apply(null, vargs)); +} + +/** + * Returns a `tapVersion`-appropriate TAP producer instance, if possible. + * + * @private + * @param {string} tapVersion - Version of TAP specification to produce. + * @returns {TAPProducer} specification-appropriate instance + * @throws {Error} if specification version has no associated producer. + */ +function createProducer(tapVersion) { + var producers = { + '12': new TAP12Producer(), + '13': new TAP13Producer() + }; + var producer = producers[tapVersion]; + + if (!producer) { + throw new Error( + 'invalid or unsupported TAP version: ' + JSON.stringify(tapVersion) + ); + } + + return producer; +} + +/** + * @summary + * Constructs a new TAPProducer. + * + * @description + * Only to be used as an abstract base class. + * + * @private + * @constructor + */ +function TAPProducer() {} + +/** + * Writes the TAP version to reporter output stream. + * + * @abstract + */ +TAPProducer.prototype.writeVersion = function() {}; + +/** + * Writes the plan to reporter output stream. + * + * @abstract + * @param {number} ntests - Number of tests that are planned to run. + */ +TAPProducer.prototype.writePlan = function(ntests) { + println('%d..%d', 1, ntests); +}; + +/** + * Writes that test passed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that passed. + * @param {Test} test - Instance containing test information. + */ +TAPProducer.prototype.writePass = function(n, test) { + println('ok %d %s', n, title(test)); +}; + +/** + * Writes that test was skipped to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that was skipped. + * @param {Test} test - Instance containing test information. + */ +TAPProducer.prototype.writePending = function(n, test) { + println('ok %d %s # SKIP -', n, title(test)); +}; + +/** + * Writes that test failed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that failed. + * @param {Test} test - Instance containing test information. + * @param {Error} err - Reason the test failed. + */ +TAPProducer.prototype.writeFail = function(n, test, err) { + println('not ok %d %s', n, title(test)); +}; + +/** + * Writes the summary epilogue to reporter output stream. + * + * @abstract + * @param {Object} stats - Object containing run statistics. + */ +TAPProducer.prototype.writeEpilogue = function(stats) { + // :TBD: Why is this not counting pending tests? + println('# tests ' + (stats.passes + stats.failures)); + println('# pass ' + stats.passes); + // :TBD: Why are we not showing pending results? + println('# fail ' + stats.failures); +}; + +/** + * @summary + * Constructs a new TAP12Producer. + * + * @description + * Produces output conforming to the TAP12 specification. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-specification.html|Specification} + */ +function TAP12Producer() { + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); + if (err.message) { + println(err.message.replace(/^/gm, ' ')); + } + if (err.stack) { + println(err.stack.replace(/^/gm, ' ')); + } + }; +} + +/** + * Inherit from `TAPProducer.prototype`. + */ +inherits(TAP12Producer, TAPProducer); + +/** + * @summary + * Constructs a new TAP13Producer. + * + * @description + * Produces output conforming to the TAP13 specification. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-version-13-specification.html|Specification} + */ +function TAP13Producer() { + /** + * Writes the TAP version to reporter output stream. + * @override + */ + this.writeVersion = function() { + println('TAP version 13'); + }; + + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + println(indent(1) + '---'); + if (err.message) { + println(indent(2) + 'message: |-'); + println(err.message.replace(/^/gm, indent(3))); + } + if (err.stack) { + println(indent(2) + 'stack: |-'); + println(err.stack.replace(/^/gm, indent(3))); + } + println(indent(1) + '...'); + } + }; + + function indent(level) { + return Array(level + 1).join(' '); + } +} + +/** + * Inherit from `TAPProducer.prototype`. + */ +inherits(TAP13Producer, TAPProducer); + +TAP.description = 'TAP-compatible output'; + +}).call(this,require('_process')) +},{"../runner":34,"../utils":38,"./base":17,"_process":69,"util":89}],32:[function(require,module,exports){ +(function (process,global){ +'use strict'; +/** + * @module XUnit + */ +/** + * Module dependencies. + */ + +var Base = require('./base'); +var utils = require('../utils'); +var fs = require('fs'); +var mkdirp = require('mkdirp'); +var path = require('path'); +var errors = require('../errors'); +var createUnsupportedError = errors.createUnsupportedError; +var constants = require('../runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var STATE_FAILED = require('../runnable').constants.STATE_FAILED; +var inherits = utils.inherits; +var escape = utils.escape; + +/** + * Save timer references to avoid Sinon interfering (see GH-237). + */ +var Date = global.Date; + +/** + * Expose `XUnit`. + */ + +exports = module.exports = XUnit; + +/** + * Constructs a new `XUnit` reporter instance. + * + * @public + * @class + * @memberof Mocha.reporters + * @extends Mocha.reporters.Base + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options + */ +function XUnit(runner, options) { + Base.call(this, runner, options); + + var stats = this.stats; + var tests = []; + var self = this; + + // the name of the test suite, as it will appear in the resulting XML file + var suiteName; + + // the default name of the test suite if none is provided + var DEFAULT_SUITE_NAME = 'Mocha Tests'; + + if (options && options.reporterOptions) { + if (options.reporterOptions.output) { + if (!fs.createWriteStream) { + throw createUnsupportedError('file output not supported in browser'); + } + + mkdirp.sync(path.dirname(options.reporterOptions.output)); + self.fileStream = fs.createWriteStream(options.reporterOptions.output); + } + + // get the suite name from the reporter options (if provided) + suiteName = options.reporterOptions.suiteName; + } + + // fall back to the default suite name + suiteName = suiteName || DEFAULT_SUITE_NAME; + + runner.on(EVENT_TEST_PENDING, function(test) { + tests.push(test); + }); + + runner.on(EVENT_TEST_PASS, function(test) { + tests.push(test); + }); + + runner.on(EVENT_TEST_FAIL, function(test) { + tests.push(test); + }); + + runner.once(EVENT_RUN_END, function() { + self.write( + tag( + 'testsuite', + { + name: suiteName, + tests: stats.tests, + failures: 0, + errors: stats.failures, + skipped: stats.tests - stats.failures - stats.passes, + timestamp: new Date().toUTCString(), + time: stats.duration / 1000 || 0 + }, + false + ) + ); + + tests.forEach(function(t) { + self.test(t); + }); + + self.write(''); + }); +} + +/** + * Inherit from `Base.prototype`. + */ +inherits(XUnit, Base); + +/** + * Override done to close the stream (if it's a file). + * + * @param failures + * @param {Function} fn + */ +XUnit.prototype.done = function(failures, fn) { + if (this.fileStream) { + this.fileStream.end(function() { + fn(failures); + }); + } else { + fn(failures); + } +}; + +/** + * Write out the given line. + * + * @param {string} line + */ +XUnit.prototype.write = function(line) { + if (this.fileStream) { + this.fileStream.write(line + '\n'); + } else if (typeof process === 'object' && process.stdout) { + process.stdout.write(line + '\n'); + } else { + console.log(line); + } +}; + +/** + * Output tag for the given `test.` + * + * @param {Test} test + */ +XUnit.prototype.test = function(test) { + Base.useColors = false; + + var attrs = { + classname: test.parent.fullTitle(), + name: test.title, + time: test.duration / 1000 || 0 + }; + + if (test.state === STATE_FAILED) { + var err = test.err; + var diff = + Base.hideDiff || !err.actual || !err.expected + ? '' + : '\n' + Base.generateDiff(err.actual, err.expected); + this.write( + tag( + 'testcase', + attrs, + false, + tag( + 'failure', + {}, + false, + escape(err.message) + escape(diff) + '\n' + escape(err.stack) + ) + ) + ); + } else if (test.isPending()) { + this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); + } else { + this.write(tag('testcase', attrs, true)); + } +}; + +/** + * HTML tag helper. + * + * @param name + * @param attrs + * @param close + * @param content + * @return {string} + */ +function tag(name, attrs, close, content) { + var end = close ? '/>' : '>'; + var pairs = []; + var tag; + + for (var key in attrs) { + if (Object.prototype.hasOwnProperty.call(attrs, key)) { + pairs.push(key + '="' + escape(attrs[key]) + '"'); + } + } + + tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; + if (content) { + tag += content + '0, 2^31-1]. + * If clamped value matches either range endpoint, timeouts will be disabled. + * + * @private + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value} + * @param {number|string} ms - Timeout threshold value. + * @returns {Runnable} this + * @chainable + */ +Runnable.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + + // Clamp to range + var INT_MAX = Math.pow(2, 31) - 1; + var range = [0, INT_MAX]; + ms = utils.clamp(ms, range); + + // see #1652 for reasoning + if (ms === range[0] || ms === range[1]) { + this._enableTimeouts = false; + } + debug('timeout %d', ms); + this._timeout = ms; + if (this.timer) { + this.resetTimeout(); + } + return this; +}; + +/** + * Set or get slow `ms`. + * + * @private + * @param {number|string} ms + * @return {Runnable|number} ms or Runnable instance. + */ +Runnable.prototype.slow = function(ms) { + if (!arguments.length || typeof ms === 'undefined') { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set and get whether timeout is `enabled`. + * + * @private + * @param {boolean} enabled + * @return {Runnable|boolean} enabled or Runnable instance. + */ +Runnable.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Halt and mark as pending. + * + * @memberof Mocha.Runnable + * @public + */ +Runnable.prototype.skip = function() { + throw new Pending('sync skip'); +}; + +/** + * Check if this runnable or its parent suite is marked as pending. + * + * @private + */ +Runnable.prototype.isPending = function() { + return this.pending || (this.parent && this.parent.isPending()); +}; + +/** + * Return `true` if this Runnable has failed. + * @return {boolean} + * @private + */ +Runnable.prototype.isFailed = function() { + return !this.isPending() && this.state === constants.STATE_FAILED; +}; + +/** + * Return `true` if this Runnable has passed. + * @return {boolean} + * @private + */ +Runnable.prototype.isPassed = function() { + return !this.isPending() && this.state === constants.STATE_PASSED; +}; + +/** + * Set or get number of retries. + * + * @private + */ +Runnable.prototype.retries = function(n) { + if (!arguments.length) { + return this._retries; + } + this._retries = n; +}; + +/** + * Set or get current retry + * + * @private + */ +Runnable.prototype.currentRetry = function(n) { + if (!arguments.length) { + return this._currentRetry; + } + this._currentRetry = n; +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @memberof Mocha.Runnable + * @public + * @return {string} + */ +Runnable.prototype.fullTitle = function() { + return this.titlePath().join(' '); +}; + +/** + * Return the title path generated by concatenating the parent's title path with the title. + * + * @memberof Mocha.Runnable + * @public + * @return {string} + */ +Runnable.prototype.titlePath = function() { + return this.parent.titlePath().concat([this.title]); +}; + +/** + * Clear the timeout. + * + * @private + */ +Runnable.prototype.clearTimeout = function() { + clearTimeout(this.timer); +}; + +/** + * Inspect the runnable void of private properties. + * + * @private + * @return {string} + */ +Runnable.prototype.inspect = function() { + return JSON.stringify( + this, + function(key, val) { + if (key[0] === '_') { + return; + } + if (key === 'parent') { + return '#'; + } + if (key === 'ctx') { + return '#'; + } + return val; + }, + 2 + ); +}; + +/** + * Reset the timeout. + * + * @private + */ +Runnable.prototype.resetTimeout = function() { + var self = this; + var ms = this.timeout() || 1e9; + + if (!this._enableTimeouts) { + return; + } + this.clearTimeout(); + this.timer = setTimeout(function() { + if (!self._enableTimeouts) { + return; + } + self.callback(self._timeoutError(ms)); + self.timedOut = true; + }, ms); +}; + +/** + * Set or get a list of whitelisted globals for this test run. + * + * @private + * @param {string[]} globals + */ +Runnable.prototype.globals = function(globals) { + if (!arguments.length) { + return this._allowedGlobals; + } + this._allowedGlobals = globals; +}; + +/** + * Run the test and invoke `fn(err)`. + * + * @param {Function} fn + * @private + */ +Runnable.prototype.run = function(fn) { + var self = this; + var start = new Date(); + var ctx = this.ctx; + var finished; + var emitted; + + // Sometimes the ctx exists, but it is not runnable + if (ctx && ctx.runnable) { + ctx.runnable(this); + } + + // called multiple times + function multiple(err) { + if (emitted) { + return; + } + emitted = true; + var msg = 'done() called multiple times'; + if (err && err.message) { + err.message += " (and Mocha's " + msg + ')'; + self.emit('error', err); + } else { + self.emit('error', new Error(msg)); + } + } + + // finished + function done(err) { + var ms = self.timeout(); + if (self.timedOut) { + return; + } + + if (finished) { + return multiple(err); + } + + self.clearTimeout(); + self.duration = new Date() - start; + finished = true; + if (!err && self.duration > ms && self._enableTimeouts) { + err = self._timeoutError(ms); + } + fn(err); + } + + // for .resetTimeout() + this.callback = done; + + // explicit async with `done` argument + if (this.async) { + this.resetTimeout(); + + // allows skip() to be used in an explicit async context + this.skip = function asyncSkip() { + done(new Pending('async skip call')); + // halt execution. the Runnable will be marked pending + // by the previous call, and the uncaught handler will ignore + // the failure. + throw new Pending('async skip; aborting execution'); + }; + + if (this.allowUncaught) { + return callFnAsync(this.fn); + } + try { + callFnAsync(this.fn); + } catch (err) { + emitted = true; + done(Runnable.toValueOrError(err)); + } + return; + } + + if (this.allowUncaught) { + if (this.isPending()) { + done(); + } else { + callFn(this.fn); + } + return; + } + + // sync or promise-returning + try { + if (this.isPending()) { + done(); + } else { + callFn(this.fn); + } + } catch (err) { + emitted = true; + done(Runnable.toValueOrError(err)); + } + + function callFn(fn) { + var result = fn.call(ctx); + if (result && typeof result.then === 'function') { + self.resetTimeout(); + result.then( + function() { + done(); + // Return null so libraries like bluebird do not warn about + // subsequently constructed Promises. + return null; + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')); + } + ); + } else { + if (self.asyncOnly) { + return done( + new Error( + '--async-only option in use without declaring `done()` or returning a promise' + ) + ); + } + + done(); + } + } + + function callFnAsync(fn) { + var result = fn.call(ctx, function(err) { + if (err instanceof Error || toString.call(err) === '[object Error]') { + return done(err); + } + if (err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done( + new Error('done() invoked with non-Error: ' + JSON.stringify(err)) + ); + } + return done(new Error('done() invoked with non-Error: ' + err)); + } + if (result && utils.isPromise(result)) { + return done( + new Error( + 'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.' + ) + ); + } + + done(); + }); + } +}; + +/** + * Instantiates a "timeout" error + * + * @param {number} ms - Timeout (in milliseconds) + * @returns {Error} a "timeout" error + * @private + */ +Runnable.prototype._timeoutError = function(ms) { + var msg = + 'Timeout of ' + + ms + + 'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.'; + if (this.file) { + msg += ' (' + this.file + ')'; + } + return new Error(msg); +}; + +var constants = utils.defineConstants( + /** + * {@link Runnable}-related constants. + * @public + * @memberof Runnable + * @readonly + * @static + * @alias constants + * @enum {string} + */ + { + /** + * Value of `state` prop when a `Runnable` has failed + */ + STATE_FAILED: 'failed', + /** + * Value of `state` prop when a `Runnable` has passed + */ + STATE_PASSED: 'passed' + } +); + +/** + * Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that. + * @param {*} [value] - Value to return, if present + * @returns {*|Error} `value`, otherwise an `Error` + * @private + */ +Runnable.toValueOrError = function(value) { + return ( + value || + createInvalidExceptionError( + 'Runnable failed with falsy or undefined exception. Please throw an Error instead.', + value + ) + ); +}; + +Runnable.constants = constants; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./errors":6,"./pending":16,"./utils":38,"debug":45,"events":50,"ms":60}],34:[function(require,module,exports){ +(function (process,global){ +'use strict'; + +/** + * Module dependencies. + */ +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var Pending = require('./pending'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:runner'); +var Runnable = require('./runnable'); +var Suite = require('./suite'); +var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH; +var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH; +var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL; +var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL; +var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN; +var STATE_FAILED = Runnable.constants.STATE_FAILED; +var STATE_PASSED = Runnable.constants.STATE_PASSED; +var dQuote = utils.dQuote; +var ngettext = utils.ngettext; +var sQuote = utils.sQuote; +var stackFilter = utils.stackTraceFilter(); +var stringify = utils.stringify; +var type = utils.type; +var createInvalidExceptionError = require('./errors') + .createInvalidExceptionError; + +/** + * Non-enumerable globals. + * @readonly + */ +var globals = [ + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'XMLHttpRequest', + 'Date', + 'setImmediate', + 'clearImmediate' +]; + +var constants = utils.defineConstants( + /** + * {@link Runner}-related constants. + * @public + * @memberof Runner + * @readonly + * @alias constants + * @static + * @enum {string} + */ + { + /** + * Emitted when {@link Hook} execution begins + */ + EVENT_HOOK_BEGIN: 'hook', + /** + * Emitted when {@link Hook} execution ends + */ + EVENT_HOOK_END: 'hook end', + /** + * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution) + */ + EVENT_RUN_BEGIN: 'start', + /** + * Emitted when Root {@link Suite} execution has been delayed via `delay` option + */ + EVENT_DELAY_BEGIN: 'waiting', + /** + * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()` + */ + EVENT_DELAY_END: 'ready', + /** + * Emitted when Root {@link Suite} execution ends + */ + EVENT_RUN_END: 'end', + /** + * Emitted when {@link Suite} execution begins + */ + EVENT_SUITE_BEGIN: 'suite', + /** + * Emitted when {@link Suite} execution ends + */ + EVENT_SUITE_END: 'suite end', + /** + * Emitted when {@link Test} execution begins + */ + EVENT_TEST_BEGIN: 'test', + /** + * Emitted when {@link Test} execution ends + */ + EVENT_TEST_END: 'test end', + /** + * Emitted when {@link Test} execution fails + */ + EVENT_TEST_FAIL: 'fail', + /** + * Emitted when {@link Test} execution succeeds + */ + EVENT_TEST_PASS: 'pass', + /** + * Emitted when {@link Test} becomes pending + */ + EVENT_TEST_PENDING: 'pending', + /** + * Emitted when {@link Test} execution has failed, but will retry + */ + EVENT_TEST_RETRY: 'retry' + } +); + +module.exports = Runner; + +/** + * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}. + * + * @extends external:EventEmitter + * @public + * @class + * @param {Suite} suite Root suite + * @param {boolean} [delay] Whether or not to delay execution of root suite + * until ready. + */ +function Runner(suite, delay) { + var self = this; + this._globals = []; + this._abort = false; + this._delay = delay; + this.suite = suite; + this.started = false; + this.total = suite.total(); + this.failures = 0; + this.on(constants.EVENT_TEST_END, function(test) { + self.checkGlobals(test); + }); + this.on(constants.EVENT_HOOK_END, function(hook) { + self.checkGlobals(hook); + }); + this._defaultGrep = /.*/; + this.grep(this._defaultGrep); + this.globals(this.globalProps().concat(extraGlobals())); +} + +/** + * Wrapper for setImmediate, process.nextTick, or browser polyfill. + * + * @param {Function} fn + * @private + */ +Runner.immediately = global.setImmediate || process.nextTick; + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Runner, EventEmitter); + +/** + * Run tests with full titles matching `re`. Updates runner.total + * with number of tests matched. + * + * @public + * @memberof Runner + * @param {RegExp} re + * @param {boolean} invert + * @return {Runner} Runner instance. + */ +Runner.prototype.grep = function(re, invert) { + debug('grep %s', re); + this._grep = re; + this._invert = invert; + this.total = this.grepTotal(this.suite); + return this; +}; + +/** + * Returns the number of tests matching the grep search for the + * given suite. + * + * @memberof Runner + * @public + * @param {Suite} suite + * @return {number} + */ +Runner.prototype.grepTotal = function(suite) { + var self = this; + var total = 0; + + suite.eachTest(function(test) { + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (match) { + total++; + } + }); + + return total; +}; + +/** + * Return a list of global properties. + * + * @return {Array} + * @private + */ +Runner.prototype.globalProps = function() { + var props = Object.keys(global); + + // non-enumerables + for (var i = 0; i < globals.length; ++i) { + if (~props.indexOf(globals[i])) { + continue; + } + props.push(globals[i]); + } + + return props; +}; + +/** + * Allow the given `arr` of globals. + * + * @public + * @memberof Runner + * @param {Array} arr + * @return {Runner} Runner instance. + */ +Runner.prototype.globals = function(arr) { + if (!arguments.length) { + return this._globals; + } + debug('globals %j', arr); + this._globals = this._globals.concat(arr); + return this; +}; + +/** + * Check for global variable leaks. + * + * @private + */ +Runner.prototype.checkGlobals = function(test) { + if (this.ignoreLeaks) { + return; + } + var ok = this._globals; + + var globals = this.globalProps(); + var leaks; + + if (test) { + ok = ok.concat(test._allowedGlobals || []); + } + + if (this.prevGlobalsLength === globals.length) { + return; + } + this.prevGlobalsLength = globals.length; + + leaks = filterLeaks(ok, globals); + this._globals = this._globals.concat(leaks); + + if (leaks.length) { + var format = ngettext( + leaks.length, + 'global leak detected: %s', + 'global leaks detected: %s' + ); + var error = new Error(util.format(format, leaks.map(sQuote).join(', '))); + this.fail(test, error); + } +}; + +/** + * Fail the given `test`. + * + * @private + * @param {Test} test + * @param {Error} err + */ +Runner.prototype.fail = function(test, err) { + if (test.isPending()) { + return; + } + + ++this.failures; + test.state = STATE_FAILED; + + if (!isError(err)) { + err = thrown2Error(err); + } + + try { + err.stack = + this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack); + } catch (ignore) { + // some environments do not take kindly to monkeying with the stack + } + + this.emit(constants.EVENT_TEST_FAIL, test, err); +}; + +/** + * Fail the given `hook` with `err`. + * + * Hook failures work in the following pattern: + * - If bail, run corresponding `after each` and `after` hooks, + * then exit + * - Failed `before` hook skips all tests in a suite and subsuites, + * but jumps to corresponding `after` hook + * - Failed `before each` hook skips remaining tests in a + * suite and jumps to corresponding `after each` hook, + * which is run only once + * - Failed `after` hook does not alter + * execution order + * - Failed `after each` hook skips remaining tests in a + * suite and subsuites, but executes other `after each` + * hooks + * + * @private + * @param {Hook} hook + * @param {Error} err + */ +Runner.prototype.failHook = function(hook, err) { + hook.originalTitle = hook.originalTitle || hook.title; + if (hook.ctx && hook.ctx.currentTest) { + hook.title = + hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title); + } else { + var parentTitle; + if (hook.parent.title) { + parentTitle = hook.parent.title; + } else { + parentTitle = hook.parent.root ? '{root}' : ''; + } + hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle); + } + + this.fail(hook, err); +}; + +/** + * Run hook `name` callbacks and then invoke `fn()`. + * + * @private + * @param {string} name + * @param {Function} fn + */ + +Runner.prototype.hook = function(name, fn) { + var suite = this.suite; + var hooks = suite.getHooks(name); + var self = this; + + function next(i) { + var hook = hooks[i]; + if (!hook) { + return fn(); + } + self.currentRunnable = hook; + + if (name === HOOK_TYPE_BEFORE_ALL) { + hook.ctx.currentTest = hook.parent.tests[0]; + } else if (name === HOOK_TYPE_AFTER_ALL) { + hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1]; + } else { + hook.ctx.currentTest = self.test; + } + + hook.allowUncaught = self.allowUncaught; + + self.emit(constants.EVENT_HOOK_BEGIN, hook); + + if (!hook.listeners('error').length) { + hook.on('error', function(err) { + self.failHook(hook, err); + }); + } + + hook.run(function(err) { + var testError = hook.error(); + if (testError) { + self.fail(self.test, testError); + } + if (err) { + if (err instanceof Pending) { + if (name === HOOK_TYPE_AFTER_ALL) { + utils.deprecate( + 'Skipping a test within an "after all" hook is DEPRECATED and will throw an exception in a future version of Mocha. ' + + 'Use a return statement or other means to abort hook execution.' + ); + } + if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) { + if (self.test) { + self.test.pending = true; + } + } else { + suite.tests.forEach(function(test) { + test.pending = true; + }); + suite.suites.forEach(function(suite) { + suite.pending = true; + }); + // a pending hook won't be executed twice. + hook.pending = true; + } + } else { + self.failHook(hook, err); + + // stop executing hooks, notify callee of hook err + return fn(err); + } + } + self.emit(constants.EVENT_HOOK_END, hook); + delete hook.ctx.currentTest; + next(++i); + }); + } + + Runner.immediately(function() { + next(0); + }); +}; + +/** + * Run hook `name` for the given array of `suites` + * in order, and callback `fn(err, errSuite)`. + * + * @private + * @param {string} name + * @param {Array} suites + * @param {Function} fn + */ +Runner.prototype.hooks = function(name, suites, fn) { + var self = this; + var orig = this.suite; + + function next(suite) { + self.suite = suite; + + if (!suite) { + self.suite = orig; + return fn(); + } + + self.hook(name, function(err) { + if (err) { + var errSuite = self.suite; + self.suite = orig; + return fn(err, errSuite); + } + + next(suites.pop()); + }); + } + + next(suites.pop()); +}; + +/** + * Run hooks from the top level down. + * + * @param {String} name + * @param {Function} fn + * @private + */ +Runner.prototype.hookUp = function(name, fn) { + var suites = [this.suite].concat(this.parents()).reverse(); + this.hooks(name, suites, fn); +}; + +/** + * Run hooks from the bottom up. + * + * @param {String} name + * @param {Function} fn + * @private + */ +Runner.prototype.hookDown = function(name, fn) { + var suites = [this.suite].concat(this.parents()); + this.hooks(name, suites, fn); +}; + +/** + * Return an array of parent Suites from + * closest to furthest. + * + * @return {Array} + * @private + */ +Runner.prototype.parents = function() { + var suite = this.suite; + var suites = []; + while (suite.parent) { + suite = suite.parent; + suites.push(suite); + } + return suites; +}; + +/** + * Run the current test and callback `fn(err)`. + * + * @param {Function} fn + * @private + */ +Runner.prototype.runTest = function(fn) { + var self = this; + var test = this.test; + + if (!test) { + return; + } + + var suite = this.parents().reverse()[0] || this.suite; + if (this.forbidOnly && suite.hasOnly()) { + fn(new Error('`.only` forbidden')); + return; + } + if (this.asyncOnly) { + test.asyncOnly = true; + } + test.on('error', function(err) { + self.fail(test, err); + }); + if (this.allowUncaught) { + test.allowUncaught = true; + return test.run(fn); + } + try { + test.run(fn); + } catch (err) { + fn(err); + } +}; + +/** + * Run tests in the given `suite` and invoke the callback `fn()` when complete. + * + * @private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runTests = function(suite, fn) { + var self = this; + var tests = suite.tests.slice(); + var test; + + function hookErr(_, errSuite, after) { + // before/after Each hook for errSuite failed: + var orig = self.suite; + + // for failed 'after each' hook start from errSuite parent, + // otherwise start from errSuite itself + self.suite = after ? errSuite.parent : errSuite; + + if (self.suite) { + // call hookUp afterEach + self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) { + self.suite = orig; + // some hooks may fail even now + if (err2) { + return hookErr(err2, errSuite2, true); + } + // report error suite + fn(errSuite); + }); + } else { + // there is no need calling other 'after each' hooks + self.suite = orig; + fn(errSuite); + } + } + + function next(err, errSuite) { + // if we bail after first err + if (self.failures && suite._bail) { + tests = []; + } + + if (self._abort) { + return fn(); + } + + if (err) { + return hookErr(err, errSuite, true); + } + + // next test + test = tests.shift(); + + // all done + if (!test) { + return fn(); + } + + // grep + var match = self._grep.test(test.fullTitle()); + if (self._invert) { + match = !match; + } + if (!match) { + // Run immediately only if we have defined a grep. When we + // define a grep — It can cause maximum callstack error if + // the grep is doing a large recursive loop by neglecting + // all tests. The run immediately function also comes with + // a performance cost. So we don't want to run immediately + // if we run the whole test suite, because running the whole + // test suite don't do any immediate recursive loops. Thus, + // allowing a JS runtime to breathe. + if (self._grep !== self._defaultGrep) { + Runner.immediately(next); + } else { + next(); + } + return; + } + + if (test.isPending()) { + if (self.forbidPending) { + test.isPending = alwaysFalse; + self.fail(test, new Error('Pending test forbidden')); + delete test.isPending; + } else { + self.emit(constants.EVENT_TEST_PENDING, test); + } + self.emit(constants.EVENT_TEST_END, test); + return next(); + } + + // execute test and hook(s) + self.emit(constants.EVENT_TEST_BEGIN, (self.test = test)); + self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) { + if (test.isPending()) { + if (self.forbidPending) { + test.isPending = alwaysFalse; + self.fail(test, new Error('Pending test forbidden')); + delete test.isPending; + } else { + self.emit(constants.EVENT_TEST_PENDING, test); + } + self.emit(constants.EVENT_TEST_END, test); + return next(); + } + if (err) { + return hookErr(err, errSuite, false); + } + self.currentRunnable = self.test; + self.runTest(function(err) { + test = self.test; + if (err) { + var retry = test.currentRetry(); + if (err instanceof Pending && self.forbidPending) { + self.fail(test, new Error('Pending test forbidden')); + } else if (err instanceof Pending) { + test.pending = true; + self.emit(constants.EVENT_TEST_PENDING, test); + } else if (retry < test.retries()) { + var clonedTest = test.clone(); + clonedTest.currentRetry(retry + 1); + tests.unshift(clonedTest); + + self.emit(constants.EVENT_TEST_RETRY, test, err); + + // Early return + hook trigger so that it doesn't + // increment the count wrong + return self.hookUp(HOOK_TYPE_AFTER_EACH, next); + } else { + self.fail(test, err); + } + self.emit(constants.EVENT_TEST_END, test); + + if (err instanceof Pending) { + return next(); + } + + return self.hookUp(HOOK_TYPE_AFTER_EACH, next); + } + + test.state = STATE_PASSED; + self.emit(constants.EVENT_TEST_PASS, test); + self.emit(constants.EVENT_TEST_END, test); + self.hookUp(HOOK_TYPE_AFTER_EACH, next); + }); + }); + } + + this.next = next; + this.hookErr = hookErr; + next(); +}; + +function alwaysFalse() { + return false; +} + +/** + * Run the given `suite` and invoke the callback `fn()` when complete. + * + * @private + * @param {Suite} suite + * @param {Function} fn + */ +Runner.prototype.runSuite = function(suite, fn) { + var i = 0; + var self = this; + var total = this.grepTotal(suite); + var afterAllHookCalled = false; + + debug('run suite %s', suite.fullTitle()); + + if (!total || (self.failures && suite._bail)) { + return fn(); + } + + this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite)); + + function next(errSuite) { + if (errSuite) { + // current suite failed on a hook from errSuite + if (errSuite === suite) { + // if errSuite is current suite + // continue to the next sibling suite + return done(); + } + // errSuite is among the parents of current suite + // stop execution of errSuite and all sub-suites + return done(errSuite); + } + + if (self._abort) { + return done(); + } + + var curr = suite.suites[i++]; + if (!curr) { + return done(); + } + + // Avoid grep neglecting large number of tests causing a + // huge recursive loop and thus a maximum call stack error. + // See comment in `this.runTests()` for more information. + if (self._grep !== self._defaultGrep) { + Runner.immediately(function() { + self.runSuite(curr, next); + }); + } else { + self.runSuite(curr, next); + } + } + + function done(errSuite) { + self.suite = suite; + self.nextSuite = next; + + if (afterAllHookCalled) { + fn(errSuite); + } else { + // mark that the afterAll block has been called once + // and so can be skipped if there is an error in it. + afterAllHookCalled = true; + + // remove reference to test + delete self.test; + + self.hook(HOOK_TYPE_AFTER_ALL, function() { + self.emit(constants.EVENT_SUITE_END, suite); + fn(errSuite); + }); + } + } + + this.nextSuite = next; + + this.hook(HOOK_TYPE_BEFORE_ALL, function(err) { + if (err) { + return done(); + } + self.runTests(suite, next); + }); +}; + +/** + * Handle uncaught exceptions. + * + * @param {Error} err + * @private + */ +Runner.prototype.uncaught = function(err) { + if (err instanceof Pending) { + return; + } + if (err) { + debug('uncaught exception %O', err); + } else { + debug('uncaught undefined/falsy exception'); + err = createInvalidExceptionError( + 'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger', + err + ); + } + + if (!isError(err)) { + err = thrown2Error(err); + } + err.uncaught = true; + + var runnable = this.currentRunnable; + + if (!runnable) { + runnable = new Runnable('Uncaught error outside test suite'); + runnable.parent = this.suite; + + if (this.started) { + this.fail(runnable, err); + } else { + // Can't recover from this failure + this.emit(constants.EVENT_RUN_BEGIN); + this.fail(runnable, err); + this.emit(constants.EVENT_RUN_END); + } + + return; + } + + runnable.clearTimeout(); + + // Ignore errors if already failed or pending + // See #3226 + if (runnable.isFailed() || runnable.isPending()) { + return; + } + // we cannot recover gracefully if a Runnable has already passed + // then fails asynchronously + var alreadyPassed = runnable.isPassed(); + // this will change the state to "failed" regardless of the current value + this.fail(runnable, err); + if (!alreadyPassed) { + // recover from test + if (runnable.type === constants.EVENT_TEST_BEGIN) { + this.emit(constants.EVENT_TEST_END, runnable); + this.hookUp(HOOK_TYPE_AFTER_EACH, this.next); + return; + } + debug(runnable); + + // recover from hooks + var errSuite = this.suite; + + // XXX how about a less awful way to determine this? + // if hook failure is in afterEach block + if (runnable.fullTitle().indexOf('after each') > -1) { + return this.hookErr(err, errSuite, true); + } + // if hook failure is in beforeEach block + if (runnable.fullTitle().indexOf('before each') > -1) { + return this.hookErr(err, errSuite, false); + } + // if hook failure is in after or before blocks + return this.nextSuite(errSuite); + } + + // bail + this.emit(constants.EVENT_RUN_END); +}; + +/** + * Run the root suite and invoke `fn(failures)` + * on completion. + * + * @public + * @memberof Runner + * @param {Function} fn + * @return {Runner} Runner instance. + */ +Runner.prototype.run = function(fn) { + var self = this; + var rootSuite = this.suite; + + fn = fn || function() {}; + + function uncaught(err) { + self.uncaught(err); + } + + function start() { + // If there is an `only` filter + if (rootSuite.hasOnly()) { + rootSuite.filterOnly(); + } + self.started = true; + if (self._delay) { + self.emit(constants.EVENT_DELAY_END); + } + self.emit(constants.EVENT_RUN_BEGIN); + + self.runSuite(rootSuite, function() { + debug('finished running'); + self.emit(constants.EVENT_RUN_END); + }); + } + + debug(constants.EVENT_RUN_BEGIN); + + // references cleanup to avoid memory leaks + this.on(constants.EVENT_SUITE_END, function(suite) { + suite.cleanReferences(); + }); + + // callback + this.on(constants.EVENT_RUN_END, function() { + debug(constants.EVENT_RUN_END); + process.removeListener('uncaughtException', uncaught); + fn(self.failures); + }); + + // uncaught exception + process.on('uncaughtException', uncaught); + + if (this._delay) { + // for reporters, I guess. + // might be nice to debounce some dots while we wait. + this.emit(constants.EVENT_DELAY_BEGIN, rootSuite); + rootSuite.once(EVENT_ROOT_SUITE_RUN, start); + } else { + start(); + } + + return this; +}; + +/** + * Cleanly abort execution. + * + * @memberof Runner + * @public + * @return {Runner} Runner instance. + */ +Runner.prototype.abort = function() { + debug('aborting'); + this._abort = true; + + return this; +}; + +/** + * Filter leaks with the given globals flagged as `ok`. + * + * @private + * @param {Array} ok + * @param {Array} globals + * @return {Array} + */ +function filterLeaks(ok, globals) { + return globals.filter(function(key) { + // Firefox and Chrome exposes iframes as index inside the window object + if (/^\d+/.test(key)) { + return false; + } + + // in firefox + // if runner runs in an iframe, this iframe's window.getInterface method + // not init at first it is assigned in some seconds + if (global.navigator && /^getInterface/.test(key)) { + return false; + } + + // an iframe could be approached by window[iframeIndex] + // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak + if (global.navigator && /^\d+/.test(key)) { + return false; + } + + // Opera and IE expose global variables for HTML element IDs (issue #243) + if (/^mocha-/.test(key)) { + return false; + } + + var matched = ok.filter(function(ok) { + if (~ok.indexOf('*')) { + return key.indexOf(ok.split('*')[0]) === 0; + } + return key === ok; + }); + return !matched.length && (!global.navigator || key !== 'onerror'); + }); +} + +/** + * Check if argument is an instance of Error object or a duck-typed equivalent. + * + * @private + * @param {Object} err - object to check + * @param {string} err.message - error message + * @returns {boolean} + */ +function isError(err) { + return err instanceof Error || (err && typeof err.message === 'string'); +} + +/** + * + * Converts thrown non-extensible type into proper Error. + * + * @private + * @param {*} thrown - Non-extensible type thrown by code + * @return {Error} + */ +function thrown2Error(err) { + return new Error( + 'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)' + ); +} + +/** + * Array of globals dependent on the environment. + * + * @return {Array} + * @deprecated + * @todo remove; long since unsupported + * @private + */ +function extraGlobals() { + if (typeof process === 'object' && typeof process.version === 'string') { + var parts = process.version.split('.'); + var nodeVersion = parts.reduce(function(a, v) { + return (a << 8) | v; + }); + + // 'errno' was renamed to process._errno in v0.9.11. + if (nodeVersion < 0x00090b) { + return ['errno']; + } + } + + return []; +} + +Runner.constants = constants; + +/** + * Node.js' `EventEmitter` + * @external EventEmitter + * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter} + */ + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./errors":6,"./pending":16,"./runnable":33,"./suite":36,"./utils":38,"_process":69,"debug":45,"events":50,"util":89}],35:[function(require,module,exports){ +(function (global){ +'use strict'; + +/** + * Provides a factory function for a {@link StatsCollector} object. + * @module + */ + +var constants = require('./runner').constants; +var EVENT_TEST_PASS = constants.EVENT_TEST_PASS; +var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL; +var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN; +var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN; +var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING; +var EVENT_RUN_END = constants.EVENT_RUN_END; +var EVENT_TEST_END = constants.EVENT_TEST_END; + +/** + * Test statistics collector. + * + * @public + * @typedef {Object} StatsCollector + * @property {number} suites - integer count of suites run. + * @property {number} tests - integer count of tests run. + * @property {number} passes - integer count of passing tests. + * @property {number} pending - integer count of pending tests. + * @property {number} failures - integer count of failed tests. + * @property {Date} start - time when testing began. + * @property {Date} end - time when testing concluded. + * @property {number} duration - number of msecs that testing took. + */ + +var Date = global.Date; + +/** + * Provides stats such as test duration, number of tests passed / failed etc., by listening for events emitted by `runner`. + * + * @private + * @param {Runner} runner - Runner instance + * @throws {TypeError} If falsy `runner` + */ +function createStatsCollector(runner) { + /** + * @type StatsCollector + */ + var stats = { + suites: 0, + tests: 0, + passes: 0, + pending: 0, + failures: 0 + }; + + if (!runner) { + throw new TypeError('Missing runner argument'); + } + + runner.stats = stats; + + runner.once(EVENT_RUN_BEGIN, function() { + stats.start = new Date(); + }); + runner.on(EVENT_SUITE_BEGIN, function(suite) { + suite.root || stats.suites++; + }); + runner.on(EVENT_TEST_PASS, function() { + stats.passes++; + }); + runner.on(EVENT_TEST_FAIL, function() { + stats.failures++; + }); + runner.on(EVENT_TEST_PENDING, function() { + stats.pending++; + }); + runner.on(EVENT_TEST_END, function() { + stats.tests++; + }); + runner.once(EVENT_RUN_END, function() { + stats.end = new Date(); + stats.duration = stats.end - stats.start; + }); +} + +module.exports = createStatsCollector; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./runner":34}],36:[function(require,module,exports){ +'use strict'; + +/** + * Module dependencies. + */ +var EventEmitter = require('events').EventEmitter; +var Hook = require('./hook'); +var utils = require('./utils'); +var inherits = utils.inherits; +var debug = require('debug')('mocha:suite'); +var milliseconds = require('ms'); +var errors = require('./errors'); +var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; + +/** + * Expose `Suite`. + */ + +exports = module.exports = Suite; + +/** + * Create a new `Suite` with the given `title` and parent `Suite`. + * + * @public + * @param {Suite} parent - Parent suite (required!) + * @param {string} title - Title + * @return {Suite} + */ +Suite.create = function(parent, title) { + var suite = new Suite(title, parent.ctx); + suite.parent = parent; + title = suite.fullTitle(); + parent.addSuite(suite); + return suite; +}; + +/** + * Constructs a new `Suite` instance with the given `title`, `ctx`, and `isRoot`. + * + * @public + * @class + * @extends EventEmitter + * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter} + * @param {string} title - Suite title. + * @param {Context} parentContext - Parent context instance. + * @param {boolean} [isRoot=false] - Whether this is the root suite. + */ +function Suite(title, parentContext, isRoot) { + if (!utils.isString(title)) { + throw createInvalidArgumentTypeError( + 'Suite argument "title" must be a string. Received type "' + + typeof title + + '"', + 'title', + 'string' + ); + } + this.title = title; + function Context() {} + Context.prototype = parentContext; + this.ctx = new Context(); + this.suites = []; + this.tests = []; + this.pending = false; + this._beforeEach = []; + this._beforeAll = []; + this._afterEach = []; + this._afterAll = []; + this.root = isRoot === true; + this._timeout = 2000; + this._enableTimeouts = true; + this._slow = 75; + this._bail = false; + this._retries = -1; + this._onlyTests = []; + this._onlySuites = []; + this.delayed = false; + + this.on('newListener', function(event) { + if (deprecatedEvents[event]) { + utils.deprecate( + 'Event "' + + event + + '" is deprecated. Please let the Mocha team know about your use case: https://git.io/v6Lwm' + ); + } + }); +} + +/** + * Inherit from `EventEmitter.prototype`. + */ +inherits(Suite, EventEmitter); + +/** + * Return a clone of this `Suite`. + * + * @private + * @return {Suite} + */ +Suite.prototype.clone = function() { + var suite = new Suite(this.title); + debug('clone'); + suite.ctx = this.ctx; + suite.root = this.root; + suite.timeout(this.timeout()); + suite.retries(this.retries()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + return suite; +}; + +/** + * Set or get timeout `ms` or short-hand such as "2s". + * + * @private + * @todo Do not attempt to set value if `ms` is undefined + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.timeout = function(ms) { + if (!arguments.length) { + return this._timeout; + } + if (ms.toString() === '0') { + this._enableTimeouts = false; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('timeout %d', ms); + this._timeout = parseInt(ms, 10); + return this; +}; + +/** + * Set or get number of times to retry a failed test. + * + * @private + * @param {number|string} n + * @return {Suite|number} for chaining + */ +Suite.prototype.retries = function(n) { + if (!arguments.length) { + return this._retries; + } + debug('retries %d', n); + this._retries = parseInt(n, 10) || 0; + return this; +}; + +/** + * Set or get timeout to `enabled`. + * + * @private + * @param {boolean} enabled + * @return {Suite|boolean} self or enabled + */ +Suite.prototype.enableTimeouts = function(enabled) { + if (!arguments.length) { + return this._enableTimeouts; + } + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + +/** + * Set or get slow `ms` or short-hand such as "2s". + * + * @private + * @param {number|string} ms + * @return {Suite|number} for chaining + */ +Suite.prototype.slow = function(ms) { + if (!arguments.length) { + return this._slow; + } + if (typeof ms === 'string') { + ms = milliseconds(ms); + } + debug('slow %d', ms); + this._slow = ms; + return this; +}; + +/** + * Set or get whether to bail after first error. + * + * @private + * @param {boolean} bail + * @return {Suite|number} for chaining + */ +Suite.prototype.bail = function(bail) { + if (!arguments.length) { + return this._bail; + } + debug('bail %s', bail); + this._bail = bail; + return this; +}; + +/** + * Check if this suite or its parent suite is marked as pending. + * + * @private + */ +Suite.prototype.isPending = function() { + return this.pending || (this.parent && this.parent.isPending()); +}; + +/** + * Generic hook-creator. + * @private + * @param {string} title - Title of hook + * @param {Function} fn - Hook callback + * @returns {Hook} A new hook + */ +Suite.prototype._createHook = function(title, fn) { + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.retries(this.retries()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + hook.file = this.file; + return hook; +}; + +/** + * Run `fn(test[, done])` before running tests. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeAll = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before all" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._beforeAll.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook); + return this; +}; + +/** + * Run `fn(test[, done])` after running tests. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterAll = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after all" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._afterAll.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook); + return this; +}; + +/** + * Run `fn(test[, done])` before each test case. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.beforeEach = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"before each" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._beforeEach.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook); + return this; +}; + +/** + * Run `fn(test[, done])` after each test case. + * + * @private + * @param {string} title + * @param {Function} fn + * @return {Suite} for chaining + */ +Suite.prototype.afterEach = function(title, fn) { + if (this.isPending()) { + return this; + } + if (typeof title === 'function') { + fn = title; + title = fn.name; + } + title = '"after each" hook' + (title ? ': ' + title : ''); + + var hook = this._createHook(title, fn); + this._afterEach.push(hook); + this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook); + return this; +}; + +/** + * Add a test `suite`. + * + * @private + * @param {Suite} suite + * @return {Suite} for chaining + */ +Suite.prototype.addSuite = function(suite) { + suite.parent = this; + suite.root = false; + suite.timeout(this.timeout()); + suite.retries(this.retries()); + suite.enableTimeouts(this.enableTimeouts()); + suite.slow(this.slow()); + suite.bail(this.bail()); + this.suites.push(suite); + this.emit(constants.EVENT_SUITE_ADD_SUITE, suite); + return this; +}; + +/** + * Add a `test` to this suite. + * + * @private + * @param {Test} test + * @return {Suite} for chaining + */ +Suite.prototype.addTest = function(test) { + test.parent = this; + test.timeout(this.timeout()); + test.retries(this.retries()); + test.enableTimeouts(this.enableTimeouts()); + test.slow(this.slow()); + test.ctx = this.ctx; + this.tests.push(test); + this.emit(constants.EVENT_SUITE_ADD_TEST, test); + return this; +}; + +/** + * Return the full title generated by recursively concatenating the parent's + * full title. + * + * @memberof Suite + * @public + * @return {string} + */ +Suite.prototype.fullTitle = function() { + return this.titlePath().join(' '); +}; + +/** + * Return the title path generated by recursively concatenating the parent's + * title path. + * + * @memberof Suite + * @public + * @return {string} + */ +Suite.prototype.titlePath = function() { + var result = []; + if (this.parent) { + result = result.concat(this.parent.titlePath()); + } + if (!this.root) { + result.push(this.title); + } + return result; +}; + +/** + * Return the total number of tests. + * + * @memberof Suite + * @public + * @return {number} + */ +Suite.prototype.total = function() { + return ( + this.suites.reduce(function(sum, suite) { + return sum + suite.total(); + }, 0) + this.tests.length + ); +}; + +/** + * Iterates through each suite recursively to find all tests. Applies a + * function in the format `fn(test)`. + * + * @private + * @param {Function} fn + * @return {Suite} + */ +Suite.prototype.eachTest = function(fn) { + this.tests.forEach(fn); + this.suites.forEach(function(suite) { + suite.eachTest(fn); + }); + return this; +}; + +/** + * This will run the root suite if we happen to be running in delayed mode. + * @private + */ +Suite.prototype.run = function run() { + if (this.root) { + this.emit(constants.EVENT_ROOT_SUITE_RUN); + } +}; + +/** + * Determines whether a suite has an `only` test or suite as a descendant. + * + * @private + * @returns {Boolean} + */ +Suite.prototype.hasOnly = function hasOnly() { + return ( + this._onlyTests.length > 0 || + this._onlySuites.length > 0 || + this.suites.some(function(suite) { + return suite.hasOnly(); + }) + ); +}; + +/** + * Filter suites based on `isOnly` logic. + * + * @private + * @returns {Boolean} + */ +Suite.prototype.filterOnly = function filterOnly() { + if (this._onlyTests.length) { + // If the suite contains `only` tests, run those and ignore any nested suites. + this.tests = this._onlyTests; + this.suites = []; + } else { + // Otherwise, do not run any of the tests in this suite. + this.tests = []; + this._onlySuites.forEach(function(onlySuite) { + // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite. + // Otherwise, all of the tests on this `only` suite should be run, so don't filter it. + if (onlySuite.hasOnly()) { + onlySuite.filterOnly(); + } + }); + // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants. + var onlySuites = this._onlySuites; + this.suites = this.suites.filter(function(childSuite) { + return onlySuites.indexOf(childSuite) !== -1 || childSuite.filterOnly(); + }); + } + // Keep the suite only if there is something to run + return this.tests.length > 0 || this.suites.length > 0; +}; + +/** + * Adds a suite to the list of subsuites marked `only`. + * + * @private + * @param {Suite} suite + */ +Suite.prototype.appendOnlySuite = function(suite) { + this._onlySuites.push(suite); +}; + +/** + * Adds a test to the list of tests marked `only`. + * + * @private + * @param {Test} test + */ +Suite.prototype.appendOnlyTest = function(test) { + this._onlyTests.push(test); +}; + +/** + * Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants. + * @private + */ +Suite.prototype.getHooks = function getHooks(name) { + return this['_' + name]; +}; + +/** + * Cleans up the references to all the deferred functions + * (before/after/beforeEach/afterEach) and tests of a Suite. + * These must be deleted otherwise a memory leak can happen, + * as those functions may reference variables from closures, + * thus those variables can never be garbage collected as long + * as the deferred functions exist. + * + * @private + */ +Suite.prototype.cleanReferences = function cleanReferences() { + function cleanArrReferences(arr) { + for (var i = 0; i < arr.length; i++) { + delete arr[i].fn; + } + } + + if (Array.isArray(this._beforeAll)) { + cleanArrReferences(this._beforeAll); + } + + if (Array.isArray(this._beforeEach)) { + cleanArrReferences(this._beforeEach); + } + + if (Array.isArray(this._afterAll)) { + cleanArrReferences(this._afterAll); + } + + if (Array.isArray(this._afterEach)) { + cleanArrReferences(this._afterEach); + } + + for (var i = 0; i < this.tests.length; i++) { + delete this.tests[i].fn; + } +}; + +var constants = utils.defineConstants( + /** + * {@link Suite}-related constants. + * @public + * @memberof Suite + * @alias constants + * @readonly + * @static + * @enum {string} + */ + { + /** + * Event emitted after a test file has been loaded Not emitted in browser. + */ + EVENT_FILE_POST_REQUIRE: 'post-require', + /** + * Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected. + */ + EVENT_FILE_PRE_REQUIRE: 'pre-require', + /** + * Event emitted immediately after a test file has been loaded. Not emitted in browser. + */ + EVENT_FILE_REQUIRE: 'require', + /** + * Event emitted when `global.run()` is called (use with `delay` option) + */ + EVENT_ROOT_SUITE_RUN: 'run', + + /** + * Namespace for collection of a `Suite`'s "after all" hooks + */ + HOOK_TYPE_AFTER_ALL: 'afterAll', + /** + * Namespace for collection of a `Suite`'s "after each" hooks + */ + HOOK_TYPE_AFTER_EACH: 'afterEach', + /** + * Namespace for collection of a `Suite`'s "before all" hooks + */ + HOOK_TYPE_BEFORE_ALL: 'beforeAll', + /** + * Namespace for collection of a `Suite`'s "before all" hooks + */ + HOOK_TYPE_BEFORE_EACH: 'beforeEach', + + // the following events are all deprecated + + /** + * Emitted after an "after all" `Hook` has been added to a `Suite`. Deprecated + */ + EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll', + /** + * Emitted after an "after each" `Hook` has been added to a `Suite` Deprecated + */ + EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach', + /** + * Emitted after an "before all" `Hook` has been added to a `Suite` Deprecated + */ + EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll', + /** + * Emitted after an "before each" `Hook` has been added to a `Suite` Deprecated + */ + EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach', + /** + * Emitted after a child `Suite` has been added to a `Suite`. Deprecated + */ + EVENT_SUITE_ADD_SUITE: 'suite', + /** + * Emitted after a `Test` has been added to a `Suite`. Deprecated + */ + EVENT_SUITE_ADD_TEST: 'test' + } +); + +/** + * @summary There are no known use cases for these events. + * @desc This is a `Set`-like object having all keys being the constant's string value and the value being `true`. + * @todo Remove eventually + * @type {Object} + * @ignore + */ +var deprecatedEvents = Object.keys(constants) + .filter(function(constant) { + return constant.substring(0, 15) === 'EVENT_SUITE_ADD'; + }) + .reduce(function(acc, constant) { + acc[constants[constant]] = true; + return acc; + }, utils.createMap()); + +Suite.constants = constants; + +},{"./errors":6,"./hook":7,"./utils":38,"debug":45,"events":50,"ms":60}],37:[function(require,module,exports){ +'use strict'; +var Runnable = require('./runnable'); +var utils = require('./utils'); +var errors = require('./errors'); +var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; +var isString = utils.isString; + +module.exports = Test; + +/** + * Initialize a new `Test` with the given `title` and callback `fn`. + * + * @public + * @class + * @extends Runnable + * @param {String} title - Test title (required) + * @param {Function} [fn] - Test callback. If omitted, the Test is considered "pending" + */ +function Test(title, fn) { + if (!isString(title)) { + throw createInvalidArgumentTypeError( + 'Test argument "title" should be a string. Received type "' + + typeof title + + '"', + 'title', + 'string' + ); + } + Runnable.call(this, title, fn); + this.pending = !fn; + this.type = 'test'; +} + +/** + * Inherit from `Runnable.prototype`. + */ +utils.inherits(Test, Runnable); + +Test.prototype.clone = function() { + var test = new Test(this.title, this.fn); + test.timeout(this.timeout()); + test.slow(this.slow()); + test.enableTimeouts(this.enableTimeouts()); + test.retries(this.retries()); + test.currentRetry(this.currentRetry()); + test.globals(this.globals()); + test.parent = this.parent; + test.file = this.file; + test.ctx = this.ctx; + return test; +}; + +},{"./errors":6,"./runnable":33,"./utils":38}],38:[function(require,module,exports){ +(function (process,Buffer){ +'use strict'; + +/** + * Various utility functions used throughout Mocha's codebase. + * @module utils + */ + +/** + * Module dependencies. + */ + +var fs = require('fs'); +var path = require('path'); +var util = require('util'); +var glob = require('glob'); +var he = require('he'); +var errors = require('./errors'); +var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError; +var createMissingArgumentError = errors.createMissingArgumentError; + +var assign = (exports.assign = require('object.assign').getPolyfill()); + +/** + * Inherit the prototype methods from one constructor into another. + * + * @param {function} ctor - Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor - Constructor function to inherit prototype from. + * @throws {TypeError} if either constructor is null, or if super constructor + * lacks a prototype. + */ +exports.inherits = util.inherits; + +/** + * Escape special characters in the given string of html. + * + * @private + * @param {string} html + * @return {string} + */ +exports.escape = function(html) { + return he.encode(String(html), {useNamedReferences: false}); +}; + +/** + * Test if the given obj is type of string. + * + * @private + * @param {Object} obj + * @return {boolean} + */ +exports.isString = function(obj) { + return typeof obj === 'string'; +}; + +/** + * Watch the given `files` for changes + * and invoke `fn(file)` on modification. + * + * @private + * @param {Array} files + * @param {Function} fn + */ +exports.watch = function(files, fn) { + var options = {interval: 100}; + var debug = require('debug')('mocha:watch'); + files.forEach(function(file) { + debug('file %s', file); + fs.watchFile(file, options, function(curr, prev) { + if (prev.mtime < curr.mtime) { + fn(file); + } + }); + }); +}; + +/** + * Predicate to screen `pathname` for further consideration. + * + * @description + * Returns false for pathname referencing: + *
        + *
      • 'npm' package installation directory + *
      • 'git' version control directory + *
      + * + * @private + * @param {string} pathname - File or directory name to screen + * @return {boolean} whether pathname should be further considered + * @example + * ['node_modules', 'test.js'].filter(considerFurther); // => ['test.js'] + */ +function considerFurther(pathname) { + var ignore = ['node_modules', '.git']; + + return !~ignore.indexOf(pathname); +} + +/** + * Lookup files in the given `dir`. + * + * @description + * Filenames are returned in _traversal_ order by the OS/filesystem. + * **Make no assumption that the names will be sorted in any fashion.** + * + * @private + * @param {string} dir + * @param {string[]} [exts=['js']] + * @param {Array} [ret=[]] + * @return {Array} + */ +exports.files = function(dir, exts, ret) { + ret = ret || []; + exts = exts || ['js']; + + fs.readdirSync(dir) + .filter(considerFurther) + .forEach(function(dirent) { + var pathname = path.join(dir, dirent); + if (fs.lstatSync(pathname).isDirectory()) { + exports.files(pathname, exts, ret); + } else if (hasMatchingExtname(pathname, exts)) { + ret.push(pathname); + } + }); + + return ret; +}; + +/** + * Compute a slug from the given `str`. + * + * @private + * @param {string} str + * @return {string} + */ +exports.slug = function(str) { + return str + .toLowerCase() + .replace(/ +/g, '-') + .replace(/[^-\w]/g, ''); +}; + +/** + * Strip the function definition from `str`, and re-indent for pre whitespace. + * + * @param {string} str + * @return {string} + */ +exports.clean = function(str) { + str = str + .replace(/\r\n?|[\n\u2028\u2029]/g, '\n') + .replace(/^\uFEFF/, '') + // (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content + .replace( + /^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, + '$1$2$3' + ); + + var spaces = str.match(/^\n?( *)/)[1].length; + var tabs = str.match(/^\n?(\t*)/)[1].length; + var re = new RegExp( + '^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs || spaces) + '}', + 'gm' + ); + + str = str.replace(re, ''); + + return str.trim(); +}; + +/** + * Parse the given `qs`. + * + * @private + * @param {string} qs + * @return {Object} + */ +exports.parseQuery = function(qs) { + return qs + .replace('?', '') + .split('&') + .reduce(function(obj, pair) { + var i = pair.indexOf('='); + var key = pair.slice(0, i); + var val = pair.slice(++i); + + // Due to how the URLSearchParams API treats spaces + obj[key] = decodeURIComponent(val.replace(/\+/g, '%20')); + + return obj; + }, {}); +}; + +/** + * Highlight the given string of `js`. + * + * @private + * @param {string} js + * @return {string} + */ +function highlight(js) { + return js + .replace(//g, '>') + .replace(/\/\/(.*)/gm, '//$1') + .replace(/('.*?')/gm, '$1') + .replace(/(\d+\.\d+)/gm, '$1') + .replace(/(\d+)/gm, '$1') + .replace( + /\bnew[ \t]+(\w+)/gm, + 'new $1' + ) + .replace( + /\b(function|new|throw|return|var|if|else)\b/gm, + '$1' + ); +} + +/** + * Highlight the contents of tag `name`. + * + * @private + * @param {string} name + */ +exports.highlightTags = function(name) { + var code = document.getElementById('mocha').getElementsByTagName(name); + for (var i = 0, len = code.length; i < len; ++i) { + code[i].innerHTML = highlight(code[i].innerHTML); + } +}; + +/** + * If a value could have properties, and has none, this function is called, + * which returns a string representation of the empty value. + * + * Functions w/ no properties return `'[Function]'` + * Arrays w/ length === 0 return `'[]'` + * Objects w/ no properties return `'{}'` + * All else: return result of `value.toString()` + * + * @private + * @param {*} value The value to inspect. + * @param {string} typeHint The type of the value + * @returns {string} + */ +function emptyRepresentation(value, typeHint) { + switch (typeHint) { + case 'function': + return '[Function]'; + case 'object': + return '{}'; + case 'array': + return '[]'; + default: + return value.toString(); + } +} + +/** + * Takes some variable and asks `Object.prototype.toString()` what it thinks it + * is. + * + * @private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString + * @param {*} value The value to test. + * @returns {string} Computed type + * @example + * type({}) // 'object' + * type([]) // 'array' + * type(1) // 'number' + * type(false) // 'boolean' + * type(Infinity) // 'number' + * type(null) // 'null' + * type(new Date()) // 'date' + * type(/foo/) // 'regexp' + * type('type') // 'string' + * type(global) // 'global' + * type(new String('foo') // 'object' + */ +var type = (exports.type = function type(value) { + if (value === undefined) { + return 'undefined'; + } else if (value === null) { + return 'null'; + } else if (Buffer.isBuffer(value)) { + return 'buffer'; + } + return Object.prototype.toString + .call(value) + .replace(/^\[.+\s(.+?)]$/, '$1') + .toLowerCase(); +}); + +/** + * Stringify `value`. Different behavior depending on type of value: + * + * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively. + * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes. + * - If `value` is an *empty* object, function, or array, return result of function + * {@link emptyRepresentation}. + * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of + * JSON.stringify(). + * + * @private + * @see exports.type + * @param {*} value + * @return {string} + */ +exports.stringify = function(value) { + var typeHint = type(value); + + if (!~['object', 'array', 'function'].indexOf(typeHint)) { + if (typeHint === 'buffer') { + var json = Buffer.prototype.toJSON.call(value); + // Based on the toJSON result + return jsonStringify( + json.data && json.type ? json.data : json, + 2 + ).replace(/,(\n|$)/g, '$1'); + } + + // IE7/IE8 has a bizarre String constructor; needs to be coerced + // into an array and back to obj. + if (typeHint === 'string' && typeof value === 'object') { + value = value.split('').reduce(function(acc, char, idx) { + acc[idx] = char; + return acc; + }, {}); + typeHint = 'object'; + } else { + return jsonStringify(value); + } + } + + for (var prop in value) { + if (Object.prototype.hasOwnProperty.call(value, prop)) { + return jsonStringify( + exports.canonicalize(value, null, typeHint), + 2 + ).replace(/,(\n|$)/g, '$1'); + } + } + + return emptyRepresentation(value, typeHint); +}; + +/** + * like JSON.stringify but more sense. + * + * @private + * @param {Object} object + * @param {number=} spaces + * @param {number=} depth + * @returns {*} + */ +function jsonStringify(object, spaces, depth) { + if (typeof spaces === 'undefined') { + // primitive types + return _stringify(object); + } + + depth = depth || 1; + var space = spaces * depth; + var str = Array.isArray(object) ? '[' : '{'; + var end = Array.isArray(object) ? ']' : '}'; + var length = + typeof object.length === 'number' + ? object.length + : Object.keys(object).length; + // `.repeat()` polyfill + function repeat(s, n) { + return new Array(n).join(s); + } + + function _stringify(val) { + switch (type(val)) { + case 'null': + case 'undefined': + val = '[' + val + ']'; + break; + case 'array': + case 'object': + val = jsonStringify(val, spaces, depth + 1); + break; + case 'boolean': + case 'regexp': + case 'symbol': + case 'number': + val = + val === 0 && 1 / val === -Infinity // `-0` + ? '-0' + : val.toString(); + break; + case 'date': + var sDate = isNaN(val.getTime()) ? val.toString() : val.toISOString(); + val = '[Date: ' + sDate + ']'; + break; + case 'buffer': + var json = val.toJSON(); + // Based on the toJSON result + json = json.data && json.type ? json.data : json; + val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']'; + break; + default: + val = + val === '[Function]' || val === '[Circular]' + ? val + : JSON.stringify(val); // string + } + return val; + } + + for (var i in object) { + if (!Object.prototype.hasOwnProperty.call(object, i)) { + continue; // not my business + } + --length; + str += + '\n ' + + repeat(' ', space) + + (Array.isArray(object) ? '' : '"' + i + '": ') + // key + _stringify(object[i]) + // value + (length ? ',' : ''); // comma + } + + return ( + str + + // [], {} + (str.length !== 1 ? '\n' + repeat(' ', --space) + end : end) + ); +} + +/** + * Return a new Thing that has the keys in sorted order. Recursive. + * + * If the Thing... + * - has already been seen, return string `'[Circular]'` + * - is `undefined`, return string `'[undefined]'` + * - is `null`, return value `null` + * - is some other primitive, return the value + * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method + * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again. + * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()` + * + * @private + * @see {@link exports.stringify} + * @param {*} value Thing to inspect. May or may not have properties. + * @param {Array} [stack=[]] Stack of seen values + * @param {string} [typeHint] Type hint + * @return {(Object|Array|Function|string|undefined)} + */ +exports.canonicalize = function canonicalize(value, stack, typeHint) { + var canonicalizedObj; + /* eslint-disable no-unused-vars */ + var prop; + /* eslint-enable no-unused-vars */ + typeHint = typeHint || type(value); + function withStack(value, fn) { + stack.push(value); + fn(); + stack.pop(); + } + + stack = stack || []; + + if (stack.indexOf(value) !== -1) { + return '[Circular]'; + } + + switch (typeHint) { + case 'undefined': + case 'buffer': + case 'null': + canonicalizedObj = value; + break; + case 'array': + withStack(value, function() { + canonicalizedObj = value.map(function(item) { + return exports.canonicalize(item, stack); + }); + }); + break; + case 'function': + /* eslint-disable guard-for-in */ + for (prop in value) { + canonicalizedObj = {}; + break; + } + /* eslint-enable guard-for-in */ + if (!canonicalizedObj) { + canonicalizedObj = emptyRepresentation(value, typeHint); + break; + } + /* falls through */ + case 'object': + canonicalizedObj = canonicalizedObj || {}; + withStack(value, function() { + Object.keys(value) + .sort() + .forEach(function(key) { + canonicalizedObj[key] = exports.canonicalize(value[key], stack); + }); + }); + break; + case 'date': + case 'number': + case 'regexp': + case 'boolean': + case 'symbol': + canonicalizedObj = value; + break; + default: + canonicalizedObj = value + ''; + } + + return canonicalizedObj; +}; + +/** + * Determines if pathname has a matching file extension. + * + * @private + * @param {string} pathname - Pathname to check for match. + * @param {string[]} exts - List of file extensions (sans period). + * @return {boolean} whether file extension matches. + * @example + * hasMatchingExtname('foo.html', ['js', 'css']); // => false + */ +function hasMatchingExtname(pathname, exts) { + var suffix = path.extname(pathname).slice(1); + return exts.some(function(element) { + return suffix === element; + }); +} + +/** + * Determines if pathname would be a "hidden" file (or directory) on UN*X. + * + * @description + * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during + * typical usage. Dotfiles, plain-text configuration files, are prime examples. + * + * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names} + * + * @private + * @param {string} pathname - Pathname to check for match. + * @return {boolean} whether pathname would be considered a hidden file. + * @example + * isHiddenOnUnix('.profile'); // => true + */ +function isHiddenOnUnix(pathname) { + return path.basename(pathname)[0] === '.'; +} + +/** + * Lookup file names at the given `path`. + * + * @description + * Filenames are returned in _traversal_ order by the OS/filesystem. + * **Make no assumption that the names will be sorted in any fashion.** + * + * @public + * @memberof Mocha.utils + * @todo Fix extension handling + * @param {string} filepath - Base path to start searching from. + * @param {string[]} extensions - File extensions to look for. + * @param {boolean} recursive - Whether to recurse into subdirectories. + * @return {string[]} An array of paths. + * @throws {Error} if no files match pattern. + * @throws {TypeError} if `filepath` is directory and `extensions` not provided. + */ +exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { + var files = []; + var stat; + + if (!fs.existsSync(filepath)) { + if (fs.existsSync(filepath + '.js')) { + filepath += '.js'; + } else { + // Handle glob + files = glob.sync(filepath); + if (!files.length) { + throw createNoFilesMatchPatternError( + 'Cannot find any files matching pattern ' + exports.dQuote(filepath), + filepath + ); + } + return files; + } + } + + // Handle file + try { + stat = fs.statSync(filepath); + if (stat.isFile()) { + return filepath; + } + } catch (err) { + // ignore error + return; + } + + // Handle directory + fs.readdirSync(filepath).forEach(function(dirent) { + var pathname = path.join(filepath, dirent); + var stat; + + try { + stat = fs.statSync(pathname); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(pathname, extensions, recursive)); + } + return; + } + } catch (err) { + // ignore error + return; + } + if (!extensions) { + throw createMissingArgumentError( + util.format( + 'Argument %s required when argument %s is a directory', + exports.sQuote('extensions'), + exports.sQuote('filepath') + ), + 'extensions', + 'array' + ); + } + + if ( + !stat.isFile() || + !hasMatchingExtname(pathname, extensions) || + isHiddenOnUnix(pathname) + ) { + return; + } + files.push(pathname); + }); + + return files; +}; + +/** + * process.emitWarning or a polyfill + * @see https://nodejs.org/api/process.html#process_process_emitwarning_warning_options + * @ignore + */ +function emitWarning(msg, type) { + if (process.emitWarning) { + process.emitWarning(msg, type); + } else { + process.nextTick(function() { + console.warn(type + ': ' + msg); + }); + } +} + +/** + * Show a deprecation warning. Each distinct message is only displayed once. + * Ignores empty messages. + * + * @param {string} [msg] - Warning to print + * @private + */ +exports.deprecate = function deprecate(msg) { + msg = String(msg); + if (msg && !deprecate.cache[msg]) { + deprecate.cache[msg] = true; + emitWarning(msg, 'DeprecationWarning'); + } +}; +exports.deprecate.cache = {}; + +/** + * Show a generic warning. + * Ignores empty messages. + * + * @param {string} [msg] - Warning to print + * @private + */ +exports.warn = function warn(msg) { + if (msg) { + emitWarning(msg); + } +}; + +/** + * @summary + * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`) + * @description + * When invoking this function you get a filter function that get the Error.stack as an input, + * and return a prettify output. + * (i.e: strip Mocha and internal node functions from stack trace). + * @returns {Function} + */ +exports.stackTraceFilter = function() { + // TODO: Replace with `process.browser` + var is = typeof document === 'undefined' ? {node: true} : {browser: true}; + var slash = path.sep; + var cwd; + if (is.node) { + cwd = process.cwd() + slash; + } else { + cwd = (typeof location === 'undefined' + ? window.location + : location + ).href.replace(/\/[^/]*$/, '/'); + slash = '/'; + } + + function isMochaInternal(line) { + return ( + ~line.indexOf('node_modules' + slash + 'mocha' + slash) || + ~line.indexOf(slash + 'mocha.js') + ); + } + + function isNodeInternal(line) { + return ( + ~line.indexOf('(timers.js:') || + ~line.indexOf('(events.js:') || + ~line.indexOf('(node.js:') || + ~line.indexOf('(module.js:') || + ~line.indexOf('GeneratorFunctionPrototype.next (native)') || + false + ); + } + + return function(stack) { + stack = stack.split('\n'); + + stack = stack.reduce(function(list, line) { + if (isMochaInternal(line)) { + return list; + } + + if (is.node && isNodeInternal(line)) { + return list; + } + + // Clean up cwd(absolute) + if (/:\d+:\d+\)?$/.test(line)) { + line = line.replace('(' + cwd, '('); + } + + list.push(line); + return list; + }, []); + + return stack.join('\n'); + }; +}; + +/** + * Crude, but effective. + * @public + * @param {*} value + * @returns {boolean} Whether or not `value` is a Promise + */ +exports.isPromise = function isPromise(value) { + return ( + typeof value === 'object' && + value !== null && + typeof value.then === 'function' + ); +}; + +/** + * Clamps a numeric value to an inclusive range. + * + * @param {number} value - Value to be clamped. + * @param {numer[]} range - Two element array specifying [min, max] range. + * @returns {number} clamped value + */ +exports.clamp = function clamp(value, range) { + return Math.min(Math.max(value, range[0]), range[1]); +}; + +/** + * Single quote text by combining with undirectional ASCII quotation marks. + * + * @description + * Provides a simple means of markup for quoting text to be used in output. + * Use this to quote names of variables, methods, and packages. + * + * package 'foo' cannot be found + * + * @private + * @param {string} str - Value to be quoted. + * @returns {string} quoted value + * @example + * sQuote('n') // => 'n' + */ +exports.sQuote = function(str) { + return "'" + str + "'"; +}; + +/** + * Double quote text by combining with undirectional ASCII quotation marks. + * + * @description + * Provides a simple means of markup for quoting text to be used in output. + * Use this to quote names of datatypes, classes, pathnames, and strings. + * + * argument 'value' must be "string" or "number" + * + * @private + * @param {string} str - Value to be quoted. + * @returns {string} quoted value + * @example + * dQuote('number') // => "number" + */ +exports.dQuote = function(str) { + return '"' + str + '"'; +}; + +/** + * Provides simplistic message translation for dealing with plurality. + * + * @description + * Use this to create messages which need to be singular or plural. + * Some languages have several plural forms, so _complete_ message clauses + * are preferable to generating the message on the fly. + * + * @private + * @param {number} n - Non-negative integer + * @param {string} msg1 - Message to be used in English for `n = 1` + * @param {string} msg2 - Message to be used in English for `n = 0, 2, 3, ...` + * @returns {string} message corresponding to value of `n` + * @example + * var sprintf = require('util').format; + * var pkgs = ['one', 'two']; + * var msg = sprintf( + * ngettext( + * pkgs.length, + * 'cannot load package: %s', + * 'cannot load packages: %s' + * ), + * pkgs.map(sQuote).join(', ') + * ); + * console.log(msg); // => cannot load packages: 'one', 'two' + */ +exports.ngettext = function(n, msg1, msg2) { + if (typeof n === 'number' && n >= 0) { + return n === 1 ? msg1 : msg2; + } +}; + +/** + * It's a noop. + * @public + */ +exports.noop = function() {}; + +/** + * Creates a map-like object. + * + * @description + * A "map" is an object with no prototype, for our purposes. In some cases + * this would be more appropriate than a `Map`, especially if your environment + * doesn't support it. Recommended for use in Mocha's public APIs. + * + * @public + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map|MDN:Map} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects|MDN:Object.create - Custom objects} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign|MDN:Object.assign} + * @param {...*} [obj] - Arguments to `Object.assign()`. + * @returns {Object} An object with no prototype, having `...obj` properties + */ +exports.createMap = function(obj) { + return assign.apply( + null, + [Object.create(null)].concat(Array.prototype.slice.call(arguments)) + ); +}; + +/** + * Creates a read-only map-like object. + * + * @description + * This differs from {@link module:utils.createMap createMap} only in that + * the argument must be non-empty, because the result is frozen. + * + * @see {@link module:utils.createMap createMap} + * @param {...*} [obj] - Arguments to `Object.assign()`. + * @returns {Object} A frozen object with no prototype, having `...obj` properties + * @throws {TypeError} if argument is not a non-empty object. + */ +exports.defineConstants = function(obj) { + if (type(obj) !== 'object' || !Object.keys(obj).length) { + throw new TypeError('Invalid argument; expected a non-empty object'); + } + return Object.freeze(exports.createMap(obj)); +}; + +}).call(this,require('_process'),require("buffer").Buffer) +},{"./errors":6,"_process":69,"buffer":43,"debug":45,"fs":42,"glob":42,"he":54,"object.assign":65,"path":42,"util":89}],39:[function(require,module,exports){ +'use strict' + +exports.byteLength = byteLength +exports.toByteArray = toByteArray +exports.fromByteArray = fromByteArray + +var lookup = [] +var revLookup = [] +var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array + +var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i] + revLookup[code.charCodeAt(i)] = i +} + +// Support decoding URL-safe base64 strings, as Node.js does. +// See: https://en.wikipedia.org/wiki/Base64#URL_applications +revLookup['-'.charCodeAt(0)] = 62 +revLookup['_'.charCodeAt(0)] = 63 + +function getLens (b64) { + var len = b64.length + + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('=') + if (validLen === -1) validLen = len + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4) + + return [validLen, placeHoldersLen] +} + +// base64 is 4/3 + up to two characters of the original data +function byteLength (b64) { + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function toByteArray (b64) { + var tmp + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) + + var curByte = 0 + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen + + for (var i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)] + arr[curByte++] = (tmp >> 16) & 0xFF + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk( + uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength) + )) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ) + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1] + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ) + } + + return parts.join('') +} + +},{}],40:[function(require,module,exports){ + +},{}],41:[function(require,module,exports){ +(function (process){ +var WritableStream = require('stream').Writable +var inherits = require('util').inherits + +module.exports = BrowserStdout + + +inherits(BrowserStdout, WritableStream) + +function BrowserStdout(opts) { + if (!(this instanceof BrowserStdout)) return new BrowserStdout(opts) + + opts = opts || {} + WritableStream.call(this, opts) + this.label = (opts.label !== undefined) ? opts.label : 'stdout' +} + +BrowserStdout.prototype._write = function(chunks, encoding, cb) { + var output = chunks.toString ? chunks.toString() : chunks + if (this.label === false) { + console.log(output) + } else { + console.log(this.label+':', output) + } + process.nextTick(cb) +} + +}).call(this,require('_process')) +},{"_process":69,"stream":84,"util":89}],42:[function(require,module,exports){ +arguments[4][40][0].apply(exports,arguments) +},{"dup":40}],43:[function(require,module,exports){ +(function (Buffer){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + +'use strict' + +var base64 = require('base64-js') +var ieee754 = require('ieee754') + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 + +var K_MAX_LENGTH = 0x7fffffff +exports.kMaxLength = K_MAX_LENGTH + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Print warning and recommend using `buffer` v4.x which has an Object + * implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * We report that the browser does not support typed arrays if the are not subclassable + * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` + * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support + * for __proto__ and has a buggy typed array implementation. + */ +Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() + +if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && + typeof console.error === 'function') { + console.error( + 'This browser lacks typed array (Uint8Array) support which is required by ' + + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' + ) +} + +function typedArraySupport () { + // Can typed array instances can be augmented? + try { + var arr = new Uint8Array(1) + arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } + return arr.foo() === 42 + } catch (e) { + return false + } +} + +Object.defineProperty(Buffer.prototype, 'parent', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.buffer + } +}) + +Object.defineProperty(Buffer.prototype, 'offset', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.byteOffset + } +}) + +function createBuffer (length) { + if (length > K_MAX_LENGTH) { + throw new RangeError('The value "' + length + '" is invalid for option "size"') + } + // Return an augmented `Uint8Array` instance + var buf = new Uint8Array(length) + buf.__proto__ = Buffer.prototype + return buf +} + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + +function Buffer (arg, encodingOrOffset, length) { + // Common case. + if (typeof arg === 'number') { + if (typeof encodingOrOffset === 'string') { + throw new TypeError( + 'The "string" argument must be of type string. Received type number' + ) + } + return allocUnsafe(arg) + } + return from(arg, encodingOrOffset, length) +} + +// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 +if (typeof Symbol !== 'undefined' && Symbol.species != null && + Buffer[Symbol.species] === Buffer) { + Object.defineProperty(Buffer, Symbol.species, { + value: null, + configurable: true, + enumerable: false, + writable: false + }) +} + +Buffer.poolSize = 8192 // not used by this implementation + +function from (value, encodingOrOffset, length) { + if (typeof value === 'string') { + return fromString(value, encodingOrOffset) + } + + if (ArrayBuffer.isView(value)) { + return fromArrayLike(value) + } + + if (value == null) { + throw TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) + } + + if (isInstance(value, ArrayBuffer) || + (value && isInstance(value.buffer, ArrayBuffer))) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof value === 'number') { + throw new TypeError( + 'The "value" argument must not be of type number. Received type number' + ) + } + + var valueOf = value.valueOf && value.valueOf() + if (valueOf != null && valueOf !== value) { + return Buffer.from(valueOf, encodingOrOffset, length) + } + + var b = fromObject(value) + if (b) return b + + if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && + typeof value[Symbol.toPrimitive] === 'function') { + return Buffer.from( + value[Symbol.toPrimitive]('string'), encodingOrOffset, length + ) + } + + throw new TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) +} + +/** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ +Buffer.from = function (value, encodingOrOffset, length) { + return from(value, encodingOrOffset, length) +} + +// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: +// https://github.com/feross/buffer/pull/148 +Buffer.prototype.__proto__ = Uint8Array.prototype +Buffer.__proto__ = Uint8Array + +function assertSize (size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be of type number') + } else if (size < 0) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } +} + +function alloc (size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpretted as a start offset. + return typeof encoding === 'string' + ? createBuffer(size).fill(fill, encoding) + : createBuffer(size).fill(fill) + } + return createBuffer(size) +} + +/** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ +Buffer.alloc = function (size, fill, encoding) { + return alloc(size, fill, encoding) +} + +function allocUnsafe (size) { + assertSize(size) + return createBuffer(size < 0 ? 0 : checked(size) | 0) +} + +/** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ +Buffer.allocUnsafe = function (size) { + return allocUnsafe(size) +} +/** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ +Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(size) +} + +function fromString (string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + + var length = byteLength(string, encoding) | 0 + var buf = createBuffer(length) + + var actual = buf.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + buf = buf.slice(0, actual) + } + + return buf +} + +function fromArrayLike (array) { + var length = array.length < 0 ? 0 : checked(array.length) | 0 + var buf = createBuffer(length) + for (var i = 0; i < length; i += 1) { + buf[i] = array[i] & 255 + } + return buf +} + +function fromArrayBuffer (array, byteOffset, length) { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('"offset" is outside of buffer bounds') + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('"length" is outside of buffer bounds') + } + + var buf + if (byteOffset === undefined && length === undefined) { + buf = new Uint8Array(array) + } else if (length === undefined) { + buf = new Uint8Array(array, byteOffset) + } else { + buf = new Uint8Array(array, byteOffset, length) + } + + // Return an augmented `Uint8Array` instance + buf.__proto__ = Buffer.prototype + return buf +} + +function fromObject (obj) { + if (Buffer.isBuffer(obj)) { + var len = checked(obj.length) | 0 + var buf = createBuffer(len) + + if (buf.length === 0) { + return buf + } + + obj.copy(buf, 0, 0, len) + return buf + } + + if (obj.length !== undefined) { + if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { + return createBuffer(0) + } + return fromArrayLike(obj) + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + return fromArrayLike(obj.data) + } +} + +function checked (length) { + // Note: cannot use `length < K_MAX_LENGTH` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= K_MAX_LENGTH) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) +} + +Buffer.isBuffer = function isBuffer (b) { + return b != null && b._isBuffer === true && + b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false +} + +Buffer.compare = function compare (a, b) { + if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) + if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError( + 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' + ) + } + + if (a === b) return 0 + + var x = a.length + var y = b.length + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + + if (list.length === 0) { + return Buffer.alloc(0) + } + + var i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; ++i) { + length += list[i].length + } + } + + var buffer = Buffer.allocUnsafe(length) + var pos = 0 + for (i = 0; i < list.length; ++i) { + var buf = list[i] + if (isInstance(buf, Uint8Array)) { + buf = Buffer.from(buf) + } + if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + buf.copy(buffer, pos) + pos += buf.length + } + return buffer +} + +function byteLength (string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length + } + if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { + return string.byteLength + } + if (typeof string !== 'string') { + throw new TypeError( + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + + 'Received type ' + typeof string + ) + } + + var len = string.length + var mustMatch = (arguments.length > 2 && arguments[2] === true) + if (!mustMatch && len === 0) return 0 + + // Use a for loop to avoid recursion + var loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 + } + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString (encoding, start, end) { + var loweredCase = false + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coersion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } + + if (!encoding) encoding = 'utf8' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) +// to detect a Buffer instance. It's not possible to use `instanceof Buffer` +// reliably in a browserify context because there could be multiple different +// copies of the 'buffer' package in use. This method works even for Buffer +// instances that were created from another copy of the `buffer` package. +// See: https://github.com/feross/buffer/issues/154 +Buffer.prototype._isBuffer = true + +function swap (b, n, m) { + var i = b[n] + b[n] = b[m] + b[m] = i +} + +Buffer.prototype.swap16 = function swap16 () { + var len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (var i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this +} + +Buffer.prototype.swap32 = function swap32 () { + var len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (var i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this +} + +Buffer.prototype.swap64 = function swap64 () { + var len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (var i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this +} + +Buffer.prototype.toString = function toString () { + var length = this.length + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.toLocaleString = Buffer.prototype.toString + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + var str = '' + var max = exports.INSPECT_MAX_BYTES + str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() + if (this.length > max) str += ' ... ' + return '' +} + +Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { + if (isInstance(target, Uint8Array)) { + target = Buffer.from(target, target.offset, target.byteLength) + } + if (!Buffer.isBuffer(target)) { + throw new TypeError( + 'The "target" argument must be one of type Buffer or Uint8Array. ' + + 'Received type ' + (typeof target) + ) + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 + + var x = thisEnd - thisStart + var y = end - start + var len = Math.min(x, y) + + var thisCopy = this.slice(thisStart, thisEnd) + var targetCopy = target.slice(start, end) + + for (var i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding) + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) + } + + throw new TypeError('val must be string, number or Buffer') +} + +function arrayIndexOf (arr, val, byteOffset, encoding, dir) { + var indexSize = 1 + var arrLength = arr.length + var valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 + } + } + + function read (buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + var i + if (dir) { + var foundIndex = -1 + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize + } else { + if (foundIndex !== -1) i -= i - foundIndex + foundIndex = -1 + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + var found = true + for (var j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } + } + + return -1 +} + +Buffer.prototype.includes = function includes (val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) +} + +Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + var strLen = string.length + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; ++i) { + var parsed = parseInt(string.substr(i * 2, 2), 16) + if (numberIsNaN(parsed)) return i + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function latin1Write (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0 + if (isFinite(length)) { + length = length >>> 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + } else { + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) + } + + var remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + var loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + return asciiWrite(this, string, offset, length) + + case 'latin1': + case 'binary': + return latin1Write(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function latin1Slice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; ++i) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + var newBuf = this.subarray(start, end) + // Return an augmented `Uint8Array` instance + newBuf.__proto__ = Buffer.prototype + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + var val = this[offset + --byteLength] + var mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var i = byteLength + var mul = 1 + var val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') +} + +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var mul = 1 + var i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var i = byteLength - 1 + var mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = 0 + var mul = 1 + var sub = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = byteLength - 1 + var mul = 1 + var sub = 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('Index out of range') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + var len = end - start + + if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { + // Use built-in when available, missing from IE11 + this.copyWithin(targetStart, start, end) + } else if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (var i = len - 1; i >= 0; --i) { + target[i + targetStart] = this[i + start] + } + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, end), + targetStart + ) + } + + return len +} + +// Usage: +// buffer.fill(number[, offset[, end]]) +// buffer.fill(buffer[, offset[, end]]) +// buffer.fill(string[, offset[, end]][, encoding]) +Buffer.prototype.fill = function fill (val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + if (val.length === 1) { + var code = val.charCodeAt(0) + if ((encoding === 'utf8' && code < 128) || + encoding === 'latin1') { + // Fast path: If `val` fits into a single byte, use that numeric value. + val = code + } + } + } else if (typeof val === 'number') { + val = val & 255 + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } + + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 + + if (!val) val = 0 + + var i + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val + } + } else { + var bytes = Buffer.isBuffer(val) + ? val + : Buffer.from(val, encoding) + var len = bytes.length + if (len === 0) { + throw new TypeError('The value "' + val + + '" is invalid for argument "value"') + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] + } + } + + return this +} + +// HELPER FUNCTIONS +// ================ + +var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node takes equal signs as end of the Base64 encoding + str = str.split('=')[0] + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (string, units) { + units = units || Infinity + var codePoint + var length = string.length + var leadSurrogate = null + var bytes = [] + + for (var i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; ++i) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass +// the `instanceof` check but they should be treated as of that type. +// See: https://github.com/feross/buffer/issues/166 +function isInstance (obj, type) { + return obj instanceof type || + (obj != null && obj.constructor != null && obj.constructor.name != null && + obj.constructor.name === type.name) +} +function numberIsNaN (obj) { + // For IE11 support + return obj !== obj // eslint-disable-line no-self-compare +} + +}).call(this,require("buffer").Buffer) +},{"base64-js":39,"buffer":43,"ieee754":55}],44:[function(require,module,exports){ +(function (Buffer){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. + +function isArray(arg) { + if (Array.isArray) { + return Array.isArray(arg); + } + return objectToString(arg) === '[object Array]'; +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = Buffer.isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +}).call(this,{"isBuffer":require("../../is-buffer/index.js")}) +},{"../../is-buffer/index.js":57}],45:[function(require,module,exports){ +(function (process){ +"use strict"; + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +/* eslint-env browser */ + +/** + * This is the web browser implementation of `debug()`. + */ +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = localstorage(); +/** + * Colors. + */ + +exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33']; +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ +// eslint-disable-next-line complexity + +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { + return true; + } // Internet Explorer and Edge do not support colors. + + + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } // Is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + + + return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773 + typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker + typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); +} +/** + * Colorize log arguments if enabled. + * + * @api public + */ + + +function formatArgs(args) { + args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff); + + if (!this.useColors) { + return; + } + + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit'); // The final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function (match) { + if (match === '%%') { + return; + } + + index++; + + if (match === '%c') { + // We only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + args.splice(lastC, 0, c); +} +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + + +function log() { + var _console; + + // This hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return (typeof console === "undefined" ? "undefined" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments); +} +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + + +function save(namespaces) { + try { + if (namespaces) { + exports.storage.setItem('debug', namespaces); + } else { + exports.storage.removeItem('debug'); + } + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } +} +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + + +function load() { + var r; + + try { + r = exports.storage.getItem('debug'); + } catch (error) {} // Swallow + // XXX (@Qix-) should we be logging these? + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + + + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; +} +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + + +function localstorage() { + try { + // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context + // The Browser also has localStorage in the global context. + return localStorage; + } catch (error) {// Swallow + // XXX (@Qix-) should we be logging these? + } +} + +module.exports = require('./common')(exports); +var formatters = module.exports.formatters; +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +formatters.j = function (v) { + try { + return JSON.stringify(v); + } catch (error) { + return '[UnexpectedJSONParseError]: ' + error.message; + } +}; + + +}).call(this,require('_process')) +},{"./common":46,"_process":69}],46:[function(require,module,exports){ +"use strict"; + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + */ +function setup(env) { + createDebug.debug = createDebug; + createDebug.default = createDebug; + createDebug.coerce = coerce; + createDebug.disable = disable; + createDebug.enable = enable; + createDebug.enabled = enabled; + createDebug.humanize = require('ms'); + Object.keys(env).forEach(function (key) { + createDebug[key] = env[key]; + }); + /** + * Active `debug` instances. + */ + + createDebug.instances = []; + /** + * The currently active debug mode names, and names to skip. + */ + + createDebug.names = []; + createDebug.skips = []; + /** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + + createDebug.formatters = {}; + /** + * Selects a color for a debug namespace + * @param {String} namespace The namespace string for the for the debug instance to be colored + * @return {Number|String} An ANSI color code for the given namespace + * @api private + */ + + function selectColor(namespace) { + var hash = 0; + + for (var i = 0; i < namespace.length; i++) { + hash = (hash << 5) - hash + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; + } + + createDebug.selectColor = selectColor; + /** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + + function createDebug(namespace) { + var prevTime; + + function debug() { + // Disabled? + if (!debug.enabled) { + return; + } + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var self = debug; // Set `diff` timestamp + + var curr = Number(new Date()); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + args[0] = createDebug.coerce(args[0]); + + if (typeof args[0] !== 'string') { + // Anything else let's inspect with %O + args.unshift('%O'); + } // Apply any `formatters` transformations + + + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) { + // If we encounter an escaped % then don't increase the array index + if (match === '%%') { + return match; + } + + index++; + var formatter = createDebug.formatters[format]; + + if (typeof formatter === 'function') { + var val = args[index]; + match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format` + + args.splice(index, 1); + index--; + } + + return match; + }); // Apply env-specific formatting (colors, etc.) + + createDebug.formatArgs.call(self, args); + var logFn = self.log || createDebug.log; + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = createDebug.enabled(namespace); + debug.useColors = createDebug.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; + debug.extend = extend; // Debug.formatArgs = formatArgs; + // debug.rawLog = rawLog; + // env-specific initialization logic for debug instances + + if (typeof createDebug.init === 'function') { + createDebug.init(debug); + } + + createDebug.instances.push(debug); + return debug; + } + + function destroy() { + var index = createDebug.instances.indexOf(this); + + if (index !== -1) { + createDebug.instances.splice(index, 1); + return true; + } + + return false; + } + + function extend(namespace, delimiter) { + return createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); + } + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + + + function enable(namespaces) { + createDebug.save(namespaces); + createDebug.names = []; + createDebug.skips = []; + var i; + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; + + for (i = 0; i < len; i++) { + if (!split[i]) { + // ignore empty strings + continue; + } + + namespaces = split[i].replace(/\*/g, '.*?'); + + if (namespaces[0] === '-') { + createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + createDebug.names.push(new RegExp('^' + namespaces + '$')); + } + } + + for (i = 0; i < createDebug.instances.length; i++) { + var instance = createDebug.instances[i]; + instance.enabled = createDebug.enabled(instance.namespace); + } + } + /** + * Disable debug output. + * + * @api public + */ + + + function disable() { + createDebug.enable(''); + } + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + + + function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + + var i; + var len; + + for (i = 0, len = createDebug.skips.length; i < len; i++) { + if (createDebug.skips[i].test(name)) { + return false; + } + } + + for (i = 0, len = createDebug.names.length; i < len; i++) { + if (createDebug.names[i].test(name)) { + return true; + } + } + + return false; + } + /** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + + + function coerce(val) { + if (val instanceof Error) { + return val.stack || val.message; + } + + return val; + } + + createDebug.enable(createDebug.load()); + return createDebug; +} + +module.exports = setup; + + +},{"ms":60}],47:[function(require,module,exports){ +'use strict'; + +var keys = require('object-keys'); +var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol'; + +var toStr = Object.prototype.toString; +var concat = Array.prototype.concat; +var origDefineProperty = Object.defineProperty; + +var isFunction = function (fn) { + return typeof fn === 'function' && toStr.call(fn) === '[object Function]'; +}; + +var arePropertyDescriptorsSupported = function () { + var obj = {}; + try { + origDefineProperty(obj, 'x', { enumerable: false, value: obj }); + // eslint-disable-next-line no-unused-vars, no-restricted-syntax + for (var _ in obj) { // jscs:ignore disallowUnusedVariables + return false; + } + return obj.x === obj; + } catch (e) { /* this is IE 8. */ + return false; + } +}; +var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported(); + +var defineProperty = function (object, name, value, predicate) { + if (name in object && (!isFunction(predicate) || !predicate())) { + return; + } + if (supportsDescriptors) { + origDefineProperty(object, name, { + configurable: true, + enumerable: false, + value: value, + writable: true + }); + } else { + object[name] = value; + } +}; + +var defineProperties = function (object, map) { + var predicates = arguments.length > 2 ? arguments[2] : {}; + var props = keys(map); + if (hasSymbols) { + props = concat.call(props, Object.getOwnPropertySymbols(map)); + } + for (var i = 0; i < props.length; i += 1) { + defineProperty(object, props[i], map[props[i]], predicates[props[i]]); + } +}; + +defineProperties.supportsDescriptors = !!supportsDescriptors; + +module.exports = defineProperties; + +},{"object-keys":62}],48:[function(require,module,exports){ +/*! + + diff v3.5.0 + +Software License Agreement (BSD License) + +Copyright (c) 2009-2015, Kevin Decker + +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of Kevin Decker nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +@license +*/ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(false) + define([], factory); + else if(typeof exports === 'object') + exports["JsDiff"] = factory(); + else + root["JsDiff"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.canonicalize = exports.convertChangesToXML = exports.convertChangesToDMP = exports.merge = exports.parsePatch = exports.applyPatches = exports.applyPatch = exports.createPatch = exports.createTwoFilesPatch = exports.structuredPatch = exports.diffArrays = exports.diffJson = exports.diffCss = exports.diffSentences = exports.diffTrimmedLines = exports.diffLines = exports.diffWordsWithSpace = exports.diffWords = exports.diffChars = exports.Diff = undefined; + + /*istanbul ignore end*/var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_character = __webpack_require__(2) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_word = __webpack_require__(3) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_sentence = __webpack_require__(6) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_css = __webpack_require__(7) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_json = __webpack_require__(8) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_array = __webpack_require__(9) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_apply = __webpack_require__(10) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_merge = __webpack_require__(13) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_create = __webpack_require__(14) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_dmp = __webpack_require__(16) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_xml = __webpack_require__(17) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /* See LICENSE file for terms of use */ + + /* + * Text diff implementation. + * + * This library supports the following APIS: + * JsDiff.diffChars: Character by character diff + * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace + * JsDiff.diffLines: Line based diff + * + * JsDiff.diffCss: Diff targeted at CSS content + * + * These methods are based on the implementation proposed in + * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 + */ + exports. /*istanbul ignore end*/Diff = _base2['default']; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffChars = _character.diffChars; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWords = _word.diffWords; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = _word.diffWordsWithSpace; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffLines = _line.diffLines; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = _line.diffTrimmedLines; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffSentences = _sentence.diffSentences; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffCss = _css.diffCss; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffJson = _json.diffJson; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffArrays = _array.diffArrays; + /*istanbul ignore start*/exports. /*istanbul ignore end*/structuredPatch = _create.structuredPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = _create.createTwoFilesPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = _create.createPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatch = _apply.applyPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = _apply.applyPatches; + /*istanbul ignore start*/exports. /*istanbul ignore end*/parsePatch = _parse.parsePatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/merge = _merge.merge; + /*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToDMP = _dmp.convertChangesToDMP; + /*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToXML = _xml.convertChangesToXML; + /*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = _json.canonicalize; + + + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports['default'] = /*istanbul ignore end*/Diff; + function Diff() {} + + Diff.prototype = { + /*istanbul ignore start*/ /*istanbul ignore end*/diff: function diff(oldString, newString) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var callback = options.callback; + if (typeof options === 'function') { + callback = options; + options = {}; + } + this.options = options; + + var self = this; + + function done(value) { + if (callback) { + setTimeout(function () { + callback(undefined, value); + }, 0); + return true; + } else { + return value; + } + } + + // Allow subclasses to massage the input prior to running + oldString = this.castInput(oldString); + newString = this.castInput(newString); + + oldString = this.removeEmpty(this.tokenize(oldString)); + newString = this.removeEmpty(this.tokenize(newString)); + + var newLen = newString.length, + oldLen = oldString.length; + var editLength = 1; + var maxEditLength = newLen + oldLen; + var bestPath = [{ newPos: -1, components: [] }]; + + // Seed editLength = 0, i.e. the content starts with the same values + var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); + if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { + // Identity per the equality and tokenizer + return done([{ value: this.join(newString), count: newString.length }]); + } + + // Main worker method. checks all permutations of a given edit length for acceptance. + function execEditLength() { + for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { + var basePath = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + var addPath = bestPath[diagonalPath - 1], + removePath = bestPath[diagonalPath + 1], + _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; + if (addPath) { + // No one else is going to attempt to use this value, clear it + bestPath[diagonalPath - 1] = undefined; + } + + var canAdd = addPath && addPath.newPos + 1 < newLen, + canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen; + if (!canAdd && !canRemove) { + // If this path is a terminal then prune + bestPath[diagonalPath] = undefined; + continue; + } + + // Select the diagonal that we want to branch from. We select the prior + // path whose position in the new string is the farthest from the origin + // and does not pass the bounds of the diff graph + if (!canAdd || canRemove && addPath.newPos < removePath.newPos) { + basePath = clonePath(removePath); + self.pushComponent(basePath.components, undefined, true); + } else { + basePath = addPath; // No need to clone, we've pulled it from the list + basePath.newPos++; + self.pushComponent(basePath.components, true, undefined); + } + + _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); + + // If we have hit the end of both strings, then we are done + if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) { + return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken)); + } else { + // Otherwise track this path as a potential candidate and continue. + bestPath[diagonalPath] = basePath; + } + } + + editLength++; + } + + // Performs the length of edit iteration. Is a bit fugly as this has to support the + // sync and async mode which is never fun. Loops over execEditLength until a value + // is produced. + if (callback) { + (function exec() { + setTimeout(function () { + // This should not happen, but we want to be safe. + /* istanbul ignore next */ + if (editLength > maxEditLength) { + return callback(); + } + + if (!execEditLength()) { + exec(); + } + }, 0); + })(); + } else { + while (editLength <= maxEditLength) { + var ret = execEditLength(); + if (ret) { + return ret; + } + } + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/pushComponent: function pushComponent(components, added, removed) { + var last = components[components.length - 1]; + if (last && last.added === added && last.removed === removed) { + // We need to clone here as the component clone operation is just + // as shallow array clone + components[components.length - 1] = { count: last.count + 1, added: added, removed: removed }; + } else { + components.push({ count: 1, added: added, removed: removed }); + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { + var newLen = newString.length, + oldLen = oldString.length, + newPos = basePath.newPos, + oldPos = newPos - diagonalPath, + commonCount = 0; + while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { + newPos++; + oldPos++; + commonCount++; + } + + if (commonCount) { + basePath.components.push({ count: commonCount }); + } + + basePath.newPos = newPos; + return oldPos; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/equals: function equals(left, right) { + if (this.options.comparator) { + return this.options.comparator(left, right); + } else { + return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); + } + }, + /*istanbul ignore start*/ /*istanbul ignore end*/removeEmpty: function removeEmpty(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + if (array[i]) { + ret.push(array[i]); + } + } + return ret; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/castInput: function castInput(value) { + return value; + }, + /*istanbul ignore start*/ /*istanbul ignore end*/tokenize: function tokenize(value) { + return value.split(''); + }, + /*istanbul ignore start*/ /*istanbul ignore end*/join: function join(chars) { + return chars.join(''); + } + }; + + function buildValues(diff, components, newString, oldString, useLongestToken) { + var componentPos = 0, + componentLen = components.length, + newPos = 0, + oldPos = 0; + + for (; componentPos < componentLen; componentPos++) { + var component = components[componentPos]; + if (!component.removed) { + if (!component.added && useLongestToken) { + var value = newString.slice(newPos, newPos + component.count); + value = value.map(function (value, i) { + var oldValue = oldString[oldPos + i]; + return oldValue.length > value.length ? oldValue : value; + }); + + component.value = diff.join(value); + } else { + component.value = diff.join(newString.slice(newPos, newPos + component.count)); + } + newPos += component.count; + + // Common case + if (!component.added) { + oldPos += component.count; + } + } else { + component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); + oldPos += component.count; + + // Reverse add and remove so removes are output first to match common convention + // The diffing algorithm is tied to add then remove output and this is the simplest + // route to get the desired output with minimal overhead. + if (componentPos && components[componentPos - 1].added) { + var tmp = components[componentPos - 1]; + components[componentPos - 1] = components[componentPos]; + components[componentPos] = tmp; + } + } + } + + // Special case handle for when one terminal is ignored (i.e. whitespace). + // For this case we merge the terminal into the prior string and drop the change. + // This is only available for string mode. + var lastComponent = components[componentLen - 1]; + if (componentLen > 1 && typeof lastComponent.value === 'string' && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) { + components[componentLen - 2].value += lastComponent.value; + components.pop(); + } + + return components; + } + + function clonePath(path) { + return { newPos: path.newPos, components: path.components.slice(0) }; + } + + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.characterDiff = undefined; + exports. /*istanbul ignore end*/diffChars = diffChars; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var characterDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/characterDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + function diffChars(oldStr, newStr, options) { + return characterDiff.diff(oldStr, newStr, options); + } + + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.wordDiff = undefined; + exports. /*istanbul ignore end*/diffWords = diffWords; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = diffWordsWithSpace; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_params = __webpack_require__(4) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/ // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode + // + // Ranges and exceptions: + // Latin-1 Supplement, 0080–00FF + // - U+00D7 × Multiplication sign + // - U+00F7 ÷ Division sign + // Latin Extended-A, 0100–017F + // Latin Extended-B, 0180–024F + // IPA Extensions, 0250–02AF + // Spacing Modifier Letters, 02B0–02FF + // - U+02C7 ˇ ˇ Caron + // - U+02D8 ˘ ˘ Breve + // - U+02D9 Ë™ ˙ Dot Above + // - U+02DA Ëš ˚ Ring Above + // - U+02DB Ë› ˛ Ogonek + // - U+02DC Ëœ ˜ Small Tilde + // - U+02DD Ë� ˝ Double Acute Accent + // Latin Extended Additional, 1E00–1EFF + var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; + + var reWhitespace = /\S/; + + var wordDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/wordDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + wordDiff.equals = function (left, right) { + if (this.options.ignoreCase) { + left = left.toLowerCase(); + right = right.toLowerCase(); + } + return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); + }; + wordDiff.tokenize = function (value) { + var tokens = value.split(/(\s+|\b)/); + + // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. + for (var i = 0; i < tokens.length - 1; i++) { + // If we have an empty string in the next field and we have only word chars before and after, merge + if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { + tokens[i] += tokens[i + 2]; + tokens.splice(i + 1, 2); + i--; + } + } + + return tokens; + }; + + function diffWords(oldStr, newStr, options) { + options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(options, { ignoreWhitespace: true }); + return wordDiff.diff(oldStr, newStr, options); + } + + function diffWordsWithSpace(oldStr, newStr, options) { + return wordDiff.diff(oldStr, newStr, options); + } + + + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/generateOptions = generateOptions; + function generateOptions(options, defaults) { + if (typeof options === 'function') { + defaults.callback = options; + } else if (options) { + for (var name in options) { + /* istanbul ignore else */ + if (options.hasOwnProperty(name)) { + defaults[name] = options[name]; + } + } + } + return defaults; + } + + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.lineDiff = undefined; + exports. /*istanbul ignore end*/diffLines = diffLines; + /*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = diffTrimmedLines; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_params = __webpack_require__(4) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var lineDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/lineDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + lineDiff.tokenize = function (value) { + var retLines = [], + linesAndNewlines = value.split(/(\n|\r\n)/); + + // Ignore the final empty token that occurs if the string ends with a new line + if (!linesAndNewlines[linesAndNewlines.length - 1]) { + linesAndNewlines.pop(); + } + + // Merge the content and line separators into single tokens + for (var i = 0; i < linesAndNewlines.length; i++) { + var line = linesAndNewlines[i]; + + if (i % 2 && !this.options.newlineIsToken) { + retLines[retLines.length - 1] += line; + } else { + if (this.options.ignoreWhitespace) { + line = line.trim(); + } + retLines.push(line); + } + } + + return retLines; + }; + + function diffLines(oldStr, newStr, callback) { + return lineDiff.diff(oldStr, newStr, callback); + } + function diffTrimmedLines(oldStr, newStr, callback) { + var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true }); + return lineDiff.diff(oldStr, newStr, options); + } + + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.sentenceDiff = undefined; + exports. /*istanbul ignore end*/diffSentences = diffSentences; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var sentenceDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/sentenceDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + sentenceDiff.tokenize = function (value) { + return value.split(/(\S.+?[.!?])(?=\s+|$)/); + }; + + function diffSentences(oldStr, newStr, callback) { + return sentenceDiff.diff(oldStr, newStr, callback); + } + + + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.cssDiff = undefined; + exports. /*istanbul ignore end*/diffCss = diffCss; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var cssDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/cssDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + cssDiff.tokenize = function (value) { + return value.split(/([{}:;,]|\s+)/); + }; + + function diffCss(oldStr, newStr, callback) { + return cssDiff.diff(oldStr, newStr, callback); + } + + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.jsonDiff = undefined; + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + + exports. /*istanbul ignore end*/diffJson = diffJson; + /*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = canonicalize; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + /*istanbul ignore end*/var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var objectPrototypeToString = Object.prototype.toString; + + var jsonDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/jsonDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a + // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: + jsonDiff.useLongestToken = true; + + jsonDiff.tokenize = /*istanbul ignore start*/_line.lineDiff /*istanbul ignore end*/.tokenize; + jsonDiff.castInput = function (value) { + /*istanbul ignore start*/var _options = /*istanbul ignore end*/this.options, + undefinedReplacement = _options.undefinedReplacement, + _options$stringifyRep = _options.stringifyReplacer, + stringifyReplacer = _options$stringifyRep === undefined ? function (k, v) /*istanbul ignore start*/{ + return (/*istanbul ignore end*/typeof v === 'undefined' ? undefinedReplacement : v + ); + } : _options$stringifyRep; + + + return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); + }; + jsonDiff.equals = function (left, right) { + return (/*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')) + ); + }; + + function diffJson(oldObj, newObj, options) { + return jsonDiff.diff(oldObj, newObj, options); + } + + // This function handles the presence of circular references by bailing out when encountering an + // object that is already on the "stack" of items being processed. Accepts an optional replacer + function canonicalize(obj, stack, replacementStack, replacer, key) { + stack = stack || []; + replacementStack = replacementStack || []; + + if (replacer) { + obj = replacer(key, obj); + } + + var i = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + for (i = 0; i < stack.length; i += 1) { + if (stack[i] === obj) { + return replacementStack[i]; + } + } + + var canonicalizedObj = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + if ('[object Array]' === objectPrototypeToString.call(obj)) { + stack.push(obj); + canonicalizedObj = new Array(obj.length); + replacementStack.push(canonicalizedObj); + for (i = 0; i < obj.length; i += 1) { + canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); + } + stack.pop(); + replacementStack.pop(); + return canonicalizedObj; + } + + if (obj && obj.toJSON) { + obj = obj.toJSON(); + } + + if ( /*istanbul ignore start*/(typeof /*istanbul ignore end*/obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + replacementStack.push(canonicalizedObj); + var sortedKeys = [], + _key = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (_key in obj) { + /* istanbul ignore else */ + if (obj.hasOwnProperty(_key)) { + sortedKeys.push(_key); + } + } + sortedKeys.sort(); + for (i = 0; i < sortedKeys.length; i += 1) { + _key = sortedKeys[i]; + canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); + } + stack.pop(); + replacementStack.pop(); + } else { + canonicalizedObj = obj; + } + return canonicalizedObj; + } + + + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports.arrayDiff = undefined; + exports. /*istanbul ignore end*/diffArrays = diffArrays; + + var /*istanbul ignore start*/_base = __webpack_require__(1) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _base2 = _interopRequireDefault(_base); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/var arrayDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayDiff = new /*istanbul ignore start*/_base2['default'] /*istanbul ignore end*/(); + arrayDiff.tokenize = function (value) { + return value.slice(); + }; + arrayDiff.join = arrayDiff.removeEmpty = function (value) { + return value; + }; + + function diffArrays(oldArr, newArr, callback) { + return arrayDiff.diff(oldArr, newArr, callback); + } + + + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/applyPatch = applyPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = applyPatches; + + var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_distanceIterator = __webpack_require__(12) /*istanbul ignore end*/; + + /*istanbul ignore start*/var _distanceIterator2 = _interopRequireDefault(_distanceIterator); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + /*istanbul ignore end*/function applyPatch(source, uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } + + if (Array.isArray(uniDiff)) { + if (uniDiff.length > 1) { + throw new Error('applyPatch only works with a single input.'); + } + + uniDiff = uniDiff[0]; + } + + // Apply the diff to the input + var lines = source.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], + hunks = uniDiff.hunks, + compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) /*istanbul ignore start*/{ + return (/*istanbul ignore end*/line === patchContent + ); + }, + errorCount = 0, + fuzzFactor = options.fuzzFactor || 0, + minLine = 0, + offset = 0, + removeEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + addEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + + /** + * Checks if the hunk exactly fits on the provided location + */ + function hunkFits(hunk, toPos) { + for (var j = 0; j < hunk.lines.length; j++) { + var line = hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line; + + if (operation === ' ' || operation === '-') { + // Context sanity check + if (!compareLine(toPos + 1, lines[toPos], operation, content)) { + errorCount++; + + if (errorCount > fuzzFactor) { + return false; + } + } + toPos++; + } + } + + return true; + } + + // Search best fit offsets for each hunk based on the previous ones + for (var i = 0; i < hunks.length; i++) { + var hunk = hunks[i], + maxLine = lines.length - hunk.oldLines, + localOffset = 0, + toPos = offset + hunk.oldStart - 1; + + var iterator = /*istanbul ignore start*/(0, _distanceIterator2['default']) /*istanbul ignore end*/(toPos, minLine, maxLine); + + for (; localOffset !== undefined; localOffset = iterator()) { + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + break; + } + } + + if (localOffset === undefined) { + return false; + } + + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; + } + + // Apply patch hunks + var diffOffset = 0; + for (var _i = 0; _i < hunks.length; _i++) { + var _hunk = hunks[_i], + _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; + diffOffset += _hunk.newLines - _hunk.oldLines; + + if (_toPos < 0) { + // Creating a new file + _toPos = 0; + } + + for (var j = 0; j < _hunk.lines.length; j++) { + var line = _hunk.lines[j], + operation = line.length > 0 ? line[0] : ' ', + content = line.length > 0 ? line.substr(1) : line, + delimiter = _hunk.linedelimiters[j]; + + if (operation === ' ') { + _toPos++; + } else if (operation === '-') { + lines.splice(_toPos, 1); + delimiters.splice(_toPos, 1); + /* istanbul ignore else */ + } else if (operation === '+') { + lines.splice(_toPos, 0, content); + delimiters.splice(_toPos, 0, delimiter); + _toPos++; + } else if (operation === '\\') { + var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; + if (previousOperation === '+') { + removeEOFNL = true; + } else if (previousOperation === '-') { + addEOFNL = true; + } + } + } + } + + // Handle EOFNL insertion/removal + if (removeEOFNL) { + while (!lines[lines.length - 1]) { + lines.pop(); + delimiters.pop(); + } + } else if (addEOFNL) { + lines.push(''); + delimiters.push('\n'); + } + for (var _k = 0; _k < lines.length - 1; _k++) { + lines[_k] = lines[_k] + delimiters[_k]; + } + return lines.join(''); + } + + // Wrapper that supports multiple file patches via callbacks. + function applyPatches(uniDiff, options) { + if (typeof uniDiff === 'string') { + uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff); + } + + var currentIndex = 0; + function processIndex() { + var index = uniDiff[currentIndex++]; + if (!index) { + return options.complete(); + } + + options.loadFile(index, function (err, data) { + if (err) { + return options.complete(err); + } + + var updatedContent = applyPatch(data, index, options); + options.patched(index, updatedContent, function (err) { + if (err) { + return options.complete(err); + } + + processIndex(); + }); + }); + } + processIndex(); + } + + + +/***/ }), +/* 11 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/parsePatch = parsePatch; + function parsePatch(uniDiff) { + /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), + delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], + list = [], + i = 0; + + function parseIndex() { + var index = {}; + list.push(index); + + // Parse diff metadata + while (i < diffstr.length) { + var line = diffstr[i]; + + // File header found, end parsing diff metadata + if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { + break; + } + + // Diff index + var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); + if (header) { + index.index = header[1]; + } + + i++; + } + + // Parse file headers if they are defined. Unified diff requires them, but + // there's no technical issues to have an isolated hunk without file header + parseFileHeader(index); + parseFileHeader(index); + + // Parse hunks + index.hunks = []; + + while (i < diffstr.length) { + var _line = diffstr[i]; + + if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { + break; + } else if (/^@@/.test(_line)) { + index.hunks.push(parseHunk()); + } else if (_line && options.strict) { + // Ignore unexpected content unless in strict mode + throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); + } else { + i++; + } + } + } + + // Parses the --- and +++ headers, if none are found, no lines + // are consumed. + function parseFileHeader(index) { + var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); + if (fileHeader) { + var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; + var data = fileHeader[2].split('\t', 2); + var fileName = data[0].replace(/\\\\/g, '\\'); + if (/^".*"$/.test(fileName)) { + fileName = fileName.substr(1, fileName.length - 2); + } + index[keyPrefix + 'FileName'] = fileName; + index[keyPrefix + 'Header'] = (data[1] || '').trim(); + + i++; + } + } + + // Parses a hunk + // This assumes that we are at the start of a hunk. + function parseHunk() { + var chunkHeaderIndex = i, + chunkHeaderLine = diffstr[i++], + chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); + + var hunk = { + oldStart: +chunkHeader[1], + oldLines: +chunkHeader[2] || 1, + newStart: +chunkHeader[3], + newLines: +chunkHeader[4] || 1, + lines: [], + linedelimiters: [] + }; + + var addCount = 0, + removeCount = 0; + for (; i < diffstr.length; i++) { + // Lines starting with '---' could be mistaken for the "remove line" operation + // But they could be the header for the next file. Therefore prune such cases out. + if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { + break; + } + var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; + + if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { + hunk.lines.push(diffstr[i]); + hunk.linedelimiters.push(delimiters[i] || '\n'); + + if (operation === '+') { + addCount++; + } else if (operation === '-') { + removeCount++; + } else if (operation === ' ') { + addCount++; + removeCount++; + } + } else { + break; + } + } + + // Handle the empty block count case + if (!addCount && hunk.newLines === 1) { + hunk.newLines = 0; + } + if (!removeCount && hunk.oldLines === 1) { + hunk.oldLines = 0; + } + + // Perform optional sanity checking + if (options.strict) { + if (addCount !== hunk.newLines) { + throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + if (removeCount !== hunk.oldLines) { + throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); + } + } + + return hunk; + } + + while (i < diffstr.length) { + parseIndex(); + } + + return list; + } + + + +/***/ }), +/* 12 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/"use strict"; + + exports.__esModule = true; + + exports["default"] = /*istanbul ignore end*/function (start, minLine, maxLine) { + var wantForward = true, + backwardExhausted = false, + forwardExhausted = false, + localOffset = 1; + + return function iterator() { + if (wantForward && !forwardExhausted) { + if (backwardExhausted) { + localOffset++; + } else { + wantForward = false; + } + + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) + if (start + localOffset <= maxLine) { + return localOffset; + } + + forwardExhausted = true; + } + + if (!backwardExhausted) { + if (!forwardExhausted) { + wantForward = true; + } + + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location + if (minLine <= start - localOffset) { + return -localOffset++; + } + + backwardExhausted = true; + return iterator(); + } + + // We tried to fit hunk before text beginning and beyond text length, then + // hunk can't fit on the text. Return undefined + }; + }; + + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/calcLineCount = calcLineCount; + /*istanbul ignore start*/exports. /*istanbul ignore end*/merge = merge; + + var /*istanbul ignore start*/_create = __webpack_require__(14) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_parse = __webpack_require__(11) /*istanbul ignore end*/; + + var /*istanbul ignore start*/_array = __webpack_require__(15) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + + /*istanbul ignore end*/function calcLineCount(hunk) { + /*istanbul ignore start*/var _calcOldNewLineCount = /*istanbul ignore end*/calcOldNewLineCount(hunk.lines), + oldLines = _calcOldNewLineCount.oldLines, + newLines = _calcOldNewLineCount.newLines; + + if (oldLines !== undefined) { + hunk.oldLines = oldLines; + } else { + delete hunk.oldLines; + } + + if (newLines !== undefined) { + hunk.newLines = newLines; + } else { + delete hunk.newLines; + } + } + + function merge(mine, theirs, base) { + mine = loadPatch(mine, base); + theirs = loadPatch(theirs, base); + + var ret = {}; + + // For index we just let it pass through as it doesn't have any necessary meaning. + // Leaving sanity checks on this to the API consumer that may know more about the + // meaning in their own context. + if (mine.index || theirs.index) { + ret.index = mine.index || theirs.index; + } + + if (mine.newFileName || theirs.newFileName) { + if (!fileNameChanged(mine)) { + // No header or no change in ours, use theirs (and ours if theirs does not exist) + ret.oldFileName = theirs.oldFileName || mine.oldFileName; + ret.newFileName = theirs.newFileName || mine.newFileName; + ret.oldHeader = theirs.oldHeader || mine.oldHeader; + ret.newHeader = theirs.newHeader || mine.newHeader; + } else if (!fileNameChanged(theirs)) { + // No header or no change in theirs, use ours + ret.oldFileName = mine.oldFileName; + ret.newFileName = mine.newFileName; + ret.oldHeader = mine.oldHeader; + ret.newHeader = mine.newHeader; + } else { + // Both changed... figure it out + ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName); + ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName); + ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader); + ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); + } + } + + ret.hunks = []; + + var mineIndex = 0, + theirsIndex = 0, + mineOffset = 0, + theirsOffset = 0; + + while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { + var mineCurrent = mine.hunks[mineIndex] || { oldStart: Infinity }, + theirsCurrent = theirs.hunks[theirsIndex] || { oldStart: Infinity }; + + if (hunkBefore(mineCurrent, theirsCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); + mineIndex++; + theirsOffset += mineCurrent.newLines - mineCurrent.oldLines; + } else if (hunkBefore(theirsCurrent, mineCurrent)) { + // This patch does not overlap with any of the others, yay. + ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset)); + theirsIndex++; + mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines; + } else { + // Overlap, merge as best we can + var mergedHunk = { + oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart), + oldLines: 0, + newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset), + newLines: 0, + lines: [] + }; + mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines); + theirsIndex++; + mineIndex++; + + ret.hunks.push(mergedHunk); + } + } + + return ret; + } + + function loadPatch(param, base) { + if (typeof param === 'string') { + if (/^@@/m.test(param) || /^Index:/m.test(param)) { + return (/*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(param)[0] + ); + } + + if (!base) { + throw new Error('Must provide a base reference or pass in a patch'); + } + return (/*istanbul ignore start*/(0, _create.structuredPatch) /*istanbul ignore end*/(undefined, undefined, base, param) + ); + } + + return param; + } + + function fileNameChanged(patch) { + return patch.newFileName && patch.newFileName !== patch.oldFileName; + } + + function selectField(index, mine, theirs) { + if (mine === theirs) { + return mine; + } else { + index.conflict = true; + return { mine: mine, theirs: theirs }; + } + } + + function hunkBefore(test, check) { + return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; + } + + function cloneHunk(hunk, offset) { + return { + oldStart: hunk.oldStart, oldLines: hunk.oldLines, + newStart: hunk.newStart + offset, newLines: hunk.newLines, + lines: hunk.lines + }; + } + + function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { + // This will generally result in a conflicted hunk, but there are cases where the context + // is the only overlap where we can successfully merge the content here. + var mine = { offset: mineOffset, lines: mineLines, index: 0 }, + their = { offset: theirOffset, lines: theirLines, index: 0 }; + + // Handle any leading content + insertLeading(hunk, mine, their); + insertLeading(hunk, their, mine); + + // Now in the overlap content. Scan through and select the best changes from each. + while (mine.index < mine.lines.length && their.index < their.lines.length) { + var mineCurrent = mine.lines[mine.index], + theirCurrent = their.lines[their.index]; + + if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { + // Both modified ... + mutualChange(hunk, mine, their); + } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { + /*istanbul ignore start*/var _hunk$lines; + + /*istanbul ignore end*/ // Mine inserted + /*istanbul ignore start*/(_hunk$lines = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(mine))); + } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { + /*istanbul ignore start*/var _hunk$lines2; + + /*istanbul ignore end*/ // Theirs inserted + /*istanbul ignore start*/(_hunk$lines2 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/collectChange(their))); + } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { + // Mine removed or edited + removal(hunk, mine, their); + } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') { + // Their removed or edited + removal(hunk, their, mine, true); + } else if (mineCurrent === theirCurrent) { + // Context identity + hunk.lines.push(mineCurrent); + mine.index++; + their.index++; + } else { + // Context mismatch + conflict(hunk, collectChange(mine), collectChange(their)); + } + } + + // Now push anything that may be remaining + insertTrailing(hunk, mine); + insertTrailing(hunk, their); + + calcLineCount(hunk); + } + + function mutualChange(hunk, mine, their) { + var myChanges = collectChange(mine), + theirChanges = collectChange(their); + + if (allRemoves(myChanges) && allRemoves(theirChanges)) { + // Special case for remove changes that are supersets of one another + if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { + /*istanbul ignore start*/var _hunk$lines3; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines3 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); + return; + } else if ( /*istanbul ignore start*/(0, _array.arrayStartsWith) /*istanbul ignore end*/(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { + /*istanbul ignore start*/var _hunk$lines4; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines4 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines4 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges)); + return; + } + } else if ( /*istanbul ignore start*/(0, _array.arrayEqual) /*istanbul ignore end*/(myChanges, theirChanges)) { + /*istanbul ignore start*/var _hunk$lines5; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines5 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines5 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/myChanges)); + return; + } + + conflict(hunk, myChanges, theirChanges); + } + + function removal(hunk, mine, their, swap) { + var myChanges = collectChange(mine), + theirChanges = collectContext(their, myChanges); + if (theirChanges.merged) { + /*istanbul ignore start*/var _hunk$lines6; + + /*istanbul ignore end*/ /*istanbul ignore start*/(_hunk$lines6 = /*istanbul ignore end*/hunk.lines).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_hunk$lines6 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/theirChanges.merged)); + } else { + conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); + } + } + + function conflict(hunk, mine, their) { + hunk.conflict = true; + hunk.lines.push({ + conflict: true, + mine: mine, + theirs: their + }); + } + + function insertLeading(hunk, insert, their) { + while (insert.offset < their.offset && insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + insert.offset++; + } + } + function insertTrailing(hunk, insert) { + while (insert.index < insert.lines.length) { + var line = insert.lines[insert.index++]; + hunk.lines.push(line); + } + } + + function collectChange(state) { + var ret = [], + operation = state.lines[state.index][0]; + while (state.index < state.lines.length) { + var line = state.lines[state.index]; + + // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. + if (operation === '-' && line[0] === '+') { + operation = '+'; + } + + if (operation === line[0]) { + ret.push(line); + state.index++; + } else { + break; + } + } + + return ret; + } + function collectContext(state, matchChanges) { + var changes = [], + merged = [], + matchIndex = 0, + contextChanges = false, + conflicted = false; + while (matchIndex < matchChanges.length && state.index < state.lines.length) { + var change = state.lines[state.index], + match = matchChanges[matchIndex]; + + // Once we've hit our add, then we are done + if (match[0] === '+') { + break; + } + + contextChanges = contextChanges || change[0] !== ' '; + + merged.push(match); + matchIndex++; + + // Consume any additions in the other block as a conflict to attempt + // to pull in the remaining context after this + if (change[0] === '+') { + conflicted = true; + + while (change[0] === '+') { + changes.push(change); + change = state.lines[++state.index]; + } + } + + if (match.substr(1) === change.substr(1)) { + changes.push(change); + state.index++; + } else { + conflicted = true; + } + } + + if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { + conflicted = true; + } + + if (conflicted) { + return changes; + } + + while (matchIndex < matchChanges.length) { + merged.push(matchChanges[matchIndex++]); + } + + return { + merged: merged, + changes: changes + }; + } + + function allRemoves(changes) { + return changes.reduce(function (prev, change) { + return prev && change[0] === '-'; + }, true); + } + function skipRemoveSuperset(state, removeChanges, delta) { + for (var i = 0; i < delta; i++) { + var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); + if (state.lines[state.index + i] !== ' ' + changeContent) { + return false; + } + } + + state.index += delta; + return true; + } + + function calcOldNewLineCount(lines) { + var oldLines = 0; + var newLines = 0; + + lines.forEach(function (line) { + if (typeof line !== 'string') { + var myCount = calcOldNewLineCount(line.mine); + var theirCount = calcOldNewLineCount(line.theirs); + + if (oldLines !== undefined) { + if (myCount.oldLines === theirCount.oldLines) { + oldLines += myCount.oldLines; + } else { + oldLines = undefined; + } + } + + if (newLines !== undefined) { + if (myCount.newLines === theirCount.newLines) { + newLines += myCount.newLines; + } else { + newLines = undefined; + } + } + } else { + if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { + newLines++; + } + if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { + oldLines++; + } + } + }); + + return { oldLines: oldLines, newLines: newLines }; + } + + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/structuredPatch = structuredPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = createTwoFilesPatch; + /*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = createPatch; + + var /*istanbul ignore start*/_line = __webpack_require__(5) /*istanbul ignore end*/; + + /*istanbul ignore start*/function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + + /*istanbul ignore end*/function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + if (!options) { + options = {}; + } + if (typeof options.context === 'undefined') { + options.context = 4; + } + + var diff = /*istanbul ignore start*/(0, _line.diffLines) /*istanbul ignore end*/(oldStr, newStr, options); + diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier + + function contextLines(lines) { + return lines.map(function (entry) { + return ' ' + entry; + }); + } + + var hunks = []; + var oldRangeStart = 0, + newRangeStart = 0, + curRange = [], + oldLine = 1, + newLine = 1; + + /*istanbul ignore start*/var _loop = function _loop( /*istanbul ignore end*/i) { + var current = diff[i], + lines = current.lines || current.value.replace(/\n$/, '').split('\n'); + current.lines = lines; + + if (current.added || current.removed) { + /*istanbul ignore start*/var _curRange; + + /*istanbul ignore end*/ // If we have previous context, start with that + if (!oldRangeStart) { + var prev = diff[i - 1]; + oldRangeStart = oldLine; + newRangeStart = newLine; + + if (prev) { + curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; + oldRangeStart -= curRange.length; + newRangeStart -= curRange.length; + } + } + + // Output our changes + /*istanbul ignore start*/(_curRange = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/lines.map(function (entry) { + return (current.added ? '+' : '-') + entry; + }))); + + // Track the updated file position + if (current.added) { + newLine += lines.length; + } else { + oldLine += lines.length; + } + } else { + // Identical context lines. Track line changes + if (oldRangeStart) { + // Close out any changes that have been output (or join overlapping) + if (lines.length <= options.context * 2 && i < diff.length - 2) { + /*istanbul ignore start*/var _curRange2; + + /*istanbul ignore end*/ // Overlapping + /*istanbul ignore start*/(_curRange2 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines))); + } else { + /*istanbul ignore start*/var _curRange3; + + /*istanbul ignore end*/ // end the range and output + var contextSize = Math.min(lines.length, options.context); + /*istanbul ignore start*/(_curRange3 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines.slice(0, contextSize)))); + + var hunk = { + oldStart: oldRangeStart, + oldLines: oldLine - oldRangeStart + contextSize, + newStart: newRangeStart, + newLines: newLine - newRangeStart + contextSize, + lines: curRange + }; + if (i >= diff.length - 2 && lines.length <= options.context) { + // EOF is inside this hunk + var oldEOFNewline = /\n$/.test(oldStr); + var newEOFNewline = /\n$/.test(newStr); + if (lines.length == 0 && !oldEOFNewline) { + // special case: old has no eol and no trailing context; no-nl can end up before adds + curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); + } else if (!oldEOFNewline || !newEOFNewline) { + curRange.push('\\ No newline at end of file'); + } + } + hunks.push(hunk); + + oldRangeStart = 0; + newRangeStart = 0; + curRange = []; + } + } + oldLine += lines.length; + newLine += lines.length; + } + }; + + for (var i = 0; i < diff.length; i++) { + /*istanbul ignore start*/_loop( /*istanbul ignore end*/i); + } + + return { + oldFileName: oldFileName, newFileName: newFileName, + oldHeader: oldHeader, newHeader: newHeader, + hunks: hunks + }; + } + + function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { + var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); + + var ret = []; + if (oldFileName == newFileName) { + ret.push('Index: ' + oldFileName); + } + ret.push('==================================================================='); + ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); + ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); + + for (var i = 0; i < diff.hunks.length; i++) { + var hunk = diff.hunks[i]; + ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); + ret.push.apply(ret, hunk.lines); + } + + return ret.join('\n') + '\n'; + } + + function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { + return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); + } + + + +/***/ }), +/* 15 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/"use strict"; + + exports.__esModule = true; + exports. /*istanbul ignore end*/arrayEqual = arrayEqual; + /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayStartsWith = arrayStartsWith; + function arrayEqual(a, b) { + if (a.length !== b.length) { + return false; + } + + return arrayStartsWith(a, b); + } + + function arrayStartsWith(array, start) { + if (start.length > array.length) { + return false; + } + + for (var i = 0; i < start.length; i++) { + if (start[i] !== array[i]) { + return false; + } + } + + return true; + } + + + +/***/ }), +/* 16 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/"use strict"; + + exports.__esModule = true; + exports. /*istanbul ignore end*/convertChangesToDMP = convertChangesToDMP; + // See: http://code.google.com/p/google-diff-match-patch/wiki/API + function convertChangesToDMP(changes) { + var ret = [], + change = /*istanbul ignore start*/void 0 /*istanbul ignore end*/, + operation = /*istanbul ignore start*/void 0 /*istanbul ignore end*/; + for (var i = 0; i < changes.length; i++) { + change = changes[i]; + if (change.added) { + operation = 1; + } else if (change.removed) { + operation = -1; + } else { + operation = 0; + } + + ret.push([operation, change.value]); + } + return ret; + } + + + +/***/ }), +/* 17 */ +/***/ (function(module, exports) { + + /*istanbul ignore start*/'use strict'; + + exports.__esModule = true; + exports. /*istanbul ignore end*/convertChangesToXML = convertChangesToXML; + function convertChangesToXML(changes) { + var ret = []; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + + ret.push(escapeHTML(change.value)); + + if (change.added) { + ret.push(''); + } else if (change.removed) { + ret.push(''); + } + } + return ret.join(''); + } + + function escapeHTML(s) { + var n = s; + n = n.replace(/&/g, '&'); + n = n.replace(//g, '>'); + n = n.replace(/"/g, '"'); + + return n; + } + + + +/***/ }) +/******/ ]) +}); +; +},{}],49:[function(require,module,exports){ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; + +},{}],50:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var objectCreate = Object.create || objectCreatePolyfill +var objectKeys = Object.keys || objectKeysPolyfill +var bind = Function.prototype.bind || functionBindPolyfill + +function EventEmitter() { + if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) { + this._events = objectCreate(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +var defaultMaxListeners = 10; + +var hasDefineProperty; +try { + var o = {}; + if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 }); + hasDefineProperty = o.x === 0; +} catch (err) { hasDefineProperty = false } +if (hasDefineProperty) { + Object.defineProperty(EventEmitter, 'defaultMaxListeners', { + enumerable: true, + get: function() { + return defaultMaxListeners; + }, + set: function(arg) { + // check whether the input is a positive number (whose value is zero or + // greater and not a NaN). + if (typeof arg !== 'number' || arg < 0 || arg !== arg) + throw new TypeError('"defaultMaxListeners" must be a positive number'); + defaultMaxListeners = arg; + } + }); +} else { + EventEmitter.defaultMaxListeners = defaultMaxListeners; +} + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { + if (typeof n !== 'number' || n < 0 || isNaN(n)) + throw new TypeError('"n" argument must be a positive number'); + this._maxListeners = n; + return this; +}; + +function $getMaxListeners(that) { + if (that._maxListeners === undefined) + return EventEmitter.defaultMaxListeners; + return that._maxListeners; +} + +EventEmitter.prototype.getMaxListeners = function getMaxListeners() { + return $getMaxListeners(this); +}; + +// These standalone emit* functions are used to optimize calling of event +// handlers for fast cases because emit() itself often has a variable number of +// arguments and can be deoptimized because of that. These functions always have +// the same number of arguments and thus do not get deoptimized, so the code +// inside them can execute faster. +function emitNone(handler, isFn, self) { + if (isFn) + handler.call(self); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self); + } +} +function emitOne(handler, isFn, self, arg1) { + if (isFn) + handler.call(self, arg1); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1); + } +} +function emitTwo(handler, isFn, self, arg1, arg2) { + if (isFn) + handler.call(self, arg1, arg2); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1, arg2); + } +} +function emitThree(handler, isFn, self, arg1, arg2, arg3) { + if (isFn) + handler.call(self, arg1, arg2, arg3); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1, arg2, arg3); + } +} + +function emitMany(handler, isFn, self, args) { + if (isFn) + handler.apply(self, args); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].apply(self, args); + } +} + +EventEmitter.prototype.emit = function emit(type) { + var er, handler, len, args, i, events; + var doError = (type === 'error'); + + events = this._events; + if (events) + doError = (doError && events.error == null); + else if (!doError) + return false; + + // If there is no 'error' event listener then throw. + if (doError) { + if (arguments.length > 1) + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } else { + // At least give some kind of context to the user + var err = new Error('Unhandled "error" event. (' + er + ')'); + err.context = er; + throw err; + } + return false; + } + + handler = events[type]; + + if (!handler) + return false; + + var isFn = typeof handler === 'function'; + len = arguments.length; + switch (len) { + // fast cases + case 1: + emitNone(handler, isFn, this); + break; + case 2: + emitOne(handler, isFn, this, arguments[1]); + break; + case 3: + emitTwo(handler, isFn, this, arguments[1], arguments[2]); + break; + case 4: + emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); + break; + // slower + default: + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + emitMany(handler, isFn, this, args); + } + + return true; +}; + +function _addListener(target, type, listener, prepend) { + var m; + var events; + var existing; + + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + + events = target._events; + if (!events) { + events = target._events = objectCreate(null); + target._eventsCount = 0; + } else { + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (events.newListener) { + target.emit('newListener', type, + listener.listener ? listener.listener : listener); + + // Re-assign `events` because a newListener handler could have caused the + // this._events to be assigned to a new object + events = target._events; + } + existing = events[type]; + } + + if (!existing) { + // Optimize the case of one listener. Don't need the extra array object. + existing = events[type] = listener; + ++target._eventsCount; + } else { + if (typeof existing === 'function') { + // Adding the second element, need to change to array. + existing = events[type] = + prepend ? [listener, existing] : [existing, listener]; + } else { + // If we've already got an array, just append. + if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + } + + // Check for listener leak + if (!existing.warned) { + m = $getMaxListeners(target); + if (m && m > 0 && existing.length > m) { + existing.warned = true; + var w = new Error('Possible EventEmitter memory leak detected. ' + + existing.length + ' "' + String(type) + '" listeners ' + + 'added. Use emitter.setMaxListeners() to ' + + 'increase limit.'); + w.name = 'MaxListenersExceededWarning'; + w.emitter = target; + w.type = type; + w.count = existing.length; + if (typeof console === 'object' && console.warn) { + console.warn('%s: %s', w.name, w.message); + } + } + } + } + + return target; +} + +EventEmitter.prototype.addListener = function addListener(type, listener) { + return _addListener(this, type, listener, false); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.prependListener = + function prependListener(type, listener) { + return _addListener(this, type, listener, true); + }; + +function onceWrapper() { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + switch (arguments.length) { + case 0: + return this.listener.call(this.target); + case 1: + return this.listener.call(this.target, arguments[0]); + case 2: + return this.listener.call(this.target, arguments[0], arguments[1]); + case 3: + return this.listener.call(this.target, arguments[0], arguments[1], + arguments[2]); + default: + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) + args[i] = arguments[i]; + this.listener.apply(this.target, args); + } + } +} + +function _onceWrap(target, type, listener) { + var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; + var wrapped = bind.call(onceWrapper, state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +EventEmitter.prototype.once = function once(type, listener) { + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.prependOnceListener = + function prependOnceListener(type, listener) { + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + this.prependListener(type, _onceWrap(this, type, listener)); + return this; + }; + +// Emits a 'removeListener' event if and only if the listener was removed. +EventEmitter.prototype.removeListener = + function removeListener(type, listener) { + var list, events, position, i, originalListener; + + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + + events = this._events; + if (!events) + return this; + + list = events[type]; + if (!list) + return this; + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) + this._events = objectCreate(null); + else { + delete events[type]; + if (events.removeListener) + this.emit('removeListener', type, list.listener || listener); + } + } else if (typeof list !== 'function') { + position = -1; + + for (i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { + originalListener = list[i].listener; + position = i; + break; + } + } + + if (position < 0) + return this; + + if (position === 0) + list.shift(); + else + spliceOne(list, position); + + if (list.length === 1) + events[type] = list[0]; + + if (events.removeListener) + this.emit('removeListener', type, originalListener || listener); + } + + return this; + }; + +EventEmitter.prototype.removeAllListeners = + function removeAllListeners(type) { + var listeners, events, i; + + events = this._events; + if (!events) + return this; + + // not listening for removeListener, no need to emit + if (!events.removeListener) { + if (arguments.length === 0) { + this._events = objectCreate(null); + this._eventsCount = 0; + } else if (events[type]) { + if (--this._eventsCount === 0) + this._events = objectCreate(null); + else + delete events[type]; + } + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + var keys = objectKeys(events); + var key; + for (i = 0; i < keys.length; ++i) { + key = keys[i]; + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = objectCreate(null); + this._eventsCount = 0; + return this; + } + + listeners = events[type]; + + if (typeof listeners === 'function') { + this.removeListener(type, listeners); + } else if (listeners) { + // LIFO order + for (i = listeners.length - 1; i >= 0; i--) { + this.removeListener(type, listeners[i]); + } + } + + return this; + }; + +function _listeners(target, type, unwrap) { + var events = target._events; + + if (!events) + return []; + + var evlistener = events[type]; + if (!evlistener) + return []; + + if (typeof evlistener === 'function') + return unwrap ? [evlistener.listener || evlistener] : [evlistener]; + + return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); +} + +EventEmitter.prototype.listeners = function listeners(type) { + return _listeners(this, type, true); +}; + +EventEmitter.prototype.rawListeners = function rawListeners(type) { + return _listeners(this, type, false); +}; + +EventEmitter.listenerCount = function(emitter, type) { + if (typeof emitter.listenerCount === 'function') { + return emitter.listenerCount(type); + } else { + return listenerCount.call(emitter, type); + } +}; + +EventEmitter.prototype.listenerCount = listenerCount; +function listenerCount(type) { + var events = this._events; + + if (events) { + var evlistener = events[type]; + + if (typeof evlistener === 'function') { + return 1; + } else if (evlistener) { + return evlistener.length; + } + } + + return 0; +} + +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; +}; + +// About 1.5x faster than the two-arg version of Array#splice(). +function spliceOne(list, index) { + for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) + list[i] = list[k]; + list.pop(); +} + +function arrayClone(arr, n) { + var copy = new Array(n); + for (var i = 0; i < n; ++i) + copy[i] = arr[i]; + return copy; +} + +function unwrapListeners(arr) { + var ret = new Array(arr.length); + for (var i = 0; i < ret.length; ++i) { + ret[i] = arr[i].listener || arr[i]; + } + return ret; +} + +function objectCreatePolyfill(proto) { + var F = function() {}; + F.prototype = proto; + return new F; +} +function objectKeysPolyfill(obj) { + var keys = []; + for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) { + keys.push(k); + } + return k; +} +function functionBindPolyfill(context) { + var fn = this; + return function () { + return fn.apply(context, arguments); + }; +} + +},{}],51:[function(require,module,exports){ +'use strict'; + +/* eslint no-invalid-this: 1 */ + +var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; +var slice = Array.prototype.slice; +var toStr = Object.prototype.toString; +var funcType = '[object Function]'; + +module.exports = function bind(that) { + var target = this; + if (typeof target !== 'function' || toStr.call(target) !== funcType) { + throw new TypeError(ERROR_MESSAGE + target); + } + var args = slice.call(arguments, 1); + + var bound; + var binder = function () { + if (this instanceof bound) { + var result = target.apply( + this, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + } + }; + + var boundLength = Math.max(0, target.length - args.length); + var boundArgs = []; + for (var i = 0; i < boundLength; i++) { + boundArgs.push('$' + i); + } + + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + + if (target.prototype) { + var Empty = function Empty() {}; + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + + return bound; +}; + +},{}],52:[function(require,module,exports){ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = Function.prototype.bind || implementation; + +},{"./implementation":51}],53:[function(require,module,exports){ +'use strict'; + +/* eslint complexity: [2, 17], max-statements: [2, 33] */ +module.exports = function hasSymbols() { + if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } + if (typeof Symbol.iterator === 'symbol') { return true; } + + var obj = {}; + var sym = Symbol('test'); + var symObj = Object(sym); + if (typeof sym === 'string') { return false; } + + if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } + if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } + + // temp disabled per https://github.com/ljharb/object.assign/issues/17 + // if (sym instanceof Symbol) { return false; } + // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 + // if (!(symObj instanceof Symbol)) { return false; } + + // if (typeof Symbol.prototype.toString !== 'function') { return false; } + // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } + + var symVal = 42; + obj[sym] = symVal; + for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax + if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } + + if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } + + var syms = Object.getOwnPropertySymbols(obj); + if (syms.length !== 1 || syms[0] !== sym) { return false; } + + if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } + + if (typeof Object.getOwnPropertyDescriptor === 'function') { + var descriptor = Object.getOwnPropertyDescriptor(obj, sym); + if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } + } + + return true; +}; + +},{}],54:[function(require,module,exports){ +(function (global){ +/*! https://mths.be/he v1.2.0 by @mathias | MIT license */ +;(function(root) { + + // Detect free variables `exports`. + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module`. + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root`. + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + // All astral symbols. + var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; + // All ASCII symbols (not just printable ASCII) except those listed in the + // first column of the overrides table. + // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides + var regexAsciiWhitelist = /[\x01-\x7F]/g; + // All BMP symbols that are not ASCII newlines, printable ASCII symbols, or + // code points listed in the first column of the overrides table on + // https://html.spec.whatwg.org/multipage/syntax.html#table-charref-overrides. + var regexBmpWhitelist = /[\x01-\t\x0B\f\x0E-\x1F\x7F\x81\x8D\x8F\x90\x9D\xA0-\uFFFF]/g; + + var regexEncodeNonAscii = /<\u20D2|=\u20E5|>\u20D2|\u205F\u200A|\u219D\u0338|\u2202\u0338|\u2220\u20D2|\u2229\uFE00|\u222A\uFE00|\u223C\u20D2|\u223D\u0331|\u223E\u0333|\u2242\u0338|\u224B\u0338|\u224D\u20D2|\u224E\u0338|\u224F\u0338|\u2250\u0338|\u2261\u20E5|\u2264\u20D2|\u2265\u20D2|\u2266\u0338|\u2267\u0338|\u2268\uFE00|\u2269\uFE00|\u226A\u0338|\u226A\u20D2|\u226B\u0338|\u226B\u20D2|\u227F\u0338|\u2282\u20D2|\u2283\u20D2|\u228A\uFE00|\u228B\uFE00|\u228F\u0338|\u2290\u0338|\u2293\uFE00|\u2294\uFE00|\u22B4\u20D2|\u22B5\u20D2|\u22D8\u0338|\u22D9\u0338|\u22DA\uFE00|\u22DB\uFE00|\u22F5\u0338|\u22F9\u0338|\u2933\u0338|\u29CF\u0338|\u29D0\u0338|\u2A6D\u0338|\u2A70\u0338|\u2A7D\u0338|\u2A7E\u0338|\u2AA1\u0338|\u2AA2\u0338|\u2AAC\uFE00|\u2AAD\uFE00|\u2AAF\u0338|\u2AB0\u0338|\u2AC5\u0338|\u2AC6\u0338|\u2ACB\uFE00|\u2ACC\uFE00|\u2AFD\u20E5|[\xA0-\u0113\u0116-\u0122\u0124-\u012B\u012E-\u014D\u0150-\u017E\u0192\u01B5\u01F5\u0237\u02C6\u02C7\u02D8-\u02DD\u0311\u0391-\u03A1\u03A3-\u03A9\u03B1-\u03C9\u03D1\u03D2\u03D5\u03D6\u03DC\u03DD\u03F0\u03F1\u03F5\u03F6\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E\u045F\u2002-\u2005\u2007-\u2010\u2013-\u2016\u2018-\u201A\u201C-\u201E\u2020-\u2022\u2025\u2026\u2030-\u2035\u2039\u203A\u203E\u2041\u2043\u2044\u204F\u2057\u205F-\u2063\u20AC\u20DB\u20DC\u2102\u2105\u210A-\u2113\u2115-\u211E\u2122\u2124\u2127-\u2129\u212C\u212D\u212F-\u2131\u2133-\u2138\u2145-\u2148\u2153-\u215E\u2190-\u219B\u219D-\u21A7\u21A9-\u21AE\u21B0-\u21B3\u21B5-\u21B7\u21BA-\u21DB\u21DD\u21E4\u21E5\u21F5\u21FD-\u2205\u2207-\u2209\u220B\u220C\u220F-\u2214\u2216-\u2218\u221A\u221D-\u2238\u223A-\u2257\u2259\u225A\u225C\u225F-\u2262\u2264-\u228B\u228D-\u229B\u229D-\u22A5\u22A7-\u22B0\u22B2-\u22BB\u22BD-\u22DB\u22DE-\u22E3\u22E6-\u22F7\u22F9-\u22FE\u2305\u2306\u2308-\u2310\u2312\u2313\u2315\u2316\u231C-\u231F\u2322\u2323\u232D\u232E\u2336\u233D\u233F\u237C\u23B0\u23B1\u23B4-\u23B6\u23DC-\u23DF\u23E2\u23E7\u2423\u24C8\u2500\u2502\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C\u2550-\u256C\u2580\u2584\u2588\u2591-\u2593\u25A1\u25AA\u25AB\u25AD\u25AE\u25B1\u25B3-\u25B5\u25B8\u25B9\u25BD-\u25BF\u25C2\u25C3\u25CA\u25CB\u25EC\u25EF\u25F8-\u25FC\u2605\u2606\u260E\u2640\u2642\u2660\u2663\u2665\u2666\u266A\u266D-\u266F\u2713\u2717\u2720\u2736\u2758\u2772\u2773\u27C8\u27C9\u27E6-\u27ED\u27F5-\u27FA\u27FC\u27FF\u2902-\u2905\u290C-\u2913\u2916\u2919-\u2920\u2923-\u292A\u2933\u2935-\u2939\u293C\u293D\u2945\u2948-\u294B\u294E-\u2976\u2978\u2979\u297B-\u297F\u2985\u2986\u298B-\u2996\u299A\u299C\u299D\u29A4-\u29B7\u29B9\u29BB\u29BC\u29BE-\u29C5\u29C9\u29CD-\u29D0\u29DC-\u29DE\u29E3-\u29E5\u29EB\u29F4\u29F6\u2A00-\u2A02\u2A04\u2A06\u2A0C\u2A0D\u2A10-\u2A17\u2A22-\u2A27\u2A29\u2A2A\u2A2D-\u2A31\u2A33-\u2A3C\u2A3F\u2A40\u2A42-\u2A4D\u2A50\u2A53-\u2A58\u2A5A-\u2A5D\u2A5F\u2A66\u2A6A\u2A6D-\u2A75\u2A77-\u2A9A\u2A9D-\u2AA2\u2AA4-\u2AB0\u2AB3-\u2AC8\u2ACB\u2ACC\u2ACF-\u2ADB\u2AE4\u2AE6-\u2AE9\u2AEB-\u2AF3\u2AFD\uFB00-\uFB04]|\uD835[\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDCCF\uDD04\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDD6B]/g; + var encodeMap = {'\xAD':'shy','\u200C':'zwnj','\u200D':'zwj','\u200E':'lrm','\u2063':'ic','\u2062':'it','\u2061':'af','\u200F':'rlm','\u200B':'ZeroWidthSpace','\u2060':'NoBreak','\u0311':'DownBreve','\u20DB':'tdot','\u20DC':'DotDot','\t':'Tab','\n':'NewLine','\u2008':'puncsp','\u205F':'MediumSpace','\u2009':'thinsp','\u200A':'hairsp','\u2004':'emsp13','\u2002':'ensp','\u2005':'emsp14','\u2003':'emsp','\u2007':'numsp','\xA0':'nbsp','\u205F\u200A':'ThickSpace','\u203E':'oline','_':'lowbar','\u2010':'dash','\u2013':'ndash','\u2014':'mdash','\u2015':'horbar',',':'comma',';':'semi','\u204F':'bsemi',':':'colon','\u2A74':'Colone','!':'excl','\xA1':'iexcl','?':'quest','\xBF':'iquest','.':'period','\u2025':'nldr','\u2026':'mldr','\xB7':'middot','\'':'apos','\u2018':'lsquo','\u2019':'rsquo','\u201A':'sbquo','\u2039':'lsaquo','\u203A':'rsaquo','"':'quot','\u201C':'ldquo','\u201D':'rdquo','\u201E':'bdquo','\xAB':'laquo','\xBB':'raquo','(':'lpar',')':'rpar','[':'lsqb',']':'rsqb','{':'lcub','}':'rcub','\u2308':'lceil','\u2309':'rceil','\u230A':'lfloor','\u230B':'rfloor','\u2985':'lopar','\u2986':'ropar','\u298B':'lbrke','\u298C':'rbrke','\u298D':'lbrkslu','\u298E':'rbrksld','\u298F':'lbrksld','\u2990':'rbrkslu','\u2991':'langd','\u2992':'rangd','\u2993':'lparlt','\u2994':'rpargt','\u2995':'gtlPar','\u2996':'ltrPar','\u27E6':'lobrk','\u27E7':'robrk','\u27E8':'lang','\u27E9':'rang','\u27EA':'Lang','\u27EB':'Rang','\u27EC':'loang','\u27ED':'roang','\u2772':'lbbrk','\u2773':'rbbrk','\u2016':'Vert','\xA7':'sect','\xB6':'para','@':'commat','*':'ast','/':'sol','undefined':null,'&':'amp','#':'num','%':'percnt','\u2030':'permil','\u2031':'pertenk','\u2020':'dagger','\u2021':'Dagger','\u2022':'bull','\u2043':'hybull','\u2032':'prime','\u2033':'Prime','\u2034':'tprime','\u2057':'qprime','\u2035':'bprime','\u2041':'caret','`':'grave','\xB4':'acute','\u02DC':'tilde','^':'Hat','\xAF':'macr','\u02D8':'breve','\u02D9':'dot','\xA8':'die','\u02DA':'ring','\u02DD':'dblac','\xB8':'cedil','\u02DB':'ogon','\u02C6':'circ','\u02C7':'caron','\xB0':'deg','\xA9':'copy','\xAE':'reg','\u2117':'copysr','\u2118':'wp','\u211E':'rx','\u2127':'mho','\u2129':'iiota','\u2190':'larr','\u219A':'nlarr','\u2192':'rarr','\u219B':'nrarr','\u2191':'uarr','\u2193':'darr','\u2194':'harr','\u21AE':'nharr','\u2195':'varr','\u2196':'nwarr','\u2197':'nearr','\u2198':'searr','\u2199':'swarr','\u219D':'rarrw','\u219D\u0338':'nrarrw','\u219E':'Larr','\u219F':'Uarr','\u21A0':'Rarr','\u21A1':'Darr','\u21A2':'larrtl','\u21A3':'rarrtl','\u21A4':'mapstoleft','\u21A5':'mapstoup','\u21A6':'map','\u21A7':'mapstodown','\u21A9':'larrhk','\u21AA':'rarrhk','\u21AB':'larrlp','\u21AC':'rarrlp','\u21AD':'harrw','\u21B0':'lsh','\u21B1':'rsh','\u21B2':'ldsh','\u21B3':'rdsh','\u21B5':'crarr','\u21B6':'cularr','\u21B7':'curarr','\u21BA':'olarr','\u21BB':'orarr','\u21BC':'lharu','\u21BD':'lhard','\u21BE':'uharr','\u21BF':'uharl','\u21C0':'rharu','\u21C1':'rhard','\u21C2':'dharr','\u21C3':'dharl','\u21C4':'rlarr','\u21C5':'udarr','\u21C6':'lrarr','\u21C7':'llarr','\u21C8':'uuarr','\u21C9':'rrarr','\u21CA':'ddarr','\u21CB':'lrhar','\u21CC':'rlhar','\u21D0':'lArr','\u21CD':'nlArr','\u21D1':'uArr','\u21D2':'rArr','\u21CF':'nrArr','\u21D3':'dArr','\u21D4':'iff','\u21CE':'nhArr','\u21D5':'vArr','\u21D6':'nwArr','\u21D7':'neArr','\u21D8':'seArr','\u21D9':'swArr','\u21DA':'lAarr','\u21DB':'rAarr','\u21DD':'zigrarr','\u21E4':'larrb','\u21E5':'rarrb','\u21F5':'duarr','\u21FD':'loarr','\u21FE':'roarr','\u21FF':'hoarr','\u2200':'forall','\u2201':'comp','\u2202':'part','\u2202\u0338':'npart','\u2203':'exist','\u2204':'nexist','\u2205':'empty','\u2207':'Del','\u2208':'in','\u2209':'notin','\u220B':'ni','\u220C':'notni','\u03F6':'bepsi','\u220F':'prod','\u2210':'coprod','\u2211':'sum','+':'plus','\xB1':'pm','\xF7':'div','\xD7':'times','<':'lt','\u226E':'nlt','<\u20D2':'nvlt','=':'equals','\u2260':'ne','=\u20E5':'bne','\u2A75':'Equal','>':'gt','\u226F':'ngt','>\u20D2':'nvgt','\xAC':'not','|':'vert','\xA6':'brvbar','\u2212':'minus','\u2213':'mp','\u2214':'plusdo','\u2044':'frasl','\u2216':'setmn','\u2217':'lowast','\u2218':'compfn','\u221A':'Sqrt','\u221D':'prop','\u221E':'infin','\u221F':'angrt','\u2220':'ang','\u2220\u20D2':'nang','\u2221':'angmsd','\u2222':'angsph','\u2223':'mid','\u2224':'nmid','\u2225':'par','\u2226':'npar','\u2227':'and','\u2228':'or','\u2229':'cap','\u2229\uFE00':'caps','\u222A':'cup','\u222A\uFE00':'cups','\u222B':'int','\u222C':'Int','\u222D':'tint','\u2A0C':'qint','\u222E':'oint','\u222F':'Conint','\u2230':'Cconint','\u2231':'cwint','\u2232':'cwconint','\u2233':'awconint','\u2234':'there4','\u2235':'becaus','\u2236':'ratio','\u2237':'Colon','\u2238':'minusd','\u223A':'mDDot','\u223B':'homtht','\u223C':'sim','\u2241':'nsim','\u223C\u20D2':'nvsim','\u223D':'bsim','\u223D\u0331':'race','\u223E':'ac','\u223E\u0333':'acE','\u223F':'acd','\u2240':'wr','\u2242':'esim','\u2242\u0338':'nesim','\u2243':'sime','\u2244':'nsime','\u2245':'cong','\u2247':'ncong','\u2246':'simne','\u2248':'ap','\u2249':'nap','\u224A':'ape','\u224B':'apid','\u224B\u0338':'napid','\u224C':'bcong','\u224D':'CupCap','\u226D':'NotCupCap','\u224D\u20D2':'nvap','\u224E':'bump','\u224E\u0338':'nbump','\u224F':'bumpe','\u224F\u0338':'nbumpe','\u2250':'doteq','\u2250\u0338':'nedot','\u2251':'eDot','\u2252':'efDot','\u2253':'erDot','\u2254':'colone','\u2255':'ecolon','\u2256':'ecir','\u2257':'cire','\u2259':'wedgeq','\u225A':'veeeq','\u225C':'trie','\u225F':'equest','\u2261':'equiv','\u2262':'nequiv','\u2261\u20E5':'bnequiv','\u2264':'le','\u2270':'nle','\u2264\u20D2':'nvle','\u2265':'ge','\u2271':'nge','\u2265\u20D2':'nvge','\u2266':'lE','\u2266\u0338':'nlE','\u2267':'gE','\u2267\u0338':'ngE','\u2268\uFE00':'lvnE','\u2268':'lnE','\u2269':'gnE','\u2269\uFE00':'gvnE','\u226A':'ll','\u226A\u0338':'nLtv','\u226A\u20D2':'nLt','\u226B':'gg','\u226B\u0338':'nGtv','\u226B\u20D2':'nGt','\u226C':'twixt','\u2272':'lsim','\u2274':'nlsim','\u2273':'gsim','\u2275':'ngsim','\u2276':'lg','\u2278':'ntlg','\u2277':'gl','\u2279':'ntgl','\u227A':'pr','\u2280':'npr','\u227B':'sc','\u2281':'nsc','\u227C':'prcue','\u22E0':'nprcue','\u227D':'sccue','\u22E1':'nsccue','\u227E':'prsim','\u227F':'scsim','\u227F\u0338':'NotSucceedsTilde','\u2282':'sub','\u2284':'nsub','\u2282\u20D2':'vnsub','\u2283':'sup','\u2285':'nsup','\u2283\u20D2':'vnsup','\u2286':'sube','\u2288':'nsube','\u2287':'supe','\u2289':'nsupe','\u228A\uFE00':'vsubne','\u228A':'subne','\u228B\uFE00':'vsupne','\u228B':'supne','\u228D':'cupdot','\u228E':'uplus','\u228F':'sqsub','\u228F\u0338':'NotSquareSubset','\u2290':'sqsup','\u2290\u0338':'NotSquareSuperset','\u2291':'sqsube','\u22E2':'nsqsube','\u2292':'sqsupe','\u22E3':'nsqsupe','\u2293':'sqcap','\u2293\uFE00':'sqcaps','\u2294':'sqcup','\u2294\uFE00':'sqcups','\u2295':'oplus','\u2296':'ominus','\u2297':'otimes','\u2298':'osol','\u2299':'odot','\u229A':'ocir','\u229B':'oast','\u229D':'odash','\u229E':'plusb','\u229F':'minusb','\u22A0':'timesb','\u22A1':'sdotb','\u22A2':'vdash','\u22AC':'nvdash','\u22A3':'dashv','\u22A4':'top','\u22A5':'bot','\u22A7':'models','\u22A8':'vDash','\u22AD':'nvDash','\u22A9':'Vdash','\u22AE':'nVdash','\u22AA':'Vvdash','\u22AB':'VDash','\u22AF':'nVDash','\u22B0':'prurel','\u22B2':'vltri','\u22EA':'nltri','\u22B3':'vrtri','\u22EB':'nrtri','\u22B4':'ltrie','\u22EC':'nltrie','\u22B4\u20D2':'nvltrie','\u22B5':'rtrie','\u22ED':'nrtrie','\u22B5\u20D2':'nvrtrie','\u22B6':'origof','\u22B7':'imof','\u22B8':'mumap','\u22B9':'hercon','\u22BA':'intcal','\u22BB':'veebar','\u22BD':'barvee','\u22BE':'angrtvb','\u22BF':'lrtri','\u22C0':'Wedge','\u22C1':'Vee','\u22C2':'xcap','\u22C3':'xcup','\u22C4':'diam','\u22C5':'sdot','\u22C6':'Star','\u22C7':'divonx','\u22C8':'bowtie','\u22C9':'ltimes','\u22CA':'rtimes','\u22CB':'lthree','\u22CC':'rthree','\u22CD':'bsime','\u22CE':'cuvee','\u22CF':'cuwed','\u22D0':'Sub','\u22D1':'Sup','\u22D2':'Cap','\u22D3':'Cup','\u22D4':'fork','\u22D5':'epar','\u22D6':'ltdot','\u22D7':'gtdot','\u22D8':'Ll','\u22D8\u0338':'nLl','\u22D9':'Gg','\u22D9\u0338':'nGg','\u22DA\uFE00':'lesg','\u22DA':'leg','\u22DB':'gel','\u22DB\uFE00':'gesl','\u22DE':'cuepr','\u22DF':'cuesc','\u22E6':'lnsim','\u22E7':'gnsim','\u22E8':'prnsim','\u22E9':'scnsim','\u22EE':'vellip','\u22EF':'ctdot','\u22F0':'utdot','\u22F1':'dtdot','\u22F2':'disin','\u22F3':'isinsv','\u22F4':'isins','\u22F5':'isindot','\u22F5\u0338':'notindot','\u22F6':'notinvc','\u22F7':'notinvb','\u22F9':'isinE','\u22F9\u0338':'notinE','\u22FA':'nisd','\u22FB':'xnis','\u22FC':'nis','\u22FD':'notnivc','\u22FE':'notnivb','\u2305':'barwed','\u2306':'Barwed','\u230C':'drcrop','\u230D':'dlcrop','\u230E':'urcrop','\u230F':'ulcrop','\u2310':'bnot','\u2312':'profline','\u2313':'profsurf','\u2315':'telrec','\u2316':'target','\u231C':'ulcorn','\u231D':'urcorn','\u231E':'dlcorn','\u231F':'drcorn','\u2322':'frown','\u2323':'smile','\u232D':'cylcty','\u232E':'profalar','\u2336':'topbot','\u233D':'ovbar','\u233F':'solbar','\u237C':'angzarr','\u23B0':'lmoust','\u23B1':'rmoust','\u23B4':'tbrk','\u23B5':'bbrk','\u23B6':'bbrktbrk','\u23DC':'OverParenthesis','\u23DD':'UnderParenthesis','\u23DE':'OverBrace','\u23DF':'UnderBrace','\u23E2':'trpezium','\u23E7':'elinters','\u2423':'blank','\u2500':'boxh','\u2502':'boxv','\u250C':'boxdr','\u2510':'boxdl','\u2514':'boxur','\u2518':'boxul','\u251C':'boxvr','\u2524':'boxvl','\u252C':'boxhd','\u2534':'boxhu','\u253C':'boxvh','\u2550':'boxH','\u2551':'boxV','\u2552':'boxdR','\u2553':'boxDr','\u2554':'boxDR','\u2555':'boxdL','\u2556':'boxDl','\u2557':'boxDL','\u2558':'boxuR','\u2559':'boxUr','\u255A':'boxUR','\u255B':'boxuL','\u255C':'boxUl','\u255D':'boxUL','\u255E':'boxvR','\u255F':'boxVr','\u2560':'boxVR','\u2561':'boxvL','\u2562':'boxVl','\u2563':'boxVL','\u2564':'boxHd','\u2565':'boxhD','\u2566':'boxHD','\u2567':'boxHu','\u2568':'boxhU','\u2569':'boxHU','\u256A':'boxvH','\u256B':'boxVh','\u256C':'boxVH','\u2580':'uhblk','\u2584':'lhblk','\u2588':'block','\u2591':'blk14','\u2592':'blk12','\u2593':'blk34','\u25A1':'squ','\u25AA':'squf','\u25AB':'EmptyVerySmallSquare','\u25AD':'rect','\u25AE':'marker','\u25B1':'fltns','\u25B3':'xutri','\u25B4':'utrif','\u25B5':'utri','\u25B8':'rtrif','\u25B9':'rtri','\u25BD':'xdtri','\u25BE':'dtrif','\u25BF':'dtri','\u25C2':'ltrif','\u25C3':'ltri','\u25CA':'loz','\u25CB':'cir','\u25EC':'tridot','\u25EF':'xcirc','\u25F8':'ultri','\u25F9':'urtri','\u25FA':'lltri','\u25FB':'EmptySmallSquare','\u25FC':'FilledSmallSquare','\u2605':'starf','\u2606':'star','\u260E':'phone','\u2640':'female','\u2642':'male','\u2660':'spades','\u2663':'clubs','\u2665':'hearts','\u2666':'diams','\u266A':'sung','\u2713':'check','\u2717':'cross','\u2720':'malt','\u2736':'sext','\u2758':'VerticalSeparator','\u27C8':'bsolhsub','\u27C9':'suphsol','\u27F5':'xlarr','\u27F6':'xrarr','\u27F7':'xharr','\u27F8':'xlArr','\u27F9':'xrArr','\u27FA':'xhArr','\u27FC':'xmap','\u27FF':'dzigrarr','\u2902':'nvlArr','\u2903':'nvrArr','\u2904':'nvHarr','\u2905':'Map','\u290C':'lbarr','\u290D':'rbarr','\u290E':'lBarr','\u290F':'rBarr','\u2910':'RBarr','\u2911':'DDotrahd','\u2912':'UpArrowBar','\u2913':'DownArrowBar','\u2916':'Rarrtl','\u2919':'latail','\u291A':'ratail','\u291B':'lAtail','\u291C':'rAtail','\u291D':'larrfs','\u291E':'rarrfs','\u291F':'larrbfs','\u2920':'rarrbfs','\u2923':'nwarhk','\u2924':'nearhk','\u2925':'searhk','\u2926':'swarhk','\u2927':'nwnear','\u2928':'toea','\u2929':'tosa','\u292A':'swnwar','\u2933':'rarrc','\u2933\u0338':'nrarrc','\u2935':'cudarrr','\u2936':'ldca','\u2937':'rdca','\u2938':'cudarrl','\u2939':'larrpl','\u293C':'curarrm','\u293D':'cularrp','\u2945':'rarrpl','\u2948':'harrcir','\u2949':'Uarrocir','\u294A':'lurdshar','\u294B':'ldrushar','\u294E':'LeftRightVector','\u294F':'RightUpDownVector','\u2950':'DownLeftRightVector','\u2951':'LeftUpDownVector','\u2952':'LeftVectorBar','\u2953':'RightVectorBar','\u2954':'RightUpVectorBar','\u2955':'RightDownVectorBar','\u2956':'DownLeftVectorBar','\u2957':'DownRightVectorBar','\u2958':'LeftUpVectorBar','\u2959':'LeftDownVectorBar','\u295A':'LeftTeeVector','\u295B':'RightTeeVector','\u295C':'RightUpTeeVector','\u295D':'RightDownTeeVector','\u295E':'DownLeftTeeVector','\u295F':'DownRightTeeVector','\u2960':'LeftUpTeeVector','\u2961':'LeftDownTeeVector','\u2962':'lHar','\u2963':'uHar','\u2964':'rHar','\u2965':'dHar','\u2966':'luruhar','\u2967':'ldrdhar','\u2968':'ruluhar','\u2969':'rdldhar','\u296A':'lharul','\u296B':'llhard','\u296C':'rharul','\u296D':'lrhard','\u296E':'udhar','\u296F':'duhar','\u2970':'RoundImplies','\u2971':'erarr','\u2972':'simrarr','\u2973':'larrsim','\u2974':'rarrsim','\u2975':'rarrap','\u2976':'ltlarr','\u2978':'gtrarr','\u2979':'subrarr','\u297B':'suplarr','\u297C':'lfisht','\u297D':'rfisht','\u297E':'ufisht','\u297F':'dfisht','\u299A':'vzigzag','\u299C':'vangrt','\u299D':'angrtvbd','\u29A4':'ange','\u29A5':'range','\u29A6':'dwangle','\u29A7':'uwangle','\u29A8':'angmsdaa','\u29A9':'angmsdab','\u29AA':'angmsdac','\u29AB':'angmsdad','\u29AC':'angmsdae','\u29AD':'angmsdaf','\u29AE':'angmsdag','\u29AF':'angmsdah','\u29B0':'bemptyv','\u29B1':'demptyv','\u29B2':'cemptyv','\u29B3':'raemptyv','\u29B4':'laemptyv','\u29B5':'ohbar','\u29B6':'omid','\u29B7':'opar','\u29B9':'operp','\u29BB':'olcross','\u29BC':'odsold','\u29BE':'olcir','\u29BF':'ofcir','\u29C0':'olt','\u29C1':'ogt','\u29C2':'cirscir','\u29C3':'cirE','\u29C4':'solb','\u29C5':'bsolb','\u29C9':'boxbox','\u29CD':'trisb','\u29CE':'rtriltri','\u29CF':'LeftTriangleBar','\u29CF\u0338':'NotLeftTriangleBar','\u29D0':'RightTriangleBar','\u29D0\u0338':'NotRightTriangleBar','\u29DC':'iinfin','\u29DD':'infintie','\u29DE':'nvinfin','\u29E3':'eparsl','\u29E4':'smeparsl','\u29E5':'eqvparsl','\u29EB':'lozf','\u29F4':'RuleDelayed','\u29F6':'dsol','\u2A00':'xodot','\u2A01':'xoplus','\u2A02':'xotime','\u2A04':'xuplus','\u2A06':'xsqcup','\u2A0D':'fpartint','\u2A10':'cirfnint','\u2A11':'awint','\u2A12':'rppolint','\u2A13':'scpolint','\u2A14':'npolint','\u2A15':'pointint','\u2A16':'quatint','\u2A17':'intlarhk','\u2A22':'pluscir','\u2A23':'plusacir','\u2A24':'simplus','\u2A25':'plusdu','\u2A26':'plussim','\u2A27':'plustwo','\u2A29':'mcomma','\u2A2A':'minusdu','\u2A2D':'loplus','\u2A2E':'roplus','\u2A2F':'Cross','\u2A30':'timesd','\u2A31':'timesbar','\u2A33':'smashp','\u2A34':'lotimes','\u2A35':'rotimes','\u2A36':'otimesas','\u2A37':'Otimes','\u2A38':'odiv','\u2A39':'triplus','\u2A3A':'triminus','\u2A3B':'tritime','\u2A3C':'iprod','\u2A3F':'amalg','\u2A40':'capdot','\u2A42':'ncup','\u2A43':'ncap','\u2A44':'capand','\u2A45':'cupor','\u2A46':'cupcap','\u2A47':'capcup','\u2A48':'cupbrcap','\u2A49':'capbrcup','\u2A4A':'cupcup','\u2A4B':'capcap','\u2A4C':'ccups','\u2A4D':'ccaps','\u2A50':'ccupssm','\u2A53':'And','\u2A54':'Or','\u2A55':'andand','\u2A56':'oror','\u2A57':'orslope','\u2A58':'andslope','\u2A5A':'andv','\u2A5B':'orv','\u2A5C':'andd','\u2A5D':'ord','\u2A5F':'wedbar','\u2A66':'sdote','\u2A6A':'simdot','\u2A6D':'congdot','\u2A6D\u0338':'ncongdot','\u2A6E':'easter','\u2A6F':'apacir','\u2A70':'apE','\u2A70\u0338':'napE','\u2A71':'eplus','\u2A72':'pluse','\u2A73':'Esim','\u2A77':'eDDot','\u2A78':'equivDD','\u2A79':'ltcir','\u2A7A':'gtcir','\u2A7B':'ltquest','\u2A7C':'gtquest','\u2A7D':'les','\u2A7D\u0338':'nles','\u2A7E':'ges','\u2A7E\u0338':'nges','\u2A7F':'lesdot','\u2A80':'gesdot','\u2A81':'lesdoto','\u2A82':'gesdoto','\u2A83':'lesdotor','\u2A84':'gesdotol','\u2A85':'lap','\u2A86':'gap','\u2A87':'lne','\u2A88':'gne','\u2A89':'lnap','\u2A8A':'gnap','\u2A8B':'lEg','\u2A8C':'gEl','\u2A8D':'lsime','\u2A8E':'gsime','\u2A8F':'lsimg','\u2A90':'gsiml','\u2A91':'lgE','\u2A92':'glE','\u2A93':'lesges','\u2A94':'gesles','\u2A95':'els','\u2A96':'egs','\u2A97':'elsdot','\u2A98':'egsdot','\u2A99':'el','\u2A9A':'eg','\u2A9D':'siml','\u2A9E':'simg','\u2A9F':'simlE','\u2AA0':'simgE','\u2AA1':'LessLess','\u2AA1\u0338':'NotNestedLessLess','\u2AA2':'GreaterGreater','\u2AA2\u0338':'NotNestedGreaterGreater','\u2AA4':'glj','\u2AA5':'gla','\u2AA6':'ltcc','\u2AA7':'gtcc','\u2AA8':'lescc','\u2AA9':'gescc','\u2AAA':'smt','\u2AAB':'lat','\u2AAC':'smte','\u2AAC\uFE00':'smtes','\u2AAD':'late','\u2AAD\uFE00':'lates','\u2AAE':'bumpE','\u2AAF':'pre','\u2AAF\u0338':'npre','\u2AB0':'sce','\u2AB0\u0338':'nsce','\u2AB3':'prE','\u2AB4':'scE','\u2AB5':'prnE','\u2AB6':'scnE','\u2AB7':'prap','\u2AB8':'scap','\u2AB9':'prnap','\u2ABA':'scnap','\u2ABB':'Pr','\u2ABC':'Sc','\u2ABD':'subdot','\u2ABE':'supdot','\u2ABF':'subplus','\u2AC0':'supplus','\u2AC1':'submult','\u2AC2':'supmult','\u2AC3':'subedot','\u2AC4':'supedot','\u2AC5':'subE','\u2AC5\u0338':'nsubE','\u2AC6':'supE','\u2AC6\u0338':'nsupE','\u2AC7':'subsim','\u2AC8':'supsim','\u2ACB\uFE00':'vsubnE','\u2ACB':'subnE','\u2ACC\uFE00':'vsupnE','\u2ACC':'supnE','\u2ACF':'csub','\u2AD0':'csup','\u2AD1':'csube','\u2AD2':'csupe','\u2AD3':'subsup','\u2AD4':'supsub','\u2AD5':'subsub','\u2AD6':'supsup','\u2AD7':'suphsub','\u2AD8':'supdsub','\u2AD9':'forkv','\u2ADA':'topfork','\u2ADB':'mlcp','\u2AE4':'Dashv','\u2AE6':'Vdashl','\u2AE7':'Barv','\u2AE8':'vBar','\u2AE9':'vBarv','\u2AEB':'Vbar','\u2AEC':'Not','\u2AED':'bNot','\u2AEE':'rnmid','\u2AEF':'cirmid','\u2AF0':'midcir','\u2AF1':'topcir','\u2AF2':'nhpar','\u2AF3':'parsim','\u2AFD':'parsl','\u2AFD\u20E5':'nparsl','\u266D':'flat','\u266E':'natur','\u266F':'sharp','\xA4':'curren','\xA2':'cent','$':'dollar','\xA3':'pound','\xA5':'yen','\u20AC':'euro','\xB9':'sup1','\xBD':'half','\u2153':'frac13','\xBC':'frac14','\u2155':'frac15','\u2159':'frac16','\u215B':'frac18','\xB2':'sup2','\u2154':'frac23','\u2156':'frac25','\xB3':'sup3','\xBE':'frac34','\u2157':'frac35','\u215C':'frac38','\u2158':'frac45','\u215A':'frac56','\u215D':'frac58','\u215E':'frac78','\uD835\uDCB6':'ascr','\uD835\uDD52':'aopf','\uD835\uDD1E':'afr','\uD835\uDD38':'Aopf','\uD835\uDD04':'Afr','\uD835\uDC9C':'Ascr','\xAA':'ordf','\xE1':'aacute','\xC1':'Aacute','\xE0':'agrave','\xC0':'Agrave','\u0103':'abreve','\u0102':'Abreve','\xE2':'acirc','\xC2':'Acirc','\xE5':'aring','\xC5':'angst','\xE4':'auml','\xC4':'Auml','\xE3':'atilde','\xC3':'Atilde','\u0105':'aogon','\u0104':'Aogon','\u0101':'amacr','\u0100':'Amacr','\xE6':'aelig','\xC6':'AElig','\uD835\uDCB7':'bscr','\uD835\uDD53':'bopf','\uD835\uDD1F':'bfr','\uD835\uDD39':'Bopf','\u212C':'Bscr','\uD835\uDD05':'Bfr','\uD835\uDD20':'cfr','\uD835\uDCB8':'cscr','\uD835\uDD54':'copf','\u212D':'Cfr','\uD835\uDC9E':'Cscr','\u2102':'Copf','\u0107':'cacute','\u0106':'Cacute','\u0109':'ccirc','\u0108':'Ccirc','\u010D':'ccaron','\u010C':'Ccaron','\u010B':'cdot','\u010A':'Cdot','\xE7':'ccedil','\xC7':'Ccedil','\u2105':'incare','\uD835\uDD21':'dfr','\u2146':'dd','\uD835\uDD55':'dopf','\uD835\uDCB9':'dscr','\uD835\uDC9F':'Dscr','\uD835\uDD07':'Dfr','\u2145':'DD','\uD835\uDD3B':'Dopf','\u010F':'dcaron','\u010E':'Dcaron','\u0111':'dstrok','\u0110':'Dstrok','\xF0':'eth','\xD0':'ETH','\u2147':'ee','\u212F':'escr','\uD835\uDD22':'efr','\uD835\uDD56':'eopf','\u2130':'Escr','\uD835\uDD08':'Efr','\uD835\uDD3C':'Eopf','\xE9':'eacute','\xC9':'Eacute','\xE8':'egrave','\xC8':'Egrave','\xEA':'ecirc','\xCA':'Ecirc','\u011B':'ecaron','\u011A':'Ecaron','\xEB':'euml','\xCB':'Euml','\u0117':'edot','\u0116':'Edot','\u0119':'eogon','\u0118':'Eogon','\u0113':'emacr','\u0112':'Emacr','\uD835\uDD23':'ffr','\uD835\uDD57':'fopf','\uD835\uDCBB':'fscr','\uD835\uDD09':'Ffr','\uD835\uDD3D':'Fopf','\u2131':'Fscr','\uFB00':'fflig','\uFB03':'ffilig','\uFB04':'ffllig','\uFB01':'filig','fj':'fjlig','\uFB02':'fllig','\u0192':'fnof','\u210A':'gscr','\uD835\uDD58':'gopf','\uD835\uDD24':'gfr','\uD835\uDCA2':'Gscr','\uD835\uDD3E':'Gopf','\uD835\uDD0A':'Gfr','\u01F5':'gacute','\u011F':'gbreve','\u011E':'Gbreve','\u011D':'gcirc','\u011C':'Gcirc','\u0121':'gdot','\u0120':'Gdot','\u0122':'Gcedil','\uD835\uDD25':'hfr','\u210E':'planckh','\uD835\uDCBD':'hscr','\uD835\uDD59':'hopf','\u210B':'Hscr','\u210C':'Hfr','\u210D':'Hopf','\u0125':'hcirc','\u0124':'Hcirc','\u210F':'hbar','\u0127':'hstrok','\u0126':'Hstrok','\uD835\uDD5A':'iopf','\uD835\uDD26':'ifr','\uD835\uDCBE':'iscr','\u2148':'ii','\uD835\uDD40':'Iopf','\u2110':'Iscr','\u2111':'Im','\xED':'iacute','\xCD':'Iacute','\xEC':'igrave','\xCC':'Igrave','\xEE':'icirc','\xCE':'Icirc','\xEF':'iuml','\xCF':'Iuml','\u0129':'itilde','\u0128':'Itilde','\u0130':'Idot','\u012F':'iogon','\u012E':'Iogon','\u012B':'imacr','\u012A':'Imacr','\u0133':'ijlig','\u0132':'IJlig','\u0131':'imath','\uD835\uDCBF':'jscr','\uD835\uDD5B':'jopf','\uD835\uDD27':'jfr','\uD835\uDCA5':'Jscr','\uD835\uDD0D':'Jfr','\uD835\uDD41':'Jopf','\u0135':'jcirc','\u0134':'Jcirc','\u0237':'jmath','\uD835\uDD5C':'kopf','\uD835\uDCC0':'kscr','\uD835\uDD28':'kfr','\uD835\uDCA6':'Kscr','\uD835\uDD42':'Kopf','\uD835\uDD0E':'Kfr','\u0137':'kcedil','\u0136':'Kcedil','\uD835\uDD29':'lfr','\uD835\uDCC1':'lscr','\u2113':'ell','\uD835\uDD5D':'lopf','\u2112':'Lscr','\uD835\uDD0F':'Lfr','\uD835\uDD43':'Lopf','\u013A':'lacute','\u0139':'Lacute','\u013E':'lcaron','\u013D':'Lcaron','\u013C':'lcedil','\u013B':'Lcedil','\u0142':'lstrok','\u0141':'Lstrok','\u0140':'lmidot','\u013F':'Lmidot','\uD835\uDD2A':'mfr','\uD835\uDD5E':'mopf','\uD835\uDCC2':'mscr','\uD835\uDD10':'Mfr','\uD835\uDD44':'Mopf','\u2133':'Mscr','\uD835\uDD2B':'nfr','\uD835\uDD5F':'nopf','\uD835\uDCC3':'nscr','\u2115':'Nopf','\uD835\uDCA9':'Nscr','\uD835\uDD11':'Nfr','\u0144':'nacute','\u0143':'Nacute','\u0148':'ncaron','\u0147':'Ncaron','\xF1':'ntilde','\xD1':'Ntilde','\u0146':'ncedil','\u0145':'Ncedil','\u2116':'numero','\u014B':'eng','\u014A':'ENG','\uD835\uDD60':'oopf','\uD835\uDD2C':'ofr','\u2134':'oscr','\uD835\uDCAA':'Oscr','\uD835\uDD12':'Ofr','\uD835\uDD46':'Oopf','\xBA':'ordm','\xF3':'oacute','\xD3':'Oacute','\xF2':'ograve','\xD2':'Ograve','\xF4':'ocirc','\xD4':'Ocirc','\xF6':'ouml','\xD6':'Ouml','\u0151':'odblac','\u0150':'Odblac','\xF5':'otilde','\xD5':'Otilde','\xF8':'oslash','\xD8':'Oslash','\u014D':'omacr','\u014C':'Omacr','\u0153':'oelig','\u0152':'OElig','\uD835\uDD2D':'pfr','\uD835\uDCC5':'pscr','\uD835\uDD61':'popf','\u2119':'Popf','\uD835\uDD13':'Pfr','\uD835\uDCAB':'Pscr','\uD835\uDD62':'qopf','\uD835\uDD2E':'qfr','\uD835\uDCC6':'qscr','\uD835\uDCAC':'Qscr','\uD835\uDD14':'Qfr','\u211A':'Qopf','\u0138':'kgreen','\uD835\uDD2F':'rfr','\uD835\uDD63':'ropf','\uD835\uDCC7':'rscr','\u211B':'Rscr','\u211C':'Re','\u211D':'Ropf','\u0155':'racute','\u0154':'Racute','\u0159':'rcaron','\u0158':'Rcaron','\u0157':'rcedil','\u0156':'Rcedil','\uD835\uDD64':'sopf','\uD835\uDCC8':'sscr','\uD835\uDD30':'sfr','\uD835\uDD4A':'Sopf','\uD835\uDD16':'Sfr','\uD835\uDCAE':'Sscr','\u24C8':'oS','\u015B':'sacute','\u015A':'Sacute','\u015D':'scirc','\u015C':'Scirc','\u0161':'scaron','\u0160':'Scaron','\u015F':'scedil','\u015E':'Scedil','\xDF':'szlig','\uD835\uDD31':'tfr','\uD835\uDCC9':'tscr','\uD835\uDD65':'topf','\uD835\uDCAF':'Tscr','\uD835\uDD17':'Tfr','\uD835\uDD4B':'Topf','\u0165':'tcaron','\u0164':'Tcaron','\u0163':'tcedil','\u0162':'Tcedil','\u2122':'trade','\u0167':'tstrok','\u0166':'Tstrok','\uD835\uDCCA':'uscr','\uD835\uDD66':'uopf','\uD835\uDD32':'ufr','\uD835\uDD4C':'Uopf','\uD835\uDD18':'Ufr','\uD835\uDCB0':'Uscr','\xFA':'uacute','\xDA':'Uacute','\xF9':'ugrave','\xD9':'Ugrave','\u016D':'ubreve','\u016C':'Ubreve','\xFB':'ucirc','\xDB':'Ucirc','\u016F':'uring','\u016E':'Uring','\xFC':'uuml','\xDC':'Uuml','\u0171':'udblac','\u0170':'Udblac','\u0169':'utilde','\u0168':'Utilde','\u0173':'uogon','\u0172':'Uogon','\u016B':'umacr','\u016A':'Umacr','\uD835\uDD33':'vfr','\uD835\uDD67':'vopf','\uD835\uDCCB':'vscr','\uD835\uDD19':'Vfr','\uD835\uDD4D':'Vopf','\uD835\uDCB1':'Vscr','\uD835\uDD68':'wopf','\uD835\uDCCC':'wscr','\uD835\uDD34':'wfr','\uD835\uDCB2':'Wscr','\uD835\uDD4E':'Wopf','\uD835\uDD1A':'Wfr','\u0175':'wcirc','\u0174':'Wcirc','\uD835\uDD35':'xfr','\uD835\uDCCD':'xscr','\uD835\uDD69':'xopf','\uD835\uDD4F':'Xopf','\uD835\uDD1B':'Xfr','\uD835\uDCB3':'Xscr','\uD835\uDD36':'yfr','\uD835\uDCCE':'yscr','\uD835\uDD6A':'yopf','\uD835\uDCB4':'Yscr','\uD835\uDD1C':'Yfr','\uD835\uDD50':'Yopf','\xFD':'yacute','\xDD':'Yacute','\u0177':'ycirc','\u0176':'Ycirc','\xFF':'yuml','\u0178':'Yuml','\uD835\uDCCF':'zscr','\uD835\uDD37':'zfr','\uD835\uDD6B':'zopf','\u2128':'Zfr','\u2124':'Zopf','\uD835\uDCB5':'Zscr','\u017A':'zacute','\u0179':'Zacute','\u017E':'zcaron','\u017D':'Zcaron','\u017C':'zdot','\u017B':'Zdot','\u01B5':'imped','\xFE':'thorn','\xDE':'THORN','\u0149':'napos','\u03B1':'alpha','\u0391':'Alpha','\u03B2':'beta','\u0392':'Beta','\u03B3':'gamma','\u0393':'Gamma','\u03B4':'delta','\u0394':'Delta','\u03B5':'epsi','\u03F5':'epsiv','\u0395':'Epsilon','\u03DD':'gammad','\u03DC':'Gammad','\u03B6':'zeta','\u0396':'Zeta','\u03B7':'eta','\u0397':'Eta','\u03B8':'theta','\u03D1':'thetav','\u0398':'Theta','\u03B9':'iota','\u0399':'Iota','\u03BA':'kappa','\u03F0':'kappav','\u039A':'Kappa','\u03BB':'lambda','\u039B':'Lambda','\u03BC':'mu','\xB5':'micro','\u039C':'Mu','\u03BD':'nu','\u039D':'Nu','\u03BE':'xi','\u039E':'Xi','\u03BF':'omicron','\u039F':'Omicron','\u03C0':'pi','\u03D6':'piv','\u03A0':'Pi','\u03C1':'rho','\u03F1':'rhov','\u03A1':'Rho','\u03C3':'sigma','\u03A3':'Sigma','\u03C2':'sigmaf','\u03C4':'tau','\u03A4':'Tau','\u03C5':'upsi','\u03A5':'Upsilon','\u03D2':'Upsi','\u03C6':'phi','\u03D5':'phiv','\u03A6':'Phi','\u03C7':'chi','\u03A7':'Chi','\u03C8':'psi','\u03A8':'Psi','\u03C9':'omega','\u03A9':'ohm','\u0430':'acy','\u0410':'Acy','\u0431':'bcy','\u0411':'Bcy','\u0432':'vcy','\u0412':'Vcy','\u0433':'gcy','\u0413':'Gcy','\u0453':'gjcy','\u0403':'GJcy','\u0434':'dcy','\u0414':'Dcy','\u0452':'djcy','\u0402':'DJcy','\u0435':'iecy','\u0415':'IEcy','\u0451':'iocy','\u0401':'IOcy','\u0454':'jukcy','\u0404':'Jukcy','\u0436':'zhcy','\u0416':'ZHcy','\u0437':'zcy','\u0417':'Zcy','\u0455':'dscy','\u0405':'DScy','\u0438':'icy','\u0418':'Icy','\u0456':'iukcy','\u0406':'Iukcy','\u0457':'yicy','\u0407':'YIcy','\u0439':'jcy','\u0419':'Jcy','\u0458':'jsercy','\u0408':'Jsercy','\u043A':'kcy','\u041A':'Kcy','\u045C':'kjcy','\u040C':'KJcy','\u043B':'lcy','\u041B':'Lcy','\u0459':'ljcy','\u0409':'LJcy','\u043C':'mcy','\u041C':'Mcy','\u043D':'ncy','\u041D':'Ncy','\u045A':'njcy','\u040A':'NJcy','\u043E':'ocy','\u041E':'Ocy','\u043F':'pcy','\u041F':'Pcy','\u0440':'rcy','\u0420':'Rcy','\u0441':'scy','\u0421':'Scy','\u0442':'tcy','\u0422':'Tcy','\u045B':'tshcy','\u040B':'TSHcy','\u0443':'ucy','\u0423':'Ucy','\u045E':'ubrcy','\u040E':'Ubrcy','\u0444':'fcy','\u0424':'Fcy','\u0445':'khcy','\u0425':'KHcy','\u0446':'tscy','\u0426':'TScy','\u0447':'chcy','\u0427':'CHcy','\u045F':'dzcy','\u040F':'DZcy','\u0448':'shcy','\u0428':'SHcy','\u0449':'shchcy','\u0429':'SHCHcy','\u044A':'hardcy','\u042A':'HARDcy','\u044B':'ycy','\u042B':'Ycy','\u044C':'softcy','\u042C':'SOFTcy','\u044D':'ecy','\u042D':'Ecy','\u044E':'yucy','\u042E':'YUcy','\u044F':'yacy','\u042F':'YAcy','\u2135':'aleph','\u2136':'beth','\u2137':'gimel','\u2138':'daleth'}; + + var regexEscape = /["&'<>`]/g; + var escapeMap = { + '"': '"', + '&': '&', + '\'': ''', + '<': '<', + // See https://mathiasbynens.be/notes/ambiguous-ampersands: in HTML, the + // following is not strictly necessary unless it’s part of a tag or an + // unquoted attribute value. We’re only escaping it to support those + // situations, and for XML support. + '>': '>', + // In Internet Explorer ≤ 8, the backtick character can be used + // to break out of (un)quoted attribute values or HTML comments. + // See http://html5sec.org/#102, http://html5sec.org/#108, and + // http://html5sec.org/#133. + '`': '`' + }; + + var regexInvalidEntity = /&#(?:[xX][^a-fA-F0-9]|[^0-9xX])/; + var regexInvalidRawCodePoint = /[\0-\x08\x0B\x0E-\x1F\x7F-\x9F\uFDD0-\uFDEF\uFFFE\uFFFF]|[\uD83F\uD87F\uD8BF\uD8FF\uD93F\uD97F\uD9BF\uD9FF\uDA3F\uDA7F\uDABF\uDAFF\uDB3F\uDB7F\uDBBF\uDBFF][\uDFFE\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; + var regexDecode = /&(CounterClockwiseContourIntegral|DoubleLongLeftRightArrow|ClockwiseContourIntegral|NotNestedGreaterGreater|NotSquareSupersetEqual|DiacriticalDoubleAcute|NotRightTriangleEqual|NotSucceedsSlantEqual|NotPrecedesSlantEqual|CloseCurlyDoubleQuote|NegativeVeryThinSpace|DoubleContourIntegral|FilledVerySmallSquare|CapitalDifferentialD|OpenCurlyDoubleQuote|EmptyVerySmallSquare|NestedGreaterGreater|DoubleLongRightArrow|NotLeftTriangleEqual|NotGreaterSlantEqual|ReverseUpEquilibrium|DoubleLeftRightArrow|NotSquareSubsetEqual|NotDoubleVerticalBar|RightArrowLeftArrow|NotGreaterFullEqual|NotRightTriangleBar|SquareSupersetEqual|DownLeftRightVector|DoubleLongLeftArrow|leftrightsquigarrow|LeftArrowRightArrow|NegativeMediumSpace|blacktriangleright|RightDownVectorBar|PrecedesSlantEqual|RightDoubleBracket|SucceedsSlantEqual|NotLeftTriangleBar|RightTriangleEqual|SquareIntersection|RightDownTeeVector|ReverseEquilibrium|NegativeThickSpace|longleftrightarrow|Longleftrightarrow|LongLeftRightArrow|DownRightTeeVector|DownRightVectorBar|GreaterSlantEqual|SquareSubsetEqual|LeftDownVectorBar|LeftDoubleBracket|VerticalSeparator|rightleftharpoons|NotGreaterGreater|NotSquareSuperset|blacktriangleleft|blacktriangledown|NegativeThinSpace|LeftDownTeeVector|NotLessSlantEqual|leftrightharpoons|DoubleUpDownArrow|DoubleVerticalBar|LeftTriangleEqual|FilledSmallSquare|twoheadrightarrow|NotNestedLessLess|DownLeftTeeVector|DownLeftVectorBar|RightAngleBracket|NotTildeFullEqual|NotReverseElement|RightUpDownVector|DiacriticalTilde|NotSucceedsTilde|circlearrowright|NotPrecedesEqual|rightharpoondown|DoubleRightArrow|NotSucceedsEqual|NonBreakingSpace|NotRightTriangle|LessEqualGreater|RightUpTeeVector|LeftAngleBracket|GreaterFullEqual|DownArrowUpArrow|RightUpVectorBar|twoheadleftarrow|GreaterEqualLess|downharpoonright|RightTriangleBar|ntrianglerighteq|NotSupersetEqual|LeftUpDownVector|DiacriticalAcute|rightrightarrows|vartriangleright|UpArrowDownArrow|DiacriticalGrave|UnderParenthesis|EmptySmallSquare|LeftUpVectorBar|leftrightarrows|DownRightVector|downharpoonleft|trianglerighteq|ShortRightArrow|OverParenthesis|DoubleLeftArrow|DoubleDownArrow|NotSquareSubset|bigtriangledown|ntrianglelefteq|UpperRightArrow|curvearrowright|vartriangleleft|NotLeftTriangle|nleftrightarrow|LowerRightArrow|NotHumpDownHump|NotGreaterTilde|rightthreetimes|LeftUpTeeVector|NotGreaterEqual|straightepsilon|LeftTriangleBar|rightsquigarrow|ContourIntegral|rightleftarrows|CloseCurlyQuote|RightDownVector|LeftRightVector|nLeftrightarrow|leftharpoondown|circlearrowleft|SquareSuperset|OpenCurlyQuote|hookrightarrow|HorizontalLine|DiacriticalDot|NotLessGreater|ntriangleright|DoubleRightTee|InvisibleComma|InvisibleTimes|LowerLeftArrow|DownLeftVector|NotSubsetEqual|curvearrowleft|trianglelefteq|NotVerticalBar|TildeFullEqual|downdownarrows|NotGreaterLess|RightTeeVector|ZeroWidthSpace|looparrowright|LongRightArrow|doublebarwedge|ShortLeftArrow|ShortDownArrow|RightVectorBar|GreaterGreater|ReverseElement|rightharpoonup|LessSlantEqual|leftthreetimes|upharpoonright|rightarrowtail|LeftDownVector|Longrightarrow|NestedLessLess|UpperLeftArrow|nshortparallel|leftleftarrows|leftrightarrow|Leftrightarrow|LeftRightArrow|longrightarrow|upharpoonleft|RightArrowBar|ApplyFunction|LeftTeeVector|leftarrowtail|NotEqualTilde|varsubsetneqq|varsupsetneqq|RightTeeArrow|SucceedsEqual|SucceedsTilde|LeftVectorBar|SupersetEqual|hookleftarrow|DifferentialD|VerticalTilde|VeryThinSpace|blacktriangle|bigtriangleup|LessFullEqual|divideontimes|leftharpoonup|UpEquilibrium|ntriangleleft|RightTriangle|measuredangle|shortparallel|longleftarrow|Longleftarrow|LongLeftArrow|DoubleLeftTee|Poincareplane|PrecedesEqual|triangleright|DoubleUpArrow|RightUpVector|fallingdotseq|looparrowleft|PrecedesTilde|NotTildeEqual|NotTildeTilde|smallsetminus|Proportional|triangleleft|triangledown|UnderBracket|NotHumpEqual|exponentiale|ExponentialE|NotLessTilde|HilbertSpace|RightCeiling|blacklozenge|varsupsetneq|HumpDownHump|GreaterEqual|VerticalLine|LeftTeeArrow|NotLessEqual|DownTeeArrow|LeftTriangle|varsubsetneq|Intersection|NotCongruent|DownArrowBar|LeftUpVector|LeftArrowBar|risingdotseq|GreaterTilde|RoundImplies|SquareSubset|ShortUpArrow|NotSuperset|quaternions|precnapprox|backepsilon|preccurlyeq|OverBracket|blacksquare|MediumSpace|VerticalBar|circledcirc|circleddash|CircleMinus|CircleTimes|LessGreater|curlyeqprec|curlyeqsucc|diamondsuit|UpDownArrow|Updownarrow|RuleDelayed|Rrightarrow|updownarrow|RightVector|nRightarrow|nrightarrow|eqslantless|LeftCeiling|Equilibrium|SmallCircle|expectation|NotSucceeds|thickapprox|GreaterLess|SquareUnion|NotPrecedes|NotLessLess|straightphi|succnapprox|succcurlyeq|SubsetEqual|sqsupseteq|Proportion|Laplacetrf|ImaginaryI|supsetneqq|NotGreater|gtreqqless|NotElement|ThickSpace|TildeEqual|TildeTilde|Fouriertrf|rmoustache|EqualTilde|eqslantgtr|UnderBrace|LeftVector|UpArrowBar|nLeftarrow|nsubseteqq|subsetneqq|nsupseteqq|nleftarrow|succapprox|lessapprox|UpTeeArrow|upuparrows|curlywedge|lesseqqgtr|varepsilon|varnothing|RightFloor|complement|CirclePlus|sqsubseteq|Lleftarrow|circledast|RightArrow|Rightarrow|rightarrow|lmoustache|Bernoullis|precapprox|mapstoleft|mapstodown|longmapsto|dotsquare|downarrow|DoubleDot|nsubseteq|supsetneq|leftarrow|nsupseteq|subsetneq|ThinSpace|ngeqslant|subseteqq|HumpEqual|NotSubset|triangleq|NotCupCap|lesseqgtr|heartsuit|TripleDot|Leftarrow|Coproduct|Congruent|varpropto|complexes|gvertneqq|LeftArrow|LessTilde|supseteqq|MinusPlus|CircleDot|nleqslant|NotExists|gtreqless|nparallel|UnionPlus|LeftFloor|checkmark|CenterDot|centerdot|Mellintrf|gtrapprox|bigotimes|OverBrace|spadesuit|therefore|pitchfork|rationals|PlusMinus|Backslash|Therefore|DownBreve|backsimeq|backprime|DownArrow|nshortmid|Downarrow|lvertneqq|eqvparsl|imagline|imagpart|infintie|integers|Integral|intercal|LessLess|Uarrocir|intlarhk|sqsupset|angmsdaf|sqsubset|llcorner|vartheta|cupbrcap|lnapprox|Superset|SuchThat|succnsim|succneqq|angmsdag|biguplus|curlyvee|trpezium|Succeeds|NotTilde|bigwedge|angmsdah|angrtvbd|triminus|cwconint|fpartint|lrcorner|smeparsl|subseteq|urcorner|lurdshar|laemptyv|DDotrahd|approxeq|ldrushar|awconint|mapstoup|backcong|shortmid|triangle|geqslant|gesdotol|timesbar|circledR|circledS|setminus|multimap|naturals|scpolint|ncongdot|RightTee|boxminus|gnapprox|boxtimes|andslope|thicksim|angmsdaa|varsigma|cirfnint|rtriltri|angmsdab|rppolint|angmsdac|barwedge|drbkarow|clubsuit|thetasym|bsolhsub|capbrcup|dzigrarr|doteqdot|DotEqual|dotminus|UnderBar|NotEqual|realpart|otimesas|ulcorner|hksearow|hkswarow|parallel|PartialD|elinters|emptyset|plusacir|bbrktbrk|angmsdad|pointint|bigoplus|angmsdae|Precedes|bigsqcup|varkappa|notindot|supseteq|precneqq|precnsim|profalar|profline|profsurf|leqslant|lesdotor|raemptyv|subplus|notnivb|notnivc|subrarr|zigrarr|vzigzag|submult|subedot|Element|between|cirscir|larrbfs|larrsim|lotimes|lbrksld|lbrkslu|lozenge|ldrdhar|dbkarow|bigcirc|epsilon|simrarr|simplus|ltquest|Epsilon|luruhar|gtquest|maltese|npolint|eqcolon|npreceq|bigodot|ddagger|gtrless|bnequiv|harrcir|ddotseq|equivDD|backsim|demptyv|nsqsube|nsqsupe|Upsilon|nsubset|upsilon|minusdu|nsucceq|swarrow|nsupset|coloneq|searrow|boxplus|napprox|natural|asympeq|alefsym|congdot|nearrow|bigstar|diamond|supplus|tritime|LeftTee|nvinfin|triplus|NewLine|nvltrie|nvrtrie|nwarrow|nexists|Diamond|ruluhar|Implies|supmult|angzarr|suplarr|suphsub|questeq|because|digamma|Because|olcross|bemptyv|omicron|Omicron|rotimes|NoBreak|intprod|angrtvb|orderof|uwangle|suphsol|lesdoto|orslope|DownTee|realine|cudarrl|rdldhar|OverBar|supedot|lessdot|supdsub|topfork|succsim|rbrkslu|rbrksld|pertenk|cudarrr|isindot|planckh|lessgtr|pluscir|gesdoto|plussim|plustwo|lesssim|cularrp|rarrsim|Cayleys|notinva|notinvb|notinvc|UpArrow|Uparrow|uparrow|NotLess|dwangle|precsim|Product|curarrm|Cconint|dotplus|rarrbfs|ccupssm|Cedilla|cemptyv|notniva|quatint|frac35|frac38|frac45|frac56|frac58|frac78|tridot|xoplus|gacute|gammad|Gammad|lfisht|lfloor|bigcup|sqsupe|gbreve|Gbreve|lharul|sqsube|sqcups|Gcedil|apacir|llhard|lmidot|Lmidot|lmoust|andand|sqcaps|approx|Abreve|spades|circeq|tprime|divide|topcir|Assign|topbot|gesdot|divonx|xuplus|timesd|gesles|atilde|solbar|SOFTcy|loplus|timesb|lowast|lowbar|dlcorn|dlcrop|softcy|dollar|lparlt|thksim|lrhard|Atilde|lsaquo|smashp|bigvee|thinsp|wreath|bkarow|lsquor|lstrok|Lstrok|lthree|ltimes|ltlarr|DotDot|simdot|ltrPar|weierp|xsqcup|angmsd|sigmav|sigmaf|zeetrf|Zcaron|zcaron|mapsto|vsupne|thetav|cirmid|marker|mcomma|Zacute|vsubnE|there4|gtlPar|vsubne|bottom|gtrarr|SHCHcy|shchcy|midast|midcir|middot|minusb|minusd|gtrdot|bowtie|sfrown|mnplus|models|colone|seswar|Colone|mstpos|searhk|gtrsim|nacute|Nacute|boxbox|telrec|hairsp|Tcedil|nbumpe|scnsim|ncaron|Ncaron|ncedil|Ncedil|hamilt|Scedil|nearhk|hardcy|HARDcy|tcedil|Tcaron|commat|nequiv|nesear|tcaron|target|hearts|nexist|varrho|scedil|Scaron|scaron|hellip|Sacute|sacute|hercon|swnwar|compfn|rtimes|rthree|rsquor|rsaquo|zacute|wedgeq|homtht|barvee|barwed|Barwed|rpargt|horbar|conint|swarhk|roplus|nltrie|hslash|hstrok|Hstrok|rmoust|Conint|bprime|hybull|hyphen|iacute|Iacute|supsup|supsub|supsim|varphi|coprod|brvbar|agrave|Supset|supset|igrave|Igrave|notinE|Agrave|iiiint|iinfin|copysr|wedbar|Verbar|vangrt|becaus|incare|verbar|inodot|bullet|drcorn|intcal|drcrop|cularr|vellip|Utilde|bumpeq|cupcap|dstrok|Dstrok|CupCap|cupcup|cupdot|eacute|Eacute|supdot|iquest|easter|ecaron|Ecaron|ecolon|isinsv|utilde|itilde|Itilde|curarr|succeq|Bumpeq|cacute|ulcrop|nparsl|Cacute|nprcue|egrave|Egrave|nrarrc|nrarrw|subsup|subsub|nrtrie|jsercy|nsccue|Jsercy|kappav|kcedil|Kcedil|subsim|ulcorn|nsimeq|egsdot|veebar|kgreen|capand|elsdot|Subset|subset|curren|aacute|lacute|Lacute|emptyv|ntilde|Ntilde|lagran|lambda|Lambda|capcap|Ugrave|langle|subdot|emsp13|numero|emsp14|nvdash|nvDash|nVdash|nVDash|ugrave|ufisht|nvHarr|larrfs|nvlArr|larrhk|larrlp|larrpl|nvrArr|Udblac|nwarhk|larrtl|nwnear|oacute|Oacute|latail|lAtail|sstarf|lbrace|odblac|Odblac|lbrack|udblac|odsold|eparsl|lcaron|Lcaron|ograve|Ograve|lcedil|Lcedil|Aacute|ssmile|ssetmn|squarf|ldquor|capcup|ominus|cylcty|rharul|eqcirc|dagger|rfloor|rfisht|Dagger|daleth|equals|origof|capdot|equest|dcaron|Dcaron|rdquor|oslash|Oslash|otilde|Otilde|otimes|Otimes|urcrop|Ubreve|ubreve|Yacute|Uacute|uacute|Rcedil|rcedil|urcorn|parsim|Rcaron|Vdashl|rcaron|Tstrok|percnt|period|permil|Exists|yacute|rbrack|rbrace|phmmat|ccaron|Ccaron|planck|ccedil|plankv|tstrok|female|plusdo|plusdu|ffilig|plusmn|ffllig|Ccedil|rAtail|dfisht|bernou|ratail|Rarrtl|rarrtl|angsph|rarrpl|rarrlp|rarrhk|xwedge|xotime|forall|ForAll|Vvdash|vsupnE|preceq|bigcap|frac12|frac13|frac14|primes|rarrfs|prnsim|frac15|Square|frac16|square|lesdot|frac18|frac23|propto|prurel|rarrap|rangle|puncsp|frac25|Racute|qprime|racute|lesges|frac34|abreve|AElig|eqsim|utdot|setmn|urtri|Equal|Uring|seArr|uring|searr|dashv|Dashv|mumap|nabla|iogon|Iogon|sdote|sdotb|scsim|napid|napos|equiv|natur|Acirc|dblac|erarr|nbump|iprod|erDot|ucirc|awint|esdot|angrt|ncong|isinE|scnap|Scirc|scirc|ndash|isins|Ubrcy|nearr|neArr|isinv|nedot|ubrcy|acute|Ycirc|iukcy|Iukcy|xutri|nesim|caret|jcirc|Jcirc|caron|twixt|ddarr|sccue|exist|jmath|sbquo|ngeqq|angst|ccaps|lceil|ngsim|UpTee|delta|Delta|rtrif|nharr|nhArr|nhpar|rtrie|jukcy|Jukcy|kappa|rsquo|Kappa|nlarr|nlArr|TSHcy|rrarr|aogon|Aogon|fflig|xrarr|tshcy|ccirc|nleqq|filig|upsih|nless|dharl|nlsim|fjlig|ropar|nltri|dharr|robrk|roarr|fllig|fltns|roang|rnmid|subnE|subne|lAarr|trisb|Ccirc|acirc|ccups|blank|VDash|forkv|Vdash|langd|cedil|blk12|blk14|laquo|strns|diams|notin|vDash|larrb|blk34|block|disin|uplus|vdash|vBarv|aelig|starf|Wedge|check|xrArr|lates|lbarr|lBarr|notni|lbbrk|bcong|frasl|lbrke|frown|vrtri|vprop|vnsup|gamma|Gamma|wedge|xodot|bdquo|srarr|doteq|ldquo|boxdl|boxdL|gcirc|Gcirc|boxDl|boxDL|boxdr|boxdR|boxDr|TRADE|trade|rlhar|boxDR|vnsub|npart|vltri|rlarr|boxhd|boxhD|nprec|gescc|nrarr|nrArr|boxHd|boxHD|boxhu|boxhU|nrtri|boxHu|clubs|boxHU|times|colon|Colon|gimel|xlArr|Tilde|nsime|tilde|nsmid|nspar|THORN|thorn|xlarr|nsube|nsubE|thkap|xhArr|comma|nsucc|boxul|boxuL|nsupe|nsupE|gneqq|gnsim|boxUl|boxUL|grave|boxur|boxuR|boxUr|boxUR|lescc|angle|bepsi|boxvh|varpi|boxvH|numsp|Theta|gsime|gsiml|theta|boxVh|boxVH|boxvl|gtcir|gtdot|boxvL|boxVl|boxVL|crarr|cross|Cross|nvsim|boxvr|nwarr|nwArr|sqsup|dtdot|Uogon|lhard|lharu|dtrif|ocirc|Ocirc|lhblk|duarr|odash|sqsub|Hacek|sqcup|llarr|duhar|oelig|OElig|ofcir|boxvR|uogon|lltri|boxVr|csube|uuarr|ohbar|csupe|ctdot|olarr|olcir|harrw|oline|sqcap|omacr|Omacr|omega|Omega|boxVR|aleph|lneqq|lnsim|loang|loarr|rharu|lobrk|hcirc|operp|oplus|rhard|Hcirc|orarr|Union|order|ecirc|Ecirc|cuepr|szlig|cuesc|breve|reals|eDDot|Breve|hoarr|lopar|utrif|rdquo|Umacr|umacr|efDot|swArr|ultri|alpha|rceil|ovbar|swarr|Wcirc|wcirc|smtes|smile|bsemi|lrarr|aring|parsl|lrhar|bsime|uhblk|lrtri|cupor|Aring|uharr|uharl|slarr|rbrke|bsolb|lsime|rbbrk|RBarr|lsimg|phone|rBarr|rbarr|icirc|lsquo|Icirc|emacr|Emacr|ratio|simne|plusb|simlE|simgE|simeq|pluse|ltcir|ltdot|empty|xharr|xdtri|iexcl|Alpha|ltrie|rarrw|pound|ltrif|xcirc|bumpe|prcue|bumpE|asymp|amacr|cuvee|Sigma|sigma|iiint|udhar|iiota|ijlig|IJlig|supnE|imacr|Imacr|prime|Prime|image|prnap|eogon|Eogon|rarrc|mdash|mDDot|cuwed|imath|supne|imped|Amacr|udarr|prsim|micro|rarrb|cwint|raquo|infin|eplus|range|rangd|Ucirc|radic|minus|amalg|veeeq|rAarr|epsiv|ycirc|quest|sharp|quot|zwnj|Qscr|race|qscr|Qopf|qopf|qint|rang|Rang|Zscr|zscr|Zopf|zopf|rarr|rArr|Rarr|Pscr|pscr|prop|prod|prnE|prec|ZHcy|zhcy|prap|Zeta|zeta|Popf|popf|Zdot|plus|zdot|Yuml|yuml|phiv|YUcy|yucy|Yscr|yscr|perp|Yopf|yopf|part|para|YIcy|Ouml|rcub|yicy|YAcy|rdca|ouml|osol|Oscr|rdsh|yacy|real|oscr|xvee|andd|rect|andv|Xscr|oror|ordm|ordf|xscr|ange|aopf|Aopf|rHar|Xopf|opar|Oopf|xopf|xnis|rhov|oopf|omid|xmap|oint|apid|apos|ogon|ascr|Ascr|odot|odiv|xcup|xcap|ocir|oast|nvlt|nvle|nvgt|nvge|nvap|Wscr|wscr|auml|ntlg|ntgl|nsup|nsub|nsim|Nscr|nscr|nsce|Wopf|ring|npre|wopf|npar|Auml|Barv|bbrk|Nopf|nopf|nmid|nLtv|beta|ropf|Ropf|Beta|beth|nles|rpar|nleq|bnot|bNot|nldr|NJcy|rscr|Rscr|Vscr|vscr|rsqb|njcy|bopf|nisd|Bopf|rtri|Vopf|nGtv|ngtr|vopf|boxh|boxH|boxv|nges|ngeq|boxV|bscr|scap|Bscr|bsim|Vert|vert|bsol|bull|bump|caps|cdot|ncup|scnE|ncap|nbsp|napE|Cdot|cent|sdot|Vbar|nang|vBar|chcy|Mscr|mscr|sect|semi|CHcy|Mopf|mopf|sext|circ|cire|mldr|mlcp|cirE|comp|shcy|SHcy|vArr|varr|cong|copf|Copf|copy|COPY|malt|male|macr|lvnE|cscr|ltri|sime|ltcc|simg|Cscr|siml|csub|Uuml|lsqb|lsim|uuml|csup|Lscr|lscr|utri|smid|lpar|cups|smte|lozf|darr|Lopf|Uscr|solb|lopf|sopf|Sopf|lneq|uscr|spar|dArr|lnap|Darr|dash|Sqrt|LJcy|ljcy|lHar|dHar|Upsi|upsi|diam|lesg|djcy|DJcy|leqq|dopf|Dopf|dscr|Dscr|dscy|ldsh|ldca|squf|DScy|sscr|Sscr|dsol|lcub|late|star|Star|Uopf|Larr|lArr|larr|uopf|dtri|dzcy|sube|subE|Lang|lang|Kscr|kscr|Kopf|kopf|KJcy|kjcy|KHcy|khcy|DZcy|ecir|edot|eDot|Jscr|jscr|succ|Jopf|jopf|Edot|uHar|emsp|ensp|Iuml|iuml|eopf|isin|Iscr|iscr|Eopf|epar|sung|epsi|escr|sup1|sup2|sup3|Iota|iota|supe|supE|Iopf|iopf|IOcy|iocy|Escr|esim|Esim|imof|Uarr|QUOT|uArr|uarr|euml|IEcy|iecy|Idot|Euml|euro|excl|Hscr|hscr|Hopf|hopf|TScy|tscy|Tscr|hbar|tscr|flat|tbrk|fnof|hArr|harr|half|fopf|Fopf|tdot|gvnE|fork|trie|gtcc|fscr|Fscr|gdot|gsim|Gscr|gscr|Gopf|gopf|gneq|Gdot|tosa|gnap|Topf|topf|geqq|toea|GJcy|gjcy|tint|gesl|mid|Sfr|ggg|top|ges|gla|glE|glj|geq|gne|gEl|gel|gnE|Gcy|gcy|gap|Tfr|tfr|Tcy|tcy|Hat|Tau|Ffr|tau|Tab|hfr|Hfr|ffr|Fcy|fcy|icy|Icy|iff|ETH|eth|ifr|Ifr|Eta|eta|int|Int|Sup|sup|ucy|Ucy|Sum|sum|jcy|ENG|ufr|Ufr|eng|Jcy|jfr|els|ell|egs|Efr|efr|Jfr|uml|kcy|Kcy|Ecy|ecy|kfr|Kfr|lap|Sub|sub|lat|lcy|Lcy|leg|Dot|dot|lEg|leq|les|squ|div|die|lfr|Lfr|lgE|Dfr|dfr|Del|deg|Dcy|dcy|lne|lnE|sol|loz|smt|Cup|lrm|cup|lsh|Lsh|sim|shy|map|Map|mcy|Mcy|mfr|Mfr|mho|gfr|Gfr|sfr|cir|Chi|chi|nap|Cfr|vcy|Vcy|cfr|Scy|scy|ncy|Ncy|vee|Vee|Cap|cap|nfr|scE|sce|Nfr|nge|ngE|nGg|vfr|Vfr|ngt|bot|nGt|nis|niv|Rsh|rsh|nle|nlE|bne|Bfr|bfr|nLl|nlt|nLt|Bcy|bcy|not|Not|rlm|wfr|Wfr|npr|nsc|num|ocy|ast|Ocy|ofr|xfr|Xfr|Ofr|ogt|ohm|apE|olt|Rho|ape|rho|Rfr|rfr|ord|REG|ang|reg|orv|And|and|AMP|Rcy|amp|Afr|ycy|Ycy|yen|yfr|Yfr|rcy|par|pcy|Pcy|pfr|Pfr|phi|Phi|afr|Acy|acy|zcy|Zcy|piv|acE|acd|zfr|Zfr|pre|prE|psi|Psi|qfr|Qfr|zwj|Or|ge|Gg|gt|gg|el|oS|lt|Lt|LT|Re|lg|gl|eg|ne|Im|it|le|DD|wp|wr|nu|Nu|dd|lE|Sc|sc|pi|Pi|ee|af|ll|Ll|rx|gE|xi|pm|Xi|ic|pr|Pr|in|ni|mp|mu|ac|Mu|or|ap|Gt|GT|ii);|&(Aacute|Agrave|Atilde|Ccedil|Eacute|Egrave|Iacute|Igrave|Ntilde|Oacute|Ograve|Oslash|Otilde|Uacute|Ugrave|Yacute|aacute|agrave|atilde|brvbar|ccedil|curren|divide|eacute|egrave|frac12|frac14|frac34|iacute|igrave|iquest|middot|ntilde|oacute|ograve|oslash|otilde|plusmn|uacute|ugrave|yacute|AElig|Acirc|Aring|Ecirc|Icirc|Ocirc|THORN|Ucirc|acirc|acute|aelig|aring|cedil|ecirc|icirc|iexcl|laquo|micro|ocirc|pound|raquo|szlig|thorn|times|ucirc|Auml|COPY|Euml|Iuml|Ouml|QUOT|Uuml|auml|cent|copy|euml|iuml|macr|nbsp|ordf|ordm|ouml|para|quot|sect|sup1|sup2|sup3|uuml|yuml|AMP|ETH|REG|amp|deg|eth|not|reg|shy|uml|yen|GT|LT|gt|lt)(?!;)([=a-zA-Z0-9]?)|&#([0-9]+)(;?)|&#[xX]([a-fA-F0-9]+)(;?)|&([0-9a-zA-Z]+)/g; + var decodeMap = {'aacute':'\xE1','Aacute':'\xC1','abreve':'\u0103','Abreve':'\u0102','ac':'\u223E','acd':'\u223F','acE':'\u223E\u0333','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','acy':'\u0430','Acy':'\u0410','aelig':'\xE6','AElig':'\xC6','af':'\u2061','afr':'\uD835\uDD1E','Afr':'\uD835\uDD04','agrave':'\xE0','Agrave':'\xC0','alefsym':'\u2135','aleph':'\u2135','alpha':'\u03B1','Alpha':'\u0391','amacr':'\u0101','Amacr':'\u0100','amalg':'\u2A3F','amp':'&','AMP':'&','and':'\u2227','And':'\u2A53','andand':'\u2A55','andd':'\u2A5C','andslope':'\u2A58','andv':'\u2A5A','ang':'\u2220','ange':'\u29A4','angle':'\u2220','angmsd':'\u2221','angmsdaa':'\u29A8','angmsdab':'\u29A9','angmsdac':'\u29AA','angmsdad':'\u29AB','angmsdae':'\u29AC','angmsdaf':'\u29AD','angmsdag':'\u29AE','angmsdah':'\u29AF','angrt':'\u221F','angrtvb':'\u22BE','angrtvbd':'\u299D','angsph':'\u2222','angst':'\xC5','angzarr':'\u237C','aogon':'\u0105','Aogon':'\u0104','aopf':'\uD835\uDD52','Aopf':'\uD835\uDD38','ap':'\u2248','apacir':'\u2A6F','ape':'\u224A','apE':'\u2A70','apid':'\u224B','apos':'\'','ApplyFunction':'\u2061','approx':'\u2248','approxeq':'\u224A','aring':'\xE5','Aring':'\xC5','ascr':'\uD835\uDCB6','Ascr':'\uD835\uDC9C','Assign':'\u2254','ast':'*','asymp':'\u2248','asympeq':'\u224D','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','awconint':'\u2233','awint':'\u2A11','backcong':'\u224C','backepsilon':'\u03F6','backprime':'\u2035','backsim':'\u223D','backsimeq':'\u22CD','Backslash':'\u2216','Barv':'\u2AE7','barvee':'\u22BD','barwed':'\u2305','Barwed':'\u2306','barwedge':'\u2305','bbrk':'\u23B5','bbrktbrk':'\u23B6','bcong':'\u224C','bcy':'\u0431','Bcy':'\u0411','bdquo':'\u201E','becaus':'\u2235','because':'\u2235','Because':'\u2235','bemptyv':'\u29B0','bepsi':'\u03F6','bernou':'\u212C','Bernoullis':'\u212C','beta':'\u03B2','Beta':'\u0392','beth':'\u2136','between':'\u226C','bfr':'\uD835\uDD1F','Bfr':'\uD835\uDD05','bigcap':'\u22C2','bigcirc':'\u25EF','bigcup':'\u22C3','bigodot':'\u2A00','bigoplus':'\u2A01','bigotimes':'\u2A02','bigsqcup':'\u2A06','bigstar':'\u2605','bigtriangledown':'\u25BD','bigtriangleup':'\u25B3','biguplus':'\u2A04','bigvee':'\u22C1','bigwedge':'\u22C0','bkarow':'\u290D','blacklozenge':'\u29EB','blacksquare':'\u25AA','blacktriangle':'\u25B4','blacktriangledown':'\u25BE','blacktriangleleft':'\u25C2','blacktriangleright':'\u25B8','blank':'\u2423','blk12':'\u2592','blk14':'\u2591','blk34':'\u2593','block':'\u2588','bne':'=\u20E5','bnequiv':'\u2261\u20E5','bnot':'\u2310','bNot':'\u2AED','bopf':'\uD835\uDD53','Bopf':'\uD835\uDD39','bot':'\u22A5','bottom':'\u22A5','bowtie':'\u22C8','boxbox':'\u29C9','boxdl':'\u2510','boxdL':'\u2555','boxDl':'\u2556','boxDL':'\u2557','boxdr':'\u250C','boxdR':'\u2552','boxDr':'\u2553','boxDR':'\u2554','boxh':'\u2500','boxH':'\u2550','boxhd':'\u252C','boxhD':'\u2565','boxHd':'\u2564','boxHD':'\u2566','boxhu':'\u2534','boxhU':'\u2568','boxHu':'\u2567','boxHU':'\u2569','boxminus':'\u229F','boxplus':'\u229E','boxtimes':'\u22A0','boxul':'\u2518','boxuL':'\u255B','boxUl':'\u255C','boxUL':'\u255D','boxur':'\u2514','boxuR':'\u2558','boxUr':'\u2559','boxUR':'\u255A','boxv':'\u2502','boxV':'\u2551','boxvh':'\u253C','boxvH':'\u256A','boxVh':'\u256B','boxVH':'\u256C','boxvl':'\u2524','boxvL':'\u2561','boxVl':'\u2562','boxVL':'\u2563','boxvr':'\u251C','boxvR':'\u255E','boxVr':'\u255F','boxVR':'\u2560','bprime':'\u2035','breve':'\u02D8','Breve':'\u02D8','brvbar':'\xA6','bscr':'\uD835\uDCB7','Bscr':'\u212C','bsemi':'\u204F','bsim':'\u223D','bsime':'\u22CD','bsol':'\\','bsolb':'\u29C5','bsolhsub':'\u27C8','bull':'\u2022','bullet':'\u2022','bump':'\u224E','bumpe':'\u224F','bumpE':'\u2AAE','bumpeq':'\u224F','Bumpeq':'\u224E','cacute':'\u0107','Cacute':'\u0106','cap':'\u2229','Cap':'\u22D2','capand':'\u2A44','capbrcup':'\u2A49','capcap':'\u2A4B','capcup':'\u2A47','capdot':'\u2A40','CapitalDifferentialD':'\u2145','caps':'\u2229\uFE00','caret':'\u2041','caron':'\u02C7','Cayleys':'\u212D','ccaps':'\u2A4D','ccaron':'\u010D','Ccaron':'\u010C','ccedil':'\xE7','Ccedil':'\xC7','ccirc':'\u0109','Ccirc':'\u0108','Cconint':'\u2230','ccups':'\u2A4C','ccupssm':'\u2A50','cdot':'\u010B','Cdot':'\u010A','cedil':'\xB8','Cedilla':'\xB8','cemptyv':'\u29B2','cent':'\xA2','centerdot':'\xB7','CenterDot':'\xB7','cfr':'\uD835\uDD20','Cfr':'\u212D','chcy':'\u0447','CHcy':'\u0427','check':'\u2713','checkmark':'\u2713','chi':'\u03C7','Chi':'\u03A7','cir':'\u25CB','circ':'\u02C6','circeq':'\u2257','circlearrowleft':'\u21BA','circlearrowright':'\u21BB','circledast':'\u229B','circledcirc':'\u229A','circleddash':'\u229D','CircleDot':'\u2299','circledR':'\xAE','circledS':'\u24C8','CircleMinus':'\u2296','CirclePlus':'\u2295','CircleTimes':'\u2297','cire':'\u2257','cirE':'\u29C3','cirfnint':'\u2A10','cirmid':'\u2AEF','cirscir':'\u29C2','ClockwiseContourIntegral':'\u2232','CloseCurlyDoubleQuote':'\u201D','CloseCurlyQuote':'\u2019','clubs':'\u2663','clubsuit':'\u2663','colon':':','Colon':'\u2237','colone':'\u2254','Colone':'\u2A74','coloneq':'\u2254','comma':',','commat':'@','comp':'\u2201','compfn':'\u2218','complement':'\u2201','complexes':'\u2102','cong':'\u2245','congdot':'\u2A6D','Congruent':'\u2261','conint':'\u222E','Conint':'\u222F','ContourIntegral':'\u222E','copf':'\uD835\uDD54','Copf':'\u2102','coprod':'\u2210','Coproduct':'\u2210','copy':'\xA9','COPY':'\xA9','copysr':'\u2117','CounterClockwiseContourIntegral':'\u2233','crarr':'\u21B5','cross':'\u2717','Cross':'\u2A2F','cscr':'\uD835\uDCB8','Cscr':'\uD835\uDC9E','csub':'\u2ACF','csube':'\u2AD1','csup':'\u2AD0','csupe':'\u2AD2','ctdot':'\u22EF','cudarrl':'\u2938','cudarrr':'\u2935','cuepr':'\u22DE','cuesc':'\u22DF','cularr':'\u21B6','cularrp':'\u293D','cup':'\u222A','Cup':'\u22D3','cupbrcap':'\u2A48','cupcap':'\u2A46','CupCap':'\u224D','cupcup':'\u2A4A','cupdot':'\u228D','cupor':'\u2A45','cups':'\u222A\uFE00','curarr':'\u21B7','curarrm':'\u293C','curlyeqprec':'\u22DE','curlyeqsucc':'\u22DF','curlyvee':'\u22CE','curlywedge':'\u22CF','curren':'\xA4','curvearrowleft':'\u21B6','curvearrowright':'\u21B7','cuvee':'\u22CE','cuwed':'\u22CF','cwconint':'\u2232','cwint':'\u2231','cylcty':'\u232D','dagger':'\u2020','Dagger':'\u2021','daleth':'\u2138','darr':'\u2193','dArr':'\u21D3','Darr':'\u21A1','dash':'\u2010','dashv':'\u22A3','Dashv':'\u2AE4','dbkarow':'\u290F','dblac':'\u02DD','dcaron':'\u010F','Dcaron':'\u010E','dcy':'\u0434','Dcy':'\u0414','dd':'\u2146','DD':'\u2145','ddagger':'\u2021','ddarr':'\u21CA','DDotrahd':'\u2911','ddotseq':'\u2A77','deg':'\xB0','Del':'\u2207','delta':'\u03B4','Delta':'\u0394','demptyv':'\u29B1','dfisht':'\u297F','dfr':'\uD835\uDD21','Dfr':'\uD835\uDD07','dHar':'\u2965','dharl':'\u21C3','dharr':'\u21C2','DiacriticalAcute':'\xB4','DiacriticalDot':'\u02D9','DiacriticalDoubleAcute':'\u02DD','DiacriticalGrave':'`','DiacriticalTilde':'\u02DC','diam':'\u22C4','diamond':'\u22C4','Diamond':'\u22C4','diamondsuit':'\u2666','diams':'\u2666','die':'\xA8','DifferentialD':'\u2146','digamma':'\u03DD','disin':'\u22F2','div':'\xF7','divide':'\xF7','divideontimes':'\u22C7','divonx':'\u22C7','djcy':'\u0452','DJcy':'\u0402','dlcorn':'\u231E','dlcrop':'\u230D','dollar':'$','dopf':'\uD835\uDD55','Dopf':'\uD835\uDD3B','dot':'\u02D9','Dot':'\xA8','DotDot':'\u20DC','doteq':'\u2250','doteqdot':'\u2251','DotEqual':'\u2250','dotminus':'\u2238','dotplus':'\u2214','dotsquare':'\u22A1','doublebarwedge':'\u2306','DoubleContourIntegral':'\u222F','DoubleDot':'\xA8','DoubleDownArrow':'\u21D3','DoubleLeftArrow':'\u21D0','DoubleLeftRightArrow':'\u21D4','DoubleLeftTee':'\u2AE4','DoubleLongLeftArrow':'\u27F8','DoubleLongLeftRightArrow':'\u27FA','DoubleLongRightArrow':'\u27F9','DoubleRightArrow':'\u21D2','DoubleRightTee':'\u22A8','DoubleUpArrow':'\u21D1','DoubleUpDownArrow':'\u21D5','DoubleVerticalBar':'\u2225','downarrow':'\u2193','Downarrow':'\u21D3','DownArrow':'\u2193','DownArrowBar':'\u2913','DownArrowUpArrow':'\u21F5','DownBreve':'\u0311','downdownarrows':'\u21CA','downharpoonleft':'\u21C3','downharpoonright':'\u21C2','DownLeftRightVector':'\u2950','DownLeftTeeVector':'\u295E','DownLeftVector':'\u21BD','DownLeftVectorBar':'\u2956','DownRightTeeVector':'\u295F','DownRightVector':'\u21C1','DownRightVectorBar':'\u2957','DownTee':'\u22A4','DownTeeArrow':'\u21A7','drbkarow':'\u2910','drcorn':'\u231F','drcrop':'\u230C','dscr':'\uD835\uDCB9','Dscr':'\uD835\uDC9F','dscy':'\u0455','DScy':'\u0405','dsol':'\u29F6','dstrok':'\u0111','Dstrok':'\u0110','dtdot':'\u22F1','dtri':'\u25BF','dtrif':'\u25BE','duarr':'\u21F5','duhar':'\u296F','dwangle':'\u29A6','dzcy':'\u045F','DZcy':'\u040F','dzigrarr':'\u27FF','eacute':'\xE9','Eacute':'\xC9','easter':'\u2A6E','ecaron':'\u011B','Ecaron':'\u011A','ecir':'\u2256','ecirc':'\xEA','Ecirc':'\xCA','ecolon':'\u2255','ecy':'\u044D','Ecy':'\u042D','eDDot':'\u2A77','edot':'\u0117','eDot':'\u2251','Edot':'\u0116','ee':'\u2147','efDot':'\u2252','efr':'\uD835\uDD22','Efr':'\uD835\uDD08','eg':'\u2A9A','egrave':'\xE8','Egrave':'\xC8','egs':'\u2A96','egsdot':'\u2A98','el':'\u2A99','Element':'\u2208','elinters':'\u23E7','ell':'\u2113','els':'\u2A95','elsdot':'\u2A97','emacr':'\u0113','Emacr':'\u0112','empty':'\u2205','emptyset':'\u2205','EmptySmallSquare':'\u25FB','emptyv':'\u2205','EmptyVerySmallSquare':'\u25AB','emsp':'\u2003','emsp13':'\u2004','emsp14':'\u2005','eng':'\u014B','ENG':'\u014A','ensp':'\u2002','eogon':'\u0119','Eogon':'\u0118','eopf':'\uD835\uDD56','Eopf':'\uD835\uDD3C','epar':'\u22D5','eparsl':'\u29E3','eplus':'\u2A71','epsi':'\u03B5','epsilon':'\u03B5','Epsilon':'\u0395','epsiv':'\u03F5','eqcirc':'\u2256','eqcolon':'\u2255','eqsim':'\u2242','eqslantgtr':'\u2A96','eqslantless':'\u2A95','Equal':'\u2A75','equals':'=','EqualTilde':'\u2242','equest':'\u225F','Equilibrium':'\u21CC','equiv':'\u2261','equivDD':'\u2A78','eqvparsl':'\u29E5','erarr':'\u2971','erDot':'\u2253','escr':'\u212F','Escr':'\u2130','esdot':'\u2250','esim':'\u2242','Esim':'\u2A73','eta':'\u03B7','Eta':'\u0397','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','euro':'\u20AC','excl':'!','exist':'\u2203','Exists':'\u2203','expectation':'\u2130','exponentiale':'\u2147','ExponentialE':'\u2147','fallingdotseq':'\u2252','fcy':'\u0444','Fcy':'\u0424','female':'\u2640','ffilig':'\uFB03','fflig':'\uFB00','ffllig':'\uFB04','ffr':'\uD835\uDD23','Ffr':'\uD835\uDD09','filig':'\uFB01','FilledSmallSquare':'\u25FC','FilledVerySmallSquare':'\u25AA','fjlig':'fj','flat':'\u266D','fllig':'\uFB02','fltns':'\u25B1','fnof':'\u0192','fopf':'\uD835\uDD57','Fopf':'\uD835\uDD3D','forall':'\u2200','ForAll':'\u2200','fork':'\u22D4','forkv':'\u2AD9','Fouriertrf':'\u2131','fpartint':'\u2A0D','frac12':'\xBD','frac13':'\u2153','frac14':'\xBC','frac15':'\u2155','frac16':'\u2159','frac18':'\u215B','frac23':'\u2154','frac25':'\u2156','frac34':'\xBE','frac35':'\u2157','frac38':'\u215C','frac45':'\u2158','frac56':'\u215A','frac58':'\u215D','frac78':'\u215E','frasl':'\u2044','frown':'\u2322','fscr':'\uD835\uDCBB','Fscr':'\u2131','gacute':'\u01F5','gamma':'\u03B3','Gamma':'\u0393','gammad':'\u03DD','Gammad':'\u03DC','gap':'\u2A86','gbreve':'\u011F','Gbreve':'\u011E','Gcedil':'\u0122','gcirc':'\u011D','Gcirc':'\u011C','gcy':'\u0433','Gcy':'\u0413','gdot':'\u0121','Gdot':'\u0120','ge':'\u2265','gE':'\u2267','gel':'\u22DB','gEl':'\u2A8C','geq':'\u2265','geqq':'\u2267','geqslant':'\u2A7E','ges':'\u2A7E','gescc':'\u2AA9','gesdot':'\u2A80','gesdoto':'\u2A82','gesdotol':'\u2A84','gesl':'\u22DB\uFE00','gesles':'\u2A94','gfr':'\uD835\uDD24','Gfr':'\uD835\uDD0A','gg':'\u226B','Gg':'\u22D9','ggg':'\u22D9','gimel':'\u2137','gjcy':'\u0453','GJcy':'\u0403','gl':'\u2277','gla':'\u2AA5','glE':'\u2A92','glj':'\u2AA4','gnap':'\u2A8A','gnapprox':'\u2A8A','gne':'\u2A88','gnE':'\u2269','gneq':'\u2A88','gneqq':'\u2269','gnsim':'\u22E7','gopf':'\uD835\uDD58','Gopf':'\uD835\uDD3E','grave':'`','GreaterEqual':'\u2265','GreaterEqualLess':'\u22DB','GreaterFullEqual':'\u2267','GreaterGreater':'\u2AA2','GreaterLess':'\u2277','GreaterSlantEqual':'\u2A7E','GreaterTilde':'\u2273','gscr':'\u210A','Gscr':'\uD835\uDCA2','gsim':'\u2273','gsime':'\u2A8E','gsiml':'\u2A90','gt':'>','Gt':'\u226B','GT':'>','gtcc':'\u2AA7','gtcir':'\u2A7A','gtdot':'\u22D7','gtlPar':'\u2995','gtquest':'\u2A7C','gtrapprox':'\u2A86','gtrarr':'\u2978','gtrdot':'\u22D7','gtreqless':'\u22DB','gtreqqless':'\u2A8C','gtrless':'\u2277','gtrsim':'\u2273','gvertneqq':'\u2269\uFE00','gvnE':'\u2269\uFE00','Hacek':'\u02C7','hairsp':'\u200A','half':'\xBD','hamilt':'\u210B','hardcy':'\u044A','HARDcy':'\u042A','harr':'\u2194','hArr':'\u21D4','harrcir':'\u2948','harrw':'\u21AD','Hat':'^','hbar':'\u210F','hcirc':'\u0125','Hcirc':'\u0124','hearts':'\u2665','heartsuit':'\u2665','hellip':'\u2026','hercon':'\u22B9','hfr':'\uD835\uDD25','Hfr':'\u210C','HilbertSpace':'\u210B','hksearow':'\u2925','hkswarow':'\u2926','hoarr':'\u21FF','homtht':'\u223B','hookleftarrow':'\u21A9','hookrightarrow':'\u21AA','hopf':'\uD835\uDD59','Hopf':'\u210D','horbar':'\u2015','HorizontalLine':'\u2500','hscr':'\uD835\uDCBD','Hscr':'\u210B','hslash':'\u210F','hstrok':'\u0127','Hstrok':'\u0126','HumpDownHump':'\u224E','HumpEqual':'\u224F','hybull':'\u2043','hyphen':'\u2010','iacute':'\xED','Iacute':'\xCD','ic':'\u2063','icirc':'\xEE','Icirc':'\xCE','icy':'\u0438','Icy':'\u0418','Idot':'\u0130','iecy':'\u0435','IEcy':'\u0415','iexcl':'\xA1','iff':'\u21D4','ifr':'\uD835\uDD26','Ifr':'\u2111','igrave':'\xEC','Igrave':'\xCC','ii':'\u2148','iiiint':'\u2A0C','iiint':'\u222D','iinfin':'\u29DC','iiota':'\u2129','ijlig':'\u0133','IJlig':'\u0132','Im':'\u2111','imacr':'\u012B','Imacr':'\u012A','image':'\u2111','ImaginaryI':'\u2148','imagline':'\u2110','imagpart':'\u2111','imath':'\u0131','imof':'\u22B7','imped':'\u01B5','Implies':'\u21D2','in':'\u2208','incare':'\u2105','infin':'\u221E','infintie':'\u29DD','inodot':'\u0131','int':'\u222B','Int':'\u222C','intcal':'\u22BA','integers':'\u2124','Integral':'\u222B','intercal':'\u22BA','Intersection':'\u22C2','intlarhk':'\u2A17','intprod':'\u2A3C','InvisibleComma':'\u2063','InvisibleTimes':'\u2062','iocy':'\u0451','IOcy':'\u0401','iogon':'\u012F','Iogon':'\u012E','iopf':'\uD835\uDD5A','Iopf':'\uD835\uDD40','iota':'\u03B9','Iota':'\u0399','iprod':'\u2A3C','iquest':'\xBF','iscr':'\uD835\uDCBE','Iscr':'\u2110','isin':'\u2208','isindot':'\u22F5','isinE':'\u22F9','isins':'\u22F4','isinsv':'\u22F3','isinv':'\u2208','it':'\u2062','itilde':'\u0129','Itilde':'\u0128','iukcy':'\u0456','Iukcy':'\u0406','iuml':'\xEF','Iuml':'\xCF','jcirc':'\u0135','Jcirc':'\u0134','jcy':'\u0439','Jcy':'\u0419','jfr':'\uD835\uDD27','Jfr':'\uD835\uDD0D','jmath':'\u0237','jopf':'\uD835\uDD5B','Jopf':'\uD835\uDD41','jscr':'\uD835\uDCBF','Jscr':'\uD835\uDCA5','jsercy':'\u0458','Jsercy':'\u0408','jukcy':'\u0454','Jukcy':'\u0404','kappa':'\u03BA','Kappa':'\u039A','kappav':'\u03F0','kcedil':'\u0137','Kcedil':'\u0136','kcy':'\u043A','Kcy':'\u041A','kfr':'\uD835\uDD28','Kfr':'\uD835\uDD0E','kgreen':'\u0138','khcy':'\u0445','KHcy':'\u0425','kjcy':'\u045C','KJcy':'\u040C','kopf':'\uD835\uDD5C','Kopf':'\uD835\uDD42','kscr':'\uD835\uDCC0','Kscr':'\uD835\uDCA6','lAarr':'\u21DA','lacute':'\u013A','Lacute':'\u0139','laemptyv':'\u29B4','lagran':'\u2112','lambda':'\u03BB','Lambda':'\u039B','lang':'\u27E8','Lang':'\u27EA','langd':'\u2991','langle':'\u27E8','lap':'\u2A85','Laplacetrf':'\u2112','laquo':'\xAB','larr':'\u2190','lArr':'\u21D0','Larr':'\u219E','larrb':'\u21E4','larrbfs':'\u291F','larrfs':'\u291D','larrhk':'\u21A9','larrlp':'\u21AB','larrpl':'\u2939','larrsim':'\u2973','larrtl':'\u21A2','lat':'\u2AAB','latail':'\u2919','lAtail':'\u291B','late':'\u2AAD','lates':'\u2AAD\uFE00','lbarr':'\u290C','lBarr':'\u290E','lbbrk':'\u2772','lbrace':'{','lbrack':'[','lbrke':'\u298B','lbrksld':'\u298F','lbrkslu':'\u298D','lcaron':'\u013E','Lcaron':'\u013D','lcedil':'\u013C','Lcedil':'\u013B','lceil':'\u2308','lcub':'{','lcy':'\u043B','Lcy':'\u041B','ldca':'\u2936','ldquo':'\u201C','ldquor':'\u201E','ldrdhar':'\u2967','ldrushar':'\u294B','ldsh':'\u21B2','le':'\u2264','lE':'\u2266','LeftAngleBracket':'\u27E8','leftarrow':'\u2190','Leftarrow':'\u21D0','LeftArrow':'\u2190','LeftArrowBar':'\u21E4','LeftArrowRightArrow':'\u21C6','leftarrowtail':'\u21A2','LeftCeiling':'\u2308','LeftDoubleBracket':'\u27E6','LeftDownTeeVector':'\u2961','LeftDownVector':'\u21C3','LeftDownVectorBar':'\u2959','LeftFloor':'\u230A','leftharpoondown':'\u21BD','leftharpoonup':'\u21BC','leftleftarrows':'\u21C7','leftrightarrow':'\u2194','Leftrightarrow':'\u21D4','LeftRightArrow':'\u2194','leftrightarrows':'\u21C6','leftrightharpoons':'\u21CB','leftrightsquigarrow':'\u21AD','LeftRightVector':'\u294E','LeftTee':'\u22A3','LeftTeeArrow':'\u21A4','LeftTeeVector':'\u295A','leftthreetimes':'\u22CB','LeftTriangle':'\u22B2','LeftTriangleBar':'\u29CF','LeftTriangleEqual':'\u22B4','LeftUpDownVector':'\u2951','LeftUpTeeVector':'\u2960','LeftUpVector':'\u21BF','LeftUpVectorBar':'\u2958','LeftVector':'\u21BC','LeftVectorBar':'\u2952','leg':'\u22DA','lEg':'\u2A8B','leq':'\u2264','leqq':'\u2266','leqslant':'\u2A7D','les':'\u2A7D','lescc':'\u2AA8','lesdot':'\u2A7F','lesdoto':'\u2A81','lesdotor':'\u2A83','lesg':'\u22DA\uFE00','lesges':'\u2A93','lessapprox':'\u2A85','lessdot':'\u22D6','lesseqgtr':'\u22DA','lesseqqgtr':'\u2A8B','LessEqualGreater':'\u22DA','LessFullEqual':'\u2266','LessGreater':'\u2276','lessgtr':'\u2276','LessLess':'\u2AA1','lesssim':'\u2272','LessSlantEqual':'\u2A7D','LessTilde':'\u2272','lfisht':'\u297C','lfloor':'\u230A','lfr':'\uD835\uDD29','Lfr':'\uD835\uDD0F','lg':'\u2276','lgE':'\u2A91','lHar':'\u2962','lhard':'\u21BD','lharu':'\u21BC','lharul':'\u296A','lhblk':'\u2584','ljcy':'\u0459','LJcy':'\u0409','ll':'\u226A','Ll':'\u22D8','llarr':'\u21C7','llcorner':'\u231E','Lleftarrow':'\u21DA','llhard':'\u296B','lltri':'\u25FA','lmidot':'\u0140','Lmidot':'\u013F','lmoust':'\u23B0','lmoustache':'\u23B0','lnap':'\u2A89','lnapprox':'\u2A89','lne':'\u2A87','lnE':'\u2268','lneq':'\u2A87','lneqq':'\u2268','lnsim':'\u22E6','loang':'\u27EC','loarr':'\u21FD','lobrk':'\u27E6','longleftarrow':'\u27F5','Longleftarrow':'\u27F8','LongLeftArrow':'\u27F5','longleftrightarrow':'\u27F7','Longleftrightarrow':'\u27FA','LongLeftRightArrow':'\u27F7','longmapsto':'\u27FC','longrightarrow':'\u27F6','Longrightarrow':'\u27F9','LongRightArrow':'\u27F6','looparrowleft':'\u21AB','looparrowright':'\u21AC','lopar':'\u2985','lopf':'\uD835\uDD5D','Lopf':'\uD835\uDD43','loplus':'\u2A2D','lotimes':'\u2A34','lowast':'\u2217','lowbar':'_','LowerLeftArrow':'\u2199','LowerRightArrow':'\u2198','loz':'\u25CA','lozenge':'\u25CA','lozf':'\u29EB','lpar':'(','lparlt':'\u2993','lrarr':'\u21C6','lrcorner':'\u231F','lrhar':'\u21CB','lrhard':'\u296D','lrm':'\u200E','lrtri':'\u22BF','lsaquo':'\u2039','lscr':'\uD835\uDCC1','Lscr':'\u2112','lsh':'\u21B0','Lsh':'\u21B0','lsim':'\u2272','lsime':'\u2A8D','lsimg':'\u2A8F','lsqb':'[','lsquo':'\u2018','lsquor':'\u201A','lstrok':'\u0142','Lstrok':'\u0141','lt':'<','Lt':'\u226A','LT':'<','ltcc':'\u2AA6','ltcir':'\u2A79','ltdot':'\u22D6','lthree':'\u22CB','ltimes':'\u22C9','ltlarr':'\u2976','ltquest':'\u2A7B','ltri':'\u25C3','ltrie':'\u22B4','ltrif':'\u25C2','ltrPar':'\u2996','lurdshar':'\u294A','luruhar':'\u2966','lvertneqq':'\u2268\uFE00','lvnE':'\u2268\uFE00','macr':'\xAF','male':'\u2642','malt':'\u2720','maltese':'\u2720','map':'\u21A6','Map':'\u2905','mapsto':'\u21A6','mapstodown':'\u21A7','mapstoleft':'\u21A4','mapstoup':'\u21A5','marker':'\u25AE','mcomma':'\u2A29','mcy':'\u043C','Mcy':'\u041C','mdash':'\u2014','mDDot':'\u223A','measuredangle':'\u2221','MediumSpace':'\u205F','Mellintrf':'\u2133','mfr':'\uD835\uDD2A','Mfr':'\uD835\uDD10','mho':'\u2127','micro':'\xB5','mid':'\u2223','midast':'*','midcir':'\u2AF0','middot':'\xB7','minus':'\u2212','minusb':'\u229F','minusd':'\u2238','minusdu':'\u2A2A','MinusPlus':'\u2213','mlcp':'\u2ADB','mldr':'\u2026','mnplus':'\u2213','models':'\u22A7','mopf':'\uD835\uDD5E','Mopf':'\uD835\uDD44','mp':'\u2213','mscr':'\uD835\uDCC2','Mscr':'\u2133','mstpos':'\u223E','mu':'\u03BC','Mu':'\u039C','multimap':'\u22B8','mumap':'\u22B8','nabla':'\u2207','nacute':'\u0144','Nacute':'\u0143','nang':'\u2220\u20D2','nap':'\u2249','napE':'\u2A70\u0338','napid':'\u224B\u0338','napos':'\u0149','napprox':'\u2249','natur':'\u266E','natural':'\u266E','naturals':'\u2115','nbsp':'\xA0','nbump':'\u224E\u0338','nbumpe':'\u224F\u0338','ncap':'\u2A43','ncaron':'\u0148','Ncaron':'\u0147','ncedil':'\u0146','Ncedil':'\u0145','ncong':'\u2247','ncongdot':'\u2A6D\u0338','ncup':'\u2A42','ncy':'\u043D','Ncy':'\u041D','ndash':'\u2013','ne':'\u2260','nearhk':'\u2924','nearr':'\u2197','neArr':'\u21D7','nearrow':'\u2197','nedot':'\u2250\u0338','NegativeMediumSpace':'\u200B','NegativeThickSpace':'\u200B','NegativeThinSpace':'\u200B','NegativeVeryThinSpace':'\u200B','nequiv':'\u2262','nesear':'\u2928','nesim':'\u2242\u0338','NestedGreaterGreater':'\u226B','NestedLessLess':'\u226A','NewLine':'\n','nexist':'\u2204','nexists':'\u2204','nfr':'\uD835\uDD2B','Nfr':'\uD835\uDD11','nge':'\u2271','ngE':'\u2267\u0338','ngeq':'\u2271','ngeqq':'\u2267\u0338','ngeqslant':'\u2A7E\u0338','nges':'\u2A7E\u0338','nGg':'\u22D9\u0338','ngsim':'\u2275','ngt':'\u226F','nGt':'\u226B\u20D2','ngtr':'\u226F','nGtv':'\u226B\u0338','nharr':'\u21AE','nhArr':'\u21CE','nhpar':'\u2AF2','ni':'\u220B','nis':'\u22FC','nisd':'\u22FA','niv':'\u220B','njcy':'\u045A','NJcy':'\u040A','nlarr':'\u219A','nlArr':'\u21CD','nldr':'\u2025','nle':'\u2270','nlE':'\u2266\u0338','nleftarrow':'\u219A','nLeftarrow':'\u21CD','nleftrightarrow':'\u21AE','nLeftrightarrow':'\u21CE','nleq':'\u2270','nleqq':'\u2266\u0338','nleqslant':'\u2A7D\u0338','nles':'\u2A7D\u0338','nless':'\u226E','nLl':'\u22D8\u0338','nlsim':'\u2274','nlt':'\u226E','nLt':'\u226A\u20D2','nltri':'\u22EA','nltrie':'\u22EC','nLtv':'\u226A\u0338','nmid':'\u2224','NoBreak':'\u2060','NonBreakingSpace':'\xA0','nopf':'\uD835\uDD5F','Nopf':'\u2115','not':'\xAC','Not':'\u2AEC','NotCongruent':'\u2262','NotCupCap':'\u226D','NotDoubleVerticalBar':'\u2226','NotElement':'\u2209','NotEqual':'\u2260','NotEqualTilde':'\u2242\u0338','NotExists':'\u2204','NotGreater':'\u226F','NotGreaterEqual':'\u2271','NotGreaterFullEqual':'\u2267\u0338','NotGreaterGreater':'\u226B\u0338','NotGreaterLess':'\u2279','NotGreaterSlantEqual':'\u2A7E\u0338','NotGreaterTilde':'\u2275','NotHumpDownHump':'\u224E\u0338','NotHumpEqual':'\u224F\u0338','notin':'\u2209','notindot':'\u22F5\u0338','notinE':'\u22F9\u0338','notinva':'\u2209','notinvb':'\u22F7','notinvc':'\u22F6','NotLeftTriangle':'\u22EA','NotLeftTriangleBar':'\u29CF\u0338','NotLeftTriangleEqual':'\u22EC','NotLess':'\u226E','NotLessEqual':'\u2270','NotLessGreater':'\u2278','NotLessLess':'\u226A\u0338','NotLessSlantEqual':'\u2A7D\u0338','NotLessTilde':'\u2274','NotNestedGreaterGreater':'\u2AA2\u0338','NotNestedLessLess':'\u2AA1\u0338','notni':'\u220C','notniva':'\u220C','notnivb':'\u22FE','notnivc':'\u22FD','NotPrecedes':'\u2280','NotPrecedesEqual':'\u2AAF\u0338','NotPrecedesSlantEqual':'\u22E0','NotReverseElement':'\u220C','NotRightTriangle':'\u22EB','NotRightTriangleBar':'\u29D0\u0338','NotRightTriangleEqual':'\u22ED','NotSquareSubset':'\u228F\u0338','NotSquareSubsetEqual':'\u22E2','NotSquareSuperset':'\u2290\u0338','NotSquareSupersetEqual':'\u22E3','NotSubset':'\u2282\u20D2','NotSubsetEqual':'\u2288','NotSucceeds':'\u2281','NotSucceedsEqual':'\u2AB0\u0338','NotSucceedsSlantEqual':'\u22E1','NotSucceedsTilde':'\u227F\u0338','NotSuperset':'\u2283\u20D2','NotSupersetEqual':'\u2289','NotTilde':'\u2241','NotTildeEqual':'\u2244','NotTildeFullEqual':'\u2247','NotTildeTilde':'\u2249','NotVerticalBar':'\u2224','npar':'\u2226','nparallel':'\u2226','nparsl':'\u2AFD\u20E5','npart':'\u2202\u0338','npolint':'\u2A14','npr':'\u2280','nprcue':'\u22E0','npre':'\u2AAF\u0338','nprec':'\u2280','npreceq':'\u2AAF\u0338','nrarr':'\u219B','nrArr':'\u21CF','nrarrc':'\u2933\u0338','nrarrw':'\u219D\u0338','nrightarrow':'\u219B','nRightarrow':'\u21CF','nrtri':'\u22EB','nrtrie':'\u22ED','nsc':'\u2281','nsccue':'\u22E1','nsce':'\u2AB0\u0338','nscr':'\uD835\uDCC3','Nscr':'\uD835\uDCA9','nshortmid':'\u2224','nshortparallel':'\u2226','nsim':'\u2241','nsime':'\u2244','nsimeq':'\u2244','nsmid':'\u2224','nspar':'\u2226','nsqsube':'\u22E2','nsqsupe':'\u22E3','nsub':'\u2284','nsube':'\u2288','nsubE':'\u2AC5\u0338','nsubset':'\u2282\u20D2','nsubseteq':'\u2288','nsubseteqq':'\u2AC5\u0338','nsucc':'\u2281','nsucceq':'\u2AB0\u0338','nsup':'\u2285','nsupe':'\u2289','nsupE':'\u2AC6\u0338','nsupset':'\u2283\u20D2','nsupseteq':'\u2289','nsupseteqq':'\u2AC6\u0338','ntgl':'\u2279','ntilde':'\xF1','Ntilde':'\xD1','ntlg':'\u2278','ntriangleleft':'\u22EA','ntrianglelefteq':'\u22EC','ntriangleright':'\u22EB','ntrianglerighteq':'\u22ED','nu':'\u03BD','Nu':'\u039D','num':'#','numero':'\u2116','numsp':'\u2007','nvap':'\u224D\u20D2','nvdash':'\u22AC','nvDash':'\u22AD','nVdash':'\u22AE','nVDash':'\u22AF','nvge':'\u2265\u20D2','nvgt':'>\u20D2','nvHarr':'\u2904','nvinfin':'\u29DE','nvlArr':'\u2902','nvle':'\u2264\u20D2','nvlt':'<\u20D2','nvltrie':'\u22B4\u20D2','nvrArr':'\u2903','nvrtrie':'\u22B5\u20D2','nvsim':'\u223C\u20D2','nwarhk':'\u2923','nwarr':'\u2196','nwArr':'\u21D6','nwarrow':'\u2196','nwnear':'\u2927','oacute':'\xF3','Oacute':'\xD3','oast':'\u229B','ocir':'\u229A','ocirc':'\xF4','Ocirc':'\xD4','ocy':'\u043E','Ocy':'\u041E','odash':'\u229D','odblac':'\u0151','Odblac':'\u0150','odiv':'\u2A38','odot':'\u2299','odsold':'\u29BC','oelig':'\u0153','OElig':'\u0152','ofcir':'\u29BF','ofr':'\uD835\uDD2C','Ofr':'\uD835\uDD12','ogon':'\u02DB','ograve':'\xF2','Ograve':'\xD2','ogt':'\u29C1','ohbar':'\u29B5','ohm':'\u03A9','oint':'\u222E','olarr':'\u21BA','olcir':'\u29BE','olcross':'\u29BB','oline':'\u203E','olt':'\u29C0','omacr':'\u014D','Omacr':'\u014C','omega':'\u03C9','Omega':'\u03A9','omicron':'\u03BF','Omicron':'\u039F','omid':'\u29B6','ominus':'\u2296','oopf':'\uD835\uDD60','Oopf':'\uD835\uDD46','opar':'\u29B7','OpenCurlyDoubleQuote':'\u201C','OpenCurlyQuote':'\u2018','operp':'\u29B9','oplus':'\u2295','or':'\u2228','Or':'\u2A54','orarr':'\u21BB','ord':'\u2A5D','order':'\u2134','orderof':'\u2134','ordf':'\xAA','ordm':'\xBA','origof':'\u22B6','oror':'\u2A56','orslope':'\u2A57','orv':'\u2A5B','oS':'\u24C8','oscr':'\u2134','Oscr':'\uD835\uDCAA','oslash':'\xF8','Oslash':'\xD8','osol':'\u2298','otilde':'\xF5','Otilde':'\xD5','otimes':'\u2297','Otimes':'\u2A37','otimesas':'\u2A36','ouml':'\xF6','Ouml':'\xD6','ovbar':'\u233D','OverBar':'\u203E','OverBrace':'\u23DE','OverBracket':'\u23B4','OverParenthesis':'\u23DC','par':'\u2225','para':'\xB6','parallel':'\u2225','parsim':'\u2AF3','parsl':'\u2AFD','part':'\u2202','PartialD':'\u2202','pcy':'\u043F','Pcy':'\u041F','percnt':'%','period':'.','permil':'\u2030','perp':'\u22A5','pertenk':'\u2031','pfr':'\uD835\uDD2D','Pfr':'\uD835\uDD13','phi':'\u03C6','Phi':'\u03A6','phiv':'\u03D5','phmmat':'\u2133','phone':'\u260E','pi':'\u03C0','Pi':'\u03A0','pitchfork':'\u22D4','piv':'\u03D6','planck':'\u210F','planckh':'\u210E','plankv':'\u210F','plus':'+','plusacir':'\u2A23','plusb':'\u229E','pluscir':'\u2A22','plusdo':'\u2214','plusdu':'\u2A25','pluse':'\u2A72','PlusMinus':'\xB1','plusmn':'\xB1','plussim':'\u2A26','plustwo':'\u2A27','pm':'\xB1','Poincareplane':'\u210C','pointint':'\u2A15','popf':'\uD835\uDD61','Popf':'\u2119','pound':'\xA3','pr':'\u227A','Pr':'\u2ABB','prap':'\u2AB7','prcue':'\u227C','pre':'\u2AAF','prE':'\u2AB3','prec':'\u227A','precapprox':'\u2AB7','preccurlyeq':'\u227C','Precedes':'\u227A','PrecedesEqual':'\u2AAF','PrecedesSlantEqual':'\u227C','PrecedesTilde':'\u227E','preceq':'\u2AAF','precnapprox':'\u2AB9','precneqq':'\u2AB5','precnsim':'\u22E8','precsim':'\u227E','prime':'\u2032','Prime':'\u2033','primes':'\u2119','prnap':'\u2AB9','prnE':'\u2AB5','prnsim':'\u22E8','prod':'\u220F','Product':'\u220F','profalar':'\u232E','profline':'\u2312','profsurf':'\u2313','prop':'\u221D','Proportion':'\u2237','Proportional':'\u221D','propto':'\u221D','prsim':'\u227E','prurel':'\u22B0','pscr':'\uD835\uDCC5','Pscr':'\uD835\uDCAB','psi':'\u03C8','Psi':'\u03A8','puncsp':'\u2008','qfr':'\uD835\uDD2E','Qfr':'\uD835\uDD14','qint':'\u2A0C','qopf':'\uD835\uDD62','Qopf':'\u211A','qprime':'\u2057','qscr':'\uD835\uDCC6','Qscr':'\uD835\uDCAC','quaternions':'\u210D','quatint':'\u2A16','quest':'?','questeq':'\u225F','quot':'"','QUOT':'"','rAarr':'\u21DB','race':'\u223D\u0331','racute':'\u0155','Racute':'\u0154','radic':'\u221A','raemptyv':'\u29B3','rang':'\u27E9','Rang':'\u27EB','rangd':'\u2992','range':'\u29A5','rangle':'\u27E9','raquo':'\xBB','rarr':'\u2192','rArr':'\u21D2','Rarr':'\u21A0','rarrap':'\u2975','rarrb':'\u21E5','rarrbfs':'\u2920','rarrc':'\u2933','rarrfs':'\u291E','rarrhk':'\u21AA','rarrlp':'\u21AC','rarrpl':'\u2945','rarrsim':'\u2974','rarrtl':'\u21A3','Rarrtl':'\u2916','rarrw':'\u219D','ratail':'\u291A','rAtail':'\u291C','ratio':'\u2236','rationals':'\u211A','rbarr':'\u290D','rBarr':'\u290F','RBarr':'\u2910','rbbrk':'\u2773','rbrace':'}','rbrack':']','rbrke':'\u298C','rbrksld':'\u298E','rbrkslu':'\u2990','rcaron':'\u0159','Rcaron':'\u0158','rcedil':'\u0157','Rcedil':'\u0156','rceil':'\u2309','rcub':'}','rcy':'\u0440','Rcy':'\u0420','rdca':'\u2937','rdldhar':'\u2969','rdquo':'\u201D','rdquor':'\u201D','rdsh':'\u21B3','Re':'\u211C','real':'\u211C','realine':'\u211B','realpart':'\u211C','reals':'\u211D','rect':'\u25AD','reg':'\xAE','REG':'\xAE','ReverseElement':'\u220B','ReverseEquilibrium':'\u21CB','ReverseUpEquilibrium':'\u296F','rfisht':'\u297D','rfloor':'\u230B','rfr':'\uD835\uDD2F','Rfr':'\u211C','rHar':'\u2964','rhard':'\u21C1','rharu':'\u21C0','rharul':'\u296C','rho':'\u03C1','Rho':'\u03A1','rhov':'\u03F1','RightAngleBracket':'\u27E9','rightarrow':'\u2192','Rightarrow':'\u21D2','RightArrow':'\u2192','RightArrowBar':'\u21E5','RightArrowLeftArrow':'\u21C4','rightarrowtail':'\u21A3','RightCeiling':'\u2309','RightDoubleBracket':'\u27E7','RightDownTeeVector':'\u295D','RightDownVector':'\u21C2','RightDownVectorBar':'\u2955','RightFloor':'\u230B','rightharpoondown':'\u21C1','rightharpoonup':'\u21C0','rightleftarrows':'\u21C4','rightleftharpoons':'\u21CC','rightrightarrows':'\u21C9','rightsquigarrow':'\u219D','RightTee':'\u22A2','RightTeeArrow':'\u21A6','RightTeeVector':'\u295B','rightthreetimes':'\u22CC','RightTriangle':'\u22B3','RightTriangleBar':'\u29D0','RightTriangleEqual':'\u22B5','RightUpDownVector':'\u294F','RightUpTeeVector':'\u295C','RightUpVector':'\u21BE','RightUpVectorBar':'\u2954','RightVector':'\u21C0','RightVectorBar':'\u2953','ring':'\u02DA','risingdotseq':'\u2253','rlarr':'\u21C4','rlhar':'\u21CC','rlm':'\u200F','rmoust':'\u23B1','rmoustache':'\u23B1','rnmid':'\u2AEE','roang':'\u27ED','roarr':'\u21FE','robrk':'\u27E7','ropar':'\u2986','ropf':'\uD835\uDD63','Ropf':'\u211D','roplus':'\u2A2E','rotimes':'\u2A35','RoundImplies':'\u2970','rpar':')','rpargt':'\u2994','rppolint':'\u2A12','rrarr':'\u21C9','Rrightarrow':'\u21DB','rsaquo':'\u203A','rscr':'\uD835\uDCC7','Rscr':'\u211B','rsh':'\u21B1','Rsh':'\u21B1','rsqb':']','rsquo':'\u2019','rsquor':'\u2019','rthree':'\u22CC','rtimes':'\u22CA','rtri':'\u25B9','rtrie':'\u22B5','rtrif':'\u25B8','rtriltri':'\u29CE','RuleDelayed':'\u29F4','ruluhar':'\u2968','rx':'\u211E','sacute':'\u015B','Sacute':'\u015A','sbquo':'\u201A','sc':'\u227B','Sc':'\u2ABC','scap':'\u2AB8','scaron':'\u0161','Scaron':'\u0160','sccue':'\u227D','sce':'\u2AB0','scE':'\u2AB4','scedil':'\u015F','Scedil':'\u015E','scirc':'\u015D','Scirc':'\u015C','scnap':'\u2ABA','scnE':'\u2AB6','scnsim':'\u22E9','scpolint':'\u2A13','scsim':'\u227F','scy':'\u0441','Scy':'\u0421','sdot':'\u22C5','sdotb':'\u22A1','sdote':'\u2A66','searhk':'\u2925','searr':'\u2198','seArr':'\u21D8','searrow':'\u2198','sect':'\xA7','semi':';','seswar':'\u2929','setminus':'\u2216','setmn':'\u2216','sext':'\u2736','sfr':'\uD835\uDD30','Sfr':'\uD835\uDD16','sfrown':'\u2322','sharp':'\u266F','shchcy':'\u0449','SHCHcy':'\u0429','shcy':'\u0448','SHcy':'\u0428','ShortDownArrow':'\u2193','ShortLeftArrow':'\u2190','shortmid':'\u2223','shortparallel':'\u2225','ShortRightArrow':'\u2192','ShortUpArrow':'\u2191','shy':'\xAD','sigma':'\u03C3','Sigma':'\u03A3','sigmaf':'\u03C2','sigmav':'\u03C2','sim':'\u223C','simdot':'\u2A6A','sime':'\u2243','simeq':'\u2243','simg':'\u2A9E','simgE':'\u2AA0','siml':'\u2A9D','simlE':'\u2A9F','simne':'\u2246','simplus':'\u2A24','simrarr':'\u2972','slarr':'\u2190','SmallCircle':'\u2218','smallsetminus':'\u2216','smashp':'\u2A33','smeparsl':'\u29E4','smid':'\u2223','smile':'\u2323','smt':'\u2AAA','smte':'\u2AAC','smtes':'\u2AAC\uFE00','softcy':'\u044C','SOFTcy':'\u042C','sol':'/','solb':'\u29C4','solbar':'\u233F','sopf':'\uD835\uDD64','Sopf':'\uD835\uDD4A','spades':'\u2660','spadesuit':'\u2660','spar':'\u2225','sqcap':'\u2293','sqcaps':'\u2293\uFE00','sqcup':'\u2294','sqcups':'\u2294\uFE00','Sqrt':'\u221A','sqsub':'\u228F','sqsube':'\u2291','sqsubset':'\u228F','sqsubseteq':'\u2291','sqsup':'\u2290','sqsupe':'\u2292','sqsupset':'\u2290','sqsupseteq':'\u2292','squ':'\u25A1','square':'\u25A1','Square':'\u25A1','SquareIntersection':'\u2293','SquareSubset':'\u228F','SquareSubsetEqual':'\u2291','SquareSuperset':'\u2290','SquareSupersetEqual':'\u2292','SquareUnion':'\u2294','squarf':'\u25AA','squf':'\u25AA','srarr':'\u2192','sscr':'\uD835\uDCC8','Sscr':'\uD835\uDCAE','ssetmn':'\u2216','ssmile':'\u2323','sstarf':'\u22C6','star':'\u2606','Star':'\u22C6','starf':'\u2605','straightepsilon':'\u03F5','straightphi':'\u03D5','strns':'\xAF','sub':'\u2282','Sub':'\u22D0','subdot':'\u2ABD','sube':'\u2286','subE':'\u2AC5','subedot':'\u2AC3','submult':'\u2AC1','subne':'\u228A','subnE':'\u2ACB','subplus':'\u2ABF','subrarr':'\u2979','subset':'\u2282','Subset':'\u22D0','subseteq':'\u2286','subseteqq':'\u2AC5','SubsetEqual':'\u2286','subsetneq':'\u228A','subsetneqq':'\u2ACB','subsim':'\u2AC7','subsub':'\u2AD5','subsup':'\u2AD3','succ':'\u227B','succapprox':'\u2AB8','succcurlyeq':'\u227D','Succeeds':'\u227B','SucceedsEqual':'\u2AB0','SucceedsSlantEqual':'\u227D','SucceedsTilde':'\u227F','succeq':'\u2AB0','succnapprox':'\u2ABA','succneqq':'\u2AB6','succnsim':'\u22E9','succsim':'\u227F','SuchThat':'\u220B','sum':'\u2211','Sum':'\u2211','sung':'\u266A','sup':'\u2283','Sup':'\u22D1','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','supdot':'\u2ABE','supdsub':'\u2AD8','supe':'\u2287','supE':'\u2AC6','supedot':'\u2AC4','Superset':'\u2283','SupersetEqual':'\u2287','suphsol':'\u27C9','suphsub':'\u2AD7','suplarr':'\u297B','supmult':'\u2AC2','supne':'\u228B','supnE':'\u2ACC','supplus':'\u2AC0','supset':'\u2283','Supset':'\u22D1','supseteq':'\u2287','supseteqq':'\u2AC6','supsetneq':'\u228B','supsetneqq':'\u2ACC','supsim':'\u2AC8','supsub':'\u2AD4','supsup':'\u2AD6','swarhk':'\u2926','swarr':'\u2199','swArr':'\u21D9','swarrow':'\u2199','swnwar':'\u292A','szlig':'\xDF','Tab':'\t','target':'\u2316','tau':'\u03C4','Tau':'\u03A4','tbrk':'\u23B4','tcaron':'\u0165','Tcaron':'\u0164','tcedil':'\u0163','Tcedil':'\u0162','tcy':'\u0442','Tcy':'\u0422','tdot':'\u20DB','telrec':'\u2315','tfr':'\uD835\uDD31','Tfr':'\uD835\uDD17','there4':'\u2234','therefore':'\u2234','Therefore':'\u2234','theta':'\u03B8','Theta':'\u0398','thetasym':'\u03D1','thetav':'\u03D1','thickapprox':'\u2248','thicksim':'\u223C','ThickSpace':'\u205F\u200A','thinsp':'\u2009','ThinSpace':'\u2009','thkap':'\u2248','thksim':'\u223C','thorn':'\xFE','THORN':'\xDE','tilde':'\u02DC','Tilde':'\u223C','TildeEqual':'\u2243','TildeFullEqual':'\u2245','TildeTilde':'\u2248','times':'\xD7','timesb':'\u22A0','timesbar':'\u2A31','timesd':'\u2A30','tint':'\u222D','toea':'\u2928','top':'\u22A4','topbot':'\u2336','topcir':'\u2AF1','topf':'\uD835\uDD65','Topf':'\uD835\uDD4B','topfork':'\u2ADA','tosa':'\u2929','tprime':'\u2034','trade':'\u2122','TRADE':'\u2122','triangle':'\u25B5','triangledown':'\u25BF','triangleleft':'\u25C3','trianglelefteq':'\u22B4','triangleq':'\u225C','triangleright':'\u25B9','trianglerighteq':'\u22B5','tridot':'\u25EC','trie':'\u225C','triminus':'\u2A3A','TripleDot':'\u20DB','triplus':'\u2A39','trisb':'\u29CD','tritime':'\u2A3B','trpezium':'\u23E2','tscr':'\uD835\uDCC9','Tscr':'\uD835\uDCAF','tscy':'\u0446','TScy':'\u0426','tshcy':'\u045B','TSHcy':'\u040B','tstrok':'\u0167','Tstrok':'\u0166','twixt':'\u226C','twoheadleftarrow':'\u219E','twoheadrightarrow':'\u21A0','uacute':'\xFA','Uacute':'\xDA','uarr':'\u2191','uArr':'\u21D1','Uarr':'\u219F','Uarrocir':'\u2949','ubrcy':'\u045E','Ubrcy':'\u040E','ubreve':'\u016D','Ubreve':'\u016C','ucirc':'\xFB','Ucirc':'\xDB','ucy':'\u0443','Ucy':'\u0423','udarr':'\u21C5','udblac':'\u0171','Udblac':'\u0170','udhar':'\u296E','ufisht':'\u297E','ufr':'\uD835\uDD32','Ufr':'\uD835\uDD18','ugrave':'\xF9','Ugrave':'\xD9','uHar':'\u2963','uharl':'\u21BF','uharr':'\u21BE','uhblk':'\u2580','ulcorn':'\u231C','ulcorner':'\u231C','ulcrop':'\u230F','ultri':'\u25F8','umacr':'\u016B','Umacr':'\u016A','uml':'\xA8','UnderBar':'_','UnderBrace':'\u23DF','UnderBracket':'\u23B5','UnderParenthesis':'\u23DD','Union':'\u22C3','UnionPlus':'\u228E','uogon':'\u0173','Uogon':'\u0172','uopf':'\uD835\uDD66','Uopf':'\uD835\uDD4C','uparrow':'\u2191','Uparrow':'\u21D1','UpArrow':'\u2191','UpArrowBar':'\u2912','UpArrowDownArrow':'\u21C5','updownarrow':'\u2195','Updownarrow':'\u21D5','UpDownArrow':'\u2195','UpEquilibrium':'\u296E','upharpoonleft':'\u21BF','upharpoonright':'\u21BE','uplus':'\u228E','UpperLeftArrow':'\u2196','UpperRightArrow':'\u2197','upsi':'\u03C5','Upsi':'\u03D2','upsih':'\u03D2','upsilon':'\u03C5','Upsilon':'\u03A5','UpTee':'\u22A5','UpTeeArrow':'\u21A5','upuparrows':'\u21C8','urcorn':'\u231D','urcorner':'\u231D','urcrop':'\u230E','uring':'\u016F','Uring':'\u016E','urtri':'\u25F9','uscr':'\uD835\uDCCA','Uscr':'\uD835\uDCB0','utdot':'\u22F0','utilde':'\u0169','Utilde':'\u0168','utri':'\u25B5','utrif':'\u25B4','uuarr':'\u21C8','uuml':'\xFC','Uuml':'\xDC','uwangle':'\u29A7','vangrt':'\u299C','varepsilon':'\u03F5','varkappa':'\u03F0','varnothing':'\u2205','varphi':'\u03D5','varpi':'\u03D6','varpropto':'\u221D','varr':'\u2195','vArr':'\u21D5','varrho':'\u03F1','varsigma':'\u03C2','varsubsetneq':'\u228A\uFE00','varsubsetneqq':'\u2ACB\uFE00','varsupsetneq':'\u228B\uFE00','varsupsetneqq':'\u2ACC\uFE00','vartheta':'\u03D1','vartriangleleft':'\u22B2','vartriangleright':'\u22B3','vBar':'\u2AE8','Vbar':'\u2AEB','vBarv':'\u2AE9','vcy':'\u0432','Vcy':'\u0412','vdash':'\u22A2','vDash':'\u22A8','Vdash':'\u22A9','VDash':'\u22AB','Vdashl':'\u2AE6','vee':'\u2228','Vee':'\u22C1','veebar':'\u22BB','veeeq':'\u225A','vellip':'\u22EE','verbar':'|','Verbar':'\u2016','vert':'|','Vert':'\u2016','VerticalBar':'\u2223','VerticalLine':'|','VerticalSeparator':'\u2758','VerticalTilde':'\u2240','VeryThinSpace':'\u200A','vfr':'\uD835\uDD33','Vfr':'\uD835\uDD19','vltri':'\u22B2','vnsub':'\u2282\u20D2','vnsup':'\u2283\u20D2','vopf':'\uD835\uDD67','Vopf':'\uD835\uDD4D','vprop':'\u221D','vrtri':'\u22B3','vscr':'\uD835\uDCCB','Vscr':'\uD835\uDCB1','vsubne':'\u228A\uFE00','vsubnE':'\u2ACB\uFE00','vsupne':'\u228B\uFE00','vsupnE':'\u2ACC\uFE00','Vvdash':'\u22AA','vzigzag':'\u299A','wcirc':'\u0175','Wcirc':'\u0174','wedbar':'\u2A5F','wedge':'\u2227','Wedge':'\u22C0','wedgeq':'\u2259','weierp':'\u2118','wfr':'\uD835\uDD34','Wfr':'\uD835\uDD1A','wopf':'\uD835\uDD68','Wopf':'\uD835\uDD4E','wp':'\u2118','wr':'\u2240','wreath':'\u2240','wscr':'\uD835\uDCCC','Wscr':'\uD835\uDCB2','xcap':'\u22C2','xcirc':'\u25EF','xcup':'\u22C3','xdtri':'\u25BD','xfr':'\uD835\uDD35','Xfr':'\uD835\uDD1B','xharr':'\u27F7','xhArr':'\u27FA','xi':'\u03BE','Xi':'\u039E','xlarr':'\u27F5','xlArr':'\u27F8','xmap':'\u27FC','xnis':'\u22FB','xodot':'\u2A00','xopf':'\uD835\uDD69','Xopf':'\uD835\uDD4F','xoplus':'\u2A01','xotime':'\u2A02','xrarr':'\u27F6','xrArr':'\u27F9','xscr':'\uD835\uDCCD','Xscr':'\uD835\uDCB3','xsqcup':'\u2A06','xuplus':'\u2A04','xutri':'\u25B3','xvee':'\u22C1','xwedge':'\u22C0','yacute':'\xFD','Yacute':'\xDD','yacy':'\u044F','YAcy':'\u042F','ycirc':'\u0177','Ycirc':'\u0176','ycy':'\u044B','Ycy':'\u042B','yen':'\xA5','yfr':'\uD835\uDD36','Yfr':'\uD835\uDD1C','yicy':'\u0457','YIcy':'\u0407','yopf':'\uD835\uDD6A','Yopf':'\uD835\uDD50','yscr':'\uD835\uDCCE','Yscr':'\uD835\uDCB4','yucy':'\u044E','YUcy':'\u042E','yuml':'\xFF','Yuml':'\u0178','zacute':'\u017A','Zacute':'\u0179','zcaron':'\u017E','Zcaron':'\u017D','zcy':'\u0437','Zcy':'\u0417','zdot':'\u017C','Zdot':'\u017B','zeetrf':'\u2128','ZeroWidthSpace':'\u200B','zeta':'\u03B6','Zeta':'\u0396','zfr':'\uD835\uDD37','Zfr':'\u2128','zhcy':'\u0436','ZHcy':'\u0416','zigrarr':'\u21DD','zopf':'\uD835\uDD6B','Zopf':'\u2124','zscr':'\uD835\uDCCF','Zscr':'\uD835\uDCB5','zwj':'\u200D','zwnj':'\u200C'}; + var decodeMapLegacy = {'aacute':'\xE1','Aacute':'\xC1','acirc':'\xE2','Acirc':'\xC2','acute':'\xB4','aelig':'\xE6','AElig':'\xC6','agrave':'\xE0','Agrave':'\xC0','amp':'&','AMP':'&','aring':'\xE5','Aring':'\xC5','atilde':'\xE3','Atilde':'\xC3','auml':'\xE4','Auml':'\xC4','brvbar':'\xA6','ccedil':'\xE7','Ccedil':'\xC7','cedil':'\xB8','cent':'\xA2','copy':'\xA9','COPY':'\xA9','curren':'\xA4','deg':'\xB0','divide':'\xF7','eacute':'\xE9','Eacute':'\xC9','ecirc':'\xEA','Ecirc':'\xCA','egrave':'\xE8','Egrave':'\xC8','eth':'\xF0','ETH':'\xD0','euml':'\xEB','Euml':'\xCB','frac12':'\xBD','frac14':'\xBC','frac34':'\xBE','gt':'>','GT':'>','iacute':'\xED','Iacute':'\xCD','icirc':'\xEE','Icirc':'\xCE','iexcl':'\xA1','igrave':'\xEC','Igrave':'\xCC','iquest':'\xBF','iuml':'\xEF','Iuml':'\xCF','laquo':'\xAB','lt':'<','LT':'<','macr':'\xAF','micro':'\xB5','middot':'\xB7','nbsp':'\xA0','not':'\xAC','ntilde':'\xF1','Ntilde':'\xD1','oacute':'\xF3','Oacute':'\xD3','ocirc':'\xF4','Ocirc':'\xD4','ograve':'\xF2','Ograve':'\xD2','ordf':'\xAA','ordm':'\xBA','oslash':'\xF8','Oslash':'\xD8','otilde':'\xF5','Otilde':'\xD5','ouml':'\xF6','Ouml':'\xD6','para':'\xB6','plusmn':'\xB1','pound':'\xA3','quot':'"','QUOT':'"','raquo':'\xBB','reg':'\xAE','REG':'\xAE','sect':'\xA7','shy':'\xAD','sup1':'\xB9','sup2':'\xB2','sup3':'\xB3','szlig':'\xDF','thorn':'\xFE','THORN':'\xDE','times':'\xD7','uacute':'\xFA','Uacute':'\xDA','ucirc':'\xFB','Ucirc':'\xDB','ugrave':'\xF9','Ugrave':'\xD9','uml':'\xA8','uuml':'\xFC','Uuml':'\xDC','yacute':'\xFD','Yacute':'\xDD','yen':'\xA5','yuml':'\xFF'}; + var decodeMapNumeric = {'0':'\uFFFD','128':'\u20AC','130':'\u201A','131':'\u0192','132':'\u201E','133':'\u2026','134':'\u2020','135':'\u2021','136':'\u02C6','137':'\u2030','138':'\u0160','139':'\u2039','140':'\u0152','142':'\u017D','145':'\u2018','146':'\u2019','147':'\u201C','148':'\u201D','149':'\u2022','150':'\u2013','151':'\u2014','152':'\u02DC','153':'\u2122','154':'\u0161','155':'\u203A','156':'\u0153','158':'\u017E','159':'\u0178'}; + var invalidReferenceCodePoints = [1,2,3,4,5,6,7,8,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,64976,64977,64978,64979,64980,64981,64982,64983,64984,64985,64986,64987,64988,64989,64990,64991,64992,64993,64994,64995,64996,64997,64998,64999,65000,65001,65002,65003,65004,65005,65006,65007,65534,65535,131070,131071,196606,196607,262142,262143,327678,327679,393214,393215,458750,458751,524286,524287,589822,589823,655358,655359,720894,720895,786430,786431,851966,851967,917502,917503,983038,983039,1048574,1048575,1114110,1114111]; + + /*--------------------------------------------------------------------------*/ + + var stringFromCharCode = String.fromCharCode; + + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + var has = function(object, propertyName) { + return hasOwnProperty.call(object, propertyName); + }; + + var contains = function(array, value) { + var index = -1; + var length = array.length; + while (++index < length) { + if (array[index] == value) { + return true; + } + } + return false; + }; + + var merge = function(options, defaults) { + if (!options) { + return defaults; + } + var result = {}; + var key; + for (key in defaults) { + // A `hasOwnProperty` check is not needed here, since only recognized + // option names are used anyway. Any others are ignored. + result[key] = has(options, key) ? options[key] : defaults[key]; + } + return result; + }; + + // Modified version of `ucs2encode`; see https://mths.be/punycode. + var codePointToSymbol = function(codePoint, strict) { + var output = ''; + if ((codePoint >= 0xD800 && codePoint <= 0xDFFF) || codePoint > 0x10FFFF) { + // See issue #4: + // “Otherwise, if the number is in the range 0xD800 to 0xDFFF or is + // greater than 0x10FFFF, then this is a parse error. Return a U+FFFD + // REPLACEMENT CHARACTER.â€� + if (strict) { + parseError('character reference outside the permissible Unicode range'); + } + return '\uFFFD'; + } + if (has(decodeMapNumeric, codePoint)) { + if (strict) { + parseError('disallowed character reference'); + } + return decodeMapNumeric[codePoint]; + } + if (strict && contains(invalidReferenceCodePoints, codePoint)) { + parseError('disallowed character reference'); + } + if (codePoint > 0xFFFF) { + codePoint -= 0x10000; + output += stringFromCharCode(codePoint >>> 10 & 0x3FF | 0xD800); + codePoint = 0xDC00 | codePoint & 0x3FF; + } + output += stringFromCharCode(codePoint); + return output; + }; + + var hexEscape = function(codePoint) { + return '&#x' + codePoint.toString(16).toUpperCase() + ';'; + }; + + var decEscape = function(codePoint) { + return '&#' + codePoint + ';'; + }; + + var parseError = function(message) { + throw Error('Parse error: ' + message); + }; + + /*--------------------------------------------------------------------------*/ + + var encode = function(string, options) { + options = merge(options, encode.options); + var strict = options.strict; + if (strict && regexInvalidRawCodePoint.test(string)) { + parseError('forbidden code point'); + } + var encodeEverything = options.encodeEverything; + var useNamedReferences = options.useNamedReferences; + var allowUnsafeSymbols = options.allowUnsafeSymbols; + var escapeCodePoint = options.decimal ? decEscape : hexEscape; + + var escapeBmpSymbol = function(symbol) { + return escapeCodePoint(symbol.charCodeAt(0)); + }; + + if (encodeEverything) { + // Encode ASCII symbols. + string = string.replace(regexAsciiWhitelist, function(symbol) { + // Use named references if requested & possible. + if (useNamedReferences && has(encodeMap, symbol)) { + return '&' + encodeMap[symbol] + ';'; + } + return escapeBmpSymbol(symbol); + }); + // Shorten a few escapes that represent two symbols, of which at least one + // is within the ASCII range. + if (useNamedReferences) { + string = string + .replace(/>\u20D2/g, '>⃒') + .replace(/<\u20D2/g, '<⃒') + .replace(/fj/g, 'fj'); + } + // Encode non-ASCII symbols. + if (useNamedReferences) { + // Encode non-ASCII symbols that can be replaced with a named reference. + string = string.replace(regexEncodeNonAscii, function(string) { + // Note: there is no need to check `has(encodeMap, string)` here. + return '&' + encodeMap[string] + ';'; + }); + } + // Note: any remaining non-ASCII symbols are handled outside of the `if`. + } else if (useNamedReferences) { + // Apply named character references. + // Encode `<>"'&` using named character references. + if (!allowUnsafeSymbols) { + string = string.replace(regexEscape, function(string) { + return '&' + encodeMap[string] + ';'; // no need to check `has()` here + }); + } + // Shorten escapes that represent two symbols, of which at least one is + // `<>"'&`. + string = string + .replace(/>\u20D2/g, '>⃒') + .replace(/<\u20D2/g, '<⃒'); + // Encode non-ASCII symbols that can be replaced with a named reference. + string = string.replace(regexEncodeNonAscii, function(string) { + // Note: there is no need to check `has(encodeMap, string)` here. + return '&' + encodeMap[string] + ';'; + }); + } else if (!allowUnsafeSymbols) { + // Encode `<>"'&` using hexadecimal escapes, now that they’re not handled + // using named character references. + string = string.replace(regexEscape, escapeBmpSymbol); + } + return string + // Encode astral symbols. + .replace(regexAstralSymbols, function($0) { + // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + var high = $0.charCodeAt(0); + var low = $0.charCodeAt(1); + var codePoint = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000; + return escapeCodePoint(codePoint); + }) + // Encode any remaining BMP symbols that are not printable ASCII symbols + // using a hexadecimal escape. + .replace(regexBmpWhitelist, escapeBmpSymbol); + }; + // Expose default options (so they can be overridden globally). + encode.options = { + 'allowUnsafeSymbols': false, + 'encodeEverything': false, + 'strict': false, + 'useNamedReferences': false, + 'decimal' : false + }; + + var decode = function(html, options) { + options = merge(options, decode.options); + var strict = options.strict; + if (strict && regexInvalidEntity.test(html)) { + parseError('malformed character reference'); + } + return html.replace(regexDecode, function($0, $1, $2, $3, $4, $5, $6, $7, $8) { + var codePoint; + var semicolon; + var decDigits; + var hexDigits; + var reference; + var next; + + if ($1) { + reference = $1; + // Note: there is no need to check `has(decodeMap, reference)`. + return decodeMap[reference]; + } + + if ($2) { + // Decode named character references without trailing `;`, e.g. `&`. + // This is only a parse error if it gets converted to `&`, or if it is + // followed by `=` in an attribute context. + reference = $2; + next = $3; + if (next && options.isAttributeValue) { + if (strict && next == '=') { + parseError('`&` did not start a character reference'); + } + return $0; + } else { + if (strict) { + parseError( + 'named character reference was not terminated by a semicolon' + ); + } + // Note: there is no need to check `has(decodeMapLegacy, reference)`. + return decodeMapLegacy[reference] + (next || ''); + } + } + + if ($4) { + // Decode decimal escapes, e.g. `𝌆`. + decDigits = $4; + semicolon = $5; + if (strict && !semicolon) { + parseError('character reference was not terminated by a semicolon'); + } + codePoint = parseInt(decDigits, 10); + return codePointToSymbol(codePoint, strict); + } + + if ($6) { + // Decode hexadecimal escapes, e.g. `𝌆`. + hexDigits = $6; + semicolon = $7; + if (strict && !semicolon) { + parseError('character reference was not terminated by a semicolon'); + } + codePoint = parseInt(hexDigits, 16); + return codePointToSymbol(codePoint, strict); + } + + // If we’re still here, `if ($7)` is implied; it’s an ambiguous + // ampersand for sure. https://mths.be/notes/ambiguous-ampersands + if (strict) { + parseError( + 'named character reference was not terminated by a semicolon' + ); + } + return $0; + }); + }; + // Expose default options (so they can be overridden globally). + decode.options = { + 'isAttributeValue': false, + 'strict': false + }; + + var escape = function(string) { + return string.replace(regexEscape, function($0) { + // Note: there is no need to check `has(escapeMap, $0)` here. + return escapeMap[$0]; + }); + }; + + /*--------------------------------------------------------------------------*/ + + var he = { + 'version': '1.2.0', + 'encode': encode, + 'decode': decode, + 'escape': escape, + 'unescape': decode + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + false + ) { + define(function() { + return he; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js, io.js, or RingoJS v0.8.0+ + freeModule.exports = he; + } else { // in Narwhal or RingoJS v0.7.0- + for (var key in he) { + has(he, key) && (freeExports[key] = he[key]); + } + } + } else { // in Rhino or a web browser + root.he = he; + } + +}(this)); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],55:[function(require,module,exports){ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = ((value * c) - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],56:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],57:[function(require,module,exports){ +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +// The _isBuffer check is for Safari 5-7 support, because it's missing +// Object.prototype.constructor. Remove this eventually +module.exports = function (obj) { + return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +} + +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} + +// For Node v0.10 support. Remove this eventually. +function isSlowBuffer (obj) { + return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) +} + +},{}],58:[function(require,module,exports){ +var toString = {}.toString; + +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; + +},{}],59:[function(require,module,exports){ +(function (process){ +var path = require('path'); +var fs = require('fs'); +var _0777 = parseInt('0777', 8); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + p = path.resolve(p); + + try { + xfs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; + +}).call(this,require('_process')) +},{"_process":69,"fs":42,"path":42}],60:[function(require,module,exports){ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var w = d * 7; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^((?:\d+)?\-?\d?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'weeks': + case 'week': + case 'w': + return n * w; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return Math.round(ms / d) + 'd'; + } + if (msAbs >= h) { + return Math.round(ms / h) + 'h'; + } + if (msAbs >= m) { + return Math.round(ms / m) + 'm'; + } + if (msAbs >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day'); + } + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour'); + } + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute'); + } + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second'); + } + return ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); +} + +},{}],61:[function(require,module,exports){ +'use strict'; + +var keysShim; +if (!Object.keys) { + // modified from https://github.com/es-shims/es5-shim + var has = Object.prototype.hasOwnProperty; + var toStr = Object.prototype.toString; + var isArgs = require('./isArguments'); // eslint-disable-line global-require + var isEnumerable = Object.prototype.propertyIsEnumerable; + var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); + var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); + var dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ]; + var equalsConstructorPrototype = function (o) { + var ctor = o.constructor; + return ctor && ctor.prototype === o; + }; + var excludedKeys = { + $applicationCache: true, + $console: true, + $external: true, + $frame: true, + $frameElement: true, + $frames: true, + $innerHeight: true, + $innerWidth: true, + $outerHeight: true, + $outerWidth: true, + $pageXOffset: true, + $pageYOffset: true, + $parent: true, + $scrollLeft: true, + $scrollTop: true, + $scrollX: true, + $scrollY: true, + $self: true, + $webkitIndexedDB: true, + $webkitStorageInfo: true, + $window: true + }; + var hasAutomationEqualityBug = (function () { + /* global window */ + if (typeof window === 'undefined') { return false; } + for (var k in window) { + try { + if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { + try { + equalsConstructorPrototype(window[k]); + } catch (e) { + return true; + } + } + } catch (e) { + return true; + } + } + return false; + }()); + var equalsConstructorPrototypeIfNotBuggy = function (o) { + /* global window */ + if (typeof window === 'undefined' || !hasAutomationEqualityBug) { + return equalsConstructorPrototype(o); + } + try { + return equalsConstructorPrototype(o); + } catch (e) { + return false; + } + }; + + keysShim = function keys(object) { + var isObject = object !== null && typeof object === 'object'; + var isFunction = toStr.call(object) === '[object Function]'; + var isArguments = isArgs(object); + var isString = isObject && toStr.call(object) === '[object String]'; + var theKeys = []; + + if (!isObject && !isFunction && !isArguments) { + throw new TypeError('Object.keys called on a non-object'); + } + + var skipProto = hasProtoEnumBug && isFunction; + if (isString && object.length > 0 && !has.call(object, 0)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)); + } + } + + if (isArguments && object.length > 0) { + for (var j = 0; j < object.length; ++j) { + theKeys.push(String(j)); + } + } else { + for (var name in object) { + if (!(skipProto && name === 'prototype') && has.call(object, name)) { + theKeys.push(String(name)); + } + } + } + + if (hasDontEnumBug) { + var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); + + for (var k = 0; k < dontEnums.length; ++k) { + if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { + theKeys.push(dontEnums[k]); + } + } + } + return theKeys; + }; +} +module.exports = keysShim; + +},{"./isArguments":63}],62:[function(require,module,exports){ +'use strict'; + +var slice = Array.prototype.slice; +var isArgs = require('./isArguments'); + +var origKeys = Object.keys; +var keysShim = origKeys ? function keys(o) { return origKeys(o); } : require('./implementation'); + +var originalKeys = Object.keys; + +keysShim.shim = function shimObjectKeys() { + if (Object.keys) { + var keysWorksWithArguments = (function () { + // Safari 5.0 bug + var args = Object.keys(arguments); + return args && args.length === arguments.length; + }(1, 2)); + if (!keysWorksWithArguments) { + Object.keys = function keys(object) { // eslint-disable-line func-name-matching + if (isArgs(object)) { + return originalKeys(slice.call(object)); + } + return originalKeys(object); + }; + } + } else { + Object.keys = keysShim; + } + return Object.keys || keysShim; +}; + +module.exports = keysShim; + +},{"./implementation":61,"./isArguments":63}],63:[function(require,module,exports){ +'use strict'; + +var toStr = Object.prototype.toString; + +module.exports = function isArguments(value) { + var str = toStr.call(value); + var isArgs = str === '[object Arguments]'; + if (!isArgs) { + isArgs = str !== '[object Array]' && + value !== null && + typeof value === 'object' && + typeof value.length === 'number' && + value.length >= 0 && + toStr.call(value.callee) === '[object Function]'; + } + return isArgs; +}; + +},{}],64:[function(require,module,exports){ +'use strict'; + +// modified from https://github.com/es-shims/es6-shim +var keys = require('object-keys'); +var bind = require('function-bind'); +var canBeObject = function (obj) { + return typeof obj !== 'undefined' && obj !== null; +}; +var hasSymbols = require('has-symbols/shams')(); +var toObject = Object; +var push = bind.call(Function.call, Array.prototype.push); +var propIsEnumerable = bind.call(Function.call, Object.prototype.propertyIsEnumerable); +var originalGetSymbols = hasSymbols ? Object.getOwnPropertySymbols : null; + +module.exports = function assign(target, source1) { + if (!canBeObject(target)) { throw new TypeError('target must be an object'); } + var objTarget = toObject(target); + var s, source, i, props, syms, value, key; + for (s = 1; s < arguments.length; ++s) { + source = toObject(arguments[s]); + props = keys(source); + var getSymbols = hasSymbols && (Object.getOwnPropertySymbols || originalGetSymbols); + if (getSymbols) { + syms = getSymbols(source); + for (i = 0; i < syms.length; ++i) { + key = syms[i]; + if (propIsEnumerable(source, key)) { + push(props, key); + } + } + } + for (i = 0; i < props.length; ++i) { + key = props[i]; + value = source[key]; + if (propIsEnumerable(source, key)) { + objTarget[key] = value; + } + } + } + return objTarget; +}; + +},{"function-bind":52,"has-symbols/shams":53,"object-keys":62}],65:[function(require,module,exports){ +'use strict'; + +var defineProperties = require('define-properties'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = getPolyfill(); + +defineProperties(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; + +},{"./implementation":64,"./polyfill":66,"./shim":67,"define-properties":47}],66:[function(require,module,exports){ +'use strict'; + +var implementation = require('./implementation'); + +var lacksProperEnumerationOrder = function () { + if (!Object.assign) { + return false; + } + // v8, specifically in node 4.x, has a bug with incorrect property enumeration order + // note: this does not detect the bug unless there's 20 characters + var str = 'abcdefghijklmnopqrst'; + var letters = str.split(''); + var map = {}; + for (var i = 0; i < letters.length; ++i) { + map[letters[i]] = letters[i]; + } + var obj = Object.assign({}, map); + var actual = ''; + for (var k in obj) { + actual += k; + } + return str !== actual; +}; + +var assignHasPendingExceptions = function () { + if (!Object.assign || !Object.preventExtensions) { + return false; + } + // Firefox 37 still has "pending exception" logic in its Object.assign implementation, + // which is 72% slower than our shim, and Firefox 40's native implementation. + var thrower = Object.preventExtensions({ 1: 2 }); + try { + Object.assign(thrower, 'xy'); + } catch (e) { + return thrower[1] === 'y'; + } + return false; +}; + +module.exports = function getPolyfill() { + if (!Object.assign) { + return implementation; + } + if (lacksProperEnumerationOrder()) { + return implementation; + } + if (assignHasPendingExceptions()) { + return implementation; + } + return Object.assign; +}; + +},{"./implementation":64}],67:[function(require,module,exports){ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +module.exports = function shimAssign() { + var polyfill = getPolyfill(); + define( + Object, + { assign: polyfill }, + { assign: function () { return Object.assign !== polyfill; } } + ); + return polyfill; +}; + +},{"./polyfill":66,"define-properties":47}],68:[function(require,module,exports){ +(function (process){ +'use strict'; + +if (!process.version || + process.version.indexOf('v0.') === 0 || + process.version.indexOf('v1.') === 0 && process.version.indexOf('v1.8.') !== 0) { + module.exports = { nextTick: nextTick }; +} else { + module.exports = process +} + +function nextTick(fn, arg1, arg2, arg3) { + if (typeof fn !== 'function') { + throw new TypeError('"callback" argument must be a function'); + } + var len = arguments.length; + var args, i; + switch (len) { + case 0: + case 1: + return process.nextTick(fn); + case 2: + return process.nextTick(function afterTickOne() { + fn.call(null, arg1); + }); + case 3: + return process.nextTick(function afterTickTwo() { + fn.call(null, arg1, arg2); + }); + case 4: + return process.nextTick(function afterTickThree() { + fn.call(null, arg1, arg2, arg3); + }); + default: + args = new Array(len - 1); + i = 0; + while (i < args.length) { + args[i++] = arguments[i]; + } + return process.nextTick(function afterTick() { + fn.apply(null, args); + }); + } +} + + +}).call(this,require('_process')) +},{"_process":69}],69:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],70:[function(require,module,exports){ +module.exports = require('./lib/_stream_duplex.js'); + +},{"./lib/_stream_duplex.js":71}],71:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. + +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +/**/ +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + keys.push(key); + }return keys; +}; +/**/ + +module.exports = Duplex; + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +var Readable = require('./_stream_readable'); +var Writable = require('./_stream_writable'); + +util.inherits(Duplex, Readable); + +{ + // avoid scope creep, the keys array can then be collected + var keys = objectKeys(Writable.prototype); + for (var v = 0; v < keys.length; v++) { + var method = keys[v]; + if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; + } +} + +function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); + + Readable.call(this, options); + Writable.call(this, options); + + if (options && options.readable === false) this.readable = false; + + if (options && options.writable === false) this.writable = false; + + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) this.allowHalfOpen = false; + + this.once('end', onend); +} + +Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._writableState.highWaterMark; + } +}); + +// the no-half-open enforcer +function onend() { + // if we allow half-open state, or if the writable side ended, + // then we're ok. + if (this.allowHalfOpen || this._writableState.ended) return; + + // no more data can be written. + // But allow more writes to happen in this tick. + pna.nextTick(onEndNT, this); +} + +function onEndNT(self) { + self.end(); +} + +Object.defineProperty(Duplex.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined || this._writableState === undefined) { + return false; + } + return this._readableState.destroyed && this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (this._readableState === undefined || this._writableState === undefined) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + this._writableState.destroyed = value; + } +}); + +Duplex.prototype._destroy = function (err, cb) { + this.push(null); + this.end(); + + pna.nextTick(cb, err); +}; +},{"./_stream_readable":73,"./_stream_writable":75,"core-util-is":44,"inherits":56,"process-nextick-args":68}],72:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. + +'use strict'; + +module.exports = PassThrough; + +var Transform = require('./_stream_transform'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); + + Transform.call(this, options); +} + +PassThrough.prototype._transform = function (chunk, encoding, cb) { + cb(null, chunk); +}; +},{"./_stream_transform":74,"core-util-is":44,"inherits":56}],73:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +module.exports = Readable; + +/**/ +var isArray = require('isarray'); +/**/ + +/**/ +var Duplex; +/**/ + +Readable.ReadableState = ReadableState; + +/**/ +var EE = require('events').EventEmitter; + +var EElistenerCount = function (emitter, type) { + return emitter.listeners(type).length; +}; +/**/ + +/**/ +var Stream = require('./internal/streams/stream'); +/**/ + +/**/ + +var Buffer = require('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} + +/**/ + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +/**/ +var debugUtil = require('util'); +var debug = void 0; +if (debugUtil && debugUtil.debuglog) { + debug = debugUtil.debuglog('stream'); +} else { + debug = function () {}; +} +/**/ + +var BufferList = require('./internal/streams/BufferList'); +var destroyImpl = require('./internal/streams/destroy'); +var StringDecoder; + +util.inherits(Readable, Stream); + +var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + +function prependListener(emitter, event, fn) { + // Sadly this is not cacheable as some libraries bundle their own + // event emitter implementation with them. + if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); + + // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; +} + +function ReadableState(options, stream) { + Duplex = Duplex || require('./_stream_duplex'); + + options = options || {}; + + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + var isDuplex = stream instanceof Duplex; + + // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + this.objectMode = !!options.objectMode; + + if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; + + // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + var hwm = options.highWaterMark; + var readableHwm = options.readableHighWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; + + if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (readableHwm || readableHwm === 0)) this.highWaterMark = readableHwm;else this.highWaterMark = defaultHwm; + + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); + + // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift() + this.buffer = new BufferList(); + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; + + // a flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + this.sync = true; + + // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + this.resumeScheduled = false; + + // has it been destroyed + this.destroyed = false; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; + + // if true, a maybeReadMore has been scheduled + this.readingMore = false; + + this.decoder = null; + this.encoding = null; + if (options.encoding) { + if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + Duplex = Duplex || require('./_stream_duplex'); + + if (!(this instanceof Readable)) return new Readable(options); + + this._readableState = new ReadableState(options, this); + + // legacy + this.readable = true; + + if (options) { + if (typeof options.read === 'function') this._read = options.read; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + } + + Stream.call(this); +} + +Object.defineProperty(Readable.prototype, 'destroyed', { + get: function () { + if (this._readableState === undefined) { + return false; + } + return this._readableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._readableState.destroyed = value; + } +}); + +Readable.prototype.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; +Readable.prototype._destroy = function (err, cb) { + this.push(null); + cb(err); +}; + +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function (chunk, encoding) { + var state = this._readableState; + var skipChunkCheck; + + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + skipChunkCheck = true; + } + } else { + skipChunkCheck = true; + } + + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); +}; + +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function (chunk) { + return readableAddChunk(this, chunk, null, true, false); +}; + +function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { + var state = stream._readableState; + if (chunk === null) { + state.reading = false; + onEofChunk(stream, state); + } else { + var er; + if (!skipChunkCheck) er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (addToFront) { + if (state.endEmitted) stream.emit('error', new Error('stream.unshift() after end event'));else addChunk(stream, state, chunk, true); + } else if (state.ended) { + stream.emit('error', new Error('stream.push() after EOF')); + } else { + state.reading = false; + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); + } else { + addChunk(stream, state, chunk, false); + } + } + } else if (!addToFront) { + state.reading = false; + } + } + + return needMoreData(state); +} + +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); + + if (state.needReadable) emitReadable(stream); + } + maybeReadMore(stream, state); +} + +function chunkInvalid(state, chunk) { + var er; + if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} + +// if it's past the high water mark, we can push in some more. +// Also, if we have no data yet, we can stand some +// more bytes. This is to work around cases where hwm=0, +// such as the repl. Also, if the push() triggered a +// readable event, and the user called read(largeNumber) such that +// needReadable was set, then we ought to push more, so that another +// 'readable' event will be triggered. +function needMoreData(state) { + return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0); +} + +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; +}; + +// backwards compatibility. +Readable.prototype.setEncoding = function (enc) { + if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; + this._readableState.decoder = new StringDecoder(enc); + this._readableState.encoding = enc; + return this; +}; + +// Don't raise the hwm > 8MB +var MAX_HWM = 0x800000; +function computeNewHighWaterMark(n) { + if (n >= MAX_HWM) { + n = MAX_HWM; + } else { + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n++; + } + return n; +} + +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function howMuchToRead(n, state) { + if (n <= 0 || state.length === 0 && state.ended) return 0; + if (state.objectMode) return 1; + if (n !== n) { + // Only flow one buffer at a time + if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; + } + // If we're asking for more than the current hwm, then raise the hwm. + if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); + if (n <= state.length) return n; + // Don't have enough + if (!state.ended) { + state.needReadable = true; + return 0; + } + return state.length; +} + +// you can override either this method, or the async _read(n) below. +Readable.prototype.read = function (n) { + debug('read', n); + n = parseInt(n, 10); + var state = this._readableState; + var nOrig = n; + + if (n !== 0) state.emittedReadable = false; + + // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); + + // if we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) endReadable(this); + return null; + } + + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); + + // if we currently have less than the highWaterMark, then also read some + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } + + // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } else if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; + // if the length is currently zero, then we *need* a readable event. + if (state.length === 0) state.needReadable = true; + // call internal read method + this._read(state.highWaterMark); + state.sync = false; + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (!state.reading) n = howMuchToRead(nOrig, state); + } + + var ret; + if (n > 0) ret = fromList(n, state);else ret = null; + + if (ret === null) { + state.needReadable = true; + n = 0; + } else { + state.length -= n; + } + + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) state.needReadable = true; + + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended) endReadable(this); + } + + if (ret !== null) this.emit('data', ret); + + return ret; +}; + +function onEofChunk(stream, state) { + if (state.ended) return; + if (state.decoder) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + state.ended = true; + + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); +} + +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + var state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + if (state.sync) pna.nextTick(emitReadable_, stream);else emitReadable_(stream); + } +} + +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); +} + +// at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + pna.nextTick(maybeReadMore_, stream, state); + } +} + +function maybeReadMore_(stream, state) { + var len = state.length; + while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) { + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) + // didn't get any data, stop spinning. + break;else len = state.length; + } + state.readingMore = false; +} + +// abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function (n) { + this.emit('error', new Error('_read() is not implemented')); +}; + +Readable.prototype.pipe = function (dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + case 1: + state.pipes = [state.pipes, dest]; + break; + default: + state.pipes.push(dest); + break; + } + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + + var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; + + var endFn = doEnd ? onend : unpipe; + if (state.endEmitted) pna.nextTick(endFn);else src.once('end', endFn); + + dest.on('unpipe', onunpipe); + function onunpipe(readable, unpipeInfo) { + debug('onunpipe'); + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } + } + } + + function onend() { + debug('onend'); + dest.end(); + } + + // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + + var cleanedUp = false; + function cleanup() { + debug('cleanup'); + // cleanup event handlers once the pipe is broken + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', unpipe); + src.removeListener('data', ondata); + + cleanedUp = true; + + // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); + } + + // If the user pushes more data while we're writing to dest then we'll end up + // in ondata again. However, we only want to increase awaitDrain once because + // dest will only emit one 'drain' event for the multiple writes. + // => Introduce a guard on increasing awaitDrain. + var increasedAwaitDrain = false; + src.on('data', ondata); + function ondata(chunk) { + debug('ondata'); + increasedAwaitDrain = false; + var ret = dest.write(chunk); + if (false === ret && !increasedAwaitDrain) { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { + debug('false write response, pause', src._readableState.awaitDrain); + src._readableState.awaitDrain++; + increasedAwaitDrain = true; + } + src.pause(); + } + } + + // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er); + } + + // Make sure our error handler is attached before userland ones. + prependListener(dest, 'error', onerror); + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + dest.once('close', onclose); + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } + + // tell the dest that it's being piped to + dest.emit('pipe', src); + + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function () { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) state.awaitDrain--; + if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + +Readable.prototype.unpipe = function (dest) { + var state = this._readableState; + var unpipeInfo = { hasUnpiped: false }; + + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) return this; + + // just one destination. most common case. + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) return this; + + if (!dest) dest = state.pipes; + + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) dest.emit('unpipe', this, unpipeInfo); + return this; + } + + // slow case. multiple pipe destinations. + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) { + dests[i].emit('unpipe', this, unpipeInfo); + }return this; + } + + // try to find the right one. + var index = indexOf(state.pipes, dest); + if (index === -1) return this; + + state.pipes.splice(index, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) state.pipes = state.pipes[0]; + + dest.emit('unpipe', this, unpipeInfo); + + return this; +}; + +// set up data events if they are asked for +// Ensure readable listeners eventually get something +Readable.prototype.on = function (ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + + if (ev === 'data') { + // Start flowing on next tick if stream isn't explicitly paused + if (this._readableState.flowing !== false) this.resume(); + } else if (ev === 'readable') { + var state = this._readableState; + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; + state.emittedReadable = false; + if (!state.reading) { + pna.nextTick(nReadingNextTick, this); + } else if (state.length) { + emitReadable(this); + } + } + } + + return res; +}; +Readable.prototype.addListener = Readable.prototype.on; + +function nReadingNextTick(self) { + debug('readable nexttick read 0'); + self.read(0); +} + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function () { + var state = this._readableState; + if (!state.flowing) { + debug('resume'); + state.flowing = true; + resume(this, state); + } + return this; +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + pna.nextTick(resume_, stream, state); + } +} + +function resume_(stream, state) { + if (!state.reading) { + debug('resume read 0'); + stream.read(0); + } + + state.resumeScheduled = false; + state.awaitDrain = 0; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} + +Readable.prototype.pause = function () { + debug('call pause flowing=%j', this._readableState.flowing); + if (false !== this._readableState.flowing) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } + return this; +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + while (state.flowing && stream.read() !== null) {} +} + +// wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function (stream) { + var _this = this; + + var state = this._readableState; + var paused = false; + + stream.on('end', function () { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) _this.push(chunk); + } + + _this.push(null); + }); + + stream.on('data', function (chunk) { + debug('wrapped data'); + if (state.decoder) chunk = state.decoder.write(chunk); + + // don't skip over falsy values in objectMode + if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; + + var ret = _this.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } + }); + + // proxy all the other methods. + // important when wrapping filters and duplexes. + for (var i in stream) { + if (this[i] === undefined && typeof stream[i] === 'function') { + this[i] = function (method) { + return function () { + return stream[method].apply(stream, arguments); + }; + }(i); + } + } + + // proxy certain important events. + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); + } + + // when we try to consume some more bytes, simply unpause the + // underlying stream. + this._read = function (n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); + } + }; + + return this; +}; + +Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._readableState.highWaterMark; + } +}); + +// exposed for testing purposes only. +Readable._fromList = fromList; + +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromList(n, state) { + // nothing buffered + if (state.length === 0) return null; + + var ret; + if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { + // read it all, truncate the list + if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.head.data;else ret = state.buffer.concat(state.length); + state.buffer.clear(); + } else { + // read part of list + ret = fromListPartial(n, state.buffer, state.decoder); + } + + return ret; +} + +// Extracts only enough buffered data to satisfy the amount requested. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromListPartial(n, list, hasStrings) { + var ret; + if (n < list.head.data.length) { + // slice is the same for buffers and strings + ret = list.head.data.slice(0, n); + list.head.data = list.head.data.slice(n); + } else if (n === list.head.data.length) { + // first chunk is a perfect match + ret = list.shift(); + } else { + // result spans more than one buffer + ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); + } + return ret; +} + +// Copies a specified amount of characters from the list of buffered data +// chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBufferString(n, list) { + var p = list.head; + var c = 1; + var ret = p.data; + n -= ret.length; + while (p = p.next) { + var str = p.data; + var nb = n > str.length ? str.length : n; + if (nb === str.length) ret += str;else ret += str.slice(0, n); + n -= nb; + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = str.slice(nb); + } + break; + } + ++c; + } + list.length -= c; + return ret; +} + +// Copies a specified amount of bytes from the list of buffered data chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBuffer(n, list) { + var ret = Buffer.allocUnsafe(n); + var p = list.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + while (p = p.next) { + var buf = p.data; + var nb = n > buf.length ? buf.length : n; + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) list.head = p.next;else list.head = list.tail = null; + } else { + list.head = p; + p.data = buf.slice(nb); + } + break; + } + ++c; + } + list.length -= c; + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) throw new Error('"endReadable()" called on non-empty stream'); + + if (!state.endEmitted) { + state.ended = true; + pna.nextTick(endReadableNT, state, stream); + } +} + +function endReadableNT(state, stream) { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + } +} + +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + return -1; +} +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./_stream_duplex":71,"./internal/streams/BufferList":76,"./internal/streams/destroy":77,"./internal/streams/stream":78,"_process":69,"core-util-is":44,"events":50,"inherits":56,"isarray":58,"process-nextick-args":68,"safe-buffer":83,"string_decoder/":85,"util":40}],74:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. + +'use strict'; + +module.exports = Transform; + +var Duplex = require('./_stream_duplex'); + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +util.inherits(Transform, Duplex); + +function afterTransform(er, data) { + var ts = this._transformState; + ts.transforming = false; + + var cb = ts.writecb; + + if (!cb) { + return this.emit('error', new Error('write callback called multiple times')); + } + + ts.writechunk = null; + ts.writecb = null; + + if (data != null) // single equals check for both `null` and `undefined` + this.push(data); + + cb(er); + + var rs = this._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + this._read(rs.highWaterMark); + } +} + +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); + + Duplex.call(this, options); + + this._transformState = { + afterTransform: afterTransform.bind(this), + needTransform: false, + transforming: false, + writecb: null, + writechunk: null, + writeencoding: null + }; + + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; + + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; + + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; + + if (typeof options.flush === 'function') this._flush = options.flush; + } + + // When the writable side finishes, then flush out anything remaining. + this.on('prefinish', prefinish); +} + +function prefinish() { + var _this = this; + + if (typeof this._flush === 'function') { + this._flush(function (er, data) { + done(_this, er, data); + }); + } else { + done(this, null, null); + } +} + +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; + +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function (chunk, encoding, cb) { + throw new Error('_transform() is not implemented'); +}; + +Transform.prototype._write = function (chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); + } +}; + +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function (n) { + var ts = this._transformState; + + if (ts.writechunk !== null && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + +Transform.prototype._destroy = function (err, cb) { + var _this2 = this; + + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + _this2.emit('close'); + }); +}; + +function done(stream, er, data) { + if (er) return stream.emit('error', er); + + if (data != null) // single equals check for both `null` and `undefined` + stream.push(data); + + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + if (stream._writableState.length) throw new Error('Calling transform done when ws.length != 0'); + + if (stream._transformState.transforming) throw new Error('Calling transform done when still transforming'); + + return stream.push(null); +} +},{"./_stream_duplex":71,"core-util-is":44,"inherits":56}],75:[function(require,module,exports){ +(function (process,global,setImmediate){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. + +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +module.exports = Writable; + +/* */ +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} + +// It seems a linked list but it is not +// there will be only 2 of these for each stream +function CorkedRequest(state) { + var _this = this; + + this.next = null; + this.entry = null; + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ + +/**/ +var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : pna.nextTick; +/**/ + +/**/ +var Duplex; +/**/ + +Writable.WritableState = WritableState; + +/**/ +var util = require('core-util-is'); +util.inherits = require('inherits'); +/**/ + +/**/ +var internalUtil = { + deprecate: require('util-deprecate') +}; +/**/ + +/**/ +var Stream = require('./internal/streams/stream'); +/**/ + +/**/ + +var Buffer = require('safe-buffer').Buffer; +var OurUint8Array = global.Uint8Array || function () {}; +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} + +/**/ + +var destroyImpl = require('./internal/streams/destroy'); + +util.inherits(Writable, Stream); + +function nop() {} + +function WritableState(options, stream) { + Duplex = Duplex || require('./_stream_duplex'); + + options = options || {}; + + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + var isDuplex = stream instanceof Duplex; + + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; + + if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; + + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + var writableHwm = options.writableHighWaterMark; + var defaultHwm = this.objectMode ? 16 : 16 * 1024; + + if (hwm || hwm === 0) this.highWaterMark = hwm;else if (isDuplex && (writableHwm || writableHwm === 0)) this.highWaterMark = writableHwm;else this.highWaterMark = defaultHwm; + + // cast to ints. + this.highWaterMark = Math.floor(this.highWaterMark); + + // if _final has been called + this.finalCalled = false; + + // drain event flag. + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; + + // has it been destroyed + this.destroyed = false; + + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // a flag to see when we're in the middle of a write. + this.writing = false; + + // when true all writes will be buffered until .uncork() call + this.corked = 0; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function (er) { + onwrite(stream, er); + }; + + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; + + // the amount that is being written when _write is called. + this.writelen = 0; + + this.bufferedRequest = null; + this.lastBufferedRequest = null; + + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; + + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; + + // True if the error was already emitted and should not be thrown again + this.errorEmitted = false; + + // count buffered requests + this.bufferedRequestCount = 0; + + // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + this.corkedRequestsFree = new CorkedRequest(this); +} + +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; + } + return out; +}; + +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function () { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); + +// Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. +var realHasInstance; +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function (object) { + if (realHasInstance.call(this, object)) return true; + if (this !== Writable) return false; + + return object && object._writableState instanceof WritableState; + } + }); +} else { + realHasInstance = function (object) { + return object instanceof this; + }; +} + +function Writable(options) { + Duplex = Duplex || require('./_stream_duplex'); + + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + if (!realHasInstance.call(Writable, this) && !(this instanceof Duplex)) { + return new Writable(options); + } + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + if (options) { + if (typeof options.write === 'function') this._write = options.write; + + if (typeof options.writev === 'function') this._writev = options.writev; + + if (typeof options.destroy === 'function') this._destroy = options.destroy; + + if (typeof options.final === 'function') this._final = options.final; + } + + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function () { + this.emit('error', new Error('Cannot pipe, not readable')); +}; + +function writeAfterEnd(stream, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + pna.nextTick(cb, er); +} + +// Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. +function validChunk(stream, state, chunk, cb) { + var valid = true; + var er = false; + + if (chunk === null) { + er = new TypeError('May not write null values to stream'); + } else if (typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + if (er) { + stream.emit('error', er); + pna.nextTick(cb, er); + valid = false; + } + return valid; +} + +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + var isBuf = !state.objectMode && _isUint8Array(chunk); + + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; + + if (typeof cb !== 'function') cb = nop; + + if (state.ended) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } + + return ret; +}; + +Writable.prototype.cork = function () { + var state = this._writableState; + + state.corked++; +}; + +Writable.prototype.uncork = function () { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); + } +}; + +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); + } + return chunk; +} + +Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function () { + return this._writableState.highWaterMark; + } +}); + +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; + } + } + var len = state.objectMode ? 1 : chunk.length; + + state.length += len; + + var ret = state.length < state.highWaterMark; + // we must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; + + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); + } + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; + + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + pna.nextTick(cb, er); + // this can emit finish, and it will always happen + // after error + pna.nextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + stream.emit('error', er); + // this can emit finish, but finish must + // always follow error + finishMaybe(stream, state); + } +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + + onwriteStateUpdate(state); + + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state); + + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } + + if (sync) { + /**/ + asyncWrite(afterWrite, stream, state, finished, cb); + /**/ + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} + +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} + +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; + + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; + + var count = 0; + var allBuffers = true; + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; + } + buffer.allBuffers = allBuffers; + + doWrite(stream, state, true, state.length, buffer, '', holder.finish); + + // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + state.pendingcb++; + state.lastBufferedRequest = null; + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); + } + state.bufferedRequestCount = 0; + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + state.bufferedRequestCount--; + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + break; + } + } + + if (entry === null) state.lastBufferedRequest = null; + } + + state.bufferedRequest = entry; + state.bufferProcessing = false; +} + +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new Error('_write() is not implemented')); +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; + + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); + } + + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) endWritable(this, state, cb); +}; + +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + if (err) { + stream.emit('error', err); + } + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function') { + state.pendingcb++; + state.finalCalled = true; + pna.nextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} + +function finishMaybe(stream, state) { + var need = needFinish(state); + if (need) { + prefinish(stream, state); + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + } + } + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) pna.nextTick(cb);else stream.once('finish', cb); + } + state.ended = true; + stream.writable = false; +} + +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; + } + if (state.corkedRequestsFree) { + state.corkedRequestsFree.next = corkReq; + } else { + state.corkedRequestsFree = corkReq; + } +} + +Object.defineProperty(Writable.prototype, 'destroyed', { + get: function () { + if (this._writableState === undefined) { + return false; + } + return this._writableState.destroyed; + }, + set: function (value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; + } + + // backward compatibility, the user is explicitly + // managing destroyed + this._writableState.destroyed = value; + } +}); + +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; +Writable.prototype._destroy = function (err, cb) { + this.end(); + cb(err); +}; +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate) +},{"./_stream_duplex":71,"./internal/streams/destroy":77,"./internal/streams/stream":78,"_process":69,"core-util-is":44,"inherits":56,"process-nextick-args":68,"safe-buffer":83,"timers":86,"util-deprecate":87}],76:[function(require,module,exports){ +'use strict'; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Buffer = require('safe-buffer').Buffer; +var util = require('util'); + +function copyBuffer(src, target, offset) { + src.copy(target, offset); +} + +module.exports = function () { + function BufferList() { + _classCallCheck(this, BufferList); + + this.head = null; + this.tail = null; + this.length = 0; + } + + BufferList.prototype.push = function push(v) { + var entry = { data: v, next: null }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + }; + + BufferList.prototype.unshift = function unshift(v) { + var entry = { data: v, next: this.head }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + }; + + BufferList.prototype.shift = function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + }; + + BufferList.prototype.clear = function clear() { + this.head = this.tail = null; + this.length = 0; + }; + + BufferList.prototype.join = function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) { + ret += s + p.data; + }return ret; + }; + + BufferList.prototype.concat = function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + if (this.length === 1) return this.head.data; + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; + } + return ret; + }; + + return BufferList; +}(); + +if (util && util.inspect && util.inspect.custom) { + module.exports.prototype[util.inspect.custom] = function () { + var obj = util.inspect({ length: this.length }); + return this.constructor.name + ' ' + obj; + }; +} +},{"safe-buffer":83,"util":40}],77:[function(require,module,exports){ +'use strict'; + +/**/ + +var pna = require('process-nextick-args'); +/**/ + +// undocumented cb() API, needed for core, not for public API +function destroy(err, cb) { + var _this = this; + + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; + + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err && (!this._writableState || !this._writableState.errorEmitted)) { + pna.nextTick(emitErrorNT, this, err); + } + return this; + } + + // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + + if (this._readableState) { + this._readableState.destroyed = true; + } + + // if this is a duplex stream mark the writable part as destroyed as well + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + pna.nextTick(emitErrorNT, _this, err); + if (_this._writableState) { + _this._writableState.errorEmitted = true; + } + } else if (cb) { + cb(err); + } + }); + + return this; +} + +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } + + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; + } +} + +function emitErrorNT(self, err) { + self.emit('error', err); +} + +module.exports = { + destroy: destroy, + undestroy: undestroy +}; +},{"process-nextick-args":68}],78:[function(require,module,exports){ +module.exports = require('events').EventEmitter; + +},{"events":50}],79:[function(require,module,exports){ +module.exports = require('./readable').PassThrough + +},{"./readable":80}],80:[function(require,module,exports){ +exports = module.exports = require('./lib/_stream_readable.js'); +exports.Stream = exports; +exports.Readable = exports; +exports.Writable = require('./lib/_stream_writable.js'); +exports.Duplex = require('./lib/_stream_duplex.js'); +exports.Transform = require('./lib/_stream_transform.js'); +exports.PassThrough = require('./lib/_stream_passthrough.js'); + +},{"./lib/_stream_duplex.js":71,"./lib/_stream_passthrough.js":72,"./lib/_stream_readable.js":73,"./lib/_stream_transform.js":74,"./lib/_stream_writable.js":75}],81:[function(require,module,exports){ +module.exports = require('./readable').Transform + +},{"./readable":80}],82:[function(require,module,exports){ +module.exports = require('./lib/_stream_writable.js'); + +},{"./lib/_stream_writable.js":75}],83:[function(require,module,exports){ +/* eslint-disable node/no-deprecated-api */ +var buffer = require('buffer') +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} + +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} + +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf +} + +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} + +},{"buffer":43}],84:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = Stream; + +var EE = require('events').EventEmitter; +var inherits = require('inherits'); + +inherits(Stream, EE); +Stream.Readable = require('readable-stream/readable.js'); +Stream.Writable = require('readable-stream/writable.js'); +Stream.Duplex = require('readable-stream/duplex.js'); +Stream.Transform = require('readable-stream/transform.js'); +Stream.PassThrough = require('readable-stream/passthrough.js'); + +// Backwards-compat with node 0.4.x +Stream.Stream = Stream; + + + +// old-style streams. Note that the pipe method (the only relevant +// part of this class) is overridden in the Readable class. + +function Stream() { + EE.call(this); +} + +Stream.prototype.pipe = function(dest, options) { + var source = this; + + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } + } + } + + source.on('data', ondata); + + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + + dest.on('drain', ondrain); + + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on('end', onend); + source.on('close', onclose); + } + + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + + dest.end(); + } + + + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + + if (typeof dest.destroy === 'function') dest.destroy(); + } + + // don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EE.listenerCount(this, 'error') === 0) { + throw er; // Unhandled stream error in pipe. + } + } + + source.on('error', onerror); + dest.on('error', onerror); + + // remove all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); + + source.removeListener('end', onend); + source.removeListener('close', onclose); + + source.removeListener('error', onerror); + dest.removeListener('error', onerror); + + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); + + dest.removeListener('close', cleanup); + } + + source.on('end', cleanup); + source.on('close', cleanup); + + dest.on('close', cleanup); + + dest.emit('pipe', source); + + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; + +},{"events":50,"inherits":56,"readable-stream/duplex.js":70,"readable-stream/passthrough.js":79,"readable-stream/readable.js":80,"readable-stream/transform.js":81,"readable-stream/writable.js":82}],85:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +/**/ + +var Buffer = require('safe-buffer').Buffer; +/**/ + +var isEncoding = Buffer.isEncoding || function (encoding) { + encoding = '' + encoding; + switch (encoding && encoding.toLowerCase()) { + case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': + return true; + default: + return false; + } +}; + +function _normalizeEncoding(enc) { + if (!enc) return 'utf8'; + var retried; + while (true) { + switch (enc) { + case 'utf8': + case 'utf-8': + return 'utf8'; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return 'utf16le'; + case 'latin1': + case 'binary': + return 'latin1'; + case 'base64': + case 'ascii': + case 'hex': + return enc; + default: + if (retried) return; // undefined + enc = ('' + enc).toLowerCase(); + retried = true; + } + } +}; + +// Do not cache `Buffer.isEncoding` when checking encoding names as some +// modules monkey-patch it to support additional encodings +function normalizeEncoding(enc) { + var nenc = _normalizeEncoding(enc); + if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); + return nenc || enc; +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. +exports.StringDecoder = StringDecoder; +function StringDecoder(encoding) { + this.encoding = normalizeEncoding(encoding); + var nb; + switch (this.encoding) { + case 'utf16le': + this.text = utf16Text; + this.end = utf16End; + nb = 4; + break; + case 'utf8': + this.fillLast = utf8FillLast; + nb = 4; + break; + case 'base64': + this.text = base64Text; + this.end = base64End; + nb = 3; + break; + default: + this.write = simpleWrite; + this.end = simpleEnd; + return; + } + this.lastNeed = 0; + this.lastTotal = 0; + this.lastChar = Buffer.allocUnsafe(nb); +} + +StringDecoder.prototype.write = function (buf) { + if (buf.length === 0) return ''; + var r; + var i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ''; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ''; +}; + +StringDecoder.prototype.end = utf8End; + +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; + +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer +StringDecoder.prototype.fillLast = function (buf) { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +}; + +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. If an invalid byte is detected, -2 is returned. +function utf8CheckByte(byte) { + if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} + +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. +function utf8CheckIncomplete(self, buf, i) { + var j = buf.length - 1; + if (j < i) return 0; + var nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0;else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'; + } + } + } +} + +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + var p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a +// partial character, the character's bytes are buffered until the required +// number of bytes are available. +function utf8Text(buf, i) { + var total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString('utf8', i); + this.lastTotal = total; + var end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString('utf8', i, end); +} + +// For UTF-8, a replacement character is added when ending on a partial +// character. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'; + return r; +} + +// UTF-16LE typically needs two bytes per character, but even if we have an even +// number of bytes available, we need to check if we end on a leading/high +// surrogate. In that case, we need to wait for the next two bytes in order to +// decode the last character properly. +function utf16Text(buf, i) { + if ((buf.length - i) % 2 === 0) { + var r = buf.toString('utf16le', i); + if (r) { + var c = r.charCodeAt(r.length - 1); + if (c >= 0xD800 && c <= 0xDBFF) { + this.lastNeed = 2; + this.lastTotal = 4; + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + return r.slice(0, -1); + } + } + return r; + } + this.lastNeed = 1; + this.lastTotal = 2; + this.lastChar[0] = buf[buf.length - 1]; + return buf.toString('utf16le', i, buf.length - 1); +} + +// For UTF-16LE we do not explicitly append special replacement characters if we +// end on a partial character, we simply let v8 handle that. +function utf16End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) { + var end = this.lastTotal - this.lastNeed; + return r + this.lastChar.toString('utf16le', 0, end); + } + return r; +} + +function base64Text(buf, i) { + var n = (buf.length - i) % 3; + if (n === 0) return buf.toString('base64', i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString('base64', i, buf.length - n); +} + +function base64End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + return r; +} + +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); +} + +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; +} +},{"safe-buffer":83}],86:[function(require,module,exports){ +(function (setImmediate,clearImmediate){ +var nextTick = require('process/browser.js').nextTick; +var apply = Function.prototype.apply; +var slice = Array.prototype.slice; +var immediateIds = {}; +var nextImmediateId = 0; + +// DOM APIs, for completeness + +exports.setTimeout = function() { + return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); +}; +exports.setInterval = function() { + return new Timeout(apply.call(setInterval, window, arguments), clearInterval); +}; +exports.clearTimeout = +exports.clearInterval = function(timeout) { timeout.close(); }; + +function Timeout(id, clearFn) { + this._id = id; + this._clearFn = clearFn; +} +Timeout.prototype.unref = Timeout.prototype.ref = function() {}; +Timeout.prototype.close = function() { + this._clearFn.call(window, this._id); +}; + +// Does not start the time, just sets up the members needed. +exports.enroll = function(item, msecs) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = msecs; +}; + +exports.unenroll = function(item) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = -1; +}; + +exports._unrefActive = exports.active = function(item) { + clearTimeout(item._idleTimeoutId); + + var msecs = item._idleTimeout; + if (msecs >= 0) { + item._idleTimeoutId = setTimeout(function onTimeout() { + if (item._onTimeout) + item._onTimeout(); + }, msecs); + } +}; + +// That's not how node.js implements it but the exposed api is the same. +exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { + var id = nextImmediateId++; + var args = arguments.length < 2 ? false : slice.call(arguments, 1); + + immediateIds[id] = true; + + nextTick(function onNextTick() { + if (immediateIds[id]) { + // fn.call() is faster so we optimize for the common use-case + // @see http://jsperf.com/call-apply-segu + if (args) { + fn.apply(null, args); + } else { + fn.call(null); + } + // Prevent ids from leaking + exports.clearImmediate(id); + } + }); + + return id; +}; + +exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { + delete immediateIds[id]; +}; +}).call(this,require("timers").setImmediate,require("timers").clearImmediate) +},{"process/browser.js":69,"timers":86}],87:[function(require,module,exports){ +(function (global){ + +/** + * Module exports. + */ + +module.exports = deprecate; + +/** + * Mark that a method should not be used. + * Returns a modified function which warns once by default. + * + * If `localStorage.noDeprecation = true` is set, then it is a no-op. + * + * If `localStorage.throwDeprecation = true` is set, then deprecated functions + * will throw an Error when invoked. + * + * If `localStorage.traceDeprecation = true` is set, then deprecated functions + * will invoke `console.trace()` instead of `console.error()`. + * + * @param {Function} fn - the function to deprecate + * @param {String} msg - the string to print to the console when `fn` is invoked + * @returns {Function} a new "deprecated" version of `fn` + * @api public + */ + +function deprecate (fn, msg) { + if (config('noDeprecation')) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (config('throwDeprecation')) { + throw new Error(msg); + } else if (config('traceDeprecation')) { + console.trace(msg); + } else { + console.warn(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +} + +/** + * Checks `localStorage` for boolean values for the given `name`. + * + * @param {String} name + * @returns {Boolean} + * @api private + */ + +function config (name) { + // accessing global.localStorage can trigger a DOMException in sandboxed iframes + try { + if (!global.localStorage) return false; + } catch (_) { + return false; + } + var val = global.localStorage[name]; + if (null == val) return false; + return String(val).toLowerCase() === 'true'; +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],88:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],89:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":88,"_process":69,"inherits":56}],90:[function(require,module,exports){ +module.exports={ + "name": "mocha", + "version": "6.1.4", + "homepage": "https://mochajs.org/", + "notifyLogo": "https://ibin.co/4QuRuGjXvl36.png" +} +},{}]},{},[1]); diff --git a/WebServer/node_modules/mocha/package.json b/WebServer/node_modules/mocha/package.json new file mode 100644 index 000000000..ce55e28ba --- /dev/null +++ b/WebServer/node_modules/mocha/package.json @@ -0,0 +1,2052 @@ +{ + "@mocha/contributors": { + "exclude": [ + "greenkeeperio-bot ", + "greenkeeper[bot] ", + "TJ Holowaychuk " + ] + }, + "_args": [ + [ + "mocha@6.1.4", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "mocha@6.1.4", + "_id": "mocha@6.1.4", + "_inBundle": false, + "_integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", + "_location": "/mocha", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "mocha@6.1.4", + "name": "mocha", + "escapedName": "mocha", + "rawSpec": "6.1.4", + "saveSpec": null, + "fetchSpec": "6.1.4" + }, + "_requiredBy": [ + "/@superalgos/mqservice" + ], + "_resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", + "_spec": "6.1.4", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "bin": { + "mocha": "./bin/mocha", + "_mocha": "./bin/_mocha" + }, + "browser": { + "./index.js": "./browser-entry.js", + "./lib/growl.js": "./lib/browser/growl.js", + "tty": "./lib/browser/tty.js", + "fs": false, + "glob": false, + "path": false, + "supports-color": false + }, + "browserify": { + "transform": [ + "./scripts/package-json-cullify" + ] + }, + "bugs": { + "url": "https://github.com/mochajs/mocha/issues/" + }, + "contributors": [ + { + "name": "38elements", + "email": "mh19820223@gmail.com" + }, + { + "name": "Aaron Brady", + "email": "aaron@mori.com" + }, + { + "name": "Aaron Hamid", + "email": "aaron.hamid@gmail.com" + }, + { + "name": "Aaron Heckmann", + "email": "aaron.heckmann+github@gmail.com" + }, + { + "name": "Aaron Krause", + "email": "aaronjkrause@gmail.com" + }, + { + "name": "Aaron Petcoff", + "email": "hello@aaronpetcoff.me" + }, + { + "name": "abrkn", + "email": "a@abrkn.com" + }, + { + "name": "Adam Crabtree", + "email": "adam.crabtree@redrobotlabs.com" + }, + { + "name": "Adam Ginzberg", + "email": "aginzberg@gmail.com" + }, + { + "name": "Adam Gruber", + "email": "talknmime@gmail.com" + }, + { + "name": "Adrian Ludwig", + "email": "me@adrianludwig.pl" + }, + { + "name": "Ahmad Bamieh", + "email": "ahmadbamieh@gmail.com" + }, + { + "name": "airportyh", + "email": "airportyh@gmail.com" + }, + { + "name": "Ajay Kodali", + "email": "ajay.kodali@citrix.com" + }, + { + "name": "Al Scott", + "email": "al.scott@atomicobject.com" + }, + { + "name": "Alex Bainter", + "email": "metalex9@users.noreply.github.com" + }, + { + "name": "Alexander Early", + "email": "alexander.early@gmail.com" + }, + { + "name": "Alexander Shepelin", + "email": "Brightcor@gmail.com" + }, + { + "name": "Alhadis", + "email": "gardnerjohng@gmail.com" + }, + { + "name": "amsul", + "email": "reach@amsul.ca" + }, + { + "name": "Anders Olsen Sandvik", + "email": "Andersos@users.noreply.github.com" + }, + { + "name": "Andreas Brekken", + "email": "andreas@opuno.com" + }, + { + "name": "Andreas Lind", + "email": "andreaslindpetersen@gmail.com" + }, + { + "name": "Andreas Lind Petersen", + "email": "andreas@one.com" + }, + { + "name": "Andrew Bradley", + "email": "abradley@brightcove.com" + }, + { + "name": "Andrew Bradley", + "email": "cspotcode@gmail.com" + }, + { + "name": "Andrew Krawchyk", + "email": "903716+akrawchyk@users.noreply.github.com" + }, + { + "name": "Andrew Nesbitt", + "email": "andrewnez@gmail.com" + }, + { + "name": "Andrey Popp", + "email": "8mayday@gmail.com" + }, + { + "name": "Andrii Shumada", + "email": "eagleeyes91@gmail.com" + }, + { + "name": "andy matthews", + "email": "andy@commadelimited.com" + }, + { + "name": "Angelica Valenta", + "email": "angelicavalenta@gmail.com" + }, + { + "name": "Anis Safine", + "email": "anis.safine.ext@francetv.fr" + }, + { + "name": "Anish Karandikar", + "email": "anishkny@gmail.com" + }, + { + "name": "Anna Henningsen", + "email": "github@addaleax.net" + }, + { + "name": "Anthony", + "email": "keppi@o2.pl" + }, + { + "name": "Anton", + "email": "anton.redfox@gmail.com" + }, + { + "name": "anton", + "email": "anton.valickij@gmail.com" + }, + { + "name": "APerson", + "email": "danielhglus@gmail.com" + }, + { + "name": "Arian Stolwijk", + "email": "arian@aryweb.nl" + }, + { + "name": "Ariel Mashraki", + "email": "ariel@mashraki.co.il" + }, + { + "name": "Arnaud Brousseau", + "email": "arnaud.brousseau@gmail.com" + }, + { + "name": "Artem Govorov", + "email": "artem.govorov@gmail.com" + }, + { + "name": "Atsuya Takagi", + "email": "asoftonight@gmail.com" + }, + { + "name": "Attila Domokos", + "email": "adomokos@gmail.com" + }, + { + "name": "Austin Birch", + "email": "mraustinbirch@gmail.com" + }, + { + "name": "Avi Vahl", + "email": "avi.vahl@wix.com" + }, + { + "name": "badunk", + "email": "baduncaduncan@gmail.com" + }, + { + "name": "Bamieh", + "email": "ahmadbamieh@gmail.com" + }, + { + "name": "Ben Bradley", + "email": "ben@bradleyit.com" + }, + { + "name": "Ben Glassman", + "email": "benglass@users.noreply.github.com" + }, + { + "name": "Ben Harris", + "email": "benhdev@gmail.com" + }, + { + "name": "Ben Hutchison", + "email": "ben@aldaviva.com" + }, + { + "name": "Ben Lindsey", + "email": "ben.lindsey@vungle.com" + }, + { + "name": "Ben Noordhuis", + "email": "info@bnoordhuis.nl" + }, + { + "name": "Ben Vinegar", + "email": "ben@benv.ca" + }, + { + "name": "Benjamin Eidelman", + "email": "beneidel@gmail.com" + }, + { + "name": "Benjie Gillam", + "email": "benjie@jemjie.com" + }, + { + "name": "Benoit Larroque", + "email": "zeta.ben@gmail.com" + }, + { + "name": "Benoît Zugmeyer", + "email": "bzugmeyer@gmail.com" + }, + { + "name": "Benson Trent", + "email": "bensontrent@gmail.com" + }, + { + "name": "Berker Peksag", + "email": "berker.peksag@gmail.com" + }, + { + "name": "berni", + "email": "berni@extensa.pl" + }, + { + "name": "Bjørge Næss", + "email": "bjoerge@origo.no" + }, + { + "name": "Bjorn Stromberg", + "email": "bjorn@bjornstar.com" + }, + { + "name": "Brendan Nee", + "email": "brendan.nee@gmail.com" + }, + { + "name": "Brian Beck", + "email": "exogen@gmail.com" + }, + { + "name": "Brian Lalor", + "email": "blalor@bravo5.org" + }, + { + "name": "Brian M. Carlson", + "email": "brian.m.carlson@gmail.com" + }, + { + "name": "Brian Moore", + "email": "guardbionic-github@yahoo.com" + }, + { + "name": "Brian Tomlin", + "email": "tendonstrength@gmail.com" + }, + { + "name": "Brittany Moore", + "email": "moore.brittanyann@gmail.com" + }, + { + "name": "Bryan Donovan", + "email": "bdondo@gmail.com" + }, + { + "name": "Buck Doyle", + "email": "b@chromatin.ca" + }, + { + "name": "C. Scott Ananian", + "email": "cscott@cscott.net" + }, + { + "name": "Callum Macrae", + "email": "callum@macr.ae" + }, + { + "name": "Can Oztokmak", + "email": "can@zeplin.io" + }, + { + "name": "Capacitor Set", + "email": "CapacitorSet@users.noreply.github.com" + }, + { + "name": "Carl-Erik Kopseng", + "email": "carlerik@gmail.com" + }, + { + "name": "Casey Foster", + "email": "casey@caseywebdev.com" + }, + { + "name": "Charles Lowell", + "email": "cowboyd@frontside.io" + }, + { + "name": "Charles Merriam", + "email": "charles.merriam@gmail.com" + }, + { + "name": "Charles Samborski", + "email": "demurgos@demurgos.net" + }, + { + "name": "Charlie Rudolph", + "email": "charles.w.rudolph@gmail.com" + }, + { + "name": "Chen Yangjian", + "email": "252317+cyjake@users.noreply.github.com" + }, + { + "name": "Chris", + "email": "chrisleck@users.noreply.github.com" + }, + { + "name": "Chris Buckley", + "email": "chris@cmbuckley.co.uk" + }, + { + "name": "Chris Lamb", + "email": "chris@chris-lamb.co.uk" + }, + { + "name": "Christian", + "email": "me@rndm.de" + }, + { + "name": "Christoffer Hallas", + "email": "christoffer.hallas@gmail.com" + }, + { + "name": "Christoph Neuroth", + "email": "christoph.neuroth@gmail.com" + }, + { + "name": "Christopher Hiller", + "email": "boneskull@boneskull.com" + }, + { + "name": "ChrisWren", + "email": "cthewren@gmail.com" + }, + { + "name": "claudyus", + "email": "claudyus@HEX.(none)", + "url": "none" + }, + { + "name": "Connor Dunn", + "email": "connorhd@gmail.com" + }, + { + "name": "Corey Butler", + "email": "corey@coreybutler.com" + }, + { + "name": "Corey Farrell", + "email": "git@cfware.com" + }, + { + "name": "Cory Thomas", + "email": "cory.thomas@bazaarvoice.com" + }, + { + "name": "Craig Taub", + "email": "craigtaub@gmail.com" + }, + { + "name": "Cube", + "email": "maty21@gmail.com" + }, + { + "name": "Daniel Ruf", + "email": "daniel@daniel-ruf.de" + }, + { + "name": "Daniel St. Jules", + "email": "danielst.jules@gmail.com" + }, + { + "name": "Daniel Stockman", + "email": "daniel.stockman@gmail.com" + }, + { + "name": "Darryl Pogue", + "email": "dvpdiner2@gmail.com" + }, + { + "name": "Dave McKenna", + "email": "davemckenna01@gmail.com" + }, + { + "name": "David da Silva Contín", + "email": "dasilvacontin@gmail.com" + }, + { + "name": "David Henderson", + "email": "david.henderson@triggeredmessaging.com" + }, + { + "name": "David M. Lee", + "email": "leedm777@yahoo.com" + }, + { + "name": "David Neubauer", + "email": "davidneub@gmail.com" + }, + { + "name": "DavidLi119", + "email": "han.david.li@gmail.com" + }, + { + "name": "DavNej", + "email": "davnej.dev@gmail.com" + }, + { + "name": "Denis Bardadym", + "email": "bardadymchik@gmail.com" + }, + { + "name": "Devin Weaver", + "email": "suki@tritarget.org" + }, + { + "name": "dfberry", + "email": "dinaberry@outlook.com" + }, + { + "name": "Di Wu", + "email": "dwu@palantir.com" + }, + { + "name": "Dina Berry", + "email": "dfberry@users.noreply.github.com" + }, + { + "name": "Diogo Monteiro", + "email": "diogo.gmt@gmail.com" + }, + { + "name": "Dmitriy Simushev", + "email": "simushevds@gmail.com" + }, + { + "name": "Dmitry Shirokov", + "email": "deadrunk@gmail.com" + }, + { + "name": "Dmitry Sorin", + "email": "info@staypositive.ru" + }, + { + "name": "Domenic Denicola", + "email": "domenic@domenicdenicola.com" + }, + { + "name": "Dominic Barnes", + "email": "dominic@dbarnes.info" + }, + { + "name": "Dominique Quatravaux", + "email": "dominique@quatravaux.org" + }, + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, + { + "name": "Duncan Beevers", + "email": "duncan@dweebd.com" + }, + { + "name": "eiji.ienaga", + "email": "eiji.ienaga@gmail.com" + }, + { + "name": "elergy", + "email": "elergy@yandex-team.ru" + }, + { + "name": "Eli Skeggs", + "email": "skeggse@users.noreply.github.com" + }, + { + "name": "ELLIOTTCABLE", + "email": "me@ell.io" + }, + { + "name": "Emanuele", + "email": "my.burning@gmail.com" + }, + { + "name": "Enric Pallerols", + "email": "enric@pallerols.cat" + }, + { + "name": "Erik Eng", + "email": "mail@ptz0n.se" + }, + { + "name": "Eugene Tiutiunnyk", + "email": "eugene.tiutiunnyk@lookout.com" + }, + { + "name": "Fabio M. Costa", + "email": "fabiomcosta@gmail.com" + }, + { + "name": "Fábio Santos", + "email": "fabiosantosart@gmail.com" + }, + { + "name": "Fagner Brack", + "email": "github3@fagnermartins.com" + }, + { + "name": "fargies", + "email": "fargies@users.noreply.github.com" + }, + { + "name": "FARKAS Máté", + "email": "mate.farkas@virtual-call-center.eu" + }, + { + "name": "fcrisci", + "email": "fabio.crisci@amadeus.com" + }, + { + "name": "Fede Ramirez", + "email": "i@2fd.me" + }, + { + "name": "Fedor Indutny", + "email": "fedor.indutny@gmail.com" + }, + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + { + "name": "Fin Chen", + "email": "finfin@gmail.com" + }, + { + "name": "Florian Margaine", + "email": "florian@margaine.com" + }, + { + "name": "FND", + "email": "FND@users.noreply.github.com" + }, + { + "name": "fool2fish", + "email": "fool2fish@gmail.com" + }, + { + "name": "Forbes Lindesay", + "email": "forbes@lindesay.co.uk" + }, + { + "name": "Frank Leon Rose", + "email": "frankleonrose@gmail.com" + }, + { + "name": "Frederico Silva", + "email": "frederico.silva@gmail.com" + }, + { + "name": "Fredrik Enestad", + "email": "fredrik@devloop.se" + }, + { + "name": "Fredrik Lindin", + "email": "fredriklindin@gmail.com" + }, + { + "name": "Fumiaki MATSUSHIMA", + "email": "mtsmfm@gmail.com" + }, + { + "name": "Gabriel Silk", + "email": "gabesilk@gmail.com" + }, + { + "name": "Gareth Aye", + "email": "gaye@mozilla.com" + }, + { + "name": "Gareth Murphy", + "email": "gareth.cpm@gmail.com" + }, + { + "name": "Gastón I. Silva", + "email": "givanse@gmail.com" + }, + { + "name": "Gavin Mogan", + "email": "GavinM@airg.com" + }, + { + "name": "gaye", + "email": "gaye@mozilla.com" + }, + { + "name": "gigadude", + "email": "gigadude@users.noreply.github.com" + }, + { + "name": "Giovanni Bassi", + "email": "giggio@giggio.net" + }, + { + "name": "gizemkeser", + "email": "44727928+gizemkeser@users.noreply.github.com" + }, + { + "name": "Glen Huang", + "email": "curvedmark@gmail.com" + }, + { + "name": "Glen Mailer", + "email": "glenjamin@gmail.com" + }, + { + "name": "grasGendarme", + "email": "me@grasgendar.me" + }, + { + "name": "Greg Perkins", + "email": "gregperkins@alum.mit.edu" + }, + { + "name": "Guangcong Luo", + "email": "guangcongluo@gmail.com" + }, + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + }, + { + "name": "Guy Arye", + "email": "arye.guy@gmail.com" + }, + { + "name": "Gyandeep Singh", + "email": "gyandeeps@gmail.com" + }, + { + "name": "Harish", + "email": "hyeluri@gmail.com" + }, + { + "name": "Harry Brundage", + "email": "harry.brundage@gmail.com" + }, + { + "name": "Harry Sarson", + "email": "harry.sarson@hotmail.co.uk" + }, + { + "name": "Harry Wolff", + "email": "hswolff@users.noreply.github.com" + }, + { + "name": "Herman Junge", + "email": "herman@geekli.st" + }, + { + "name": "hokaccha", + "email": "k.hokamura@gmail.com" + }, + { + "name": "Honza Javorek", + "email": "mail@honzajavorek.cz" + }, + { + "name": "Hugo Giraudel", + "email": "hugo.giraudel@gmail.com" + }, + { + "name": "Ian Storm Taylor", + "email": "ian@ianstormtaylor.com" + }, + { + "name": "Ian W. Remmel", + "email": "design@ianwremmel.com" + }, + { + "name": "Ian Young", + "email": "ian.greenleaf@gmail.com" + }, + { + "name": "Ian Zamojc", + "email": "ian@thesecretlocation.net" + }, + { + "name": "Igwe Kalu", + "email": "igwe.kalu@live.com" + }, + { + "name": "ImgBot", + "email": "31427850+ImgBotApp@users.noreply.github.com" + }, + { + "name": "inxorable", + "email": "inxorable@codewren.ch" + }, + { + "name": "Ivan", + "email": "ivan@kinvey.com" + }, + { + "name": "Jaakko Salonen", + "email": "jaakko.salonen@iki.fi" + }, + { + "name": "Jacob Wejendorp", + "email": "jacob@wejendorp.dk" + }, + { + "name": "Jake Craige", + "email": "james.craige@gmail.com" + }, + { + "name": "Jake Marsh", + "email": "jakemmarsh@gmail.com" + }, + { + "name": "Jakob Krigovsky", + "email": "jakob@krigovsky.com" + }, + { + "name": "Jakub NesÌŒetrÌŒil", + "email": "jakub@apiary.io" + }, + { + "name": "James Bowes", + "email": "jbowes@repl.ca" + }, + { + "name": "James Carr", + "email": "james.r.carr@gmail.com" + }, + { + "name": "James D. Rogers", + "email": "jd2rogers2@gmail.com" + }, + { + "name": "James G. Kim", + "email": "jgkim@jayg.org" + }, + { + "name": "James Lal", + "email": "james@lightsofapollo.com" + }, + { + "name": "James Nylen", + "email": "jnylen@gmail.com" + }, + { + "name": "Jan Kopriva", + "email": "jan.kopriva@gooddata.com" + }, + { + "name": "Jan Krems", + "email": "jan.krems@groupon.com" + }, + { + "name": "Jan Lehnardt", + "email": "jan@apache.org" + }, + { + "name": "Jason Barry", + "email": "jay@jcbarry.com" + }, + { + "name": "Jason Lai", + "email": "jason@getpebble.com" + }, + { + "name": "Jason Leyba", + "email": "jmleyba@gmail.com" + }, + { + "name": "Javier Aranda", + "email": "javierav@javierav.com" + }, + { + "name": "Jayasankar", + "email": "jayasankar.m@gmail.com" + }, + { + "name": "Jean Ponchon", + "email": "gelule@gmail.com" + }, + { + "name": "Jeff Kunkle", + "email": "jeff.kunkle@nearinfinity.com" + }, + { + "name": "Jeff Schilling", + "email": "jeff.schilling@q2ebanking.com" + }, + { + "name": "JeongHoon Byun", + "email": "outsideris@gmail.com", + "url": "aka Outsider" + }, + { + "name": "Jérémie Astori", + "email": "jeremie@astori.fr" + }, + { + "name": "Jeremy Martin", + "email": "jmar777@gmail.com" + }, + { + "name": "Jerry Muzsik", + "email": "jerrymuzsik@icloud.com" + }, + { + "name": "Jesse Dailey", + "email": "jesse.dailey@gmail.com" + }, + { + "name": "jimenglish81", + "email": "jimenglish81@gmail.com" + }, + { + "name": "Jimmy Cuadra", + "email": "jimmy@jimmycuadra.com" + }, + { + "name": "Jo Liss", + "email": "joliss42@gmail.com" + }, + { + "name": "Joao Moreno", + "email": "mail@joaomoreno.com" + }, + { + "name": "Joel Kemp", + "email": "mrjoelkemp@gmail.com" + }, + { + "name": "Joey Cozza", + "email": "joey@grow.com" + }, + { + "name": "John Doty", + "email": "jrhdoty@gmail.com" + }, + { + "name": "John Firebaugh", + "email": "john.firebaugh@gmail.com" + }, + { + "name": "John Reeves", + "email": "github@jonnyreeves.co.uk" + }, + { + "name": "Johnathon Sanders", + "email": "outdooricon@gmail.com" + }, + { + "name": "Jon Surrell", + "email": "jon.surrell@automattic.com" + }, + { + "name": "Jonas Dohse", + "email": "jonas@mbr-targeting.com" + }, + { + "name": "Jonas Westerlund", + "email": "jonas.westerlund@me.com" + }, + { + "name": "Jonathan Creamer", + "email": "matrixhasyou2k4@gmail.com" + }, + { + "name": "Jonathan Delgado", + "email": "jdelgado@rewip.com" + }, + { + "name": "Jonathan Kim", + "email": "jkimbo@gmail.com" + }, + { + "name": "Jonathan Ong", + "email": "jonathanrichardong@gmail.com" + }, + { + "name": "Jonathan Park", + "email": "jpark@daptiv.com" + }, + { + "name": "Jonathan Rajavuori", + "email": "jrajav@gmail.com" + }, + { + "name": "Jordan Sexton", + "email": "jordan@jordansexton.com" + }, + { + "name": "Joseph Lin", + "email": "josephlin55555@gmail.com" + }, + { + "name": "Josh Eversmann", + "email": "josh.eversmann@gmail.com" + }, + { + "name": "Josh Lory", + "email": "josh.lory@code.org" + }, + { + "name": "Josh Soref", + "email": "jsoref@users.noreply.github.com" + }, + { + "name": "Joshua Appelman", + "email": "jappelman@xebia.com" + }, + { + "name": "Joshua Krall", + "email": "joshuakrall@pobox.com" + }, + { + "name": "JP Bochi", + "email": "jpbochi@gmail.com" + }, + { + "name": "jsdevel", + "email": "js.developer.undefined@gmail.com" + }, + { + "name": "Juerg B", + "email": "44573692+juergba@users.noreply.github.com" + }, + { + "name": "juergba", + "email": "filodron@gmail.com" + }, + { + "name": "Julien Wajsberg", + "email": "felash@gmail.com" + }, + { + "name": "Jupp Müller", + "email": "jupp0r@gmail.com" + }, + { + "name": "Jussi Virtanen", + "email": "jussi.k.virtanen@gmail.com" + }, + { + "name": "Justin DuJardin", + "email": "justin.dujardin@sococo.com" + }, + { + "name": "Juzer Ali", + "email": "er.juzerali@gmail.com" + }, + { + "name": "Katie Gengler", + "email": "katiegengler@gmail.com" + }, + { + "name": "kavun", + "email": "kevin.a.reed@gmail.com" + }, + { + "name": "Kazuhito Hokamura", + "email": "k.hokamura@gmail.com" + }, + { + "name": "Keith Cirkel", + "email": "github@keithcirkel.co.uk" + }, + { + "name": "Kelong Wang", + "email": "buaawkl@gmail.com" + }, + { + "name": "Kent C. Dodds", + "email": "kent+github@doddsfamily.us" + }, + { + "name": "Kevin Burke", + "email": "kev@inburke.com" + }, + { + "name": "Kevin Conway", + "email": "kevinjacobconway@gmail.com" + }, + { + "name": "Kevin Kirsche", + "email": "Kev.Kirsche+GitHub@gmail.com" + }, + { + "name": "Kevin Partington", + "email": "platinum.azure@kernelpanicstudios.com" + }, + { + "name": "Kevin Wang", + "email": "kevin@fossa.io" + }, + { + "name": "Kirill Korolyov", + "email": "kirill.korolyov@gmail.com" + }, + { + "name": "klaemo", + "email": "klaemo@fastmail.fm" + }, + { + "name": "Koen Punt", + "email": "koen@koenpunt.nl" + }, + { + "name": "Konstantin Käfer", + "email": "github@kkaefer.com" + }, + { + "name": "Kris Rasmussen", + "email": "kristopher.rasmussen@gmail.com" + }, + { + "name": "Kunal Nagpal", + "email": "kunagpal@users.noreply.github.com" + }, + { + "name": "Kyle Mitchell", + "email": "kyle@kemitchell.com" + }, + { + "name": "lakmeer", + "email": "lakmeerkravid@gmail.com" + }, + { + "name": "Lane Kelly", + "email": "lanekelly16@gmail.com" + }, + { + "name": "László Bácsi", + "email": "lackac@lackac.hu" + }, + { + "name": "Laurence Rowe", + "email": "lrowe@netflix.com" + }, + { + "name": "Liam Newman", + "email": "bitwiseman@gmail.com" + }, + { + "name": "Linus Unnebäck", + "email": "linus@folkdatorn.se" + }, + { + "name": "lodr", + "email": "salva@unoyunodiez.com" + }, + { + "name": "Long Ho", + "email": "longlho@users.noreply.github.com" + }, + { + "name": "Maciej MaÅ‚ecki", + "email": "maciej.malecki@notimplemented.org" + }, + { + "name": "Mal Graty", + "email": "mal.graty@googlemail.com" + }, + { + "name": "Marais Rossouw", + "email": "me@maraisr.com" + }, + { + "name": "Marc Kuo", + "email": "kuomarc2@gmail.com" + }, + { + "name": "Marc Udoff", + "email": "mlucool@gmail.com" + }, + { + "name": "Marcello Bastea-Forte", + "email": "marcello@cellosoft.com" + }, + { + "name": "Mark Banner", + "email": "standard8@mozilla.com" + }, + { + "name": "Mark Owsiak", + "email": "mark.owsiak@gmail.com" + }, + { + "name": "Markus Tacker", + "email": "m@coderbyheart.com" + }, + { + "name": "Martijn Cuppens", + "email": "martijn.cuppens@intracto.com" + }, + { + "name": "Martin Marko", + "email": "marcus@gratex.com" + }, + { + "name": "Mathieu Desvé", + "email": "mathieudesve@MacBook-Pro-de-Mathieu.local" + }, + { + "name": "Matija Marohnić", + "email": "matija.marohnic@gmail.com" + }, + { + "name": "Matt Bierner", + "email": "mattbierner@gmail.com" + }, + { + "name": "Matt Giles", + "email": "matt.giles@cerner.com" + }, + { + "name": "Matt Robenolt", + "email": "matt@ydekproductions.com" + }, + { + "name": "Matt Smith", + "email": "matthewgarysmith@gmail.com" + }, + { + "name": "Matthew Shanley", + "email": "matthewshanley@littlesecretsrecords.com" + }, + { + "name": "Mattias Tidlund", + "email": "mattias.tidlund@learningwell.se" + }, + { + "name": "Max Goodman", + "email": "c@chromakode.com" + }, + { + "name": "Maximilian Antoni", + "email": "mail@maxantoni.de" + }, + { + "name": "Merrick Christensen", + "email": "merrick.christensen@gmail.com" + }, + { + "name": "Michael Demmer", + "email": "demmer@jut.io" + }, + { + "name": "Michael Jackson", + "email": "mjijackson@gmail.com" + }, + { + "name": "Michael Olson", + "email": "mwolson@member.fsf.org" + }, + { + "name": "Michael Riley", + "email": "michael.riley@autodesk.com" + }, + { + "name": "Michael Schoonmaker", + "email": "michael.r.schoonmaker@gmail.com" + }, + { + "name": "Michal Charemza", + "email": "michalcharemza@gmail.com" + }, + { + "name": "Michiel de Jong", + "email": "michiel@unhosted.org" + }, + { + "name": "Mick Brooks", + "email": "mick.brooks@sinking.in" + }, + { + "name": "Mike Pennisi", + "email": "mike@mikepennisi.com" + }, + { + "name": "Mislav Marohnić", + "email": "mislav.marohnic@gmail.com" + }, + { + "name": "monowerker", + "email": "monowerker@gmail.com" + }, + { + "name": "Moshe Kolodny", + "email": "mkolodny@integralads.com" + }, + { + "name": "mrShturman", + "email": "mrshturman@gmail.com" + }, + { + "name": "Nathan Alderson", + "email": "nathan.alderson@adtran.com" + }, + { + "name": "Nathan Black", + "email": "nathan@nathanblack.org" + }, + { + "name": "Nathan Bowser", + "email": "nathan.bowser@spiderstrategies.com" + }, + { + "name": "Nathan Houle", + "email": "nathan@nathanhoule.com" + }, + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net" + }, + { + "name": "nexdrew", + "email": "andrew.goode@nextraq.com" + }, + { + "name": "Nick Fitzgerald", + "email": "fitzgen@gmail.com" + }, + { + "name": "Nicolas Girault", + "email": "nic.girault@gmail.com" + }, + { + "name": "Nicolo Taddei", + "email": "taddei.uk@gmail.com" + }, + { + "name": "Nik Nyby", + "email": "nnyby@columbia.edu" + }, + { + "name": "Nikolaos Georgiou", + "email": "Nikolaos.Georgiou@gmail.com" + }, + { + "name": "nishigori", + "email": "Takuya_Nishigori@voyagegroup.com" + }, + { + "name": "Noshir Patel", + "email": "nosh@blackpiano.com" + }, + { + "name": "not-an-aardvark", + "email": "not-an-aardvark@users.noreply.github.com" + }, + { + "name": "OlegTsyba", + "email": "oleg.tsyba.ua@gmail.com" + }, + { + "name": "olsonpm", + "email": "olsonpm@users.noreply.github.com" + }, + { + "name": "omardelarosa", + "email": "thedelarosa@gmail.com" + }, + { + "name": "Oscar Godson", + "email": "oscargodson@outlook.com" + }, + { + "name": "Outsider", + "email": "outsideris@gmail.com" + }, + { + "name": "oveddan", + "email": "stangogh@gmail.com" + }, + { + "name": "P. Roebuck", + "email": "plroebuck@users.noreply.github.com" + }, + { + "name": "Panu Horsmalahti", + "email": "panu.horsmalahti@iki.fi" + }, + { + "name": "Parker Moore", + "email": "parkrmoore@gmail.com" + }, + { + "name": "Pat Finnigan", + "email": "patrick.k.finnigan@gmail.com" + }, + { + "name": "Paul Armstrong", + "email": "paul@paularmstrongdesigns.com" + }, + { + "name": "Paul Miller", + "email": "paul@paulmillr.com" + }, + { + "name": "Paul Roebuck", + "email": "plroebuck@users.noreply.github.com" + }, + { + "name": "Pavel Zubkou", + "email": "pavel.zubkou@gmail.com" + }, + { + "name": "Pete Hawkins", + "email": "pete@petes-imac.frontinternal.net" + }, + { + "name": "Peter Müller", + "email": "munter@fumle.dk" + }, + { + "name": "Peter Rust", + "email": "peter@cornerstonenw.com" + }, + { + "name": "Phil Sung", + "email": "psung@dnanexus.com" + }, + { + "name": "Philip M. White", + "email": "philip@mailworks.org" + }, + { + "name": "PoppinL", + "email": "poppinlp@gmail.com" + }, + { + "name": "Poprádi Ã�rpád", + "email": "popradi.arpad11@gmail.com" + }, + { + "name": "Prayag Verma", + "email": "prayag.verma@gmail.com" + }, + { + "name": "qiuzuhui", + "email": "qiuzuhui@users.noreply.github.com" + }, + { + "name": "Quang Van", + "email": "quangvvv@gmail.com" + }, + { + "name": "Quanlong He", + "email": "kyan.ql.he@gmail.com" + }, + { + "name": "R56", + "email": "rviskus@gmail.com" + }, + { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + { + "name": "Refael Ackermann", + "email": "refael@empeeric.com" + }, + { + "name": "Rich Trott", + "email": "rtrott@gmail.com" + }, + { + "name": "Richard Dingwall", + "email": "rdingwall@gmail.com" + }, + { + "name": "Richard Knop", + "email": "RichardKnop@users.noreply.github.com" + }, + { + "name": "Rico Sta. Cruz", + "email": "rstacruz@users.noreply.github.com" + }, + { + "name": "rmacklin", + "email": "richard.github@nrm.com" + }, + { + "name": "Rob Loach", + "email": "robloach@gmail.com" + }, + { + "name": "Rob Raux", + "email": "rraux@peachworks.com" + }, + { + "name": "Rob Wu", + "email": "rob@robwu.nl" + }, + { + "name": "Robert Rossmann", + "email": "rr.rossmann@me.com" + }, + { + "name": "Romain Prieto", + "email": "romain.prieto@gmail.com" + }, + { + "name": "Roman Neuhauser", + "email": "rneuhauser@suse.cz" + }, + { + "name": "Roman Shtylman", + "email": "shtylman@gmail.com" + }, + { + "name": "Ross Warren", + "email": "rosswarren4@gmail.com" + }, + { + "name": "rotemdan", + "email": "rotemdan@gmail.com" + }, + { + "name": "Russ Bradberry", + "email": "devdazed@me.com" + }, + { + "name": "Russell Munson", + "email": "rmunson@github.com" + }, + { + "name": "Rustem Mustafin", + "email": "mustafin.rustem@gmail.com" + }, + { + "name": "Ryan Hubbard", + "email": "ryanmhubbard@gmail.com" + }, + { + "name": "Ryan Shaw", + "email": "ryan.shaw@min.vc" + }, + { + "name": "Ryan Tablada", + "email": "ryan.tablada@gmail.com" + }, + { + "name": "Ryunosuke SATO", + "email": "tricknotes.rs@gmail.com" + }, + { + "name": "ryym", + "email": "ryym.64@gmail.com" + }, + { + "name": "Salehen Shovon Rahman", + "email": "salehen.rahman@gmail.com" + }, + { + "name": "Sam Mussell", + "email": "smussell@gmail.com" + }, + { + "name": "samuel goldszmidt", + "email": "samuel.goldszmidt@gmail.com" + }, + { + "name": "sarehag", + "email": "joakim.sarehag@gmail.com" + }, + { + "name": "Sasha Koss", + "email": "koss@nocorp.me" + }, + { + "name": "Scott Kao", + "email": "Scottkao85@users.noreply.github.com" + }, + { + "name": "Scott Santucci", + "email": "ScottFreeCode@users.noreply.github.com" + }, + { + "name": "ScottFreeCode", + "email": "ScottFreeCode@users.noreply.github.com" + }, + { + "name": "Sean Lang", + "email": "slang800@gmail.com" + }, + { + "name": "Sebastian Van Sande", + "email": "sebastian@vansande.org" + }, + { + "name": "sebv", + "email": "seb.vincent@gmail.com" + }, + { + "name": "Seiya Konno", + "email": "nulltask@gmail.com" + }, + { + "name": "Sergey Simonchik", + "email": "sergey.simonchik@jetbrains.com" + }, + { + "name": "Sergio Santoro", + "email": "santoro.srg@gmail.com" + }, + { + "name": "Shaine Hatch", + "email": "shaine@squidtree.com" + }, + { + "name": "Shawn Krisman", + "email": "telaviv@github" + }, + { + "name": "Shinnosuke Watanabe", + "email": "snnskwtnb@gmail.com" + }, + { + "name": "silentcloud", + "email": "rjmuqiang@gmail.com" + }, + { + "name": "Silvio Massari", + "email": "silvio.massari@auth0.com" + }, + { + "name": "Simon Gaeremynck", + "email": "gaeremyncks@gmail.com" + }, + { + "name": "Simon Goumaz", + "email": "simon@attentif.ch" + }, + { + "name": "simov", + "email": "simeonvelichkov@gmail.com" + }, + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com" + }, + { + "name": "Slobodan MiÅ¡ković", + "email": "slobodan@miskovic.ca" + }, + { + "name": "slyg", + "email": "syl.faucherand@gmail.com" + }, + { + "name": "Soel", + "email": "shachar.soel@sap.com" + }, + { + "name": "solodynamo", + "email": "bittuf3@gmail.com" + }, + { + "name": "Sorin Iclanzan", + "email": "sorin@iclanzan.com" + }, + { + "name": "Standa Opichal", + "email": "opichals@gmail.com" + }, + { + "name": "startswithaj", + "email": "jake.mc@icloud.com" + }, + { + "name": "Stephen Mathieson", + "email": "smath23@gmail.com" + }, + { + "name": "Steve Mason", + "email": "stevem@brandwatch.com" + }, + { + "name": "Stewart Taylor", + "email": "stewart.taylor1@gmail.com" + }, + { + "name": "Stone", + "email": "baoshi.li@adleida.com" + }, + { + "name": "Sulabh Bista", + "email": "sul4bh@gmail.com" + }, + { + "name": "Sune Simonsen", + "email": "sune@we-knowhow.dk" + }, + { + "name": "Svetlana", + "email": "39729453+Lana-Light@users.noreply.github.com" + }, + { + "name": "Sylvain", + "email": "sstephant+github@gmail.com" + }, + { + "name": "Sylvester Keil", + "email": "sylvester@keil.or.at" + }, + { + "name": "Szauka", + "email": "33459309+Szauka@users.noreply.github.com" + }, + { + "name": "Tapiwa Kelvin", + "email": "tapiwa@munzwa.tk" + }, + { + "name": "Ted Yavuzkurt", + "email": "hello@TedY.io" + }, + { + "name": "Teddy Zeenny", + "email": "teddyzeenny@gmail.com" + }, + { + "name": "tgautier@yahoo.com", + "email": "tgautier@gmail.com" + }, + { + "name": "Thedark1337", + "email": "thedark1337@thedark1337.com" + }, + { + "name": "Thomas Broadley", + "email": "buriedunderbooks@hotmail.com" + }, + { + "name": "Thomas Grainger", + "email": "tagrain@gmail.com" + }, + { + "name": "Thomas Vantuycom", + "email": "thomasvantuycom@protonmail.com" + }, + { + "name": "Tim Ehat", + "email": "timehat@gmail.com" + }, + { + "name": "Tim Harshman", + "email": "goteamtim+git@gmail.com" + }, + { + "name": "Timo Tijhof", + "email": "krinklemail@gmail.com" + }, + { + "name": "Timothy Gu", + "email": "timothygu99@gmail.com" + }, + { + "name": "Tingan Ho", + "email": "tingan87@gmail.com" + }, + { + "name": "tmont", + "email": "tommy.mont@gmail.com" + }, + { + "name": "Tobias Bieniek", + "email": "tobias.bieniek@gmail.com" + }, + { + "name": "Tobias Mollstam", + "email": "tobias@mollstam.com" + }, + { + "name": "Todd Agulnick", + "email": "tagulnick@onjack.com" + }, + { + "name": "Tom Coquereau", + "email": "tom@thau.me" + }, + { + "name": "Tom Hughes", + "email": "tom@compton.nu" + }, + { + "name": "Tomer Eskenazi", + "email": "tomer.eskenazi@ironsrc.com" + }, + { + "name": "traleig1", + "email": "darkphoenix739@gmail.com" + }, + { + "name": "Travis Jeffery", + "email": "tj@travisjeffery.com" + }, + { + "name": "tripu", + "email": "t@tripu.info" + }, + { + "name": "Tyson Tate", + "email": "tyson@tysontate.com" + }, + { + "name": "Vadim Nikitin", + "email": "vnikiti@ncsu.edu" + }, + { + "name": "Valentin Agachi", + "email": "github-com@agachi.name" + }, + { + "name": "Valeri Karpov", + "email": "val@karpov.io" + }, + { + "name": "Victor", + "email": "victor@turo.com" + }, + { + "name": "Victor Costan", + "email": "costan@gmail.com" + }, + { + "name": "Ville Saukkonen", + "email": "villesau@users.noreply.github.com" + }, + { + "name": "Vivek Ganesan", + "email": "caliberoviv@gmail.com" + }, + { + "name": "vlad", + "email": "iamvlad@gmail.com" + }, + { + "name": "Vlad Magdalin", + "email": "vlad@webflow.com" + }, + { + "name": "Volker Buzek", + "email": "volker.buzek@sap.com" + }, + { + "name": "Wanseob Lim", + "email": "email@wanseob.com" + }, + { + "name": "Wil Moore III", + "email": "wil.moore@wilmoore.com" + }, + { + "name": "Will Langstroth", + "email": "will@langstroth.com" + }, + { + "name": "wsw", + "email": "wsw0108@gmail.com" + }, + { + "name": "Xavier Antoviaque", + "email": "xavier@antoviaque.org" + }, + { + "name": "Xavier Damman", + "email": "xdamman@gmail.com" + }, + { + "name": "XhmikosR", + "email": "xhmikosr@gmail.com" + }, + { + "name": "XhmikosR", + "email": "xhmikosr@users.sourceforge.net" + }, + { + "name": "Yanis Wang", + "email": "yanis.wang@gmail.com" + }, + { + "name": "yehiyam", + "email": "yehiyam@users.noreply.github.com" + }, + { + "name": "Yoshiya Hinosawa", + "email": "hinosawa@waku-2.com" + }, + { + "name": "Yuest Wang", + "email": "yuestwang@gmail.com" + }, + { + "name": "yuitest", + "email": "yuitest@cjhat.net" + }, + { + "name": "zhiyelee", + "email": "zhiyelee@gmail.com" + }, + { + "name": "Zsolt Takács", + "email": "zsolt@takacs.cc" + }, + { + "name": "现充", + "email": "qixiang.cqx@alibaba-inc.com" + } + ], + "dependencies": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + }, + "description": "simple, flexible, fun test framework", + "devDependencies": { + "@11ty/eleventy": "^0.7.1", + "@mocha/contributors": "^1.0.3", + "@mocha/docdash": "^2.1.0", + "assetgraph-builder": "^6.10.0", + "autoprefixer": "^9.4.10", + "browserify": "^16.2.3", + "browserify-package-json": "^1.0.1", + "chai": "^4.2.0", + "coffee-script": "^1.12.7", + "coveralls": "^3.0.3", + "cross-env": "^5.2.0", + "cross-spawn": "^6.0.5", + "eslint": "^5.15.0", + "eslint-config-prettier": "^3.6.0", + "eslint-config-semistandard": "^13.0.0", + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.16.0", + "eslint-plugin-node": "^8.0.1", + "eslint-plugin-prettier": "^3.0.1", + "eslint-plugin-promise": "^4.0.1", + "eslint-plugin-standard": "^4.0.0", + "husky": "^1.3.1", + "jsdoc": "^3.5.5", + "karma": "^4.0.1", + "karma-browserify": "^6.0.0", + "karma-chrome-launcher": "^2.2.0", + "karma-mocha": "^1.3.0", + "karma-mocha-reporter": "^2.2.5", + "karma-sauce-launcher": "^2.0.2", + "lint-staged": "^8.1.5", + "markdown-it": "^8.4.2", + "markdown-it-anchor": "^5.0.2", + "markdown-it-attrs": "^2.3.2", + "markdown-it-prism": "^2.0.1", + "markdown-magic": "^0.1.25", + "markdown-magic-package-json": "^2.0.0", + "markdown-toc": "^1.2.0", + "markdownlint-cli": "^0.14.1", + "nps": "^5.9.4", + "nyc": "^13.3.0", + "prettier": "^1.16.4", + "remark": "^10.0.1", + "remark-github": "^7.0.6", + "remark-inline-links": "^3.1.2", + "rewiremock": "^3.13.0", + "rimraf": "^2.6.3", + "sinon": "^7.2.7", + "strip-ansi": "^5.0.0", + "svgo": "^1.2.0", + "through2": "^3.0.1", + "to-vfile": "^5.0.2", + "unexpected": "^10.40.2", + "unexpected-eventemitter": "^1.1.3", + "unexpected-sinon": "^10.11.1", + "uslug": "^1.0.4", + "watchify": "^3.11.1" + }, + "directories": { + "lib": "./lib", + "test": "./test" + }, + "engines": { + "node": ">= 6.0.0" + }, + "files": [ + "bin/{*mocha,options.js}", + "assets/growl/*.png", + "lib/**/*.{js,html,json}", + "index.js", + "mocha.css", + "mocha.js", + "browser-entry.js" + ], + "gitter": "https://gitter.im/mochajs/mocha", + "homepage": "https://mochajs.org/", + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "keywords": [ + "mocha", + "test", + "bdd", + "tdd", + "tap" + ], + "license": "MIT", + "logo": "https://cldup.com/S9uQ-cOLYz.svg", + "name": "mocha", + "notifyLogo": "https://ibin.co/4QuRuGjXvl36.png", + "prettier": { + "singleQuote": true, + "bracketSpacing": false, + "endOfLine": "auto" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mochajs/mocha.git" + }, + "scripts": { + "prepublishOnly": "nps test clean build", + "start": "nps", + "test": "nps test", + "version": "nps version" + }, + "version": "6.1.4" +} diff --git a/WebServer/node_modules/ms/index.js b/WebServer/node_modules/ms/index.js new file mode 100644 index 000000000..72297501f --- /dev/null +++ b/WebServer/node_modules/ms/index.js @@ -0,0 +1,162 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var w = d * 7; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^((?:\d+)?\-?\d?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'weeks': + case 'week': + case 'w': + return n * w; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return Math.round(ms / d) + 'd'; + } + if (msAbs >= h) { + return Math.round(ms / h) + 'h'; + } + if (msAbs >= m) { + return Math.round(ms / m) + 'm'; + } + if (msAbs >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return plural(ms, msAbs, d, 'day'); + } + if (msAbs >= h) { + return plural(ms, msAbs, h, 'hour'); + } + if (msAbs >= m) { + return plural(ms, msAbs, m, 'minute'); + } + if (msAbs >= s) { + return plural(ms, msAbs, s, 'second'); + } + return ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); +} diff --git a/WebServer/node_modules/ms/package.json b/WebServer/node_modules/ms/package.json new file mode 100644 index 000000000..dd74ece2a --- /dev/null +++ b/WebServer/node_modules/ms/package.json @@ -0,0 +1,73 @@ +{ + "_args": [ + [ + "ms@2.1.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "ms@2.1.1", + "_id": "ms@2.1.1", + "_inBundle": false, + "_integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "_location": "/ms", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "ms@2.1.1", + "name": "ms", + "escapedName": "ms", + "rawSpec": "2.1.1", + "saveSpec": null, + "fetchSpec": "2.1.1" + }, + "_requiredBy": [ + "/debug", + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "_spec": "2.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "bugs": { + "url": "https://github.com/zeit/ms/issues" + }, + "description": "Tiny millisecond conversion utility", + "devDependencies": { + "eslint": "4.12.1", + "expect.js": "0.3.1", + "husky": "0.14.3", + "lint-staged": "5.0.0", + "mocha": "4.0.1" + }, + "eslintConfig": { + "extends": "eslint:recommended", + "env": { + "node": true, + "es6": true + } + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/zeit/ms#readme", + "license": "MIT", + "lint-staged": { + "*.js": [ + "npm run lint", + "prettier --single-quote --write", + "git add" + ] + }, + "main": "./index", + "name": "ms", + "repository": { + "type": "git", + "url": "git+https://github.com/zeit/ms.git" + }, + "scripts": { + "lint": "eslint lib/* bin/*", + "precommit": "lint-staged", + "test": "mocha tests.js" + }, + "version": "2.1.1" +} diff --git a/WebServer/node_modules/nice-try/package.json b/WebServer/node_modules/nice-try/package.json new file mode 100644 index 000000000..7bd38e6f0 --- /dev/null +++ b/WebServer/node_modules/nice-try/package.json @@ -0,0 +1,64 @@ +{ + "_args": [ + [ + "nice-try@1.0.5", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "nice-try@1.0.5", + "_id": "nice-try@1.0.5", + "_inBundle": false, + "_integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "_location": "/nice-try", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "nice-try@1.0.5", + "name": "nice-try", + "escapedName": "nice-try", + "rawSpec": "1.0.5", + "saveSpec": null, + "fetchSpec": "1.0.5" + }, + "_requiredBy": [ + "/cross-spawn" + ], + "_resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "_spec": "1.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "authors": [ + "Tobias Reich " + ], + "bugs": { + "url": "https://github.com/electerious/nice-try/issues" + }, + "description": "Tries to execute a function and discards any error that occurs", + "devDependencies": { + "chai": "^4.1.2", + "coveralls": "^3.0.0", + "mocha": "^5.1.1", + "nyc": "^12.0.1" + }, + "files": [ + "src" + ], + "homepage": "https://github.com/electerious/nice-try", + "keywords": [ + "try", + "catch", + "error" + ], + "license": "MIT", + "main": "src/index.js", + "name": "nice-try", + "repository": { + "type": "git", + "url": "git+https://github.com/electerious/nice-try.git" + }, + "scripts": { + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "test": "nyc node_modules/mocha/bin/_mocha" + }, + "version": "1.0.5" +} diff --git a/WebServer/node_modules/nice-try/src/index.js b/WebServer/node_modules/nice-try/src/index.js new file mode 100644 index 000000000..837506f2c --- /dev/null +++ b/WebServer/node_modules/nice-try/src/index.js @@ -0,0 +1,12 @@ +'use strict' + +/** + * Tries to execute a function and discards any error that occurs. + * @param {Function} fn - Function that might or might not throw an error. + * @returns {?*} Return-value of the function when no error occurred. + */ +module.exports = function(fn) { + + try { return fn() } catch (e) {} + +} \ No newline at end of file diff --git a/WebServer/node_modules/node-environment-flags/flags.json b/WebServer/node_modules/node-environment-flags/flags.json new file mode 100644 index 000000000..cdb0117e3 --- /dev/null +++ b/WebServer/node_modules/node-environment-flags/flags.json @@ -0,0 +1,439 @@ +{ + ">=6.0.0 <6.3.0": [ + "--debug", + "--debug-brk", + "--icu-data-dir", + "--no-deprecation", + "--no-warnings", + "--port", + "--prof-process", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=6.3.0 <6.9.0": [ + "--debug", + "--debug-brk", + "--icu-data-dir", + "--inspect", + "--no-deprecation", + "--no-warnings", + "--port", + "--prof-process", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=6.9.0 <6.11.0": [ + "--debug", + "--debug-brk", + "--icu-data-dir", + "--inspect", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--port", + "--prof-process", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + "~6.11.0": [ + "--debug", + "--debug-brk", + "--icu-data-dir", + "--inspect", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--port", + "--prof-process", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=6.12.0 <8.0.0": [ + "--debug", + "--debug-brk", + "--icu-data-dir", + "--inspect", + "--inspect-brk", + "--inspect-port", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--port", + "--prof-process", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=8.0.0 <8.2.0": [ + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--max-old-space-size", + "--napi-modules", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=8.2.0 <8.4.0": [ + "--abort-on-uncaught-exception", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--max-old-space-size", + "--napi-modules", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + "~8.4.0": [ + "--abort-on-uncaught-exception", + "--expose-http2", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--max-old-space-size", + "--napi-modules", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=8.5.0 <8.8.0": [ + "--abort-on-uncaught-exception", + "--experimental-modules", + "--expose-http2", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--max-old-space-size", + "--napi-modules", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=8.8.0 <8.12.0": [ + "--abort-on-uncaught-exception", + "--experimental-modules", + "--expose-http2", + "--force-async-hooks-checks", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--max-old-space-size", + "--napi-modules", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=8.12.0 <10.0.0": [ + "--abort-on-uncaught-exception", + "--experimental-modules", + "--expose-http2", + "--force-async-hooks-checks", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--max-old-space-size", + "--napi-modules", + "--no-deprecation", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-event-file-pattern", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=10.0.0 <10.3.0": [ + "--enable-fips", + "--experimental-modules", + "--experimental-repl-await", + "--experimental-vm-modules", + "--force-fips", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--napi-modules", + "--no-deprecation", + "--no-force-async-hooks-checks", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-event-file-pattern", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=10.3.0 <10.5.0": [ + "--enable-fips", + "--experimental-modules", + "--experimental-repl-await", + "--experimental-vm-modules", + "--force-fips", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--napi-modules", + "--no-deprecation", + "--no-force-async-hooks-checks", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--prof", + "--prof-process", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-event-file-pattern", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=10.5.0 <10.7.0": [ + "--enable-fips", + "--experimental-modules", + "--experimental-repl-await", + "--experimental-vm-modules", + "--experimental-worker", + "--force-fips", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--napi-modules", + "--no-deprecation", + "--no-force-async-hooks-checks", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--prof", + "--prof-process", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-event-file-pattern", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ], + ">=10.7.0 <10.10.0": [ + "--enable-fips", + "--experimental-modules", + "--experimental-repl-await", + "--experimental-vm-modules", + "--experimental-worker", + "--force-fips", + "--icu-data-dir", + "--inspect-brk", + "--inspect-port", + "--inspect", + "--loader", + "--napi-modules", + "--no-deprecation", + "--no-force-async-hooks-checks", + "--no-warnings", + "--openssl-config", + "--pending-deprecation", + "--prof", + "--prof-process", + "--redirect-warnings", + "--require", + "--throw-deprecation", + "--title", + "--tls-cipher-list", + "--trace-deprecation", + "--trace-event-categories", + "--trace-event-file-pattern", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", + "--track-heap-objects", + "--use-bundled-ca", + "--use-openssl-ca", + "--v8-pool-size", + "--zero-fill-buffers", + "-r" + ] +} diff --git a/WebServer/node_modules/node-environment-flags/implementation.js b/WebServer/node_modules/node-environment-flags/implementation.js new file mode 100644 index 000000000..e69165bde --- /dev/null +++ b/WebServer/node_modules/node-environment-flags/implementation.js @@ -0,0 +1,62 @@ +'use strict'; + +const semver = require('semver'); +const flags = require('./flags.json'); +const getDescriptors = require('object.getownpropertydescriptors'); + +const replaceUnderscoresRegex = /_/g; +const leadingDashesRegex = /^--?/; +const trailingValuesRegex = /=.*$/; + +const replace = Function.call.bind(String.prototype.replace); +const has = Function.call.bind(Set.prototype.has); +const test = Function.call.bind(RegExp.prototype.test); + +const [allowedNodeEnvironmentFlags, detectedSemverRange] = Object.keys( + flags +).reduce( + (acc, range) => + acc || + (semver.satisfies(process.version, range) ? [flags[range], range] : acc), + null +); + +const trimLeadingDashes = flag => replace(flag, leadingDashesRegex, ''); + +class NodeEnvironmentFlagsSet extends Set { + constructor(...args) { + super(...args); + + this.add = () => this; + } + + delete() { + return false; + } + + clear() {} + + has(key) { + if (typeof key === 'string') { + key = replace(key, replaceUnderscoresRegex, '-'); + if (test(leadingDashesRegex, key)) { + key = replace(key, trailingValuesRegex, ''); + return has(this, key); + } + return has(nodeFlags, key); + } + return false; + } +} +const nodeFlags = Object.defineProperties( + new Set(allowedNodeEnvironmentFlags.map(trimLeadingDashes)), + getDescriptors(Set.prototype) +); + +Object.freeze(NodeEnvironmentFlagsSet.prototype.constructor); +Object.freeze(NodeEnvironmentFlagsSet.prototype); + +exports.allowedNodeEnvironmentFlags = Object.freeze( + new NodeEnvironmentFlagsSet(allowedNodeEnvironmentFlags) +); +exports.detectedSemverRange = detectedSemverRange; diff --git a/WebServer/node_modules/node-environment-flags/index.js b/WebServer/node_modules/node-environment-flags/index.js new file mode 100644 index 000000000..79631a866 --- /dev/null +++ b/WebServer/node_modules/node-environment-flags/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./polyfill')(); diff --git a/WebServer/node_modules/node-environment-flags/package.json b/WebServer/node_modules/node-environment-flags/package.json new file mode 100644 index 000000000..36327200a --- /dev/null +++ b/WebServer/node_modules/node-environment-flags/package.json @@ -0,0 +1,100 @@ +{ + "_args": [ + [ + "node-environment-flags@1.0.5", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "node-environment-flags@1.0.5", + "_id": "node-environment-flags@1.0.5", + "_inBundle": false, + "_integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "_location": "/node-environment-flags", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "node-environment-flags@1.0.5", + "name": "node-environment-flags", + "escapedName": "node-environment-flags", + "rawSpec": "1.0.5", + "saveSpec": null, + "fetchSpec": "1.0.5" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "_spec": "1.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Christopher Hiller", + "email": "boneskull@boneskull.com", + "url": "https://boneskull.com/" + }, + "bugs": { + "url": "https://github.com/boneskull/node-environment-flags/issues" + }, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + }, + "description": "> Polyfill/shim for `process.allowedNodeEnvironmentFlags`", + "devDependencies": { + "eslint": "^5.9.0", + "eslint-config-prettier": "^3.3.0", + "eslint-config-semistandard": "^13.0.0", + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-node": "^8.0.0", + "eslint-plugin-prettier": "^3.0.0", + "eslint-plugin-promise": "^4.0.1", + "eslint-plugin-standard": "^4.0.0", + "husky": "^1.1.3", + "lint-staged": "^8.0.4", + "mocha": "^5.2.0", + "nyc": "^13.3.0", + "prettier-eslint-cli": "^4.7.1", + "semantic-release": "^15.13.8", + "travis-deploy-once": "^5.0.9", + "unexpected": "^10.39.1" + }, + "files": [ + "implementation.js", + "flags.json", + "index.js", + "polyfill.js", + "shim.js" + ], + "homepage": "https://github.com/boneskull/node-environment-flags#readme", + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "keywords": [], + "license": "Apache-2.0", + "lint-staged": { + "*.{js,json}": [ + "prettier-eslint --write", + "git add" + ] + }, + "main": "index.js", + "name": "node-environment-flags", + "prettier": { + "singleQuote": true, + "bracketSpacing": false + }, + "repository": { + "type": "git", + "url": "git+https://github.com/boneskull/node-environment-flags.git" + }, + "scripts": { + "format": "prettier-eslint --write \"*.js\" \"*.json\" \"test/**/*.js\"", + "semantic-release": "semantic-release", + "test": "mocha", + "travis-deploy-once": "travis-deploy-once" + }, + "version": "1.0.5" +} diff --git a/WebServer/node_modules/node-environment-flags/polyfill.js b/WebServer/node_modules/node-environment-flags/polyfill.js new file mode 100644 index 000000000..ccba9e242 --- /dev/null +++ b/WebServer/node_modules/node-environment-flags/polyfill.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = () => { + if (typeof process.allowedNodeEnvironmentFlags === 'object') { + return process.allowedNodeEnvironmentFlags; + } + return require('./implementation').allowedNodeEnvironmentFlags; +}; diff --git a/WebServer/node_modules/node-environment-flags/shim.js b/WebServer/node_modules/node-environment-flags/shim.js new file mode 100644 index 000000000..ed0e13ec9 --- /dev/null +++ b/WebServer/node_modules/node-environment-flags/shim.js @@ -0,0 +1,16 @@ +'use strict'; + +const getPolyfill = require('./polyfill'); + +module.exports = () => { + const polyfill = getPolyfill(); + if (polyfill !== process.allowedNodeEnvironmentFlags) { + Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { + writable: true, + enumerable: true, + configurable: true, + value: polyfill + }); + } + return polyfill; +}; diff --git a/WebServer/node_modules/npm-run-path/index.js b/WebServer/node_modules/npm-run-path/index.js new file mode 100644 index 000000000..56f31e471 --- /dev/null +++ b/WebServer/node_modules/npm-run-path/index.js @@ -0,0 +1,39 @@ +'use strict'; +const path = require('path'); +const pathKey = require('path-key'); + +module.exports = opts => { + opts = Object.assign({ + cwd: process.cwd(), + path: process.env[pathKey()] + }, opts); + + let prev; + let pth = path.resolve(opts.cwd); + const ret = []; + + while (prev !== pth) { + ret.push(path.join(pth, 'node_modules/.bin')); + prev = pth; + pth = path.resolve(pth, '..'); + } + + // ensure the running `node` binary is used + ret.push(path.dirname(process.execPath)); + + return ret.concat(opts.path).join(path.delimiter); +}; + +module.exports.env = opts => { + opts = Object.assign({ + env: process.env + }, opts); + + const env = Object.assign({}, opts.env); + const path = pathKey({env}); + + opts.path = env[path]; + env[path] = module.exports(opts); + + return env; +}; diff --git a/WebServer/node_modules/npm-run-path/package.json b/WebServer/node_modules/npm-run-path/package.json new file mode 100644 index 000000000..51fae5266 --- /dev/null +++ b/WebServer/node_modules/npm-run-path/package.json @@ -0,0 +1,80 @@ +{ + "_args": [ + [ + "npm-run-path@2.0.2", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "npm-run-path@2.0.2", + "_id": "npm-run-path@2.0.2", + "_inBundle": false, + "_integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "_location": "/npm-run-path", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "npm-run-path@2.0.2", + "name": "npm-run-path", + "escapedName": "npm-run-path", + "rawSpec": "2.0.2", + "saveSpec": null, + "fetchSpec": "2.0.2" + }, + "_requiredBy": [ + "/execa" + ], + "_resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "_spec": "2.0.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/npm-run-path/issues" + }, + "dependencies": { + "path-key": "^2.0.0" + }, + "description": "Get your PATH prepended with locally installed binaries", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/npm-run-path#readme", + "keywords": [ + "npm", + "run", + "path", + "package", + "bin", + "binary", + "binaries", + "script", + "cli", + "command-line", + "execute", + "executable" + ], + "license": "MIT", + "name": "npm-run-path", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/npm-run-path.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "2.0.2", + "xo": { + "esnext": true + } +} diff --git a/WebServer/node_modules/number-is-nan/index.js b/WebServer/node_modules/number-is-nan/index.js new file mode 100644 index 000000000..79be4b9cb --- /dev/null +++ b/WebServer/node_modules/number-is-nan/index.js @@ -0,0 +1,4 @@ +'use strict'; +module.exports = Number.isNaN || function (x) { + return x !== x; +}; diff --git a/WebServer/node_modules/number-is-nan/package.json b/WebServer/node_modules/number-is-nan/package.json new file mode 100644 index 000000000..2a416b690 --- /dev/null +++ b/WebServer/node_modules/number-is-nan/package.json @@ -0,0 +1,70 @@ +{ + "_args": [ + [ + "number-is-nan@1.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "number-is-nan@1.0.1", + "_id": "number-is-nan@1.0.1", + "_inBundle": false, + "_integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "_location": "/number-is-nan", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "number-is-nan@1.0.1", + "name": "number-is-nan", + "escapedName": "number-is-nan", + "rawSpec": "1.0.1", + "saveSpec": null, + "fetchSpec": "1.0.1" + }, + "_requiredBy": [ + "/wrap-ansi/is-fullwidth-code-point" + ], + "_resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "_spec": "1.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/number-is-nan/issues" + }, + "description": "ES2015 Number.isNaN() ponyfill", + "devDependencies": { + "ava": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/number-is-nan#readme", + "keywords": [ + "es2015", + "ecmascript", + "ponyfill", + "polyfill", + "shim", + "number", + "is", + "nan", + "not" + ], + "license": "MIT", + "name": "number-is-nan", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/number-is-nan.git" + }, + "scripts": { + "test": "ava" + }, + "version": "1.0.1" +} diff --git a/WebServer/node_modules/object-keys/implementation.js b/WebServer/node_modules/object-keys/implementation.js new file mode 100644 index 000000000..5b3298619 --- /dev/null +++ b/WebServer/node_modules/object-keys/implementation.js @@ -0,0 +1,122 @@ +'use strict'; + +var keysShim; +if (!Object.keys) { + // modified from https://github.com/es-shims/es5-shim + var has = Object.prototype.hasOwnProperty; + var toStr = Object.prototype.toString; + var isArgs = require('./isArguments'); // eslint-disable-line global-require + var isEnumerable = Object.prototype.propertyIsEnumerable; + var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); + var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); + var dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ]; + var equalsConstructorPrototype = function (o) { + var ctor = o.constructor; + return ctor && ctor.prototype === o; + }; + var excludedKeys = { + $applicationCache: true, + $console: true, + $external: true, + $frame: true, + $frameElement: true, + $frames: true, + $innerHeight: true, + $innerWidth: true, + $onmozfullscreenchange: true, + $onmozfullscreenerror: true, + $outerHeight: true, + $outerWidth: true, + $pageXOffset: true, + $pageYOffset: true, + $parent: true, + $scrollLeft: true, + $scrollTop: true, + $scrollX: true, + $scrollY: true, + $self: true, + $webkitIndexedDB: true, + $webkitStorageInfo: true, + $window: true + }; + var hasAutomationEqualityBug = (function () { + /* global window */ + if (typeof window === 'undefined') { return false; } + for (var k in window) { + try { + if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { + try { + equalsConstructorPrototype(window[k]); + } catch (e) { + return true; + } + } + } catch (e) { + return true; + } + } + return false; + }()); + var equalsConstructorPrototypeIfNotBuggy = function (o) { + /* global window */ + if (typeof window === 'undefined' || !hasAutomationEqualityBug) { + return equalsConstructorPrototype(o); + } + try { + return equalsConstructorPrototype(o); + } catch (e) { + return false; + } + }; + + keysShim = function keys(object) { + var isObject = object !== null && typeof object === 'object'; + var isFunction = toStr.call(object) === '[object Function]'; + var isArguments = isArgs(object); + var isString = isObject && toStr.call(object) === '[object String]'; + var theKeys = []; + + if (!isObject && !isFunction && !isArguments) { + throw new TypeError('Object.keys called on a non-object'); + } + + var skipProto = hasProtoEnumBug && isFunction; + if (isString && object.length > 0 && !has.call(object, 0)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)); + } + } + + if (isArguments && object.length > 0) { + for (var j = 0; j < object.length; ++j) { + theKeys.push(String(j)); + } + } else { + for (var name in object) { + if (!(skipProto && name === 'prototype') && has.call(object, name)) { + theKeys.push(String(name)); + } + } + } + + if (hasDontEnumBug) { + var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); + + for (var k = 0; k < dontEnums.length; ++k) { + if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { + theKeys.push(dontEnums[k]); + } + } + } + return theKeys; + }; +} +module.exports = keysShim; diff --git a/WebServer/node_modules/object-keys/index.js b/WebServer/node_modules/object-keys/index.js new file mode 100644 index 000000000..a43807d2f --- /dev/null +++ b/WebServer/node_modules/object-keys/index.js @@ -0,0 +1,32 @@ +'use strict'; + +var slice = Array.prototype.slice; +var isArgs = require('./isArguments'); + +var origKeys = Object.keys; +var keysShim = origKeys ? function keys(o) { return origKeys(o); } : require('./implementation'); + +var originalKeys = Object.keys; + +keysShim.shim = function shimObjectKeys() { + if (Object.keys) { + var keysWorksWithArguments = (function () { + // Safari 5.0 bug + var args = Object.keys(arguments); + return args && args.length === arguments.length; + }(1, 2)); + if (!keysWorksWithArguments) { + Object.keys = function keys(object) { // eslint-disable-line func-name-matching + if (isArgs(object)) { + return originalKeys(slice.call(object)); + } + return originalKeys(object); + }; + } + } else { + Object.keys = keysShim; + } + return Object.keys || keysShim; +}; + +module.exports = keysShim; diff --git a/WebServer/node_modules/object-keys/isArguments.js b/WebServer/node_modules/object-keys/isArguments.js new file mode 100644 index 000000000..f2a2a9014 --- /dev/null +++ b/WebServer/node_modules/object-keys/isArguments.js @@ -0,0 +1,17 @@ +'use strict'; + +var toStr = Object.prototype.toString; + +module.exports = function isArguments(value) { + var str = toStr.call(value); + var isArgs = str === '[object Arguments]'; + if (!isArgs) { + isArgs = str !== '[object Array]' && + value !== null && + typeof value === 'object' && + typeof value.length === 'number' && + value.length >= 0 && + toStr.call(value.callee) === '[object Function]'; + } + return isArgs; +}; diff --git a/WebServer/node_modules/object-keys/package.json b/WebServer/node_modules/object-keys/package.json new file mode 100644 index 000000000..f82a0b40f --- /dev/null +++ b/WebServer/node_modules/object-keys/package.json @@ -0,0 +1,122 @@ +{ + "_args": [ + [ + "object-keys@1.1.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "object-keys@1.1.1", + "_id": "object-keys@1.1.1", + "_inBundle": false, + "_integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "_location": "/object-keys", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "object-keys@1.1.1", + "name": "object-keys", + "escapedName": "object-keys", + "rawSpec": "1.1.1", + "saveSpec": null, + "fetchSpec": "1.1.1" + }, + "_requiredBy": [ + "/define-properties", + "/es-abstract", + "/object.assign" + ], + "_resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "_spec": "1.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes" + }, + "bugs": { + "url": "https://github.com/ljharb/object-keys/issues" + }, + "contributors": [ + { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes" + }, + { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net" + }, + { + "name": "Ivan Starkov", + "email": "istarkov@gmail.com" + }, + { + "name": "Gary Katsevman", + "email": "git@gkatsev.com" + } + ], + "dependencies": {}, + "description": "An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim", + "devDependencies": { + "@ljharb/eslint-config": "^13.1.1", + "covert": "^1.1.1", + "eslint": "^5.13.0", + "foreach": "^2.0.5", + "indexof": "^0.0.1", + "is": "^3.3.0", + "tape": "^4.9.2" + }, + "engines": { + "node": ">= 0.4" + }, + "homepage": "https://github.com/ljharb/object-keys#readme", + "keywords": [ + "Object.keys", + "keys", + "ES5", + "shim" + ], + "license": "MIT", + "main": "index.js", + "name": "object-keys", + "repository": { + "type": "git", + "url": "git://github.com/ljharb/object-keys.git" + }, + "scripts": { + "audit": "npm audit", + "coverage": "covert test/*.js", + "coverage-quiet": "covert test/*.js --quiet", + "lint": "eslint .", + "postaudit": "rm package-lock.json", + "posttest": "npm run --silent audit", + "preaudit": "npm install --package-lock --package-lock-only", + "pretest": "npm run --silent lint", + "test": "npm run --silent tests-only", + "tests-only": "node test/index.js" + }, + "testling": { + "files": "test/index.js", + "browsers": [ + "iexplore/6.0..latest", + "firefox/3.0..6.0", + "firefox/15.0..latest", + "firefox/nightly", + "chrome/4.0..10.0", + "chrome/20.0..latest", + "chrome/canary", + "opera/10.0..latest", + "opera/next", + "safari/4.0..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2" + ] + }, + "version": "1.1.1" +} diff --git a/WebServer/node_modules/object-keys/test/index.js b/WebServer/node_modules/object-keys/test/index.js new file mode 100644 index 000000000..5402465a1 --- /dev/null +++ b/WebServer/node_modules/object-keys/test/index.js @@ -0,0 +1,5 @@ +'use strict'; + +require('./isArguments'); + +require('./shim'); diff --git a/WebServer/node_modules/object.assign/auto.js b/WebServer/node_modules/object.assign/auto.js new file mode 100644 index 000000000..8ebf606cb --- /dev/null +++ b/WebServer/node_modules/object.assign/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/WebServer/node_modules/object.assign/dist/browser.js b/WebServer/node_modules/object.assign/dist/browser.js new file mode 100644 index 000000000..1b8da6118 --- /dev/null +++ b/WebServer/node_modules/object.assign/dist/browser.js @@ -0,0 +1,492 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 2 ? arguments[2] : {}; + var props = keys(map); + if (hasSymbols) { + props = props.concat(Object.getOwnPropertySymbols(map)); + } + foreach(props, function (name) { + defineProperty(object, name, map[name], predicates[name]); + }); +}; + +defineProperties.supportsDescriptors = !!supportsDescriptors; + +module.exports = defineProperties; + +},{"foreach":5,"object-keys":9}],5:[function(require,module,exports){ + +var hasOwn = Object.prototype.hasOwnProperty; +var toString = Object.prototype.toString; + +module.exports = function forEach (obj, fn, ctx) { + if (toString.call(fn) !== '[object Function]') { + throw new TypeError('iterator must be a function'); + } + var l = obj.length; + if (l === +l) { + for (var i = 0; i < l; i++) { + fn.call(ctx, obj[i], i, obj); + } + } else { + for (var k in obj) { + if (hasOwn.call(obj, k)) { + fn.call(ctx, obj[k], k, obj); + } + } + } +}; + + +},{}],6:[function(require,module,exports){ +'use strict'; + +/* eslint no-invalid-this: 1 */ + +var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; +var slice = Array.prototype.slice; +var toStr = Object.prototype.toString; +var funcType = '[object Function]'; + +module.exports = function bind(that) { + var target = this; + if (typeof target !== 'function' || toStr.call(target) !== funcType) { + throw new TypeError(ERROR_MESSAGE + target); + } + var args = slice.call(arguments, 1); + + var bound; + var binder = function () { + if (this instanceof bound) { + var result = target.apply( + this, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + } + }; + + var boundLength = Math.max(0, target.length - args.length); + var boundArgs = []; + for (var i = 0; i < boundLength; i++) { + boundArgs.push('$' + i); + } + + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + + if (target.prototype) { + var Empty = function Empty() {}; + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + + return bound; +}; + +},{}],7:[function(require,module,exports){ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = Function.prototype.bind || implementation; + +},{"./implementation":6}],8:[function(require,module,exports){ +'use strict'; + +/* eslint complexity: [2, 17], max-statements: [2, 33] */ +module.exports = function hasSymbols() { + if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } + if (typeof Symbol.iterator === 'symbol') { return true; } + + var obj = {}; + var sym = Symbol('test'); + var symObj = Object(sym); + if (typeof sym === 'string') { return false; } + + if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } + if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } + + // temp disabled per https://github.com/ljharb/object.assign/issues/17 + // if (sym instanceof Symbol) { return false; } + // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 + // if (!(symObj instanceof Symbol)) { return false; } + + // if (typeof Symbol.prototype.toString !== 'function') { return false; } + // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } + + var symVal = 42; + obj[sym] = symVal; + for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax + if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } + + if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } + + var syms = Object.getOwnPropertySymbols(obj); + if (syms.length !== 1 || syms[0] !== sym) { return false; } + + if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } + + if (typeof Object.getOwnPropertyDescriptor === 'function') { + var descriptor = Object.getOwnPropertyDescriptor(obj, sym); + if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } + } + + return true; +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +// modified from https://github.com/es-shims/es5-shim +var has = Object.prototype.hasOwnProperty; +var toStr = Object.prototype.toString; +var slice = Array.prototype.slice; +var isArgs = require('./isArguments'); +var isEnumerable = Object.prototype.propertyIsEnumerable; +var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); +var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); +var dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' +]; +var equalsConstructorPrototype = function (o) { + var ctor = o.constructor; + return ctor && ctor.prototype === o; +}; +var excludedKeys = { + $console: true, + $external: true, + $frame: true, + $frameElement: true, + $frames: true, + $innerHeight: true, + $innerWidth: true, + $outerHeight: true, + $outerWidth: true, + $pageXOffset: true, + $pageYOffset: true, + $parent: true, + $scrollLeft: true, + $scrollTop: true, + $scrollX: true, + $scrollY: true, + $self: true, + $webkitIndexedDB: true, + $webkitStorageInfo: true, + $window: true +}; +var hasAutomationEqualityBug = (function () { + /* global window */ + if (typeof window === 'undefined') { return false; } + for (var k in window) { + try { + if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { + try { + equalsConstructorPrototype(window[k]); + } catch (e) { + return true; + } + } + } catch (e) { + return true; + } + } + return false; +}()); +var equalsConstructorPrototypeIfNotBuggy = function (o) { + /* global window */ + if (typeof window === 'undefined' || !hasAutomationEqualityBug) { + return equalsConstructorPrototype(o); + } + try { + return equalsConstructorPrototype(o); + } catch (e) { + return false; + } +}; + +var keysShim = function keys(object) { + var isObject = object !== null && typeof object === 'object'; + var isFunction = toStr.call(object) === '[object Function]'; + var isArguments = isArgs(object); + var isString = isObject && toStr.call(object) === '[object String]'; + var theKeys = []; + + if (!isObject && !isFunction && !isArguments) { + throw new TypeError('Object.keys called on a non-object'); + } + + var skipProto = hasProtoEnumBug && isFunction; + if (isString && object.length > 0 && !has.call(object, 0)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)); + } + } + + if (isArguments && object.length > 0) { + for (var j = 0; j < object.length; ++j) { + theKeys.push(String(j)); + } + } else { + for (var name in object) { + if (!(skipProto && name === 'prototype') && has.call(object, name)) { + theKeys.push(String(name)); + } + } + } + + if (hasDontEnumBug) { + var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); + + for (var k = 0; k < dontEnums.length; ++k) { + if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { + theKeys.push(dontEnums[k]); + } + } + } + return theKeys; +}; + +keysShim.shim = function shimObjectKeys() { + if (Object.keys) { + var keysWorksWithArguments = (function () { + // Safari 5.0 bug + return (Object.keys(arguments) || '').length === 2; + }(1, 2)); + if (!keysWorksWithArguments) { + var originalKeys = Object.keys; + Object.keys = function keys(object) { + if (isArgs(object)) { + return originalKeys(slice.call(object)); + } else { + return originalKeys(object); + } + }; + } + } else { + Object.keys = keysShim; + } + return Object.keys || keysShim; +}; + +module.exports = keysShim; + +},{"./isArguments":10}],10:[function(require,module,exports){ +'use strict'; + +var toStr = Object.prototype.toString; + +module.exports = function isArguments(value) { + var str = toStr.call(value); + var isArgs = str === '[object Arguments]'; + if (!isArgs) { + isArgs = str !== '[object Array]' && + value !== null && + typeof value === 'object' && + typeof value.length === 'number' && + value.length >= 0 && + toStr.call(value.callee) === '[object Function]'; + } + return isArgs; +}; + +},{}],11:[function(require,module,exports){ +'use strict'; + +var implementation = require('./implementation'); + +var lacksProperEnumerationOrder = function () { + if (!Object.assign) { + return false; + } + // v8, specifically in node 4.x, has a bug with incorrect property enumeration order + // note: this does not detect the bug unless there's 20 characters + var str = 'abcdefghijklmnopqrst'; + var letters = str.split(''); + var map = {}; + for (var i = 0; i < letters.length; ++i) { + map[letters[i]] = letters[i]; + } + var obj = Object.assign({}, map); + var actual = ''; + for (var k in obj) { + actual += k; + } + return str !== actual; +}; + +var assignHasPendingExceptions = function () { + if (!Object.assign || !Object.preventExtensions) { + return false; + } + // Firefox 37 still has "pending exception" logic in its Object.assign implementation, + // which is 72% slower than our shim, and Firefox 40's native implementation. + var thrower = Object.preventExtensions({ 1: 2 }); + try { + Object.assign(thrower, 'xy'); + } catch (e) { + return thrower[1] === 'y'; + } + return false; +}; + +module.exports = function getPolyfill() { + if (!Object.assign) { + return implementation; + } + if (lacksProperEnumerationOrder()) { + return implementation; + } + if (assignHasPendingExceptions()) { + return implementation; + } + return Object.assign; +}; + +},{"./implementation":2}],12:[function(require,module,exports){ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +module.exports = function shimAssign() { + var polyfill = getPolyfill(); + define( + Object, + { assign: polyfill }, + { assign: function () { return Object.assign !== polyfill; } } + ); + return polyfill; +}; + +},{"./polyfill":11,"define-properties":4}]},{},[1]); diff --git a/WebServer/node_modules/object.assign/hasSymbols.js b/WebServer/node_modules/object.assign/hasSymbols.js new file mode 100644 index 000000000..f6e0db978 --- /dev/null +++ b/WebServer/node_modules/object.assign/hasSymbols.js @@ -0,0 +1,41 @@ +'use strict'; + +var keys = require('object-keys'); + +module.exports = function hasSymbols() { + if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } + if (typeof Symbol.iterator === 'symbol') { return true; } + + var obj = {}; + var sym = Symbol('test'); + var symObj = Object(sym); + if (typeof sym === 'string') { return false; } + + if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } + if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } + + // temp disabled per https://github.com/ljharb/object.assign/issues/17 + // if (sym instanceof Symbol) { return false; } + // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 + // if (!(symObj instanceof Symbol)) { return false; } + + var symVal = 42; + obj[sym] = symVal; + for (sym in obj) { return false; } + if (keys(obj).length !== 0) { return false; } + if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } + + if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } + + var syms = Object.getOwnPropertySymbols(obj); + if (syms.length !== 1 || syms[0] !== sym) { return false; } + + if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } + + if (typeof Object.getOwnPropertyDescriptor === 'function') { + var descriptor = Object.getOwnPropertyDescriptor(obj, sym); + if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } + } + + return true; +}; diff --git a/WebServer/node_modules/object.assign/implementation.js b/WebServer/node_modules/object.assign/implementation.js new file mode 100644 index 000000000..6db68e76a --- /dev/null +++ b/WebServer/node_modules/object.assign/implementation.js @@ -0,0 +1,41 @@ +'use strict'; + +// modified from https://github.com/es-shims/es6-shim +var keys = require('object-keys'); +var bind = require('function-bind'); +var canBeObject = function (obj) { + return typeof obj !== 'undefined' && obj !== null; +}; +var hasSymbols = require('has-symbols/shams')(); +var toObject = Object; +var push = bind.call(Function.call, Array.prototype.push); +var propIsEnumerable = bind.call(Function.call, Object.prototype.propertyIsEnumerable); +var originalGetSymbols = hasSymbols ? Object.getOwnPropertySymbols : null; + +module.exports = function assign(target, source1) { + if (!canBeObject(target)) { throw new TypeError('target must be an object'); } + var objTarget = toObject(target); + var s, source, i, props, syms, value, key; + for (s = 1; s < arguments.length; ++s) { + source = toObject(arguments[s]); + props = keys(source); + var getSymbols = hasSymbols && (Object.getOwnPropertySymbols || originalGetSymbols); + if (getSymbols) { + syms = getSymbols(source); + for (i = 0; i < syms.length; ++i) { + key = syms[i]; + if (propIsEnumerable(source, key)) { + push(props, key); + } + } + } + for (i = 0; i < props.length; ++i) { + key = props[i]; + value = source[key]; + if (propIsEnumerable(source, key)) { + objTarget[key] = value; + } + } + } + return objTarget; +}; diff --git a/WebServer/node_modules/object.assign/index.js b/WebServer/node_modules/object.assign/index.js new file mode 100644 index 000000000..1111459cf --- /dev/null +++ b/WebServer/node_modules/object.assign/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var defineProperties = require('define-properties'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = getPolyfill(); + +defineProperties(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/WebServer/node_modules/object.assign/package.json b/WebServer/node_modules/object.assign/package.json new file mode 100644 index 000000000..ece457db7 --- /dev/null +++ b/WebServer/node_modules/object.assign/package.json @@ -0,0 +1,115 @@ +{ + "_args": [ + [ + "object.assign@4.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "object.assign@4.1.0", + "_id": "object.assign@4.1.0", + "_inBundle": false, + "_integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "_location": "/object.assign", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "object.assign@4.1.0", + "name": "object.assign", + "escapedName": "object.assign", + "rawSpec": "4.1.0", + "saveSpec": null, + "fetchSpec": "4.1.0" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "_spec": "4.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jordan Harband" + }, + "bugs": { + "url": "https://github.com/ljharb/object.assign/issues" + }, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "description": "ES6 spec-compliant Object.assign shim. From https://github.com/es-shims/es6-shim", + "devDependencies": { + "@es-shims/api": "^2.1.1", + "@ljharb/eslint-config": "^12.2.1", + "browserify": "^14.5.0", + "covert": "^1.1.0", + "eslint": "^4.13.1", + "for-each": "^0.3.2", + "is": "^3.2.1", + "jscs": "^3.0.7", + "nsp": "^3.1.0", + "tape": "^4.8.0" + }, + "engines": { + "node": ">= 0.4" + }, + "homepage": "https://github.com/ljharb/object.assign#readme", + "keywords": [ + "Object.assign", + "assign", + "ES6", + "extend", + "$.extend", + "jQuery", + "_.extend", + "Underscore", + "es-shim API", + "polyfill", + "shim" + ], + "license": "MIT", + "main": "index.js", + "name": "object.assign", + "repository": { + "type": "git", + "url": "git://github.com/ljharb/object.assign.git" + }, + "scripts": { + "build": "mkdir -p dist && browserify browserShim.js > dist/browser.js", + "coverage": "covert test/*.js", + "coverage:quiet": "covert test/*.js --quiet", + "eslint": "eslint *.js test/*.js", + "jscs": "jscs *.js test/*.js", + "lint": "npm run --silent jscs && npm run --silent eslint", + "posttest": "npm run --silent security", + "prepublish": "npm run --silent build", + "pretest": "npm run --silent lint && es-shim-api", + "security": "nsp check", + "test": "npm run --silent tests-only", + "test:implementation": "node test/index.js", + "test:native": "node test/native.js", + "test:shim": "node test/shimmed.js", + "tests-only": "npm run --silent test:implementation && npm run --silent test:shim" + }, + "testling": { + "files": "test/index.js", + "browsers": [ + "iexplore/6.0..latest", + "firefox/3.0..6.0", + "firefox/15.0..latest", + "firefox/nightly", + "chrome/4.0..10.0", + "chrome/20.0..latest", + "chrome/canary", + "opera/10.0..latest", + "opera/next", + "safari/4.0..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2" + ] + }, + "version": "4.1.0" +} diff --git a/WebServer/node_modules/object.assign/polyfill.js b/WebServer/node_modules/object.assign/polyfill.js new file mode 100644 index 000000000..5b6c5774e --- /dev/null +++ b/WebServer/node_modules/object.assign/polyfill.js @@ -0,0 +1,51 @@ +'use strict'; + +var implementation = require('./implementation'); + +var lacksProperEnumerationOrder = function () { + if (!Object.assign) { + return false; + } + // v8, specifically in node 4.x, has a bug with incorrect property enumeration order + // note: this does not detect the bug unless there's 20 characters + var str = 'abcdefghijklmnopqrst'; + var letters = str.split(''); + var map = {}; + for (var i = 0; i < letters.length; ++i) { + map[letters[i]] = letters[i]; + } + var obj = Object.assign({}, map); + var actual = ''; + for (var k in obj) { + actual += k; + } + return str !== actual; +}; + +var assignHasPendingExceptions = function () { + if (!Object.assign || !Object.preventExtensions) { + return false; + } + // Firefox 37 still has "pending exception" logic in its Object.assign implementation, + // which is 72% slower than our shim, and Firefox 40's native implementation. + var thrower = Object.preventExtensions({ 1: 2 }); + try { + Object.assign(thrower, 'xy'); + } catch (e) { + return thrower[1] === 'y'; + } + return false; +}; + +module.exports = function getPolyfill() { + if (!Object.assign) { + return implementation; + } + if (lacksProperEnumerationOrder()) { + return implementation; + } + if (assignHasPendingExceptions()) { + return implementation; + } + return Object.assign; +}; diff --git a/WebServer/node_modules/object.assign/shim.js b/WebServer/node_modules/object.assign/shim.js new file mode 100644 index 000000000..9f896ae3c --- /dev/null +++ b/WebServer/node_modules/object.assign/shim.js @@ -0,0 +1,14 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +module.exports = function shimAssign() { + var polyfill = getPolyfill(); + define( + Object, + { assign: polyfill }, + { assign: function () { return Object.assign !== polyfill; } } + ); + return polyfill; +}; diff --git a/WebServer/node_modules/object.assign/test/index.js b/WebServer/node_modules/object.assign/test/index.js new file mode 100644 index 000000000..776b2b334 --- /dev/null +++ b/WebServer/node_modules/object.assign/test/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var assign = require('../'); +var test = require('tape'); +var runTests = require('./tests'); + +test('as a function', function (t) { + t.test('bad array/this value', function (st) { + st['throws'](function () { assign(undefined); }, TypeError, 'undefined is not an object'); + st['throws'](function () { assign(null); }, TypeError, 'null is not an object'); + st.end(); + }); + + runTests(assign, t); + + t.end(); +}); diff --git a/WebServer/node_modules/object.assign/test/native.js b/WebServer/node_modules/object.assign/test/native.js new file mode 100644 index 000000000..6ebd70bbd --- /dev/null +++ b/WebServer/node_modules/object.assign/test/native.js @@ -0,0 +1,47 @@ +'use strict'; + +var test = require('tape'); +var defineProperties = require('define-properties'); +var isEnumerable = Object.prototype.propertyIsEnumerable; +var functionsHaveNames = function f() {}.name === 'f'; + +var runTests = require('./tests'); + +test('native', function (t) { + t.equal(Object.assign.length, 2, 'Object.assign has a length of 2'); + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Object.assign.name, 'assign', 'Object.assign has name "assign"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Object, 'assign'), 'Object.assign is not enumerable'); + et.end(); + }); + + var supportsStrictMode = (function () { return typeof this === 'undefined'; }()); + + t.test('bad object value', { skip: !supportsStrictMode }, function (st) { + st['throws'](function () { return Object.assign(undefined); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Object.assign(null); }, TypeError, 'null is not an object'); + st.end(); + }); + + // v8 in node 0.8 and 0.10 have non-enumerable string properties + var stringCharsAreEnumerable = isEnumerable.call('xy', 0); + t.test('when Object.assign is present and has pending exceptions', { skip: !stringCharsAreEnumerable || !Object.preventExtensions }, function (st) { + // Firefox 37 still has "pending exception" logic in its Object.assign implementation, + // which is 72% slower than our shim, and Firefox 40's native implementation. + var thrower = Object.preventExtensions({ 1: '2' }); + var error; + try { Object.assign(thrower, 'xy'); } catch (e) { error = e; } + st.equal(error instanceof TypeError, true, 'error is TypeError'); + st.equal(thrower[1], '2', 'thrower[1] === "2"'); + + st.end(); + }); + + runTests(Object.assign, t); + + t.end(); +}); diff --git a/WebServer/node_modules/object.assign/test/shimmed.js b/WebServer/node_modules/object.assign/test/shimmed.js new file mode 100644 index 000000000..905b6531e --- /dev/null +++ b/WebServer/node_modules/object.assign/test/shimmed.js @@ -0,0 +1,50 @@ +'use strict'; + +var assign = require('../'); +assign.shim(); + +var test = require('tape'); +var defineProperties = require('define-properties'); +var isEnumerable = Object.prototype.propertyIsEnumerable; +var functionsHaveNames = function f() {}.name === 'f'; + +var runTests = require('./tests'); + +test('shimmed', function (t) { + t.equal(Object.assign.length, 2, 'Object.assign has a length of 2'); + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Object.assign.name, 'assign', 'Object.assign has name "assign"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Object, 'assign'), 'Object.assign is not enumerable'); + et.end(); + }); + + var supportsStrictMode = (function () { return typeof this === 'undefined'; }()); + + t.test('bad object value', { skip: !supportsStrictMode }, function (st) { + st['throws'](function () { return Object.assign(undefined); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Object.assign(null); }, TypeError, 'null is not an object'); + st.end(); + }); + + // v8 in node 0.8 and 0.10 have non-enumerable string properties + var stringCharsAreEnumerable = isEnumerable.call('xy', 0); + t.test('when Object.assign is present and has pending exceptions', { skip: !stringCharsAreEnumerable || !Object.preventExtensions }, function (st) { + // Firefox 37 still has "pending exception" logic in its Object.assign implementation, + // which is 72% slower than our shim, and Firefox 40's native implementation. + var thrower = Object.preventExtensions({ 1: '2' }); + var error; + try { Object.assign(thrower, 'xy'); } catch (e) { error = e; } + st.equal(error instanceof TypeError, true, 'error is TypeError'); + st.equal(thrower[1], '2', 'thrower[1] === "2"'); + + st.end(); + }); + + runTests(Object.assign, t); + + t.end(); +}); diff --git a/WebServer/node_modules/object.assign/test/tests.js b/WebServer/node_modules/object.assign/test/tests.js new file mode 100644 index 000000000..34f6f2c19 --- /dev/null +++ b/WebServer/node_modules/object.assign/test/tests.js @@ -0,0 +1,224 @@ +'use strict'; + +var hasSymbols = require('has-symbols/shams')(); +var forEach = require('for-each'); + +module.exports = function (assign, t) { + t.test('error cases', function (st) { + st['throws'](function () { assign(null); }, TypeError, 'target must be an object'); + st['throws'](function () { assign(undefined); }, TypeError, 'target must be an object'); + st['throws'](function () { assign(null, {}); }, TypeError, 'target must be an object'); + st['throws'](function () { assign(undefined, {}); }, TypeError, 'target must be an object'); + st.end(); + }); + + t.test('non-object target, no sources', function (st) { + var bool = assign(true); + st.equal(typeof bool, 'object', 'bool is object'); + st.equal(Boolean.prototype.valueOf.call(bool), true, 'bool coerces to `true`'); + + var number = assign(1); + st.equal(typeof number, 'object', 'number is object'); + st.equal(Number.prototype.valueOf.call(number), 1, 'number coerces to `1`'); + + var string = assign('1'); + st.equal(typeof string, 'object', 'number is object'); + st.equal(String.prototype.valueOf.call(string), '1', 'number coerces to `"1"`'); + + st.end(); + }); + + t.test('non-object target, with sources', function (st) { + var signal = {}; + + st.test('boolean', function (st2) { + var bool = assign(true, { a: signal }); + st2.equal(typeof bool, 'object', 'bool is object'); + st.equal(Boolean.prototype.valueOf.call(bool), true, 'bool coerces to `true`'); + st2.equal(bool.a, signal, 'source properties copied'); + st2.end(); + }); + + st.test('number', function (st2) { + var number = assign(1, { a: signal }); + st2.equal(typeof number, 'object', 'number is object'); + st2.equal(Number.prototype.valueOf.call(number), 1, 'number coerces to `1`'); + st2.equal(number.a, signal, 'source properties copied'); + st2.end(); + }); + + st.test('string', function (st2) { + var string = assign('1', { a: signal }); + st2.equal(typeof string, 'object', 'number is object'); + st2.equal(String.prototype.valueOf.call(string), '1', 'number coerces to `"1"`'); + st2.equal(string.a, signal, 'source properties copied'); + st2.end(); + }); + + st.end(); + }); + + t.test('non-object sources', function (st) { + st.deepEqual(assign({ a: 1 }, null, { b: 2 }), { a: 1, b: 2 }, 'ignores null source'); + st.deepEqual(assign({ a: 1 }, { b: 2 }, undefined), { a: 1, b: 2 }, 'ignores undefined source'); + st.end(); + }); + + t.test('returns the modified target object', function (st) { + var target = {}; + var returned = assign(target, { a: 1 }); + st.equal(returned, target, 'returned object is the same reference as the target object'); + st.end(); + }); + + t.test('has the right length', function (st) { + st.equal(assign.length, 2, 'length is 2 => 2 required arguments'); + st.end(); + }); + + t.test('merge two objects', function (st) { + var target = { a: 1 }; + var returned = assign(target, { b: 2 }); + st.deepEqual(returned, { a: 1, b: 2 }, 'returned object has properties from both'); + st.end(); + }); + + t.test('works with functions', function (st) { + var target = function () {}; + target.a = 1; + var returned = assign(target, { b: 2 }); + st.equal(target, returned, 'returned object is target'); + st.equal(returned.a, 1); + st.equal(returned.b, 2); + st.end(); + }); + + t.test('works with primitives', function (st) { + var target = 2; + var source = { b: 42 }; + var returned = assign(target, source); + st.equal(Object.prototype.toString.call(returned), '[object Number]', 'returned is object form of number primitive'); + st.equal(Number(returned), target, 'returned and target have same valueOf'); + st.equal(returned.b, source.b); + st.end(); + }); + + t.test('merge N objects', function (st) { + var target = { a: 1 }; + var source1 = { b: 2 }; + var source2 = { c: 3 }; + var returned = assign(target, source1, source2); + st.deepEqual(returned, { a: 1, b: 2, c: 3 }, 'returned object has properties from all sources'); + st.end(); + }); + + t.test('only iterates over own keys', function (st) { + var Foo = function () {}; + Foo.prototype.bar = true; + var foo = new Foo(); + foo.baz = true; + var target = { a: 1 }; + var returned = assign(target, foo); + st.equal(returned, target, 'returned object is the same reference as the target object'); + st.deepEqual(target, { a: 1, baz: true }, 'returned object has only own properties from both'); + st.end(); + }); + + t.test('includes enumerable symbols, after keys', { skip: !hasSymbols }, function (st) { + var visited = []; + var obj = {}; + Object.defineProperty(obj, 'a', { enumerable: true, get: function () { visited.push('a'); return 42; } }); + var symbol = Symbol('enumerable'); + Object.defineProperty(obj, symbol, { + enumerable: true, + get: function () { visited.push(symbol); return Infinity; } + }); + var nonEnumSymbol = Symbol('non-enumerable'); + Object.defineProperty(obj, nonEnumSymbol, { + enumerable: false, + get: function () { visited.push(nonEnumSymbol); return -Infinity; } + }); + var target = assign({}, obj); + st.deepEqual(visited, ['a', symbol], 'key is visited first, then symbol'); + st.equal(target.a, 42, 'target.a is 42'); + st.equal(target[symbol], Infinity, 'target[symbol] is Infinity'); + st.notEqual(target[nonEnumSymbol], -Infinity, 'target[nonEnumSymbol] is not -Infinity'); + st.end(); + }); + + t.test('does not fail when symbols are not present', function (st) { + var getSyms; + if (hasSymbols) { + getSyms = Object.getOwnPropertySymbols; + delete Object.getOwnPropertySymbols; + } + + var visited = []; + var obj = {}; + Object.defineProperty(obj, 'a', { enumerable: true, get: function () { visited.push('a'); return 42; } }); + var keys = ['a']; + if (hasSymbols) { + var symbol = Symbol('sym'); + Object.defineProperty(obj, symbol, { + enumerable: true, + get: function () { visited.push(symbol); return Infinity; } + }); + keys.push(symbol); + } + var target = assign({}, obj); + st.deepEqual(visited, keys, 'assign visits expected keys'); + st.equal(target.a, 42, 'target.a is 42'); + + if (hasSymbols) { + st.equal(target[symbol], Infinity); + + Object.getOwnPropertySymbols = getSyms; + } + st.end(); + }); + + t.test('preserves correct property enumeration order', function (st) { + var str = 'abcdefghijklmnopqrst'; + var letters = {}; + forEach(str.split(''), function (letter) { + letters[letter] = letter; + }); + + var n = 5; + st.comment('run the next test ' + n + ' times'); + var object = assign({}, letters); + var actual = ''; + for (var k in object) { + actual += k; + } + for (var i = 0; i < n; ++i) { + st.equal(actual, str, 'property enumeration order should be followed'); + } + st.end(); + }); + + t.test('checks enumerability and existence, in case of modification during [[Get]]', { skip: !Object.defineProperty }, function (st) { + var targetBvalue = {}; + var targetCvalue = {}; + var target = { b: targetBvalue, c: targetCvalue }; + var source = {}; + Object.defineProperty(source, 'a', { + enumerable: true, + get: function () { + delete this.b; + Object.defineProperty(this, 'c', { enumerable: false }); + return 'a'; + } + }); + var sourceBvalue = {}; + var sourceCvalue = {}; + source.b = sourceBvalue; + source.c = sourceCvalue; + var result = assign(target, source); + st.equal(result, target, 'sanity check: result is === target'); + st.equal(result.b, targetBvalue, 'target key not overwritten by deleted source key'); + st.equal(result.c, targetCvalue, 'target key not overwritten by non-enumerable source key'); + + st.end(); + }); +}; diff --git a/WebServer/node_modules/object.getownpropertydescriptors/.jscs.json b/WebServer/node_modules/object.getownpropertydescriptors/.jscs.json new file mode 100644 index 000000000..3d099c4b1 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/.jscs.json @@ -0,0 +1,176 @@ +{ + "es3": true, + + "additionalRules": [], + + "requireSemicolons": true, + + "disallowMultipleSpaces": true, + + "disallowIdentifierNames": [], + + "requireCurlyBraces": { + "allExcept": [], + "keywords": ["if", "else", "for", "while", "do", "try", "catch"] + }, + + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"], + + "disallowSpaceAfterKeywords": [], + + "disallowSpaceBeforeComma": true, + "disallowSpaceAfterComma": false, + "disallowSpaceBeforeSemicolon": true, + + "disallowNodeTypes": [ + "DebuggerStatement", + "ForInStatement", + "LabeledStatement", + "SwitchCase", + "SwitchStatement", + "WithStatement" + ], + + "requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] }, + + "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true }, + "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true }, + "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true }, + "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true }, + "disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true }, + + "requireSpaceBetweenArguments": true, + + "disallowSpacesInsideParentheses": true, + + "disallowSpacesInsideArrayBrackets": true, + + "disallowQuotedKeysInObjects": { "allExcept": ["reserved"] }, + + "disallowSpaceAfterObjectKeys": true, + + "requireCommaBeforeLineBreak": true, + + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "requireSpaceAfterPrefixUnaryOperators": [], + + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "requireSpaceBeforePostfixUnaryOperators": [], + + "disallowSpaceBeforeBinaryOperators": [], + "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + + "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], + "disallowSpaceAfterBinaryOperators": [], + + "disallowImplicitTypeConversion": ["binary", "string"], + + "disallowKeywords": ["with", "eval"], + + "requireKeywordsOnNewLine": [], + "disallowKeywordsOnNewLine": ["else"], + + "requireLineFeedAtFileEnd": true, + + "disallowTrailingWhitespace": true, + + "disallowTrailingComma": true, + + "excludeFiles": ["node_modules/**", "vendor/**"], + + "disallowMultipleLineStrings": true, + + "requireDotNotation": { "allExcept": ["keywords"] }, + + "requireParenthesesAroundIIFE": true, + + "validateLineBreaks": "LF", + + "validateQuoteMarks": { + "escape": true, + "mark": "'" + }, + + "disallowOperatorBeforeLineBreak": [], + + "requireSpaceBeforeKeywords": [ + "do", + "for", + "if", + "else", + "switch", + "case", + "try", + "catch", + "finally", + "while", + "with", + "return" + ], + + "validateAlignedFunctionParameters": { + "lineBreakAfterOpeningBraces": true, + "lineBreakBeforeClosingBraces": true + }, + + "requirePaddingNewLinesBeforeExport": true, + + "validateNewlineAfterArrayElements": { + "maximum": 1 + }, + + "requirePaddingNewLinesAfterUseStrict": true, + + "disallowArrowFunctions": true, + + "disallowMultiLineTernary": true, + + "validateOrderInObjectKeys": "asc-insensitive", + + "disallowIdenticalDestructuringNames": true, + + "disallowNestedTernaries": { "maxLevel": 1 }, + + "requireSpaceAfterComma": { "allExcept": ["trailing"] }, + "requireAlignedMultilineParams": false, + + "requireSpacesInGenerator": { + "afterStar": true + }, + + "disallowSpacesInGenerator": { + "beforeStar": true + }, + + "disallowVar": false, + + "requireArrayDestructuring": false, + + "requireEnhancedObjectLiterals": false, + + "requireObjectDestructuring": false, + + "requireEarlyReturn": false, + + "requireCapitalizedConstructorsNew": { + "allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array"] + }, + + "requireImportAlphabetized": false, + + "requireSpaceBeforeObjectValues": true, + "requireSpaceBeforeDestructuredValues": true, + + "disallowSpacesInsideTemplateStringPlaceholders": true, + + "disallowArrayDestructuringReturn": false, + + "requireNewlineBeforeSingleStatementsInIf": false, + + "disallowUnusedVariables": true, + + "requireSpacesInsideImportedObjectBraces": true, + + "requireUseStrict": true +} + diff --git a/WebServer/node_modules/object.getownpropertydescriptors/implementation.js b/WebServer/node_modules/object.getownpropertydescriptors/implementation.js new file mode 100644 index 000000000..784c22c99 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/implementation.js @@ -0,0 +1,44 @@ +'use strict'; + +var ES = require('es-abstract/es7'); + +var defineProperty = Object.defineProperty; +var getDescriptor = Object.getOwnPropertyDescriptor; +var getOwnNames = Object.getOwnPropertyNames; +var getSymbols = Object.getOwnPropertySymbols; +var concat = Function.call.bind(Array.prototype.concat); +var reduce = Function.call.bind(Array.prototype.reduce); +var getAll = getSymbols ? function (obj) { + return concat(getOwnNames(obj), getSymbols(obj)); +} : getOwnNames; + +var isES5 = ES.IsCallable(getDescriptor) && ES.IsCallable(getOwnNames); + +var safePut = function put(obj, prop, val) { // eslint-disable-line max-params + if (defineProperty && prop in obj) { + defineProperty(obj, prop, { + configurable: true, + enumerable: true, + value: val, + writable: true + }); + } else { + obj[prop] = val; + } +}; + +module.exports = function getOwnPropertyDescriptors(value) { + ES.RequireObjectCoercible(value); + if (!isES5) { + throw new TypeError('getOwnPropertyDescriptors requires Object.getOwnPropertyDescriptor'); + } + + var O = ES.ToObject(value); + return reduce(getAll(O), function (acc, key) { + var descriptor = getDescriptor(O, key); + if (typeof descriptor !== 'undefined') { + safePut(acc, key, descriptor); + } + return acc; + }, {}); +}; diff --git a/WebServer/node_modules/object.getownpropertydescriptors/index.js b/WebServer/node_modules/object.getownpropertydescriptors/index.js new file mode 100644 index 000000000..bf2aec5d5 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/index.js @@ -0,0 +1,15 @@ +'use strict'; + +var define = require('define-properties'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +define(implementation, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = implementation; diff --git a/WebServer/node_modules/object.getownpropertydescriptors/package.json b/WebServer/node_modules/object.getownpropertydescriptors/package.json new file mode 100644 index 000000000..ef3c1bcd2 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/package.json @@ -0,0 +1,110 @@ +{ + "_args": [ + [ + "object.getownpropertydescriptors@2.0.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "object.getownpropertydescriptors@2.0.3", + "_id": "object.getownpropertydescriptors@2.0.3", + "_inBundle": false, + "_integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "_location": "/object.getownpropertydescriptors", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "object.getownpropertydescriptors@2.0.3", + "name": "object.getownpropertydescriptors", + "escapedName": "object.getownpropertydescriptors", + "rawSpec": "2.0.3", + "saveSpec": null, + "fetchSpec": "2.0.3" + }, + "_requiredBy": [ + "/node-environment-flags" + ], + "_resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "_spec": "2.0.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jordan Harband" + }, + "bugs": { + "url": "https://github.com/ljharb/object.getownpropertydescriptors/issues" + }, + "dependencies": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + }, + "description": "ES2017 spec-compliant shim for `Object.getOwnPropertyDescriptors` that works in ES5.", + "devDependencies": { + "@es-shims/api": "^1.2.0", + "@ljharb/eslint-config": "^6.0.0", + "covert": "^1.1.0", + "eslint": "^3.1.1", + "jscs": "^3.0.7", + "nsp": "^2.6.1", + "replace": "^0.3.0", + "semver": "^5.3.0", + "tape": "^4.6.0" + }, + "engines": { + "node": ">= 0.8" + }, + "homepage": "https://github.com/ljharb/object.getownpropertydescriptors#readme", + "keywords": [ + "Object.getOwnPropertyDescriptors", + "descriptor", + "property descriptor", + "ES8", + "ES2017", + "shim", + "polyfill", + "getOwnPropertyDescriptor", + "es-shim API" + ], + "license": "MIT", + "main": "index.js", + "name": "object.getownpropertydescriptors", + "repository": { + "type": "git", + "url": "git://github.com/ljharb/object.getownpropertydescriptors.git" + }, + "scripts": { + "coverage": "covert test/*.js", + "coverage:quiet": "covert test/*.js --quiet", + "eslint": "eslint test/*.js *.js", + "jscs": "jscs test/*.js *.js", + "lint": "npm run --silent jscs && npm run --silent eslint", + "posttest": "npm run --silent security", + "pretest": "npm run --silent lint && es-shim-api", + "security": "nsp check", + "test": "npm run --silent tests-only", + "test:module": "node test/index.js", + "test:shimmed": "node test/shimmed.js", + "tests-only": "npm run --silent test:shimmed && npm run --silent test:module" + }, + "testling": { + "files": [ + "test/index.js", + "test/shimmed.js" + ], + "browsers": [ + "iexplore/9.0..latest", + "firefox/4.0..6.0", + "firefox/15.0..latest", + "firefox/nightly", + "chrome/5.0..10.0", + "chrome/20.0..latest", + "chrome/canary", + "opera/12.0..latest", + "opera/next", + "safari/5.0..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2" + ] + }, + "version": "2.0.3" +} diff --git a/WebServer/node_modules/object.getownpropertydescriptors/polyfill.js b/WebServer/node_modules/object.getownpropertydescriptors/polyfill.js new file mode 100644 index 000000000..0424acfb1 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/polyfill.js @@ -0,0 +1,7 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Object.getOwnPropertyDescriptors === 'function' ? Object.getOwnPropertyDescriptors : implementation; +}; diff --git a/WebServer/node_modules/object.getownpropertydescriptors/shim.js b/WebServer/node_modules/object.getownpropertydescriptors/shim.js new file mode 100644 index 000000000..799c7d3c2 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/shim.js @@ -0,0 +1,14 @@ +'use strict'; + +var getPolyfill = require('./polyfill'); +var define = require('define-properties'); + +module.exports = function shimGetOwnPropertyDescriptors() { + var polyfill = getPolyfill(); + define( + Object, + { getOwnPropertyDescriptors: polyfill }, + { getOwnPropertyDescriptors: function () { return Object.getOwnPropertyDescriptors !== polyfill; } } + ); + return polyfill; +}; diff --git a/WebServer/node_modules/object.getownpropertydescriptors/test/index.js b/WebServer/node_modules/object.getownpropertydescriptors/test/index.js new file mode 100644 index 000000000..618a20506 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/test/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var getDescriptors = require('../'); +var test = require('tape'); +var runTests = require('./tests'); + +test('as a function', function (t) { + t.test('bad object/this value', function (st) { + st.throws(function () { return getDescriptors(undefined); }, TypeError, 'undefined is not an object'); + st.throws(function () { return getDescriptors(null); }, TypeError, 'null is not an object'); + st.end(); + }); + + runTests(getDescriptors, t); + + t.end(); +}); diff --git a/WebServer/node_modules/object.getownpropertydescriptors/test/shimmed.js b/WebServer/node_modules/object.getownpropertydescriptors/test/shimmed.js new file mode 100644 index 000000000..c9af4c1d3 --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/test/shimmed.js @@ -0,0 +1,35 @@ +'use strict'; + +var getDescriptors = require('../'); +getDescriptors.shim(); + +var test = require('tape'); +var defineProperties = require('define-properties'); +var runTests = require('./tests'); +var isEnumerable = Object.prototype.propertyIsEnumerable; +var functionsHaveNames = function f() {}.name === 'f'; + +test('shimmed', function (t) { + t.equal(Object.getOwnPropertyDescriptors.length, 1, 'Object.getOwnPropertyDescriptors has a length of 1'); + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Object.getOwnPropertyDescriptors.name, 'getOwnPropertyDescriptors', 'Object.getOwnPropertyDescriptors has name "getOwnPropertyDescriptors"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Object, 'getOwnPropertyDescriptors'), 'Object.getOwnPropertyDescriptors is not enumerable'); + et.end(); + }); + + var supportsStrictMode = (function () { return typeof this === 'undefined'; }()); + + t.test('bad object/this value', { skip: !supportsStrictMode }, function (st) { + st.throws(function () { return getDescriptors(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st.throws(function () { return getDescriptors(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + runTests(Object.getOwnPropertyDescriptors, t); + + t.end(); +}); diff --git a/WebServer/node_modules/object.getownpropertydescriptors/test/tests.js b/WebServer/node_modules/object.getownpropertydescriptors/test/tests.js new file mode 100644 index 000000000..b9aa29edf --- /dev/null +++ b/WebServer/node_modules/object.getownpropertydescriptors/test/tests.js @@ -0,0 +1,127 @@ +'use strict'; + +module.exports = function (getDescriptors, t) { + var enumDescriptor = { + configurable: true, + enumerable: false, + value: true, + writable: false + }; + var writableDescriptor = { + configurable: true, + enumerable: true, + value: 42, + writable: true + }; + + t.test('works with Object.prototype poisoned setter', { skip: !Object.defineProperty }, function (st) { + var key = 'foo'; + + var obj = {}; + obj[key] = 42; + + var expected = {}; + expected[key] = { + configurable: true, + enumerable: true, + value: 42, + writable: true + }; + + /* eslint-disable no-extend-native, accessor-pairs */ + Object.defineProperty(Object.prototype, key, { configurable: true, set: function (v) { throw new Error(v); } }); + /* eslint-enable no-extend-native, accessor-pairs */ + + var hasOwnNamesBug = false; + try { + Object.getOwnPropertyNames(obj); + } catch (e) { + // v8 in node 0.6 - 0.12 has a bug :-( + hasOwnNamesBug = true; + st.comment('SKIP: this engine has a bug with Object.getOwnPropertyNames: it can not handle a throwing setter on Object.prototype.'); + } + + if (!hasOwnNamesBug) { + st.doesNotThrow(function () { + var result = getDescriptors(obj); + st.deepEqual(result, expected, 'got expected descriptors'); + }); + } + + /* eslint-disable no-extend-native */ + delete Object.prototype[key]; + /* eslint-enable no-extend-native */ + st.end(); + }); + + t.test('gets all expected non-Symbol descriptors', function (st) { + var obj = { normal: Infinity }; + Object.defineProperty(obj, 'enumerable', enumDescriptor); + Object.defineProperty(obj, 'writable', writableDescriptor); + + var descriptors = getDescriptors(obj); + + st.deepEqual(descriptors, { + enumerable: enumDescriptor, + normal: { + configurable: true, + enumerable: true, + value: Infinity, + writable: true + }, + writable: writableDescriptor + }); + st.end(); + }); + + var supportsSymbols = typeof Symbol === 'function' && typeof Symbol() === 'symbol'; + t.test('gets Symbol descriptors too', { skip: !supportsSymbols }, function (st) { + var symbol = Symbol(); + var symDescriptor = { + configurable: false, + enumerable: true, + value: [symbol], + writable: true + }; + var obj = { normal: Infinity }; + Object.defineProperty(obj, 'enumerable', enumDescriptor); + Object.defineProperty(obj, 'writable', writableDescriptor); + Object.defineProperty(obj, 'symbol', symDescriptor); + + var descriptors = getDescriptors(obj); + + st.deepEqual(descriptors, { + enumerable: enumDescriptor, + normal: { + configurable: true, + enumerable: true, + value: Infinity, + writable: true + }, + symbol: symDescriptor, + writable: writableDescriptor + }); + st.end(); + }); + + /* global Proxy */ + var supportsProxy = typeof Proxy === 'function'; + t.test('Proxies that return an undefined descriptor', { skip: !supportsProxy }, function (st) { + var obj = { foo: true }; + var fooDescriptor = Object.getOwnPropertyDescriptor(obj, 'foo'); + + var proxy = new Proxy(obj, { + getOwnPropertyDescriptor: function (target, key) { + return Object.getOwnPropertyDescriptor(target, key); + }, + ownKeys: function () { + return [ + 'foo', + 'bar' + ]; + } + }); + st.deepEqual(getDescriptors(proxy), { foo: fooDescriptor }, 'object has no descriptors'); + st.end(); + }); +}; diff --git a/WebServer/node_modules/once/once.js b/WebServer/node_modules/once/once.js new file mode 100644 index 000000000..235406736 --- /dev/null +++ b/WebServer/node_modules/once/once.js @@ -0,0 +1,42 @@ +var wrappy = require('wrappy') +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} diff --git a/WebServer/node_modules/once/package.json b/WebServer/node_modules/once/package.json new file mode 100644 index 000000000..bd1f0986c --- /dev/null +++ b/WebServer/node_modules/once/package.json @@ -0,0 +1,72 @@ +{ + "_args": [ + [ + "once@1.4.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "once@1.4.0", + "_id": "once@1.4.0", + "_inBundle": false, + "_integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "_location": "/once", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "once@1.4.0", + "name": "once", + "escapedName": "once", + "rawSpec": "1.4.0", + "saveSpec": null, + "fetchSpec": "1.4.0" + }, + "_requiredBy": [ + "/end-of-stream", + "/glob", + "/inflight", + "/pump" + ], + "_resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "_spec": "1.4.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/isaacs/once/issues" + }, + "dependencies": { + "wrappy": "1" + }, + "description": "Run a function exactly one time", + "devDependencies": { + "tap": "^7.0.1" + }, + "directories": { + "test": "test" + }, + "files": [ + "once.js" + ], + "homepage": "https://github.com/isaacs/once#readme", + "keywords": [ + "once", + "function", + "one", + "single" + ], + "license": "ISC", + "main": "once.js", + "name": "once", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/once.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "version": "1.4.0" +} diff --git a/WebServer/node_modules/open/index.js b/WebServer/node_modules/open/index.js new file mode 100644 index 000000000..feea5b5b4 --- /dev/null +++ b/WebServer/node_modules/open/index.js @@ -0,0 +1,134 @@ +'use strict'; +const {promisify} = require('util'); +const path = require('path'); +const childProcess = require('child_process'); +const fs = require('fs'); +const isWsl = require('is-wsl'); + +const pAccess = promisify(fs.access); +const pExecFile = promisify(childProcess.execFile); + +// Path to included `xdg-open` +const localXdgOpenPath = path.join(__dirname, 'xdg-open'); + +// Convert a path from WSL format to Windows format: +// `/mnt/c/Program Files/Example/MyApp.exe` → `C:\Program Files\Example\MyApp.exe` +const wslToWindowsPath = async path => { + const {stdout} = await pExecFile('wslpath', ['-w', path]); + return stdout.trim(); +}; + +module.exports = async (target, options) => { + if (typeof target !== 'string') { + throw new TypeError('Expected a `target`'); + } + + options = { + wait: false, + background: false, + ...options + }; + + let command; + let appArguments = []; + const cliArguments = []; + const childProcessOptions = {}; + + if (Array.isArray(options.app)) { + appArguments = options.app.slice(1); + options.app = options.app[0]; + } + + if (process.platform === 'darwin') { + command = 'open'; + + if (options.wait) { + cliArguments.push('--wait-apps'); + } + + if (options.background) { + cliArguments.push('--background'); + } + + if (options.app) { + cliArguments.push('-a', options.app); + } + } else if (process.platform === 'win32' || isWsl) { + command = 'cmd' + (isWsl ? '.exe' : ''); + cliArguments.push('/c', 'start', '""', '/b'); + target = target.replace(/&/g, '^&'); + + if (options.wait) { + cliArguments.push('/wait'); + } + + if (options.app) { + if (isWsl && options.app.startsWith('/mnt/')) { + const windowsPath = await wslToWindowsPath(options.app); + options.app = windowsPath; + } + + cliArguments.push(options.app); + } + + if (appArguments.length > 0) { + cliArguments.push(...appArguments); + } + } else { + if (options.app) { + command = options.app; + } else { + // When bundled by Webpack, there's no actual package file path and no local `xdg-open`. + const isBundled = !__dirname || __dirname === '/'; + + // Check if local `xdg-open` exists and is executable. + let exeLocalXdgOpen = false; + try { + await pAccess(localXdgOpenPath, fs.constants.X_OK); + exeLocalXdgOpen = true; + } catch (error) {} + + const useSystemXdgOpen = process.versions.electron || + process.platform === 'android' || isBundled || !exeLocalXdgOpen; + command = useSystemXdgOpen ? 'xdg-open' : localXdgOpenPath; + } + + if (appArguments.length > 0) { + cliArguments.push(...appArguments); + } + + if (!options.wait) { + // `xdg-open` will block the process unless stdio is ignored + // and it's detached from the parent even if it's unref'd. + childProcessOptions.stdio = 'ignore'; + childProcessOptions.detached = true; + } + } + + cliArguments.push(target); + + if (process.platform === 'darwin' && appArguments.length > 0) { + cliArguments.push('--args', ...appArguments); + } + + const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions); + + if (options.wait) { + return new Promise((resolve, reject) => { + subprocess.once('error', reject); + + subprocess.once('close', exitCode => { + if (exitCode > 0) { + reject(new Error(`Exited with code ${exitCode}`)); + return; + } + + resolve(subprocess); + }); + }); + } + + subprocess.unref(); + + return subprocess; +}; diff --git a/WebServer/node_modules/open/package.json b/WebServer/node_modules/open/package.json new file mode 100644 index 000000000..28c5e1c10 --- /dev/null +++ b/WebServer/node_modules/open/package.json @@ -0,0 +1,93 @@ +{ + "_args": [ + [ + "open@6.4.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "open@6.4.0", + "_id": "open@6.4.0", + "_inBundle": false, + "_integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "_location": "/open", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "open@6.4.0", + "name": "open", + "escapedName": "open", + "rawSpec": "6.4.0", + "saveSpec": null, + "fetchSpec": "6.4.0" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "_spec": "6.4.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/open/issues" + }, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "description": "Open stuff like URLs, files, executables. Cross-platform.", + "devDependencies": { + "@types/node": "^11.13.6", + "ava": "^1.4.0", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts", + "xdg-open" + ], + "homepage": "https://github.com/sindresorhus/open#readme", + "keywords": [ + "app", + "open", + "opener", + "opens", + "launch", + "start", + "xdg-open", + "xdg", + "default", + "cmd", + "browser", + "editor", + "executable", + "exe", + "url", + "urls", + "arguments", + "args", + "spawn", + "exec", + "child", + "process", + "website", + "file" + ], + "license": "MIT", + "name": "open", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/open.git" + }, + "scripts": { + "test": "xo && tsd" + }, + "version": "6.4.0" +} diff --git a/WebServer/node_modules/os-locale/index.js b/WebServer/node_modules/os-locale/index.js new file mode 100644 index 000000000..8c73c99f2 --- /dev/null +++ b/WebServer/node_modules/os-locale/index.js @@ -0,0 +1,114 @@ +'use strict'; +const execa = require('execa'); +const lcid = require('lcid'); +const mem = require('mem'); + +const defaultOptions = {spawn: true}; +const defaultLocale = 'en_US'; + +function getEnvLocale(env = process.env) { + return env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE; +} + +function parseLocale(string) { + const env = string.split('\n').reduce((env, def) => { + const [key, value] = def.split('='); + env[key] = value.replace(/^"|"$/g, ''); + return env; + }, {}); + + return getEnvLocale(env); +} + +function getLocale(string) { + return (string && string.replace(/[.:].*/, '')); +} + +function getLocales() { + return execa.stdout('locale', ['-a']); +} + +function getLocalesSync() { + return execa.sync('locale', ['-a']).stdout; +} + +function getSupportedLocale(locale, locales = '') { + return locales.includes(locale) ? locale : defaultLocale; +} + +function getAppleLocale() { + return Promise.all([ + execa.stdout('defaults', ['read', '-globalDomain', 'AppleLocale']), + getLocales() + ]).then(results => getSupportedLocale(results[0], results[1])); +} + +function getAppleLocaleSync() { + return getSupportedLocale( + execa.sync('defaults', ['read', '-globalDomain', 'AppleLocale']).stdout, + getLocalesSync() + ); +} + +function getUnixLocale() { + if (process.platform === 'darwin') { + return getAppleLocale(); + } + + return execa.stdout('locale') + .then(stdout => getLocale(parseLocale(stdout))); +} + +function getUnixLocaleSync() { + if (process.platform === 'darwin') { + return getAppleLocaleSync(); + } + + return getLocale(parseLocale(execa.sync('locale').stdout)); +} + +function getWinLocale() { + return execa.stdout('wmic', ['os', 'get', 'locale']) + .then(stdout => { + const lcidCode = parseInt(stdout.replace('Locale', ''), 16); + return lcid.from(lcidCode); + }); +} + +function getWinLocaleSync() { + const {stdout} = execa.sync('wmic', ['os', 'get', 'locale']); + const lcidCode = parseInt(stdout.replace('Locale', ''), 16); + return lcid.from(lcidCode); +} + +module.exports = mem((options = defaultOptions) => { + const envLocale = getEnvLocale(); + + let thenable; + if (envLocale || options.spawn === false) { + thenable = Promise.resolve(getLocale(envLocale)); + } else if (process.platform === 'win32') { + thenable = getWinLocale(); + } else { + thenable = getUnixLocale(); + } + + return thenable + .then(locale => locale || defaultLocale) + .catch(() => defaultLocale); +}); + +module.exports.sync = mem((options = defaultOptions) => { + const envLocale = getEnvLocale(); + + let res; + if (envLocale || options.spawn === false) { + res = getLocale(envLocale); + } else { + try { + res = process.platform === 'win32' ? getWinLocaleSync() : getUnixLocaleSync(); + } catch (_) {} + } + + return res || defaultLocale; +}); diff --git a/WebServer/node_modules/os-locale/package.json b/WebServer/node_modules/os-locale/package.json new file mode 100644 index 000000000..8e9ff7ea4 --- /dev/null +++ b/WebServer/node_modules/os-locale/package.json @@ -0,0 +1,81 @@ +{ + "_args": [ + [ + "os-locale@3.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "os-locale@3.1.0", + "_id": "os-locale@3.1.0", + "_inBundle": false, + "_integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "_location": "/os-locale", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "os-locale@3.1.0", + "name": "os-locale", + "escapedName": "os-locale", + "rawSpec": "3.1.0", + "saveSpec": null, + "fetchSpec": "3.1.0" + }, + "_requiredBy": [ + "/yargs", + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "_spec": "3.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/os-locale/issues" + }, + "dependencies": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "description": "Get the system locale", + "devDependencies": { + "ava": "^1.0.1", + "import-fresh": "^3.0.0", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/os-locale#readme", + "keywords": [ + "locale", + "lang", + "language", + "system", + "os", + "string", + "str", + "user", + "country", + "id", + "identifier", + "region" + ], + "license": "MIT", + "name": "os-locale", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/os-locale.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "3.1.0" +} diff --git a/WebServer/node_modules/p-defer/index.js b/WebServer/node_modules/p-defer/index.js new file mode 100644 index 000000000..eaef75e4e --- /dev/null +++ b/WebServer/node_modules/p-defer/index.js @@ -0,0 +1,11 @@ +'use strict'; +module.exports = () => { + const ret = {}; + + ret.promise = new Promise((resolve, reject) => { + ret.resolve = resolve; + ret.reject = reject; + }); + + return ret; +}; diff --git a/WebServer/node_modules/p-defer/package.json b/WebServer/node_modules/p-defer/package.json new file mode 100644 index 000000000..0dc98f4bb --- /dev/null +++ b/WebServer/node_modules/p-defer/package.json @@ -0,0 +1,76 @@ +{ + "_args": [ + [ + "p-defer@1.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "p-defer@1.0.0", + "_id": "p-defer@1.0.0", + "_inBundle": false, + "_integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "_location": "/p-defer", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "p-defer@1.0.0", + "name": "p-defer", + "escapedName": "p-defer", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/map-age-cleaner" + ], + "_resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "_spec": "1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/p-defer/issues" + }, + "description": "Create a deferred promise", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/p-defer#readme", + "keywords": [ + "promise", + "defer", + "deferred", + "resolve", + "reject", + "lazy", + "later", + "async", + "await", + "promises", + "bluebird" + ], + "license": "MIT", + "name": "p-defer", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/p-defer.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.0.0", + "xo": { + "esnext": true + } +} diff --git a/WebServer/node_modules/p-finally/index.js b/WebServer/node_modules/p-finally/index.js new file mode 100644 index 000000000..52b7b49c5 --- /dev/null +++ b/WebServer/node_modules/p-finally/index.js @@ -0,0 +1,15 @@ +'use strict'; +module.exports = (promise, onFinally) => { + onFinally = onFinally || (() => {}); + + return promise.then( + val => new Promise(resolve => { + resolve(onFinally()); + }).then(() => val), + err => new Promise(resolve => { + resolve(onFinally()); + }).then(() => { + throw err; + }) + ); +}; diff --git a/WebServer/node_modules/p-finally/package.json b/WebServer/node_modules/p-finally/package.json new file mode 100644 index 000000000..f1eac38d2 --- /dev/null +++ b/WebServer/node_modules/p-finally/package.json @@ -0,0 +1,77 @@ +{ + "_args": [ + [ + "p-finally@1.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "p-finally@1.0.0", + "_id": "p-finally@1.0.0", + "_inBundle": false, + "_integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "_location": "/p-finally", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "p-finally@1.0.0", + "name": "p-finally", + "escapedName": "p-finally", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/execa" + ], + "_resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "_spec": "1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/p-finally/issues" + }, + "description": "`Promise#finally()` ponyfill - Invoked when the promise is settled regardless of outcome", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/p-finally#readme", + "keywords": [ + "promise", + "finally", + "handler", + "function", + "async", + "await", + "promises", + "settled", + "ponyfill", + "polyfill", + "shim", + "bluebird" + ], + "license": "MIT", + "name": "p-finally", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/p-finally.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.0.0", + "xo": { + "esnext": true + } +} diff --git a/WebServer/node_modules/p-is-promise/index.js b/WebServer/node_modules/p-is-promise/index.js new file mode 100644 index 000000000..389d38fc5 --- /dev/null +++ b/WebServer/node_modules/p-is-promise/index.js @@ -0,0 +1,15 @@ +'use strict'; + +const isPromise = input => ( + input instanceof Promise || + ( + input !== null && + typeof input === 'object' && + typeof input.then === 'function' && + typeof input.catch === 'function' + ) +); + +module.exports = isPromise; +// TODO: Remove this for the next major release +module.exports.default = isPromise; diff --git a/WebServer/node_modules/p-is-promise/package.json b/WebServer/node_modules/p-is-promise/package.json new file mode 100644 index 000000000..562dc1767 --- /dev/null +++ b/WebServer/node_modules/p-is-promise/package.json @@ -0,0 +1,77 @@ +{ + "_args": [ + [ + "p-is-promise@2.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "p-is-promise@2.1.0", + "_id": "p-is-promise@2.1.0", + "_inBundle": false, + "_integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "_location": "/p-is-promise", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "p-is-promise@2.1.0", + "name": "p-is-promise", + "escapedName": "p-is-promise", + "rawSpec": "2.1.0", + "saveSpec": null, + "fetchSpec": "2.1.0" + }, + "_requiredBy": [ + "/mem" + ], + "_resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "_spec": "2.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/p-is-promise/issues" + }, + "description": "Check if something is a promise", + "devDependencies": { + "ava": "^1.4.1", + "bluebird": "^3.5.4", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/p-is-promise#readme", + "keywords": [ + "promise", + "is", + "detect", + "check", + "kind", + "type", + "thenable", + "es2015", + "async", + "await", + "promises", + "bluebird" + ], + "license": "MIT", + "name": "p-is-promise", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/p-is-promise.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "2.1.0" +} diff --git a/WebServer/node_modules/p-limit/index.js b/WebServer/node_modules/p-limit/index.js new file mode 100644 index 000000000..d22fbe8a4 --- /dev/null +++ b/WebServer/node_modules/p-limit/index.js @@ -0,0 +1,52 @@ +'use strict'; +const pTry = require('p-try'); + +const pLimit = concurrency => { + if (concurrency < 1) { + throw new TypeError('Expected `concurrency` to be a number from 1 and up'); + } + + const queue = []; + let activeCount = 0; + + const next = () => { + activeCount--; + + if (queue.length > 0) { + queue.shift()(); + } + }; + + const run = (fn, resolve, ...args) => { + activeCount++; + + const result = pTry(fn, ...args); + + resolve(result); + + result.then(next, next); + }; + + const enqueue = (fn, resolve, ...args) => { + if (activeCount < concurrency) { + run(fn, resolve, ...args); + } else { + queue.push(run.bind(null, fn, resolve, ...args)); + } + }; + + const generator = (fn, ...args) => new Promise(resolve => enqueue(fn, resolve, ...args)); + Object.defineProperties(generator, { + activeCount: { + get: () => activeCount + }, + pendingCount: { + get: () => queue.length + } + }); + + return generator; +}; + +module.exports = pLimit; +module.exports.default = pLimit; diff --git a/WebServer/node_modules/p-limit/package.json b/WebServer/node_modules/p-limit/package.json new file mode 100644 index 000000000..a5c801307 --- /dev/null +++ b/WebServer/node_modules/p-limit/package.json @@ -0,0 +1,86 @@ +{ + "_args": [ + [ + "p-limit@2.2.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "p-limit@2.2.0", + "_id": "p-limit@2.2.0", + "_inBundle": false, + "_integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "_location": "/p-limit", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "p-limit@2.2.0", + "name": "p-limit", + "escapedName": "p-limit", + "rawSpec": "2.2.0", + "saveSpec": null, + "fetchSpec": "2.2.0" + }, + "_requiredBy": [ + "/p-locate" + ], + "_resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "_spec": "2.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/p-limit/issues" + }, + "dependencies": { + "p-try": "^2.0.0" + }, + "description": "Run multiple promise-returning & async functions with limited concurrency", + "devDependencies": { + "ava": "^1.2.1", + "delay": "^4.1.0", + "in-range": "^1.0.0", + "random-int": "^1.0.0", + "time-span": "^2.0.0", + "tsd-check": "^0.3.0", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/p-limit#readme", + "keywords": [ + "promise", + "limit", + "limited", + "concurrency", + "throttle", + "throat", + "rate", + "batch", + "ratelimit", + "task", + "queue", + "async", + "await", + "promises", + "bluebird" + ], + "license": "MIT", + "name": "p-limit", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/p-limit.git" + }, + "scripts": { + "test": "xo && ava && tsd-check" + }, + "version": "2.2.0" +} diff --git a/WebServer/node_modules/p-locate/index.js b/WebServer/node_modules/p-locate/index.js new file mode 100644 index 000000000..4bd08aad1 --- /dev/null +++ b/WebServer/node_modules/p-locate/index.js @@ -0,0 +1,34 @@ +'use strict'; +const pLimit = require('p-limit'); + +class EndError extends Error { + constructor(value) { + super(); + this.value = value; + } +} + +// The input can also be a promise, so we `Promise.resolve()` it +const testElement = (el, tester) => Promise.resolve(el).then(tester); + +// The input can also be a promise, so we `Promise.all()` them both +const finder = el => Promise.all(el).then(val => val[1] === true && Promise.reject(new EndError(val[0]))); + +module.exports = (iterable, tester, opts) => { + opts = Object.assign({ + concurrency: Infinity, + preserveOrder: true + }, opts); + + const limit = pLimit(opts.concurrency); + + // Start all the promises concurrently with optional limit + const items = [...iterable].map(el => [el, limit(testElement, el, tester)]); + + // Check the promises either serially or concurrently + const checkLimit = pLimit(opts.preserveOrder ? 1 : Infinity); + + return Promise.all(items.map(el => checkLimit(finder, el))) + .then(() => {}) + .catch(err => err instanceof EndError ? err.value : Promise.reject(err)); +}; diff --git a/WebServer/node_modules/p-locate/package.json b/WebServer/node_modules/p-locate/package.json new file mode 100644 index 000000000..bbb0f0a21 --- /dev/null +++ b/WebServer/node_modules/p-locate/package.json @@ -0,0 +1,86 @@ +{ + "_args": [ + [ + "p-locate@3.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "p-locate@3.0.0", + "_id": "p-locate@3.0.0", + "_inBundle": false, + "_integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "_location": "/p-locate", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "p-locate@3.0.0", + "name": "p-locate", + "escapedName": "p-locate", + "rawSpec": "3.0.0", + "saveSpec": null, + "fetchSpec": "3.0.0" + }, + "_requiredBy": [ + "/locate-path" + ], + "_resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "_spec": "3.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/p-locate/issues" + }, + "dependencies": { + "p-limit": "^2.0.0" + }, + "description": "Get the first fulfilled promise that satisfies the provided testing function", + "devDependencies": { + "ava": "*", + "delay": "^3.0.0", + "in-range": "^1.0.0", + "time-span": "^2.0.0", + "xo": "*" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/p-locate#readme", + "keywords": [ + "promise", + "locate", + "find", + "finder", + "search", + "searcher", + "test", + "array", + "collection", + "iterable", + "iterator", + "race", + "fulfilled", + "fastest", + "async", + "await", + "promises", + "bluebird" + ], + "license": "MIT", + "name": "p-locate", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/p-locate.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "3.0.0" +} diff --git a/WebServer/node_modules/p-try/index.js b/WebServer/node_modules/p-try/index.js new file mode 100644 index 000000000..db858da29 --- /dev/null +++ b/WebServer/node_modules/p-try/index.js @@ -0,0 +1,9 @@ +'use strict'; + +const pTry = (fn, ...arguments_) => new Promise(resolve => { + resolve(fn(...arguments_)); +}); + +module.exports = pTry; +// TODO: remove this in the next major version +module.exports.default = pTry; diff --git a/WebServer/node_modules/p-try/package.json b/WebServer/node_modules/p-try/package.json new file mode 100644 index 000000000..df771e548 --- /dev/null +++ b/WebServer/node_modules/p-try/package.json @@ -0,0 +1,77 @@ +{ + "_args": [ + [ + "p-try@2.2.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "p-try@2.2.0", + "_id": "p-try@2.2.0", + "_inBundle": false, + "_integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "_location": "/p-try", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "p-try@2.2.0", + "name": "p-try", + "escapedName": "p-try", + "rawSpec": "2.2.0", + "saveSpec": null, + "fetchSpec": "2.2.0" + }, + "_requiredBy": [ + "/p-limit" + ], + "_resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "_spec": "2.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/p-try/issues" + }, + "description": "`Start a promise chain", + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.1", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/p-try#readme", + "keywords": [ + "promise", + "try", + "resolve", + "function", + "catch", + "async", + "await", + "promises", + "settled", + "ponyfill", + "polyfill", + "shim", + "bluebird" + ], + "license": "MIT", + "name": "p-try", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/p-try.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "2.2.0" +} diff --git a/WebServer/node_modules/path-exists/index.js b/WebServer/node_modules/path-exists/index.js new file mode 100644 index 000000000..16ae60acb --- /dev/null +++ b/WebServer/node_modules/path-exists/index.js @@ -0,0 +1,17 @@ +'use strict'; +const fs = require('fs'); + +module.exports = fp => new Promise(resolve => { + fs.access(fp, err => { + resolve(!err); + }); +}); + +module.exports.sync = fp => { + try { + fs.accessSync(fp); + return true; + } catch (err) { + return false; + } +}; diff --git a/WebServer/node_modules/path-exists/package.json b/WebServer/node_modules/path-exists/package.json new file mode 100644 index 000000000..f76efce70 --- /dev/null +++ b/WebServer/node_modules/path-exists/package.json @@ -0,0 +1,75 @@ +{ + "_args": [ + [ + "path-exists@3.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "path-exists@3.0.0", + "_id": "path-exists@3.0.0", + "_inBundle": false, + "_integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "_location": "/path-exists", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "path-exists@3.0.0", + "name": "path-exists", + "escapedName": "path-exists", + "rawSpec": "3.0.0", + "saveSpec": null, + "fetchSpec": "3.0.0" + }, + "_requiredBy": [ + "/locate-path" + ], + "_resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "_spec": "3.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/path-exists/issues" + }, + "description": "Check if a path exists", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/path-exists#readme", + "keywords": [ + "path", + "exists", + "exist", + "file", + "filepath", + "fs", + "filesystem", + "file-system", + "access", + "stat" + ], + "license": "MIT", + "name": "path-exists", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/path-exists.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "3.0.0", + "xo": { + "esnext": true + } +} diff --git a/WebServer/node_modules/path-is-absolute/index.js b/WebServer/node_modules/path-is-absolute/index.js new file mode 100644 index 000000000..22aa6c356 --- /dev/null +++ b/WebServer/node_modules/path-is-absolute/index.js @@ -0,0 +1,20 @@ +'use strict'; + +function posix(path) { + return path.charAt(0) === '/'; +} + +function win32(path) { + // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 + var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + var result = splitDeviceRe.exec(path); + var device = result[1] || ''; + var isUnc = Boolean(device && device.charAt(1) !== ':'); + + // UNC paths are always absolute + return Boolean(result[2] || isUnc); +} + +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; diff --git a/WebServer/node_modules/path-is-absolute/package.json b/WebServer/node_modules/path-is-absolute/package.json new file mode 100644 index 000000000..51a53cf97 --- /dev/null +++ b/WebServer/node_modules/path-is-absolute/package.json @@ -0,0 +1,78 @@ +{ + "_args": [ + [ + "path-is-absolute@1.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "path-is-absolute@1.0.1", + "_id": "path-is-absolute@1.0.1", + "_inBundle": false, + "_integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "_location": "/path-is-absolute", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "path-is-absolute@1.0.1", + "name": "path-is-absolute", + "escapedName": "path-is-absolute", + "rawSpec": "1.0.1", + "saveSpec": null, + "fetchSpec": "1.0.1" + }, + "_requiredBy": [ + "/glob" + ], + "_resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "_spec": "1.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/path-is-absolute/issues" + }, + "description": "Node.js 0.12 path.isAbsolute() ponyfill", + "devDependencies": { + "xo": "^0.16.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/path-is-absolute#readme", + "keywords": [ + "path", + "paths", + "file", + "dir", + "absolute", + "isabsolute", + "is-absolute", + "built-in", + "util", + "utils", + "core", + "ponyfill", + "polyfill", + "shim", + "is", + "detect", + "check" + ], + "license": "MIT", + "name": "path-is-absolute", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/path-is-absolute.git" + }, + "scripts": { + "test": "xo && node test.js" + }, + "version": "1.0.1" +} diff --git a/WebServer/node_modules/path-key/index.js b/WebServer/node_modules/path-key/index.js new file mode 100644 index 000000000..62c8250ab --- /dev/null +++ b/WebServer/node_modules/path-key/index.js @@ -0,0 +1,13 @@ +'use strict'; +module.exports = opts => { + opts = opts || {}; + + const env = opts.env || process.env; + const platform = opts.platform || process.platform; + + if (platform !== 'win32') { + return 'PATH'; + } + + return Object.keys(env).find(x => x.toUpperCase() === 'PATH') || 'Path'; +}; diff --git a/WebServer/node_modules/path-key/package.json b/WebServer/node_modules/path-key/package.json new file mode 100644 index 000000000..41d10a3f3 --- /dev/null +++ b/WebServer/node_modules/path-key/package.json @@ -0,0 +1,75 @@ +{ + "_args": [ + [ + "path-key@2.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "path-key@2.0.1", + "_id": "path-key@2.0.1", + "_inBundle": false, + "_integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "_location": "/path-key", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "path-key@2.0.1", + "name": "path-key", + "escapedName": "path-key", + "rawSpec": "2.0.1", + "saveSpec": null, + "fetchSpec": "2.0.1" + }, + "_requiredBy": [ + "/cross-spawn", + "/npm-run-path" + ], + "_resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "_spec": "2.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/path-key/issues" + }, + "description": "Get the PATH environment variable key cross-platform", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/path-key#readme", + "keywords": [ + "path", + "key", + "environment", + "env", + "variable", + "var", + "get", + "cross-platform", + "windows" + ], + "license": "MIT", + "name": "path-key", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/path-key.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "2.0.1", + "xo": { + "esnext": true + } +} diff --git a/WebServer/node_modules/pathval/index.js b/WebServer/node_modules/pathval/index.js new file mode 100644 index 000000000..1ec2148a0 --- /dev/null +++ b/WebServer/node_modules/pathval/index.js @@ -0,0 +1,291 @@ +'use strict'; + +/* ! + * Chai - pathval utility + * Copyright(c) 2012-2014 Jake Luer + * @see https://github.com/logicalparadox/filtr + * MIT Licensed + */ + +/** + * ### .hasProperty(object, name) + * + * This allows checking whether an object has own + * or inherited from prototype chain named property. + * + * Basically does the same thing as the `in` + * operator but works properly with null/undefined values + * and other primitives. + * + * var obj = { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * + * The following would be the results. + * + * hasProperty(obj, 'str'); // true + * hasProperty(obj, 'constructor'); // true + * hasProperty(obj, 'bar'); // false + * + * hasProperty(obj.str, 'length'); // true + * hasProperty(obj.str, 1); // true + * hasProperty(obj.str, 5); // false + * + * hasProperty(obj.arr, 'length'); // true + * hasProperty(obj.arr, 2); // true + * hasProperty(obj.arr, 3); // false + * + * @param {Object} object + * @param {String|Symbol} name + * @returns {Boolean} whether it exists + * @namespace Utils + * @name hasProperty + * @api public + */ + +function hasProperty(obj, name) { + if (typeof obj === 'undefined' || obj === null) { + return false; + } + + // The `in` operator does not work with primitives. + return name in Object(obj); +} + +/* ! + * ## parsePath(path) + * + * Helper function used to parse string object + * paths. Use in conjunction with `internalGetPathValue`. + * + * var parsed = parsePath('myobject.property.subprop'); + * + * ### Paths: + * + * * Can be infinitely deep and nested. + * * Arrays are also valid using the formal `myobject.document[3].property`. + * * Literal dots and brackets (not delimiter) must be backslash-escaped. + * + * @param {String} path + * @returns {Object} parsed + * @api private + */ + +function parsePath(path) { + var str = path.replace(/([^\\])\[/g, '$1.['); + var parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function mapMatches(value) { + var regexp = /^\[(\d+)\]$/; + var mArr = regexp.exec(value); + var parsed = null; + if (mArr) { + parsed = { i: parseFloat(mArr[1]) }; + } else { + parsed = { p: value.replace(/\\([.\[\]])/g, '$1') }; + } + + return parsed; + }); +} + +/* ! + * ## internalGetPathValue(obj, parsed[, pathDepth]) + * + * Helper companion function for `.parsePath` that returns + * the value located at the parsed address. + * + * var value = getPathValue(obj, parsed); + * + * @param {Object} object to search against + * @param {Object} parsed definition from `parsePath`. + * @param {Number} depth (nesting level) of the property we want to retrieve + * @returns {Object|Undefined} value + * @api private + */ + +function internalGetPathValue(obj, parsed, pathDepth) { + var temporaryValue = obj; + var res = null; + pathDepth = (typeof pathDepth === 'undefined' ? parsed.length : pathDepth); + + for (var i = 0; i < pathDepth; i++) { + var part = parsed[i]; + if (temporaryValue) { + if (typeof part.p === 'undefined') { + temporaryValue = temporaryValue[part.i]; + } else { + temporaryValue = temporaryValue[part.p]; + } + + if (i === (pathDepth - 1)) { + res = temporaryValue; + } + } + } + + return res; +} + +/* ! + * ## internalSetPathValue(obj, value, parsed) + * + * Companion function for `parsePath` that sets + * the value located at a parsed address. + * + * internalSetPathValue(obj, 'value', parsed); + * + * @param {Object} object to search and define on + * @param {*} value to use upon set + * @param {Object} parsed definition from `parsePath` + * @api private + */ + +function internalSetPathValue(obj, val, parsed) { + var tempObj = obj; + var pathDepth = parsed.length; + var part = null; + // Here we iterate through every part of the path + for (var i = 0; i < pathDepth; i++) { + var propName = null; + var propVal = null; + part = parsed[i]; + + // If it's the last part of the path, we set the 'propName' value with the property name + if (i === (pathDepth - 1)) { + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Now we set the property with the name held by 'propName' on object with the desired val + tempObj[propName] = val; + } else if (typeof part.p !== 'undefined' && tempObj[part.p]) { + tempObj = tempObj[part.p]; + } else if (typeof part.i !== 'undefined' && tempObj[part.i]) { + tempObj = tempObj[part.i]; + } else { + // If the obj doesn't have the property we create one with that name to define it + var next = parsed[i + 1]; + // Here we set the name of the property which will be defined + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Here we decide if this property will be an array or a new object + propVal = typeof next.p === 'undefined' ? [] : {}; + tempObj[propName] = propVal; + tempObj = tempObj[propName]; + } + } +} + +/** + * ### .getPathInfo(object, path) + * + * This allows the retrieval of property info in an + * object given a string path. + * + * The path info consists of an object with the + * following properties: + * + * * parent - The parent object of the property referenced by `path` + * * name - The name of the final property, a number if it was an array indexer + * * value - The value of the property, if it exists, otherwise `undefined` + * * exists - Whether the property exists or not + * + * @param {Object} object + * @param {String} path + * @returns {Object} info + * @namespace Utils + * @name getPathInfo + * @api public + */ + +function getPathInfo(obj, path) { + var parsed = parsePath(path); + var last = parsed[parsed.length - 1]; + var info = { + parent: parsed.length > 1 ? internalGetPathValue(obj, parsed, parsed.length - 1) : obj, + name: last.p || last.i, + value: internalGetPathValue(obj, parsed), + }; + info.exists = hasProperty(info.parent, info.name); + + return info; +} + +/** + * ### .getPathValue(object, path) + * + * This allows the retrieval of values in an + * object given a string path. + * + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * } + * + * The following would be the results. + * + * getPathValue(obj, 'prop1.str'); // Hello + * getPathValue(obj, 'prop1.att[2]'); // b + * getPathValue(obj, 'prop2.arr[0].nested'); // Universe + * + * @param {Object} object + * @param {String} path + * @returns {Object} value or `undefined` + * @namespace Utils + * @name getPathValue + * @api public + */ + +function getPathValue(obj, path) { + var info = getPathInfo(obj, path); + return info.value; +} + +/** + * ### .setPathValue(object, path, value) + * + * Define the value in an object at a given string path. + * + * ```js + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * }; + * ``` + * + * The following would be acceptable. + * + * ```js + * var properties = require('tea-properties'); + * properties.set(obj, 'prop1.str', 'Hello Universe!'); + * properties.set(obj, 'prop1.arr[2]', 'B'); + * properties.set(obj, 'prop2.arr[0].nested.value', { hello: 'universe' }); + * ``` + * + * @param {Object} object + * @param {String} path + * @param {Mixed} value + * @api private + */ + +function setPathValue(obj, path, val) { + var parsed = parsePath(path); + internalSetPathValue(obj, val, parsed); + return obj; +} + +module.exports = { + hasProperty: hasProperty, + getPathInfo: getPathInfo, + getPathValue: getPathValue, + setPathValue: setPathValue, +}; diff --git a/WebServer/node_modules/pathval/package.json b/WebServer/node_modules/pathval/package.json new file mode 100644 index 000000000..a4a09d2bd --- /dev/null +++ b/WebServer/node_modules/pathval/package.json @@ -0,0 +1,113 @@ +{ + "_args": [ + [ + "pathval@1.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "pathval@1.1.0", + "_id": "pathval@1.1.0", + "_inBundle": false, + "_integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "_location": "/pathval", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "pathval@1.1.0", + "name": "pathval", + "escapedName": "pathval", + "rawSpec": "1.1.0", + "saveSpec": null, + "fetchSpec": "1.1.0" + }, + "_requiredBy": [ + "/chai" + ], + "_resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "_spec": "1.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Veselin Todorov", + "email": "hi@vesln.com" + }, + "bugs": { + "url": "https://github.com/chaijs/pathval/issues" + }, + "config": { + "ghooks": { + "commit-msg": "validate-commit-msg" + } + }, + "description": "Object value retrieval given a string path", + "devDependencies": { + "browserify": "^13.0.0", + "browserify-istanbul": "^1.0.0", + "coveralls": "2.11.9", + "eslint": "^2.4.0", + "eslint-config-strict": "^8.5.0", + "eslint-plugin-filenames": "^0.2.0", + "ghooks": "^1.0.1", + "istanbul": "^0.4.2", + "karma": "^0.13.22", + "karma-browserify": "^5.0.2", + "karma-coverage": "^0.5.5", + "karma-mocha": "^0.2.2", + "karma-phantomjs-launcher": "^1.0.0", + "karma-sauce-launcher": "^0.3.1", + "lcov-result-merger": "^1.0.2", + "mocha": "^3.1.2", + "phantomjs-prebuilt": "^2.1.5", + "semantic-release": "^4.3.5", + "simple-assert": "^1.0.0", + "travis-after-all": "^1.4.4", + "validate-commit-msg": "^2.3.1" + }, + "engines": { + "node": "*" + }, + "eslintConfig": { + "extends": [ + "strict/es5" + ], + "env": { + "es6": true + }, + "globals": { + "HTMLElement": false + }, + "rules": { + "complexity": 0, + "max-statements": 0 + } + }, + "files": [ + "index.js", + "pathval.js" + ], + "homepage": "https://github.com/chaijs/pathval", + "keywords": [ + "pathval", + "value retrieval", + "chai util" + ], + "license": "MIT", + "main": "./index.js", + "name": "pathval", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/chaijs/pathval.git" + }, + "scripts": { + "build": "browserify --bare $npm_package_main --standalone pathval -o pathval.js", + "lint": "eslint --ignore-path .gitignore .", + "prepublish": "npm run build", + "pretest": "npm run lint", + "semantic-release": "semantic-release pre && npm publish && semantic-release post", + "test": "npm run test:node && npm run test:browser && npm run upload-coverage", + "test:browser": "karma start --singleRun=true", + "test:node": "istanbul cover _mocha", + "upload-coverage": "lcov-result-merger 'coverage/**/lcov.info' | coveralls; exit 0" + }, + "version": "1.1.0" +} diff --git a/WebServer/node_modules/pathval/pathval.js b/WebServer/node_modules/pathval/pathval.js new file mode 100644 index 000000000..77086363b --- /dev/null +++ b/WebServer/node_modules/pathval/pathval.js @@ -0,0 +1,295 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pathval = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + * @see https://github.com/logicalparadox/filtr + * MIT Licensed + */ + +/** + * ### .hasProperty(object, name) + * + * This allows checking whether an object has own + * or inherited from prototype chain named property. + * + * Basically does the same thing as the `in` + * operator but works properly with null/undefined values + * and other primitives. + * + * var obj = { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * + * The following would be the results. + * + * hasProperty(obj, 'str'); // true + * hasProperty(obj, 'constructor'); // true + * hasProperty(obj, 'bar'); // false + * + * hasProperty(obj.str, 'length'); // true + * hasProperty(obj.str, 1); // true + * hasProperty(obj.str, 5); // false + * + * hasProperty(obj.arr, 'length'); // true + * hasProperty(obj.arr, 2); // true + * hasProperty(obj.arr, 3); // false + * + * @param {Object} object + * @param {String|Symbol} name + * @returns {Boolean} whether it exists + * @namespace Utils + * @name hasProperty + * @api public + */ + +function hasProperty(obj, name) { + if (typeof obj === 'undefined' || obj === null) { + return false; + } + + // The `in` operator does not work with primitives. + return name in Object(obj); +} + +/* ! + * ## parsePath(path) + * + * Helper function used to parse string object + * paths. Use in conjunction with `internalGetPathValue`. + * + * var parsed = parsePath('myobject.property.subprop'); + * + * ### Paths: + * + * * Can be infinitely deep and nested. + * * Arrays are also valid using the formal `myobject.document[3].property`. + * * Literal dots and brackets (not delimiter) must be backslash-escaped. + * + * @param {String} path + * @returns {Object} parsed + * @api private + */ + +function parsePath(path) { + var str = path.replace(/([^\\])\[/g, '$1.['); + var parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function mapMatches(value) { + var regexp = /^\[(\d+)\]$/; + var mArr = regexp.exec(value); + var parsed = null; + if (mArr) { + parsed = { i: parseFloat(mArr[1]) }; + } else { + parsed = { p: value.replace(/\\([.\[\]])/g, '$1') }; + } + + return parsed; + }); +} + +/* ! + * ## internalGetPathValue(obj, parsed[, pathDepth]) + * + * Helper companion function for `.parsePath` that returns + * the value located at the parsed address. + * + * var value = getPathValue(obj, parsed); + * + * @param {Object} object to search against + * @param {Object} parsed definition from `parsePath`. + * @param {Number} depth (nesting level) of the property we want to retrieve + * @returns {Object|Undefined} value + * @api private + */ + +function internalGetPathValue(obj, parsed, pathDepth) { + var temporaryValue = obj; + var res = null; + pathDepth = (typeof pathDepth === 'undefined' ? parsed.length : pathDepth); + + for (var i = 0; i < pathDepth; i++) { + var part = parsed[i]; + if (temporaryValue) { + if (typeof part.p === 'undefined') { + temporaryValue = temporaryValue[part.i]; + } else { + temporaryValue = temporaryValue[part.p]; + } + + if (i === (pathDepth - 1)) { + res = temporaryValue; + } + } + } + + return res; +} + +/* ! + * ## internalSetPathValue(obj, value, parsed) + * + * Companion function for `parsePath` that sets + * the value located at a parsed address. + * + * internalSetPathValue(obj, 'value', parsed); + * + * @param {Object} object to search and define on + * @param {*} value to use upon set + * @param {Object} parsed definition from `parsePath` + * @api private + */ + +function internalSetPathValue(obj, val, parsed) { + var tempObj = obj; + var pathDepth = parsed.length; + var part = null; + // Here we iterate through every part of the path + for (var i = 0; i < pathDepth; i++) { + var propName = null; + var propVal = null; + part = parsed[i]; + + // If it's the last part of the path, we set the 'propName' value with the property name + if (i === (pathDepth - 1)) { + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Now we set the property with the name held by 'propName' on object with the desired val + tempObj[propName] = val; + } else if (typeof part.p !== 'undefined' && tempObj[part.p]) { + tempObj = tempObj[part.p]; + } else if (typeof part.i !== 'undefined' && tempObj[part.i]) { + tempObj = tempObj[part.i]; + } else { + // If the obj doesn't have the property we create one with that name to define it + var next = parsed[i + 1]; + // Here we set the name of the property which will be defined + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Here we decide if this property will be an array or a new object + propVal = typeof next.p === 'undefined' ? [] : {}; + tempObj[propName] = propVal; + tempObj = tempObj[propName]; + } + } +} + +/** + * ### .getPathInfo(object, path) + * + * This allows the retrieval of property info in an + * object given a string path. + * + * The path info consists of an object with the + * following properties: + * + * * parent - The parent object of the property referenced by `path` + * * name - The name of the final property, a number if it was an array indexer + * * value - The value of the property, if it exists, otherwise `undefined` + * * exists - Whether the property exists or not + * + * @param {Object} object + * @param {String} path + * @returns {Object} info + * @namespace Utils + * @name getPathInfo + * @api public + */ + +function getPathInfo(obj, path) { + var parsed = parsePath(path); + var last = parsed[parsed.length - 1]; + var info = { + parent: parsed.length > 1 ? internalGetPathValue(obj, parsed, parsed.length - 1) : obj, + name: last.p || last.i, + value: internalGetPathValue(obj, parsed), + }; + info.exists = hasProperty(info.parent, info.name); + + return info; +} + +/** + * ### .getPathValue(object, path) + * + * This allows the retrieval of values in an + * object given a string path. + * + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * } + * + * The following would be the results. + * + * getPathValue(obj, 'prop1.str'); // Hello + * getPathValue(obj, 'prop1.att[2]'); // b + * getPathValue(obj, 'prop2.arr[0].nested'); // Universe + * + * @param {Object} object + * @param {String} path + * @returns {Object} value or `undefined` + * @namespace Utils + * @name getPathValue + * @api public + */ + +function getPathValue(obj, path) { + var info = getPathInfo(obj, path); + return info.value; +} + +/** + * ### .setPathValue(object, path, value) + * + * Define the value in an object at a given string path. + * + * ```js + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * }; + * ``` + * + * The following would be acceptable. + * + * ```js + * var properties = require('tea-properties'); + * properties.set(obj, 'prop1.str', 'Hello Universe!'); + * properties.set(obj, 'prop1.arr[2]', 'B'); + * properties.set(obj, 'prop2.arr[0].nested.value', { hello: 'universe' }); + * ``` + * + * @param {Object} object + * @param {String} path + * @param {Mixed} value + * @api private + */ + +function setPathValue(obj, path, val) { + var parsed = parsePath(path); + internalSetPathValue(obj, val, parsed); + return obj; +} + +module.exports = { + hasProperty: hasProperty, + getPathInfo: getPathInfo, + getPathValue: getPathValue, + setPathValue: setPathValue, +}; + +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/WebServer/node_modules/pump/index.js b/WebServer/node_modules/pump/index.js new file mode 100644 index 000000000..c15059f17 --- /dev/null +++ b/WebServer/node_modules/pump/index.js @@ -0,0 +1,82 @@ +var once = require('once') +var eos = require('end-of-stream') +var fs = require('fs') // we only need fs to get the ReadStream and WriteStream prototypes + +var noop = function () {} +var ancient = /^v?\.0/.test(process.version) + +var isFn = function (fn) { + return typeof fn === 'function' +} + +var isFS = function (stream) { + if (!ancient) return false // newer node version do not need to care about fs is a special way + if (!fs) return false // browser + return (stream instanceof (fs.ReadStream || noop) || stream instanceof (fs.WriteStream || noop)) && isFn(stream.close) +} + +var isRequest = function (stream) { + return stream.setHeader && isFn(stream.abort) +} + +var destroyer = function (stream, reading, writing, callback) { + callback = once(callback) + + var closed = false + stream.on('close', function () { + closed = true + }) + + eos(stream, {readable: reading, writable: writing}, function (err) { + if (err) return callback(err) + closed = true + callback() + }) + + var destroyed = false + return function (err) { + if (closed) return + if (destroyed) return + destroyed = true + + if (isFS(stream)) return stream.close(noop) // use close for fs streams to avoid fd leaks + if (isRequest(stream)) return stream.abort() // request.destroy just do .end - .abort is what we want + + if (isFn(stream.destroy)) return stream.destroy() + + callback(err || new Error('stream was destroyed')) + } +} + +var call = function (fn) { + fn() +} + +var pipe = function (from, to) { + return from.pipe(to) +} + +var pump = function () { + var streams = Array.prototype.slice.call(arguments) + var callback = isFn(streams[streams.length - 1] || noop) && streams.pop() || noop + + if (Array.isArray(streams[0])) streams = streams[0] + if (streams.length < 2) throw new Error('pump requires two streams per minimum') + + var error + var destroys = streams.map(function (stream, i) { + var reading = i < streams.length - 1 + var writing = i > 0 + return destroyer(stream, reading, writing, function (err) { + if (!error) error = err + if (err) destroys.forEach(call) + if (reading) return + destroys.forEach(call) + callback(error) + }) + }) + + return streams.reduce(pipe) +} + +module.exports = pump diff --git a/WebServer/node_modules/pump/package.json b/WebServer/node_modules/pump/package.json new file mode 100644 index 000000000..29fdb55a1 --- /dev/null +++ b/WebServer/node_modules/pump/package.json @@ -0,0 +1,62 @@ +{ + "_args": [ + [ + "pump@3.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "pump@3.0.0", + "_id": "pump@3.0.0", + "_inBundle": false, + "_integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "_location": "/pump", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "pump@3.0.0", + "name": "pump", + "escapedName": "pump", + "rawSpec": "3.0.0", + "saveSpec": null, + "fetchSpec": "3.0.0" + }, + "_requiredBy": [ + "/get-stream" + ], + "_resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "_spec": "3.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Mathias Buus Madsen", + "email": "mathiasbuus@gmail.com" + }, + "browser": { + "fs": false + }, + "bugs": { + "url": "https://github.com/mafintosh/pump/issues" + }, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + }, + "description": "pipe streams together and close all of them if one of them closes", + "homepage": "https://github.com/mafintosh/pump#readme", + "keywords": [ + "streams", + "pipe", + "destroy", + "callback" + ], + "license": "MIT", + "name": "pump", + "repository": { + "type": "git", + "url": "git://github.com/mafintosh/pump.git" + }, + "scripts": { + "test": "node test-browser.js && node test-node.js" + }, + "version": "3.0.0" +} diff --git a/WebServer/node_modules/pump/test-browser.js b/WebServer/node_modules/pump/test-browser.js new file mode 100644 index 000000000..9a06c8a4c --- /dev/null +++ b/WebServer/node_modules/pump/test-browser.js @@ -0,0 +1,66 @@ +var stream = require('stream') +var pump = require('./index') + +var rs = new stream.Readable() +var ws = new stream.Writable() + +rs._read = function (size) { + this.push(Buffer(size).fill('abc')) +} + +ws._write = function (chunk, encoding, cb) { + setTimeout(function () { + cb() + }, 100) +} + +var toHex = function () { + var reverse = new (require('stream').Transform)() + + reverse._transform = function (chunk, enc, callback) { + reverse.push(chunk.toString('hex')) + callback() + } + + return reverse +} + +var wsClosed = false +var rsClosed = false +var callbackCalled = false + +var check = function () { + if (wsClosed && rsClosed && callbackCalled) { + console.log('test-browser.js passes') + clearTimeout(timeout) + } +} + +ws.on('finish', function () { + wsClosed = true + check() +}) + +rs.on('end', function () { + rsClosed = true + check() +}) + +var res = pump(rs, toHex(), toHex(), toHex(), ws, function () { + callbackCalled = true + check() +}) + +if (res !== ws) { + throw new Error('should return last stream') +} + +setTimeout(function () { + rs.push(null) + rs.emit('close') +}, 1000) + +var timeout = setTimeout(function () { + check() + throw new Error('timeout') +}, 5000) diff --git a/WebServer/node_modules/pump/test-node.js b/WebServer/node_modules/pump/test-node.js new file mode 100644 index 000000000..561251a08 --- /dev/null +++ b/WebServer/node_modules/pump/test-node.js @@ -0,0 +1,53 @@ +var pump = require('./index') + +var rs = require('fs').createReadStream('/dev/random') +var ws = require('fs').createWriteStream('/dev/null') + +var toHex = function () { + var reverse = new (require('stream').Transform)() + + reverse._transform = function (chunk, enc, callback) { + reverse.push(chunk.toString('hex')) + callback() + } + + return reverse +} + +var wsClosed = false +var rsClosed = false +var callbackCalled = false + +var check = function () { + if (wsClosed && rsClosed && callbackCalled) { + console.log('test-node.js passes') + clearTimeout(timeout) + } +} + +ws.on('close', function () { + wsClosed = true + check() +}) + +rs.on('close', function () { + rsClosed = true + check() +}) + +var res = pump(rs, toHex(), toHex(), toHex(), ws, function () { + callbackCalled = true + check() +}) + +if (res !== ws) { + throw new Error('should return last stream') +} + +setTimeout(function () { + rs.destroy() +}, 1000) + +var timeout = setTimeout(function () { + throw new Error('timeout') +}, 5000) diff --git a/WebServer/node_modules/require-directory/index.js b/WebServer/node_modules/require-directory/index.js new file mode 100644 index 000000000..cd37da7ea --- /dev/null +++ b/WebServer/node_modules/require-directory/index.js @@ -0,0 +1,86 @@ +'use strict'; + +var fs = require('fs'), + join = require('path').join, + resolve = require('path').resolve, + dirname = require('path').dirname, + defaultOptions = { + extensions: ['js', 'json', 'coffee'], + recurse: true, + rename: function (name) { + return name; + }, + visit: function (obj) { + return obj; + } + }; + +function checkFileInclusion(path, filename, options) { + return ( + // verify file has valid extension + (new RegExp('\\.(' + options.extensions.join('|') + ')$', 'i').test(filename)) && + + // if options.include is a RegExp, evaluate it and make sure the path passes + !(options.include && options.include instanceof RegExp && !options.include.test(path)) && + + // if options.include is a function, evaluate it and make sure the path passes + !(options.include && typeof options.include === 'function' && !options.include(path, filename)) && + + // if options.exclude is a RegExp, evaluate it and make sure the path doesn't pass + !(options.exclude && options.exclude instanceof RegExp && options.exclude.test(path)) && + + // if options.exclude is a function, evaluate it and make sure the path doesn't pass + !(options.exclude && typeof options.exclude === 'function' && options.exclude(path, filename)) + ); +} + +function requireDirectory(m, path, options) { + var retval = {}; + + // path is optional + if (path && !options && typeof path !== 'string') { + options = path; + path = null; + } + + // default options + options = options || {}; + for (var prop in defaultOptions) { + if (typeof options[prop] === 'undefined') { + options[prop] = defaultOptions[prop]; + } + } + + // if no path was passed in, assume the equivelant of __dirname from caller + // otherwise, resolve path relative to the equivalent of __dirname + path = !path ? dirname(m.filename) : resolve(dirname(m.filename), path); + + // get the path of each file in specified directory, append to current tree node, recurse + fs.readdirSync(path).forEach(function (filename) { + var joined = join(path, filename), + files, + key, + obj; + + if (fs.statSync(joined).isDirectory() && options.recurse) { + // this node is a directory; recurse + files = requireDirectory(m, joined, options); + // exclude empty directories + if (Object.keys(files).length) { + retval[options.rename(filename, joined, filename)] = files; + } + } else { + if (joined !== m.filename && checkFileInclusion(joined, filename, options)) { + // hash node key shouldn't include file extension + key = filename.substring(0, filename.lastIndexOf('.')); + obj = m.require(joined); + retval[options.rename(key, joined, filename)] = options.visit(obj, joined, filename) || obj; + } + } + }); + + return retval; +} + +module.exports = requireDirectory; +module.exports.defaults = defaultOptions; diff --git a/WebServer/node_modules/require-directory/package.json b/WebServer/node_modules/require-directory/package.json new file mode 100644 index 000000000..0e547b90f --- /dev/null +++ b/WebServer/node_modules/require-directory/package.json @@ -0,0 +1,73 @@ +{ + "_args": [ + [ + "require-directory@2.1.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "require-directory@2.1.1", + "_id": "require-directory@2.1.1", + "_inBundle": false, + "_integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "_location": "/require-directory", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "require-directory@2.1.1", + "name": "require-directory", + "escapedName": "require-directory", + "rawSpec": "2.1.1", + "saveSpec": null, + "fetchSpec": "2.1.1" + }, + "_requiredBy": [ + "/yargs", + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "_spec": "2.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Troy Goode", + "email": "troygoode@gmail.com", + "url": "http://github.com/troygoode/" + }, + "bugs": { + "url": "http://github.com/troygoode/node-require-directory/issues/" + }, + "contributors": [ + { + "name": "Troy Goode", + "email": "troygoode@gmail.com", + "url": "http://github.com/troygoode/" + } + ], + "description": "Recursively iterates over specified directory, require()'ing each file, and returning a nested hash structure containing those modules.", + "devDependencies": { + "jshint": "^2.6.0", + "mocha": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "homepage": "https://github.com/troygoode/node-require-directory/", + "keywords": [ + "require", + "directory", + "library", + "recursive" + ], + "license": "MIT", + "main": "index.js", + "name": "require-directory", + "repository": { + "type": "git", + "url": "git://github.com/troygoode/node-require-directory.git" + }, + "scripts": { + "lint": "jshint index.js test/test.js", + "test": "mocha" + }, + "version": "2.1.1" +} diff --git a/WebServer/node_modules/require-main-filename/index.js b/WebServer/node_modules/require-main-filename/index.js new file mode 100644 index 000000000..dca7f0cc1 --- /dev/null +++ b/WebServer/node_modules/require-main-filename/index.js @@ -0,0 +1,18 @@ +module.exports = function (_require) { + _require = _require || require + var main = _require.main + if (main && isIISNode(main)) return handleIISNode(main) + else return main ? main.filename : process.cwd() +} + +function isIISNode (main) { + return /\\iisnode\\/.test(main.filename) +} + +function handleIISNode (main) { + if (!main.children.length) { + return main.filename + } else { + return main.children[0].filename + } +} diff --git a/WebServer/node_modules/require-main-filename/package.json b/WebServer/node_modules/require-main-filename/package.json new file mode 100644 index 000000000..62564c58b --- /dev/null +++ b/WebServer/node_modules/require-main-filename/package.json @@ -0,0 +1,66 @@ +{ + "_args": [ + [ + "require-main-filename@2.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "require-main-filename@2.0.0", + "_id": "require-main-filename@2.0.0", + "_inBundle": false, + "_integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "_location": "/require-main-filename", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "require-main-filename@2.0.0", + "name": "require-main-filename", + "escapedName": "require-main-filename", + "rawSpec": "2.0.0", + "saveSpec": null, + "fetchSpec": "2.0.0" + }, + "_requiredBy": [ + "/yargs" + ], + "_resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "_spec": "2.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/yargs/require-main-filename/issues" + }, + "description": "shim for require.main.filename() that works in as many environments as possible", + "devDependencies": { + "chai": "^4.0.0", + "standard": "^10.0.3", + "standard-version": "^4.0.0", + "tap": "^11.0.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/yargs/require-main-filename#readme", + "keywords": [ + "require", + "shim", + "iisnode" + ], + "license": "ISC", + "main": "index.js", + "name": "require-main-filename", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/yargs/require-main-filename.git" + }, + "scripts": { + "pretest": "standard", + "release": "standard-version", + "test": "tap --coverage test.js" + }, + "version": "2.0.0" +} diff --git a/WebServer/node_modules/rimraf/bin.js b/WebServer/node_modules/rimraf/bin.js new file mode 100644 index 000000000..023814cc9 --- /dev/null +++ b/WebServer/node_modules/rimraf/bin.js @@ -0,0 +1,68 @@ +#!/usr/bin/env node + +const rimraf = require('./') + +const path = require('path') + +const isRoot = arg => /^(\/|[a-zA-Z]:\\)$/.test(path.resolve(arg)) +const filterOutRoot = arg => { + const ok = preserveRoot === false || !isRoot(arg) + if (!ok) { + console.error(`refusing to remove ${arg}`) + console.error('Set --no-preserve-root to allow this') + } + return ok +} + +let help = false +let dashdash = false +let noglob = false +let preserveRoot = true +const args = process.argv.slice(2).filter(arg => { + if (dashdash) + return !!arg + else if (arg === '--') + dashdash = true + else if (arg === '--no-glob' || arg === '-G') + noglob = true + else if (arg === '--glob' || arg === '-g') + noglob = false + else if (arg.match(/^(-+|\/)(h(elp)?|\?)$/)) + help = true + else if (arg === '--preserve-root') + preserveRoot = true + else if (arg === '--no-preserve-root') + preserveRoot = false + else + return !!arg +}).filter(arg => !preserveRoot || filterOutRoot(arg)) + +const go = n => { + if (n >= args.length) + return + const options = noglob ? { glob: false } : {} + rimraf(args[n], options, er => { + if (er) + throw er + go(n+1) + }) +} + +if (help || args.length === 0) { + // If they didn't ask for help, then this is not a "success" + const log = help ? console.log : console.error + log('Usage: rimraf [ ...]') + log('') + log(' Deletes all files and folders at "path" recursively.') + log('') + log('Options:') + log('') + log(' -h, --help Display this usage info') + log(' -G, --no-glob Do not expand glob patterns in arguments') + log(' -g, --glob Expand glob patterns in arguments (default)') + log(' --preserve-root Do not remove \'/\' (default)') + log(' --no-preserve-root Do not treat \'/\' specially') + log(' -- Stop parsing flags') + process.exit(help ? 0 : 1) +} else + go(0) diff --git a/WebServer/node_modules/rimraf/package.json b/WebServer/node_modules/rimraf/package.json new file mode 100644 index 000000000..3be21a5dc --- /dev/null +++ b/WebServer/node_modules/rimraf/package.json @@ -0,0 +1,70 @@ +{ + "_args": [ + [ + "rimraf@3.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "rimraf@3.0.0", + "_id": "rimraf@3.0.0", + "_inBundle": false, + "_integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "_location": "/rimraf", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "rimraf@3.0.0", + "name": "rimraf", + "escapedName": "rimraf", + "rawSpec": "3.0.0", + "saveSpec": null, + "fetchSpec": "3.0.0" + }, + "_requiredBy": [ + "/" + ], + "_resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "_spec": "3.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bin": { + "rimraf": "./bin.js" + }, + "bugs": { + "url": "https://github.com/isaacs/rimraf/issues" + }, + "dependencies": { + "glob": "^7.1.3" + }, + "description": "A deep deletion module for node (like `rm -rf`)", + "devDependencies": { + "mkdirp": "^0.5.1", + "tap": "^12.1.1" + }, + "files": [ + "LICENSE", + "README.md", + "bin.js", + "rimraf.js" + ], + "homepage": "https://github.com/isaacs/rimraf#readme", + "license": "ISC", + "main": "rimraf.js", + "name": "rimraf", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/rimraf.git" + }, + "scripts": { + "postpublish": "git push origin --follow-tags", + "postversion": "npm publish", + "preversion": "npm test", + "test": "tap test/*.js" + }, + "version": "3.0.0" +} diff --git a/WebServer/node_modules/rimraf/rimraf.js b/WebServer/node_modules/rimraf/rimraf.js new file mode 100644 index 000000000..309b8cad2 --- /dev/null +++ b/WebServer/node_modules/rimraf/rimraf.js @@ -0,0 +1,368 @@ +const assert = require("assert") +const path = require("path") +const fs = require("fs") +let glob = undefined +try { + glob = require("glob") +} catch (_err) { + // treat glob as optional. +} + +const defaultGlobOpts = { + nosort: true, + silent: true +} + +// for EMFILE handling +let timeout = 0 + +const isWindows = (process.platform === "win32") + +const defaults = options => { + const methods = [ + 'unlink', + 'chmod', + 'stat', + 'lstat', + 'rmdir', + 'readdir' + ] + methods.forEach(m => { + options[m] = options[m] || fs[m] + m = m + 'Sync' + options[m] = options[m] || fs[m] + }) + + options.maxBusyTries = options.maxBusyTries || 3 + options.emfileWait = options.emfileWait || 1000 + if (options.glob === false) { + options.disableGlob = true + } + if (options.disableGlob !== true && glob === undefined) { + throw Error('glob dependency not found, set `options.disableGlob = true` if intentional') + } + options.disableGlob = options.disableGlob || false + options.glob = options.glob || defaultGlobOpts +} + +const rimraf = (p, options, cb) => { + if (typeof options === 'function') { + cb = options + options = {} + } + + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert.equal(typeof cb, 'function', 'rimraf: callback function required') + assert(options, 'rimraf: invalid options argument provided') + assert.equal(typeof options, 'object', 'rimraf: options should be object') + + defaults(options) + + let busyTries = 0 + let errState = null + let n = 0 + + const next = (er) => { + errState = errState || er + if (--n === 0) + cb(errState) + } + + const afterGlob = (er, results) => { + if (er) + return cb(er) + + n = results.length + if (n === 0) + return cb() + + results.forEach(p => { + const CB = (er) => { + if (er) { + if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && + busyTries < options.maxBusyTries) { + busyTries ++ + // try again, with the same exact callback as this one. + return setTimeout(() => rimraf_(p, options, CB), busyTries * 100) + } + + // this one won't happen if graceful-fs is used. + if (er.code === "EMFILE" && timeout < options.emfileWait) { + return setTimeout(() => rimraf_(p, options, CB), timeout ++) + } + + // already gone + if (er.code === "ENOENT") er = null + } + + timeout = 0 + next(er) + } + rimraf_(p, options, CB) + }) + } + + if (options.disableGlob || !glob.hasMagic(p)) + return afterGlob(null, [p]) + + options.lstat(p, (er, stat) => { + if (!er) + return afterGlob(null, [p]) + + glob(p, options.glob, afterGlob) + }) + +} + +// Two possible strategies. +// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR +// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR +// +// Both result in an extra syscall when you guess wrong. However, there +// are likely far more normal files in the world than directories. This +// is based on the assumption that a the average number of files per +// directory is >= 1. +// +// If anyone ever complains about this, then I guess the strategy could +// be made configurable somehow. But until then, YAGNI. +const rimraf_ = (p, options, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') + + // sunos lets the root user unlink directories, which is... weird. + // so we have to lstat here and make sure it's not a dir. + options.lstat(p, (er, st) => { + if (er && er.code === "ENOENT") + return cb(null) + + // Windows can EPERM on stat. Life is suffering. + if (er && er.code === "EPERM" && isWindows) + fixWinEPERM(p, options, er, cb) + + if (st && st.isDirectory()) + return rmdir(p, options, er, cb) + + options.unlink(p, er => { + if (er) { + if (er.code === "ENOENT") + return cb(null) + if (er.code === "EPERM") + return (isWindows) + ? fixWinEPERM(p, options, er, cb) + : rmdir(p, options, er, cb) + if (er.code === "EISDIR") + return rmdir(p, options, er, cb) + } + return cb(er) + }) + }) +} + +const fixWinEPERM = (p, options, er, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') + if (er) + assert(er instanceof Error) + + options.chmod(p, 0o666, er2 => { + if (er2) + cb(er2.code === "ENOENT" ? null : er) + else + options.stat(p, (er3, stats) => { + if (er3) + cb(er3.code === "ENOENT" ? null : er) + else if (stats.isDirectory()) + rmdir(p, options, er, cb) + else + options.unlink(p, cb) + }) + }) +} + +const fixWinEPERMSync = (p, options, er) => { + assert(p) + assert(options) + if (er) + assert(er instanceof Error) + + try { + options.chmodSync(p, 0o666) + } catch (er2) { + if (er2.code === "ENOENT") + return + else + throw er + } + + let stats + try { + stats = options.statSync(p) + } catch (er3) { + if (er3.code === "ENOENT") + return + else + throw er + } + + if (stats.isDirectory()) + rmdirSync(p, options, er) + else + options.unlinkSync(p) +} + +const rmdir = (p, options, originalEr, cb) => { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) + assert(typeof cb === 'function') + + // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) + // if we guessed wrong, and it's not a directory, then + // raise the original error. + options.rmdir(p, er => { + if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) + rmkids(p, options, cb) + else if (er && er.code === "ENOTDIR") + cb(originalEr) + else + cb(er) + }) +} + +const rmkids = (p, options, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') + + options.readdir(p, (er, files) => { + if (er) + return cb(er) + let n = files.length + if (n === 0) + return options.rmdir(p, cb) + let errState + files.forEach(f => { + rimraf(path.join(p, f), options, er => { + if (errState) + return + if (er) + return cb(errState = er) + if (--n === 0) + options.rmdir(p, cb) + }) + }) + }) +} + +// this looks simpler, and is strictly *faster*, but will +// tie up the JavaScript thread and fail on excessively +// deep directory trees. +const rimrafSync = (p, options) => { + options = options || {} + defaults(options) + + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.equal(typeof options, 'object', 'rimraf: options should be object') + + let results + + if (options.disableGlob || !glob.hasMagic(p)) { + results = [p] + } else { + try { + options.lstatSync(p) + results = [p] + } catch (er) { + results = glob.sync(p, options.glob) + } + } + + if (!results.length) + return + + for (let i = 0; i < results.length; i++) { + const p = results[i] + + let st + try { + st = options.lstatSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + + // Windows can EPERM on stat. Life is suffering. + if (er.code === "EPERM" && isWindows) + fixWinEPERMSync(p, options, er) + } + + try { + // sunos lets the root user unlink directories, which is... weird. + if (st && st.isDirectory()) + rmdirSync(p, options, null) + else + options.unlinkSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "EPERM") + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) + if (er.code !== "EISDIR") + throw er + + rmdirSync(p, options, er) + } + } +} + +const rmdirSync = (p, options, originalEr) => { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) + + try { + options.rmdirSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "ENOTDIR") + throw originalEr + if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") + rmkidsSync(p, options) + } +} + +const rmkidsSync = (p, options) => { + assert(p) + assert(options) + options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) + + // We only end up here once we got ENOTEMPTY at least once, and + // at this point, we are guaranteed to have removed all the kids. + // So, we know that it won't be ENOENT or ENOTDIR or anything else. + // try really hard to delete stuff on windows, because it has a + // PROFOUNDLY annoying habit of not closing handles promptly when + // files are deleted, resulting in spurious ENOTEMPTY errors. + const retries = isWindows ? 100 : 1 + let i = 0 + do { + let threw = true + try { + const ret = options.rmdirSync(p, options) + threw = false + return ret + } finally { + if (++i < retries && threw) + continue + } + } while (true) +} + +module.exports = rimraf +rimraf.sync = rimrafSync diff --git a/WebServer/node_modules/semver/package.json b/WebServer/node_modules/semver/package.json new file mode 100644 index 000000000..eebf3e325 --- /dev/null +++ b/WebServer/node_modules/semver/package.json @@ -0,0 +1,64 @@ +{ + "_args": [ + [ + "semver@5.7.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "semver@5.7.0", + "_id": "semver@5.7.0", + "_inBundle": false, + "_integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "_location": "/semver", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "semver@5.7.0", + "name": "semver", + "escapedName": "semver", + "rawSpec": "5.7.0", + "saveSpec": null, + "fetchSpec": "5.7.0" + }, + "_requiredBy": [ + "/cross-spawn", + "/node-environment-flags" + ], + "_resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "_spec": "5.7.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "bin": { + "semver": "./bin/semver" + }, + "bugs": { + "url": "https://github.com/npm/node-semver/issues" + }, + "description": "The semantic version parser used by npm.", + "devDependencies": { + "tap": "^13.0.0-rc.18" + }, + "files": [ + "bin", + "range.bnf", + "semver.js" + ], + "homepage": "https://github.com/npm/node-semver#readme", + "license": "ISC", + "main": "semver.js", + "name": "semver", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/node-semver.git" + }, + "scripts": { + "postpublish": "git push origin --all; git push origin --tags", + "postversion": "npm publish", + "preversion": "npm test", + "test": "tap" + }, + "tap": { + "check-coverage": true + }, + "version": "5.7.0" +} diff --git a/WebServer/node_modules/semver/semver.js b/WebServer/node_modules/semver/semver.js new file mode 100644 index 000000000..d315d5d68 --- /dev/null +++ b/WebServer/node_modules/semver/semver.js @@ -0,0 +1,1483 @@ +exports = module.exports = SemVer + +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} +} + +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' + +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 + +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 + +// The actual regexps go on exports.re +var re = exports.re = [] +var src = exports.src = [] +var R = 0 + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +var NUMERICIDENTIFIER = R++ +src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' +var NUMERICIDENTIFIERLOOSE = R++ +src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +var NONNUMERICIDENTIFIER = R++ +src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' + +// ## Main Version +// Three dot-separated numeric identifiers. + +var MAINVERSION = R++ +src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')' + +var MAINVERSIONLOOSE = R++ +src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')' + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +var PRERELEASEIDENTIFIER = R++ +src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + + '|' + src[NONNUMERICIDENTIFIER] + ')' + +var PRERELEASEIDENTIFIERLOOSE = R++ +src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + + '|' + src[NONNUMERICIDENTIFIER] + ')' + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +var PRERELEASE = R++ +src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' + +var PRERELEASELOOSE = R++ +src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +var BUILDIDENTIFIER = R++ +src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +var BUILD = R++ +src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +var FULL = R++ +var FULLPLAIN = 'v?' + src[MAINVERSION] + + src[PRERELEASE] + '?' + + src[BUILD] + '?' + +src[FULL] = '^' + FULLPLAIN + '$' + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + + src[PRERELEASELOOSE] + '?' + + src[BUILD] + '?' + +var LOOSE = R++ +src[LOOSE] = '^' + LOOSEPLAIN + '$' + +var GTLT = R++ +src[GTLT] = '((?:<|>)?=?)' + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +var XRANGEIDENTIFIERLOOSE = R++ +src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +var XRANGEIDENTIFIER = R++ +src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' + +var XRANGEPLAIN = R++ +src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:' + src[PRERELEASE] + ')?' + + src[BUILD] + '?' + + ')?)?' + +var XRANGEPLAINLOOSE = R++ +src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[PRERELEASELOOSE] + ')?' + + src[BUILD] + '?' + + ')?)?' + +var XRANGE = R++ +src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' +var XRANGELOOSE = R++ +src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +var COERCE = R++ +src[COERCE] = '(?:^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +var LONETILDE = R++ +src[LONETILDE] = '(?:~>?)' + +var TILDETRIM = R++ +src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' +re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') +var tildeTrimReplace = '$1~' + +var TILDE = R++ +src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' +var TILDELOOSE = R++ +src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +var LONECARET = R++ +src[LONECARET] = '(?:\\^)' + +var CARETTRIM = R++ +src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' +re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') +var caretTrimReplace = '$1^' + +var CARET = R++ +src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' +var CARETLOOSE = R++ +src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +var COMPARATORLOOSE = R++ +src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' +var COMPARATOR = R++ +src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +var COMPARATORTRIM = R++ +src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' + +// this one has to use the /g flag +re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') +var comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +var HYPHENRANGE = R++ +src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAIN] + ')' + + '\\s*$' + +var HYPHENRANGELOOSE = R++ +src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s*$' + +// Star ranges basically just allow anything at all. +var STAR = R++ +src[STAR] = '(<|>)?=?\\s*\\*' + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + } +} + +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + if (version.length > MAX_LENGTH) { + return null + } + + var r = options.loose ? re[LOOSE] : re[FULL] + if (!r.test(version)) { + return null + } + + try { + return new SemVer(version, options) + } catch (er) { + return null + } +} + +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null +} + +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null +} + +exports.SemVer = SemVer + +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } + + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } + + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } + + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose + + var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) + + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } + + this.raw = version + + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] + + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num + } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() +} + +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version +} + +SemVer.prototype.toString = function () { + return this.version +} + +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + return this.compareMain(other) || this.comparePre(other) +} + +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) +} + +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) +} + +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } + } + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) + } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] + } + } + break + + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} + +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } + + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} + +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key + } + } + } + return defaultResult // may be undefined + } +} + +exports.compareIdentifiers = compareIdentifiers + +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) + + if (anum && bnum) { + a = +a + b = +b + } + + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} + +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} + +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major +} + +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor +} + +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch +} + +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) +} + +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) +} + +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} + +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compare(a, b, loose) + }) +} + +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.rcompare(a, b, loose) + }) +} + +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} + +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 +} + +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} + +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} + +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} + +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} + +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b + + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b + + case '': + case '=': + case '==': + return eq(a, b, loose) + + case '!=': + return neq(a, b, loose) + + case '>': + return gt(a, b, loose) + + case '>=': + return gte(a, b, loose) + + case '<': + return lt(a, b, loose) + + case '<=': + return lte(a, b, loose) + + default: + throw new TypeError('Invalid operator: ' + op) + } +} + +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } + + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } + + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) + + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } + + debug('comp', this) +} + +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var m = comp.match(r) + + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } + + this.operator = m[1] + if (this.operator === '=') { + this.operator = '' + } + + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} + +Comparator.prototype.toString = function () { + return this.value +} + +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) + + if (this.semver === ANY) { + return true + } + + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } + + return cmp(version, this.operator, this.semver, this.options) +} + +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') + } + + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + var rangeTmp + + if (this.operator === '') { + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) + } + + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) + + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan +} + +exports.Range = Range +function Range (range, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range + } else { + return new Range(range.raw, options) + } + } + + if (range instanceof Comparator) { + return new Range(range.value, options) + } + + if (!(this instanceof Range)) { + return new Range(range, options) + } + + this.options = options + this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease + + // First, split based on boolean or || + this.raw = range + this.set = range.split(/\s*\|\|\s*/).map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) + + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range) + } + + this.format() +} + +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range +} + +Range.prototype.toString = function () { + return this.range +} + +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + range = range.trim() + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, re[COMPARATORTRIM]) + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace) + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace) + + // normalize spaces + range = range.split(/\s+/).join(' ') + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) + } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) + + return set +} + +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') + } + + return this.set.some(function (thisComparators) { + return thisComparators.every(function (thisComparator) { + return range.set.some(function (rangeComparators) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + }) + }) +} + +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) +} + +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp +} + +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' +} + +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') +} + +function replaceTilde (comp, options) { + var r = options.loose ? re[TILDELOOSE] : re[TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + + debug('tilde return', ret) + return ret + }) +} + +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') +} + +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? re[CARETLOOSE] : re[CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } + + debug('caret return', ret) + return ret + }) +} + +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') +} + +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp + + if (gtlt === '=' && anyX) { + gtlt = '' + } + + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 + + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } + + ret = gtlt + M + '.' + m + '.' + p + } else if (xm) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (xp) { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } + + debug('xRange return', ret) + + return ret + }) +} + +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], '') +} + +// This function is passed to string.replace(re[HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0 +function hyphenReplace ($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + if (isX(fM)) { + from = '' + } else if (isX(fm)) { + from = '>=' + fM + '.0.0' + } else if (isX(fp)) { + from = '>=' + fM + '.' + fm + '.0' + } else { + from = '>=' + from + } + + if (isX(tM)) { + to = '' + } else if (isX(tm)) { + to = '<' + (+tM + 1) + '.0.0' + } else if (isX(tp)) { + to = '<' + tM + '.' + (+tm + 1) + '.0' + } else if (tpr) { + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr + } else { + to = '<=' + to + } + + return (from + ' ' + to).trim() +} + +// if ANY of the sets match ALL of its comparators, then pass +Range.prototype.test = function (version) { + if (!version) { + return false + } + + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } + + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version, this.options)) { + return true + } + } + return false +} + +function testSet (set, version, options) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) { + return false + } + } + + if (version.prerelease.length && !options.includePrerelease) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (i = 0; i < set.length; i++) { + debug(set[i].semver) + if (set[i].semver === ANY) { + continue + } + + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) { + return true + } + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false + } + + return true +} + +exports.satisfies = satisfies +function satisfies (version, range, options) { + try { + range = new Range(range, options) + } catch (er) { + return false + } + return range.test(version) +} + +exports.maxSatisfying = maxSatisfying +function maxSatisfying (versions, range, options) { + var max = null + var maxSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!max || maxSV.compare(v) === -1) { + // compare(max, v, true) + max = v + maxSV = new SemVer(max, options) + } + } + }) + return max +} + +exports.minSatisfying = minSatisfying +function minSatisfying (versions, range, options) { + var min = null + var minSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!min || minSV.compare(v) === 1) { + // compare(min, v, true) + min = v + minSV = new SemVer(min, options) + } + } + }) + return min +} + +exports.minVersion = minVersion +function minVersion (range, loose) { + range = new Range(range, loose) + + var minver = new SemVer('0.0.0') + if (range.test(minver)) { + return minver + } + + minver = new SemVer('0.0.0-0') + if (range.test(minver)) { + return minver + } + + minver = null + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] + + comparators.forEach(function (comparator) { + // Clone to avoid manipulating the comparator's semver object. + var compver = new SemVer(comparator.semver.version) + switch (comparator.operator) { + case '>': + if (compver.prerelease.length === 0) { + compver.patch++ + } else { + compver.prerelease.push(0) + } + compver.raw = compver.format() + /* fallthrough */ + case '': + case '>=': + if (!minver || gt(minver, compver)) { + minver = compver + } + break + case '<': + case '<=': + /* Ignore maximum versions */ + break + /* istanbul ignore next */ + default: + throw new Error('Unexpected operation: ' + comparator.operator) + } + }) + } + + if (minver && range.test(minver)) { + return minver + } + + return null +} + +exports.validRange = validRange +function validRange (range, options) { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, options).range || '*' + } catch (er) { + return null + } +} + +// Determine if version is less than all the versions possible in the range +exports.ltr = ltr +function ltr (version, range, options) { + return outside(version, range, '<', options) +} + +// Determine if version is greater than all the versions possible in the range. +exports.gtr = gtr +function gtr (version, range, options) { + return outside(version, range, '>', options) +} + +exports.outside = outside +function outside (version, range, hilo, options) { + version = new SemVer(version, options) + range = new Range(range, options) + + var gtfn, ltefn, ltfn, comp, ecomp + switch (hilo) { + case '>': + gtfn = gt + ltefn = lte + ltfn = lt + comp = '>' + ecomp = '>=' + break + case '<': + gtfn = lt + ltefn = gte + ltfn = gt + comp = '<' + ecomp = '<=' + break + default: + throw new TypeError('Must provide a hilo val of "<" or ">"') + } + + // If it satisifes the range it is not outside + if (satisfies(version, range, options)) { + return false + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] + + var high = null + var low = null + + comparators.forEach(function (comparator) { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0') + } + high = high || comparator + low = low || comparator + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator + } + }) + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false + } + } + return true +} + +exports.prerelease = prerelease +function prerelease (version, options) { + var parsed = parse(version, options) + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null +} + +exports.intersects = intersects +function intersects (r1, r2, options) { + r1 = new Range(r1, options) + r2 = new Range(r2, options) + return r1.intersects(r2) +} + +exports.coerce = coerce +function coerce (version) { + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + var match = version.match(re[COERCE]) + + if (match == null) { + return null + } + + return parse(match[1] + + '.' + (match[2] || '0') + + '.' + (match[3] || '0')) +} diff --git a/WebServer/node_modules/set-blocking/index.js b/WebServer/node_modules/set-blocking/index.js new file mode 100644 index 000000000..6f78774bb --- /dev/null +++ b/WebServer/node_modules/set-blocking/index.js @@ -0,0 +1,7 @@ +module.exports = function (blocking) { + [process.stdout, process.stderr].forEach(function (stream) { + if (stream._handle && stream.isTTY && typeof stream._handle.setBlocking === 'function') { + stream._handle.setBlocking(blocking) + } + }) +} diff --git a/WebServer/node_modules/set-blocking/package.json b/WebServer/node_modules/set-blocking/package.json new file mode 100644 index 000000000..e19d803d4 --- /dev/null +++ b/WebServer/node_modules/set-blocking/package.json @@ -0,0 +1,74 @@ +{ + "_args": [ + [ + "set-blocking@2.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "set-blocking@2.0.0", + "_id": "set-blocking@2.0.0", + "_inBundle": false, + "_integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "_location": "/set-blocking", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "set-blocking@2.0.0", + "name": "set-blocking", + "escapedName": "set-blocking", + "rawSpec": "2.0.0", + "saveSpec": null, + "fetchSpec": "2.0.0" + }, + "_requiredBy": [ + "/yargs", + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "_spec": "2.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/yargs/set-blocking/issues" + }, + "description": "set blocking stdio and stderr ensuring that terminal output does not truncate", + "devDependencies": { + "chai": "^3.5.0", + "coveralls": "^2.11.9", + "mocha": "^2.4.5", + "nyc": "^6.4.4", + "standard": "^7.0.1", + "standard-version": "^2.2.1" + }, + "files": [ + "index.js", + "LICENSE.txt" + ], + "homepage": "https://github.com/yargs/set-blocking#readme", + "keywords": [ + "flush", + "terminal", + "blocking", + "shim", + "stdio", + "stderr" + ], + "license": "ISC", + "main": "index.js", + "name": "set-blocking", + "repository": { + "type": "git", + "url": "git+https://github.com/yargs/set-blocking.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "pretest": "standard", + "test": "nyc mocha ./test/*.js", + "version": "standard-version" + }, + "version": "2.0.0" +} diff --git a/WebServer/node_modules/shebang-command/index.js b/WebServer/node_modules/shebang-command/index.js new file mode 100644 index 000000000..2de70b074 --- /dev/null +++ b/WebServer/node_modules/shebang-command/index.js @@ -0,0 +1,19 @@ +'use strict'; +var shebangRegex = require('shebang-regex'); + +module.exports = function (str) { + var match = str.match(shebangRegex); + + if (!match) { + return null; + } + + var arr = match[0].replace(/#! ?/, '').split(' '); + var bin = arr[0].split('/').pop(); + var arg = arr[1]; + + return (bin === 'env' ? + arg : + bin + (arg ? ' ' + arg : '') + ); +}; diff --git a/WebServer/node_modules/shebang-command/package.json b/WebServer/node_modules/shebang-command/package.json new file mode 100644 index 000000000..f773a8e55 --- /dev/null +++ b/WebServer/node_modules/shebang-command/package.json @@ -0,0 +1,74 @@ +{ + "_args": [ + [ + "shebang-command@1.2.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "shebang-command@1.2.0", + "_id": "shebang-command@1.2.0", + "_inBundle": false, + "_integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "_location": "/shebang-command", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "shebang-command@1.2.0", + "name": "shebang-command", + "escapedName": "shebang-command", + "rawSpec": "1.2.0", + "saveSpec": null, + "fetchSpec": "1.2.0" + }, + "_requiredBy": [ + "/cross-spawn" + ], + "_resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "_spec": "1.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Kevin Martensson", + "email": "kevinmartensson@gmail.com", + "url": "github.com/kevva" + }, + "bugs": { + "url": "https://github.com/kevva/shebang-command/issues" + }, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "description": "Get the command from a shebang", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/kevva/shebang-command#readme", + "keywords": [ + "cmd", + "command", + "parse", + "shebang" + ], + "license": "MIT", + "name": "shebang-command", + "repository": { + "type": "git", + "url": "git+https://github.com/kevva/shebang-command.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.2.0", + "xo": { + "ignores": [ + "test.js" + ] + } +} diff --git a/WebServer/node_modules/shebang-regex/index.js b/WebServer/node_modules/shebang-regex/index.js new file mode 100644 index 000000000..d052d2e05 --- /dev/null +++ b/WebServer/node_modules/shebang-regex/index.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = /^#!.*/; diff --git a/WebServer/node_modules/shebang-regex/package.json b/WebServer/node_modules/shebang-regex/package.json new file mode 100644 index 000000000..4b6bf6407 --- /dev/null +++ b/WebServer/node_modules/shebang-regex/package.json @@ -0,0 +1,67 @@ +{ + "_args": [ + [ + "shebang-regex@1.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "shebang-regex@1.0.0", + "_id": "shebang-regex@1.0.0", + "_inBundle": false, + "_integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "_location": "/shebang-regex", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "shebang-regex@1.0.0", + "name": "shebang-regex", + "escapedName": "shebang-regex", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/shebang-command" + ], + "_resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "_spec": "1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/shebang-regex/issues" + }, + "description": "Regular expression for matching a shebang", + "devDependencies": { + "ava": "0.0.4" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/shebang-regex#readme", + "keywords": [ + "re", + "regex", + "regexp", + "shebang", + "match", + "test" + ], + "license": "MIT", + "name": "shebang-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/shebang-regex.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.0.0" +} diff --git a/WebServer/node_modules/signal-exit/index.js b/WebServer/node_modules/signal-exit/index.js new file mode 100644 index 000000000..337f691ed --- /dev/null +++ b/WebServer/node_modules/signal-exit/index.js @@ -0,0 +1,157 @@ +// Note: since nyc uses this module to output coverage, any lines +// that are in the direct sync flow of nyc's outputCoverage are +// ignored, since we can never get coverage for them. +var assert = require('assert') +var signals = require('./signals.js') + +var EE = require('events') +/* istanbul ignore if */ +if (typeof EE !== 'function') { + EE = EE.EventEmitter +} + +var emitter +if (process.__signal_exit_emitter__) { + emitter = process.__signal_exit_emitter__ +} else { + emitter = process.__signal_exit_emitter__ = new EE() + emitter.count = 0 + emitter.emitted = {} +} + +// Because this emitter is a global, we have to check to see if a +// previous version of this library failed to enable infinite listeners. +// I know what you're about to say. But literally everything about +// signal-exit is a compromise with evil. Get used to it. +if (!emitter.infinite) { + emitter.setMaxListeners(Infinity) + emitter.infinite = true +} + +module.exports = function (cb, opts) { + assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler') + + if (loaded === false) { + load() + } + + var ev = 'exit' + if (opts && opts.alwaysLast) { + ev = 'afterexit' + } + + var remove = function () { + emitter.removeListener(ev, cb) + if (emitter.listeners('exit').length === 0 && + emitter.listeners('afterexit').length === 0) { + unload() + } + } + emitter.on(ev, cb) + + return remove +} + +module.exports.unload = unload +function unload () { + if (!loaded) { + return + } + loaded = false + + signals.forEach(function (sig) { + try { + process.removeListener(sig, sigListeners[sig]) + } catch (er) {} + }) + process.emit = originalProcessEmit + process.reallyExit = originalProcessReallyExit + emitter.count -= 1 +} + +function emit (event, code, signal) { + if (emitter.emitted[event]) { + return + } + emitter.emitted[event] = true + emitter.emit(event, code, signal) +} + +// { : , ... } +var sigListeners = {} +signals.forEach(function (sig) { + sigListeners[sig] = function listener () { + // If there are no other listeners, an exit is coming! + // Simplest way: remove us and then re-send the signal. + // We know that this will kill the process, so we can + // safely emit now. + var listeners = process.listeners(sig) + if (listeners.length === emitter.count) { + unload() + emit('exit', null, sig) + /* istanbul ignore next */ + emit('afterexit', null, sig) + /* istanbul ignore next */ + process.kill(process.pid, sig) + } + } +}) + +module.exports.signals = function () { + return signals +} + +module.exports.load = load + +var loaded = false + +function load () { + if (loaded) { + return + } + loaded = true + + // This is the number of onSignalExit's that are in play. + // It's important so that we can count the correct number of + // listeners on signals, and don't wait for the other one to + // handle it instead of us. + emitter.count += 1 + + signals = signals.filter(function (sig) { + try { + process.on(sig, sigListeners[sig]) + return true + } catch (er) { + return false + } + }) + + process.emit = processEmit + process.reallyExit = processReallyExit +} + +var originalProcessReallyExit = process.reallyExit +function processReallyExit (code) { + process.exitCode = code || 0 + emit('exit', process.exitCode, null) + /* istanbul ignore next */ + emit('afterexit', process.exitCode, null) + /* istanbul ignore next */ + originalProcessReallyExit.call(process, process.exitCode) +} + +var originalProcessEmit = process.emit +function processEmit (ev, arg) { + if (ev === 'exit') { + if (arg !== undefined) { + process.exitCode = arg + } + var ret = originalProcessEmit.apply(this, arguments) + emit('exit', process.exitCode, null) + /* istanbul ignore next */ + emit('afterexit', process.exitCode, null) + return ret + } else { + return originalProcessEmit.apply(this, arguments) + } +} diff --git a/WebServer/node_modules/signal-exit/package.json b/WebServer/node_modules/signal-exit/package.json new file mode 100644 index 000000000..e195e85a5 --- /dev/null +++ b/WebServer/node_modules/signal-exit/package.json @@ -0,0 +1,69 @@ +{ + "_args": [ + [ + "signal-exit@3.0.2", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "signal-exit@3.0.2", + "_id": "signal-exit@3.0.2", + "_inBundle": false, + "_integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "_location": "/signal-exit", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "signal-exit@3.0.2", + "name": "signal-exit", + "escapedName": "signal-exit", + "rawSpec": "3.0.2", + "saveSpec": null, + "fetchSpec": "3.0.2" + }, + "_requiredBy": [ + "/execa" + ], + "_resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "_spec": "3.0.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/tapjs/signal-exit/issues" + }, + "description": "when you want to fire an event no matter how a process exits.", + "devDependencies": { + "chai": "^3.5.0", + "coveralls": "^2.11.10", + "nyc": "^8.1.0", + "standard": "^7.1.2", + "standard-version": "^2.3.0", + "tap": "^8.0.1" + }, + "files": [ + "index.js", + "signals.js" + ], + "homepage": "https://github.com/tapjs/signal-exit", + "keywords": [ + "signal", + "exit" + ], + "license": "ISC", + "main": "index.js", + "name": "signal-exit", + "repository": { + "type": "git", + "url": "git+https://github.com/tapjs/signal-exit.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "pretest": "standard", + "release": "standard-version", + "test": "tap --timeout=240 ./test/*.js --cov" + }, + "version": "3.0.2" +} diff --git a/WebServer/node_modules/signal-exit/signals.js b/WebServer/node_modules/signal-exit/signals.js new file mode 100644 index 000000000..3bd67a8a5 --- /dev/null +++ b/WebServer/node_modules/signal-exit/signals.js @@ -0,0 +1,53 @@ +// This is not the set of all possible signals. +// +// It IS, however, the set of all signals that trigger +// an exit on either Linux or BSD systems. Linux is a +// superset of the signal names supported on BSD, and +// the unknown signals just fail to register, so we can +// catch that easily enough. +// +// Don't bother with SIGKILL. It's uncatchable, which +// means that we can't fire any callbacks anyway. +// +// If a user does happen to register a handler on a non- +// fatal signal like SIGWINCH or something, and then +// exit, it'll end up firing `process.emit('exit')`, so +// the handler will be fired anyway. +// +// SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised +// artificially, inherently leave the process in a +// state from which it is not safe to try and enter JS +// listeners. +module.exports = [ + 'SIGABRT', + 'SIGALRM', + 'SIGHUP', + 'SIGINT', + 'SIGTERM' +] + +if (process.platform !== 'win32') { + module.exports.push( + 'SIGVTALRM', + 'SIGXCPU', + 'SIGXFSZ', + 'SIGUSR2', + 'SIGTRAP', + 'SIGSYS', + 'SIGQUIT', + 'SIGIOT' + // should detect profiler and enable/disable accordingly. + // see #21 + // 'SIGPROF' + ) +} + +if (process.platform === 'linux') { + module.exports.push( + 'SIGIO', + 'SIGPOLL', + 'SIGPWR', + 'SIGSTKFLT', + 'SIGUNUSED' + ) +} diff --git a/WebServer/node_modules/sprintf-js/bower.json b/WebServer/node_modules/sprintf-js/bower.json new file mode 100644 index 000000000..d90a75989 --- /dev/null +++ b/WebServer/node_modules/sprintf-js/bower.json @@ -0,0 +1,14 @@ +{ + "name": "sprintf", + "description": "JavaScript sprintf implementation", + "version": "1.0.3", + "main": "src/sprintf.js", + "license": "BSD-3-Clause-Clear", + "keywords": ["sprintf", "string", "formatting"], + "authors": ["Alexandru Marasteanu (http://alexei.ro/)"], + "homepage": "https://github.com/alexei/sprintf.js", + "repository": { + "type": "git", + "url": "git://github.com/alexei/sprintf.js.git" + } +} diff --git a/WebServer/node_modules/sprintf-js/demo/angular.html b/WebServer/node_modules/sprintf-js/demo/angular.html new file mode 100644 index 000000000..3559efd76 --- /dev/null +++ b/WebServer/node_modules/sprintf-js/demo/angular.html @@ -0,0 +1,20 @@ + + + + + + + + +
      {{ "%+010d"|sprintf:-123 }}
      +
      {{ "%+010d"|vsprintf:[-123] }}
      +
      {{ "%+010d"|fmt:-123 }}
      +
      {{ "%+010d"|vfmt:[-123] }}
      +
      {{ "I've got %2$d apples and %1$d oranges."|fmt:4:2 }}
      +
      {{ "I've got %(apples)d apples and %(oranges)d oranges."|fmt:{apples: 2, oranges: 4} }}
      + + + + diff --git a/WebServer/node_modules/sprintf-js/dist/angular-sprintf.min.js b/WebServer/node_modules/sprintf-js/dist/angular-sprintf.min.js new file mode 100644 index 000000000..dbaf744d8 --- /dev/null +++ b/WebServer/node_modules/sprintf-js/dist/angular-sprintf.min.js @@ -0,0 +1,4 @@ +/*! sprintf-js | Alexandru Marasteanu (http://alexei.ro/) | BSD-3-Clause */ + +angular.module("sprintf",[]).filter("sprintf",function(){return function(){return sprintf.apply(null,arguments)}}).filter("fmt",["$filter",function(a){return a("sprintf")}]).filter("vsprintf",function(){return function(a,b){return vsprintf(a,b)}}).filter("vfmt",["$filter",function(a){return a("vsprintf")}]); +//# sourceMappingURL=angular-sprintf.min.map \ No newline at end of file diff --git a/WebServer/node_modules/sprintf-js/dist/sprintf.min.js b/WebServer/node_modules/sprintf-js/dist/sprintf.min.js new file mode 100644 index 000000000..dc61e51ad --- /dev/null +++ b/WebServer/node_modules/sprintf-js/dist/sprintf.min.js @@ -0,0 +1,4 @@ +/*! sprintf-js | Alexandru Marasteanu (http://alexei.ro/) | BSD-3-Clause */ + +!function(a){function b(){var a=arguments[0],c=b.cache;return c[a]&&c.hasOwnProperty(a)||(c[a]=b.parse(a)),b.format.call(null,c[a],arguments)}function c(a){return Object.prototype.toString.call(a).slice(8,-1).toLowerCase()}function d(a,b){return Array(b+1).join(a)}var e={not_string:/[^s]/,number:/[diefg]/,json:/[j]/,not_json:/[^j]/,text:/^[^\x25]+/,modulo:/^\x25{2}/,placeholder:/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/,key:/^([a-z_][a-z_\d]*)/i,key_access:/^\.([a-z_][a-z_\d]*)/i,index_access:/^\[(\d+)\]/,sign:/^[\+\-]/};b.format=function(a,f){var g,h,i,j,k,l,m,n=1,o=a.length,p="",q=[],r=!0,s="";for(h=0;o>h;h++)if(p=c(a[h]),"string"===p)q[q.length]=a[h];else if("array"===p){if(j=a[h],j[2])for(g=f[n],i=0;i=0),j[8]){case"b":g=g.toString(2);break;case"c":g=String.fromCharCode(g);break;case"d":case"i":g=parseInt(g,10);break;case"j":g=JSON.stringify(g,null,j[6]?parseInt(j[6]):0);break;case"e":g=j[7]?g.toExponential(j[7]):g.toExponential();break;case"f":g=j[7]?parseFloat(g).toFixed(j[7]):parseFloat(g);break;case"g":g=j[7]?parseFloat(g).toPrecision(j[7]):parseFloat(g);break;case"o":g=g.toString(8);break;case"s":g=(g=String(g))&&j[7]?g.substring(0,j[7]):g;break;case"u":g>>>=0;break;case"x":g=g.toString(16);break;case"X":g=g.toString(16).toUpperCase()}e.json.test(j[8])?q[q.length]=g:(!e.number.test(j[8])||r&&!j[3]?s="":(s=r?"+":"-",g=g.toString().replace(e.sign,"")),l=j[4]?"0"===j[4]?"0":j[4].charAt(1):" ",m=j[6]-(s+g).length,k=j[6]&&m>0?d(l,m):"",q[q.length]=j[5]?s+g+k:"0"===l?s+k+g:k+s+g)}return q.join("")},b.cache={},b.parse=function(a){for(var b=a,c=[],d=[],f=0;b;){if(null!==(c=e.text.exec(b)))d[d.length]=c[0];else if(null!==(c=e.modulo.exec(b)))d[d.length]="%";else{if(null===(c=e.placeholder.exec(b)))throw new SyntaxError("[sprintf] unexpected placeholder");if(c[2]){f|=1;var g=[],h=c[2],i=[];if(null===(i=e.key.exec(h)))throw new SyntaxError("[sprintf] failed to parse named argument key");for(g[g.length]=i[1];""!==(h=h.substring(i[0].length));)if(null!==(i=e.key_access.exec(h)))g[g.length]=i[1];else{if(null===(i=e.index_access.exec(h)))throw new SyntaxError("[sprintf] failed to parse named argument key");g[g.length]=i[1]}c[2]=g}else f|=2;if(3===f)throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported");d[d.length]=c}b=b.substring(c[0].length)}return d};var f=function(a,c,d){return d=(c||[]).slice(0),d.splice(0,0,a),b.apply(null,d)};"undefined"!=typeof exports?(exports.sprintf=b,exports.vsprintf=f):(a.sprintf=b,a.vsprintf=f,"function"==typeof define&&define.amd&&define(function(){return{sprintf:b,vsprintf:f}}))}("undefined"==typeof window?this:window); +//# sourceMappingURL=sprintf.min.map \ No newline at end of file diff --git a/WebServer/node_modules/sprintf-js/gruntfile.js b/WebServer/node_modules/sprintf-js/gruntfile.js new file mode 100644 index 000000000..246e1c3b9 --- /dev/null +++ b/WebServer/node_modules/sprintf-js/gruntfile.js @@ -0,0 +1,36 @@ +module.exports = function(grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON("package.json"), + + uglify: { + options: { + banner: "/*! <%= pkg.name %> | <%= pkg.author %> | <%= pkg.license %> */\n", + sourceMap: true + }, + build: { + files: [ + { + src: "src/sprintf.js", + dest: "dist/sprintf.min.js" + }, + { + src: "src/angular-sprintf.js", + dest: "dist/angular-sprintf.min.js" + } + ] + } + }, + + watch: { + js: { + files: "src/*.js", + tasks: ["uglify"] + } + } + }) + + grunt.loadNpmTasks("grunt-contrib-uglify") + grunt.loadNpmTasks("grunt-contrib-watch") + + grunt.registerTask("default", ["uglify", "watch"]) +} diff --git a/WebServer/node_modules/sprintf-js/package.json b/WebServer/node_modules/sprintf-js/package.json new file mode 100644 index 000000000..5ecd9b8d9 --- /dev/null +++ b/WebServer/node_modules/sprintf-js/package.json @@ -0,0 +1,57 @@ +{ + "_args": [ + [ + "sprintf-js@1.0.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "sprintf-js@1.0.3", + "_id": "sprintf-js@1.0.3", + "_inBundle": false, + "_integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "_location": "/sprintf-js", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "sprintf-js@1.0.3", + "name": "sprintf-js", + "escapedName": "sprintf-js", + "rawSpec": "1.0.3", + "saveSpec": null, + "fetchSpec": "1.0.3" + }, + "_requiredBy": [ + "/argparse" + ], + "_resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "_spec": "1.0.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Alexandru Marasteanu", + "email": "hello@alexei.ro", + "url": "http://alexei.ro/" + }, + "bugs": { + "url": "https://github.com/alexei/sprintf.js/issues" + }, + "description": "JavaScript sprintf implementation", + "devDependencies": { + "grunt": "*", + "grunt-contrib-uglify": "*", + "grunt-contrib-watch": "*", + "mocha": "*" + }, + "homepage": "https://github.com/alexei/sprintf.js#readme", + "license": "BSD-3-Clause", + "main": "src/sprintf.js", + "name": "sprintf-js", + "repository": { + "type": "git", + "url": "git+https://github.com/alexei/sprintf.js.git" + }, + "scripts": { + "test": "mocha test/test.js" + }, + "version": "1.0.3" +} diff --git a/WebServer/node_modules/sprintf-js/src/angular-sprintf.js b/WebServer/node_modules/sprintf-js/src/angular-sprintf.js new file mode 100644 index 000000000..9c69123be --- /dev/null +++ b/WebServer/node_modules/sprintf-js/src/angular-sprintf.js @@ -0,0 +1,18 @@ +angular. + module("sprintf", []). + filter("sprintf", function() { + return function() { + return sprintf.apply(null, arguments) + } + }). + filter("fmt", ["$filter", function($filter) { + return $filter("sprintf") + }]). + filter("vsprintf", function() { + return function(format, argv) { + return vsprintf(format, argv) + } + }). + filter("vfmt", ["$filter", function($filter) { + return $filter("vsprintf") + }]) diff --git a/WebServer/node_modules/sprintf-js/src/sprintf.js b/WebServer/node_modules/sprintf-js/src/sprintf.js new file mode 100644 index 000000000..c0fc7c08b --- /dev/null +++ b/WebServer/node_modules/sprintf-js/src/sprintf.js @@ -0,0 +1,208 @@ +(function(window) { + var re = { + not_string: /[^s]/, + number: /[diefg]/, + json: /[j]/, + not_json: /[^j]/, + text: /^[^\x25]+/, + modulo: /^\x25{2}/, + placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/, + key: /^([a-z_][a-z_\d]*)/i, + key_access: /^\.([a-z_][a-z_\d]*)/i, + index_access: /^\[(\d+)\]/, + sign: /^[\+\-]/ + } + + function sprintf() { + var key = arguments[0], cache = sprintf.cache + if (!(cache[key] && cache.hasOwnProperty(key))) { + cache[key] = sprintf.parse(key) + } + return sprintf.format.call(null, cache[key], arguments) + } + + sprintf.format = function(parse_tree, argv) { + var cursor = 1, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = "" + for (i = 0; i < tree_length; i++) { + node_type = get_type(parse_tree[i]) + if (node_type === "string") { + output[output.length] = parse_tree[i] + } + else if (node_type === "array") { + match = parse_tree[i] // convenience purposes only + if (match[2]) { // keyword argument + arg = argv[cursor] + for (k = 0; k < match[2].length; k++) { + if (!arg.hasOwnProperty(match[2][k])) { + throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k])) + } + arg = arg[match[2][k]] + } + } + else if (match[1]) { // positional argument (explicit) + arg = argv[match[1]] + } + else { // positional argument (implicit) + arg = argv[cursor++] + } + + if (get_type(arg) == "function") { + arg = arg() + } + + if (re.not_string.test(match[8]) && re.not_json.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) { + throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg))) + } + + if (re.number.test(match[8])) { + is_positive = arg >= 0 + } + + switch (match[8]) { + case "b": + arg = arg.toString(2) + break + case "c": + arg = String.fromCharCode(arg) + break + case "d": + case "i": + arg = parseInt(arg, 10) + break + case "j": + arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0) + break + case "e": + arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential() + break + case "f": + arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg) + break + case "g": + arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg) + break + case "o": + arg = arg.toString(8) + break + case "s": + arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg) + break + case "u": + arg = arg >>> 0 + break + case "x": + arg = arg.toString(16) + break + case "X": + arg = arg.toString(16).toUpperCase() + break + } + if (re.json.test(match[8])) { + output[output.length] = arg + } + else { + if (re.number.test(match[8]) && (!is_positive || match[3])) { + sign = is_positive ? "+" : "-" + arg = arg.toString().replace(re.sign, "") + } + else { + sign = "" + } + pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " " + pad_length = match[6] - (sign + arg).length + pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : "") : "" + output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg) + } + } + } + return output.join("") + } + + sprintf.cache = {} + + sprintf.parse = function(fmt) { + var _fmt = fmt, match = [], parse_tree = [], arg_names = 0 + while (_fmt) { + if ((match = re.text.exec(_fmt)) !== null) { + parse_tree[parse_tree.length] = match[0] + } + else if ((match = re.modulo.exec(_fmt)) !== null) { + parse_tree[parse_tree.length] = "%" + } + else if ((match = re.placeholder.exec(_fmt)) !== null) { + if (match[2]) { + arg_names |= 1 + var field_list = [], replacement_field = match[2], field_match = [] + if ((field_match = re.key.exec(replacement_field)) !== null) { + field_list[field_list.length] = field_match[1] + while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") { + if ((field_match = re.key_access.exec(replacement_field)) !== null) { + field_list[field_list.length] = field_match[1] + } + else if ((field_match = re.index_access.exec(replacement_field)) !== null) { + field_list[field_list.length] = field_match[1] + } + else { + throw new SyntaxError("[sprintf] failed to parse named argument key") + } + } + } + else { + throw new SyntaxError("[sprintf] failed to parse named argument key") + } + match[2] = field_list + } + else { + arg_names |= 2 + } + if (arg_names === 3) { + throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported") + } + parse_tree[parse_tree.length] = match + } + else { + throw new SyntaxError("[sprintf] unexpected placeholder") + } + _fmt = _fmt.substring(match[0].length) + } + return parse_tree + } + + var vsprintf = function(fmt, argv, _argv) { + _argv = (argv || []).slice(0) + _argv.splice(0, 0, fmt) + return sprintf.apply(null, _argv) + } + + /** + * helpers + */ + function get_type(variable) { + return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase() + } + + function str_repeat(input, multiplier) { + return Array(multiplier + 1).join(input) + } + + /** + * export to either browser or node.js + */ + if (typeof exports !== "undefined") { + exports.sprintf = sprintf + exports.vsprintf = vsprintf + } + else { + window.sprintf = sprintf + window.vsprintf = vsprintf + + if (typeof define === "function" && define.amd) { + define(function() { + return { + sprintf: sprintf, + vsprintf: vsprintf + } + }) + } + } +})(typeof window === "undefined" ? this : window); diff --git a/WebServer/node_modules/sprintf-js/test/test.js b/WebServer/node_modules/sprintf-js/test/test.js new file mode 100644 index 000000000..6f57b2538 --- /dev/null +++ b/WebServer/node_modules/sprintf-js/test/test.js @@ -0,0 +1,82 @@ +var assert = require("assert"), + sprintfjs = require("../src/sprintf.js"), + sprintf = sprintfjs.sprintf, + vsprintf = sprintfjs.vsprintf + +describe("sprintfjs", function() { + var pi = 3.141592653589793 + + it("should return formated strings for simple placeholders", function() { + assert.equal("%", sprintf("%%")) + assert.equal("10", sprintf("%b", 2)) + assert.equal("A", sprintf("%c", 65)) + assert.equal("2", sprintf("%d", 2)) + assert.equal("2", sprintf("%i", 2)) + assert.equal("2", sprintf("%d", "2")) + assert.equal("2", sprintf("%i", "2")) + assert.equal('{"foo":"bar"}', sprintf("%j", {foo: "bar"})) + assert.equal('["foo","bar"]', sprintf("%j", ["foo", "bar"])) + assert.equal("2e+0", sprintf("%e", 2)) + assert.equal("2", sprintf("%u", 2)) + assert.equal("4294967294", sprintf("%u", -2)) + assert.equal("2.2", sprintf("%f", 2.2)) + assert.equal("3.141592653589793", sprintf("%g", pi)) + assert.equal("10", sprintf("%o", 8)) + assert.equal("%s", sprintf("%s", "%s")) + assert.equal("ff", sprintf("%x", 255)) + assert.equal("FF", sprintf("%X", 255)) + assert.equal("Polly wants a cracker", sprintf("%2$s %3$s a %1$s", "cracker", "Polly", "wants")) + assert.equal("Hello world!", sprintf("Hello %(who)s!", {"who": "world"})) + }) + + it("should return formated strings for complex placeholders", function() { + // sign + assert.equal("2", sprintf("%d", 2)) + assert.equal("-2", sprintf("%d", -2)) + assert.equal("+2", sprintf("%+d", 2)) + assert.equal("-2", sprintf("%+d", -2)) + assert.equal("2", sprintf("%i", 2)) + assert.equal("-2", sprintf("%i", -2)) + assert.equal("+2", sprintf("%+i", 2)) + assert.equal("-2", sprintf("%+i", -2)) + assert.equal("2.2", sprintf("%f", 2.2)) + assert.equal("-2.2", sprintf("%f", -2.2)) + assert.equal("+2.2", sprintf("%+f", 2.2)) + assert.equal("-2.2", sprintf("%+f", -2.2)) + assert.equal("-2.3", sprintf("%+.1f", -2.34)) + assert.equal("-0.0", sprintf("%+.1f", -0.01)) + assert.equal("3.14159", sprintf("%.6g", pi)) + assert.equal("3.14", sprintf("%.3g", pi)) + assert.equal("3", sprintf("%.1g", pi)) + assert.equal("-000000123", sprintf("%+010d", -123)) + assert.equal("______-123", sprintf("%+'_10d", -123)) + assert.equal("-234.34 123.2", sprintf("%f %f", -234.34, 123.2)) + + // padding + assert.equal("-0002", sprintf("%05d", -2)) + assert.equal("-0002", sprintf("%05i", -2)) + assert.equal(" <", sprintf("%5s", "<")) + assert.equal("0000<", sprintf("%05s", "<")) + assert.equal("____<", sprintf("%'_5s", "<")) + assert.equal("> ", sprintf("%-5s", ">")) + assert.equal(">0000", sprintf("%0-5s", ">")) + assert.equal(">____", sprintf("%'_-5s", ">")) + assert.equal("xxxxxx", sprintf("%5s", "xxxxxx")) + assert.equal("1234", sprintf("%02u", 1234)) + assert.equal(" -10.235", sprintf("%8.3f", -10.23456)) + assert.equal("-12.34 xxx", sprintf("%f %s", -12.34, "xxx")) + assert.equal('{\n "foo": "bar"\n}', sprintf("%2j", {foo: "bar"})) + assert.equal('[\n "foo",\n "bar"\n]', sprintf("%2j", ["foo", "bar"])) + + // precision + assert.equal("2.3", sprintf("%.1f", 2.345)) + assert.equal("xxxxx", sprintf("%5.5s", "xxxxxx")) + assert.equal(" x", sprintf("%5.1s", "xxxxxx")) + + }) + + it("should return formated strings for callbacks", function() { + assert.equal("foobar", sprintf("%s", function() { return "foobar" })) + assert.equal(Date.now(), sprintf("%s", Date.now)) // should pass... + }) +}) diff --git a/WebServer/node_modules/string-width/index.js b/WebServer/node_modules/string-width/index.js new file mode 100644 index 000000000..bbc49d29b --- /dev/null +++ b/WebServer/node_modules/string-width/index.js @@ -0,0 +1,36 @@ +'use strict'; +const stripAnsi = require('strip-ansi'); +const isFullwidthCodePoint = require('is-fullwidth-code-point'); + +module.exports = str => { + if (typeof str !== 'string' || str.length === 0) { + return 0; + } + + str = stripAnsi(str); + + let width = 0; + + for (let i = 0; i < str.length; i++) { + const code = str.codePointAt(i); + + // Ignore control characters + if (code <= 0x1F || (code >= 0x7F && code <= 0x9F)) { + continue; + } + + // Ignore combining characters + if (code >= 0x300 && code <= 0x36F) { + continue; + } + + // Surrogates + if (code > 0xFFFF) { + i++; + } + + width += isFullwidthCodePoint(code) ? 2 : 1; + } + + return width; +}; diff --git a/WebServer/node_modules/string-width/package.json b/WebServer/node_modules/string-width/package.json new file mode 100644 index 000000000..a95d895cc --- /dev/null +++ b/WebServer/node_modules/string-width/package.json @@ -0,0 +1,92 @@ +{ + "_args": [ + [ + "string-width@2.1.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "string-width@2.1.1", + "_id": "string-width@2.1.1", + "_inBundle": false, + "_integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "_location": "/string-width", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "string-width@2.1.1", + "name": "string-width", + "escapedName": "string-width", + "rawSpec": "2.1.1", + "saveSpec": null, + "fetchSpec": "2.1.1" + }, + "_requiredBy": [ + "/cliui", + "/wide-align", + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "_spec": "2.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/string-width/issues" + }, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "description": "Get the visual width of a string - the number of columns required to display it", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/string-width#readme", + "keywords": [ + "string", + "str", + "character", + "char", + "unicode", + "width", + "visual", + "column", + "columns", + "fullwidth", + "full-width", + "full", + "ansi", + "escape", + "codes", + "cli", + "command-line", + "terminal", + "console", + "cjk", + "chinese", + "japanese", + "korean", + "fixed-width" + ], + "license": "MIT", + "name": "string-width", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/string-width.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "2.1.1" +} diff --git a/WebServer/node_modules/strip-ansi/index.js b/WebServer/node_modules/strip-ansi/index.js new file mode 100644 index 000000000..96e0292c8 --- /dev/null +++ b/WebServer/node_modules/strip-ansi/index.js @@ -0,0 +1,4 @@ +'use strict'; +const ansiRegex = require('ansi-regex'); + +module.exports = input => typeof input === 'string' ? input.replace(ansiRegex(), '') : input; diff --git a/WebServer/node_modules/strip-ansi/package.json b/WebServer/node_modules/strip-ansi/package.json new file mode 100644 index 000000000..077860349 --- /dev/null +++ b/WebServer/node_modules/strip-ansi/package.json @@ -0,0 +1,88 @@ +{ + "_args": [ + [ + "strip-ansi@4.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "strip-ansi@4.0.0", + "_id": "strip-ansi@4.0.0", + "_inBundle": false, + "_integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "_location": "/strip-ansi", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "strip-ansi@4.0.0", + "name": "strip-ansi", + "escapedName": "strip-ansi", + "rawSpec": "4.0.0", + "saveSpec": null, + "fetchSpec": "4.0.0" + }, + "_requiredBy": [ + "/cliui", + "/string-width" + ], + "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "_spec": "4.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "description": "Strip ANSI escape codes", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/strip-ansi#readme", + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "strip-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "4.0.0" +} diff --git a/WebServer/node_modules/strip-eof/index.js b/WebServer/node_modules/strip-eof/index.js new file mode 100644 index 000000000..a17d0afd3 --- /dev/null +++ b/WebServer/node_modules/strip-eof/index.js @@ -0,0 +1,15 @@ +'use strict'; +module.exports = function (x) { + var lf = typeof x === 'string' ? '\n' : '\n'.charCodeAt(); + var cr = typeof x === 'string' ? '\r' : '\r'.charCodeAt(); + + if (x[x.length - 1] === lf) { + x = x.slice(0, x.length - 1); + } + + if (x[x.length - 1] === cr) { + x = x.slice(0, x.length - 1); + } + + return x; +}; diff --git a/WebServer/node_modules/strip-eof/package.json b/WebServer/node_modules/strip-eof/package.json new file mode 100644 index 000000000..b4c7909f1 --- /dev/null +++ b/WebServer/node_modules/strip-eof/package.json @@ -0,0 +1,74 @@ +{ + "_args": [ + [ + "strip-eof@1.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "strip-eof@1.0.0", + "_id": "strip-eof@1.0.0", + "_inBundle": false, + "_integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "_location": "/strip-eof", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "strip-eof@1.0.0", + "name": "strip-eof", + "escapedName": "strip-eof", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/execa" + ], + "_resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "_spec": "1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/strip-eof/issues" + }, + "description": "Strip the End-Of-File (EOF) character from a string/buffer", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/strip-eof#readme", + "keywords": [ + "strip", + "trim", + "remove", + "delete", + "eof", + "end", + "file", + "newline", + "linebreak", + "character", + "string", + "buffer" + ], + "license": "MIT", + "name": "strip-eof", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/strip-eof.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.0.0" +} diff --git a/WebServer/node_modules/strip-json-comments/index.js b/WebServer/node_modules/strip-json-comments/index.js new file mode 100644 index 000000000..4e6576e6d --- /dev/null +++ b/WebServer/node_modules/strip-json-comments/index.js @@ -0,0 +1,70 @@ +'use strict'; +var singleComment = 1; +var multiComment = 2; + +function stripWithoutWhitespace() { + return ''; +} + +function stripWithWhitespace(str, start, end) { + return str.slice(start, end).replace(/\S/g, ' '); +} + +module.exports = function (str, opts) { + opts = opts || {}; + + var currentChar; + var nextChar; + var insideString = false; + var insideComment = false; + var offset = 0; + var ret = ''; + var strip = opts.whitespace === false ? stripWithoutWhitespace : stripWithWhitespace; + + for (var i = 0; i < str.length; i++) { + currentChar = str[i]; + nextChar = str[i + 1]; + + if (!insideComment && currentChar === '"') { + var escaped = str[i - 1] === '\\' && str[i - 2] !== '\\'; + if (!escaped) { + insideString = !insideString; + } + } + + if (insideString) { + continue; + } + + if (!insideComment && currentChar + nextChar === '//') { + ret += str.slice(offset, i); + offset = i; + insideComment = singleComment; + i++; + } else if (insideComment === singleComment && currentChar + nextChar === '\r\n') { + i++; + insideComment = false; + ret += strip(str, offset, i); + offset = i; + continue; + } else if (insideComment === singleComment && currentChar === '\n') { + insideComment = false; + ret += strip(str, offset, i); + offset = i; + } else if (!insideComment && currentChar + nextChar === '/*') { + ret += str.slice(offset, i); + offset = i; + insideComment = multiComment; + i++; + continue; + } else if (insideComment === multiComment && currentChar + nextChar === '*/') { + i++; + insideComment = false; + ret += strip(str, offset, i + 1); + offset = i + 1; + continue; + } + } + + return ret + (insideComment ? strip(str.substr(offset)) : str.substr(offset)); +}; diff --git a/WebServer/node_modules/strip-json-comments/package.json b/WebServer/node_modules/strip-json-comments/package.json new file mode 100644 index 000000000..976f845c9 --- /dev/null +++ b/WebServer/node_modules/strip-json-comments/package.json @@ -0,0 +1,77 @@ +{ + "_args": [ + [ + "strip-json-comments@2.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "strip-json-comments@2.0.1", + "_id": "strip-json-comments@2.0.1", + "_inBundle": false, + "_integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "_location": "/strip-json-comments", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "strip-json-comments@2.0.1", + "name": "strip-json-comments", + "escapedName": "strip-json-comments", + "rawSpec": "2.0.1", + "saveSpec": null, + "fetchSpec": "2.0.1" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "_spec": "2.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/strip-json-comments/issues" + }, + "description": "Strip comments from JSON. Lets you use comments in your JSON files!", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/strip-json-comments#readme", + "keywords": [ + "json", + "strip", + "remove", + "delete", + "trim", + "comments", + "multiline", + "parse", + "config", + "configuration", + "conf", + "settings", + "util", + "env", + "environment" + ], + "license": "MIT", + "name": "strip-json-comments", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/strip-json-comments.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "2.0.1" +} diff --git a/WebServer/node_modules/supports-color/browser.js b/WebServer/node_modules/supports-color/browser.js new file mode 100644 index 000000000..62afa3a74 --- /dev/null +++ b/WebServer/node_modules/supports-color/browser.js @@ -0,0 +1,5 @@ +'use strict'; +module.exports = { + stdout: false, + stderr: false +}; diff --git a/WebServer/node_modules/supports-color/index.js b/WebServer/node_modules/supports-color/index.js new file mode 100644 index 000000000..6aacf5b49 --- /dev/null +++ b/WebServer/node_modules/supports-color/index.js @@ -0,0 +1,137 @@ +'use strict'; +const os = require('os'); +const hasFlag = require('has-flag'); + +const {env} = process; + +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false')) { + forceColor = 0; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = 1; +} +if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === true || env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === false || env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } +} + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} + +function supportsColor(stream) { + if (forceColor === 0) { + return 0; + } + + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + + if (stream && !stream.isTTY && forceColor === undefined) { + return 0; + } + + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } + + if (process.platform === 'win32') { + // Node.js 7.5.0 is the first version of Node.js to include a patch to + // libuv that enables 256 color output on Windows. Anything earlier and it + // won't work. However, here we target Node.js 8 at minimum as it is an LTS + // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows + // release that supports 256 colors. Windows 10 build 14931 is the first release + // that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(process.versions.node.split('.')[0]) >= 8 && + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + return min; +} + +function getSupportLevel(stream) { + const level = supportsColor(stream); + return translateLevel(level); +} + +module.exports = { + supportsColor: getSupportLevel, + stdout: getSupportLevel(process.stdout), + stderr: getSupportLevel(process.stderr) +}; diff --git a/WebServer/node_modules/supports-color/package.json b/WebServer/node_modules/supports-color/package.json new file mode 100644 index 000000000..dde44f4b0 --- /dev/null +++ b/WebServer/node_modules/supports-color/package.json @@ -0,0 +1,88 @@ +{ + "_args": [ + [ + "supports-color@6.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "supports-color@6.0.0", + "_id": "supports-color@6.0.0", + "_inBundle": false, + "_integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "_location": "/supports-color", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "supports-color@6.0.0", + "name": "supports-color", + "escapedName": "supports-color", + "rawSpec": "6.0.0", + "saveSpec": null, + "fetchSpec": "6.0.0" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "_spec": "6.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "browser": "browser.js", + "bugs": { + "url": "https://github.com/chalk/supports-color/issues" + }, + "dependencies": { + "has-flag": "^3.0.0" + }, + "description": "Detect whether a terminal supports color", + "devDependencies": { + "ava": "^0.25.0", + "import-fresh": "^2.0.0", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "browser.js" + ], + "homepage": "https://github.com/chalk/supports-color#readme", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "ansi", + "styles", + "tty", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "support", + "supports", + "capability", + "detect", + "truecolor", + "16m" + ], + "license": "MIT", + "name": "supports-color", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/supports-color.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "6.0.0" +} diff --git a/WebServer/node_modules/type-detect/index.js b/WebServer/node_modules/type-detect/index.js new file mode 100644 index 000000000..98a1e031c --- /dev/null +++ b/WebServer/node_modules/type-detect/index.js @@ -0,0 +1,378 @@ +/* ! + * type-detect + * Copyright(c) 2013 jake luer + * MIT Licensed + */ +const promiseExists = typeof Promise === 'function'; + +/* eslint-disable no-undef */ +const globalObject = typeof self === 'object' ? self : global; // eslint-disable-line id-blacklist + +const symbolExists = typeof Symbol !== 'undefined'; +const mapExists = typeof Map !== 'undefined'; +const setExists = typeof Set !== 'undefined'; +const weakMapExists = typeof WeakMap !== 'undefined'; +const weakSetExists = typeof WeakSet !== 'undefined'; +const dataViewExists = typeof DataView !== 'undefined'; +const symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; +const symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; +const setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; +const mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; +const setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries()); +const mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries()); +const arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +const arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); +const stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function'; +const stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); +const toStringLeftSliceLength = 8; +const toStringRightSliceLength = -1; +/** + * ### typeOf (obj) + * + * Uses `Object.prototype.toString` to determine the type of an object, + * normalising behaviour across engine versions & well optimised. + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ +export default function typeDetect(obj) { + /* ! Speed optimisation + * Pre: + * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) + * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) + * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) + * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) + * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) + * Post: + * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) + * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) + * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) + * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) + * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) + */ + const typeofObj = typeof obj; + if (typeofObj !== 'object') { + return typeofObj; + } + + /* ! Speed optimisation + * Pre: + * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) + * Post: + * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) + */ + if (obj === null) { + return 'null'; + } + + /* ! Spec Conformance + * Test: `Object.prototype.toString.call(window)`` + * - Node === "[object global]" + * - Chrome === "[object global]" + * - Firefox === "[object Window]" + * - PhantomJS === "[object Window]" + * - Safari === "[object Window]" + * - IE 11 === "[object Window]" + * - IE Edge === "[object Window]" + * Test: `Object.prototype.toString.call(this)`` + * - Chrome Worker === "[object global]" + * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" + * - Safari Worker === "[object DedicatedWorkerGlobalScope]" + * - IE 11 Worker === "[object WorkerGlobalScope]" + * - IE Edge Worker === "[object WorkerGlobalScope]" + */ + if (obj === globalObject) { + return 'global'; + } + + /* ! Speed optimisation + * Pre: + * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) + * Post: + * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) + */ + if ( + Array.isArray(obj) && + (symbolToStringTagExists === false || !(Symbol.toStringTag in obj)) + ) { + return 'Array'; + } + + // Not caching existence of `window` and related properties due to potential + // for `window` to be unset before tests in quasi-browser environments. + if (typeof window === 'object' && window !== null) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/browsers.html#location) + * WhatWG HTML$7.7.3 - The `Location` interface + * Test: `Object.prototype.toString.call(window.location)`` + * - IE <=11 === "[object Object]" + * - IE Edge <=13 === "[object Object]" + */ + if (typeof window.location === 'object' && obj === window.location) { + return 'Location'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#document) + * WhatWG HTML$3.1.1 - The `Document` object + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * WhatWG HTML states: + * > For historical reasons, Window objects must also have a + * > writable, configurable, non-enumerable property named + * > HTMLDocument whose value is the Document interface object. + * Test: `Object.prototype.toString.call(document)`` + * - Chrome === "[object HTMLDocument]" + * - Firefox === "[object HTMLDocument]" + * - Safari === "[object HTMLDocument]" + * - IE <=10 === "[object Document]" + * - IE 11 === "[object HTMLDocument]" + * - IE Edge <=13 === "[object HTMLDocument]" + */ + if (typeof window.document === 'object' && obj === window.document) { + return 'Document'; + } + + if (typeof window.navigator === 'object') { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray + * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` + * - IE <=10 === "[object MSMimeTypesCollection]" + */ + if (typeof window.navigator.mimeTypes === 'object' && + obj === window.navigator.mimeTypes) { + return 'MimeTypeArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray + * Test: `Object.prototype.toString.call(navigator.plugins)`` + * - IE <=10 === "[object MSPluginsCollection]" + */ + if (typeof window.navigator.plugins === 'object' && + obj === window.navigator.plugins) { + return 'PluginArray'; + } + } + + if ((typeof window.HTMLElement === 'function' || + typeof window.HTMLElement === 'object') && + obj instanceof window.HTMLElement) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` + * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` + * - IE <=10 === "[object HTMLBlockElement]" + */ + if (obj.tagName === 'BLOCKQUOTE') { + return 'HTMLQuoteElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltabledatacellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('td')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TD') { + return 'HTMLTableDataCellElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltableheadercellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('th')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TH') { + return 'HTMLTableHeaderCellElement'; + } + } + } + + /* ! Speed optimisation + * Pre: + * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) + * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) + * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) + * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) + * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) + * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) + * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) + * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) + * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) + * Post: + * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) + * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) + * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) + * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) + * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) + * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) + * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) + * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) + * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) + */ + const stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); + if (typeof stringTag === 'string') { + return stringTag; + } + + const objPrototype = Object.getPrototypeOf(obj); + /* ! Speed optimisation + * Pre: + * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) + * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) + * Post: + * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) + * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) + */ + if (objPrototype === RegExp.prototype) { + return 'RegExp'; + } + + /* ! Speed optimisation + * Pre: + * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) + * Post: + * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) + */ + if (objPrototype === Date.prototype) { + return 'Date'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) + * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": + * Test: `Object.prototype.toString.call(Promise.resolve())`` + * - Chrome <=47 === "[object Object]" + * - Edge <=20 === "[object Object]" + * - Firefox 29-Latest === "[object Promise]" + * - Safari 7.1-Latest === "[object Promise]" + */ + if (promiseExists && objPrototype === Promise.prototype) { + return 'Promise'; + } + + /* ! Speed optimisation + * Pre: + * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) + * Post: + * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) + */ + if (setExists && objPrototype === Set.prototype) { + return 'Set'; + } + + /* ! Speed optimisation + * Pre: + * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) + * Post: + * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) + */ + if (mapExists && objPrototype === Map.prototype) { + return 'Map'; + } + + /* ! Speed optimisation + * Pre: + * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) + * Post: + * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) + */ + if (weakSetExists && objPrototype === WeakSet.prototype) { + return 'WeakSet'; + } + + /* ! Speed optimisation + * Pre: + * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) + * Post: + * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) + */ + if (weakMapExists && objPrototype === WeakMap.prototype) { + return 'WeakMap'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) + * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": + * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` + * - Edge <=13 === "[object Object]" + */ + if (dataViewExists && objPrototype === DataView.prototype) { + return 'DataView'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) + * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": + * Test: `Object.prototype.toString.call(new Map().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (mapExists && objPrototype === mapIteratorPrototype) { + return 'Map Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) + * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": + * Test: `Object.prototype.toString.call(new Set().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (setExists && objPrototype === setIteratorPrototype) { + return 'Set Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) + * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": + * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { + return 'Array Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) + * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": + * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (stringIteratorExists && objPrototype === stringIteratorPrototype) { + return 'String Iterator'; + } + + /* ! Speed optimisation + * Pre: + * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) + * Post: + * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) + */ + if (objPrototype === null) { + return 'Object'; + } + + return Object + .prototype + .toString + .call(obj) + .slice(toStringLeftSliceLength, toStringRightSliceLength); +} diff --git a/WebServer/node_modules/type-detect/package.json b/WebServer/node_modules/type-detect/package.json new file mode 100644 index 000000000..1b2962879 --- /dev/null +++ b/WebServer/node_modules/type-detect/package.json @@ -0,0 +1,172 @@ +{ + "_args": [ + [ + "type-detect@4.0.8", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "type-detect@4.0.8", + "_id": "type-detect@4.0.8", + "_inBundle": false, + "_integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "_location": "/type-detect", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "type-detect@4.0.8", + "name": "type-detect", + "escapedName": "type-detect", + "rawSpec": "4.0.8", + "saveSpec": null, + "fetchSpec": "4.0.8" + }, + "_requiredBy": [ + "/chai", + "/deep-eql" + ], + "_resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "_spec": "4.0.8", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Jake Luer", + "email": "jake@alogicalparadox.com", + "url": "http://alogicalparadox.com" + }, + "bugs": { + "url": "https://github.com/chaijs/type-detect/issues" + }, + "contributors": [ + { + "name": "Keith Cirkel", + "url": "https://github.com/keithamus" + }, + { + "name": "David Losert", + "url": "https://github.com/davelosert" + }, + { + "name": "Aleksey Shvayka", + "url": "https://github.com/shvaikalesh" + }, + { + "name": "Lucas Fernandes da Costa", + "url": "https://github.com/lucasfcosta" + }, + { + "name": "Grant Snodgrass", + "url": "https://github.com/meeber" + }, + { + "name": "Jeremy Tice", + "url": "https://github.com/jetpacmonkey" + }, + { + "name": "Edward Betts", + "url": "https://github.com/EdwardBetts" + }, + { + "name": "dvlsg", + "url": "https://github.com/dvlsg" + }, + { + "name": "Amila Welihinda", + "url": "https://github.com/amilajack" + }, + { + "name": "Jake Champion", + "url": "https://github.com/JakeChampion" + }, + { + "name": "Miroslav Bajtoš", + "url": "https://github.com/bajtos" + } + ], + "description": "Improved typeof detection for node.js and the browser.", + "devDependencies": { + "@commitlint/cli": "^4.2.2", + "benchmark": "^2.1.0", + "buble": "^0.16.0", + "codecov": "^3.0.0", + "commitlint-config-angular": "^4.2.1", + "cross-env": "^5.1.1", + "eslint": "^4.10.0", + "eslint-config-strict": "^14.0.0", + "eslint-plugin-filenames": "^1.2.0", + "husky": "^0.14.3", + "karma": "^1.7.1", + "karma-chrome-launcher": "^2.2.0", + "karma-coverage": "^1.1.1", + "karma-detect-browsers": "^2.2.5", + "karma-edge-launcher": "^0.4.2", + "karma-firefox-launcher": "^1.0.1", + "karma-ie-launcher": "^1.0.0", + "karma-mocha": "^1.3.0", + "karma-opera-launcher": "^1.0.0", + "karma-safari-launcher": "^1.0.0", + "karma-safaritechpreview-launcher": "0.0.6", + "karma-sauce-launcher": "^1.2.0", + "mocha": "^4.0.1", + "nyc": "^11.3.0", + "rollup": "^0.50.0", + "rollup-plugin-buble": "^0.16.0", + "rollup-plugin-commonjs": "^8.2.6", + "rollup-plugin-istanbul": "^1.1.0", + "rollup-plugin-node-resolve": "^3.0.0", + "semantic-release": "^8.2.0", + "simple-assert": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "eslintConfig": { + "env": { + "es6": true + }, + "extends": [ + "strict/es6" + ], + "globals": { + "HTMLElement": false + }, + "rules": { + "complexity": 0, + "max-statements": 0, + "prefer-rest-params": 0 + } + }, + "files": [ + "index.js", + "type-detect.js" + ], + "homepage": "https://github.com/chaijs/type-detect#readme", + "keywords": [ + "type", + "typeof", + "types" + ], + "license": "MIT", + "main": "./type-detect.js", + "name": "type-detect", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/chaijs/type-detect.git" + }, + "scripts": { + "bench": "node bench", + "build": "rollup -c rollup.conf.js", + "commit-msg": "commitlint -x angular", + "lint": "eslint --ignore-path .gitignore .", + "posttest:browser": "npm run upload-coverage", + "posttest:node": "nyc report --report-dir \"coverage/node-$(node --version)\" --reporter=lcovonly && npm run upload-coverage", + "prepare": "cross-env NODE_ENV=production npm run build", + "pretest:browser": "cross-env NODE_ENV=test npm run build", + "pretest:node": "cross-env NODE_ENV=test npm run build", + "semantic-release": "semantic-release pre && npm publish && semantic-release post", + "test": "npm run test:node && npm run test:browser", + "test:browser": "karma start --singleRun=true", + "test:node": "nyc mocha type-detect.test.js", + "upload-coverage": "codecov" + }, + "version": "4.0.8" +} diff --git a/WebServer/node_modules/type-detect/type-detect.js b/WebServer/node_modules/type-detect/type-detect.js new file mode 100644 index 000000000..2f5552525 --- /dev/null +++ b/WebServer/node_modules/type-detect/type-detect.js @@ -0,0 +1,388 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.typeDetect = factory()); +}(this, (function () { 'use strict'; + +/* ! + * type-detect + * Copyright(c) 2013 jake luer + * MIT Licensed + */ +var promiseExists = typeof Promise === 'function'; + +/* eslint-disable no-undef */ +var globalObject = typeof self === 'object' ? self : global; // eslint-disable-line id-blacklist + +var symbolExists = typeof Symbol !== 'undefined'; +var mapExists = typeof Map !== 'undefined'; +var setExists = typeof Set !== 'undefined'; +var weakMapExists = typeof WeakMap !== 'undefined'; +var weakSetExists = typeof WeakSet !== 'undefined'; +var dataViewExists = typeof DataView !== 'undefined'; +var symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; +var symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; +var setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; +var mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; +var setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries()); +var mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries()); +var arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +var arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); +var stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function'; +var stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); +var toStringLeftSliceLength = 8; +var toStringRightSliceLength = -1; +/** + * ### typeOf (obj) + * + * Uses `Object.prototype.toString` to determine the type of an object, + * normalising behaviour across engine versions & well optimised. + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ +function typeDetect(obj) { + /* ! Speed optimisation + * Pre: + * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) + * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) + * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) + * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) + * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) + * Post: + * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) + * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) + * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) + * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) + * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) + */ + var typeofObj = typeof obj; + if (typeofObj !== 'object') { + return typeofObj; + } + + /* ! Speed optimisation + * Pre: + * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) + * Post: + * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) + */ + if (obj === null) { + return 'null'; + } + + /* ! Spec Conformance + * Test: `Object.prototype.toString.call(window)`` + * - Node === "[object global]" + * - Chrome === "[object global]" + * - Firefox === "[object Window]" + * - PhantomJS === "[object Window]" + * - Safari === "[object Window]" + * - IE 11 === "[object Window]" + * - IE Edge === "[object Window]" + * Test: `Object.prototype.toString.call(this)`` + * - Chrome Worker === "[object global]" + * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" + * - Safari Worker === "[object DedicatedWorkerGlobalScope]" + * - IE 11 Worker === "[object WorkerGlobalScope]" + * - IE Edge Worker === "[object WorkerGlobalScope]" + */ + if (obj === globalObject) { + return 'global'; + } + + /* ! Speed optimisation + * Pre: + * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) + * Post: + * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) + */ + if ( + Array.isArray(obj) && + (symbolToStringTagExists === false || !(Symbol.toStringTag in obj)) + ) { + return 'Array'; + } + + // Not caching existence of `window` and related properties due to potential + // for `window` to be unset before tests in quasi-browser environments. + if (typeof window === 'object' && window !== null) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/browsers.html#location) + * WhatWG HTML$7.7.3 - The `Location` interface + * Test: `Object.prototype.toString.call(window.location)`` + * - IE <=11 === "[object Object]" + * - IE Edge <=13 === "[object Object]" + */ + if (typeof window.location === 'object' && obj === window.location) { + return 'Location'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#document) + * WhatWG HTML$3.1.1 - The `Document` object + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * WhatWG HTML states: + * > For historical reasons, Window objects must also have a + * > writable, configurable, non-enumerable property named + * > HTMLDocument whose value is the Document interface object. + * Test: `Object.prototype.toString.call(document)`` + * - Chrome === "[object HTMLDocument]" + * - Firefox === "[object HTMLDocument]" + * - Safari === "[object HTMLDocument]" + * - IE <=10 === "[object Document]" + * - IE 11 === "[object HTMLDocument]" + * - IE Edge <=13 === "[object HTMLDocument]" + */ + if (typeof window.document === 'object' && obj === window.document) { + return 'Document'; + } + + if (typeof window.navigator === 'object') { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray + * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` + * - IE <=10 === "[object MSMimeTypesCollection]" + */ + if (typeof window.navigator.mimeTypes === 'object' && + obj === window.navigator.mimeTypes) { + return 'MimeTypeArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray + * Test: `Object.prototype.toString.call(navigator.plugins)`` + * - IE <=10 === "[object MSPluginsCollection]" + */ + if (typeof window.navigator.plugins === 'object' && + obj === window.navigator.plugins) { + return 'PluginArray'; + } + } + + if ((typeof window.HTMLElement === 'function' || + typeof window.HTMLElement === 'object') && + obj instanceof window.HTMLElement) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` + * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` + * - IE <=10 === "[object HTMLBlockElement]" + */ + if (obj.tagName === 'BLOCKQUOTE') { + return 'HTMLQuoteElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltabledatacellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('td')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TD') { + return 'HTMLTableDataCellElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltableheadercellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('th')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TH') { + return 'HTMLTableHeaderCellElement'; + } + } + } + + /* ! Speed optimisation + * Pre: + * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) + * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) + * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) + * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) + * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) + * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) + * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) + * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) + * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) + * Post: + * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) + * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) + * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) + * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) + * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) + * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) + * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) + * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) + * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) + */ + var stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); + if (typeof stringTag === 'string') { + return stringTag; + } + + var objPrototype = Object.getPrototypeOf(obj); + /* ! Speed optimisation + * Pre: + * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) + * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) + * Post: + * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) + * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) + */ + if (objPrototype === RegExp.prototype) { + return 'RegExp'; + } + + /* ! Speed optimisation + * Pre: + * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) + * Post: + * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) + */ + if (objPrototype === Date.prototype) { + return 'Date'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) + * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": + * Test: `Object.prototype.toString.call(Promise.resolve())`` + * - Chrome <=47 === "[object Object]" + * - Edge <=20 === "[object Object]" + * - Firefox 29-Latest === "[object Promise]" + * - Safari 7.1-Latest === "[object Promise]" + */ + if (promiseExists && objPrototype === Promise.prototype) { + return 'Promise'; + } + + /* ! Speed optimisation + * Pre: + * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) + * Post: + * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) + */ + if (setExists && objPrototype === Set.prototype) { + return 'Set'; + } + + /* ! Speed optimisation + * Pre: + * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) + * Post: + * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) + */ + if (mapExists && objPrototype === Map.prototype) { + return 'Map'; + } + + /* ! Speed optimisation + * Pre: + * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) + * Post: + * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) + */ + if (weakSetExists && objPrototype === WeakSet.prototype) { + return 'WeakSet'; + } + + /* ! Speed optimisation + * Pre: + * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) + * Post: + * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) + */ + if (weakMapExists && objPrototype === WeakMap.prototype) { + return 'WeakMap'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) + * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": + * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` + * - Edge <=13 === "[object Object]" + */ + if (dataViewExists && objPrototype === DataView.prototype) { + return 'DataView'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) + * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": + * Test: `Object.prototype.toString.call(new Map().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (mapExists && objPrototype === mapIteratorPrototype) { + return 'Map Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) + * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": + * Test: `Object.prototype.toString.call(new Set().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (setExists && objPrototype === setIteratorPrototype) { + return 'Set Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) + * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": + * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { + return 'Array Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) + * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": + * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (stringIteratorExists && objPrototype === stringIteratorPrototype) { + return 'String Iterator'; + } + + /* ! Speed optimisation + * Pre: + * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) + * Post: + * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) + */ + if (objPrototype === null) { + return 'Object'; + } + + return Object + .prototype + .toString + .call(obj) + .slice(toStringLeftSliceLength, toStringRightSliceLength); +} + +return typeDetect; + +}))); diff --git a/WebServer/node_modules/which-module/index.js b/WebServer/node_modules/which-module/index.js new file mode 100644 index 000000000..45559b781 --- /dev/null +++ b/WebServer/node_modules/which-module/index.js @@ -0,0 +1,9 @@ +'use strict' + +module.exports = function whichModule (exported) { + for (var i = 0, files = Object.keys(require.cache), mod; i < files.length; i++) { + mod = require.cache[files[i]] + if (mod.exports === exported) return mod + } + return null +} diff --git a/WebServer/node_modules/which-module/package.json b/WebServer/node_modules/which-module/package.json new file mode 100644 index 000000000..46de2ee15 --- /dev/null +++ b/WebServer/node_modules/which-module/package.json @@ -0,0 +1,72 @@ +{ + "_args": [ + [ + "which-module@2.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "which-module@2.0.0", + "_id": "which-module@2.0.0", + "_inBundle": false, + "_integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "_location": "/which-module", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "which-module@2.0.0", + "name": "which-module", + "escapedName": "which-module", + "rawSpec": "2.0.0", + "saveSpec": null, + "fetchSpec": "2.0.0" + }, + "_requiredBy": [ + "/yargs", + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "_spec": "2.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "nexdrew" + }, + "bugs": { + "url": "https://github.com/nexdrew/which-module/issues" + }, + "description": "Find the module object for something that was require()d", + "devDependencies": { + "ava": "^0.19.1", + "coveralls": "^2.13.1", + "nyc": "^10.3.0", + "standard": "^10.0.2", + "standard-version": "^4.0.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/nexdrew/which-module#readme", + "keywords": [ + "which", + "module", + "exports", + "filename", + "require", + "reverse", + "lookup" + ], + "license": "ISC", + "main": "index.js", + "name": "which-module", + "repository": { + "type": "git", + "url": "git+https://github.com/nexdrew/which-module.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "pretest": "standard", + "release": "standard-version", + "test": "nyc ava" + }, + "version": "2.0.0" +} diff --git a/WebServer/node_modules/which/package.json b/WebServer/node_modules/which/package.json new file mode 100644 index 000000000..e18059b96 --- /dev/null +++ b/WebServer/node_modules/which/package.json @@ -0,0 +1,69 @@ +{ + "_args": [ + [ + "which@1.3.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "which@1.3.1", + "_id": "which@1.3.1", + "_inBundle": false, + "_integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "_location": "/which", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "which@1.3.1", + "name": "which", + "escapedName": "which", + "rawSpec": "1.3.1", + "saveSpec": null, + "fetchSpec": "1.3.1" + }, + "_requiredBy": [ + "/cross-spawn", + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "_spec": "1.3.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me" + }, + "bin": { + "which": "./bin/which" + }, + "bugs": { + "url": "https://github.com/isaacs/node-which/issues" + }, + "dependencies": { + "isexe": "^2.0.0" + }, + "description": "Like which(1) unix command. Find the first instance of an executable in the PATH.", + "devDependencies": { + "mkdirp": "^0.5.0", + "rimraf": "^2.6.2", + "tap": "^12.0.1" + }, + "files": [ + "which.js", + "bin/which" + ], + "homepage": "https://github.com/isaacs/node-which#readme", + "license": "ISC", + "main": "which.js", + "name": "which", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/node-which.git" + }, + "scripts": { + "changelog": "bash gen-changelog.sh", + "postversion": "npm run changelog && git add CHANGELOG.md && git commit -m 'update changelog - '${npm_package_version}", + "test": "tap test/*.js --cov" + }, + "version": "1.3.1" +} diff --git a/WebServer/node_modules/which/which.js b/WebServer/node_modules/which/which.js new file mode 100644 index 000000000..4347f91a1 --- /dev/null +++ b/WebServer/node_modules/which/which.js @@ -0,0 +1,135 @@ +module.exports = which +which.sync = whichSync + +var isWindows = process.platform === 'win32' || + process.env.OSTYPE === 'cygwin' || + process.env.OSTYPE === 'msys' + +var path = require('path') +var COLON = isWindows ? ';' : ':' +var isexe = require('isexe') + +function getNotFoundError (cmd) { + var er = new Error('not found: ' + cmd) + er.code = 'ENOENT' + + return er +} + +function getPathInfo (cmd, opt) { + var colon = opt.colon || COLON + var pathEnv = opt.path || process.env.PATH || '' + var pathExt = [''] + + pathEnv = pathEnv.split(colon) + + var pathExtExe = '' + if (isWindows) { + pathEnv.unshift(process.cwd()) + pathExtExe = (opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM') + pathExt = pathExtExe.split(colon) + + + // Always test the cmd itself first. isexe will check to make sure + // it's found in the pathExt set. + if (cmd.indexOf('.') !== -1 && pathExt[0] !== '') + pathExt.unshift('') + } + + // If it has a slash, then we don't bother searching the pathenv. + // just check the file itself, and that's it. + if (cmd.match(/\//) || isWindows && cmd.match(/\\/)) + pathEnv = [''] + + return { + env: pathEnv, + ext: pathExt, + extExe: pathExtExe + } +} + +function which (cmd, opt, cb) { + if (typeof opt === 'function') { + cb = opt + opt = {} + } + + var info = getPathInfo(cmd, opt) + var pathEnv = info.env + var pathExt = info.ext + var pathExtExe = info.extExe + var found = [] + + ;(function F (i, l) { + if (i === l) { + if (opt.all && found.length) + return cb(null, found) + else + return cb(getNotFoundError(cmd)) + } + + var pathPart = pathEnv[i] + if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"') + pathPart = pathPart.slice(1, -1) + + var p = path.join(pathPart, cmd) + if (!pathPart && (/^\.[\\\/]/).test(cmd)) { + p = cmd.slice(0, 2) + p + } + ;(function E (ii, ll) { + if (ii === ll) return F(i + 1, l) + var ext = pathExt[ii] + isexe(p + ext, { pathExt: pathExtExe }, function (er, is) { + if (!er && is) { + if (opt.all) + found.push(p + ext) + else + return cb(null, p + ext) + } + return E(ii + 1, ll) + }) + })(0, pathExt.length) + })(0, pathEnv.length) +} + +function whichSync (cmd, opt) { + opt = opt || {} + + var info = getPathInfo(cmd, opt) + var pathEnv = info.env + var pathExt = info.ext + var pathExtExe = info.extExe + var found = [] + + for (var i = 0, l = pathEnv.length; i < l; i ++) { + var pathPart = pathEnv[i] + if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"') + pathPart = pathPart.slice(1, -1) + + var p = path.join(pathPart, cmd) + if (!pathPart && /^\.[\\\/]/.test(cmd)) { + p = cmd.slice(0, 2) + p + } + for (var j = 0, ll = pathExt.length; j < ll; j ++) { + var cur = p + pathExt[j] + var is + try { + is = isexe.sync(cur, { pathExt: pathExtExe }) + if (is) { + if (opt.all) + found.push(cur) + else + return cur + } + } catch (ex) {} + } + } + + if (opt.all && found.length) + return found + + if (opt.nothrow) + return null + + throw getNotFoundError(cmd) +} diff --git a/WebServer/node_modules/wide-align/align.js b/WebServer/node_modules/wide-align/align.js new file mode 100644 index 000000000..4f94ca4cd --- /dev/null +++ b/WebServer/node_modules/wide-align/align.js @@ -0,0 +1,65 @@ +'use strict' +var stringWidth = require('string-width') + +exports.center = alignCenter +exports.left = alignLeft +exports.right = alignRight + +// lodash's way of generating pad characters. + +function createPadding (width) { + var result = '' + var string = ' ' + var n = width + do { + if (n % 2) { + result += string; + } + n = Math.floor(n / 2); + string += string; + } while (n); + + return result; +} + +function alignLeft (str, width) { + var trimmed = str.trimRight() + if (trimmed.length === 0 && str.length >= width) return str + var padding = '' + var strWidth = stringWidth(trimmed) + + if (strWidth < width) { + padding = createPadding(width - strWidth) + } + + return trimmed + padding +} + +function alignRight (str, width) { + var trimmed = str.trimLeft() + if (trimmed.length === 0 && str.length >= width) return str + var padding = '' + var strWidth = stringWidth(trimmed) + + if (strWidth < width) { + padding = createPadding(width - strWidth) + } + + return padding + trimmed +} + +function alignCenter (str, width) { + var trimmed = str.trim() + if (trimmed.length === 0 && str.length >= width) return str + var padLeft = '' + var padRight = '' + var strWidth = stringWidth(trimmed) + + if (strWidth < width) { + var padLeftBy = parseInt((width - strWidth) / 2, 10) + padLeft = createPadding(padLeftBy) + padRight = createPadding(width - (strWidth + padLeftBy)) + } + + return padLeft + trimmed + padRight +} diff --git a/WebServer/node_modules/wide-align/package.json b/WebServer/node_modules/wide-align/package.json new file mode 100644 index 000000000..7d5cfa9f6 --- /dev/null +++ b/WebServer/node_modules/wide-align/package.json @@ -0,0 +1,69 @@ +{ + "_args": [ + [ + "wide-align@1.1.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "wide-align@1.1.3", + "_id": "wide-align@1.1.3", + "_inBundle": false, + "_integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "_location": "/wide-align", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "wide-align@1.1.3", + "name": "wide-align", + "escapedName": "wide-align", + "rawSpec": "1.1.3", + "saveSpec": null, + "fetchSpec": "1.1.3" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "_spec": "1.1.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Rebecca Turner", + "email": "me@re-becca.org", + "url": "http://re-becca.org/" + }, + "bugs": { + "url": "https://github.com/iarna/wide-align/issues" + }, + "dependencies": { + "string-width": "^1.0.2 || 2" + }, + "description": "A wide-character aware text alignment function for use on the console or with fixed width fonts.", + "devDependencies": { + "tap": "10 || 11 || 12" + }, + "files": [ + "align.js" + ], + "homepage": "https://github.com/iarna/wide-align#readme", + "keywords": [ + "wide", + "double", + "unicode", + "cjkv", + "pad", + "align" + ], + "license": "ISC", + "main": "align.js", + "name": "wide-align", + "repository": { + "type": "git", + "url": "git+https://github.com/iarna/wide-align.git" + }, + "scripts": { + "test": "tap --coverage test/*.js", + "version": "perl -pi -e 's/^( \"version\": $ENV{npm_config_node_version}\").*?\",/$1abc\",/' package-lock.json ; git add package-lock.json" + }, + "version": "1.1.3" +} diff --git a/WebServer/node_modules/wrap-ansi/index.js b/WebServer/node_modules/wrap-ansi/index.js new file mode 100644 index 000000000..ff625435f --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/index.js @@ -0,0 +1,168 @@ +'use strict'; +var stringWidth = require('string-width'); +var stripAnsi = require('strip-ansi'); + +var ESCAPES = [ + '\u001b', + '\u009b' +]; + +var END_CODE = 39; + +var ESCAPE_CODES = { + 0: 0, + 1: 22, + 2: 22, + 3: 23, + 4: 24, + 7: 27, + 8: 28, + 9: 29, + 30: 39, + 31: 39, + 32: 39, + 33: 39, + 34: 39, + 35: 39, + 36: 39, + 37: 39, + 90: 39, + 40: 49, + 41: 49, + 42: 49, + 43: 49, + 44: 49, + 45: 49, + 46: 49, + 47: 49 +}; + +function wrapAnsi(code) { + return ESCAPES[0] + '[' + code + 'm'; +} + +// calculate the length of words split on ' ', ignoring +// the extra characters added by ansi escape codes. +function wordLengths(str) { + return str.split(' ').map(function (s) { + return stringWidth(s); + }); +} + +// wrap a long word across multiple rows. +// ansi escape codes do not count towards length. +function wrapWord(rows, word, cols) { + var insideEscape = false; + var visible = stripAnsi(rows[rows.length - 1]).length; + + for (var i = 0; i < word.length; i++) { + var x = word[i]; + + rows[rows.length - 1] += x; + + if (ESCAPES.indexOf(x) !== -1) { + insideEscape = true; + } else if (insideEscape && x === 'm') { + insideEscape = false; + continue; + } + + if (insideEscape) { + continue; + } + + visible++; + + if (visible >= cols && i < word.length - 1) { + rows.push(''); + visible = 0; + } + } + + // it's possible that the last row we copy over is only + // ansi escape characters, handle this edge-case. + if (!visible && rows[rows.length - 1].length > 0 && rows.length > 1) { + rows[rows.length - 2] += rows.pop(); + } +} + +// the wrap-ansi module can be invoked +// in either 'hard' or 'soft' wrap mode. +// +// 'hard' will never allow a string to take up more +// than cols characters. +// +// 'soft' allows long words to expand past the column length. +function exec(str, cols, opts) { + var options = opts || {}; + + var pre = ''; + var ret = ''; + var escapeCode; + + var lengths = wordLengths(str); + var words = str.split(' '); + var rows = ['']; + + for (var i = 0, word; (word = words[i]) !== undefined; i++) { + var rowLength = stringWidth(rows[rows.length - 1]); + + if (rowLength) { + rows[rows.length - 1] += ' '; + rowLength++; + } + + // in 'hard' wrap mode, the length of a line is + // never allowed to extend past 'cols'. + if (lengths[i] > cols && options.hard) { + if (rowLength) { + rows.push(''); + } + wrapWord(rows, word, cols); + continue; + } + + if (rowLength + lengths[i] > cols && rowLength > 0) { + if (options.wordWrap === false && rowLength < cols) { + wrapWord(rows, word, cols); + continue; + } + + rows.push(''); + } + + rows[rows.length - 1] += word; + } + + pre = rows.map(function (r) { + return r.trim(); + }).join('\n'); + + for (var j = 0; j < pre.length; j++) { + var y = pre[j]; + + ret += y; + + if (ESCAPES.indexOf(y) !== -1) { + var code = parseFloat(/[0-9][^m]*/.exec(pre.slice(j, j + 4))); + escapeCode = code === END_CODE ? null : code; + } + + if (escapeCode && ESCAPE_CODES[escapeCode]) { + if (pre[j + 1] === '\n') { + ret += wrapAnsi(ESCAPE_CODES[escapeCode]); + } else if (y === '\n') { + ret += wrapAnsi(escapeCode); + } + } + } + + return ret; +} + +// for each line break, invoke the method separately. +module.exports = function (str, cols, opts) { + return String(str).split('\n').map(function (substr) { + return exec(substr, cols, opts); + }).join('\n'); +}; diff --git a/WebServer/node_modules/wrap-ansi/node_modules/ansi-regex/index.js b/WebServer/node_modules/wrap-ansi/node_modules/ansi-regex/index.js new file mode 100644 index 000000000..b9574ed7e --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/ansi-regex/index.js @@ -0,0 +1,4 @@ +'use strict'; +module.exports = function () { + return /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]/g; +}; diff --git a/WebServer/node_modules/wrap-ansi/node_modules/ansi-regex/package.json b/WebServer/node_modules/wrap-ansi/node_modules/ansi-regex/package.json new file mode 100644 index 000000000..aa3a90db2 --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/ansi-regex/package.json @@ -0,0 +1,111 @@ +{ + "_args": [ + [ + "ansi-regex@2.1.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "ansi-regex@2.1.1", + "_id": "ansi-regex@2.1.1", + "_inBundle": false, + "_integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "_location": "/wrap-ansi/ansi-regex", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "ansi-regex@2.1.1", + "name": "ansi-regex", + "escapedName": "ansi-regex", + "rawSpec": "2.1.1", + "saveSpec": null, + "fetchSpec": "2.1.1" + }, + "_requiredBy": [ + "/wrap-ansi/strip-ansi" + ], + "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "_spec": "2.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-regex/issues" + }, + "description": "Regular expression for matching ANSI escape codes", + "devDependencies": { + "ava": "0.17.0", + "xo": "0.16.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-regex#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "license": "MIT", + "maintainers": [ + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + { + "name": "Joshua Appelman", + "email": "jappelman@xebia.com", + "url": "jbnicolai.com" + }, + { + "name": "JD Ballard", + "email": "i.am.qix@gmail.com", + "url": "github.com/qix-" + } + ], + "name": "ansi-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-regex.git" + }, + "scripts": { + "test": "xo && ava --verbose", + "view-supported": "node fixtures/view-codes.js" + }, + "version": "2.1.1", + "xo": { + "rules": { + "guard-for-in": 0, + "no-loop-func": 0 + } + } +} diff --git a/WebServer/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point/index.js b/WebServer/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point/index.js new file mode 100644 index 000000000..a7d3e3855 --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point/index.js @@ -0,0 +1,46 @@ +'use strict'; +var numberIsNan = require('number-is-nan'); + +module.exports = function (x) { + if (numberIsNan(x)) { + return false; + } + + // https://github.com/nodejs/io.js/blob/cff7300a578be1b10001f2d967aaedc88aee6402/lib/readline.js#L1369 + + // code points are derived from: + // http://www.unix.org/Public/UNIDATA/EastAsianWidth.txt + if (x >= 0x1100 && ( + x <= 0x115f || // Hangul Jamo + 0x2329 === x || // LEFT-POINTING ANGLE BRACKET + 0x232a === x || // RIGHT-POINTING ANGLE BRACKET + // CJK Radicals Supplement .. Enclosed CJK Letters and Months + (0x2e80 <= x && x <= 0x3247 && x !== 0x303f) || + // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A + 0x3250 <= x && x <= 0x4dbf || + // CJK Unified Ideographs .. Yi Radicals + 0x4e00 <= x && x <= 0xa4c6 || + // Hangul Jamo Extended-A + 0xa960 <= x && x <= 0xa97c || + // Hangul Syllables + 0xac00 <= x && x <= 0xd7a3 || + // CJK Compatibility Ideographs + 0xf900 <= x && x <= 0xfaff || + // Vertical Forms + 0xfe10 <= x && x <= 0xfe19 || + // CJK Compatibility Forms .. Small Form Variants + 0xfe30 <= x && x <= 0xfe6b || + // Halfwidth and Fullwidth Forms + 0xff01 <= x && x <= 0xff60 || + 0xffe0 <= x && x <= 0xffe6 || + // Kana Supplement + 0x1b000 <= x && x <= 0x1b001 || + // Enclosed Ideographic Supplement + 0x1f200 <= x && x <= 0x1f251 || + // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane + 0x20000 <= x && x <= 0x3fffd)) { + return true; + } + + return false; +} diff --git a/WebServer/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point/package.json b/WebServer/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point/package.json new file mode 100644 index 000000000..b8fb466ce --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point/package.json @@ -0,0 +1,80 @@ +{ + "_args": [ + [ + "is-fullwidth-code-point@1.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "is-fullwidth-code-point@1.0.0", + "_id": "is-fullwidth-code-point@1.0.0", + "_inBundle": false, + "_integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "_location": "/wrap-ansi/is-fullwidth-code-point", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "is-fullwidth-code-point@1.0.0", + "name": "is-fullwidth-code-point", + "escapedName": "is-fullwidth-code-point", + "rawSpec": "1.0.0", + "saveSpec": null, + "fetchSpec": "1.0.0" + }, + "_requiredBy": [ + "/wrap-ansi/string-width" + ], + "_resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "_spec": "1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/is-fullwidth-code-point/issues" + }, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "description": "Check if the character represented by a given Unicode code point is fullwidth", + "devDependencies": { + "ava": "0.0.4", + "code-point-at": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/is-fullwidth-code-point#readme", + "keywords": [ + "fullwidth", + "full-width", + "full", + "width", + "unicode", + "character", + "char", + "string", + "str", + "codepoint", + "code", + "point", + "is", + "detect", + "check" + ], + "license": "MIT", + "name": "is-fullwidth-code-point", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/is-fullwidth-code-point.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.0.0" +} diff --git a/WebServer/node_modules/wrap-ansi/node_modules/string-width/index.js b/WebServer/node_modules/wrap-ansi/node_modules/string-width/index.js new file mode 100644 index 000000000..b9bec6244 --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/string-width/index.js @@ -0,0 +1,37 @@ +'use strict'; +var stripAnsi = require('strip-ansi'); +var codePointAt = require('code-point-at'); +var isFullwidthCodePoint = require('is-fullwidth-code-point'); + +// https://github.com/nodejs/io.js/blob/cff7300a578be1b10001f2d967aaedc88aee6402/lib/readline.js#L1345 +module.exports = function (str) { + if (typeof str !== 'string' || str.length === 0) { + return 0; + } + + var width = 0; + + str = stripAnsi(str); + + for (var i = 0; i < str.length; i++) { + var code = codePointAt(str, i); + + // ignore control characters + if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) { + continue; + } + + // surrogates + if (code >= 0x10000) { + i++; + } + + if (isFullwidthCodePoint(code)) { + width += 2; + } else { + width++; + } + } + + return width; +}; diff --git a/WebServer/node_modules/wrap-ansi/node_modules/string-width/package.json b/WebServer/node_modules/wrap-ansi/node_modules/string-width/package.json new file mode 100644 index 000000000..b1a0adb93 --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/string-width/package.json @@ -0,0 +1,91 @@ +{ + "_args": [ + [ + "string-width@1.0.2", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "string-width@1.0.2", + "_id": "string-width@1.0.2", + "_inBundle": false, + "_integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "_location": "/wrap-ansi/string-width", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "string-width@1.0.2", + "name": "string-width", + "escapedName": "string-width", + "rawSpec": "1.0.2", + "saveSpec": null, + "fetchSpec": "1.0.2" + }, + "_requiredBy": [ + "/wrap-ansi" + ], + "_resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "_spec": "1.0.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/string-width/issues" + }, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "description": "Get the visual width of a string - the number of columns required to display it", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/string-width#readme", + "keywords": [ + "string", + "str", + "character", + "char", + "unicode", + "width", + "visual", + "column", + "columns", + "fullwidth", + "full-width", + "full", + "ansi", + "escape", + "codes", + "cli", + "command-line", + "terminal", + "console", + "cjk", + "chinese", + "japanese", + "korean", + "fixed-width" + ], + "license": "MIT", + "name": "string-width", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/string-width.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.0.2" +} diff --git a/WebServer/node_modules/wrap-ansi/node_modules/strip-ansi/index.js b/WebServer/node_modules/wrap-ansi/node_modules/strip-ansi/index.js new file mode 100644 index 000000000..099480fbf --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/strip-ansi/index.js @@ -0,0 +1,6 @@ +'use strict'; +var ansiRegex = require('ansi-regex')(); + +module.exports = function (str) { + return typeof str === 'string' ? str.replace(ansiRegex, '') : str; +}; diff --git a/WebServer/node_modules/wrap-ansi/node_modules/strip-ansi/package.json b/WebServer/node_modules/wrap-ansi/node_modules/strip-ansi/package.json new file mode 100644 index 000000000..96c22e5ee --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/node_modules/strip-ansi/package.json @@ -0,0 +1,105 @@ +{ + "_args": [ + [ + "strip-ansi@3.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "strip-ansi@3.0.1", + "_id": "strip-ansi@3.0.1", + "_inBundle": false, + "_integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "_location": "/wrap-ansi/strip-ansi", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "strip-ansi@3.0.1", + "name": "strip-ansi", + "escapedName": "strip-ansi", + "rawSpec": "3.0.1", + "saveSpec": null, + "fetchSpec": "3.0.1" + }, + "_requiredBy": [ + "/wrap-ansi", + "/wrap-ansi/string-width" + ], + "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "_spec": "3.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "description": "Strip ANSI escape codes", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/strip-ansi#readme", + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "maintainers": [ + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + { + "name": "Joshua Boy Nicolai Appelman", + "email": "joshua@jbna.nl", + "url": "jbna.nl" + }, + { + "name": "JD Ballard", + "email": "i.am.qix@gmail.com", + "url": "github.com/qix-" + } + ], + "name": "strip-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "3.0.1" +} diff --git a/WebServer/node_modules/wrap-ansi/package.json b/WebServer/node_modules/wrap-ansi/package.json new file mode 100644 index 000000000..b9043310a --- /dev/null +++ b/WebServer/node_modules/wrap-ansi/package.json @@ -0,0 +1,122 @@ +{ + "_args": [ + [ + "wrap-ansi@2.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "wrap-ansi@2.1.0", + "_id": "wrap-ansi@2.1.0", + "_inBundle": false, + "_integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "_location": "/wrap-ansi", + "_phantomChildren": { + "code-point-at": "1.1.0", + "number-is-nan": "1.0.1" + }, + "_requested": { + "type": "version", + "registry": true, + "raw": "wrap-ansi@2.1.0", + "name": "wrap-ansi", + "escapedName": "wrap-ansi", + "rawSpec": "2.1.0", + "saveSpec": null, + "fetchSpec": "2.1.0" + }, + "_requiredBy": [ + "/cliui" + ], + "_resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "_spec": "2.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/wrap-ansi/issues" + }, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "description": "Wordwrap a string with ANSI escape codes", + "devDependencies": { + "ava": "^0.16.0", + "chalk": "^1.1.0", + "coveralls": "^2.11.4", + "has-ansi": "^2.0.0", + "nyc": "^6.2.1", + "strip-ansi": "^3.0.0", + "xo": "*" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/wrap-ansi#readme", + "keywords": [ + "wrap", + "break", + "wordwrap", + "wordbreak", + "linewrap", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "maintainers": [ + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + { + "name": "Joshua Appelman", + "email": "jappelman@xebia.com", + "url": "jbnicolai.com" + }, + { + "name": "JD Ballard", + "email": "i.am.qix@gmail.com", + "url": "github.com/qix-" + }, + { + "name": "Benjamin Coe", + "email": "ben@npmjs.com", + "url": "github.com/bcoe" + } + ], + "name": "wrap-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/wrap-ansi.git" + }, + "scripts": { + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "test": "xo && nyc ava" + }, + "version": "2.1.0" +} diff --git a/WebServer/node_modules/wrappy/package.json b/WebServer/node_modules/wrappy/package.json new file mode 100644 index 000000000..2b1cb7bc7 --- /dev/null +++ b/WebServer/node_modules/wrappy/package.json @@ -0,0 +1,62 @@ +{ + "_args": [ + [ + "wrappy@1.0.2", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "wrappy@1.0.2", + "_id": "wrappy@1.0.2", + "_inBundle": false, + "_integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "_location": "/wrappy", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "wrappy@1.0.2", + "name": "wrappy", + "escapedName": "wrappy", + "rawSpec": "1.0.2", + "saveSpec": null, + "fetchSpec": "1.0.2" + }, + "_requiredBy": [ + "/inflight", + "/once" + ], + "_resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "_spec": "1.0.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/npm/wrappy/issues" + }, + "dependencies": {}, + "description": "Callback wrapping utility", + "devDependencies": { + "tap": "^2.3.1" + }, + "directories": { + "test": "test" + }, + "files": [ + "wrappy.js" + ], + "homepage": "https://github.com/npm/wrappy", + "license": "ISC", + "main": "wrappy.js", + "name": "wrappy", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/wrappy.git" + }, + "scripts": { + "test": "tap --coverage test/*.js" + }, + "version": "1.0.2" +} diff --git a/WebServer/node_modules/wrappy/wrappy.js b/WebServer/node_modules/wrappy/wrappy.js new file mode 100644 index 000000000..bb7e7d6fc --- /dev/null +++ b/WebServer/node_modules/wrappy/wrappy.js @@ -0,0 +1,33 @@ +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} diff --git a/WebServer/node_modules/y18n/index.js b/WebServer/node_modules/y18n/index.js new file mode 100644 index 000000000..d72068162 --- /dev/null +++ b/WebServer/node_modules/y18n/index.js @@ -0,0 +1,188 @@ +var fs = require('fs') +var path = require('path') +var util = require('util') + +function Y18N (opts) { + // configurable options. + opts = opts || {} + this.directory = opts.directory || './locales' + this.updateFiles = typeof opts.updateFiles === 'boolean' ? opts.updateFiles : true + this.locale = opts.locale || 'en' + this.fallbackToLanguage = typeof opts.fallbackToLanguage === 'boolean' ? opts.fallbackToLanguage : true + + // internal stuff. + this.cache = {} + this.writeQueue = [] +} + +Y18N.prototype.__ = function () { + if (typeof arguments[0] !== 'string') { + return this._taggedLiteral.apply(this, arguments) + } + var args = Array.prototype.slice.call(arguments) + var str = args.shift() + var cb = function () {} // start with noop. + + if (typeof args[args.length - 1] === 'function') cb = args.pop() + cb = cb || function () {} // noop. + + if (!this.cache[this.locale]) this._readLocaleFile() + + // we've observed a new string, update the language file. + if (!this.cache[this.locale][str] && this.updateFiles) { + this.cache[this.locale][str] = str + + // include the current directory and locale, + // since these values could change before the + // write is performed. + this._enqueueWrite([this.directory, this.locale, cb]) + } else { + cb() + } + + return util.format.apply(util, [this.cache[this.locale][str] || str].concat(args)) +} + +Y18N.prototype._taggedLiteral = function (parts) { + var args = arguments + var str = '' + parts.forEach(function (part, i) { + var arg = args[i + 1] + str += part + if (typeof arg !== 'undefined') { + str += '%s' + } + }) + return this.__.apply(null, [str].concat([].slice.call(arguments, 1))) +} + +Y18N.prototype._enqueueWrite = function (work) { + this.writeQueue.push(work) + if (this.writeQueue.length === 1) this._processWriteQueue() +} + +Y18N.prototype._processWriteQueue = function () { + var _this = this + var work = this.writeQueue[0] + + // destructure the enqueued work. + var directory = work[0] + var locale = work[1] + var cb = work[2] + + var languageFile = this._resolveLocaleFile(directory, locale) + var serializedLocale = JSON.stringify(this.cache[locale], null, 2) + + fs.writeFile(languageFile, serializedLocale, 'utf-8', function (err) { + _this.writeQueue.shift() + if (_this.writeQueue.length > 0) _this._processWriteQueue() + cb(err) + }) +} + +Y18N.prototype._readLocaleFile = function () { + var localeLookup = {} + var languageFile = this._resolveLocaleFile(this.directory, this.locale) + + try { + localeLookup = JSON.parse(fs.readFileSync(languageFile, 'utf-8')) + } catch (err) { + if (err instanceof SyntaxError) { + err.message = 'syntax error in ' + languageFile + } + + if (err.code === 'ENOENT') localeLookup = {} + else throw err + } + + this.cache[this.locale] = localeLookup +} + +Y18N.prototype._resolveLocaleFile = function (directory, locale) { + var file = path.resolve(directory, './', locale + '.json') + if (this.fallbackToLanguage && !this._fileExistsSync(file) && ~locale.lastIndexOf('_')) { + // attempt fallback to language only + var languageFile = path.resolve(directory, './', locale.split('_')[0] + '.json') + if (this._fileExistsSync(languageFile)) file = languageFile + } + return file +} + +// this only exists because fs.existsSync() "will be deprecated" +// see https://nodejs.org/api/fs.html#fs_fs_existssync_path +Y18N.prototype._fileExistsSync = function (file) { + try { + return fs.statSync(file).isFile() + } catch (err) { + return false + } +} + +Y18N.prototype.__n = function () { + var args = Array.prototype.slice.call(arguments) + var singular = args.shift() + var plural = args.shift() + var quantity = args.shift() + + var cb = function () {} // start with noop. + if (typeof args[args.length - 1] === 'function') cb = args.pop() + + if (!this.cache[this.locale]) this._readLocaleFile() + + var str = quantity === 1 ? singular : plural + if (this.cache[this.locale][singular]) { + str = this.cache[this.locale][singular][quantity === 1 ? 'one' : 'other'] + } + + // we've observed a new string, update the language file. + if (!this.cache[this.locale][singular] && this.updateFiles) { + this.cache[this.locale][singular] = { + one: singular, + other: plural + } + + // include the current directory and locale, + // since these values could change before the + // write is performed. + this._enqueueWrite([this.directory, this.locale, cb]) + } else { + cb() + } + + // if a %d placeholder is provided, add quantity + // to the arguments expanded by util.format. + var values = [str] + if (~str.indexOf('%d')) values.push(quantity) + + return util.format.apply(util, values.concat(args)) +} + +Y18N.prototype.setLocale = function (locale) { + this.locale = locale +} + +Y18N.prototype.getLocale = function () { + return this.locale +} + +Y18N.prototype.updateLocale = function (obj) { + if (!this.cache[this.locale]) this._readLocaleFile() + + for (var key in obj) { + this.cache[this.locale][key] = obj[key] + } +} + +module.exports = function (opts) { + var y18n = new Y18N(opts) + + // bind all functions to y18n, so that + // they can be used in isolation. + for (var key in y18n) { + if (typeof y18n[key] === 'function') { + y18n[key] = y18n[key].bind(y18n) + } + } + + return y18n +} diff --git a/WebServer/node_modules/y18n/package.json b/WebServer/node_modules/y18n/package.json new file mode 100644 index 000000000..449b3f853 --- /dev/null +++ b/WebServer/node_modules/y18n/package.json @@ -0,0 +1,71 @@ +{ + "_args": [ + [ + "y18n@4.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "y18n@4.0.0", + "_id": "y18n@4.0.0", + "_inBundle": false, + "_integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "_location": "/y18n", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "y18n@4.0.0", + "name": "y18n", + "escapedName": "y18n", + "rawSpec": "4.0.0", + "saveSpec": null, + "fetchSpec": "4.0.0" + }, + "_requiredBy": [ + "/yargs", + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "_spec": "4.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/yargs/y18n/issues" + }, + "description": "the bare-bones internationalization library used by yargs", + "devDependencies": { + "chai": "^4.0.1", + "coveralls": "^3.0.0", + "mocha": "^4.0.1", + "nyc": "^11.0.1", + "rimraf": "^2.5.0", + "standard": "^10.0.0-beta.0", + "standard-version": "^4.2.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/yargs/y18n", + "keywords": [ + "i18n", + "internationalization", + "yargs" + ], + "license": "ISC", + "main": "index.js", + "name": "y18n", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/yargs/y18n.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "pretest": "standard", + "release": "standard-version", + "test": "nyc mocha" + }, + "version": "4.0.0" +} diff --git a/WebServer/node_modules/yargs-parser/index.js b/WebServer/node_modules/yargs-parser/index.js new file mode 100644 index 000000000..20d472d10 --- /dev/null +++ b/WebServer/node_modules/yargs-parser/index.js @@ -0,0 +1,892 @@ +var camelCase = require('camelcase') +var decamelize = require('decamelize') +var path = require('path') +var tokenizeArgString = require('./lib/tokenize-arg-string') +var util = require('util') + +function parse (args, opts) { + if (!opts) opts = {} + // allow a string argument to be passed in rather + // than an argv array. + args = tokenizeArgString(args) + + // aliases might have transitive relationships, normalize this. + var aliases = combineAliases(opts.alias || {}) + var configuration = assign({ + 'short-option-groups': true, + 'camel-case-expansion': true, + 'dot-notation': true, + 'parse-numbers': true, + 'boolean-negation': true, + 'negation-prefix': 'no-', + 'duplicate-arguments-array': true, + 'flatten-duplicate-arrays': true, + 'populate--': false, + 'combine-arrays': false, + 'set-placeholder-key': false, + 'halt-at-non-option': false + }, opts.configuration) + var defaults = opts.default || {} + var configObjects = opts.configObjects || [] + var envPrefix = opts.envPrefix + var notFlagsOption = configuration['populate--'] + var notFlagsArgv = notFlagsOption ? '--' : '_' + var newAliases = {} + // allow a i18n handler to be passed in, default to a fake one (util.format). + var __ = opts.__ || function (str) { + return util.format.apply(util, Array.prototype.slice.call(arguments)) + } + var error = null + var flags = { + aliases: {}, + arrays: {}, + bools: {}, + strings: {}, + numbers: {}, + counts: {}, + normalize: {}, + configs: {}, + defaulted: {}, + nargs: {}, + coercions: {}, + keys: [] + } + var negative = /^-[0-9]+(\.[0-9]+)?/ + var negatedBoolean = new RegExp('^--' + configuration['negation-prefix'] + '(.+)') + + ;[].concat(opts.array).filter(Boolean).forEach(function (opt) { + var key = opt.key || opt + + // assign to flags[bools|strings|numbers] + const assignment = Object.keys(opt).map(function (key) { + return ({ + boolean: 'bools', + string: 'strings', + number: 'numbers' + })[key] + }).filter(Boolean).pop() + + // assign key to be coerced + if (assignment) { + flags[assignment][key] = true + } + + flags.arrays[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.boolean).filter(Boolean).forEach(function (key) { + flags.bools[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.number).filter(Boolean).forEach(function (key) { + flags.numbers[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.count).filter(Boolean).forEach(function (key) { + flags.counts[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.normalize).filter(Boolean).forEach(function (key) { + flags.normalize[key] = true + flags.keys.push(key) + }) + + Object.keys(opts.narg || {}).forEach(function (k) { + flags.nargs[k] = opts.narg[k] + flags.keys.push(k) + }) + + Object.keys(opts.coerce || {}).forEach(function (k) { + flags.coercions[k] = opts.coerce[k] + flags.keys.push(k) + }) + + if (Array.isArray(opts.config) || typeof opts.config === 'string') { + ;[].concat(opts.config).filter(Boolean).forEach(function (key) { + flags.configs[key] = true + }) + } else { + Object.keys(opts.config || {}).forEach(function (k) { + flags.configs[k] = opts.config[k] + }) + } + + // create a lookup table that takes into account all + // combinations of aliases: {f: ['foo'], foo: ['f']} + extendAliases(opts.key, aliases, opts.default, flags.arrays) + + // apply default values to all aliases. + Object.keys(defaults).forEach(function (key) { + (flags.aliases[key] || []).forEach(function (alias) { + defaults[alias] = defaults[key] + }) + }) + + var argv = { _: [] } + + Object.keys(flags.bools).forEach(function (key) { + if (Object.prototype.hasOwnProperty.call(defaults, key)) { + setArg(key, defaults[key]) + setDefaulted(key) + } + }) + + var notFlags = [] + + for (var i = 0; i < args.length; i++) { + var arg = args[i] + var broken + var key + var letters + var m + var next + var value + + // -- separated by = + if (arg.match(/^--.+=/) || ( + !configuration['short-option-groups'] && arg.match(/^-.+=/) + )) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + m = arg.match(/^--?([^=]+)=([\s\S]*)$/) + + // nargs format = '--f=monkey washing cat' + if (checkAllAliases(m[1], flags.nargs)) { + args.splice(i + 1, 0, m[2]) + i = eatNargs(i, m[1], args) + // arrays format = '--f=a b c' + } else if (checkAllAliases(m[1], flags.arrays) && args.length > i + 1) { + args.splice(i + 1, 0, m[2]) + i = eatArray(i, m[1], args) + } else { + setArg(m[1], m[2]) + } + } else if (arg.match(negatedBoolean) && configuration['boolean-negation']) { + key = arg.match(negatedBoolean)[1] + setArg(key, false) + + // -- seperated by space. + } else if (arg.match(/^--.+/) || ( + !configuration['short-option-groups'] && arg.match(/^-.+/) + )) { + key = arg.match(/^--?(.+)/)[1] + + // nargs format = '--foo a b c' + if (checkAllAliases(key, flags.nargs)) { + i = eatNargs(i, key, args) + // array format = '--foo a b c' + } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) { + i = eatArray(i, key, args) + } else { + next = args[i + 1] + + if (next !== undefined && (!next.match(/^-/) || + next.match(negative)) && + !checkAllAliases(key, flags.bools) && + !checkAllAliases(key, flags.counts)) { + setArg(key, next) + i++ + } else if (/^(true|false)$/.test(next)) { + setArg(key, next) + i++ + } else { + setArg(key, defaultValue(key)) + } + } + + // dot-notation flag seperated by '='. + } else if (arg.match(/^-.\..+=/)) { + m = arg.match(/^-([^=]+)=([\s\S]*)$/) + setArg(m[1], m[2]) + + // dot-notation flag seperated by space. + } else if (arg.match(/^-.\..+/)) { + next = args[i + 1] + key = arg.match(/^-(.\..+)/)[1] + + if (next !== undefined && !next.match(/^-/) && + !checkAllAliases(key, flags.bools) && + !checkAllAliases(key, flags.counts)) { + setArg(key, next) + i++ + } else { + setArg(key, defaultValue(key)) + } + } else if (arg.match(/^-[^-]+/) && !arg.match(negative)) { + letters = arg.slice(1, -1).split('') + broken = false + + for (var j = 0; j < letters.length; j++) { + next = arg.slice(j + 2) + + if (letters[j + 1] && letters[j + 1] === '=') { + value = arg.slice(j + 3) + key = letters[j] + + // nargs format = '-f=monkey washing cat' + if (checkAllAliases(key, flags.nargs)) { + args.splice(i + 1, 0, value) + i = eatNargs(i, key, args) + // array format = '-f=a b c' + } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) { + args.splice(i + 1, 0, value) + i = eatArray(i, key, args) + } else { + setArg(key, value) + } + + broken = true + break + } + + if (next === '-') { + setArg(letters[j], next) + continue + } + + // current letter is an alphabetic character and next value is a number + if (/[A-Za-z]/.test(letters[j]) && + /^-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next) + broken = true + break + } + + if (letters[j + 1] && letters[j + 1].match(/\W/)) { + setArg(letters[j], next) + broken = true + break + } else { + setArg(letters[j], defaultValue(letters[j])) + } + } + + key = arg.slice(-1)[0] + + if (!broken && key !== '-') { + // nargs format = '-f a b c' + if (checkAllAliases(key, flags.nargs)) { + i = eatNargs(i, key, args) + // array format = '-f a b c' + } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) { + i = eatArray(i, key, args) + } else { + next = args[i + 1] + + if (next !== undefined && (!/^(-|--)[^-]/.test(next) || + next.match(negative)) && + !checkAllAliases(key, flags.bools) && + !checkAllAliases(key, flags.counts)) { + setArg(key, next) + i++ + } else if (/^(true|false)$/.test(next)) { + setArg(key, next) + i++ + } else { + setArg(key, defaultValue(key)) + } + } + } + } else if (arg === '--') { + notFlags = args.slice(i + 1) + break + } else if (configuration['halt-at-non-option']) { + notFlags = args.slice(i) + break + } else { + argv._.push(maybeCoerceNumber('_', arg)) + } + } + + // order of precedence: + // 1. command line arg + // 2. value from env var + // 3. value from config file + // 4. value from config objects + // 5. configured default value + applyEnvVars(argv, true) // special case: check env vars that point to config file + applyEnvVars(argv, false) + setConfig(argv) + setConfigObjects() + applyDefaultsAndAliases(argv, flags.aliases, defaults) + applyCoercions(argv) + if (configuration['set-placeholder-key']) setPlaceholderKeys(argv) + + // for any counts either not in args or without an explicit default, set to 0 + Object.keys(flags.counts).forEach(function (key) { + if (!hasKey(argv, key.split('.'))) setArg(key, 0) + }) + + // '--' defaults to undefined. + if (notFlagsOption && notFlags.length) argv[notFlagsArgv] = [] + notFlags.forEach(function (key) { + argv[notFlagsArgv].push(key) + }) + + // how many arguments should we consume, based + // on the nargs option? + function eatNargs (i, key, args) { + var ii + const toEat = checkAllAliases(key, flags.nargs) + + // nargs will not consume flag arguments, e.g., -abc, --foo, + // and terminates when one is observed. + var available = 0 + for (ii = i + 1; ii < args.length; ii++) { + if (!args[ii].match(/^-[^0-9]/)) available++ + else break + } + + if (available < toEat) error = Error(__('Not enough arguments following: %s', key)) + + const consumed = Math.min(available, toEat) + for (ii = i + 1; ii < (consumed + i + 1); ii++) { + setArg(key, args[ii]) + } + + return (i + consumed) + } + + // if an option is an array, eat all non-hyphenated arguments + // following it... YUM! + // e.g., --foo apple banana cat becomes ["apple", "banana", "cat"] + function eatArray (i, key, args) { + var start = i + 1 + var argsToSet = [] + var multipleArrayFlag = i > 0 + for (var ii = i + 1; ii < args.length; ii++) { + if (/^-/.test(args[ii]) && !negative.test(args[ii])) { + if (ii === start) { + setArg(key, defaultForType('array')) + } + multipleArrayFlag = true + break + } + i = ii + argsToSet.push(args[ii]) + } + if (multipleArrayFlag) { + setArg(key, argsToSet.map(function (arg) { + return processValue(key, arg) + })) + } else { + argsToSet.forEach(function (arg) { + setArg(key, arg) + }) + } + + return i + } + + function setArg (key, val) { + unsetDefaulted(key) + + if (/-/.test(key) && configuration['camel-case-expansion']) { + var alias = key.split('.').map(function (prop) { + return camelCase(prop) + }).join('.') + addNewAlias(key, alias) + } + + var value = processValue(key, val) + + var splitKey = key.split('.') + setKey(argv, splitKey, value) + + // handle populating aliases of the full key + if (flags.aliases[key]) { + flags.aliases[key].forEach(function (x) { + x = x.split('.') + setKey(argv, x, value) + }) + } + + // handle populating aliases of the first element of the dot-notation key + if (splitKey.length > 1 && configuration['dot-notation']) { + ;(flags.aliases[splitKey[0]] || []).forEach(function (x) { + x = x.split('.') + + // expand alias with nested objects in key + var a = [].concat(splitKey) + a.shift() // nuke the old key. + x = x.concat(a) + + setKey(argv, x, value) + }) + } + + // Set normalize getter and setter when key is in 'normalize' but isn't an array + if (checkAllAliases(key, flags.normalize) && !checkAllAliases(key, flags.arrays)) { + var keys = [key].concat(flags.aliases[key] || []) + keys.forEach(function (key) { + argv.__defineSetter__(key, function (v) { + val = path.normalize(v) + }) + + argv.__defineGetter__(key, function () { + return typeof val === 'string' ? path.normalize(val) : val + }) + }) + } + } + + function addNewAlias (key, alias) { + if (!(flags.aliases[key] && flags.aliases[key].length)) { + flags.aliases[key] = [alias] + newAliases[alias] = true + } + if (!(flags.aliases[alias] && flags.aliases[alias].length)) { + addNewAlias(alias, key) + } + } + + function processValue (key, val) { + // strings may be quoted, clean this up as we assign values. + if (typeof val === 'string' && + (val[0] === "'" || val[0] === '"') && + val[val.length - 1] === val[0] + ) { + val = val.substring(1, val.length - 1) + } + + // handle parsing boolean arguments --foo=true --bar false. + if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) { + if (typeof val === 'string') val = val === 'true' + } + + var value = maybeCoerceNumber(key, val) + + // increment a count given as arg (either no value or value parsed as boolean) + if (checkAllAliases(key, flags.counts) && (isUndefined(value) || typeof value === 'boolean')) { + value = increment + } + + // Set normalized value when key is in 'normalize' and in 'arrays' + if (checkAllAliases(key, flags.normalize) && checkAllAliases(key, flags.arrays)) { + if (Array.isArray(val)) value = val.map(path.normalize) + else value = path.normalize(val) + } + return value + } + + function maybeCoerceNumber (key, value) { + if (!checkAllAliases(key, flags.strings) && !checkAllAliases(key, flags.coercions)) { + const shouldCoerceNumber = isNumber(value) && configuration['parse-numbers'] && ( + Number.isSafeInteger(Math.floor(value)) + ) + if (shouldCoerceNumber || (!isUndefined(value) && checkAllAliases(key, flags.numbers))) value = Number(value) + } + return value + } + + // set args from config.json file, this should be + // applied last so that defaults can be applied. + function setConfig (argv) { + var configLookup = {} + + // expand defaults/aliases, in-case any happen to reference + // the config.json file. + applyDefaultsAndAliases(configLookup, flags.aliases, defaults) + + Object.keys(flags.configs).forEach(function (configKey) { + var configPath = argv[configKey] || configLookup[configKey] + if (configPath) { + try { + var config = null + var resolvedConfigPath = path.resolve(process.cwd(), configPath) + + if (typeof flags.configs[configKey] === 'function') { + try { + config = flags.configs[configKey](resolvedConfigPath) + } catch (e) { + config = e + } + if (config instanceof Error) { + error = config + return + } + } else { + config = require(resolvedConfigPath) + } + + setConfigObject(config) + } catch (ex) { + if (argv[configKey]) error = Error(__('Invalid JSON config file: %s', configPath)) + } + } + }) + } + + // set args from config object. + // it recursively checks nested objects. + function setConfigObject (config, prev) { + Object.keys(config).forEach(function (key) { + var value = config[key] + var fullKey = prev ? prev + '.' + key : key + + // if the value is an inner object and we have dot-notation + // enabled, treat inner objects in config the same as + // heavily nested dot notations (foo.bar.apple). + if (typeof value === 'object' && value !== null && !Array.isArray(value) && configuration['dot-notation']) { + // if the value is an object but not an array, check nested object + setConfigObject(value, fullKey) + } else { + // setting arguments via CLI takes precedence over + // values within the config file. + if (!hasKey(argv, fullKey.split('.')) || (flags.defaulted[fullKey]) || (flags.arrays[fullKey] && configuration['combine-arrays'])) { + setArg(fullKey, value) + } + } + }) + } + + // set all config objects passed in opts + function setConfigObjects () { + if (typeof configObjects === 'undefined') return + configObjects.forEach(function (configObject) { + setConfigObject(configObject) + }) + } + + function applyEnvVars (argv, configOnly) { + if (typeof envPrefix === 'undefined') return + + var prefix = typeof envPrefix === 'string' ? envPrefix : '' + Object.keys(process.env).forEach(function (envVar) { + if (prefix === '' || envVar.lastIndexOf(prefix, 0) === 0) { + // get array of nested keys and convert them to camel case + var keys = envVar.split('__').map(function (key, i) { + if (i === 0) { + key = key.substring(prefix.length) + } + return camelCase(key) + }) + + if (((configOnly && flags.configs[keys.join('.')]) || !configOnly) && (!hasKey(argv, keys) || flags.defaulted[keys.join('.')])) { + setArg(keys.join('.'), process.env[envVar]) + } + } + }) + } + + function applyCoercions (argv) { + var coerce + var applied = {} + Object.keys(argv).forEach(function (key) { + if (!applied.hasOwnProperty(key)) { // If we haven't already coerced this option via one of its aliases + coerce = checkAllAliases(key, flags.coercions) + if (typeof coerce === 'function') { + try { + var value = coerce(argv[key]) + ;([].concat(flags.aliases[key] || [], key)).forEach(ali => { + applied[ali] = argv[ali] = value + }) + } catch (err) { + error = err + } + } + } + }) + } + + function setPlaceholderKeys (argv) { + flags.keys.forEach((key) => { + // don't set placeholder keys for dot notation options 'foo.bar'. + if (~key.indexOf('.')) return + if (typeof argv[key] === 'undefined') argv[key] = undefined + }) + return argv + } + + function applyDefaultsAndAliases (obj, aliases, defaults) { + Object.keys(defaults).forEach(function (key) { + if (!hasKey(obj, key.split('.'))) { + setKey(obj, key.split('.'), defaults[key]) + + ;(aliases[key] || []).forEach(function (x) { + if (hasKey(obj, x.split('.'))) return + setKey(obj, x.split('.'), defaults[key]) + }) + } + }) + } + + function hasKey (obj, keys) { + var o = obj + + if (!configuration['dot-notation']) keys = [keys.join('.')] + + keys.slice(0, -1).forEach(function (key) { + o = (o[key] || {}) + }) + + var key = keys[keys.length - 1] + + if (typeof o !== 'object') return false + else return key in o + } + + function setKey (obj, keys, value) { + var o = obj + + if (!configuration['dot-notation']) keys = [keys.join('.')] + + keys.slice(0, -1).forEach(function (key, index) { + if (typeof o === 'object' && o[key] === undefined) { + o[key] = {} + } + + if (typeof o[key] !== 'object' || Array.isArray(o[key])) { + // ensure that o[key] is an array, and that the last item is an empty object. + if (Array.isArray(o[key])) { + o[key].push({}) + } else { + o[key] = [o[key], {}] + } + + // we want to update the empty object at the end of the o[key] array, so set o to that object + o = o[key][o[key].length - 1] + } else { + o = o[key] + } + }) + + var key = keys[keys.length - 1] + + var isTypeArray = checkAllAliases(keys.join('.'), flags.arrays) + var isValueArray = Array.isArray(value) + var duplicate = configuration['duplicate-arguments-array'] + + if (value === increment) { + o[key] = increment(o[key]) + } else if (Array.isArray(o[key])) { + if (duplicate && isTypeArray && isValueArray) { + o[key] = configuration['flatten-duplicate-arrays'] ? o[key].concat(value) : (Array.isArray(o[key][0]) ? o[key] : [o[key]]).concat([value]) + } else if (!duplicate && Boolean(isTypeArray) === Boolean(isValueArray)) { + o[key] = value + } else { + o[key] = o[key].concat([value]) + } + } else if (o[key] === undefined && isTypeArray) { + o[key] = isValueArray ? value : [value] + } else if (duplicate && !(o[key] === undefined || checkAllAliases(key, flags.bools) || checkAllAliases(keys.join('.'), flags.bools) || checkAllAliases(key, flags.counts))) { + o[key] = [ o[key], value ] + } else { + o[key] = value + } + } + + // extend the aliases list with inferred aliases. + function extendAliases () { + Array.prototype.slice.call(arguments).forEach(function (obj) { + Object.keys(obj || {}).forEach(function (key) { + // short-circuit if we've already added a key + // to the aliases array, for example it might + // exist in both 'opts.default' and 'opts.key'. + if (flags.aliases[key]) return + + flags.aliases[key] = [].concat(aliases[key] || []) + // For "--option-name", also set argv.optionName + flags.aliases[key].concat(key).forEach(function (x) { + if (/-/.test(x) && configuration['camel-case-expansion']) { + var c = camelCase(x) + if (c !== key && flags.aliases[key].indexOf(c) === -1) { + flags.aliases[key].push(c) + newAliases[c] = true + } + } + }) + // For "--optionName", also set argv['option-name'] + flags.aliases[key].concat(key).forEach(function (x) { + if (x.length > 1 && /[A-Z]/.test(x) && configuration['camel-case-expansion']) { + var c = decamelize(x, '-') + if (c !== key && flags.aliases[key].indexOf(c) === -1) { + flags.aliases[key].push(c) + newAliases[c] = true + } + } + }) + flags.aliases[key].forEach(function (x) { + flags.aliases[x] = [key].concat(flags.aliases[key].filter(function (y) { + return x !== y + })) + }) + }) + }) + } + + // check if a flag is set for any of a key's aliases. + function checkAllAliases (key, flag) { + var isSet = false + var toCheck = [].concat(flags.aliases[key] || [], key) + + toCheck.forEach(function (key) { + if (flag[key]) isSet = flag[key] + }) + + return isSet + } + + function setDefaulted (key) { + [].concat(flags.aliases[key] || [], key).forEach(function (k) { + flags.defaulted[k] = true + }) + } + + function unsetDefaulted (key) { + [].concat(flags.aliases[key] || [], key).forEach(function (k) { + delete flags.defaulted[k] + }) + } + + // make a best effor to pick a default value + // for an option based on name and type. + function defaultValue (key) { + if (!checkAllAliases(key, flags.bools) && + !checkAllAliases(key, flags.counts) && + `${key}` in defaults) { + return defaults[key] + } else { + return defaultForType(guessType(key)) + } + } + + // return a default value, given the type of a flag., + // e.g., key of type 'string' will default to '', rather than 'true'. + function defaultForType (type) { + var def = { + boolean: true, + string: '', + number: undefined, + array: [] + } + + return def[type] + } + + // given a flag, enforce a default type. + function guessType (key) { + var type = 'boolean' + + if (checkAllAliases(key, flags.strings)) type = 'string' + else if (checkAllAliases(key, flags.numbers)) type = 'number' + else if (checkAllAliases(key, flags.arrays)) type = 'array' + + return type + } + + function isNumber (x) { + if (x === null || x === undefined) return false + // if loaded from config, may already be a number. + if (typeof x === 'number') return true + // hexadecimal. + if (/^0x[0-9a-f]+$/i.test(x)) return true + // don't treat 0123 as a number; as it drops the leading '0'. + if (x.length > 1 && x[0] === '0') return false + return /^[-]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x) + } + + function isUndefined (num) { + return num === undefined + } + + return { + argv: argv, + error: error, + aliases: flags.aliases, + newAliases: newAliases, + configuration: configuration + } +} + +// if any aliases reference each other, we should +// merge them together. +function combineAliases (aliases) { + var aliasArrays = [] + var change = true + var combined = {} + + // turn alias lookup hash {key: ['alias1', 'alias2']} into + // a simple array ['key', 'alias1', 'alias2'] + Object.keys(aliases).forEach(function (key) { + aliasArrays.push( + [].concat(aliases[key], key) + ) + }) + + // combine arrays until zero changes are + // made in an iteration. + while (change) { + change = false + for (var i = 0; i < aliasArrays.length; i++) { + for (var ii = i + 1; ii < aliasArrays.length; ii++) { + var intersect = aliasArrays[i].filter(function (v) { + return aliasArrays[ii].indexOf(v) !== -1 + }) + + if (intersect.length) { + aliasArrays[i] = aliasArrays[i].concat(aliasArrays[ii]) + aliasArrays.splice(ii, 1) + change = true + break + } + } + } + } + + // map arrays back to the hash-lookup (de-dupe while + // we're at it). + aliasArrays.forEach(function (aliasArray) { + aliasArray = aliasArray.filter(function (v, i, self) { + return self.indexOf(v) === i + }) + combined[aliasArray.pop()] = aliasArray + }) + + return combined +} + +function assign (defaults, configuration) { + var o = {} + configuration = configuration || {} + + Object.keys(defaults).forEach(function (k) { + o[k] = defaults[k] + }) + Object.keys(configuration).forEach(function (k) { + o[k] = configuration[k] + }) + + return o +} + +// this function should only be called when a count is given as an arg +// it is NOT called to set a default value +// thus we can start the count at 1 instead of 0 +function increment (orig) { + return orig !== undefined ? orig + 1 : 1 +} + +function Parser (args, opts) { + var result = parse(args.slice(), opts) + + return result.argv +} + +// parse arguments and return detailed +// meta information, aliases, etc. +Parser.detailed = function (args, opts) { + return parse(args.slice(), opts) +} + +module.exports = Parser diff --git a/WebServer/node_modules/yargs-parser/lib/tokenize-arg-string.js b/WebServer/node_modules/yargs-parser/lib/tokenize-arg-string.js new file mode 100644 index 000000000..569f61ad4 --- /dev/null +++ b/WebServer/node_modules/yargs-parser/lib/tokenize-arg-string.js @@ -0,0 +1,39 @@ +// take an un-split argv string and tokenize it. +module.exports = function (argString) { + if (Array.isArray(argString)) return argString + + argString = argString.trim() + + var i = 0 + var prevC = null + var c = null + var opening = null + var args = [] + + for (var ii = 0; ii < argString.length; ii++) { + prevC = c + c = argString.charAt(ii) + + // split on spaces unless we're in quotes. + if (c === ' ' && !opening) { + if (!(prevC === ' ')) { + i++ + } + continue + } + + // don't split the string if we're in matching + // opening or closing single and double quotes. + if (c === opening) { + if (!args[i]) args[i] = '' + opening = null + } else if ((c === "'" || c === '"') && !opening) { + opening = c + } + + if (!args[i]) args[i] = '' + args[i] += c + } + + return args +} diff --git a/WebServer/node_modules/yargs-parser/package.json b/WebServer/node_modules/yargs-parser/package.json new file mode 100644 index 000000000..80ac74ab5 --- /dev/null +++ b/WebServer/node_modules/yargs-parser/package.json @@ -0,0 +1,83 @@ +{ + "_args": [ + [ + "yargs-parser@13.0.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "yargs-parser@13.0.0", + "_id": "yargs-parser@13.0.0", + "_inBundle": false, + "_integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "_location": "/yargs-parser", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "yargs-parser@13.0.0", + "name": "yargs-parser", + "escapedName": "yargs-parser", + "rawSpec": "13.0.0", + "saveSpec": null, + "fetchSpec": "13.0.0" + }, + "_requiredBy": [ + "/mocha", + "/yargs" + ], + "_resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "_spec": "13.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/yargs/yargs-parser/issues" + }, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "description": "the mighty option parser used by yargs", + "devDependencies": { + "chai": "^4.2.0", + "coveralls": "^3.0.2", + "mocha": "^5.2.0", + "nyc": "^13.0.1", + "standard": "^12.0.1", + "standard-version": "^4.4.0" + }, + "engine": { + "node": ">=6" + }, + "files": [ + "lib", + "index.js" + ], + "homepage": "https://github.com/yargs/yargs-parser#readme", + "keywords": [ + "argument", + "parser", + "yargs", + "command", + "cli", + "parsing", + "option", + "args", + "argument" + ], + "license": "ISC", + "main": "index.js", + "name": "yargs-parser", + "repository": { + "url": "git+ssh://git@github.com/yargs/yargs-parser.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "posttest": "standard", + "release": "standard-version", + "test": "nyc mocha test/*.js" + }, + "version": "13.0.0" +} diff --git a/WebServer/node_modules/yargs-unparser/index.js b/WebServer/node_modules/yargs-unparser/index.js new file mode 100644 index 000000000..b774c31ed --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/index.js @@ -0,0 +1,130 @@ +'use strict'; + +const yargs = require('yargs/yargs'); +const flatten = require('flat'); +const castArray = require('lodash/castArray'); +const some = require('lodash/some'); +const isPlainObject = require('lodash/isPlainObject'); +const camelCase = require('lodash/camelCase'); +const kebabCase = require('lodash/kebabCase'); +const omitBy = require('lodash/omitBy'); + +function isAlias(key, alias) { + return some(alias, (aliases) => castArray(aliases).indexOf(key) !== -1); +} + +function hasDefaultValue(key, value, defaults) { + return value === defaults[key]; +} + +function isCamelCased(key, argv) { + return /[A-Z]/.test(key) && camelCase(key) === key && // Is it camel case? + argv[kebabCase(key)] != null; // Is the standard version defined? +} + +function keyToFlag(key) { + return key.length === 1 ? `-${key}` : `--${key}`; +} + +function unparseOption(key, value, unparsed) { + if (typeof value === 'string') { + unparsed.push(keyToFlag(key), value); + } else if (value === true) { + unparsed.push(keyToFlag(key)); + } else if (value === false) { + unparsed.push(`--no-${key}`); + } else if (Array.isArray(value)) { + value.forEach((item) => unparseOption(key, item, unparsed)); + } else if (isPlainObject(value)) { + const flattened = flatten(value, { safe: true }); + + for (const flattenedKey in flattened) { + unparseOption(`${key}.${flattenedKey}`, flattened[flattenedKey], unparsed); + } + // Fallback case (numbers and other types) + } else if (value != null) { + unparsed.push(keyToFlag(key), `${value}`); + } +} + +function unparsePositional(argv, options, unparsed) { + const knownPositional = []; + + // Unparse command if set, collecting all known positional arguments + // e.g.: build + if (options.command) { + const { 0: cmd, index } = options.command.match(/[^<[]*/); + const { demanded, optional } = yargs() + .getCommandInstance() + .parseCommand(`foo ${options.command.substr(index + cmd.length)}`); + + // Push command (can be a deep command) + unparsed.push(...cmd.trim().split(/\s+/)); + + // Push positional arguments + [...demanded, ...optional].forEach(({ cmd: cmds, variadic }) => { + knownPositional.push(...cmds); + + const cmd = cmds[0]; + const args = (variadic ? argv[cmd] || [] : [argv[cmd]]) + .filter((arg) => arg != null) + .map((arg) => `${arg}`); + + unparsed.push(...args); + }); + } + + // Unparse unkown positional arguments + argv._ && unparsed.push(...argv._.slice(knownPositional.length)); + + return knownPositional; +} + +function unparseOptions(argv, options, knownPositional, unparsed) { + const optionsArgv = omitBy(argv, (value, key) => + // Remove positional arguments + knownPositional.includes(key) || + // Remove special _, -- and $0 + ['_', '--', '$0'].includes(key) || + // Remove aliases + isAlias(key, options.alias) || + // Remove default values + hasDefaultValue(key, value, options.default) || + // Remove camel-cased + isCamelCased(key, argv)); + + for (const key in optionsArgv) { + unparseOption(key, optionsArgv[key], unparsed); + } +} + +function unparseEndOfOptions(argv, options, unparsed) { + // Unparse ending (--) arguments if set + argv['--'] && unparsed.push('--', ...argv['--']); +} + +// ------------------------------------------------------------ + +function unparser(argv, options) { + options = Object.assign({ + alias: {}, + default: {}, + command: null, + }, options); + + const unparsed = []; + + // Unparse known & unknown positional arguments (foo [rest...]) + // All known positional will be returned so that they are not added as flags + const knownPositional = unparsePositional(argv, options, unparsed); + + // Unparse option arguments (--foo hello --bar hi) + unparseOptions(argv, options, knownPositional, unparsed); + + // Unparse "end-of-options" arguments (stuff after " -- ") + unparseEndOfOptions(argv, options, unparsed); + + return unparsed; +} + +module.exports = unparser; diff --git a/WebServer/node_modules/yargs-unparser/node_modules/get-caller-file/index.js b/WebServer/node_modules/yargs-unparser/node_modules/get-caller-file/index.js new file mode 100644 index 000000000..03e7dfc39 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/get-caller-file/index.js @@ -0,0 +1,20 @@ +'use strict'; + +// Call this function in a another function to find out the file from +// which that function was called from. (Inspects the v8 stack trace) +// +// Inspired by http://stackoverflow.com/questions/13227489 + +module.exports = function getCallerFile(_position) { + var oldPrepareStackTrace = Error.prepareStackTrace; + Error.prepareStackTrace = function(err, stack) { return stack; }; + var stack = new Error().stack; + Error.prepareStackTrace = oldPrepareStackTrace; + + var position = _position ? _position : 2; + + // stack[0] holds this file + // stack[1] holds where this function was called + // stack[2] holds the file we're interested in + return stack[position] ? stack[position].getFileName() : undefined; +}; diff --git a/WebServer/node_modules/yargs-unparser/node_modules/get-caller-file/package.json b/WebServer/node_modules/yargs-unparser/node_modules/get-caller-file/package.json new file mode 100644 index 000000000..6ad5da83a --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/get-caller-file/package.json @@ -0,0 +1,61 @@ +{ + "_args": [ + [ + "get-caller-file@1.0.3", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "get-caller-file@1.0.3", + "_id": "get-caller-file@1.0.3", + "_inBundle": false, + "_integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "_location": "/yargs-unparser/get-caller-file", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "get-caller-file@1.0.3", + "name": "get-caller-file", + "escapedName": "get-caller-file", + "rawSpec": "1.0.3", + "saveSpec": null, + "fetchSpec": "1.0.3" + }, + "_requiredBy": [ + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "_spec": "1.0.3", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Stefan Penner" + }, + "bugs": { + "url": "https://github.com/stefanpenner/get-caller-file/issues" + }, + "description": "[![Build Status](https://travis-ci.org/stefanpenner/get-caller-file.svg?branch=master)](https://travis-ci.org/stefanpenner/get-caller-file) [![Build status](https://ci.appveyor.com/api/projects/status/ol2q94g1932cy14a/branch/master?svg=true)](https://ci.appveyor.com/project/embercli/get-caller-file/branch/master)", + "devDependencies": { + "chai": "^4.1.2", + "ensure-posix-path": "^1.0.1", + "mocha": "^5.2.0" + }, + "directories": { + "test": "tests" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/stefanpenner/get-caller-file#readme", + "license": "ISC", + "main": "index.js", + "name": "get-caller-file", + "repository": { + "type": "git", + "url": "git+https://github.com/stefanpenner/get-caller-file.git" + }, + "scripts": { + "test": "mocha test", + "test:debug": "mocha test" + }, + "version": "1.0.3" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/index.js b/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/index.js new file mode 100644 index 000000000..dca7f0cc1 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/index.js @@ -0,0 +1,18 @@ +module.exports = function (_require) { + _require = _require || require + var main = _require.main + if (main && isIISNode(main)) return handleIISNode(main) + else return main ? main.filename : process.cwd() +} + +function isIISNode (main) { + return /\\iisnode\\/.test(main.filename) +} + +function handleIISNode (main) { + if (!main.children.length) { + return main.filename + } else { + return main.children[0].filename + } +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/package.json b/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/package.json new file mode 100644 index 000000000..d4b500fd5 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/package.json @@ -0,0 +1,61 @@ +{ + "_args": [ + [ + "require-main-filename@1.0.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "require-main-filename@1.0.1", + "_id": "require-main-filename@1.0.1", + "_inBundle": false, + "_integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "_location": "/yargs-unparser/require-main-filename", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "require-main-filename@1.0.1", + "name": "require-main-filename", + "escapedName": "require-main-filename", + "rawSpec": "1.0.1", + "saveSpec": null, + "fetchSpec": "1.0.1" + }, + "_requiredBy": [ + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "_spec": "1.0.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/yargs/require-main-filename/issues" + }, + "description": "shim for require.main.filename() that works in as many environments as possible", + "devDependencies": { + "chai": "^3.5.0", + "standard": "^6.0.5", + "tap": "^5.2.0" + }, + "homepage": "https://github.com/yargs/require-main-filename#readme", + "keywords": [ + "require", + "shim", + "iisnode" + ], + "license": "ISC", + "main": "index.js", + "name": "require-main-filename", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/yargs/require-main-filename.git" + }, + "scripts": { + "pretest": "standard", + "test": "tap --coverage test.js" + }, + "version": "1.0.1" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/test.js b/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/test.js new file mode 100644 index 000000000..d89e7dcba --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/require-main-filename/test.js @@ -0,0 +1,36 @@ +/* global describe, it */ + +var requireMainFilename = require('./') + +require('tap').mochaGlobals() +require('chai').should() + +describe('require-main-filename', function () { + it('returns require.main.filename in normal circumstances', function () { + requireMainFilename().should.match(/test\.js/) + }) + + it('should use children[0].filename when running on iisnode', function () { + var main = { + filename: 'D:\\Program Files (x86)\\iisnode\\interceptor.js', + children: [ {filename: 'D:\\home\\site\\wwwroot\\server.js'} ] + } + requireMainFilename({ + main: main + }).should.match(/server\.js/) + }) + + it('should not use children[0] if no children exist', function () { + var main = { + filename: 'D:\\Program Files (x86)\\iisnode\\interceptor.js', + children: [] + } + requireMainFilename({ + main: main + }).should.match(/interceptor\.js/) + }) + + it('should default to process.cwd() if require.main is undefined', function () { + requireMainFilename({}).should.match(/require-main-filename/) + }) +}) diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/index.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/index.js new file mode 100644 index 000000000..69fa72dc1 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/index.js @@ -0,0 +1,866 @@ +var camelCase = require('camelcase') +var decamelize = require('decamelize') +var path = require('path') +var tokenizeArgString = require('./lib/tokenize-arg-string') +var util = require('util') + +function parse (args, opts) { + if (!opts) opts = {} + // allow a string argument to be passed in rather + // than an argv array. + args = tokenizeArgString(args) + // aliases might have transitive relationships, normalize this. + var aliases = combineAliases(opts.alias || {}) + var configuration = assign({ + 'short-option-groups': true, + 'camel-case-expansion': true, + 'dot-notation': true, + 'parse-numbers': true, + 'boolean-negation': true, + 'negation-prefix': 'no-', + 'duplicate-arguments-array': true, + 'flatten-duplicate-arrays': true, + 'populate--': false, + 'combine-arrays': false, + 'set-placeholder-key': false, + 'halt-at-non-option': false + }, opts.configuration) + var defaults = opts.default || {} + var configObjects = opts.configObjects || [] + var envPrefix = opts.envPrefix + var notFlagsOption = configuration['populate--'] + var notFlagsArgv = notFlagsOption ? '--' : '_' + var newAliases = {} + // allow a i18n handler to be passed in, default to a fake one (util.format). + var __ = opts.__ || function (str) { + return util.format.apply(util, Array.prototype.slice.call(arguments)) + } + var error = null + var flags = { + aliases: {}, + arrays: {}, + bools: {}, + strings: {}, + numbers: {}, + counts: {}, + normalize: {}, + configs: {}, + defaulted: {}, + nargs: {}, + coercions: {}, + keys: [] + } + var negative = /^-[0-9]+(\.[0-9]+)?/ + var negatedBoolean = new RegExp('^--' + configuration['negation-prefix'] + '(.+)') + + ;[].concat(opts.array).filter(Boolean).forEach(function (opt) { + var key = opt.key || opt + + // assign to flags[bools|strings|numbers] + const assignment = Object.keys(opt).map(function (key) { + return ({ + boolean: 'bools', + string: 'strings', + number: 'numbers' + })[key] + }).filter(Boolean).pop() + + // assign key to be coerced + if (assignment) { + flags[assignment][key] = true + } + + flags.arrays[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.boolean).filter(Boolean).forEach(function (key) { + flags.bools[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.number).filter(Boolean).forEach(function (key) { + flags.numbers[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.count).filter(Boolean).forEach(function (key) { + flags.counts[key] = true + flags.keys.push(key) + }) + + ;[].concat(opts.normalize).filter(Boolean).forEach(function (key) { + flags.normalize[key] = true + flags.keys.push(key) + }) + + Object.keys(opts.narg || {}).forEach(function (k) { + flags.nargs[k] = opts.narg[k] + flags.keys.push(k) + }) + + Object.keys(opts.coerce || {}).forEach(function (k) { + flags.coercions[k] = opts.coerce[k] + flags.keys.push(k) + }) + + if (Array.isArray(opts.config) || typeof opts.config === 'string') { + ;[].concat(opts.config).filter(Boolean).forEach(function (key) { + flags.configs[key] = true + }) + } else { + Object.keys(opts.config || {}).forEach(function (k) { + flags.configs[k] = opts.config[k] + }) + } + + // create a lookup table that takes into account all + // combinations of aliases: {f: ['foo'], foo: ['f']} + extendAliases(opts.key, aliases, opts.default, flags.arrays) + + // apply default values to all aliases. + Object.keys(defaults).forEach(function (key) { + (flags.aliases[key] || []).forEach(function (alias) { + defaults[alias] = defaults[key] + }) + }) + + var argv = { _: [] } + + Object.keys(flags.bools).forEach(function (key) { + if (Object.prototype.hasOwnProperty.call(defaults, key)) { + setArg(key, defaults[key]) + setDefaulted(key) + } + }) + + var notFlags = [] + + for (var i = 0; i < args.length; i++) { + var arg = args[i] + var broken + var key + var letters + var m + var next + var value + + // -- separated by = + if (arg.match(/^--.+=/) || ( + !configuration['short-option-groups'] && arg.match(/^-.+=/) + )) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + m = arg.match(/^--?([^=]+)=([\s\S]*)$/) + + // nargs format = '--f=monkey washing cat' + if (checkAllAliases(m[1], flags.nargs)) { + args.splice(i + 1, 0, m[2]) + i = eatNargs(i, m[1], args) + // arrays format = '--f=a b c' + } else if (checkAllAliases(m[1], flags.arrays) && args.length > i + 1) { + args.splice(i + 1, 0, m[2]) + i = eatArray(i, m[1], args) + } else { + setArg(m[1], m[2]) + } + } else if (arg.match(negatedBoolean) && configuration['boolean-negation']) { + key = arg.match(negatedBoolean)[1] + setArg(key, false) + + // -- seperated by space. + } else if (arg.match(/^--.+/) || ( + !configuration['short-option-groups'] && arg.match(/^-.+/) + )) { + key = arg.match(/^--?(.+)/)[1] + + // nargs format = '--foo a b c' + if (checkAllAliases(key, flags.nargs)) { + i = eatNargs(i, key, args) + // array format = '--foo a b c' + } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) { + i = eatArray(i, key, args) + } else { + next = args[i + 1] + + if (next !== undefined && (!next.match(/^-/) || + next.match(negative)) && + !checkAllAliases(key, flags.bools) && + !checkAllAliases(key, flags.counts)) { + setArg(key, next) + i++ + } else if (/^(true|false)$/.test(next)) { + setArg(key, next) + i++ + } else { + setArg(key, defaultForType(guessType(key, flags))) + } + } + + // dot-notation flag seperated by '='. + } else if (arg.match(/^-.\..+=/)) { + m = arg.match(/^-([^=]+)=([\s\S]*)$/) + setArg(m[1], m[2]) + + // dot-notation flag seperated by space. + } else if (arg.match(/^-.\..+/)) { + next = args[i + 1] + key = arg.match(/^-(.\..+)/)[1] + + if (next !== undefined && !next.match(/^-/) && + !checkAllAliases(key, flags.bools) && + !checkAllAliases(key, flags.counts)) { + setArg(key, next) + i++ + } else { + setArg(key, defaultForType(guessType(key, flags))) + } + } else if (arg.match(/^-[^-]+/) && !arg.match(negative)) { + letters = arg.slice(1, -1).split('') + broken = false + + for (var j = 0; j < letters.length; j++) { + next = arg.slice(j + 2) + + if (letters[j + 1] && letters[j + 1] === '=') { + value = arg.slice(j + 3) + key = letters[j] + + // nargs format = '-f=monkey washing cat' + if (checkAllAliases(key, flags.nargs)) { + args.splice(i + 1, 0, value) + i = eatNargs(i, key, args) + // array format = '-f=a b c' + } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) { + args.splice(i + 1, 0, value) + i = eatArray(i, key, args) + } else { + setArg(key, value) + } + + broken = true + break + } + + if (next === '-') { + setArg(letters[j], next) + continue + } + + // current letter is an alphabetic character and next value is a number + if (/[A-Za-z]/.test(letters[j]) && + /^-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next) + broken = true + break + } + + if (letters[j + 1] && letters[j + 1].match(/\W/)) { + setArg(letters[j], next) + broken = true + break + } else { + setArg(letters[j], defaultForType(guessType(letters[j], flags))) + } + } + + key = arg.slice(-1)[0] + + if (!broken && key !== '-') { + // nargs format = '-f a b c' + if (checkAllAliases(key, flags.nargs)) { + i = eatNargs(i, key, args) + // array format = '-f a b c' + } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) { + i = eatArray(i, key, args) + } else { + next = args[i + 1] + + if (next !== undefined && (!/^(-|--)[^-]/.test(next) || + next.match(negative)) && + !checkAllAliases(key, flags.bools) && + !checkAllAliases(key, flags.counts)) { + setArg(key, next) + i++ + } else if (/^(true|false)$/.test(next)) { + setArg(key, next) + i++ + } else { + setArg(key, defaultForType(guessType(key, flags))) + } + } + } + } else if (arg === '--') { + notFlags = args.slice(i + 1) + break + } else if (configuration['halt-at-non-option']) { + notFlags = args.slice(i) + break + } else { + argv._.push(maybeCoerceNumber('_', arg)) + } + } + + // order of precedence: + // 1. command line arg + // 2. value from env var + // 3. value from config file + // 4. value from config objects + // 5. configured default value + applyEnvVars(argv, true) // special case: check env vars that point to config file + applyEnvVars(argv, false) + setConfig(argv) + setConfigObjects() + applyDefaultsAndAliases(argv, flags.aliases, defaults) + applyCoercions(argv) + if (configuration['set-placeholder-key']) setPlaceholderKeys(argv) + + // for any counts either not in args or without an explicit default, set to 0 + Object.keys(flags.counts).forEach(function (key) { + if (!hasKey(argv, key.split('.'))) setArg(key, 0) + }) + + // '--' defaults to undefined. + if (notFlagsOption && notFlags.length) argv[notFlagsArgv] = [] + notFlags.forEach(function (key) { + argv[notFlagsArgv].push(key) + }) + + // how many arguments should we consume, based + // on the nargs option? + function eatNargs (i, key, args) { + var ii + const toEat = checkAllAliases(key, flags.nargs) + + // nargs will not consume flag arguments, e.g., -abc, --foo, + // and terminates when one is observed. + var available = 0 + for (ii = i + 1; ii < args.length; ii++) { + if (!args[ii].match(/^-[^0-9]/)) available++ + else break + } + + if (available < toEat) error = Error(__('Not enough arguments following: %s', key)) + + const consumed = Math.min(available, toEat) + for (ii = i + 1; ii < (consumed + i + 1); ii++) { + setArg(key, args[ii]) + } + + return (i + consumed) + } + + // if an option is an array, eat all non-hyphenated arguments + // following it... YUM! + // e.g., --foo apple banana cat becomes ["apple", "banana", "cat"] + function eatArray (i, key, args) { + var start = i + 1 + var argsToSet = [] + var multipleArrayFlag = i > 0 + for (var ii = i + 1; ii < args.length; ii++) { + if (/^-/.test(args[ii]) && !negative.test(args[ii])) { + if (ii === start) { + setArg(key, defaultForType('array')) + } + multipleArrayFlag = true + break + } + i = ii + argsToSet.push(args[ii]) + } + if (multipleArrayFlag) { + setArg(key, argsToSet.map(function (arg) { + return processValue(key, arg) + })) + } else { + argsToSet.forEach(function (arg) { + setArg(key, arg) + }) + } + + return i + } + + function setArg (key, val) { + unsetDefaulted(key) + + if (/-/.test(key) && configuration['camel-case-expansion']) { + var alias = key.split('.').map(function (prop) { + return camelCase(prop) + }).join('.') + addNewAlias(key, alias) + } + + var value = processValue(key, val) + + var splitKey = key.split('.') + setKey(argv, splitKey, value) + + // handle populating aliases of the full key + if (flags.aliases[key]) { + flags.aliases[key].forEach(function (x) { + x = x.split('.') + setKey(argv, x, value) + }) + } + + // handle populating aliases of the first element of the dot-notation key + if (splitKey.length > 1 && configuration['dot-notation']) { + ;(flags.aliases[splitKey[0]] || []).forEach(function (x) { + x = x.split('.') + + // expand alias with nested objects in key + var a = [].concat(splitKey) + a.shift() // nuke the old key. + x = x.concat(a) + + setKey(argv, x, value) + }) + } + + // Set normalize getter and setter when key is in 'normalize' but isn't an array + if (checkAllAliases(key, flags.normalize) && !checkAllAliases(key, flags.arrays)) { + var keys = [key].concat(flags.aliases[key] || []) + keys.forEach(function (key) { + argv.__defineSetter__(key, function (v) { + val = path.normalize(v) + }) + + argv.__defineGetter__(key, function () { + return typeof val === 'string' ? path.normalize(val) : val + }) + }) + } + } + + function addNewAlias (key, alias) { + if (!(flags.aliases[key] && flags.aliases[key].length)) { + flags.aliases[key] = [alias] + newAliases[alias] = true + } + if (!(flags.aliases[alias] && flags.aliases[alias].length)) { + addNewAlias(alias, key) + } + } + + function processValue (key, val) { + // handle parsing boolean arguments --foo=true --bar false. + if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) { + if (typeof val === 'string') val = val === 'true' + } + + var value = maybeCoerceNumber(key, val) + + // increment a count given as arg (either no value or value parsed as boolean) + if (checkAllAliases(key, flags.counts) && (isUndefined(value) || typeof value === 'boolean')) { + value = increment + } + + // Set normalized value when key is in 'normalize' and in 'arrays' + if (checkAllAliases(key, flags.normalize) && checkAllAliases(key, flags.arrays)) { + if (Array.isArray(val)) value = val.map(path.normalize) + else value = path.normalize(val) + } + return value + } + + function maybeCoerceNumber (key, value) { + if (!checkAllAliases(key, flags.strings) && !checkAllAliases(key, flags.coercions)) { + const shouldCoerceNumber = isNumber(value) && configuration['parse-numbers'] && ( + Number.isSafeInteger(Math.floor(value)) + ) + if (shouldCoerceNumber || (!isUndefined(value) && checkAllAliases(key, flags.numbers))) value = Number(value) + } + return value + } + + // set args from config.json file, this should be + // applied last so that defaults can be applied. + function setConfig (argv) { + var configLookup = {} + + // expand defaults/aliases, in-case any happen to reference + // the config.json file. + applyDefaultsAndAliases(configLookup, flags.aliases, defaults) + + Object.keys(flags.configs).forEach(function (configKey) { + var configPath = argv[configKey] || configLookup[configKey] + if (configPath) { + try { + var config = null + var resolvedConfigPath = path.resolve(process.cwd(), configPath) + + if (typeof flags.configs[configKey] === 'function') { + try { + config = flags.configs[configKey](resolvedConfigPath) + } catch (e) { + config = e + } + if (config instanceof Error) { + error = config + return + } + } else { + config = require(resolvedConfigPath) + } + + setConfigObject(config) + } catch (ex) { + if (argv[configKey]) error = Error(__('Invalid JSON config file: %s', configPath)) + } + } + }) + } + + // set args from config object. + // it recursively checks nested objects. + function setConfigObject (config, prev) { + Object.keys(config).forEach(function (key) { + var value = config[key] + var fullKey = prev ? prev + '.' + key : key + + // if the value is an inner object and we have dot-notation + // enabled, treat inner objects in config the same as + // heavily nested dot notations (foo.bar.apple). + if (typeof value === 'object' && value !== null && !Array.isArray(value) && configuration['dot-notation']) { + // if the value is an object but not an array, check nested object + setConfigObject(value, fullKey) + } else { + // setting arguments via CLI takes precedence over + // values within the config file. + if (!hasKey(argv, fullKey.split('.')) || (flags.defaulted[fullKey]) || (flags.arrays[fullKey] && configuration['combine-arrays'])) { + setArg(fullKey, value) + } + } + }) + } + + // set all config objects passed in opts + function setConfigObjects () { + if (typeof configObjects === 'undefined') return + configObjects.forEach(function (configObject) { + setConfigObject(configObject) + }) + } + + function applyEnvVars (argv, configOnly) { + if (typeof envPrefix === 'undefined') return + + var prefix = typeof envPrefix === 'string' ? envPrefix : '' + Object.keys(process.env).forEach(function (envVar) { + if (prefix === '' || envVar.lastIndexOf(prefix, 0) === 0) { + // get array of nested keys and convert them to camel case + var keys = envVar.split('__').map(function (key, i) { + if (i === 0) { + key = key.substring(prefix.length) + } + return camelCase(key) + }) + + if (((configOnly && flags.configs[keys.join('.')]) || !configOnly) && (!hasKey(argv, keys) || flags.defaulted[keys.join('.')])) { + setArg(keys.join('.'), process.env[envVar]) + } + } + }) + } + + function applyCoercions (argv) { + var coerce + var applied = {} + Object.keys(argv).forEach(function (key) { + if (!applied.hasOwnProperty(key)) { // If we haven't already coerced this option via one of its aliases + coerce = checkAllAliases(key, flags.coercions) + if (typeof coerce === 'function') { + try { + var value = coerce(argv[key]) + ;([].concat(flags.aliases[key] || [], key)).forEach(ali => { + applied[ali] = argv[ali] = value + }) + } catch (err) { + error = err + } + } + } + }) + } + + function setPlaceholderKeys (argv) { + flags.keys.forEach((key) => { + // don't set placeholder keys for dot notation options 'foo.bar'. + if (~key.indexOf('.')) return + if (typeof argv[key] === 'undefined') argv[key] = undefined + }) + return argv + } + + function applyDefaultsAndAliases (obj, aliases, defaults) { + Object.keys(defaults).forEach(function (key) { + if (!hasKey(obj, key.split('.'))) { + setKey(obj, key.split('.'), defaults[key]) + + ;(aliases[key] || []).forEach(function (x) { + if (hasKey(obj, x.split('.'))) return + setKey(obj, x.split('.'), defaults[key]) + }) + } + }) + } + + function hasKey (obj, keys) { + var o = obj + + if (!configuration['dot-notation']) keys = [keys.join('.')] + + keys.slice(0, -1).forEach(function (key) { + o = (o[key] || {}) + }) + + var key = keys[keys.length - 1] + + if (typeof o !== 'object') return false + else return key in o + } + + function setKey (obj, keys, value) { + var o = obj + + if (!configuration['dot-notation']) keys = [keys.join('.')] + + keys.slice(0, -1).forEach(function (key, index) { + if (typeof o === 'object' && o[key] === undefined) { + o[key] = {} + } + + if (typeof o[key] !== 'object' || Array.isArray(o[key])) { + // ensure that o[key] is an array, and that the last item is an empty object. + if (Array.isArray(o[key])) { + o[key].push({}) + } else { + o[key] = [o[key], {}] + } + + // we want to update the empty object at the end of the o[key] array, so set o to that object + o = o[key][o[key].length - 1] + } else { + o = o[key] + } + }) + + var key = keys[keys.length - 1] + + var isTypeArray = checkAllAliases(keys.join('.'), flags.arrays) + var isValueArray = Array.isArray(value) + var duplicate = configuration['duplicate-arguments-array'] + + if (value === increment) { + o[key] = increment(o[key]) + } else if (Array.isArray(o[key])) { + if (duplicate && isTypeArray && isValueArray) { + o[key] = configuration['flatten-duplicate-arrays'] ? o[key].concat(value) : (Array.isArray(o[key][0]) ? o[key] : [o[key]]).concat([value]) + } else if (!duplicate && Boolean(isTypeArray) === Boolean(isValueArray)) { + o[key] = value + } else { + o[key] = o[key].concat([value]) + } + } else if (o[key] === undefined && isTypeArray) { + o[key] = isValueArray ? value : [value] + } else if (duplicate && !(o[key] === undefined || checkAllAliases(key, flags.bools) || checkAllAliases(keys.join('.'), flags.bools) || checkAllAliases(key, flags.counts))) { + o[key] = [ o[key], value ] + } else { + o[key] = value + } + } + + // extend the aliases list with inferred aliases. + function extendAliases () { + Array.prototype.slice.call(arguments).forEach(function (obj) { + Object.keys(obj || {}).forEach(function (key) { + // short-circuit if we've already added a key + // to the aliases array, for example it might + // exist in both 'opts.default' and 'opts.key'. + if (flags.aliases[key]) return + + flags.aliases[key] = [].concat(aliases[key] || []) + // For "--option-name", also set argv.optionName + flags.aliases[key].concat(key).forEach(function (x) { + if (/-/.test(x) && configuration['camel-case-expansion']) { + var c = camelCase(x) + if (c !== key && flags.aliases[key].indexOf(c) === -1) { + flags.aliases[key].push(c) + newAliases[c] = true + } + } + }) + // For "--optionName", also set argv['option-name'] + flags.aliases[key].concat(key).forEach(function (x) { + if (x.length > 1 && /[A-Z]/.test(x) && configuration['camel-case-expansion']) { + var c = decamelize(x, '-') + if (c !== key && flags.aliases[key].indexOf(c) === -1) { + flags.aliases[key].push(c) + newAliases[c] = true + } + } + }) + flags.aliases[key].forEach(function (x) { + flags.aliases[x] = [key].concat(flags.aliases[key].filter(function (y) { + return x !== y + })) + }) + }) + }) + } + + // check if a flag is set for any of a key's aliases. + function checkAllAliases (key, flag) { + var isSet = false + var toCheck = [].concat(flags.aliases[key] || [], key) + + toCheck.forEach(function (key) { + if (flag[key]) isSet = flag[key] + }) + + return isSet + } + + function setDefaulted (key) { + [].concat(flags.aliases[key] || [], key).forEach(function (k) { + flags.defaulted[k] = true + }) + } + + function unsetDefaulted (key) { + [].concat(flags.aliases[key] || [], key).forEach(function (k) { + delete flags.defaulted[k] + }) + } + + // return a default value, given the type of a flag., + // e.g., key of type 'string' will default to '', rather than 'true'. + function defaultForType (type) { + var def = { + boolean: true, + string: '', + number: undefined, + array: [] + } + + return def[type] + } + + // given a flag, enforce a default type. + function guessType (key, flags) { + var type = 'boolean' + + if (checkAllAliases(key, flags.strings)) type = 'string' + else if (checkAllAliases(key, flags.numbers)) type = 'number' + else if (checkAllAliases(key, flags.arrays)) type = 'array' + + return type + } + + function isNumber (x) { + if (typeof x === 'number') return true + if (/^0x[0-9a-f]+$/i.test(x)) return true + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x) + } + + function isUndefined (num) { + return num === undefined + } + + return { + argv: argv, + error: error, + aliases: flags.aliases, + newAliases: newAliases, + configuration: configuration + } +} + +// if any aliases reference each other, we should +// merge them together. +function combineAliases (aliases) { + var aliasArrays = [] + var change = true + var combined = {} + + // turn alias lookup hash {key: ['alias1', 'alias2']} into + // a simple array ['key', 'alias1', 'alias2'] + Object.keys(aliases).forEach(function (key) { + aliasArrays.push( + [].concat(aliases[key], key) + ) + }) + + // combine arrays until zero changes are + // made in an iteration. + while (change) { + change = false + for (var i = 0; i < aliasArrays.length; i++) { + for (var ii = i + 1; ii < aliasArrays.length; ii++) { + var intersect = aliasArrays[i].filter(function (v) { + return aliasArrays[ii].indexOf(v) !== -1 + }) + + if (intersect.length) { + aliasArrays[i] = aliasArrays[i].concat(aliasArrays[ii]) + aliasArrays.splice(ii, 1) + change = true + break + } + } + } + } + + // map arrays back to the hash-lookup (de-dupe while + // we're at it). + aliasArrays.forEach(function (aliasArray) { + aliasArray = aliasArray.filter(function (v, i, self) { + return self.indexOf(v) === i + }) + combined[aliasArray.pop()] = aliasArray + }) + + return combined +} + +function assign (defaults, configuration) { + var o = {} + configuration = configuration || {} + + Object.keys(defaults).forEach(function (k) { + o[k] = defaults[k] + }) + Object.keys(configuration).forEach(function (k) { + o[k] = configuration[k] + }) + + return o +} + +// this function should only be called when a count is given as an arg +// it is NOT called to set a default value +// thus we can start the count at 1 instead of 0 +function increment (orig) { + return orig !== undefined ? orig + 1 : 1 +} + +function Parser (args, opts) { + var result = parse(args.slice(), opts) + + return result.argv +} + +// parse arguments and return detailed +// meta information, aliases, etc. +Parser.detailed = function (args, opts) { + return parse(args.slice(), opts) +} + +module.exports = Parser diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/lib/tokenize-arg-string.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/lib/tokenize-arg-string.js new file mode 100644 index 000000000..73e14cd64 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/lib/tokenize-arg-string.js @@ -0,0 +1,41 @@ +// take an un-split argv string and tokenize it. +module.exports = function (argString) { + if (Array.isArray(argString)) return argString + + argString = argString.trim() + + var i = 0 + var prevC = null + var c = null + var opening = null + var args = [] + + for (var ii = 0; ii < argString.length; ii++) { + prevC = c + c = argString.charAt(ii) + + // split on spaces unless we're in quotes. + if (c === ' ' && !opening) { + if (!(prevC === ' ')) { + i++ + } + continue + } + + // don't split the string if we're in matching + // opening or closing single and double quotes. + if (c === opening) { + if (!args[i]) args[i] = '' + opening = null + continue + } else if ((c === "'" || c === '"') && !opening) { + opening = c + continue + } + + if (!args[i]) args[i] = '' + args[i] += c + } + + return args +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/package.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/package.json new file mode 100644 index 000000000..107c0b587 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs-parser/package.json @@ -0,0 +1,82 @@ +{ + "_args": [ + [ + "yargs-parser@11.1.1", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "yargs-parser@11.1.1", + "_id": "yargs-parser@11.1.1", + "_inBundle": false, + "_integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "_location": "/yargs-unparser/yargs-parser", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "yargs-parser@11.1.1", + "name": "yargs-parser", + "escapedName": "yargs-parser", + "rawSpec": "11.1.1", + "saveSpec": null, + "fetchSpec": "11.1.1" + }, + "_requiredBy": [ + "/yargs-unparser/yargs" + ], + "_resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "_spec": "11.1.1", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Ben Coe", + "email": "ben@npmjs.com" + }, + "bugs": { + "url": "https://github.com/yargs/yargs-parser/issues" + }, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "description": "the mighty option parser used by yargs", + "devDependencies": { + "chai": "^4.2.0", + "coveralls": "^3.0.2", + "mocha": "^5.2.0", + "nyc": "^13.0.1", + "standard": "^12.0.1", + "standard-version": "^4.4.0" + }, + "engine": { + "node": ">=6" + }, + "files": [ + "lib", + "index.js" + ], + "homepage": "https://github.com/yargs/yargs-parser#readme", + "keywords": [ + "argument", + "parser", + "yargs", + "command", + "cli", + "parsing", + "option", + "args", + "argument" + ], + "license": "ISC", + "main": "index.js", + "name": "yargs-parser", + "repository": { + "url": "git+ssh://git@github.com/yargs/yargs-parser.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "posttest": "standard", + "release": "standard-version", + "test": "nyc mocha test/*.js" + }, + "version": "11.1.1" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/index.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/index.js new file mode 100644 index 000000000..dfed54bc5 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/index.js @@ -0,0 +1,32 @@ +'use strict' +// classic singleton yargs API, to use yargs +// without running as a singleton do: +// require('yargs/yargs')(process.argv.slice(2)) +const yargs = require('./yargs') + +Argv(process.argv.slice(2)) + +module.exports = Argv + +function Argv (processArgs, cwd) { + const argv = yargs(processArgs, cwd, require) + singletonify(argv) + return argv +} + +/* Hack an instance of Argv with process.argv into Argv + so people can do + require('yargs')(['--beeble=1','-z','zizzle']).argv + to parse a list of args and + require('yargs').argv + to get a parsed version of process.argv. +*/ +function singletonify (inst) { + Object.keys(inst).forEach((key) => { + if (key === 'argv') { + Argv.__defineGetter__(key, inst.__lookupGetter__(key)) + } else { + Argv[key] = typeof inst[key] === 'function' ? inst[key].bind(inst) : inst[key] + } + }) +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/apply-extends.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/apply-extends.js new file mode 100644 index 000000000..1436b6508 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/apply-extends.js @@ -0,0 +1,53 @@ + +'use strict' +const fs = require('fs') +const path = require('path') +const YError = require('./yerror') + +let previouslyVisitedConfigs = [] + +function checkForCircularExtends (cfgPath) { + if (previouslyVisitedConfigs.indexOf(cfgPath) > -1) { + throw new YError(`Circular extended configurations: '${cfgPath}'.`) + } +} + +function getPathToDefaultConfig (cwd, pathToExtend) { + return path.resolve(cwd, pathToExtend) +} + +function applyExtends (config, cwd) { + let defaultConfig = {} + + if (config.hasOwnProperty('extends')) { + if (typeof config.extends !== 'string') return defaultConfig + const isPath = /\.json|\..*rc$/.test(config.extends) + let pathToDefault = null + if (!isPath) { + try { + pathToDefault = require.resolve(config.extends) + } catch (err) { + // most likely this simply isn't a module. + } + } else { + pathToDefault = getPathToDefaultConfig(cwd, config.extends) + } + // maybe the module uses key for some other reason, + // err on side of caution. + if (!pathToDefault && !isPath) return config + + checkForCircularExtends(pathToDefault) + + previouslyVisitedConfigs.push(pathToDefault) + + defaultConfig = isPath ? JSON.parse(fs.readFileSync(pathToDefault, 'utf8')) : require(config.extends) + delete config.extends + defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault)) + } + + previouslyVisitedConfigs = [] + + return Object.assign({}, defaultConfig, config) +} + +module.exports = applyExtends diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/argsert.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/argsert.js new file mode 100644 index 000000000..ed1d59871 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/argsert.js @@ -0,0 +1,66 @@ +'use strict' +const command = require('./command')() +const YError = require('./yerror') + +const positionName = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth'] + +module.exports = function argsert (expected, callerArguments, length) { + // TODO: should this eventually raise an exception. + try { + // preface the argument description with "cmd", so + // that we can run it through yargs' command parser. + let position = 0 + let parsed = {demanded: [], optional: []} + if (typeof expected === 'object') { + length = callerArguments + callerArguments = expected + } else { + parsed = command.parseCommand(`cmd ${expected}`) + } + const args = [].slice.call(callerArguments) + + while (args.length && args[args.length - 1] === undefined) args.pop() + length = length || args.length + + if (length < parsed.demanded.length) { + throw new YError(`Not enough arguments provided. Expected ${parsed.demanded.length} but received ${args.length}.`) + } + + const totalCommands = parsed.demanded.length + parsed.optional.length + if (length > totalCommands) { + throw new YError(`Too many arguments provided. Expected max ${totalCommands} but received ${length}.`) + } + + parsed.demanded.forEach((demanded) => { + const arg = args.shift() + const observedType = guessType(arg) + const matchingTypes = demanded.cmd.filter(type => type === observedType || type === '*') + if (matchingTypes.length === 0) argumentTypeError(observedType, demanded.cmd, position, false) + position += 1 + }) + + parsed.optional.forEach((optional) => { + if (args.length === 0) return + const arg = args.shift() + const observedType = guessType(arg) + const matchingTypes = optional.cmd.filter(type => type === observedType || type === '*') + if (matchingTypes.length === 0) argumentTypeError(observedType, optional.cmd, position, true) + position += 1 + }) + } catch (err) { + console.warn(err.stack) + } +} + +function guessType (arg) { + if (Array.isArray(arg)) { + return 'array' + } else if (arg === null) { + return 'null' + } + return typeof arg +} + +function argumentTypeError (observedType, allowedTypes, position, optional) { + throw new YError(`Invalid ${positionName[position] || 'manyith'} argument. Expected ${allowedTypes.join(' or ')} but received ${observedType}.`) +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/command.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/command.js new file mode 100644 index 000000000..65ffc4ea5 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/command.js @@ -0,0 +1,433 @@ +'use strict' + +const inspect = require('util').inspect +const path = require('path') +const Parser = require('yargs-parser') + +const DEFAULT_MARKER = /(^\*)|(^\$0)/ + +// handles parsing positional arguments, +// and populating argv with said positional +// arguments. +module.exports = function command (yargs, usage, validation, globalMiddleware) { + const self = {} + let handlers = {} + let aliasMap = {} + let defaultCommand + globalMiddleware = globalMiddleware || [] + self.addHandler = function addHandler (cmd, description, builder, handler, middlewares) { + let aliases = [] + handler = handler || (() => {}) + middlewares = middlewares || [] + globalMiddleware.push(...middlewares) + middlewares = globalMiddleware + if (Array.isArray(cmd)) { + aliases = cmd.slice(1) + cmd = cmd[0] + } else if (typeof cmd === 'object') { + let command = (Array.isArray(cmd.command) || typeof cmd.command === 'string') ? cmd.command : moduleName(cmd) + if (cmd.aliases) command = [].concat(command).concat(cmd.aliases) + self.addHandler(command, extractDesc(cmd), cmd.builder, cmd.handler, cmd.middlewares) + return + } + + // allow a module to be provided instead of separate builder and handler + if (typeof builder === 'object' && builder.builder && typeof builder.handler === 'function') { + self.addHandler([cmd].concat(aliases), description, builder.builder, builder.handler, builder.middlewares) + return + } + + // parse positionals out of cmd string + const parsedCommand = self.parseCommand(cmd) + + // remove positional args from aliases only + aliases = aliases.map(alias => self.parseCommand(alias).cmd) + + // check for default and filter out '*'' + let isDefault = false + const parsedAliases = [parsedCommand.cmd].concat(aliases).filter((c) => { + if (DEFAULT_MARKER.test(c)) { + isDefault = true + return false + } + return true + }) + + // standardize on $0 for default command. + if (parsedAliases.length === 0 && isDefault) parsedAliases.push('$0') + + // shift cmd and aliases after filtering out '*' + if (isDefault) { + parsedCommand.cmd = parsedAliases[0] + aliases = parsedAliases.slice(1) + cmd = cmd.replace(DEFAULT_MARKER, parsedCommand.cmd) + } + + // populate aliasMap + aliases.forEach((alias) => { + aliasMap[alias] = parsedCommand.cmd + }) + + if (description !== false) { + usage.command(cmd, description, isDefault, aliases) + } + + handlers[parsedCommand.cmd] = { + original: cmd, + description: description, + handler, + builder: builder || {}, + middlewares: middlewares || [], + demanded: parsedCommand.demanded, + optional: parsedCommand.optional + } + + if (isDefault) defaultCommand = handlers[parsedCommand.cmd] + } + + self.addDirectory = function addDirectory (dir, context, req, callerFile, opts) { + opts = opts || {} + // disable recursion to support nested directories of subcommands + if (typeof opts.recurse !== 'boolean') opts.recurse = false + // exclude 'json', 'coffee' from require-directory defaults + if (!Array.isArray(opts.extensions)) opts.extensions = ['js'] + // allow consumer to define their own visitor function + const parentVisit = typeof opts.visit === 'function' ? opts.visit : o => o + // call addHandler via visitor function + opts.visit = function visit (obj, joined, filename) { + const visited = parentVisit(obj, joined, filename) + // allow consumer to skip modules with their own visitor + if (visited) { + // check for cyclic reference + // each command file path should only be seen once per execution + if (~context.files.indexOf(joined)) return visited + // keep track of visited files in context.files + context.files.push(joined) + self.addHandler(visited) + } + return visited + } + require('require-directory')({ require: req, filename: callerFile }, dir, opts) + } + + // lookup module object from require()d command and derive name + // if module was not require()d and no name given, throw error + function moduleName (obj) { + const mod = require('which-module')(obj) + if (!mod) throw new Error(`No command name given for module: ${inspect(obj)}`) + return commandFromFilename(mod.filename) + } + + // derive command name from filename + function commandFromFilename (filename) { + return path.basename(filename, path.extname(filename)) + } + + function extractDesc (obj) { + for (let keys = ['describe', 'description', 'desc'], i = 0, l = keys.length, test; i < l; i++) { + test = obj[keys[i]] + if (typeof test === 'string' || typeof test === 'boolean') return test + } + return false + } + + self.parseCommand = function parseCommand (cmd) { + const extraSpacesStrippedCommand = cmd.replace(/\s{2,}/g, ' ') + const splitCommand = extraSpacesStrippedCommand.split(/\s+(?![^[]*]|[^<]*>)/) + const bregex = /\.*[\][<>]/g + const parsedCommand = { + cmd: (splitCommand.shift()).replace(bregex, ''), + demanded: [], + optional: [] + } + splitCommand.forEach((cmd, i) => { + let variadic = false + cmd = cmd.replace(/\s/g, '') + if (/\.+[\]>]/.test(cmd) && i === splitCommand.length - 1) variadic = true + if (/^\[/.test(cmd)) { + parsedCommand.optional.push({ + cmd: cmd.replace(bregex, '').split('|'), + variadic + }) + } else { + parsedCommand.demanded.push({ + cmd: cmd.replace(bregex, '').split('|'), + variadic + }) + } + }) + return parsedCommand + } + + self.getCommands = () => Object.keys(handlers).concat(Object.keys(aliasMap)) + + self.getCommandHandlers = () => handlers + + self.hasDefaultCommand = () => !!defaultCommand + + self.runCommand = function runCommand (command, yargs, parsed, commandIndex) { + let aliases = parsed.aliases + const commandHandler = handlers[command] || handlers[aliasMap[command]] || defaultCommand + const currentContext = yargs.getContext() + let numFiles = currentContext.files.length + const parentCommands = currentContext.commands.slice() + + // what does yargs look like after the buidler is run? + let innerArgv = parsed.argv + let innerYargs = null + let positionalMap = {} + if (command) { + currentContext.commands.push(command) + currentContext.fullCommands.push(commandHandler.original) + } + if (typeof commandHandler.builder === 'function') { + // a function can be provided, which builds + // up a yargs chain and possibly returns it. + innerYargs = commandHandler.builder(yargs.reset(parsed.aliases)) + // if the builder function did not yet parse argv with reset yargs + // and did not explicitly set a usage() string, then apply the + // original command string as usage() for consistent behavior with + // options object below. + if (yargs.parsed === false) { + if (shouldUpdateUsage(yargs)) { + yargs.getUsageInstance().usage( + usageFromParentCommandsCommandHandler(parentCommands, commandHandler), + commandHandler.description + ) + } + innerArgv = innerYargs ? innerYargs._parseArgs(null, null, true, commandIndex) : yargs._parseArgs(null, null, true, commandIndex) + } else { + innerArgv = yargs.parsed.argv + } + + if (innerYargs && yargs.parsed === false) aliases = innerYargs.parsed.aliases + else aliases = yargs.parsed.aliases + } else if (typeof commandHandler.builder === 'object') { + // as a short hand, an object can instead be provided, specifying + // the options that a command takes. + innerYargs = yargs.reset(parsed.aliases) + if (shouldUpdateUsage(innerYargs)) { + innerYargs.getUsageInstance().usage( + usageFromParentCommandsCommandHandler(parentCommands, commandHandler), + commandHandler.description + ) + } + Object.keys(commandHandler.builder).forEach((key) => { + innerYargs.option(key, commandHandler.builder[key]) + }) + innerArgv = innerYargs._parseArgs(null, null, true, commandIndex) + aliases = innerYargs.parsed.aliases + } + + if (!yargs._hasOutput()) { + positionalMap = populatePositionals(commandHandler, innerArgv, currentContext, yargs) + } + + // we apply validation post-hoc, so that custom + // checks get passed populated positional arguments. + if (!yargs._hasOutput()) yargs._runValidation(innerArgv, aliases, positionalMap, yargs.parsed.error) + + if (commandHandler.handler && !yargs._hasOutput()) { + yargs._setHasOutput() + if (commandHandler.middlewares.length > 0) { + const middlewareArgs = commandHandler.middlewares.reduce(function (initialObj, middleware) { + return Object.assign(initialObj, middleware(innerArgv)) + }, {}) + Object.assign(innerArgv, middlewareArgs) + } + const handlerResult = commandHandler.handler(innerArgv) + if (handlerResult && typeof handlerResult.then === 'function') { + handlerResult.then( + null, + (error) => yargs.getUsageInstance().fail(null, error) + ) + } + } + + if (command) { + currentContext.commands.pop() + currentContext.fullCommands.pop() + } + numFiles = currentContext.files.length - numFiles + if (numFiles > 0) currentContext.files.splice(numFiles * -1, numFiles) + + return innerArgv + } + + function shouldUpdateUsage (yargs) { + return !yargs.getUsageInstance().getUsageDisabled() && + yargs.getUsageInstance().getUsage().length === 0 + } + + function usageFromParentCommandsCommandHandler (parentCommands, commandHandler) { + const c = DEFAULT_MARKER.test(commandHandler.original) ? commandHandler.original.replace(DEFAULT_MARKER, '').trim() : commandHandler.original + const pc = parentCommands.filter((c) => { return !DEFAULT_MARKER.test(c) }) + pc.push(c) + return `$0 ${pc.join(' ')}` + } + + self.runDefaultBuilderOn = function (yargs) { + if (shouldUpdateUsage(yargs)) { + // build the root-level command string from the default string. + const commandString = DEFAULT_MARKER.test(defaultCommand.original) + ? defaultCommand.original : defaultCommand.original.replace(/^[^[\]<>]*/, '$0 ') + yargs.getUsageInstance().usage( + commandString, + defaultCommand.description + ) + } + const builder = defaultCommand.builder + if (typeof builder === 'function') { + builder(yargs) + } else { + Object.keys(builder).forEach((key) => { + yargs.option(key, builder[key]) + }) + } + } + + // transcribe all positional arguments "command [apple]" + // onto argv. + function populatePositionals (commandHandler, argv, context, yargs) { + argv._ = argv._.slice(context.commands.length) // nuke the current commands + const demanded = commandHandler.demanded.slice(0) + const optional = commandHandler.optional.slice(0) + const positionalMap = {} + + validation.positionalCount(demanded.length, argv._.length) + + while (demanded.length) { + const demand = demanded.shift() + populatePositional(demand, argv, positionalMap) + } + + while (optional.length) { + const maybe = optional.shift() + populatePositional(maybe, argv, positionalMap) + } + + argv._ = context.commands.concat(argv._) + + postProcessPositionals(argv, positionalMap, self.cmdToParseOptions(commandHandler.original)) + + return positionalMap + } + + function populatePositional (positional, argv, positionalMap, parseOptions) { + const cmd = positional.cmd[0] + if (positional.variadic) { + positionalMap[cmd] = argv._.splice(0).map(String) + } else { + if (argv._.length) positionalMap[cmd] = [String(argv._.shift())] + } + } + + // we run yargs-parser against the positional arguments + // applying the same parsing logic used for flags. + function postProcessPositionals (argv, positionalMap, parseOptions) { + // combine the parsing hints we've inferred from the command + // string with explicitly configured parsing hints. + const options = Object.assign({}, yargs.getOptions()) + options.default = Object.assign(parseOptions.default, options.default) + options.alias = Object.assign(parseOptions.alias, options.alias) + options.array = options.array.concat(parseOptions.array) + delete options.config // don't load config when processing positionals. + + const unparsed = [] + Object.keys(positionalMap).forEach((key) => { + positionalMap[key].map((value) => { + unparsed.push(`--${key}`) + unparsed.push(value) + }) + }) + + // short-circuit parse. + if (!unparsed.length) return + + const parsed = Parser.detailed(unparsed, options) + + if (parsed.error) { + yargs.getUsageInstance().fail(parsed.error.message, parsed.error) + } else { + // only copy over positional keys (don't overwrite + // flag arguments that were already parsed). + const positionalKeys = Object.keys(positionalMap) + Object.keys(positionalMap).forEach((key) => { + [].push.apply(positionalKeys, parsed.aliases[key]) + }) + + Object.keys(parsed.argv).forEach((key) => { + if (positionalKeys.indexOf(key) !== -1) { + // any new aliases need to be placed in positionalMap, which + // is used for validation. + if (!positionalMap[key]) positionalMap[key] = parsed.argv[key] + argv[key] = parsed.argv[key] + } + }) + } + } + + self.cmdToParseOptions = function (cmdString) { + const parseOptions = { + array: [], + default: {}, + alias: {}, + demand: {} + } + + const parsed = self.parseCommand(cmdString) + parsed.demanded.forEach((d) => { + const cmds = d.cmd.slice(0) + const cmd = cmds.shift() + if (d.variadic) { + parseOptions.array.push(cmd) + parseOptions.default[cmd] = [] + } + cmds.forEach((c) => { + parseOptions.alias[cmd] = c + }) + parseOptions.demand[cmd] = true + }) + + parsed.optional.forEach((o) => { + const cmds = o.cmd.slice(0) + const cmd = cmds.shift() + if (o.variadic) { + parseOptions.array.push(cmd) + parseOptions.default[cmd] = [] + } + cmds.forEach((c) => { + parseOptions.alias[cmd] = c + }) + }) + + return parseOptions + } + + self.reset = () => { + handlers = {} + aliasMap = {} + defaultCommand = undefined + return self + } + + // used by yargs.parse() to freeze + // the state of commands such that + // we can apply .parse() multiple times + // with the same yargs instance. + let frozen + self.freeze = () => { + frozen = {} + frozen.handlers = handlers + frozen.aliasMap = aliasMap + frozen.defaultCommand = defaultCommand + } + self.unfreeze = () => { + handlers = frozen.handlers + aliasMap = frozen.aliasMap + defaultCommand = frozen.defaultCommand + frozen = undefined + } + + return self +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/completion.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/completion.js new file mode 100644 index 000000000..ad6969a2d --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/completion.js @@ -0,0 +1,105 @@ +'use strict' +const fs = require('fs') +const path = require('path') + +// add bash completions to your +// yargs-powered applications. +module.exports = function completion (yargs, usage, command) { + const self = { + completionKey: 'get-yargs-completions' + } + + // get a list of completion commands. + // 'args' is the array of strings from the line to be completed + self.getCompletion = function getCompletion (args, done) { + const completions = [] + const current = args.length ? args[args.length - 1] : '' + const argv = yargs.parse(args, true) + const aliases = yargs.parsed.aliases + + // a custom completion function can be provided + // to completion(). + if (completionFunction) { + if (completionFunction.length < 3) { + const result = completionFunction(current, argv) + + // promise based completion function. + if (typeof result.then === 'function') { + return result.then((list) => { + process.nextTick(() => { done(list) }) + }).catch((err) => { + process.nextTick(() => { throw err }) + }) + } + + // synchronous completion function. + return done(result) + } else { + // asynchronous completion function + return completionFunction(current, argv, (completions) => { + done(completions) + }) + } + } + + const handlers = command.getCommandHandlers() + for (let i = 0, ii = args.length; i < ii; ++i) { + if (handlers[args[i]] && handlers[args[i]].builder) { + const builder = handlers[args[i]].builder + if (typeof builder === 'function') { + const y = yargs.reset() + builder(y) + return y.argv + } + } + } + + if (!current.match(/^-/)) { + usage.getCommands().forEach((usageCommand) => { + const commandName = command.parseCommand(usageCommand[0]).cmd + if (args.indexOf(commandName) === -1) { + completions.push(commandName) + } + }) + } + + if (current.match(/^-/)) { + Object.keys(yargs.getOptions().key).forEach((key) => { + // If the key and its aliases aren't in 'args', add the key to 'completions' + const keyAndAliases = [key].concat(aliases[key] || []) + const notInArgs = keyAndAliases.every(val => args.indexOf(`--${val}`) === -1) + if (notInArgs) { + completions.push(`--${key}`) + } + }) + } + + done(completions) + } + + // generate the completion script to add to your .bashrc. + self.generateCompletionScript = function generateCompletionScript ($0, cmd) { + let script = fs.readFileSync( + path.resolve(__dirname, '../completion.sh.hbs'), + 'utf-8' + ) + const name = path.basename($0) + + // add ./to applications not yet installed as bin. + if ($0.match(/\.js$/)) $0 = `./${$0}` + + script = script.replace(/{{app_name}}/g, name) + script = script.replace(/{{completion_command}}/g, cmd) + return script.replace(/{{app_path}}/g, $0) + } + + // register a function to perform your own custom + // completions., this function can be either + // synchrnous or asynchronous. + let completionFunction = null + self.registerFunction = (fn) => { + completionFunction = fn + } + + return self +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/levenshtein.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/levenshtein.js new file mode 100644 index 000000000..dd3107333 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/levenshtein.js @@ -0,0 +1,47 @@ +/* +Copyright (c) 2011 Andrei Mackenzie + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// levenshtein distance algorithm, pulled from Andrei Mackenzie's MIT licensed. +// gist, which can be found here: https://gist.github.com/andrei-m/982927 +'use strict' +// Compute the edit distance between the two given strings +module.exports = function levenshtein (a, b) { + if (a.length === 0) return b.length + if (b.length === 0) return a.length + + const matrix = [] + + // increment along the first column of each row + let i + for (i = 0; i <= b.length; i++) { + matrix[i] = [i] + } + + // increment each column in the first row + let j + for (j = 0; j <= a.length; j++) { + matrix[0][j] = j + } + + // Fill in the rest of the matrix + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1] + } else { + matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution + Math.min(matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1)) // deletion + } + } + } + + return matrix[b.length][a.length] +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/middleware.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/middleware.js new file mode 100644 index 000000000..567ba571a --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/middleware.js @@ -0,0 +1,10 @@ +module.exports = function (globalMiddleware, context) { + return function (callback) { + if (Array.isArray(callback)) { + Array.prototype.push.apply(globalMiddleware, callback) + } else if (typeof callback === 'function') { + globalMiddleware.push(callback) + } + return context + } +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/obj-filter.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/obj-filter.js new file mode 100644 index 000000000..c344ac58c --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/obj-filter.js @@ -0,0 +1,11 @@ +'use strict' +module.exports = function objFilter (original, filter) { + const obj = {} + filter = filter || ((k, v) => true) + Object.keys(original || {}).forEach((key) => { + if (filter(key, original[key])) { + obj[key] = original[key] + } + }) + return obj +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/usage.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/usage.js new file mode 100644 index 000000000..9be8a995b --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/usage.js @@ -0,0 +1,535 @@ +'use strict' +// this file handles outputting usage instructions, +// failures, etc. keeps logging in one place. +const stringWidth = require('string-width') +const objFilter = require('./obj-filter') +const path = require('path') +const setBlocking = require('set-blocking') +const YError = require('./yerror') + +module.exports = function usage (yargs, y18n) { + const __ = y18n.__ + const self = {} + + // methods for ouputting/building failure message. + const fails = [] + self.failFn = function failFn (f) { + fails.push(f) + } + + let failMessage = null + let showHelpOnFail = true + self.showHelpOnFail = function showHelpOnFailFn (enabled, message) { + if (typeof enabled === 'string') { + message = enabled + enabled = true + } else if (typeof enabled === 'undefined') { + enabled = true + } + failMessage = message + showHelpOnFail = enabled + return self + } + + let failureOutput = false + self.fail = function fail (msg, err) { + const logger = yargs._getLoggerInstance() + + if (fails.length) { + for (let i = fails.length - 1; i >= 0; --i) { + fails[i](msg, err, self) + } + } else { + if (yargs.getExitProcess()) setBlocking(true) + + // don't output failure message more than once + if (!failureOutput) { + failureOutput = true + if (showHelpOnFail) { + yargs.showHelp('error') + logger.error() + } + if (msg || err) logger.error(msg || err) + if (failMessage) { + if (msg || err) logger.error('') + logger.error(failMessage) + } + } + + err = err || new YError(msg) + if (yargs.getExitProcess()) { + return yargs.exit(1) + } else if (yargs._hasParseCallback()) { + return yargs.exit(1, err) + } else { + throw err + } + } + } + + // methods for ouputting/building help (usage) message. + let usages = [] + let usageDisabled = false + self.usage = (msg, description) => { + if (msg === null) { + usageDisabled = true + usages = [] + return + } + usageDisabled = false + usages.push([msg, description || '']) + return self + } + self.getUsage = () => { + return usages + } + self.getUsageDisabled = () => { + return usageDisabled + } + + self.getPositionalGroupName = () => { + return __('Positionals:') + } + + let examples = [] + self.example = (cmd, description) => { + examples.push([cmd, description || '']) + } + + let commands = [] + self.command = function command (cmd, description, isDefault, aliases) { + // the last default wins, so cancel out any previously set default + if (isDefault) { + commands = commands.map((cmdArray) => { + cmdArray[2] = false + return cmdArray + }) + } + commands.push([cmd, description || '', isDefault, aliases]) + } + self.getCommands = () => commands + + let descriptions = {} + self.describe = function describe (key, desc) { + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.describe(k, key[k]) + }) + } else { + descriptions[key] = desc + } + } + self.getDescriptions = () => descriptions + + let epilog + self.epilog = (msg) => { + epilog = msg + } + + let wrapSet = false + let wrap + self.wrap = (cols) => { + wrapSet = true + wrap = cols + } + + function getWrap () { + if (!wrapSet) { + wrap = windowWidth() + wrapSet = true + } + + return wrap + } + + const deferY18nLookupPrefix = '__yargsString__:' + self.deferY18nLookup = str => deferY18nLookupPrefix + str + + const defaultGroup = 'Options:' + self.help = function help () { + normalizeAliases() + + // handle old demanded API + const base$0 = path.basename(yargs.$0) + const demandedOptions = yargs.getDemandedOptions() + const demandedCommands = yargs.getDemandedCommands() + const groups = yargs.getGroups() + const options = yargs.getOptions() + + let keys = [] + keys = keys.concat(Object.keys(descriptions)) + keys = keys.concat(Object.keys(demandedOptions)) + keys = keys.concat(Object.keys(demandedCommands)) + keys = keys.concat(Object.keys(options.default)) + keys = keys.filter(filterHiddenOptions) + keys = Object.keys(keys.reduce((acc, key) => { + if (key !== '_') acc[key] = true + return acc + }, {})) + + const theWrap = getWrap() + const ui = require('cliui')({ + width: theWrap, + wrap: !!theWrap + }) + + // the usage string. + if (!usageDisabled) { + if (usages.length) { + // user-defined usage. + usages.forEach((usage) => { + ui.div(`${usage[0].replace(/\$0/g, base$0)}`) + if (usage[1]) { + ui.div({text: `${usage[1]}`, padding: [1, 0, 0, 0]}) + } + }) + ui.div() + } else if (commands.length) { + let u = null + // demonstrate how commands are used. + if (demandedCommands._) { + u = `${base$0} <${__('command')}>\n` + } else { + u = `${base$0} [${__('command')}]\n` + } + ui.div(`${u}`) + } + } + + // your application's commands, i.e., non-option + // arguments populated in '_'. + if (commands.length) { + ui.div(__('Commands:')) + + const context = yargs.getContext() + const parentCommands = context.commands.length ? `${context.commands.join(' ')} ` : '' + + commands.forEach((command) => { + const commandString = `${base$0} ${parentCommands}${command[0].replace(/^\$0 ?/, '')}` // drop $0 from default commands. + ui.span( + { + text: commandString, + padding: [0, 2, 0, 2], + width: maxWidth(commands, theWrap, `${base$0}${parentCommands}`) + 4 + }, + {text: command[1]} + ) + const hints = [] + if (command[2]) hints.push(`[${__('default:').slice(0, -1)}]`) // TODO hacking around i18n here + if (command[3] && command[3].length) { + hints.push(`[${__('aliases:')} ${command[3].join(', ')}]`) + } + if (hints.length) { + ui.div({text: hints.join(' '), padding: [0, 0, 0, 2], align: 'right'}) + } else { + ui.div() + } + }) + + ui.div() + } + + // perform some cleanup on the keys array, making it + // only include top-level keys not their aliases. + const aliasKeys = (Object.keys(options.alias) || []) + .concat(Object.keys(yargs.parsed.newAliases) || []) + + keys = keys.filter(key => !yargs.parsed.newAliases[key] && aliasKeys.every(alias => (options.alias[alias] || []).indexOf(key) === -1)) + + // populate 'Options:' group with any keys that have not + // explicitly had a group set. + if (!groups[defaultGroup]) groups[defaultGroup] = [] + addUngroupedKeys(keys, options.alias, groups) + + // display 'Options:' table along with any custom tables: + Object.keys(groups).forEach((groupName) => { + if (!groups[groupName].length) return + + // if we've grouped the key 'f', but 'f' aliases 'foobar', + // normalizedKeys should contain only 'foobar'. + const normalizedKeys = groups[groupName].filter(filterHiddenOptions).map((key) => { + if (~aliasKeys.indexOf(key)) return key + for (let i = 0, aliasKey; (aliasKey = aliasKeys[i]) !== undefined; i++) { + if (~(options.alias[aliasKey] || []).indexOf(key)) return aliasKey + } + return key + }) + + if (normalizedKeys.length < 1) return + + ui.div(__(groupName)) + + // actually generate the switches string --foo, -f, --bar. + const switches = normalizedKeys.reduce((acc, key) => { + acc[key] = [ key ].concat(options.alias[key] || []) + .map(sw => { + // for the special positional group don't + // add '--' or '-' prefix. + if (groupName === self.getPositionalGroupName()) return sw + else return (sw.length > 1 ? '--' : '-') + sw + }) + .join(', ') + + return acc + }, {}) + + normalizedKeys.forEach((key) => { + const kswitch = switches[key] + let desc = descriptions[key] || '' + let type = null + + if (~desc.lastIndexOf(deferY18nLookupPrefix)) desc = __(desc.substring(deferY18nLookupPrefix.length)) + + if (~options.boolean.indexOf(key)) type = `[${__('boolean')}]` + if (~options.count.indexOf(key)) type = `[${__('count')}]` + if (~options.string.indexOf(key)) type = `[${__('string')}]` + if (~options.normalize.indexOf(key)) type = `[${__('string')}]` + if (~options.array.indexOf(key)) type = `[${__('array')}]` + if (~options.number.indexOf(key)) type = `[${__('number')}]` + + const extra = [ + type, + (key in demandedOptions) ? `[${__('required')}]` : null, + options.choices && options.choices[key] ? `[${__('choices:')} ${ + self.stringifiedValues(options.choices[key])}]` : null, + defaultString(options.default[key], options.defaultDescription[key]) + ].filter(Boolean).join(' ') + + ui.span( + {text: kswitch, padding: [0, 2, 0, 2], width: maxWidth(switches, theWrap) + 4}, + desc + ) + + if (extra) ui.div({text: extra, padding: [0, 0, 0, 2], align: 'right'}) + else ui.div() + }) + + ui.div() + }) + + // describe some common use-cases for your application. + if (examples.length) { + ui.div(__('Examples:')) + + examples.forEach((example) => { + example[0] = example[0].replace(/\$0/g, base$0) + }) + + examples.forEach((example) => { + if (example[1] === '') { + ui.div( + { + text: example[0], + padding: [0, 2, 0, 2] + } + ) + } else { + ui.div( + { + text: example[0], + padding: [0, 2, 0, 2], + width: maxWidth(examples, theWrap) + 4 + }, { + text: example[1] + } + ) + } + }) + + ui.div() + } + + // the usage string. + if (epilog) { + const e = epilog.replace(/\$0/g, base$0) + ui.div(`${e}\n`) + } + + // Remove the trailing white spaces + return ui.toString().replace(/\s*$/, '') + } + + // return the maximum width of a string + // in the left-hand column of a table. + function maxWidth (table, theWrap, modifier) { + let width = 0 + + // table might be of the form [leftColumn], + // or {key: leftColumn} + if (!Array.isArray(table)) { + table = Object.keys(table).map(key => [table[key]]) + } + + table.forEach((v) => { + width = Math.max( + stringWidth(modifier ? `${modifier} ${v[0]}` : v[0]), + width + ) + }) + + // if we've enabled 'wrap' we should limit + // the max-width of the left-column. + if (theWrap) width = Math.min(width, parseInt(theWrap * 0.5, 10)) + + return width + } + + // make sure any options set for aliases, + // are copied to the keys being aliased. + function normalizeAliases () { + // handle old demanded API + const demandedOptions = yargs.getDemandedOptions() + const options = yargs.getOptions() + + ;(Object.keys(options.alias) || []).forEach((key) => { + options.alias[key].forEach((alias) => { + // copy descriptions. + if (descriptions[alias]) self.describe(key, descriptions[alias]) + // copy demanded. + if (alias in demandedOptions) yargs.demandOption(key, demandedOptions[alias]) + // type messages. + if (~options.boolean.indexOf(alias)) yargs.boolean(key) + if (~options.count.indexOf(alias)) yargs.count(key) + if (~options.string.indexOf(alias)) yargs.string(key) + if (~options.normalize.indexOf(alias)) yargs.normalize(key) + if (~options.array.indexOf(alias)) yargs.array(key) + if (~options.number.indexOf(alias)) yargs.number(key) + }) + }) + } + + // given a set of keys, place any keys that are + // ungrouped under the 'Options:' grouping. + function addUngroupedKeys (keys, aliases, groups) { + let groupedKeys = [] + let toCheck = null + Object.keys(groups).forEach((group) => { + groupedKeys = groupedKeys.concat(groups[group]) + }) + + keys.forEach((key) => { + toCheck = [key].concat(aliases[key]) + if (!toCheck.some(k => groupedKeys.indexOf(k) !== -1)) { + groups[defaultGroup].push(key) + } + }) + return groupedKeys + } + + function filterHiddenOptions (key) { + return yargs.getOptions().hiddenOptions.indexOf(key) < 0 || yargs.parsed.argv[yargs.getOptions().showHiddenOpt] + } + + self.showHelp = (level) => { + const logger = yargs._getLoggerInstance() + if (!level) level = 'error' + const emit = typeof level === 'function' ? level : logger[level] + emit(self.help()) + } + + self.functionDescription = (fn) => { + const description = fn.name ? require('decamelize')(fn.name, '-') : __('generated-value') + return ['(', description, ')'].join('') + } + + self.stringifiedValues = function stringifiedValues (values, separator) { + let string = '' + const sep = separator || ', ' + const array = [].concat(values) + + if (!values || !array.length) return string + + array.forEach((value) => { + if (string.length) string += sep + string += JSON.stringify(value) + }) + + return string + } + + // format the default-value-string displayed in + // the right-hand column. + function defaultString (value, defaultDescription) { + let string = `[${__('default:')} ` + + if (value === undefined && !defaultDescription) return null + + if (defaultDescription) { + string += defaultDescription + } else { + switch (typeof value) { + case 'string': + string += `"${value}"` + break + case 'object': + string += JSON.stringify(value) + break + default: + string += value + } + } + + return `${string}]` + } + + // guess the width of the console window, max-width 80. + function windowWidth () { + const maxWidth = 80 + if (typeof process === 'object' && process.stdout && process.stdout.columns) { + return Math.min(maxWidth, process.stdout.columns) + } else { + return maxWidth + } + } + + // logic for displaying application version. + let version = null + self.version = (ver) => { + version = ver + } + + self.showVersion = () => { + const logger = yargs._getLoggerInstance() + logger.log(version) + } + + self.reset = function reset (localLookup) { + // do not reset wrap here + // do not reset fails here + failMessage = null + failureOutput = false + usages = [] + usageDisabled = false + epilog = undefined + examples = [] + commands = [] + descriptions = objFilter(descriptions, (k, v) => !localLookup[k]) + return self + } + + let frozen + self.freeze = function freeze () { + frozen = {} + frozen.failMessage = failMessage + frozen.failureOutput = failureOutput + frozen.usages = usages + frozen.usageDisabled = usageDisabled + frozen.epilog = epilog + frozen.examples = examples + frozen.commands = commands + frozen.descriptions = descriptions + } + self.unfreeze = function unfreeze () { + failMessage = frozen.failMessage + failureOutput = frozen.failureOutput + usages = frozen.usages + usageDisabled = frozen.usageDisabled + epilog = frozen.epilog + examples = frozen.examples + commands = frozen.commands + descriptions = frozen.descriptions + frozen = undefined + } + + return self +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/validation.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/validation.js new file mode 100644 index 000000000..e9bbb12bc --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/validation.js @@ -0,0 +1,341 @@ +'use strict' +const argsert = require('./argsert') +const objFilter = require('./obj-filter') +const specialKeys = ['$0', '--', '_'] + +// validation-type-stuff, missing params, +// bad implications, custom checks. +module.exports = function validation (yargs, usage, y18n) { + const __ = y18n.__ + const __n = y18n.__n + const self = {} + + // validate appropriate # of non-option + // arguments were provided, i.e., '_'. + self.nonOptionCount = function nonOptionCount (argv) { + const demandedCommands = yargs.getDemandedCommands() + // don't count currently executing commands + const _s = argv._.length - yargs.getContext().commands.length + + if (demandedCommands._ && (_s < demandedCommands._.min || _s > demandedCommands._.max)) { + if (_s < demandedCommands._.min) { + if (demandedCommands._.minMsg !== undefined) { + usage.fail( + // replace $0 with observed, $1 with expected. + demandedCommands._.minMsg ? demandedCommands._.minMsg.replace(/\$0/g, _s).replace(/\$1/, demandedCommands._.min) : null + ) + } else { + usage.fail( + __('Not enough non-option arguments: got %s, need at least %s', _s, demandedCommands._.min) + ) + } + } else if (_s > demandedCommands._.max) { + if (demandedCommands._.maxMsg !== undefined) { + usage.fail( + // replace $0 with observed, $1 with expected. + demandedCommands._.maxMsg ? demandedCommands._.maxMsg.replace(/\$0/g, _s).replace(/\$1/, demandedCommands._.max) : null + ) + } else { + usage.fail( + __('Too many non-option arguments: got %s, maximum of %s', _s, demandedCommands._.max) + ) + } + } + } + } + + // validate the appropriate # of + // positional arguments were provided: + self.positionalCount = function positionalCount (required, observed) { + if (observed < required) { + usage.fail( + __('Not enough non-option arguments: got %s, need at least %s', observed, required) + ) + } + } + + // make sure all the required arguments are present. + self.requiredArguments = function requiredArguments (argv) { + const demandedOptions = yargs.getDemandedOptions() + let missing = null + + Object.keys(demandedOptions).forEach((key) => { + if (!argv.hasOwnProperty(key) || typeof argv[key] === 'undefined') { + missing = missing || {} + missing[key] = demandedOptions[key] + } + }) + + if (missing) { + const customMsgs = [] + Object.keys(missing).forEach((key) => { + const msg = missing[key] + if (msg && customMsgs.indexOf(msg) < 0) { + customMsgs.push(msg) + } + }) + + const customMsg = customMsgs.length ? `\n${customMsgs.join('\n')}` : '' + + usage.fail(__n( + 'Missing required argument: %s', + 'Missing required arguments: %s', + Object.keys(missing).length, + Object.keys(missing).join(', ') + customMsg + )) + } + } + + // check for unknown arguments (strict-mode). + self.unknownArguments = function unknownArguments (argv, aliases, positionalMap) { + const commandKeys = yargs.getCommandInstance().getCommands() + const unknown = [] + const currentContext = yargs.getContext() + + Object.keys(argv).forEach((key) => { + if (specialKeys.indexOf(key) === -1 && + !positionalMap.hasOwnProperty(key) && + !yargs._getParseContext().hasOwnProperty(key) && + !aliases.hasOwnProperty(key) + ) { + unknown.push(key) + } + }) + + if (commandKeys.length > 0) { + argv._.slice(currentContext.commands.length).forEach((key) => { + if (commandKeys.indexOf(key) === -1) { + unknown.push(key) + } + }) + } + + if (unknown.length > 0) { + usage.fail(__n( + 'Unknown argument: %s', + 'Unknown arguments: %s', + unknown.length, + unknown.join(', ') + )) + } + } + + // validate arguments limited to enumerated choices + self.limitedChoices = function limitedChoices (argv) { + const options = yargs.getOptions() + const invalid = {} + + if (!Object.keys(options.choices).length) return + + Object.keys(argv).forEach((key) => { + if (specialKeys.indexOf(key) === -1 && + options.choices.hasOwnProperty(key)) { + [].concat(argv[key]).forEach((value) => { + // TODO case-insensitive configurability + if (options.choices[key].indexOf(value) === -1 && + value !== undefined) { + invalid[key] = (invalid[key] || []).concat(value) + } + }) + } + }) + + const invalidKeys = Object.keys(invalid) + + if (!invalidKeys.length) return + + let msg = __('Invalid values:') + invalidKeys.forEach((key) => { + msg += `\n ${__( + 'Argument: %s, Given: %s, Choices: %s', + key, + usage.stringifiedValues(invalid[key]), + usage.stringifiedValues(options.choices[key]) + )}` + }) + usage.fail(msg) + } + + // custom checks, added using the `check` option on yargs. + let checks = [] + self.check = function check (f, global) { + checks.push({ + func: f, + global + }) + } + + self.customChecks = function customChecks (argv, aliases) { + for (let i = 0, f; (f = checks[i]) !== undefined; i++) { + const func = f.func + let result = null + try { + result = func(argv, aliases) + } catch (err) { + usage.fail(err.message ? err.message : err, err) + continue + } + + if (!result) { + usage.fail(__('Argument check failed: %s', func.toString())) + } else if (typeof result === 'string' || result instanceof Error) { + usage.fail(result.toString(), result) + } + } + } + + // check implications, argument foo implies => argument bar. + let implied = {} + self.implies = function implies (key, value) { + argsert(' [array|number|string]', [key, value], arguments.length) + + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.implies(k, key[k]) + }) + } else { + yargs.global(key) + if (!implied[key]) { + implied[key] = [] + } + if (Array.isArray(value)) { + value.forEach((i) => self.implies(key, i)) + } else { + implied[key].push(value) + } + } + } + self.getImplied = function getImplied () { + return implied + } + + self.implications = function implications (argv) { + const implyFail = [] + + Object.keys(implied).forEach((key) => { + const origKey = key + ;(implied[key] || []).forEach((value) => { + let num + let key = origKey + const origValue = value + + // convert string '1' to number 1 + num = Number(key) + key = isNaN(num) ? key : num + + if (typeof key === 'number') { + // check length of argv._ + key = argv._.length >= key + } else if (key.match(/^--no-.+/)) { + // check if key doesn't exist + key = key.match(/^--no-(.+)/)[1] + key = !argv[key] + } else { + // check if key exists + key = argv[key] + } + + num = Number(value) + value = isNaN(num) ? value : num + + if (typeof value === 'number') { + value = argv._.length >= value + } else if (value.match(/^--no-.+/)) { + value = value.match(/^--no-(.+)/)[1] + value = !argv[value] + } else { + value = argv[value] + } + if (key && !value) { + implyFail.push(` ${origKey} -> ${origValue}`) + } + }) + }) + + if (implyFail.length) { + let msg = `${__('Implications failed:')}\n` + + implyFail.forEach((value) => { + msg += (value) + }) + + usage.fail(msg) + } + } + + let conflicting = {} + self.conflicts = function conflicts (key, value) { + argsert(' [array|string]', [key, value], arguments.length) + + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.conflicts(k, key[k]) + }) + } else { + yargs.global(key) + if (!conflicting[key]) { + conflicting[key] = [] + } + if (Array.isArray(value)) { + value.forEach((i) => self.conflicts(key, i)) + } else { + conflicting[key].push(value) + } + } + } + self.getConflicting = () => conflicting + + self.conflicting = function conflictingFn (argv) { + Object.keys(argv).forEach((key) => { + if (conflicting[key]) { + conflicting[key].forEach((value) => { + // we default keys to 'undefined' that have been configured, we should not + // apply conflicting check unless they are a value other than 'undefined'. + if (value && argv[key] !== undefined && argv[value] !== undefined) { + usage.fail(__('Arguments %s and %s are mutually exclusive', key, value)) + } + }) + } + }) + } + + self.recommendCommands = function recommendCommands (cmd, potentialCommands) { + const distance = require('./levenshtein') + const threshold = 3 // if it takes more than three edits, let's move on. + potentialCommands = potentialCommands.sort((a, b) => b.length - a.length) + + let recommended = null + let bestDistance = Infinity + for (let i = 0, candidate; (candidate = potentialCommands[i]) !== undefined; i++) { + const d = distance(cmd, candidate) + if (d <= threshold && d < bestDistance) { + bestDistance = d + recommended = candidate + } + } + if (recommended) usage.fail(__('Did you mean %s?', recommended)) + } + + self.reset = function reset (localLookup) { + implied = objFilter(implied, (k, v) => !localLookup[k]) + conflicting = objFilter(conflicting, (k, v) => !localLookup[k]) + checks = checks.filter(c => c.global) + return self + } + + let frozen + self.freeze = function freeze () { + frozen = {} + frozen.implied = implied + frozen.checks = checks + frozen.conflicting = conflicting + } + self.unfreeze = function unfreeze () { + implied = frozen.implied + checks = frozen.checks + conflicting = frozen.conflicting + frozen = undefined + } + + return self +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/yerror.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/yerror.js new file mode 100644 index 000000000..53375a0f7 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/lib/yerror.js @@ -0,0 +1,11 @@ +'use strict' +function YError (msg) { + this.name = 'YError' + this.message = msg || 'yargs error' + Error.captureStackTrace(this, YError) +} + +YError.prototype = Object.create(Error.prototype) +YError.prototype.constructor = YError + +module.exports = YError diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/be.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/be.json new file mode 100644 index 000000000..141ebe1e1 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/be.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Каманды:", + "Options:": "Опцыі:", + "Examples:": "Прыклады:", + "boolean": "булевы тып", + "count": "падлік", + "string": "радковы тып", + "number": "лік", + "array": "ма�іў", + "required": "неабходна", + "default:": "па змаўчанні:", + "choices:": "магчыма�ці:", + "aliases:": "аліа�ы:", + "generated-value": "згенераванае знач�нне", + "Not enough non-option arguments: got %s, need at least %s": "�еда�таткова неапцыйных аргументаў: ё�ць %s, тр�ба �к мінімум %s", + "Too many non-option arguments: got %s, maximum of %s": "Занадта шмат неапцыйных аргументаў: ё�ць %s, мак�імум дапушчальна %s", + "Missing argument value: %s": { + "one": "�е хапае знач�нн� аргументу: %s", + "other": "�е хапае знач�нн�ў аргументаў: %s" + }, + "Missing required argument: %s": { + "one": "�е хапае неабходнага аргументу: %s", + "other": "�е хапае неабходных аргументаў: %s" + }, + "Unknown argument: %s": { + "one": "�ев�домы аргумент: %s", + "other": "�ев�домы� аргументы: %s" + }, + "Invalid values:": "�е�апраўдны� знач�нн�:", + "Argument: %s, Given: %s, Choices: %s": "�ргумент: %s, Дадзенае знач�нне: %s, Магчыма�ці: %s", + "Argument check failed: %s": "Праверка аргументаў не ўдала��: %s", + "Implications failed:": "Дадзены аргумент патрабуе на�тупны дадатковы аргумент:", + "Not enough arguments following: %s": "�еда�таткова на�тупных аргументаў: %s", + "Invalid JSON config file: %s": "�е�апраўдны файл канфігурацыі JSON: %s", + "Path to JSON config file": "Шл�х да файла канфігурацыі JSON", + "Show help": "Паказаць дапамогу", + "Show version number": "Паказаць нумар вер�іі", + "Did you mean %s?": "Вы мелі на ўвазе %s?" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/de.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/de.json new file mode 100644 index 000000000..d805710b0 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/de.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Kommandos:", + "Options:": "Optionen:", + "Examples:": "Beispiele:", + "boolean": "boolean", + "count": "Zähler", + "string": "string", + "number": "Zahl", + "array": "array", + "required": "erforderlich", + "default:": "Standard:", + "choices:": "Möglichkeiten:", + "aliases:": "Aliase:", + "generated-value": "Generierter-Wert", + "Not enough non-option arguments: got %s, need at least %s": "Nicht genügend Argumente ohne Optionen: %s vorhanden, mindestens %s benötigt", + "Too many non-option arguments: got %s, maximum of %s": "Zu viele Argumente ohne Optionen: %s vorhanden, maximal %s erlaubt", + "Missing argument value: %s": { + "one": "Fehlender Argumentwert: %s", + "other": "Fehlende Argumentwerte: %s" + }, + "Missing required argument: %s": { + "one": "Fehlendes Argument: %s", + "other": "Fehlende Argumente: %s" + }, + "Unknown argument: %s": { + "one": "Unbekanntes Argument: %s", + "other": "Unbekannte Argumente: %s" + }, + "Invalid values:": "Unzulässige Werte:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gegeben: %s, Möglichkeiten: %s", + "Argument check failed: %s": "Argumente-Check fehlgeschlagen: %s", + "Implications failed:": "Implikationen fehlgeschlagen:", + "Not enough arguments following: %s": "Nicht genügend Argumente nach: %s", + "Invalid JSON config file: %s": "Fehlerhafte JSON-Config Datei: %s", + "Path to JSON config file": "Pfad zur JSON-Config Datei", + "Show help": "Hilfe anzeigen", + "Show version number": "Version anzeigen", + "Did you mean %s?": "Meintest du %s?" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/en.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/en.json new file mode 100644 index 000000000..fc65c2a0d --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/en.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Commands:", + "Options:": "Options:", + "Examples:": "Examples:", + "boolean": "boolean", + "count": "count", + "string": "string", + "number": "number", + "array": "array", + "required": "required", + "default:": "default:", + "choices:": "choices:", + "aliases:": "aliases:", + "generated-value": "generated-value", + "Not enough non-option arguments: got %s, need at least %s": "Not enough non-option arguments: got %s, need at least %s", + "Too many non-option arguments: got %s, maximum of %s": "Too many non-option arguments: got %s, maximum of %s", + "Missing argument value: %s": { + "one": "Missing argument value: %s", + "other": "Missing argument values: %s" + }, + "Missing required argument: %s": { + "one": "Missing required argument: %s", + "other": "Missing required arguments: %s" + }, + "Unknown argument: %s": { + "one": "Unknown argument: %s", + "other": "Unknown arguments: %s" + }, + "Invalid values:": "Invalid values:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Given: %s, Choices: %s", + "Argument check failed: %s": "Argument check failed: %s", + "Implications failed:": "Implications failed:", + "Not enough arguments following: %s": "Not enough arguments following: %s", + "Invalid JSON config file: %s": "Invalid JSON config file: %s", + "Path to JSON config file": "Path to JSON config file", + "Show help": "Show help", + "Show version number": "Show version number", + "Did you mean %s?": "Did you mean %s?", + "Arguments %s and %s are mutually exclusive" : "Arguments %s and %s are mutually exclusive", + "Positionals:": "Positionals:", + "command": "command" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/es.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/es.json new file mode 100644 index 000000000..d7c8af9f8 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/es.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Comandos:", + "Options:": "Opciones:", + "Examples:": "Ejemplos:", + "boolean": "booleano", + "count": "cuenta", + "string": "cadena de caracteres", + "number": "número", + "array": "tabla", + "required": "requerido", + "default:": "defecto:", + "choices:": "selección:", + "aliases:": "alias:", + "generated-value": "valor-generado", + "Not enough non-option arguments: got %s, need at least %s": "Hacen falta argumentos no-opcionales: Número recibido %s, necesita por lo menos %s", + "Too many non-option arguments: got %s, maximum of %s": "Demasiados argumentos no-opcionales: Número recibido %s, máximo es %s", + "Missing argument value: %s": { + "one": "Falta argumento: %s", + "other": "Faltan argumentos: %s" + }, + "Missing required argument: %s": { + "one": "Falta argumento requerido: %s", + "other": "Faltan argumentos requeridos: %s" + }, + "Unknown argument: %s": { + "one": "Argumento desconocido: %s", + "other": "Argumentos desconocidos: %s" + }, + "Invalid values:": "Valores inválidos:", + "Argument: %s, Given: %s, Choices: %s": "Argumento: %s, Recibido: %s, Seleccionados: %s", + "Argument check failed: %s": "Verificación de argumento ha fallado: %s", + "Implications failed:": "Implicaciones fallidas:", + "Not enough arguments following: %s": "No hay suficientes argumentos después de: %s", + "Invalid JSON config file: %s": "Archivo de configuración JSON inválido: %s", + "Path to JSON config file": "Ruta al archivo de configuración JSON", + "Show help": "Muestra ayuda", + "Show version number": "Muestra número de versión", + "Did you mean %s?": "Quisiste decir %s?" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/fr.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/fr.json new file mode 100644 index 000000000..481f47e37 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/fr.json @@ -0,0 +1,37 @@ +{ + "Commands:": "Commandes:", + "Options:": "Options:", + "Examples:": "Exemples:", + "boolean": "booléen", + "count": "comptage", + "string": "chaine de caractère", + "number": "nombre", + "array": "tableau", + "required": "requis", + "default:": "défaut:", + "choices:": "choix:", + "generated-value": "valeur générée", + "Not enough non-option arguments: got %s, need at least %s": "Pas assez d'arguments non-option: reçu %s, besoin d'au moins %s", + "Too many non-option arguments: got %s, maximum of %s": "Trop d'arguments non-option: reçu %s, maximum %s", + "Missing argument value: %s": { + "one": "Argument manquant: %s", + "other": "Arguments manquants: %s" + }, + "Missing required argument: %s": { + "one": "Argument requis manquant: %s", + "other": "Arguments requis manquants: %s" + }, + "Unknown argument: %s": { + "one": "Argument inconnu: %s", + "other": "Arguments inconnus: %s" + }, + "Invalid values:": "Valeurs invalides:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Donné: %s, Choix: %s", + "Argument check failed: %s": "Echec de la vérification de l'argument: %s", + "Implications failed:": "Implications échouées:", + "Not enough arguments following: %s": "Pas assez d'arguments suivant: %s", + "Invalid JSON config file: %s": "Fichier de configuration JSON invalide: %s", + "Path to JSON config file": "Chemin du fichier de configuration JSON", + "Show help": "Affiche de l'aide", + "Show version number": "Affiche le numéro de version" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/hi.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/hi.json new file mode 100644 index 000000000..2cd677acb --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/hi.json @@ -0,0 +1,42 @@ +{ + "Commands:": "आदेश:", + "Options:": "विकल�प:", + "Examples:": "उदाहरण:", + "boolean": "सत�यता", + "count": "संख�या", + "string": "वर�णों का तार ", + "number": "अंक", + "array": "सरणी", + "required": "आवश�यक", + "default:": "डिफॉल�ट:", + "choices:": "विकल�प:", + "aliases:": "उपनाम:", + "generated-value": "उत�पन�न-मूल�य", + "Not enough non-option arguments: got %s, need at least %s": "पर�याप�त गैर-विकल�प तर�क प�राप�त नहीं: %s प�राप�त, कम से कम %s की आवश�यकता है", + "Too many non-option arguments: got %s, maximum of %s": "बह�त सारे गैर-विकल�प तर�क: %s प�राप�त, अधिकतम %s मान�य", + "Missing argument value: %s": { + "one": "क�छ तर�को के मूल�य ग�म हैं: %s", + "other": "क�छ तर�को के मूल�य ग�म हैं: %s" + }, + "Missing required argument: %s": { + "one": "आवश�यक तर�क ग�म हैं: %s", + "other": "आवश�यक तर�क ग�म हैं: %s" + }, + "Unknown argument: %s": { + "one": "अज�ञात तर�क प�राप�त: %s", + "other": "अज�ञात तर�क प�राप�त: %s" + }, + "Invalid values:": "अमान�य मूल�य:", + "Argument: %s, Given: %s, Choices: %s": "तर�क: %s, प�राप�त: %s, विकल�प: %s", + "Argument check failed: %s": "तर�क जांच विफल: %s", + "Implications failed:": "दि� ग� तर�क के लि� अतिरिक�त तर�क की अपेक�षा है:", + "Not enough arguments following: %s": "निम�नलिखित के बाद पर�याप�त तर�क नहीं प�राप�त: %s", + "Invalid JSON config file: %s": "अमान�य JSON config फाइल: %s", + "Path to JSON config file": "JSON config फाइल का पथ", + "Show help": "सहायता दिखा��", + "Show version number": "Version संख�या दिखा��", + "Did you mean %s?": "क�या आपका मतलब है %s?", + "Arguments %s and %s are mutually exclusive" : "तर�क %s और %s परस�पर अनन�य हैं", + "Positionals:": "स�थानीय:", + "command": "आदेश" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/hu.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/hu.json new file mode 100644 index 000000000..7b7d16606 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/hu.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Parancsok:", + "Options:": "Opciók:", + "Examples:": "Példák:", + "boolean": "boolean", + "count": "számláló", + "string": "szöveg", + "number": "szám", + "array": "tömb", + "required": "kötelező", + "default:": "alapértelmezett:", + "choices:": "lehetőségek:", + "aliases:": "aliaszok:", + "generated-value": "generált-érték", + "Not enough non-option arguments: got %s, need at least %s": "Nincs elég nem opcionális argumentum: %s van, legalább %s kell", + "Too many non-option arguments: got %s, maximum of %s": "Túl sok nem opciánlis argumentum van: %s van, maximum %s lehet", + "Missing argument value: %s": { + "one": "Hiányzó argumentum érték: %s", + "other": "Hiányzó argumentum értékek: %s" + }, + "Missing required argument: %s": { + "one": "Hiányzó kötelező argumentum: %s", + "other": "Hiányzó kötelező argumentumok: %s" + }, + "Unknown argument: %s": { + "one": "Ismeretlen argumentum: %s", + "other": "Ismeretlen argumentumok: %s" + }, + "Invalid values:": "Érvénytelen érték:", + "Argument: %s, Given: %s, Choices: %s": "Argumentum: %s, Megadott: %s, Lehetőségek: %s", + "Argument check failed: %s": "Argumentum ellenőrzés sikertelen: %s", + "Implications failed:": "Implikációk sikertelenek:", + "Not enough arguments following: %s": "Nem elég argumentum követi: %s", + "Invalid JSON config file: %s": "Érvénytelen JSON konfigurációs file: %s", + "Path to JSON config file": "JSON konfigurációs file helye", + "Show help": "Súgo megjelenítése", + "Show version number": "Verziószám megjelenítése", + "Did you mean %s?": "Erre gondoltál %s?" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/id.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/id.json new file mode 100644 index 000000000..87e441cd8 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/id.json @@ -0,0 +1,43 @@ + +{ + "Commands:": "Perintah:", + "Options:": "Pilihan:", + "Examples:": "Contoh:", + "boolean": "boolean", + "count": "jumlah", + "number": "nomor", + "string": "string", + "array": "larik", + "required": "diperlukan", + "default:": "bawaan:", + "aliases:": "istilah lain:", + "choices:": "pilihan:", + "generated-value": "nilai-yang-dihasilkan", + "Not enough non-option arguments: got %s, need at least %s": "Argumen wajib kurang: hanya %s, minimal %s", + "Too many non-option arguments: got %s, maximum of %s": "Terlalu banyak argumen wajib: ada %s, maksimal %s", + "Missing argument value: %s": { + "one": "Kurang argumen: %s", + "other": "Kurang argumen: %s" + }, + "Missing required argument: %s": { + "one": "Kurang argumen wajib: %s", + "other": "Kurang argumen wajib: %s" + }, + "Unknown argument: %s": { + "one": "Argumen tak diketahui: %s", + "other": "Argumen tak diketahui: %s" + }, + "Invalid values:": "Nilai-nilai tidak valid:", + "Argument: %s, Given: %s, Choices: %s": "Argumen: %s, Diberikan: %s, Pilihan: %s", + "Argument check failed: %s": "Pemeriksaan argument gagal: %s", + "Implications failed:": "Implikasi gagal:", + "Not enough arguments following: %s": "Kurang argumen untuk: %s", + "Invalid JSON config file: %s": "Berkas konfigurasi JSON tidak valid: %s", + "Path to JSON config file": "Alamat berkas konfigurasi JSON", + "Show help": "Lihat bantuan", + "Show version number": "Lihat nomor versi", + "Did you mean %s?": "Maksud Anda: %s?", + "Arguments %s and %s are mutually exclusive" : "Argumen %s dan %s saling eksklusif", + "Positionals:": "Posisional-posisional:", + "command": "perintah" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/it.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/it.json new file mode 100644 index 000000000..f9eb3756e --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/it.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Comandi:", + "Options:": "Opzioni:", + "Examples:": "Esempi:", + "boolean": "booleano", + "count": "contatore", + "string": "stringa", + "number": "numero", + "array": "vettore", + "required": "richiesto", + "default:": "predefinito:", + "choices:": "scelte:", + "aliases:": "alias:", + "generated-value": "valore generato", + "Not enough non-option arguments: got %s, need at least %s": "Numero insufficiente di argomenti non opzione: inseriti %s, richiesti almeno %s", + "Too many non-option arguments: got %s, maximum of %s": "Troppi argomenti non opzione: inseriti %s, massimo possibile %s", + "Missing argument value: %s": { + "one": "Argomento mancante: %s", + "other": "Argomenti mancanti: %s" + }, + "Missing required argument: %s": { + "one": "Argomento richiesto mancante: %s", + "other": "Argomenti richiesti mancanti: %s" + }, + "Unknown argument: %s": { + "one": "Argomento sconosciuto: %s", + "other": "Argomenti sconosciuti: %s" + }, + "Invalid values:": "Valori non validi:", + "Argument: %s, Given: %s, Choices: %s": "Argomento: %s, Richiesto: %s, Scelte: %s", + "Argument check failed: %s": "Controllo dell'argomento fallito: %s", + "Implications failed:": "Argomenti impliciti non soddisfatti:", + "Not enough arguments following: %s": "Argomenti insufficienti dopo: %s", + "Invalid JSON config file: %s": "File di configurazione JSON non valido: %s", + "Path to JSON config file": "Percorso del file di configurazione JSON", + "Show help": "Mostra la schermata di aiuto", + "Show version number": "Mostra il numero di versione", + "Did you mean %s?": "Intendi forse %s?" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ja.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ja.json new file mode 100644 index 000000000..64ee6d3fb --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ja.json @@ -0,0 +1,42 @@ +{ + "Commands:": "コマンド:", + "Options:": "オプション:", + "Examples:": "例:", + "boolean": "真�", + "count": "カウント", + "string": "文字列", + "number": "数値", + "array": "�列", + "required": "必須", + "default:": "デフォルト:", + "choices:": "�択������:", + "aliases:": "エイリアス:", + "generated-value": "生��れ�値", + "Not enough non-option arguments: got %s, need at least %s": "オプション����引数� %s 個���足�����。少���も %s 個�引数�必���:", + "Too many non-option arguments: got %s, maximum of %s": "オプション����引数� %s 個��多����。最大� %s 個����:", + "Missing argument value: %s": { + "one": "引数�見��り��ん: %s", + "other": "引数�見��り��ん: %s" + }, + "Missing required argument: %s": { + "one": "必須�引数�見��り��ん: %s", + "other": "必須�引数�見��り��ん: %s" + }, + "Unknown argument: %s": { + "one": "未知�引数��: %s", + "other": "未知�引数��: %s" + }, + "Invalid values:": "�正�値��:", + "Argument: %s, Given: %s, Choices: %s": "引数� %s ��。指定��る�� %s ���。�択������: %s", + "Argument check failed: %s": "引数��ェック�失敗����: %s", + "Implications failed:": "オプション�組������正�生����:", + "Not enough arguments following: %s": "次�引数��足�����。: %s", + "Invalid JSON config file: %s": "JSON�設定ファイル��正��: %s", + "Path to JSON config file": "JSON�設定ファイル���path", + "Show help": "ヘルプを表示", + "Show version number": "�ージョンを表示", + "Did you mean %s?": "も���� %s?", + "Arguments %s and %s are mutually exclusive" : "引数 %s � %s ��時�指定����ん", + "Positionals:": "�置:", + "command": "コマンド" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ko.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ko.json new file mode 100644 index 000000000..0eaeab2f8 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ko.json @@ -0,0 +1,42 @@ +{ + "Commands:": "명령:", + "Options:": "옵션:", + "Examples:": "예시:", + "boolean": "여부", + "count": "개수", + "string": "문�열", + "number": "숫�", + "array": "배열", + "required": "필수", + "default:": "기본:", + "choices:": "선�:", + "aliases:": "별칭:", + "generated-value": "�성� 값", + "Not enough non-option arguments: got %s, need at least %s": "옵션� 아닌 ��가 충분치 않습니다: %s개를 받았지만, �어� %s개는 필요합니다", + "Too many non-option arguments: got %s, maximum of %s": "옵션� 아닌 ��가 너무 많습니다: %s개를 받았지만, %s개 �하여야 합니다", + "Missing argument value: %s": { + "one": "��값� 받지 못했습니다: %s", + "other": "��값들� 받지 못했습니다: %s" + }, + "Missing required argument: %s": { + "one": "필수 ��를 받지 못했습니다: %s", + "other": "필수 ��들� 받지 못했습니다: %s" + }, + "Unknown argument: %s": { + "one": "알 수 없는 ��입니다: %s", + "other": "알 수 없는 ��들입니다: %s" + }, + "Invalid values:": "잘못� 값입니다:", + "Argument: %s, Given: %s, Choices: %s": "��: %s, 입력받� 값: %s, 선�지: %s", + "Argument check failed: %s": "유효하지 않� ��입니다: %s", + "Implications failed:": "옵션� 조합� 잘못�었습니다:", + "Not enough arguments following: %s": "��가 충분하게 주어지지 않았습니다: %s", + "Invalid JSON config file: %s": "유효하지 않� JSON 설정파�입니다: %s", + "Path to JSON config file": "JSON 설정파� 경로", + "Show help": "�움�� 보여�니다", + "Show version number": "버전 넘버를 보여�니다", + "Did you mean %s?": "찾고계신게 %s입니까?", + "Arguments %s and %s are mutually exclusive" : "%s와 %s ��는 같� 사용� 수 없습니다", + "Positionals:": "위치:", + "command": "명령" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nb.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nb.json new file mode 100644 index 000000000..55be1fbed --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nb.json @@ -0,0 +1,37 @@ +{ + "Commands:": "Kommandoer:", + "Options:": "Alternativer:", + "Examples:": "Eksempler:", + "boolean": "boolsk", + "count": "antall", + "string": "streng", + "number": "nummer", + "array": "matrise", + "required": "obligatorisk", + "default:": "standard:", + "choices:": "valg:", + "generated-value": "generert-verdi", + "Not enough non-option arguments: got %s, need at least %s": "Ikke nok ikke-alternativ argumenter: fikk %s, trenger minst %s", + "Too many non-option arguments: got %s, maximum of %s": "For mange ikke-alternativ argumenter: fikk %s, maksimum %s", + "Missing argument value: %s": { + "one": "Mangler argument verdi: %s", + "other": "Mangler argument verdier: %s" + }, + "Missing required argument: %s": { + "one": "Mangler obligatorisk argument: %s", + "other": "Mangler obligatoriske argumenter: %s" + }, + "Unknown argument: %s": { + "one": "Ukjent argument: %s", + "other": "Ukjente argumenter: %s" + }, + "Invalid values:": "Ugyldige verdier:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gitt: %s, Valg: %s", + "Argument check failed: %s": "Argumentsjekk mislyktes: %s", + "Implications failed:": "Konsekvensene mislyktes:", + "Not enough arguments following: %s": "Ikke nok følgende argumenter: %s", + "Invalid JSON config file: %s": "Ugyldig JSON konfigurasjonsfil: %s", + "Path to JSON config file": "Bane til JSON konfigurasjonsfil", + "Show help": "Vis hjelp", + "Show version number": "Vis versjonsnummer" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nl.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nl.json new file mode 100644 index 000000000..1d144724e --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nl.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Opdrachten:", + "Options:": "Opties:", + "Examples:": "Voorbeelden:", + "boolean": "boolean", + "count": "aantal", + "string": "text", + "number": "nummer", + "array": "lijst", + "required": "verplicht", + "default:": "standaard:", + "choices:": "keuzes:", + "aliases:": "aliassen:", + "generated-value": "gegenereerde waarde", + "Not enough non-option arguments: got %s, need at least %s": "Niet genoeg non-optie argumenten. Gekregen: %s, minstens nodig: %s", + "Too many non-option arguments: got %s, maximum of %s": "Te veel non-optie argumenten. Gekregen: %s, maximum: %s", + "Missing argument value: %s": { + "one": "Missing argument value: %s", + "other": "Missing argument values: %s" + }, + "Missing required argument: %s": { + "one": "Missend verplichte argument: %s", + "other": "Missende verplichte argumenten: %s" + }, + "Unknown argument: %s": { + "one": "Onbekend argument: %s", + "other": "Onbekende argumenten: %s" + }, + "Invalid values:": "Ongeldige waardes:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gegeven: %s, Keuzes: %s", + "Argument check failed: %s": "Argument check mislukt: %s", + "Implications failed:": "Implicaties mislukt:", + "Not enough arguments following: %s": "Niet genoeg argumenten na: %s", + "Invalid JSON config file: %s": "Ongeldig JSON configuratiebestand: %s", + "Path to JSON config file": "Pad naar JSON configuratiebestand", + "Show help": "Toon help", + "Show version number": "Toon versie nummer", + "Did you mean %s?": "Bedoelde u misschien %s?", + "Arguments %s and %s are mutually exclusive": "Argumenten %s en %s zijn onderling uitsluitend", + "Positionals:": "Positie-afhankelijke argumenten", + "command": "commando" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nn.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nn.json new file mode 100644 index 000000000..5a3c9514d --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/nn.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Kommandoar:", + "Options:": "Alternativ:", + "Examples:": "Døme:", + "boolean": "boolsk", + "count": "mengd", + "string": "streng", + "number": "nummer", + "array": "matrise", + "required": "obligatorisk", + "default:": "standard:", + "choices:": "val:", + "generated-value": "generert-verdi", + "Not enough non-option arguments: got %s, need at least %s": + "Ikkje nok ikkje-alternativ argument: fekk %s, treng minst %s", + "Too many non-option arguments: got %s, maximum of %s": + "For mange ikkje-alternativ argument: fekk %s, maksimum %s", + "Missing argument value: %s": { + "one": "Manglar argumentverdi: %s", + "other": "Manglar argumentverdiar: %s" + }, + "Missing required argument: %s": { + "one": "Manglar obligatorisk argument: %s", + "other": "Manglar obligatoriske argument: %s" + }, + "Unknown argument: %s": { + "one": "Ukjent argument: %s", + "other": "Ukjende argument: %s" + }, + "Invalid values:": "Ugyldige verdiar:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gjeve: %s, Val: %s", + "Argument check failed: %s": "Argumentsjekk mislukkast: %s", + "Implications failed:": "Konsekvensane mislukkast:", + "Not enough arguments following: %s": "Ikkje nok fylgjande argument: %s", + "Invalid JSON config file: %s": "Ugyldig JSON konfigurasjonsfil: %s", + "Path to JSON config file": "Bane til JSON konfigurasjonsfil", + "Show help": "Vis hjelp", + "Show version number": "Vis versjonsnummer" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pirate.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pirate.json new file mode 100644 index 000000000..dcb5cb753 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pirate.json @@ -0,0 +1,13 @@ +{ + "Commands:": "Choose yer command:", + "Options:": "Options for me hearties!", + "Examples:": "Ex. marks the spot:", + "required": "requi-yar-ed", + "Missing required argument: %s": { + "one": "Ye be havin' to set the followin' argument land lubber: %s", + "other": "Ye be havin' to set the followin' arguments land lubber: %s" + }, + "Show help": "Parlay this here code of conduct", + "Show version number": "'Tis the version ye be askin' fer", + "Arguments %s and %s are mutually exclusive" : "Yon scurvy dogs %s and %s be as bad as rum and a prudish wench" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pl.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pl.json new file mode 100644 index 000000000..6926a4548 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pl.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Polecenia:", + "Options:": "Opcje:", + "Examples:": "Przykłady:", + "boolean": "boolean", + "count": "ilość", + "string": "ciąg znaków", + "number": "liczba", + "array": "tablica", + "required": "wymagany", + "default:": "domyślny:", + "choices:": "dostępne:", + "aliases:": "aliasy:", + "generated-value": "wygenerowana-wartość", + "Not enough non-option arguments: got %s, need at least %s": "Niewystarczająca ilość argumentów: otrzymano %s, wymagane co najmniej %s", + "Too many non-option arguments: got %s, maximum of %s": "Zbyt duża ilość argumentów: otrzymano %s, wymagane co najwyżej %s", + "Missing argument value: %s": { + "one": "Brak wartości dla argumentu: %s", + "other": "Brak wartości dla argumentów: %s" + }, + "Missing required argument: %s": { + "one": "Brak wymaganego argumentu: %s", + "other": "Brak wymaganych argumentów: %s" + }, + "Unknown argument: %s": { + "one": "Nieznany argument: %s", + "other": "Nieznane argumenty: %s" + }, + "Invalid values:": "Nieprawidłowe wartości:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Otrzymano: %s, Dostępne: %s", + "Argument check failed: %s": "Weryfikacja argumentów nie powiodła się: %s", + "Implications failed:": "Założenia nie zostały spełnione:", + "Not enough arguments following: %s": "Niewystarczająca ilość argumentów następujących po: %s", + "Invalid JSON config file: %s": "Nieprawidłowy plik konfiguracyjny JSON: %s", + "Path to JSON config file": "Ścieżka do pliku konfiguracyjnego JSON", + "Show help": "Pokaż pomoc", + "Show version number": "Pokaż numer wersji", + "Did you mean %s?": "Czy chodziło Ci o %s?", + "Arguments %s and %s are mutually exclusive": "Argumenty %s i %s wzajemnie się wykluczają", + "Positionals:": "Pozycyjne:", + "command": "polecenie" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pt.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pt.json new file mode 100644 index 000000000..75c3921c8 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pt.json @@ -0,0 +1,38 @@ +{ + "Commands:": "Comandos:", + "Options:": "Opções:", + "Examples:": "Exemplos:", + "boolean": "boolean", + "count": "contagem", + "string": "cadeia de caracteres", + "number": "número", + "array": "arranjo", + "required": "requerido", + "default:": "padrão:", + "choices:": "escolhas:", + "generated-value": "valor-gerado", + "Not enough non-option arguments: got %s, need at least %s": "Argumentos insuficientes não opcionais: Argumento %s, necessário pelo menos %s", + "Too many non-option arguments: got %s, maximum of %s": "Excesso de argumentos não opcionais: recebido %s, máximo de %s", + "Missing argument value: %s": { + "one": "Falta valor de argumento: %s", + "other": "Falta valores de argumento: %s" + }, + "Missing required argument: %s": { + "one": "Falta argumento obrigatório: %s", + "other": "Faltando argumentos obrigatórios: %s" + }, + "Unknown argument: %s": { + "one": "Argumento desconhecido: %s", + "other": "Argumentos desconhecidos: %s" + }, + "Invalid values:": "Valores inválidos:", + "Argument: %s, Given: %s, Choices: %s": "Argumento: %s, Dado: %s, Escolhas: %s", + "Argument check failed: %s": "Verificação de argumento falhou: %s", + "Implications failed:": "Implicações falharam:", + "Not enough arguments following: %s": "Insuficientes argumentos a seguir: %s", + "Invalid JSON config file: %s": "Arquivo de configuração em JSON esta inválido: %s", + "Path to JSON config file": "Caminho para o arquivo de configuração em JSON", + "Show help": "Mostra ajuda", + "Show version number": "Mostra número de versão", + "Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pt_BR.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pt_BR.json new file mode 100644 index 000000000..904cb66eb --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/pt_BR.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Comandos:", + "Options:": "Opções:", + "Examples:": "Exemplos:", + "boolean": "booleano", + "count": "contagem", + "string": "string", + "number": "número", + "array": "array", + "required": "obrigatório", + "default:": "padrão:", + "choices:": "opções:", + "aliases:": "sinônimos:", + "generated-value": "valor-gerado", + "Not enough non-option arguments: got %s, need at least %s": "Argumentos insuficientes: Argumento %s, necessário pelo menos %s", + "Too many non-option arguments: got %s, maximum of %s": "Excesso de argumentos: recebido %s, máximo de %s", + "Missing argument value: %s": { + "one": "Falta valor de argumento: %s", + "other": "Falta valores de argumento: %s" + }, + "Missing required argument: %s": { + "one": "Falta argumento obrigatório: %s", + "other": "Faltando argumentos obrigatórios: %s" + }, + "Unknown argument: %s": { + "one": "Argumento desconhecido: %s", + "other": "Argumentos desconhecidos: %s" + }, + "Invalid values:": "Valores inválidos:", + "Argument: %s, Given: %s, Choices: %s": "Argumento: %s, Dado: %s, Opções: %s", + "Argument check failed: %s": "Verificação de argumento falhou: %s", + "Implications failed:": "Implicações falharam:", + "Not enough arguments following: %s": "Argumentos insuficientes a seguir: %s", + "Invalid JSON config file: %s": "Arquivo JSON de configuração inválido: %s", + "Path to JSON config file": "Caminho para o arquivo JSON de configuração", + "Show help": "Exibe ajuda", + "Show version number": "Exibe a versão", + "Did you mean %s?": "Você quis dizer %s?", + "Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos", + "Positionals:": "Posicionais:", + "command": "comando" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ru.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ru.json new file mode 100644 index 000000000..cb7b88b49 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/ru.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Команды:", + "Options:": "Опции:", + "Examples:": "Примеры:", + "boolean": "булевый тип", + "count": "под�чет", + "string": "�троковой тип", + "number": "чи�ло", + "array": "ма��ив", + "required": "необходимо", + "default:": "по умолчанию:", + "choices:": "возможно�ти:", + "aliases:": "алиа�ы:", + "generated-value": "генерированное значение", + "Not enough non-option arguments: got %s, need at least %s": "�едо�таточно неопционных аргументов: е�ть %s, нужно как минимум %s", + "Too many non-option arguments: got %s, maximum of %s": "Слишком много неопционных аргументов: е�ть %s, мак�имум допу�тимо %s", + "Missing argument value: %s": { + "one": "�е хватает значени� аргумента: %s", + "other": "�е хватает значений аргументов: %s" + }, + "Missing required argument: %s": { + "one": "�е хватает необходимого аргумента: %s", + "other": "�е хватает необходимых аргументов: %s" + }, + "Unknown argument: %s": { + "one": "�еизве�тный аргумент: %s", + "other": "�еизве�тные аргументы: %s" + }, + "Invalid values:": "�едей�твительные значени�:", + "Argument: %s, Given: %s, Choices: %s": "�ргумент: %s, Данное значение: %s, Возможно�ти: %s", + "Argument check failed: %s": "Проверка аргументов не удала�ь: %s", + "Implications failed:": "Данный аргумент требует �ледующий дополнительный аргумент:", + "Not enough arguments following: %s": "�едо�таточно �ледующих аргументов: %s", + "Invalid JSON config file: %s": "�едей�твительный файл конфигурации JSON: %s", + "Path to JSON config file": "Путь к файлу конфигурации JSON", + "Show help": "Показать помощь", + "Show version number": "Показать номер вер�ии", + "Did you mean %s?": "Вы имели в виду %s?" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/th.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/th.json new file mode 100644 index 000000000..3f08dcd23 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/th.json @@ -0,0 +1,39 @@ +{ + "Commands:": "คอมมาน", + "Options:": "ออฟชั่น", + "Examples:": "ตัวอย่าง", + "boolean": "บูลีน", + "count": "นับ", + "string": "สตริง", + "number": "ตัวเลข", + "array": "อาเรย์", + "required": "จำเป็น", + "default:": "ค่าเริ่มต้น", + "choices:": "ตัวเลือ�", + "aliases:": "เอเลียส", + "generated-value": "ค่าที่ถู�สร้างขึ้น", + "Not enough non-option arguments: got %s, need at least %s": "ใส่อาร์�ิวเมนต์ไม่ครบตามจำนวนที่�ำหนด: ใส่ค่ามาจำนวน %s ค่า, �ต่ต้อง�ารอย่างน้อย %s ค่า", + "Too many non-option arguments: got %s, maximum of %s": "ใส่อาร์�ิวเมนต์เ�ินจำนวนที่�ำหนด: ใส่ค่ามาจำนวน %s ค่า, �ต่ต้อง�ารมา�ที่สุด %s ค่า", + "Missing argument value: %s": { + "one": "ค่าอาร์�ิวเมนต์ที่ขาดไป: %s", + "other": "ค่าอาร์�ิวเมนต์ที่ขาดไป: %s" + }, + "Missing required argument: %s": { + "one": "อาร์�ิวเมนต์จำเป็นที่ขาดไป: %s", + "other": "อาร์�ิวเมนต์จำเป็นที่ขาดไป: %s" + }, + "Unknown argument: %s": { + "one": "อาร์�ิวเมนต์ที่ไม่รู้จั�: %s", + "other": "อาร์�ิวเมนต์ที่ไม่รู้จั�: %s" + }, + "Invalid values:": "ค่าไม่ถู�ต้อง:", + "Argument: %s, Given: %s, Choices: %s": "อาร์�ิวเมนต์: %s, ได้รับ: %s, ตัวเลือ�: %s", + "Argument check failed: %s": "ตรวจสอบพบอาร์�ิวเมนต์ที่ไม่ถู�ต้อง: %s", + "Implications failed:": "Implications ไม่สำเร็จ:", + "Not enough arguments following: %s": "ใส่อาร์�ิวเมนต์ไม่ครบ: %s", + "Invalid JSON config file: %s": "ไฟล์คอนฟิค JSON ไม่ถู�ต้อง: %s", + "Path to JSON config file": "พาทไฟล์คอนฟิค JSON", + "Show help": "ขอความช่วยเหลือ", + "Show version number": "�สดงตัวเลขเวอร์ชั่น", + "Did you mean %s?": "คุณหมายถึง %s?" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/tr.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/tr.json new file mode 100644 index 000000000..9b06c52a8 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/tr.json @@ -0,0 +1,41 @@ +{ + "Commands:": "Komutlar:", + "Options:": "Seçenekler:", + "Examples:": "Örnekler:", + "boolean": "boolean", + "count": "sayı", + "string": "string", + "number": "numara", + "array": "array", + "required": "zorunlu", + "default:": "varsayılan:", + "choices:": "seçimler:", + "aliases:": "takma adlar:", + "generated-value": "oluşturulan-değer", + "Not enough non-option arguments: got %s, need at least %s": "Seçenek dışı argümanlar yetersiz: %s bulundu, %s gerekli", + "Too many non-option arguments: got %s, maximum of %s": "Seçenek dışı argümanlar gereğinden fazla: %s bulundu, azami %s", + "Missing argument value: %s": { + "one": "Eksik argüman değeri: %s", + "other": "Eksik argüman değerleri: %s" + }, + "Missing required argument: %s": { + "one": "Eksik zorunlu argüman: %s", + "other": "Eksik zorunlu argümanlar: %s" + }, + "Unknown argument: %s": { + "one": "Bilinmeyen argüman: %s", + "other": "Bilinmeyen argümanlar: %s" + }, + "Invalid values:": "Geçersiz değerler:", + "Argument: %s, Given: %s, Choices: %s": "Argüman: %s, Verilen: %s, Seçimler: %s", + "Argument check failed: %s": "Argüman kontrolü başarısız oldu: %s", + "Implications failed:": "Sonuçlar başarısız oldu:", + "Not enough arguments following: %s": "%s için yeterli argüman bulunamadı", + "Invalid JSON config file: %s": "Geçersiz JSON yapılandırma dosyası: %s", + "Path to JSON config file": "JSON yapılandırma dosya konumu", + "Show help": "Yardım detaylarını göster", + "Show version number": "Versiyon detaylarını göster", + "Did you mean %s?": "Bunu mu demek istediniz: %s?", + "Positionals:": "Sıralılar:", + "command": "komut" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/zh_CN.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/zh_CN.json new file mode 100644 index 000000000..03a3d94f0 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/zh_CN.json @@ -0,0 +1,41 @@ +{ + "Commands:": "命令:", + "Options:": "选项:", + "Examples:": "示例:", + "boolean": "布尔", + "count": "计数", + "string": "字符串", + "number": "数字", + "array": "数组", + "required": "必需", + "default:": "默认值:", + "choices:": "�选值:", + "generated-value": "生�的值", + "Not enough non-option arguments: got %s, need at least %s": "缺少 non-option �数:传入了 %s 个, 至少需� %s 个", + "Too many non-option arguments: got %s, maximum of %s": "non-option �数过多:传入了 %s 个, 最大�许 %s 个", + "Missing argument value: %s": { + "one": "没有给此选项指定值:%s", + "other": "没有给这些选项指定值:%s" + }, + "Missing required argument: %s": { + "one": "缺少必须的选项:%s", + "other": "缺少这些必须的选项:%s" + }, + "Unknown argument: %s": { + "one": "无法识别的选项:%s", + "other": "无法识别这些选项:%s" + }, + "Invalid values:": "无效的选项值:", + "Argument: %s, Given: %s, Choices: %s": "选项�称: %s, 传入的值: %s, �选的值:%s", + "Argument check failed: %s": "选项值验�失败:%s", + "Implications failed:": "缺少�赖的选项:", + "Not enough arguments following: %s": "没有�供足够的值给此选项:%s", + "Invalid JSON config file: %s": "无效的 JSON �置文件:%s", + "Path to JSON config file": "JSON �置文件的路径", + "Show help": "显示帮助信�", + "Show version number": "显示版本�", + "Did you mean %s?": "是指 %s?", + "Arguments %s and %s are mutually exclusive" : "选项 %s 和 %s 是互斥的", + "Positionals:": "�置:", + "command": "命令" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/zh_TW.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/zh_TW.json new file mode 100644 index 000000000..12498888a --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/locales/zh_TW.json @@ -0,0 +1,40 @@ +{ + "Commands:": "命令:", + "Options:": "�項:", + "Examples:": "例:", + "boolean": "布林", + "count": "次數", + "string": "字串", + "number": "數字", + "array": "陣列", + "required": "必須", + "default:": "�設值:", + "choices:": "��值:", + "aliases:": "別�:", + "generated-value": "生�的值", + "Not enough non-option arguments: got %s, need at least %s": "non-option 引數�足:�傳入了 %s 個, 至少� %s 個", + "Too many non-option arguments: got %s, maximum of %s": "non-option 引數�多:傳入了 %s 個, 但最多 %s 個", + "Missing argument value: %s": { + "one": "此引數無指定值:%s", + "other": "這些引數無指定值:%s" + }, + "Missing required argument: %s": { + "one": "缺少必須的引數:%s", + "other": "缺少這些必須的引數:%s" + }, + "Unknown argument: %s": { + "one": "未知的引數:%s", + "other": "未知的這些引數:%s" + }, + "Invalid values:": "無效的�項值:", + "Argument: %s, Given: %s, Choices: %s": "引數�稱: %s, 傳入的值: %s, ��的值:%s", + "Argument check failed: %s": "引數驗證失敗:%s", + "Implications failed:": "缺少�賴的�項:", + "Not enough arguments following: %s": "沒有�供足夠的值給此引數:%s", + "Invalid JSON config file: %s": "無效的 JSON 設置文件:%s", + "Path to JSON config file": "JSON 設置文件的路徑", + "Show help": "顯示說明", + "Show version number": "顯示版本", + "Did you mean %s?": "是指 %s?", + "Arguments %s and %s are mutually exclusive" : "引數 %s 和 %s 是互斥的" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/package.json b/WebServer/node_modules/yargs-unparser/node_modules/yargs/package.json new file mode 100644 index 000000000..8f9fa772e --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/package.json @@ -0,0 +1,110 @@ +{ + "_args": [ + [ + "yargs@12.0.5", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "yargs@12.0.5", + "_id": "yargs@12.0.5", + "_inBundle": false, + "_integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "_location": "/yargs-unparser/yargs", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "yargs@12.0.5", + "name": "yargs", + "escapedName": "yargs", + "rawSpec": "12.0.5", + "saveSpec": null, + "fetchSpec": "12.0.5" + }, + "_requiredBy": [ + "/yargs-unparser" + ], + "_resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "_spec": "12.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "bugs": { + "url": "https://github.com/yargs/yargs/issues" + }, + "contributors": [ + { + "name": "Yargs Contributors", + "url": "https://github.com/yargs/yargs/graphs/contributors" + } + ], + "dependencies": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + }, + "description": "yargs the modern, pirate-themed, successor to optimist.", + "devDependencies": { + "chai": "^4.1.2", + "chalk": "^1.1.3", + "coveralls": "^3.0.2", + "cpr": "^2.0.0", + "cross-spawn": "^6.0.4", + "es6-promise": "^4.0.2", + "hashish": "0.0.4", + "mocha": "^5.1.1", + "nyc": "^11.7.3", + "rimraf": "^2.5.0", + "standard": "^11.0.1", + "standard-version": "^4.2.0", + "which": "^1.2.9", + "yargs-test-extends": "^1.0.1" + }, + "engine": { + "node": ">=6" + }, + "files": [ + "index.js", + "yargs.js", + "lib", + "locales", + "completion.sh.hbs", + "LICENSE" + ], + "homepage": "https://yargs.js.org/", + "keywords": [ + "argument", + "args", + "option", + "parser", + "parsing", + "cli", + "command" + ], + "license": "MIT", + "main": "./index.js", + "name": "yargs", + "repository": { + "type": "git", + "url": "git+https://github.com/yargs/yargs.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "pretest": "standard", + "release": "standard-version", + "test": "nyc --cache mocha --require ./test/before.js --timeout=8000 --check-leaks" + }, + "standard": { + "ignore": [ + "**/example/**" + ] + }, + "version": "12.0.5" +} diff --git a/WebServer/node_modules/yargs-unparser/node_modules/yargs/yargs.js b/WebServer/node_modules/yargs-unparser/node_modules/yargs/yargs.js new file mode 100644 index 000000000..24c8e99ce --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/node_modules/yargs/yargs.js @@ -0,0 +1,1190 @@ +'use strict' +const argsert = require('./lib/argsert') +const fs = require('fs') +const Command = require('./lib/command') +const Completion = require('./lib/completion') +const Parser = require('yargs-parser') +const path = require('path') +const Usage = require('./lib/usage') +const Validation = require('./lib/validation') +const Y18n = require('y18n') +const objFilter = require('./lib/obj-filter') +const setBlocking = require('set-blocking') +const applyExtends = require('./lib/apply-extends') +const middlewareFactory = require('./lib/middleware') +const YError = require('./lib/yerror') + +exports = module.exports = Yargs +function Yargs (processArgs, cwd, parentRequire) { + processArgs = processArgs || [] // handle calling yargs(). + + const self = {} + let command = null + let completion = null + let groups = {} + let globalMiddleware = [] + let output = '' + let preservedGroups = {} + let usage = null + let validation = null + + const y18n = Y18n({ + directory: path.resolve(__dirname, './locales'), + updateFiles: false + }) + + self.middleware = middlewareFactory(globalMiddleware, self) + + if (!cwd) cwd = process.cwd() + + self.scriptName = function scriptName (scriptName) { + self.$0 = scriptName + return self + } + + // ignore the node bin, specify this in your + // bin file with #!/usr/bin/env node + if (/\b(node|iojs|electron)(\.exe)?$/.test(process.argv[0])) { + self.$0 = process.argv.slice(1, 2) + } else { + self.$0 = process.argv.slice(0, 1) + } + + self.$0 = self.$0 + .map((x, i) => { + const b = rebase(cwd, x) + return x.match(/^(\/|([a-zA-Z]:)?\\)/) && b.length < x.length ? b : x + }) + .join(' ').trim() + + if (process.env._ !== undefined && process.argv[1] === process.env._) { + self.$0 = process.env._.replace( + `${path.dirname(process.execPath)}/`, '' + ) + } + + // use context object to keep track of resets, subcommand execution, etc + // submodules should modify and check the state of context as necessary + const context = { resets: -1, commands: [], fullCommands: [], files: [] } + self.getContext = () => context + + // puts yargs back into an initial state. any keys + // that have been set to "global" will not be reset + // by this action. + let options + self.resetOptions = self.reset = function resetOptions (aliases) { + context.resets++ + aliases = aliases || {} + options = options || {} + // put yargs back into an initial state, this + // logic is used to build a nested command + // hierarchy. + const tmpOptions = {} + tmpOptions.local = options.local ? options.local : [] + tmpOptions.configObjects = options.configObjects ? options.configObjects : [] + + // if a key has been explicitly set as local, + // we should reset it before passing options to command. + const localLookup = {} + tmpOptions.local.forEach((l) => { + localLookup[l] = true + ;(aliases[l] || []).forEach((a) => { + localLookup[a] = true + }) + }) + + // preserve all groups not set to local. + preservedGroups = Object.keys(groups).reduce((acc, groupName) => { + const keys = groups[groupName].filter(key => !(key in localLookup)) + if (keys.length > 0) { + acc[groupName] = keys + } + return acc + }, {}) + // groups can now be reset + groups = {} + + const arrayOptions = [ + 'array', 'boolean', 'string', 'skipValidation', + 'count', 'normalize', 'number', + 'hiddenOptions' + ] + + const objectOptions = [ + 'narg', 'key', 'alias', 'default', 'defaultDescription', + 'config', 'choices', 'demandedOptions', 'demandedCommands', 'coerce' + ] + + arrayOptions.forEach((k) => { + tmpOptions[k] = (options[k] || []).filter(k => !localLookup[k]) + }) + + objectOptions.forEach((k) => { + tmpOptions[k] = objFilter(options[k], (k, v) => !localLookup[k]) + }) + + tmpOptions.envPrefix = options.envPrefix + options = tmpOptions + + // if this is the first time being executed, create + // instances of all our helpers -- otherwise just reset. + usage = usage ? usage.reset(localLookup) : Usage(self, y18n) + validation = validation ? validation.reset(localLookup) : Validation(self, usage, y18n) + command = command ? command.reset() : Command(self, usage, validation, globalMiddleware) + if (!completion) completion = Completion(self, usage, command) + + completionCommand = null + output = '' + exitError = null + hasOutput = false + self.parsed = false + + return self + } + self.resetOptions() + + // temporary hack: allow "freezing" of reset-able state for parse(msg, cb) + let frozen + function freeze () { + frozen = {} + frozen.options = options + frozen.configObjects = options.configObjects.slice(0) + frozen.exitProcess = exitProcess + frozen.groups = groups + usage.freeze() + validation.freeze() + command.freeze() + frozen.strict = strict + frozen.completionCommand = completionCommand + frozen.output = output + frozen.exitError = exitError + frozen.hasOutput = hasOutput + frozen.parsed = self.parsed + } + function unfreeze () { + options = frozen.options + options.configObjects = frozen.configObjects + exitProcess = frozen.exitProcess + groups = frozen.groups + output = frozen.output + exitError = frozen.exitError + hasOutput = frozen.hasOutput + self.parsed = frozen.parsed + usage.unfreeze() + validation.unfreeze() + command.unfreeze() + strict = frozen.strict + completionCommand = frozen.completionCommand + parseFn = null + parseContext = null + frozen = undefined + } + + self.boolean = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('boolean', keys) + return self + } + + self.array = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('array', keys) + return self + } + + self.number = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('number', keys) + return self + } + + self.normalize = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('normalize', keys) + return self + } + + self.count = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('count', keys) + return self + } + + self.string = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('string', keys) + return self + } + + self.requiresArg = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintObject(self.nargs, false, 'narg', keys, 1) + return self + } + + self.skipValidation = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('skipValidation', keys) + return self + } + + function populateParserHintArray (type, keys, value) { + keys = [].concat(keys) + keys.forEach((key) => { + options[type].push(key) + }) + } + + self.nargs = function (key, value) { + argsert(' [number]', [key, value], arguments.length) + populateParserHintObject(self.nargs, false, 'narg', key, value) + return self + } + + self.choices = function (key, value) { + argsert(' [string|array]', [key, value], arguments.length) + populateParserHintObject(self.choices, true, 'choices', key, value) + return self + } + + self.alias = function (key, value) { + argsert(' [string|array]', [key, value], arguments.length) + populateParserHintObject(self.alias, true, 'alias', key, value) + return self + } + + // TODO: actually deprecate self.defaults. + self.default = self.defaults = function (key, value, defaultDescription) { + argsert(' [*] [string]', [key, value, defaultDescription], arguments.length) + if (defaultDescription) options.defaultDescription[key] = defaultDescription + if (typeof value === 'function') { + if (!options.defaultDescription[key]) options.defaultDescription[key] = usage.functionDescription(value) + value = value.call() + } + populateParserHintObject(self.default, false, 'default', key, value) + return self + } + + self.describe = function (key, desc) { + argsert(' [string]', [key, desc], arguments.length) + populateParserHintObject(self.describe, false, 'key', key, true) + usage.describe(key, desc) + return self + } + + self.demandOption = function (keys, msg) { + argsert(' [string]', [keys, msg], arguments.length) + populateParserHintObject(self.demandOption, false, 'demandedOptions', keys, msg) + return self + } + + self.coerce = function (keys, value) { + argsert(' [function]', [keys, value], arguments.length) + populateParserHintObject(self.coerce, false, 'coerce', keys, value) + return self + } + + function populateParserHintObject (builder, isArray, type, key, value) { + if (Array.isArray(key)) { + // an array of keys with one value ['x', 'y', 'z'], function parse () {} + const temp = {} + key.forEach((k) => { + temp[k] = value + }) + builder(temp) + } else if (typeof key === 'object') { + // an object of key value pairs: {'x': parse () {}, 'y': parse() {}} + Object.keys(key).forEach((k) => { + builder(k, key[k]) + }) + } else { + // a single key value pair 'x', parse() {} + if (isArray) { + options[type][key] = (options[type][key] || []).concat(value) + } else { + options[type][key] = value + } + } + } + + function deleteFromParserHintObject (optionKey) { + // delete from all parsing hints: + // boolean, array, key, alias, etc. + Object.keys(options).forEach((hintKey) => { + const hint = options[hintKey] + if (Array.isArray(hint)) { + if (~hint.indexOf(optionKey)) hint.splice(hint.indexOf(optionKey), 1) + } else if (typeof hint === 'object') { + delete hint[optionKey] + } + }) + // now delete the description from usage.js. + delete usage.getDescriptions()[optionKey] + } + + self.config = function config (key, msg, parseFn) { + argsert('[object|string] [string|function] [function]', [key, msg, parseFn], arguments.length) + // allow a config object to be provided directly. + if (typeof key === 'object') { + key = applyExtends(key, cwd) + options.configObjects = (options.configObjects || []).concat(key) + return self + } + + // allow for a custom parsing function. + if (typeof msg === 'function') { + parseFn = msg + msg = null + } + + key = key || 'config' + self.describe(key, msg || usage.deferY18nLookup('Path to JSON config file')) + ;(Array.isArray(key) ? key : [key]).forEach((k) => { + options.config[k] = parseFn || true + }) + + return self + } + + self.example = function (cmd, description) { + argsert(' [string]', [cmd, description], arguments.length) + usage.example(cmd, description) + return self + } + + self.command = function (cmd, description, builder, handler, middlewares) { + argsert(' [string|boolean] [function|object] [function] [array]', [cmd, description, builder, handler, middlewares], arguments.length) + command.addHandler(cmd, description, builder, handler, middlewares) + return self + } + + self.commandDir = function (dir, opts) { + argsert(' [object]', [dir, opts], arguments.length) + const req = parentRequire || require + command.addDirectory(dir, self.getContext(), req, require('get-caller-file')(), opts) + return self + } + + // TODO: deprecate self.demand in favor of + // .demandCommand() .demandOption(). + self.demand = self.required = self.require = function demand (keys, max, msg) { + // you can optionally provide a 'max' key, + // which will raise an exception if too many '_' + // options are provided. + if (Array.isArray(max)) { + max.forEach((key) => { + self.demandOption(key, msg) + }) + max = Infinity + } else if (typeof max !== 'number') { + msg = max + max = Infinity + } + + if (typeof keys === 'number') { + self.demandCommand(keys, max, msg, msg) + } else if (Array.isArray(keys)) { + keys.forEach((key) => { + self.demandOption(key, msg) + }) + } else { + if (typeof msg === 'string') { + self.demandOption(keys, msg) + } else if (msg === true || typeof msg === 'undefined') { + self.demandOption(keys) + } + } + + return self + } + + self.demandCommand = function demandCommand (min, max, minMsg, maxMsg) { + argsert('[number] [number|string] [string|null|undefined] [string|null|undefined]', [min, max, minMsg, maxMsg], arguments.length) + + if (typeof min === 'undefined') min = 1 + + if (typeof max !== 'number') { + minMsg = max + max = Infinity + } + + self.global('_', false) + + options.demandedCommands._ = { + min, + max, + minMsg, + maxMsg + } + + return self + } + + self.getDemandedOptions = () => { + argsert([], 0) + return options.demandedOptions + } + + self.getDemandedCommands = () => { + argsert([], 0) + return options.demandedCommands + } + + self.implies = function (key, value) { + argsert(' [number|string|array]', [key, value], arguments.length) + validation.implies(key, value) + return self + } + + self.conflicts = function (key1, key2) { + argsert(' [string|array]', [key1, key2], arguments.length) + validation.conflicts(key1, key2) + return self + } + + self.usage = function (msg, description, builder, handler) { + argsert(' [string|boolean] [function|object] [function]', [msg, description, builder, handler], arguments.length) + + if (description !== undefined) { + // .usage() can be used as an alias for defining + // a default command. + if ((msg || '').match(/^\$0( |$)/)) { + return self.command(msg, description, builder, handler) + } else { + throw new YError('.usage() description must start with $0 if being used as alias for .command()') + } + } else { + usage.usage(msg) + return self + } + } + + self.epilogue = self.epilog = function (msg) { + argsert('', [msg], arguments.length) + usage.epilog(msg) + return self + } + + self.fail = function (f) { + argsert('', [f], arguments.length) + usage.failFn(f) + return self + } + + self.check = function (f, _global) { + argsert(' [boolean]', [f, _global], arguments.length) + validation.check(f, _global !== false) + return self + } + + self.global = function global (globals, global) { + argsert(' [boolean]', [globals, global], arguments.length) + globals = [].concat(globals) + if (global !== false) { + options.local = options.local.filter(l => globals.indexOf(l) === -1) + } else { + globals.forEach((g) => { + if (options.local.indexOf(g) === -1) options.local.push(g) + }) + } + return self + } + + self.pkgConf = function pkgConf (key, rootPath) { + argsert(' [string]', [key, rootPath], arguments.length) + let conf = null + // prefer cwd to require-main-filename in this method + // since we're looking for e.g. "nyc" config in nyc consumer + // rather than "yargs" config in nyc (where nyc is the main filename) + const obj = pkgUp(rootPath || cwd) + + // If an object exists in the key, add it to options.configObjects + if (obj[key] && typeof obj[key] === 'object') { + conf = applyExtends(obj[key], rootPath || cwd) + options.configObjects = (options.configObjects || []).concat(conf) + } + + return self + } + + const pkgs = {} + function pkgUp (rootPath) { + const npath = rootPath || '*' + if (pkgs[npath]) return pkgs[npath] + const findUp = require('find-up') + + let obj = {} + try { + let startDir = rootPath || require('require-main-filename')(parentRequire || require) + + // When called in an environment that lacks require.main.filename, such as a jest test runner, + // startDir is already process.cwd(), and should not be shortened. + // Whether or not it is _actually_ a directory (e.g., extensionless bin) is irrelevant, find-up handles it. + if (!rootPath && path.extname(startDir)) { + startDir = path.dirname(startDir) + } + + const pkgJsonPath = findUp.sync('package.json', { + cwd: startDir + }) + obj = JSON.parse(fs.readFileSync(pkgJsonPath)) + } catch (noop) {} + + pkgs[npath] = obj || {} + return pkgs[npath] + } + + let parseFn = null + let parseContext = null + self.parse = function parse (args, shortCircuit, _parseFn) { + argsert('[string|array] [function|boolean|object] [function]', [args, shortCircuit, _parseFn], arguments.length) + if (typeof args === 'undefined') { + return self._parseArgs(processArgs) + } + + // a context object can optionally be provided, this allows + // additional information to be passed to a command handler. + if (typeof shortCircuit === 'object') { + parseContext = shortCircuit + shortCircuit = _parseFn + } + + // by providing a function as a second argument to + // parse you can capture output that would otherwise + // default to printing to stdout/stderr. + if (typeof shortCircuit === 'function') { + parseFn = shortCircuit + shortCircuit = null + } + // completion short-circuits the parsing process, + // skipping validation, etc. + if (!shortCircuit) processArgs = args + + freeze() + if (parseFn) exitProcess = false + + const parsed = self._parseArgs(args, shortCircuit) + if (parseFn) parseFn(exitError, parsed, output) + unfreeze() + + return parsed + } + + self._getParseContext = () => parseContext || {} + + self._hasParseCallback = () => !!parseFn + + self.option = self.options = function option (key, opt) { + argsert(' [object]', [key, opt], arguments.length) + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.options(k, key[k]) + }) + } else { + if (typeof opt !== 'object') { + opt = {} + } + + options.key[key] = true // track manually set keys. + + if (opt.alias) self.alias(key, opt.alias) + + const demand = opt.demand || opt.required || opt.require + + // deprecated, use 'demandOption' instead + if (demand) { + self.demand(key, demand) + } + + if (opt.demandOption) { + self.demandOption(key, typeof opt.demandOption === 'string' ? opt.demandOption : undefined) + } + + if ('conflicts' in opt) { + self.conflicts(key, opt.conflicts) + } + + if ('default' in opt) { + self.default(key, opt.default) + } + + if ('implies' in opt) { + self.implies(key, opt.implies) + } + + if ('nargs' in opt) { + self.nargs(key, opt.nargs) + } + + if (opt.config) { + self.config(key, opt.configParser) + } + + if (opt.normalize) { + self.normalize(key) + } + + if ('choices' in opt) { + self.choices(key, opt.choices) + } + + if ('coerce' in opt) { + self.coerce(key, opt.coerce) + } + + if ('group' in opt) { + self.group(key, opt.group) + } + + if (opt.boolean || opt.type === 'boolean') { + self.boolean(key) + if (opt.alias) self.boolean(opt.alias) + } + + if (opt.array || opt.type === 'array') { + self.array(key) + if (opt.alias) self.array(opt.alias) + } + + if (opt.number || opt.type === 'number') { + self.number(key) + if (opt.alias) self.number(opt.alias) + } + + if (opt.string || opt.type === 'string') { + self.string(key) + if (opt.alias) self.string(opt.alias) + } + + if (opt.count || opt.type === 'count') { + self.count(key) + } + + if (typeof opt.global === 'boolean') { + self.global(key, opt.global) + } + + if (opt.defaultDescription) { + options.defaultDescription[key] = opt.defaultDescription + } + + if (opt.skipValidation) { + self.skipValidation(key) + } + + const desc = opt.describe || opt.description || opt.desc + self.describe(key, desc) + if (opt.hidden) { + self.hide(key) + } + + if (opt.requiresArg) { + self.requiresArg(key) + } + } + + return self + } + self.getOptions = () => options + + self.positional = function (key, opts) { + argsert(' ', [key, opts], arguments.length) + if (context.resets === 0) { + throw new YError(".positional() can only be called in a command's builder function") + } + + // .positional() only supports a subset of the configuration + // options availble to .option(). + const supportedOpts = ['default', 'implies', 'normalize', + 'choices', 'conflicts', 'coerce', 'type', 'describe', + 'desc', 'description', 'alias'] + opts = objFilter(opts, (k, v) => { + let accept = supportedOpts.indexOf(k) !== -1 + // type can be one of string|number|boolean. + if (k === 'type' && ['string', 'number', 'boolean'].indexOf(v) === -1) accept = false + return accept + }) + + // copy over any settings that can be inferred from the command string. + const fullCommand = context.fullCommands[context.fullCommands.length - 1] + const parseOptions = fullCommand ? command.cmdToParseOptions(fullCommand) : { + array: [], + alias: {}, + default: {}, + demand: {} + } + Object.keys(parseOptions).forEach((pk) => { + if (Array.isArray(parseOptions[pk])) { + if (parseOptions[pk].indexOf(key) !== -1) opts[pk] = true + } else { + if (parseOptions[pk][key] && !(pk in opts)) opts[pk] = parseOptions[pk][key] + } + }) + self.group(key, usage.getPositionalGroupName()) + return self.option(key, opts) + } + + self.group = function group (opts, groupName) { + argsert(' ', [opts, groupName], arguments.length) + const existing = preservedGroups[groupName] || groups[groupName] + if (preservedGroups[groupName]) { + // we now only need to track this group name in groups. + delete preservedGroups[groupName] + } + + const seen = {} + groups[groupName] = (existing || []).concat(opts).filter((key) => { + if (seen[key]) return false + return (seen[key] = true) + }) + return self + } + // combine explicit and preserved groups. explicit groups should be first + self.getGroups = () => Object.assign({}, groups, preservedGroups) + + // as long as options.envPrefix is not undefined, + // parser will apply env vars matching prefix to argv + self.env = function (prefix) { + argsert('[string|boolean]', [prefix], arguments.length) + if (prefix === false) options.envPrefix = undefined + else options.envPrefix = prefix || '' + return self + } + + self.wrap = function (cols) { + argsert('', [cols], arguments.length) + usage.wrap(cols) + return self + } + + let strict = false + self.strict = function (enabled) { + argsert('[boolean]', [enabled], arguments.length) + strict = enabled !== false + return self + } + self.getStrict = () => strict + + self.showHelp = function (level) { + argsert('[string|function]', [level], arguments.length) + if (!self.parsed) self._parseArgs(processArgs) // run parser, if it has not already been executed. + if (command.hasDefaultCommand()) { + context.resets++ // override the restriction on top-level positoinals. + command.runDefaultBuilderOn(self, true) + } + usage.showHelp(level) + return self + } + + let versionOpt = null + self.version = function version (opt, msg, ver) { + const defaultVersionOpt = 'version' + argsert('[boolean|string] [string] [string]', [opt, msg, ver], arguments.length) + + // nuke the key previously configured + // to return version #. + if (versionOpt) { + deleteFromParserHintObject(versionOpt) + usage.version(undefined) + versionOpt = null + } + + if (arguments.length === 0) { + ver = guessVersion() + opt = defaultVersionOpt + } else if (arguments.length === 1) { + if (opt === false) { // disable default 'version' key. + return self + } + ver = opt + opt = defaultVersionOpt + } else if (arguments.length === 2) { + ver = msg + msg = null + } + + versionOpt = typeof opt === 'string' ? opt : defaultVersionOpt + msg = msg || usage.deferY18nLookup('Show version number') + + usage.version(ver || undefined) + self.boolean(versionOpt) + self.describe(versionOpt, msg) + return self + } + + function guessVersion () { + const obj = pkgUp() + + return obj.version || 'unknown' + } + + let helpOpt = null + self.addHelpOpt = self.help = function addHelpOpt (opt, msg) { + const defaultHelpOpt = 'help' + argsert('[string|boolean] [string]', [opt, msg], arguments.length) + + // nuke the key previously configured + // to return help. + if (helpOpt) { + deleteFromParserHintObject(helpOpt) + helpOpt = null + } + + if (arguments.length === 1) { + if (opt === false) return self + } + + // use arguments, fallback to defaults for opt and msg + helpOpt = typeof opt === 'string' ? opt : defaultHelpOpt + self.boolean(helpOpt) + self.describe(helpOpt, msg || usage.deferY18nLookup('Show help')) + return self + } + + const defaultShowHiddenOpt = 'show-hidden' + options.showHiddenOpt = defaultShowHiddenOpt + self.addShowHiddenOpt = self.showHidden = function addShowHiddenOpt (opt, msg) { + argsert('[string|boolean] [string]', [opt, msg], arguments.length) + + if (arguments.length === 1) { + if (opt === false) return self + } + + const showHiddenOpt = typeof opt === 'string' ? opt : defaultShowHiddenOpt + self.boolean(showHiddenOpt) + self.describe(showHiddenOpt, msg || usage.deferY18nLookup('Show hidden options')) + options.showHiddenOpt = showHiddenOpt + return self + } + + self.hide = function hide (key) { + argsert('', [key], arguments.length) + options.hiddenOptions.push(key) + return self + } + + self.showHelpOnFail = function showHelpOnFail (enabled, message) { + argsert('[boolean|string] [string]', [enabled, message], arguments.length) + usage.showHelpOnFail(enabled, message) + return self + } + + var exitProcess = true + self.exitProcess = function (enabled) { + argsert('[boolean]', [enabled], arguments.length) + if (typeof enabled !== 'boolean') { + enabled = true + } + exitProcess = enabled + return self + } + self.getExitProcess = () => exitProcess + + var completionCommand = null + self.completion = function (cmd, desc, fn) { + argsert('[string] [string|boolean|function] [function]', [cmd, desc, fn], arguments.length) + + // a function to execute when generating + // completions can be provided as the second + // or third argument to completion. + if (typeof desc === 'function') { + fn = desc + desc = null + } + + // register the completion command. + completionCommand = cmd || 'completion' + if (!desc && desc !== false) { + desc = 'generate bash completion script' + } + self.command(completionCommand, desc) + + // a function can be provided + if (fn) completion.registerFunction(fn) + + return self + } + + self.showCompletionScript = function ($0) { + argsert('[string]', [$0], arguments.length) + $0 = $0 || self.$0 + _logger.log(completion.generateCompletionScript($0, completionCommand)) + return self + } + + self.getCompletion = function (args, done) { + argsert(' ', [args, done], arguments.length) + completion.getCompletion(args, done) + } + + self.locale = function (locale) { + argsert('[string]', [locale], arguments.length) + if (arguments.length === 0) { + guessLocale() + return y18n.getLocale() + } + detectLocale = false + y18n.setLocale(locale) + return self + } + + self.updateStrings = self.updateLocale = function (obj) { + argsert('', [obj], arguments.length) + detectLocale = false + y18n.updateLocale(obj) + return self + } + + let detectLocale = true + self.detectLocale = function (detect) { + argsert('', [detect], arguments.length) + detectLocale = detect + return self + } + self.getDetectLocale = () => detectLocale + + var hasOutput = false + var exitError = null + // maybe exit, always capture + // context about why we wanted to exit. + self.exit = (code, err) => { + hasOutput = true + exitError = err + if (exitProcess) process.exit(code) + } + + // we use a custom logger that buffers output, + // so that we can print to non-CLIs, e.g., chat-bots. + const _logger = { + log () { + const args = [] + for (let i = 0; i < arguments.length; i++) args.push(arguments[i]) + if (!self._hasParseCallback()) console.log.apply(console, args) + hasOutput = true + if (output.length) output += '\n' + output += args.join(' ') + }, + error () { + const args = [] + for (let i = 0; i < arguments.length; i++) args.push(arguments[i]) + if (!self._hasParseCallback()) console.error.apply(console, args) + hasOutput = true + if (output.length) output += '\n' + output += args.join(' ') + } + } + self._getLoggerInstance = () => _logger + // has yargs output an error our help + // message in the current execution context. + self._hasOutput = () => hasOutput + + self._setHasOutput = () => { + hasOutput = true + } + + let recommendCommands + self.recommendCommands = function (recommend) { + argsert('[boolean]', [recommend], arguments.length) + recommendCommands = typeof recommend === 'boolean' ? recommend : true + return self + } + + self.getUsageInstance = () => usage + + self.getValidationInstance = () => validation + + self.getCommandInstance = () => command + + self.terminalWidth = () => { + argsert([], 0) + return typeof process.stdout.columns !== 'undefined' ? process.stdout.columns : null + } + + Object.defineProperty(self, 'argv', { + get: () => self._parseArgs(processArgs), + enumerable: true + }) + + self._parseArgs = function parseArgs (args, shortCircuit, _skipValidation, commandIndex) { + let skipValidation = !!_skipValidation + args = args || processArgs + + options.__ = y18n.__ + options.configuration = pkgUp()['yargs'] || {} + + const parsed = Parser.detailed(args, options) + let argv = parsed.argv + if (parseContext) argv = Object.assign({}, argv, parseContext) + const aliases = parsed.aliases + + argv.$0 = self.$0 + self.parsed = parsed + + try { + guessLocale() // guess locale lazily, so that it can be turned off in chain. + + // while building up the argv object, there + // are two passes through the parser. If completion + // is being performed short-circuit on the first pass. + if (shortCircuit) { + return argv + } + + // if there's a handler associated with a + // command defer processing to it. + if (helpOpt) { + // consider any multi-char helpOpt alias as a valid help command + // unless all helpOpt aliases are single-char + // note that parsed.aliases is a normalized bidirectional map :) + const helpCmds = [helpOpt] + .concat(aliases[helpOpt] || []) + .filter(k => k.length > 1) + // check if help should trigger and strip it from _. + if (~helpCmds.indexOf(argv._[argv._.length - 1])) { + argv._.pop() + argv[helpOpt] = true + } + } + + const handlerKeys = command.getCommands() + const requestCompletions = completion.completionKey in argv + const skipRecommendation = argv[helpOpt] || requestCompletions + const skipDefaultCommand = skipRecommendation && (handlerKeys.length > 1 || handlerKeys[0] !== '$0') + + if (argv._.length) { + if (handlerKeys.length) { + let firstUnknownCommand + for (let i = (commandIndex || 0), cmd; argv._[i] !== undefined; i++) { + cmd = String(argv._[i]) + if (~handlerKeys.indexOf(cmd) && cmd !== completionCommand) { + // commands are executed using a recursive algorithm that executes + // the deepest command first; we keep track of the position in the + // argv._ array that is currently being executed. + return command.runCommand(cmd, self, parsed, i + 1) + } else if (!firstUnknownCommand && cmd !== completionCommand) { + firstUnknownCommand = cmd + break + } + } + + // run the default command, if defined + if (command.hasDefaultCommand() && !skipDefaultCommand) { + return command.runCommand(null, self, parsed) + } + + // recommend a command if recommendCommands() has + // been enabled, and no commands were found to execute + if (recommendCommands && firstUnknownCommand && !skipRecommendation) { + validation.recommendCommands(firstUnknownCommand, handlerKeys) + } + } + + // generate a completion script for adding to ~/.bashrc. + if (completionCommand && ~argv._.indexOf(completionCommand) && !requestCompletions) { + if (exitProcess) setBlocking(true) + self.showCompletionScript() + self.exit(0) + } + } else if (command.hasDefaultCommand() && !skipDefaultCommand) { + return command.runCommand(null, self, parsed) + } + + // we must run completions first, a user might + // want to complete the --help or --version option. + if (requestCompletions) { + if (exitProcess) setBlocking(true) + + // we allow for asynchronous completions, + // e.g., loading in a list of commands from an API. + const completionArgs = args.slice(args.indexOf(`--${completion.completionKey}`) + 1) + completion.getCompletion(completionArgs, (completions) => { + ;(completions || []).forEach((completion) => { + _logger.log(completion) + }) + + self.exit(0) + }) + return argv + } + + // Handle 'help' and 'version' options + // if we haven't already output help! + if (!hasOutput) { + Object.keys(argv).forEach((key) => { + if (key === helpOpt && argv[key]) { + if (exitProcess) setBlocking(true) + + skipValidation = true + self.showHelp('log') + self.exit(0) + } else if (key === versionOpt && argv[key]) { + if (exitProcess) setBlocking(true) + + skipValidation = true + usage.showVersion() + self.exit(0) + } + }) + } + + // Check if any of the options to skip validation were provided + if (!skipValidation && options.skipValidation.length > 0) { + skipValidation = Object.keys(argv).some(key => options.skipValidation.indexOf(key) >= 0 && argv[key] === true) + } + + // If the help or version options where used and exitProcess is false, + // or if explicitly skipped, we won't run validations. + if (!skipValidation) { + if (parsed.error) throw new YError(parsed.error.message) + + // if we're executed via bash completion, don't + // bother with validation. + if (!requestCompletions) { + self._runValidation(argv, aliases, {}, parsed.error) + } + } + } catch (err) { + if (err instanceof YError) usage.fail(err.message, err) + else throw err + } + + return argv + } + + self._runValidation = function runValidation (argv, aliases, positionalMap, parseErrors) { + if (parseErrors) throw new YError(parseErrors.message) + validation.nonOptionCount(argv) + validation.requiredArguments(argv) + if (strict) validation.unknownArguments(argv, aliases, positionalMap) + validation.customChecks(argv, aliases) + validation.limitedChoices(argv) + validation.implications(argv) + validation.conflicting(argv) + } + + function guessLocale () { + if (!detectLocale) return + + try { + const osLocale = require('os-locale') + self.locale(osLocale.sync({ spawn: false })) + } catch (err) { + // if we explode looking up locale just noop + // we'll keep using the default language 'en'. + } + } + + // an app should almost always have --version and --help, + // if you *really* want to disable this use .help(false)/.version(false). + self.help() + self.version() + + return self +} + +// rebase an absolute path to a relative one with respect to a base directory +// exported for tests +exports.rebase = rebase +function rebase (base, dir) { + return path.relative(base, dir) +} diff --git a/WebServer/node_modules/yargs-unparser/package.json b/WebServer/node_modules/yargs-unparser/package.json new file mode 100644 index 000000000..a49814ce8 --- /dev/null +++ b/WebServer/node_modules/yargs-unparser/package.json @@ -0,0 +1,110 @@ +{ + "_args": [ + [ + "yargs-unparser@1.5.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "yargs-unparser@1.5.0", + "_id": "yargs-unparser@1.5.0", + "_inBundle": false, + "_integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "_location": "/yargs-unparser", + "_phantomChildren": { + "camelcase": "5.3.1", + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "3.0.0", + "os-locale": "3.1.0", + "require-directory": "2.1.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "4.0.0" + }, + "_requested": { + "type": "version", + "registry": true, + "raw": "yargs-unparser@1.5.0", + "name": "yargs-unparser", + "escapedName": "yargs-unparser", + "rawSpec": "1.5.0", + "saveSpec": null, + "fetchSpec": "1.5.0" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "_spec": "1.5.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "André Cruz", + "email": "andre@moxy.studio" + }, + "bugs": { + "url": "https://github.com/yargs/yargs-unparser/issues" + }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "description": "Converts back a yargs argv object to its original array form", + "devDependencies": { + "@commitlint/cli": "^7.2.1", + "@commitlint/config-conventional": "^7.1.2", + "eslint": "^5.9.0", + "eslint-config-moxy": "^6.1.1", + "husky": "^1.2.0", + "jest": "^23.6.0", + "lint-staged": "^8.1.0", + "minimist": "^1.2.0", + "standard-version": "^4.4.0", + "yargs-parser": "^11.1.1" + }, + "engines": { + "node": ">=6" + }, + "files": [], + "homepage": "https://github.com/yargs/yargs-unparser", + "keywords": [ + "yargs", + "unparse", + "expand", + "inverse", + "argv" + ], + "license": "MIT", + "lint-staged": { + "*.js": [ + "eslint --fix", + "git add" + ] + }, + "main": "index.js", + "name": "yargs-unparser", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/yargs/yargs-unparser.git" + }, + "scripts": { + "commitmsg": "commitlint -e $GIT_PARAMS", + "lint": "eslint .", + "precommit": "lint-staged", + "prerelease": "npm t && npm run lint", + "release": "standard-version", + "test": "jest --env node --coverage" + }, + "standard-version": { + "scripts": { + "posttag": "git push --follow-tags origin master && npm publish" + } + }, + "version": "1.5.0" +} diff --git a/WebServer/node_modules/yargs/index.js b/WebServer/node_modules/yargs/index.js new file mode 100644 index 000000000..dfed54bc5 --- /dev/null +++ b/WebServer/node_modules/yargs/index.js @@ -0,0 +1,32 @@ +'use strict' +// classic singleton yargs API, to use yargs +// without running as a singleton do: +// require('yargs/yargs')(process.argv.slice(2)) +const yargs = require('./yargs') + +Argv(process.argv.slice(2)) + +module.exports = Argv + +function Argv (processArgs, cwd) { + const argv = yargs(processArgs, cwd, require) + singletonify(argv) + return argv +} + +/* Hack an instance of Argv with process.argv into Argv + so people can do + require('yargs')(['--beeble=1','-z','zizzle']).argv + to parse a list of args and + require('yargs').argv + to get a parsed version of process.argv. +*/ +function singletonify (inst) { + Object.keys(inst).forEach((key) => { + if (key === 'argv') { + Argv.__defineGetter__(key, inst.__lookupGetter__(key)) + } else { + Argv[key] = typeof inst[key] === 'function' ? inst[key].bind(inst) : inst[key] + } + }) +} diff --git a/WebServer/node_modules/yargs/lib/apply-extends.js b/WebServer/node_modules/yargs/lib/apply-extends.js new file mode 100644 index 000000000..1436b6508 --- /dev/null +++ b/WebServer/node_modules/yargs/lib/apply-extends.js @@ -0,0 +1,53 @@ + +'use strict' +const fs = require('fs') +const path = require('path') +const YError = require('./yerror') + +let previouslyVisitedConfigs = [] + +function checkForCircularExtends (cfgPath) { + if (previouslyVisitedConfigs.indexOf(cfgPath) > -1) { + throw new YError(`Circular extended configurations: '${cfgPath}'.`) + } +} + +function getPathToDefaultConfig (cwd, pathToExtend) { + return path.resolve(cwd, pathToExtend) +} + +function applyExtends (config, cwd) { + let defaultConfig = {} + + if (config.hasOwnProperty('extends')) { + if (typeof config.extends !== 'string') return defaultConfig + const isPath = /\.json|\..*rc$/.test(config.extends) + let pathToDefault = null + if (!isPath) { + try { + pathToDefault = require.resolve(config.extends) + } catch (err) { + // most likely this simply isn't a module. + } + } else { + pathToDefault = getPathToDefaultConfig(cwd, config.extends) + } + // maybe the module uses key for some other reason, + // err on side of caution. + if (!pathToDefault && !isPath) return config + + checkForCircularExtends(pathToDefault) + + previouslyVisitedConfigs.push(pathToDefault) + + defaultConfig = isPath ? JSON.parse(fs.readFileSync(pathToDefault, 'utf8')) : require(config.extends) + delete config.extends + defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault)) + } + + previouslyVisitedConfigs = [] + + return Object.assign({}, defaultConfig, config) +} + +module.exports = applyExtends diff --git a/WebServer/node_modules/yargs/lib/argsert.js b/WebServer/node_modules/yargs/lib/argsert.js new file mode 100644 index 000000000..f310b4e74 --- /dev/null +++ b/WebServer/node_modules/yargs/lib/argsert.js @@ -0,0 +1,68 @@ +'use strict' + +// hoisted due to circular dependency on command. +module.exports = argsert +const command = require('./command')() +const YError = require('./yerror') + +const positionName = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth'] +function argsert (expected, callerArguments, length) { + // TODO: should this eventually raise an exception. + try { + // preface the argument description with "cmd", so + // that we can run it through yargs' command parser. + let position = 0 + let parsed = { demanded: [], optional: [] } + if (typeof expected === 'object') { + length = callerArguments + callerArguments = expected + } else { + parsed = command.parseCommand(`cmd ${expected}`) + } + const args = [].slice.call(callerArguments) + + while (args.length && args[args.length - 1] === undefined) args.pop() + length = length || args.length + + if (length < parsed.demanded.length) { + throw new YError(`Not enough arguments provided. Expected ${parsed.demanded.length} but received ${args.length}.`) + } + + const totalCommands = parsed.demanded.length + parsed.optional.length + if (length > totalCommands) { + throw new YError(`Too many arguments provided. Expected max ${totalCommands} but received ${length}.`) + } + + parsed.demanded.forEach((demanded) => { + const arg = args.shift() + const observedType = guessType(arg) + const matchingTypes = demanded.cmd.filter(type => type === observedType || type === '*') + if (matchingTypes.length === 0) argumentTypeError(observedType, demanded.cmd, position, false) + position += 1 + }) + + parsed.optional.forEach((optional) => { + if (args.length === 0) return + const arg = args.shift() + const observedType = guessType(arg) + const matchingTypes = optional.cmd.filter(type => type === observedType || type === '*') + if (matchingTypes.length === 0) argumentTypeError(observedType, optional.cmd, position, true) + position += 1 + }) + } catch (err) { + console.warn(err.stack) + } +} + +function guessType (arg) { + if (Array.isArray(arg)) { + return 'array' + } else if (arg === null) { + return 'null' + } + return typeof arg +} + +function argumentTypeError (observedType, allowedTypes, position, optional) { + throw new YError(`Invalid ${positionName[position] || 'manyith'} argument. Expected ${allowedTypes.join(' or ')} but received ${observedType}.`) +} diff --git a/WebServer/node_modules/yargs/lib/command.js b/WebServer/node_modules/yargs/lib/command.js new file mode 100644 index 000000000..895423a2b --- /dev/null +++ b/WebServer/node_modules/yargs/lib/command.js @@ -0,0 +1,437 @@ +'use strict' + +const inspect = require('util').inspect +const isPromise = require('./is-promise') +const { applyMiddleware, commandMiddlewareFactory } = require('./middleware') +const path = require('path') +const Parser = require('yargs-parser') + +const DEFAULT_MARKER = /(^\*)|(^\$0)/ + +// handles parsing positional arguments, +// and populating argv with said positional +// arguments. +module.exports = function command (yargs, usage, validation, globalMiddleware) { + const self = {} + let handlers = {} + let aliasMap = {} + let defaultCommand + globalMiddleware = globalMiddleware || [] + + self.addHandler = function addHandler (cmd, description, builder, handler, commandMiddleware) { + let aliases = [] + const middlewares = commandMiddlewareFactory(commandMiddleware) + handler = handler || (() => {}) + + if (Array.isArray(cmd)) { + aliases = cmd.slice(1) + cmd = cmd[0] + } else if (typeof cmd === 'object') { + let command = (Array.isArray(cmd.command) || typeof cmd.command === 'string') ? cmd.command : moduleName(cmd) + if (cmd.aliases) command = [].concat(command).concat(cmd.aliases) + self.addHandler(command, extractDesc(cmd), cmd.builder, cmd.handler, cmd.middlewares) + return + } + + // allow a module to be provided instead of separate builder and handler + if (typeof builder === 'object' && builder.builder && typeof builder.handler === 'function') { + self.addHandler([cmd].concat(aliases), description, builder.builder, builder.handler, builder.middlewares) + return + } + + // parse positionals out of cmd string + const parsedCommand = self.parseCommand(cmd) + + // remove positional args from aliases only + aliases = aliases.map(alias => self.parseCommand(alias).cmd) + + // check for default and filter out '*'' + let isDefault = false + const parsedAliases = [parsedCommand.cmd].concat(aliases).filter((c) => { + if (DEFAULT_MARKER.test(c)) { + isDefault = true + return false + } + return true + }) + + // standardize on $0 for default command. + if (parsedAliases.length === 0 && isDefault) parsedAliases.push('$0') + + // shift cmd and aliases after filtering out '*' + if (isDefault) { + parsedCommand.cmd = parsedAliases[0] + aliases = parsedAliases.slice(1) + cmd = cmd.replace(DEFAULT_MARKER, parsedCommand.cmd) + } + + // populate aliasMap + aliases.forEach((alias) => { + aliasMap[alias] = parsedCommand.cmd + }) + + if (description !== false) { + usage.command(cmd, description, isDefault, aliases) + } + + handlers[parsedCommand.cmd] = { + original: cmd, + description: description, + handler, + builder: builder || {}, + middlewares: middlewares || [], + demanded: parsedCommand.demanded, + optional: parsedCommand.optional + } + + if (isDefault) defaultCommand = handlers[parsedCommand.cmd] + } + + self.addDirectory = function addDirectory (dir, context, req, callerFile, opts) { + opts = opts || {} + // disable recursion to support nested directories of subcommands + if (typeof opts.recurse !== 'boolean') opts.recurse = false + // exclude 'json', 'coffee' from require-directory defaults + if (!Array.isArray(opts.extensions)) opts.extensions = ['js'] + // allow consumer to define their own visitor function + const parentVisit = typeof opts.visit === 'function' ? opts.visit : o => o + // call addHandler via visitor function + opts.visit = function visit (obj, joined, filename) { + const visited = parentVisit(obj, joined, filename) + // allow consumer to skip modules with their own visitor + if (visited) { + // check for cyclic reference + // each command file path should only be seen once per execution + if (~context.files.indexOf(joined)) return visited + // keep track of visited files in context.files + context.files.push(joined) + self.addHandler(visited) + } + return visited + } + require('require-directory')({ require: req, filename: callerFile }, dir, opts) + } + + // lookup module object from require()d command and derive name + // if module was not require()d and no name given, throw error + function moduleName (obj) { + const mod = require('which-module')(obj) + if (!mod) throw new Error(`No command name given for module: ${inspect(obj)}`) + return commandFromFilename(mod.filename) + } + + // derive command name from filename + function commandFromFilename (filename) { + return path.basename(filename, path.extname(filename)) + } + + function extractDesc (obj) { + for (let keys = ['describe', 'description', 'desc'], i = 0, l = keys.length, test; i < l; i++) { + test = obj[keys[i]] + if (typeof test === 'string' || typeof test === 'boolean') return test + } + return false + } + + self.parseCommand = function parseCommand (cmd) { + const extraSpacesStrippedCommand = cmd.replace(/\s{2,}/g, ' ') + const splitCommand = extraSpacesStrippedCommand.split(/\s+(?![^[]*]|[^<]*>)/) + const bregex = /\.*[\][<>]/g + const parsedCommand = { + cmd: (splitCommand.shift()).replace(bregex, ''), + demanded: [], + optional: [] + } + splitCommand.forEach((cmd, i) => { + let variadic = false + cmd = cmd.replace(/\s/g, '') + if (/\.+[\]>]/.test(cmd) && i === splitCommand.length - 1) variadic = true + if (/^\[/.test(cmd)) { + parsedCommand.optional.push({ + cmd: cmd.replace(bregex, '').split('|'), + variadic + }) + } else { + parsedCommand.demanded.push({ + cmd: cmd.replace(bregex, '').split('|'), + variadic + }) + } + }) + return parsedCommand + } + + self.getCommands = () => Object.keys(handlers).concat(Object.keys(aliasMap)) + + self.getCommandHandlers = () => handlers + + self.hasDefaultCommand = () => !!defaultCommand + + self.runCommand = function runCommand (command, yargs, parsed, commandIndex) { + let aliases = parsed.aliases + const commandHandler = handlers[command] || handlers[aliasMap[command]] || defaultCommand + const currentContext = yargs.getContext() + let numFiles = currentContext.files.length + const parentCommands = currentContext.commands.slice() + + // what does yargs look like after the buidler is run? + let innerArgv = parsed.argv + let innerYargs = null + let positionalMap = {} + if (command) { + currentContext.commands.push(command) + currentContext.fullCommands.push(commandHandler.original) + } + if (typeof commandHandler.builder === 'function') { + // a function can be provided, which builds + // up a yargs chain and possibly returns it. + innerYargs = commandHandler.builder(yargs.reset(parsed.aliases)) + // if the builder function did not yet parse argv with reset yargs + // and did not explicitly set a usage() string, then apply the + // original command string as usage() for consistent behavior with + // options object below. + if (yargs.parsed === false) { + if (shouldUpdateUsage(yargs)) { + yargs.getUsageInstance().usage( + usageFromParentCommandsCommandHandler(parentCommands, commandHandler), + commandHandler.description + ) + } + innerArgv = innerYargs ? innerYargs._parseArgs(null, null, true, commandIndex) : yargs._parseArgs(null, null, true, commandIndex) + } else { + innerArgv = yargs.parsed.argv + } + + if (innerYargs && yargs.parsed === false) aliases = innerYargs.parsed.aliases + else aliases = yargs.parsed.aliases + } else if (typeof commandHandler.builder === 'object') { + // as a short hand, an object can instead be provided, specifying + // the options that a command takes. + innerYargs = yargs.reset(parsed.aliases) + if (shouldUpdateUsage(innerYargs)) { + innerYargs.getUsageInstance().usage( + usageFromParentCommandsCommandHandler(parentCommands, commandHandler), + commandHandler.description + ) + } + Object.keys(commandHandler.builder).forEach((key) => { + innerYargs.option(key, commandHandler.builder[key]) + }) + innerArgv = innerYargs._parseArgs(null, null, true, commandIndex) + aliases = innerYargs.parsed.aliases + } + + if (!yargs._hasOutput()) { + positionalMap = populatePositionals(commandHandler, innerArgv, currentContext, yargs) + } + + const middlewares = globalMiddleware.slice(0).concat(commandHandler.middlewares || []) + applyMiddleware(innerArgv, yargs, middlewares, true) + + // we apply validation post-hoc, so that custom + // checks get passed populated positional arguments. + if (!yargs._hasOutput()) yargs._runValidation(innerArgv, aliases, positionalMap, yargs.parsed.error) + + if (commandHandler.handler && !yargs._hasOutput()) { + yargs._setHasOutput() + + innerArgv = applyMiddleware(innerArgv, yargs, middlewares, false) + + const handlerResult = isPromise(innerArgv) + ? innerArgv.then(argv => commandHandler.handler(argv)) + : commandHandler.handler(innerArgv) + + if (isPromise(handlerResult)) { + handlerResult.catch(error => + yargs.getUsageInstance().fail(null, error) + ) + } + } + + if (command) { + currentContext.commands.pop() + currentContext.fullCommands.pop() + } + numFiles = currentContext.files.length - numFiles + if (numFiles > 0) currentContext.files.splice(numFiles * -1, numFiles) + + return innerArgv + } + + function shouldUpdateUsage (yargs) { + return !yargs.getUsageInstance().getUsageDisabled() && + yargs.getUsageInstance().getUsage().length === 0 + } + + function usageFromParentCommandsCommandHandler (parentCommands, commandHandler) { + const c = DEFAULT_MARKER.test(commandHandler.original) ? commandHandler.original.replace(DEFAULT_MARKER, '').trim() : commandHandler.original + const pc = parentCommands.filter((c) => { return !DEFAULT_MARKER.test(c) }) + pc.push(c) + return `$0 ${pc.join(' ')}` + } + + self.runDefaultBuilderOn = function (yargs) { + if (shouldUpdateUsage(yargs)) { + // build the root-level command string from the default string. + const commandString = DEFAULT_MARKER.test(defaultCommand.original) + ? defaultCommand.original : defaultCommand.original.replace(/^[^[\]<>]*/, '$0 ') + yargs.getUsageInstance().usage( + commandString, + defaultCommand.description + ) + } + const builder = defaultCommand.builder + if (typeof builder === 'function') { + builder(yargs) + } else { + Object.keys(builder).forEach((key) => { + yargs.option(key, builder[key]) + }) + } + } + + // transcribe all positional arguments "command [apple]" + // onto argv. + function populatePositionals (commandHandler, argv, context, yargs) { + argv._ = argv._.slice(context.commands.length) // nuke the current commands + const demanded = commandHandler.demanded.slice(0) + const optional = commandHandler.optional.slice(0) + const positionalMap = {} + + validation.positionalCount(demanded.length, argv._.length) + + while (demanded.length) { + const demand = demanded.shift() + populatePositional(demand, argv, positionalMap) + } + + while (optional.length) { + const maybe = optional.shift() + populatePositional(maybe, argv, positionalMap) + } + + argv._ = context.commands.concat(argv._) + + postProcessPositionals(argv, positionalMap, self.cmdToParseOptions(commandHandler.original)) + + return positionalMap + } + + function populatePositional (positional, argv, positionalMap, parseOptions) { + const cmd = positional.cmd[0] + if (positional.variadic) { + positionalMap[cmd] = argv._.splice(0).map(String) + } else { + if (argv._.length) positionalMap[cmd] = [String(argv._.shift())] + } + } + + // we run yargs-parser against the positional arguments + // applying the same parsing logic used for flags. + function postProcessPositionals (argv, positionalMap, parseOptions) { + // combine the parsing hints we've inferred from the command + // string with explicitly configured parsing hints. + const options = Object.assign({}, yargs.getOptions()) + options.default = Object.assign(parseOptions.default, options.default) + options.alias = Object.assign(parseOptions.alias, options.alias) + options.array = options.array.concat(parseOptions.array) + delete options.config // don't load config when processing positionals. + + const unparsed = [] + Object.keys(positionalMap).forEach((key) => { + positionalMap[key].map((value) => { + unparsed.push(`--${key}`) + unparsed.push(value) + }) + }) + + // short-circuit parse. + if (!unparsed.length) return + + const parsed = Parser.detailed(unparsed, options) + + if (parsed.error) { + yargs.getUsageInstance().fail(parsed.error.message, parsed.error) + } else { + // only copy over positional keys (don't overwrite + // flag arguments that were already parsed). + const positionalKeys = Object.keys(positionalMap) + Object.keys(positionalMap).forEach((key) => { + [].push.apply(positionalKeys, parsed.aliases[key]) + }) + + Object.keys(parsed.argv).forEach((key) => { + if (positionalKeys.indexOf(key) !== -1) { + // any new aliases need to be placed in positionalMap, which + // is used for validation. + if (!positionalMap[key]) positionalMap[key] = parsed.argv[key] + argv[key] = parsed.argv[key] + } + }) + } + } + + self.cmdToParseOptions = function (cmdString) { + const parseOptions = { + array: [], + default: {}, + alias: {}, + demand: {} + } + + const parsed = self.parseCommand(cmdString) + parsed.demanded.forEach((d) => { + const cmds = d.cmd.slice(0) + const cmd = cmds.shift() + if (d.variadic) { + parseOptions.array.push(cmd) + parseOptions.default[cmd] = [] + } + cmds.forEach((c) => { + parseOptions.alias[cmd] = c + }) + parseOptions.demand[cmd] = true + }) + + parsed.optional.forEach((o) => { + const cmds = o.cmd.slice(0) + const cmd = cmds.shift() + if (o.variadic) { + parseOptions.array.push(cmd) + parseOptions.default[cmd] = [] + } + cmds.forEach((c) => { + parseOptions.alias[cmd] = c + }) + }) + + return parseOptions + } + + self.reset = () => { + handlers = {} + aliasMap = {} + defaultCommand = undefined + return self + } + + // used by yargs.parse() to freeze + // the state of commands such that + // we can apply .parse() multiple times + // with the same yargs instance. + let frozen + self.freeze = () => { + frozen = {} + frozen.handlers = handlers + frozen.aliasMap = aliasMap + frozen.defaultCommand = defaultCommand + } + self.unfreeze = () => { + handlers = frozen.handlers + aliasMap = frozen.aliasMap + defaultCommand = frozen.defaultCommand + frozen = undefined + } + + return self +} diff --git a/WebServer/node_modules/yargs/lib/completion.js b/WebServer/node_modules/yargs/lib/completion.js new file mode 100644 index 000000000..a8de5b04e --- /dev/null +++ b/WebServer/node_modules/yargs/lib/completion.js @@ -0,0 +1,118 @@ +'use strict' +const fs = require('fs') +const path = require('path') + +// add bash completions to your +// yargs-powered applications. +module.exports = function completion (yargs, usage, command) { + const self = { + completionKey: 'get-yargs-completions' + } + + const zshShell = process.env.SHELL && process.env.SHELL.indexOf('zsh') !== -1 + // get a list of completion commands. + // 'args' is the array of strings from the line to be completed + self.getCompletion = function getCompletion (args, done) { + const completions = [] + const current = args.length ? args[args.length - 1] : '' + const argv = yargs.parse(args, true) + const aliases = yargs.parsed.aliases + const parentCommands = yargs.getContext().commands + + // a custom completion function can be provided + // to completion(). + if (completionFunction) { + if (completionFunction.length < 3) { + const result = completionFunction(current, argv) + + // promise based completion function. + if (typeof result.then === 'function') { + return result.then((list) => { + process.nextTick(() => { done(list) }) + }).catch((err) => { + process.nextTick(() => { throw err }) + }) + } + + // synchronous completion function. + return done(result) + } else { + // asynchronous completion function + return completionFunction(current, argv, (completions) => { + done(completions) + }) + } + } + + const handlers = command.getCommandHandlers() + for (let i = 0, ii = args.length; i < ii; ++i) { + if (handlers[args[i]] && handlers[args[i]].builder) { + const builder = handlers[args[i]].builder + if (typeof builder === 'function') { + const y = yargs.reset() + builder(y) + return y.argv + } + } + } + + if (!current.match(/^-/) && parentCommands[parentCommands.length - 1] !== current) { + usage.getCommands().forEach((usageCommand) => { + const commandName = command.parseCommand(usageCommand[0]).cmd + if (args.indexOf(commandName) === -1) { + if (!zshShell) { + completions.push(commandName) + } else { + const desc = usageCommand[1] || '' + completions.push(commandName.replace(/:/g, '\\:') + ':' + desc) + } + } + }) + } + + if (current.match(/^-/) || (current === '' && completions.length === 0)) { + const descs = usage.getDescriptions() + Object.keys(yargs.getOptions().key).forEach((key) => { + // If the key and its aliases aren't in 'args', add the key to 'completions' + const keyAndAliases = [key].concat(aliases[key] || []) + const notInArgs = keyAndAliases.every(val => args.indexOf(`--${val}`) === -1) + if (notInArgs) { + if (!zshShell) { + completions.push(`--${key}`) + } else { + const desc = descs[key] || '' + completions.push(`--${key.replace(/:/g, '\\:')}:${desc.replace('__yargsString__:', '')}`) + } + } + }) + } + + done(completions) + } + + // generate the completion script to add to your .bashrc. + self.generateCompletionScript = function generateCompletionScript ($0, cmd) { + let script = fs.readFileSync( + path.resolve(__dirname, zshShell ? '../completion.zsh.hbs' : '../completion.sh.hbs'), + 'utf-8' + ) + const name = path.basename($0) + + // add ./to applications not yet installed as bin. + if ($0.match(/\.js$/)) $0 = `./${$0}` + + script = script.replace(/{{app_name}}/g, name) + script = script.replace(/{{completion_command}}/g, cmd) + return script.replace(/{{app_path}}/g, $0) + } + + // register a function to perform your own custom + // completions., this function can be either + // synchrnous or asynchronous. + let completionFunction = null + self.registerFunction = (fn) => { + completionFunction = fn + } + + return self +} diff --git a/WebServer/node_modules/yargs/lib/decamelize.js b/WebServer/node_modules/yargs/lib/decamelize.js new file mode 100644 index 000000000..c3a415151 --- /dev/null +++ b/WebServer/node_modules/yargs/lib/decamelize.js @@ -0,0 +1,32 @@ +/* +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +'use strict' + +module.exports = (text, separator) => { + separator = typeof separator === 'undefined' ? '_' : separator + + return text + .replace(/([a-z\d])([A-Z])/g, `$1${separator}$2`) + .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, `$1${separator}$2`) + .toLowerCase() +} diff --git a/WebServer/node_modules/yargs/lib/is-promise.js b/WebServer/node_modules/yargs/lib/is-promise.js new file mode 100644 index 000000000..0516defa9 --- /dev/null +++ b/WebServer/node_modules/yargs/lib/is-promise.js @@ -0,0 +1,3 @@ +module.exports = function isPromise (maybePromise) { + return maybePromise instanceof Promise +} diff --git a/WebServer/node_modules/yargs/lib/levenshtein.js b/WebServer/node_modules/yargs/lib/levenshtein.js new file mode 100644 index 000000000..c66c1babb --- /dev/null +++ b/WebServer/node_modules/yargs/lib/levenshtein.js @@ -0,0 +1,58 @@ +/* +Copyright (c) 2011 Andrei Mackenzie + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// levenshtein distance algorithm, pulled from Andrei Mackenzie's MIT licensed. +// gist, which can be found here: https://gist.github.com/andrei-m/982927 +'use strict' +// Compute the edit distance between the two given strings +module.exports = function levenshtein (a, b) { + if (a.length === 0) return b.length + if (b.length === 0) return a.length + + const matrix = [] + + // increment along the first column of each row + let i + for (i = 0; i <= b.length; i++) { + matrix[i] = [i] + } + + // increment each column in the first row + let j + for (j = 0; j <= a.length; j++) { + matrix[0][j] = j + } + + // Fill in the rest of the matrix + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1] + } else { + matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution + Math.min(matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1)) // deletion + } + } + } + + return matrix[b.length][a.length] +} diff --git a/WebServer/node_modules/yargs/lib/middleware.js b/WebServer/node_modules/yargs/lib/middleware.js new file mode 100644 index 000000000..9f9c19696 --- /dev/null +++ b/WebServer/node_modules/yargs/lib/middleware.js @@ -0,0 +1,65 @@ +'use strict' + +// hoisted due to circular dependency on command. +module.exports = { + applyMiddleware, + commandMiddlewareFactory, + globalMiddlewareFactory +} +const isPromise = require('./is-promise') +const argsert = require('./argsert') + +function globalMiddlewareFactory (globalMiddleware, context) { + return function (callback, applyBeforeValidation = false) { + argsert(' [boolean]', [callback, applyBeforeValidation], arguments.length) + if (Array.isArray(callback)) { + for (let i = 0; i < callback.length; i++) { + if (typeof callback[i] !== 'function') { + throw Error('middleware must be a function') + } + callback[i].applyBeforeValidation = applyBeforeValidation + } + Array.prototype.push.apply(globalMiddleware, callback) + } else if (typeof callback === 'function') { + callback.applyBeforeValidation = applyBeforeValidation + globalMiddleware.push(callback) + } + return context + } +} + +function commandMiddlewareFactory (commandMiddleware) { + if (!commandMiddleware) return [] + return commandMiddleware.map(middleware => { + middleware.applyBeforeValidation = false + return middleware + }) +} + +function applyMiddleware (argv, yargs, middlewares, beforeValidation) { + const beforeValidationError = new Error('middleware cannot return a promise when applyBeforeValidation is true') + return middlewares + .reduce((accumulation, middleware) => { + if (middleware.applyBeforeValidation !== beforeValidation && + !isPromise(accumulation)) { + return accumulation + } + + if (isPromise(accumulation)) { + return accumulation + .then(initialObj => + Promise.all([initialObj, middleware(initialObj, yargs)]) + ) + .then(([initialObj, middlewareObj]) => + Object.assign(initialObj, middlewareObj) + ) + } else { + const result = middleware(argv, yargs) + if (beforeValidation && isPromise(result)) throw beforeValidationError + + return isPromise(result) + ? result.then(middlewareObj => Object.assign(accumulation, middlewareObj)) + : Object.assign(accumulation, result) + } + }, argv) +} diff --git a/WebServer/node_modules/yargs/lib/obj-filter.js b/WebServer/node_modules/yargs/lib/obj-filter.js new file mode 100644 index 000000000..c344ac58c --- /dev/null +++ b/WebServer/node_modules/yargs/lib/obj-filter.js @@ -0,0 +1,11 @@ +'use strict' +module.exports = function objFilter (original, filter) { + const obj = {} + filter = filter || ((k, v) => true) + Object.keys(original || {}).forEach((key) => { + if (filter(key, original[key])) { + obj[key] = original[key] + } + }) + return obj +} diff --git a/WebServer/node_modules/yargs/lib/usage.js b/WebServer/node_modules/yargs/lib/usage.js new file mode 100644 index 000000000..8c3b068f8 --- /dev/null +++ b/WebServer/node_modules/yargs/lib/usage.js @@ -0,0 +1,540 @@ +'use strict' +// this file handles outputting usage instructions, +// failures, etc. keeps logging in one place. +const decamelize = require('./decamelize') +const stringWidth = require('string-width') +const objFilter = require('./obj-filter') +const path = require('path') +const setBlocking = require('set-blocking') +const YError = require('./yerror') + +module.exports = function usage (yargs, y18n) { + const __ = y18n.__ + const self = {} + + // methods for ouputting/building failure message. + const fails = [] + self.failFn = function failFn (f) { + fails.push(f) + } + + let failMessage = null + let showHelpOnFail = true + self.showHelpOnFail = function showHelpOnFailFn (enabled, message) { + if (typeof enabled === 'string') { + message = enabled + enabled = true + } else if (typeof enabled === 'undefined') { + enabled = true + } + failMessage = message + showHelpOnFail = enabled + return self + } + + let failureOutput = false + self.fail = function fail (msg, err) { + const logger = yargs._getLoggerInstance() + + if (fails.length) { + for (let i = fails.length - 1; i >= 0; --i) { + fails[i](msg, err, self) + } + } else { + if (yargs.getExitProcess()) setBlocking(true) + + // don't output failure message more than once + if (!failureOutput) { + failureOutput = true + if (showHelpOnFail) { + yargs.showHelp('error') + logger.error() + } + if (msg || err) logger.error(msg || err) + if (failMessage) { + if (msg || err) logger.error('') + logger.error(failMessage) + } + } + + err = err || new YError(msg) + if (yargs.getExitProcess()) { + return yargs.exit(1) + } else if (yargs._hasParseCallback()) { + return yargs.exit(1, err) + } else { + throw err + } + } + } + + // methods for ouputting/building help (usage) message. + let usages = [] + let usageDisabled = false + self.usage = (msg, description) => { + if (msg === null) { + usageDisabled = true + usages = [] + return + } + usageDisabled = false + usages.push([msg, description || '']) + return self + } + self.getUsage = () => { + return usages + } + self.getUsageDisabled = () => { + return usageDisabled + } + + self.getPositionalGroupName = () => { + return __('Positionals:') + } + + let examples = [] + self.example = (cmd, description) => { + examples.push([cmd, description || '']) + } + + let commands = [] + self.command = function command (cmd, description, isDefault, aliases) { + // the last default wins, so cancel out any previously set default + if (isDefault) { + commands = commands.map((cmdArray) => { + cmdArray[2] = false + return cmdArray + }) + } + commands.push([cmd, description || '', isDefault, aliases]) + } + self.getCommands = () => commands + + let descriptions = {} + self.describe = function describe (key, desc) { + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.describe(k, key[k]) + }) + } else { + descriptions[key] = desc + } + } + self.getDescriptions = () => descriptions + + let epilog + self.epilog = (msg) => { + epilog = msg + } + + let wrapSet = false + let wrap + self.wrap = (cols) => { + wrapSet = true + wrap = cols + } + + function getWrap () { + if (!wrapSet) { + wrap = windowWidth() + wrapSet = true + } + + return wrap + } + + const deferY18nLookupPrefix = '__yargsString__:' + self.deferY18nLookup = str => deferY18nLookupPrefix + str + + const defaultGroup = 'Options:' + self.help = function help () { + normalizeAliases() + + // handle old demanded API + const base$0 = path.basename(yargs.$0) + const demandedOptions = yargs.getDemandedOptions() + const demandedCommands = yargs.getDemandedCommands() + const groups = yargs.getGroups() + const options = yargs.getOptions() + + let keys = [] + keys = keys.concat(Object.keys(descriptions)) + keys = keys.concat(Object.keys(demandedOptions)) + keys = keys.concat(Object.keys(demandedCommands)) + keys = keys.concat(Object.keys(options.default)) + keys = keys.filter(filterHiddenOptions) + keys = Object.keys(keys.reduce((acc, key) => { + if (key !== '_') acc[key] = true + return acc + }, {})) + + const theWrap = getWrap() + const ui = require('cliui')({ + width: theWrap, + wrap: !!theWrap + }) + + // the usage string. + if (!usageDisabled) { + if (usages.length) { + // user-defined usage. + usages.forEach((usage) => { + ui.div(`${usage[0].replace(/\$0/g, base$0)}`) + if (usage[1]) { + ui.div({ text: `${usage[1]}`, padding: [1, 0, 0, 0] }) + } + }) + ui.div() + } else if (commands.length) { + let u = null + // demonstrate how commands are used. + if (demandedCommands._) { + u = `${base$0} <${__('command')}>\n` + } else { + u = `${base$0} [${__('command')}]\n` + } + ui.div(`${u}`) + } + } + + // your application's commands, i.e., non-option + // arguments populated in '_'. + if (commands.length) { + ui.div(__('Commands:')) + + const context = yargs.getContext() + const parentCommands = context.commands.length ? `${context.commands.join(' ')} ` : '' + + if (yargs.getParserConfiguration()['sort-commands'] === true) { + commands = commands.sort((a, b) => a[0].localeCompare(b[0])) + } + + commands.forEach((command) => { + const commandString = `${base$0} ${parentCommands}${command[0].replace(/^\$0 ?/, '')}` // drop $0 from default commands. + ui.span( + { + text: commandString, + padding: [0, 2, 0, 2], + width: maxWidth(commands, theWrap, `${base$0}${parentCommands}`) + 4 + }, + { text: command[1] } + ) + const hints = [] + if (command[2]) hints.push(`[${__('default:').slice(0, -1)}]`) // TODO hacking around i18n here + if (command[3] && command[3].length) { + hints.push(`[${__('aliases:')} ${command[3].join(', ')}]`) + } + if (hints.length) { + ui.div({ text: hints.join(' '), padding: [0, 0, 0, 2], align: 'right' }) + } else { + ui.div() + } + }) + + ui.div() + } + + // perform some cleanup on the keys array, making it + // only include top-level keys not their aliases. + const aliasKeys = (Object.keys(options.alias) || []) + .concat(Object.keys(yargs.parsed.newAliases) || []) + + keys = keys.filter(key => !yargs.parsed.newAliases[key] && aliasKeys.every(alias => (options.alias[alias] || []).indexOf(key) === -1)) + + // populate 'Options:' group with any keys that have not + // explicitly had a group set. + if (!groups[defaultGroup]) groups[defaultGroup] = [] + addUngroupedKeys(keys, options.alias, groups) + + // display 'Options:' table along with any custom tables: + Object.keys(groups).forEach((groupName) => { + if (!groups[groupName].length) return + + // if we've grouped the key 'f', but 'f' aliases 'foobar', + // normalizedKeys should contain only 'foobar'. + const normalizedKeys = groups[groupName].filter(filterHiddenOptions).map((key) => { + if (~aliasKeys.indexOf(key)) return key + for (let i = 0, aliasKey; (aliasKey = aliasKeys[i]) !== undefined; i++) { + if (~(options.alias[aliasKey] || []).indexOf(key)) return aliasKey + } + return key + }) + + if (normalizedKeys.length < 1) return + + ui.div(__(groupName)) + + // actually generate the switches string --foo, -f, --bar. + const switches = normalizedKeys.reduce((acc, key) => { + acc[key] = [ key ].concat(options.alias[key] || []) + .map(sw => { + // for the special positional group don't + // add '--' or '-' prefix. + if (groupName === self.getPositionalGroupName()) return sw + else return (sw.length > 1 ? '--' : '-') + sw + }) + .join(', ') + + return acc + }, {}) + + normalizedKeys.forEach((key) => { + const kswitch = switches[key] + let desc = descriptions[key] || '' + let type = null + + if (~desc.lastIndexOf(deferY18nLookupPrefix)) desc = __(desc.substring(deferY18nLookupPrefix.length)) + + if (~options.boolean.indexOf(key)) type = `[${__('boolean')}]` + if (~options.count.indexOf(key)) type = `[${__('count')}]` + if (~options.string.indexOf(key)) type = `[${__('string')}]` + if (~options.normalize.indexOf(key)) type = `[${__('string')}]` + if (~options.array.indexOf(key)) type = `[${__('array')}]` + if (~options.number.indexOf(key)) type = `[${__('number')}]` + + const extra = [ + type, + (key in demandedOptions) ? `[${__('required')}]` : null, + options.choices && options.choices[key] ? `[${__('choices:')} ${ + self.stringifiedValues(options.choices[key])}]` : null, + defaultString(options.default[key], options.defaultDescription[key]) + ].filter(Boolean).join(' ') + + ui.span( + { text: kswitch, padding: [0, 2, 0, 2], width: maxWidth(switches, theWrap) + 4 }, + desc + ) + + if (extra) ui.div({ text: extra, padding: [0, 0, 0, 2], align: 'right' }) + else ui.div() + }) + + ui.div() + }) + + // describe some common use-cases for your application. + if (examples.length) { + ui.div(__('Examples:')) + + examples.forEach((example) => { + example[0] = example[0].replace(/\$0/g, base$0) + }) + + examples.forEach((example) => { + if (example[1] === '') { + ui.div( + { + text: example[0], + padding: [0, 2, 0, 2] + } + ) + } else { + ui.div( + { + text: example[0], + padding: [0, 2, 0, 2], + width: maxWidth(examples, theWrap) + 4 + }, { + text: example[1] + } + ) + } + }) + + ui.div() + } + + // the usage string. + if (epilog) { + const e = epilog.replace(/\$0/g, base$0) + ui.div(`${e}\n`) + } + + // Remove the trailing white spaces + return ui.toString().replace(/\s*$/, '') + } + + // return the maximum width of a string + // in the left-hand column of a table. + function maxWidth (table, theWrap, modifier) { + let width = 0 + + // table might be of the form [leftColumn], + // or {key: leftColumn} + if (!Array.isArray(table)) { + table = Object.keys(table).map(key => [table[key]]) + } + + table.forEach((v) => { + width = Math.max( + stringWidth(modifier ? `${modifier} ${v[0]}` : v[0]), + width + ) + }) + + // if we've enabled 'wrap' we should limit + // the max-width of the left-column. + if (theWrap) width = Math.min(width, parseInt(theWrap * 0.5, 10)) + + return width + } + + // make sure any options set for aliases, + // are copied to the keys being aliased. + function normalizeAliases () { + // handle old demanded API + const demandedOptions = yargs.getDemandedOptions() + const options = yargs.getOptions() + + ;(Object.keys(options.alias) || []).forEach((key) => { + options.alias[key].forEach((alias) => { + // copy descriptions. + if (descriptions[alias]) self.describe(key, descriptions[alias]) + // copy demanded. + if (alias in demandedOptions) yargs.demandOption(key, demandedOptions[alias]) + // type messages. + if (~options.boolean.indexOf(alias)) yargs.boolean(key) + if (~options.count.indexOf(alias)) yargs.count(key) + if (~options.string.indexOf(alias)) yargs.string(key) + if (~options.normalize.indexOf(alias)) yargs.normalize(key) + if (~options.array.indexOf(alias)) yargs.array(key) + if (~options.number.indexOf(alias)) yargs.number(key) + }) + }) + } + + // given a set of keys, place any keys that are + // ungrouped under the 'Options:' grouping. + function addUngroupedKeys (keys, aliases, groups) { + let groupedKeys = [] + let toCheck = null + Object.keys(groups).forEach((group) => { + groupedKeys = groupedKeys.concat(groups[group]) + }) + + keys.forEach((key) => { + toCheck = [key].concat(aliases[key]) + if (!toCheck.some(k => groupedKeys.indexOf(k) !== -1)) { + groups[defaultGroup].push(key) + } + }) + return groupedKeys + } + + function filterHiddenOptions (key) { + return yargs.getOptions().hiddenOptions.indexOf(key) < 0 || yargs.parsed.argv[yargs.getOptions().showHiddenOpt] + } + + self.showHelp = (level) => { + const logger = yargs._getLoggerInstance() + if (!level) level = 'error' + const emit = typeof level === 'function' ? level : logger[level] + emit(self.help()) + } + + self.functionDescription = (fn) => { + const description = fn.name ? decamelize(fn.name, '-') : __('generated-value') + return ['(', description, ')'].join('') + } + + self.stringifiedValues = function stringifiedValues (values, separator) { + let string = '' + const sep = separator || ', ' + const array = [].concat(values) + + if (!values || !array.length) return string + + array.forEach((value) => { + if (string.length) string += sep + string += JSON.stringify(value) + }) + + return string + } + + // format the default-value-string displayed in + // the right-hand column. + function defaultString (value, defaultDescription) { + let string = `[${__('default:')} ` + + if (value === undefined && !defaultDescription) return null + + if (defaultDescription) { + string += defaultDescription + } else { + switch (typeof value) { + case 'string': + string += `"${value}"` + break + case 'object': + string += JSON.stringify(value) + break + default: + string += value + } + } + + return `${string}]` + } + + // guess the width of the console window, max-width 80. + function windowWidth () { + const maxWidth = 80 + if (typeof process === 'object' && process.stdout && process.stdout.columns) { + return Math.min(maxWidth, process.stdout.columns) + } else { + return maxWidth + } + } + + // logic for displaying application version. + let version = null + self.version = (ver) => { + version = ver + } + + self.showVersion = () => { + const logger = yargs._getLoggerInstance() + logger.log(version) + } + + self.reset = function reset (localLookup) { + // do not reset wrap here + // do not reset fails here + failMessage = null + failureOutput = false + usages = [] + usageDisabled = false + epilog = undefined + examples = [] + commands = [] + descriptions = objFilter(descriptions, (k, v) => !localLookup[k]) + return self + } + + let frozen + self.freeze = function freeze () { + frozen = {} + frozen.failMessage = failMessage + frozen.failureOutput = failureOutput + frozen.usages = usages + frozen.usageDisabled = usageDisabled + frozen.epilog = epilog + frozen.examples = examples + frozen.commands = commands + frozen.descriptions = descriptions + } + self.unfreeze = function unfreeze () { + failMessage = frozen.failMessage + failureOutput = frozen.failureOutput + usages = frozen.usages + usageDisabled = frozen.usageDisabled + epilog = frozen.epilog + examples = frozen.examples + commands = frozen.commands + descriptions = frozen.descriptions + frozen = undefined + } + + return self +} diff --git a/WebServer/node_modules/yargs/lib/validation.js b/WebServer/node_modules/yargs/lib/validation.js new file mode 100644 index 000000000..e9bbb12bc --- /dev/null +++ b/WebServer/node_modules/yargs/lib/validation.js @@ -0,0 +1,341 @@ +'use strict' +const argsert = require('./argsert') +const objFilter = require('./obj-filter') +const specialKeys = ['$0', '--', '_'] + +// validation-type-stuff, missing params, +// bad implications, custom checks. +module.exports = function validation (yargs, usage, y18n) { + const __ = y18n.__ + const __n = y18n.__n + const self = {} + + // validate appropriate # of non-option + // arguments were provided, i.e., '_'. + self.nonOptionCount = function nonOptionCount (argv) { + const demandedCommands = yargs.getDemandedCommands() + // don't count currently executing commands + const _s = argv._.length - yargs.getContext().commands.length + + if (demandedCommands._ && (_s < demandedCommands._.min || _s > demandedCommands._.max)) { + if (_s < demandedCommands._.min) { + if (demandedCommands._.minMsg !== undefined) { + usage.fail( + // replace $0 with observed, $1 with expected. + demandedCommands._.minMsg ? demandedCommands._.minMsg.replace(/\$0/g, _s).replace(/\$1/, demandedCommands._.min) : null + ) + } else { + usage.fail( + __('Not enough non-option arguments: got %s, need at least %s', _s, demandedCommands._.min) + ) + } + } else if (_s > demandedCommands._.max) { + if (demandedCommands._.maxMsg !== undefined) { + usage.fail( + // replace $0 with observed, $1 with expected. + demandedCommands._.maxMsg ? demandedCommands._.maxMsg.replace(/\$0/g, _s).replace(/\$1/, demandedCommands._.max) : null + ) + } else { + usage.fail( + __('Too many non-option arguments: got %s, maximum of %s', _s, demandedCommands._.max) + ) + } + } + } + } + + // validate the appropriate # of + // positional arguments were provided: + self.positionalCount = function positionalCount (required, observed) { + if (observed < required) { + usage.fail( + __('Not enough non-option arguments: got %s, need at least %s', observed, required) + ) + } + } + + // make sure all the required arguments are present. + self.requiredArguments = function requiredArguments (argv) { + const demandedOptions = yargs.getDemandedOptions() + let missing = null + + Object.keys(demandedOptions).forEach((key) => { + if (!argv.hasOwnProperty(key) || typeof argv[key] === 'undefined') { + missing = missing || {} + missing[key] = demandedOptions[key] + } + }) + + if (missing) { + const customMsgs = [] + Object.keys(missing).forEach((key) => { + const msg = missing[key] + if (msg && customMsgs.indexOf(msg) < 0) { + customMsgs.push(msg) + } + }) + + const customMsg = customMsgs.length ? `\n${customMsgs.join('\n')}` : '' + + usage.fail(__n( + 'Missing required argument: %s', + 'Missing required arguments: %s', + Object.keys(missing).length, + Object.keys(missing).join(', ') + customMsg + )) + } + } + + // check for unknown arguments (strict-mode). + self.unknownArguments = function unknownArguments (argv, aliases, positionalMap) { + const commandKeys = yargs.getCommandInstance().getCommands() + const unknown = [] + const currentContext = yargs.getContext() + + Object.keys(argv).forEach((key) => { + if (specialKeys.indexOf(key) === -1 && + !positionalMap.hasOwnProperty(key) && + !yargs._getParseContext().hasOwnProperty(key) && + !aliases.hasOwnProperty(key) + ) { + unknown.push(key) + } + }) + + if (commandKeys.length > 0) { + argv._.slice(currentContext.commands.length).forEach((key) => { + if (commandKeys.indexOf(key) === -1) { + unknown.push(key) + } + }) + } + + if (unknown.length > 0) { + usage.fail(__n( + 'Unknown argument: %s', + 'Unknown arguments: %s', + unknown.length, + unknown.join(', ') + )) + } + } + + // validate arguments limited to enumerated choices + self.limitedChoices = function limitedChoices (argv) { + const options = yargs.getOptions() + const invalid = {} + + if (!Object.keys(options.choices).length) return + + Object.keys(argv).forEach((key) => { + if (specialKeys.indexOf(key) === -1 && + options.choices.hasOwnProperty(key)) { + [].concat(argv[key]).forEach((value) => { + // TODO case-insensitive configurability + if (options.choices[key].indexOf(value) === -1 && + value !== undefined) { + invalid[key] = (invalid[key] || []).concat(value) + } + }) + } + }) + + const invalidKeys = Object.keys(invalid) + + if (!invalidKeys.length) return + + let msg = __('Invalid values:') + invalidKeys.forEach((key) => { + msg += `\n ${__( + 'Argument: %s, Given: %s, Choices: %s', + key, + usage.stringifiedValues(invalid[key]), + usage.stringifiedValues(options.choices[key]) + )}` + }) + usage.fail(msg) + } + + // custom checks, added using the `check` option on yargs. + let checks = [] + self.check = function check (f, global) { + checks.push({ + func: f, + global + }) + } + + self.customChecks = function customChecks (argv, aliases) { + for (let i = 0, f; (f = checks[i]) !== undefined; i++) { + const func = f.func + let result = null + try { + result = func(argv, aliases) + } catch (err) { + usage.fail(err.message ? err.message : err, err) + continue + } + + if (!result) { + usage.fail(__('Argument check failed: %s', func.toString())) + } else if (typeof result === 'string' || result instanceof Error) { + usage.fail(result.toString(), result) + } + } + } + + // check implications, argument foo implies => argument bar. + let implied = {} + self.implies = function implies (key, value) { + argsert(' [array|number|string]', [key, value], arguments.length) + + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.implies(k, key[k]) + }) + } else { + yargs.global(key) + if (!implied[key]) { + implied[key] = [] + } + if (Array.isArray(value)) { + value.forEach((i) => self.implies(key, i)) + } else { + implied[key].push(value) + } + } + } + self.getImplied = function getImplied () { + return implied + } + + self.implications = function implications (argv) { + const implyFail = [] + + Object.keys(implied).forEach((key) => { + const origKey = key + ;(implied[key] || []).forEach((value) => { + let num + let key = origKey + const origValue = value + + // convert string '1' to number 1 + num = Number(key) + key = isNaN(num) ? key : num + + if (typeof key === 'number') { + // check length of argv._ + key = argv._.length >= key + } else if (key.match(/^--no-.+/)) { + // check if key doesn't exist + key = key.match(/^--no-(.+)/)[1] + key = !argv[key] + } else { + // check if key exists + key = argv[key] + } + + num = Number(value) + value = isNaN(num) ? value : num + + if (typeof value === 'number') { + value = argv._.length >= value + } else if (value.match(/^--no-.+/)) { + value = value.match(/^--no-(.+)/)[1] + value = !argv[value] + } else { + value = argv[value] + } + if (key && !value) { + implyFail.push(` ${origKey} -> ${origValue}`) + } + }) + }) + + if (implyFail.length) { + let msg = `${__('Implications failed:')}\n` + + implyFail.forEach((value) => { + msg += (value) + }) + + usage.fail(msg) + } + } + + let conflicting = {} + self.conflicts = function conflicts (key, value) { + argsert(' [array|string]', [key, value], arguments.length) + + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.conflicts(k, key[k]) + }) + } else { + yargs.global(key) + if (!conflicting[key]) { + conflicting[key] = [] + } + if (Array.isArray(value)) { + value.forEach((i) => self.conflicts(key, i)) + } else { + conflicting[key].push(value) + } + } + } + self.getConflicting = () => conflicting + + self.conflicting = function conflictingFn (argv) { + Object.keys(argv).forEach((key) => { + if (conflicting[key]) { + conflicting[key].forEach((value) => { + // we default keys to 'undefined' that have been configured, we should not + // apply conflicting check unless they are a value other than 'undefined'. + if (value && argv[key] !== undefined && argv[value] !== undefined) { + usage.fail(__('Arguments %s and %s are mutually exclusive', key, value)) + } + }) + } + }) + } + + self.recommendCommands = function recommendCommands (cmd, potentialCommands) { + const distance = require('./levenshtein') + const threshold = 3 // if it takes more than three edits, let's move on. + potentialCommands = potentialCommands.sort((a, b) => b.length - a.length) + + let recommended = null + let bestDistance = Infinity + for (let i = 0, candidate; (candidate = potentialCommands[i]) !== undefined; i++) { + const d = distance(cmd, candidate) + if (d <= threshold && d < bestDistance) { + bestDistance = d + recommended = candidate + } + } + if (recommended) usage.fail(__('Did you mean %s?', recommended)) + } + + self.reset = function reset (localLookup) { + implied = objFilter(implied, (k, v) => !localLookup[k]) + conflicting = objFilter(conflicting, (k, v) => !localLookup[k]) + checks = checks.filter(c => c.global) + return self + } + + let frozen + self.freeze = function freeze () { + frozen = {} + frozen.implied = implied + frozen.checks = checks + frozen.conflicting = conflicting + } + self.unfreeze = function unfreeze () { + implied = frozen.implied + checks = frozen.checks + conflicting = frozen.conflicting + frozen = undefined + } + + return self +} diff --git a/WebServer/node_modules/yargs/lib/yerror.js b/WebServer/node_modules/yargs/lib/yerror.js new file mode 100644 index 000000000..53375a0f7 --- /dev/null +++ b/WebServer/node_modules/yargs/lib/yerror.js @@ -0,0 +1,11 @@ +'use strict' +function YError (msg) { + this.name = 'YError' + this.message = msg || 'yargs error' + Error.captureStackTrace(this, YError) +} + +YError.prototype = Object.create(Error.prototype) +YError.prototype.constructor = YError + +module.exports = YError diff --git a/WebServer/node_modules/yargs/locales/be.json b/WebServer/node_modules/yargs/locales/be.json new file mode 100644 index 000000000..141ebe1e1 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/be.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Каманды:", + "Options:": "Опцыі:", + "Examples:": "Прыклады:", + "boolean": "булевы тып", + "count": "падлік", + "string": "радковы тып", + "number": "лік", + "array": "ма�іў", + "required": "неабходна", + "default:": "па змаўчанні:", + "choices:": "магчыма�ці:", + "aliases:": "аліа�ы:", + "generated-value": "згенераванае знач�нне", + "Not enough non-option arguments: got %s, need at least %s": "�еда�таткова неапцыйных аргументаў: ё�ць %s, тр�ба �к мінімум %s", + "Too many non-option arguments: got %s, maximum of %s": "Занадта шмат неапцыйных аргументаў: ё�ць %s, мак�імум дапушчальна %s", + "Missing argument value: %s": { + "one": "�е хапае знач�нн� аргументу: %s", + "other": "�е хапае знач�нн�ў аргументаў: %s" + }, + "Missing required argument: %s": { + "one": "�е хапае неабходнага аргументу: %s", + "other": "�е хапае неабходных аргументаў: %s" + }, + "Unknown argument: %s": { + "one": "�ев�домы аргумент: %s", + "other": "�ев�домы� аргументы: %s" + }, + "Invalid values:": "�е�апраўдны� знач�нн�:", + "Argument: %s, Given: %s, Choices: %s": "�ргумент: %s, Дадзенае знач�нне: %s, Магчыма�ці: %s", + "Argument check failed: %s": "Праверка аргументаў не ўдала��: %s", + "Implications failed:": "Дадзены аргумент патрабуе на�тупны дадатковы аргумент:", + "Not enough arguments following: %s": "�еда�таткова на�тупных аргументаў: %s", + "Invalid JSON config file: %s": "�е�апраўдны файл канфігурацыі JSON: %s", + "Path to JSON config file": "Шл�х да файла канфігурацыі JSON", + "Show help": "Паказаць дапамогу", + "Show version number": "Паказаць нумар вер�іі", + "Did you mean %s?": "Вы мелі на ўвазе %s?" +} diff --git a/WebServer/node_modules/yargs/locales/de.json b/WebServer/node_modules/yargs/locales/de.json new file mode 100644 index 000000000..d805710b0 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/de.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Kommandos:", + "Options:": "Optionen:", + "Examples:": "Beispiele:", + "boolean": "boolean", + "count": "Zähler", + "string": "string", + "number": "Zahl", + "array": "array", + "required": "erforderlich", + "default:": "Standard:", + "choices:": "Möglichkeiten:", + "aliases:": "Aliase:", + "generated-value": "Generierter-Wert", + "Not enough non-option arguments: got %s, need at least %s": "Nicht genügend Argumente ohne Optionen: %s vorhanden, mindestens %s benötigt", + "Too many non-option arguments: got %s, maximum of %s": "Zu viele Argumente ohne Optionen: %s vorhanden, maximal %s erlaubt", + "Missing argument value: %s": { + "one": "Fehlender Argumentwert: %s", + "other": "Fehlende Argumentwerte: %s" + }, + "Missing required argument: %s": { + "one": "Fehlendes Argument: %s", + "other": "Fehlende Argumente: %s" + }, + "Unknown argument: %s": { + "one": "Unbekanntes Argument: %s", + "other": "Unbekannte Argumente: %s" + }, + "Invalid values:": "Unzulässige Werte:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gegeben: %s, Möglichkeiten: %s", + "Argument check failed: %s": "Argumente-Check fehlgeschlagen: %s", + "Implications failed:": "Implikationen fehlgeschlagen:", + "Not enough arguments following: %s": "Nicht genügend Argumente nach: %s", + "Invalid JSON config file: %s": "Fehlerhafte JSON-Config Datei: %s", + "Path to JSON config file": "Pfad zur JSON-Config Datei", + "Show help": "Hilfe anzeigen", + "Show version number": "Version anzeigen", + "Did you mean %s?": "Meintest du %s?" +} diff --git a/WebServer/node_modules/yargs/locales/en.json b/WebServer/node_modules/yargs/locales/en.json new file mode 100644 index 000000000..fc65c2a0d --- /dev/null +++ b/WebServer/node_modules/yargs/locales/en.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Commands:", + "Options:": "Options:", + "Examples:": "Examples:", + "boolean": "boolean", + "count": "count", + "string": "string", + "number": "number", + "array": "array", + "required": "required", + "default:": "default:", + "choices:": "choices:", + "aliases:": "aliases:", + "generated-value": "generated-value", + "Not enough non-option arguments: got %s, need at least %s": "Not enough non-option arguments: got %s, need at least %s", + "Too many non-option arguments: got %s, maximum of %s": "Too many non-option arguments: got %s, maximum of %s", + "Missing argument value: %s": { + "one": "Missing argument value: %s", + "other": "Missing argument values: %s" + }, + "Missing required argument: %s": { + "one": "Missing required argument: %s", + "other": "Missing required arguments: %s" + }, + "Unknown argument: %s": { + "one": "Unknown argument: %s", + "other": "Unknown arguments: %s" + }, + "Invalid values:": "Invalid values:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Given: %s, Choices: %s", + "Argument check failed: %s": "Argument check failed: %s", + "Implications failed:": "Implications failed:", + "Not enough arguments following: %s": "Not enough arguments following: %s", + "Invalid JSON config file: %s": "Invalid JSON config file: %s", + "Path to JSON config file": "Path to JSON config file", + "Show help": "Show help", + "Show version number": "Show version number", + "Did you mean %s?": "Did you mean %s?", + "Arguments %s and %s are mutually exclusive" : "Arguments %s and %s are mutually exclusive", + "Positionals:": "Positionals:", + "command": "command" +} diff --git a/WebServer/node_modules/yargs/locales/es.json b/WebServer/node_modules/yargs/locales/es.json new file mode 100644 index 000000000..d7c8af9f8 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/es.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Comandos:", + "Options:": "Opciones:", + "Examples:": "Ejemplos:", + "boolean": "booleano", + "count": "cuenta", + "string": "cadena de caracteres", + "number": "número", + "array": "tabla", + "required": "requerido", + "default:": "defecto:", + "choices:": "selección:", + "aliases:": "alias:", + "generated-value": "valor-generado", + "Not enough non-option arguments: got %s, need at least %s": "Hacen falta argumentos no-opcionales: Número recibido %s, necesita por lo menos %s", + "Too many non-option arguments: got %s, maximum of %s": "Demasiados argumentos no-opcionales: Número recibido %s, máximo es %s", + "Missing argument value: %s": { + "one": "Falta argumento: %s", + "other": "Faltan argumentos: %s" + }, + "Missing required argument: %s": { + "one": "Falta argumento requerido: %s", + "other": "Faltan argumentos requeridos: %s" + }, + "Unknown argument: %s": { + "one": "Argumento desconocido: %s", + "other": "Argumentos desconocidos: %s" + }, + "Invalid values:": "Valores inválidos:", + "Argument: %s, Given: %s, Choices: %s": "Argumento: %s, Recibido: %s, Seleccionados: %s", + "Argument check failed: %s": "Verificación de argumento ha fallado: %s", + "Implications failed:": "Implicaciones fallidas:", + "Not enough arguments following: %s": "No hay suficientes argumentos después de: %s", + "Invalid JSON config file: %s": "Archivo de configuración JSON inválido: %s", + "Path to JSON config file": "Ruta al archivo de configuración JSON", + "Show help": "Muestra ayuda", + "Show version number": "Muestra número de versión", + "Did you mean %s?": "Quisiste decir %s?" +} diff --git a/WebServer/node_modules/yargs/locales/fr.json b/WebServer/node_modules/yargs/locales/fr.json new file mode 100644 index 000000000..481f47e37 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/fr.json @@ -0,0 +1,37 @@ +{ + "Commands:": "Commandes:", + "Options:": "Options:", + "Examples:": "Exemples:", + "boolean": "booléen", + "count": "comptage", + "string": "chaine de caractère", + "number": "nombre", + "array": "tableau", + "required": "requis", + "default:": "défaut:", + "choices:": "choix:", + "generated-value": "valeur générée", + "Not enough non-option arguments: got %s, need at least %s": "Pas assez d'arguments non-option: reçu %s, besoin d'au moins %s", + "Too many non-option arguments: got %s, maximum of %s": "Trop d'arguments non-option: reçu %s, maximum %s", + "Missing argument value: %s": { + "one": "Argument manquant: %s", + "other": "Arguments manquants: %s" + }, + "Missing required argument: %s": { + "one": "Argument requis manquant: %s", + "other": "Arguments requis manquants: %s" + }, + "Unknown argument: %s": { + "one": "Argument inconnu: %s", + "other": "Arguments inconnus: %s" + }, + "Invalid values:": "Valeurs invalides:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Donné: %s, Choix: %s", + "Argument check failed: %s": "Echec de la vérification de l'argument: %s", + "Implications failed:": "Implications échouées:", + "Not enough arguments following: %s": "Pas assez d'arguments suivant: %s", + "Invalid JSON config file: %s": "Fichier de configuration JSON invalide: %s", + "Path to JSON config file": "Chemin du fichier de configuration JSON", + "Show help": "Affiche de l'aide", + "Show version number": "Affiche le numéro de version" +} diff --git a/WebServer/node_modules/yargs/locales/hi.json b/WebServer/node_modules/yargs/locales/hi.json new file mode 100644 index 000000000..2cd677acb --- /dev/null +++ b/WebServer/node_modules/yargs/locales/hi.json @@ -0,0 +1,42 @@ +{ + "Commands:": "आदेश:", + "Options:": "विकल�प:", + "Examples:": "उदाहरण:", + "boolean": "सत�यता", + "count": "संख�या", + "string": "वर�णों का तार ", + "number": "अंक", + "array": "सरणी", + "required": "आवश�यक", + "default:": "डिफॉल�ट:", + "choices:": "विकल�प:", + "aliases:": "उपनाम:", + "generated-value": "उत�पन�न-मूल�य", + "Not enough non-option arguments: got %s, need at least %s": "पर�याप�त गैर-विकल�प तर�क प�राप�त नहीं: %s प�राप�त, कम से कम %s की आवश�यकता है", + "Too many non-option arguments: got %s, maximum of %s": "बह�त सारे गैर-विकल�प तर�क: %s प�राप�त, अधिकतम %s मान�य", + "Missing argument value: %s": { + "one": "क�छ तर�को के मूल�य ग�म हैं: %s", + "other": "क�छ तर�को के मूल�य ग�म हैं: %s" + }, + "Missing required argument: %s": { + "one": "आवश�यक तर�क ग�म हैं: %s", + "other": "आवश�यक तर�क ग�म हैं: %s" + }, + "Unknown argument: %s": { + "one": "अज�ञात तर�क प�राप�त: %s", + "other": "अज�ञात तर�क प�राप�त: %s" + }, + "Invalid values:": "अमान�य मूल�य:", + "Argument: %s, Given: %s, Choices: %s": "तर�क: %s, प�राप�त: %s, विकल�प: %s", + "Argument check failed: %s": "तर�क जांच विफल: %s", + "Implications failed:": "दि� ग� तर�क के लि� अतिरिक�त तर�क की अपेक�षा है:", + "Not enough arguments following: %s": "निम�नलिखित के बाद पर�याप�त तर�क नहीं प�राप�त: %s", + "Invalid JSON config file: %s": "अमान�य JSON config फाइल: %s", + "Path to JSON config file": "JSON config फाइल का पथ", + "Show help": "सहायता दिखा��", + "Show version number": "Version संख�या दिखा��", + "Did you mean %s?": "क�या आपका मतलब है %s?", + "Arguments %s and %s are mutually exclusive" : "तर�क %s और %s परस�पर अनन�य हैं", + "Positionals:": "स�थानीय:", + "command": "आदेश" +} diff --git a/WebServer/node_modules/yargs/locales/hu.json b/WebServer/node_modules/yargs/locales/hu.json new file mode 100644 index 000000000..7b7d16606 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/hu.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Parancsok:", + "Options:": "Opciók:", + "Examples:": "Példák:", + "boolean": "boolean", + "count": "számláló", + "string": "szöveg", + "number": "szám", + "array": "tömb", + "required": "kötelező", + "default:": "alapértelmezett:", + "choices:": "lehetőségek:", + "aliases:": "aliaszok:", + "generated-value": "generált-érték", + "Not enough non-option arguments: got %s, need at least %s": "Nincs elég nem opcionális argumentum: %s van, legalább %s kell", + "Too many non-option arguments: got %s, maximum of %s": "Túl sok nem opciánlis argumentum van: %s van, maximum %s lehet", + "Missing argument value: %s": { + "one": "Hiányzó argumentum érték: %s", + "other": "Hiányzó argumentum értékek: %s" + }, + "Missing required argument: %s": { + "one": "Hiányzó kötelező argumentum: %s", + "other": "Hiányzó kötelező argumentumok: %s" + }, + "Unknown argument: %s": { + "one": "Ismeretlen argumentum: %s", + "other": "Ismeretlen argumentumok: %s" + }, + "Invalid values:": "Érvénytelen érték:", + "Argument: %s, Given: %s, Choices: %s": "Argumentum: %s, Megadott: %s, Lehetőségek: %s", + "Argument check failed: %s": "Argumentum ellenőrzés sikertelen: %s", + "Implications failed:": "Implikációk sikertelenek:", + "Not enough arguments following: %s": "Nem elég argumentum követi: %s", + "Invalid JSON config file: %s": "Érvénytelen JSON konfigurációs file: %s", + "Path to JSON config file": "JSON konfigurációs file helye", + "Show help": "Súgo megjelenítése", + "Show version number": "Verziószám megjelenítése", + "Did you mean %s?": "Erre gondoltál %s?" +} diff --git a/WebServer/node_modules/yargs/locales/id.json b/WebServer/node_modules/yargs/locales/id.json new file mode 100644 index 000000000..87e441cd8 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/id.json @@ -0,0 +1,43 @@ + +{ + "Commands:": "Perintah:", + "Options:": "Pilihan:", + "Examples:": "Contoh:", + "boolean": "boolean", + "count": "jumlah", + "number": "nomor", + "string": "string", + "array": "larik", + "required": "diperlukan", + "default:": "bawaan:", + "aliases:": "istilah lain:", + "choices:": "pilihan:", + "generated-value": "nilai-yang-dihasilkan", + "Not enough non-option arguments: got %s, need at least %s": "Argumen wajib kurang: hanya %s, minimal %s", + "Too many non-option arguments: got %s, maximum of %s": "Terlalu banyak argumen wajib: ada %s, maksimal %s", + "Missing argument value: %s": { + "one": "Kurang argumen: %s", + "other": "Kurang argumen: %s" + }, + "Missing required argument: %s": { + "one": "Kurang argumen wajib: %s", + "other": "Kurang argumen wajib: %s" + }, + "Unknown argument: %s": { + "one": "Argumen tak diketahui: %s", + "other": "Argumen tak diketahui: %s" + }, + "Invalid values:": "Nilai-nilai tidak valid:", + "Argument: %s, Given: %s, Choices: %s": "Argumen: %s, Diberikan: %s, Pilihan: %s", + "Argument check failed: %s": "Pemeriksaan argument gagal: %s", + "Implications failed:": "Implikasi gagal:", + "Not enough arguments following: %s": "Kurang argumen untuk: %s", + "Invalid JSON config file: %s": "Berkas konfigurasi JSON tidak valid: %s", + "Path to JSON config file": "Alamat berkas konfigurasi JSON", + "Show help": "Lihat bantuan", + "Show version number": "Lihat nomor versi", + "Did you mean %s?": "Maksud Anda: %s?", + "Arguments %s and %s are mutually exclusive" : "Argumen %s dan %s saling eksklusif", + "Positionals:": "Posisional-posisional:", + "command": "perintah" +} diff --git a/WebServer/node_modules/yargs/locales/it.json b/WebServer/node_modules/yargs/locales/it.json new file mode 100644 index 000000000..f9eb3756e --- /dev/null +++ b/WebServer/node_modules/yargs/locales/it.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Comandi:", + "Options:": "Opzioni:", + "Examples:": "Esempi:", + "boolean": "booleano", + "count": "contatore", + "string": "stringa", + "number": "numero", + "array": "vettore", + "required": "richiesto", + "default:": "predefinito:", + "choices:": "scelte:", + "aliases:": "alias:", + "generated-value": "valore generato", + "Not enough non-option arguments: got %s, need at least %s": "Numero insufficiente di argomenti non opzione: inseriti %s, richiesti almeno %s", + "Too many non-option arguments: got %s, maximum of %s": "Troppi argomenti non opzione: inseriti %s, massimo possibile %s", + "Missing argument value: %s": { + "one": "Argomento mancante: %s", + "other": "Argomenti mancanti: %s" + }, + "Missing required argument: %s": { + "one": "Argomento richiesto mancante: %s", + "other": "Argomenti richiesti mancanti: %s" + }, + "Unknown argument: %s": { + "one": "Argomento sconosciuto: %s", + "other": "Argomenti sconosciuti: %s" + }, + "Invalid values:": "Valori non validi:", + "Argument: %s, Given: %s, Choices: %s": "Argomento: %s, Richiesto: %s, Scelte: %s", + "Argument check failed: %s": "Controllo dell'argomento fallito: %s", + "Implications failed:": "Argomenti impliciti non soddisfatti:", + "Not enough arguments following: %s": "Argomenti insufficienti dopo: %s", + "Invalid JSON config file: %s": "File di configurazione JSON non valido: %s", + "Path to JSON config file": "Percorso del file di configurazione JSON", + "Show help": "Mostra la schermata di aiuto", + "Show version number": "Mostra il numero di versione", + "Did you mean %s?": "Intendi forse %s?" +} diff --git a/WebServer/node_modules/yargs/locales/ja.json b/WebServer/node_modules/yargs/locales/ja.json new file mode 100644 index 000000000..64ee6d3fb --- /dev/null +++ b/WebServer/node_modules/yargs/locales/ja.json @@ -0,0 +1,42 @@ +{ + "Commands:": "コマンド:", + "Options:": "オプション:", + "Examples:": "例:", + "boolean": "真�", + "count": "カウント", + "string": "文字列", + "number": "数値", + "array": "�列", + "required": "必須", + "default:": "デフォルト:", + "choices:": "�択������:", + "aliases:": "エイリアス:", + "generated-value": "生��れ�値", + "Not enough non-option arguments: got %s, need at least %s": "オプション����引数� %s 個���足�����。少���も %s 個�引数�必���:", + "Too many non-option arguments: got %s, maximum of %s": "オプション����引数� %s 個��多����。最大� %s 個����:", + "Missing argument value: %s": { + "one": "引数�見��り��ん: %s", + "other": "引数�見��り��ん: %s" + }, + "Missing required argument: %s": { + "one": "必須�引数�見��り��ん: %s", + "other": "必須�引数�見��り��ん: %s" + }, + "Unknown argument: %s": { + "one": "未知�引数��: %s", + "other": "未知�引数��: %s" + }, + "Invalid values:": "�正�値��:", + "Argument: %s, Given: %s, Choices: %s": "引数� %s ��。指定��る�� %s ���。�択������: %s", + "Argument check failed: %s": "引数��ェック�失敗����: %s", + "Implications failed:": "オプション�組������正�生����:", + "Not enough arguments following: %s": "次�引数��足�����。: %s", + "Invalid JSON config file: %s": "JSON�設定ファイル��正��: %s", + "Path to JSON config file": "JSON�設定ファイル���path", + "Show help": "ヘルプを表示", + "Show version number": "�ージョンを表示", + "Did you mean %s?": "も���� %s?", + "Arguments %s and %s are mutually exclusive" : "引数 %s � %s ��時�指定����ん", + "Positionals:": "�置:", + "command": "コマンド" +} diff --git a/WebServer/node_modules/yargs/locales/ko.json b/WebServer/node_modules/yargs/locales/ko.json new file mode 100644 index 000000000..0eaeab2f8 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/ko.json @@ -0,0 +1,42 @@ +{ + "Commands:": "명령:", + "Options:": "옵션:", + "Examples:": "예시:", + "boolean": "여부", + "count": "개수", + "string": "문�열", + "number": "숫�", + "array": "배열", + "required": "필수", + "default:": "기본:", + "choices:": "선�:", + "aliases:": "별칭:", + "generated-value": "�성� 값", + "Not enough non-option arguments: got %s, need at least %s": "옵션� 아닌 ��가 충분치 않습니다: %s개를 받았지만, �어� %s개는 필요합니다", + "Too many non-option arguments: got %s, maximum of %s": "옵션� 아닌 ��가 너무 많습니다: %s개를 받았지만, %s개 �하여야 합니다", + "Missing argument value: %s": { + "one": "��값� 받지 못했습니다: %s", + "other": "��값들� 받지 못했습니다: %s" + }, + "Missing required argument: %s": { + "one": "필수 ��를 받지 못했습니다: %s", + "other": "필수 ��들� 받지 못했습니다: %s" + }, + "Unknown argument: %s": { + "one": "알 수 없는 ��입니다: %s", + "other": "알 수 없는 ��들입니다: %s" + }, + "Invalid values:": "잘못� 값입니다:", + "Argument: %s, Given: %s, Choices: %s": "��: %s, 입력받� 값: %s, 선�지: %s", + "Argument check failed: %s": "유효하지 않� ��입니다: %s", + "Implications failed:": "옵션� 조합� 잘못�었습니다:", + "Not enough arguments following: %s": "��가 충분하게 주어지지 않았습니다: %s", + "Invalid JSON config file: %s": "유효하지 않� JSON 설정파�입니다: %s", + "Path to JSON config file": "JSON 설정파� 경로", + "Show help": "�움�� 보여�니다", + "Show version number": "버전 넘버를 보여�니다", + "Did you mean %s?": "찾고계신게 %s입니까?", + "Arguments %s and %s are mutually exclusive" : "%s와 %s ��는 같� 사용� 수 없습니다", + "Positionals:": "위치:", + "command": "명령" +} diff --git a/WebServer/node_modules/yargs/locales/nb.json b/WebServer/node_modules/yargs/locales/nb.json new file mode 100644 index 000000000..55be1fbed --- /dev/null +++ b/WebServer/node_modules/yargs/locales/nb.json @@ -0,0 +1,37 @@ +{ + "Commands:": "Kommandoer:", + "Options:": "Alternativer:", + "Examples:": "Eksempler:", + "boolean": "boolsk", + "count": "antall", + "string": "streng", + "number": "nummer", + "array": "matrise", + "required": "obligatorisk", + "default:": "standard:", + "choices:": "valg:", + "generated-value": "generert-verdi", + "Not enough non-option arguments: got %s, need at least %s": "Ikke nok ikke-alternativ argumenter: fikk %s, trenger minst %s", + "Too many non-option arguments: got %s, maximum of %s": "For mange ikke-alternativ argumenter: fikk %s, maksimum %s", + "Missing argument value: %s": { + "one": "Mangler argument verdi: %s", + "other": "Mangler argument verdier: %s" + }, + "Missing required argument: %s": { + "one": "Mangler obligatorisk argument: %s", + "other": "Mangler obligatoriske argumenter: %s" + }, + "Unknown argument: %s": { + "one": "Ukjent argument: %s", + "other": "Ukjente argumenter: %s" + }, + "Invalid values:": "Ugyldige verdier:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gitt: %s, Valg: %s", + "Argument check failed: %s": "Argumentsjekk mislyktes: %s", + "Implications failed:": "Konsekvensene mislyktes:", + "Not enough arguments following: %s": "Ikke nok følgende argumenter: %s", + "Invalid JSON config file: %s": "Ugyldig JSON konfigurasjonsfil: %s", + "Path to JSON config file": "Bane til JSON konfigurasjonsfil", + "Show help": "Vis hjelp", + "Show version number": "Vis versjonsnummer" +} diff --git a/WebServer/node_modules/yargs/locales/nl.json b/WebServer/node_modules/yargs/locales/nl.json new file mode 100644 index 000000000..1d144724e --- /dev/null +++ b/WebServer/node_modules/yargs/locales/nl.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Opdrachten:", + "Options:": "Opties:", + "Examples:": "Voorbeelden:", + "boolean": "boolean", + "count": "aantal", + "string": "text", + "number": "nummer", + "array": "lijst", + "required": "verplicht", + "default:": "standaard:", + "choices:": "keuzes:", + "aliases:": "aliassen:", + "generated-value": "gegenereerde waarde", + "Not enough non-option arguments: got %s, need at least %s": "Niet genoeg non-optie argumenten. Gekregen: %s, minstens nodig: %s", + "Too many non-option arguments: got %s, maximum of %s": "Te veel non-optie argumenten. Gekregen: %s, maximum: %s", + "Missing argument value: %s": { + "one": "Missing argument value: %s", + "other": "Missing argument values: %s" + }, + "Missing required argument: %s": { + "one": "Missend verplichte argument: %s", + "other": "Missende verplichte argumenten: %s" + }, + "Unknown argument: %s": { + "one": "Onbekend argument: %s", + "other": "Onbekende argumenten: %s" + }, + "Invalid values:": "Ongeldige waardes:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gegeven: %s, Keuzes: %s", + "Argument check failed: %s": "Argument check mislukt: %s", + "Implications failed:": "Implicaties mislukt:", + "Not enough arguments following: %s": "Niet genoeg argumenten na: %s", + "Invalid JSON config file: %s": "Ongeldig JSON configuratiebestand: %s", + "Path to JSON config file": "Pad naar JSON configuratiebestand", + "Show help": "Toon help", + "Show version number": "Toon versie nummer", + "Did you mean %s?": "Bedoelde u misschien %s?", + "Arguments %s and %s are mutually exclusive": "Argumenten %s en %s zijn onderling uitsluitend", + "Positionals:": "Positie-afhankelijke argumenten", + "command": "commando" +} diff --git a/WebServer/node_modules/yargs/locales/nn.json b/WebServer/node_modules/yargs/locales/nn.json new file mode 100644 index 000000000..5a3c9514d --- /dev/null +++ b/WebServer/node_modules/yargs/locales/nn.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Kommandoar:", + "Options:": "Alternativ:", + "Examples:": "Døme:", + "boolean": "boolsk", + "count": "mengd", + "string": "streng", + "number": "nummer", + "array": "matrise", + "required": "obligatorisk", + "default:": "standard:", + "choices:": "val:", + "generated-value": "generert-verdi", + "Not enough non-option arguments: got %s, need at least %s": + "Ikkje nok ikkje-alternativ argument: fekk %s, treng minst %s", + "Too many non-option arguments: got %s, maximum of %s": + "For mange ikkje-alternativ argument: fekk %s, maksimum %s", + "Missing argument value: %s": { + "one": "Manglar argumentverdi: %s", + "other": "Manglar argumentverdiar: %s" + }, + "Missing required argument: %s": { + "one": "Manglar obligatorisk argument: %s", + "other": "Manglar obligatoriske argument: %s" + }, + "Unknown argument: %s": { + "one": "Ukjent argument: %s", + "other": "Ukjende argument: %s" + }, + "Invalid values:": "Ugyldige verdiar:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Gjeve: %s, Val: %s", + "Argument check failed: %s": "Argumentsjekk mislukkast: %s", + "Implications failed:": "Konsekvensane mislukkast:", + "Not enough arguments following: %s": "Ikkje nok fylgjande argument: %s", + "Invalid JSON config file: %s": "Ugyldig JSON konfigurasjonsfil: %s", + "Path to JSON config file": "Bane til JSON konfigurasjonsfil", + "Show help": "Vis hjelp", + "Show version number": "Vis versjonsnummer" +} diff --git a/WebServer/node_modules/yargs/locales/pirate.json b/WebServer/node_modules/yargs/locales/pirate.json new file mode 100644 index 000000000..dcb5cb753 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/pirate.json @@ -0,0 +1,13 @@ +{ + "Commands:": "Choose yer command:", + "Options:": "Options for me hearties!", + "Examples:": "Ex. marks the spot:", + "required": "requi-yar-ed", + "Missing required argument: %s": { + "one": "Ye be havin' to set the followin' argument land lubber: %s", + "other": "Ye be havin' to set the followin' arguments land lubber: %s" + }, + "Show help": "Parlay this here code of conduct", + "Show version number": "'Tis the version ye be askin' fer", + "Arguments %s and %s are mutually exclusive" : "Yon scurvy dogs %s and %s be as bad as rum and a prudish wench" +} diff --git a/WebServer/node_modules/yargs/locales/pl.json b/WebServer/node_modules/yargs/locales/pl.json new file mode 100644 index 000000000..6926a4548 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/pl.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Polecenia:", + "Options:": "Opcje:", + "Examples:": "Przykłady:", + "boolean": "boolean", + "count": "ilość", + "string": "ciąg znaków", + "number": "liczba", + "array": "tablica", + "required": "wymagany", + "default:": "domyślny:", + "choices:": "dostępne:", + "aliases:": "aliasy:", + "generated-value": "wygenerowana-wartość", + "Not enough non-option arguments: got %s, need at least %s": "Niewystarczająca ilość argumentów: otrzymano %s, wymagane co najmniej %s", + "Too many non-option arguments: got %s, maximum of %s": "Zbyt duża ilość argumentów: otrzymano %s, wymagane co najwyżej %s", + "Missing argument value: %s": { + "one": "Brak wartości dla argumentu: %s", + "other": "Brak wartości dla argumentów: %s" + }, + "Missing required argument: %s": { + "one": "Brak wymaganego argumentu: %s", + "other": "Brak wymaganych argumentów: %s" + }, + "Unknown argument: %s": { + "one": "Nieznany argument: %s", + "other": "Nieznane argumenty: %s" + }, + "Invalid values:": "Nieprawidłowe wartości:", + "Argument: %s, Given: %s, Choices: %s": "Argument: %s, Otrzymano: %s, Dostępne: %s", + "Argument check failed: %s": "Weryfikacja argumentów nie powiodła się: %s", + "Implications failed:": "Założenia nie zostały spełnione:", + "Not enough arguments following: %s": "Niewystarczająca ilość argumentów następujących po: %s", + "Invalid JSON config file: %s": "Nieprawidłowy plik konfiguracyjny JSON: %s", + "Path to JSON config file": "Ścieżka do pliku konfiguracyjnego JSON", + "Show help": "Pokaż pomoc", + "Show version number": "Pokaż numer wersji", + "Did you mean %s?": "Czy chodziło Ci o %s?", + "Arguments %s and %s are mutually exclusive": "Argumenty %s i %s wzajemnie się wykluczają", + "Positionals:": "Pozycyjne:", + "command": "polecenie" +} diff --git a/WebServer/node_modules/yargs/locales/pt.json b/WebServer/node_modules/yargs/locales/pt.json new file mode 100644 index 000000000..75c3921c8 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/pt.json @@ -0,0 +1,38 @@ +{ + "Commands:": "Comandos:", + "Options:": "Opções:", + "Examples:": "Exemplos:", + "boolean": "boolean", + "count": "contagem", + "string": "cadeia de caracteres", + "number": "número", + "array": "arranjo", + "required": "requerido", + "default:": "padrão:", + "choices:": "escolhas:", + "generated-value": "valor-gerado", + "Not enough non-option arguments: got %s, need at least %s": "Argumentos insuficientes não opcionais: Argumento %s, necessário pelo menos %s", + "Too many non-option arguments: got %s, maximum of %s": "Excesso de argumentos não opcionais: recebido %s, máximo de %s", + "Missing argument value: %s": { + "one": "Falta valor de argumento: %s", + "other": "Falta valores de argumento: %s" + }, + "Missing required argument: %s": { + "one": "Falta argumento obrigatório: %s", + "other": "Faltando argumentos obrigatórios: %s" + }, + "Unknown argument: %s": { + "one": "Argumento desconhecido: %s", + "other": "Argumentos desconhecidos: %s" + }, + "Invalid values:": "Valores inválidos:", + "Argument: %s, Given: %s, Choices: %s": "Argumento: %s, Dado: %s, Escolhas: %s", + "Argument check failed: %s": "Verificação de argumento falhou: %s", + "Implications failed:": "Implicações falharam:", + "Not enough arguments following: %s": "Insuficientes argumentos a seguir: %s", + "Invalid JSON config file: %s": "Arquivo de configuração em JSON esta inválido: %s", + "Path to JSON config file": "Caminho para o arquivo de configuração em JSON", + "Show help": "Mostra ajuda", + "Show version number": "Mostra número de versão", + "Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos" +} diff --git a/WebServer/node_modules/yargs/locales/pt_BR.json b/WebServer/node_modules/yargs/locales/pt_BR.json new file mode 100644 index 000000000..904cb66eb --- /dev/null +++ b/WebServer/node_modules/yargs/locales/pt_BR.json @@ -0,0 +1,42 @@ +{ + "Commands:": "Comandos:", + "Options:": "Opções:", + "Examples:": "Exemplos:", + "boolean": "booleano", + "count": "contagem", + "string": "string", + "number": "número", + "array": "array", + "required": "obrigatório", + "default:": "padrão:", + "choices:": "opções:", + "aliases:": "sinônimos:", + "generated-value": "valor-gerado", + "Not enough non-option arguments: got %s, need at least %s": "Argumentos insuficientes: Argumento %s, necessário pelo menos %s", + "Too many non-option arguments: got %s, maximum of %s": "Excesso de argumentos: recebido %s, máximo de %s", + "Missing argument value: %s": { + "one": "Falta valor de argumento: %s", + "other": "Falta valores de argumento: %s" + }, + "Missing required argument: %s": { + "one": "Falta argumento obrigatório: %s", + "other": "Faltando argumentos obrigatórios: %s" + }, + "Unknown argument: %s": { + "one": "Argumento desconhecido: %s", + "other": "Argumentos desconhecidos: %s" + }, + "Invalid values:": "Valores inválidos:", + "Argument: %s, Given: %s, Choices: %s": "Argumento: %s, Dado: %s, Opções: %s", + "Argument check failed: %s": "Verificação de argumento falhou: %s", + "Implications failed:": "Implicações falharam:", + "Not enough arguments following: %s": "Argumentos insuficientes a seguir: %s", + "Invalid JSON config file: %s": "Arquivo JSON de configuração inválido: %s", + "Path to JSON config file": "Caminho para o arquivo JSON de configuração", + "Show help": "Exibe ajuda", + "Show version number": "Exibe a versão", + "Did you mean %s?": "Você quis dizer %s?", + "Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos", + "Positionals:": "Posicionais:", + "command": "comando" +} diff --git a/WebServer/node_modules/yargs/locales/ru.json b/WebServer/node_modules/yargs/locales/ru.json new file mode 100644 index 000000000..cb7b88b49 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/ru.json @@ -0,0 +1,39 @@ +{ + "Commands:": "Команды:", + "Options:": "Опции:", + "Examples:": "Примеры:", + "boolean": "булевый тип", + "count": "под�чет", + "string": "�троковой тип", + "number": "чи�ло", + "array": "ма��ив", + "required": "необходимо", + "default:": "по умолчанию:", + "choices:": "возможно�ти:", + "aliases:": "алиа�ы:", + "generated-value": "генерированное значение", + "Not enough non-option arguments: got %s, need at least %s": "�едо�таточно неопционных аргументов: е�ть %s, нужно как минимум %s", + "Too many non-option arguments: got %s, maximum of %s": "Слишком много неопционных аргументов: е�ть %s, мак�имум допу�тимо %s", + "Missing argument value: %s": { + "one": "�е хватает значени� аргумента: %s", + "other": "�е хватает значений аргументов: %s" + }, + "Missing required argument: %s": { + "one": "�е хватает необходимого аргумента: %s", + "other": "�е хватает необходимых аргументов: %s" + }, + "Unknown argument: %s": { + "one": "�еизве�тный аргумент: %s", + "other": "�еизве�тные аргументы: %s" + }, + "Invalid values:": "�едей�твительные значени�:", + "Argument: %s, Given: %s, Choices: %s": "�ргумент: %s, Данное значение: %s, Возможно�ти: %s", + "Argument check failed: %s": "Проверка аргументов не удала�ь: %s", + "Implications failed:": "Данный аргумент требует �ледующий дополнительный аргумент:", + "Not enough arguments following: %s": "�едо�таточно �ледующих аргументов: %s", + "Invalid JSON config file: %s": "�едей�твительный файл конфигурации JSON: %s", + "Path to JSON config file": "Путь к файлу конфигурации JSON", + "Show help": "Показать помощь", + "Show version number": "Показать номер вер�ии", + "Did you mean %s?": "Вы имели в виду %s?" +} diff --git a/WebServer/node_modules/yargs/locales/th.json b/WebServer/node_modules/yargs/locales/th.json new file mode 100644 index 000000000..3f08dcd23 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/th.json @@ -0,0 +1,39 @@ +{ + "Commands:": "คอมมาน", + "Options:": "ออฟชั่น", + "Examples:": "ตัวอย่าง", + "boolean": "บูลีน", + "count": "นับ", + "string": "สตริง", + "number": "ตัวเลข", + "array": "อาเรย์", + "required": "จำเป็น", + "default:": "ค่าเริ่มต้น", + "choices:": "ตัวเลือ�", + "aliases:": "เอเลียส", + "generated-value": "ค่าที่ถู�สร้างขึ้น", + "Not enough non-option arguments: got %s, need at least %s": "ใส่อาร์�ิวเมนต์ไม่ครบตามจำนวนที่�ำหนด: ใส่ค่ามาจำนวน %s ค่า, �ต่ต้อง�ารอย่างน้อย %s ค่า", + "Too many non-option arguments: got %s, maximum of %s": "ใส่อาร์�ิวเมนต์เ�ินจำนวนที่�ำหนด: ใส่ค่ามาจำนวน %s ค่า, �ต่ต้อง�ารมา�ที่สุด %s ค่า", + "Missing argument value: %s": { + "one": "ค่าอาร์�ิวเมนต์ที่ขาดไป: %s", + "other": "ค่าอาร์�ิวเมนต์ที่ขาดไป: %s" + }, + "Missing required argument: %s": { + "one": "อาร์�ิวเมนต์จำเป็นที่ขาดไป: %s", + "other": "อาร์�ิวเมนต์จำเป็นที่ขาดไป: %s" + }, + "Unknown argument: %s": { + "one": "อาร์�ิวเมนต์ที่ไม่รู้จั�: %s", + "other": "อาร์�ิวเมนต์ที่ไม่รู้จั�: %s" + }, + "Invalid values:": "ค่าไม่ถู�ต้อง:", + "Argument: %s, Given: %s, Choices: %s": "อาร์�ิวเมนต์: %s, ได้รับ: %s, ตัวเลือ�: %s", + "Argument check failed: %s": "ตรวจสอบพบอาร์�ิวเมนต์ที่ไม่ถู�ต้อง: %s", + "Implications failed:": "Implications ไม่สำเร็จ:", + "Not enough arguments following: %s": "ใส่อาร์�ิวเมนต์ไม่ครบ: %s", + "Invalid JSON config file: %s": "ไฟล์คอนฟิค JSON ไม่ถู�ต้อง: %s", + "Path to JSON config file": "พาทไฟล์คอนฟิค JSON", + "Show help": "ขอความช่วยเหลือ", + "Show version number": "�สดงตัวเลขเวอร์ชั่น", + "Did you mean %s?": "คุณหมายถึง %s?" +} diff --git a/WebServer/node_modules/yargs/locales/tr.json b/WebServer/node_modules/yargs/locales/tr.json new file mode 100644 index 000000000..9b06c52a8 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/tr.json @@ -0,0 +1,41 @@ +{ + "Commands:": "Komutlar:", + "Options:": "Seçenekler:", + "Examples:": "Örnekler:", + "boolean": "boolean", + "count": "sayı", + "string": "string", + "number": "numara", + "array": "array", + "required": "zorunlu", + "default:": "varsayılan:", + "choices:": "seçimler:", + "aliases:": "takma adlar:", + "generated-value": "oluşturulan-değer", + "Not enough non-option arguments: got %s, need at least %s": "Seçenek dışı argümanlar yetersiz: %s bulundu, %s gerekli", + "Too many non-option arguments: got %s, maximum of %s": "Seçenek dışı argümanlar gereğinden fazla: %s bulundu, azami %s", + "Missing argument value: %s": { + "one": "Eksik argüman değeri: %s", + "other": "Eksik argüman değerleri: %s" + }, + "Missing required argument: %s": { + "one": "Eksik zorunlu argüman: %s", + "other": "Eksik zorunlu argümanlar: %s" + }, + "Unknown argument: %s": { + "one": "Bilinmeyen argüman: %s", + "other": "Bilinmeyen argümanlar: %s" + }, + "Invalid values:": "Geçersiz değerler:", + "Argument: %s, Given: %s, Choices: %s": "Argüman: %s, Verilen: %s, Seçimler: %s", + "Argument check failed: %s": "Argüman kontrolü başarısız oldu: %s", + "Implications failed:": "Sonuçlar başarısız oldu:", + "Not enough arguments following: %s": "%s için yeterli argüman bulunamadı", + "Invalid JSON config file: %s": "Geçersiz JSON yapılandırma dosyası: %s", + "Path to JSON config file": "JSON yapılandırma dosya konumu", + "Show help": "Yardım detaylarını göster", + "Show version number": "Versiyon detaylarını göster", + "Did you mean %s?": "Bunu mu demek istediniz: %s?", + "Positionals:": "Sıralılar:", + "command": "komut" +} diff --git a/WebServer/node_modules/yargs/locales/zh_CN.json b/WebServer/node_modules/yargs/locales/zh_CN.json new file mode 100644 index 000000000..03a3d94f0 --- /dev/null +++ b/WebServer/node_modules/yargs/locales/zh_CN.json @@ -0,0 +1,41 @@ +{ + "Commands:": "命令:", + "Options:": "选项:", + "Examples:": "示例:", + "boolean": "布尔", + "count": "计数", + "string": "字符串", + "number": "数字", + "array": "数组", + "required": "必需", + "default:": "默认值:", + "choices:": "�选值:", + "generated-value": "生�的值", + "Not enough non-option arguments: got %s, need at least %s": "缺少 non-option �数:传入了 %s 个, 至少需� %s 个", + "Too many non-option arguments: got %s, maximum of %s": "non-option �数过多:传入了 %s 个, 最大�许 %s 个", + "Missing argument value: %s": { + "one": "没有给此选项指定值:%s", + "other": "没有给这些选项指定值:%s" + }, + "Missing required argument: %s": { + "one": "缺少必须的选项:%s", + "other": "缺少这些必须的选项:%s" + }, + "Unknown argument: %s": { + "one": "无法识别的选项:%s", + "other": "无法识别这些选项:%s" + }, + "Invalid values:": "无效的选项值:", + "Argument: %s, Given: %s, Choices: %s": "选项�称: %s, 传入的值: %s, �选的值:%s", + "Argument check failed: %s": "选项值验�失败:%s", + "Implications failed:": "缺少�赖的选项:", + "Not enough arguments following: %s": "没有�供足够的值给此选项:%s", + "Invalid JSON config file: %s": "无效的 JSON �置文件:%s", + "Path to JSON config file": "JSON �置文件的路径", + "Show help": "显示帮助信�", + "Show version number": "显示版本�", + "Did you mean %s?": "是指 %s?", + "Arguments %s and %s are mutually exclusive" : "选项 %s 和 %s 是互斥的", + "Positionals:": "�置:", + "command": "命令" +} diff --git a/WebServer/node_modules/yargs/locales/zh_TW.json b/WebServer/node_modules/yargs/locales/zh_TW.json new file mode 100644 index 000000000..12498888a --- /dev/null +++ b/WebServer/node_modules/yargs/locales/zh_TW.json @@ -0,0 +1,40 @@ +{ + "Commands:": "命令:", + "Options:": "�項:", + "Examples:": "例:", + "boolean": "布林", + "count": "次數", + "string": "字串", + "number": "數字", + "array": "陣列", + "required": "必須", + "default:": "�設值:", + "choices:": "��值:", + "aliases:": "別�:", + "generated-value": "生�的值", + "Not enough non-option arguments: got %s, need at least %s": "non-option 引數�足:�傳入了 %s 個, 至少� %s 個", + "Too many non-option arguments: got %s, maximum of %s": "non-option 引數�多:傳入了 %s 個, 但最多 %s 個", + "Missing argument value: %s": { + "one": "此引數無指定值:%s", + "other": "這些引數無指定值:%s" + }, + "Missing required argument: %s": { + "one": "缺少必須的引數:%s", + "other": "缺少這些必須的引數:%s" + }, + "Unknown argument: %s": { + "one": "未知的引數:%s", + "other": "未知的這些引數:%s" + }, + "Invalid values:": "無效的�項值:", + "Argument: %s, Given: %s, Choices: %s": "引數�稱: %s, 傳入的值: %s, ��的值:%s", + "Argument check failed: %s": "引數驗證失敗:%s", + "Implications failed:": "缺少�賴的�項:", + "Not enough arguments following: %s": "沒有�供足夠的值給此引數:%s", + "Invalid JSON config file: %s": "無效的 JSON 設置文件:%s", + "Path to JSON config file": "JSON 設置文件的路徑", + "Show help": "顯示說明", + "Show version number": "顯示版本", + "Did you mean %s?": "是指 %s?", + "Arguments %s and %s are mutually exclusive" : "引數 %s 和 %s 是互斥的" +} diff --git a/WebServer/node_modules/yargs/node_modules/ansi-regex/index.js b/WebServer/node_modules/yargs/node_modules/ansi-regex/index.js new file mode 100644 index 000000000..c25448009 --- /dev/null +++ b/WebServer/node_modules/yargs/node_modules/ansi-regex/index.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = options => { + options = Object.assign({ + onlyFirst: false + }, options); + + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, options.onlyFirst ? undefined : 'g'); +}; diff --git a/WebServer/node_modules/yargs/node_modules/ansi-regex/package.json b/WebServer/node_modules/yargs/node_modules/ansi-regex/package.json new file mode 100644 index 000000000..ad1d4f837 --- /dev/null +++ b/WebServer/node_modules/yargs/node_modules/ansi-regex/package.json @@ -0,0 +1,88 @@ +{ + "_args": [ + [ + "ansi-regex@4.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "ansi-regex@4.1.0", + "_id": "ansi-regex@4.1.0", + "_inBundle": false, + "_integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "_location": "/yargs/ansi-regex", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "ansi-regex@4.1.0", + "name": "ansi-regex", + "escapedName": "ansi-regex", + "rawSpec": "4.1.0", + "saveSpec": null, + "fetchSpec": "4.1.0" + }, + "_requiredBy": [ + "/yargs/strip-ansi" + ], + "_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "_spec": "4.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-regex/issues" + }, + "description": "Regular expression for matching ANSI escape codes", + "devDependencies": { + "ava": "^0.25.0", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-regex#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "license": "MIT", + "name": "ansi-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-regex.git" + }, + "scripts": { + "test": "xo && ava", + "view-supported": "node fixtures/view-codes.js" + }, + "version": "4.1.0" +} diff --git a/WebServer/node_modules/yargs/node_modules/string-width/index.js b/WebServer/node_modules/yargs/node_modules/string-width/index.js new file mode 100644 index 000000000..33c9d6c06 --- /dev/null +++ b/WebServer/node_modules/yargs/node_modules/string-width/index.js @@ -0,0 +1,39 @@ +'use strict'; +const stripAnsi = require('strip-ansi'); +const isFullwidthCodePoint = require('is-fullwidth-code-point'); +const emojiRegex = require('emoji-regex')(); + +module.exports = input => { + input = input.replace(emojiRegex, ' '); + + if (typeof input !== 'string' || input.length === 0) { + return 0; + } + + input = stripAnsi(input); + + let width = 0; + + for (let i = 0; i < input.length; i++) { + const code = input.codePointAt(i); + + // Ignore control characters + if (code <= 0x1F || (code >= 0x7F && code <= 0x9F)) { + continue; + } + + // Ignore combining characters + if (code >= 0x300 && code <= 0x36F) { + continue; + } + + // Surrogates + if (code > 0xFFFF) { + i++; + } + + width += isFullwidthCodePoint(code) ? 2 : 1; + } + + return width; +}; diff --git a/WebServer/node_modules/yargs/node_modules/string-width/package.json b/WebServer/node_modules/yargs/node_modules/string-width/package.json new file mode 100644 index 000000000..4f9284698 --- /dev/null +++ b/WebServer/node_modules/yargs/node_modules/string-width/package.json @@ -0,0 +1,91 @@ +{ + "_args": [ + [ + "string-width@3.1.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "string-width@3.1.0", + "_id": "string-width@3.1.0", + "_inBundle": false, + "_integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "_location": "/yargs/string-width", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "string-width@3.1.0", + "name": "string-width", + "escapedName": "string-width", + "rawSpec": "3.1.0", + "saveSpec": null, + "fetchSpec": "3.1.0" + }, + "_requiredBy": [ + "/yargs" + ], + "_resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "_spec": "3.1.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/string-width/issues" + }, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "description": "Get the visual width of a string - the number of columns required to display it", + "devDependencies": { + "ava": "^1.0.1", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/string-width#readme", + "keywords": [ + "string", + "str", + "character", + "char", + "unicode", + "width", + "visual", + "column", + "columns", + "fullwidth", + "full-width", + "full", + "ansi", + "escape", + "codes", + "cli", + "command-line", + "terminal", + "console", + "cjk", + "chinese", + "japanese", + "korean", + "fixed-width" + ], + "license": "MIT", + "name": "string-width", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/string-width.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "3.1.0" +} diff --git a/WebServer/node_modules/yargs/node_modules/strip-ansi/index.js b/WebServer/node_modules/yargs/node_modules/strip-ansi/index.js new file mode 100644 index 000000000..9788c96df --- /dev/null +++ b/WebServer/node_modules/yargs/node_modules/strip-ansi/index.js @@ -0,0 +1,7 @@ +'use strict'; +const ansiRegex = require('ansi-regex'); + +const stripAnsi = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; + +module.exports = stripAnsi; +module.exports.default = stripAnsi; diff --git a/WebServer/node_modules/yargs/node_modules/strip-ansi/package.json b/WebServer/node_modules/yargs/node_modules/strip-ansi/package.json new file mode 100644 index 000000000..73570f4b5 --- /dev/null +++ b/WebServer/node_modules/yargs/node_modules/strip-ansi/package.json @@ -0,0 +1,89 @@ +{ + "_args": [ + [ + "strip-ansi@5.2.0", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "strip-ansi@5.2.0", + "_id": "strip-ansi@5.2.0", + "_inBundle": false, + "_integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "_location": "/yargs/strip-ansi", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "strip-ansi@5.2.0", + "name": "strip-ansi", + "escapedName": "strip-ansi", + "rawSpec": "5.2.0", + "saveSpec": null, + "fetchSpec": "5.2.0" + }, + "_requiredBy": [ + "/yargs/string-width" + ], + "_resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "_spec": "5.2.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "description": "Strip ANSI escape codes from a string", + "devDependencies": { + "ava": "^1.3.1", + "tsd-check": "^0.5.0", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/chalk/strip-ansi#readme", + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "strip-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "scripts": { + "test": "xo && ava && tsd-check" + }, + "version": "5.2.0" +} diff --git a/WebServer/node_modules/yargs/package.json b/WebServer/node_modules/yargs/package.json new file mode 100644 index 000000000..553b698ff --- /dev/null +++ b/WebServer/node_modules/yargs/package.json @@ -0,0 +1,113 @@ +{ + "_args": [ + [ + "yargs@13.2.2", + "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer" + ] + ], + "_from": "yargs@13.2.2", + "_id": "yargs@13.2.2", + "_inBundle": false, + "_integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "_location": "/yargs", + "_phantomChildren": { + "emoji-regex": "7.0.3", + "is-fullwidth-code-point": "2.0.0" + }, + "_requested": { + "type": "version", + "registry": true, + "raw": "yargs@13.2.2", + "name": "yargs", + "escapedName": "yargs", + "rawSpec": "13.2.2", + "saveSpec": null, + "fetchSpec": "13.2.2" + }, + "_requiredBy": [ + "/mocha" + ], + "_resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "_spec": "13.2.2", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer", + "bugs": { + "url": "https://github.com/yargs/yargs/issues" + }, + "contributors": [ + { + "name": "Yargs Contributors", + "url": "https://github.com/yargs/yargs/graphs/contributors" + } + ], + "dependencies": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" + }, + "description": "yargs the modern, pirate-themed, successor to optimist.", + "devDependencies": { + "chai": "^4.2.0", + "chalk": "^2.4.2", + "coveralls": "^3.0.2", + "cpr": "^3.0.1", + "cross-spawn": "^6.0.4", + "es6-promise": "^4.2.5", + "hashish": "0.0.4", + "mocha": "^5.2.0", + "nyc": "^13.2.0", + "rimraf": "^2.6.3", + "standard": "^12.0.1", + "standard-version": "^5.0.0", + "which": "^1.3.1", + "yargs-test-extends": "^1.0.1" + }, + "engine": { + "node": ">=6" + }, + "files": [ + "index.js", + "yargs.js", + "lib", + "locales", + "completion.sh.hbs", + "completion.zsh.hbs", + "LICENSE" + ], + "homepage": "https://yargs.js.org/", + "keywords": [ + "argument", + "args", + "option", + "parser", + "parsing", + "cli", + "command" + ], + "license": "MIT", + "main": "./index.js", + "name": "yargs", + "repository": { + "type": "git", + "url": "git+https://github.com/yargs/yargs.git" + }, + "scripts": { + "coverage": "nyc report --reporter=text-lcov | coveralls", + "pretest": "standard", + "release": "standard-version", + "test": "nyc --cache mocha --require ./test/before.js --timeout=12000 --check-leaks" + }, + "standard": { + "ignore": [ + "**/example/**" + ] + }, + "version": "13.2.2" +} diff --git a/WebServer/node_modules/yargs/yargs.js b/WebServer/node_modules/yargs/yargs.js new file mode 100644 index 000000000..ea1f15fae --- /dev/null +++ b/WebServer/node_modules/yargs/yargs.js @@ -0,0 +1,1205 @@ +'use strict' +const argsert = require('./lib/argsert') +const fs = require('fs') +const Command = require('./lib/command') +const Completion = require('./lib/completion') +const Parser = require('yargs-parser') +const path = require('path') +const Usage = require('./lib/usage') +const Validation = require('./lib/validation') +const Y18n = require('y18n') +const objFilter = require('./lib/obj-filter') +const setBlocking = require('set-blocking') +const applyExtends = require('./lib/apply-extends') +const { globalMiddlewareFactory } = require('./lib/middleware') +const YError = require('./lib/yerror') + +exports = module.exports = Yargs +function Yargs (processArgs, cwd, parentRequire) { + processArgs = processArgs || [] // handle calling yargs(). + + const self = {} + let command = null + let completion = null + let groups = {} + let globalMiddleware = [] + let output = '' + let preservedGroups = {} + let usage = null + let validation = null + + const y18n = Y18n({ + directory: path.resolve(__dirname, './locales'), + updateFiles: false + }) + + self.middleware = globalMiddlewareFactory(globalMiddleware, self) + + if (!cwd) cwd = process.cwd() + + self.scriptName = function scriptName (scriptName) { + self.$0 = scriptName + return self + } + + // ignore the node bin, specify this in your + // bin file with #!/usr/bin/env node + if (/\b(node|iojs|electron)(\.exe)?$/.test(process.argv[0])) { + self.$0 = process.argv.slice(1, 2) + } else { + self.$0 = process.argv.slice(0, 1) + } + + self.$0 = self.$0 + .map((x, i) => { + const b = rebase(cwd, x) + return x.match(/^(\/|([a-zA-Z]:)?\\)/) && b.length < x.length ? b : x + }) + .join(' ').trim() + + if (process.env._ !== undefined && process.argv[1] === process.env._) { + self.$0 = process.env._.replace( + `${path.dirname(process.execPath)}/`, '' + ) + } + + // use context object to keep track of resets, subcommand execution, etc + // submodules should modify and check the state of context as necessary + const context = { resets: -1, commands: [], fullCommands: [], files: [] } + self.getContext = () => context + + // puts yargs back into an initial state. any keys + // that have been set to "global" will not be reset + // by this action. + let options + self.resetOptions = self.reset = function resetOptions (aliases) { + context.resets++ + aliases = aliases || {} + options = options || {} + // put yargs back into an initial state, this + // logic is used to build a nested command + // hierarchy. + const tmpOptions = {} + tmpOptions.local = options.local ? options.local : [] + tmpOptions.configObjects = options.configObjects ? options.configObjects : [] + + // if a key has been explicitly set as local, + // we should reset it before passing options to command. + const localLookup = {} + tmpOptions.local.forEach((l) => { + localLookup[l] = true + ;(aliases[l] || []).forEach((a) => { + localLookup[a] = true + }) + }) + + // preserve all groups not set to local. + preservedGroups = Object.keys(groups).reduce((acc, groupName) => { + const keys = groups[groupName].filter(key => !(key in localLookup)) + if (keys.length > 0) { + acc[groupName] = keys + } + return acc + }, {}) + // groups can now be reset + groups = {} + + const arrayOptions = [ + 'array', 'boolean', 'string', 'skipValidation', + 'count', 'normalize', 'number', + 'hiddenOptions' + ] + + const objectOptions = [ + 'narg', 'key', 'alias', 'default', 'defaultDescription', + 'config', 'choices', 'demandedOptions', 'demandedCommands', 'coerce' + ] + + arrayOptions.forEach((k) => { + tmpOptions[k] = (options[k] || []).filter(k => !localLookup[k]) + }) + + objectOptions.forEach((k) => { + tmpOptions[k] = objFilter(options[k], (k, v) => !localLookup[k]) + }) + + tmpOptions.envPrefix = options.envPrefix + options = tmpOptions + + // if this is the first time being executed, create + // instances of all our helpers -- otherwise just reset. + usage = usage ? usage.reset(localLookup) : Usage(self, y18n) + validation = validation ? validation.reset(localLookup) : Validation(self, usage, y18n) + command = command ? command.reset() : Command(self, usage, validation, globalMiddleware) + if (!completion) completion = Completion(self, usage, command) + + completionCommand = null + output = '' + exitError = null + hasOutput = false + self.parsed = false + + return self + } + self.resetOptions() + + // temporary hack: allow "freezing" of reset-able state for parse(msg, cb) + let frozen + function freeze () { + frozen = {} + frozen.options = options + frozen.configObjects = options.configObjects.slice(0) + frozen.exitProcess = exitProcess + frozen.groups = groups + usage.freeze() + validation.freeze() + command.freeze() + frozen.strict = strict + frozen.completionCommand = completionCommand + frozen.output = output + frozen.exitError = exitError + frozen.hasOutput = hasOutput + frozen.parsed = self.parsed + } + function unfreeze () { + options = frozen.options + options.configObjects = frozen.configObjects + exitProcess = frozen.exitProcess + groups = frozen.groups + output = frozen.output + exitError = frozen.exitError + hasOutput = frozen.hasOutput + self.parsed = frozen.parsed + usage.unfreeze() + validation.unfreeze() + command.unfreeze() + strict = frozen.strict + completionCommand = frozen.completionCommand + parseFn = null + parseContext = null + frozen = undefined + } + + self.boolean = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('boolean', keys) + return self + } + + self.array = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('array', keys) + return self + } + + self.number = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('number', keys) + return self + } + + self.normalize = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('normalize', keys) + return self + } + + self.count = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('count', keys) + return self + } + + self.string = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('string', keys) + return self + } + + self.requiresArg = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintObject(self.nargs, false, 'narg', keys, 1) + return self + } + + self.skipValidation = function (keys) { + argsert('', [keys], arguments.length) + populateParserHintArray('skipValidation', keys) + return self + } + + function populateParserHintArray (type, keys, value) { + keys = [].concat(keys) + keys.forEach((key) => { + options[type].push(key) + }) + } + + self.nargs = function (key, value) { + argsert(' [number]', [key, value], arguments.length) + populateParserHintObject(self.nargs, false, 'narg', key, value) + return self + } + + self.choices = function (key, value) { + argsert(' [string|array]', [key, value], arguments.length) + populateParserHintObject(self.choices, true, 'choices', key, value) + return self + } + + self.alias = function (key, value) { + argsert(' [string|array]', [key, value], arguments.length) + populateParserHintObject(self.alias, true, 'alias', key, value) + return self + } + + // TODO: actually deprecate self.defaults. + self.default = self.defaults = function (key, value, defaultDescription) { + argsert(' [*] [string]', [key, value, defaultDescription], arguments.length) + if (defaultDescription) options.defaultDescription[key] = defaultDescription + if (typeof value === 'function') { + if (!options.defaultDescription[key]) options.defaultDescription[key] = usage.functionDescription(value) + value = value.call() + } + populateParserHintObject(self.default, false, 'default', key, value) + return self + } + + self.describe = function (key, desc) { + argsert(' [string]', [key, desc], arguments.length) + populateParserHintObject(self.describe, false, 'key', key, true) + usage.describe(key, desc) + return self + } + + self.demandOption = function (keys, msg) { + argsert(' [string]', [keys, msg], arguments.length) + populateParserHintObject(self.demandOption, false, 'demandedOptions', keys, msg) + return self + } + + self.coerce = function (keys, value) { + argsert(' [function]', [keys, value], arguments.length) + populateParserHintObject(self.coerce, false, 'coerce', keys, value) + return self + } + + function populateParserHintObject (builder, isArray, type, key, value) { + if (Array.isArray(key)) { + // an array of keys with one value ['x', 'y', 'z'], function parse () {} + const temp = {} + key.forEach((k) => { + temp[k] = value + }) + builder(temp) + } else if (typeof key === 'object') { + // an object of key value pairs: {'x': parse () {}, 'y': parse() {}} + Object.keys(key).forEach((k) => { + builder(k, key[k]) + }) + } else { + // a single key value pair 'x', parse() {} + if (isArray) { + options[type][key] = (options[type][key] || []).concat(value) + } else { + options[type][key] = value + } + } + } + + function deleteFromParserHintObject (optionKey) { + // delete from all parsing hints: + // boolean, array, key, alias, etc. + Object.keys(options).forEach((hintKey) => { + const hint = options[hintKey] + if (Array.isArray(hint)) { + if (~hint.indexOf(optionKey)) hint.splice(hint.indexOf(optionKey), 1) + } else if (typeof hint === 'object') { + delete hint[optionKey] + } + }) + // now delete the description from usage.js. + delete usage.getDescriptions()[optionKey] + } + + self.config = function config (key, msg, parseFn) { + argsert('[object|string] [string|function] [function]', [key, msg, parseFn], arguments.length) + // allow a config object to be provided directly. + if (typeof key === 'object') { + key = applyExtends(key, cwd) + options.configObjects = (options.configObjects || []).concat(key) + return self + } + + // allow for a custom parsing function. + if (typeof msg === 'function') { + parseFn = msg + msg = null + } + + key = key || 'config' + self.describe(key, msg || usage.deferY18nLookup('Path to JSON config file')) + ;(Array.isArray(key) ? key : [key]).forEach((k) => { + options.config[k] = parseFn || true + }) + + return self + } + + self.example = function (cmd, description) { + argsert(' [string]', [cmd, description], arguments.length) + usage.example(cmd, description) + return self + } + + self.command = function (cmd, description, builder, handler, middlewares) { + argsert(' [string|boolean] [function|object] [function] [array]', [cmd, description, builder, handler, middlewares], arguments.length) + command.addHandler(cmd, description, builder, handler, middlewares) + return self + } + + self.commandDir = function (dir, opts) { + argsert(' [object]', [dir, opts], arguments.length) + const req = parentRequire || require + command.addDirectory(dir, self.getContext(), req, require('get-caller-file')(), opts) + return self + } + + // TODO: deprecate self.demand in favor of + // .demandCommand() .demandOption(). + self.demand = self.required = self.require = function demand (keys, max, msg) { + // you can optionally provide a 'max' key, + // which will raise an exception if too many '_' + // options are provided. + if (Array.isArray(max)) { + max.forEach((key) => { + self.demandOption(key, msg) + }) + max = Infinity + } else if (typeof max !== 'number') { + msg = max + max = Infinity + } + + if (typeof keys === 'number') { + self.demandCommand(keys, max, msg, msg) + } else if (Array.isArray(keys)) { + keys.forEach((key) => { + self.demandOption(key, msg) + }) + } else { + if (typeof msg === 'string') { + self.demandOption(keys, msg) + } else if (msg === true || typeof msg === 'undefined') { + self.demandOption(keys) + } + } + + return self + } + + self.demandCommand = function demandCommand (min, max, minMsg, maxMsg) { + argsert('[number] [number|string] [string|null|undefined] [string|null|undefined]', [min, max, minMsg, maxMsg], arguments.length) + + if (typeof min === 'undefined') min = 1 + + if (typeof max !== 'number') { + minMsg = max + max = Infinity + } + + self.global('_', false) + + options.demandedCommands._ = { + min, + max, + minMsg, + maxMsg + } + + return self + } + + self.getDemandedOptions = () => { + argsert([], 0) + return options.demandedOptions + } + + self.getDemandedCommands = () => { + argsert([], 0) + return options.demandedCommands + } + + self.implies = function (key, value) { + argsert(' [number|string|array]', [key, value], arguments.length) + validation.implies(key, value) + return self + } + + self.conflicts = function (key1, key2) { + argsert(' [string|array]', [key1, key2], arguments.length) + validation.conflicts(key1, key2) + return self + } + + self.usage = function (msg, description, builder, handler) { + argsert(' [string|boolean] [function|object] [function]', [msg, description, builder, handler], arguments.length) + + if (description !== undefined) { + // .usage() can be used as an alias for defining + // a default command. + if ((msg || '').match(/^\$0( |$)/)) { + return self.command(msg, description, builder, handler) + } else { + throw new YError('.usage() description must start with $0 if being used as alias for .command()') + } + } else { + usage.usage(msg) + return self + } + } + + self.epilogue = self.epilog = function (msg) { + argsert('', [msg], arguments.length) + usage.epilog(msg) + return self + } + + self.fail = function (f) { + argsert('', [f], arguments.length) + usage.failFn(f) + return self + } + + self.check = function (f, _global) { + argsert(' [boolean]', [f, _global], arguments.length) + validation.check(f, _global !== false) + return self + } + + self.global = function global (globals, global) { + argsert(' [boolean]', [globals, global], arguments.length) + globals = [].concat(globals) + if (global !== false) { + options.local = options.local.filter(l => globals.indexOf(l) === -1) + } else { + globals.forEach((g) => { + if (options.local.indexOf(g) === -1) options.local.push(g) + }) + } + return self + } + + self.pkgConf = function pkgConf (key, rootPath) { + argsert(' [string]', [key, rootPath], arguments.length) + let conf = null + // prefer cwd to require-main-filename in this method + // since we're looking for e.g. "nyc" config in nyc consumer + // rather than "yargs" config in nyc (where nyc is the main filename) + const obj = pkgUp(rootPath || cwd) + + // If an object exists in the key, add it to options.configObjects + if (obj[key] && typeof obj[key] === 'object') { + conf = applyExtends(obj[key], rootPath || cwd) + options.configObjects = (options.configObjects || []).concat(conf) + } + + return self + } + + const pkgs = {} + function pkgUp (rootPath) { + const npath = rootPath || '*' + if (pkgs[npath]) return pkgs[npath] + const findUp = require('find-up') + + let obj = {} + try { + let startDir = rootPath || require('require-main-filename')(parentRequire || require) + + // When called in an environment that lacks require.main.filename, such as a jest test runner, + // startDir is already process.cwd(), and should not be shortened. + // Whether or not it is _actually_ a directory (e.g., extensionless bin) is irrelevant, find-up handles it. + if (!rootPath && path.extname(startDir)) { + startDir = path.dirname(startDir) + } + + const pkgJsonPath = findUp.sync('package.json', { + cwd: startDir + }) + obj = JSON.parse(fs.readFileSync(pkgJsonPath)) + } catch (noop) {} + + pkgs[npath] = obj || {} + return pkgs[npath] + } + + let parseFn = null + let parseContext = null + self.parse = function parse (args, shortCircuit, _parseFn) { + argsert('[string|array] [function|boolean|object] [function]', [args, shortCircuit, _parseFn], arguments.length) + if (typeof args === 'undefined') { + return self._parseArgs(processArgs) + } + + // a context object can optionally be provided, this allows + // additional information to be passed to a command handler. + if (typeof shortCircuit === 'object') { + parseContext = shortCircuit + shortCircuit = _parseFn + } + + // by providing a function as a second argument to + // parse you can capture output that would otherwise + // default to printing to stdout/stderr. + if (typeof shortCircuit === 'function') { + parseFn = shortCircuit + shortCircuit = null + } + // completion short-circuits the parsing process, + // skipping validation, etc. + if (!shortCircuit) processArgs = args + + freeze() + if (parseFn) exitProcess = false + + const parsed = self._parseArgs(args, shortCircuit) + if (parseFn) parseFn(exitError, parsed, output) + unfreeze() + + return parsed + } + + self._getParseContext = () => parseContext || {} + + self._hasParseCallback = () => !!parseFn + + self.option = self.options = function option (key, opt) { + argsert(' [object]', [key, opt], arguments.length) + if (typeof key === 'object') { + Object.keys(key).forEach((k) => { + self.options(k, key[k]) + }) + } else { + if (typeof opt !== 'object') { + opt = {} + } + + options.key[key] = true // track manually set keys. + + if (opt.alias) self.alias(key, opt.alias) + + const demand = opt.demand || opt.required || opt.require + + // deprecated, use 'demandOption' instead + if (demand) { + self.demand(key, demand) + } + + if (opt.demandOption) { + self.demandOption(key, typeof opt.demandOption === 'string' ? opt.demandOption : undefined) + } + + if ('conflicts' in opt) { + self.conflicts(key, opt.conflicts) + } + + if ('default' in opt) { + self.default(key, opt.default) + } + + if ('implies' in opt) { + self.implies(key, opt.implies) + } + + if ('nargs' in opt) { + self.nargs(key, opt.nargs) + } + + if (opt.config) { + self.config(key, opt.configParser) + } + + if (opt.normalize) { + self.normalize(key) + } + + if ('choices' in opt) { + self.choices(key, opt.choices) + } + + if ('coerce' in opt) { + self.coerce(key, opt.coerce) + } + + if ('group' in opt) { + self.group(key, opt.group) + } + + if (opt.boolean || opt.type === 'boolean') { + self.boolean(key) + if (opt.alias) self.boolean(opt.alias) + } + + if (opt.array || opt.type === 'array') { + self.array(key) + if (opt.alias) self.array(opt.alias) + } + + if (opt.number || opt.type === 'number') { + self.number(key) + if (opt.alias) self.number(opt.alias) + } + + if (opt.string || opt.type === 'string') { + self.string(key) + if (opt.alias) self.string(opt.alias) + } + + if (opt.count || opt.type === 'count') { + self.count(key) + } + + if (typeof opt.global === 'boolean') { + self.global(key, opt.global) + } + + if (opt.defaultDescription) { + options.defaultDescription[key] = opt.defaultDescription + } + + if (opt.skipValidation) { + self.skipValidation(key) + } + + const desc = opt.describe || opt.description || opt.desc + self.describe(key, desc) + if (opt.hidden) { + self.hide(key) + } + + if (opt.requiresArg) { + self.requiresArg(key) + } + } + + return self + } + self.getOptions = () => options + + self.positional = function (key, opts) { + argsert(' ', [key, opts], arguments.length) + if (context.resets === 0) { + throw new YError(".positional() can only be called in a command's builder function") + } + + // .positional() only supports a subset of the configuration + // options availble to .option(). + const supportedOpts = ['default', 'implies', 'normalize', + 'choices', 'conflicts', 'coerce', 'type', 'describe', + 'desc', 'description', 'alias'] + opts = objFilter(opts, (k, v) => { + let accept = supportedOpts.indexOf(k) !== -1 + // type can be one of string|number|boolean. + if (k === 'type' && ['string', 'number', 'boolean'].indexOf(v) === -1) accept = false + return accept + }) + + // copy over any settings that can be inferred from the command string. + const fullCommand = context.fullCommands[context.fullCommands.length - 1] + const parseOptions = fullCommand ? command.cmdToParseOptions(fullCommand) : { + array: [], + alias: {}, + default: {}, + demand: {} + } + Object.keys(parseOptions).forEach((pk) => { + if (Array.isArray(parseOptions[pk])) { + if (parseOptions[pk].indexOf(key) !== -1) opts[pk] = true + } else { + if (parseOptions[pk][key] && !(pk in opts)) opts[pk] = parseOptions[pk][key] + } + }) + self.group(key, usage.getPositionalGroupName()) + return self.option(key, opts) + } + + self.group = function group (opts, groupName) { + argsert(' ', [opts, groupName], arguments.length) + const existing = preservedGroups[groupName] || groups[groupName] + if (preservedGroups[groupName]) { + // we now only need to track this group name in groups. + delete preservedGroups[groupName] + } + + const seen = {} + groups[groupName] = (existing || []).concat(opts).filter((key) => { + if (seen[key]) return false + return (seen[key] = true) + }) + return self + } + // combine explicit and preserved groups. explicit groups should be first + self.getGroups = () => Object.assign({}, groups, preservedGroups) + + // as long as options.envPrefix is not undefined, + // parser will apply env vars matching prefix to argv + self.env = function (prefix) { + argsert('[string|boolean]', [prefix], arguments.length) + if (prefix === false) options.envPrefix = undefined + else options.envPrefix = prefix || '' + return self + } + + self.wrap = function (cols) { + argsert('', [cols], arguments.length) + usage.wrap(cols) + return self + } + + let strict = false + self.strict = function (enabled) { + argsert('[boolean]', [enabled], arguments.length) + strict = enabled !== false + return self + } + self.getStrict = () => strict + + let parserConfig = {} + self.parserConfiguration = function parserConfiguration (config) { + argsert('', [config], arguments.length) + parserConfig = config + return self + } + self.getParserConfiguration = () => parserConfig + + self.showHelp = function (level) { + argsert('[string|function]', [level], arguments.length) + if (!self.parsed) self._parseArgs(processArgs) // run parser, if it has not already been executed. + if (command.hasDefaultCommand()) { + context.resets++ // override the restriction on top-level positoinals. + command.runDefaultBuilderOn(self, true) + } + usage.showHelp(level) + return self + } + + let versionOpt = null + self.version = function version (opt, msg, ver) { + const defaultVersionOpt = 'version' + argsert('[boolean|string] [string] [string]', [opt, msg, ver], arguments.length) + + // nuke the key previously configured + // to return version #. + if (versionOpt) { + deleteFromParserHintObject(versionOpt) + usage.version(undefined) + versionOpt = null + } + + if (arguments.length === 0) { + ver = guessVersion() + opt = defaultVersionOpt + } else if (arguments.length === 1) { + if (opt === false) { // disable default 'version' key. + return self + } + ver = opt + opt = defaultVersionOpt + } else if (arguments.length === 2) { + ver = msg + msg = null + } + + versionOpt = typeof opt === 'string' ? opt : defaultVersionOpt + msg = msg || usage.deferY18nLookup('Show version number') + + usage.version(ver || undefined) + self.boolean(versionOpt) + self.describe(versionOpt, msg) + return self + } + + function guessVersion () { + const obj = pkgUp() + + return obj.version || 'unknown' + } + + let helpOpt = null + self.addHelpOpt = self.help = function addHelpOpt (opt, msg) { + const defaultHelpOpt = 'help' + argsert('[string|boolean] [string]', [opt, msg], arguments.length) + + // nuke the key previously configured + // to return help. + if (helpOpt) { + deleteFromParserHintObject(helpOpt) + helpOpt = null + } + + if (arguments.length === 1) { + if (opt === false) return self + } + + // use arguments, fallback to defaults for opt and msg + helpOpt = typeof opt === 'string' ? opt : defaultHelpOpt + self.boolean(helpOpt) + self.describe(helpOpt, msg || usage.deferY18nLookup('Show help')) + return self + } + + const defaultShowHiddenOpt = 'show-hidden' + options.showHiddenOpt = defaultShowHiddenOpt + self.addShowHiddenOpt = self.showHidden = function addShowHiddenOpt (opt, msg) { + argsert('[string|boolean] [string]', [opt, msg], arguments.length) + + if (arguments.length === 1) { + if (opt === false) return self + } + + const showHiddenOpt = typeof opt === 'string' ? opt : defaultShowHiddenOpt + self.boolean(showHiddenOpt) + self.describe(showHiddenOpt, msg || usage.deferY18nLookup('Show hidden options')) + options.showHiddenOpt = showHiddenOpt + return self + } + + self.hide = function hide (key) { + argsert('', [key], arguments.length) + options.hiddenOptions.push(key) + return self + } + + self.showHelpOnFail = function showHelpOnFail (enabled, message) { + argsert('[boolean|string] [string]', [enabled, message], arguments.length) + usage.showHelpOnFail(enabled, message) + return self + } + + var exitProcess = true + self.exitProcess = function (enabled) { + argsert('[boolean]', [enabled], arguments.length) + if (typeof enabled !== 'boolean') { + enabled = true + } + exitProcess = enabled + return self + } + self.getExitProcess = () => exitProcess + + var completionCommand = null + self.completion = function (cmd, desc, fn) { + argsert('[string] [string|boolean|function] [function]', [cmd, desc, fn], arguments.length) + + // a function to execute when generating + // completions can be provided as the second + // or third argument to completion. + if (typeof desc === 'function') { + fn = desc + desc = null + } + + // register the completion command. + completionCommand = cmd || 'completion' + if (!desc && desc !== false) { + desc = 'generate completion script' + } + self.command(completionCommand, desc) + + // a function can be provided + if (fn) completion.registerFunction(fn) + + return self + } + + self.showCompletionScript = function ($0) { + argsert('[string]', [$0], arguments.length) + $0 = $0 || self.$0 + _logger.log(completion.generateCompletionScript($0, completionCommand)) + return self + } + + self.getCompletion = function (args, done) { + argsert(' ', [args, done], arguments.length) + completion.getCompletion(args, done) + } + + self.locale = function (locale) { + argsert('[string]', [locale], arguments.length) + if (arguments.length === 0) { + guessLocale() + return y18n.getLocale() + } + detectLocale = false + y18n.setLocale(locale) + return self + } + + self.updateStrings = self.updateLocale = function (obj) { + argsert('', [obj], arguments.length) + detectLocale = false + y18n.updateLocale(obj) + return self + } + + let detectLocale = true + self.detectLocale = function (detect) { + argsert('', [detect], arguments.length) + detectLocale = detect + return self + } + self.getDetectLocale = () => detectLocale + + var hasOutput = false + var exitError = null + // maybe exit, always capture + // context about why we wanted to exit. + self.exit = (code, err) => { + hasOutput = true + exitError = err + if (exitProcess) process.exit(code) + } + + // we use a custom logger that buffers output, + // so that we can print to non-CLIs, e.g., chat-bots. + const _logger = { + log () { + const args = [] + for (let i = 0; i < arguments.length; i++) args.push(arguments[i]) + if (!self._hasParseCallback()) console.log.apply(console, args) + hasOutput = true + if (output.length) output += '\n' + output += args.join(' ') + }, + error () { + const args = [] + for (let i = 0; i < arguments.length; i++) args.push(arguments[i]) + if (!self._hasParseCallback()) console.error.apply(console, args) + hasOutput = true + if (output.length) output += '\n' + output += args.join(' ') + } + } + self._getLoggerInstance = () => _logger + // has yargs output an error our help + // message in the current execution context. + self._hasOutput = () => hasOutput + + self._setHasOutput = () => { + hasOutput = true + } + + let recommendCommands + self.recommendCommands = function (recommend) { + argsert('[boolean]', [recommend], arguments.length) + recommendCommands = typeof recommend === 'boolean' ? recommend : true + return self + } + + self.getUsageInstance = () => usage + + self.getValidationInstance = () => validation + + self.getCommandInstance = () => command + + self.terminalWidth = () => { + argsert([], 0) + return typeof process.stdout.columns !== 'undefined' ? process.stdout.columns : null + } + + Object.defineProperty(self, 'argv', { + get: () => self._parseArgs(processArgs), + enumerable: true + }) + + self._parseArgs = function parseArgs (args, shortCircuit, _skipValidation, commandIndex) { + let skipValidation = !!_skipValidation + args = args || processArgs + + options.__ = y18n.__ + options.configuration = self.getParserConfiguration() + + // Deprecated + let pkgConfig = pkgUp()['yargs'] + if (pkgConfig) { + console.warn('Configuring yargs through package.json is deprecated and will be removed in the next major release, please use the JS API instead.') + options.configuration = Object.assign({}, pkgConfig, options.configuration) + } + + const parsed = Parser.detailed(args, options) + let argv = parsed.argv + if (parseContext) argv = Object.assign({}, argv, parseContext) + const aliases = parsed.aliases + + argv.$0 = self.$0 + self.parsed = parsed + + try { + guessLocale() // guess locale lazily, so that it can be turned off in chain. + + // while building up the argv object, there + // are two passes through the parser. If completion + // is being performed short-circuit on the first pass. + if (shortCircuit) { + return argv + } + + // if there's a handler associated with a + // command defer processing to it. + if (helpOpt) { + // consider any multi-char helpOpt alias as a valid help command + // unless all helpOpt aliases are single-char + // note that parsed.aliases is a normalized bidirectional map :) + const helpCmds = [helpOpt] + .concat(aliases[helpOpt] || []) + .filter(k => k.length > 1) + // check if help should trigger and strip it from _. + if (~helpCmds.indexOf(argv._[argv._.length - 1])) { + argv._.pop() + argv[helpOpt] = true + } + } + + const handlerKeys = command.getCommands() + const requestCompletions = completion.completionKey in argv + const skipRecommendation = argv[helpOpt] || requestCompletions + const skipDefaultCommand = skipRecommendation && (handlerKeys.length > 1 || handlerKeys[0] !== '$0') + + if (argv._.length) { + if (handlerKeys.length) { + let firstUnknownCommand + for (let i = (commandIndex || 0), cmd; argv._[i] !== undefined; i++) { + cmd = String(argv._[i]) + if (~handlerKeys.indexOf(cmd) && cmd !== completionCommand) { + // commands are executed using a recursive algorithm that executes + // the deepest command first; we keep track of the position in the + // argv._ array that is currently being executed. + return command.runCommand(cmd, self, parsed, i + 1) + } else if (!firstUnknownCommand && cmd !== completionCommand) { + firstUnknownCommand = cmd + break + } + } + + // run the default command, if defined + if (command.hasDefaultCommand() && !skipDefaultCommand) { + return command.runCommand(null, self, parsed) + } + + // recommend a command if recommendCommands() has + // been enabled, and no commands were found to execute + if (recommendCommands && firstUnknownCommand && !skipRecommendation) { + validation.recommendCommands(firstUnknownCommand, handlerKeys) + } + } + + // generate a completion script for adding to ~/.bashrc. + if (completionCommand && ~argv._.indexOf(completionCommand) && !requestCompletions) { + if (exitProcess) setBlocking(true) + self.showCompletionScript() + self.exit(0) + } + } else if (command.hasDefaultCommand() && !skipDefaultCommand) { + return command.runCommand(null, self, parsed) + } + + // we must run completions first, a user might + // want to complete the --help or --version option. + if (requestCompletions) { + if (exitProcess) setBlocking(true) + + // we allow for asynchronous completions, + // e.g., loading in a list of commands from an API. + const completionArgs = args.slice(args.indexOf(`--${completion.completionKey}`) + 1) + completion.getCompletion(completionArgs, (completions) => { + ;(completions || []).forEach((completion) => { + _logger.log(completion) + }) + + self.exit(0) + }) + return argv + } + + // Handle 'help' and 'version' options + // if we haven't already output help! + if (!hasOutput) { + Object.keys(argv).forEach((key) => { + if (key === helpOpt && argv[key]) { + if (exitProcess) setBlocking(true) + + skipValidation = true + self.showHelp('log') + self.exit(0) + } else if (key === versionOpt && argv[key]) { + if (exitProcess) setBlocking(true) + + skipValidation = true + usage.showVersion() + self.exit(0) + } + }) + } + + // Check if any of the options to skip validation were provided + if (!skipValidation && options.skipValidation.length > 0) { + skipValidation = Object.keys(argv).some(key => options.skipValidation.indexOf(key) >= 0 && argv[key] === true) + } + + // If the help or version options where used and exitProcess is false, + // or if explicitly skipped, we won't run validations. + if (!skipValidation) { + if (parsed.error) throw new YError(parsed.error.message) + + // if we're executed via bash completion, don't + // bother with validation. + if (!requestCompletions) { + self._runValidation(argv, aliases, {}, parsed.error) + } + } + } catch (err) { + if (err instanceof YError) usage.fail(err.message, err) + else throw err + } + + return argv + } + + self._runValidation = function runValidation (argv, aliases, positionalMap, parseErrors) { + if (parseErrors) throw new YError(parseErrors.message || parseErrors) + validation.nonOptionCount(argv) + validation.requiredArguments(argv) + if (strict) validation.unknownArguments(argv, aliases, positionalMap) + validation.customChecks(argv, aliases) + validation.limitedChoices(argv) + validation.implications(argv) + validation.conflicting(argv) + } + + function guessLocale () { + if (!detectLocale) return + + try { + const osLocale = require('os-locale') + self.locale(osLocale.sync({ spawn: false })) + } catch (err) { + // if we explode looking up locale just noop + // we'll keep using the default language 'en'. + } + } + + // an app should almost always have --version and --help, + // if you *really* want to disable this use .help(false)/.version(false). + self.help() + self.version() + + return self +} + +// rebase an absolute path to a relative one with respect to a base directory +// exported for tests +exports.rebase = rebase +function rebase (base, dir) { + return path.relative(base, dir) +} diff --git a/WebServer/package-lock.json b/WebServer/package-lock.json new file mode 100644 index 000000000..904f6b402 --- /dev/null +++ b/WebServer/package-lock.json @@ -0,0 +1,975 @@ +{ + "name": "@superalgos/saweb", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@superalgos/mqservice": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@superalgos/mqservice/-/mqservice-0.0.12.tgz", + "integrity": "sha512-Dw6VvjuqB1a7w+7Ghqi4pxvEJDUKWMbS1W/xNmSlTJhI3sW/YexmBbF8nmvdyIfxtajO/+Q1ZoqeBWsiPcjmcQ==", + "requires": { + "chai": "4.2.0", + "mocha": "6.1.4" + } + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "1.9.3" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "1.0.3" + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "2.0.3" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "requires": { + "assertion-error": "1.1.0", + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.0", + "type-detect": "4.0.8" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.7.0", + "shebang-command": "1.2.0", + "which": "1.3.1" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "4.0.8" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "1.1.1" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "dotenv": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz", + "integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "1.4.0" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "requires": { + "es-to-primitive": "1.2.0", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4", + "object-keys": "1.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "6.0.5", + "get-stream": "4.1.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "3.0.0" + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "requires": { + "is-buffer": "2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + } + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "3.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "1.0.3" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "1.0.0" + } + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.1" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "3.0.0", + "path-exists": "3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "requires": { + "chalk": "2.4.2" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "1.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "requires": { + "map-age-cleaner": "0.1.3", + "mimic-fn": "2.1.0", + "p-is-promise": "2.1.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", + "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "requires": { + "object.getownpropertydescriptors": "2.0.3", + "semver": "5.7.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "2.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "1.1.3", + "function-bind": "1.1.1", + "has-symbols": "1.0.0", + "object-keys": "1.1.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "1.1.3", + "es-abstract": "1.13.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "requires": { + "is-wsl": "1.1.0" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "1.0.0", + "lcid": "2.0.0", + "mem": "4.3.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "requires": { + "p-try": "2.2.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "requires": { + "glob": "7.1.3" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "requires": { + "has-flag": "3.0.0" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "2.1.1" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yargs": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "requires": { + "cliui": "4.1.0", + "find-up": "3.0.0", + "get-caller-file": "2.0.5", + "os-locale": "3.1.0", + "require-directory": "2.1.1", + "require-main-filename": "2.0.0", + "set-blocking": "2.0.0", + "string-width": "3.1.0", + "which-module": "2.0.0", + "y18n": "4.0.0", + "yargs-parser": "13.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "7.0.3", + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "5.2.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "requires": { + "camelcase": "5.3.1", + "decamelize": "1.2.0" + } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "requires": { + "flat": "4.1.0", + "lodash": "4.17.11", + "yargs": "12.0.5" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "3.0.0", + "get-caller-file": "1.0.3", + "os-locale": "3.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "4.0.0", + "yargs-parser": "11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "5.3.1", + "decamelize": "1.2.0" + } + } + } + } + } +} diff --git a/WebServer/package.json b/WebServer/package.json new file mode 100644 index 000000000..c4824cd0f --- /dev/null +++ b/WebServer/package.json @@ -0,0 +1,19 @@ +{ + "name": "@superalgos/saweb", + "version": "0.1.0", + "description": "Superalgos web interface.", + "main": "server.js", + "author": "Superalgos, ltd.", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/Superalgos/AAWeb.git" + }, + "dependencies": { + "@superalgos/mqservice": "0.0.12", + "axios": "0.19.0", + "dotenv": "8.0.0", + "open": "^6.4.0", + "rimraf": "^3.0.0" + } +} diff --git a/WebServer/server.js b/WebServer/server.js new file mode 100644 index 000000000..ec81065fb --- /dev/null +++ b/WebServer/server.js @@ -0,0 +1,555 @@ + +require('dotenv').config() +const { fork } = require('child_process') +const open = require('open') + +CONSOLE_LOG = process.env.CONSOLE_LOG === 'true' +CONSOLE_ERROR_LOG = process.env.CONSOLE_ERROR_LOG === 'true' +LOG_FILE_CONTENT = process.env.LOG_FILE_CONTENT === 'true' + +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 port = process.env.WEB_SERVER_PORT + +process.on('uncaughtException', function (err) { + if (err.message.indexOf("EADDRINUSE") > 0) { + console.log("Superalgos Web Server cannot be started. Reason: the port " + port + " is already in use by another application. The web server cannot be started until either you stop the other application or you change the port number at the WebServer/.env file for an unused one. (variable name WEB_SERVER_PORT)") + return + } + console.log('[ERROR] Web Server -> server -> uncaughtException -> err.message = ' + err.message) + console.log('[ERROR] Web Server -> server -> uncaughtException -> err.stack = ' + err.stack) + process.exit(1) +}) + +process.on('unhandledRejection', (reason, p) => { + console.log('[ERROR] Web Server -> server -> unhandledRejection -> reason = ' + JSON.stringify(reason)) + console.log('[ERROR] Web Server -> server -> unhandledRejection -> p = ' + JSON.stringify(p)) + process.exit(1) +}) + +let http = require('http') +let isHttpServerStarted = false +let cloneExecutorChildProcess + +startHtttpServer() + +function startHtttpServer () { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> startHtttpServer -> Entering function.') } + + try { + if (isHttpServerStarted === false) { + gWebServer = http.createServer(onBrowserRequest).listen(port) + isHttpServerStarted = true + open('http://localhost:' + port) + console.log('Web Server Started.') + } + } catch (err) { + console.log('[ERROR] server -> startHtttpServer -> Error = ' + err.stack) + } +} + +function startCloneExecutor () { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> startCloneExecutor -> Entering function.') }; + + if (process.env.RUN_CLON_EXECUTOR !== 'true') { return } + + let path = process.env.CLONE_EXECUTOR_PATH + '/run.js' + cloneExecutorChildProcess = fork(path, [], { stdio: 'inherit' }) + + cloneExecutorChildProcess.on('error', (err) => { + console.log(`[ERROR] server -> startCloneExecutor -> Clone Executor exited with error ${err}`) + }) + cloneExecutorChildProcess.on('close', (code, signal) => { + console.log(`[INFO] CloneExecutor process terminated.`) + }) +} + +function stopCloneExecutor () { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> stopCloneExecutor -> Entering function.') }; + + if (cloneExecutorChildProcess) { + cloneExecutorChildProcess.send('STOP'); + } +} + +function onBrowserRequest (request, response) { + if (CONSOLE_LOG === true && request.url.indexOf('NO-LOG') === -1) { console.log('[INFO] server -> onBrowserRequest -> request.url = ' + request.url) } + + 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 + } + + requestParameters = request.url.split('?') // Remove version information + requestParameters = requestParameters[0].split('/') + + switch (requestParameters[1]) { + + case 'ResetLogsAndData': + { + try { + let rimraf = require('rimraf') + rimraf.sync(process.env.STORAGE_PATH + '/AAMasters/AAMasters/AAJason.1.0') + rimraf.sync(process.env.LOG_PATH) + + respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), response) + } catch (err) { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> ResetLogsAndData -> Could not delete Logs and Data.') } + let error = { + result: 'Fail Because', + message: err.message + } + respondWithContent(JSON.stringify(error), response) + } + break + } + case 'RestartCloneExecutor': + { + try { + stopCloneExecutor() + startCloneExecutor() + respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), response) + } catch (err) { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> ResetLogsAndData -> Could not restart Clone Executor.') } + let error = { + result: 'Fail Because', + message: err.message + } + respondWithContent(JSON.stringify(error), response) + } + + break + } + case 'StopCloneExecutor': + { + try { + stopCloneExecutor() + respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), response) + } catch (err) { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> ResetLogsAndData -> Could not Stop Clone Executor.') } + let error = { + result: 'Fail Because', + message: err.message + } + respondWithContent(JSON.stringify(error), response) + } + break + } + case 'MQService': + { + let filePath = process.env.PATH_TO_WEB_SERVER + 'node_modules/@superalgos/mqservice/orderLifeCicle/webDependency.js' + + respondWithFile(filePath, response) + break + } + + case 'LegacyPlotter.js': + { + respondWithFile(process.env.PATH_TO_WEB_SERVER + 'LegacyPlotter.js', response) + } + break + + case 'PlotterPanel.js': + { + respondWithFile(process.env.PATH_TO_WEB_SERVER + 'PlotterPanel.js', response) + } + break + + case 'Ecosystem.js': + { + respondWithFile(process.env.PATH_TO_WEB_SERVER + 'Ecosystem.js', response) + } + break + + case 'Images': // This means the Scripts folder. + { + let path = process.env.PATH_TO_WEB_SERVER + '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] + } + + path = unescape(path) + + respondWithImage(path, response) + } + break + + case 'favicon.ico': // This means the Scripts folder. + { + respondWithImage(process.env.PATH_TO_WEB_SERVER + 'Images/' + 'favicon.ico', response) + } + break + + case 'CockpitSpace': // This means the CockpitSpace folder. + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/CockpitSpace/' + requestParameters[2], response) + } + break + + case 'TopSpace': // This means the TopSpace folder. + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/TopSpace/' + requestParameters[2], response) + } + break + + case 'DesignSpace': // This means the DesignSpace folder. + + { + if (requestParameters[3] === undefined) { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/DesignSpace/' + requestParameters[2], response) + return + } + if (requestParameters[4] === undefined) { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/DesignSpace/' + requestParameters[2] + '/' + requestParameters[3], response) + return + } + if (requestParameters[5] === undefined) { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/DesignSpace/' + requestParameters[2] + '/' + requestParameters[3] + '/' + requestParameters[4], response) + return + } + } + break + + case 'ControlsToolBox': // This means the DesignSpace folder. + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/ControlsToolBox/' + requestParameters[2], response) + } + break + + case 'Utilities': // This means the DesignSpace folder. + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/Utilities/' + requestParameters[2], response) + } + break + + case 'Scripts': // This means the Scripts folder. + { + respondWithFile(process.env.PATH_TO_WEB_SERVER + 'Scripts/' + requestParameters[2], response) + } + break + + case 'externalScripts': // This means the Scripts folder. + { + respondWithFile(process.env.PATH_TO_WEB_SERVER + 'externalScripts/' + requestParameters[2], response) + } + break + + case 'Plotters': // This means the plotter folder, not to be confused with the Plotters script! + { + let dataMine = requestParameters[2] + let codeName = requestParameters[3] + let moduleName = requestParameters[4] + let filePath = process.env.PLOTTERS_PATH + '/' + dataMine + '/plotters/' + codeName + '/' + moduleName + respondWithFile(filePath, response) + } + break + + case 'PlotterPanels': // This means the PlotterPanels folder, not to be confused with the Plotter Panels scripts! + { + let dataMine = requestParameters[2] + let codeName = requestParameters[3] + let moduleName = requestParameters[4] + let filePath = process.env.PLOTTERS_PATH + '/' + dataMine + '/plotters/' + codeName + '/' + moduleName + respondWithFile(filePath, response) + } + break + case 'Panels': + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response) + } + break + + case 'ChartLayers': + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response) + } + break + + case 'Spaces': + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response) + } + break + + case 'Scales': + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response) + } + break + + case 'Files': + { + respondWithFile(process.env.PATH_TO_FILES_COMPONENT + '/' + requestParameters[2], response) + } + break + + case 'FloatingSpace': + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response) + } + break + + case 'ChartingSpace': + { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response) + } + break + + case 'UserEcosystem.js': + { + let fs = require('fs') + + try { + let filePath = process.env.INTER_PROCESS_FILES_PATH + '/ecosystem.json' + fs.readFile(filePath, onFileRead) + } catch (e) { + console.log('[ERROR] Error reading the user ecosystem.', e) + } + + function onFileRead (err, userEcosystem) { + if (err) { + respondWithContent(undefined, response) + } else { + let responseContent = 'function getUserEcosystem(){ return ' + userEcosystem + '}' + respondWithContent(responseContent, response) + } + } + } + break + + case 'AppSchema.js': + { + let fs = require('fs') + + try { + let filePath = process.env.INTER_PROCESS_FILES_PATH + '/AppSchema.json' + fs.readFile(filePath, onFileRead) + } catch (e) { + console.log('[ERROR] Error reading the App Schema.', e) + } + + function onFileRead(err, appSchema) { + if (err) { + respondWithContent(undefined, response) + } else { + let responseContent = 'function getAppSchema(){ return ' + appSchema + '}' + respondWithContent(responseContent, response) + } + } + } + break + + case 'Storage': + { + respondWithFile(process.env.STORAGE_PATH + '/' + request.url.substring(9), response) + } + break + + case 'GetDefinition': + { + respondWithFile(process.env.INTER_PROCESS_FILES_PATH + '/definition.json', response) + } + break + + case 'SaveDefinition': + { + let fs = require('fs') + let body = '' + + request.on('data', function (data) { + body += data + // Too much POST data + if (body.length > 1e6) { + request.connection.destroy() + } + }) + + request.on('end', function () { + try { + let filePath = process.env.INTER_PROCESS_FILES_PATH + '/definition.json' + fs.writeFile(filePath, body, onFileWrite) + } catch (e) { + console.log('[ERROR] Error writing user config.', e) + respondWithContent(undefined, response) + } + + function onFileWrite (err) { + if (err) { + respondWithContent(undefined, response) + } else { + let responseContent = 'User config updated.' + respondWithContent(responseContent, response) + } + } + }) + } + break + + default: + { + homePage() + } + } + + function homePage () { + if (requestParameters[1] === '') { + let fs = require('fs') + try { + let fileName = process.env.PATH_TO_WEB_SERVER + 'index.html' + 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() + + fileContent = fileContent.replace('WEB_SERVER_PORT', process.env.WEB_SERVER_PORT) + respondWithContent(fileContent, response) + } catch (err) { + console.log('[ERROR] server -> onBrowserRequest -> File Not Found: ' + fileName + ' or Error = ' + err.stack) + } + } + } catch (err) { + console.log(err) + } + } else { + respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1], response) + } + } +} + +function respondWithFile (fileName, response) { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> respondWithFile -> Entering function.') } + + let fs = require('fs') + if (fileName.indexOf('undefined') > 0) { + console.log('[WRN] server -> respondWithFile -> Received request for undefined file. ') + respondWithContent(undefined, response) + } else { + try { + fs.readFile(fileName, onFileRead) + + function onFileRead (err, file) { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> respondWithFile -> onFileRead -> Entering function.') } + if (!err) { + respondWithContent(file.toString(), response) + } else { + respondWithContent(undefined, response) + } + } + } catch (err) { + returnEmptyArray() + } + } +} + +function respondWithContent (content, response) { + if (CONSOLE_LOG === true) { console.log('[INFO] server -> respondWithContent -> 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. + + if (content !== undefined) { + response.writeHead(200, { 'Content-Type': 'text/html' }) + response.write(content) + } else { + response.writeHead(404, { 'Content-Type': 'text/html' }) + response.write('The specified key does not exist.') + } + response.end('\n') + } catch (err) { + returnEmptyArray(response) + } +} + +function respondWithImage (fileName, response) { + 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.stack) + } + } + } catch (err) { + console.log('[ERROR] server -> respondWithImage -> err = ' + err.stack) + } +} + +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. + response.setHeader('Expires', '0') // Proxies. + + response.writeHead(200, { 'Content-Type': 'text/html' }) + response.write('[]') + response.end('\n') + } catch (err) { + console.log('[ERROR] server -> returnEmptyArray -> err.message ' + err.message) + } +} diff --git a/WebSocketsServer/.env b/WebSocketsServer/.env new file mode 100644 index 000000000..4bc79e391 --- /dev/null +++ b/WebSocketsServer/.env @@ -0,0 +1,10 @@ + +INTER_PROCESS_FILES_PATH=../Inter-Process-Files +STORAGE_PATH=../Data-Storage +PLOTTERS_PATH=../Legacy-Code + +USE_KEYVAULT=false +WRITE_LOGS_TO_FILES=true +BOTS_PATH=../Legacy-Code +LOG_PATH=../Log-Files + diff --git a/WebSocketsServer/node_modules/async-limiter/index.js b/WebSocketsServer/node_modules/async-limiter/index.js new file mode 100644 index 000000000..c9bd2f977 --- /dev/null +++ b/WebSocketsServer/node_modules/async-limiter/index.js @@ -0,0 +1,67 @@ +'use strict'; + +function Queue(options) { + if (!(this instanceof Queue)) { + return new Queue(options); + } + + options = options || {}; + this.concurrency = options.concurrency || Infinity; + this.pending = 0; + this.jobs = []; + this.cbs = []; + this._done = done.bind(this); +} + +var arrayAddMethods = [ + 'push', + 'unshift', + 'splice' +]; + +arrayAddMethods.forEach(function(method) { + Queue.prototype[method] = function() { + var methodResult = Array.prototype[method].apply(this.jobs, arguments); + this._run(); + return methodResult; + }; +}); + +Object.defineProperty(Queue.prototype, 'length', { + get: function() { + return this.pending + this.jobs.length; + } +}); + +Queue.prototype._run = function() { + if (this.pending === this.concurrency) { + return; + } + if (this.jobs.length) { + var job = this.jobs.shift(); + this.pending++; + job(this._done); + this._run(); + } + + if (this.pending === 0) { + while (this.cbs.length !== 0) { + var cb = this.cbs.pop(); + process.nextTick(cb); + } + } +}; + +Queue.prototype.onDone = function(cb) { + if (typeof cb === 'function') { + this.cbs.push(cb); + this._run(); + } +}; + +function done() { + this.pending--; + this._run(); +} + +module.exports = Queue; diff --git a/WebSocketsServer/node_modules/async-limiter/package.json b/WebSocketsServer/node_modules/async-limiter/package.json new file mode 100644 index 000000000..6228c970f --- /dev/null +++ b/WebSocketsServer/node_modules/async-limiter/package.json @@ -0,0 +1,69 @@ +{ + "_from": "async-limiter@^1.0.0", + "_id": "async-limiter@1.0.1", + "_inBundle": false, + "_integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "_location": "/async-limiter", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "async-limiter@^1.0.0", + "name": "async-limiter", + "escapedName": "async-limiter", + "rawSpec": "^1.0.0", + "saveSpec": null, + "fetchSpec": "^1.0.0" + }, + "_requiredBy": [ + "/ws" + ], + "_resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "_shasum": "dd379e94f0db8310b08291f9d64c3209766617fd", + "_spec": "async-limiter@^1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSockets-Server\\node_modules\\ws", + "author": { + "name": "Samuel Reed" + }, + "bugs": { + "url": "https://github.com/strml/async-limiter/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "asynchronous function queue with adjustable concurrency", + "devDependencies": { + "coveralls": "^3.0.3", + "eslint": "^5.16.0", + "eslint-plugin-mocha": "^5.3.0", + "intelli-espower-loader": "^1.0.1", + "mocha": "^6.1.4", + "nyc": "^14.1.1", + "power-assert": "^1.6.1" + }, + "homepage": "https://github.com/strml/async-limiter#readme", + "keywords": [ + "throttle", + "async", + "limiter", + "asynchronous", + "job", + "task", + "concurrency", + "concurrent" + ], + "license": "MIT", + "name": "async-limiter", + "repository": { + "type": "git", + "url": "git+https://github.com/strml/async-limiter.git" + }, + "scripts": { + "coverage": "nyc npm test && nyc report --reporter=text-lcov | coveralls", + "example": "node example", + "lint": "eslint .", + "test": "mocha --require intelli-espower-loader test/", + "travis": "npm run lint && npm run test" + }, + "version": "1.0.1" +} diff --git a/WebSocketsServer/node_modules/dotenv/config.js b/WebSocketsServer/node_modules/dotenv/config.js new file mode 100644 index 000000000..c7e6447fc --- /dev/null +++ b/WebSocketsServer/node_modules/dotenv/config.js @@ -0,0 +1,11 @@ +/* @flow */ + +(function () { + require('./lib/main').config( + Object.assign( + {}, + require('./lib/env-options'), + require('./lib/cli-options')(process.argv) + ) + ) +})() diff --git a/WebSocketsServer/node_modules/dotenv/lib/cli-options.js b/WebSocketsServer/node_modules/dotenv/lib/cli-options.js new file mode 100644 index 000000000..ad69fabd9 --- /dev/null +++ b/WebSocketsServer/node_modules/dotenv/lib/cli-options.js @@ -0,0 +1,13 @@ +/* @flow */ + +const re = /^dotenv_config_(encoding|path|debug)=(.+)$/ + +module.exports = function optionMatcher (args /*: Array */) { + return args.reduce(function (acc, cur) { + const matches = cur.match(re) + if (matches) { + acc[matches[1]] = matches[2] + } + return acc + }, {}) +} diff --git a/WebSocketsServer/node_modules/dotenv/lib/env-options.js b/WebSocketsServer/node_modules/dotenv/lib/env-options.js new file mode 100644 index 000000000..b4c4a6068 --- /dev/null +++ b/WebSocketsServer/node_modules/dotenv/lib/env-options.js @@ -0,0 +1,18 @@ +/* @flow */ + +// ../config.js accepts options via environment variables +const options = {} + +if (process.env.DOTENV_CONFIG_ENCODING != null) { + options.encoding = process.env.DOTENV_CONFIG_ENCODING +} + +if (process.env.DOTENV_CONFIG_PATH != null) { + options.path = process.env.DOTENV_CONFIG_PATH +} + +if (process.env.DOTENV_CONFIG_DEBUG != null) { + options.debug = process.env.DOTENV_CONFIG_DEBUG +} + +module.exports = options diff --git a/WebSocketsServer/node_modules/dotenv/lib/main.js b/WebSocketsServer/node_modules/dotenv/lib/main.js new file mode 100644 index 000000000..e6c591c6b --- /dev/null +++ b/WebSocketsServer/node_modules/dotenv/lib/main.js @@ -0,0 +1,113 @@ +/* @flow */ +/*:: + +type DotenvParseOptions = { + debug?: boolean +} + +// keys and values from src +type DotenvParseOutput = { [string]: string } + +type DotenvConfigOptions = { + path?: string, // path to .env file + encoding?: string, // encoding of .env file + debug?: string // turn on logging for debugging purposes +} + +type DotenvConfigOutput = { + parsed?: DotenvParseOutput, + error?: Error +} + +*/ + +const fs = require('fs') +const path = require('path') + +function log (message /*: string */) { + console.log(`[dotenv][DEBUG] ${message}`) +} + +const NEWLINE = '\n' +const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/ +const RE_NEWLINES = /\\n/g +const NEWLINES_MATCH = /\n|\r|\r\n/ + +// Parses src into an Object +function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ { + const debug = Boolean(options && options.debug) + const obj = {} + + // convert Buffers before splitting into lines and processing + src.toString().split(NEWLINES_MATCH).forEach(function (line, idx) { + // matching "KEY' and 'VAL' in 'KEY=VAL' + const keyValueArr = line.match(RE_INI_KEY_VAL) + // matched? + if (keyValueArr != null) { + const key = keyValueArr[1] + // default undefined or missing values to empty string + let val = (keyValueArr[2] || '') + const end = val.length - 1 + const isDoubleQuoted = val[0] === '"' && val[end] === '"' + const isSingleQuoted = val[0] === "'" && val[end] === "'" + + // if single or double quoted, remove quotes + if (isSingleQuoted || isDoubleQuoted) { + val = val.substring(1, end) + + // if double quoted, expand newlines + if (isDoubleQuoted) { + val = val.replace(RE_NEWLINES, NEWLINE) + } + } else { + // remove surrounding whitespace + val = val.trim() + } + + obj[key] = val + } else if (debug) { + log(`did not match key and value when parsing line ${idx + 1}: ${line}`) + } + }) + + return obj +} + +// Populates process.env from .env file +function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ { + let dotenvPath = path.resolve(process.cwd(), '.env') + let encoding /*: string */ = 'utf8' + let debug = false + + if (options) { + if (options.path != null) { + dotenvPath = options.path + } + if (options.encoding != null) { + encoding = options.encoding + } + if (options.debug != null) { + debug = true + } + } + + try { + // specifying an encoding returns a string instead of a buffer + const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug }) + + Object.keys(parsed).forEach(function (key) { + if (!Object.prototype.hasOwnProperty.call(process.env, key)) { + process.env[key] = parsed[key] + } else if (debug) { + log(`"${key}" is already defined in \`process.env\` and will not be overwritten`) + } + }) + + return { parsed } + } catch (e) { + return { error: e } + } +} + +module.exports.config = config +module.exports.parse = parse diff --git a/WebSocketsServer/node_modules/dotenv/package.json b/WebSocketsServer/node_modules/dotenv/package.json new file mode 100644 index 000000000..e34966ff3 --- /dev/null +++ b/WebSocketsServer/node_modules/dotenv/package.json @@ -0,0 +1,80 @@ +{ + "_from": "dotenv", + "_id": "dotenv@8.2.0", + "_inBundle": false, + "_integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "_location": "/dotenv", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "dotenv", + "name": "dotenv", + "escapedName": "dotenv", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "_shasum": "97e619259ada750eea3e4ea3e26bceea5424b16a", + "_spec": "dotenv", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSocketsServer", + "bugs": { + "url": "https://github.com/motdotla/dotenv/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Loads environment variables from .env file", + "devDependencies": { + "decache": "^4.5.1", + "dtslint": "^0.9.8", + "flow-bin": "^0.109.0", + "sinon": "^7.5.0", + "standard": "^13.1.0", + "standard-markdown": "^5.1.0", + "standard-version": "^7.0.0", + "tap": "^14.7.0" + }, + "engines": { + "node": ">=8" + }, + "homepage": "https://github.com/motdotla/dotenv#readme", + "keywords": [ + "dotenv", + "env", + ".env", + "environment", + "variables", + "config", + "settings" + ], + "license": "BSD-2-Clause", + "main": "lib/main.js", + "name": "dotenv", + "repository": { + "type": "git", + "url": "git://github.com/motdotla/dotenv.git" + }, + "scripts": { + "dtslint": "dtslint types", + "flow": "flow", + "lint": "standard", + "postlint": "standard-markdown", + "prerelease": "npm test", + "pretest": "npm run lint && npm run dtslint", + "release": "standard-version", + "test": "tap tests/*.js --100" + }, + "standard": { + "ignore": [ + "flow-typed/" + ] + }, + "types": "types", + "version": "8.2.0" +} diff --git a/WebSocketsServer/node_modules/dotenv/types/tsconfig.json b/WebSocketsServer/node_modules/dotenv/types/tsconfig.json new file mode 100644 index 000000000..012b05dd0 --- /dev/null +++ b/WebSocketsServer/node_modules/dotenv/types/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es6"], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noEmit": true, + "baseUrl": ".", + "paths": { + "dotenv": ["."] + } + } +} diff --git a/WebSocketsServer/node_modules/dotenv/types/tslint.json b/WebSocketsServer/node_modules/dotenv/types/tslint.json new file mode 100644 index 000000000..c634f996e --- /dev/null +++ b/WebSocketsServer/node_modules/dotenv/types/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "dtslint/dtslint.json", + "rules": { + "strict-export-declare-modifiers": false + } +} diff --git a/WebSocketsServer/node_modules/easy-stack/es5.js b/WebSocketsServer/node_modules/easy-stack/es5.js new file mode 100644 index 000000000..a756a7bbf --- /dev/null +++ b/WebSocketsServer/node_modules/easy-stack/es5.js @@ -0,0 +1,74 @@ +function Stack(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToStack + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearStack + }, + contents:{ + enumerable:false, + get:getStack, + set:setStack + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var stack=[]; + var running=false; + var stop=false; + + function clearStack(){ + stack=[]; + return stack; + } + + function getStack(){ + return stack; + } + + function setStack(val){ + stack=val; + return stack; + } + + function addToStack(){ + for(var i in arguments){ + stack.unshift(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(stack.length<1 || this.stop){ + running=false; + return; + } + + stack.shift().bind(this)(); + } +} diff --git a/WebSocketsServer/node_modules/easy-stack/example/basic.js b/WebSocketsServer/node_modules/easy-stack/example/basic.js new file mode 100644 index 000000000..f0d7fbb11 --- /dev/null +++ b/WebSocketsServer/node_modules/easy-stack/example/basic.js @@ -0,0 +1,18 @@ +var Stack=require('../stack'); +//create a new Stack instance +var stack=new Stack; +stack.autoRun=false; + +for(var i=0; i<50; i++){ + //add a bunch of stuff to the stack + stack.add(makeRequest.bind(stack,i)); +} + +stack.next(); + +function makeRequest(index){ + //do stuff + console.log(`making LIFO request ${index}`); + + this.next(); +} diff --git a/WebSocketsServer/node_modules/easy-stack/package.json b/WebSocketsServer/node_modules/easy-stack/package.json new file mode 100644 index 000000000..3b7e5ab52 --- /dev/null +++ b/WebSocketsServer/node_modules/easy-stack/package.json @@ -0,0 +1,59 @@ +{ + "_from": "easy-stack@^1.0.0", + "_id": "easy-stack@1.0.0", + "_inBundle": false, + "_integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "_location": "/easy-stack", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "easy-stack@^1.0.0", + "name": "easy-stack", + "escapedName": "easy-stack", + "rawSpec": "^1.0.0", + "saveSpec": null, + "fetchSpec": "^1.0.0" + }, + "_requiredBy": [ + "/js-queue" + ], + "_resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "_shasum": "12c91b3085a37f0baa336e9486eac4bf94e3e788", + "_spec": "easy-stack@^1.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSockets-Server\\node_modules\\js-queue", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/easy-stack/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Simple JS stack with auto run for node and browsers", + "engines": { + "node": ">=6.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/easy-stack#readme", + "keywords": [ + "stack", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "stack.js", + "name": "easy-stack", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/easy-stack.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.0" +} diff --git a/WebSocketsServer/node_modules/easy-stack/stack.js b/WebSocketsServer/node_modules/easy-stack/stack.js new file mode 100644 index 000000000..bfadd1dee --- /dev/null +++ b/WebSocketsServer/node_modules/easy-stack/stack.js @@ -0,0 +1,39 @@ +class Stack{ + constructor(){ + this.stack=[]; + this.autoRun=true; + this.running=false; + this.stop=false; + } + + clear(){ + this.stack=[]; + return this.stack; + } + + contents(val){ + if(val){ + this.stack=val; + } + return this.stack; + } + + add(...callbacks){ + this.stack.push(...callbacks); + if(!this.running && !this.stop && this.autoRun){ + this.next(); + } + } + + next(){ + this.running=true; + if(this.stack.length<1 || this.stop){ + this.running=false; + return; + } + + this.stack.pop().bind(this)(); + } +} + +module.exports=Stack; diff --git a/WebSocketsServer/node_modules/event-pubsub/bower.json b/WebSocketsServer/node_modules/event-pubsub/bower.json new file mode 100644 index 000000000..a207b0a5d --- /dev/null +++ b/WebSocketsServer/node_modules/event-pubsub/bower.json @@ -0,0 +1,28 @@ +{ + "name": "event-pubsub", + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "main": "event-pubsub.js", + "authors": [ + "Brandon Nozaki Miller" + ], + "license": "DBAD", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "moduleType": [ + "globals", + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/WebSocketsServer/node_modules/event-pubsub/es5.js b/WebSocketsServer/node_modules/event-pubsub/es5.js new file mode 100644 index 000000000..7faf8b737 --- /dev/null +++ b/WebSocketsServer/node_modules/event-pubsub/es5.js @@ -0,0 +1,143 @@ +'use strict'; + +function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} + +module.exports=EventPubSub; diff --git a/WebSocketsServer/node_modules/event-pubsub/es6.js b/WebSocketsServer/node_modules/event-pubsub/es6.js new file mode 100644 index 000000000..e9e859f9b --- /dev/null +++ b/WebSocketsServer/node_modules/event-pubsub/es6.js @@ -0,0 +1,99 @@ +'use strict'; + +class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +module.exports = EventPubSub; diff --git a/WebSocketsServer/node_modules/event-pubsub/event-pubsub-browser-es5.js b/WebSocketsServer/node_modules/event-pubsub/event-pubsub-browser-es5.js new file mode 100644 index 000000000..03f27625e --- /dev/null +++ b/WebSocketsServer/node_modules/event-pubsub/event-pubsub-browser-es5.js @@ -0,0 +1,141 @@ +'use strict'; + +window.EventPubSub=function EventPubSub() { + this._events_={}; + this.publish=this.trigger=this.emit=emit; + this.subscribe=this.on=on; + this.once=once; + this.unSubscribe=this.off=off; + this.emit$=emit$; + + function on(type,handler,once){ + if(!handler){ + throw new ReferenceError('handler not defined.'); + } + + if(!this._events_[type]){ + this._events_[type]=[]; + } + + if(once){ + handler._once_ = once; + } + this._events_[type].push(handler); + return this; + } + + function once(type,handler){ + return this.on(type, handler, true); + } + + function off(type,handler){ + if(!this._events_[type]){ + return this; + } + + if(!handler){ + throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler'); + } + + if(handler=='*'){ + delete this._events_[type]; + return this; + } + + var handlers=this._events_[type]; + + while(handlers.includes(handler)){ + handlers.splice( + handlers.indexOf(handler), + 1 + ); + } + + if(handlers.length<1){ + delete this._events_[type]; + } + + return this; + } + + function emit(type){ + this.emit$.apply(this, arguments); + if(!this._events_[type]){ + return this; + } + arguments.splice=Array.prototype.splice; + arguments.splice(0,1); + + var handlers=this._events_[type]; + var onceHandled=[]; + + for(var i in handlers){ + var handler=handlers[i]; + handler.apply(this, arguments); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(var i in onceHandled){ + this.off( + type, + onceHandled[i] + ); + } + + return this; + } + + function emit$(type, args){ + if(!this._events_['*']){ + return this; + } + + var catchAll=this._events_['*']; + + args.shift=Array.prototype.shift; + args.shift(type); + + for(var handler of catchAll){ + handler.apply(this, args); + } + + return this; + } + + return this; +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/WebSocketsServer/node_modules/event-pubsub/event-pubsub-browser.js b/WebSocketsServer/node_modules/event-pubsub/event-pubsub-browser.js new file mode 100644 index 000000000..d0bcd7b56 --- /dev/null +++ b/WebSocketsServer/node_modules/event-pubsub/event-pubsub-browser.js @@ -0,0 +1,130 @@ +'use strict'; + +window.EventPubSub=class EventPubSub { + constructor( scope ) { + this._events_ = {}; + this.publish = this.trigger = this.emit; + this.subscribe = this.on; + this.unSubscribe = this.off; + } + + on( type, handler, once ) { + if ( !handler ) { + throw new ReferenceError( 'handler not defined.' ); + } + + if ( !this._events_[ type ] ) { + this._events_[ type ] = []; + } + + if(once){ + handler._once_ = once; + } + + this._events_[ type ].push( handler ); + return this; + } + + once( type, handler ) { + return this.on( type, handler, true ); + } + + off( type, handler ) { + if ( !this._events_[ type ] ) { + return this; + } + + if ( !handler ) { + throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' ); + } + + if ( handler == '*' ) { + delete this._events_[ type ]; + return this; + } + + const handlers = this._events_[ type ]; + + while ( handlers.includes( handler ) ) { + handlers.splice( + handlers.indexOf( handler ), + 1 + ); + } + + if ( handlers.length < 1 ) { + delete this._events_[ type ]; + } + + return this; + } + + emit( type, ...args ) { + if ( !this._events_[ type ] ) { + return this.emit$( type, ...args ); + } + + const handlers = this._events_[ type ]; + const onceHandled=[]; + + for ( let handler of handlers ) { + handler.apply( this, args ); + if(handler._once_){ + onceHandled.push(handler); + } + } + + for(let handler of onceHandled){ + this.off(type,handler); + } + + return this.emit$( type, ...args ); + } + + emit$( type, ...args ) { + if ( !this._events_[ '*' ] ) { + return this; + } + + const catchAll = this._events_[ '*' ]; + + for ( let handler of catchAll ) { + handler.call( this, type, ...args ); + } + + return this; + } +} + +if (!Array.prototype.includes) { + Array.prototype.includes = function(searchElement /*, fromIndex*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined'); + } + + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) {k = 0;} + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + }; +} diff --git a/WebSocketsServer/node_modules/event-pubsub/event-pubsub.js b/WebSocketsServer/node_modules/event-pubsub/event-pubsub.js new file mode 100644 index 000000000..65bd02419 --- /dev/null +++ b/WebSocketsServer/node_modules/event-pubsub/event-pubsub.js @@ -0,0 +1,8 @@ +'use strict'; + +let EventPubSub = require('./es5'); +if(process.version[1]>5){ + EventPubSub = require('./es6'); +} + +module.exports=EventPubSub; diff --git a/WebSocketsServer/node_modules/event-pubsub/package.json b/WebSocketsServer/node_modules/event-pubsub/package.json new file mode 100644 index 000000000..0ec9ca8b0 --- /dev/null +++ b/WebSocketsServer/node_modules/event-pubsub/package.json @@ -0,0 +1,59 @@ +{ + "_from": "event-pubsub@4.3.0", + "_id": "event-pubsub@4.3.0", + "_inBundle": false, + "_integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "_location": "/event-pubsub", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "event-pubsub@4.3.0", + "name": "event-pubsub", + "escapedName": "event-pubsub", + "rawSpec": "4.3.0", + "saveSpec": null, + "fetchSpec": "4.3.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "_shasum": "f68d816bc29f1ec02c539dc58c8dd40ce72cb36e", + "_spec": "event-pubsub@4.3.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSockets-Server\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/event-pubsub/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!", + "directories": { + "example": "examples" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/event-pubsub", + "keywords": [ + "event", + "events", + "pubsub", + "node", + "browser" + ], + "license": "Unlicense", + "main": "event-pubsub.js", + "name": "event-pubsub", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/event-pubsub.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "4.3.0" +} diff --git a/WebSocketsServer/node_modules/js-message/Message.js b/WebSocketsServer/node_modules/js-message/Message.js new file mode 100644 index 000000000..9a4698232 --- /dev/null +++ b/WebSocketsServer/node_modules/js-message/Message.js @@ -0,0 +1,71 @@ +function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } +} + +module.exports=Message; diff --git a/WebSocketsServer/node_modules/js-message/js-message-vanilla.js b/WebSocketsServer/node_modules/js-message/js-message-vanilla.js new file mode 100644 index 000000000..0c49c2b6c --- /dev/null +++ b/WebSocketsServer/node_modules/js-message/js-message-vanilla.js @@ -0,0 +1,75 @@ +( + function(){ + function Message() { + Object.defineProperties( + this, { + data: { + enumerable: true, + get: getData, + set: setData + }, + type: { + enumerable: true, + get: getType, + set: setType + }, + load:{ + enumarable:true, + writable:false, + value:parse + }, + JSON: { + enumerable: true, + get: getJSON + } + } + ); + + var type = ''; + var data = {}; + + function getType() { + return type; + } + + function getData() { + return data; + } + + function getJSON() { + return JSON.stringify( + { + type: type, + data: data + } + ); + } + + function setType(value) { + type = value; + } + + function setData(value) { + data = value; + } + + function parse(message){ + try{ + var message=JSON.parse(message); + type=message.type; + data=message.data; + }catch(err){ + var badMessage=message; + type='error', + data={ + message:'Invalid JSON response format', + err:err, + response:badMessage + } + } + } + } + + window.message=Message; + } +)(); diff --git a/WebSocketsServer/node_modules/js-message/package.json b/WebSocketsServer/node_modules/js-message/package.json new file mode 100644 index 000000000..5ebf51ee3 --- /dev/null +++ b/WebSocketsServer/node_modules/js-message/package.json @@ -0,0 +1,81 @@ +{ + "_from": "js-message@1.0.5", + "_id": "js-message@1.0.5", + "_inBundle": false, + "_integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "_location": "/js-message", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-message@1.0.5", + "name": "js-message", + "escapedName": "js-message", + "rawSpec": "1.0.5", + "saveSpec": null, + "fetchSpec": "1.0.5" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "_shasum": "2300d24b1af08e89dd095bc1a4c9c9cfcb892d15", + "_spec": "js-message@1.0.5", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSockets-Server\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-message/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "normalized JS Object and JSON message and event protocol for node.js, vanialla js, react.js, components, actions, stores and dispatchers", + "devDependencies": {}, + "engines": { + "node": ">=0.6.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-message#readme", + "keywords": [ + "message", + "normalize", + "events", + "js", + "json", + "protocol", + "ipc", + "node", + "nodejs", + "node.js", + "react", + "react.js", + "reactjs", + "websocket", + "websockets", + "web", + "socket", + "sockets", + "ws", + "flux", + "reflux", + "component", + "components", + "store", + "stores", + "action", + "actions" + ], + "license": "DBAD", + "main": "Message.js", + "name": "js-message", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-message.git" + }, + "scripts": { + "start": "node devServer.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "1.0.5" +} diff --git a/WebSocketsServer/node_modules/js-queue/package.json b/WebSocketsServer/node_modules/js-queue/package.json new file mode 100644 index 000000000..4287eec41 --- /dev/null +++ b/WebSocketsServer/node_modules/js-queue/package.json @@ -0,0 +1,62 @@ +{ + "_from": "js-queue@2.0.0", + "_id": "js-queue@2.0.0", + "_inBundle": false, + "_integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "_location": "/js-queue", + "_phantomChildren": {}, + "_requested": { + "type": "version", + "registry": true, + "raw": "js-queue@2.0.0", + "name": "js-queue", + "escapedName": "js-queue", + "rawSpec": "2.0.0", + "saveSpec": null, + "fetchSpec": "2.0.0" + }, + "_requiredBy": [ + "/node-ipc" + ], + "_resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "_shasum": "362213cf860f468f0125fc6c96abc1742531f948", + "_spec": "js-queue@2.0.0", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSockets-Server\\node_modules\\node-ipc", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/js-queue/issues" + }, + "bundleDependencies": false, + "dependencies": { + "easy-stack": "^1.0.0" + }, + "deprecated": false, + "description": "Simple JS queue with auto run for node and browsers", + "engines": { + "node": ">=1.0.0" + }, + "homepage": "https://github.com/RIAEvangelist/js-queue#readme", + "keywords": [ + "queue", + "node", + "js", + "auto", + "run", + "execute", + "browser", + "react" + ], + "license": "DBAD", + "main": "queue.js", + "name": "js-queue", + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/js-queue.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "2.0.0" +} diff --git a/WebSocketsServer/node_modules/js-queue/queue-vanilla.js b/WebSocketsServer/node_modules/js-queue/queue-vanilla.js new file mode 100644 index 000000000..6f15495ae --- /dev/null +++ b/WebSocketsServer/node_modules/js-queue/queue-vanilla.js @@ -0,0 +1,74 @@ +function Queue(){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} diff --git a/WebSocketsServer/node_modules/js-queue/queue.js b/WebSocketsServer/node_modules/js-queue/queue.js new file mode 100644 index 000000000..a8698d7e9 --- /dev/null +++ b/WebSocketsServer/node_modules/js-queue/queue.js @@ -0,0 +1,76 @@ +function Queue(asStack){ + Object.defineProperties( + this, + { + add:{ + enumerable:true, + writable:false, + value:addToQueue + }, + next:{ + enumerable:true, + writable:false, + value:run + }, + clear:{ + enumerable:true, + writable:false, + value:clearQueue + }, + contents:{ + enumerable:false, + get:getQueue, + set:setQueue + }, + autoRun:{ + enumerable:true, + writable:true, + value:true + }, + stop:{ + enumerable:true, + writable:true, + value:false + } + } + ); + + var queue=[]; + var running=false; + var stop=false; + + function clearQueue(){ + queue=[]; + return queue; + } + + function getQueue(){ + return queue; + } + + function setQueue(val){ + queue=val; + return queue; + } + + function addToQueue(){ + for(var i in arguments){ + queue.push(arguments[i]); + } + if(!running && !this.stop && this.autoRun){ + this.next(); + } + } + + function run(){ + running=true; + if(queue.length<1 || this.stop){ + running=false; + return; + } + + queue.shift().bind(this)(); + } +} + +module.exports=Queue; diff --git a/WebSocketsServer/node_modules/js-queue/stack.js b/WebSocketsServer/node_modules/js-queue/stack.js new file mode 100644 index 000000000..a9c25640e --- /dev/null +++ b/WebSocketsServer/node_modules/js-queue/stack.js @@ -0,0 +1,3 @@ +const Stack=require('easy-stack'); + +module.exports = Stack; diff --git a/WebSocketsServer/node_modules/node-ipc/dao/client.js b/WebSocketsServer/node_modules/node-ipc/dao/client.js new file mode 100644 index 000000000..41ce50411 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/dao/client.js @@ -0,0 +1,257 @@ +'use strict'; + +const net = require('net'), + tls = require('tls'), + EventParser = require('../entities/EventParser.js'), + Message = require('js-message'), + fs = require('fs'), + Queue = require('js-queue'); + +let Events = require('event-pubsub/es5'); +if(process.version[1]>4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Client extends Events{ + constructor(config,log){ + super(); + Object.assign( + this, + { + Client : Client, + config : config, + queue : new Queue, + socket : false, + connect : connect, + emit : emit, + log : log, + retriesRemaining:config.maxRetries||0, + explicitlyDisconnected: false + } + ); + + eventParser=new EventParser(this.config); + } +} + +function emit(type,data){ + this.log('dispatching event to ', this.id, this.path, ' : ', type, ',', data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(!this.config.sync){ + this.socket.write(message); + return; + } + + this.queue.add( + syncEmit.bind(this,message) + ); +} + +function syncEmit(message){ + this.log('dispatching event to ', this.id, this.path, ' : ', message); + this.socket.write(message); +} + +function connect(){ + //init client object for scope persistance especially inside of socket events. + let client=this; + + client.log('requested connection to ', client.id, client.path); + if(!this.path){ + client.log('\n\n######\nerror: ', client.id ,' client has not specified socket path it wishes to connect to.'); + return; + } + + const options={}; + + if(!client.port){ + client.log('Connecting client on Unix Socket :', client.path); + + options.path=client.path; + + if (process.platform ==='win32' && !client.path.startsWith('\\\\.\\pipe\\')){ + options.path = options.path.replace(/^\//, ''); + options.path = options.path.replace(/\//g, '-'); + options.path= `\\\\.\\pipe\\${options.path}`; + } + + client.socket = net.connect(options); + }else{ + options.host=client.path; + options.port=client.port; + + if(client.config.interface.localAddress){ + options.localAddress=client.config.interface.localAddress; + } + + if(client.config.interface.localPort){ + options.localPort=client.config.interface.localPort; + } + + if(client.config.interface.family){ + options.family=client.config.interface.family; + } + + if(client.config.interface.hints){ + options.hints=client.config.interface.hints; + } + + if(client.config.interface.lookup){ + options.lookup=client.config.interface.lookup; + } + + if(!client.config.tls){ + client.log('Connecting client via TCP to', options); + client.socket = net.connect(options); + }else{ + client.log('Connecting client via TLS to', client.path ,client.port,client.config.tls); + if(client.config.tls.private){ + client.config.tls.key=fs.readFileSync(client.config.tls.private); + } + if(client.config.tls.public){ + client.config.tls.cert=fs.readFileSync(client.config.tls.public); + } + if(client.config.tls.trustedConnections){ + if(typeof client.config.tls.trustedConnections === 'string'){ + client.config.tls.trustedConnections=[client.config.tls.trustedConnections]; + } + client.config.tls.ca=[]; + for(let i=0; i4){ + Events = require('event-pubsub'); +} + +let eventParser = new EventParser(); + +class Server extends Events{ + constructor(path,config,log,port){ + super(); + Object.assign( + this, + { + config : config, + path : path, + port : port, + udp4 : false, + udp6 : false, + log : log, + server : false, + sockets : [], + emit : emit, + broadcast : broadcast + } + ); + + eventParser=new EventParser(this.config); + + this.on( + 'close', + serverClosed.bind(this) + ); + } + + onStart(socket){ + this.trigger( + 'start', + socket + ); + } + + stop(){ + this.server.close(); + } + + start(){ + if(!this.path){ + this.log('Socket Server Path not specified, refusing to start'); + return; + } + + if(this.config.unlink){ + fs.unlink( + this.path, + startServer.bind(this) + ); + }else{ + startServer.bind(this)(); + } + } +} + +function emit(socket, type, data){ + this.log('dispatching event to socket', ' : ', type, data); + + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + this.log(this.config.encoding) + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + + if(!socket.address || !socket.port){ + this.log('Attempting to emit to a single UDP socket without supplying socket address or port. Redispatching event as broadcast to all connected sockets'); + this.broadcast(type,data); + return; + } + + this.server.write( + message, + socket + ); + return; + } + + socket.write(message); +} + +function broadcast(type,data){ + this.log('broadcasting event to all known sockets listening to ', this.path,' : ', ((this.port)?this.port:''), type, data); + let message=new Message; + message.type=type; + message.data=data; + + if(this.config.rawBuffer){ + message=new Buffer(type,this.config.encoding); + }else{ + message=eventParser.format(message); + } + + if(this.udp4 || this.udp6){ + for(let i=1, count=this.sockets.length; i0){ + let message=new Message; + message.load(data.shift()); + + // Only set the sock id if it is specified. + if (message.data && message.data.id){ + sock.id=message.data.id; + } + + this.log('received event of : ',message.type,message.data); + + this.publish( + message.type, + message.data, + sock + ); + } +} + +function socketClosed(socket){ + this.publish( + 'close', + socket + ); +} + +function serverCreated(socket) { + this.sockets.push(socket); + + if(socket.setEncoding){ + socket.setEncoding(this.config.encoding); + } + + this.log('## socket connection to server detected ##'); + socket.on( + 'close', + socketClosed.bind(this) + ); + + socket.on( + 'error', + function(err){ + this.log('server socket error',err); + + this.publish('error',err); + }.bind(this) + ); + + socket.on( + 'data', + gotData.bind(this,socket) + ); + + socket.on( + 'message', + function(msg,rinfo) { + if (!rinfo){ + return; + } + + this.log('Received UDP message from ', rinfo.address, rinfo.port); + let data; + + if(this.config.rawSocket){ + data=new Buffer(msg,this.config.encoding); + }else{ + data=msg.toString(); + } + socket.emit('data',data,rinfo); + }.bind(this) + ); + + this.publish( + 'connect', + socket + ); + + if(this.config.rawBuffer){ + return; + } +} + +function startServer() { + this.log( + 'starting server on ',this.path, + ((this.port)?`:${this.port}`:'') + ); + + if(!this.udp4 && !this.udp6){ + this.log('starting TLS server',this.config.tls); + if(!this.config.tls){ + this.server=net.createServer( + serverCreated.bind(this) + ); + }else{ + startTLSServer.bind(this)(); + } + }else{ + this.server=dgram.createSocket( + ((this.udp4)? 'udp4':'udp6') + ); + this.server.write=UDPWrite.bind(this); + this.server.on( + 'listening', + function UDPServerStarted() { + serverCreated.bind(this)(this.server); + }.bind(this) + ); + } + + this.server.on( + 'error', + function(err){ + this.log('server error',err); + + this.publish( + 'error', + err + ); + }.bind(this) + ); + + this.server.maxConnections=this.config.maxConnections; + + if(!this.port){ + this.log('starting server as', 'Unix || Windows Socket'); + if (process.platform ==='win32'){ + this.path = this.path.replace(/^\//, ''); + this.path = this.path.replace(/\//g, '-'); + this.path= `\\\\.\\pipe\\${this.path}`; + } + + this.server.listen( + this.path, + this.onStart.bind(this) + ); + + return; + } + + if(!this.udp4 && !this.udp6){ + this.log('starting server as', (this.config.tls?'TLS':'TCP')); + this.server.listen( + this.port, + this.path, + this.onStart.bind(this) + ); + return; + } + + this.log('starting server as',((this.udp4)? 'udp4':'udp6')); + + this.server.bind( + this.port, + this.path + ); + + this.onStart( + { + address : this.path, + port : this.port + } + ); +} + +function startTLSServer(){ + this.log('starting TLS server',this.config.tls); + if(this.config.tls.private){ + this.config.tls.key=fs.readFileSync(this.config.tls.private); + }else{ + this.config.tls.key=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/private/server.key`); + } + if(this.config.tls.public){ + this.config.tls.cert=fs.readFileSync(this.config.tls.public); + }else{ + this.config.tls.cert=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/server.pub`); + } + if(this.config.tls.dhparam){ + this.config.tls.dhparam=fs.readFileSync(this.config.tls.dhparam); + } + if(this.config.tls.trustedConnections){ + if(typeof this.config.tls.trustedConnections === 'string'){ + this.config.tls.trustedConnections=[this.config.tls.trustedConnections]; + } + this.config.tls.ca=[]; + for(let i=0; i 0) { + // getting the family of first network interface available + IPType = networkInterfaces [ + Object.keys( networkInterfaces )[0] + ][0].family; + } + return IPType; +} + +module.exports=Defaults; diff --git a/WebSocketsServer/node_modules/node-ipc/entities/EventParser.js b/WebSocketsServer/node_modules/node-ipc/entities/EventParser.js new file mode 100644 index 000000000..8ac1f2bda --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/entities/EventParser.js @@ -0,0 +1,32 @@ +'use strict'; + +const Defaults = require('./Defaults.js'); + +class Parser{ + constructor(config){ + if(!config){ + config=new Defaults; + } + this.delimiter=config.delimiter; + } + + format(message){ + if(!message.data && message.data!==false && message.data!==0){ + message.data={}; + } + if(message.data['_maxListeners']){ + message.data={}; + } + + message=message.JSON+this.delimiter; + return message; + } + + parse(data){ + let events=data.split(this.delimiter); + events.pop(); + return events; + } +} + +module.exports=Parser; diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..9d5ce05d6 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..74136333d --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,50 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..2bef44794 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js new file mode 100644 index 000000000..6ae692edf --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basic/hello-client.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js new file mode 100644 index 000000000..6c8790170 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basic/world-server.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.maxConnections=1; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log('DISCONNECTED\n\n',arguments); + } + ); + } +); + +ipc.server.on( + 'error', + function(err){ + ipc.log('Got an ERROR!',err); + } +); + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js new file mode 100644 index 000000000..292415053 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basicSync/hello-client.js @@ -0,0 +1,46 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data,'\n\n'); + } + ); + } +); + +console.log(ipc); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js new file mode 100644 index 000000000..3e817da13 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/basicSync/world-server.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync = true; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + //fake some synch procedural code + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..b6ac81f6b --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..5da4de4d5 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TCPSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js new file mode 100644 index 000000000..508702d01 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/goodbye-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; +ipc.config.maxRetries= 10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js new file mode 100644 index 000000000..16fd21fc3 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/hello-client.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.maxRetries=10; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data.message); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js new file mode 100644 index 000000000..0c52c268d --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/Multi-Client-Broadcast-basic/world-server.js @@ -0,0 +1,55 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js new file mode 100644 index 000000000..3cbcd8e38 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-local-only/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +//node-ipc will default to its local certs +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js new file mode 100644 index 000000000..bda3eb701 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js new file mode 100644 index 000000000..56eee122b --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-more-secure/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:false, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js new file mode 100644 index 000000000..26d5b1da6 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js new file mode 100644 index 000000000..ee984a4e1 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic-most-secure/world-server.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.networkHost='localhost'; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js new file mode 100644 index 000000000..15771565b --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic/hello-client.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'message', + 'hello' + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js new file mode 100644 index 000000000..a52c3602a --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basic/world-server.js @@ -0,0 +1,42 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js new file mode 100644 index 000000000..405db4468 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basicSync/hello-client.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + rejectUnauthorized:false +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'message', + 'hello'+i + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js new file mode 100644 index 000000000..aec87c100 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/basicSync/world-server.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key' +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message : ', data); + setTimeout( + function(){ + ipc.server.emit( + socket, + 'message', + data+' world!' + ); + }, + 3000 + ); + } + ); + + ipc.server.on( + 'socket.disconnected', + function(data,socket){ + console.log(arguments); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js new file mode 100644 index 000000000..f0c66ad22 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/hello-client.js @@ -0,0 +1,45 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + private: __dirname+'/../../../local-node-ipc-certs/private/client.key', + public: __dirname+'/../../../local-node-ipc-certs/client.pub', + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/server.pub' + ] +}; + +ipc.connectToNet( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js new file mode 100644 index 000000000..f0d0b29af --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/TLSSocket/rawBuffer-only-works-with-most-secure/world.server.js @@ -0,0 +1,53 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; +ipc.config.networkHost='localhost'; + +ipc.config.tls={ + public: __dirname+'/../../../local-node-ipc-certs/server.pub', + private: __dirname+'/../../../local-node-ipc-certs/private/server.key', + dhparam: __dirname+'/../../../local-node-ipc-certs/private/dhparam.pem', + requestCert: true, + rejectUnauthorized:true, + trustedConnections: [ + __dirname+'/../../../local-node-ipc-certs/client.pub' + ] +}; + +ipc.serveNet( + function(){ + ipc.server.on( + 'connect', + function(socket){ + console.log('connection detected'); + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..d98a7b061 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8002, //we set the port here because the hello client and world server are already using the default of 8000 and the port 8001. So we can not bind to those while hello and world are connected to them. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Goodbye' + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..ecc0b45a4 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,48 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..97f927088 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,63 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * Since there is no client relationship + * with UDP sockets sockets are not kept + * open. + * + * This means the order sockets are opened + * is important. + * + * Start World first. Then you can start + * hello or goodbye in any order you + * choose. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serveNet( + 'udp4', + function(){ + console.log(123); + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + messages[data.id]=true; + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling evryone how muchg I am loved!'); + ipc.server.broadcast( + 'message', + { + id : ipc.config.id, + message : 'Everybody Loves The World! Got messages from hello and goodbye!' + } + ); + } + } + ); + + console.log(ipc.server); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js new file mode 100644 index 000000000..f731b1ba8 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/basic/hello-client.js @@ -0,0 +1,52 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data){ + ipc.log('got Data'); + ipc.log('got a message from ', data.id ,' : ', data.message); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'message', + { + id : ipc.config.id, + message : 'Hello' + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js new file mode 100644 index 000000000..0f7aa4543 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/basic/world-server.js @@ -0,0 +1,47 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'message', + function(data,socket){ + ipc.log('got a message from ', data.id ,' : ', data.message); + ipc.server.emit( + socket, + 'message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..73cd13ac5 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 8001, //we set the port here because the world server is already using the default of 8000. So we can not bind to 8000 while world is using it. + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data){ + ipc.log('got a message from world ', data, data.toString()); + } + ); + ipc.server.emit( + { + address : 'localhost', + port : ipc.config.networkPort + }, + 'hello' + ); + } +); + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..d0c34f7e2 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/UDPSocket/rawBuffer/world.server.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * UDP Client is really a UDP server + * + * Dedicated UDP sockets on the same + * machine can not be bound to in the + * traditional client/server method + * + * Every UDP socket is it's own UDP server + * And so must have a unique port on its + * machine, unlike TCP or Unix Sockts + * which can share on the same machine. + * + * Since there is no open client server + * relationship, you should start world + * first and then hello. + * + ***************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serveNet( + 'udp4', + function(){ + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js b/WebSocketsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js new file mode 100644 index 000000000..eca2a889c --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-client.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const ipc = require('../../node-ipc'); + +const socketPath = '/tmp/ipc.sock'; + +//loop forever so you can see the pid of the cluster sever change in the logs +setInterval( + function() { + ipc.connectTo( + 'world', + socketPath, + connecting + ); + }, + 2000 +); + +function connecting(socket) { + ipc.of.world.on( + 'connect', + function() { + ipc.of.world.emit( + 'currentDate', + { + message: new Date().toISOString() + } + ); + ipc.disconnect('world'); + } + ); +} diff --git a/WebSocketsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js b/WebSocketsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js new file mode 100644 index 000000000..b0942a338 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/clusterUnixSocket/cluster-server.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const ipc=require('../../../node-ipc'); +const cpuCount = require('os').cpus().length; +const cluster = require('cluster'); +const socketPath = '/tmp/ipc.sock'; + +ipc.config.unlink = false; + +if (cluster.isMaster) { + if (fs.existsSync(socketPath)) { + fs.unlinkSync(socketPath); + } + + for (let i = 0; i < cpuCount; i++) { + cluster.fork(); + } +}else{ + ipc.serve( + socketPath, + function() { + ipc.server.on( + 'currentDate', + function(data,socket) { + console.log(`pid ${process.pid} got: `, data); + } + ); + } + ); + + ipc.server.start(); + console.log(`pid ${process.pid} listening on ${socketPath}`); +} diff --git a/WebSocketsServer/node_modules/node-ipc/example/rawBuffer/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/rawBuffer/hello-client.js new file mode 100644 index 000000000..f22ab5376 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/rawBuffer/hello-client.js @@ -0,0 +1,43 @@ +'use strict'; + +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + //make a 6 byte buffer for example + const myBuffer=new Buffer(6).fill(0); + + myBuffer.writeUInt16BE(0x02,0); + myBuffer.writeUInt32BE(0xffeecc,2); + + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + myBuffer + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/rawBuffer/world.server.js b/WebSocketsServer/node_modules/node-ipc/example/rawBuffer/world.server.js new file mode 100644 index 000000000..f27a02cd5 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='hex'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + [0xaa] + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data); + ipc.server.emit( + socket, + [0x0d,0xee] + ); + } + ); + } +); + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js new file mode 100644 index 000000000..57d455eec --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/goodbye-client.js @@ -0,0 +1,43 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'goodbye'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'goodbye' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js new file mode 100644 index 000000000..d556c4694 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + ipc.of.world.on( + 'kill.connection', + function(data){ + ipc.log('world requested kill.connection'); + ipc.disconnect('world'); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js new file mode 100644 index 000000000..69cfb31c0 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/Multi-Client-Broadcast/world-server.js @@ -0,0 +1,51 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +var messages={ + goodbye:false, + hello:false +}; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.log('got a message from', (data.id), (data.message)); + messages[data.id]=true; + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + + if(messages.hello && messages.goodbye){ + ipc.log('got all required events, telling clients to kill connection'); + ipc.server.broadcast( + 'kill.connection', + { + id:ipc.config.id + } + ); + } + } + ); + } +); + + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js new file mode 100644 index 000000000..c6d87cb27 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/hello-client.js @@ -0,0 +1,44 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello' + } + ); + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js new file mode 100644 index 000000000..6b3052242 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basic/world-server.js @@ -0,0 +1,33 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js new file mode 100644 index 000000000..3142677a2 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/hello-client.js @@ -0,0 +1,49 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry = 1000; +ipc.config.sync= true; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + + //queue up a bunch of requests to be sent synchronously + for(var i=0; i<10; i++){ + ipc.of.world.emit( + 'app.message', + { + id : ipc.config.id, + message : 'hello'+i + } + ); + } + } + ); + ipc.of.world.on( + 'disconnect', + function(){ + ipc.log('disconnected from world'); + } + ); + ipc.of.world.on( + 'app.message', + function(data){ + ipc.log('got a message from world : ', data); + } + ); + + console.log(ipc.of.world.destroy); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js new file mode 100644 index 000000000..c76e1bb6f --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/basicSync/world-server.js @@ -0,0 +1,39 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.sync= true; + +ipc.serve( + function(){ + ipc.server.on( + 'app.message', + function(data,socket){ + setTimeout( + function(){ + ipc.server.emit( + socket, + 'app.message', + { + id : ipc.config.id, + message : data.message+' world!' + } + ); + }, + 2000 + ); + } + ); + } +); + + + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js new file mode 100644 index 000000000..9c7042a90 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/hello-client.js @@ -0,0 +1,35 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'hello'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.connectTo( + 'world', + function(){ + ipc.of.world.on( + 'connect', + function(){ + ipc.log('## connected to world ##', ipc.config.delay); + ipc.of.world.emit( + 'hello' + ); + } + ); + + ipc.of.world.on( + 'data', + function(data){ + ipc.log('got a message from world : ', data,data.toString()); + } + ); + } +); diff --git a/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js new file mode 100644 index 000000000..c3ed83c9a --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/example/unixWindowsSocket/rawBuffer/world.server.js @@ -0,0 +1,40 @@ +const ipc=require('../../../node-ipc'); + +/***************************************\ + * + * You should start both hello and world + * then you will see them communicating. + * + * *************************************/ + +ipc.config.id = 'world'; +ipc.config.retry= 1500; +ipc.config.rawBuffer=true; +ipc.config.encoding='ascii'; + +ipc.serve( + function(){ + ipc.server.on( + 'connect', + function(socket){ + ipc.server.emit( + socket, + 'hello' + ); + } + ); + + ipc.server.on( + 'data', + function(data,socket){ + ipc.log('got a message', data,data.toString()); + ipc.server.emit( + socket, + 'goodbye' + ); + } + ); + } +); + +ipc.server.start(); diff --git a/WebSocketsServer/node_modules/node-ipc/node-ipc.js b/WebSocketsServer/node_modules/node-ipc/node-ipc.js new file mode 100644 index 000000000..1c6336061 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/node-ipc.js @@ -0,0 +1,21 @@ +'use strict'; + +const IPC = require('./services/IPC.js'); + +class IPCModule extends IPC{ + constructor(){ + super(); + //include IPC to make extensible + Object.defineProperty( + this, + 'IPC', + { + enumerable:true, + writable:false, + value:IPC + } + ) + } +} + +module.exports=new IPCModule; diff --git a/WebSocketsServer/node_modules/node-ipc/package.json b/WebSocketsServer/node_modules/node-ipc/package.json new file mode 100644 index 000000000..ba49befef --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/package.json @@ -0,0 +1,92 @@ +{ + "_from": "node-ipc", + "_id": "node-ipc@9.1.1", + "_inBundle": false, + "_integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "_location": "/node-ipc", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "node-ipc", + "name": "node-ipc", + "escapedName": "node-ipc", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "_shasum": "4e245ed6938e65100e595ebc5dc34b16e8dd5d69", + "_spec": "node-ipc", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSockets-Server", + "author": { + "name": "Brandon Nozaki Miller" + }, + "bugs": { + "url": "https://github.com/RIAEvangelist/node-ipc/issues" + }, + "bundleDependencies": false, + "dependencies": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + }, + "deprecated": false, + "description": "A nodejs module for local and remote Inter Process Communication (IPC), Neural Networking, and able to facilitate machine learning.", + "devDependencies": { + "codacy-coverage": "2.0.0", + "istanbul": "0.4.1", + "jasmine": "2.4.1", + "node-cmd": "2.0.0" + }, + "directories": { + "example": "example" + }, + "engines": { + "node": ">=4.0.0" + }, + "homepage": "http://riaevangelist.github.io/node-ipc/", + "keywords": [ + "IPC", + "Neural Networking", + "Machine Learning", + "inter", + "process", + "communication", + "unix", + "windows", + "win", + "socket", + "TCP", + "UDP", + "domain", + "sockets", + "threaded", + "communication", + "multi", + "process", + "shared", + "memory" + ], + "license": "DBAD", + "main": "node-ipc.js", + "name": "node-ipc", + "pre-commit": [ + "cover" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RIAEvangelist/node-ipc.git" + }, + "scripts": { + "cover": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "coverup": "cat ./spec/coverage/lcov.info | codacy-coverage", + "test": "istanbul cover -x **/spec/** -dir ./spec/coverage jasmine", + "test-windows": "istanbul cover -x **/spec/** -dir ./spec/coverage ./node_modules/jasmine/bin/jasmine.js" + }, + "version": "9.1.1" +} diff --git a/WebSocketsServer/node_modules/node-ipc/services/IPC.js b/WebSocketsServer/node_modules/node-ipc/services/IPC.js new file mode 100644 index 000000000..3a6edc7f5 --- /dev/null +++ b/WebSocketsServer/node_modules/node-ipc/services/IPC.js @@ -0,0 +1,337 @@ +'use strict'; + +const Defaults = require('../entities/Defaults.js'), + Client = require('../dao/client.js'), + Server = require('../dao/socketServer.js'), + util = require('util'); + +class IPC{ + constructor(){ + Object.defineProperties( + this, + { + config : { + enumerable:true, + writable:true, + value:new Defaults + }, + connectTo : { + enumerable:true, + writable:false, + value:connect + }, + connectToNet: { + enumerable:true, + writable:false, + value:connectNet + }, + disconnect : { + enumerable:true, + writable:false, + value:disconnect + }, + serve : { + enumerable:true, + writable:false, + value:serve + }, + serveNet : { + enumerable:true, + writable:false, + value:serveNet + }, + of : { + enumerable:true, + writable:true, + value:{} + }, + server : { + enumerable:true, + writable:true, + configurable:true, + value:false + }, + log : { + enumerable:true, + writable:false, + value:log + } + } + ); + } +} + +function log(...args){ + if(this.config.silent){ + return; + } + + for(let i=0, count=args.length; i {} +}; diff --git a/WebSocketsServer/node_modules/ws/lib/event-target.js b/WebSocketsServer/node_modules/ws/lib/event-target.js new file mode 100644 index 000000000..c060d63a8 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/event-target.js @@ -0,0 +1,170 @@ +'use strict'; + +/** + * Class representing an event. + * + * @private + */ +class Event { + /** + * Create a new `Event`. + * + * @param {String} type The name of the event + * @param {Object} target A reference to the target to which the event was dispatched + */ + constructor(type, target) { + this.target = target; + this.type = type; + } +} + +/** + * Class representing a message event. + * + * @extends Event + * @private + */ +class MessageEvent extends Event { + /** + * Create a new `MessageEvent`. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(data, target) { + super('message', target); + + this.data = data; + } +} + +/** + * Class representing a close event. + * + * @extends Event + * @private + */ +class CloseEvent extends Event { + /** + * Create a new `CloseEvent`. + * + * @param {Number} code The status code explaining why the connection is being closed + * @param {String} reason A human-readable string explaining why the connection is closing + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(code, reason, target) { + super('close', target); + + this.wasClean = target._closeFrameReceived && target._closeFrameSent; + this.reason = reason; + this.code = code; + } +} + +/** + * Class representing an open event. + * + * @extends Event + * @private + */ +class OpenEvent extends Event { + /** + * Create a new `OpenEvent`. + * + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(target) { + super('open', target); + } +} + +/** + * Class representing an error event. + * + * @extends Event + * @private + */ +class ErrorEvent extends Event { + /** + * Create a new `ErrorEvent`. + * + * @param {Object} error The error that generated this event + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(error, target) { + super('error', target); + + this.message = error.message; + this.error = error; + } +} + +/** + * This provides methods for emulating the `EventTarget` interface. It's not + * meant to be used directly. + * + * @mixin + */ +const EventTarget = { + /** + * Register an event listener. + * + * @param {String} method A string representing the event type to listen for + * @param {Function} listener The listener to add + * @public + */ + addEventListener(method, listener) { + if (typeof listener !== 'function') return; + + function onMessage(data) { + listener.call(this, new MessageEvent(data, this)); + } + + function onClose(code, message) { + listener.call(this, new CloseEvent(code, message, this)); + } + + function onError(error) { + listener.call(this, new ErrorEvent(error, this)); + } + + function onOpen() { + listener.call(this, new OpenEvent(this)); + } + + if (method === 'message') { + onMessage._listener = listener; + this.on(method, onMessage); + } else if (method === 'close') { + onClose._listener = listener; + this.on(method, onClose); + } else if (method === 'error') { + onError._listener = listener; + this.on(method, onError); + } else if (method === 'open') { + onOpen._listener = listener; + this.on(method, onOpen); + } else { + this.on(method, listener); + } + }, + + /** + * Remove an event listener. + * + * @param {String} method A string representing the event type to remove + * @param {Function} listener The listener to remove + * @public + */ + removeEventListener(method, listener) { + const listeners = this.listeners(method); + + for (let i = 0; i < listeners.length; i++) { + if (listeners[i] === listener || listeners[i]._listener === listener) { + this.removeListener(method, listeners[i]); + } + } + } +}; + +module.exports = EventTarget; diff --git a/WebSocketsServer/node_modules/ws/lib/extension.js b/WebSocketsServer/node_modules/ws/lib/extension.js new file mode 100644 index 000000000..87a421329 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/extension.js @@ -0,0 +1,223 @@ +'use strict'; + +// +// Allowed token characters: +// +// '!', '#', '$', '%', '&', ''', '*', '+', '-', +// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~' +// +// tokenChars[32] === 0 // ' ' +// tokenChars[33] === 1 // '!' +// tokenChars[34] === 0 // '"' +// ... +// +// prettier-ignore +const tokenChars = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127 +]; + +/** + * Adds an offer to the map of extension offers or a parameter to the map of + * parameters. + * + * @param {Object} dest The map of extension offers or parameters + * @param {String} name The extension or parameter name + * @param {(Object|Boolean|String)} elem The extension parameters or the + * parameter value + * @private + */ +function push(dest, name, elem) { + if (dest[name] === undefined) dest[name] = [elem]; + else dest[name].push(elem); +} + +/** + * Parses the `Sec-WebSocket-Extensions` header into an object. + * + * @param {String} header The field value of the header + * @return {Object} The parsed object + * @public + */ +function parse(header) { + const offers = Object.create(null); + + if (header === undefined || header === '') return offers; + + let params = Object.create(null); + let mustUnescape = false; + let isEscaping = false; + let inQuotes = false; + let extensionName; + let paramName; + let start = -1; + let end = -1; + let i = 0; + + for (; i < header.length; i++) { + const code = header.charCodeAt(i); + + if (extensionName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + const name = header.slice(start, end); + if (code === 0x2c) { + push(offers, name, params); + params = Object.create(null); + } else { + extensionName = name; + } + + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (paramName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 || code === 0x09) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + push(params, header.slice(start, end), true); + if (code === 0x2c) { + push(offers, extensionName, params); + params = Object.create(null); + extensionName = undefined; + } + + start = end = -1; + } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) { + paramName = header.slice(start, i); + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else { + // + // The value of a quoted-string after unescaping must conform to the + // token ABNF, so only token characters are valid. + // Ref: https://tools.ietf.org/html/rfc6455#section-9.1 + // + if (isEscaping) { + if (tokenChars[code] !== 1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (start === -1) start = i; + else if (!mustUnescape) mustUnescape = true; + isEscaping = false; + } else if (inQuotes) { + if (tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x22 /* '"' */ && start !== -1) { + inQuotes = false; + end = i; + } else if (code === 0x5c /* '\' */) { + isEscaping = true; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) { + inQuotes = true; + } else if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (start !== -1 && (code === 0x20 || code === 0x09)) { + if (end === -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + let value = header.slice(start, end); + if (mustUnescape) { + value = value.replace(/\\/g, ''); + mustUnescape = false; + } + push(params, paramName, value); + if (code === 0x2c) { + push(offers, extensionName, params); + params = Object.create(null); + extensionName = undefined; + } + + paramName = undefined; + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } + } + + if (start === -1 || inQuotes) { + throw new SyntaxError('Unexpected end of input'); + } + + if (end === -1) end = i; + const token = header.slice(start, end); + if (extensionName === undefined) { + push(offers, token, params); + } else { + if (paramName === undefined) { + push(params, token, true); + } else if (mustUnescape) { + push(params, paramName, token.replace(/\\/g, '')); + } else { + push(params, paramName, token); + } + push(offers, extensionName, params); + } + + return offers; +} + +/** + * Builds the `Sec-WebSocket-Extensions` header field value. + * + * @param {Object} extensions The map of extensions and parameters to format + * @return {String} A string representing the given object + * @public + */ +function format(extensions) { + return Object.keys(extensions) + .map((extension) => { + let configurations = extensions[extension]; + if (!Array.isArray(configurations)) configurations = [configurations]; + return configurations + .map((params) => { + return [extension] + .concat( + Object.keys(params).map((k) => { + let values = params[k]; + if (!Array.isArray(values)) values = [values]; + return values + .map((v) => (v === true ? k : `${k}=${v}`)) + .join('; '); + }) + ) + .join('; '); + }) + .join(', '); + }) + .join(', '); +} + +module.exports = { format, parse }; diff --git a/WebSocketsServer/node_modules/ws/lib/permessage-deflate.js b/WebSocketsServer/node_modules/ws/lib/permessage-deflate.js new file mode 100644 index 000000000..ddb2aa669 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/permessage-deflate.js @@ -0,0 +1,518 @@ +'use strict'; + +const Limiter = require('async-limiter'); +const zlib = require('zlib'); + +const bufferUtil = require('./buffer-util'); +const { kStatusCode, NOOP } = require('./constants'); + +const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]); +const EMPTY_BLOCK = Buffer.from([0x00]); + +const kPerMessageDeflate = Symbol('permessage-deflate'); +const kTotalLength = Symbol('total-length'); +const kCallback = Symbol('callback'); +const kBuffers = Symbol('buffers'); +const kError = Symbol('error'); + +// +// We limit zlib concurrency, which prevents severe memory fragmentation +// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913 +// and https://github.com/websockets/ws/issues/1202 +// +// Intentionally global; it's the global thread pool that's an issue. +// +let zlibLimiter; + +/** + * permessage-deflate implementation. + */ +class PerMessageDeflate { + /** + * Creates a PerMessageDeflate instance. + * + * @param {Object} options Configuration options + * @param {Boolean} options.serverNoContextTakeover Request/accept disabling + * of server context takeover + * @param {Boolean} options.clientNoContextTakeover Advertise/acknowledge + * disabling of client context takeover + * @param {(Boolean|Number)} options.serverMaxWindowBits Request/confirm the + * use of a custom server window size + * @param {(Boolean|Number)} options.clientMaxWindowBits Advertise support + * for, or request, a custom client window size + * @param {Object} options.zlibDeflateOptions Options to pass to zlib on deflate + * @param {Object} options.zlibInflateOptions Options to pass to zlib on inflate + * @param {Number} options.threshold Size (in bytes) below which messages + * should not be compressed + * @param {Number} options.concurrencyLimit The number of concurrent calls to + * zlib + * @param {Boolean} isServer Create the instance in either server or client + * mode + * @param {Number} maxPayload The maximum allowed message length + */ + constructor(options, isServer, maxPayload) { + this._maxPayload = maxPayload | 0; + this._options = options || {}; + this._threshold = + this._options.threshold !== undefined ? this._options.threshold : 1024; + this._isServer = !!isServer; + this._deflate = null; + this._inflate = null; + + this.params = null; + + if (!zlibLimiter) { + const concurrency = + this._options.concurrencyLimit !== undefined + ? this._options.concurrencyLimit + : 10; + zlibLimiter = new Limiter({ concurrency }); + } + } + + /** + * @type {String} + */ + static get extensionName() { + return 'permessage-deflate'; + } + + /** + * Create an extension negotiation offer. + * + * @return {Object} Extension parameters + * @public + */ + offer() { + const params = {}; + + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true; + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits; + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true; + } + + return params; + } + + /** + * Accept an extension negotiation offer/response. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Object} Accepted configuration + * @public + */ + accept(configurations) { + configurations = this.normalizeParams(configurations); + + this.params = this._isServer + ? this.acceptAsServer(configurations) + : this.acceptAsClient(configurations); + + return this.params; + } + + /** + * Releases all resources used by the extension. + * + * @public + */ + cleanup() { + if (this._inflate) { + this._inflate.close(); + this._inflate = null; + } + + if (this._deflate) { + if (this._deflate[kCallback]) { + this._deflate[kCallback](); + } + + this._deflate.close(); + this._deflate = null; + } + } + + /** + * Accept an extension negotiation offer. + * + * @param {Array} offers The extension negotiation offers + * @return {Object} Accepted configuration + * @private + */ + acceptAsServer(offers) { + const opts = this._options; + const accepted = offers.find((params) => { + if ( + (opts.serverNoContextTakeover === false && + params.server_no_context_takeover) || + (params.server_max_window_bits && + (opts.serverMaxWindowBits === false || + (typeof opts.serverMaxWindowBits === 'number' && + opts.serverMaxWindowBits > params.server_max_window_bits))) || + (typeof opts.clientMaxWindowBits === 'number' && + !params.client_max_window_bits) + ) { + return false; + } + + return true; + }); + + if (!accepted) { + throw new Error('None of the extension offers can be accepted'); + } + + if (opts.serverNoContextTakeover) { + accepted.server_no_context_takeover = true; + } + if (opts.clientNoContextTakeover) { + accepted.client_no_context_takeover = true; + } + if (typeof opts.serverMaxWindowBits === 'number') { + accepted.server_max_window_bits = opts.serverMaxWindowBits; + } + if (typeof opts.clientMaxWindowBits === 'number') { + accepted.client_max_window_bits = opts.clientMaxWindowBits; + } else if ( + accepted.client_max_window_bits === true || + opts.clientMaxWindowBits === false + ) { + delete accepted.client_max_window_bits; + } + + return accepted; + } + + /** + * Accept the extension negotiation response. + * + * @param {Array} response The extension negotiation response + * @return {Object} Accepted configuration + * @private + */ + acceptAsClient(response) { + const params = response[0]; + + if ( + this._options.clientNoContextTakeover === false && + params.client_no_context_takeover + ) { + throw new Error('Unexpected parameter "client_no_context_takeover"'); + } + + if (!params.client_max_window_bits) { + if (typeof this._options.clientMaxWindowBits === 'number') { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } + } else if ( + this._options.clientMaxWindowBits === false || + (typeof this._options.clientMaxWindowBits === 'number' && + params.client_max_window_bits > this._options.clientMaxWindowBits) + ) { + throw new Error( + 'Unexpected or invalid parameter "client_max_window_bits"' + ); + } + + return params; + } + + /** + * Normalize parameters. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Array} The offers/response with normalized parameters + * @private + */ + normalizeParams(configurations) { + configurations.forEach((params) => { + Object.keys(params).forEach((key) => { + let value = params[key]; + + if (value.length > 1) { + throw new Error(`Parameter "${key}" must have only a single value`); + } + + value = value[0]; + + if (key === 'client_max_window_bits') { + if (value !== true) { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if (!this._isServer) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else if (key === 'server_max_window_bits') { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if ( + key === 'client_no_context_takeover' || + key === 'server_no_context_takeover' + ) { + if (value !== true) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else { + throw new Error(`Unknown parameter "${key}"`); + } + + params[key] = value; + }); + }); + + return configurations; + } + + /** + * Decompress data. Concurrency limited by async-limiter. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + decompress(data, fin, callback) { + zlibLimiter.push((done) => { + this._decompress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Compress data. Concurrency limited by async-limiter. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + compress(data, fin, callback) { + zlibLimiter.push((done) => { + this._compress(data, fin, (err, result) => { + done(); + if (err || result) { + callback(err, result); + } + }); + }); + } + + /** + * Decompress data. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _decompress(data, fin, callback) { + const endpoint = this._isServer ? 'client' : 'server'; + + if (!this._inflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._inflate = zlib.createInflateRaw({ + ...this._options.zlibInflateOptions, + windowBits + }); + this._inflate[kPerMessageDeflate] = this; + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + this._inflate.on('error', inflateOnError); + this._inflate.on('data', inflateOnData); + } + + this._inflate[kCallback] = callback; + + this._inflate.write(data); + if (fin) this._inflate.write(TRAILER); + + this._inflate.flush(() => { + const err = this._inflate[kError]; + + if (err) { + this._inflate.close(); + this._inflate = null; + callback(err); + return; + } + + const data = bufferUtil.concat( + this._inflate[kBuffers], + this._inflate[kTotalLength] + ); + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._inflate.close(); + this._inflate = null; + } else { + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + } + + callback(null, data); + }); + } + + /** + * Compress data. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _compress(data, fin, callback) { + if (!data || data.length === 0) { + process.nextTick(callback, null, EMPTY_BLOCK); + return; + } + + const endpoint = this._isServer ? 'server' : 'client'; + + if (!this._deflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._deflate = zlib.createDeflateRaw({ + ...this._options.zlibDeflateOptions, + windowBits + }); + + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + + // + // An `'error'` event is emitted, only on Node.js < 10.0.0, if the + // `zlib.DeflateRaw` instance is closed while data is being processed. + // This can happen if `PerMessageDeflate#cleanup()` is called at the wrong + // time due to an abnormal WebSocket closure. + // + this._deflate.on('error', NOOP); + this._deflate.on('data', deflateOnData); + } + + this._deflate[kCallback] = callback; + + this._deflate.write(data); + this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { + if (!this._deflate) { + // + // This `if` statement is only needed for Node.js < 10.0.0 because as of + // commit https://github.com/nodejs/node/commit/5e3f5164, the flush + // callback is no longer called if the deflate stream is closed while + // data is being processed. + // + return; + } + + let data = bufferUtil.concat( + this._deflate[kBuffers], + this._deflate[kTotalLength] + ); + + if (fin) data = data.slice(0, data.length - 4); + + // + // Ensure that the callback will not be called again in + // `PerMessageDeflate#cleanup()`. + // + this._deflate[kCallback] = null; + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._deflate.close(); + this._deflate = null; + } else { + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + } + + callback(null, data); + }); + } +} + +module.exports = PerMessageDeflate; + +/** + * The listener of the `zlib.DeflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function deflateOnData(chunk) { + this[kBuffers].push(chunk); + this[kTotalLength] += chunk.length; +} + +/** + * The listener of the `zlib.InflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function inflateOnData(chunk) { + this[kTotalLength] += chunk.length; + + if ( + this[kPerMessageDeflate]._maxPayload < 1 || + this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload + ) { + this[kBuffers].push(chunk); + return; + } + + this[kError] = new RangeError('Max payload size exceeded'); + this[kError][kStatusCode] = 1009; + this.removeListener('data', inflateOnData); + this.reset(); +} + +/** + * The listener of the `zlib.InflateRaw` stream `'error'` event. + * + * @param {Error} err The emitted error + * @private + */ +function inflateOnError(err) { + // + // There is no need to call `Zlib#close()` as the handle is automatically + // closed when an error is emitted. + // + this[kPerMessageDeflate]._inflate = null; + err[kStatusCode] = 1007; + this[kCallback](err); +} diff --git a/WebSocketsServer/node_modules/ws/lib/receiver.js b/WebSocketsServer/node_modules/ws/lib/receiver.js new file mode 100644 index 000000000..28ffca0f5 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/receiver.js @@ -0,0 +1,492 @@ +'use strict'; + +const { Writable } = require('stream'); + +const PerMessageDeflate = require('./permessage-deflate'); +const { + BINARY_TYPES, + EMPTY_BUFFER, + kStatusCode, + kWebSocket +} = require('./constants'); +const { concat, toArrayBuffer, unmask } = require('./buffer-util'); +const { isValidStatusCode, isValidUTF8 } = require('./validation'); + +const GET_INFO = 0; +const GET_PAYLOAD_LENGTH_16 = 1; +const GET_PAYLOAD_LENGTH_64 = 2; +const GET_MASK = 3; +const GET_DATA = 4; +const INFLATING = 5; + +/** + * HyBi Receiver implementation. + * + * @extends stream.Writable + */ +class Receiver extends Writable { + /** + * Creates a Receiver instance. + * + * @param {String} binaryType The type for binary data + * @param {Object} extensions An object containing the negotiated extensions + * @param {Number} maxPayload The maximum allowed message length + */ + constructor(binaryType, extensions, maxPayload) { + super(); + + this._binaryType = binaryType || BINARY_TYPES[0]; + this[kWebSocket] = undefined; + this._extensions = extensions || {}; + this._maxPayload = maxPayload | 0; + + this._bufferedBytes = 0; + this._buffers = []; + + this._compressed = false; + this._payloadLength = 0; + this._mask = undefined; + this._fragmented = 0; + this._masked = false; + this._fin = false; + this._opcode = 0; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragments = []; + + this._state = GET_INFO; + this._loop = false; + } + + /** + * Implements `Writable.prototype._write()`. + * + * @param {Buffer} chunk The chunk of data to write + * @param {String} encoding The character encoding of `chunk` + * @param {Function} cb Callback + */ + _write(chunk, encoding, cb) { + if (this._opcode === 0x08 && this._state == GET_INFO) return cb(); + + this._bufferedBytes += chunk.length; + this._buffers.push(chunk); + this.startLoop(cb); + } + + /** + * Consumes `n` bytes from the buffered data. + * + * @param {Number} n The number of bytes to consume + * @return {Buffer} The consumed bytes + * @private + */ + consume(n) { + this._bufferedBytes -= n; + + if (n === this._buffers[0].length) return this._buffers.shift(); + + if (n < this._buffers[0].length) { + const buf = this._buffers[0]; + this._buffers[0] = buf.slice(n); + return buf.slice(0, n); + } + + const dst = Buffer.allocUnsafe(n); + + do { + const buf = this._buffers[0]; + + if (n >= buf.length) { + this._buffers.shift().copy(dst, dst.length - n); + } else { + buf.copy(dst, dst.length - n, 0, n); + this._buffers[0] = buf.slice(n); + } + + n -= buf.length; + } while (n > 0); + + return dst; + } + + /** + * Starts the parsing loop. + * + * @param {Function} cb Callback + * @private + */ + startLoop(cb) { + let err; + this._loop = true; + + do { + switch (this._state) { + case GET_INFO: + err = this.getInfo(); + break; + case GET_PAYLOAD_LENGTH_16: + err = this.getPayloadLength16(); + break; + case GET_PAYLOAD_LENGTH_64: + err = this.getPayloadLength64(); + break; + case GET_MASK: + this.getMask(); + break; + case GET_DATA: + err = this.getData(cb); + break; + default: + // `INFLATING` + this._loop = false; + return; + } + } while (this._loop); + + cb(err); + } + + /** + * Reads the first two bytes of a frame. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getInfo() { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + const buf = this.consume(2); + + if ((buf[0] & 0x30) !== 0x00) { + this._loop = false; + return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002); + } + + const compressed = (buf[0] & 0x40) === 0x40; + + if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + this._fin = (buf[0] & 0x80) === 0x80; + this._opcode = buf[0] & 0x0f; + this._payloadLength = buf[1] & 0x7f; + + if (this._opcode === 0x00) { + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (!this._fragmented) { + this._loop = false; + return error(RangeError, 'invalid opcode 0', true, 1002); + } + + this._opcode = this._fragmented; + } else if (this._opcode === 0x01 || this._opcode === 0x02) { + if (this._fragmented) { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + this._compressed = compressed; + } else if (this._opcode > 0x07 && this._opcode < 0x0b) { + if (!this._fin) { + this._loop = false; + return error(RangeError, 'FIN must be set', true, 1002); + } + + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (this._payloadLength > 0x7d) { + this._loop = false; + return error( + RangeError, + `invalid payload length ${this._payloadLength}`, + true, + 1002 + ); + } + } else { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + if (!this._fin && !this._fragmented) this._fragmented = this._opcode; + this._masked = (buf[1] & 0x80) === 0x80; + + if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; + else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; + else return this.haveLength(); + } + + /** + * Gets extended payload length (7+16). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength16() { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + this._payloadLength = this.consume(2).readUInt16BE(0); + return this.haveLength(); + } + + /** + * Gets extended payload length (7+64). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength64() { + if (this._bufferedBytes < 8) { + this._loop = false; + return; + } + + const buf = this.consume(8); + const num = buf.readUInt32BE(0); + + // + // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned + // if payload length is greater than this number. + // + if (num > Math.pow(2, 53 - 32) - 1) { + this._loop = false; + return error( + RangeError, + 'Unsupported WebSocket frame: payload length > 2^53 - 1', + false, + 1009 + ); + } + + this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); + return this.haveLength(); + } + + /** + * Payload length has been read. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + haveLength() { + if (this._payloadLength && this._opcode < 0x08) { + this._totalPayloadLength += this._payloadLength; + if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { + this._loop = false; + return error(RangeError, 'Max payload size exceeded', false, 1009); + } + } + + if (this._masked) this._state = GET_MASK; + else this._state = GET_DATA; + } + + /** + * Reads mask bytes. + * + * @private + */ + getMask() { + if (this._bufferedBytes < 4) { + this._loop = false; + return; + } + + this._mask = this.consume(4); + this._state = GET_DATA; + } + + /** + * Reads data bytes. + * + * @param {Function} cb Callback + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + getData(cb) { + let data = EMPTY_BUFFER; + + if (this._payloadLength) { + if (this._bufferedBytes < this._payloadLength) { + this._loop = false; + return; + } + + data = this.consume(this._payloadLength); + if (this._masked) unmask(data, this._mask); + } + + if (this._opcode > 0x07) return this.controlMessage(data); + + if (this._compressed) { + this._state = INFLATING; + this.decompress(data, cb); + return; + } + + if (data.length) { + // + // This message is not compressed so its lenght is the sum of the payload + // length of all fragments. + // + this._messageLength = this._totalPayloadLength; + this._fragments.push(data); + } + + return this.dataMessage(); + } + + /** + * Decompresses data. + * + * @param {Buffer} data Compressed data + * @param {Function} cb Callback + * @private + */ + decompress(data, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + perMessageDeflate.decompress(data, this._fin, (err, buf) => { + if (err) return cb(err); + + if (buf.length) { + this._messageLength += buf.length; + if (this._messageLength > this._maxPayload && this._maxPayload > 0) { + return cb( + error(RangeError, 'Max payload size exceeded', false, 1009) + ); + } + + this._fragments.push(buf); + } + + const er = this.dataMessage(); + if (er) return cb(er); + + this.startLoop(cb); + }); + } + + /** + * Handles a data message. + * + * @return {(Error|undefined)} A possible error + * @private + */ + dataMessage() { + if (this._fin) { + const messageLength = this._messageLength; + const fragments = this._fragments; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragmented = 0; + this._fragments = []; + + if (this._opcode === 2) { + let data; + + if (this._binaryType === 'nodebuffer') { + data = concat(fragments, messageLength); + } else if (this._binaryType === 'arraybuffer') { + data = toArrayBuffer(concat(fragments, messageLength)); + } else { + data = fragments; + } + + this.emit('message', data); + } else { + const buf = concat(fragments, messageLength); + + if (!isValidUTF8(buf)) { + this._loop = false; + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('message', buf.toString()); + } + } + + this._state = GET_INFO; + } + + /** + * Handles a control message. + * + * @param {Buffer} data Data to handle + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + controlMessage(data) { + if (this._opcode === 0x08) { + this._loop = false; + + if (data.length === 0) { + this.emit('conclude', 1005, ''); + this.end(); + } else if (data.length === 1) { + return error(RangeError, 'invalid payload length 1', true, 1002); + } else { + const code = data.readUInt16BE(0); + + if (!isValidStatusCode(code)) { + return error(RangeError, `invalid status code ${code}`, true, 1002); + } + + const buf = data.slice(2); + + if (!isValidUTF8(buf)) { + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('conclude', code, buf.toString()); + this.end(); + } + } else if (this._opcode === 0x09) { + this.emit('ping', data); + } else { + this.emit('pong', data); + } + + this._state = GET_INFO; + } +} + +module.exports = Receiver; + +/** + * Builds an error object. + * + * @param {(Error|RangeError)} ErrorCtor The error constructor + * @param {String} message The error message + * @param {Boolean} prefix Specifies whether or not to add a default prefix to + * `message` + * @param {Number} statusCode The status code + * @return {(Error|RangeError)} The error + * @private + */ +function error(ErrorCtor, message, prefix, statusCode) { + const err = new ErrorCtor( + prefix ? `Invalid WebSocket frame: ${message}` : message + ); + + Error.captureStackTrace(err, error); + err[kStatusCode] = statusCode; + return err; +} diff --git a/WebSocketsServer/node_modules/ws/lib/sender.js b/WebSocketsServer/node_modules/ws/lib/sender.js new file mode 100644 index 000000000..a9bcef201 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/sender.js @@ -0,0 +1,360 @@ +'use strict'; + +const { randomFillSync } = require('crypto'); + +const PerMessageDeflate = require('./permessage-deflate'); +const { EMPTY_BUFFER } = require('./constants'); +const { isValidStatusCode } = require('./validation'); +const { mask: applyMask, toBuffer } = require('./buffer-util'); + +const mask = Buffer.alloc(4); + +/** + * HyBi Sender implementation. + */ +class Sender { + /** + * Creates a Sender instance. + * + * @param {net.Socket} socket The connection socket + * @param {Object} extensions An object containing the negotiated extensions + */ + constructor(socket, extensions) { + this._extensions = extensions || {}; + this._socket = socket; + + this._firstFragment = true; + this._compress = false; + + this._bufferedBytes = 0; + this._deflating = false; + this._queue = []; + } + + /** + * Frames a piece of data according to the HyBi WebSocket protocol. + * + * @param {Buffer} data The data to frame + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @return {Buffer[]} The framed data as a list of `Buffer` instances + * @public + */ + static frame(data, options) { + const merge = options.mask && options.readOnly; + let offset = options.mask ? 6 : 2; + let payloadLength = data.length; + + if (data.length >= 65536) { + offset += 8; + payloadLength = 127; + } else if (data.length > 125) { + offset += 2; + payloadLength = 126; + } + + const target = Buffer.allocUnsafe(merge ? data.length + offset : offset); + + target[0] = options.fin ? options.opcode | 0x80 : options.opcode; + if (options.rsv1) target[0] |= 0x40; + + target[1] = payloadLength; + + if (payloadLength === 126) { + target.writeUInt16BE(data.length, 2); + } else if (payloadLength === 127) { + target.writeUInt32BE(0, 2); + target.writeUInt32BE(data.length, 6); + } + + if (!options.mask) return [target, data]; + + randomFillSync(mask, 0, 4); + + target[1] |= 0x80; + target[offset - 4] = mask[0]; + target[offset - 3] = mask[1]; + target[offset - 2] = mask[2]; + target[offset - 1] = mask[3]; + + if (merge) { + applyMask(data, mask, target, offset, data.length); + return [target]; + } + + applyMask(data, mask, data, 0, data.length); + return [target, data]; + } + + /** + * Sends a close message to the other peer. + * + * @param {(Number|undefined)} code The status code component of the body + * @param {String} data The message component of the body + * @param {Boolean} mask Specifies whether or not to mask the message + * @param {Function} cb Callback + * @public + */ + close(code, data, mask, cb) { + let buf; + + if (code === undefined) { + buf = EMPTY_BUFFER; + } else if (typeof code !== 'number' || !isValidStatusCode(code)) { + throw new TypeError('First argument must be a valid error code number'); + } else if (data === undefined || data === '') { + buf = Buffer.allocUnsafe(2); + buf.writeUInt16BE(code, 0); + } else { + buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data)); + buf.writeUInt16BE(code, 0); + buf.write(data, 2); + } + + if (this._deflating) { + this.enqueue([this.doClose, buf, mask, cb]); + } else { + this.doClose(buf, mask, cb); + } + } + + /** + * Frames and sends a close message. + * + * @param {Buffer} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @private + */ + doClose(data, mask, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x08, + mask, + readOnly: false + }), + cb + ); + } + + /** + * Sends a ping message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + ping(data, mask, cb) { + const buf = toBuffer(data); + + if (this._deflating) { + this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]); + } else { + this.doPing(buf, mask, toBuffer.readOnly, cb); + } + } + + /** + * Frames and sends a ping message. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPing(data, mask, readOnly, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x09, + mask, + readOnly + }), + cb + ); + } + + /** + * Sends a pong message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + pong(data, mask, cb) { + const buf = toBuffer(data); + + if (this._deflating) { + this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]); + } else { + this.doPong(buf, mask, toBuffer.readOnly, cb); + } + } + + /** + * Frames and sends a pong message. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPong(data, mask, readOnly, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x0a, + mask, + readOnly + }), + cb + ); + } + + /** + * Sends a data message to the other peer. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + send(data, options, cb) { + const buf = toBuffer(data); + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + let opcode = options.binary ? 2 : 1; + let rsv1 = options.compress; + + if (this._firstFragment) { + this._firstFragment = false; + if (rsv1 && perMessageDeflate) { + rsv1 = buf.length >= perMessageDeflate._threshold; + } + this._compress = rsv1; + } else { + rsv1 = false; + opcode = 0; + } + + if (options.fin) this._firstFragment = true; + + if (perMessageDeflate) { + const opts = { + fin: options.fin, + rsv1, + opcode, + mask: options.mask, + readOnly: toBuffer.readOnly + }; + + if (this._deflating) { + this.enqueue([this.dispatch, buf, this._compress, opts, cb]); + } else { + this.dispatch(buf, this._compress, opts, cb); + } + } else { + this.sendFrame( + Sender.frame(buf, { + fin: options.fin, + rsv1: false, + opcode, + mask: options.mask, + readOnly: toBuffer.readOnly + }), + cb + ); + } + } + + /** + * Dispatches a data message. + * + * @param {Buffer} data The message to send + * @param {Boolean} compress Specifies whether or not to compress `data` + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @param {Function} cb Callback + * @private + */ + dispatch(data, compress, options, cb) { + if (!compress) { + this.sendFrame(Sender.frame(data, options), cb); + return; + } + + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + this._deflating = true; + perMessageDeflate.compress(data, options.fin, (_, buf) => { + this._deflating = false; + options.readOnly = false; + this.sendFrame(Sender.frame(buf, options), cb); + this.dequeue(); + }); + } + + /** + * Executes queued send operations. + * + * @private + */ + dequeue() { + while (!this._deflating && this._queue.length) { + const params = this._queue.shift(); + + this._bufferedBytes -= params[1].length; + Reflect.apply(params[0], this, params.slice(1)); + } + } + + /** + * Enqueues a send operation. + * + * @param {Array} params Send operation parameters. + * @private + */ + enqueue(params) { + this._bufferedBytes += params[1].length; + this._queue.push(params); + } + + /** + * Sends a frame. + * + * @param {Buffer[]} list The frame to send + * @param {Function} cb Callback + * @private + */ + sendFrame(list, cb) { + if (list.length === 2) { + this._socket.cork(); + this._socket.write(list[0]); + this._socket.write(list[1], cb); + this._socket.uncork(); + } else { + this._socket.write(list[0], cb); + } + } +} + +module.exports = Sender; diff --git a/WebSocketsServer/node_modules/ws/lib/stream.js b/WebSocketsServer/node_modules/ws/lib/stream.js new file mode 100644 index 000000000..adfcf3193 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/stream.js @@ -0,0 +1,150 @@ +'use strict'; + +const { Duplex } = require('stream'); + +/** + * Emits the `'close'` event on a stream. + * + * @param {stream.Duplex} The stream. + * @private + */ +function emitClose(stream) { + stream.emit('close'); +} + +/** + * The listener of the `'end'` event. + * + * @private + */ +function duplexOnEnd() { + if (!this.destroyed && this._writableState.finished) { + this.destroy(); + } +} + +/** + * The listener of the `'error'` event. + * + * @private + */ +function duplexOnError(err) { + this.removeListener('error', duplexOnError); + this.destroy(); + if (this.listenerCount('error') === 0) { + // Do not suppress the throwing behavior. + this.emit('error', err); + } +} + +/** + * Wraps a `WebSocket` in a duplex stream. + * + * @param {WebSocket} ws The `WebSocket` to wrap + * @param {Object} options The options for the `Duplex` constructor + * @return {stream.Duplex} The duplex stream + * @public + */ +function createWebSocketStream(ws, options) { + let resumeOnReceiverDrain = true; + + function receiverOnDrain() { + if (resumeOnReceiverDrain) ws._socket.resume(); + } + + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + ws._receiver.removeAllListeners('drain'); + ws._receiver.on('drain', receiverOnDrain); + }); + } else { + ws._receiver.removeAllListeners('drain'); + ws._receiver.on('drain', receiverOnDrain); + } + + const duplex = new Duplex({ + ...options, + autoDestroy: false, + emitClose: false, + objectMode: false, + readableObjectMode: false, + writableObjectMode: false + }); + + ws.on('message', function message(msg) { + if (!duplex.push(msg)) { + resumeOnReceiverDrain = false; + ws._socket.pause(); + } + }); + + ws.once('error', function error(err) { + duplex.destroy(err); + }); + + ws.once('close', function close() { + if (duplex.destroyed) return; + + duplex.push(null); + }); + + duplex._destroy = function(err, callback) { + if (ws.readyState === ws.CLOSED) { + callback(err); + process.nextTick(emitClose, duplex); + return; + } + + ws.once('close', function close() { + callback(err); + process.nextTick(emitClose, duplex); + }); + ws.terminate(); + }; + + duplex._final = function(callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + duplex._final(callback); + }); + return; + } + + if (ws._socket._writableState.finished) { + if (duplex._readableState.endEmitted) duplex.destroy(); + callback(); + } else { + ws._socket.once('finish', function finish() { + // `duplex` is not destroyed here because the `'end'` event will be + // emitted on `duplex` after this `'finish'` event. The EOF signaling + // `null` chunk is, in fact, pushed when the WebSocket emits `'close'`. + callback(); + }); + ws.close(); + } + }; + + duplex._read = function() { + if (ws.readyState === ws.OPEN && !resumeOnReceiverDrain) { + resumeOnReceiverDrain = true; + if (!ws._receiver._writableState.needDrain) ws._socket.resume(); + } + }; + + duplex._write = function(chunk, encoding, callback) { + if (ws.readyState === ws.CONNECTING) { + ws.once('open', function open() { + duplex._write(chunk, encoding, callback); + }); + return; + } + + ws.send(chunk, callback); + }; + + duplex.on('end', duplexOnEnd); + duplex.on('error', duplexOnError); + return duplex; +} + +module.exports = createWebSocketStream; diff --git a/WebSocketsServer/node_modules/ws/lib/validation.js b/WebSocketsServer/node_modules/ws/lib/validation.js new file mode 100644 index 000000000..479a7db08 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/validation.js @@ -0,0 +1,30 @@ +'use strict'; + +try { + const isValidUTF8 = require('utf-8-validate'); + + exports.isValidUTF8 = + typeof isValidUTF8 === 'object' + ? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0 + : isValidUTF8; +} catch (e) /* istanbul ignore next */ { + exports.isValidUTF8 = () => true; +} + +/** + * Checks if a status code is allowed in a close frame. + * + * @param {Number} code The status code + * @return {Boolean} `true` if the status code is valid, else `false` + * @public + */ +exports.isValidStatusCode = (code) => { + return ( + (code >= 1000 && + code <= 1013 && + code !== 1004 && + code !== 1005 && + code !== 1006) || + (code >= 3000 && code <= 4999) + ); +}; diff --git a/WebSocketsServer/node_modules/ws/lib/websocket-server.js b/WebSocketsServer/node_modules/ws/lib/websocket-server.js new file mode 100644 index 000000000..057a12be5 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/websocket-server.js @@ -0,0 +1,406 @@ +'use strict'; + +const EventEmitter = require('events'); +const { createHash } = require('crypto'); +const { createServer, STATUS_CODES } = require('http'); + +const PerMessageDeflate = require('./permessage-deflate'); +const WebSocket = require('./websocket'); +const { format, parse } = require('./extension'); +const { GUID } = require('./constants'); + +const keyRegex = /^[+/0-9A-Za-z]{22}==$/; +const kUsedByWebSocketServer = Symbol('kUsedByWebSocketServer'); + +/** + * Class representing a WebSocket server. + * + * @extends EventEmitter + */ +class WebSocketServer extends EventEmitter { + /** + * Create a `WebSocketServer` instance. + * + * @param {Object} options Configuration options + * @param {Number} options.backlog The maximum length of the queue of pending + * connections + * @param {Boolean} options.clientTracking Specifies whether or not to track + * clients + * @param {Function} options.handleProtocols A hook to handle protocols + * @param {String} options.host The hostname where to bind the server + * @param {Number} options.maxPayload The maximum allowed message size + * @param {Boolean} options.noServer Enable no server mode + * @param {String} options.path Accept only connections matching this path + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable + * permessage-deflate + * @param {Number} options.port The port where to bind the server + * @param {http.Server} options.server A pre-created HTTP/S server to use + * @param {Function} options.verifyClient A hook to reject connections + * @param {Function} callback A listener for the `listening` event + */ + constructor(options, callback) { + super(); + + options = { + maxPayload: 100 * 1024 * 1024, + perMessageDeflate: false, + handleProtocols: null, + clientTracking: true, + verifyClient: null, + noServer: false, + backlog: null, // use default (511 as implemented in net.js) + server: null, + host: null, + path: null, + port: null, + ...options + }; + + if (options.port == null && !options.server && !options.noServer) { + throw new TypeError( + 'One of the "port", "server", or "noServer" options must be specified' + ); + } + + if (options.port != null) { + this._server = createServer((req, res) => { + const body = STATUS_CODES[426]; + + res.writeHead(426, { + 'Content-Length': body.length, + 'Content-Type': 'text/plain' + }); + res.end(body); + }); + this._server.listen( + options.port, + options.host, + options.backlog, + callback + ); + } else if (options.server) { + if (options.server[kUsedByWebSocketServer]) { + throw new Error( + 'The HTTP/S server is already being used by another WebSocket server' + ); + } + + options.server[kUsedByWebSocketServer] = true; + this._server = options.server; + } + + if (this._server) { + this._removeListeners = addListeners(this._server, { + listening: this.emit.bind(this, 'listening'), + error: this.emit.bind(this, 'error'), + upgrade: (req, socket, head) => { + this.handleUpgrade(req, socket, head, (ws) => { + this.emit('connection', ws, req); + }); + } + }); + } + + if (options.perMessageDeflate === true) options.perMessageDeflate = {}; + if (options.clientTracking) this.clients = new Set(); + this.options = options; + } + + /** + * Returns the bound address, the address family name, and port of the server + * as reported by the operating system if listening on an IP socket. + * If the server is listening on a pipe or UNIX domain socket, the name is + * returned as a string. + * + * @return {(Object|String|null)} The address of the server + * @public + */ + address() { + if (this.options.noServer) { + throw new Error('The server is operating in "noServer" mode'); + } + + if (!this._server) return null; + return this._server.address(); + } + + /** + * Close the server. + * + * @param {Function} cb Callback + * @public + */ + close(cb) { + if (cb) this.once('close', cb); + + // + // Terminate all associated clients. + // + if (this.clients) { + for (const client of this.clients) client.terminate(); + } + + const server = this._server; + + if (server) { + this._removeListeners(); + this._removeListeners = this._server = null; + + // + // Close the http server if it was internally created. + // + if (this.options.port != null) { + server.close(() => this.emit('close')); + return; + } + + delete server[kUsedByWebSocketServer]; + } + + process.nextTick(emitClose, this); + } + + /** + * See if a given request should be handled by this server instance. + * + * @param {http.IncomingMessage} req Request object to inspect + * @return {Boolean} `true` if the request is valid, else `false` + * @public + */ + shouldHandle(req) { + if (this.options.path) { + const index = req.url.indexOf('?'); + const pathname = index !== -1 ? req.url.slice(0, index) : req.url; + + if (pathname !== this.options.path) return false; + } + + return true; + } + + /** + * Handle a HTTP Upgrade request. + * + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @public + */ + handleUpgrade(req, socket, head, cb) { + socket.on('error', socketOnError); + + const key = + req.headers['sec-websocket-key'] !== undefined + ? req.headers['sec-websocket-key'].trim() + : false; + const version = +req.headers['sec-websocket-version']; + const extensions = {}; + + if ( + req.method !== 'GET' || + req.headers.upgrade.toLowerCase() !== 'websocket' || + !key || + !keyRegex.test(key) || + (version !== 8 && version !== 13) || + !this.shouldHandle(req) + ) { + return abortHandshake(socket, 400); + } + + if (this.options.perMessageDeflate) { + const perMessageDeflate = new PerMessageDeflate( + this.options.perMessageDeflate, + true, + this.options.maxPayload + ); + + try { + const offers = parse(req.headers['sec-websocket-extensions']); + + if (offers[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]); + extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + } catch (err) { + return abortHandshake(socket, 400); + } + } + + // + // Optionally call external client verification handler. + // + if (this.options.verifyClient) { + const info = { + origin: + req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`], + secure: !!(req.connection.authorized || req.connection.encrypted), + req + }; + + if (this.options.verifyClient.length === 2) { + this.options.verifyClient(info, (verified, code, message, headers) => { + if (!verified) { + return abortHandshake(socket, code || 401, message, headers); + } + + this.completeUpgrade(key, extensions, req, socket, head, cb); + }); + return; + } + + if (!this.options.verifyClient(info)) return abortHandshake(socket, 401); + } + + this.completeUpgrade(key, extensions, req, socket, head, cb); + } + + /** + * Upgrade the connection to WebSocket. + * + * @param {String} key The value of the `Sec-WebSocket-Key` header + * @param {Object} extensions The accepted extensions + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @private + */ + completeUpgrade(key, extensions, req, socket, head, cb) { + // + // Destroy the socket if the client has already sent a FIN packet. + // + if (!socket.readable || !socket.writable) return socket.destroy(); + + const digest = createHash('sha1') + .update(key + GUID) + .digest('base64'); + + const headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + `Sec-WebSocket-Accept: ${digest}` + ]; + + const ws = new WebSocket(null); + let protocol = req.headers['sec-websocket-protocol']; + + if (protocol) { + protocol = protocol.trim().split(/ *, */); + + // + // Optionally call external protocol selection handler. + // + if (this.options.handleProtocols) { + protocol = this.options.handleProtocols(protocol, req); + } else { + protocol = protocol[0]; + } + + if (protocol) { + headers.push(`Sec-WebSocket-Protocol: ${protocol}`); + ws.protocol = protocol; + } + } + + if (extensions[PerMessageDeflate.extensionName]) { + const params = extensions[PerMessageDeflate.extensionName].params; + const value = format({ + [PerMessageDeflate.extensionName]: [params] + }); + headers.push(`Sec-WebSocket-Extensions: ${value}`); + ws._extensions = extensions; + } + + // + // Allow external modification/inspection of handshake headers. + // + this.emit('headers', headers, req); + + socket.write(headers.concat('\r\n').join('\r\n')); + socket.removeListener('error', socketOnError); + + ws.setSocket(socket, head, this.options.maxPayload); + + if (this.clients) { + this.clients.add(ws); + ws.on('close', () => this.clients.delete(ws)); + } + + cb(ws); + } +} + +module.exports = WebSocketServer; + +/** + * Add event listeners on an `EventEmitter` using a map of + * pairs. + * + * @param {EventEmitter} server The event emitter + * @param {Object.} map The listeners to add + * @return {Function} A function that will remove the added listeners when called + * @private + */ +function addListeners(server, map) { + for (const event of Object.keys(map)) server.on(event, map[event]); + + return function removeListeners() { + for (const event of Object.keys(map)) { + server.removeListener(event, map[event]); + } + }; +} + +/** + * Emit a `'close'` event on an `EventEmitter`. + * + * @param {EventEmitter} server The event emitter + * @private + */ +function emitClose(server) { + server.emit('close'); +} + +/** + * Handle premature socket errors. + * + * @private + */ +function socketOnError() { + this.destroy(); +} + +/** + * Close the connection when preconditions are not fulfilled. + * + * @param {net.Socket} socket The socket of the upgrade request + * @param {Number} code The HTTP response status code + * @param {String} [message] The HTTP response body + * @param {Object} [headers] Additional HTTP response headers + * @private + */ +function abortHandshake(socket, code, message, headers) { + if (socket.writable) { + message = message || STATUS_CODES[code]; + headers = { + Connection: 'close', + 'Content-type': 'text/html', + 'Content-Length': Buffer.byteLength(message), + ...headers + }; + + socket.write( + `HTTP/1.1 ${code} ${STATUS_CODES[code]}\r\n` + + Object.keys(headers) + .map((h) => `${h}: ${headers[h]}`) + .join('\r\n') + + '\r\n\r\n' + + message + ); + } + + socket.removeListener('error', socketOnError); + socket.destroy(); +} diff --git a/WebSocketsServer/node_modules/ws/lib/websocket.js b/WebSocketsServer/node_modules/ws/lib/websocket.js new file mode 100644 index 000000000..cd8929bdc --- /dev/null +++ b/WebSocketsServer/node_modules/ws/lib/websocket.js @@ -0,0 +1,908 @@ +'use strict'; + +const EventEmitter = require('events'); +const https = require('https'); +const http = require('http'); +const net = require('net'); +const tls = require('tls'); +const { randomBytes, createHash } = require('crypto'); +const { URL } = require('url'); + +const PerMessageDeflate = require('./permessage-deflate'); +const Receiver = require('./receiver'); +const Sender = require('./sender'); +const { + BINARY_TYPES, + EMPTY_BUFFER, + GUID, + kStatusCode, + kWebSocket, + NOOP +} = require('./constants'); +const { addEventListener, removeEventListener } = require('./event-target'); +const { format, parse } = require('./extension'); +const { toBuffer } = require('./buffer-util'); + +const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; +const protocolVersions = [8, 13]; +const closeTimeout = 30 * 1000; + +/** + * Class representing a WebSocket. + * + * @extends EventEmitter + */ +class WebSocket extends EventEmitter { + /** + * Create a new `WebSocket`. + * + * @param {(String|url.URL)} address The URL to which to connect + * @param {(String|String[])} protocols The subprotocols + * @param {Object} options Connection options + */ + constructor(address, protocols, options) { + super(); + + this.readyState = WebSocket.CONNECTING; + this.protocol = ''; + + this._binaryType = BINARY_TYPES[0]; + this._closeFrameReceived = false; + this._closeFrameSent = false; + this._closeMessage = ''; + this._closeTimer = null; + this._closeCode = 1006; + this._extensions = {}; + this._receiver = null; + this._sender = null; + this._socket = null; + + if (address !== null) { + this._bufferedAmount = 0; + this._isServer = false; + this._redirects = 0; + + if (Array.isArray(protocols)) { + protocols = protocols.join(', '); + } else if (typeof protocols === 'object' && protocols !== null) { + options = protocols; + protocols = undefined; + } + + initAsClient(this, address, protocols, options); + } else { + this._isServer = true; + } + } + + get CONNECTING() { + return WebSocket.CONNECTING; + } + get CLOSING() { + return WebSocket.CLOSING; + } + get CLOSED() { + return WebSocket.CLOSED; + } + get OPEN() { + return WebSocket.OPEN; + } + + /** + * This deviates from the WHATWG interface since ws doesn't support the + * required default "blob" type (instead we define a custom "nodebuffer" + * type). + * + * @type {String} + */ + get binaryType() { + return this._binaryType; + } + + set binaryType(type) { + if (!BINARY_TYPES.includes(type)) return; + + this._binaryType = type; + + // + // Allow to change `binaryType` on the fly. + // + if (this._receiver) this._receiver._binaryType = type; + } + + /** + * @type {Number} + */ + get bufferedAmount() { + if (!this._socket) return this._bufferedAmount; + + // + // `socket.bufferSize` is `undefined` if the socket is closed. + // + return (this._socket.bufferSize || 0) + this._sender._bufferedBytes; + } + + /** + * @type {String} + */ + get extensions() { + return Object.keys(this._extensions).join(); + } + + /** + * Set up the socket and the internal resources. + * + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Number} maxPayload The maximum allowed message size + * @private + */ + setSocket(socket, head, maxPayload) { + const receiver = new Receiver( + this._binaryType, + this._extensions, + maxPayload + ); + + this._sender = new Sender(socket, this._extensions); + this._receiver = receiver; + this._socket = socket; + + receiver[kWebSocket] = this; + socket[kWebSocket] = this; + + receiver.on('conclude', receiverOnConclude); + receiver.on('drain', receiverOnDrain); + receiver.on('error', receiverOnError); + receiver.on('message', receiverOnMessage); + receiver.on('ping', receiverOnPing); + receiver.on('pong', receiverOnPong); + + socket.setTimeout(0); + socket.setNoDelay(); + + if (head.length > 0) socket.unshift(head); + + socket.on('close', socketOnClose); + socket.on('data', socketOnData); + socket.on('end', socketOnEnd); + socket.on('error', socketOnError); + + this.readyState = WebSocket.OPEN; + this.emit('open'); + } + + /** + * Emit the `'close'` event. + * + * @private + */ + emitClose() { + this.readyState = WebSocket.CLOSED; + + if (!this._socket) { + this.emit('close', this._closeCode, this._closeMessage); + return; + } + + if (this._extensions[PerMessageDeflate.extensionName]) { + this._extensions[PerMessageDeflate.extensionName].cleanup(); + } + + this._receiver.removeAllListeners(); + this.emit('close', this._closeCode, this._closeMessage); + } + + /** + * Start a closing handshake. + * + * +----------+ +-----------+ +----------+ + * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - + * | +----------+ +-----------+ +----------+ | + * +----------+ +-----------+ | + * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING + * +----------+ +-----------+ | + * | | | +---+ | + * +------------------------+-->|fin| - - - - + * | +---+ | +---+ + * - - - - -|fin|<---------------------+ + * +---+ + * + * @param {Number} code Status code explaining why the connection is closing + * @param {String} data A string explaining why the connection is closing + * @public + */ + close(code, data) { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this.readyState === WebSocket.CLOSING) { + if (this._closeFrameSent && this._closeFrameReceived) this._socket.end(); + return; + } + + this.readyState = WebSocket.CLOSING; + this._sender.close(code, data, !this._isServer, (err) => { + // + // This error is handled by the `'error'` listener on the socket. We only + // want to know if the close frame has been sent here. + // + if (err) return; + + this._closeFrameSent = true; + if (this._closeFrameReceived) this._socket.end(); + }); + + // + // Specify a timeout for the closing handshake to complete. + // + this._closeTimer = setTimeout( + this._socket.destroy.bind(this._socket), + closeTimeout + ); + } + + /** + * Send a ping. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the ping is sent + * @public + */ + ping(data, mask, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + if (mask === undefined) mask = !this._isServer; + this._sender.ping(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Send a pong. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the pong is sent + * @public + */ + pong(data, mask, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + if (mask === undefined) mask = !this._isServer; + this._sender.pong(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Send a data message. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress + * `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback which is executed when data is written out + * @public + */ + send(data, options, cb) { + if (this.readyState === WebSocket.CONNECTING) { + throw new Error('WebSocket is not open: readyState 0 (CONNECTING)'); + } + + if (typeof options === 'function') { + cb = options; + options = {}; + } + + if (typeof data === 'number') data = data.toString(); + + if (this.readyState !== WebSocket.OPEN) { + sendAfterClose(this, data, cb); + return; + } + + const opts = { + binary: typeof data !== 'string', + mask: !this._isServer, + compress: true, + fin: true, + ...options + }; + + if (!this._extensions[PerMessageDeflate.extensionName]) { + opts.compress = false; + } + + this._sender.send(data || EMPTY_BUFFER, opts, cb); + } + + /** + * Forcibly close the connection. + * + * @public + */ + terminate() { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this._socket) { + this.readyState = WebSocket.CLOSING; + this._socket.destroy(); + } + } +} + +readyStates.forEach((readyState, i) => { + WebSocket[readyState] = i; +}); + +// +// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. +// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface +// +['open', 'error', 'close', 'message'].forEach((method) => { + Object.defineProperty(WebSocket.prototype, `on${method}`, { + /** + * Return the listener of the event. + * + * @return {(Function|undefined)} The event listener or `undefined` + * @public + */ + get() { + const listeners = this.listeners(method); + for (let i = 0; i < listeners.length; i++) { + if (listeners[i]._listener) return listeners[i]._listener; + } + + return undefined; + }, + /** + * Add a listener for the event. + * + * @param {Function} listener The listener to add + * @public + */ + set(listener) { + const listeners = this.listeners(method); + for (let i = 0; i < listeners.length; i++) { + // + // Remove only the listeners added via `addEventListener`. + // + if (listeners[i]._listener) this.removeListener(method, listeners[i]); + } + this.addEventListener(method, listener); + } + }); +}); + +WebSocket.prototype.addEventListener = addEventListener; +WebSocket.prototype.removeEventListener = removeEventListener; + +module.exports = WebSocket; + +/** + * Initialize a WebSocket client. + * + * @param {WebSocket} websocket The client to initialize + * @param {(String|url.URL)} address The URL to which to connect + * @param {String} protocols The subprotocols + * @param {Object} options Connection options + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable + * permessage-deflate + * @param {Number} options.handshakeTimeout Timeout in milliseconds for the + * handshake request + * @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` + * header + * @param {String} options.origin Value of the `Origin` or + * `Sec-WebSocket-Origin` header + * @param {Number} options.maxPayload The maximum allowed message size + * @param {Boolean} options.followRedirects Whether or not to follow redirects + * @param {Number} options.maxRedirects The maximum number of redirects allowed + * @private + */ +function initAsClient(websocket, address, protocols, options) { + const opts = { + protocolVersion: protocolVersions[1], + maxPayload: 100 * 1024 * 1024, + perMessageDeflate: true, + followRedirects: false, + maxRedirects: 10, + ...options, + createConnection: undefined, + socketPath: undefined, + hostname: undefined, + protocol: undefined, + timeout: undefined, + method: undefined, + auth: undefined, + host: undefined, + path: undefined, + port: undefined + }; + + if (!protocolVersions.includes(opts.protocolVersion)) { + throw new RangeError( + `Unsupported protocol version: ${opts.protocolVersion} ` + + `(supported versions: ${protocolVersions.join(', ')})` + ); + } + + let parsedUrl; + + if (address instanceof URL) { + parsedUrl = address; + websocket.url = address.href; + } else { + parsedUrl = new URL(address); + websocket.url = address; + } + + const isUnixSocket = parsedUrl.protocol === 'ws+unix:'; + + if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) { + throw new Error(`Invalid URL: ${websocket.url}`); + } + + const isSecure = + parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:'; + const defaultPort = isSecure ? 443 : 80; + const key = randomBytes(16).toString('base64'); + const get = isSecure ? https.get : http.get; + let perMessageDeflate; + + opts.createConnection = isSecure ? tlsConnect : netConnect; + opts.defaultPort = opts.defaultPort || defaultPort; + opts.port = parsedUrl.port || defaultPort; + opts.host = parsedUrl.hostname.startsWith('[') + ? parsedUrl.hostname.slice(1, -1) + : parsedUrl.hostname; + opts.headers = { + 'Sec-WebSocket-Version': opts.protocolVersion, + 'Sec-WebSocket-Key': key, + Connection: 'Upgrade', + Upgrade: 'websocket', + ...opts.headers + }; + opts.path = parsedUrl.pathname + parsedUrl.search; + opts.timeout = opts.handshakeTimeout; + + if (opts.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate( + opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, + false, + opts.maxPayload + ); + opts.headers['Sec-WebSocket-Extensions'] = format({ + [PerMessageDeflate.extensionName]: perMessageDeflate.offer() + }); + } + if (protocols) { + opts.headers['Sec-WebSocket-Protocol'] = protocols; + } + if (opts.origin) { + if (opts.protocolVersion < 13) { + opts.headers['Sec-WebSocket-Origin'] = opts.origin; + } else { + opts.headers.Origin = opts.origin; + } + } + if (parsedUrl.username || parsedUrl.password) { + opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; + } + + if (isUnixSocket) { + const parts = opts.path.split(':'); + + opts.socketPath = parts[0]; + opts.path = parts[1]; + } + + let req = (websocket._req = get(opts)); + + if (opts.timeout) { + req.on('timeout', () => { + abortHandshake(websocket, req, 'Opening handshake has timed out'); + }); + } + + req.on('error', (err) => { + if (websocket._req.aborted) return; + + req = websocket._req = null; + websocket.readyState = WebSocket.CLOSING; + websocket.emit('error', err); + websocket.emitClose(); + }); + + req.on('response', (res) => { + const location = res.headers.location; + const statusCode = res.statusCode; + + if ( + location && + opts.followRedirects && + statusCode >= 300 && + statusCode < 400 + ) { + if (++websocket._redirects > opts.maxRedirects) { + abortHandshake(websocket, req, 'Maximum redirects exceeded'); + return; + } + + req.abort(); + + const addr = new URL(location, address); + + initAsClient(websocket, addr, protocols, options); + } else if (!websocket.emit('unexpected-response', req, res)) { + abortHandshake( + websocket, + req, + `Unexpected server response: ${res.statusCode}` + ); + } + }); + + req.on('upgrade', (res, socket, head) => { + websocket.emit('upgrade', res); + + // + // The user may have closed the connection from a listener of the `upgrade` + // event. + // + if (websocket.readyState !== WebSocket.CONNECTING) return; + + req = websocket._req = null; + + const digest = createHash('sha1') + .update(key + GUID) + .digest('base64'); + + if (res.headers['sec-websocket-accept'] !== digest) { + abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header'); + return; + } + + const serverProt = res.headers['sec-websocket-protocol']; + const protList = (protocols || '').split(/, */); + let protError; + + if (!protocols && serverProt) { + protError = 'Server sent a subprotocol but none was requested'; + } else if (protocols && !serverProt) { + protError = 'Server sent no subprotocol'; + } else if (serverProt && !protList.includes(serverProt)) { + protError = 'Server sent an invalid subprotocol'; + } + + if (protError) { + abortHandshake(websocket, socket, protError); + return; + } + + if (serverProt) websocket.protocol = serverProt; + + if (perMessageDeflate) { + try { + const extensions = parse(res.headers['sec-websocket-extensions']); + + if (extensions[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]); + websocket._extensions[ + PerMessageDeflate.extensionName + ] = perMessageDeflate; + } + } catch (err) { + abortHandshake( + websocket, + socket, + 'Invalid Sec-WebSocket-Extensions header' + ); + return; + } + } + + websocket.setSocket(socket, head, opts.maxPayload); + }); +} + +/** + * Create a `net.Socket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {net.Socket} The newly created socket used to start the connection + * @private + */ +function netConnect(options) { + options.path = options.socketPath; + return net.connect(options); +} + +/** + * Create a `tls.TLSSocket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {tls.TLSSocket} The newly created socket used to start the connection + * @private + */ +function tlsConnect(options) { + options.path = undefined; + + if (!options.servername && options.servername !== '') { + options.servername = options.host; + } + + return tls.connect(options); +} + +/** + * Abort the handshake and emit an error. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {(http.ClientRequest|net.Socket)} stream The request to abort or the + * socket to destroy + * @param {String} message The error message + * @private + */ +function abortHandshake(websocket, stream, message) { + websocket.readyState = WebSocket.CLOSING; + + const err = new Error(message); + Error.captureStackTrace(err, abortHandshake); + + if (stream.setHeader) { + stream.abort(); + stream.once('abort', websocket.emitClose.bind(websocket)); + websocket.emit('error', err); + } else { + stream.destroy(err); + stream.once('error', websocket.emit.bind(websocket, 'error')); + stream.once('close', websocket.emitClose.bind(websocket)); + } +} + +/** + * Handle cases where the `ping()`, `pong()`, or `send()` methods are called + * when the `readyState` attribute is `CLOSING` or `CLOSED`. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {*} data The data to send + * @param {Function} cb Callback + * @private + */ +function sendAfterClose(websocket, data, cb) { + if (data) { + const length = toBuffer(data).length; + + // + // The `_bufferedAmount` property is used only when the peer is a client and + // the opening handshake fails. Under these circumstances, in fact, the + // `setSocket()` method is not called, so the `_socket` and `_sender` + // properties are set to `null`. + // + if (websocket._socket) websocket._sender._bufferedBytes += length; + else websocket._bufferedAmount += length; + } + + if (cb) { + const err = new Error( + `WebSocket is not open: readyState ${websocket.readyState} ` + + `(${readyStates[websocket.readyState]})` + ); + cb(err); + } +} + +/** + * The listener of the `Receiver` `'conclude'` event. + * + * @param {Number} code The status code + * @param {String} reason The reason for closing + * @private + */ +function receiverOnConclude(code, reason) { + const websocket = this[kWebSocket]; + + websocket._socket.removeListener('data', socketOnData); + websocket._socket.resume(); + + websocket._closeFrameReceived = true; + websocket._closeMessage = reason; + websocket._closeCode = code; + + if (code === 1005) websocket.close(); + else websocket.close(code, reason); +} + +/** + * The listener of the `Receiver` `'drain'` event. + * + * @private + */ +function receiverOnDrain() { + this[kWebSocket]._socket.resume(); +} + +/** + * The listener of the `Receiver` `'error'` event. + * + * @param {(RangeError|Error)} err The emitted error + * @private + */ +function receiverOnError(err) { + const websocket = this[kWebSocket]; + + websocket._socket.removeListener('data', socketOnData); + + websocket.readyState = WebSocket.CLOSING; + websocket._closeCode = err[kStatusCode]; + websocket.emit('error', err); + websocket._socket.destroy(); +} + +/** + * The listener of the `Receiver` `'finish'` event. + * + * @private + */ +function receiverOnFinish() { + this[kWebSocket].emitClose(); +} + +/** + * The listener of the `Receiver` `'message'` event. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message + * @private + */ +function receiverOnMessage(data) { + this[kWebSocket].emit('message', data); +} + +/** + * The listener of the `Receiver` `'ping'` event. + * + * @param {Buffer} data The data included in the ping frame + * @private + */ +function receiverOnPing(data) { + const websocket = this[kWebSocket]; + + websocket.pong(data, !websocket._isServer, NOOP); + websocket.emit('ping', data); +} + +/** + * The listener of the `Receiver` `'pong'` event. + * + * @param {Buffer} data The data included in the pong frame + * @private + */ +function receiverOnPong(data) { + this[kWebSocket].emit('pong', data); +} + +/** + * The listener of the `net.Socket` `'close'` event. + * + * @private + */ +function socketOnClose() { + const websocket = this[kWebSocket]; + + this.removeListener('close', socketOnClose); + this.removeListener('end', socketOnEnd); + + websocket.readyState = WebSocket.CLOSING; + + // + // The close frame might not have been received or the `'end'` event emitted, + // for example, if the socket was destroyed due to an error. Ensure that the + // `receiver` stream is closed after writing any remaining buffered data to + // it. If the readable side of the socket is in flowing mode then there is no + // buffered data as everything has been already written and `readable.read()` + // will return `null`. If instead, the socket is paused, any possible buffered + // data will be read as a single chunk and emitted synchronously in a single + // `'data'` event. + // + websocket._socket.read(); + websocket._receiver.end(); + + this.removeListener('data', socketOnData); + this[kWebSocket] = undefined; + + clearTimeout(websocket._closeTimer); + + if ( + websocket._receiver._writableState.finished || + websocket._receiver._writableState.errorEmitted + ) { + websocket.emitClose(); + } else { + websocket._receiver.on('error', receiverOnFinish); + websocket._receiver.on('finish', receiverOnFinish); + } +} + +/** + * The listener of the `net.Socket` `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function socketOnData(chunk) { + if (!this[kWebSocket]._receiver.write(chunk)) { + this.pause(); + } +} + +/** + * The listener of the `net.Socket` `'end'` event. + * + * @private + */ +function socketOnEnd() { + const websocket = this[kWebSocket]; + + websocket.readyState = WebSocket.CLOSING; + websocket._receiver.end(); + this.end(); +} + +/** + * The listener of the `net.Socket` `'error'` event. + * + * @private + */ +function socketOnError() { + const websocket = this[kWebSocket]; + + this.removeListener('error', socketOnError); + this.on('error', NOOP); + + if (websocket) { + websocket.readyState = WebSocket.CLOSING; + this.destroy(); + } +} diff --git a/WebSocketsServer/node_modules/ws/package.json b/WebSocketsServer/node_modules/ws/package.json new file mode 100644 index 000000000..1b81ee964 --- /dev/null +++ b/WebSocketsServer/node_modules/ws/package.json @@ -0,0 +1,86 @@ +{ + "_from": "ws", + "_id": "ws@7.1.2", + "_inBundle": false, + "_integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", + "_location": "/ws", + "_phantomChildren": {}, + "_requested": { + "type": "tag", + "registry": true, + "raw": "ws", + "name": "ws", + "escapedName": "ws", + "rawSpec": "", + "saveSpec": null, + "fetchSpec": "latest" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", + "_shasum": "c672d1629de8bb27a9699eb599be47aeeedd8f73", + "_spec": "ws", + "_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebSockets-Server", + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "browser": "browser.js", + "bugs": { + "url": "https://github.com/websockets/ws/issues" + }, + "bundleDependencies": false, + "dependencies": { + "async-limiter": "^1.0.0" + }, + "deprecated": false, + "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", + "devDependencies": { + "benchmark": "^2.1.4", + "bufferutil": "^4.0.1", + "coveralls": "^3.0.3", + "eslint": "^6.0.0", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-prettier": "^3.0.1", + "mocha": "^6.1.3", + "nyc": "^14.0.0", + "prettier": "^1.17.0", + "utf-8-validate": "^5.0.2" + }, + "files": [ + "browser.js", + "index.js", + "lib/*.js" + ], + "greenkeeper": { + "commitMessages": { + "dependencyUpdate": "[pkg] Update ${dependency} to version ${version}", + "devDependencyUpdate": "[pkg] Update ${dependency} to version ${version}" + } + }, + "homepage": "https://github.com/websockets/ws", + "keywords": [ + "HyBi", + "Push", + "RFC-6455", + "WebSocket", + "WebSockets", + "real-time" + ], + "license": "MIT", + "main": "index.js", + "name": "ws", + "repository": { + "type": "git", + "url": "git+https://github.com/websockets/ws.git" + }, + "scripts": { + "integration": "npm run lint && mocha --throw-deprecation test/*.integration.js", + "lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\"", + "test": "npm run lint && nyc --reporter=html --reporter=text mocha --throw-deprecation test/*.test.js" + }, + "version": "7.1.2" +} diff --git a/WebSocketsServer/package-lock.json b/WebSocketsServer/package-lock.json new file mode 100644 index 000000000..0f5dc028f --- /dev/null +++ b/WebSocketsServer/package-lock.json @@ -0,0 +1,59 @@ +{ + "name": "events-server", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "easy-stack": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", + "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=" + }, + "event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==" + }, + "js-message": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", + "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=" + }, + "js-queue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", + "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "requires": { + "easy-stack": "1.0.0" + } + }, + "node-ipc": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", + "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "requires": { + "event-pubsub": "4.3.0", + "js-message": "1.0.5", + "js-queue": "2.0.0" + } + }, + "ws": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", + "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", + "requires": { + "async-limiter": "1.0.1" + } + } + } +} diff --git a/WebSocketsServer/package.json b/WebSocketsServer/package.json new file mode 100644 index 000000000..0f735d57f --- /dev/null +++ b/WebSocketsServer/package.json @@ -0,0 +1,14 @@ +{ + "name": "events-server", + "version": "0.0.0", + "description": "Events-Server", + "main": "app.js", + "author": { + "name": "Luis" + }, + "dependencies": { + "dotenv": "^8.2.0", + "node-ipc": "^9.1.1", + "ws": "^7.1.2" + } +} diff --git a/WebSocketsServer/server.js b/WebSocketsServer/server.js new file mode 100644 index 000000000..7c641b616 --- /dev/null +++ b/WebSocketsServer/server.js @@ -0,0 +1,87 @@ +require('dotenv').config() +const WebSocket = require('ws') +let wss + +let ipc = require('node-ipc'); + +ipc.config.id = 'hello'; +ipc.config.retry = 1500; +ipc.config.silent = true; + +let port = process.env.WEB_SOCKETS_PORT + +process.on('uncaughtException', function (err) { + if (err.message.indexOf("EADDRINUSE") > 0) { + console.log("Superalgos Web Server cannot be started. Reason: the port " + port + " is already in use by another application. The web server cannot be started until either you stop the other application or you change the port number at the WebServer/.env file for an unused one. (variable name VIRTUAL_PORT)") + return + } + console.log('[ERROR] Web Sockets Server -> server -> uncaughtException -> err.message = ' + err.message) + console.log('[ERROR] Web Sockets Server -> server -> uncaughtException -> err.stack = ' + err.stack) + process.exit(1) +}) + +process.on('unhandledRejection', (reason, p) => { + console.log('[ERROR] Web Sockets Server -> server -> unhandledRejection -> reason = ' + JSON.stringify(reason)) + console.log('[ERROR] Web Sockets Server -> server -> unhandledRejection -> p = ' + JSON.stringify(p)) + process.exit(1) +}) + +try { + ipc.connectTo( + 'world', + function () { + ipc.of.world.on( + 'connect', + function () { + ipc.log('## connected to world ##'.rainbow, ipc.config.delay); + + let eventCommand = { + action: 'greetings', + from: 'Web Sockets Server' + } + + ipc.of.world.emit( + 'message', + JSON.stringify(eventCommand) + ) + + wss = new WebSocket.Server({ port: port }) + + wss.on('connection', ws => { + ws.on('message', message => { + + if (ipc.config.silent !== true) { + console.log(`Received message from browser => ${message}`) + } + + ipc.of.world.emit( + 'message', + message + ) + }) + + ipc.of.world.on( + 'message', + function (data) { + ipc.log('got a message from world : '.debug, data); + + ws.send(data) + } + ); + }) + + console.log('Web Socket Server Started.') + } + ); + ipc.of.world.on( + 'disconnect', + function () { + ipc.log('disconnected from world'.notice); + } + ); + } + ); +} catch (err) { + console.log('[ERROR] Web Sockets Server -> server -> connectTo -> err.message = ' + err.message) +} + diff --git a/docs/.gitignore b/docs/.gitignore index f40fbd8ba..c46da9a7b 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,5 +1,8 @@ -_site -.sass-cache -.jekyll-cache +_site/ +.sass-cache/ .jekyll-metadata -vendor +_pdf +.DS_Store +.idea +vendor/ +.bundle/ diff --git a/docs/.gitlab-ci.yml b/docs/.gitlab-ci.yml new file mode 100644 index 000000000..7167efd52 --- /dev/null +++ b/docs/.gitlab-ci.yml @@ -0,0 +1,26 @@ +image: ruby:2.3 + +variables: + JEKYLL_ENV: production + +test: + stage: test + script: + - sh ./var/build.sh + - bundle exec jekyll build -d test + artifacts: + paths: + - test + except: + - master + +pages: + stage: deploy + script: + - sh ./var/build.sh + - bundle exec jekyll build -d public + artifacts: + paths: + - public + only: + - master diff --git a/docs/.vscode/settings.json b/docs/.vscode/settings.json new file mode 100644 index 000000000..5af001d55 --- /dev/null +++ b/docs/.vscode/settings.json @@ -0,0 +1,6 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.associations": { + "*.html": "liquid" + } +} diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index 086a5c9ea..000000000 --- a/docs/404.html +++ /dev/null @@ -1,25 +0,0 @@ ---- -permalink: /404.html -layout: default ---- - - - -
      -

      404

      - -

      Page not found :(

      -

      The requested page could not be found.

      -
      diff --git a/docs/404.md b/docs/404.md new file mode 100644 index 000000000..a7b58c002 --- /dev/null +++ b/docs/404.md @@ -0,0 +1,6 @@ +--- +title: "Page Not Found" +search: exclude +--- + +Sorry, but the page you were trying to view does not exist. Try searching for it or looking at the URL to see if it looks correct. diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 000000000..7eed201a6 --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,14 @@ +FROM jekyll/builder + +WORKDIR /tmp +ADD Gemfile /tmp/ +ADD Gemfile.lock /tmp/ +RUN bundle install + +FROM jekyll/jekyll + +VOLUME /src +EXPOSE 4000 + +WORKDIR /src +ENTRYPOINT ["jekyll", "serve", "--livereload", "-H", "0.0.0.0"] diff --git a/docs/Gemfile b/docs/Gemfile index 6dc48a17e..515633aa3 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -1,30 +1,7 @@ source "https://rubygems.org" -# Hello! This is where you manage which Jekyll version is used to run. -# When you want to use a different version, change it below, save the -# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: -# -# bundle exec jekyll serve -# -# This will help ensure the proper Jekyll version is running. -# Happy Jekylling! -# gem "jekyll", "~> 4.0.0" -# This is the default theme for new Jekyll sites. You may change this to anything you like. -gem "minima", "~> 2.5" -# If you want to use GitHub Pages, remove the "gem "jekyll"" above and -# uncomment the line below. To upgrade, run `bundle update github-pages`. -gem "github-pages", "~> 203", group: :jekyll_plugins -# If you have any plugins, put them here! -group :jekyll_plugins do - gem "jekyll-feed", "~> 0.12" -end -# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem -# and associated library. -install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do - gem "tzinfo", "~> 1.2" - gem "tzinfo-data" -end - -# Performance-booster for watching directories on Windows -gem "wdm", "~> 0.1.1", :install_if => Gem.win_platform? +# to publish on github page +gem 'github-pages', group: :jekyll_plugins +# to publich without github page +#gem "jekyll" diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 6bdc330bc..dd64253d1 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -24,10 +24,12 @@ GEM http_parser.rb (~> 0.6.0) ethon (0.12.0) ffi (>= 1.3.0) + eventmachine (1.2.7) eventmachine (1.2.7-x64-mingw32) execjs (2.7.0) faraday (1.0.0) multipart-post (>= 1.2, < 3) + ffi (1.12.1) ffi (1.12.1-x64-mingw32) forwardable-extended (2.6.0) gemoji (3.0.1) @@ -204,6 +206,8 @@ GEM jekyll-seo-tag (~> 2.1) minitest (5.14.0) multipart-post (2.1.1) + nokogiri (1.10.7) + mini_portile2 (~> 2.4.0) nokogiri (1.10.7-x64-mingw32) mini_portile2 (~> 2.4.0) octokit (4.15.0) @@ -235,22 +239,15 @@ GEM ethon (>= 0.9.0) tzinfo (1.2.6) thread_safe (~> 0.1) - tzinfo-data (1.2019.3) - tzinfo (>= 1.0.0) unicode-display_width (1.6.1) - wdm (0.1.1) zeitwerk (2.2.2) PLATFORMS + ruby x64-mingw32 DEPENDENCIES - github-pages (~> 203) - jekyll-feed (~> 0.12) - minima (~> 2.5) - tzinfo (~> 1.2) - tzinfo-data - wdm (~> 0.1.1) + github-pages BUNDLED WITH 2.1.4 diff --git a/docs/_config.yml b/docs/_config.yml index 60e14aa77..5f5844fdb 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,55 +1,122 @@ -# Welcome to Jekyll! -# -# This config file is meant for settings that affect your whole blog, values -# which you are expected to set up once and rarely edit after that. If you find -# yourself editing this file very often, consider using Jekyll's data files -# feature for the data you need to update frequently. -# -# For technical reasons, this file is *NOT* reloaded automatically when you use -# 'bundle exec jekyll serve'. If you change this file, please restart the server process. -# -# If you need help with YAML syntax, here are some quick references for you: -# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml -# https://learnxinyminutes.com/docs/yaml/ -# -# Site settings -# These are used to personalize your new site. If you look in the HTML files, -# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. -# You can create any custom variable you would like, and they will be accessible -# in the templates via {{ site.myvariable }}. +repository: tomjoht/documentation-theme-jekyll -title: Your awesome title -email: your-email@example.com -description: >- # this means to ignore newlines until "baseurl:" - Write an awesome description for your new site here. You can edit this - line in _config.yml. It will appear in your document head meta (for - Google search results) and in your feed.xml site description. -baseurl: "" # the subpath of your site, e.g. /blog -url: "" # the base hostname & protocol for your site, e.g. http://example.com -twitter_username: jekyllrb -github_username: jekyll +output: web +# this property is useful for conditional filtering of content that is separate from the PDF. -# Build settings -theme: minima -plugins: - - jekyll-feed +topnav_title: Superalgos Documentation +# this appears on the top navigation bar next to the home button -# Exclude from processing. -# The following items will not be processed, by default. -# Any item listed under the `exclude:` key here will be automatically added to -# the internal "default list". -# -# Excluded items can be processed by explicitly listing the directories or -# their entries' file path in the `include:` list. -# -# exclude: -# - .sass-cache/ -# - .jekyll-cache/ -# - gemfiles/ -# - Gemfile -# - Gemfile.lock -# - node_modules/ -# - vendor/bundle/ -# - vendor/cache/ -# - vendor/gems/ -# - vendor/ruby/ +site_title: Superalgos Crypto Intelligence Suite Documentation +# this appears in the html browser tab for the site title (seen mostly by search engines, not users) + +company_name: Superalgos.org +# this appears in the footer + +github_editme_path: +# if you're using Github, provide the basepath to the branch you've created for reviews, following the sample here. if not, leave this value blank. + +# gitlab_editme_path: tomjoht/documentation-theme-jekyll/blob/gh-pages/ +# if you're using GitLab, provide the basepath to the branch you've created for reviews, following the sample here. if not, leave this value blank. + +google_analytics: UA-121623442-1 +# if you have google-analytics ID, put it in. if not, edit this value to blank. + +host: 127.0.0.1 +# the preview server used. Leave as is. + +port: 4000 +# the port where the preview is rendered. You can leave this as is unless you have other Jekyll builds using this same port that might cause conflicts. in that case, use another port such as 4006. + +exclude: + - .idea/ + - .gitignore + - vendor +# these are the files and directories that jekyll will exclude from the build + +feedback_subject_line: + +feedback_email: +# used as a contact email for the Feedback link in the top navigation bar + +# feedback_disable: true +# if you uncomment the previous line, the Feedback link gets removed + +feedback_text: "Need help?" +# if you uncomment the previous line, it changes the Feedback text + +feedback_link: "https://t.me/superalgoscommunity" +# if you uncomment the previous line, it changes where the feedback link points to + +highlighter: rouge +# library used for syntax highlighting + +markdown: kramdown +kramdown: + input: GFM + auto_ids: true + hard_wrap: false + syntax_highlighter: rouge + parse_block_html: true + +# filter used to process markdown. note that kramdown differs from github-flavored markdown in some subtle ways + +collections: + tooltips: + output: false +# collections are declared here. this renders the content in _tooltips and processes it, but doesn't output it as actual files in the output unless you change output to true + +defaults: + - + scope: + path: "" + type: "pages" + values: + layout: "page" + comments: false + # if you don't want to use Commento.io and just hide comments, change true to false wherever you see the comments property + search: true + sidebar: home_sidebar + topnav: topnav + - + scope: + path: "" + type: "tooltips" + values: + layout: "page" + search: true + tooltip: true + + - + scope: + path: "" + type: "posts" + values: + layout: "post" + comments: false + # if you don't want to use Commento.io and just hide comments, change true to false wherever you see the comments property + search: true + sidebar: home_sidebar + topnav: topnav + +# these are defaults used for the frontmatter for these file types + +sidebars: +- mydoc_sidebar +- suite_sidebar +- syst_sidebar +- algo_sidebar +- disc_sidebar +- smart_sidebar +- coach_sidebar +- market_sidebar +- mine_sidebar + +description: "The Superalgos Suite Documentation covers general workings of Superalgos and of each of the products in the suite." +# the description is used in the feed.xml file + +# needed for sitemap.xml file only +# url: http://idratherbewriting.com +# baseurl: /documentation-theme-jekyll + + +github: [metadata] diff --git a/docs/_data/alerts.yml b/docs/_data/alerts.yml new file mode 100644 index 000000000..157e1622b --- /dev/null +++ b/docs/_data/alerts.yml @@ -0,0 +1,15 @@ +tip: '